I’ve been trying out various in-browser template libraries, like mustache.js and jQote, where you put HTML templates into <script type="text/html"> tags. The browser is required to ignore them, since text/html isn’t a scripting language, but you can use regular editing tools on them, which isn’t true if the template is inside a JavaScript string as in other template libraries.

I also like using Haml to generate HTML, and it turns out these two things aren’t quite compatible out of the box. The trouble is the CDATA section that’s required to keep the HTML valid in this scheme. You can’t do this:

<script id='foo_tmpl' type='text/x-mustache'>
  <p>This is Haml enclosed in CDATA.</p>
  <p>You can use it for templating {{stuff}}.</p>
</script>

Just as you have to escape < in a JavaScript script tag, you have to do it in a template script tag. Or, much easier, you can surround the contents with CDATA tags like so:

<script id='foo_tmpl' type='text/x-mustache'>
  <![CDATA[
    <p>This is Haml enclosed in CDATA.</p>
    <p>You can use it for templating {{stuff}}.</p>
  ]]>
</script>

Unfortunately, there’s no built-in way to do this and use Haml to write the inner HTML. There’s a :cdata filter in Haml, but it takes the contents literally.

To get around this, I wrote a :hamlcdata filter that runs the contents through Haml, then surrounds the output with CDATA tags. It’s a bit fragile, because I had to turn off #{} interpolation (that needs to happen in the inner Haml run), and there’s no way to do that with the stable API. I had to override the compile method, which means this code may need to be revised if Filters::Base#compile changes. However, since the change was simply to delete some code (commented out below), it shouldn’t be too hard to keep up.