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 = $('
this.$otherRow = $('
this.nameCell = $('
.append($('').text(this.name))[0];
this.refCell = $('
.append(this.$ref)[0];
this.moveButton = new OO.ui.ButtonWidget({
framed: false,
invisibleLabel: true
}).connect(dialog, { click: ['toggleActive', this] });
this.moveCell = $('
.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' });
}
$('
this.keepCheck && this.keepCheck.$element,
this.input.$element,
this.reapplyButton.$element,
this.propsButton.$element
).appendTo(this.$row);
}
initToggle() {
if (this.$toggle) return;
this.$toggle = $('
class: 'refrenamer-toggle oo-ui-icon-expand',
title: getMsg('expand')
}).on('click', onToggle).prependTo(this.refCell);
}
onPropChoose(item) {
this.input.insertContent(item.getTitle());
}
onKeepChange(selected) {
this.$row.toggleClass('refrenamer-kept', selected);
if (selected && !this.input.getValue()) {
dialog.applyConfig([this], true);
}
}
setupCollapsible(width) {
if (this.collapsible && Math.abs(width - this.refWidth) < 10) return;
this.refCell.classList.remove('refrenamer-collapsible');
this.collapsible = this.$ref[0].scrollHeight > this.$ref[0].clientHeight;
this.refCell.classList.toggle('refrenamer-collapsible', this.collapsible);
this.refWidth = width;
}
}
window.refRenamer = () => {
let encodedPn = encodeURIComponent(mw.config.get('wgPageName'));
let headers = {
'Api-User-Agent': 'RefRenamer (https://en.wikipedia.org/wiki/User:Nardog/RefRenamer)'
};
let dependencies = [
'mediawiki.storage', 'mediawiki.Title', 'oojs-ui-windows',
'oojs-ui-widgets', 'oojs-ui.styles.icons-interactions',
'oojs-ui.styles.icons-movement', 'oojs-ui.styles.icons-content',
'oojs-ui.styles.icons-editing-core'
];
let data = {
isEdit: !!document.getElementById('wpTextbox1') &&
!$('input[name=wpSection]').val(),
refs: [],
templates: new Set(),
invalid: new Set()
};
let promise;
if (data.isEdit) {
dependencies.push('jquery.textSelection');
} else {
dependencies.push('mediawiki.api', 'user.options');
data.started = performance.now();
notify('loadingSource');
promise = $.ajax('/w/rest.php/v1/page/' + encodedPn, { headers }).then(response => {
data.wikitext = response.source;
data.revId = response.latest.id;
data.editTime = response.latest.timestamp.replace(/\D/g, '');
});
}
$.when(mw.loader.using(dependencies), promise).then(() => {
if (data.isEdit) {
data.wikitext = $('#wpTextbox1').textSelection('getContents');
}
let wikitext = data.wikitext.replace(//g, '');
let match;
let re = /]+?))\s*\/?>/gi;
while ((match = re.exec(wikitext))) {
let name = match[1] || match[2] || match[3];
let normalized = normalize(name);
let ref = data.refs.find(r => r.normalized === normalized);
if (ref) {
ref.reused = true;
ref.names.add(name);
} else {
data.refs.push(new Ref(name, normalized));
}
}
if (!data.refs.length) throw 'nonames';
if (data.isEdit) {
notify('parsing');
return $.ajax('/api/rest_v1/transform/wikitext/to/html/' + encodedPn, {
type: 'POST',
data: { wikitext: data.wikitext, body_only: true },
headers: headers
});
} else {
notify('loadingHtml');
return $.ajax('/api/rest_v1/page/html/' + encodedPn, { headers });
}
}).then(response => {
let numbers = new Set();
let $page = $($.parseHTML(response));
$page.find(
'.mw-references:not([data-mw-group]) .mw-reference-text'
).each(function () {
let match = this.id.match(/^mw-reference-text-cite_note-(.+)-(\d+)$/);
if (!match) return;
let ref = data.refs.find(r => r.normalized === match[1]);
if (!ref) return;
if (ref.$ref) {
data.invalid.add(ref.name);
ref.invalid = true;
return;
}
ref.$ref = $(this);
ref.reused = ref.reused || (
ref.$ref.prev('span[rel="mw:referencedBy"]').children().length > 1
);
ref.$ref.remove().find('[id], [about]').addBack().removeAttr('id about');
ref.$ref.find('a').attr('target', '_blank')
.filter('[href^="./"]').attr('href', (_, href) => (
mw.format(mw.config.get('wgArticlePath'), href.slice(2))
));
numbers.add(match[2]);
});
$page.find(
'.mw-ref[typeof~="mw:Transclusion"], [typeof~="mw:Transclusion"] .mw-ref'
).filter(function () {
if (!/^\[\d+\]$/.test(this.textContent)) return;
let match = this.id.match(/_(\d+)-\d+$/);
return match && numbers.has(match[1]);
}).closest('[typeof~="mw:Transclusion"]').each(function () {
try {
data.templates.add(
JSON.parse(this.dataset.mw).parts[0].template.target.href
.replace(/^.\/Template:/, '')
);
} catch (e) {}
});
data.refs = data.refs.filter(ref => ref.$ref && !ref.invalid);
if (!data.refs.length) throw 'nonames';
let collator;
try {
collator = Intl.Collator(mw.config.get('wgContentLanguage') + '-u-kn-true');
} catch (e) {
collator = Intl.Collator('en-u-kn-true');
}
data.refs.sort((a, b) => collator.compare(a.name, b.name));
if (!dialog) initDialog();
dialog.open(data);
}).catch(e => {
OO.ui.alert(e === 'nonames' ? getMsg('noNamesAlert') : e.message);
}).always(() => {
if (notif) {
notif.close();
notif = null;
}
});
};
let initDialog = () => {
let rtl = document.dir === 'rtl';
let left = rtl ? 'right' : 'left';
let right = rtl ? 'left' : 'right';
mw.loader.addStyleTag(`.refrenamer .oo-ui-tabOptionWidget > .oo-ui-labelElement-label::after{content:" (" attr(data-refrenamer) ")"} .refrenamer-split{columns:2} .refrenamer-split .oo-ui-fieldLayout{break-inside:avoid} .refrenamer-split, .refrenamer .oo-ui-layout.oo-ui-labelElement:nth-child(n+2), .refrenamer .oo-ui-fieldLayout-align-inline .oo-ui-labelElement-label + *{margin:4px 0} .refrenamer .oo-ui-textInputWidget > .oo-ui-inputWidget-input{font-family:monospace,monospace} .refrenamer .wikitable{margin-bottom:0;overflow-wrap:break-word;width:100%} .refrenamer-maintable .refrenamer-name, .refrenamer .mw-reference-text, .refrenamer-othertable .refrenamer-name::after{font-size:90%} .refrenamer-unreused > .refrenamer-name::after{content:"${getMsg('notReused')}";display:inline-block} .refrenamer-unreused > .refrenamer-name > span::after{content:" "} .refrenamer-hidden, .refrenamer-unreused:not(.refrenamer-kept) > .refrenamer-newname > :not(.refrenamer-keepcheck), .refrenamer-maintable .refrenamer-toggle, :not(.refrenamer-collapsible) > .refrenamer-toggle{display:none} .refrenamer-name, .refrenamer-ref{vertical-align:top} .refrenamer-maintable .refrenamer-name{max-width:6em} .refrenamer-ref{line-height:normal} .refrenamer-newname{height:3em;position:relative;width:12em} .refrenamer-unreused:not(.refrenamer-kept) > .refrenamer-newname::after{content:"${getMsg('tableRemove')}"} .refrenamer-newname > .oo-ui-textInputWidget, .refrenamer-addremove > .oo-ui-buttonElement-frameless.oo-ui-iconElement, .refrenamer-toggle{margin:0;position:absolute;top:0;bottom:0;left:0;right:0} .refrenamer-newname textarea{height:100%;resize:none} .refrenamer-keepcheck{position:absolute;${right}:4px;top:50%;transform:translateY(-50%);margin:0;z-index:1} .refrenamer-keepcheck + .oo-ui-textInputWidget > textarea{padding-${right}:28px} .refrenamer-newname .oo-ui-buttonElement-frameless{position:absolute;${right}:0;bottom:0;margin:0} .refrenamer .refrenamer-reapplybutton{${right}:24px} .refrenamer-newname:not(:hover):not(:focus-within) > .oo-ui-buttonElement-frameless{opacity:0} .refrenamer-newname .oo-ui-buttonElement-frameless > .oo-ui-buttonElement-button{min-width:24px;min-height:24px;padding:0} .refrenamer-newname .oo-ui-buttonElement-frameless > .oo-ui-buttonElement-button > .oo-ui-iconElement-icon{background-size:16px 16px;left:0;right:0;margin:auto} .refrenamer-propsbutton .oo-ui-menuOptionWidget{font-size:85%;padding:2px 8px} .refrenamer-propsbutton .oo-ui-menuOptionWidget > .oo-ui-labelElement-label::before{content:attr(data-refrenamer);color:var(--color-subtle,#54595d);float:${right}} .refrenamer .refrenamer-addremove{padding:0;position:relative;width:32px;height:32px} .refrenamer-addremove .oo-ui-buttonElement-button{height:100%} .refrenamer-othertable{margin:0} .refrenamer-othertable .refrenamer-name{max-width:16em} .refrenamer-othertable .refrenamer-ref{position:relative} .refrenamer-othertable .refrenamer-collapsible{padding-right:20px} .refrenamer-toggle{width:100%;background-position:center ${right} 4px;background-repeat:no-repeat;background-size:12px 12px;background-color:transparent;border:none;cursor:pointer;z-index:1} .refrenamer-expanded > .refrenamer-toggle{width:20px;${left}:auto} .refrenamer-toggle:hover{background-color:var(--background-color-button-quiet--hover,rgba(0,24,73,0.027))} .refrenamer-othertable .mw-reference-text{overflow:hidden;display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical} .refrenamer-othertable .refrenamer-collapsible.refrenamer-expanded > .mw-reference-text{-webkit-line-clamp:unset}`);
function RefRenamerDialog(config) {
RefRenamerDialog.parent.call(this, config);
this.$element.addClass('refrenamer');
}
OO.inheritClass(RefRenamerDialog, OO.ui.ProcessDialog);
RefRenamerDialog.static.name = 'refRenamerDialog';
RefRenamerDialog.static.title = 'RefRenamer';
RefRenamerDialog.static.size = 'large';
RefRenamerDialog.static.actions = [
{
flags: ['safe', 'close'],
modes: ['main', 'mainReset', 'other', 'otherReset']
},
{
action: 'continue',
flags: ['primary', 'progressive'],
label: getMsg('continue'),
modes: ['main', 'mainReset', 'other', 'otherReset']
},
{
action: 'addAll',
flags: ['progressive'],
label: getMsg('addAll'),
modes: ['other', 'otherReset']
},
{
action: 'resetSelection',
label: getMsg('resetSelection'),
modes: ['main', 'other']
}
];
RefRenamerDialog.prototype.initialize = function () {
RefRenamerDialog.parent.prototype.initialize.apply(this, arguments);
this.index = new OO.ui.IndexLayout({
autoFocus: false
}).addTabPanels([
new OO.ui.TabPanelLayout('main', { label: getMsg('tableCaption') }),
new OO.ui.TabPanelLayout('other', { label: getMsg('otherTableCaption') })
]).on('set', () => {
let mode = this.index.getCurrentTabPanelName();
if (this.refs.every(ref => ref.active === ref.isAuto)) {
mode += 'Reset';
}
this.actions.setMode(mode);
this.updateSize();
this.setupCollapsibles();
});
this.warning = new OO.ui.MessageWidget({
showClose: true,
type: 'warning'
}).toggle().connect(this, { close: 'updateSize' });
this.mainSelect = new OO.ui.MenuTagMultiselectWidget({
input: { autocomplete: false },
options: [
{ data: 'aulast', label: getMsg('lastName') },
{ data: 'aufirst', label: getMsg('firstName') },
{ data: 'au', label: getMsg('author') },
{ data: 'jtitle', label: getMsg('periodical') },
{ data: 'pub|inst', label: getMsg('publisher') },
{ data: 'atitle|title', label: getMsg('article') },
{ data: 'btitle', label: getMsg('book') },
{ data: 'domain|linkDomain', label: getMsg('domain') },
{ data: 'phrase', label: getMsg('firstPhrase') }
]
}).connect(this, { change: 'updateSize', reorder: 'updateSize' });
this.lowercaseCheck = new OO.ui.CheckboxInputWidget();
this.removeDiaCheck = new OO.ui.CheckboxInputWidget();
this.removePunctCheck = new OO.ui.CheckboxInputWidget();
this.replaceSpaceCheck = new OO.ui.CheckboxInputWidget();
this.replaceSpaceLayout = new OO.ui.FieldLayout(this.replaceSpaceCheck, {
align: 'inline',
label: getMsg('replaceSpace')
});
this.replaceSpaceInput = new OO.ui.TextInputWidget({
autocomplete: false
}).toggle().connect(this, { toggle: 'updateSize' });
this.replaceSpaceCheck.connect(this.replaceSpaceInput, { change: 'toggle' });
this.replaceSpaceLayout.$header.append(this.replaceSpaceInput.$element);
this.yearCheck = new OO.ui.CheckboxInputWidget();
this.yearLayout = new OO.ui.FieldLayout(this.yearCheck, {
align: 'inline',
label: getMsg('year')
});
this.yearFallbackCheck = new OO.ui.CheckboxInputWidget();
this.yearConvertCheck = new OO.ui.CheckboxInputWidget();
this.yearConvertLayout = new OO.ui.FieldLayout(this.yearConvertCheck, {
align: 'inline',
label: getMsg('yearConvert')
});
this.latinIncrementCheck = new OO.ui.CheckboxInputWidget();
this.yearSubLayout = new OO.ui.FieldsetLayout({
items: [
new OO.ui.FieldLayout(this.yearFallbackCheck, {
align: 'inline',
label: getMsg('yearFallback')
}),
this.yearConvertLayout,
new OO.ui.FieldLayout(this.latinIncrementCheck, {
align: 'inline',
label: getMsg('latinIncrement')
})
]
}).toggle().connect(this, { toggle: 'updateSize' });
this.yearCheck.connect(this.yearSubLayout, { change: 'toggle' });
this.yearLayout.$header.append(this.yearSubLayout.$element);
this.incrementDropdown = new OO.ui.DropdownWidget({
menu: {
items: [
new OO.ui.MenuOptionWidget({ data: [1, false] }),
new OO.ui.MenuOptionWidget({ data: [2, false] }),
new OO.ui.MenuOptionWidget({ data: [0, true] }),
new OO.ui.MenuOptionWidget({ data: [1, true] })
]
}
});
this.incrementDropdown.updateLabels = () => {
let example = getMsg('incrementExample');
let delimiter = this.delimitConditionalCheck.isSelected()
? ''
: this.delimiterInput.getValue();
this.incrementDropdown.getMenu().getItems().forEach(item => {
let [start, incrementAll] = item.getData();
let label = getMsg(
'incrementExamples',
example + (incrementAll ? delimiter + start : ''),
example + delimiter + (start + Number(incrementAll))
);
item.setLabel(label);
if (item.isSelected()) {
this.incrementDropdown.setLabel(label);
}
});
};
this.delimiterInput = new OO.ui.TextInputWidget({
autocomplete: false
}).connect(this.incrementDropdown, { change: 'updateLabels' });
this.delimitConditionalCheck = new OO.ui.CheckboxInputWidget()
.connect(this.incrementDropdown, { change: 'updateLabels' });
this.removeUnreusedCheck = new OO.ui.CheckboxInputWidget();
this.applyButton = new OO.ui.ButtonInputWidget({
flags: ['primary', 'progressive'],
label: getMsg('apply'),
type: 'submit'
}).connect(this, { click: 'applyConfig' });
this.resetButton = new OO.ui.ButtonWidget({
label: getMsg('reset')
}).connect(this, { click: 'setConfig' });
this.form = new OO.ui.FormLayout({
items: [
this.warning,
new OO.ui.FieldLayout(this.mainSelect, {
align: 'top',
label: getMsg('main')
}),
new OO.ui.FieldsetLayout({
classes: ['refrenamer-split'],
items: [
new OO.ui.FieldLayout(this.lowercaseCheck, {
align: 'inline',
label: getMsg('lowercase')
}),
new OO.ui.FieldLayout(this.removeDiaCheck, {
align: 'inline',
label: getMsg('removeDia')
}),
new OO.ui.FieldLayout(this.removePunctCheck, {
align: 'inline',
label: getMsg('removePunct')
}),
this.replaceSpaceLayout,
this.yearLayout,
new OO.ui.FieldLayout(this.incrementDropdown, {
align: 'top',
label: getMsg('increment')
}),
new OO.ui.FieldLayout(this.delimiterInput, {
align: 'top',
label: getMsg('delimiter')
}),
new OO.ui.FieldLayout(this.delimitConditionalCheck, {
align: 'inline',
label: getMsg('delimitConditional')
})
]
}),
new OO.ui.FieldLayout(this.removeUnreusedCheck, {
align: 'inline',
label: getMsg('removeUnreused')
}),
this.applyButton,
this.resetButton
]
});
this.$tbody = $('
this.$table = $('
').text(getMsg('tableName')),
$(' | ').text(getMsg('tableRef')),
$(' | ').text(getMsg('tableNewName')),
$(' | ').text(getMsg('tableAddRemove'))
) ), this.$tbody ); this.index.getTabPanel('main').$element.append( this.form.$element, this.$table ); this.$otherTbody = $(' |
---|
').text(getMsg('tableName')),
$(' | ').text(getMsg('tableRef')),
$(' | ').text(getMsg('tableAddRemove'))
) ), this.$otherTbody ) ); this.$body.append(this.index.$element); this.defaults = { main: ['aulast', 'aufirst', 'au', 'jtitle', 'pub|inst', 'phrase'], lowercase: false, removeDia: false, removePunct: false, replaceSpace: false, year: true, yearFallback: false, yearConvert: true, latinIncrement: true, increment: 2, incrementAll: false, delimiter: '-', delimitConditional: false, removeUnreused: true }; }; RefRenamerDialog.prototype.getConfig = function () { let incrementData = this.incrementDropdown.getMenu() .findSelectedItem().getData(); return { main: this.mainSelect.getValue(), lowercase: this.lowercaseCheck.isSelected(), removeDia: this.removeDiaCheck.isSelected(), removePunct: this.removePunctCheck.isSelected(), replaceSpace: this.replaceSpaceCheck.isSelected() && this.replaceSpaceInput.getValue(), year: this.yearCheck.isSelected(), yearFallback: this.yearFallbackCheck.isSelected(), yearConvert: this.yearConvertCheck.isSelected(), latinIncrement: this.latinIncrementCheck.isSelected(), increment: incrementData[0], incrementAll: incrementData[1], delimiter: this.delimiterInput.getValue(), delimitConditional: this.delimitConditionalCheck.isSelected(), removeUnreused: this.removeUnreusedCheck.isSelected() }; }; RefRenamerDialog.prototype.setConfig = function (config) { config = Object.assign({}, this.defaults, config); this.mainSelect.setValue(config.main); this.lowercaseCheck.setSelected(config.lowercase); this.removeDiaCheck.setSelected(config.removeDia); this.removePunctCheck.setSelected(config.removePunct); let replaceSpace = typeof config.replaceSpace === 'string'; this.replaceSpaceCheck.setSelected(replaceSpace); this.replaceSpaceInput .setValue(replaceSpace ? config.replaceSpace : '-'); this.yearCheck.setSelected(config.year); this.yearFallbackCheck.setSelected(config.yearFallback); this.yearConvertCheck.setSelected(config.yearConvert); this.latinIncrementCheck.setSelected( // compatibility typeof config.latinIncrement === 'number' || config.latinIncrement ); let incrementMenu = this.incrementDropdown.getMenu(); let incrementItem = incrementMenu .findItemFromData([config.increment, config.incrementAll]); if (incrementItem) { incrementMenu.selectItem(incrementItem); } else { incrementMenu.selectItemByData([2, false]); } this.delimiterInput.setValue(config.delimiter); this.delimitConditionalCheck.setSelected(config.delimitConditional); this.removeUnreusedCheck.setSelected(config.removeUnreused); }; RefRenamerDialog.prototype.applyConfig = function (refs, forceKeep) { refs = (refs || this.refs).filter(ref => ref.active); if (!refs.length) return; let config = this.getConfig(), withYear = new Set(); let stack = config.main.flatMap(k => k.split('|')); let names = refs.map(ref => { if (!forceKeep && ref.keepCheck) { ref.keepCheck.setSelected(!config.removeUnreused); if (config.removeUnreused) return; } let exports = { name: ref.name, props: ref.props, getElement: () => ref.$ref.clone()[0] }; mw.hook('refrenamer.rename').fire(exports); if (typeof exports.newName === 'string') { return exports.newName; } let s; stack.some(k => (s = ref.props[k])); if (!s) return; if (config.lowercase) { s = s.toLowerCase(); } if (config.removeDia) { s = s.normalize('NFD').replace(/\p{Mn}/gu, ''); } if (config.removePunct) { s = s.replace(/\p{P}/gu, ''); } if (typeof config.replaceSpace === 'string') { s = s.replace(/\s+/g, config.replaceSpace); } let year = config.year && ( config.yearConvert && ref.props.yearAscii || ref.props.year || config.yearFallback && ( config.yearConvert && ref.props.textYearAscii || ref.props.textYear ) ); if (year) { let delimiter = config.delimitConditional && /\P{Nd}$/u.test(s) ? '' : config.delimiter; s += delimiter + year; withYear.add(ref); } return s; }); let comps = names.map(s => s && normalize(s)); let hardComps = new Set(); this.refs.forEach(ref => { if (refs.includes(ref)) return; hardComps.add( ref.active && normalize(ref.input.getValue()) || ref.normalized ); }); refs.forEach((ref, i) => { let s = names[i]; if (!s) { ref.input.setValue(''); return; } let normalized = comps[i]; let useLatin = config.latinIncrement && withYear.has(ref); let needsIncrement = hardComps.has(normalized) || comps.indexOf(normalized) !== i || (useLatin || config.incrementAll) && comps.slice(i + 1).includes(normalized); if (needsIncrement) { let unsuffixed = s; let delimiter = useLatin || ( config.delimitConditional && /\P{Nd}$/u.test(s) ) ? '' : config.delimiter; let increment = useLatin ? 0 : config.increment; do { s = unsuffixed + delimiter + (useLatin ? toLatin(increment) : increment); normalized = normalize(s); increment++; } while (hardComps.has(normalized) || comps.includes(normalized)); comps.push(normalized); } ref.input.setValue(s); }); }; RefRenamerDialog.prototype.setActive = function (refs, active) { refs.forEach(ref => { ref.setActive(active === undefined ? ref.isAuto : active); }); this.applyConfig(refs); let activeCount = this.refs.filter(ref => ref.active).length; let inactiveCount = this.refs.length - activeCount; let tabs = this.index.getTabs().getItems(); tabs[0].$label.attr('data-refrenamer', activeCount); tabs[1].setDisabled(!inactiveCount) .$label.attr('data-refrenamer', inactiveCount); this.$table.toggleClass('refrenamer-hidden', !activeCount); if (!activeCount) { this.index.setTabPanel('other'); } else if (!inactiveCount) { this.index.setTabPanel('main'); } else { this.index.emit('set'); } }; RefRenamerDialog.prototype.toggleActive = function (ref) { this.setActive([ref], !ref.active); }; RefRenamerDialog.prototype.getSetupProcess = function (data) { Object.assign(this, data); let warnings = []; if (this.templates.size) { warnings.push( document.createTextNode(getMsg('templatesWarn')), $('
[...this.templates].map(s => $(' |
---|