<?php
$self->extends('master');

$self->header_title = 'flash.moe / ascii table';
$self->header_minimal = true;

$self->block('container', function($self) {
    $table = [
        // Control characters
        ['Null character',                'NUL'],
        ['Start of heading',              'SOH'],
        ['Start of text',                 'STX'],
        ['End of text',                   'ETX'],
        ['End of transmission',           'EOT'],
        ['Enquiry',                       'ENQ'],
        ['Acknowledgement',               'ACK'],
        ['Bell',                          'BEL'],
        ['Backspace',                     'BS' ],
        ['Horizontal tab',                'HT' ],
        ['Line feed',                     'LF' ],
        ['Vertical tab',                  'VT' ],
        ['Form feed',                     'FF' ],
        ['Carriage return',               'CR' ],
        ['Shift out/X-On',                'SO' ],
        ['Shift in/X-Off',                'SI' ],
        ['Delta line escape',             'DLE'],
        ['Device control 1 (often XON)',  'DC1'],
        ['Device control 2',              'DC2'],
        ['Device control 3 (often XOFF)', 'DC3'],
        ['Device control 4',              'DC4'],
        ['Negative acknowledgement',      'NAK'],
        ['Synchronous idle',              'SYN'],
        ['End of transmit block',         'ETB'],
        ['Cancel',                        'CAN'],
        ['End of medium',                 'EM' ],
        ['Substitute',                    'SUB'],
        ['Escape',                        'ESC'],
        ['File separator',                'FS' ],
        ['Group separator',               'GS' ],
        ['Record separator',              'RS' ],
        ['Unit separator',                'US' ],

        // Printable characters
        ['Space'],
        ['Excalamation mark'],
        ['Double quotes', 'quot'],
        ['Hash'],
        ['Dollar'],
        ['Percent'],
        ['Ampersand', 'amp'],
        ['Single quote'],
        ['Open parenthesis'],
        ['Close parenthesis'],
        ['Asterisk'],
        ['Plus'],
        ['Comma'],
        ['Hyphen'],
        ['Period'],
        ['Slash'],
        ['Zero'],
        ['One'],
        ['Two'],
        ['Three'],
        ['Four'],
        ['Five'],
        ['Six'],
        ['Seven'],
        ['Eight'],
        ['Nine'],
        ['Colon'],
        ['Semicolon'],
        ['Less than', 'lt'],
        ['Equals'],
        ['Greater than', 'gt'],
        ['Question mark'],
        ['At symbol'],
        ['Uppercase A'],
        ['Uppercase B'],
        ['Uppercase C'],
        ['Uppercase D'],
        ['Uppercase E'],
        ['Uppercase F'],
        ['Uppercase G'],
        ['Uppercase H'],
        ['Uppercase I'],
        ['Uppercase J'],
        ['Uppercase K'],
        ['Uppercase L'],
        ['Uppercase M'],
        ['Uppercase N'],
        ['Uppercase O'],
        ['Uppercase P'],
        ['Uppercase Q'],
        ['Uppercase R'],
        ['Uppercase S'],
        ['Uppercase T'],
        ['Uppercase U'],
        ['Uppercase V'],
        ['Uppercase W'],
        ['Uppercase X'],
        ['Uppercase Y'],
        ['Uppercase Z'],
        ['Opening bracket'],
        ['Backslash'],
        ['Closing bracket'],
        ['Caret'],
        ['Underscore'],
        ['Accent grave'],
        ['Lowercase a'],
        ['Lowercase b'],
        ['Lowercase c'],
        ['Lowercase d'],
        ['Lowercase e'],
        ['Lowercase f'],
        ['Lowercase g'],
        ['Lowercase h'],
        ['Lowercase i'],
        ['Lowercase j'],
        ['Lowercase k'],
        ['Lowercase l'],
        ['Lowercase m'],
        ['Lowercase n'],
        ['Lowercase o'],
        ['Lowercase p'],
        ['Lowercase q'],
        ['Lowercase r'],
        ['Lowercase s'],
        ['Lowercase t'],
        ['Lowercase u'],
        ['Lowercase v'],
        ['Lowercase w'],
        ['Lowercase x'],
        ['Lowercase y'],
        ['Lowercase z'],
        ['Opening curly brace'],
        ['Vertical bar'],
        ['Closing curly brace'],
        ['Tilde'],

        // Delete
        ['Delete', 'DEL'],
    ];
?>
    <div class="ascii-wrap">
        <div class="ascii-search">
            <div class="ascii-search-box">
                <input type="search" id="search" placeholder="Filter..." autocomplete="off">
            </div>
            <div class="ascii-search-hint js-invisible-on-scroll">
                Type <em><code>printable</code></em> for all printable characters, or <em><code>control</code></em> for all control characters.
            </div>
        </div>
        <div class="ascii-chars"><?php
            foreach($table as $code => $info):
                $isPrintable = ($code > 31 && $code < 127);
                $print = $isPrintable ? chr($code) : $info[1];
                $attrs = ['data-key-code' => $code, 'data-key-desc' => $info[0], 'data-key-print' => $print, 'data-copy' => $print];
                $attrStr = '';

                if($isPrintable && isset($info[1]))
                    $attrs['data-key-html'] = $info[1];

                foreach($attrs as $name => $value)
                    $attrStr .= $name . '="' . htmlentities($value) . '" ';
        ?><div class="ascii-char" <?=trim($attrStr);?>>
                <div class="ascii-char-print"><?=$print;?></div>
                <div class="ascii-char-desc"><?=$info[0];?></div>
                <div class="ascii-char-misc">
                    <div class="ascii-char-misc-item" data-copy="<?=$code;?>">
                        <div class="ascii-char-misc-item-head">Decimal</div>
                        <div class="ascii-char-misc-item-value"><?=$code;?></div>
                    </div>
                    <div class="ascii-char-misc-item" data-copy="<?=decoct($code);?>">
                        <div class="ascii-char-misc-item-head">Octal</div>
                        <div class="ascii-char-misc-item-value"><?=decoct($code);?></div>
                    </div>
                    <div class="ascii-char-misc-item" data-copy="<?=dechex($code);?>">
                        <div class="ascii-char-misc-item-head">Hex</div>
                        <div class="ascii-char-misc-item-value"><?=dechex($code);?></div>
                    </div>
                    <?php if(isset($attrs['data-key-html'])): ?>
                    <div class="ascii-char-misc-item" data-copy="&amp;<?=$attrs['data-key-html'];?>;">
                        <div class="ascii-char-misc-item-head">HTML</div>
                        <div class="ascii-char-misc-item-value">&amp;<?=$attrs['data-key-html'];?>;</div>
                    </div>
                    <?php endif; ?>
                </div>
            </div><?php endforeach; ?></div>
    </div>
    <script type="text/javascript">
        var chars = document.getElementsByClassName('ascii-char'),
            search = document.getElementById('search');

        function charsFilter(filter) {
            if(!filter) {
                for(var i = 0; i < chars.length; ++i)
                    chars[i].classList.remove('hidden');
                return;
            }

            filter = filter.toLowerCase();

            for(var i = 0; i < chars.length; ++i) {
                var chr = chars[i],
                    code = (chr.dataset.keyCode || 0).toString().toLowerCase(),
                    print = (chr.dataset.keyPrint || "\0").toString().toLowerCase(),
                    desc = (chr.dataset.keyDesc || '').toString().toLowerCase(),
                    html = (chr.dataset.keyHtml || "\0").toString().toLowerCase(),
                    codeInt = parseInt(code),
                    isMatch = (filter === 'printable' && (codeInt > 31 && codeInt < 127))
                        || (filter === 'control' && (codeInt < 32 || codeInt === 127))
                        || code == filter || print == filter
                        || html == filter || desc.indexOf(filter) >= 0;
                chr.classList[isMatch ? 'remove' : 'add']('hidden');
            }
        };

        window.addEventListener('scroll', function() {
            var hidden = document.getElementsByClassName('js-hidden-on-scroll'),
                invisible = document.getElementsByClassName('js-invisible-on-scroll'),
                atTop = window.scrollY === 0;

            for(var i = 0; i < hidden.length; ++i)
                hidden[i].classList[atTop ? 'remove' : 'add']('hidden');
            for(var i = 0; i < invisible.length; ++i)
                invisible[i].classList[atTop ? 'remove' : 'add']('invisible');
        });
        search.addEventListener('keyup', function() {
            location.hash = search.value.trim();
        });
        window.addEventListener('hashchange', function() {
            charsFilter(decodeURIComponent((location.hash || '#').substring(1)));
        });
        if(location.hash.length > 0) {
            search.value = location.hash.substring(1).trim();
            charsFilter(search.value);
        }

        for(var i = 0; i < chars.length; ++i) {
            chars[i].addEventListener('click', function(ev) {
                var target = ev.target;

                while(target !== null && typeof target.dataset.copy === 'undefined') {
                    target = target.parentNode || null;
                    
                    if(target.classList.contains('char'))
                        break;
                }

                if(target === null || typeof target.dataset.copy === 'undefined')
                    return;

                // Clipboard interactions are fucking horrendous
                /*if(document.execCommand) {
                    /*var clipfriend = document.createElement('input');
                    clipfriend.type = 'text';
                    clipfriend.value = target.dataset.copy;
                    clipfriend.className = 'hidden';
                    document.body.appendChild(clipfriend);
                    clipfriend.select();
                    clipfriend.setSelectionRange(0, clipfriend.value.length);
                    document.execCommand('copy');
                    document.body.removeChild(clipfriend);
                } else {*/
                var doCopy = function() { navigator.clipboard.writeText(target.dataset.copy); };
                
                if(typeof window.mozInnerScreenX !== 'undefined')
                    doCopy();
                else
                    navigator.permissions.query({name: 'clipboard-write'}).then(function(res) {
                        if(res.state === 'granted' || res.state === 'prompt')
                            doCopy();
                    });
                //}
            });
        }
    </script>
<?php
});