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.
Files changed (69) hide show
  1. package/README.md +5 -18
  2. package/asset/css/github-calendar.css +41 -0
  3. package/asset/js/algolia.js +18 -69
  4. package/asset/js/github-calendar.js +185 -0
  5. package/asset/js/google_cse.js +19 -68
  6. package/asset/js/insight.js +45 -208
  7. package/lib/core/commented_yaml.js +35 -0
  8. package/lib/core/default_value.js +150 -0
  9. package/lib/core/schema.js +8 -196
  10. package/lib/core/schema_util.js +14 -0
  11. package/lib/hexo/helper/cdn.js +10 -97
  12. package/lib/hexo/tag/tabs.js +4 -84
  13. package/lib/schema/comment/artalk.json +7 -1
  14. package/lib/schema/comment/facebook.json +1 -1
  15. package/lib/schema/comment/gitalk.json +23 -6
  16. package/lib/schema/comment/gitment.json +1 -1
  17. package/lib/schema/comment/twikoo.json +3 -3
  18. package/lib/schema/comment/waline.json +28 -3
  19. package/lib/schema/misc/open_graph.json +2 -1
  20. package/lib/schema/plugin/cookie_consent.json +2 -2
  21. package/lib/schema/plugin/mathjax.json +1 -1
  22. package/lib/schema/plugin/outdated_browser.json +1 -1
  23. package/lib/schema/share/bdshare.json +1 -1
  24. package/lib/schema/share/sharejs.json +1 -1
  25. package/lib/schema/share/web_share.json +13 -0
  26. package/lib/schema/widget/github_calendar.json +30 -0
  27. package/lib/schema/widget/profile.json +67 -0
  28. package/lib/util/archives.js +86 -0
  29. package/lib/util/categories.js +102 -0
  30. package/lib/util/cdn-rules.js +109 -0
  31. package/lib/util/cdn.js +116 -0
  32. package/lib/util/consent.js +122 -0
  33. package/lib/util/deprecate.js +1 -1
  34. package/lib/util/gallery.js +227 -0
  35. package/lib/util/insight-search.js +98 -0
  36. package/lib/util/searchbox.js +88 -0
  37. package/lib/util/tab-options.js +109 -0
  38. package/lib/view/comment/artalk.js +24 -6
  39. package/lib/view/comment/disqus.js +1 -1
  40. package/lib/view/comment/disqusjs.js +7 -6
  41. package/lib/view/comment/facebook.js +3 -16
  42. package/lib/view/comment/gitalk.js +18 -15
  43. package/lib/view/comment/gitment.js +2 -2
  44. package/lib/view/comment/twikoo.js +5 -3
  45. package/lib/view/comment/valine.js +4 -3
  46. package/lib/view/comment/waline.js +8 -6
  47. package/lib/view/misc/open_graph.js +3 -5
  48. package/lib/view/plugin/cookie_consent.js +8 -7
  49. package/lib/view/plugin/gallery.js +42 -19
  50. package/lib/view/plugin/katex.js +4 -4
  51. package/lib/view/plugin/mathjax.js +10 -7
  52. package/lib/view/plugin/outdated_browser.js +59 -45
  53. package/lib/view/plugin/progressbar.js +6 -1
  54. package/lib/view/plugin/waline_pageview.js +2 -2
  55. package/lib/view/search/algolia.js +8 -3
  56. package/lib/view/search/google_cse.js +13 -10
  57. package/lib/view/search/insight.js +9 -2
  58. package/lib/view/share/addthis.js +4 -9
  59. package/lib/view/share/addtoany.js +23 -5
  60. package/lib/view/share/bdshare.js +9 -30
  61. package/lib/view/share/sharejs.js +7 -34
  62. package/lib/view/share/sharethis.js +21 -6
  63. package/lib/view/share/web_share.js +153 -0
  64. package/lib/view/widget/archives.js +19 -71
  65. package/lib/view/widget/categories.js +20 -67
  66. package/lib/view/widget/github_calendar.js +94 -0
  67. package/lib/view/widget/profile.js +194 -0
  68. package/lib/view/widget/subscribe_email.js +4 -26
  69. 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>&lt;!-- tab ... --&gt;</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 darkModeOption = darkMode === null || typeof darkMode === 'undefined' ? "".concat(getAppearance(), " === 'auto' ? 'auto' : ").concat(isDark()) : toJs(darkMode);
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
- jsUrl: helper.cdn('artalk', '2.9.1', 'dist/Artalk.js'),
116
- cssUrl: helper.cdn('artalk', '2.9.1', 'dist/Artalk.css')
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'); \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 })();");
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/disqus.js"
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": "disqus_thread"
86
+ "id": "disqusjs"
86
87
  }), (0, _inferno.createVNode)(1, "script", null, null, 1, {
87
88
  "dangerouslySetInnerHTML": {
88
- __html: loadScripts(jsUrl, js)
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', '1.3.0', 'dist/disqus.js'),
138
- cssUrl: helper.cdn('disqusjs', '1.3.0', 'dist/disqusjs.css')
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;