“MediaWiki:Gadget-ReferenceTooltips.js”的版本间的差异

来自维阿百科
跳转至: 导航搜索
(撤销霓虹灯鱼讨论)的版本4682)
1个标签撤销
 
第1行: 第1行:
/* <pre> */
+
// See [[mw:Reference Tooltips]]
/**
+
// Source https://en.wikipedia.org/wiki/MediaWiki:Gadget-ReferenceTooltips.js
* -------------------------------------------------------------------------
 
* !!! DON'T MODIFY THIS PAGE MANUALLY, YOUR CHANGES WILL BE OVERWRITTEN !!!
 
* -------------------------------------------------------------------------
 
*/
 
var _addText = '{{GHIACode|page=GHIA:MoegirlPediaInterfaceCodes/blob/master/src/gadgets/ReferenceTooltips/MediaWiki:Gadget-ReferenceTooltips.js|user=[[U:AnnAngela]]|co-authors=|longId=b16e23d7a02982d3b78d4dc0e1b5c99b466590aa|shortId=b16e23d|message=refactor: @annangela/prefer-reflect}}';
 
  
"use strict";
+
( function () {
var __extends = (this && this.__extends) || (function () {
 
    var extendStatics = function (d, b) {
 
        extendStatics = Object.setPrototypeOf ||
 
            ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
 
            function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
 
        return extendStatics(d, b);
 
    };
 
    return function (d, b) {
 
        if (typeof b !== "function" && b !== null)
 
            throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
 
        extendStatics(d, b);
 
        function __() { this.constructor = d; }
 
        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
 
    };
 
})();
 
var __assign = (this && this.__assign) || function () {
 
    __assign = Object.assign || function(t) {
 
        for (var s, i = 1, n = arguments.length; i < n; i++) {
 
            s = arguments[i];
 
            for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
 
                t[p] = s[p];
 
        }
 
        return t;
 
    };
 
    return __assign.apply(this, arguments);
 
};
 
var __read = (this && this.__read) || function (o, n) {
 
    var m = typeof Symbol === "function" && o[Symbol.iterator];
 
    if (!m) return o;
 
    var i = m.call(o), r, ar = [], e;
 
    try {
 
        while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
 
    }
 
    catch (error) { e = { error: error }; }
 
    finally {
 
        try {
 
            if (r && !r.done && (m = i["return"])) m.call(i);
 
        }
 
        finally { if (e) throw e.error; }
 
    }
 
    return ar;
 
};
 
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
 
    if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
 
        if (ar || !(i in from)) {
 
            if (!ar) ar = Array.prototype.slice.call(from, 0, i);
 
            ar[i] = from[i];
 
        }
 
    }
 
    return to.concat(ar || Array.prototype.slice.call(from));
 
};
 
