Fix node html compression and refactor registry

This commit is contained in:
Nick O'Leary 2014-07-28 16:37:39 +01:00
parent 3d31a0abca
commit e07a523c3b
4 changed files with 148 additions and 111 deletions

View File

@ -400,7 +400,7 @@
}, },
button: { button: {
onclick: function() { onclick: function() {
var label = (this.name||this.payload).replace(/&/g,"&amp;amp;").replace(/</g,"&amp;lt;").replace(/>/g,"&amp;gt;"); var label = (this.name||this.payload).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;");
d3.xhr("inject/"+this.id).post(function(err,resp) { d3.xhr("inject/"+this.id).post(function(err,resp) {
if (err) { if (err) {
if (err.status == 404) { if (err.status == 404) {

View File

@ -150,9 +150,9 @@
} }
}; };
var name = (o.name?o.name:o.id).toString().replace(/&/g,"&amp;amp;").replace(/</g,"&amp;lt;").replace(/>/g,"&amp;gt;"); var name = (o.name?o.name:o.id).toString().replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;");
var topic = (o.topic||"").toString().replace(/&/g,"&amp;amp;").replace(/</g,"&amp;lt;").replace(/>/g,"&amp;gt;"); var topic = (o.topic||"").toString().replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;");
var payload = (o.msg||"").toString().replace(/&/g,"&amp;amp;").replace(/</g,"&amp;lt;").replace(/>/g,"&amp;gt;"); var payload = (o.msg||"").toString().replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;");
msg.className = 'debug-message'+(o.level?(' debug-message-level-'+o.level):'') msg.className = 'debug-message'+(o.level?(' debug-message-level-'+o.level):'')
msg.innerHTML = '<span class="debug-message-date">'+getTimestamp()+'</span>'+ msg.innerHTML = '<span class="debug-message-date">'+getTimestamp()+'</span>'+
'<span class="debug-message-name">['+name+']</span>'+ '<span class="debug-message-name">['+name+']</span>'+

View File

@ -29,7 +29,9 @@ var settings;
var node_types = {}; var node_types = {};
var node_configs = []; var node_configs = [];
var node_scripts = [];
//TODO: clear this cache whenever a node type is added/removed
var node_config_cache = null;
/** /**
* Synchronously walks the directory looking for node files. * Synchronously walks the directory looking for node files.
@ -63,7 +65,7 @@ function getNodeFiles(dir) {
* Scans the node_modules path for nodes * Scans the node_modules path for nodes
* @return a list of node modules: {dir,package} * @return a list of node modules: {dir,package}
*/ */
function scanTreeForNodesModules() { function scanTreeForNodesModules(moduleName) {
var dir = __dirname+"/../../nodes"; var dir = __dirname+"/../../nodes";
var results = []; var results = [];
var up = path.resolve(path.join(dir,"..")); var up = path.resolve(path.join(dir,".."));
@ -72,6 +74,7 @@ function scanTreeForNodesModules() {
try { try {
var files = fs.readdirSync(pm); var files = fs.readdirSync(pm);
files.forEach(function(fn) { files.forEach(function(fn) {
if (!moduleName || fn == moduleName) {
var pkgfn = path.join(pm,fn,"package.json"); var pkgfn = path.join(pm,fn,"package.json");
try { try {
var pkg = require(pkgfn); var pkg = require(pkgfn);
@ -84,6 +87,7 @@ function scanTreeForNodesModules() {
// TODO: handle unexpected error // TODO: handle unexpected error
} }
} }
}
}); });
} catch(err) { } catch(err) {
} }
@ -94,53 +98,6 @@ function scanTreeForNodesModules() {
return results; return results;
} }
/**
* Loads the specified node into the registry.
* @param nodeDir the directory containing the node
* @param nodeFn the node file
* @param nodeLabel the name of the node (npm nodes only)
* @return a promise that resolves to either {fn,path,err}
*/
function loadNode(nodeDir, nodeFn, nodeLabel) {
if (settings.nodesExcludes) {
for (var i=0;i<settings.nodesExcludes.length;i++) {
if (settings.nodesExcludes[i] == nodeFn) {
return when.resolve();
}
}
}
var nodeFilename = path.join(nodeDir,nodeFn);
try {
var loadPromise = null;
var r = require(nodeFilename);
if (typeof r === "function") {
var promise = r(require('../red'));
if (promise != null && typeof promise.then === "function") {
loadPromise = promise.then(function() {
return when.resolve({fn:nodeLabel||nodeFn,path:nodeFilename});
}).otherwise(function(err) {
return when.resolve({fn:nodeLabel||nodeFn,path:nodeFilename,err:err});
});
}
}
if (loadPromise == null) {
loadPromise = when.resolve({fn:nodeLabel||nodeFn,path:nodeFilename});
}
return loadPromise;
} catch(err) {
return when.resolve({fn:nodeLabel||nodeFn,path:nodeFilename,err:err});
}
}
/**
* Loads the html template file for a node
* @param templateFilanem
*/
function loadTemplate(templateFilename) {
var content = fs.readFileSync(templateFilename,'utf8');
registerConfig(content);
}
/** /**
* Loads the nodes provided in an npm package. * Loads the nodes provided in an npm package.
* @param moduleDir the root directory of the package * @param moduleDir the root directory of the package
@ -153,7 +110,7 @@ function loadNodesFromModule(moduleDir,pkg) {
var iconDirs = []; var iconDirs = [];
for (var n in nodes) { for (var n in nodes) {
if (nodes.hasOwnProperty(n)) { if (nodes.hasOwnProperty(n)) {
promises.push(loadNode(moduleDir,nodes[n],pkg.name+":"+n)); promises.push(loadNode(path.join(moduleDir,nodes[n]),pkg.name,n));
var iconDir = path.join(moduleDir,path.dirname(nodes[n]),"icons"); var iconDir = path.join(moduleDir,path.dirname(nodes[n]),"icons");
if (iconDirs.indexOf(iconDir) == -1) { if (iconDirs.indexOf(iconDir) == -1) {
if (fs.existsSync(iconDir)) { if (fs.existsSync(iconDir)) {
@ -167,6 +124,105 @@ function loadNodesFromModule(moduleDir,pkg) {
} }
/**
* Loads the specified node into the registry.
* @param nodeFile the fully qualified path of the node's .js file
* @param nodeModule the name of the module (npm nodes only)
* @param nodeName the name of the node (npm nodes only)
* @return a promise that resolves to a node info object
* {
* name: the name of the node file, or label from the npm module
* module: the name of the node npm module (npm nodes only)
* path: the fully qualified path to the node's .js file
* template: the fully qualified path to the node's .html file
* config: the non-script parts of the node's .html file
* script: the script part of the node's .html file
* err: any error encountered whilst loading the node
* }
* The node info object must be added to the node_config array by the caller.
* This allows nodes to be added in a defined order, regardless of how async
* their loading becomes.
*/
function loadNode(nodeFile, nodeModule, nodeName) {
var nodeDir = path.dirname(nodeFile);
var nodeFn = path.basename(nodeFile);
if (settings.nodesExcludes) {
for (var i=0;i<settings.nodesExcludes.length;i++) {
if (settings.nodesExcludes[i] == nodeFn) {
return when.resolve();
}
}
}
var nodeFilename = path.join(nodeDir,nodeFn);
var nodeInfo = {name:nodeFn, path:nodeFilename};
if (nodeModule) {
nodeInfo.name = nodeModule+":"+nodeName;
nodeInfo.module = nodeModule;
}
try {
var loadPromise = null;
var r = require(nodeFilename);
if (typeof r === "function") {
var promise = r(require('../red'));
if (promise != null && typeof promise.then === "function") {
loadPromise = promise.then(function() {
nodeInfo = loadTemplate(nodeInfo);
return when.resolve(nodeInfo);
}).otherwise(function(err) {
nodeInfo.err = err;
return when.resolve(nodeInfo);
});
}
}
if (loadPromise == null) {
nodeInfo = loadTemplate(nodeInfo);
loadPromise = when.resolve(nodeInfo);
}
return loadPromise;
} catch(err) {
nodeInfo.err = err;
return when.resolve(nodeInfo);
}
}
/**
* Loads the html template file for a node
* @param templateFilanem
*/
function loadTemplate(nodeInfo) {
var templateFilename = nodeInfo.path.replace(/\.js$/,".html");
var content = fs.readFileSync(templateFilename,'utf8');
$ = cheerio.load(content);
var template = "";
var script = "";
$("*").each(function(i,el) {
if (el.type == "script" && el.attribs.type == "text/javascript") {
script += el.children[0].data;
} else if (el.name == "script" || el.name == "style") {
var openTag = "<"+el.name;
var closeTag = "</"+el.name+">";
if (el.attribs) {
for (var j in el.attribs) {
if (el.attribs.hasOwnProperty(j)) {
openTag += " "+j+'="'+el.attribs[j]+'"';
}
}
}
openTag += ">";
template += openTag+$(el).text()+closeTag;
}
});
nodeInfo.template = templateFilename;
nodeInfo.config = template;
nodeInfo.script = script;
return nodeInfo;
}
function init(_settings) { function init(_settings) {
Node = require("./Node"); Node = require("./Node");
settings = _settings; settings = _settings;
@ -197,9 +253,7 @@ function load() {
// Load all of the nodes in the order they were discovered // Load all of the nodes in the order they were discovered
var loadPromises = []; var loadPromises = [];
nodeFiles.forEach(function(file) { nodeFiles.forEach(function(file) {
var dir = path.dirname(file); loadPromises.push(loadNode(file));
var fn = path.basename(file);
loadPromises.push(loadNode(dir,fn));
}); });
moduleFiles.forEach(function(file) { moduleFiles.forEach(function(file) {
@ -213,64 +267,40 @@ function load() {
// Store the error to pass up // Store the error to pass up
errors.push(result.value); errors.push(result.value);
} else { } else {
// Load the node template node_configs.push(result.value);
var templateFilename = result.value.path.replace(/\.js$/,".html");
loadTemplate(templateFilename);
} }
}); });
// Trigger a load of the configs to get it precached
getNodeConfigs();
resolve(errors); resolve(errors);
}); });
}); });
} }
/**
* Registers a node's html configuration.
*/
function registerConfig(config) {
$ = cheerio.load(config);
var template = "";
$("*").each(function(i,el) {
if (el.type == "script" && el.attribs.type == "text/javascript") {
var content = el.children[0].data;
el.children[0].data = UglifyJS.minify(content, {fromString: true}).code;
node_scripts.push($(this).text());
} else if (el.name == "script" || el.name == "style") {
var openTag = "<"+el.name;
var closeTag = "</"+el.name+">";
if (el.attribs) {
for (var j in el.attribs) {
if (el.attribs.hasOwnProperty(j)) {
openTag += " "+j+'="'+el.attribs[j]+'"';
}
}
}
openTag += ">";
template += openTag+$(el).text()+closeTag;
}
});
node_configs.push(template);
}
/** /**
* Gets all of the node template configs * Gets all of the node template configs
* @return all of the node templates in a single string * @return all of the node templates in a single string
*/ */
function getNodeConfigs() { function getNodeConfigs() {
if (!node_config_cache) {
var result = ""; var result = "";
var script = "";
for (var i=0;i<node_configs.length;i++) { for (var i=0;i<node_configs.length;i++) {
result += node_configs[i]; var config = node_configs[i];
result += config.config||"";
script += config.script||"";
} }
result += '<script type="text/javascript">'; result += '<script type="text/javascript">';
for (var j=0;j<node_scripts.length;j++) { result += UglifyJS.minify(script, {fromString: true}).code;
result += node_scripts[j];
}
result += '</script>'; result += '</script>';
return result; node_config_cache = result;
}
return node_config_cache;
} }
var typeRegistry = module.exports = { module.exports = {
init:init, init:init,
load:load, load:load,
registerType: function(type,node) { registerType: function(type,node) {
@ -281,6 +311,13 @@ var typeRegistry = module.exports = {
get: function(type) { get: function(type) {
return node_types[type]; return node_types[type];
}, },
getNodeConfigs: getNodeConfigs getNodeConfigs: getNodeConfigs,
loadNode: function(filename) {
},
removeNode: function(filename) {
}
} }

View File

@ -76,7 +76,7 @@ function start() {
util.log("------------------------------------------"); util.log("------------------------------------------");
if (settings.verbose) { if (settings.verbose) {
for (var i=0;i<nodeErrors.length;i+=1) { for (var i=0;i<nodeErrors.length;i+=1) {
util.log("["+nodeErrors[i].fn+"] "+nodeErrors[i].err); util.log("["+nodeErrors[i].name+"] "+nodeErrors[i].err);
} }
} else { } else {
util.log("[red] Failed to register "+nodeErrors.length+" node type"+(nodeErrors.length==1?"":"s")); util.log("[red] Failed to register "+nodeErrors.length+" node type"+(nodeErrors.length==1?"":"s"));