function LayerFactory(trigger, content, options) {
    return this.init(trigger, content, options);
}

LayerFactory.prototype = new function() {
    this.init = function(trigger, content, options) {
        if (!trigger) { return true; }
        if (!trigger.layerFactory) {
            trigger.layerFactory = this;
            this.trigger = trigger;
            this.options = this.normalizeOptions(options, content) || options || {};
            this.format();
            $(trigger).addClass('layer-anchor');
        } else { return; }
        if (this.options.on != 'mouseover') {
            window.setTimeout(function() { this.showLayer(); }.bind(this), ((this.options || {}).showDelay || 0));
        }
        if ((this.options || {}).onload && $.isFunction(this.options.onload)) {
            this.options.onload.bind(this)();
        }
        return false;
    };
    this.format = function() {
        this.layer = $(('<div class="layer-root '+this.options.layerClass+' '+(this.options.classNames || 0)+' '+(this.options.hook ? 'hook-'+this.options.hook : '')+'">'+
        '<div class="layer-container">'+
            '<table><tbody>'+
                '<tr class="top"><td class="left"></td><td class="center"><div class="layer-hook"></div></td><td class="right"></td></tr>'+
                '<tr class="center">'+
                    '<td class="left"></td>'+
                    '<td class="center">'+this.getContentWrapper()+'</td>'+
                    '<td class="right"></td>'+
                '</tr>'+
                '<tr class="bottom"><td class="left"></td><td class="center"><div class="layer-hook"></div></td><td class="right"></td></tr>'+
            '</tbody></table>'+
        '</div>'+
        ($.ua.browser.msie ? '<iframe class="layer-background-fixer" frameborder="0" src="//img.ui-portal.de/p.gif"></iframe>' : '')+
        '</div>')).appendTo('body');
        this.content = $('tr.center td.center *:empty', this.layer).append(this.getContent()).show();
        this.layer.width(this.options.layerWidth || this.layer.width());
        if ($.ua.browser.msie) {
            window.setTimeout(function() {
                $('iframe.layer-background-fixer', this.layer).width($('tr.center td.center', this.layer).outerWidth()).height($('tr.center td.center', this.layer).outerHeight());
            }.bind(this), 15);
        }
        this.setPosition();
        this.layer.hide();
        $('table:first', this.layer).attr('width', '100%');
        if (this.options.loading) {
            this.loading = $('<div class="layer-loading-animation '+(this.options.loadingClassNames || '')+'"><h1><span>Loading...</span></h1>'+
            (!$.support.opacity ? '<iframe class="layer-loading-background-fixer" frameborder="0" src="//img.ui-portal.de/p.gif"></iframe>' : '')+
            '</div>').appendTo('body').bind('click', function() { this.hideLayer(); }.bind(this)).height($(window).height()+
            $('body').height()).width($(window).width());
            $('.layer-loading-background-fixer', this.loading).width(this.loading.width()).height(this.loading.height());
        }
        $(this.trigger).removeAttr('on'+this.options.on);
        if (this.options.on == 'mouseover') {
            if (this.options.showDelay || this.options.hideDelay) {
                if (!this.options.mousePosition) {
                    $(this.layer).hover(function() { this.hover = true; }.bind(this),
                        function() { this.hover = false; }.bind(this));
                }
                $(this.trigger).bind('mousemove', function() {
                    if (this.layer.active) { return; }
                    if (!this.showTimeout) {
                        this.showTimeout = window.setTimeout(function() { this.showLayer(); }.bind(this), (this.options.showDelay || 0));
                    }
                    if (this.hideInterval) {
                        window.clearInterval(this.hideInterval);
                        this.hideInterval = null;
                    }
                }.bind(this)).bind('mouseleave', function() {
                    if (!this.hideInterval) {
                        this.hideInterval = window.setInterval(function() {
                            if (!this.hover) {
                                this.hideLayer();
                                window.clearInterval(this.hideInterval);
                                this.hideInterval = null;
                            }
                        }.bind(this), (this.options.hideDelay || 60));
                    }
                    if (this.showTimeout) {
                        window.clearTimeout(this.showTimeout);
                        this.showTimeout = null;
                    }
                }.bind(this));
            } else {
                $(this.trigger).bind('mousemove', function() {
                    if (this.layer.active) { return; }
                    this.showLayer();
                }.bind(this)).bind('mouseleave', function() { this.hideLayer(); }.bind(this));
            }
        } else if (this.options.on == 'click') {
            if ($('.layer-close', this.content).length > 0) {
                $('.layer-close', this.content).click(function() { this.hideLayer(); }.bind(this));
            } else {
                this.layer.click(function() {
                    this.hideLayer();
                }.bind(this));
            }
            $(this.trigger).click(function() {
                if (!this.layer.active) { this.showLayer(); } else { this.hideLayer(); }
                return false;
            }.bind(this));
        } else if (this.options.on == 'focus') {
            $(this.trigger).bind('focus', function() { this.showLayer(); }.bind(this)
            ).bind('blur', function() { this.hideLayer(); }.bind(this));
        }
    };
    this.normalizeOptions = function(options, content) {
        return $.extend({}, (this.defaultOptions || {}), (options || {}),
            ({'content': (content || (options || {}).content)}));
    };
    this.getContentWrapper = function() {
        var wrapper = ['', ''];
        $.each((this.options.contentwrapper || [ 'layer-content' ]), function() {
            wrapper[0] += '<div class="'+this+'">';
            wrapper[1] += '</div>';
        });
        return wrapper[0]+wrapper[1];
    };
    this.getContent = function() {
        if (this.trigger.title) {
            var titleValue = this.trigger.title;
            try { this.trigger.title = ''; } catch(e) {
                try { this.trigger.removeAttribute('title'); } catch(e) {}
            }
        }
        if (this.options.ajaxUrl) {
            if ($.isFunction(this.options.onload)) { this.options.onload.bind(this)(); }
            this.options.onload = function() {
                $(this.content).load(this.options.ajaxUrl, function() {
					if (this.options.on == 'click' && $('.layer-close', this.content).length > 0) {
						$(this.layer).unbind('click');
						$('.layer-close', this.content).click(function() { this.hideLayer(); }.bind(this));
					}
                    this.showLayer();
                    if (this.loading) { $('h1', this.loading).hide(); }
                }.bind(this));
            }.bind(this);
            this.options.loading = true;
            return '';
        } else if (this.options.iframeUrl) {
            this.options.onload = function() {
                $('h1', this.loading).hide();
                this.showLayer();
            };
            window.closePageLayer = function() { this.hideLayer(); }.bind(this);
            return $('<iframe src="'+this.options.iframeUrl+'" frameborder="0"></iframe>').css(this.options.iframeSize || { width: ($(window).width() >> 1)+'px', height: ($(window).height() >> 1)+'px' });
        } else if (this.options.content) {
            if ($.isFunction(this.options.content)) { return this.options.content.bind(this)(); }
            if (this.options.content.clone) {
                $('script', this.options.content).remove();
                var content = $(this.options.content).clone(true);
                return content;
            }
            return this.options.content;
        }
        return titleValue;
    };
    this.abberatePosition = function(x) {
        this.mainleft = this.mainleft || $('#main').offset().left;
        this.mainwidth = this.mainwidth || $('#main').width();
        var a = 0;
        if (x < this.mainleft) {
            a = this.mainleft - x;
        } else if (x + this.layer.width() > this.mainleft + this.mainwidth) {
            a = this.mainleft + this.mainwidth - this.layer.width() - x;
        }
        return a;
    };
    this.setHookPosition = function(a) {
        if (!this.options.hook) { return; }
        var centerleft = ((this.layer.width() >> 1) - 16);
        $('.layer-hook', this.layer).css('margin-left', (centerleft + Math.min(centerleft, -(a || 0))) + 'px');
    };
    this.showPosition = false;
    this.hidePosition = false;
    this.setPosition = function() {
        if (this.options.layerPosition) {
            this.layer.css(this.options.layerPosition);
            this.setHookPosition();
        } else if (this.options.viewportPosition) {
            this.showPosition = function() {
                if (!this.layer.active) { return; }
                $('html').attr('style', 'overflow: hidden');
                this.layer.css({
                    'left': ($(window).scrollLeft() + (($(window).width() - this.layer.width()) >> 1)) + 'px',
                    'top':  ($(window).scrollTop() + (($(window).height() - this.layer.height()) >> 1)) + 'px'
                });
                if (this.loading) {
                     $('h1', this.loading).css({
                        'left': ($(window).scrollLeft() + (($(window).width() - $('h1', this.loading).width()) >> 1)) + 'px',
                        'top':  ($(window).scrollTop() + (($(window).height() - $('h1', this.loading).height()) >> 1)) + 'px'
                    });
                }
                if (!this.positionEvent) {
                    this.positionEvent = $(window).bind('resize', this.showPosition);
                }
            }.bind(this)
            this.hidePosition = function() {
                $(window).unbind('resize', this.positionEvent);
                window.setTimeout(function() { $('html').removeAttr('style'); }, 45);
                if (window.scrollBy) {
                    window.scrollBy(0,1);
                    window.scrollBy(0,-1);
                }
            }
        } else if (this.options.mousePosition) {
            this.showPosition = function() {
                if (!this.positionEvent) {
                    this.mousePosition = { x: false, y: false, a: false };
                    this.mainTop = $('#main').offset().top;
                    this.positionEvent = function(e) {
                        if (!e || !e.pageY || e.pageY <= (this.layerFactory.mainTop || 1)) {
                            this.layerFactory.hideLayer(); return;
                        }
                        if (!this.layerFactory.layer.active ||
                            this.layerFactory.mousePosition.x === e.pageX &&
                            this.layerFactory.mousePosition.y === e.pageY) { return; }
                        var x = (e.pageX - ((this.layerFactory.layer.width() || 0) >> 1));
                        var y = e.pageY - ((this.layerFactory.options.hook || '').match(/bottom/) ? (this.layerFactory.layer.height() || 0) : -15);
                        var a = this.layerFactory.abberatePosition(x);
                        this.layerFactory.layer.css({
                            'left': (x + a) + 'px',
                            'top': y + 'px'
                        });
                        if (a !== this.layerFactory.mousePosition.a) {
                            this.layerFactory.setHookPosition(a);
                        }
                        this.layerFactory.mousePosition = { x: e.pageX, y: e.pageY, a: a };
                    }
                    $(this.trigger).mousemove(this.positionEvent);
                }
            }
        } else {
            this.showPosition = function() {
                var triggeroffset = $(this.trigger).offset();
                var x = triggeroffset.left + (($(this.trigger).width() - this.layer.width()) >> 1 - $('.center .left', this.layer).outerWidth());
                var a = this.abberatePosition(x);
                this.layer.css({
                    'left': (x + a) + 'px',
                    'top': (triggeroffset.top - (this.options.hook != 'top' ? this.layer.height()-5 : -15)) + 'px'
                });
                this.setHookPosition(a);
            }
        }
    };
    this.showLayer = function() {
        this.layer.active = true;
        if (!this.options.onshow) { this.layer.show(); }
        else                      { this.options.onshow.bind(this)(); }
        if (this.showPosition)    { this.showPosition(); }
        if (this.loading) {
            $(this.loading).show();
            $('h1', this.loading).hide();
        }
        this.layer.appendTo('body');
        this.coverObj();
    };
    this.hideLayer = function() {
        if (!this.options.onhide) { this.layer.hide(); }
        else                      { this.options.onhide.bind(this)(); }
        if (this.hidePosition)    { this.hidePosition(); }
        if (this.loading) { $(this.loading).hide(); }
        this.uncoverObj();
        this.layer.active = false;
    };
    this.objProtected = function(idx) {
        var objOffset = $(this).offset();
        var mainOffset = $('#main').offset();
        var nn = $(this).get(0).nodeName.toLowerCase();
        if (nn == 'embed' && ($(this).attr('wmode') || 'window') != 'window' ||
            nn == 'object' && ($('param[name=wmode]', this).attr('value') || 'window') != 'window') { return false; }
        return !(objOffset.left > $('#main').width() + mainOffset.left ||
            objOffset.top + $(this).height() < mainOffset.top);
    };
    this.coverObj = function() {
        LayerFactory.coveredObj
            ? LayerFactory.coveredObj.add($('object:visible, embed:visible').not('.protected').not($('object, embed', this.layer)).filter(this.objProtected).css('visibility', 'hidden'))
            : LayerFactory.coveredObj = $('object:visible, embed:visible').not('.protected').not($('object, embed', this.layer)).filter(this.objProtected).css('visibility', 'hidden');
    };
    this.uncoverObj = function() {
        if (!LayerFactory.coveredObj || $('.layer-root:visible').length != 0) { return; }
        LayerFactory.coveredObj.css('visibility', 'visible');
        LayerFactory.coveredObj = null;
    };
};
LayerFactory.coveredObj = null;
LayerFactory.hideAll = function(root) {
    $('.layer-anchor', root).each(function() {
        if (this.layerFactory) { this.layerFactory.hideLayer(); }
    });
}

