Changeset View
Changeset View
Standalone View
Standalone View
webroot/rsrc/js/core/ToolTip.js
Show First 20 Lines • Show All 59 Lines • ▼ Show 20 Lines | show : function(root, scale, align, content) { | ||||
// Jump through some hoops trying to auto-position the tooltip | // Jump through some hoops trying to auto-position the tooltip | ||||
var pos = self._getSmartPosition(align, root, node); | var pos = self._getSmartPosition(align, root, node); | ||||
pos.setPos(node); | pos.setPos(node); | ||||
}, | }, | ||||
_getSmartPosition: function (align, root, node) { | _getSmartPosition: function (align, root, node) { | ||||
var self = JX.Tooltip; | var self = JX.Tooltip; | ||||
var pos = self._proposePosition(align, root, node); | |||||
// If toolip is offscreen, try to be clever | // Figure out how to position the tooltip on screen. We will try the | ||||
if (!JX.Tooltip.isOnScreen(pos, node)) { | // configured aligment first. | ||||
align = self._getImprovedOrientation(pos, node); | var try_alignments = [align]; | ||||
pos = self._proposePosition(align, root, node); | |||||
// If the configured alignment does not fit, we'll try the opposite | |||||
// alignment. | |||||
var opposites = { | |||||
N: 'S', | |||||
S: 'N', | |||||
E: 'W', | |||||
W: 'E' | |||||
}; | |||||
try_alignments.push(opposites[align]); | |||||
// Then we'll try the other alignments, in arbitrary order. | |||||
for (var k in opposites) { | |||||
try_alignments.push(k); | |||||
} | |||||
var use_alignment = null; | |||||
var use_pos = null; | |||||
for (var ii = 0; ii < try_alignments.length; ii++) { | |||||
var try_alignment = try_alignments[ii]; | |||||
var pos = self._proposePosition(try_alignment, root, node); | |||||
if (self.isOnScreen(pos, node)) { | |||||
use_alignment = try_alignment; | |||||
use_pos = pos; | |||||
break; | |||||
} | |||||
} | |||||
// If we don't come up with a good answer, default to the configured | |||||
// alignment. | |||||
if (use_alignment === null) { | |||||
use_alignment = align; | |||||
use_pos = self._proposePosition(use_alignment, root, node); | |||||
} | } | ||||
self._setAnchor(align); | self._setAnchor(use_alignment); | ||||
return pos; | return pos; | ||||
}, | }, | ||||
_proposePosition: function (align, root, node) { | _proposePosition: function (align, root, node) { | ||||
var p = JX.$V(root); | var p = JX.$V(root); | ||||
var d = JX.Vector.getDim(root); | var d = JX.Vector.getDim(root); | ||||
var n = JX.Vector.getDim(node); | var n = JX.Vector.getDim(node); | ||||
var l = 0; | var l = 0; | ||||
Show All 18 Lines | _proposePosition: function (align, root, node) { | ||||
t = parseInt(p.y - ((n.y - d.y) / 2), 10); | t = parseInt(p.y - ((n.y - d.y) / 2), 10); | ||||
break; | break; | ||||
} | } | ||||
return new JX.Vector(l, t); | return new JX.Vector(l, t); | ||||
}, | }, | ||||
isOnScreen: function (a, node) { | isOnScreen: function (a, node) { | ||||
var s = JX.Vector.getScroll(); | var view = this._getViewBoundaries(); | ||||
var v = JX.Vector.getViewport(); | |||||
var max_x = s.x + v.x; | |||||
var max_y = s.y + v.y; | |||||
var corners = this._getNodeCornerPositions(a, node); | var corners = this._getNodeCornerPositions(a, node); | ||||
// Check if any of the corners are offscreen | // Check if any of the corners are offscreen. | ||||
for (var i = 0; i < corners.length; i++) { | for (var i = 0; i < corners.length; i++) { | ||||
var corner = corners[i]; | var corner = corners[i]; | ||||
if (corner.x < s.x || | if (corner.x < view.w || | ||||
corner.y < s.y || | corner.y < view.n || | ||||
corner.x > max_x || | corner.x > view.e || | ||||
corner.y > max_y) { | corner.y > view.s) { | ||||
return false; | return false; | ||||
} | } | ||||
} | } | ||||
return true; | return true; | ||||
}, | }, | ||||
_getImprovedOrientation: function (a, node) { | |||||
// Try to predict the "more correct" orientation | |||||
var s = JX.Vector.getScroll(); | |||||
var v = JX.Vector.getViewport(); | |||||
var max_x = s.x + v.x; | |||||
var max_y = s.y + v.y; | |||||
var corners = this._getNodeCornerPositions(a, node); | |||||
for (var i = 0; i < corners.length; i++) { | |||||
var corner = corners[i]; | |||||
if (corner.y < v.y) { | |||||
return 'S'; | |||||
} else | |||||
if (corner.x < v.x) { | |||||
return 'E'; | |||||
} else | |||||
if (corner.y > max_y) { | |||||
return 'N'; | |||||
} else | |||||
if (corner.x > max_x) { | |||||
return 'W'; | |||||
} else { | |||||
return 'N'; | |||||
} | |||||
} | |||||
}, | |||||
_getNodeCornerPositions: function(pos, node) { | _getNodeCornerPositions: function(pos, node) { | ||||
// Get positions of all four corners of a node | // Get positions of all four corners of a node. | ||||
var n = JX.Vector.getDim(node); | var n = JX.Vector.getDim(node); | ||||
return [new JX.Vector(pos.x, pos.y), | return [new JX.Vector(pos.x, pos.y), | ||||
new JX.Vector(pos.x + n.x, pos.y), | new JX.Vector(pos.x + n.x, pos.y), | ||||
new JX.Vector(pos.x, pos.y + n.y), | new JX.Vector(pos.x, pos.y + n.y), | ||||
new JX.Vector(pos.x + n.x, pos.y + n.y)]; | new JX.Vector(pos.x + n.x, pos.y + n.y)]; | ||||
}, | }, | ||||
_getViewBoundaries: function() { | |||||
var s = JX.Vector.getScroll(); | |||||
var v = JX.Vector.getViewport(); | |||||
var max_x = s.x + v.x; | |||||
var max_y = s.y + v.y; | |||||
// Even if the corner is technically on the screen, don't allow the | |||||
// tip to display too close to the edge of the screen. | |||||
var margin = 16; | |||||
return { | |||||
w: s.x + margin, | |||||
e: max_x - margin, | |||||
n: s.y + margin, | |||||
s: max_y - margin | |||||
}; | |||||
}, | |||||
_setAnchor: function (align) { | _setAnchor: function (align) { | ||||
// Orient the little tail | // Orient the little tail | ||||
JX.DOM.alterClass(this._node, 'jx-tooltip-align-' + align, true); | JX.DOM.alterClass(this._node, 'jx-tooltip-align-' + align, true); | ||||
}, | }, | ||||
hide : function() { | hide : function() { | ||||
if (this._node) { | if (this._node) { | ||||
JX.DOM.remove(this._node); | JX.DOM.remove(this._node); | ||||
Show All 16 Lines |