mirror of
https://github.com/wahyd4/cdnjs.git
synced 2026-08-29 06:36:08 +10:00
add jquery-powertip
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
jQuery PowerTip Plugin v1.1.0
|
||||
http://stevenbenner.github.com/jquery-powertip/
|
||||
Copyright 2012 Steven Benner (http://stevenbenner.com/)
|
||||
Released under the MIT license.
|
||||
<https://raw.github.com/stevenbenner/jquery-powertip/master/LICENSE.txt>
|
||||
*/
|
||||
(function(c){function w(b,c,m){function j(h,d){e();b.data("hasActiveHover")||(h?(d&&b.data("forcedOpen",!0),m.showTip(b)):(a.popOpenImminent=!0,i=setTimeout(function(){i=null;var g=Math.abs(a.previousX-a.currentX),d=Math.abs(a.previousY-a.currentY);g+d<c.intentSensitivity?m.showTip(b):(a.previousX=a.currentX,a.previousY=a.currentY,j())},c.intentPollInterval)))}function e(){i=clearTimeout(i)}var i=null;return{show:j,hide:function(h){e();b.data("hasActiveHover")&&(a.popOpenImminent=!1,b.data("forcedOpen",
|
||||
!1),h?m.hideTip(b):i=setTimeout(function(){i=null;m.hideTip(b)},c.closeDelay))},cancel:e}}function y(b){function u(g){if(g.data("hasActiveHover"))if(a.isPopOpen)a.isClosing||m(a.activeHover),d.delay(100).queue(function(a){u(g);a()});else{g.trigger("powerTipPreRender");var p=g.data("powertip"),r=g.data("powertiptarget"),s=g.data("powertipjq"),f=r?c("#"+r):[];if(p)d.html(p);else if(s&&0<s.length)d.empty(),s.clone(!0,!0).appendTo(d);else if(f&&0<f.length)d.html(c("#"+r).html());else return;g.trigger("powerTipRender");
|
||||
j.on("closePowerTip",function(){g.data("displayController").hide(!0)});a.activeHover=g;a.isPopOpen=!0;d.data("followMouse",b.followMouse);d.data("mouseOnToPopup",b.mouseOnToPopup);if(b.followMouse)e();else{var t=d.outerWidth(),k=d.outerHeight(),q,o,l;b.smartPlacement?(p=c.fn.powerTip.smartPlacementLists[b.placement],c.each(p,function(a,b){q=i(g,b,t,k);o=b;var d=q,c=n.scrollLeft(),f=n.scrollTop(),p=n.width(),h=n.height(),e=[];d.y<f&&e.push("top");d.y+k>f+h&&e.push("bottom");d.x<c&&e.push("left");d.x+
|
||||
t>c+p&&e.push("right");l=e;if(0===l.length)return!1})):(q=i(g,b.placement,t,k),o=b.placement);d.addClass(o);h(q.x,q.y);a.isFixedPopOpen=!0}d.fadeIn(b.fadeInTime,function(){a.desyncTimeout||(a.desyncTimeout=setInterval(x,500));g.trigger("powerTipOpen")})}}function m(g){a.isClosing=!0;g.data("hasActiveHover",!1);g.data("forcedOpen",!1);a.activeHover=null;a.isPopOpen=!1;a.desyncTimeout=clearInterval(a.desyncTimeout);j.off("closePowerTip");d.fadeOut(b.fadeOutTime,function(){a.isClosing=!1;a.isFixedPopOpen=
|
||||
!1;d.removeClass();h(a.currentX+b.offset,a.currentY+b.offset);g.trigger("powerTipClose")})}function x(){if(a.isPopOpen&&!a.isClosing){var b=!1;!1===a.activeHover.data("hasActiveHover")?b=!0:!o(a.activeHover)&&(!a.activeHover.is(":focus")&&!a.activeHover.data("forcedOpen"))&&(d.data("mouseOnToPopup")?o(d)||(b=!0):b=!0);b&&m(a.activeHover)}}function e(){if(a.isPopOpen&&!a.isFixedPopOpen||a.popOpenImminent&&!a.isFixedPopOpen&&d.data("hasMouseMove")){var g=n.scrollTop(),c=n.width(),i=n.height(),e=d.outerWidth(),
|
||||
f=d.outerHeight(),j=0,k=0,j=e+a.currentX+b.offset<c?a.currentX+b.offset:c-e,k=f+a.currentY+b.offset<g+i?a.currentY+b.offset:g+i-f;h(j,k)}}function i(a,d,c,i){var f=a.offset(),e=a.outerWidth(),a=a.outerHeight(),k=0,h=0;switch(d){case "n":k=f.left+e/2-c/2;h=f.top-i-b.offset;break;case "e":k=f.left+e+b.offset;h=f.top+a/2-i/2;break;case "s":k=f.left+e/2-c/2;h=f.top+a+b.offset;break;case "w":k=f.left-c-b.offset;h=f.top+a/2-i/2;break;case "nw":k=f.left-c+20;h=f.top-i-b.offset;break;case "ne":k=f.left+e-
|
||||
20;h=f.top-i-b.offset;break;case "sw":k=f.left-c+20;h=f.top+a+b.offset;break;case "se":k=f.left+e-20,h=f.top+a+b.offset}return{x:Math.round(k),y:Math.round(h)}}function h(a,b){d.css("left",a+"px");d.css("top",b+"px")}var d=c("#"+b.popupId);0===d.length&&(d=c("<div></div>",{id:b.popupId}),0===l.length&&(l=c("body")),l.append(d));if(b.followMouse){if(!d.data("hasMouseMove"))j.on({mousemove:e,scroll:e});d.data("hasMouseMove",!0)}if(b.followMouse||b.mouseOnToPopup)d.on({mouseenter:function(){(d.data("followMouse")||
|
||||
d.data("mouseOnToPopup"))&&a.activeHover&&a.activeHover.data("displayController").cancel()},mouseleave:function(){d.data("mouseOnToPopup")&&a.activeHover&&a.activeHover.data("displayController").hide()}});return{showTip:function(a){a.data("hasActiveHover",true);d.queue(function(b){u(a);b()})},hideTip:m}}function v(b){a.currentX=b.pageX;a.currentY=b.pageY}function o(b){var c=b.offset();return a.currentX>=c.left&&a.currentX<=c.left+b.outerWidth()&&a.currentY>=c.top&&a.currentY<=c.top+b.outerHeight()}
|
||||
var j=c(document),n=c(window),l=c("body"),a={isPopOpen:!1,isFixedPopOpen:!1,isClosing:!1,popOpenImminent:!1,activeHover:null,currentX:0,currentY:0,previousX:0,previousY:0,desyncTimeout:null,mouseTrackingActive:!1};c.fn.powerTip=function(b){if(!this.length)return this;var n=c.extend({},c.fn.powerTip.defaults,b),m=new y(n),l=0,e=0;a.mouseTrackingActive||(a.mouseTrackingActive=!0,c(function(){l=j.scrollLeft();e=j.scrollTop()}),j.on({mousemove:v,scroll:function(){var b=j.scrollLeft(),c=j.scrollTop();
|
||||
b!==l&&(a.currentX+=b-l,l=b);c!==e&&(a.currentY+=c-e,e=c)}}));this.each(function(){var a=c(this),b=a.data("powertip"),d=a.data("powertipjq"),e=a.data("powertiptarget"),j=a.attr("title");if(!b&&!e&&!d&&j){a.data("powertip",j);a.removeAttr("title")}a.data("displayController",new w(a,n,m))});return this.on({mouseenter:function(b){v(b);a.previousX=b.pageX;a.previousY=b.pageY;c(this).data("displayController").show()},mouseleave:function(){c(this).data("displayController").hide()},focus:function(){var a=
|
||||
c(this);o(a)||a.data("displayController").show(true)},blur:function(){c(this).data("displayController").hide(true)}})};c.fn.powerTip.defaults={fadeInTime:200,fadeOutTime:100,followMouse:!1,popupId:"powerTip",intentSensitivity:7,intentPollInterval:100,closeDelay:100,placement:"n",smartPlacement:!1,offset:10,mouseOnToPopup:!1};c.fn.powerTip.smartPlacementLists={n:["n","ne","nw","s"],e:"e ne se w nw sw n s e".split(" "),s:["s","se","sw","n"],w:"w nw sw e ne se n s w".split(" "),nw:"nw w sw n s se nw".split(" "),
|
||||
ne:"ne e se n s sw ne".split(" "),sw:"sw w nw s n ne sw".split(" "),se:"se e ne s n nw se".split(" ")};c.powerTip={showTip:function(a){c.powerTip.closeTip();a=a.first();o(a)||a.data("displayController").show(!0,!0)},closeTip:function(){j.triggerHandler("closePowerTip")}}})(jQuery);
|
||||
@@ -0,0 +1,796 @@
|
||||
/**
|
||||
* PowerTip
|
||||
*
|
||||
* @fileoverview jQuery plugin that creates hover tooltips.
|
||||
* @link http://stevenbenner.github.com/jquery-powertip/
|
||||
* @author Steven Benner (http://stevenbenner.com/)
|
||||
* @version 1.1.0
|
||||
* @requires jQuery 1.7+
|
||||
*
|
||||
* @license jQuery PowerTip Plugin v1.1.0
|
||||
* http://stevenbenner.github.com/jquery-powertip/
|
||||
* Copyright 2012 Steven Benner (http://stevenbenner.com/)
|
||||
* Released under the MIT license.
|
||||
* <https://raw.github.com/stevenbenner/jquery-powertip/master/LICENSE.txt>
|
||||
*/
|
||||
|
||||
(function($) {
|
||||
'use strict';
|
||||
|
||||
// useful private variables
|
||||
var $document = $(document),
|
||||
$window = $(window),
|
||||
$body = $('body');
|
||||
|
||||
/**
|
||||
* Session data
|
||||
* Private properties global to all powerTip instances
|
||||
* @type Object
|
||||
*/
|
||||
var session = {
|
||||
isPopOpen: false,
|
||||
isFixedPopOpen: false,
|
||||
isClosing: false,
|
||||
popOpenImminent: false,
|
||||
activeHover: null,
|
||||
currentX: 0,
|
||||
currentY: 0,
|
||||
previousX: 0,
|
||||
previousY: 0,
|
||||
desyncTimeout: null,
|
||||
mouseTrackingActive: false
|
||||
};
|
||||
|
||||
/**
|
||||
* Display hover tooltips on the matched elements.
|
||||
* @param {Object} opts The options object to use for the plugin.
|
||||
* @return {Object} jQuery object for the matched selectors.
|
||||
*/
|
||||
$.fn.powerTip = function(opts) {
|
||||
|
||||
// don't do any work if there were no matched elements
|
||||
if (!this.length) {
|
||||
return this;
|
||||
}
|
||||
|
||||
// extend options
|
||||
var options = $.extend({}, $.fn.powerTip.defaults, opts),
|
||||
tipController = new TooltipController(options);
|
||||
|
||||
// hook mouse tracking
|
||||
initMouseTracking();
|
||||
|
||||
// setup the elements
|
||||
this.each(function() {
|
||||
var $this = $(this),
|
||||
dataPowertip = $this.data('powertip'),
|
||||
dataElem = $this.data('powertipjq'),
|
||||
dataTarget = $this.data('powertiptarget'),
|
||||
title = $this.attr('title');
|
||||
|
||||
|
||||
// attempt to use title attribute text if there is no data-powertip,
|
||||
// data-powertipjq or data-powertiptarget. If we do use the title
|
||||
// attribute, delete the attribute so the browser will not show it
|
||||
if (!dataPowertip && !dataTarget && !dataElem && title) {
|
||||
$this.data('powertip', title);
|
||||
$this.removeAttr('title');
|
||||
}
|
||||
|
||||
// create hover controllers for each element
|
||||
$this.data(
|
||||
'displayController',
|
||||
new DisplayController($this, options, tipController)
|
||||
);
|
||||
});
|
||||
|
||||
// attach hover events to all matched elements
|
||||
return this.on({
|
||||
// mouse events
|
||||
mouseenter: function(event) {
|
||||
trackMouse(event);
|
||||
session.previousX = event.pageX;
|
||||
session.previousY = event.pageY;
|
||||
$(this).data('displayController').show();
|
||||
},
|
||||
mouseleave: function() {
|
||||
$(this).data('displayController').hide();
|
||||
},
|
||||
|
||||
// keyboard events
|
||||
focus: function() {
|
||||
var element = $(this);
|
||||
if (!isMouseOver(element)) {
|
||||
element.data('displayController').show(true);
|
||||
}
|
||||
},
|
||||
blur: function() {
|
||||
$(this).data('displayController').hide(true);
|
||||
}
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Default options for the powerTip plugin.
|
||||
* @type Object
|
||||
*/
|
||||
$.fn.powerTip.defaults = {
|
||||
fadeInTime: 200,
|
||||
fadeOutTime: 100,
|
||||
followMouse: false,
|
||||
popupId: 'powerTip',
|
||||
intentSensitivity: 7,
|
||||
intentPollInterval: 100,
|
||||
closeDelay: 100,
|
||||
placement: 'n',
|
||||
smartPlacement: false,
|
||||
offset: 10,
|
||||
mouseOnToPopup: false
|
||||
};
|
||||
|
||||
/**
|
||||
* Default smart placement priority lists.
|
||||
* The first item in the array is the highest priority, the last is the
|
||||
* lowest. The last item is also the default, which will be used if all
|
||||
* previous options do not fit.
|
||||
* @type Object
|
||||
*/
|
||||
$.fn.powerTip.smartPlacementLists = {
|
||||
n: ['n', 'ne', 'nw', 's'],
|
||||
e: ['e', 'ne', 'se', 'w', 'nw', 'sw', 'n', 's', 'e'],
|
||||
s: ['s', 'se', 'sw', 'n'],
|
||||
w: ['w', 'nw', 'sw', 'e', 'ne', 'se', 'n', 's', 'w'],
|
||||
nw: ['nw', 'w', 'sw', 'n', 's', 'se', 'nw'],
|
||||
ne: ['ne', 'e', 'se', 'n', 's', 'sw', 'ne'],
|
||||
sw: ['sw', 'w', 'nw', 's', 'n', 'ne', 'sw'],
|
||||
se: ['se', 'e', 'ne', 's', 'n', 'nw', 'se']
|
||||
};
|
||||
|
||||
/**
|
||||
* Public API
|
||||
* @type Object
|
||||
*/
|
||||
$.powerTip = {
|
||||
|
||||
/**
|
||||
* Attempts to show the tooltip for the specified element.
|
||||
* @public
|
||||
* @param {Object} element The element that the tooltip should for.
|
||||
*/
|
||||
showTip: function(element) {
|
||||
// close any open tooltip
|
||||
$.powerTip.closeTip();
|
||||
// grab only the first matched element and ask it to show its tip
|
||||
element = element.first();
|
||||
if (!isMouseOver(element)) {
|
||||
element.data('displayController').show(true, true);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Attempts to close any open tooltips.
|
||||
* @public
|
||||
*/
|
||||
closeTip: function() {
|
||||
$document.triggerHandler('closePowerTip');
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a new tooltip display controller.
|
||||
* @private
|
||||
* @constructor
|
||||
* @param {Object} element The element that this controller will handle.
|
||||
* @param {Object} options Options object containing settings.
|
||||
* @param {TooltipController} tipController The TooltipController for this instance.
|
||||
*/
|
||||
function DisplayController(element, options, tipController) {
|
||||
var hoverTimer = null;
|
||||
|
||||
/**
|
||||
* Begins the process of showing a tooltip.
|
||||
* @private
|
||||
* @param {Boolean=} immediate Skip intent testing (optional).
|
||||
* @param {Boolean=} forceOpen Ignore cursor position and force tooltip to open (optional).
|
||||
*/
|
||||
function openTooltip(immediate, forceOpen) {
|
||||
cancelTimer();
|
||||
if (!element.data('hasActiveHover')) {
|
||||
if (!immediate) {
|
||||
session.popOpenImminent = true;
|
||||
hoverTimer = setTimeout(
|
||||
function() {
|
||||
hoverTimer = null;
|
||||
checkForIntent(element);
|
||||
},
|
||||
options.intentPollInterval
|
||||
);
|
||||
} else {
|
||||
if (forceOpen) {
|
||||
element.data('forcedOpen', true);
|
||||
}
|
||||
tipController.showTip(element);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Begins the process of closing a tooltip.
|
||||
* @private
|
||||
* @param {Boolean=} disableDelay Disable close delay (optional).
|
||||
*/
|
||||
function closeTooltip(disableDelay) {
|
||||
cancelTimer();
|
||||
if (element.data('hasActiveHover')) {
|
||||
session.popOpenImminent = false;
|
||||
element.data('forcedOpen', false);
|
||||
if (!disableDelay) {
|
||||
hoverTimer = setTimeout(
|
||||
function() {
|
||||
hoverTimer = null;
|
||||
tipController.hideTip(element);
|
||||
},
|
||||
options.closeDelay
|
||||
);
|
||||
} else {
|
||||
tipController.hideTip(element);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks mouse position to make sure that the user intended to hover
|
||||
* on the specified element before showing the tooltip.
|
||||
* @private
|
||||
*/
|
||||
function checkForIntent() {
|
||||
// calculate mouse position difference
|
||||
var xDifference = Math.abs(session.previousX - session.currentX),
|
||||
yDifference = Math.abs(session.previousY - session.currentY),
|
||||
totalDifference = xDifference + yDifference;
|
||||
|
||||
// check if difference has passed the sensitivity threshold
|
||||
if (totalDifference < options.intentSensitivity) {
|
||||
tipController.showTip(element);
|
||||
} else {
|
||||
// try again
|
||||
session.previousX = session.currentX;
|
||||
session.previousY = session.currentY;
|
||||
openTooltip();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels active hover timer.
|
||||
* @private
|
||||
*/
|
||||
function cancelTimer() {
|
||||
hoverTimer = clearTimeout(hoverTimer);
|
||||
}
|
||||
|
||||
// expose the methods
|
||||
return {
|
||||
show: openTooltip,
|
||||
hide: closeTooltip,
|
||||
cancel: cancelTimer
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new tooltip controller.
|
||||
* @private
|
||||
* @constructor
|
||||
* @param {Object} options Options object containing settings.
|
||||
*/
|
||||
function TooltipController(options) {
|
||||
|
||||
// build and append popup div if it does not already exist
|
||||
var tipElement = $('#' + options.popupId);
|
||||
if (tipElement.length === 0) {
|
||||
tipElement = $('<div></div>', { id: options.popupId });
|
||||
// grab body element if it was not populated when the script loaded
|
||||
// this hack exists solely for jsfiddle support
|
||||
if ($body.length === 0) {
|
||||
$body = $('body');
|
||||
}
|
||||
$body.append(tipElement);
|
||||
}
|
||||
|
||||
// hook mousemove for cursor follow tooltips
|
||||
if (options.followMouse) {
|
||||
// only one positionTipOnCursor hook per popup element, please
|
||||
if (!tipElement.data('hasMouseMove')) {
|
||||
$document.on({
|
||||
mousemove: positionTipOnCursor,
|
||||
scroll: positionTipOnCursor
|
||||
});
|
||||
}
|
||||
tipElement.data('hasMouseMove', true);
|
||||
}
|
||||
|
||||
// if we want to be able to mouse onto the popup then we need to attach
|
||||
// hover events to the popup that will cancel a close request on hover
|
||||
// and start a new close request on mouseleave
|
||||
if (options.followMouse || options.mouseOnToPopup) {
|
||||
tipElement.on({
|
||||
mouseenter: function() {
|
||||
if (tipElement.data('followMouse') || tipElement.data('mouseOnToPopup')) {
|
||||
// check activeHover in case the mouse cursor entered
|
||||
// the tooltip during the fadeOut and close cycle
|
||||
if (session.activeHover) {
|
||||
session.activeHover.data('displayController').cancel();
|
||||
}
|
||||
}
|
||||
},
|
||||
mouseleave: function() {
|
||||
if (tipElement.data('mouseOnToPopup')) {
|
||||
// check activeHover in case the mouse cursor entered
|
||||
// the tooltip during the fadeOut and close cycle
|
||||
if (session.activeHover) {
|
||||
session.activeHover.data('displayController').hide();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Gives the specified element the active-hover state and queues up
|
||||
* the showTip function.
|
||||
* @private
|
||||
* @param {Object} element The element that the tooltip should target.
|
||||
*/
|
||||
function beginShowTip(element) {
|
||||
element.data('hasActiveHover', true);
|
||||
// show popup, asap
|
||||
tipElement.queue(function(next) {
|
||||
showTip(element);
|
||||
next();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the tooltip popup, as soon as possible.
|
||||
* @private
|
||||
* @param {Object} element The element that the popup should target.
|
||||
*/
|
||||
function showTip(element) {
|
||||
// it is possible, especially with keyboard navigation, to move on
|
||||
// to another element with a tooltip during the queue to get to
|
||||
// this point in the code. if that happens then we need to not
|
||||
// proceed or we may have the fadeout callback for the last tooltip
|
||||
// execute immediately after this code runs, causing bugs.
|
||||
if (!element.data('hasActiveHover')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// if the popup is open and we got asked to open another one then
|
||||
// the old one is still in its fadeOut cycle, so wait and try again
|
||||
if (session.isPopOpen) {
|
||||
if (!session.isClosing) {
|
||||
hideTip(session.activeHover);
|
||||
}
|
||||
tipElement.delay(100).queue(function(next) {
|
||||
showTip(element);
|
||||
next();
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// trigger powerTipPreRender event
|
||||
element.trigger('powerTipPreRender');
|
||||
|
||||
var tipText = element.data('powertip'),
|
||||
tipTarget = element.data('powertiptarget'),
|
||||
tipElem = element.data('powertipjq'),
|
||||
tipContent = tipTarget ? $('#' + tipTarget) : [];
|
||||
|
||||
// set popup content
|
||||
if (tipText) {
|
||||
tipElement.html(tipText);
|
||||
} else if (tipElem && tipElem.length > 0) {
|
||||
tipElement.empty();
|
||||
tipElem.clone(true, true).appendTo(tipElement);
|
||||
} else if (tipContent && tipContent.length > 0) {
|
||||
tipElement.html($('#' + tipTarget).html());
|
||||
} else {
|
||||
// we have no content to display, give up
|
||||
return;
|
||||
}
|
||||
|
||||
// trigger powerTipRender event
|
||||
element.trigger('powerTipRender');
|
||||
|
||||
// hook close event for triggering from the api
|
||||
$document.on('closePowerTip', function() {
|
||||
element.data('displayController').hide(true);
|
||||
});
|
||||
|
||||
session.activeHover = element;
|
||||
session.isPopOpen = true;
|
||||
|
||||
tipElement.data('followMouse', options.followMouse);
|
||||
tipElement.data('mouseOnToPopup', options.mouseOnToPopup);
|
||||
|
||||
// set popup position
|
||||
if (!options.followMouse) {
|
||||
positionTipOnElement(element);
|
||||
session.isFixedPopOpen = true;
|
||||
} else {
|
||||
positionTipOnCursor();
|
||||
}
|
||||
|
||||
// fadein
|
||||
tipElement.fadeIn(options.fadeInTime, function() {
|
||||
// start desync polling
|
||||
if (!session.desyncTimeout) {
|
||||
session.desyncTimeout = setInterval(closeDesyncedTip, 500);
|
||||
}
|
||||
|
||||
// trigger powerTipOpen event
|
||||
element.trigger('powerTipOpen');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Hides the tooltip popup, immediately.
|
||||
* @private
|
||||
* @param {Object} element The element that the popup should target.
|
||||
*/
|
||||
function hideTip(element) {
|
||||
session.isClosing = true;
|
||||
element.data('hasActiveHover', false);
|
||||
element.data('forcedOpen', false);
|
||||
// reset session
|
||||
session.activeHover = null;
|
||||
session.isPopOpen = false;
|
||||
// stop desync polling
|
||||
session.desyncTimeout = clearInterval(session.desyncTimeout);
|
||||
// unhook close event api listener
|
||||
$document.off('closePowerTip');
|
||||
// fade out
|
||||
tipElement.fadeOut(options.fadeOutTime, function() {
|
||||
session.isClosing = false;
|
||||
session.isFixedPopOpen = false;
|
||||
tipElement.removeClass();
|
||||
// support mouse-follow and fixed position pops at the same
|
||||
// time by moving the popup to the last known cursor location
|
||||
// after it is hidden
|
||||
setTipPosition(
|
||||
session.currentX + options.offset,
|
||||
session.currentY + options.offset
|
||||
);
|
||||
|
||||
// trigger powerTipClose event
|
||||
element.trigger('powerTipClose');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for a tooltip desync and closes the tooltip if one occurs.
|
||||
* @private
|
||||
*/
|
||||
function closeDesyncedTip() {
|
||||
// It is possible for the mouse cursor to leave an element without
|
||||
// firing the mouseleave event. This seems to happen (in FF) if the
|
||||
// element is disabled under mouse cursor, the element is moved out
|
||||
// from under the mouse cursor (such as a slideDown() occurring
|
||||
// above it), or if the browser is resized by code moving the
|
||||
// element from under the mouse cursor. If this happens it will
|
||||
// result in a desynced tooltip because we wait for any exiting
|
||||
// open tooltips to close before opening a new one. So we should
|
||||
// periodically check for a desync situation and close the tip if
|
||||
// such a situation arises.
|
||||
if (session.isPopOpen && !session.isClosing) {
|
||||
var isDesynced = false;
|
||||
|
||||
// case 1: user already moused onto another tip - easy test
|
||||
if (session.activeHover.data('hasActiveHover') === false) {
|
||||
isDesynced = true;
|
||||
} else {
|
||||
// case 2: hanging tip - have to test if mouse position is
|
||||
// not over the active hover and not over a tooltip set to
|
||||
// let the user interact with it.
|
||||
// for keyboard navigation, this only counts if the element
|
||||
// does not have focus.
|
||||
// for tooltips opened via the api we need to check if it
|
||||
// has the forcedOpen flag.
|
||||
if (!isMouseOver(session.activeHover) && !session.activeHover.is(":focus") && !session.activeHover.data('forcedOpen')) {
|
||||
if (tipElement.data('mouseOnToPopup')) {
|
||||
if (!isMouseOver(tipElement)) {
|
||||
isDesynced = true;
|
||||
}
|
||||
} else {
|
||||
isDesynced = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isDesynced) {
|
||||
// close the desynced tip
|
||||
hideTip(session.activeHover);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves the tooltip popup to the users mouse cursor.
|
||||
* @private
|
||||
*/
|
||||
function positionTipOnCursor() {
|
||||
// to support having fixed powertips on the same page as cursor
|
||||
// powertips, where both instances are referencing the same popup
|
||||
// element, we need to keep track of the mouse position constantly,
|
||||
// but we should only set the pop location if a fixed pop is not
|
||||
// currently open, a pop open is imminent or active, and the popup
|
||||
// element in question does have a mouse-follow using it.
|
||||
if ((session.isPopOpen && !session.isFixedPopOpen) || (session.popOpenImminent && !session.isFixedPopOpen && tipElement.data('hasMouseMove'))) {
|
||||
// grab measurements
|
||||
var scrollTop = $window.scrollTop(),
|
||||
windowWidth = $window.width(),
|
||||
windowHeight = $window.height(),
|
||||
popWidth = tipElement.outerWidth(),
|
||||
popHeight = tipElement.outerHeight(),
|
||||
x = 0,
|
||||
y = 0;
|
||||
|
||||
// constrain pop to browser viewport
|
||||
if ((popWidth + session.currentX + options.offset) < windowWidth) {
|
||||
x = session.currentX + options.offset;
|
||||
} else {
|
||||
x = windowWidth - popWidth;
|
||||
}
|
||||
if ((popHeight + session.currentY + options.offset) < (scrollTop + windowHeight)) {
|
||||
y = session.currentY + options.offset;
|
||||
} else {
|
||||
y = scrollTop + windowHeight - popHeight;
|
||||
}
|
||||
|
||||
// position the tooltip
|
||||
setTipPosition(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the tooltip popup too the correct position relative to the
|
||||
* specified target element. Based on options settings.
|
||||
* @private
|
||||
* @param {Object} element The element that the popup should target.
|
||||
*/
|
||||
function positionTipOnElement(element) {
|
||||
var tipWidth = tipElement.outerWidth(),
|
||||
tipHeight = tipElement.outerHeight(),
|
||||
priorityList,
|
||||
placementCoords,
|
||||
finalPlacement,
|
||||
collisions;
|
||||
|
||||
// with smart placement we will try a series of placement
|
||||
// options and use the first one that does not collide with the
|
||||
// browser view port boundaries.
|
||||
if (options.smartPlacement) {
|
||||
|
||||
// grab the placement priority list
|
||||
priorityList = $.fn.powerTip.smartPlacementLists[options.placement];
|
||||
|
||||
// iterate over the priority list and use the first placement
|
||||
// option that does not collide with the viewport. if they all
|
||||
// collide then the last placement in the list will be used.
|
||||
$.each(priorityList, function(idx, pos) {
|
||||
// get placement coordinates
|
||||
placementCoords = computePlacementCoords(
|
||||
element,
|
||||
pos,
|
||||
tipWidth,
|
||||
tipHeight
|
||||
);
|
||||
finalPlacement = pos;
|
||||
|
||||
// find collisions
|
||||
collisions = getViewportCollisions(
|
||||
placementCoords,
|
||||
tipWidth,
|
||||
tipHeight
|
||||
);
|
||||
|
||||
// break if there were no collisions
|
||||
if (collisions.length === 0) {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
} else {
|
||||
|
||||
// if we're not going to use the smart placement feature then
|
||||
// just compute the coordinates and do it
|
||||
placementCoords = computePlacementCoords(
|
||||
element,
|
||||
options.placement,
|
||||
tipWidth,
|
||||
tipHeight
|
||||
);
|
||||
finalPlacement = options.placement;
|
||||
|
||||
}
|
||||
|
||||
// add placement as class for CSS arrows
|
||||
tipElement.addClass(finalPlacement);
|
||||
|
||||
// position the tooltip
|
||||
setTipPosition(placementCoords.x, placementCoords.y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the top/left coordinates to display the tooltip at the
|
||||
* specified placement relative to the specified element.
|
||||
* @private
|
||||
* @param {Object} element The element that the tooltip should target.
|
||||
* @param {String} placement The placement for the tooltip.
|
||||
* @param {Number} popWidth Width of the tooltip element in pixels.
|
||||
* @param {Number} popHeight Height of the tooltip element in pixels.
|
||||
* @retun {Object} An object with the x and y coordinates.
|
||||
*/
|
||||
function computePlacementCoords(element, placement, popWidth, popHeight) {
|
||||
// grab measurements
|
||||
var objectOffset = element.offset(),
|
||||
objectWidth = element.outerWidth(),
|
||||
objectHeight = element.outerHeight(),
|
||||
x = 0,
|
||||
y = 0;
|
||||
|
||||
// calculate the appropriate x and y position in the document
|
||||
switch (placement) {
|
||||
case 'n':
|
||||
x = (objectOffset.left + (objectWidth / 2)) - (popWidth / 2);
|
||||
y = objectOffset.top - popHeight - options.offset;
|
||||
break;
|
||||
case 'e':
|
||||
x = objectOffset.left + objectWidth + options.offset;
|
||||
y = (objectOffset.top + (objectHeight / 2)) - (popHeight / 2);
|
||||
break;
|
||||
case 's':
|
||||
x = (objectOffset.left + (objectWidth / 2)) - (popWidth / 2);
|
||||
y = objectOffset.top + objectHeight + options.offset;
|
||||
break;
|
||||
case 'w':
|
||||
x = objectOffset.left - popWidth - options.offset;
|
||||
y = (objectOffset.top + (objectHeight / 2)) - (popHeight / 2);
|
||||
break;
|
||||
case 'nw':
|
||||
x = (objectOffset.left - popWidth) + 20;
|
||||
y = objectOffset.top - popHeight - options.offset;
|
||||
break;
|
||||
case 'ne':
|
||||
x = (objectOffset.left + objectWidth) - 20;
|
||||
y = objectOffset.top - popHeight - options.offset;
|
||||
break;
|
||||
case 'sw':
|
||||
x = (objectOffset.left - popWidth) + 20;
|
||||
y = objectOffset.top + objectHeight + options.offset;
|
||||
break;
|
||||
case 'se':
|
||||
x = (objectOffset.left + objectWidth) - 20;
|
||||
y = objectOffset.top + objectHeight + options.offset;
|
||||
break;
|
||||
}
|
||||
|
||||
return {
|
||||
x: Math.round(x),
|
||||
y: Math.round(y)
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the tooltip CSS position on the document.
|
||||
* @private
|
||||
* @param {Number} x Left position in pixels.
|
||||
* @param {Number} y Top position in pixels.
|
||||
*/
|
||||
function setTipPosition(x, y) {
|
||||
tipElement.css('left', x + 'px');
|
||||
tipElement.css('top', y + 'px');
|
||||
}
|
||||
|
||||
// expose methods
|
||||
return {
|
||||
showTip: beginShowTip,
|
||||
hideTip: hideTip
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Hooks mouse position tracking to mousemove and scroll events.
|
||||
* Prevents attaching the events more than once.
|
||||
* @private
|
||||
*/
|
||||
function initMouseTracking() {
|
||||
var lastScrollX = 0,
|
||||
lastScrollY = 0;
|
||||
|
||||
if (!session.mouseTrackingActive) {
|
||||
session.mouseTrackingActive = true;
|
||||
|
||||
// grab the current scroll position on load
|
||||
$(function() {
|
||||
lastScrollX = $document.scrollLeft();
|
||||
lastScrollY = $document.scrollTop();
|
||||
});
|
||||
|
||||
// hook mouse position tracking
|
||||
$document.on({
|
||||
mousemove: trackMouse,
|
||||
scroll: function() {
|
||||
var x = $document.scrollLeft(),
|
||||
y = $document.scrollTop();
|
||||
if (x !== lastScrollX) {
|
||||
session.currentX += x - lastScrollX;
|
||||
lastScrollX = x;
|
||||
}
|
||||
if (y !== lastScrollY) {
|
||||
session.currentY += y - lastScrollY;
|
||||
lastScrollY = y;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the current mouse coordinates to the powerTip session object.
|
||||
* @private
|
||||
* @param {Object} event The mousemove event for the document.
|
||||
*/
|
||||
function trackMouse(event) {
|
||||
session.currentX = event.pageX;
|
||||
session.currentY = event.pageY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if the mouse is currently over the specified element.
|
||||
* @private
|
||||
* @param {Object} element The element to check for hover.
|
||||
* @return {Boolean}
|
||||
*/
|
||||
function isMouseOver(element) {
|
||||
var elementPosition = element.offset();
|
||||
return session.currentX >= elementPosition.left &&
|
||||
session.currentX <= elementPosition.left + element.outerWidth() &&
|
||||
session.currentY >= elementPosition.top &&
|
||||
session.currentY <= elementPosition.top + element.outerHeight();
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds any viewport collisions that an element (the tooltip) would have
|
||||
* if it were absolutely positioned at the specified coordinates.
|
||||
* @private
|
||||
* @param {Object} coords Coordinates for the element. (e.g. {x: 123, y: 123})
|
||||
* @param {Number} elementWidth Width of the element in pixels.
|
||||
* @param {Number} elementHeight Height of the element in pixels.
|
||||
* @return {Array} Array of words representing directional collisions.
|
||||
*/
|
||||
function getViewportCollisions(coords, elementWidth, elementHeight) {
|
||||
var scrollLeft = $window.scrollLeft(),
|
||||
scrollTop = $window.scrollTop(),
|
||||
windowWidth = $window.width(),
|
||||
windowHeight = $window.height(),
|
||||
collisions = [];
|
||||
|
||||
if (coords.y < scrollTop) {
|
||||
collisions.push('top');
|
||||
}
|
||||
if (coords.y + elementHeight > scrollTop + windowHeight) {
|
||||
collisions.push('bottom');
|
||||
}
|
||||
if (coords.x < scrollLeft) {
|
||||
collisions.push('left');
|
||||
}
|
||||
if (coords.x + elementWidth > scrollLeft + windowWidth) {
|
||||
collisions.push('right');
|
||||
}
|
||||
|
||||
return collisions;
|
||||
}
|
||||
|
||||
}(jQuery));
|
||||
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"name": "jquery-powertip",
|
||||
"filename": "jquery.powertip-1.1.0.min.js",
|
||||
"version": "1.1.0",
|
||||
"description": "A jQuery plugin that creates hover tooltips.",
|
||||
"homepage": "http://stevenbenner.github.com/jquery-powertip/",
|
||||
"keywords": [
|
||||
"jquery-powertip"
|
||||
],
|
||||
"maintainers": [
|
||||
{
|
||||
"name": "Steven Benner"
|
||||
}
|
||||
],
|
||||
"repositories": [
|
||||
{
|
||||
"type": "git",
|
||||
"url": "https://github.com/stevenbenner/jquery-powertip"
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user