Skip to content

Commit

Permalink
Added pagination handling method to support array pagination.
Browse files Browse the repository at this point in the history
  • Loading branch information
flik1337 committed Aug 3, 2024
1 parent bf23650 commit f199e49
Show file tree
Hide file tree
Showing 3 changed files with 181 additions and 46 deletions.
55 changes: 54 additions & 1 deletion src/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,15 +47,34 @@ function removeDirectory(dirPath) {
}

function buildPages(pagesDir, buildDir, url) {
paginationList = [];
// check if it's pagination page
const staticJsonPath = path.join(currentDirectory, 'static.json');
if (fs.existsSync(staticJsonPath)) {
const staticJsonContent = fs.readFileSync(staticJsonPath, 'utf8');
staticJSON = JSON.parse(staticJsonContent);
if (staticJSON.hasOwnProperty('paginationList')) {
paginationList = staticJSON.paginationList
}
}
const entries = fs.readdirSync(pagesDir, { withFileTypes: true });

for (const entry of entries) {
const entryPath = path.join(pagesDir, entry.name);
if (entry.isDirectory()) {
const newBuildDir = path.join(buildDir, entry.name);
fs.mkdirSync(newBuildDir, { recursive: true });
buildPages(entryPath, newBuildDir, url);
} else if (entry.isFile() && entry.name.endsWith('.html')) {
buildFile(entryPath, buildDir, url);
routeBaseName = path.basename(entry.name,'.html')
// check this route in static.json
let pagination = getPageSizeForRoute("/" + routeBaseName, paginationList)

if ( paginationList == [] || !pagination ){
buildFile(entryPath, buildDir, url);
}else{
buildPaginationFile(entryPath, buildDir, url, routeBaseName, pagination);
}
}
}
}
Expand Down Expand Up @@ -122,4 +141,38 @@ function buildFile(filePath, buildDir, url){
if(content != null){
fs.writeFileSync(filePath, content);
}
}
function buildPaginationFile(filePath, buildDir, url, keyName, pagination){
containsMagicLast = false
pageNo = 0;
while (!containsMagicLast){
pageContent = parser.processFile(filePath, true, url, pageNo, pagination)
pageContent = parser.parseURLs(pageContent, url);
if ( pageContent.includes("<div id='last-page-marker'></div>")){
containsMagicLast = true
}
if (pageNo == 5){
containsMagicLast = true
}
// Generate the file path for the current page
let pageFileDir = path.join(buildDir, keyName, 'pgn', `${pageNo}`);
fs.mkdirSync(pageFileDir, { recursive: true });
fs.writeFileSync(path.join(pageFileDir, 'index.html'), pageContent);
// 额外为page0设置非/pgn的访问
if (pageNo == 0){
const newBuildDir = path.join(buildDir, keyName);
fs.writeFileSync(path.join(newBuildDir, 'index.html'), pageContent);
}
pageNo ++

}

}
function getPageSizeForRoute(routeToCheck, paginationList) {
// Use the Array.prototype.find() method to find the first object that matches the given route.
const item = paginationList.find(item => item.route === routeToCheck);

// Check if an item was found with the specified route; if so, return its pageSize.
// If no item is found (item is undefined), return null.
return item ? item : null;
}
47 changes: 42 additions & 5 deletions src/dev.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ module.exports = {

},
handleRequest(req, res, url){
const route = req.path === '/' ? '/index' : req.path;
let route = req.path === '/' ? '/index' : req.path;

// First we are going to check if we have a content file in this location
let contentPath = path.join(currentDirectory, './content', route + '.md');
Expand All @@ -114,21 +114,50 @@ module.exports = {
return res.send(contentFile);
}

// If we made it this far we want to now check if the static html file exists
// Handle pagination if specified in 'static.json'.
paginationList = [];
const staticJsonPath = path.join(currentDirectory, 'static.json');
if (fs.existsSync(staticJsonPath)) {
const staticJsonContent = fs.readFileSync(staticJsonPath, 'utf8');
staticJSON = JSON.parse(staticJsonContent);
if (staticJSON.hasOwnProperty('paginationList')) {
paginationList = staticJSON.paginationList
}
}

// Regex to extract page number from the route.
let pageNo = null;
const pageRegex = /\/pgn\/(\d+)/;
isPaginationRoute = false;
const containsPageNo = route.match(pageRegex); // route = /posts/page/0
if ( containsPageNo ){
pageNo = parseInt(containsPageNo[1], 10);
isPaginationRoute = true
route = route.replace(pageRegex, '')
}

// Check for pagination details in the static JSON configuration.
let pagination = this.getPageSizeForRoute(route, paginationList)

if ( isPaginationRoute || pagination ){
pageNo = isPaginationRoute ? pageNo : 0
}

// Define paths to page files based on the current directory and the route.
let pagePath = path.join(currentDirectory, './pages', route + '.html');
let pagePathIndex = path.join(currentDirectory, './pages', route, '/index.html');
let pageContent = null;

// Check if the specified HTML file or its index exists and process it if found.
if (fs.existsSync(pagePath)) {
pageContent = parser.processFile(pagePath);
pageContent = parser.processFile(pagePath, false, 'relative', pageNo, pagination);

} else if(fs.existsSync(pagePathIndex)) {
pageContent = parser.processFile(pagePathIndex);
pageContent = parser.processFile(pagePathIndex, false, 'relative', pageNo, pagination);
}

if (pageContent != null) {
pageContent = parser.parseURLs(pageContent, url);

return res.send(pageContent);
}

Expand Down Expand Up @@ -165,5 +194,13 @@ module.exports = {

server.listen(port);
});
},
getPageSizeForRoute(routeToCheck, paginationList) {
// Use the Array.prototype.find() method to find the first object that matches the given route.
const item = paginationList.find(item => item.route === routeToCheck);

// Check if an item was found with the specified route; if so, return its pageSize.
// If no item is found (item is undefined), return null.
return item ? item : null;
}
}
125 changes: 85 additions & 40 deletions src/parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,8 @@ let isContentFile = false;
let env = require('./env.js');

