Neil's Blog

Literate Programming Setup

Sun Jan 19 21:46:10 2020 +1100

A big part of this blog is to act as a technical portfolio. Here I want to be able to write about technical, often programming, concepts and provide example code with examples of it working. Basically, I want to be able to do Literate Programming; I want to talk about a concept, why you would do it and how, and then write the code.

Literate Programming is often criticised as being an overkill approach to documentation, as there are very few places you actually need to go into that much detail as to why you are doing something. It does really lend itself well to reports and blogs, where you are reading a narrative instead of code.

Exporting JavaScript code from org-mode

I'm writing my blog in Emacs' org-mode. One of the advantages that org-mode has over markdown is that it has been built as a literate programming tool, with org-babel, a package that takes code blocks and executes them. It will then run code blocks on export, showing the results.

(+ 1 1)

I can straight up write in elisp, as Emacs is basically a big lisp interpreter, and it is available, but this is a web-log; it is on the web, so I'm going to start a bit with JavaScript. Not to mention that there are a lot more jobs in web dev…

Now, I could enable JavaScript in babel, basically running all the code when I export the page, and just showing the results, but if I just stick the code on the web page, then it will evaluate in your, the reader's, browser. This will allow me to do do things that you can interact with.

To do this, I will actually write an elisp function that takes a code block and exports it as html <script> tags. We will make it a named block so that we can call it from org mode latter. To do this, we use the #+NAME: heading, and add :results html to the beginning of the code block.

(format
  "<script type=\"text/JavaScript\">\n%s\n</script>\n"
  (save-excursion
    (org-babel-next-src-block)
    (cadr (org-babel-get-src-block-info))))

format is the elisp format string function. We move to the next org-babel block, and then get the string that represents its contents, feed it into format to stick it inside the <script> tags.

By naming it and setting the code block to export the results as html, we have a block that we can call that sticks the next block in script tags as raw HTML.

#+CALL:inlineJSBlock()
#+BEGIN_SRC javascript
console.log("Hi From this unnamed text block, being called by inlineJSBlock()")
#+END_SRC

Becomes:

console.log("Hi From this unnamed text block, being called by inlineJSBlock()")

Have a look at your console log. and you will see that the log has actually updated. If you look at the source, you will also see that the <script> tags have actually been inserted between the #+CALL block and the JavaScript code block, but it works.

Loading JS Libraries

The other thing we are going to need, is a block that will load a JavaScript library. We can use the same principle, write an elisp code block that we can call in org-mode, but instead of jumping code blocks, we can just parse it a variable, using :var name in the source block.

(format "<script src=\"%s\"></script>" libpath)
#+CALL:includeJSLibrary(libpath="/scripts/lib/jquery-3.4.1.min.js")

<script src="/scripts/lib/jquery-3.4.1.min.js"></script>

Doing jQuery tutorials

Now that I'm set up to export JavaScript to <script> tags, and I've loaded in jQuery, lets do some quick things to show off interactive functionality.

jQuery's alert example

The obvious place to start is jQuery's first few examples. One of the first that is sort of equivalent to a "hello, world" example is their alert example. They basically setup a callback for the click hook on a elements.

$( document ).ready(function() {
  $( 'a' ).click(function(event) {
    alert('Thanks for visiting!');
  });
});

So, now, any time you click on an a element, or a link, from this page, it will send you an alert before it loads the new page. Here, go to google and then come back.

Source block hover effect

Something you've probably noticed is the hover effect for the code blocks. I've setup callbacks for the mouseover and mouseout events that change the css styles.

They aren't perfect, as hovering over nested elements counts as mouseout, but it is a nice way to show interaction with a code heavy block post.

$( document ).ready(function() {
  $( "pre.src" ).mouseover((event) => {
    let style = {
      "box-shadow": ".5em .5em .5em #1a1a1a",
      "border": "2px solid #121212"
    };
    $( event.target ).css(style);
  });
  $( "pre.src" ).mouseout(() => {
    let style = {
      "box-shadow": ".3em .3em .3em #2a2a2a",
      "border": "1px solid #121212"
    };
    $( event.target ).css(style);
  });
});

Folding sections

Another potentially useful example is folding sections. jQuery has a handy function specifically to do this, called slideToggle. The way my export function is writing html, it is building sections so that a div surrounds a header a section content div, like this:

<div>
  <h3>Section title</h3>
  <div>
     ...
  </div>
</div>

This means that I can just use the next method on a jQuery class.

$( document ).ready(() => {
  $( "h3" ).click(() => {
    $( event.target ).next().slideToggle("slow");
  });
});

Note that org-export html by default encloses the section header inside the section header div, so using next() wont work if a h3 has subsections, which are exported after the content <div>.

Now that we have the sections folding, we need to set something up to telegraph that sections titles can be interacted with, so that the user knows that they can actually do something with it.

I've done a couple of quick, simple things, as this is only an example. You may have noticed that the subsections turned blue. This time, instead of using mouseover and mouseout, I've just used hover and fed it a start and end callback.

The other thing that I thought was worth doing was changing the heading text to mark when a section has been collapsed. To achieve this I've just modified the target's innerHTML element to include an elipsis if it didn't have one, and remove one if it is already there.

$( document ).ready(() => {
  $( "h3" ).hover(() => {
    $( event.target ).css("color", "#8ac6f2");
  }, () => {
    $( event.target ).css("color", "white");
  });

  $( "h3" ).click(() => {
    let header = event.target.innerHTML;
    if ("..." == header.substring(header.length - 3))
      event.target.innerHTML = header.substring(0, header.length - 3);
    else
      event.target.innerHTML += "...";
  });
});

And there we have it. I can write a blog post about codeing things, explaining why I am doing it and showing my code, and actually have it run in browser, so they reader can interact with it.

I'm going to do a series as I build a character sheet for a role playing game, which should allow you to interact with it as you go.