コンテンツにスキップ

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

「利用者:Dragoniez/Template Paster.js」の版間の差分

削除された内容 追加された内容
Version: 1.0 beta
(相違点なし)

2022年1月24日 (月) 11:50時点における版

/*************************************  
 *  Template Paster (TP)
 *  Author: Dragoniez
 *  Version: 1.0 beta
 *************************************/
//<nowiki>

$(function(){

    // Don't run the scipt if the page is a special page or an edit page
	if (mw.config.get('wgNamespaceNumber') != -1 && mw.config.get("wgAction") != 'edit') {

		// Load external scripts
		mw.loader.load('jquery.ui.dialog');

        // Add TP tab
        $(mw.util.addPortletLink('p-cactions', '#', 'TP', 'tp', 'Template Paster'))
        .click(function(e){

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

            // ----- CREATE DIALOG -----

            //CSS for elements in dialog
            var labelCSS = 'display: inline-block; width: 8ch;';
            var missingCSS = 'display: inline-block; font-size: larger; width: 1.5ch; font-weight: bold; margin: 0; line-height: normal;';
            var marginCSS = 'margin: 1em 0';

            //Asterik mark in red for required entries
            var rq = '<span style="color:red; font-size: larger">*</span>';

            //Create the html contour (CO parts are added later in the code)
            var modalHtml =
            //  <div class="tp-modal-dialog" title="Template Paster">
            `       <div class="tp-modal-header">` +
            `           <h2>ノート用テンプレートの添付</h2>` +
            `       </div>` +
            `       <div class="tp-modal-body" >` +
            `           <form>` +
            `               <div class="tp-select-div" style="${marginCSS}">` +
            `                   <label for="tp-select-options" style="${labelCSS}">区分${rq}</label>` +
            `                   <p class="tp-p tp-select-p" style="${missingCSS}"></p>` +
            `                   <select class="tp-select" id="tp-select-options">` +
            `                       <option selected disabled hidden>選択してください</option>` +
            `                       <option>統合</option>` +
            `                       <option>分割</option>` +
            `                   </select>` +
            `               </div>` +
            `               <div class="tp-dynamic-div">` +
            //                  modalSelDyna
            `               </div>` +
            `           </form>` +
            `       </div>`;
            //  </div>`

            // Add the frame div to the page
            $('body').append($('<div class="tp-modal-dialog" title="Template Paster" />'));

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

            // Show dialog
            $('.tp-modal-dialog').dialog({
                'minHeight': 50,
                'width': 'auto',
                'modal': true,
                'position': { my: 'center', at: 'top+20%', of: window },
                'buttons': [{
                    'text': '貼り付け',

                    // Event to trigger when the "貼り付け" button is hit
                    'click': function() { 
                        
                        // ----- BEFORE EDIT -----
                        
                        // Variables
                        var $dialog = $(this);
                        var sel = $('.tp-select').find(':selected').text(); // Selection - 'Merge' or 'Split'
                        var srcGoal = '', srcGoalEn = '', srcGoalReverted = '', srcGoalRevertedEn = '';
                        if (sel === '統合') {
                            srcGoal = '元';                    
                            srcGoalEn = 'src';
                            srcGoalReverted = '先';
                            srcGoalRevertedEn = 'goal';
                        } else if (sel === '分割') {
                            srcGoal = '先';
                            srcGoalEn = 'goal';
                            srcGoalReverted = '元';
                            srcGoalRevertedEn = 'src';
                        }
                        var pagenames = []; // Store target page names
                        var rqPage = ''; // Required field
                        var optPages = []; // Optional fields
                        var dPage = ''; // Discussion page

                        // Check if the necessary fields are filled
                        if (trimA($(`#tp-${srcGoalRevertedEn}-input-1`).val()) === '') {
                            alert(`${sel}${srcGoalReverted}が入力されていません`); // 統合先・分割元
                            return;
                        } else {
                            rqPage = trimA($(`#tp-${srcGoalRevertedEn}-input-1`).val());
                            if (sel === '分割') {
                                pagenames.push(rqPage); // 分割元
                            }                    
                        }

                        // Check if at least one of the necessary and optional fields is filled
                        var notFilled = true;
                        var $val;
                        for (let i = 1; i <= 10; i++) {                    

                            // If ID is not found, exit for
                            if ($(`#tp-${srcGoalEn}-input-${i}`).length === 0) {
                                break;
                            }
                            // If any of the fields is filled, set notFilled = false and get the value in each field
                            $val = trimA($(`#tp-${srcGoalEn}-input-${i}`).val());
                            if ($val !== '') {
                                if (notFilled) {
                                    notFilled = false;
                                }
                                if (isInArray($val, pagenames)) {
                                    alert('重複入力があります\n\n' + $val);
                                    return;
                                }
                                pagenames.push($val); // 統合元1-10 or 分割先1-10
                                optPages.push($val);
                            }
                        }                        

                        // Show error message and exit function if any of the necessary fields is NOT filled
                        if (notFilled) {
                            alert(`${sel}${srcGoal}が入力されていません`);
                            return;
                        }

                        // Push the values in the rest of the boxes into the array
                        if (sel === '統合') {
                            if (isInArray(rqPage, pagenames)) {
                                alert('重複入力があります\n\n' + rqPage);
                                return;
                            }
                            pagenames.push(rqPage); // 統合先
                        }

                        // Check if the discussion page field is filled
                        dPage = trimA($('#tp-disc-input').val());
                        if (dPage === '') {
                            alert(`議論場所が入力されていません`);
                            return;
                        } else {
                            pagenames.push(dPage); // 議論場所
                        }                        

                        // Check if the pages exist
                        var notFnd = false;
                        $('.tp-p').each(function(){                            
                            if ($(this).text() === '✗') {
                                notFnd = true;
                            }
                        });

                        // If the boxes include pages that don't exist, ask whether to proceed
                        if (notFnd) {
                            if (confirm('存在しないページ名が含まれています。このまま入力してよろしいですか?') === false ) {
                                return;
                            }
                        }

                        // ----- EDIT -----                       

                        /*********************************************************************************
                         * {{統合元ノート|統合提案がされたページ名|統合先の記事名}}
                         * {{統合先ノート|統合元の記事名1|統合元の記事名2...|d=統合提案がされたページ名}}
                         * {{分割元ノート|分割先の記事名1|分割先の記事名2...|d=分割提案がされたページ名}}
                         * {{分割先ノート|分割提案がされたノート名|分割元の記事名}}
                        ***********************************************************************************/

                        var TP1 = `\n{{${sel}${srcGoalReverted}ノート|${optPages.join('|')}|d=${dPage}}}`;
                        var TP2 = `\n{{${sel}${srcGoal}ノート|${dPage}|${rqPage}}}`;
                        var cnt = 0;
                        var msg = '編集が完了しました';
                        var err = '以下のページは、エラーにより編集できませんでした';
                        var msgDone = '';
                        var sig = ' ([[User:Dragoniez/Template Paster|Template Paster]])';
                        var editTar = [];

                        // Hide elements
                        $dialog.dialog({"buttons": []}); // Hide button
                        $('.tp-modal-body form').css('display','none'); // Hide form
                        $('.tp-modal-body').append($(`<p class="doing">編集中...</p>`)); // Show message

                        // Templates to paste (debugger)
                        //console.log(TP1 + '\n' + TP2);
                        
                        // Edit the talk page of merge goal or split source
                        $.ajax({
                            url: mw.util.wikiScript('api'),
                            data: {
                                format: 'json',
                                action: 'edit',
                                title: ConvToTalk(rqPage),
                                section: 0,
                                summary: `{{[[Template:${sel}${srcGoalReverted}ノート|${sel}${srcGoalReverted}ノート]]}}${sig}`,
                                watchlist: 'watch',                                
                                appendtext: TP1,
                                token: mw.user.tokens.get('csrfToken')
                            },
                            dataType: 'json',
                            type: 'POST',
                            success: function(result) {
                                
                                if(result && result.edit &&result.edit.result == 'Success' ) {// if edit was successful
                                    editTar.push(ConvToTalk(rqPage)); // Get the page edited
                                } else if(result && result.error) { // if error occured                                 
                                    err += `<br><br>ページ名: ${ConvToTalk(rqPage)}<br>コード: ${result.error.code}<br>詳細: ${result.error.info}`;
                                } else {  //if unknown error occurred
                                    err += `<br><br>ページ名: ${ConvToTalk(rqPage)}<br>コード: N/A<br>詳細: Unknown error`;
                                }

                                // Edit the talk page(s) of merge source(s) or split goal(s)
                                for (let i = 0; i < optPages.length; i++) {
                                    setTimeout(function(){ // Set a 0.1s dalay to get the right error details
                                        $.ajax({
                                            url: mw.util.wikiScript('api'),
                                            data: {
                                                format: 'json',
                                                action: 'edit',
                                                title: ConvToTalk(optPages[i]),
                                                section: 0,
                                                summary: `{{[[Template:${sel}${srcGoal}ノート|${sel}${srcGoal}ノート]]}}${sig}`,
                                                watchlist: 'watch',                                
                                                appendtext: TP2,
                                                token: mw.user.tokens.get('csrfToken')
                                            },
                                            dataType: 'json',
                                            type: 'POST',                                        
                                            success: function(result) {  
                                                cnt++;                              
                                                if(result && result.edit &&result.edit.result == 'Success' ) {// if edit was successful
                                                    editTar.push(ConvToTalk(optPages[i])); // Get the page edited
                                                } else if(result && result.error) { // if error occured                                 
                                                    err += `<br><br>ページ名: ${ConvToTalk(optPages[i])}<br>コード: ${result.error.code}<br>詳細: ${result.error.info}`;
                                                } else { //if unknown error occurred
                                                    err += `<br><br>ページ名: ${ConvToTalk(optPages[i])}<br>コード: N/A<br>詳細: Unknown error`;
                                                }            
                                                
                                                // In the last iteration
                                                if (cnt === optPages.length) {

                                                    // Ending message
                                                    if (err === '以下のページは、エラーにより編集できませんでした') {
                                                        msgDone = msg;
                                                    } else {
                                                        msgDone = msg + '<br><br>' + err;
                                                    }

                                                    console.log(editTar);
                                                    $('.doing').remove(); // Remove '編集中...'
                                                    $('.tp-modal-body').append($(`<p>${msgDone}</p>`)); // Show message
                                                    $dialog.dialog({ // Show close button
                                                        'position': { my: 'center', at: 'top+20%', of: window },
                                                        'buttons': [{
                                                            'text': '閉じる',
                                                            'click': function(){
                                                                $(this).dialog('close');
                                                                if (isInArray(mw.config.get('wgPageName'), editTar)) { // Reload page
                                                                    location.reload();
                                                                }
                                                            }
                                                        }]
                                                    });                                               

                                                }
                                            }
                                        });
                                    }, 100);
                                }
                            }
                        });                              

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

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

            // When selection is changed between 統合 and 分割
            $('.tp-select').change(function(){
                var sel = $(this).find(':selected').text();
                
                // Reset content
                $('.tp-dynamic-div').empty();

                // Html for 'Add' button
                var optNum = 1;
                var btnDivSrc = '', btnDivGoal = '';
                var btnDiv =
                `   <div class="tp-btn-div">` +
                `       <button type="button" class="addBtn">追加</button>` +
                `   </div>`;

                // Variable substitution to insert the html into the right place
                if (sel === '統合') {
                    btnDivSrc = btnDiv;
                } else if (sel === '分割') {
                    btnDivGoal = btnDiv;
                } else {
                    alert('エラーが発生しました。');
                    return;
                }

                // Html for the dynamic elements
                var modalSelDyna =
                `   <div class="tp-src-div" style="${marginCSS}">` +
                `       <label for="tp-src-input-${optNum}" style="${labelCSS}">${sel}${rq}</label>` +
                `       <p class="tp-p tp-src-p" id="tp-src-p-${optNum}" style="${missingCSS}"></p>` +
                `       <input class="tp-src-input" id="tp-src-input-${optNum}">` +
                        btnDivSrc +
                `   </div>` +
                `   <div class="tp-goal-div" style="${marginCSS}">` +
                `       <label for="tp-goal-input-${optNum}" style="${labelCSS}">${sel}${rq}</label>` +
                `       <p class="tp-p tp-goal-p" id="tp-goal-p-${optNum}" style="${missingCSS}"></p>` +
                `       <input class="tp-goal-input" id="tp-goal-input-${optNum}">` +
                        btnDivGoal +
                `   </div>` +
                `   <div class="tp-disc-div" style="${marginCSS}">` +
                `       <label for="tp-disc-input" style="${labelCSS}">議論場所${rq}</label>` +
                `       <p class="tp-p" id="tp-disc-p" style="${missingCSS}"></p>` +
                `       <input id="tp-disc-input">` +
                `   </div>`;

                // Show the dynamic elements
                $('.tp-dynamic-div').html(modalSelDyna);
                
                // Function to add optional inputboxes when addBtn is hit
                $('.addBtn').click(function(){

                    // Html for the optional box
                    var srcGoal = '', srcGoalEn = '';
                    if (sel === '統合') {
                        srcGoal = '元';
                        srcGoalEn = 'src';
                    } else if (sel === '分割') {
                        srcGoal = '先';
                        srcGoalEn = 'goal';
                    }
                    optNum++; // 1 is added every time the addBtn is hit
                    var modalOptDyna =
                    `   <br>` +
                    `   <label for="tp-${srcGoalEn}-input-${optNum}" style="${labelCSS}; margin-top: 0.5em">${sel}${srcGoal}${optNum}</label>` +
                    `   <p class="tp-p tp-${srcGoalEn}-p" id="tp-${srcGoalEn}-p-${optNum}" style="${missingCSS}"></p>` +
                    `   <input class="tp-${srcGoalEn}-input" id="tp-${srcGoalEn}-input-${optNum}">`;

                    // Show the box
                    $('.tp-btn-div').before(modalOptDyna);

                    // Hide addBtn if optNum === 10
                    if (optNum === 10) {
                        $('.tp-btn-div').hide();
                    }

                    // Check the existence of the pagenames in the addional inputboxes 
                    var isRunning = '';          
                    $('.tp-modal-dialog input').on('input', function(e){
                        setTimeout(function(){ // Delay by 0.5s
                            if (isRunning === e.target.id) { // Don't run when the code is already running                                
                                return;
                            }
                            isRunning = e.target.id;
                            var $tar = $('#' + e.target.id);
                            var tarVal = trimA($tar.val());
                            var $tarP = $('#' + e.target.id.replace('input', 'p'));
                            if (tarVal === '') {
                                $tarP.text('');
                                isRunning = '';
                            } else {
                                new mw.Api().get({
                                    action: 'query',
                                    titles: tarVal,
                                    formatversion: 2
                                }).done(function(res){
                                    var missing = res.query.pages[0].missing == true ? true: false;
                                    if (missing) {
                                        $tarP.text('✗').css('color', 'red');                                    
                                    } else if (missing === false) {
                                        $tarP.text('✓').css('color', 'limegreen');
                                    }
                                    isRunning = '';
                                });
                            }
                        }, 500);
                    });
                    return;
                });

                // Check the existence of the pagenames in the default inputboxes  
                var isRunning = '';          
                $('.tp-modal-dialog input').on('input', function(e){
                    setTimeout(function(){ // Delay by 0.5s
                        if (isRunning === e.target.id) { // Don't run when the code is already running                            
                            return;
                        }
                        isRunning = e.target.id;
                        var $tar = $('#' + e.target.id);
                        var tarVal = trimA($tar.val());
                        var $tarP = $('#' + e.target.id.replace('input', 'p'));
                        if (tarVal === '') {
                            $tarP.text('');
                            isRunning = '';
                        } else {
                            new mw.Api().get({
                                action: 'query',
                                titles: tarVal,
                                formatversion: 2
                            }).done(function(res){
                                var missing = res.query.pages[0].missing == true ? true: false;
                                if (missing) {
                                    $tarP.text('✗').css('color', 'red');                                    
                                } else if (missing === false) {
                                    $tarP.text('✓').css('color', 'limegreen');
                                }
                                isRunning = '';
                            });
                        }
                    }, 500);                       
                });

            });
        });

        // Function to convert the name of a main-space page to that of its talk page
        function ConvToTalk(page){
            var ns = page.split(':');
            var pageNoNs = page.replace(ns[0] + ':', '');
            switch(ns[0]) {
                case 'ノート':
                    return 'ノート:' + pageNoNs;
                case '利用者': 
                case '利用者‐会話': 
                case '利用者-会話':
                case '利用者・トーク':
                case 'User':
                case 'User talk':
                    return '利用者‐会話:' + pageNoNs;
                case 'Wikipedia':
                case 'Wikipedia‐ノート':
                case 'Wikipedia-ノート':
                case 'Wikipedia talk':
                case 'Project':
                case 'Project talk':
                case 'WP':
                case 'WT':
                    return 'Wikipedia‐ノート:' + pageNoNs;
                case 'ファイル':
                case 'ファイル‐ノート':
                case 'ファイル-ノート':
                case 'ファイル・トーク':
                case 'File':
                case 'File talk':
                case '画像':
                case '画像‐ノート':
                case '画像-ノート':
                case 'Image':
                case 'Image talk':
                    return 'ファイル‐ノート:' + pageNoNs;
                case 'MediaWiki':
                case 'MediaWiki‐ノート':
                case 'MediaWiki-ノート':
                case 'MediaWiki・トーク':
                case 'MediaWiki talk':
                    return 'MediaWiki‐ノート:' + pageNoNs;
                case 'Template':
                case 'Template‐ノート':
                case 'Template-ノート':
                case 'テンプレート':
                case 'テンプレート・トーク':
                case 'Template talk':
                    return 'Template‐ノート:' + pageNoNs;
                case 'Help':
                case 'Help‐ノート':
                case 'Help-ノート':
                case 'ヘルプ':
                case 'ヘルプ・トーク':
                case 'Help talk':
                    return 'Help‐ノート:' + pageNoNs;
                case 'Category':
                case 'Category‐ノート':
                case 'Category-ノート':
                case 'カテゴリ':
                case 'カテゴリ・トーク':
                case 'Category talk':
                    return 'Category‐ノート:' + pageNoNs;
                case 'Portal':
                case 'Portal‐ノート':
                case 'Portal-ノート':
                case 'ポータル‐ノート':
                case 'ポータル-ノート':
                case 'Portal・トーク':
                    return 'Portal‐ノート:' + pageNoNs;
                case 'プロジェクト':
                case 'プロジェクト‐ノート':
                case 'プロジェクト-ノート':
                case 'プロジェクト・トーク':
                    return 'プロジェクト‐ノート:' + pageNoNs;
                case 'モジュール':
                case 'モジュール‐ノート':
                case 'モジュール-ノート':
                case 'モジュール・トーク':
                case 'Module':
                case 'Module talk':
                    return 'モジュール‐ノート:' + pageNoNs;
                case 'Gadget':
                case 'Gadget talk':
                    return 'Gadget talk:' + pageNoNs;
                case 'Gadget definition':
                case 'Gadget definition talk':
                    return 'Gadget definition talk:' + pageNoNs;
                default:
                    return 'ノート:' + page;
            }
        }

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