// Tip coordinates calculator
function calculateTip(corner, width, height)
{
var width2 = Math.ceil(width / 2), height2 = Math.ceil(height / 2),
// Define tip coordinates in terms of height and width values
tips = {
bottomright: [[0,0], [width,height], [width,0]],
bottomleft: [[0,0], [width,0], [0,height]],
topright: [[0,height], [width,0], [width,height]],
topleft: [[0,0], [0,height], [width,height]],
topcenter: [[0,height], [width2,0], [width,height]],
bottomcenter: [[0,0], [width,0], [width2,height]],
rightcenter: [[0,0], [width,height2], [0,height]],
leftcenter: [[width,0], [width,height], [0,height2]]
};
// Set common side shapes
tips.lefttop = tips.bottomright; tips.righttop = tips.bottomleft;
tips.leftbottom = tips.topright; tips.rightbottom = tips.topleft;
return tips[ corner.string() ];
}
function Tip(qTip, command)
{
var self = this,
opts = qTip.options.style.tip,
elems = qTip.elements,
tooltip = elems.tooltip,
cache = { top: 0, left: 0 },
size = {
width: opts.width,
height: opts.height
},
color = { },
border = opts.border || 0,
namespace = '.qtip-tip',
hasCanvas = !!($('')[0] || {}).getContext,
tiphtml;
self.corner = NULL;
self.mimic = NULL;
self.border = border;
self.offset = opts.offset;
self.size = size;
// Add new option checks for the plugin
qTip.checks.tip = {
'^position.my|style.tip.(corner|mimic|border)$': function() {
// Make sure a tip can be drawn
if(!self.init()) {
self.destroy();
}
// Reposition the tooltip
qTip.reposition();
},
'^style.tip.(height|width)$': function() {
// Re-set dimensions and redraw the tip
size = {
width: opts.width,
height: opts.height
};
self.create();
self.update();
// Reposition the tooltip
qTip.reposition();
},
'^content.title.text|style.(classes|widget)$': function() {
if(elems.tip && elems.tip.length) {
self.update();
}
}
};
function whileVisible(callback) {
var visible = tooltip.is(':visible');
tooltip.show(); callback(); tooltip.toggle(visible);
}
function swapDimensions() {
size.width = opts.height;
size.height = opts.width;
}
function resetDimensions() {
size.width = opts.width;
size.height = opts.height;
}
function reposition(event, api, pos, viewport) {
if(!elems.tip) { return; }
var newCorner = self.corner.clone(),
adjust = pos.adjusted,
method = qTip.options.position.adjust.method.split(' '),
horizontal = method[0],
vertical = method[1] || method[0],
shift = { left: FALSE, top: FALSE, x: 0, y: 0 },
offset, css = {}, props;
// If our tip position isn't fixed e.g. doesn't adjust with viewport...
if(self.corner.fixed !== TRUE) {
// Horizontal - Shift or flip method
if(horizontal === SHIFT && newCorner.precedance === X && adjust.left && newCorner.y !== CENTER) {
newCorner.precedance = newCorner.precedance === X ? Y : X;
}
else if(horizontal !== SHIFT && adjust.left){
newCorner.x = newCorner.x === CENTER ? (adjust.left > 0 ? LEFT : RIGHT) : (newCorner.x === LEFT ? RIGHT : LEFT);
}
// Vertical - Shift or flip method
if(vertical === SHIFT && newCorner.precedance === Y && adjust.top && newCorner.x !== CENTER) {
newCorner.precedance = newCorner.precedance === Y ? X : Y;
}
else if(vertical !== SHIFT && adjust.top) {
newCorner.y = newCorner.y === CENTER ? (adjust.top > 0 ? TOP : BOTTOM) : (newCorner.y === TOP ? BOTTOM : TOP);
}
// Update and redraw the tip if needed (check cached details of last drawn tip)
if(newCorner.string() !== cache.corner.string() && (cache.top !== adjust.top || cache.left !== adjust.left)) {
self.update(newCorner, FALSE);
}
}
// Setup tip offset properties
offset = self.position(newCorner, adjust);
offset[ newCorner.x ] += parseWidth(newCorner, newCorner.x);
offset[ newCorner.y ] += parseWidth(newCorner, newCorner.y);
// Readjust offset object to make it left/top
if(offset.right !== undefined) { offset.left = -offset.right; }
if(offset.bottom !== undefined) { offset.top = -offset.bottom; }
offset.user = Math.max(0, opts.offset);
// Viewport "shift" specific adjustments
if(shift.left = (horizontal === SHIFT && !!adjust.left)) {
if(newCorner.x === CENTER) {
css['margin-left'] = shift.x = offset['margin-left'] - adjust.left;
}
else {
props = offset.right !== undefined ?
[ adjust.left, -offset.left ] : [ -adjust.left, offset.left ];
if( (shift.x = Math.max(props[0], props[1])) > props[0] ) {
pos.left -= adjust.left;
shift.left = FALSE;
}
css[ offset.right !== undefined ? RIGHT : LEFT ] = shift.x;
}
}
if(shift.top = (vertical === SHIFT && !!adjust.top)) {
if(newCorner.y === CENTER) {
css['margin-top'] = shift.y = offset['margin-top'] - adjust.top;
}
else {
props = offset.bottom !== undefined ?
[ adjust.top, -offset.top ] : [ -adjust.top, offset.top ];
if( (shift.y = Math.max(props[0], props[1])) > props[0] ) {
pos.top -= adjust.top;
shift.top = FALSE;
}
css[ offset.bottom !== undefined ? BOTTOM : TOP ] = shift.y;
}
}
/*
* If the tip is adjusted in both dimensions, or in a
* direction that would cause it to be anywhere but the
* outer border, hide it!
*/
elems.tip.css(css).toggle(
!((shift.x && shift.y) || (newCorner.x === CENTER && shift.y) || (newCorner.y === CENTER && shift.x))
);
// Adjust position to accomodate tip dimensions
pos.left -= offset.left.charAt ? offset.user : horizontal !== SHIFT || shift.top || !shift.left && !shift.top ? offset.left : 0;
pos.top -= offset.top.charAt ? offset.user : vertical !== SHIFT || shift.left || !shift.left && !shift.top ? offset.top : 0;
// Cache details
cache.left = adjust.left; cache.top = adjust.top;
cache.corner = newCorner.clone();
}
function parseCorner() {
var corner = opts.corner,
posOptions = qTip.options.position,
at = posOptions.at,
my = posOptions.my.string ? posOptions.my.string() : posOptions.my;
// Detect corner and mimic properties
if(corner === FALSE || (my === FALSE && at === FALSE)) {
return FALSE;
}
else {
if(corner === TRUE) {
self.corner = new PLUGINS.Corner(my);
}
else if(!corner.string) {
self.corner = new PLUGINS.Corner(corner);
self.corner.fixed = TRUE;
}
}
// Cache it
cache.corner = new PLUGINS.Corner( self.corner.string() );
return self.corner.string() !== 'centercenter';
}
/* border width calculator */
function parseWidth(corner, side, use) {
side = !side ? corner[corner.precedance] : side;
var isTitleTop = elems.titlebar && corner.y === TOP,
elem = isTitleTop ? elems.titlebar : tooltip,
borderSide = 'border-' + side + '-width',
css = function(elem) { return parseInt(elem.css(borderSide), 10); },
val;
// Grab the border-width value (make tooltip visible first)
whileVisible(function() {
val = (use ? css(use) : (css(elems.content) || css(elem) || css(tooltip))) || 0;
});
return val;
}
function parseRadius(corner) {
var isTitleTop = elems.titlebar && corner.y === TOP,
elem = isTitleTop ? elems.titlebar : elems.content,
moz = $.browser.mozilla,
prefix = moz ? '-moz-' : $.browser.webkit ? '-webkit-' : '',
nonStandard = 'border-radius-' + corner.y + corner.x,
standard = 'border-' + corner.y + '-' + corner.x + '-radius',
css = function(c) { return parseInt(elem.css(c), 10) || parseInt(tooltip.css(c), 10); },
val;
whileVisible(function() {
val = css(standard) || css(prefix + standard) || css(prefix + nonStandard) || css(nonStandard) || 0;
});
return val;
}
function parseColours(actual) {
var i, fill, border,
tip = elems.tip.css('cssText', ''),
corner = actual || self.corner,
invalid = /rgba?\(0, 0, 0(, 0)?\)|transparent|#123456/i,
borderSide = 'border-' + corner[ corner.precedance ] + '-color',
bgColor = 'background-color',
transparent = 'transparent',
important = ' !important',
titlebar = elems.titlebar,
useTitle = titlebar && (corner.y === TOP || (corner.y === CENTER && tip.position().top + (size.height / 2) + opts.offset < titlebar.outerHeight(TRUE))),
colorElem = useTitle ? titlebar : elems.content;
function css(elem, prop, compare) {
var val = elem.css(prop) || transparent;
if(compare && val === elem.css(compare)) { return FALSE; }
else { return invalid.test(val) ? FALSE : val; }
}
// Ensure tooltip is visible then...
whileVisible(function() {
// Attempt to detect the background colour from various elements, left-to-right precedance
color.fill = css(tip, bgColor) || css(colorElem, bgColor) || css(elems.content, bgColor) ||
css(tooltip, bgColor) || tip.css(bgColor);
// Attempt to detect the correct border side colour from various elements, left-to-right precedance
color.border = css(tip, borderSide, 'color') || css(colorElem, borderSide, 'color') ||
css(elems.content, borderSide, 'color') || css(tooltip, borderSide, 'color') || tooltip.css(borderSide);
// Reset background and border colours
$('*', tip).add(tip).css('cssText', bgColor+':'+transparent+important+';border:0'+important+';');
});
}
function calculateSize(corner) {
var y = corner.precedance === Y,
width = size [ y ? WIDTH : HEIGHT ],
height = size [ y ? HEIGHT : WIDTH ],
isCenter = corner.string().indexOf(CENTER) > -1,
base = width * (isCenter ? 0.5 : 1),
pow = Math.pow,
round = Math.round,
bigHyp, ratio, result,
smallHyp = Math.sqrt( pow(base, 2) + pow(height, 2) ),
hyp = [
(border / base) * smallHyp, (border / height) * smallHyp
];
hyp[2] = Math.sqrt( pow(hyp[0], 2) - pow(border, 2) );
hyp[3] = Math.sqrt( pow(hyp[1], 2) - pow(border, 2) );
bigHyp = smallHyp + hyp[2] + hyp[3] + (isCenter ? 0 : hyp[0]);
ratio = bigHyp / smallHyp;
result = [ round(ratio * height), round(ratio * width) ];
return { height: result[ y ? 0 : 1 ], width: result[ y ? 1 : 0 ] };
}
function createVML(tag, props, style) {
return '