/** * @category Core * @subcategory Application */ class Controller { static _template = null; static _contents = []; static _currentContent = null; /** * */ constructor() { } /** * */ destructor() {} /** * @async * @param {*} name * @param {*} args * @param {*} data * @returns {View} */ loadView(name, args, data) { args = args || {}; args.name = name+'.html'; args.className = name.split('/').pop(); args.onContentLoaded = args.onContentLoaded || this.appendHTML; args.path = app.config.router.viewsPath; let request = app.Assets.loadHtml(args); return request.then(args.onContentLoaded.bind(this, args, data)); } /** * * @param {*} args * @param {*} data * @param {*} html * @returns {View} */ appendHTML(args,data, html) { if(!args.target) { args.target = document.body; } else { if(typeof args.target == 'string') { let selector = null; let content = document.body; let crumbs = args.target.split('@') if(crumbs.length > 1) { let targetId = crumbs.pop() content = Controller._contents.find(o => o.view._sparcId == targetId).view.el; } selector = crumbs.join('@'); args.target = content.querySelector(selector); } } let container = document.createElement('div'); container.innerHTML = Controller.processTemplate(args.name, html, data); let parent = args.target; parent.innerHTML = ''; while(container.children.length > 0) { parent.appendChild(container.children[0]); } let view = new app.LoadedClasses[args.className](); view._className = args.name.replace('.html', ''); Controller._currentContent = view; Controller._contents.push({view: view, dom: parent, type: 'nested'}); view.el = parent; view.el.setAttribute('sparc-id', view._sparcId); view._controller = this; //view.DOMContentLoaded(data); return view; } /** * * @param {*} content */ unloadView(content) { app.events.clear(content.view._sparcId); Controller._contents = Controller._contents.filter(o => o.view._sparcId != content.view._sparcId); content.view.DOMContentRemoved(); content.view.el.remove(); } /** * * @param {*} name * @param {*} args */ loadModel(name, args) { } /** * * @param {*} id * @returns {View} */ static getContentById(id) { return Controller._contents.find(o => o.view._sparcId == id); } /** * * @param {*} name * @param {*} html * @param {*} templateData * @returns {string} */ static processTemplate(name, html, templateData) { // Define a function inside of which templateData keys will become local variables // AND where templateData values will become thos variable values ;-) // Drawback is that complex epression function evalaluateTemplate(tpl, params) { var interpretor = Function(...Object.keys(params), 'return('+tpl+');' ); return interpretor(...Object.values(params)); } // Define a function inside of which a string of a variable name becomes that variable // and where we check tht the corresponding (evaluable) expression in templateData is defined function checkExpression(varname, params) { var code = ' try{var t=typeof('+varname+');} catch(e){ t="badexpr"; } return(t)'; var interpretor = Function(...Object.keys(params), code ); return interpretor(...Object.values(params)); } // Preflight check to avoid template crashes on undefined expressions const rgxp = /\$\{(.*?)\}/gm; let m = null; templateData = templateData || {}; while ((m = rgxp.exec(html)) !== null) { let safeType = checkExpression(m[1],templateData); if(safeType == 'undefined') { // variable undefined in templateData: add an empty one console.warn(`In template ${name}, ${m[1]} is not defined !`); templateData[m[1]]=''; } else if(safeType == 'badexpr') { // unevaluable expression : remove it from template console.warn(`In template ${name}, Bad expression: ${m[1]}`); var escsearch = m[1].replace(/[.*+?^${}()|[\]\\]/g, '\\$&') var replrexp = new RegExp('\\$\\{'+escsearch+'\\}','gm') html = html.replace(replrexp,''); } } // Now that all remaining expressions are OK, evaluate the template via backtits ! return(evalaluateTemplate('`' + html + '`', templateData)); } } app.registerClass("Controller", Controller);