/*
On template Rendering we have
following variables
var vars = {}; // variables that a sent from controllers
var env = {
error : [],
vars : envVars,
path : "", // filePath
dirname : "", // dirname
render : function( text, vars, env ) {}, // render function
renderFile : function( file, vars, callback ) {}, // render file function
// callback( error {{ [] or false }}, htm)
};
env.vars = {
// env varssent from app
response : [object] // http reponse object
}
*/
/**
* @class
* @name FaceboxTemplate
* @param {object} options
* @param {FSLibrary} options._fs
*/
function FaceboxTemplate(options) {
/**
* @memberof FaceboxTemplate#
* @var {boolean} _debug
*/
let _debug = false;
Object.defineProperty(
this,
'_debug',
{
get: () => _debug,
set: (v) => {
_debug = !!v;
}
}
);
/**
* @memberof FaceboxTemplate#
* @var {Object<string,any>} _env
*/;
let _env = {};
Object.defineProperty(
this,
'_env',
{
get: () => _env,
set: (data) => {
Object.assign(_env, data);
}
}
);
/**
* @memberof FaceboxTemplate#
* @var {Object<string,string>} _cachedFiles
*/
this._cachedFiles = {};
const readFileSync = (addr) => {
if (addr in this._cachedFiles) {
return this._cachedFiles[addr];
} else {
//@ts-ignore
const content = options._fs.readFileSync( addr, { encoding: "utf-8" } );
// @ts-ignore
if (!this._debug) {
this._cachedFiles[addr] = content;
}
return content;
}
};
/**
* @memberof FaceboxTemplate#
* @var {number} INCLUDE_LEVEL
*/
this.INCLUDE_LEVEL = 10;
/**
* @memberof FaceboxTemplate#
* @method render
* @param {string} text
* @param {Object<string,any>} vars
* @param {Object<string,any>} env
* @this FaceboxTemplate
*/
this.render = function FaceboxTemplateRender( text, vars, env ) {
if (vars === undefined) vars = {};
const template = { text: text };
const replacers = [];
let err = false;
/**
* @private
* @param {string} addr
* @returns {string}
*/
const parseAddr = function parseAddr(addr) {
let err;
if(addr.subs(1) == ":") {
return `${env.dirname.replace(/\/+$/,'')}/${addr.subs(1,0)}`;
} else {
try {
// jshint -W054
return (new Function(
`const env = arguments[1];const vars = arguments[0];\n return ${addr}`
))(vars, env);
// jshint +W054
} catch(err) {
throw Error(
`Unable to eval path: ${addr} ; ${err.meesage} ;`
);
}
}
}.bind(this);
text = text.replace(
/\{\{(read|read\-base64|read\-hex)\:(.*?)\}\}/g,
function (match, p1, p2, offset, string) {
const x = '[[replacer-'+Math.floor(Math.random()*10000000)+'-'+Math.floor(Math.random()*10000000)+'-'+new Date().valueOf()+']]';
replacers.push({ id : x, type : 'action-'+p1, param : p2 });
return x;
}
);
text = text.replace(
/\{\{(include)\:(.*?)\}\}/g,
function (match, p1, p2, offset, string) {
return readFileSync( parseAddr(p2) );
}
);
text = text.replace(
/\{\{(render)\:(.*?)\}\}/g,
function (match, p1, p2, offset, string) {
const addr = parseAddr(p2);
return this.render(
readFileSync(addr),
vars,
{
vars : this._env,
path : addr,
dirname : ((addr).replace(/[^\/]+$/,'')+'/').replace(/^\/+$/,'/'),
render : this.render,
renderFile : this.renderFile
}
);
}.bind(this)
);
text = text.replace(
// jshint -W049
/\<script[^\>]+?type\s*?=\s*?\"text\/facebox\-template\"[^\>]*?\>([\s\S]*?)\<\/script\>/g,
// jshint +W049
function (match, p1, p2, offset, string) {
const x = '[[replacer-'+Math.floor(Math.random()*10000000)+'-'+Math.floor(Math.random()*10000000)+'-'+new Date().valueOf()+']]';
replacers.push({ id : x, type : 'js-return', code : p1 });
return x;
}
);
text = text.replace(
/\{(code|js\-return|eval|js\-script|css\-style)\}([\s\S]*?)\{\/\1\}/g,
function (match, p1, p2, offset, string) {
const x = '[[replacer-'+Math.floor(Math.random()*10000000)+'-'+Math.floor(Math.random()*10000000)+'-'+new Date().valueOf()+']]';
replacers.push({ id : x, type : p1, code : p2 });
return x;
}
);
text = text.replace(
/\{\{([\s\S]*?)\}\}/g,
function (match, p1, offset, string) {
const x = '[[replacer-'+Math.floor(Math.random()*10000000)+'-'+Math.floor(Math.random()*10000000)+'-'+new Date().valueOf()+']]';
replacers.push({ id : x, type : 'js-return', code : 'return '+p1 });
return x;
}
);
let i;
for (i=0; i<replacers.length; i++) {
switch(replacers[i].type) {
case 'code':
text = text.split(replacers[i].id).join(replacers[i].code);
break;
case 'js-return':
// jshint -W054
text = text.split(replacers[i].id).join((new Function("var vars = arguments[0];var env = arguments[1];\nvar result = (function faceBoxTemplate_jsReturn() {\n"+replacers[i].code + "\n})(); if (result === undefined) return ''; return result;" ))( vars, env ));
// jshint +W054
break;
case 'eval':
// jshint -W054
(new Function("var vars = arguments[0];var env = arguments[1];\n"+replacers[i].code ))( vars, env );
// jshint +W054
text = text.split(replacers[i].id).join('');
break;
case 'js-script':
text = text.split(replacers[i].id).join('<script type="text/javascript" charset="utf-8">\n'+replacers[i].code+'\n</script>');
break;
case 'css-style':
text = text.split(replacers[i].id).join('<style type="text/css">\n'+replacers[i].code+'\n</style>');
break;
case 'action-read':
text = text.split(replacers[i].id).join(readFileSync(parseAddr(replacers[i].param)));
break;
case 'action-read-base64':
text = text.split(replacers[i].id).join(readFileSync(parseAddr(replacers[i].param)).base64encode());
break;
case 'action-read-hex':
text = text.split(replacers[i].id).join(readFileSync(parseAddr(replacers[i].param)).toHex());
break;
}
}
return text;
}.bind(this);
/**
* @memberof FaceboxTemplate#
* @method renderFile
* @param {string} filePath
* @param {Object<string,any>} vars
* @param {function} callback
* @this FaceboxTemplate
*/
this.renderFile = function( filePath, vars, callback ) {
const env = {
vars : this._env,
path : filePath,
dirname : (filePath.replace(/[^\/]+$/,'')+'/').replace(/^\/+$/,'/'),
render : this.render,
renderFile : this.renderFile
};
callback(
null,
this.render( readFileSync( filePath ), vars, env )
);
}.bind(this);
/**
* @memberof FaceboxTemplate#
* @method renderCode
* @param {string} code
* @param {Object<string,any>} vars
* @param {function} callback
* @param {string} virtualFilePath
* @this FaceboxTemplate
*/
this.renderCode = function( code, vars, callback, virtualFilePath ) {
const env = {
vars : this._env,
path : virtualFilePath || '',
dirname : ((virtualFilePath || '').replace(/[^\/]+$/,'')+'/').replace(/^\/+$/,'/'),
render : this.render,
renderFile : this.renderFile
};
callback(
null,
this.render(code, vars, env)
);
}.bind(this);
return this;
};
module.exports = FaceboxTemplate;