UNPKG

qtip2

Version:

Introducing... qTip2. The second generation of the advanced qTip plugin for the ever popular jQuery framework.

291 lines (239 loc) 9.59 kB
PROTOTYPE._createPosClass = function(my) { return NAMESPACE + '-pos-' + (my || this.options.position.my).abbrev(); }; PROTOTYPE.reposition = function(event, effect) { if(!this.rendered || this.positioning || this.destroyed) { return this; } // Set positioning flag this.positioning = TRUE; var cache = this.cache, tooltip = this.tooltip, posOptions = this.options.position, target = posOptions.target, my = posOptions.my, at = posOptions.at, viewport = posOptions.viewport, container = posOptions.container, adjust = posOptions.adjust, method = adjust.method.split(' '), tooltipWidth = tooltip.outerWidth(FALSE), tooltipHeight = tooltip.outerHeight(FALSE), targetWidth = 0, targetHeight = 0, type = tooltip.css('position'), position = { left: 0, top: 0 }, visible = tooltip[0].offsetWidth > 0, isScroll = event && event.type === 'scroll', win = $(window), doc = container[0].ownerDocument, mouse = this.mouse, pluginCalculations, offset, adjusted, newClass; // Check if absolute position was passed if($.isArray(target) && target.length === 2) { // Force left top and set position at = { x: LEFT, y: TOP }; position = { left: target[0], top: target[1] }; } // Check if mouse was the target else if(target === 'mouse') { // Force left top to allow flipping at = { x: LEFT, y: TOP }; // Use the mouse origin that caused the show event, if distance hiding is enabled if((!adjust.mouse || this.options.hide.distance) && cache.origin && cache.origin.pageX) { event = cache.origin; } // Use cached event for resize/scroll events else if(!event || event && (event.type === 'resize' || event.type === 'scroll')) { event = cache.event; } // Otherwise, use the cached mouse coordinates if available else if(mouse && mouse.pageX) { event = mouse; } // Calculate body and container offset and take them into account below if(type !== 'static') { position = container.offset(); } if(doc.body.offsetWidth !== (window.innerWidth || doc.documentElement.clientWidth)) { offset = $(document.body).offset(); } // Use event coordinates for position position = { left: event.pageX - position.left + (offset && offset.left || 0), top: event.pageY - position.top + (offset && offset.top || 0) }; // Scroll events are a pain, some browsers if(adjust.mouse && isScroll && mouse) { position.left -= (mouse.scrollX || 0) - win.scrollLeft(); position.top -= (mouse.scrollY || 0) - win.scrollTop(); } } // Target wasn't mouse or absolute... else { // Check if event targetting is being used if(target === 'event') { if(event && event.target && event.type !== 'scroll' && event.type !== 'resize') { cache.target = $(event.target); } else if(!event.target) { cache.target = this.elements.target; } } else if(target !== 'event'){ cache.target = $(target.jquery ? target : this.elements.target); } target = cache.target; // Parse the target into a jQuery object and make sure there's an element present target = $(target).eq(0); if(target.length === 0) { return this; } // Check if window or document is the target else if(target[0] === document || target[0] === window) { targetWidth = BROWSER.iOS ? window.innerWidth : target.width(); targetHeight = BROWSER.iOS ? window.innerHeight : target.height(); if(target[0] === window) { position = { top: (viewport || target).scrollTop(), left: (viewport || target).scrollLeft() }; } } // Check if the target is an <AREA> element else if(PLUGINS.imagemap && target.is('area')) { pluginCalculations = PLUGINS.imagemap(this, target, at, PLUGINS.viewport ? method : FALSE); } // Check if the target is an SVG element else if(PLUGINS.svg && target && target[0].ownerSVGElement) { pluginCalculations = PLUGINS.svg(this, target, at, PLUGINS.viewport ? method : FALSE); } // Otherwise use regular jQuery methods else { targetWidth = target.outerWidth(FALSE); targetHeight = target.outerHeight(FALSE); position = target.offset(); } // Parse returned plugin values into proper variables if(pluginCalculations) { targetWidth = pluginCalculations.width; targetHeight = pluginCalculations.height; offset = pluginCalculations.offset; position = pluginCalculations.position; } // Adjust position to take into account offset parents position = this.reposition.offset(target, position, container); // Adjust for position.fixed tooltips (and also iOS scroll bug in v3.2-4.0 & v4.3-4.3.2) if(BROWSER.iOS > 3.1 && BROWSER.iOS < 4.1 || BROWSER.iOS >= 4.3 && BROWSER.iOS < 4.33 || !BROWSER.iOS && type === 'fixed' ){ position.left -= win.scrollLeft(); position.top -= win.scrollTop(); } // Adjust position relative to target if(!pluginCalculations || pluginCalculations && pluginCalculations.adjustable !== FALSE) { position.left += at.x === RIGHT ? targetWidth : at.x === CENTER ? targetWidth / 2 : 0; position.top += at.y === BOTTOM ? targetHeight : at.y === CENTER ? targetHeight / 2 : 0; } } // Adjust position relative to tooltip position.left += adjust.x + (my.x === RIGHT ? -tooltipWidth : my.x === CENTER ? -tooltipWidth / 2 : 0); position.top += adjust.y + (my.y === BOTTOM ? -tooltipHeight : my.y === CENTER ? -tooltipHeight / 2 : 0); // Use viewport adjustment plugin if enabled if(PLUGINS.viewport) { adjusted = position.adjusted = PLUGINS.viewport( this, position, posOptions, targetWidth, targetHeight, tooltipWidth, tooltipHeight ); // Apply offsets supplied by positioning plugin (if used) if(offset && adjusted.left) { position.left += offset.left; } if(offset && adjusted.top) { position.top += offset.top; } // Apply any new 'my' position if(adjusted.my) { this.position.my = adjusted.my; } } // Viewport adjustment is disabled, set values to zero else { position.adjusted = { left: 0, top: 0 }; } // Set tooltip position class if it's changed if(cache.posClass !== (newClass = this._createPosClass(this.position.my))) { cache.posClass = newClass; tooltip.removeClass(cache.posClass).addClass(newClass); } // tooltipmove event if(!this._trigger('move', [position, viewport.elem || viewport], event)) { return this; } delete position.adjusted; // If effect is disabled, target it mouse, no animation is defined or positioning gives NaN out, set CSS directly if(effect === FALSE || !visible || isNaN(position.left) || isNaN(position.top) || target === 'mouse' || !$.isFunction(posOptions.effect)) { tooltip.css(position); } // Use custom function if provided else if($.isFunction(posOptions.effect)) { posOptions.effect.call(tooltip, this, $.extend({}, position)); tooltip.queue(function(next) { // Reset attributes to avoid cross-browser rendering bugs $(this).css({ opacity: '', height: '' }); if(BROWSER.ie) { this.style.removeAttribute('filter'); } next(); }); } // Set positioning flag this.positioning = FALSE; return this; }; // Custom (more correct for qTip!) offset calculator PROTOTYPE.reposition.offset = function(elem, pos, container) { if(!container[0]) { return pos; } var ownerDocument = $(elem[0].ownerDocument), quirks = !!BROWSER.ie && document.compatMode !== 'CSS1Compat', parent = container[0], scrolled, position, parentOffset, overflow; function scroll(e, i) { pos.left += i * e.scrollLeft(); pos.top += i * e.scrollTop(); } // Compensate for non-static containers offset do { if((position = $.css(parent, 'position')) !== 'static') { if(position === 'fixed') { parentOffset = parent.getBoundingClientRect(); scroll(ownerDocument, -1); } else { parentOffset = $(parent).position(); parentOffset.left += parseFloat($.css(parent, 'borderLeftWidth')) || 0; parentOffset.top += parseFloat($.css(parent, 'borderTopWidth')) || 0; } pos.left -= parentOffset.left + (parseFloat($.css(parent, 'marginLeft')) || 0); pos.top -= parentOffset.top + (parseFloat($.css(parent, 'marginTop')) || 0); // If this is the first parent element with an overflow of "scroll" or "auto", store it if(!scrolled && (overflow = $.css(parent, 'overflow')) !== 'hidden' && overflow !== 'visible') { scrolled = $(parent); } } } while(parent = parent.offsetParent); // Compensate for containers scroll if it also has an offsetParent (or in IE quirks mode) if(scrolled && (scrolled[0] !== ownerDocument[0] || quirks)) { scroll(scrolled, 1); } return pos; }; // Corner class var C = (CORNER = PROTOTYPE.reposition.Corner = function(corner, forceY) { corner = ('' + corner).replace(/([A-Z])/, ' $1').replace(/middle/gi, CENTER).toLowerCase(); this.x = (corner.match(/left|right/i) || corner.match(/center/) || ['inherit'])[0].toLowerCase(); this.y = (corner.match(/top|bottom|center/i) || ['inherit'])[0].toLowerCase(); this.forceY = !!forceY; var f = corner.charAt(0); this.precedance = f === 't' || f === 'b' ? Y : X; }).prototype; C.invert = function(z, center) { this[z] = this[z] === LEFT ? RIGHT : this[z] === RIGHT ? LEFT : center || this[z]; }; C.string = function(join) { var x = this.x, y = this.y; var result = x !== y ? x === 'center' || y !== 'center' && (this.precedance === Y || this.forceY) ? [y,x] : [x,y] : [x]; return join !== false ? result.join(' ') : result; }; C.abbrev = function() { var result = this.string(false); return result[0].charAt(0) + (result[1] && result[1].charAt(0) || ''); }; C.clone = function() { return new CORNER( this.string(), this.forceY ); };