API Docs for: 1.0

src/js/gui.js

/*! @file gui.js */

/*
 * part of the 'Transfinite Ordinal Calculator'
 * author: Claudio Kressibucher
 * license: GNU LGPL
 */

// The gui object will handle the interaction with the gui.
// It works as the basic layer of the application

/* JSHINT global declaration */
/*global oGui:true, util:true, go:true, model:true, config:true, ClassedError:true, xGetElementById, xAddEventListener, xAddClass, xRemoveClass, xGetElementsByClassName, xHasClass */

/**
 * The object which communicates with the HTML-GUI. It is
 * initialized by the function go.initGui at the start of
 * the application.
 * @class oGui
 * @static
 */
var oGui;

go.initGui = function initGui(){

	// this object is returned as "gui object"
	var obj = {};
	
	// HTMLElement: Input Keys
	var keypad = xGetElementById('keys');
	
	// HTMLElement: the wrapAll div
	var wrapAll = xGetElementById('wrapAll');
	
	// HTMLElement: the backspace key
	var bsKey = xGetElementById('bspace');
	
	// HTMLElement: input / result field (div#innerResult)
	var mainDisplay = xGetElementById('innerResult');
	// HTMLElement: div#outerResult
	var outerMainDisplay = xGetElementById('outerResult');
	// HTMLElement: compare display (div#innerCompLine)
	var cmpDisplay = xGetElementById('innerCompLine');
	
	/**
	 * Method to init the gui console (if an own console object
	 * is used instead of the browser's console)
	 * @method initGuiConsole
	 * @private
	 */
	var initGuiConsole = function (){
		obj.guiConsole = document.createElement('div');
		obj.guiConsole.setAttribute('class', 'console');
		
		tmp = document.createElement('h2');
		xAddClass(tmp, 'consoleTitle');
		tmp.innerHTML = "Console";
		
		document.body.appendChild(tmp);
		document.body.appendChild(obj.guiConsole);
		
		obj.out = {
				
			print: function (msg, cls){
				var el = document.createElement('p');
				var txt = document.createTextNode(msg);
				el.appendChild(txt);
				if (cls) {
					xAddClass(el, cls);
				}
				obj.guiConsole.insertBefore(el, obj.guiConsole.firstChild);
			},
				
			error: function (msg){
				this.print(msg, "consoleErr");
			},
			
			warn: function (msg){
				this.print(msg, "consoleWarn");
			},
			
			log: function (msg){
				this.print(msg, "consoleLog");
			}
		};
	};
	
	// button to augment display when it was reduced and to reduce when it was augmented
	var toggleBtn = document.createElement('span');
	var dots = document.createElement('div'); // to append when reduced (indicating reduced state)
	
	// flag to indicate an overflow in mainDisplay
	var bOverflow = false;
	
	var mDisContent = ""; // store last content of the display to check if it must be updated or not
	
	var tmp; // temporary variable for anything ...
	
	/**
	 * Print debug messages of different levels to the console
	 * object. Is called by the methods dbgErr, dbgWarn and dbgLog;
	 * don't call it directly...
	 * @method debug
	 * @param {string} msg The message to show
	 * @param {number} level The debug level (see config.debug)
	 * @private
	 */
	var debug = function debug(msg, level){
		if (!msg) msg = '(no message)';
		if (level <= config.debug && oGui.out !== null ) {
			switch(level){
			case 1: oGui.out.error(msg); break;
			case 2: oGui.out.warn(msg); break;
			case 3: oGui.out.log(msg); break;
			}
		}
	};
	
	/**
	 * Event to register a kind of a click... (working with touch devices
	 * and non-touch devices).
	 * @property selEvt
	 * @type string
	 * @private
	 */
	var selEvt = "ontouchend" in document ? "touchend" : "mouseup";
	
	/**
	 * Event used to detect when a key is pressed down
	 * @property downEvt
	 * @type string
	 * @private
	 */
	var downEvt = "ontouchstart" in document ? "touchstart" : "mousedown";
	
	/**
	 * Event handler to toggle the main display mode from augmented
	 * state to reduced or from reduced (or from no-overflow) to
	 * augmented state. May also be called directly (not as an event
	 * handler), this method doesn't depend on an event parameter.
	 * @method fnToggleDMode
	 * @private
	 */
	var fnToggleDMode = function (){
		// release "hover"-status
		xRemoveClass(toggleBtn, 'btnHover');
		if (!bOverflow || !xHasClass(outerMainDisplay, 'augmented')){ // expand
			if (bOverflow){ // not the first expanding: elements exist
				outerMainDisplay.removeChild(dots);
				xRemoveClass(mainDisplay, 'rightMarg'); // right margin for dots
			} else {
				// add toggle button
				outerMainDisplay.insertBefore(toggleBtn, mainDisplay);
				xAddClass(mainDisplay, 'leftMarg'); // left margin: space for toggle button
			}
			// add augmented class 5 milliseconds later to enable css animation
			setTimeout(function(){
				xAddClass(outerMainDisplay, 'augmented');
			}, 5);
		} else { // reduce
			outerMainDisplay.insertBefore(dots, mainDisplay);
			xAddClass(mainDisplay, 'rightMarg');
			xRemoveClass(outerMainDisplay, 'augmented');
		}
	};
	
	/**
	 * When display was overflowed, and now the content is small
	 * enough again to fit in the display, this method is called
	 * to remove HTMLElements and formatting used for reduced
	 * and augmented states. This method is normally called directly
	 * (not as an event handler).
	 * @method fnToNormalSizedisp
	 * @private
	 */
	var fnToNormalSizeDisp = function (){
		if (bOverflow){ // only if it was in reduced or augmented state
			xRemoveClass(outerMainDisplay, 'augmented');
			xRemoveClass(mainDisplay, 'leftMarg');
			outerMainDisplay.removeChild(toggleBtn);
			try {
				outerMainDisplay.removeChild(dots);
				xRemoveClass(mainDisplay, 'rightMarg');
			} catch (e){
				// node dots is not in the DOM, nothing to do
			}
		}
	};
	
	/**
	 * Handles a click on one of the input panel's keys
	 * @method fnKey
	 * @param evt The event object (the activated key or window.event in IE)
	 * @private
	 */
	var fnKey = function fnKey(evt){
		// init
		var target, cmd, ch;
		var err = null;
		var hovArr, i, l; // btnHover array, index and length
		
		if (!evt) {
			evt = window.event; // IE
		}
		target = evt.target || evt.srcElement;
		
		// perhaps the user clicked on the inner span element: set target to outer span
		if ( !xHasClass(target, 'btn') ){
			target = target.parentNode;
			if (!xHasClass(target, 'btn')) return; // the original target was an outer element
		}
		
		// release "hover"-status
		hovArr = xGetElementsByClassName('btnHover', keypad, 'span');
		l = hovArr.length;
		for (i=l-1; i>=0; i--){
			xRemoveClass(hovArr[i], 'btnHover');
		}
		
		if (xHasClass(bsKey, 'btnHover')){
			xRemoveClass(bsKey, 'btnHover');
		}
		
		if (xHasClass(target, 'inactive')){
			oGui.dbgLog("User clicked on inactive button...");
			return;
		}
		
		cmd = target.getAttribute('data-cmd');
		ch = target.getAttribute('data-char');
		
		if (cmd === "add-char"){
			model.addChar(ch);
		} else if (cmd === "calc"){
			model.addChar("=");
		} else if (cmd === "clear"){
			model.resetModel();
		} else if (cmd === "clrall"){
			model.clearRegister();
		} else if (cmd === "stoRcl"){
			if (ch === "R1"){ // register 1
				if (xHasClass(target, 'sto')){
					model.store(0);
				} else { // rcl
					model.recall(0);
				}
			} else { // register 2
				if (xHasClass(target, 'sto')){
					model.store(1);
				} else { // rcl
					model.recall(1);
				}
			}
		} else if (cmd === "bspace") {
			model.backSpace();
		} else {
			err = new ClassedError("Unknown Operation", ClassedError.types.progError);
			err.handleErr();
		}
	};
	
	/**
	 * Shows a popup with the complete compare register
	 * values. Triggered by Click on compare display.
	 * @method fnPopupCompare
	 */
	var fnPopupCompare = function(){
		var frag; // document fragment
		var tDiv; // temp div element
		var cmpResStr;
		var opnds = model.getCmpOpnds();
		var result = model.getCmpResult();
		if (opnds !== null && result !== null){
			// cmp resutl string
			if (result > 0){
				cmpResStr = "&gt;";
			} else if (result < 0){
				cmpResStr = "&lt;";
			} else if (result === 0){
				cmpResStr = "=";
			} else {
				oGui.dbgErr("wrong type of resutl in oGui.fnPopupCompare");
				cmpResStr = "?";
			}
			
			// create and fill fragment...
			frag = document.createDocumentFragment();
			// Register A: Label
			tDiv = document.createElement('div');
			tDiv.className = "cmpTitle";
			tDiv.innerHTML = "A =";
			frag.appendChild(tDiv);
			// Register A: Value
			tDiv = document.createElement('div');
			tDiv.className = "cmpContent";
			tDiv.innerHTML = opnds[0].toString(true);
			frag.appendChild(tDiv);
			// Register B: Label
			tDiv = document.createElement('div');
			tDiv.className = "cmpTitle";
			tDiv.innerHTML = "B =";
			frag.appendChild(tDiv);
			// Register B: Value
			tDiv = document.createElement('div');
			tDiv.className = "cmpContent";
			tDiv.innerHTML = opnds[1].toString(true);
			frag.appendChild(tDiv);
			// Comparison: Label
			tDiv = document.createElement('div');
			tDiv.className = "cmpTitle";
			tDiv.innerHTML = "Comparison:";
			frag.appendChild(tDiv);
			// Comparison: Result
			tDiv = document.createElement('div');
			tDiv.className = "cmpContent";
			tDiv.innerHTML = "<span class=\"it\">A </span>" + cmpResStr + "<span class=\"it\"> B</span>";
			frag.appendChild(tDiv);
			
			// show hint
			oGui.showHint(frag, "OK");
		} // else: do nothing, errors are already handled by model
	};
	
	// functions used by showHint to show an lightBox before an overlay layer

	/**
	 * Event Handler to remove the overlay layer again.
	 * @method fnRemoveOL
	 * @private
	 */
	var fnRemoveOL = function (){
		var el = xGetElementById('ovlay');
		var inner;
		if (el){
			inner = xGetElementById('innerOvlay');
			if (inner){
				inner.className = "hidden"; // transition effect
				setTimeout(function(){
					document.body.removeChild(el);
				}, 550);
			} else {
				document.body.removeChild(el);
			}
		}
	};
	
	/**
	 * Method to create a semi-transparent overlay layer
	 * with a lightbox as a smoother alert...
	 * @method fnOverlay
	 * @param {HTMLElement} content The Element to show in the lightbox
	 * @param {string} closeBtnText A short text, e.g. "close" or "OK" or
	 * similar, which is used on a button inside the lightbox
	 */
	var fnOverlay = function (content, closeBtnText){
		var layer = document.createElement('div');
		var inner = document.createElement('div');
		var btn = document.createElement('span'); // the close button
		var btnLine = document.createElement('div');
		content.className = content.className + (content.className ? " " : "") + "ovlContent";
		layer.id = "ovlay";
		inner.id = "innerOvlay";
		inner.className = "hidden";
		btnLine.className = "topBorder btnLine";
		btn.className = "btn dark singleOnLine"; // the only button on a line
		btn.innerHTML = closeBtnText;
		xAddEventListener(btn, selEvt, fnRemoveOL, false);
		
		btnLine.appendChild(btn);
		content.appendChild(btnLine);
		inner.appendChild(content);
		layer.appendChild(inner);
		document.body.appendChild(layer);
		// remove class hidden to start transition effect (needs a little delay)
		setTimeout(function(){inner.className = "";}, 5);
	};
	
	// init some HTML Elements...
	dots.innerHTML = "..."; // dots element for reduced state
	dots.id = "reduceDots";
	
	// toggle button
	toggleBtn.id = "toggleBtn";
	toggleBtn.className = "btn toggleBtn dark";
	toggleBtn.innerHTML = "<img alt=\"\" src=\"img/arrow_down.png\" />";
	// add event listener to toggle button
	xAddEventListener(toggleBtn, selEvt, fnToggleDMode, false);
	
	
	// === error and debug methods and similar things... (public) ===
	/**
	 * Will show an error message on the gui, indicating
	 * that some software error has occurred. Should be invoked,
	 * when the result may be incorrect due to a program error.
	 * Note: This method is not for development logs, but
	 * for error notification to the USER, even in
	 * the productive state.<br />
	 * This method throws an error that shouldn't be handled anywhere, so
	 * it executes the script
	 * @method progError
	 * @param {String} msg The message to log to the console as error message. (The
	 * user won't see this message! So it may be verbose...)
	 */
	obj.progError = function progError(msg){
		var err;
		this.dbgErr("program error: " + (msg ? msg : ""));
		// reset the model, as we don't know what happend, and if the result is correct...
		model.resetModel();
		// generate an error to inform the user (not the verbose info of msg
		err = new ClassedError("A program error has occurred... please retry", ClassedError.types.progError);
		err.handleErr();
		throw ''; // exit further execution
	};
	
	/**
	 * This is not an error, but can be used to inform the
	 * user without doing anything else.
	 * @method showHint
	 * @param {string / DocumentFragment} msg The message showed to the user.
	 * @param {string} closeBtnText Optional. The Text of the close button
	 * of the hint dialog. If omitted, the Text defaults to "Close".
	 */
	obj.showHint = function showHint(msg, closeBtnText){
		var el;
		closeBtnText = closeBtnText ? closeBtnText : "Close";
		if (msg){
			el = document.createElement('div');
			if (typeof(msg) === 'string'){
				el.innerHTML = msg;
			} else {
				// msg should be a document fragment
				el.appendChild(msg);
			}
			fnOverlay(el, closeBtnText);
		}
	};
	
	/**
	 * Debug an Error. If it will really be showed and where it will be showed
	 * is defined by the properties of the global config object.
	 * @method dbgErr
	 * @param {String} msg The message to show.
	 */
	obj.dbgErr = function dbgErr(msg){
		debug(msg, 1);
	};
	
	/**
	 * Debug a Warning or non-fatal error. If it will really be showed and
	 * where it will be showed is defined by the properties of the global
	 * config object.
	 * @method dbgWarn
	 * @param {String} msg The message to show.
	 */
	obj.dbgWarn = function dbgWarn(msg){
		debug(msg, 2);
	};
	
	/**
	 * Debug a log message. If it will really be showed and where it will be showed
	 * is defined by the properties of the global config object.
	 * @method dbgLog
	 * @param {String} msg The message to show.
	 */
	obj.dbgLog = function dbgLog(msg){
		debug(msg, 3);
	};
	
	// === update methods ====
	
	/**
	 * Call this method to inform the gui, that the model
	 * has changed. This method will then look for the needed
	 * information and update the gui.
	 * @method update
	 */
	obj.update = function(){
		var state = model.getState();
		var stack = model.getData();
		var cmp;
		if (model.regsDirty()){
			cmp = model.compareRegs();
			if (cmp !== null)
				this.updateCmp(cmp);
		}
		if (mDisContent !== stack){
			mDisContent = stack;
			this.updateMainDisplay('<div>' + stack + '</div>');
		}
		this.updateKeys(state);
		oGui.dbgLog("Model State: " + model.getState(true));
	};
	
	/**
	 * Update the keys (depending on the model state given
	 * as argument, some keys may be set inactive)
	 * @method updateKeys
	 * @param {model.enumState} state The state of the model
	 * @private
	 */
	obj.updateKeys = function (state){
		var keys = keypad;
		var eS = model.enumState;
		
		var resultLine; // parent of bsKey
		var clearKey = xGetElementById('clearKey');
		
		var inact = []; // array with elements to set inactive
		var ta; // temporary array
		var i, l; // index and length of ta
		
		// remove keys from DOM while changing classes to improve performance (by avoiding layout reflows)
		keys.parentNode.removeChild(keys); // all normal keys
		resultLine = bsKey.parentNode;
		resultLine.removeChild(bsKey); // the backspace key (not in the keys div)
		
		// helper functions
		var setInactive = function(key){
			xAddClass(key, 'inactive');
		};
		var setActive = function(key){
			xRemoveClass(key, "inactive");
		};
		var setAllActive = function(){
			ta = xGetElementsByClassName("btn", keys, "span");
			l = ta.length;
			for (i=0; i<l; i++){
				setActive(ta[i]);
			}
			setActive(bsKey);
		};
		
		// type: "c" for clear, "ac" for all-clear
		var setClearText = function(type){
			var current = clearKey.getAttribute('data-cmd');
			if (type === 'ac' && current !== 'clrall'){
				clearKey.innerHTML = '<span>AC</span>';
				clearKey.setAttribute('data-cmd', 'clrall');
			} else if (type === 'c' && current !== 'clear') {
				clearKey.innerHTML = '<span>C</span>';
				clearKey.setAttribute('data-cmd', 'clear');
			}
		};
		
		// type: "s" for STO or "r" for RCL
		var setStoRegText = function(type){
			var stoRcl;
			var len, i;
			var fn = function(el){
				var str;
				if (type === "s"){
					str = "STO";
					xAddClass(el, "sto");
					xRemoveClass(el, "rcl");
				} else {
					str = "RCL";
					xAddClass(el, "rcl");
					xRemoveClass(el, "sto");
				}
				el.firstChild.innerHTML = str;
			};
			stoRcl = xGetElementsByClassName("sto", keys, "span");
			len = stoRcl.length;
			if (len === 2 && type === "r"){
				// iterate: change to RCL
				for (i = len-1; i >= 0; i--){
					fn(stoRcl[i]);
				}
			} else if (type === "s" && len === 0) {	 // iterate: change to STO
				stoRcl = xGetElementsByClassName("rcl", keys, "span");
				len = stoRcl.length;
				for (i=len-1; i>=0; i--){
					fn(stoRcl[i]);
				}
			}
		};
		
		// reset: everything is active
		setAllActive();

		// depending on number of open brackets set calc or rightBracket to inactive
		if (model.getNumOfOpenBr() <= 0) {
			ta = xGetElementsByClassName("rightBra", keys, "span");
		} else {
			ta = xGetElementsByClassName("calc", keys, "span");
		}
		inact = inact.concat(ta);
		
		// text of clear button
		if (state !== eS.init){
			setClearText("c");
		}
		// backspace button
		if (state === eS.init || state === eS.calc){
			setInactive(bsKey);
		} // else active (is already active, since setAllActive was called...

		switch(state){
		case eS.init:
			inact = inact.concat(
					xGetElementsByClassName("optor", keys, "span"),
					xGetElementsByClassName("cmp", keys, "span"),
					xGetElementsByClassName("calc", keys, "span") );
			if (model.regsEmpty()){
				setInactive(clearKey);
			} else {
				setClearText("ac");
			}
			setStoRegText("r");
			break;
		case eS.digit:
			inact = inact.concat(
					xGetElementsByClassName("const", keys, "span"),
					xGetElementsByClassName("leftBra", keys, "span") );
			setStoRegText("s");
			break;
		case eS.optor:
			inact = inact.concat(
					xGetElementsByClassName("optor", keys, "span"),
					xGetElementsByClassName("rightBra", keys, "span"),
					xGetElementsByClassName("calc", keys, "span") );
			setStoRegText("r");
			break;
		case eS.constant:
		case eS.rightBracket:
			inact = inact.concat(
					xGetElementsByClassName("const", keys, "span"),
					xGetElementsByClassName("num", keys, "span"),
					xGetElementsByClassName("leftBra", keys, "span") );
			setStoRegText("s");
			break;
		case eS.calc:
			inact = inact.concat( xGetElementsByClassName("calc", keys, "span") );
			setStoRegText("s");
			break;
		case eS.leftBracket:
			inact = inact.concat(
					xGetElementsByClassName("optor", keys, "span"),
					xGetElementsByClassName("rightBra", keys, "span") );
			setStoRegText("r");
			break;
		case eS.stored:
			setStoRegText("r");
			break;
		default:
		}
		
		// set all elements from array inact to state 'inactive'
		l = inact.length;
		for (i=l-1; i>=0; i--){
			setInactive(inact[i]);
		}
		// re-insert elements into dom
		wrapAll.appendChild(keys);
		resultLine.insertBefore(bsKey, outerMainDisplay);
	};
	
	/**
	 * Method to update the compare line to show the
	 * relationship between the two registers.
	 * @method updateCmp
	 * @param {DocumentFragment} cmpDocFrag The document fragment containing
	 * the content of the compare line
	 * @private
	 */
	obj.updateCmp = function updateCmp(cmpDocFrag){
		var l, r; // left and right operand
		var el;
		var i;
		
		// register fields (labels)
		var fields = xGetElementsByClassName("rField", cmpDisplay, "div");
		
		l = cmpDocFrag.childNodes[0];
		r = cmpDocFrag.childNodes[2];
		
		cmpDisplay.innerHTML = "";
		for (i=fields.length-1; i>=0; i--){
			cmpDocFrag.insertBefore( fields[i], cmpDocFrag.firstChild );
		}
		cmpDisplay.appendChild(cmpDocFrag);
		
		// check height of left operand:
		if (l.firstChild.offsetHeight > cmpDisplay.offsetHeight){
			oGui.dbgLog("left operand needs to much space");
			// overflow: signalise overflow
			el = document.createElement('div');
			el.innerHTML = "...";
			el.className = "cmpDots";
			cmpDisplay.insertBefore(el, cmpDisplay.childNodes[3]);
		}
		if (r.firstChild.offsetHeight > cmpDisplay.offsetHeight){
			oGui.dbgLog("right operand needs to much space");
			// overflow: signalise overflow
			el = document.createElement('div');
			el.innerHTML = "...";
			el.className = "cmpDots";
			cmpDisplay.appendChild(el);
		}

	};
	
	/**
	 * Update the result field's content
	 * @method updateMainDisplay
	 * @param {string} content The new content (innerHTML) to be set
	 * @private
	 */
	obj.updateMainDisplay = function updateMainDisplay(content){
		var actOverflow; // overflow detected now
		var stateAugm = false;
		mainDisplay.innerHTML = content;
		// check overflow...
		if (xHasClass(outerMainDisplay, 'augmented')){
			// the div element must be in normal state (overflow: hidden)
			// to ckeck the overflow (use clipped height of outer div).
			xRemoveClass(outerMainDisplay, 'augmented');
			stateAugm = true;
		}
		// offsetHeight includes borders, clientHeight not (but padding)
		actOverflow = mainDisplay.offsetHeight > outerMainDisplay.clientHeight;
		if (stateAugm){ // re-insert class augmented
			xAddClass(outerMainDisplay, 'augmented');
		}
		if (actOverflow){
			if (!bOverflow){
				// overflow is new
				oGui.dbgLog("overflow in mainDisplay");
				// handle overflow
				fnToggleDMode();
				bOverflow = true;
			}
		} else {
			// no overflow
			if (bOverflow){ // overflow disappeared..
				fnToNormalSizeDisp();
				bOverflow = false;
			}
		}
	};
	
	// add event listeners
	xAddEventListener(keypad, selEvt, fnKey, false);
	xAddEventListener(bsKey, selEvt, fnKey, false);
	// when a key is pressed down, change css class to "aktiv"-style (class "btnHover")
	xAddEventListener(wrapAll, downEvt, function (evt){
		// init
		var target;
		if (!evt) {
			evt = window.event; // IE
		}
		target = evt.target || evt.srcElement;
		// perhaps the user clicked on the inner span element: set target to outer span
		if ( !xHasClass(target, 'btn') ){
			target = target.parentNode;
			if (!xHasClass(target, 'btn')) return; // the original target was an outer element...
		}
		if (xHasClass(target, 'inactive')){
			return;
		}
		xAddClass(target, 'btnHover');
	}, false);
	// open the information dialog when 'about' button is pressed
	xAddEventListener(xGetElementById('aboutLink'), selEvt, function(){
		var msg = document.createDocumentFragment();
		var btn = document.createElement('a');
		var p = document.createElement('p');
		var sub = document.createElement('sub');
		sub.innerHTML = "0";
		
		// p element
		p.innerHTML = "Ordinal Calculator, Version " + config.projectVersion + "<br />A simple calculator that performs arithmetic operations with ordinal numbers up " +
				"to (but not including) &epsilon;";
		p.appendChild(sub);
		p.appendChild( document.createTextNode('.') );
		p.className = "leftAlign bottomSpace";
		msg.appendChild(p);
		
		// link to project site
		btn.className = "btn dark";
		btn.href = config.projectUrl;
		btn.target = "_blank";
		btn.innerHTML = "Go to project site";
		msg.appendChild(btn);

		oGui.showHint(msg);
	}, false);
	// compare display: show popup
	xAddEventListener(cmpDisplay, selEvt, fnPopupCompare, false);

	// determine which console object should be used (if any) and
	// set it to oGui.out...
	if (config.debug <= 0){
		// productive state, no logs
		obj.out = null; // oGui.debug checks if oGui.out is null
	} else if (config.prohibitOwnConsole) {
		if (console && console.log && console.warn && console.error){
			// browser console available. Because an own console is
			// prohibited anyway, the property config.useOwnConsole doesn't matter
			obj.out = console;
		} else {
			// browser console doesn't exits and an own object isn't allowed:
			// logs will be trashed
			obj.out = null;
		}
	} else {
		// logging required...
		if (config.useOwnConsole || !console ||
				!console.log || !console.warn || !console.error){
			initGuiConsole();
		} else {
			// browser console exist
			obj.out = console;
		}
	}
	
	// return the created object
	return obj;
};