User:Jeeputer/InternationalRefRenamer-core.js

// The Original script is written by User:Nardog at User:Nardog/RefRenamer-core.js // This is just a fork to support i18n // for more information see https://en.wikipedia.org/wiki/Special:Permalink/1140828312#i18n

(function refRenamerCore {	//Added for i18n	let i18n = key => {		let message = window.RefRenameri18n[key];		let out;		let iw = mw.config.get('wgWikiID') === 'enwiki' ? '' : 'w:en:';		if (key === 'summary') {			out = message.replace(/\$1/g, iw);		} else {			out = message;		}		return out;	};	let dialog;	let encodedPn = encodeURIComponent(mw.config.get('wgPageName'));	let headers = {		'Api-User-Agent': i18n('tool-name') + ' (https://en.wikipedia.org/wiki/User:Jeeputer/InternationalRefRenamer.js)'	};	window.refRenamer =  => {		let isEdit = document.getElementById('wpTextbox1') && !$('input[name=wpSection]').val;		let promise, wikitext;		let dependencies = [			'mediawiki.storage', 'oojs-ui-windows', 'oojs-ui-widgets',			'ext.cite.style', 'ext.cite.styles'		];		if (isEdit) {			dependencies.push('jquery.textSelection');		} else {			dependencies.push('mediawiki.api', 'user.options');			promise = $.ajax('/api/rest_v1/page/html/' + encodedPn, { headers }); notify(i18n('html-loading')); }		mw.loader.using(dependencies).then( => {			if (isEdit) {				wikitext = $('#wpTextbox1').textSelection('getContents');				promise = $.ajax('/api/rest_v1/transform/wikitext/to/html/' + encodedPn, { type: 'POST', data: { wikitext: wikitext, body_only: true }, headers: headers, });				notify(i18n('parsing-wikitext'));			}			return promise;		}).then(response => {			let $page = $($.parseHTML(response));			let names = [];			let $refs = $page.find('.mw-references:not([data-mw-group]) .mw-reference-text');			let refs = $refs.map(function (i) { let match = this.id.match(/^mw-reference-text-cite_note-(.+)-\d+$/); if (!match) return; let name = match[1]; if (!/^:\d+$/.test(name)) { names.push(name); return; }				let $ref = $refs.eq(i); let ref = { number: name.slice(1), reused: $ref.prev.is('span[rel="mw:referencedBy"]'), $ref: $ref.clone, coins: {} };				ref.$ref.find('[id], [about]').addBack.removeAttr('id about'); ref.$ref.find('a').attr('target', '_blank') .filter('[href^="./"]').attr('href', function {						return mw.format( mw.config.get('wgArticlePath'), this.getAttribute('href').slice(2) );					});				let span = this.querySelector('.Z3988'); if (span) { new URLSearchParams(span.title).forEach((v, k) => {						if (k.startsWith('rft.')) {							ref.coins[k.slice(4)] = v;						}					}); }				let text = this.textContent; ref.phrase = (text.match(/[^\s\p{P}].*?(?=\s*(?:\p{P}|$))/u) || [])[0]; ref.year = (text.match(/(?:^|\P{N})(\p{N}{4})(?!\p{N})/u) || [])[1]; return ref; }).get.sort((a, b) => a.number - b.number);			mw.requestIdleCallback( => { let notif = $('.mw-notification-tag-refrenamer').data('mw-notification'); if (notif) notif.close; });			if (!refs.length) {				OO.ui.alert(i18n('no-ref-to-rename'));				return;			}			if (!dialog) initDialog;			dialog.open({ refs, names, wikitext });		}); };	let initDialog = => { mw.loader.addStyleTag('.refrenamer .oo-ui-window-body{padding:1em} .refrenamer .oo-ui-layout.oo-ui-labelElement.oo-ui-fieldLayout.oo-ui-fieldLayout-align-inline{margin-top:4px;margin-bottom:4px} .refrenamer-subinput{padding-left:2em} .refrenamer .wikitable td:first-child{text-align:center} .refrenamer .mw-reference-text{font-size:90%;word-break:break-word} .refrenamer .wikitable td:last-child{height:3em;position:relative;width:16em} .refrenamer .wikitable .oo-ui-textInputWidget{position:absolute;top:0;left:0;height:100%} .refrenamer .wikitable .oo-ui-inputWidget-input{height:100%;resize:none;scrollbar-width:none} .refrenamer .oo-ui-textInputWidget-type-text > .oo-ui-inputWidget-input{font-family:monospace;font-size:revert} .refrenamer .wikitable .oo-ui-checkboxInputWidget{float:right;margin-right:0;z-index:1} .refrenamer .wikitable .oo-ui-checkboxInputWidget ~ .oo-ui-textInputWidget-type-text > .oo-ui-inputWidget-input{padding-right:2em}'); 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 = i18n('dialog-title'); RefRenamerDialog.static.size = 'large'; RefRenamerDialog.static.actions = [ {				flags: ['safe', 'close'] },			{				action: 'continue', label: i18n('continue'), flags: ['primary', 'progressive'] }		];		RefRenamerDialog.prototype.initialize = function { RefRenamerDialog.parent.prototype.initialize.apply(this, arguments); this.mainSelect = new OO.ui.MenuTagMultiselectWidget({				options: [					{ data: 'aulast', label: i18n('aulast-label') },					{ data: 'aufirst', label: i18n('aufirst-label') },					{ data: 'au', label: i18n('au-label') },					{ data: 'jtitle', label: i18n('jtitle-label') },					{ data: 'pub|inst', label: i18n('publisher-label') },					{ data: 'atitle|title', label: i18n('article-title-label') },					{ data: 'btitle', label: i18n('book-title-label') },					{ data: 'phrase', label: i18n('phrase-label') }				]			}); this.lowercaseCheck = new OO.ui.CheckboxInputWidget; this.removeDiaCheck = new OO.ui.CheckboxInputWidget; this.removePunctCheck = new OO.ui.CheckboxInputWidget; this.repSpaceCheck = new OO.ui.CheckboxInputWidget.on('change', selected => {				this.repSpaceInput.toggle(selected);				this.updateSize;			}); this.repSpaceInput = new OO.ui.TextInputWidget({				classes: ['refrenamer-subinput']			}).toggle; this.yearCheck = new OO.ui.CheckboxInputWidget.on('change', selected => {				this.digitsCheckLayout.toggle(selected);				this.yearDivInputLayout.toggle(selected);				this.updateSize;			}); this.digitsCheck = new OO.ui.CheckboxInputWidget; this.digitsCheckLayout = new OO.ui.FieldLayout(this.digitsCheck, {				label: i18n('digits-check-label'),				align: 'inline',				classes: ['refrenamer-subinput']			}); this.yearDivInput = new OO.ui.TextInputWidget; this.yearDivInputLayout = new OO.ui.FieldLayout(this.yearDivInput, {				label: i18n('year-div-input-label'),				align: 'top'			}); this.incrementInput = new OO.ui.NumberInputWidget({				min: 0			}); this.incrementDivInput = new OO.ui.TextInputWidget; this.forceIncrementCheck = new OO.ui.CheckboxInputWidget; this.divConditionalCheck = new OO.ui.CheckboxInputWidget; this.removeUnreusedCheck = new OO.ui.CheckboxInputWidget; this.applyButton = new OO.ui.ButtonInputWidget({				label: i18n('apply'),				flags: ['progressive'],				type: 'submit'			}).connect(this, { click: 'applyConfig' }); this.resetButton = new OO.ui.ButtonWidget({				label: i18n('reset')			}).connect(this, { click: 'setConfig' }); this.form = new OO.ui.FormLayout({				items: [					new OO.ui.FieldLayout(this.mainSelect, { label: i18n('main-fallback-label'), align: 'top' }),					new OO.ui.FieldLayout(this.lowercaseCheck, { label: i18n('lowercase-label'), align: 'inline' }),					new OO.ui.FieldLayout(this.removeDiaCheck, { label: i18n('remove-diacritics-label'), align: 'inline' }),					new OO.ui.FieldLayout(this.removePunctCheck, { label: i18n('remove-punctuation-label'), align: 'inline' }),					new OO.ui.FieldLayout(this.repSpaceCheck, { label: i18n('replace-space-label'), align: 'inline' }),					this.repSpaceInput,					new OO.ui.FieldLayout(this.yearCheck, { label: i18n('year'), align: 'inline' }),					this.digitsCheckLayout,					this.yearDivInputLayout,					new OO.ui.FieldLayout(this.incrementInput, { label: i18n('increment-start-label'), align: 'top' }),					new OO.ui.FieldLayout(this.incrementDivInput, { label: i18n('delimiter-pre-increment'), align: 'top' }),					new OO.ui.FieldLayout(this.forceIncrementCheck, { label: i18n('increment-always'), align: 'inline' }),					new OO.ui.FieldLayout(this.divConditionalCheck, { label: i18n('delimiter-only-numerals'), align: 'inline' }),					new OO.ui.FieldLayout(this.removeUnreusedCheck, { label: i18n('remove-unreused-names'), align: 'inline' }),					this.applyButton,					this.resetButton				]			}); this.$tbody = $(' '); this.namesTextarea = new OO.ui.MultilineTextInputWidget({				autosize: true,				readOnly: true			}); this.namesTextareaLayout = new OO.ui.FieldLayout(this.namesTextarea, {				label: i18n('names-text-area-label'),				align: 'top'			}); this.$body.addClass('mw-parser-output').append(				this.form.$element,				$(' ').addClass('wikitable').append( $(' ').append($(' ').append( $(' ').text('#'), $(' ').text(i18n('reference')), $(' ').text(i18n('new-name')) )),					this.$tbody ),				this.namesTextareaLayout.$element			); this.defaults = { main: ['aulast', 'aufirst', 'au', 'jtitle', 'pub|inst', 'phrase'], lowercase: false, removeDia: false, removePunct: false, repSpace: false, year: true, digits: false, yearDiv: '-', increment: 2, incrementDiv: '-', forceIncrement: false, divConditional: false, removeUnreused: true };		};		RefRenamerDialog.prototype.getConfig = function { return { main: this.mainSelect.getValue, lowercase: this.lowercaseCheck.isSelected, removeDia: this.removeDiaCheck.isSelected, removePunct: this.removePunctCheck.isSelected, repSpace: this.repSpaceCheck.isSelected && this.repSpaceInput.getValue, year: this.yearCheck.isSelected, digits: this.digitsCheck.isSelected, yearDiv: this.yearDivInput.getValue, increment: this.incrementInput.getNumericValue, incrementDiv: this.incrementDivInput.getValue, forceIncrement: this.forceIncrementCheck.isSelected, divConditional: this.divConditionalCheck.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); this.repSpaceCheck.setSelected(typeof config.repSpace === 'string'); this.repSpaceInput.setValue(this.repSpaceCheck.isSelected ? config.repSpace : '-'); this.yearCheck.setSelected(config.year); this.digitsCheck.setSelected(config.digits); this.yearDivInput.setValue(config.yearDiv); this.incrementInput.setValue(config.increment); this.incrementDivInput.setValue(config.incrementDiv); this.forceIncrementCheck.setSelected(config.forceIncrement); this.divConditionalCheck.setSelected(config.divConditional); this.removeUnreusedCheck.setSelected(config.removeUnreused); };		RefRenamerDialog.prototype.applyConfig = function { let config = this.getConfig; let processed = new Set; this.refs.forEach(ref => {				ref.input.setValue();				if (!ref.reused) {					ref.keepCheck.setSelected(!config.removeUnreused);					if (config.removeUnreused) return;				}				let s;				config.main.some(key => key.split('|').some(subkey => {					s = ref.coins[subkey] || ref[subkey];					return s;				}));				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.repSpace === 'string') {					s = s.replace(/\s+/g, config.repSpace);				}				if (config.year) {					let digits = (ref.coins.date || ).match(/\d+/g);					let year = digits && Math.max(...digits) ||						config.digits && ref.year;					if (year) {						let yearDiv = config.divConditional && !/\d$/.test(s)							? ''							: config.yearDiv; s += yearDiv + year; }				}				let unsuffixed = s;				let normalized = normalize(s); let increment = config.increment; let incrementDiv = config.divConditional && !/\d$/.test(unsuffixed) ? ''					: config.incrementDiv; let forceIncrement = config.forceIncrement; while (forceIncrement ||					this.names.includes(normalized) || processed.has(normalized)				) { s = unsuffixed + incrementDiv + increment; normalized = normalize(s); increment++; forceIncrement = false; }				processed.add(normalized); ref.input.setValue(s); });		};		RefRenamerDialog.prototype.getSetupProcess = function (data) {			this.refs = data.refs;			this.names = data.names;			this.wikitext = data.wikitext;			this.$tbody.empty.append(this.refs.map(ref => {				if (!ref.reused) {					ref.keepCheck = new OO.ui.CheckboxInputWidget({ selected: true, title: i18n('uncheck-to-remove') }).on('change', onChange, [ref]);				}				ref.input = new RefRenamerInputWidget({ placeholder: i18n('renamer-input-placeholder') }).connect(this, { enter: ['executeAction', 'continue'] });				return $(' ').append( $(' ').text(ref.number), $(' ').append(ref.$ref), $(' ').append(						ref.keepCheck && ref.keepCheck.$element,						ref.keepCheck && $(' ').text(i18n('remove-span')),						ref.input.$element					) );			}));			this.namesTextarea.setValue( this.names.map(n => n.replace(/_/g, ' ')).sort(					Intl.Collator( mw.config.get('wgContentLanguage'), { numeric: true } ).compare				).join('\n') );			this.namesTextareaLayout.toggle(this.names.length);			this.setConfig(mw.storage.getObject('refrenamer'));			this.applyConfig;			return RefRenamerDialog.super.prototype.getSetupProcess.call(this, data);		};		RefRenamerDialog.prototype.getReadyProcess = function (data) {			return RefRenamerDialog.super.prototype.getReadyProcess.call(this, data).next(function { this.namesTextarea.adjustSize(true); }, this);		};		RefRenamerDialog.prototype.getActionProcess = function (action) {			return RefRenamerDialog.super.prototype.getActionProcess.call(this, action).next(function { if (action !== 'continue') return; let removed = []; let modified = this.refs.filter(ref => {					if (ref.keepCheck && !ref.keepCheck.isSelected) {						removed.push(ref);						return;					}					ref.newName = ref.input.getValue						.replace(/^[\s_]+|[\s_]+$|\n+/g, '');					ref.normalized = normalize(ref.newName);					return ref.normalized;				}); if (!modified.length && !removed.length) { return new OO.ui.Error(						i18n('no-names-modified'),						{ recoverable: false }					); }				let duplicates = new Set(					modified.filter(ref => (						this.names.includes(ref.normalized) ||							modified.some(ref2 => ref2 !== ref && ref2.normalized === ref.normalized)					)).map(ref => ref.normalized)				); if (duplicates.size) { return new OO.ui.Error($([ document.createTextNode(i18n('duplicate-names-list-label')), $('').append([...duplicates].map(name => $('').text(name)))[0] ]), { recoverable: false }); }				this.close; let subs = {}; modified.concat(removed).forEach(ref => {					subs[ref.number] = ref.newName;				}); let summary = i18n('summary'); if (this.wikitext) { $('#wpTextbox1').textSelection('setContents', replace(this.wikitext, subs)); if (document.documentElement.classList.contains('ve-active')) { ve.init.target.once('showChanges', => {							ve.init.target.saveDialog.reviewModeButtonSelect.selectItemByData('source');							ve.init.target.saveDialog.setEditSummary(summary);						}); ve.init.target.showSaveDialog('review'); } else { $('#wpSummary').textSelection('setContents', summary); $('#wpDiff').click; }				} else { $.when(						$.ajax('/w/rest.php/v1/page/' + encodedPn, { headers }),						new mw.Api.get({ action: 'query', titles: mw.config.get('wgPageName'), prop: 'info', inprop: 'watched', formatversion: 2 })					).then(([response], [queryResponse]) => {						let formData = [							['wpTextbox1', replace(response.source, subs)],							['wpSummary', summary],							['wpMinoredit', '']						];						let page = (((queryResponse || {}).query || {}).pages || [])[0] || {};						if (page.watched || Number(mw.user.options.get('watchdefault')) === 1 ) {							formData.push(['wpWatchthis', ]);							if (page.watchlistexpiry) {								formData.push(['wpWatchlistExpiry', page.watchlistexpiry]);							}						}						formData.push(['wpDiff', ], ['wpUltimateParam', 1]);						$(' ').attr({ method: 'post', action: mw.util.getUrl(null, { action: 'submit' }) }).append( formData.map(([n, v]) => $(' ').attr({ name: n,								type: 'hidden' }).val(v)) ).appendTo(document.body).submit.remove;					}); notify(i18n('opening-diff')); }				mw.requestIdleCallback( => {					let config = {};					Object.entries(this.getConfig).forEach(([k, v]) => { if (String(v) !== String(this.defaults[k])) { config[k] = v;						} });					if ($.isEmptyObject(config)) {						mw.storage.remove('refrenamer');					} else {						mw.storage.setObject('refrenamer', config, 7776000);					}				}); }, this);		};		RefRenamerDialog.prototype.hideErrors = function {			RefRenamerDialog.super.prototype.hideErrors.call(this);			this.actions.setAbilities({ continue: true });		};		function RefRenamerInputWidget(config) {			RefRenamerInputWidget.super.call(this, config);			this.$input.on({ focus: onFocus, blur: onBlur });		}		OO.inheritClass(RefRenamerInputWidget, OO.ui.MultilineTextInputWidget);		RefRenamerInputWidget.prototype.onKeyPress = function (e) {			if (e.which === OO.ui.Keys.ENTER) e.preventDefault;			OO.ui.TextInputWidget.prototype.onKeyPress.call(this, e);		};		dialog = new RefRenamerDialog;		let winMan = new OO.ui.WindowManager;		winMan.addWindows([dialog]);		winMan.$element.appendTo(document.body);	};	let replace = (text, subs) => text.replace( /<(ref)\s+(name)\s*=\s*"?:(\d+)"?(\s*\/?)>/gi, (s, ref, name, num, slash) => (			subs.hasOwnProperty(num)				? subs[num]					? `<${ref} ${name}="${subs[num]}"${slash}>`					: `<${ref}>`				: s		) );	let normalize = s => s.replace(/[\s_]+/g, '_').replace(/^_+|_+$/g, '');	let onFocus = => {		dialog.refs.forEach(ref => { ref.$ref.find('a').attr('tabindex', -1); });	};	let onBlur = => {		dialog.refs.forEach(ref => { ref.$ref.find('a').removeAttr('tabindex'); });	};	let onChange = (ref, selected) => {		ref.input.toggle(selected).$element.next.toggle(!selected);	};	let notify = msg => {		mw.notify(msg, { autoHideSeconds: 'long', tag: 'refrenamer' });	};	window.refRenamer; });