/**
 * JS FILE: de.zehn.js
 * @author: haudum
 * @copyright: Lovely Systems AG
 */

var zl = {
    VERSION : "0.1",
    AUTHOR : "Lovely Systems AG",
    listRegExp : /^(.+\/die-10-)[^\/]+-(\d+)(-0)$/,
    itemRegExp : /^(.+\/)[^\/]+-(\d+)(-\d+)$/
};

/****************************************
 * zl.Coverflow Singleton
 * @version 0.1
 * @author haudum
 * @anti-author zauchner
 * @description manager to control coverflow
 ****************************************/

zl.Coverflow = {
    SOURCE : app.SETTINGS.media_url+"swf/coverflow.swf",
    ALTERNATIVE_URL : "/__static_coverflow__/default/1${category}",
    CLOSE_BUTTON : app.SETTINGS.media_url+"images/coverflow-close-btn.gif",
    COOKIE_ALT : "coverflowAlternative",
    COOKIE_VISIBLE : "coverflowVisibility",
    FLASH_ID : "flash-cf",
    isOpen : false,
    position : 0,
    isWheelLocked : false,
    noFlash : function(){
        return (swfobject.getFlashPlayerVersion().major <= 8);
    },
    holder : function(){
        return $("#coverflow-holder");
    },
    button : function(){
        return $("#coverflow-show");
    },
    init : function(){
        if (window.addEventListener) {
            window.addEventListener('DOMMouseScroll', this.wheelEventHandler, false);
        }
        window.onmousewheel = document.onmousewheel = this.wheelEventHandler;
        if (this.holder().hasClass("coverflow-open")) {
            this.button().hide();
            var altCookie = getCookie(this.COOKIE_ALT);
            if (altCookie == "true" || this.noFlash()){
                this.showAlternative();
            } else if (altCookie != "true"){
                this.embedCoverflow();
            }
        } else {
            this.holder().css("height","0px");
        }
    },
    showAlternative : function(){
        setCookie(this.COOKIE_ALT,true);
        var restURL = window.GLOBALS.URL.replace(window.GLOBALS.HOSTNAME,'');
        var loadURL = ls.LovelyUtils.compileString(this.ALTERNATIVE_URL,{category:restURL});
        if (this.noFlash()) {
            zl.AjaxLoader.addEventListener(events.AjaxEvent.SUCCESS,function(){
                $(".flashLink").hide();
            });
        }
        zl.AjaxLoader.load(loadURL,'coverflow-target');
        this.appendCloseButton();
        this.isOpen = true;
    },
    showFlash : function(){
        if (this.noFlash()) {
            console.warn("Keine, oder eine zu alte  Flash-Version installiert.");
        } else {
            setCookie(this.COOKIE_ALT,false);
            this.holder().empty().append("<div id='coverflow-target'><div id='coverflow'></div></div>");
            this.embedCoverflow();
        }
    },
    showCoverflow : function(){
        setCookie(this.COOKIE_VISIBLE,"open");
        this.button().hide();
        this.holder().toggleClass("coverflow-open");
        var altCookie = getCookie(this.COOKIE_ALT);
        if (altCookie == "true" || this.noFlash()) {
            this.holder().animate({height:"425px"},
                                1000,
                                ls.Delegate.create(this,
                                                   this.showAlternative,
                                                   null));
        } else if (altCookie != "true"){
            this.holder().animate({height:"425px"},
                                1000,
                                ls.Delegate.create(this,
                                                   this.embedCoverflow,
                                                   null));
        }
    },
    embedCoverflow : function(){
        this.holder().bind("mouseover", ls.Delegate.create(this,
                                                           this.lockWheelEvent,
                                                           true));
        this.holder().bind("mouseout", ls.Delegate.create(this,
                                                          this.lockWheelEvent,
                                                          false));
        var url = encodeURIComponent(GLOBALS.URL.replace(/^(http:\/\/)(.*)[\?](.*)/,"$1$2"));
        var pos = this.getPosition();
        var list_url = app.SETTINGS.cf_url ? encodeURIComponent(app.SETTINGS.cf_url) : "";
        var flashvars = {"context":url,
                         "media_url":app.SETTINGS.media_url,
                         "start_id":pos,
                         "list_url":list_url};
        var params = {"wmode":"opaque",
                      "allowScriptAccess":"always"};
        var attributes = {"id":this.FLASH_ID};
        swfobject.embedSWF(this.SOURCE, "coverflow", "830", "425", "9.0.0", "",flashvars, params, attributes);
        this.appendCloseButton();
        this.isOpen = true;
    },
    hideCoverflow : function(){
        setCookie(this.COOKIE_VISIBLE,"closed");
        this.holder().empty().append("<div id='coverflow'></div>");
        this.holder().animate({height:"0px"},
                              1000,
                              ls.Delegate.create(this,
                                                 this.unembedCoverflow,
                                                 null));
    },
    unembedCoverflow : function(){
        this.button().show();
        this.holder().toggleClass("coverflow-open");
        this.isOpen = false;
    },
    appendCloseButton : function(){
        var img = "<div id='coverflow-close-button'><iframe></iframe><a href='javascript:zl.Coverflow.hideCoverflow()' title='Schlie&szlig;en'><img src='${btn}' alt='Schlie&szlig;en' title='Schlie&szlig;en' /></a></div>";
        this.holder().append(ls.LovelyUtils.compileString(img,{btn:this.CLOSE_BUTTON}));
    },
    toggleCoverflow : function() {
        if (this.isOpen) {
            this.hideCoverflow();
        } else {
            this.showCoverflow();
        }
    },
    lockWheelEvent : function(bool,context,event) {
        this.isWheelLocked = bool;
    },
    wheelEventHandler : function(event){
        // the context here is the window object !!!
		if (!event) event = window.event;
        if (zl.Coverflow.isWheelLocked) {
            var delta = 0;
            if (event.wheelDelta) {
                delta = event.wheelDelta/120;
                if (window.opera) delta *= -1;
            } else if (event.detail) {
                delta = -event.detail;
            }
            if (delta && zl.Coverflow.getMovie()) zl.Coverflow.getMovie().controlWheel(delta);
		    if (event.preventDefault) event.preventDefault();
            event.returnValue = false;
        }
	},
    getMovie : function() {
        var movie = document[this.FLASH_ID];
        if (!movie) movie = window[this.FLASH_ID];
        return movie;
    },
    setPosition : function(pos){
        if (!isNaN(pos)) {
            this.position = parseInt(pos,10);
        }
    },
    getPosition : function(){
        var data = ls.StorageManager.getData();
        var key = "cfpos_"+window.location.pathname.replace(/[\/\.\-_]/g,"");
        if (data[key]) {
            return data[key];
        } else {
            return null;
        }
    },
    resetHistory : function(){
        ls.StorageManager.addData("cfpos_", 0);
        // overrides method from app.js !!!
        window.onunload = function(){
            ls.StorageManager.addData("cfpos_", 0);
        };
    },
    countIVW : function(){
        var ivw = app.IVW.code.replace(/^([^_]+)_([^_]+)(_ugc)?/,"cf_$2");
        zl.IVWTracking.countIVW(ivw);
    }
};


/****************************************
 * zl.AjaxLoader Singleton
 * @version 0.1
 * @author haudum
 * @description replace a DOM node by
 * html response got from an ajax request
 ****************************************/

