/*
* jQuery clueTip plugin
* Version 0.9.9 (03/13/2009)
* @requires jQuery v1.1.4+
* @requires Dimensions plugin IF USED WITH jQuery VERSIONS PRIOR TO 1.2.5)
*
* Dual licensed under the MIT and GPL licenses:
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl.html
*/

/*
*
* Full list of options/settings can be found at the bottom of this file and at http://plugins.learningjquery.com/cluetip/
*
* Examples can be found at http://plugins.learningjquery.com/cluetip/demo/
*
*/

; (function($) {

    var $cluetip, $cluetipInner, $cluetipOuter, $cluetipTitle, $cluetipArrows, $dropShadow, imgCount;
    $.fn.cluetip = function(js, options) {
        if (typeof js == 'object') {
            options = js;
            js = null;
        }
        return this.each(function(index) {
            var $this = $(this);

            // support metadata plugin (v1.0 and 2.0)
            var opts = $.extend(true, {}, $.fn.cluetip.defaults, options || {}, $.metadata ? $this.metadata() : $.meta ? $this.data() : {});

            // start out with no contents (for ajax activation)
            var cluetipContents = false;
            var cluezIndex = parseInt(opts.cluezIndex, 10) - 1;
            var isActive = false, closeOnDelay = 0;

            // create the cluetip divs
            if (!$('#cluetip').length) {
                $cluetipInner = $('<div id="cluetip-inner"></div>');
                $cluetipTitle = $('<h3 id="cluetip-title"></h3>');
                $cluetipOuter = $('<div id="cluetip-outer"></div>').append($cluetipInner).prepend($cluetipTitle);
                $cluetip = $('<div id="cluetip"></div>').css({ zIndex: opts.cluezIndex })
        .append($cluetipOuter).append('<div id="cluetip-extra"></div>')[insertionType](insertionElement).hide();
                $('<div id="cluetip-waitimage"></div>').css({ position: 'absolute', zIndex: cluezIndex - 1 })
        .insertBefore('#cluetip').hide();
                $cluetip.css({ position: 'absolute', zIndex: cluezIndex });
                $cluetipOuter.css({ position: 'relative', zIndex: cluezIndex + 1 });
                $cluetipArrows = $('<div id="cluetip-arrows" class="cluetip-arrows"></div>').css({ zIndex: cluezIndex + 1 }).appendTo('#cluetip');
            }
            var dropShadowSteps = (opts.dropShadow) ? +opts.dropShadowSteps : 0;
            if (!$dropShadow) {
                $dropShadow = $([]);
                for (var i = 0; i < dropShadowSteps; i++) {
                    $dropShadow = $dropShadow.add($('<div></div>').css({ zIndex: cluezIndex - i - 1, opacity: .1, top: 1 + i, left: 1 + i }));
                };
                $dropShadow.css({ position: 'absolute', backgroundColor: '#000' })
        .prependTo($cluetip);
            }
            var tipAttribute = $this.attr(opts.attribute), ctClass = opts.cluetipClass;
            if (!tipAttribute && !opts.splitTitle && !js) return true;
            // if hideLocal is set to true, on DOM ready hide the local content that will be displayed in the clueTip
            if (opts.local && opts.localPrefix) { tipAttribute = opts.localPrefix + tipAttribute; }
            if (opts.local && opts.hideLocal) { $(tipAttribute + ':first').hide(); }
            var tOffset = parseInt(opts.topOffset, 10), lOffset = parseInt(opts.leftOffset, 10);
            // vertical measurement variables
            var tipHeight, wHeight;
            var defHeight = isNaN(parseInt(opts.height, 10)) ? 'auto' : (/\D/g).test(opts.height) ? opts.height : opts.height + 'px';
            var sTop, linkTop, posY, tipY, mouseY, baseline;
            // horizontal measurement variables
            var tipInnerWidth = isNaN(parseInt(opts.width, 10)) ? 275 : parseInt(opts.width, 10);
            var tipWidth = tipInnerWidth + (parseInt($cluetip.css('paddingLeft'), 10) || 0) + (parseInt($cluetip.css('paddingRight'), 10) || 0) + dropShadowSteps;
            var linkWidth = this.offsetWidth;
            var linkLeft, posX, tipX, mouseX, winWidth;

            // parse the title
            var tipParts;
            var tipTitle = (opts.attribute != 'title') ? $this.attr(opts.titleAttribute) : '';
            if (opts.splitTitle) {
                if (tipTitle == undefined) { tipTitle = ''; }
                tipParts = tipTitle.split(opts.splitTitle);
                tipTitle = tipParts.shift();
            }
            if (opts.escapeTitle) {
                tipTitle = tipTitle.replace(/&/g, '&amp;').replace(/>/g, '&gt;').replace(/</g, '&lt;');
            }

            var localContent;

            /***************************************
            * ACTIVATION
            ****************************************/

            //activate clueTip
            var activate = function(event) {
                if (!opts.onActivate($this)) {
                    return false;
                }
                isActive = true;
                $cluetip.removeClass().css({ width: tipInnerWidth });
                if (tipAttribute == $this.attr('href')) {
                    $this.css('cursor', opts.cursor);
                }
                $this.attr('title', '');
                if (opts.hoverClass) {
                    $this.addClass(opts.hoverClass);
                }
                linkTop = posY = $this.offset().top;
                linkLeft = $this.offset().left;
                mouseX = event.pageX;
                mouseY = event.pageY;
                if ($this[0].tagName.toLowerCase() != 'area') {
                    sTop = $(document).scrollTop();
                    winWidth = $(window).width();
                }
                // position clueTip horizontally
                if (opts.positionBy == 'fixed') {
                    posX = linkWidth + linkLeft + lOffset;
                    $cluetip.css({ left: posX });
                } else {
                    posX = (linkWidth > linkLeft && linkLeft > tipWidth)
          || linkLeft + linkWidth + tipWidth + lOffset > winWidth
          ? linkLeft - tipWidth - lOffset
          : linkWidth + linkLeft + lOffset;
                    if ($this[0].tagName.toLowerCase() == 'area' || opts.positionBy == 'mouse' || linkWidth + tipWidth > winWidth) { // position by mouse
                        if (mouseX + 20 + tipWidth > winWidth) {
                            $cluetip.addClass(' cluetip-' + ctClass);
                            posX = (mouseX - tipWidth - lOffset) >= 0 ? mouseX - tipWidth - lOffset - parseInt($cluetip.css('marginLeft'), 10) + parseInt($cluetipInner.css('marginRight'), 10) : mouseX - (tipWidth / 2);
                        } else {
                            posX = mouseX + lOffset;
                        }
                    }
                    var pY = posX < 0 ? event.pageY + tOffset : event.pageY;
                    $cluetip.css({ left: (posX > 0 && opts.positionBy != 'bottomTop') ? posX : (mouseX + (tipWidth / 2) > winWidth) ? winWidth / 2 - tipWidth / 2 : Math.max(mouseX - (tipWidth / 2), 0) });
                }
                wHeight = $(window).height();

                /***************************************
                * load a string from cluetip method's first argument
                ***************************************/
                if (js) {
                    if (typeof js == 'function') {
                        js = js($this[0]);
                    }
                    $cluetipInner.html(js);
                    cluetipShow(pY);
                }
                /***************************************
                * load the title attribute only (or user-selected attribute).
                * clueTip title is the string before the first delimiter
                * subsequent delimiters place clueTip body text on separate lines
                ***************************************/

                else if (tipParts) {
                    var tpl = tipParts.length;
                    $cluetipInner.empty();
                    for (var i = 0; i < tpl; i++) {
                        if (i == 0) {
                            $cluetipInner.html(tipParts[i]);
                        } else {
                            $cluetipInner.append('<div class="split-body">' + tipParts[i] + '</div>');
                        }
                    };
                    cluetipShow(pY);
                }
                /***************************************
                * load external file via ajax
                ***************************************/

                else if (!opts.local && tipAttribute.indexOf('#') != 0) {
                    if (/\.(jpe?g|tiff?|gif|png)$/i.test(tipAttribute)) {
                        $cluetipInner.html('<img src="' + tipAttribute + '" alt="' + tipTitle + '" />');
                        cluetipShow(pY);
                    } else if (cluetipContents && opts.ajaxCache) {
                        $cluetipInner.html(cluetipContents);
                        cluetipShow(pY);
                    } else {
                        var ajaxSettings = opts.ajaxSettings;
                        ajaxSettings.url = tipAttribute;
                        ajaxSettings.beforeSend = function() {
                            $cluetipOuter.children().empty();
                            if (opts.waitImage) {
                                $('#cluetip-waitimage')
              .css({ top: mouseY + 20, left: mouseX + 20 })
              .show();
                            }
                        };
                        ajaxSettings.error = function() {
                            if (isActive) {
                                $cluetipInner.html('<i>sorry, the contents could not be loaded</i>');
                            }
                        };
                        ajaxSettings.success = function(data) {
                            cluetipContents = opts.ajaxProcess(data);
                            if (isActive) {
                                $cluetipInner.html(cluetipContents);
                            }
                        };
                        ajaxSettings.complete = function() {
                            imgCount = $('#cluetip-inner img').length;
                            if (imgCount && !$.browser.opera) {
                                $('#cluetip-inner img').load(function() {
                                    imgCount--;
                                    if (imgCount < 1) {
                                        $('#cluetip-waitimage').hide();
                                        if (isActive) cluetipShow(pY);
                                    }
                                });
                            } else {
                                $('#cluetip-waitimage').hide();
                                if (isActive) cluetipShow(pY);
                            }
                        };
                        $.ajax(ajaxSettings);
                    }

                    /***************************************
                    * load an element from the same page
                    ***************************************/
                } else if (opts.local) {
                    var $localContent = $(tipAttribute + ':eq(' + index + ')').clone(true).show();
                    $cluetipInner.html($localContent);
                    cluetipShow(pY);
                }
            };

            // get dimensions and options for cluetip and prepare it to be shown
            var cluetipShow = function(bpY) {
                $cluetip.addClass('cluetip-' + ctClass);

                if (opts.truncate) {
                    var $truncloaded = $cluetipInner.text().slice(0, opts.truncate) + '...';
                    $cluetipInner.html($truncloaded);
                }
                function doNothing() { }; //empty function
                tipTitle ? $cluetipTitle.show().html(tipTitle) : (opts.showTitle) ? $cluetipTitle.show().html('&nbsp;') : $cluetipTitle.hide();
                if (opts.sticky) {
                    var $closeLink = $('<div id="cluetip-close"><a href="#">' + opts.closeText + '</a></div>');
                    (opts.closePosition == 'bottom') ? $closeLink.appendTo($cluetipInner) : (opts.closePosition == 'title') ? $closeLink.prependTo($cluetipTitle) : $closeLink.prependTo($cluetipInner);
                    $closeLink.click(function() {
                        cluetipClose();
                        return false;
                    });
                    if (opts.mouseOutClose) {
                        if ($.fn.hoverIntent && opts.hoverIntent) {
                            $cluetip.hoverIntent({
                                over: doNothing,
                                timeout: opts.hoverIntent.timeout,
                                out: function() { $closeLink.trigger('click'); }
                            });
                        } else {
                            $cluetip.hover(doNothing,
            function() { $closeLink.trigger('click'); });
                        }
                    } else {
                        $cluetip.unbind('mouseout');
                    }
                }
                // now that content is loaded, finish the positioning
                var direction = '';
                $cluetipOuter.css({ overflow: defHeight == 'auto' ? 'visible' : 'auto', height: defHeight });
                tipHeight = defHeight == 'auto' ? Math.max($cluetip.outerHeight(), $cluetip.height()) : parseInt(defHeight, 10);
                tipY = posY;
                baseline = sTop + wHeight;
                if (opts.positionBy == 'fixed') {
                    tipY = posY - opts.dropShadowSteps + tOffset;
                } else if ((posX < mouseX && Math.max(posX, 0) + tipWidth > mouseX) || opts.positionBy == 'bottomTop') {
                    if (posY + tipHeight + tOffset > baseline && mouseY - sTop > tipHeight + tOffset) {
                        tipY = mouseY - tipHeight - tOffset;
                        direction = 'top';
                    } else {
                        tipY = mouseY + tOffset;
                        direction = 'bottom';
                    }
                } else if (posY + tipHeight + tOffset > baseline) {
                    tipY = (tipHeight >= wHeight) ? sTop : baseline - tipHeight - tOffset;
                } else if ($this.css('display') == 'block' || $this[0].tagName.toLowerCase() == 'area' || opts.positionBy == "mouse") {
                    tipY = bpY - tOffset;
                } else {
                    tipY = posY - opts.dropShadowSteps;
                }
                if (direction == '') {
                    posX < linkLeft ? direction = 'left' : direction = 'right';
                }
                $cluetip.css({ top: tipY + 'px' }).removeClass().addClass('clue-' + direction + '-' + ctClass).addClass(' cluetip-' + ctClass);
                if (opts.arrows) { // set up arrow positioning to align with element
                    var bgY = (posY - tipY - opts.dropShadowSteps);
                    $cluetipArrows.css({ top: (/(left|right)/.test(direction) && posX >= 0 && bgY > 0) ? bgY + 'px' : /(left|right)/.test(direction) ? 0 : '' }).show();
                } else {
                    $cluetipArrows.hide();
                }

                // (first hide, then) ***SHOW THE CLUETIP***
                $dropShadow.hide();
                $cluetip.hide()[opts.fx.open](opts.fx.open != 'show' && opts.fx.openSpeed);
                if (opts.dropShadow) $dropShadow.css({ height: tipHeight, width: tipInnerWidth }).show();
                if ($.fn.bgiframe) { $cluetip.bgiframe(); }
                // delayed close (not fully tested)
                if (opts.delayedClose > 0) {
                    closeOnDelay = setTimeout(cluetipClose, opts.delayedClose);
                }
                // trigger the optional onShow function
                opts.onShow($cluetip, $cluetipInner);

            };

            /***************************************
            =INACTIVATION
            -------------------------------------- */
            var inactivate = function(event) {
                isActive = false;
                $('#cluetip-waitimage').hide();
                if (!opts.sticky || (/click|toggle/).test(opts.activation)) {
                    cluetipClose();
                    clearTimeout(closeOnDelay);
                };
                if (opts.hoverClass) {
                    $this.removeClass(opts.hoverClass);
                }
            };
            // close cluetip and reset some things
            var cluetipClose = function() {
                $cluetipOuter
      .parent().hide().removeClass();
                opts.onHide($cluetip, $cluetipInner);
                $this.removeClass('cluetip-clicked');
                if (tipTitle) {
                    $this.attr(opts.titleAttribute, tipTitle);
                }
                $this.css('cursor', '');
                if (opts.arrows) $cluetipArrows.css({ top: '' });
            };

            /***************************************
            =BIND EVENTS
            -------------------------------------- */
            // activate by click
            if ((/click|toggle/).test(opts.activation)) {
                $this.click(function(event) {
                    if ($cluetip.is(':hidden') || !$this.is('.cluetip-clicked')) {
                        activate(event);
                        $('.cluetip-clicked').removeClass('cluetip-clicked');
                        $this.addClass('cluetip-clicked');
                    } else {
                        inactivate(event);
                    }
                    this.blur();
                    return false;
                });
                // activate by focus; inactivate by blur
            } else if (opts.activation == 'focus') {
                $this.focus(function(event) {
                    activate(event);
                });
                $this.blur(function(event) {
                    inactivate(event);
                });
                // activate by hover
                // clicking is returned false if cluetip url is same as href url
            } else {
                $this.click(function() {
                    if ($this.attr('href') && $this.attr('href') == tipAttribute && !opts.clickThrough) {
                        return false;
                    }
                });
                //set up mouse tracking
                var mouseTracks = function(evt) {
                    if (opts.tracking == true) {
                        var trackX = posX - evt.pageX;
                        var trackY = tipY ? tipY - evt.pageY : posY - evt.pageY;
                        $this.mousemove(function(evt) {
                            $cluetip.css({ left: evt.pageX + trackX, top: evt.pageY + trackY });
                        });
                    }
                };
                if ($.fn.hoverIntent && opts.hoverIntent) {
                    $this.mouseover(function() { $this.attr('title', ''); })
          .hoverIntent({
              sensitivity: opts.hoverIntent.sensitivity,
              interval: opts.hoverIntent.interval,
              over: function(event) {
                  activate(event);
                  mouseTracks(event);
              },
              timeout: opts.hoverIntent.timeout,
              out: function(event) { inactivate(event); $this.unbind('mousemove'); }
          });
                } else {
                    $this.hover(function(event) {
                        activate(event);
                        mouseTracks(event);
                    }, function(event) {
                        inactivate(event);
                        $this.unbind('mousemove');
                    });
                }
            }
        });
    };

    /*
    * options for clueTip
    *
    * each one can be explicitly overridden by changing its value.
    * for example: $.fn.cluetip.defaults.width = 200;
    * would change the default width for all clueTips to 200.
    *
    * each one can also be overridden by passing an options map to the cluetip method.
    * for example: $('a.example').cluetip({width: 200});
    * would change the default width to 200 for clueTips invoked by a link with class of "example"
    *
    */

    $.fn.cluetip.defaults = { // set up default options
        width: 275, // The width of the clueTip
        height: 'auto', // The height of the clueTip
        cluezIndex: 97, // Sets the z-index style property of the clueTip
        positionBy: 'auto', // Sets the type of positioning: 'auto', 'mouse','bottomTop', 'fixed'
        topOffset: 15, // Number of px to offset clueTip from top of invoking element
        leftOffset: 15, // Number of px to offset clueTip from left of invoking element
        local: false, // Whether to use content from the same page for the clueTip's body
        localPrefix: null, // string to be prepended to the tip attribute if local is true
        hideLocal: true, // If local option is set to true, this determines whether local content
        // to be shown in clueTip should be hidden at its original location
        attribute: 'rel', // the attribute to be used for fetching the clueTip's body content
        titleAttribute: 'title', // the attribute to be used for fetching the clueTip's title
        splitTitle: '', // A character used to split the title attribute into the clueTip title and divs
        // within the clueTip body. more info below [6]
        escapeTitle: false, // whether to html escape the title attribute
        showTitle: true, // show title bar of the clueTip, even if title attribute not set
        cluetipClass: 'default', // class added to outermost clueTip div in the form of 'cluetip-' + clueTipClass.
        hoverClass: '', // class applied to the invoking element onmouseover and removed onmouseout
        waitImage: true, // whether to show a "loading" img, which is set in jquery.cluetip.css
        cursor: 'help',
        arrows: false, // if true, displays arrow on appropriate side of clueTip
        dropShadow: true, // set to false if you don't want the drop-shadow effect on the clueTip
        dropShadowSteps: 6, // adjusts the size of the drop shadow
        sticky: false, // keep visible until manually closed
        mouseOutClose: false, // close when clueTip is moused out
        activation: 'hover', // set to 'click' to force user to click to show clueTip
        // set to 'focus' to show on focus of a form element and hide on blur
        clickThrough: false, // if true, and activation is not 'click', then clicking on link will take user to the link's href,
        // even if href and tipAttribute are equal
        tracking: false, // if true, clueTip will track mouse movement (experimental)
        delayedClose: 0, // close clueTip on a timed delay (experimental)
        closePosition: 'top', // location of close text for sticky cluetips; can be 'top' or 'bottom' or 'title'
        closeText: 'Close', // text (or HTML) to to be clicked to close sticky clueTips
        truncate: 0, // number of characters to truncate clueTip's contents. if 0, no truncation occurs

        // effect and speed for opening clueTips
        fx: {
            open: 'show', // can be 'show' or 'slideDown' or 'fadeIn'
            openSpeed: ''
        },

        // settings for when hoverIntent plugin is used
        hoverIntent: {
            sensitivity: 3,
            interval: 50,
            timeout: 0
        },

        // function to run just before clueTip is shown.
        onActivate: function(e) { return true; },

        // function to run just after clueTip is shown.
        onShow: function(ct, c) { },
        // function to run just after clueTip is hidden.
        onHide: function(ct, c) { },
        // whether to cache results of ajax request to avoid unnecessary hits to server
        ajaxCache: true,

        // process data retrieved via xhr before it's displayed
        ajaxProcess: function(data) {
            data = data.replace(/<s(cript|tyle)(.|\s)*?\/s(cript|tyle)>/g, '').replace(/<(link|title)(.|\s)*?\/(link|title)>/g, '');
            return data;
        },

        // can pass in standard $.ajax() parameters, not including error, complete, success, and url
        ajaxSettings: {
            dataType: 'html'
        },
        debug: false
    };


    /*
    * Global defaults for clueTips. Apply to all calls to the clueTip plugin.
    *
    * @example $.cluetip.setup({
    * insertionType: 'prependTo',
    * insertionElement: '#container'
    * });
    *
    * @property
    * @name $.cluetip.setup
    * @type Map
    * @cat Plugins/tooltip
    * @option String insertionType: Default is 'appendTo'. Determines the method to be used for inserting the clueTip into the DOM. Permitted values are 'appendTo', 'prependTo', 'insertBefore', and 'insertAfter'
    * @option String insertionElement: Default is 'body'. Determines which element in the DOM the plugin will reference when inserting the clueTip.
    *
    */

    var insertionType = 'appendTo', insertionElement = 'body';
    $.cluetip = {};
    $.cluetip.setup = function(options) {
        if (options && options.insertionType && (options.insertionType).match(/appendTo|prependTo|insertBefore|insertAfter/)) {
            insertionType = options.insertionType;
        }
        if (options && options.insertionElement) {
            insertionElement = options.insertionElement;
        }
    };

})(jQuery);
 