「利用者:Dragoniez/scripts/RFD Helper.js」の版間の差分
表示
削除された内容 追加された内容
v1.2: 同じページ上で2度目の報告を行おうとすると無効トークンが返るバグを修正 |
m v1.3.2: cl |
||
(同じ利用者による、間の2版が非表示) | |||
2行目: | 2行目: | ||
* Name: RFD Helper * |
* Name: RFD Helper * |
||
* Author: Dragoniez * |
* Author: Dragoniez * |
||
* Version: 1.2 |
* Version: 1.3.2 * |
||
****************************/ |
****************************/ |
||
//<nowiki> |
//<nowiki> |
||
// ******************** CONFIGS ******************** |
// ******************** CONFIGS ******************** |
||
// var {dragoLib} = require('../dragoLib/dragoLib.js'); |
|||
/* Config |
/* Config |
||
52行目: | 54行目: | ||
// DebugMode |
// DebugMode |
||
const debuggingMode = { |
const debuggingMode = { |
||
library: false, |
|||
portletlinkText: false, |
|||
targetPage: false, |
|||
scriptAd: false, |
|||
causeIntentionalError: false |
|||
}; |
}; |
||
const library = debuggingMode.library ? |
const library = debuggingMode.library ? |
||
68行目: | 70行目: | ||
const REDIRS = {}; // {source: goal, source2: goal2}... |
const REDIRS = {}; // {source: goal, source2: goal2}... |
||
const btns = [{ // Buttons of the dialog |
const btns = [{ // Buttons of the dialog |
||
text: '依頼', |
|||
click: submitRequest |
|||
}, { |
}, { |
||
text: 'プレビュー', |
|||
click: preview |
|||
}, { |
}, { |
||
text: '閉じる', |
|||
click: function() { |
|||
$(this).dialog('close'); |
$(this).dialog('close'); |
||
} |
} |
||
169行目: | 171行目: | ||
// Show dialog |
// Show dialog |
||
$('#rfdh-dialog').dialog({ |
$('#rfdh-dialog').dialog({ |
||
dialogClass: 'rfdh-dialog-main', |
|||
resizable: false, |
|||
height: 'auto', |
|||
width: 'auto', |
|||
modal: true, |
|||
dragoLib.dialogCSS(rfdhConfig.headerColor, rfdhConfig.backgroundColor, rfdhConfig.fontSize); |
open: function() { |
||
dragoLib.dialogCSS($('.rfdh-dialog-main'), rfdhConfig.headerColor, rfdhConfig.backgroundColor, rfdhConfig.fontSize); |
|||
$('#rfdh-vote-select').children('option').eq(1).prop('selected', true); |
$('#rfdh-vote-select').children('option').eq(1).prop('selected', true); |
||
}, |
}, |
||
buttons: btns |
|||
}); |
}); |
||
187行目: | 190行目: | ||
// Error storage |
// Error storage |
||
const problems = { |
const problems = { |
||
redirects: '', |
|||
conjunction: '', |
|||
text: '' |
|||
}; |
}; |
||
202行目: | 205行目: | ||
var vote2 = $vote2.children('option').filter(':selected').val(); |
var vote2 = $vote2.children('option').filter(':selected').val(); |
||
if (vote && vote2) { |
if (vote && vote2) { |
||
if (!delimiter) problems |
if (!delimiter) problems.conjunction = '・依頼者票の中間表現'; |
||
} else if ((vote && !vote2) || (!vote && !vote2)) { |
} else if ((vote && !vote2) || (!vote && !vote2)) { |
||
if (delimiter) { |
if (delimiter) { |
||
219行目: | 222行目: | ||
// Get text for the request (at this point RFD templates are not included) |
// Get text for the request (at this point RFD templates are not included) |
||
var reqText = dragoLib. |
var reqText = dragoLib.trim($('#rfdh-reason-input').val()); |
||
if (!reqText) problems.text = '・依頼文'; |
if (!reqText) problems.text = '・依頼文'; |
||
$('#rfdh-reason-input').val(reqText); |
$('#rfdh-reason-input').val(reqText); |
||
239行目: | 242行目: | ||
// Return values once (Go on to API query) |
// Return values once (Go on to API query) |
||
return { |
return { |
||
redirects: redirects, |
|||
reqText: reqText, |
|||
summary: generateSummary(dragoLib.getSection5('依頼'), redirects.length) |
|||
}; |
}; |
||
255行目: | 258行目: | ||
return new Promise(function(resolve) { |
return new Promise(function(resolve) { |
||
new mw.Api().get({ |
new mw.Api().get({ |
||
action: 'query', |
|||
titles: redirectsArr.join('|'), // Max 50 |
|||
redirects: 1, |
|||
formatversion: 2 |
|||
}).then(function(res) { |
}).then(function(res) { |
||
var resRdr, redirect; |
var resRdr, redirect; |
||
280行目: | 283行目: | ||
}); |
}); |
||
}); |
}); |
||
} |
}; |
||
const redirectsArr = JSON.parse(JSON.stringify(redirects)); // Prevent pass-by-reference (the variable will be spliced) |
const redirectsArr = JSON.parse(JSON.stringify(redirects)); // Prevent pass-by-reference (the variable will be spliced) |
||
290行目: | 293行目: | ||
return await Promise.all(queries); |
return await Promise.all(queries); |
||
} |
}; |
||
// Return reqText and summary as an object |
// Return reqText and summary as an object |
||
311行目: | 314行目: | ||
reqText = tlRfdForRequest.join('') + reqText; |
reqText = tlRfdForRequest.join('') + reqText; |
||
return { |
return { |
||
reqText: reqText, |
|||
redirects: redirects, |
|||
summary: ep.summary, |
|||
needQuery: needQuery |
|||
}; |
}; |
||
320行目: | 323行目: | ||
function generateSummary(section, redirectsCnt) { |
function generateSummary(section, redirectsCnt) { |
||
var summary = dragoLib. |
var summary = dragoLib.trim($('#rfdh-summary-input').val()); |
||
summary = summary ? summary : '+' + redirectsCnt; |
summary = summary ? summary : '+' + redirectsCnt; |
||
summary = `/*${section}*/ ${summary}${scriptAd}`; |
summary = `/*${section}*/ ${summary}${scriptAd}`; |
||
367行目: | 370行目: | ||
$('body').append(previewDiv); |
$('body').append(previewDiv); |
||
$('#rfdh-preview-dialog').dialog({ |
$('#rfdh-preview-dialog').dialog({ |
||
dialogClass: 'rfdh-dialog-preview', |
|||
height: 'auto', |
|||
' |
width: $('#content').width() * 0.8, |
||
modal: true, |
|||
open: async function(){ |
|||
// Initialize the design of the dialog |
// Initialize the design of the dialog |
||
dragoLib.dialogCSS(rfdhConfig.headerColor, rfdhConfig.backgroundColor, rfdhConfig.fontSize); |
dragoLib.dialogCSS($('.rfdh-dialog-preview'), rfdhConfig.headerColor, rfdhConfig.backgroundColor, rfdhConfig.fontSize); |
||
// Update REDIRS and get text and summary to preview |
// Update REDIRS and get text and summary to preview |
||
406行目: | 410行目: | ||
}, |
}, |
||
buttons: [{ |
|||
text: '閉じる', |
|||
click: function(){ |
|||
if ($('#rfdh-preview-checkbox').is(':checked')) { |
if ($('#rfdh-preview-checkbox').is(':checked')) { |
||
const $input = $('#rfdh-redirectlist-input'); |
const $input = $('#rfdh-redirectlist-input'); |
||
var inputVal = dragoLib. |
var inputVal = dragoLib.trim($input.val()); |
||
inputVal = inputVal.split('\n'); |
inputVal = inputVal.split('\n'); |
||
inputVal = inputVal.filter(function(item) { |
inputVal = inputVal.filter(function(item) { |
||
444行目: | 448行目: | ||
$dialog |
$dialog |
||
.append(`<p id="rfdh-editting">準備中${dragoLib.toggleLoadingSpinner('add')}</p>`) |
.append(`<p id="rfdh-editting">準備中${dragoLib.toggleLoadingSpinner('add')}</p>`) |
||
.dialog({ |
.dialog({buttons: []}) |
||
.find('form').css('display', 'none'); |
.find('form').css('display', 'none'); |
||
451行目: | 455行目: | ||
$('#rfdh-editting').prop('innerHTML', '<span style="color: MediumVioletRed;">転送先不明のリダイレクトを検出しました</span>'); |
$('#rfdh-editting').prop('innerHTML', '<span style="color: MediumVioletRed;">転送先不明のリダイレクトを検出しました</span>'); |
||
$dialog.dialog({ |
$dialog.dialog({ |
||
buttons: [{ |
|||
text: 'プレビュー', |
|||
click: preview |
|||
}, { |
}, { |
||
text: '続行', |
|||
click: function() { |
|||
$(this).dialog({'buttons': []}); |
$(this).dialog({'buttons': []}); |
||
submitRequest2(ep); |
submitRequest2(ep); |
||
} |
} |
||
}, { |
}, { |
||
text: '戻る', |
|||
click: function() { |
|||
$(this).find('form').css('display', 'block'); |
$(this).find('form').css('display', 'block'); |
||
$(this).dialog({'buttons': btns}); |
$(this).dialog({'buttons': btns}); |
||
468行目: | 472行目: | ||
} |
} |
||
}, { |
}, { |
||
text: '中止', |
|||
click: function() { |
|||
$(this).remove(); |
$(this).remove(); |
||
} |
} |
||
485行目: | 489行目: | ||
// Get timestamps |
// Get timestamps |
||
var lr = await dragoLib.getLatestRevision(RFDR); |
|||
if (! |
if (!lr) return editDone(ep, 'ts'); |
||
lr = lr[0]; |
|||
// Get section |
// Get section title |
||
var sectiontitle = dragoLib.getSection5('依頼'); |
|||
ep.section = |
ep.section = sectiontitle; |
||
// Get the content of the section to which the request will be submitted |
// Get the content of the section to which the request will be submitted |
||
var sections = dragoLib.parseContentBySection(lr.content); |
|||
sections = sections.filter(function(obj) { |
|||
⚫ | |||
return obj.title === sectiontitle; |
|||
⚫ | |||
⚫ | |||
⚫ | |||
⚫ | |||
⚫ | |||
⚫ | |||
⚫ | |||
// Check for duplicate requests |
// Check for duplicate requests |
||
505行目: | 509行目: | ||
const redirectsEscaped = dragoLib.escapeRegExp(ep.redirects.join('|')); |
const redirectsEscaped = dragoLib.escapeRegExp(ep.redirects.join('|')); |
||
const redirectRegExp = new RegExp(`(?:${redirectsEscaped})`); |
const redirectRegExp = new RegExp(`(?:${redirectsEscaped})`); |
||
const templates = dragoLib.findTemplates( |
const templates = dragoLib.findTemplates(lr.content, 'rfd'); // Extract RFD templates |
||
if (templates.length !== 0) { |
if (templates.length !== 0) { |
||
for (const tl of templates) { // Loop through all the extracted RFD templates and check if the 1st parameter contains the redirect(s) to be RFD-ed |
for (const tl of templates) { // Loop through all the extracted RFD templates and check if the 1st parameter contains the redirect(s) to be RFD-ed |
||
525行目: | 529行目: | ||
var mtch; |
var mtch; |
||
if (!(mtch = ep.reqText.match(/\* {{RFD\|/g))) return editDone(ep, 'dr'); // If reqText has no RFD template in it, all the redirects have already been reported |
if (!(mtch = ep.reqText.match(/\* {{RFD\|/g))) return editDone(ep, 'dr'); // If reqText has no RFD template in it, all the redirects have already been reported |
||
ep.summary = generateSummary( |
ep.summary = generateSummary(sectiontitle, mtch.length); // Re-generate edit summary |
||
// Edit page |
// Edit page |
||
const |
const result = await new mw.Api().post({ |
||
action: 'edit', |
|||
const result = await dragoLib.editPage(RFDR, '\n\n' + ep.reqText, 'appendtext', ts.baseTS, ts.curTS, sectNum, ep.summary, token); |
|||
title: RFDR, |
|||
switch(result) { |
|||
section: sectNum, |
|||
appendtext: '\n\n' + ep.reqText, |
|||
basetimestamp: lr.basetimestamp, |
|||
starttimestamp: lr.curtimestamp, |
|||
summary: ep.summary, |
|||
token: debuggingMode.causeIntentionalError ? '' : mw.user.tokens.get('csrfToken'), |
|||
format: 'json' |
|||
}).then(function(res) { |
|||
if (res && res.edit) { |
|||
if (res.edit.result === 'Success') return true; |
|||
⚫ | |||
return false; |
|||
}).catch(function(code, err) { |
|||
return err.error.info; |
|||
}); |
|||
⚫ | |||
case true: // Edit succeeded |
case true: // Edit succeeded |
||
editDone(ep, true); |
editDone(ep, true); |
||
545行目: | 566行目: | ||
/** |
/** |
||
* @param {*} ep |
* @param {*} ep |
||
* @param {*} type 'dr' when request is cancelled because of duplicates, ' |
* @param {*} type 'dr' when request is cancelled because of duplicates, 'lr' when the latest revision failed to be fetched, 'sect' when section number |
||
* failed to be fetcehd, true when edit succeeded, false when unexpected error occurred on edit, errcode when edit failed |
* failed to be fetcehd, true when edit succeeded, false when unexpected error occurred on edit, errcode when edit failed |
||
*/ |
*/ |
||
559行目: | 580行目: | ||
showCopyButton = false; |
showCopyButton = false; |
||
break; |
break; |
||
case ' |
case 'lr': |
||
$msg.prop('innerHTML', '<span style="color: MediumVioletRed;">失敗: 報告先の最新版が取得できませんでした</span>'); |
$msg.prop('innerHTML', '<span style="color: MediumVioletRed;">失敗: 報告先の最新版が取得できませんでした</span>'); |
||
break; |
break; |
||
580行目: | 601行目: | ||
const $dialog = $('#rfdh-dialog'); |
const $dialog = $('#rfdh-dialog'); |
||
$dialog.dialog({ |
$dialog.dialog({ |
||
buttons: [{ |
|||
text: '報告先', |
|||
click: function() { |
|||
window.open(mw.util.getUrl(RFDR + '#' + ep.section), '_blank'); |
window.open(mw.util.getUrl(RFDR + '#' + ep.section), '_blank'); |
||
} |
} |
||
}, { |
}, { |
||
text: '閉じる', |
|||
click: function() { |
|||
$(this).dialog('close'); |
$(this).dialog('close'); |
||
if (mw.config.get('wgPageName') === RFDR) location.reload(true); |
if (mw.config.get('wgPageName') === RFDR) location.reload(true); |
||
613行目: | 634行目: | ||
function getRedirectSources() { |
function getRedirectSources() { |
||
const $input = $('#rfdh-redirectlist-input'); |
const $input = $('#rfdh-redirectlist-input'); |
||
var inputVal = dragoLib. |
var inputVal = dragoLib.trim($input.val()); |
||
if (!inputVal) return []; |
if (!inputVal) return []; |
||
inputVal = inputVal.split('\n'); |
inputVal = inputVal.split('\n'); |
2022年10月31日 (月) 09:26時点における版
/****************************
* Name: RFD Helper *
* Author: Dragoniez *
* Version: 1.3.2 *
****************************/
//<nowiki>
// ******************** CONFIGS ********************
// var {dragoLib} = require('../dragoLib/dragoLib.js');
/* Config
rfdhConfig: {
headerColor: '#FEC493',
backgroundColor: '#FFF0E4',
portletlinkPosition: 'skin-dependent',
fontSize: 'skin-dependent'
} */
if (typeof rfdhConfig === 'undefined') var rfdhConfig = {};
if (!rfdhConfig.headerColor) rfdhConfig.headerColor = '#FEC493';
if (!rfdhConfig.backgroundColor) rfdhConfig.backgroundColor = '#FFF0E4';
if (!rfdhConfig.portletlinkPosition) {
if (mw.config.get('skin') === 'minerva') {
rfdhConfig.portletlinkPosition = 'p-personal';
} else {
rfdhConfig.portletlinkPosition = 'p-cactions';
}
}
if (!rfdhConfig.fontSize) {
switch(mw.config.get('skin')) {
case 'vector':
case 'vector-2022':
case 'minerva':
rfdhConfig.fontSize = '80%';
break;
case 'monobook':
rfdhConfig.fontSize = '110%';
break;
case 'timeless':
rfdhConfig.fontSize = '90%';
break;
default:
rfdhConfig.fontSize = '80%';
}
}
// ******************** SCRIPT BODY ********************
(function() { // Create a function scope
// ************************* VARIABLES *************************
// DebugMode
const debuggingMode = {
library: false,
portletlinkText: false,
targetPage: false,
scriptAd: false,
causeIntentionalError: false
};
const library = debuggingMode.library ?
'http://127.0.0.1:5500/dragoLib/dragoLib.js' :
'//ja-two.iwiki.icu/w/index.php?title=User:Dragoniez/scripts/dragoLib.js&action=raw&ctype=text/javascript';
const portletlinkText = debuggingMode.portletlinkText ? 'リダイレクトの削除依頼β' : 'リダイレクトの削除依頼';
const RFDR = debuggingMode.targetPage ? '利用者:Dragoniez/test3' : 'Wikipedia:リダイレクトの削除依頼/受付';
const scriptAd = ' ([[User:Dragoniez/scripts/RFD Helper|' + (debuggingMode.scriptAd ? 'RFD Helper Dev]])' : 'RFD Helper]])');
// Others
const REDIRS = {}; // {source: goal, source2: goal2}...
const btns = [{ // Buttons of the dialog
text: '依頼',
click: submitRequest
}, {
text: 'プレビュー',
click: preview
}, {
text: '閉じる',
click: function() {
$(this).dialog('close');
}
}];
// ************************* DOM READY FUNCTION *************************
$.when(
$.getScript(library),
mw.loader.using('jquery.ui'),
$.ready
).then(function() {
$('head').append( // <style> for the dialog
'<style>' +
' .rfdh-needmargin {' +
' margin: 0.5em 0;' +
' }' +
' .rfdh-textarea {' +
' width: 100%;' +
' box-sizing: border-box;' +
' }' +
' .rfdh-preview-notarget {' +
` background: ${rfdhConfig.headerColor};` +
' }' +
'</style>'
);
if (dragoLib.inGroup('autoconfirmed') && mw.config.get('wgAction') !== 'edit') {
$(mw.util.addPortletLink(rfdhConfig.portletlinkPosition, '#', portletlinkText, 'ca-rfdh', 'リダイレクトの削除依頼を提出', null, '#ca-move')).click(openDialog);
}
});
// ************************* MAIN FUNCTIONS *************************
function openDialog(e) {
e.preventDefault();
const votes =
'<option value="">なし</option>' +
'<option value="{{AFD|削除}}">削除</option>' +
'<option value="{{AFD|全削除}}">全削除</option>' +
'<option value="{{AFD|即時削除}}">即時削除</option>' +
'<option value="{{AFD|全即時削除}}">全即時削除</option>' +
'<option value="{{AFD|緊急削除}}">緊急削除</option>' +
'<option value="{{AFD|緊急即時削除}}">緊急即時削除</option>' +
'<option value="{{AFD|一部}}">一部削除/存続</option>' +
'<option value="{{AFD|存続}}">存続</option>' +
'<option value="{{AFD|全存続}}">全存続</option>' +
'<option value="{{AFD|履歴統合}}">履歴統合</option>';
const dialogHtml =
'<div id="rfdh-dialog" title="RFD Helper" style="max-height: 80vh; min-width: 515px;">' +
' <div id="rfdh-dialog-header">' +
' <h2>リダイレクトの削除依頼</h2>' +
' </div>' +
' <div id="rfdh-dialog-body">' +
' <form>' +
' <div id="rfdh-redirectlist-div">' +
' <label for="rfdh-redirectlist-input">リダイレクト元 (ページごとに改行)</label>' +
' <textarea id="rfdh-redirectlist-input" class="rfdh-textarea" rows="8"></textarea>' +
' <input id="rfdh-redirectlist-cleanup" class="rfdh-needmargin" type="button" value="整形" style="margin-right: 0.5em;">' +
' <span>(余分な改行および重複項目を除去)</span>' +
' </div>' +
' <div id="rfdh-vote-div" class="rfdh-needmargin">' +
' <label for="rfdh-vote-select">依頼者票 <span style="font-size: smaller;">(複数選択する場合のみ中間表現必須)</span></label><br>' +
' <select id="rfdh-vote-select">' +
votes +
' </select>' +
' <select id="rfdh-votedelimiter-select">' +
' <option></option>' +
' <option value="および">および</option>' +
' <option value="または">または</option>' +
' </select>' +
' <select id="rfdh-vote2-select">' +
votes +
' </select>' +
' </div>' +
' <div id="rfdh-reason-div" class="rfdh-needmargin">' +
' <label for="rfdh-reason-input">依頼文 <span style="font-size: smaller;">(署名不要)</span></label>' +
' <textarea id="rfdh-reason-input" class="rfdh-textarea" rows="3"></textarea>' +
' </div>' +
' <div id="rfdh-summary-div" class="rfdh-needmargin">' +
' <input id="rfdh-summary-checkbox" type="checkbox">' +
' <label for="rfdh-summary-checkbox">要約を指定</label>' +
' <textarea id="rfdh-summary-input" class="rfdh-textarea" rows="3" style="display: none;"></textarea>' +
' </div>' +
' </form>' +
' </div>' +
'</div>';
// Add the frame div to the page
$('body').append(dialogHtml);
// Show dialog
$('#rfdh-dialog').dialog({
dialogClass: 'rfdh-dialog-main',
resizable: false,
height: 'auto',
width: 'auto',
modal: true,
open: function() {
dragoLib.dialogCSS($('.rfdh-dialog-main'), rfdhConfig.headerColor, rfdhConfig.backgroundColor, rfdhConfig.fontSize);
$('#rfdh-vote-select').children('option').eq(1).prop('selected', true);
},
buttons: btns
});
}
// Check whether the necessary fileds are filleld
function editPrep1() {
// Error storage
const problems = {
redirects: '',
conjunction: '',
text: ''
};
// Redirect sources
const redirects = getRedirectSources();
if (redirects.length === 0) problems.redirects = '・リダイレクト元';
// Votes
const $vote = $('#rfdh-vote-select'), $delimiter = $('#rfdh-votedelimiter-select'), $vote2 = $('#rfdh-vote2-select');
var vote = $vote.children('option').filter(':selected').val();
var delimiter = $delimiter.children('option').filter(':selected').val();
var vote2 = $vote2.children('option').filter(':selected').val();
if (vote && vote2) {
if (!delimiter) problems.conjunction = '・依頼者票の中間表現';
} else if ((vote && !vote2) || (!vote && !vote2)) {
if (delimiter) {
$delimiter.children('option').eq(0).prop('selected', true); // Blank delimiter
delimiter = '';
}
} else if (!vote && vote2) {
$vote.children(`option[value=${vote2}]`).prop('selected', true); // Move vote2 to vote
$delimiter.children('option').eq(0).prop('selected', true); // Blank delimiter
$vote2.children('option').eq(0).prop('selected', true); // Blank vote2
vote = vote2;
delimiter = '';
vote2 = '';
}
const votetext = vote + delimiter + vote2;
// Get text for the request (at this point RFD templates are not included)
var reqText = dragoLib.trim($('#rfdh-reason-input').val());
if (!reqText) problems.text = '・依頼文';
$('#rfdh-reason-input').val(reqText);
reqText = '** ' + votetext + ' ' + reqText + (reqText.substring(reqText.length - 4) === '~~~~' ? '' : '--~~~~');
// Check the necessary fields
var msg = '以下の項目に入力の不備があります:', proceed = true;
for (let key in problems) {
if (problems[key]) {
proceed = false;
msg += '\n' + problems[key];
}
}
if (!proceed) {
alert(msg);
return;
}
// Return values once (Go on to API query)
return {
redirects: redirects,
reqText: reqText,
summary: generateSummary(dragoLib.getSection5('依頼'), redirects.length)
};
}
// Add RFD templates to reqText
async function editPred2(ep) {
// Anonymous function to get redirect targets and update REDIRS
const getRredirectTargets = async function(redirects) {
const queryRedirects = function(redirectsArr) {
return new Promise(function(resolve) {
new mw.Api().get({
action: 'query',
titles: redirectsArr.join('|'), // Max 50
redirects: 1,
formatversion: 2
}).then(function(res) {
var resRdr, redirect;
if (res && (resRdr = res.query)) {
if (resRdr = resRdr.redirects) {
for (let i = 0; i < resRdr.length; i++) {
redirect = resRdr[i];
REDIRS[redirect.from] = redirect.to;
}
} else { // If the API doesn't return res.query.redirects, the queried titles are all non-redirects
for (let i = 0; i < redirectsArr.length; i++) {
redirect = redirectsArr[i];
REDIRS[redirect] = undefined;
}
}
}
resolve();
}).catch(function(code, err) {
console.log(err.error.info);
resolve();
});
});
};
const redirectsArr = JSON.parse(JSON.stringify(redirects)); // Prevent pass-by-reference (the variable will be spliced)
const queries = [];
while(redirectsArr.length !== 0) {
queries.push(queryRedirects(redirectsArr.slice(0, 50)));
redirectsArr.splice(0, 50);
}
return await Promise.all(queries);
};
// Return reqText and summary as an object
const tlRfd = '* {{RFD|SOURCE|GOAL}}\n', tlRfdForRequest = [];
var redirects = ep.redirects, reqText = ep.reqText, needQuery = false;
for (let i = 0; i < redirects.length; i++) {
if (!REDIRS[redirects[i]]) { // If the object doesn't know the target of any redirect, it can be retrieved from the API
needQuery = true;
break;
}
}
if (needQuery) {
needQuery = false;
await getRredirectTargets(redirects);
}
for (let i = 0; i < redirects.length; i++) {
if (!REDIRS[redirects[i]]) needQuery = true;
tlRfdForRequest.push(tlRfd.replace('SOURCE', redirects[i]).replace('GOAL', REDIRS[redirects[i]]));
}
reqText = tlRfdForRequest.join('') + reqText;
return {
reqText: reqText,
redirects: redirects,
summary: ep.summary,
needQuery: needQuery
};
}
function generateSummary(section, redirectsCnt) {
var summary = dragoLib.trim($('#rfdh-summary-input').val());
summary = summary ? summary : '+' + redirectsCnt;
summary = `/*${section}*/ ${summary}${scriptAd}`;
return summary;
}
function preview() {
// Check if the necessary fields are filled and get edit information
var ep = editPrep1();
if (!ep) return;
// Preview dialog contour
const previewDiv =
'<div id="rfdh-preview-dialog" title="RFD Helper Preview" style="max-height: 80vh;">' +
' <div id="rfdh-preview-header" style="padding: 0.5em;">' +
' <p id="rfdh-preview-loading">' +
' プレビューを読み込み中' + dragoLib.toggleLoadingSpinner('add') +
' </p>' +
' <div id="rfdh-preview-warning" style="display: none;">' +
' <p>' +
' 転送先不明の項目は以下のいずれかに起因します:' +
' </p>' +
' <ol>' +
' <li>リダイレクト元のページ名が間違っている</li>' +
' <li>リダイレクト元として入力されたページがリダイレクトではない</li>' +
' <li>リダイレクト先の取得時に通信に失敗した</li>' +
' </ol>' +
' </div>' +
' </div>' +
' <div id="rfdh-preview-body" style="display: none; font-size: 1.1em; padding-top: 1em; border-top: 1px solid silver;">' +
' <div id="rfdh-preview-text" style="border: 1px solid silver; padding: 0.2em 0.5em; background: white;">' +
// previewHtml
' </div>' +
' <div id="rfdh-preview-summary" style="margin-top: 0.8em; border: 1px solid silver; padding: 0.2em 0.5em; background: white;">' +
// summaryHtml
' </div>' +
' <div id="rfdh-preview-checkbox-div" style="margin: 0.5em 0; display: none;">' +
' <input id="rfdh-preview-checkbox" type="checkbox">' +
' <label for="rfdh-preview-checkbox">プレビューを閉じる際に転送先不明の項目を自動的にリダイレクト元リストから除去</label>' +
' </div>' +
' </div>' +
'</div>';
// Show preview dialog
$('body').append(previewDiv);
$('#rfdh-preview-dialog').dialog({
dialogClass: 'rfdh-dialog-preview',
height: 'auto',
width: $('#content').width() * 0.8,
modal: true,
open: async function(){
// Initialize the design of the dialog
dragoLib.dialogCSS($('.rfdh-dialog-preview'), rfdhConfig.headerColor, rfdhConfig.backgroundColor, rfdhConfig.fontSize);
// Update REDIRS and get text and summary to preview
ep = await editPred2(ep);
// Convert text on the dialog to html
const parsed = await dragoLib.getParsedHtml(ep.reqText, ep.summary);
if (parsed) {
if (ep.needQuery) $('#rfdh-preview-checkbox-div').css('display', 'block').children('input').prop('checked', true);
const previewHtml = parsed.htmltext;
const summaryHtml = parsed.htmlsummary.replace(/API/g, RFDR);
$('#rfdh-preview-text').append(previewHtml);
$('#rfdh-preview-text a').each(function() { // Loop through all <a> tags in preview
if ($(this).text() === 'undefined') $(this).addClass('rfdh-preview-notarget').text('転送先不明'); // Highlight if undefined
});
$('#rfdh-preview-summary').append(summaryHtml);
$('.autocomment a').css('color', 'gray'); // Change color of section spec in summary
$('#rfdh-preview-dialog a').attr('target', '_blank'); // Open all links on a new tab
$('#rfdh-preview-body').css('display', 'block');
$('#rfdh-preview-loading').remove();
$('#rfdh-preview-warning').css('display', 'block');
dragoLib.centerDialog('#rfdh-preview-dialog');
} else {
$('#rfdh-preview-loading').text('プレビューの読み込みに失敗しました').css('color', 'MediumVioletRed');
dragoLib.centerDialog('#rfdh-preview-dialog');
setTimeout(function(){
$('#rfdh-preview-dialog').dialog('close');
}, 5000);
}
},
buttons: [{
text: '閉じる',
click: function(){
if ($('#rfdh-preview-checkbox').is(':checked')) {
const $input = $('#rfdh-redirectlist-input');
var inputVal = dragoLib.trim($input.val());
inputVal = inputVal.split('\n');
inputVal = inputVal.filter(function(item) {
return REDIRS[item]; // Remove the undefined
});
$input.val(inputVal.join('\n'));
}
$(this).dialog('close');
$('#rfdh-dialog')
.dialog({'buttons': btns})
.find('form').css('display', 'block');
}
}]
});
}
async function submitRequest() {
if (!dragoLib.inGroup('autoconfirmed')) { // Just in case
$('#rfdh-dialog, #ca-rfdh').remove();
return;
}
const $dialog = $('#rfdh-dialog');
// Check if the necessary fields are filled
var ep = editPrep1();
if (!ep) return;
// Hide the form and buttons and show a message
$dialog
.append(`<p id="rfdh-editting">準備中${dragoLib.toggleLoadingSpinner('add')}</p>`)
.dialog({buttons: []})
.find('form').css('display', 'none');
ep = await editPred2(ep);
if (ep.needQuery) { // If any redirect target failed to be fetched
$('#rfdh-editting').prop('innerHTML', '<span style="color: MediumVioletRed;">転送先不明のリダイレクトを検出しました</span>');
$dialog.dialog({
buttons: [{
text: 'プレビュー',
click: preview
}, {
text: '続行',
click: function() {
$(this).dialog({'buttons': []});
submitRequest2(ep);
}
}, {
text: '戻る',
click: function() {
$(this).find('form').css('display', 'block');
$(this).dialog({'buttons': btns});
$('#rfdh-editting').remove();
}
}, {
text: '中止',
click: function() {
$(this).remove();
}
}]
});
} else {
submitRequest2(ep);
}
}
async function submitRequest2(ep) {
$('#rfdh-editting').prop('innerHTML', '依頼を提出しています' + dragoLib.toggleLoadingSpinner('add'));
// Get timestamps
var lr = await dragoLib.getLatestRevision(RFDR);
if (!lr) return editDone(ep, 'ts');
lr = lr[0];
// Get section title
var sectiontitle = dragoLib.getSection5('依頼');
ep.section = sectiontitle;
// Get the content of the section to which the request will be submitted
var sections = dragoLib.parseContentBySection(lr.content);
sections = sections.filter(function(obj) {
return obj.title === sectiontitle;
});
if (sections.length === 0) return editDone(ep, 'sect');
var sectNum = sections[0].index;
// Check for duplicate requests
const duplicateRequests = [];
const redirectsEscaped = dragoLib.escapeRegExp(ep.redirects.join('|'));
const redirectRegExp = new RegExp(`(?:${redirectsEscaped})`);
const templates = dragoLib.findTemplates(lr.content, 'rfd'); // Extract RFD templates
if (templates.length !== 0) {
for (const tl of templates) { // Loop through all the extracted RFD templates and check if the 1st parameter contains the redirect(s) to be RFD-ed
let mtch;
if (mtch = tl.split('|')[1].match(redirectRegExp)) {
mtch = mtch[0].replace(/_/g, ' ');
if ($.inArray(mtch, duplicateRequests) === -1) duplicateRequests.push(mtch);
}
}
}
// Update ep
const drEscaped = dragoLib.escapeRegExp(duplicateRequests.join('|'));
const rqTxtRegExp = new RegExp('\\* \\{{2}RFD\\|(?:' + drEscaped + ')\\|.+\\}{2}\\n', 'g');
if (ep.reqText.match(rqTxtRegExp)) {
ep.reqText = ep.reqText.replace(rqTxtRegExp, ''); // Remove all duplicates from the report text
ep.updated = true; // Create a new property in ep which signals that reqText has been updated
}
var mtch;
if (!(mtch = ep.reqText.match(/\* {{RFD\|/g))) return editDone(ep, 'dr'); // If reqText has no RFD template in it, all the redirects have already been reported
ep.summary = generateSummary(sectiontitle, mtch.length); // Re-generate edit summary
// Edit page
const result = await new mw.Api().post({
action: 'edit',
title: RFDR,
section: sectNum,
appendtext: '\n\n' + ep.reqText,
basetimestamp: lr.basetimestamp,
starttimestamp: lr.curtimestamp,
summary: ep.summary,
token: debuggingMode.causeIntentionalError ? '' : mw.user.tokens.get('csrfToken'),
format: 'json'
}).then(function(res) {
if (res && res.edit) {
if (res.edit.result === 'Success') return true;
}
return false;
}).catch(function(code, err) {
return err.error.info;
});
switch (result) {
case true: // Edit succeeded
editDone(ep, true);
return;
case false: // Edit failed with an unknown error
editDone(ep, false);
return;
default: // Edit failed with a known error ('result' stores error info)
editDone(ep, result);
}
}
/**
* @param {*} ep
* @param {*} type 'dr' when request is cancelled because of duplicates, 'lr' when the latest revision failed to be fetched, 'sect' when section number
* failed to be fetcehd, true when edit succeeded, false when unexpected error occurred on edit, errcode when edit failed
*/
function editDone(ep, type) {
const drText = ep.updated ? ' (重複依頼分は除去されました)' : '';
const $msg = $('#rfdh-editting');
var editFailed = true, showCopyButton = true;
switch(type) {
case 'dr':
$msg.prop('innerHTML', '<span style="color: MediumVioletRed;">中止: 指定されたリダイレクトは既に全て依頼されています</span>');
showCopyButton = false;
break;
case 'lr':
$msg.prop('innerHTML', '<span style="color: MediumVioletRed;">失敗: 報告先の最新版が取得できませんでした</span>');
break;
case 'sect':
$msg.prop('innerHTML', '<span style="color: MediumVioletRed;">失敗: 報告先のセクション情報が取得できませんでした</span>');
break;
case true:
$msg.prop('innerHTML', `<span style="color: MediumSeaGreen;">成功: 依頼が完了しました${drText}</span>`);
editFailed = false;
showCopyButton = false;
break;
case false:
$msg.prop('innerHTML', '<span style="color: MediumSeaGreen;">失敗: ページの編集段階で不明なエラーが発生しました</span>');
break;
default:
$msg.prop('innerHTML', `<span style="color: MediumVioletRed;">失敗: ${type}</span>`);
break;
}
const $dialog = $('#rfdh-dialog');
$dialog.dialog({
buttons: [{
text: '報告先',
click: function() {
window.open(mw.util.getUrl(RFDR + '#' + ep.section), '_blank');
}
}, {
text: '閉じる',
click: function() {
$(this).dialog('close');
if (mw.config.get('wgPageName') === RFDR) location.reload(true);
}
}]
});
if (editFailed && showCopyButton) {
$dialog.append(
'<label for="rfdh-editfailed">手動編集用:</label>' +
'<input id="rfdh-editfailed" type="button" style="margin-left: 0.5em;" value="依頼文をコピー">'
);
}
$('#rfdh-editfailed').click(function() {
dragoLib.copyToClipboard(ep.reqText);
alert('コピーしました');
});
}
/**
* Check and update the redirect list textarea, and return its content as an array without duplicates
* @returns {Array}
*/
function getRedirectSources() {
const $input = $('#rfdh-redirectlist-input');
var inputVal = dragoLib.trim($input.val());
if (!inputVal) return [];
inputVal = inputVal.split('\n');
inputVal = inputVal.filter(function(item, index) {
return item !== '' && inputVal.indexOf(item) === index;
});
$input.val(inputVal.join('\n'));
return inputVal;
}
// ************************* EVENT HANDLERS *************************
// Clean up the redirect list textarea when the button is hit
$(document).off('click', '#rfdh-redirectlist-cleanup').on('click', '#rfdh-redirectlist-cleanup', function() {
getRedirectSources();
});
// Expand/shrink the summary textarea when the checkbox is (un)checked
$(document).off('click', '#rfdh-summary-checkbox').on('click', '#rfdh-summary-checkbox', function() {
const $textarea = $('#rfdh-summary-input');
if ($(this).is(':checked')) {
let redirectsCnt, summary;
summary = (redirectsCnt = getRedirectSources().length) === 0 ? '' : '+' + redirectsCnt;
$textarea.css('display', 'inline-block').val(summary);
} else {
$textarea.css('display', 'none').val('');
}
dragoLib.centerDialog('#rfdh-dialog');
});
// Remove the dialog html when closed
$(document).off('dialogclose', '#rfdh-dialog, #rfdh-preview-dialog').on('dialogclose', '#rfdh-dialog, #rfdh-preview-dialog', function() {
$(this).remove();
});
})();
//</nowiki>