old-run.php


<html>
<head>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/js-beautify/1.10.1/beautify-html.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/js-beautify/1.10.1/beautify.js"></script>
<style>

    html,body,table,tr {
        min-width:100%;
        min-height:100%;
        padding:0;
        margin:0;
        height:100%;
    }
    td {
        width: 33%;
        min-height:100%;
    }

    td > div {
        width:100%;
        height:100%;
    }

    #blocks > div {
        cursor:pointer;
    }

    #block_creator {
        background:blue;
        display:none;
    }

</style>

<script>
    function allowDrop(ev) {
        ev.preventDefault();
    }

    function drag(ev) {
        ev.dataTransfer.setData("html", ev.target.outerHTML);
    }
    function nodeLocation(node){
        var range = document.createRange();
        range.selectNode(node);
        var rects = range.getClientRects();
        if (rects.length > 0) {
            return rects[0]['y'];
        }
    }
    function isTopHalf(y,node){
        if (typeof node !== 'object' 
            ||node.nodeName=='#text')return true;
        var range = document.createRange();
        range.selectNode(node);
        //range.selectNodeContents(node);
        var rects = range.getClientRects();
        if (typeof rects === undefined
            ||typeof rects[0] === undefined)return true;
        const nodeY = rects[0]['y'];
        const nodeEnd = nodeY + node.offsetHeight;
        const avg = (nodeY+nodeEnd)/2;


        if (y<avg)return true;
        else return false;
    }
    function getClosestVerticalChild(y,targetNode){
        var closestChild;
        var diff = 0;
        var nodeY;
        for(childNode of targetNode.childNodes){
            // if (typeof childNode !== 'object'
            //     ||childNode.nodeName=='#text')continue;
            nodeY = nodeLocation(childNode);
            if (diff===0
                ||diff>Math.abs(y-nodeY)){
                    diff = Math.abs(y-nodeY);
                    closestChild = childNode;
                }
        }
        return closestChild;
    }
    function htmlAsNode(html){
        var div = document.createElement('div');
        div.innerHTML = html.trim();

        return div.firstChild;
    }
    function blockAsContentNode(node){
        if (node.className=='block'){
            node.innerHTML = node.innerHTML.trim();
            node = node.firstChild;
        }
        return node;
    }
    function placeNearClosestChild(event){
        const cursorY = event.clientY;
        var contentNode = event.target;
        var insertHtml = event.dataTransfer.getData("html");
        let relatedChild = getClosestVerticalChild(cursorY,contentNode);
        let goesAbove = isTopHalf(cursorY,relatedChild);
        let insertNode = blockAsContentNode(htmlAsNode(insertHtml));

        const existingNode = (relatedChild === undefined ||relatedChild === null || typeof relatedChild ===undefined) ? contentNode.firstChild : 
                                (goesAbove ? relatedChild : relatedChild.nextSibling);

        placeNode(insertNode,existingNode);
    }
    function placeNearNode(event,newNode){
        var lastParent = event.target;
        const cursorY = event.clientY;
        const content = document.getElementById("content");
        while (lastParent!==content
                &&lastParent.parentNode!==content){
            lastParent = lastParent.parentNode;
        }
        let goesAbove = isTopHalf(cursorY,lastParent);
        const existingNode = goesAbove ? lastParent : lastParent.nextSibling;
        placeNode(newNode,existingNode);
    }


    var nodeMap = [];

    function placeNode(newNode,existingNode){
        const content = document.getElementById("content");
        existingNode.parentNode.insertBefore(newNode,existingNode);
        const settings = newNode.getAttribute('data-editable');
        newNode.removeAttribute('data-editable');
        id = nodeMap.length;
        nodeMap[id] = {'settings':settings==null ? '' : settings, 'node':newNode};

    }
    function drop(event) {
        event.preventDefault();
        const cursorY = event.clientY;
        var data = event.dataTransfer.getData("html");
        var wrapper = document.createElement('div');
        wrapper.innerHTML = data;
        var insertNode = wrapper.firstChild.firstChild;
        while (insertNode.nodeName=="#text"){
            insertNode = insertNode.nextSibling;
        }

        if (event.target.getAttribute("id")=="content"){
            placeNearClosestChild(event);
        } else {
            placeNearNode(event,insertNode);
        }
    
        updateHtml();
    }
    function updateHtml(){
        const html = document.getElementById("html");
        html.value = html_beautify(document.getElementById("content").innerHTML);
    }
    function saveSetting(input){
        const editable = input.getAttribute('name');
        const value = input.value;
        const nodeId = input.parentNode.getAttribute('data-nodeid');
        const node = nodeMap[nodeId]['node'];
        setEditableValue(node, editable, value);
        //idMap[nodeId] = node;
        //input.addEventListener('keyup',this);
       // updateHtml();
    }
    function setEditableValue(node, editable, value){
        if (editable in node){
            node[editable] = value;
        } else {
            node.setAttribute(editable,value);
        }
        updateHtml();
    }
    function getEditableValue(node,editable){
        if (editable in node){
            return node[editable];
        } else {
            return node.getAttribute(editable);
        }
    }
    var idMap = [];
    function getSettingsHtml(node){

        let settings = '';
        let nodeId = -1;
        for (let i=0;i<nodeMap.length;i++){
            if (nodeMap[i]['node']===node){
                nodeId = i;
                settings = nodeMap[i]['settings'];
                break;
            }
        }
        let editables = settings.split(',');
        editables.push('id');
        editables.push('className');
        editables.push('style');
        function onlyUnique(value, index, self) { 
            return self.indexOf(value) === index;
        }
        editables = editables.filter(onlyUnique);
        var html = '<div data-nodeid="'+nodeId+'">'
                    +'<h1>Settings</h1>';
        for (let i=0;i<editables.length;i++){
            const editable = editables[i].trim();
            html = html 
                    + '<label for="'+editable+'">'+editable+'</label>'
                    + '<br>'
                    + '<input onkeyup="saveSetting(this);" type="text" name="'+editable+'" value="'+getEditableValue(node,editable)+'">'
                    + '<br>';
        }
        html = html 
                //+ '<button onclick="updateSettings();">Update</button>'
                + '<br>'
                + '<button onclick="moveUp(getNodeFromSettings(this));">Move Up</button>'
                + '&nbsp; <button onclick="moveDown(getNodeFromSettings(this));">Move Down</button>'
                + '<br><br>'
                + '<button onclick="deleteNode(getNodeFromSettings(this));">Delete Node</button>'
                + '</div>';
        return html;
    }
    function moveDown(node){
        const parent = node.parentNode;
        const sibling = node.nextSibling;
        if (sibling != null){
            const nextSibling = sibling.nextSibling;
            parent.removeChild(node);
            parent.insertBefore(node,nextSibling);
        } else {
            console.log('next sibling null');
        }
        updateHtml();
    }
    function moveUp(node){
        const parent = node.parentNode;
        const sibling = node.previousSibling;
        if (sibling != null){
            parent.removeChild(node);
            parent.insertBefore(node,sibling);
        } else {
            console.log('prev sibling null');
            console.log(node);
        }
        updateHtml();
    }
    function getNodeFromSettings(settingsNode){
        const nodeId = settingsNode.parentNode.getAttribute('data-nodeid');
        const node = nodeMap[nodeId]['node'];
        return node;
    }
    function deleteNode(node){
        node.parentNode.removeChild(node);
    }
    function editBlock(event){
        const node = event.target;
        const settingsNode = document.getElementById("settings");
        settingsNode.innerHTML = getSettingsHtml(node);
    }

    function blocksAreEditable(){
        const blockCreator = document.getElementById("block_creator");
        return (blockCreator.style.display=='block');
    }
    function enableEditing(){
        const blockCreator = document.getElementById("block_creator");
        const contentArea = document.getElementById("content");
        const toggleButton = document.getElementById("toggle_edit_button");
        toggleButton.innerText = 'Disable Block Editing';
        contentArea.style.display = "none";
        blockCreator.style.display = "block";
    }
    function disableEditing(){
        const blockCreator = document.getElementById("block_creator");
        const contentArea = document.getElementById("content");
        const toggleButton = document.getElementById("toggle_edit_button");
        toggleButton.innerText = 'Enable Block Editing';
        contentArea.style.display = "block";
        blockCreator.style.display = "none";

        const blockHtml = document.getElementById("block_html");
        blockHtml.value = "";
        blockCreator.removeAttribute('data-nodeid');
    }
    function toggleBlockEditing(){
        const blockCreator = document.getElementById("block_creator");
        const contentArea = document.getElementById("content");
        if (!blocksAreEditable()){
            enableEditing();
        } else {
            disableEditing();
        }
        
    }
    function editBlockTemplate(block){
        const blockHtml = document.getElementById("block_html");
        blockHtml.value = html_beautify(block.innerHTML.trim());
        const blockCreator = document.getElementById("block_creator");
        const nodeId = nodeMap.length;
        nodeMap[nodeId] = block;
        blockCreator.setAttribute('data-nodeid',nodeId);
    }
    function blockClicked(event){
        if (!blocksAreEditable()){
            return;
        }
        editBlockTemplate(this);
    }
    function saveEdit(){
        const blockCreator = document.getElementById("block_creator");
        const blockHtml = document.getElementById("block_html");
        const nodeId = blockCreator.getAttribute('data-nodeid');
        const node = nodeMap[nodeId];
        node.innerHTML = blockHtml.value;
        disableEditing();
        setupDrag();
    }
    function createBlock(){
        enableEditing();
        const blocks = document.getElementById("blocks");
        const newBlock = document.createElement('div');
        newBlock.className = 'block';
        blocks.insertBefore(newBlock,document.getElementById("blocks_end"));
        editBlockTemplate(newBlock);
    }
    function setupDrag(){
        const blocksNode = document.getElementById("blocks");
        const nodeList = Array.from(blocksNode.childNodes);
        nodeList.forEach(function(node){
            if (typeof node === 'object'
                &&node.className=="block"){
                node.addEventListener('dragstart',function(event){drag(event);});
                node.setAttribute("draggable","true");
                node.addEventListener('click',blockClicked);
            }
        });
    }
    window.onload = setupDrag;
