MediaWiki:Common.js

注意: 保存後、変更を確認するにはブラウザーのキャッシュを消去する必要がある場合があります。

  • Firefox / Safari: Shift を押しながら 再読み込み をクリックするか、Ctrl-F5 または Ctrl-R を押してください (Mac では ⌘-R)
  • Google Chrome: Ctrl-Shift-R を押してください (Mac では ⌘-Shift-R)
  • Internet Explorer: Ctrl を押しながら 最新の情報に更新 をクリックするか、Ctrl-F5 を押してください
  • Opera: メニュー → 設定 (Mac では Opera → 環境設定) に移動し、プライバシーとセキュリティ → 閲覧データを消去 → キャッシュされた画像およびファイル からキャッシュをクリアしてください。
// <source lang="javascript">
/* ここに書いたスクリプトは全ての外装に反映されます */

/**
 * Collapsible tables *********************************************************
 *
 * Description: Allows tables to be collapsed, showing only the header. See
 *              [[Wikipedia:NavFrame]].
 * Maintainers: [[User:R. Koot]]
 */
 
var autoCollapse = 2;
var collapseCaption = "隠す";
var expandCaption = "表示";
 
window.collapseTable = function ( tableIndex ) {
    var Button = document.getElementById( 'collapseButton' + tableIndex );
    var Table = document.getElementById( 'collapsibleTable' + tableIndex );
 
    if ( !Table || !Button ) {
        return false;
    }
 
    var Rows = Table.rows;
    var i;
 
    if ( Button.firstChild.data === collapseCaption ) {
        for ( i = 1; i < Rows.length; i++ ) {
            Rows[i].style.display = 'none';
        }
        Button.firstChild.data = expandCaption;
    } else {
        for ( i = 1; i < Rows.length; i++ ) {
            Rows[i].style.display = Rows[0].style.display;
        }
        Button.firstChild.data = collapseCaption;
    }
};
 
function createCollapseButtons() {
    var tableIndex = 0;
    var NavigationBoxes = {};
    var Tables = document.getElementsByTagName( 'table' );
    var i;
 
    function handleButtonLink( index, e ) {
        window.collapseTable( index );
        e.preventDefault();
    }
 
    for ( i = 0; i < Tables.length; i++ ) {
        if ( $( Tables[i] ).hasClass( 'collapsible' ) ) {
 
            /* only add button and increment count if there is a header row to work with */
            var HeaderRow = Tables[i].getElementsByTagName( 'tr' )[0];
            if ( !HeaderRow ) continue;
            var Header = HeaderRow.getElementsByTagName( 'th' )[0];
            if ( !Header ) continue;
 
            NavigationBoxes[ tableIndex ] = Tables[i];
            Tables[i].setAttribute( 'id', 'collapsibleTable' + tableIndex );
 
            var Button     = document.createElement( 'span' );
            var ButtonLink = document.createElement( 'a' );
            var ButtonText = document.createTextNode( collapseCaption );
 
            Button.className = 'collapseButton';  /* Styles are declared in Common.css */
 
            ButtonLink.style.color = Header.style.color;
            ButtonLink.setAttribute( 'id', 'collapseButton' + tableIndex );
            ButtonLink.setAttribute( 'href', '#' );
            $( ButtonLink ).on( 'click', $.proxy( handleButtonLink, ButtonLink, tableIndex ) );
            ButtonLink.appendChild( ButtonText );
 
            Button.appendChild( document.createTextNode( '[' ) );
            Button.appendChild( ButtonLink );
            Button.appendChild( document.createTextNode( ']' ) );
 
            Header.insertBefore( Button, Header.firstChild );
            tableIndex++;
        }
    }
 
    for ( i = 0;  i < tableIndex; i++ ) {
        if ( $( NavigationBoxes[i] ).hasClass( 'collapsed' ) || ( tableIndex >= autoCollapse && $( NavigationBoxes[i] ).hasClass( 'autocollapse' ) ) ) {
            window.collapseTable( i );
        } 
        else if ( $( NavigationBoxes[i] ).hasClass ( 'innercollapse' ) ) {
            var element = NavigationBoxes[i];
            while ((element = element.parentNode)) {
                if ( $( element ).hasClass( 'outercollapse' ) ) {
                    window.collapseTable ( i );
                    break;
                }
            }
        }
    }
}
 
