-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.js
More file actions
101 lines (82 loc) · 2.98 KB
/
Copy pathmain.js
File metadata and controls
101 lines (82 loc) · 2.98 KB
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
// Auto Anchor Links - Copyright (C) 2026 InPoint Automation Sp. z o.o.
// Licensed under the GNU General Public License v3 or later; see LICENSE.
//
// Publii plugin. Wraps h2/h3 headings in anchor links for linking.
class AutoAnchorLinks {
constructor(API, name, config) {
this.API = API;
this.name = name;
this.config = config;
}
addModifiers() {
const isPost = this.config.applyTo === 'post' || this.config.applyTo === 'both';
const isPage = this.config.applyTo === 'page' || this.config.applyTo === 'both';
if (isPost) {
this.API.addModifier('postText', this.addAnchors, 2, this);
}
if (isPage) {
this.API.addModifier('pageText', this.addAnchors, 2, this);
}
}
addInsertions() {
this.API.addInsertion('customHeadCode', this.addStyles, 1, this);
}
addAnchors(rendererInstance, text) {
const usedIds = {};
const self = this;
const headingRegex = /<(h[23])(\s[^>]*)?>([\s\S]+?)<\/h[23]>/gi;
return text.replace(headingRegex, function (match, tag, attrs, content) {
attrs = attrs || '';
const existingIdMatch = attrs.match(/\bid="([^"]*)"/);
let id;
if (existingIdMatch) {
id = existingIdMatch[1];
} else {
const plainText = content.replace(/<[^>]+>/g, '');
id = self.generateSlug(plainText);
if (!id) return match; // empty heading, skip
let uniqueId = id;
let counter = 2;
while (usedIds[uniqueId]) {
uniqueId = id + '-' + counter;
counter++;
}
id = uniqueId;
attrs += ' id="' + id + '"';
}
usedIds[id] = true;
return '<' + tag + attrs + '>'
+ '<a class="heading-anchor" href="#' + id + '">' + content + '</a>'
+ '</' + tag + '>';
});
}
generateSlug(text) {
return text
.toLowerCase()
.trim()
.normalize('NFD')
.replace(/[\u0300-\u036f]/g, '')
.replace(/ł/g, 'l')
.replace(/Ł/g, 'l')
.replace(/[\s_]+/g, '-')
.replace(/[^a-z0-9-]/g, '')
.replace(/-+/g, '-')
.replace(/^-|-$/g, '');
}
addStyles(rendererInstance, context) {
if (!context.post && !context.page) return '';
return '<style>'
+ '.heading-anchor {'
+ ' color: inherit !important;'
+ ' font-weight: inherit !important;'
+ ' text-decoration: none !important;'
+ ' border-bottom: none !important;'
+ '}'
+ '.heading-anchor:hover {'
+ ' text-decoration: none !important;'
+ ' border-bottom: none !important;'
+ '}'
+ '</style>';
}
}
module.exports = AutoAnchorLinks;