﻿(function($) {

    var hoverPreview_open = 'hoverPreview_open',
	hoverPreview_closed = 'hoverPreview_closed';

    $.fn.hoverPreview = function(settings) {
        //
        // BEGIN CONFIG
        //
        var config = {
            cursorOffsetX: 15,
            cursorOffsetY: 15,
            mouseMoveEnabled: true,
            maxSize: 0,
            previewId: "screenshot",
            hoverClass: "screenshotLinkHover",
            delay: 5,
            fadeSpeed: "fast",
            paddingX: 10,
            paddingY: 20
        };
        if (settings) $.extend(config, settings);
        //
        // END CONFIG
        //

        var jQueryMatchedObj = this;

        _getViewportScrollX = function() {
            var scrollX = 0;
            if (document.documentElement && document.documentElement.scrollLeft) {
                scrollX = document.documentElement.scrollLeft;
            }
            else if (document.body && document.body.scrollLeft) {
                scrollX = document.body.scrollLeft;
            }
            else if (window.pageXOffset) {
                scrollX = window.pageXOffset;
            }
            else if (window.scrollX) {
                scrollX = window.scrollX;
            }
            return scrollX;
        };
        _getViewportScrollY = function() {
            var scrollY = 0;
            if (document.documentElement && document.documentElement.scrollTop) {
                scrollY = document.documentElement.scrollTop;
            }
            else if (document.body && document.body.scrollTop) {
                scrollY = document.body.scrollTop;
            }
            else if (window.pageYOffset) {
                scrollY = window.pageYOffset;
            }
            else if (window.scrollY) {
                scrollY = window.scrollY;
            }
            return scrollY;
        };
        _getViewportWidth = function() {
            var width = 0;
            if (document.documentElement && document.documentElement.clientWidth) {
                width = document.documentElement.clientWidth;
            }
            else if (document.body && document.body.clientWidth) {
                width = document.body.clientWidth;
            }
            else if (window.innerWidth) {
                width = window.innerWidth - 18;
            }
            return width;
        };
        _getViewportHeight = function() {
            var height = 0;
            if (document.documentElement && document.documentElement.clientHeight) {
                height = document.documentElement.clientHeight;
            }
            else if (document.body && document.body.clientHeight) {
                height = document.body.clientHeight;
            }
            else if (window.innerHeight) {
                height = window.innerHeight - 18;
            }
            return height;
        };
        _urlencode = function(str) {
            return str.replace(/!/g, '%21')
                .replace(/'/g, '%27')
                .replace(/\(/g, '%28')
                .replace(/\)/g, '%29')
                .replace(/\*/g, '%2A')
            //.replace(/%20/g, '+')
                .replace(/#/g, '%23');
        };
        _scaleDimensions = function(w, h, maxSz) {
            maxSz = maxSz || config.maxSize;
            if (w > maxSz || h > maxSz) {
                // find which dimension is scaled the most
                var scaleW = (1.0 * maxSz) / w;
                var scaleH = (1.0 * maxSz) / h;

                // scale the image
                if (scaleH < scaleW) {
                    h = maxSz;
                    w = parseInt(Math.round(w * scaleH));
                } else {
                    w = maxSz;
                    h = parseInt(Math.round(h * scaleW));
                }
            }
            return { w: w, h: h };
        };
        _calculatePosition = function(pageX, pageY, w, h) {
            // set position of div to the Right and bottom of cursor position
            var setX = pageX + config.cursorOffsetX; // + config.paddingX;
            var setY = pageY + config.cursorOffsetY; // + config.paddingY + config.titleHeight;
            var viewportWidth = _getViewportWidth();
            var viewportHeight = _getViewportHeight();
            var viewportScrollY = _getViewportScrollY();

            // overflowing right of page
            if ((setX + w + config.paddingX) > viewportWidth) {
                // try setting it left of cursor
                setX = pageX - w - config.paddingX - config.cursorOffsetX;
            }
            // overflowing bottom of page
            if ((setY + h + config.paddingY - viewportScrollY) > viewportHeight) {
                // try setting above cursor
                setY = pageY - h - config.paddingY - config.cursorOffsetY;
            }
            // If setX or setY have become smaller than 0, make them 0.
            if (setX < 0) setX = 0;
            if (setY < 0) setY = 0;

            return { x: setX, y: setY };
        };

        // define the mouse over/out hover functions
        _onMouseOver = function(e) {
            if (this.isHovering && true == this.isHovering)
                return;

            this.isHovering = true;
            this._previewed = false;
            this._pageX = e.pageX;
            this._pageY = e.pageY;
            var thisObj = this;
            setTimeout(function() {
                // check if the user moved off the element
                if (false == thisObj.isHovering)
                    return;

                // e.preventDefault();
                // thisObj.t = "";
                var t = "";
                var c = "";
                if (typeof (thisObj.getAttribute("title")) !== 'undefined') {
                    t = thisObj.getAttribute("title");
                    if (t != null && t != "" && t.length > 0)
                        c = "<br/><b>" + t + "</b>";
                }
                else if (typeof (thisObj.getAttribute("alt")) !== 'undefined') {
                    t = thisObj.getAttribute("alt");
                    if (t != null && t != "" && t.length > 0)
                        c = "<br /><b>" + t + "</b>";
                }
                // thisObj.title = "";
                var url = thisObj.getAttribute("rel");
                if (url != null && url.length > 0 && typeof (thisObj.getAttribute("relType")) !== 'undefined') {
                    var urlType = thisObj.getAttribute("relType");
                    if (urlType != null && urlType.length > 0 && urlType == "function") {
                        //alert("before url eval:\r\n\r\n" + url);
                        url = eval(url);
                        //alert("after url eval:\r\n\r\n" + url);                        
                    }
                }
                if (!url)
                    url = thisObj.getAttribute("href");
                if (!url)
                    url = thisObj.getAttribute("src");

                thisObj.tempImg = new Image();
                thisObj.tempImg.onload = function() {
                    if (thisObj.tempImg == null)
                        return;
                    thisObj.tempImg.onload = null;

                    var size = {
                        w: parseInt(thisObj.tempImg.width),
                        h: parseInt(thisObj.tempImg.height)
                    };
                    if (config.maxSize > 0 && (size.w > config.maxSize || size.h > config.maxSize)) {
                        size = _scaleDimensions(size.w, size.h, config.maxSize);
                    }

                    $("body").append("<center><p id='" + config.previewId + "'>"
                                    + "<img id='img_" + config.previewId + "' src='" + _urlencode(url)
                                    + "' width='" + size.w + "' height='" + size.h + "'"
                                    + " alt='Image Preview' />" + c + "</p></center>");

                    // 1. calculate position of image (try and put it to the lower right of the mouse)
                    var position = _calculatePosition(thisObj._pageX, thisObj._pageY, size.w, size.h);
                    // 2. check for really large images (possible scale down super large images)
                    var hoverPreviewIsOverMouse = position.x < thisObj._pageX
                                                    && position.y < thisObj._pageY
                                                    && (position.x + size.w) > thisObj._pageX
                                                    && (position.y + size.h) > thisObj._pageY;
                    if (hoverPreviewIsOverMouse) {
                        size = _scaleDimensions(size.w, size.h, parseInt(0.75 * config.maxSize));
                        $("#img_" + config.previewId).attr("width", size.w).attr("height", size.h);
                        position = _calculatePosition(thisObj._pageX, thisObj._pageY, size.w, size.h);
                    }
                    // 3. check if the label is larger than the image's width (if so, increase width and re-position)
                    var widthWithLabel = c.length == 0 ? 0 : $("#" + config.previewId).width();
                    if (widthWithLabel > size.w) {
                        size.w = widthWithLabel;
                        position = _calculatePosition(thisObj._pageX, thisObj._pageY, size.w, size.h);
                    }
                    thisObj._x = position.x;
                    thisObj._y = position.y;
                    thisObj._w = size.w;
                    thisObj._h = size.h;

                    $.event.trigger(hoverPreview_open);
                    
                    $("#" + config.previewId)
                        .css("top", position.y + "px")
                        .css("left", position.x + "px")
                        .fadeIn(config.fadeSpeed, // normal or fast 
                            function() { thisObj._previewed = true; });
                }
                thisObj.tempImg.src = url;
                $(thisObj).addClass(config.hoverClass);

            }, config.delay);
        };

        _onMouseOut = function(e) {
            // this.title = this.t;
            this.tempImg = null;
            this.isHovering = false;
            this._previewed = false;
            if (config.hoverClass != "")
                $(this).removeClass(config.hoverClass);

            $("#" + config.previewId).remove();
            $.event.trigger(hoverPreview_closed);
        };

        _onMouseMove = function(e) {
            if (e.pageX == this._pageX && e.pageY == this._pageY)
                return;

            this._pageX = e.pageX;
            this._pageY = e.pageY;
            if (true == this.isHovering
                && true == this._previewed
                && true == config.mouseMoveEnabled) {

                var position = _calculatePosition(this._pageX, this._pageY, this._w, this._h);
                this._x = position.x;
                this._y = position.y;

                $("#" + config.previewId)
                    .css("top", position.y + "px")
                    .css("left", position.x + "px");
            }
        };

        // element-specific code here that enables the hover preview functionality
        this.each(function(i) {
            this.isHovering = false;
            this._previewed = false;
            this._pageX = -1;
            this._pageY = -1;
            $(this).hover(_onMouseOver, _onMouseOut);
            $(this).mousemove(_onMouseMove);
        });

        return jQueryMatchedObj;
    };

})(jQuery);