Can I define tiny templates or functions in jekyll? - jekyll

Sometimes I could make use of simple shortcuts to skip writing lot of HTML manually. For example, I would like to define a template so that instead of this:
<img class="big" src="{{site.baseurl}}/images/screenshots/image.png" />
I can write this:
{image.png | bigscreenshot}
Another example. This is what I use for keyboard shortcuts:
<kbd>Ctrl</kbd>+<kbd>J</kbd>
This would be way better:
{Ctrl + J | to_shortcut}
That would make the code way shorter and that's valuable for me, because I'm writing documentation. Is this somehow possible? It must work on GitHub pages.

There is no way to define new tags without plugins.
If you want to completely delegate code generation to gh pages, an alternative can be to use includes.
{% include img name="toto" %}
_includes/img
<img class="big" src="{{site.baseurl}}/path/{{ include.name }}.png" />
Or {% include c k="j" %}
_includes/c
<kbd>Ctrl</kbd>+<kbd>{{ include.k | capitalize }}</kbd>

Related

Setting URL variable for all posts

I'm trying to reference an image from a GitHub pages post. I have been referencing images from the layout, and to do so I have used the following code in my _layouts/default.html:
{% assign custom_url = site.url | append: site.baseurl %}
{% assign full_base_url = custom_url | default: site.github.url %}
… href="{{ "/images/logo.png" | prepend: full_base_url }}" …
(I'm not sure how exactly I ame up with the first two lines, but they seem to work well in both the live pages and my local preview so I'm inclined to keep their semantics.)
Trying to duplicate only the last row in the body of a post failed, leading to nothing being prepended. I assume that the variable is not present when the post gets rendered, only when that rendering gets plugged into the layout. Using all three rows in every post with images feels a bit repetitive.
I have read Jekyll Front Matter Defaults which describes how to perform settings for all posts, but that does seem to only allow setting static values, not computed values like I do in my assignments.
I have also read How to define global variables in Liquid? which seems to be addressing the same Liquid problem. But the question is very bare-bones, and tagged for Shopify not Jekyll so some of the answers don't feel applicable to me. The gist I get from the answers there is that maybe I might be able to include some “snippets” somehow somewhere, but I have no clue how to do that, let alone do that in a way that would not require adding lines to every post. The fact that the term “snippet” tends to refer to code snippets does not make searching for guides any easier.
In one of my Jekyll project, I had the same kind of requirement and as you, I did not find a really clear way to do it.
I battled a long time trying to fit this into _config.yaml, but because of the processing order, realised it would never work.
What I finally ended up with is maybe not the cleanest of the solution, and I would really like someone coming with a better solution than this one, but here it is:
In the _includes folder I created a variables.html file containing all those global variables I knew I could use on all pages.
In your case the file _includes/variables.html would be
{% assign custom_url = site.url | append: site.baseurl %}
{% assign full_base_url = custom_url | default: site.github.url %}
Then in all the layouts I would need those variables, I would just include the variables.html files right after the DOCTYPE, here is a simplified example of _layouts/default.html:
<!DOCTYPE html>
{% include variables.html %}
<html>
<head></head>
<body>
{{ content }}
Some image
</body>
</html>
This way your variables custom_url and full_base_url are globally accessible in everywhere you are using the default layout.

Asciidoctor images that are links to the image

