~glacambre/firenvim

ref: 8d90a8c3fa5f59a46a206f5fa94a9df7bbda4d8a firenvim/src/content.ts -rw-r--r-- 2.8 KiB
8d90a8c3glacambre Populate buffer with textarea content, remove iframe on VimLeave 2 years ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
import { computeSelector } from "./CSSUtils";

const selectorToElems = new Map<string, [HTMLSpanElement, HTMLElement]>();

const functions: any = {
    getEditorLocation: () => lastEditorLocation,
    getElementContent: (selector: string) => {
        const [_, e] = selectorToElems.get(selector) as [any, any];
        if (e.value !== undefined) {
            return e.value;
        }
        if (e.textContent !== undefined) {
            return e.textContent;
        }
        return e.innerText;
    },
    killEditor: (selector: string) => {
        const [e, _] = selectorToElems.get(selector) as [any, any];
        e.parentNode.removeChild(e);
        selectorToElems.delete(selector);
    },
    setElementContent: (selector: string, text: string) => {
        const [_, e] = selectorToElems.get(selector) as [any, any];
        if (e.value !== undefined) {
            e.value = text;
        } else {
            e.textContent = text;
        }
    },
};

browser.runtime.onMessage.addListener(async (request: any, sender: any, sendResponse: any) => {
    if (!functions[request.function]) {
        throw new Error(`Error: unhandled content request: ${request.toString()}.`);
    }
    return functions[request.function](...(request.args || []));
});

let lastEditorLocation = ["", ""];

function nvimify(evt: FocusEvent) {
    const elem = evt.target as HTMLElement;
    const rect = elem.getBoundingClientRect();
    const iframe = elem.ownerDocument
        .createElementNS("http://www.w3.org/1999/xhtml", "iframe") as HTMLIFrameElement;
    iframe.style.height = `${rect.height}px`;
    iframe.style.left = `${rect.left + window.scrollX}px`;
    iframe.style.position = "absolute";
    iframe.style.top = `${rect.top + window.scrollY}px`;
    iframe.style.width = `${rect.width}px`;
    iframe.src = (browser as any).extension.getURL("/NeovimFrame.html");
    const span = iframe.ownerDocument
        .createElementNS("http://www.w3.org/1999/xhtml", "span") as HTMLSpanElement;
    span.attachShadow({ mode: "closed" }).appendChild(iframe);
    elem.ownerDocument.body.appendChild(span);
    iframe.focus();

    const selector = computeSelector(evt.target as HTMLElement);
    lastEditorLocation = [document.location.href, selector];
    selectorToElems.set(selector, [span, elem]);
}

function isEditable(elem: HTMLElement) {
    return elem.tagName === "TEXTAREA"
        || (elem.tagName === "INPUT" && (elem as HTMLInputElement).type === "text");
}

(new MutationObserver(changes => {
    changes
        .filter((change: MutationRecord) => change.addedNodes.length > 0)
        .forEach((change: MutationRecord) => {
            Array.from(change.addedNodes)
                .filter(node => isEditable(node as HTMLElement))
                .forEach(node => node.addEventListener("focus", nvimify));
        });
})).observe(window.document, { subtree: true, childList: true });