(function () {
 
    var REF_LINK_SELECTOR = '.reference, a[href^="#CITEREF"]', COMMENTED_TEXT_CLASS = "rt-commentedText", COMMENTED_TEXT_SELECTOR = "".concat(COMMENTED_TEXT_CLASS ? ".".concat(COMMENTED_TEXT_CLASS, ", ") : "", "abbr[title]");
 
    mw.messages.set(wgULS({
 
        "rt-settings": "参考文献提示工具设置",
 
        "rt-enable-footer": "启用参考文献提示工具",
 
        "rt-settings-title": "参考文献提示工具",
 
        "rt-save": "保存",
 
        "rt-cancel": "取消",
 
        "rt-enable": "启用",
 
        "rt-disable": "禁用",
 
        "rt-activationMethod": "提示工具显示方式",
 
        "rt-hovering": "鼠标悬浮",
 
        "rt-clicking": "点击",
 
        "rt-delay": "工具提示显示延迟(毫秒)",
 
        "rt-tooltipsForComments": "\u5728\u53C2\u8003\u6587\u732E\u63D0\u793A\u5DE5\u5177\u6837\u5F0F\u4E2D\u7528<span title=\"\u63D0\u793A\u5DE5\u5177\u7684\u4F8B\u5B50\" class=\"".concat(COMMENTED_TEXT_CLASS || "rt-commentedText", "\" style=\"border-bottom: 1px dotted; cursor: help;\">\u865A\u4E0B\u5212\u7EBF</span>\u7684\u65B9\u5F0F\u5728\u6587\u5B57\u4E0A\u663E\u793A\u63D0\u793A\u5DE5\u5177\uFF08\u53EF\u4EE5\u5728\u4E0D\u652F\u6301\u9F20\u6807\u7684\u8BBE\u5907\u4E0A\u663E\u793A\u63D0\u793A\u5DE5\u5177\uFF09"),
 
        "rt-disabledNote": "你可以通过页脚中的链接重新启用参考文献提示工具",
 
        "rt-done": "完成",
 
        "rt-enabled": "参考文献提示工具已启用"
 
    }, {
 
        "rt-settings": "參考文獻提示工具設定",
 
        "rt-enable-footer": "啟用參考文獻提示工具",
 
        "rt-settings-title": "參考文獻提示工具",
 
        "rt-save": "儲存",
 
        "rt-cancel": "取消",
 
        "rt-enable": "啟用",
 
        "rt-disable": "停用",
 
        "rt-activationMethod": "提示工具顯示方式",
 
        "rt-hovering": "滑鼠懸浮",
 
        "rt-clicking": "點擊",
 
        "rt-delay": "工具提示顯示延遲(毫秒)",
 
        "rt-tooltipsForComments": "\u5728\u53C3\u8003\u6587\u737B\u63D0\u793A\u5DE5\u5177\u6A23\u5F0F\u4E2D\u7528<span title=\"\u63D0\u793A\u5DE5\u5177\u7684\u4F8B\u5B50\" class=\"".concat(COMMENTED_TEXT_CLASS || "rt-commentedText", "\" style=\"border-bottom: 1px dotted; cursor: help;\">\u865B\u5E95\u7DDA</span>\u7684\u65B9\u5F0F\u5728\u6587\u5B57\u4E0A\u986F\u793A\u63D0\u793A\u5DE5\u5177\uFF08\u53EF\u4EE5\u5728\u4E0D\u652F\u6301\u6ED1\u9F20\u7684\u88DD\u7F6E\u4E0A\u986F\u793A\u63D0\u793A\u5DE5\u5177\uFF09"),
 
        "rt-disabledNote": "你可以通過頁尾中的連結重新啟用參考文獻提示工具",
 
        "rt-done": "完成",
 
        "rt-enabled": "參考文獻提示工具已啟用"
 
    }));
 
    var SECONDS_IN_A_DAY = 60 * 60 * 24, CLASSES = {
 
        FADE_IN_DOWN: "rt-fade-in-down",
 
        FADE_IN_UP: "rt-fade-in-up",
 
        FADE_OUT_DOWN: "rt-fade-out-down",
 
        FADE_OUT_UP: "rt-fade-out-up"
 
    }, IS_TOUCHSCREEN = Reflect.has(document.documentElement, "ontouchstart"), IS_MOBILE = /Mobi|Android/i.test(navigator.userAgent) ||
 
        typeof window.orientation !== "undefined", CLIENT_NAME = $.client.profile().name, $body = $(document.body), $window = $(window);
 
    var settings, enabled, delay, activatedByClick, tooltipsForComments, windowManager;
 
    var rt = function ($content) {
 
        if (window.pg || mw.config.get("wgPopupsReferencePreviews")) {
 
            return;
 
        }
 
        var teSelector, settingsDialogOpening = false;
 
        var setSettingsCookie = function () { return mw.cookie.set("RTsettings", "".concat(+enabled, "|").concat(delay, "|").concat(+activatedByClick, "|").concat(+tooltipsForComments), { path: "/", expires: 90 * SECONDS_IN_A_DAY, prefix: "" }); };
 
        var enableRt = function () {
 
            enabled = true;
 
            setSettingsCookie();
 
            $(".rt-enableItem").remove();
 
            rt($content);
 
            mw.notify(mw.msg("rt-enabled"));
 
        };
 
        var disableRt = function () {
 
            $content.find(teSelector).removeClass("rt-commentedText").off(".rt");
 
            $body.off(".rt");
 
            $window.off(".rt");
 
        };
 
        var addEnableLink = function () {
 
            var $footer = $("#footer-places, #f-list");
 
            if (!$footer.length) {
 
                $footer = $("#footer li").parent();
 
            }
 
            $footer.append($("<li>")
 
                .addClass("rt-enableItem")
 
                .append($("<a>")
 
                .text(mw.msg("rt-enable-footer"))
 
                .attr("href", "javascript:")
 
                .on("click", function (e) {
 
                e.preventDefault();
 
                enableRt();
 
            })));
 
        };
 
        var TooltippedElement = (function () {
 
            function TooltippedElement($element) {
 
                var _this = this;
 
                this.onBodyClick = function (e) {
 
                    var _a;
 
                    if (!_this.tooltip && !((_a = _this.$ref) === null || _a === void 0 ? void 0 : _a.hasClass("rt-target"))) {
 
                        return;
 
                    }
 
                    var $current = $(e.target);
 
                    var contextMatchesParameter = function (parameter) {
 
                        return this === parameter;
 
                    };
 
                    while ($current.length &&
 
                        (!$current.hasClass("rt-tooltip") ||
 
                            !$current.data("tooltip") ||
 
                            !$current.data("tooltip").upToTopParent(contextMatchesParameter, [_this.tooltip], true))) {
 
                        $current = $current.parent();
 
                    }
 
                    if (!$current.length) {
 
                        _this.hideRef();
 
                    }
 
                };
 
                var events;
 
                if (!$element) {
 
                    return;
 
                }
 
                var onStartEvent = function (e) {
 
                    var showRefArgs;
 
                    if (activatedByClick && _this.type !== "commentedText" && e.type !== "contextmenu") {
 
                        e.preventDefault();
 
                    }
 
                    if (!_this.noRef) {
 
                        showRefArgs = [_this.$element];
 
                        if (_this.type !== "supRef") {
 
                            showRefArgs.push(e.pageX, e.pageY);
 
                        }
 
                        _this.showRef.apply(_this, __spreadArray([], __read(showRefArgs), false));
 
                    }
 
                };
 
                var onEndEvent = function () {
 
                    if (!_this.noRef) {
 
                        _this.hideRef();
 
                    }
 
                };
 
                this.$element = $element;
 
                this.$originalElement = $element;
 
                if (this.$element.is(REF_LINK_SELECTOR)) {
 
                    if (this.$element.prop("tagName") === "SUP") {
 
                        this.type = "supRef";
 
                    }
 
                    else {
 
                        this.type = "harvardRef";
 
                    }
 
                }
 
                else {
 
                    this.type = "commentedText";
 
                    this.comment = this.$element.attr("title");
 
                    if (!this.comment) {
 
                        return;
 
                    }
 
                    this.$element.addClass("rt-commentedText");
 
                }
 
                if (activatedByClick) {
 
                    events = {
 
                        "click.rt": onStartEvent
 
                    };
 
                    if (this.type === "commentedText" &&
 
                        (this.$element.closest("a").length ||
 
                            this.$element.has("a").length)) {
 
                        events["contextmenu.rt"] = onStartEvent;
 
                    }
 
                }
 
                else {
 
                    events = {
 
                        "mouseenter.rt": onStartEvent,
 
                        "mouseleave.rt": onEndEvent
 
                    };
 
                }
 
                this.$element.on(events);
 
            }
 
            TooltippedElement.prototype.hideRef = function (immediately) {
 
                var _this = this;
 
                clearTimeout(this.showTimer);
 
                if (this.type === "commentedText") {
 
                    this.$element.attr("title", this.comment);
 
                }
 
                if (this.tooltip && this.tooltip.isPresent) {
 
                    if (activatedByClick || immediately) {
 
                        this.tooltip.hide();
 
                    }
 
                    else {
 
                        this.hideTimer = setTimeout(function () {
 
                            _this.tooltip.hide();
 
                        }, 200);
 
                    }
 
                }
 
                else if (this.$ref && this.$ref.hasClass("rt-target")) {
 
                    this.$ref.removeClass("rt-target");
 
                    if (activatedByClick) {
 
                        $body.off("click.rt touchstart.rt", this.onBodyClick);
 
                    }
 
                }
 
            };
 
            TooltippedElement.prototype.showRef = function ($element, ePageX, ePageY) {
 
                var _this = this;
 
                if (window.pg) {
 
                    disableRt();
 
                    return;
 
                }
 
                if (this.tooltip && !this.tooltip.$content.length) {
 
                    return;
 
                }
 
                var tooltipInitiallyPresent = this.tooltip && this.tooltip.isPresent;
 
                var reallyShow = function () {
 
                    var viewportTop, refOffsetTop, teHref;
 
                    if (!_this.$ref && !_this.comment) {
 
                        teHref = _this.type === "supRef" ?
 
                            _this.$element.find("a").attr("href") :
 
                            _this.$element.attr("href");
 
                        _this.$ref = teHref &&
 
                            $("#".concat($.escapeSelector(teHref.slice(1))));
 
                        if (!_this.$ref || !_this.$ref.length || !_this.$ref.text()) {
 
                            _this.noRef = true;
 
                            return;
 
                        }
 
                    }
 
                    if (!tooltipInitiallyPresent && !_this.comment) {
 
                        viewportTop = $window.scrollTop();
 
                        refOffsetTop = _this.$ref.offset().top;
 
                        if (!activatedByClick &&
 
                            viewportTop < refOffsetTop &&
 
                            viewportTop + $window.height() > refOffsetTop + _this.$ref.height() &&
 
                            $window.width() > _this.$ref.offset().left + _this.$ref.width()) {
 
                            _this.$ref.addClass("rt-target");
 
                            return;
 
                        }
 
                    }
 
                    if (!_this.tooltip) {
 
                        _this.tooltip = new Tooltip(_this);
 
                        if (!_this.tooltip.$content.length) {
 
                            return;
 
                        }
 
                    }
 
                    _this.tooltip.parent = _this.$element.closest(".rt-tooltip").data("tooltip");
 
                    if (_this.tooltip.parent && _this.tooltip.parent.disappearing) {
 
                        return;
 
                    }
 
                    _this.tooltip.show();
 
                    if (tooltipInitiallyPresent) {
 
                        if (_this.tooltip.$element.hasClass("rt-tooltip-above")) {
 
                            _this.tooltip.$element.addClass(CLASSES.FADE_IN_DOWN);
 
                        }
 
                        else {
 
                            _this.tooltip.$element.addClass(CLASSES.FADE_IN_UP);
 
                        }
 
                        return;
 
                    }
 
                    _this.tooltip.calculatePosition(ePageX, ePageY);
 
                    $window.on("resize.rt", _this.onWindowResize);
 
                };
 
                this.$element = $element;
 
                if (this.type === "commentedText") {
 
                    this.$element.attr("title", "");
 
                }
 
                if (activatedByClick) {
 
                    if (tooltipInitiallyPresent ||
 
                        this.$ref && this.$ref.hasClass("rt-target")) {
 
                        return;
 
                    }
 
                    setTimeout(function () {
 
                        $body.on("click.rt touchstart.rt", _this.onBodyClick);
 
                    }, 0);
 
                }
 
                if (activatedByClick || tooltipInitiallyPresent) {
 
                    reallyShow();
 
                }
 
                else {
 
                    this.showTimer = setTimeout(reallyShow, delay);
 
                }
 
            };
 
            TooltippedElement.prototype.onWindowResize = function () {
 
                this.tooltip.calculatePosition();
 
            };
 
            return TooltippedElement;
 
        }());
 
        var SettingsDialog = (function (_super) {
 
            __extends(SettingsDialog, _super);
 
            function SettingsDialog() {
 
                return _super !== null && _super.apply(this, arguments) || this;
 
            }
 
            SettingsDialog.prototype.initialize = function () {
 
                var _this = this;
 
                var args = [];
 
                for (var _i = 0; _i < arguments.length; _i++) {
 
                    args[_i] = arguments[_i];
 
                }
 
                _super.prototype.initialize.apply(this, __spreadArray([], __read(args), false));
 
                this.enableOption = new OO.ui.RadioOptionWidget({
 
                    label: mw.msg("rt-enable")
 
                });
 
                this.disableOption = new OO.ui.RadioOptionWidget({
 
                    label: mw.msg("rt-disable")
 
                });
 
                this.enableSelect = new OO.ui.RadioSelectWidget({
 
                    items: [this.enableOption, this.disableOption],
 
                    classes: ["rt-enableSelect"]
 
                });
 
                this.enableSelect.selectItem(this.enableOption);
 
                this.enableSelect.on("choose", function (item) {
 
                    if (item === _this.disableOption) {
 
                        _this.activationMethodSelect.setDisabled(true);
 
                        _this.delayInput.setDisabled(true);
 
                        _this.tooltipsForCommentsCheckbox.setDisabled(true);
 
                    }
 
                    else {
 
                        _this.activationMethodSelect.setDisabled(false);
 
                        _this.delayInput.setDisabled(_this.clickOption.isSelected());
 
                        _this.tooltipsForCommentsCheckbox.setDisabled(false);
 
                    }
 
                });
 
                this.hoverOption = new OO.ui.RadioOptionWidget({
 
                    label: mw.msg("rt-hovering")
 
                });
 
                this.clickOption = new OO.ui.RadioOptionWidget({
 
                    label: mw.msg("rt-clicking")
 
                });
 
                this.activationMethodSelect = new OO.ui.RadioSelectWidget({
 
                    items: [this.hoverOption, this.clickOption]
 
                });
 
                this.activationMethodSelect.selectItem(activatedByClick ?
 
                    this.clickOption :
 
                    this.hoverOption);
 
                this.activationMethodSelect.on("choose", function (item) {
 
                    if (item === _this.clickOption) {
 
                        _this.delayInput.setDisabled(true);
 
                    }
 
                    else {
 
                        _this.delayInput.setDisabled(_this.clickOption.isSelected());
 
                    }
 
                });
 
                this.activationMethodField = new OO.ui.FieldLayout(this.activationMethodSelect, {
 
                    label: mw.msg("rt-activationMethod"),
 
                    align: "top"
 
                });
 
                this.delayInput = new OO.ui.NumberInputWidget({
 
                    input: { value: delay },
 
                    step: 50,
 
                    min: 0,
 
                    max: 5000,
 
                    disabled: activatedByClick,
 
                    classes: ["rt-numberInput"]
 
                });
 
                this.delayField = new OO.ui.FieldLayout(this.delayInput, {
 
                    label: mw.msg("rt-delay"),
 
                    align: "top"
 
                });
 
                this.tooltipsForCommentsCheckbox = new OO.ui.CheckboxInputWidget({
 
                    selected: tooltipsForComments
 
                });
 
                this.tooltipsForCommentsField = new OO.ui.FieldLayout(this.tooltipsForCommentsCheckbox, {
 
                    label: new OO.ui.HtmlSnippet(mw.msg("rt-tooltipsForComments")),
 
                    align: "inline",
 
                    classes: ["rt-tooltipsForCommentsField"]
 
                });
 
                new TooltippedElement(this.tooltipsForCommentsField.$element.find(".".concat(COMMENTED_TEXT_CLASS || "rt-commentedText")));
 
                this.fieldset = new OO.ui.FieldsetLayout();
 
                this.fieldset.addItems([
 
                    this.activationMethodField,
 
                    this.delayField,
 
                    this.tooltipsForCommentsField,
 
                ]);
 
                this.panelSettings = new OO.ui.PanelLayout({
 
                    padded: true,
 
                    expanded: false
 
                });
 
                this.panelSettings.$element.append(this.enableSelect.$element, $("<hr>").addClass("rt-settingsFormSeparator"), this.fieldset.$element);
 
                this.panelDisabled = new OO.ui.PanelLayout({
 
                    padded: true,
 
                    expanded: false
 
                });
 
                this.panelDisabled.$element.append($("<table>")
 
                    .addClass("rt-disabledHelp")
 
                    .append($("<tr>").append($("<td>").append($("<img>").attr("src", "https://en.wikipedia.org/w/load.php?modules=ext.popups.images&image=footer&format=rasterized&lang=ru&skin=vector&version=0uotisb")), $("<td>")
 
                    .addClass("rt-disabledNote")
 
                    .text(mw.msg("rt-disabledNote")))));
 
                this.stackLayout = new OO.ui.StackLayout({
 
                    items: [this.panelSettings, this.panelDisabled]
 
                });
 
                this.$body.append(this.stackLayout.$element);
 
            };
 
            SettingsDialog.prototype.getSetupProcess = function (data) {
 
                var _this = this;
 
                return SettingsDialog["super"].prototype.getSetupProcess.bind(this)(data)
 
                    .next(function () {
 
                    _this.stackLayout.setItem(_this.panelSettings);
 
                    _this.actions.setMode("basic");
 
                }, this);
 
            };
 
            SettingsDialog.prototype.getActionProcess = function (action) {
 
                var _this = this;
 
                if (action === "save") {
 
                    return new OO.ui.Process(function () {
 
                        var newDelay = +_this.delayInput.getValue();
 
                        enabled = _this.enableOption.isSelected();
 
                        if (newDelay >= 0 && newDelay <= 5000) {
 
                            delay = newDelay;
 
                        }
 
                        activatedByClick = _this.clickOption.isSelected();
 
                        tooltipsForComments = _this.tooltipsForCommentsCheckbox.isSelected();
 
                        setSettingsCookie();
 
                        if (enabled) {
 
                            _this.close();
 
                            disableRt();
 
                            rt($content);
 
                        }
 
                        else {
 
                            _this.actions.setMode("disabled");
 
                            _this.stackLayout.setItem(_this.panelDisabled);
 
                            disableRt();
 
                            addEnableLink();
 
                        }
 
                    });
 
                }
 
                else if (action === "deactivated") {
 
                    this.close();
 
                }
 
                return SettingsDialog["super"].prototype.getActionProcess.bind(this)(action);
 
            };
 
            SettingsDialog.prototype.getBodyHeight = function () {
 
                return this.stackLayout.getCurrentItem().$element.outerHeight(true);
 
            };
 
            SettingsDialog.static = __assign(__assign({}, _super.static), { tagName: "div", name: "settingsDialog", title: "参考文献提示工具", actions: [
 
                    {
 
                        modes: "basic",
 
                        action: "save",
 
                        label: "保存",
 
                        flags: [
 
                            "primary",
 
                            "progressive",
 
                        ]
 
                    },
 
                    {
 
                        modes: "basic",
 
                        label: "取消",
 
                        flags: "safe"
 
                    },
 
                    {
 
                        modes: "disabled",
 
                        action: "deactivated",
 
                        label: "完成",
 
                        flags: [
 
                            "primary",
 
                            "progressive",
 
                        ]
 
                    },
 
                ] });
 
            return SettingsDialog;
 
        }(OO.ui.ProcessDialog));
 
        var Tooltip = (function () {
 
            function Tooltip(te) {
 
                var _this = this;
 
                this.te = te;
 
                switch (this.te.type) {
 
                    case "supRef":
 
                        this.id = "rt-".concat(this.te.$originalElement.attr("id"));
 
                        this.$content = this.te.$ref
 
                            .contents()
 
                            .filter(function (i, ele) {
 
                            var $this = $(ele);
 
                            return ele.nodeType === Node.TEXT_NODE ||
 
                                !($this.is(".mw-cite-backlink") ||
 
                                    i === 0 &&
 
                                        ($this.is("b") ||
 
                                            $this.is("a") &&
 
                                                $this.attr("href").indexOf("#ref") === 0));
 
                        })
 
                            .clone(true);
 
                        break;
 
                    case "harvardRef":
 
                        this.id = "rt-".concat(this.te.$originalElement.closest("li").attr("id"));
 
                        this.$content = this.te.$ref
 
                            .clone(true)
 
                            .removeAttr("id");
 
                        break;
 
                    case "commentedText":
 
                        this.id = "rt-".concat("".concat(Math.random()).slice(2));
 
                        this.$content = $(document.createTextNode(this.te.comment));
 
                        break;
 
                }
 
                if (!this.$content.length) {
 
                    return;
 
                }
 
                this.insideWindow = !!this.te.$element.closest(".oo-ui-window").length;
 
                this.$element = $("<div>")
 
                    .addClass("rt-tooltip")
 
                    .attr("id", this.id)
 
                    .attr("role", "tooltip")
 
                    .data("tooltip", this);
 
                if (this.insideWindow) {
 
                    this.$element.addClass("rt-tooltip-insideWindow");
 
                }
 
                this.$content = this.$content
 
                    .wrapAll("<div>")
 
                    .parent()
 
                    .addClass("rt-tooltipContent")
 
                    .addClass("mw-parser-output")
 
                    .appendTo(this.$element);
 
                if (!activatedByClick) {
 
                    this.$element
 
                        .on("mouseenter", function () {
 
                        if (!_this.disappearing) {
 
                            _this.upToTopParent(function (tt) {
 
                                tt.show();
 
                            });
 
                        }
 
                    })
 
                        .on("mouseleave", function (e) {
 
                        if (CLIENT_NAME !== "chrome" ||
 
                            (!e.originalEvent ||
 
                                e.originalEvent.relatedTarget !== null ||
 
                                !_this.clickedTime ||
 
                                Date.now() - _this.clickedTime > 50)) {
 
                            _this.upToTopParent(function (tt) {
 
                                tt.te.hideRef();
 
                            });
 
                        }
 
                    })
 
                        .on("click", function () {
 
                        _this.clickedTime = Date.now();
 
                    });
 
                }
 
                if (!this.insideWindow) {
 
                    $("<div>")
 
                        .addClass("rt-settingsLink")
 
                        .attr("title", mw.msg("rt-settings"))
 
                        .on("click", function () {
 
                        if (settingsDialogOpening) {
 
                            return;
 
                        }
 
                        settingsDialogOpening = true;
 
                        _this.upToTopParent(function (tt) {
 
                            if (tt.isPresent) {
 
                                if (tt.$element[0].style.right) {
 
                                    tt.$element.css("right", "+=".concat(window.innerWidth - $window.width()));
 
                                }
 
                                tt.te.hideRef(true);
 
                            }
 
                        });
 
                        if (!windowManager) {
 
                            windowManager = new OO.ui.WindowManager();
 
                            $body.append(windowManager.$element);
 
                        }
 
                        var settingsDialog = new SettingsDialog();
 
                        windowManager.addWindows([settingsDialog]);
 
                        var settingsWindow = windowManager.openWindow(settingsDialog);
 
                        settingsWindow.opened.then(function () {
 
                            settingsDialogOpening = false;
 
                        });
 
                        settingsWindow.closed.then(function () {
 
                            windowManager.clearWindows();
 
                        });
 
                    })
 
                        .prependTo(this.$content);
 
                }
 
                this.$tail = $("<div>")
 
                    .addClass("rt-tooltipTail")
 
                    .prependTo(this.$element);
 
                this.disappearing = false;
 
            }
 
            Tooltip.prototype.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;
 
            };
 
            Tooltip.prototype.hide = function () {
 
                var _this = this;
 
                this.disappearing = true;
 
                if (this.$element.hasClass("rt-tooltip-above")) {
 
                    this.$element
 
                        .removeClass(CLASSES.FADE_IN_DOWN)
 
                        .addClass(CLASSES.FADE_OUT_UP);
 
                }
 
                else {
 
                    this.$element
 
                        .removeClass(CLASSES.FADE_IN_UP)
 
                        .addClass(CLASSES.FADE_OUT_DOWN);
 
                }
 
                this.te.removeTimer = setTimeout(function () {
 
                    if (_this.isPresent) {
 
                        _this.$element.detach();
 
                        _this.$tail.css("left", "");
 
                        if (activatedByClick) {
 
                            $body.off("click.rt touchstart.rt", _this.te.onBodyClick);
 
                        }
 
                        $window.off("resize.rt", _this.te.onWindowResize);
 
                        _this.isPresent = false;
 
                    }
 
                }, 200);
 
            };
 
            Tooltip.prototype.calculatePosition = function (ePageX, ePageY) {
 
                var teOffsets, teOffset, tooltipTailOffsetX, tooltipTailLeft, offsetYCorrection = 0;
 
                this.$tail.css("left", "");
 
                var 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 = -+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: ""
 
                });
 
                if (this.$element.offset().left + this.$element.outerWidth() > $window.width() - 1) {
 
                    this.$element.css({
 
                        left: "",
 
                        right: 0
 
                    });
 
                    tooltipTailLeft = tooltipTailOffsetX - this.$element.offset().left - 5;
 
                }
 
                if (teOffset.top < this.$element.outerHeight() + $window.scrollTop() + 6) {
 
                    this.$element
 
                        .removeClass("rt-tooltip-above")
 
                        .addClass("rt-tooltip-below")
 
                        .addClass(CLASSES.FADE_IN_UP)
 
                        .css({
 
                        top: teOffset.top + teOffset.height + 9 + offsetYCorrection
 
                    });
 
                    if (tooltipTailLeft) {
 
                        this.$tail.css("left", "".concat(tooltipTailLeft + 12, "px"));
 
                    }
 
                }
 
                else {
 
                    this.$element
 
                        .removeClass("rt-tooltip-below")
 
                        .addClass("rt-tooltip-above")
 
                        .addClass(CLASSES.FADE_IN_DOWN)
 
                        .css({
 
                        top: teOffset.top - this.$element.outerHeight() - 7 + offsetYCorrection
 
                    });
 
                    if (tooltipTailLeft) {
 
                        this.$tail.css("left", "".concat(tooltipTailLeft, "px"));
 
                    }
 
                }
 
            };
 
            Tooltip.prototype.upToTopParent = function (func, parameters, stopAtTrue) {
 
                var returnValue, currentTooltip = this;
 
                do {
 
                    returnValue = func.bind(currentTooltip).apply(void 0, __spreadArray([currentTooltip], __read(parameters !== null && parameters !== void 0 ? parameters : []), false));
 
                    if (stopAtTrue && returnValue) {
 
                        break;
 
                    }
 
                    currentTooltip = currentTooltip.parent;
 
                } while (currentTooltip);
 
                if (stopAtTrue) {
 
                    return returnValue;
 
                }
 
            };
 
            return Tooltip;
 
        }());
 
        if (!enabled) {
 
            addEnableLink();
 
            return;
 
        }
 
        teSelector = REF_LINK_SELECTOR;
 
        if (tooltipsForComments) {
 
            teSelector += ", ".concat(COMMENTED_TEXT_SELECTOR);
 
        }
 
        $content.find(teSelector).each(function (_, ele) {
 
            new TooltippedElement($(ele));
 
        });
 
    };
 
    var settingsString = mw.cookie.get("RTsettings", "");
 
    if (settingsString) {
 
        settings = settingsString.split("|");
 
        enabled = !!+settings[0];
 
        delay = +settings[1];
 
        activatedByClick = !!+settings[2];
 
        tooltipsForComments = settings[3] === undefined ?
 
            IS_TOUCHSCREEN && IS_MOBILE :
 
            !!+settings[3];
 
    }
 
    else {
 
        enabled = true;
 
        delay = 200;
 
        activatedByClick = IS_TOUCHSCREEN;
 
        tooltipsForComments = IS_TOUCHSCREEN && IS_MOBILE;
 
    }
 
    mw.hook("wikipage.content").add(rt);
 
})();
 
  
/* </pre> */
+
// enwiki settings
 +
