['edit', 'submit'].includes(mw.config.get('wgAction')) &&
(function () {
let clicked, dialog;
let openDialog = async context => {
if (clicked) {
if (dialog) {
dialog.context = context;
dialog.open();
}
return;
}
clicked = true;
mw.loader.addStyleTag(`.oo-ui-windowManager-floating > .insertanychar > .oo-ui-window-frame{margin-top:0 !important;margin-inline-end:0 !important} .insertanychar{cursor:pointer} .insertanychar-item{padding-inline:0} .insertanychar-item > .oo-ui-labelElement-label{display:flex !important;overflow:visible !important;align-items:center} .insertanychar-char{font-size:200%;min-width:1em;line-height:1;margin-inline:8px;text-align:center;font-family:'Charis SIL','Doulos SIL','Gentium','Gentium Plus','Noto Serif','Liberation Serif','Roboto Serif',serif,'Andika','Noto Sans','Liberation Sans','Roboto Sans','Bitstream Cyberbit','Code2000','Lucida Grande','Lucida Sans Unicode',sans-serif} .insertanychar-name{white-space:normal !important;word-break:break-word;flex-grow:1} .insertanychar-item > .oo-ui-labelElement-label::after{content:attr(data-insertanychar);font-size:85%;margin-inline:4px;color:var(--color-subtle,#54595d)}`);
let promise = mw.loader.using(['oojs-ui-windows', 'jquery.textSelection']);
let response = await $.get('//en.wiktionary.org/w/rest.php/v1/revision/80968402');
let data = [], block;
response.source.split('\n').forEach(s => {
let props = s.split(';');
let v = parseInt(props[0], 16);
let name = props[1];
if (name?.[0] === '<') {
if (name === '<control>') {
name = props[10];
} else {
let blockName = name.match(/[^,<>]+/)?.[0].toUpperCase();
if (!blockName || blockName.endsWith(' SURROGATE')) return;
if (name.endsWith(', First>')) {
block = [v];
data.push([blockName, block]);
} else if (name.endsWith(', Last>')) {
block.push(v);
}
return;
}
}
if (props[2] === 'Mn') {
data.push([name, v, ['233', '234'].includes(props[3])]);
} else {
data.push([name, v]);
}
});
await promise;
function InsertAnyCharDialog(config) {
InsertAnyCharDialog.super.call(this, config);
this.$element.addClass('insertanychar');
}
OO.inheritClass(InsertAnyCharDialog, OO.ui.Dialog);
InsertAnyCharDialog.static.name = 'insertAnyCharDialog';
InsertAnyCharDialog.prototype.initialize = function () {
InsertAnyCharDialog.super.prototype.initialize.call(this);
this.$element.on('click', e => {
if (!this.$content[0].contains(e.target) &&
!this.$overlay[0].contains(e.target)
) {
this.close();
}
});
this.moreButton = new OO.ui.MenuOptionWidget({
label: 'Load more'
});
new IntersectionObserver(entries => {
if (entries[0].isIntersecting) {
this.continue();
}
}, { threshold: 0.75 }).observe(this.moreButton.$element[0]);
this.input = new OO.ui.SearchInputWidget({
autocomplete: false
}).on('change', OO.ui.debounce(value => {
this.menu.toggle(false).clearItems();
this.res = value.toUpperCase().split(/[^\dA-Z]+/).filter(Boolean)
.map(s => new RegExp('\\b' + s));
if (!this.res.length) return;
this.startFrom = 0;
this.startBlockFrom = 0;
this.lookUp();
}, 250));
this.menu = new OO.ui.MenuSelectWidget({
$floatableContainer: this.input.$element,
autoHide: false,
hideOnChoose: false,
input: this.input,
widget: this.input
}).on('choose', item => {
if (item === this.moreButton) {
this.continue();
return;
}
this.context.$textarea.textSelection('encapsulateSelection', {
peri: String.fromCodePoint(parseInt(item.$label.data('insertanychar'), 16)),
selectPeri: false
});
this.menu.unselectItem();
});
this.manager.connect(this.menu, { resize: 'position' })
.connect(this.menu, { resize: 'clip' });
this.$body.append(this.input.$element);
this.$overlay.append(this.menu.$element);
};
InsertAnyCharDialog.prototype.lookUp = function () {
this.busy = true;
let matches = [];
let halted = data.slice(this.startFrom).some(entry => {
if (typeof entry[1] === 'number') {
this.startFrom++;
if (this.res.every(re => re.test(entry[0]))) {
return matches.push(entry) === 100;
}
} else if (this.res.every(re => (
re.test(entry[0]) || /^\\b[\dA-F]+$/.test(re.source)
))) {
for (let i = entry[1][0] + this.startBlockFrom; i <= entry[1][1]; i++) {
this.startBlockFrom++;
let hex = i.toString(16).toUpperCase();
if (this.res.every(re => re.test(entry[0]) || re.test(hex)) &&
matches.push([entry[0], i]) === 100
) {
return true;
}
}
this.startBlockFrom = 0;
this.startFrom++;
}
});
if (!matches.length) return;
let items = matches.map(([name, v, isDouble]) => new OO.ui.MenuOptionWidget({
$label: $('<span>').attr(
'data-insertanychar',
v.toString(16).toUpperCase().padStart(4, '0')
),
classes: ['insertanychar-item'],
label: $('<span>').addClass('insertanychar-char').text(
`${isDouble === undefined ? '' : '◌'}${
String.fromCodePoint(v)
}${isDouble === true ? '◌' : ''}`
).add($('<span>').addClass('insertanychar-name').text(
name.replace(
/ (?:AND|AS|AT|BY|FOR|FROM|IN|OF|ON|OR|THE|TO|WITH)(?= )/g,
s => s.toLowerCase()
).replace(
/\b(?!CJK\b)[\dA-Z]{2,}\b/g,
s => s[0] + s.slice(1).toLowerCase()
)
))
}));
if (halted) {
items.push(this.moreButton);
}
this.menu.addItems(items).toggle(true);
setTimeout(() => {
this.busy = false;
}, 500);
};
InsertAnyCharDialog.prototype.continue = function () {
if (this.busy) return;
this.menu.removeItems([this.moreButton]);
this.lookUp();
};
InsertAnyCharDialog.prototype.getReadyProcess = function () {
return InsertAnyCharDialog.super.prototype.getReadyProcess.apply(this, arguments).next(function () {
this.input.focus();
this.menu.position().clip();
}, this);
};
dialog = new InsertAnyCharDialog();
dialog.context = context;
let winMan = new OO.ui.WindowManager();
winMan.addWindows([dialog]);
winMan.$element.appendTo(OO.ui.getTeleportTarget());
dialog.open();
};
mw.hook('wikiEditor.toolbarReady').add($textarea => {
$textarea.wikiEditor('addToToolbar', {
section: 'main',
group: 'insert',
tools: {
dialog: {
label: 'InsertAnyChar',
type: 'button',
oouiIcon: 'specialCharacter',
action: { type: 'callback', execute: openDialog }
}
}
});
});
}());