コンテンツにスキップ

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

削除された内容 追加された内容
v3.3: WP:VIPのリンクを貼る際にVIP名を確認するのが面倒なのでAPIから自動取得してコピーできる機能を追加
v3.3.1: WP:VIPの名称コピー機能において、値が初期値の場合コピーされないように修正
2行目: 2行目:
* AN Reporter (ANR)
* AN Reporter (ANR)
* Author: Dragoniez
* Author: Dragoniez
* Version: 3.3
* Version: 3.3.1
*************************************/
*************************************/
//<nowiki>
//<nowiki>
1,270行目: 1,270行目:
// Copy VIP name
// Copy VIP name
$(document).on('click', '.anr-viplist-btn', function() {
$(document).on('click', '.anr-viplist-btn', function() {
let vipWikitext = '[[WP:VIP#' + $('#anr-viplist-select').find('option').filter(':selected').text().trim() + ']]';
let vipSelectVal = $('#anr-viplist-select').find('option').filter(':selected').text().trim();
copyToClipboard(vipWikitext);
let vipLink = '[[WP:VIP#' + vipSelectVal + ']]';
if (vipSelectVal !== '[[Wikipedia:進行中の荒らし行為]]を検索') {
copyToClipboard(vipLink);
} else {
alert('ドロップダウンの値が選択されていません');
}
});
});



2022年2月19日 (土) 07:34時点における版

/*************************************  
 *  AN Reporter (ANR)
 *  Author: Dragoniez
 *  Version: 3.3.1
 *************************************/
//<nowiki>