zl.AjaxLoader = {
    CLASS : "zl.AjaxLoader",
    WRAP : "<div id='${id}\' class='${class}'></div>",
    LOADER : "<div class='loader-image-holder'><div class='loader-image'><img src='${src}' /></div></div>",
    LOADERSOURCE : app.SETTINGS.media_url+"images/ajax-loader-big.gif",
    ERRORSOURCE : app.SETTINGS.media_url+"images/ajax-error.gif",
    STATECOOKIE : 'listViewmode',
    VIEWPATTERN : /(.*?)\/(.*?)\/(\d+)\/$/,
    HTTPSTATUS :  null,
    TARGET: null,
    AJAX_CLASS : "ajax-wrapper",
    load : function(url,id, event, nocount) {
        if (event) {
            if (event.preventDefault) {
                event.preventDefault();
                event.stopPropagation();
            } else {
                /* ie event handling */
                event.cancelBubble = true;
                event.returnValue = false;
            }
        }
        if (!url || !id) {
            return false;
        }

        this.TARGET = $(document.getElementById(id));
        if (this.TARGET.length < 0 || this.TARGET.length > 1) {
            console.log("No or too many targets.");
            return false;
        }
        if (!this.TARGET.parent().hasClass(this.AJAX_CLASS)) {
            var wrapper = ls.LovelyUtils.compileString(this.WRAP,{"id" : "ajax-wrapper-"+new Date().getTime(),
                                                                  "class" : this.AJAX_CLASS});
            this.TARGET.wrap(wrapper);
        }
        var node = this.TARGET.parent();
        /* in order to refresh comments
         * you have to load same page twice or more often
         * so let 'em do! */
        if (node.attr("ajax:current") == url) {
            return;
        } else if (!url.match(/comment/)) {
            node.attr("ajax:current",url);
        }
        var historyData = {"key":id,"value":url};
        // nocount suppresses IVW verpixelung for ajax loaded on page load
        // default: false
        if (!nocount) this.dispatchEvent(new ls.AjaxEvent(events.AjaxEvent.LOAD, historyData));
        this.createLoaderImage();
        var request = $.ajax({ url: url,
                               type: "GET",
                               error: ls.Delegate.create(zl.AjaxLoader,
                                                         zl.AjaxLoader.onErrorHTTP,
                                                         url),
                               success: ls.Delegate.create(zl.AjaxLoader,
                                                           zl.AjaxLoader.onAjaxLoadComplete,
                                                           node)
                            });
        return false;
    },
    onAjaxLoadComplete : function(data,context,response){
        var node = data;
        node.empty().append(response);
        this.dispatchEvent(new ls.AjaxEvent(events.AjaxEvent.SUCCESS));
        ls.LovelyUtils.scrollToNode(data[0]);
    },
    onErrorHTTP : function(url,context,xhr){
        if (xhr.status == 0) {
            this.destroyLoaderImage(xhr, url);
        } else {
            this.showError(xhr, url);
        }
        return;
    },
    createLoaderImage : function(){
        if (this.LOADERSOURCE) {
            var node = this.TARGET.parent();
            node.css("position","relative");
            if($('.loader-image-holder',node).length < 1){
                node.prepend(ls.LovelyUtils.compileString(this.LOADER,{"src":this.LOADERSOURCE}));
                var loader = $(".loader-image-holder", node);
                var w = node.width();
                var h = node.height();
                var offset = 0;
                if ($(".title",node).length == 1) {
                    offset = $(".title",node).height();
                    loader.css("top",offset);
                }
                loader.css("height",h-offset);
                loader.css("width",w);
                var image = $(".loader-image", loader);
                var t = h/2 - image.height()/2 -offset;
                image.css("top",t);
            }
        }
    },
    showError : function(xhr, url){
        var node = this.TARGET.parent();
        var loaderNode = $(".loader-image", node);
        var loaderImg = $('img', loaderNode);
        loaderImg.attr('src',this.ERRORSOURCE).attr('title',xhr.status);
        if (loaderNode.children().length < 2){
            loaderNode.append('<p class="error">'+url+'</p>');
        }
    },
    destroyLoaderImage : function(xhr, url){
        var node = this.TARGET.parent();
        $('.loader-image-holder',node).hide();
        console.info('Sie sind nicht mit dem Internet verbunden!')
    },
    restoreHistory : function(data,context,event){
        for (n in event) {
            if (document.getElementById(n) != null) {
                var id = n;
                var url = event[n];
                if (url == "DEFAULT") {
                    url = this.getBaseURL(id);
                }
                if (!url || !url.indexOf("/") == 0) {
                } else {
                    this.load(url,id);
                }
            }
        }
    },
    getBaseURL : function(id){
        if (typeof id == "string") {
            var node = $(document.getElementById(id));
            return node ? node.attr("ajax:base") : null;
        }
    }
};
makeDispatchable(zl.AjaxLoader);


/****************************************
 * zl.FormManager
 * @author haudum
 * @extends ls.EventDispatcher
 ****************************************/

zl.FormManager = function(node,fill){
    this.EventDispatcher();
    this.FormManager(node,fill);
};

zl.FormManager.prototype.FormManager = function(node,fill){
    this.DEBUG = false;
    this.context = node;
    this.fid = this.context.attr("id") || "";
    this.params = this.context.getNSParams("ajax");
    this.requiredFields = this.params.required ? eval(this.params.required) : [];
    this.confirmFields = this.params.confirm ? eval(this.params.confirm) : [];
    this.additionalValidations = [];
    this.errors = $("#form-error_general",this.context);
    this.loader = $(".loader-image",this.context);
    this.submitBtn = this.params.submitbutton ? $(this.params.submitbutton,this.context) : false;
    this.onEnterSubmit = this.params.onentersubmit ? eval(this.params.onentersubmit) : true;
    this.allowHTML = this.params.allowhtml ? eval(this.params.allowhtml) : false;
    this.type = "dict";
    this.autofill = false;
    this.mapping = ['recaptcha_challenge_field',
                    'recaptcha_response_field'];
    this.init();
    if (fill) {
        this.autofill = true;
        this.getFormData();
    }
};

zl.FormManager.prototype.getFormData = function(){
    var formDataProxy = new ls.JsonRPC(this.params.endpoint);
    formDataProxy.addEventListener(events.RPCEvent.RESPONSE,
                                   ls.Delegate.create(this,
                                                      this.fillFormData,
                                                      null));
    formDataProxy.call("asDict",{});
};

zl.FormManager.prototype.fillFormData = function(data,context,response){
    if (this.DEBUG) console.log("fillFormData",data,context,response);
    if (!response.result) return;
    var res = response.result;
    for (key in res) {
        var val = res[key];
        var node = $("#"+this.fid+"_"+key);
        if (this.DEBUG) console.log("fillFormData",node,val);
        if (node.length > 0) {
            if (val === null) val = "";
            if (typeof val == "boolean" && node.is("input:checkbox")) {
                node[0].checked = val;
            }
            var decoded = decodeURIComponent(val);
            if (node.is("select") || (node.is("fieldset") && node.hasClass("radiolist"))){
                for (var i=0; i<node.children().length; i++) {
                    var option = $(node.children()[i]);
                    if (option.val() === decoded) option.select();
                }
            }
            var fillrule = node.attr("ajax:fillrule");
            var fillpattern = node.attr("ajax:fillpattern");
            if (fillrule && fillpattern) {
                var re = new RegExp(fillpattern);
                if (this.DEBUG) console.log(fillrule,fillpattern,re,decoded);
                decoded = decoded.replace(re,fillrule);
            }
            node.val(decoded);
        }
    }
    this.dispatchEvent(new ls.AjaxEvent(events.FormEvent.FORMDATA));
};


zl.FormManager.prototype.init = function(){
    if (!this.params.submitbutton) {
        return;
    }
    $("input:checkbox",this.context).bind("change",function(){
        if ($(this).val() === "" || $(this).val() === "false") {
            $(this).val("true");
        } else if ($(this).val() === "true") {
            $(this).val("false");
        }
    });
    if (this.params.type) this.type = this.params.type;
    $("input",this.context).bind("keypress",ls.Delegate.create(this,
                                                               this.onKeyPress,
                                                               null));
    $("textarea",this.context).bind("keypress",ls.Delegate.create(this,
                                                               this.onKeyPress,
                                                               null));
    this.submitBtn.bind("click",ls.Delegate.create(this,
                                                   this.submitForm,
                                                   null));
    this.proxy = new ls.JsonRPC(this.params.endpoint);
    this.proxy.addEventListener(events.RPCEvent.FAIL,
                                ls.Delegate.create(this,
                                                   this.onFormFail,
                                                   null));
    this.proxy.addEventListener(events.RPCEvent.SUCCESS,
                                ls.Delegate.create(this,
                                                   this.onFormSuccess,
                                                   null));
    this.proxy.addEventListener(events.RPCEvent.RESPONSE,
                                ls.Delegate.create(this,
                                                   this.onFormResponse,
                                                   null));
    $("input",this.context).bind("focus", ls.Delegate.create(this,
                                                             this.hideSavedButton,
                                                             null));
    $("textarea",this.context).bind("focus", ls.Delegate.create(this,
                                                                this.hideSavedButton,
                                                                null));
};

zl.FormManager.prototype.submitForm = function(){
    this.dispatchEvent(new ls.AjaxEvent(events.FormEvent.BEFORE_SUBMIT, null));
    var fd = this.context.formSerialize();
    var formData = ls.LovelyUtils.parseQueryString(fd,this.type,this.mapping,this.allowHTML);
    var required = this.checkRequiredFields();
    var confirm = this.checkConfirmFields();
    var validation = this.validateForm(formData);
    var additionalValidation = this.validateAdditional();
    if (required || confirm || validation || additionalValidation) {
    } else {
        this.submitBtn.hide();
        this.loader.show();
        this.proxy.call(this.params.method,formData);
    }
};

zl.FormManager.prototype.validateForm = function(data){
    if (!this.fid) {
        return false;
    }
    var ERROR = false;
    var fdata = null;
    if (this.type == "data") {
        fdata = data.data;
    } else if (this.type == "dict") {
        fdata = data;
    }
    for (f in fdata) {
        var key = f;
        var val = fdata[f];
        var fieldId = this.fid+"_"+key;
        var field = $("#"+fieldId);
        var fieldName = field.attr("name");
        var validation = field.attr("ajax:valid");
        var transform = field.attr("ajax:transform");
        var seperator = field.attr("ajax:seperator");
        var invalidNode = $("#form-invalid_"+fieldName+" p");
        if (validation) {
            var re = new RegExp(validation);
            var valid = true;
            if (seperator) {
                var list = val.split(seperator);
                for (var i=0; i<list.length; i++) {
                    if (!re.test(list[i].trim())) valid = false;
                }
            } else {
                valid = re.test(val);
            }
            if (!valid) {
                field.addClass("alert");
                invalidNode.show();
                field.bind("focus", ls.Delegate.create(this,
                                                       this.clearErrorMessage,
                                                       key));
                ERROR = true;
            } else {
                if (seperator) {
                    fdata[f] = val.split(seperator);
                    for (var i=0; i<fdata[f].length; i++) {
                        fdata[f][i] = fdata[f][i].trim();
                    }
                }
                if (transform && val != "") {
                    fdata[f] = val.replace(re,transform);
                }
            }
        }
    }
    return ERROR;
};

