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
@@ -4,9 +4,43 @@
4
4
  */
5
5
  // eslint-disable-next-line no-unused-vars
6
6
  function loadInsight(config, translation) {
7
+ const search = window.searchInsight;
7
8
  const $main = $('.searchbox');
8
- const $input = $main.find('.searchbox-input');
9
- const $container = $main.find('.searchbox-body');
9
+ let $input, $container;
10
+ const controller = window.loadSearchbox($main, {
11
+ touch: true,
12
+ onDestroy: () => {
13
+ $(document).off('.insightSearch');
14
+ $input.off('.insightSearch');
15
+ },
16
+ onKeyDown: (e) => {
17
+ switch (e.keyCode) {
18
+ case 38: // UP
19
+ selectItemByDiff(-1);
20
+ break;
21
+ case 40: // DOWN
22
+ selectItemByDiff(1);
23
+ break;
24
+ case 13: // ENTER
25
+ gotoLink($container.find('.searchbox-result-item.active').eq(0));
26
+ break;
27
+ }
28
+ },
29
+ });
30
+
31
+ // Replacing the controller disposes the previous engine, which may remove its input widget.
32
+ // Both search views keep this header container; Insight owns a plain text input inside it.
33
+ $input = $main.find('.searchbox-input');
34
+ if (!$input.length) {
35
+ $input = $('<input>').attr({
36
+ type: 'text',
37
+ class: 'searchbox-input',
38
+ placeholder: translation.hint,
39
+ });
40
+ $main.find('.searchbox-header .searchbox-input-container').first().append($input);
41
+ }
42
+ $container = $main.find('.searchbox-body');
43
+ if ($main.hasClass('show')) $input.focus();
10
44
 
11
45
  function section(title) {
12
46
  return $('<section>').addClass('searchbox-result-section').append($('<header>').text(title));
@@ -145,136 +179,10 @@ function loadInsight(config, translation) {
145
179
  return section(sectionTitle).append($searchItems);
146
180
  }
147
181
 
148
- function parseKeywords(keywords) {
149
- return keywords
150
- .split(' ')
151
- .filter((keyword) => {
152
- return !!keyword;
153
- })
154
- .map((keyword) => {
155
- return keyword.toLowerCase();
156
- });
157
- }
158
-
159
- /**
160
- * Judge if a given post/page/category/tag contains all of the keywords.
161
- * @param Object obj Object to be weighted
162
- * @param Array<String> fields Object's fields to find matches
163
- */
164
- function filter(keywords, obj, fields) {
165
- const keywordArray = parseKeywords(keywords);
166
- const containKeywords = keywordArray.filter((keyword) => {
167
- const containFields = fields.filter((field) => {
168
- if (!Object.prototype.hasOwnProperty.call(obj, field)) {
169
- return false;
170
- }
171
- if (obj[field].toLowerCase().indexOf(keyword) > -1) {
172
- return true;
173
- }
174
- return false;
175
- });
176
- if (containFields.length > 0) {
177
- return true;
178
- }
179
- return false;
180
- });
181
- return containKeywords.length === keywordArray.length;
182
- }
183
-
184
- function filterFactory(keywords) {
185
- return {
186
- post: function (obj) {
187
- return filter(keywords, obj, ['title', 'text']);
188
- },
189
- page: function (obj) {
190
- return filter(keywords, obj, ['title', 'text']);
191
- },
192
- category: function (obj) {
193
- return filter(keywords, obj, ['name', 'slug']);
194
- },
195
- tag: function (obj) {
196
- return filter(keywords, obj, ['name', 'slug']);
197
- },
198
- };
199
- }
200
-
201
- /**
202
- * Calculate the weight of a matched post/page/category/tag.
203
- * @param Object obj Object to be weighted
204
- * @param Array<String> fields Object's fields to find matches
205
- * @param Array<Integer> weights Weight of every field
206
- */
207
- function weight(keywords, obj, fields, weights) {
208
- let value = 0;
209
- parseKeywords(keywords).forEach((keyword) => {
210
- fields.forEach((field, index) => {
211
- if (Object.prototype.hasOwnProperty.call(obj, field)) {
212
- value += countMatches(obj[field].toLowerCase(), keyword) * weights[index];
213
- }
214
- });
215
- });
216
- return value;
217
- }
218
-
219
- // Count the occurrences of a lowercase keyword, which is plain text, not a regular expression
220
- function countMatches(text, keyword) {
221
- let count = 0;
222
- for (let i = text.indexOf(keyword); i !== -1; i = text.indexOf(keyword, i + keyword.length)) {
223
- count++;
224
- }
225
- return count;
226
- }
227
-
228
- function weightFactory(keywords) {
229
- return {
230
- post: function (obj) {
231
- return weight(keywords, obj, ['title', 'text'], [3, 1]);
232
- },
233
- page: function (obj) {
234
- return weight(keywords, obj, ['title', 'text'], [3, 1]);
235
- },
236
- category: function (obj) {
237
- return weight(keywords, obj, ['name', 'slug'], [1, 1]);
238
- },
239
- tag: function (obj) {
240
- return weight(keywords, obj, ['name', 'slug'], [1, 1]);
241
- },
242
- };
243
- }
244
-
245
- // Weigh each matching item once, instead of in every comparison of the sort
246
- function topItems(items, filter, weigh) {
247
- return items
248
- .filter(filter)
249
- .map((item) => {
250
- return { item, weight: weigh(item) };
251
- })
252
- .sort((a, b) => {
253
- return b.weight - a.weight;
254
- })
255
- .slice(0, 5)
256
- .map((result) => {
257
- return result.item;
258
- });
259
- }
260
-
261
- function search(json, keywords) {
262
- const weights = weightFactory(keywords);
263
- const filters = filterFactory(keywords);
264
- return {
265
- posts: topItems(json.posts, filters.post, weights.post),
266
- pages: topItems(json.pages, filters.page, weights.page),
267
- categories: topItems(json.categories, filters.category, weights.category),
268
- tags: topItems(json.tags, filters.tag, weights.tag),
269
- };
270
- }
271
-
272
182
  function searchResultToDOM(keywords, searchResult) {
273
183
  $container.empty();
274
184
  for (const key in searchResult) {
275
- $container.append(
276
- sectionFactory(parseKeywords(keywords), key.toUpperCase(), searchResult[key]),
277
- );
185
+ $container.append(sectionFactory(keywords, key.toUpperCase(), searchResult[key]));
278
186
  }
279
187
  }
280
188
 
@@ -305,39 +213,6 @@ function loadInsight(config, translation) {
305
213
  scrollTo($($items[nextPosition]));
306
214
  }
307
215
 
308
- let opener = null;
309
-
310
- function open() {
311
- if (!$main.hasClass('show')) {
312
- opener = document.activeElement;
313
- $main.addClass('show');
314
- }
315
- $input.focus();
316
- }
317
-
318
- // Return the focus to the element that opened the search box, unless it moved elsewhere
319
- function close(restoreFocus = true) {
320
- $main.removeClass('show');
321
- if (restoreFocus && opener && document.contains(opener)) {
322
- opener.focus();
323
- }
324
- opener = null;
325
- }
326
-
327
- // Open the search box with "/" (unless typing in a field) or Ctrl/Cmd+K
328
- function isSearchShortcut(e) {
329
- if (e.altKey) {
330
- return false;
331
- }
332
- if (e.ctrlKey || e.metaKey) {
333
- return !e.shiftKey && typeof e.key === 'string' && e.key.toLowerCase() === 'k';
334
- }
335
- // e.key is "/" whatever keys type it on the keyboard layout, so do not check e.shiftKey
336
- const target = e.target;
337
- const typing = target.isContentEditable || /^(input|textarea|select)$/i.test(target.tagName);
338
- return e.key === '/' && !typing;
339
- }
340
-
341
216
  function gotoLink($item) {
342
217
  if ($item && $item.length) {
343
218
  location.href = $item.attr('href');
@@ -345,69 +220,31 @@ function loadInsight(config, translation) {
345
220
  }
346
221
 
347
222
  $.getJSON(config.contentUrl, (json) => {
223
+ if (!controller.isCurrent()) return;
348
224
  if (location.hash.trim() === '#insight-search') {
349
225
  $main.addClass('show');
350
226
  }
351
- $input.on('input', function () {
352
- const keywords = $(this).val();
353
- searchResultToDOM(keywords, search(json, keywords));
227
+ $input.on('input.insightSearch', function () {
228
+ const result = search(json, $(this).val());
229
+ searchResultToDOM(result.keywords, result.results);
354
230
  });
355
231
  $input.trigger('input');
356
232
  });
357
233
 
358
234
  let touch = false;
359
235
  $(document)
360
- .on('click', '.navbar-main .search', open)
361
- .on('click touchend', '.searchbox-result-item', function (e) {
236
+ .on('click.insightSearch touchend.insightSearch', '.searchbox-result-item', function (e) {
237
+ if (!controller.isCurrent()) return;
362
238
  if (e.type !== 'click' && !touch) {
363
239
  return;
364
240
  }
365
241
  gotoLink($(this));
366
242
  touch = false;
367
243
  })
368
- .on('click touchend', '.searchbox-close', (e) => {
369
- if (e.type !== 'click' && !touch) {
370
- return;
371
- }
372
- $('.navbar-main').css('pointer-events', 'none');
373
- setTimeout(() => {
374
- $('.navbar-main').css('pointer-events', 'auto');
375
- }, 400);
376
- close();
377
- touch = false;
378
- })
379
- .on('focusin', (e) => {
380
- // Close the search box when the focus moves to the rest of the page, e.g., by Tab
381
- if ($main.hasClass('show') && !$main[0].contains(e.target)) {
382
- close(false);
383
- }
384
- })
385
- .on('keydown', (e) => {
386
- if (isSearchShortcut(e)) {
387
- e.preventDefault();
388
- open();
389
- return;
390
- }
391
- if (!$main.hasClass('show')) return;
392
- switch (e.keyCode) {
393
- case 27: // ESC
394
- close();
395
- break;
396
- case 38: // UP
397
- selectItemByDiff(-1);
398
- break;
399
- case 40: // DOWN
400
- selectItemByDiff(1);
401
- break;
402
- case 13: // ENTER
403
- gotoLink($container.find('.searchbox-result-item.active').eq(0));
404
- break;
405
- }
406
- })
407
- .on('touchstart', (e) => {
244
+ .on('touchstart.insightSearch', () => {
408
245
  touch = true;
409
246
  })
410
- .on('touchmove', (e) => {
247
+ .on('touchmove.insightSearch', () => {
411
248
  touch = false;
412
249
  });
413
250
  }
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Serialize the comment markers produced by DefaultValue as YAML comments.
3
+ * @module core/commented_yaml
4
+ */
5
+ const yaml = require('../util/yaml');
6
+
7
+ /** A magic string for reformatting comment lines in the YAML output. */
8
+ const MAGIC = 'c823d4d4';
9
+
10
+ /**
11
+ * @param {any} value The commented object or array.
12
+ * @returns {string} The formatted YAML string.
13
+ */
14
+ function stringify(value) {
15
+ // The YAML writer may quote comments. Parse them rather than removing quotes by hand.
16
+ const property = new RegExp(`^(\\s*)(['"]?)${MAGIC}.*?\\2: (.*)$`);
17
+ const element = new RegExp(`^(\\s*)- (.*${MAGIC}.*)$`);
18
+ const comment = (indent, text) => `${indent}# ${text === null || text === undefined ? '' : text}`;
19
+ return yaml.stringify(value).split('\n').map(line => {
20
+ let match = property.exec(line);
21
+ if (match) {
22
+ return comment(match[1], yaml.parse(match[3]));
23
+ }
24
+ match = element.exec(line);
25
+ if (match) {
26
+ const text = String(yaml.parse(match[2]));
27
+ return comment(match[1], text.slice(text.indexOf(': ') + 2));
28
+ }
29
+ return line;
30
+ }).join('\n');
31
+ }
32
+ module.exports = {
33
+ MAGIC,
34
+ stringify
35
+ };
@@ -0,0 +1,150 @@
1
+ /**
2
+ * Default value wrappers, merging, and comment annotation, independent of schema loading/validation.
3
+ * @module core/default_value
4
+ */
5
+ const {
6
+ typeOf,
7
+ hasOwnProperty
8
+ } = require('./schema_util');
9
+ const {
10
+ MAGIC,
11
+ stringify
12
+ } = require('./commented_yaml');
13
+
14
+ /** The default value wrapper class. */
15
+ class DefaultValue {
16
+ /**
17
+ * @param {any} value The wrapped default value.
18
+ * @param {string} description A description used to produce the comment string.
19
+ */
20
+ constructor(value, description) {
21
+ this.value = value;
22
+ this.description = description;
23
+ }
24
+
25
+ /**
26
+ * Duplicate this wrapper, shallow-copying its wrapped arrays and objects.
27
+ * @returns {DefaultValue} A new wrapper.
28
+ */
29
+ clone() {
30
+ const result = new DefaultValue(this.value, this.description);
31
+ if (result.value instanceof DefaultValue) {
32
+ result.value = result.value.clone();
33
+ } else if (typeOf(result.value) === 'array') {
34
+ result.value = [].concat(result.value);
35
+ } else if (typeOf(result.value) === 'object') {
36
+ result.value = Object.assign({}, result.value);
37
+ }
38
+ return result;
39
+ }
40
+
41
+ /** Unwrap nested wrappers, preferring the innermost non-empty description. */
42
+ flatten() {
43
+ if (this.value instanceof DefaultValue) {
44
+ this.value.flatten();
45
+ if (hasOwnProperty(this.value, 'description') && this.value.description) {
46
+ this.description = this.value.description;
47
+ }
48
+ this.value = this.value.value;
49
+ }
50
+ }
51
+
52
+ /**
53
+ * Merge a wrapper with the same value type, concatenating arrays and assigning object properties.
54
+ * @param {DefaultValue} source The wrapper to merge.
55
+ */
56
+ merge(source) {
57
+ if (hasOwnProperty(source, 'value') && source.value !== null) {
58
+ this.flatten();
59
+ if (source.value instanceof DefaultValue) {
60
+ source = source.clone();
61
+ source.flatten();
62
+ }
63
+ if (typeOf(source.value) === typeOf(this.value)) {
64
+ if (typeOf(this.value) === 'array') {
65
+ this.value = this.value.concat(source.value);
66
+ } else if (typeOf(this.value) === 'object') {
67
+ Object.keys(source.value).forEach(key => {
68
+ this.value[key] = source.value[key];
69
+ });
70
+ } else {
71
+ this.value = source.value;
72
+ }
73
+ } else if (typeOf(this.value) === 'undefined' || typeOf(this.value) === 'null') {
74
+ this.value = source.value;
75
+ }
76
+ }
77
+ if (hasOwnProperty(source, 'description') && source.description) {
78
+ this.description = source.description;
79
+ }
80
+ }
81
+
82
+ /**
83
+ * Insert description markers before each wrapped array element.
84
+ * @returns {Array} A new array of comments and annotated values.
85
+ */
86
+ toCommentedArray() {
87
+ return [].concat(...this.value.map(item => {
88
+ if (item instanceof DefaultValue) {
89
+ if (typeOf(item.description) !== 'string' || !item.description.trim()) {
90
+ return [item.toCommented()];
91
+ }
92
+ return item.description.split('\n').map((line, i) => {
93
+ return MAGIC + i + ': ' + line;
94
+ }).concat(item.toCommented());
95
+ } else if (typeOf(item) === 'array' || typeOf(item) === 'object') {
96
+ return new DefaultValue(item).toCommented();
97
+ }
98
+ return [item];
99
+ }));
100
+ }
101
+
102
+ /**
103
+ * Insert description markers before each wrapped object property.
104
+ * @returns {Object} A new object of comments and annotated values.
105
+ */
106
+ toCommentedObject() {
107
+ if (this.value instanceof DefaultValue) {
108
+ return this.value.toCommented();
109
+ }
110
+ const result = {};
111
+ for (const key in this.value) {
112
+ const item = this.value[key];
113
+ if (item instanceof DefaultValue) {
114
+ if (typeOf(item.description) === 'string' && item.description.trim()) {
115
+ item.description.split('\n').forEach((line, i) => {
116
+ result[MAGIC + key + i] = line;
117
+ });
118
+ }
119
+ result[key] = item.toCommented();
120
+ } else if (typeOf(item) === 'array' || typeOf(item) === 'object') {
121
+ result[key] = new DefaultValue(item).toCommented();
122
+ } else {
123
+ result[key] = item;
124
+ }
125
+ }
126
+ return result;
127
+ }
128
+
129
+ /**
130
+ * Annotate arrays and objects with comment markers, or return a primitive value unchanged.
131
+ * @returns {any} The annotated value.
132
+ */
133
+ toCommented() {
134
+ if (typeOf(this.value) === 'array') {
135
+ return this.toCommentedArray();
136
+ } else if (typeOf(this.value) === 'object') {
137
+ return this.toCommentedObject();
138
+ }
139
+ return this.value;
140
+ }
141
+
142
+ /**
143
+ * Create YAML including the descriptions of nested values as comments.
144
+ * @returns {string} The formatted YAML string.
145
+ */
146
+ toYaml() {
147
+ return stringify(this.toCommented());
148
+ }
149
+ }
150
+ module.exports = DefaultValue;