hexo-plugin-aurora
Version:
A plugin for Hexo Aurora theme
351 lines (304 loc) • 9.84 kB
JavaScript
const { postMapper, postListMapper } = require('../helpers/mapper');
const { formatNumber, throwError, throwInfo } = require('../helpers/utils');
class PostGenerator {
data = [];
normalData = []; // Without the reordering cause by feature/pin posts
pagination = [];
features = [];
configs = {};
authors = {};
postByAuthors = new Map();
featureCapacity = 3;
isFeature = true;
constructor(posts, configs, options = { sort: true }) {
this.data = posts;
this.fullData = posts;
this.configs = configs;
this.authors = this.configs.theme_config.authors || {};
this.isFeature = this.configs.theme_config.theme.feature;
this.transform(options);
}
/**
* Transform post data into API formats.
* @returns void
*/
transform(options = { sort: true }) {
if (this.count() <= 0) return;
let prevPost = {};
let dummyList = [];
let featureIndexes = [];
// Used when feature posts is not enough
if (options.sort) {
this.sortByDate();
}
this.reorderFeaturePosts();
this.data.data.forEach((post, index) => {
let current = postMapper(post, this.configs);
current.prev_post = prevPost;
current.next_post = {};
prevPost = postListMapper(current, this.configs);
if (index !== 0) {
dummyList[index - 1].next_post = postListMapper(current, this.configs);
}
dummyList.push(current);
if (
this.isFeature &&
Boolean(current.feature) === true &&
featureIndexes.length < this.featureCapacity
) {
featureIndexes.push(index);
}
this.fillAuthorPost(current);
});
if (this.isFeature && featureIndexes.length > 0) {
this.features = featureIndexes.map(function (postIndex) {
return dummyList[postIndex];
});
}
this.data = dummyList;
this.normalData = this.sortByDateForNormalData();
}
removeHiddenPosts() {
this.data = this.data.filter((item) => !item.hidden);
}
sortByDate() {
this.data = this.data.sort('-date').filter(function (post) {
return post.published;
});
}
sortByDateForNormalData() {
return this.data.sort(function (a, b) {
// Turn your strings into dates, and then subtract them
// to get a value that is either negative, positive, or zero.
return new Date(b.date) - new Date(a.date);
});
}
reorderFeaturePosts() {
const featureData = [];
const dummyData = [];
const fillOutIndexes = [];
let data = Object.create(this.data.data);
// Check if articles count > feature capacity.
// Switch to PIN MODE if not enough posts.
if (data.length < this.featureCapacity) {
throwInfo(
'Aurora Generator Warning',
`You need at least ${this.featureCapacity} articles to enable [FEATURE MODE], you currently have ${data.length}. [PIN MODE] is activated instead!`
);
this.isFeature = false;
}
// Pull out the feature posts and fill-in posts
let currentIndex = 0;
for (let value of this.data.data) {
if (this.isFeature && featureData.length === this.featureCapacity) {
break;
}
// Hidden posts are not able to be featured
// 1. Skip feature post that has hidden attribute
// 2. Skip hidden posts for filling
if (value.feature && !value.hidden) {
featureData.push({
index: currentIndex,
date: value.date.valueOf(),
data: value
});
fillOutIndexes.push(currentIndex);
} else if (this.isFeature && dummyData.length !== this.featureCapacity) {
dummyData.push({
index: currentIndex,
date: value.date.valueOf(),
data: value
});
fillOutIndexes.push(currentIndex);
}
currentIndex++;
}
if (
!this.isFeature ||
(featureData.length < 3 && featureData.length + dummyData.length < this.featureCapacity)
) {
// Switch into pin mode.
this.isFeature = false;
} else {
// Fill until max feature capacity.
dummyData.some((value) => {
// Skip filling for post that has hidden = true
if (featureData.length < this.featureCapacity && !value.hidden) {
value.data.feature = true;
featureData.push(value);
} else {
fillOutIndexes.splice(fillOutIndexes.indexOf(value.index), 1);
}
});
}
// Sort by index (=== sort by latest)
featureData.sort((a, b) => {
return a.date - b.date;
});
// Filter out all the pull out posts
data = data.filter((value, index) => {
return fillOutIndexes.indexOf(index) === -1;
});
// Reorder all the feature / pinned post
featureData.forEach((value) => {
if (!this.isFeature) {
value.data.pinned = true;
} else {
data.unshift(value.data);
}
});
if (this.isFeature && this.data.data.length !== data.length) {
throwError('Aurora Generator Error', 'Mismatch post count after feature processing!');
}
// Filter all the hidden posts
this.data.data = data;
}
/**
* Adding author's posts
* @param {*} post
*/
fillAuthorPost(post) {
let authorPostData = {};
if (!this.postByAuthors.has(post.author.slug)) {
Object.assign(authorPostData, post.author);
// authorPostData = Object.create(post.author)
authorPostData.post_list = [postListMapper(post, this.configs)];
authorPostData.categories = new Set();
authorPostData.tags = new Set();
authorPostData.word_count = 0;
authorPostData.post_count = 0;
} else {
authorPostData = this.postByAuthors.get(post.author.slug);
authorPostData.post_list.push(postListMapper(post, this.configs));
}
let wordCount = post.count_time.symbolsCount;
if (String(wordCount).indexOf('k') > -1) {
wordCount = Number(String(wordCount).replace(/[k]+/g, '')) * 1000;
}
authorPostData.word_count += Number(wordCount);
authorPostData.post_count += 1;
if (post.categories && post.categories.length > 0) {
post.categories.forEach(function (category) {
authorPostData.categories.add(category.name);
});
}
if (post.categories && post.categories.length > 0) {
post.tags.forEach(function (tag) {
authorPostData.tags.add(tag);
});
}
this.postByAuthors.set(post.author.slug, authorPostData);
}
/**
* Adding post pagination API data
* @returns Array
*/
addPaginationPost(data) {
if (this.count() <= 0) return data;
const pageJson = [];
// Filter out all the hidden articles from pagination
const filteredData = this.data.filter((item) => !item.hidden);
const length = filteredData.length;
// `Pinned mode` use first post as cover post.
// To keep the list post count event, use 13 instead of 12
const pageSize = this.isFeature ? 12 : 13;
const pageCount = Math.ceil(length / pageSize);
const postData = filteredData.map((item) => postListMapper(item, this.configs));
for (let i = 0; i < length; i += pageSize) {
pageJson.push({
path: 'api/posts/' + Math.ceil((i + 1) / pageSize) + '.json',
data: JSON.stringify({
total: length,
pageSize: pageSize,
pageCount: pageCount,
data: postData.slice(i, i + pageSize)
})
});
}
data = data.concat(pageJson);
return data;
}
/**
* Adding post pagination API data
* @returns Array
*/
addArchivesPaginationPost(data) {
if (this.count() <= 0) return data;
const pageJson = [];
// Filter out all the hidden articles from pagination
const filteredData = this.normalData.filter((item) => !item.hidden);
const length = filteredData.length;
// `Pinned mode` use first post as cover post.
// To keep the list post count event, use 13 instead of 12
const pageSize = this.isFeature ? 12 : 13;
const pageCount = Math.ceil(length / pageSize);
const postData = filteredData.map((item) => postListMapper(item, this.configs));
for (let i = 0; i < length; i += pageSize) {
pageJson.push({
path: 'api/archives/' + Math.ceil((i + 1) / pageSize) + '.json',
data: JSON.stringify({
total: length,
pageSize: pageSize,
pageCount: pageCount,
data: postData.slice(i, i + pageSize)
})
});
}
data = data.concat(pageJson);
return data;
}
/**
* Adding article API data
* @returns Array
*/
addArticles(data) {
if (this.count() <= 0) return data;
const postData = this.data;
data = data.concat(
postData.map(function (post) {
const path = 'api/articles/' + post.slug + '.json';
return {
path: path,
data: JSON.stringify(post)
};
})
);
return data;
}
/**
* Creating feature post API data
* @returns Array
*/
addFeatures(data) {
if (this.count() <= 0) return data;
data.push({
path: 'api/features.json',
data: JSON.stringify(this.features.map((item) => postListMapper(item, this.configs)))
});
return data;
}
/**
* Creating Authors API data
* @returns Array
*/
addAuthorPost(data) {
if (this.postByAuthors.size <= 0) return data;
const postData = [];
this.postByAuthors.forEach(function (value, key) {
const path = `api/authors/${key}.json`;
value.categories = value.categories.size;
value.tags = value.tags.size;
value.word_count = formatNumber(value.word_count);
postData.push({
path: path,
data: JSON.stringify(value)
});
});
data = data.concat(postData);
return data;
}
count() {
return this.data.length;
}
}
module.exports = PostGenerator;