mw.hook( 'wikipage.content' ).add( createCollapseButtons );

/////////////////////////////////////////////////////////////////
// 記事名チェッカ for SRW Wiki
// Originally written by Tietew and Hatukanezumi
// Modified by ocha
/////////////////////////////////////////////////////////////////

/*jslint regexp: true, browser: true */
/*global $, wgPageName, wgNamespaceNumber, wgTitle, wgArticleId, skin, getElementsByClassName */

// 無効化のフラグ変数
// 無効化は、利用者のユーザスクリプトで真に設定することでする
var disableTitleChecker = false;

var titleChecker = (function () {
    'use strict';

    // 記事名チェッカによる処理を適用しない記事名のリスト(除外リスト)
    var excludes = [
            // [名前空間番号, 項目名], ...
        ],
        lenExcludes = excludes.length,

        /*
         * 記事名を検査し、結果を返す
         * 引数:args {
         *   ns:    名前空間番号(標準:0;ノート:1;…)
         *   title: 項目名
         *   body:  項目本文の内容
         * }
         * 返値:{
         *   action: 処理
         *   reasons: 理由。[{message: 説明文, guides: [ガイドライン等, ...]}]
         *   for_redirect: リダイレクトの即時削除対象になりうるかどうか
         * }
         *   ただし、処理が「許可」であるときは false
         */
        check = function (args) {
            var ns = args.ns,
                title = args.title,
                body = args.body,

                matchesSafari, // 'AppleWebkit' へのマッチ

                is_redirect, // リダイレクトであるかどうか
                action = 0, // 処理。0:許可する;1:警告する;2:拒否する
                reasons = [], // 理由。[{message: 説明文, 
                              // guides: [ガイドライン等, ...]}]
                for_redirect = false,

                i,

                do_check = function (args) {
                    var re,
                        m,
                        matched,
                        reason,
                        msgstr;

                    re = new RegExp(args.pattern, 'g');
                    m = title.match(re);
                    if (m && (matched = m.join(' ')) !== '') {
                        if (args.newaction > action) {
                            action = args.newaction;
                        }

                        msgstr = args.message;
                        msgstr = msgstr.replace('%s', matched)
                            .replace('%%', '%');
                        reason = {
                            message: msgstr,
                            guides: args.guides || null
                        };
                        reasons.push(reason);

                        /*
                         * if (guides && is_redirect) {
                         *     lenGuides = guids.length;
                         *     for (var i = 0; i < lenGuides; i += 1) {
                         *         if (guides[i] === "Wikipedia:即時削除の方針#リダイレクト") {
                         *             for_redirect = true;
                         *             break;
                         *         }
                         *     }
                         * }
                         */
                    }
                };

            title = title.replace(/_/g, ' ');

            // 記事ごとの無効化。除外リストにあれば検査をしない。
            if (lenExcludes > 0) {
                for (i = 0; i < lenExcludes; i += 1) {
                    if (excludes[i][0] === ns && excludes[i][1] === title) {
                        return false;
                    }
                }
            }

            // プラットフォームの判別
            // AppleWebKit/416 (Safari/2.0.2) 以降か?
            matchesSafari = navigator.userAgent.match(/\bAppleWebKit\/(\d+)/);
            if (matchesSafari && matchesSafari[1] < 416) {
                return false; // Safari/1.x ならば検査をしない
            }

            // リダイレクトであるかどうかの判定
            is_redirect = (body && body.match(/^#(?:redirect|転送)\s*\[\[.+?\]\]/i));

            /*
             * 書式の検査
             */

            // 記事名の全体を「」または『』などでくくっているもの
            // ないしは、その後に曖昧さ回避の括弧があるもの(拒否)
            do_check({
                pattern: '^[「『].*[」』]([ _]+[(][^)]+[)])?$',
                newaction: 2,
                message: '記事名が鈎括弧でくくられています。' +
                    'タイトルは鈎括弧でくくるべきではありません。'
            });

            // 記事名の末尾の括弧書きに半角の小括弧(丸括弧)を使用する場合に、
            // 左括弧の前に半角スペースがないもの(警告)
            // 注: 入れ子は2重まで
            do_check({
                pattern: '[^ ][(]([(][^()]*[)]|[^()])*[)]$',
                newaction: 1,
                message: '記事名の最後の左括弧の前に半角スペースがありません' +
                    ': %s。' +
                    '曖昧さ回避の括弧である場合は、括弧の前に半角スペースを' +
                    '入れてください。' +
                    '名称自体に括弧を含んでいる場合はこの限りではありません。',
                guides: ['Project:記事制作のポイント#記事名の表記法']
            });

            // 記事名に半角の小括弧(丸括弧)を使用する場合に、
            // 括弧の左右が対称でないもの(警告)
            // 注: 入れ子は2重まで
            do_check({
                pattern: '[(]([(][^()()]*[)]|[(][^()()]*[)]|[^()()])*[)]|[(]([(][^()()]*[)]|[(][^()()]*[)]|[^()()])*[)]',
                newaction: 1,
                message: '括弧の左右が対称ではありません: %s。' +
                    '両方を、半角括弧か全角括弧に統一してください。'
            });

            // 記事名に実体参照または数値文字参照を含んでいるもの(拒否)
            do_check({
                pattern: '([&][#]?[A-Za-z\\d]+;)+',
                newaction: 2,
                message: '記事名に実体参照を含んでいます: %s。' +
                    '実体参照を記事名に使うべきではありません。'
            });

            /*
             * 使用文字種の検査
             */

            // 技術的な考慮(拒否)

            do_check({
                pattern: '&',
                newaction: 2,
                message: '半角アンパサンド (&) を含んでいます。' +
                    '記事が正常に表示されなくなるため、' +
                    'この文字は記事名に使用できません。' +
                    '代わりに全角アンパサンド(&)を使用してください。'
            });

            do_check({
                pattern: '\\+',
                newaction: 2,
                message: '半角プラス記号 (+) を含んでいます。' +
                    '記事が正常に表示されなくなるため、' +
                    'この文字は記事名に使用できません。' +
                    '代わりに全角プラス記号(+)を使用してください。'
            });

            do_check({
                pattern: '[\uD83F\uD87F\uD8BF\uD8FF\uD93F\uD97F\uD9BF\uD9FF\uDA3F\uDA7F\uDABF\uDAFF\uDB3F\uDB7F\uDBBF\uDBFF][\uDFFE\uDFFF]',
                newaction: 2,
                message: '文字でないものを含んでいます。' +
                    '文字でないものを記事名に使うべきではありません。'
            });

            do_check({
                pattern: '[\u0080-\u009F\uFFF0-\uFFFD\uDB40-\uDB7F]',
                newaction: 2,
                message: '制御文字を含んでいます。' +
                    '制御文字を記事名に使うべきではありません。'
            });

            do_check({
                pattern: '[\u00A0]',
                newaction: 2,
                message: 'ノーブレークスペースを含んでいます。' +
                    '通常のスペースを使ってください。'
            });

            do_check({
                pattern: '[\u00AD]',
                newaction: 2,
                message: 'ソフトハイフンを含んでいます。' +
                    'ソフトハイフンを記事名に使うべきではありません。'
            });

            do_check({
                pattern: '[\u2000-\u200A\u200B\u205F]',
                newaction: 2,
                message: '特別な幅のスペースを含んでいます。' +
                    '通常のスペースを使ってください。'
            });

            do_check({
                pattern: '[\u200C\u200D\u2060]',
                newaction: 2,
                message: '書式制御文字を含んでいます。' +
                    '一部の言語ではこの文字を使いますが、' +
                    '記事名には日本語を使ってください。'
            });

            do_check({
                pattern: '[\u2011]',
                newaction: 2,
                message: 'ノンプレーキングハイフンを含んでいます。' +
                    'ハイフンマイナス (-) を使ってください。'
            });

            do_check({
                pattern: '[\u2028-\u202E\u2061-\u206F]',
                newaction: 2,
                message: '書式制御文字を含んでいます。' +
                    '書式制御文字を記事名に使うべきではありません。'
            });

            do_check({
                pattern: '[\u202F]',
                newaction: 2,
                message: '特別な幅のノーブレークスペースを含んでいます。' +
                    '通常のスペースを使ってください。'
            });

            do_check({
                pattern: '[\uE000-\uF8FF\uDB80-\uDBFF]',
                newaction: 2,
                message: '私用文字を含んでいます。' +
                    '私用文字(外字)を記事名に使うべきではありません。'
            });

            do_check({
                pattern: '[\uFEFF]',
                newaction: 2,
                message: '不可視な文字を含んでいます。' +
                    'この文字を記事名に使うべきではありません。'
            });

            // その他のガイドライン等(拒否)

            do_check({
                pattern: '[\uFF65]',
                newaction: 2,
                message: '半角中黒 (・) を含んでいます。' +
                    '代わりに全角中黒(・)を使用してください。',
                guides: ['Project:記事制作のポイント#記事名の表記法']
            });

            do_check({
                pattern: '[=\u30A0]',
                newaction: 2,
                message: '半角等号 (=) またはダブルハイフン(゠)を含んでいます。' +
                    '代わりに全角等号(=)を使用してください。',
                guides: ['Project:記事制作のポイント#記事名の表記法']
            });

            do_check({
                pattern: '[\uFF08\uFF09]',
                newaction: 2,
                message: '全角括弧を含んでいます。' +
                    '代わりに半角括弧を使用してください。',
                guides: ['Project:記事制作のポイント#記事名の表記法']
            });

            do_check({
                pattern: '[\u2160-\u217F]+',
                newaction: 2,
                message: 'ローマ数字を含んでいます: %s。' +
                    'これは機種依存文字です。' +
                    'ローマ数字は半角英字(iやVなど)を並べて表記してください。'
            });

            do_check({
                pattern: '[\u2460-\u2473\u24EA-\u24FF\u3251-\u325F\u32B1-\u32BF]+',
                newaction: 2,
                message: '丸数字を含んでいます: %s。' +
                    'これは機種依存文字です。' +
                    '代わりに (1)、(2)、(3) などを使用してください。'
            });

            do_check({
                pattern: '[\u2474-\u24B5\u3200-\u3250\u32C0-\u32CF\u3300-\u33FF]+',
                newaction: 2,
                message: '組文字を含んでいます: %s。' +
                    '片仮名や、漢字、英数字で表記してください。' +
                    'これは機種依存文字である場合もあります。'
            });

            do_check({
                pattern: '[\u3000]',
                newaction: 2,
                message: '全角空白を含んでいます。' +
                    '全角空白を記事名に使うべきではありません。',
                guides: ['Project:記事制作のポイント#記事名の表記法']
            });

            do_check({
                pattern: '[\uFB00-\uFEFE\uFFE0-\uFFE7\uFFE8-\uFFEF]+',
                newaction: 2,
                message: '機種依存文字を含んでいます: %s。' +
                    '機種依存文字を記事名に使うべきではありません。'
            });

            do_check({
                pattern: '[\uFF10-\uFF19]+',
                newaction: 2,
                message: '全角数字を含んでいます: %s。' +
                    '全角英数字を記事名に使うべきではありません。',
                guides: ['Project:記事制作のポイント#記事名の表記法']
            });

            do_check({
                pattern: '[\uFF21-\uFF3A\uFF41-\uFF5A]+',
                newaction: 2,
                message: '全角英字を含んでいます: %s。' +
                    '全角英数字を記事名に使うべきではありません。',
                guides: ['Project:記事制作のポイント#記事名の表記法']
            });

            /*
             * do_check({
             *     pattern: '[\uFF00\uFF02\uFF04-\uFF07\uFF0A-\uFF0F\uFF1B\uFF20\uFF3C\uFF3E-\uFF40\uFF5F-\uFF60]+',
             *     newaction: 1,
             *     message: '全角英記号を含んでいます: %s。' +
             *         '全角英記号を記事名に使うべきではありません。',
             *     guides: ['Project:記事制作のポイント#記事名の表記法']
             * });
             */

            do_check({
                pattern: '[\uFF61-\uFF64\uFF66-\uFF9F]+',
                newaction: 2,
                message: '半角片仮名を含んでいます: %s。' +
                    '半角片仮名を記事名に使うべきではありません。'
            });

            do_check({
                pattern: '[\uFFA0-\uFFDF]+',
                newaction: 2,
                message: '半角ハングル字母を含んでいます: %s。' +
                    '半角ハングル字母を記事名に使うべきではありません。'
            });

            // その他のガイドライン等(警告)

            if (!is_redirect) { // リダイレクトでない場合のみ
                do_check({
                    pattern: '[\uFF5E]+',
                    newaction: 1,
                    message: '全角チルダを含んでいます。' +
                        'この文字は、一部の環境で正しく表示されません。' +
                        '波ダッシュ(〜)か、できればハイフンマイナス (-) を' +
                        '使ってください。' +
                        '波ダッシュを使った記事名へのリダイレクトを作成しようと' +
                        'している場合は、この限りではありません。'
                });
            }

            if (action === 0) {
                return false; // 処理が「許可」であるときは false を返す
            }

            return {
                action: action,
                reasons: reasons,
                for_redirect: for_redirect
            };
        },

        /*
         * 記事名を検査した結果を表示する
         * 引数:args {
         *   mark:   説明文などを挿入する場所のelement
         *   action: 処理
         *   reasons: 理由。[{message: 説明文, guides: [ガイドライン等, ...]}]
         *   for_redirect: リダイレクトの即時削除対象でありうるかどうか
         * }
         * 返値:
         *   なし
         */
        warn = function (args) {
            /*
             * プロトタイプ拡張
             */

            // [関数]
            // メソッドの追加
            Function.prototype.method = function (name, func) {
                if (!this.prototype[name]) {
                    this.prototype[name] = func;
                    return this;
                }
            };

            // [文字列]
            // '<'、'>'、'&' の実体参照化
            String.method('entityify', function () {
                return this.replace(/&/g, '&amp;').replace(/</g, '&lt;')
                    .replace(/>/g, '&gt;');
            });

            /*
             * 処理
             */

            var mark = args.mark,
                reasons = args.reasons,
                lenReasons,
                guides = [],
                lenGuides,
                guideLinks = [],

                wl = function (pageName, opt_linkName) {
                    pageName = pageName.replace(/_/g, ' ');
                    var linkName = opt_linkName || pageName,
                        url = encodeURI(pageName.replace(/ /g, '_')),
                        hashIndex;

                    // アンカーを MediaWiki の形式に変換する
                    hashIndex = url.lastIndexOf('#');
                    if (hashIndex !== -1) {
                        url = url.slice(0, hashIndex) +
                            url.slice(hashIndex).replace(/%/g, '.');
                    }

                    return '<a href="' + url +
                        '" title="' + pageName.entityify() + '">' +
                        linkName.entityify() + '</a>';
                },

                divWarn,
                html = '\n<p><strong>警告: このページの記事名の付け方は、' +
                    wl('Project:記事制作のポイント', '{{SITENAME}}の方針') +
                    'に沿っていないかもしれません。' +
                    '理由は以下のとおりです。</strong></p>',

                i,
                j;

            if (reasons) {
                html += '\n<ul>';

                lenReasons = reasons.length;
                for (i = 0; i < lenReasons; i += 1) {
                    html = html + '\n<li>' +
                        reasons[i].message.entityify(); // 説明文
                    if (reasons[i].guides) {
                        guides = reasons[i].guides.concat(); // 配列のコピー
                        lenGuides = guides.length;
                        guideLinks = [];
                        for (j = 0; j < lenGuides; j += 1) {
                            guideLinks.push(wl(guides[j]));
                        }
                        html = html + '詳しくは、' + guideLinks.join('、') +
                            'を参照してください。';
                    }
                    html += '</li>';
                }
                html += '\n</ul>';
            }
            html = html +
                '\n<p>記事名の変更を検討してみてください。' +
                'なお、記事名を変更したときは、' +
                wl('特別:Whatlinkshere/' + wgPageName, 'このページのリンク元') +
                'を調べて、新しい記事へのリンクに変更するようにしてください。</p>\n';
                /*
                 * '<p>記事名チェック機能の詳細は、' +
                 * wl('Help:記事名のチェック') +
                 * 'をご覧ください。</p>';
                 */

            /*
             * if (for_redirect && wgArticleId)
             *     html = html +
             *         '<p>編集中のページは' + wl('Wikipedia:リダイレクト', 'リダイレクト') + 'ですが、' +
             *         '即時削除に出せるかもしれません。' +
             *         'リダイレクトの即時削除に出すことができるのは、以下のすべてが該当する場合です。</p><ul>' +
             *         '<li>項目名の書き誤りで、それが誰が見ても明らかに誤りだとわかる。</li>' +
             *         '<li>項目が有益な履歴を持っていない。</li>' +
             *         '<li>項目がどこからもリンクされていない。</li>' +
             *         '</ul><p>リダイレクトの即時削除についての詳細は、' + wl('Wikipedia:即時削除の方針')+'を参照してください。</p>';
             */

            divWarn = document.createElement('div');
            divWarn.className = 'titleChecker-warn';
            divWarn.innerHTML = html;
            mark.parentNode.insertBefore(divWarn, mark.nextSibling);
        };

    /*
     * 記事名チェッカの処理を実行する。
     * 引数:
     *   なし。
     * 返値:
     *   処理を実行したならば true
     *   処理を実行しなかったならば false
     */
    return function () {

        /*
         * 要素のdisabled属性をオンにする
         * 引数:
         *   elements: 要素の配列
         * 返値:
         *   なし
         */
        var disableElements = function (elements) {
                var lenElements = elements.length,
                    e,
                    i;

                for (i = 0; i < lenElements; i += 1) {
                    e = document.getElementById(elements[i]);
                    if (e) {
                        e.disabled = true;
                    }
                }
            },

            editform = document.getElementById('editform'),
            checkResult,
            mark;

        // 利用者ごとの無効化
        if (disableTitleChecker) {
            return false;
        }

        // 検査は標準名前空間の記事に対してのみ行う
        if (wgNamespaceNumber !== 0) {
            return false;
        }

        // 検査は利用者が新規作成または編集しようとしたときにのみ行う
        if (!editform) {
            return false;
        }

        // 検査して結果を得る
        checkResult = check({
            ns: wgNamespaceNumber,
            title: wgTitle,
            body: document.getElementById('wpTextbox1').value
        });

        // 検査の結果、次のいずれかの処理を行う

        // 許可:「警告」や「拒否」に該当する処理をしない場合は、何もしない
        if (!checkResult) {
            return false;
        }

        // 拒否:編集をできなくする
        if (checkResult.action > 1) {
            if (wgArticleId === 0) {
                editform.parentNode.removeChild(editform);
            } else {
                disableElements([
                    'wpSummary',
                    'wpMinoredit',
                    'wpWatchthis',
                    'wpSave',
                    'wpPreview',
                    'wpDiff'
                ]);
                document.getElementById('wpTextbox1').readOnly = true;
            }
        }

        // 警告と拒否:説明文を表示する
        if (skin === 'standard' || skin === 'cologneblue' ||
                skin === 'nostalgia') {
            mark = document.getElementById('specialpages') ?
                    document.getElementById('topbar') :
                    getElementsByClassName(document, 'h1', 'pagetitle')[0]
                        .nextSibling;
        } else {
            mark = document.getElementById('jump-to-nav') ||
                document.getElementById('contentSub');
        }

        warn({
            mark: mark,
            action: checkResult.action,
            reasons: checkResult.reasons,
            for_redirect: checkResult.for_redirect
        });

        return true;
    };
}());

/*
 * 以上の処理は、ページ読み込みの際に実行する
 */
$(titleChecker);

// </source>