hexo-theme-gnix 9.0.0 → 10.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -2
- package/include/hexo/feed.js +5 -5
- package/include/hexo/filter.js +25 -1
- package/include/hexo/generator/archive.js +116 -0
- package/include/hexo/generator/home.js +64 -0
- package/include/hexo/generator/index.js +82 -0
- package/include/hexo/generator/md_generator.js +87 -0
- package/include/hexo/generator/page.js +55 -0
- package/include/hexo/generator/tag.js +84 -0
- package/include/hexo/helper.js +38 -0
- package/include/hexo/i18n.js +183 -0
- package/include/util/article_font.js +132 -0
- package/include/util/i18n.js +280 -0
- package/include/util/theme.js +84 -0
- package/languages/en.yml +28 -0
- package/languages/zh-CN.yml +28 -0
- package/layout/archive.jsx +131 -127
- package/layout/common/article.jsx +283 -16
- package/layout/common/article_info.jsx +339 -0
- package/layout/common/article_media.jsx +11 -4
- package/layout/common/comment.jsx +15 -7
- package/layout/common/footer.jsx +6 -5
- package/layout/common/head.jsx +121 -32
- package/layout/common/navbar.jsx +195 -65
- package/layout/common/theme_selector.jsx +16 -14
- package/layout/layout.jsx +43 -5
- package/layout/misc/open_graph.jsx +162 -66
- package/layout/misc/paginator.jsx +2 -8
- package/layout/plugin/cookie_consent.jsx +252 -53
- package/layout/plugin/swup.jsx +1 -1
- package/layout/search/insight.jsx +1 -1
- package/layout/tag.jsx +3 -2
- package/layout/tags.jsx +81 -73
- package/package.json +5 -5
- package/scripts/index.js +1 -0
- package/source/css/archive.css +225 -180
- package/source/css/default.css +1162 -98
- package/source/css/responsive.css +426 -0
- package/source/css/shiki/shiki.css +12 -2081
- package/source/css/tags.css +183 -0
- package/source/css/twikoo.css +1049 -1045
- package/source/img/favicon.svg +1 -6
- package/source/img/og_image.webp +0 -0
- package/source/js/article-font-utils.js +99 -0
- package/source/js/busuanzi.js +91 -24
- package/source/js/components/chat.js +169 -50
- package/source/js/components/image-carousel.js +152 -108
- package/source/js/components/sidenote.js +210 -0
- package/source/js/components/text-image-section.js +78 -90
- package/source/js/components/theme-stacked.js +65 -33
- package/source/js/components/tree.js +30 -16
- package/source/js/decrypt.js +7 -2
- package/source/js/main.js +428 -5
- package/source/js/swup.js +39 -0
- package/source/js/theme-selector.js +26 -16
- package/include/hexo/generator.js +0 -53
- package/layout/misc/article_licensing.jsx +0 -99
- package/source/css/responsive/desktop.css +0 -36
- package/source/css/responsive/mobile.css +0 -29
- package/source/css/responsive/tablet.css +0 -43
- package/source/css/responsive/touch.css +0 -155
- package/source/img/logo.svg +0 -9
- package/source/js/archive-breadcrumb.js +0 -132
- package/source/js/host/cookieconsent/3.1.1/build/cookieconsent.min.css +0 -6
- package/source/js/host/cookieconsent/3.1.1/build/cookieconsent.min.js +0 -1
- package/source/js/swup.bundle.js +0 -1
|
@@ -1,7 +1,18 @@
|
|
|
1
1
|
const { Component, isValidDate, parseISO } = require("../../include/util/common");
|
|
2
|
-
const { encodeURL, stripHTML
|
|
2
|
+
const { encodeURL, stripHTML } = require("hexo-util");
|
|
3
|
+
|
|
4
|
+
const DESCRIPTION_LENGTH = 160;
|
|
5
|
+
const IMAGE_TYPES = {
|
|
6
|
+
avif: "image/avif",
|
|
7
|
+
gif: "image/gif",
|
|
8
|
+
jpeg: "image/jpeg",
|
|
9
|
+
jpg: "image/jpeg",
|
|
10
|
+
png: "image/png",
|
|
11
|
+
webp: "image/webp",
|
|
12
|
+
};
|
|
3
13
|
|
|
4
14
|
const localeMap = {
|
|
15
|
+
zh: "zh_CN",
|
|
5
16
|
en: "en_US",
|
|
6
17
|
de: "de_DE",
|
|
7
18
|
es: "es_ES",
|
|
@@ -17,91 +28,176 @@ const localeMap = {
|
|
|
17
28
|
tr: "tr_TR",
|
|
18
29
|
vi: "vi_VN",
|
|
19
30
|
};
|
|
20
|
-
|
|
31
|
+
|
|
32
|
+
function normalizeText(value, maxLength) {
|
|
33
|
+
if (typeof value !== "string") return "";
|
|
34
|
+
|
|
35
|
+
const text = stripHTML(value).replace(/\s+/g, " ").trim();
|
|
36
|
+
if (!maxLength || text.length <= maxLength) return text;
|
|
37
|
+
|
|
38
|
+
return (
|
|
39
|
+
text
|
|
40
|
+
.slice(0, maxLength)
|
|
41
|
+
.replace(/\s+\S*$/, "")
|
|
42
|
+
.trim() || text.slice(0, maxLength).trim()
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function normalizeUrl(value, base) {
|
|
47
|
+
if (!value) return "";
|
|
48
|
+
|
|
49
|
+
try {
|
|
50
|
+
return encodeURL(new URL(value, base).toString());
|
|
51
|
+
} catch (_) {
|
|
52
|
+
return "";
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function normalizeLocale(language) {
|
|
57
|
+
if (typeof language !== "string") return "";
|
|
58
|
+
|
|
59
|
+
const normalized = language.replace("_", "-").trim();
|
|
60
|
+
if (normalized.length === 2) {
|
|
61
|
+
return localeMap[normalized.toLowerCase()] || "";
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (/^[a-z]{2}-[a-z]{2}$/i.test(normalized)) {
|
|
65
|
+
const [lang, territory] = normalized.split("-");
|
|
66
|
+
return `${lang.toLowerCase()}_${territory.toUpperCase()}`;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return "";
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function normalizeImageUrls(images, base) {
|
|
73
|
+
const values = Array.isArray(images) ? images : [images];
|
|
74
|
+
|
|
75
|
+
return [
|
|
76
|
+
...new Set(
|
|
77
|
+
values
|
|
78
|
+
.filter(Boolean)
|
|
79
|
+
.map((path) => normalizeUrl(path, base))
|
|
80
|
+
.filter(Boolean)
|
|
81
|
+
.filter((url) => {
|
|
82
|
+
try {
|
|
83
|
+
const { protocol } = new URL(url);
|
|
84
|
+
return protocol === "http:" || protocol === "https:";
|
|
85
|
+
} catch (_) {
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
}),
|
|
89
|
+
),
|
|
90
|
+
];
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function getImageType(url) {
|
|
94
|
+
try {
|
|
95
|
+
const ext = new URL(url).pathname.split(".").pop().toLowerCase();
|
|
96
|
+
return IMAGE_TYPES[ext] || "";
|
|
97
|
+
} catch (_) {
|
|
98
|
+
return "";
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function normalizeKeywords(keywords) {
|
|
103
|
+
const values = Array.isArray(keywords) ? keywords : [keywords];
|
|
104
|
+
const names = values.flatMap((keyword) => {
|
|
105
|
+
const value = keyword && typeof keyword === "object" ? keyword.name : keyword;
|
|
106
|
+
if (typeof value !== "string") return [];
|
|
107
|
+
return value.split(",");
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
return [...new Set(names.map((keyword) => normalizeText(keyword)).filter(Boolean))];
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function normalizeTwitterHandle(value) {
|
|
114
|
+
const handle = normalizeText(value);
|
|
115
|
+
if (!handle) return "";
|
|
116
|
+
|
|
117
|
+
return handle.startsWith("@") ? handle : `@${handle}`;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function normalizeDate(value) {
|
|
121
|
+
if (!value) return "";
|
|
122
|
+
|
|
123
|
+
const date = typeof value === "string" ? parseISO(value) : value;
|
|
124
|
+
return isValidDate(date) ? date.toISOString() : "";
|
|
125
|
+
}
|
|
21
126
|
|
|
22
127
|
module.exports = class extends Component {
|
|
23
128
|
render() {
|
|
24
|
-
const { type, title, date, updated, author, url, siteName, twitterCard, twitterSite, googlePlus, facebookAdmins, facebookAppId } = this.props;
|
|
25
|
-
let { description, language, images, keywords, twitterId } = this.props;
|
|
129
|
+
const { type, title, date, updated, author, url, siteName, twitterCard, twitterSite, googlePlus, facebookAdmins, facebookAppId, section, imageWidth, imageHeight } = this.props;
|
|
26
130
|
|
|
27
|
-
|
|
131
|
+
let { description, language, images, keywords, twitterId, imageAlt } = this.props;
|
|
28
132
|
|
|
133
|
+
const htmlTags = [];
|
|
134
|
+
const ogType = type || "website";
|
|
135
|
+
const isArticle = ogType === "article";
|
|
136
|
+
const pageUrl = normalizeUrl(url, url);
|
|
137
|
+
const normalizedTitle = normalizeText(title || siteName);
|
|
138
|
+
const normalizedSiteName = normalizeText(siteName);
|
|
139
|
+
const normalizedAuthor = normalizeText(author);
|
|
140
|
+
const publishedTime = normalizeDate(date);
|
|
141
|
+
const modifiedTime = normalizeDate(updated);
|
|
142
|
+
const normalizedKeywords = normalizeKeywords(keywords);
|
|
143
|
+
|
|
144
|
+
description = normalizeText(description, DESCRIPTION_LENGTH);
|
|
29
145
|
if (description) {
|
|
30
|
-
description = escapeHTML(stripHTML(description).substring(0, 200).trim()).replace(/\n/g, " ");
|
|
31
146
|
htmlTags.push(<meta name="description" content={description} />);
|
|
32
147
|
htmlTags.push(<meta property="og:description" content={description} />);
|
|
33
148
|
}
|
|
34
149
|
|
|
35
|
-
htmlTags.push(<meta property="og:type" content={
|
|
36
|
-
htmlTags.push(<meta property="og:title" content={
|
|
37
|
-
htmlTags.push(<meta property="og:url" content={
|
|
38
|
-
htmlTags.push(<meta property="og:site_name" content={
|
|
39
|
-
|
|
40
|
-
if (language) {
|
|
41
|
-
if (language.length === 2) {
|
|
42
|
-
language = language.replace(localeRegex, (str) => localeMap[str]);
|
|
43
|
-
htmlTags.push(<meta property="og:locale" content={language} />);
|
|
44
|
-
} else if (language.length === 5) {
|
|
45
|
-
const territory = language.slice(-2);
|
|
46
|
-
const territoryRegex = new RegExp(territory.concat("$"));
|
|
47
|
-
language = language.replace("-", "_").replace(territoryRegex, territory.toUpperCase());
|
|
48
|
-
htmlTags.push(<meta property="og:locale" content={language} />);
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
if (!Array.isArray(images)) {
|
|
53
|
-
images = [images];
|
|
54
|
-
}
|
|
55
|
-
images
|
|
56
|
-
.map((path) => {
|
|
57
|
-
const parsed = new URL(path, url);
|
|
58
|
-
return parsed.toString();
|
|
59
|
-
})
|
|
60
|
-
.forEach((path) => {
|
|
61
|
-
htmlTags.push(<meta property="og:image" content={path} />);
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
if (date) {
|
|
65
|
-
const d = typeof date === "string" ? parseISO(date) : date;
|
|
66
|
-
if (isValidDate(d)) {
|
|
67
|
-
htmlTags.push(<meta property="article:published_time" content={d.toISOString()} />);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
150
|
+
htmlTags.push(<meta property="og:type" content={ogType} />);
|
|
151
|
+
if (normalizedTitle) htmlTags.push(<meta property="og:title" content={normalizedTitle} />);
|
|
152
|
+
if (pageUrl) htmlTags.push(<meta property="og:url" content={pageUrl} />);
|
|
153
|
+
if (normalizedSiteName) htmlTags.push(<meta property="og:site_name" content={normalizedSiteName} />);
|
|
70
154
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
if (isValidDate(u)) {
|
|
74
|
-
htmlTags.push(<meta property="article:modified_time" content={u.toISOString()} />);
|
|
75
|
-
}
|
|
76
|
-
}
|
|
155
|
+
language = normalizeLocale(language);
|
|
156
|
+
if (language) htmlTags.push(<meta property="og:locale" content={language} />);
|
|
77
157
|
|
|
78
|
-
|
|
158
|
+
const imageUrls = normalizeImageUrls(images, pageUrl || url);
|
|
159
|
+
imageAlt = normalizeText(imageAlt || title);
|
|
79
160
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
}
|
|
161
|
+
imageUrls.forEach((path) => {
|
|
162
|
+
htmlTags.push(<meta property="og:image" content={path} />);
|
|
163
|
+
if (path.startsWith("https://")) htmlTags.push(<meta property="og:image:secure_url" content={path} />);
|
|
84
164
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
htmlTags.push(<meta property="article:tag" content={keyword} />);
|
|
92
|
-
});
|
|
93
|
-
}
|
|
165
|
+
const imageType = getImageType(path);
|
|
166
|
+
if (imageType) htmlTags.push(<meta property="og:image:type" content={imageType} />);
|
|
167
|
+
if (imageWidth) htmlTags.push(<meta property="og:image:width" content={imageWidth} />);
|
|
168
|
+
if (imageHeight) htmlTags.push(<meta property="og:image:height" content={imageHeight} />);
|
|
169
|
+
if (imageAlt) htmlTags.push(<meta property="og:image:alt" content={imageAlt} />);
|
|
170
|
+
});
|
|
94
171
|
|
|
95
|
-
|
|
172
|
+
if (isArticle) {
|
|
173
|
+
if (publishedTime) htmlTags.push(<meta property="article:published_time" content={publishedTime} />);
|
|
174
|
+
if (modifiedTime) htmlTags.push(<meta property="article:modified_time" content={modifiedTime} />);
|
|
175
|
+
if (normalizedAuthor) htmlTags.push(<meta property="article:author" content={normalizedAuthor} />);
|
|
96
176
|
|
|
97
|
-
|
|
177
|
+
const normalizedSection = normalizeText(section);
|
|
178
|
+
if (normalizedSection) htmlTags.push(<meta property="article:section" content={normalizedSection} />);
|
|
98
179
|
|
|
99
|
-
|
|
180
|
+
normalizedKeywords.forEach((keyword) => {
|
|
181
|
+
htmlTags.push(<meta property="article:tag" content={keyword} />);
|
|
182
|
+
});
|
|
183
|
+
} else if (modifiedTime) {
|
|
184
|
+
htmlTags.push(<meta property="og:updated_time" content={modifiedTime} />);
|
|
185
|
+
}
|
|
100
186
|
|
|
187
|
+
htmlTags.push(<meta name="twitter:card" content={twitterCard || (imageUrls.length > 0 ? "summary_large_image" : "summary")} />);
|
|
188
|
+
if (normalizedTitle) htmlTags.push(<meta name="twitter:title" content={normalizedTitle} />);
|
|
189
|
+
if (description) htmlTags.push(<meta name="twitter:description" content={description} />);
|
|
190
|
+
if (pageUrl) htmlTags.push(<meta name="twitter:url" content={pageUrl} />);
|
|
191
|
+
if (imageUrls.length > 0) {
|
|
192
|
+
htmlTags.push(<meta name="twitter:image" content={imageUrls[0]} />);
|
|
193
|
+
if (imageAlt) htmlTags.push(<meta name="twitter:image:alt" content={imageAlt} />);
|
|
194
|
+
}
|
|
195
|
+
const normalizedTwitterId = normalizeTwitterHandle(twitterId);
|
|
196
|
+
const normalizedTwitterSite = normalizeTwitterHandle(twitterSite);
|
|
197
|
+
if (normalizedTwitterId) htmlTags.push(<meta name="twitter:creator" content={normalizedTwitterId} />);
|
|
198
|
+
if (normalizedTwitterSite) htmlTags.push(<meta name="twitter:site" content={normalizedTwitterSite} />);
|
|
101
199
|
if (googlePlus) htmlTags.push(<link rel="publisher" href={googlePlus} />);
|
|
102
|
-
|
|
103
200
|
if (facebookAdmins) htmlTags.push(<meta property="fb:admins" content={facebookAdmins} />);
|
|
104
|
-
|
|
105
201
|
if (facebookAppId) htmlTags.push(<meta property="fb:app_id" content={facebookAppId} />);
|
|
106
202
|
|
|
107
203
|
return <>{htmlTags}</>;
|
|
@@ -57,16 +57,10 @@ module.exports = class extends Component {
|
|
|
57
57
|
return (
|
|
58
58
|
<nav class="pagination card" aria-label="pagination">
|
|
59
59
|
<a href={getPageUrl(current - 1)} class={`pagination-previous`} style={current > 1 ? {} : { visibility: "hidden" }}>
|
|
60
|
-
|
|
61
|
-
<title>go to previous page</title>
|
|
62
|
-
<path fill="currentColor" d="m4 10l9 9l1.4-1.5L7 10l7.4-7.5L13 1z" />
|
|
63
|
-
</svg>
|
|
60
|
+
Prev
|
|
64
61
|
</a>
|
|
65
62
|
<a href={getPageUrl(current + 1)} class={`pagination-next`} style={current < total ? {} : { visibility: "hidden" }}>
|
|
66
|
-
|
|
67
|
-
<title>go to next page</title>
|
|
68
|
-
<path fill="currentColor" d="M7 1L5.6 2.5L13 10l-7.4 7.5L7 19l9-9z" />
|
|
69
|
-
</svg>
|
|
63
|
+
Next
|
|
70
64
|
</a>
|
|
71
65
|
<ul class="pagination-list is-hidden-mobile">{pagination(current, total)}</ul>
|
|
72
66
|
</nav>
|
|
@@ -1,51 +1,264 @@
|
|
|
1
1
|
const { Component, cacheComponent } = require("../../include/util/common");
|
|
2
2
|
|
|
3
|
+
const STORAGE_KEY = "gnix:cookie-consent";
|
|
4
|
+
const LEGACY_STORAGE_KEY = "cookieconsent_status";
|
|
5
|
+
|
|
6
|
+
const COOKIE_CONSENT_STYLE = `
|
|
7
|
+
.gnix-cookie-consent {
|
|
8
|
+
--cookie-consent-x: 0;
|
|
9
|
+
--cookie-consent-y: 0.5rem;
|
|
10
|
+
position: fixed;
|
|
11
|
+
bottom: max(1rem, env(safe-area-inset-bottom));
|
|
12
|
+
left: max(1rem, env(safe-area-inset-left));
|
|
13
|
+
z-index: 140;
|
|
14
|
+
box-sizing: border-box;
|
|
15
|
+
display: grid;
|
|
16
|
+
grid-template-columns: minmax(0, 1fr) auto;
|
|
17
|
+
gap: 0.875rem 1rem;
|
|
18
|
+
width: min(32rem, calc(100vw - 2rem));
|
|
19
|
+
padding: 1rem;
|
|
20
|
+
border: 1px solid var(--surface0);
|
|
21
|
+
border-radius: var(--radius);
|
|
22
|
+
background: var(--mantle);
|
|
23
|
+
color: var(--text);
|
|
24
|
+
box-shadow:
|
|
25
|
+
0 20px 60px -32px rgba(0, 0, 0, 0.58),
|
|
26
|
+
0 0 0 1px hsl(from var(--base) h s l / 0.18);
|
|
27
|
+
font-family: var(--font-sans-serif);
|
|
28
|
+
line-height: 1.55;
|
|
29
|
+
opacity: 0;
|
|
30
|
+
transform: translate3d(var(--cookie-consent-x), var(--cookie-consent-y), 0);
|
|
31
|
+
transition:
|
|
32
|
+
opacity 0.18s ease,
|
|
33
|
+
transform 0.18s ease;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
@supports (background: color-mix(in oklch, black 50%, transparent)) {
|
|
37
|
+
.gnix-cookie-consent {
|
|
38
|
+
background: color-mix(in oklch, var(--mantle) 88%, transparent);
|
|
39
|
+
-webkit-backdrop-filter: blur(16px) saturate(1.2);
|
|
40
|
+
backdrop-filter: blur(16px) saturate(1.2);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.gnix-cookie-consent.is-visible {
|
|
45
|
+
--cookie-consent-y: 0;
|
|
46
|
+
opacity: 1;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.gnix-cookie-consent.is-hiding {
|
|
50
|
+
--cookie-consent-y: 0.5rem;
|
|
51
|
+
opacity: 0;
|
|
52
|
+
pointer-events: none;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.gnix-cookie-consent.is-static {
|
|
56
|
+
position: static;
|
|
57
|
+
width: min(42rem, calc(100% - 2rem));
|
|
58
|
+
margin: 1rem auto;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.gnix-cookie-consent__message {
|
|
62
|
+
min-width: 0;
|
|
63
|
+
margin: 0;
|
|
64
|
+
color: var(--subtext1);
|
|
65
|
+
font-size: 0.9rem;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.gnix-cookie-consent__link {
|
|
69
|
+
color: var(--lavender);
|
|
70
|
+
text-decoration: none;
|
|
71
|
+
white-space: nowrap;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.gnix-cookie-consent__link:hover {
|
|
75
|
+
text-decoration: underline;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.gnix-cookie-consent__actions {
|
|
79
|
+
display: flex;
|
|
80
|
+
flex-wrap: wrap;
|
|
81
|
+
align-items: center;
|
|
82
|
+
justify-content: flex-end;
|
|
83
|
+
gap: 0.5rem;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
.gnix-cookie-consent__button {
|
|
87
|
+
min-height: 2.25rem;
|
|
88
|
+
padding: 0.55rem 0.8rem;
|
|
89
|
+
border: 1px solid var(--surface0);
|
|
90
|
+
border-radius: 8px;
|
|
91
|
+
background: hsl(from var(--surface0) h s l / 0.24);
|
|
92
|
+
color: var(--text);
|
|
93
|
+
cursor: pointer;
|
|
94
|
+
font: 600 0.78rem/1 var(--font-sans-serif);
|
|
95
|
+
transition:
|
|
96
|
+
background-color 0.15s ease,
|
|
97
|
+
border-color 0.15s ease,
|
|
98
|
+
color 0.15s ease,
|
|
99
|
+
transform 0.15s ease;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.gnix-cookie-consent__button:hover {
|
|
103
|
+
border-color: var(--overlay0);
|
|
104
|
+
background: hsl(from var(--surface0) h s l / 0.38);
|
|
105
|
+
transform: translateY(-1px);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.gnix-cookie-consent__button:focus-visible {
|
|
109
|
+
outline: 2px solid var(--lavender);
|
|
110
|
+
outline-offset: 2px;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.gnix-cookie-consent__button--primary {
|
|
114
|
+
border-color: var(--lavender);
|
|
115
|
+
background: var(--lavender);
|
|
116
|
+
color: var(--base);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
.gnix-cookie-consent__button--primary:hover {
|
|
120
|
+
border-color: var(--mauve);
|
|
121
|
+
background: var(--mauve);
|
|
122
|
+
color: var(--base);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
@media (max-width: 640px) {
|
|
126
|
+
.gnix-cookie-consent {
|
|
127
|
+
left: max(0.75rem, env(safe-area-inset-left));
|
|
128
|
+
grid-template-columns: 1fr;
|
|
129
|
+
width: auto;
|
|
130
|
+
padding: 0.875rem;
|
|
131
|
+
--cookie-consent-x: 0;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
.gnix-cookie-consent__actions {
|
|
135
|
+
justify-content: stretch;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
.gnix-cookie-consent__button {
|
|
139
|
+
flex: 1 1 auto;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
`;
|
|
143
|
+
|
|
144
|
+
function stringifyScriptData(value) {
|
|
145
|
+
return JSON.stringify(value).replace(/</g, "\\u003c");
|
|
146
|
+
}
|
|
147
|
+
|
|
3
148
|
class CookieConsent extends Component {
|
|
4
|
-
// https://www.osano.com/cookieconsent/documentation/javascript-api/
|
|
5
149
|
render() {
|
|
6
|
-
const { head
|
|
7
|
-
const { type, theme, position, policyLink } = this.props;
|
|
8
|
-
const { message, dismiss, allow, deny, link, policy } = text;
|
|
150
|
+
const { head } = this.props;
|
|
9
151
|
|
|
10
152
|
if (head) {
|
|
11
|
-
return <
|
|
153
|
+
return <style id="gnix-cookie-consent-style" dangerouslySetInnerHTML={{ __html: COOKIE_CONSENT_STYLE }} />;
|
|
12
154
|
}
|
|
155
|
+
|
|
156
|
+
const options = {
|
|
157
|
+
storageKey: STORAGE_KEY,
|
|
158
|
+
legacyStorageKey: LEGACY_STORAGE_KEY,
|
|
159
|
+
policyLink: this.props.policyLink,
|
|
160
|
+
isStatic: this.props.isStatic,
|
|
161
|
+
};
|
|
162
|
+
|
|
13
163
|
return (
|
|
14
164
|
<script
|
|
15
165
|
dangerouslySetInnerHTML={{
|
|
16
166
|
__html: `
|
|
17
167
|
(function() {
|
|
18
|
-
var
|
|
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
|
-
|
|
168
|
+
var options = ${stringifyScriptData(options)};
|
|
169
|
+
var rootSelector = "[data-gnix-cookie-consent]";
|
|
170
|
+
|
|
171
|
+
function readStoredConsent() {
|
|
172
|
+
try {
|
|
173
|
+
var stored = window.localStorage && (localStorage.getItem(options.storageKey) || localStorage.getItem(options.legacyStorageKey));
|
|
174
|
+
if (stored) return stored;
|
|
175
|
+
} catch (_) {}
|
|
176
|
+
|
|
177
|
+
var match = document.cookie.match(/(?:^|; )cookieconsent_status=([^;]+)/);
|
|
178
|
+
return match ? decodeURIComponent(match[1]) : "";
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function writeConsent(status) {
|
|
182
|
+
try {
|
|
183
|
+
localStorage.setItem(options.storageKey, status);
|
|
184
|
+
localStorage.setItem(options.legacyStorageKey, status);
|
|
185
|
+
} catch (_) {}
|
|
186
|
+
|
|
187
|
+
document.cookie = "cookieconsent_status=" + encodeURIComponent(status) + "; path=/; max-age=31536000; SameSite=Lax";
|
|
188
|
+
window.gnixCookieConsent = { status: status };
|
|
189
|
+
|
|
190
|
+
try {
|
|
191
|
+
window.dispatchEvent(new CustomEvent("gnix:cookie-consent", { detail: { status: status } }));
|
|
192
|
+
} catch (_) {}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function createButton(label, action, variant) {
|
|
196
|
+
var button = document.createElement("button");
|
|
197
|
+
button.type = "button";
|
|
198
|
+
button.className = "gnix-cookie-consent__button" + (variant ? " gnix-cookie-consent__button--" + variant : "");
|
|
199
|
+
button.textContent = label;
|
|
200
|
+
button.setAttribute("data-cookie-consent-action", action);
|
|
201
|
+
return button;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function closeConsent(root, status) {
|
|
205
|
+
writeConsent(status);
|
|
206
|
+
root.classList.remove("is-visible");
|
|
207
|
+
root.classList.add("is-hiding");
|
|
208
|
+
window.setTimeout(function() {
|
|
209
|
+
if (root.parentNode) root.parentNode.removeChild(root);
|
|
210
|
+
}, 180);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function initCookieConsent() {
|
|
214
|
+
if (readStoredConsent() || document.querySelector(rootSelector)) return;
|
|
215
|
+
|
|
216
|
+
var root = document.createElement("aside");
|
|
217
|
+
root.className = "gnix-cookie-consent" + (options.isStatic ? " is-static" : "");
|
|
218
|
+
root.setAttribute("data-gnix-cookie-consent", "");
|
|
219
|
+
root.setAttribute("role", "dialog");
|
|
220
|
+
root.setAttribute("aria-live", "polite");
|
|
221
|
+
root.setAttribute("aria-label", "Cookie notice");
|
|
222
|
+
|
|
223
|
+
var message = document.createElement("p");
|
|
224
|
+
message.className = "gnix-cookie-consent__message";
|
|
225
|
+
message.appendChild(document.createTextNode("This website uses cookies to improve your experience."));
|
|
226
|
+
|
|
227
|
+
if (options.policyLink) {
|
|
228
|
+
var link = document.createElement("a");
|
|
229
|
+
link.className = "gnix-cookie-consent__link";
|
|
230
|
+
link.href = options.policyLink;
|
|
231
|
+
link.target = "_blank";
|
|
232
|
+
link.rel = "noopener";
|
|
233
|
+
link.textContent = "Learn more";
|
|
234
|
+
message.appendChild(document.createTextNode(" "));
|
|
235
|
+
message.appendChild(link);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
var actions = document.createElement("div");
|
|
239
|
+
actions.className = "gnix-cookie-consent__actions";
|
|
240
|
+
actions.appendChild(createButton("Decline", "deny"));
|
|
241
|
+
actions.appendChild(createButton("Accept", "allow", "primary"));
|
|
242
|
+
|
|
243
|
+
actions.addEventListener("click", function(event) {
|
|
244
|
+
var button = event.target.closest("[data-cookie-consent-action]");
|
|
245
|
+
if (!button) return;
|
|
246
|
+
closeConsent(root, button.getAttribute("data-cookie-consent-action"));
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
root.appendChild(message);
|
|
250
|
+
root.appendChild(actions);
|
|
251
|
+
document.body.appendChild(root);
|
|
252
|
+
window.requestAnimationFrame(function() {
|
|
253
|
+
root.classList.add("is-visible");
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
if (document.readyState === "loading") {
|
|
258
|
+
document.addEventListener("DOMContentLoaded", initCookieConsent, { once: true });
|
|
259
|
+
} else {
|
|
260
|
+
initCookieConsent();
|
|
261
|
+
}
|
|
49
262
|
})();
|
|
50
263
|
`,
|
|
51
264
|
}}
|
|
@@ -55,26 +268,12 @@ class CookieConsent extends Component {
|
|
|
55
268
|
}
|
|
56
269
|
|
|
57
270
|
CookieConsent.Cacheable = cacheComponent(CookieConsent, "plugin.cookieconsent", (props) => {
|
|
58
|
-
const { head, plugin
|
|
59
|
-
const { type = "info", theme = "edgeless", position = "bottom-left", policyLink = "https://www.cookiesandyou.com/" } = plugin;
|
|
271
|
+
const { head, plugin } = props;
|
|
60
272
|
|
|
61
273
|
return {
|
|
62
274
|
head,
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
position,
|
|
66
|
-
policyLink,
|
|
67
|
-
static: plugin.static || false,
|
|
68
|
-
text: {
|
|
69
|
-
message: helper.__("plugin.cookie_consent.message"),
|
|
70
|
-
dismiss: helper.__("plugin.cookie_consent.dismiss"),
|
|
71
|
-
allow: helper.__("plugin.cookie_consent.allow"),
|
|
72
|
-
deny: helper.__("plugin.cookie_consent.deny"),
|
|
73
|
-
link: helper.__("plugin.cookie_consent.link"),
|
|
74
|
-
policy: helper.__("plugin.cookie_consent.policy"),
|
|
75
|
-
},
|
|
76
|
-
cssUrl: helper.cdn("cookieconsent", "3.1.1", "build/cookieconsent.min.css"),
|
|
77
|
-
jsUrl: helper.cdn("cookieconsent", "3.1.1", "build/cookieconsent.min.js"),
|
|
275
|
+
policyLink: plugin.policyLink || "https://www.cookiesandyou.com/",
|
|
276
|
+
isStatic: plugin.static || false,
|
|
78
277
|
};
|
|
79
278
|
});
|
|
80
279
|
|
package/layout/plugin/swup.jsx
CHANGED
|
@@ -52,7 +52,7 @@ Insight.Cacheable = cacheComponent(Insight, "search.insight", (props) => {
|
|
|
52
52
|
pages: helper._p("common.page", Infinity),
|
|
53
53
|
tags: helper._p("common.tag", Infinity),
|
|
54
54
|
},
|
|
55
|
-
contentUrl: helper.url_for("/content.json"),
|
|
55
|
+
contentUrl: helper.is_i18n_enabled() ? helper.localized_url_for("/content.json") : helper.url_for("/content.json"),
|
|
56
56
|
jsUrl: helper.url_for("/js/insight.js"),
|
|
57
57
|
};
|
|
58
58
|
});
|
package/layout/tag.jsx
CHANGED
|
@@ -48,7 +48,8 @@ const breadcrumb_css = `
|
|
|
48
48
|
module.exports = class extends Component {
|
|
49
49
|
render() {
|
|
50
50
|
const { config, page, helper } = this.props;
|
|
51
|
-
const {
|
|
51
|
+
const { _p } = helper;
|
|
52
|
+
const langKey = helper.language_key(page);
|
|
52
53
|
|
|
53
54
|
return (
|
|
54
55
|
<Fragment>
|
|
@@ -57,7 +58,7 @@ module.exports = class extends Component {
|
|
|
57
58
|
<ul>
|
|
58
59
|
<li>
|
|
59
60
|
<font style="color: var(--green)">$</font> ls
|
|
60
|
-
<a href={
|
|
61
|
+
<a href={helper.localized_url_for("/tags/", langKey)}>{_p("common.tag", Infinity)}/</a>
|
|
61
62
|
</li>
|
|
62
63
|
<li>
|
|
63
64
|
<a href="#" aria-current="page" style="cursor: default; pointer-events: none; color: var(--mauve);">
|