fchat-rising/learn/dictionary/word-pos-search.ts

114 lines
2.4 KiB
TypeScript

import _ from 'lodash';
export interface FoundWord {
word: string;
start: number;
clickedRect: DOMRect;
}
export class WordPosSearch {
private listener?: (e: MouseEvent) => void;
private lastClicked: FoundWord | null = null;
constructor() {
this.start();
}
stop() {
if (this.listener) {
document.removeEventListener('contextmenu', this.listener);
}
delete this.listener;
}
start() {
this.stop();
this.listener = this.generateListener();
document.addEventListener(
'contextmenu',
this.listener
);
}
generateListener(): ((e: MouseEvent) => void) {
return (e: MouseEvent): void => {
try {
if (!e.target) {
return;
}
this.lastClicked = _.reduce(
(e.target as any).childNodes || [],
(accum: FoundWord | null, node) => (accum ? accum : this.findClickedWord(node as any, e.clientX, e.clientY)),
null
);
} catch (err) {
console.log('wordpos.event', err);
}
};
}
findClickedWord(parentElt: Element, x: number, y: number): FoundWord | null {
if (!parentElt.textContent) {
return null;
}
const range = document.createRange();
const words = parentElt.textContent.split(/[\s\.\<\>\,\?\!\;\:\@\$\*\(\)]/);
let start = 0;
let end = 0;
for(let i = 0; i < words.length; i++) {
const word = words[i];
end = start + word.length;
try {
range.setStart(parentElt, start);
range.setEnd(parentElt, end);
// not getBoundingClientRect as word could wrap
const rects = range.getClientRects();
const clickedRect = this.isClickInRects(x, y, rects);
if (clickedRect) {
return { word, start, clickedRect };
}
start = end + 1;
} catch (e) {
// console.log('MM', 'word', word, 'start', start, 'end', end, 'i', i, words);
// console.error(e);
return null;
}
}
return null;
}
isClickInRects(x: number, y: number, rects: DOMRectList): DOMRect | null {
for(let i = 0; i < rects.length; ++i) {
const r = rects[i];
if ((r.left < x) && (r.right > x) && (r.top < y) && (r.bottom > y)) {
return r;
}
}
return null;
}
getLastClickedWord(): string | null {
return this.lastClicked ? this.lastClicked.word : null;
}
}