This document describes how to integrate your articles (in Emacs org-mode format) into jekyll based HTML site with twitter bootstrap scaffolding style.
Note that this article is heavily influenced by Using org to Blog with Jekyll.
. ├── _plugins # custom Jekyll/Liquid plugins ├── org # Your org files should be placed here ├── src # Jekyll source directory │ ├── _layouts │ └── articles # org generated html files are installed here └── www # Jekyll destination directory └── articles # final destination of your .org based html files
All your .org
files should be located in org/
directory.
Since, jekyll requires that all source files that
Your .org
file should have following header.
#+BEGIN_HTML --- layout: org title: Jekyll with org-mode and twitter bootstrap --- #+END_HTML
You MUST change the value of some keys in _config.yml
, especially
for bootstrap
, prettify
, and jquery
.
bootstrap: "http://some.place.com/bootstrap" prettify: "http://some.place.com/bootstrap" jquery: "http://some.place.com/bootstrap/js/jquery.js"
You may need to modify both src/_layouts/default.html
and
src/_layouts/org.html
to reflect the above configuration. For example,
default.html
contains a HTML tags such as:
<link href="http://some.place.com/bootstrap/css/bootstrap.css" rel="stylesheet"/> <link href="http://some.place.com/bootstrap/css/bootstrap-responsive.css" rel="stylesheet"/> <link href="http://some.place.com/bootstrap/css/docs.css" rel="stylesheet"/> <link href="http://some.place.com/prettify.css" rel="stylesheet"/> ... <script src="http://some.place.com/bootstrap/js/jquery.js"></script>
To use make(1) to build your html generation, the jekyll should
not create HTTP server; make sure that you have "auto=false
".
To use org-specific Jekyll plugins, make sure that you have
"safe=false
".
To use custom plug-ins provided in this package, you may need to provide several custom variable in the YAML front matter of each page.
To work with jekyll-org
, it is recommended you provide following
variables:
id
: id of the page, this will be used as the internal identification
of the page content. Note that if there are multiple pages written
in several languages and their contents are the same, the id
of these
pages should be the same. (mandatory)
label
: short name of the page, which may be used as the text of
some hyperlinks. If omitted, the page id
will be used
for label
value. (optional)
title
: title(full name) of the page, which will be used as the
text of some hyperlinks. (mandatory)
lead
: descriptive sentence to describe the purpose of the page.
(optional)
lang
: mnemonic of the page locale. You can omit this
variable, if the page is in the your primary language. In this
case, jekyll-org
will treat the page locale to 'en
'
(English).
So, the full example of the YAML front matter will look like:
--- layout: default title: Jekyll with Org-mode label: Home id: home lead: static site generated by Jekyll, with org-mode articles ---
To help generating top-level page index, jekyll-org
provides
toplink
block. toplink
block will uses the list of page
IDs, evaulating the contents in the block multiple times
according to the number of elements in topnav-list
of the
_config.yml
as in the following:
topnav-list: [ "home", "article", "pg1", "pg2" ]
toplink
tag accepts five parameters separated by
comma('=,=') character. For example:
{% toplink id, label, locale, url, self %} ... {% endtoplink %}
Each parameter name will turn to the variable name which can be
used in the toplink
block. The meaning of the variables are:
topnav-list
.
true
if the current page is the same
as the evaluating page.
For example, if you want to create hyperlinks to the top-level pages, you can do this by:
<ul> {% toplink id, label, locale, url, self %} <li><a href="{{url}}">{{label}}</a></li> {% endtoplink %} </ul>
If you want to remove the hyperlink for the current page, among evaluating pages, you can check the 5th parameter as in the following:
<ul> {% toplink id, label, locale, url, self %} {% if self == true %} <li>{{label}}</li> {% else %} <li><a href="{{url}}">{{label}}</a></li> {% endif %} {% endtoplink %} </ul>
articles
block can enumerate pages which has a specified
layouts.
articles
block will be evaluated multiple times for each
article. The usage is almost the same as toplink
block.
articles
will accept four parameters, and each meaning of the
parameter is:
_config.yml
where its value is a list of layout names.
For example, suppose that you have pages in either "my_article1" layout
or "my_article2" layout. To enumerate these pages, you need to specify
the list of layouts in _config.yml
like:
article_layouts: [ "my_article1", "my_article2" ]
Then, you can enumerate these pages with article
block like:
<ul> {% articles article_layouts, id, title, lead, url, lang %} {% if lang == "" %} <li><a href="{{url}}">{{title}}</a></li> {% else %} <li><a href="{{url}}">{{title}}</a> ({{lang}}) </li> {% endif %} {% endarticles %} </ul>
Or, if you want to insert 'lead' value, use the following:
<ul> {% articles article_layouts, id, title, lead, url, lang %} {% if lead == nil or lead == "" %} {% assign leadsep = '' %} {% else %} {% assign leadsep = ': ' %} {% endif %} {% if lang == "" %} <li><a href="{{url}}">{{title}}</a>{{leadsep}}{{lead}}</li> {% else %} <li><a href="{{url}}">{{title}}</a> ({{lang}}){{leadsep}}{{lead}}</li> {% endif %} {% endarticles %} </ul>
You may have some problem on the generated table of contents, which looks like following:
The problem is that, your Emacs scripts set
org-odd-levels-only
to t
, but the batch script, publish.el
in the package, does not aware of it. To work around this,
add "odd
" in the STARTUP
keyword.
Your Emacs uses out-dated org-version probably 6.x. Install recent org-mode.
At first, to convert org files into twitter's scaffolding style, I need a way to generate first-level headings into scaffolding way.
However, to parse org file directly to gereate scaffolding TOC
is somewhat difficult to implement; I briefly overlooked
org-html.el
in org-mode, which is fairly complicated.
Since this is just my hobby to publish my own site, I decieded to make a Jekyll/Liquid plugin, that parses the HTML (which org-mode exported) to get the first-level headings, and use Liquid markup to generate scaffolding TOC.
Thankfully, org-mode HTML output is fairly simple. Here are an example of HTML that org-mode generated:
<div id="table-of-contents"> <h2>Table of Contents</h2> <div id="text-table-of-contents"> <ul> <li><a href="#sec-1">1 Introduction</a> <ul> <li><a href="#sec-1-1">1.1 Conventions in this article</a></li> <li><a href="#id-core">1.2 ID management</a></li> </ul> </li> <li><a href="#sec-2">2 Service Components Layout</a> <ul> <li><a href="#sec-2-1">2.1 Permanent Cluster</a></li> <li><a href="#sec-2-2">2.2 Caching Cluster</a></li> </ul> </ul> </div> </div>
As you can see, all hyperlink IDs are in the form of sec-N-M
, so
it can be easily extracted with the following regular expression:
/^ *<li> *<a +href *= *"(#sec-[0-9]+)" *>(.*?)<\/a>/
So, I made a Jekyll plugin, which parses the org-generated HTML file, and put the metadata of the each top-level headings in the Jekyll context, so that the Jekyll layout pages can take benefits from the context:
module Jekyll class OrgToc < Liquid::Block ... def render(context) contents = context['page']['content'] thetoc = [] contents.each_line { |line| m = /^ *<li> *<a +href *= *"(#sec-[0-9]+)" *>(.*?)<\/a>/.match line if m thetoc.push( { "id" => m[1], "title" => m[2] }) end break if / *<div +id *= *"outline-container-[0-9]+\"/.match line } context['page']['orgtoc'] = thetoc super end ... end end Liquid::Template.register_tag('orgtoc', Jekyll::OrgToc)
Then, the Jekyll layout for org HTML files will uses page.orgtoc
to
generate scaffolding TOC:
<div class="span3 bs-doc-sidebar"> <ul class="nav nav-list bs-docs-sidenav"> {% orgtoc %} {% for item in page.orgtoc %} <li><a href="{{item.id}}"><i class="icon-chevron-right"></i> {{ item.title }}</a></li> {% endfor %} {% endorgtoc %} </ul> </div>
However, there are some problems with this approach.
In scaffolding page, one of the left-side navigation item is highlighted, depending on the current location of the contents.
I cannot make this happen on org-generated page, since the contents generation is handled by org-mode, which I cannot control manually.
In detail, the scaffolding has following structures:
<!-- This is the left-side navigation list --> <div class="span3 bs-docs-sidebar"> <ul class="nav nav-list bs-docs-sidenav"> <li><a href="#sec-1"><i class="icon-chevron-right"></i>Section 1</a></li> <li><a href="#sec-2"><i class="icon-chevron-right"></i>Section 2</a></li> <li><a href="#sec-3"><i class="icon-chevron-right"></i>Section 3</a></li> ... </ul> </div> <!-- This is the right-side, contents --> <div class="span9"> <section id="sec-1"> <div class="page-header"> <h1>Section 1</h1> </div> <p>...</p> </section> <section id="sec-2"> <div class="page-header"> <h1>Section 2</h1> </div> <p>...</p> </section> ... </div>
Following is the structure that I implemented:
<!-- This is the left-side navigation list --> <div class="span3 bs-docs-sidebar"> <ul class="nav nav-list bs-docs-sidenav"> <li><a href="#sec-1"><i class="icon-chevron-right"></i>Section 1</a></li> <li><a href="#sec-2"><i class="icon-chevron-right"></i>Section 2</a></li> <li><a href="#sec-3"><i class="icon-chevron-right"></i>Section 3</a></li> ... </ul> </div> <!-- This is the right-side, contents --> <div class="span9"> <!-- from now on, this is org-mode generated contents --> <div id="outline-container-1" class="outline-2"> <h2 id="sec-1">Section 1</h2> ... </div> <div id="outline-container-2" class="outline-2"> <h2 id="sec-1">Section 2</h2> ... </div> ... </div>