mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-03-01 02:03:22 +00:00
Bring powertip from spree_backend, including js and css
This commit is contained in:
@@ -11,6 +11,7 @@
|
||||
//= require jquery_ujs
|
||||
//= require jquery.ui.all
|
||||
//= require jquery-ui-timepicker-addon
|
||||
//= require jquery.powertip
|
||||
//= require angular
|
||||
//= require angular-resource
|
||||
//= require angular-animate
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
*
|
||||
|
||||
*= require admin/spree_backend
|
||||
*= require jquery.powertip
|
||||
|
||||
*= require jquery-ui-timepicker-addon
|
||||
*= require shared/textAngular
|
||||
@@ -13,6 +14,8 @@
|
||||
*= require_self
|
||||
*/
|
||||
|
||||
@import 'plugins/powertip';
|
||||
|
||||
@import 'hacks/mozilla';
|
||||
@import 'hacks/opera';
|
||||
@import 'hacks/ie';
|
||||
@@ -20,4 +23,5 @@
|
||||
@import 'variables';
|
||||
@import 'components/*';
|
||||
@import 'pages/*';
|
||||
|
||||
@import '*';
|
||||
|
||||
86
app/assets/stylesheets/admin/plugins/powertip.scss
Normal file
86
app/assets/stylesheets/admin/plugins/powertip.scss
Normal file
@@ -0,0 +1,86 @@
|
||||
#powerTip {
|
||||
background-color: $color-3;
|
||||
padding: 5px 15px;
|
||||
@include border-radius($border-radius);
|
||||
|
||||
&.n:before, &.ne:before, &.nw:before {
|
||||
border-top-width: 5px;
|
||||
border-top-color: $color-3;
|
||||
bottom: -5px;
|
||||
}
|
||||
|
||||
&.e:before {
|
||||
border-right-width: 5px;
|
||||
border-right-color: $color-3;
|
||||
left: -5px;
|
||||
}
|
||||
&.s:before, &.se:before, &.sw:before {
|
||||
border-bottom-width: 5px;
|
||||
border-bottom-color: $color-3;
|
||||
top: -5px;
|
||||
}
|
||||
&.w:before {
|
||||
border-left-width: 5px;
|
||||
border-left-color: $color-3;
|
||||
right: -5px;
|
||||
}
|
||||
&.ne:before, &.se:before {
|
||||
border-right-width: 5px;
|
||||
border-right-color: $color-3;
|
||||
left: -5px;
|
||||
}
|
||||
&.nw:before, &.sw:before {
|
||||
border-left-width: 5px;
|
||||
border-right-color: $color-3;
|
||||
right: -5px;
|
||||
}
|
||||
|
||||
&.clone, &.yellow, &.cancel {
|
||||
background-color: $color-notice;
|
||||
|
||||
&.n:before, &.ne:before, &.nw:before {
|
||||
border-top-color: $color-notice;
|
||||
}
|
||||
&.e:before, &.nw:before, &.sw:before {
|
||||
border-right-color: $color-notice;
|
||||
}
|
||||
&.s:before, &.se:before, &.sw:before {
|
||||
border-bottom-color: $color-notice;
|
||||
}
|
||||
&.w:before {
|
||||
border-left-color: $color-notice;
|
||||
}
|
||||
}
|
||||
&.edit, &.green, &.capture, &.save, &.add {
|
||||
background-color: $color-success;
|
||||
|
||||
&.n:before, &.ne:before, &.nw:before {
|
||||
border-top-color: $color-success;
|
||||
}
|
||||
&.e:before, &.nw:before, &.sw:before {
|
||||
border-right-color: $color-success;
|
||||
}
|
||||
&.s:before, &.se:before, &.sw:before {
|
||||
border-bottom-color: $color-success;
|
||||
}
|
||||
&.w:before {
|
||||
border-left-color: $color-success;
|
||||
}
|
||||
}
|
||||
&.remove, &.red, &.void {
|
||||
background-color: $color-error;
|
||||
|
||||
&.n:before, &.ne:before, &.nw:before {
|
||||
border-top-color: $color-error;
|
||||
}
|
||||
&.e:before, &.nw:before, &.sw:before {
|
||||
border-right-color: $color-error;
|
||||
}
|
||||
&.s:before, &.se:before, &.sw:before {
|
||||
border-bottom-color: $color-error;
|
||||
}
|
||||
&.w:before {
|
||||
border-left-color: $color-error;
|
||||
}
|
||||
}
|
||||
}
|
||||
796
vendor/assets/javascripts/jquery.powertip.js
vendored
Normal file
796
vendor/assets/javascripts/jquery.powertip.js
vendored
Normal file
@@ -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));
|
||||
85
vendor/assets/stylesheets/jquery.powertip.css
vendored
Normal file
85
vendor/assets/stylesheets/jquery.powertip.css
vendored
Normal file
@@ -0,0 +1,85 @@
|
||||
/* PowerTip Plugin */
|
||||
#powerTip {
|
||||
cursor: default;
|
||||
background-color: #333; /* fallback for browsers that dont support rgba */
|
||||
background-color: rgba(0, 0, 0, 0.8);
|
||||
border-radius: 6px;
|
||||
color: #FFF;
|
||||
display: none;
|
||||
padding: 10px;
|
||||
position: absolute;
|
||||
white-space: nowrap;
|
||||
z-index: 2147483647;
|
||||
}
|
||||
#powerTip:before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
}
|
||||
#powerTip.n:before, #powerTip.s:before {
|
||||
border-right: 5px solid transparent;
|
||||
border-left: 5px solid transparent;
|
||||
left: 50%;
|
||||
margin-left: -5px;
|
||||
}
|
||||
#powerTip.e:before, #powerTip.w:before {
|
||||
border-bottom: 5px solid transparent;
|
||||
border-top: 5px solid transparent;
|
||||
margin-top: -5px;
|
||||
top: 50%;
|
||||
}
|
||||
#powerTip.n:before {
|
||||
border-top: 10px solid rgba(0, 0, 0, 0.8);
|
||||
bottom: -10px;
|
||||
}
|
||||
#powerTip.e:before {
|
||||
border-right: 10px solid rgba(0, 0, 0, 0.8);
|
||||
left: -10px;
|
||||
}
|
||||
#powerTip.s:before {
|
||||
border-bottom: 10px solid rgba(0, 0, 0, 0.8);
|
||||
top: -10px;
|
||||
}
|
||||
#powerTip.w:before {
|
||||
border-left: 10px solid rgba(0, 0, 0, 0.8);
|
||||
right: -10px;
|
||||
}
|
||||
#powerTip.ne:before, #powerTip.se:before {
|
||||
border-right: 10px solid transparent;
|
||||
border-left: 0;
|
||||
left: 10px;
|
||||
}
|
||||
#powerTip.nw:before, #powerTip.sw:before {
|
||||
border-left: 10px solid transparent;
|
||||
border-right: 0;
|
||||
right: 10px;
|
||||
}
|
||||
#powerTip.ne:before, #powerTip.nw:before {
|
||||
border-top: 10px solid rgba(0, 0, 0, 0.8);
|
||||
bottom: -10px;
|
||||
}
|
||||
#powerTip.se:before, #powerTip.sw:before {
|
||||
border-bottom: 10px solid rgba(0, 0, 0, 0.8);
|
||||
top: -10px;
|
||||
}
|
||||
#powerTip.nw-alt:before, #powerTip.ne-alt:before,
|
||||
#powerTip.sw-alt:before, #powerTip.se-alt:before {
|
||||
border-top: 10px solid rgba(0, 0, 0, 0.8);
|
||||
bottom: -10px;
|
||||
border-left: 5px solid transparent;
|
||||
border-right: 5px solid transparent;
|
||||
left: 10px;
|
||||
}
|
||||
#powerTip.ne-alt:before {
|
||||
left: auto;
|
||||
right: 10px;
|
||||
}
|
||||
#powerTip.sw-alt:before, #powerTip.se-alt:before {
|
||||
border-top: none;
|
||||
border-bottom: 10px solid rgba(0, 0, 0, 0.8);
|
||||
bottom: auto;
|
||||
top: -10px;
|
||||
}
|
||||
#powerTip.se-alt:before {
|
||||
left: auto;
|
||||
right: 10px;
|
||||
}
|
||||
Reference in New Issue
Block a user