Page.js
Wyg.Page = class {
static initialize(){
this.instance = new Wyg.Page();
}
constructor(){
this.initializeNodes();
this.addEventListeners();
this.initializeParams();
this.showEditorHtml();
this.breadcrumbRefs = [];
}
initializeParams(){
this.editor = new Wyg.Editor(this.codeNode.value,this.valuesNode.value);
this.insertables = new Wyg.Insertables(this.insertables.childNodes);
}
initializeNodes(){
this.codeNode = document.getElementById("templateCode");
this.valuesNode = document.getElementById('valuesCode');
this.outputNode = document.getElementById("output");
this.fields = document.getElementById("fields");
this.controls = document.getElementById("controls");
this.breadcrumbs = document.getElementById("breadcrumbs");
this.editorNode = document.getElementById("editor");
this.editorNode.outerHTML = this.editorNode.outerHTML;
this.editorNode = document.getElementById("editor");
this.insertables = document.getElementById("insertables");
this.insertables.outerHTML = this.insertables.outerHTML;
this.insertables = document.getElementById("insertables");
this.tabBar = document.getElementById("tabBar");
this.templateIdNode = document.getElementById("templateId");
this.styleNode = document.getElementById("styleCode");
this.scriptNode = document.getElementById('scriptCode');
this.templateName = document.getElementById('templateName');
this.siteName = document.getElementById('siteName');
this.groupName = document.getElementById('groupName');
}
showEditorHtml(){
// const node = this.editor.createNode();
this.editorNode.innerHTML = '';
this.editorNode.appendChild(this.editor.node);
this.outputNode.value = this.editor.getOutputCode();
}
addEventListeners(){
this.codeNode.addEventListener('change',this.codeChanged.bind(this));
this.valuesNode.addEventListener('keyup',this.valuesChanged.bind(this));
this.editorNode.addEventListener('click',this.editorClicked.bind(this));
this.editorNode.addEventListener('mouseover',this.editorHovered.bind(this));
this.editorNode.addEventListener('mouseout',this.editorUnhovered.bind(this));
this.editorNode.addEventListener('drop',this.templateDropped.bind(this));
this.editorNode.addEventListener('dragover',event=>event.preventDefault());
this.setupInsertablesListeners();
this.tabBar.addEventListener('click',this.tabSelected.bind(this));
}
panelByTabName(tabName){
const panels = document.querySelectorAll('[class=panel]');
for (const panel of panels){
if (panel.getAttribute('data-tab')==tabName){
return panel;
}
}
}
tabSelected(event){
// console.log(event.target);
if (event.target.classList.contains('tab')
&&!event.target.classList.contains('selected')){
const selectedNodes = document.querySelectorAll('.selected');
for (const selected of selectedNodes){
selected.classList.remove('selected');
}
event.target.classList.add('selected');
const panel = this.panelByTabName(event.target.innerText.trim());
panel.classList.add('selected');
}
}
codeChanged(){
// console.log('code changed');
Wyg.Page.initialize();
}
valuesChanged(){
}
showBreadcrumbs(fromNode){
this.breadcrumbs.innerHTML = '';
const headHtml = ' <h3>'+fromNode.tagName+':</h3> ';
this.breadcrumbs.appendChild(Wyg.Parser.htmlAsNode(headHtml));
let parent = fromNode;
const breadcrumbNodes = [];
while(parent.parentNode!==this.editorNode
&&parent.parentNode!=undefined
&&parent.parentNode!=null
&&parent.parentNode.classList!=null
&&!parent.parentNode.classList.contains('wyg-wrapper')){
parent = parent.parentNode;
breadcrumbNodes.push(parent);
}
let breadcrumbHtml = '';
for (const breadcrumb of breadcrumbNodes.reverse()){
const breadcrumbHtml = '<span class="breadcrumb" onclick="Wyg.Page.instance.goToBreadcrumb(event);">'
+breadcrumb.tagName
+' -></span>';
const span = Wyg.Parser.htmlAsNode(breadcrumbHtml);
this.breadcrumbRefs.push(
{"node":breadcrumb,
"breadcrumb":span}
);
this.breadcrumbs.appendChild(span);
}
}
getTargetNode(node){
// if (node.id=="editor"){
// return node.firstChild.firstChild;
// } else if (node.classList.contains('wyg-wrapper')){
// return node.firstChild;
// } else if (node.nodeName==='#text'){
// node = node.parentNode;
// } else if (node.tagName=='INPUT'){
// node = node.parentNode;
// } else if (node.parentNode.classList.contains('wyg-wrapper')) {
// return node;
// }
// while (node!==undefined
// &&node!==null
// &&!this.editor.isContainer(node)){
// node = node.parentNode;
// }
// return node;
}
getFieldsFromTarget(targetNode){
const refs = [];
let list = [];
let iterables = [targetNode];
while (iterables.length>0){
const iterable = iterables[0];
let ref = this.editor.map.refFrom('node',iterable);
if (ref !== undefined)list.push(...ref.fields);
for (const target of iterable.childNodes){
if (this.editor.isContainer(target))continue;
ref = this.editor.map.refFrom('node',target);
if (ref !==undefined)list.push(...ref.fields);
iterables.push(...(target.childNodes || []));
}
iterables.splice(0,1);
}
iterables = Array.from(new Set(iterables));
return list;
}
editorClicked(event){
event.preventDefault();
event.stopPropagation();
const targetNode = this.getTargetNode(event.target);
this.editNode(targetNode);
}
editNode(targetNode){
this.fields.innerHTML = '';
this.controls.innerHTML = '';
this.breadcrumbs.innerHTML = '';
this.showBreadcrumbs(targetNode);
this.showChildNodes(targetNode);
this.showButtons(targetNode);
const fieldsFromTarget = this.getFieldsFromTarget(targetNode);
this.showEditables(fieldsFromTarget);
this.highlightNode(targetNode);
}
deleteNode(breadcrumb){
const node = this.nodeFromBreadcrumb(breadcrumb);
const newSelected = node.previousSibling || node.nextSibling || node.parentNode;
this.editor.deleteNode(node);
this.codeNode.value = this.editor.templateCode;
this.outputNode.value = this.editor.getOutputCode();
this.editNode(newSelected);
// Wyg.Page.initialize();
}
moveNode(event,direction){
const button = event.target;
const node = this.nodeFromBreadcrumb(button);
const templateNode = this.editor.map.refFrom('node',node).templateNode;
let sibling = undefined;
let templateSibling = undefined;
switch(direction){
case "up":
const parent = node.parentNode;
const templateParent = templateNode.parentNode;
if (parent.classList.contains('wyg-wrapper')||parent.id==='editor'){
console.log(node);
throw "cannot move node up because node is at the root of its container";
}
parent.removeChild(node);
templateParent.removeChild(templateNode);
parent.parentNode.insertBefore(node,parent);
templateParent.parentNode.insertBefore(templateNode,templateParent);
break;
case "down":
sibling = node.nextElementSibling;
if (sibling===undefined||sibling===null||!this.editor.isContainer(sibling)){
console.log(sibling);
throw 'the next sibling node is not a valid container node.';
}
templateSibling = templateNode.nextElementSibling;
node.parentNode.removeChild(node);
templateNode.parentNode.removeChild(templateNode);
sibling.insertBefore(node,sibling.firstChild);
templateSibling.insertBefore(templateNode,templateSibling.firstChild);
break;
case "left":
sibling = node.previousSibling;
templateSibling = templateNode.previousSibling;
if (sibling===null||sibling===undefined){
console.log(node);
throw 'cannot move node left because previous sibling is undefined';
}
sibling.parentNode.removeChild(node);
sibling.parentNode.insertBefore(node,sibling);
templateSibling.parentNode.removeChild(templateNode);
templateSibling.parentNode.insertBefore(templateNode,templateSibling);
break;
case "right":
sibling = node.nextSibling;
templateSibling = templateNode.nextSibling;
if (sibling===null||sibling===undefined){
console.log(node);
throw 'cannot move node right because next sibling is undefined';
}
sibling.parentNode.removeChild(node);
sibling.parentNode.insertBefore(node,sibling.nextSibling);
templateSibling.parentNode.removeChild(templateNode);
templateSibling.parentNode.insertBefore(templateNode,templateSibling.nextSibling);
break;
}
this.codeNode.value = this.editor.templateNode.innerHTML;
this.outputNode.value = this.editor.getOutputCode();
this.editNode(node);
}
saveTemplate(){
console.log('save template');
const templateCode = this.codeNode.value;
const templateId = this.templateIdNode.value;
const templateValues = this.valuesNode.value;
const css = this.styleNode.value;
const js = this.scriptNode.value;
const templateName = this.templateName.value;
const siteName = this.siteName.value;
const groupName = this.groupName.value;
const formData = new FormData();
formData.append('code',templateCode);
formData.append('id',templateId);
formData.append('name',templateName);
formData.append('values',templateValues);
formData.append('css',css);
formData.append('js',js);
formData.append('site',siteName);
formData.append('group',groupName);
const requestArgs = {
'method':'POST',
'body':formData
};
const request = new Request('/wyg/save_template/',requestArgs);
fetch(request)
.then(response=>{
return response.json();
})
.then(json=>{
const id = json.id;
this.templateIdNode.value = id;
console.log('saved successfully (probably)');
console.log(json);
});
}
showButtons(targetNode){
const leftNode = Wyg.Parser.htmlAsNode('<button onclick="Wyg.Page.instance.moveNode.call(Wyg.Page.instance,event,\'left\')">←</button>');
const rightNode = Wyg.Parser.htmlAsNode('<button onclick="Wyg.Page.instance.moveNode.call(Wyg.Page.instance,event,\'right\')">→</button>');
const upNode = Wyg.Parser.htmlAsNode('<button onclick="Wyg.Page.instance.moveNode.call(Wyg.Page.instance,event,\'up\')">↑</button>');
const downNode = Wyg.Parser.htmlAsNode('<button onclick="Wyg.Page.instance.moveNode.call(Wyg.Page.instance,event,\'down\')">↓</button>');
this.breadcrumbRefs.push({'breadcrumb':leftNode,'node':targetNode});
this.breadcrumbRefs.push({'breadcrumb':rightNode,'node':targetNode});
this.breadcrumbRefs.push({'breadcrumb':upNode,'node':targetNode});
this.breadcrumbRefs.push({'breadcrumb':downNode,'node':targetNode});
this.controls.appendChild(leftNode);
this.controls.appendChild(rightNode);
this.controls.appendChild(upNode);
this.controls.appendChild(downNode);
const btn1Html = '<button onclick="Wyg.Page.instance.deleteNode(this);">DELETE THIS NODE</button>';
const btn1 = Wyg.Parser.htmlAsNode(btn1Html);
this.breadcrumbRefs.push({'breadcrumb':btn1,'node':targetNode});
this.controls.appendChild(btn1);
}
showNextSiblingButton(targetNode){
if (targetNode.nextSibling==null){
return;
} else {
const name = targetNode.nextSibling.tagName || targetNode.nextSibling.nodeName;
const nextHtml = '<button>--'+name.toLowerCase()+'--></button>';
const next = Wyg.Parser.htmlAsNode(nextHtml);
this.breadcrumbs.appendChild(next);
next.addEventListener('click',this.nextClicked.bind(this));
this.breadcrumbRefs.push({"breadcrumb":next,"node":targetNode});
}
}
showPrevSiblingButton(targetNode){
if (targetNode.previousSibling==null){
return;
} else {
const name = targetNode.previousSibling.tagName || targetNode.previousSibling.nodeName;
const prevHtml = '<button><--'+name.toLowerCase()+'--</button>';
const prev = Wyg.Parser.htmlAsNode(prevHtml);
prev.addEventListener('click',this.prevClicked.bind(this));
this.breadcrumbs.appendChild(prev);
this.breadcrumbRefs.push({"breadcrumb":prev,"node":targetNode});
}
}
nextClicked(event){
// console.log('next clicked');
// console.log(event.target);
const next = event.target;
const node = this.nodeFromBreadcrumb(next);
const nextNode = node.nextSibling;
this.editNode(nextNode);
}
prevClicked(event){
// console.log('next clicked');
// console.log(event.target);
const prev = event.target;
const node = this.nodeFromBreadcrumb(prev);
const prevNode = node.previousSibling;
this.editNode(prevNode);
}
showChildNodes(targetNode){
let html = '';
this.breadcrumbs.appendChild(Wyg.Parser.htmlAsNode('<br>'));
this.breadcrumbs.appendChild(Wyg.Parser.htmlAsNode('<br>'));
this.showPrevSiblingButton(targetNode);
const select = Wyg.Parser.htmlAsNode('<select onchange="Wyg.Page.instance.goToChild.call(Wyg.Page.instance,event,{\'this\':this,\'target\':this.options[this.selIndex]});"></select>');
this.breadcrumbs.appendChild(select);
let option = Wyg.Parser.htmlAsNode('<option>[Child Nodes]</option>');
select.appendChild(option);
const i=0;
for (const child of targetNode.childNodes){
// i++;
html =
` <option value="${child.tagName || child}">
${child.tagName || child}
</option>\n`;
option = Wyg.Parser.htmlAsNode(html);
select.appendChild(option);
this.breadcrumbRefs.push({"breadcrumb":option,"node":child});
}
this.showNextSiblingButton(targetNode);
}
showEditables(fields){
for (const field of fields){
const html =
`<label>${field.key} \n`
+`<input name="${field.key}" type="text" value="${field.value}" placeholder="${field.defaultValue}"
`+` onkeyup="Wyg.Page.instance.editor.updateValue.call(Wyg.Page.instance.editor,'${field.key}',this.value);">`
+'</label>';
const editNode = Wyg.Parser.htmlAsNode(html)
this.fields.appendChild(editNode);
}
}
highlightNode(node){
const highlightName = 'data-wyg-highlight';
const nodes = document.querySelectorAll(`[${highlightName}="true"]`);
for (const oldNode of nodes){
if (oldNode.hasAttribute(highlightName)){
oldNode.removeAttribute(highlightName);
}
}
if (node.setAttribute==null)return;
node.setAttribute(highlightName,'true');
}
editorHovered(){
}
editorUnhovered(){
}
setupInsertablesListeners(){
this.insertables.addEventListener('click',this.insertablesClicked.bind(this));
const draggables = document.querySelectorAll('.wyg-template');
for (const node of draggables){
node.draggable=true;
node.addEventListener('dragstart',this.templateDragged.bind(this));
}
// console.log(draggables);
}
templateDragged(event){
const node = event.target;
event.dataTransfer.setData('code',node.children[0].value);
// console.log('dragstart');
}
templateDropped(event){
event.preventDefault();
event.stopPropagation();
let target = event.target;
if (target===this.editorNode){
target = target.firstChild;
}
if (target.classList.contains('wyg-wrapper')){
target = target.lastChild;
}
// console.log('dropped');
// console.log(target);
const enCodedString = event.dataTransfer.getData('code');
const codeJson = JSON.parse(enCodedString);
const code = this.codeWithFreshKeys(codeJson.code);
const templateNodes = Wyg.Parser.htmlAsNode(code.trim(),true,'span');
for (const newNode of templateNodes.childNodes){
if (target===null||target===undefined){
this.editor.templateNode.appendChild(newNode);
} else {
const ref = this.editor.map.refFrom('node',target);
const templateNode = ref.templateNode;
templateNode.parentNode.insertBefore(newNode,templateNode);
}
}
if (this.codeNode.value.trim()==''
&&confirm("Edit existing template?\nOtherwise, you are creating a new template.")){
this.templateIdNode.value = codeJson.id;
this.templateName.value = codeJson.name;
this.siteName.value = codeJson.site;
this.groupName.value = codeJson.group;
this.styleNode.value = codeJson.css;
this.scriptNode.value = codeJson.js;
this.valuesNode.value = codeJson.values;
}
this.codeNode.value = this.editor.templateNode.innerHTML;
// console.log(this.editor);
this.editorNode.innerHTML = '';
Wyg.Page.initialize();
}
insertablesClicked(event){
const node = event.target;
// console.log(node);
if (node.tagName=='H2'
||node.tagName=='H3'){
node.parentNode.classList.toggle('selected');
} else if (node.classList.contains('wyg-template')){
const codeHolder = node.children[0];
const code = codeHolder.value;
// console.log('you gotta drag it!');
}
}
goToChild(event){
const select = event.target;
const breadcrumb = select.options[select.selectedIndex];
const node = this.nodeFromBreadcrumb(breadcrumb);
this.editNode(node);
}
goToBreadcrumb(event){
const breadcrumb = event.target;
const node = this.nodeFromBreadcrumb(breadcrumb);
// const fakeEvent = {'target':node,'stopPropagation':function(){},'preventDefault':function(){}};
this.editNode(node);
}
nodeFromBreadcrumb(breadcrumb){
for (const ref of this.breadcrumbRefs){
if (ref.breadcrumb===breadcrumb){
return ref.node;
}
}
}
codeWithFreshKeys(code){
const fields = Wyg.Parser.fields(code);
let newFieldCode = code;
for (const fieldKey in fields){
const field = fields[fieldKey];
let newKey = field.key;
const keyString = field.key;
let keyCount = 1;
while (this.keyExists(newKey)){
newKey = keyString+keyCount;
keyCount++;
}
newFieldCode = newFieldCode.replace('{{'+field.key,'{{'+newKey);
code.replace(field.code,newFieldCode);
}
return newFieldCode;
}
keyExists(key){
const field = this.editor.map.fieldFromKey(key);
if (field===undefined)return false;
else return true;
}
};