コンテンツにスキップ

MediaWiki:Forum.js

提供: Re-Agatadia
2026年2月17日 (火) 15:12時点におけるSzkt (トーク | 投稿記録)による版 (ページの作成:「// <nowiki> (()=>{ // 設定の読み込み待機 const checkConfig = () => { if (!window.mw || !mw.config || !mw.loader) return; // 設定がない場合のデフォルト値 mw.forum = mw.forum || {}; mw.forum.toppage = mw.forum.toppage || 'KenryoBBS1'; mw.forum.threads = mw.forum.threads || 20; const fedit = mw.util.getParamValue('fedit'); // ページ判定(掲示板ペ…」)
(差分) ← 古い版 | 最新版 (差分) | 新しい版 → (差分)

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

  • Firefox / Safari: Shift を押しながら 再読み込み をクリックするか、Ctrl-F5 または Ctrl-R を押してください (Mac では ⌘-R)
  • Google Chrome: Ctrl-Shift-R を押してください (Mac では ⌘-Shift-R)
  • Microsoft Edge: Ctrl を押しながら 最新の情報に更新 をクリックするか、Ctrl-F5 を押してください。
// <nowiki>
(()=>{
    // 設定の読み込み待機
    const checkConfig = () => {
        if (!window.mw || !mw.config || !mw.loader) return;
        
        // 設定がない場合のデフォルト値
        mw.forum = mw.forum || {};
        mw.forum.toppage = mw.forum.toppage || 'KenryoBBS1';
        mw.forum.threads = mw.forum.threads || 20;
        
        const fedit = mw.util.getParamValue('fedit');
        
        // ページ判定(掲示板ページでなければ終了)
        // バッククォート ( ` ) に注意してください
        if(!new RegExp(`^${mw.forum.toppage}(|/.*)$`).test(mw.config.get('wgPageName')) || mw.config.get('wgAction') != 'view'){
            return;
        }
        
        // 編集モード時のユーザーチェック
        if(fedit && document.querySelector(`[id="${fedit}-user"]`) && document.querySelector(`[id="${fedit}-user"]`).innerText != mw.config.get('wgUserName')){
            return;
        }

        // UIモジュールの読み込み
        mw.loader.load(['mediawiki.ui.button','mediawiki.ui.input','mediawiki.ui.checkbox']);
        
        mw.loader.using(['mw.util', 'mw.user', 'mediawiki.api'], function() {
            // 動物リスト
            const animals = [
                'アライグマ', 'カピバラ', 'キリン', 'ペンギン', 'パンダ', 
                'ウサギ', 'リス', 'ハリネズミ', 'コアラ', 'ナマケモノ',
                'イルカ', 'クジラ', 'サメ', 'タコ', 'イカ',
                'フクロウ', 'タカ', 'スズメ', 'ツバメ', 'カラス',
                'ライオン', 'トラ', 'ゾウ', 'サイ', 'カバ',
                'オオカミ', 'キツネ', 'タヌキ', 'イノシシ', 'シカ'
            ];

            const isAnon = mw.user.isAnon();
            const isAdmin = mw.config.get('wgUserGroups').includes('sysop');

            // ユーザー名の決定
            const stringToHash = (str) => {
                let hash = 0;
                if (!str || str.length === 0) return hash;
                for (let i = 0; i < str.length; i++) {
                    const char = str.charCodeAt(i);
                    hash = ((hash << 5) - hash) + char;
                    hash = hash & hash;
                }
                return Math.abs(hash);
            };

            (async()=>{
                if (isAnon) {
                    const identifier = mw.user.getName() || mw.user.sessionId();
                    const index = stringToHash(identifier) % animals.length;
                    mw.forum.username = '匿名' + animals[index];
                } else {
                    mw.forum.username = mw.config.get('wgUserName');
                }
                if(!mw.forum.username) {
                     // フォールバック
                     try {
                         const token = await fetch(mw.config.get('wgArticlePath').replace('$1',"特別:トークページ")).then(r=>r.text());
                         const doc = new DOMParser().parseFromString(token,'text/html');
                         const title = doc.querySelector('.mw-page-title-main');
                         if(title) mw.forum.username = title.innerText;
                     } catch(e) {}
                }
                
                // メイン処理の開始
                startForum();
            })();

            // メイン処理関数
            function startForum() {
                const msg = Object.assign({
                    loading: '<p>読み込み中...</p>',
                    postform: `<div id="f-form"><h2>投稿${fedit ? `の編集 (<a href="#post-${fedit}")>#${fedit}</a>)` : ''}</h2><div class="mw-ui-checkbox" style="margin-bottom:.3em;${fedit ? 'display:none;' : ''}"><input type="checkbox" class="mw-ui-checkbox" id="f-reply-cb"><label for="f-reply-cb" style="user-select:none;">返信する</label></div><input type="number" id="f-reply" class="mw-ui-input" style="widht:50%;margin-bottom:.5em;"><textarea accesskey="," id="wpTextbox1" cols="80" rows="25" class="mw-editfont-monospace"></textarea><input type="button" id="f-post" value="投稿" class="mw-ui-button mw-ui-progressive" style="margin-top:.5em;"><input type="button" id="f-preview" value="プレビュー" class="mw-ui-button" style="margin: .5em 0 0 .5em;"><fieldset hidden><legend>プレビュー</legend><div id="f-preview-content"></div></fieldset><style>.mw-ui-checkbox:has(#f-reply-cb:checked)+input{display:block;}#f-reply{display:none;}</style></div>`,
                    postsummary: 'post',
                    replysummary: 'reply to',
                    editsummary: 'edit',
                    deletesummary: 'delete post',
                    toppage_css: "<style>.f-sticky>td:first-child>a::before{content:'';background-image:url(https://upload.wikimedia.org/wikipedia/commons/a/a5/OOjs_UI_icon_pushPin.svg);width:.8em;height:.8em;margin-right:.2em;display:inline-block;background-size:.8em}#f-loadmore{display:block;margin-left:auto;margin-right:auto;}</style>",
                    load_more: '<input type="button" value="もっと読み込む" class="mw-ui-button" id="f-loadmore">',
                    create: '<input type="button" class="mw-ui-button mw-ui-progressive" value="スレッドを作成">',
                    createform: '<div id="f-form"><label>スレッド名: <input type="text" id="f-threadname" class="mw-ui-input" style="margin-bottom:.5em;"></label><textarea accesskey="," id="wpTextbox1" cols="80" rows="25" class="mw-editfont-monospace"></textarea><input type="button" value="スレッドを作成" id="f-create" class="mw-ui-button mw-ui-progressive" style="margin-top:.5em;"><input type="button" id="f-preview" value="プレビュー" class="mw-ui-button" style="margin: .5em 0 0 .5em;"><fieldset hidden><legend>プレビュー</legend><div id="f-preview-content"></div></fieldset></div>',
                    createthreadsummary: 'スレッドの作成',
                    posterror: 'エラー: 投稿できませんでした',
                    deleteerror: 'エラー: 削除できませんでした',
                    gotoform: '<a href="#wpTextbox1"><input type="button" value="投稿フォームへ" class="mw-ui-button" id="f-loadmore"></a>',
                    editlink: `<a href="${(()=>{const url=new URL(location.href);url.searchParams.set('fedit','$1');return url;})()}" style="margin-left:7px;">編集</a>`
                }, mw.forum.msg);

                const func = {
                    getthreads: async (cont) => {
                        return await new mw.Api().get({
                            action: "query",
                            format: "json",
                            list: "allpages",
                            formatversion: "2",
                            [(cont ? 'apcontinue' : 'apprefix')]: (cont ?? new mw.Title(mw.forum.toppage).title+'/'),
                            apnamespace: new mw.Title(mw.forum.toppage).namespace,
                            apfilterredir: "nonredirects",
                            aplimit: mw.forum.threads
                        }).then(async (allpages) => {
                            return await new mw.Api().get({
                                action: "parse",
                                format: "json",
                                text: `{{#invoke:Thread|main|1=${cont ? '{}' : JSON.stringify(mw.forum.sticky)}|2=${JSON.stringify(allpages.query.allpages)}${cont ? '|3=true' : ''}}}`,
                                prop: "text",
                                wrapoutputclass: "",
                                contentmodel: "wikitext",
                                formatversion: "2"
                            }).then((parse) => {
                                return [parse.parse.text, (allpages.continue ? allpages.continue.apcontinue : undefined)];
                            });
                        });
                    },
                    preview: function(){
                        new mw.Api().get({
                            action: "parse",
                            format: "json",
                            text: document.querySelector("#wpTextbox1").value,
                            prop: "text",
                            preview: "true",
                            wrapoutputclass: "",
                            contentmodel: "wikitext",
                            disablelimitreport: "1",
                            formatversion: "2"
                        }).then((parse) => {
                            document.querySelector("#f-form > fieldset").hidden = false;
                            document.querySelector("#f-preview-content").innerHTML = `<div style="white-space:pre;">${parse.parse.text}</div>`;
                        });
                    },
                    deletePost: async (postId) => {
                        if(!confirm('本当にこの投稿を削除しますか?')) return;
                        const source = await (await fetch(mw.config.get('wgScript') + `?title=${mw.config.get('wgPageName')}&action=raw`)).text();
                        const newText = source.replace(new RegExp(`(\\{\\{post\\|(.*?)\\|${postId}\\|(.*?)\\|4=)((.|\n)*?)(}})`), '$1[Deleted]$6');
                        new mw.Api().postWithToken('csrf', {
                            action: 'edit',
                            title: mw.config.get('wgPageName'),
                            text: newText,
                            summary: `${msg.deletesummary} #${postId}`,
                            format: 'json'
                        }).done(() => {
                            location.reload();
                        }).fail(() => {
                            mw.notify(msg.deleteerror);
                        });
                    }
                };

                const mc = document.querySelector('#mw-content-text>.mw-parser-output');
                if(!mc) return; // コンテンツエリアが見つからない場合は終了

                if(mw.config.get('wgPageName') == mw.forum.toppage){
                    // トップページ処理
                    if(mw.util.getParamValue('newthread')=='1'){
                        mc.innerHTML = msg.createform;
                        document.querySelector("#f-preview").onclick = func.preview;
                        mw.loader.using('ext.wikiEditor');
                        document.querySelector('#f-create').onclick = function(){
                            let content = '';
                            if(mw.forum.zeroTemplate){
                                content += `{{post|System|0|{{subst:#timel:Y/m/d H:i}}|4=${mw.forum.zeroTemplate}}}\n`;
                            }
                            content += `{{post|${mw.forum.username}|1|{{subst:#timel:Y/m/d H:i}}|4=${document.querySelector('#wpTextbox1').value}}}`;
                            new mw.Api().postWithToken('csrf', {
                                action: 'edit',
                                title: `${mw.forum.toppage}/${document.querySelector('#f-threadname').value}`,
                                text: content,
                                summary: msg.createthreadsummary,
                                format: 'json'
                            }).done(() => {
                                location.href = `${mw.config.get('wgArticlePath').replace('$1',mw.forum.toppage)}/${document.querySelector('#f-threadname').value}`;
                            }).fail(() => {
                                mw.notify(msg.posterror);
                            });
                        };
                        return;
                    }
                    mc.innerHTML = msg.loading;
                    func.getthreads().then((res) => {
                        mc.innerHTML = res[0] + msg.toppage_css;
                        if(res[1]){
                            document.querySelector('#mw-content-text').innerHTML += msg.load_more;
                            document.querySelector('#f-loadmore').onclick = function(){
                                func.getthreads(res[1]).then((mres) => {
                                    document.querySelector('#mw-content-text>.mw-parser-output>table>tbody').innerHTML += mres[0];
                                    if(!mres[1]){
                                        document.querySelector('#f-loadmore').remove();
                                    }
                                });
                            };
                        }
                    });
                    const indicators = document.querySelector('.mw-indicators');
                    if(indicators) {
                        indicators.innerHTML = msg.create;
                        indicators.querySelector("input").onclick = function(){
                            const url=new URL(window.location.href);
                            url.searchParams.set('newthread','1');
                            location.href=url;
                        };
                    }
                }else{
                    // スレッドページ処理
                    mc.innerHTML += msg.postform;
                    document.querySelector("#f-preview").onclick = func.preview;
                    mw.loader.using('ext.wikiEditor');
                    const indicators = document.querySelector('.mw-indicators');
                    if(indicators) indicators.innerHTML = msg.gotoform;
                    
                    document.querySelector('#f-post').onclick = (async() => {
                        document.querySelector('#f-post').disabled = true;
                        const source = (await (await fetch(mw.config.get('wgScript') + `?title=${mw.config.get('wgPageName')}&action=raw`)).text());
                        const lp = source.split('{{post|').length;
                        let summary;
                        if(fedit) {
                            summary = `${msg.editsummary} [[${mw.config.get('wgPageName')}#post-${fedit}|#${fedit}]]`;
                        } else if(document.querySelector('#f-reply-cb').checked) {
                            const replyToUser = document.querySelector('[id="'+document.querySelector('#f-reply').value+'-user"]').innerText;
                            summary = `${msg.postsummary} [[${mw.config.get('wgPageName')}#post-${lp}|#${lp}]] ${msg.replysummary} [[User:${replyToUser}|${replyToUser}]]`;
                        } else {
                            summary = `${msg.postsummary} [[${mw.config.get('wgPageName')}#post-${lp}|#${lp}]]`;
                        }
                        new mw.Api().postWithToken('csrf', {
                            action: 'edit',
                            title: mw.config.get('wgPageName'),
                            [fedit ? 'text' : 'appendtext']: fedit ? source.replace(new RegExp(`\\{\\{post\\|(.*?)\\|${fedit}\\|(.*?)\\|4=((.|\n)*?)}}`), `{{post|$1|${fedit}|$2|4=${document.querySelector('#wpTextbox1').value}}}`) :`\n{{post|${mw.forum.username}|${lp}|{{subst:#timel:Y/m/d H:i}}|4=${document.querySelector('#wpTextbox1').value}${document.querySelector('#f-reply-cb').checked ? '|re='+document.querySelector('#f-reply').value : ''}}}`,
                            summary: summary,
                            format: 'json'
                        }).done(() => {
                            if(fedit){
                                location.href = `${mw.util.getUrl()}#post-${fedit}`;
                            }else{
                                location.reload();
                            }
                        }).fail(() => {
                            mw.notify(msg.posterror);
                            document.querySelector('#f-post').disabled = false;
                        });
                    });

                    if(fedit){
                        location.hash = '#f-form';
                        document.querySelector('#wpTextbox1').focus();
                        (async()=>{(await (await fetch(mw.config.get('wgScript') + `?title=${mw.config.get('wgPageName')}&action=raw`)).text()).replace(new RegExp(`\\{\\{post\\|(.*?)\\|${fedit}\\|(.*?)\\|4=((.|\n)*?)}}`), (m, u, d, t)=>{document.querySelector('#wpTextbox1').value = t;})})();
                    }

                    if (!isAnon) {
                        document.querySelectorAll('.f-u-' + mw.config.get('wgUserName')).forEach(el => {
                            const numText = el.querySelector('.f-number-text').innerText;
                            const postId = numText.replace('#', '');
                            el.innerHTML += msg.editlink.replace('%241', postId);
                        });
                    }
                    if (isAdmin) {
                        document.querySelectorAll('.f-header').forEach(header => {
                            const delSpan = document.createElement('span');
                            delSpan.style.marginLeft = '10px';
                            const delLink = document.createElement('a');
                            delLink.textContent = '削除';
                            delLink.style.color = '#d33';
                            delLink.style.cursor = 'pointer';
                            delLink.onclick = function() {
                                const numText = header.querySelector('.f-number-text').innerText;
                                const postId = numText.replace('#', '');
                                func.deletePost(postId);
                            };
                            delSpan.appendChild(delLink);
                            header.appendChild(delSpan);
                        });
                    }
                }
            } // end startForum
        });
    };
    
    // 確実に読み込まれるように少し待つか、即時実行
    if (window.mw && window.mw.loader) {
        checkConfig();
    } else {
        // 万が一ロード前ならイベントを待つ
        window.addEventListener('load', checkConfig);
    }
})();
// </nowiki>
当サイトでは、アクセス解析や利便性向上のためにCookieを使用しています。当サイトを継続して利用することで、Cookieの使用に同意したものとみなされます。