﻿/**
* LavaLamp - A menu plugin for jQuery with cool hover effects.
* @requires jQuery v1.1.3.1 or above
*
* http://gmarwaha.com/blog/?p=7
*
* Copyright (c) 2007 Ganeshji Marwaha (gmarwaha.com)
* Dual licensed under the MIT and GPL licenses:
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl.html
*
* Version: 0.2.0
* Requires Jquery 1.2.1 from version 0.2.0 onwards. 
* For jquery 1.1.x, use version 0.1.0 of lavalamp
*/

/**
* Creates a menu with an unordered list of menu-items. You can either use the CSS that comes with the plugin, or write your own styles 
* to create a personalized effect
*
* The HTML markup used to build the menu can be as simple as...
*
*       <ul class="lavaLamp">
*           <li><a href="#">Home</a></li>
*           <li><a href="#">Plant a tree</a></li>
*           <li><a href="#">Travel</a></li>
*           <li><a href="#">Ride an elephant</a></li>
*       </ul>
*
* Once you have included the style sheet that comes with the plugin, you will have to include 
* a reference to jquery library, easing plugin(optional) and the LavaLamp(this) plugin.
*
* Use the following snippet to initialize the menu.
*   $(function() { $(".lavaLamp").lavaLamp({ fx: "backout", speed: 700}) });
*
* Thats it. Now you should have a working lavalamp menu. 
*
* @param an options object - You can specify all the options shown below as an options object param.
*
* @option fx - default is "linear"
* @example
* $(".lavaLamp").lavaLamp({ fx: "backout" });
* @desc Creates a menu with "backout" easing effect. You need to include the easing plugin for this to work.
*
* @option speed - default is 500 ms
* @example
* $(".lavaLamp").lavaLamp({ speed: 500 });
* @desc Creates a menu with an animation speed of 500 ms.
*
* @option click - no defaults
* @example
* $(".lavaLamp").lavaLamp({ click: function(event, menuItem) { return false; } });
* @desc You can supply a callback to be executed when the menu item is clicked. 
* The event object and the menu-item that was clicked will be passed in as arguments.
*/
(function($) {
    $.fn.lavaLamp = function(o) {
    o = $.extend({ fx: "easeOutExpo", speed: 800, click: function() { } }, o || {});

        return this.each(function() {
            var me = $(this), noop = function() { },
            $back = $('<li class="back"><div class="left"></div></li>').appendTo(me),
            $li = $("li", this), curr = $("li.current", this)[0] || $($li[0]).addClass("current")[0];

            $li.not(".back").hover(function() {
                move(this);
            }, noop);

            $(this).hover(noop, function() {
                move(curr);
            });

            $li.click(function(e) {
                setCurr(this);
                return o.click.apply(this, [e, this]);
            });

            setCurr(curr);

            function setCurr(el) {
                $back.css({ "left": el.offsetLeft + "px", "width": el.offsetWidth + "px" });
                curr = el;
            };

            function move(el) {
                $back.each(function() {
                    $(this).dequeue();
                }).animate({
                    width: el.offsetWidth,
                    left: el.offsetLeft
                }, o.speed, o.fx);
            };

        });
    };
})(jQuery);