var REF_LINK_SELECTOR = '.reference, a[href^="#CITEREF"]',
 +
COMMENTED_TEXT_CLASS = 'rt-commentedText',
 +
COMMENTED_TEXT_SELECTOR = ( COMMENTED_TEXT_CLASS ? '.' + COMMENTED_TEXT_CLASS + ', ' : '') +
 +
'abbr[title]';
 +
 
 +
mw.messages.set(wgULS({
 +
'rt-settings': '参考文献提示工具设置',
 +
'rt-enable-footer': '启用参考文献提示工具',
 +
'rt-settings-title': '参考文献提示工具',
 +
'rt-save': '保存',
 +
'rt-cancel': '取消',
 +
'rt-enable': '启用',
 +
'rt-disable': '禁用',
 +
'rt-activationMethod': '提示工具显示方式',
 +
'rt-hovering': '鼠标悬浮',
 +
'rt-clicking': '点击',
 +
'rt-delay': '工具提示显示延迟(毫秒)',
 +
'rt-tooltipsForComments': '在参考文献提示工具样式中用<span title="提示工具的例子" class="' + (COMMENTED_TEXT_CLASS || 'rt-commentedText') + '" style="border-bottom: 1px dotted; cursor: help;">虚下划线</span>的方式在文字上显示提示工具(可以在不支持鼠标的设备上显示提示工具)',
 +
'rt-disabledNote': '你可以通过页脚中的链接重新启用参考文献提示工具',
 +
'rt-done': '完成',
 +
'rt-enabled': '参考文献提示工具已启用'
 +
}, {
 +
'rt-settings': '參考文獻提示工具設定',
 +
'rt-enable-footer': '啟用參考文獻提示工具',
 +
'rt-settings-title': '參考文獻提示工具',
 +
'rt-save': '儲存',
 +
'rt-cancel': '取消',
 +
'rt-enable': '啟用',
 +
'rt-disable': '停用',
 +
'rt-activationMethod': '提示工具顯示方式',
 +
'rt-hovering': '滑鼠懸浮',
 +
'rt-clicking': '點擊',
 +
'rt-delay': '工具提示顯示延遲(毫秒)',
 +
'rt-tooltipsForComments': '在參考文獻提示工具樣式中用<span title="提示工具的例子" class="' + (COMMENTED_TEXT_CLASS || 'rt-commentedText') + '" style="border-bottom: 1px dotted; cursor: help;">虛底線</span>的方式在文字上顯示提示工具(可以在不支持滑鼠的裝置上顯示提示工具)',
 +
'rt-disabledNote': '你可以通過頁尾中的連結重新啟用參考文獻提示工具',
 +
'rt-done': '完成',
 +
'rt-enabled': '參考文獻提示工具已啟用'
 +
}));
 +
 
 +
