272 lines
11 KiB
JavaScript
272 lines
11 KiB
JavaScript
class TemplateEngine
|
|
{
|
|
constructor(tpl, adminData) {
|
|
this.tpl = tpl;
|
|
this.adminData = adminData;
|
|
this.modeFallback = {
|
|
":EDIT": ":ADMIN",
|
|
":ADMIN": "",
|
|
"": null
|
|
};
|
|
}
|
|
|
|
Render(content, marker, mode = "") {
|
|
return this.render(marker, content, this.extractSection(this.tpl, marker, mode), mode);
|
|
}
|
|
|
|
render(sections, content, tpl, mode = "") {
|
|
// content has to be an object of `marker` => `data`
|
|
// data can be a primitive, an array, or an object
|
|
// Boolean corresponds to a conditional section, call replaceCondition
|
|
// String and number correspond to a simple marker, call replaceMarker for it
|
|
// Array and object always correspond to a section
|
|
// Object can be handed recursively to a new instance of render. Be sure to extract the correct section of tpl though
|
|
// If data is an array, iterate over it and hand every entry to a new instance like with object. Concatenate the results
|
|
if (typeof(content)!="object" || tpl=="")
|
|
return "";
|
|
var ret = tpl;
|
|
for (var marker in content) {
|
|
var sectionMarker = (sections!="" ? sections+"."+marker : marker);
|
|
var type = typeof(content[marker]);
|
|
|
|
if (mode==":EDIT" && sections.indexOf("MAIN")!=-1 && typeof(this.adminData[marker])!="undefined") {
|
|
var data = this.adminData[marker];
|
|
var admin_tpl = this.extractSection(ret, sectionMarker, mode);
|
|
var admin_ret = "";
|
|
for (var i in data) {
|
|
if (content[marker]==null) {
|
|
data[i].SELECTED = data[i].ID=="__NULL__";
|
|
} else if (Array.isArray(content[marker])) {
|
|
var selected = false;
|
|
for (var j in content[marker]) {
|
|
if (data[i].ID==content[marker][j].ID) {
|
|
selected = true;
|
|
}
|
|
}
|
|
data[i].SELECTED = selected;
|
|
} else {
|
|
data[i].SELECTED = data[i].ID==content[marker].ID;
|
|
}
|
|
|
|
admin_ret+= this.render(sectionMarker, data[i], admin_tpl, "");
|
|
}
|
|
ret = this.replaceSection(ret, sectionMarker, admin_ret, "");
|
|
} else {
|
|
if (content[marker]==null) {
|
|
ret = this.replaceSection(ret, sectionMarker, "", mode);
|
|
ret = this.replaceMarker(ret, sectionMarker, "__NULL__", mode);
|
|
} if (type=="boolean") {
|
|
ret = this.replaceCondition(ret, sectionMarker, content[marker], mode);
|
|
} else if (type=="string" && "__CASE__:"==content[marker].substr(0, 9)) {
|
|
ret = this.replaceSwitch(ret, sectionMarker, content[marker].split(":"), mode);
|
|
} else if (type=="string" || type=="number") {
|
|
ret = this.replaceMarker(ret, sectionMarker, content[marker], mode);
|
|
} else if (type=="object" && Array.isArray(content[marker])) {
|
|
var arr_tpl = this.extractSection(ret, sectionMarker, mode);
|
|
var arr_ret = "";
|
|
for (var ind in content[marker]) {
|
|
arr_ret+= this.render(sectionMarker, content[marker][ind], arr_tpl, mode);
|
|
}
|
|
ret = this.replaceSection(ret, sectionMarker, arr_ret, mode);
|
|
} else if (type=="object") { // Hopefully a JSON
|
|
ret = this.replaceSection(ret, sectionMarker, this.render(sectionMarker, content[marker], this.extractSection(ret, sectionMarker, mode), mode), mode);
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
makeMarker(marker) {
|
|
return "###" + marker + "###";
|
|
}
|
|
|
|
makeSectionMarker(marker, pos) { //creates START or END marker of section
|
|
return "<!-- " + marker + " " + pos + " -->";
|
|
}
|
|
|
|
findMarkerModes(tpl, section) {
|
|
var ret = [];
|
|
var sectionMarker = this.makeMarker(section+":");
|
|
var markerLength = 5 + sectionMarker.length - 3; // 5 = "<!-- ", 3 = "###"
|
|
var anyMarker = this.makeSectionMarker(sectionMarker, "").substr(0, markerLength);
|
|
for (var startIndex = 0; startIndex < tpl.length;) {
|
|
var foundIndex = tpl.indexOf(anyMarker, startIndex);
|
|
if (foundIndex==-1) {
|
|
startIndex = tpl.length;
|
|
} else {
|
|
var markerEnd = tpl.indexOf("###", foundIndex + markerLength);
|
|
var mode = tpl.substr(foundIndex + markerLength - 1, markerEnd - foundIndex - markerLength + 1); // Should be e.g. :EDIT
|
|
var endMarker = this.makeSectionMarker(this.makeMarker(section + mode), "END");
|
|
var endPos = tpl.indexOf(endMarker, markerEnd);
|
|
if (endPos==-1) {
|
|
startIndex = tpl.length;
|
|
} else {
|
|
startIndex = endPos + markerLength;
|
|
ret.push(mode);
|
|
} } }
|
|
return ret;
|
|
}
|
|
|
|
deleteSection(tpl, section) {
|
|
var sectionMarker = this.makeMarker(section);
|
|
var anyMarker = this.makeSectionMarker(sectionMarker, "").substr(0, 5 + sectionMarker.length); // 5 = "<!-- "
|
|
var endMarker = this.makeSectionMarker(sectionMarker, "END");
|
|
var startPos = tpl.indexOf(anyMarker);
|
|
var endPos = tpl.lastIndexOf(endMarker) + endMarker.length;
|
|
if ((startPos==-1) || (endPos==-1) || (endPos<=startPos)) {
|
|
return tpl;
|
|
}
|
|
var ret = (startPos>0 ? tpl.substr(0, startPos) : "");
|
|
if (endPos<tpl.length) ret+= tpl.substr(endPos);
|
|
return ret;
|
|
}
|
|
|
|
findAndDeleteUnneededModes(tpl, section, mode = "") {
|
|
var ret = tpl;
|
|
// Find and destroy unneeded mode siblings
|
|
var modes = this.findMarkerModes(tpl, section);
|
|
var chosenMode = mode;
|
|
while (!modes.includes(chosenMode) && this.modeFallback[chosenMode]!=null) {
|
|
chosenMode = this.modeFallback[chosenMode];
|
|
}
|
|
for (var i in modes) {
|
|
if (modes[i]!=chosenMode) {
|
|
ret = this.deleteSection(ret, section + modes[i]);
|
|
}
|
|
}
|
|
if (chosenMode!="") {
|
|
ret = this.deleteSection(ret, section);
|
|
}
|
|
return {"tpl": ret, "mode": chosenMode};
|
|
}
|
|
|
|
extractSection(tpl, section, mode = "") { //Extracts a section from a template
|
|
var sectionMarker = this.makeMarker(section + mode);
|
|
var startMarker = this.makeSectionMarker(sectionMarker, "START");
|
|
var endMarker = this.makeSectionMarker(sectionMarker, "END");
|
|
var startPos = tpl.indexOf(startMarker) + startMarker.length;
|
|
var endPos = tpl.lastIndexOf(endMarker);
|
|
if ((startPos==-1) || (endPos==-1) || (endPos<=startPos)) {
|
|
return (this.modeFallback[mode]!=null ? this.extractSection(tpl, section, this.modeFallback[mode]) : "");
|
|
}
|
|
return tpl.substr(startPos,endPos-startPos);
|
|
}
|
|
|
|
replaceSection(tpl, section, value, mode = "") { //replaces a section in a template with a value
|
|
var modeTpl = this.findAndDeleteUnneededModes(tpl, section, mode);
|
|
|
|
var sectionMarker = this.makeMarker(section + modeTpl.mode);
|
|
var startMarker = this.makeSectionMarker(sectionMarker, "START");
|
|
var endMarker = this.makeSectionMarker(sectionMarker, "END");
|
|
var startPos = modeTpl.tpl.indexOf(startMarker);
|
|
var endPos = modeTpl.tpl.lastIndexOf(endMarker) + endMarker.length;
|
|
if ((startPos==-1) || (endPos==-1) || (endPos<=startPos)) {return modeTpl.tpl;}
|
|
var ret = (startPos>0 ? modeTpl.tpl.substr(0, startPos) : "");
|
|
ret+= value;
|
|
if (endPos<modeTpl.tpl.length) ret+= modeTpl.tpl.substr(endPos);
|
|
return ret;
|
|
}
|
|
|
|
replaceCondition(tpl, section, bool, mode = "") { // selects one of two sections in a template
|
|
// Template looks like this:
|
|
// <!-- ###SECTION### IF -->
|
|
// True text
|
|
// <!-- ###SECTION### ELSE -->
|
|
// False text
|
|
// <!-- ###SECTION### END -->
|
|
var modeTpl = this.findAndDeleteUnneededModes(tpl, section, mode);
|
|
|
|
var sectionMarker = this.makeMarker(section + modeTpl.mode);
|
|
var ifMarker = this.makeSectionMarker(sectionMarker, "IF");
|
|
var elseMarker = this.makeSectionMarker(sectionMarker, "ELSE");
|
|
var endMarker = this.makeSectionMarker(sectionMarker, "END");
|
|
|
|
var startPos = modeTpl.tpl.indexOf(ifMarker);
|
|
var else_startPos = modeTpl.tpl.indexOf(elseMarker);
|
|
var else_endPos = else_startPos + elseMarker.length;
|
|
var endPos = modeTpl.tpl.lastIndexOf(endMarker) + endMarker.length;
|
|
|
|
if ((startPos==-1) || (else_startPos==-1) || (endPos==-1) || (endPos<=startPos)) {
|
|
if (bool) {
|
|
return modeTpl.tpl;
|
|
} else {
|
|
return this.replaceSection(modeTpl.tpl, section, "", modeTpl.mode);
|
|
}
|
|
}
|
|
|
|
var value = "";
|
|
if (bool) {
|
|
// Show if-part
|
|
value = modeTpl.tpl.substr(startPos, else_startPos - startPos);
|
|
} else {
|
|
// Show else-part
|
|
value = modeTpl.tpl.substr(else_endPos, endPos - else_endPos - endMarker.length);
|
|
}
|
|
|
|
var ret = (startPos>0 ? modeTpl.tpl.substr(0, startPos) : "");
|
|
ret+= value;
|
|
if (endPos<modeTpl.tpl.length) ret+= modeTpl.tpl.substr(endPos);
|
|
return ret;
|
|
}
|
|
|
|
replaceSwitch(tpl, section, keys, mode = "") { // selects one or more of a variable number of cases from a section in a template
|
|
// Template looks like this:
|
|
// <!-- ###SECTION### SWITCH -->
|
|
// Default text
|
|
// <!-- ###SECTION### CASE_A -->
|
|
// Text A
|
|
// <!-- ###SECTION### CASE_B -->
|
|
// Text B
|
|
// <!-- ###SECTION### END -->
|
|
var modeTpl = this.findAndDeleteUnneededModes(tpl, section, mode);
|
|
var sectionMarker = this.makeMarker(section + modeTpl.mode);
|
|
|
|
var switchMarker = this.makeSectionMarker(sectionMarker, "SWITCH");
|
|
var endMarker = this.makeSectionMarker(sectionMarker, "END");
|
|
var anyMarker = this.makeSectionMarker(sectionMarker, "").substr(0, 5 + sectionMarker.length); // 5 = "<!-- "
|
|
|
|
var switchPos = modeTpl.tpl.indexOf(switchMarker);
|
|
var endPos = modeTpl.tpl.lastIndexOf(endMarker) + endMarker.length;
|
|
if (switchPos==-1 || endPos==-1) {return modeTpl.tpl;}
|
|
|
|
var value = "";
|
|
for (var i = 1; i<keys.length; i++) {
|
|
var keyMarker = this.makeSectionMarker(sectionMarker, keys[i]);
|
|
var keyPos = modeTpl.tpl.indexOf(keyMarker);
|
|
|
|
if (keyPos==-1) {
|
|
keyPos = switchPos + switchMarker.length; // Case `key` wasn't found, use default case
|
|
} else {
|
|
keyPos+= keyMarker.length;
|
|
}
|
|
// Find the start of the next marker.
|
|
var nextPos = modeTpl.tpl.indexOf(anyMarker, keyPos);
|
|
if (nextPos==-1) {
|
|
// Shouldn't happen. If it does, endMarker is before keyMarker
|
|
nextPos = endPos;
|
|
}
|
|
|
|
value+= modeTpl.tpl.substr(keyPos, nextPos - keyPos);
|
|
}
|
|
|
|
|
|
var ret = (switchPos>0 ? modeTpl.tpl.substr(0, switchPos) : "");
|
|
ret+= value;
|
|
if (endPos<modeTpl.tpl.length) ret+= modeTpl.tpl.substr(endPos);
|
|
return ret;
|
|
}
|
|
|
|
replaceMarker(tpl, marker, value) { //replaces a marker in a template with a value
|
|
return tpl.replaceAll(this.makeMarker(marker), value);
|
|
}
|
|
|
|
/* replaceMarkerArray(tpl, array) { //replaces all markers in a template with values from an array (index defines marker)
|
|
for (var ind in array)
|
|
{
|
|
tpl = tpl.replaceAll(this.makeMarker(ind), array[ind]);
|
|
}
|
|
return tpl;
|
|
}*/
|
|
}
|
|
|