/******
* Tipped: A tooltip plugin for jQuery
* http://code.google.com/p/tipped/
*
* Copyright 2010, University of Alberta
*
* Tipped is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* 
* Tipped is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
* 
* You should have received a copy of the GNU General Public License v2
* along with Tipped.  If not, see <http://www.gnu.org/licenses/gpl-2.0.html>
*
*
* Compatibility: Tested with jQuery 1.4.2, though it should work with 1.3.x
*
* v1.5		- Fixed bug with 'title' attribute being added back after tip goes away
*			- Added 'position' and 'posX', 'posY' options
*			- Added 'delay' option
* v1.4		- Added logic that resizes tips that are larger than the viewport, to fit inside the viewport
*			- Added oversizeStick option
* v1.3.4:  - Made themeroller compatible
*			- Used removeAttr() to remove title attribute, rather than setting the attribute to blank
*			- Thanks to Durval Agnelo for the advice/contribution
* v1.3.3:	- Became a good jQuery citizen and return the jQuery object from tipped() so it supports chaining
*			- Fixed a bug that emptied out the title stored in data(), if tipped() is called
*			  on an element twice
* v1.3.2:	Fixed 'title' based tips that were trying to show the title from the attribute after it was emptied out
* v1.3.1:	Did some stuff
* v1.3:	Reposition tooltip at top left before width calculation for repositioning done.  This
*			prevents inline elements from being squished.
* v1.2:	Fixed showing/hiding of the "Close" button if there are tips with both "hover" and "click" mode
* v1.1: 	- Added turning off of default tooltip that appears when an elelment has a title
*			- #tipped element is now created explicitely as an window variable - fixes a problem with Safari
* v1.0: 	Initial release
*/
(function($) {
    /******
    /*	Options
		
			ajaxType:	The type of HTTP request to make.
    Possible values: Anything $.ajax accepts (usually 'GET' or 'POST')
    Default: 'POST'
			
			cache:		Whether or not to cache AJAX requests.  Cache is based on URL, not URL + data, so if 
    you are making multiple requests to the same URL with different data, turn off cache
    Default: false
			
			closer:		The HTML to display when a tip is to be manually closed (ie: when triggered by a click).  
    All text in 'closer' will be injected inside another element that has the close listener
    Default: 'Close'
			
			delay:		The milliseconds to wait between when the trigger is hovered over, and the tip appears.
    Ignored if "mode" is "click".
    Default: 0
    marginX:	The pixels to the right of the element that the tip should appear.  This amount will be
    overridden if necessary to ensure the entire tip shows on the screen.
    Possible values: Any integer.  Negative numbers will position the tip to the left of the right
    edge of the triggering element
    Default: '10'
				
    marginY:	The pixels to the bottom of the element that the tip should appear.  This amount will be
    overridden if necessary to ensure the entire tip shows on the screen.
    Possible values: Any integer.  Negative numbers will position the tip above of the bottom
    edge of the triggering element
    Default: '10'
				
    mode:		The type of tip to make.  'Hover' shows and hides on hover, 'Click' is triggered with a
    click and requires clicking of the closer to go away
    Possible values: 'hover', 'click'
    Default: 'hover'
			
			oversizeStick:	Whether to revert to "click" mode if the content is too large for the screen.  If the
    content is too large, scrollbars appear.  Users can't use those scrollbars though, if
    the tooltip disappears when they hover off the target.  This will remedy that problem.
							
    The 'closer' option will be used when necessary.
    Possible values: true,false
    Default: true
				
    params:		An object representing the parameters to send along with an AJAX request as 'data'
    Possible values:
    A callback: Data passed will be the object returned from this function.  Function will be passed
    a jQuery object representing the triggering element
    An object: Will be used as the data
    Default: {}
			
			position:	The method Tipped will use to determine position.
    Possible values:
    'absolute': The position of the tip will be determined by the posX and posY parameters, 
    with no application of the margins and no consideration for where the triggering 
    element is
    'mouse':	The position of the tip will be determined by the location of the mouse when the 
    tip is triggered. Margins will be applied.
    'element':	The position of the tip will be determined by the bottom right corner of the 
    triggering element. Margins will be applied.
    Default: 'element'
			
			posX:	The absolute position on the x-axis the tooltip will have when displayed.  Only used if the 
    'position' option is "absolute"
    Possible values:
    A callback:  Function must return an integer.  Function will be passed a jQuery object 
    representing the triggering element
    An integer
    Default value: 0
			
			posY:	The absolute position on the y-axis the tooltip will have when displayed.  Only used if the 
    'position' option is "absolute"
    Possible values:
    A callback:  Function must return an integer.  Function will be passed a jQuery object 
    representing the triggering element
    An integer
    Default value: 0
			
			source:		The source of the value to display.
    Possible values: 
    'title':	Value to display will be pulled from the 'title' attribute of the triggering element
    A callback: Value to display will be returned from the callback function.  Function will be passed
    a jQuery object representing the triggering element
    'url':		An AJAX request will be made to the address specified by the 'url' option
    Any other string:	Will be displayed
    Default: 'title'
				
    themeroller:	Whether or not to make Themeroller compatible
    Possible values: true, false
    Default: false
				
    throbber:	The URL to the image to display while the AJAX request is being sent.  If blank, no throbber
    will be shown.
    Default: ''
				
    url:		The web address to make the AJAX request to.  Unused if 'source' is not 'url'
    */

    var defaults = {
        ajaxType: 'POST',
        cache: false,
        cached: {},
        closer: 'Close',
        delay: 0,
        marginX: 10,
        marginY: 10,
        mode: 'hover',
        oversizeStick: true,
        params: {},
        position: 'element',
        posX: 0,
        posY: 0,
        source: 'title',
        themeroller: false,
        throbber: '',
        url: ''
    };

    //create single tooltip
    window.$tip = {};
    window.$tip_content = {};

    $(document).ready(function() {
        $tip = $("#tipped").length ? $("#tipped") : $('<div id = "tipped"><div id = "tipped_content"></div></div>').appendTo(document.body).data('showing', false);
        $tip_content = $("#tipped_content");
    });

    $.fn.tipped = function(settings) {
        this.each(function(i) {

            $target = $(this); //shortcut

            //store settings
            settings = $.extend({}, defaults, settings);
            $target.data('tipped', { settings: settings });

            //make compatible with themeroller
            if (settings.themeroller)
                $tip.addClass('ui-helper-hidden ui-widget ui-dialog ui-corner-all');
            else
                $tip.removeClass('ui-helper-hidden ui-widget ui-dialog ui-corner-all');


            //2 modes act differently
            if (settings.mode == 'hover') {
                var t = undefined;
                $target
					.mouseover(function(e) {
					    var $thisTarget = $(this);
					    $.fn.tipped.removeTitle($thisTarget);
					    t = setTimeout(function() { $.fn.tipped.initiateShow($thisTarget, e); }, settings.delay);
					})
					.mouseout(function() {
					    $.fn.tipped.hideTip($(this));
					    clearTimeout(t);
					});
            }
            else if (settings.mode == 'click') {
                $.fn.tipped.addCloser($target);

                if (settings.themeroller) {
                    $("#tipped-closer")
						.addClass('ui-button ui-state-hover ui-state-default')
						.hover(function() { $(this).addClass('ui-state-hover'); }, function() { $(this).removeClass('ui-state-hover'); })
						.mousedown(function() { $(this).addClass('ui-state-active'); })
						.mouseup(function() { $(this).removeClass('ui-state-active'); })
                }
                else
                    $("#tipped-closer").removeClass('ui-button ui-state-hover ui-state-default');

                $target.click(function() { $.fn.tipped.initiateShow($(this)); });
            }
        });

        return this;
    };

    /**
    * Function: addCloser()
    * Purpose: To add a "close" element to the tooltip
    * Parameters: $target: a jQuery object that has had a tip bound to it.
    */
    $.fn.tipped.addCloser = function($target) {
        //don't add if it's already there
        if ($("#tipped-closer").length == 0) {
            $tip.append('<div id = "tipped-closer-wrapper"><span id = "tipped-closer">' + $target.data('tipped').settings.closer + '</span>');
            $("#tipped-closer").click(function() { $.fn.tipped.hideTip($target); });
        }
        else
            $("#tipped-closer-wrapper").show();
    }

    /**
    * Function: removeTitle()
    * Purpose: To remove the title attribute from the target, and store that value in data
    * Parameters: $target: a jQuery object that has had a tip bound to it.
    */
    $.fn.tipped.removeTitle = function($target) {
        if ($target.data('tipped').title === undefined)
            $target.data('tipped', $.extend($target.data('tipped'), { title: $target.attr('title') }));

        //IE doesn't respect removal of the attribute, so need to set it to blank.
        $target.removeAttr('title').attr('title', '');
    }


    /**
    * Function: initiateShow()
    * Purpose: To initiate the showing of a tip.
    * Parameters: $target: a jQuery object that has had a tip bound to it.  Tipped uses
    *                      the settings associated with the $target to determine what to display
    *			   e: The jQuery event that triggered the hover.  Only provided if "position" is "mouse"
    */
    $.fn.tipped.initiateShow = function($target, e) {
        //shortcuts
        var settings = $target.data('tipped').settings;
        var cached = $tip.data('cached');

        //manage the closer
        if (settings.mode != 'click')
            $("#tipped-closer-wrapper").hide();
        else
            $("#tipped-closer-wrapper").show();

        //AJAX
        if (settings.source === 'url') {
            //if we're not caching, retrieve the value
            if (!settings.cache || cached === undefined || cached[settings.url] === undefined) {
                //set parameters
                var data = {};
                if (typeof settings.params == 'function')
                    data = settings.params($target);
                else if (typeof settings.params == 'object')
                    data = settings.params;

                $.ajax({
                    beforeSend: function() {
                        show($target, '<img src = ' + settings.throbber + ' alt = "Loading..." />', e);
                    },
                    data: data,
                    error: function() {
                        show($target, 'Unable to retrieve contents', e);
                    },
                    success: function(display) {
                        if ($tip.data('showing'))
                            show($target, display, e);

                        //cache results if necessary
                        if (settings.cache) {
                            var newCache = new Object;
                            newCache[settings.url] = display;
                            cached = $.extend(cached, newCache);
                            $tip.data('cached', cached);
                        }
                    },
                    type: settings.ajaxType,
                    url: settings.url
                });
                return;
            }
            //otherwise, show the cached copy
            else {
                show($target, cached[settings.url], e);
                return;
            }
        }


        var value = '';

        //'title' attribute
        if (settings.source === 'title')
            value = $target.data('tipped').title;

        //any other string
        else if (typeof settings.source == 'string')
            value = settings.source;

        //custom function
        else if (typeof settings.source == 'function')
            value = settings.source($target);

        //jQuery object
        else if (typeof settings.source == 'object')
            value = settings.source.html();

        show($target, value, e);
    }

    /*
    * Function: hideTip()
    * Purpose: To hide the tip
    * Parameters: $target:	a jQuery object representing the element that triggered the tip
    */
    $.fn.tipped.hideTip = function($target) {
        //$target.attr('title',$target.data('tipped').title);
        $tip.data('showing', false).data('original', '').hide();
        $tip_content.html('');
    }


    /*
    * Function: getTrigger()
    * Purpose: To provide access to the element that triggered the tip.  Useful for 
    *          clicked tips that need to know who triggered them
    *
    * Access with: $.getTrigger()
    */
    $.extend({
        getTrigger: function() {
            return $tip.data('original');
        }
    });

    /*
    * Function: show()
    * Purpose: To actually show the tip
    * Parameters: $target: The element (wrapped in a jQuery object) that triggered the showing of this tip
    *			   value: The HTML to place into the tip
    *
    * Note: This function is private
    */
    function show($target, value, e) {
        $tip_content.html(value);
        setPosition($target, e)
        $tip.data('showing', true).data('original', $target).show();
    }


    /*
    * Function: setPosition()
    * Purpose: To set the position of the tip.  This function is called after the content of the tip
    *          is set, allowing the function to make a dynamic decision about the position of the tip
    *			
    *			The tip is always displayed fully on the screen & will be moved to ensure that.
    * Parameters: $elem: a jQuery object representing the element relative to which the tip is to be positioned.
    *
    * Note: This function is private
    */
    function setPosition($target) {
        var settings = $target.data('tipped').settings;

        //position tip in the top left corner, so full, proper width gets calculated
        $tip.css({ left: 0, top: 0 });

        resize($target, settings);

        //determine element position on screen
        var posX = 0;
        var posY = 0;

        switch (settings.position) {
            case 'mouse':
                posX = e.pageX + settings.marginX;
                posY = e.pageY + settings.marginY;
                break;
            case 'absolute':
                if (typeof settings.posX == 'function')
                    posX = settings.posX($target);
                else
                    posX = settings.posX;

                if (typeof settings.posY == 'function')
                    posY = settings.posY($target);
                else
                    posY = settings.posY;
                break;
            default:
                var targetPos = $target.offset();
                posX = targetPos.left + $target.outerWidth() + settings.marginX;
                posY = targetPos.top + $target.outerHeight() + settings.marginY;
                break;
        }

        //adjust to ensure tip is inside viewable screen
        var right = posX + $tip.outerWidth();
        var bottom = posY + $tip.outerHeight();

        var windowWidth = $(window).width() + $(window).scrollLeft() - 5;
        var windowHeight = $(window).height() + $(window).scrollTop() - 5;

        posX = (right > windowWidth) ? posX - (right - windowWidth) : posX;
        posY = (bottom > windowHeight) ? posY - (bottom - windowHeight) : posY

        $tip.css({ left: posX, top: posY });
    }

    function resize($target, settings) {
        //reset height & width settings - may not be needed
        $tip_content.css({ height: 'auto', width: 'auto' });
        $tip.css({ height: 'auto', width: 'auto' });

        if ($tip.outerHeight() > $(window).height()) {
            if (settings.oversizeStick) {
                $.fn.tipped.addCloser($elem);
                $elem.unbind('mouseout');
            }

            var innerHeightDifference = $tip.outerHeight() - $tip_content.outerHeight();
            var tipHeightDifference = $tip.outerHeight() - $tip.height();

            //-10 to account for the -5 applied in setPosition
            $tip.css('height', $(window).height() - tipHeightDifference - 10);

            //+20 to account for the scrollbar. Browsers don't account for "auto" placed scrollbars in width calculations
            //so the contents ends up getting squished
            $tip_content.css({ height: $tip.outerHeight() - innerHeightDifference, overflow: "auto", width: $tip.outerWidth() + 20 });
        }
        if ($tip.outerWidth() > $(window).width()) {
            if (settings.oversizeStick) {
                $.fn.tipped.addCloser($elem);
                $target.unbind('mouseout');
            }
            $tip_content.css({ width: $(window).width() - 20, overflow: "auto", height: $tip.outerWidth() + 20 });
        }
    }
})(jQuery);

