プロジェクト:ウィキ技術部/スクリプト開発/trunk/modifyEditsection.js

/**
 * modifyEditsection (mediawiki.template.mustacheを使わないバージョン)
 * @author [[User:Dragoniez]]
 * @link https://ja.wikipedia.org/wiki/MediaWiki:Gadget-modifyEditsection.js
 */
//<nowiki>
(function() {
// *********************************************************************************

if (['view', 'purge'].indexOf(mw.config.get('wgAction')) === -1 || // action=view または action=purgeのみで実行
    mw.config.get('wgNamespaceNumber') % 2 === 0 && mw.config.get('wgNamespaceNumber') !== 4 // Wikipedia名前空間、または各種ノートページのみで実行
) {
    return;
}

$.when(
    mw.loader.using('mediawiki.Uri'),
    $.ready
).then(function() {

    /** @type {HTMLSpanElement[]} */
    var esSpans = Array.prototype.slice.call(document.querySelectorAll('.mw-editsection'));
    if (!esSpans.length) return;

    // styleタグを追加 (拡張したeditsectionリンクの区切り用擬似エレメント)
    var style = document.createElement('style');
    style.textContent = '.mw-editsection > .es-extended::before { content: " | "; }';
    document.head.appendChild(style);

    /**
     * 拡張したeditsectionリンクのページタイトルとヘッダーレベルを格納するオブジェクト
     * @type {Object.<string, number>}
     */
    var extended = {};

    /**
     * 拡張リンクを挿入する関数
     * @param {HTMLElement} parent
     * @param {HTMLElement} newNode 挿入する要素
     * @param {Element} child この要素の前
     */
    var addExtendedLink = function(parent, newNode, child) {
        parent.insertBefore(newNode, child);
    };

    mw.messages.set({
        'es-view': '閲覧',
        'es-view-tooltip': '「$1#$2」を閲覧',
        'es-history': '履歴',
        'es-history-tooltip': '「$1」の履歴',
        'es-watch': 'ウォッチ',
        'es-watch-tooltip': '「$1」をウォッチリストに追加',
        'es-unwatch': '-',
        'es-unwatch-tooltip': '「$1」をウォッチリストから削除',
        'es-purge': '更新',
        'es-purge-tooltip': '「$1」の最新版を反映',
    });

    /**
     * @param {"view"|"history"|"watch"|"unwatch"|"purge"} action
     * @param {mw.Uri} uri このUriオブジェクトをもとにアンカーを作成; actionパラメターは関数内で自動付与
     * @param {HTMLSpanElement} [refSpan] 指定されている場合、作成したアンカーを括弧で囲みこのspanにappend (新規のラッパーspanは作成しない)
     * @returns {HTMLSpanElement} 作成したラッパーspan、またはrefSpan指定時はアップデートされたrefSpan自身
     */
    var createExtendedLink = function(action, uri, refSpan) {

        uri.extend({action: action});
        var anchor = document.createElement('a');
        anchor.href = uri.toString();
        anchor.textContent = mw.msg('es-' + action);
        anchor.title = mw.msg('es-' + action + '-tooltip', uri.query.title, uri.fragment || '');

        var span;
        if (refSpan) {
            span = refSpan;
            span.appendChild(document.createTextNode('('));
            span.appendChild(anchor);
            span.appendChild(document.createTextNode(')'));
        } else {
            span = document.createElement('span');
            span.classList.add('es-extended');
            span.appendChild(anchor);
        }
        return span;

    };

    esSpans.forEach(function(span) {

        // リンク拡張に必要な要素の情報を取得
        var insertPoint = span.querySelectorAll('.mw-editsection-bracket')[1];
        if (!insertPoint) return;
        /** 
         * トランスクルードされた節のアンカー (section=T-nを含む)
         * @type {HTMLAnchorElement|null}
         */
        var a = span.querySelector('a[href*="&section=T-"]');
        if (!a) return;
        var href = a.href;
        var pr = span.parentElement;
        if (!pr) return;
        var m = pr.tagName.match(/^H(\d)$/);
        if (!m) return;
        var level = parseInt(m[1]);
        var headline;
        var section = (headline = pr.querySelector('.mw-headline')) ? headline.id : '';
        
        // 「ソースを編集」に割り当てられたページ名を取得し参照用mw.Uriインスタンスを作成
        var uri = new mw.Uri(href);
        /** @type {string|undefined} */
        var title = uri.query.title;
        if (!title) {
            // "title=" パラメターがない場合: "section=T-" をhrefにもつeditsectionアンカーは全て "/w/index.php" のため、基本的にこのブロックに辿り付くことはない
            console.warn(a);
            return;
        }
        if (!extended[title]) {
            extended[title] = level;
        } else if (level > extended[title]) { // 以前に同じページからのトランスクルードがあり、かつ見出しレベルが下がっていれば拡張しない
            return;
        }
        var baseUri = new mw.Uri(mw.config.get('wgServer') + mw.config.get('wgScript'));
        baseUri.extend({title: title});

        // リンクを拡張
        var viewUri = baseUri.clone();
        if (section) viewUri.fragment = section;
        var viewSpan = createExtendedLink('view', viewUri);
        addExtendedLink(span, viewSpan, insertPoint);

        var historySpan = createExtendedLink('history', baseUri.clone());
        addExtendedLink(span, historySpan, insertPoint);

        var watchSpan = createExtendedLink('watch', baseUri.clone());
        watchSpan = createExtendedLink('unwatch', baseUri.clone(), watchSpan);
        addExtendedLink(span, watchSpan, insertPoint);

        var purgeSpan = createExtendedLink('purge', baseUri.clone());
        addExtendedLink(span, purgeSpan, insertPoint);

    });

});

// *********************************************************************************
})();
//</nowiki>