zl.FormManager.prototype.onKeyPress = function(data,context,event){
    if (context.is("input")){
        if (event.which == 13 && this.onEnterSubmit) {
            this.submitForm();
        }
    }
};

zl.FormManager.prototype.onFormResponse = function(data,context,response){
    this.submitBtn.show();
    this.loader.hide();
};

zl.FormManager.prototype.onFormSuccess = function(data,context,response){
    if (this.autofill) {
        var result = {"result":response};
        this.fillFormData(data,context,result);
    }
    $(".saved", this.context).show();
    this.dispatchEvent(new ls.AjaxEvent(events.FormEvent.SUCCESS, response));
};

zl.FormManager.prototype.hideSavedButton = function(data,context,event){
    $(".saved", this.context).hide();
};

zl.FormManager.prototype.onFormFail = function(data,context,response){
    for (var p in response) {
        var fieldName = response[p];
        var fieldNode = $("#"+this.fid+"_"+fieldName);
        var errorNode = $("#form-error_"+fieldName+" p");
        errorNode.show();
        fieldNode.addClass("alert");
        fieldNode.bind("focus", ls.Delegate.create(this,
                                                   this.clearErrorMessage,
                                                   fieldName));
    }
    this.dispatchEvent(new ls.AjaxEvent(events.FormEvent.FAIL,response));
};

zl.FormManager.prototype.clearErrorMessage = function(data,context,event){
    if (this.DEBUG) console.log("clearErrorMessage",data,context,event);
    var node = $("#"+this.fid+"_"+data);
    var cnode = $("#"+this.fid+"_"+data+"-verify");
    node.removeClass("alert");
    cnode.removeClass("alert");
    node.unbind("focus");
    cnode.unbind("focus");
    var specificErrorNode = null;
    if (data && this.fid) {
        var specificErrorNode = $("#form-error_"+data+" p");
        var specificConfirmErrorNode = $("#form-error_"+data+"-verify p");
        var specificValidationErrorNode = $("#form-invalid_"+data+" p");
    }
    if (!specificErrorNode) {
        if (this.DEBUG) console.log("No such DOM node.");
        return;
    }
    specificErrorNode.hide();
    specificValidationErrorNode.hide();
    specificConfirmErrorNode.hide();
};


zl.FormManager.prototype.checkRequiredFields = function(){
    var ERROR = false;
    if (!this.fid) {
        if (this.DEBUG) console.log("no this.fid");
        return false;
    }
    if (this.requiredFields.length > 0) {
        for (var i=0; i<this.requiredFields.length; i++) {
            var fieldName = this.requiredFields[i];
            var field = $("#"+this.fid+"_"+fieldName);
            var errorField = $("#form-error_"+fieldName+" p");
            if (field.val() == "" || field.val() == " " || field.val() == "false") {
                errorField.show();
                field.addClass("alert");
                ERROR = true;
                field.bind("focus", ls.Delegate.create(this,
                                                       this.clearErrorMessage,
                                                       fieldName));
            }
        }
    }
    return ERROR;
};

zl.FormManager.prototype.checkConfirmFields = function(){
    if (!this.fid) {
        if (this.DEBUG) console.log("no this.fid");
        return false;
    }
    var ERROR = false;
    if (this.confirmFields.length > 0) {
        for (var i=0; i<this.confirmFields.length; i++){
            var fieldName = this.confirmFields[i];
            var field = $("#"+this.fid+"_"+fieldName);
            var confirmField = $("#"+this.fid+"_"+fieldName+"-verify");
            if (this.DEBUG) console.log(field,confirmField);
            if (!field || !confirmField) return;
            if (this.DEBUG) console.log(field.val(),confirmField.val());
            if (field.val() != confirmField.val()){
                var errorNode = $("#form-error_"+fieldName+"-verify p");
                if (errorNode) {
                    errorNode.show();
                    field.addClass("alert");
                    confirmField.addClass("alert");
                    field.bind("focus", ls.Delegate.create(this,
                                                           this.clearErrorMessage,
                                                           fieldName));
                    confirmField.bind("focus", ls.Delegate.create(this,
                                                                  this.clearErrorMessage,
                                                                  fieldName));
                }
                ERROR = true;
            }
        }
    }
    return ERROR;
};

zl.FormManager.prototype.addValidation = function(field,validation){
    this.additionalValidations.push({"field" : field,
                                     "func" : validation});
};

zl.FormManager.prototype.validateAdditional = function(){
    var ERROR = false;
    var len = this.additionalValidations.length;
    for (var i=0; i<len; i++) {
        var fieldName = this.additionalValidations[i].field;
        var field = $("#"+this.fid+"_"+fieldName);
        var errorField = $("#form-invalid_"+fieldName+" p");
        var func = this.additionalValidations[i].func;
        var valid = func.call();
        if (!valid) {
            errorField.show();
            field.addClass("alert");
            ERROR = true;
            field.bind("focus", ls.Delegate.create(this,
                                                   this.clearErrorMessage,
                                                   fieldName));
        }
    }
    return ERROR;
};

zl.FormManager.prototype.setField = function(fieldName, val) {
    var field = $("#"+this.fid+"_"+fieldName);
    if (field.length > 0) {
        field.val(val.toString());
    }
};


inheritClass(zl.FormManager, ls.EventDispatcher);


/****************************************
 * zl.CommentManager
 * @author zauchner
 * @extends ls.EventDispatcher
 ****************************************/

zl.CommentManager = function(node,fill){
    this.EventDispatcher();
    this.FormManager(node,fill);
    this.CommentManager(node,fill);
};

zl.CommentManager.prototype.CommentManager = function(node, fill){
    this.template = new ls.TemplateParser($("#itemCommentTemplate"));
    this.template.fade = true;
    this.ANSWER_TEMPLATE = "@${text} ";
};

zl.CommentManager.prototype.onKeyPress = function(data,context,event){
    if(context.is('textarea')){
        var amount = context.val().length;
        var maxAmount = parseInt($('span#maxChars').text());
        $('span#charsLeft').empty().text(amount);

        if(amount > maxAmount-1){
           var cutted = context.val().substr(0,maxAmount-1);
           context.val(cutted);
        }
    } else {
        if(this.DEBUG) console.log('is not a textarea node not found');
        return;
    }
};
zl.CommentManager.prototype.onFormSuccess = function(data,context,response){
    if (this.autofill) {
        this.fillFormData(data,context,response);
    }
    $(".saved", this.context).show();
    this.dispatchEvent(new ls.AjaxEvent(events.FormEvent.SUCCESS));
    this.injectComment(response);
    this.removeCaptcha();
    $('span#charsLeft').empty().text(0);
};

zl.CommentManager.prototype.removeCaptcha = function(data,context,response){
    var id = ls.RecaptchaManager.currentId;
    ls.RecaptchaManager.destroyCaptcha();
    $('.captchaContainer').empty().attr('id',id).removeClass();
};

zl.CommentManager.prototype.onFormFail = function(data,context,response){
    for (var p in response) {
        var fieldName = response[p];
        var fieldNode = $("#"+this.fid+"_"+fieldName);
        var errorNode = $("#form-error_"+fieldName+" p");
        var errorCaptcha = $("#newCommentForm_body");
        errorNode.show();
        fieldNode.addClass("alert");
        fieldNode.bind("focus", ls.Delegate.create(this,
                                                   this.clearErrorMessage,
                                                   fieldName));
        errorCaptcha.bind("focus", ls.Delegate.create(this,
                                                      this.clearErrorMessage,
                                                      fieldName));
    }
    ls.RecaptchaManager.reloadCaptcha();
    this.dispatchEvent(new ls.AjaxEvent(events.FormEvent.FAIL,response));
};