module.exports = {
processFile(filePath, build=false, url='relative') {


processFile(filePath, build=false, url='relative', pageNo=null, pagination=null) {

let page = this.getPage(filePath);

const layoutTagExists = /<layout[^>]*>[\s\S]*?<\/layout>/.test(page);
Expand All @@ -30,8 +29,10 @@ module.exports = {

// replace {slot} with content inside of Layout
layout = layout.replace('{slot}', this.parseIncludeContent(this.getPageContent(page)));

processedContentLoops = this.processContentLoops(this.parseShortCodes(this.replaceAttributesInLayout(layout, layoutAttributes), url, build), filePath);

page = this.processCollectionLoops(this.processContentLoops(this.parseShortCodes(this.replaceAttributesInLayout(layout, layoutAttributes), url, build), filePath), filePath);
page = this.processCollectionLoops(processedContentLoops, filePath, pageNo, pagination);

page = this.processCollectionJSON(page);
}
Expand Down Expand Up @@ -450,71 +451,118 @@ module.exports = {
fs.writeFileSync(filePath, jsonData);
},

processCollectionLoops(template, filePath) {
processCollectionLoops(template, filePath, pageNo, pagination) {
const { route, pageSize, iteratorKey } = pagination || {}; // Destructure pagination details

// Regular expression to capture the ForEach sections
const loopRegex = /<ForEach\s+([^>]+)>([\s\S]*?)<\/ForEach>/g;

let match;
// Define regex to match <paginator> tag and its content
// const paginatorRegex = /<paginator>([\s\S]*?)<\/paginator>/;
const paginatorRegex = /(?<!<!--\s*)<paginator>([\s\S]*?)<\/paginator>(?!\s*-->)/;

// Extract paginator inner content
const extractedContentMatch = template.match(paginatorRegex);
let extractedPaginator = extractedContentMatch ? extractedContentMatch[1].trim() : null;

// Remove <paginator> tag and its content from the template
template = template.replace(paginatorRegex, '').trim();

while ((match = loopRegex.exec(template)) !== null) {
const attributeString = match[1];
const loopBody = match[2];

const attributes = this.forEachAttributesAndValues('<ForEach ' + attributeString + '>');
const attributeString = match[1]; // Extract attributes string
const loopBody = match[2]; // Extract loop body content

// Extract the collection name from the attributes
//const collectionNameMatch = /collection="([^"]+)"/.exec(attributeString);
const attributes = this.forEachAttributesAndValues('<ForEach ' + attributeString + '>');

// Continue if the collection name is not found in attributes
if (!attributes.collection) {
continue; // Skip if collection name is not found
continue;
}


// Load the corresponding JSON file
// Load JSON data from specified file
let jsonData = JSON.parse(fs.readFileSync(path.join(currentDirectory, '/collections/', `${attributes.collection}.json`), 'utf8'));

// Determine the loop keyword, default to collection name
let loopKeyword = attributes.as || attributes.collection.replace(/\//g, '.');

let loopKeyword = attributes.collection.replace(/\//g, '.');
if (attributes.as) {
loopKeyword = attributes.as;
}
// Target the specific ForEach loop based on iteratorKey
const targetForEach = attributes.iteratorKey && attributes.iteratorKey === iteratorKey;

let count = attributes.count || null; // Maximum items to process, if specified

let count = null;
if(attributes.count){
count = attributes.count;
}
jsonData = this.handleOrderBy(jsonData, attributes); // Apply sorting to data

let paginationHtml = ""
const generatePgn = targetForEach && pageSize != null && pageNo != null

jsonData = this.handleOrderBy(jsonData, attributes);
// slice the jsonData and generate paginator content
if (generatePgn) {
const { pageData, isFirstPage, isLastPage } = this.paginateData(jsonData, pageSize, pageNo); //slice
jsonData = pageData;

// Generate pagination links
const { prevLink, nextLink } = this.generatePaginationLinks(isFirstPage, isLastPage, pageNo, '/posts/pgn/');
if (extractedPaginator != null
&& extractedPaginator.includes("prev")
&& extractedPaginator.includes("next") ){

paginationHtml = extractedPaginator.replace("prev", prevLink).replace("next", nextLink);
}

if (isLastPage) {
paginationHtml += "<div id='last-page-marker'></div>"; // Add a marker for the last page
}

}
let loopResult = '';
let loop = 1;
for (const item of jsonData) {
let processedBody = loopBody;
const data = { ...item, loop };
const data = { ...item, loop }; // Merge item data with loop index

// Process conditions
// Process conditions and placeholders
processedBody = this.processConditions(processedBody, data, loopKeyword, loop);

for (const key in item) {
// Regular expression to replace the placeholders
const placeholderRegex = new RegExp(`{${loopKeyword}.${key}}`, 'g');
let itemValue = item[key];
if (Array.isArray(item[key])) {
itemValue = item[key].join("|");
}
let itemValue = Array.isArray(item[key]) ? item[key].join("|") : item[key];
processedBody = processedBody.replace(placeholderRegex, itemValue);
}

loopResult += processedBody;
loop++;

if((loop-1) == count){
break;
if ((loop - 1) == count) {
break; // Stop processing if count limit is reached
}
}


// add the paginationHtml
if ( generatePgn ){
loopResult += paginationHtml
}
template = template.replace(match[0], loopResult);


}

return template;
},
paginateData(jsonData, pageSize, pageNo) {
const totalPages = Math.ceil(jsonData.length / pageSize); // Calculate total pages
const startIndex = pageNo * pageSize; // Compute start index for slicing
const endIndex = startIndex + pageSize; // Compute end index for slicing
const pageData = jsonData.slice(startIndex, endIndex); // Extract data for the current page
const isLastPage = pageNo === totalPages - 1; // Check if it is the last page
const isFirstPage = pageNo === 0; // Check if it is the first page

return { pageData, isFirstPage, isLastPage, totalPages };
},
generatePaginationLinks(isFirstPage, isLastPage, pageNo, baseUrl) {
let prevLink = isFirstPage ? `style='visibility: hidden;'` : `href='${baseUrl}${pageNo - 1}'`; // Generate previous page link
let nextLink = isLastPage ? `style='visibility: hidden;'` : `href='${baseUrl}${pageNo + 1}'`; // Generate next page link
return { prevLink, nextLink };
},

processConditions(content, data, parentCollection) {
// Regular expression to capture the If sections
Expand Down Expand Up @@ -555,6 +603,7 @@ module.exports = {
const orderBy = attributes.orderBy.split(',').map(item => item.trim());
const valueA = a[orderBy[0]];
const valueB = b[orderBy[0]];

//let direction = 'asc';
// Check if the orderBy array has more than one element
if (orderBy.length > 1) {
Expand All @@ -566,10 +615,6 @@ module.exports = {
direction = attributes.orderSort == 'desc' ? 'desc' : 'asc'
}

if (orderBy.length > 1) {
direction = orderBy[1].toLowerCase().trim();
}

if (typeof valueA === 'string' && typeof valueB === 'string') {
if (direction === 'desc') {
return valueB.localeCompare(valueA);
Expand Down

0 comments on commit f199e49

Please sign in to comment.