Published 2020-10-03.
Last modified 2023-01-20.
Time to read: 14 minutes.
This is a Jekyll-powered web site. Jekyll is a free open-source preprocessor that generates static web sites. You can extend Jekyll by using the Liquid language to write includes. Includes are just macros for Jekyll.
I prefer to write and use Jekyll plugins instead of Jekyll includes. Non-trivial Jekyll includes require logic to be expressed in the Liquid language. Liquid is an interesting language, but it is quite verbose, syntax can be awkward, some expressions are impossible to formulate, and there are no debugging tools.
In contrast, plugins are written in Ruby. Plugin usage syntax is more flexible and require less typing for users.
The argument against writing plugins is that the Ruby language is subtle and powerful, and could be overwhelming for novice programmers. However, just as the Apache Spark framework allows novice Scala programmers to write in Just Enough Scala for Spark, and the Ruby on Rails framework allows novice Ruby programmers to write Just Enough Ruby for Rails, writing plugins for the Jekyll framework generally does not require total mastery of Ruby.
Below are some of my Jekyll plugins.
The source code for a plugin can be copied to the clipboard whenever you click on this icon at the top right corner of the code:
.
All plugins are RubyGems that I maintain on GitHub.
archive_create
Originally called make_archive
, archive_create
creates tar
and zip
archives according to the make_archive
entry in _config.yml
.
Archives are placed in the top-level of the Jekyll project, and are copied to _site
by Jekyll’s normal build process.
Entries are created in .gitignore
for each of the generated archives.
In production
mode, the archives are built each time Jekyll generates the web site.
In development
mode, the archives are only built if they do not already exist,
or if delete: true
is set for that archive in _config.yml
.
File Specifications
This plugin supports 4 types of file specifications:
- Absolute filenames (start with
/
). - Filenames relative to the top-level directory of the Jekyll web site (Do not preface with
.
or/
). - Filenames relative to the user home directory (preface with
~
). - Executable programs on the
PATH
(preface with!
).
_config.yml Syntax
Any number of archives can be specified.
Each archive has 3 properties: archive_name
, delete
(defaults to true
) and files
.
Take care that the dashes have exactly 2 spaces before them, and that the 2 lines following each dash have exactly 4 spaces in front.
make_archive: - archive_name: cloud9.zip delete: true # This is the default, and need not be specified. files: [ index.html, 404.html, ~/.ssh/config, /etc/passwd, '!update' ] - archive_name: cloud9.tar delete: false # Do not overwrite the archive if it already exists files: [ index.html, 404.html, ~/.ssh/config, /etc/passwd, '!update' ]
GitHub Project and RubyGem
The GitHub project is github.com/mslinn/jekyll_archive_create
and the gem is provided at rubygems.org/gems/jekyll_archive_create
.
archive_display
Lists the names and contents of each file in a tar
file.
Syntax
{% archive_display /relative/or/absolute/path/to/filename.tar %}
Output
For each text file within the tar file, the following HTML is emitted:
<div class='codeLabel'>{entry.full_name}{entry.mime_type}</div> <pre data-lt-active='false'><code>{tar_entry.file_contents}</pre>
.codeLabel { color: white; background-color: #666; margin-bottom: 0; padding-bottom: 0; padding-left: 1em; } .codeLabel+pre { margin-top: 0; } .codeLabel, pre { font-family: Monaco,"Bitstream Vera Sans Mono","Lucida Console",Terminal,monospace; font-stretch: semi-condensed; } pre { background: #78B4DB21; border: solid 1px #cecece; overflow: auto; padding: 4px; text-indent: initial; text-shadow: none; } pre { font-size: 11pt; margin-bottom: 30px; margin-left: 0; margin-right: 0; }
Using the above CSS, text files within a tar file are displayed like this:
#!/bin/sh echo "Hello, world"
Binary files are displayed like this:
Binary file
Installation
-
Install
libmagic
.
Ubuntu & WSLShell$ sudo apt install libmagic-dev
MacShell$ brew install libmagic
-
Add the following to
Gemfile
:Gemfilegroup :jekyll_plugins do
gem 'jekyll_archive_display'
end -
Add the following to
_config.yml
:_config.ymlplugins:
- archive_display -
From your Jekyll site's top-level directory, type:
Shell
$ bundle install
- Restart Jekyll.
GitHub Project and RubyGem
The GitHub project is github.com/mslinn/jekyll_archive_display
and the gem is provided at rubygems.org/gems/jekyll_archive_display
.
basename, dirname and basename_without_extension
These filters all return portions of a string. They are all defined in the same plugin.
basename
– Filters a string containing a path, returning the filename and extension.dirname
– Filters a string containing a path, returning the portion before the filename and extension.basename_without_extension
– Filters a string containing a path, returning the filename without the extension.
Syntax
{{ "blah/blah/filename.ext" | dirname }} => blah/blah {{ "blah/blah/filename.ext" | basename }} => filename.ext {{ "blah/blah/filename.ext" | basename_without_extension }} => filename
GitHub Project and RubyGem
The GitHub project is github.com/mslinn/jekyll_basename_dirname
and the gem is provided at rubygems.org/gems/jekyll_basename_dirname
.
begins_with, does_not_begin_with, ends_with, does_not_end_with, and append_suffix_if_does_not_start_with
These filters all return portions of a string. They are all defined in the same plugin.
begins_with
– returnstrue
if a string starts with a given substring.does_not_begin_with
– returnsfalse
if a string starts with a given substring.ends_with
– returnstrue
if a string end with a given substring.does_not_end_with
– returnsfalse
if a string end with a given substring.append_suffix_if_does_not_start_with
– appends a suffix to the string if the string does not start with a substring
Important: the name of each of these filters must be followed by a colon (:). If you fail to do that an error will be generated and the Jekyll site building process will halt.
The error message looks something like this:
Liquid Warning: Liquid syntax error (line 285):
Expected end_of_string but found string in "{{ lines | begins_with 'blah' | xml_escape }}" in
/some_directory/some_files.html Liquid Exception: Liquid error (line 285):
wrong number of arguments (given 1, expected 2) in /some_directory/some_file.html
Error: Liquid error (line 285): wrong number of arguments (given 1, expected 2)
Examples
begins_with
{% assign url = "https:/asdf.com" %} {% assign isAbsolute = url | begins_with: 'http' %}
{% assign url = "https:/asdf.com" %} {% if url | begins_with: 'http' %} <p>Absolute</p> {% else %} <p>Relative</p> {% endif %}
does_not_begin_with
{% assign url = "https:/asdf.com" %} {% assign isRelative = url | does_not_begin_with: 'http' %}
{% assign url = "https:/asdf.com" %} {% if url | does_not_begin_with: 'http' %} <p>Relative</p> {% else %} <p>Absolute</p> {% endif %}
ends_with
{% assign url = "https:/asdf.com" %} {% assign isDotCom = url | ends_with: '.com' %}
{% assign url = "https:/asdf.com" %} {% if url | ends_with: '.com' %} <p>.com found</p> {% else %} <p>Not a .com</p> {% endif %}
does_not_end_with
{% assign url = "https:/asdf.com" %} {% assign isNotDotCom = url | does_not_end_with: '.com' %}
{% assign url = "https:/asdf.com" %} {% if url | does_not_end_with: '.com' %} <p>Not a .com</p> {% else %} <p>.com found</p> {% endif %}
append_suffix_if_does_not_start_with
This filter was created to make asset reloading work better.
Given a portion of _layouts/default.html that looks like this:
{% assign csses = page.css | default: layout.css %} {% assign nowMillis = site.time | date: '%s' %} {% assign suffix = '?v=' | append: nowMillis %} {% for css in csses %} <link rel="stylesheet" href="{{ css | append_suffix_if_does_not_start_with: 'http', suffix }}" type="text/css"> {% endfor %}
And given index.html with front matter that looks like this:
--- css: [ https://cdnjs.cloudflare.com/ajax/libs/jquery-modal/0.9.1/jquery.modal.min.css, /order/order.css ] ---
The following is generated.
Note that the suffix s?v=1612879301
in only applied to the relative URL for order.css
.
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/jquery-modal/0.9.1/jquery.modal.min.css" type="text/css"> <link rel="stylesheet" href="/order/order.css?v=1612879301" type="text/css">
GitHub Project and RubyGem
The GitHub project is github.com/mslinn/jekyll_begin_end
and the gem is provided at rubygems.org/gems/jekyll_begin_end
.
download_link
This plugin generates a link to the given URI, which must be a file on the server. The file name can be absolute or relative to the top-level directory of the web site.
Syntax
{% download_link uri %}
Usage Example
{% download_link cloud9.tar %}
Generates:
<a href="/cloud9.tar"><code>cloud9.tar</code></a> (4.5 KB)
Which renders as:
cloud9.tar
(4.5 KB)
GitHub Project and RubyGem
The GitHub project is github.com/mslinn/jekyll_download_link
and the gem is provided at rubygems.org/gems/jekyll_download_link
.
flexible_include
Jekyll's built-in include
tag does not support including files outside of the _includes
folder.
Originally called include_absolute
, this plugin name is now called flexible_include
because it no longer just includes absolute file names.
This plugin now supports 4 types of includes:
-
Filenames relative to the top-level directory of the Jekyll web site.
It is unnecessary to preface these paths with
./
). -
Absolute filenames (first character is
/
). This feature can be modified or denied where security is a concern by specifying an array of Ruby glob expressions in theFLEXIBLE_INCLUDE_PATHS
environment variable. -
Filenames relative to the user home directory (first character is
~
). This feature can also be modified or denied where security is a concern by specifying an array of Ruby glob expressions in theFLEXIBLE_INCLUDE_PATHS
environment variable. -
Executable filenames on the
PATH
(first character is!
). This feature can be disabled by defining theDISABLE_FLEXIBLE_INCLUDE
environment variable before launching Jekyll.
In addition, filenames that require environment expansion because they contain a $
character are
expanded according to the environment variables defined when the jekyll build
process was launched.
Syntax
The following are equivalent:
- {% flexible_include path [ OPTIONS ] %}
- {% flexible_include 'path' [ OPTIONS ] %}
- {% flexible_include "path" [ OPTIONS ] %}
- {% flexible_include file='path' [ OPTIONS ] %}
- {% flexible_include file="path" [ OPTIONS ] %}
By default, the included file will escape characters <
, {
and }
unless do_not_escape
is specified.
Note that the [square brackets] merely indicate optional parameters and are not intended to be written literally.
The file must be specified as a relative or absolute path on the server, or a command to execute.
A URL cannot be provided (you cannot write a file name that starts with http:
or https:
).
This capability is under consideration for a possible future release.
Options
do_not_escape
includes the content without HTML escaping it.pre
causes the included file to be wrapped inside a<pre></pre>
tag, no label is generated.
The following options imply pre
:
copyButton
draws an icon at the top right of the<pre></pre>
area, which causes the included contents to be copied to the clipboard.download
uses the name of the file as a label, and displays it above the<pre></pre>
tag. Clicking the label causes the file to be downloaded.label
specifies that an automatically generated label be placed above the contents. There is no need to specify this option ifdownload
orcopyButton
options are provided.label="blah blah"
specifies a label for the contents; this value overrides the default label. The value can be enclosed in single or double quotes. If you want to display a text message other than the file name, use this option.number
numbers the lines.
Usage Examples
This example shows a typical set of options. A label is automatically generated from the file name or process output that is included:
{% flexible_include ~/.mem_settings.yaml download copyButton %}
Here is what the above looks like when rendered by a web browser:
append_file_name: sample.html output_format: qt
... and without the download
option, but still including the copyButton
option:
append_file_name: sample.html output_format: qt
... now with just the pre
option:
append_file_name: sample.html output_format: qt
... and finally this is the result of using flexible_include
without any options.
The generated text is outlined in a <pre></pre>
tag so it is noticable.
The file has a trailing newline, which is apparent below,
however flexible_include
trims any leading and trailing whitespace when the pre
option is specified or implied.
append_file_name: sample.html output_format: qt
Dark Mode
Normally my website uses light colors, however some content displays better on a dark background.
The dark
option causes the generated <pre> tag to have a dark
class applied.
You can define the CSS for the dark
and darkLabel
classes.
The CSS that defines those classes for this web site is here.
append_file_name: sample.html output_format: qt
Home Directory
The included file path can use a tilde (~
) to denote the user $HOME
directory.
append_file_name: sample.html output_format: qt
Environment variables can be used; they will be expanded according to the environment variables that were current in the process when the Jekyll generator launched.
[alias] lol=log --graph --decorate --pretty=oneline --abbrev-commit lola=log --graph --decorate --pretty=oneline --abbrev-commit --all ls=ls-files st = status ci = commit br = branch co = checkout df = diff dc = diff --cached dif = diff --word-diff=color --ignore-space-at-eol lg = log -p ign = ls-files -o -i --exclude-standard [branch "master"] remote = origin merge = refs/heads/master [core] filemode = false autocrlf = input safecrlf = false excludesfile = C:\\Users\\Mike Slinn\\Documents\\gitignore_global.txt [color] status = auto branch = auto ui = auto [gui] trustmtime = true [push] default = matching autoSetupRemote = true autoSetupRemote = true [user] name = Mike Slinn email = mslinn@mslinn.com [rebase] autostash = true [diff "exif"] textconv = exiftool [diff] compactionHeuristic = true [init] defaultBranch = master
Environment Variable Expansion
This example includes the output of running the bash command which jekyll
,
according to the Ruby environment that was current when the Jekyll generator was launched:
/home/mslinn/.rbenv/versions/3.1.0/bin/jekyll
Does This Make Your Brain Hurt?
Ready to have your mind twisted? This invocation:
{% flexible_include '~/.mem_settings.yaml' download copyButton label='{% flexible_include download copyButton ~/.mem_settings.yaml %}' %}
Renders like this:
append_file_name: sample.html output_format: qt
Hint: {
and }
are HTML entities for {
and }
, respectively.
Highlighting Text
A regular expression can be passed to the highlight
option.
This causes text that matches the regex pattern to be wrapped within
a <span class="bg_color"></span>
tag.
The following highlights filenames in a directory listing that contain django-admin
that might contain dots, underscores, dashes and forward slashes:
{% flexible_include highlight="[\w./\-_]*django-admin[\w.\-_]*" label="ls ~/venv/aw/bin/*" file="!ls ~/venv/aw/bin/*" %}
Renders as:
/home/mslinn/venv/aw/bin/activate /home/mslinn/venv/aw/bin/activate.csh /home/mslinn/venv/aw/bin/activate.fish /home/mslinn/venv/aw/bin/Activate.ps1 /home/mslinn/venv/aw/bin/pip /home/mslinn/venv/aw/bin/pip3 /home/mslinn/venv/aw/bin/pip3.10 /home/mslinn/venv/aw/bin/python /home/mslinn/venv/aw/bin/python3 /home/mslinn/venv/aw/bin/python3.10
Numbering Lines
{% flexible_include label="ls ~/venv/aw/*" file="!ls ~/venv/aw/*" number %}
Renders as (notice that the numbers are unselectable):
1: /home/mslinn/venv/aw/pyvenv.cfg 2: 3: /home/mslinn/venv/aw/bin: 4: activate 5: activate.csh 6: activate.fish 7: Activate.ps1 8: pip 9: pip3 10: pip3.10 11: python 12: python3 13: python3.10 14: 15: /home/mslinn/venv/aw/include: 16: 17: /home/mslinn/venv/aw/lib: 18: python3.10 19: 20: /home/mslinn/venv/aw/lib64: 21: python3.10
CSS
Below are the CSS declarations that I defined for the flexible_include
tag that produced the above output.
This CSS is the same as used by pre
.
pre.dark { color: #eee; background-color: #222; } pre.dark .unselectable, pre.dark .unselectable > code { color: rgb(155, 150, 150); } .bg_yellow { background-color: yellow; padding: 2px; } .codeLabel { color: white; background-color: #666; margin-bottom: 0; padding-bottom: 2px; padding-left: 10px; padding-right: 10px; padding-top: 2px; font-family: $mono-font; font-stretch: semi-condensed; } .darkLabel { color: ivory; } li div.codeLabel { padding-left: 2em; } .codeLabel.unselectable, div.codeLabel.unselectable > code { color: yellow; } .codeLabel + pre { margin-top: 0; } .codeLabel a { color: #c0e6fb; } .codeLabel a:hover { color: #FFE59F; } .copyBtn { -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; -webkit-appearance: none; background-color: #eee; background-image: linear-gradient(#fcfcfc, #eee); border: 1px solid #d5d5d5; border-radius: 3px; color: #333; cursor: pointer; float: right; font-size: 13px; font-weight: 700; line-height: 20px; padding: 2px 2px 0 4px;; position: -webkit-sticky; position: sticky; right: 4px; top: 0; user-select: none; z-index: 1; } .copyContainer { position: relative; } ol li .codeLabel { padding-left: 1.75em; } .maxOneScreenHigh { max-height: 500px; } .numbered_line, .unselectable.numbered_line, .numbered_line.unselectable { color: #5fb25f; } .unselectable { color: #7922f9; -moz-user-select: none; -khtml-user-select: none; user-select: none; } .wrap_pre { white-space: pre-wrap; }
Restricting Directory Access
By default, flexible_include
can read from all directories according to the
permissions of the user account that launched the jekyll
process.
For security-conscience environments, the accessible paths can be restricted.
Defining an environment variable called FLEXIBLE_INCLUDE_PATHS
prior to launching
Jekyll will restrict the paths that flexible_include
will be able to read from.
This environment variable consists of a colon-delimited set of regular expressions.
For example, the following restricts access to only the files within:
- The
~/my_dir
directory tree of the account of the user that launched Jekyll. - The directory tree rooted at
/var/files
. - The directory tree rooted at the expanded value of the
$work
environment variable.
$ export FLEXIBLE_INCLUDE_PATHS='~/my_dir/.*:/var/files/.*:$work/.*'
If a reference to an unauthorized file is intercepted, a big red message will appear on the generated web page that says something like
Access to #{path} denied by FLEXIBLE_INCLUDE_PATHS value.
... and an error message will be logged on the console that looks something like:
ERROR FlexibleInclude: _posts/2020/2020-10-03-jekyll-plugins.html - Access to #{path} denied by FLEXIBLE_INCLUDE_PATHS value.
Restricting Arbitrary Processes
By default, flexible_include
can execute any command.
You can disable that by setting the environment variable DISABLE_FLEXIBLE_INCLUDE
to any non-empty value.
$ export DISABLE_FLEXIBLE_INCLUDE=true
If a potential command execution is intercepted, a big red message will appear on the generated web page that says:
Arbitrary command execution denied by DISABLE_FLEXIBLE_INCLUDE value.
... and an error message will be logged on the console that looks something like:
ERROR FlexibleInclude: #{path} - Arbitrary command execution denied by DISABLE_FLEXIBLE_INCLUDE value.
GitHub Project and RubyGem
More information is available about this plugin from its GitHub project at
github.com/mslinn/jekyll_flexible_include_plugin
.
This plugin is provided as a Ruby gem at rubygems.org/gems/jekyll_flexible_include
.
from, to and until
These filters all return portions of a multiline string. They are all defined in the same plugin. A regular expression is used to specify the match; the simplest regular expression is a string.
from
– returns the portion beginning with the line that satisfies a regular expression to the end of the multiline string.to
– returns the portion from the first line to the line that satisfies a regular expression, including the matched line.until
– returns the portion from the first line to the line that satisfies a regular expression, excluding the matched line.
from
All of these examples perform identically.{{ sourceOfLines | from: 'regex' }} {{ sourceOfLines | from: "regex" }} {{ sourceOfLines | from: regex }}
to
All of these examples perform identically.{{ sourceOfLines | to: 'regex' }} {{ sourceOfLines | to: "regex" }} {{ sourceOfLines | to: regex }}
until
All of these examples perform identically.{{ sourceOfLines | until: 'regex' }} {{ sourceOfLines | until: "regex" }} {{ sourceOfLines | until: regex }}
GitHub Project and RubyGem
More information is available about this plugin from its GitHub project at
github.com/mslinn/jekyll_from_to_until
.
This plugin is provided as a Ruby gem at rubygems.org/gems/jekyll_from_to_until
.
href
Generates an a href
tag with target="_blank"
and rel=nofollow
.
Also provides a convenient way to generate formatted and clickable URIs.
Syntax
{% href [match | [follow] [notarget]] [url] text to display %}
The url should not be enclosed in quotes.
If no url is provided then the text to display is assumed to be a URI, and is formatted into a clickable link.
Usage Examples
Defaults
{% href https://www.mslinn.com The Awesome %}
This generates:
<a href='https://www.mslinn.com' target='_blank' rel='nofollow'>The Awesome</a>
Which renders as: The Awesome
follow
{% href follow https://www.mslinn.com The Awesome %}
This generates:
<a href='https://www.mslinn.com' target='_blank'>The Awesome</a>
notarget
{% href notarget https://www.mslinn.com The Awesome %}
This generates:
<a href='https://www.mslinn.com' rel='nofollow'>The Awesome</a>
follow notarget
{% href follow notarget https://www.mslinn.com The Awesome %}
This generates:
<a href='https://www.mslinn.com'>The Awesome</a>
match
Looks for a post with a matching URL.{% href match setting-up-django-oscar.html tutorial site %}
This might generate:
tutorial site
URI
{% href mslinn.com %}
This generates:
<a href='https://mslinn.com' target='_blank' rel='nofollow'><code>mslinn.com</code></a>
Which renders as: mslinn.com
GitHub Project and RubyGem
More information is available about this plugin from its GitHub project at
github.com/mslinn/jekyll_href
.
This plugin is provided as a Ruby gem at rubygems.org/gems/jekyll_href
.
img
This very useful plugin was one of the most difficult to write.
GitHub Project and RubyGem
More information is available about this plugin from its GitHub project at
github.com/mslinn/jekyll_img
.
This plugin is provided as a Ruby gem at rubygems.org/gems/jekyll_img
.
Usage Examples
All of these examples have a src
parameter, like this:
{% img src="jekyll_240x103.webp" %}
Additional parameters for each example are shown.
Sizes
Captions
URLs
Classes
Styles
Wrapper Styles
Flow-like
nth
nth
is a Liquid filter that returns item n of array, origin 0.
Usage Example
This example obtains the second item of the array.
Note that because Liquid does not provide a syntax for defining arrays,
the following transforms a string of numbers to an array of numbers using split
.
{{ "1,2,3,4,5" | split: ',' | nth: 2 }} # returns 3
GitHub Project and RubyGem
More information is available about this plugin from its GitHub project at
github.com/mslinn/jekyll_nth
.
This plugin is provided as a Ruby gem at rubygems.org/gems/jekyll_nth
.
outline
This Jekyll tag plugin creates a clickable table of contents. You can see it in action in several areas of this web site:
Django / Django-Oscar index
{% outline django %} 0: Django / Oscar Evaluation 400: Notes 800: Digging Deeper 1900: Debugging 2700: Production {% endoutline %}
Jekyll index
{% outline jekyll %} 0000: Jekyll User Setup (including Ruby) 2000: Jekyll Operation, including My Scripts and Plugins 4000: Visual Studio For Ruby 6000: Ruby Development and Gems 8000: Jekyll Internals 10000: Creating and Debugging Custom Jekyll Plugins {% endoutline %}
A/V Studio index
{% outline av_studio %} 0: General 200: Video 150: Audio 300: RME TotalMix 400: OBS Studio 500: Pro Tools 600: Guitar Pro & MuseScore 700: MIDI Hardware & Software 800: Davinci Resolve {% endoutline %}
GitHub Project and RubyGem
More information is available about this plugin from its GitHub project at
github.com/mslinn/jekyll_outline
.
This plugin is provided as a Ruby gem at rubygems.org/gems/jekyll_outline
.
jekyll_plugin_logger
Formerly known as logger_factory
,
jekyll_plugin_logger
is Jekyll plugin packaged as a Ruby gem that provides colored console logs.
All of my plugins use jekyll_plugin_logger
, for notifying the user that the plugin has loaded, and for generating log messages.
I wrote it up separately.
GitHub Project and RubyGem
The GitHub project is github.com/mslinn/jekyll_plugin_logger
and the gem is provided at rubygems.org/gems/jekyll_plugin_logger
.
pre
This Jekyll plugin provides 2 new Liquid tags that work together:
-
A
pre
block tag that can optionally display a labeled heading and a copy button.{% pre [class="class1 class2"] [clear] [copyButton] [dark] [shell] [style="font-weight: bold;"] [highlight="regex"] [headline words] %}
The
Contents of pre tag
{% endpre %}clear
keyword ensures no floated elements overlap the renderedpre
tag. -
A
noselect
tag that can render HTML content passed to it unselectable.{% pre [copyButton] [dark] %}
{% noselect [text string, defaults to $] %}Contents of pre tag
{% endpre %}
Clipboard Javascript
The clipboard functionality requires Javascript. You might want to load it in a Jekyll layout.
The clipboard button is fuctionally equivalent to the following HTML:
<button class="copyBtn" data-clipboard-target="#id098814fabaf3" title="Copy to clipboard"> <img src="/assets/images/clippy.svg" alt="Copy to clipboard" style="width: 13px"> </button>
The value of the data-clipboard-target
attribute is the id
of the container holding the text to be copied.
clipboard.js must be loaded by the web page. For example:
<script src="https://cdn.jsdelivr.net/npm/clipboard@2.0.6/dist/clipboard.min.js"></script>
After the JavaScript loads, a new `ClipboardJS` instance must be created.
The constructor needs to know the CSS selector for the buttons that the user will click on when they want to copy text to the clipboard.
In this example, all of the buttons on the web page have class .copyBtn
.
new ClipboardJS('.copyBtn');
Usage
Dark Mode Example
Normally my website uses light colors, however some content displays better on a dark background. You can define the CSS any way you like.
{% pre copyButton dark Dark Mode Example %} {% noselect >>> %}Contents of pre tag {% noselect How now brown cow %} {% endpre %}
Renders as:
>>> Contents of pre tag How now brown cow
Example 1
This example does not generate a copy button and does not demonstrate noselect
.
{% pre %}Contents of pre tag{% endpre %}
Generates:
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='id377433c30186'>Contents of pre tag</pre>
Which renders as:
Contents of pre tag
Example 2
This example generates a copy button and does not demonstrate noselect
.
{% pre copyButton %}
Contents of pre tag
{% endpre %}
Generates:
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='id6a831a3e8992'><button class='copyBtn' data-clipboard-target='#id6a831a3e8992' title='Copy to clipboard'><img src='images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button>Contents of pre tag</pre>
Which renders as (note the clipboard icon at the far right):
Contents of pre tag
Example 3
This example generates a copy button and demonstrates the default usage of noselect
,
which renders an unselectable dollar sign followed by a space.
{% pre copyButton %}
{% noselect %}Contents of pre tag
{% endpre %}
Generates:
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='id1e4a8fe53480'><button class='copyBtn' data-clipboard-target='#id1e4a8fe53480' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button><span class='unselectable'>$ </span>Contents of pre tag</pre>
Which renders as:
$ Contents of pre tag
Example 4
This example generates a copy button and demonstrates the noselect
being used twice:
the first time to render an unselectable custom prompt,
and the second time to render unselectable output.
{% pre copyButton %} {% noselect >>> %}Contents of pre tag {% noselect How now brown cow %}{% endpre %}
Generates:
<pre data-lt-active='false' class='maxOneScreenHigh copyContainer' id='idb58a6cf1761c'><button class='copyBtn' data-clipboard-target='#idb58a6cf1761c' title='Copy to clipboard'><img src='/assets/images/clippy.svg' alt='Copy to clipboard' style='width: 13px'></button><span class='unselectable'>>> </span>contents of pre tag <span class='unselectable'>How now brown cow</span></pre>
Which renders as:
$ Contents of pre tag >>> How now brown cow
Example 5
A regular expression can be passed to the highlight
option.
This causes text that matches the regex pattern to be wrapped within
a <span class="bg_color"></span>
tag.
This example demonstrates highlighting text that matches a regular expression.
Regular expressions match against lines, which are delimited via newlines (\n
).
{% pre copyButton highlight="Line 2" %} Line 1 Line 2 Line 3 Line 4 Line 5 Line 6 Line 7 {% endpre %}
Which renders as:
Line 1
Line 2
Line 3
Line 4
Line 5
Line 6
Line 7
Example 6
Regular expressions match against lines, which are delimited view newlines (\n
).
Thus to match an entire line that contains a phrase, specify the regex as .*phrase.*
.
The following matches 3 possible phrases (2
, 4
or 6
), then selects the entire line if matched.
{% pre copyButton highlight=".*(2|4|6).*" %} Line 1 Line 2 Line 3 Line 4 Line 5 Line 6 Line 7 {% endpre %}
Which renders as:
Line 1 Line 2 Line 3 Line 4 Line 5 Line 6 Line 7
CSS
Below are the CSS declarations that I defined for the pre
tag that produced the above output.
This CSS is the same as used by flexible_include
.
pre.dark { color: #eee; background-color: #222; } pre.dark .unselectable, pre.dark .unselectable > code { color: rgb(155, 150, 150); } .bg_yellow { background-color: yellow; padding: 2px; } .codeLabel { color: white; background-color: #666; margin-bottom: 0; padding-bottom: 2px; padding-left: 10px; padding-right: 10px; padding-top: 2px; font-family: $mono-font; font-stretch: semi-condensed; } .darkLabel { color: ivory; } li div.codeLabel { padding-left: 2em; } .codeLabel.unselectable, div.codeLabel.unselectable > code { color: yellow; } .codeLabel + pre { margin-top: 0; } .codeLabel a { color: #c0e6fb; } .codeLabel a:hover { color: #FFE59F; } .copyBtn { -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; -webkit-appearance: none; background-color: #eee; background-image: linear-gradient(#fcfcfc, #eee); border: 1px solid #d5d5d5; border-radius: 3px; color: #333; cursor: pointer; float: right; font-size: 13px; font-weight: 700; line-height: 20px; padding: 2px 2px 0 4px;; position: -webkit-sticky; position: sticky; right: 4px; top: 0; user-select: none; z-index: 1; } .copyContainer { position: relative; } ol li .codeLabel { padding-left: 1.75em; } .maxOneScreenHigh { max-height: 500px; } .numbered_line, .unselectable.numbered_line, .numbered_line.unselectable { color: #5fb25f; } .unselectable { color: #7922f9; -moz-user-select: none; -khtml-user-select: none; user-select: none; } .wrap_pre { white-space: pre-wrap; }
Comprehensive Example
The code that generates the above CSS is a good example of how the plugins work together with
the from
and to
tags from my
from_to_until
plugin:
{% capture css %}{% flexible_include '_sass/mystyle.scss' %}{% endcapture %} {% pre copyButton %}{{ css | from: '.copyBtn' | to: '^$' | strip }} {{ css | from: '.copyContainer' | to: '^$' | strip }} {{ css | from: '.maxOneScreenHigh' | to: '^$' | strip }} {{ css | from: '.unselectable' | to: '^$' | strip }} {% endpre %}
GitHub Project and RubyGem
More information is available about this plugin from its GitHub project at
github.com/mslinn/jekyll_pre
.
This plugin is provided as a Ruby gem at rubygems.org/gems/jekyll_pre
.
quote
GitHub Project and RubyGem
More information is available about this plugin from its GitHub project at
github.com/mslinn/jekyll_quote
.
This plugin is provided as a Ruby gem at rubygems.org/gems/jekyll_quote
.
Usage Examples
{% quote cite="Blaise Pascal, in Lettres provinciales" url="https://en.wikipedia.org/wiki/Lettres_provinciales" %} I have only made this letter longer because I have not had the time to make it shorter. {% endquote %}
Renders as:
break
, cite
and url
were provided.
by
, break
, cite
and url
were provided.
noprep
, break
, cite
and url
were provided.
No URLs were provided for these quotes
cite
was specified.
– From Blaise Pascal
By
and cite
were specified.
– By Blaise Pascal
Break
, By
and cite
were specified.
Break
, noprep
and cite
were specified.
cite
or url
was provided for this quote.
No cite was provided for the following quote
cite
was provided for this quote.
random_hex_string
This Liquid filter generates a random hexadecimal string of any length. Each byte displays as two characters. You can specify the number of bytes in the hex string; if you do not, 6 random bytes (12 characters) will be generated.
Usage Example
This example generates a random hex string 6 bytes long and stores the result in a Liquid variable called id
.
Both of the following do the same thing:
{% assign id = random_hex_string %} {% assign id = random_hex_string 6 %}
The generated 6 bytes (12 characters) might be: f993d15f08e5
.
GitHub Project and RubyGem
More information is available about this plugin from its GitHub project at
github.com/mslinn/jekyll_random_hex
.
This plugin is provided as a Ruby gem at rubygems.org/gems/jekyll_random_hex
.
run
jekyll_run
is a Jekyll tag plugin that executes a program and returns the output from STDOUT
.
Because the output includes the command that was executed,
and contains unselectable <span>
tags,
this plugin is intended to be embedded within a {% pre %}{% endpre %}
tags.
Usage Example
{% run echo "asdf" %}
The generated HTML is:
<span class='unselectable'>$ </span>#{@command}\n<span class='unselectable'>#{output}</span>
Which renders as the following, when enclosed within {% pre %}{% endpre %}
:
$ echo "asdf" asdf
GitHub Project and RubyGem
More information is available about this plugin from its GitHub project at
github.com/mslinn/jekyll_run
.
This plugin is provided as a Ruby gem at rubygems.org/gems/jekyll_run
.
site_inspector
Dumps lots of information from site
when enabled by the
site_inspector
setting in _config.yml
.
_config.yml Syntax
site_inspector: true # Run in development mode
site_inspector: force # Run in development and production modes
site_inspector: false # The default is to not run
Sample Output
site is of type Jekyll::Site site.time = 2020-10-05 05:18:27 -0400 site.config['env']['JEKYLL_ENV'] = development site.collections.posts site.collections.expertArticles site.config.source = '/mnt/_/www/www.mslinn.com' site.config.destination = '/mnt/_/www/www.mslinn.com/_site' site.config.collections_dir = '' site.config.plugins_dir = '_plugins' site.config.layouts_dir = '_layouts' site.config.data_dir = '_data' site.config.includes_dir = '_includes' site.config.collections = '{"posts"=>{"output"=>true, "permalink"=>"/blog/:year/:month/:day/:title:output_ext"}, "expertArticles"=>{"output"=>true, "relative_directory"=>"_expertArticles", "sort_by"=>"order"}}' site.config.safe = 'false' site.config.include = '[".htaccess"]' site.config.exclude = '["_bin", ".ai", ".git", ".github", ".gitignore", "Gemfile", "Gemfile.lock", "script", ".jekyll-cache/assets"]' site.config.keep_files = '[".git", ".svn", "cloud9.tar"]' site.config.encoding = 'utf-8' site.config.markdown_ext = 'markdown,mkdown,mkdn,mkd,md' site.config.strict_front_matter = 'false' site.config.show_drafts = 'true' site.config.limit_posts = '0' site.config.future = 'true' site.config.unpublished = 'false' site.config.whitelist = '[]' site.config.plugins = '["classifier-reborn", "html-proofer", "jekyll", "jekyll-admin", "jekyll-assets", "jekyll-docs", "jekyll-environment-variables", "jekyll-feed", "jekyll-gist", "jekyll-sitemap", "kramdown"]' site.config.markdown = 'kramdown' site.config.lsi = 'false' site.config.excerpt_separator = ' ' site.config.incremental = 'true' site.config.detach = 'false' site.config.port = '4000' site.config.host = '127.0.0.1' site.config.baseurl = '' site.config.show_dir_listing = 'false' site.config.permalink = '/blog/:year/:month/:day/:title:output_ext' site.config.paginate_path = '/page:num' site.config.timezone = '' site.config.quiet = 'false' site.config.verbose = 'false' site.config.defaults = '[]' site.config.liquid = '{"error_mode"=>"warn", "strict_filters"=>false, "strict_variables"=>false}' site.config.rdiscount = '{"extensions"=>[]}' site.config.redcarpet = '{"extensions"=>[]}' site.config.kramdown = '{"auto_ids"=>true, "toc_levels"=>"1..6", "entity_output"=>"as_char", "smart_quotes"=>"lsquo,rsquo,ldquo,rdquo", "input"=>"GFM", "hard_wrap"=>false, "footnote_nr"=>1, "show_warnings"=>false}' site.config.author = 'Mike Slinn' site.config.compress_html = '{"blanklines"=>false, "clippings"=>"all", "comments"=>[""], "endings"=>"all", "ignore"=>{"envs"=>["development"]}, "profile"=>false, "startings"=>["html", "head", "body"]}' site.config.email = 'mslinn@mslinn.com' site.config.feed = '{"categories"=>["AI", "Blockchain", "Scala", "Software-Expert"]}' site.config.ignore_theme_config = 'true' site.config.site_inspector = 'false' site.config.make_archive = '[{"archive_name"=>"cloud9.tar", "delete"=>true, "files"=>["!killPortFwdLocal", "!killPortFwdOnJumper", "!tunnelToJumper"]}]' site.config.sass = '{"style"=>"compressed"}' site.config.title = 'Mike Slinn' site.config.twitter = '{"username"=>"mslinn", "card"=>"summary"}' site.config.url = 'http://localhost:4000' site.config.livereload = 'true' site.config.livereload_port = '35729' site.config.serving = 'true' site.config.watch = 'true' site.config.assets = '{}' site.config.tag_data = '[]' site.keep_files: [".git", ".svn", "cloud9.tar"]
GitHub Project and RubyGem
More information is available about this plugin from its GitHub project at
github.com/mslinn/jekyll_site_inspector
.
This plugin is provided as a Ruby gem at rubygems.org/gems/jekyll_site_inspector
.
sort_natural
jekyll_sort
is a Jekyll plugin that sorts hashes using String#casecmp
,
the case-insensitive version of String#<=>
.
Usage Example
{% assign sorted_categories = site.categories | sort_natural %}
For More Information
By default, Enumerable#sort
uses <=>
for comparisons
See Add sort_natural
to jekyll/filters.rb
.
This plugin overrides sort_natural
provided by Liquid 4.
GitHub Project and RubyGem
More information is available about this plugin from its GitHub project at
github.com/mslinn/jekyll_sort_natural
.
This plugin is provided as a Ruby gem at rubygems.org/gems/jekyll_sort_natural
.
This plugin contains code modified from tkrotoff.
time_since
time_since
is a collection of Liquid filters that returns the elaped time since a date or date and time.
The filters are years_since
, months_since
, days_since
, hours_since
, minutes_since
and seconds_since
.
Usage Examples
These tags all work in a similar manner; they accept one parameter, which is either a date string (YYYY-MM-dd) or an ISO 8601-1:2019 datetime string (YYYY-MM-ddThh:mm). Just a year can be specified, which implies Jan 1 at midnight.
The date and time strings can be surrounded with quotes or double quotes. Time zone can be omitted, or specified as GMT, Z, or +/- HHMM.
{{ "1959" | years_since }}: 64 {{ "1959-02-03" | years_since }}: 63 {{ "1959-02-03T01:02" | years_since }}: 63 {{ "1959-02-03T01:02:00Z" | years_since }}: 63 {{ "1959" | months_since }}: 769 {{ "1959-02-03" | months_since }}: 768 {{ "1959-02-03T01:02" | months_since }}: 768 {{ "1959-02-03T01:02:00Z" | months_since }}: 768 {{ "1959" | weeks_since }}: 3344 {{ "1959-02-03" | weeks_since }}: 3339 {{ "1959-02-03T01:02" | weeks_since }}: 3339 {{ "1959-02-03T01:02:00Z" | weeks_since }}: 3339 {{ "1959" | days_since }}: 23408 {{ "1959-02-03" | days_since }}: 23375 {{ "1959-02-03T01:02" | days_since }}: 23375 {{ "1959-02-03T01:02:00Z" | days_since }}: 23375 {{ "1959" | hours_since}}: 561809 {{ "1959-02-03" | hours_since }}: 561017 {{ "1959-02-03T01:02" | hours_since }}: 561016 {{ "1959-02-03T01:02:00Z" | hours_since }}: 561021 {{ "1959" | minutes_since }}: 33708573 {{ "1959-02-03" | minutes_since }}: 33661053 {{ "1959-02-03T01:02" | minutes_since }}: 33660991 {{ "1959-02-03T01:02:00Z" | minutes_since }}: 33661291 {{ "1959" | seconds_since }}: 2022514381 {{ "1959-02-03" | seconds_since }}: 2019663181 {{ "1959-02-03T01:02" | seconds_since }}: 2019659461 {{ "1959-02-03T01:02:00" | seconds_since }}: 2019659461
GitHub Project and RubyGem
More information is available about this plugin from its GitHub project at
github.com/mslinn/jekyll_time_since
.
This plugin is provided as a Ruby gem at rubygems.org/gems/jekyll_time_since
.