hexo-theme-ayeria 1.9.10 → 1.9.13
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/LICENSE +37 -37
- package/README.en.md +560 -560
- package/README.md +560 -560
- package/_config.yml +271 -271
- package/_scaffolds/post.md +8 -8
- package/index.js +11 -2
- package/languages/de.yml +39 -39
- package/languages/default.yml +40 -39
- package/languages/en.yml +40 -39
- package/languages/es.yml +39 -39
- package/languages/fr.yml +39 -39
- package/languages/ja.yml +39 -39
- package/languages/ko.yml +39 -39
- package/languages/nl.yml +39 -39
- package/languages/no.yml +39 -39
- package/languages/pt.yml +39 -39
- package/languages/ru.yml +39 -39
- package/languages/vi.yml +39 -39
- package/languages/zh-CN.yml +40 -39
- package/languages/zh-TW.yml +40 -39
- package/layout/_partial/ads.ejs +8 -8
- package/layout/_partial/after-footer.ejs +84 -84
- package/layout/_partial/archive-post.ejs +13 -13
- package/layout/_partial/archive.ejs +61 -61
- package/layout/_partial/article.ejs +94 -94
- package/layout/_partial/ayeria.ejs +53 -53
- package/layout/_partial/baidu-analytics.ejs +11 -11
- package/layout/_partial/broadcast.ejs +19 -19
- package/layout/_partial/float-btns.ejs +7 -7
- package/layout/_partial/footer.ejs +61 -61
- package/layout/_partial/google-analytics.ejs +10 -10
- package/layout/_partial/head.ejs +64 -68
- package/layout/_partial/katex.ejs +21 -21
- package/layout/_partial/lock.ejs +37 -37
- package/layout/_partial/mathjax.ejs +18 -18
- package/layout/_partial/modal.ejs +70 -76
- package/layout/_partial/music.ejs +15 -15
- package/layout/_partial/post/albums.ejs +16 -16
- package/layout/_partial/post/author.ejs +7 -7
- package/layout/_partial/post/busuanzi.ejs +4 -4
- package/layout/_partial/post/category.ejs +9 -9
- package/layout/_partial/post/clipboard.ejs +54 -54
- package/layout/_partial/post/date.ejs +2 -2
- package/layout/_partial/post/gallery.ejs +15 -15
- package/layout/_partial/post/giscus.ejs +40 -40
- package/layout/_partial/post/gitalk.ejs +12 -12
- package/layout/_partial/post/justifiedGallery.ejs +8 -8
- package/layout/_partial/post/nav.ejs +22 -22
- package/layout/_partial/post/search.ejs +3 -3
- package/layout/_partial/post/share.ejs +53 -53
- package/layout/_partial/post/tag.ejs +5 -5
- package/layout/_partial/post/title.ejs +16 -16
- package/layout/_partial/post/tocbot.ejs +6 -6
- package/layout/_partial/post/topping.ejs +2 -2
- package/layout/_partial/post/twikoo.ejs +7 -7
- package/layout/_partial/post/valine.ejs +48 -48
- package/layout/_partial/post/word.ejs +19 -19
- package/layout/_partial/sidebar.ejs +48 -33
- package/layout/_partial/viewer.ejs +105 -105
- package/layout/categories.ejs +8 -8
- package/layout/friends.ejs +19 -19
- package/layout/index.ejs +4 -4
- package/layout/layout.ejs +35 -30
- package/layout/page.ejs +2 -2
- package/layout/plain-page.ejs +4 -4
- package/layout/post.ejs +2 -2
- package/layout/tags.ejs +88 -88
- package/package.json +39 -39
- package/scripts/default_config.js +6 -6
- package/scripts/events/index.js +11 -11
- package/scripts/events/lib/hello.js +38 -38
- package/scripts/events/lib/merge-configs.js +28 -28
- package/scripts/filters/index.js +13 -13
- package/scripts/filters/meta_generator.js +17 -17
- package/scripts/helpers/ayeria-plus-vendors.js +9 -9
- package/scripts/helpers/category-tree.js +90 -90
- package/scripts/helpers/wordcount.js +39 -39
- package/scripts/lib/core.js +25 -25
- package/scripts/utils/join-path.js +12 -12
- package/scripts/utils/object.js +31 -31
- package/source/404.html +187 -187
- package/source/css/ayeria-layout.styl +48 -48
- package/source/css/clipboard.styl +37 -37
- package/source/css/custom.styl +17 -17
- package/source/css/fonts/remixicon.css +1816 -1816
- package/source/css/fonts/remixicon.svg +5329 -5329
- package/source/data/random-sentences.txt +2629 -141
- package/source/dist/main.css +1 -1
- package/source/dist/main.js +1 -1
- package/source/js/clickBoom1.js +152 -152
- package/source/js/clickBoom2.js +180 -180
- package/source/js/jquery-3.6.0.min.js +2 -2
- package/source/js/lazyload.min.js +7 -7
- package/source/js/search.js +126 -126
- package/source/js/random-sentences.js +0 -166
|
@@ -1,90 +1,90 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Custom category tree helper for the /categories page.
|
|
5
|
-
*
|
|
6
|
-
* Generates a "dual-column block" per root category:
|
|
7
|
-
* Left — category name (large) + total post count
|
|
8
|
-
* Right — subcategories as a tree list with indent guide lines
|
|
9
|
-
*
|
|
10
|
-
* Usage in EJS: <%- category_tree([options]) %>
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
hexo.extend.helper.register('category_tree', function (options) {
|
|
14
|
-
const opts = Object.assign(
|
|
15
|
-
{
|
|
16
|
-
orderby: 'name',
|
|
17
|
-
order: 1,
|
|
18
|
-
},
|
|
19
|
-
(this.theme || {}).category_tree,
|
|
20
|
-
options
|
|
21
|
-
);
|
|
22
|
-
|
|
23
|
-
const categories = this.site.categories;
|
|
24
|
-
if (!categories || !categories.length) return '';
|
|
25
|
-
|
|
26
|
-
const self = this;
|
|
27
|
-
|
|
28
|
-
function sortCategories(cats) {
|
|
29
|
-
const arr = cats.toArray();
|
|
30
|
-
const key = opts.orderby === 'count' ? 'length' : 'name';
|
|
31
|
-
arr.sort((a, b) => {
|
|
32
|
-
const va = key === 'length' ? a.posts.length : a.name;
|
|
33
|
-
const vb = key === 'length' ? b.posts.length : b.name;
|
|
34
|
-
if (va < vb) return -1 * opts.order;
|
|
35
|
-
if (va > vb) return 1 * opts.order;
|
|
36
|
-
return 0;
|
|
37
|
-
});
|
|
38
|
-
return arr;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function renderSubtree(cats) {
|
|
42
|
-
const sorted = sortCategories(cats);
|
|
43
|
-
if (!sorted.length) return '';
|
|
44
|
-
|
|
45
|
-
let html = '<ul class="cat-subtree">';
|
|
46
|
-
|
|
47
|
-
sorted.forEach(cat => {
|
|
48
|
-
const children = categories.filter(c => c.parent === cat._id);
|
|
49
|
-
const hasChildren = children.length > 0;
|
|
50
|
-
|
|
51
|
-
html += '<li class="cat-subitem">';
|
|
52
|
-
html += `<a class="cat-sublink" href="${self.url_for(cat.path)}">${cat.name}</a>`;
|
|
53
|
-
html += `<span class="cat-subcount">\uff08${cat.posts.length}\uff09</span>`;
|
|
54
|
-
|
|
55
|
-
if (hasChildren) {
|
|
56
|
-
html += renderSubtree(children);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
html += '</li>';
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
html += '</ul>';
|
|
63
|
-
return html;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
const roots = sortCategories(categories.filter(cat => !cat.parent));
|
|
67
|
-
let html = '';
|
|
68
|
-
|
|
69
|
-
roots.forEach(cat => {
|
|
70
|
-
const total = cat.posts.length;
|
|
71
|
-
const children = categories.filter(c => c.parent === cat._id);
|
|
72
|
-
|
|
73
|
-
html += '<div class="cat-block">';
|
|
74
|
-
|
|
75
|
-
html += '<div class="cat-block-left">';
|
|
76
|
-
html += `<a class="cat-block-name" href="${self.url_for(cat.path)}">${cat.name}</a>`;
|
|
77
|
-
html += `<span class="cat-block-total">${self.__('posts_count', total)}</span>`;
|
|
78
|
-
html += '</div>';
|
|
79
|
-
|
|
80
|
-
html += '<div class="cat-block-right">';
|
|
81
|
-
if (children.length > 0) {
|
|
82
|
-
html += renderSubtree(children);
|
|
83
|
-
}
|
|
84
|
-
html += '</div>';
|
|
85
|
-
|
|
86
|
-
html += '</div>';
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
return html;
|
|
90
|
-
});
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Custom category tree helper for the /categories page.
|
|
5
|
+
*
|
|
6
|
+
* Generates a "dual-column block" per root category:
|
|
7
|
+
* Left — category name (large) + total post count
|
|
8
|
+
* Right — subcategories as a tree list with indent guide lines
|
|
9
|
+
*
|
|
10
|
+
* Usage in EJS: <%- category_tree([options]) %>
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
hexo.extend.helper.register('category_tree', function (options) {
|
|
14
|
+
const opts = Object.assign(
|
|
15
|
+
{
|
|
16
|
+
orderby: 'name',
|
|
17
|
+
order: 1,
|
|
18
|
+
},
|
|
19
|
+
(this.theme || {}).category_tree,
|
|
20
|
+
options
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
const categories = this.site.categories;
|
|
24
|
+
if (!categories || !categories.length) return '';
|
|
25
|
+
|
|
26
|
+
const self = this;
|
|
27
|
+
|
|
28
|
+
function sortCategories(cats) {
|
|
29
|
+
const arr = cats.toArray();
|
|
30
|
+
const key = opts.orderby === 'count' ? 'length' : 'name';
|
|
31
|
+
arr.sort((a, b) => {
|
|
32
|
+
const va = key === 'length' ? a.posts.length : a.name;
|
|
33
|
+
const vb = key === 'length' ? b.posts.length : b.name;
|
|
34
|
+
if (va < vb) return -1 * opts.order;
|
|
35
|
+
if (va > vb) return 1 * opts.order;
|
|
36
|
+
return 0;
|
|
37
|
+
});
|
|
38
|
+
return arr;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function renderSubtree(cats) {
|
|
42
|
+
const sorted = sortCategories(cats);
|
|
43
|
+
if (!sorted.length) return '';
|
|
44
|
+
|
|
45
|
+
let html = '<ul class="cat-subtree">';
|
|
46
|
+
|
|
47
|
+
sorted.forEach(cat => {
|
|
48
|
+
const children = categories.filter(c => c.parent === cat._id);
|
|
49
|
+
const hasChildren = children.length > 0;
|
|
50
|
+
|
|
51
|
+
html += '<li class="cat-subitem">';
|
|
52
|
+
html += `<a class="cat-sublink" href="${self.url_for(cat.path)}">${cat.name}</a>`;
|
|
53
|
+
html += `<span class="cat-subcount">\uff08${cat.posts.length}\uff09</span>`;
|
|
54
|
+
|
|
55
|
+
if (hasChildren) {
|
|
56
|
+
html += renderSubtree(children);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
html += '</li>';
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
html += '</ul>';
|
|
63
|
+
return html;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const roots = sortCategories(categories.filter(cat => !cat.parent));
|
|
67
|
+
let html = '';
|
|
68
|
+
|
|
69
|
+
roots.forEach(cat => {
|
|
70
|
+
const total = cat.posts.length;
|
|
71
|
+
const children = categories.filter(c => c.parent === cat._id);
|
|
72
|
+
|
|
73
|
+
html += '<div class="cat-block">';
|
|
74
|
+
|
|
75
|
+
html += '<div class="cat-block-left">';
|
|
76
|
+
html += `<a class="cat-block-name" href="${self.url_for(cat.path)}">${cat.name}</a>`;
|
|
77
|
+
html += `<span class="cat-block-total">${self.__('posts_count', total)}</span>`;
|
|
78
|
+
html += '</div>';
|
|
79
|
+
|
|
80
|
+
html += '<div class="cat-block-right">';
|
|
81
|
+
if (children.length > 0) {
|
|
82
|
+
html += renderSubtree(children);
|
|
83
|
+
}
|
|
84
|
+
html += '</div>';
|
|
85
|
+
|
|
86
|
+
html += '</div>';
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
return html;
|
|
90
|
+
});
|
|
@@ -1,39 +1,39 @@
|
|
|
1
|
-
var util = require('hexo-util');
|
|
2
|
-
var stripHTML = util.stripHTML;
|
|
3
|
-
|
|
4
|
-
var counter = function (content) {
|
|
5
|
-
content = stripHTML(content);
|
|
6
|
-
const cn = (content.match(/[\u4E00-\u9FA5]/g) || []).length;
|
|
7
|
-
const en = (content.replace(/[\u4E00-\u9FA5]/g, '').match(/[a-zA-Z0-9_\u0392-\u03c9\u0400-\u04FF]+|[\u4E00-\u9FFF\u3400-\u4dbf\uf900-\ufaff\u3040-\u309f\uac00-\ud7af\u0400-\u04FF]+|[\u00E4\u00C4\u00E5\u00C5\u00F6\u00D6]+|\w+/g) || []).length;
|
|
8
|
-
return [cn, en];
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
hexo.extend.helper.register('min2read', function (content, {
|
|
12
|
-
cn = 300,
|
|
13
|
-
en = 160
|
|
14
|
-
} = {}) {
|
|
15
|
-
var len = counter(content);
|
|
16
|
-
var readingTime = len[0] / cn + len[1] / en;
|
|
17
|
-
return readingTime < 1 ? '1' : parseInt(readingTime, 10);
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
hexo.extend.helper.register('wordcount', function (content) {
|
|
21
|
-
var len = counter(content);
|
|
22
|
-
var count = len[0] + len[1];
|
|
23
|
-
if (count < 1000) {
|
|
24
|
-
return count;
|
|
25
|
-
}
|
|
26
|
-
return Math.round(count / 100) / 10 + 'k';
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
hexo.extend.helper.register('totalcount', function (site) {
|
|
30
|
-
var count = 0;
|
|
31
|
-
site.posts.forEach(function (post) {
|
|
32
|
-
var len = counter(post.content);
|
|
33
|
-
count += len[0] + len[1];
|
|
34
|
-
});
|
|
35
|
-
if (count < 1000) {
|
|
36
|
-
return count;
|
|
37
|
-
}
|
|
38
|
-
return Math.round(count / 100) / 10 + 'k';
|
|
39
|
-
});
|
|
1
|
+
var util = require('hexo-util');
|
|
2
|
+
var stripHTML = util.stripHTML;
|
|
3
|
+
|
|
4
|
+
var counter = function (content) {
|
|
5
|
+
content = stripHTML(content);
|
|
6
|
+
const cn = (content.match(/[\u4E00-\u9FA5]/g) || []).length;
|
|
7
|
+
const en = (content.replace(/[\u4E00-\u9FA5]/g, '').match(/[a-zA-Z0-9_\u0392-\u03c9\u0400-\u04FF]+|[\u4E00-\u9FFF\u3400-\u4dbf\uf900-\ufaff\u3040-\u309f\uac00-\ud7af\u0400-\u04FF]+|[\u00E4\u00C4\u00E5\u00C5\u00F6\u00D6]+|\w+/g) || []).length;
|
|
8
|
+
return [cn, en];
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
hexo.extend.helper.register('min2read', function (content, {
|
|
12
|
+
cn = 300,
|
|
13
|
+
en = 160
|
|
14
|
+
} = {}) {
|
|
15
|
+
var len = counter(content);
|
|
16
|
+
var readingTime = len[0] / cn + len[1] / en;
|
|
17
|
+
return readingTime < 1 ? '1' : parseInt(readingTime, 10);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
hexo.extend.helper.register('wordcount', function (content) {
|
|
21
|
+
var len = counter(content);
|
|
22
|
+
var count = len[0] + len[1];
|
|
23
|
+
if (count < 1000) {
|
|
24
|
+
return count;
|
|
25
|
+
}
|
|
26
|
+
return Math.round(count / 100) / 10 + 'k';
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
hexo.extend.helper.register('totalcount', function (site) {
|
|
30
|
+
var count = 0;
|
|
31
|
+
site.posts.forEach(function (post) {
|
|
32
|
+
var len = counter(post.content);
|
|
33
|
+
count += len[0] + len[1];
|
|
34
|
+
});
|
|
35
|
+
if (count < 1000) {
|
|
36
|
+
return count;
|
|
37
|
+
}
|
|
38
|
+
return Math.round(count / 100) / 10 + 'k';
|
|
39
|
+
});
|
package/scripts/lib/core.js
CHANGED
|
@@ -1,25 +1,25 @@
|
|
|
1
|
-
/* global hexo */
|
|
2
|
-
|
|
3
|
-
'use strict';
|
|
4
|
-
|
|
5
|
-
// Hexo事件:https://hexo.io/zh-cn/api/events
|
|
6
|
-
|
|
7
|
-
/** 在静态文件生成前发布 */
|
|
8
|
-
hexo.on('generateBefore', () => {
|
|
9
|
-
// Merge config.
|
|
10
|
-
//require('./../filters/index')(hexo);
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
/** 在文章文件建立后发布。该事件返回文章参数。 */
|
|
14
|
-
hexo.on('new', function (post) {
|
|
15
|
-
//console.log(post)
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
/** 在文章开始渲染前执行 */
|
|
19
|
-
hexo.extend.filter.register('before_post_render', function (data) {
|
|
20
|
-
//var config = hexo.config;
|
|
21
|
-
//console.log(3);
|
|
22
|
-
//console.log(config);
|
|
23
|
-
//console.log(hexo.theme.config);
|
|
24
|
-
|
|
25
|
-
});
|
|
1
|
+
/* global hexo */
|
|
2
|
+
|
|
3
|
+
'use strict';
|
|
4
|
+
|
|
5
|
+
// Hexo事件:https://hexo.io/zh-cn/api/events
|
|
6
|
+
|
|
7
|
+
/** 在静态文件生成前发布 */
|
|
8
|
+
hexo.on('generateBefore', () => {
|
|
9
|
+
// Merge config.
|
|
10
|
+
//require('./../filters/index')(hexo);
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
/** 在文章文件建立后发布。该事件返回文章参数。 */
|
|
14
|
+
hexo.on('new', function (post) {
|
|
15
|
+
//console.log(post)
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
/** 在文章开始渲染前执行 */
|
|
19
|
+
hexo.extend.filter.register('before_post_render', function (data) {
|
|
20
|
+
//var config = hexo.config;
|
|
21
|
+
//console.log(3);
|
|
22
|
+
//console.log(config);
|
|
23
|
+
//console.log(hexo.theme.config);
|
|
24
|
+
|
|
25
|
+
});
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const joinPath = function(base, relative) {
|
|
4
|
-
if (relative && /^https*:\/\//.test(relative)) {
|
|
5
|
-
return relative;
|
|
6
|
-
}
|
|
7
|
-
return relative
|
|
8
|
-
? base.replace(/\/+$/, '') + '/' + relative.replace(/^\/+/, '')
|
|
9
|
-
: base;
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
module.exports = joinPath;
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const joinPath = function(base, relative) {
|
|
4
|
+
if (relative && /^https*:\/\//.test(relative)) {
|
|
5
|
+
return relative;
|
|
6
|
+
}
|
|
7
|
+
return relative
|
|
8
|
+
? base.replace(/\/+$/, '') + '/' + relative.replace(/^\/+/, '')
|
|
9
|
+
: base;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
module.exports = joinPath;
|
package/scripts/utils/object.js
CHANGED
|
@@ -1,31 +1,31 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const isObject = (obj) => {
|
|
4
|
-
return obj && typeof obj === 'object' && !Array.isArray(obj);
|
|
5
|
-
};
|
|
6
|
-
|
|
7
|
-
const isNotEmptyObject = (obj) => {
|
|
8
|
-
return obj && typeof obj === 'object' && Object.getOwnPropertyNames(obj).length !== 0;
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
const merge = (target, ...sources) => {
|
|
12
|
-
for (const source of sources) {
|
|
13
|
-
for (const key in source) {
|
|
14
|
-
if (!Object.prototype.hasOwnProperty.call(source, key)) {
|
|
15
|
-
continue;
|
|
16
|
-
}
|
|
17
|
-
if (isObject(target[key]) && isObject(source[key])) {
|
|
18
|
-
merge(target[key], source[key]);
|
|
19
|
-
} else {
|
|
20
|
-
target[key] = source[key];
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
return target;
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
module.exports = {
|
|
28
|
-
isObject,
|
|
29
|
-
isNotEmptyObject,
|
|
30
|
-
merge
|
|
31
|
-
};
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const isObject = (obj) => {
|
|
4
|
+
return obj && typeof obj === 'object' && !Array.isArray(obj);
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
const isNotEmptyObject = (obj) => {
|
|
8
|
+
return obj && typeof obj === 'object' && Object.getOwnPropertyNames(obj).length !== 0;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const merge = (target, ...sources) => {
|
|
12
|
+
for (const source of sources) {
|
|
13
|
+
for (const key in source) {
|
|
14
|
+
if (!Object.prototype.hasOwnProperty.call(source, key)) {
|
|
15
|
+
continue;
|
|
16
|
+
}
|
|
17
|
+
if (isObject(target[key]) && isObject(source[key])) {
|
|
18
|
+
merge(target[key], source[key]);
|
|
19
|
+
} else {
|
|
20
|
+
target[key] = source[key];
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return target;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
module.exports = {
|
|
28
|
+
isObject,
|
|
29
|
+
isNotEmptyObject,
|
|
30
|
+
merge
|
|
31
|
+
};
|