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>'
+ ' <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>
<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>
<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>