来自柯南百科

注意:在发布之后,您可能需要清除浏览器缓存才能看到所作出的变更的影响。

  • Firefox或Safari:按住Shift的同时单击刷新,或按Ctrl-F5Ctrl-R(Mac为⌘-R
  • Google Chrome:Ctrl-Shift-R(Mac为⌘-Shift-R
  • Internet Explorer或Edge:按住Ctrl的同时单击刷新,或按Ctrl-F5
  • Opera:Ctrl-F5
(function () {
    var COMMENTED_TEXT_SELECTOR = '.tt-commentedText, .tt-general, .tt',
        SOURCE_CLASS = 'tt',
        TARGET_CLASS = 'tt-general',
        TOOLTIP_TIMEOUT = 500 /* ms */;

    // "Global" variables
    var CLASSES = {
        FADE_IN_DOWN: 'tt-fade-in-down',
        FADE_IN_UP: 'tt-fade-in-up',
        FADE_OUT_DOWN: 'tt-fade-out-down',
        FADE_OUT_UP: 'tt-fade-out-up'
    },
        CLIENT_NAME = $.client.profile().name,
        enabled, delay,
        $body = $(document.body),
        $window = $(window);

    function isMobile() {
        return /Mobi|Android/i.test(navigator.userAgent);
    }

    function activatedByClick() {
        return isMobile();
    }

    function tt($content) {
        // Popups gadget & Reference Previews
        if (window.pg || mw.config.get("wgPopupsReferencePreviews")) {
            return;
        }

        var teSelector;

        function enableTt() {
            enabled = true;
            $('.tt-enableItem').remove();
            rt($content);
            mw.notify(mw.msg('tt-enabled'));
        }

        function disableTt() {
            $content.find(teSelector).removeClass('tt-commentedText').off('.tt');
            $body.off('.tt');
            $window.off('.tt');
        }

        function TooltippedElement($element) {
            var events,
                te = this;

            function onStartEvent(e) {
                var showRefArgs;

                e.preventDefault();
                if (!te.noRef) {
                    showRefArgs = [$(this)];
                    te.showRef.apply(te, showRefArgs);
                }
            }

            function onEndEvent() {
                if (!te.noRef) {
                    if (te.tooltip) {
                        $hover = $(':hover').filter('.tt-tooltip');
                        if ($hover.length > 0) {
                            return;
                        }
                    }
                    te.hideRef();
                }
            }

            if (!$element) {
                return;
            }

            // TooltippedElement.$element and TooltippedElement.$originalElement will be different when
            // the first is changed after its cloned version is hovered in a tooltip
            this.$element = $element;
            this.$originalElement = $element;
            this.type = 'commentedText';
            this.saveComment = this.$element.attr('title');

            this.comment = this.$element.next().find('.tt-hidetext').html();
            if (!this.comment) {
                return;
            }
            //YOHO 增加有链接时的按钮
            if (this.$element.closest('a').length || this.$element.has('a').length) {
                this.comment += '<a href="' + this.$element.find("a").attr('href') + '"><div class="tt-Link"></div></a>';
            }

            this.$element.addClass('tt-commentedText');


            if (activatedByClick()) {
                events = {
                    'click.tt': onStartEvent
                };
                // Adds an ability to see tooltips for links
                if (this.$element.closest('a').length || this.$element.has('a').length) {
                    events['contextmenu.tt'] = onStartEvent;
                }
            } else {
                events = {
                    'mouseenter.tt': onStartEvent,
                    'mouseleave.tt': onEndEvent
                };
            }

            this.$element.on(events);

            this.hideRef = function (immediately) {
                clearTimeout(te.showTimer);

                if (this.type === 'commentedText') {
                    this.$element.attr('title', this.saveComment);
                }

                if (this.tooltip && this.tooltip.isPresent) {
                    this.tooltip.hide();
                } else if (this.$ref && this.$ref.hasClass('tt-target')) {
                    this.$ref.removeClass('tt-target');

                    $body.off('click.tt touchstart.tt', this.onBodyClick);
                }
            };

            this.showRef = function ($element, ePageX, ePageY) {
                // Popups gadget
                if (window.pg) {
                    disableTt();
                    return;
                }

                if (this.tooltip && !this.tooltip.$content.length) {
                    return;
                }

                var tooltipInitiallyPresent = this.tooltip && this.tooltip.isPresent;

                function reallyShow() {
                    var viewportTop, refOffsetTop, teHref;

                    if (!te.tooltip) {
                        te.tooltip = new Tooltip(te);
                        if (!te.tooltip.$content.length) {
                            return;
                        }
                    }

                    // If this tooltip is called from inside another tooltip. We can't define it
                    // in the constructor since a ref can be cloned but have the same Tooltip object;
                    // so, Tooltip.parent is a floating value.
                    te.tooltip.parent = te.$element.closest('.tt-tooltip').data('tooltip');
                    if (te.tooltip.parent && te.tooltip.parent.disappearing) {
                        return;
                    }

                    te.tooltip.show();

                    if (tooltipInitiallyPresent) {
                        if (te.tooltip.$element.hasClass('tt-tooltip-above')) {
                            te.tooltip.$element.addClass(CLASSES.FADE_IN_DOWN);
                        } else {
                            te.tooltip.$element.addClass(CLASSES.FADE_IN_UP);
                        }
                        return;
                    }

                    te.tooltip.calculatePosition(ePageX, ePageY);

                    $window.on('resize.tt', te.onWindowResize);
                }

                // We redefine this.$element here because e.target can be a reference link inside
                // a reference tooltip, not a link that was initially assigned to this.$element
                this.$element = $element;

                this.$element.attr('title', '');

                if (tooltipInitiallyPresent ||
                    (this.$ref && this.$ref.hasClass('tt-target'))
                ) {
                    return;
                } else if (activatedByClick()) {
                    setTimeout(function () {
                        $body.on('click.tt touchstart.tt', te.onBodyClick);
                    }, 0);
                }

                reallyShow();
            };

            this.onBodyClick = function (e) {
                if (!te.tooltip && !te.$ref.hasClass('tt-target')) {
                    return;
                }

                var $current = $(e.target);

                function contextMatchesParameter(parameter) {
                    return this === parameter;
                }

                // The last condition is used to determine cases when a clicked tooltip is the current
                // element's tooltip or one of its descendants
                while ($current.length &&
                    (!$current.hasClass('tt-tooltip') ||
                        !$current.data('tooltip') ||
                        !$current.data('tooltip').upToTopParent(
                            contextMatchesParameter, [te.tooltip],
                            true
                        )
                    )
                ) {
                    $current = $current.parent();
                }
                if (!$current.length) {
                    te.hideRef();
                }
            };

            this.onWindowResize = function () {
                te.tooltip.calculatePosition();
            };
        }

        function Tooltip(te) {

            var tooltip = this;

            // This variable can change: one tooltip can be called from a harvard-style reference link
            // that is put into different tooltips
            this.te = te;


            this.id = 'tt-' + String(Math.random()).slice(2);
            this.$content = $('<div></div>').html(this.te.comment);

            if (!this.$content.length) {
                return;
            }

            this.insideWindow = Boolean(this.te.$element.closest('.oo-ui-window').length);

            this.$element = $('<div>')
                .addClass('tt-tooltip')
                .attr('id', this.id)
                //.attr('data-href', this.te.$ref.attr('id')) //YOHO
                .attr('role', 'tooltip')
                .data('tooltip', this);
            if (this.insideWindow) {
                this.$element.addClass('tt-tooltip-insideWindow');
            }

            var tte = this.te;
            if (!activatedByClick()) {
                this.$element.on('mouseleave.tt',
                    function () {
                        tte.hideRef();
                    }
                )
            }

            // We need the $content interlayer here in order for the settings icon to have correct
            // margins
            this.$content = this.$content
                //.wrapAll('<div>')
                //.parent()
                .addClass('tt-tooltipContent')
                .addClass('mw-parser-output')
                .appendTo(this.$element);

            // Tooltip tail element is inside tooltip content element in order for the tooltip
            // not to disappear when the mouse is above the tail
            this.$tail = $('<div>')
                .addClass('tt-tooltipTail')
                .prependTo(this.$element);

            this.disappearing = false;

            this.show = function () {
                this.disappearing = false;
                clearTimeout(this.te.hideTimer);
                clearTimeout(this.te.removeTimer);

                this.$element
                    .removeClass(CLASSES.FADE_OUT_DOWN)
                    .removeClass(CLASSES.FADE_OUT_UP);

                if (!this.isPresent) {
                    $body.append(this.$element);
                }

                this.isPresent = true;
            };

            this.hide = function () {
                var tooltip = this;

                tooltip.disappearing = true;

                if (tooltip.$element.hasClass('tt-tooltip-above')) {
                    tooltip.$element
                        .removeClass(CLASSES.FADE_IN_DOWN)
                        .addClass(CLASSES.FADE_OUT_UP);
                } else {
                    tooltip.$element
                        .removeClass(CLASSES.FADE_IN_UP)
                        .addClass(CLASSES.FADE_OUT_DOWN);
                }

                tooltip.te.removeTimer = setTimeout(function () {
                    if (tooltip.isPresent) {
                        tooltip.$element.detach();

                        tooltip.$tail.css('left', '');

                        $body.off('click.tt touchstart.tt', tooltip.te.onBodyClick);

                        $window.off('resize.tt', tooltip.te.onWindowResize);

                        tooltip.isPresent = false;
                    }
                }, 200);
            };

            this.calculatePosition = function (ePageX, ePageY) {
                var teElement, teOffsets, teOffset, tooltipTailOffsetX, tooltipTailLeft,
                    offsetYCorrection = 0;

                this.$tail.css('left', '');

                teElement = this.te.$element.get(0);
                if (ePageX !== undefined) {
                    tooltipTailOffsetX = ePageX;
                    teOffsets = teElement.getClientRects &&
                        teElement.getClientRects() ||
                        teElement.getBoundingClientRect();
                    if (teOffsets.length > 1) {
                        for (var i = teOffsets.length - 1; i >= 0; i--) {
                            if (ePageY >= Math.round($window.scrollTop() + teOffsets[i].top) &&
                                ePageY <= Math.round(
                                    $window.scrollTop() + teOffsets[i].top + teOffsets[i].height
                                )
                            ) {
                                teOffset = teOffsets[i];
                            }
                        }
                    }
                }

                if (!teOffset) {
                    teOffset = teElement.getClientRects &&
                        teElement.getClientRects()[0] ||
                        teElement.getBoundingClientRect();
                }
                teOffset = {
                    top: $window.scrollTop() + teOffset.top,
                    left: $window.scrollLeft() + teOffset.left,
                    width: teOffset.width,
                    height: teOffset.height
                };
                if (!tooltipTailOffsetX) {
                    tooltipTailOffsetX = (teOffset.left * 2 + teOffset.width) / 2;
                }
                if (CLIENT_NAME === 'msie' && this.te.type === 'supRef') {
                    offsetYCorrection = -Number(
                        this.te.$element.parent().css('font-size').replace('px', '')
                    ) / 2;
                }
                this.$element.css({
                    top: teOffset.top - this.$element.outerHeight() - 7 + offsetYCorrection,
                    left: tooltipTailOffsetX - 20,
                    right: ''
                });

                // Is it squished against the right side of the page?
                if (this.$element.offset().left + this.$element.outerWidth() > $window.width() - 1) {
                    this.$element.css({
                        left: '',
                        right: 0
                    });
                    tooltipTailLeft = tooltipTailOffsetX - this.$element.offset().left - 5;
                }

                // Is a part of it above the top of the screen?
                if (teOffset.top < this.$element.outerHeight() + $window.scrollTop() + 70) {
                    this.$element
                        .removeClass('tt-tooltip-above')
                        .addClass('tt-tooltip-below')
                        .addClass(CLASSES.FADE_IN_UP)
                        .css({
                            top: teOffset.top + teOffset.height + 9 + offsetYCorrection
                        });
                    if (tooltipTailLeft) {
                        this.$tail.css('left', (tooltipTailLeft + 12) + 'px');
                    }
                } else {
                    this.$element
                        .removeClass('tt-tooltip-below')
                        .addClass('tt-tooltip-above')
                        .addClass(CLASSES.FADE_IN_DOWN)
                        // A fix for cases when a tooltip shown once is then wrongly positioned when it
                        // is shown again after a window resize. We just repeat what is above.
                        .css({
                            top: teOffset.top - this.$element.outerHeight() - 7 + offsetYCorrection
                        });
                    if (tooltipTailLeft) {
                        // 12 is the tail element width/height
                        this.$tail.css('left', tooltipTailLeft + 'px');
                    }
                }
            };

            // Run some function for all the tooltips up to the top one in a tree. Its context will be
            // the tooltip, while its parameters may be passed to Tooltip.upToTopParent as an array
            // in the second parameter. If the third parameter passed to ToolTip.upToTopParent is true,
            // the execution stops when the function in question returns true for the first time,
            // and ToolTip.upToTopParent returns true as well.
            this.upToTopParent = function (func, parameters, stopAtTrue) {
                var returnValue,
                    currentTooltip = this;

                do {
                    returnValue = func.apply(currentTooltip, parameters);
                    if (stopAtTrue && returnValue) {
                        break;
                    }
                } while (currentTooltip = currentTooltip.parent);

                if (stopAtTrue) {
                    return returnValue;
                }
            };
        }


        teSelector = '';
        teSelector += COMMENTED_TEXT_SELECTOR;

        $content.find(teSelector).each(function () {
            if ($(this).hasClass(SOURCE_CLASS)) {
                $(this).removeClass(SOURCE_CLASS).addClass(TARGET_CLASS)
                $(this).data('toggle', '');
            }
            new TooltippedElement($(this));
        });
    }
    enabled = true;
    delay = 200;


    mw.hook('wikipage.content').add(tt);

}());