///
///
///
import { Asearch } from "../deno-asearch/mod.ts";
import {
editor,
textInput,
insertText,
press,
caret,
getInternalLink,
takeCursor,
takeSelection,
} from "../scrapbox-userscript-std/dom.ts";
import { loadEmojis } from "./load.ts";
import { makeBox } from "./ui.ts";
import { Candidate } from "./types.ts";
import { getMaxDistance } from "./distance.ts";
const projectName = scrapbox.Project.name;
const emojis: Candidate[] = await loadEmojis(projectName);
scrapbox.PageMenu.addMenu({
title: "emoji",
image: "https://gyazo.com/d57fea8a143650375af1c8bba1fc1370/raw",
});
scrapbox.PageMenu("emoji").addItem({
title: "load emojis from /emoji",
onClick: async () => {
for (const emoji of await loadEmojis("emoji")) {
if(emojis.some((e) => emoji.name === e.name)) continue;
emojis.push(emoji);
}
},
});
const detectLink = () => {
const { line, char } = caret().position;
const link = getInternalLink(line, char);
if (!link) return undefined;
const text = /^\[[^\]]+\]$/.test(link.textContent) ?
link.textContent.replace(/^\[([^\]]+)\]$/, "$1").trim() :
link.textContent.trim();
const start = getIndex(getChars(link).next().value);
return text.startsWith(":") ? {
text: text.slice(1),
raw: `[${text}]`,
pos: {
line,
char: start,
} : undefined;
};
let completing = false;
const callback = () => {
const text = detectLink()?.text;
if (text !== undefined) return;
handleEnd();
};
const handleStart = () => {
if (completing) return;
completing = true;
const cursor = takeCursor();
cursor.addChangeListener(callback);
};
const handleEnd = () => {
if (!completing) return;
completing = false;
const cursor = takeCursor();
cursor.removeChangeListener(callback);
close();
};
textInput!.addEventListener("input", (e) => {
if (e.isComposing) return;
const text = detectLink()?.text;
if (text === undefined) {
handleEnd();
return;
}
const { match } = Asearch(` ${text} `);
const compare = new Intl.Collator().compare;
setItems(emojis
.flatMap((emoji) => {
const result = match(emoji.name, getMaxDistance[text.length]);
if (!result.found) return [];
return [{
distance: result.distance,
onClick: () => {
const link = detectLink();
if (link === undefined) {
handleEnd();
return;
}
const selection = takeSelection();
selection.setSelection({
start: {
line: link.pos.line,
char: link.pos.char,
},
end: {
line: link.pos.line,
char: link.pos.char + link.raw.length -1,
},
});
await insertText();
},
...emoji
}];
})
.sort((a, b) => a.distance === b.distance ? compare(a.name, b.name) : a.distance - b.distance)
);
open();
});
editor.keydown( e => {
const key = e.key;
const cursor = $('#text-input')[0];
if( key.match(/^[\w\s\-\:\+]$/) ){
stack += e.key;
let focused = $(':focus');
if(focused.is(items.find('li > a'))){
cursor.focus();
}
}
switch(key){
case 'Backspace':
stack = stack.slice(0, stack.length - 1);
if(stack.length === 0){
close();
return;
}
break;
case 'ArrowUp':
let focusedUp = $(':focus');
if( focusedUp.is(items.find('li > a').eq(0)) ){
e.stopPropagation();
cursor.focus();
}else if( !focusedUp.is(items.find('li > a')) ){
close();
return;
}
break;
case 'ArrowDown':
let focusedDown = $(':focus');
if( !focusedDown.is(items.find('li > a'))) {
e.stopPropagation();
e.preventDefault();
items.find("li > a").eq(0).focus();
}
break;
case 'Escape':
case 'ArrowLeft':
case 'ArrowRight':
case 'Home':
case 'End':
case 'PageUp':
case 'PageDown':
close();
break;
case 'Enter':
if( stack.length === 1 ){
close();
break;
}
let focused = $(':focus');
if(!focused.is(items.find('li > a'))){
e.stopPropagation();
e.preventDefault();
items.find('li > a').eq(0).click();
}
break;
}
if( stack.length <= 1 || !key.match(/^[\w\s\:\-\+]$|Backspace/)) return;
const matchedEmoji = fizzSearch(stack, emojis)
if( matchedEmoji.length === 0){
close();
return;
}
const newItems = $('
').addClass('dropdown-menu');
matchedEmoji.forEach( ( emoji, index) => {
if( index > 30 ) return;
newItems.append(makeItem(emoji.name, emoji.src));
a.on('click', () => {
cursor.focus();
replaceText(stack, cursor, emoji.path);
})
a.on('keypress', ev => {
if(ev.key === "Enter"){
ev.preventDefault();
ev.stopPropagation();
replaceText(stack, cursor, emoji.path);
}
})
})
items.replaceWith(newItems);
items = newItems;
let css = {};
cursor.style.cssText.split(';').filter( text => text !== '' )
.forEach( text => {
const props = text.split(':').map( text => text.replace(' ', '').replace('px', ''));
css[props[0]] = props[1];
});
box.css({
top: `${parseInt(css.top) + parseInt(css.height) + 3}px`,
left: `${css.left}px`,
});
})