diff --git a/src/core.js b/src/core.js index 19cf95fa..fa763374 100644 --- a/src/core.js +++ b/src/core.js @@ -179,6 +179,8 @@ $.fn.powerTip.defaults = { placement: 'n', smartPlacement: false, offset: 10, + inset: 20, + fromCenter: 1.0, mouseOnToPopup: false, manual: false, openEvents: [ 'mouseenter', 'focus' ], diff --git a/src/csscoordinates.js b/src/csscoordinates.js index fd793078..c407f58a 100644 --- a/src/csscoordinates.js +++ b/src/csscoordinates.js @@ -32,4 +32,13 @@ function CSSCoordinates() { me[property] = Math.round(value); } }; + + /** + * Check if a property is set + * @private + * @param {string} property The name of the property. + */ + me.isSet = function(property) { + return $.isNumeric(me[property]); + }; } diff --git a/src/placementcalculator.js b/src/placementcalculator.js index a2ce37d7..bdeab656 100644 --- a/src/placementcalculator.js +++ b/src/placementcalculator.js @@ -21,18 +21,23 @@ function PlacementCalculator() { * @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. + * @param {number} options.offset Distance to offset tooltips in pixels. + * @param {number} options.fromCenter Distance along line from center (0) to boundary (1). + * @param {number} options.inset Distance from corner of tooltip to object for ne, nw, se, sw. * @return {CSSCoordinates} A CSSCoordinates object with the position. */ - function computePlacementCoords(element, placement, tipWidth, tipHeight, offset) { + function computePlacementCoords(element, placement, tipWidth, tipHeight, options ) { var placementBase = placement.split('-')[0], // ignore 'alt' for corners + offset = options.offset, + fromCenter = options.fromCenter, + inset = options.inset, coords = new CSSCoordinates(), position; if (isSvgElement(element)) { - position = getSvgPlacement(element, placementBase); + position = getSvgPlacement(element, placementBase, fromCenter); } else { - position = getHtmlPlacement(element, placementBase); + position = getHtmlPlacement(element, placementBase, fromCenter); } // calculate the appropriate x and y position in the document @@ -55,14 +60,14 @@ function PlacementCalculator() { break; case 'nw': coords.set('bottom', session.windowHeight - position.top + offset); - coords.set('right', session.windowWidth - position.left - 20); + coords.set('right', session.windowWidth - position.left - inset); 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('left', position.left - inset); coords.set('bottom', session.windowHeight - position.top + offset); break; case 'ne-alt': @@ -71,14 +76,14 @@ function PlacementCalculator() { break; case 'sw': coords.set('top', position.top + offset); - coords.set('right', session.windowWidth - position.left - 20); + coords.set('right', session.windowWidth - position.left - inset); 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('left', position.left - inset); coords.set('top', position.top + offset); break; case 'se-alt': @@ -90,20 +95,36 @@ function PlacementCalculator() { return coords; } + /** + * Finds the weighted average of two values + * @private + * @param {number} a the first value (returned if weight=0) + * @param {number} b the second value (returned if weight=1) + * @param {number} weight the weight (0 <= weight <= 1) + */ + function weightedAvg(a, b, weight) { + return Math.round( b * weight + a * (1.0 - weight) ); + } + /** * 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. + * @param {number} fromCenter The relative distance between center and boundary * @return {Object} An object with the top,left position values. */ - function getHtmlPlacement(element, placement) { + function getHtmlPlacement(element, placement, fromCenter) { var objectOffset = element.offset(), objectWidth = element.outerWidth(), objectHeight = element.outerHeight(), left, - top; + top, + objectCenter = { + top: objectOffset.top + objectHeight / 2, + left: objectOffset.left + objectWidth / 2 + }; // calculate the appropriate x and y position in the document switch (placement) { @@ -142,8 +163,8 @@ function PlacementCalculator() { } return { - top: top, - left: left + top: weightedAvg(objectCenter.top, top, fromCenter), + left: weightedAvg(objectCenter.left, left, fromCenter) }; } @@ -153,9 +174,10 @@ function PlacementCalculator() { * @private * @param {jQuery} element The element that the tooltip should target. * @param {string} placement The placement for the tooltip. + * @param {number} fromCenter The relative distance between center and boundary * @return {Object} An object with the top,left position values. */ - function getSvgPlacement(element, placement) { + function getSvgPlacement(element, placement, fromCenter) { var svgElement = element.closest('svg')[0], domElement = element[0], point = svgElement.createSVGPoint(), @@ -166,6 +188,7 @@ function PlacementCalculator() { placements = [], placementKeys = ['nw', 'n', 'ne', 'e', 'se', 's', 'sw', 'w'], coords, + center = svgElement.createSVGPoint(), rotation, steps, x; @@ -174,9 +197,12 @@ function PlacementCalculator() { placements.push(point.matrixTransform(matrix)); } - // get bounding box corners and midpoints + // Get bounding box corners and midpoints point.x = boundingBox.x; point.y = boundingBox.y; + center.x = point.x + halfWidth; + center.y = point.y + halfHeight; + center = center.matrixTransform(matrix); pushPlacement(); point.x += halfWidth; pushPlacement(); @@ -197,6 +223,7 @@ function PlacementCalculator() { 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); + center = center.matrixTransform(matrix); if (steps < 1) { steps += 8; } @@ -214,8 +241,8 @@ function PlacementCalculator() { } return { - top: coords.y + session.scrollTop, - left: coords.x + session.scrollLeft + top: weightedAvg(center.y, coords.y, fromCenter) + session.scrollTop, + left: weightedAvg(center.x, coords.x, fromCenter) + session.scrollLeft }; } diff --git a/src/tooltipcontroller.js b/src/tooltipcontroller.js index 80ae643b..19e45d3f 100644 --- a/src/tooltipcontroller.js +++ b/src/tooltipcontroller.js @@ -339,7 +339,7 @@ function TooltipController(options) { placement, tipWidth, tipHeight, - options.offset + options ); // place the tooltip