利用者:鈴音雨/modifyEditsection-modified.js

/*
 * modifyEditsection
 * トランスクルードされた節の節編集リンクを拡張する
 *
 *   dbenzhuser (de:Benutzer:Dbenzhuser)
 *   Alex Smotrov (en:User:Alex Smotrov)
 *   TheDJ (en:User:TheDJ)
 *   mizusumashi (ja:User:Mizusumashi)
 *   cpro (ja:User:Cpro)
 *   鈴音雨 (ja:User:鈴音雨)
 */
if ((mw.config.get('wgAction') == 'view' || mw.config.get('wgAction') == 'purge') &&
    // Wikipedia名前空間、または各種ノートページのみで実行
    (mw.config.get('wgNamespaceNumber') == 4 || mw.config.get('wgNamespaceNumber') % 2 == 1)) {

    mw.loader.using(['mediawiki.template', 'mediawiki.template.mustache', 'mediawiki.Uri']).done(function () {
        if (!window.modifyEditsectionConfig) {
            window.modifyEditsectionConfig = {
                show: ["view", "copy", "history", "watch", "purge"],
            };
        }

        /** mw.config の内容を保持 */
        var conf = mw.config.get([
            'wgPageName',
            'wgUserLanguage',
            'wgServer',
            'wgScript',
            'wgUserName',
            'wgAction'
        ]);

        // メッセージのセットアップ
        var messages = {
            en: {
                'openTitle': 'open',
                'openDescription': 'Open "$1#$2"',
                'copyTitle': 'copy',
                'historyTitle': 'history',
                'historyDescription': 'Past version of "$1"',
                'watchTitle': 'watch',
                'watchDescription': 'Add "$1" to your watchlist',
                'unwatchDescription': 'Remove "$1" from your watchlist',
                'purgeTitle': 'purge',
                'purgeDescription': 'Clear the cache of "$1" and view the last version of "$2"'
            },
            ja: {
                'openTitle': '閲覧',
                'openDescription': '「$1#$2」を閲覧',
                'copyTitle': 'コピー',
                'copyDescription': '「$1#$2」のアンカー付きリンクをコピー',
                'historyTitle': '履歴',
                'historyDescription': '「$1」の履歴',
                'watchTitle': 'ウォッチ',
                'watchDescription': '「$1」をウォッチリストに追加',
                'unwatchDescription': '「$1」をウォッチリストから削除',
                'purgeTitle': '更新',
                'purgeDescription': '「$1」を更新し、「$2」の最新版を反映'
            }
        };
        var myMessage = messages[conf.wgUserLanguage] || {};
        /**
         * キーを指定して現在の言語設定に対応するメッセージを取得する
         * 第2引数以降に指定があれば、メッセージ内のプレースホルダー
         * "$1", "$2"" ... を指定した文字列で置換する
         * @returns {string}
         */
        var message = function (key) {
            var msg = myMessage[key] || messages.en[key];
            for (var i = 1; i < arguments.length; i++) {
                msg = msg.replace('$' + i.toString(), arguments[i]);
            }
            return msg;
        };

        /**
         * クエリ付きのMediaWiki URIを生成する (.../w/index.php?title=...)
         * @returns {string}
         */
        var getUri = function (pageName, action, fragment) {
            var uri = new mw.Uri(conf.wgServer + conf.wgScript);
            uri.query = {
                title: pageName,
                action: action
            };
            if (fragment)
                uri.fragment = fragment;

            return uri.toString();
        };

        /** 拡張リンクのmushtacheテンプレート */
        var extensionTemplate = mw.template.add('_client', 'sectionEditLinkExtension.mustache', // <!--
            '<span class="editsection-extensions">' +
            '<span class="mw-editsection-bracket">[</span>' +
            '{{#extension}}' +
            '{{^skip}}' +
            '{{^first}}<span class="mw-editsection-divider"> | </span>{{/first}}' +
            '<a href="{{href}}" onclick="{{onclick}}" title="{{title}}">{{text}}</a>' +
            '{{#unwatchHref}}(<a href="{{unwatchHref}}" title="{{unwatchTitle}}">-</a>){{/unwatchHref}}' +
            '{{/skip}}' +
            '{{/extension}}' +
            '<span class="mw-editsection-bracket">]</span>' +
            '</span>'); // -->

        /**
         * テンプレート用のデータを生成する
         * @param {string} transcluded トランスクルードされているページ名
         * @param {string} sectionName 節タイトル
         * @param {string} sectionFragment 節リンク用識別子(URLの # 以降)
         */
        var createExtLinkDataForTemplate = function (transcluded, sectionName, sectionFragment, isTranscluded) {
            var data = {
                extension: [
                    window.modifyEditsectionConfig.show.includes("view") && isTranscluded && { // 閲覧
                        text: message('openTitle'),
                        href: getUri(transcluded, 'view', sectionFragment),
                        title: message('openDescription', transcluded, sectionName)
                    },
                    window.modifyEditsectionConfig.show.includes("copy") && { // コピー
                        text: message('copyTitle'),
                        href: 'javascript:void(0);',
                        onclick: `navigator.clipboard.writeText('${mw.config.get("wgPageName")}#${sectionFragment}');mw.notify("Copied ${mw.config.get("wgPageName")}#${sectionFragment}");return false;`,
                        title: message('copyDescription', transcluded, sectionName)
                    },
                    window.modifyEditsectionConfig.show.includes("history") && isTranscluded & { // 履歴
                        text: message('historyTitle'),
                        href: getUri(transcluded, 'history'),
                        title: message('historyDescription', transcluded)
                    },
                    window.modifyEditsectionConfig.show.includes("watch") && isTranscluded && { // ウォッチリスト追加/削除
                        text: message('watchTitle'),
                        href: getUri(transcluded, 'watch'),
                        title: message('watchDescription', transcluded),
                        unwatchHref: getUri(transcluded, 'unwatch'),
                        unwatchTitle: message('unwatchDescription', transcluded),
                        // ログインしていない場合はウォッチリストの処理をスキップ
                        skip: conf.wgUserName === null
                    },
                    window.modifyEditsectionConfig.show.includes("purge") && isTranscluded && { // 更新
                        text: message('purgeTitle'),
                        href: getUri(conf.wgPageName, 'purge', sectionFragment),
                        title: message('purgeDescription', conf.wgPageName, transcluded),
                        // パージ直後の場合はパージの処理をスキップ
                        skip: conf.wgAction == 'purge'
                    }
                ].filter(Boolean)
            };
            data.extension[0].first = true; // 最初の要素を認識させる(区切り線追加をスキップするため)

            return data;
        };

        /** 拡張処理済みの節編集リンクの名前・見出しレベルを保持するオブジェクト */
        var expanded = {};

        /** 節編集リンクを拡張する */
        var extendEditSectionLink = function (i, $editSpan) {
            var href = $('a[href*="&section=T-"]', $editSpan).attr('href');
            /** トランスクルードされているページ名 */
            var transcluded = (new mw.Uri(href)).query.title;

            /** 節編集リンクを含んでいるh*要素 */
            var parentHn = $editSpan.parentNode;

            /** 節の見出しレベル */
            var level = Number(parentHn.tagName.replace(/h/i, ''));
            // 以前に同じページからのトランスクルードがあり、かつ見出しレベルが下がっていれば、拡張しない
            if (expanded[transcluded] && level > expanded[transcluded]) {
                return;
            }
            expanded[transcluded] = level;

            var $section = $('span.mw-headline', parentHn);
            var sectionName = $section.text().trim();
            /** 節リンク用識別子(URLの # 以降) */
            var sectionFragment = $section.attr('id');

            /** テンプレートに渡すデータ */
            var data = createExtLinkDataForTemplate(transcluded, sectionName, sectionFragment, true);

            $($editSpan).append(extensionTemplate.render(data));
        };
        var extendEditSectionLinkNoTransclude = function (i, $editSpan) {
            var label = (
            	$('a[href*="&section="]', $editSpan).parent().parent().find(".mw-headline").attr("id") || ''
            ).trim();
            if ( !label ) {
            	return;
            }
            var uri = new mw.Uri($('a[href*="&section="]', $editSpan).attr('href'));
            var transcluded = uri.query.title;
            var sectionName = "";//$(`#${sectionFragment}`).text().trim();
            var data = createExtLinkDataForTemplate(transcluded, sectionName, label, false);
            $($editSpan).append(extensionTemplate.render(data));
        }

        $(function () {
            /** トランスクルードされた節編集リンク(section=T-n を含む)を子要素に持つ span.mw-editsection */
            var $editSectionLinks = $('span.mw-editsection').has('a[href*="&section=T-"]');

            var $editSection = $('span.mw-editsection').has('a[href*="&section="]').filter(function (i, el) {
                console.log($(el).find("a").attr('href'));
                return $(el).find("a").attr('href').indexOf("&section=T-") === -1;
            });
            console.log($editSection);
            if ($editSection.length === 0 && $editSectionLinks.length === 0) {
                return;
            }
            $editSection.each(extendEditSectionLinkNoTransclude);

            // ビジュアルエディターが無効だと区切り線が非表示なので強制表示
            $('.editsection-extensions .mw-editsection-divider').show();

            // 節編集リンクを順次処理
            $editSectionLinks.each(extendEditSectionLink);

            // ビジュアルエディターが無効だと区切り線が非表示なので強制表示
            $('.editsection-extensions .mw-editsection-divider').show();
        });
    }); // mw.loader.using().done(function() {
} // end if