(function() {
    /* Overlay effect applelite ********************************************** */
    // AppleLite is effect that does not use img tag, instead it uses simple div
    // for performance reasons.
    //
    // This is code is based on original apple effect from jQuery Tools.
    // 
    // @author: Jari Pennanen
    // @version: 0.7
    // @date: July 2010
    var $ = jQuery;
    
    // version number
    var t = $.tools.overlay, 
        w = $(window);
    
    // extend global configuration with effect specific defaults
    $.extend(t.conf, {
        start: {
            //bottom: null,
            //right: null,
            top: null,
            left: null
        },
        fadeInSpeed: 'fast',
        zIndex: 9999
    });
    
    // utility function
    function getPosition(el) {
        var p = el.offset();
        if (el.data('overlayDialog')) {
            return el.data('overlayDialog').animOffset();
        }
        return {
            top: p.top + el.height() / 2,
            left: p.left + el.width() / 2
        };
    }
    
    var loadEffect = function(pos, onLoad) {
        var overlay = this.getOverlay(), 
            conf = this.getConf(), 
            trigger = this.getTrigger(), 
            self = this, 
            oWidth = overlay.outerWidth(true), 
            oHeight = overlay.outerHeight(true), 
            img = overlay.data("img");
        
        // growing image is required.
        if (!img) {
            // TODO: Following is error prone
            var bgClass = overlay.attr('class').split(" ")[0] + "-bg";
            img = $('<div />').addClass(bgClass);
            img.css({
                display: 'none'
            }).width(oWidth);
            $('body').append(img);
            overlay.data("img", img);
        }
        
        // initial top & left
        var itop = conf.start.top || Math.round(w.height() / 2), 
            ileft = conf.start.left || Math.round(w.width() / 2);
        
        if (trigger) {
            var p = getPosition(trigger);
            itop = p.top;
            ileft = p.left;
        }
        
        // initialize background image and make it visible
        img.css({
            position: 'absolute',
            top: itop,
            left: ileft,
            width: 0,
            height: 0,
            zIndex: conf.zIndex
        }).show();
        
        // put overlay into final position
        pos.top += w.scrollTop();
        pos.left += w.scrollLeft();
        pos.position = 'absolute';
        overlay.css(pos);
        overlay.css({
            display: 'block',
            visibility: 'hidden',
            opacity: 0
        });
        // begin growing
        img.animate({
            top: overlay.css("top"),
            left: overlay.css("left"),
            width: oWidth,
            height: oHeight
        }, conf.speed, function() {
            if (conf.fixed) {
                pos.top -= w.scrollTop();
                pos.left -= w.scrollLeft();
                pos.position = 'fixed';
                img.add(overlay).css(pos);
            }
            
            // set close button and content over the image
            overlay.css({
                zIndex: conf.zIndex + 1,
                visibility: "visible"
            }).animate({
                opacity: 1
            }, conf.fadeInSpeed, function() {
                img.hide(0);
                if (self.isOpened() && !$(this).index(overlay)) {
                    onLoad.call();
                }
                else {
                    overlay.hide(0);
                }
            });
        });
    };
    
    var closeEffect = function(onClose) {
        // variables
        var overlay = this.getOverlay().css('visibility', 'hidden'), 
            conf = this.getConf(), 
            trigger = this.getTrigger(), 
            img = overlay.data("img"), 
            css = {
                top: conf.start.top,
                left: conf.start.left,
                width: 0,
                height: 0
            };
        
        // trigger position
        if (trigger) {
            $.extend(css, getPosition(trigger));
        }
        
        img.width(overlay.outerWidth());
        img.height(overlay.outerHeight());
        
        // change from fixed to absolute position
        if (conf.fixed) {
            img.css({
                position: 'absolute'
            }).animate({
                top: "+=" + w.scrollTop(),
                left: "+=" + w.scrollLeft()
            }, 0);
        }
        
        // shrink image		
        img.animate(css, conf.closeSpeed, onClose);
    };
    
    // add overlay effect	
    t.addEffect("applelite", loadEffect, closeEffect);
    
    /* OvelayDialog ********************************************************* */
    // $.overlayDialog
    //
    // Uses jQuery Tools $.overlay to create iframe based dialogs.
    //
    // @author: Jari Pennanen
    // @version: 0.7
    // @date: July 2010
    $.overlayDialog = {};
    $.overlayDialog.framecount = 0;
    $.overlayDialog.defaults = {
        id: '',
        width: 0,
        height: 0,
        bottom: 0,
        url: 0,
        offset: 0,
        onIframeBeforeLoad: 0, // function (dialog, iframe, win)
        onIframeLoad: 0, // function (dialog, iframe, win)
        onIframeAfterLoad: 0, // function (dialog, iframe, win)
        // Submited handlers primary purpose is to create hooks that call success or error handler
        // e.g. hooking submit button to ajax call that after execution calls success or error.
        // Choices are: 
        //  json - TODO: JSON Submisted handler not implementend
        //  resize - Resize the dialog after loading to match the contents body height
        //  change-host - Take over all overlay dialogs in child window
        //  submit-loading - Loading animation when perssing submit
        //  html - Success if new window html is listed in success_htmls
        //  pathname - Success if new window pathname is listed in success_pathnames
        submitedHandlers: 'resize change-host submit-loading pathname html',
        successHandlers: 'close', // close refresh
        errorHandlers: '',
        success_htmls: ['saved', 'deleted', 'added', 'created'],
        success_pathnames: 0,
        overlay: {
            top: 20,
            effect: 'applelite',
            speed: 'fast',
            oneInstance: false,
            closeOnClick: false,
            fixed: false,
            /*
             mask: {
             color: 'black',
             loadSpeed: 0,
             closeSpeed: 0,
             opacity: 0.2
             },*/
            fadeInSpeed: 0,
            onBeforeLoad: function() {
                var that = this;
                var overlayEl = this.getOverlay();
                var overlayContent = overlayEl.find('.content').eq(0);
                var trigger = this.getTrigger();
                var overlayDialog = trigger.data('overlayDialog');
                overlayDialog.initIFrame(overlayEl, overlayContent);
            }
        }
    };
    $.overlayDialog.successHandlers = {
        'close': function() {
            this.close();
        },
        'refresh': function() {
            this.close();
            window.location.reload(); 
        }
    };
    $.overlayDialog.errorHandlers = {};
    $.overlayDialog.submitedHandlers = {
        'json': function(iframe, ifwin) {
        
        },
        'resize': function(iframe, ifwin) {
            if (this.getConf().height || this.getConf().width) {
                this.resize({
                    height: iframe.contents().find('body').outerHeight(true)
                });
            }
        },
        'change-host': function(iframe, ifwin) {
            // Change overlayDialog creator
            this.changeHostWindow(iframe, ifwin);
        },
        'submit-loading': function(iframe, ifwin) {
            // Adds loading state if submitted 
            // (should not be used on forms that has submit handler)
            var forms = iframe.contents().find('form');
            var self = this;
            forms.submit(function() {
                self.getOverlay().addClass('loading');
            });
        },
        'html': function(iframe, ifwin) {
            // Success if html matches
            var html = iframe.contents().find('body').html();
            if ($.inArray(html, this.getConf().success_htmls || []) != -1) {
                this.successHandler(null, 'html', iframe, ifwin);
            }
        },
        'pathname': function(iframe, ifwin) {
            // Success if window pathname is known
            if ($.inArray(ifwin.location.pathname, this.getConf().success_pathnames || []) != -1) {
                this.successHandler(null, 'pathname', iframe, ifwin);
            }
        }
    };
    $.overlayDialog.els = [];
    
    // OverlayDialog objects
    function OverlayDialog(el, opts) {
        // Privates
        var self = this, 
            fire = el.add(self), 
            callbacks = ['onSuccess', 'onError', 'onSubmited', 'onIframeLoad', 'onIframeAfterLoad', 'onIframeBeforeLoad'], 
            frames = window.frames;
        
        // callbacks
        function createCallbacks(options) {
            $.each(callbacks, function(i, name) {
                if (!self[name]) {
                    self[name] = function(fn) {
                        $(self).bind(name, fn);
                        return self;
                    };
                }
                // Bind options
                if (options[name]) {
                    self[name](options[name]);
                }
            });
        }
        
        createCallbacks(opts);
        
        // Use attr href if not given
        opts.url = opts.url || el.attr('href') || 0;
        
        //console.log('create:', opts.id);
        if (!el.attr('rel')) {
        
            el.attr('rel', '#overlay-dialog' + opts.id);
            //console.log('dialogi:', el.attr('rel'));
            if (!$("#overlay-dialog" + opts.id).length) {
            
                $('<div class="overlay-dialog"><a href="#" class="close">Close</a><div class="content"></div></div>').attr('id', 'overlay-dialog' + opts.id).prependTo('body');
            }
        }
        
        var overlay = el.overlay(opts.overlay).data('overlay');
        var overlayEl = overlay.getOverlay();
        var overlayContent = overlayEl.find('.content').eq(0);
        var closeBtn = overlayEl.find('.close').eq(0);
        var overlayOpts = overlay.getConf();
        var child_overlaydialog_opts = {
            overlay: {
                zIndex: overlayOpts.zIndex + 1,
                top: overlayOpts.top + 20
            },
            id: overlayOpts.zIndex + 1
        };
        
        var closeBtnHandler = function() {
            self.close();
            return false;            
        };
        
        var onIframeBeforeLoad = function(iframe, win) {
            overlayEl.addClass('loading');
        };
        
        var onIframeLoad = function(iframe, win) {
        };
        
        var onIframeAfterLoad = function(iframe, win) {
            overlayEl.removeClass('loading');
        };
        
        function _callHandlerList(str, handlers, args) {
            $.each((str || "").split(" "), function() {
                if (handlers[this]) 
                    handlers[this].apply(self, args);
            });
        }
        
        this.submitedHandler = function(iframe, ifwin) {
            fire.trigger('onSubmited', [iframe, ifwin]);
            _callHandlerList(opts.submitedHandlers, $.overlayDialog.submitedHandlers, [iframe, ifwin]);
        };
        
        this.successHandler = function(data, reason, iframe, ifwin) {
            fire.trigger('onSuccess', [data, reason, iframe, ifwin]);
            _callHandlerList(opts.successHandlers, $.overlayDialog.successHandlers, [data, reason, iframe, ifwin]);
        };
        
        this.errorHandler = function(data, reason, iframe, ifwin) {
            fire.trigger('onError', [data, reason, iframe, ifwin]);
            _callHandlerList(opts.errorHandlers, $.overlayDialog.errorHandlers, [data, reason, iframe, ifwin]);
        };
        
        closeBtn.click(closeBtnHandler);
        
        // Publics
        
        $.extend(this, {
            changeHostWindow: function(iframe, ifwin) {
                // Change the host of child window
                if (ifwin.jQuery && ifwin.jQuery.overlayDialog && ifwin.jQuery.overlayDialog.els) {
                    // Loop all overlayDialogs in child window
                    $.each(ifwin.jQuery.overlayDialog.els, function() {
                        // Destroy them from child window's jQuery
                        var od = this.data('overlayDialog');
                        if (!od) {
                            // Els is list, and may have multiple same el
                            return;
                        }
                        var conf = od.getConf();
                        var domEl = this.get(0);
                        this.unbind();
                        this.attr('rel', '');
                        this.data('overlay', null);
                        this.data('overlayDialog', null);
                        
                        // (Optional) One handler to help
                        //this.click(function (e) {
                        //	$(domEl).click(e);
                        //});
                        
                        // Register again to main window jquery
                        var o = $.extend(true, conf, child_overlaydialog_opts, {
                            'dialogOffset': function() {
                                return iframe.offset();
                            }
                        });
                        $(domEl).overlayDialog(o);
                    });
                    ifwin.jQuery.overlayDialog.els = [];
                    ifwin.jQuery.overlayDialog.jQueryTakeOver = $;
                }
                else {
                    // No dialogs in window
                }
            },
            getOverlay: function() {
                return el.data('overlay').getOverlay();
            },
            getOverlayConf: function() {
                return el.data('overlay').getConf();
            },
            getConf: function() {
                return opts;
            },
            getSubmitedHandlers: function() {
                return submitedHandlers;
            },
            getSuccessHandlers: function() {
                return successHandlers;
            },
            getErrorHandlers: function() {
                return errorHandlers;
            },
            extendConf: function(options) {
                createCallbacks(options);
                $.extend(true, opts, options);
            },
            resize: function (dimensions) {
                var padding = overlayEl.outerHeight(true) - overlayContent.outerHeight(true);
                if (dimensions.width) {
                    overlayEl.width(dimensions.width);
                }
                if (dimensions.height) {
                    overlayEl.height(dimensions.height + padding);
                }
            },
            close: function() {
                el.data('overlay').close();
            },
            animOffset: function() {
                var p;
                if (opts.offset) {
                    p = opts.offset();
                }
                else {
                    p = el.offset();
                    // Center
                    p.left += el.width() / 2;
                    p.top += el.height() / 2;
                }
                if (opts.dialogOffset) {
                    var extraOffset = opts.dialogOffset();
                    p.top += extraOffset.top;
                    p.left += extraOffset.left;
                }
                
                return p;
            },
            initIFrame: function(overlayEl, overlayContent) {
                var iframeUrl = opts.url, 
                    newWidth = opts.width, 
                    newHeight = opts.height, 
                    newBottom = opts.bottom, 
                    newTop = opts.top;
                
                overlayEl.width('').height(''); // Reset the width/height
                overlayContent.empty();
                
                if (newWidth) {
                    overlayEl.width(newWidth);
                }
                if (newHeight) {
                    overlayEl.height(newHeight);
                }
                if (newBottom) {
                    overlayEl.css("bottom", newBottom);
                }
                if (newTop) {
                    overlayEl.css("top", newTop);
                }
                
                // We cannot reuse the name on new iframe, firefox fails if doing so
                var name = overlayEl.attr('id').substring(1) + $.overlayDialog.framecount++;
                var iframe = $("<iframe width='100%' height='100%' frameborder='0' />").css({
                    border: '0px white solid'
                }).attr('src', iframeUrl).attr('name', name).load(function() {
                    var iframe = $(this);
                    var ifwin = frames[name];
                    
                    onIframeLoad(iframe, ifwin);
                    fire.trigger('onIframeLoad', [iframe, ifwin]);
                    
                    self.submitedHandler(iframe, ifwin);
                    
                    onIframeAfterLoad(iframe, ifwin);
                    fire.trigger('onIframeAfterLoad', [iframe, ifwin]);
                }).appendTo(overlayContent);
                
                onIframeBeforeLoad(iframe, frames[name]);
                fire.trigger('onIframeBeforeLoad', [iframe, frames[name]]);
                
                return iframe;
            }
        });
    }
    
    $.fn.overlayDialog = function(options) {
        return this.each(function(i) {
            var el = $(this);
            $.overlayDialog.els.push(el);
            var api = el.data("overlayDialog");
            if (!api) {
                var opts = $.extend(true, {}, $.overlayDialog.defaults, options);
                api = el.data("overlayDialog", new OverlayDialog(el, opts));
            }
            else {
                if (options) {
                    // Extend current conf
                    api.extendConf(options);
                }
                return; // Already created, do nothing.
            }
        });
    };
})();