// "Global" variables
 +
var SECONDS_IN_A_DAY = 60 * 60 * 24,
 +
CLASSES = {
 +
FADE_IN_DOWN: 'rt-fade-in-down',
 +
FADE_IN_UP: 'rt-fade-in-up',
 +
FADE_OUT_DOWN: 'rt-fade-out-down',
 +
FADE_OUT_UP: 'rt-fade-out-up'
 +
},
 +
IS_TOUCHSCREEN = 'ontouchstart' in document.documentElement,
 +
// Quite a rough check for mobile browsers, a mix of what is advised at
 +
// https://stackoverflow.com/a/24600597 (sends to
 +
// https://developer.mozilla.org/en-US/docs/Browser_detection_using_the_user_agent)
 +
// and https://stackoverflow.com/a/14301832
 +
IS_MOBILE = /Mobi|Android/i.test( navigator.userAgent ) ||
 +
typeof window.orientation !== 'undefined',
 +
CLIENT_NAME = $.client.profile().name,
 +
settingsString, settings, enabled, delay, activatedByClick, tooltipsForComments, cursorWaitCss,
 +
windowManager,
 +
$body = $( document.body ),
 +
$window = $( window );
 +
 
 +
function rt( $content ) {
 +
// Popups gadget & Reference Previews
 +
if ( window.pg || mw.config.get("wgPopupsReferencePreviews") ) {
 +
return;
 +
}
 +
 
 +
var teSelector,
 +
settingsDialogOpening = false;
 +
 
 +
function setSettingsCookie() {
 +
mw.cookie.set(
 +
'RTsettings',
 +
Number( enabled ) + '|' + delay + '|' + Number( activatedByClick ) + '|' +
 +
Number( tooltipsForComments ),
 +
{ path: '/', expires: 90 * SECONDS_IN_A_DAY, prefix: '' }
 +
);
 +
}
 +
 
 +
function enableRt() {
 +
enabled = true;
 +
setSettingsCookie();
 +
$( '.rt-enableItem' ).remove();
 +
rt( $content );
 +
mw.notify( mw.msg( 'rt-enabled' ) );
 +
}
 +
 
 +
function disableRt() {
 +
$content.find( teSelector ).removeClass( 'rt-commentedText' ).off( '.rt' );
 +
$body.off( '.rt' );
 +
$window.off( '.rt' );
 +
}
 +
 
 +
function addEnableLink() {
 +
// #footer-places – Vector
 +
// #f-list – Timeless, Monobook, Modern
 +
// parent of #footer li – Cologne Blue
 +
var $footer = $( '#footer-places, #f-list' );
 +
if ( !$footer.length ) {
 +
$footer = $( '#footer li' ).parent();
 +
}
 +
$footer.append(
 +
$( '<li>' )
 +
.addClass( 'rt-enableItem' )
 +
.append(
 +
$( '<a>' )
 +
.text( mw.msg( 'rt-enable-footer' ) )
 +
.attr( 'href', 'javascript:' )
 +
.click( function ( e ) {
 +
e.preventDefault();
 +
enableRt();
 +
} )
 +
)
 +
);
 +
}
 +
 
 +
function TooltippedElement( $element ) {
 +
var tooltip,
 +
events,
 +
te = this;
 +
 
 +
function onStartEvent( e ) {
 +
var showRefArgs;
 +
 
 +
if ( activatedByClick && te.type !== 'commentedText' && e.type !== 'contextmenu' ) {
 +
e.preventDefault();
 +
}
 +
if ( !te.noRef ) {
 +
showRefArgs = [ $( this ) ];
 +
if ( te.type !== 'supRef' ) {
 +
showRefArgs.push( e.pageX, e.pageY );
 +
}
 +
te.showRef.apply( te, showRefArgs );
 +
}
 +
}
 +
 
 +
function onEndEvent() {
 +
if ( !te.noRef ) {
 +
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;
 +
if ( this.$element.is( REF_LINK_SELECTOR ) ) {
 +
if ( this.$element.prop( 'tagName' ) === 'SUP' ) {
 +
this.type = 'supRef';
 +
} else {
 +
this.type = 'harvardRef';
 +
}
 +
} else {
 +
this.type = 'commentedText';
 +
this.comment = this.$element.attr( 'title' );
 +
if ( !this.comment ) {
 +
return;
 +
}
 +
this.$element.addClass('rt-commentedText');
 +
}
 +
 +
if ( activatedByClick ) {
 +
events = {
 +
'click.rt': onStartEvent
 +
};
 +
// Adds an ability to see tooltips for links
 +
if ( this.type === 'commentedText' &&
 +
( this.$element.closest( 'a' ).length ||
 +
this.$element.has( 'a' ).length
 +
)
 +
) {
 +
events[ 'contextmenu.rt' ] = onStartEvent;
 +
}
 +
} else {
 +
events = {
 +
'mouseenter.rt': onStartEvent,
 +
'mouseleave.rt': onEndEvent
 +
};
 +
}
 +
 
 +
this.$element.on( events );
 +
 
 +
this.hideRef = function ( immediately ) {
 +
clearTimeout( te.showTimer );
 +
 
 +
if ( this.type === 'commentedText' ) {
 +
this.$element.attr( 'title', this.comment );
 +
}
 +
 
 +
if ( this.tooltip && this.tooltip.isPresent ) {
 +
if ( activatedByClick || immediately ) {
 +
this.tooltip.hide();
 +
} else {
 +
this.hideTimer = setTimeout( function () {
 +
te.tooltip.hide();
 +
}, 200 );
 +
}
 +
} else if ( this.$ref && this.$ref.hasClass( 'rt-target' ) ) {
 +
this.$ref.removeClass( 'rt-target' );
 +
if ( activatedByClick ) {
 +
$body.off( 'click.rt touchstart.rt', this.onBodyClick );
 +
}
 +
}
 +
};
 +
 
 +
this.showRef = function ( $element, ePageX, ePageY ) {
 +
// Popups gadget
 +
if ( window.pg ) {
 +
disableRt();
 +
return;
 +
}
 +
 +
if ( this.tooltip && !this.tooltip.$content.length ) {
 +
return;
 +
}
 +
 
 +
var tooltipInitiallyPresent = this.tooltip && this.tooltip.isPresent;
 +
 
 +
function reallyShow() {
 +
var viewportTop, refOffsetTop, teHref;
 +
 
 +
if ( !te.$ref && !te.comment ) {
 +
teHref = te.type === 'supRef' ?
 +
te.$element.find( 'a' ).attr( 'href' ) :
 +
te.$element.attr( 'href' ); // harvardRef
 +
te.$ref = teHref &&
 +
$( '#' + $.escapeSelector( teHref.slice( 1 ) ) );
 +
if ( !te.$ref || !te.$ref.length || !te.$ref.text() ) {
 +
te.noRef = true;
 +
return;
 +
}
 +
}
 +
 
 +
if ( !tooltipInitiallyPresent && !te.comment ) {
 +
viewportTop = $window.scrollTop();
 +
refOffsetTop = te.$ref.offset().top;
 +
if ( !activatedByClick &&
 +
viewportTop < refOffsetTop &&
 +
viewportTop + $window.height() > refOffsetTop + te.$ref.height() &&
 +
// There can be gadgets/scripts that make references horizontally scrollable.
 +
$window.width() > te.$ref.offset().left + te.$ref.width()
 +
) {
 +
// Highlight the reference itself
 +
te.$ref.addClass( 'rt-target' );
 +
return;
 +
}
 +
}
 +
 
 +
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( '.rt-tooltip' ).data( 'tooltip' );
 +
if ( te.tooltip.parent && te.tooltip.parent.disappearing ) {
 +
return;
 +
}
 +
 
 +
te.tooltip.show();
 +
 
 +
if ( tooltipInitiallyPresent ) {
 +
if ( te.tooltip.$element.hasClass( 'rt-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.rt', 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;
 +
 
 +
if ( this.type === 'commentedText' ) {
 +
this.$element.attr( 'title', '' );
 +
}
 +
 
 +
if ( activatedByClick ) {
 +
if ( tooltipInitiallyPresent ||
 +
( this.$ref && this.$ref.hasClass( 'rt-target' ) )
 +
) {
 +
return;
 +
} else {
 +
setTimeout( function () {
 +
$body.on( 'click.rt touchstart.rt', te.onBodyClick );
 +
}, 0 );
 +
}
 +
}
 +
 
 +
if ( activatedByClick || tooltipInitiallyPresent ) {
 +
reallyShow();
 +
} else {
 +
this.showTimer = setTimeout( reallyShow, delay );
 +
}
 +
};
 +
 
 +
this.onBodyClick = function ( e ) {
 +
if ( !te.tooltip && !te.$ref.hasClass( 'rt-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( 'rt-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 ) {
 +
function openSettingsDialog() {
 +
var settingsDialog, settingsWindow;
 +
 
 +
if ( cursorWaitCss ) {
 +
cursorWaitCss.disabled = true;
 +
}
 +
 
 +
function SettingsDialog() {
 +
SettingsDialog.parent.call( this );
 +
}
 +
OO.inheritClass( SettingsDialog, OO.ui.ProcessDialog );
 +
 
 +
SettingsDialog.static.name = 'settingsDialog';
 +
SettingsDialog.static.title = mw.msg( 'rt-settings-title' );
 +
SettingsDialog.static.actions = [
 +
{
 +
modes: 'basic',
 +
action: 'save',
 +
label: mw.msg( 'rt-save' ),
 +
flags: [ 'primary', 'progressive' ]
 +
},
 +
{
 +
modes: 'basic',
 +
label: mw.msg( 'rt-cancel' ),
 +
flags: 'safe'
 +
},
 +
{
 +
modes: 'disabled',
 +
action: 'deactivated',
 +
label: mw.msg( 'rt-done' ),
 +
flags: [ 'primary', 'progressive' ]
 +
}
 +
];
 +
 
 +
SettingsDialog.prototype.initialize = function () {
 +
var dialog = this;
 +
 
 +
SettingsDialog.parent.prototype.initialize.apply( this, arguments );
 +
 
 +
this.enableOption = new OO.ui.RadioOptionWidget( {
 +
label: mw.msg( 'rt-enable' )
 +
} );
 +
this.disableOption = new OO.ui.RadioOptionWidget( {
 +
label: mw.msg( 'rt-disable' )
 +
} );
 +
this.enableSelect = new OO.ui.RadioSelectWidget( {
 +
items: [ this.enableOption, this.disableOption ],
 +
classes: [ 'rt-enableSelect' ]
 +
} );
 +
this.enableSelect.selectItem( this.enableOption );
 +
this.enableSelect.on( 'choose', function ( item ) {
 +
if ( item === dialog.disableOption ) {
 +
dialog.activationMethodSelect.setDisabled( true );
 +
dialog.delayInput.setDisabled( true );
 +
dialog.tooltipsForCommentsCheckbox.setDisabled( true );
 +
} else {
 +
dialog.activationMethodSelect.setDisabled( false );
 +
dialog.delayInput.setDisabled( dialog.clickOption.isSelected() );
 +
dialog.tooltipsForCommentsCheckbox.setDisabled( false );
 +
}
 +
} );
 +
 
 +
this.hoverOption = new OO.ui.RadioOptionWidget( {
 +
label: mw.msg( 'rt-hovering' )
 +
} );
 +
this.clickOption = new OO.ui.RadioOptionWidget( {
 +
label: mw.msg( 'rt-clicking' )
 +
} );
 +
this.activationMethodSelect = new OO.ui.RadioSelectWidget( {
 +
items: [ this.hoverOption, this.clickOption ]
 +
} );
 +
this.activationMethodSelect.selectItem( activatedByClick ?
 +
this.clickOption :
 +
this.hoverOption
 +
);
 +
this.activationMethodSelect.on( 'choose', function ( item ) {
 +
if ( item === dialog.clickOption ) {
 +
dialog.delayInput.setDisabled( true );
 +
} else {
 +
dialog.delayInput.setDisabled( dialog.clickOption.isSelected() );
 +
}
 +
} );
 +
this.activationMethodField = new OO.ui.FieldLayout( this.activationMethodSelect, {
 +
label: mw.msg( 'rt-activationMethod' ),
 +
align: 'top'
 +
} );
 +
 
 +
this.delayInput = new OO.ui.NumberInputWidget( {
 +
input: { value: delay },
 +
step: 50,
 +
min: 0,
 +
max: 5000,
 +
disabled: activatedByClick,
 +
classes: [ 'rt-numberInput' ]
 +
} );
 +
this.delayField = new OO.ui.FieldLayout( this.delayInput, {
 +
label: mw.msg( 'rt-delay' ),
 +
align: 'top'
 +
} );
 +
 
 +
this.tooltipsForCommentsCheckbox = new OO.ui.CheckboxInputWidget( {
 +
selected: tooltipsForComments
 +
} );
 +
this.tooltipsForCommentsField = new OO.ui.FieldLayout(
 +
this.tooltipsForCommentsCheckbox,
 +
{
 +
label: new OO.ui.HtmlSnippet( mw.msg( 'rt-tooltipsForComments' ) ),
 +
align: 'inline',
 +
classes: [ 'rt-tooltipsForCommentsField' ]
 +
}
 +
);
 +
new TooltippedElement(
 +
this.tooltipsForCommentsField.$element.find(
 +
'.' + ( COMMENTED_TEXT_CLASS || 'rt-commentedText' )
 +
)
 +
);
 +
 
 +
this.fieldset = new OO.ui.FieldsetLayout();
 +
this.fieldset.addItems( [
 +
this.activationMethodField,
 +
this.delayField,
 +
this.tooltipsForCommentsField
 +
] );
 +
 
 +
this.panelSettings = new OO.ui.PanelLayout( {
 +
padded: true,
 +
expanded: false
 +
} );
 +
this.panelSettings.$element.append(
 +
this.enableSelect.$element,
 +
$( '<hr>' ).addClass( 'rt-settingsFormSeparator' ),
 +
this.fieldset.$element
 +
);
 +
 
 +
this.panelDisabled = new OO.ui.PanelLayout( {
 +
padded: true,
 +
expanded: false
 +
} );
 +
this.panelDisabled.$element.append(
 +
$( '<table>' )
 +
.addClass( 'rt-disabledHelp' )
 +
.append(
 +
$( '<tr>' ).append(
 +
$( '<td>' ).append(
 +
$( '<img>' ).attr( 'src', 'https://en.wikipedia.org/w/load.php?modules=ext.popups.images&image=footer&format=rasterized&lang=ru&skin=vector&version=0uotisb' )
 +
),
 +
$( '<td>' )
 +
.addClass( 'rt-disabledNote' )
 +
.text( mw.msg( 'rt-disabledNote' ) )
 +
)
 +
)
 +
);
 +
 
 +
this.stackLayout = new OO.ui.StackLayout( {
 +
items: [ this.panelSettings, this.panelDisabled ]
 +
} );
 +
 
 +
this.$body.append( this.stackLayout.$element );
 +
};
 +
 
 +
SettingsDialog.prototype.getSetupProcess = function ( data ) {
 +
return SettingsDialog.parent.prototype.getSetupProcess.call( this, data )
 +
.next( function () {
 +
this.stackLayout.setItem( this.panelSettings );
 +
this.actions.setMode( 'basic' );
 +
}, this );
 +
};
 +
 
 +
SettingsDialog.prototype.getActionProcess = function ( action ) {
 +
var dialog = this;
 +
 
 +
if ( action === 'save' ) {
 +
return new OO.ui.Process( function () {
 +
var newDelay = Number( dialog.delayInput.getValue() );
 +
 
 +
enabled = dialog.enableOption.isSelected();
 +
if ( newDelay >= 0 && newDelay <= 5000 ) {
 +
delay = newDelay;
 +
}
 +
activatedByClick = dialog.clickOption.isSelected();
 +
tooltipsForComments = dialog.tooltipsForCommentsCheckbox.isSelected();
 +
 
 +
setSettingsCookie();
 +
 
 +
if ( enabled ) {
 +
dialog.close();
 +
disableRt();
 +
rt( $content );
 +
} else {
 +
dialog.actions.setMode( 'disabled' );
 +
dialog.stackLayout.setItem( dialog.panelDisabled );
 +
disableRt();
 +
addEnableLink();
 +
}
 +
} );
 +
} else if ( action === 'deactivated' ) {
 +
dialog.close();
 +
}
 +
return SettingsDialog.parent.prototype.getActionProcess.call( this, action );
 +
};
 +
 
 +
SettingsDialog.prototype.getBodyHeight = function () {
 +
return this.stackLayout.getCurrentItem().$element.outerHeight( true );
 +
};
 +
 
 +
tooltip.upToTopParent( function adjustRightAndHide() {
 +
if ( this.isPresent ) {
 +
if ( this.$element[ 0 ].style.right ) {
 +
this.$element.css(
 +
'right',
 +
'+=' + ( window.innerWidth - $window.width() )
 +
);
 +
}
 +
this.te.hideRef( true );
 +
}
 +
} );
 +
 
 +
if ( !windowManager ) {
 +
windowManager = new OO.ui.WindowManager();
 +
$body.append( windowManager.$element );
 +
}
 +
 
 +
settingsDialog = new SettingsDialog();
 +
windowManager.addWindows( [ settingsDialog ] );
 +
settingsWindow = windowManager.openWindow( settingsDialog );
 +
settingsWindow.opened.then( function () {
 +
settingsDialogOpening = false;
 +
} );
 +
settingsWindow.closed.then( function () {
 +
windowManager.clearWindows();
 +
} );
 +
}
 +
 
 +
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;
 +
 
 +
switch ( this.te.type ) {
 +
case 'supRef':
 +
this.id = 'rt-' + this.te.$originalElement.attr( 'id' );
 +
this.$content = this.te.$ref
 +
.contents()
 +
.filter( function ( i ) {
 +
var $this = $( this );
 +
return this.nodeType === Node.TEXT_NODE ||
 +
!( $this.is( '.mw-cite-backlink' ) ||
 +
( i === 0 &&
 +
// Template:Cnote, Template:Note
 +
( $this.is( 'b' ) ||
 +
// Template:Note_label
 +
$this.is( 'a' ) &&
 +
$this.attr( 'href' ).indexOf( '#ref' ) === 0
 +
)
 +
)
 +
);
 +
} )
 +
.clone( true );
 +
break;
 +
case 'harvardRef':
 +
this.id = 'rt-' + this.te.$originalElement.closest( 'li' ).attr( 'id' );
 +
this.$content = this.te.$ref
 +
.clone( true )
 +
.removeAttr( 'id' );
 +
break;
 +
case 'commentedText':
 +
this.id = 'rt-' + String( Math.random() ).slice( 2 );
 +
this.$content = $( document.createTextNode( this.te.comment ) );
 +
break;
 +
}
 +
if ( !this.$content.length ) {
 +
return;
 +
}
 +
 
 +
this.insideWindow = Boolean( this.te.$element.closest( '.oo-ui-window' ).length );
 +
 
 +
this.$element = $( '<div>' )
 +
.addClass( 'rt-tooltip' )
 +
.attr( 'id', this.id )
 +
.attr( 'role', 'tooltip' )
 +
.data( 'tooltip', this );
 +
if ( this.insideWindow ) {
 +
this.$element.addClass( 'rt-tooltip-insideWindow' );
 +
}
 +
 
 +
// We need the $content interlayer here in order for the settings icon to have correct
 +
// margins
 +
this.$content = this.$content
 +
.wrapAll( '<div>' )
 +
.parent()
 +
.addClass( 'rt-tooltipContent' )
 +
.addClass( 'mw-parser-output' )
 +
.appendTo( this.$element );
 +
 
 +
if ( !activatedByClick ) {
 +
this.$element
 +
.mouseenter( function () {
 +
if ( !tooltip.disappearing ) {
 +
tooltip.upToTopParent( function () {
 +
this.show();
 +
} );
 +
}
 +
} )
 +
.mouseleave( function ( e ) {
 +
// https://stackoverflow.com/q/47649442 workaround. Relying on relatedTarget
 +
// alone has pitfalls: when alt-tabbing, relatedTarget is empty too
 +
if ( CLIENT_NAME !== 'chrome' ||
 +
( !e.originalEvent ||
 +
e.originalEvent.relatedTarget !== null ||
 +
!tooltip.clickedTime ||
 +
$.now() - tooltip.clickedTime > 50
 +
)
 +
) {
 +
tooltip.upToTopParent( function () {
 +
this.te.hideRef();
 +
} );
 +
}
 +
} )
 +
.click( function () {
 +
tooltip.clickedTime = $.now();
 +
} );
 +
}
 +
 
 +
if ( !this.insideWindow ) {
 +
$( '<div>' )
 +
.addClass( 'rt-settingsLink' )
 +
.attr( 'title', mw.msg( 'rt-settings' ) )
 +
.click( function () {
 +
if ( settingsDialogOpening ) {
 +
return;
 +
}
 +
settingsDialogOpening = true;
 +
 
 +
if ( mw.loader.getState( 'oojs-ui' ) !== 'ready' ) {
 +
if ( cursorWaitCss ) {
 +
cursorWaitCss.disabled = false;
 +
} else {
 +
cursorWaitCss = mw.util.addCSS( 'body { cursor: wait; }' );
 +
}
 +
}
 +
mw.loader.using( [ 'oojs', 'oojs-ui' ], openSettingsDialog );
 +
} )
 +
.prependTo( this.$content );
 +
}
 +
 
 +
// 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( 'rt-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( 'rt-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', '' );
 +
 
 +
if ( activatedByClick ) {
 +
$body.off( 'click.rt touchstart.rt', tooltip.te.onBodyClick );
 +
}
 +
$window.off( 'resize.rt', 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() + 6 ) {
 +
this.$element
 +
.removeClass( 'rt-tooltip-above' )
 +
.addClass( 'rt-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( 'rt-tooltip-below' )
 +
.addClass( 'rt-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;
 +
}
 +
};
 +
}
 +
 
 +
if ( !enabled ) {
 +
addEnableLink();
 +
return;
 +
}
 +
 
 +
teSelector = REF_LINK_SELECTOR;
 +
if ( tooltipsForComments ) {
 +
teSelector += ', ' + COMMENTED_TEXT_SELECTOR;
 +
}
 +
$content.find( teSelector ).each( function () {
 +
new TooltippedElement( $( this ) );
 +
} );
 +
}
 +
 
 +
settingsString = mw.cookie.get( 'RTsettings', '' );
 +
if ( settingsString ) {
 +
settings = settingsString.split( '|' );
 +
enabled = Boolean( Number( settings[ 0 ] ) );
 +
delay = Number( settings[ 1 ] );
 +
activatedByClick = Boolean( Number( settings[ 2 ] ) );
 +
// The forth value was added later, so we provide for a default value. See comments below
 +
// for why we use "IS_TOUCHSCREEN && IS_MOBILE".
 +
tooltipsForComments = settings[ 3 ] === undefined ?
 +
IS_TOUCHSCREEN && IS_MOBILE :
 +
Boolean( Number( settings[ 3 ] ) );
 +
} else {
 +
enabled = true;
 +
delay = 200;
 +
// Since the mobile browser check is error-prone, adding IS_MOBILE condition here would probably
 +
// leave cases where a user interacting with the browser using touches doesn't know how to call
 +
// a tooltip in order to switch to activation by click. Some touch-supporting laptop users
 +
// interacting by touch (though probably not the most popular use case) would not be happy too.
 +
activatedByClick = IS_TOUCHSCREEN;
 +
// Arguably we shouldn't convert native tooltips into gadget tooltips for devices that have
 +
// mouse support, even if they have touchscreens (there are laptops with touchscreens).
 +
// IS_TOUCHSCREEN check here is for reliability, since the mobile check is prone to false
 +
// positives.
 +
tooltipsForComments = IS_TOUCHSCREEN && IS_MOBILE;
 +
}
 +
 
 +
mw.hook( 'wikipage.content' ).add( rt );
 +
 
 +
}() );

2023年10月10日 (二) 11:37的最新版本

// See [[mw:Reference Tooltips]]
// Source https://en.wikipedia.org/wiki/MediaWiki:Gadget-ReferenceTooltips.js

( function () {

// enwiki settings
var REF_LINK_SELECTOR = '.reference, a[href^="#CITEREF"]',
	COMMENTED_TEXT_CLASS = 'rt-commentedText',
	COMMENTED_TEXT_SELECTOR = ( COMMENTED_TEXT_CLASS ? '.' + COMMENTED_TEXT_CLASS + ', ' : '') +
		'abbr[title]';

mw.messages.set(wgULS({
	'rt-settings': '参考文献提示工具设置',
	'rt-enable-footer': '启用参考文献提示工具',
	'rt-settings-title': '参考文献提示工具',
	'rt-save': '保存',
	'rt-cancel': '取消',
	'rt-enable': '启用',
	'rt-disable': '禁用',
	'rt-activationMethod': '提示工具显示方式',
	'rt-hovering': '鼠标悬浮',
	'rt-clicking': '点击',
	'rt-delay': '工具提示显示延迟(毫秒)',
	'rt-tooltipsForComments': '在参考文献提示工具样式中用<span title="提示工具的例子" class="' + (COMMENTED_TEXT_CLASS || 'rt-commentedText') + '" style="border-bottom: 1px dotted; cursor: help;">虚下划线</span>的方式在文字上显示提示工具(可以在不支持鼠标的设备上显示提示工具)',
	'rt-disabledNote': '你可以通过页脚中的链接重新启用参考文献提示工具',
	'rt-done': '完成',
	'rt-enabled': '参考文献提示工具已启用'
}, {
	'rt-settings': '參考文獻提示工具設定',
	'rt-enable-footer': '啟用參考文獻提示工具',
	'rt-settings-title': '參考文獻提示工具',
	'rt-save': '儲存',
	'rt-cancel': '取消',
	'rt-enable': '啟用',
	'rt-disable': '停用',
	'rt-activationMethod': '提示工具顯示方式',
	'rt-hovering': '滑鼠懸浮',
	'rt-clicking': '點擊',
	'rt-delay': '工具提示顯示延遲(毫秒)',
	'rt-tooltipsForComments': '在參考文獻提示工具樣式中用<span title="提示工具的例子" class="' + (COMMENTED_TEXT_CLASS || 'rt-commentedText') + '" style="border-bottom: 1px dotted; cursor: help;">虛底線</span>的方式在文字上顯示提示工具(可以在不支持滑鼠的裝置上顯示提示工具)',
	'rt-disabledNote': '你可以通過頁尾中的連結重新啟用參考文獻提示工具',
	'rt-done': '完成',
	'rt-enabled': '參考文獻提示工具已啟用'
}));

// "Global" variables
var SECONDS_IN_A_DAY = 60 * 60 * 24,
	CLASSES = {
		FADE_IN_DOWN: 'rt-fade-in-down',
		FADE_IN_UP: 'rt-fade-in-up',
		FADE_OUT_DOWN: 'rt-fade-out-down',
		FADE_OUT_UP: 'rt-fade-out-up'
	},
	IS_TOUCHSCREEN = 'ontouchstart' in document.documentElement,
	// Quite a rough check for mobile browsers, a mix of what is advised at
	// https://stackoverflow.com/a/24600597 (sends to
	// https://developer.mozilla.org/en-US/docs/Browser_detection_using_the_user_agent)
	// and https://stackoverflow.com/a/14301832
	IS_MOBILE = /Mobi|Android/i.test( navigator.userAgent ) ||
		typeof window.orientation !== 'undefined',
	CLIENT_NAME = $.client.profile().name,
	settingsString, settings, enabled, delay, activatedByClick, tooltipsForComments, cursorWaitCss,
	windowManager,
	$body = $( document.body ),
	$window = $( window );

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

	var teSelector,
		settingsDialogOpening = false;

	function setSettingsCookie() {
		mw.cookie.set(
			'RTsettings',
			Number( enabled ) + '|' + delay + '|' + Number( activatedByClick ) + '|' +
				Number( tooltipsForComments ),
			{ path: '/', expires: 90 * SECONDS_IN_A_DAY, prefix: '' }
		);
	}

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

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

	function addEnableLink() {
		// #footer-places – Vector
		// #f-list – Timeless, Monobook, Modern
		// parent of #footer li – Cologne Blue
		var $footer = $( '#footer-places, #f-list' );
		if ( !$footer.length ) {
			$footer = $( '#footer li' ).parent();
		}
		$footer.append(
			$( '<li>' )
				.addClass( 'rt-enableItem' )
				.append(
					$( '<a>' )
						.text( mw.msg( 'rt-enable-footer' ) )
						.attr( 'href', 'javascript:' )
						.click( function ( e ) {
							e.preventDefault();
							enableRt();
						} )
			)
		);
	}

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

		function onStartEvent( e ) {
			var showRefArgs;

			if ( activatedByClick && te.type !== 'commentedText' && e.type !== 'contextmenu' ) {
				e.preventDefault();
			}
			if ( !te.noRef ) {
				showRefArgs = [ $( this ) ];
				if ( te.type !== 'supRef' ) {
					showRefArgs.push( e.pageX, e.pageY );
				}
				te.showRef.apply( te, showRefArgs );
			}
		}

		function onEndEvent() {
			if ( !te.noRef ) {
				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;
		if ( this.$element.is( REF_LINK_SELECTOR ) ) {
			if ( this.$element.prop( 'tagName' ) === 'SUP' ) {
				this.type = 'supRef';
			} else {
				this.type = 'harvardRef';
			}
		} else {
			this.type = 'commentedText';
			this.comment = this.$element.attr( 'title' );
			if ( !this.comment ) {
				return;
			}
			this.$element.addClass('rt-commentedText');
		}
		
		if ( activatedByClick ) {
			events = {
				'click.rt': onStartEvent
			};
			// Adds an ability to see tooltips for links
			if ( this.type === 'commentedText' &&
				( this.$element.closest( 'a' ).length ||
					this.$element.has( 'a' ).length
				)
			) {
				events[ 'contextmenu.rt' ] = onStartEvent;
			}
		} else {
			events = {
				'mouseenter.rt': onStartEvent,
				'mouseleave.rt': onEndEvent
			};
		}

		this.$element.on( events );

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

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

			if ( this.tooltip && this.tooltip.isPresent ) {
				if ( activatedByClick || immediately ) {
					this.tooltip.hide();
				} else {
					this.hideTimer = setTimeout( function () {
						te.tooltip.hide();
					}, 200 );
				}
			} else if ( this.$ref && this.$ref.hasClass( 'rt-target' ) ) {
				this.$ref.removeClass( 'rt-target' );
				if ( activatedByClick ) {
					$body.off( 'click.rt touchstart.rt', this.onBodyClick );
				}
			}
		};

		this.showRef = function ( $element, ePageX, ePageY ) {
			// Popups gadget
			if ( window.pg ) {
				disableRt();
				return;
			}
			
			if ( this.tooltip && !this.tooltip.$content.length ) {
				return;
			}

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

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

				if ( !te.$ref && !te.comment ) {
					teHref = te.type === 'supRef' ?
						te.$element.find( 'a' ).attr( 'href' ) :
						te.$element.attr( 'href' ); // harvardRef
					te.$ref = teHref &&
						$( '#' + $.escapeSelector( teHref.slice( 1 ) ) );
					if ( !te.$ref || !te.$ref.length || !te.$ref.text() ) {
						te.noRef = true;
						return;
					}
				}

				if ( !tooltipInitiallyPresent && !te.comment ) {
					viewportTop = $window.scrollTop();
					refOffsetTop = te.$ref.offset().top;
					if ( !activatedByClick &&
						viewportTop < refOffsetTop &&
						viewportTop + $window.height() > refOffsetTop + te.$ref.height() &&
						// There can be gadgets/scripts that make references horizontally scrollable.
						$window.width() > te.$ref.offset().left + te.$ref.width()
					) {
						// Highlight the reference itself
						te.$ref.addClass( 'rt-target' );
						return;
					}
				}

				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( '.rt-tooltip' ).data( 'tooltip' );
				if ( te.tooltip.parent && te.tooltip.parent.disappearing ) {
					return;
				}

				te.tooltip.show();

				if ( tooltipInitiallyPresent ) {
					if ( te.tooltip.$element.hasClass( 'rt-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.rt', 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;

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

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

			if ( activatedByClick || tooltipInitiallyPresent ) {
				reallyShow();
			} else {
				this.showTimer = setTimeout( reallyShow, delay );
			}
		};

		this.onBodyClick = function ( e ) {
			if ( !te.tooltip && !te.$ref.hasClass( 'rt-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( 'rt-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 ) {
		function openSettingsDialog() {
			var settingsDialog, settingsWindow;

			if ( cursorWaitCss ) {
				cursorWaitCss.disabled = true;
			}

			function SettingsDialog() {
				SettingsDialog.parent.call( this );
			}
			OO.inheritClass( SettingsDialog, OO.ui.ProcessDialog );

			SettingsDialog.static.name = 'settingsDialog';
			SettingsDialog.static.title = mw.msg( 'rt-settings-title' );
			SettingsDialog.static.actions = [
				{
					modes: 'basic',
					action: 'save',
					label: mw.msg( 'rt-save' ),
					flags: [ 'primary', 'progressive' ]
				},
				{
					modes: 'basic',
					label: mw.msg( 'rt-cancel' ),
					flags: 'safe'
				},
				{
					modes: 'disabled',
					action: 'deactivated',
					label: mw.msg( 'rt-done' ),
					flags: [ 'primary', 'progressive' ]
				}
			];

			SettingsDialog.prototype.initialize = function () {
				var dialog = this;

				SettingsDialog.parent.prototype.initialize.apply( this, arguments );

				this.enableOption = new OO.ui.RadioOptionWidget( {
					label: mw.msg( 'rt-enable' )
				} );
				this.disableOption = new OO.ui.RadioOptionWidget( {
					label: mw.msg( 'rt-disable' )
				} );
				this.enableSelect = new OO.ui.RadioSelectWidget( {
					items: [ this.enableOption, this.disableOption ],
					classes: [ 'rt-enableSelect' ]
				} );
				this.enableSelect.selectItem( this.enableOption );
				this.enableSelect.on( 'choose', function ( item ) {
					if ( item === dialog.disableOption ) {
						dialog.activationMethodSelect.setDisabled( true );
						dialog.delayInput.setDisabled( true );
						dialog.tooltipsForCommentsCheckbox.setDisabled( true );
					} else {
						dialog.activationMethodSelect.setDisabled( false );
						dialog.delayInput.setDisabled( dialog.clickOption.isSelected() );
						dialog.tooltipsForCommentsCheckbox.setDisabled( false );
					}
				} );

				this.hoverOption = new OO.ui.RadioOptionWidget( {
					label: mw.msg( 'rt-hovering' )
				} );
				this.clickOption = new OO.ui.RadioOptionWidget( {
					label: mw.msg( 'rt-clicking' )
				} );
				this.activationMethodSelect = new OO.ui.RadioSelectWidget( {
					items: [ this.hoverOption, this.clickOption ]
				} );
				this.activationMethodSelect.selectItem( activatedByClick ?
					this.clickOption :
					this.hoverOption
				);
				this.activationMethodSelect.on( 'choose', function ( item ) {
					if ( item === dialog.clickOption ) {
						dialog.delayInput.setDisabled( true );
					} else {
						dialog.delayInput.setDisabled( dialog.clickOption.isSelected() );
					}
				} );
				this.activationMethodField = new OO.ui.FieldLayout( this.activationMethodSelect, {
					label: mw.msg( 'rt-activationMethod' ),
					align: 'top'
				} );

				this.delayInput = new OO.ui.NumberInputWidget( {
					input: { value: delay },
					step: 50,
					min: 0,
					max: 5000,
					disabled: activatedByClick,
					classes: [ 'rt-numberInput' ]
				} );
				this.delayField = new OO.ui.FieldLayout( this.delayInput, {
					label: mw.msg( 'rt-delay' ),
					align: 'top'
				} );

				this.tooltipsForCommentsCheckbox = new OO.ui.CheckboxInputWidget( {
					selected: tooltipsForComments
				} );
				this.tooltipsForCommentsField = new OO.ui.FieldLayout(
					this.tooltipsForCommentsCheckbox,
					{
						label: new OO.ui.HtmlSnippet( mw.msg( 'rt-tooltipsForComments' ) ),
						align: 'inline',
						classes: [ 'rt-tooltipsForCommentsField' ]
					}
				);
				new TooltippedElement(
					this.tooltipsForCommentsField.$element.find(
						'.' + ( COMMENTED_TEXT_CLASS || 'rt-commentedText' )
					)
				);

				this.fieldset = new OO.ui.FieldsetLayout();
				this.fieldset.addItems( [
					this.activationMethodField,
					this.delayField,
					this.tooltipsForCommentsField
				] );

				this.panelSettings = new OO.ui.PanelLayout( {
					padded: true,
					expanded: false
				} );
				this.panelSettings.$element.append(
					this.enableSelect.$element,
					$( '<hr>' ).addClass( 'rt-settingsFormSeparator' ),
					this.fieldset.$element
				);

				this.panelDisabled = new OO.ui.PanelLayout( {
					padded: true,
					expanded: false
				} );
				this.panelDisabled.$element.append(
					$( '<table>' )
						.addClass( 'rt-disabledHelp' )
						.append(
							$( '<tr>' ).append(
								$( '<td>' ).append(
									$( '<img>' ).attr( 'src', 'https://en.wikipedia.org/w/load.php?modules=ext.popups.images&image=footer&format=rasterized&lang=ru&skin=vector&version=0uotisb' )
								),
								$( '<td>' )
									.addClass( 'rt-disabledNote' )
									.text( mw.msg( 'rt-disabledNote' ) )
							)
						)
				);

				this.stackLayout = new OO.ui.StackLayout( {
					items: [ this.panelSettings, this.panelDisabled ]
				} );

				this.$body.append( this.stackLayout.$element );
			};

			SettingsDialog.prototype.getSetupProcess = function ( data ) {
				return SettingsDialog.parent.prototype.getSetupProcess.call( this, data )
					.next( function () {
						this.stackLayout.setItem( this.panelSettings );
						this.actions.setMode( 'basic' );
					}, this );
			};

			SettingsDialog.prototype.getActionProcess = function ( action ) {
				var dialog = this;

				if ( action === 'save' ) {
					return new OO.ui.Process( function () {
						var newDelay = Number( dialog.delayInput.getValue() );

						enabled = dialog.enableOption.isSelected();
						if ( newDelay >= 0 && newDelay <= 5000 ) {
							delay = newDelay;
						}
						activatedByClick = dialog.clickOption.isSelected();
						tooltipsForComments = dialog.tooltipsForCommentsCheckbox.isSelected();

						setSettingsCookie();

						if ( enabled ) {
							dialog.close();
							disableRt();
							rt( $content );
						} else {
							dialog.actions.setMode( 'disabled' );
							dialog.stackLayout.setItem( dialog.panelDisabled );
							disableRt();
							addEnableLink();
						}
					} );
				} else if ( action === 'deactivated' ) {
					dialog.close();
				}
				return SettingsDialog.parent.prototype.getActionProcess.call( this, action );
			};

			SettingsDialog.prototype.getBodyHeight = function () {
				return this.stackLayout.getCurrentItem().$element.outerHeight( true );
			};

			tooltip.upToTopParent( function adjustRightAndHide() {
				if ( this.isPresent ) {
					if ( this.$element[ 0 ].style.right ) {
						this.$element.css(
							'right',
							'+=' + ( window.innerWidth - $window.width() )
						);
					}
					this.te.hideRef( true );
				}
			} );

			if ( !windowManager ) {
				windowManager = new OO.ui.WindowManager();
				$body.append( windowManager.$element );
			}

			settingsDialog = new SettingsDialog();
			windowManager.addWindows( [ settingsDialog ] );
			settingsWindow = windowManager.openWindow( settingsDialog );
			settingsWindow.opened.then( function () {
				settingsDialogOpening = false;
			} );
			settingsWindow.closed.then( function () {
				windowManager.clearWindows();
			} );
		}

		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;

		switch ( this.te.type ) {
			case 'supRef':
				this.id = 'rt-' + this.te.$originalElement.attr( 'id' );
				this.$content = this.te.$ref
					.contents()
					.filter( function ( i ) {
						var $this = $( this );
						return this.nodeType === Node.TEXT_NODE ||
							!( $this.is( '.mw-cite-backlink' ) ||
								( i === 0 &&
									// Template:Cnote, Template:Note
									( $this.is( 'b' ) ||
										// Template:Note_label
										$this.is( 'a' ) &&
										$this.attr( 'href' ).indexOf( '#ref' ) === 0
									)
								)
							);
					} )
					.clone( true );
				break;
			case 'harvardRef':
				this.id = 'rt-' + this.te.$originalElement.closest( 'li' ).attr( 'id' );
				this.$content = this.te.$ref
					.clone( true )
					.removeAttr( 'id' );
				break;
			case 'commentedText':
				this.id = 'rt-' + String( Math.random() ).slice( 2 );
				this.$content = $( document.createTextNode( this.te.comment ) );
				break;
		}
		if ( !this.$content.length ) {
			return;
		}

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

		this.$element = $( '<div>' )
			.addClass( 'rt-tooltip' )
			.attr( 'id', this.id )
			.attr( 'role', 'tooltip' )
			.data( 'tooltip', this );
		if ( this.insideWindow ) {
			this.$element.addClass( 'rt-tooltip-insideWindow' );
		}

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

		if ( !activatedByClick ) {
			this.$element
				.mouseenter( function () {
					if ( !tooltip.disappearing ) {
						tooltip.upToTopParent( function () {
							this.show();
						} );
					}
				} )
				.mouseleave( function ( e ) {
					// https://stackoverflow.com/q/47649442 workaround. Relying on relatedTarget
					// alone has pitfalls: when alt-tabbing, relatedTarget is empty too
					if ( CLIENT_NAME !== 'chrome' ||
						( !e.originalEvent ||
							e.originalEvent.relatedTarget !== null ||
							!tooltip.clickedTime ||
							$.now() - tooltip.clickedTime > 50
						)
					) {
						tooltip.upToTopParent( function () {
							this.te.hideRef();
						} );
					}
				} )
				.click( function () {
					tooltip.clickedTime = $.now();
				} );
		}

		if ( !this.insideWindow ) {
			$( '<div>' )
				.addClass( 'rt-settingsLink' )
				.attr( 'title', mw.msg( 'rt-settings' ) )
				.click( function () {
					if ( settingsDialogOpening ) {
						return;
					}
					settingsDialogOpening = true;

					if ( mw.loader.getState( 'oojs-ui' ) !== 'ready' ) {
						if ( cursorWaitCss ) {
							cursorWaitCss.disabled = false;
						} else {
							cursorWaitCss = mw.util.addCSS( 'body { cursor: wait; }' );
						}
					}
					mw.loader.using( [ 'oojs', 'oojs-ui' ], openSettingsDialog );
				} )
				.prependTo( this.$content );
		}

		// 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( 'rt-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( 'rt-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', '' );

					if ( activatedByClick ) {
						$body.off( 'click.rt touchstart.rt', tooltip.te.onBodyClick );
					}
					$window.off( 'resize.rt', 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() + 6 ) {
				this.$element
					.removeClass( 'rt-tooltip-above' )
					.addClass( 'rt-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( 'rt-tooltip-below' )
					.addClass( 'rt-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;
			}
		};
	}

	if ( !enabled ) {
		addEnableLink();
		return;
	}

	teSelector = REF_LINK_SELECTOR;
	if ( tooltipsForComments ) {
		teSelector += ', ' + COMMENTED_TEXT_SELECTOR;
	}
	$content.find( teSelector ).each( function () {
		new TooltippedElement( $( this ) );
	} );
}

settingsString = mw.cookie.get( 'RTsettings', '' );
if ( settingsString ) {
	settings = settingsString.split( '|' );
	enabled = Boolean( Number( settings[ 0 ] ) );
	delay = Number( settings[ 1 ] );
	activatedByClick = Boolean( Number( settings[ 2 ] ) );
	// The forth value was added later, so we provide for a default value. See comments below
	// for why we use "IS_TOUCHSCREEN && IS_MOBILE".
	tooltipsForComments = settings[ 3 ] === undefined ?
		IS_TOUCHSCREEN && IS_MOBILE :
		Boolean( Number( settings[ 3 ] ) );
} else {
	enabled = true;
	delay = 200;
	// Since the mobile browser check is error-prone, adding IS_MOBILE condition here would probably
	// leave cases where a user interacting with the browser using touches doesn't know how to call
	// a tooltip in order to switch to activation by click. Some touch-supporting laptop users
	// interacting by touch (though probably not the most popular use case) would not be happy too.
	activatedByClick = IS_TOUCHSCREEN;
	// Arguably we shouldn't convert native tooltips into gadget tooltips for devices that have
	// mouse support, even if they have touchscreens (there are laptops with touchscreens).
	// IS_TOUCHSCREEN check here is for reliability, since the mobile check is prone to false
	// positives.
	tooltipsForComments = IS_TOUCHSCREEN && IS_MOBILE;
}

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

}() );