VENOM-10: WIP

This commit is contained in:
Maurice Grönwoldt 2020-11-18 17:50:01 +01:00
commit 5c44d50989
33 changed files with 890 additions and 601 deletions

View file

@ -1 +1 @@
:focus{outline:0}*{box-sizing:border-box}body,html{width:100vw;height:100vh;overflow:hidden;padding:0;margin:0;background:#1f2857;background:linear-gradient(144deg,#1f2954 25%,#000 50%,#5e002c 80%);background-attachment:fixed;color:#fff;font-family:sans-serif;font-size:16px}.hide{display:none!important}v-table{display:block}v-table v-table-footer,v-table v-table-header,v-table v-table-row{display:grid;grid-auto-columns:1fr;grid-auto-flow:column;-moz-column-gap:20px;column-gap:20px}v-table v-table-header h2{text-align:center}v-table v-table-row{margin:10px 0}v-table .grid12{grid-template-columns:repeat(12,[col-start] 1fr);-moz-column-gap:5px;column-gap:5px}v-table .grid12 .col-1to4{grid-column:1/3}v-table .grid12 .col-1to6{grid-column:1/6}v-table .grid12 .col-5to6{grid-column:5/6}v-table .grid12 .col-6to11{grid-column:6/11}v-table .grid12 .col-6to9{grid-column:6/9}v-table .grid12 .col-9to10{grid-column:9/10}v-table .grid12 .col-11to13{grid-column:11/13}v-label,v-option,v-options,v-select{display:inline-block;box-sizing:border-box}v-select{display:block;position:relative;margin-bottom:.5rem}v-label{padding:.5rem 1rem;background-color:#3949ab;color:#fff;cursor:pointer;display:block;border-radius:4px}v-options{position:absolute;display:flex;border-radius:4px;flex-direction:column;overflow:hidden;max-height:0;top:.5rem;left:.5rem;background-color:#3949ab;z-index:1000;cursor:pointer;color:#fff;box-shadow:0 3px 6px rgba(0,0,0,.16),0 3px 6px rgba(0,0,0,.23)}v-option{padding:.5rem 1rem;min-width:200px}v-option:not([disabled]):hover{background-color:rgba(0,0,0,.3)}v-option[selected]{background-color:#3949ab;color:#fff}v-option[disabled]{color:#aaa}main{display:flex;height:100vh;overflow:hidden}.menu{width:220px;background-color:#1b1b1b;box-shadow:0 3px 6px rgba(0,0,0,.16),0 3px 6px rgba(0,0,0,.23);flex-shrink:0;display:flex;flex-direction:column}.menu .logo{text-align:center;font-family:monospace}.menu div[data-link]{padding:.75rem .5rem;position:relative}.menu div[data-link]:after{background-color:#3949ab;content:"";position:absolute;left:0;bottom:0;height:3px;width:100%;transform:scaleX(0);transition:transform .4s;transform-origin:left}.menu div[data-link]:hover:after{transform:scaleX(1)}.menu div[data-link]:last-child{margin-top:auto}.menu div[data-link].active{font-weight:700}.content-area{padding:0 1%;margin:1rem 1.5rem;flex-grow:1;overflow-y:auto;width:100%;max-height:100%;background:rgba(27,27,27,.5)}.content-area .inline{display:inline}.content-area .inline div{display:inline}.content-area .btn-line{margin-top:35px}.content-area .btn-line div{text-align:right}.content-area .btn-line div button{display:inline;margin-left:10px;min-width:100px}.content-area textarea{background:rgba(27,27,27,.5);color:#fff;margin:15px 0 0 0;font-family:sans-serif;font-size:1.1rem;min-width:100%}.content-area .modules div{padding:6px 20px 6px 0}.content-area .icon-cont div{display:inline-block;vertical-align:middle;margin-top:5px}.content-area .icon-cont .icon{width:18px;height:18px;background-size:cover;margin:0 15px 0 0;fill:#fff}.content-area .icon-cont .icon-add{background-image:url(../images/icon/ic_add_circle_outline_24px.svg)}.content-area .icon-cont .icon-edit{background-image:url(../images/icon/ic_edit_24px.svg)}.content-area .icon-cont .icon-visibility{background-image:url(../images/icon/ic_visibility_24px.svg)}.content-area .icon-cont .icon-delete{background-image:url(../images/icon/ic_delete_24px.svg)}.content-area .icon-cont .icon-arrow-back{background-image:url(../images/icon/ic_arrow_back_24px.svg);width:24px;height:24px}.content-area .description{margin-top:25px}.content-area .fix-pad{padding-top:14px}
main{display:flex;height:100vh;overflow:hidden}.menu{width:220px;background-color:#1b1b1b;box-shadow:0 3px 6px rgba(0,0,0,.16),0 3px 6px rgba(0,0,0,.23);flex-shrink:0;display:flex;flex-direction:column}.menu .logo{text-align:center;font-family:monospace}.menu div[data-link]{padding:.75rem .5rem;position:relative}.menu div[data-link]:after{background-color:#3949ab;content:"";position:absolute;left:0;bottom:0;height:3px;width:100%;transform:scaleX(0);transition:transform .4s;transform-origin:left}.menu div[data-link]:hover:after{transform:scaleX(1)}.menu div[data-link]:last-child{margin-top:auto}.menu div[data-link].active{font-weight:700}.content-area{padding:0 1%;margin:1rem 1.5rem;flex-grow:1;overflow-y:auto;width:100%;max-height:100%;background:rgba(27,27,27,.5)}.content-area .inline{display:inline}.content-area .inline div{display:inline}.content-area .btn-line{margin-top:35px}.content-area .btn-line div{text-align:right}.content-area .btn-line div button{display:inline;margin-left:10px;min-width:100px}.content-area textarea{background:rgba(27,27,27,.5);color:#fff;margin:15px 0 0 0;font-family:sans-serif;font-size:1.1rem;min-width:100%}.content-area .modules div{padding:6px 20px 6px 0}.content-area .fix-pad{padding-top:14px}

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1 @@
<?xml version="1.0" encoding="UTF-8"?><svg xmlns="http://www.w3.org/2000/svg"><symbol viewBox="0 0 24 24" id="vt-add" xmlns="http://www.w3.org/2000/svg"><path fill="#fff" d="M13 7h-2v4H7v2h4v4h2v-4h4v-2h-4V7zm-1-5C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z"/></symbol><symbol viewBox="0 0 24 24" id="vt-arrow-back" xmlns="http://www.w3.org/2000/svg"><path fill="#fff" d="M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z"/></symbol><symbol viewBox="0 0 24 24" id="vt-check" xmlns="http://www.w3.org/2000/svg"><path fill="#fff" d="M19 3H5a2 2 0 00-2 2v14a2 2 0 002 2h14a2 2 0 002-2V5a2 2 0 00-2-2zm-9 14l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"/></symbol><symbol viewBox="0 0 24 24" id="vt-delete" xmlns="http://www.w3.org/2000/svg"><path fill="#fff" d="M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z"/><path d="M0 0h24v24H0z" fill="none"/></symbol><symbol viewBox="0 0 24 24" id="vt-edit" xmlns="http://www.w3.org/2000/svg"><path fill="#fff" d="M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04a.996.996 0 000-1.41l-2.34-2.34a.996.996 0 00-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z"/></symbol><symbol viewBox="0 0 24 24" id="vt-visibility" xmlns="http://www.w3.org/2000/svg"><path fill="#fff" d="M12 4.5C7 4.5 2.73 7.61 1 12c1.73 4.39 6 7.5 11 7.5s9.27-3.11 11-7.5c-1.73-4.39-6-7.5-11-7.5zM12 17c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5zm0-8c-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3z"/></symbol></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View file

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="#fff" d="M13 7h-2v4H7v2h4v4h2v-4h4v-2h-4V7zm-1-5C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z"/></svg>

Before

Width:  |  Height:  |  Size: 271 B

View file

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="#fff" d="M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z"/></svg>

Before

Width:  |  Height:  |  Size: 173 B

View file

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="#fff" d="M19 3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.11 0 2-.9 2-2V5c0-1.1-.89-2-2-2zm-9 14l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"/></svg>

Before

Width:  |  Height:  |  Size: 244 B

View file

@ -1,4 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<path fill="#fff" d="M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z"/>
<path d="M0 0h24v24H0z" fill="none"/>
</svg>

Before

Width:  |  Height:  |  Size: 239 B

View file

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="#fff" d="M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04c.39-.39.39-1.02 0-1.41l-2.34-2.34c-.39-.39-1.02-.39-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z"/></svg>

Before

Width:  |  Height:  |  Size: 262 B

View file

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="#fff" d="M12 4.5C7 4.5 2.73 7.61 1 12c1.73 4.39 6 7.5 11 7.5s9.27-3.11 11-7.5c-1.73-4.39-6-7.5-11-7.5zM12 17c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5zm0-8c-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3z"/></svg>

Before

Width:  |  Height:  |  Size: 322 B

View file

@ -0,0 +1,59 @@
class Component {
constructor(name) {
this.name = name;
this.start();
}
handle(data, ds) {
}
init() {
}
getUrl(ds) {
return '';
}
start() {
if (window.routerIsReady) {
router.addComponent(this.name || VUtils.tempId(), this);
this.init();
} else {
window.addEventListener('routerReady', this.start.bind(this));
}
}
}
class RolesComponent extends Component {
constructor() {
super("/roles");
this.tpl = "rolesList";
this.tpl2 = "roleEdit";
this._url = "/admin/api/roles";
}
async handle(data, ds) {
let meTpl = ds.id ? this.tpl2 : this.tpl;
await tpl.loadTemplate(meTpl);
return await tpl.renderOn(meTpl, data.content);
}
getUrl(ds) {
let url = this._url;
if (ds.id) {
url += '/' + ds.id;
}
return url;
}
}
(() => {
// init all Components ;)
new RolesComponent();
if (routerIsReady) {
document.body.dispatchEvent(new CustomEvent('triggerRouter'));
} else {
document.addEventListener('routerReady', e => {
document.body.dispatchEvent(new CustomEvent('triggerRouter'));
})
}
})();

View file

@ -0,0 +1 @@
class Component{constructor(t){this.name=t,this.start()}handle(t,e){}init(){}getUrl(t){return""}start(){window.routerIsReady?(router.addComponent(this.name||VUtils.tempId(),this),this.init()):window.addEventListener("routerReady",this.start.bind(this))}}class RolesComponent extends Component{constructor(){super("/roles"),this.tpl="rolesList",this.tpl2="roleEdit",this._url="/admin/api/roles"}async handle(t,e){let n=e.id?this.tpl2:this.tpl;return await tpl.loadTemplate(n),await tpl.renderOn(n,t.content)}getUrl(t){let e=this._url;return t.id&&(e+="/"+t.id),e}}new RolesComponent,routerIsReady?document.body.dispatchEvent(new CustomEvent("triggerRouter")):document.addEventListener("routerReady",t=>{document.body.dispatchEvent(new CustomEvent("triggerRouter"))});

View file

@ -85,7 +85,9 @@ class VUtils {
};
Node.prototype.createNew = function (tag, options) {
let el = document.createElement(tag);
el.classList.add(...VUtils.get(options.classes, []));
if (options.classes) {
el.classList.add(...VUtils.get(options.classes, []));
}
el.id = VUtils.get(options.id, '');
el.innerHTML = VUtils.get(options.content, "");
VUtils.mergeKeys(options.dataset, el.dataset);
@ -123,6 +125,7 @@ class VUtils {
};
}
}
VUtils.makePublic();
@ -199,6 +202,7 @@ class VRipple {
}
}
}
const rippler = new VRipple();
@ -426,40 +430,29 @@ class FormHandler {
}
(function () {
class VButton extends HTMLElement {
class VInput extends HTMLElement {
constructor() {
super();
let self = this;
self.id = self.id || VUtils.tempId();
self.label = document.createElement('label');
self.input = document.createElement('input');
self.errorBox = document.createElement('span');
self.errorBox.classList.add('error');
self.errorBox.innerHTML = self.dataset.error;
self.label.setAttribute('for', self.id);
self.input.id = self.id;
self.input.type = self.getAttribute('type') || 'text';
self.label.innerHTML = self.dataset.label;
self.input.value = self.innerHTML.trim();
let val = self.innerHTML;
self.innerHTML = '';
self.input.required = self.hasAttribute('required');
self.input.name = self.getAttribute('name');
self.appendChild(self.input)
self.appendChild(self.label)
self.appendChild(self.errorBox);
self.input.addMultiListener('change input', self.cb.bind(self));
let input = self.input = self.createNew('input', {id: self.id})
let label = self.createNew('label', {content: self.dataset.label});
self.createNew('span', {classes: 'error', content: self.dataset.error});
label.setAttribute('for', self.id);
input.type = self.getAttribute('type') || 'text';
input.value = val.trim();
input.required = self.hasAttribute('required');
input.name = self.getAttribute('name');
input.addMultiListener('change input', self.cb.bind(self));
}
connectedCallback() {
let cl = this.classList;
if (this.input.value === "") {
cl.remove('focus')
} else {
cl.add('focus')
}
this.cb({currentTarget: this.input}, true);
}
cb(e) {
cb(e, noInvalid) {
let el = e.currentTarget
let errorMessage = $('.error-message', el.find('form'));
if (errorMessage) {
@ -475,13 +468,15 @@ class FormHandler {
cl.add('valid');
cl.remove('invalid');
} else {
cl.remove('valid');
cl.add('invalid');
if (!noInvalid) {
cl.remove('valid');
cl.add('invalid');
}
}
}
}
customElements.define("v-button", VButton);
customElements.define("v-input", VInput);
if ($('#login')) {
new FormHandler('form#login', 'body', () => {
@ -673,4 +668,518 @@ class FormHandler {
}
customElements.define("v-editor", VEditor);
})();
(() => {
class Router {
constructor(options) {
let self = this;
self.options = options;
document.body.addDelegatedEventListener('click', '[data-link]', (e, el) => {
e.preventDefault();
$$('[data-link].active').forEach(ex => (ex.classList.remove('active')));
let loader = $('.loader').classList;
loader.remove('hide');
this.handleRouting(el.dataset).then(e => {
loader.add('hide');
el.classList.add('active');
});
})
document.body.addEventListener('triggerRouter', e => {
this.handle(sessionStorage.getItem('url'));
})
window.addEventListener('popstate', e => {
this.handle(e.state);
})
self.components = {};
window.dispatchEvent(new CustomEvent('routerReady'));
window.routerIsReady = true;
}
handle(item) {
if (!item) {
return;
}
item = JSON.parse(item);
this.handleRouting(item.data).then(r => {
let it = $('[data-link="' + item.data.link + '"]');
if (it) {
it.classList.add('active');
}
});
}
async handleRouting(dataset) {
try {
let url = dataset.link,
comp = this.components[url];
if (url === "") return null;
if (comp) url = comp.getUrl(dataset);
let data = await this.handleRequest(url, true); // we know the admin backend only returns json so we cheat a bit :)
if (data.reload) {
return location.reload();
}
comp = comp || this.components[data.component] || null;
if (comp) {
sessionStorage.setItem('url', JSON.stringify({data: dataset}));
comp.handle(data, dataset).then(r => {
$(this.options.toReplace).innerHTML = r;
history.pushState(JSON.stringify({data: dataset}), document.title);
});
} else {
await alert("Error");
}
return null;
} catch (e) {
return e;
}
}
async handleRequest(url, forceJSON) {
url = url.trim();
if (url === '') return;
// await ;)
return await fetch(url, {
credentials: "same-origin"
}).then(res => {
if (!res.ok) {
throw `URL is Status: ${res.status}`;
}
let c = res.headers.get("Content-Type");
if (c.indexOf('json') !== -1 || forceJSON) return res.json();
if (c.indexOf('text') !== -1) return res.text();
return res.blob();
}).catch(err => {
console.error(err)
return null;
});
}
addComponent(name, component) {
this.components[name] = component;
}
}
window.router = new Router({
toReplace: '.content-area'
})
})();
'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) {
if (this.templates[name]) {
return null;
}
let path = this.path.replace('%s', name);
let rawData = await fetch(path, {cache: "force-cache"});
if (rawData.ok) {
let data = await rawData.text();
this.addTpl(name, data);
}
return null;
}
async loadArray(names) {
for (let name of names) {
await this.loadTemplate(name);
}
}
addTpl(name, content) {
let temp = this.templates[name] = new VTpeLTemplate(name, content, this);
temp.parseContent(this.cache);
}
async renderOn(name, data) {
if (this.templates[name]) {
return await this.templates[name].render(data);
}
return '';
}
}
'use strict';
const VParserTypes = {
content: 0,
variable: 1,
for: 2,
forEach: 3,
forContent: 4,
forEnd: 5,
if: 6,
ifContent: 7,
ifEnd: 8,
assign: 9,
include: 10,
none: -1,
};
class VTpeLParser {
constructor(name, content) {
let self = this;
self.name = name;
self.legex = content.trim();
self.index = 0;
self.content = '';
self.parsed = [];
self.contexts = [0];
}
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('<!--', i, true)) {
self.extract('-->', VParserTypes.none);
} else if (self.nextContains('{for(', i, true)) {
self.extract(')}', VParserTypes.for);
self.contexts.push(VParserTypes.for);
} else if (self.nextContains('{include(', i, true)) {
self.extract(')}', VParserTypes.include);
self.contexts.push(VParserTypes.include);
} 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.replace(/^\n+|\n+$/g, ''),
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.trim();
self.addType(type);
return;
}
content += char;
}
if (firstFind === "\n") {
self.index = legex.length;
self.content = content.trim();
self.addType(type);
return
}
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 [];
}
}
'use strict';
class VTepLInterpreter {
constructor(parser, core) {
this.parser = parser;
this.data = [];
this.content = '';
this.core = core;
}
async render(data) {
let self = this;
self.data = data;
let newData = await self.interpreter(self.parser.parsed);
self.data = [];
return newData[0];
}
async 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 = await this.handleForEach(item, parsed, i);
i = d[0];
tplCont += d[1];
break;
case types.for:
let fd = await this.handleFor(item, parsed, i);
i = fd[0];
tplCont += fd[1];
break;
case types.if:
let id = await this.handleIf(item, parsed, i);
i = id[0];
tplCont += id[1];
break;
case types.include:
tplCont += await this.handleInclude(item);
break;
case types.ifEnd:
tplCont += content;
return [tplCont, i];
case types.forEnd:
tplCont += content;
return [tplCont, i];
default:
console.warn("Invalid Type found");
break;
}
}
return [tplCont, parsed.length];
}
getVariable(variable) {
variable = variable.toString();
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;
}
if (typeof prevVar === 'number') {
return prevVar.toString();
}
return '';
}
setVariable(value, variable) {
let c = this.getVariable(value);
if (c !== '') {
value = c;
}
this.data[variable] = value;
}
async 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 = {invalid: "true"};
}
let d = Object.keys(root),
raw = '',
varContent = content[1].trim().split(",");
for (let x of d) {
if (varContent.length === 2) {
this.setVariable(x, varContent[1]);
}
this.setVariable(root[x], varContent[0]);
let data = await this.interpreter(parsed, i + 1);
addTo = data[1];
raw += data[0];
}
if (isInvalid) {
raw = '';
}
return [addTo, raw];
}
async handleInclude(item) {
let split = item.content.split(";"),
name = split.shift(),
data = {};
await this.core.loadTemplate(name);
for (let item of split) {
let d = item.split("="),
index = d.shift(),
dat = d.join("=");
if (dat.startsWith("$")) {
dat = this.getVariable(dat.substr(1, dat.length));
}
data[index] = dat;
}
return await this.core.renderOn(name, data);
}
async 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 = await this.interpreter(parsed, ind + 1);
addTo = data[1];
newContent += data[0];
}
return [addTo, newContent];
}
async handleIf(item, parsed, i) {
let data = await this.interpreter(parsed, i + 1);
return [data[1], data[0]];
}
}
'use strict';
class VTpeLTemplate {
constructor(name, content, core) {
this.name = name;
this.tpl = content;
this.parser = new VTpeLParser(name, content);
this.core = core;
}
async render(data = {}) {
return await new VTepLInterpreter(this.parser, this.core).render(data);
}
parseContent(cache) {
if (cache) {
let storage = localStorage.getItem(this.name);
if (storage) {
this.parser.parsed = JSON.parse(storage);
return;
}
}
this.parser.tokenize();
if (cache) {
localStorage.setItem(this.name, JSON.stringify(this.parser.parsed));
}
}
}
(() => {
window.tpl = new VTpeLCore({
template: '/admin/api/templateLoader?tpl=%s',
cache: !document.body.hasAttribute('debug')
});
//preload includes to make sure they are loaded always :)
window.tpl.loadArray([
'includes/btn',
'includes/input',
'includes/select',
'includes/svg',
])
})();

File diff suppressed because one or more lines are too long