The following Asciidoc code creates an image (with suitable styling etc) such that if you click on it, you open the image:
image:./myimage.jpg[my alt text, role="my css styling", link="./myimage.jpg"]
Note the path to the jpg file ./myimage.jpg is duplicated. This is inelegant, invites typos, and if the path is long it can become quite inconvenient to maintain.
My question is this:
is there a neat way to achieve this effect that does not require duplicating the path to the image, so that the path to the image is included precisely once in the code?
Thank you.
This is possible by defining an attribute:
:myimage: ./myimage.png
image::{myimage}[my alt text, role="my css styling", link="{myimage}"]
(also note: :: instead of a single :).
This becomes something like the following when run through AsciiDoctor:
<div class="content">
<a class="image" href="./myimage.png"><img src="./myimage.png" alt="my alt text"></a>
</div>
This is obviously more text, but as you mentioned, maintenance (certainly for long file names or URLs to external images) becomes easier.
Be aware that the space between :myimage: and ./myimage.png is required.
Also, if you redefine the attribute later in the document, it will only impact the next uses of the attribute. Thus,
:myimage: ./myimage.png
image::{myimage}[my alt text, role="my css styling", link="{myimage}"]
:myimage: ./myimage2.png
image::{myimage}[my second alt text, role="my css styling", link="{myimage}"]
will render two different images: the second attribute definition doesn't override the first one.
Though one may prefer different attribute names in such cases.
I couldn't see how to do this in pure Asciidoc and would still welcome input on the matter. In the meantime, I worked around the problem.
I'm in Jekyll so I used a Liquid template. I placed a file myimage in the _includes directory
image:{{ include.p }}[{{ include.t }}, link="{{ include.p }}"]
and invoked it with
{% include myimage p="image-name.jpg" t="alt text" %}
This is actually a slight simplification. In full, myimage is
[.cssstyling]#image:{{ include.p }}[{{ include.t }}, link="{{ include.p }}"]{% if include.c != null %}_{{include.c}}_{% endif %}#
and the invocation is one of the following:
{% include myimage p="image-name.jpg" t="alt text" %}
{% include myimage p="image-name.jpg" t="alt text" c="optional caption" %}

Is there a way to evaluate string with liquid tags