</script>
</head>
<body>
<table>
<tr>
    <td style="background:red;">
        <div id="settings">
        </div>
    </td>


    <td>
        <div id="content" onclick="editBlock(event);" ondrop="drop(event)" ondragover="allowDrop(event)"> </div>
        <div id="block_creator">
            <textarea id="block_html" rows="10" cols="70"></textarea>
            <br>
            <button onclick="disableEditing()">Cancel</button>&nbsp;&nbsp;
            <button onclick="saveEdit()">Save Block</button>
        </div>
    </td>

    <td style="background:green;">
        <div id="blocks">
            <div class="block">
                <input type="text" name="[--name--]" placeholder="[--placeholder--]" data-editable="name,placeholder,type">
            </div>
            <div class="block">
                <h1 data-editable="innerText">[--Heading--]</h1>
            </div>
            <div class="block">
                <h2>[--Heading--]</h2>
            </div>
            <div class="block">
                <h3>[--Heading--]</h3>
            </div>

            <span id="blocks_end"></span>
            <br><br>
            <button onclick="createBlock();">Create New Block</button>&nbsp;&nbsp;
            <button id="toggle_edit_button" onclick="toggleBlockEditing();">Enable Block Editing</button>
        </div>
    </td>

</tr>
<tr>
<td></td><td><textarea id="html"></textarea></td><td></td>
</tr>
</table>
</body>
</html>