/*
 * jso      JavaScript Object
 * jsd      JavaScript Data (template parameter)
 * jst      JavaScript Template (prefix for classes for templates and placehodlers for externals)
 * jsid     JavaScript ID (for receiving configuration and for unique ids e.g. for labels)
 * jsc      JavaScript CSS (for css styling)
 * jsp      JavaScript Prameter?
 *
 */

//TODO nich immer noConflict --> falls die Seite jQuery schon nutzt, is das doof ...
// jQuery.noConflict();
 
var _iGlobalId = 0;

/*
 * Hmmmm .... ne $.fn.session(sName, sValue) wie .data, aber nur für strings türlich
 * Und eben aus document.location + this.attr(id) + sName ein cookie-namen bilden
 * Ggf. document.location nicht rein, je nachdem was der User will? --> Global für die Seite ...
 *
 */
window.cookieFunc = {
    createCookie: function(name,value,days) {
      value = escape(value);//value.replace(/;/g, '\\\\;');
      
    	if (days) {
    		var date = new Date();
    		date.setTime(date.getTime()+(days*24*60*60*1000));
    		var expires = "; expires="+date.toGMTString();
    	}
    	else var expires = "";
    	document.cookie = name+"="+value+expires+"; path=/";
    },
    
    readCookie: function(name) {
    	var nameEQ = name + "=";
    	var ca = document.cookie.split(';');
    	for(var i=0;i < ca.length;i++) {
    		var c = ca[i];
    		while (c.charAt(0)==' ') c = c.substring(1,c.length);
    		if (c.indexOf(nameEQ) == 0) return unescape(c.substring(nameEQ.length,c.length));
    	}
    	return null;
    },
    
    eraseCookie: function(sName) {
        window.cookieFunc.createCookie(sName, '', -1);
    }
}


