diff --git a/backend/app/assets/javascripts/spree/backend.js b/backend/app/assets/javascripts/spree/backend.js
index 66edd0b1b52..877d79459b6 100644
--- a/backend/app/assets/javascripts/spree/backend.js
+++ b/backend/app/assets/javascripts/spree/backend.js
@@ -1,3 +1,4 @@
+//= require solidus_admin/bind-polyfill
//= require handlebars
//= require jquery
//= require jquery_ujs
@@ -5,7 +6,6 @@
//= require jquery-ui/sortable
//= require jquery-ui/autocomplete
//= require jquery.cookie
-//= require jquery.powertip
//= require jquery.sticky-kit.min
//= require underscore-min
//= require backbone
diff --git a/backend/app/assets/javascripts/spree/backend/admin.js b/backend/app/assets/javascripts/spree/backend/admin.js
index 420e9757a9b..622b33ec820 100644
--- a/backend/app/assets/javascripts/spree/backend/admin.js
+++ b/backend/app/assets/javascripts/spree/backend/admin.js
@@ -15,36 +15,27 @@ Hopefully, this will evolve into a propper class.
**/
jQuery(function($) {
+ $('body').tooltip({selector: '.with-tip'})
- // Add some tips
- $('.with-tip').powerTip({
- smartPlacement: true,
- fadeInTime: 50,
- fadeOutTime: 50,
+ $('body').on('inserted.bs.tooltip', function(e){
+ var $target = $(e.target);
+ var $tooltip = $("#" + $target.attr("aria-describedby"));
+ $tooltip.addClass("action-" + $target.data("action"));
});
- $('body')
- .on('powerTipPreRender', '.with-tip', function() {
- $('#powerTip').addClass($(this).data('action'));
- $('#powerTip').addClass($(this).data('tip-color'));
- })
- .on('powerTipClose', '.with-tip', function() {
- $('#powerTip').removeClass($(this).data('action'));
- })
-
// Highlight hovered table column
- $('table tbody tr td.actions').find('a, button').hover(function(){
+ $('table').on("mouseenter", 'td.actions a, td.actions button', function(){
var tr = $(this).closest('tr');
var klass = 'highlight action-' + $(this).data('action')
tr.addClass(klass)
tr.prev().addClass('before-' + klass);
- }, function(){
+ });
+ $('table').on("mouseleave", 'td.actions a, td.actions button', function(){
var tr = $(this).closest('tr');
var klass = 'highlight action-' + $(this).data('action')
tr.removeClass(klass)
tr.prev().removeClass('before-' + klass);
});
-
});
diff --git a/backend/app/assets/javascripts/spree/backend/line_items_on_order_edit.js b/backend/app/assets/javascripts/spree/backend/line_items_on_order_edit.js
index a2899f9dba5..f93365e4bee 100644
--- a/backend/app/assets/javascripts/spree/backend/line_items_on_order_edit.js
+++ b/backend/app/assets/javascripts/spree/backend/line_items_on_order_edit.js
@@ -13,15 +13,6 @@ $(document).ready(function () {
$('#stock_details').show();
$('button.add_variant').click(addVariant);
-
- // Add some tips
- $('.with-tip').powerTip({
- smartPlacement: true,
- fadeInTime: 50,
- fadeOutTime: 50,
- intentPollInterval: 300
- });
-
});
});
diff --git a/backend/app/assets/javascripts/spree/backend/payments/edit.js.coffee b/backend/app/assets/javascripts/spree/backend/payments/edit.js.coffee
index f6afa991e3f..e6c5f50d1e7 100644
--- a/backend/app/assets/javascripts/spree/backend/payments/edit.js.coffee
+++ b/backend/app/assets/javascripts/spree/backend/payments/edit.js.coffee
@@ -61,10 +61,6 @@ jQuery ($) ->
$(@).data('clicked', true)
mouseup: =>
@[action]()
- .powerTip
- smartPlacement: true
- fadeInTime: 50
- fadeOutTime: 50
$buttons: ->
@$actions().find(".fa-#{@action}, .fa-cancel")
diff --git a/backend/app/assets/javascripts/spree/backend/shipments.js b/backend/app/assets/javascripts/spree/backend/shipments.js
index 6a904a12590..f27cc587260 100644
--- a/backend/app/assets/javascripts/spree/backend/shipments.js
+++ b/backend/app/assets/javascripts/spree/backend/shipments.js
@@ -13,15 +13,6 @@ $(document).ready(function () {
$('#stock_details').show();
$('button.add_variant').click(addVariantFromStockLocation);
-
- // Add some tips
- $('.with-tip').powerTip({
- smartPlacement: true,
- fadeInTime: 50,
- fadeOutTime: 50,
- intentPollInterval: 300
- });
-
});
//handle edit click
@@ -237,13 +228,6 @@ startItemSplit = function(event){
$('a.cancel-split').click(cancelItemSplit);
$('a.save-split').click(completeItemSplit);
- // Add some tips
- $('.with-tip').powerTip({
- smartPlacement: true,
- fadeInTime: 50,
- fadeOutTime: 50,
- intentPollInterval: 300
- });
$('#item_stock_location').select2({ width: 'resolve', placeholder: Spree.translations.item_stock_placeholder });
}
diff --git a/backend/app/assets/javascripts/spree/backend/spree-select2.js b/backend/app/assets/javascripts/spree/backend/spree-select2.js
index 7e6cd3b5214..23f45319983 100644
--- a/backend/app/assets/javascripts/spree/backend/spree-select2.js
+++ b/backend/app/assets/javascripts/spree/backend/spree-select2.js
@@ -11,14 +11,4 @@ jQuery(function($) {
new_taxon = taxon.text.replace('->', '')
return new_taxon;
}
-
- $("#product_taxon_ids").on({
- change: function(e){
- $('.select2-search-choice .with-tip').powerTip({
- smartPlacement: true,
- fadeInTime: 50,
- fadeOutTime: 50
- })
- }
- })
})
diff --git a/backend/app/assets/stylesheets/spree/backend.css b/backend/app/assets/stylesheets/spree/backend.css
index 83c4b02839f..39df4e55601 100644
--- a/backend/app/assets/stylesheets/spree/backend.css
+++ b/backend/app/assets/stylesheets/spree/backend.css
@@ -6,7 +6,6 @@
*= require normalize
*= require jquery-ui/datepicker
*= require jquery-ui/autocomplete
- *= require jquery.powertip
*= require select2
*= require prism
diff --git a/backend/app/assets/stylesheets/spree/backend/_bootstrap_custom.scss b/backend/app/assets/stylesheets/spree/backend/_bootstrap_custom.scss
index 3e872fa3484..d41696d9aa4 100644
--- a/backend/app/assets/stylesheets/spree/backend/_bootstrap_custom.scss
+++ b/backend/app/assets/stylesheets/spree/backend/_bootstrap_custom.scss
@@ -142,7 +142,7 @@ $grid-gutter-width: 1.875rem !default; // 30px
//
// Font, line-height, and color for body text, headings, and more.
-$font-family-sans-serif: "Helvetica Neue", Helvetica, Arial, sans-serif !default;
+$font-family-sans-serif: $base-font-family !default;
$font-family-serif: Georgia, "Times New Roman", Times, serif !default;
$font-family-monospace: Menlo, Monaco, Consolas, "Courier New", monospace !default;
$font-family-base: $font-family-sans-serif !default;
diff --git a/backend/app/assets/stylesheets/spree/backend/plugins/_bootstrap_tooltip.scss b/backend/app/assets/stylesheets/spree/backend/plugins/_bootstrap_tooltip.scss
new file mode 100644
index 00000000000..3916a852d07
--- /dev/null
+++ b/backend/app/assets/stylesheets/spree/backend/plugins/_bootstrap_tooltip.scss
@@ -0,0 +1,47 @@
+@mixin bootstrap-tooltip-color($color) {
+ .tooltip-inner{
+ background-color: $color;
+ }
+
+ &.tooltip-top,
+ &.bs-tether-element-attached-bottom {
+ .tooltip-arrow {
+ border-top-color: $color;
+ }
+ }
+
+ &.tooltip-right,
+ &.bs-tether-element-attached-left {
+ .tooltip-arrow {
+ border-right-color: $color;
+ }
+ }
+
+ &.tooltip-bottom,
+ &.bs-tether-element-attached-top {
+ .tooltip-arrow {
+ border-bottom-color: $color;
+ }
+ }
+
+ &.tooltip-left,
+ &.bs-tether-element-attached-right {
+ .tooltip-arrow {
+ border-left-color: $color;
+ }
+ }
+}
+
+.tooltip {
+ pointer-events: none;
+
+ &.action-edit, &.action-save, &.action-capture, &.action-add {
+ @include bootstrap-tooltip-color($brand-success);
+ }
+ &.action-clone {
+ @include bootstrap-tooltip-color($brand-warning);
+ }
+ &.action-void, &.action-failure, &.action-cancel, &.action-remove {
+ @include bootstrap-tooltip-color($brand-danger);
+ }
+}
diff --git a/backend/app/assets/stylesheets/spree/backend/plugins/_powertip.scss b/backend/app/assets/stylesheets/spree/backend/plugins/_powertip.scss
deleted file mode 100644
index 57ed5308610..00000000000
--- a/backend/app/assets/stylesheets/spree/backend/plugins/_powertip.scss
+++ /dev/null
@@ -1,87 +0,0 @@
-#powerTip {
- background-color: $color-3;
- padding: 5px 15px;
- border-radius: $border-radius;
- pointer-events: none;
-
- &.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;
- }
- }
-}
diff --git a/backend/app/assets/stylesheets/spree/backend/spree_admin.scss b/backend/app/assets/stylesheets/spree/backend/spree_admin.scss
index d29f190853e..813e1026dc4 100644
--- a/backend/app/assets/stylesheets/spree/backend/spree_admin.scss
+++ b/backend/app/assets/stylesheets/spree/backend/spree_admin.scss
@@ -30,8 +30,8 @@
@import 'spree/backend/components/tabs';
@import 'font-awesome';
-@import 'spree/backend/plugins/powertip';
@import 'spree/backend/plugins/select2';
+@import 'spree/backend/plugins/bootstrap_tooltip';
@import 'spree/backend/sections/adjustments_table';
@import 'spree/backend/sections/orders';
diff --git a/backend/app/views/spree/admin/style_guide/topics/messaging/_tooltips.html.erb b/backend/app/views/spree/admin/style_guide/topics/messaging/_tooltips.html.erb
index cbdb5579bad..f68a8ceb07c 100644
--- a/backend/app/views/spree/admin/style_guide/topics/messaging/_tooltips.html.erb
+++ b/backend/app/views/spree/admin/style_guide/topics/messaging/_tooltips.html.erb
@@ -3,7 +3,7 @@
<%- end %>
- We are using jQuery PowerTip
+ We are using bootstrap's tooltip library
with the class .with-tip
as a trigger.
diff --git a/backend/vendor/assets/javascripts/jquery.powertip.js b/backend/vendor/assets/javascripts/jquery.powertip.js
deleted file mode 100644
index 2fd74bcedd0..00000000000
--- a/backend/vendor/assets/javascripts/jquery.powertip.js
+++ /dev/null
@@ -1,1165 +0,0 @@
-/*!
- PowerTip - v1.2.0 - 2013-04-03
- http://stevenbenner.github.com/jquery-powertip/
- Copyright (c) 2013 Steven Benner (http://stevenbenner.com/).
- Released under MIT license.
- https://raw.github.com/stevenbenner/jquery-powertip/master/LICENSE.txt
-*/
-(function(factory) {
- if (typeof define === 'function' && define.amd) {
- // AMD. Register as an anonymous module.
- define(['jquery'], factory);
- } else {
- // Browser globals
- factory(jQuery);
- }
-}(function($) {
- // useful private variables
- var $document = $(document),
- $window = $(window),
- $body = $('body');
-
- // constants
- var DATA_DISPLAYCONTROLLER = 'displayController',
- DATA_HASACTIVEHOVER = 'hasActiveHover',
- DATA_FORCEDOPEN = 'forcedOpen',
- DATA_HASMOUSEMOVE = 'hasMouseMove',
- DATA_MOUSEONTOTIP = 'mouseOnToPopup',
- DATA_ORIGINALTITLE = 'originalTitle',
- DATA_POWERTIP = 'powertip',
- DATA_POWERTIPJQ = 'powertipjq',
- DATA_POWERTIPTARGET = 'powertiptarget',
- RAD2DEG = 180 / Math.PI;
-
- /**
- * Session data
- * Private properties global to all powerTip instances
- */
- var session = {
- isTipOpen: false,
- isFixedTipOpen: false,
- isClosing: false,
- tipOpenImminent: false,
- activeHover: null,
- currentX: 0,
- currentY: 0,
- previousX: 0,
- previousY: 0,
- desyncTimeout: null,
- mouseTrackingActive: false,
- delayInProgress: false,
- windowWidth: 0,
- windowHeight: 0,
- scrollTop: 0,
- scrollLeft: 0
- };
-
- /**
- * Collision enumeration
- * @enum {number}
- */
- var Collision = {
- none: 0,
- top: 1,
- bottom: 2,
- left: 4,
- right: 8
- };
-
- /**
- * Display hover tooltips on the matched elements.
- * @param {(Object|string)} opts The options object to use for the plugin, or
- * the name of a method to invoke on the first matched element.
- * @param {*=} [arg] Argument for an invoked method (optional).
- * @return {jQuery} jQuery object for the matched selectors.
- */
- $.fn.powerTip = function(opts, arg) {
- // don't do any work if there were no matched elements
- if (!this.length) {
- return this;
- }
-
- // handle api method calls on the plugin, e.g. powerTip('hide')
- if ($.type(opts) === 'string' && $.powerTip[opts]) {
- return $.powerTip[opts].call(this, this, arg);
- }
-
- // extend options and instantiate TooltipController
- var options = $.extend({}, $.fn.powerTip.defaults, opts),
- tipController = new TooltipController(options);
-
- // hook mouse and viewport dimension tracking
- initTracking();
-
- // setup the elements
- this.each(function elementSetup() {
- var $this = $(this),
- dataPowertip = $this.data(DATA_POWERTIP),
- dataElem = $this.data(DATA_POWERTIPJQ),
- dataTarget = $this.data(DATA_POWERTIPTARGET),
- title;
-
- // handle repeated powerTip calls on the same element by destroying the
- // original instance hooked to it and replacing it with this call
- if ($this.data(DATA_DISPLAYCONTROLLER)) {
- $.powerTip.destroy($this);
- }
-
- // 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
- title = $this.attr('title');
- if (!dataPowertip && !dataTarget && !dataElem && title) {
- $this.data(DATA_POWERTIP, title);
- $this.data(DATA_ORIGINALTITLE, title);
- $this.removeAttr('title');
- }
-
- // create hover controllers for each element
- $this.data(
- DATA_DISPLAYCONTROLLER,
- new DisplayController($this, options, tipController)
- );
- });
-
- // attach events to matched elements if the manual options is not enabled
- if (!options.manual) {
- this.on({
- // mouse events
- 'mouseenter.powertip': function elementMouseEnter(event) {
- $.powerTip.show(this, event);
- },
- 'mouseleave.powertip': function elementMouseLeave() {
- $.powerTip.hide(this);
- },
- // keyboard events
- 'focus.powertip': function elementFocus() {
- $.powerTip.show(this);
- },
- 'blur.powertip': function elementBlur() {
- $.powerTip.hide(this, true);
- },
- 'keydown.powertip': function elementKeyDown(event) {
- // close tooltip when the escape key is pressed
- if (event.keyCode === 27) {
- $.powerTip.hide(this, true);
- }
- }
- });
- }
-
- return this;
- };
-
- /**
- * Default options for the powerTip plugin.
- */
- $.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,
- manual: 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.
- */
- $.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'],
- 'nw-alt': ['nw-alt', 'n', 'ne-alt', 'sw-alt', 's', 'se-alt', 'w', 'e'],
- 'ne-alt': ['ne-alt', 'n', 'nw-alt', 'se-alt', 's', 'sw-alt', 'e', 'w'],
- 'sw-alt': ['sw-alt', 's', 'se-alt', 'nw-alt', 'n', 'ne-alt', 'w', 'e'],
- 'se-alt': ['se-alt', 's', 'sw-alt', 'ne-alt', 'n', 'nw-alt', 'e', 'w']
- };
-
- /**
- * Public API
- */
- $.powerTip = {
- /**
- * Attempts to show the tooltip for the specified element.
- * @param {jQuery|Element} element The element to open the tooltip for.
- * @param {jQuery.Event=} event jQuery event for hover intent and mouse
- * tracking (optional).
- */
- show: function apiShowTip(element, event) {
- if (event) {
- trackMouse(event);
- session.previousX = event.pageX;
- session.previousY = event.pageY;
- $(element).data(DATA_DISPLAYCONTROLLER).show();
- } else {
- $(element).first().data(DATA_DISPLAYCONTROLLER).show(true, true);
- }
- return element;
- },
-
- /**
- * Repositions the tooltip on the element.
- * @param {jQuery|Element} element The element the tooltip is shown for.
- */
- reposition: function apiResetPosition(element) {
- $(element).first().data(DATA_DISPLAYCONTROLLER).resetPosition();
- return element;
- },
-
- /**
- * Attempts to close any open tooltips.
- * @param {(jQuery|Element)=} element The element with the tooltip that
- * should be closed (optional).
- * @param {boolean=} immediate Disable close delay (optional).
- */
- hide: function apiCloseTip(element, immediate) {
- if (element) {
- $(element).first().data(DATA_DISPLAYCONTROLLER).hide(immediate);
- } else {
- if (session.activeHover) {
- session.activeHover.data(DATA_DISPLAYCONTROLLER).hide(true);
- }
- }
- return element;
- },
-
- /**
- * Destroy and roll back any powerTip() instance on the specified element.
- * @param {jQuery|Element} element The element with the powerTip instance.
- */
- destroy: function apiDestroy(element) {
- $(element).off('.powertip').each(function destroy() {
- var $this = $(this),
- dataAttributes = [
- DATA_ORIGINALTITLE,
- DATA_DISPLAYCONTROLLER,
- DATA_HASACTIVEHOVER,
- DATA_FORCEDOPEN
- ];
-
- if ($this.data(DATA_ORIGINALTITLE)) {
- $this.attr('title', $this.data(DATA_ORIGINALTITLE));
- dataAttributes.push(DATA_POWERTIP);
- }
-
- $this.removeData(dataAttributes);
- });
- return element;
- }
- };
-
- // API aliasing
- $.powerTip.showTip = $.powerTip.show;
- $.powerTip.closeTip = $.powerTip.hide;
-
- /**
- * Creates a new CSSCoordinates object.
- * @private
- * @constructor
- */
- function CSSCoordinates() {
- var me = this;
-
- // initialize object properties
- me.top = 'auto';
- me.left = 'auto';
- me.right = 'auto';
- me.bottom = 'auto';
-
- /**
- * Set a property to a value.
- * @private
- * @param {string} property The name of the property.
- * @param {number} value The value of the property.
- */
- me.set = function(property, value) {
- if ($.isNumeric(value)) {
- me[property] = Math.round(value);
- }
- };
- }
-
- /**
- * Creates a new tooltip display controller.
- * @private
- * @constructor
- * @param {jQuery} element The element that this controller will handle.
- * @param {Object} options Options object containing settings.
- * @param {TooltipController} tipController The TooltipController object 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(DATA_HASACTIVEHOVER)) {
- if (!immediate) {
- session.tipOpenImminent = true;
- hoverTimer = setTimeout(
- function intentDelay() {
- hoverTimer = null;
- checkForIntent();
- },
- options.intentPollInterval
- );
- } else {
- if (forceOpen) {
- element.data(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();
- session.tipOpenImminent = false;
- if (element.data(DATA_HASACTIVEHOVER)) {
- element.data(DATA_FORCEDOPEN, false);
- if (!disableDelay) {
- session.delayInProgress = true;
- hoverTimer = setTimeout(
- function closeDelay() {
- hoverTimer = null;
- tipController.hideTip(element);
- session.delayInProgress = false;
- },
- 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);
- session.delayInProgress = false;
- }
-
- /**
- * Repositions the tooltip on this element.
- * @private
- */
- function repositionTooltip() {
- tipController.resetPosition(element);
- }
-
- // expose the methods
- this.show = openTooltip;
- this.hide = closeTooltip;
- this.cancel = cancelTimer;
- this.resetPosition = repositionTooltip;
- }
-
- /**
- * Creates a new Placement Calculator.
- * @private
- * @constructor
- */
- function PlacementCalculator() {
- /**
- * Compute the CSS position to display a tooltip at the specified placement
- * relative to the specified element.
- * @private
- * @param {jQuery} element The element that the tooltip should target.
- * @param {string} placement The placement for the tooltip.
- * @param {number} tipWidth Width of the tooltip element in pixels.
- * @param {number} tipHeight Height of the tooltip element in pixels.
- * @param {number} offset Distance to offset tooltips in pixels.
- * @return {CSSCoordinates} A CSSCoordinates object with the position.
- */
- function computePlacementCoords(element, placement, tipWidth, tipHeight, offset) {
- var placementBase = placement.split('-')[0], // ignore 'alt' for corners
- coords = new CSSCoordinates(),
- position;
-
- if (isSvgElement(element)) {
- position = getSvgPlacement(element, placementBase);
- } else {
- position = getHtmlPlacement(element, placementBase);
- }
-
- // calculate the appropriate x and y position in the document
- switch (placement) {
- case 'n':
- coords.set('left', position.left - (tipWidth / 2));
- coords.set('bottom', session.windowHeight - position.top + offset);
- break;
- case 'e':
- coords.set('left', position.left + offset);
- coords.set('top', position.top - (tipHeight / 2));
- break;
- case 's':
- coords.set('left', position.left - (tipWidth / 2));
- coords.set('top', position.top + offset);
- break;
- case 'w':
- coords.set('top', position.top - (tipHeight / 2));
- coords.set('right', session.windowWidth - position.left + offset);
- break;
- case 'nw':
- coords.set('bottom', session.windowHeight - position.top + offset);
- coords.set('right', session.windowWidth - position.left - 20);
- break;
- case 'nw-alt':
- coords.set('left', position.left);
- coords.set('bottom', session.windowHeight - position.top + offset);
- break;
- case 'ne':
- coords.set('left', position.left - 20);
- coords.set('bottom', session.windowHeight - position.top + offset);
- break;
- case 'ne-alt':
- coords.set('bottom', session.windowHeight - position.top + offset);
- coords.set('right', session.windowWidth - position.left);
- break;
- case 'sw':
- coords.set('top', position.top + offset);
- coords.set('right', session.windowWidth - position.left - 20);
- break;
- case 'sw-alt':
- coords.set('left', position.left);
- coords.set('top', position.top + offset);
- break;
- case 'se':
- coords.set('left', position.left - 20);
- coords.set('top', position.top + offset);
- break;
- case 'se-alt':
- coords.set('top', position.top + offset);
- coords.set('right', session.windowWidth - position.left);
- break;
- }
-
- return coords;
- }
-
- /**
- * Finds the tooltip attachment point in the document for a HTML DOM element
- * for the specified placement.
- * @private
- * @param {jQuery} element The element that the tooltip should target.
- * @param {string} placement The placement for the tooltip.
- * @return {Object} An object with the top,left position values.
- */
- function getHtmlPlacement(element, placement) {
- var objectOffset = element.offset(),
- objectWidth = element.outerWidth(),
- objectHeight = element.outerHeight(),
- left,
- top;
-
- // calculate the appropriate x and y position in the document
- switch (placement) {
- case 'n':
- left = objectOffset.left + objectWidth / 2;
- top = objectOffset.top;
- break;
- case 'e':
- left = objectOffset.left + objectWidth;
- top = objectOffset.top + objectHeight / 2;
- break;
- case 's':
- left = objectOffset.left + objectWidth / 2;
- top = objectOffset.top + objectHeight;
- break;
- case 'w':
- left = objectOffset.left;
- top = objectOffset.top + objectHeight / 2;
- break;
- case 'nw':
- left = objectOffset.left;
- top = objectOffset.top;
- break;
- case 'ne':
- left = objectOffset.left + objectWidth;
- top = objectOffset.top;
- break;
- case 'sw':
- left = objectOffset.left;
- top = objectOffset.top + objectHeight;
- break;
- case 'se':
- left = objectOffset.left + objectWidth;
- top = objectOffset.top + objectHeight;
- break;
- }
-
- return {
- top: top,
- left: left
- };
- }
-
- /**
- * Finds the tooltip attachment point in the document for a SVG element for
- * the specified placement.
- * @private
- * @param {jQuery} element The element that the tooltip should target.
- * @param {string} placement The placement for the tooltip.
- * @return {Object} An object with the top,left position values.
- */
- function getSvgPlacement(element, placement) {
- var svgElement = element.closest('svg')[0],
- domElement = element[0],
- point = svgElement.createSVGPoint(),
- boundingBox = domElement.getBBox(),
- matrix = domElement.getScreenCTM(),
- halfWidth = boundingBox.width / 2,
- halfHeight = boundingBox.height / 2,
- placements = [],
- placementKeys = ['nw', 'n', 'ne', 'e', 'se', 's', 'sw', 'w'],
- coords,
- rotation,
- steps,
- x;
-
- function pushPlacement() {
- placements.push(point.matrixTransform(matrix));
- }
-
- // get bounding box corners and midpoints
- point.x = boundingBox.x;
- point.y = boundingBox.y;
- pushPlacement();
- point.x += halfWidth;
- pushPlacement();
- point.x += halfWidth;
- pushPlacement();
- point.y += halfHeight;
- pushPlacement();
- point.y += halfHeight;
- pushPlacement();
- point.x -= halfWidth;
- pushPlacement();
- point.x -= halfWidth;
- pushPlacement();
- point.y -= halfHeight;
- pushPlacement();
-
- // determine rotation
- if (placements[0].y !== placements[1].y || placements[0].x !== placements[7].x) {
- rotation = Math.atan2(matrix.b, matrix.a) * RAD2DEG;
- steps = Math.ceil(((rotation % 360) - 22.5) / 45);
- if (steps < 1) {
- steps += 8;
- }
- while (steps--) {
- placementKeys.push(placementKeys.shift());
- }
- }
-
- // find placement
- for (x = 0; x < placements.length; x++) {
- if (placementKeys[x] === placement) {
- coords = placements[x];
- break;
- }
- }
-
- return {
- top: coords.y + session.scrollTop,
- left: coords.x + session.scrollLeft
- };
- }
-
- // expose methods
- this.compute = computePlacementCoords;
- }
-
- /**
- * Creates a new tooltip controller.
- * @private
- * @constructor
- * @param {Object} options Options object containing settings.
- */
- function TooltipController(options) {
- var placementCalculator = new PlacementCalculator(),
- tipElement = $('#' + options.popupId);
-
- // build and append tooltip div if it does not already exist
- if (tipElement.length === 0) {
- tipElement = $('', { id: options.popupId });
- // grab body element if it was not populated when the script loaded
- // note: 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 tooltip element, please
- if (!tipElement.data(DATA_HASMOUSEMOVE)) {
- $document.on('mousemove', positionTipOnCursor);
- $window.on('scroll', positionTipOnCursor);
- tipElement.data(DATA_HASMOUSEMOVE, true);
- }
- }
-
- // if we want to be able to mouse onto the tooltip then we need to attach
- // hover events to the tooltip that will cancel a close request on hover and
- // start a new close request on mouseleave
- if (options.mouseOnToPopup) {
- tipElement.on({
- mouseenter: function tipMouseEnter() {
- // we only let the mouse stay on the tooltip if it is set to let
- // users interact with it
- if (tipElement.data(DATA_MOUSEONTOTIP)) {
- // check activeHover in case the mouse cursor entered the
- // tooltip during the fadeOut and close cycle
- if (session.activeHover) {
- session.activeHover.data(DATA_DISPLAYCONTROLLER).cancel();
- }
- }
- },
- mouseleave: function tipMouseLeave() {
- // check activeHover in case the mouse cursor entered the
- // tooltip during the fadeOut and close cycle
- if (session.activeHover) {
- session.activeHover.data(DATA_DISPLAYCONTROLLER).hide();
- }
- }
- });
- }
-
- /**
- * Gives the specified element the active-hover state and queues up the
- * showTip function.
- * @private
- * @param {jQuery} element The element that the tooltip should target.
- */
- function beginShowTip(element) {
- element.data(DATA_HASACTIVEHOVER, true);
- // show tooltip, asap
- tipElement.queue(function queueTipInit(next) {
- showTip(element);
- next();
- });
- }
-
- /**
- * Shows the tooltip, as soon as possible.
- * @private
- * @param {jQuery} element The element that the tooltip should target.
- */
- function showTip(element) {
- var tipContent;
-
- // 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(DATA_HASACTIVEHOVER)) {
- return;
- }
-
- // if the tooltip 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.isTipOpen) {
- if (!session.isClosing) {
- hideTip(session.activeHover);
- }
- tipElement.delay(100).queue(function queueTipAgain(next) {
- showTip(element);
- next();
- });
- return;
- }
-
- // trigger powerTipPreRender event
- element.trigger('powerTipPreRender');
-
- // set tooltip content
- tipContent = getTooltipContent(element);
- if (tipContent) {
- tipElement.empty().append(tipContent);
- } else {
- // we have no content to display, give up
- return;
- }
-
- // trigger powerTipRender event
- element.trigger('powerTipRender');
-
- session.activeHover = element;
- session.isTipOpen = true;
-
- tipElement.data(DATA_MOUSEONTOTIP, options.mouseOnToPopup);
-
- // set tooltip position
- if (!options.followMouse) {
- positionTipOnElement(element);
- session.isFixedTipOpen = true;
- } else {
- positionTipOnCursor();
- }
-
- // fadein
- tipElement.fadeIn(options.fadeInTime, function fadeInCallback() {
- // start desync polling
- if (!session.desyncTimeout) {
- session.desyncTimeout = setInterval(closeDesyncedTip, 500);
- }
-
- // trigger powerTipOpen event
- element.trigger('powerTipOpen');
- });
- }
-
- /**
- * Hides the tooltip.
- * @private
- * @param {jQuery} element The element that the tooltip should target.
- */
- function hideTip(element) {
- // reset session
- session.isClosing = true;
- session.activeHover = null;
- session.isTipOpen = false;
-
- // stop desync polling
- session.desyncTimeout = clearInterval(session.desyncTimeout);
-
- // reset element state
- element.data(DATA_HASACTIVEHOVER, false);
- element.data(DATA_FORCEDOPEN, false);
-
- // fade out
- tipElement.fadeOut(options.fadeOutTime, function fadeOutCallback() {
- var coords = new CSSCoordinates();
-
- // reset session and tooltip element
- session.isClosing = false;
- session.isFixedTipOpen = false;
- tipElement.removeClass();
-
- // support mouse-follow and fixed position tips at the same time by
- // moving the tooltip to the last cursor location after it is hidden
- coords.set('top', session.currentY + options.offset);
- coords.set('left', session.currentX + options.offset);
- tipElement.css(coords);
-
- // trigger powerTipClose event
- element.trigger('powerTipClose');
- });
- }
-
- /**
- * Moves the tooltip to the users mouse cursor.
- * @private
- */
- function positionTipOnCursor() {
- // to support having fixed tooltips on the same page as cursor tooltips,
- // where both instances are referencing the same tooltip element, we
- // need to keep track of the mouse position constantly, but we should
- // only set the tip location if a fixed tip is not currently open, a tip
- // open is imminent or active, and the tooltip element in question does
- // have a mouse-follow using it.
- if (!session.isFixedTipOpen && (session.isTipOpen || (session.tipOpenImminent && tipElement.data(DATA_HASMOUSEMOVE)))) {
- // grab measurements
- var tipWidth = tipElement.outerWidth(),
- tipHeight = tipElement.outerHeight(),
- coords = new CSSCoordinates(),
- collisions,
- collisionCount;
-
- // grab collisions
- coords.set('top', session.currentY + options.offset);
- coords.set('left', session.currentX + options.offset);
- collisions = getViewportCollisions(
- coords,
- tipWidth,
- tipHeight
- );
-
- // handle tooltip view port collisions
- if (collisions !== Collision.none) {
- collisionCount = countFlags(collisions);
- if (collisionCount === 1) {
- // if there is only one collision (bottom or right) then
- // simply constrain the tooltip to the view port
- if (collisions === Collision.right) {
- coords.set('left', session.windowWidth - tipWidth);
- } else if (collisions === Collision.bottom) {
- coords.set('top', session.scrollTop + session.windowHeight - tipHeight);
- }
- } else {
- // if the tooltip has more than one collision then it is
- // trapped in the corner and should be flipped to get it out
- // of the users way
- coords.set('left', session.currentX - tipWidth - options.offset);
- coords.set('top', session.currentY - tipHeight - options.offset);
- }
- }
-
- // position the tooltip
- tipElement.css(coords);
- }
- }
-
- /**
- * Sets the tooltip to the correct position relative to the specified target
- * element. Based on options settings.
- * @private
- * @param {jQuery} element The element that the tooltip should target.
- */
- function positionTipOnElement(element) {
- var priorityList,
- finalPlacement;
-
- if (options.smartPlacement) {
- priorityList = $.fn.powerTip.smartPlacementLists[options.placement];
-
- // iterate over the priority list and use the first placement option
- // that does not collide with the view port. if they all collide
- // then the last placement in the list will be used.
- $.each(priorityList, function(idx, pos) {
- // place tooltip and find collisions
- var collisions = getViewportCollisions(
- placeTooltip(element, pos),
- tipElement.outerWidth(),
- tipElement.outerHeight()
- );
-
- // update the final placement variable
- finalPlacement = pos;
-
- // break if there were no collisions
- if (collisions === Collision.none) {
- return false;
- }
- });
- } else {
- // if we're not going to use the smart placement feature then just
- // compute the coordinates and do it
- placeTooltip(element, options.placement);
- finalPlacement = options.placement;
- }
-
- // add placement as class for CSS arrows
- tipElement.addClass(finalPlacement);
- }
-
- /**
- * Sets the tooltip position to the appropriate values to show the tip at
- * the specified placement. This function will iterate and test the tooltip
- * to support elastic tooltips.
- * @private
- * @param {jQuery} element The element that the tooltip should target.
- * @param {string} placement The placement for the tooltip.
- * @return {CSSCoordinates} A CSSCoordinates object with the top, left, and
- * right position values.
- */
- function placeTooltip(element, placement) {
- var iterationCount = 0,
- tipWidth,
- tipHeight,
- coords = new CSSCoordinates();
-
- // set the tip to 0,0 to get the full expanded width
- coords.set('top', 0);
- coords.set('left', 0);
- tipElement.css(coords);
-
- // to support elastic tooltips we need to check for a change in the
- // rendered dimensions after the tooltip has been positioned
- do {
- // grab the current tip dimensions
- tipWidth = tipElement.outerWidth();
- tipHeight = tipElement.outerHeight();
-
- // get placement coordinates
- coords = placementCalculator.compute(
- element,
- placement,
- tipWidth,
- tipHeight,
- options.offset
- );
-
- // place the tooltip
- tipElement.css(coords);
- } while (
- // sanity check: limit to 5 iterations, and...
- ++iterationCount <= 5 &&
- // try again if the dimensions changed after placement
- (tipWidth !== tipElement.outerWidth() || tipHeight !== tipElement.outerHeight())
- );
-
- return coords;
- }
-
- /**
- * Checks for a tooltip desync and closes the tooltip if one occurs.
- * @private
- */
- function closeDesyncedTip() {
- var isDesynced = false;
- // It is possible for the mouse cursor to leave an element without
- // firing the mouseleave or blur event. This most commonly happens when
- // the element is disabled under mouse cursor. If this happens it will
- // result in a desynced tooltip because the tooltip was never asked to
- // close. So we should periodically check for a desync situation and
- // close the tip if such a situation arises.
- if (session.isTipOpen && !session.isClosing && !session.delayInProgress) {
- // user moused onto another tip or active hover is disabled
- if (session.activeHover.data(DATA_HASACTIVEHOVER) === false || session.activeHover.is(':disabled')) {
- isDesynced = true;
- } else {
- // 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(DATA_FORCEDOPEN)) {
- if (tipElement.data(DATA_MOUSEONTOTIP)) {
- if (!isMouseOver(tipElement)) {
- isDesynced = true;
- }
- } else {
- isDesynced = true;
- }
- }
- }
-
- if (isDesynced) {
- // close the desynced tip
- hideTip(session.activeHover);
- }
- }
- }
-
- // expose methods
- this.showTip = beginShowTip;
- this.hideTip = hideTip;
- this.resetPosition = positionTipOnElement;
- }
-
- /**
- * Determine whether a jQuery object is an SVG element
- * @private
- * @param {jQuery} element The element to check
- * @return {boolean} Whether this is an SVG element
- */
- function isSvgElement(element) {
- return window.SVGElement && element[0] instanceof SVGElement;
- }
-
- /**
- * Initializes the viewport dimension cache and hooks up the mouse position
- * tracking and viewport dimension tracking events.
- * Prevents attaching the events more than once.
- * @private
- */
- function initTracking() {
- if (!session.mouseTrackingActive) {
- session.mouseTrackingActive = true;
-
- // grab the current viewport dimensions on load
- $(function getViewportDimensions() {
- session.scrollLeft = $window.scrollLeft();
- session.scrollTop = $window.scrollTop();
- session.windowWidth = $window.width();
- session.windowHeight = $window.height();
- });
-
- // hook mouse move tracking
- $document.on('mousemove', trackMouse);
-
- // hook viewport dimensions tracking
- $window.on({
- resize: function trackResize() {
- session.windowWidth = $window.width();
- session.windowHeight = $window.height();
- },
- scroll: function trackScroll() {
- var x = $window.scrollLeft(),
- y = $window.scrollTop();
- if (x !== session.scrollLeft) {
- session.currentX += x - session.scrollLeft;
- session.scrollLeft = x;
- }
- if (y !== session.scrollTop) {
- session.currentY += y - session.scrollTop;
- session.scrollTop = y;
- }
- }
- });
- }
- }
-
- /**
- * Saves the current mouse coordinates to the session object.
- * @private
- * @param {jQuery.Event} 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 {jQuery} element The element to check for hover.
- * @return {boolean}
- */
- function isMouseOver(element) {
- // use getBoundingClientRect() because jQuery's width() and height()
- // methods do not work with SVG elements
- // compute width/height because those properties do not exist on the object
- // returned by getBoundingClientRect() in older versions of IE
- var elementPosition = element.offset(),
- elementBox = element[0].getBoundingClientRect(),
- elementWidth = elementBox.right - elementBox.left,
- elementHeight = elementBox.bottom - elementBox.top;
-
- return session.currentX >= elementPosition.left &&
- session.currentX <= elementPosition.left + elementWidth &&
- session.currentY >= elementPosition.top &&
- session.currentY <= elementPosition.top + elementHeight;
- }
-
- /**
- * Fetches the tooltip content from the specified element's data attributes.
- * @private
- * @param {jQuery} element The element to get the tooltip content for.
- * @return {(string|jQuery|undefined)} The text/HTML string, jQuery object, or
- * undefined if there was no tooltip content for the element.
- */
- function getTooltipContent(element) {
- var tipText = element.data(DATA_POWERTIP),
- tipObject = element.data(DATA_POWERTIPJQ),
- tipTarget = element.data(DATA_POWERTIPTARGET),
- targetElement,
- content;
-
- if (tipText) {
- if ($.isFunction(tipText)) {
- tipText = tipText.call(element[0]);
- }
- content = tipText;
- } else if (tipObject) {
- if ($.isFunction(tipObject)) {
- tipObject = tipObject.call(element[0]);
- }
- if (tipObject.length > 0) {
- content = tipObject.clone(true, true);
- }
- } else if (tipTarget) {
- targetElement = $('#' + tipTarget);
- if (targetElement.length > 0) {
- content = targetElement.html();
- }
- }
-
- return content;
- }
-
- /**
- * Finds any viewport collisions that an element (the tooltip) would have if it
- * were absolutely positioned at the specified coordinates.
- * @private
- * @param {CSSCoordinates} coords Coordinates for the element.
- * @param {number} elementWidth Width of the element in pixels.
- * @param {number} elementHeight Height of the element in pixels.
- * @return {number} Value with the collision flags.
- */
- function getViewportCollisions(coords, elementWidth, elementHeight) {
- var viewportTop = session.scrollTop,
- viewportLeft = session.scrollLeft,
- viewportBottom = viewportTop + session.windowHeight,
- viewportRight = viewportLeft + session.windowWidth,
- collisions = Collision.none;
-
- if (coords.top < viewportTop || Math.abs(coords.bottom - session.windowHeight) - elementHeight < viewportTop) {
- collisions |= Collision.top;
- }
- if (coords.top + elementHeight > viewportBottom || Math.abs(coords.bottom - session.windowHeight) > viewportBottom) {
- collisions |= Collision.bottom;
- }
- if (coords.left < viewportLeft || coords.right + elementWidth > viewportRight) {
- collisions |= Collision.left;
- }
- if (coords.left + elementWidth > viewportRight || coords.right < viewportLeft) {
- collisions |= Collision.right;
- }
-
- return collisions;
- }
-
- /**
- * Counts the number of bits set on a flags value.
- * @param {number} value The flags value.
- * @return {number} The number of bits that have been set.
- */
- function countFlags(value) {
- var count = 0;
- while (value) {
- value &= value - 1;
- count++;
- }
- return count;
- }
-
-}));
diff --git a/backend/vendor/assets/javascripts/solidus_admin/bind-polyfill.js b/backend/vendor/assets/javascripts/solidus_admin/bind-polyfill.js
new file mode 100644
index 00000000000..6dae783dc05
--- /dev/null
+++ b/backend/vendor/assets/javascripts/solidus_admin/bind-polyfill.js
@@ -0,0 +1,28 @@
+// Taken from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind#Polyfill
+if (!Function.prototype.bind) {
+ Function.prototype.bind = function(oThis) {
+ if (typeof this !== 'function') {
+ // closest thing possible to the ECMAScript 5
+ // internal IsCallable function
+ throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
+ }
+
+ var aArgs = Array.prototype.slice.call(arguments, 1),
+ fToBind = this,
+ fNOP = function() {},
+ fBound = function() {
+ return fToBind.apply(this instanceof fNOP
+ ? this
+ : oThis,
+ aArgs.concat(Array.prototype.slice.call(arguments)));
+ };
+
+ if (this.prototype) {
+ // Function.prototype doesn't have a prototype property
+ fNOP.prototype = this.prototype;
+ }
+ fBound.prototype = new fNOP();
+
+ return fBound;
+ };
+}
diff --git a/backend/vendor/assets/stylesheets/jquery.powertip.css b/backend/vendor/assets/stylesheets/jquery.powertip.css
deleted file mode 100644
index 42721d60366..00000000000
--- a/backend/vendor/assets/stylesheets/jquery.powertip.css
+++ /dev/null
@@ -1,85 +0,0 @@
-/* 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;
-}