<!DOCTYPE html>
<!--
    Error {{ code }}: {{ message }}
    Description: {{ description }}
-->
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="robots" content="noindex, nofollow"/>
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>{{ message }} ({{ code }})</title>
    <link rel="preconnect" href="https://fonts.bunny.net" crossorigin>
    <link rel="dns-prefetch" href="https://fonts.bunny.net">
    <link href="https://fonts.bunny.net/css2?family=Inconsolata:wght@400;700&display=swap" rel="stylesheet">
    <style>
        :root{--matrix-glyph-size:15px;--matrix-glyph-font-size:15px;--matrix-glyph-front-color:rgba(255, 255, 255, 0.8);--matrix-glyph-tail-color:#0f0;--matrix-overlay-color:rgba(0, 0, 0, 0.12)}
        body,html{margin:0;padding:0;background-color:#000;height:100vh}
        #matrix{display:block;position:fixed;width:100vw;height:100vh}
        .container{align-items:center;display:flex;justify-content:center;position:absolute;top:0;left:0;width:100vw;height:100vh;z-index:1}
        .container .message{background-color:rgba(0,0,0,.85);border:2px solid var(--matrix-glyph-tail-color);padding:15px 20px;margin:0 20px;font-family:Inconsolata,Helvetica,sans-serif;text-align:center;font-size:0;color:var(--matrix-glyph-tail-color);text-shadow:1px 0 2px var(--matrix-glyph-tail-color),-1px 0 2px var(--matrix-glyph-tail-color);box-shadow:1px 0 5px var(--matrix-glyph-tail-color),-1px 0 2px var(--matrix-glyph-tail-color);max-width:640px}
        .container .message h1{margin:0;font-size:52px}
        .container .message p{margin:.3em 0 0 0;font-size:17px;color:var(--matrix-glyph-front-color)}
        /* {{ if show_details }} */
        .container .details{margin-top:20px}
        .container .details ul{padding:0}
        .container .details code,.container .details span{font-size:11px}
        .container .details code{padding-left:7px}
        /* {{ end }} */
        .hidden {display:none}
        @media screen and (max-width:820px){
            :root{--matrix-glyph-size:10px;--matrix-glyph-font-size:10px}
            .container .message h1{font-size:38px}
            .container .message p{font-size:13px}
            /* {{ if show_details }} */
            .container .details code,.container .details span{font-size:11px}
            /* {{ end }} */
        }
    </style>
</head>
<body>

<div class="container">
    <ul id="matrix-words" class="hidden">
        <li>{{ code }}</li>
        <li>{{ message }}</li>
        <li>{{ description }}</li>
        <li>{{ code }} {{ message }}</li>
    </ul>

    <div class="message">
        <h1>{{ code }}: <span data-l10n>{{ message }}</span></h1>
        <p data-l10n>{{ description }}</p>

        {{ if show_details }}
        <div class="details">
            <ul>
                {{- if host }}<li><span><span data-l10n>Host</span>:</span> <code>{{ host }}</code></li>{{ end -}}
                {{- if original_uri }}<li><span><span data-l10n>Original URI</span>:</span> <code>{{ original_uri }}</code></li>{{ end -}}
                {{- if forwarded_for }}<li><span><span data-l10n>Forwarded for</span>:</span> <code>{{ forwarded_for }}</code></li>{{ end -}}
                {{- if namespace }}<li><span><span data-l10n>Namespace</span>:</span> <code>{{ namespace }}</code></li>{{ end -}}
                {{- if ingress_name }}<li><span><span data-l10n>Ingress name</span>:</span> <code>{{ ingress_name }}</code></li>{{ end -}}
                {{- if service_name }}<li><span><span data-l10n>Service name</span>:</span> <code>{{ service_name }}</code></li>{{ end -}}
                {{- if service_port }}<li><span><span data-l10n>Service port</span>:</span> <code>{{ service_port }}</code></li>{{ end -}}
                {{- if request_id }}<li><span><span data-l10n>Request ID</span>:</span> <code>{{ request_id }}</code></li>{{ end -}}
                <li><span><span data-l10n>Timestamp</span>:</span> <code>{{ now.Unix }}</code></li>
            </ul>
        </div>
        {{ end }}
    </div>
</div>

<canvas id="matrix"></canvas>

<script>
    'use strict';

    /**
     * @param {HTMLCanvasElement} $canvas
     * @constructor
     */
    const Matrix = function ($canvas) {
        const symbols = 'ラドクリフマラソンわたしワタシんょンョたばこタバコとうきょうトウキョウ '.split('');

        /**
         * @return {string}
         */
        const getRandomSymbol = function () {
            return symbols[Math.floor(Math.random() * symbols.length)];
        }

        const ctx = $canvas.getContext('2d');
        ctx.globalCompositeOperation = 'lighter'; // https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation

        this.redrawInterval = 90; // fade oud speed
        this.glyphSize = 0;
        this.rowsCapacity = 0;
        this.columnsCapacity = 0;

        /**
         * @return {void}
         */
        this.init = () => {
            $canvas.width = $canvas.clientWidth;
            $canvas.height = $canvas.clientHeight;

            this.glyphSize = parseInt(getComputedStyle($canvas).getPropertyValue('--matrix-glyph-size'), 10);
            this.rowsCapacity = Math.ceil($canvas.clientHeight / this.glyphSize);
            this.columnsCapacity = Math.ceil($canvas.clientWidth / this.glyphSize);
        };

        /**
         * @param {string} symbol
         * @param {number} row
         * @param {number} column
         * @param {string} color
         */
        const drawSymbol = (symbol, row, column, color) => {
            if (row > this.rowsCapacity || column > this.columnsCapacity) {
                return;
            }

            ctx.fillStyle = color;
            ctx.font = getComputedStyle($canvas).getPropertyValue('--matrix-glyph-font-size') + ' monospace';

            if (symbol.length > 1) {
                symbol = symbol.charAt(0); // only one char is allowed
            }

            let xOffset = 0, charCode = symbol.charCodeAt(0);

            if (charCode >= 33 && charCode <= 126) { // is ascii
                xOffset = this.glyphSize / 5;
            }

            ctx.fillText(symbol, (column * this.glyphSize) + xOffset, row * this.glyphSize);
        };

        /**
         * @param {number} column
         * @param {number} speed Lowest = fastest, largest = slowest
         * @param {string?} text
         * @param {number?} offset
         */
        const drawLine = (column, speed, text, offset) => {
            let cursor = 0;

            const tailColor = getComputedStyle($canvas).getPropertyValue('--matrix-glyph-tail-color'),
                frontColor = getComputedStyle($canvas).getPropertyValue('--matrix-glyph-front-color');

            const handler = window.setInterval(() => {
                if (column > this.columnsCapacity) {
                    return window.clearInterval(handler);
                }

                if (cursor <= this.rowsCapacity) {
                    let symbol = getRandomSymbol();

                    if (typeof text === 'string' && typeof offset === 'number') {
                        if (cursor >= offset && text.length >= cursor - offset) {
                            symbol = text.charAt(cursor - offset);
                        }
                    }

                    if (typeof symbol === 'string' && symbol !== ' ') {
                        const prev = cursor;

                        window.setTimeout(() => { // redraw with a green color
                            drawSymbol(symbol, prev, column, tailColor);
                        }, speed / 1.3);

                        drawSymbol(symbol, cursor, column, frontColor); // white color first
                    }

                    cursor++;
                } else {
                    window.clearInterval(handler);
                }
            }, speed);
        };

        /**
         * @return {void}
         */
        this.redraw = () => {
            ctx.fillStyle = getComputedStyle($canvas).getPropertyValue('--matrix-overlay-color');
            ctx.fillRect(0, 0, $canvas.clientWidth, $canvas.clientHeight);
        };

        let redrawIntervalHandler = undefined, dropsIntervalHandler = undefined;

        /**
         * @param {HTMLUListElement?} $linesList
         */
        this.run = ($linesList) => {
            if (redrawIntervalHandler === undefined) {
                redrawIntervalHandler = window.setInterval(this.redraw, this.redrawInterval);
            }

            if (dropsIntervalHandler === undefined) {
                const fn = () => {
                    const randomColumn = Math.floor(Math.random() * (this.columnsCapacity + 1)),
                        minSpeed = 200, maxSpeed = 50,
                        randomSpeed = Math.floor(Math.random() * (maxSpeed - minSpeed + 1)) + minSpeed;

                    const list = [];
                    let line = undefined, offset = undefined;

                    if ($linesList !== undefined) {
                        Array.prototype.forEach.call($linesList.querySelectorAll('li'), $li => {
                            const text = $li.innerText.trim();

                            if (text.length > 0) {
                                list.push(text);
                            }
                        });

                        if (list.length > 0 && Math.random() > 0.4) {
                            line = list[Math.floor(Math.random() * list.length)];
                            offset = Math.floor(Math.random() * line.length);

                            if (offset <= 5) {
                                offset *= 3;
                            }
                        }
                    }

                    drawLine(randomColumn, randomSpeed, line, offset);

                    if (dropsIntervalHandler !== undefined) {
                        window.clearInterval(dropsIntervalHandler);
                        dropsIntervalHandler = undefined;
                    }

                    dropsIntervalHandler = window.setInterval(fn, ((minSpeed + maxSpeed) / 2 * this.rowsCapacity) / this.columnsCapacity / 0.5);
                };

                fn();
            }
        };

        /**
         * @return {void}
         */
        this.stop = () => {
            if (redrawIntervalHandler !== undefined) {
                window.clearInterval(redrawIntervalHandler);
                redrawIntervalHandler = undefined;
            }

            if (dropsIntervalHandler !== undefined) {
                window.clearInterval(dropsIntervalHandler);
                dropsIntervalHandler = undefined;
            }
        };

        if (typeof ResizeObserver === 'function') {
            (new ResizeObserver(this.init)).observe($canvas);
        } else {
            this.init();
        }
    };

    (new Matrix(document.getElementById('matrix'))).run(document.getElementById('matrix-words'));

    // {{ if l10n_enabled }}
    if (navigator.language.substring(0, 2).toLowerCase() !== 'en') {
        ((s, p) => { // localize the page (details here - https://github.com/tarampampam/error-pages/tree/master/l10n)
            s.src = 'https://cdn.jsdelivr.net/gh/tarampampam/error-pages@2/l10n/l10n.min.js'; // '../l10n/l10n.js';
            s.async = s.defer = true;
            s.addEventListener('load', () => p.removeChild(s));
            p.appendChild(s);
        })(document.createElement('script'), document.body);
    }
    // {{ end }}
</script>
</body>
<!--
    Error {{ code }}: {{ message }}
    Description: {{ description }}
-->
</html>