I’ve been writing some code examples for some of the UI components I’m writing for Ojay, and I need to display the implementation code and stylesheet on the page. Pretty standard fare: here’s a UI example, and here’s the code you need to implement it. Easy.
Trouble is, I don’t want to duplicate the code (once in a script
block, once
displayed on the page), especially while editing. It’s only going to get out of
sync at some point, and it’s tedious to maintain. So, I cooked up a way of
getting my page to display its own source code as part of the example. First
thing you’re going to need is a script block and a textarea to display its
source:
<script type="text/javascript" id="the-script">
// code goes here...
</script>
<textarea id="the-display"><textarea>
You throw your example code in the script block and it sets up the example UI
you’re demonstrating. Then, all you need to do is extract its contents and put
the code in the textarea
for display. (It doesn’t have to be a textarea
,
although you can be sure it will preserve line breaks that other elements might
throw out.) In another script block, put the following:
var code = $('#the-script').node.innerHTML.unindent();
$('#the-display').setAttributes({
value: code,
rows: code.split(/\n/).length + 1
});
Simple enough, now you can be sure the example code that people read will match
what’s actually running on the page. Just one thing is missing: that
unindent()
function. I needed this because the script gets indented quite a
lot by my templating system, which produces nice nested easy-to-read HTML. I
need to strip off the common indentation from all the lines to just leave the
raw code. To do this, we find the minimum indentation of all the lines and then
remove it from each line of the string, like so:
String.prototype.unindent = function() {
var lines = this.replace(/^(\s*\n)*/, '')
.replace(/(\n\s*)*$/, '').split(/\n/);
var indent = lines.reduce(function(memo, line) {
if (/^\s*$/.test(line)) return memo;
var spaces = line.match(/^ +/);
if (!spaces) return memo;
return Math.min(memo, spaces[0].length);
}, Infinity);
return lines.map({slice: indent}).join("\n");
};
Not rocket science, just a nice demo of some functional programming and one less
bit of duplication in my codebase. It works with inline style
elements as
well, but so far I’ve not found any reliable way of doing it with HTML code
while maintaining line breaks in the display (at least, not without manually
parsing the HTML myself). If anyone knows how I might do this then do leave some
advice in the comments.