client/lualibs/htmlskel.lua
local skel_1 = [[
<html>
<head>

    <script>
        var dictionary =  {}
        function add(commaSeparated)
        {
            var fields = commaSeparated.split(",");
            var manufacturerCode = fields[0];
            var modelCode   = fields[1];
            var modelSubCode= fields[2];
            var modelName   = fields[3];
            var ATQA        = fields[4];
            var SAK         = fields[5];

            //In the data below, wrong endian is used. Mifare is
            // written as "0004" instead of "0400", so we need to
            // flip it
            ATQA = ATQA.substr(2,4)+ATQA.substr(0,2)

            var info = {
                modelCode : modelCode,
                modelSubCode : modelSubCode,
                modelName : modelName,
                ATQA    : ATQA,
                SAK     : SAK
            }
            console.log("Adding "+modelName+" , "+SAK)

            dictionary[ATQA] = dictionary[ATQA] || [];
            dictionary[ATQA].push(info)
        }
        function lookup(atqa, sak)
        {
            if (!dictionary[atqa]) return "UNKNOWN";

            var possibleMatches = [];
            for(var i = 0 ; i < dictionary[atqa].length ; i++)
            {

                var info = dictionary[atqa][i];
                console.log("Comparing "+sak+ " with "+ info.SAK);
                if(sak && info.SAK == sak)//exact match
                {
                    return info.modelName;
//                  possibleMatches.push(info.modelName);
                }else //SAK unknown
                {
                    possibleMatches.push(info.modelName);
                }
            }
            if(possibleMatches.length > 0)
                return possibleMatches.join(" or ");
            return "UNKNOWN"
        }

        add("04,,,Mifare TNP3xxx Activision 1K,0f01,01");
        add("04,,,Mifare Mini,0004,09");
        add("04,,,Mifare Classic 1k/Mifare Plus(4 byte UID) 2K SL1,0004,08");
        add("04,,,Mifare Plus (4 byte UID) 2K SL2,0004,10");
        add("04,,,Mifare Plus (4 byte UID) 4K SL2,0004,11");
        add("04,,,Mifare Plus (4 byte UID) 4K SL1,0004,18");
        add("04,,,Mifare Plus (4 byte UID) 2K/4K SL3,0004,20");
        add("04,,,Mifare Classic 4K,0002,18");
        add("xx,,,NDEF Tags,0044,00  ");
        add("04,,,Mifare Ultralight/UltralightC,0044,04");
        add("04,,,Mifare Plus (7 byte UID) 2K SL1,0042,08");
        add("04,,,Mifare Plus (7 byte UID) 2K SL1,0044,08");
        add("04,,,Mifare Plus (7 byte UID) 4K SL1,0042,18");
        add("04,,,Mifare Plus (7 byte UID) 4K SL1,0044,18");
        add("04,,,Mifare Plus (7 byte UID),0042,10");
        add("04,,,Mifare Plus (7 byte UID),0044,10");
        add("04,,,Mifare Plus (7 byte UID),0042,11");
        add("04,,,Mifare Plus (7 byte UID),0044,11");
        add("04,,,Mifare Plus (7 byte UID),0042,20");
        add("04,,,Mifare Plus (7 byte UID),0044,20");
        add("04,,,Mifare DesFire / DesFire EV1,0344,20067577810280");
        add("04,,,JCOP31,0304,283877B14A434F503331");
        add("04,,,JCOP31 v2.4.1,0048,207877B1024A434F5076323431");
        add("04,,,JCOP41 v2.2,0048,203833B14A434F503431563232");
        add("04,,,JCOP41 v2.3.1,0004,283833B14A434F50343156323331");
        add("05,,,Mifare Classic 1K,0004,88");
        add("40,,,MPCOS,0002,98");
        add("25,,,Topaz/Topaz512/Jewel,0C00,");
        add("1D,,,FM1208SH01,0004,53");
        add("1D,,,FM1208,0008,20");
        add("Nokia,,,Mifare Classic 4K emulated by Nokia 6212,0002,38");
        add("Nokia,,,Mifare Classic 4K emulated by Nokia 6131,0008,38");
        add("04,,,Smart MX with Mifare 4K emulation,0002");
        add("04,,,Smart MX with Mifare 4K emulation,0102");
        add("04,,,Smart MX with Mifare 4K emulation,0202");
        add("04,,,Smart MX with Mifare 4K emulation,0302");
        add("04,,,Smart MX with Mifare 4K emulation,0402");
        add("04,,,Smart MX with Mifare 4K emulation,0502");
        add("04,,,Smart MX with Mifare 4K emulation,0602");
        add("04,,,Smart MX with Mifare 4K emulation,0702");
        add("04,,,Smart MX with Mifare 4K emulation,0802");
        add("04,,,Smart MX with Mifare 4K emulation,0902");
        add("04,,,Smart MX with Mifare 4K emulation,0A02");
        add("04,,,Smart MX with Mifare 4K emulation,0B02");
        add("04,,,Smart MX with Mifare 4K emulation,0C02");
        add("04,,,Smart MX with Mifare 4K emulation,0D02");
        add("04,,,Smart MX with Mifare 4K emulation,0E02");
        add("04,,,Smart MX with Mifare 4K emulation,0F02");
        add("04,,,Smart MX with Mifare 1K emulation,0004");
        add("04,,,Smart MX with Mifare 1K emulation,0104");
        add("04,,,Smart MX with Mifare 1K emulation,0204");
        add("04,,,Smart MX with Mifare 1K emulation,0304");
        add("04,,,Smart MX with Mifare 1K emulation,0404");
        add("04,,,Smart MX with Mifare 1K emulation,0504");
        add("04,,,Smart MX with Mifare 1K emulation,0604");
        add("04,,,Smart MX with Mifare 1K emulation,0704");
        add("04,,,Smart MX with Mifare 1K emulation,0804");
        add("04,,,Smart MX with Mifare 1K emulation,0904");
        add("04,,,Smart MX with Mifare 1K emulation,0A04");
        add("04,,,Smart MX with Mifare 1K emulation,0B04");
        add("04,,,Smart MX with Mifare 1K emulation,0C04");
        add("04,,,Smart MX with Mifare 1K emulation,0D04");
        add("04,,,Smart MX with Mifare 1K emulation,0E04");
        add("04,,,Smart MX with Mifare 1K emulation,0F04");
        add("04,,,Smart MX with 7 byte UID,0048");
        add("04,,,Smart MX with 7 byte UID,0148");
        add("04,,,Smart MX with 7 byte UID,0248");
        add("04,,,Smart MX with 7 byte UID,0348");
        add("04,,,Smart MX with 7 byte UID,0448");
        add("04,,,Smart MX with 7 byte UID,0548");
        add("04,,,Smart MX with 7 byte UID,0648");
        add("04,,,Smart MX with 7 byte UID,0748");
        add("04,,,Smart MX with 7 byte UID,0848");
        add("04,,,Smart MX with 7 byte UID,0948");
        add("04,,,Smart MX with 7 byte UID,0A48");
        add("04,,,Smart MX with 7 byte UID,0B48");
        add("04,,,Smart MX with 7 byte UID,0C48");
        add("04,,,Smart MX with 7 byte UID,0D48");
        add("04,,,Smart MX with 7 byte UID,0E48");
        add("04,,,Smart MX with 7 byte UID,0F48");
    </script>

    <style>
        * {
            background-color: #2F3440;
            background-color:#232323;
            color : #F5E5C0;
            xtext-transform: uppercase;
            font-size: 1.05em;
            font-family: monospace,Arial;
        }
        table{
            float:left;
            border: 1px solid white;
        }
        td {
            empty-cells: show;
        }
        td.blockzero, .turqoise{
            color: rgb(140, 245, 193);
        }
        td.key_a, .yellow{
            color : #F8CA4D;
        }
        td.key_b, .blue{
            color : #3F5666;
        }
        td.accessconditions, .red{
            color : #EA6045;
        }

        td.sectorhdr{
            border-top: 1px solid white;
        }
    </style>
    <script>
    /** Jquery for the poor **/
    function dc(x){return document.createElement(x)}

    function tr(table){
        var row = dc('tr');
        table.appendChild(row);
        return row;
    }
    function td(row, text){
        var tdata = dc('td');
        row.appendChild(tdata);
        tdata.appendChild(document.createTextNode(text))
        return tdata;
    }

    /**
    * The identifiers that determine how to highlight data and present information
    **/
    var identifiers = [
        function(data)
        {
            // Should be 32 characters long ( 16 bytes per block)
            if(data[0].length != 32) { return false; }
            // ... add more checks if necessary ...

            var info = {Type : "Mifare"}
            info['Size'] = (data[0].length / 2 * data.length) + " Bytes";
            info['UID'] = data[0].substring(0,8);
            info['SAK'] = data[0].substring(10,12);
            info['ATQA'] = data[0].substring(12,16);

            info['Name'] = lookup(info.ATQA, info.SAK);
            return {info: info, highlighter : mifareHighlighter }
        },
        function(data)
        {
            // Should be 8 characters long ( 4 bytes per block)
            if(data[0].length != 8) { return false; }
            // ... add more checks if necessary ...
            var info = {Type : "NDEF"}
            info['Size'] = (data[0].length / 2 * data.length) + " Bytes";

            return {info: info, highlighter : ndefHighligheter }
        },

        function(data)
        {//This is the catch-all
            return {info: {type : "Unknown"}, highlighter : noHighlighter}
        }
    ]


    /**
    * Helper function to convert bin-data into printable chars
    **/

    function to_ascii(hexval)
    {
        var intval = parseInt(hexval,16);
        if(intval > 31 && intval < 127)
        {
            return String.fromCharCode(intval);
        }
        return ".";
    }


    function loadIntoTable(data, info, ascii)
    {
        var t = dc("table")
        for(var i = 0 ; i < data.length ; i++)
        {
            line = data[i];
            var row = tr(t);
            var bytes = line.match(/(.{1,2})/g);
            for(var b = 0 ; b < bytes.length ; b++)
            {
                    var elem = td(row, ascii ? to_ascii(bytes[b]) : bytes[b]);
                    info.highlighter.addClass(elem,i,b, bytes[b]);
            }
        }
        document.body.appendChild(t);
    }
    function loadGeneralInfo(data, info)
    {
        var t = dc("table");
        for (var key in info)
        {
            var row = tr(t);
            td(row,key);
            td(row,info[key]);
        }
        document.body.appendChild(t);
    }

    function handle(dump)
    {
        var data = dump.data;
        var info = null;
        for(var i = 0; i < identifiers.length && !info; i++)
            info = identifiers[i](data);

        console.log(info);

        loadIntoTable(data, info, false);
        loadIntoTable(data, info, true);
        loadGeneralInfo(data, info.info);

    }
    var noHighlighter = {
        addClass : function(el ,line, byte)
        {
            return;
        }
    };
    var ndefHighligheter = {
        addClass : function(el ,line, byte, value)
        {
            if(line  < 3)
            {
                el.className += " red";
            }
            if ( line == 3)
            {
                console.log(value);
                if( byte == 0 && "e1" == value.toLowerCase()) el.className += " turqoise";
                if( byte == 1 ) el.className += " yellow";
                if( byte == 2 ) el.className += " blue";
                return;
            }
        }
    };
    var mifareHighlighter = {
        addClass : function(el ,line, byte)
        {
            if (line == 0)
            {
                el.className += " blockzero";
            }

            if(line < 128){
                linesPerSector = 4;
            } else {
                //Quadruple size sectors
                linesPerSector = 16;
                line = line - 128;
            }

            if(line % linesPerSector == 0)
            {
                el.className += " sectorhdr";
            }
            if(line % linesPerSector == (linesPerSector -1))
            {
                el.className += " sectortrailer";
                if(byte == undefined)
                {
                    return;
                }

                if(byte < 6) el.className += " key_a";
                else if(byte < 10) el.className += " accessconditions";
                else el.className += " key_b";
            }

        }
    };

    </script>

</head>
<body></body>
<script>
    var x = { data :
]]
local skel_2 = [[

        };
    handle(x);
    </script>
</html>

]]
local function getHTML(data)
    return skel_1 .. data .. skel_2
end

return {getHTML = getHTML}