zl.CommentManager.prototype.injectComment = function(comment){
    var re = new RegExp(/(\s|^)((http|https):\/\/([\w\d\-_\.]+)+\.([\w\-\.,@?^=%&amp;:/~\+#]*[\w\-\@?^=%&amp;/~\+#])?)/g);
    comment.body = comment.body.replace(re, '$1<a href="$2" rel="nofollow" target="_blank">$2</a>');
    re = new RegExp(/(\s|^)((www)+\.([\.\w\d\-_]+)+\.([\w\-\.,@?^=%&amp;:/~\+#]*[\w\-\@?^=%&amp;/~\+#])?)/g);
    comment.body = comment.body.replace(re, '$1<a href="http://$2" rel="nofollow" target="_blank">http://$2</a>');
    comment.body = comment.body.replace(/\n/g, "<br />");
    comment.c_time = this.getFormattedDateTime();
    var nodeId = "comments_in_content__" + this.params.contextkey;
    var target = $("ul.neue-listen", $(".commentbox"));
    this.template.parseTemplate(comment, "prepend", target);
    ls.LovelyUtils.scrollToNode(target);
    if (isIE) {
        // !!! quite bad IE hack !!!
        window.setTimeout(function(){
            $("#adsdf_830_250_95_f").hide().show();
        }, 200);
    }
};

zl.CommentManager.prototype.getFormattedDateTime = function(timestamp){
    var date = timestamp ? new Date(timestamp) : new Date();
    var day  = (date.getDate().toString().length == 1) ? "0"+date.getDate() : date.getDate();
    var month = (date.getMonth().toString().length == 1) ? "0"+date.getMonth() : date.getMonth();
    var year = date.getFullYear();
    var hours = (date.getHours().toString().length == 1) ? "0"+date.getHours() : date.getHours();
    var seconds = (date.getMinutes().toString().length == 1) ? "0"+date.getMinutes() : date.getMinutes();
    return day+"."+month+"."+year+" um "+hours+":"+seconds;
};

inheritClass(zl.CommentManager, zl.FormManager);


/****************************************
 * zl.GarbageCollector Singleton
 * @version 0.1
 * @author haudum
 ****************************************/

zl.GarbageCollector = {
    garbage : [],
    addItem : function(item) {
        this.garbage.push(item);
    },
    purge : function(){
        while (this.garbage.length > 0) {
            var item = this.garbage.pop();
            console.info("delete",item);
            delete item;
        }
    }
};
makeDispatchable(zl.GarbageCollector);


/****************************************
 * zl.SearchForm Singleton
 * @version 0.1
 * @author haudum
 ****************************************/

zl.SearchForm = {
    URL : "/search/${query}",
    params : {},
    helper : "",
    init : false,
    context : null,
    initSearch : function(node){
        this.params = node.getNSParams("ajax");
        this.context = $(this.params.input);
        this.context.val(this.params.helper);
        this.bind();
    },
    bind : function() {
        $(this.params.submit).bind("click", ls.Delegate.create(this,
                                                               this.submitSearch));
        $(this.params.input).bind("keydown", ls.Delegate.create(this,
                                                                this.keyDownHandler));
        this.context.bind("focus",
                          ls.Delegate.create(this,
                                             this.focusHandler,
                                             null));
        this.context.bind("click",
                          ls.Delegate.create(this,
                                             this.focusHandler,
                                             null));
        this.context.bind("blur",
                          ls.Delegate.create(this,
                                             this.blurHandler,
                                             null));
    },
    keyDownHandler : function(data,context,event){
        if (event.which == 13) {
            this.submitSearch();
        }
    },
    submitSearch : function(data,context,event){
        var query = encodeURIComponent($(this.params.input).val().toLowerCase());
        if (query.length > 0 && query != " ") {
            window.location.href = ls.LovelyUtils.compileString(this.URL,{"query":query});
        }
    },
    blurHandler : function(data,context,event){
        this.context.css('color','#706E6E');
        if (this.context.val() == "") {
            this.context.val(this.params.helper);
        }
    },
    focusHandler : function(data,context,event){
        this.context.css('color','#000000');
        if (this.context.val() == this.params.helper) {
            this.context.val("");
        }
    }
};


/****************************************
 * zl.Utils Singleton
 * @version 0.1
 * @author haudum
 ****************************************/

zl.Utils = {
    DEBUG : false,
    listEditor : null,
    deleteKey : null,
    datasheetLoaded : false,

    deleteKey : null,
    favouriteId : null,
    favouriteKey : null,
    observedId : null,
    observedKey : null,

    ivwCode : function(){
        var code = app.IVW.code.match(/_ugc$/) ? app.IVW.code : app.IVW.code+"_ugc";
        return code;
    }(),

    switchMITab : function(){
        var context = $("div#images");
        $("div#beschreibung",context).toggle();
        $("div#datenblatt",context).toggle();
        $("li#beschreibungTab",context).toggleClass("active");
        $("li#datenblattTab",context).toggleClass("active");
    },
    switchToDatasheet : function(key){
        this.switchMITab();
        if(!this.datasheetLoaded){
            var mapping = [];
            mapping[0] = [];
            mapping[0]['field'] = "description";
            mapping[0]['target'] = $(".item-data",$("#datenblatt"));
            mapping[0]['defaultTxt'] = "Die Daten zu diesem Artikel sind noch nicht verfügbar. Bitte versuchen " + 
                                       "Sie es zu einem späteren Zeitpunkt wieder.";
            ls.LovelyUtils.ajaxRPC('/masteritems','getDescription',{'key': key}, mapping);
            this.datasheetLoaded = true;
        }
    },
    toggleLogin : function(){
        $("#header").toggleClass("login-inline");
        $(".userbox").toggle();
        $(".userbox-login-inline").toggle();
        $("#search-form").toggle();
        if ($("#header").hasClass("login-inline")) {
            $("#login_inline_form_username").focus();
            var user = getCookie("zl_uid");
            if (user) $("#login_inline_form_username").val(user);
        }
    },
    goToUrl : function(href){
        if(!href) return;
        window.location.href = href;
    },
    checkHash : function(){
        if (GLOBALS.URL.match(/\/user\/(.+)#willkommen$/)) {
            ls.OverlayManager.openOverlay("/willkommen");
            if (!isIE) window.location.hash = "";
        }
        if (GLOBALS.URL.match(/\/user\/(.+)#change_password$/)) {
            ls.OverlayManager.openOverlay("/revert_password_overlay");
            if (!isIE) window.location.hash = "";
        }
        if (GLOBALS.URL.match(/\/#profile_deleted$/)) {
            ls.OverlayManager.openOverlay("/profile_deleted");
            if (!isIE) window.location.hash = "";
        }
        if (GLOBALS.URL.match(/\/#login$/)) {
            ls.OverlayManager.openOverlay("/login_overlay");
            if (!isIE) window.location.hash = "";
        }
        if (GLOBALS.URL.match(/\/#register$/)) {
            ls.OverlayManager.openOverlay("/register_overlay");
            if (!isIE) window.location.hash = "";
        }
    },
    openListEditor : function(listKey){
        this.dispatchEvent(new ls.AjaxEvent(events.OverlayEvent.LOAD),null);
        var PAIR_TEMPLATE = '${key}=${value}';
        var op = {width : '815',
            height : '612',
            left: '10',
            top : '10',
            location :'no',
            scrollbars : 'no',
            toolbar : 'no',
            status : 'no',
            resizeable: 'no',
            menubar : 'no',
            innerWidth : '815',
            innerHeight : '582'
            };
        var optArray = [];
        for (key in op) {
            optArray.push(ls.LovelyUtils.compileString(PAIR_TEMPLATE, {key: key,
                                                                       value: op[key]}));
        }
        var optString = optArray.join(',');
        var name = "LIST_EDITOR";
        var url = listKey ? "/listeditor#"+listKey : "/listeditor";
        if (!this.listEditor || this.listEditor.closed) {
            this.listEditor = window.open(url,name,optString);
        } else {
            this.listEditor.location.href = url;
            if (listKey) this.listEditor.location.reload();
        }
        this.listEditor.focus();
    },
    confirmDeleteList : function(key){
        ls.OverlayManager.openOverlay("/delete_list_overlay");
        this.deleteKey = key;
    },
    deleteList : function(){
        if (this.deleteKey) {
            var proxy = new ls.JsonRPC("/lists");
            var params = {"o":this.deleteKey};
            proxy.addEventListener(events.RPCEvent.RESPONSE,
                                   ls.Delegate.create(this,
                                                      this.removeItemFromList,
                                                      null));
            proxy.call("delete",params);
        } else {
            console.log('Need a list id to delete list');
            return;
        }
    },
    removeItemFromList : function(data,context,response){
        if (response.error) {
            console.log("Could not delete list "+this.deleteKey);
        } else if (response.id){
            $("#list_"+this.deleteKey).parent().fadeOut();
            ls.OverlayManager.closeOverlay();
        }
        this.deleteKey = null;
    },
    confirmRemoveFavourite : function(id,key){
        ls.OverlayManager.openOverlay("/remove_favourite_overlay");
        this.favouriteId = parseInt(id,10);
        this.favouriteKey = key;
    },
    removeFavourite : function(){
        if (this.favouriteId) {
            var proxy = new ls.JsonRPC("/lists");
            var params = {"id":this.favouriteId,"v":false};
            proxy.addEventListener(events.RPCEvent.RESPONSE,
                                   ls.Delegate.create(this,
                                                      this.removeFavouriteFromList,
                                                      null));
            proxy.call("set_favourite",params);
        } else {
            console.log('Need a list id to delete list');
            return;
        }
    },
    removeFavouriteFromList : function(data,context,response){
        if (response.error) {
            console.log("Could not delete list "+this.favouriteKey);
        } else if (response.id){
            var elem = $("#list_"+this.favouriteKey).parent();
            var list = elem.parent();
            if (list.children().length <= 1) {
                var whoami = new ls.JsonRPC("/myprofile");
                whoami.addEventListener(events.RPCEvent.RESPONSE,
                                        ls.Delegate.create(this,
                                                           this.onNoList,
                                                           {'type':'favourites', 'list':list}));
                whoami.call("asDict",{});
            }
            elem.fadeOut("slow", function(){
                $(this).remove();
            });
            ls.OverlayManager.closeOverlay();
        }
        this.favouriteId = null;
        this.favouriteKey = null;
    },
    confirmRemoveObserved : function(key){
        ls.OverlayManager.openOverlay("/remove_observed_overlay");
        this.observedId = key;
        this.observedKey = key;
    },
    removeObserved : function(){
        if (this.observedId) {
            var proxy = new ls.JsonRPC("/profiles");
            var params = {"o":this.observedId,"v":false};
            proxy.addEventListener(events.RPCEvent.RESPONSE,
                                   ls.Delegate.create(this,
                                                      this.removeObservedFromList,
                                                      null));
            proxy.call("set_observed",params);
        } else {
            console.log('Need a list id to delete list');
            return;
        }
    },
    removeObservedFromList : function(data,context,response){
        if (response.error) {
            console.log("Could not delete profile "+this.observedKey);
        } else if (response.id){
            var elem = $("#profile_big_"+this.observedKey).parent();
            var list = elem.parent();
            if (list.children().length <= 1) {
                var whoami = new ls.JsonRPC("/myprofile");
                whoami.addEventListener(events.RPCEvent.RESPONSE,
                                        ls.Delegate.create(this,
                                                           this.onNoList,
                                                           {'type':'observed', 'list':list}));
                whoami.call("asDict",{});
            }
            elem.fadeOut("slow", function(){
                $(this).remove();
            });
            ls.OverlayManager.closeOverlay();
        }
        this.observedId = null;
        this.observedKey = null;
    },
    onNoList : function(data, context, response){
        console.log(data,context, response);
        if (response.result[data.type].length < 1) {
            data.list.parent().fadeOut("slow", function(){
                $(this).remove();
            });
        } else {
            var id = data.list.parent().attr("id");
            var urlTemplate = data.list.parent().attr("ajax:base");
            var url = urlTemplate.replace(/^\/(.*)\/(\d)\/([^\/]+)$/, "/$1/1/$3")
            zl.AjaxLoader.load(url, id);
        }
    },
    register : function(){
        ls.OverlayManager.openOverlay('/register_overlay','reg_popup');
    },
    validateMultipleEmails : function(input){
        if (!input) false;
        var noError = true;
        var emails = input.val().split(",");
        for (var i=0; i<emails.length; i++) {
            var email = emails[i].trim();
            if (email != "") {
                var re = new RegExp(/^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$/);
                if (!re.test(email)) noError = false;
            }
        }
        return noError;
    },
    twitterList : function(title,url){
        var twitterURL = "http://twitter.com/home/?status=${title}:+${url}";
        if (zl.listRegExp.test(url)) {
            var shortURL = url.replace(zl.listRegExp, "$1$2$3");
        } else if (zl.itemRegExp.test(url)){
            var shortURL = url.replace(zl.itemRegExp, "$1die-10-$2-0");
        } else {
            return;
        }
        title = title.replace(/\s/g,"+");
        var url = ls.LovelyUtils.compileString(twitterURL, {"title": title,
                                                            "url"  : shortURL});
        window.open(url);
    }
};
makeDispatchable(zl.Utils);


zl.MediaItemManager = {
    URL : "/mediaitem_overlay",
    items : null,
    context : null,
    pos : 0,
    TEMPLATE_IMAGE : "<img class='mi-image' id='mi-image-${id}' src='${src}' title='${descr}' />",
    IMAGE_WIDTH : 814,
    IMAGE_HEIGHT : 590,
    TEMPLATE_YOUTUBE : "<object width='425' height='344'><param name='movie' value='${src}&hl=en&fs=1'></param><param name='allowFullScreen' value='true'></param><param name='allowscriptaccess' value='always'></param><embed src='${src}&hl=en&fs=1' type='application/x-shockwave-flash' allowscriptaccess='always' allowfullscreen='true' width='425' height='344'></embed></object>",
    YOUTUBE_WIDTH : 490,
    YOUTUBE_HEIGHT : 435,
    TEMPLATE_AUDIO : "<div id='playerCover'></div>\n<div id='player'></div>",
    AUDIO_WIDTH : 342,
    AUDIO_HEIGHT : 380,
    getMediaItems : function(key,media){
        if (!key || typeof key != "string") {
            if (this.DEBUG) console.log("No (valid) Key");
            return;
        }
        if (!this.items) {
            var proxy = new ls.JsonRPC("/files");
            var params = {"item":key,
                "fields":["url","description","mime_type"],
                "offset":0,
                "limit":100};
            proxy.addEventListener(events.RPCEvent.FAIL,
                                   function(){ console.log("Loading media items failed."); });
            proxy.addEventListener(events.RPCEvent.SUCCESS,
                                   ls.Delegate.create(this,
                                                      this.openMediaOverlay,
                                                      media));
            proxy.call("media_of",params);
        } else {
            this.openMediaOverlay(media);
        }
    },
    getItem : function(key){
        if (!key || typeof key != "string") {
            if (this.DEBUG) console.log("No (valid) Key");
            return;
        }
        var proxy = new ls.JsonRPC("/files");
        var params = {"key":key,
                      "fields":["url","description","mime_type"]};
        proxy.addEventListener(events.RPCEvent.FAIL,
                                   function(){ console.log("Loading media items failed."); });
        proxy.addEventListener(events.RPCEvent.SUCCESS,
                                   ls.Delegate.create(this,
                                                      this.openItemOverlay,
                                                      key));
        proxy.call("asDict",params);
    },
    openItemOverlay : function(data,context,response){
        ls.OverlayManager.addEventOnce(events.OverlayEvent.COMPLETE,
                                       ls.Delegate.create(this,
                                                          this.loadSingleItem,
                                                          response));
        ls.OverlayManager.openOverlay(this.URL);
    },
    loadSingleItem : function(data,context,event){
            var item = data;
            var src = item.url + "thumb_750x500";
            var html = ls.LovelyUtils.compileString(this.TEMPLATE_IMAGE,{"src":src,"descr":item.description});
            $("#mediaitem-body").empty().append(html);
            $("#mediaitem-descr").empty().append(item.description);
            this.checkNavigation("all");
    },
    getOffset : function(key){
        if (!key) return 0;
        var i = 0;
        for (i; i<this.items.length; i++) {
            if (this.items[i].key == key) return i;
        }
        return i;
    },
    openMediaOverlay : function(data,context,response) {
        if (data) {
            if (response) this.items = response.data;
            this.pos = this.getOffset(data);
        }
        ls.OverlayManager.addEventListener(events.OverlayEvent.COMPLETE,
                                           ls.Delegate.create(this,
                                                              this.loadItem,
                                                              null));
        ls.OverlayManager.openOverlay(this.URL);
    },
    beforeLoadItem : function(data,context,event){
        $("#mediaitem-body").fadeOut("slow",
                                     ls.Delegate.create(this,
                                                        this.loadItem,
                                                        data));
    },
    loadItem : function(data,context,event){
        ls.OverlayManager.removeEventListener(events.OverlayEvent.COMPLETE);
        this.context = $(".mediaitem-ol");
        $("#mediaitem-body").empty();
        if (data || data === 0) this.pos = parseInt(data,10);
        var data = this.items[this.pos];
        if (data.mime_type.match(/image\/.*/)) {
            var src = data.url + "thumb_750x500";
            var html = ls.LovelyUtils.compileString(this.TEMPLATE_IMAGE,{"src":src,"descr":data.description,"id":data.key});
            $(".mediaitem-ol").animate({"width":this.IMAGE_WIDTH,"height":this.IMAGE_HEIGHT},"slow");
            $("#mediaitem-body").animate({"width":this.IMAGE_WIDTH-64,"height":this.IMAGE_HEIGHT-90},
                                         "slow",
                                         ls.Delegate.create(this,
                                                            this.loadImage,
                                                            {"html":html,"data":data}));
        } else if (data.mime_type.match(/audio\/.*/)) {
            var html = this.TEMPLATE_AUDIO;
            var player = app.SETTINGS.media_url + "swf/mediaplayer.swf";
            var flashvars = {"file":encodeURIComponent(data.url)};
            var params = {"wmode":"transparent",
                          "allowScriptAccess":"always"};
            $(".mediaitem-ol").animate({"width":this.AUDIO_WIDTH,"height":this.AUDIO_HEIGHT},"slow");
            $("#mediaitem-body").animate({"width":this.AUDIO_WIDTH-64,"height":this.AUDIO_HEIGHT-90},
                                         "slow",
                                         function(){
                                             $("#mediaitem-body").append(html);
                                             $("#mediaitem-descr").empty().append(data.description);
                                             swfobject.embedSWF(player, "player", "280", "22", "9.0.0", "", flashvars, params);
                                         });
        } else if (data.mime_type.match(/video\/youtube/)) {
            var src = data.url.replace(/\/watch\?v=/,"/v/");
            var html = ls.LovelyUtils.compileString(this.TEMPLATE_YOUTUBE,{"src":src});
            $(".mediaitem-ol").animate({"width":this.YOUTUBE_WIDTH,"height":this.YOUTUBE_HEIGHT},"slow");
            $("#mediaitem-body").animate({"width":this.YOUTUBE_WIDTH-64,"height":this.YOUTUBE_HEIGHT-90},
                                         "slow",
                                         function(){
                                             $("#mediaitem-body").append(html);
                                             $("#mediaitem-descr").empty().append(data.description);
                                         });
        }
        this.checkNavigation();
    },
    loadImage : function(data,context,event) {
        $("#mediaitem-body").append(data.html);
        var image = $("img",$("#mediaitem-body"));
        data.image = image;
        image.hide();
        image.bind("load",
                   ls.Delegate.create(this,
                                      this.onImageLoaded,
                                      data));
    },
    onImageLoaded : function(data, context, event) {
        $("#mediaitem-body").show();
        $("#mediaitem-descr").empty().append(data.data.description);
        data.image.fadeIn("slow",
                          ls.Delegate.create(this,
                                             this.afterLoadItem,
                                             null));
    },
    afterLoadItem : function(data, context, event) {
        this.checkNavigation();
    },
    nextItem : function(){
        this.dispatchEvent(new ls.AjaxEvent(events.OverlayEvent.CHANGE, app.IVW.code));
        var next = this.pos+1;
        if (next < this.items.length) {
            this.beforeLoadItem(next);
        }
    },
    prevItem : function(){
        this.dispatchEvent(new ls.AjaxEvent(events.OverlayEvent.CHANGE, app.IVW.code));
        var prev = this.pos-1;
        if (prev >= 0) {
            this.beforeLoadItem(prev);
        }
    },
    checkNavigation : function(disable){
        var prevButton = $("a.nav_prev",this.context);
        var nextButton = $("a.nav_next",this.context);
        var spanGap = $("span.nav_gap",this.context);
        if(disable=="all"){
            prevButton.hide();
            nextButton.hide();
            spanGap.hide();
            return;
        }
        if (this.pos > 0) {
            prevButton.show();
        } else {
            prevButton.hide();
        }
        if (this.pos+1 >= this.items.length) {
            nextButton.hide();
        } else {
            nextButton.show();
        }
        if (this.pos > 0 && this.pos < this.items.length-1) {
            spanGap.show();
        } else {
            spanGap.hide();
        }
    }
};
makeDispatchable(zl.MediaItemManager);


zl.Tracking = {
    DEBUG : false,
    profilesProxy : null,
    listsProxy : null,
    init : function(){
        this.profilesProxy = new ls.JsonRPC("/profiles", GLOBALS.HOSTNAME);
        this.listsProxy = new ls.JsonRPC("/lists", GLOBALS.HOSTNAME);
        var cname = "tracked" + window.location.pathname.replace(/\//g,"_");
        if (!getCookie(cname)) {
            this.track();
            setCookie(cname,"true",null,window.location.pathname);
        } else {
            if (this.DEBUG) console.log(window.location.href + " already tracked");
        }
    },
    track : function(){
        var url = GLOBALS.URL.replace(/[#\?](.*)/,"");
        if (zl.listRegExp.test(url)) {
            var res = url.match(zl.listRegExp)[2];
            this.trackList(res);
        } else if (url.match(/\/user\/(.+)$/) && app.SETTINGS.auth == "True") {
            var res = url.match(/\/user\/(.+)$/)[1];
            this.trackProfile(res);
        } else if (url.match(/\/experten\/(.+)$/) && app.SETTINGS.auth == "True") {
            var res = url.match(/\/experten\/(.+)$/)[1];
            this.trackProfile(res);
        }
    },
    trackProfile : function(res){
        if (res) {
            var params = {"url_part":res};
            this.profilesProxy.call("visit",params);
            if (this.DEBUG) console.log("Track Profile "+res);
        }
    },
    trackList : function(res){
        if (res) {
            if (isNaN(res)) return;
            var id = parseInt(res,10);
            var params = {"id":id};
            this.listsProxy.call("count",params);
            if (this.DEBUG) console.log("Track List "+res);
        }
    }
};


zl.ListDashboard = {
    DEBUG : false,
    proxy : null,
    firstOpen : false,
    ivwCode : function(){
        var code = app.IVW.code.match(/_ugc$/) ? app.IVW.code : app.IVW.code+"_ugc";
        return code;
    }(),
    getListProperties : function(){
        if (this.getContext()) {
            var key = this.getContext().attr("ajax:key");
            var id = this.getContext().attr("ajax:id");
            var own = this.getContext().attr("ajax:isown");
            if (this.DEBUG) console.log(key,id,own);
            return {"key":key,"id":parseInt(id,10),"isOwn":eval(own)};
        } else {
            return null;
        }
    },
    getContext : function(){
        var node = $(".dashboard");
        if (node.length < 1) {
            node = $(".dashboard-large");
        }
        return node;
    },
    defaultTitleNode : function(){
        var node = $("#dashboardDefaultTitle", this.getContext());
        if (node) return node;
        return false;
    },
    init : function(){
        if (this.getContext()) {
            this.proxy = new ls.JsonRPC("/lists");
            $("ul.dashboardButtons a",this.getContext()).bind("mouseover",
                                                         ls.Delegate.create(this,
                                                                            this.onMouseOver,
                                                                            null));
            $("ul.dashboardButtons a",this.getContext()).bind("mouseout",
                                                         ls.Delegate.create(this,
                                                                            this.onMouseOut,
                                                                            null));
            $("ul.dashboardButtons a",this.getContext()).bind("click",
                                                         ls.Delegate.create(this,
                                                                            this.setFallback,
                                                                            null));
            if (this.getListProperties().isOwn) {
                $("strong#otherOpinion").hide();
                $("strong#editOwn").show();
            }
        } else {
            if (this.DEBUG) console.log("No dashboard available.");
        }
    },
    auth : function(){
        return eval(app.SETTINGS.auth.toLowerCase());
    },
    board : function(action,needsAuth){
        if (needsAuth && !this.auth()) {
            ls.OverlayManager.openOverlay("/login_overlay","login_popup");
            return false;
        } else {
            if (this.DEBUG) console.log("board::",action);
            $(".dashboardItem",this.getContext()).hide();
            if (action) $("#"+action).show();
            return true;
        }
    },
    hideBoard : function(){
        $(".dashboardItem",this.getContext()).hide();
        $("ul.dashboardButtons li",this.getContext()).removeClass("button-down");
    },
    nothing : function(){
        if (this.DEBUG) console.log("Dashboard: Do nothing.");
    },
    write : function(key){
        if (this.board("",true)) {
			var listKey =  key ? key : this.getListProperties().key;
            zl.Utils.openListEditor(listKey);
        }
    },
    recommend : function(node){
        this.setActive(node);
        this.board("dashboardRecommend",true);
    },
    submitRecommendation : function(fid){
        if (!this.proxy) return;
        var context = fid ? $("#"+fid) : $("form#dashboardRecommendForm");
        var message = $("#"+fid+"_message",context).val();
        var email = $("#"+fid+"_email",context).val();
        var key = $("#"+fid+"_key",context).val();
        var regexEmail = new RegExp(/^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$/);
        if (!regexEmail.test(email)) {
            $("#"+fid+"_email",context).addClass("alert").bind("focus",function(){
                $(this).removeClass("alert");
            });
            return;
        }
        if (message.length > 300) {
            $("#"+fid+"_message",context).addClass("alert").bind("focus",function(){
                $(this).removeClass("alert");
            });
            return;
        }
        this.proxy.removeEventListener(events.RPCEvent.RESPONSE);
        this.proxy.addEventOnce(events.RPCEvent.RESPONSE,
                                ls.Delegate.create(this,
                                                   this.onRecommendResponse,
                                                   null));
        var params = {"o":key,
                      "recipient":email,
                      "message":message};
        this.proxy.call("recommend",params);
    },
    onRecommendResponse : function(data,context,response){
        if (response.result === true) {
            $("#dashboardRecommendForm_email").val("");
            $("#dashboardRecommendForm_message").val("");
            this.board("dashboardRecommendSuccess");
            this.setActive();
            this.dispatchEvent(new ls.AjaxEvent(events.UserEvent.ACTION,'popup_mail'));
        }
    },
    countChars : function(field,max){
        var len = $(field).val().length;
        if (len >= max) {
            $(field).val($(field).val().substr(0,len));
        }
    },
    compliment : function(){
        if (!this.proxy) return;
        if (this.board("",true)) {
            var id = this.getListProperties().id;
            if (!id) return;
            this.proxy.removeEventListener(events.RPCEvent.RESPONSE);
            this.proxy.addEventOnce(events.RPCEvent.RESPONSE,
                                    ls.Delegate.create(this,
                                                       this.onComplimentResponse,
                                                       null));
            var params = {"id":id};
            this.proxy.call("compliment",params);
        }
    },
    onComplimentResponse : function(data,context,response){
        if (response.result) {
            this.board("dashboardCompliment");
            this.setVoted();
            this.dispatchEvent(new ls.AjaxEvent(events.UserEvent.ACTION,this.ivwCode));
        } else if (response.result === false) {
            this.board("dashboardComplimentError");
        } else if (response.error) {
            this.board("dashboardComplimentOwn");
        }
        this.setActive();
    },
    setVoted : function(){
        var node = $("ul.dashboardButtons a.i00",this.getContext());
        node.addClass("i00voted");
        node.removeClass("i00");
        var digg = $("a.digg", $("#content"));
        if (digg.length == 1) {
            digg.html(parseInt(digg.html(),10)+1);
        }
    },
    addFavourite : function(){
        if (!this.proxy) return;
        if (this.board("",true)) {
            var id = this.getListProperties().id;
            if (!id) return;
            this.proxy.removeEventListener(events.RPCEvent.RESPONSE);
            this.proxy.addEventOnce(events.RPCEvent.RESPONSE,
                                    ls.Delegate.create(this,
                                                       this.onFavResponse,
                                                       null));
            var params = {"id":id,"v":true};
            this.proxy.call("set_favourite",params);
        }
    },
    onFavResponse : function(data,context,response){
        if (response.result) {
            console.log(response);
            this.dispatchEvent(new ls.AjaxEvent(events.UserEvent.ACTION,this.ivwCode));
            this.board("dashboardFavourite");
            this.setFavoured();
        } else {
            this.board("dashboardFavouriteError");
        }
        this.setActive();
    },
    setFavoured : function(){
        var node = $("ul.dashboardButtons a.i03",this.getContext());
        node.addClass("i03favoured");
        node.removeClass("i03");
    },
    bookmark : function(node){
        if (!this.isActive(node)) this.dispatchEvent(new ls.AjaxEvent(events.UserEvent.ACTION,this.ivwCode));
        this.setActive(node);
        this.board("dashboardBookmark");
    },
    embedCode : function(node){
        if (!this.isActive(node)) this.dispatchEvent(new ls.AjaxEvent(events.UserEvent.ACTION,this.ivwCode));
        this.setActive(node);
        this.board("dashboardEmbedCode");
    },
    comment : function(text){
        var form = $("#newCommentForm");
        if (form.length > 0){
            var node = $("#newCommentForm_body");
            node.val(text);
            node.focus();
            ls.LovelyUtils.scrollToNode(node);
        } else {
            if (this.DEBUG) console.log('No new CommentForm found');
            return;
        }
    },
    print : function(){
        this.dispatchEvent(new ls.AjaxEvent(events.UserEvent.ACTION, app.IVW.code));
        var pdoc = window.open("/printlist/"+this.getListProperties().key,"_printdoc_","width=810,height=582,left=10,top=10,toolbar=no,status=no,scrollbars=yes,resizable=yes");
    },
    blame : function(node){
        if (!this.auth()) {
            ls.OverlayManager.openOverlay("/blame_overlay","popup_mail");
        } else {
            this.setActive(node);
            this.board("dashboardBlame",true);
        }
    },
    blameUser : function(node){
        if (!this.auth()) {
            ls.OverlayManager.openOverlay("/blame_user_overlay","popup_mail");
        } else {
            if (this.isActive(node)) {
                this.reset(node);
            } else {
                this.setActive(node);
                this.board("dashboardBlame",true);
                $("#dashboardBlameForm").resetForm();
            }
        }
    },
    submitBlame : function(fid){
        if (!this.proxy) return;
        var context = fid ? $("#"+fid) : $("form#dashboardBlameForm");
        var message = $("#"+fid+"_message",context).val();
        var key = $("#"+fid+"_o",context).val();
        if (message.length > 300) {
            $("#"+fid+"_message",context).addClass("alert").bind("focus",function(){
                $(this).removeClass("alert");
            });
            return;
        }
        this.proxy.removeEventListener(events.RPCEvent.RESPONSE);
        this.proxy.addEventOnce(events.RPCEvent.RESPONSE,
                                ls.Delegate.create(this,
                                                   this.onBlameResponse,
                                                   null));
        var params = {"o":key,
                      "message":message};
        this.proxy.call("report",params);
    },
    submitUserBlame : function(fid){
        var context = fid ? $("#"+fid) : $("form#dashboardBlameForm");
        var message = $("#"+fid+"_message",context).val();
        var key = $("#"+fid+"_o",context).val();
        if (message.length > 300) {
            $("#"+fid+"_message",context).addClass("alert").bind("focus",function(){
                $(this).removeClass("alert");
            });
            return;
        }
        var proxy = new ls.JsonRPC("/profiles");
        proxy.addEventOnce(events.RPCEvent.RESPONSE,
                           ls.Delegate.create(this,
                                              this.onBlameResponse,
                                              null));
        var params = {"o":key,
                      "message":message};
        proxy.call("report",params);
    },
    onBlameResponse : function(data,context,response){
        if (response.result) {
            this.dispatchEvent(new ls.AjaxEvent(events.UserEvent.ACTION,"popup_mail"));
            this.board("dashboardBlameSuccess");
        } else {
            this.board("dashboardBlameError");
        }
    },
    followUser : function(node){
        var follow = eval(node.attr("ajax:attr"));
        if (this.board("",true)) {
            var key = this.getListProperties().key;
            if (!key) return;
            var proxy = new ls.JsonRPC("/profiles");
            proxy.addEventOnce(events.RPCEvent.RESPONSE,
                               ls.Delegate.create(this,
                                                  this.onFollowUser,
                                                  {"follow":follow,"node":node}));
            var params = {"o":key,"v":follow};
            proxy.call("set_observed",params);
        }
    },
    onFollowUser : function(data,context,event){
        var newState = !data.follow;
        data.node.attr("ajax:attr",newState.toString());
        var node = $("ul.profileFeatures li#followUserItem",this.getContext());
        if (newState === true) {
            node.removeClass("button-down");
            data.node.text("... beobachten");
        } else if (newState === false) {
            node.addClass("button-down");
            data.node.text("... nicht mehr beobachten");
            this.dispatchEvent(new ls.AjaxEvent(events.UserEvent.ACTION,this.ivwCode));
        }
    },
    inviteFriend : function(){
        if (this.board("",true)) {
            ls.OverlayManager.openOverlay('/invite_friends_overlay',"popup_mail");
        }
    },
    onMouseOver : function(data,context,event){
        var id = context.attr("ajax:helper");
        var action = "show";
        this.helper(id,action);
    },
    onMouseOut : function(data,context,event){
        var action = "hide";
        this.helper(1,action);
    },
    setActive : function(args){
        var args = (args) ? args : null;
        $("ul.dashboardButtons li",this.getContext()).removeClass("button-down");
        if (args != null) {
            $(args[0]).parent().addClass("button-down");
        }
    },
    isActive : function(node) {
        if (node) {
            return $(node[0]).parent().hasClass("button-down");
        } else {
            false;
        }
    },
    reset : function(node){
        if (node) {
            $(node[0]).parent().removeClass("button-down");
        }
        return this.board();
    },
    helper : function(id,action){
        if (!id || !action) {
            if (this.DEBUG) console.log("No id or action for helper.");
            return;
        }
        var text = $("#"+id, this.getContext()).text();
        if (!action || action == "show") {
            this.defaultTitleNode().text(text);
            if (this.DEBUG) console.log("show",id);
        } else if (action == "hide") {
            this.defaultTitleNode().text();
            if (this.DEBUG) console.log("hide",id);
        }
    },
    setFallback: function(data,context){
        var node = context;
        this.setActive(node);
        var id = node.attr('ajax:helper');

        if(!id){
            if (this.DEBUG) console.log("No id or action for helper.");
            return;
        }
        var text = $("#"+id, this.getContext()).text();
        this.defaultTitleNode().text(text);
    }
};
makeDispatchable(zl.ListDashboard);


/*
 * zl.UserImageUpload
 * upload/replace user image
 * upload to AWS cloud
 * @author haudum
 */
zl.UserImageUpload = {
    context : null,
    fid : null,
    uploader : null,
    USER_IMAGES : [[".profile-box-user-image","thumbc_72x96"],
                   [".login-box-user-image","thumbc_36x48"]],
    MESSAGE_ERROR : "Das Bild konnte wegen eines Fehlers nicht hochgeladen werden.",
    init : function(node){
        this.context = node ? node : null;
        this.fid = this.context.attr("id") ? this.context.attr("id") : "uploadForm";
        this.uploader = new ls.AWSUploader(this.context);
        this.uploader.addEventListener(events.AjaxEvent.SUCCESS,
                                       ls.Delegate.create(this,
                                                          this.onSuccess,
                                                          null));
        this.uploader.addEventListener(events.AjaxEvent.FAIL,
                                       ls.Delegate.create(this,
                                                          this.onFail,
                                                          null));
        zl.GarbageCollector.addItem(this.uploader);
        $("#"+this.fid+"_agbs").bind("click", ls.Delegate.create(this,
                                                                 this.activateButton,
                                                                 null));
    },
    switchTabs : function(){
        $(".widgetTab").toggle();
    },
    submitUpload : function(){
        var fileNode = $("#"+this.fid+"_file",this.context);
        if (fileNode.val().length != 0) {
            this.updateStatus("Bild wird hochgeladen ...");
            $("#"+this.fid+"_agbs").trigger("click");
            this.uploader.requestPolicy();
        } else {
            this.updateStatus();
            $("#form-error_"+this.fid+"_file p").show();
            fileNode.addClass("alert");
            fileNode.bind("focus",ls.Delegate.create(this,
                                                     this.clearErrors,
                                                     null));
        }
    },
    onSuccess : function(data,context,response){
        this.updateStatus("Bild erfolgreich hochgeladen.");
        this.lastFile = response;
        this.writeToProfile();
    },
    onFail : function(data,context,response){
        this.updateStatus("Das Bild konnte nicht hochgeladen werden.",true);
    },
    activateButton : function(data,context,event){
        var button = $("#upload-button",this.context);
        if (button.hasClass("upload-inactive")) {
            button.removeClass("upload-inactive");
            button.addClass("upload-active");
            button.bind("click",ls.Delegate.create(this,
                                                   this.submitUpload,
                                                   null));
        } else {
            button.removeClass("upload-active");
            button.addClass("upload-inactive");
            button.unbind("click");
        }
    },
    replaceImage : function(url){
        this.updateStatus("Userbild wird ersetzt.");
        for (var i=0; i<this.USER_IMAGES.length; i++) {
            var node = $(this.USER_IMAGES[i][0]);
            var size = this.USER_IMAGES[i][1];
            node.attr("src",url+size);
        }
    },
    writeToProfile : function(){
        this.updateStatus("Neues Bild im Profil speichern.");
        var proxy = new ls.JsonRPC("/myprofile");
        var data = {"data":{"image":this.lastFile.key}};
        proxy.addEventListener(events.RPCEvent.SUCCESS,
                               ls.Delegate.create(this,
                                                  this.getFileInfo,
                                                  null));
        proxy.call("update",data);
    },
    getFileInfo : function(){
        var proxy = new ls.JsonRPC("/files");
        var data = {"key":this.lastFile.key};
        proxy.addEventListener(events.RPCEvent.SUCCESS,
                               ls.Delegate.create(this,
                                                  this.finishUpload,
                                                  null));
        proxy.call("asDict",data);
    },
    finishUpload : function(data,context,response){
        this.replaceImage(response.url);
        this.switchTabs();
        this.updateStatus();
    },
    updateStatus : function(msg,anim){
        var message = msg ? msg : "";
        var hideAnimation = anim ? true : false;
        var node = $("#upload-status",this.context);
        node.empty().append(message);
        if (msg && !hideAnimation) {
            $("#uploading").show();
        } else {
            $("#uploading").hide();
        }
    },
    clearErrors : function(){
        $(".form-error p",this.context).hide();
        $(".alert",this.context).removeClass("alert");
    }
};


/*
 * Ich im Netz
 * add and remove profile links
 * @author haudum
 */
zl.IchImNetz = {
    context : null,
    proxy : null,
    parser : null,
    DEBUG : false,
    first : true,
    PROVIDER : {"Facebook"  :"^http:\/\/(.*)facebook.com\/people\/[\\w\\d\\.\\-_]+\/[\\d]+\/?\\??$",
                "Flickr"    :"^http:\/\/(.*)flickr.com\/(photos|people)\/[\\w\\d]+\/?$",
                "Friendfeed":"^http:\/\/(.*)friendfeed.com\/[\\w\\d]+\/?$",
                "Last.fm"   :"^http:\/\/(.*)last\.?fm[^\/]*\/(user|music)/[\\d\\w\\-\\+]+\/?$",
                "Myspace"   :"^http:\/\/(.*)myspace.com\/[\\d\\w]+$",
                "Twitter"   :"^(http|https):\/\/(.*)twitter.com\/[\\d\\w\\._\\-]+\/?$",
                "Wordpress" :"^http:\/\/[\\w\\d]+.wordpress.com\/?",
                "Xing"      :"^(http|https):\/\/(.*)xing.(de\|com)\/profile\/[\\d\\w\\.\\-_]+\/?$"},
    init : function(node){
        if (!node) {
            return;
        } else {
            this.first = true;
            this.context = node;
            this.fid = node.attr("id");
            this.params = this.context.getNSParams("ajax");
            this.parser = new ls.TemplateParser($(this.params.template));
            this.context.bind("submit", ls.Delegate.create(this,
                                                           this.addItem,
                                                           null));
            $("#"+this.fid+"_url", this.context).bind("focus",
                                                      ls.Delegate.create(this,
                                                                         this.hideMessages,
                                                                         null));
            this.proxy = new ls.JsonRPC(this.params.endpoint);
            this.proxy.addEventListener(events.RPCEvent.SUCCESS,
                                        ls.Delegate.create(this,
                                                           this.onSuccess,
                                                           null));
            this.proxy.call("asDict",{});
        }
    },
    addItem : function(data,context,event){
        if (event) event.preventDefault();
        var v = $(this.params.validator,this.context);
        var url = $(this.params.profileurl,this.context).val();
        var validation = new RegExp(v.val());
        if (validation.test(url)) {
            this.links.push(url);
            this.submit();
        } else {
            this.notValidUrl();
        }
    },
    removeItem : function(pos){
        this.links.splice(pos,1);
        this.submit();
    },
    submit : function() {
        var data = {data:{"links":this.links}};
        this.proxy.call(this.params.method,data);
    },
    onSuccess : function(data,context,response){
        if (!this.first) {
            $("#"+this.fid+"_saved").show();
        } else {
            this.first = false;
        }
        this.parser.clearNode();
        this.links = response.links;
        if(!this.links || this.links.length == 0) {
            $(".profile-links-box").hide();
        } else {
            $(".profile-links-box").show();
        }
        $(this.params.profileurl,this.context).val("");
        for (var i=0; i<this.links.length; i++) {
            var url = this.links[i];
            var provider = "Unbekanntes Profil";
            for (key in this.PROVIDER) {
                var re = new RegExp(this.PROVIDER[key]);
                if (re.test(url)) provider = key;
            }
            this.parser.parseTemplate({"pos":i,
                                       "url":url,
                                       "provider":provider,
                                       "short":provider.toLowerCase().replace(/[\.\-\n\s]/g,"")});
        }
    },
    onFail : function(data,context,response){
    },
    notValidUrl : function(){
        $("#"+this.fid+"_url", this.context).addClass("alert");
        $("#form-error_"+this.fid+"_url p",this.context).show();
    },
    hideMessages : function(){
        $("#"+this.fid+"_url", this.context).removeClass("alert");
        $("#"+this.fid+"_saved").hide();
        $("#form-error_"+this.fid+"_url p",this.context).hide();
    }
};


/***************************************
 * zl.AjaxHistory Singleton
 * @version 0.1
 * @author haudum
 * @depends on ReallySimpleHistory (RSH)
 ****************************************/

zl.AjaxHistory = {
	rsh : dhtmlHistory,
    base : {},
    history : {},
	lastHash : null,
	init : function(){
        if (isSafari) {
            return;
        } else {
            this.setDefault();
            var hash = window.location.hash;
            if (hash != "") {
                this.historyChanged(null,null,hash.substr(1));
            }
	        this.rsh.initialize();
	        this.rsh.addListener(ls.Delegate.create(this,
	                                                this.historyChanged,
	                                                null))
        }
	},
    change : function(data,context,action){
        var hKey = action["key"];
        var hVal = action["value"];
        if (!hKey || !hVal || hKey.match(/comment/)) {
            console.log("No history action key defined or action key "+hKey+" filtered");
            return;
        } else {
            this.history[hKey] = hVal;
            var entry = this.createHash();
            this.rsh.add(entry);
            this.lastHash = entry;
        }
    },
	historyChanged : function(data,context,hash){
	    if (decodeURIComponent(hash) == decodeURIComponent(this.lastHash)) {
            console.log("SAME HASH:"+hash+", "+this.lastHash);
            return;
        }
        var newHashObject = this.parseHash(hash);
        var oldHashObject = this.parseHash(this.lastHash);
        for (id in oldHashObject) {
            if (!newHashObject[id]) {
                var base = this.base[id] ? this.base[id] : "DEFAULT";
                newHashObject[id] = base;
            }
        }
        this.dispatchEvent(new ls.AjaxEvent(events.AjaxEvent.HISTORY,newHashObject));
	},
    createHash : function(){
        var hash = "/";
        var len = 0;
        for (h in this.history) { len++; }
        var l = 0;
        for (h in this.history) {
            hash += h + "=" + this.history[h];
            if (l < len-1) hash += "&";
            l++;
        }
        return hash;
    },
    parseHash : function(hashString){
        if (typeof hashString == "string" && hashString.substr(1).isQueryString()) {
            var p = ls.LovelyUtils.parseQueryString(hashString.substr(1));
            return p;
        } else {
            return {};
        }
    },
    setDefault : function(){
        var divs = $("div");
        for (var i=0; i<divs.length; i++) {
            var obj = $(divs[i]);
            var params = obj.getNSParams("ajax");
            if (params.base) {
                var key = obj.attr("id");
                this.base[key] = params.base;
            }
        }
    }
};
makeDispatchable(zl.AjaxHistory);



/***************************************
 * zl.IVWTracking Singleton
 * @version 0.1
 * @author haudum
 * @depends on ReallySimpleHistory (RSH)
 ****************************************/

zl.IVWTracking = {

    DEBUG : false,

    IVW_URL : "<img src='http://${account}.ivwbox.de/cgi-bin/ivw/${type}/${code};${comment}?r=${referrer}&d=${rand}' width='1' height='1' alt='szmtag' />",

    conf : {},

    init : function(conf){
        this.conf = conf;
    },

    countIVW : function(code){
        var lcode = code ? code : this.conf.code;
        if (!lcode || !this.conf.account || !this.conf.type) {
            if (this.DEBUG) console.log("IVW: Try fallback ...");
            if (app && app.IVW) {
                this.conf = app.IVW;
                lcode = code ? code : this.conf.code;
                if (!lcode || !this.conf.account || !this.conf.type) {
                    if (this.DEBUG) console.log("IVW: Aborting ...");
                    return;
                }
            } else {
                if (this.DEBUG) console.log("IVW: Aborting ...");
                return;
            }
        }
        var pixel = ls.LovelyUtils.compileString(this.IVW_URL,{"account":this.conf.account,
                                                               "type":this.conf.type,
                                                               "code":lcode,
                                                               "comment":this.conf.comment,
                                                               "referrer":document.referrer,
                                                               "rand":Math.random()*100000});
        $("body").append(pixel);
        if (this.DEBUG) console.log("IVW: Inject "+pixel+" ...");
    }
};