Skip to content

Instantly share code, notes, and snippets.

@laurie71
Created April 5, 2011 06:52
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save laurie71/903142 to your computer and use it in GitHub Desktop.
Save laurie71/903142 to your computer and use it in GitHub Desktop.
proof-of-concept middleware to support content blocks
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();
};
};
<!-- 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>
<!-- 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>
<!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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment