|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
(function(){ |
|
|
let cache = {}; |
|
|
let startTag = "{%"; |
|
|
let endTag = "%}"; |
|
|
let re1 = new RegExp(`((^|${endTag})[^\t]*)'`,"g"); |
|
|
let re2 = new RegExp(`\t=(.*?)${endTag}`,"g"); |
|
|
|
|
|
this.tmpl = function tmpl(str, data){ |
|
|
|
|
|
|
|
|
let fn = /^[-a-zA-Z0-9]+$/.test(str) ? |
|
|
cache[str] = cache[str] || |
|
|
tmpl(document.getElementById(str).innerHTML) : |
|
|
|
|
|
|
|
|
|
|
|
new Function("obj", |
|
|
"let p=[],print=function(){p.push.apply(p,arguments);};" + |
|
|
|
|
|
|
|
|
"with(obj){p.push('" + |
|
|
|
|
|
|
|
|
str |
|
|
.replace(/[\r\t\n]/g, " ") |
|
|
.split(startTag).join("\t") |
|
|
.replace(re1, "$1\r") |
|
|
.replace(re2, "',$1,'") |
|
|
.split("\t").join("');") |
|
|
.split(endTag).join("p.push('") |
|
|
.split("\r").join("\\'") |
|
|
+ "');}return p.join('');"); |
|
|
|
|
|
return data ? fn( data ) : fn; |
|
|
}; |
|
|
})(); |
|
|
|
|
|
|
|
|
function buildParams( prefix, obj, add ) { |
|
|
let rbracket = /\[\]$/; |
|
|
|
|
|
if ( Array.isArray( obj ) ) { |
|
|
|
|
|
|
|
|
for(const key in obj) { |
|
|
let v = obj[key]; |
|
|
if ( rbracket.test( prefix ) ) { |
|
|
|
|
|
|
|
|
add( prefix, v ); |
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
|
buildParams( |
|
|
prefix + "[" + ( typeof v === "object" && v != null ? key : "" ) + "]", |
|
|
v, |
|
|
add |
|
|
); |
|
|
} |
|
|
} |
|
|
|
|
|
} else if ( typeof obj === "object" ) { |
|
|
|
|
|
|
|
|
for (const name in obj ) { |
|
|
buildParams( prefix + "[" + name + "]", obj[ name ], add ); |
|
|
} |
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
|
add( prefix, obj ); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
function nestedFormData( a ) { |
|
|
let prefix, |
|
|
s = [], |
|
|
add = function( key, valueOrFunction ) { |
|
|
|
|
|
|
|
|
let value = typeof valueOrFunction === "function" ? |
|
|
valueOrFunction() : |
|
|
valueOrFunction; |
|
|
|
|
|
s[ s.length ] = encodeURIComponent( key ) + "=" + |
|
|
encodeURIComponent( value == null ? "" : value ); |
|
|
}; |
|
|
|
|
|
if ( a == null ) { |
|
|
return ""; |
|
|
} |
|
|
|
|
|
|
|
|
if ( Array.isArray( a ) || ( Object.is( a ) ) ) { |
|
|
|
|
|
|
|
|
for(const key in object) { |
|
|
let v = object[key]; |
|
|
|
|
|
add( key, v ); |
|
|
}; |
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
|
|
|
|
for (const prefix in a ) { |
|
|
buildParams( prefix, a[ prefix ], add ); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
return s.join( "&" ); |
|
|
}; |
|
|
|
|
|
|
|
|
let delay = (function(){ |
|
|
let timer = 0; |
|
|
return function(callback, ms){ |
|
|
clearTimeout (timer); |
|
|
timer = setTimeout(callback, ms); |
|
|
}; |
|
|
})(); |
|
|
|
|
|
function isElement(obj){ |
|
|
return (typeof obj==="object") && |
|
|
(obj.nodeType===1) && (typeof obj.style === "object") && |
|
|
(typeof obj.ownerDocument ==="object"); |
|
|
} |
|
|
|
|
|
function generateElements(html) { |
|
|
const template = document.createElement('template'); |
|
|
template.innerHTML = html.trim(); |
|
|
return template.content.children; |
|
|
} |
|
|
|
|
|
function offset(el) { |
|
|
box = el.getBoundingClientRect(); |
|
|
docElem = document.documentElement; |
|
|
return { |
|
|
top: box.top + window.pageYOffset - docElem.clientTop, |
|
|
left: box.left + window.pageXOffset - docElem.clientLeft |
|
|
}; |
|
|
} |
|
|
|
|
|
if (Vvveb === undefined) var Vvveb = {}; |
|
|
|
|
|
Vvveb.defaultComponent = "_base"; |
|
|
Vvveb.preservePropertySections = true; |
|
|
|
|
|
Vvveb.dragIcon = 'icon'; |
|
|
|
|
|
Vvveb.dragElementStyle = "background:limegreen;width:100%;height:3px;border:1px solid limegreen;box-shadow:0px 0px 2px 1px rgba(0,0,0,0.14);overflow:hidden;"; |
|
|
Vvveb.dragHtml = '<div style="' + Vvveb.dragElementStyle + '"></div>'; |
|
|
|
|
|
Vvveb.baseUrl = document.currentScript?document.currentScript.src.replace(/[^\/]*?\.js$/,''):''; |
|
|
Vvveb.imgBaseUrl = Vvveb.baseUrl; |
|
|
|
|
|
Vvveb.ComponentsGroup = {}; |
|
|
Vvveb.SectionsGroup = {}; |
|
|
Vvveb.BlocksGroup = {}; |
|
|
|
|
|
Vvveb.Components = { |
|
|
|
|
|
_components: {}, |
|
|
|
|
|
_nodesLookup: {}, |
|
|
|
|
|
_attributesLookup: {}, |
|
|
|
|
|
_classesLookup: {}, |
|
|
|
|
|
_classesRegexLookup: {}, |
|
|
|
|
|
componentPropertiesElement: "#right-panel .component-properties", |
|
|
|
|
|
componentPropertiesDefaultSection: "content", |
|
|
|
|
|
get: function(type) { |
|
|
return this._components[type]; |
|
|
}, |
|
|
|
|
|
updateProperty: function(type, key, value) { |
|
|
let properties = this._components[type]["properties"]; |
|
|
for (property in properties) { |
|
|
if (key == properties[property]["key"]) { |
|
|
return this._components[type]["properties"][property] = |
|
|
Object.assign(properties[property], value); |
|
|
} |
|
|
} |
|
|
}, |
|
|
|
|
|
getProperty: function(type, key) { |
|
|
let properties = this._components[type] ? this._components[type]["properties"] : []; |
|
|
for (property in properties) { |
|
|
if (key == properties[property]["key"]) { |
|
|
return properties[property]; |
|
|
} |
|
|
} |
|
|
}, |
|
|
|
|
|
add: function(type, data) { |
|
|
data.type = type; |
|
|
|
|
|
this._components[type] = data; |
|
|
|
|
|
if (data.nodes) { |
|
|
for (let i in data.nodes) { |
|
|
this._nodesLookup[ data.nodes[i] ] = data; |
|
|
} |
|
|
} |
|
|
|
|
|
if (data.attributes) { |
|
|
if (data.attributes.constructor === Array) { |
|
|
for (let i in data.attributes) { |
|
|
this._attributesLookup[ data.attributes[i] ] = data; |
|
|
} |
|
|
} else { |
|
|
for (let i in data.attributes) { |
|
|
if (typeof this._attributesLookup[i] === 'undefined') { |
|
|
this._attributesLookup[i] = {}; |
|
|
} |
|
|
|
|
|
if (typeof this._attributesLookup[i][ data.attributes[i] ] === 'undefined') { |
|
|
this._attributesLookup[i][ data.attributes[i] ] = {}; |
|
|
} |
|
|
|
|
|
this._attributesLookup[i][ data.attributes[i] ] = data; |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
if (data.classes) { |
|
|
for (let i in data.classes) { |
|
|
this._classesLookup[ data.classes[i] ] = data; |
|
|
} |
|
|
} |
|
|
|
|
|
if (data.classesRegex) { |
|
|
for (let i in data.classesRegex) { |
|
|
this._classesRegexLookup[ data.classesRegex[i] ] = data; |
|
|
} |
|
|
} |
|
|
}, |
|
|
|
|
|
extend: function(inheritType, type, data) { |
|
|
|
|
|
let newData = data; |
|
|
|
|
|
if (inheritData = this._components[inheritType]) { |
|
|
newData = {...inheritData, ...data}; |
|
|
newData.properties = (data.properties ? data.properties : []).concat(inheritData.properties ? inheritData.properties : []); |
|
|
} |
|
|
|
|
|
|
|
|
newData.properties.sort(function (a,b) { |
|
|
if (typeof a.sort === "undefined") a.sort = 0; |
|
|
if (typeof b.sort === "undefined") b.sort = 0; |
|
|
|
|
|
if (a.sort < b.sort) |
|
|
return -1; |
|
|
if (a.sort > b.sort) |
|
|
return 1; |
|
|
return 0; |
|
|
}); |
|
|
|
|
|
this.add(type, newData); |
|
|
}, |
|
|
|
|
|
|
|
|
matchNode: function(node) { |
|
|
let component = {}; |
|
|
|
|
|
if (!node || !node.tagName) return false; |
|
|
|
|
|
if (node.attributes && node.attributes.length) { |
|
|
|
|
|
for (let i in node.attributes) { |
|
|
if (node.attributes[i]) { |
|
|
attr = node.attributes[i].name; |
|
|
value = node.attributes[i].value; |
|
|
|
|
|
if (attr in this._attributesLookup) { |
|
|
component = this._attributesLookup[ attr ]; |
|
|
|
|
|
|
|
|
|
|
|
if (typeof component["name"] === "undefined") { |
|
|
if (value in component) { |
|
|
return component[value]; |
|
|
} |
|
|
} else { |
|
|
return component; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
for (let i in node.attributes) { |
|
|
attr = node.attributes[i].name; |
|
|
value = node.attributes[i].value; |
|
|
|
|
|
|
|
|
if (attr == "class") { |
|
|
classes = value.split(" "); |
|
|
|
|
|
for (j in classes) { |
|
|
if (classes[j] in this._classesLookup) |
|
|
return this._classesLookup[ classes[j] ]; |
|
|
} |
|
|
|
|
|
for (regex in this._classesRegexLookup) { |
|
|
regexObj = new RegExp(regex); |
|
|
if (regexObj.exec(value)) { |
|
|
return this._classesRegexLookup[ regex ]; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
tagName = node.tagName.toLowerCase(); |
|
|
if (tagName in this._nodesLookup) return this._nodesLookup[ tagName ]; |
|
|
|
|
|
return false; |
|
|
|
|
|
}, |
|
|
|
|
|
render: function(type, panel = false) { |
|
|
|
|
|
let component = this._components[type]; |
|
|
if (!component) return; |
|
|
|
|
|
if (!panel) { |
|
|
|
|
|
panel = this.componentPropertiesElement; |
|
|
} |
|
|
|
|
|
let defaultSection = this.componentPropertiesDefaultSection; |
|
|
let componentsPanelSections = {}; |
|
|
|
|
|
document.querySelectorAll(panel + " .tab-pane").forEach((el, i) => { |
|
|
let sectionName = el.dataset.section; |
|
|
componentsPanelSections[sectionName] = el; |
|
|
for (const item of el.querySelectorAll( |
|
|
'label:not([data-header="default"]) + input,' + |
|
|
'label:not([data-header="default"]),' + |
|
|
'.section:not([data-section="default"])' |
|
|
)) { |
|
|
item.remove(); |
|
|
} |
|
|
}); |
|
|
|
|
|
let section = componentsPanelSections[defaultSection].querySelector('.section[data-section="default"]'); |
|
|
|
|
|
if (!(Vvveb.preservePropertySections && section)) { |
|
|
let template = tmpl("vvveb-input-sectioninput", {key:"default", header:component.name}); |
|
|
|
|
|
componentsPanelSections[defaultSection].replaceChildren(); |
|
|
componentsPanelSections[defaultSection].append(generateElements(template)[0]); |
|
|
|
|
|
section = componentsPanelSections[defaultSection].querySelector(".section"); |
|
|
} |
|
|
|
|
|
componentsPanelSections[defaultSection].querySelector('[data-header="default"] span').innerHTML = component.name; |
|
|
section.replaceChildren(); |
|
|
|
|
|
if (component.beforeInit) component.beforeInit(Vvveb.Builder.selectedEl); |
|
|
|
|
|
let element; |
|
|
|
|
|
let fn = function(component, property) { |
|
|
if (property.input) { |
|
|
property.input.addEventListener('propertyChange', (event) => { |
|
|
element = selectedElement = Vvveb.Builder.selectedEl; |
|
|
let value = event.detail.value, input = event.detail.input, origEvent = event.detail.origEvent; |
|
|
|
|
|
if (property.child) element = element.querySelector(property.child); |
|
|
if (property.parent) element = element.parent(property.parent); |
|
|
|
|
|
if (property.onChange) { |
|
|
let ret = property.onChange(element, value, input, component, origEvent); |
|
|
|
|
|
if (typeof ret == "object") { |
|
|
element = ret; |
|
|
} else { |
|
|
value = ret; |
|
|
} |
|
|
} |
|
|
if (property.htmlAttr) { |
|
|
oldValue = element.getAttribute(property.htmlAttr); |
|
|
|
|
|
if (property.htmlAttr == "class" && property.validValues) { |
|
|
if (property.validValues) { |
|
|
element.classList.remove(...property.validValues.filter(v => v)); |
|
|
} |
|
|
if (value) { |
|
|
element.classList.add(...value.split(" ")); |
|
|
} |
|
|
} |
|
|
else if (property.htmlAttr == "style") { |
|
|
|
|
|
oldStyle = window.FrameDocument.getElementById("vvvebjs-styles").textContent; |
|
|
element = Vvveb.StyleManager.setStyle(element, property.key, value); |
|
|
} else if (property.htmlAttr == "innerHTML") { |
|
|
element = Vvveb.ContentManager.setHtml(element, value); |
|
|
} else if (property.htmlAttr == "innerText") { |
|
|
element = Vvveb.ContentManager.setText(element, value); |
|
|
} else { |
|
|
|
|
|
if (value) { |
|
|
element.setAttribute(property.htmlAttr, value); |
|
|
} else { |
|
|
element.removeAttribute(property.htmlAttr); |
|
|
} |
|
|
} |
|
|
|
|
|
if (property.htmlAttr == "style") { |
|
|
mutation = { |
|
|
type: 'style', |
|
|
target: element, |
|
|
attributeName: property.htmlAttr, |
|
|
oldValue: oldStyle, |
|
|
newValue: window.FrameDocument.getElementById("vvvebjs-styles").textContent}; |
|
|
|
|
|
Vvveb.Undo.addMutation(mutation); |
|
|
} else { |
|
|
Vvveb.Undo.addMutation({ |
|
|
type: 'attributes', |
|
|
target: element, |
|
|
attributeName: property.htmlAttr, |
|
|
oldValue: oldValue, |
|
|
newValue: element.getAttribute(property.htmlAttr) |
|
|
}); |
|
|
} |
|
|
} |
|
|
|
|
|
if (component.onChange) { |
|
|
element = component.onChange(element, property, value, input); |
|
|
} |
|
|
|
|
|
if (property.child || property.parent) { |
|
|
Vvveb.Builder.selectNode(selectedElement); |
|
|
} else { |
|
|
Vvveb.Builder.selectNode(element); |
|
|
} |
|
|
|
|
|
return element; |
|
|
}); |
|
|
} |
|
|
|
|
|
return property.input; |
|
|
}; |
|
|
|
|
|
let nodeElement = Vvveb.Builder.selectedEl; |
|
|
|
|
|
for (let i in component.properties) { |
|
|
let property = component.properties[i]; |
|
|
let element = nodeElement; |
|
|
|
|
|
if (property.beforeInit) property.beforeInit(element); |
|
|
|
|
|
if (property.child) element = element.querySelector(property.child) ?? element; |
|
|
if (property.parent) element = element.closest(property.parent) ?? element; |
|
|
|
|
|
if (property.data) { |
|
|
property.data["key"] = property.key; |
|
|
} else { |
|
|
property.data = {"key" : property.key}; |
|
|
} |
|
|
|
|
|
if (typeof property.group === 'undefined') property.group = null; |
|
|
|
|
|
property.input = property.inputtype.init(property.data, element); |
|
|
|
|
|
let value; |
|
|
if (property.init) { |
|
|
property.inputtype.setValue(property.init(element)); |
|
|
} else if (property.htmlAttr) { |
|
|
if (property.htmlAttr == "style") { |
|
|
|
|
|
value = Vvveb.StyleManager.getStyle(element, property.key); |
|
|
} else |
|
|
if (property.htmlAttr == "innerHTML") { |
|
|
value = Vvveb.ContentManager.getHtml(element); |
|
|
} else if (property.htmlAttr == "innerText") { |
|
|
value = Vvveb.ContentManager.getText(element); |
|
|
} else { |
|
|
value = element.getAttribute(property.htmlAttr); |
|
|
} |
|
|
|
|
|
|
|
|
if (value && property.htmlAttr == "class" && property.validValues) { |
|
|
let valid = value.split(" ").filter(function(el) { |
|
|
return property.validValues.indexOf(el) != -1 |
|
|
}); |
|
|
|
|
|
if (valid && valid.length) { |
|
|
value = valid[0]; |
|
|
} else { |
|
|
value = ""; |
|
|
} |
|
|
} |
|
|
|
|
|
if (!value && property.defaultValue) { |
|
|
value = property.defaultValue; |
|
|
} |
|
|
|
|
|
property.inputtype.setValue(value); |
|
|
} else { |
|
|
if (!value && property.defaultValue) { |
|
|
value = property.defaultValue; |
|
|
} |
|
|
|
|
|
property.inputtype.setValue(value); |
|
|
} |
|
|
|
|
|
fn(component, property); |
|
|
|
|
|
let propertySection = defaultSection; |
|
|
if (property.section) { |
|
|
propertySection = property.section; |
|
|
} |
|
|
|
|
|
if (property.inputtype == SectionInput) { |
|
|
section = componentsPanelSections[propertySection].querySelector('.section[data-section="' + property.key + '"]'); |
|
|
|
|
|
if (Vvveb.preservePropertySections && section) { |
|
|
section.replaceChildren(); |
|
|
} else { |
|
|
componentsPanelSections[propertySection].append(property.input); |
|
|
section = componentsPanelSections[propertySection].querySelector('.section[data-section="' + property.key + '"]'); |
|
|
} |
|
|
} |
|
|
else { |
|
|
let row = generateElements(tmpl('vvveb-property', property))[0]; |
|
|
row.querySelector('.input').append(property.input); |
|
|
section.append(row); |
|
|
} |
|
|
|
|
|
if (property.inputtype.afterInit) { |
|
|
property.inputtype.afterInit(property.input); |
|
|
} |
|
|
|
|
|
if (property.afterInit) { |
|
|
property.afterInit(element, property.input); |
|
|
} |
|
|
} |
|
|
|
|
|
if (component.init) component.init(nodeElement); |
|
|
} |
|
|
}; |
|
|
|
|
|
|
|
|
Vvveb.Blocks = { |
|
|
|
|
|
_blocks: {}, |
|
|
|
|
|
get: function(type) { |
|
|
return this._blocks[type]; |
|
|
}, |
|
|
|
|
|
add: function(type, data) { |
|
|
data.type = type; |
|
|
this._blocks[type] = data; |
|
|
}, |
|
|
}; |
|
|
|
|
|
Vvveb.Sections = { |
|
|
|
|
|
_sections: {}, |
|
|
|
|
|
get: function(type) { |
|
|
return this._sections[type]; |
|
|
}, |
|
|
|
|
|
add: function(type, data) { |
|
|
data.type = type; |
|
|
this._sections[type] = data; |
|
|
}, |
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
Vvveb.WysiwygEditor = { |
|
|
|
|
|
isActive: false, |
|
|
oldValue: '', |
|
|
doc:false, |
|
|
|
|
|
|
|
|
editorSetStyle: function (tag, style = {}, toggle = false) { |
|
|
let iframeWindow = Vvveb.Builder.iframe.contentWindow; |
|
|
let selection = iframeWindow.getSelection(); |
|
|
let element = this.element; |
|
|
let range; |
|
|
|
|
|
if (!tag) { |
|
|
tag = "span"; |
|
|
} |
|
|
|
|
|
if (selection.rangeCount > 0) { |
|
|
|
|
|
if ((selection.baseNode && selection.baseNode.nextSibling == null && selection.baseNode.previousSibling == null |
|
|
&& selection.anchorOffset == 0 && selection.focusOffset == selection.baseNode.length) |
|
|
|| (selection.anchorOffset == selection.focusOffset)) { |
|
|
|
|
|
element = selection.baseNode.parentNode; |
|
|
|
|
|
} else { |
|
|
element = document.createElement(tag); |
|
|
range = selection.getRangeAt(0); |
|
|
range.surroundContents(element); |
|
|
range.selectNodeContents(element.childNodes[0], 0); |
|
|
} |
|
|
} |
|
|
|
|
|
if (element && style) { |
|
|
for (name in style) { |
|
|
|
|
|
if ( !style[name] || |
|
|
(toggle && element.style.getPropertyValue(name))) { |
|
|
|
|
|
element.style.removeProperty(name); |
|
|
|
|
|
} else { |
|
|
element.style.setProperty(name, style[name]); |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (element.tagName == "SPAN" && element.style.length == 0 && element.attributes.length <= 1) { |
|
|
let textNode = iframeWindow.document.createTextNode(element.innerText); |
|
|
element.replaceWith(textNode); |
|
|
element = textNode; |
|
|
|
|
|
range = iframeWindow.document.createRange(); |
|
|
range.selectNodeContents(element); |
|
|
selection.removeAllRanges(); |
|
|
selection.addRange(range); |
|
|
} |
|
|
|
|
|
|
|
|
if (tag == "a") { |
|
|
Vvveb.Builder.selectNode(element); |
|
|
Vvveb.Builder.loadNodeComponent(element); |
|
|
} |
|
|
return element; |
|
|
}, |
|
|
|
|
|
init: function(doc) { |
|
|
this.doc = doc; |
|
|
let self = this; |
|
|
|
|
|
document.getElementById("bold-btn").addEventListener("click", function (e) { |
|
|
|
|
|
|
|
|
self.editorSetStyle(false, {"font-weight" : "bold"}, true); |
|
|
e.preventDefault(); |
|
|
return false; |
|
|
}); |
|
|
|
|
|
document.getElementById("italic-btn").addEventListener("click", function (e) { |
|
|
|
|
|
|
|
|
self.editorSetStyle(false, {"font-style" : "italic"}, true); |
|
|
e.preventDefault(); |
|
|
return false; |
|
|
}); |
|
|
|
|
|
document.getElementById("underline-btn").addEventListener("click", function (e) { |
|
|
|
|
|
|
|
|
self.editorSetStyle(false, {"text-decoration" : "underline"}, true); |
|
|
e.preventDefault(); |
|
|
return false; |
|
|
}); |
|
|
|
|
|
document.getElementById("strike-btn").addEventListener("click", function (e) { |
|
|
|
|
|
|
|
|
self.editorSetStyle(false, {"text-decoration" : "line-through"}, true); |
|
|
e.preventDefault(); |
|
|
return false; |
|
|
}); |
|
|
|
|
|
document.getElementById("link-btn").addEventListener("click", function (e) { |
|
|
|
|
|
self.editorSetStyle("a"); |
|
|
e.preventDefault(); |
|
|
return false; |
|
|
}); |
|
|
|
|
|
document.getElementById("fore-color").addEventListener("change", function (e) { |
|
|
|
|
|
self.editorSetStyle(false, {"color" : this.value}); |
|
|
e.preventDefault(); |
|
|
return false; |
|
|
}); |
|
|
|
|
|
|
|
|
document.getElementById("back-color").addEventListener("change", function (e) { |
|
|
|
|
|
self.editorSetStyle(false, {"background-color" : this.value}); |
|
|
e.preventDefault(); |
|
|
return false; |
|
|
}); |
|
|
|
|
|
document.getElementById("font-size").addEventListener("change", function (e) { |
|
|
|
|
|
self.editorSetStyle(false, {"font-size" : this.value}); |
|
|
e.preventDefault(); |
|
|
return false; |
|
|
}); |
|
|
|
|
|
let sizes = "<option value=''> - Font size - </option>"; |
|
|
for (i = 1;i <= 128; i++) { |
|
|
sizes += "<option value='"+ i +"px'>"+ i +"</option>"; |
|
|
} |
|
|
document.getElementById("font-size").innerHTML = sizes; |
|
|
|
|
|
document.getElementById("font-family").addEventListener("change", function (e) { |
|
|
let option = this.options[this.selectedIndex]; |
|
|
let element = self.editorSetStyle(false, {"font-family" : this.value}); |
|
|
Vvveb.FontsManager.addFont(option.dataset.provider, this.value, element); |
|
|
|
|
|
e.preventDefault(); |
|
|
return false; |
|
|
}); |
|
|
|
|
|
document.getElementById("justify-btn").addEventListener("click", function (e) { |
|
|
|
|
|
|
|
|
|
|
|
self.editorSetStyle(false, {"text-align" : e.srcElement.dataset.value}); |
|
|
e.preventDefault(); |
|
|
return false; |
|
|
}); |
|
|
|
|
|
doc.addEventListener('keydown', event => { |
|
|
if (event.key === 'Enter') { |
|
|
let target = event.target.closest("[contenteditable]"); |
|
|
if (target) { |
|
|
doc.execCommand('insertLineBreak'); |
|
|
event.preventDefault(); |
|
|
} |
|
|
} |
|
|
}) |
|
|
}, |
|
|
|
|
|
undo: function(element) { |
|
|
this.doc.execCommand('undo',false,null); |
|
|
}, |
|
|
|
|
|
redo: function(element) { |
|
|
this.doc.execCommand('redo',false,null); |
|
|
}, |
|
|
|
|
|
edit: function(element) { |
|
|
element.setAttribute("contenteditable", true); |
|
|
element.setAttribute("spellcheckker", false); |
|
|
document.getElementById("wysiwyg-editor").style.display = "block"; |
|
|
|
|
|
this.element = element; |
|
|
this.isActive = true; |
|
|
this.oldValue = element.innerHTML; |
|
|
|
|
|
document.getElementById("font-family").value = Vvveb.StyleManager.getStyle(element,'font-family'); |
|
|
document.getElementById("fore-color").value = Vvveb.StyleManager.getStyle(element,'color'); |
|
|
document.getElementById("back-color").value = Vvveb.StyleManager.getStyle(element,'background-color'); |
|
|
element.focus(); |
|
|
}, |
|
|
|
|
|
destroy: function(element) { |
|
|
element.removeAttribute("contenteditable"); |
|
|
element.removeAttribute("spellcheckker"); |
|
|
|
|
|
document.getElementById("wysiwyg-editor").style.display = "none"; |
|
|
this.isActive = false; |
|
|
|
|
|
|
|
|
node = this.element; |
|
|
Vvveb.Undo.addMutation({type:'characterData', |
|
|
target: node, |
|
|
oldValue: this.oldValue, |
|
|
newValue: node.innerHTML}); |
|
|
} |
|
|
} |
|
|
|
|
|
Vvveb.Builder = { |
|
|
|
|
|
component : {}, |
|
|
dragMoveMutation : false, |
|
|
isPreview : false, |
|
|
runJsOnSetHtml : false, |
|
|
designerMode : false, |
|
|
highlightEnabled : false, |
|
|
selectPadding: 0, |
|
|
leftPanelWidth: 275, |
|
|
ignoreClasses: ["clearfix", "masonry", "has-shadow"], |
|
|
|
|
|
init: function(url, callback) { |
|
|
|
|
|
let self = this; |
|
|
|
|
|
self.loadControlGroups(); |
|
|
self.loadBlockGroups(); |
|
|
self.loadSectionGroups(); |
|
|
|
|
|
self.selectedEl = null; |
|
|
self.highlightEl = null; |
|
|
self.initCallback = callback; |
|
|
|
|
|
self.documentFrame = document.querySelector("#iframe-wrapper > iframe"); |
|
|
self.canvas = document.getElementById("canvas"); |
|
|
|
|
|
self._loadIframe(url + (url.indexOf('?') > -1 ? '&r=':'?r=') + Math.random()); |
|
|
|
|
|
self._initDragdrop(); |
|
|
|
|
|
self._initBox(); |
|
|
|
|
|
self.dragElement = null; |
|
|
|
|
|
self.highlightEnabled = true; |
|
|
|
|
|
self.leftPanelWidth = document.getElementById("left-panel").clientWidth; |
|
|
}, |
|
|
|
|
|
|
|
|
loadControlGroups : function() { |
|
|
|
|
|
let componentsList = document.querySelectorAll(".components-list"); |
|
|
let item = {}, component = {}; |
|
|
let count = 0; |
|
|
|
|
|
componentsList.forEach(function (list, i) { |
|
|
let type = list.dataset.type; |
|
|
list.replaceChildren(); |
|
|
count ++; |
|
|
|
|
|
for (group in Vvveb.ComponentsGroup) { |
|
|
|
|
|
list.append(generateElements( |
|
|
`<li class="header" data-section="${group}" data-search=""> |
|
|
<label class="header" for="${type}_comphead_${group}${count}"> |
|
|
${group}<div class="header-arrow"></div> |
|
|
</label> |
|
|
<input class="header_check" type="checkbox" checked="true" id="${type}_comphead_${group}${count}"> |
|
|
<ol></ol> |
|
|
</li>`)[0]); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let componentsSubList = list.querySelector('li[data-section="' + group + '"] ol'); |
|
|
|
|
|
components = Vvveb.ComponentsGroup[ group ]; |
|
|
|
|
|
for (i in components) { |
|
|
const componentType = components[i]; |
|
|
component = Vvveb.Components.get(componentType); |
|
|
|
|
|
if (component) { |
|
|
item = generateElements(`<li data-section="${group}" data-drag-type="component" data-type="${componentType}" data-search="${component.name.toLowerCase()}"> |
|
|
<span>${component.name}</span> |
|
|
</li>`)[0]; |
|
|
|
|
|
if (component.image) { |
|
|
|
|
|
item.style.backgroundImage = "url(" + Vvveb.imgBaseUrl + component.image + ")"; |
|
|
item.style.backgroundRepeat = "no-repeat"; |
|
|
} |
|
|
|
|
|
componentsSubList.append(item); |
|
|
} |
|
|
} |
|
|
} |
|
|
}); |
|
|
}, |
|
|
|
|
|
loadSectionGroups : function() { |
|
|
|
|
|
let sectionsList = document.querySelectorAll(".sections-list"); |
|
|
let item = {}; |
|
|
|
|
|
sectionsList.forEach(function (list, i) { |
|
|
let type = list.dataset.type; |
|
|
list.replaceChildren(); |
|
|
|
|
|
for (group in Vvveb.SectionsGroup) { |
|
|
list.append(generateElements( |
|
|
`<li class="header" data-section="${group}" data-search=""> |
|
|
<label class="header" for="${type}_sectionhead_${group}"> |
|
|
${group}<div class="header-arrow"></div> |
|
|
</label> |
|
|
<input class="header_check" type="checkbox" checked="true" id="${type}_sectionhead_${group}"> |
|
|
<ol></ol> |
|
|
</li>`)[0]); |
|
|
|
|
|
let sectionsSubList = list.querySelector('li[data-section="' + group + '"] ol'); |
|
|
let sections = Vvveb.SectionsGroup[ group ]; |
|
|
|
|
|
for (i in sections) { |
|
|
const sectionType = sections[i]; |
|
|
const section = Vvveb.Sections.get(sectionType); |
|
|
|
|
|
if (section) { |
|
|
item = generateElements(`<li data-section="${group}" data-drag-type="section" data-type="${sectionType}" data-search="${section.name.toLowerCase()}"> |
|
|
<span class="name">${section.name}</span> |
|
|
<div class="add-section-btn" title="Add section"><i class="la la-plus"></i></div> |
|
|
<img class="preview" src="" loading="lazy"> |
|
|
</li>`)[0]; |
|
|
|
|
|
if (section.image) { |
|
|
|
|
|
let image = ((section.image.indexOf('/') == -1) ? Vvveb.imgBaseUrl:'') + section.image; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
item.querySelector("img").setAttribute("src", image); |
|
|
} |
|
|
|
|
|
sectionsSubList.append(item) |
|
|
} |
|
|
} |
|
|
} |
|
|
}); |
|
|
}, |
|
|
|
|
|
loadBlockGroups : function() { |
|
|
|
|
|
let blocksList = document.querySelectorAll(".blocks-list"); |
|
|
let item = {}; |
|
|
|
|
|
blocksList.forEach(function (list, i) { |
|
|
let type = list.dataset.type; |
|
|
list.replaceChildren(); |
|
|
|
|
|
for (group in Vvveb.BlocksGroup) { |
|
|
list.append(generateElements( |
|
|
`<li class="header" data-section="${group}" data-search=""> |
|
|
<label class="header" for="${type}_blockhead_${group}"> |
|
|
${group}<div class="header-arrow"></div> |
|
|
</label> |
|
|
<input class="header_check" type="checkbox" checked="true" id="${type}_blockhead_${group}"> |
|
|
<ol></ol> |
|
|
</li>`)[0]); |
|
|
|
|
|
let blocksSubList = list.querySelector('li[data-section="' + group + '"] ol'); |
|
|
blocks = Vvveb.BlocksGroup[ group ]; |
|
|
|
|
|
for (i in blocks) { |
|
|
const blockType = blocks[i]; |
|
|
const block = Vvveb.Blocks.get(blockType); |
|
|
|
|
|
if (block) { |
|
|
item = generateElements(`<li data-section="${group}" data-drag-type="block" data-type="${blockType}" data-search="${block.name.toLowerCase()}"> |
|
|
<span class="name">${block.name}</span> |
|
|
<img class="preview" src="" loading="lazy"> |
|
|
</li>`)[0]; |
|
|
|
|
|
if (block.image) { |
|
|
|
|
|
let image = ((block.image.indexOf('/') == -1) ? Vvveb.imgBaseUrl:'') + block.image; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
item.querySelector("img").setAttribute("src", image); |
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
blocksSubList.append(item); |
|
|
} |
|
|
} |
|
|
} |
|
|
}); |
|
|
}, |
|
|
|
|
|
loadUrl : function(url, callback) { |
|
|
let self = this; |
|
|
document.getElementById("select-box").style.display = "none"; |
|
|
|
|
|
self.initCallback = callback; |
|
|
if (Vvveb.Builder.iframe.src != url) Vvveb.Builder.iframe.src = url; |
|
|
}, |
|
|
|
|
|
|
|
|
_loadIframe : function(url) { |
|
|
|
|
|
let self = this; |
|
|
self.iframe = this.documentFrame; |
|
|
self.iframe.src = url; |
|
|
|
|
|
return this.documentFrame.addEventListener("load", function() { |
|
|
window.FrameWindow = self.iframe.contentWindow; |
|
|
window.FrameDocument = self.iframe.contentWindow.document; |
|
|
let addSectionBox = document.getElementById("add-section-box"); |
|
|
let highlightBox = document.getElementById("highlight-box"); |
|
|
let SelectBox = document.getElementById("select-box"); |
|
|
|
|
|
highlightBox.style.display = "none"; |
|
|
|
|
|
|
|
|
window.FrameWindow.addEventListener("beforeunload", function(event) { |
|
|
if (Vvveb.Undo.undoIndex >= 0) { |
|
|
let dialogText = "You have unsaved changes"; |
|
|
event.returnValue = dialogText; |
|
|
return dialogText; |
|
|
} |
|
|
}); |
|
|
|
|
|
window.FrameWindow.addEventListener("unload", function(event) { |
|
|
document.querySelector(".loading-message").classList.add("active"); |
|
|
Vvveb.Undo.reset(); |
|
|
}); |
|
|
|
|
|
|
|
|
window.FrameDocument.addEventListener("click", function(event) { |
|
|
if (Vvveb.WysiwygEditor.isActive && event.target.closest("a")) { |
|
|
event.preventDefault(); |
|
|
return false; |
|
|
} |
|
|
}); |
|
|
|
|
|
selectBoxPosition = function(event) { |
|
|
let pos; |
|
|
let target = self.selectedEl; |
|
|
|
|
|
highlightBox.style.display = "none"; |
|
|
|
|
|
if (target) { |
|
|
pos = offset(target); |
|
|
|
|
|
SelectBox.style.top = (pos.top - (self.frameDoc.scrollTop ?? 0) - self.selectPadding) + "px"; |
|
|
SelectBox.style.left = (pos.left - (self.frameDoc.scrollLeft ?? 0) - self.selectPadding) + "px"; |
|
|
|
|
|
SelectBox.style.width = ((target.offsetWidth ?? target.clientWidth) + self.selectPadding * 2) + "px"; |
|
|
SelectBox.style.height = ((target.offsetHeight ?? target.clientHeight) + self.selectPadding * 2) + "px"; |
|
|
} |
|
|
} |
|
|
|
|
|
window.FrameWindow.addEventListener("scroll", selectBoxPosition); |
|
|
window.FrameWindow.addEventListener("resize", selectBoxPosition); |
|
|
|
|
|
Vvveb.WysiwygEditor.init(window.FrameDocument); |
|
|
Vvveb.StyleManager.init(window.FrameDocument); |
|
|
Vvveb.ColorPaletteManager.init(window.FrameDocument); |
|
|
|
|
|
if (self.initCallback) self.initCallback(); |
|
|
|
|
|
return self._frameLoaded(); |
|
|
}); |
|
|
|
|
|
}, |
|
|
|
|
|
_frameLoaded : function() { |
|
|
|
|
|
let self = Vvveb.Builder; |
|
|
|
|
|
self.frameDoc = window.FrameDocument; |
|
|
self.frameHtml = window.FrameDocument.querySelector("html"); |
|
|
self.frameBody = window.FrameDocument.querySelector("body"); |
|
|
self.frameHead = window.FrameDocument.querySelector("head"); |
|
|
|
|
|
|
|
|
self.frameHead.append(generateElements('<link data-vvveb-helpers href="' + Vvveb.baseUrl + '../../css/vvvebjs-editor-helpers.css" rel="stylesheet">')[0]); |
|
|
|
|
|
self._initHighlight(); |
|
|
|
|
|
window.dispatchEvent(new CustomEvent("vvveb.iframe.loaded", {detail: self.frameDoc})); |
|
|
|
|
|
document.querySelector(".loading-message").classList.remove("active"); |
|
|
|
|
|
|
|
|
let setSaveButtonState = function (e) { |
|
|
if (Vvveb.Undo.hasChanges()){ |
|
|
document.querySelectorAll("#top-panel .save-btn").forEach(e => e.removeAttribute("disabled")); |
|
|
} else { |
|
|
document.querySelectorAll("#top-panel .save-btn").forEach(e => e.setAttribute("disabled", "true")); |
|
|
} |
|
|
}; |
|
|
|
|
|
Vvveb.Builder.frameBody.addEventListener("vvveb.undo.add", setSaveButtonState); |
|
|
Vvveb.Builder.frameBody.addEventListener("vvveb.undo.restore", setSaveButtonState); |
|
|
}, |
|
|
|
|
|
_getElementType: function(el) { |
|
|
|
|
|
|
|
|
let componentName = ''; |
|
|
let componentAttribute = ''; |
|
|
|
|
|
if (el.attributes) { |
|
|
for (let j = 0; j < el.attributes.length; j++){ |
|
|
let nodeName = el.attributes[j].nodeName; |
|
|
|
|
|
if (nodeName.indexOf('data-component') > -1) { |
|
|
componentName = nodeName.replace('data-component-', ''); |
|
|
return [componentName, "component"]; |
|
|
} |
|
|
|
|
|
if (nodeName.indexOf('data-v-component-') > -1) { |
|
|
componentName = nodeName.replace('data-v-component-', ''); |
|
|
return [componentName,"component"]; |
|
|
} |
|
|
|
|
|
if (nodeName.indexOf('data-v-') > -1) { |
|
|
componentAttribute = (componentAttribute ? componentAttribute + " - " : "") + |
|
|
nodeName.replace('data-v-', '') + " "; |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
if (componentAttribute != '') return [componentAttribute, "attribute"]; |
|
|
|
|
|
if (el.id) { |
|
|
componentName = "#" + el.id; |
|
|
} else { |
|
|
componentName = (el.className && (typeof el.className == "string")) ? "." + el.className.split(" ")[0] : ""; |
|
|
} |
|
|
|
|
|
return [componentName, el.tagName]; |
|
|
}, |
|
|
|
|
|
loadNodeComponent: function(node) { |
|
|
const data = Vvveb.Components.matchNode(node); |
|
|
let component; |
|
|
|
|
|
if (data) |
|
|
component = data.type; |
|
|
else |
|
|
component = Vvveb.defaultComponent; |
|
|
|
|
|
Vvveb.component = Vvveb.Components.get(component); |
|
|
Vvveb.Components.render(component); |
|
|
this.selectedComponent = component; |
|
|
|
|
|
}, |
|
|
|
|
|
reloadComponent: function() { |
|
|
Vvveb.Components.render(this.selectedComponent); |
|
|
}, |
|
|
|
|
|
moveNodeUp: function(node) { |
|
|
if (!node) { |
|
|
node = Vvveb.Builder.selectedEl; |
|
|
} |
|
|
|
|
|
const oldParent = node.parentNode; |
|
|
const oldNextSibling = node.nextSibling; |
|
|
const next = node.previousElementSibling; |
|
|
|
|
|
if (next) { |
|
|
next.before(node); |
|
|
} else { |
|
|
node.parentNode.before(node); |
|
|
} |
|
|
|
|
|
Vvveb.Builder.selectNode(node); |
|
|
|
|
|
const newParent = node.parentNode; |
|
|
const newNextSibling = node.nextSibling; |
|
|
|
|
|
Vvveb.Undo.addMutation({type: 'move', |
|
|
target: node, |
|
|
oldParent: oldParent, |
|
|
newParent: newParent, |
|
|
oldNextSibling: oldNextSibling, |
|
|
newNextSibling: newNextSibling}); |
|
|
|
|
|
}, |
|
|
|
|
|
moveNodeDown: function(node) { |
|
|
if (!node) { |
|
|
node = Vvveb.Builder.selectedEl; |
|
|
} |
|
|
|
|
|
const oldParent = node.parentNode; |
|
|
const oldNextSibling = node.nextSibling; |
|
|
const next = node.nextElementSibling; |
|
|
|
|
|
if (next) { |
|
|
next.after(node); |
|
|
} else { |
|
|
node.parentNode.after(node); |
|
|
} |
|
|
|
|
|
Vvveb.Builder.selectNode(node); |
|
|
|
|
|
const newParent = node.parentNode; |
|
|
const newNextSibling = node.nextSibling; |
|
|
|
|
|
Vvveb.Undo.addMutation({type: 'move', |
|
|
target: node, |
|
|
oldParent: oldParent, |
|
|
newParent: newParent, |
|
|
oldNextSibling: oldNextSibling, |
|
|
newNextSibling: newNextSibling}); |
|
|
}, |
|
|
|
|
|
cloneNode: function(node) { |
|
|
if (!node) { |
|
|
node = Vvveb.Builder.selectedEl; |
|
|
} |
|
|
|
|
|
const clone = node.cloneNode(true); |
|
|
node.after(clone); |
|
|
node.click(); |
|
|
|
|
|
Vvveb.Undo.addMutation({type: 'childList', |
|
|
target: node.parentNode, |
|
|
addedNodes: [clone], |
|
|
nextSibling: node.nextSibling}); |
|
|
|
|
|
}, |
|
|
|
|
|
|
|
|
selectNode: function(node) { |
|
|
let SelectBox = document.getElementById("select-box"); |
|
|
|
|
|
if (!node) { |
|
|
SelectBox.style.display = "none"; |
|
|
return; |
|
|
} |
|
|
|
|
|
let self = this; |
|
|
let SelectActions = document.getElementById("select-actions"); |
|
|
let AddSectionBtn = document.getElementById("add-section-btn"); |
|
|
let elementType = this._getElementType(node); |
|
|
|
|
|
if (self.texteditEl && (self.selectedEl != node)) { |
|
|
Vvveb.WysiwygEditor.destroy(self.texteditEl); |
|
|
self.selectPadding = 0; |
|
|
SelectBox.classList.remove("text-edit"); |
|
|
SelectActions.style.display = ""; |
|
|
self.texteditEl = null; |
|
|
} |
|
|
|
|
|
if (elementType[1] == "BODY") { |
|
|
SelectActions.style.display = "none"; |
|
|
AddSectionBtn.style.display = "none"; |
|
|
} else { |
|
|
SelectActions.style.display = ""; |
|
|
AddSectionBtn.style.display = ""; |
|
|
} |
|
|
|
|
|
let target = node; |
|
|
self.selectedEl = target; |
|
|
|
|
|
try { |
|
|
let pos = offset(target); |
|
|
let top = (pos.top - (self.frameDoc.scrollTop ?? 0) - self.selectPadding); |
|
|
|
|
|
SelectBox.style.top = top + "px"; |
|
|
SelectBox.style.left = (pos.left - (self.frameDoc.scrollLeft ?? 0) - self.selectPadding) + "px"; |
|
|
SelectBox.style.width = ((target.offsetWidth ?? target.clientWidth) + self.selectPadding * 2) + "px"; |
|
|
SelectBox.style.height = ((target.offsetHeight ?? target.clientHeight) + self.selectPadding * 2) + "px"; |
|
|
SelectBox.style.display = "block"; |
|
|
|
|
|
|
|
|
if (top < 30) { |
|
|
SelectActions.style.top = "unset"; |
|
|
SelectActions.style.bottom = "-25px"; |
|
|
} else { |
|
|
SelectActions.style.top = ""; |
|
|
SelectActions.style.bottom = ""; |
|
|
} |
|
|
|
|
|
Vvveb.Breadcrumb.loadBreadcrumb(target); |
|
|
|
|
|
} catch(err) { |
|
|
console.log(err); |
|
|
return false; |
|
|
} |
|
|
|
|
|
document.querySelector("#highlight-name .type").innerHTML = elementType[0]; |
|
|
document.querySelector("#highlight-name .name").innerHTML = elementType[1]; |
|
|
|
|
|
}, |
|
|
|
|
|
|
|
|
_initHighlight: function() { |
|
|
|
|
|
let self = Vvveb.Builder; |
|
|
|
|
|
let highlightMove = function(event) { |
|
|
if (self.highlightEnabled == true && event.target && isElement(event.target)) { |
|
|
|
|
|
self.highlightEl = target = event.target; |
|
|
let pos = offset(target); |
|
|
let height = target.offsetHeight; |
|
|
let halfHeight = Math.max(height / 2, 5); |
|
|
let width = target.offsetWidth; |
|
|
let halfWidth = Math.max(width / 2, 5); |
|
|
let prepend = true; |
|
|
|
|
|
let x = event.x; |
|
|
let y = event.y; |
|
|
|
|
|
if (self.isResize) { |
|
|
if (!self.initialPosition) { |
|
|
self.initialPosition = {x,y}; |
|
|
} |
|
|
|
|
|
let deltaX = x - self.initialPosition.x; |
|
|
let deltaY = y - self.initialPosition.y; |
|
|
|
|
|
pos = offset(self.selectedEl); |
|
|
|
|
|
width = self.initialSize.width; |
|
|
height = self.initialSize.height; |
|
|
|
|
|
switch (self.resizeHandler) { |
|
|
|
|
|
case "top-left": |
|
|
height -= deltaY; |
|
|
width -= deltaX; |
|
|
break; |
|
|
|
|
|
case "top-center": |
|
|
height -= deltaY; |
|
|
break; |
|
|
|
|
|
case "top-right": |
|
|
height -= deltaY; |
|
|
width += deltaX; |
|
|
break; |
|
|
|
|
|
|
|
|
case "center-left": |
|
|
width -= deltaX; |
|
|
break; |
|
|
|
|
|
case "center-right": |
|
|
width += deltaX; |
|
|
break; |
|
|
|
|
|
|
|
|
case "bottom-left": |
|
|
width -= deltaX; |
|
|
height += deltaY; |
|
|
break; |
|
|
|
|
|
case "bottom-center": |
|
|
height += deltaY; |
|
|
break; |
|
|
|
|
|
case "bottom-right": |
|
|
width += deltaX; |
|
|
height += deltaY; |
|
|
break; |
|
|
} |
|
|
|
|
|
if (self.resizeMode == "css") { |
|
|
self.selectedEl.style.width = width + "px"; |
|
|
self.selectedEl.style.height = height + "px"; |
|
|
} else { |
|
|
self.selectedEl.setAttribute("width", width); |
|
|
self.selectedEl.setAttribute("height", height); |
|
|
} |
|
|
|
|
|
let SelectBox = document.getElementById("select-box"); |
|
|
SelectBox.style.top = pos.top - (self.frameDoc.scrollTop ?? 0) + "px"; |
|
|
SelectBox.style.left = pos.left - (self.frameDoc.scrollLeft ?? 0) + "px"; |
|
|
SelectBox.style.width = width + "px"; |
|
|
SelectBox.style.height = self.selectedEl.offsetHeight + "px"; |
|
|
SelectBox.style.display = "block"; |
|
|
|
|
|
} else if (self.isDragging) { |
|
|
let noChildren = { |
|
|
input: true, |
|
|
textarea: true, |
|
|
img: true, |
|
|
svg: true, |
|
|
iframe: true, |
|
|
embed: true, |
|
|
col: true, |
|
|
area: true, |
|
|
hr: true, |
|
|
br: true, |
|
|
wbr: true |
|
|
}; |
|
|
|
|
|
let parent = self.highlightEl; |
|
|
|
|
|
if (self.dragType == "section") { |
|
|
let closest = parent.closest("section, header, footer, body"); |
|
|
if (closest) { |
|
|
parent = closest; |
|
|
} |
|
|
noChildren.section = true; |
|
|
} |
|
|
|
|
|
let parentTagName = parent.tagName.toLowerCase(); |
|
|
let isVattribute = false; |
|
|
|
|
|
if (parent.childElementCount == 0) { |
|
|
for (let attr of parent.attributes) { |
|
|
if (attr.name.startsWith("data-v-") && !attr.name.startsWith("data-v-component-")) { |
|
|
isVattribute = true; |
|
|
break; |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
try { |
|
|
if ((pos.top < (y - halfHeight)) || (pos.left < (x - halfWidth))) { |
|
|
if (noChildren[parentTagName] || isVattribute) { |
|
|
parent.after(self.dragElement); |
|
|
} else { |
|
|
if (parent == self.dragElement.parenNode) { |
|
|
parent.appendChild(self.dragElement); |
|
|
} else { |
|
|
parent.append(self.dragElement); |
|
|
} |
|
|
} |
|
|
|
|
|
prepend = true; |
|
|
} else { |
|
|
if (noChildren[parentTagName] || isVattribute) { |
|
|
parent.parentNode.insertBefore(self.dragElement, parent); |
|
|
} else { |
|
|
parent.prepend(self.dragElement); |
|
|
} |
|
|
|
|
|
prepend = false; |
|
|
}; |
|
|
|
|
|
if (self.designerMode) { |
|
|
let parentOffset = offset(self.dragElement.offsetParent); |
|
|
self.dragElement.style.position = "absolute"; |
|
|
self.dragElement.style.x = x - (parentOffset.left - self.frameDoc.scrollLeft); |
|
|
self.dragElement.style.y = y - (parentOffset.top - self.frameDoc.scrollTop); |
|
|
} |
|
|
|
|
|
} catch(err) { |
|
|
console.log(err); |
|
|
return false; |
|
|
} |
|
|
|
|
|
if (!self.designerMode && self.iconDrag) { |
|
|
self.iconDrag.style.top = (y + 60) + "px"; |
|
|
self.iconDrag.style.left = (x + self.leftPanelWidth + 10) + "px"; |
|
|
} |
|
|
} |
|
|
{ |
|
|
|
|
|
if (Vvveb.WysiwygEditor.isActive ) { |
|
|
if (self.texteditEl.contains(event.target)) { |
|
|
return true; |
|
|
} |
|
|
} |
|
|
|
|
|
document.getElementById("highlight-box").setAttribute("style", |
|
|
`top:${pos.top - (self.frameDoc.scrollTop ?? 0)}px; |
|
|
left:${pos.left - (self.frameDoc.scrollLeft ?? 0)}px; |
|
|
width:${width}px; |
|
|
height:${height}px; |
|
|
display:${event.target.hasAttribute('contenteditable') ? "none":"block"}; |
|
|
border:${self.isDragging ? "1px dashed #0d6efd":""}; |
|
|
`); |
|
|
|
|
|
if (height < 50) { |
|
|
document.getElementById("section-actions").classList.add("outside"); |
|
|
} else { |
|
|
document.getElementById("section-actions").classList.remove("outside"); |
|
|
} |
|
|
|
|
|
let elementType = self._getElementType(event.target); |
|
|
document.querySelector("#highlight-name .type").innerHTML = elementType[0]; |
|
|
document.querySelector("#highlight-name .name").innerHTML = elementType[1]; |
|
|
} |
|
|
} |
|
|
|
|
|
}; |
|
|
|
|
|
self.frameBody.addEventListener("mousemove", highlightMove); |
|
|
|
|
|
let highlightUp = function(event) { |
|
|
self.isResize = false; |
|
|
document.querySelectorAll("#section-actions, #highlight-name").forEach(el => el.style.display = ""); |
|
|
if (self.isDragging) { |
|
|
self.isDragging = false; |
|
|
Vvveb.Builder.highlightEnabled = true; |
|
|
if (self.iconDrag) self.iconDrag.remove(); |
|
|
document.getElementById("component-clone")?.remove(); |
|
|
|
|
|
if (self.dragMoveMutation === false) { |
|
|
if (self.component.dragHtml || Vvveb.dragHtml) { |
|
|
if (self.component) { |
|
|
newElement = generateElements(self.component.html)[0]; |
|
|
self.dragElement.replaceWith(newElement); |
|
|
self.dragElement = newElement; |
|
|
} |
|
|
} |
|
|
|
|
|
if (self.component.afterDrop) self.dragElement = self.component.afterDrop(self.dragElement); |
|
|
} else { |
|
|
self.selectedEl.classList.remove("is-dragged"); |
|
|
self.dragElement.replaceWith(self.selectedEl); |
|
|
self.dragElement = self.selectedEl; |
|
|
} |
|
|
|
|
|
const node = self.dragElement; |
|
|
self.selectNode(node); |
|
|
Vvveb.TreeList.loadComponents(); |
|
|
Vvveb.TreeList.selectComponent(node); |
|
|
self.loadNodeComponent(node); |
|
|
|
|
|
let propertiesTab = document.querySelector(".component-properties-tab a"); |
|
|
if (propertiesTab.offsetParent) { |
|
|
propertiesTab.style.display = ""; |
|
|
const bsTab = bootstrap.Tab.getOrCreateInstance(propertiesTab); |
|
|
bsTab.show(); |
|
|
} |
|
|
|
|
|
if (self.dragType == "section") { |
|
|
node.scrollIntoView({behavior: "smooth", block: "center", inline: "center"}); |
|
|
} |
|
|
|
|
|
if (self.dragMoveMutation === false) { |
|
|
Vvveb.Undo.addMutation({type: 'childList', |
|
|
target: node.parentNode, |
|
|
addedNodes: [node], |
|
|
nextSibling: node.nextSibling}); |
|
|
} else { |
|
|
self.dragMoveMutation.newParent = node.parentNode; |
|
|
self.dragMoveMutation.newNextSibling = node.nextSibling; |
|
|
|
|
|
Vvveb.Undo.addMutation(self.dragMoveMutation); |
|
|
self.dragMoveMutation = false; |
|
|
} |
|
|
} |
|
|
}; |
|
|
|
|
|
self.frameBody.addEventListener("mouseup", highlightUp); |
|
|
|
|
|
let highlightDbClick = function(event) { |
|
|
|
|
|
if (Vvveb.Builder.isPreview == false) { |
|
|
|
|
|
if (!Vvveb.WysiwygEditor.isActive) { |
|
|
self.selectPadding = 10; |
|
|
self.texteditEl = target = event.target; |
|
|
|
|
|
Vvveb.WysiwygEditor.edit(self.texteditEl); |
|
|
|
|
|
_updateSelectBox = function(event) { |
|
|
if (!self.texteditEl) return; |
|
|
let pos = offset(self.selectedEl); |
|
|
|
|
|
let SelectBox = document.getElementById("select-box"); |
|
|
|
|
|
SelectBox.style.top = (pos.top - (self.frameDoc.scrollTop ?? 0) - self.selectPadding) + "px"; |
|
|
SelectBox.style.left = (pos.left - (self.frameDoc.scrollLeft ?? 0) - self.selectPadding) + "px"; |
|
|
SelectBox.style.width = (self.texteditEl.offsetWidth + (self.selectPadding * 2)) + "px"; |
|
|
SelectBox.style.height = (self.texteditEl.offsetHeight + (self.selectPadding * 2)) + "px"; |
|
|
SelectBox.style.display = "block"; |
|
|
}; |
|
|
|
|
|
|
|
|
self.texteditEl.addEventListener("blur", _updateSelectBox); |
|
|
self.texteditEl.addEventListener("keyup", _updateSelectBox); |
|
|
self.texteditEl.addEventListener("paste", _updateSelectBox); |
|
|
self.texteditEl.addEventListener("input", _updateSelectBox); |
|
|
_updateSelectBox(); |
|
|
|
|
|
document.getElementById("select-box").classList.add("text-edit") |
|
|
document.getElementById("select-actions").style.display = "none"; |
|
|
document.getElementById("highlight-box").style.display = "none"; |
|
|
} |
|
|
} |
|
|
}; |
|
|
|
|
|
self.frameBody.addEventListener("dblclick", highlightDbClick); |
|
|
|
|
|
let highlightClick = function(event) { |
|
|
|
|
|
if (Vvveb.Builder.isPreview == false){ |
|
|
if (event.target) { |
|
|
if (Vvveb.WysiwygEditor.isActive ) { |
|
|
if (self.texteditEl.contains(event.target)) { |
|
|
return true; |
|
|
} |
|
|
} |
|
|
|
|
|
let componentTab = document.querySelector(".component-properties-tab a"); |
|
|
if (componentTab.offsetParent) { |
|
|
componentTab.style.display = ""; |
|
|
const bsTab = bootstrap.Tab.getOrCreateInstance(componentTab); |
|
|
bsTab.show(); |
|
|
} |
|
|
|
|
|
self.selectNode(event.target); |
|
|
Vvveb.TreeList.selectComponent(event.target); |
|
|
self.loadNodeComponent(event.target); |
|
|
|
|
|
if (Vvveb.component.resizable) { |
|
|
document.getElementById("select-box").classList.add("resizable"); |
|
|
self.resizeMode = Vvveb.component.resizeMode; |
|
|
} else { |
|
|
document.getElementById("select-box").classList.remove("resizable"); |
|
|
} |
|
|
|
|
|
document.getElementById("add-section-box").style.display = "none"; |
|
|
event.preventDefault(); |
|
|
return false; |
|
|
} |
|
|
} |
|
|
|
|
|
}; |
|
|
|
|
|
self.frameBody.addEventListener("click", highlightClick); |
|
|
|
|
|
}, |
|
|
|
|
|
_initBox: function() { |
|
|
let self = this; |
|
|
|
|
|
document.getElementById("drag-btn").addEventListener("mousedown", function(event) { |
|
|
|
|
|
if (event.which == 1) { |
|
|
self.isDragging = true; |
|
|
document.querySelectorAll("#section-actions, #highlight-name, #select-box").forEach(el => el.style.display = ""); |
|
|
|
|
|
|
|
|
if (self.designerMode) { |
|
|
self.dragElement = self.selectedEl; |
|
|
} else { |
|
|
self.selectedEl.style.position = ""; |
|
|
self.selectedEl.style.top = ""; |
|
|
self.selectedEl.style.left = ""; |
|
|
|
|
|
self.selectedEl.classList.add("is-dragged"); |
|
|
self.dragElement = generateElements(Vvveb.dragHtml)[0]; |
|
|
} |
|
|
|
|
|
const node = self.selectedEl; |
|
|
|
|
|
self.dragMoveMutation = {type: 'move', |
|
|
target: node, |
|
|
oldParent: node.parentNode, |
|
|
oldNextSibling: node.nextSibling}; |
|
|
|
|
|
|
|
|
event.preventDefault(); |
|
|
return false; |
|
|
} |
|
|
}); |
|
|
|
|
|
|
|
|
let resizeDown = function(event) { |
|
|
if (event.which == 1) { |
|
|
document.querySelector("#section-actions, #highlight-name, #highlight-box").style.display = "none"; |
|
|
|
|
|
self.isResize = true; |
|
|
self.initialSize = {"width" : self.selectedEl.offsetWidth, "height" : self.selectedEl.offsetHeight}; |
|
|
self.initialPosition = false; |
|
|
self.resizeHandler = this.className; |
|
|
|
|
|
event.preventDefault(); |
|
|
return false; |
|
|
} |
|
|
}; |
|
|
|
|
|
document.querySelectorAll(".resize > div").forEach(e => e.addEventListener("mousedown", resizeDown)); |
|
|
|
|
|
document.getElementById("down-btn").addEventListener("click", function(event) { |
|
|
|
|
|
document.getElementById("select-box").style.display = "none"; |
|
|
|
|
|
Vvveb.Builder.moveNodeDown(); |
|
|
|
|
|
event.preventDefault(); |
|
|
return false; |
|
|
}); |
|
|
|
|
|
document.getElementById("up-btn").addEventListener("click", function(event) { |
|
|
document.getElementById("select-box").style.display = "none"; |
|
|
|
|
|
Vvveb.Builder.moveNodeUp(); |
|
|
|
|
|
event.preventDefault(); |
|
|
return false; |
|
|
}); |
|
|
|
|
|
document.getElementById("clone-btn").addEventListener("click", function(event) { |
|
|
|
|
|
Vvveb.Builder.cloneNode(); |
|
|
|
|
|
event.preventDefault(); |
|
|
return false; |
|
|
}); |
|
|
|
|
|
document.getElementById("parent-btn").addEventListener("click", function(event) { |
|
|
|
|
|
const node = self.selectedEl.parentNode; |
|
|
|
|
|
self.selectNode(node); |
|
|
self.loadNodeComponent(node); |
|
|
Vvveb.TreeList.selectComponent(node); |
|
|
|
|
|
event.preventDefault(); |
|
|
return false; |
|
|
}); |
|
|
|
|
|
document.getElementById("save-reusable-btn").addEventListener("click", function(event) { |
|
|
|
|
|
const node = self.selectedEl; |
|
|
|
|
|
let type = 'block'; |
|
|
if (node.tagName.toLowerCase() == 'section') { |
|
|
type = 'section'; |
|
|
} |
|
|
|
|
|
const name = prompt("Enter name for new reusable " + type, ''); |
|
|
if (name) { |
|
|
Vvveb.Builder.saveElement(node, type, name); |
|
|
} |
|
|
|
|
|
event.preventDefault(); |
|
|
return false; |
|
|
}); |
|
|
|
|
|
let codeEditorOldValue; |
|
|
document.getElementById("edit-code-btn").addEventListener("click", function(event) { |
|
|
let value = Vvveb.Builder.selectedEl.innerHTML; |
|
|
|
|
|
Vvveb.ModalCodeEditor.show(); |
|
|
Vvveb.ModalCodeEditor.setValue(value); |
|
|
|
|
|
codeEditorOldValue = value; |
|
|
|
|
|
event.preventDefault(); |
|
|
return false; |
|
|
}); |
|
|
|
|
|
let onSave = function(event) { |
|
|
Vvveb.Builder.selectedEl.innerHTML = event.detail; |
|
|
|
|
|
const node = Vvveb.Builder.selectedEl; |
|
|
Vvveb.Undo.addMutation({type:'characterData', |
|
|
target: node, |
|
|
oldValue: codeEditorOldValue, |
|
|
newValue: node.innerHTML}); |
|
|
|
|
|
Vvveb.Builder.selectNode(node); |
|
|
}; |
|
|
|
|
|
window.addEventListener("vvveb.ModalCodeEditor.save", onSave); |
|
|
|
|
|
document.getElementById("translate-code-btn")?.addEventListener("click", function(event) { |
|
|
let selectedEl = Vvveb.Builder.selectedEl; |
|
|
let value = selectedEl.innerHTML.trim(); |
|
|
|
|
|
|
|
|
Vvveb.ModalCodeEditor.show(); |
|
|
Vvveb.ModalCodeEditor.setValue(value); |
|
|
|
|
|
let onSave = function(event) { |
|
|
selectedEl.innerHTML = event.detail; |
|
|
|
|
|
}; |
|
|
|
|
|
window.removeEventListener("vvveb.ModalCodeEditor.save", onSave); |
|
|
window.addEventListener("vvveb.ModalCodeEditor.save", onSave); |
|
|
|
|
|
event.preventDefault(); |
|
|
return false; |
|
|
}); |
|
|
|
|
|
document.getElementById("delete-btn").addEventListener("click", function(event) { |
|
|
document.getElementById("select-box").style.display = "none"; |
|
|
|
|
|
const node = self.selectedEl; |
|
|
|
|
|
Vvveb.Undo.addMutation({type: 'childList', |
|
|
target: node.parentNode, |
|
|
removedNodes: [node], |
|
|
nextSibling: node.nextSibling}); |
|
|
|
|
|
self.selectedEl.remove(); |
|
|
Vvveb.TreeList.loadComponents(); |
|
|
Vvveb.SectionList.loadSections(); |
|
|
|
|
|
event.preventDefault(); |
|
|
return false; |
|
|
}); |
|
|
|
|
|
let addSectionBox = document.getElementById("add-section-box"); |
|
|
let addSectionElement = {}; |
|
|
|
|
|
document.getElementById("add-section-btn").addEventListener("click", function(event) { |
|
|
|
|
|
addSectionElement = self.highlightEl; |
|
|
addSectionBox.style.display = "block"; |
|
|
|
|
|
let pos = offset(addSectionElement); |
|
|
let top = ((pos.top + window.FrameWindow.pageYOffset + addSectionElement.clientTop) - self.frameHtml.scrollTop) + addSectionElement.offsetHeight; |
|
|
let left = ((pos.left + window.FrameWindow.pageXOffset + addSectionElement.clientLeft) - self.frameHtml.scrollLeft) + (addSectionElement.offsetWidth / 2) - (addSectionBox.offsetWidth / 2); |
|
|
let outerHeight = window.FrameWindow.innerHeight + self.frameHtml.scrollTop; |
|
|
|
|
|
|
|
|
if (left < 0) left = 0; |
|
|
if (top < 0) top = 0; |
|
|
if ((left + addSectionBox.offsetWidth) > self.frameHtml.offsetWidth) left = self.frameHtml.offsetWidth - addSectionBox.offsetWidth; |
|
|
if (((top + addSectionBox.offsetHeight) + self.frameHtml.scrollTop) > outerHeight) top = top - addSectionBox.offsetHeight; |
|
|
|
|
|
addSectionBox.style.top = top + "px"; |
|
|
addSectionBox.style.left = left + "px"; |
|
|
|
|
|
event.preventDefault(); |
|
|
return false; |
|
|
}); |
|
|
|
|
|
document.getElementById("close-section-btn").addEventListener("click", function(event) { |
|
|
addSectionBox.style.display = "none"; |
|
|
}); |
|
|
|
|
|
function addSectionComponent(component, after = true) { |
|
|
let node = generateElements(component.html)[0]; |
|
|
|
|
|
if (after) { |
|
|
addSectionElement.after(node); |
|
|
} else { |
|
|
addSectionElement.append(node); |
|
|
} |
|
|
|
|
|
if (component.afterDrop) { |
|
|
node = component.afterDrop(node); |
|
|
} |
|
|
|
|
|
self.selectNode(node); |
|
|
self.loadNodeComponent(node); |
|
|
Vvveb.TreeList.loadComponents(); |
|
|
Vvveb.TreeList.selectComponent(node); |
|
|
|
|
|
Vvveb.Undo.addMutation({type: 'childList', |
|
|
target: node.parentNode, |
|
|
addedNodes: [node], |
|
|
nextSibling: node.nextSibling}); |
|
|
} |
|
|
|
|
|
addSectionBox.addEventListener("click", function(event) { |
|
|
let element = event.target.closest(".components-list li ol li"); |
|
|
if (element) { |
|
|
let html = Vvveb.Components.get(element.dataset.type); |
|
|
|
|
|
addSectionComponent(html, (document.querySelector("[name='add-section-insert-mode']:checked").value == "after")); |
|
|
|
|
|
addSectionBox.style.display = "none"; |
|
|
} |
|
|
}); |
|
|
|
|
|
addSectionBox.addEventListener("click", function(event) { |
|
|
let element = event.target.closest(".blocks-list li ol li"); |
|
|
if (element) { |
|
|
let html = Vvveb.Blocks.get(element.dataset.type); |
|
|
|
|
|
addSectionComponent(html, (document.querySelector("[name='add-section-insert-mode']:checked").value == "after")); |
|
|
|
|
|
addSectionBox.style.display = "none"; |
|
|
} |
|
|
}); |
|
|
|
|
|
|
|
|
addSectionBox.addEventListener("click", function(event) { |
|
|
let element = event.target.closest(".sections-list li ol li"); |
|
|
if (element) { |
|
|
let html = Vvveb.Sections.get(element.dataset.type); |
|
|
|
|
|
addSectionComponent(html, (document.querySelector("[name='add-section-insert-mode']:checked").value == "after")); |
|
|
|
|
|
addSectionBox.style.display = "none"; |
|
|
} |
|
|
}); |
|
|
|
|
|
}, |
|
|
|
|
|
|
|
|
_initDragdrop : function() { |
|
|
|
|
|
let self = this; |
|
|
self.isDragging = false; |
|
|
|
|
|
document.addEventListener("mousedown", function(event) { |
|
|
let element = event.target.closest(".drag-elements-sidepane ul > li > ol > li[data-drag-type]"); |
|
|
let html; |
|
|
|
|
|
if (element && event.which == 1) { |
|
|
document.getElementById("component-clone")?.remove(); |
|
|
document.querySelectorAll("#section-actions, #highlight-name, #select-box").forEach(e => e.style.display = "none"); |
|
|
|
|
|
self.dragType = element.dataset.dragType; |
|
|
if (self.dragType == "component") { |
|
|
self.component = Vvveb.Components.get(element.dataset.type); |
|
|
} |
|
|
else if (self.dragType == "section") { |
|
|
self.component = Vvveb.Sections.get(element.dataset.type); |
|
|
} |
|
|
else if (self.dragType == "block") { |
|
|
self.component = Vvveb.Blocks.get(element.dataset.type); |
|
|
} |
|
|
|
|
|
if (self.component.dragHtml) { |
|
|
html = self.component.dragHtml; |
|
|
} else if (Vvveb.dragHtml) { |
|
|
html = Vvveb.dragHtml; |
|
|
} else { |
|
|
html = self.component.html; |
|
|
} |
|
|
|
|
|
self.dragElement = generateElements(html)[0]; |
|
|
|
|
|
|
|
|
if (self.component.dragStart) self.dragElement = self.component.dragStart(self.dragElement); |
|
|
|
|
|
self.isDragging = true; |
|
|
if (Vvveb.dragIcon == 'html') { |
|
|
self.iconDrag = generateElements(html)[0]; |
|
|
self.iconDrag.setAttribute("id", "dragElement-clone"); |
|
|
self.iconDrag.style.position = "absolute"; |
|
|
} |
|
|
else if (self.designerMode == false) { |
|
|
self.iconDrag = document.createElement("img"); |
|
|
self.iconDrag.setAttribute("id", "dragElement-clone"); |
|
|
self.iconDrag.setAttribute("src", element.style.backgroundImage.replace(/^url\(['"](.+)['"]\)/, '$1')); |
|
|
|
|
|
self.iconDrag.style.zIndex = "100"; |
|
|
self.iconDrag.style.position = "absolute"; |
|
|
self.iconDrag.style.width = "64px"; |
|
|
self.iconDrag.style.height = "64px"; |
|
|
self.iconDrag.style.top = event.y + "px"; |
|
|
self.iconDrag.style.left = event.x + "px"; |
|
|
} |
|
|
|
|
|
document.body.append(self.iconDrag); |
|
|
|
|
|
event.preventDefault(); |
|
|
return false; |
|
|
} |
|
|
}); |
|
|
|
|
|
document.addEventListener('mouseup', function(event) { |
|
|
if (self.iconDrag && self.isDragging == true) { |
|
|
self.isDragging = false; |
|
|
document.getElementById("component-clone")?.remove(); |
|
|
document.querySelectorAll("#section-actions, #highlight-name, #select-box").forEach(el => el.style.display = ""); |
|
|
self.iconDrag.remove(); |
|
|
if(self.dragElement){ |
|
|
self.dragElement.remove(); |
|
|
} |
|
|
} |
|
|
}); |
|
|
|
|
|
document.addEventListener('mousemove', function(event) { |
|
|
if (self.iconDrag && self.isDragging == true) { |
|
|
let x = (event.clientX || event.clientX); |
|
|
let y = (event.clientY || event.clientY); |
|
|
|
|
|
self.iconDrag.style.left = (x - 60) + "px"; |
|
|
self.iconDrag.style.top = (y - 30) + "px"; |
|
|
|
|
|
const elementMouseIsOver = document.elementFromPoint(x - 60, y - 40); |
|
|
|
|
|
|
|
|
return; |
|
|
if (elementMouseIsOver && elementMouseIsOver.tagName == 'IFRAME') { |
|
|
self.frameBody.dispatchEvent(new MouseEvent("mousemove", { |
|
|
bubbles: true, |
|
|
cancelable: true, |
|
|
})); |
|
|
|
|
|
|
|
|
event.stopPropagation(); |
|
|
self.selectNode(false); |
|
|
} |
|
|
} |
|
|
}); |
|
|
|
|
|
document.addEventListener("mouseup", function(event) { |
|
|
let element = event.target.closest(".drag-elements-sidepane ul > ol > li > li"); |
|
|
if (element) { |
|
|
self.isDragging = false; |
|
|
document.getElementById("component-clone")?.remove() |
|
|
document.querySelectorAll("#section-actions, #highlight-name, #select-box").forEach(el => el.style.display = ""); |
|
|
} |
|
|
}); |
|
|
|
|
|
}, |
|
|
|
|
|
removeHelpers: function (html, keepHelperAttributes = false) { |
|
|
|
|
|
html = html.replace(/<[^>]+?data-vvveb-helpers.+?>/gi, ""); |
|
|
|
|
|
if (!keepHelperAttributes) { |
|
|
html = html.replace(/\s*data-vvveb-\w+(=["'].*?["'])?\s*/gi, ""); |
|
|
} |
|
|
|
|
|
html = html.replaceAll("vvveb-hidden", ""); |
|
|
return html; |
|
|
}, |
|
|
|
|
|
getHtml: function(keepHelperAttributes = true) { |
|
|
let doc = window.FrameDocument; |
|
|
let hasDoctpe = (doc.doctype !== null); |
|
|
let html = ""; |
|
|
|
|
|
doc.querySelectorAll("[contenteditable]").forEach(e => e.removeAttribute("contenteditable")); |
|
|
doc.querySelectorAll("[spellcheckker]").forEach(e => e.removeAttribute("spellcheckker")); |
|
|
doc.querySelectorAll('script[src^="chrome-extension://"]').forEach(e => e.remove()); |
|
|
doc.querySelectorAll('script[src^="moz-extension://"]').forEach(e => e.remove()); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
window.dispatchEvent(new CustomEvent("vvveb.getHtml.before", {detail: doc})); |
|
|
|
|
|
if (hasDoctpe) html = |
|
|
"<!DOCTYPE " |
|
|
+ doc.doctype.name |
|
|
+ (doc.doctype.publicId ? ' PUBLIC "' + doc.doctype.publicId + '"' : '') |
|
|
+ (!doc.doctype.publicId && doc.doctype.systemId ? ' SYSTEM' : '') |
|
|
+ (doc.doctype.systemId ? ' "' + doc.doctype.systemId + '"' : '') |
|
|
+ ">\n"; |
|
|
|
|
|
Vvveb.FontsManager.cleanUnusedFonts(); |
|
|
|
|
|
html += doc.documentElement.outerHTML; |
|
|
html = this.removeHelpers(html, keepHelperAttributes); |
|
|
|
|
|
window.dispatchEvent(new CustomEvent("vvveb.getHtml.after", {detail: doc})); |
|
|
window.dispatchEvent(new CustomEvent("vvveb.getHtml.filter", {detail: html})); |
|
|
|
|
|
return html; |
|
|
}, |
|
|
|
|
|
setHtml: function(html) { |
|
|
|
|
|
|
|
|
|
|
|
function getTag(html, tag, outerHtml = false) { |
|
|
const start = html.indexOf("<" + tag); |
|
|
const end = html.indexOf("</" + tag); |
|
|
|
|
|
if (start >= 0 && end >= 0) { |
|
|
if (outerHtml) |
|
|
return html.slice(start, end + 3 + tag.length); |
|
|
else |
|
|
return html.slice(html.indexOf(">", start) + 1, end); |
|
|
} else { |
|
|
return html; |
|
|
} |
|
|
} |
|
|
|
|
|
if (this.runJsOnSetHtml) { |
|
|
this.frameBody.innerHTML = getTag(html, "body"); |
|
|
} |
|
|
else { |
|
|
window.FrameDocument.body.innerHTML = getTag(html, "body"); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let headHtml = getTag(html, "head"); |
|
|
if (window.FrameDocument.head.innerHTML != headHtml) { |
|
|
window.FrameDocument.head.innerHTML = headHtml; |
|
|
} |
|
|
}, |
|
|
|
|
|
saveElement: function(element, type, name, callback) { |
|
|
if (type == 'section') { |
|
|
Vvveb.Sections.add('reusable/'+ name, { |
|
|
name, |
|
|
image: "img/logo-small.png", |
|
|
html: element.outerHTML}); |
|
|
|
|
|
if (Vvveb.SectionsGroup["Reusable"] === undefined) { |
|
|
Vvveb.SectionsGroup["Reusable"] = []; |
|
|
} |
|
|
|
|
|
Vvveb.SectionsGroup["Reusable"].push('reusable/'+ name); |
|
|
Vvveb.Builder.loadSectionGroups(); |
|
|
} else { |
|
|
Vvveb.Blocks.add('reusable/'+ name, { |
|
|
name, |
|
|
image: "img/logo-small.png", |
|
|
html: element.outerHTML}); |
|
|
|
|
|
if (Vvveb.BlocksGroup["Reusable"] === undefined) { |
|
|
Vvveb.BlocksGroup["Reusable"] = []; |
|
|
} |
|
|
|
|
|
Vvveb.BlocksGroup["Reusable"].push('reusable/'+ name); |
|
|
Vvveb.Builder.loadBlockGroups(); |
|
|
} |
|
|
|
|
|
let data = {type, name, html:element.outerHTML}; |
|
|
|
|
|
fetch(saveReusableUrl, {method: "POST", body: new URLSearchParams(data)}) |
|
|
.then((response) => { |
|
|
if (!response.ok) { throw new Error(response) } |
|
|
return response.text() |
|
|
}) |
|
|
.then((data) => { |
|
|
if (callback) callback(data); |
|
|
let bg = "bg-success"; |
|
|
if (true || data.success || text == "success") { |
|
|
} else { |
|
|
bg = "bg-danger"; |
|
|
} |
|
|
|
|
|
displayToast(bg, "Save", data.message ?? data); |
|
|
}) |
|
|
.catch(error => { |
|
|
console.log(error.statusText); |
|
|
displayToast("bg-danger", "Error", "Error saving!"); |
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}, |
|
|
|
|
|
saveAjax: function(data, saveUrl, callback, error ) { |
|
|
if (!data["file"]) { |
|
|
data["file"] = Vvveb.FileManager.getCurrentFileName(); |
|
|
} |
|
|
|
|
|
if (!data["startTemplateUrl"]) { |
|
|
data["html"] = this.getHtml(); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return fetch(saveUrl, { |
|
|
method: "POST", |
|
|
headers: {'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'}, |
|
|
body: nestedFormData(data) |
|
|
}) |
|
|
.then((response) => { |
|
|
if (!response.ok) { return Promise.reject(response); } |
|
|
return response.text(); |
|
|
}) |
|
|
.then((data) => { |
|
|
if (callback) callback(data); |
|
|
Vvveb.Undo.reset(); |
|
|
document.querySelectorAll("#top-panel .save-btn").forEach(e => e.setAttribute("disabled", "true")); |
|
|
}) |
|
|
.catch((err) => { |
|
|
if (error) error(err); |
|
|
let message = error?.statusText ?? "Error saving!"; |
|
|
displayToast("bg-danger", "Error", message); |
|
|
|
|
|
if (err.hasOwnProperty('text')) err.text().then( errorMessage => { |
|
|
let message = errorMessage.substr(0, 200); |
|
|
displayToast("bg-danger", "Error", message); |
|
|
}); |
|
|
}); |
|
|
}, |
|
|
|
|
|
setDesignerMode: function(designerMode = false) { |
|
|
this.designerMode = designerMode; |
|
|
} |
|
|
|
|
|
}; |
|
|
|
|
|
Vvveb.ModalCodeEditor = { |
|
|
modal: false, |
|
|
editor: false, |
|
|
|
|
|
init: function(modal = false, editor = false) { |
|
|
if (modal) { |
|
|
this.modal = modal; |
|
|
} else { |
|
|
this.modal = document.getElementById('codeEditorModal'); |
|
|
} |
|
|
if (editor) { |
|
|
this.editor = editor; |
|
|
} else { |
|
|
this.editor = this.modal.querySelector('textarea'); |
|
|
} |
|
|
|
|
|
let self = this; |
|
|
|
|
|
this.modal.querySelector('.save-btn').addEventListener("click", function(event) { |
|
|
window.dispatchEvent(new CustomEvent("vvveb.ModalCodeEditor.save", {detail: self.getValue()})); |
|
|
self.hide(); |
|
|
return false; |
|
|
}); |
|
|
}, |
|
|
|
|
|
show: function(value) { |
|
|
if (!this.modal) { |
|
|
this.init(); |
|
|
} |
|
|
|
|
|
const bsModal = bootstrap.Modal.getOrCreateInstance(this.modal); |
|
|
return bsModal.show(); |
|
|
}, |
|
|
|
|
|
hide: function(value) { |
|
|
const bsModal = bootstrap.Modal.getOrCreateInstance(this.modal); |
|
|
return bsModal.hide(); |
|
|
}, |
|
|
|
|
|
getValue: function() { |
|
|
return this.editor.value;; |
|
|
}, |
|
|
|
|
|
setValue: function(value) { |
|
|
if (!this.modal) { |
|
|
this.init(); |
|
|
} |
|
|
|
|
|
document.querySelectorAll("#top-panel .save-btn").forEach(e => e.removeAttribute("disabled")); |
|
|
this.editor.value = value; |
|
|
}, |
|
|
} |
|
|
|
|
|
Vvveb.CodeEditor = { |
|
|
|
|
|
isActive: false, |
|
|
oldValue: '', |
|
|
doc:false, |
|
|
textarea:false, |
|
|
|
|
|
init: function(doc) { |
|
|
this.textarea = document.querySelector("#vvveb-code-editor textarea"); |
|
|
this.textarea.value = Vvveb.Builder.getHtml(); |
|
|
|
|
|
this.textarea.addEventListener("keyup", e => { |
|
|
delay(() => Vvveb.Builder.setHtml(this.value), 1000); |
|
|
}); |
|
|
|
|
|
|
|
|
Vvveb.Builder.frameBody.addEventListener("vvveb.undo.add", () => Vvveb.CodeEditor.setValue()); |
|
|
Vvveb.Builder.frameBody.addEventListener("vvveb.undo.restore", () => Vvveb.CodeEditor.setValue()); |
|
|
|
|
|
|
|
|
Vvveb.Builder.documentFrame.addEventListener("load", () => Vvveb.CodeEditor.setValue()); |
|
|
|
|
|
this.isActive = true; |
|
|
}, |
|
|
|
|
|
setValue: function(value) { |
|
|
if (this.isActive) { |
|
|
this.textarea.value = Vvveb.Builder.getHtml(); |
|
|
} |
|
|
}, |
|
|
|
|
|
destroy: function(element) { |
|
|
|
|
|
}, |
|
|
|
|
|
toggle: function() { |
|
|
if (this.isActive != true) { |
|
|
this.isActive = true; |
|
|
return this.init(); |
|
|
} |
|
|
this.isActive = false; |
|
|
this.destroy(); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
Vvveb.CssEditor = { |
|
|
|
|
|
isActive: false, |
|
|
oldValue: '', |
|
|
doc:false, |
|
|
textarea:false, |
|
|
|
|
|
init: function(doc) { |
|
|
this.textarea = document.getElementById("css-editor") |
|
|
this.textarea.value = Vvveb.StyleManager.getCss(); |
|
|
let self = this; |
|
|
|
|
|
document.querySelectorAll('[href="#css-tab"],[href="#configuration"]').forEach( t => t.addEventListener("click", e => { |
|
|
self.textarea.value = Vvveb.StyleManager.getCss(); |
|
|
})); |
|
|
|
|
|
this.textarea.addEventListener("keyup", e => { |
|
|
delay(() => Vvveb.StyleManager.setCss(self.textarea.value), 1000); |
|
|
}); |
|
|
}, |
|
|
|
|
|
getValue: function() { |
|
|
return this.textarea.value; |
|
|
}, |
|
|
|
|
|
setValue: function(value) { |
|
|
this.textarea.value = value; |
|
|
Vvveb.StyleManager.setCss(value); |
|
|
}, |
|
|
|
|
|
destroy: function() { |
|
|
} |
|
|
} |
|
|
|
|
|
function displayToast(bg, title, message, id = "top-toast") { |
|
|
document.querySelector("#" + id + " .toast-body .message").innerHTML = message.replace(/(?:\r\n|\r|\n)/g, '<br>'); |
|
|
let header = document.querySelector("#" + id + " .toast-header"); |
|
|
header.classList.remove("bg-danger", "bg-success") |
|
|
header.classList.add(bg); |
|
|
header.querySelector("strong").innerHTML = title; |
|
|
document.querySelector("#" + id + " .toast").classList.add("show"); |
|
|
delay(() => document.querySelector("#" + id + " .toast").classList.remove("show"), 5000); |
|
|
} |
|
|
|
|
|
Vvveb.Gui = { |
|
|
|
|
|
init: function() { |
|
|
document.querySelectorAll("[data-vvveb-action]").forEach(function (el,i) { |
|
|
const on = el.dataset.vvvebOn ?? "click"; |
|
|
el.addEventListener(on, Vvveb.Gui[el.dataset.vvvebAction]); |
|
|
}); |
|
|
|
|
|
this.shortcuts(); |
|
|
}, |
|
|
|
|
|
shortcuts: function() { |
|
|
let self = this; |
|
|
|
|
|
handleShortcuts = function(e) { |
|
|
if (e.ctrlKey) { |
|
|
switch (e.key) { |
|
|
case 's': |
|
|
e.preventDefault(); |
|
|
let btn = document.querySelector('.save-btn'); |
|
|
let url = btn.dataset.vvvebUrl; |
|
|
self.saveAjax(null, url, document.querySelector('.save-btn')); |
|
|
return; |
|
|
case 'z': |
|
|
e.preventDefault(); |
|
|
self.undo(); |
|
|
return; |
|
|
case 'Z': |
|
|
case 'y': |
|
|
e.preventDefault(); |
|
|
self.redo(); |
|
|
return; |
|
|
case 'L': |
|
|
e.preventDefault(); |
|
|
self.toggleTreeList(); |
|
|
return; |
|
|
case 'e': |
|
|
e.preventDefault(); |
|
|
self.toggleEditor(); |
|
|
return; |
|
|
case 'P': |
|
|
e.preventDefault(); |
|
|
self.newPage(); |
|
|
return; |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
document.addEventListener('keydown', handleShortcuts); |
|
|
window.addEventListener('vvveb.iframe.loaded', () => { |
|
|
Vvveb.Builder.frameBody.addEventListener('keydown', handleShortcuts); |
|
|
}); |
|
|
|
|
|
}, |
|
|
|
|
|
undo : function () { |
|
|
if (Vvveb.WysiwygEditor.isActive) { |
|
|
Vvveb.WysiwygEditor.undo(); |
|
|
} else { |
|
|
Vvveb.Undo.undo(); |
|
|
} |
|
|
Vvveb.Builder.selectNode(); |
|
|
}, |
|
|
|
|
|
redo : function () { |
|
|
if (Vvveb.WysiwygEditor.isActive) { |
|
|
Vvveb.WysiwygEditor.redo(); |
|
|
} else { |
|
|
Vvveb.Undo.redo(); |
|
|
} |
|
|
Vvveb.Builder.selectNode(); |
|
|
}, |
|
|
|
|
|
|
|
|
save : function () { |
|
|
document.getElementById('textarea-modal textarea').value = Vvveb.Builder.getHtml(); |
|
|
document.getElementById('textarea-modal').modal(); |
|
|
}, |
|
|
|
|
|
|
|
|
saveAjax : function (event, saveUrl = null, saveBtn = null) { |
|
|
let btn = saveBtn ?? this; |
|
|
saveUrl = saveUrl ?? this.dataset.vvvebUrl; |
|
|
let file = Vvveb.FileManager.getPageData('file'); |
|
|
|
|
|
if (btn.classList.contains("save-offcanvas")) { |
|
|
if (document.querySelector("#save-offcanvas [name=template]:checked").value == "new") { |
|
|
file = document.querySelector("#save-offcanvas [name=folder]").value + "/" + document.querySelector("#save-offcanvas [name=file]").value; |
|
|
} |
|
|
} |
|
|
|
|
|
btn.querySelector(".loading").classList.remove("d-none"); |
|
|
btn.querySelector(".button-text").classList.add("d-none"); |
|
|
|
|
|
return Vvveb.Builder.saveAjax({file}, saveUrl, (data) => { |
|
|
|
|
|
|
|
|
let bg = "bg-success"; |
|
|
if (true || data.success || data == "success") { |
|
|
document.querySelectorAll("#top-panel .save-btn").forEach(e => e.setAttribute("disabled", "true")); |
|
|
} else { |
|
|
bg = "bg-danger"; |
|
|
} |
|
|
|
|
|
displayToast(bg, "Save", data.message ?? data); |
|
|
|
|
|
const offcanvas = document.getElementById('save-offcanvas'); |
|
|
if (offcanvas) { |
|
|
let instance = bootstrap.Offcanvas.getInstance(offcanvas); |
|
|
if (instance) instance.hide(); |
|
|
} |
|
|
|
|
|
btn.querySelector(".loading").classList.add("d-none"); |
|
|
btn.querySelector(".button-text").classList.remove("d-none"); |
|
|
}, (error) => { |
|
|
btn.querySelector(".loading").classList.add("d-none"); |
|
|
btn.querySelector(".button-text").classList.remove("d-none"); |
|
|
let message = error?.statusText ?? "Error saving!"; |
|
|
displayToast("bg-danger", "Error", message); |
|
|
}); |
|
|
}, |
|
|
|
|
|
download : function () { |
|
|
const filename = /[^\/]+$/.exec(Vvveb.Builder.iframe.src)[0]; |
|
|
const uriContent = "data:application/octet-stream," + encodeURIComponent(Vvveb.Builder.getHtml()); |
|
|
|
|
|
let link = document.createElement('a'); |
|
|
if ('download' in link) { |
|
|
link.dataset.download = filename; |
|
|
link.href = uriContent; |
|
|
link.target = "_blank"; |
|
|
|
|
|
document.body.appendChild(link); |
|
|
const result = link.click(); |
|
|
document.body.removeChild(link); |
|
|
link.remove(); |
|
|
|
|
|
} else { |
|
|
location.href = uriContent; |
|
|
} |
|
|
}, |
|
|
|
|
|
viewport : function () { |
|
|
document.getElementById("canvas").setAttribute("class", this.dataset.view); |
|
|
document.getElementById("iframe1").removeAttribute("style"); |
|
|
document.querySelectorAll(".responsive-btns .active").forEach(e => e.classList.remove("active")); |
|
|
if (this.dataset.view) this.classList.add("active"); |
|
|
}, |
|
|
|
|
|
toggleEditor : function () { |
|
|
document.getElementById("vvveb-builder").classList.toggle("bottom-panel-expand"); |
|
|
document.getElementById("toggleEditorJsExecute").classList.toggle("d-none"); |
|
|
|
|
|
document.querySelector(".breadcrumb-navigator .breadcrumb").classList.toggle("d-none"); |
|
|
Vvveb.CodeEditor.toggle(); |
|
|
}, |
|
|
|
|
|
toggleEditorJsExecute : function () { |
|
|
Vvveb.Builder.runJsOnSetHtml = this.checked; |
|
|
}, |
|
|
|
|
|
preview : function () { |
|
|
(Vvveb.Builder.isPreview == true)?Vvveb.Builder.isPreview = false:Vvveb.Builder.isPreview = true; |
|
|
document.getElementById("iframe-layer").classList.toggle("d-none"); |
|
|
document.getElementById("vvveb-builder").classList.toggle("preview"); |
|
|
}, |
|
|
|
|
|
fullscreen : function () { |
|
|
launchFullScreen(document); |
|
|
}, |
|
|
|
|
|
search : function () { |
|
|
let searchText = this.value; |
|
|
let panel = this.parentNode.parentNode.querySelector("div > ul"); |
|
|
panel.querySelectorAll("li ol li").forEach(function (el, i) { |
|
|
el.style.display = "none"; |
|
|
if (el.dataset.search.indexOf(searchText) > -1) el.style.display = ""; |
|
|
}); |
|
|
}, |
|
|
|
|
|
clearSearch : function (e) { |
|
|
let input = this.parentNode.querySelector("input"); |
|
|
input.value = ""; |
|
|
input.dispatchEvent(new KeyboardEvent("keyup", { |
|
|
bubbles: true, |
|
|
cancelable: true, |
|
|
})); |
|
|
}, |
|
|
|
|
|
expand : function (e) { |
|
|
this.parentNode.parentNode.parentNode.querySelectorAll('input.header_check[type="checkbox"]').forEach(e => e.checked = true); |
|
|
}, |
|
|
|
|
|
collapse : function (e) { |
|
|
this.parentNode.parentNode.parentNode.querySelectorAll('input.header_check[type="checkbox"]').forEach(e => e.checked = false); |
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
newPage : function () { |
|
|
|
|
|
let newPageModal = document.getElementById('new-page-modal'); |
|
|
let form = newPageModal.querySelector("form"); |
|
|
|
|
|
const bsModal = bootstrap.Modal.getOrCreateInstance(newPageModal); |
|
|
bsModal.show(); |
|
|
|
|
|
let submitForm = function(e) { |
|
|
|
|
|
let data = {}; |
|
|
this.querySelectorAll("input[type=text],input[type=checkbox]:checked,input[type=radio]:checked,input[name=image], select:not(:disabled)").forEach( (el, i) => { |
|
|
if (el.offsetParent || el.name == 'image') data[el.name] = el.value; |
|
|
}); |
|
|
|
|
|
if (data['file']) { |
|
|
data['title'] = data['file'].replace('/', '').replace('.html', ''); |
|
|
|
|
|
if (!data['name']) { |
|
|
data['name'] = data['title']; |
|
|
} |
|
|
data['url'] = data['file'] = data['folder'] + "/" + data['file']; |
|
|
|
|
|
} |
|
|
|
|
|
e.preventDefault(); |
|
|
|
|
|
return Vvveb.Builder.saveAjax(data, this.action, function (savedData) { |
|
|
data.title = data.name; |
|
|
|
|
|
if (typeof savedData === 'object' && savedData !== null) { |
|
|
data.name = savedData.name ?? data.name; |
|
|
data.url = savedData.url ?? data.url; |
|
|
data.file = savedData.file ?? data.file; |
|
|
data.title = savedData.title ?? data.title; |
|
|
} |
|
|
|
|
|
let page = Vvveb.FileManager.addPage(data.name, data); |
|
|
Vvveb.FileManager.loadPage(data.name); |
|
|
Vvveb.FileManager.scrollToPage(page); |
|
|
bsModal.hide(); |
|
|
}); |
|
|
}; |
|
|
|
|
|
if (!form.dataset.init) { |
|
|
form.addEventListener("submit", submitForm); |
|
|
form.dataset.init = true; |
|
|
} |
|
|
}, |
|
|
|
|
|
setDesignerMode : function () { |
|
|
|
|
|
let designerMode = this.attributes["aria-pressed"].value == "true"; |
|
|
Vvveb.Builder.setDesignerMode(designerMode); |
|
|
}, |
|
|
|
|
|
|
|
|
togglePanel: function (panel, cssVar) { |
|
|
panel = document.querySelector(panel); |
|
|
let body = document.querySelector("body"); |
|
|
let prevValue = getComputedStyle(body).getPropertyValue(cssVar); |
|
|
let visible = false; |
|
|
|
|
|
if (prevValue !== "0px") { |
|
|
panel.dataset.layoutToggle = prevValue; |
|
|
body.style.setProperty(cssVar, "0px"); |
|
|
panel.style.display = "none"; |
|
|
visible = false; |
|
|
} else { |
|
|
prevValue= panel.dataset.layoutToggle; |
|
|
body.style.setProperty(cssVar, ""); |
|
|
panel.style.display = ""; |
|
|
visible = true; |
|
|
} |
|
|
|
|
|
return visible; |
|
|
}, |
|
|
|
|
|
toggleFileManager: function () { |
|
|
Vvveb.Gui.togglePanel("#filemanager", "--builder-filemanager-height"); |
|
|
}, |
|
|
|
|
|
toggleLeftColumn: function () { |
|
|
Vvveb.Gui.togglePanel("#left-panel", "--builder-left-panel-width"); |
|
|
}, |
|
|
|
|
|
toggleRightColumn: function (rightColumnEnabled = null) { |
|
|
rightColumnEnabled = Vvveb.Gui.togglePanel("#right-panel", "--builder-right-panel-width"); |
|
|
|
|
|
document.getElementById("vvveb-builder").classList.toggle("no-right-panel"); |
|
|
document.querySelector(".component-properties-tab").classList.toggle("d-none"); |
|
|
|
|
|
Vvveb.Components.componentPropertiesElement = (rightColumnEnabled ? "#right-panel" :"#left-panel #properties") + " .component-properties"; |
|
|
let componentTab = document.querySelector("#components-tab"); |
|
|
|
|
|
if (document.getElementById("properties").offsetParent) { |
|
|
const bsTab = bootstrap.Tab.getOrCreateInstance(componentTab); |
|
|
componentTab.style.display = ""; |
|
|
bsTab.show(); |
|
|
} |
|
|
|
|
|
}, |
|
|
|
|
|
toggleTreeList: function () { |
|
|
let treeList = document.getElementById("tree-list"); |
|
|
treeList.classList.toggle("d-none"); |
|
|
if (!treeList.offsetParent) { |
|
|
document.getElementById("toggle-tree-list").classList.remove("active"); |
|
|
} |
|
|
}, |
|
|
|
|
|
darkMode: function () { |
|
|
let theme = document.documentElement.getAttribute("data-bs-theme"); |
|
|
let icon = document.querySelector(".btn-dark-mode i"); |
|
|
|
|
|
if (theme == "dark") { |
|
|
theme = "light"; |
|
|
icon.classList.remove("la-moon") |
|
|
icon.classList.add("la-sun"); |
|
|
} else if (theme == "light" || theme == "auto") { |
|
|
theme = "dark"; |
|
|
icon.classList.remove("la-sun") |
|
|
icon.classList.add("la-moon"); |
|
|
} else { |
|
|
theme = "auto"; |
|
|
} |
|
|
|
|
|
document.documentElement.setAttribute("data-bs-theme", theme); |
|
|
localStorage.setItem('theme', theme); |
|
|
|
|
|
}, |
|
|
|
|
|
zoomChange: function () { |
|
|
let wrapper = document.getElementById("iframe-wrapper"); |
|
|
let scale = ""; |
|
|
let height = ""; |
|
|
if (this.value != "100") { |
|
|
scale = "scale(" + this.value + "%)"; |
|
|
height = ((100 / this.value) * 100) + "%"; |
|
|
} |
|
|
wrapper.style.transform = scale; |
|
|
wrapper.style.height = height; |
|
|
}, |
|
|
|
|
|
setState: function () { |
|
|
Vvveb.StyleManager.setState(this.value); |
|
|
Vvveb.Builder.reloadComponent(); |
|
|
} |
|
|
} |
|
|
|
|
|
Vvveb.StyleManager = { |
|
|
|
|
|
styles:{}, |
|
|
cssContainer:false, |
|
|
mobileWidth: '320px', |
|
|
tabletWidth: '768px', |
|
|
doc:false, |
|
|
inlineCSS:false, |
|
|
currentElement:null, |
|
|
currentSelector:null, |
|
|
state:"", |
|
|
|
|
|
init: function(doc) { |
|
|
if (doc) { |
|
|
this.doc = doc; |
|
|
|
|
|
let style = false; |
|
|
let _style = false; |
|
|
|
|
|
|
|
|
for (let i = 0; i < doc.styleSheets.length; i++) { |
|
|
_style = doc.styleSheets[i]; |
|
|
if (_style.ownerNode.id && _style.ownerNode.id == "vvvebjs-styles") { |
|
|
style = _style.ownerNode; |
|
|
break; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (!style) { |
|
|
style = generateElements('<style id="vvvebjs-styles"></style>')[0]; |
|
|
doc.head.append(style); |
|
|
return this.cssContainer = style; |
|
|
} |
|
|
|
|
|
|
|
|
this.cssContainer = style; |
|
|
this.loadCss(); |
|
|
|
|
|
return this.cssContainer; |
|
|
} |
|
|
}, |
|
|
|
|
|
loadCss: function() { |
|
|
let style = this.cssContainer.sheet; |
|
|
|
|
|
for (let j = 0; j < style.cssRules.length; j++) { |
|
|
const media = (typeof style.cssRules[j].media === "undefined") ? |
|
|
"desktop" : (style.cssRules[j].media[0] === "screen and (max-width: 1220px)") |
|
|
? "tablet" : (style.cssRules[j].media[0] === "screen and (max-width: 320px)") |
|
|
? "mobile" : "desktop"; |
|
|
|
|
|
const selector = (media === "desktop") ? style.cssRules[j].selectorText : style.cssRules[j].cssRules[0].selectorText; |
|
|
const styles = (media === "desktop") ? style.cssRules[j].style : style.cssRules[j].cssRules[0].style; |
|
|
|
|
|
if (media) { |
|
|
this.styles[media] = this.styles[media] ?? {}; |
|
|
if (selector) { |
|
|
this.styles[media][selector] = {}; |
|
|
|
|
|
for (let k = 0; k < styles.length; k++) { |
|
|
|
|
|
const property = styles[k]; |
|
|
const value = styles[property]; |
|
|
|
|
|
this.styles[media][selector][property] = value; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
}, |
|
|
|
|
|
getSelectorForElement: function(element) { |
|
|
if (!element) return ''; |
|
|
|
|
|
let currentElement = element; |
|
|
let selector = []; |
|
|
|
|
|
while (currentElement.parentElement) { |
|
|
let elementSelector = ""; |
|
|
let classSelector = Array.from(currentElement.classList).map(function (className) { |
|
|
if (Vvveb.Builder.ignoreClasses.indexOf(className) == -1) { |
|
|
return "." + className; |
|
|
} |
|
|
}).join(""); |
|
|
|
|
|
|
|
|
let tag = currentElement.tagName.toLowerCase(); |
|
|
|
|
|
if (tag == "body" && selector.length > 1) { |
|
|
break; |
|
|
} |
|
|
|
|
|
|
|
|
if (currentElement.id) { |
|
|
elementSelector = "#" + currentElement.id; |
|
|
selector.push(elementSelector); |
|
|
break; |
|
|
} else if (classSelector) { |
|
|
|
|
|
elementSelector = classSelector; |
|
|
} else { |
|
|
|
|
|
elementSelector = tag |
|
|
} |
|
|
|
|
|
if (elementSelector) { |
|
|
selector.push(elementSelector); |
|
|
} |
|
|
|
|
|
currentElement = currentElement.parentElement; |
|
|
} |
|
|
|
|
|
return selector.reverse().join(" > "); |
|
|
}, |
|
|
|
|
|
setState: function(state) { |
|
|
this.state = state; |
|
|
}, |
|
|
|
|
|
addSelectorState: function(selector) { |
|
|
return selector + (this.state ? ":" + this.state : ""); |
|
|
}, |
|
|
|
|
|
setStyle: function(element, styleProp, value) { |
|
|
let selector; |
|
|
|
|
|
if (typeof(element) == "string") { |
|
|
selector = element; |
|
|
} else { |
|
|
let node = element; |
|
|
|
|
|
|
|
|
|
|
|
if (node.style && node.style[styleProp]) { |
|
|
node.style[styleProp] = value; |
|
|
return element; |
|
|
} |
|
|
|
|
|
selector = this.getSelectorForElement(node); |
|
|
} |
|
|
|
|
|
if (this.inlineCSS) { |
|
|
element.style[styleProp] = value; |
|
|
return element; |
|
|
} |
|
|
|
|
|
selector = this.addSelectorState(selector); |
|
|
|
|
|
const media = document.getElementById("canvas").classList.contains("tablet") ? "tablet" : document.getElementById("canvas").classList.contains("mobile") ? "mobile" : "desktop"; |
|
|
|
|
|
|
|
|
if (!this.styles[media]) { |
|
|
this.styles[media] = {}; |
|
|
} |
|
|
if (!this.styles[media][selector]) { |
|
|
this.styles[media][selector] = {}; |
|
|
} |
|
|
if (!this.styles[media][selector][styleProp]) { |
|
|
this.styles[media][selector][styleProp] = {}; |
|
|
} |
|
|
this.styles[media][selector][styleProp] = value; |
|
|
|
|
|
this.generateCss(media); |
|
|
|
|
|
return element; |
|
|
|
|
|
|
|
|
}, |
|
|
|
|
|
setCss: function (css) { |
|
|
this.cssContainer.innerHTML = css; |
|
|
this.loadCss(); |
|
|
}, |
|
|
|
|
|
getCss: function (css) { |
|
|
return this.cssContainer.innerHTML; |
|
|
}, |
|
|
|
|
|
generateCss: function (media) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.cssContainer = this.doc.getElementById("vvvebjs-styles"); |
|
|
|
|
|
let css = ""; |
|
|
for (media in this.styles) { |
|
|
if (media === "tablet" || media === "mobile") { |
|
|
css += `@media screen and (max-width: ${(media === 'tablet') ? this.tabletWidth : this.mobileWidth}){\n\n` |
|
|
} |
|
|
for (selector in this.styles[media]) { |
|
|
css += `${selector} {\n`; |
|
|
for (property in this.styles[media][selector]) { |
|
|
const value = this.styles[media][selector][property]; |
|
|
css += `\t${property}: ${value};\n`; |
|
|
} |
|
|
css += '}\n\n'; |
|
|
} |
|
|
if (media === "tablet" || media === "mobile") { |
|
|
css += `}\n\n` |
|
|
} |
|
|
} |
|
|
|
|
|
return this.cssContainer.innerHTML = css; |
|
|
}, |
|
|
|
|
|
|
|
|
_getCssStyle: function(element, styleProp){ |
|
|
let value = "", el, selector, media; |
|
|
|
|
|
el = element; |
|
|
if (el != this.currentElement) { |
|
|
selector = this.getSelectorForElement(el); |
|
|
this.currentElement = el; |
|
|
this.currentSelector = selector |
|
|
} else { |
|
|
selector = this.currentSelector; |
|
|
} |
|
|
|
|
|
selector = this.addSelectorState(selector); |
|
|
media = document.getElementById("canvas").classList.contains("tablet") ? "tablet" : document.getElementById("canvas").classList.contains("mobile") ? "mobile" : "desktop"; |
|
|
|
|
|
if (el.style && el.style.length > 0 && el.style[styleProp]) { |
|
|
value = el.style[styleProp]; |
|
|
} else if (this.styles[media] !== undefined && this.styles[media][selector] !== undefined && this.styles[media][selector][styleProp] !== undefined) { |
|
|
value = this.styles[media][selector][styleProp]; |
|
|
|
|
|
if (styleProp == 'font-family') { |
|
|
} |
|
|
} else if (window.getComputedStyle) { |
|
|
value = document.defaultView.getDefaultComputedStyle ? |
|
|
document.defaultView.getDefaultComputedStyle(el,null).getPropertyValue(styleProp) : |
|
|
window.getComputedStyle(el,null).getPropertyValue(styleProp); |
|
|
|
|
|
} |
|
|
|
|
|
return value; |
|
|
}, |
|
|
|
|
|
getStyle: function(element,styleProp){ |
|
|
return this._getCssStyle(element, styleProp); |
|
|
} |
|
|
} |
|
|
|
|
|
Vvveb.ContentManager = { |
|
|
getAttr: function(element, attrName) { |
|
|
return element.getAttribute(attrName); |
|
|
}, |
|
|
|
|
|
setAttr: function(element, attrName, value) { |
|
|
return element.setAttribute(attrName, value); |
|
|
}, |
|
|
|
|
|
setHtml: function(element, html) { |
|
|
return element.innerHTML = html; |
|
|
}, |
|
|
|
|
|
getHtml: function(element) { |
|
|
return element.innerHTML; |
|
|
}, |
|
|
|
|
|
setText: function(element, text) { |
|
|
return element.textContent = text; |
|
|
}, |
|
|
|
|
|
getText: function(element) { |
|
|
return element.textContent; |
|
|
}, |
|
|
}; |
|
|
|
|
|
function getNodeTree (node, parent, allowedComponents, idToNode = {}) { |
|
|
|
|
|
function getNodeTreeTraverse (node, parent, id = '') { |
|
|
|
|
|
if (node.hasChildNodes()) { |
|
|
for (let j = 0; j < node.childNodes.length; j++) { |
|
|
|
|
|
const child = node.childNodes[j]; |
|
|
|
|
|
|
|
|
if (child.nodeType == 3 || child.nodeType == 8) { |
|
|
continue; |
|
|
} |
|
|
|
|
|
let element; |
|
|
if (child && child["attributes"] != undefined && |
|
|
(matchChild = Vvveb.Components.matchNode(child))) { |
|
|
|
|
|
if (Array.isArray(allowedComponents) |
|
|
&& allowedComponents.indexOf(matchChild.type) == -1) { |
|
|
|
|
|
element = getNodeTreeTraverse(child, parent); |
|
|
continue; |
|
|
} |
|
|
|
|
|
element = { |
|
|
name: matchChild.name, |
|
|
image: matchChild.image, |
|
|
type: matchChild.type, |
|
|
node: child, |
|
|
id: id + '-' + j, |
|
|
children: [] |
|
|
}; |
|
|
|
|
|
element.children = []; |
|
|
parent.push(element); |
|
|
idToNode[id + '-' + j] = child; |
|
|
|
|
|
element = getNodeTreeTraverse(child, element.children, id + '-' + j); |
|
|
} else { |
|
|
element = getNodeTreeTraverse(child, parent, id + '-' + j); |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
return false; |
|
|
} |
|
|
|
|
|
getNodeTreeTraverse(node, parent, '1'); |
|
|
} |
|
|
|
|
|
function drawComponentsTree(tree) { |
|
|
let j = 1; |
|
|
let prefix = Math.floor(Math.random() * 100); |
|
|
|
|
|
function drawComponentsTreeTraverse(tree) { |
|
|
let list = document.createElement("ol"); |
|
|
j++; |
|
|
|
|
|
for (i in tree) { |
|
|
let node = tree[i]; |
|
|
let id = node.id; |
|
|
let li; |
|
|
|
|
|
if (!id) { |
|
|
id = prefix + '-' + j + '-' + i; |
|
|
} |
|
|
|
|
|
if (tree[i].children.length > 0) { |
|
|
li = generateElements('<li data-component="' + node.name + '">\ |
|
|
<label for="id' + id + '" style="background-image:url(' + Vvveb.imgBaseUrl + node.image + ')"><span>' + node.name + '</span></label>\ |
|
|
<input type="checkbox" id="id' + id + '">\ |
|
|
</li>')[0]; |
|
|
li.append(drawComponentsTreeTraverse(node.children)); |
|
|
} |
|
|
else { |
|
|
li = generateElements('<li data-component="' + node.name + '" class="file">\ |
|
|
<label for="id' + id + '" style="background-image:url(' + Vvveb.imgBaseUrl + node.image + ')"><span>' + node.name + '</span></label>\ |
|
|
<input type="checkbox" id="id' + id + '">\ |
|
|
</li>')[0]; |
|
|
} |
|
|
|
|
|
li._treeNode = node.node; |
|
|
list.append(li); |
|
|
} |
|
|
|
|
|
return list; |
|
|
} |
|
|
|
|
|
return drawComponentsTreeTraverse(tree); |
|
|
} |
|
|
|
|
|
|
|
|
let selected = null; |
|
|
let dragover = null; |
|
|
|
|
|
Vvveb.SectionList = { |
|
|
|
|
|
selector: '.sections-container', |
|
|
allowedComponents: {}, |
|
|
|
|
|
init: function(allowedComponents = {}) { |
|
|
|
|
|
this.allowedComponents = allowedComponents; |
|
|
|
|
|
document.querySelector(this.selector).addEventListener("click", function (e) { |
|
|
let element = e.target.closest(":scope > div .controls"); |
|
|
if (element) { |
|
|
let node = element.parentNode._node; |
|
|
if (node) { |
|
|
node.scrollIntoView({behavior: "smooth", block: "center", inline: "center"}); |
|
|
|
|
|
Vvveb.Builder.selectNode(node); |
|
|
Vvveb.Builder.loadNodeComponent(node); |
|
|
} |
|
|
} |
|
|
}); |
|
|
|
|
|
document.querySelector(this.selector).addEventListener("dblclick", function (e) { |
|
|
let element = e.target.closest(":scope > div"); |
|
|
if (element) { |
|
|
const node = element._node; |
|
|
node.click(); |
|
|
} |
|
|
}); |
|
|
|
|
|
|
|
|
document.querySelector(this.selector).addEventListener("click", function (e) { |
|
|
let element = e.target.closest("li[data-component] label"); |
|
|
if (element) { |
|
|
let node = element.parentNode._node; |
|
|
if (node) { |
|
|
node.scrollIntoView({behavior: "smooth", block: "center", inline: "center"}); |
|
|
node.click(); |
|
|
} |
|
|
} |
|
|
}); |
|
|
|
|
|
document.querySelector(this.selector).addEventListener("mouseenter", function (e) { |
|
|
let element = e.target.closest("li[data-component] label"); |
|
|
if (element) { |
|
|
const node = document.querySelector(element.parentNode._node); |
|
|
node.css("outline","1px dashed blue"); |
|
|
} |
|
|
}); |
|
|
|
|
|
document.querySelector(this.selector).addEventListener("mouseleave", function (e){ |
|
|
let element = e.target.closest("li[data-component] label"); |
|
|
if (element) { |
|
|
const node = document.querySelector(element.parentNode._node); |
|
|
node.css("outline",""); |
|
|
if (node.getAttribute("style") == "") node.removeAttribute("style"); |
|
|
} |
|
|
}); |
|
|
|
|
|
document.querySelector(this.selector).addEventListener("dragstart", this.dragStart); |
|
|
document.querySelector(this.selector).addEventListener("dragover", this.dragOver); |
|
|
document.querySelector(this.selector).addEventListener("dragend", this.dragEnd); |
|
|
|
|
|
document.querySelector(this.selector).addEventListener("click", function (e) { |
|
|
let element = e.target.closest(".delete-btn"); |
|
|
if (element) { |
|
|
let section = element.closest(".section-item"); |
|
|
let node = section._node; |
|
|
node.remove(); |
|
|
section.remove(); |
|
|
Vvveb.TreeList.loadComponents(); |
|
|
|
|
|
e.stopPropagation(); |
|
|
e.preventDefault(); |
|
|
} |
|
|
}); |
|
|
|
|
|
let sectionIn; |
|
|
let img = document.querySelector(".block-preview img"); |
|
|
document.querySelector(".sections-list").addEventListener("mouseover", function (e) { |
|
|
let element = e.target.closest("li[data-type]"); |
|
|
if (element) { |
|
|
if (sectionIn != element) { |
|
|
let src = element.querySelector("img").getAttribute("src"); |
|
|
sectionIn = element; |
|
|
img.setAttribute("src", src); |
|
|
img.style.display = ""; |
|
|
} |
|
|
} else { |
|
|
sectionIn = element; |
|
|
img.setAttribute("src", ""); |
|
|
img.style.display = "none"; |
|
|
} |
|
|
}) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let self = this; |
|
|
document.querySelector(".sections-list").addEventListener("click", function (e) { |
|
|
let element = e.target.closest(".add-section-btn"); |
|
|
if (element) { |
|
|
let item = element.closest("li"); |
|
|
let section = Vvveb.Sections.get(item.dataset.type); |
|
|
let node = generateElements(section.html)[0]; |
|
|
let sectionType = node.tagName.toLowerCase(); |
|
|
let afterSection = Vvveb.Builder.frameBody.querySelector(":scope > " + sectionType + ":last-of-type"); |
|
|
|
|
|
if (afterSection) { |
|
|
afterSection.after(node); |
|
|
} else { |
|
|
if (sectionType == "nav") { |
|
|
afterSection = Vvveb.Builder.frameBody.querySelector(":scope > nav:first,> header:last-of-type"); |
|
|
|
|
|
if (afterSection) { |
|
|
afterSection.before(node); |
|
|
} else { |
|
|
Vvveb.Builder.frameBody.append(node); |
|
|
} |
|
|
} else if (sectionType != "footer") { |
|
|
afterSection = Vvveb.Builder.frameBody.querySelector("body > footer:last-of-type"); |
|
|
|
|
|
if (afterSection) { |
|
|
afterSection.before(node); |
|
|
} else { |
|
|
Vvveb.Builder.frameBody.append(node); |
|
|
} |
|
|
} else { |
|
|
Vvveb.Builder.frameBody.append(node); |
|
|
} |
|
|
} |
|
|
|
|
|
node.scrollIntoView({behavior: "smooth", block: "center", inline: "center"}); |
|
|
|
|
|
Vvveb.Builder.selectNode(node); |
|
|
Vvveb.Builder.loadNodeComponent(node); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Vvveb.Undo.addMutation({type: 'childList', |
|
|
target: node.parentNode, |
|
|
addedNodes: [node], |
|
|
nextSibling: node.nextSibling}); |
|
|
|
|
|
|
|
|
self.loadSections(); |
|
|
Vvveb.TreeList.loadComponents(); |
|
|
Vvveb.TreeList.selectComponent(node); |
|
|
|
|
|
e.preventDefault(); |
|
|
} |
|
|
}); |
|
|
|
|
|
document.querySelector(this.selector).addEventListener("click", function (e) { |
|
|
let element = e.target.closest(".properties-btn"); |
|
|
if (element) { |
|
|
let section = element.closest(".section-item"); |
|
|
let node = section._node; |
|
|
node.click(); |
|
|
|
|
|
e.preventDefault(); |
|
|
} |
|
|
}); |
|
|
|
|
|
}, |
|
|
|
|
|
getSections: function() { |
|
|
let sections = []; |
|
|
let sectionList = |
|
|
window.FrameDocument.body.querySelectorAll(':scope > section, :scope > header, :scope > footer, :scope > main, :scope > nav'); |
|
|
|
|
|
sectionList.forEach(function (node, i) { |
|
|
let id = node.id ? node.id : (node.title ? node.title : node.ariaLabel ?? node.className); |
|
|
if (!id) { |
|
|
id = 'section-' + Math.floor(Math.random() * 10000); |
|
|
} |
|
|
let section = { |
|
|
name: id.replace(/[^\w+]+/g,' '), |
|
|
id: node.id, |
|
|
type: node.tagName.toLowerCase(), |
|
|
node: node |
|
|
}; |
|
|
sections.push(section); |
|
|
}); |
|
|
|
|
|
return sections; |
|
|
}, |
|
|
|
|
|
loadComponents: function(sectionListItem, section, allowedComponents = {}) { |
|
|
|
|
|
let tree = []; |
|
|
getNodeTree(section, tree, allowedComponents); |
|
|
|
|
|
let html = drawComponentsTree(tree); |
|
|
document.querySelector("ol", sectionListItem).replaceWith(html); |
|
|
}, |
|
|
|
|
|
|
|
|
addSection: function(data) { |
|
|
let section = generateElements(tmpl("vvveb-section", data))[0]; |
|
|
section._node = data.node; |
|
|
document.querySelector(this.selector).append(section); |
|
|
|
|
|
|
|
|
}, |
|
|
|
|
|
loadSections: function() { |
|
|
let sections = this.getSections(); |
|
|
let container = document.querySelector(this.selector); |
|
|
|
|
|
container.replaceChildren(); |
|
|
for (i in sections) { |
|
|
this.addSection(sections[i]); |
|
|
} |
|
|
|
|
|
}, |
|
|
|
|
|
|
|
|
dragOver: function(e) { |
|
|
let element = e.target.closest("div"); |
|
|
if (element) { |
|
|
if (e.target != dragover && |
|
|
e.target.className == "section-item") { |
|
|
|
|
|
if (dragover) { |
|
|
dragover.classList.remove("drag-over"); |
|
|
} |
|
|
|
|
|
const dragover = e.target; |
|
|
dragover.classList.add("drag-over"); |
|
|
} |
|
|
} |
|
|
}, |
|
|
|
|
|
dragEnd: function (e) { |
|
|
let element = e.target.closest("div"); |
|
|
if (element) { |
|
|
|
|
|
if (dragover) { |
|
|
let parent = selected.parentNode; |
|
|
let selectedNode = selected._node; |
|
|
let replaceNode = dragover._node; |
|
|
|
|
|
if ((dragover.offsetTop > selected.offsetTop)) { |
|
|
|
|
|
parent.insertBefore(selected, dragover.nextElementSibling); |
|
|
|
|
|
replaceNode.parentNode.insertBefore(selectedNode, replaceNode.nextElementSibling); |
|
|
} else { |
|
|
|
|
|
parent.insertBefore(selected, dragover); |
|
|
|
|
|
replaceNode.parentNode.insertBefore(selectedNode, replaceNode); |
|
|
} |
|
|
|
|
|
dragover.classList.remove("drag-over"); |
|
|
|
|
|
let node = selectedNode; |
|
|
|
|
|
Vvveb.Undo.addMutation({type: 'move', |
|
|
target: node, |
|
|
oldParent: node.parentNode, |
|
|
oldNextSibling: node.nextSibling}); |
|
|
} |
|
|
|
|
|
selected = null; |
|
|
dragover = null; |
|
|
} |
|
|
}, |
|
|
|
|
|
dragStart: function (e) { |
|
|
let element = e.target.closest("div"); |
|
|
if (element) { |
|
|
selected = e.target |
|
|
} |
|
|
}, |
|
|
} |
|
|
|
|
|
Vvveb.TreeList = { |
|
|
selector: '#tree-list', |
|
|
|
|
|
container: null, |
|
|
|
|
|
tree: [], |
|
|
|
|
|
idToNode : {}, |
|
|
|
|
|
init: function() { |
|
|
|
|
|
this.container = document.querySelector(this.selector); |
|
|
let header = this.container.querySelector(".header"); |
|
|
let isDown = false; |
|
|
let offset = [0,0]; |
|
|
let self = this; |
|
|
|
|
|
header.addEventListener('mousedown', function(e) { |
|
|
if (e.which == 1) { |
|
|
isDown = true; |
|
|
offset = [ |
|
|
self.container.offsetLeft - e.clientX, |
|
|
self.container.offsetTop - e.clientY |
|
|
]; |
|
|
} |
|
|
}, true); |
|
|
|
|
|
document.addEventListener('mouseup', function() { |
|
|
isDown = false; |
|
|
}, true); |
|
|
|
|
|
document.addEventListener('mousemove', function(event) { |
|
|
if (isDown) { |
|
|
event.preventDefault(); |
|
|
let left = Math.max(event.clientX + offset[0], 0); |
|
|
let top = Math.max(event.clientY + offset[1], 0); |
|
|
|
|
|
if (left >= 0 && (left < (window.innerWidth - self.container.clientWidth))) self.container.style.left = left + "px"; |
|
|
if (top >= 0 && (top < (window.innerHeight - self.container.clientHeight))) self.container.style.top = top + "px"; |
|
|
} |
|
|
}); |
|
|
|
|
|
document.querySelector(this.selector).addEventListener("click", function (e) { |
|
|
let element = e.target.closest("li[data-component]"); |
|
|
if (element) { |
|
|
const node = element._treeNode; |
|
|
node.scrollIntoView({behavior: "smooth", block: "center", inline: "center"}); |
|
|
|
|
|
Vvveb.Builder.selectNode(node); |
|
|
Vvveb.Builder.loadNodeComponent(node); |
|
|
|
|
|
document.querySelector(self.selector + " .active")?.classList.remove("active"); |
|
|
element.querySelector("label").classList.add("active"); |
|
|
} |
|
|
}) |
|
|
|
|
|
document.querySelector(this.selector).addEventListener("mousemove", function (e) { |
|
|
let element = e.target.closest("li[data-component]"); |
|
|
if (element) { |
|
|
const node = element._treeNode; |
|
|
|
|
|
node.dispatchEvent(new MouseEvent("mousemove", { |
|
|
bubbles: true, |
|
|
cancelable: true, |
|
|
})); |
|
|
} |
|
|
}) |
|
|
}, |
|
|
|
|
|
selectComponent: function(node) { |
|
|
let id; |
|
|
for (const i in this.idToNode) { |
|
|
if (node == this.idToNode[i]) { |
|
|
id = i; |
|
|
break; |
|
|
} |
|
|
} |
|
|
|
|
|
if (id) { |
|
|
let element = document.getElementById("id" + id); |
|
|
|
|
|
this.container.querySelector(".active")?.classList.remove("active"); |
|
|
|
|
|
let checkboxes = this.container.querySelectorAll("input[type=checkbox]:checked"); |
|
|
for (let i = 0, len = checkboxes.length; i < len; i++) { |
|
|
checkboxes[i].checked = false; |
|
|
let label = checkboxes[i].labels[0]; |
|
|
if (label) { |
|
|
label.classList.remove("active"); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (element) { |
|
|
let parent = element; |
|
|
let current = element; |
|
|
while (parent = current.closest("li")) { |
|
|
current = parent.parentNode; |
|
|
let input = parent.querySelector("input"); |
|
|
if (input && input.hasAttribute("type") && input.type == "checkbox") { |
|
|
input.checked = true; |
|
|
} |
|
|
} |
|
|
|
|
|
element.checked = true; |
|
|
element.labels[0].classList.add("active"); |
|
|
element.scrollIntoView({behavior: "smooth", block: "center", inline: "center"}); |
|
|
} |
|
|
} |
|
|
|
|
|
return false; |
|
|
}, |
|
|
|
|
|
loadComponents: function() { |
|
|
let list = this.container.querySelector(".tree > ol"); |
|
|
|
|
|
if (list.offsetParent === null) return; |
|
|
|
|
|
this.tree = []; |
|
|
this.idToNode = {}; |
|
|
getNodeTree(window.FrameDocument.body, this.tree, {}, this.idToNode); |
|
|
|
|
|
let ol = drawComponentsTree(this.tree); |
|
|
list.replaceWith(ol); |
|
|
|
|
|
}, |
|
|
} |
|
|
|
|
|
Vvveb.FileManager = { |
|
|
tree:false, |
|
|
pages:{}, |
|
|
currentPage: false, |
|
|
allowedComponents: {}, |
|
|
|
|
|
init: function(allowedComponents = {}) { |
|
|
|
|
|
this.allowedComponents = allowedComponents; |
|
|
this.tree = document.querySelector("#filemanager .tree > ol"); |
|
|
this.tree.replaceChildren(); |
|
|
|
|
|
this.tree.addEventListener("click", function (e) { |
|
|
let element = event.target.closest("a"); |
|
|
if (element) { |
|
|
e.stopImmediatePropagation(); |
|
|
if (element.classList.contains('view')) return; |
|
|
e.preventDefault(); |
|
|
return false; |
|
|
} |
|
|
}); |
|
|
|
|
|
this.tree.addEventListener("click", function (e) { |
|
|
let element = event.target.closest(".delete"); |
|
|
if (element) { |
|
|
Vvveb.FileManager.deletePage(element.closest("li"), e); |
|
|
e.stopImmediatePropagation(); |
|
|
e.preventDefault(); |
|
|
return false; |
|
|
} |
|
|
}); |
|
|
|
|
|
this.tree.addEventListener("click", function (e) { |
|
|
let element = event.target.closest(".rename"); |
|
|
if (element) { |
|
|
Vvveb.FileManager.renamePage(element.closest("li"), e, false); |
|
|
e.stopImmediatePropagation(); |
|
|
e.preventDefault(); |
|
|
return false; |
|
|
} |
|
|
}); |
|
|
|
|
|
this.tree.addEventListener("click", function (e) { |
|
|
let element = event.target.closest(".duplicate"); |
|
|
if (element) { |
|
|
Vvveb.FileManager.renamePage(element.closest("li"), e, true); |
|
|
e.stopImmediatePropagation(); |
|
|
e.preventDefault(); |
|
|
return false; |
|
|
} |
|
|
}); |
|
|
|
|
|
this.tree.addEventListener("click", function (e) { |
|
|
let element = event.target.closest("li[data-page] label"); |
|
|
if (element) { |
|
|
let page = element.parentNode.dataset.page; |
|
|
if (page) Vvveb.FileManager.loadPage(page, allowedComponents); |
|
|
e.preventDefault(); |
|
|
return false; |
|
|
} |
|
|
}); |
|
|
|
|
|
this.tree.addEventListener("click", function (e) { |
|
|
let element = event.target.closest("li[data-component] label"); |
|
|
if (element) { |
|
|
const node = e.currentTarget.parentNode._node; |
|
|
node.scrollIntoView({behavior: "smooth", block: "center", inline: "center"}); |
|
|
node.click(); |
|
|
} |
|
|
}); |
|
|
|
|
|
this.tree.addEventListener("mouseenter", function (e) { |
|
|
let element = event.target.closest("li[data-component] label"); |
|
|
if (element) { |
|
|
const node = e.currentTarget.parentNode._node; |
|
|
node.scrollIntoView({behavior: "smooth", block: "center", inline: "center"}); |
|
|
|
|
|
node.dispatchEvent(new MouseEvent("mousemove", { |
|
|
bubbles: true, |
|
|
cancelable: true, |
|
|
})); |
|
|
|
|
|
} |
|
|
}); |
|
|
}, |
|
|
|
|
|
clear: function() { |
|
|
this.pages = {}; |
|
|
this.currentPage = false; |
|
|
this.tree.replaceChildren(); |
|
|
}, |
|
|
|
|
|
deletePage: function(element, e) { |
|
|
let page = element.dataset; |
|
|
if (confirm(`Are you sure you want to delete "${page.file}"template?`)) { |
|
|
|
|
|
let detail = {page, element}; |
|
|
|
|
|
window.dispatchEvent(new CustomEvent("vvveb.FileManager.deletePage", { |
|
|
detail |
|
|
})); |
|
|
|
|
|
if (detail.page) { |
|
|
|
|
|
fetch(deleteUrl, {method: "POST", body: new URLSearchParams({file:page.file})}) |
|
|
.then((response) => { |
|
|
if (!response.ok) { return Promise.reject(response); } |
|
|
return response.text() |
|
|
}) |
|
|
.then((data) => { |
|
|
let bg = "bg-success"; |
|
|
if (data.success) { |
|
|
document.querySelectorAll("#top-panel .save-btn").forEach(e => e.setAttribute("disabled", "true")); |
|
|
} else { |
|
|
bg = "bg-danger"; |
|
|
} |
|
|
|
|
|
displayToast(bg, "Delete", data.message ?? data); |
|
|
}) |
|
|
.catch(error => { |
|
|
console.log(error); |
|
|
let message = error.statusText ?? "Error deleting page!"; |
|
|
displayToast("bg-danger", "Error", message); |
|
|
|
|
|
err.text().then( errorMessage => { |
|
|
let message = errorMessage.substr(0, 200); |
|
|
displayToast("bg-danger", "Error", message); |
|
|
}) |
|
|
}); |
|
|
|
|
|
element.remove(); |
|
|
} |
|
|
} |
|
|
}, |
|
|
|
|
|
renamePage: function(element, e, duplicate = false) { |
|
|
let page = element.dataset; |
|
|
let newfile = prompt(`Enter new file name for "${page.file}"`, page.file); |
|
|
let _self = this; |
|
|
if (newfile) { |
|
|
|
|
|
let detail = {page, newfile, element}; |
|
|
|
|
|
window.dispatchEvent(new CustomEvent("vvveb.FileManager.renamePage", { |
|
|
detail |
|
|
})); |
|
|
|
|
|
if (detail.page) { |
|
|
|
|
|
fetch(renameUrl, {method: "POST", body: new URLSearchParams({file:page.file, newfile:newfile, duplicate})}) |
|
|
.then((response) => { |
|
|
if (!response.ok) { return Promise.reject(response); } |
|
|
return response.text() |
|
|
}) |
|
|
.then((data) => { |
|
|
let bg = "bg-success"; |
|
|
if (data.success) { |
|
|
document.querySelectorAll("#top-panel .save-btn").forEach(e => e.setAttribute("disabled", "true")); |
|
|
} else { |
|
|
bg = "bg-danger"; |
|
|
} |
|
|
|
|
|
newfile = data.newfile ?? newfile; |
|
|
displayToast(bg, "Rename", data.message ?? data); |
|
|
let baseName = newfile.replace('.html', ''); |
|
|
let newName = friendlyName(newfile.replace(/.*[\/\\]+/, '')).replace('.html', ''); |
|
|
|
|
|
if (duplicate) { |
|
|
let data = _self.pages[page.page]; |
|
|
data["file"] = newfile; |
|
|
data["title"] = newName; |
|
|
Vvveb.FileManager.addPage(baseName, data); |
|
|
} else { |
|
|
_self.pages[page.page]["file"] = newfile; |
|
|
_self.pages[page.page]["title"] = newName; |
|
|
page.url = data.url ?? page.url.replace(page.file, newfile); |
|
|
page.file = newfile; |
|
|
element.querySelector(":scope > label span").innerHTML = newName; |
|
|
element.querySelector(":scope > label a.view").setAttribute("href", page.url); |
|
|
_self.pages[page.page]["url"] = page.url; |
|
|
_self.pages[page.page]["file"] = page.file; |
|
|
} |
|
|
}) |
|
|
.catch(error => { |
|
|
console.log(error); |
|
|
let message = error.statusText ?? "Error renaming page!"; |
|
|
displayToast("bg-danger", "Error", message); |
|
|
|
|
|
error.text().then( errorMessage => { |
|
|
let message = errorMessage.substr(0, 200); |
|
|
displayToast("bg-danger", "Error", message); |
|
|
}) |
|
|
}); |
|
|
} |
|
|
} |
|
|
}, |
|
|
|
|
|
addPage: function(name, data, afterPage = false) { |
|
|
|
|
|
|
|
|
window.dispatchEvent(new CustomEvent("vvveb.FileManager.addPage", { |
|
|
detail: [name, data], |
|
|
})); |
|
|
|
|
|
if (!name) { |
|
|
return false; |
|
|
} |
|
|
|
|
|
this.pages[name] = data; |
|
|
data['name'] = name; |
|
|
|
|
|
let folder = this.tree; |
|
|
if (data.folder) { |
|
|
if ((data.folder && data.folder != "/") && !(folder = folder.querySelector('li[data-folder="' + data.folder + '"]'))) { |
|
|
data.folderTitle = friendlyName(data.folder); |
|
|
folder = generateElements(tmpl("vvveb-filemanager-folder", data))[0]; |
|
|
this.tree.append(folder); |
|
|
} |
|
|
|
|
|
folder = folder.querySelector("ol"); |
|
|
} |
|
|
|
|
|
let page = generateElements(tmpl("vvveb-filemanager-page", data))[0]; |
|
|
if (afterPage && (afterPage = folder.querySelector('[data-page="' + afterPage + '"]'))) { |
|
|
afterPage.after(page); |
|
|
} else { |
|
|
folder.append(page); |
|
|
} |
|
|
|
|
|
return page; |
|
|
}, |
|
|
|
|
|
addPages: function(pages) { |
|
|
for (page in pages) { |
|
|
this.addPage(pages[page]['name'], pages[page]); |
|
|
} |
|
|
}, |
|
|
|
|
|
addComponent: function(name, url, title, page) { |
|
|
document.querySelector("[data-page='" + page + "'] > ol", this.tree).append( |
|
|
tmpl("vvveb-filemanager-component", {name:name, url:url, title:title})); |
|
|
}, |
|
|
|
|
|
loadComponents: function(allowedComponents = {}) { |
|
|
|
|
|
let tree = []; |
|
|
getNodeTree(window.FrameDocument.body, tree, allowedComponents); |
|
|
|
|
|
let html = drawComponentsTree(tree); |
|
|
document.querySelector("[data-page='" + this.currentPage + "'] > ol", this.tree).replaceWith(html); |
|
|
}, |
|
|
|
|
|
getCurrentUrl: function() { |
|
|
if (this.currentPage) { |
|
|
return this.pages[this.currentPage]['url']; |
|
|
} |
|
|
}, |
|
|
|
|
|
getCurrentPage: function() { |
|
|
return this.currentPage; |
|
|
}, |
|
|
|
|
|
getPageData: function(key) { |
|
|
if (this.currentPage) { |
|
|
return this.pages[this.currentPage][key]; |
|
|
} |
|
|
}, |
|
|
|
|
|
|
|
|
getCurrentFileName: function() { |
|
|
if (this.currentPage) { |
|
|
let folder = this.pages[this.currentPage]['folder']; |
|
|
folder = folder ? folder + '/': ''; |
|
|
return folder + this.pages[this.currentPage]['file']; |
|
|
} |
|
|
}, |
|
|
|
|
|
reloadCurrentPage: function() { |
|
|
if (this.currentPage) |
|
|
return this.loadPage(this.currentPage); |
|
|
}, |
|
|
|
|
|
loadPage: function(name, allowedComponents = false, disableCache = true, loadComponents = false) { |
|
|
let url = this.pages[name]['url'] ?? ""; |
|
|
|
|
|
if (!url) { |
|
|
return; |
|
|
} |
|
|
|
|
|
let page = this.tree.querySelector("[data-page='" + name + "']"); |
|
|
|
|
|
this.tree.querySelector("[data-page].active")?.classList.remove("active"); |
|
|
|
|
|
page.classList.add("active"); |
|
|
|
|
|
page.closest("[data-folder]")?.querySelector("input[type=checkbox]").setAttribute("checked", true); |
|
|
|
|
|
this.currentPage = name; |
|
|
document.querySelector(".btn-preview-url").setAttribute("href", url); |
|
|
|
|
|
|
|
|
let self = this; |
|
|
|
|
|
window.dispatchEvent(new CustomEvent("vvveb.FileManager.loadPage", { |
|
|
detail: self.pages[name], |
|
|
})); |
|
|
|
|
|
if (url) { |
|
|
Vvveb.Builder.loadUrl(url + (disableCache ? (url.indexOf('?') > -1 ? '&r=':'?r=') + Math.random():''), |
|
|
function () { |
|
|
if (loadComponents) { Vvveb.FileManager.loadComponents(allowedComponents); } |
|
|
Vvveb.SectionList.loadSections(allowedComponents); |
|
|
Vvveb.TreeList.loadComponents(); |
|
|
Vvveb.StyleManager.init(); |
|
|
}); |
|
|
} |
|
|
}, |
|
|
|
|
|
scrollToPage: function(page) { |
|
|
page.scrollIntoView({behavior: "smooth", block: "center", inline: "center"}); |
|
|
}, |
|
|
} |
|
|
|
|
|
Vvveb.Breadcrumb = { |
|
|
tree:false, |
|
|
|
|
|
init: function() { |
|
|
this.tree = document.querySelector(".breadcrumb-navigator > .breadcrumb"); |
|
|
this.tree.replaceChildren(); |
|
|
|
|
|
this.tree.addEventListener("click", function (e) { |
|
|
let element = event.target.closest(".breadcrumb-item"); |
|
|
if (element) { |
|
|
let node = element._node; |
|
|
if (node) { |
|
|
|
|
|
Vvveb.Builder.selectNode(node); |
|
|
Vvveb.Builder.loadNodeComponent(node); |
|
|
node.scrollIntoView({behavior: "smooth", block: "center", inline: "center"}); |
|
|
} |
|
|
|
|
|
e.preventDefault(); |
|
|
} |
|
|
}); |
|
|
|
|
|
let currentHoverNode; |
|
|
this.tree.addEventListener("mousemove", function (e) { |
|
|
if (event.target == currentHoverNode) return; |
|
|
currentHoverNode = event.target; |
|
|
|
|
|
let element = event.target.closest(".breadcrumb-item"); |
|
|
if (element) { |
|
|
let node = element._node; |
|
|
|
|
|
node.dispatchEvent(new MouseEvent("mousemove", { |
|
|
bubbles: true, |
|
|
cancelable: true, |
|
|
})); |
|
|
} |
|
|
}) |
|
|
}, |
|
|
|
|
|
addElement: function(data, element) { |
|
|
let li = generateElements(tmpl("vvveb-breadcrumb-navigaton-item", data))[0]; |
|
|
li._node = element; |
|
|
this.tree.prepend(li); |
|
|
}, |
|
|
|
|
|
loadBreadcrumb: function(element) { |
|
|
this.tree.replaceChildren(); |
|
|
let currentElement = element; |
|
|
|
|
|
while (currentElement.parentElement) { |
|
|
let elementType = Vvveb.Builder._getElementType(currentElement); |
|
|
let el = elementType[1].toLowerCase(); |
|
|
|
|
|
this.addElement({ |
|
|
"name": el + " " + elementType[0], |
|
|
"className": "el-" + el |
|
|
}, currentElement); |
|
|
|
|
|
currentElement = currentElement.parentElement; |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
Vvveb.FontsManager = { |
|
|
|
|
|
activeFonts:[], |
|
|
providers: {}, |
|
|
|
|
|
addFontList: function(provider, groupName, fontList) { |
|
|
let fonts = {}; |
|
|
let fontNames = []; |
|
|
|
|
|
let fontSelect = generateElements("<optgroup label='" + groupName + "'></optgroup>")[0]; |
|
|
for (const font in fontList) { |
|
|
fontNames.push({"text":font, "value":font, "data-provider": provider}); |
|
|
let option = new Option(font, font); |
|
|
option.dataset.provider = provider; |
|
|
|
|
|
fontSelect.append(option); |
|
|
} |
|
|
document.getElementById("font-family").append(fontSelect); |
|
|
|
|
|
let list = Vvveb.Components.getProperty("_base", "font-family"); |
|
|
if (list) { |
|
|
list.onChange = function (node,value, input, component) { |
|
|
let option = input.options[input.selectedIndex]; |
|
|
Vvveb.FontsManager.addFont(option.dataset.provider, value, node); |
|
|
return node; |
|
|
}; |
|
|
|
|
|
list.data.options.push({optgroup:groupName}); |
|
|
list.data.options = list.data.options.concat(fontNames); |
|
|
|
|
|
Vvveb.Components.updateProperty("_base", "font-family", {data:list.data}); |
|
|
|
|
|
|
|
|
fontList = list.data.options; |
|
|
} |
|
|
}, |
|
|
|
|
|
addProvider: function(provider, Obj) { |
|
|
this.providers[provider] = Obj; |
|
|
}, |
|
|
|
|
|
|
|
|
addFont: function(provider, fontFamily, element = false) { |
|
|
if (!provider) return; |
|
|
|
|
|
let providerObj = this.providers[provider]; |
|
|
if (providerObj) { |
|
|
providerObj.addFont(fontFamily); |
|
|
this.activeFonts.push({provider, fontFamily, element}); |
|
|
} |
|
|
}, |
|
|
|
|
|
removeFont: function(provider, fontFamily) { |
|
|
if (!provider) return; |
|
|
|
|
|
let providerObj = this.providers[provider]; |
|
|
if (provider!= "default" && providerObj) { |
|
|
providerObj.removeFont(fontFamily); |
|
|
} |
|
|
}, |
|
|
|
|
|
|
|
|
cleanUnusedFonts: function (){ |
|
|
for (i in this.activeFonts) { |
|
|
let elementFont = this.activeFonts[i]; |
|
|
if (elementFont.element) { |
|
|
if (Vvveb.StyleManager.getStyle(elementFont.element,'font-family').replaceAll('"','') != elementFont.fontFamily) { |
|
|
this.removeFont(elementFont.provider, elementFont.fontFamily); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
}; |
|
|
|
|
|
|
|
|
Vvveb.ColorPalette = { |
|
|
colors: {}, |
|
|
|
|
|
getAll: function() { |
|
|
return this.colors; |
|
|
}, |
|
|
|
|
|
add: function(name, color) { |
|
|
this.colors[name] = color; |
|
|
}, |
|
|
|
|
|
remove: function(color) { |
|
|
delete this.colors[name]; |
|
|
}, |
|
|
} |
|
|
|
|
|
function friendlyName(name) { |
|
|
name = name.replaceAll("--bs-","").replace(/[-_]/g, " ").trim(); |
|
|
return name = name[0].toUpperCase() + name.slice(1); |
|
|
} |
|
|
|
|
|
Vvveb.ColorPaletteManager = { |
|
|
|
|
|
cssVars: {"font": {}, "color" : {}, "dimensions": {}}, |
|
|
|
|
|
getType: function (type) { |
|
|
return this.cssVars[type]; |
|
|
}, |
|
|
|
|
|
getAllCSSVariableNames: function (styleSheets = document.styleSheets, selector){ |
|
|
|
|
|
for(let i = 0; i < styleSheets.length; i++){ |
|
|
try{ |
|
|
let cssRules = styleSheets[i].cssRules; |
|
|
for( let j = 0; j < cssRules.length; j++){ |
|
|
try{ |
|
|
let style = cssRules[j].style; |
|
|
if (selector && cssRules[j].selectorText && cssRules[j].selectorText != selector) continue; |
|
|
for(let k = 0; k < style.length; k++){ |
|
|
let name = style[k]; |
|
|
let value = style.getPropertyValue(name).trim(); |
|
|
let type = ""; |
|
|
|
|
|
if(name.startsWith("--")){ |
|
|
|
|
|
if (name.endsWith("-rgb")) continue; |
|
|
|
|
|
if (value.startsWith("var(")) continue; |
|
|
|
|
|
let friendlyName = name.replace("--bs-","").replaceAll("-", " "); |
|
|
|
|
|
if (value.startsWith("#")) { |
|
|
type = "color"; |
|
|
} else if (value.indexOf('"') >= 0 || value.indexOf("'") >= 0) { |
|
|
type = "font"; |
|
|
} else if (value.endsWith('em') > 0 || value.endsWith('px') > 0) { |
|
|
type = "dimensions"; |
|
|
} else if (!isNaN(parseFloat(value))) { |
|
|
type = "dimensions"; |
|
|
} |
|
|
|
|
|
if (type) { |
|
|
if (!this.cssVars[type]) this.cssVars[type] = {}; |
|
|
this.cssVars[type][name] = {value, type, friendlyName}; |
|
|
} |
|
|
} |
|
|
} |
|
|
} catch (error) {} |
|
|
} |
|
|
} catch (error) {} |
|
|
} |
|
|
|
|
|
return this.cssVars; |
|
|
}, |
|
|
|
|
|
getCssWithVars: function (styleSheets = document.styleSheets, vars){ |
|
|
let cssVars = {}; |
|
|
let css = ""; |
|
|
let cssStyles = ""; |
|
|
for(let i = 0; i < styleSheets.length; i++){ |
|
|
try{ |
|
|
let cssRules = styleSheets[i].cssRules; |
|
|
for( let j = 0; j < cssRules.length; j++){ |
|
|
try{ |
|
|
let style = cssRules[j].style; |
|
|
|
|
|
cssStyles = ""; |
|
|
for(let k = 0; k < style.length; k++){ |
|
|
let name = style[k]; |
|
|
let value = style.getPropertyValue(name); |
|
|
if(name.startsWith('--bs-btn-')) { |
|
|
for (v in vars) { |
|
|
if (value == vars[v]) { |
|
|
cssVars[name] = v; |
|
|
cssStyles += name + ":var(" + v + ");\n"; |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
} |
|
|
if (cssStyles) { |
|
|
css += cssRules[j].selectorText + "{\n" |
|
|
css += cssStyles; |
|
|
css += "}\n"; |
|
|
} |
|
|
} catch (error) {} |
|
|
} |
|
|
} catch (error) {} |
|
|
} |
|
|
return cssVars; |
|
|
}, |
|
|
|
|
|
init: function(document) { |
|
|
Vvveb.Components.render("config/bootstrap", "#configuration .component-properties"); |
|
|
|
|
|
|
|
|
|
|
|
let colors = this.cssVars.color; |
|
|
for (const name in colors) { |
|
|
let color = colors[name].value; |
|
|
|
|
|
if (color[0] == "#" && color.length == 7) { |
|
|
|
|
|
Vvveb.ColorPalette.add(color, color); |
|
|
} |
|
|
} |
|
|
}, |
|
|
|
|
|
}; |
|
|
|
|
|
Vvveb.Config = { |
|
|
components :[], |
|
|
blocks :[], |
|
|
plugins :[], |
|
|
|
|
|
load: function(url = 'default.json') { |
|
|
$.getJSON( url, function( data ) { |
|
|
|
|
|
}); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
function launchFullScreen(document) { |
|
|
if(document.documentElement.requestFullScreen) { |
|
|
|
|
|
if (document.FullScreenElement) |
|
|
document.exitFullScreen(); |
|
|
else |
|
|
document.documentElement.requestFullScreen(); |
|
|
|
|
|
} else if(document.documentElement.mozRequestFullScreen) { |
|
|
|
|
|
if (document.mozFullScreenElement) |
|
|
document.mozCancelFullScreen(); |
|
|
else |
|
|
document.documentElement.mozRequestFullScreen(); |
|
|
|
|
|
} else if(document.documentElement.webkitRequestFullScreen) { |
|
|
|
|
|
if (document.webkitFullscreenElement) |
|
|
document.webkitExitFullscreen(); |
|
|
else |
|
|
document.documentElement.webkitRequestFullScreen(); |
|
|
|
|
|
} else if(document.documentElement.msRequestFullscreen) { |
|
|
|
|
|
if (document.msFullScreenElement) |
|
|
document.msExitFullscreen(); |
|
|
else |
|
|
document.documentElement.msRequestFullscreen(); |
|
|
} |
|
|
} |
|
|
|
|
|
let fontList = [{ |
|
|
value: "", |
|
|
text: "Default" |
|
|
}, { |
|
|
value: "Arial, Helvetica, sans-serif", |
|
|
text: "Arial" |
|
|
}, { |
|
|
value: "'Lucida Sans Unicode', 'Lucida Grande', sans-serif", |
|
|
text: 'Lucida Grande' |
|
|
}, { |
|
|
value: "'Palatino Linotype', 'Book Antiqua', Palatino, serif", |
|
|
text: 'Palatino Linotype' |
|
|
}, { |
|
|
value: "'Times New Roman', Times, serif", |
|
|
text: 'Times New Roman' |
|
|
}, { |
|
|
value: "Georgia, serif", |
|
|
text: "Georgia, serif" |
|
|
}, { |
|
|
value: "Tahoma, Geneva, sans-serif", |
|
|
text: "Tahoma" |
|
|
}, { |
|
|
value: "'Comic Sans MS', cursive, sans-serif", |
|
|
text: 'Comic Sans' |
|
|
}, { |
|
|
value: "Verdana, Geneva, sans-serif", |
|
|
text: 'Verdana' |
|
|
}, { |
|
|
value: "Impact, Charcoal, sans-serif", |
|
|
text: 'Impact' |
|
|
}, { |
|
|
value: "'Arial Black', Gadget, sans-serif", |
|
|
text: 'Arial Black' |
|
|
}, { |
|
|
value: "'Trebuchet MS', Helvetica, sans-serif", |
|
|
text: 'Trebuchet' |
|
|
}, { |
|
|
value: "'Courier New', Courier, monospace", |
|
|
text: 'Courier New' |
|
|
}, { |
|
|
value: "'Brush Script MT', sans-serif", |
|
|
text: 'Brush Script' |
|
|
}]; |
|
|
|