Object.defineProperty(exports, '__esModule', { value: true });

var helpers_js = require('../helpers.js');

const noWhitespaceCollapseElements = new Set([
    'script',
    'style',
    'pre',
    'textarea',
    'template'
]);
const noTrimWhitespacesArroundElements = new Set([
    // non-empty tags that will maintain whitespace around them
    'a',
    'abbr',
    'acronym',
    'b',
    'bdi',
    'bdo',
    'big',
    'button',
    'cite',
    'code',
    'del',
    'dfn',
    'em',
    'font',
    'i',
    'ins',
    'kbd',
    'label',
    'mark',
    'math',
    'nobr',
    'object',
    'q',
    'rp',
    'rt',
    'rtc',
    'ruby',
    's',
    'samp',
    'select',
    'small',
    'span',
    'strike',
    'strong',
    'sub',
    'sup',
    'svg',
    'textarea',
    'time',
    'tt',
    'u',
    'var',
    // self-closing tags that will maintain whitespace around them
    'comment',
    'img',
    'input',
    'wbr'
]);
const noTrimWhitespacesInsideElements = new Set([
    // non-empty tags that will maintain whitespace within them
    'a',
    'abbr',
    'acronym',
    'b',
    'bdi',
    'bdo',
    'big',
    'cite',
    'code',
    'del',
    'dfn',
    'em',
    'font',
    'i',
    'ins',
    'kbd',
    'label',
    'mark',
    'nobr',
    'q',
    'rp',
    'rt',
    'rtc',
    'ruby',
    's',
    'samp',
    'small',
    'span',
    'strike',
    'strong',
    'sub',
    'sup',
    'time',
    'tt',
    'u',
    'var'
]);
const startsWithWhitespacePattern = /^\s/;
const endsWithWhitespacePattern = /\s$/;
// See https://infra.spec.whatwg.org/#strip-and-collapse-ascii-whitespace and https://infra.spec.whatwg.org/#ascii-whitespace
const multipleWhitespacePattern = /[\t\n\f\r ]+/g;
const NONE = '';
const SINGLE_SPACE = ' ';
const validOptions = [
    'all',
    'aggressive',
    'conservative'
];
function collapseWhitespace(tree, options, collapseType, parent) {
    collapseType = validOptions.includes(collapseType) ? collapseType : 'conservative';
    tree.forEach((node, index)=>{
        const prevNode = tree[index - 1];
        const nextNode = tree[index + 1];
        if (typeof node === 'string') {
            const parentNodeTag = parent == null ? void 0 : parent.node.tag;
            const isTopLevel = parentNodeTag == null || parentNodeTag === 'html' || parentNodeTag === 'head';
            const shouldTrim = isTopLevel || collapseType === 'all' || collapseType === 'aggressive';
            node = collapseRedundantWhitespaces(node, collapseType, shouldTrim, parent, prevNode, nextNode);
        } else if (node.tag) {
            var _node_content;
            const isAllowCollapseWhitespace = !noWhitespaceCollapseElements.has(node.tag);
            if (isAllowCollapseWhitespace && ((_node_content = node.content) == null ? void 0 : _node_content.length)) {
                node.content = collapseWhitespace(node.content, options, collapseType, {
                    node,
                    prevNode});
            }
        }
        tree[index] = node;
    });
    return tree;
}
function collapseRedundantWhitespaces(text, collapseType, shouldTrim = false, parent, prevNode, nextNode) {
    if (!text || text.length === 0) {
        return NONE;
    }
    if (!helpers_js.isComment(text)) {
        text = text.replace(multipleWhitespacePattern, SINGLE_SPACE);
    }
    if (shouldTrim) {
        // either all or top level, trim all
        if (collapseType === 'all' || collapseType === 'conservative') {
            return text.trim();
        }
        if (collapseType === 'aggressive' && text.trim().length === 0 && (isTrimmableAroundNode(prevNode) || prevNode == null) && (isTrimmableAroundNode(nextNode) || nextNode == null) && !(isCommentNode(prevNode) && isCommentNode(nextNode))) {
            return NONE;
        }
        if (typeof parent !== 'object' || !(parent == null ? void 0 : parent.node.tag) || !noTrimWhitespacesInsideElements.has(parent.node.tag)) {
            if (// It is the first child node of the parent
            !prevNode || typeof prevNode === 'object' && prevNode.tag && !noTrimWhitespacesArroundElements.has(prevNode.tag)) {
                text = text.trimStart();
            } else {
                // previous node is a "no trim whitespaces arround element"
                if (// but previous node ends with a whitespace
                typeof prevNode === 'object' && prevNode.content) {
                    const prevNodeLastContent = prevNode.content[prevNode.content.length - 1];
                    if (typeof prevNodeLastContent === 'string' && endsWithWhitespacePattern.test(prevNodeLastContent) && (!nextNode // either the current node is the last child of the parent
                     || // or the next node starts with a white space
                    typeof nextNode === 'object' && nextNode.content && typeof nextNode.content[0] === 'string' && !startsWithWhitespacePattern.test(nextNode.content[0]))) {
                        text = text.trimStart();
                    }
                }
            }
            if (!nextNode || typeof nextNode === 'object' && nextNode.tag && !noTrimWhitespacesArroundElements.has(nextNode.tag)) {
                text = text.trimEnd();
            }
        } else {
            var _parent_prevNode_;
            // now it is a textNode inside a "no trim whitespaces inside elements" node
            if (!prevNode // it the textnode is the first child of the node
             && startsWithWhitespacePattern.test(text[0]) // it starts with white space
             && typeof (parent == null ? void 0 : parent.prevNode) === 'string' // the prev of the node is a textNode as well
             && endsWithWhitespacePattern.test((_parent_prevNode_ = parent.prevNode[parent.prevNode.length - 1]) != null ? _parent_prevNode_ : '') // that prev is ends with a white
            ) {
                text = text.trimStart();
            }
        }
    }
    return text;
}
function isTrimmableAroundNode(node) {
    if (!node) return true;
    if (typeof node === 'string') return helpers_js.isComment(node);
    return typeof node.tag === 'string' && !noTrimWhitespacesArroundElements.has(node.tag);
}
function isCommentNode(node) {
    return typeof node === 'string' && helpers_js.isComment(node);
}
const mod = {
    default: collapseWhitespace
};

exports.default = mod;
