User:Nardog/RefRenamer-core.js

(function refRenamerCore() {

let messages = Object.assign({

loadingSource: 'Loading the source...',

loadingHtml: 'Loading HTML...',

parsing: 'Parsing wikitext...',

opening: 'Opening the diff...',

continue: 'Continue',

main: 'Main fallback stack:',

lastName: 'Last name',

firstName: 'First name',

author: 'Author',

periodical: 'Periodical/website',

publisher: 'Publisher',

article: 'Article',

book: 'Book',

domain: 'Domain',

firstPhrase: 'First phrase',

lowercase: 'Lowercase',

removeDia: 'Remove diacritics',

removePunct: 'Remove punctuation',

replaceSpace: 'Replace space with:',

year: 'Year',

yearFallback: 'Fall back on any 4-digit number',

yearConvert: 'Convert to ASCII',

latinIncrement: 'Append Latin letters on collision',

increment: 'Collision resolution:',

incrementExample: 'Example',

incrementExamples: '$1, $2...',

delimiter: 'Delimiter:',

delimitConditional: 'Insert delimiters only after numerals',

removeUnreused: 'Remove unreused names',

apply: 'Apply',

reset: 'Reset',

tableName: 'Name',

tableCaption: 'References to rename',

tableRef: 'Reference',

tableNewName: 'New name',

tableAddRemove: '±',

reapplyTooltip: 'Reapply current options',

propsTooltip: 'View/insert properties',

keepTooltip: 'Uncheck to remove',

tableRemove: '(Remove)',

removeTooltip: 'Remove from references to rename',

otherTableCaption: 'Other named references',

notReused: '(not reused)',

expand: 'Expand',

collapse: 'Collapse',

addTooltip: 'Add to references to rename',

addAll: 'Add all',

resetSelection: 'Reset selection',

noNamesAlert: 'The source does not contain ref names to rename.',

noChangesError: 'No names have been modified.',

numericError: 'The following names are invalid as they consist only of numerals:',

duplicatesError: 'The following names are already used or input more than once:',

templatesWarn: 'Ref names in the following templates will not be replaced:',

invalidWarn: 'The following names have been ignored because it could not be determined which references correspond to them:',

summary: 'Replaced VE ref names using RefRenamer',

genericSummary: 'Renamed references using RefRenamer'

}, window.refrenamerMessages);

let getMsg = (key, ...args) => (

messages.hasOwnProperty(key) ? mw.format(messages[key], ...args) : key

);

let notif;

let notify = key => {

mw.notify(getMsg(key), {

autoHideSeconds: 'long',

tag: 'refrenamer'

}).then(n => {

notif = n;

});

};

let dialog;

class Ref {

constructor(name, normalized) {

this.name = name;

this.names = new Set([name]);

this.normalized = normalized;

this.isVe = /^:\d+$/.test(name);

this.isAuto = this.isVe || /^auto(?:generated)?\d*$/.test(name);

}

initProps() {

this.props = {};

let coinsSpan = this.$ref[0].querySelector('.Z3988');

if (coinsSpan) {

new URLSearchParams(coinsSpan.title).forEach((v, k) => {

if (k.startsWith('rft.')) {

this.props[k.slice(4)] = v;

} else if (k === 'rft_id') {

if (/^https?:/.test(v)) {

if (this.props.domain) return;

try {

let url = new URL(v);

this.props.domain = url.hostname;

} catch (e) {}

} else {

let match = v.match(/^info:([^\/]+)\/(.+)$/);

if (match) {

this.props[match[1]] = match[2];

}

}

}

});

}

let text = this.$ref.text();

if (this.props.date) {

let numbers = this.props.date.match(/\p{Nd}+/gu);

if (numbers) {

let converted = numbers.map(n => toAscii(n));

let year = String(Math.max(...converted));

let original = numbers[converted.indexOf(year)];

this.props.year = original;

if (original !== year) {

this.props.yearAscii = year;

}

}

} else {

let match = text.match(/(?:^|\P{Nd})(\p{Nd}{4})(?!\p{Nd})/u);

if (match) {

this.props.textYear = match[1];

let ascii = toAscii(match[1]);

if (ascii !== match[1]) {

this.props.textYearAscii = ascii;

}

}

}

let link = this.$ref[0].querySelector('a.external');

if (link && link.hostname !== this.props.domain) {

this.props.linkDomain = link.hostname;

}

let match = text.match(/[^\s\p{P}].*?(?=\s*(?:\p{P}|$))/u);

if (match) {

this.props.phrase = match[0];

}

Object.freeze(this.props);

}

initRows() {

let rowClass = !this.reused && 'refrenamer-unreused';

this.$row = $('').addClass(rowClass).appendTo(dialog.$tbody);

this.$otherRow = $('').addClass(rowClass).appendTo(dialog.$otherTbody);

this.nameCell = $('').addClass('refrenamer-name')

.append($('').text(this.name))[0];

this.refCell = $('').addClass('refrenamer-ref mw-parser-output')

.append(this.$ref)[0];

this.moveButton = new OO.ui.ButtonWidget({

framed: false,

invisibleLabel: true

}).connect(dialog, { click: ['toggleActive', this] });

this.moveCell = $('').addClass('refrenamer-addremove')

.append(this.moveButton.$element)[0];

}

setActive(active) {

if (active === this.active) return;

this.active = active;

this[active ? '$otherRow' : '$row'].addClass('refrenamer-hidden');

let tooltip = getMsg(active ? 'removeTooltip' : 'addTooltip');

this.moveButton

.setFlags({ destructive: active, progressive: !active })

.setIcon(active ? 'subtract' : 'add')

.setLabel(tooltip).setTitle(tooltip);

this[active ? 'initInput' : 'initToggle']();

this[active ? '$row' : '$otherRow']

.prepend(this.nameCell, this.refCell)

.append(this.moveCell)

.removeClass('refrenamer-hidden');

}

initInput() {

if (this.input) return;

this.input = new OO.ui.MultilineTextInputWidget({

allowLinebreaks: false,

placeholder: this.name,

spellcheck: false

}).connect(dialog, { enter: ['executeAction', 'continue'] });

this.reapplyButton = new OO.ui.ButtonWidget({

classes: ['refrenamer-reapplybutton'],

framed: false,

icon: 'undo',

invisibleLabel: true,

label: getMsg('reapplyTooltip')

}).connect(dialog, {

click: ['applyConfig', [this], true]

}).connect(this.input, { click: 'focus' });

this.propsButton = new OO.ui.ButtonMenuSelectWidget({

classes: ['refrenamer-propsbutton'],

clearOnSelect: true,

framed: false,

icon: 'downTriangle',

invisibleLabel: true,

label: getMsg('propsTooltip'),

menu: {

$floatableContainer: this.input.$element,

horizontalPosition: 'end',

width: '16em'

}

});

this.propsButton.getMenu().addItems(

Object.entries(this.props).map(([k, v]) => {

let option = new OO.ui.MenuOptionWidget({

label: v,

title: v

});

option.$label.attr('data-refrenamer', k);

return option;

})

).connect(this, { choose: 'onPropChoose' });

if (!this.reused) {

this.keepCheck = new OO.ui.CheckboxInputWidget({

classes: ['refrenamer-keepcheck'],

invisibleLabel: true,

label: getMsg('keepTooltip')

}).connect(this, { change: 'onKeepChange' });

}

$('').addClass('refrenamer-newname').append(

this.keepCheck && this.keepCheck.$element,

this.input.$element,

this.reapplyButton.$element,

this.propsButton.$element

).appendTo(this.$row);

}

initToggle() {

if (this.$toggle) return;

this.$toggle = $('