/* Benignware Javascript Framework */

if (!window.console) {
	window.console = {
		log: function(message) {
		},
		error: function(message) {
		}
	}
}

/* OOP Core */

if (this != null && this["benignware"] == null) {
	this["benignware"] = {core: {}}
}

/**
 * Root of the class hierarchy.
 * @class Object
 */

benignware.core.Object = (function(){
	
	function Object(){
	}
	
	/**
	 * Tests if the object is an instance of the specified superclass.
	 * @method instanceOf
	 * @param {benignware.core.Class} classObject
	 * @return {Boolean} true if the object is an instance of the specified class
	 */ 
	Object.prototype.instanceOf = function(classObject) {
		if (!classObject) return false;
		if (typeof(classObject) == "string") {
			classObject = benignware.core.Class.getClass(classObject);
		}
		if (this.constructor == classObject) {
			return true;
		}
		if (typeof(this.__class__) == "function") {
			var proto = this.__class__.prototype;
			while (proto) {
				if (proto == classObject.prototype) return true;
				proto = proto.__proto__;
			}
		}
		
		return false;
	}
	/**
	 * Retrieves the class of the instance
	 * @method getClass
	 * @return {benignware.core.Class} the constructor class of the instance
	 */
	Object.prototype.getClass = function() {
		// overridden by Element.create
		return constructor;
	}
	 
	return Object;
})();
/**
 * The Class Object provides the oop-model of the library
 * @class benignware.core.Class
 */
