「利用者:Dragoniez/scripts/AN Reporter.js」の版間の差分
表示
削除された内容 追加された内容
v3.1: ダイアログのデザインの変更、修正、および改善 (特にスキンの違いによる表示の改善) |
v3.2: コードの可読性の向上、作業上明るみになった残存バグの修正 ほか |
||
2行目: | 2行目: | ||
* AN Reporter (ANR) |
* AN Reporter (ANR) |
||
* Author: Dragoniez |
* Author: Dragoniez |
||
* Version: 3. |
* Version: 3.2 |
||
*************************************/ |
*************************************/ |
||
//<nowiki> |
//<nowiki> |
||
20行目: | 20行目: | ||
// Define the position of the '報告' button |
// 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')) { |
switch(mw.config.get('skin')) { |
||
case 'vector': |
case 'vector': |
||
76行目: | 76行目: | ||
// CSS |
// 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 |
// Page names |
||
92行目: | 92行目: | ||
// Sections on WP:AN/I |
// Sections on WP:AN/I |
||
let sectionsI = |
|||
` <label for="anr-section-options-i" style="${labelCSS}">節</label>` + |
` <label for="anr-section-options-i" style="${labelCSS}">節</label>` + |
||
` <select id="anr-section-options-i" style="${siCSS}">` + |
` <select id="anr-section-options-i" style="${siCSS}">` + |
||
104行目: | 104行目: | ||
// Sections on WP:AN/S |
// Sections on WP:AN/S |
||
let sectionsS = |
|||
` <label for="anr-section-options-s" style="${labelCSS}">名称</label>` + |
` <label for="anr-section-options-s" style="${labelCSS}">名称</label>` + |
||
` <select id="anr-section-options-s" style="${siCSS}">` + |
` <select id="anr-section-options-s" style="${siCSS}">` + |
||
210行目: | 210行目: | ||
// Username input |
// Username input |
||
let userHtml = |
|||
// <div class="anr-user-div"> |
// <div class="anr-user-div"> |
||
` <div id="anr-user1-input-div">` + |
` <div id="anr-user1-input-div">` + |
||
236行目: | 236行目: | ||
// The whole html contour |
// The whole html contour |
||
let modalHtml = |
|||
// <div class="anr-modal-dialog" title="AN Reporter"> |
// <div class="anr-modal-dialog" title="AN Reporter"> |
||
` <div class="anr-modal-header">` + |
` <div class="anr-modal-header">` + |
||
301行目: | 301行目: | ||
// Get the name of the user to report if it can be retrieved from the page |
// 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 |
// Workaround to pick up IP ranges |
||
331行目: | 331行目: | ||
// Check if the necessary fields are filled and get edit information |
// Check if the necessary fields are filled and get edit information |
||
let ep = editPrep(); |
|||
if (ep.rqFieldsEmpty) { |
if (ep.rqFieldsEmpty) { |
||
alert('必須項目が入力・選択されていません'); // Show error and cancel the preview |
alert('必須項目が入力・選択されていません'); // Show error and cancel the preview |
||
339行目: | 339行目: | ||
// Preview dialog contour |
// Preview dialog contour |
||
const ANSMisc = `<a href="${mw.util.getUrl('WP:AN/S#OTH')}" target="_blank">WP:AN/S#その他</a>`; |
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 class="anr-preview-dialog" title="AN Reporter Preview">' + |
||
' <div id="anr-preview-header" style="padding: 0.5em;">' + |
' <div id="anr-preview-header" style="padding: 0.5em;">' + |
||
391行目: | 391行目: | ||
if (res && res.parse) { |
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-text').append(previewHtml); |
||
$('#anr-preview-summary').append(summaryHtml); |
$('#anr-preview-summary').append(summaryHtml); |
||
420行目: | 420行目: | ||
// Check if the necessary fields are filled and get edit information |
// Check if the necessary fields are filled and get edit information |
||
let ep = editPrep(); |
|||
if (ep.rqFieldsEmpty) { |
if (ep.rqFieldsEmpty) { |
||
alert('必須項目が入力・選択されていません'); // Show error and cancel the edit |
alert('必須項目が入力・選択されていません'); // Show error and cancel the edit |
||
426行目: | 426行目: | ||
} |
} |
||
// |
// Variables for edit |
||
let $dialog = $(this); |
|||
let width = $dialog.width(); |
|||
let msgEditing = |
|||
' <div class="anr-editing">' + |
' <div class="anr-editing">' + |
||
' <p>最新版を取得しています</p>' + |
' <p>最新版を取得しています</p>' + |
||
' </div>'; |
' </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.find('form').css('display', 'none'); // Hide dialog content |
||
$dialog.dialog('option', 'width', width); |
$dialog.dialog('option', 'width', width); |
||
442行目: | 447行目: | ||
//ep.editSummary = 'Test edit via mediawiki API'; |
//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){ |
|||
'formatversion': 2 |
|||
}).done(function(res1){ |
|||
if ( |
if (res && res.query && res.query.pages) { // If the latest revision is successfully retrieved |
||
if ( |
if (res.query.pages[0].missing !== true) { // If the page exists |
||
// Get the details of the latest revision |
// Get the details of the latest revision |
||
let baseTS = res.query.pages[0].revisions[0].timestamp; |
|||
let curTS = res.curtimestamp; |
|||
// Update message |
// Update message |
||
msgEditing = |
msgEditing = |
||
' <p style="color: MediumSeaGreen">取得に成功しました</p>' + |
' <p style="color: MediumSeaGreen">取得に成功しました</p>' + |
||
' <p>セクション番号を取得しています</p>'; |
' <p>セクション番号を取得しています</p>'; |
||
$('.anr-editing').append(msgEditing); |
$('.anr-editing').append(msgEditing); |
||
resolve({ |
|||
'baseTS': baseTS, |
|||
'curTS': curTS |
|||
}); |
|||
'action': 'parse', |
|||
'page': ep.pageToEdit, |
|||
'formatversion': 2 |
|||
}).done(function(res2){ |
|||
} 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); |
|||
} |
|||
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); |
|||
} else { // If revision retrieval fails |
|||
queryFailed(); |
|||
resolve(undefined); |
|||
} |
|||
}); |
|||
}); |
|||
} |
|||
// Function to get the section number from the section title |
|||
async function getSectionNumber() { |
|||
' <p style="color: MediumSeaGreen">取得に成功しました</p>' + |
|||
' <p>最新版のテキストを取得しています</p>'; |
|||
$('.anr-editing').append(msgEditing); |
|||
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 |
|||
if (ep.sectionToEdit === 'その他' && wikitextObtained.indexOf(miscHeader) === -1) { |
|||
ep.textToSubmit = '; ' + miscHeader + '\n\n' + ep.textToSubmit; |
|||
} |
|||
// Update message |
|||
msgEditing = |
|||
' <p style="color: MediumSeaGreen">取得に成功しました</p>' + |
|||
' <p>最新版のテキストを取得しています</p>'; |
|||
$('.anr-editing').append(msgEditing); |
|||
resolve(sectionNum); |
|||
wikitextSplit[0] + delimiter + trimA(wikitextSplit[1]) + '\n\n' + |
|||
ep.textToSubmit + '\n\n' + delimiter + wikitextSplit[2]; |
|||
} |
|||
} else { // If the section list retrieval fails |
|||
queryFailed(); |
|||
resolve(undefined); |
|||
} |
|||
}); |
|||
}); |
|||
} |
|||
$('.anr-editing').append(msgDone); |
|||
// Function to get the text to replace with the current text in the section |
|||
async function getTextToReplace(sectionNum) { |
|||
editDone($dialog, editFailed, wikiPagename); |
|||
return; |
|||
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}}`; |
|||
'data': { |
|||
'format': 'json', |
|||
'action': 'edit', |
|||
'title': ep.pageToEdit, |
|||
'section': sectionNum, |
|||
'summary': ep.editSummary, |
|||
'basetimestamp': baseTS, |
|||
'starttimestamp': curTS, |
|||
'text': wholeTextToSubmit, |
|||
'token': mw.user.tokens.get('csrfToken'), |
|||
'curtimestamp': true |
|||
}, |
|||
'dataType': 'json', |
|||
'type': 'POST', |
|||
success: function(res4) { |
|||
// If the edit was successful |
|||
if (res4 && res4.edit && res4.edit.result == 'Success') { |
|||
setTimeout(async function(){ |
|||
var diffNum = await getDiffNum(res4.curtimestamp); // Get diff number |
|||
// Show message |
|||
$('.anr-editing').append(`<p style="color: MediumSeaGreen">報告が完了しました</p>`); |
|||
$dialog.dialog('option', 'width', width); |
|||
editDone($dialog, editFailed, wikiPagename, diffNum); |
|||
}, 0); |
|||
if (ep.reportToANS) { // If the target is WP:AN/S |
|||
} else if (res4 && res4.error) { |
|||
// Show the details of the error |
|||
msgDone = |
|||
'<p style="color: MediumVioletRed">報告に失敗しました</p>' + |
|||
'<br>' + |
|||
'<p>詳細:</p>' + |
|||
`<p>${res4.error.info}</p>` + |
|||
manualEdit(); |
|||
$('.anr-editing').append(msgDone); |
|||
// 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 |
|||
editFailed = true; |
|||
editDone($dialog, editFailed, wikiPagename); |
|||
} |
|||
// 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 |
} else { // If the target is WP:AN/I or WP:AN/3RR |
||
queryFailed(); |
|||
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) { |
|||
'<p style="color: MediumVioletRed">エラー: 報告先のページが存在しません</p>' + manualEdit(); |
|||
$.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 to show message when edit attempt is done |
||
683行目: | 748行目: | ||
// Function to generate the html for the manul edit helper tab |
// Function to generate the html for the manul edit helper tab |
||
function manualEdit() { |
function manualEdit() { |
||
let meHtml = |
|||
'<br>' + |
'<br>' + |
||
'<p>手動編集用:</p>' + |
'<p>手動編集用:</p>' + |
||
692行目: | 757行目: | ||
return meHtml; |
return meHtml; |
||
} |
} |
||
// Report the user(s) |
|||
reportUser(); |
|||
} |
} |
||
706行目: | 774行目: | ||
// Dynamically change the content of the section dropdown depending on the value selected in '報告先' |
// Dynamically change the content of the section dropdown depending on the value selected in '報告先' |
||
$(document).on('change', '#anr-target-options', function(){ |
$(document).on('change', '#anr-target-options', function(){ |
||
let selectedTar = $(this).children('option').filter(':selected').text(); |
|||
switch(selectedTar) { |
switch(selectedTar) { |
||
case ANI: |
case ANI: |
||
733行目: | 801行目: | ||
}); |
}); |
||
// Add section name to the '報告先' link when section is specified |
// Add section name to the '報告先' link when section is specified |
||
$(document).on('change', '#anr-section-options-i', function(){ |
$(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; |
|||
} |
} |
||
}); |
|||
// Add section name to the '報告先' link when section is specified (for WP:AN/S) |
|||
$(document).on('change', '#anr-section-options-s', function(){ |
|||
var tarSection = ''; |
|||
if ($(this).find('option').filter(':selected').text() !== '選択してください') { |
if ($(this).find('option').filter(':selected').text() !== '選択してください') { |
||
tarSection = '#' + $(this).find('option').filter(':selected').text(); |
tarSection = '#' + $(this).find('option').filter(':selected').text(); |
||
$('#anr-target-a').attr('href', mw.util.getUrl( |
$('#anr-target-a').attr('href', mw.util.getUrl(tarPage + tarSection)); |
||
} |
} |
||
758行目: | 821行目: | ||
$(document).on('change','.anr-user-div select', function(e){ |
$(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 |
if (valSelected === 'UNL' || valSelected === 'User2' ) { // if type=UNL or User2 |
||
794行目: | 857行目: | ||
$(document).on('input', '.anr-user-div :text', function(e){ |
$(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); |
typeDropdown(inputID, selectID); |
||
resetDropdown(inputID, selectID); |
resetDropdown(inputID, selectID); |
||
802行目: | 865行目: | ||
// When 'hide username' is clicked, get logid, change dropdown options, show href and so on |
// 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){ |
$(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 |
if ($(checkboxID).is(':checked')) { // if the checkbox is checked |
||
// Function to update type dropdown |
// Function to update type dropdown |
||
let updateDropdown = function(logid) { |
|||
$(selectID).children('.anr-opt-UNL').prop('hidden', true); |
$(selectID).children('.anr-opt-UNL').prop('hidden', true); |
||
$(selectID).children('.anr-opt-User2').prop('hidden', true); |
$(selectID).children('.anr-opt-User2').prop('hidden', true); |
||
826行目: | 889行目: | ||
} |
} |
||
let logid; |
|||
if (objLogid[inputVal] !== undefined) { |
if (objLogid[inputVal] !== undefined) { |
||
890行目: | 953行目: | ||
// When the 'add' button is hit, add another input layer |
// When the 'add' button is hit, add another input layer |
||
let userCnt = 1; |
|||
$('.anr-addBtn').click(function(){ |
$('.anr-addBtn').click(function(){ |
||
userCnt++; |
userCnt++; |
||
let replaceTar = new RegExp(`${userCnt-1}-`, 'g'); |
|||
userHtml = userHtml.replace(replaceTar, `${userCnt}-`); // 1 → 2, 2 → 3 and so forth |
userHtml = userHtml.replace(replaceTar, `${userCnt}-`); // 1 → 2, 2 → 3 and so forth |
||
$('.anr-btn-div').before(userHtml); |
$('.anr-btn-div').before(userHtml); |
||
907行目: | 970行目: | ||
// When the summary checkbox is (un)checked |
// When the summary checkbox is (un)checked |
||
$('#anr-summary-checkbox' |
$(document).on('change', '#anr-summary-checkbox', function(){ |
||
let $textarea = $('#anr-summary-text'); |
|||
if ($(this).is(':checked')) { // Box is checked |
if ($(this).is(':checked')) { // Box is checked |
||
// Show textarea |
// Show textarea |
||
$textarea.css('display','inline-block'). |
$textarea.css('display','inline-block').val(genEditSummary()); |
||
} else { // Box is unchecked |
} else { // Box is unchecked |
||
$textarea.css('display','none'). |
$textarea.css('display','none').val(''); |
||
} |
} |
||
}); |
}); |
||
924行目: | 987行目: | ||
function editPrep() { |
function editPrep() { |
||
let rqFieldsEmpty = false; |
|||
// Check if at least one username is given and get users to report (and their UserAN types) |
// 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 |
for (let i = 1; i < Infinity; i++) { // Loop through all inputs |
||
if ($(`#anr-user${i}-input`).length === 0) { // if selector is not found |
if ($(`#anr-user${i}-input`).length === 0) { // if selector is not found |
||
941行目: | 1,004行目: | ||
// Get the name of the section to edit |
// 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 |
if (pageToEdit === ANI) { // If WP:AN/I is selected as the target page to edit |
||
989行目: | 1,052行目: | ||
// UserAN template and the reason of the report |
// UserAN template and the reason of the report |
||
const UserAN = '{{UserAN|t=TYPE|USER}}'; |
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 |
if (reason.substring(reason.length - 4) !== '~~~~') { // If reason doesn't contain signature, add one |
||
reason = reason + '--~~~~'; |
reason = reason + '--~~~~'; |
||
997行目: | 1,060行目: | ||
const editSummarySection = '/*' + sectionToEdit + '*/'; |
const editSummarySection = '/*' + sectionToEdit + '*/'; |
||
let editSummary = |
|||
trimA($('#anr-summary-text').val()) === '' ? |
trimA($('#anr-summary-text').val()) === '' ? |
||
editSummarySection + genEditSummary() + scriptAd: |
editSummarySection + genEditSummary().replace(' - ', '') + scriptAd: |
||
editSummarySection + trimA($('#anr-summary-text').val()) + scriptAd; |
editSummarySection + trimA($('#anr-summary-text').val()) + scriptAd; |
||
// Get text to add to the page |
// Get text to add to the page |
||
let textToSubmit = ''; |
|||
if (users.length < 2) { // If user to report is just one |
if (users.length < 2) { // If user to report is just one |
||
textToSubmit = '\* ' + UserAN.replace('TYPE', types[0]).replace('USER', users[0]) + ' |
textToSubmit = '\* ' + UserAN.replace('TYPE', types[0]).replace('USER', users[0]) + '- ' + reason; |
||
} else { // If two or more |
} else { // If two or more |
||
for (let i = 0; i < users.length; i++) { |
for (let i = 0; i < users.length; i++) { |
||
1,039行目: | 1,102行目: | ||
// Get the page name without a section specifier |
// Get the page name without a section specifier |
||
let tarPage = wikiPagename.split('#')[0]; |
|||
// Button to jump to diff |
// Button to jump to diff |
||
let btns = []; |
|||
let diffBtn, destBtn, closeBtn; |
|||
if (diffNum !== undefined) { // Show the button only if diff number is available |
if (diffNum !== undefined) { // Show the button only if diff number is available |
||
diffBtn = { |
diffBtn = { |
||
1,070行目: | 1,133行目: | ||
'click': function(){ |
'click': function(){ |
||
$(this).dialog('close'); |
$(this).dialog('close'); |
||
let curPage = mw.config.get('wgPageName'); |
|||
if ( |
if ( |
||
curPage === ANI || |
curPage === ANI || |
||
1,100行目: | 1,163行目: | ||
function genEditSummary() { |
function genEditSummary() { |
||
let inputRemains = true; |
|||
let i = 1; |
let i = 1; |
||
let arrOfContribs = []; |
|||
let contribs = ''; |
|||
let type = ''; |
|||
let reportee = ''; |
|||
// Check content of all inputs into which usernames are typed |
// Check content of all inputs into which usernames are typed |
||
1,149行目: | 1,212行目: | ||
// Get edit summary |
// Get edit summary |
||
let textToShow = ''; |
|||
if (arrOfContribs.length === 0) { |
if (arrOfContribs.length === 0) { |
||
// Do nothing |
// Do nothing |
||
} else if (arrOfContribs.length === 1) { |
} else if (arrOfContribs.length === 1) { |
||
textToShow += '+' + arrOfContribs[0]; |
textToShow += '+' + arrOfContribs[0] + ' - '; |
||
} else { |
} else { |
||
textToShow += '+' + arrOfContribs.join(', '); |
textToShow += '+' + arrOfContribs.join(', ') + ' - '; |
||
} |
} |
||
1,165行目: | 1,228行目: | ||
// Function to get the last day of the month |
// Function to get the last day of the month |
||
let lastDay = function(y,m){ |
|||
return new Date(y, m +1, 0).getDate(); |
return new Date(y, m +1, 0).getDate(); |
||
} |
} |
||
1,171行目: | 1,234行目: | ||
// Function to get the current date and the section name to which to report |
// Function to get the current date and the section name to which to report |
||
function getSectionI(){ |
function getSectionI(){ |
||
let d = new Date(); |
|||
let curSection; |
|||
switch(true) { |
switch(true) { |
||
case (1 <= d.getDate() && d.getDate() <= 5): |
case (1 <= d.getDate() && d.getDate() <= 5): |
||
1,200行目: | 1,263行目: | ||
// Function to get today's date |
// Function to get today's date |
||
function today() { |
function today() { |
||
let d = new Date(); |
|||
return d.getMonth()+1 + '月' + d.getDate() + '日'; |
return d.getMonth()+1 + '月' + d.getDate() + '日'; |
||
} |
} |
||
1,223行目: | 1,286行目: | ||
// Function to manipulate dropdown options for UserAN types (also maniputes show/hide of checkbox) |
// Function to manipulate dropdown options for UserAN types (also maniputes show/hide of checkbox) |
||
let typeDropdownTimeout; |
|||
function typeDropdown(inputID, selectID, setNone) { |
function typeDropdown(inputID, selectID, setNone) { |
||
1,231行目: | 1,294行目: | ||
} |
} |
||
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 |
clearTimeout(typeDropdownTimeout); // Run the async function only once when there's been no input change for 0.35 seconds |
||
1,257行目: | 1,321行目: | ||
$(selectID).children('.anr-opt-none').prop('hidden', false); |
$(selectID).children('.anr-opt-none').prop('hidden', false); |
||
$(checkboxDivID).css('display', 'none'); // hide 'hide username' checkbox |
$(checkboxDivID).css('display', 'none'); // hide 'hide username' checkbox |
||
$(checkboxID).prop('checked', false); // uncheck the checkbox |
$(checkboxID).prop('checked', false); // uncheck the checkbox |
||
$(aDivID).css('display', 'none'); |
|||
} else if (await userExists(tarVal)) { // if user |
} else if (await userExists(tarVal)) { // if user |
||
1,268行目: | 1,333行目: | ||
$(selectID).children('.anr-opt-none').prop('hidden', false); |
$(selectID).children('.anr-opt-none').prop('hidden', false); |
||
$(checkboxDivID).css('display', 'block'); // show 'hide username' checkbox |
$(checkboxDivID).css('display', 'block'); // show 'hide username' checkbox |
||
$(checkboxID).prop('checked', false); // uncheck the checkbox |
$(checkboxID).prop('checked', false); // uncheck the checkbox |
||
$(aDivID).css('display', 'none'); |
|||
} else { // if something else (like random numbers or strings) |
} else { // if something else (like random numbers or strings) |
||
1,283行目: | 1,349行目: | ||
} |
} |
||
$(checkboxDivID).css('display', 'none'); // hide 'hide username' checkbox |
$(checkboxDivID).css('display', 'none'); // hide 'hide username' checkbox |
||
$(checkboxID).prop('checked', false); // uncheck the checkbox |
$(checkboxID).prop('checked', false); // uncheck the checkbox |
||
$(aDivID).css('display', 'none'); |
|||
} |
} |
||
1,295行目: | 1,362行目: | ||
function resetDropdown(inputID, selectID) { |
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()) === '') { |
if (trimA($(inputID).val()) === '') { |
||
1,340行目: | 1,407行目: | ||
}).done(function(res){ |
}).done(function(res){ |
||
if (res && res.query && res.query.allrevisions) { |
if (res && res.query && res.query.allrevisions) { |
||
let revArr = res.query.allrevisions; |
|||
for (let i = 0; i < revArr.length; i++) { |
for (let i = 0; i < revArr.length; i++) { |
||
if (revArr[i].revisions[0].timestamp === curtimestamp) { |
if (revArr[i].revisions[0].timestamp === curtimestamp) { |
2022年2月18日 (金) 17:16時点における版
/*************************************
* AN Reporter (ANR)
* Author: Dragoniez
* Version: 3.2
*************************************/
//<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;' +
' }' +
' .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 =
` <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>`;
// Sections on WP:AN/S
let sectionsS =
` <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>`;
// 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
}
},
'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 **********
// 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');
})
.hover(function(){
$(this).css('border-color', '#999999');
}, function(){
$(this).css('border-color', '#d3d3d3');
});
// 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('');
}
});
// ********** USER-DEFINED 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;
}
});
// 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>