User:Nardog/IPAInput-core.js

(function ipaInputCore {	mw.loader.addStyleTag('.ipainput-config{padding:0 1em 1em} .ipainput-input{position:sticky;top:0;left:0;opacity:0.8;font-size:200%;z-index:999} .ipainput-input > input{text-align:center} .ipainput .ipainput-input.oo-ui-indicatorElement > input{padding-right:56px} .ipainput .ipainput-input > .oo-ui-indicator-clear, .ipainput-symbol{cursor:pointer} .ipainput-undo, .ipainput-diaonly{font-size:50%;position:absolute;right:0;margin:0} .ipainput-diaonly{top:150%} .ipainput-status{text-align:center;font-size:120%;padding:1em 0 0.5em} .ipainput-status > a{font-weight:bold} .ipainput .mw-parser-output{margin:auto;width:max-content;max-width:100%;padding:0 0.5em;overflow:auto} .ipainput-symbol:hover{background-color:#fff} .ipainput-symbol:active{background-color:#c8ccd1} .ipainput-symbol:focus{outline:solid 2px #36c} .ipainput-symbol-disabled, .ipainput-symbol-disabled:hover, .ipainput-symbol-disabled:active, .ipainput-symbol-disabled:focus{cursor:auto;background-color:#c8ccd1 !important;outline:0} .ipainput-symbol-disabled, .ipainput-symbol-disabled a{color:#fff}'); let promise = mw.loader.using([		'jquery.textSelection', 'oojs-ui-windows', 'oojs-ui-widgets',		'oojs-ui.styles.icons-interactions', 'oojs-ui.styles.icons-editing-core',		'oojs-ui.styles.icons-editing-advanced'	]); let langs = mw.storage.getObject('ipainput-cache'); if (!langs) { mw.notify('Retrieving keys...', { autoHide: false, tag: 'ipainput' }); promise = $.when(promise, $.post('//en.wikipedia.org/api/rest_v1/transform/wikitext/to/html', { wikitext: '', body_only: true }).then(response => { langs = { '(Full IPA chart)': [ ['und', 'Undetermined'], ['', '(No linking)'] ],				'English': [ ['en', 'English'] ]			};			let lastKey, lastLang; $($.parseHTML(response)).find('td:last-child').each(function {				let key, lang;				let prev = this.previousElementSibling;				if (prev) {					lang = prev.textContent;					lastLang = lang;					let prevPrev = prev.previousElementSibling;					if (prevPrev) {						key = prevPrev.textContent.slice(9);						lastKey = key;					} else {						key = lastKey;					}				} else {					key = lastKey;					lang = lastLang;				}				if (key === 'English') return;				if (!langs.hasOwnProperty(key)) langs[key] = [];				langs[key].push([this.textContent, lang]);			}); mw.requestIdleCallback( => {				let notif = $('.mw-notification-tag-ipainput').data('mw-notification');				if (notif) notif.close;				mw.storage.setObject('ipainput-cache', langs, 604800);			}); }));	}	promise.then( => {		function IpaInputDialog(config) {			IpaInputDialog.parent.call(this, config);			this.$element.addClass('ipainput');		}		OO.inheritClass(IpaInputDialog, OO.ui.ProcessDialog);		IpaInputDialog.static.name = 'ipaInputDialog';		IpaInputDialog.static.title = 'IPAInput';		IpaInputDialog.static.size = 'small';		IpaInputDialog.static.actions = [			{				modes: 'config',				flags: ['safe', 'close']			},			{				action: 'transcribe',				label: 'Transcribe',				modes: 'config',				flags: ['primary', 'progressive']			},			{				action: 'goBack',				modes: 'transcription',				flags: ['safe', 'back']			},			{				action: 'insert',				label: 'Insert',				modes: 'transcription',				flags: ['primary', 'progressive']			}		];		IpaInputDialog.prototype.initialize = function {			IpaInputDialog.parent.prototype.initialize.apply(this, arguments);			this.keyDropdown = new OO.ui.DropdownWidget({ $overlay: this.$overlay, menu: { items: Object.keys(langs).map(k => ( new OO.ui.MenuOptionWidget({ label: k }) ))				}			});			this.keyDropdown.getMenu.on('select', item => { let options = langs[item.getLabel].map(([code, lang]) => ( new OO.ui.MenuOptionWidget({						data: code,						label: code ? `${lang} (${code})` : lang					}) ));				this.languageDropdown.getMenu.clearItems.addItems(options) .selectItem(options[0]); });			this.languageDropdown = new OO.ui.DropdownWidget({ $overlay: this.$overlay });			this.noTemplateCheckbox = new OO.ui.CheckboxInputWidget				.connect(this.languageDropdown, { change: 'setDisabled' });			this.rememberCheckbox = new OO.ui.CheckboxInputWidget;			this.form = new OO.ui.FormLayout({ items: [ new OO.ui.FieldLayout(this.keyDropdown, {						label: 'Key:',						align: 'top'					}), new OO.ui.FieldLayout(this.languageDropdown, {						label: 'Language:',						align: 'top'					}), new OO.ui.FieldLayout(this.noTemplateCheckbox, {						label: 'No template',						align: 'inline'					}), new OO.ui.FieldLayout(this.rememberCheckbox, {						label: 'Remember these for next time',						align: 'inline'					}) ],				content: [$(' ').attr({ type: 'submit', hidden: '' })], classes: ['ipainput-config'] }).connect(this, { submit: ['executeAction', 'transcribe'] });			this.$body.append(this.form.$element);			this.input = new OO.ui.TextInputWidget({ spellcheck: false, classes: ['ipainput-input', 'IPA'] }).on('change', value => { this.input.setIndicator(value ? 'clear' : null); }).connect(this, { enter: ['executeAction', 'insert'] });			this.input.$input.on('keydown', e => { if (e.which !== 27 || !this.input.getValue) return; e.stopPropagation; this.input.setValue(''); });			this.input.$indicator.on('click', => { this.input.setValue('').focus; });			this.$status = $(' ').addClass('ipainput-status');			this.$parserOutput = $(' ').attr({ class: 'mw-parser-output mw-body-content', lang: 'en' }).on('click', '.ipainput-symbol', e => { if (e.ctrlKey || e.shiftKey || e.metaKey || e.altKey) return; e.preventDefault; e.stopPropagation; if (e.currentTarget.classList.contains('ipainput-symbol-disabled')) { return; }				let $target = $(e.currentTarget).clone.find('.IPA'); if (!$target.length) $target = $target.end; $target.find('.reference').remove; let ins = $target.text.trim .replace(/◌|^[\(\/\[]+\s*(?=\S)|(\S)\s*[\)\/\]]+$/g, '$1'); if (e.currentTarget.classList.contains('ipainput-symbol-dia') &&					this.diaOnlyButton.getValue				) { let match = ins.normalize('NFD').match(/[^̧\P{Mn}]+/u); if (match) ins = match[0]; }				let start = this.input.$input.prop('selectionStart'); let end = this.input.$input.prop('selectionEnd'); let text = this.input.getValue; let pos = start + ins.length; let newText = text.slice(0, start) + ins + text.slice(end); this.input.setValue(newText).selectRange(pos).focus; if (this.undoCache.length					? text !== this.undoCache[this.undoCache.length - 1][0]					: text				) { this.undoCache.push([text, end]); }				this.undoCache.push([newText, pos]); this.undoCache = this.undoCache.slice(-500); }).on('keydown', '.ipainput-symbol', function (e) { if (e.ctrlKey || e.shiftKey || e.metaKey || e.altKey) return; if (e.which === 13 || e.which === 32) { e.preventDefault; e.stopPropagation; this.click; this.focus; }			});			this.undoButton = new OO.ui.ButtonWidget({ icon: 'undo', invisibleLabel: true, label: 'Undo', classes: ['ipainput-undo'] }).on('click', => { let arr = this.undoCache.pop; if (!arr) { this.input.setValue('').focus; return; }				if (this.undoCache.length && this.input.getValue === arr[0]) { arr = this.undoCache.pop; }				this.input.setValue(arr[0]).selectRange(arr[1]).focus; });			this.diaOnlyButton = new OO.ui.ToggleButtonWidget({ icon: 'searchDiacritics', invisibleLabel: true, label: 'Insert diacrtics only', classes: ['ipainput-diaonly'] }).on('change', enabled => { this.$parserOutput.find(					'.ipainput-symbol:not(.ipainput-symbol-dia)'				).attr({					tabindex: enabled ? -1 : 0,					'aria-disabled': enabled ? 'true' : null				}).toggleClass('ipainput-symbol-disabled', enabled); });			this.input.$element.append( this.undoButton.$element, this.diaOnlyButton.$element );			this.$transcription = $([ this.input.$element[0], this.$status[0], this.$parserOutput[0] ]);		};		IpaInputDialog.prototype.getSetupProcess = function (data) {			return IpaInputDialog.super.prototype.getSetupProcess.call(this, data).next(function { let storage = (mw.storage.get('ipainput') || '').split('|'); let key = langs.hasOwnProperty(storage[0]) ? storage[0] : 'English'; this.keyDropdown.getMenu.selectItemByLabel(key); if (storage[1] === 'null') { this.noTemplateCheckbox.setSelected(true); } else if (langs[key].some(([k]) => k === storage[1])) { this.languageDropdown.getMenu.selectItemByData(storage[1]); }				this.rememberCheckbox.setSelected(storage[0]); this.actions.setMode('config'); }, this);		};		IpaInputDialog.prototype.getKey = function {			let isGeneric = this.keyName === '(Full IPA chart)';			let pageName = isGeneric				? 'International Phonetic Alphabet chart'				: 'Help:IPA/' + this.keyName;			this.actions.get[3].setDisabled(true);			this.pushPending;			this.input.setValue('');			this.undoCache = [];			this.$status.empty.append( 'Loading ', $('').attr({					href: mw.util.getUrl(pageName),					target: '_blank'				}).text(pageName), '...'			);			this.$parserOutput.empty;			$.get( '//en.wikipedia.org/api/rest_v1/page/html/' + encodeURIComponent(pageName.replace(/ /g, '_')) ).then(data => { this.curKeyName = this.keyName; let $data = $($.parseHTML(data)); let $tables = $data.filter('section').children.unwrap; if (isGeneric) { $tables = $tables.filter('h2#Vowels').nextUntil('#See_also').addBack; } else { $tables = $tables.filter(function {						return this.classList.contains('wikitable') ||							this.querySelector('.wikitable');					}); if ($tables.length > 1) { $tables = $tables.first.nextUntil($tables.last.next).addBack; }				}				$tables.find('.IPA').filter(function {					return /[\s,~]/.test(this.textContent.trim) || this.querySelector('br');				}).find('*').addBack.contents.filter(function  {					return this.nodeType === 3;				}).replaceWith(function  {					return this.textContent.split(/([\s,~]+)/).reduce((acc, s, i) => { if (s) { acc.push(i % 2 ? s : $(' ').attr({ class: 'ipainput-symbol', tabindex: 0, role: 'button' }).text(s)); }						return acc; }, []);				});				$tables.find('td, th').filter(function {					if (this.querySelector('.ipainput-symbol, .IPA-vowels-container')) return;					return this.classList.contains('IPA') ||						this.querySelector('.IPA') &&						!this.querySelector('br, p') &&						!$(this).find(':not(.IPA, .IPA *, .reference, .reference *)').addBack.contents.get							.some(n => n.nodeType === 3 && n.textContent.trim);				}).addClass('ipainput-symbol').attr({					tabindex: 0,					role: 'button'				}); let $spans = $tables.find('.IPA:not(.ipainput-symbol, .ipainput-symbol .IPA)').filter(function {					return !this.querySelector('.ipainput-symbol');				}); let consec = []; $spans.filter(function (i) {					if ($spans[i + 1] === this.nextSibling) {						consec.push(this);					} else if (consec.length) {						consec.push(this);						$(consec).wrapAll(' ').parent							.addClass('ipainput-symbol').attr({ tabindex: 0, role: 'button' });						consec.length = 0;					} else {						return true;					}				}).addClass('ipainput-symbol').attr({					tabindex: 0,					role: 'button'				}); $tables.find('[id], [about]').addBack.removeAttr('id about'); $tables.find('a').attr({					target: '_blank',					tabindex: -1				}).filter('[href^="./"]').attr('href', function {					return '//en.wikipedia.org/wiki' +						this.getAttribute('href').slice(1);				}); let hasDia = $tables.find('.ipainput-symbol').filter(function {					return /[^̧\P{Mn}]/u.test(this.textContent.normalize('NFD'));				}).addClass('ipainput-symbol-dia').length && true; this.diaOnlyButton.setValue.toggle(hasDia); let modules = (					$data.filter('meta[property="mw:moduleStyles"]').attr('content') || ''				).split('|'); return mw.loader.using(modules).always( => {					this.$status.html(this.$status.children);					this.$parserOutput.append($tables);					this.updateSize;					this.actions.get[3].setDisabled;				}); }, data => { let msg = ((data || {}).responseJSON || {}).title; if (msg && data.responseJSON.detail) { msg += ': ' + data.responseJSON.detail; }				this.$status.text(msg || 'Unknown error'); }).always( => { this.popPending; });		};		IpaInputDialog.prototype.getActionProcess = function (action) {			if (action === 'transcribe') {				this.keyName = this.keyDropdown.getMenu.findSelectedItem.getLabel;				this.langCode = this.noTemplateCheckbox.isSelected					? null					: this.languageDropdown.getMenu.findSelectedItem.getData;				this.actions.setMode('transcription');				this.form.toggle(false).$element.after(this.$transcription);				this.setSize('larger');				if (this.keyName !== this.curKeyName) {					this.getKey;				}				$(document).on('keydown.ipaInput', e => { if (e.shiftKey || e.altKey) return; if (e.which === 90 &&						[e.ctrlKey, e.metaKey].filter(Boolean).length === 1					) { e.preventDefault; this.undoButton.emit('click'); }				});				this.input.focus;				mw.requestIdleCallback( => { mw.storage.remove('IpaInput-keyName'); mw.storage.remove('IpaInput-template'); if (this.rememberCheckbox.isSelected) { mw.storage.set(							'ipainput',							this.keyName + '|' + this.langCode,							31556952						); } else { mw.storage.remove('ipainput'); }				});			} else {				$(document).off('keydown.ipaInput');				this.actions.setMode('config');				this.$transcription.detach;				this.form.toggle(true);				this.setSize('small');				if (action === 'insert') {					let text = this.input.getValue.trim, template;					if (this.keyName === 'English') {						text = text							.replace(/\s+/g, '_')							.replace(/a[ɪʊ]ər|ɔɪər|[ɛɪʊ]ə[ˈˌ]r|\.\.\.|[ɑɔɜ]ːr|[ɛɪʊ]ər|!!|,_|a[ɪʊ]|[dlnstzθ]j(?=u|ʊə)|dʒ|eɪ|hw|[iuɑɔɜ]ː|oʊ|tʃ|[æɒɛɪʊʌ]r|[æɒ]̃|ɔɪ|(?:(?<=[bdfkprstvxzðɡʃʒʔθ]\.?)ə[ln]|(?<=[fsvzðʃʒθ]\.?)əm|ər)(?![aeæɑɒɔɛɜʊʌˈˌ]|[iu]ː|ɪə)|[!#\(\)\-\._bdfhijklmnprstuvwxzæðŋɒəɛɡɪʃʊʌʒʔˈˌθ]/g, '$&|')							.replace(/\|$/, '');						template = 'IPAc-en';					} else {						template = 'IPA';						if (this.langCode) {							text = this.langCode + '|' + text;						}					}					if (document.documentElement.classList.contains('ve-active')) {						text = this.langCode === null ? text : [{ type: 'mwTransclusionInline', attributes: { mw: { parts: [{ template: { target: { wt: template }, params: text.split('|').reduce((acc, s, i) => {												acc[i + 1] = { wt: s };												return acc;											}, {}) }									}]								}							}						}];						ve.init.target.getSurface.getModel.getFragment.collapseToEnd .insertContent(text).collapseToEnd.select; } else { if (this.langCode !== null) { text = ``; }						$('#wpTextbox1').textSelection('encapsulateSelection', {							peri: text,							replace: true						}); }					this.input.setValue(''); this.undoCache = []; this.close; }			}			return IpaInputDialog.super.prototype.getActionProcess.call(this, action); };		window.ipaInputDialog = new IpaInputDialog; let winMan = new OO.ui.WindowManager; winMan.addWindows([window.ipaInputDialog]); winMan.$element.appendTo(OO.ui.getTeleportTarget); window.ipaInputDialog.open; }); });