Tag plugins for Hexo

31 July 2022. Comments .

Below are the plugins I use for personal needs with the Hexo static pages generator.

These tag plugins are built-in in Hexo theme helix.

Table of contents #

Usage #

Place prompt.js, jg.js, gif.js, comment.js into <root>/themes/<theme>/scripts/ or <root>/scripts/.

prompt tag #

prompt.js
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
'use strict';

/**
* Prompt tag by lisakov.com
*
* Syntax
*
* {% prompt [NAME] [DIR] [SYMBOL] [PREFIX] %}
* code
* code
* {% endprompt %}
*
* Examples
*
* {% prompt root 0 # %} → root #
* {% prompt root /dir # %} → root /dir #
* {% prompt livecd ~ # %} → livecd ~ #
* {% prompt user /dir $ %} → user /dir $
* {% prompt user 0 $ %} → user $
* {% prompt user %} → user $
* {% prompt livecd ~ # (chroot) %} → (chroot) livecd ~ #
*
*/

hexo.extend.tag.register('prompt', function(args, content){
args.unshift('prompt'); // make first argument args[1] not args[0]
var name='user';
var dir='';
var symbol='$';
var prefix='';
var promptclass = 'promptuser';
if (args.length > 1) {
name = args[1];
dir = args[2]+' ';
symbol = args[3];
}
if (typeof args[1] !=='undefined' && args[1].indexOf('root') >-1 || args[1]=='livecd') {
promptclass = 'promptroot';
}
if (args[2]=='0' || typeof args[2] == 'undefined') { dir = ''; }
if (args[3]=='0' || typeof args[3] == 'undefined') { symbol = ''; }
if (typeof args[4] !== 'undefined') { prefix = args[4] + ' '; }

var lines = content.split(/\r\n|\r|\n/).length+1
var gutter='';
var i=1;
for (i=1; i<lines; i++) {
gutter += prefix+"<span class='"+promptclass+"'>"+name+"</span> <span class='promptdir'>" + dir + symbol + "</span><br>"
}

var table = '<figure class="highlight prompt"><table><tr><td class="gutter"><pre>' + gutter + '</pre></td><td class="code"><pre>' + content + '</pre></td></tr></table></figure>';

return table;

}, {ends: true});

Example #

