vendor: 4,906 bytes, lines 1-190
1/* 2 * qTip2 - Pretty powerful tooltips - v3.0.3 3 * http://qtip2.com 4 * 5 * Copyright (c) 2017 6 * Released under the MIT licenses 7 * http://jquery.org/license 8 * 9 * Date: Tue Aug 29 2017 07:45 EDT-0400 10 * Plugins: tips viewport svg modal ie6 11 * Styles: core basic css3 12 */ 13/*global window: false, jQuery: false, console: false, define: false */ 14 15/* Cache window, document, undefined */ 16(function( window, document, undefined ) { 17 18// Uses AMD or browser globals to create a jQuery plugin. 19(function( factory ) { 20 "use strict"; 21 if(typeof define === 'function' && define.amd) { 22 define(['jquery'], factory); 23 } 24 else if(jQuery && !jQuery.fn.qtip) { 25 factory(jQuery); 26 } 27} 28(function($) { 29 "use strict"; // Enable ECMAScript "strict" operation for this function. See more: http://ejohn.org/blog/ecmascript-5-strict-mode-json-and-more/ 30;// Munge the primitives - Paul Irish tip 31var TRUE = true, 32FALSE = false, 33NULL = null, 34 35// Common variables 36X = 'x', Y = 'y', 37WIDTH = 'width', 38HEIGHT = 'height', 39 40// Positioning sides 41TOP = 'top', 42LEFT = 'left', 43BOTTOM = 'bottom', 44RIGHT = 'right', 45CENTER = 'center', 46 47// Position adjustment types 48FLIP = 'flip', 49FLIPINVERT = 'flipinvert', 50SHIFT = 'shift', 51 52// Shortcut vars 53QTIP, PROTOTYPE, CORNER, CHECKS, 54PLUGINS = {}, 55NAMESPACE = 'qtip', 56ATTR_HAS = 'data-hasqtip', 57ATTR_ID = 'data-qtip-id', 58WIDGET = ['ui-widget', 'ui-tooltip'], 59SELECTOR = '.'+NAMESPACE, 60INACTIVE_EVENTS = 'click dblclick mousedown mouseup mousemove mouseleave mouseenter'.split(' '), 61 62CLASS_FIXED = NAMESPACE+'-fixed', 63CLASS_DEFAULT = NAMESPACE + '-default', 64CLASS_FOCUS = NAMESPACE + '-focus', 65CLASS_HOVER = NAMESPACE + '-hover', 66CLASS_DISABLED = NAMESPACE+'-disabled', 67 68replaceSuffix = '_replacedByqTip', 69oldtitle = 'oldtitle', 70trackingBound, 71 72// Browser detection 73BROWSER = { 74 /* 75 * IE version detection 76 * 77 * Adapted from: http://ajaxian.com/archives/attack-of-the-ie-conditional-comment 78 * Credit to James Padolsey for the original implemntation! 79 */ 80 ie: (function() { 81 /* eslint-disable no-empty */ 82 var v, i; 83 for ( 84 v = 4, i = document.createElement('div'); 85 (i.innerHTML = '<!--[if gt IE ' + v + ']><i></i><![endif]-->') && i.getElementsByTagName('i')[0]; 86 v+=1 87 ) {} 88 return v > 4 ? v : NaN; 89 /* eslint-enable no-empty */ 90 })(), 91 92 /* 93 * iOS version detection 94 */ 95 iOS: parseFloat( 96 ('' + (/CPU.*OS ([0-9_]{1,5})|(CPU like).*AppleWebKit.*Mobile/i.exec(navigator.userAgent) || [0,''])[1]) 97 .replace('undefined', '3_2').replace('_', '.').replace('_', '') 98 ) || FALSE 99}; 100;function QTip(target, options, id, attr) { 101 // Elements and ID 102 this.id = id; 103 this.target = target; 104 this.tooltip = NULL; 105 this.elements = { target: target }; 106 107 // Internal constructs 108 this._id = NAMESPACE + '-' + id; 109 this.timers = { img: {} }; 110 this.options = options; 111 this.plugins = {}; 112 113 // Cache object 114 this.cache = { 115 event: {}, 116 target: $(), 117 disabled: FALSE, 118 attr: attr, 119 onTooltip: FALSE, 120 lastClass: '' 121 }; 122 123 // Set the initial flags 124 this.rendered = this.destroyed = this.disabled = this.waiting = 125 this.hiddenDuringWait = this.positioning = this.triggering = FALSE; 126} 127PROTOTYPE = QTip.prototype; 128 129PROTOTYPE._when = function(deferreds) { 130 return $.when.apply($, deferreds); 131}; 132 133PROTOTYPE.render = function(show) { 134 if(this.rendered || this.destroyed) { return this; } // If tooltip has already been rendered, exit 135 136 var self = this, 137 options = this.options, 138 cache = this.cache, 139 elements = this.elements, 140 text = options.content.text, 141 title = options.content.title, 142 button = options.content.button, 143 posOptions = options.position, 144 deferreds = []; 145 146 // Add ARIA attributes to target 147 $.attr(this.target[0], 'aria-describedby', this._id); 148 149 // Create public position object that tracks current position corners 150 cache.posClass = this._createPosClass( 151 (this.position = { my: posOptions.my, at: posOptions.at }).my 152 ); 153 154 // Create tooltip element 155 this.tooltip = elements.tooltip = $('<div/>', { 156 'id': this._id, 157 'class': [ NAMESPACE, CLASS_DEFAULT, options.style.classes, cache.posClass ].join(' '), 158 'width': options.style.width || '', 159 'height': options.style.height || '', 160 'tracking': posOptions.target === 'mouse' && posOptions.adjust.mouse, 161 162 /* ARIA specific attributes */ 163 'role': 'alert', 164 'aria-live': 'polite', 165 'aria-atomic': FALSE, 166 'aria-describedby': this._id + '-content', 167 'aria-hidden': TRUE 168 }) 169 .toggleClass(CLASS_DISABLED, this.disabled) 170 .attr(ATTR_ID, this.id) 171 .data(NAMESPACE, this) 172 .appendTo(posOptions.container) 173 .append( 174 // Create content element 175 elements.content = $('<div />', { 176 'class': NAMESPACE + '-content', 177 'id': this._id + '-content', 178 'aria-atomic': TRUE 179 }) 180 ); 181 182 // Set rendered flag and prevent redundant reposition calls for now 183 this.rendered = -1; 184 this.positioning = TRUE; 185 186 // Create title... 187 if(title) { 188 this._createTitle(); 189 190 // Update title only if its not a callback (called in toggle if so
190) 191 if(!$.isFunction(title)) { 192 deferreds.push( this._updateTitle(title, FALSE) ); 193 } 194 } 195 196 // Create button 197 if(button) { this._createButton(); } 198 199 // Set proper rendered flag and update content if not a callback function (called in toggle) 200 if(!$.isFunction(text)) { 201 deferreds.push( this._updateContent(text, FALSE) ); 202 } 203 this.rendered = TRUE; 204 205 // Setup widget classes 206 this._setWidget(); 207 208 // Initialize 'render' plugins 209 $.each(PLUGINS, function(name) { 210 var instance; 211 if(this.initialize === 'render' && (instance = this(self))) { 212 self.plugins[name] = instance; 213 } 214 }); 215 216 // Unassign initial events and assign proper events 217 this._unassignEvents(); 218 this._assignEvents(); 219 220 // When deferreds have completed 221 this._when(deferreds).then(function() { 222 // tooltiprender event 223 self._trigger('render'); 224 225 // Reset flags 226 self.positioning = FALSE; 227 228 // Show tooltip if not hidden during wait period 229 if(!self.hiddenDuringWait && (options.show.ready || show)) { 230 self.toggle(TRUE, cache.event, FALSE); 231 } 232 self.hiddenDuringWait = FALSE; 233 }); 234 235 // Expose API 236 QTIP.api[this.id] = this; 237 /* */ 238 //!!! stats 239 240 var data = { 241 action: 'tooltips_open_content_stat', 242 postid: self.options.postid 243 }; 244 245 246 247 $.ajax({ 248 url: ajaxurl, 249 type: 'POST', 250 data: data, 251 success: function (response) { 252 if (response) { 253 254 } 255 } 256 }); 257 //!!! end stats 258
vendor: 52,066 bytes, lines 258-1978
258 259 return this; 260}; 261 262PROTOTYPE.destroy = function(immediate) { 263 // Set flag the signify destroy is taking place to plugins 264 // and ensure it only gets destroyed once! 265 if(this.destroyed) { return this.target; } 266 267 function process() { 268 if(this.destroyed) { return; } 269 this.destroyed = TRUE; 270 271 var target = this.target, 272 title = target.attr(oldtitle), 273 timer; 274 275 // Destroy tooltip if rendered 276 if(this.rendered) { 277 this.tooltip.stop(1,0).find('*').remove().end().remove(); 278 } 279 280 // Destroy all plugins 281 $.each(this.plugins, function() { 282 this.destroy && this.destroy(); 283 }); 284 285 // Clear timers 286 for (timer in this.timers) { 287 if (this.timers.hasOwnProperty(timer)) { 288 clearTimeout(this.timers[timer]); 289 } 290 } 291 292 // Remove api object and ARIA attributes 293 target.removeData(NAMESPACE) 294 .removeAttr(ATTR_ID) 295 .removeAttr(ATTR_HAS) 296 .removeAttr('aria-describedby'); 297 298 // Reset old title attribute if removed 299 if(this.options.suppress && title) { 300 target.attr('title', title).removeAttr(oldtitle); 301 } 302 303 // Remove qTip events associated with this API 304 this._unassignEvents(); 305 306 // Remove ID from used id objects, and delete object references 307 // for better garbage collection and leak protection 308 this.options = this.elements = this.cache = this.timers = 309 this.plugins = this.mouse = NULL; 310 311 // Delete epoxsed API object 312 delete QTIP.api[this.id]; 313 } 314 315 // If an immediate destroy is needed 316 if((immediate !== TRUE || this.triggering === 'hide') && this.rendered) { 317 this.tooltip.one('tooltiphidden', $.proxy(process, this)); 318 !this.triggering && this.hide(); 319 } 320 321 // If we're not in the process of hiding... process 322 else { process.call(this); } 323 324 return this.target; 325}; 326;function invalidOpt(a) { 327 return a === NULL || $.type(a) !== 'object'; 328} 329 330function invalidContent(c) { 331 return !($.isFunction(c) || 332 c && c.attr || 333 c.length || 334 $.type(c) === 'object' && (c.jquery || c.then)); 335} 336 337// Option object sanitizer 338function sanitizeOptions(opts) { 339 var content, text, ajax, once; 340 341 if(invalidOpt(opts)) { return FALSE; } 342 343 if(invalidOpt(opts.metadata)) { 344 opts.metadata = { type: opts.metadata }; 345 } 346 347 if('content' in opts) { 348 content = opts.content; 349 350 if(invalidOpt(content) || content.jquery || content.done) { 351 text = invalidContent(content) ? FALSE : content; 352 content = opts.content = { 353 text: text 354 }; 355 } 356 else { text = content.text; } 357 358 // DEPRECATED - Old content.ajax plugin functionality 359 // Converts it into the proper Deferred syntax 360 if('ajax' in content) { 361 ajax = content.ajax; 362 once = ajax && ajax.once !== FALSE; 363 delete content.ajax; 364 365 content.text = function(event, api) { 366 var loading = text || $(this).attr(api.options.content.attr) || 'Loading...', 367 368 deferred = $.ajax( 369 $.extend({}, ajax, { context: api }) 370 ) 371 .then(ajax.success, NULL, ajax.error) 372 .then(function(newContent) { 373 if(newContent && once) { api.set('content.text', newContent); } 374 return newContent; 375 }, 376 function(xhr, status, error) { 377 if(api.destroyed || xhr.status === 0) { return; } 378 api.set('content.text', status + ': ' + error); 379 }); 380 381 return !once ? (api.set('content.text', loading), deferred) : loading; 382 }; 383 } 384 385 if('title' in content) { 386 if($.isPlainObject(content.title)) { 387 content.button = content.title.button; 388 content.title = content.title.text; 389 } 390 391 if(invalidContent(content.title || FALSE)) { 392 content.title = FALSE; 393 } 394 } 395 } 396 397 if('position' in opts && invalidOpt(opts.position)) { 398 opts.position = { my: opts.position, at: opts.position }; 399 } 400 401 if('show' in opts && invalidOpt(opts.show)) { 402 opts.show = opts.show.jquery ? { target: opts.show } : 403 opts.show === TRUE ? { ready: TRUE } : { event: opts.show }; 404 } 405 406 if('hide' in opts && invalidOpt(opts.hide)) { 407 opts.hide = opts.hide.jquery ? { target: opts.hide } : { event: opts.hide }; 408 } 409 410 if('style' in opts && invalidOpt(opts.style)) { 411 opts.style = { classes: opts.style }; 412 } 413 414 // Sanitize plugin options 415 $.each(PLUGINS, function() { 416 this.sanitize && this.sanitize(opts); 417 }); 418 419 return opts; 420} 421 422// Setup builtin .set() option checks 423CHECKS = PROTOTYPE.checks = { 424 builtin: { 425 // Core checks 426 '^id$': function(obj, o, v, prev) { 427 var id = v === TRUE ? QTIP.nextid : v, 428 newId = NAMESPACE + '-' + id; 429 430 if(id !== FALSE && id.length > 0 && !$('#'+newId).length) { 431 this._id = newId; 432 433 if(this.rendered) { 434 this.tooltip[0].id = this._id; 435 this.elements.content[0].id = this._id + '-content'; 436 this.elements.title[0].id = this._id + '-title'; 437 } 438 } 439 else { obj[o] = prev; } 440 }, 441 '^prerender': function(obj, o, v) { 442 v && !this.rendered && this.render(this.options.show.ready); 443 }, 444 445 // Content checks 446 '^content.text$': function(obj, o, v) { 447 this._updateContent(v); 448 }, 449 '^content.attr$': function(obj, o, v, prev) { 450 if(this.options.content.text === this.target.attr(prev)) { 451 this._updateContent( this.target.attr(v) ); 452 } 453 }, 454 '^content.title$': function(obj, o, v) { 455 // Remove title if content is null 456 if(!v) { return this._removeTitle(); } 457 458 // If title isn't already created, create it now and update 459 v && !this.elements.title && this._createTitle(); 460 this._updateTitle(v); 461 }, 462 '^content.button$': function(obj, o, v) { 463 this._updateButton(v); 464 }, 465 '^content.title.(text|button)$': function(obj, o, v) { 466 this.set('content.'+o, v); // Backwards title.text/button compat 467 }, 468 469 // Position checks 470 '^position.(my|at)$': function(obj, o, v){ 471 if('string' === typeof v) { 472 this.position[o] = obj[o] = new CORNER(v, o === 'at'); 473 } 474 }, 475 '^position.container$': function(obj, o, v){ 476 this.rendered && this.tooltip.appendTo(v); 477 }, 478 479 // Show checks 480 '^show.ready$': function(obj, o, v) { 481 v && (!this.rendered && this.render(TRUE) || this.toggle(TRUE)); 482 }, 483 484 // Style checks 485 '^style.classes$': function(obj, o, v, p) { 486 this.rendered && this.tooltip.removeClass(p).addClass(v); 487 }, 488 '^style.(width|height)': function(obj, o, v) { 489 this.rendered && this.tooltip.css(o, v); 490 }, 491 '^style.widget|content.title': function() { 492 this.rendered && this._setWidget(); 493 }, 494 '^style.def': function(obj, o, v) { 495 this.rendered && this.tooltip.toggleClass(CLASS_DEFAULT, !!v); 496 }, 497 498 // Events check 499 '^events.(render|show|move|hide|focus|blur)$': function(obj, o, v) { 500 this.rendered && this.tooltip[($.isFunction(v) ? '' : 'un') + 'bind']('tooltip'+o, v); 501 }, 502 503 // Properties which require event reassignment 504 '^(show|hide|position).(event|target|fixed|inactive|leave|distance|viewport|adjust)': function() { 505 if(!this.rendered) { return; } 506 507 // Set tracking flag 508 var posOptions = this.options.position; 509 this.tooltip.attr('tracking', posOptions.target === 'mouse' && posOptions.adjust.mouse); 510 511 // Reassign events 512 this._unassignEvents(); 513 this._assignEvents(); 514 } 515 } 516}; 517 518// Dot notation converter 519function convertNotation(options, notation) { 520 var i = 0, obj, option = options, 521 522 // Split notation into array 523 levels = notation.split('.'); 524 525 // Loop through 526 while(option = option[ levels[i++] ]) { 527 if(i < levels.length) { obj = option; } 528 } 529 530 return [obj || options, levels.pop()]; 531} 532 533PROTOTYPE.get = function(notation) { 534 if(this.destroyed) { return this; } 535 536 var o = convertNotation(this.options, notation.toLowerCase()), 537 result = o[0][ o[1] ]; 538 539 return result.precedance ? result.string() : result; 540}; 541 542function setCallback(notation, args) { 543 var category, rule, match; 544 545 for(category in this.checks) { 546 if (!this.checks.hasOwnProperty(category)) { continue; } 547 548 for(rule in this.checks[category]) { 549 if (!this.checks[category].hasOwnProperty(rule)) { continue; } 550 551 if(match = (new RegExp(rule, 'i')).exec(notation)) { 552 args.push(match); 553 554 if(category === 'builtin' || this.plugins[category]) { 555 this.checks[category][rule].apply( 556 this.plugins[category] || this, args 557 ); 558 } 559 } 560 } 561 } 562} 563 564var rmove = /^position\.(my|at|adjust|target|container|viewport)|style|content|show\.ready/i, 565 rrender = /^prerender|show\.ready/i; 566 567PROTOTYPE.set = function(option, value) { 568 if(this.destroyed) { return this; } 569 570 var rendered = this.rendered, 571 reposition = FALSE, 572 options = this.options, 573 name; 574 575 // Convert singular option/value pair into object form 576 if('string' === typeof option) { 577 name = option; option = {}; option[name] = value; 578 } 579 else { option = $.extend({}, option); } 580 581 // Set all of the defined options to their new values 582 $.each(option, function(notation, val) { 583 if(rendered && rrender.test(notation)) { 584 delete option[notation]; return; 585 } 586 587 // Set new obj value 588 var obj = convertNotation(options, notation.toLowerCase()), previous; 589 previous = obj[0][ obj[1] ]; 590 obj[0][ obj[1] ] = val && val.nodeType ? $(val) : val; 591 592 // Also check if we need to reposition 593 reposition = rmove.test(notation) || reposition; 594 595 // Set the new params for the callback 596 option[notation] = [obj[0], obj[1], val, previous]; 597 }); 598 599 // Re-sanitize options 600 sanitizeOptions(options); 601 602 /* 603 * Execute any valid callbacks for the set options 604 * Also set positioning flag so we don't get loads of redundant repositioning calls. 605 */ 606 this.positioning = TRUE; 607 $.each(option, $.proxy(setCallback, this)); 608 this.positioning = FALSE; 609 610 // Update position if needed 611 if(this.rendered && this.tooltip[0].offsetWidth > 0 && reposition) { 612 this.reposition( options.position.target === 'mouse' ? NULL : this.cache.event ); 613 } 614 615 return this; 616}; 617;PROTOTYPE._update = function(content, element) { 618 var self = this, 619 cache = this.cache; 620 621 // Make sure tooltip is rendered and content is defined. If not return 622 if(!this.rendered || !content) { return FALSE; } 623 624 // Use function to parse content 625 if($.isFunction(content)) { 626 content = content.call(this.elements.target, cache.event, this) || ''; 627 } 628 629 // Handle deferred content 630 if($.isFunction(content.then)) { 631 cache.waiting = TRUE; 632 return content.then(function(c) { 633 cache.waiting = FALSE; 634 return self._update(c, element); 635 }, NULL, function(e) { 636 return self._update(e, element); 637 }); 638 } 639 640 // If content is null... return false 641 if(content === FALSE || !content && content !== '') { return FALSE; } 642 643 // Append new content if its a DOM array and show it if hidden 644 if(content.jquery && content.length > 0) { 645 element.empty().append( 646 content.css({ display: 'block', visibility: 'visible' }) 647 ); 648 } 649 650 // Content is a regular string, insert the new content 651 else { element.html(content); } 652 653 // Wait for content to be loaded, and reposition 654 return this._waitForContent(element).then(function(images) { 655 if(self.rendered && self.tooltip[0].offsetWidth > 0) { 656 self.reposition(cache.event, !images.length); 657 } 658 }); 659}; 660 661PROTOTYPE._waitForContent = function(element) { 662 var cache = this.cache; 663 664 // Set flag 665 cache.waiting = TRUE; 666 667 // If imagesLoaded is included, ensure images have loaded and return promise 668 return ( $.fn.imagesLoaded ? element.imagesLoaded() : new $.Deferred().resolve([]) ) 669 .done(function() { cache.waiting = FALSE; }) 670 .promise(); 671}; 672 673PROTOTYPE._updateContent = function(content, reposition) { 674 this._update(content, this.elements.content, reposition); 675}; 676 677PROTOTYPE._updateTitle = function(content, reposition) { 678 if(this._update(content, this.elements.title, reposition) === FALSE) { 679 this._removeTitle(FALSE); 680 } 681}; 682 683PROTOTYPE._createTitle = function() 684{ 685 var elements = this.elements, 686 id = this._id+'-title'; 687 688 // Destroy previous title element, if present 689 if(elements.titlebar) { this._removeTitle(); } 690 691 // Create title bar and title elements 692 elements.titlebar = $('<div />', { 693 'class': NAMESPACE + '-titlebar ' + (this.options.style.widget ? createWidgetClass('header') : '') 694 }) 695 .append( 696 elements.title = $('<div />', { 697 'id': id, 698 'class': NAMESPACE + '-title', 699 'aria-atomic': TRUE 700 }) 701 ) 702 .insertBefore(elements.content) 703 704 // Button-specific events 705 .delegate('.qtip-close', 'mousedown keydown mouseup keyup mouseout', function(event) { 706 $(this).toggleClass('ui-state-active ui-state-focus', event.type.substr(-4) === 'down'); 707 }) 708 .delegate('.qtip-close', 'mouseover mouseout', function(event){ 709 $(this).toggleClass('ui-state-hover', event.type === 'mouseover'); 710 }); 711 712 // Create button if enabled 713 if(this.options.content.button) { this._createButton(); } 714}; 715 716PROTOTYPE._removeTitle = function(reposition) 717{ 718 var elements = this.elements; 719 720 if(elements.title) { 721 elements.titlebar.remove(); 722 elements.titlebar = elements.title = elements.button = NULL; 723 724 // Reposition if enabled 725 if(reposition !== FALSE) { this.reposition(); } 726 } 727}; 728;PROTOTYPE._createPosClass = function(my) { 729 return NAMESPACE + '-pos-' + (my || this.options.position.my).abbrev(); 730}; 731 732PROTOTYPE.reposition = function(event, effect) { 733 if(!this.rendered || this.positioning || this.destroyed) { return this; } 734 735 // Set positioning flag 736 this.positioning = TRUE; 737 738 var cache = this.cache, 739 tooltip = this.tooltip, 740 posOptions = this.options.position, 741 target = posOptions.target, 742 my = posOptions.my, 743 at = posOptions.at, 744 viewport = posOptions.viewport, 745 container = posOptions.container, 746 adjust = posOptions.adjust, 747 method = adjust.method.split(' '), 748 tooltipWidth = tooltip.outerWidth(FALSE), 749 tooltipHeight = tooltip.outerHeight(FALSE), 750 targetWidth = 0, 751 targetHeight = 0, 752 type = tooltip.css('position'), 753 position = { left: 0, top: 0 }, 754 visible = tooltip[0].offsetWidth > 0, 755 isScroll = event && event.type === 'scroll', 756 win = $(window), 757 doc = container[0].ownerDocument, 758 mouse = this.mouse, 759 pluginCalculations, offset, adjusted, newClass; 760 761 // Check if absolute position was passed 762 if($.isArray(target) && target.length === 2) { 763 // Force left top and set position 764 at = { x: LEFT, y: TOP }; 765 position = { left: target[0], top: target[1] }; 766 } 767 768 // Check if mouse was the target 769 else if(target === 'mouse') { 770 // Force left top to allow flipping 771 at = { x: LEFT, y: TOP }; 772 773 // Use the mouse origin that caused the show event, if distance hiding is enabled 774 if((!adjust.mouse || this.options.hide.distance) && cache.origin && cache.origin.pageX) { 775 event = cache.origin; 776 } 777 778 // Use cached event for resize/scroll events 779 else if(!event || event && (event.type === 'resize' || event.type === 'scroll')) { 780 event = cache.event; 781 } 782 783 // Otherwise, use the cached mouse coordinates if available 784 else if(mouse && mouse.pageX) { 785 event = mouse; 786 } 787 788 // Calculate body and container offset and take them into account below 789 if(type !== 'static') { position = container.offset(); } 790 if(doc.body.offsetWidth !== (window.innerWidth || doc.documentElement.clientWidth)) { 791 offset = $(document.body).offset(); 792 } 793 794 // Use event coordinates for position 795 position = { 796 left: event.pageX - position.left + (offset && offset.left || 0), 797 top: event.pageY - position.top + (offset && offset.top || 0) 798 }; 799 800 // Scroll events are a pain, some browsers 801 if(adjust.mouse && isScroll && mouse) { 802 position.left -= (mouse.scrollX || 0) - win.scrollLeft(); 803 position.top -= (mouse.scrollY || 0) - win.scrollTop(); 804 } 805 } 806 807 // Target wasn't mouse or absolute... 808 else { 809 // Check if event targetting is being used 810 if(target === 'event') { 811 if(event && event.target && event.type !== 'scroll' && event.type !== 'resize') { 812 cache.target = $(event.target); 813 } 814 else if(!event.target) { 815 cache.target = this.elements.target; 816 } 817 } 818 else if(target !== 'event'){ 819 cache.target = $(target.jquery ? target : this.elements.target); 820 } 821 target = cache.target; 822 823 // Parse the target into a jQuery object and make sure there's an element present 824 target = $(target).eq(0); 825 if(target.length === 0) { return this; } 826 827 // Check if window or document is the target 828 else if(target[0] === document || target[0] === window) { 829 targetWidth = BROWSER.iOS ? window.innerWidth : target.width(); 830 targetHeight = BROWSER.iOS ? window.innerHeight : target.height(); 831 832 if(target[0] === window) { 833 position = { 834 top: (viewport || target).scrollTop(), 835 left: (viewport || target).scrollLeft() 836 }; 837 } 838 } 839 840 // Check if the target is an <AREA> element 841 else if(PLUGINS.imagemap && target.is('area')) { 842 pluginCalculations = PLUGINS.imagemap(this, target, at, PLUGINS.viewport ? method : FALSE); 843 } 844 845 // Check if the target is an SVG element 846 else if(PLUGINS.svg && target && target[0].ownerSVGElement) { 847 pluginCalculations = PLUGINS.svg(this, target, at, PLUGINS.viewport ? method : FALSE); 848 } 849 850 // Otherwise use regular jQuery methods 851 else { 852 targetWidth = target.outerWidth(FALSE); 853 targetHeight = target.outerHeight(FALSE); 854 position = target.offset(); 855 } 856 857 // Parse returned plugin values into proper variables 858 if(pluginCalculations) { 859 targetWidth = pluginCalculations.width; 860 targetHeight = pluginCalculations.height; 861 offset = pluginCalculations.offset; 862 position = pluginCalculations.position; 863 } 864 865 // Adjust position to take into account offset parents 866 position = this.reposition.offset(target, position, container); 867 868 // Adjust for position.fixed tooltips (and also iOS scroll bug in v3.2-4.0 & v4.3-4.3.2) 869 if(BROWSER.iOS > 3.1 && BROWSER.iOS < 4.1 || 870 BROWSER.iOS >= 4.3 && BROWSER.iOS < 4.33 || 871 !BROWSER.iOS && type === 'fixed' 872 ){ 873 position.left -= win.scrollLeft(); 874 position.top -= win.scrollTop(); 875 } 876 877 // Adjust position relative to target 878 if(!pluginCalculations || pluginCalculations && pluginCalculations.adjustable !== FALSE) { 879 position.left += at.x === RIGHT ? targetWidth : at.x === CENTER ? targetWidth / 2 : 0; 880 position.top += at.y === BOTTOM ? targetHeight : at.y === CENTER ? targetHeight / 2 : 0; 881 } 882 } 883 884 // Adjust position relative to tooltip 885 position.left += adjust.x + (my.x === RIGHT ? -tooltipWidth : my.x === CENTER ? -tooltipWidth / 2 : 0); 886 position.top += adjust.y + (my.y === BOTTOM ? -tooltipHeight : my.y === CENTER ? -tooltipHeight / 2 : 0); 887 888 // Use viewport adjustment plugin if enabled 889 if(PLUGINS.viewport) { 890 adjusted = position.adjusted = PLUGINS.viewport( 891 this, position, posOptions, targetWidth, targetHeight, tooltipWidth, tooltipHeight 892 ); 893 894 // Apply offsets supplied by positioning plugin (if used) 895 if(offset && adjusted.left) { position.left += offset.left; } 896 if(offset && adjusted.top) { position.top += offset.top; } 897 898 // Apply any new 'my' position 899 if(adjusted.my) { this.position.my = adjusted.my; } 900 } 901 902 // Viewport adjustment is disabled, set values to zero 903 else { position.adjusted = { left: 0, top: 0 }; } 904 905 // Set tooltip position class if it's changed 906 if(cache.posClass !== (newClass = this._createPosClass(this.position.my))) { 907 cache.posClass = newClass; 908 tooltip.removeClass(cache.posClass).addClass(newClass); 909 } 910 911 // tooltipmove event 912 if(!this._trigger('move', [position, viewport.elem || viewport], event)) { return this; } 913 delete position.adjusted; 914 915 // If effect is disabled, target it mouse, no animation is defined or positioning gives NaN out, set CSS directly 916 if(effect === FALSE || !visible || isNaN(position.left) || isNaN(position.top) || target === 'mouse' || !$.isFunction(posOptions.effect)) { 917 tooltip.css(position); 918 } 919 920 // Use custom function if provided 921 else if($.isFunction(posOptions.effect)) { 922 posOptions.effect.call(tooltip, this, $.extend({}, position)); 923 tooltip.queue(function(next) { 924 // Reset attributes to avoid cross-browser rendering bugs 925 $(this).css({ opacity: '', height: '' }); 926 if(BROWSER.ie) { this.style.removeAttribute('filter'); } 927 928 next(); 929 }); 930 } 931 932 // Set positioning flag 933 this.positioning = FALSE; 934 935 return this; 936}; 937 938// Custom (more correct for qTip!) offset calculator 939PROTOTYPE.reposition.offset = function(elem, pos, container) { 940 if(!container[0]) { return pos; } 941 942 var ownerDocument = $(elem[0].ownerDocument), 943 quirks = !!BROWSER.ie && document.compatMode !== 'CSS1Compat', 944 parent = container[0], 945 scrolled, position, parentOffset, overflow; 946 947 function scroll(e, i) { 948 pos.left += i * e.scrollLeft(); 949 pos.top += i * e.scrollTop(); 950 } 951 952 // Compensate for non-static containers offset 953 do { 954 if((position = $.css(parent, 'position')) !== 'static') { 955 if(position === 'fixed') { 956 parentOffset = parent.getBoundingClientRect(); 957 scroll(ownerDocument, -1); 958 } 959 else { 960 parentOffset = $(parent).position(); 961 parentOffset.left += parseFloat($.css(parent, 'borderLeftWidth')) || 0; 962 parentOffset.top += parseFloat($.css(parent, 'borderTopWidth')) || 0; 963 } 964 965 pos.left -= parentOffset.left + (parseFloat($.css(parent, 'marginLeft')) || 0); 966 pos.top -= parentOffset.top + (parseFloat($.css(parent, 'marginTop')) || 0); 967 968 // If this is the first parent element with an overflow of "scroll" or "auto", store it 969 if(!scrolled && (overflow = $.css(parent, 'overflow')) !== 'hidden' && overflow !== 'visible') { scrolled = $(parent); } 970 } 971 } 972 while(parent = parent.offsetParent); 973 974 // Compensate for containers scroll if it also has an offsetParent (or in IE quirks mode) 975 if(scrolled && (scrolled[0] !== ownerDocument[0] || quirks)) { 976 scroll(scrolled, 1); 977 } 978 979 return pos; 980}; 981 982// Corner class 983var C = (CORNER = PROTOTYPE.reposition.Corner = function(corner, forceY) { 984 corner = ('' + corner).replace(/([A-Z])/, ' $1').replace(/middle/gi, CENTER).toLowerCase(); 985 this.x = (corner.match(/left|right/i) || corner.match(/center/) || ['inherit'])[0].toLowerCase(); 986 this.y = (corner.match(/top|bottom|center/i) || ['inherit'])[0].toLowerCase(); 987 this.forceY = !!forceY; 988 989 var f = corner.charAt(0); 990 this.precedance = f === 't' || f === 'b' ? Y : X; 991}).prototype; 992 993C.invert = function(z, center) { 994 this[z] = this[z] === LEFT ? RIGHT : this[z] === RIGHT ? LEFT : center || this[z]; 995}; 996 997C.string = function(join) { 998 var x = this.x, y = this.y; 999 1000 var result = x !== y ? 1001 x === 'center' || y !== 'center' && (this.precedance === Y || this.forceY) ? 1002 [y,x] : 1003 [x,y] : 1004 [x]; 1005 1006 return join !== false ? result.join(' ') : result; 1007}; 1008 1009C.abbrev = function() { 1010 var result = this.string(false); 1011 return result[0].charAt(0) + (result[1] && result[1].charAt(0) || ''); 1012}; 1013 1014C.clone = function() { 1015 return new CORNER( this.string(), this.forceY ); 1016}; 1017 1018; 1019PROTOTYPE.toggle = function(state, event) { 1020 var cache = this.cache, 1021 options = this.options, 1022 tooltip = this.tooltip; 1023 1024 // Try to prevent flickering when tooltip overlaps show element 1025 if(event) { 1026 if((/over|enter/).test(event.type) && cache.event && (/out|leave/).test(cache.event.type) && 1027 options.show.target.add(event.target).length === options.show.target.length && 1028 tooltip.has(event.relatedTarget).length) { 1029 return this; 1030 } 1031 1032 // Cache event 1033 cache.event = $.event.fix(event); 1034 } 1035 1036 // If we're currently waiting and we've just hidden... stop it 1037 this.waiting && !state && (this.hiddenDuringWait = TRUE); 1038 1039 // Render the tooltip if showing and it isn't already 1040 if(!this.rendered) { return state ? this.render(1) : this; } 1041 else if(this.destroyed || this.disabled) { return this; } 1042 1043 var type = state ? 'show' : 'hide', 1044 opts = this.options[type], 1045 posOptions = this.options.position, 1046 contentOptions = this.options.content, 1047 width = this.tooltip.css('width'), 1048 visible = this.tooltip.is(':visible'), 1049 animate = state || opts.target.length === 1, 1050 sameTarget = !event || opts.target.length < 2 || cache.target[0] === event.target, 1051 identicalState, allow, after; 1052 1053 // Detect state if valid one isn't provided 1054 if((typeof state).search('boolean|number')) { state = !visible; } 1055 1056 // Check if the tooltip is in an identical state to the new would-be state 1057 identicalState = !tooltip.is(':animated') && visible === state && sameTarget; 1058 1059 // Fire tooltip(show/hide) event and check if destroyed 1060 allow = !identicalState ? !!this._trigger(type, [90]) : NULL; 1061 1062 // Check to make sure the tooltip wasn't destroyed in the callback 1063 if(this.destroyed) { return this; } 1064 1065 // If the user didn't stop the method prematurely and we're showing the tooltip, focus it 1066 if(allow !== FALSE && state) { this.focus(event); } 1067 1068 // If the state hasn't changed or the user stopped it, return early 1069 if(!allow || identicalState) { return this; } 1070 1071 // Set ARIA hidden attribute 1072 $.attr(tooltip[0], 'aria-hidden', !!!state); 1073 1074 // Execute state specific properties 1075 if(state) { 1076 // Store show origin coordinates 1077 this.mouse && (cache.origin = $.event.fix(this.mouse)); 1078 1079 // Update tooltip content & title if it's a dynamic function 1080 if($.isFunction(contentOptions.text)) { this._updateContent(contentOptions.text, FALSE); } 1081 if($.isFunction(contentOptions.title)) { this._updateTitle(contentOptions.title, FALSE); } 1082 1083 // Cache mousemove events for positioning purposes (if not already tracking) 1084 if(!trackingBound && posOptions.target === 'mouse' && posOptions.adjust.mouse) { 1085 $(document).bind('mousemove.'+NAMESPACE, this._storeMouse); 1086 trackingBound = TRUE; 1087 } 1088 1089 // Update the tooltip position (set width first to prevent viewport/max-width issues) 1090 if(!width) { tooltip.css('width', tooltip.outerWidth(FALSE)); } 1091 this.reposition(event, arguments[2]); 1092 if(!width) { tooltip.css('width', ''); } 1093 1094 // Hide other tooltips if tooltip is solo 1095 if(!!opts.solo) { 1096 (typeof opts.solo === 'string' ? $(opts.solo) : $(SELECTOR, opts.solo)) 1097 .not(tooltip).not(opts.target).qtip('hide', new $.Event('tooltipsolo')); 1098 } 1099 } 1100 else { 1101 // Clear show timer if we're hiding 1102 clearTimeout(this.timers.show); 1103 1104 // Remove cached origin on hide 1105 delete cache.origin; 1106 1107 // Remove mouse tracking event if not needed (all tracking qTips are hidden) 1108 if(trackingBound && !$(SELECTOR+'[tracking="true"]:visible', opts.solo).not(tooltip).length) { 1109 $(document).unbind('mousemove.'+NAMESPACE); 1110 trackingBound = FALSE; 1111 } 1112 1113 // Blur the tooltip 1114 this.blur(event); 1115 } 1116 1117 // Define post-animation, state specific properties 1118 after = $.proxy(function() { 1119 if(state) { 1120 // Prevent antialias from disappearing in IE by removing filter 1121 if(BROWSER.ie) { tooltip[0].style.removeAttribute('filter'); } 1122 1123 // Remove overflow setting to prevent tip bugs 1124 tooltip.css('overflow', ''); 1125 1126 // Autofocus elements if enabled 1127 if('string' === typeof opts.autofocus) { 1128 $(this.options.show.autofocus, tooltip).focus(); 1129 } 1130 1131 // If set, hide tooltip when inactive for delay period 1132 this.options.show.target.trigger('qtip-'+this.id+'-inactive'); 1133 } 1134 else { 1135 // Reset CSS states 1136 tooltip.css({ 1137 display: '', 1138 visibility: '', 1139 opacity: '', 1140 left: '', 1141 top: '' 1142 }); 1143 } 1144 1145 // tooltipvisible/tooltiphidden events 1146 this._trigger(state ? 'visible' : 'hidden'); 1147 }, this); 1148 1149 // If no effect type is supplied, use a simple toggle 1150 if(opts.effect === FALSE || animate === FALSE) { 1151 tooltip[ type ](); 1152 after(); 1153 } 1154 1155 // Use custom function if provided 1156 else if($.isFunction(opts.effect)) { 1157 tooltip.stop(1, 1); 1158 opts.effect.call(tooltip, this); 1159 tooltip.queue('fx', function(n) { 1160 after(); n(); 1161 }); 1162 } 1163 1164 // Use basic fade function by default 1165 else { tooltip.fadeTo(90, state ? 1 : 0, after); } 1166 1167 // If inactive hide method is set, active it 1168 if(state) { opts.target.trigger('qtip-'+this.id+'-inactive'); } 1169 1170 return this; 1171}; 1172 1173PROTOTYPE.show = function(event) { return this.toggle(TRUE, event); }; 1174 1175PROTOTYPE.hide = function(event) { return this.toggle(FALSE, event); }; 1176;PROTOTYPE.focus = function(event) { 1177 if(!this.rendered || this.destroyed) { return this; } 1178 1179 var qtips = $(SELECTOR), 1180 tooltip = this.tooltip, 1181 curIndex = parseInt(tooltip[0].style.zIndex, 10), 1182 newIndex = QTIP.zindex + qtips.length; 1183 1184 // Only update the z-index if it has changed and tooltip is not already focused 1185 if(!tooltip.hasClass(CLASS_FOCUS)) { 1186 // tooltipfocus event 1187 if(this._trigger('focus', [newIndex], event)) { 1188 // Only update z-index's if they've changed 1189 if(curIndex !== newIndex) { 1190 // Reduce our z-index's and keep them properly ordered 1191 qtips.each(function() { 1192 if(this.style.zIndex > curIndex) { 1193 this.style.zIndex = this.style.zIndex - 1; 1194 } 1195 }); 1196 1197 // Fire blur event for focused tooltip 1198 qtips.filter('.' + CLASS_FOCUS).qtip('blur', event); 1199 } 1200 1201 // Set the new z-index 1202 tooltip.addClass(CLASS_FOCUS)[0].style.zIndex = newIndex; 1203 } 1204 } 1205 1206 return this; 1207}; 1208 1209PROTOTYPE.blur = function(event) { 1210 if(!this.rendered || this.destroyed) { return this; } 1211 1212 // Set focused status to FALSE 1213 this.tooltip.removeClass(CLASS_FOCUS); 1214 1215 // tooltipblur event 1216 this._trigger('blur', [ this.tooltip.css('zIndex') ], event); 1217 1218 return this; 1219}; 1220;PROTOTYPE.disable = function(state) { 1221 if(this.destroyed) { return this; } 1222 1223 // If 'toggle' is passed, toggle the current state 1224 if(state === 'toggle') { 1225 state = !(this.rendered ? this.tooltip.hasClass(CLASS_DISABLED) : this.disabled); 1226 } 1227 1228 // Disable if no state passed 1229 else if('boolean' !== typeof state) { 1230 state = TRUE; 1231 } 1232 1233 if(this.rendered) { 1234 this.tooltip.toggleClass(CLASS_DISABLED, state) 1235 .attr('aria-disabled', state); 1236 } 1237 1238 this.disabled = !!state; 1239 1240 return this; 1241}; 1242 1243PROTOTYPE.enable = function() { return this.disable(FALSE); }; 1244;PROTOTYPE._createButton = function() 1245{ 1246 var self = this, 1247 elements = this.elements, 1248 tooltip = elements.tooltip, 1249 button = this.options.content.button, 1250 isString = typeof button === 'string', 1251 close = isString ? button : 'Close tooltip'; 1252 1253 if(elements.button) { elements.button.remove(); } 1254 1255 // Use custom button if one was supplied by user, else use default 1256 if(button.jquery) { 1257 elements.button = button; 1258 } 1259 else { 1260 elements.button = $('<a />', { 1261 'class': 'qtip-close ' + (this.options.style.widget ? '' : NAMESPACE+'-icon'), 1262 'title': close, 1263 'aria-label': close 1264 }) 1265 .prepend( 1266 $('<span />', { 1267 'class': 'ui-icon ui-icon-close', 1268 'html': '×' 1269 }) 1270 ); 1271 } 1272 1273 // Create button and setup attributes 1274 elements.button.appendTo(elements.titlebar || tooltip) 1275 .attr('role', 'button') 1276 .click(function(event) { 1277 if(!tooltip.hasClass(CLASS_DISABLED)) { self.hide(event); } 1278 return FALSE; 1279 }); 1280}; 1281 1282PROTOTYPE._updateButton = function(button) 1283{ 1284 // Make sure tooltip is rendered and if not, return 1285 if(!this.rendered) { return FALSE; } 1286 1287 var elem = this.elements.button; 1288 if(button) { this._createButton(); } 1289 else { elem.remove(); } 1290}; 1291;// Widget class creator 1292function createWidgetClass(cls) { 1293 return WIDGET.concat('').join(cls ? '-'+cls+' ' : ' '); 1294} 1295 1296// Widget class setter method 1297PROTOTYPE._setWidget = function() 1298{ 1299 var on = this.options.style.widget, 1300 elements = this.elements, 1301 tooltip = elements.tooltip, 1302 disabled = tooltip.hasClass(CLASS_DISABLED); 1303 1304 tooltip.removeClass(CLASS_DISABLED); 1305 CLASS_DISABLED = on ? 'ui-state-disabled' : 'qtip-disabled'; 1306 tooltip.toggleClass(CLASS_DISABLED, disabled); 1307 1308 tooltip.toggleClass('ui-helper-reset '+createWidgetClass(), on).toggleClass(CLASS_DEFAULT, this.options.style.def && !on); 1309 1310 if(elements.content) { 1311 elements.content.toggleClass( createWidgetClass('content'), on); 1312 } 1313 if(elements.titlebar) { 1314 elements.titlebar.toggleClass( createWidgetClass('header'), on); 1315 } 1316 if(elements.button) { 1317 elements.button.toggleClass(NAMESPACE+'-icon', !on); 1318 } 1319}; 1320;function delay(callback, duration) { 1321 // If tooltip has displayed, start hide timer 1322 if(duration > 0) { 1323 return setTimeout( 1324 $.proxy(callback, this), duration 1325 ); 1326 } 1327 else{ callback.call(this); } 1328} 1329 1330function showMethod(event) { 1331 if(this.tooltip.hasClass(CLASS_DISABLED)) { return; } 1332 1333 // Clear hide timers 1334 clearTimeout(this.timers.show); 1335 clearTimeout(this.timers.hide); 1336 1337 // Start show timer 1338 this.timers.show = delay.call(this, 1339 function() { this.toggle(TRUE, event); }, 1340 this.options.show.delay 1341 ); 1342} 1343 1344function hideMethod(event) { 1345 if(this.tooltip.hasClass(CLASS_DISABLED) || this.destroyed) { return; } 1346 1347 // Check if new target was actually the tooltip element 1348 var relatedTarget = $(event.relatedTarget), 1349 ontoTooltip = relatedTarget.closest(SELECTOR)[0] === this.tooltip[0], 1350 ontoTarget = relatedTarget[0] === this.options.show.target[0]; 1351 1352 // Clear timers and stop animation queue 1353 clearTimeout(this.timers.show); 1354 clearTimeout(this.timers.hide); 1355 1356 // Prevent hiding if tooltip is fixed and event target is the tooltip. 1357 // Or if mouse positioning is enabled and cursor momentarily overlaps 1358 if(this !== relatedTarget[0] && 1359 (this.options.position.target === 'mouse' && ontoTooltip) || 1360 this.options.hide.fixed && ( 1361 (/mouse(out|leave|move)/).test(event.type) && (ontoTooltip || ontoTarget)) 1362 ) 1363 { 1364 /* eslint-disable no-empty */ 1365 try { 1366 event.preventDefault(); 1367 event.stopImmediatePropagation(); 1368 } catch(e) {} 1369 /* eslint-enable no-empty */ 1370 1371 return; 1372 } 1373 1374 // If tooltip has displayed, start hide timer 1375 this.timers.hide = delay.call(this, 1376 function() { this.toggle(FALSE, event); }, 1377 this.options.hide.delay, 1378 this 1379 ); 1380} 1381 1382function inactiveMethod(event) { 1383 if(this.tooltip.hasClass(CLASS_DISABLED) || !this.options.hide.inactive) { return; } 1384 1385 // Clear timer 1386 clearTimeout(this.timers.inactive); 1387 1388 this.timers.inactive = delay.call(this, 1389 function(){ this.hide(event); }, 1390 this.options.hide.inactive 1391 ); 1392} 1393 1394function repositionMethod(event) { 1395 if(this.rendered && this.tooltip[0].offsetWidth > 0) { this.reposition(event); } 1396} 1397 1398// Store mouse coordinates 1399PROTOTYPE._storeMouse = function(event) { 1400 (this.mouse = $.event.fix(event)).type = 'mousemove'; 1401 return this; 1402}; 1403 1404// Bind events 1405PROTOTYPE._bind = function(targets, events, method, suffix, context) { 1406 if(!targets || !method || !events.length) { return; } 1407 var ns = '.' + this._id + (suffix ? '-'+suffix : ''); 1408 $(targets).bind( 1409 (events.split ? events : events.join(ns + ' ')) + ns, 1410 $.proxy(method, context || this) 1411 ); 1412 return this; 1413}; 1414PROTOTYPE._unbind = function(targets, suffix) { 1415 targets && $(targets).unbind('.' + this._id + (suffix ? '-'+suffix : '')); 1416 return this; 1417}; 1418 1419// Global delegation helper 1420function delegate(selector, events, method) { 1421 $(document.body).delegate(selector, 1422 (events.split ? events : events.join('.'+NAMESPACE + ' ')) + '.'+NAMESPACE, 1423 function() { 1424 var api = QTIP.api[ $.attr(this, ATTR_ID) ]; 1425 api && !api.disabled && method.apply(api, arguments); 1426 } 1427 ); 1428} 1429// Event trigger 1430PROTOTYPE._trigger = function(type, args, event) { 1431 var callback = new $.Event('tooltip'+type); 1432 callback.originalEvent = event && $.extend({}, event) || this.cache.event || NULL; 1433 1434 this.triggering = type; 1435 this.tooltip.trigger(callback, [this].concat(args || [])); 1436 this.triggering = FALSE; 1437 1438 return !callback.isDefaultPrevented(); 1439}; 1440 1441PROTOTYPE._bindEvents = function(showEvents, hideEvents, showTargets, hideTargets, showCallback, hideCallback) { 1442 // Get tasrgets that lye within both 1443 var similarTargets = showTargets.filter( hideTargets ).add( hideTargets.filter(showTargets) ), 1444 toggleEvents = []; 1445 1446 // If hide and show targets are the same... 1447 if(similarTargets.length) { 1448 1449 // Filter identical show/hide events 1450 $.each(hideEvents, function(i, type) { 1451 var showIndex = $.inArray(type, showEvents); 1452 1453 // Both events are identical, remove from both hide and show events 1454 // and append to toggleEvents 1455 showIndex > -1 && toggleEvents.push( showEvents.splice( showIndex, 1 )[0] ); 1456 }); 1457 1458 // Toggle events are special case of identical show/hide events, which happen in sequence 1459 if(toggleEvents.length) { 1460 // Bind toggle events to the similar targets 1461 this._bind(similarTargets, toggleEvents, function(event) { 1462 var state = this.rendered ? this.tooltip[0].offsetWidth > 0 : false; 1463 (state ? hideCallback : showCallback).call(this, event); 1464 }); 1465 1466 // Remove the similar targets from the regular show/hide bindings 1467 showTargets = showTargets.not(similarTargets); 1468 hideTargets = hideTargets.not(similarTargets); 1469 } 1470 } 1471 1472 // Apply show/hide/toggle events 1473 this._bind(showTargets, showEvents, showCallback); 1474 this._bind(hideTargets, hideEvents, hideCallback); 1475}; 1476 1477PROTOTYPE._assignInitialEvents = function(event) { 1478 var options = this.options, 1479 showTarget = options.show.target, 1480 hideTarget = options.hide.target, 1481 showEvents = options.show.event ? $.trim('' + options.show.event).split(' ') : [], 1482 hideEvents = options.hide.event ? $.trim('' + options.hide.event).split(' ') : []; 1483 1484 // Catch remove/removeqtip events on target element to destroy redundant tooltips 1485 this._bind(this.elements.target, ['remove', 'removeqtip'], function() { 1486 this.destroy(true); 1487 }, 'destroy'); 1488 1489 /* 1490 * Make sure hoverIntent functions properly by using mouseleave as a hide event if 1491 * mouseenter/mouseout is used for show.event, even if it isn't in the users options. 1492 */ 1493 if(/mouse(over|enter)/i.test(options.show.event) && !/mouse(out|leave)/i.test(options.hide.event)) { 1494 hideEvents.push('mouseleave'); 1495 } 1496 1497 /* 1498 * Also make sure initial mouse targetting works correctly by caching mousemove coords 1499 * on show targets before the tooltip has rendered. Also set onTarget when triggered to 1500 * keep mouse tracking working. 1501 */ 1502 this._bind(showTarget, 'mousemove', function(moveEvent) { 1503 this._storeMouse(moveEvent); 1504 this.cache.onTarget = TRUE; 1505 }); 1506 1507 // Define hoverIntent function 1508 function hoverIntent(hoverEvent) { 1509 // Only continue if tooltip isn't disabled 1510 if(this.disabled || this.destroyed) { return FALSE; } 1511 1512 // Cache the event data 1513 this.cache.event = hoverEvent && $.event.fix(hoverEvent); 1514 this.cache.target = hoverEvent && $(hoverEvent.target); 1515 1516 // Start the event sequence 1517 clearTimeout(this.timers.show); 1518 this.timers.show = delay.call(this, 1519 function() { this.render(typeof hoverEvent === 'object' || options.show.ready); }, 1520 options.prerender ? 0 : options.show.delay 1521 ); 1522 } 1523 1524 // Filter and bind events 1525 this._bindEvents(showEvents, hideEvents, showTarget, hideTarget, hoverIntent, function() { 1526 if(!this.timers) { return FALSE; } 1527 clearTimeout(this.timers.show); 1528 }); 1529 1530 // Prerendering is enabled, create tooltip now 1531 if(options.show.ready || options.prerender) { hoverIntent.call(this, event); } 1532}; 1533 1534// Event assignment method 1535PROTOTYPE._assignEvents = function() { 1536 var self = this, 1537 options = this.options, 1538 posOptions = options.position, 1539 1540 tooltip = this.tooltip, 1541 showTarget = options.show.target, 1542 hideTarget = options.hide.target, 1543 containerTarget = posOptions.container, 1544 viewportTarget = posOptions.viewport, 1545 documentTarget = $(document), 1546 windowTarget = $(window), 1547 1548 showEvents = options.show.event ? $.trim('' + options.show.event).split(' ') : [], 1549 hideEvents = options.hide.event ? $.trim('' + options.hide.event).split(' ') : []; 1550 1551 1552 // Assign passed event callbacks 1553 $.each(options.events, function(name, callback) { 1554 self._bind(tooltip, name === 'toggle' ? ['tooltipshow','tooltiphide'] : ['tooltip'+name], callback, null, tooltip); 1555 }); 1556 1557 // Hide tooltips when leaving current window/frame (but not select/option elements) 1558 if(/mouse(out|leave)/i.test(options.hide.event) && options.hide.leave === 'window') { 1559 this._bind(documentTarget, ['mouseout', 'blur'], function(event) { 1560 if(!/select|option/.test(event.target.nodeName) && !event.relatedTarget) { 1561 this.hide(event); 1562 } 1563 }); 1564 } 1565 1566 // Enable hide.fixed by adding appropriate class 1567 if(options.hide.fixed) { 1568 hideTarget = hideTarget.add( tooltip.addClass(CLASS_FIXED) ); 1569 } 1570 1571 /* 1572 * Make sure hoverIntent functions properly by using mouseleave to clear show timer if 1573 * mouseenter/mouseout is used for show.event, even if it isn't in the users options. 1574 */ 1575 else if(/mouse(over|enter)/i.test(options.show.event)) { 1576 this._bind(hideTarget, 'mouseleave', function() { 1577 clearTimeout(this.timers.show); 1578 }); 1579 } 1580 1581 // Hide tooltip on document mousedown if unfocus events are enabled 1582 if(('' + options.hide.event).indexOf('unfocus') > -1) { 1583 this._bind(containerTarget.closest('html'), ['mousedown', 'touchstart'], function(event) { 1584 var elem = $(event.target), 1585 enabled = this.rendered && !this.tooltip.hasClass(CLASS_DISABLED) && this.tooltip[0].offsetWidth > 0, 1586 isAncestor = elem.parents(SELECTOR).filter(this.tooltip[0]).length > 0; 1587 1588 if(elem[0] !== this.target[0] && elem[0] !== this.tooltip[0] && !isAncestor && 1589 !this.target.has(elem[0]).length && enabled 1590 ) { 1591 this.hide(event); 1592 } 1593 }); 1594 } 1595 1596 // Check if the tooltip hides when inactive 1597 if('number' === typeof options.hide.inactive) { 1598 // Bind inactive method to show target(s) as a custom event 1599 this._bind(showTarget, 'qtip-'+this.id+'-inactive', inactiveMethod, 'inactive'); 1600 1601 // Define events which reset the 'inactive' event handler 1602 this._bind(hideTarget.add(tooltip), QTIP.inactiveEvents, inactiveMethod); 1603 } 1604 1605 // Filter and bind events 1606 this._bindEvents(showEvents, hideEvents, showTarget, hideTarget, showMethod, hideMethod); 1607 1608 // Mouse movement bindings 1609 this._bind(showTarget.add(tooltip), 'mousemove', function(event) { 1610 // Check if the tooltip hides when mouse is moved a certain distance 1611 if('number' === typeof options.hide.distance) { 1612 var origin = this.cache.origin || {}, 1613 limit = this.options.hide.distance, 1614 abs = Math.abs; 1615 1616 // Check if the movement has gone beyond the limit, and hide it if so 1617 if(abs(event.pageX - origin.pageX) >= limit || abs(event.pageY - origin.pageY) >= limit) { 1618 this.hide(event); 1619 } 1620 } 1621 1622 // Cache mousemove coords on show targets 1623 this._storeMouse(event); 1624 }); 1625 1626 // Mouse positioning events 1627 if(posOptions.target === 'mouse') { 1628 // If mouse adjustment is on... 1629 if(posOptions.adjust.mouse) { 1630 // Apply a mouseleave event so we don't get problems with overlapping 1631 if(options.hide.event) { 1632 // Track if we're on the target or not 1633 this._bind(showTarget, ['mouseenter', 'mouseleave'], function(event) { 1634 if(!this.cache) {return FALSE; } 1635 this.cache.onTarget = event.type === 'mouseenter'; 1636 }); 1637 } 1638 1639 // Update tooltip position on mousemove 1640 this._bind(documentTarget, 'mousemove', function(event) { 1641 // Update the tooltip position only if the tooltip is visible and adjustment is enabled 1642 if(this.rendered && this.cache.onTarget && !this.tooltip.hasClass(CLASS_DISABLED) && this.tooltip[0].offsetWidth > 0) { 1643 this.reposition(event); 1644 } 1645 }); 1646 } 1647 } 1648 1649 // Adjust positions of the tooltip on window resize if enabled 1650 if(posOptions.adjust.resize || viewportTarget.length) { 1651 this._bind( $.event.special.resize ? viewportTarget : windowTarget, 'resize', repositionMethod ); 1652 } 1653 1654 // Adjust tooltip position on scroll of the window or viewport element if present 1655 if(posOptions.adjust.scroll) { 1656 this._bind( windowTarget.add(posOptions.container), 'scroll', repositionMethod ); 1657 } 1658}; 1659 1660// Un-assignment method 1661PROTOTYPE._unassignEvents = function() { 1662 var options = this.options, 1663 showTargets = options.show.target, 1664 hideTargets = options.hide.target, 1665 targets = $.grep([ 1666 this.elements.target[0], 1667 this.rendered && this.tooltip[0], 1668 options.position.container[0], 1669 options.position.viewport[0], 1670 options.position.container.closest('html')[0], // unfocus 1671 window, 1672 document 1673 ], function(i) { 1674 return typeof i === 'object'; 1675 }); 1676 1677 // Add show and hide targets if they're valid 1678 if(showTargets && showTargets.toArray) { 1679 targets = targets.concat(showTargets.toArray()); 1680 } 1681 if(hideTargets && hideTargets.toArray) { 1682 targets = targets.concat(hideTargets.toArray()); 1683 } 1684 1685 // Unbind the events 1686 this._unbind(targets) 1687 ._unbind(targets, 'destroy') 1688 ._unbind(targets, 'inactive'); 1689}; 1690 1691// Apply common event handlers using delegate (avoids excessive .bind calls!) 1692$(function() { 1693 delegate(SELECTOR, ['mouseenter', 'mouseleave'], function(event) { 1694 var state = event.type === 'mouseenter', 1695 tooltip = $(event.currentTarget), 1696 target = $(event.relatedTarget || event.target), 1697 options = this.options; 1698 1699 // On mouseenter... 1700 if(state) { 1701 // Focus the tooltip on mouseenter (z-index stacking) 1702 this.focus(event); 1703 1704 // Clear hide timer on tooltip hover to prevent it from closing 1705 tooltip.hasClass(CLASS_FIXED) && !tooltip.hasClass(CLASS_DISABLED) && clearTimeout(this.timers.hide); 1706 } 1707 1708 // On mouseleave... 1709 else { 1710 // When mouse tracking is enabled, hide when we leave the tooltip and not onto the show target (if a hide event is set) 1711 if(options.position.target === 'mouse' && options.position.adjust.mouse && 1712 options.hide.event && options.show.target && !target.closest(options.show.target[0]).length) { 1713 this.hide(event); 1714 } 1715 } 1716 1717 // Add hover class 1718 tooltip.toggleClass(CLASS_HOVER, state); 1719 }); 1720 1721 // Define events which reset the 'inactive' event handler 1722 delegate('['+ATTR_ID+']', INACTIVE_EVENTS, inactiveMethod); 1723}); 1724;// Initialization method 1725function init(elem, id, opts) { 1726 var obj, posOptions, attr, config, title, 1727 1728 // Setup element references 1729 docBody = $(document.body), 1730 1731 // Use document body instead of document element if needed 1732 newTarget = elem[0] === document ? docBody : elem, 1733 1734 // Grab metadata from element if plugin is present 1735 metadata = elem.metadata ? elem.metadata(opts.metadata) : NULL, 1736 1737 // If metadata type if HTML5, grab 'name' from the object instead, or use the regular data object otherwise 1738 metadata5 = opts.metadata.type === 'html5' && metadata ? metadata[opts.metadata.name] : NULL, 1739 1740 // Grab data from metadata.name (or data-qtipopts as fallback) using .data() method, 1741 html5 = elem.data(opts.metadata.name || 'qtipopts'); 1742 1743 // If we don't get an object returned attempt to parse it manualyl without parseJSON 1744 /* eslint-disable no-empty */ 1745 try { html5 = typeof html5 === 'string' ? $.parseJSON(html5) : html5; } 1746 catch(e) {} 1747 /* eslint-enable no-empty */ 1748 1749 // Merge in and sanitize metadata 1750 config = $.extend(TRUE, {}, QTIP.defaults, opts, 1751 typeof html5 === 'object' ? sanitizeOptions(html5) : NULL, 1752 sanitizeOptions(metadata5 || metadata)); 1753 1754 // Re-grab our positioning options now we've merged our metadata and set id to passed value 1755 posOptions = config.position; 1756 config.id = id; 1757 1758 // Setup missing content if none is detected 1759 if('boolean' === typeof config.content.text) { 1760 attr = elem.attr(config.content.attr); 1761 1762 // Grab from supplied attribute if available 1763 if(config.content.attr !== FALSE && attr) { config.content.text = attr; } 1764 1765 // No valid content was found, abort render 1766 else { return FALSE; } 1767 } 1768 1769 // Setup target options 1770 if(!posOptions.container.length) { posOptions.container = docBody; } 1771 if(posOptions.target === FALSE) { posOptions.target = newTarget; } 1772 if(config.show.target === FALSE) { config.show.target = newTarget; } 1773 if(config.show.solo === TRUE) { config.show.solo = posOptions.container.closest('body'); } 1774 if(config.hide.target === FALSE) { config.hide.target = newTarget; } 1775 if(config.position.viewport === TRUE) { config.position.viewport = posOptions.container; } 1776 1777 // Ensure we only use a single container 1778 posOptions.container = posOptions.container.eq(0); 1779 1780 // Convert position corner values into x and y strings 1781 posOptions.at = new CORNER(posOptions.at, TRUE); 1782 posOptions.my = new CORNER(posOptions.my); 1783 1784 // Destroy previous tooltip if overwrite is enabled, or skip element if not 1785 if(elem.data(NAMESPACE)) { 1786 if(config.overwrite) { 1787 elem.qtip('destroy', true); 1788 } 1789 else if(config.overwrite === FALSE) { 1790 return FALSE; 1791 } 1792 } 1793 1794 // Add has-qtip attribute 1795 elem.attr(ATTR_HAS, id); 1796 1797 // Remove title attribute and store it if present 1798 if(config.suppress && (title = elem.attr('title'))) { 1799 // Final attr call fixes event delegatiom and IE default tooltip showing problem 1800 elem.removeAttr('title').attr(oldtitle, title).attr('title', ''); 1801 } 1802 1803 // Initialize the tooltip and add API reference 1804 obj = new QTip(elem, config, id, !!attr); 1805 elem.data(NAMESPACE, obj); 1806 1807 return obj; 1808} 1809 1810// jQuery $.fn extension method 1811QTIP = $.fn.qtip = function(options, notation, newValue) 1812{ 1813 var command = ('' + options).toLowerCase(), // Parse command 1814 returned = NULL, 1815 args = $.makeArray(arguments).slice(1), 1816 event = args[args.length - 1], 1817 opts = this[0] ? $.data(this[0], NAMESPACE) : NULL; 1818 1819 // Check for API request 1820 if(!arguments.length && opts || command === 'api') { 1821 return opts; 1822 } 1823 1824 // Execute API command if present 1825 else if('string' === typeof options) { 1826 this.each(function() { 1827 var api = $.data(this, NAMESPACE); 1828 if(!api) { return TRUE; } 1829 1830 // Cache the event if possible 1831 if(event && event.timeStamp) { api.cache.event = event; } 1832 1833 // Check for specific API commands 1834 if(notation && (command === 'option' || command === 'options')) { 1835 if(newValue !== undefined || $.isPlainObject(notation)) { 1836 api.set(notation, newValue); 1837 } 1838 else { 1839 returned = api.get(notation); 1840 return FALSE; 1841 } 1842 } 1843 1844 // Execute API command 1845 else if(api[command]) { 1846 api[command].apply(api, args); 1847 } 1848 }); 1849 1850 return returned !== NULL ? returned : this; 1851 } 1852 1853 // No API commands. validate provided options and setup qTips 1854 else if('object' === typeof options || !arguments.length) { 1855 // Sanitize options first 1856 opts = sanitizeOptions($.extend(TRUE, {}, options)); 1857 1858 return this.each(function(i) { 1859 var api, id; 1860 1861 // Find next available ID, or use custom ID if provided 1862 id = $.isArray(opts.id) ? opts.id[i] : opts.id; 1863 id = !id || id === FALSE || id.length < 1 || QTIP.api[id] ? QTIP.nextid++ : id; 1864 1865 // Initialize the qTip and re-grab newly sanitized options 1866 api = init($(this), id, opts); 1867 if(api === FALSE) { return TRUE; } 1868 else { QTIP.api[id] = api; } 1869 1870 // Initialize plugins 1871 $.each(PLUGINS, function() { 1872 if(this.initialize === 'initialize') { this(api); } 1873 }); 1874 1875 // Assign initial pre-render events 1876 api._assignInitialEvents(event); 1877 }); 1878 } 1879}; 1880 1881// Expose class 1882$.qtip = QTip; 1883 1884// Populated in render method 1885QTIP.api = {}; 1886;$.each({ 1887 /* Allow other plugins to successfully retrieve the title of an element with a qTip applied */ 1888 attr: function(attr, val) { 1889 if(this.length) { 1890 var self = this[0], 1891 title = 'title', 1892 api = $.data(self, 'qtip'); 1893 1894 if(attr === title && api && api.options && 'object' === typeof api && 'object' === typeof api.options && api.options.suppress) { 1895 if(arguments.length < 2) { 1896 return $.attr(self, oldtitle); 1897 } 1898 1899 // If qTip is rendered and title was originally used as content, update it 1900 if(api && api.options.content.attr === title && api.cache.attr) { 1901 api.set('content.text', val); 1902 } 1903 1904 // Use the regular attr method to set, then cache the result 1905 return this.attr(oldtitle, val); 1906 } 1907 } 1908 1909 return $.fn['attr'+replaceSuffix].apply(this, arguments); 1910 }, 1911 1912 /* Allow clone to correctly retrieve cached title attributes */ 1913 clone: function(keepData) { 1914 // Clone our element using the real clone method 1915 var elems = $.fn['clone'+replaceSuffix].apply(this, arguments); 1916 1917 // Grab all elements with an oldtitle set, and change it to regular title attribute, if keepData is false 1918 if(!keepData) { 1919 elems.filter('['+oldtitle+']').attr('title', function() { 1920 return $.attr(this, oldtitle); 1921 }) 1922 .removeAttr(oldtitle); 1923 } 1924 1925 return elems; 1926 } 1927}, function(name, func) { 1928 if(!func || $.fn[name+replaceSuffix]) { return TRUE; } 1929 1930 var old = $.fn[name+replaceSuffix] = $.fn[name]; 1931 $.fn[name] = function() { 1932 return func.apply(this, arguments) || old.apply(this, arguments); 1933 }; 1934}); 1935 1936/* Fire off 'removeqtip' handler in $.cleanData if jQuery UI not present (it already does similar). 1937 * This snippet is taken directly from jQuery UI source code found here: 1938 * http://code.jquery.com/ui/jquery-ui-git.js 1939 */ 1940if(!$.ui) { 1941 $['cleanData'+replaceSuffix] = $.cleanData; 1942 $.cleanData = function( elems ) { 1943 for(var i = 0, elem; (elem = $( elems[i] )).length; i++) { 1944 if(elem.attr(ATTR_HAS)) { 1945 /* eslint-disable no-empty */ 1946 try { elem.triggerHandler('removeqtip'); } 1947 catch( e ) {} 1948 /* eslint-enable no-empty */ 1949 } 1950 } 1951 $['cleanData'+replaceSuffix].apply(this, arguments); 1952 }; 1953} 1954;// qTip version 1955QTIP.version = '3.0.3'; 1956 1957// Base ID for all qTips 1958QTIP.nextid = 0; 1959 1960// Inactive events array 1961QTIP.inactiveEvents = INACTIVE_EVENTS; 1962 1963// Base z-index for all qTips 1964QTIP.zindex = 15000; 1965 1966// Define configuration defaults 1967QTIP.defaults = { 1968 prerender: FALSE, 1969 id: FALSE, 1970 overwrite: TRUE, 1971 suppress: TRUE, 1972 content: { 1973 text: TRUE, 1974 attr: 'title', 1975 title: FALSE, 1976 button: FALSE 1977 }, 1978 position: {
vendor: 19,933 bytes, lines 1979-2672
1979 my: 'top left', 1980 at: 'bottom right', 1981 target: FALSE, 1982 container: FALSE, 1983 viewport: FALSE, 1984 adjust: { 1985 x: 0, y: 0, 1986 mouse: TRUE, 1987 scroll: TRUE, 1988 resize: TRUE, 1989 method: 'flipinvert flipinvert' 1990 }, 1991 effect: function(api, pos) { 1992 $(this).animate(pos, { 1993 duration: 200, 1994 queue: FALSE 1995 }); 1996 } 1997 }, 1998 show: { 1999 target: FALSE, 2000 event: 'mouseenter', 2001 effect: TRUE, 2002 delay: 90, 2003 solo: FALSE, 2004 ready: FALSE, 2005 autofocus: FALSE 2006 }, 2007 hide: { 2008 target: FALSE, 2009 event: 'mouseleave', 2010 effect: TRUE, 2011 delay: 0, 2012 fixed: FALSE, 2013 inactive: FALSE, 2014 leave: 'window', 2015 distance: FALSE 2016 }, 2017 style: { 2018 classes: '', 2019 widget: FALSE, 2020 width: FALSE, 2021 height: FALSE, 2022 def: TRUE 2023 }, 2024 events: { 2025 render: NULL, 2026 move: NULL, 2027 show: NULL, 2028 hide: NULL, 2029 toggle: NULL, 2030 visible: NULL, 2031 hidden: NULL, 2032 focus: NULL, 2033 blur: NULL 2034 } 2035}; 2036;var TIP, 2037createVML, 2038SCALE, 2039PIXEL_RATIO, 2040BACKING_STORE_RATIO, 2041 2042// Common CSS strings 2043MARGIN = 'margin', 2044BORDER = 'border', 2045COLOR = 'color', 2046BG_COLOR = 'background-color', 2047TRANSPARENT = 'transparent', 2048IMPORTANT = ' !important', 2049 2050// Check if the browser supports <canvas/> elements 2051HASCANVAS = !!document.createElement('canvas').getContext, 2052 2053// Invalid colour values used in parseColours() 2054INVALID = /rgba?\(0, 0, 0(, 0)?\)|transparent|#123456/i; 2055 2056// Camel-case method, taken from jQuery source 2057// http://code.jquery.com/jquery-1.8.0.js 2058function camel(s) { return s.charAt(0).toUpperCase() + s.slice(1); } 2059 2060/* 2061 * Modified from Modernizr's testPropsAll() 2062 * http://modernizr.com/downloads/modernizr-latest.js 2063 */ 2064var cssProps = {}, cssPrefixes = ['Webkit', 'O', 'Moz', 'ms']; 2065function vendorCss(elem, prop) { 2066 var ucProp = prop.charAt(0).toUpperCase() + prop.slice(1), 2067 props = (prop + ' ' + cssPrefixes.join(ucProp + ' ') + ucProp).split(' '), 2068 cur, val, i = 0; 2069 2070 // If the property has already been mapped... 2071 if(cssProps[prop]) { return elem.css(cssProps[prop]); } 2072 2073 while(cur = props[i++]) { 2074 if((val = elem.css(cur)) !== undefined) { 2075 cssProps[prop] = cur; 2076 return val; 2077 } 2078 } 2079} 2080 2081// Parse a given elements CSS property into an int 2082function intCss(elem, prop) { 2083 return Math.ceil(parseFloat(vendorCss(elem, prop))); 2084} 2085 2086 2087// VML creation (for IE only) 2088if(!HASCANVAS) { 2089 createVML = function(tag, props, style) { 2090 return '<qtipvml:'+tag+' xmlns="urn:schemas-microsoft.com:vml" class="qtip-vml" '+(props||'')+ 2091 ' style="behavior: url(#default#VML); '+(style||'')+ '" />'; 2092 }; 2093} 2094 2095// Canvas only definitions 2096else { 2097 PIXEL_RATIO = window.devicePixelRatio || 1; 2098 BACKING_STORE_RATIO = (function() { 2099 var context = document.createElement('canvas').getContext('2d'); 2100 return context.backingStorePixelRatio || context.webkitBackingStorePixelRatio || context.mozBackingStorePixelRatio || 2101 context.msBackingStorePixelRatio || context.oBackingStorePixelRatio || 1; 2102 })(); 2103 SCALE = PIXEL_RATIO / BACKING_STORE_RATIO; 2104} 2105 2106 2107function Tip(qtip, options) { 2108 this._ns = 'tip'; 2109 this.options = options; 2110 this.offset = options.offset; 2111 this.size = [ options.width, options.height ]; 2112 2113 // Initialize 2114 this.qtip = qtip; 2115 this.init(qtip); 2116} 2117 2118$.extend(Tip.prototype, { 2119 init: function(qtip) { 2120 var context, tip; 2121 2122 // Create tip element and prepend to the tooltip 2123 tip = this.element = qtip.elements.tip = $('<div />', { 'class': NAMESPACE+'-tip' }).prependTo(qtip.tooltip); 2124 2125 // Create tip drawing element(s) 2126 if(HASCANVAS) { 2127 // save() as soon as we create the canvas element so FF2 doesn't bork on our first restore()! 2128 context = $('<canvas />').appendTo(this.element)[0].getContext('2d'); 2129 2130 // Setup constant parameters 2131 context.lineJoin = 'miter'; 2132 context.miterLimit = 100000; 2133 context.save(); 2134 } 2135 else { 2136 context = createVML('shape', 'coordorigin="0,0"', 'position:absolute;'); 2137 this.element.html(context + context); 2138 2139 // Prevent mousing down on the tip since it causes problems with .live() handling in IE due to VML 2140 qtip._bind( $('*', tip).add(tip), ['click', 'mousedown'], function(event) { event.stopPropagation(); }, this._ns); 2141 } 2142 2143 // Bind update events 2144 qtip._bind(qtip.tooltip, 'tooltipmove', this.reposition, this._ns, this); 2145 2146 // Create it 2147 this.create(); 2148 }, 2149 2150 _swapDimensions: function() { 2151 this.size[0] = this.options.height; 2152 this.size[1] = this.options.width; 2153 }, 2154 _resetDimensions: function() { 2155 this.size[0] = this.options.width; 2156 this.size[1] = this.options.height; 2157 }, 2158 2159 _useTitle: function(corner) { 2160 var titlebar = this.qtip.elements.titlebar; 2161 return titlebar && ( 2162 corner.y === TOP || corner.y === CENTER && this.element.position().top + this.size[1] / 2 + this.options.offset < titlebar.outerHeight(TRUE) 2163 ); 2164 }, 2165 2166 _parseCorner: function(corner) { 2167 var my = this.qtip.options.position.my; 2168 2169 // Detect corner and mimic properties 2170 if(corner === FALSE || my === FALSE) { 2171 corner = FALSE; 2172 } 2173 else if(corner === TRUE) { 2174 corner = new CORNER( my.string() ); 2175 } 2176 else if(!corner.string) { 2177 corner = new CORNER(corner); 2178 corner.fixed = TRUE; 2179 } 2180 2181 return corner; 2182 }, 2183 2184 _parseWidth: function(corner, side, use) { 2185 var elements = this.qtip.elements, 2186 prop = BORDER + camel(side) + 'Width'; 2187 2188 return (use ? intCss(use, prop) : 2189 intCss(elements.content, prop) || 2190 intCss(this._useTitle(corner) && elements.titlebar || elements.content, prop) || 2191 intCss(elements.tooltip, prop) 2192 ) || 0; 2193 }, 2194 2195 _parseRadius: function(corner) { 2196 var elements = this.qtip.elements, 2197 prop = BORDER + camel(corner.y) + camel(corner.x) + 'Radius'; 2198 2199 return BROWSER.ie < 9 ? 0 : 2200 intCss(this._useTitle(corner) && elements.titlebar || elements.content, prop) || 2201 intCss(elements.tooltip, prop) || 0; 2202 }, 2203 2204 _invalidColour: function(elem, prop, compare) { 2205 var val = elem.css(prop); 2206 return !val || compare && val === elem.css(compare) || INVALID.test(val) ? FALSE : val; 2207 }, 2208 2209 _parseColours: function(corner) { 2210 var elements = this.qtip.elements, 2211 tip = this.element.css('cssText', ''), 2212 borderSide = BORDER + camel(corner[ corner.precedance ]) + camel(COLOR), 2213 colorElem = this._useTitle(corner) && elements.titlebar || elements.content, 2214 css = this._invalidColour, color = []; 2215 2216 // Attempt to detect the background colour from various elements, left-to-right precedance 2217 color[0] = css(tip, BG_COLOR) || css(colorElem, BG_COLOR) || css(elements.content, BG_COLOR) || 2218 css(elements.tooltip, BG_COLOR) || tip.css(BG_COLOR); 2219 2220 // Attempt to detect the correct border side colour from various elements, left-to-right precedance 2221 color[1] = css(tip, borderSide, COLOR) || css(colorElem, borderSide, COLOR) || 2222 css(elements.content, borderSide, COLOR) || css(elements.tooltip, borderSide, COLOR) || elements.tooltip.css(borderSide); 2223 2224 // Reset background and border colours 2225 $('*', tip).add(tip).css('cssText', BG_COLOR+':'+TRANSPARENT+IMPORTANT+';'+BORDER+':0'+IMPORTANT+';'); 2226 2227 return color; 2228 }, 2229 2230 _calculateSize: function(corner) { 2231 var y = corner.precedance === Y, 2232 width = this.options.width, 2233 height = this.options.height, 2234 isCenter = corner.abbrev() === 'c', 2235 base = (y ? width: height) * (isCenter ? 0.5 : 1), 2236 pow = Math.pow, 2237 round = Math.round, 2238 bigHyp, ratio, result, 2239 2240 smallHyp = Math.sqrt( pow(base, 2) + pow(height, 2) ), 2241 hyp = [ 2242 this.border / base * smallHyp, 2243 this.border / height * smallHyp 2244 ]; 2245 2246 hyp[2] = Math.sqrt( pow(hyp[0], 2) - pow(this.border, 2) ); 2247 hyp[3] = Math.sqrt( pow(hyp[1], 2) - pow(this.border, 2) ); 2248 2249 bigHyp = smallHyp + hyp[2] + hyp[3] + (isCenter ? 0 : hyp[0]); 2250 ratio = bigHyp / smallHyp; 2251 2252 result = [ round(ratio * width), round(ratio * height) ]; 2253 return y ? result : result.reverse(); 2254 }, 2255 2256 // Tip coordinates calculator 2257 _calculateTip: function(corner, size, scale) { 2258 scale = scale || 1; 2259 size = size || this.size; 2260 2261 var width = size[0] * scale, 2262 height = size[1] * scale, 2263 width2 = Math.ceil(width / 2), height2 = Math.ceil(height / 2), 2264 2265 // Define tip coordinates in terms of height and width values 2266 tips = { 2267 br: [0,0, width,height, width,0], 2268 bl: [0,0, width,0, 0,height], 2269 tr: [0,height, width,0, width,height], 2270 tl: [0,0, 0,height, width,height], 2271 tc: [0,height, width2,0, width,height], 2272 bc: [0,0, width,0, width2,height], 2273 rc: [0,0, width,height2, 0,height], 2274 lc: [width,0, width,height, 0,height2] 2275 }; 2276 2277 // Set common side shapes 2278 tips.lt = tips.br; tips.rt = tips.bl; 2279 tips.lb = tips.tr; tips.rb = tips.tl; 2280 2281 return tips[ corner.abbrev() ]; 2282 }, 2283 2284 // Tip coordinates drawer (canvas) 2285 _drawCoords: function(context, coords) { 2286 context.beginPath(); 2287 context.moveTo(coords[0], coords[1]); 2288 context.lineTo(coords[2], coords[3]); 2289 context.lineTo(coords[4], coords[5]); 2290 context.closePath(); 2291 }, 2292 2293 create: function() { 2294 // Determine tip corner 2295 var c = this.corner = (HASCANVAS || BROWSER.ie) && this._parseCorner(this.options.corner); 2296 2297 // If we have a tip corner... 2298 this.enabled = !!this.corner && this.corner.abbrev() !== 'c'; 2299 if(this.enabled) { 2300 // Cache it 2301 this.qtip.cache.corner = c.clone(); 2302 2303 // Create it 2304 this.update(); 2305 } 2306 2307 // Toggle tip element 2308 this.element.toggle(this.enabled); 2309 2310 return this.corner; 2311 }, 2312 2313 update: function(corner, position) { 2314 if(!this.enabled) { return this; } 2315 2316 var elements = this.qtip.elements, 2317 tip = this.element, 2318 inner = tip.children(), 2319 options = this.options, 2320 curSize = this.size, 2321 mimic = options.mimic, 2322 round = Math.round, 2323 color, precedance, context, 2324 coords, bigCoords, translate, newSize, border; 2325 2326 // Re-determine tip if not already set 2327 if(!corner) { corner = this.qtip.cache.corner || this.corner; } 2328 2329 // Use corner property if we detect an invalid mimic value 2330 if(mimic === FALSE) { mimic = corner; } 2331 2332 // Otherwise inherit mimic properties from the corner object as necessary 2333 else { 2334 mimic = new CORNER(mimic); 2335 mimic.precedance = corner.precedance; 2336 2337 if(mimic.x === 'inherit') { mimic.x = corner.x; } 2338 else if(mimic.y === 'inherit') { mimic.y = corner.y; } 2339 else if(mimic.x === mimic.y) { 2340 mimic[ corner.precedance ] = corner[ corner.precedance ]; 2341 } 2342 } 2343 precedance = mimic.precedance; 2344 2345 // Ensure the tip width.height are relative to the tip position 2346 if(corner.precedance === X) { this._swapDimensions(); } 2347 else { this._resetDimensions(); } 2348 2349 // Update our colours 2350 color = this.color = this._parseColours(corner); 2351 2352 // Detect border width, taking into account colours 2353 if(color[1] !== TRANSPARENT) { 2354 // Grab border width 2355 border = this.border = this._parseWidth(corner, corner[corner.precedance]); 2356 2357 // If border width isn't zero, use border color as fill if it's not invalid (1.0 style tips) 2358 if(options.border && border < 1 && !INVALID.test(color[1])) { color[0] = color[1]; } 2359 2360 // Set border width (use detected border width if options.border is true) 2361 this.border = border = options.border !== TRUE ? options.border : border; 2362 } 2363 2364 // Border colour was invalid, set border to zero 2365 else { this.border = border = 0; } 2366 2367 // Determine tip size 2368 newSize = this.size = this._calculateSize(corner); 2369 tip.css({ 2370 width: newSize[0], 2371 height: newSize[1], 2372 lineHeight: newSize[1]+'px' 2373 }); 2374 2375 // Calculate tip translation 2376 if(corner.precedance === Y) { 2377 translate = [ 2378 round(mimic.x === LEFT ? border : mimic.x === RIGHT ? newSize[0] - curSize[0] - border : (newSize[0] - curSize[0]) / 2), 2379 round(mimic.y === TOP ? newSize[1] - curSize[1] : 0) 2380 ]; 2381 } 2382 else { 2383 translate = [ 2384 round(mimic.x === LEFT ? newSize[0] - curSize[0] : 0), 2385 round(mimic.y === TOP ? border : mimic.y === BOTTOM ? newSize[1] - curSize[1] - border : (newSize[1] - curSize[1]) / 2) 2386 ]; 2387 } 2388 2389 // Canvas drawing implementation 2390 if(HASCANVAS) { 2391 // Grab canvas context and clear/save it 2392 context = inner[0].getContext('2d'); 2393 context.restore(); context.save(); 2394 context.clearRect(0,0,6000,6000); 2395 2396 // Calculate coordinates 2397 coords = this._calculateTip(mimic, curSize, SCALE); 2398 bigCoords = this._calculateTip(mimic, this.size, SCALE); 2399 2400 // Set the canvas size using calculated size 2401 inner.attr(WIDTH, newSize[0] * SCALE).attr(HEIGHT, newSize[1] * SCALE); 2402 inner.css(WIDTH, newSize[0]).css(HEIGHT, newSize[1]); 2403 2404 // Draw the outer-stroke tip 2405 this._drawCoords(context, bigCoords); 2406 context.fillStyle = color[1]; 2407 context.fill(); 2408 2409 // Draw the actual tip 2410 context.translate(translate[0] * SCALE, translate[1] * SCALE); 2411 this._drawCoords(context, coords); 2412 context.fillStyle = color[0]; 2413 context.fill(); 2414 } 2415 2416 // VML (IE Proprietary implementation) 2417 else { 2418 // Calculate coordinates 2419 coords = this._calculateTip(mimic); 2420 2421 // Setup coordinates string 2422 coords = 'm' + coords[0] + ',' + coords[1] + ' l' + coords[2] + 2423 ',' + coords[3] + ' ' + coords[4] + ',' + coords[5] + ' xe'; 2424 2425 // Setup VML-specific offset for pixel-perfection 2426 translate[2] = border && /^(r|b)/i.test(corner.string()) ? 2427 BROWSER.ie === 8 ? 2 : 1 : 0; 2428 2429 // Set initial CSS 2430 inner.css({ 2431 coordsize: newSize[0]+border + ' ' + newSize[1]+border, 2432 antialias: ''+(mimic.string().indexOf(CENTER) > -1), 2433 left: translate[0] - translate[2] * Number(precedance === X), 2434 top: translate[1] - translate[2] * Number(precedance === Y), 2435 width: newSize[0] + border, 2436 height: newSize[1] + border 2437 }) 2438 .each(function(i) { 2439 var $this = $(this); 2440 2441 // Set shape specific attributes 2442 $this[ $this.prop ? 'prop' : 'attr' ]({ 2443 coordsize: newSize[0]+border + ' ' + newSize[1]+border, 2444 path: coords, 2445 fillcolor: color[0], 2446 filled: !!i, 2447 stroked: !i 2448 }) 2449 .toggle(!!(border || i)); 2450 2451 // Check if border is enabled and add stroke element 2452 !i && $this.html( createVML( 2453 'stroke', 'weight="'+border*2+'px" color="'+color[1]+'" miterlimit="1000" joinstyle="miter"' 2454 ) ); 2455 }); 2456 } 2457 2458 // Opera bug #357 - Incorrect tip position 2459 // https://github.com/Craga89/qTip2/issues/367 2460 window.opera && setTimeout(function() { 2461 elements.tip.css({ 2462 display: 'inline-block', 2463 visibility: 'visible' 2464 }); 2465 }, 1); 2466 2467 // Position if needed 2468 if(position !== FALSE) { this.calculate(corner, newSize); } 2469 }, 2470 2471 calculate: function(corner, size) { 2472 if(!this.enabled) { return FALSE; } 2473 2474 var self = this, 2475 elements = this.qtip.elements, 2476 tip = this.element, 2477 userOffset = this.options.offset, 2478 position = {}, 2479 precedance, corners; 2480 2481 // Inherit corner if not provided 2482 corner = corner || this.corner; 2483 precedance = corner.precedance; 2484 2485 // Determine which tip dimension to use for adjustment 2486 size = size || this._calculateSize(corner); 2487 2488 // Setup corners and offset array 2489 corners = [ corner.x, corner.y ]; 2490 if(precedance === X) { corners.reverse(); } 2491 2492 // Calculate tip position 2493 $.each(corners, function(i, side) { 2494 var b, bc, br; 2495 2496 if(side === CENTER) { 2497 b = precedance === Y ? LEFT : TOP; 2498 position[ b ] = '50%'; 2499 position[MARGIN+'-' + b] = -Math.round(size[ precedance === Y ? 0 : 1 ] / 2) + userOffset; 2500 } 2501 else { 2502 b = self._parseWidth(corner, side, elements.tooltip); 2503 bc = self._parseWidth(corner, side, elements.content); 2504 br = self._parseRadius(corner); 2505 2506 position[ side ] = Math.max(-self.border, i ? bc : userOffset + (br > b ? br : -b)); 2507 } 2508 }); 2509 2510 // Adjust for tip size 2511 position[ corner[precedance] ] -= size[ precedance === X ? 0 : 1 ]; 2512 2513 // Set and return new position 2514 tip.css({ margin: '', top: '', bottom: '', left: '', right: '' }).css(position); 2515 return position; 2516 }, 2517 2518 reposition: function(event, api, pos) { 2519 if(!this.enabled) { return; } 2520 2521 var cache = api.cache, 2522 newCorner = this.corner.clone(), 2523 adjust = pos.adjusted, 2524 method = api.options.position.adjust.method.split(' '), 2525 horizontal = method[0], 2526 vertical = method[1] || method[0], 2527 shift = { left: FALSE, top: FALSE, x: 0, y: 0 }, 2528 offset, css = {}, props; 2529 2530 function shiftflip(direction, precedance, popposite, side, opposite) { 2531 // Horizontal - Shift or flip method 2532 if(direction === SHIFT && newCorner.precedance === precedance && adjust[side] && newCorner[popposite] !== CENTER) { 2533 newCorner.precedance = newCorner.precedance === X ? Y : X; 2534 } 2535 else if(direction !== SHIFT && adjust[side]){ 2536 newCorner[precedance] = newCorner[precedance] === CENTER ? 2537 adjust[side] > 0 ? side : opposite : 2538 newCorner[precedance] === side ? opposite : side; 2539 } 2540 } 2541 2542 function shiftonly(xy, side, opposite) { 2543 if(newCorner[xy] === CENTER) { 2544 css[MARGIN+'-'+side] = shift[xy] = offset[MARGIN+'-'+side] - adjust[side]; 2545 } 2546 else { 2547 props = offset[opposite] !== undefined ? 2548 [ adjust[side], -offset[side] ] : [ -adjust[side], offset[side] ]; 2549 2550 if( (shift[xy] = Math.max(props[0], props[1])) > props[0] ) { 2551 pos[side] -= adjust[side]; 2552 shift[side] = FALSE; 2553 } 2554 2555 css[ offset[opposite] !== undefined ? opposite : side ] = shift[xy]; 2556 } 2557 } 2558 2559 // If our tip position isn't fixed e.g. doesn't adjust with viewport... 2560 if(this.corner.fixed !== TRUE) { 2561 // Perform shift/flip adjustments 2562 shiftflip(horizontal, X, Y, LEFT, RIGHT); 2563 shiftflip(vertical, Y, X, TOP, BOTTOM); 2564 2565 // Update and redraw the tip if needed (check cached details of last drawn tip) 2566 if(newCorner.string() !== cache.corner.string() || cache.cornerTop !== adjust.top || cache.cornerLeft !== adjust.left) { 2567 this.update(newCorner, FALSE); 2568 } 2569 } 2570 2571 // Setup tip offset properties 2572 offset = this.calculate(newCorner); 2573 2574 // Readjust offset object to make it left/top 2575 if(offset.right !== undefined) { offset.left = -offset.right; } 2576 if(offset.bottom !== undefined) { offset.top = -offset.bottom; } 2577 offset.user = this.offset; 2578 2579 // Perform shift adjustments 2580 shift.left = horizontal === SHIFT && !!adjust.left; 2581 if(shift.left) { 2582 shiftonly(X, LEFT, RIGHT); 2583 } 2584 shift.top = vertical === SHIFT && !!adjust.top; 2585 if(shift.top) { 2586 shiftonly(Y, TOP, BOTTOM); 2587 } 2588 2589 /* 2590 * If the tip is adjusted in both dimensions, or in a 2591 * direction that would cause it to be anywhere but the 2592 * outer border, hide it! 2593 */ 2594 this.element.css(css).toggle( 2595 !(shift.x && shift.y || newCorner.x === CENTER && shift.y || newCorner.y === CENTER && shift.x) 2596 ); 2597 2598 // Adjust position to accomodate tip dimensions 2599 pos.left -= offset.left.charAt ? offset.user : 2600 horizontal !== SHIFT || shift.top || !shift.left && !shift.top ? offset.left + this.border : 0; 2601 pos.top -= offset.top.charAt ? offset.user : 2602 vertical !== SHIFT || shift.left || !shift.left && !shift.top ? offset.top + this.border : 0; 2603 2604 // Cache details 2605 cache.cornerLeft = adjust.left; cache.cornerTop = adjust.top; 2606 cache.corner = newCorner.clone(); 2607 }, 2608 2609 destroy: function() { 2610 // Unbind events 2611 this.qtip._unbind(this.qtip.tooltip, this._ns); 2612 2613 // Remove the tip element(s) 2614 if(this.qtip.elements.tip) { 2615 this.qtip.elements.tip.find('*') 2616 .remove().end().remove(); 2617 } 2618 } 2619}); 2620 2621TIP = PLUGINS.tip = function(api) { 2622 return new Tip(api, api.options.style.tip); 2623}; 2624 2625// Initialize tip on render 2626TIP.initialize = 'render'; 2627 2628// Setup plugin sanitization options 2629TIP.sanitize = function(options) { 2630 if(options.style && 'tip' in options.style) { 2631 var opts = options.style.tip; 2632 if(typeof opts !== 'object') { opts = options.style.tip = { corner: opts }; } 2633 if(!(/string|boolean/i).test(typeof opts.corner)) { opts.corner = TRUE; } 2634 } 2635}; 2636 2637// Add new option checks for the plugin 2638CHECKS.tip = { 2639 '^position.my|style.tip.(corner|mimic|border)$': function() { 2640 // Make sure a tip can be drawn 2641 this.create(); 2642 2643 // Reposition the tooltip 2644 this.qtip.reposition(); 2645 }, 2646 '^style.tip.(height|width)$': function(obj) { 2647 // Re-set dimensions and redraw the tip 2648 this.size = [ obj.width, obj.height ]; 2649 this.update(); 2650 2651 // Reposition the tooltip 2652 this.qtip.reposition(); 2653 }, 2654 '^content.title|style.(classes|widget)$': function() { 2655 this.update(); 2656 } 2657}; 2658 2659// Extend original qTip defaults 2660$.extend(TRUE, QTIP.defaults, { 2661 style: { 2662 tip: { 2663 corner: TRUE, 2664 mimic: FALSE, 2665 width: 6, 2666 height: 6, 2667 border: TRUE, 2668 offset: 0 2669 } 2670 } 2671}); 2672;
vendor: 10,110 bytes, lines 2672-2985
2672PLUGINS.viewport = function(api, position, posOptions, targetWidth, targetHeight, elemWidth, elemHeight) 2673{ 2674 var target = posOptions.target, 2675 tooltip = api.elements.tooltip, 2676 my = posOptions.my, 2677 at = posOptions.at, 2678 adjust = posOptions.adjust, 2679 method = adjust.method.split(' '), 2680 methodX = method[0], 2681 methodY = method[1] || method[0], 2682 viewport = posOptions.viewport, 2683 container = posOptions.container, 2684 adjusted = { left: 0, top: 0 }, 2685 fixed, newMy, containerOffset, containerStatic, 2686 viewportWidth, viewportHeight, viewportScroll, viewportOffset; 2687 2688 // If viewport is not a jQuery element, or it's the window/document, or no adjustment method is used... return 2689 if(!viewport.jquery || target[0] === window || target[0] === document.body || adjust.method === 'none') { 2690 return adjusted; 2691 } 2692 2693 // Cach container details 2694 containerOffset = container.offset() || adjusted; 2695 containerStatic = container.css('position') === 'static'; 2696 2697 // Cache our viewport details 2698 fixed = tooltip.css('position') === 'fixed'; 2699 viewportWidth = viewport[0] === window ? viewport.width() : viewport.outerWidth(FALSE); 2700 viewportHeight = viewport[0] === window ? viewport.height() : viewport.outerHeight(FALSE); 2701 viewportScroll = { left: fixed ? 0 : viewport.scrollLeft(), top: fixed ? 0 : viewport.scrollTop() }; 2702 viewportOffset = viewport.offset() || adjusted; 2703 2704 // Generic calculation method 2705 function calculate(side, otherSide, type, adjustment, side1, side2, lengthName, targetLength, elemLength) { 2706 var initialPos = position[side1], 2707 mySide = my[side], 2708 atSide = at[side], 2709 isShift = type === SHIFT, 2710 myLength = mySide === side1 ? elemLength : mySide === side2 ? -elemLength : -elemLength / 2, 2711 atLength = atSide === side1 ? targetLength : atSide === side2 ? -targetLength : -targetLength / 2, 2712 sideOffset = viewportScroll[side1] + viewportOffset[side1] - (containerStatic ? 0 : containerOffset[side1]), 2713 overflow1 = sideOffset - initialPos, 2714 overflow2 = initialPos + elemLength - (lengthName === WIDTH ? viewportWidth : viewportHeight) - sideOffset, 2715 offset = myLength - (my.precedance === side || mySide === my[otherSide] ? atLength : 0) - (atSide === CENTER ? targetLength / 2 : 0); 2716 2717 // shift 2718 if(isShift) { 2719 offset = (mySide === side1 ? 1 : -1) * myLength; 2720 2721 // Adjust position but keep it within viewport dimensions 2722 position[side1] += overflow1 > 0 ? overflow1 : overflow2 > 0 ? -overflow2 : 0; 2723 position[side1] = Math.max( 2724 -containerOffset[side1] + viewportOffset[side1], 2725 initialPos - offset, 2726 Math.min( 2727 Math.max( 2728 -containerOffset[side1] + viewportOffset[side1] + (lengthName === WIDTH ? viewportWidth : viewportHeight), 2729 initialPos + offset 2730 ), 2731 position[side1], 2732 2733 // Make sure we don't adjust complete off the element when using 'center' 2734 mySide === 'center' ? initialPos - myLength : 1E9 2735 ) 2736 ); 2737 2738 } 2739 2740 // flip/flipinvert 2741 else { 2742 // Update adjustment amount depending on if using flipinvert or flip 2743 adjustment *= type === FLIPINVERT ? 2 : 0; 2744 2745 // Check for overflow on the left/top 2746 if(overflow1 > 0 && (mySide !== side1 || overflow2 > 0)) { 2747 position[side1] -= offset + adjustment; 2748 newMy.invert(side, side1); 2749 } 2750 2751 // Check for overflow on the bottom/right 2752 else if(overflow2 > 0 && (mySide !== side2 || overflow1 > 0) ) { 2753 position[side1] -= (mySide === CENTER ? -offset : offset) + adjustment; 2754 newMy.invert(side, side2); 2755 } 2756 2757 // Make sure we haven't made things worse with the adjustment and reset if so 2758 if(position[side1] < viewportScroll[side1] && -position[side1] > overflow2) { 2759 position[side1] = initialPos; newMy = my.clone(); 2760 } 2761 } 2762 2763 return position[side1] - initialPos; 2764 } 2765 2766 // Set newMy if using flip or flipinvert methods 2767 if(methodX !== 'shift' || methodY !== 'shift') { newMy = my.clone(); } 2768 2769 // Adjust position based onviewport and adjustment options 2770 adjusted = { 2771 left: methodX !== 'none' ? calculate( X, Y, methodX, adjust.x, LEFT, RIGHT, WIDTH, targetWidth, elemWidth ) : 0, 2772 top: methodY !== 'none' ? calculate( Y, X, methodY, adjust.y, TOP, BOTTOM, HEIGHT, targetHeight, elemHeight ) : 0, 2773 my: newMy 2774 }; 2775 2776 return adjusted; 2777}; 2778;PLUGINS.polys = { 2779 // POLY area coordinate calculator 2780 // Special thanks to Ed Cradock for helping out with this. 2781 // Uses a binary search algorithm to find suitable coordinates. 2782 polygon: function(baseCoords, corner) { 2783 var result = { 2784 width: 0, height: 0, 2785 position: { 2786 top: 1e10, right: 0, 2787 bottom: 0, left: 1e10 2788 }, 2789 adjustable: FALSE 2790 }, 2791 i = 0, next, 2792 coords = [], 2793 compareX = 1, compareY = 1, 2794 realX = 0, realY = 0, 2795 newWidth, newHeight; 2796 2797 // First pass, sanitize coords and determine outer edges 2798 i = baseCoords.length; 2799 while(i--) { 2800 next = [ parseInt(baseCoords[--i], 10), parseInt(baseCoords[i+1], 10) ]; 2801 2802 if(next[0] > result.position.right){ result.position.right = next[0]; } 2803 if(next[0] < result.position.left){ result.position.left = next[0]; } 2804 if(next[1] > result.position.bottom){ result.position.bottom = next[1]; } 2805 if(next[1] < result.position.top){ result.position.top = next[1]; } 2806 2807 coords.push(next); 2808 } 2809 2810 // Calculate height and width from outer edges 2811 newWidth = result.width = Math.abs(result.position.right - result.position.left); 2812 newHeight = result.height = Math.abs(result.position.bottom - result.position.top); 2813 2814 // If it's the center corner... 2815 if(corner.abbrev() === 'c') { 2816 result.position = { 2817 left: result.position.left + result.width / 2, 2818 top: result.position.top + result.height / 2 2819 }; 2820 } 2821 else { 2822 // Second pass, use a binary search algorithm to locate most suitable coordinate 2823 while(newWidth > 0 && newHeight > 0 && compareX > 0 && compareY > 0) 2824 { 2825 newWidth = Math.floor(newWidth / 2); 2826 newHeight = Math.floor(newHeight / 2); 2827 2828 if(corner.x === LEFT){ compareX = newWidth; } 2829 else if(corner.x === RIGHT){ compareX = result.width - newWidth; } 2830 else{ compareX += Math.floor(newWidth / 2); } 2831 2832 if(corner.y === TOP){ compareY = newHeight; } 2833 else if(corner.y === BOTTOM){ compareY = result.height - newHeight; } 2834 else{ compareY += Math.floor(newHeight / 2); } 2835 2836 i = coords.length; 2837 while(i--) 2838 { 2839 if(coords.length < 2){ break; } 2840 2841 realX = coords[i][0] - result.position.left; 2842 realY = coords[i][1] - result.position.top; 2843 2844 if( 2845 corner.x === LEFT && realX >= compareX || 2846 corner.x === RIGHT && realX <= compareX || 2847 corner.x === CENTER && (realX < compareX || realX > result.width - compareX) || 2848 corner.y === TOP && realY >= compareY || 2849 corner.y === BOTTOM && realY <= compareY || 2850 corner.y === CENTER && (realY < compareY || realY > result.height - compareY)) { 2851 coords.splice(i, 1); 2852 } 2853 } 2854 } 2855 result.position = { left: coords[0][0], top: coords[0][1] }; 2856 } 2857 2858 return result; 2859 }, 2860 2861 rect: function(ax, ay, bx, by) { 2862 return { 2863 width: Math.abs(bx - ax), 2864 height: Math.abs(by - ay), 2865 position: { 2866 left: Math.min(ax, bx), 2867 top: Math.min(ay, by) 2868 } 2869 }; 2870 }, 2871 2872 _angles: { 2873 tc: 3 / 2, tr: 7 / 4, tl: 5 / 4, 2874 bc: 1 / 2, br: 1 / 4, bl: 3 / 4, 2875 rc: 2, lc: 1, c: 0 2876 }, 2877 ellipse: function(cx, cy, rx, ry, corner) { 2878 var c = PLUGINS.polys._angles[ corner.abbrev() ], 2879 rxc = c === 0 ? 0 : rx * Math.cos( c * Math.PI ), 2880 rys = ry * Math.sin( c * Math.PI ); 2881 2882 return { 2883 width: rx * 2 - Math.abs(rxc), 2884 height: ry * 2 - Math.abs(rys), 2885 position: { 2886 left: cx + rxc, 2887 top: cy + rys 2888 }, 2889 adjustable: FALSE 2890 }; 2891 }, 2892 circle: function(cx, cy, r, corner) { 2893 return PLUGINS.polys.ellipse(cx, cy, r, r, corner); 2894 } 2895}; 2896;PLUGINS.svg = function(api, svg, corner) 2897{ 2898 var elem = svg[0], 2899 root = $(elem.ownerSVGElement), 2900 ownerDocument = elem.ownerDocument, 2901 strokeWidth2 = (parseInt(svg.css('stroke-width'), 10) || 0) / 2, 2902 frameOffset, mtx, transformed, 2903 len, next, i, points, 2904 result, position; 2905 2906 // Ascend the parentNode chain until we find an element with getBBox() 2907 while(!elem.getBBox) { elem = elem.parentNode; } 2908 if(!elem.getBBox || !elem.parentNode) { return FALSE; } 2909 2910 // Determine which shape calculation to use 2911 switch(elem.nodeName) { 2912 case 'ellipse': 2913 case 'circle': 2914 result = PLUGINS.polys.ellipse( 2915 elem.cx.baseVal.value, 2916 elem.cy.baseVal.value, 2917 (elem.rx || elem.r).baseVal.value + strokeWidth2, 2918 (elem.ry || elem.r).baseVal.value + strokeWidth2, 2919 corner 2920 ); 2921 break; 2922 2923 case 'line': 2924 case 'polygon': 2925 case 'polyline': 2926 // Determine points object (line has none, so mimic using array) 2927 points = elem.points || [ 2928 { x: elem.x1.baseVal.value, y: elem.y1.baseVal.value }, 2929 { x: elem.x2.baseVal.value, y: elem.y2.baseVal.value } 2930 ]; 2931 2932 for(result = [], i = -1, len = points.numberOfItems || points.length; ++i < len;) { 2933 next = points.getItem ? points.getItem(i) : points[i]; 2934 result.push.apply(result, [next.x, next.y]); 2935 } 2936 2937 result = PLUGINS.polys.polygon(result, corner); 2938 break; 2939 2940 // Unknown shape or rectangle? Use bounding box 2941 default: 2942 result = elem.getBBox(); 2943 result = { 2944 width: result.width, 2945 height: result.height, 2946 position: { 2947 left: result.x, 2948 top: result.y 2949 } 2950 }; 2951 break; 2952 } 2953 2954 // Shortcut assignments 2955 position = result.position; 2956 root = root[0]; 2957 2958 // Convert position into a pixel value 2959 if(root.createSVGPoint) { 2960 mtx = elem.getScreenCTM(); 2961 points = root.createSVGPoint(); 2962 2963 points.x = position.left; 2964 points.y = position.top; 2965 transformed = points.matrixTransform( mtx ); 2966 position.left = transformed.x; 2967 position.top = transformed.y; 2968 } 2969 2970 // Check the element is not in a child document, and if so, adjust for frame elements offset 2971 if(ownerDocument !== document && api.position.target !== 'mouse') { 2972 frameOffset = $((ownerDocument.defaultView || ownerDocument.parentWindow).frameElement).offset(); 2973 if(frameOffset) { 2974 position.left += frameOffset.left; 2975 position.top += frameOffset.top; 2976 } 2977 } 2978 2979 // Adjust by scroll offset of owner document 2980 ownerDocument = $(ownerDocument); 2981 position.left += ownerDocument.scrollLeft(); 2982 position.top += ownerDocument.scrollTop(); 2983 2984 return result; 2985}
vendor: 9,204 bytes, lines 2985-3324
2985; 2986;var MODAL, OVERLAY, 2987 MODALCLASS = 'qtip-modal', 2988 MODALSELECTOR = '.'+MODALCLASS; 2989 2990OVERLAY = function() 2991{ 2992 var self = this, 2993 focusableElems = {}, 2994 current, 2995 prevState, 2996 elem; 2997 2998 // Modified code from jQuery UI 1.10.0 source 2999 // http://code.jquery.com/ui/1.10.0/jquery-ui.js 3000 function focusable(element) { 3001 // Use the defined focusable checker when possible 3002 if($.expr[':'].focusable) { return $.expr[':'].focusable; } 3003 3004 var isTabIndexNotNaN = !isNaN($.attr(element, 'tabindex')), 3005 nodeName = element.nodeName && element.nodeName.toLowerCase(), 3006 map, mapName, img; 3007 3008 if('area' === nodeName) { 3009 map = element.parentNode; 3010 mapName = map.name; 3011 if(!element.href || !mapName || map.nodeName.toLowerCase() !== 'map') { 3012 return false; 3013 } 3014 img = $('img[usemap=#' + mapName + ']')[0]; 3015 return !!img && img.is(':visible'); 3016 } 3017 3018 return /input|select|textarea|button|object/.test( nodeName ) ? 3019 !element.disabled : 3020 'a' === nodeName ? 3021 element.href || isTabIndexNotNaN : 3022 isTabIndexNotNaN 3023 ; 3024 } 3025 3026 // Focus inputs using cached focusable elements (see update()) 3027 function focusInputs(blurElems) { 3028 // Blurring body element in IE causes window.open windows to unfocus! 3029 if(focusableElems.length < 1 && blurElems.length) { blurElems.not('body').blur(); } 3030 3031 // Focus the inputs 3032 else { focusableElems.first().focus(); } 3033 } 3034 3035 // Steal focus from elements outside tooltip 3036 function stealFocus(event) { 3037 if(!elem.is(':visible')) { return; } 3038 3039 var target = $(event.target), 3040 tooltip = current.tooltip, 3041 container = target.closest(SELECTOR), 3042 targetOnTop; 3043 3044 // Determine if input container target is above this 3045 targetOnTop = container.length < 1 ? FALSE : 3046 parseInt(container[0].style.zIndex, 10) > parseInt(tooltip[0].style.zIndex, 10); 3047 3048 // If we're showing a modal, but focus has landed on an input below 3049 // this modal, divert focus to the first visible input in this modal 3050 // or if we can't find one... the tooltip itself 3051 if(!targetOnTop && target.closest(SELECTOR)[0] !== tooltip[0]) { 3052 focusInputs(target); 3053 } 3054 } 3055 3056 $.extend(self, { 3057 init: function() { 3058 // Create document overlay 3059 elem = self.elem = $('<div />', { 3060 id: 'qtip-overlay', 3061 html: '<div></div>', 3062 mousedown: function() { return FALSE; } 3063 }) 3064 .hide(); 3065 3066 // Make sure we can't focus anything outside the tooltip 3067 $(document.body).bind('focusin'+MODALSELECTOR, stealFocus); 3068 3069 // Apply keyboard "Escape key" close handler 3070 $(document).bind('keydown'+MODALSELECTOR, function(event) { 3071 if(current && current.options.show.modal.escape && event.keyCode === 27) { 3072 current.hide(event); 3073 } 3074 }); 3075 3076 // Apply click handler for blur option 3077 elem.bind('click'+MODALSELECTOR, function(event) { 3078 if(current && current.options.show.modal.blur) { 3079 current.hide(event); 3080 } 3081 }); 3082 3083 return self; 3084 }, 3085 3086 update: function(api) { 3087 // Update current API reference 3088 current = api; 3089 3090 // Update focusable elements if enabled 3091 if(api.options.show.modal.stealfocus !== FALSE) { 3092 focusableElems = api.tooltip.find('*').filter(function() { 3093 return focusable(this); 3094 }); 3095 } 3096 else { focusableElems = []; } 3097 }, 3098 3099 toggle: function(api, state, duration) { 3100 var tooltip = api.tooltip, 3101 options = api.options.show.modal, 3102 effect = options.effect, 3103 type = state ? 'show': 'hide', 3104 visible = elem.is(':visible'), 3105 visibleModals = $(MODALSELECTOR).filter(':visible:not(:animated)').not(tooltip); 3106 3107 // Set active tooltip API reference 3108 self.update(api); 3109 3110 // If the modal can steal the focus... 3111 // Blur the current item and focus anything in the modal we an 3112 if(state && options.stealfocus !== FALSE) { 3113 focusInputs( $(':focus') ); 3114 } 3115 3116 // Toggle backdrop cursor style on show 3117 elem.toggleClass('blurs', options.blur); 3118 3119 // Append to body on show 3120 if(state) { 3121 elem.appendTo(document.body); 3122 } 3123 3124 // Prevent modal from conflicting with show.solo, and don't hide backdrop is other modals are visible 3125 if(elem.is(':animated') && visible === state && prevState !== FALSE || !state && visibleModals.length) { 3126 return self; 3127 } 3128 3129 // Stop all animations 3130 elem.stop(TRUE, FALSE); 3131 3132 // Use custom function if provided 3133 if($.isFunction(effect)) { 3134 effect.call(elem, state); 3135 } 3136 3137 // If no effect type is supplied, use a simple toggle 3138 else if(effect === FALSE) { 3139 elem[ type ](); 3140 } 3141 3142 // Use basic fade function 3143 else { 3144 elem.fadeTo( parseInt(duration, 10) || 90, state ? 1 : 0, function() { 3145 if(!state) { elem.hide(); } 3146 }); 3147 } 3148 3149 // Reset position and detach from body on hide 3150 if(!state) { 3151 elem.queue(function(next) { 3152 elem.css({ left: '', top: '' }); 3153 if(!$(MODALSELECTOR).length) { elem.detach(); } 3154 next(); 3155 }); 3156 } 3157 3158 // Cache the state 3159 prevState = state; 3160 3161 // If the tooltip is destroyed, set reference to null 3162 if(current.destroyed) { current = NULL; } 3163 3164 return self; 3165 } 3166 }); 3167 3168 self.init(); 3169}; 3170OVERLAY = new OVERLAY(); 3171 3172function Modal(api, options) { 3173 this.options = options; 3174 this._ns = '-modal'; 3175 3176 this.qtip = api; 3177 this.init(api); 3178} 3179 3180$.extend(Modal.prototype, { 3181 init: function(qtip) { 3182 var tooltip = qtip.tooltip; 3183 3184 // If modal is disabled... return 3185 if(!this.options.on) { return this; } 3186 3187 // Set overlay reference 3188 qtip.elements.overlay = OVERLAY.elem; 3189 3190 // Add unique attribute so we can grab modal tooltips easily via a SELECTOR, and set z-index 3191 tooltip.addClass(MODALCLASS).css('z-index', QTIP.modal_zindex + $(MODALSELECTOR).length); 3192 3193 // Apply our show/hide/focus modal events 3194 qtip._bind(tooltip, ['tooltipshow', 'tooltiphide'], function(event, api, duration) { 3195 var oEvent = event.originalEvent; 3196 3197 // Make sure mouseout doesn't trigger a hide when showing the modal and mousing onto backdrop 3198 if(event.target === tooltip[0]) { 3199 if(oEvent && event.type === 'tooltiphide' && /mouse(leave|enter)/.test(oEvent.type) && $(oEvent.relatedTarget).closest(OVERLAY.elem[0]).length) { 3200 /* eslint-disable no-empty */ 3201 try { event.preventDefault(); } 3202 catch(e) {} 3203 /* eslint-enable no-empty */ 3204 } 3205 else if(!oEvent || oEvent && oEvent.type !== 'tooltipsolo') { 3206 this.toggle(event, event.type === 'tooltipshow', duration); 3207 } 3208 } 3209 }, this._ns, this); 3210 3211 // Adjust modal z-index on tooltip focus 3212 qtip._bind(tooltip, 'tooltipfocus', function(event, api) { 3213 // If focus was cancelled before it reached us, don't do anything 3214 if(event.isDefaultPrevented() || event.target !== tooltip[0]) { return; } 3215 3216 var qtips = $(MODALSELECTOR), 3217 3218 // Keep the modal's lower than other, regular qtips 3219 newIndex = QTIP.modal_zindex + qtips.length, 3220 curIndex = parseInt(tooltip[0].style.zIndex, 10); 3221 3222 // Set overlay z-index 3223 OVERLAY.elem[0].style.zIndex = newIndex - 1; 3224 3225 // Reduce modal z-index's and keep them properly ordered 3226 qtips.each(function() { 3227 if(this.style.zIndex > curIndex) { 3228 this.style.zIndex -= 1; 3229 } 3230 }); 3231 3232 // Fire blur event for focused tooltip 3233 qtips.filter('.' + CLASS_FOCUS).qtip('blur', event.originalEvent); 3234 3235 // Set the new z-index 3236 tooltip.addClass(CLASS_FOCUS)[0].style.zIndex = newIndex; 3237 3238 // Set current 3239 OVERLAY.update(api); 3240 3241 // Prevent default handling 3242 /* eslint-disable no-empty */ 3243 try { event.preventDefault(); } 3244 catch(e) {} 3245 /* eslint-enable no-empty */ 3246 }, this._ns, this); 3247 3248 // Focus any other visible modals when this one hides 3249 qtip._bind(tooltip, 'tooltiphide', function(event) { 3250 if(event.target === tooltip[0]) { 3251 $(MODALSELECTOR).filter(':visible').not(tooltip).last().qtip('focus', event); 3252 } 3253 }, this._ns, this); 3254 }, 3255 3256 toggle: function(event, state, duration) { 3257 // Make sure default event hasn't been prevented 3258 if(event && event.isDefaultPrevented()) { return this; } 3259 3260 // Toggle it 3261 OVERLAY.toggle(this.qtip, !!state, duration); 3262 }, 3263 3264 destroy: function() { 3265 // Remove modal class 3266 this.qtip.tooltip.removeClass(MODALCLASS); 3267 3268 // Remove bound events 3269 this.qtip._unbind(this.qtip.tooltip, this._ns); 3270 3271 // Delete element reference 3272 OVERLAY.toggle(this.qtip, FALSE); 3273 delete this.qtip.elements.overlay; 3274 } 3275}); 3276 3277 3278MODAL = PLUGINS.modal = function(api) { 3279 return new Modal(api, api.options.show.modal); 3280}; 3281 3282// Setup sanitiztion rules 3283MODAL.sanitize = function(opts) { 3284 if(opts.show) { 3285 if(typeof opts.show.modal !== 'object') { opts.show.modal = { on: !!opts.show.modal }; } 3286 else if(typeof opts.show.modal.on === 'undefined') { opts.show.modal.on = TRUE; } 3287 } 3288}; 3289 3290// Base z-index for all modal tooltips (use qTip core z-index as a base) 3291/* eslint-disable camelcase */ 3292QTIP.modal_zindex = QTIP.zindex - 200; 3293/* eslint-enable camelcase */ 3294 3295// Plugin needs to be initialized on render 3296MODAL.initialize = 'render'; 3297 3298// Setup option set checks 3299CHECKS.modal = { 3300 '^show.modal.(on|blur)$': function() { 3301 // Initialise 3302 this.destroy(); 3303 this.init(); 3304 3305 // Show the modal if not visible already and tooltip is visible 3306 this.qtip.elems.overlay.toggle( 3307 this.qtip.tooltip[0].offsetWidth > 0 3308 ); 3309 } 3310}; 3311 3312// Extend original api defaults 3313$.extend(TRUE, QTIP.defaults, { 3314 show: { 3315 modal: { 3316 on: FALSE, 3317 effect: TRUE, 3318 blur: TRUE, 3319 stealfocus: TRUE, 3320 escape: TRUE 3321 } 3322 } 3323}); 3324;
3324var IE6, 3325 3326/* 3327 * BGIFrame adaption (http://plugins.jquery.com/project/bgiframe) 3328 * Special thanks to Brandon Aaron 3329 */ 3330BGIFRAME = '<iframe class="qtip-bgiframe" frameborder="0" tabindex="-1" src="javascript:\'\';" ' + 3331 ' style="display:block; position:absolute; z-index:-1; filter:alpha(opacity=0); ' + 3332 '-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=0)";"></iframe>'; 3333 3334function Ie6(api) { 3335 this._ns = 'ie6'; 3336 3337 this.qtip = api;
vendor: 3,592 bytes, lines 3338-3465
3338 this.init(api); 3339} 3340 3341$.extend(Ie6.prototype, { 3342 _scroll : function() { 3343 var overlay = this.qtip.elements.overlay; 3344 overlay && (overlay[0].style.top = $(window).scrollTop() + 'px'); 3345 }, 3346 3347 init: function(qtip) { 3348 var tooltip = qtip.tooltip; 3349 3350 // Create the BGIFrame element if needed 3351 if($('select, object').length < 1) { 3352 this.bgiframe = qtip.elements.bgiframe = $(BGIFRAME).appendTo(tooltip); 3353 3354 // Update BGIFrame on tooltip move 3355 qtip._bind(tooltip, 'tooltipmove', this.adjustBGIFrame, this._ns, this); 3356 } 3357 3358 // redraw() container for width/height calculations 3359 this.redrawContainer = $('<div/>', { id: NAMESPACE+'-rcontainer' }) 3360 .appendTo(document.body); 3361 3362 // Fixup modal plugin if present too 3363 if( qtip.elements.overlay && qtip.elements.overlay.addClass('qtipmodal-ie6fix') ) { 3364 qtip._bind(window, ['scroll', 'resize'], this._scroll, this._ns, this); 3365 qtip._bind(tooltip, ['tooltipshow'], this._scroll, this._ns, this); 3366 } 3367 3368 // Set dimensions 3369 this.redraw(); 3370 }, 3371 3372 adjustBGIFrame: function() { 3373 var tooltip = this.qtip.tooltip, 3374 dimensions = { 3375 height: tooltip.outerHeight(FALSE), 3376 width: tooltip.outerWidth(FALSE) 3377 }, 3378 plugin = this.qtip.plugins.tip, 3379 tip = this.qtip.elements.tip, 3380 tipAdjust, offset; 3381 3382 // Adjust border offset 3383 offset = parseInt(tooltip.css('borderLeftWidth'), 10) || 0; 3384 offset = { left: -offset, top: -offset }; 3385 3386 // Adjust for tips plugin 3387 if(plugin && tip) { 3388 tipAdjust = plugin.corner.precedance === 'x' ? [WIDTH, LEFT] : [HEIGHT, TOP]; 3389 offset[ tipAdjust[1] ] -= tip[ tipAdjust[0] ](); 3390 } 3391 3392 // Update bgiframe 3393 this.bgiframe.css(offset).css(dimensions); 3394 }, 3395 3396 // Max/min width simulator function 3397 redraw: function() { 3398 if(this.qtip.rendered < 1 || this.drawing) { return this; } 3399 3400 var tooltip = this.qtip.tooltip, 3401 style = this.qtip.options.style, 3402 container = this.qtip.options.position.container, 3403 perc, width, max, min; 3404 3405 // Set drawing flag 3406 this.qtip.drawing = 1; 3407 3408 // If tooltip has a set height/width, just set it... like a boss! 3409 if(style.height) { tooltip.css(HEIGHT, style.height); } 3410 if(style.width) { tooltip.css(WIDTH, style.width); } 3411 3412 // Simulate max/min width if not set width present... 3413 else { 3414 // Reset width and add fluid class 3415 tooltip.css(WIDTH, '').appendTo(this.redrawContainer); 3416 3417 // Grab our tooltip width (add 1 if odd so we don't get wrapping problems.. huzzah!) 3418 width = tooltip.width(); 3419 if(width % 2 < 1) { width += 1; } 3420 3421 // Grab our max/min properties 3422 max = tooltip.css('maxWidth') || ''; 3423 min = tooltip.css('minWidth') || ''; 3424 3425 // Parse into proper pixel values 3426 perc = (max + min).indexOf('%') > -1 ? container.width() / 100 : 0; 3427 max = (max.indexOf('%') > -1 ? perc : 1 * parseInt(max, 10)) || width; 3428 min = (min.indexOf('%') > -1 ? perc : 1 * parseInt(min, 10)) || 0; 3429 3430 // Determine new dimension size based on max/min/current values 3431 width = max + min ? Math.min(Math.max(width, min), max) : width; 3432 3433 // Set the newly calculated width and remvoe fluid class 3434 tooltip.css(WIDTH, Math.round(width)).appendTo(container); 3435 } 3436 3437 // Set drawing flag 3438 this.drawing = 0; 3439 3440 return this; 3441 }, 3442 3443 destroy: function() { 3444 // Remove iframe 3445 this.bgiframe && this.bgiframe.remove(); 3446 3447 // Remove bound events 3448 this.qtip._unbind([window, this.qtip.tooltip], this._ns); 3449 } 3450}); 3451 3452IE6 = PLUGINS.ie6 = function(api) { 3453 // Proceed only if the browser is IE6 3454 return BROWSER.ie === 6 ? new Ie6(api) : FALSE; 3455}; 3456 3457IE6.initialize = 'render'; 3458 3459CHECKS.ie6 = { 3460 '^content|style$': function() { 3461 this.redraw(); 3462 } 3463}; 3464;})); 3465}( window, document ));
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.