/*! * jquery.qtip. The jQuery tooltip plugin * * Copyright (c) 2009 Craig Thompson * http://craigsworks.com * * Licensed under MIT * http://www.opensource.org/licenses/mit-license.php * * Launch : February 2009 * Version : 1.0.0-rc3 * Released: Tuesday 12th May, 2009 - 00:00 * Debug: jquery.qtip.debug.js */ "use strict"; // Enable ECMAScript "strict" operation for this function. See more: http://ejohn.org/blog/ecmascript-5-strict-mode-json-and-more/ /*jslint browser: true, forin: true, onevar: true, undef: true, nomen: true, eqeqeq: true, bitwise: true, regexp: true, strict: true, newcap: true, immed: true, maxerr: 300 */ /*global window: false, jQuery: false */ (function ($) { // Assign cache and event initialisation on document load $(document).ready(function () { // Setup library cache with window scroll and dimensions of document $.fn.qtip.cache = { screen: { scroll: { left: $(window).scrollLeft(), top: $(window).scrollTop() }, width: $(window).width(), height: $(window).height() } }; // Adjust positions of the tooltips on window resize or scroll if enabled var adjustTimer, i; $(window).bind('resize scroll', function (event) { clearTimeout(adjustTimer); adjustTimer = setTimeout(function () { // Readjust cached screen values if(event.type === 'scroll') { $.fn.qtip.cache.screen.scroll = { left: $(window).scrollLeft(), top: $(window).scrollTop() }; } else { $.fn.qtip.cache.screen.width = $(window).width(); $.fn.qtip.cache.screen.height = $(window).height(); } for (i = 0; i < $.fn.qtip.interfaces.length; i++) { // Access current elements API var api = $.fn.qtip.interfaces[i]; // Update position if resize or scroll adjustments are enabled if(api && api.status && api.status.rendered === true && api.options.position.type !== 'static' && (api.options.position.adjust.scroll && event.type === 'scroll' || api.options.position.adjust.resize && event.type === 'resize')) { // Queue the animation so positions are updated correctly api.updatePosition(event, true); } } }, 100); }); // Hide unfocus toolipts on document mousedown $(document).bind('mousedown.qtip', function (event) { if($(event.target).parents('div.qtip').length === 0) { $('.qtip[unfocus]').each(function () { var api = $(this).qtip('api'); // Only hide if its visible and not the tooltips target if($(this).is(':visible') && api && api.status && !api.status.disabled && $(event.target).add(api.elements.target).length > 1) { api.hide(event); } }); } }); }); // Corner object parser function Corner(corner) { if(!corner){ return false; } this.x = String(corner).replace(/middle/i, 'center').match(/left|right|center/i)[0].toLowerCase(); this.y = String(corner).replace(/middle/i, 'center').match(/top|bottom|center/i)[0].toLowerCase(); this.offset = { left: 0, top: 0 }; this.precedance = (corner.charAt(0).search(/^(t|b)/) > -1) ? 'y' : 'x'; this.string = function(){ return (this.precedance === 'y') ? this.y+this.x : this.x+this.y; }; } // Tip coordinates calculator function calculateTip(corner, width, height) { // Define tip coordinates in terms of height and width values var 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], [width / 2, 0], [width, height]], bottomcenter: [[0, 0], [width, 0], [width / 2, height]], rightcenter: [[0, 0], [width, height / 2], [0, height]], leftcenter: [[width, 0], [width, height], [0, height / 2]] }; tips.lefttop = tips.bottomright; tips.righttop = tips.bottomleft; tips.leftbottom = tips.topright; tips.rightbottom = tips.topleft; return tips[corner]; } // Border coordinates calculator function calculateBorders(radius) { var borders; // Use canvas element if supported if($('').get(0).getContext) { borders = { topLeft: [radius, radius], topRight: [0, radius], bottomLeft: [radius, 0], bottomRight: [0, 0] }; } // Canvas not supported - Use VML (IE) else if($.browser.msie) { borders = { topLeft: [-90, 90, 0], topRight: [-90, 90, -radius], bottomLeft: [90, 270, 0], bottomRight: [90, 270, -radius] }; } return borders; } // Build a jQuery style object from supplied style object function jQueryStyle(style, sub) { var styleObj, i; styleObj = $.extend(true, {}, style); for (i in styleObj) { if(sub === true && (/(tip|classes)/i).test(i)) { delete styleObj[i]; } else if(!sub && (/(width|border|tip|title|classes|user)/i).test(i)) { delete styleObj[i]; } } return styleObj; } // Sanitize styles function sanitizeStyle(style) { if(typeof style.tip !== 'object') { style.tip = { corner: style.tip }; } if(typeof style.tip.size !== 'object') { style.tip.size = { width: style.tip.size, height: style.tip.size }; } if(typeof style.border !== 'object') { style.border = { width: style.border }; } if(typeof style.width !== 'object') { style.width = { value: style.width }; } if(typeof style.width.max === 'string') { style.width.max = parseInt(style.width.max.replace(/([0-9]+)/i, "$1"), 10); } if(typeof style.width.min === 'string') { style.width.min = parseInt(style.width.min.replace(/([0-9]+)/i, "$1"), 10); } // Convert deprecated x and y tip values to width/height if(typeof style.tip.size.x === 'number') { style.tip.size.width = style.tip.size.x; delete style.tip.size.x; } if(typeof style.tip.size.y === 'number') { style.tip.size.height = style.tip.size.y; delete style.tip.size.y; } return style; } // Build styles recursively with inheritance function buildStyle() { var self, i, styleArray, styleExtend, finalStyle, ieAdjust; self = this; // Build style options from supplied arguments styleArray = [true, {}]; for(i = 0; i < arguments.length; i++){ styleArray.push(arguments[i]); } styleExtend = [$.extend.apply($, styleArray)]; // Loop through each named style inheritance while(typeof styleExtend[0].name === 'string') { // Sanitize style data and append to extend array styleExtend.unshift(sanitizeStyle($.fn.qtip.styles[styleExtend[0].name])); } // Make sure resulting tooltip className represents final style styleExtend.unshift(true, { classes: { tooltip: 'qtip-' + (arguments[0].name || 'defaults') } }, $.fn.qtip.styles.defaults); // Extend into a single style object finalStyle = $.extend.apply($, styleExtend); // Adjust tip size if needed (IE 1px adjustment bug fix) ieAdjust = ($.browser.msie) ? 1 : 0; finalStyle.tip.size.width += ieAdjust; finalStyle.tip.size.height += ieAdjust; // Force even numbers for pixel precision if(finalStyle.tip.size.width % 2 > 0) { finalStyle.tip.size.width += 1; } if(finalStyle.tip.size.height % 2 > 0) { finalStyle.tip.size.height += 1; } // Sanitize final styles tip corner value if(finalStyle.tip.corner === true) { if(self.options.position.corner.tooltip === 'center' && self.options.position.corner.target === 'center') { finalStyle.tip.corner = false; } else { finalStyle.tip.corner = self.options.position.corner.tooltip; } } return finalStyle; } // Border canvas draw method function drawBorder(canvas, coordinates, radius, color) { // Create corner var context = canvas.get(0).getContext('2d'); context.fillStyle = color; context.beginPath(); context.arc(coordinates[0], coordinates[1], radius, 0, Math.PI * 2, false); context.fill(); } // Create borders using canvas and VML function createBorder() { var self, i, width, radius, color, coordinates, containers, size, betweenWidth, betweenCorners, borderTop, borderBottom, borderCoord, sideWidth, vertWidth; self = this; // Destroy previous border elements, if present self.elements.wrapper.find('.qtip-borderBottom, .qtip-borderTop').remove(); // Setup local variables width = self.options.style.border.width; radius = self.options.style.border.radius; color = self.options.style.border.color || self.options.style.tip.color; // Calculate border coordinates coordinates = calculateBorders(radius); // Create containers for the border shapes containers = {}; for (i in coordinates) { // Create shape container containers[i] = '
'; // Canvas is supported if($('').get(0).getContext) { containers[i] += ''; } // No canvas, but if it's IE use VML else if($.browser.msie) { size = radius * 2 + 3; containers[i] += ''; } containers[i] += '
'; } // Create between corners elements betweenWidth = self.getDimensions().width - (Math.max(width, radius) * 2); betweenCorners = '
'; // Create top border container borderTop = '
' + containers.topLeft + containers.topRight + betweenCorners; self.elements.wrapper.prepend(borderTop); // Create bottom border container borderBottom = '
' + containers.bottomLeft + containers.bottomRight + betweenCorners; self.elements.wrapper.append(borderBottom); // Draw the borders if canvas were used (Delayed til after DOM creation) if($('').get(0).getContext) { self.elements.wrapper.find('canvas').each(function () { borderCoord = coordinates[$(this).parent('[rel]:first').attr('rel')]; drawBorder.call(self, $(this), borderCoord, radius, color); }); } // Create a phantom VML element (IE won't show the last created VML element otherwise) else if($.browser.msie) { self.elements.tooltip.append(''); } // Setup contentWrapper border sideWidth = Math.max(radius, (radius + (width - radius))); vertWidth = Math.max(width - radius, 0); self.elements.contentWrapper.css({ border: '0px solid ' + color, borderWidth: vertWidth + 'px ' + sideWidth + 'px' }); } // Canvas tip drawing method function drawTip(canvas, coordinates, color) { // Setup properties var context = canvas.get(0).getContext('2d'); context.fillStyle = color; // Create tip context.beginPath(); context.moveTo(coordinates[0][0], coordinates[0][1]); context.lineTo(coordinates[1][0], coordinates[1][1]); context.lineTo(coordinates[2][0], coordinates[2][1]); context.fill(); } function positionTip(corner) { var self, ieAdjust, positionAdjust, paddingCorner, paddingSize, newMargin; self = this; // Return if tips are disabled or tip is not yet rendered if(self.options.style.tip.corner === false || !self.elements.tip) { return; } if(!corner) { corner = new Corner(self.elements.tip.attr('rel')); } // Setup adjustment variables ieAdjust = positionAdjust = ($.browser.msie) ? 1 : 0; // Set initial position self.elements.tip.css(corner[corner.precedance], 0); // Set position of tip to correct side if(corner.precedance === 'y') { // Adjustments for IE6 - 0.5px border gap bug if($.browser.msie) { if(parseInt($.browser.version.charAt(0), 10) === 6) { positionAdjust = corner.y === 'top' ? -3 : 1; } else { positionAdjust = corner.y === 'top' ? 1 : 2; } } if(corner.x === 'center') { self.elements.tip.css({ left: '50%', marginLeft: -(self.options.style.tip.size.width / 2) }); } else if(corner.x === 'left') { self.elements.tip.css({ left: self.options.style.border.radius - ieAdjust }); } else { self.elements.tip.css({ right: self.options.style.border.radius + ieAdjust }); } if(corner.y === 'top') { self.elements.tip.css({ top: -positionAdjust }); } else { self.elements.tip.css({ bottom: positionAdjust }); } } else { // Adjustments for IE6 - 0.5px border gap bug if($.browser.msie) { positionAdjust = (parseInt($.browser.version.charAt(0), 10) === 6) ? 1 : (corner.x === 'left' ? 1 : 2); } if(corner.y === 'center') { self.elements.tip.css({ top: '50%', marginTop: -(self.options.style.tip.size.height / 2) }); } else if(corner.y === 'top') { self.elements.tip.css({ top: self.options.style.border.radius - ieAdjust }); } else { self.elements.tip.css({ bottom: self.options.style.border.radius + ieAdjust }); } if(corner.x === 'left') { self.elements.tip.css({ left: -positionAdjust }); } else { self.elements.tip.css({ right: positionAdjust }); } } // Adjust tooltip padding to compensate for tip paddingCorner = 'padding-' + corner[corner.precedance]; paddingSize = self.options.style.tip.size[corner.precedance === 'x' ? 'width' : 'height']; self.elements.tooltip.css('padding', 0).css(paddingCorner, paddingSize); // Match content margin to prevent gap bug in IE6 ONLY if($.browser.msie && parseInt($.browser.version.charAt(0), 6) === 6) { newMargin = parseInt(self.elements.tip.css('margin-top'), 10) || 0; newMargin += parseInt(self.elements.content.css('margin-top'), 10) || 0; self.elements.tip.css({ marginTop: newMargin }); } } // Create tip using canvas and VML function createTip(corner) { var self, color, coordinates, coordsize, path, tip; self = this; // Destroy previous tip, if there is one if(self.elements.tip !== null) { self.elements.tip.remove(); } // Setup color and corner values color = self.options.style.tip.color || self.options.style.border.color; if(self.options.style.tip.corner === false) { return; } else if(!corner) { corner = new Corner(self.options.style.tip.corner); } // Calculate tip coordinates coordinates = calculateTip(corner.string(), self.options.style.tip.size.width, self.options.style.tip.size.height); // Create tip element self.elements.tip = '
'; // Attach new tip to tooltip element self.elements.tooltip.prepend(self.elements.tip); // Use canvas element if supported if($('').get(0).getContext) { tip = ''; } // Canvas not supported - Use VML (IE) else if($.browser.msie) { // Create coordize and tip path using tip coordinates coordsize = self.options.style.tip.size.width + ',' + self.options.style.tip.size.height; path = 'm' + coordinates[0][0] + ',' + coordinates[0][1]; path += ' l' + coordinates[1][0] + ',' + coordinates[1][1]; path += ' ' + coordinates[2][0] + ',' + coordinates[2][1]; path += ' xe'; // Create VML element tip = ''; // Create a phantom VML element (IE won't show the last created VML element otherwise) tip += ''; // Prevent tooltip appearing above the content (IE z-index bug) self.elements.contentWrapper.css('position', 'relative'); } // Create element reference and append vml/canvas self.elements.tip = self.elements.tooltip.find('.' + self.options.style.classes.tip).eq(0); self.elements.tip.html(tip); // Draw the canvas tip (Delayed til after DOM creation) if($('').get(0).getContext) { drawTip.call(self, self.elements.tip.find('canvas:first'), coordinates, color); } // Fix IE small tip bug if(corner.y === 'top' && $.browser.msie && parseInt($.browser.version.charAt(0), 10) === 6) { self.elements.tip.css({ marginTop: -4 }); } // Set the tip position positionTip.call(self, corner); } // Create title bar for content function createTitle() { var self = this; // Destroy previous title element, if present if(self.elements.title !== null) { self.elements.title.remove(); } // Append new ARIA attribute to tooltip self.elements.tooltip.attr('aria-labelledby', 'qtip-' + self.id + '-title'); // Create title element self.elements.title = $('
').css(jQueryStyle(self.options.style.title, true)).css({ zoom: ($.browser.msie) ? 1 : 0 }).prependTo(self.elements.contentWrapper); // Update title with contents if enabled if(self.options.content.title.text) { self.updateTitle.call(self, self.options.content.title.text); } // Create title close buttons if enabled if(self.options.content.title.button !== false && typeof self.options.content.title.button === 'string') { self.elements.button = $('').css(jQueryStyle(self.options.style.button, true)).html(self.options.content.title.button).prependTo(self.elements.title).click(function (event) { if(!self.status.disabled) { self.hide(event); } }); } } // Assign hide and show events function assignEvents() { var self, showTarget, hideTarget, inactiveEvents; self = this; // Setup event target variables showTarget = self.options.show.when.target; hideTarget = self.options.hide.when.target; // Add tooltip as a hideTarget is its fixed if(self.options.hide.fixed) { hideTarget = hideTarget.add(self.elements.tooltip); } // Define events which reset the 'inactive' event handler inactiveEvents = ['click', 'dblclick', 'mousedown', 'mouseup', 'mousemove', 'mouseout', 'mouseenter', 'mouseleave', 'mouseover']; // Define 'inactive' event timer method function inactiveMethod(event) { if(self.status.disabled === true) { return; } //Clear and reset the timer clearTimeout(self.timers.inactive); self.timers.inactive = setTimeout(function () { // Unassign 'inactive' events $(inactiveEvents).each(function () { hideTarget.unbind(this + '.qtip-inactive'); self.elements.content.unbind(this + '.qtip-inactive'); }); // Hide the tooltip self.hide(event); }, self.options.hide.delay); } // Check if the tooltip is 'fixed' if(self.options.hide.fixed === true) { self.elements.tooltip.bind('mouseover.qtip', function () { if(self.status.disabled === true) { return; } // Reset the hide timer clearTimeout(self.timers.hide); }); } // Define show event method function showMethod(event) { if(self.status.disabled === true) { return; } // If set, hide tooltip when inactive for delay period if(self.options.hide.when.event === 'inactive') { // Assign each reset event $(inactiveEvents).each(function () { hideTarget.bind(this + '.qtip-inactive', inactiveMethod); self.elements.content.bind(this + '.qtip-inactive', inactiveMethod); }); // Start the inactive timer inactiveMethod(); } // Clear hide timers clearTimeout(self.timers.show); clearTimeout(self.timers.hide); // Start show timer if(self.options.show.delay > 0) { self.timers.show = setTimeout(function () { self.show(event); }, self.options.show.delay); } else { self.show(event); } } // Define hide event method function hideMethod(event) { if(self.status.disabled === true) { return; } // Prevent hiding if tooltip is fixed and event target is the tooltip if(self.options.hide.fixed === true && (/mouse(out|leave)/i).test(self.options.hide.when.event) && $(event.relatedTarget).parents('div.qtip[id^="qtip"]').length > 0) { // Prevent default and popagation event.stopPropagation(); event.preventDefault(); // Reset the hide timer clearTimeout(self.timers.hide); return false; } // Clear timers and stop animation queue clearTimeout(self.timers.show); clearTimeout(self.timers.hide); self.elements.tooltip.stop(true, true); // If tooltip has displayed, start hide timer self.timers.hide = setTimeout(function () { self.hide(event); }, self.options.hide.delay); } // Both events and targets are identical, apply events using a toggle if((self.options.show.when.target.add(self.options.hide.when.target).length === 1 && self.options.show.when.event === self.options.hide.when.event && self.options.hide.when.event !== 'inactive') || self.options.hide.when.event === 'unfocus') { self.cache.toggle = 0; // Use a toggle to prevent hide/show conflicts showTarget.bind(self.options.show.when.event + '.qtip', function (event) { if(self.cache.toggle === 0) { showMethod(event); } else { hideMethod(event); } }); } // Events are not identical, bind normally else { showTarget.bind(self.options.show.when.event + '.qtip', showMethod); // If the hide event is not 'inactive', bind the hide method if(self.options.hide.when.event !== 'inactive') { hideTarget.bind(self.options.hide.when.event + '.qtip', hideMethod); } } // Focus the tooltip on mouseover if((/(fixed|absolute)/).test(self.options.position.type)) { self.elements.tooltip.bind('mouseover.qtip', self.focus); } // If mouse is the target, update tooltip position on mousemove if(self.options.position.target === 'mouse' && self.options.position.type !== 'static') { showTarget.bind('mousemove.qtip', function (event) { // Set the new mouse positions if adjustment is enabled self.cache.mouse = { x: event.pageX, y: event.pageY }; // Update the tooltip position only if the tooltip is visible and adjustment is enabled if(self.status.disabled === false && self.options.position.adjust.mouse === true && self.options.position.type !== 'static' && self.elements.tooltip.css('display') !== 'none') { self.updatePosition(event); } }); } } // BGIFRAME JQUERY PLUGIN ADAPTION // Special thanks to Brandon Aaron for this plugin // http://plugins.jquery.com/project/bgiframe function bgiframe() { var self, html, dimensions; self = this; dimensions = self.getDimensions(); // Setup iframe HTML string html = '