hexo-component-inferno 4.2.2 → 5.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 +5 -18
- package/asset/css/github-calendar.css +41 -0
- package/asset/js/algolia.js +18 -69
- package/asset/js/github-calendar.js +185 -0
- package/asset/js/google_cse.js +19 -68
- package/asset/js/insight.js +45 -208
- package/lib/core/commented_yaml.js +35 -0
- package/lib/core/default_value.js +150 -0
- package/lib/core/schema.js +8 -196
- package/lib/core/schema_util.js +14 -0
- package/lib/hexo/helper/cdn.js +10 -97
- package/lib/hexo/tag/tabs.js +4 -84
- package/lib/schema/comment/artalk.json +7 -1
- package/lib/schema/comment/facebook.json +1 -1
- package/lib/schema/comment/gitalk.json +23 -6
- package/lib/schema/comment/gitment.json +1 -1
- package/lib/schema/comment/twikoo.json +3 -3
- package/lib/schema/comment/waline.json +28 -3
- package/lib/schema/misc/open_graph.json +2 -1
- package/lib/schema/plugin/cookie_consent.json +2 -2
- package/lib/schema/plugin/mathjax.json +1 -1
- package/lib/schema/plugin/outdated_browser.json +1 -1
- package/lib/schema/share/bdshare.json +1 -1
- package/lib/schema/share/sharejs.json +1 -1
- package/lib/schema/share/web_share.json +13 -0
- package/lib/schema/widget/github_calendar.json +30 -0
- package/lib/schema/widget/profile.json +67 -0
- package/lib/util/archives.js +86 -0
- package/lib/util/categories.js +102 -0
- package/lib/util/cdn-rules.js +109 -0
- package/lib/util/cdn.js +116 -0
- package/lib/util/consent.js +122 -0
- package/lib/util/deprecate.js +1 -1
- package/lib/util/gallery.js +227 -0
- package/lib/util/insight-search.js +98 -0
- package/lib/util/searchbox.js +88 -0
- package/lib/util/tab-options.js +109 -0
- package/lib/view/comment/artalk.js +24 -6
- package/lib/view/comment/disqus.js +1 -1
- package/lib/view/comment/disqusjs.js +7 -6
- package/lib/view/comment/facebook.js +3 -16
- package/lib/view/comment/gitalk.js +18 -15
- package/lib/view/comment/gitment.js +2 -2
- package/lib/view/comment/twikoo.js +5 -3
- package/lib/view/comment/valine.js +4 -3
- package/lib/view/comment/waline.js +8 -6
- package/lib/view/misc/open_graph.js +3 -5
- package/lib/view/plugin/cookie_consent.js +8 -7
- package/lib/view/plugin/gallery.js +42 -19
- package/lib/view/plugin/katex.js +4 -4
- package/lib/view/plugin/mathjax.js +10 -7
- package/lib/view/plugin/outdated_browser.js +59 -45
- package/lib/view/plugin/progressbar.js +6 -1
- package/lib/view/plugin/waline_pageview.js +2 -2
- package/lib/view/search/algolia.js +8 -3
- package/lib/view/search/google_cse.js +13 -10
- package/lib/view/search/insight.js +9 -2
- package/lib/view/share/addthis.js +4 -9
- package/lib/view/share/addtoany.js +23 -5
- package/lib/view/share/bdshare.js +9 -30
- package/lib/view/share/sharejs.js +7 -34
- package/lib/view/share/sharethis.js +21 -6
- package/lib/view/share/web_share.js +153 -0
- package/lib/view/widget/archives.js +19 -71
- package/lib/view/widget/categories.js +20 -67
- package/lib/view/widget/github_calendar.js +94 -0
- package/lib/view/widget/profile.js +194 -0
- package/lib/view/widget/subscribe_email.js +4 -26
- package/package.json +6 -4
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Inline Gallery installer shared by the standalone component and consuming themes.
|
|
3
|
+
* Installing it does not enable or download either SDK. Keep it self-contained after Babel so it
|
|
4
|
+
* can also be used without the Hexo asset generator.
|
|
5
|
+
* @module util/gallery
|
|
6
|
+
*/
|
|
7
|
+
function installGallery(window) {
|
|
8
|
+
if (window.HexoGallery) return;
|
|
9
|
+
const document = window.document;
|
|
10
|
+
const requests = new Map();
|
|
11
|
+
const layouts = new Map();
|
|
12
|
+
const lightboxes = new Map();
|
|
13
|
+
function destroyLightbox(container) {
|
|
14
|
+
const state = lightboxes.get(container);
|
|
15
|
+
if (!state) return;
|
|
16
|
+
container.removeEventListener('click', state.click);
|
|
17
|
+
// The SDK removes its container through parentNode, even after a host replaced the body.
|
|
18
|
+
const dialog = state.instance.$container && state.instance.$container.get();
|
|
19
|
+
if (dialog && !dialog.parentNode) {
|
|
20
|
+
dialog.style.display = 'none';
|
|
21
|
+
document.body.appendChild(dialog);
|
|
22
|
+
}
|
|
23
|
+
if (state.instance.lgOpened) {
|
|
24
|
+
state.instance.settings.startAnimationDuration = 0;
|
|
25
|
+
state.instance.settings.backdropDuration = 0;
|
|
26
|
+
}
|
|
27
|
+
state.instance.destroy();
|
|
28
|
+
lightboxes.delete(container);
|
|
29
|
+
}
|
|
30
|
+
function lightbox(container, enabled) {
|
|
31
|
+
if (!enabled) {
|
|
32
|
+
destroyLightbox(container);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
if (typeof window.lightGallery !== 'function') return;
|
|
36
|
+
const plugins = (window.HexoGallery.lightGalleryPlugins || []).map(name => window[name]);
|
|
37
|
+
if (plugins.some(plugin => typeof plugin !== 'function')) return;
|
|
38
|
+
const items = Array.from(container.querySelectorAll('a.gallery-item[href]'));
|
|
39
|
+
if (!items.length) {
|
|
40
|
+
destroyLightbox(container);
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
const elements = items.map(link => {
|
|
44
|
+
const image = link.querySelector('img');
|
|
45
|
+
const caption = document.createElement('span');
|
|
46
|
+
caption.textContent = image ? image.alt : '';
|
|
47
|
+
return {
|
|
48
|
+
src: link.href,
|
|
49
|
+
thumb: image ? image.currentSrc || image.src : link.href,
|
|
50
|
+
subHtml: caption.innerHTML,
|
|
51
|
+
alt: caption.innerHTML.replace(/"/g, '"')
|
|
52
|
+
};
|
|
53
|
+
});
|
|
54
|
+
const signature = JSON.stringify(elements);
|
|
55
|
+
let state = lightboxes.get(container);
|
|
56
|
+
if (state) {
|
|
57
|
+
if (!sameItems(state.items, items) || state.signature !== signature) {
|
|
58
|
+
if (state.instance.lgOpened) {
|
|
59
|
+
state.instance.updateSlides(elements, Math.min(state.instance.index, elements.length - 1));
|
|
60
|
+
} else {
|
|
61
|
+
state.instance.refresh(elements);
|
|
62
|
+
}
|
|
63
|
+
state.items = items;
|
|
64
|
+
state.signature = signature;
|
|
65
|
+
}
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
const instance = window.lightGallery(container, {
|
|
69
|
+
dynamic: true,
|
|
70
|
+
dynamicEl: elements,
|
|
71
|
+
plugins
|
|
72
|
+
});
|
|
73
|
+
state = {
|
|
74
|
+
instance,
|
|
75
|
+
items,
|
|
76
|
+
signature,
|
|
77
|
+
click: null
|
|
78
|
+
};
|
|
79
|
+
state.click = event => {
|
|
80
|
+
if (event.defaultPrevented || event.button !== 0 || event.ctrlKey || event.metaKey || event.shiftKey || event.altKey) return;
|
|
81
|
+
const link = event.target.closest('a.gallery-item');
|
|
82
|
+
const index = state.items.indexOf(link);
|
|
83
|
+
if (index < 0) return;
|
|
84
|
+
event.preventDefault();
|
|
85
|
+
instance.openGallery(index, link);
|
|
86
|
+
};
|
|
87
|
+
container.addEventListener('click', state.click);
|
|
88
|
+
lightboxes.set(container, state);
|
|
89
|
+
}
|
|
90
|
+
function sameItems(items, current) {
|
|
91
|
+
return items.length === current.length && Array.from(items).every((item, i) => item === current[i]);
|
|
92
|
+
}
|
|
93
|
+
function enhance(container) {
|
|
94
|
+
container.querySelectorAll('img:not(.not-gallery-item)').forEach(image => {
|
|
95
|
+
// Third-party SDK icons are controls, not article images. Script-load retries also
|
|
96
|
+
// scan icons inserted after the article's initial enhancement.
|
|
97
|
+
if (image.closest('a, .sharethis-inline-share-buttons, .a2a_kit, [data-share-provider]')) return;
|
|
98
|
+
const link = document.createElement('a');
|
|
99
|
+
link.className = 'gallery-item';
|
|
100
|
+
const src = image.getAttribute('src');
|
|
101
|
+
if (src !== null) link.setAttribute('href', src);
|
|
102
|
+
image.parentNode.insertBefore(link, image);
|
|
103
|
+
link.appendChild(image);
|
|
104
|
+
if (image.alt) {
|
|
105
|
+
const caption = document.createElement('p');
|
|
106
|
+
caption.className = 'has-text-centered is-size-6 caption';
|
|
107
|
+
caption.textContent = image.alt;
|
|
108
|
+
link.appendChild(caption);
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
function apply(root, options) {
|
|
113
|
+
const containers = root === document ? root.querySelectorAll('.article') : [root];
|
|
114
|
+
containers.forEach(enhance);
|
|
115
|
+
// Do not capture an incomplete article while HTML is still being parsed.
|
|
116
|
+
if (document.readyState === 'loading') return;
|
|
117
|
+
containers.forEach(container => lightbox(container, options.lightGallery));
|
|
118
|
+
if (options.justifiedGallery && typeof window.JustifiedGallery === 'function') {
|
|
119
|
+
const galleries = Array.from(root.querySelectorAll('.justified-gallery'));
|
|
120
|
+
if (root !== document && root.matches('.justified-gallery')) galleries.unshift(root);
|
|
121
|
+
galleries.forEach(gallery => {
|
|
122
|
+
const paragraphs = new Set(Array.from(gallery.querySelectorAll(':scope > p > .gallery-item'), link => link.parentNode));
|
|
123
|
+
paragraphs.forEach(paragraph => {
|
|
124
|
+
paragraph.replaceWith(...paragraph.childNodes);
|
|
125
|
+
});
|
|
126
|
+
gallery.querySelectorAll(':scope > .gallery-item > .caption').forEach(caption => {
|
|
127
|
+
caption.classList.add('jg-caption');
|
|
128
|
+
});
|
|
129
|
+
let state = layouts.get(gallery);
|
|
130
|
+
const items = () => Array.from(gallery.children).filter(item => !item.classList.contains('jg-spinner') && (item.matches(state.instance.settings.selector) || item.matches('div')));
|
|
131
|
+
if (state) {
|
|
132
|
+
if (sameItems(state.items, items())) return;
|
|
133
|
+
// init refreshes entries but starts another width watcher; stop and rewind first.
|
|
134
|
+
state.instance.widthWatcher.stop();
|
|
135
|
+
state.instance.stopImgAnalyzerStarter();
|
|
136
|
+
state.instance.rewind();
|
|
137
|
+
} else {
|
|
138
|
+
// The SDK reuses .jg-caption and safely creates captions for pre-existing links.
|
|
139
|
+
state = {
|
|
140
|
+
instance: new window.JustifiedGallery(gallery)
|
|
141
|
+
};
|
|
142
|
+
layouts.set(gallery, state);
|
|
143
|
+
}
|
|
144
|
+
state.instance.init();
|
|
145
|
+
state.items = items();
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
function prune() {
|
|
150
|
+
layouts.forEach((state, gallery) => {
|
|
151
|
+
if (!gallery.isConnected) destroyLayout(gallery);
|
|
152
|
+
});
|
|
153
|
+
lightboxes.forEach((state, container) => {
|
|
154
|
+
if (!container.isConnected) destroyLightbox(container);
|
|
155
|
+
});
|
|
156
|
+
requests.forEach((options, root) => {
|
|
157
|
+
if (root !== document && !root.isConnected) requests.delete(root);
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
function destroyLayout(gallery) {
|
|
161
|
+
const {
|
|
162
|
+
instance
|
|
163
|
+
} = layouts.get(gallery);
|
|
164
|
+
// The SDK's destroy does not stop an active loading spinner.
|
|
165
|
+
instance.stopLoadingSpinnerAnimation();
|
|
166
|
+
instance.destroy();
|
|
167
|
+
layouts.delete(gallery);
|
|
168
|
+
}
|
|
169
|
+
function retry() {
|
|
170
|
+
prune();
|
|
171
|
+
requests.forEach((options, root) => apply(root, options));
|
|
172
|
+
}
|
|
173
|
+
window.HexoGallery = {
|
|
174
|
+
/**
|
|
175
|
+
* Enhance images in a content container, optionally initializing the two Gallery SDKs.
|
|
176
|
+
* Repeated calls reuse existing wrappers, captions and plugin instances. Missing SDKs can be
|
|
177
|
+
* retried by calling init again; DOM ready, window load and subsequent script loads also retry
|
|
178
|
+
* connected containers with their latest options. SDK initialization waits for DOM readiness.
|
|
179
|
+
* Changed item collections refresh their existing SDK instance. Disabling the lightbox destroys
|
|
180
|
+
* it; the independently managed layout plugin is unchanged.
|
|
181
|
+
* @param {Document|Element} [root=document] Content container; document targets .article images
|
|
182
|
+
* and lightboxes, and all .justified-gallery elements.
|
|
183
|
+
* @param {Object} [options={}] SDK opt-ins; omitted flags disable plugin initialization.
|
|
184
|
+
* @param {boolean} [options.lightGallery=false] Initialize lightGallery when available.
|
|
185
|
+
* @param {boolean} [options.justifiedGallery=false] Initialize justifiedGallery when available.
|
|
186
|
+
*/
|
|
187
|
+
init: function (root, options) {
|
|
188
|
+
root = root || document;
|
|
189
|
+
options = options || {};
|
|
190
|
+
const enabled = {
|
|
191
|
+
lightGallery: options.lightGallery === true,
|
|
192
|
+
justifiedGallery: options.justifiedGallery === true
|
|
193
|
+
};
|
|
194
|
+
prune();
|
|
195
|
+
requests.set(root, enabled);
|
|
196
|
+
apply(root, enabled);
|
|
197
|
+
},
|
|
198
|
+
/** Destroy lightboxes, layouts and pending retries before content is replaced (e.g. PJAX). */
|
|
199
|
+
destroy: function (root) {
|
|
200
|
+
root = root || document;
|
|
201
|
+
lightboxes.forEach((state, container) => {
|
|
202
|
+
if (root === document || root === container || root.contains(container)) destroyLightbox(container);
|
|
203
|
+
});
|
|
204
|
+
layouts.forEach((state, gallery) => {
|
|
205
|
+
if (root === document || root === gallery || root.contains(gallery)) destroyLayout(gallery);
|
|
206
|
+
});
|
|
207
|
+
requests.forEach((options, target) => {
|
|
208
|
+
if (root === document || root === target || root.contains(target)) requests.delete(target);
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
};
|
|
212
|
+
if (document.readyState === 'loading') {
|
|
213
|
+
document.addEventListener('DOMContentLoaded', retry, {
|
|
214
|
+
once: true
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
if (document.readyState !== 'complete') window.addEventListener('load', retry, {
|
|
218
|
+
once: true
|
|
219
|
+
});
|
|
220
|
+
document.addEventListener('hexo:gallery:ready', retry);
|
|
221
|
+
// Deferred scripts may arrive after init, including on the first enabled page reached by PJAX.
|
|
222
|
+
// Retry the latest requests, rather than capturing a previous page's enabled configuration.
|
|
223
|
+
document.addEventListener('load', event => {
|
|
224
|
+
if (event.target.tagName === 'SCRIPT') retry();
|
|
225
|
+
}, true);
|
|
226
|
+
}
|
|
227
|
+
module.exports = '(' + installGallery.toString() + ')(window);';
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Create the DOM-independent Insight query engine.
|
|
3
|
+
* Keep the factory self-contained: the search view serializes it into an inline script, including
|
|
4
|
+
* when used without the Hexo asset generator or with a CDN-hosted insight.js.
|
|
5
|
+
* @module util/insight-search
|
|
6
|
+
* @returns {Function} Search (index, query), returning parsed keywords and ranked result sections.
|
|
7
|
+
*/
|
|
8
|
+
function createInsightSearch() {
|
|
9
|
+
const content = {
|
|
10
|
+
fields: ['title', 'text'],
|
|
11
|
+
weights: [3, 1]
|
|
12
|
+
};
|
|
13
|
+
const taxonomy = {
|
|
14
|
+
fields: ['name', 'slug'],
|
|
15
|
+
weights: [1, 1]
|
|
16
|
+
};
|
|
17
|
+
const sections = {
|
|
18
|
+
posts: content,
|
|
19
|
+
pages: content,
|
|
20
|
+
categories: taxonomy,
|
|
21
|
+
tags: taxonomy
|
|
22
|
+
};
|
|
23
|
+
function parseKeywords(query) {
|
|
24
|
+
return query.split(' ').filter(keyword => {
|
|
25
|
+
return !!keyword;
|
|
26
|
+
}).map(keyword => {
|
|
27
|
+
return keyword.toLowerCase();
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Every keyword must occur in at least one own field; keywords may span different fields.
|
|
32
|
+
function filter(keywords, obj, fields) {
|
|
33
|
+
const containKeywords = keywords.filter(keyword => {
|
|
34
|
+
const containFields = fields.filter(field => {
|
|
35
|
+
if (!Object.prototype.hasOwnProperty.call(obj, field)) {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
if (obj[field].toLowerCase().indexOf(keyword) > -1) {
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
return false;
|
|
42
|
+
});
|
|
43
|
+
if (containFields.length > 0) {
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
return false;
|
|
47
|
+
});
|
|
48
|
+
return containKeywords.length === keywords.length;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Count non-overlapping occurrences of a literal lowercase keyword, not a regular expression.
|
|
52
|
+
function countMatches(text, keyword) {
|
|
53
|
+
let count = 0;
|
|
54
|
+
for (let i = text.indexOf(keyword); i !== -1; i = text.indexOf(keyword, i + keyword.length)) {
|
|
55
|
+
count++;
|
|
56
|
+
}
|
|
57
|
+
return count;
|
|
58
|
+
}
|
|
59
|
+
function weight(keywords, obj, fields, weights) {
|
|
60
|
+
let value = 0;
|
|
61
|
+
keywords.forEach(keyword => {
|
|
62
|
+
fields.forEach((field, index) => {
|
|
63
|
+
if (Object.prototype.hasOwnProperty.call(obj, field)) {
|
|
64
|
+
value += countMatches(obj[field].toLowerCase(), keyword) * weights[index];
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
return value;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Weigh each matching item once. Equal weights retain the input order, including an empty query.
|
|
72
|
+
function topItems(items, keywords, config) {
|
|
73
|
+
return items.filter(item => {
|
|
74
|
+
return filter(keywords, item, config.fields);
|
|
75
|
+
}).map(item => {
|
|
76
|
+
return {
|
|
77
|
+
item,
|
|
78
|
+
weight: weight(keywords, item, config.fields, config.weights)
|
|
79
|
+
};
|
|
80
|
+
}).sort((a, b) => {
|
|
81
|
+
return b.weight - a.weight;
|
|
82
|
+
}).slice(0, 5).map(result => {
|
|
83
|
+
return result.item;
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
return function search(json, query) {
|
|
87
|
+
const keywords = parseKeywords(query);
|
|
88
|
+
const results = {};
|
|
89
|
+
Object.keys(sections).forEach(section => {
|
|
90
|
+
results[section] = topItems(json[section], keywords, sections[section]);
|
|
91
|
+
});
|
|
92
|
+
return {
|
|
93
|
+
keywords,
|
|
94
|
+
results
|
|
95
|
+
};
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
module.exports = createInsightSearch;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Inline installer shared by the search components. Keep it self-contained: components can be used
|
|
3
|
+
* without the Hexo asset generator, and PJAX may execute this script more than once.
|
|
4
|
+
* @module util/searchbox
|
|
5
|
+
*/
|
|
6
|
+
function installSearchbox(window) {
|
|
7
|
+
if (window.loadSearchbox) return;
|
|
8
|
+
const document = window.document;
|
|
9
|
+
let current;
|
|
10
|
+
window.loadSearchbox = function ($main, options) {
|
|
11
|
+
const $ = window.jQuery;
|
|
12
|
+
options = options || {};
|
|
13
|
+
// Preserve focus state only when replacing the controller for the same open DOM element.
|
|
14
|
+
let opener = current ? current.destroy($main[0]) : null;
|
|
15
|
+
let touch = false;
|
|
16
|
+
const $document = $(document);
|
|
17
|
+
const namespace = '.hexoSearchbox';
|
|
18
|
+
const controller = {
|
|
19
|
+
isCurrent: function () {
|
|
20
|
+
return current === controller && document.contains($main[0]);
|
|
21
|
+
},
|
|
22
|
+
destroy: function (nextMain) {
|
|
23
|
+
if (current !== controller) return null;
|
|
24
|
+
const nextOpener = nextMain === $main[0] && $main.hasClass('show') ? opener : null;
|
|
25
|
+
$document.off(namespace);
|
|
26
|
+
opener = null;
|
|
27
|
+
current = null;
|
|
28
|
+
if (options.onDestroy) options.onDestroy();
|
|
29
|
+
return nextOpener;
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
current = controller;
|
|
33
|
+
function open() {
|
|
34
|
+
if (!controller.isCurrent()) return;
|
|
35
|
+
if (!$main.hasClass('show')) {
|
|
36
|
+
opener = document.activeElement;
|
|
37
|
+
$main.addClass('show');
|
|
38
|
+
}
|
|
39
|
+
$main.find('.searchbox-input').focus();
|
|
40
|
+
}
|
|
41
|
+
function close(restoreFocus) {
|
|
42
|
+
$main.removeClass('show');
|
|
43
|
+
if (restoreFocus !== false && opener && document.contains(opener)) opener.focus();
|
|
44
|
+
opener = null;
|
|
45
|
+
}
|
|
46
|
+
function isSearchShortcut(e) {
|
|
47
|
+
if (e.altKey) return false;
|
|
48
|
+
if (e.ctrlKey || e.metaKey) {
|
|
49
|
+
return !e.shiftKey && typeof e.key === 'string' && e.key.toLowerCase() === 'k';
|
|
50
|
+
}
|
|
51
|
+
// e.key already accounts for the keyboard layout (including Shift).
|
|
52
|
+
const target = e.target;
|
|
53
|
+
const typing = target.isContentEditable || /^(input|textarea|select)$/i.test(target.tagName);
|
|
54
|
+
return e.key === '/' && !typing;
|
|
55
|
+
}
|
|
56
|
+
$document.on('click' + namespace, '.navbar-main .search', open).on('click' + namespace + (options.touch ? ' touchend' + namespace : ''), '.searchbox-close, .searchbox .searchbox-mask', e => {
|
|
57
|
+
if (!controller.isCurrent()) return;
|
|
58
|
+
if (e.type !== 'click' && !touch) return;
|
|
59
|
+
if (options.touch) {
|
|
60
|
+
$('.navbar-main').css('pointer-events', 'none');
|
|
61
|
+
setTimeout(() => {
|
|
62
|
+
$('.navbar-main').css('pointer-events', 'auto');
|
|
63
|
+
}, 400);
|
|
64
|
+
}
|
|
65
|
+
close();
|
|
66
|
+
touch = false;
|
|
67
|
+
}).on('focusin' + namespace, e => {
|
|
68
|
+
if (controller.isCurrent() && $main.hasClass('show') && !$main[0].contains(e.target)) {
|
|
69
|
+
close(false);
|
|
70
|
+
}
|
|
71
|
+
}).on('keydown' + namespace, e => {
|
|
72
|
+
if (!controller.isCurrent()) return;
|
|
73
|
+
if (isSearchShortcut(e)) {
|
|
74
|
+
e.preventDefault();
|
|
75
|
+
open();
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
if (!$main.hasClass('show')) return;
|
|
79
|
+
if (e.keyCode === 27) close();else if (options.onKeyDown) options.onKeyDown(e);
|
|
80
|
+
}).on('touchstart' + namespace, () => {
|
|
81
|
+
touch = true;
|
|
82
|
+
}).on('touchmove' + namespace, () => {
|
|
83
|
+
touch = false;
|
|
84
|
+
});
|
|
85
|
+
return controller;
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
module.exports = '(' + installSearchbox.toString() + ')(window);';
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure option splitting for the Hexo tabs tag and its individual tab comments.
|
|
3
|
+
* @module util/tab-options
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Split a tabs option at its first colon, without trimming or interpreting the value.
|
|
8
|
+
* @param {string} option An option such as <code>title:Step 1: Install</code>.
|
|
9
|
+
* @returns {string[]} The key and value, or only the key when there is no colon.
|
|
10
|
+
*/
|
|
11
|
+
function splitOption(option) {
|
|
12
|
+
const index = option.indexOf(':');
|
|
13
|
+
return index === -1 ? [option] : [option.slice(0, index), option.slice(index + 1)];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Split individual tab comment options on ASCII spaces, respecting leading single or double quotes.
|
|
18
|
+
* Quotes within bare tokens are literal. Backslashes inside quotes protect the next character from
|
|
19
|
+
* closing the token, but remain in the result; they do not escape spaces in bare tokens.
|
|
20
|
+
* @param {string} source The option text inside a <code><!-- tab ... --></code> comment.
|
|
21
|
+
* @returns {string[]} Options with enclosing quotes removed and all other characters preserved.
|
|
22
|
+
* @throws {Error} For an unclosed quoted option or a non-space character after its closing quote.
|
|
23
|
+
*/
|
|
24
|
+
function splitTabOptions(source) {
|
|
25
|
+
const options = [];
|
|
26
|
+
const EOL = -1;
|
|
27
|
+
let i = 0;
|
|
28
|
+
let state = 1;
|
|
29
|
+
let begin = 0;
|
|
30
|
+
while (i <= source.length) {
|
|
31
|
+
const c = i < source.length ? source.charAt(i) : '';
|
|
32
|
+
switch (state) {
|
|
33
|
+
case 1:
|
|
34
|
+
// Start of an option
|
|
35
|
+
if (c === ' ') {
|
|
36
|
+
state = 1;
|
|
37
|
+
begin = i + 1;
|
|
38
|
+
} else if (c === '"') {
|
|
39
|
+
state = 2;
|
|
40
|
+
begin = i + 1;
|
|
41
|
+
} else if (c === "'") {
|
|
42
|
+
state = 4;
|
|
43
|
+
begin = i + 1;
|
|
44
|
+
} else if (c === '') {
|
|
45
|
+
state = EOL;
|
|
46
|
+
} else {
|
|
47
|
+
state = 7;
|
|
48
|
+
}
|
|
49
|
+
break;
|
|
50
|
+
case 2:
|
|
51
|
+
// Double-quoted option
|
|
52
|
+
if (c === '\\') {
|
|
53
|
+
state = 3;
|
|
54
|
+
} else if (c === '"') {
|
|
55
|
+
state = 6;
|
|
56
|
+
}
|
|
57
|
+
break;
|
|
58
|
+
case 3:
|
|
59
|
+
// Escaped character in a double-quoted option
|
|
60
|
+
state = 2;
|
|
61
|
+
break;
|
|
62
|
+
case 4:
|
|
63
|
+
// Single-quoted option
|
|
64
|
+
if (c === '\\') {
|
|
65
|
+
state = 5;
|
|
66
|
+
} else if (c === "'") {
|
|
67
|
+
state = 6;
|
|
68
|
+
}
|
|
69
|
+
break;
|
|
70
|
+
case 5:
|
|
71
|
+
// Escaped character in a single-quoted option
|
|
72
|
+
state = 4;
|
|
73
|
+
break;
|
|
74
|
+
case 6:
|
|
75
|
+
// The preceding character closed the quote
|
|
76
|
+
options.push(source.substring(begin, i - 1));
|
|
77
|
+
begin = i + 1;
|
|
78
|
+
if (c === ' ') {
|
|
79
|
+
state = 1;
|
|
80
|
+
} else if (c === '') {
|
|
81
|
+
state = EOL;
|
|
82
|
+
} else {
|
|
83
|
+
throw new Error(`malformatted tab options: ${source}`);
|
|
84
|
+
}
|
|
85
|
+
break;
|
|
86
|
+
case 7:
|
|
87
|
+
// Bare option
|
|
88
|
+
if (c === ' ') {
|
|
89
|
+
options.push(source.substring(begin, i));
|
|
90
|
+
begin = i + 1;
|
|
91
|
+
state = 1;
|
|
92
|
+
} else if (c === '') {
|
|
93
|
+
options.push(source.substring(begin, i));
|
|
94
|
+
begin = i + 1;
|
|
95
|
+
state = EOL;
|
|
96
|
+
}
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
99
|
+
i++;
|
|
100
|
+
}
|
|
101
|
+
if (state !== EOL) {
|
|
102
|
+
throw new Error(`malformatted tab options: ${source}`);
|
|
103
|
+
}
|
|
104
|
+
return options;
|
|
105
|
+
}
|
|
106
|
+
module.exports = {
|
|
107
|
+
splitOption,
|
|
108
|
+
splitTabOptions
|
|
109
|
+
};
|
|
@@ -59,15 +59,22 @@ var Artalk = /*#__PURE__*/function (_Component) {
|
|
|
59
59
|
pageTitle = _this$props.pageTitle,
|
|
60
60
|
locale = _this$props.locale,
|
|
61
61
|
darkMode = _this$props.darkMode,
|
|
62
|
+
preferRemoteConf = _this$props.preferRemoteConf,
|
|
62
63
|
jsUrl = _this$props.jsUrl,
|
|
63
|
-
cssUrl = _this$props.cssUrl
|
|
64
|
+
cssUrl = _this$props.cssUrl,
|
|
65
|
+
_this$props$localeUrl = _this$props.localeUrls,
|
|
66
|
+
localeUrls = _this$props$localeUrl === void 0 ? [] : _this$props$localeUrl;
|
|
64
67
|
if (!server) {
|
|
65
68
|
return (0, _inferno.createVNode)(1, "div", "notification is-danger", [(0, _inferno.createTextVNode)("You forgot to set the "), (0, _inferno.createVNode)(1, "code", null, "server", 16), (0, _inferno.createTextVNode)(" for Artalk. Please set it in"), (0, _inferno.createTextVNode)(' '), (0, _inferno.createVNode)(1, "code", null, "_config.yml", 16), (0, _inferno.createTextVNode)(".")], 0);
|
|
66
69
|
}
|
|
67
70
|
// Without the dark_mode option, follow the appearance of the page
|
|
68
|
-
var
|
|
71
|
+
var followAppearance = darkMode === null || typeof darkMode === 'undefined';
|
|
72
|
+
var darkModeOption = followAppearance ? "".concat(getAppearance(), " === 'auto' ? 'auto' : ").concat(isDark()) : toJs(darkMode);
|
|
69
73
|
// PJAX replaces the element of the previous instance, so destroy it before initializing again
|
|
70
|
-
var js = "if (window.__artalkInstance) {\n window.__artalkInstance.destroy();\n }\n window.__artalkInstance = Artalk.init({\n el: '#artalk-thread',\n server: ".concat(toJs(server), ",\n ").concat(site ? "site: ".concat(toJs(site), ",") : '', "\n ").concat(pageKey ? "pageKey: ".concat(toJs(pageKey), ",") : '', "\n ").concat(pageTitle ? "pageTitle: ".concat(toJs(pageTitle), ",") : '', "\n ").concat(locale ? "locale: ".concat(toJs(locale), ",") : '', "\n darkMode: ").concat(darkModeOption, ",\n });");
|
|
74
|
+
var js = "if (!container || !container.isConnected) return;\n if (window.__artalkInstance) {\n window.__artalkInstance.destroy();\n }\n window.__artalkInstance = Artalk.init({\n el: '#artalk-thread',\n server: ".concat(toJs(server), ",\n ").concat(site ? "site: ".concat(toJs(site), ",") : '', "\n ").concat(pageKey ? "pageKey: ".concat(toJs(pageKey), ",") : '', "\n ").concat(pageTitle ? "pageTitle: ".concat(toJs(pageTitle), ",") : '', "\n ").concat(locale ? "locale: ".concat(toJs(locale), ",") : '', "\n ").concat(typeof preferRemoteConf === 'boolean' ? "preferRemoteConf: ".concat(preferRemoteConf, ",") : '', "\n darkMode: ").concat(darkModeOption, ",\n });");
|
|
75
|
+
if (followAppearance) {
|
|
76
|
+
js += "\n var instance = window.__artalkInstance;\n var observer = new MutationObserver(function () {\n instance.update({ darkMode: ".concat(darkModeOption, " });\n });\n observer.observe(document.documentElement, { attributes: true, attributeFilter: ['data-appearance'] });\n instance.on('unmounted', function () { observer.disconnect(); });");
|
|
77
|
+
}
|
|
71
78
|
return (0, _inferno.createFragment)([(0, _inferno.createVNode)(1, "div", null, null, 1, {
|
|
72
79
|
"id": "artalk-thread"
|
|
73
80
|
}), (0, _inferno.createVNode)(1, "link", null, null, 1, {
|
|
@@ -77,7 +84,7 @@ var Artalk = /*#__PURE__*/function (_Component) {
|
|
|
77
84
|
"onload": "this.media='all'"
|
|
78
85
|
}), (0, _inferno.createVNode)(1, "script", null, null, 1, {
|
|
79
86
|
"dangerouslySetInnerHTML": {
|
|
80
|
-
__html: loadScripts(jsUrl, js)
|
|
87
|
+
__html: "(function () {\n var container = document.getElementById('artalk-thread');\n ".concat(loadScripts([jsUrl].concat(localeUrls), js), "\n })();")
|
|
81
88
|
}
|
|
82
89
|
})], 4);
|
|
83
90
|
}
|
|
@@ -105,6 +112,13 @@ Artalk.Cacheable = cacheComponent(Artalk, 'comment.artalk', function (props) {
|
|
|
105
112
|
var comment = props.comment,
|
|
106
113
|
helper = props.helper,
|
|
107
114
|
page = props.page;
|
|
115
|
+
// The full bundle includes en and zh-CN only. Other published dictionaries register themselves
|
|
116
|
+
// on window.ArtalkI18n before initialization. For auto, make every supported dictionary available.
|
|
117
|
+
var externalLocales = ['zh-TW', 'ja', 'ko', 'fr', 'ru', 'tr'];
|
|
118
|
+
var language = String(comment.locale || '').toLowerCase();
|
|
119
|
+
var localeNames = externalLocales.filter(function (name) {
|
|
120
|
+
return language === 'auto' || language === name.toLowerCase() || language.startsWith(name.toLowerCase() + '-');
|
|
121
|
+
});
|
|
108
122
|
return {
|
|
109
123
|
server: comment.server,
|
|
110
124
|
site: comment.site,
|
|
@@ -112,8 +126,12 @@ Artalk.Cacheable = cacheComponent(Artalk, 'comment.artalk', function (props) {
|
|
|
112
126
|
pageTitle: page.title,
|
|
113
127
|
locale: comment.locale,
|
|
114
128
|
darkMode: comment.dark_mode,
|
|
115
|
-
|
|
116
|
-
|
|
129
|
+
preferRemoteConf: comment.prefer_remote_conf,
|
|
130
|
+
jsUrl: helper.cdn('artalk', '2.10.0', 'dist/Artalk.js'),
|
|
131
|
+
cssUrl: helper.cdn('artalk', '2.10.0', 'dist/Artalk.css'),
|
|
132
|
+
localeUrls: localeNames.map(function (name) {
|
|
133
|
+
return helper.cdn('artalk', '2.10.0', "dist/i18n/".concat(name, ".js"));
|
|
134
|
+
})
|
|
117
135
|
};
|
|
118
136
|
});
|
|
119
137
|
module.exports = Artalk;
|
|
@@ -53,7 +53,7 @@ var Disqus = /*#__PURE__*/function (_Component) {
|
|
|
53
53
|
if (!shortname) {
|
|
54
54
|
return (0, _inferno.createVNode)(1, "div", "notification is-danger", [(0, _inferno.createTextVNode)("You forgot to set the "), (0, _inferno.createVNode)(1, "code", null, "shortname", 16), (0, _inferno.createTextVNode)(" for Disqus. Please set it in"), (0, _inferno.createTextVNode)(' '), (0, _inferno.createVNode)(1, "code", null, "_config.yml", 16), (0, _inferno.createTextVNode)(".")], 0);
|
|
55
55
|
}
|
|
56
|
-
var js = "var disqus_config = function () {\n this.page.url = ".concat(toJs(permalink), ";\n this.page.identifier = ").concat(toJs(disqusId || path), ";\n };\n (function() {\n var d = document, s = d.createElement('script');
|
|
56
|
+
var js = "var disqus_config = function () {\n this.page.url = ".concat(toJs(permalink), ";\n this.page.identifier = ").concat(toJs(disqusId || path), ";\n };\n (function() {\n var d = document, s = d.createElement('script');\n // Disqus parses computed link/background colors with a legacy RGB-only parser.\n // Serialize the current theme colors to sRGB without changing the rest of the page.\n var thread = d.getElementById('disqus_thread');\n var canvas = d.createElement('canvas');\n canvas.width = canvas.height = 1;\n var context = canvas.getContext('2d');\n function rgb(color) {\n context.clearRect(0, 0, 1, 1);\n context.fillStyle = color;\n context.fillRect(0, 0, 1, 1);\n var pixel = context.getImageData(0, 0, 1, 1).data;\n return 'rgba(' + pixel[0] + ',' + pixel[1] + ',' + pixel[2] + ',' + pixel[3] / 255 + ')';\n }\n var link = d.createElement('a');\n link.href = '#';\n thread.appendChild(link);\n var linkColor = rgb(getComputedStyle(link).color);\n thread.removeChild(link);\n var style = d.createElement('style');\n style.textContent = '#disqus_thread a { color: ' + linkColor + '; }';\n thread.parentNode.insertBefore(style, thread);\n for (var node = thread; node; node = node.parentElement) {\n var background = rgb(getComputedStyle(node).backgroundColor);\n if (background !== 'rgba(0,0,0,0)') {\n thread.style.backgroundColor = background;\n break;\n }\n }\n s.src = ").concat(toJs("//".concat(shortname, ".disqus.com/embed.js")), ";\n s.setAttribute('data-timestamp', +new Date());\n (d.head || d.body).appendChild(s);\n })();");
|
|
57
57
|
return (0, _inferno.createFragment)([(0, _inferno.createVNode)(1, "div", null, (0, _inferno.createVNode)(1, "noscript", null, [(0, _inferno.createTextVNode)("Please enable JavaScript to view the"), (0, _inferno.createTextVNode)(' '), (0, _inferno.createVNode)(1, "a", null, "comments powered by Disqus.", 16, {
|
|
58
58
|
"href": "//disqus.com/?ref_noscript"
|
|
59
59
|
})], 0), 2, {
|
|
@@ -42,7 +42,7 @@ var toJs = loadScripts.toJs;
|
|
|
42
42
|
* permalink="/page/permanent/path"
|
|
43
43
|
* pageTitle="******"
|
|
44
44
|
* siteTitle="******"
|
|
45
|
-
* jsUrl="/path/to/
|
|
45
|
+
* jsUrl="/path/to/disqusjs.es2015.umd.min.js"
|
|
46
46
|
* cssUrl="/path/to/disqusjs.css" />
|
|
47
47
|
*/
|
|
48
48
|
var DisqusJs = /*#__PURE__*/function (_Component) {
|
|
@@ -73,7 +73,8 @@ var DisqusJs = /*#__PURE__*/function (_Component) {
|
|
|
73
73
|
}
|
|
74
74
|
var url = permalink || path;
|
|
75
75
|
var identifier = disqusId || path;
|
|
76
|
-
var js = "new DisqusJS({\n shortname: ".concat(toJs(shortname), ",\n apikey: ").concat(toJs(apiKey), ",\n ").concat(siteTitle ? "siteName: ".concat(toJs(siteTitle), ",") : '', "\n ").concat(identifier ? "identifier: ".concat(toJs(identifier), ",") : '', "\n ").concat(url ? "url: ".concat(toJs(url), ",") : '', "\n ").concat(pageTitle ? "title: ".concat(toJs(pageTitle), ",") : '', "\n ").concat(api ? "api: ".concat(toJs(api), ",") : '', "\n ").concat(admin ? "admin: ".concat(toJs(admin), ",") : '', "\n ").concat(adminLabel ? "adminLabel: ".concat(toJs(adminLabel), ",") : '', "\n ").concat(nesting ? "nesting: ".concat(toJs(nesting), ",") : '', "\n });");
|
|
76
|
+
var js = "if (!container.isConnected) return;\n instance = new DisqusJS({\n shortname: ".concat(toJs(shortname), ",\n apikey: ").concat(toJs(apiKey), ",\n ").concat(siteTitle ? "siteName: ".concat(toJs(siteTitle), ",") : '', "\n ").concat(identifier ? "identifier: ".concat(toJs(identifier), ",") : '', "\n ").concat(url ? "url: ".concat(toJs(url), ",") : '', "\n ").concat(pageTitle ? "title: ".concat(toJs(pageTitle), ",") : '', "\n ").concat(api ? "api: ".concat(toJs(api), ",") : '', "\n ").concat(admin ? "admin: ".concat(toJs(admin), ",") : '', "\n ").concat(adminLabel ? "adminLabel: ".concat(toJs(adminLabel), ",") : '', "\n ").concat(nesting ? "nesting: ".concat(toJs(nesting), ",") : '', "\n });\n instance.render(container);");
|
|
77
|
+
var script = "(function () {\n var container = document.getElementById('disqusjs');\n var instance;\n function destroy() {\n document.removeEventListener('hexo:page:before-replace', destroy);\n if (instance) instance.destroy();\n }\n document.addEventListener('hexo:page:before-replace', destroy);\n ".concat(loadScripts(jsUrl, js), "\n })();");
|
|
77
78
|
return (0, _inferno.createFragment)([(0, _inferno.createVNode)(1, "link", null, null, 1, {
|
|
78
79
|
"rel": "stylesheet",
|
|
79
80
|
"href": cssUrl,
|
|
@@ -82,10 +83,10 @@ var DisqusJs = /*#__PURE__*/function (_Component) {
|
|
|
82
83
|
}), (0, _inferno.createVNode)(1, "div", null, (0, _inferno.createVNode)(1, "noscript", null, [(0, _inferno.createTextVNode)("Please enable JavaScript to view the"), (0, _inferno.createTextVNode)(' '), (0, _inferno.createVNode)(1, "a", null, "comments powered by Disqus.", 16, {
|
|
83
84
|
"href": "//disqus.com/?ref_noscript"
|
|
84
85
|
})], 0), 2, {
|
|
85
|
-
"id": "
|
|
86
|
+
"id": "disqusjs"
|
|
86
87
|
}), (0, _inferno.createVNode)(1, "script", null, null, 1, {
|
|
87
88
|
"dangerouslySetInnerHTML": {
|
|
88
|
-
__html:
|
|
89
|
+
__html: script
|
|
89
90
|
}
|
|
90
91
|
})], 4);
|
|
91
92
|
}
|
|
@@ -134,8 +135,8 @@ DisqusJs.Cacheable = cacheComponent(DisqusJs, 'comment.disqusjs', function (prop
|
|
|
134
135
|
permalink: page.permalink,
|
|
135
136
|
pageTitle: page.title,
|
|
136
137
|
siteTitle: config.title,
|
|
137
|
-
jsUrl: helper.cdn('disqusjs', '
|
|
138
|
-
cssUrl: helper.cdn('disqusjs', '
|
|
138
|
+
jsUrl: helper.cdn('disqusjs', '3.2.2', 'dist/browser/disqusjs.es2015.umd.min.js'),
|
|
139
|
+
cssUrl: helper.cdn('disqusjs', '3.2.2', 'dist/browser/styles/disqusjs.css')
|
|
139
140
|
};
|
|
140
141
|
});
|
|
141
142
|
module.exports = DisqusJs;
|