I need to provide page content reference list (it should contain references on sections on page).
The only way which I can see is to use page.content and parse it, but I stumbled on problem with data evaluation. For example I can pull out this string from page.content: {{site.data.sdk.language}} SDK but there is no way to make jekyll process it, it outputs as is.
Also I want to make it possible to create cross-pages links (on specific section on page, but that link generated by another inclusion and doesn't persist in page.content in HTML form).
Is there any way to make it evaluate values from page.content?
P.S. I'm including piece of code which should build page content and return HTML with list (so there is no recursions).
P.P.S. I can't use submodules, because I need to run this pages on github pages.
Thanks.
Shouldn't {{ site.data.sdk.language | strip_html }} do it? I don't know, most probably I didn't understand the problem. Can you elaborate more? Maybe provide a link to a post you're referring to?
Thinking about the similar
{% assign title = site.data.sdk.language %}
which is a stock Liquid tag and does the job well, so instead of
{% section title={{site.data.sdk.language}} %}
write your code as
{% section title = site.data.sdk.language %}
The key here is that once you enter {%, you're in Liquid. Don't expect Liquid to go "Inception" all over itself. {{ is just a shorthand for "print this to output", but an parameter passing is not output, it's just reading a variable.
You should be able to also go wild and do:
{% section title = site.data.sdk.language | capitalize %}
For more re-read the docs: https://github.com/Shopify/liquid/wiki/Liquid-for-Designers

Reusable HTML component libraries in Django

I have a Django website with many pages all with their own unique content and markup (as opposed to a single template that is dynamically filled with data). These pages' templates are derived from a common "tool" template, which in turn derives from a "base" template. Each page also has a JS file which does the scripting for that page. Each pages data is spread around 3 or 4 locations:
the template
the page script (and maybe other page-specific statics)
a central "tool registry" which contains thing like the tool name, description and categories
for some tools, a set of template tags to do some HTML construction specific to that page (e.g. data to present in a table).
However, although each page has a unique layout and content, I still want to be able to share commonly used parametrised HTML "snippets" between pages. For example, one page has an input with a dropdown button that, using Bootstrap, looks like this:
<div class="input-group">
<span class="input-group-btn">
<a class="btn btn-default dropdown-toggle" data-toggle="dropdown" href="#">
Common baudrates
<span class="caret"></span>
</a>
<ul class="dropdown-menu pull-right" id="common-baudrate-list">
<li><a href='#'>9600</a></li>
<li><a href='#'>115200</a></li>
</ul>
</span>
<input class="form-control" type="text" id="baudrate-input" val="115200">
</div>
I'd like to make a re-uasable parametrised function/template/tag which allows me to put in a structure like this given something like "group ID", "list of entries", "left/right button position", "default value".
The ways I see to do it so far are:
As a template, which has a fairly limited syntax, and make it hard to do something like a list of entries like this: ['Entry 1', 'Entry 2']
As a template, passing data into the context via the view (doesn't really appeal because I'd be spreading the pages contents around even more).
As a template tag, building the HTML as a big nasty string
As a template tag using an XML library like lxml, which is a pretty flexible method, but suffers from verbose syntax, over-complexity and I still have get data into the tag from the template!
None of these seem like a neat, re-usable and loosely coupled way to deal with this (for example, if I change up to Bootstrap 4 in future, I may need to re-write these components, and I'd rather have to do it just once). Having a library of components like this will also make future pages easier to construct. Is there a canonical "right" way to do this, or even a commonly used idiom for it?
Edit to show solution implementation
Using inclusion tags as answered below by Wolph, I avoided using a clunky construction like this
{% with [1,2,3] as items %}
{% my_tag items.split %}
{% endwith %}
as follows:
#register.inclusion_tag('components/this_component.html')
def input_with_dropdown(dropdown_title, dropdown_items, default_value, group_id, button_left, *args, **kwargs):
split_char = kwargs['split_char'] if 'split_char' in kwargs else ' '
return {
'dropdown_items': dropdown_items.split(split_char),
'dropdown_title': dropdown_title,
'group_id': group_id,
'default_value' : default_value,
'button_left': button_left
}
And then passing in the variables like this:
{% input_with_dropdown 'Common baudrates' '9600 48000 115200' 115200 'baudrate-input' 0 %}
Have you taken a look at inclusion tags? https://docs.djangoproject.com/en/dev/howto/custom-template-tags/#inclusion-tags
I believe that these would do exactly what you want, you can still specify parameters but you can just pass along a template to have it render really easily.
Example:
from django import template
register = template.Library()
#register.inclusion_tag('list_results.html')
def list_results(results):
return {
'results': results,
'count': len(results),
}
list_results.html:
Found {{ count }} results.<br>
<ul>
{% for result in results %}
<li>{{ result }}</li>
{% endfor %}
</ul>
Use templatetags and html template... Then you can reuse that HTML template...

Embedding Markdown in Jekyll HTML

I'm trying to nest markdown in an HTML file while using Jekyll. Is there a way to achieve something like the following?
# index.html
---
layout: default
---
<p>[Stack Overflow](http://www.stackoverflow.com)</p>
Note: I'm aware that I could do this instead.
# index.html
---
layout: default
---
<p>Stack Overflow</p>
If you are using Kramdown, based on their doc you can do this:
<div markdown="1">
My text with **markdown** syntax
</div>
And this way, the text within the div is rendered as markdown.
Make sure to use the .md or .markdown extension for the file, as .html files aren't sent to Kramdown for processing!
Here's how you can define a markdown block with a Jekyll plugin:
module Jekyll
class MarkdownBlock < Liquid::Block
def initialize(tag_name, text, tokens)
super
end
require "kramdown"
def render(context)
content = super
"#{Kramdown::Document.new(content).to_html}"
end
end
end
Liquid::Template.register_tag('markdown', Jekyll::MarkdownBlock)
(To install this snippet as a plugin, put it in a *.rb file under _plugins directory of your source site root)
Then, use it like this:
{% markdown %}
[Stack Overflow](http://www.stackoverflow.com)
{% endmarkdown %}
EDIT: See #Cristian's answer for a better solution! If you're using Kramdown (which is likely the case since you are using Jekyll), you can use it's feature to render markdown inside div's with a markdown="1" attribute.
As of current Jekyll 3.6.2 life can be a lot simpler with the following two options:
<div>
{{ "## Yes, this renders as markdown" | markdownify }}
</div>
note the markdown-attribute:
<div markdown="1">
## some markdown
inside some html. `snippet` _italic_ **bold**
</div>
#sunny-juneja, check out the Liquid Extension Filter called markdownify:
https://github.com/mojombo/jekyll/wiki/liquid-extensions#markdownify
Use it like this:
<p>{{ '[Stack Overflow](http://www.stackoverflow.com)' | markdownify }}</p>
Put single or double quotes around your string inside of the Output tag.
Works for me on Jekyll 1.0.0beta3
I just recently spent way too many hours trying to do something similar. #J.T.'s 2nd bullet point, referencing markdownify, is what ultimately got me moving in the right direction.
I had in my _layouts directory a few different layouts. One of them, I wanted to add a bit of an "index" before the page content. The index was working perfectly as a partial, if I called it directly from a page or post, but not when trying to add it to a layout.
Here's what I had:
---
layout: default
---
<div class="table-of-contents">
{% include series_index.md %}
{{ content }}
</div>
But it wouldn't work. Instead of rendering HTML on the page, the include was spitting out the markdown, which was then being added to the page as an ugly block of markdown, instead of rendering the markdown as HTML.
So, I tried tacking | markdownify to the include statement, like so:
{% include jekyll-bug-fix-index.md | markdownify %}
and that didn't work.
The solution, using a variable, a capture "block", and markdownify
So, I captured the markdown, saved to a variable, and then rendered the variable with the liquid markdownify tag:
{% capture index %}
{% include series_index.md %}
{% endcapture %}
{{ index | markdownify }}
And this works! The Markdown is rendered as HTML on my pages, and all is right in the world.
I have no doubt this is unconventional, and I hope to someday learn a better solution, but it's 100% good enough for me, and I wanted to share so others might benefit from this.
To convert the markdown-formatted string to HTML in a Jekyll page, there are THREE WAYS can be selected as below:
1. Kramdown:
If you are using Kramdown, based on their doc you can do this:
<div markdown="1">
## Some Markdown Title
Let's have a look. `snippet` _italic_ **bold**
</div>
The markdown attribute:
If an HTML tag has an attribute markdown="0", then the tag is parsed as raw HTML block.
If an HTML tag has an attribute markdown="1", then the default mechanism for parsing syntax in this tag is used.
If an HTML tag has an attribute markdown="block", then the content of the tag is parsed as block level elements.
If an HTML tag has an attribute markdown="span", then the content of the tag is parsed as span level elements.
Requirments:
The markdown content need to be within the DIV tag.
Make sure to use the .md or .markdown extension for the file
as .html files aren't sent to Kramdown for processing)
2. Liquid Extension Filter
There is a liquid extension filter called markdownify, it also can help you convert a Markdown-formatted string into HTML.
<div>
{{ "Renders as markdown. `snippet` _italic_ **bold**" | markdownify }}
</div>
3. Jekyll plugin:
jekyll-spaceship - 🚀 A Jekyll plugin to provide powerful supports for table, mathjax, mermaid, plantuml, emoji, youtube, vimeo, dailymotion, etc.
https://github.com/jeffreytse/jekyll-spaceship
With this plugin, it's easy to write markdown inside HTML:
<script type="text/markdown">
# Hybrid HTML with Markdown is a not bad choice ^\_^
##2. Table Usage
| : Fruits \|\| Food : |||
| :--------- | :-------- | :-------- |
| Apple | : Apple :| Apple \
| Banana | Banana | Banana \
| Orange | Orange | Orange |
| : Rowspan is 4 : || How's it? |
|^^ A. Peach || 1. Fine :|
|^^ B. Orange ||^^ 2. Bad |
|^^ C. Banana || It's OK! |
## PlantUML Usage
#startuml
Bob -> Alice : hello
#enduml
## Video Usage
![](https://www.youtube.com/watch?v=Ptk_1Dc2iPY)
</script>
Take a look at Paul Irish's Gist for a JS code that can interpret sections of your page from Markdown to HTML.