mirror of
https://github.com/len0rd/personal-website.git
synced 2025-03-01 03:51:57 -05:00
generate recipe link and hashtag list on startup. filter recipes in list based on active tags. tags in recipes dont link back yet
This commit is contained in:
parent
e8010aa1f2
commit
1e1ba15118
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,4 +1,5 @@
|
||||||
*.DS_STORE
|
*.DS_STORE
|
||||||
node_modules
|
node_modules
|
||||||
views/partials/md/
|
views/partials/md/
|
||||||
|
views/partials/generated/
|
||||||
*.mp4
|
*.mp4
|
73
prestart.js
73
prestart.js
|
@ -13,6 +13,7 @@ const showdown = require('showdown'),
|
||||||
projectOutputDir = './views/partials/md/projects/',
|
projectOutputDir = './views/partials/md/projects/',
|
||||||
recipeInputDir = './recipes/',
|
recipeInputDir = './recipes/',
|
||||||
recipeOutputDir = './views/partials/md/recipes/',
|
recipeOutputDir = './views/partials/md/recipes/',
|
||||||
|
recipeListGeneratedOutputDir = './views/partials/generated/',
|
||||||
projectClassMap = {
|
projectClassMap = {
|
||||||
h1: 'display-1' //tag type : class to add to all tags of that type (class="display-1" added to all <h1>)
|
h1: 'display-1' //tag type : class to add to all tags of that type (class="display-1" added to all <h1>)
|
||||||
};
|
};
|
||||||
|
@ -115,13 +116,16 @@ function convertRecipeMarkdown(inputDir, outputDir) {
|
||||||
mkdirp.sync(outputDir);
|
mkdirp.sync(outputDir);
|
||||||
fs.readdir(inputDir, (err, files) => {
|
fs.readdir(inputDir, (err, files) => {
|
||||||
files.forEach(file => {
|
files.forEach(file => {
|
||||||
if (file.endsWith('.md')) {
|
if (!file.endsWith('.md')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
let fileNameNoExtension = file.slice(0, -3);
|
let fileNameNoExtension = file.slice(0, -3);
|
||||||
console.log('converting: ' + fileNameNoExtension);
|
console.log('converting: ' + fileNameNoExtension);
|
||||||
fs.readFile(inputDir + file, 'utf8', (err, data) => {
|
fs.readFile(inputDir + file, 'utf8', (err, data) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
} else {
|
return;
|
||||||
|
}
|
||||||
let tokens = md.parse(data)
|
let tokens = md.parse(data)
|
||||||
|
|
||||||
let sections = []
|
let sections = []
|
||||||
|
@ -133,7 +137,6 @@ function convertRecipeMarkdown(inputDir, outputDir) {
|
||||||
numSections++;
|
numSections++;
|
||||||
}
|
}
|
||||||
else if (numSections < mdSectionHtmlTitles.length) {
|
else if (numSections < mdSectionHtmlTitles.length) {
|
||||||
console.log("found heading open. start new section arr");
|
|
||||||
numSections++;
|
numSections++;
|
||||||
sections.push([]);
|
sections.push([]);
|
||||||
}
|
}
|
||||||
|
@ -150,7 +153,6 @@ function convertRecipeMarkdown(inputDir, outputDir) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// console.log(sections[0]);
|
|
||||||
|
|
||||||
for (let ii = 0; ii < sections.length; ii++) {
|
for (let ii = 0; ii < sections.length; ii++) {
|
||||||
let html = md.renderer.render(sections[ii], md.options);
|
let html = md.renderer.render(sections[ii], md.options);
|
||||||
|
@ -166,12 +168,71 @@ function convertRecipeMarkdown(inputDir, outputDir) {
|
||||||
}
|
}
|
||||||
fs.writeFileSync(outputDir + fileNameNoExtension + '-' + mdSectionHtmlTitles[ii] + '.ejs', html, 'utf8');
|
fs.writeFileSync(outputDir + fileNameNoExtension + '-' + mdSectionHtmlTitles[ii] + '.ejs', html, 'utf8');
|
||||||
}
|
}
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function generateRecipeNavigatorList(recipeSrcDir, generatedOutputDir) {
|
||||||
|
// generate a list of recipe links. While doing so generate an array
|
||||||
|
// of unique hashtags found in all recipes
|
||||||
|
mkdirp.sync(generatedOutputDir);
|
||||||
|
let recipeListPartialOut = "";
|
||||||
|
let allRecipeHashtags = [];
|
||||||
|
fs.readdir(recipeSrcDir, (err, files) => {
|
||||||
|
files.sort().forEach(file => {
|
||||||
|
if (!file.endsWith('.md')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let fileNameNoExtension = file.slice(0, -3);
|
||||||
|
|
||||||
|
const data = fs.readFileSync(recipeSrcDir + file, { encoding: 'utf8', flag: 'r' });
|
||||||
|
|
||||||
|
// find all hashtags in the file
|
||||||
|
var hashtagRegex = new RegExp(`#(\\w+)`, `g`);
|
||||||
|
hashtagMatcher = hashtagRegex.exec(data);
|
||||||
|
var recipeTags = []; // hashtags of the current recipe only
|
||||||
|
while (hashtagMatcher != null) {
|
||||||
|
var hashtag = hashtagMatcher[1].toLowerCase();
|
||||||
|
if (!allRecipeHashtags.includes(hashtag)) {
|
||||||
|
allRecipeHashtags.push(hashtag);
|
||||||
|
}
|
||||||
|
if (!recipeTags.includes(hashtag)) {
|
||||||
|
recipeTags.push(hashtag);
|
||||||
|
}
|
||||||
|
hashtagMatcher = hashtagRegex.exec(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
let combinedRecipeTags = "";
|
||||||
|
if (recipeTags.length > 0) {
|
||||||
|
combinedRecipeTags = recipeTags.join(",");
|
||||||
|
}
|
||||||
|
|
||||||
|
// get first recipe title from document
|
||||||
|
var titleRegex = new RegExp(`#\\s+(.+)\\n`, `g`);
|
||||||
|
titleMatcher = titleRegex.exec(data);
|
||||||
|
var recipeTitle = fileNameNoExtension;
|
||||||
|
if (titleMatcher != null) {
|
||||||
|
recipeTitle = titleMatcher[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
recipeListPartialOut += `<a href="recipes/${fileNameNoExtension}" class="list-group-item list-group-item-action" tags="${combinedRecipeTags}">${recipeTitle}</a>\n`;
|
||||||
|
});
|
||||||
|
|
||||||
|
// writeout the link list partial
|
||||||
|
fs.writeFileSync(generatedOutputDir + "recipe-links.ejs", recipeListPartialOut, "utf-8");
|
||||||
|
|
||||||
|
// now generate the hashtag button list partial
|
||||||
|
// TODO: in the future sort the list by number of hashtag hits (most -> least common)
|
||||||
|
// instead of alphabetically
|
||||||
|
let tagListPartialOut = "";
|
||||||
|
allRecipeHashtags.sort().forEach(hashtag => {
|
||||||
|
tagListPartialOut += `<button type="button" class="btn btn-light">${hashtag}</button>\n`;
|
||||||
|
});
|
||||||
|
fs.writeFileSync(generatedOutputDir + "recipe-tags.ejs", tagListPartialOut, "utf-8");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
convertMarkdownInDirWithShowdown(projectInputDir, projectOutputDir, projectsConverter);
|
convertMarkdownInDirWithShowdown(projectInputDir, projectOutputDir, projectsConverter);
|
||||||
convertRecipeMarkdown(recipeInputDir, recipeOutputDir);
|
convertRecipeMarkdown(recipeInputDir, recipeOutputDir);
|
||||||
|
generateRecipeNavigatorList(recipeInputDir, recipeListGeneratedOutputDir);
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
# Banana Bread
|
# Banana Bread
|
||||||
|
|
||||||
|
#bread #dessert
|
||||||
|
|
||||||
## Ingredients
|
## Ingredients
|
||||||
|
|
||||||
Measure | Weight | Ingredient
|
Measure | Weight | Ingredient
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
# Cinnamon Rolls
|
# Cinnamon Rolls
|
||||||
|
|
||||||
|
#breakfast #bread #dessert
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Ingredients
|
## Ingredients
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
# Pizza Dough
|
# Pizza Dough
|
||||||
|
|
||||||
|
#bread #italian
|
||||||
|
|
||||||
## Ingredients
|
## Ingredients
|
||||||
|
|
||||||
Measure | Weight | Ingredient
|
Measure | Weight | Ingredient
|
||||||
|
|
|
@ -13,15 +13,58 @@
|
||||||
|
|
||||||
<div class="container mt-5 topMargin">
|
<div class="container mt-5 topMargin">
|
||||||
<h1>Recipes</h1>
|
<h1>Recipes</h1>
|
||||||
<div class="list-group">
|
<div class="row mb-2">
|
||||||
<a href="recipes/chocolateChipCookies" class="list-group-item list-group-item-action">Chocolate Chip Cookies</a>
|
<div class="col btn-container">
|
||||||
<a href="recipes/pizzaDough" class="list-group-item list-group-item-action">Pizza Dough</a>
|
<%- include('../partials/generated/recipe-tags') %>
|
||||||
<a href="recipes/bananaBread" class="list-group-item list-group-item-action">Banana Bread</a>
|
</div>
|
||||||
<a href="recipes/cinnamonRolls" class="list-group-item list-group-item-action">Cinnamon Rolls</a>
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="list-group" id="recipe-link-container">
|
||||||
|
<%- include('../partials/generated/recipe-links') %>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<%- include('../partials/post_html_include') %>
|
<%- include('../partials/post_html_include') %>
|
||||||
|
<script>
|
||||||
|
/// This script is responsible for filtering the recipe list
|
||||||
|
/// based on enabled/disabled hashtags. I'm certain theres more
|
||||||
|
/// efficient ways to do this but /shrug
|
||||||
|
$(".btn-container").on("click", "button", function() {
|
||||||
|
$(this).toggleClass("btn-light btn-primary");
|
||||||
|
var tag = $(this).text();
|
||||||
|
var activeTags = [];
|
||||||
|
// create a list of currently active tags
|
||||||
|
$(".btn-container button").filter(function() {
|
||||||
|
if ($(this).hasClass("btn-primary")){
|
||||||
|
activeTags.push($(this).text())
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if ($(this).hasClass("btn-primary")) {
|
||||||
|
// if tag is active, activate its filter
|
||||||
|
$("#recipe-link-container a:visible").filter(function() {
|
||||||
|
$(this).toggle($(this).attr("tags").toLowerCase().indexOf(tag) > -1)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else if ($(this).hasClass("btn-light")) {
|
||||||
|
// tag has been toggled off. re-add any thing that is hidden but shouldnt be
|
||||||
|
$("#recipe-link-container a:hidden").filter(function() {
|
||||||
|
const elemTagList = $(this).attr("tags").toLowerCase();
|
||||||
|
hasActiveTags = true;
|
||||||
|
for (const activeTag of activeTags) {
|
||||||
|
if (elemTagList.indexOf(activeTag) == -1) {
|
||||||
|
hasActiveTags = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (hasActiveTags) {
|
||||||
|
$(this).toggle(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -6,3 +6,4 @@
|
||||||
<link rel="stylesheet" type="text/css" href="/css/site.css">
|
<link rel="stylesheet" type="text/css" href="/css/site.css">
|
||||||
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.2.0/css/all.css" integrity="sha384-hWVjflwFxL6sNzntih27bfxkr27PmbbK/iSvJ+a4+0owXq79v+lsFkW54bOGbiDQ" crossorigin="anonymous">
|
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.2.0/css/all.css" integrity="sha384-hWVjflwFxL6sNzntih27bfxkr27PmbbK/iSvJ+a4+0owXq79v+lsFkW54bOGbiDQ" crossorigin="anonymous">
|
||||||
<link rel="stylesheet" href="//cdn.jsdelivr.net/gh/highlightjs/cdn-release@9.16.2/build/styles/default.min.css">
|
<link rel="stylesheet" href="//cdn.jsdelivr.net/gh/highlightjs/cdn-release@9.16.2/build/styles/default.min.css">
|
||||||
|
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
|
||||||
|
|
Loading…
Reference in a new issue