let bgMap = new Map();
let iconElement = DomElement.createWithStyle("i", "bi-x-square", "padding:1%;");
let layerElement = document.createElement("div");
layerElement.appendChild(iconElement);
function hideElement(element)
{
element.style.display = "none";
}
function showElement(element)
{
element.style.display = "block";
}
function highlight(element) {
bgMap.set(element, element.style.border);
element.style.border = "thick solid #ffff00";
//obj.style.backgroundColor = "yellow";
}
function submitForm(submitButton){
submitButton.closest("form").submit();
}
function edit(element){
var rect = element.getBoundingClientRect();
var left = rect.x-200;
var top = rect.y;
console.log(rect);
layerElement.setAttribute("style", 'width:' + 200 + 'px;'
+ "height:100px" + ';position:absolute;left:'+left+'px;top:'+top+'px;overflow:hidden;' +
'z-index:' + 10000 + ';' + "padding:10px;");
element.appendChild(layerElement);
}
function capitalizeFirstLetter(val) {
return String(val).charAt(0).toUpperCase() + String(val).slice(1);
}
function stopEdit(element) {
element.style.border = bgMap.get(element);
element.removeChild(layerElement);
}
let textVariables = new Map();
function isElement(element) {
return (
typeof HTMLElement === "object" ? element instanceof HTMLElement : //DOM2
element && typeof element === "object" && element !== null && element.nodeType === 1 && typeof element.nodeName==="string"
);
}
function resolveShortcodes(text) {
if (text.indexOf("[$") === -1)
return text;//no shortcode saves loop!
for (let [key, value] of textVariables)
text = text.replaceAll(key, value);
return text;
}
function getLanguageText(text) {
let result = "no text '" + name + "' for lang " + ModuleSite.currentLanguage;
let languages = text.split(";;");
if (languages.length === 1)//not multilang? //TODO: use it as key
result = text;
else {
for (let lang of languages) {
lang = lang.trim();
if (lang.startsWith(ModuleSite.currentLanguage))
result = lang.substring(3);
}
}
result = resolveShortcodes(result);
return result;
}
class Module {
children = [];
mandatoryAttributes = [];
contentLines = "";
widthPx = getCanvasWidth();
heightPx = 0;
parent = null;
paddingPx = 0;
id = "";
static index = 0;
elementToAppend = null;
editMode = false;
applyEditAttributes(element)
{
element.setAttribute("onmouseover","event.stopPropagation();highlight(this);");
element.setAttribute("onclick","event.stopPropagation();edit(this);");
//element.setAttribute("onmouseout","stopEdit(this)");
element.setAttribute("data-bs-toggle","tooltip");
element.setAttribute("data-bs-placement","top");
element.setAttribute("title",this.constructor.name);
}
getWidthPx(itemHeight) {
let sum = 0;
//alert("getWidthPx() in " + this.name + " with " + this.children.length + " children");
for (let child of this.children) {
let w = child.getWidthPx(itemHeight);
//alert(child.name + " returned a width of " + w);
sum += w;
}
return sum;
}
registerMandatoryAttributes(mandatoryAttributes) {
for (let attr of mandatoryAttributes)
this.mandatoryAttributes.push(attr);
}
collectIds(ids) {
if (this.attributes) {
let id = this.getOrDefaultAttribute("id", "");
if (id !== "")
ids.push(id);
}
for (let child of this.children) {
child.collectIds(ids);
}
}
checkIds() {
let ids = [];
this.collectIds(ids);
let count = ids.length;
for (let i = 0; i < count; ++i) {
for (let j = i + 1; j < count; ++j) {
if (ids[i] === ids[j]) {
return "id " + ids[i] + " is not unique! " + i + " and " + j + " are equal";
}
}
}
return false;
}
findById(id) {
if (this.attributes && this.getOrDefaultAttribute("id", "") === id && id !== "")
return this;
for (let child of this.children) {
let module = child.findById(id);
if (module)
return module;
}
return false;
}
findByType(type) {
//console.log("findByType(" + type + ") this=" + this.constructor.name);
if (this.constructor.name === type)
return this;
for (let child of this.children) {
let module = child.findByType(type);
if (module)
return module;
}
}
addContentLine(line) {
this.contentLines += line + "\n";
}
constructor(attributes, name= "") {
this.attributes = attributes;
if (name)
this.name = name;
else
this.name = this.constructor.name;
//if(attributes)
this.id = this.getOrDefaultAttribute("id", this.constructor.name + Module.index++);
console.log("constructed module " + this.name + " with id " + this.id);
}
create() {
return document.body;
}
renderId() {
return ' id="' + this.id + '"';
}
init() {
}
getBooleanAttribute(name, defaultValue) {
if (this.attributes.has(name))
return this.attributes.get(name) == "true";
return defaultValue;
}
getOrDefaultAttribute(name, defaultValue) {
if (!this.attributes)
alert("ERROR: Module " + this.name + " does not have attributes!");
return this.attributes.has(name) ? this.attributes.get(name).replace("#_#", " ") : defaultValue;
}
getLabel(defaultValue) {
return this.getMultilangAttribute("label", defaultValue);
}
getTitle(defaultValue) {
return this.getMultilangAttribute("title", defaultValue);
}
getMultilangText() {
return getLanguageText(this.contentLines);
}
scanFolder(folder, filter) {
const folderEncoded = encodeURIComponent(he.encode(folder));
const filterEncoded = encodeURIComponent(he.encode(filter));
let pathsString = ajaxCallSync("getFilesInFolder.php?folder=" + folderEncoded + "&filter=" + filterEncoded);
//alert(pathsString);
let pathsArray = pathsString.split(",");
if (!pathsArray[pathsArray.length - 1])
pathsArray.pop();
return pathsArray;
}
getMultilangAttribute(name, defaultValue) {
if (!this.attributes.has(name))
return defaultValue;
let text = this.attributes.get(name);
let languages = text.split(";;");
if (languages.length === 1)//not multilang?
return text.replaceAll("#_#", " ");
for (let lang of languages) {
if (lang.startsWith(ModuleSite.currentLanguage))
return lang.substring(3).replaceAll("#_#", " ");
}
return "no text '" + name + "' for lang " + ModuleSite.currentLanguage;
}
attribute(name) {
let attr = "";
if (this.attributes.has(name))
attr = ' ' + name + '="' + this.attributes.get(name).replace("#_#", " ") + '"';
return attr;
}
addChild(child) {
child.parent = this;
this.children.push(child);
}
hasChildren() {
return this.children.length > 0;
}
start() {
for (let child of this.children) {
child.start();
}
}
applyTooltipDefault(element, tooltip) {
element.setAttribute("data-bs-toggle", "tooltip");
element.setAttribute("data-bs-html", "true");
element.setAttribute("data-bs-placement", "auto");
element.setAttribute("title", tooltip);
}
languageTest() {
//console.log(this);
let lang = this.getOrDefaultAttribute("language", false);
if (!lang)
return true;
return lang === ModuleSite.currentLanguage;
}
appendToStyle(style) {
this.attributes.set("style", this.getOrDefaultAttribute("style", "") + style);
}
renderStyle() {
let html = "";
if (this.attributes.has("style"))
html += ' style="' + this.attributes.get("style") + '"';
return html;
}
getHtmlDimensions(html) {
let test = document.getElementById("dim");
test.style.display = "block";
test.innerHTML = html;
let result = test.getBoundingClientRect();
test.style.display = "none";
test.innerHTML = "";
return result;
}
getElementDimensions(element) {
let test = document.getElementById("dim");
test.appendChild(element);
test.style.display = "block";
let result = test.getBoundingClientRect();
test.style.display = "none";
test.removeChild(element);
return result;
}
useAlign() {
let style = "";
let vAlign = this.getOrDefaultAttribute("valign", false);
let translateY = false;
switch (vAlign) {
default:
break;
case "center":
translateY = "-50%";
style += "top:50%;";
break;
case "bottom":
style += "bottom:0px;";
break;
}
let hAlign = this.getOrDefaultAttribute("halign", false);
let translateX = false;
switch (hAlign) {
default:
break;
case "center":
translateX = "-50%";
style += "left:50%;";
break;
case "right":
style += "right:0px;";
break;
}
if (translateX) {
style += "position:absolute;";
if (translateY) {
style += "transform:translate(" + translateX + "," + translateY + ");"
} else {
style += "transform:translateX(" + translateX + ");";
}
} else if (translateY) {
style += "position:absolute;";
style += "transform:translateY(" + translateY + ");";
}
return style;
}
usePadding(defaultPct) {
let padding = this.getOrDefaultAttribute("padding", defaultPct);
if (padding) {
let pxValue = this.parent.widthPx * padding / 100;
console.log(this.name +" padding:" + this.paddingPx + " / " + pxValue);
if (this.paddingPx != pxValue) {
this.paddingPx = pxValue;
this.appendToStyle("padding:" + pxValue + "px;");
}
return "padding:" + pxValue + "px;";
}
return "";
}
append(parentElement) {
let newModule = this.create();
newModule.id = this.id;
if(this.editMode)
this.applyEditAttributes(newModule)
parentElement.appendChild(newModule);
}
appendChildrenRange(parent, from, to) {
for (let i = from; i < to; ++i) {
let child = this.children[i];
child.widthPx = this.widthPx - this.paddingPx * 2;
if (child.languageTest())
child.append(parent);
}
}
appendChildren(parent) {
for (let child of this.children) {
child.widthPx = this.widthPx - this.paddingPx * 2;
if (child.languageTest())
child.append(parent);
}
}
}class ModuleBrand extends Module {
create() {
let filter = this.getOrDefaultAttribute("filter", "");
if (filter) {
filter = "filter:"+filter.replaceAll(";", " ")+";";
}
let link = document.createElement("a");
link.setAttribute("class", "navbar-brand");
link.setAttribute("href", "#");
if (!ModuleNavbar.horizontal) {
link.setAttribute("style", "margin-left:auto;margin-right:auto;text-align:center;");
} else {
link.setAttribute("style", "margin:auto;");
}
let logo = this.getOrDefaultAttribute("logo", false);
if (logo) {
let alt = this.getOrDefaultAttribute("alt", "");
let logoStyle = "";
if (ModuleNavbar.horizontal)
logoStyle += ModuleNavbar.logoSize;
else
logoStyle += 'max-width:100%;';
let image = document.createElement("img");
image.setAttribute("src", logo.replace("#_#", " "));
if (alt)
image.setAttribute("alt", alt);
image.setAttribute("style", logoStyle + filter);
link.appendChild(image);
}
let name = this.getLabel("");
let html = "";
if (name) {
if (!ModuleNavbar.horizontal) {
if (getTextWidth(name, getCanvasFont()) * 1.25 > this.widthPx)
name = name.substring(0, 5);//shorten name if it is too wide
if (getTextWidth(name, getCanvasFont()) * 1.25 < this.widthPx)
html += '
' + name.toUpperCase() + '
';
} else
html += name;
let span = document.createElement("span");
span.innerHTML = html;
link.appendChild(span);
}
return link;
}
}class ModuleButton extends Module {
create(){
let label = this.getLabel("Button");
let id = this.getOrDefaultAttribute("id", "");
let button = DomElement.createButton("button", label);
if(id)
button.id = id;
return button;
}
}class ModuleCard extends Module {
create() {
let header = this.getMultilangAttribute("header", "");
let footer = this.getMultilangAttribute("footer", "");
let imagePath = this.getOrDefaultAttribute("image", "");
let color = this.getOrDefaultAttribute("bordercolor", "var(--textcolor)");
let borderColor = this.getOrDefaultAttribute("bordercolor", "var(--textcolor)");
let backgroundColor = this.getOrDefaultAttribute("backgroundcolor", "transparent");
let div = DomElement.createWithStyle("div", "card", 'height:100%;color:' + color + ';border-color: ' + borderColor + ';background-color:' + backgroundColor + ';');
if (header) {
let headerDiv = DomElement.createWithContent("div", "card-header", header);
div.appendChild(headerDiv);
}
let image = DomElement.create("img", "card-img-top");
image.setAttribute("src", imagePath);
div.appendChild(image);
let body = this.createBody();
div.appendChild(body);
if (footer) {
let footerDiv = DomElement.createWithContent("div", "card-footer", footer);
div.appendChild(footerDiv);
}
return div;
}
createBody() {
let title = this.getMultilangAttribute("title", "");
let subtitle = this.getMultilangAttribute("subtitle", false);
let text = this.getMultilangText();
let body = DomElement.create("div", "card-body");
let cardTitle = DomElement.createWithContent("h3", "card-title", title);
body.appendChild(cardTitle);
if (subtitle) {
let cardSubtitle = DomElement.createWithContent("h4", "card-subtitle", subtitle);
body.appendChild(cardTitle);
}
let cardText = DomElement.createWithContent("p", "card-text", text);
body.appendChild(cardText);
return body;
}
}class ModuleContent extends Module {
scaleFont = false;
maxWordWidth = 0;
biggestWord = "";
create() {
let responsiveBehavior = this.getOrDefaultAttribute("responsive", "break");
let div = document.createElement("div");
let style = this.usePadding();
switch (responsiveBehavior) {
case "break":
style += "word-break: break-word;";
break;
case "scale":
this.scaleFont = true;
break;
}
style += this.useAlign();
let htmlText = this.getMultilangText();
div.innerHTML = htmlText;
if (this.scaleFont) {
const temp = document.createElement("div");
temp.setAttribute("class", "scalefonts");
document.body.appendChild(temp);
temp.innerHTML = htmlText;
this.getMaxWordWidth(temp);
document.body.removeChild(temp);
//alert(this.maxWordWidth + " "+ this.widthPx);
let factor = 1;
if (this.maxWordWidth > this.widthPx)
factor = this.widthPx / this.maxWordWidth;
let scaleStyle = document.createElement('style');
scaleStyle.type = 'text/css';
let styleclass = this.id+"Style";
scaleStyle.innerHTML = '.' + styleclass + " h1 { font-size: calc(" + factor + " * 6vw) }" +
'.' + styleclass + " h2 { font-size: calc(" + factor + " * 5vw) }" +
'.' + styleclass + " h3 { font-size: calc(" + factor + " * 4vw) }" +
'.' + styleclass + " h4 { font-size: calc(" + factor + " * 3vw) }" +
'.' + styleclass + " h5 { font-size: calc(" + factor + " * 2vw) }";
div.prepend(scaleStyle)
div.setAttribute("class", styleclass);
}
div.setAttribute("style", style);
return div;
}
getMaxWordWidth(element) {
if (!isElement(element)) {
//alert("node "+element + " is not an element");
return;
}
for (let child of element.childNodes) {
this.getMaxWordWidth(child);
}
//if(element.nodeType === 3) {
//alert("found node=" + element + " of type "+element.nodeType);
const text = element.innerText || "";
if (text.length > 0) {
//alert("text=" + text + " in node with type " + element.nodeType);
let words = text.split(/\s/);
for (let word of words) {
let font = getCanvasFont(element);
let width = getTextWidth(word, font);
//alert("word " + word + " in element " + element.tagName + element +" with font "+ font +" has a width of " + width);
if (width > this.maxWordWidth) {
this.maxWordWidth = width;
this.biggestWord = word;
}
}
}
// }
}
}function showDataElement(id, key, className) {
if (key) {
let keyInputId = id + "Key";
let keyInputField = document.getElementById(keyInputId);
if (!keyInputField)
alert("ERROR: key field " + keyInputId + " not found");
keyInputField.setAttribute("value", key);
}
let elementToShow = document.getElementById(id);
if (className) {
//get first form
const form = elementToShow.getElementsByTagName("form")[0];
//get all input fields
let inputs = form.getElementsByTagName("input");
//get data now and match it in fillInputs
ajaxCallFuncParams("task.php?_task_="+className+"_read&key=" + key, fillInputs, inputs);
}
elementToShow.style.display = "block";
}
function fillInputs(json, inputFields) {
//alert("fillInputs with "+json);
//make data string to associative array
let data = JSON.parse(json);
//alert(data);
//alert(inputFields);
for (let input of inputFields) {
//get field name
let fieldName = input.name;
alert("filling field "+fieldName + " with " + data[fieldName]);
//fill inputField with value of databaseField when names match
if (data[fieldName])
input.value = data[fieldName];
}
}
function fillTable(json, dataModul) {
dataModul.fillTable(json);
}
class ModuleData extends Module {
query = "";
addLabel = "";
editLabel = "";
deleteLabel = "";
replaceId = "";
actions = "";
className = "";
fillTable(json) {
//alert(json);
let root = DomElement.createDiv();
const __ret = this.handleActions(root);
let addButtonRow = __ret.addDeleteButton || __ret.addEditButton;
let rows = JSON.parse(json);
let table = DomElement.create("table", "table table-dark table-striped table-hover");
let rowIndex = 1;
for (let row of rows) {
//alert(JSON.stringify(row));
let tr = document.createElement("tr");
let key = row['PrimKey'];
//alert(key)
row['PrimKey'] = rowIndex;
if (__ret.datasetActions.length > 0) {
for (let datasetAction of __ret.datasetActions) {
let td = document.createElement("td");
td.appendChild(this.createSetActionButton(key, datasetAction[0], datasetAction[1]));
tr.appendChild(td);
}
}
for (let item in row) {
let td = document.createElement("td");
//alert(item);
td.innerHTML = row[item];
tr.appendChild(td);
}
if (addButtonRow) {
let td = document.createElement("td");
if (__ret.addEditButton)
td.appendChild(this.createEditButton(key, __ret.editActionParts));
if (__ret.addDeleteButton) {
// if(__ret.addEditButton) {
// let spacer = DomElement.createStyled("span","");
// td.appendChild(spacer);
// }
td.appendChild(this.createDeleteButton(key, __ret.deleteActionParts));
}
tr.appendChild(td);
}
table.appendChild(tr);
++rowIndex;
}
root.appendChild(table);
//alert(parameters + " rows: " + rowsString);
let toBeReplaced = document.getElementById(this.replaceId);
toBeReplaced.parentNode.replaceChild(root, toBeReplaced);
}
handleActions(root) {
let addEditButton = false;
let addDeleteButton = false;
let editActionParts = [];
let deleteActionParts = [];
let datasetActions = [];
let actions = this.actions.split(",");//all actions are comma separated
for (let action of actions) {
let actionFunction = action + capitalizeFirstLetter(this.className);
switch (action) {
case "add":
root.appendChild(this.createAddButton(actionFunction));//add one addButton
break;
case "edit":
editActionParts = actionFunction;
addEditButton = true;
break;
case "delete":
deleteActionParts = actionFunction;
addDeleteButton = true;
break;
default:
datasetActions.push([action, actionFunction]);
//alert("[dataModul '" + this.id + "' actions] custom action " + actionName + " added");
break;
}
}
return {deleteActionParts, editActionParts, addEditButton, addDeleteButton, datasetActions};
}
createTargetFunction(elementId, key, dbTableName = "") {
return "showDataElement('" + elementId + "','" + key + "','" + dbTableName + "')";
}
createEditButton(key, elementId) {
let editButton = DomElement.clone("editDataButton");
if (this.editLabel)
editButton.innerHTML = this.editLabel;
let targetFunction = this.createTargetFunction(elementId, key, this.className);
editButton.setAttribute('onclick', targetFunction);
return editButton;
}
createDeleteButton(key, elementId) {
let deleteButton = DomElement.clone("deleteDataButton");
if (this.deleteLabel)
deleteButton.innerHTML = this.deleteLabel;
let targetFunction = this.createTargetFunction(elementId, key);
deleteButton.setAttribute('onclick', targetFunction);
return deleteButton;
}
createAddButton(elementId) {
let addButton = DomElement.clone("addDataButton");
if (this.addLabel)
addButton.innerHTML = this.addLabel;
let targetFunction = this.createTargetFunction(elementId, "");
addButton.setAttribute('onclick', targetFunction);
return addButton;
}
createSetActionButton(key, action, elementId) {
let setActionButton = DomElement.clone(elementId + "Button");
//alert("[dataModul '" + this.id + "' actions] custom action " + datasetAction + " added. button=" + setActionButton);
setActionButton.setAttribute('onclick', elementId + "('" + key + "','" + elementId + "')");
return setActionButton;
}
getTargetParts(actionParts) {
let actionTarget = actionParts[1];
return actionTarget.split("#");
}
start() {
ajaxCallFuncParams(this.query, fillTable, this);
}
create() {
this.actions = this.getOrDefaultAttribute("actions", "");//possible values are add, edit, delete
this.addLabel = this.getMultilangAttribute("addLabel", "");
this.editLabel = this.getMultilangAttribute("editLabel", "");
this.deleteLabel = this.getMultilangAttribute("deleteLabel", "");
this.className = this.getOrDefaultAttribute("class", "");
let attributes = this.getOrDefaultAttribute("attributes", "");
let encoded = encodeURIComponent(he.encode("key," + attributes));//make sure first is the primary Key
this.query = "task.php?_task_="+this.className+"_readAll&attributes=" + encoded;
this.replaceId = this.id + "loader";
let root = document.createElement("div");
let loader = DomElement.createSpinner();
loader.id = this.replaceId;
root.appendChild(loader);
return root;
}
}class ModuleFeature extends Module {
create(){
let icon = this.getOrDefaultAttribute("icon", "");
let text = this.getMultilangText();
let tr = document.createElement("tr");
let td1 = document.createElement("td");
let iconI = DomElement.createWithStyle("i", icon, "padding:1%;");
td1.appendChild(iconI);
let td2 = document.createElement("td");
td2.setAttribute("style","padding:1%;");
let textI = document.createElement("i");
textI.innerHTML = text;
td2.appendChild(textI);
tr.appendChild(td1);
tr.appendChild(td2);
return tr;
}
}class ModuleFeatures extends Module {
create() {
let table = document.createElement("table");
this.appendChildren(table);
return table;
}
}class ModuleFile extends Module {
isImage() {
let extension = this.getExtension().toLowerCase();
//console.log("ext="+extension);
switch (extension) {
case "jpg":
case "png":
case "svg":
case "gif":
case "webp":
return true;
}
return false;
}
getTooltip() {
if (this.isImage())
return " ";
return this.getTitle(this.getPath());
}
getExtension() {
let index = this.getPath().lastIndexOf(".");
return this.getPath().substring(index + 1);
}
getFileIcon() {
//TODO get mapping from system settings or moduleFileGrid sitescript
let iconMapping = new Map();
iconMapping.set("doc", "media/icons/files/file-earmark-word.svg");
let extension = this.getExtension();
if (!iconMapping.has(extension))
return "media/icons/files/file-earmark.svg";
return iconMapping.get(extension);
}
getPath() {
return this.attributes.get('path').replace("#_#", " ");
}
getPreviewPath() {
if (this.isImage())
return this.getPath();
return this.getFileIcon();
}
getThumbPath() {
if (this.isImage())
return this.getPath();
return this.getFileIcon();
}
create() {
let include = this.getOrDefaultAttribute("include", "");
if (include) {
let fileContent = document.createElement("div");
fileContent.setAttribute("style", "height:900px");//TODO: why 900? make dynamic?
fileContent.id = this.id;
ajaxCallFunc(include, fileContent.id, activateTooltips);
return fileContent;
} else {
let thumbSize = this.getOrDefaultAttribute("thumbSize", "30");
let paddingMm = this.getOrDefaultAttribute("paddingMm", "1");
let div = DomElement.createWithStyle("div", "col text-center", "padding:" + paddingMm + "mm;flex:0 0 " + thumbSize + "mm;");
let image = DomElement.createWithStyle("img", "figure-img img-fluid rounded",
"width:" + thumbSize + "mm; height: " + thumbSize + "mm; object-fit: cover;");
this.applyTooltipDefault(image, this.getTooltip());
image.setAttribute("src", this.getThumbPath());
image.setAttribute("alt", this.getTitle());
image.setAttribute("onmouseup", "applySettingsToModal('Image','" + this.id + "');");
div.appendChild(image);
return div;
}
}
}class ModuleFileGrid extends Module {
create() {
let numCols = this.getFiles();
if (this.children.length > 0) {//got files?
//console.log(alignment);
let justifyContent = this.getAlignment();
let div = DomElement.create("div", "container-fluid");
//alert(canvasWidth + " / " + elementWidth + " = " + numCols + " (" + mm2px + ")");
let rowCount = Math.ceil(this.children.length / numCols);
//alert(numCols + " / " + rowCount + " / " + this.children.length);
for (let r = 0; r < rowCount; ++r) {
let row = DomElement.createWithStyle("div", "row", "justify-content: " + justifyContent + ";");
let from = r * numCols;
//alert("appending media files "+from + " to " + Math.min(this.children.length, from + numCols));
this.appendChildrenRange(row, from, Math.min(this.children.length, from + numCols));
div.appendChild(row);
}
return div;
} else {
let h2 = document.createElement("h2");
h2.innerHTML = this.getMultilangAttribute("empty", "No files found");
return h2;
}
}
getAlignment() {
let alignment = this.getOrDefaultAttribute("align", false);
let justifyContent = "flex-start";//default?
if (alignment) {
switch (alignment) {
default:
case "start":
justifyContent = "flex-start";
break;
case "end":
justifyContent = "flex-end";
break;
case "center":
justifyContent = "center";
break;
case "evenly":
justifyContent = "space-evenly";
break;
}
}
return justifyContent;
}
getFiles() {
let folder = this.getOrDefaultAttribute("folder", false);
let filter = this.getOrDefaultAttribute("filter", false);
let thumbSize = this.getOrDefaultAttribute("thumbSize_mm", "30");
let padding = this.getOrDefaultAttribute("thumbPadding", "2");
let paddingMm = parseFloat(thumbSize) * parseFloat(padding) / 100.0;
let elementWidth = thumbSize * mm2px;
let numCols = Math.floor(this.widthPx / elementWidth);
console.log("file grid: " + this.widthPx + " / " + elementWidth + " = " + numCols + " (" + mm2px + ")");
if (folder) {
let paths = this.scanFolder(folder, filter);
for (let path of paths) {
let attribs = new Map();
attribs.set("paddingMm", paddingMm.toString());
attribs.set("path", path);
attribs.set("thumbSize", thumbSize);
let moduleFile = new ModuleFile(attribs, false);
moduleFile.widthPx = elementWidth;
this.addChild(moduleFile);
}
}
return numCols;
}
}class ModuleFontSelector extends Module {
start() {
if (!getFontList().length)
document.addEventListener("FontListAvailable", (e) => {
this.fontListAvailable();
});
else
this.fontListAvailable();
}
fontListAvailable() {
let fonts = getFontList();
let fontOptions = "";
for (let font of fonts) {
fontOptions += "" + font + " ";
}
if (this.elementToAppend) {
this.elementToAppend.innerHTML = fontOptions;
} else {
let select = document.getElementById(this.id);
select.innerHTML = fontOptions;
}
}
create() {
let title = this.getTitle("");
let action = this.getOrDefaultAttribute("action", "");
let select = document.createElement("select");
select.setAttribute("onselect", action);
select.setAttribute("title", getLanguageText(title));
return select;
}
}class ModuleFooter extends Module {
create() {
let classes = "footer";
let overlay = this.getBooleanAttribute("overlay", false);
let sticky = this.getBooleanAttribute("sticky", false);
let opacity = this.getOrDefaultAttribute("opacity", "");
if (opacity)
opacity = "opacity: " + opacity + ";";
let heightStyle = "height:" + this.getOrDefaultAttribute("size", 5) + "vh;";
console.log("footerAttribs=%o", this.attributes);
let root = document.createElement("div");
if (sticky) {
let stickyClass = " fixed-bottom";
classes += stickyClass;
if (!overlay) {
let spacer = document.createElement("div");
spacer.setAttribute("style", heightStyle);
root.appendChild(spacer);
}
}
let footer = DomElement.createWithStyle("footer", classes,heightStyle + opacity);
this.appendChildren(footer);
root.appendChild(footer);
return root;
}
}
class ModuleForm extends Module {
create() {
let textAlign = this.getOrDefaultAttribute("text-align", "left");
let task = this.getOrDefaultAttribute("task", "");
let form = DomElement.createStyled("form", "text-align:" + textAlign);
form.setAttribute("method", "post");
let input = document.createElement("input");
input.setAttribute("type", "hidden");
input.setAttribute("name", "_task_");
input.setAttribute("value", task);
form.appendChild(input);
this.appendChildren(form);
return form;
}
}class ModuleGlobals extends Module {
create(){
let globals = DomElement.createDiv("display:none;");
this.appendChildren(globals);
return globals;
}
}class ModuleInputField extends Module {
static helpTextCounter = 0;
name = "";
create() {
let additionalInputAttributes = new Map();
let name = this.getOrDefaultAttribute("name", "");
let data = this.getOrDefaultAttribute("data", "");
let value = this.getOrDefaultAttribute("value", "");
let autocomplete = this.getOrDefaultAttribute("autocomplete","on");
if (name) {
additionalInputAttributes.set('name', name);
this.name = name;
}
if (!value && data) {
//console.log(data);
const queryComp = data.split(".");
let rowString = ajaxCallSync("query.php?action=readkeyset&table=" + queryComp[0] + "&key=" + queryComp[1]);
//console.log(rowString);
//here we should only get one field of one row
//extract it
//TODO: make DBResult.getValue(rowsString,0,0);
let fields = rowString.split(",,");
let assoc = [];
for (let field of fields) {
let splitter = field.split("=");
assoc[splitter[0]] = splitter[1];
}
value = assoc[queryComp[2]];//2 is the field name
//console.log("assoc =" + assoc);
let separator = queryComp[3];
if (separator) {//3rd component can be an index of a semicolon separated string
//console.log(separator);
let values = value.split(separator);
if (queryComp.length < 5)
alert("query component 3 needs to be a separator and 4 needs to be an index");
let index = queryComp[4];
//console.log(index);
value = values[index];
}
}
additionalInputAttributes.set("value", value);
let placeholder = this.getMultilangAttribute("placeholder", "");
let type = this.getOrDefaultAttribute("type", "");
let helpText = this.getMultilangAttribute("help", "");
let helpTextId = "";
if (helpText) {
helpTextId = "help" + ModuleInputField.helpTextCounter;
++ModuleInputField.helpTextCounter;
additionalInputAttributes.set("aria-describedby", helpTextId);
}
let root = DomElement.create("div", "form-group");
this.appendLabel(root);
this.appendPrepend(root);
switch (type) {
default:
let input = DomElement.create("input", "form-control");
input.id = this.id;
input.setAttribute("type", type);
input.setAttribute("placeholder", placeholder);
input.setAttribute("autocomplete",autocomplete);
for (let [key, value] of additionalInputAttributes) {
input.setAttribute(key, value);
}
root.appendChild(input);
break;
case "hidden":
let hiddenField = DomElement.create("input");
hiddenField.id = this.id;
hiddenField.setAttribute("type", type);
hiddenField.setAttribute("name", name);
hiddenField.setAttribute("value", value);
root.appendChild(hiddenField);
break;
case "select":
let select = DomElement.create("select", "form-control");
select.id = this.id;
this.appendChildren(select)
root.appendChild(select);
break;
}
if (helpText) {
let help = DomElement.create("small", "form-text text-muted");
help.id = helpTextId;
help.innerHTML = helpText;
root.appendChild(help);
}
this.id="";
return root;
}
appendPrepend(root) {
let prepend = this.getMultilangAttribute("prepend", "");
if (prepend) {
let prep = DomElement.create("div", "input-group-prepend");
let text = DomElement.create("span", "input-group-text");
text.innerHTML = prepend;
prep.appendChild(text);
root.appendChild(prep);
}
}
appendLabel(root) {
let label = this.getLabel("");
let labelElement = document.createElement("label");
labelElement.setAttribute("for", this.id);
labelElement.innerHTML = label;
root.appendChild(labelElement);
}
}class ModuleLayer extends Module {
static layerIndex = 0;
getResponsibleHeight() {
let heightResponsible = this.getOrDefaultAttribute("height-responsible", false);
if (heightResponsible) {
let info = null;
info = this.getElementDimensions(this.createByPosition("relative"));
//alert(info.width + "x" + info.height);
let result = info.height;
return result;
}
return false;
}
create() {
let element = this.createByPosition("absolute");
++ModuleLayer.layerIndex;
return element;
}
createByPosition(position) {
let padding = this.usePadding();
console.log(position + " padding of layer = " + padding);
console.log(this);
let height = "";
if (position === "absolute")
height = "height:100%";
let div = document.createElement("div");
div.setAttribute("style", 'width:' + this.parent.widthPx + 'px;'
+ height + ';position:' + position + ';overflow:hidden;' +
'z-index:' + ModuleLayer.layerIndex + ';' + padding);
this.appendChildren(div);
return div;
}
}class ModuleMap extends Module {
static index = 0;
center = [48.837398661225926, 13.34104422351722];
level = 15;
markers = [];
icon = false;
start() {
const map = L.map(this.id).setView(this.center, this.level);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
'attribution': 'Kartendaten © OpenStreetMap Mitwirkende',
'useCache': true
}).addTo(map);
map.scrollWheelZoom.disable();
map.dragging.disable();
let options = false;
if (this.icon) {
let splits = this.icon.split(";");
let url = splits[0];
let size = [16, 16];
if (splits[1])
size = splits[1].split(",").map(Number);
let anchor = [size[0] / 2, size[1] / 2];
if (splits[2])
anchor = splits[2].split(",").map(Number);
let myIcon = L.icon({
iconUrl: url,
iconSize: size, // size of the icon
iconAnchor: anchor, // point of the icon which will correspond to marker's location
});
options = {icon: myIcon};
}
for (let marker of this.markers) {
console.log(marker);
if (options)
L.marker(marker, options).addTo(map);
else
L.marker(marker).addTo(map);
}
}
create(){
this.applyMembers();
let height = this.getOrDefaultAttribute("height", "600");
let div = document.createElement("div");
div.id = this.id;
div.setAttribute("style","height:"+height+"px;width:100%;");
return div;
}
applyMembers() {
let center = this.getOrDefaultAttribute("center", "48.837398661225926,13.34104422351722");
center = center.split(",");
this.center[0] = Number(center[0]);
this.center[1] = Number(center[1]);
this.level = parseInt(this.getOrDefaultAttribute("level", "15"), 10);
this.icon = this.getOrDefaultAttribute("icon", false);
let markers = this.getOrDefaultAttribute("marker", "48.837398661225926,13.34104422351722");
markers = markers.split(",");
for (let i = 0; i < markers.length; i += 2) {
this.markers[i / 2] = [Number(markers[i]), Number(markers[i + 1])];
}
}
}class ModuleOption extends Module {
init() {
this.registerMandatoryAttributes(["value", "label"]);
}
create() {
let val = this.getOrDefaultAttribute("value","");
let label = this.getLabel("label");
let option = document.createElement("option");
option.setAttribute("value",val);
option.innerHTML = label;
}
}
class ModulePostEditor extends Module {
languageCount = 1;
editors = [];
start() {
for (let editor of this.editors)
{
editor.setup();
}
}
create() {
let condition = encodeURIComponent("PostKey=" + ModulePage.current.param);
let request = "query.php?action=read&table=contents&fields=*&condition=" + condition;
let rowsString = ajaxCallSync(request);
//alert("ModulePostEditor + " + ModulePage.current.param + " " + request + " res=" + rowsString);
//every row contains the post in one language
let rows = rowsString.split(";;").filter(Boolean);
this.languageCount = rows.length;
let root = document.createElement("div");
let heading = document.createElement("h3");
heading.innerHTML = "Post Editor";
root.appendChild(heading);
for (let row of rows) {
let fields = row.split(",,");
let editor = new CodeEditor(new HtmlParser());
editor.setKey(ModulePage.current.param+"_"+fields[2]);
editor.setCode(fields[3]);
let htmlEditor = new HtmlEditor(editor);
let div = document.createElement("div");
div.setAttribute("style",'display: inline-block; padding: 1%;border: 1px solid white;');
div.innerHTML = editor.render();
let div2 = div.cloneNode(false);
div2.innerHTML= htmlEditor.render();
root.appendChild(div);
root.appendChild(div2);
root.appendChild(document.createElement("p"));
this.editors.push(editor);
this.editors.push(htmlEditor);
}
return root;
}
}class ModuleProduct extends Module {
folderUrl = "";
readTextFile(file, callback, param) {
var rawFile = new XMLHttpRequest();
rawFile.overrideMimeType("application/json");
rawFile.open("GET", file, false);
rawFile.send();
//rawFile.onreadystatechange = function() {
if (rawFile.readyState === 4 && rawFile.status == "200") {
return callback(rawFile.responseText, param);
}
//}
}
formatMoney(number, decPlaces, decSep, thouSep) {
decPlaces = isNaN(decPlaces = Math.abs(decPlaces)) ? 2 : decPlaces,
decSep = typeof decSep === "undefined" ? "." : decSep;
thouSep = typeof thouSep === "undefined" ? "," : thouSep;
var sign = number < 0 ? "-" : "";
var i = String(parseInt(number = Math.abs(Number(number) || 0).toFixed(decPlaces)));
var j = (j = i.length) > 3 ? j % 3 : 0;
return sign +
(j ? i.substr(0, j) + thouSep : "") +
i.substr(j).replace(/(\decSep{3})(?=\decSep)/g, "$1" + thouSep) +
(decPlaces ? decSep + Math.abs(number - i).toFixed(decPlaces).slice(2) : "");
}
createCard(productdata) {
console.log("product json data = %o", productdata);
let cardAttributes = new Map();
let imagePath = this.folderUrl + "thumb" + productdata.mainImageIndex + ".jpg";
cardAttributes.set("image", imagePath);
let hint = ' ';
if (productdata.hint)
hint = '' + productdata.hint + ' ';
let title = hint + '' + productdata.brand + ' ' + productdata.title + ' ';
cardAttributes.set("title", title);
if (productdata.subtitle)
cardAttributes.set("subtitle", productdata.subtitle);
let borderColor = this.getOrDefaultAttribute("bordercolor", "var(--textcolor)");
cardAttributes.set("bordercolor", borderColor);
let backgroundColor = this.getOrDefaultAttribute("backgroundcolor", "transparent");
cardAttributes.set("backgroundcolor", backgroundColor);
let moduleCard = new ModuleCard(cardAttributes, "product");
if (productdata.rating) {
let emptyStars = 5;
let fullStars = Math.floor(productdata.rating);
let halfStar = false;
let fraction = productdata.rating - fullStars;
if (fraction > 0.75) {
fullStars++;
} else if (fraction > 0.25)
halfStar = true;
emptyStars -= fullStars;
let rating = '';
for (let i = 0; i < fullStars; ++i) {
rating += ' ';
}
if (halfStar) {
rating += ' ';
--emptyStars;
}
for (let i = 0; i < emptyStars; ++i) {
rating += ' ';
}
moduleCard.addContentLine('' + rating + "
");
}
let priceInfo = '' + this.makePrice(productdata.price) + ' ';
if (productdata.rrp && productdata.rrp !== productdata.price) {
let discountPct = 100 * (1 - productdata.price / productdata.rrp);
priceInfo = '' +
'-' + discountPct + '% ' + priceInfo +
' ' + this.makePrice(productdata.rrp) + ' ' +
'
';
}
moduleCard.addContentLine(priceInfo);
if (productdata.shippingInfo)
moduleCard.addContentLine(productdata.shippingInfo);
return moduleCard.create();
}
makePrice(value) {
let currency = "$";//TODO: use global currency
return this.formatMoney(this.convertToCurrency(value), 2, ",", ".") + currency;
}
convertToCurrency(value) {
console.log("TODO: implement ModuleProduct::convertToCurrency");
return value;
}
create() {
let folder = this.getOrDefaultAttribute("folder", "");
this.folderUrl = "./products/" + folder + "/";
console.log("productUrl = " + this.folderUrl);
let jsonPath = this.folderUrl + "product.json";
let container = DomElement.createStyled("div", "max-width:7cm;");
let div = this.readTextFile(jsonPath, function (text, thiz) {
let data = JSON.parse(text);
return thiz.createCard(data);
}, this);
container.appendChild(div);
return container;
}
}
class ModuleSection extends Module {
create(){
let section = DomElement.createStyled("section",'width:'+getCanvasWidth()+'px;height:'+window.innerHeight+'px;');
this.appendChildren(section);
return section;
}
render() {
this.appendToStyle('width:'+getCanvasWidth()+'px;height:'+window.innerHeight+'px;');
let html =
'';
html += super.renderChildren();
html += ' ';
return html;
}
}class ModuleSearch extends Module {
create(){
//
// $
//
// .00
//
//
//Button
let group = DomElement.createWithStyle("div", "input-group","width:unset;");
let input = DomElement.create("input","form-control");
input.setAttribute("type", "text");
input.setAttribute("name", "search");
let button = DomElement.create("button","btn btn-secondary");
button.setAttribute("type", "button");
let icon = DomElement.create("i","bi bi-search");
button.appendChild(icon);
group.appendChild(input);
group.appendChild(button);
return group;
}
}class ModuleRow extends Module {
createInternal(create) {
let columns = this.children;
let sum = 0;
let i = 0;
let breaks = [];
for (let column of columns) {
let minMM = column.getMinSizeCm() * 10;
let columnMinSizePx = mm2px * minMM;
sum += columnMinSizePx;
//console.log(column.attributes + " minMM=" + minMM + " columnMinSizePx=" + columnMinSizePx + " sum=" + sum + " row.widthPx=" + this.widthPx);
if (sum > this.widthPx) {
breaks.push(Math.max(i, 1));
sum = columnMinSizePx;
}
++i;
}
if (breaks[breaks.length - 1] != columns.length)
breaks.push(columns.length);//make sure to also handle the last column(s)
//console.log("breaks:" + breaks);
let lastPos = 0;
for (let pos of breaks) {
let sumPct = 0;
for (let i = lastPos; i < pos; ++i) {
sumPct += parseFloat(columns[i].getSizePct());
}
//console.log("sumPct:" + sumPct);
for (let i = lastPos; i < pos; ++i) {
let pct = 100 * parseFloat(columns[i].getSizePct()) / sumPct;
columns[i].attributes.set("size", pct.toString());
}
lastPos = pos;
}
let paddingStyle = this.usePadding();
if (create) {
let row = DomElement.create("div","row");
let sizeStyle = "";
if (this.attributes.has("size"))
sizeStyle = 'height:' + this.attributes.get("size") + '%;'
row.setAttribute("style",sizeStyle+paddingStyle);
this.appendChildren(row);
return row;
}
}
create() {
return this.createInternal(true);
}
render() {
return this.createInternal(false);
}
}class ModuleColumn extends Module {
getMinSizeCm() {
return this.getOrDefaultAttribute("min-size", "6");
}
getSizePct() {
return this.getOrDefaultAttribute("size", "100");
}
create() {
let style = this.createStyle();
let div = document.createElement("div");
div.setAttribute("style", style);
super.appendChildren(div);
return div;
}
createStyle() {
let sizePct = this.getSizePct();
this.widthPx = this.parent.widthPx * sizePct / 100;
let minHeight = this.getOrDefaultAttribute("min-height", "5");
for (let child of this.children) {
if (typeof child.getResponsibleHeight === "function") {
let height = child.getResponsibleHeight();
if (height)
minHeight = height;
}
}
let textAlign = this.getOrDefaultAttribute("text-align", "center");
let bgColor = this.getOrDefaultAttribute("background-color", false);
let style = "min-height:" + minHeight + "px;" +
"overflow:hidden;" +
"position:relative;" +
"flex: 0 0 auto;" +
"width:" + sizePct + "%;" +
"text-align:" + textAlign + ";" +
this.usePadding();
if (bgColor)
style += "background-color:" + bgColor + ";";
return style;
}
}class ModuleImage extends Module {
static imageIndex = 0;
width = 0;
height = 0;
anchor = "center";
init() {
let path = this.attributes.get("src").replace("#_#", " ");
let dimensionsString = this.getDimensions(path);
let dimensions = dimensionsString.split(",,");
this.width = parseInt(dimensions[0]);
this.height = parseInt(dimensions[1]);
//alert("imageDims=" + this.width + "x" + this.height + " " + path);
// if (!dimensions[0])
// this.writeDimensions(path);
}
getWidthPx(itemHeight) {
let factor = itemHeight / this.height;
if (factor > 1)
factor = 1;
let w = this.width * factor;
//alert("this.width=" + this.width + " w=" + w + " "+this.name);
return w;
}
getDimensions(path) {
let condition = encodeURIComponent("path LIKE '%" + path + "'");
let rowString = ajaxCallSync("query.php?action=readset&table=media&fields=width,height&condition=" + condition);
return rowString;
}
calculateLeft(xPct) {
let xOffset = this.parent.widthPx * xPct / 100;
switch (this.anchor) {
case "center":
return xOffset - this.width / 2;
case "topleft":
return xOffset;
case "bottomright":
return xOffset - this.width;
}
}
calculateTop(yPct) {
let yOffset = window.innerHeight * yPct / 100;
switch (this.anchor) {
case "center":
return yOffset - this.height / 2;
case "topleft":
return yOffset;
case "bottomright":
return yOffset - this.height;
}
}
create() {
let effect = this.getOrDefaultAttribute("effect", "");
let effectTiming = this.getOrDefaultAttribute("effecttiming", "ease");
let offset = this.getOrDefaultAttribute("offset", "");
let style = this.getOrDefaultAttribute("style", "");
this.anchor = this.getOrDefaultAttribute("anchor", "center");
let fill = this.getOrDefaultAttribute("fill", false);
let alt = super.attribute("alt");
let path = this.attributes.get("src").replace("#_#", " ");
if (fill) {
if (fill === "cover")
style += "width:100%;height:100%;object-fit:cover;";
else if (fill.startsWith("wvmin")) {
let pct = fill.substring(5);
style += "width:" + pct + "vmin;";
} else if (fill.startsWith("hvmin")) {
let pct = fill.substring(5);
style += "height:" + pct + "vmin;";
} else if (fill === "height")
style += "max-height:100%;width:auto;";
else
style += "max-width:100%;max-height:100%;";
}
if (offset) {
//get percentage offset
let xy = offset.split(",");
style += "position:absolute;left:" + xy[0] + "px; top:" + xy[1] + "px;";
}
let root = null;
if (effect) {
root = document.createElement("span");
let effectVars = effect.split(";");
let effectType = effectVars[0];
let keyframes = document.createElement("style");
ModuleImage.imageIndex++;
switch (effectType) {
case "zoom":
// noinspection CssInvalidFunction
keyframes.innerHTML = "@keyframes zoom" + ModuleImage.imageIndex + " {\n" +
" from {transform: scale(1.0);}\n" +
" to {transform: scale(" + effectVars[1] + ");}\n" +
" }";
style += " width:100%;height:100%;" +
"object-fit:cover; " +
"animation-fill-mode:forwards;" +
"animation-timing-function:" + effectTiming + ";" +
"animation-name:zoom" + ModuleImage.imageIndex + ";" +
"animation-duration:" + effectVars[2] + "s;";
break;
case "move":
// noinspection CssInvalidFunction
let coordinates = effectVars[1].split(",");
let coordinatePairCount = coordinates.length / 2;
let kf = "@keyframes move" + ModuleImage.imageIndex + " {\n";
for (let i = 0; i < coordinatePairCount; ++i) {
let percentage = 100 * i / (coordinatePairCount - 1);
kf += percentage + "% {left: " + coordinates[i * 2] + "%; top: " + coordinates[i * 2 + 1] + "%;} \n";
}
kf += "}\n";
keyframes.innerHTML = kf;
if (this.anchor === "center")
style += "transform:translate(-50%,-50%);";
style +=
"position: relative;" +
"animation-fill-mode: forwards;" +
"animation-timing-function:" + effectTiming + ";" +
"animation-name:move" + ModuleImage.imageIndex + ";" +
"animation-duration:" + effectVars[2] + "s;";
if (effectVars[3])
style += "animation-iteration-count:" + effectVars[3] + ";";
if (effectVars[4])
style += "animation-direction:" + effectVars[4] + ";";
break;
default:
break;
}
root.appendChild(keyframes);
}
let filter = this.getOrDefaultAttribute("filter", "");
if (filter) {
filter = "filter:"+filter.replaceAll(";", " ")+";";
style += filter;
}
let image = DomElement.createWithStyle("img", "img-fluid", style);
image.setAttribute("src",path);
if(alt)
image.setAttribute("alt",alt);
if (root)
root.appendChild(image);
else
root = image;
return root;
}
writeDimensions(path) {
const img = new Image();
img.onload = function () {
const h = img.height;
const w = img.width;
let condition = encodeURIComponent("path LIKE '%" + path + "'");
let rowString = ajaxCallSync("query.php?action=update&table=media&fields=width,height&values=" + w + "," + h + "&condition=" + condition);
//alert("rowString=" + rowString);
// code here to use the dimensions
}
img.src = path;
}
}class ModuleMenu extends Module {
calculateLodHorizontal(spacing) {
const totalLodsizes = [0, 0, 0];
console.log("spacing:" + spacing + " tot:" + totalLodsizes + " " + this.children[0].getLodSizes().length + " " + this.widthPx);
for (let child of this.children) {
child.setSpacing(spacing);
let lodSizes = child.getLodSizes();
console.log("lodSizes=" + lodSizes);
for (let i = 0; i < lodSizes.length; ++i) {
totalLodsizes[i] += lodSizes[i] + spacing * 2;
}
}
console.log("tot:" + totalLodsizes + " w:" + this.widthPx);
for (let i = 0; i < totalLodsizes.length; ++i) {
if (totalLodsizes[i] < this.widthPx) {
//console.log(this.widthPx + " lod: " + i);
return i;
}
}
return totalLodsizes.length - 1;
}
calculateLodVertical(spacing) {
const maxLodsizes = [0, 0, 0];
console.log("spacing:" + spacing + " tot:" + maxLodsizes + " " + this.children[0].getLodSizes().length + " " + this.widthPx);
for (let child of this.children) {
child.setSpacing(spacing);
let lodSizes = child.getLodSizes();
console.log(lodSizes);
for (let i = 0; i < lodSizes.length; ++i) {
maxLodsizes[i] = Math.max(maxLodsizes[i], lodSizes[i] + spacing * 2);
}
}
console.log("tot:" + maxLodsizes + " w:" + this.widthPx);
for (let i = 0; i < maxLodsizes.length; ++i) {
if (maxLodsizes[i] < this.widthPx) {
//console.log(this.widthPx + " lod: " + i);
return i;
}
}
}
createTop() {
let div = DomElement.createStyled("div", "margin:auto");
let ul = DomElement.createWithStyle("ul", "navbar-nav", "flex-direction: row;align-items: center;justify-content: space-evenly");
this.appendChildren(ul);
div.appendChild(ul);
return div;
}
createLeft() {
let additionalClasses = this.sticky();
let ul = DomElement.create("ul", "nav nav-pills flex-column mb-auto" + additionalClasses);
this.appendChildren(ul);
return ul;
}
sticky() {
let additionalClasses = "";
if (ModuleNavbar.sticky) {
additionalClasses += " sticky-top";
}
return additionalClasses;
}
setChildSizes() {
let spacingPx = Number(this.getOrDefaultAttribute("spacing", "2")) / 100 * this.parent.widthPx;
let lod;
if (ModuleNavbar.horizontal) {
this.widthPx = this.parent.widthPx * 0.5;//TODO: at the moment we have hardcoded 50%-> find better solution
lod = this.calculateLodHorizontal(spacingPx);
} else {
lod = this.calculateLodVertical(spacingPx);
}
//alert(spacing * mm2px());
console.log("lod=" + lod);
if (lod < 0)
lod = 0;
for (let child of this.children) {
child.setLod(lod);
child.setSpacing(spacingPx);
}
}
create() {
this.setChildSizes();
if (ModuleNavbar.horizontal)
return this.createTop();
return this.createLeft();
}
}function uploadMigrationPackage(result, moduleMigration) {
moduleMigration.upload(result);
}
function uploadMigrationDone(result, moduleMigration) {
moduleMigration.uploadDone(result);
}
function findMigrationModule() {
let migrationModule = null;
if (pageModule) {
migrationModule = pageModule.findByType("ModuleMigration");
} else
alert("ERROR: there is no pageModule");
return migrationModule;
}
function runMigration(key, elementId) {
let migrationModule = findMigrationModule();
if (migrationModule) {
if (elementId) {//show the element the migration module is on, when there is no elementId, we assume the migrationModule is visible by default
let elem = document.getElementById(elementId);
elem.style.display = "block";
}
migrationModule.show(key);
} else
alert("ERROR: cannot find migrationModule");
}
function startMigration(key) {
let migrationModule = findMigrationModule();
if (migrationModule) {
migrationModule.run(key);
} else
alert("ERROR: cannot find migrationModule");
}
class ModuleMigration extends Module {
_key = "";
_progress = null;
_packagingLabel = "";
_packageUploadLabel = "";
_finishGotoLabel = "";
_root;
_checkmark = " ";
_button = null;
_startTime = 0;
addToResult(content, style="") {
let div = DomElement.createDiv("margin:3%;"+style);
div.innerHTML = content;
this._result.appendChild(div);
}
upload(result) {
if (result && result.length > 0) {
this.addToResult(result,"color:red;");//ERROR
this._button.style.display = "block";
this._progress.style.display = "none";
} else {
this.addToResult(this._checkmark);
this.addToResult(this.packageUploadLabel);
ajaxCallFuncParams('task.php?_task_=migration_uploadPackage&key=' + this._key, uploadMigrationDone, this);
}
}
uploadDone(result) {
if (result.includes("ERROR:")) {
this.addToResult(result,"color:red;");//ERROR
} else {
this.addToResult(this._checkmark);
let datetime = new Date().toLocaleString();
let duration = this._startTime - Date.now();
this.addToResult(datetime + " " + (duration/1000) +"s");
this.addToResult(this._finishGotoLabel + '' + result + ' ');
}
this._progress.style.display = "none";
this._button.style.display = "block";
}
show(key) {
this._button.setAttribute("onclick", "startMigration('" + key + "')");
}
run(key) {
this.resetResult();
this._key = key;
this._button.style.display = "none";
this._progress.style.display = "inline-block";
this.addToResult(this.packagingLabel);
this._startTime = Date.now();
ajaxCallFuncParams('task.php?_task_=migration_createPackage', uploadMigrationPackage, this);
}
resetResult() {
this._result.replaceChildren();
}
create() {
this.packagingLabel = this.getMultilangAttribute("packagingLabel", "Step 1: creating package");
this.packageUploadLabel = this.getMultilangAttribute("packageUploadLabel", "Step 2: package upload");
this._finishGotoLabel = this.getMultilangAttribute("finishedLabel", "Upload successful. To finish migration go to");
let buttonLabel = this.getLabel("Start");
this._root = DomElement.createDiv();
let progress = DomElement.createLoadingBlocks();
progress.id = "progress" + this.id;
progress.style.display = "none";
this._result = DomElement.createDiv();
this._root.appendChild(this._result);
this._root.appendChild(progress);
this._progress = progress;
let buttonAttributes = new Map();
buttonAttributes.set("label", buttonLabel);
let button = new ModuleButton(buttonAttributes);
this._button = button.create();
this._button.style.margin = "10% auto auto";
this._root.appendChild(this._button);
return this._root;
}
}class ModuleModal extends Module {
create() {
let opacity = this.getOrDefaultAttribute("opacity", "0.9");
let bg = "background-color:rgba(0,0,0," + opacity + ");"
let paddingPct = this.getOrDefaultAttribute("padding", "2");
let background = DomElement.createDiv("display:none;position:fixed;top:0;right:0;width:100%;height:100%;z-index:2000;" + bg);
background.setAttribute("onclick", "hideElement(this)");
let [vStyle, transformY] = this.getVerticalAlignment(paddingPct);
let [hStyle, transformX] = this.getHorizontalAlignment(paddingPct);
let transform = "";
if (transformX) {
if (transformY)
transform = "transform: translate(" + transformX + "," + transformY + ");";
else
transform = "transform: translateX(" + transformX + ");";
} else if (transformY)
transform = "transform: translateY(" + transformY + ");";
let alignment = vStyle + hStyle + transform;
let panel = DomElement.createWithStyle("span", "panel", "position:fixed;" + alignment);
panel.setAttribute("onclick", "event.stopPropagation();");//do not bubble up onclick or the modal will close
background.appendChild(panel);
this.appendChildren(panel);
return background;
}
getHorizontalAlignment(paddingPct) {
let halign = this.getOrDefaultAttribute("halign", "center");
let hStyle = "";
let transform = "";
switch (halign) {
case "left":
hStyle = "left:" + paddingPct + "%;";
break;
default:
case "center":
hStyle = "left:50%;";
transform = "-50%";
break;
case "right":
hStyle = "right:" + paddingPct + "%;";
break;
}
return [hStyle, transform];
}
getVerticalAlignment(paddingPct) {
let valign = this.getOrDefaultAttribute("valign", "center");
let vStyle = "";
let transform = "";
switch (valign) {
default:
case "middle":
vStyle = "top:50%;";
transform = "-50%";
break;
case "top":
vStyle = "top:" + paddingPct + "%;";
break;
case "bottom":
vStyle = "bottom:" + paddingPct + "%;";
}
return [vStyle, transform];
}
}class ModuleNavItem extends Module {
static navItemCounter = 0;
static navLinkFont = 0;
hasParentNavItem = false;
horizontal = true;
lod = 0;
spacing = 0;
setLod(lod) {
this.lod = lod;
}
setSpacing(spacing) {
this.spacing = spacing;
}
getLodSizes() {
if (ModuleNavItem.navLinkFont === 0) {
let test = document.createElement("div");
test.classList.add("nav-link");
document.body.appendChild(test);
ModuleNavItem.navLinkFont = getCanvasFont(test);
console.log("font=" + ModuleNavItem.navLinkFont);
document.body.removeChild(test);
}
let fontSize = ModuleNavItem.navLinkFont.split(" ")[1];
let iconSize = getTextWidth("SS", ModuleNavItem.navLinkFont);
let textWidth = getTextWidth(this.getLabel(), ModuleNavItem.navLinkFont) + this.spacing;//8 is space between icon and text
let shortLabel = this.getLabel().substring(0, 5).trim();
let shortTextWidth = getTextWidth(shortLabel, ModuleNavItem.navLinkFont) + this.spacing;
// noinspection UnnecessaryLocalVariableJS
let dropdownSpace = 0;
if (this.hasChildren())
dropdownSpace += 14 + 0.255 * parseInt(fontSize);
console.log("iconSize="+iconSize+"textWidth="+textWidth+"dropdownSpace="+dropdownSpace);
let sizes = [iconSize + textWidth + dropdownSpace, iconSize + shortTextWidth + dropdownSpace, iconSize + dropdownSpace];
return sizes;
}
setHasParentNavItem() {
this.hasParentNavItem = true;
}
create() {
ModuleNavItem.navItemCounter++;
if (this.hasParentNavItem) {
this.setSpacing(this.parent.spacing);
this.lod = this.parent.lod;
}
let liAdditionalClass = "";
let aAdditionalClass = "";
let li = document.createElement("li");
let a = document.createElement("a");
a.setAttribute("aria-expanded", "false");
if (this.hasChildren()) {
for (let child of this.children) {
try {
child.setHasParentNavItem();
} catch (e) {
//console.log("nav item child " + child.name + " does not have a setHasParentNavItem method!");
}
}
if (ModuleNavbar.horizontal) {
liAdditionalClass += " dropdown";
aAdditionalClass += " dropdown-toggle";
a.setAttribute("data-bs-toggle", "dropdown");
} else {
aAdditionalClass += " dropdown-toggle";
a.setAttribute("data-bs-toggle", "collapse");
}
}
//console.log("navItem lod=" + this.lod + " label=" + label);
let href = this.getOrDefaultAttribute("target", "");
let padding = this.spacing;
let {iconMargin, label} = this.setupLabel();
let icon = this.getOrDefaultAttribute("icon", "");
if (icon) {
icon = DomElement.createWithStyle("i", icon, "margin-right:" + iconMargin + "px;");
icon.setAttribute("role", "img");
icon.setAttribute("aria-label", label);
a.appendChild(icon);
}
if (this.hasParentNavItem) {
let align = "center";
if (!ModuleNavbar.horizontal)
align = "left";
li.setAttribute("style", "text-align:" + align);
a.setAttribute("class", "dropdown-item nav-link");
a.setAttribute("href", href);
} else {
if (!ModuleNavbar.horizontal) {
if (this.hasChildren()) {
href = "#child" + ModuleNavItem.navItemCounter;
}
}
if (ModuleNavbar.horizontal) {
liAdditionalClass += " text-center";
}
if (padding)
a.setAttribute("style", "padding:" + padding + "px");
else
li.setAttribute("style", "flex-grow:1");
li.setAttribute("class", "nav-item" + liAdditionalClass);
a.setAttribute("class", "nav-link" + aAdditionalClass);
a.setAttribute("href", href);
}
a.appendChild(document.createTextNode(label));
li.appendChild(a);
if (this.hasChildren()) {
let style;
let classAttr = "dropdown-menu"
if (!ModuleNavbar.horizontal) {
classAttr = "collapse";
style = "list-style:none;";
} else {
style = "position:absolute;right:0";
}
let ul = DomElement.createWithStyle("ul", classAttr, style);
ul.setAttribute("aria-labelledby","dropdown07XL");
ul.id = "child" + ModuleNavItem.navItemCounter;
this.appendChildren(ul);
li.appendChild(ul);
}
return li;
}
setupLabel() {
let iconMargin = this.spacing;
let label = this.getLabel("navitem");
switch (this.lod) {
case 1:
label = label.substring(0, 5);
break;
case 2:
label = "";
iconMargin = 0;
break;
default:
break;
}
return {iconMargin, label};
}
}class ModuleLanguageSelector extends Module {
create(){
let horizontalStyle = "";
let verticalStyle = "width:100%;text-align:center;"
let style = verticalStyle;
if (ModuleNavbar.horizontal)
style = horizontalStyle;
let root = document.createElement("span");
root.setAttribute("style","margin:auto;" + style);
for (let lang of ModuleSite.supportedLanguages) {
let languageStyle = "";
if (lang === ModuleSite.currentLanguage)
languageStyle = " border: 1px solid white;";
else
languageStyle = " filter:opacity(50%);";
let link = document.createElement("a");
link.setAttribute("onclick","switchLanguage('" + lang + "')");
let icon = DomElement.createWithStyle("img","rounded-1","padding:2px;height:calc(7px*var(--mm2px));" + languageStyle);
icon.setAttribute("src","language-icons/" + lang + ".svg");
icon.setAttribute("alt",lang);
link.appendChild(icon);
root.appendChild(link)
}
return root;
}
}class ModuleNavbar extends Module {
static height = 0;
static logoSize = 0;
static sticky = false;
static overlay = false;
static backgroundColorRGBA = "";
static horizontal = true;
remainingWidth = getCanvasWidth();
top() {
this.widthPx = this.parent.widthPx;
let size = this.getOrDefaultAttribute("size", 1);
let logoPadding = this.getOrDefaultAttribute("logo-padding", 1);
ModuleMenu.textColorRGBA = this.getOrDefaultAttribute("text-color", "");
ModuleNavbar.backgroundColorRGBA = this.getOrDefaultAttribute("background-color", "");
let opacity = this.getOrDefaultAttribute("opacity", "");
ModuleNavbar.minSize = this.attributes.get("min-size");
ModuleNavbar.height = window.innerHeight * size / 100;
let logoHeight = ModuleNavbar.height - window.innerHeight * logoPadding / 100;//minus logoPadding%
ModuleNavbar.logoSize = "height:" + logoHeight + "px; min-height:" + ModuleNavbar.minSize + "cm;";
let barSize = /*"height:" + ModuleNavbar.height + "px;*/" min-height:" + ModuleNavbar.minSize + "cm;";
// noinspection UnnecessaryLocalVariableJS
let stickyClass = "sticky-top";
if (ModuleNavbar.overlay) {
stickyClass = "fixed-top";
} else {
barSize += "width:100%;";
}
let additionalClasses = "";
if (ModuleNavbar.sticky) {
additionalClasses += " " + stickyClass;
}
let colors = "";
if (ModuleNavbar.textColorRGBA)
colors += "color:" + ModuleNavbar.textColorRGBA + ";";
//if (ModuleNavbar.backgroundColorRGBA)
colors += "background-color:rgba(var(--bgcolorRGB), " + opacity + ");"
// else if (opacity)
// colors += "opacity:" + opacity + ";";
let elem = DomElement.createWithStyle("nav", "navbar" + additionalClasses, barSize + "padding-top:0;padding-bottom:0;" + colors);
elem.setAttribute("aria-label", "navbar");
this.appendChildren(elem);
return elem;
}
getPadding() {
return this.paddingPx;
}
left() {
ModuleNavbar.horizontal = false;
let size = this.attributes.get("size").replace("%", "");
let minSizeCm = this.getOrDefaultAttribute("min-size", "1");
let paddingPct = this.getOrDefaultAttribute("padding", "0.5");
let minSizePx = minSizeCm * 10 * mm2px;
//alert(this.parent.widthPx + " / " + this.parent.widthPx * size / 100 + " / " + minSizePx);
this.widthPx = Math.max(this.parent.widthPx * size / 100, minSizePx);
this.paddingPx = getCanvasWidth() * paddingPct / 100;//padding = paddingPct of full canvas
this.remainingWidth = this.parent.widthPx - this.widthPx - this.paddingPx * 2;
console.log("navbar left: " + this.paddingPx + " / " + this.remainingWidth + " width: " + this.widthPx);
let additionalClasses = "";
if (ModuleNavbar.sticky) {
additionalClasses += " sticky-top";
}
let height = window.innerHeight + "px";
for (let child of this.children) {
if (child.languageTest())
child.widthPx = this.widthPx - this.paddingPx * 2;
}
let root = DomElement.createWithStyle("div", "d-flex flex-column" + additionalClasses, "height:" + height + ";flex:0 0 auto;width:" + this.widthPx + "px;padding:" + this.paddingPx + "px;margin:0;");
this.appendChildren(root);
return root;
}
getRemainingWidth() {
return this.remainingWidth;
}
create() {
let position = this.attributes.get("position");
switch (position) {
default:
case "top": {
ModuleNavbar.sticky = this.getBooleanAttribute("sticky", true);
ModuleNavbar.overlay = this.getBooleanAttribute("overlay", true);
return this.top();
}
case "left":
ModuleNavbar.sticky = this.getBooleanAttribute("sticky", false);
ModuleNavbar.overlay = this.getBooleanAttribute("overlay", false);
return this.left();
}
}
}class ModuleRangeBar extends Module {
createInternal(create) {
let thickness = this.getOrDefaultAttribute("thickness", "10");
let range = this.getOrDefaultAttribute("range", "0;100");
let value = this.getOrDefaultAttribute("value", "0");
let colorVar = this.getOrDefaultAttribute("color", "--color1");
let border = this.getOrDefaultAttribute("border", "0,#000000");
let barMargin = this.getOrDefaultAttribute("barMargin", "8");
let padding = this.usePadding();
let label = this.getLabel("");
border = border.split(";");
let borderSize = border[0];
let borderColor = border[1];
let color = "background-color:var(" + colorVar + ");"
let borderStyle = "";
if (borderSize > 0) {
borderStyle = "border:" + borderSize + "px solid " + borderColor + ";";
}
let minmax = range.split(";");
let min = minmax[0];
let max = minmax[1];
let width = 100 * value / (max - min);
if (create) {
let root = document.createElement("div");
root.setAttribute("style", padding);
let labelAndValueContainer = DomElement.createStyled("div", "position:relative;overflow:hidden;");
let labelAndPercentage = DomElement.createStyled("div", "position:relative;overflow:hidden;");
let labelSpan = DomElement.createStyled("span", "float:left");
labelSpan.innerHTML = label;
labelAndPercentage.appendChild(labelSpan);
let valueElem = DomElement.createStyled("span", "float:right");
valueElem.innerHTML = value + '%';
labelAndPercentage.appendChild(valueElem);
labelAndValueContainer.appendChild(labelAndPercentage);
let barBorder = DomElement.createWithStyle("div", "progress", 'margin-top:' + barMargin + 'px;margin-bottom:' + barMargin + 'px;height:' + thickness + 'px;' + borderStyle)
let barInner = DomElement.createWithStyle("div", "progress-bar", 'width:' + width + '%;' + color);
barInner.setAttribute("aria-valuenow", value);
barInner.setAttribute("aria-valuemin", min);
barInner.setAttribute("aria-valuemax", max);
barBorder.appendChild(barInner);
root.appendChild(labelAndValueContainer);
root.appendChild(barBorder);
return root;
} else {
let html =
'' +
'
' + label + ' ' + value + '%
' +
'
' +
'
';
return html;
}
}
create() {
return this.createInternal(true);
}
render() {
return this.createInternal(false);
}
}
class ModuleSiteEditor extends Module {
_interval = 0;
_parseResult = {};
_lastContent = 0;
editor = new CodeEditor(new SiteScriptParser());
start() {
this.editor.init();
super.start();
}
create(){
this.editor.setKey(ModulePage.current.param);
let root = document.createElement("div");
let heading = document.createElement("h3");
heading.innerHTML = this.getTitle("Site Editor");
root.appendChild(heading);
let editor = document.createElement("div");
editor.innerHTML = this.editor.render();
root.appendChild(editor);
return root;
}
render() {
this.editor.setKey(ModulePage.current.param);
let html = ``+this.getTitle("Site Editor")+` `;
html += this.editor.render();
return html;
}
}class ModuleSlider extends Module {
init() {
this.registerMandatoryAttributes(["value", "label"]);
}
createInternal(create) {
let height = this.getOrDefaultAttribute("height", "50");
let itemHeight = height * mm2px;
let startslide = parseInt(this.getOrDefaultAttribute("startslide", "0"));
let distribute = this.getOrDefaultAttribute("distribute", false);
let arrows = this.getOrDefaultAttribute("arrows", false);
let indicators = this.getOrDefaultAttribute("indicators", "true");
let interval = this.getOrDefaultAttribute("interval", "3000");
let slides = [];
if (distribute && distribute !== "false") {
let sum = 0;
//check how many items fit in one slide
for (let i = 0; i < this.children.length; ++i) {
let paddingPct = 1;
let child = this.children[i];
this.children[i].attributes.set("padding", paddingPct.toString());
let paddingPx = paddingPct / 100 * this.widthPx;
let w = child.getWidthPx(itemHeight) + paddingPx * 2;
sum = sum + w;
//alert(i + " sum=" + sum + " w=" + w + " this.widthPx=" + this.widthPx + " " + child.name);
if (sum > this.widthPx) {
slides.push(i);
sum = w;
}
}
slides.push(this.children.length);
if (startslide > slides.length)
startslide = 0;
} else {
for (let i = 0; i < this.children.length; ++i)
slides.push(i);
}
let indicatorsId = "indicators_" + this.id;
if (create) {
let root = DomElement.create("div", "carousel slide");
root.setAttribute("data-bs-ride", "carousel");
root.setAttribute("data-bs-interval", interval);
this.appendInner(slides, startslide, itemHeight, root);
this.appendIndicators(indicators, slides, root, indicatorsId, startslide);
if (arrows && slides.length > 1) {
let arrowPrev = DomElement.create("a", "carousel-control-prev");
arrowPrev.setAttribute("data-bs-target", "#" + indicatorsId);
arrowPrev.setAttribute("role", "button");
arrowPrev.setAttribute("data-bs-slide", "prev");
let prevIcon = DomElement.create("span", "carousel-control-prev-icon");
prevIcon.setAttribute("aria-hidden", "true");
arrowPrev.appendChild(prevIcon);
root.appendChild(arrowPrev);
let arrowNext = DomElement.create("a", "carousel-control-next");
arrowNext.setAttribute("data-bs-target", "#" + indicatorsId);
arrowNext.setAttribute("role", "button");
arrowNext.setAttribute("data-bs-slide", "next");
let nextIcon = DomElement.create("span", "carousel-control-next-icon");
nextIcon.setAttribute("aria-hidden", "true");
arrowNext.appendChild(nextIcon);
root.appendChild(arrowNext);
}
return root;
} else {
let html =
'';
if (indicators && slides.length > 1) {
html += '
';
for (let i = 0; i < slides.length; ++i) {
let active = "";
if (i === startslide) {
active = 'class="active" aria-current="true"';
}
html += ' ';
}
html += '
';
}
html += '
';
//alert(slides);
let start = 0;
for (let b = 0; b < slides.length; ++b) {
let active = "";
if (b === startslide)
active = " active";
html += '
';
for (let i = start; i < slides[b]; ++i)
html += this.children[i].render();
start = slides[b];
html += '
';
}
html += '
';
if (arrows && slides.length > 1) {
html += '
' +
' ' +
//'Previous ' +
' ' +
'
' +
' ' +
// 'Next ' +
' ';
}
html += '
';
return html;
}
}
appendInner(slides, startslide, itemHeight, root) {
let inner = DomElement.create("div", "carousel-inner");
//alert(slides);
let start = 0;
for (let b = 0; b < slides.length; ++b) {
let active = "";
if (b === startslide)
active = " active";
let item = DomElement.createWithStyle("div", "carousel-item" + active, "height:" + itemHeight + "px");
for (let i = start; i < slides[b]; ++i)
item.appendChild(this.children[i].create());
start = slides[b];
inner.appendChild(item);
}
root.appendChild(inner);
}
appendIndicators(indicators, slides, root, indicatorsId, startslide) {
if (indicators && slides.length > 1) {
let indicators = DomElement.create("div", "carousel-indicators");
root.appendChild(indicators);
for (let i = 0; i < slides.length; ++i) {
let button = document.createElement("button");
button.setAttribute("type", "button");
button.setAttribute("data-bs-target", "#" + indicatorsId);
button.setAttribute("data-bs-slide-to", i.toString());
if (i === startslide) {
button.setAttribute("class", "active");
button.setAttribute("aria-current", "true");
}
indicators.appendChild(button);
}
}
}
create() {
return this.createInternal(true);
}
render() {
return this.createInternal(false);
}
}class ModuleSliderItem extends Module {
create() {
let padding = this.usePadding();
let item = document.createElement("span");
item.setAttribute("style", padding);
this.appendChildren(item);
return item;
}
render() {
this.usePadding();
let html = '' + this.renderChildren() + ' ';
return html;
}
}
class ModuleSubmit extends Module {
create() {
let button = DomElement.create("button","btn btn-outline-secondary");
button.setAttribute("type", "button");
button.setAttribute("onclick", "submitForm(this);");
let label = this.getLabel("Submit");
button.innerHTML = label;
let fill = this.getOrDefaultAttribute("fill", "width");
if (fill === "width")
button.setAttribute("style", "width:100%;");
return button;
}
}class ModuleTabs extends Module {
static index = 0;
create() {
let root = document.createElement("span");
let navTab = DomElement.create("ul", "nav nav-tabs");
navTab.id = "tabs" + ModuleTabs.index;
navTab.setAttribute("role", "tablist");
let content = DomElement.create("div", "tab-content");
content.id = "tabContent" + ModuleTabs.index;
//all children must be of type ModuleTab
let panes = "";
let i = 0;
for (let child of this.children) {
if (i == 0)
child.setActive(true);
child.widthPx = this.widthPx;
navTab.appendChild(child.create());
content.appendChild(child.createContent());
++i;
}
root.appendChild(navTab);
root.appendChild(content);
++ModuleTabs.index;
return root;
}
}class ModuleTab extends Module {
active = false;
buttonId = "";
buttonTarget = "";
label = "";
setActive(active) {
this.active = "";
if (active)
this.active = "active";
}
createContent() {
let paneActive = "";
if (this.active)
paneActive = this.active + " show";
let pane = DomElement.create("div","tab-pane fade " + paneActive);
pane.id = this.tabId;
pane.setAttribute("role","tabpanel");
pane.setAttribute("aria-labelledby",this.buttonId);
this.appendChildren(pane);
return pane;
}
getContent() {
let paneActive = "";
if (this.active)
paneActive = this.active + " show";
let pane = ``;
pane += this.renderChildren();
pane += "
";
return pane;
}
create() {
this.tabId = "tab" + this.id;
this.label = this.getLabel(this.tabId);
this.buttonId = this.tabId + '-tab';
let li = DomElement.create("li", "nav-item");
li.setAttribute("role", "presentation");
let button = DomElement.create("button", "nav-link " + this.active);
button.id = this.buttonId;
button.setAttribute("data-bs-toggle", "tab");
button.setAttribute("data-bs-target", "#" + this.tabId);
button.setAttribute("type", "button");
button.setAttribute("role", "tab");
button.setAttribute("aria-controls", this.tabId);
button.setAttribute("aria-selected", "true");
button.innerHTML = this.label;
li.appendChild(button);
return li;
}
render() {
this.tabId = "tab" + this.id;
this.label = this.getLabel(this.tabId);
this.buttonId = this.tabId + '-tab';
let html = `
` + this.label + `
`;
return html;
}
}class ModuleTask extends Module {
create(){
let root = document.createElement("span");
root.innerHTML = this.render();
return root;
}
render() {
let elem = document.getElementById(this.id);
if (elem)
return elem.innerHTML;
return "";
}
}class ModuleTitle extends Module {
create() {
let title = this.getLabel("");
let subtitle = this.getMultilangText();
let padding = this.usePadding();
let root = document.createElement("span");
if (padding)
root = DomElement.createStyled("span", padding);
let heading = document.createElement("h1");
let dot = this.getOrDefaultAttribute("dot", "");
if (dot) {
let dotStyle = this.createDotStyle(dot);
let dotElement = DomElement.createStyled("span", dotStyle);
heading.appendChild(dotElement);
let span = document.createElement("span");
span.innerHTML = title;
heading.appendChild(document.createTextNode(title));
}
root.appendChild(heading);
let subHeading = DomElement.createStyled("i", "color:#888888;");
subHeading.innerHTML = subtitle;
root.appendChild(subHeading);
return root;
}
createDotStyle(dot) {
let dotVars = dot.split(";");
let size = dotVars[0] * mm2px;
let radius = "";
if (dotVars[1])
radius = "border-radius:" + dotVars[1] * mm2px + "px;";
let colorVar = "var(--color1)";
if (dotVars[2])
colorVar = "var(" + dotVars[2] + ")";
let bottom = size - 2;
let dotStyle = 'border-style: solid;' + radius +
' border-bottom-width: ' + bottom + 'px;' +
' border-color: ' + colorVar + ';' +
' width: ' + size + 'px;' +
' margin: 0 10px;' +
' display:inline-block;';
return dotStyle;
}
}class ModuleToolbar extends Module {
create() {
let div = DomElement.createWithStyle("div", "toolbar", "position:absolute;display:none;");
this.appendChildren(div);
return div;
}
render() {
let html = ''
+ this.renderChildren() + '
';
return html;
}
}class ModuleToolbarButton extends Module {
createInternal(create){
let title = this.getTitle("");
let icon = this.getOrDefaultAttribute("icon", "bi-exclamation-triangle");
let action = this.getOrDefaultAttribute("action", "");
if(create){
let i = DomElement.create("i", icon);
i.setAttribute("title",getLanguageText(title));
i.setAttribute("onclick",action);
return i;
}else {
let html = ' ';
return html;
}
}
create(){
return this.createInternal(true);
}
render() {
return this.createInternal(false);
}
}class ModuleUploadForm extends Module {
create() {
let label = this.getLabel("Upload");
let target = this.getOrDefaultAttribute("target","");
let form = document.createElement("form");
form.setAttribute("method","post");
form.setAttribute("encType","multipart/form-data");
let fields = document.createElement("div","mb-3");
let labelElem = DomElement.create("label","form-label");
labelElem.setAttribute("htmlFor","formFile") ;
labelElem.innerHTML = label;
fields.appendChild(labelElem);
let fileField = DomElement.create("input","form-control");
fileField.setAttribute("type","file");
fileField.setAttribute("name","fileToUpload");
fileField.setAttribute("multiple","multiple");
fileField.id = "fileToUpload";
let accept = this.getOrDefaultAttribute("accept",false);
if(accept)
fileField.setAttribute("accept",accept);
fields.appendChild(fileField);
form.appendChild(fields);
let hidden = DomElement.create("input","form-control");
hidden.setAttribute("type","hidden");
hidden.setAttribute("name","target");
hidden.setAttribute("value",target);
form.appendChild(hidden);
let butCol = document.createElement("div","col-auto");
let button = DomElement.create("button","btn btn-primary mb-3");
button.setAttribute("type","submit");
button.setAttribute("name","submit");
button.innerHTML = label;
butCol.appendChild(button);
form.appendChild(butCol);
return form;
}
render() {
let accept = this.getOrDefaultAttribute("accept",false);
if(accept)
accept = 'accept="'+accept+'"';
let label = this.getLabel("Upload");
let target = this.getOrDefaultAttribute("target","");
let html = ``;
return html;
}
}class ModuleUserThumb extends Module {
static username = "";
static imageUrl = "resources/ewok.svg";
create() {
let root = DomElement.createStyled("div", "font-size: calc(7px * var(--mm2px));");
let a = DomElement.create("a", "text-decoration-none");
a.setAttribute("href", "?user");
a.id = "dropdownUser1";
let img = DomElement.createWithStyle("img", "rounded-circle", "object-fit:cover;");
img.setAttribute("src", ModuleUserThumb.imageUrl);
img.setAttribute("alt", ModuleUserThumb.username);
img.setAttribute("width", "32");
img.setAttribute("height", "32");
a.appendChild(img);
let b = document.createElement("span");
b.innerHTML = " " +ModuleUserThumb.username+" ";
a.appendChild(b);
root.appendChild(a);
let signout = document.createElement("a");
signout.setAttribute("href", "?signout=1");
signout.setAttribute("title", "signout");
let icon = DomElement.create("i","bi-box-arrow-right");
signout.appendChild(icon);
root.appendChild(signout);
return root;
}
}class ModuleVideo extends Module {
create() {
let src = this.getOrDefaultAttribute("src", "");
let video = document.createElement("video");
video.src = src;
video.controls = true;
video.autoplay = true;
video.muted = true;
video.loop = true;
video.preload = "auto";
video.innerHTML = 'Cannot play html5 video ' + src;
video.setAttribute("width","100%");
return video;
}
render() {
let src = this.getOrDefaultAttribute("src", "");
let html =
'' +
'Cannot play html5 video' +
' ';
return html;
}
}class ModulePage extends Module {
static current = 0;
menu = 0;
footer = 0;
param = 0;
site = 0;
setParam(pageParam) {
this.param = pageParam;
}
resolve(rootModule) {
//getMenu by menuid
this.site = rootModule.findByType("ModuleSite");//contains title, favicon...
this.menu = rootModule.findById(this.getOrDefaultAttribute("menuid", ""));
this.footer = rootModule.findById(this.getOrDefaultAttribute("footerid", ""));
this.globals = rootModule.findByType("ModuleGlobals");
}
create() {
ModulePage.current = this;
let root = document.createElement("div");
this.site.create();
let pageParent = root;
let container = DomElement.createStyled("div","display: flex; height:100%");
if(this.globals)
root.appendChild(this.globals.create());
root.appendChild(container);
if (this.menu) {
let navbar = this.getOrDefaultAttribute("navbar", false);
if(navbar)
{
//console.log("custom navbar attribs = "+navbar);
let navbarAttributes = navbar.split(";");
for (let attrib of navbarAttributes) {
let keyValue = attrib.split(":");
this.menu.attributes.set(keyValue[0], keyValue[1]);
//console.log("custom menu attribs = %o",this.menu.attributes);
}
}
container.appendChild(this.menu.create());
if(!ModuleNavbar.horizontal)
{
let rem = this.menu.getRemainingWidth();
let paddingPx = this.menu.getPadding();
let contentColumn = DomElement.createWithStyle("div","col", "width:" + rem + "px;margin-right:0;padding:" + paddingPx + "px;overflow:auto;max-height:100%");
container.appendChild(contentColumn);
pageParent = contentColumn;
}
}
this.widthPx = this.menu.getRemainingWidth();
let page = document.createElement("main");
this.file = this.getOrDefaultAttribute("file", "");
if (this.file) {
if (this.param)
this.file += "?" + this.param;
let loader = DomElement.create("div", "loader");
let fileContent = document.createElement("div");
fileContent.setAttribute("style","height:900px");//TODO: why 900? make dynamic?
fileContent.id = "fileContent";
fileContent.appendChild(loader);
page.appendChild(fileContent);
ajaxCallFunc(this.file, "fileContent", activateTooltips);
} else {
this.appendChildren(page);
}
pageParent.appendChild(page);
if (this.footer)
pageParent.appendChild(this.footer.create());
return root;
}
}
class ModuleSite extends Module {
static supportedLanguages = 0;
static currentLanguage = "de";
static darkTheme = true;
page = null;
init(){
this.widthPx = getCanvasWidth();
}
start(){
if(this.page)
this.page.start();
}
createPage(pageId, pageParam){
this.page = this.findById(pageId);//should deliver ModulePage object
let pageRoot = document.createElement("h1");
if(!document.body)
document.body = document.createElement("body");
pageRoot.innerHTML = "ERROR: Cannot render page " + pageId;
if (this.page) {
console.log("pageId: " + pageId + " page: " + this.page.name + " param: " + pageParam);
this.page.setParam(pageParam);
this.page.resolve(this);
pageRoot = this.page.create();
}
let pageModule = this.page;
return {pageRoot,pageModule};
}
create() {
let title = "" + this.getMultilangAttribute("title", "Ewok CMS") + " ";
let favicon = ' ';
ModuleSite.supportedLanguages = this.getOrDefaultAttribute("languages","").split(",");
let html = title + favicon;
document.head.innerHTML = document.head.innerHTML + html;
return "";
}
}/**
* Created by roulio on 13.12.2022.
*/
String.prototype.insertAt = function (index, string) {
return this.substring(0, index) + string + this.substring(index);
}
String.prototype.splice = function (start, end, replacement) {
return this.substring(0, start) + replacement + this.substring(end);
}
const classesMapping = new Map([
["brand", ModuleBrand],
["button", ModuleButton],
["card", ModuleCard],
["col", ModuleColumn],
["content", ModuleContent],
["data", ModuleData],
["feature", ModuleFeature],
["features", ModuleFeatures],
["file", ModuleFile],
["fileGrid", ModuleFileGrid],
["fontSelector", ModuleFontSelector],
["footer", ModuleFooter],
["form", ModuleForm],
["globals", ModuleGlobals],
["image", ModuleImage],
["input", ModuleInputField],
["map", ModuleMap],
["migration", ModuleMigration],
["option", ModuleOption],
["languageSelector", ModuleLanguageSelector],
["layer", ModuleLayer],
["menu", ModuleMenu],
["modal", ModuleModal],
["navbar", ModuleNavbar],
["navItem", ModuleNavItem],
["page", ModulePage],
["postEditor", ModulePostEditor],
["product", ModuleProduct],
["range", ModuleRangeBar],
["row", ModuleRow],
["search",ModuleSearch],
["section",ModuleSection],
["site", ModuleSite],
["siteEditor", ModuleSiteEditor],
["slider", ModuleSlider],
["sliderItem", ModuleSliderItem],
["submit", ModuleSubmit],
["tabs", ModuleTabs],
["tab", ModuleTab],
["task", ModuleTask],
["title", ModuleTitle],
["toolbar", ModuleToolbar],
["toolbarButton", ModuleToolbarButton],
["uploadForm", ModuleUploadForm],
["userThumb", ModuleUserThumb],
["video", ModuleVideo]
]);