// Defining some JSLint options (http://www.jslint.com/lint.html)
/*global $, Ape, console */
/*jslint nomen: true, debug: true, evil: true, onevar: true, 
 		 bitwise: true, browser: true, passfail: false */

if (typeof Ape === 'undefined' || !Ape) {
	
/**
 * Single static object. This is our Namespace.
 * Everything we do outside a closure we do here!
 *
 * @class Ape
 * @static
 * 
 * @author Sebastian Müller <mueller@vonaffenfels.de>
 * @author Sven Strittmatter <strittmatter@vonaffenfels.de>
 */
var Ape = {
	version:	1.0,
	vendor:		'www.vonaffenfels.de',
	/**
	 * This variable is used as a global storage. Here 
	 * you ca nsave variables which are acces from outsid
	 * your closure.
	 * 
	 * Example:
	 * If you have some code in your closure which need to be accessed
	 * by an other piece of code in an other closure. Put it in Ape.global.
	 * 
	 * Your closure:
	 * <code>
	 * (function() {
	 * 		Ape.global.communityElement = new Ape.Element.Community();
	 * 		// ... your stuff here
	 * })();
	 * <code>
	 * 
	 * Some other closure now can access your object in an other closure 
	 * like this:
	 * <code>
	 * (function() {
	 * 		// ... 
	 * 
	 * 		// You can access the Element-Object which was
	 * 		// created in an other closure
	 * 		Ape.global.communityElemen.showLogin();
	 * 
	 * 		// ... 
	 * })();
	 * <code>
	 * 
	 * ATTENTION:
	 * Please be carefull and do not OVERWRITE properties in Ape.global from others!
	 */
	globals: 	{},
	isDebugingEnabled: false,
	
	/**
	 * @link http://yuiblog.com/blog/2006/09/26/for-in-intrigue/
	 */
	hasProperty: function(o, name) {
		if (typeof (o.hasOwnProperty) === 'function') {
			return o.hasOwnProperty(name);
		} else {
			return typeof o[name] !== 'function';
		}
	},
	
	isConsoleAvailable: function() {
		if (0 === arguments.length) {
			return (typeof console === 'object');
		} else if (1 === arguments.length) {
			return ( (typeof console === 'object') && (typeof console[arguments[0]] === 'function') );
		} else {
			throw new Error('Please use this function with no or one argument!');
		}
	},
	
	debug: function(msg) {
		if (this.isDebugingEnabled) {
			if (this.isConsoleAvailable('debug')) {
				console.debug(msg);
			} else if ($.browser.msie) {
				this.MsIe.debug(msg);
			}
		}
	},
	
	log: function(msg) {
		if (this.isDebugingEnabled) {
			if (this.isConsoleAvailable('log')) {
				console.log(msg);
			} else if ($.browser.msie) {
				this.MsIe.log(msg);
			}
		}
	},
	
	/**
	 * @link http://www.gettingclever.com/2008/06/javascript-stacktrace.html
	 */
	 stacktrace: function() {
		var regEx		= /function\W+([\w\-]+)/i,  
			func		= arguments.callee.caller,  
			stacktrace	= '',
			i;
		
		while (func) {  
			stacktrace += (regEx.exec(func))[1] + '(';   
   
				for (i = 0; i < func.arguments.length - 1; i++) {  
					stacktrace += '\'' + func.arguments[i] + '\', ';  
				}  
   
				if (arguments.length > 0) {  
					stacktrace += '\'' + func.arguments[i] + '\'';  
				}  
   
				stacktrace += ')\n\n';  
   
				func = func.arguments.callee.caller;  
			}  
			
			if ($.browser.msie) {
				this.log('<strong>stacktrace:</strong>');
			this.log('<pre>' + stacktrace + '</pre>');
		} else {
			this.log('stacktrace:');
			this.log(stacktrace);
		}
	},
	
	/**
	 * Inspired by !YUI
	 */
	namespace: function() {
		var a = arguments, 
			o = null, 
			i, j, d;
		
	    for (i = 0; i < a.length; i = i + 1) {
	        d = ('' + a[i]).split('.');
	        o = Ape;

	        // Ape is implied, so it is ignored if it is included
	        for (j = (d[0] == 'Ape') ? 1 : 0; j < d.length; j = j + 1) {
	            o[d[j]] = o[d[j]] || {};
	            o = o[d[j]];
	        }
	    }

	    return o;
	},
	
	/**
	 * @link http://unscriptable.com/index.php/2009/03/20/debouncing-javascript-methods/
	 */
	debounce: function (func, threshold, execAsap) {
	    var timeout; 	 // handle to setTimeout async task (detection period)
	    
	    /*
	     * return the new debounced function which executes the 
	     * original function only once until the detection period expires.
	     */
	    function debounced() {
	        var obj  = this, 	  // reference to original context object
	            args = arguments; // arguments at execution time
	        
	        /*
	         *  this is the detection function. it will be executed 
	         *  if/when the threshold expires
	         */
	        function delayed () {
	            // if we're executing at the end of the detection period
	            if (!execAsap) {
	                func.apply(obj, args); // execute now
	            }
	            
	            // clear timeout handle
	            timeout = null;
	        }
	        
	        // stop any current detection period
	        if (timeout) {
	            clearTimeout(timeout);
	        } else if (execAsap) {
	        	// otherwise, if we're not already waiting and we're executing at the beginning of the detection period
	            func.apply(obj, args); // execute now
	        }
	        
	        // reset the detection period
	        timeout = setTimeout(delayed, threshold || 100);
	    }
	    
	    return debounced;
	},
	
	parseQueryString: function(str) {
		var searchParams;
//		searchParams.length = 0;

		if ('?' === str[0]) {
			str = str.substr(1);
		}
		
		$.each(str.split('&'), function(index, item) {
			var keyValue;

            if (item) {
                if (!searchParams) {
                    searchParams = {};
                }
                
                keyValue = item.split('=');
                searchParams[keyValue[0]] = keyValue[1];
            }
		});
		
		return searchParams;
	}
};
	 
 /**
  * Ein mehr schlecht als rechter Ersatz fuer Firebug auf dem MSIE.
  * 
  * Besser als gar nichts ;.)
  * 
  * @class MsIe
  * @static
  * 
  * @author Sven Strittmatter <stritmatter@vonaffenfels.de>
  */
Ape.MsIe = {
	loggedLines: 0,
	logWindow: null,
	
	write: function(line) {
		if (null === this.logWindow) {
			this.logWindow = window.open('about:blank', '_blank', 'width=600,height=500,dependent=yes,resizable=yes,scrollbars=yes');
			this.logWindow.document.writeln('<style>\n/* <![CDATA[ */');
			this.logWindow.document.writeln('\tdiv { border-style: solid; border-width: 1pt; margin-left: 10pt; padding: 2pt; }');
			this.logWindow.document.writeln('\t.visible { display: block }');
			this.logWindow.document.writeln('\t.invisible { display: none }');
			this.logWindow.document.writeln('/* ]]> */\n</style>');
			this.logWindow.document.writeln('<script type="text/javascript">\n/* <![CDATA[ */');
			this.logWindow.document.writeln('function deeper(a) {');
			this.logWindow.document.writeln('\ta.className = (a.className == "visible") ? "invisible" : "visible" ;');
			this.logWindow.document.writeln('}\n');
			this.logWindow.document.writeln('/* ]]> */\n</script>');
			this.logWindow.document.writeln('<strong>Debug-Konsole:</strong><br/>');
			this.loggedLines++;
		}
		
		this.logWindow.document.writeln(line);
		this.loggedLines++;
		
		if (this.loggedLines > 14) {
			this.logWindow.scrollTo(0, (500 + this.loggedLines * 50));
		}
	},
	
	log: function(msg) {
		this.write('<p class="log">' + msg + '</p>');
	},
	
	/**
	 * @link http://www.gettingclever.com/2008/06/javascript-object-inspector.html
	 */
	debug: function(o, level) {
		if (level === undefined) {
			this.write('<div class="visible">');
		} else {
			this.write('<div class="invisible">');
		}
		
		if (o === undefined) {
			this.write('<p class="debug">(undefined)</p>');
		} else if (typeof (o) === 'object') {
			if (o instanceof Array) {
				this.write('<p class="debug">(array) length=' + o.length + '</p>');
			} else {
				this.write('<p class="debug">(object)</p>');
			}
			
			for (var i in o) {
				if (Ape.hasProperty(o, i)) { // http://yuiblog.com/blog/2006/09/26/for-in-intrigue/
					if (o[i] === undefined) {
						this.write('<p class="debug">['+ i +'] = undefined</p>');
					} else if (typeof (o[i]) === 'object') {
						if ((i != 'parentNode') && (i != 'parentElement') && (i != 'nextSibling') &&   
							(i != 'previousSibling') && (i != 'ownerDocument') && (i != 'offsetParent') &&   
							(i != 'ownerElement') && (i != 'document') && (i != 'namespaces') && (i != 'parentTextEdit'))
						{
							this.write('<p class="debug" onclick="deeper(this.nextSibling);">['+ i +'] = ' + o[i] +'</p>');
							this.debug(o[i], 1);
						}
					} else if (typeof (o[i]) === 'function') {
						this.write('<p class="debug">['+ i +'] = function()</p>');
					} else {
						this.write('<p class="debug">['+ i +'] = ' + o[i] +'</p>');
					}
				}
			}
		} else if (typeof (o) == 'string') {
			this.write('<p class="debug"><strong>' + o + '</strong> (string) length=' + o.length + '</p>');
		} else {
			this.write('<p class="debug"><strong>' + o + '</strong> (' + typeof (o) + ')</p>');
		}
		
		this.write('</div>');
	}
};

}

Ape.namespace('Element');