2023年政策修订增补工作正在进行中,欢迎参与!
  • Moegirl.ICU:萌娘百科流亡社群 581077156(QQ),欢迎对萌娘百科运营感到失望的编辑者加入
  • Moegirl.ICU:账号认领正在试运行,有意者请参照账号认领流程

User:Leranjun/js/UtatenSearch.js

萌娘百科,万物皆可萌的百科全书!转载请标注来源页面的网页链接,并声明引自萌娘百科。内容不可商用。
跳转到导航 跳转到搜索

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

  • Firefox/Safari:按住“Shift”的同时单击“刷新”,或按“Ctrl-F5”或“Ctrl-R”(Mac为“⌘-R”)
  • Google Chrome:按“Ctrl-Shift-R”(Mac为“⌘-Shift-R”)
  • Internet Explorer:按住“Ctrl”的同时单击“刷新”,或按“Ctrl-F5”
  • Opera:在“工具→首选项”中清除缓存
如果您已登录但该页面出现未登录状态,请尝试在地址栏的地址最后添加代码?_=1来访问最新页面。
添加代码后的本页地址如下:-{R|https://moegirl.icu/User:Leranjun/js/UtatenSearch.js?_=1}-
"use strict";
$(() => (async () => {
    await mw.loader.using("ext.gadget.site-lib");
    const searchTitle = wgULS("UtaTen搜索", "UtaTen搜尋");
    if (mw.config.get("wgPageName") !== "Special:UtatenSearch") {
        if (mw.config.get("wgNamespaceNumber") === 0 && !$(".photrans")[0]) {
            mw.util.addPortletLink("p-tb", `/Special:UtatenSearch?song=${encodeURIComponent(mw.config.get("wgTitle"))}`, searchTitle, "ca-lr-utaten", wgULS("搜索UtaTen.com的歌词并转换为Photrans格式", "搜尋UtaTen.com的歌詞並轉換為Photrans格式"));
        }
        return;
    }
    $("#firstHeading").text(searchTitle);
    $("#mw-content-text").html(`<p>${wgULS("正在加载", "正在載入")}……</p>`);
    document.title = searchTitle + document.title.replace(/^.*?( - .*?)$/, "$1");

    await mw.loader.using(["mediawiki.Uri", "oojs-ui"]);
    const fetchHTML = async (url) => $($.parseHTML((await $.getJSON(`https://api.allorigins.win/get?url=${encodeURIComponent(url)}`)).contents));

    class UtaTenWindow extends OO.ui.ProcessDialog {
        static static = $.extend(Object.create(super.static), {
            name: "lr-utaten",
            title: searchTitle,
            actions: [
                {
                    action: "cancel",
                    label: "取消",
                    flags: ["safe", "close", "destructive"],
                    modes: "query",
                },
                {
                    action: "continue",
                    label: wgULS("继续", "繼續"),
                    flags: ["primary", "progressive"],
                    modes: "query",
                },
                {
                    action: "back",
                    label: "返回",
                    flags: ["safe", "back"],
                    modes: ["select", "result"],
                },
                {
                    action: "close",
                    label: wgULS("关闭", "關閉"),
                    flags: ["primary", "progressive"],
                    modes: "result",
                },
            ],
        });
        constructor(config) {
            // Parent constructor
            super(config);

            this.prepopContent = config.data.prepopContent;
        }
        initialize() {
            // Parent method
            super.initialize();

            this.queryPanel = new OO.ui.PanelLayout({
                scrollable: false,
                expanded: false,
                padded: true,
            });
            this.selectPanel = new OO.ui.PanelLayout({
                scrollable: false,
                expanded: false,
                padded: true,
            });
            this.resultPanel = new OO.ui.PanelLayout({
                scrollable: false,
                expanded: false,
                padded: true,
            });

            const labels = {
                artist_name: "歌手",
                song: `歌曲(${wgULS("请使用日文原标题", "請使用日文原標題")})`,
                beginning: wgULS("歌词开头", "歌詞開頭"),
                body: wgULS("歌词", "歌詞"),
                lyricist: wgULS("作词", "作詞"),
                composer: "作曲",
                sub_title: wgULS("副标题", "副標題"),
                tag: wgULS("标签", "標籤"),
            };
            this.fields = Object.entries(labels).reduce((acc, [key]) => {
                acc[key] = new OO.ui.TextInputWidget({
                    value: this.prepopContent[key] || "",
                });
                return acc;
            }, {});

            this.queryPanel.$element.append(Object.entries(this.fields).map(([key, field], index) => {
                const layout = new OO.ui.FieldLayout(field, {
                    label: labels[key],
                    align: "top",
                });
                layout.$element.css({
                    width: "50%",
                    display: "inline-block",
                    "box-sizing": "border-box",
                });
                if (!(index % 2)) {
                    layout.$element.css("padding-right", "1em");
                }
                if (index === 1) {
                    layout.$element.css("margin-top", 0);
                }
                return layout.$element;
            }));

            this.stackLayout = new OO.ui.StackLayout({
                items: [this.queryPanel, this.selectPanel, this.resultPanel],
            });

            this.$body.append(this.stackLayout.$element);
        }
        getBodyHeight() {
            return this.stackLayout.getCurrentItem().$element.outerHeight(true);
        }
        getSetupProcess(data) {
            return super.getSetupProcess(data).next(() => {
                this.actions.setMode("query");
                this.stackLayout.setItem(this.queryPanel);
            }, this);
        }
        getReadyProcess(data) {
            return super.getReadyProcess(data)
                .next(() => {
                    this.fields.artist_name.focus();
                }, this);
        }
        getActionProcess(action) {
            const dfd = $.Deferred();
            if (action === "cancel") {
                return new OO.ui.Process(() => {
                    this.close({ action: action });
                }, this);
            } else if (action === "continue") {
                return new OO.ui.Process(() => {
                    this.selectPanel.$element.empty();
                    this.search().then(() => {
                        this.actions.setMode("select");
                        this.stackLayout.setItem(this.selectPanel);
                        this.updateSize();
                        dfd.resolve();
                    }).catch((e) => {
                        dfd.reject(new OO.ui.Error(e));
                    });
                    return dfd.promise();
                }, this);
            } else if (action === "back") {
                const cur = this.stackLayout.getCurrentItem();
                if (cur === this.selectPanel) {
                    this.actions.setMode("query");
                    this.stackLayout.setItem(this.queryPanel);
                    this.updateSize();
                } else if (cur === this.resultPanel) {
                    this.actions.setMode("select");
                    this.stackLayout.setItem(this.selectPanel);
                    this.updateSize();
                }
            } else if (action === "close") {
                return new OO.ui.Process(() => {
                    this.close({ action: action });
                }, this);
            }
            // Fallback to parent handler
            return super.getActionProcess(action);
        }
        async search() {
            const $panel = this.selectPanel.$element;
            const query = Object.entries(this.fields).map(([key, field]) => `${key === "song" ? "title" : key}=${field.getValue()}`).join("/");
            const searchURL = `https://utaten.com/search/${query}/`;
            const res = await fetchHTML(searchURL);
            if (res.find(".noItem")[0]) {
                $panel.html(`<div class="errorbox">${wgULS("无搜索结果", "無搜尋結果")}。</div>`);
                return;
            }
            const table = res.find(".searchResult__title").closest("table");

            const results = table.find("tr").map((_, ele) => {
                const self = $(ele);
                if (!self.find(".searchResult__title")[0]) {
                    return;
                }
                const title = self.find(".searchResult__title").text().trim();
                const src = self.find(".searchResult__title a").attr("href");
                const artist = self.find(".searchResult__name").text().trim();
                const lyrics = self.find(".lyricList__beginning").text().trim();
                return { title, src, artist, lyrics };
            }).get();
            const resTable = $(`<table class="wikitable" style="width:100%;"><tr><th>歌曲</th><th>${wgULS("歌词开头", "歌詞開頭")}</th></tr></table>`);
            results.forEach((result) => {
                $("<tr>").append(`<td>${result.title}<br />by ${result.artist}</td><td>${result.lyrics}</td>`).on("click", () => {
                    this.getLyrics(/lyric\/(.*?)\//.exec(result.src)[1]);
                }).css("cursor", "pointer").on("mouseover", (e) => {
                    $(e.currentTarget).css("background-color", "#eaecf0");
                }).on("mouseout", (e) => {
                    $(e.currentTarget).css("background-color", "");
                }).appendTo(resTable);
            });
            const manualID = new OO.ui.TextInputWidget();
            $panel.empty().append(`<p>${wgULS("请选择您所寻找的歌曲", "請選擇您所尋找的歌曲")}:</p>`, resTable, new OO.ui.FieldsetLayout({
                items: [
                    new OO.ui.FieldLayout(manualID, {
                        label: wgULS("未找到结果?请手动输入歌词ID", "未找到結果?請手動輸入歌詞ID"),
                        align: "top",
                    }),
                    new OO.ui.FieldLayout(new OO.ui.ButtonWidget({
                        label: wgULS("搜索", "搜尋"),
                        icon: "search",
                        flags: ["progressive"],
                    }).on("click", () => {
                        const id = manualID.getValue();
                        if (!id) {
                            return;
                        }
                        this.getLyrics(id);
                    })),
                ],
            }).$element);
        }
        async getLyrics(id) {
            const $panel = this.resultPanel.$element;
            $panel.html("<p>正在获取歌词……</p>");
            this.actions.setMode("result");
            this.stackLayout.setItem(this.resultPanel);
            this.updateSize();
            const res = await fetchHTML(`https://utaten.com/lyric/${id}/`);
            const raw = res.find(".hiragana").first().html();
            if (!raw) {
                $panel.html(`<div class="errorbox">${wgULS("无法找到歌词,请检查", "無法找到歌詞,請檢查")}ID ${id} ${wgULS("是否正确", "是否正確")}。</div>`);
                this.updateSize();
                return;
            }
            const replaced = raw.replace(/\n|\r/g, "").replace(/<br>/g, "\n").replace(/<span class="ruby"><span class="rb">(.*?)<\/span><span class="rt">(.*?)<\/span><\/span>/g, (_, o, r) => o.trim() && r.trim() ? `{{Photrans|${o.trim()}|${r.trim()}}}` : o.trim()).trim();
            const decoded = $("<textarea/>").html(replaced).text(); // Decode html entities
            $panel.empty().append(`<p>${wgULS("转换成功!以下为Photrans歌词", "轉換成功!以下為Photrans歌詞")}:</p>`, new OO.ui.MultilineTextInputWidget({
                value: decoded,
                autosize: true,
                maxRows: 30,
            }).$element.css("max-width", "100%"));
            this.updateSize();
        }
    }

    const $body = $(document.body);
    const windowManager = new OO.ui.WindowManager();
    $body.append(windowManager.$element);
    const utatenDialog = new UtaTenWindow({
        size: "full",
        data: {
            prepopContent: new mw.Uri(location.href).query,
        },
    });
    windowManager.addWindows([utatenDialog]);
    windowManager.openWindow(utatenDialog);

    $("#mw-content-text").empty().append(new OO.ui.ButtonWidget({
        label: wgULS("打开搜索窗口", "開啟搜尋視窗"),
        flags: ["primary", "progressive"],
    }).on("click", () => {
        windowManager.openWindow(utatenDialog);
    }).$element);
})());