function TooltipLayer(trigger, content, options) {
    return this.init(trigger, content, options);
}

TooltipLayer.prototype = new LayerFactory();
TooltipLayer.prototype.defaultOptions = {
    'on':            'mouseover',
    'hook':          'bottom',
    'layerClass':    'tooltip',
    'mousePosition': true,
    'onload':        false
};
function InfoLayer(trigger, content, options) {
    return this.init(trigger, content, options);
}

InfoLayer.prototype = new LayerFactory();
InfoLayer.prototype.defaultOptions = {
    'on':           'click',
    'hook':         'bottom',
    'layerClass':   'infolayer',
    'onload':        false
};
function PageLayer(trigger, content, options) {
    return this.init(trigger, content, options);
}

PageLayer.prototype = new LayerFactory();
PageLayer.prototype.defaultOptions = {
    'on':               'click',
    'hook':             false,
    'loading':          true,
    'viewportPosition': true,
    'layerClass':       'pagelayer',
    'onload':           false
};
$(document).ready(function() {
    $('.layer-anchor.tooltip:not([onmouseover]):not([onclick])').each(function() {
        this.onmouseover = function() { new TooltipLayer(this, null); };
    });
    $('.layer-anchor.infolayer:not([onmouseover]):not([onclick])').each(function() {
        var content = /infolayer\-([\S]+)/.exec(this.className)[1];
        if (content) {
            this.onclick = function() { new InfoLayer(this, $('#'+content)); };
        }
    });
});