// Run the script only after Select2, jQuery UI, and the DOM are loaded
$.when(
    $.getScript('https://cdn.jsdelivr.net/npm/[email protected]/dist/js/select2.min.js'),
    mw.loader.using('jquery.ui'),
    $.ready
).then(function(){

    // Load CSS source for Select2
    $('head').append('<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/select2.min.css">');

    // 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') {

        // Define the position of the '報告' button
        let btnPosition;
        let fSize; // Font size of the elements on the dialog
        let s2fSize; // Font size of Select2 dropdown options
        switch(mw.config.get('skin')) {
            case 'vector':
            case 'vector-2022':
                btnPosition = 'p-views';
                fSize = '80%';
                s2fSize = '0.85em';
                break;
            case 'minerva':
                btnPosition = 'p-personal';
                fSize = '80%';
                s2fSize = '0.85em';
                break;
            case 'monobook':
                btnPosition = 'p-cactions';
                fSize = '110%';
                s2fSize = '1em';
                break;
            case 'timeless':
                btnPosition = 'p-cactions';
                fSize = '90%';
                s2fSize = '0.9em';
                break;
            default:
                btnPosition = 'p-cactions';
                fSize = '80%';
                s2fSize = '0.85em';
        }

        // Default CSS for Select2
        $('head').append(
            '<style>' +
            '   .select2-selection__rendered {' +
            '       padding: 1px 2px;' +
            '       font-size: 1em;' +
            '       line-height: normal !important;' +
            '    }' +
            '   .select2-results__option, .select2-results__group {' +
            '       padding: 1px 8px;' +
            `       font-size: ${s2fSize};` +
            '       margin: 0;' +
            '    }' +
            '</style>'
        );

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

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

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

            // CSS
            let labelCSS = 'display: inline-block; width: 8ch;';
            let marginCSS = 'margin: 1em 0;';
            let siCSS = 'border: 1px solid #d3d3d3; border-radius: 1%; background: white; padding: 2px 4px;'; // For select and input
            let btnCSS = 'color: black; font-weight: normal; border: 1px solid #d3d3d3; background: white; padding: 0.2em 0.5em; border-radius: 10%;'; // For button
            
            // Page names
            const ANI = 'Wikipedia:管理者伝言板/投稿ブロック';
            const ANS = 'Wikipedia:管理者伝言板/投稿ブロック/ソックパペット';
            const AN3RR = 'Wikipedia:管理者伝言板/3RR';
            const Iccic = 'Wikipedia:進行中の荒らし行為/長期/Iccic/投稿ブロック依頼';
            const ISECHIKA = 'Wikipedia:管理者伝言板/投稿ブロック/いせちか';
            const KAGE = 'Wikipedia:管理者伝言板/投稿ブロック/影武者';
            const KIYOSHIMA = 'Wikipedia:管理者伝言板/投稿ブロック/清島達郎';
            const SHINJU = 'Wikipedia:管理者伝言板/投稿ブロック/真珠王子';

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

            // WP:VIP list (for copy to clipboard)
            let VIPList = async function() {
                return new Promise(function(resolve, reject) {
                    new mw.Api().get({
                        'action': 'parse',
                        'page': 'Wikipedia:進行中の荒らし行為',
                        'prop': 'sections',
                        'formatversion': 2
                    }).then(function(res){

                        if (res && res.parse) {

                            // Get VIP's names
                            let sectionsParsed = res.parse.sections;
                            let excludeList = ['記述について', '急を要する二段階', '配列', 'ブロック等の手段', 'このページに利用者名を加える',
                                               '注意と選択', '警告の方法', '未登録(匿名・IP)ユーザーの場合', '登録済み(ログイン)ユーザーの場合',
                                               '警告中', '関連項目'];
                            let sectionList = [];
                            for (let i = 0; i < sectionsParsed.length; i++) {
                                if (!isInArray(sectionsParsed[i].line, excludeList) && sectionsParsed[i].level === '3') {
                                    sectionList.push('<option>' + sectionsParsed[i].line + '</option>');
                                }
                            }

                            // Show the VIP list on the dialog
                            let VIPListHtml =
                            '<div class="anr-viplist-div" style="width: 100%;">' +
                            '   <select id="anr-viplist-select">' +
                            '       <option selected disabled hidden>[[Wikipedia:進行中の荒らし行為]]を検索</option>' +
                                    sectionList.join() +
                            '   </select>' +
                            `   <button type="button" class="anr-viplist-btn" style="${btnCSS}">コピー</button>` +
                            '</div>';
                            $('.anr-reason-div').before(VIPListHtml);
                            $('#anr-viplist-select').select2();
                            resolve();

                        } else {
                            resolve();
                        }

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

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

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

            // Script ad for edit summary
            const scriptAd = ' ([[User:Dragoniez/AN Reporter|AN Reporter]])';
            //const scriptAd = ' ([[User:Dragoniez/AN Reporter|AN Reporter Experimental]])'; // For debugging

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

                    // CSS
                    dialogCSS();

                    // Get the name of the user to report if it can be retrieved from the page
                    let username = mw.config.get('wgRelevantUserName'); // Note: This does not pick up IP ranges

                    // Workaround to pick up IP ranges
                    if (username === null && mw.config.get('wgCanonicalSpecialPageName') === 'Contributions') {
                        let relUsername = $('#firstHeading').text().replace('の投稿記録', '');
                        if (mw.util.isIPAddress(relUsername, true)) {
                            username = relUsername;
                        }
                    }
                    
                    // If wgRelevantUserName returns null or the name of the current user, set the variable as a null string
                    if (!username || username === mw.config.get('wgUserName')) {
                        username = '';
                    }

                    // Show the user name on the dialog
                    $('#anr-user1-input').val(username);
                    typeDropdown('#anr-user1-input', '#anr-user1-select');
                    if (username === mw.config.get('wgRelevantUserName') && !mw.util.isIPAddress(username, true)) {
                        $('#anr-user1-checkbox-div').css('display', 'block'); // Show 'hide username' if the name obtained is a user's
                    }

                    // Show VIP list
                    VIPList();

                },
                'buttons': [{

                    // 'Preview' button
                    'text': 'プレビュー',
                    'click': function() {

                        // Check if the necessary fields are filled and get edit information
                        let ep = editPrep();
                        if (ep.rqFieldsEmpty) {
                            alert('必須項目が入力・選択されていません'); // Show error and cancel the preview
                            return;
                        }
                        
                        // Preview dialog contour
                        const ANSMisc = `<a href="${mw.util.getUrl('WP:AN/S#OTH')}" target="_blank">WP:AN/S#その他</a>`;
                        let previewDiv =
                        '<div class="anr-preview-dialog" title="AN Reporter Preview">' +
                        '   <div id="anr-preview-header" style="padding: 0.5em;">' +
                        '       <p id="anr-preview-loading" style="font-weight: bold">' +
                        '           プレビューを読み込み中...' +
                        '       </p>' +
                        '       <p id="anr-preview-warning" style="display: none;">' +
                        '           注意1: このプレビュー上のリンクは全て新しいタブで開かれます' +
                        '              <br>' +
                        `           注意2: 報告先が ${ANSMisc} の場合、このプレビューには表示されませんが「他X月X日」のヘッダーは必要に応じて自動挿入されます` +
                        '       </p>' +
                        '   </div>' +
                        '   <div id="anr-preview-body" style="display: none; font-size: 1.1em; padding-top: 1em; border-top: 1px solid silver;">' +
                        '       <div id="anr-preview-text" style="border: 1px solid silver; padding: 0.2em 0.5em; background: white;">' +
                        //          previewHtml
                        '       </div>' +
                        '       <div id="anr-preview-summary" style="margin-top: 0.8em; border: 1px solid silver; padding: 0.2em 0.5em; background: white;">' +
                        //          summaryHtml
                        '       </div>' +
                        '   </div>' +
                        '</div>';

                        // Show preview dialog
                        $('body').append(previewDiv);
                        $('.anr-preview-dialog').dialog({
                            'height': 'auto',
                            'width': $('#content').width() * 0.8,
                            'modal': true,
                            'buttons': [{
                                'text': '閉じる',
                                'click': function(){
                                    $(this).dialog('close');
                                }
                            }],
                            'open': function(){

                                // CSS
                                dialogCSS();

                                // Convert text on the dialog to html
                                new mw.Api().get({
                                    'action': 'parse',
                                    'text': ep.textToSubmit,
                                    'summary': ep.editSummary,
                                    'contentmodel': 'wikitext',
                                    'prop': 'text',
                                    'disableeditsection': true,
                                    'formatversion': 2
                                }).then(function(res){

                                    if (res && res.parse) {

                                        let previewHtml = res.parse.text;
                                        let rep = new RegExp('API', 'g');
                                        let summaryHtml = res.parse.parsedsummary.replace(rep, ep.pageToEdit);
                                        $('#anr-preview-text').append(previewHtml);
                                        $('#anr-preview-summary').append(summaryHtml);
                                        $('.autocomment a').css('color', 'gray'); // Change color of section spec in summary
                                        $('.anr-preview-dialog a').attr('target', '_blank'); // Open all links on a new tab
                                        $('#anr-preview-body').css('display', 'block');
                                        $('#anr-preview-loading').css('display', 'none');
                                        $('#anr-preview-warning').css('display', 'inline');

                                    } else {
                                        $('#anr-preview-loading').text('プレビューの読み込みに失敗しました').css('color', 'red');
                                        setTimeout(function(){
                                            $('.anr-preview-dialog').dialog('close');
                                        }, 3000);
                                    }
                                    
                                });
                            }
                        });
                        
                    }}, {

                    // 報告 button
                    'text': '報告',
                    'click': function() {

                        // Check if the necessary fields are filled and get edit information
                        let ep = editPrep();
                        if (ep.rqFieldsEmpty) {
                            alert('必須項目が入力・選択されていません'); // Show error and cancel the edit
                            return;
                        }

                        // Variables for edit
                        let $dialog = $(this);
                        let width = $dialog.width();
                        let msgEditing = 
                        '   <div class="anr-editing">' +
                        '       <p>最新版を取得しています</p>' +
                        '   </div>';
                        let msgDone = ''; // Message to show when edit attempt is done
                        let wikiPagename = ep.pageToEdit + '#' + ep.sectionToEdit; // The wiki pagename for link
                        let editFailed = false; // Boolean value to pass to function when edit attempt is done

                        // Change dialog content
                        $dialog.find('form').css('display', 'none'); // Hide dialog content
                        $dialog.dialog('option', 'width', width);
                        $dialog.dialog({'buttons': [] }); // Hide the button
                        $dialog.append(msgEditing);

                        // For debugging
                        //ep.pageToEdit = '利用者:Dragoniez/test';
                        //ep.editSummary = 'Test edit via mediawiki API';

                        // Function to get the latest revision of the administrator's noticeboard
                        async function getLastestRevision() {

                            return new Promise(function(resolve, reject) {
                        
                                new mw.Api().get({
                                    'action': 'query',
                                    'titles': ep.pageToEdit,
                                    'prop': 'revisions',
                                    'curtimestamp': true,
                                    'formatversion': 2
                                }).done(function(res){

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

                                            // Get the details of the latest revision
                                            let baseTS = res.query.pages[0].revisions[0].timestamp;
                                            let curTS = res.curtimestamp;

                                            // Update message
                                            msgEditing =
                                            '   <p style="color: MediumSeaGreen">取得に成功しました</p>' +
                                            '   <p>セクション番号を取得しています</p>';
                                            $('.anr-editing').append(msgEditing);

                                            resolve({
                                                'baseTS': baseTS,
                                                'curTS': curTS
                                            });

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

                                            msgDone = 
                                            '<p style="color: MediumVioletRed">エラー: 報告先のページが存在しません</p>' + manualEdit();
                                            $('.anr-editing').append(msgDone);
                                            
                                            $dialog.dialog('option', 'width', width);
                                            editFailed = true;
                                            editDone($dialog, editFailed, wikiPagename);
                                            resolve(undefined);

                                        }

                                    } else { // If revision retrieval fails
                                        queryFailed();
                                        resolve(undefined);
                                    }

                                });
                            });
                        }

                        // Function to get the section number from the section title 
                        async function getSectionNumber() {

                            return new Promise(function(resolve, reject) {
                        
                                let sectionsAPI = {};                                
                                new mw.Api().get({
                                    'action': 'parse',
                                    'page': ep.pageToEdit,
                                    'formatversion': 2
                                }).done(function(res){

                                    if (res && res.parse && res.parse.sections) { // If the section list is successfully retrieved

                                        // Get section numbers from section titles
                                        for (let i = 0; i < Object.keys(res.parse.sections).length; i++) {
                                            sectionsAPI[res.parse.sections[i].line] = res.parse.sections[i].index;
                                        }                                            
                                        
                                        let sectionNum = sectionsAPI[ep.sectionToEdit];
                                        if (sectionNum === undefined) { // If section title in the dropdown is not found

                                            // Show the details of the error
                                            msgDone = 
                                            '<p style="color: MediumVioletRed">取得に失敗しました</p>' +
                                            '<p>指定されたセクションが見つかりませんでした</p>' +
                                            '<br>' +
                                            '<p>考えられる原因:</p>' + 
                                            `<p>1. 編集先のページの節構成が変更された</p>` +
                                            `<p>2. 通信に失敗した</p>` +
                                            `<p>3. スクリプトのバグ</p>` +
                                            manualEdit();
                                            $('.anr-editing').append(msgDone);

                                            $dialog.dialog('option', 'width', width);
                                            editFailed = true;
                                            editDone($dialog, editFailed, wikiPagename);
                                            resolve(undefined);

                                        } else { // If section title in the dropdown is found

                                            // Update message
                                            msgEditing =
                                            '   <p style="color: MediumSeaGreen">取得に成功しました</p>' +
                                            '   <p>最新版のテキストを取得しています</p>';
                                            $('.anr-editing').append(msgEditing);

                                            resolve(sectionNum);

                                        }

                                    } else { // If the section list retrieval fails
                                        queryFailed();
                                        resolve(undefined);
                                    }
                                });
                            });
                        }

                        // Function to get the text to replace with the current text in the section
                        async function getTextToReplace(sectionNum) {

                            return new Promise(function(resolve, reject) {
                        
                                // Get the text of the revision
                                new mw.Api().get({
                                    'action': 'parse',
                                    'page': ep.pageToEdit,
                                    'section': sectionNum,
                                    'prop': 'wikitext',
                                    'formatversion': 2
                                }).done(function(res){

                                    if (res && res.parse) {

                                        // Update message
                                        msgEditing =
                                        '   <p style="color: MediumSeaGreen">取得に成功しました</p>' +
                                        '   <p>報告を試みています</p>';
                                        $('.anr-editing').append(msgEditing);

                                        // Get the whole text to append
                                        let wikitextObtained = res.parse.wikitext;
                                        let wholeTextToSubmit;
                                        const delimiter = '<!-- ◆';
                                        const miscHeader = `{{bgcolor|#eee|{{Visible anchor|他${today()}}}|div}}`;

                                        if (ep.reportToANS) { // If the target is WP:AN/S

                                            // Add div if the target section is 'その他' but lacks div for the current date
                                            if (ep.sectionToEdit === 'その他' && wikitextObtained.indexOf(miscHeader) === -1) {
                                                ep.textToSubmit = '; ' + miscHeader + '\n\n' + ep.textToSubmit;
                                            }

                                            // Insert text into the right place
                                            let wikitextSplit = wikitextObtained.split(delimiter);
                                            if (wikitextSplit.length === 3) {

                                                wholeTextToSubmit = 
                                                wikitextSplit[0] + delimiter + trimA(wikitextSplit[1]) + '\n\n' +
                                                ep.textToSubmit + '\n\n' + delimiter + wikitextSplit[2];
                                                resolve(wholeTextToSubmit);

                                            } else { // If section structure has been changed

                                                // Show error and quit the procedure
                                                msgDone = 
                                                '<p style="color: MediumVioletRed">報告に失敗しました</p>' +
                                                '<p>セクション構造が改変されているため、' +
                                                `<a href="${mw.util.getUrl('User talk:Dragoniez/AN Reporter')}" target="_blank">開発者</a>` +
                                                'に連絡をお願いします</p>' +
                                                manualEdit();
                                                $('.anr-editing').append(msgDone);

                                                $dialog.dialog('option', 'width', width);
                                                editFailed = true;
                                                editDone($dialog, editFailed, wikiPagename);
                                                resolve(undefined);

                                            }

                                        } else { // If the target is WP:AN/I or WP:AN/3RR

                                            wholeTextToSubmit = trimA(wikitextObtained) + '\n\n' + ep.textToSubmit;
                                            resolve(wholeTextToSubmit);

                                        }

                                    } else { // If wikitext retrieval fails
                                        queryFailed();
                                        resolve(undefined);
                                    }

                                });
                            });
                        }

                        // Function to edit the page
                        function edit(sectionNum, text, baseTS, curTS) {                                                
                            
                            $.ajax({
                                'url': mw.util.wikiScript('api'),
                                'data': {
                                    'format': 'json',
                                    'action': 'edit',
                                    'title': ep.pageToEdit,
                                    'section': sectionNum,
                                    'text': text,
                                    'summary': ep.editSummary,
                                    'basetimestamp': baseTS,
                                    'starttimestamp': curTS,                                        
                                    'token': mw.user.tokens.get('csrfToken'),
                                    'curtimestamp': true
                                },
                                'dataType': 'json',
                                'type': 'POST',
                                success: function(res) {
                                    
                                    // If the edit was successful 
                                    if (res && res.edit && res.edit.result == 'Success') {
                                        
                                        setTimeout(async function(){
                                            let diffNum = await getDiffNum(res.curtimestamp); // Get diff number
                                            
                                            // Show message
                                            $('.anr-editing').append(`<p style="color: MediumSeaGreen">報告が完了しました</p>`);
                                            $dialog.dialog('option', 'width', width);
                                            editDone($dialog, editFailed, wikiPagename, diffNum);
                                            return;

                                        }, 0);

                                    // If the edit failed
                                    } else if (res && res.error) { 
                                        
                                        // Show the details of the error
                                        msgDone = 
                                        '<p style="color: MediumVioletRed">報告に失敗しました</p>' +
                                        '<br>' +
                                        '<p>詳細:</p>' + 
                                        `<p>${res.error.info}</p>` +
                                        manualEdit();
                                        $('.anr-editing').append(msgDone);

                                        $dialog.dialog('option', 'width', width);
                                        editFailed = true;
                                        editDone($dialog, editFailed, wikiPagename);
                                        return;

                                    // If unknown error occurs
                                    } else {

                                        // Show message
                                        msgDone = 
                                        '<p style="color: MediumVioletRed">不明なエラーが発生しました</p>' + manualEdit();
                                        $('.anr-editing').append(msgDone);

                                        $dialog.dialog('option', 'width', width);
                                        editFailed = true;
                                        editDone($dialog, editFailed, wikiPagename);
                                        return;
                                        
                                    }

                                }
                            });
                            
                        }

                        // Function to execute report
                        async function reportUser() {

                            let rev = await getLastestRevision();
                            let baseTS, curTS;
                            if (rev === undefined) {
                                return;
                            } else {
                                baseTS = rev.baseTS;
                                curTS = rev.curTS;
                            }

                            let sectionNum = await getSectionNumber();
                            if (sectionNum === undefined) {
                                return;
                            }

                            let text = await getTextToReplace(sectionNum);
                            if (text === undefined) {
                                return;
                            }

                            edit(sectionNum, text, baseTS, curTS);

                        }                        

                        // Function to show message when edit attempt is done
                        function queryFailed() {
                            msgDone = 
                            '<p style="color: MediumVioletRed">取得に失敗しました</p>' + manualEdit();
                            $('.anr-editing').append(msgDone);
                            
                            $dialog.dialog('option', 'width', width);
                            editFailed = true;
                            editDone($dialog, editFailed, wikiPagename);
                        }

                        // Function to generate the html for the manul edit helper tab
                        function manualEdit() {
                            let meHtml =
                            '<br>' +
                            '<p>手動編集用:</p>' +
                            `<textarea disabled rows="4" style="width: 100%">${ep.textToSubmit}</textarea>` +
                            '<br>' +
                            '<p>要約:</p>' + 
                            `<textarea disabled rows="2" style="width: 100%">${ep.editSummary.replace(scriptAd, '')}</textarea>`;
                            return meHtml;                            
                        }

                        // Report the user(s)
                        reportUser();

                    }
                }]
            });

            // ********** EVENT HANDLERS AND FUNCTIONS **********

            function editPrep() {
                let rqFieldsEmpty = false;

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

                // Get the name of the section to edit
                let pageToEdit =  $('#anr-target-options').children('option').filter(':selected').text();
                let sectionToEdit = '選択してください';
                let ANSOptSelected = $('#anr-section-options-s').find('option').filter(':selected').text();
                let reportToANS = false;

                if (pageToEdit === ANI) { // If WP:AN/I is selected as the target page to edit

                    sectionToEdit = $('#anr-section-options-i').children('option').filter(':selected').text();

                } else if (pageToEdit === ANS) { // If WP:AN/S is selected as the target page to edit

                    reportToANS = true;
                    if (ANSOptSelected === 'Iccic系 (Iccic)') {
                        pageToEdit = Iccic;
                        sectionToEdit = '新規依頼';
                    } else if (ANSOptSelected === 'いせちか系 (ISECHIKA)') {
                        pageToEdit = ISECHIKA;
                        sectionToEdit = '新規依頼';
                    } else if (ANSOptSelected === '影武者系(KAGE)') {
                        pageToEdit = KAGE;
                        sectionToEdit = '新規依頼';
                    } else if (ANSOptSelected === '清島達郎系 (清島、KIYOSHIMA)') {
                        pageToEdit = KIYOSHIMA;
                        sectionToEdit = '新規依頼';
                    } else if (ANSOptSelected === '真珠王子系(SHINJU)') {
                        pageToEdit = SHINJU;
                        sectionToEdit = '新規依頼';
                    } else {
                        sectionToEdit = ANSOptSelected;
                    }

                } else if (pageToEdit === AN3RR) { // If WP:AN/3RR is selected as the target page to edit

                    sectionToEdit = '3RR';

                }

                if ( // Check if necessary fields are filled
                    pageToEdit === '選択してください' || // The page dropdown's remained 選択してください 
                    sectionToEdit === '選択してください' || // The section dropdown's remained 選択してください
                    trimA($('#anr-reason-text').val()) === '' || // No reason is given
                    users.length === 0 // No username is given
                ) {
                    rqFieldsEmpty = true;
                }
                
                // UserAN template and the reason of the report
                const UserAN = '{{UserAN|t=TYPE|USER}}';
                let reason = trimA($('#anr-reason-text').val());
                if (reason.substring(reason.length - 4) !== '~~~~') { // If reason doesn't contain signature, add one
                    reason = reason + '--~~~~';
                }

                // Get edit summary
                const editSummarySection = '/*' + sectionToEdit + '*/';

                let editSummary = 
                trimA($('#anr-summary-text').val()) === '' ? 
                editSummarySection + genEditSummary().replace(' - ', '') + scriptAd: 
                editSummarySection + trimA($('#anr-summary-text').val()) + scriptAd;

                // Get text to add to the page
                let 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;
                }

                // Return values
                return {
                    'pageToEdit': pageToEdit,
                    'sectionToEdit': sectionToEdit,
                    'reportToANS': reportToANS,
                    'rqFieldsEmpty': rqFieldsEmpty,
                    'editSummary': editSummary,
                    'textToSubmit': textToSubmit
                }
            }

            // Function to change the CSS of the dialog
            function dialogCSS() {
                $('.ui-dialog-content, .ui-dialog-buttonpane, .ui-corner-all, .ui-draggable, .ui-resizable').css('background', '#FFF0E4');                
                $('.ui-button').css({
                    'color': 'black',
                    'background-color': 'white'
                });
                $('.ui-dialog-titlebar, .ui-dialog-titlebar-close').attr('style', 'background: #FEC493 !important;');
                $('.ui-dialog').css('font-size', fSize);
            }

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

                // Get the page name without a section specifier
                let tarPage = wikiPagename.split('#')[0];
                
                // Button to jump to diff
                let btns = [];
                let diffBtn, destBtn, closeBtn;
                if (diffNum !== undefined) { // Show the button only if diff number is available
                    diffBtn = {
                        'text': '差分',
                        'click': function(){
                            window.open(mw.util.getUrl('特別:差分/' + diffNum), '_blank');
                        }                        
                    };
                    btns.push(diffBtn);
                }

                // Button to jump to the report page
                if (editFailed || mw.config.get('wgPageName') !== tarPage) { // Show the button if the edit failed or if the user is NOT on the page
                    destBtn = {
                        'text': '報告先',
                        'click': function(){
                            window.open(mw.util.getUrl(wikiPagename), '_blank');
                        }
                    };
                    btns.push(destBtn);
                }

                // Button to close the dialog (always shown)
                closeBtn = {
                    'text': '閉じる',
                    'click': function(){
                        $(this).dialog('close');
                        let curPage = mw.config.get('wgPageName');
                        if (
                            curPage === ANI ||
                            curPage === ANS ||
                            curPage === AN3RR ||
                            curPage === Iccic ||
                            curPage === ISECHIKA ||
                            curPage === KAGE ||
                            curPage === KIYOSHIMA ||
                            curPage === SHINJU ||
                            curPage === '利用者:Dragoniez/test' ||
                            curPage === '利用者:Dragoniez/test2'
                        ) {
                            location.reload(true);
                        }
                    }
                };
                btns.push(closeBtn);

                // Show the button(s) on the dialog
                $dialog.dialog({ 
                    'position': { my: 'center', at: 'top+20%', of: window },
                    'buttons': btns
                });
                
            }

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

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

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

                        type = $(`#anr-user${i}-select`).children('option').filter(':selected').text(); // UserAN type specified in the dropdown
                        reportee = trimA($(`#anr-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
                let textToShow = '';
                if (arrOfContribs.length === 0) {
                    // Do nothing
                } else if (arrOfContribs.length === 1) {
                    textToShow += '+' + arrOfContribs[0] + ' - ';
                } else {
                    textToShow += '+' + arrOfContribs.join(', ') + ' - ';
                }

                return textToShow;

            }

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

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

            // Add section name to the '報告先' link when section is specified
            $(document).on('change', '#anr-section-options-i, #anr-section-options-s', function(){
                
                let tarSection = '';
                let tarPage = '';
                if ($(this).attr('id') === 'anr-section-options-i') {
                    tarPage = ANI;
                } else if ($(this).attr('id') === 'anr-section-options-s') {
                    tarPage = ANS;
                }
                if ($(this).find('option').filter(':selected').text() !== '選択してください') {
                    tarSection = '#' + $(this).find('option').filter(':selected').text();
                    $('#anr-target-a').attr('href', mw.util.getUrl(tarPage + tarSection));
                }

            });

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

                let selectID = '#' + e.target.id;
                let valSelected = $(selectID).children('option').filter(':selected').text(); // Selected type
                let checkboxDivID = selectID.replace('select', 'checkbox-div'); // ID of div containing <input type="checkbox"> tag
                let checkboxID = selectID.replace('select', 'checkbox'); // ID of checkbox
                let aDivID = selectID.replace('select', 'a-div'); // ID of div containing <a> tag
                let aID = selectID.replace('select', 'a'); // ID of a
                let 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', mw.util.getUrl('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', mw.util.getUrl('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', '.anr-user-div :text', function(e){

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

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

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

                if ($(checkboxID).is(':checked')) { // if the checkbox is checked
    
                    // Function to update type dropdown
                    let updateDropdown = function(logid) {
                        $(selectID).children('.anr-opt-UNL').prop('hidden', true);
                        $(selectID).children('.anr-opt-User2').prop('hidden', true);
                        $(selectID).children('.anr-opt-IP2').prop('hidden', true);
                        $(selectID).children('.anr-opt-logid').prop('hidden', false).prop('selected', true);
                        $(selectID).children('.anr-opt-diff').prop('hidden', false);
                        $(selectID).children('.anr-opt-none').prop('hidden', false);
                        $(aDivID).css('display', 'block');
                        $(aID).attr('href', mw.util.getUrl('Special:redirect/logid/' + logid)).text('特別:転送/logid/' + logid);
                    }
    
                    let logid;
                    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', false);
                                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
    
                    if (objLogid[inputVal] !== undefined) {
    
                        $(inputID).val(objLogid[inputVal]); // if the object knows the username for the logid, retrieve the data
                        $(selectID).children('.anr-opt-UNL').prop('hidden', false).prop('selected', true);
                        $(selectID).children('.anr-opt-User2').prop('hidden', false);
                        $(selectID).children('.anr-opt-IP2').prop('hidden', true);
                        $(selectID).children('.anr-opt-logid').prop('hidden', true);
                        $(selectID).children('.anr-opt-diff').prop('hidden', true);
                        $(selectID).children('.anr-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
            let userCnt = 1;
            $('.anr-addBtn').click(function(){

                userCnt++;                    
                let replaceTar = new RegExp(`${userCnt-1}-`, 'g');
                userHtml = userHtml.replace(replaceTar, `${userCnt}-`); // 1 → 2, 2 → 3 and so forth
                $('.anr-btn-div').before(userHtml);
                $(`#anr-user${userCnt}-input-div`).css('margin-top', '0.2em');

            });

            // When buttons are moused on and off
            $(document).on({
                'mouseover': function(e) {
                    e.target.style.borderColor = '#999999';
                },
                'mouseleave': function(e) {
                    e.target.style.borderColor = '#d3d3d3';
                }
            }, '.anr-modal-dialog form button');

            // Copy VIP name
            $(document).on('click', '.anr-viplist-btn', function() {
                let vipSelectVal = $('#anr-viplist-select').find('option').filter(':selected').text().trim();
                let vipLink = '[[WP:VIP#' + vipSelectVal + ']]';
                if (vipSelectVal !== '[[Wikipedia:進行中の荒らし行為]]を検索') {
                    copyToClipboard(vipLink);
                } else {
                    alert('ドロップダウンの値が選択されていません');
                }
            });

            // Function to copy a string to the clipboard
            function copyToClipboard(str) {
                try { // This should handle browser incompatibility
                    let $temp = $('<input>');
                    $('body').append($temp); // Create a temporarily hidden text field
                    $temp.val(str).select(); // Copy the text string into the field and select the text
                    document.execCommand('copy'); // Copy it to the clipboard
                    $temp.remove(); // Remove the text field
                }
                catch (err) {
                    alert('ご利用のブラウザはこの機能に対応していません');
                    console.log(err);
                }
            }

            // When the summary checkbox is (un)checked
            $(document).on('change', '#anr-summary-checkbox', function(){

                let $textarea = $('#anr-summary-text');

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

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

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

        });

        // Function to get the last day of the month
        let 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(){
            let d = new Date();
            let 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 get today's date
        function today() {
            let d = new Date();
            return d.getMonth()+1 + '月' + d.getDate() + '日';
        }

        // 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)
        let 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;
            }
            
            let tarVal = trimA($(inputID).val()); // The value typed into the input
            let checkboxDivID = selectID.replace('select', 'checkbox-div');
            let checkboxID = selectID.replace('select', 'checkbox');
            let aDivID = selectID.replace('select', 'a-div');

            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('.anr-opt-UNL').prop('hidden', true);
                        $(selectID).children('.anr-opt-User2').prop('hidden', true);
                        $(selectID).children('.anr-opt-IP2').prop('hidden', false).prop('selected', true);
                        $(selectID).children('.anr-opt-logid').prop('hidden', true);
                        $(selectID).children('.anr-opt-diff').prop('hidden', true);
                        $(selectID).children('.anr-opt-none').prop('hidden', false);
                        $(checkboxDivID).css('display', 'none'); // hide 'hide username' checkbox
                        $(checkboxID).prop('checked', false); // uncheck the checkbox
                        $(aDivID).css('display', 'none');

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

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

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

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

                    }
                    
                }
                
            }, 350);
        }

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

            let checkboxDivID = selectID.replace('select', 'checkbox-div'); // ID of div containing <input type="checkbox"> tag
            let aDivID = selectID.replace('select', 'a-div'); // ID of div containing <a> tag
        
            if (trimA($(inputID).val()) === '') { 
                $(selectID).children('.anr-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',
                    leuser: username,
                    ledir: 'newer',
                    lelimit: 1,
                    formatversion: 2
                }).done(function(res){
                    if (res.query.logevents.length === 0) { // If empty array is returned (=if no logevent exists)
                        resolve(undefined);
                    } else {
                        resolve(res.query.logevents[0].logid);
                    }                    
                });
            });
        }

        // Function to get diff number and its URL after edit
        async function getDiffNum(curtimestamp) {
            return new Promise(function(resolve, reject) {
                new mw.Api().get({
                    action: 'query',
                    list: 'allrevisions',
                    arvprop: 'ids|timestamp',
                    arvlimit: 5,
                    arvuser: mw.config.get('wgUserName'),
                    arvstart: curtimestamp,
                    arvdir: 'newer',
                    formatversion: 2
                }).done(function(res){
                    if (res && res.query && res.query.allrevisions) {
                        let revArr = res.query.allrevisions;
                        for (let i = 0; i < revArr.length; i++) {
                            if (revArr[i].revisions[0].timestamp === curtimestamp) {
                                resolve(revArr[i].revisions[0].revid);
                            }
                            if (i === revArr.length -1) {
                                resolve(undefined);
                            }
                        }
                    }
                });
            });
        }

        // Function to trim U+200E space
        function trimA(str) {
            return str.trim().replace(/\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>