vendor: 3,652 bytes, lines 1-141
1/* 2 * qTip2 - Pretty powerful tooltips - v2.2.1 3 * http://qtip2.com 4 * 5 * Copyright (c) 2014 6 * Released under the MIT licenses 7 * http://jquery.org/license 8 * 9 * Date: Sat Sep 6 2014 09:55 EDT-0400 10 * Plugins: tips viewport 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 for ( 82 var v = 4, i = document.createElement("div"); 83 (i.innerHTML = "<!--[if gt IE " + v + "]><i></i><![endif]-->") && i.getElementsByTagName("i")[0]; 84 v+=1 85 ) {} 86 return v > 4 ? v : NaN; 87 }()), 88 89 /* 90 * iOS version detection 91 */ 92 iOS: parseFloat( 93 ('' + (/CPU.*OS ([0-9_]{1,5})|(CPU like).*AppleWebKit.*Mobile/i.exec(navigator.userAgent) || [0,''])[1]) 94 .replace('undefined', '3_2').replace('_', '.').replace('_', '') 95 ) || FALSE 96}; 97;function QTip(target, options, id, attr) { 98 // Elements and ID 99 this.id = id; 100 this.target = target; 101 this.tooltip = NULL; 102 this.elements = { target: target }; 103 104 // Internal constructs 105 this._id = NAMESPACE + '-' + id; 106 this.timers = { img: {} }; 107 this.options = options; 108 this.plugins = {}; 109 110 // Cache object 111 this.cache = { 112 event: {}, 113 target: $(), 114 disabled: FALSE, 115 attr: attr, 116 onTooltip: FALSE, 117 lastClass: '' 118 }; 119 120 // Set the initial flags 121 this.rendered = this.destroyed = this.disabled = this.waiting = 122 this.hiddenDuringWait = this.positioning = this.triggering = FALSE; 123} 124PROTOTYPE = QTip.prototype; 125 126PROTOTYPE._when = function(deferreds) { 127 return $.when.apply($, deferreds); 128}; 129 130PROTOTYPE.render = function(show) { 131 if(this.rendered || this.destroyed) { return this; } // If tooltip has already been rendered, exit 132 133 var self = this, 134 options = this.options, 135 cache = this.cache, 136 elements = this.elements, 137 text = options.content.text, 138 title = options.content.title, 139 button = options.content.button, 140 posOptions = options.position, 141 namespace = '.'+this._id+' ',
vendor: 4,287 bytes, lines 142-302
142 deferreds = [], 143 tooltip; 144 145 // Add ARIA attributes to target 146 $.attr(this.target[0], 'aria-describedby', this._id); 147 148 // Create public position object that tracks current position corners 149 cache.posClass = this._createPosClass( 150 (this.position = { my: posOptions.my, at: posOptions.at }).my 151 ); 152 153 // Create tooltip element 154 this.tooltip = elements.tooltip = tooltip = $('<div/>', { 155 'id': this._id, 156 'class': [ NAMESPACE, CLASS_DEFAULT, options.style.classes, cache.posClass ].join(' '), 157 'width': options.style.width || '', 158 'height': options.style.height || '', 159 'tracking': posOptions.target === 'mouse' && posOptions.adjust.mouse, 160 161 /* ARIA specific attributes */ 162 'role': 'alert', 163 'aria-live': 'polite', 164 'aria-atomic': FALSE, 165 'aria-describedby': this._id + '-content', 166 'aria-hidden': TRUE 167 }) 168 .toggleClass(CLASS_DISABLED, this.disabled) 169 .attr(ATTR_ID, this.id) 170 .data(NAMESPACE, this) 171 .appendTo(posOptions.container) 172 .append( 173 // Create content element 174 elements.content = $('<div />', { 175 'class': NAMESPACE + '-content', 176 'id': this._id + '-content', 177 'aria-atomic': TRUE 178 }) 179 ); 180 181 // Set rendered flag and prevent redundant reposition calls for now 182 this.rendered = -1; 183 this.positioning = TRUE; 184 185 // Create title... 186 if(title) { 187 this._createTitle(); 188 189 // Update title only if its not a callback (called in toggle if so) 190 if(!$.isFunction(title)) { 191 deferreds.push( this._updateTitle(title, FALSE) ); 192 } 193 } 194 195 // Create button 196 if(button) { this._createButton(); } 197 198 // Set proper rendered flag and update content if not a callback function (called in toggle) 199 if(!$.isFunction(text)) { 200 deferreds.push( this._updateContent(text, FALSE) ); 201 } 202 this.rendered = TRUE; 203 204 // Setup widget classes 205 this._setWidget(); 206 207 // Initialize 'render' plugins 208 $.each(PLUGINS, function(name) { 209 var instance; 210 if(this.initialize === 'render' && (instance = this(self))) { 211 self.plugins[name] = instance; 212 } 213 }); 214 215 // Unassign initial events and assign proper events 216 this._unassignEvents(); 217 this._assignEvents(); 218 219 // When deferreds have completed 220 this._when(deferreds).then(function() { 221 // tooltiprender event 222 self._trigger('render'); 223 224 // Reset flags 225 self.positioning = FALSE; 226 227 // Show tooltip if not hidden during wait period 228 if(!self.hiddenDuringWait && (options.show.ready || show)) { 229 self.toggle(TRUE, cache.event, FALSE); 230 } 231 self.hiddenDuringWait = FALSE; 232 }); 233 234 // Expose API 235 QTIP.api[this.id] = this; 236 237 return this; 238}; 239 240PROTOTYPE.destroy = function(immediate) { 241 // Set flag the signify destroy is taking place to plugins 242 // and ensure it only gets destroyed once! 243 if(this.destroyed) { return this.target; } 244 245 function process() { 246 if(this.destroyed) { return; } 247 this.destroyed = TRUE; 248 249 var target = this.target, 250 title = target.attr(oldtitle), 251 timer; 252 253 // Destroy tooltip if rendered 254 if(this.rendered) { 255 this.tooltip.stop(1,0).find('*').remove().end().remove(); 256 } 257 258 // Destroy all plugins 259 $.each(this.plugins, function(name) { 260 this.destroy && this.destroy(); 261 }); 262 263 // Clear timers 264 for(timer in this.timers) { 265 clearTimeout(this.timers[timer]); 266 } 267 268 // Remove api object and ARIA attributes 269 target.removeData(NAMESPACE) 270 .removeAttr(ATTR_ID) 271 .removeAttr(ATTR_HAS) 272 .removeAttr('aria-describedby'); 273 274 // Reset old title attribute if removed 275 if(this.options.suppress && title) { 276 target.attr('title', title).removeAttr(oldtitle); 277 } 278 279 // Remove qTip events associated with this API 280 this._unassignEvents(); 281 282 // Remove ID from used id objects, and delete object references 283 // for better garbage collection and leak protection 284 this.options = this.elements = this.cache = this.timers = 285 this.plugins = this.mouse = NULL; 286 287 // Delete epoxsed API object 288 delete QTIP.api[this.id]; 289 } 290 291 // If an immediate destory is needed 292 if((immediate !== TRUE || this.triggering === 'hide') && this.rendered) { 293 this.tooltip.one('tooltiphidden', $.proxy(process, this)); 294 !this.triggering && this.hide(); 295 } 296 297 // If we're not in the process of hiding... process 298 else { process.call(this); } 299 300 return this.target; 301}; 302;
vendor: 13,798 bytes, lines 302-773
302function invalidOpt(a) { 303 return a === NULL || $.type(a) !== 'object'; 304} 305 306function invalidContent(c) { 307 return !( $.isFunction(c) || (c && c.attr) || c.length || ($.type(c) === 'object' && (c.jquery || c.then) )); 308} 309 310// Option object sanitizer 311function sanitizeOptions(opts) { 312 var content, text, ajax, once; 313 314 if(invalidOpt(opts)) { return FALSE; } 315 316 if(invalidOpt(opts.metadata)) { 317 opts.metadata = { type: opts.metadata }; 318 } 319 320 if('content' in opts) { 321 content = opts.content; 322 323 if(invalidOpt(content) || content.jquery || content.done) { 324 content = opts.content = { 325 text: (text = invalidContent(content) ? FALSE : content) 326 }; 327 } 328 else { text = content.text; } 329 330 // DEPRECATED - Old content.ajax plugin functionality 331 // Converts it into the proper Deferred syntax 332 if('ajax' in content) { 333 ajax = content.ajax; 334 once = ajax && ajax.once !== FALSE; 335 delete content.ajax; 336 337 content.text = function(event, api) { 338 var loading = text || $(this).attr(api.options.content.attr) || 'Loading...', 339 340 deferred = $.ajax( 341 $.extend({}, ajax, { context: api }) 342 ) 343 .then(ajax.success, NULL, ajax.error) 344 .then(function(content) { 345 if(content && once) { api.set('content.text', content); } 346 return content; 347 }, 348 function(xhr, status, error) { 349 if(api.destroyed || xhr.status === 0) { return; } 350 api.set('content.text', status + ': ' + error); 351 }); 352 353 return !once ? (api.set('content.text', loading), deferred) : loading; 354 }; 355 } 356 357 if('title' in content) { 358 if($.isPlainObject(content.title)) { 359 content.button = content.title.button; 360 content.title = content.title.text; 361 } 362 363 if(invalidContent(content.title || FALSE)) { 364 content.title = FALSE; 365 } 366 } 367 } 368 369 if('position' in opts && invalidOpt(opts.position)) { 370 opts.position = { my: opts.position, at: opts.position }; 371 } 372 373 if('show' in opts && invalidOpt(opts.show)) { 374 opts.show = opts.show.jquery ? { target: opts.show } : 375 opts.show === TRUE ? { ready: TRUE } : { event: opts.show }; 376 } 377 378 if('hide' in opts && invalidOpt(opts.hide)) { 379 opts.hide = opts.hide.jquery ? { target: opts.hide } : { event: opts.hide }; 380 } 381 382 if('style' in opts && invalidOpt(opts.style)) { 383 opts.style = { classes: opts.style }; 384 } 385 386 // Sanitize plugin options 387 $.each(PLUGINS, function() { 388 this.sanitize && this.sanitize(opts); 389 }); 390 391 return opts; 392} 393 394// Setup builtin .set() option checks 395CHECKS = PROTOTYPE.checks = { 396 builtin: { 397 // Core checks 398 '^id$': function(obj, o, v, prev) { 399 var id = v === TRUE ? QTIP.nextid : v, 400 new_id = NAMESPACE + '-' + id; 401 402 if(id !== FALSE && id.length > 0 && !$('#'+new_id).length) { 403 this._id = new_id; 404 405 if(this.rendered) { 406 this.tooltip[0].id = this._id; 407 this.elements.content[0].id = this._id + '-content'; 408 this.elements.title[0].id = this._id + '-title'; 409 } 410 } 411 else { obj[o] = prev; } 412 }, 413 '^prerender': function(obj, o, v) { 414 v && !this.rendered && this.render(this.options.show.ready); 415 }, 416 417 // Content checks 418 '^content.text$': function(obj, o, v) { 419 this._updateContent(v); 420 }, 421 '^content.attr$': function(obj, o, v, prev) { 422 if(this.options.content.text === this.target.attr(prev)) { 423 this._updateContent( this.target.attr(v) ); 424 } 425 }, 426 '^content.title$': function(obj, o, v) { 427 // Remove title if content is null 428 if(!v) { return this._removeTitle(); } 429 430 // If title isn't already created, create it now and update 431 v && !this.elements.title && this._createTitle(); 432 this._updateTitle(v); 433 }, 434 '^content.button$': function(obj, o, v) { 435 this._updateButton(v); 436 }, 437 '^content.title.(text|button)$': function(obj, o, v) { 438 this.set('content.'+o, v); // Backwards title.text/button compat 439 }, 440 441 // Position checks 442 '^position.(my|at)$': function(obj, o, v){ 443 'string' === typeof v && (this.position[o] = obj[o] = new CORNER(v, o === 'at')); 444 }, 445 '^position.container$': function(obj, o, v){ 446 this.rendered && this.tooltip.appendTo(v); 447 }, 448 449 // Show checks 450 '^show.ready$': function(obj, o, v) { 451 v && (!this.rendered && this.render(TRUE) || this.toggle(TRUE)); 452 }, 453 454 // Style checks 455 '^style.classes$': function(obj, o, v, p) { 456 this.rendered && this.tooltip.removeClass(p).addClass(v); 457 }, 458 '^style.(width|height)': function(obj, o, v) { 459 this.rendered && this.tooltip.css(o, v); 460 }, 461 '^style.widget|content.title': function() { 462 this.rendered && this._setWidget(); 463 }, 464 '^style.def': function(obj, o, v) { 465 this.rendered && this.tooltip.toggleClass(CLASS_DEFAULT, !!v); 466 }, 467 468 // Events check 469 '^events.(render|show|move|hide|focus|blur)$': function(obj, o, v) { 470 this.rendered && this.tooltip[($.isFunction(v) ? '' : 'un') + 'bind']('tooltip'+o, v); 471 }, 472 473 // Properties which require event reassignment 474 '^(show|hide|position).(event|target|fixed|inactive|leave|distance|viewport|adjust)': function() { 475 if(!this.rendered) { return; } 476 477 // Set tracking flag 478 var posOptions = this.options.position; 479 this.tooltip.attr('tracking', posOptions.target === 'mouse' && posOptions.adjust.mouse); 480 481 // Reassign events 482 this._unassignEvents(); 483 this._assignEvents(); 484 } 485 } 486}; 487 488// Dot notation converter 489function convertNotation(options, notation) { 490 var i = 0, obj, option = options, 491 492 // Split notation into array 493 levels = notation.split('.'); 494 495 // Loop through 496 while( option = option[ levels[i++] ] ) { 497 if(i < levels.length) { obj = option; } 498 } 499 500 return [obj || options, levels.pop()]; 501} 502 503PROTOTYPE.get = function(notation) { 504 if(this.destroyed) { return this; } 505 506 var o = convertNotation(this.options, notation.toLowerCase()), 507 result = o[0][ o[1] ]; 508 509 return result.precedance ? result.string() : result; 510}; 511 512function setCallback(notation, args) { 513 var category, rule, match; 514 515 for(category in this.checks) { 516 for(rule in this.checks[category]) { 517 if(match = (new RegExp(rule, 'i')).exec(notation)) { 518 args.push(match); 519 520 if(category === 'builtin' || this.plugins[category]) { 521 this.checks[category][rule].apply( 522 this.plugins[category] || this, args 523 ); 524 } 525 } 526 } 527 } 528} 529 530var rmove = /^position\.(my|at|adjust|target|container|viewport)|style|content|show\.ready/i, 531 rrender = /^prerender|show\.ready/i; 532 533PROTOTYPE.set = function(option, value) { 534 if(this.destroyed) { return this; } 535 536 var rendered = this.rendered, 537 reposition = FALSE, 538 options = this.options, 539 checks = this.checks, 540 name; 541 542 // Convert singular option/value pair into object form 543 if('string' === typeof option) { 544 name = option; option = {}; option[name] = value; 545 } 546 else { option = $.extend({}, option); } 547 548 // Set all of the defined options to their new values 549 $.each(option, function(notation, value) { 550 if(rendered && rrender.test(notation)) { 551 delete option[notation]; return; 552 } 553 554 // Set new obj value 555 var obj = convertNotation(options, notation.toLowerCase()), previous; 556 previous = obj[0][ obj[1] ]; 557 obj[0][ obj[1] ] = value && value.nodeType ? $(value) : value; 558 559 // Also check if we need to reposition 560 reposition = rmove.test(notation) || reposition; 561 562 // Set the new params for the callback 563 option[notation] = [obj[0], obj[1], value, previous]; 564 }); 565 566 // Re-sanitize options 567 sanitizeOptions(options); 568 569 /* 570 * Execute any valid callbacks for the set options 571 * Also set positioning flag so we don't get loads of redundant repositioning calls. 572 */ 573 this.positioning = TRUE; 574 $.each(option, $.proxy(setCallback, this)); 575 this.positioning = FALSE; 576 577 // Update position if needed 578 if(this.rendered && this.tooltip[0].offsetWidth > 0 && reposition) { 579 this.reposition( options.position.target === 'mouse' ? NULL : this.cache.event ); 580 } 581 582 return this; 583}; 584;PROTOTYPE._update = function(content, element, reposition) { 585 var self = this, 586 cache = this.cache; 587 588 // Make sure tooltip is rendered and content is defined. If not return 589 if(!this.rendered || !content) { return FALSE; } 590 591 // Use function to parse content 592 if($.isFunction(content)) { 593 content = content.call(this.elements.target, cache.event, this) || ''; 594 } 595 596 // Handle deferred content 597 if($.isFunction(content.then)) { 598 cache.waiting = TRUE; 599 return content.then(function(c) { 600 cache.waiting = FALSE; 601 return self._update(c, element); 602 }, NULL, function(e) { 603 return self._update(e, element); 604 }); 605 } 606 607 // If content is null... return false 608 if(content === FALSE || (!content && content !== '')) { return FALSE; } 609 610 // Append new content if its a DOM array and show it if hidden 611 if(content.jquery && content.length > 0) { 612 element.empty().append( 613 content.css({ display: 'block', visibility: 'visible' }) 614 ); 615 } 616 617 // Content is a regular string, insert the new content 618 else { element.html(content); } 619 620 // Wait for content to be loaded, and reposition 621 return this._waitForContent(element).then(function(images) { 622 if(self.rendered && self.tooltip[0].offsetWidth > 0) { 623 self.reposition(cache.event, !images.length); 624 } 625 }); 626}; 627 628PROTOTYPE._waitForContent = function(element) { 629 var cache = this.cache; 630 631 // Set flag 632 cache.waiting = TRUE; 633 634 // If imagesLoaded is included, ensure images have loaded and return promise 635 return ( $.fn.imagesLoaded ? element.imagesLoaded() : $.Deferred().resolve([]) ) 636 .done(function() { cache.waiting = FALSE; }) 637 .promise(); 638}; 639 640PROTOTYPE._updateContent = function(content, reposition) { 641 this._update(content, this.elements.content, reposition); 642}; 643 644PROTOTYPE._updateTitle = function(content, reposition) { 645 if(this._update(content, this.elements.title, reposition) === FALSE) { 646 this._removeTitle(FALSE); 647 } 648}; 649 650PROTOTYPE._createTitle = function() 651{ 652 var elements = this.elements, 653 id = this._id+'-title'; 654 655 // Destroy previous title element, if present 656 if(elements.titlebar) { this._removeTitle(); } 657 658 // Create title bar and title elements 659 elements.titlebar = $('<div />', { 660 'class': NAMESPACE + '-titlebar ' + (this.options.style.widget ? createWidgetClass('header') : '') 661 }) 662 .append( 663 elements.title = $('<div />', { 664 'id': id, 665 'class': NAMESPACE + '-title', 666 'aria-atomic': TRUE 667 }) 668 ) 669 .insertBefore(elements.content) 670 671 // Button-specific events 672 .delegate('.qtip-close', 'mousedown keydown mouseup keyup mouseout', function(event) { 673 $(this).toggleClass('ui-state-active ui-state-focus', event.type.substr(-4) === 'down'); 674 }) 675 .delegate('.qtip-close', 'mouseover mouseout', function(event){ 676 $(this).toggleClass('ui-state-hover', event.type === 'mouseover'); 677 }); 678 679 // Create button if enabled 680 if(this.options.content.button) { this._createButton(); } 681}; 682 683PROTOTYPE._removeTitle = function(reposition) 684{ 685 var elements = this.elements; 686 687 if(elements.title) { 688 elements.titlebar.remove(); 689 elements.titlebar = elements.title = elements.button = NULL; 690 691 // Reposition if enabled 692 if(reposition !== FALSE) { this.reposition(); } 693 } 694}; 695;PROTOTYPE._createPosClass = function(my) { 696 return NAMESPACE + '-pos-' + (my || this.options.position.my).abbrev(); 697}; 698 699PROTOTYPE.reposition = function(event, effect) { 700 if(!this.rendered || this.positioning || this.destroyed) { return this; } 701 702 // Set positioning flag 703 this.positioning = TRUE; 704 705 var cache = this.cache, 706 tooltip = this.tooltip, 707 posOptions = this.options.position, 708 target = posOptions.target, 709 my = posOptions.my, 710 at = posOptions.at, 711 viewport = posOptions.viewport, 712 container = posOptions.container, 713 adjust = posOptions.adjust, 714 method = adjust.method.split(' '), 715 tooltipWidth = tooltip.outerWidth(FALSE), 716 tooltipHeight = tooltip.outerHeight(FALSE), 717 targetWidth = 0, 718 targetHeight = 0, 719 type = tooltip.css('position'), 720 position = { left: 0, top: 0 }, 721 visible = tooltip[0].offsetWidth > 0, 722 isScroll = event && event.type === 'scroll', 723 win = $(window), 724 doc = container[0].ownerDocument, 725 mouse = this.mouse, 726 pluginCalculations, offset, adjusted, newClass; 727 728 // Check if absolute position was passed 729 if($.isArray(target) && target.length === 2) { 730 // Force left top and set position 731 at = { x: LEFT, y: TOP }; 732 position = { left: target[0], top: target[1] }; 733 } 734 735 // Check if mouse was the target 736 else if(target === 'mouse') { 737 // Force left top to allow flipping 738 at = { x: LEFT, y: TOP }; 739 740 // Use the mouse origin that caused the show event, if distance hiding is enabled 741 if((!adjust.mouse || this.options.hide.distance) && cache.origin && cache.origin.pageX) { 742 event = cache.origin; 743 } 744 745 // Use cached event for resize/scroll events 746 else if(!event || (event && (event.type === 'resize' || event.type === 'scroll'))) { 747 event = cache.event; 748 } 749 750 // Otherwise, use the cached mouse coordinates if available 751 else if(mouse && mouse.pageX) { 752 event = mouse; 753 } 754 755 // Calculate body and container offset and take them into account below 756 if(type !== 'static') { position = container.offset(); } 757 if(doc.body.offsetWidth !== (window.innerWidth || doc.documentElement.clientWidth)) { 758 offset = $(document.body).offset(); 759 } 760 761 // Use event coordinates for position 762 position = { 763 left: event.pageX - position.left + (offset && offset.left || 0), 764 top: event.pageY - position.top + (offset && offset.top || 0) 765 }; 766 767 // Scroll events are a pain, some browsers 768 if(adjust.mouse && isScroll && mouse) { 769 position.left -= (mouse.scrollX || 0) - win.scrollLeft(); 770 position.top -= (mouse.scrollY || 0) - win.scrollTop(); 771 } 772 } 773
vendor: 4,649 bytes, lines 774-902
774 // Target wasn't mouse or absolute... 775 else { 776 // Check if event targetting is being used 777 if(target === 'event') { 778 if(event && event.target && event.type !== 'scroll' && event.type !== 'resize') { 779 cache.target = $(event.target); 780 } 781 else if(!event.target) { 782 cache.target = this.elements.target; 783 } 784 } 785 else if(target !== 'event'){ 786 cache.target = $(target.jquery ? target : this.elements.target); 787 } 788 target = cache.target; 789 790 // Parse the target into a jQuery object and make sure there's an element present 791 target = $(target).eq(0); 792 if(target.length === 0) { return this; } 793 794 // Check if window or document is the target 795 else if(target[0] === document || target[0] === window) { 796 targetWidth = BROWSER.iOS ? window.innerWidth : target.width(); 797 targetHeight = BROWSER.iOS ? window.innerHeight : target.height(); 798 799 if(target[0] === window) { 800 position = { 801 top: (viewport || target).scrollTop(), 802 left: (viewport || target).scrollLeft() 803 }; 804 } 805 } 806 807 // Check if the target is an <AREA> element 808 else if(PLUGINS.imagemap && target.is('area')) { 809 pluginCalculations = PLUGINS.imagemap(this, target, at, PLUGINS.viewport ? method : FALSE); 810 } 811 812 // Check if the target is an SVG element 813 else if(PLUGINS.svg && target && target[0].ownerSVGElement) { 814 pluginCalculations = PLUGINS.svg(this, target, at, PLUGINS.viewport ? method : FALSE); 815 } 816 817 // Otherwise use regular jQuery methods 818 else { 819 targetWidth = target.outerWidth(FALSE); 820 targetHeight = target.outerHeight(FALSE); 821 position = target.offset(); 822 } 823 824 // Parse returned plugin values into proper variables 825 if(pluginCalculations) { 826 targetWidth = pluginCalculations.width; 827 targetHeight = pluginCalculations.height; 828 offset = pluginCalculations.offset; 829 position = pluginCalculations.position; 830 } 831 832 // Adjust position to take into account offset parents 833 position = this.reposition.offset(target, position, container); 834 835 // Adjust for position.fixed tooltips (and also iOS scroll bug in v3.2-4.0 & v4.3-4.3.2) 836 if((BROWSER.iOS > 3.1 && BROWSER.iOS < 4.1) || 837 (BROWSER.iOS >= 4.3 && BROWSER.iOS < 4.33) || 838 (!BROWSER.iOS && type === 'fixed') 839 ){ 840 position.left -= win.scrollLeft(); 841 position.top -= win.scrollTop(); 842 } 843 844 // Adjust position relative to target 845 if(!pluginCalculations || (pluginCalculations && pluginCalculations.adjustable !== FALSE)) { 846 position.left += at.x === RIGHT ? targetWidth : at.x === CENTER ? targetWidth / 2 : 0; 847 position.top += at.y === BOTTOM ? targetHeight : at.y === CENTER ? targetHeight / 2 : 0; 848 } 849 } 850 851 // Adjust position relative to tooltip 852 position.left += adjust.x + (my.x === RIGHT ? -tooltipWidth : my.x === CENTER ? -tooltipWidth / 2 : 0); 853 position.top += adjust.y + (my.y === BOTTOM ? -tooltipHeight : my.y === CENTER ? -tooltipHeight / 2 : 0); 854 855 // Use viewport adjustment plugin if enabled 856 if(PLUGINS.viewport) { 857 adjusted = position.adjusted = PLUGINS.viewport( 858 this, position, posOptions, targetWidth, targetHeight, tooltipWidth, tooltipHeight 859 ); 860 861 // Apply offsets supplied by positioning plugin (if used) 862 if(offset && adjusted.left) { position.left += offset.left; } 863 if(offset && adjusted.top) { position.top += offset.top; } 864 865 // Apply any new 'my' position 866 if(adjusted.my) { this.position.my = adjusted.my; } 867 } 868 869 // Viewport adjustment is disabled, set values to zero 870 else { position.adjusted = { left: 0, top: 0 }; } 871 872 // Set tooltip position class if it's changed 873 if(cache.posClass !== (newClass = this._createPosClass(this.position.my))) { 874 tooltip.removeClass(cache.posClass).addClass( (cache.posClass = newClass) ); 875 } 876 877 // tooltipmove event 878 if(!this._trigger('move', [position, viewport.elem || viewport], event)) { return this; } 879 delete position.adjusted; 880 881 // If effect is disabled, target it mouse, no animation is defined or positioning gives NaN out, set CSS directly 882 if(effect === FALSE || !visible || isNaN(position.left) || isNaN(position.top) || target === 'mouse' || !$.isFunction(posOptions.effect)) { 883 tooltip.css(position); 884 } 885 886 // Use custom function if provided 887 else if($.isFunction(posOptions.effect)) { 888 posOptions.effect.call(tooltip, this, $.extend({}, position)); 889 tooltip.queue(function(next) { 890 // Reset attributes to avoid cross-browser rendering bugs 891 $(this).css({ opacity: '', height: '' }); 892 if(BROWSER.ie) { this.style.removeAttribute('filter'); } 893 894 next(); 895 }); 896 } 897 898 // Set positioning flag 899 this.positioning = FALSE; 900 901 return this; 902};
vendor: 33,242 bytes, lines 903-1938
903 904// Custom (more correct for qTip!) offset calculator 905PROTOTYPE.reposition.offset = function(elem, pos, container) { 906 if(!container[0]) { return pos; } 907 908 var ownerDocument = $(elem[0].ownerDocument), 909 quirks = !!BROWSER.ie && document.compatMode !== 'CSS1Compat', 910 parent = container[0], 911 scrolled, position, parentOffset, overflow; 912 913 function scroll(e, i) { 914 pos.left += i * e.scrollLeft(); 915 pos.top += i * e.scrollTop(); 916 } 917 918 // Compensate for non-static containers offset 919 do { 920 if((position = $.css(parent, 'position')) !== 'static') { 921 if(position === 'fixed') { 922 parentOffset = parent.getBoundingClientRect(); 923 scroll(ownerDocument, -1); 924 } 925 else { 926 parentOffset = $(parent).position(); 927 parentOffset.left += (parseFloat($.css(parent, 'borderLeftWidth')) || 0); 928 parentOffset.top += (parseFloat($.css(parent, 'borderTopWidth')) || 0); 929 } 930 931 pos.left -= parentOffset.left + (parseFloat($.css(parent, 'marginLeft')) || 0); 932 pos.top -= parentOffset.top + (parseFloat($.css(parent, 'marginTop')) || 0); 933 934 // If this is the first parent element with an overflow of "scroll" or "auto", store it 935 if(!scrolled && (overflow = $.css(parent, 'overflow')) !== 'hidden' && overflow !== 'visible') { scrolled = $(parent); } 936 } 937 } 938 while((parent = parent.offsetParent)); 939 940 // Compensate for containers scroll if it also has an offsetParent (or in IE quirks mode) 941 if(scrolled && (scrolled[0] !== ownerDocument[0] || quirks)) { 942 scroll(scrolled, 1); 943 } 944 945 return pos; 946}; 947 948// Corner class 949var C = (CORNER = PROTOTYPE.reposition.Corner = function(corner, forceY) { 950 corner = ('' + corner).replace(/([A-Z])/, ' $1').replace(/middle/gi, CENTER).toLowerCase(); 951 this.x = (corner.match(/left|right/i) || corner.match(/center/) || ['inherit'])[0].toLowerCase(); 952 this.y = (corner.match(/top|bottom|center/i) || ['inherit'])[0].toLowerCase(); 953 this.forceY = !!forceY; 954 955 var f = corner.charAt(0); 956 this.precedance = (f === 't' || f === 'b' ? Y : X); 957}).prototype; 958 959C.invert = function(z, center) { 960 this[z] = this[z] === LEFT ? RIGHT : this[z] === RIGHT ? LEFT : center || this[z]; 961}; 962 963C.string = function(join) { 964 var x = this.x, y = this.y; 965 966 var result = x !== y ? 967 (x === 'center' || y !== 'center' && (this.precedance === Y || this.forceY) ? 968 [y,x] : [x,y] 969 ) : 970 [x]; 971 972 return join !== false ? result.join(' ') : result; 973}; 974 975C.abbrev = function() { 976 var result = this.string(false); 977 return result[0].charAt(0) + (result[1] && result[1].charAt(0) || ''); 978}; 979 980C.clone = function() { 981 return new CORNER( this.string(), this.forceY ); 982}; 983 984; 985PROTOTYPE.toggle = function(state, event) { 986 var cache = this.cache, 987 options = this.options, 988 tooltip = this.tooltip; 989 990 // Try to prevent flickering when tooltip overlaps show element 991 if(event) { 992 if((/over|enter/).test(event.type) && cache.event && (/out|leave/).test(cache.event.type) && 993 options.show.target.add(event.target).length === options.show.target.length && 994 tooltip.has(event.relatedTarget).length) { 995 return this; 996 } 997 998 // Cache event 999 cache.event = $.event.fix(event); 1000 } 1001 1002 // If we're currently waiting and we've just hidden... stop it 1003 this.waiting && !state && (this.hiddenDuringWait = TRUE); 1004 1005 // Render the tooltip if showing and it isn't already 1006 if(!this.rendered) { return state ? this.render(1) : this; } 1007 else if(this.destroyed || this.disabled) { return this; } 1008 1009 var type = state ? 'show' : 'hide', 1010 opts = this.options[type], 1011 otherOpts = this.options[ !state ? 'show' : 'hide' ], 1012 posOptions = this.options.position, 1013 contentOptions = this.options.content, 1014 width = this.tooltip.css('width'), 1015 visible = this.tooltip.is(':visible'), 1016 animate = state || opts.target.length === 1, 1017 sameTarget = !event || opts.target.length < 2 || cache.target[0] === event.target, 1018 identicalState, allow, showEvent, delay, after; 1019 1020 // Detect state if valid one isn't provided 1021 if((typeof state).search('boolean|number')) { state = !visible; } 1022 1023 // Check if the tooltip is in an identical state to the new would-be state 1024 identicalState = !tooltip.is(':animated') && visible === state && sameTarget; 1025 1026 // Fire tooltip(show/hide) event and check if destroyed 1027 allow = !identicalState ? !!this._trigger(type, [90]) : NULL; 1028 1029 // Check to make sure the tooltip wasn't destroyed in the callback 1030 if(this.destroyed) { return this; } 1031 1032 // If the user didn't stop the method prematurely and we're showing the tooltip, focus it 1033 if(allow !== FALSE && state) { this.focus(event); } 1034 1035 // If the state hasn't changed or the user stopped it, return early 1036 if(!allow || identicalState) { return this; } 1037 1038 // Set ARIA hidden attribute 1039 $.attr(tooltip[0], 'aria-hidden', !!!state); 1040 1041 // Execute state specific properties 1042 if(state) { 1043 // Store show origin coordinates 1044 this.mouse && (cache.origin = $.event.fix(this.mouse)); 1045 1046 // Update tooltip content & title if it's a dynamic function 1047 if($.isFunction(contentOptions.text)) { this._updateContent(contentOptions.text, FALSE); } 1048 if($.isFunction(contentOptions.title)) { this._updateTitle(contentOptions.title, FALSE); } 1049 1050 // Cache mousemove events for positioning purposes (if not already tracking) 1051 if(!trackingBound && posOptions.target === 'mouse' && posOptions.adjust.mouse) { 1052 $(document).bind('mousemove.'+NAMESPACE, this._storeMouse); 1053 trackingBound = TRUE; 1054 } 1055 1056 // Update the tooltip position (set width first to prevent viewport/max-width issues) 1057 if(!width) { tooltip.css('width', tooltip.outerWidth(FALSE)); } 1058 this.reposition(event, arguments[2]); 1059 if(!width) { tooltip.css('width', ''); } 1060 1061 // Hide other tooltips if tooltip is solo 1062 if(!!opts.solo) { 1063 (typeof opts.solo === 'string' ? $(opts.solo) : $(SELECTOR, opts.solo)) 1064 .not(tooltip).not(opts.target).qtip('hide', $.Event('tooltipsolo')); 1065 } 1066 } 1067 else { 1068 // Clear show timer if we're hiding 1069 clearTimeout(this.timers.show); 1070 1071 // Remove cached origin on hide 1072 delete cache.origin; 1073 1074 // Remove mouse tracking event if not needed (all tracking qTips are hidden) 1075 if(trackingBound && !$(SELECTOR+'[tracking="true"]:visible', opts.solo).not(tooltip).length) { 1076 $(document).unbind('mousemove.'+NAMESPACE); 1077 trackingBound = FALSE; 1078 } 1079 1080 // Blur the tooltip 1081 this.blur(event); 1082 } 1083 1084 // Define post-animation, state specific properties 1085 after = $.proxy(function() { 1086 if(state) { 1087 // Prevent antialias from disappearing in IE by removing filter 1088 if(BROWSER.ie) { tooltip[0].style.removeAttribute('filter'); } 1089 1090 // Remove overflow setting to prevent tip bugs 1091 tooltip.css('overflow', ''); 1092 1093 // Autofocus elements if enabled 1094 if('string' === typeof opts.autofocus) { 1095 $(this.options.show.autofocus, tooltip).focus(); 1096 } 1097 1098 // If set, hide tooltip when inactive for delay period 1099 this.options.show.target.trigger('qtip-'+this.id+'-inactive'); 1100 } 1101 else { 1102 // Reset CSS states 1103 tooltip.css({ 1104 display: '', 1105 visibility: '', 1106 opacity: '', 1107 left: '', 1108 top: '' 1109 }); 1110 } 1111 1112 // tooltipvisible/tooltiphidden events 1113 this._trigger(state ? 'visible' : 'hidden'); 1114 }, this); 1115 1116 // If no effect type is supplied, use a simple toggle 1117 if(opts.effect === FALSE || animate === FALSE) { 1118 tooltip[ type ](); 1119 after(); 1120 } 1121 1122 // Use custom function if provided 1123 else if($.isFunction(opts.effect)) { 1124 tooltip.stop(1, 1); 1125 opts.effect.call(tooltip, this); 1126 tooltip.queue('fx', function(n) { 1127 after(); n(); 1128 }); 1129 } 1130 1131 // Use basic fade function by default 1132 else { tooltip.fadeTo(90, state ? 1 : 0, after); } 1133 1134 // If inactive hide method is set, active it 1135 if(state) { opts.target.trigger('qtip-'+this.id+'-inactive'); } 1136 1137 return this; 1138}; 1139 1140PROTOTYPE.show = function(event) { return this.toggle(TRUE, event); }; 1141 1142PROTOTYPE.hide = function(event) { return this.toggle(FALSE, event); }; 1143;PROTOTYPE.focus = function(event) { 1144 if(!this.rendered || this.destroyed) { return this; } 1145 1146 var qtips = $(SELECTOR), 1147 tooltip = this.tooltip, 1148 curIndex = parseInt(tooltip[0].style.zIndex, 10), 1149 newIndex = QTIP.zindex + qtips.length, 1150 focusedElem; 1151 1152 // Only update the z-index if it has changed and tooltip is not already focused 1153 if(!tooltip.hasClass(CLASS_FOCUS)) { 1154 // tooltipfocus event 1155 if(this._trigger('focus', [newIndex], event)) { 1156 // Only update z-index's if they've changed 1157 if(curIndex !== newIndex) { 1158 // Reduce our z-index's and keep them properly ordered 1159 qtips.each(function() { 1160 if(this.style.zIndex > curIndex) { 1161 this.style.zIndex = this.style.zIndex - 1; 1162 } 1163 }); 1164 1165 // Fire blur event for focused tooltip 1166 qtips.filter('.' + CLASS_FOCUS).qtip('blur', event); 1167 } 1168 1169 // Set the new z-index 1170 tooltip.addClass(CLASS_FOCUS)[0].style.zIndex = newIndex; 1171 } 1172 } 1173 1174 return this; 1175}; 1176 1177PROTOTYPE.blur = function(event) { 1178 if(!this.rendered || this.destroyed) { return this; } 1179 1180 // Set focused status to FALSE 1181 this.tooltip.removeClass(CLASS_FOCUS); 1182 1183 // tooltipblur event 1184 this._trigger('blur', [ this.tooltip.css('zIndex') ], event); 1185 1186 return this; 1187}; 1188;PROTOTYPE.disable = function(state) { 1189 if(this.destroyed) { return this; } 1190 1191 // If 'toggle' is passed, toggle the current state 1192 if(state === 'toggle') { 1193 state = !(this.rendered ? this.tooltip.hasClass(CLASS_DISABLED) : this.disabled); 1194 } 1195 1196 // Disable if no state passed 1197 else if('boolean' !== typeof state) { 1198 state = TRUE; 1199 } 1200 1201 if(this.rendered) { 1202 this.tooltip.toggleClass(CLASS_DISABLED, state) 1203 .attr('aria-disabled', state); 1204 } 1205 1206 this.disabled = !!state; 1207 1208 return this; 1209}; 1210 1211PROTOTYPE.enable = function() { return this.disable(FALSE); }; 1212;PROTOTYPE._createButton = function() 1213{ 1214 var self = this, 1215 elements = this.elements, 1216 tooltip = elements.tooltip, 1217 button = this.options.content.button, 1218 isString = typeof button === 'string', 1219 close = isString ? button : 'Close tooltip'; 1220 1221 if(elements.button) { elements.button.remove(); } 1222 1223 // Use custom button if one was supplied by user, else use default 1224 if(button.jquery) { 1225 elements.button = button; 1226 } 1227 else { 1228 elements.button = $('<a />', { 1229 'class': 'qtip-close ' + (this.options.style.widget ? '' : NAMESPACE+'-icon'), 1230 'title': close, 1231 'aria-label': close 1232 }) 1233 .prepend( 1234 $('<span />', { 1235 'class': 'ui-icon ui-icon-close', 1236 'html': '×' 1237 }) 1238 ); 1239 } 1240 1241 // Create button and setup attributes 1242 elements.button.appendTo(elements.titlebar || tooltip) 1243 .attr('role', 'button') 1244 .click(function(event) { 1245 if(!tooltip.hasClass(CLASS_DISABLED)) { self.hide(event); } 1246 return FALSE; 1247 }); 1248}; 1249 1250PROTOTYPE._updateButton = function(button) 1251{ 1252 // Make sure tooltip is rendered and if not, return 1253 if(!this.rendered) { return FALSE; } 1254 1255 var elem = this.elements.button; 1256 if(button) { this._createButton(); } 1257 else { elem.remove(); } 1258}; 1259;// Widget class creator 1260function createWidgetClass(cls) { 1261 return WIDGET.concat('').join(cls ? '-'+cls+' ' : ' '); 1262} 1263 1264// Widget class setter method 1265PROTOTYPE._setWidget = function() 1266{ 1267 var on = this.options.style.widget, 1268 elements = this.elements, 1269 tooltip = elements.tooltip, 1270 disabled = tooltip.hasClass(CLASS_DISABLED); 1271 1272 tooltip.removeClass(CLASS_DISABLED); 1273 CLASS_DISABLED = on ? 'ui-state-disabled' : 'qtip-disabled'; 1274 tooltip.toggleClass(CLASS_DISABLED, disabled); 1275 1276 tooltip.toggleClass('ui-helper-reset '+createWidgetClass(), on).toggleClass(CLASS_DEFAULT, this.options.style.def && !on); 1277 1278 if(elements.content) { 1279 elements.content.toggleClass( createWidgetClass('content'), on); 1280 } 1281 if(elements.titlebar) { 1282 elements.titlebar.toggleClass( createWidgetClass('header'), on); 1283 } 1284 if(elements.button) { 1285 elements.button.toggleClass(NAMESPACE+'-icon', !on); 1286 } 1287}; 1288;function delay(callback, duration) { 1289 // If tooltip has displayed, start hide timer 1290 if(duration > 0) { 1291 return setTimeout( 1292 $.proxy(callback, this), duration 1293 ); 1294 } 1295 else{ callback.call(this); } 1296} 1297 1298function showMethod(event) { 1299 if(this.tooltip.hasClass(CLASS_DISABLED)) { return; } 1300 1301 // Clear hide timers 1302 clearTimeout(this.timers.show); 1303 clearTimeout(this.timers.hide); 1304 1305 // Start show timer 1306 this.timers.show = delay.call(this, 1307 function() { this.toggle(TRUE, event); }, 1308 this.options.show.delay 1309 ); 1310} 1311 1312function hideMethod(event) { 1313 if(this.tooltip.hasClass(CLASS_DISABLED) || this.destroyed) { return; } 1314 1315 // Check if new target was actually the tooltip element 1316 var relatedTarget = $(event.relatedTarget), 1317 ontoTooltip = relatedTarget.closest(SELECTOR)[0] === this.tooltip[0], 1318 ontoTarget = relatedTarget[0] === this.options.show.target[0]; 1319 1320 // Clear timers and stop animation queue 1321 clearTimeout(this.timers.show); 1322 clearTimeout(this.timers.hide); 1323 1324 // Prevent hiding if tooltip is fixed and event target is the tooltip. 1325 // Or if mouse positioning is enabled and cursor momentarily overlaps 1326 if(this !== relatedTarget[0] && 1327 (this.options.position.target === 'mouse' && ontoTooltip) || 1328 (this.options.hide.fixed && ( 1329 (/mouse(out|leave|move)/).test(event.type) && (ontoTooltip || ontoTarget)) 1330 )) 1331 { 1332 try { 1333 event.preventDefault(); 1334 event.stopImmediatePropagation(); 1335 } catch(e) {} 1336 1337 return; 1338 } 1339 1340 // If tooltip has displayed, start hide timer 1341 this.timers.hide = delay.call(this, 1342 function() { this.toggle(FALSE, event); }, 1343 this.options.hide.delay, 1344 this 1345 ); 1346} 1347 1348function inactiveMethod(event) { 1349 if(this.tooltip.hasClass(CLASS_DISABLED) || !this.options.hide.inactive) { return; } 1350 1351 // Clear timer 1352 clearTimeout(this.timers.inactive); 1353 1354 this.timers.inactive = delay.call(this, 1355 function(){ this.hide(event); }, 1356 this.options.hide.inactive 1357 ); 1358} 1359 1360function repositionMethod(event) { 1361 if(this.rendered && this.tooltip[0].offsetWidth > 0) { this.reposition(event); } 1362} 1363 1364// Store mouse coordinates 1365PROTOTYPE._storeMouse = function(event) { 1366 (this.mouse = $.event.fix(event)).type = 'mousemove'; 1367 return this; 1368}; 1369 1370// Bind events 1371PROTOTYPE._bind = function(targets, events, method, suffix, context) { 1372 if(!targets || !method || !events.length) { return; } 1373 var ns = '.' + this._id + (suffix ? '-'+suffix : ''); 1374 $(targets).bind( 1375 (events.split ? events : events.join(ns + ' ')) + ns, 1376 $.proxy(method, context || this) 1377 ); 1378 return this; 1379}; 1380PROTOTYPE._unbind = function(targets, suffix) { 1381 targets && $(targets).unbind('.' + this._id + (suffix ? '-'+suffix : '')); 1382 return this; 1383}; 1384 1385// Global delegation helper 1386function delegate(selector, events, method) { 1387 $(document.body).delegate(selector, 1388 (events.split ? events : events.join('.'+NAMESPACE + ' ')) + '.'+NAMESPACE, 1389 function() { 1390 var api = QTIP.api[ $.attr(this, ATTR_ID) ]; 1391 api && !api.disabled && method.apply(api, arguments); 1392 } 1393 ); 1394} 1395// Event trigger 1396PROTOTYPE._trigger = function(type, args, event) { 1397 var callback = $.Event('tooltip'+type); 1398 callback.originalEvent = (event && $.extend({}, event)) || this.cache.event || NULL; 1399 1400 this.triggering = type; 1401 this.tooltip.trigger(callback, [this].concat(args || [])); 1402 this.triggering = FALSE; 1403 1404 return !callback.isDefaultPrevented(); 1405}; 1406 1407PROTOTYPE._bindEvents = function(showEvents, hideEvents, showTargets, hideTargets, showMethod, hideMethod) { 1408 // Get tasrgets that lye within both 1409 var similarTargets = showTargets.filter( hideTargets ).add( hideTargets.filter(showTargets) ), 1410 toggleEvents = []; 1411 1412 // If hide and show targets are the same... 1413 if(similarTargets.length) { 1414 1415 // Filter identical show/hide events 1416 $.each(hideEvents, function(i, type) { 1417 var showIndex = $.inArray(type, showEvents); 1418 1419 // Both events are identical, remove from both hide and show events 1420 // and append to toggleEvents 1421 showIndex > -1 && toggleEvents.push( showEvents.splice( showIndex, 1 )[0] ); 1422 }); 1423 1424 // Toggle events are special case of identical show/hide events, which happen in sequence 1425 if(toggleEvents.length) { 1426 // Bind toggle events to the similar targets 1427 this._bind(similarTargets, toggleEvents, function(event) { 1428 var state = this.rendered ? this.tooltip[0].offsetWidth > 0 : false; 1429 (state ? hideMethod : showMethod).call(this, event); 1430 }); 1431 1432 // Remove the similar targets from the regular show/hide bindings 1433 showTargets = showTargets.not(similarTargets); 1434 hideTargets = hideTargets.not(similarTargets); 1435 } 1436 } 1437 1438 // Apply show/hide/toggle events 1439 this._bind(showTargets, showEvents, showMethod); 1440 this._bind(hideTargets, hideEvents, hideMethod); 1441}; 1442 1443PROTOTYPE._assignInitialEvents = function(event) { 1444 var options = this.options, 1445 showTarget = options.show.target, 1446 hideTarget = options.hide.target, 1447 showEvents = options.show.event ? $.trim('' + options.show.event).split(' ') : [], 1448 hideEvents = options.hide.event ? $.trim('' + options.hide.event).split(' ') : []; 1449 1450 // Catch remove/removeqtip events on target element to destroy redundant tooltips 1451 this._bind(this.elements.target, ['remove', 'removeqtip'], function(event) { 1452 this.destroy(true); 1453 }, 'destroy'); 1454 1455 /* 1456 * Make sure hoverIntent functions properly by using mouseleave as a hide event if 1457 * mouseenter/mouseout is used for show.event, even if it isn't in the users options. 1458 */ 1459 if(/mouse(over|enter)/i.test(options.show.event) && !/mouse(out|leave)/i.test(options.hide.event)) { 1460 hideEvents.push('mouseleave'); 1461 } 1462 1463 /* 1464 * Also make sure initial mouse targetting works correctly by caching mousemove coords 1465 * on show targets before the tooltip has rendered. Also set onTarget when triggered to 1466 * keep mouse tracking working. 1467 */ 1468 this._bind(showTarget, 'mousemove', function(event) { 1469 this._storeMouse(event); 1470 this.cache.onTarget = TRUE; 1471 }); 1472 1473 // Define hoverIntent function 1474 function hoverIntent(event) { 1475 // Only continue if tooltip isn't disabled 1476 if(this.disabled || this.destroyed) { return FALSE; } 1477 1478 // Cache the event data 1479 this.cache.event = event && $.event.fix(event); 1480 this.cache.target = event && $(event.target); 1481 1482 // Start the event sequence 1483 clearTimeout(this.timers.show); 1484 this.timers.show = delay.call(this, 1485 function() { this.render(typeof event === 'object' || options.show.ready); }, 1486 options.prerender ? 0 : options.show.delay 1487 ); 1488 } 1489 1490 // Filter and bind events 1491 this._bindEvents(showEvents, hideEvents, showTarget, hideTarget, hoverIntent, function() { 1492 if(!this.timers) { return FALSE; } 1493 clearTimeout(this.timers.show); 1494 }); 1495 1496 // Prerendering is enabled, create tooltip now 1497 if(options.show.ready || options.prerender) { hoverIntent.call(this, event); } 1498}; 1499 1500// Event assignment method 1501PROTOTYPE._assignEvents = function() { 1502 var self = this, 1503 options = this.options, 1504 posOptions = options.position, 1505 1506 tooltip = this.tooltip, 1507 showTarget = options.show.target, 1508 hideTarget = options.hide.target, 1509 containerTarget = posOptions.container, 1510 viewportTarget = posOptions.viewport, 1511 documentTarget = $(document), 1512 bodyTarget = $(document.body), 1513 windowTarget = $(window), 1514 1515 showEvents = options.show.event ? $.trim('' + options.show.event).split(' ') : [], 1516 hideEvents = options.hide.event ? $.trim('' + options.hide.event).split(' ') : []; 1517 1518 1519 // Assign passed event callbacks 1520 $.each(options.events, function(name, callback) { 1521 self._bind(tooltip, name === 'toggle' ? ['tooltipshow','tooltiphide'] : ['tooltip'+name], callback, null, tooltip); 1522 }); 1523 1524 // Hide tooltips when leaving current window/frame (but not select/option elements) 1525 if(/mouse(out|leave)/i.test(options.hide.event) && options.hide.leave === 'window') { 1526 this._bind(documentTarget, ['mouseout', 'blur'], function(event) { 1527 if(!/select|option/.test(event.target.nodeName) && !event.relatedTarget) { 1528 this.hide(event); 1529 } 1530 }); 1531 } 1532 1533 // Enable hide.fixed by adding appropriate class 1534 if(options.hide.fixed) { 1535 hideTarget = hideTarget.add( tooltip.addClass(CLASS_FIXED) ); 1536 } 1537 1538 /* 1539 * Make sure hoverIntent functions properly by using mouseleave to clear show timer if 1540 * mouseenter/mouseout is used for show.event, even if it isn't in the users options. 1541 */ 1542 else if(/mouse(over|enter)/i.test(options.show.event)) { 1543 this._bind(hideTarget, 'mouseleave', function() { 1544 clearTimeout(this.timers.show); 1545 }); 1546 } 1547 1548 // Hide tooltip on document mousedown if unfocus events are enabled 1549 if(('' + options.hide.event).indexOf('unfocus') > -1) { 1550 this._bind(containerTarget.closest('html'), ['mousedown', 'touchstart'], function(event) { 1551 var elem = $(event.target), 1552 enabled = this.rendered && !this.tooltip.hasClass(CLASS_DISABLED) && this.tooltip[0].offsetWidth > 0, 1553 isAncestor = elem.parents(SELECTOR).filter(this.tooltip[0]).length > 0; 1554 1555 if(elem[0] !== this.target[0] && elem[0] !== this.tooltip[0] && !isAncestor && 1556 !this.target.has(elem[0]).length && enabled 1557 ) { 1558 this.hide(event); 1559 } 1560 }); 1561 } 1562 1563 // Check if the tooltip hides when inactive 1564 if('number' === typeof options.hide.inactive) { 1565 // Bind inactive method to show target(s) as a custom event 1566 this._bind(showTarget, 'qtip-'+this.id+'-inactive', inactiveMethod, 'inactive'); 1567 1568 // Define events which reset the 'inactive' event handler 1569 this._bind(hideTarget.add(tooltip), QTIP.inactiveEvents, inactiveMethod); 1570 } 1571 1572 // Filter and bind events 1573 this._bindEvents(showEvents, hideEvents, showTarget, hideTarget, showMethod, hideMethod); 1574 1575 // Mouse movement bindings 1576 this._bind(showTarget.add(tooltip), 'mousemove', function(event) { 1577 // Check if the tooltip hides when mouse is moved a certain distance 1578 if('number' === typeof options.hide.distance) { 1579 var origin = this.cache.origin || {}, 1580 limit = this.options.hide.distance, 1581 abs = Math.abs; 1582 1583 // Check if the movement has gone beyond the limit, and hide it if so 1584 if(abs(event.pageX - origin.pageX) >= limit || abs(event.pageY - origin.pageY) >= limit) { 1585 this.hide(event); 1586 } 1587 } 1588 1589 // Cache mousemove coords on show targets 1590 this._storeMouse(event); 1591 }); 1592 1593 // Mouse positioning events 1594 if(posOptions.target === 'mouse') { 1595 // If mouse adjustment is on... 1596 if(posOptions.adjust.mouse) { 1597 // Apply a mouseleave event so we don't get problems with overlapping 1598 if(options.hide.event) { 1599 // Track if we're on the target or not 1600 this._bind(showTarget, ['mouseenter', 'mouseleave'], function(event) { 1601 if(!this.cache) {return FALSE; } 1602 this.cache.onTarget = event.type === 'mouseenter'; 1603 }); 1604 } 1605 1606 // Update tooltip position on mousemove 1607 this._bind(documentTarget, 'mousemove', function(event) { 1608 // Update the tooltip position only if the tooltip is visible and adjustment is enabled 1609 if(this.rendered && this.cache.onTarget && !this.tooltip.hasClass(CLASS_DISABLED) && this.tooltip[0].offsetWidth > 0) { 1610 this.reposition(event); 1611 } 1612 }); 1613 } 1614 } 1615 1616 // Adjust positions of the tooltip on window resize if enabled 1617 if(posOptions.adjust.resize || viewportTarget.length) { 1618 this._bind( $.event.special.resize ? viewportTarget : windowTarget, 'resize', repositionMethod ); 1619 } 1620 1621 // Adjust tooltip position on scroll of the window or viewport element if present 1622 if(posOptions.adjust.scroll) { 1623 this._bind( windowTarget.add(posOptions.container), 'scroll', repositionMethod ); 1624 } 1625}; 1626 1627// Un-assignment method 1628PROTOTYPE._unassignEvents = function() { 1629 var options = this.options, 1630 showTargets = options.show.target, 1631 hideTargets = options.hide.target, 1632 targets = $.grep([ 1633 this.elements.target[0], 1634 this.rendered && this.tooltip[0], 1635 options.position.container[0], 1636 options.position.viewport[0], 1637 options.position.container.closest('html')[0], // unfocus 1638 window, 1639 document 1640 ], function(i) { 1641 return typeof i === 'object'; 1642 }); 1643 1644 // Add show and hide targets if they're valid 1645 if(showTargets && showTargets.toArray) { 1646 targets = targets.concat(showTargets.toArray()); 1647 } 1648 if(hideTargets && hideTargets.toArray) { 1649 targets = targets.concat(hideTargets.toArray()); 1650 } 1651 1652 // Unbind the events 1653 this._unbind(targets) 1654 ._unbind(targets, 'destroy') 1655 ._unbind(targets, 'inactive'); 1656}; 1657 1658// Apply common event handlers using delegate (avoids excessive .bind calls!) 1659$(function() { 1660 delegate(SELECTOR, ['mouseenter', 'mouseleave'], function(event) { 1661 var state = event.type === 'mouseenter', 1662 tooltip = $(event.currentTarget), 1663 target = $(event.relatedTarget || event.target), 1664 options = this.options; 1665 1666 // On mouseenter... 1667 if(state) { 1668 // Focus the tooltip on mouseenter (z-index stacking) 1669 this.focus(event); 1670 1671 // Clear hide timer on tooltip hover to prevent it from closing 1672 tooltip.hasClass(CLASS_FIXED) && !tooltip.hasClass(CLASS_DISABLED) && clearTimeout(this.timers.hide); 1673 } 1674 1675 // On mouseleave... 1676 else { 1677 // When mouse tracking is enabled, hide when we leave the tooltip and not onto the show target (if a hide event is set) 1678 if(options.position.target === 'mouse' && options.position.adjust.mouse && 1679 options.hide.event && options.show.target && !target.closest(options.show.target[0]).length) { 1680 this.hide(event); 1681 } 1682 } 1683 1684 // Add hover class 1685 tooltip.toggleClass(CLASS_HOVER, state); 1686 }); 1687 1688 // Define events which reset the 'inactive' event handler 1689 delegate('['+ATTR_ID+']', INACTIVE_EVENTS, inactiveMethod); 1690}); 1691;// Initialization method 1692function init(elem, id, opts) { 1693 var obj, posOptions, attr, config, title, 1694 1695 // Setup element references 1696 docBody = $(document.body), 1697 1698 // Use document body instead of document element if needed 1699 newTarget = elem[0] === document ? docBody : elem, 1700 1701 // Grab metadata from element if plugin is present 1702 metadata = (elem.metadata) ? elem.metadata(opts.metadata) : NULL, 1703 1704 // If metadata type if HTML5, grab 'name' from the object instead, or use the regular data object otherwise 1705 metadata5 = opts.metadata.type === 'html5' && metadata ? metadata[opts.metadata.name] : NULL, 1706 1707 // Grab data from metadata.name (or data-qtipopts as fallback) using .data() method, 1708 html5 = elem.data(opts.metadata.name || 'qtipopts'); 1709 1710 // If we don't get an object returned attempt to parse it manualyl without parseJSON 1711 try { html5 = typeof html5 === 'string' ? $.parseJSON(html5) : html5; } catch(e) {} 1712 1713 // Merge in and sanitize metadata 1714 config = $.extend(TRUE, {}, QTIP.defaults, opts, 1715 typeof html5 === 'object' ? sanitizeOptions(html5) : NULL, 1716 sanitizeOptions(metadata5 || metadata)); 1717 1718 // Re-grab our positioning options now we've merged our metadata and set id to passed value 1719 posOptions = config.position; 1720 config.id = id; 1721 1722 // Setup missing content if none is detected 1723 if('boolean' === typeof config.content.text) { 1724 attr = elem.attr(config.content.attr); 1725 1726 // Grab from supplied attribute if available 1727 if(config.content.attr !== FALSE && attr) { config.content.text = attr; } 1728 1729 // No valid content was found, abort render 1730 else { return FALSE; } 1731 } 1732 1733 // Setup target options 1734 if(!posOptions.container.length) { posOptions.container = docBody; } 1735 if(posOptions.target === FALSE) { posOptions.target = newTarget; } 1736 if(config.show.target === FALSE) { config.show.target = newTarget; } 1737 if(config.show.solo === TRUE) { config.show.solo = posOptions.container.closest('body'); } 1738 if(config.hide.target === FALSE) { config.hide.target = newTarget; } 1739 if(config.position.viewport === TRUE) { config.position.viewport = posOptions.container; } 1740 1741 // Ensure we only use a single container 1742 posOptions.container = posOptions.container.eq(0); 1743 1744 // Convert position corner values into x and y strings 1745 posOptions.at = new CORNER(posOptions.at, TRUE); 1746 posOptions.my = new CORNER(posOptions.my); 1747 1748 // Destroy previous tooltip if overwrite is enabled, or skip element if not 1749 if(elem.data(NAMESPACE)) { 1750 if(config.overwrite) { 1751 elem.qtip('destroy', true); 1752 } 1753 else if(config.overwrite === FALSE) { 1754 return FALSE; 1755 } 1756 } 1757 1758 // Add has-qtip attribute 1759 elem.attr(ATTR_HAS, id); 1760 1761 // Remove title attribute and store it if present 1762 if(config.suppress && (title = elem.attr('title'))) { 1763 // Final attr call fixes event delegatiom and IE default tooltip showing problem 1764 elem.removeAttr('title').attr(oldtitle, title).attr('title', ''); 1765 } 1766 1767 // Initialize the tooltip and add API reference 1768 obj = new QTip(elem, config, id, !!attr); 1769 elem.data(NAMESPACE, obj); 1770 1771 return obj; 1772} 1773 1774// jQuery $.fn extension method 1775QTIP = $.fn.qtip = function(options, notation, newValue) 1776{ 1777 var command = ('' + options).toLowerCase(), // Parse command 1778 returned = NULL, 1779 args = $.makeArray(arguments).slice(1), 1780 event = args[args.length - 1], 1781 opts = this[0] ? $.data(this[0], NAMESPACE) : NULL; 1782 1783 // Check for API request 1784 if((!arguments.length && opts) || command === 'api') { 1785 return opts; 1786 } 1787 1788 // Execute API command if present 1789 else if('string' === typeof options) { 1790 this.each(function() { 1791 var api = $.data(this, NAMESPACE); 1792 if(!api) { return TRUE; } 1793 1794 // Cache the event if possible 1795 if(event && event.timeStamp) { api.cache.event = event; } 1796 1797 // Check for specific API commands 1798 if(notation && (command === 'option' || command === 'options')) { 1799 if(newValue !== undefined || $.isPlainObject(notation)) { 1800 api.set(notation, newValue); 1801 } 1802 else { 1803 returned = api.get(notation); 1804 return FALSE; 1805 } 1806 } 1807 1808 // Execute API command 1809 else if(api[command]) { 1810 api[command].apply(api, args); 1811 } 1812 }); 1813 1814 return returned !== NULL ? returned : this; 1815 } 1816 1817 // No API commands. validate provided options and setup qTips 1818 else if('object' === typeof options || !arguments.length) { 1819 // Sanitize options first 1820 opts = sanitizeOptions($.extend(TRUE, {}, options)); 1821 1822 return this.each(function(i) { 1823 var api, id; 1824 1825 // Find next available ID, or use custom ID if provided 1826 id = $.isArray(opts.id) ? opts.id[i] : opts.id; 1827 id = !id || id === FALSE || id.length < 1 || QTIP.api[id] ? QTIP.nextid++ : id; 1828 1829 // Initialize the qTip and re-grab newly sanitized options 1830 api = init($(this), id, opts); 1831 if(api === FALSE) { return TRUE; } 1832 else { QTIP.api[id] = api; } 1833 1834 // Initialize plugins 1835 $.each(PLUGINS, function() { 1836 if(this.initialize === 'initialize') { this(api); } 1837 }); 1838 1839 // Assign initial pre-render events 1840 api._assignInitialEvents(event); 1841 }); 1842 } 1843}; 1844 1845// Expose class 1846$.qtip = QTip; 1847 1848// Populated in render method 1849QTIP.api = {}; 1850;$.each({ 1851 /* Allow other plugins to successfully retrieve the title of an element with a qTip applied */ 1852 attr: function(attr, val) { 1853 if(this.length) { 1854 var self = this[0], 1855 title = 'title', 1856 api = $.data(self, 'qtip'); 1857 1858 if(attr === title && api && 'object' === typeof api && api.options.suppress) { 1859 if(arguments.length < 2) { 1860 return $.attr(self, oldtitle); 1861 } 1862 1863 // If qTip is rendered and title was originally used as content, update it 1864 if(api && api.options.content.attr === title && api.cache.attr) { 1865 api.set('content.text', val); 1866 } 1867 1868 // Use the regular attr method to set, then cache the result 1869 return this.attr(oldtitle, val); 1870 } 1871 } 1872 1873 return $.fn['attr'+replaceSuffix].apply(this, arguments); 1874 }, 1875 1876 /* Allow clone to correctly retrieve cached title attributes */ 1877 clone: function(keepData) { 1878 var titles = $([]), title = 'title', 1879 1880 // Clone our element using the real clone method 1881 elems = $.fn['clone'+replaceSuffix].apply(this, arguments); 1882 1883 // Grab all elements with an oldtitle set, and change it to regular title attribute, if keepData is false 1884 if(!keepData) { 1885 elems.filter('['+oldtitle+']').attr('title', function() { 1886 return $.attr(this, oldtitle); 1887 }) 1888 .removeAttr(oldtitle); 1889 } 1890 1891 return elems; 1892 } 1893}, function(name, func) { 1894 if(!func || $.fn[name+replaceSuffix]) { return TRUE; } 1895 1896 var old = $.fn[name+replaceSuffix] = $.fn[name]; 1897 $.fn[name] = function() { 1898 return func.apply(this, arguments) || old.apply(this, arguments); 1899 }; 1900}); 1901 1902/* Fire off 'removeqtip' handler in $.cleanData if jQuery UI not present (it already does similar). 1903 * This snippet is taken directly from jQuery UI source code found here: 1904 * http://code.jquery.com/ui/jquery-ui-git.js 1905 */ 1906if(!$.ui) { 1907 $['cleanData'+replaceSuffix] = $.cleanData; 1908 $.cleanData = function( elems ) { 1909 for(var i = 0, elem; (elem = $( elems[i] )).length; i++) { 1910 if(elem.attr(ATTR_HAS)) { 1911 try { elem.triggerHandler('removeqtip'); } 1912 catch( e ) {} 1913 } 1914 } 1915 $['cleanData'+replaceSuffix].apply(this, arguments); 1916 }; 1917} 1918;// qTip version 1919QTIP.version = '2.2.1'; 1920 1921// Base ID for all qTips 1922QTIP.nextid = 0; 1923 1924// Inactive events array 1925QTIP.inactiveEvents = INACTIVE_EVENTS; 1926 1927// Base z-index for all qTips 1928QTIP.zindex = 15000; 1929 1930// Define configuration defaults 1931QTIP.defaults = { 1932 prerender: FALSE, 1933 id: FALSE, 1934 overwrite: TRUE, 1935 suppress: TRUE, 1936 content: { 1937 text: TRUE, 1938 attr: 'title',
1939 title: FALSE, 1940 button: FALSE 1941 }, 1942 position: { 1943 my: 'top left', 1944 at: 'bottom right', 1945 target: FALSE, 1946 container: FALSE, 1947 viewport: FALSE, 1948 adjust: { 1949 x: 0, y: 0, 1950 mouse: TRUE, 1951 scroll: TRUE, 1952 resize: TRUE, 1953 method: 'flipinvert flipinvert' 1954 }, 1955 effect: function(api, pos, viewport) { 1956 $(this).animate(pos, { 1957 duration: 200, 1958 queue: FALSE 1959 }); 1960 } 1961 }, 1962 show: { 1963 target: FALSE, 1964 event: 'mouseenter', 1965 effect: TRUE, 1966 delay: 90, 1967 solo: FALSE, 1968 ready: FALSE, 1969 autofocus: FALSE 1970 }, 1971 hide: { 1972 target: FALSE, 1973 event: 'mouseleave', 1974 effect: TRUE, 1975 delay: 0, 1976 fixed: FALSE, 1977 inactive: FALSE, 1978 leave: 'window', 1979 distance: FALSE 1980 }, 1981 style: { 1982 classes: '', 1983 widget: FALSE, 1984 width: FALSE, 1985 height: FALSE, 1986 def: TRUE 1987 }, 1988 events: { 1989 render: NULL, 1990 move: NULL, 1991 show: NULL, 1992 hide: NULL, 1993 toggle: NULL, 1994 visible: NULL, 1995 hidden: NULL, 1996 focus: NULL, 1997 blur: NULL 1998 } 1999}; 2000;var TIP, 2001 2002// .bind()/.on() namespace 2003TIPNS = '.qtip-tip', 2004 2005// Common CSS strings 2006MARGIN = 'margin', 2007BORDER = 'border', 2008COLOR = 'color', 2009BG_COLOR = 'background-color', 2010TRANSPARENT = 'transparent', 2011IMPORTANT = ' !important', 2012 2013// Check if the browser supports <canvas/> elements 2014HASCANVAS = !!document.createElement('canvas').getContext, 2015 2016// Invalid colour values used in parseColours() 2017INVALID = /rgba?\(0, 0, 0(, 0)?\)|transparent|#123456/i; 2018 2019// Camel-case method, taken from jQuery source 2020// http://code.jquery.com/jquery-1.8.0.js 2021function camel(s) { return s.charAt(0).toUpperCase() + s.slice(1); } 2022 2023/* 2024 * Modified from Modernizr's testPropsAll() 2025 * http://modernizr.com/downloads/modernizr-latest.js 2026 */ 2027var cssProps = {}, cssPrefixes = ["Webkit", "O", "Moz", "ms"]; 2028function vendorCss(elem, prop) { 2029 var ucProp = prop.charAt(0).toUpperCase() + prop.slice(1), 2030 props = (prop + ' ' + cssPrefixes.join(ucProp + ' ') + ucProp).split(' '), 2031 cur, val, i = 0; 2032 2033 // If the property has already been mapped... 2034 if(cssProps[prop]) { return elem.css(cssProps[prop]); } 2035 2036 while((cur = props[i++])) { 2037 if((val = elem.css(cur)) !== undefined) { 2038 return cssProps[prop] = cur, val; 2039 } 2040 } 2041} 2042 2043// Parse a given elements CSS property into an int 2044function intCss(elem, prop) { 2045 return Math.ceil(parseFloat(vendorCss(elem, prop))); 2046} 2047 2048 2049// VML creation (for IE only) 2050if(!HASCANVAS) { 2051 var createVML = function(tag, props, style) { 2052 return '<qtipvml:'+tag+' xmlns="urn:schemas-microsoft.com:vml" class="qtip-vml" '+(props||'')+ 2053 ' style="behavior: url(#default#VML); '+(style||'')+ '" />'; 2054 }; 2055} 2056 2057// Canvas only definitions 2058else { 2059 var PIXEL_RATIO = window.devicePixelRatio || 1, 2060 BACKING_STORE_RATIO = (function() { 2061 var context = document.createElement('canvas').getContext('2d'); 2062 return context.backingStorePixelRatio || context.webkitBackingStorePixelRatio || context.mozBackingStorePixelRatio || 2063 context.msBackingStorePixelRatio || context.oBackingStorePixelRatio || 1; 2064 }()), 2065 SCALE = PIXEL_RATIO / BACKING_STORE_RATIO; 2066} 2067 2068 2069function Tip(qtip, options) { 2070 this._ns = 'tip'; 2071 this.options = options; 2072 this.offset = options.offset; 2073 this.size = [ options.width, options.height ]; 2074 2075 // Initialize 2076 this.init( (this.qtip = qtip) ); 2077} 2078 2079$.extend(Tip.prototype, { 2080 init: function(qtip) { 2081 var context, tip; 2082 2083 // Create tip element and prepend to the tooltip 2084 tip = this.element = qtip.elements.tip = $('<div />', { 'class': NAMESPACE+'-tip' }).prependTo(qtip.tooltip); 2085 2086 // Create tip drawing element(s) 2087 if(HASCANVAS) { 2088 // save() as soon as we create the canvas element so FF2 doesn't bork on our first restore()! 2089 context = $('<canvas />').appendTo(this.element)[0].getContext('2d'); 2090 2091 // Setup constant parameters 2092 context.lineJoin = 'miter'; 2093 context.miterLimit = 100000; 2094 context.save(); 2095 } 2096 else { 2097 context = createVML('shape', 'coordorigin="0,0"', 'position:absolute;'); 2098 this.element.html(context + context); 2099 2100 // Prevent mousing down on the tip since it causes problems with .live() handling in IE due to VML 2101 qtip._bind( $('*', tip).add(tip), ['click', 'mousedown'], function(event) { event.stopPropagation(); }, this._ns); 2102 } 2103 2104 // Bind update events 2105 qtip._bind(qtip.tooltip, 'tooltipmove', this.reposition, this._ns, this); 2106 2107 // Create it 2108 this.create(); 2109 }, 2110 2111 _swapDimensions: function() { 2112 this.size[0] = this.options.height; 2113 this.size[1] = this.options.width; 2114 }, 2115 _resetDimensions: function() { 2116 this.size[0] = this.options.width; 2117 this.size[1] = this.options.height; 2118 }, 2119 2120 _useTitle: function(corner) { 2121 var titlebar = this.qtip.elements.titlebar; 2122 return titlebar && ( 2123 corner.y === TOP || (corner.y === CENTER && this.element.position().top + (this.size[1] / 2) + this.options.offset < titlebar.outerHeight(TRUE)) 2124 ); 2125 }, 2126 2127 _parseCorner: function(corner) { 2128 var my = this.qtip.options.position.my; 2129 2130 // Detect corner and mimic properties 2131 if(corner === FALSE || my === FALSE) { 2132 corner = FALSE; 2133 } 2134 else if(corner === TRUE) { 2135 corner = new CORNER( my.string() ); 2136 } 2137 else if(!corner.string) { 2138 corner = new CORNER(corner);
vendor: 7,681 bytes, lines 2139-2368
2139 corner.fixed = TRUE; 2140 } 2141 2142 return corner; 2143 }, 2144 2145 _parseWidth: function(corner, side, use) { 2146 var elements = this.qtip.elements, 2147 prop = BORDER + camel(side) + 'Width'; 2148 2149 return (use ? intCss(use, prop) : ( 2150 intCss(elements.content, prop) || 2151 intCss(this._useTitle(corner) && elements.titlebar || elements.content, prop) || 2152 intCss(elements.tooltip, prop) 2153 )) || 0; 2154 }, 2155 2156 _parseRadius: function(corner) { 2157 var elements = this.qtip.elements, 2158 prop = BORDER + camel(corner.y) + camel(corner.x) + 'Radius'; 2159 2160 return BROWSER.ie < 9 ? 0 : 2161 intCss(this._useTitle(corner) && elements.titlebar || elements.content, prop) || 2162 intCss(elements.tooltip, prop) || 0; 2163 }, 2164 2165 _invalidColour: function(elem, prop, compare) { 2166 var val = elem.css(prop); 2167 return !val || (compare && val === elem.css(compare)) || INVALID.test(val) ? FALSE : val; 2168 }, 2169 2170 _parseColours: function(corner) { 2171 var elements = this.qtip.elements, 2172 tip = this.element.css('cssText', ''), 2173 borderSide = BORDER + camel(corner[ corner.precedance ]) + camel(COLOR), 2174 colorElem = this._useTitle(corner) && elements.titlebar || elements.content, 2175 css = this._invalidColour, color = []; 2176 2177 // Attempt to detect the background colour from various elements, left-to-right precedance 2178 color[0] = css(tip, BG_COLOR) || css(colorElem, BG_COLOR) || css(elements.content, BG_COLOR) || 2179 css(elements.tooltip, BG_COLOR) || tip.css(BG_COLOR); 2180 2181 // Attempt to detect the correct border side colour from various elements, left-to-right precedance 2182 color[1] = css(tip, borderSide, COLOR) || css(colorElem, borderSide, COLOR) || 2183 css(elements.content, borderSide, COLOR) || css(elements.tooltip, borderSide, COLOR) || elements.tooltip.css(borderSide); 2184 2185 // Reset background and border colours 2186 $('*', tip).add(tip).css('cssText', BG_COLOR+':'+TRANSPARENT+IMPORTANT+';'+BORDER+':0'+IMPORTANT+';'); 2187 2188 return color; 2189 }, 2190 2191 _calculateSize: function(corner) { 2192 var y = corner.precedance === Y, 2193 width = this.options['width'], 2194 height = this.options['height'], 2195 isCenter = corner.abbrev() === 'c', 2196 base = (y ? width: height) * (isCenter ? 0.5 : 1), 2197 pow = Math.pow, 2198 round = Math.round, 2199 bigHyp, ratio, result, 2200 2201 smallHyp = Math.sqrt( pow(base, 2) + pow(height, 2) ), 2202 hyp = [ (this.border / base) * smallHyp, (this.border / height) * smallHyp ]; 2203 2204 hyp[2] = Math.sqrt( pow(hyp[0], 2) - pow(this.border, 2) ); 2205 hyp[3] = Math.sqrt( pow(hyp[1], 2) - pow(this.border, 2) ); 2206 2207 bigHyp = smallHyp + hyp[2] + hyp[3] + (isCenter ? 0 : hyp[0]); 2208 ratio = bigHyp / smallHyp; 2209 2210 result = [ round(ratio * width), round(ratio * height) ]; 2211 return y ? result : result.reverse(); 2212 }, 2213 2214 // Tip coordinates calculator 2215 _calculateTip: function(corner, size, scale) { 2216 scale = scale || 1; 2217 size = size || this.size; 2218 2219 var width = size[0] * scale, 2220 height = size[1] * scale, 2221 width2 = Math.ceil(width / 2), height2 = Math.ceil(height / 2), 2222 2223 // Define tip coordinates in terms of height and width values 2224 tips = { 2225 br: [0,0, width,height, width,0], 2226 bl: [0,0, width,0, 0,height], 2227 tr: [0,height, width,0, width,height], 2228 tl: [0,0, 0,height, width,height], 2229 tc: [0,height, width2,0, width,height], 2230 bc: [0,0, width,0, width2,height], 2231 rc: [0,0, width,height2, 0,height], 2232 lc: [width,0, width,height, 0,height2] 2233 }; 2234 2235 // Set common side shapes 2236 tips.lt = tips.br; tips.rt = tips.bl; 2237 tips.lb = tips.tr; tips.rb = tips.tl; 2238 2239 return tips[ corner.abbrev() ]; 2240 }, 2241 2242 // Tip coordinates drawer (canvas) 2243 _drawCoords: function(context, coords) { 2244 context.beginPath(); 2245 context.moveTo(coords[0], coords[1]); 2246 context.lineTo(coords[2], coords[3]); 2247 context.lineTo(coords[4], coords[5]); 2248 context.closePath(); 2249 }, 2250 2251 create: function() { 2252 // Determine tip corner 2253 var c = this.corner = (HASCANVAS || BROWSER.ie) && this._parseCorner(this.options.corner); 2254 2255 // If we have a tip corner... 2256 if( (this.enabled = !!this.corner && this.corner.abbrev() !== 'c') ) { 2257 // Cache it 2258 this.qtip.cache.corner = c.clone(); 2259 2260 // Create it 2261 this.update(); 2262 } 2263 2264 // Toggle tip element 2265 this.element.toggle(this.enabled); 2266 2267 return this.corner; 2268 }, 2269 2270 update: function(corner, position) { 2271 if(!this.enabled) { return this; } 2272 2273 var elements = this.qtip.elements, 2274 tip = this.element, 2275 inner = tip.children(), 2276 options = this.options, 2277 curSize = this.size, 2278 mimic = options.mimic, 2279 round = Math.round, 2280 color, precedance, context, 2281 coords, bigCoords, translate, newSize, border, BACKING_STORE_RATIO; 2282 2283 // Re-determine tip if not already set 2284 if(!corner) { corner = this.qtip.cache.corner || this.corner; } 2285 2286 // Use corner property if we detect an invalid mimic value 2287 if(mimic === FALSE) { mimic = corner; } 2288 2289 // Otherwise inherit mimic properties from the corner object as necessary 2290 else { 2291 mimic = new CORNER(mimic); 2292 mimic.precedance = corner.precedance; 2293 2294 if(mimic.x === 'inherit') { mimic.x = corner.x; } 2295 else if(mimic.y === 'inherit') { mimic.y = corner.y; } 2296 else if(mimic.x === mimic.y) { 2297 mimic[ corner.precedance ] = corner[ corner.precedance ]; 2298 } 2299 } 2300 precedance = mimic.precedance; 2301 2302 // Ensure the tip width.height are relative to the tip position 2303 if(corner.precedance === X) { this._swapDimensions(); } 2304 else { this._resetDimensions(); } 2305 2306 // Update our colours 2307 color = this.color = this._parseColours(corner); 2308 2309 // Detect border width, taking into account colours 2310 if(color[1] !== TRANSPARENT) { 2311 // Grab border width 2312 border = this.border = this._parseWidth(corner, corner[corner.precedance]); 2313 2314 // If border width isn't zero, use border color as fill if it's not invalid (1.0 style tips) 2315 if(options.border && border < 1 && !INVALID.test(color[1])) { color[0] = color[1]; } 2316 2317 // Set border width (use detected border width if options.border is true) 2318 this.border = border = options.border !== TRUE ? options.border : border; 2319 } 2320 2321 // Border colour was invalid, set border to zero 2322 else { this.border = border = 0; } 2323 2324 // Determine tip size 2325 newSize = this.size = this._calculateSize(corner); 2326 tip.css({ 2327 width: newSize[0], 2328 height: newSize[1], 2329 lineHeight: newSize[1]+'px' 2330 }); 2331 2332 // Calculate tip translation 2333 if(corner.precedance === Y) { 2334 translate = [ 2335 round(mimic.x === LEFT ? border : mimic.x === RIGHT ? newSize[0] - curSize[0] - border : (newSize[0] - curSize[0]) / 2), 2336 round(mimic.y === TOP ? newSize[1] - curSize[1] : 0) 2337 ]; 2338 } 2339 else { 2340 translate = [ 2341 round(mimic.x === LEFT ? newSize[0] - curSize[0] : 0), 2342 round(mimic.y === TOP ? border : mimic.y === BOTTOM ? newSize[1] - curSize[1] - border : (newSize[1] - curSize[1]) / 2) 2343 ]; 2344 } 2345 2346 // Canvas drawing implementation 2347 if(HASCANVAS) { 2348 // Grab canvas context and clear/save it 2349 context = inner[0].getContext('2d'); 2350 context.restore(); context.save(); 2351 context.clearRect(0,0,6000,6000); 2352 2353 // Calculate coordinates 2354 coords = this._calculateTip(mimic, curSize, SCALE); 2355 bigCoords = this._calculateTip(mimic, this.size, SCALE); 2356 2357 // Set the canvas size using calculated size 2358 inner.attr(WIDTH, newSize[0] * SCALE).attr(HEIGHT, newSize[1] * SCALE); 2359 inner.css(WIDTH, newSize[0]).css(HEIGHT, newSize[1]); 2360 2361 // Draw the outer-stroke tip 2362 this._drawCoords(context, bigCoords); 2363 context.fillStyle = color[1]; 2364 context.fill(); 2365 2366 // Draw the actual tip 2367 context.translate(translate[0] * SCALE, translate[1] * SCALE); 2368 this._drawCoords(context, coords);
2369 context.fillStyle = color[0]; 2370 context.fill(); 2371 } 2372 2373 // VML (IE Proprietary implementation) 2374 else { 2375 // Calculate coordinates 2376 coords = this._calculateTip(mimic); 2377 2378 // Setup coordinates string 2379 coords = 'm' + coords[0] + ',' + coords[1] + ' l' + coords[2] + 2380 ',' + coords[3] + ' ' + coords[4] + ',' + coords[5] + ' xe'; 2381 2382 // Setup VML-specific offset for pixel-perfection 2383 translate[2] = border && /^(r|b)/i.test(corner.string()) ? 2384 BROWSER.ie === 8 ? 2 : 1 : 0; 2385 2386 // Set initial CSS 2387 inner.css({ 2388 coordsize: (newSize[0]+border) + ' ' + (newSize[1]+border), 2389 antialias: ''+(mimic.string().indexOf(CENTER) > -1), 2390 left: translate[0] - (translate[2] * Number(precedance === X)), 2391 top: translate[1] - (translate[2] * Number(precedance === Y)), 2392 width: newSize[0] + border, 2393 height: newSize[1] + border 2394 }) 2395 .each(function(i) { 2396 var $this = $(this); 2397 2398 // Set shape specific attributes 2399 $this[ $this.prop ? 'prop' : 'attr' ]({ 2400 coordsize: (newSize[0]+border) + ' ' + (newSize[1]+border), 2401 path: coords, 2402 fillcolor: color[0], 2403 filled: !!i, 2404 stroked: !i 2405 }) 2406 .toggle(!!(border || i)); 2407 2408 // Check if border is enabled and add stroke element 2409 !i && $this.html( createVML( 2410 'stroke', 'weight="'+(border*2)+'px" color="'+color[1]+'" miterlimit="1000" joinstyle="miter"' 2411 ) ); 2412 }); 2413 } 2414 2415 // Opera bug #357 - Incorrect tip position 2416 // https://github.com/Craga89/qTip2/issues/367 2417 window.opera && setTimeout(function() { 2418 elements.tip.css({ 2419 display: 'inline-block', 2420 visibility: 'visible' 2421 }); 2422 }, 1); 2423 2424 // Position if needed 2425 if(position !== FALSE) { this.calculate(corner, newSize); } 2426 }, 2427 2428 calculate: function(corner, size) { 2429 if(!this.enabled) { return FALSE; } 2430 2431 var self = this, 2432 elements = this.qtip.elements, 2433 tip = this.element, 2434 userOffset = this.options.offset, 2435 isWidget = elements.tooltip.hasClass('ui-widget'), 2436 position = { }, 2437 precedance, corners; 2438 2439 // Inherit corner if not provided 2440 corner = corner || this.corner; 2441 precedance = corner.precedance; 2442 2443 // Determine which tip dimension to use for adjustment 2444 size = size || this._calculateSize(corner); 2445 2446 // Setup corners and offset array 2447 corners = [ corner.x, corner.y ]; 2448 if(precedance === X) { corners.reverse(); } 2449 2450 // Calculate tip position 2451 $.each(corners, function(i, side) { 2452 var b, bc, br; 2453 2454 if(side === CENTER) { 2455 b = precedance === Y ? LEFT : TOP; 2456 position[ b ] = '50%'; 2457 position[MARGIN+'-' + b] = -Math.round(size[ precedance === Y ? 0 : 1 ] / 2) + userOffset; 2458 } 2459 else { 2460 b = self._parseWidth(corner, side, elements.tooltip); 2461 bc = self._parseWidth(corner, side, elements.content); 2462 br = self._parseRadius(corner); 2463 2464 position[ side ] = Math.max(-self.border, i ? bc : (userOffset + (br > b ? br : -b))); 2465 } 2466 }); 2467 2468 // Adjust for tip size 2469 position[ corner[precedance] ] -= size[ precedance === X ? 0 : 1 ]; 2470 2471 // Set and return new position 2472 tip.css({ margin: '', top: '', bottom: '', left: '', right: '' }).css(position); 2473 return position; 2474 }, 2475 2476 reposition: function(event, api, pos, viewport) { 2477 if(!this.enabled) { return; } 2478 2479 var cache = api.cache, 2480 newCorner = this.corner.clone(),
vendor: 8,840 bytes, lines 2481-2731
2481 adjust = pos.adjusted, 2482 method = api.options.position.adjust.method.split(' '), 2483 horizontal = method[0], 2484 vertical = method[1] || method[0], 2485 shift = { left: FALSE, top: FALSE, x: 0, y: 0 }, 2486 offset, css = {}, props; 2487 2488 function shiftflip(direction, precedance, popposite, side, opposite) { 2489 // Horizontal - Shift or flip method 2490 if(direction === SHIFT && newCorner.precedance === precedance && adjust[side] && newCorner[popposite] !== CENTER) { 2491 newCorner.precedance = newCorner.precedance === X ? Y : X; 2492 } 2493 else if(direction !== SHIFT && adjust[side]){ 2494 newCorner[precedance] = newCorner[precedance] === CENTER ? 2495 (adjust[side] > 0 ? side : opposite) : (newCorner[precedance] === side ? opposite : side); 2496 } 2497 } 2498 2499 function shiftonly(xy, side, opposite) { 2500 if(newCorner[xy] === CENTER) { 2501 css[MARGIN+'-'+side] = shift[xy] = offset[MARGIN+'-'+side] - adjust[side]; 2502 } 2503 else { 2504 props = offset[opposite] !== undefined ? 2505 [ adjust[side], -offset[side] ] : [ -adjust[side], offset[side] ]; 2506 2507 if( (shift[xy] = Math.max(props[0], props[1])) > props[0] ) { 2508 pos[side] -= adjust[side]; 2509 shift[side] = FALSE; 2510 } 2511 2512 css[ offset[opposite] !== undefined ? opposite : side ] = shift[xy]; 2513 } 2514 } 2515 2516 // If our tip position isn't fixed e.g. doesn't adjust with viewport... 2517 if(this.corner.fixed !== TRUE) { 2518 // Perform shift/flip adjustments 2519 shiftflip(horizontal, X, Y, LEFT, RIGHT); 2520 shiftflip(vertical, Y, X, TOP, BOTTOM); 2521 2522 // Update and redraw the tip if needed (check cached details of last drawn tip) 2523 if(newCorner.string() !== cache.corner.string() || cache.cornerTop !== adjust.top || cache.cornerLeft !== adjust.left) { 2524 this.update(newCorner, FALSE); 2525 } 2526 } 2527 2528 // Setup tip offset properties 2529 offset = this.calculate(newCorner); 2530 2531 // Readjust offset object to make it left/top 2532 if(offset.right !== undefined) { offset.left = -offset.right; } 2533 if(offset.bottom !== undefined) { offset.top = -offset.bottom; } 2534 offset.user = this.offset; 2535 2536 // Perform shift adjustments 2537 if(shift.left = (horizontal === SHIFT && !!adjust.left)) { shiftonly(X, LEFT, RIGHT); } 2538 if(shift.top = (vertical === SHIFT && !!adjust.top)) { shiftonly(Y, TOP, BOTTOM); } 2539 2540 /* 2541 * If the tip is adjusted in both dimensions, or in a 2542 * direction that would cause it to be anywhere but the 2543 * outer border, hide it! 2544 */ 2545 this.element.css(css).toggle( 2546 !((shift.x && shift.y) || (newCorner.x === CENTER && shift.y) || (newCorner.y === CENTER && shift.x)) 2547 ); 2548 2549 // Adjust position to accomodate tip dimensions 2550 pos.left -= offset.left.charAt ? offset.user : 2551 horizontal !== SHIFT || shift.top || !shift.left && !shift.top ? offset.left + this.border : 0; 2552 pos.top -= offset.top.charAt ? offset.user : 2553 vertical !== SHIFT || shift.left || !shift.left && !shift.top ? offset.top + this.border : 0; 2554 2555 // Cache details 2556 cache.cornerLeft = adjust.left; cache.cornerTop = adjust.top; 2557 cache.corner = newCorner.clone(); 2558 }, 2559 2560 destroy: function() { 2561 // Unbind events 2562 this.qtip._unbind(this.qtip.tooltip, this._ns); 2563 2564 // Remove the tip element(s) 2565 if(this.qtip.elements.tip) { 2566 this.qtip.elements.tip.find('*') 2567 .remove().end().remove(); 2568 } 2569 } 2570}); 2571 2572TIP = PLUGINS.tip = function(api) { 2573 return new Tip(api, api.options.style.tip); 2574}; 2575 2576// Initialize tip on render 2577TIP.initialize = 'render'; 2578 2579// Setup plugin sanitization options 2580TIP.sanitize = function(options) { 2581 if(options.style && 'tip' in options.style) { 2582 var opts = options.style.tip; 2583 if(typeof opts !== 'object') { opts = options.style.tip = { corner: opts }; } 2584 if(!(/string|boolean/i).test(typeof opts.corner)) { opts.corner = TRUE; } 2585 } 2586}; 2587 2588// Add new option checks for the plugin 2589CHECKS.tip = { 2590 '^position.my|style.tip.(corner|mimic|border)$': function() { 2591 // Make sure a tip can be drawn 2592 this.create(); 2593 2594 // Reposition the tooltip 2595 this.qtip.reposition(); 2596 }, 2597 '^style.tip.(height|width)$': function(obj) { 2598 // Re-set dimensions and redraw the tip 2599 this.size = [ obj.width, obj.height ]; 2600 this.update(); 2601 2602 // Reposition the tooltip 2603 this.qtip.reposition(); 2604 }, 2605 '^content.title|style.(classes|widget)$': function() { 2606 this.update(); 2607 } 2608}; 2609 2610// Extend original qTip defaults 2611$.extend(TRUE, QTIP.defaults, { 2612 style: { 2613 tip: { 2614 corner: TRUE, 2615 mimic: FALSE, 2616 width: 6, 2617 height: 6, 2618 border: TRUE, 2619 offset: 0 2620 } 2621 } 2622}); 2623;PLUGINS.viewport = function(api, position, posOptions, targetWidth, targetHeight, elemWidth, elemHeight) 2624{ 2625 var target = posOptions.target, 2626 tooltip = api.elements.tooltip, 2627 my = posOptions.my, 2628 at = posOptions.at, 2629 adjust = posOptions.adjust, 2630 method = adjust.method.split(' '), 2631 methodX = method[0], 2632 methodY = method[1] || method[0], 2633 viewport = posOptions.viewport, 2634 container = posOptions.container, 2635 cache = api.cache, 2636 adjusted = { left: 0, top: 0 }, 2637 fixed, newMy, containerOffset, containerStatic, 2638 viewportWidth, viewportHeight, viewportScroll, viewportOffset; 2639 2640 // If viewport is not a jQuery element, or it's the window/document, or no adjustment method is used... return 2641 if(!viewport.jquery || target[0] === window || target[0] === document.body || adjust.method === 'none') { 2642 return adjusted; 2643 } 2644 2645 // Cach container details 2646 containerOffset = container.offset() || adjusted; 2647 containerStatic = container.css('position') === 'static'; 2648 2649 // Cache our viewport details 2650 fixed = tooltip.css('position') === 'fixed'; 2651 viewportWidth = viewport[0] === window ? viewport.width() : viewport.outerWidth(FALSE); 2652 viewportHeight = viewport[0] === window ? viewport.height() : viewport.outerHeight(FALSE); 2653 viewportScroll = { left: fixed ? 0 : viewport.scrollLeft(), top: fixed ? 0 : viewport.scrollTop() }; 2654 viewportOffset = viewport.offset() || adjusted; 2655 2656 // Generic calculation method 2657 function calculate(side, otherSide, type, adjust, side1, side2, lengthName, targetLength, elemLength) { 2658 var initialPos = position[side1], 2659 mySide = my[side], 2660 atSide = at[side], 2661 isShift = type === SHIFT, 2662 myLength = mySide === side1 ? elemLength : mySide === side2 ? -elemLength : -elemLength / 2, 2663 atLength = atSide === side1 ? targetLength : atSide === side2 ? -targetLength : -targetLength / 2, 2664 sideOffset = viewportScroll[side1] + viewportOffset[side1] - (containerStatic ? 0 : containerOffset[side1]), 2665 overflow1 = sideOffset - initialPos, 2666 overflow2 = initialPos + elemLength - (lengthName === WIDTH ? viewportWidth : viewportHeight) - sideOffset, 2667 offset = myLength - (my.precedance === side || mySide === my[otherSide] ? atLength : 0) - (atSide === CENTER ? targetLength / 2 : 0); 2668 2669 // shift 2670 if(isShift) { 2671 offset = (mySide === side1 ? 1 : -1) * myLength; 2672 2673 // Adjust position but keep it within viewport dimensions 2674 position[side1] += overflow1 > 0 ? overflow1 : overflow2 > 0 ? -overflow2 : 0; 2675 position[side1] = Math.max( 2676 -containerOffset[side1] + viewportOffset[side1], 2677 initialPos - offset, 2678 Math.min( 2679 Math.max( 2680 -containerOffset[side1] + viewportOffset[side1] + (lengthName === WIDTH ? viewportWidth : viewportHeight), 2681 initialPos + offset 2682 ), 2683 position[side1], 2684 2685 // Make sure we don't adjust complete off the element when using 'center' 2686 mySide === 'center' ? initialPos - myLength : 1E9 2687 ) 2688 ); 2689 2690 } 2691 2692 // flip/flipinvert 2693 else { 2694 // Update adjustment amount depending on if using flipinvert or flip 2695 adjust *= (type === FLIPINVERT ? 2 : 0); 2696 2697 // Check for overflow on the left/top 2698 if(overflow1 > 0 && (mySide !== side1 || overflow2 > 0)) { 2699 position[side1] -= offset + adjust; 2700 newMy.invert(side, side1); 2701 } 2702 2703 // Check for overflow on the bottom/right 2704 else if(overflow2 > 0 && (mySide !== side2 || overflow1 > 0) ) { 2705 position[side1] -= (mySide === CENTER ? -offset : offset) + adjust; 2706 newMy.invert(side, side2); 2707 } 2708 2709 // Make sure we haven't made things worse with the adjustment and reset if so 2710 if(position[side1] < viewportScroll && -position[side1] > overflow2) { 2711 position[side1] = initialPos; newMy = my.clone(); 2712 } 2713 } 2714 2715 return position[side1] - initialPos; 2716 } 2717 2718 // Set newMy if using flip or flipinvert methods 2719 if(methodX !== 'shift' || methodY !== 'shift') { newMy = my.clone(); } 2720 2721 // Adjust position based onviewport and adjustment options 2722 adjusted = { 2723 left: methodX !== 'none' ? calculate( X, Y, methodX, adjust.x, LEFT, RIGHT, WIDTH, targetWidth, elemWidth ) : 0, 2724 top: methodY !== 'none' ? calculate( Y, X, methodY, adjust.y, TOP, BOTTOM, HEIGHT, targetHeight, elemHeight ) : 0, 2725 my: newMy 2726 }; 2727 2728 return adjusted; 2729}; 2730;})); 2731}( 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.