1
2
3
4
{% prompt root ~ # %}
mkdir -p /mnt/usb
mount -t vfat /dev/sdb1 /mnt/usb
{% endprompt %}

gives the following code (one-liner, but here I add line breaks and tabs for convenience):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<figure class="highlight">
<table>
<tr>
<td class="gutter">
<pre>
<span class='promptroot'>root</span> <span class='promptdir'>~ #</span><br>
<span class='promptroot'>root</span> <span class='promptdir'>~ #</span><br>
</pre>
</td>
<td class="code">
<pre>mkdir -p /mnt/usb
mount -t vfat /dev/sdb1 /mnt/usb
</pre>
</td>
</tr>
</table>
</figure>

With my style files browser outputs this:

root ~ #
root ~ #
mkdir -p /mnt/usb
mount -t vfat /dev/sdb1 /mnt/usb

Or an image (from 2022):

jg tag #

Creates justified brick wall of images with justifiedGallery.js. Another js-library, magnificPopup.js, makes every image clickable and opens a large version on the same page.

jg.js
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
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
'use strict';

/**
* jg tag by lisakov.com
*
* Examples jg
*
* {% jg img_path=https://lisakov.com/images/ thumbs_dir=800 thumbs_dir2=2000 lastonly=true %}
* imagename1.jpg
* imagename2.jpg "Title with description for imagename2"
* {% endjg %}
*
* Arguments
*
* path:
* path to images (img_path)
* default: 'https://lisakov.com/images/'
*
* thumbs_dir:
* path to thumbs (appended to img_path)
* default: 'thumbs/'
* none: no thumbs, use original images, useful for a single image
*
* thumbs_dir2:
* path to medium thumbs (appended to img_path)
* default: '800/'
* if lastonly=true, last element gets
*
* lastonly:
* <bool> (if true, use larger thumbs or original only for the last item)
* default: false
* use img_path + 'thumbs/' for every image except for the last,
* last will be img_path + thumbs (equals to anything, if thumbs=undefined, use original)
*
*/

hexo.extend.tag.register('jg', function(args, content){
// set defaults
var img_path = 'https://lisakov.com/images/';
var thumbs_dir = 'thumbs/';
var thumbs_dir2 = '800/';
var lastonly = 'false';
// read arguments and replace defaults if needed
args.forEach((el, idx) => {
if (el.includes('img_path=')) { img_path = el.replace('img_path=',''); }
if (el.includes('lastonly=')) { lastonly = el.replace('lastonly=',''); }
if (el.includes('thumbs_dir=')) {
thumbs_dir = el.replace('thumbs_dir=','');
thumbs_dir += thumbs_dir.endsWith("/") ? "" : "/" //add trailing slash if not present
if (thumbs_dir.includes('none')) { thumbs_dir=''; }
}
if (el.includes('thumbs_dir2=')) {
thumbs_dir2 = el.replace('thumbs_dir2=','');
thumbs_dir2 += thumbs_dir2.endsWith("/") ? "" : "/" //add trailing slash if not present
if (thumbs_dir2.includes('none')) { thumbs_dir2 = ''; }
}
});

// Loop through all lines
var lines = content.split(/\r\n|\r|\n/);
lines.forEach((element, index, array) => {
if (lastonly === 'false') {
// if a line contains imgname but no title
if (lines[index].indexOf('"') === -1) {
lines[index] = '<a href=" ' + img_path + element + '"><img src="' + img_path + thumbs_dir + element + '"/></a>';
// if a line contains imgname and title
} else {
var title = lines[index].match(/"([^']+)"/)[1];
var img_name = lines[index].split(' ')[0];
lines[index] = '<a href=" ' + img_path + img_name + '"' + 'title="' + title + '"><img src="' + img_path + thumbs_dir + img_name + '"/></a>';
}
// if lastonly=true
} else {

// if it is the LAST element
if (index === array.length - 1) {
// if a line contains imgname but no title
if (lines[index].indexOf('"') === -1) {
lines[index] = '<a href=" ' + img_path + element + '"><img src="' + img_path + thumbs_dir2 + element + '"/></a>';
// if a line contains imgname and title
} else {
var title = lines[index].match(/"([^']+)"/)[1];
var img_name = lines[index].split(' ')[0];
lines[index] = '<a href=" ' + img_path + img_name + '"' + 'title="' + title + '"><img src="' + img_path + thumbs_dir2 + img_name + '"/></a>';
}

// if it is NOT the LAST element
} else {
// if a line contains imgname but no title
if (lines[index].indexOf('"') === -1) {
lines[index] = '<a href=" ' + img_path + element + '"><img src="' + img_path + thumbs_dir + element + '"/></a>';
// if a line contains imgname and title
} else {
var title = lines[index].match(/"([^']+)"/)[1];
var img_name = lines[index].split(' ')[0];
lines[index] = '<a href=" ' + img_path + img_name + '"' + 'title="' + title + '"><img src="' + img_path + thumbs_dir + img_name + '"/></a>';
}
}

}
});

return '<div class="jg">' + lines.join(' ') + '</div>';
}, {ends: true});

Load on the page (set correct paths):

1
2
3
4
<link rel="stylesheet" href="justifiedGallery.min.css" />
<script src="jquery.justifiedGallery.min.js"></script>
<script src="magnific-popup.js" ></script>
<link rel="stylesheet" href="magnific-popup.min.css" />

Tag plugin adds .js class to gallery. Tell justifiedGallery.js about it:

1
2
3
4
5
6
7
<script>
$('.jg').justifiedGallery({
rowHeight : 130,
lastRow : '<%=page.justifiedgallery %>',
margins : 2
});
</script>

Inform magnificPopup.js, that it needs to work with the same .jg class:

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
<script>
$('.jg').magnificPopup({
delegate: 'a',
type: 'image',
verticalFit: true,
gallery: { enabled:true }
});
$(document).ready(function() {
$('.popup-with-zoom-anim').magnificPopup({
type: 'inline',
fixedContentPos: false,
fixedBgPos: true,
overflowY: 'auto',
closeBtnInside: true,
preloader: false,
gallery: {
enabled: true,
navigateByImgClick: true,
preload: [0,1] // Will preload 0 - before current, and 1 after the current image
},
midClick: true,
removalDelay: 300,
mainClass: 'my-mfp-zoom-in'
});
});
</script>

Use theme layouts to load <script> tags like so in themes/themename/layout/_partial/head.ejs and after_footer.ejs:

1
2
3
<% if (page.justifiedgallery) { %>
...
<% } %>

Example #

1
2
3
4
5
{% jg img_path=https://lisakov.com/images/pu-2021/ %}
map-3.png "ESRI Satellite"
map-1.png "10-km"
map-2.png "Google Terrain"
{% endjg %}

returns (again, actually in one line):

1
2
3
4
5
6
7
8
9
10
11
<div class="jg">
<a href=" https://lisakov.com/images/pu-2021/map-3.png"title="ESRI Satellite">
<img src="https://lisakov.com/images/pu-2021/thumbs/map-3.png"/>
</a>
<a href=" https://lisakov.com/images/pu-2021/map-1.png"title="10-km">
<img src="https://lisakov.com/images/pu-2021/thumbs/map-1.png"/>
</a>
<a href=" https://lisakov.com/images/pu-2021/map-2.png"title="Google Terrain">
<img src="https://lisakov.com/images/pu-2021/thumbs/map-2.png"/>
</a>
</div>

Check how your browser outputs it:

gif tag #

Play/stop gif-animations on click with gifonclick.js. Don’t forget to load it on the page.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
'use strict';

/**
* gifonclick tag by lisakov.com
*
* Syntax:
* {% gif image.png animation.gif %}
*/

hexo.extend.tag.register('gif', function(args){
var img_path = args[0];
var gif_path = args[1];
return '<div class="cf" style="text-align:center"> <figure> <img src="' + img_path + '" data-alt="' + gif_path + '"></figure></div>'
});

You also need some css for it:

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
.cf figure {
display: inline-block;
margin: 5 auto;
padding: 0px;
position: relative;
cursor: pointer;
}
.cf figure:before,
.cf figure:after {
position: absolute;
}
.cf figure:before {
content: url('https://lisakov.com/images/pbg.png');
display: inline-block;
left: 45%;
top: 45%;
}
.cf figure:after {
content: 'GIF';
position: absolute;
display: inline-block;
width: 40px;
text-align: center;
top: 47px;
right: 12px;
font-size: 11px;
font-weight: 600;
padding: 0px;
border-radius: 3px;
color: #fff;
background-color: rgba(170, 178, 189, 0.1);
}
.cf figure.play:before {
display: none;
}
.cf figure.play:after {
color: #fff;
background-color: #8CC152;
}
.cf figcaption {
text-align: center;
padding-top: 15px;
font-size: 14px;
color: #8d9bad;
}
.cf figcaption a {
color: #59687b;
text-decoration: none;
}

comment tag #

Comment lines in your *.md files. Won’t render to html.

1
2
3
4
5
6
7
8
9
10
11
12
13
'use strict';

/**
* Comment tag by lisakov.com
*
* {% comment %}
* Text here won't be rendered
* {% endcomment %}
*
*/

hexo.extend.tag.register('comment', function(args, content){
}, {ends: true});