diff --git a/raw/javascript/template.js b/raw/javascript/template.js index 0b89aad..a0a1646 100644 --- a/raw/javascript/template.js +++ b/raw/javascript/template.js @@ -1,3 +1,4 @@ +/** @deprecated use VTepL instead! */ class Template { constructor() { this.tpl = {}; @@ -37,4 +38,4 @@ class Template { } const templateEx = /\$(.*?)\$/gm; -const templateDir = "/out/tpl/" \ No newline at end of file +const templateDir = "/out/tpl/" diff --git a/raw/javascript/templateLang/VTepLCore.js b/raw/javascript/templateLang/VTepLCore.js new file mode 100644 index 0000000..faae842 --- /dev/null +++ b/raw/javascript/templateLang/VTepLCore.js @@ -0,0 +1,34 @@ +'use strict'; + +class VTpeLCore { + constructor(options = {}) { + this.templates = {}; + this.dir = options.path || '/tpl/'; + this.suffix = options.suffix || 'tpl'; + this.path = options.template || `${this.dir}%s.${this.suffix}`; + this.cache = options.cache === undefined ? true : options.cache; + } + + async loadTemplate(name) { + let rawData = await fetch(this.path.replace('%s', name)); + if (rawData.ok) { + let data = await rawData.text(); + this.addTpl(name, data); + } + } + + async loadArray(names) { + for (let name of names) { + await this.loadTemplate(name); + } + } + + addTpl(name, content) { + let temp = this.templates[name] = new VTpeLTemplate(name, content) + temp.parseContent(this.cache); + } + + renderOn(name, data) { + return this.templates[name].render(data); + } +} diff --git a/raw/javascript/templateLang/VTepLInterpreter.js b/raw/javascript/templateLang/VTepLInterpreter.js new file mode 100644 index 0000000..e549672 --- /dev/null +++ b/raw/javascript/templateLang/VTepLInterpreter.js @@ -0,0 +1,133 @@ +'use strict'; + +class VTepLInterpreter { + constructor(parser) { + this.parser = parser; + this.data = []; + this.content = ''; + } + + render(data) { + let self = this; + self.data = data; + let newData = self.interpreter(self.parser.parsed); + self.data = []; + return newData[0]; + } + + interpreter(parsed, index = 0) { + let self = this; + let types = VParserTypes; + let tplCont = ''; + for (let i = index; i < parsed.length; i++) { + let item = parsed[i], + content = item.content; + switch (item.type) { + case types.content: + tplCont += content; + break; + case types.variable: + tplCont += self.getVariable(content) + break; + case types.assign: + let data = content.split("="), + key = data.shift(); + self.setVariable(data.join("=").trim(), key.trim()); + break; + case types.forEach: + let d = this.handleForEach(item, parsed, i); + i = d[0]; + tplCont += d[1]; + break; + case types.for: + let fd = this.handleFor(item, parsed, i); + i = fd[0]; + tplCont += fd[1]; + break; + case types.if: + let id = this.handleIf(item, parsed, i); + i = id[0]; + tplCont += id[1]; + break; + case types.ifEnd: + tplCont += content; + return [tplCont, i]; + case types.forEnd: + tplCont += content; + return [tplCont, i]; + default: + console.warn("Invalid Type found"); + break; + } + } + //this.content = tplCont; + return [tplCont, parsed.length]; + } + + getVariable(variable) { + if (this.data[variable]) { + return this.data[variable]; + } + let split = variable.split("."), + prevVar = this.data; + for (let i = 0; i < split.length; i++) { + prevVar = prevVar[split[i]] || prevVar; + } + if (typeof prevVar === 'string') { + return prevVar; + } + return ''; + } + + setVariable(value, variable) { + let c = this.getVariable(value); + if (c !== '') { + value = c; + } + this.data[variable] = value; + } + + handleForEach(item, parsed, i) { + let content = item.content.split(" as "); + let root = this.getVariable(content[0].trim()); + let addTo = 0, + isInvalid = false; + if (root === '') { + isInvalid = true; + root = []; + } + let d = Object.keys(root), + raw = ''; + for (let x of d) { + this.setVariable(root[x], content[1].trim()); + let data = this.interpreter(parsed, i + 1); + addTo = data[1]; + raw += data[0]; + } + if (isInvalid) { + raw = ''; + } + return [addTo, raw]; + } + + handleFor(item, parsed, ind) { + let content = item.content.split(" as "), + addTo = 0, + count = content[0].trim().split(".."), + max = parseInt(count[1]), + min = parseInt(count[0]), + newContent = ''; + for (let i = min; i < max; i++) { + this.setVariable(i.toString(), content[1]); + let data = this.interpreter(parsed, ind + 1); + addTo = data[1]; + newContent += data[0]; + } + return [addTo, newContent]; + } + + handleIf(item, parsed, i) { + let data = this.interpreter(parsed, i + 1); + return [data[1], data[0]]; + } +} diff --git a/raw/javascript/templateLang/VTepLParser.js b/raw/javascript/templateLang/VTepLParser.js new file mode 100644 index 0000000..40c8525 --- /dev/null +++ b/raw/javascript/templateLang/VTepLParser.js @@ -0,0 +1,178 @@ +'use strict'; + +const VParserTypes = { + content: 0, + variable: 1, + for: 2, + forEach: 3, + forContent: 4, + forEnd: 5, + if: 6, + ifContent: 7, + ifEnd: 8, + assign: 9, + none: -1, + + // operators + '!=': 100, + '==': 101, + '&&': 102, + '||': 103, + '>=': 104, + '<=': 105, + '>': 106, + '<': 107, + '+': 108, + '-': 109, + '*': 110, + '/': 111, + '%': 112 +}; + + +class VTpeLParser { + constructor(name, content) { + let self = this; + self.name = name; + self.legex = content; + self.index = 0; + self.content = ''; + self.parsed = []; + self.contexts = [0]; + self.allowedOperators = ['!=', '==', '&&', '||', '>=', '<=', '>', '<', '+', '-', '*', '/', '%'] + } + + tokenize() { + let self = this; + for (self.index = 0; self.index < self.legex.length; self.index++) { + let i = self.index, + char = self.legex.charAt(i); + if (self.nextContains('/*', i, true)) { + self.extract('*/', VParserTypes.none) + } else if (self.nextContains('//', i, true)) { + self.extract('\n', VParserTypes.none); + } else if (self.nextContains('', VParserTypes.none); + } else if (self.nextContains('{for(', i, true)) { + self.extract(')}', VParserTypes.for); + self.contexts.push(VParserTypes.for); + } else if (self.nextContains('{foreach(', i, true)) { + self.extract(')}', VParserTypes.forEach); + self.contexts.push(VParserTypes.forEach); + } else if (self.nextContains('{/for}', i, true)) { + self.addType(VParserTypes.forEnd); + self.contexts.pop(); + } else if (self.nextContains('{if(', i, true)) { + self.extract(')}', VParserTypes.if); + self.contexts.push(VParserTypes.if); + } else if (self.nextContains('{/if}', i, true)) { + self.addType(VParserTypes.ifEnd); + self.contexts.pop(); + } else if (self.nextContains('$${', i, true)) { + self.extract('}', VParserTypes.assign); + } else if (self.nextContains('${', i, true)) { + self.extract('}', VParserTypes.variable); + } else { + self.content += char; + } + } + self.addType(VParserTypes.content); + return self.parsed; + } + + addType(type) { + let self = this; + let content = self.content.trim(), + instructions = self.findInstructions(type); + self.content = ''; + if (type !== VParserTypes.none) { + if (type === VParserTypes.content && content === '') { + return null; + } + return self.parsed.push({ + content: content, + type: type, + context: self.contexts[self.contexts.length - 1], + instructions: instructions + }); + } + return null; + } + + nextContains(find, index, add = false) { + let count = this.nextContainsRaw(this.legex, find, index); + if (add && count > 0) { + this.index += count; + } + return count > 0 || count === -1; + } + + nextContainsRaw(raw, find, index) { + if (typeof find === "string") { + find = find.split(""); + } + let count = find.length; + if (count < 1) { + return -1; + } + for (let i = 0; i < count; i++) { + let nc = raw.charAt(index + i); + if ((find[i] === '\n' && nc === undefined)) { + return count; + } + if (find[i] !== nc) { + return 0; + } + } + return count; + } + + extract(findUntil = '}', type = 1) { + let self = this; + self.addType(0); + findUntil = findUntil.split("") + let content = '', + index = self.index, + legex = self.legex, + firstFind = findUntil.shift(); + for (let i = self.index; i < legex.length; i++) { + let char = legex.charAt(i); + if (char === firstFind && self.nextContains(findUntil, i + 1)) { + console.debug(`[Parser][${index} > ${i}] >> ${content}`); + self.index = i + findUntil.length; + self.content = content; + self.addType(type); + return; + } + content += char; + } + throw 'Template variable at Position: ' + index + ' not closed!'; + } + + // @todo implement split... is needed for if statements and math stuff or get it stupid! + getOperator(string) { + let operators = []; + for (let i = 0; i < string.length; i++) { + if (this.nextContainsRaw(string, "(", i)) { + let innerCon = ''; + for (let x = 0; x < string.length; x++) { + let char = string.charAt(i + x); + if (char === ')') { + break; + } + innerCon += char; + } + operators = [...operators, this.getOperator(innerCon)]; + } else { + } + } + return operators; + } + + findInstructions(type) { + if (type === VParserTypes.if) { + return this.getOperator(this.content); + } + return []; + } +} diff --git a/raw/javascript/templateLang/VTepLTemplate.js b/raw/javascript/templateLang/VTepLTemplate.js new file mode 100644 index 0000000..608f9ea --- /dev/null +++ b/raw/javascript/templateLang/VTepLTemplate.js @@ -0,0 +1,18 @@ +'use strict'; + +class VTpeLTemplate { + constructor(name, content) { + this.name = name; + this.tpl = content; + this.parser = new VTpeLParser(name, content); + this.interpreter = new VTepLInterpreter(this.parser); + } + + render(data = {}) { + return this.interpreter.render(data); + } + + parseContent() { + this.parser.tokenize(); + } +}