shine-testutils 0.2.0

SHINE. Unit test helpers for the shine engine.
<!DOCTYPE html>
<html>

<head>
    <meta http-equiv="Cache-Control" content="no-cache,no-store,must-revalidate">
    <meta http-equiv="Pragma" content="no-cache">
    <meta http-equiv="Expires" content="0">

    <style style="text/css">
        body {
            margin: 0;  
            max-height: 100vh;
            height: 100vh;

            height: 100%;
            background: #343a3f;
            color: #FFF;
            font-family: Helvetica;
            text-align: center;

            display: grid;
            grid-template-rows: 1fr 5fr;
            grid-template-columns: 1fr 5fr;
            grid-template-areas: 
                "header header"
                "nav main";
            grid-gap: .25em;        
        }
        header {
            grid-area: header;
        }
        nav {
            grid-area: nav;
            overflow: auto;
        }
        main {
            grid-area: main;
            overflow: hidden;
        }
        header, nav, main {
            border: 1px solid rgba(255,255,255,0.8);
            border-radius: 3px;
        }

        .controls button {
            background-color: #4CAF50;
            color: white;
            border: 0;
            font-family: Helvetica;
            text-decoration: none;
            display: inline-block;
            padding: 8px 16px;
            margin: 3px;
        }
        .controls button:hover {
            background-color: #ddd;
            color: black;
        }  

        .groups ul { 
            padding: 0 0 0 0;
            list-style: none;
            text-align: left;
        }
        .groups li { 
            padding: 2px 0 2px 32px;
        }
    </style>
</head>

<script src="jscript/shine/shine.js"></script>
<script src="jscript/svg-pan-zoom/svg-pan-zoom.min.js" type="text/javascript"></script>
<script src="jscript/stats/stats.min.js"></script>

<script type="text/javascript">
    var images = {{ svg_list | safe }};
    var image_id = 0;
    var stats, shine;
    var svg = null;
    var svgPreserveSize = [];
    var panZoom = null;

    function init() {
        shine = new Shine();

        stats = new Stats();
        document.body.appendChild(stats.dom);
    }

    function releaseSVG() {
        let groupContent = document.getElementById('groups');
        while (groupContent.firstChild) {
            groupContent.removeChild(groupContent.firstChild);
        }

        let svgContent = document.getElementById('svgContainer');
        while (svgContent.firstChild) {
            svgContent.removeChild(svgContent.firstChild)
        }

        if (panZoom) {
            panZoom.destroy();
            panZoom = null;
        }

        svg = null;
        svgPreserveSize.length = 0;
    }

    function createGroupItem(groupContent, node, name) {
        let checkbox = document.createElement('input');
        checkbox.type = 'checkbox';
        checkbox.checked = true;
        checkbox.onchange = function (event) {
            let visible = event.target.checked;
            node.setAttribute('opacity', visible ? '1' : '0');
        };

        let label = document.createElement('label')
        label.appendChild(document.createTextNode(name));

        let li = document.createElement('li')
        li.appendChild(checkbox);
        li.appendChild(label);

        groupContent.appendChild(li);
        return li;
    }

    function createSVG() {
        let titleContent = document.getElementById('title');
        let svgContent = document.getElementById('svgContainer');
        let groupContent = document.getElementById('groups');

        //console.log("selecting data: ", image_id)
        if (image_id < 0 || image_id >= images.length) {
            // no image
            //console.log("empty data");
            svgContent.innerHTML = ""
            titleContent.innerHTML = "Image - None" + "/" + (images.length).toString()
            return;
        }

        svgContent.innerHTML = images[image_id];
        titleContent.innerHTML = "Image - " + (image_id + 1).toString() + "/" + (images.length).toString()

        let childNodes = svgContent.childNodes;
        for (let i = 0; i < childNodes.length; i++) {
            if (childNodes[i].nodeName == 'svg') {
                svg = childNodes[i];
                break;
            }
        }
        svg.setAttribute('style', 'display: inline; min-width: 100%; min-height: 100%; max-width: 100%; max-height: 100%')

        //parse svg
        let openNodes = [[svg, groupContent]];
        let auto_id = 0;
        while (openNodes.length > 0) {
            let current = openNodes.pop();
            let children = current[0].children;
            let docParent = current[1];
            let listParent = undefined;
            for (let i = 0; i < children.length; i++) {
                let node = children[i];
                if (node.tagName == 'g') {
                    let name = node.getAttribute('group-name');
                    auto_id += 1;
                    if (!name) { 'group_' + auto_id; }

                    // groups to preserve size
                    if (node.getAttribute('preserve-size')) {
                        // for now it means text
                        svgPreserveSize.push(node);
                    }

                    let childNode;
                    if (name != '*') {
                        //groups
                        if (!listParent) {
                            listParent = docParent.appendChild(document.createElement('ul'));
                        }
                        childNode = createGroupItem(listParent, node, name);
                    }
                    else {
                        childNode = docParent;
                    }
                    openNodes.push([node, childNode]);
                }
            }
        }

        panZoom = svgPanZoom('svg', {
            zoomEnabled: true,
            controlIconsEnabled: true,
            fit: true,
            center: true,
            zoomScaleSensitivity: 0.5,
            onZoom: function (v) {
                let scl = 1 / v;
                svgPreserveSize.forEach(node => {
                    let trsf = node.getAttribute('transform').replace(/scale\(([^)]+)\)/i, 'scale(' + scl + ')');
                    node.setAttribute('transform', trsf);
                })
            }
        });
    }

    function selectSVG(id, preserveZoom, preserveVisibility) {
        image_id = id
        if (image_id < 0) image_id = 0;
        if (image_id >= images.length) image_id = images.length - 1;

        if (!panZoom) {
            createSVG();
            return
        }

        let zoom = panZoom.getZoom();
        let pan = panZoom.getPan();
        releaseSVG();
        createSVG();

        if (panZoom && preserveZoom) {
            panZoom.zoom(zoom);
            panZoom.pan(pan);
        }
    }

    function step(delta) {
        console.log(delta)
        selectSVG(image_id + delta, true, true)
    }

    function animate() {
        requestAnimationFrame(animate);
        stats.update();
    }

    function onWindowResize() {
        if (panZoom) {
            panZoom.resize();
            panZoom.fit();
            panZoom.center();
        };
    }

    window.onload = function () {
        init();
        selectSVG(0, false, false);
        animate();
    }
    window.onresize = function () {
        onWindowResize();
    }    
</script>

<body>
    <header>
        <h1 id="title">Image</h1>
        <div class="controls">
            <button onclick="step(Number.MIN_SAFE_INTEGER)">First</button>
            <button onclick="step(-10)">&laquo; &laquo; Previous</button>
            <button onclick="step(-1)">&laquo; Previous</button>
            <button onclick="step(1)">Next &raquo;</button>
            <button onclick="step(10)">Next &raquo; &raquo;</button>
            <button onclick="step(Number.MAX_SAFE_INTEGER)">Last</button>
        </div>
        <div class="controls">
            <button onclick="location.reload(true)">Refresh</button>
            <button onclick="shine.notifyUser();location.reload(true)">Continue</button>
        </div>
    </header>
    <nav>
        <div id="groups" class="groups" />
    </nav>
    <main>
        <div id="svgContainer" style="height: 100%;" />
    </main>
</body>

</html>