public
Created

proof-of-concept middleware to support content blocks

  • Download Gist
blocks.js
JavaScript
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88
var debug = require('util').debug;
 
// helper prototype for rendering block scripts, stylesheet links, etc
var blocktag = {
toString: function() {
var tag = this.tag,
html = '<'+tag,
that = this;
Object.getOwnPropertyNames(this).forEach(function(prop) {
if (prop !== 'tag') {
html += ' '+prop+'="'+that[prop]+'"';
}
});
if (tag === 'script') {
html += '></script>';
} else {
html += ' />';
}
return html;
}
}
 
module.exports = function(app) {
return function(req, res, next) {
// request-local hash of named blocks
var blocks = {};
 
// dynamic helper for defining/rendering blocks
function block(name) {
var block, html;
 
if (arguments.length === 0) return next(new Error('unnamed block'));
block = blocks[name];
 
if (arguments.length === 1) {
// render block contents
html = '';
if (block) {
block.forEach(function(item) {
html = String(item) + html;
});
}
debug('block: '+name+': '+JSON.stringify(block));
debug('html: '+html);
return html;
} else {
// define/append block contents
if (! block) block = blocks[name] = [];
block.push(Array.prototype.slice.call(arguments, 1));
debug('block: '+name+': '+JSON.stringify(block));
return '';
}
};
// utility method for adding a script to a block
block.script = function(src) {
return Object.create(blocktag, {
src : { enumerable: true, value: src },
tag : { enumerable: true, value: 'script' },
type: { enumerable: true, value: 'text/javascript' }
});
};
// utility method for adding a stylesheet to a block
block.stylesheet = function(href, media) {
var tag = Object.create(blocktag, {
href: { enumerable: true, value: href },
tag : { enumerable: true, value: 'link' },
type: { enumerable: true, value: 'text/css' },
rel : { enumerable: true, value: 'stylesheet' }
});
if (media) {
tag.media = media;
}
return tag;
};
 
debug('add helpers');
// res.local('block', block);
// req.app.dynamicHelpers(block);
// req.app.helpers(block);
app.helpers({block:block});
next();
};
};
index.ejs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
<!-- use blocks.js to inject content into layout -->
<!-- use new layout() helper to get nested layouts -->
 
<% layout('layout_main') %>
<% block('head', block.stylesheet('page.css')) %>
<% block('foot', block.script('page.js')) %>
 
<h2>Index Page</h2>
<p>
This is the main page content.
It is wrapped by the <t>layout_main</t> layout.
It adds the following blocks to the layout:
</p>
<ul><li>A page.css stylesheet in the document head</li>
<li>A page.js script at the document foot</li>
</ul>
layout_main.ejs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
<!-- use blocks.js to inject content into *parent* layout -->
<!-- use new layout() helper to select parent layout -->
 
<% layout('layout_site') %>
<% block('head', block.stylesheet('layout.css')) %>
<% block('foot', block.script('layout.js')) %>
 
<h1>Main Layout</h1>
<p>
This is the main page layout.
It is wrapped by the <t>layout_site</t> layout.
It adds the following blocks to the layout:
</p>
<ul><li>A layout.css stylesheet in the document head</li>
<li>A layout.js script at the document foot</li>
</ul>
layout_site.ejs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
<!doctype html>
<head>
<meta charset=utf-8>
<title>Nested Layouts with Blocks Demo</title>
<!-- pull in the 'head' block content defined by our child layouts/pages -->
<%- block('head') %>
</head>
<body>
<h1>Site Layout</h1>
<p>
This is the master site layout.
It is not wrapped by a layout.
It includes the following dynamic blocks:
</p>
<ul><li><t>head</t>: a block whose content is rendered in the HTML 'head' section</li>
<li><t>foot</t>: a block whose content is rendered just before the closing 'body' tag</li>
</ul>
 
<!-- pull in the 'foot' block content defined by our child layouts/pages -->
<%- block('foot') %>
</body>
</html>

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.