let bgMap = new Map();
let iconElement = Element.createWithStyle("i", "bi-x-square", "padding:1%;");
let layerElement = document.createElement("div");
layerElement.appendChild(iconElement);
function highlight(element) {
bgMap.set(element, element.style.border);
element.style.border = "thick solid #ffff00";
//obj.style.backgroundColor = "yellow";
}
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 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?
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 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 = Element.createWithStyle("div", "card", 'height:100%;color:' + color + ';border-color: ' + borderColor + ';background-color:' + backgroundColor + ';');
if (header) {
let headerDiv = Element.createWithContent("div", "card-header", header);
div.appendChild(headerDiv);
}
let image = Element.create("img", "card-img-top");
image.setAttribute("src", imagePath);
div.appendChild(image);
let body = this.createBody();
div.appendChild(body);
if (footer) {
let footerDiv = Element.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 = Element.create("div", "card-body");
let cardTitle = Element.createWithContent("h3", "card-title", title);
body.appendChild(cardTitle);
if (subtitle) {
let cardSubtitle = Element.createWithContent("h4", "card-subtitle", subtitle);
body.appendChild(cardTitle);
}
let cardText = Element.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;
}
}
}
// }
}
}class ModuleDbTable extends Module {
create() {
let dbTableName = this.getOrDefaultAttribute("id", "");
let fields = this.getOrDefaultAttribute("fields", "");
const encoded = encodeURIComponent(he.encode(fields));
let rowsString = ajaxCallSync("query.php?action=readall&table=" + dbTableName + "&fields=" + encoded);
let rows = rowsString.split(";;").filter(Boolean);
let table = Element.create("table","table table-dark table-striped table-hover");
for (let row of rows) {
let tr = document.createElement("tr");
let items = row.split(",,");
for (let item of items) {
let td = document.createElement("td");
td.innerHTML = item;
tr.appendChild(td);
}
table.appendChild(tr);
}
return table;
}
}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 = Element.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 thumbSize = this.getOrDefaultAttribute("thumbSize", "30");
let paddingMm = this.getOrDefaultAttribute("paddingMm", "1");
let div = Element.createWithStyle("div", "col text-center", "padding:" + paddingMm + "mm;flex:0 0 " + thumbSize + "mm;");
let image = Element.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 + "',dummy,0);");
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 = Element.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 = Element.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 += "";
}
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 = Element.createWithStyle("footer", classes,heightStyle + opacity);
this.appendChildren(footer);
root.appendChild(footer);
return root;
}
}
class ModuleForm extends Module {
create(){
let task = this.getOrDefaultAttribute("task", "");
let form = document.createElement("form");
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 ModuleInputField extends Module {
static helpTextCounter = 0;
create() {
let additionalInputAttributes = new Map();
let id = this.getOrDefaultAttribute("id", "");
let name = this.getOrDefaultAttribute("name", "");
let data = this.getOrDefaultAttribute("data", "");
let value = this.getOrDefaultAttribute("value", "");
if (name)
additionalInputAttributes.set('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 = Element.create("div", "form-group");
this.appendLabel(root);
this.appendPrepend(root);
switch (type) {
default:
let input = Element.create("input", "form-control");
input.id = id;
input.setAttribute("type", type);
input.setAttribute("placeholder", placeholder);
for (let [key, value] of additionalInputAttributes) {
input.setAttribute(key, value);
}
root.appendChild(input);
break;
case "select":
let select = Element.create("select", "form-control");
select.id = id;
this.appendChildren(select)
root.appendChild(select);
break;
}
if (helpText) {
let help = Element.create("small", "form-text text-muted");
help.id = helpTextId;
help.innerHTML = helpText;
root.appendChild(help);
}
return root;
}
appendPrepend(root) {
let prepend = this.getMultilangAttribute("prepend", "");
if (prepend) {
let prep = Element.create("div", "input-group-prepend");
let text = Element.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;
}
render() {
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 html = '
' + '-' + 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", ""); let url = window.location.protocol + "//" + window.location.host; this.folderUrl = url + "/products/" + folder + "/"; console.log("productUrl = " + this.folderUrl); let jsonPath = this.folderUrl + "product.json"; let container = Element.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 = Element.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 = '' + name.toUpperCase() + '
'; } else html += name; let span = document.createElement("span"); span.innerHTML = html; link.appendChild(span); } return link; } }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 = Element.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 = Element.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 * 0.5; 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 = Element.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(create) { 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 + ";"; if (create) { let elem = Element.createWithStyle("nav", "navbar" + additionalClasses, barSize + "padding-top:0;padding-bottom:0;" + colors); elem.setAttribute("aria-label", "navbar"); this.appendChildren(elem); return elem; } else { let html = ""; //add spacer // if (ModuleMenu.sticky) { // html += ""; // } let dims = this.getHtmlDimensions(html); console.log("navbar dims=" + dims.width + " x " + dims.height); return html; } } getPadding() { return this.paddingPx; } left(create) { 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; } if (create) { let root = Element.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; } else { let html = '