;(function($){
    $.easyBox = function( options, from ) {
        options = $.extend({}, $.easyBox.defaults, options, ($.metaData ? $( from ).metaData() : {}) );

        options.target = options.target || from;

        easyBox.open( options );
        
        return easyBox;
    }

    $.fn.easyBox = function( options ) {
        return this.each(function(i) {
            if ( !$( this ).data('hasEasyBox') ) {
        		$( this )
                    .data('hasEasyBox', true)
                    .bind('click.easyBoxOpen', function( e ) {
            			e.preventDefault();

            			$.easyBox( options, this );
            		});
        	}
        });
    };

    $.easyBox.defaults = {
        className   : 'easybox-window',
        opacity     : 0.5,
        width       : 'auto',
        height      : 'auto',
        modal       : false
    };

    // easyBox singleton
    var easyBox = (function(){
        // containers
        var $overlay = null;
        var $wrapper = null;
        var $winData = null;

        var $boxMorph = null;
        var $iconLoad = null;

        var config  = {};
        var $origElm = null;
        var origData= null;
        var xhr     = null;
        
        var data = {
                header  : null,
                content : null,
                footer  : null
            };

        var quirksFix = { x:0, y:0 };
        var outerDiff = { x:0, y:0 };

        function _initBox( opts ) {
            $overlay = $('<div />')
                .css({
                        display     : 'none',
                        position    : 'fixed',
                        height      : '100%',
                        width       : '100%',
                        left        : 0,
                        top         : 0,
                        opacity     : 0,
                        background  : '#000',
                        zIndex      : 5000
                    })
                .prependTo( document.body );

            $wrapper = $('<div />')
                .css({
                        display     : 'none',
                        position    : 'fixed',
                        height      : '100%',
                        width       : '100%',
                        top         : 0,
                        left        : 0,
                        cursor      : 'default',
                        zIndex      : 5100
                    })
                .insertAfter( $overlay );

            $winData = $('<div />')
                .addClass( opts.className )
                .css({
                        display     : 'none',
                        position    : 'absolute',
                        top         : '50%',
                        left        : '50%',
                        margin      : 0,
                        padding     : 0,
                        cursor      : 'default'
            	   })
                .html(  '<div style="overflow:hidden;height:auto;" class="easybox-header"></div>' +
                        '<div style="overflow:auto;position:relative;" class="easybox-content"></div>' +
                        '<div style="overflow:hidden;height:auto;" class="easybox-footer"></div>' )
                .appendTo( $wrapper );
                
            $boxMorph = $winData.clone()
                .empty()
                .insertAfter( $winData );
                
            $iconLoad = $('<div>')
                .css({
                        display     : 'none',
                        position    : 'absolute',
                        zIndex      : 110000,
                        width       : '100%',
                        height      : '100%',
                        top         : 0,
                        left        : 0,
                        padding     : 0,
                        margin      : 0
                    })
                .insertAfter( $boxMorph );
                
            outerDiff.x = $boxMorph.outerWidth() - $boxMorph.width();
            outerDiff.y = $boxMorph.outerHeight() - $boxMorph.height();

            if ( !$.boxModel ) {
                quirksFix.x = outerDiff.x;
                quirksFix.y = outerDiff.y;
            }

            // IE position fix
           if ( $.browser.msie && ( $.browser.version < 7 || !$.boxModel ) ) {
                $wrapper.css({ overflow:'hidden' });

                $.each( [ _get('header'), _get('footer') ], function() {
                    $( this ).css({ height:'', overflow:'', zoom:1 });
                });

                $.each( [ $overlay, $wrapper ], function() {
                    $( this ).css({ position:'absolute' });

                    this[0].style.setExpression('width',   'jQuery.boxModel && document.documentElement.clientWidth || document.body.clientWidth + "px"');
                    this[0].style.setExpression('height',  'jQuery.boxModel && document.documentElement.clientHeight || document.body.clientHeight + "px"');
                    this[0].style.setExpression('left',    'jQuery.boxModel && document.documentElement.scrollLeft || document.body.scrollLeft + "px"');
                    this[0].style.setExpression('top',     'jQuery.boxModel && document.documentElement.scrollTop || document.body.scrollTop + "px"');
                });
                
                if ( $.browser.version < 7 ) {
                    $('<iframe frameborder="0" scrolling="no" src="javascript:\'\'">')
                        .css({  position: 'absolute',
                                width   : '100%',
                                height  : '100%',
                                opacity : 0
                            })
                        .appendTo( $overlay );
                }
            }
        };

        function _resetBox( opts ) {
            if ( $.isFunction( config.onHide ) ) {
                config.onHide.call( easyBox, _get('content') );
                config.onHide = null;
            }

            if ( $origElm != null ) {
                origData && $origElm.html( origData );
                $origElm = origData = null;
            }

            $.each( data, function(k,v){
                    data[ k ] = null;
                });

            _unbindEvents();

            if ( xhr ) {
                xhr.abort();
                xhr = null;
            }
            
            $winData
                .removeClass()
                .addClass( opts && opts.className );
        }

        function _updateConfig( opts ) {
            config = {
                opacity     : parseFloat( opts.opacity ) || 0.5,
                width       : opts.width || 'auto',
                height      : opts.height || 'auto',
                modal       : opts.modal || false,
                speed       : (opts.speed === null || opts.speed) ? opts.speed : 400,
                loader      : opts.loader || '/com/img/easybox_loader.gif',
                title       : opts.title || '',
                type        : opts.type ? opts.type.toLowerCase() : null,
                target      : opts.target || null,
                url         : opts.url || $origElm.attr('href') || '',
                onOpen      : opts.onOpen || null,
                onShow      : opts.onShow || null,
                onClose     : opts.onClose || null,
                onHide      : opts.onHide || null
            };

            // just preload image
            (new Image()).src = config.loader;

            if ( config.opacity > 1 ) {
                config.opacity = 0.8;
            }
            else if ( config.opacity < 0 ) {
                config.opacity = 0;
            }

            var url = config.url;

            if ( url.indexOf('?') !== -1 ) {
                url = url.substr( 0, url.indexOf('?') );
            }

            var local = new RegExp( '^'+ document.location.protocol +'\/\/'+ document.location.hostname );

            var urlTestCases = {
                'inline'    : ( config.type == 'inline' ),
                'ajax'      : ( url && ( !/^https?:/.test( url ) || local.test( url.toLowerCase() ) || config.type == 'ajax' ) ),
                'iframe'    : ( url && ( /^https?:/.test( url ) || config.type == 'iframe' ) )
            };

            for ( var detectedType in urlTestCases ) {
                if ( urlTestCases[ detectedType ] ) {
                    config.type = detectedType;
                    break;
                }
            }

            config.type = ( config.type in urlTestCases ) ? config.type : 'inline';

            if ( config.type == 'iframe' ) {
                config.modal = false;
            }
        };

        function _offsetParse( size, setHeight ) {
            return {
                    width       : ( size.width + quirksFix.x ) +'px',
                    height      : setHeight ? ( size.height + quirksFix.y ) +'px' : '',
                    marginLeft  : ( 0 - Math.floor( ( size.width + outerDiff.x ) / 2 ) ) +'px',
                    marginTop   : ( 0 - Math.floor( ( size.height + outerDiff.y ) / 2 ) ) +'px'
                };
        };

        function _morphOpen( skipLoadIcon ) {
            var size = { width:100, height:100 };

            if ( $winData.data('isOpened') ) {
                size.width   = $winData.width();
                size.height  = $winData.height();
            }

            $boxMorph
                .css( _offsetParse( size, true ) )
                .css({ visibility:'', zIndex:100000, fontSize:0 })
                .show();
                
            if ( !skipLoadIcon ) {
                $iconLoad.css({
                        background : 'url('+ config.loader +') center center no-repeat'
                    }).show();
            }
        };

        function _morphClose() {
            $boxMorph.hide();
            $iconLoad.hide();
        };

        function _boxRender( customWidth, customHeight ) {
            var size = {
                    width   : customWidth || config.width,
                    height  : customHeight || config.height
                };

            var page = {
                    width   : document.documentElement.clientWidth || document.body.clientWidth,
                    height  : document.documentElement.clientHeight || document.body.clientHeight,
                    offset  : 100
                };

            var doShrink = { x:false, y:false };

            $boxMorph || _morphOpen();
            
            $winData.css({ visibility:'hidden' }).show();

            var $newContent = $('<div>')
                .html( data.content )
                .css({ position:'absolute', overflow:'auto', visibility:'hidden' })
                .insertAfter( $winData );

            data.content = null;
            
            // calculate AUTO width
            if ( size.width == 'auto' ) {
                if ( config.type == 'iframe' ) {
                    size.width = page.width;
                }
                else {
                    size.width = $.boxModel ? $newContent.width() : $newContent.outerWidth();
                }

                doShrink.x = true;
            }

            if ( doShrink.x && ( size.width + page.offset >= page.width ) ) {
                size.width = page.width - page.offset;
            }

            // calculate AUTO height
            if ( size.height == 'auto' ) {
                if ( config.type == 'iframe' ) {
                    size.height = page.height;
                }
                else {
                    $newContent.width( size.width );

                    size.height = $.boxModel ? $newContent.height() : $newContent.outerHeight();
                }

                doShrink.y = true;
            }

            if ( doShrink.y && ( size.height + page.offset >= page.height ) ) {
                size.height = page.height - page.offset;
            }
            
            size.width  = ( size.width < 20 ) ? 20 : size.width;
            size.height = ( size.height < 20 ) ? 20 : size.height;

            // fix scroll offset
            $newContent.width( size.width ).height( size.height );

            if ( $newContent[0].scrollHeight > $newContent[0].clientHeight ) {
                size.width += 17;
                $newContent.width( size.width );
            }

            if ( $newContent[0].scrollWidth > $newContent[0].clientWidth ) {
                size.height += 17;
                $newContent.height( size.height );
            }

            _get('content').replaceWith( $newContent.addClass('easybox-content') );

            $newContent = null;

            var box = {
                    width   : _get('content').outerWidth( true ),
                    height  : size.height
                };
                
            _get('content').css({ visibility:'', position:'relative', overflow:'hidden' });
            _get('header').css({ display:'none' }).empty();
            _get('footer').css({ display:'none' }).empty();
            
            if ( data.header ) {
                _get('header').html( data.header ).show();
                data.header = null;
            }
            
            if ( data.footer ) {
                _get('footer').html( data.footer ).show();
                data.footer = null;
            }

            // set width to outer
            $winData.width( box.width + quirksFix.x ).height('');

            // get height difference
            box.height += $winData.height() - size.height;

            $winData.css( _offsetParse( box ) ).css({ visibility:'' });

            _get('content').css({ overflow:'auto' });

            _morphClose();
            _bindEvents();
        };

        function _bindEvents( loadingMode ) {
            _unbindEvents();

            if ( loadingMode || !config.modal ) {
                $( document )
                    .bind('keydown.easyBoxClose', function(e) {
                            if ( e.keyCode == 27 ) {
                                easyBox.close();
                            }
                        });
            }

            $('[rel="easyBoxClose"]')
                .bind('click.easyBoxClose', function(e){
                        e.preventDefault();

                        easyBox.close();
                    });

            if ( !loadingMode && config.type == 'ajax' ) {
                $( '.easybox', _get('content') ).easyBox();
            }
        }

        function _unbindEvents() {
            $(document).unbind('keydown.easyBoxClose');

            $('[rel="easyBoxClose"]').unbind('click.easyBoxClose');
        }

        function _get( name ) {
            switch ( name.toLowerCase() ) {
                case 'header':      return $winData.find('>div:eq(0)');
                case 'content':     return $winData.find('>div:eq(1)');
                case 'footer':      return $winData.find('>div:eq(2)');
            }
        };

        // do the magic
        function _contentSet( html ) {
            data.content = $( html ).css({ position:'' });
        };
        
        function _headerSet( html ) {
            data.header = $( '<a href="#" rel="easyBoxClose" title="Close">close X</a><strong>&nbsp;</strong>');
            data.header.filter('strong').html( html );
        };
        
        function _footerSet( html ) {
            data.footer = $( html );
        };

        function _contentShow( customWidth, customHeight ) {
            if ( !config.modal ) {
                _headerSet( config.title );
            }

            _boxRender( customWidth, customHeight );
            
            if ( $.isFunction( config.onShow ) ) {
                config.onShow.call( easyBox, _get('content') );
            }
        };

        function _showContentInline() {
            // save original content
            origData = $origElm.contents();

            _contentSet( origData );
            _contentShow();
        };

        function _showContentIframe() {
            _contentSet( '<div style="width:100%;height:100%;overflow:hidden;">'
                    + '<iframe frameborder="0" marginwidth="0" marginheight="0"'
                    + ' src="'+ config.url +'" allowtransparency="true"'
                    + ' style="width:100%;height:100%;"></iframe></div>' );
            _contentShow();
        };

        function _showContentAjax() {
            xhr = $.ajax({
                url     : config.url,
                cache   : false,
                beforeSend : function( xhr ) {
                    _morphOpen();
                    _bindEvents( true );
                },
                success : function( data ) {
                    _contentSet( data );
                    _contentShow();
                },
                error   : function( xhr, msg ) {
                    config.title = 'Error Occurs';

                    config.onOpen = null;
                    config.onShow = null;
                    config.onClose = null;
                    config.onHide = null;

                    _contentSet( '<div style="color:red;padding:10px 0;text-align:center;">'
                                + 'Sorry, requested content could not be loaded<br /><br />Please try later'
                                + '</div>' );

                    _contentShow( 300, 'auto' );
                },
                complete : function() {
                    xhr = null;
                }
            });
        };

        // API
        return {
            open : function( options ) {
                ( $overlay === null ) ? _initBox( options ) : _resetBox( options );

                if ( !options.target ) {
                    return;
                }

                // set checkpoint to restore inline content
                $origElm = $( options.target ).eq(0);
                
                _updateConfig( options );

                $overlay.show().css({ opacity:config.opacity });
                $wrapper.show();
                
                if ( $.isFunction( config.onOpen ) ) {
                    config.onOpen.call( easyBox, _get('content') );
                }

                ( config.type == 'ajax' )   && _showContentAjax();
                ( config.type == 'iframe' ) && _showContentIframe();
                ( config.type == 'inline' ) && _showContentInline();

                $winData.data('isOpened', true);
            },
            close : function() {
                _resetBox();
                
                $wrapper.hide();
                $overlay.hide();
                
                _get('content').empty();
                _get('header').empty();
                _get('footer').empty();
                
                if ( $.isFunction( config.onClose ) ) {
                    config.onClose.call( easyBox );
                }
            
                $winData.hide().data('isOpened', false);
            },
            setHeader : _headerSet,
            setFooter : _footerSet
        };
    })();
})( jQuery );
