run.php
<?php
echo str_repeat("\n",9);
if (!empty($_SERVER['REDIRECT_URL'])&&$_SERVER['REDIRECT_URL']!='/'){
include __DIR__.$_SERVER['REDIRECT_URL'];
exit;
}
?>
<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;
}
#template_creator {
display:none;
}
</style>
<script>
class ContentContainer {
constructor(node){
this.newestNodeId = 0;
this.node = node;
const thisArg = this;
node.addEventListener('dragover',function(event){event.preventDefault();});
node.ondrop = function(event){thisArg.dropElement.call(thisArg,event);} ;
console.log(node);
console.log('content container constructed');
}
dropElement(event){
console.log('dropElement__start');
event.preventDefault();
const cursorY = event.clientY;
const elementHtml = event.dataTransfer.getData('html');
const insertable = this.htmlAsNode(elementHtml);
console.log(insertable);
if (event.target.getAttribute("id")=="content"){
this.placeNodeByEvent(insertable,event);
} else {
this.placeNearNode(event,insertable);
}
insertable.removeAttribute('draggable');
console.log('dropElement__endish');
this.updateHtmlOutput();
console.log('dropElement__end');
}
updateHtmlOutput(){
const html = document.getElementById("html");
html.value = html_beautify(document.getElementById("content").innerHTML);
}
getFirstChild(parent){
var insertNode = parent.firstChild;
while (insertNode !== null
&&typeof insertNode !== undefined
&&insertNode.nodeName=="#text"
&&insertNode.nextSibling !== null){
insertNode = insertNode.nextSibling;
}
return insertNode;
}
placeNearNode(event,newNode){
var lastParent = event.target;
const cursorY = event.clientY;
const content = document.getElementById("content");
const existingNode = this.isCursorOnTopHalf(cursorY,lastParent)
? lastParent : lastParent.nextSibling;
this.placeBefore(newNode,existingNode);
}
placeBefore(newNode,existingNode){
const content = document.getElementById("content");
existingNode.parentNode.insertBefore(newNode,existingNode);
newNode.removeAttribute('draggable');
const id = this.getNewNodeId();
wyg.nodeMap[id] = {'settings':settings==null ? '' : settings, 'node':newNode};
}
getNewNodeId(){
if (this.newestNodeId>wyg.nodeMap.length){
this.newestNodeId += 1;
} else {
this.newestNodeId = wyg.nodeMap.length;
}
return this.newestNodeId;
}
htmlAsNode(html){
var div = document.createElement('div');
div.innerHTML = html.trim();
return div.firstChild;
}
placeNodeByEvent(insertable,event){
return this.placeNodeNearXY(
insertable,
event.cursorX,
event.cursorY,
event.target
);
}
placeNodeNearXY(insertable,x,y,targetNode = null){
console.log('placeNodeNearXY__start');
if (targetNode==null)targetNode = document.getElementById("content");
let relatedChild = this.getNearestChild(targetNode,x,y,1);
let goesAbove = this.isCursorOnTopHalf(y,relatedChild);
//let insertable = this.blockAsContentNode(this.htmlAsNode(insertHtml));
console.log(relatedChild);
const existingNode = (relatedChild === undefined ||relatedChild === null || typeof relatedChild ===undefined)
? targetNode.firstChild :
(goesAbove
? relatedChild
: relatedChild.nextSibling
);
this.placeBefore(insertable,existingNode);
console.log('placeNodeNearXY__end');
}
isCursorOnTopHalf(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;
}
getNearestChild(parentNode,x,y,maxDepth=1){
return this.getNearestVerticalChild(parentNode,y,1);
}
nodeLocation(node){
var range = document.createRange();
range.selectNode(node);
var rects = range.getClientRects();
if (rects.length > 0) {
return rects[0]['y'];
}
}
getNearestVerticalChild(parentNode,y){
console.log(' getNearestVerticalChild__start');
var closestChild;
var diff = 0;
var nodeY;
var childNode;
console.log('\t\tparentNode');
console.log(parentNode);
for(childNode of parentNode.childNodes){
// if (typeof childNode !== 'object'
// ||childNode.nodeName=='#text')continue;
nodeY = this.nodeLocation(childNode);
console.log(childNode+':::nodeY_:'+nodeY);
if (diff===0
||diff>Math.abs(y-nodeY)){
diff = Math.abs(y-nodeY);
closestChild = childNode;
}
}
if (closestChild==null){
closestChild = document.createTextNode('');
parentNode.appendChild(closestChild);
}
console.log(' getNearestVerticalChild__end');
return closestChild;
}
}
class DragContainer {
constructor(node){
this.node = node;
//const thisArg = this;
// node.addEventListener('drop', function(event){thisArg.dropElement.call(thisArg,event);} );
console.log('drag container constructed');
}
}
class TemplateEditor {
constructor(template){
this.template = template;
this.templateNode = template.node;
}
display(){
wyg.enableTemplateEditing();
const blockHtml = document.getElementById("html");
blockHtml.value = html_beautify(this.templateNode.outerHTML.trim());
const blockCreator = document.getElementById("template_creator");
const nodeId = wyg.nodeMap.length;
wyg.nodeMap[nodeId] = this.templateNode;
blockCreator.setAttribute('data-nodeid',nodeId);
var thisObj = this;
document.getElementById("template_editor_cancel").onclick =
function(event){
thisObj.cancel.call(thisObj,event);
};
document.getElementById("template_editor_save").onclick =
function(event){
thisObj.save.call(thisObj,event);
};
}
save(){
const blockCreator = document.getElementById("template_creator");
const blockHtml = document.getElementById("html");
const nodeId = blockCreator.getAttribute('data-nodeid');
const settingsHtml = document.getElementById("settings_html");
const node = wyg.nodeMap[nodeId];
node.innerHTML = blockHtml.value;
var url = "/save_element_template.php";
var params = "element_html="+blockHtml.value
+"&settings_html="+settingsHtml.value;
var xhr = new XMLHttpRequest();
xhr.open("POST", url, true);
xhr.onreadystatechange = function(event){
if (this.readyState===4){
console.log(this.response);
}
}
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xhr.send(params);
this.cancel();
//wyg.setupDrag();
wyg.refreshElementTemplates();
}
cancel(){
wyg.disableTemplateEditing();
}
}
class WYSIWYG {
constructor(){
this.nodeMap = [];
}
showTemplateEditor(){
this.enableTemplateEditing();
const blocks = document.getElementById("content");
const newBlock = document.createElement('div');
newBlock.className = 'block';
blocks.appendChild(newBlock);
const template = new Draggable(newBlock);
const templateEditor = new TemplateEditor(template);
templateEditor.display();
}
enableTemplateEditing(){
const templateCreator = document.getElementById("template_creator");
const contentArea = document.getElementById("content");
const toggleButton = document.getElementById("toggle_edit_button");
toggleButton.innerText = 'Disable Template Editing';
contentArea.style.display = "none";
templateCreator.style.display = "block";
}
initiate(){
this.content = new ContentContainer(document.getElementById('content'));
this.drag_container = new DragContainer(document.getElementById("drag_container"));
this.draggables = [];
const children = this.drag_container.node.childNodes;
let draggable;
for (let node of children){
if (typeof node === undefined || node.nodeName=='#text')continue;
draggable = new Draggable(node);
this.draggables.push(draggable);
console.log('draggable pushed');
}
}
}
class Draggable {
constructor(node){
this.node = node;
this.initiate();
}
initiate(){
let nodeClicked = this.nodeClicked;
let dragStarted = this.dragStarted;
let thisArg = this;
this.node.addEventListener('dragstart',function(event){dragStarted.call(thisArg,event)});
this.node.addEventListener('click',function(){nodeClicked.call(thisArg);});
this.node.setAttribute("draggable","true");
}
dragStarted(event){
event.dataTransfer.setData("html", event.target.outerHTML);
}
nodeClicked(){
console.log("a click"+this.node.tagName);
}
}
class Setting {
}
class Inserted {
}
var wyg = new WYSIWYG();
window.onload = function(){wyg.initiate.call(wyg)};
console.error('Template Editor save() is broken. Line 234. Log message on line 331 after window.onload');
</script>
</head>
<body>
<table>
<tr>
<td>
<div id="settings">
</div>
</td>
<td style="border:1px solid black;">
<div id="content"></div>
<div id="template_creator">
<legend for="element_html">Element HTML</legend><br>
<textarea id="block_html" name="element_html" rows="3" cols="70"></textarea>
<br>
<legend for="element_settings_html">Element SettingsHTML</legend><br>
<textarea id="settings_html" name="element_settings_html" rows="3" cols="70"></textarea>
<br>
<button id="template_editor_cancel">Cancel</button>
<button id="template_editor_save">Save Template</button>
</div>
</td>
<td>
<div>
<div>
<div id="drag_container">
<?php
$pdo = new PDO('mysql:host=localhost;dbname=test','reed','pass_word');
$statement = $pdo->prepare("SELECT * FROM wysiwyg_elements ORDER BY element_html ASC");
$statement->execute();
$elements = $statement->fetchAll();
foreach ($elements as $tagName=>$data){
echo $data['element_html'];
}
?>
</div>
</div>
<br><br>
<button onclick="wyg.showTemplateEditor('');">Create New Template</button>
<button id="toggle_edit_button" onclick="wyg.toggleTemplateEditing();">Enable Template Editing</button>
</div>
</td>
</tr>
<tr>
<td></td><td><textarea id="html"></textarea></td><td></td>
</tr>
</table>
</body>
</html>