// Detect It Easy: detection rule file

// Author: DosX
// E-Mail: collab@kay-software.ru
// GitHub: https://github.com/DosX-dev
// Telegram: @DosX_dev

function detect() { main(); }

function main() {
    if (Binary.isHeuristicScan()) {
        switch (Binary.getFileSuffix().toLowerCase()) {
            case "js": // JavaScript
            case "jse": // JScript Encoded
            case "jsc": // JavaScript Compiled
            case "sg": // Detect It Easy module
                var options = String();

                if (!Binary.isPlainText()) {
                    options = "bytecode";
                } else {
                    if (Binary.getSize() > 3000) {
                        var scriptContent = Binary.getString(0x00, Binary.getSize());

                        // Split the code into segments that are outside of string literals.
                        // Handle escaping (\", \\', \`) — escaped quotes are not treated as string delimiters.
                        // Also include expressions inside template literals `${...}` as code (they will be scanned).
                        var segments = [],
                            currentSegment = String(),
                            insideString = false,
                            stringDelimiter = String(),
                            isEscaped = false;

                        for (var i = 0; i < scriptContent.length; i++) {
                            var currChar = scriptContent[i];

                            if (insideString) {
                                if (isEscaped) {
                                    isEscaped = false;
                                    continue;
                                }
                                if (currChar === '\\') {
                                    isEscaped = true;
                                    continue;
                                }

                                // For template literals: when encountering `${` include nested expression into current segment
                                if (stringDelimiter === '`' && currChar === '$' && i + 1 < scriptContent.length && scriptContent[i + 1] === '{') {
                                    // skip '{' and start accumulating the expression content
                                    i++; // now scriptContent[i] === '{'
                                    var braceDepth = 1;
                                    // Inside the expression, handle escaping as well
                                    while (i + 1 < scriptContent.length && braceDepth > 0) {
                                        i++;
                                        var innerChar = scriptContent[i];
                                        if (innerChar === '\\') {
                                            // capture escaped char and the next one
                                            currentSegment += innerChar;
                                            if (i + 1 < scriptContent.length) {
                                                i++; currentSegment += scriptContent[i];
                                            }
                                            continue;
                                        }
                                        if (innerChar === '{') { braceDepth++; currentSegment += innerChar; continue; }
                                        if (innerChar === '}') { braceDepth--; if (braceDepth === 0) break; currentSegment += innerChar; continue; }
                                        currentSegment += innerChar;
                                    }
                                    continue;
                                }

                                if (currChar === stringDelimiter) {
                                    insideString = false;
                                    stringDelimiter = String();
                                }
                                // do not copy characters that are inside strings
                            } else {
                                // not inside a string
                                if (currChar === '"' || currChar === "'" || currChar === '`') {
                                    // start of string — finish current segment
                                    if (currentSegment.length > 0) { segments.push(currentSegment); currentSegment = String(); }
                                    insideString = true;
                                    stringDelimiter = currChar;
                                } else {
                                    currentSegment += currChar;
                                }
                            }
                        }

                        if (currentSegment.length > 0) segments.push(currentSegment);

                        for (var segIndex = 0; segIndex < segments.length; segIndex++) {
                            var tokenToProcess = segments[segIndex];
                            if (!/(  |\t)/.test(tokenToProcess) && (
                                /(((var|let|const)[\t ]|\())\b[a-zA-Z](?:,[a-zA-Z]){3,}\b/.test(tokenToProcess) ||
                                /[a-zA-Z][!=]?=?=![01][;,})]/.test(tokenToProcess)
                            )) {
                                options = "minified/compiled";
                                break;
                            }
                        }
                    }
                }

                _setResult("~language", "JavaScript", String(), Binary.isVerbose() ? options : String());

                break;
        }
    }
}