/**
 * Functions to create input triggers according to some common patterns.
 * @author Louis-Dominique Dubeau
 * @license MPL 2.0
 * @copyright Mangalam Research Center for Buddhist Languages
 */
var __importStar = (this && this.__importStar) || function (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
    result["default"] = mod;
    return result;
};
define(["require", "exports", "./domtypeguards", "./domutil", "./input-trigger", "./transformation"], function (require, exports, domtypeguards_1, domutil, input_trigger_1, transformation) {
    "use strict";
    Object.defineProperty(exports, "__esModule", { value: true });
    domutil = __importStar(domutil);
    transformation = __importStar(transformation);
    /**
     * A transformation handler for a node splitting transformation.
     *
     * @param editor The editor which invoked the transformation.
     *
     * @param data The key that is splitting the element.
     *
     * @throws {Error} If the data passed is incorrect.
     */
    function splitNodeOn(editor, data) {
        let node = data.node;
        const sep = data.sep;
        if (typeof sep !== "string" || sep.length !== 1) {
            throw new Error("transformation invoked with incorrect data");
        }
        let modified = true;
        while (modified) {
            modified = false;
            const textNodes = [];
            let child = node.firstChild;
            while (child !== null) {
                if (domtypeguards_1.isText(child)) {
                    textNodes.push(child);
                }
                child = child.nextSibling;
            }
            for (let i = 0; !modified && i < textNodes.length; ++i) {
                const text = textNodes[i];
                const offset = text.data.indexOf(sep);
                if (node.firstChild === text && offset === 0) {
                    // We just drop the separator
                    editor.dataUpdater.deleteText(text, offset, 1);
                    modified = true;
                }
                else if (node.lastChild === text && offset !== -1 &&
                    offset === text.length - 1) {
                    // Just drop the separator
                    editor.dataUpdater.deleteText(text, text.length - 1, 1);
                    modified = true;
                }
                else if (offset !== -1) {
                    const [, end] = editor.dataUpdater.splitAt(node, text, offset);
                    // Continue with the 2nd half of the split
                    if (end === null) {
                        throw new Error("null end; we should not be getting that");
                    }
                    node = end;
                    modified = true;
                }
            }
        }
        // Find the deepest location at the start of the last element.
        editor.caretManager.setCaret(domutil.firstDescendantOrSelf(node), 0);
    }
    /**
     * Makes an input trigger that splits and merges consecutive elements.
     *
     * @param editor The editor for which to create the input trigger.
     *
     * @param selector A CSS selector that determines which element we want to
     * split or merge. For instance, to operate on all paragraphs, this parameter
     * could be ``"p"``. This selector must be fit to be used in the GUI tree.
     *
     * @param splitKey The key which splits the element.
     *
     * @param mergeWithPreviousKey The key which merges the element with its
     * previous sibling.
     *
     * @param mergeWithNextKey The key which merges the element with its next
     * sibling.
     *
     * @returns The input trigger.
     */
    function makeSplitMergeInputTrigger(editor, mode, selector, splitKey, mergeWithPreviousKey, mergeWithNextKey) {
        const splitNodeOnTr = new transformation.Transformation(editor, "split", "Split node on character", splitNodeOn);
        const ret = new input_trigger_1.InputTrigger(editor, mode, selector);
        ret.addKeyHandler(splitKey, (eventType, el, ev) => {
            if (ev !== undefined) {
                ev.stopImmediatePropagation();
                ev.preventDefault();
            }
            if (eventType === "keypress" || eventType === "keydown") {
                editor.fireTransformation(editor.splitNodeTr, { node: el,
                    name: el.tagName });
            }
            else {
                editor.fireTransformation(splitNodeOnTr, {
                    name: el.tagName,
                    node: el,
                    sep: String.fromCharCode(splitKey.which),
                });
            }
        });
        ret.addKeyHandler(mergeWithPreviousKey, (_eventType, el, ev) => {
            const caret = editor.caretManager.getDataCaret();
            if (caret == null) {
                return;
            }
            // Fire it only if it the caret is at the start of the element we are
            // listening on and can't go back.
            if ((caret.offset === 0) &&
                (caret.node === el ||
                    (domtypeguards_1.isText(caret.node) && caret.node === el.firstChild))) {
                if (ev !== undefined) {
                    ev.stopImmediatePropagation();
                    ev.preventDefault();
                }
                editor.fireTransformation(editor.mergeWithPreviousHomogeneousSiblingTr, { node: el, name: el.tagName });
            }
        });
        ret.addKeyHandler(mergeWithNextKey, (_eventType, el, ev) => {
            const caret = editor.caretManager.getDataCaret();
            if (caret == null) {
                return;
            }
            // Fire it only if it the caret is at the end of the element we are
            // listening on and can't actually delete text.
            if ((caret.node === el && caret.offset === el.childNodes.length) ||
                (domtypeguards_1.isText(caret.node) &&
                    caret.node === el.lastChild &&
                    caret.offset === el.lastChild.length)) {
                if (ev !== undefined) {
                    ev.stopImmediatePropagation();
                    ev.preventDefault();
                }
                editor.fireTransformation(editor.mergeWithNextHomogeneousSiblingTr, { node: el, name: el.tagName });
            }
        });
        return ret;
    }
    exports.makeSplitMergeInputTrigger = makeSplitMergeInputTrigger;
});
//  LocalWords:  InputTrigger keydown keypress domutil deleteText lastChild MPL
//  LocalWords:  Dubeau Mangalam
//# sourceMappingURL=input-trigger-factory.js.map