Show Compiled Source of Coffee in Octopress

Every time I wrote some Javascript I use Coffeescript for those posts (and most of the time for dev also). The problem is, some people don’t know Coffee and don’t want to use it for good or bad reasons, this doesn’t matter. So I wanted to offer the possibility in this blog to switch between coffeescript and javascript files.

This blog is written with Octopress and with it you can write or modify some plugins. My goal now is to modify the plugin which convert my code block (using ```…```) from markdown to a pretty block with colors and everything and then modify it to add another block with the javascript compiled from coffee.

First we need to find this plugin… all the plugins are located in /plugins and then you will find one called backtick_code_block.rb.

This plugin contains one function render_code_block, this one is called on each page and will replace all content matching ```…``` with the block code. So in this function I will add another block when the language used is coffeescript and I will create a function to convert the coffee into javascript using coffee compiler.

backtick_code_block.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
...
def render_code_block(input)
  ...
  input.gsub(/^`{3} *([^\n]+)?\n(.+?)\n`{3}/m) do
    ...
    if @lang.nil? || @lang == 'plain'
      ...
    else
      if @lang.include? "-raw"
        ...
      else
        code = highlight(str, @lang)
        "<figure class='code'>#{@caption}#{code}</figure>"
        # here will come the code to add the new block
      end
    end
  end
end

But first we will create this function to convert our coffee in js and to be mode generic I will store this in an object which will contains all converters (later we will be able to add converter for sass, less, typescript…).

backtick_code_block.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
module BacktickCodeBlock
  ...
  CharToEscapeToConvert = /([$"])/
  Converter = {
    coffeescript: {
      command: lambda { |escaped_code| `coffee --eval --print --bare "#{escaped_code}"` },
      destination: "javascript",
      extension_in: "coffee",
      extension_out: "js"
    }
  }
  def convert converter, code
    escaped_code = code.gsub(CharToEscapeToConvert) { |match| "\\#{match}" }
    converter[:command].call escaped_code

    rescue Exception => e
      puts "/!\\ error while converting to #{converter[:destination]}"
      puts e
  end
  ...
  • CharToEscapeToConvert represent the list of character I should escape to be able to execute with shell command.
  • Converter is my object with all converters (for now only coffeescript to javascript). It contains the command to execute, the destination language and the extensions in input and in output
  • convert function will just escape one code and convert it using a converter given in parameter

So now we have our function to convert coffee into javascript we just need to update the code to display the code block.

backtick_code_block.rb
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
...
def render_code_block(input)
  ...
  input.gsub(/^`{3} *([^\n]+)?\n(.+?)\n`{3}/m) do
    ...
    if @lang.nil? || @lang == 'plain'
      ...
    else
      if @lang.include? "-raw"
        ...
      else
        code = highlight(str, @lang)

        result = "<figure class='code'>#{@caption}#{code}</figure>"
        converter = Converter[@lang.to_sym]
        if converter
          converted_code = convert(converter, str)
          if converted_code && !converted_code.empty?
            converted_code = highlight(converted_code, converter[:destination])
            caption = @caption.gsub converter[:extension_in], converter[:extension_out]
            result = """<div>
              #{result}
              <figure class='code hide'>#{caption}#{converted_code}</figure>
              <a href='#' class='toggle-code'>Toggle #{@lang}/#{converter[:destination]}</a>
            </div>"""
          end
        end
        result
      end
    end
  end
end

We try to find a converter for this language and if we found, we convert the code then highlight it (this function will create the pretty block code) and then wrap everything in a div and add a link to switch the view. We add also the class “hide” to display only one block code.

Almost done, now when you compile your files you will be able to see your code coffee and js so we just need to implement the toggle now.

First, add the “hide” class

screen.scss
1
.hide { display: none; }

then create a new js script

source/javascripts/code-toggle.coffee
1
2
3
4
5
6
7
8
9
10
11
12
(->
  clickFunction = (event) ->
    for child in @parentElement.children when child.tagName is "FIGURE"
      child.classList.toggle "hide"
    if event.preventDefault
      event.preventDefault()
    else
      event.returnValue = false
  for element in document.getElementsByClassName "toggle-code"
    element.onclick = clickFunction
  return
)()
source/javascripts/code-toggle.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
(function() {
  var clickFunction, element, _i, _len, _ref;
  clickFunction = function(event) {
    var child, _i, _len, _ref;
    _ref = this.parentElement.children;
    for (_i = 0, _len = _ref.length; _i < _len; _i++) {
      child = _ref[_i];
      if (child.tagName === "FIGURE") {
        child.classList.toggle("hide");
      }
    }
    if (event.preventDefault) {
      return event.preventDefault();
    } else {
      return event.returnValue = false;
    }
  };
  _ref = document.getElementsByClassName("toggle-code");
  for (_i = 0, _len = _ref.length; _i < _len; _i++) {
    element = _ref[_i];
    element.onclick = clickFunction;
  }
})();
Toggle coffeescript/javascript

Last step include your script in source/_includes/after_footer.html and everything is done.

Comments

Copyright © 2014 - Anthony Estebe -