slot-framework
Version:
Closing the gap between server and client side, Slot is a Cross Side Web Framework that let you reuse the same code on client and server side
517 lines (439 loc) • 24.4 kB
JavaScript
var connect = require('connect'),
http = require("http"),
url = require("url"),
path = require("path"),
fs = require("fs"),
config = require('./config'),
mime = require("mime"),
Util = require("./util")
port = process.argv[2] || 80 /*8888*/;
function Slot() {
this.key = "";
this.value = "";
}
var slots = new Object();
var slotModels = new Object();
var templates = new Object();
var fragments,
slotJson;
//
function s4() {
return Math.floor((1 + Math.random()) * 0x10000)
.toString(16)
.substring(1);
};
function guid() {
return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
s4() + '-' + s4() + s4() + s4();
}
var manyTabs = 0;
var tabs = " ";
//
function logmsg(message) {
var index = 0;
while (index++ < manyTabs) {
message = tabs + message;
}
console.log(message);
};
//function prefixFileName(fullFilePath, prefix) {
// var filename = fullFilePath.split(path.sep).pop();
// fullFilePath = fullFilePath.replace(filename, prefix + Util.upperCaseCharAt0(filename));
//
// return fullFilePath;
//}
//function upperCaseCharAt0(word) {
// return word.charAt(0).toUpperCase() + word.substr(1, word.length);
//}
function execJsonBinding(nameSpace, slotKey, uriBuildKey, fileContent, filename, binds, callback) {
// Create a new Object for the Slot, if not exists a "slotModels[slotKey]" instance
slotModels[slotKey] = slotModels[slotKey] ? slotModels[slotKey] : new Object();
manyTabs++;
for(var field in binds) {
/**
* Fields are Objects or SimpleText
*/
if(binds[field] instanceof Object) {
var uuid = guid();
var slotKeyNew = binds[field].fragmentID;
// If 'binds[field].layout' was not assigned, then take layout from fragment.json definition
var slotUri = binds[field].layout ? binds[field].layout.trim() : "",
slotUri = slotUri != "" ? slotUri : (fragments[slotKeyNew] ? fragments[slotKeyNew]+".html" : "");
// If 'binds[field].bind' was not assigned, then take bind from fragment.json definition
var jsonBindFile = binds[field].bind
&& ( binds[field].bind instanceof Array || binds[field].bind.trim() != "" )
? binds[field].bind
: (fragments[slotKeyNew] ? fragments[slotKeyNew]+".json" : "")
;
var layoutFileName = path.join(process.cwd(), slotJson.framework.webRootDir, slotUri);
// Save template path on cache
slots[uriBuildKey][slotKeyNew] = slotUri;
if(jsonBindFile instanceof Array) {
logmsg("\n"); pageModel.push("\n");
lineMsg = "// attribute " + nameSpace+"."+field + " is an array of fragment " + slotKeyNew + "";
pageModel.push(lineMsg);
logmsg(lineMsg);
lineMsg = nameSpace+"."+field + " = []";
pageModel.push(lineMsg);
logmsg(lineMsg);
}
else {
logmsg("\n"); pageModel.push("\n");
lineMsg = "// attribute " + nameSpace+"."+field + " is an instance of fragment " + slotKeyNew + "";
pageModel.push(lineMsg);
logmsg(lineMsg);
lineMsg = nameSpace+"."+field + " = model.fragments." + slotKeyNew + ".create();";
pageModel.push(lineMsg);
logmsg(lineMsg);
}
var template;
try {
template =
loadFile(nameSpace+"."+field, // <<== nameSpace
slotKeyNew, // <<== Slot key
slotUri, // <<== Uri parameter
uriBuildKey,
layoutFileName, // <<== Full template path
jsonBindFile, // <<== Full jsonBindFile path (if was declared on slot definition field)
function () {
return "Slot template not found " + layoutFileName;
},
function (err) {
return "Slot template read error " + layoutFileName;
},
function (fileX, fileContent) {
//logmsg("--");
return fileContent;
}
);
template = template.replace(RegExp("{@fragmentID@}","g"), slotKeyNew);
}
catch (e) {
logmsg("Exception on " + uuid + " " + e);
}
fileContent = fileContent.replace(RegExp("{@" + field + "@}","g"), template);
logmsg("--x");
slotModels[slotKey][field] = new Object();
slotModels[slotKey][field].slot = slotKeyNew;
}
else {
fileContent = fileContent.replace(RegExp("{@" + field + "@}", "g"), binds[field]);
lineMsg = "" +nameSpace+ "." +field+ " = '"+binds[field]+"';";
// Exclude fragmentID from the list, it will be created and asigned directly from
// the constructor
if(field != "fragmentID") {
slotModels[slotKey][field] = "";
pageModel.push(lineMsg);
}
/**
* TODO:
* 1. Hide fragmentID from pageModel creation.
* 2. Add code to allow fragmentID field always be set from create() method.
*/
logmsg(lineMsg);
}
}
manyTabs--;
return callback(fileContent);
}
var count = 0,
lineMsg,
pageModel = [];
function loadFile(nameSpace, slotKey, uri, uriBuildKey, filename, jsonBindFile, onDontExistsCallback, onReadFileError, onBindComplete) {
var exists = fs.existsSync(filename), uriFileName = uri;
if (!exists) { // <<== function called if template don't exists
return onDontExistsCallback();
}
//logmsg("Name space: slotKey["+slotKey+"] uri["+uri+"] uriBuildKey["+uriBuildKey+"]");
if (fs.statSync(filename).isDirectory()) {
filename += (filename.lastIndexOf(path.sep)+1==filename.length ? 'index.html' : path.sep+'index.html');
uriFileName = uriFileName.replace(/\/+$/, '');
uriFileName += (uri.lastIndexOf(path.sep)+1==uri.length ? 'index.html' : /*path.sep*/'/'+'index.html');
}
try {
var file = (templates[filename]=fs.readFileSync( filename ,'binary' /*'utf8'*/));
/**
* Bind just allowed extentions (html, htm)
* TODO:
* 1. Create allowExt.json to define wich extentions are allowed to be binded
*/
if((filename.split(".")[filename.split(".").length-1]).toLowerCase() == "html") {
var binds;
if(jsonBindFile instanceof Array) {
binds = jsonBindFile;
}
else {
var bindDir = path.join(process.cwd(), slotJson.framework.metaData /*"/bind"*/);
/**
* Si no hemos pasado el jsonBindFile, pues entonces tomamos bind segun el nombre del html
* con el que estamos trabajando.
*/
jsonBindFile = (jsonBindFile == undefined ? path.join(bindDir, uriFileName.replace(/.html/gi, ".json")) : path.join(bindDir, jsonBindFile));
//
slots[uriBuildKey] = slots[uriBuildKey] ? slots[uriBuildKey] : new Object();
slots[uriBuildKey][slotKey] = uri /*uriFileName*/;
// Parse fields and slots defined on JSON File.
jsonBindFile = fs.readFileSync( jsonBindFile ,'binary' /*'utf8'*/);
binds = JSON.parse(jsonBindFile);
binds = binds["binds"];
}
if (binds) {
// Call bindings
if(binds instanceof Array) {
var rows = "";
lineMsg = "// attribute " + nameSpace+ " is an array of fragment " + slotKey + "";
pageModel.push(lineMsg);
logmsg(lineMsg);
lineMsg = nameSpace + " = []";
pageModel.push(lineMsg);
logmsg(lineMsg);
//Iterate over each slot data record
for(var row in binds) {
logmsg("--y");
var currentVar = slotKey +""+ count ;
//lineMsg = "var " + slotKey +""+( count )+ " = model.fragments." + slotKey + ".create();";
lineMsg = "var " +currentVar+ " = model.fragments." + slotKey + ".create();";
pageModel.push(lineMsg);
logmsg(lineMsg);
//Doing binding
rows += execJsonBinding(slotKey +""+( count ) /*nameSpace*/, slotKey, uriBuildKey, file, filename, binds[row],
function (fileContent) { // <<== function called after binding templates
return fileContent;
}
);
count++;
//lineMsg = nameSpace + ".push("+slotKey +""+( count++ )+")";
lineMsg = nameSpace + ".push("+currentVar+")";
pageModel.push(lineMsg);
logmsg(lineMsg);
}
return onBindComplete(filename, rows);
}
else {
//Doing binding
return execJsonBinding(nameSpace, slotKey, uriBuildKey, file, filename, binds,
function (fileContent) { // <<== function called after binding templates
return onBindComplete(filename, fileContent);
}
);
}
}
manyTabs--;
}
return onBindComplete(filename, file);
}
catch (e) {
logmsg("loading exception: " + filename);
console.log(e);
return onReadFileError(e);
}
}
var app = connect()
.use(connect.favicon())
.use(connect.cookieParser())
.use(connect.session({ secret: 'keyboard cat', cookie: { maxAge: 60000 }}))
.use(function (request, response, next) {
var uri = url.parse(request.url).pathname,
uriBuildKey = url.parse(request.url).pathname.replace(/\//g, "-"),
filename = path.join(process.cwd(), slotJson.framework.webRootDir, uri);
// Load requested file
loadFile("model", // <<== nameSpace
"main", // <<== slotKey
uri,
uriBuildKey,
filename,
undefined, // <<== Pass jsonBindFile as undefined to force to use default named jsonBinder
function () { // <<== onDontExistsCallback: function called if template don't exists
response.writeHead(404, {"Content-Type": "text/plain"});
response.write("404 Not Found\n");
response.end();
},
function (err) { // <<== onReadFileError: function called if and error reading template occurs
response.writeHead(500, {"Content-Type": "text/plain"});
//response.write(err + "\n");
response.write("500 Not Found\n" + uri);
response.end();
},
function (filename, fileContent) { // <<== onBindComplete: function called after binding templates
response.writeHead(200, {"Content-Type": mime.lookup(filename)});
response.write(fileContent, "binary");
response.end();
/**
* If valid URL path whit allowed Slot rendering, we are going to generate
* Slot Models.
*/
if((filename.split(".")[filename.split(".").length-1]).toLowerCase() == "html") {
if(slots[uriBuildKey] && Object.keys(slots[uriBuildKey]).length) {
var os = require("os"),
nowTimeStamp = (new Date()).toDateString() + " " + (new Date()).toLocaleTimeString();
var uri = url.parse(request.url).pathname,
modelName = filename.split(process.cwd())[1];
//modelName = path.join(slotJson.framework.mvcRootDir, modelName.replace(/[\\www][/www]/g, ""));
modelName = path.join(slotJson.framework.mvcRootDir, modelName.replace(/^\\www|^\/www/g, ""));
modelName = modelName.replace(/^\\/, ''); //Remove first backslash
var modelFile = Util.prefixFileName(modelName, "m").replace(".html", ".js");
var modelFileSrv = Util.prefixFileName(modelName, "m").replace(".html", "Srv.js");
var viewFile = Util.prefixFileName(modelName, "v").replace(".html", ".js");
var pageModelFile = Util.prefixFileName(modelName, "pageModel").replace(".html", ".js");
var mkdirp = require('mkdirp'),
mcvFolder = ( modelName),
mcvFile = mcvFolder.split(path.sep).pop(),
mcvFolder = mcvFolder.replace(mcvFile, "");
//
//Add code to enssure directory is fullpath created
//
console.log('MVC mcvFolder %s \r\n' +
'MVC mcvFile %s \r\n' +
'MVC modelName %s \r\n' +
'MVC __dirname %s \r\n' +
'MVC process.cwd %s \r\n \r\n' +
'MVC modelFile %s \r\n' +
'MVC modelFileSrv %s \r\n' +
'MVC viewFile %s \r\n'
, mcvFolder, mcvFile, modelName, __dirname, process.cwd(), modelFile, modelFileSrv, viewFile);
mkdirp(path.join(process.cwd(), mcvFolder), function (err) {
if (err) console.error(err)
else {
console.log('creating MVC for %s', modelName)
/**
* TODO:
* 1. Recorrer cada property del slotModels[slotKeyNew] para construir el Object
* segun el slotObjectDefinitionTemplate.txt
* 2. Tomar el path del template usando slots[uriBuildKey][slotKeyNew]
*/
var tempTemplate = fs.readFileSync(path.join(__dirname, '/templates/slotObjectDefinitionTemplate.txt'),'binary' /*'utf8'*/),
attrs = "",
obj = "",
objs = "",
val = "",
layouts = "";
for(var sl in slots[uriBuildKey]) {
obj = tempTemplate.replace(RegExp("{@slotName@}","g"), sl);
attrs = "\r\n\tthis.fragmentID = \"" +sl+ "\";";
for (var fl in slotModels[sl]) {
val = typeof slotModels[sl][fl] == "object" // There is a Model, represented as an Object
&&
slotModels[sl][fl]["slot"] // Validate if this model have defined a Slot yet
? slotModels[sl][fl]["slot"] // Take the Slot name
: "\"\""; // Don't assign a Slot name
attrs += "\r\n\tthis." + fl + " = " + val + ";";
}
obj = obj.replace("{@slotAttributes@}", attrs);
objs += obj;
/**
* Take the html template
*/
var tempLayout = slots[uriBuildKey][sl] == '/' ? "index.html" : slots[uriBuildKey][sl];
tempLayout = templates[path.join(path.join(process.cwd(), slotJson.framework.webRootDir), tempLayout)];
tempLayout = tempLayout
.replace(RegExp("\r","g"), "@@SLR@@")
.replace(RegExp("\n","g"), "@@SLN@@")
.replace(RegExp("\t","g"), "@@SLT@@")
.replace(RegExp("'","g"), "@@SQUOTE@@")
.replace(RegExp("\"","g"), "@@DQUOTE@@")
.replace(RegExp("<","g"), "@@LT@@")
.replace(RegExp(">","g"), "@@GT@@")
//.replace(RegExp("*","g"), "@@ASTE@@")
.replace(/\*/g, "@@ASTE@@")
.replace(RegExp("/","g"), "@@BSLASH@@")
;
layouts += "\r\n\t\tthis." + sl + " = \"" + tempLayout + "\";";
/**
* TODO:
* 1. Fix this bug that occurs when creating the MVC for index.html on any folder
* into, by example 'http://localhost:801/store/' generated the next error
creating MVC for app\mvc\store\index.html
C:\datadrive\workspaces\Webstorm\gCloud\slot-cli\node_modules\slot-framework\designer.js:379
.replace(RegExp("\r","g"), "@@SLR@
^
TypeError: Cannot call method 'replace' of undefined
at C:\datadrive\workspaces\Webstorm\gCloud\slot-cli\node_modules\slot-framework\designer.js:379:46
at C:\datadrive\workspaces\Webstorm\gCloud\slot-cli\node_modules\mkdirp\index.js:38:26
at Object.oncomplete (fs.js:107:15)
*/
}
tempTemplate = fs.readFileSync(path.join(__dirname, '/templates/modelTemplate.txt'),'binary' /*'utf8'*/);
tempTemplate = tempTemplate
//.replace("{@jsonModel@}", JSON.stringify(slotModels, null, 4))
.replace("{@objectsDef@}", objs)
.replace("{@layouts@}", layouts)
;
fs.writeFile(path.join(process.cwd(), modelFile), tempTemplate, function (err) {
if (err) throw err;
console.log('It\'s saved model for client side!');
});
tempTemplate = fs.readFileSync(path.join(__dirname, '/templates/modelTemplateSrv.txt'),'binary' /*'utf8'*/);
tempTemplate = tempTemplate
.replace("{@pc-machine@}", os.hostname())
.replace("{@date@}", nowTimeStamp)
.replace("{@message@}", modelFileSrv.split(path.sep).pop() + ", this model holds the data structure for the web page")
.replace("{@page@}", uri)
//.replace("{@jsonModel@}", JSON.stringify(slotModels, null, 4))
.replace("{@objectsDef@}", objs)
.replace("{@layouts@}", layouts)
;
fs.writeFile(path.join(process.cwd(), modelFileSrv), tempTemplate, function (err) {
if (err) throw err;
console.log('It\'s saved model for server side!');
});
console.log('Verifying pageView! %s', path.join(process.cwd(), viewFile));
//fs.exists(viewFile, function(flag) {
fs.exists(path.join(process.cwd(), viewFile), function(flag) {
if(!flag){
tempTemplate = fs.readFileSync(path.join(__dirname, '/templates/viewTemplate.txt'),'binary' /*'utf8'*/);
tempTemplate = tempTemplate
.replace("{@pc-machine@}", os.hostname())
.replace("{@date@}", nowTimeStamp)
.replace("{@message@}", viewFile.split(path.sep).pop() + ", this view was built to serve web page")
.replace("{@page@}", uri)
.replace(RegExp("{@modulePath@}", "g"), "./" + modelFileSrv.split(path.sep).pop())
;
fs.writeFile(path.join(process.cwd(), viewFile), tempTemplate, function (err) {
if (err) throw err;
console.log('It\'s saved view file!');
});
}
tempTemplate = fs.readFileSync(path.join(__dirname, '/templates/pageModelTemplate.txt'),'binary' /*'utf8'*/);
tempTemplate = tempTemplate
.replace("{@pc-machine@}", os.hostname())
.replace("{@date@}", nowTimeStamp)
.replace(RegExp("{@modelFile@}", "g"), modelFileSrv.split('\\').pop())
.replace("{@content@}", pageModel.join('\r'));
;
fs.writeFile(path.join(process.cwd(), pageModelFile), tempTemplate, function (err) {
if (err) throw err;
console.log('It\'s saved page model file!');
//console.log(tempTemplate);
});
});
}
});
}
else {
console.log("Resource %s does not have a slot definition, no MVC objects will be created", filename)
}
}
}
);
}
);
function start(port) {
// Load slot.json config file
config.load(
function(err) {
console.log('Problems loading slot.json file, you must have this file: %s', err);
},
function(buffer) {
//port = port ? port : 800;
port || (port = 800)
http.createServer(app).listen(parseInt(port, 10));
slotJson = buffer;
fragments = slotJson.fragments;
Util.startSplash("Designer", port, slotJson);
}
)
}
/**
* Export functions
*/
module.exports.start = start;