about:benjie

Random learnings and other thoughts from an unashamed geek

Octopress: CoffeeScript/JS Code Toggle

| Comments

Dru Riley asked via a comment how I achieved the CoffeeScript/JS highlighting switch on my blog which looks like this:

.coffee.js<-- These buttons toggle CoffeeScript/JavaScript
1
2
3
4
CoffeeScript is 'awesome'
dance() if you.love CoffeeScript
i.love CoffeeScript
yay()

The answer: a hack. I don’t code ruby, so I couldn’t figure out how to do it neatly (or even if I could - feedback welcome!). The easiest way for me to do it was just to modify plugins/backtick_code_block.rb, then add the JS and CSS to make it work.

If you want to see how I did it, or do it yourself, here are my diffs:

NOTE: These diffs should be ran against revision c069dc7 of Octopress, which is already quite out of date…

Hack backtick plugin to highlight both JS and CS (plugins/backtick_code_block.rb.diff) download
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
diff --git a/plugins/backtick_code_block.rb b/plugins/backtick_code_block.rb
index 40e7900..4450f12 100644
--- a/plugins/backtick_code_block.rb
+++ b/plugins/backtick_code_block.rb
@@ -14,12 +14,18 @@ module BacktickCodeBlock
       @options = $1 || ''
       str = $2

+      extra = ''
+      id = rand(999999999)
+      fig_id = "figure_#{id}"
+
       if @options =~ AllOptions
         @lang = $1
-        @caption = "<figcaption><span>#{$2}</span><a href='#{$3}'>#{$4 || 'link'}</a></figcaption>"
+        extra = genExtra @lang, fig_id
+        @caption = "<figcaption>#{extra}<span>#{$2}</span><a href='#{$3}'>#{$4 || 'link'}</a></figcaption>"
       elsif @options =~ LangCaption
         @lang = $1
-        @caption = "<figcaption><span>#{$2}</span></figcaption>"
+        extra = genExtra @lang, fig_id
+        @caption = "<figcaption>#{extra}<span>#{$2}</span></figcaption>"
       end

       if str.match(/\A( {4}|\t)/)
@@ -35,9 +41,40 @@ module BacktickCodeBlock
           raw += "\n```\n"
         else
           code = highlight(str, @lang)
-          "<figure class='code'>#{@caption}#{code}</figure>"
+          if extra == ''
+            "<figure class='code'>#{@caption}#{code}</figure>"            
+          else
+            alt_lang = ''
+            str2 = ''
+            altcode = ''
+            if @lang == 'coffee-script'
+              lang_display = 'CoffeeScript'
+              alt_lang = 'javascript'
+              alt_lang_display = 'JavaScript'
+              stdin, stdout, stderr, wait_thr = Open3.popen3("coffee -s -b -c -p")
+              stdin.print(str)
+              stdin.close
+              str2 = stdout.readlines(nil)[0]
+              if !str2
+                str2 = stderr.readlines(nil)[0]
+                str2 = "Compilation failed: \n#{str2}"
+              end
+              stdout.close
+              stderr.close
+              exit_status = wait_thr.value
+            end
+            if alt_lang != ''
+              altcode = highlight(str2, alt_lang)
+            end
+            "<figure class='code'>#{@caption}<div clang='#{lang_display}' id=\"#{fig_id}\">#{code}</div><div clang='#{alt_lang_display}' id=\"#{fig_id}_alt\" style=\"display:none;\">#{altcode}</div></figure>"
+          end
         end
       end
     end
   end
+  def genExtra(lang,fig_id)
+    if @lang == "coffee-script"
+      return "<span class=\"switchLang\"><a class=\"switchLang selected\" onclick=\"switchLang(event,this,'#{fig_id}');\">.coffee</a><a class=\"switchLang\" onclick=\"switchLang(event,this,'#{fig_id}');\">.js</a></span>"
+    end
+  end
 end
The JavaScript to allow the user to toggle (source/javascripts/switchlang.js) download
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
function switchLang(e,me,id) {
  if (me.className.match(/selected/)) {
    return;
  }
  if (!e) e = window.event;
  if (e.preventDefault) {
    e.preventDefault();
  }
  if (!me.onmouseup) {
    me.onmouseup = function(e) {
      if (e && e.preventDefault) {
        e.preventDefault();
      }
    }
  }
  var el1 = document.getElementById(id);
  var el2 = document.getElementById(id+"_alt");
  if (!el1 || !el2) {
    return;
  }
  if (el1.style.display == 'none') {
    el2.style.display = 'none';
    el1.style.display = 'block';
    me.parentNode.childNodes[0].className = "switchLang selected";
    me.parentNode.childNodes[1].className = "switchLang";
  } else {
    el1.style.display = 'none';
    el2.style.display = 'block';
    me.parentNode.childNodes[0].className = "switchLang";
    me.parentNode.childNodes[1].className = "switchLang selected";
  }
}
Require the JS (source/_includes/custom/head.html.diff) download
1
2
3
4
5
6
7
8
9
diff --git a/source/_includes/custom/head.html b/source/_includes/custom/head.html
index 85879f4..96009eb 100644
--- a/source/_includes/custom/head.html
+++ b/source/_includes/custom/head.html
@@ -1,3 +1,7 @@
 <!--Fonts from Google"s Web font directory at http://google.com/webfonts -->
 <link href="http://fonts.googleapis.com/css?family=PT+Serif:regular,italic,bold,bolditalic" rel="stylesheet" type="text/css">
 <link href="http://fonts.googleapis.com/css?family=PT+Sans:regular,italic,bold,bolditalic" rel="stylesheet" type="text/css">
+<script src="/javascripts/switchlang.js"></script>
Add some (terrible) styling (sass/custom/_styles.scss.diff) download
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
diff --git a/sass/custom/_styles.scss b/sass/custom/_styles.scss
index 680d07f..85db5e3 100644
--- a/sass/custom/_styles.scss
+++ b/sass/custom/_styles.scss
@@ -13,4 +13,102 @@ footer {
          vertical-align:top;
      }
  }
-}
\ No newline at end of file
+}
+
+figure.code figcaption {
+  & a.switchLang {
+    color:white !important;
+    text-shadow: #333 0 1px 0;
+  }
+  span.switchLang {
+    position:absolute;
+    left: .8em;
+    right:auto;
+    padding-left:0;
+    padding-right:3em;
+    -webkit-user-select:none;
+    -moz-user-select:none;
+    user-select:none;
+    -webkit-transition: text-shadow, color 0.3s;
+  }
+  a.switchLang {
+    -webkit-user-select:none;
+    -moz-user-select:none;
+    user-select:none;
+    -webkit-transition: text-shadow, color 0.3s;
+    background-color: #888;
+    position:initial;
+    right:auto;
+    padding:0;
+    padding-left:.8em;
+    padding-right:.8em;
+    //border:1px solid black;
+    display: inline-block;
+    border-top-right-radius:6px;
+    border-bottom-right-radius:6px;
+    border-left:0;
+    height: 1.4em;
+    line-height: 1.4em;
+  }
+  a.switchLang:first-child {
+    //border-right:0;
+    //border-left:1px solid black;
+    border-radius:0;
+    border-top-left-radius:6px;
+    border-bottom-left-radius:6px;
+    
+  }
+  a.switchLang.selected {
+    background-color:#666;
+  }
+}

Thanks for your question, Dru!

Comments