Katochimoto/x-bubbles

View on GitHub
src/core/bubbleset.js

Summary

Maintainability
D
2 days
Test Coverage
const bubble = require('./bubble');
const text = require('./text');

exports.closestNodeBubble = closestNodeBubble;
exports.closestNodeSet = closestNodeSet;
exports.findBubbleLeft = findBubbleLeft;
exports.findBubbleRight = findBubbleRight;
exports.getBubbles = getBubbles;
exports.hasBubbles = hasBubbles;
exports.headBubble = headBubble;
exports.lastBubble = lastBubble;
exports.nextBubble = nextBubble;
exports.prevBubble = prevBubble;
exports.removeBubbles = removeBubbles;
exports.moveBubbles = moveBubbles;

function lastBubble(nodeSet) {
    return nodeSet.querySelector('[bubble]:last-child');
}

function headBubble(nodeSet) {
    return nodeSet.querySelector('[bubble]:first-child');
}

function getBubbles(nodeSet) {
    return Array.prototype.slice.call(nodeSet.querySelectorAll('[bubble]'));
}

function hasBubbles(nodeSet) {
    return Boolean(nodeSet.querySelector('[bubble]'));
}

function findBubbleLeft(selection) {
    if (!selection.focusNode || !selection.anchorNode) {
        return;
    }

    let node = selection.focusNode.previousSibling;

    if (selection.anchorNode !== selection.focusNode &&
        selection.anchorNode.compareDocumentPosition(selection.focusNode) & Node.DOCUMENT_POSITION_FOLLOWING) {
        node = selection.anchorNode.previousSibling;
    }

    while (node) {
        if (bubble.isBubbleNode(node)) {
            return node;
        }

        if (node.nodeType === Node.TEXT_NODE && text.textClean(node.nodeValue)) {
            return;
        }

        node = node.previousSibling;
    }
}

function findBubbleRight(selection) {
    if (!selection.focusNode || !selection.anchorNode) {
        return;
    }

    let node = selection.focusNode.nextSibling;

    if (selection.anchorNode !== selection.focusNode &&
        selection.anchorNode.compareDocumentPosition(selection.focusNode) & Node.DOCUMENT_POSITION_FOLLOWING) {
        node = selection.anchorNode.nextSibling;
    }

    while (node) {
        if (bubble.isBubbleNode(node)) {
            return node;
        }

        if (node.nodeType === Node.TEXT_NODE && text.textClean(node.nodeValue)) {
            return;
        }

        node = node.nextSibling;
    }
}

function closestNodeSet(node) {
    while (node) {
        if (isEditorNode(node)) {
            return node;
        }

        node = node.parentNode;
    }
}

function closestNodeBubble(node) {
    while (node) {
        if (bubble.isBubbleNode(node)) {
            return node;
        }

        if (isEditorNode(node)) {
            return;
        }

        node = node.parentNode;
    }
}

function prevBubble(target) {
    let node = target && target.previousSibling;
    while (node) {
        if (bubble.isBubbleNode(node)) {
            return node;
        }

        node = node.previousSibling;
    }
}

function nextBubble(target) {
    let node = target && target.nextSibling;
    while (node) {
        if (bubble.isBubbleNode(node)) {
            return node;
        }

        node = node.nextSibling;
    }
}

function isEditorNode(node) {
    return (
        node.nodeType === Node.ELEMENT_NODE &&
        node.getAttribute('is') === 'x-bubbles'
    );
}

function removeBubbles(nodeEditor, list) {
    nodeEditor.fireBeforeRemove(list);
    list.forEach(item => nodeEditor.removeChild(item));
}

function moveBubbles(nodeEditorFrom, nodeEditorTo, list) {
    nodeEditorFrom.fireBeforeRemove(list);
    list.forEach(item => nodeEditorTo.appendChild(item));
}