/// #### html
///
/// HTML tags are kept as they are. Moreover, it also preserves whole contents of the following tags: pre, code, style,
/// script.

export default function () {
    return function (input) {
        var result = null;
        var last = 0;
        var output = [];

        while ((result = findTag(input, last)) !== null) {
            output.push({ transform: true, content: input.slice(last, result[0]) });
            output.push({ transform: false, content: input.slice(result[0], result[1]) });
            last = result[1];
        }

        output.push({ transform: true, content: input.slice(last) });

        return output;
    };
}

function findTag(input, last) {
    // global flag needed for setting `lastIndex` property when doing `exec`
    var pattern = /<\/?[a-z][a-z0-9-_]*/gi;
    pattern.lastIndex = last;

    var tagEnd;
    var result = null;
    if ((result = pattern.exec(input)) !== null) {
        var tag = result[0];
        var start = result.index;
        if (['<pre', '<code', '<style', '<script'].indexOf(tag) != -1) {
            var closeTag = new RegExp(tag[0] + '/' + tag.slice(1), 'gi');
            closeTag.lastIndex = pattern.lastIndex;
            if ((result = closeTag.exec(input)) !== null) {
                tagEnd = findTagEnd(input, closeTag.lastIndex);
                return [start, tagEnd];
            } else {
                // not closed special tag
                return [start, input.length];
            }
        } else {
            tagEnd = findTagEnd(input, pattern.lastIndex);
            return [start, tagEnd];
        }
    } else {
        return null;
    }
}

function findTagEnd(input, last) {
    var state = 'initial';
    var escape = false;

    for (var i = last; i < input.length; i++) {
        if (state === 'initial') {
            if (input[i] === '>') {
                return i + 1;
            } else if (input[i] === '"') {
                state = 'double';
            } else if (input[i] === '\'') {
                state = 'single';
            }
        } else if (state === 'double') {
            if (input[i] === '"' && !escape) {
                state = 'initial';
            } else if (input[i] === '\\') {
                escape = true;
            } else {
                escape = false;
            }
        } else if (state === 'single') {
            if (input[i] === '\'' && !escape) {
                state = 'initial';
            } else if (input[i] === '\\') {
                escape = true;
            } else {
                escape = false;
            }
        }
    }

    return input.length;
}

export function tests() {
    return [
        {
            description: 'pair tags',
            input: 'lorem <b>ipsum</b> dolor',
            expected: 'lorem ipsum dolor'
        },
        {
            description: 'single tag',
            input: 'lorem <br />ipsum<br> dolor',
            expected: 'lorem ipsum dolor'
        }, {
            description: 'pre tag',
            input: 'lorem <pre>ipsum</pre> dolor',
            expected: 'lorem  dolor'
        }, {
            description: 'code tag',
            input: 'lorem <code>ipsum</code> dolor',
            expected: 'lorem  dolor'
        }, {
            description: 'custom tag starting with pre',
            input: 'lorem <precision>ipsum</precision> dolor',
            expected: 'lorem ipsum dolor'
        }, {
            description: 'custom tag starting with code',
            input: 'lorem <codec>ipsum</codec> dolor',
            expected: 'lorem ipsum dolor'
        }, {
            description: 'style tag',
            input: '<style>\nhtml { height: 100%; }\n</style>',
            expected: ''
        }, {
            description: 'script tag',
            input: '<script>\nconsole.log("Hello world!")\n</script>',
            expected: ''
        }, {
            description: 'multiple scripts',
            input: '<script>lorem</script> ipsum <script>dolor</script>',
            expected: ' ipsum '
        }, {
            description: 'complex attributes',
            input: '<i class="is this > even\\" legit?">lorem</i>',
            expected: 'lorem'
        }
    ];
}

@@ -791,10 +791,12 @@
 = /%3C
-%5C/
+%5B/!%5D
 ?%5Ba-
@@ -808,16 +808,21 @@
 z0-9-_%5D*
+%7C%3C!--
 /gi;%0A   
@@ -966,16 +966,30 @@
 esult%5B0%5D
+.toLowerCase()
 ;%0A      
@@ -1027,16 +1027,164 @@
      if 
+(tag === '%3C!--') %7B%0A            var commentEnd = findCommentEnd(input, pattern.lastIndex);%0A            return %5Bstart, commentEnd%5D;%0A        %7D else if 
 (%5B'%3Cpre'
@@ -2796,16 +2796,270 @@
 gth;%0A%7D%0A%0A
+function findCommentEnd(input, last) %7B%0A    var pattern = /--%3E/g;%0A    pattern.lastIndex = last;%0A%0A    var result = null;%0A    if ((result = pattern.exec(input)) !== null) %7B%0A        return result.index + 3;%0A    %7D else %7B%0A        return input.length;%0A    %7D%0A%7D%0A%0A
 export f
@@ -4608,16 +4608,273 @@
 'lorem'%0A
+        %7D, %7B%0A            description: 'html comments',%0A            input: 'lorem %3C!-- comment --%3E ipsum',%0A            expected: 'lorem  ipsum',%0A        %7D, %7B%0A            description: 'doctype',%0A            input: '%3C!DOCTYPE html%3E',%0A            expected: '',%0A
         

Handle doctype tag and comments in html format