benignware.core.Class = (function(){
	// imports
	var Object = benignware.core.Object;
	
	//  classMap: stores information about the classes 
	var classMap = {}
	
	/**
	 * Create new classes and manage class organization.
	 * The constructor for the Class object is an alias to the Class.extend method.
	 * @class benignware.core.Class
	 * @constructor
	 * @param {benignware.core.Class} superClass 
	 * @param {Prototype} constructor
	 * @return {benignware.core.Class} the new class object
	 */
	function Class(superclass, constructor) {
		return Class.extend.apply(this, arguments);
	}
	
	Class.autoLoadDependencies = false;
	
	/**
	 * 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];
				superclass = benignware.core.Object;
			}
		}
		if (!superclass) {
			superclass = benignware.core.Object;
		}
		
		for (var property in superclass.prototype) {
			if (constructor.prototype[property] == null) {
				constructor.prototype[property] = superclass.prototype[property];
			}
		}
		constructor.prototype.__super__ = superclass;
		constructor.prototype.__proto__ = superclass.prototype;
		constructor.prototype.getClass = function() {
			return constructor;
		}
		return constructor;
	} 
	 
	 Class.implement = function(constructor, interfaceObj) {
		if (!constructor.prototype.__interfaces__) {
			constructor.prototype.__interfaces__ = [];
		}
		constructor.prototype.__interfaces__.push(interfaceObj);
		return constructor;
	 }
	 
	 
	/**
	 * 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;
	}

	// remove whitespace
	function trim(str) {
		return str.replace(/^\s+/,"").replace(/\s+$/,"");
	}
	
	/**
	 * 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;
	}
	
	
	/*
	 * require
	 */
	
	
	// cross-browser http-request
	function getHttpRequest() {
		var httpRequest;
		if (typeof XMLHttpRequest != undefined) {
	   		httpRequest = new XMLHttpRequest();
	  	} else if (typeof ActiveXObject != undefined) {
	      	httpRequest = new ActiveXObject('Microsoft.XMLHTTP');
		}
		// error
		return httpRequest;
	}
	
	
	// classpath
	var classPath = [];
	
	
	
	/**
	 * Adds a class path for the require-method
	 * @static
	 * @method addClassPath
	 * @param {String} directory
	 */
	Class.addClassPath = function(directory) {	
		if (directory == null) return;
		if (directory.length > 0 && directory.charAt(directory.length-1) != "/") {
			directory+= "/";
		}
		classPath.push(directory);
	}
	
	Class.instanceOf = function(obj, classObject) {
		if (!obj || !classObject) return false;
		if (typeof(classObject) == "string") {
			classObject = benignware.core.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;
	}
	
	
	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 the directory from a filename
	function extractDirectory(filename) {
	  	var dir = unescape(filename.substring(0,(filename.lastIndexOf("/"))));
	  	return dir;
	}
	
	function getDefaultClassPath(className) {
		var scriptLocations = getScriptLocations();
		var classFile = className.replace(/\./g, "/")+".js";
		for (var i = 0;i<scriptLocations.length;i++) {
			var srcFile = scriptLocations[i];
			if (srcFile.length >= classFile.length && srcFile.substring(srcFile.length-classFile.length) == classFile) {
				return srcFile.substring(0, srcFile.length-classFile.length);
			}
			var classDir = extractDirectory(classFile);
			var srcDir = extractDirectory(srcFile);
			var arr = classDir.split("/");
			while (arr.length > 0) {
				var strDir = arr.join("/");
				if (srcDir.length >= strDir.length && srcDir.substring(srcDir.length-strDir.length) == strDir) {
					return srcDir.substring(0, srcDir.length-strDir.length);
				}
				var last = arr.pop();
			} 
			
		}
		return "./";
	}

	var path = getDefaultClassPath("benignware.core.Class");
	var location = window.location;
	Class.addClassPath(path);
	/**
	 * Removes a ClassPath
	 * @static
	 * @method removeClassPath
	 * @param {String} directory
	 * @see benignware.core.Object#require
	 */
	Class.removeClassPath = function(directory) {
		for (var i=0;i<classPath.length;i++) if (classPath[i]==directory) 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 (!qualifiedName) return null;
		if (classMap[qualifiedName] && classMap[qualifiedName].__class) return classMap[qualifiedName].__class;
		
		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;
	}
	
	function getFilename(fullClassID) {
		if (typeof(fullClassID) == "string") {
			var a = fullClassID.split(".");
			var className = a.pop();
			return a.join("/") + "/" + className + ".js";
		}
		return "";
	}
	/**
	 * Loads the class for the specified full name.
	 * @param {String} className
	 * @return {Class} the loaded class object
	 */
	Class.require = function(fullClassID) {
		var __class = Class.getClass(fullClassID);
		if (__class != null) return __class;
		if (classMap[fullClassID] != null) return null;
		if (!Class.autoLoadDependencies) return null;
		var relFileName = getFilename(fullClassID);
		for (var i = classPath.length - 1; i >= 0; i--) {
			var filename = classPath[i] + relFileName;
			var httpRequest = getHttpRequest();
			httpRequest.open("GET", filename, 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(fullClassID);
					if (cl) __class = cl;
				}
			}
		}
		
		Class.registerClass(fullClassID, __class, filename);
		
		if (!__class) {
			// class not found error
			if (typeof(console) != "undefined")
			console.error("ClassNotFound: " + fullClassID);
			return null;
		}
		
		return __class;	 
	}
	
	
	/**
	 * Retrieves a filename relative to a previously required class file. 
	 * Use this method to reference resource files.
	 * @param {String} className
	 * @param {String} relativePath
	 * @return {String} filename
	 */
	var locationDir = extractDirectory(window.location.pathname);
	 
	Class.getRessource = function(qualifiedName, relativePath) {
		var filename;
		if (typeof(qualifiedName) == "function") {
			for (var x in classMap) {
				if (classMap[x].__class == qualifiedName) {
					qualifiedName = x;
					break;
				}
			}
		}
		
		if (classMap[qualifiedName] && classMap[qualifiedName].filename != null) {
			filename = classMap[qualifiedName].filename;
			return extractDirectory(filename) + "/" + relativePath;
		}
		
		if (classPath.length) {
			filename = classPath[0] + getFilename(qualifiedName);
			if (filename) {
				var fileDir = extractDirectory(filename);
				//var result = locationDir ? locationDir + "/" : "";
				var result = "";
				result+= fileDir ? fileDir + "/" : "";
				result+= relativePath;
				return result;
			}
		}
		return null;
	}
	 
	Class.registerClass = function(qualifiedName, __class, filename) {
		classMap[qualifiedName] = {
			__class: __class,
			filename: filename
		}
		// register on global scope:
		var s = qualifiedName.split('.');
		var className = s.pop();
		var p = s.join('.');
		var obj = Class.namespace(p);
		obj[className] = __class;
		return __class;
	}

	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.__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;
})());
/**
 * 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 __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 (this.attachEvent) {
				this.attachEvent("on"+whichEvent, handler);
				if (__super && __super.addEventListener) {
					__super.addEventListener.apply(this, arguments);
				}
			} else if (!Class.instanceOf(this, Element) && this.addEventListener) {
				this.addEventListener(whichEvent, handler, useCapture);
			}
			
		}
	}
	
	//Element.prototype.handleEvent = function()
	
	Element.prototype.removeEventListener = function(whichEvent, handler, useCapture) {
		try {
			if (useCapture == undefined) useCapture = false;
			if (typeof(this.detachEvent) != "undefined") {
				this.detachEvent("on" + whichEvent, handler);
				if (__super && __super.removeEventListener) {
					__super.removeEventListener.apply(this, arguments);
				}
			} else if (this.__native__ && typeof(this.__native__.removeEventListener) != "undefined") {
				this.__native__.removeEventListener.call(this, whichEvent, handler, useCapture);
			} else if (!Class.instanceOf(this, Element) && this.addEventListener) {
				this.removeEventListener(whichEvent, handler, useCapture);
			}
		} catch (e) {}
	}
	
	Element.prototype.dispatchEvent = function(event) {
		if (this.__native__.dispatchEvent) {
			this.__native__.dispatchEvent.call(this, event);
		} else {
			__super.dispatchEvent.apply(this, arguments);
		}
	}
	
	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 doc = element.ownerDocument;
		var docElem = doc.documentElement;
		var pos = {x: -docElem.scrollLeft, y: -docElem.scrollTop}
		//var pos = {x:0, y:0}
		if (element) {
			var elem = element;
			do {
				if (elem == element.ownerDocument.documentElement) {
					break;
				}
				pos.x += elem.offsetLeft;
				pos.y += elem.offsetTop;
					
				pos.x -= elem.scrollLeft;
				pos.y -= elem.scrollTop;
				
			} while (elem = elem.offsetParent);
			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
})();
/**
 * 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));
		}
	}
	/**
	 * Updates the layout of the component's children.
	 * This protected method is called when the size of the component has changed. 
	 * @method resizeComponent 
	 */
	Component.prototype.resizeComponent = function() {
	}
	
	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() {
		__super.call(this);
	}
	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(Component);
		this.content = Element.create(Component);
		this.content.setStyle("position", "relative");
		this.content.addCSSName("containerContent");
		this.appendChild(this.content);
	}
	
	
	
	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.EXACT_FIT:
				w = width;
				h = height;
				break;
				
			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;
				
		
		}
		
		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 = size.width + "px";
			elem.style.height = size.height + "px";
		}
	}
	
	
	return Image;
})();

