PageSourceSearch

https://www.monitoringresources.org/content/qTip/qTip2/jquery.qtip.js

js monitoringresources.org collected 2026-10-04 03:51:11 UTC 145,892 bytes, 3,439 lines download raw bytes

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