if ( ! String.prototype.parseUrl ) {

/**
 * Considers the string object as URL and returns it's parts separately
 *
 * @param void
 * @return Object
 * @access public
 */
String.prototype.parseUrl = function()
{
    var matches = this.match(arguments.callee.re); //TODO arguments.callee?

    if ( ! matches ) {
        return null;
    }

    var result = {
        'scheme': matches[1] || '',
        'subscheme': matches[2] || '',
        'user': matches[3] || '',
        'pass': matches[4] || '',
        'host': matches[5],
        'port': matches[6] || '',
        'path': matches[7] || '',
        'query': matches[8] || '',
        'fragment': matches[9] || ''};

    return result;
};

String.prototype.parseUrl.re = /^(?:([a-z]+):(?:([a-z]*):)?\/\/)?(?:([^:@]*)(?::([^:@]*))?@)?((?:[a-z0-9_-]+\.)+[a-z]{2,}|localhost|(?:(?:[01]?\d\d?|2[0-4]\d|25[0-5])\.){3}(?:(?:[01]?\d\d?|2[0-4]\d|25[0-5])))(?::(\d+))?(?:([^:\?\#]+))?(?:\?([^\#]+))?(?:\#([^\s]+))?$/i;

}

if ( ! String.prototype.parseQuery ) {
    String.prototype.parseQuery = function() {
        var aPairs = this.parseUrl()['query'].split("&");
        var aParams = new Object();
        
        for (iI = 0; iI < aPairs.length; iI++) {
             var aPair = aPairs[iI].split("=");
             
             aParams[aPair[0]] = aPair[1];
        }
        
        return aParams;
    }
}

//TODO add String.prototpye.parseToFloat --> also replace '.' with '', ',' with '.'
Number.prototype.formattedStr = function() {
    if (this >= Infinity) return '&infin;';
    
    var fNum = String(Math.round(this * 100) / 100).replace(/\./, ',');
    
    var aNum = String(fNum).split(',');
    var iI   = 3;
    
    while (iI < aNum[0].length) {
        aNum[0] = aNum[0].substr(0, aNum[0].length - iI) + '.' + aNum[0].substr(aNum[0].length - iI)
        iI += 4;
    }
    
    if (aNum.length == 1) aNum[1] = '';
    else {
        if (aNum[1].length == 1) aNum[1] = aNum[1] + '0';
        
        aNum[1] = ',' + aNum[1];
    }
    
    return aNum[0] + aNum[1];
}


jQuery.fn.closestChildren = function(sExpr) {
    var aRtn = new Array();
    
    this.children().each(function() {
        var jE = jQuery(this);
        
        if (jE.filter(sExpr).length) {
            aRtn.push(this);
        }
        else {
            aRtn = jQuery.merge(aRtn, jE.closestChildren(sExpr));
        }
    });
    
    return jQuery(aRtn);
};

jQuery.fn.XXXouterHTML = function(s) {
    //return (s)
    //    ? this.before(s).remove()
    //    : jQuery("<p>").append(this.eq(0).clone()).html();
    
    
    return this.html(); //jQuery(document.createElement('span')).append(this.eq(0).clone()).html();
}
jQuery.fn.outerHTML = function(s) {
    var p = document.createElement('div');
    var c = this.eq(0).clone();
    p.appendChild(c[0]);
    return (s) 
        ? this.before(s).remove() 
        : p.innerHTML;
}

jQuery.fn.getObj = function() {
    return this.data('obj');
}

jQuery.fn.getRootLine = function() {
    var aRL = new Array();
    var oC  = this.parent();
    
    while(oC.length) {
        var sElem = oC.get(0).nodeName;
        
        if (oC.attr('id'))    sElem += '#' + oC.attr('id');
        if (oC.attr('class')) sElem += '.' + oC.attr('class');
        
        aRL.push(sElem);
        oC = oC.parent();
    }
    
    return aRL.join(' > ');
}

jQuery.getScript = function(url, callback, cache, scriptCharset) {
	jQuery.ajax({
        type: "GET",
        url: url,
        success: callback,
        dataType: "script",
        cache: cache,
        scriptCharset: scriptCharset
    });
}




jQuery.extend(jQuery.template.helpers, {
    'if': function(sFunc, aValue, aValues) {
        //for (var iI in this) alert(iI + ': ' + this[iI]);
        //alert(sFunc);
        //alert(aValue);
        //alert(aValues);
    },
    
    'for': function() {
        
    },
    
    'wrap': function(sValue, sBefore, sAfter, bRequired, sAlt) {
        var bReq  = bRequired ? true : false;
        
        if (sValue || !bReq) sValue = sBefore + sValue + sAfter;
        else if (sAlt)       sValue = sAlt;
            
        return sValue;
    },
    
    'numberFormat': function(sValue, sZero) {
        var fVal = parseFloat(sValue);
        
        if (!fVal && sZero) return sZero;
        else {
            return fVal.formattedStr();
        }
    },
    
    'nl2br': function() {
        // Set \nn to <p></p>, \n to <br />
        // In PHP from Typo3 RTE -> \n to \n\n, <br /> to \n ... or don't use RTE at all?
        // How to format stuff? See what the RTE http://www.themaninblue.com/experiment/widgEditor returns!
    }
});









window.jso = new Object();
//window.jso_form_data = new Object();
//window.jso_form_data['ALT'] = 'adsasdas';

if (!window.jso_form_data) window.jso_form_data = new Object();

// Base class for all
window.jso[''] = function(oUpdate, bAjax, sHtml, fId) {
    this.jE         = undefined;
    this.sPath      = undefined;
    this.sObjPath   = undefined;
    this.oChildren  = undefined;
    this.oExternals = new Object();
    
    var that = this;
    
    var oMessages   = new Object();
    var aMsgHandles = new Array();
    
    var oProtected = new Array();
    this.iObjs     = 0;
    
    var oJSDs = undefined;
    
    var _iObjId = ++_iGlobalId;
    
    this.getId = function() {
        return _iObjId;
    }
    
    this.$ = function(fObjId, iObjId) {
        //if (sName) {
        //    alert('Trying to access a protected variable "'+sName+'". Accessing and setting protected variables is done with "$ = this.$(); $(\'protectedVar\', \'new value\'); alert($(\'protectedVar\'));"');
        //    return undefined;
        //}
        
        //var fId = Math.random();
        //iObjs++;
        
        if (fObjId != fId) return function(sName, xxValue, oObj) { alert('Wrong ID'); return null; };
        
        return function(sName, xxValue, oObj) {
            var iObj = iObjId;
            alert(iObj);
            // oObj -- get for special variable, like this/that or super, or super.super ;)
            // How to find out the corresponding iObjId?
            
            if (xxValue) {
                if (!oProtected[iObjId]) oProtected[iObjId] = new Object();
                
                oProtected[iObjId][sName] = xxValue;
            }
            else {
                while (!oProtected[iObj] && iObj > 0) iObj--;
                
                if (oProtected[iObj]) {
                    while (!oProtected[iObj][sName] && iObj > 0) iObj--;
                    
                    return oProtected[iObj][sName] ? oProtected[iObj][sName] : null;
                }
                else return null;
            }
        };
        
        // Params: fId
        // Class params: super, fId
        // if fIds match -> return function to get/set protected
        // every prototype gets another id für oProtected array
        // if function is called without oObj, get first occurence of var above iObj
        // if oObj given, find out iObj of oObj (or only possible to get var of super class?)
        // return value as eval(), to write iObjs down?
    }
    
    //
    //TODO
    //
    // this.childrenLoaded, this.childLoaded, this.childReady, this.childrenReady defaultmäßig setzen
    // Kann ja überschrieben werden - macht halt nix, nur um den check if this.childLoaded zu sparen!
    
    // initElements sets bAjax -> only setChildLoaded if ASYNC call!!
    if (bAjax) {
        oUpdate.setChildLoaded(this.jE);
    }
    
    var iLoading = 0;
    var iBusy    = 0;
    
    this.setLoading = function(iSetLoading) { //TODO pass private random() val to initElements, which passes this here to check?
        iLoading = iSetLoading;
    }
    
    this.setChildLoaded = function(jChild) {
        // if iLoading > 0
        // check with oChildren.jE == jChild
        // iLoading--;
        // this.childLoaded(jChild);
        // if iLoading == 0 { this.childrenLoaded();
    }
    
    this.setChildReady = function(jChild) {
        // check with oChildren.jE == jChild
        // iBusy--;
        // this.childReady(jChild);
        // if iBusy == 0 {
        //  this.childrenReady();
        //  oUpdate.setChildReady(this.jE)
    }
    
    
    var oReady = new Array();
    
    this.childrenReady = function(oCallback) {
        oReady.push(oCallback);
    };
    
    this.parentReady = function(oCallback) {
        oUpdate.childrenReady(oCallback);
    };
    
    var childrenReady = function() {
        for (var iI = 0; iI < oReady.length; iI++) {
            oReady[iI](that);
        }
    };
    
    
    //TODO
    this.initializeChildren = function() {
        // als dritter parameter this.jE (oE) übergeben
        this.oChildren = window.aes.initElements(this.jE, this.sPath, 0);
        
        
        //iBusy = this.oChildren.length;
        
        // No jso_* - children
        if (iBusy == 0) {
            //this.childrenReady();
            //this.childrenLoaded();
            
            // oUpdate.setChildReady(this.jE);
        }
        
        // No ajax loaded children!
        if (iLoading == 0) {
            //this.childrenLoaded();
        }
        
        //TODO Inwieweit nötig, wenn keine Objekte per AJAX geladen werden?
        childrenReady();
        
        return '';
    }
    
    //TODO
    this.initializeElements = function(jElems) {
        this.oChildren = window.aes.initElements(this.jE, this.sPath, jElems.filter(window.aes.gP('jso')));
        childrenReady();
    }
    
    //TODO - not this.oChildren!
    this.initializeNextChildren = function() {
        // ggf. tiefere Level durchsuchen, falls im ersten kein jso_* gefunden
        this.oChildren = window.aes.initElements(this.jE, this.sPath, 2);
        childrenReady();
    }
    
    //TODO - not this.oChildren!
    this.initializeAllChildren = function() {
        // --> find()
        this.oChildren = window.aes.initElements(this.jE, this.sPath, 1);
        childrenReady();
    }
    
    this.initializeByParam = function() {
        var oParams = this.getParams();
        
        if (oParams.parseJSDs) this.parseJSDs();
        else if (oParams.all)  this.initializeAllChildren();
        else if (oParams.next) this.initializeNextChildren();
        else                   this.initializeChildren();
    }
    
    //TODO
    this.childObjs = function(sFilter) {
        // alle jso_* children() objekte (also nich die html elemente, direkt die objekte)
        // sExtraFilter an jso_[sExtraFilter]
        
        return window.aes.getObjs(this.jE.children(window.aes.gP('jso', sFilter)));
    }
    
    //TODO
    this.findObjs = function(sFilter) {
        // alle jso_* find() objekte (also nich die html elemente, direkt die objekte)
        // sExtraFilter an jso_[sExtraFilter]
        
        return window.aes.getObjs(this.jE.find(window.aes.gP('jso', sFilter)));
    }
    
    this.callerObj = function() {
        return oUpdate;
    }
    //TODO callerObj or parentObj? Which is better?
    this.parentObj = function() {
        //TODO this.jE.parents(window.aes.gP('jso')).each() -> if getObj != undefined -> return obj
        //     ... or even better - .closest(window.aes.gP('jso'))
        return window.aes.getObj(this.jE.parent());
    }
    
    //TODO
    this.replaceTag = function(sNewTag, aAttrs) {
        if (!aAttrs) aAttrs = new Array();
        
        // First create new node, to place it after the old one within the DOM tree
        var jNew = jQuery(document.createElement(sNewTag)).insertAfter(this.jE).append(this.jE.children());
        var jOld = this.jE.remove();
        
        this.jE = jNew.attr({ 'id': jOld.attr('id'), 'class': jOld.attr('class'), 'style': jOld.attr('style') });
        this.oE = this.jE.get(0);
        
        //TODO: check if aAttrs contains id/class --> remove
        for (var iI = 0; iI < aAttrs.length; iI++) this.jE.attr(aAttrs[iI], jOld.attr(aAttrs[iI]));
        
        jQuery.data(this.oE, 'obj', this);
        
        // Tag ersetzen durch sNewTag (zB select -> div, falls options durch checkboxen)
        // oAttrs = attribute die zusätzlich zur id (falls vorhanden) und jso_* class übernommen werden sollen
        //   oAttrs.class --> alle classes, nich nur jso
    }
    
    this.getXML = function() {
        return sHtml;
    }
    
    this.registerMessage = function(sMsg, oCb) {
        if (!oMessages[sMsg]) oMessages[sMsg] = new Array();
        
        oMessages[sMsg].push(oCb);
    }
    
    /* should be a protected method! */
    this.sendMessage = function(sMsg, oData) {
        var aRtn = new Array();
        
        if (oMessages[sMsg]) {
            if (typeof oData == 'undefined') oData = {};
            
            //TODO auch that übergeben? Bzw. window.aes.getObj(that.jE), damit es def. die "aktuellste" Var. is
            //     Rückgabe checken -> if (xRtn) aRtn.push(xRtn)!!
            for (var iI = 0; iI < oMessages[sMsg].length; iI++) aRtn.push(oMessages[sMsg][iI](sMsg, oData));
        }
        
        return aRtn;
    }
    
    //TODO was z.B. bei input FormText - registriert selbst beim parent "validate",
    //     für Form aber automatisch auch, bei den inherited ... naja, is dann halt zweimal drin,
    //     aber das Inherit halt unnötig, da das Formularding keine Kinder hat (außer div mit checkboxen o.ä.)
    this.addInheritMessage = function(sMsg) {
        aInheritMessages.push(sMsg);
    }
    this.getInheritMessages = function() {
        return aInheritMessages;
    }
    
    // hmmm ...?
    var messageHandler = function(sMsg, oData) {
        that.sendMessage(sMsg, oData);
    }
    this.handleMessage = function(oObj, sMsg) {
        oObj.registerMessage(sMsg, messageHandler);
    }
    
    
    
    
    this.getObjName = function() {
        return this.sPath.substr(this.sPath.lastIndexOf('/') + 1);
    }
    
    
    //TODO
    this.findTplPath = function(sObj) {
        // Find tpl, if sObj empty: just travel through rootpath and find tpl:
        // menuBox_selectBox_otherBox, menuBox_otherBox, otherBox
        
        // If sObj given, this?
        // menuBox_selectBox_otherBox_Box, menuBox_otherBox_Box, otherBox_Box, menuBox_selectBox_Box, menuBox_Box, Box
        // or
        // menuBox_selectBox_otherBox_Box, menuBox_selectBox_Box, menuBox_otherBox_Box, menuBox_Box, otherBox_Box, Box
        
        // Oder das mit menuBox_selectBox_iconBar_Box bleiben lassen? Also:
        // menuBox_selectBox_Box, menuBox_Box, Box
        
        // OOOOder natürlich
        // menuBox_selectBox_otherBox-Box
    }
    
    this.getTplPath = function(sSub, sObj) {
        if (sSub && (sSub.substr(0, 1) != '-')) sSub = '-' + sSub;
        else                                    sSub = '';
        
        var sPath = this.sPath;
        
        if (sObj && (this.sPath.substr(this.sPath.lastIndexOf('/') + 1) != sObj)) {
            sPath = this.sPath.substr(0, this.sPath.lastIndexOf('/') + 1) + sObj;
        }
        
        sPath = sPath + sSub;
        
        var aPath = sPath.substr(1).split('/');
        //alert(aPath.join('_'));
        //if (sSub == '-ToolBar') alert('#jst_' + aPath.join('_'));
        //TODO #jst --> #jso_jst oder so? Ebenfalls ggf. Templates?
        while (!jQuery('#jst_' + aPath.join('_')).length && aPath.length) {
            aPath.splice(aPath.length - 2, 1);
        }
        
        return aPath.length ? aPath.join('_') : '';
        // return this.sPath.substr(1).replace(/\//g, '_');
    }
    
    this.getTpl = function(sSub, sObj, bString) {
        var sTpl = this.getTplPath(sSub, sObj);
        var oTpl = undefined;
        
        if (sTpl) {
            if (bString) oTpl = decodeURI(jQuery('#jst_' + sTpl).html());
            else         oTpl = jQuery.template(decodeURI(jQuery('#jst_' + sTpl).html()));
        }
        
        return oTpl;
    }
    
    
    /*
     * Parse template
     *
     * oParams      Object with all params to replace - default: child jsd_ elements
     * sTpl         Name of the template - default: path
     * oElem        Element to assign template to - default: main element
     * bExternals   Assign externally set params? Internals overwrite externals - default: true
     *
     */
    this.parseTpl = function(oParams, sTpl, oElem, bExternals) {
        // If template not found:
        //      - search through root path for template (split sTpl before replacing / through _)
        //      - load templates via AJAX, later
        
        // Check: isobj sTpl -> oElem = sTpl; sTpl = this.sPath;
        //        isboolean oElem -> bExternals = oElem;
        //        isstring oParams -> sTpl = oParams
        //        isobj oPa
        // (...)
        
        if (!sTpl) sTpl = this.sPath;
        // Schleife Pfad nach oben durchgehen, bis Tpl gefunden wird
        sTpl = sTpl.substr(1).replace('/', '_');
        
        jQuery(this.jE).html(jQuery.template(decodeURI(jQuery('#jst_' + sTpl).html())), oParams);
    }
    
    this.makeTplParams = function(jElem, sName, oParams) {
        //TODO possibility that jElem contains a set of elements! ... how to assign sName?
        //     if sName not given -> html to 'html', text to 'text', attrs directly to aA[iI] ...
        //     check if jElem is a jQuery Obj!
        
        if (!oParams) oParams = new Object();
        
        var oA = jElem.attr();
        var aA = new Array('src', 'title', 'href', 'target', 'rel', 'alt');
        
        oParams[sName] = jElem.html();
        
        for (var iI = 0; iI < aA.length; iI++) {
            if (jElem.attr(aA[iI])) oParams[sName + '[' + aA[iI] + ']'] = jElem.attr(aA[iI]);
        }
        
        //oParams[sName + '[abc]'] = oParams[sName + '[src]'];
        oParams['_obj_id'] = _iObjId;
        
        return oParams;
    }
    
    this.getJSDs = function() {
        if (!oJSDs) {
            oJSDs = new Object();
            
            this.jE.children(window.aes.gP('jsd')).remove().each(function() {
                // Get first jsd_* class and use as parameter name (without the jsd_ prefix)
                var sP = String(/\bjsd_\S+\b/.exec(jQuery(this).attr('class'))).substr(4);
                
                oJSDs = that.makeTplParams(jQuery(this), sP, oJSDs);
            });
        }
        
        return oJSDs;
    }
    
    this.parseJSDs = function(sTpl, bExternals) {
        if (!sTpl) sTpl = this.getTplPath();//this.sPath;
        if (!sTpl) return;
        
        if (sTpl.substr(0, 1) == '/') sTpl = sTpl.substr(1);
        
        bExternals = (bExternals !== false);
        sTpl       = sTpl.replace(/\//g, '_');
        
        
        // If e.g. jsd_xyz is also a jso_
        //      - if (jst_xyz).length: (jst_xyz:first).after(jsd_xyz).remove()
        //      - also replace ${xyz} with html of element?
        
        var oParams = this.getJSDs();//new Object();
        
        oParams['_parseJSDs'] = 'true';
        oParams['_parseJSDs_rand'] = Math.round(Math.random() * 1000000);
        
        /*
        this.jE.children(window.aes.sFilterJSD).remove().each(function() {
            // Get first jsd_* class and use as parameter name (without the jsd_ prefix)
            var sP = String(/\bjsd_\S+\b/.exec(jQuery(this).attr('class'))).substr(4);
            
            oParams = that.makeTplParams(jQuery(this), sP, oParams);
        });
        */
        
        /*
        if (oParams.count == 12) {
            sP = '';
            for (var s in oJSDs) sP += '--'+s+': '+oJSDs[s];
            //alert(sP);
            oParams['screenshotsSS[]'] = 'asd!!';
            //alert(decodeURI(jQuery('#jst_' + sTpl).html()));
            alert(jQuery('<div />').html(jQuery.template(decodeURI(jQuery('#jst_' + sTpl).html())), oParams).html());
        }
        */
        
        if (this.jE.children(window.aes.sFilterJSO).length) {
            jQuery(this.jE).prepend(jQuery.template(decodeURI(jQuery('#jst_' + sTpl).html())), oParams);
        }
        else jQuery(this.jE).html(jQuery.template(decodeURI(jQuery('#jst_' + sTpl).html())), oParams);
        
        //if (jFirst.length) jQuery(jFirst).after(jQuery.template(jQuery('#jst_' + sTpl).html()), oParams);
        //else jQuery(this.jE).prepend(jQuery.template(jQuery('#jst_' + sTpl).html()), oParams);
        
        
        // Process JSDs that are also JSOs
        //jQuery(jQuery(this.jE).children(window.aes.byPrefix('jso_')).filter(window.aes.byPrefix('jsd_')).remove()).each(function() {
        //  jQuery(this.jE).find('[class=jst_' + sId + ']').replaceWith(this);
        //});
        
        
        /*
        if (bExternals) {
            for (var sId in this.oExternals) {
                jQuery(this.jE).find('[class=jst_' + sId + ']').replaceWith(jQuery(this.oExternals[sId]).children());
            }
        }
        */
    }
    
    this.addExternal = function(sName, jElem, fPos) {
        // Later: possible to set an initial element for every external
        //        -> fPos prepend / append
        //        -> where to get initial element(s) html from?
        
        /*
        if (!this.oExternals[sName]) this.oExternals[sName] = jQuery(document.createElement('div'));
        
        if (fPos < 0) jQuery(this.oExternals[sName]).prepend(oElem);
        else          jQuery(this.oExternals[sName]).append(oElem);
        */
        
        //TODO check if jElem is a string, then do jElem = jQuery(jElem)?
        
        if (fPos < 0) this.jE.find('.jst_' + sName + '').append(jElem);
        else          this.jE.find('.jst_' + sName + '').prepend(jElem);
        
        return jElem;
    }
    
    
    this.getInContext = function(sContext) {
        // parseJSDs(this.sPath + '-' + sContext)
        // --> alle JSDs auf Template anwenden, z.B. von Mark:
        // sOverlay = sOverlay + oMarked.getInContext('Mark')
        //  -> Template NewsList_News-Mark
        //
        // ggf NewsList_News->Mark, und die einzelnen Kontexte sind in einem Template file drin!!
        //      -> ggf. <div class="jsd_News"><h1>... standard context ...</h1><div class="jsx_Mark">Kontext Mark!</div></div>
        
        // getView()?
        // zB dragNdrop in ein Element oder anzeige FullScreen
        //      -> fullscreen button, view NewsCtr/Mark/News aufrufen
        //         d.h. nich Kontext, sondern Tempate in Pfad des aufrufenden Buttons
        //         - das Tpl kann ja zB auch in /Mark/News liegen
        //      -> DragNdrop nur akzeptieren wenn getView klappt für /This/DragToList/DraggedElement
        //
        // naja je nachdem ... News in NewsList wäre Kontext News-Fullscreen / News-Mark besser
        //  dragNdrop wäre mit getView besser
        //
        // wie auch immer: html zurückliefern ODER jQuery-Obj (falls Click-Events usw mit dabei sind)
        //  -> nicht sS = sS + sRtn sondern $().append(jRtn)!!
    }
    this.getWH = function() {
        var jNode = this.jE;
        var aShow = new Array();
        
        while (jNode.length) {
            //aShow.push(jNode.filter(':visible').length);
            aShow.push((jNode.css('display') == 'none')? false : true);
            
            jNode.show();
            
            if (jNode.parent('body').length) break;
            jNode = jNode.parent();
        }
        
        var oRtn = {
            width: this.jE.width(), height: this.jE.height(),
            outerWidth: this.jE.outerWidth(true), outerHeight: this.jE.outerHeight(true)
        };
        
        jNode = this.jE;
        
        while (jNode.length) {
            if (!aShow[0]) jNode.hide();
            aShow.shift();
            
            if (jNode.parent('body').length) break;
            jNode = jNode.parent();
        }
        
        return oRtn;
    }
    
    
    
    this.getHtml = function() {
        if (jQuery(this.jE).length) {
            return jQuery(this.jE).html();
        }
        
        return '';
    }
    
    this.getParams = function() {
        // Parse all jsp_* classes
        var oRg      = /\bjsp_\S+\b/g;
        var oParams  = new Object();
        var sClasses = this.jE.attr('class');
        var sParam;
        
        while (sParam = oRg.exec(sClasses)) {
            oParams[String(sParam).substr(4)] = true;
        }
        
        return oParams;
    }
    
    
    
    
    
    /*
     *
     * Initialize inherit messages
     *
     */
    var oCaller = this.callerObj();
    var aInheritMessages = new Array();
    
    if (oCaller) aInheritMessages = oCaller.getInheritMessages();
    
    if (aInheritMessages.length) {
        for (var iI = 0; iI < aInheritMessages.length; iI++) {
            oCaller.registerMessage(aInheritMessages[iI], function(sMsg, oData) {
                return that.sendMessage(sMsg, oData);
            });
        }
    }
    
}

// Default ("passthrough") class
window.jso['/'] = function() {
    this.jso = function() {
        //alert(this.sPath);
        
        //TODO Eigentlich schon, oder? Das Objekt selbst soll ja nich in den Pfad reinzählen
        //this.sPath = this.sPath.substr(0, this.sPath.lastIndexOf('/'));
        
        window.aes.initElements(this.jE, this.sPath);
        
        //jQuery(this.jE).prepend('<h1>/</h1>');
    }
}

window.jso['/init'] = function() {
    this.init = function() {
        //alert(this.sPath + '-------asd');
        this.sPath = ''; //this.sPath.substr(0, this.sPath.lastIndexOf('/'));
        //alert(this.sPath);
        //window.aes.initElements(this.jE);
        this.initializeChildren();
        
        //jQuery(this.jE).prepend('<h1>/</h1>');
    }
}



window.aes = {
    sFilterJSO: '[class*=" jso_"], [class^="jso_"]',
    sFilterJSD: '[class*=" jsd_"], [class^="jsd_"]',
    
    gP: function(sType, sFilter) {
        if (!sFilter) sFilter = '';
        if (!sType)   sType   = 'jso';
        
        return '[class*=" '+sType+'_'+sFilter+'"], [class^="'+sType+'_'+sFilter+'"]';
    },
    
    getObjById: function(sId) {
        return jQuery.data(document.getElementById(sId), 'obj');
    },
    
    getObj: function(jObj) {
        return jQuery.data(jQuery(jObj).get(0), 'obj');
    },
    
    getObjs: function(jE) {
        var aRtn = new Array();
        
        jQuery(jE).each(function() {
            aRtn.push(jQuery.data(this, 'obj'));
        });
        
        return aRtn;
    },
    
    /*
     * NOT TESTED?
     *
    findSubObjs: function(oE) {
        // Only get "jso_*"
        oE = jQuery(oE).find(window.aes.sFilterJSO);
        
        if (jQuery(oE).length > 0) {
            var aRtn = new Array();
            
            // Receive real html dom node via get() -> necessary for .data
            for (var iI = 0; iI < jQuery(oE).length; iI++) aRtn.push(jQuery.data(jQuery(oE).get(iI), 'obj'));
            
            return aRtn;
        }
        
        return new Array();
    },
    */
    
    getJSOs: function(jParent, bAll) {
        if (bAll)
            return jQuery(jParent).find(window.aes.sFilterJSO);
        else
            return jQuery(jParent).children(window.aes.sFilterJSO);
    },
    
    getNextJSOs: function(jParent) {
        // get all children with .each, check (this).filter(window.aes.sFilterJSO).length
        // jso -> return, no jso -> parse with getNextJSOs
        //jQuery(jParent).children(window.aes.sFilterJSO);
        
        //return jQuery(jParent).find(window.aes.sFilterJSO);
        
        return jQuery(jParent).closestChildren(window.aes.gP());
    },
    
    /*
     * initElements
     * initializes JSO elements
     *
     * jParent  jQuery object of caller
     * sPath    Current path (or a special one)
     * iMode    0 = children, 1 = find, 2 = next children
     *
     * ToDo
     * - sPath and sHtmlPath -> get an object of sPath, but with sHtmlPath e.g. for templates
     * - also possible to pass a jQuery object with a set of elements to initialize? Instead of iMode ... typeof iMode = object ?
     *
     */
    initElements: function(jParent, sPath, iMode) {
        //window.startTimer('initElements');
        
        if (!jParent.length) return;
        
        var oUpdate = window.aes.getObj(jParent);
        var aO      = new Array();
        var jO      = undefined;
        
        // Das is irgendwie strange ... also wenn der Pfad leer is, muss er auch leer bleiben!
        if (arguments.length < 2) sPath = '';
        else if (sPath && (sPath.substr(0, 1) != '/')) sPath = '/' + sPath;
        
        if (arguments.length < 3) iMode = 0;
        //alert(iMode);
        // Special move: don't find children, iMode is the set of elements to intialize!
        if (typeof iMode == 'object') {
            jO = iMode;
        }
        else if (iMode == 1) {
            jO = window.aes.getJSOs(jParent, true);
        }
        else if (iMode == 2) {
            jO = window.aes.getNextJSOs(jParent);
            
            var sT = '';
            jO.each(function() { sT += '-' + jQuery(this).attr('class') + '-'; });
            //alert(sT);
        }
        else {
            jO = window.aes.getJSOs(jParent);
        }
        
        //window.endTimer('initElements');
        //window.startTimer('initElements2');
        
        jO.each(function() {
            var jE = jQuery(this);
            
            if (jE.closest('#jst').length) return;
            
            /*
             * Find first "jso_" in class attribute (could contain several classes!)
             */
            // \b - border of a word; \S - all characters except whitepace
            var sObj = String(/\bjso_\S+\b/.exec(jE.attr('class'))).substr(4);
            var sThisPath = sPath;
            
            
            /*
             * Find out for each depth-level of path if class exists
             */
            
            //TODO if window.jso[sTmpPath] is a string, use this instead of sPath
            // Later still use sPath since the object should think it's there
            // Not possible to add several objects comma separated as for '_ObjName'
            // Should actually also work for each prototype in path: check while aPathes generation,
            // in case window.jso[sTmpPath] is a string -> change complete remaining prototype rootpath!
            // Allow comma separated list? Prototypes-rootlines would have to be merged together
            
            // Current path and array where existing classes in rootline will be stored
            var aPath      = sThisPath.split('/');
            
            // This should be done within the aPath loop, but only one time (the "target" of the shortcut
            // should "really" exist anyway and if another shortcut is found in its rooline -> omit!)
            if (typeof window.jso[sThisPath + '/' + sObj] == 'string') {
                var sTmpPath = window.jso[sThisPath + '/' + sObj];
                
                // Really reset everything!
                if (sTmpPath.substr(0, 1) == '_') {
                    sTmpPath = (sTmpPath.length > 1) ? '/' + sTmpPath.substr(1) : '';
                    sThisPath = sTmpPath;
                }
                else if (sTmpPath == '/') sTmpPath = '';
                
                aPath = sTmpPath.split('/');
            }
            
            //TODO if no class is found, check every element in rootline (from deepest path to
            //     root) if it exists in root. If yes, try path with that element as root!
            
            
            var aPathes  = new Array(); //
            
            while (aPath.length > 0) {
                var sTmpPath = aPath.join('/') + '/' + sObj;
                
                if (window.jso[sTmpPath]) aPathes.push(sTmpPath);
                
                aPath.pop();
            }
            
            
            //TODO
            // after window.jso['/'], check for window.jso['/jso_prototypes/'+sObj]
            // then window.jso['/'+sObj]
            // then (sMainObjs).each{ window.jso['/'+sMainObj+'/'+sObj]
            //      -> defForm & crazyForm zB jeweils mainObj -> formInput, formSubmit etc können in /crazyForm/formSubmit liegen,
            //         auch wenn aktueller Pfad z.B. /contReiter/defForm/vertTabs/tab/formSubmit
            // wie mainObjs definieren? wenn verzeichnis jso_defForm usw in root existiert?
            
            /*
             * No class found? -> default class!
             */
            var oTmp = undefined;
            
            if (aPathes.length == 0) {
                var oObj = window.jso['/'];
                
                // Base class
                oObj.prototype = new window.jso[''](oUpdate, false, jE.outerHTML());
                
                // Set some variables ...
                oObj.prototype.jE         = jE;
                oObj.prototype.oE         = this;
                oObj.prototype.sPath      = sThisPath + '/' + sObj;
                oObj.prototype.sObjPath   = sMain;
                
                // Initialize
                oTmp = new oObj();
                
                /*
                 * Write object to HTML element BEFORE calling constructor
                 */
                jQuery.data(this, 'obj', oTmp);
                
                oTmp.jso();
                
                aO.push(oTmp);
            }
            else {
                //TODO
                // increase each-counter (before if statement!)
                // if class has to be loaded via ajax, set oTmp = undefined
                //  pass oUpdate and counter to ajaxLoadFunction
                //  increase missing-counter
                //  after each-schleife: oUpdate.setLoading(iMissing)
                
                // each obj: oUpdate.setChildrenReady() (pass oUpdate on initialization)
                // ajax obj: oUpdate.setReady()
                
                // IMPORTANT: ein ajax-call --> mehrere klassen, also alle hier notwendingen, laden!
                //          -> ggf. index mit allen verfügbaren klassen?
                
                // -> ajax - later! (childLoaded, childrenLoaded)
                // -> ready sofort (childReady, childrenReady)
                
                // oUpdate + ggf. bAjax an [''] constructor!
                
                
                // random wert, der allen übergeben wird -> können intern feststellen, dass sie
                // zusammen gehören, wer eben den Wert kennt
                
                /*
                 * Initialize parent classes (prototypes)
                 */
                // Main class is the first entry in aPathes - write object (without initializing it!) to variable
                var sMain = aPathes.shift();
                var oObj = window.jso[sMain];
                
                if (sMain == '/menuBox') {
                    //alert(Math.random() +'----------------'+(oUpdate.jE ? oUpdate.jE.html() : oUpdate.html()));
                    //alert(jParent.attr('id'));
                }
                
                //var jE   = jQuery(this);
                //var oE   = this;
                //var fId  = Math.random();
                
                // Base class
                oObj.prototype = new window.jso[''](oUpdate, false, jE.outerHTML());//, fId);
                
                // Set some variables ...
                oObj.prototype.jE         = jE;
                oObj.prototype.oE         = this;
                
                //TODO sPath = sMain, sObjPath -> sHtmlPath = sPath ?
                //     -> check html path to find out if something's e.g. within a form (e.g. a Slide where sPath is
                //        resettet to /Slides/..., but validates because Form is within sHtmlPath)?
                // Scheisse, da echt noch aufräumen ...
                // Weiterhin bei Shortcuts checken - wenn _ am Anfang, sPath dahin resetten, wenn nicht, wirlich nur den
                // Obj Path für das aktuelle Element ändern (d.h. Kindelemente weiterhin in der kompletten Hierarchie
                // Oder erledigt sich das ohnehin mit dem oben, dass auch der Pfad von jedem Elem in der Rootline, da im Root liegt, gecheckt wird?
                //  -> evtl. grundsätzlich einfach bei jedem Elem das im Root liegt ein Prototype einfügen? Wenn zwischendrin keines im Root, ganz normal!
                
                // z.B. Slide hat immer /Slides/slideContainer/Slide [außer z.B. /subtabBox/verttabBox/Slide/slideContainer/Slide gibt's], aber für's Template
                // also bei getTplPath sollte sHtmlPath berücksichtigt werden -> jst_subtabBox_verttabBox_..._Slide
                // bei jedem getTplPath muss eigentl. die komplette Suche wie oben gemacht werden, immer checken ob in den alternativen
                // Roots ein Tpl liegt. Aber initElems hier sollt eh die komplette Pfad-Info zurückliefern in nem Arr, siehe oben!
                
                // Pfadinfo von oben sollt von allen Rootlines den kompletten Pfad (root > ... > obj selbst) liefern -> kann ja sein dass in einem
                // Root das Obj nicht vorkommt, aber das Tpl liegt -> hauptsächl. haupt-pfad
                // Rootline obj ist ein obj sobald es das nä. Elem in der Rootline als Kindobj hat, also /Slides ist start Rootline, falls /slideContainer
                // darin existiert - ob wirklich Slides selbst drin liegt, egal!
                
                // => vllt. aber doch besser dass man wirklich angibt, welches die "Block"/Root-Elemente sind, aber wenn da mehrere von in der
                //    Rootline sind, wird halt immer RootlElem -> letztes Elem, 2. Rootelem (über RootElem) zu letztem, ...! Reicht das nich?
                //    -> oder halt Block, wenn in Root UND Kindelemente, also ['/Elem/*']
                oObj.prototype.sPath      = sThisPath + '/' + sObj;
                oObj.prototype.sObjPath   = sMain;
                
                
                // Add additional prototypes 
                if (window.jso['_' + sObj]) {
                    var aPrototypes = window.jso['_' + sObj].split(',');
                    
                    for (var iI = 0; iI < aPrototypes.length; iI++) {
                        if (window.jso[sTmpPath]) aPathes.push(aPrototypes[iI]);
                    }
                }
                
                // Assign prototypes to class variable
                // Direction is top -> bottom so that classes deeper in path overwrite parents
                for (var iI = aPathes.length - 1; iI >= 0; iI--) {
                    //oObj.prototype = jQuery.extend(oObj.prototype, new window.jso[aPathes[iI]](jE));
                    
                    // Workaround to merge prototypes together ...
                    // Is there a better way?
                    // jQuery.extend would work, but the prototypes wouldn't have the parent prototypes
                    var oPrototype = window.jso[aPathes[iI]];
                    oPrototype.prototype = oObj.prototype;
                    oObj.prototype = new oPrototype(oObj.prototype);//, fId);
                }
                
                //if (sObj == 'nextButton') {
                    //for (var sI in oObj.prototype) jQuery('body').append('d<h3>' + sI + '</h3><p>' + oObj.prototype[sI] + '</p>');
                    //var oPrototype = window.jso['/Slides/prevButton'];
                    //oPrototype.prototype = oObj.prototype;
                    //oObj.prototype = new oPrototype(oObj.prototype);
                //}
                
                /*
                 * Initialize main object
                 */
                oObj.prototype.constructor = oObj;
                oTmp = new oObj(oObj.prototype);//, fId);
                
                /*
                 * Write object to HTML element BEFORE calling constructor
                 */
                jQuery.data(this, 'obj', oTmp);
                
                // ... and call "constructor" (just that the object knows that it's the main class)
                if (oTmp[sObj]) oTmp[sObj]();
                
                aO.push(oTmp);
            }
        });
        
        //window.endTimer('initElements2');
        return aO;
    }
}



jQuery(function() {
    jQuery('html').attr('id', 'jso_jsc');
    jQuery('body').find(window.aes.gP('jsd')).hide();
    
    jQuery.extend(jQuery.template.regx , { standard: /\$\{([\w-\[\]]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g });
});






//jQuery(function() {
    /*
     * jQuery Template
     * 
     * Change standard regExp for template: copied from default JSP regExp, but allows
     * parameters like ${param[attr]}
     *
     */
    //jQuery.extend(jQuery.template.regx , { standard: /\$\{([\w-\[\]]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g });
    // jsp:        /\$\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
    // ext:        /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
    // jtemplates: /\{\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}\}/g
    
    /*
    jQuery.extend(jQuery.template.helpers, {
        'if': function(sFunc, aValue, aValues) {
            //for (var iI in this) alert(iI + ': ' + this[iI]);
            //alert(sFunc);
            //alert(aValue);
            //alert(aValues);
        },
        
        'for': function() {
            
        },
        
        'nl2br': function() {
            // Set \nn to <p></p>, \n to <br />
            // In PHP from Typo3 RTE -> \n to \n\n, <br /> to \n ... or don't use RTE at all?
            // How to format stuff? See what the RTE http://www.themaninblue.com/experiment/widgEditor returns!
        }
    });
    */
    
    //var aO = window.aes.initElements(jQuery('body'));
    
    /*
    jQuery(aO).each(function() {
        var oO = jQuery.data(this, 'obj');
        //alert(oO.validateForm() + ' || ' + oO.returnData());
        alert('~~ ' + jQuery(oO.jE).attr('id') + ' ~~');
        alert(oO.getHtml());
    });
    */
    
    //alert(aes.getObj('tabRegister2').getHtml());
//});