move to public repo
commit
47c352b77b
@ -0,0 +1,20 @@
|
||||
module.exports = {
|
||||
tagsBySize: (collection) => {
|
||||
// callback that can return any arbitrary data (since v0.5.3)
|
||||
// see https://www.11ty.dev/docs/collections/#getall()
|
||||
const allPosts = collection.getAll();
|
||||
|
||||
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map
|
||||
const countPostsByTag = new Map();
|
||||
allPosts.forEach((post) => {
|
||||
// short circuit eval sets tags to an empty array if there are no tags set
|
||||
const tags = post.data.tags || [];
|
||||
tags.forEach((tag) => {
|
||||
const count = countPostsByTag.get(tag) || 0;
|
||||
countPostsByTag.set(tag, count + 1)
|
||||
})
|
||||
});
|
||||
|
||||
return [...countPostsByTag].sort((a, b) => b[1] - a[1])
|
||||
}
|
||||
};
|
@ -0,0 +1,66 @@
|
||||
const Image = require('@11ty/eleventy-img');
|
||||
|
||||
module.exports = {
|
||||
featuredImageFilter: (src, sizes, style, postData, callback) => {
|
||||
if ((typeof src === 'undefined' || !src) && postData.outputPath) {
|
||||
callback(null, ``);
|
||||
return null;
|
||||
}
|
||||
const documentPath = postData.filePathStem;
|
||||
src = src.replace(/^\//, '');
|
||||
|
||||
let outputPath = '';
|
||||
try {
|
||||
outputPath = postData.outputPath
|
||||
.substring(0, postData.outputPath.lastIndexOf("/")) // Remove document from path
|
||||
.replace(/^\//, ''); // remove first slash
|
||||
// If the image is absolute path or external
|
||||
} catch (error) {
|
||||
outputPath = '';
|
||||
}
|
||||
|
||||
const folderPath = documentPath
|
||||
.substring(0, documentPath.lastIndexOf("/") + 1) // Remove document from path
|
||||
.replace(/^\//, ''); // remove first slash
|
||||
|
||||
// If the image is absolute path or external
|
||||
if (src.startsWith('assets') || src.startsWith('http')) {
|
||||
|
||||
} else { // Otherwise assume the file is relative to the document folder
|
||||
src = folderPath + src;
|
||||
}
|
||||
|
||||
const options = {
|
||||
widths: [500, 900, 1500, 2500, null],
|
||||
formats: [null],
|
||||
outputDir: outputPath,
|
||||
urlPath: postData.url
|
||||
};
|
||||
Image(src, options).then((metadata) => {
|
||||
let format = '';
|
||||
for (const key in metadata) {
|
||||
format = key;
|
||||
}
|
||||
|
||||
let lowsrc = (metadata[format].length > 1) ? metadata[format][1] : metadata[format][0];
|
||||
|
||||
let imageSrc = metadata[format][0];
|
||||
console.log('[' + '\x1b[36m%s\x1b[0m', '11ty Image' + '\x1b[0m' + ']:', 'Created featured image ', imageSrc.url);
|
||||
callback(null, `<figure class="image ${style}">
|
||||
<picture>
|
||||
${Object.values(metadata).map(imageFormat => {
|
||||
return ` <source type="${imageFormat[0].sourceType}" srcset="${imageFormat.map(entry => entry.srcset).join(", ")}" sizes="${sizes}">`;
|
||||
}).join("\n")}
|
||||
<img
|
||||
src="${lowsrc.url}"
|
||||
width="${lowsrc.width}"
|
||||
height="${lowsrc.height}"
|
||||
alt=""
|
||||
loading="lazy"
|
||||
decoding="async">
|
||||
</picture></figure>`);
|
||||
}).catch(message => {
|
||||
console.error('[' + '\x1b[36m%s\x1b[0m', '11ty Image' + '\x1b[0m' + ']:', message)
|
||||
});
|
||||
}
|
||||
};
|
@ -0,0 +1,18 @@
|
||||
const hashString = (string) => {
|
||||
const cyrb53 = function (str, seed = 0) {
|
||||
let h1 = 0xdeadbeef ^ seed, h2 = 0x41c6ce57 ^ seed;
|
||||
for (let i = 0, ch; i < str.length; i++) {
|
||||
ch = str.charCodeAt(i);
|
||||
h1 = Math.imul(h1 ^ ch, 2654435761);
|
||||
h2 = Math.imul(h2 ^ ch, 1597334677);
|
||||
}
|
||||
h1 = Math.imul(h1 ^ (h1 >>> 16), 2246822507) ^ Math.imul(h2 ^ (h2 >>> 13), 3266489909);
|
||||
h2 = Math.imul(h2 ^ (h2 >>> 16), 2246822507) ^ Math.imul(h1 ^ (h1 >>> 13), 3266489909);
|
||||
return 4294967296 * (2097151 & h2) + (h1 >>> 0);
|
||||
};
|
||||
return cyrb53(string).toString(16); // Returns hash in hexadecimal
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
hashString
|
||||
};
|
@ -0,0 +1,104 @@
|
||||
require('dotenv').config();
|
||||
|
||||
const pluginRss = require('@11ty/eleventy-plugin-rss');
|
||||
const pluginPageAssets = require('eleventy-plugin-page-assets');
|
||||
const pluginNavigation = require('@11ty/eleventy-navigation');
|
||||
const pluginSchema = require('@quasibit/eleventy-plugin-schema');
|
||||
|
||||
const markdown = require('./.eleventy.markdown.js');
|
||||
const filters = require('./.eleventy.filters.js');
|
||||
const asyncFilters = require('./.eleventy.filters.async.js');
|
||||
const shortcodes = require('./.eleventy.shortcodes.js');
|
||||
const asyncShortcodes = require('./.eleventy.shortcodes.async.js');
|
||||
const collections = require('./.eleventy.collections.js');
|
||||
|
||||
module.exports = function (eleventyConfig) {
|
||||
const ELEVENTY_ENVIRONMENT = (typeof process.env.ELEVENTY_ENV !== 'undefined') ? process.env.ELEVENTY_ENV : 'production';
|
||||
console.log('Building with', ELEVENTY_ENVIRONMENT, 'environment.');
|
||||
console.log('');
|
||||
|
||||
eleventyConfig.setFrontMatterParsingOptions({
|
||||
excerpt: true,
|
||||
excerpt_alias: 'custom_excerpt'
|
||||
});
|
||||
eleventyConfig.setLiquidOptions({
|
||||
dynamicPartials: false,
|
||||
strictFilters: true
|
||||
});
|
||||
eleventyConfig.addPlugin(pluginRss, {
|
||||
posthtmlRenderOptions: {
|
||||
closingSingleTag: 'default' // opt-out of <img/>-style XHTML single tags
|
||||
}
|
||||
});
|
||||
eleventyConfig.addPlugin(pluginNavigation);
|
||||
eleventyConfig.addPlugin(pluginPageAssets, {
|
||||
mode: 'directory',
|
||||
postsMatching: 'posts/**/*.md|pages/**/*.md',
|
||||
assetsMatching: '*.png|*.jpg|*.jpeg|*.gif|*.webp|*.gpx|*.fit',
|
||||
recursive: false,
|
||||
hashAssets: false
|
||||
});
|
||||
eleventyConfig.addPlugin(pluginSchema);
|
||||
|
||||
// Filters
|
||||
Object.keys(filters).forEach((filterName) => {
|
||||
eleventyConfig.addFilter(filterName, filters[filterName])
|
||||
});
|
||||
|
||||
// Asynchronous filters
|
||||
Object.keys(asyncFilters).forEach((filterName) => {
|
||||
eleventyConfig.addNunjucksAsyncFilter(filterName, asyncFilters[filterName])
|
||||
});
|
||||
|
||||
// Shortcodes
|
||||
Object.keys(shortcodes).forEach((shortcodeName) => {
|
||||
eleventyConfig.addShortcode(shortcodeName, shortcodes[shortcodeName])
|
||||
});
|
||||
|
||||
// Asynchronous shortcodes
|
||||
Object.keys(asyncShortcodes).forEach((shortcodeName) => {
|
||||
eleventyConfig.addNunjucksAsyncShortcode(shortcodeName, asyncShortcodes[shortcodeName])
|
||||
});
|
||||
|
||||
// Collections
|
||||
Object.keys(collections).forEach((collectionName) => {
|
||||
eleventyConfig.addCollection(collectionName, collections[collectionName])
|
||||
});
|
||||
|
||||
eleventyConfig.addPassthroughCopy('assets');
|
||||
eleventyConfig.addPassthroughCopy('CNAME');
|
||||
eleventyConfig.addPassthroughCopy('robots.txt');
|
||||
eleventyConfig.addPassthroughCopy({'webfinger.json': '/.well-known/webfinger'});
|
||||
eleventyConfig.addPassthroughCopy({'favicon': '/'});
|
||||
eleventyConfig.addPassthroughCopy('_redirects');
|
||||
|
||||
// Layouts
|
||||
eleventyConfig.addLayoutAlias('base', 'base.njk');
|
||||
eleventyConfig.addLayoutAlias('page', 'page.njk');
|
||||
eleventyConfig.addLayoutAlias('post', 'post.njk');
|
||||
eleventyConfig.addLayoutAlias('beer', 'beer.njk');
|
||||
eleventyConfig.addLayoutAlias('brewery', 'brewery.njk');
|
||||
|
||||
eleventyConfig.setDataDeepMerge(true);
|
||||
eleventyConfig.setLibrary('md', markdown);
|
||||
|
||||
return {
|
||||
// Control which files Eleventy will process
|
||||
// e.g.: *.md, *.njk, *.html, *.liquid
|
||||
templateFormats: [
|
||||
'md',
|
||||
'hbs',
|
||||
'njk',
|
||||
'html',
|
||||
],
|
||||
|
||||
// Opt-out of pre-processing global data JSON files: (default: `liquid`)
|
||||
dataTemplateEngine: false,
|
||||
|
||||
// Pre-process *.md files with: (default: `liquid`)
|
||||
markdownTemplateEngine: 'njk',
|
||||
|
||||
// Pre-process *.html files with: (default: `liquid`)
|
||||
htmlTemplateEngine: 'njk',
|
||||
};
|
||||
};
|
@ -0,0 +1,81 @@
|
||||
const slugify = require('slugify');
|
||||
const markdownItAnchor = require('markdown-it-anchor');
|
||||
const markdownIt = require('markdown-it');
|
||||
const markdownItTasklist = require('markdown-it-task-lists');
|
||||
const markdownItSup = require('markdown-it-sup');
|
||||
const markdownItAbbr = require('markdown-it-abbr');
|
||||
const markdownItKbd = require('markdown-it-kbd');
|
||||
const markdownItFootnotes = require('markdown-it-footnote');
|
||||
const markdownItTables = require('markdown-it-multimd-table');
|
||||
const markdownItContainer = require('markdown-it-container');
|
||||
const markdownItAttrs = require('markdown-it-attrs');
|
||||
const markdownItImageFigures = require('markdown-it-image-figures');
|
||||
const markdownItAnchorOptions = {
|
||||
level: [1, 2, 3, 4],
|
||||
slugify: (str) =>
|
||||
slugify(str, {
|
||||
lower: true,
|
||||
strict: true,
|
||||
remove: /[*+~.()'"!:@]/g,
|
||||
}),
|
||||
tabIndex: false,
|
||||
permalink: markdownItAnchor.permalink.headerLink(),
|
||||
};
|
||||
|
||||
const scrollBlock = {
|
||||
validate: function (params) {
|
||||
return params.trim().match(/^scroll-block\s*(.*)$/);
|
||||
},
|
||||
|
||||
render: function (tokens, idx) {
|
||||
const m = tokens[idx].info.trim().match(/^scroll-block\s*(.*)$/);
|
||||
|
||||
if (tokens[idx].nesting === 1) {
|
||||
// opening tag
|
||||
return '<section class="scroll-block ' + m[1] + '">\n';
|
||||
|
||||
} else {
|
||||
// closing tag
|
||||
return '</section>\n';
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Customize Markdown library and settings:
|
||||
let markdown = markdownIt({
|
||||
html: true,
|
||||
breaks: true,
|
||||
linkify: true
|
||||
})
|
||||
.use(markdownItTasklist, {
|
||||
enabled: false,
|
||||
label: true,
|
||||
labelAfter: false
|
||||
})
|
||||
.use(markdownItSup)
|
||||
.use(markdownItAbbr)
|
||||
.use(markdownItKbd)
|
||||
.use(markdownItFootnotes)
|
||||
.use(markdownItTables, {
|
||||
multiline: false,
|
||||
rowspan: true,
|
||||
headerless: true,
|
||||
})
|
||||
.use(markdownItAnchor, markdownItAnchorOptions)
|
||||
.use(markdownItContainer, 'scroll-block', scrollBlock)
|
||||
.use(markdownItAttrs)
|
||||
.use(markdownItImageFigures, {
|
||||
lazy: true,
|
||||
async: true,
|
||||
copyAttrs: "class",
|
||||
dataType: true,
|
||||
figcaption: true
|
||||
});
|
||||
|
||||
markdown.renderer.rules.footnote_block_open = () => (
|
||||
'<section class="footnotes" aria-labelledby="fotnoter">\n' +
|
||||
'<h2 class="separator-heading" id="fotnoter"><span>Fotnoter</span></h2>\n' +
|
||||
'<ol class="footnotes-list">\n'
|
||||
);
|
||||
|
||||
module.exports = markdown;
|
@ -0,0 +1,59 @@
|
||||
const EleventyImage = require('@11ty/eleventy-img');
|
||||
const markdown = require('./.eleventy.markdown');
|
||||
|
||||
module.exports = {
|
||||
image: async function (src, style, alt, caption = undefined) {
|
||||
if (typeof this.page.outputPath !== 'undefined' && typeof this.page.outputPath.lastIndexOf !== 'undefined') {
|
||||
const sizes = '100vw';
|
||||
const documentPath = this.page.filePathStem;
|
||||
const outputPath = this.page.outputPath
|
||||
.substring(0, this.page.outputPath.lastIndexOf('/')) // Remove document from path
|
||||
.replace(/^\//, ''); // remove first slash
|
||||
// If the image is absolute path or external
|
||||
|
||||
const folderPath = documentPath
|
||||
.substring(0, documentPath.lastIndexOf('/') + 1) // Remove document from path
|
||||
.replace(/^\//, ''); // remove first slash
|
||||
// If the image is absolute path or external
|
||||
|
||||
if (src.startsWith('assets') || src.startsWith('http')) {
|
||||
|
||||
} else { // Otherwise assume the file is relative to the document folder
|
||||
src = folderPath + src;
|
||||
}
|
||||
|
||||
const options = {
|
||||
widths: [500, 900, 1500, 2500, null],
|
||||
formats: [null],
|
||||
outputDir: outputPath,
|
||||
urlPath: ''
|
||||
};
|
||||
|
||||
let metadata = await EleventyImage(src, options);
|
||||
console.log('[' + '\x1b[36m%s\x1b[0m', '11ty Image' + '\x1b[0m' + ']:', 'Created responsive images for', src);
|
||||
let format = '';
|
||||
for (const key in metadata) {
|
||||
format = key;
|
||||
}
|
||||
|
||||
let lowsrc = (metadata[format].length > 1) ? metadata[format][1] : metadata[format][0];
|
||||
let highsrc = metadata[format][metadata[format].length - 1];
|
||||
let captionElement = (typeof caption !== 'undefined') ? `<figcaption>${markdown.render(caption)}</figcaption>` : '';
|
||||
let inlineStyling = (style === '-inline') ? ` style="flex: ${highsrc.width / highsrc.height}"` : '';
|
||||
return `<figure class="image ${style}"${inlineStyling}>
|
||||
<picture>
|
||||
${Object.values(metadata).map(imageFormat => {
|
||||
return ` <source type="${imageFormat[0].sourceType}" srcset="${imageFormat.map(entry => entry.srcset).join(', ')}" sizes="${sizes}">`;
|
||||
}).join('\n')}
|
||||
<img
|
||||
src="${lowsrc.url}"
|
||||
width="${lowsrc.width}"
|
||||
height="${lowsrc.height}"
|
||||
alt="${alt}"
|
||||
loading="lazy"
|
||||
decoding="async">
|
||||
</picture>${captionElement}</figure>`;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
@ -0,0 +1,168 @@
|
||||
const fs = require('fs');
|
||||
const hashString = require('./.eleventy.functions').hashString;
|
||||
|
||||
module.exports = {
|
||||
pack: (pack) => {
|
||||
function prettyDigits (number) {
|
||||
return number.toString().replace(/(?!^)(?=(?:\d{3})+(?:\.|$))/gm, ' ');
|
||||
}
|
||||
|
||||
if (pack) {
|
||||
let outputString = `<section class="pack">`;
|
||||
outputString += `<ul class="pack__list">`;
|
||||
pack.contents.forEach((category) => {
|
||||
const color = (typeof category.color !== 'undefined' && category.color.length) ? category.color : '#808080';
|
||||
outputString += `<li class="pack__list-item -overview small">
|
||||
<span class="pack__list-item__left"><svg class="icon -large" role="presentation" style="color: ${color}" aria-hidden="true" width="12" height="12" viewBox="0 0 24 24"><use xmlns:xlink="http://www.w3.org/1999/xlink" href="/assets/icons/${category.icon}.svg#icon"></use></svg></span><span class="pack__list-item__middle">${category.name}<span class="sr-only">:</span></span>
|
||||
<span class="pack__list-item__right" style="padding-right: 0.3rem">${prettyDigits(category.total_weight)}g</span>
|
||||
<div class="pack__list__bar" style="width: ${((category.total_weight / pack.total_weight) * 200).toFixed(3)}%; background: ${color};"></div>
|
||||
</li>`;
|
||||
});
|
||||
outputString += `</ul>`;
|
||||
outputString += `<ul class="statistics__list ${(pack.consumables_weight > 0) ? '-column-count-4' : ''}">
|
||||
<li class="statistics__list-item">
|
||||
<span class="statistics__list-item__label">Total vikt<span class="sr-only">:</span></span>
|
||||
<span class="statistics__list-item__value">${prettyDigits(pack.total_weight)}g</span>
|
||||
</li>
|
||||
<li class="statistics__list-item">
|
||||
<span class="statistics__list-item__label">Basvikt<span class="sr-only">:</span></span>
|
||||
<span class="statistics__list-item__value">${prettyDigits(pack.base_weight)}g</span>
|
||||
</li>
|
||||
<li class="statistics__list-item">
|
||||
<span class="statistics__list-item__label">På kroppen<span class="sr-only">:</span></span>
|
||||
<span class="statistics__list-item__value">${prettyDigits(pack.worn_weight)}g</span>
|
||||
</li>`;
|
||||
if (pack.consumables_weight > 0) {
|
||||
outputString += `<li class="statistics__list-item">
|
||||
<span class="statistics__list-item__label">Förbrukningsvaror<span class="sr-only">:</span></span>
|
||||
<span class="statistics__list-item__value">${prettyDigits(pack.consumables_weight)}g</span>
|
||||
</li>`;
|
||||
}
|
||||
outputString += `</ul>`;
|
||||
outputString += `<p><a href="https://www.packstack.io/pack/${pack.id}">Utrustningslistan ${pack.name} på Packstack</a></p>`;
|
||||
let equipmentString = ``;
|
||||
equipmentString += '<div id="equipment" class="pack__list-container -collapsed">';
|
||||
pack.contents.forEach((item_category) => {
|
||||
let packList = '';
|
||||
const color = (typeof item_category.color !== 'undefined' && item_category.color.length) ? item_category.color : '#ffffff';
|
||||
packList += `<ul class="pack__list">`;
|
||||
item_category.items.forEach((item) => {
|
||||
const quantity = (typeof item.quantity !== 'undefined') ? item.quantity : 1;
|
||||
let metaString = '';
|
||||
if (typeof item.worn !== 'undefined' && item.worn) {
|
||||
metaString = ` <span class="pack__list-item__bottom-right secondary"><svg class="icon" role="presentation" style="color: #5E35B1" aria-label="Buren" width="12" height="12" viewBox="0 0 24 24"><use xmlns:xlink="http://www.w3.org/1999/xlink" href="/assets/icons/tshirt-crew.svg#icon"></use></svg></span>`;
|
||||
} else if (typeof item.consumable !== 'undefined' && item.consumable) {
|
||||
metaString = ` <span class="pack__list-item__bottom-right secondary">Förbrukningsvara</span>`;
|
||||
}
|
||||
packList += `<li class="pack__list-item"><span class="pack__list-item__left secondary">${quantity}st</span> <span class="pack__list-item__bottom secondary">${item.item.name}<span class="sr-only">.</span></span> <span class="pack__list-item__middle">${(typeof item.item.brand !== 'undefined' && item.item.brand !== null) ? item.item.brand.name : ''} <span class="bold">${(typeof item.item.product !== 'undefined' && item.item.product !== null) ? item.item.product.name : ''}</span><span class="sr-only">.</span></span> <span class="pack__list-item__right">${prettyDigits(item.item.weight * quantity)}g</span>${metaString}</li>`;
|
||||
});
|
||||
equipmentString += `<span class="pack__label">
|
||||
<svg class="icon -large" role="presentation" style="color: ${color}" aria-hidden="true" width="12" height="12" viewBox="0 0 24 24"><use xmlns:xlink="http://www.w3.org/1999/xlink" href="/assets/icons/${item_category.icon}.svg#icon"></use></svg>
|
||||
<span class="uppercase semibold">${item_category.name}</span>
|
||||
<span class="secondary">(${prettyDigits(item_category.total_weight)}g)</span>
|
||||
</span>`;
|
||||
equipmentString += packList;
|
||||
equipmentString += `</ul>`;
|
||||
});
|
||||
equipmentString += '<button id="visa-utrustning" class="Button pack__list-button">Visa all utrustning</button></div>';
|
||||
outputString += equipmentString;
|
||||
outputString += `</section>`;
|
||||
outputString += `<script>document.getElementById('visa-utrustning').addEventListener('click', (event) => { let container = document.getElementById('equipment'); container.classList.remove('-collapsed'); container.removeChild(event.target); });</script>`;
|
||||
outputString += `<script>const pack = ${JSON.stringify(pack)};</script>`;
|
||||
return outputString;
|
||||
}
|
||||
return '';
|
||||
},
|
||||
packStatistics: (pack) => {
|
||||
function prettyDigits (number) {
|
||||
return number.toString().replace(/(?!^)(?=(?:\d{3})+(?:\.|$))/gm, ' ');
|
||||
}
|
||||
|
||||
if (pack) {
|
||||
let outputString = `<section class="packStatistics">`;
|
||||
outputString += `<ul class="pack__list">`;
|
||||
pack.contents.forEach((category) => {
|
||||
const color = (typeof category.color !== 'undefined' && category.color.length) ? category.color : 'var(--color__text)';
|
||||
outputString += `<li class="pack__list-item -overview small">
|
||||
<span class="pack__list-item__left"><svg class="icon -large" role="presentation" style="color: ${color}" aria-hidden="true" width="12" height="12" viewBox="0 0 24 24"><use xmlns:xlink="http://www.w3.org/1999/xlink" href="/assets/icons/${category.icon}.svg#icon"></use></svg></span><span class="pack__list-item__middle">${category.name}<span class="sr-only">:</span></span>
|
||||
<span class="pack__list-item__right" style="padding-right: 0.3rem">${prettyDigits(category.total_weight)}g</span>
|
||||
<div class="pack__list__bar" style="width: ${((category.total_weight / pack.total_weight) * 200).toFixed(3)}%; background: ${color};"></div>
|
||||
</li>`;
|
||||
});
|
||||
outputString += `</ul>`;
|
||||
outputString += `<ul class="statistics__list ${(pack.consumables_weight > 0) ? '-column-count-4' : ''}">
|
||||
<li class="statistics__list-item">
|
||||
<span class="statistics__list-item__label">Total vikt<span class="sr-only">:</span></span>
|
||||
<span class="statistics__list-item__value">${prettyDigits(pack.total_weight)}g</span>
|
||||
</li>
|
||||
<li class="statistics__list-item">
|
||||
<span class="statistics__list-item__label">Basvikt<span class="sr-only">:</span></span>
|
||||
<span class="statistics__list-item__value">${prettyDigits(pack.base_weight)}g</span>
|
||||
</li>
|
||||
<li class="statistics__list-item">
|
||||
<span class="statistics__list-item__label">På kroppen<span class="sr-only">:</span></span>
|
||||
<span class="statistics__list-item__value">${prettyDigits(pack.worn_weight)}g</span>
|
||||
</li>`;
|
||||
if (pack.consumables_weight > 0) {
|
||||
outputString += `<li class="statistics__list-item">
|
||||
<span class="statistics__list-item__label">Förbrukningsvaror<span class="sr-only">:</span></span>
|
||||
<span class="statistics__list-item__value">${prettyDigits(pack.consumables_weight)}g</span>
|
||||
</li>`;
|
||||
}
|
||||
outputString += `</ul>`;
|
||||
outputString += `<p><a class="Button" href="https://www.packstack.io/pack/${pack.id}">Utrustningslistan ${pack.name} på Packstack</a></p>`;
|
||||
outputString += `</section>`;
|
||||
return outputString;
|
||||
}
|
||||
return '';
|
||||
},
|
||||
packInventory: (pack) => {
|
||||
function prettyDigits (number) {
|
||||
return number.toString().replace(/(?!^)(?=(?:\d{3})+(?:\.|$))/gm, ' ');
|
||||
}
|
||||
|
||||
if (pack) {
|
||||
let outputString = `<section class="packStatistics">`;
|
||||
let equipmentString = ``;
|
||||
equipmentString += '<div id="equipment" class="pack__list-container -collapsed">';
|
||||
pack.contents.forEach((item_category) => {
|
||||
let packList = '';
|
||||
const color = (typeof item_category.color !== 'undefined' && item_category.color.length) ? item_category.color : 'var(--color__text)';
|
||||
packList += `<ul class="pack__list">`;
|
||||
item_category.items.forEach((item) => {
|
||||
const quantity = (typeof item.quantity !== 'undefined') ? item.quantity : 1;
|
||||
let metaString = '';
|
||||
if (typeof item.worn !== 'undefined' && item.worn) {
|
||||
metaString = ` <span class="pack__list-item__bottom-right secondary"><svg class="icon" role="presentation" style="color: #5E35B1" aria-label="Buren" width="12" height="12" viewBox="0 0 24 24"><use xmlns:xlink="http://www.w3.org/1999/xlink" href="/assets/icons/tshirt-crew.svg#icon"></use></svg></span>`;
|
||||
} else if (typeof item.consumable !== 'undefined' && item.consumable) {
|
||||
metaString = ` <span class="pack__list-item__bottom-right secondary">Förbrukningsvara</span>`;
|
||||
}
|
||||
if (typeof item.item.brand !== 'undefined' && item.item.brand !== null) {
|
||||
packList += `<li class="pack__list-item"><span class="pack__list-item__left secondary">${quantity}st</span> <span class="pack__list-item__bottom secondary">${item.item.name}<span class="sr-only">.</span></span> <span class="pack__list-item__middle"><span class="light">${(typeof item.item.brand !== 'undefined' && item.item.brand !== null) ? item.item.brand.name : ''}</span> <span class="bold">${(typeof item.item.product !== 'undefined' && item.item.product !== null) ? item.item.product.name : ''}</span><span class="sr-only">.</span></span> <span class="pack__list-item__right">${prettyDigits(item.item.weight * quantity)}g</span>${metaString}</li>`;
|
||||
} else {
|
||||
packList += `<li class="pack__list-item"><span class="pack__list-item__left secondary">${quantity}st</span> <span class="pack__list-item__middle bold">${item.item.name}<span class="sr-only">.</span></span> <span class="pack__list-item__bottom secondary">${(item.item.notes) ? item.item.notes : ''}<span class="sr-only">.</span></span> <span class="pack__list-item__right">${prettyDigits(item.item.weight * quantity)}g</span>${metaString}</li>`;
|
||||
}
|
||||
|
||||
});
|
||||
equipmentString += `<span class="pack__label">
|
||||
<svg class="icon -large" role="presentation" style="color: ${color}" aria-hidden="true" width="12" height="12" viewBox="0 0 24 24"><use xmlns:xlink="http://www.w3.org/1999/xlink" href="/assets/icons/${item_category.icon}.svg#icon"></use></svg>
|
||||
<span class="uppercase semibold">${item_category.name}</span>
|
||||
<span class="secondary">(${prettyDigits(item_category.total_weight)}g)</span>
|
||||
</span>`;
|
||||
equipmentString += packList;
|
||||
equipmentString += `</ul>`;
|
||||
});
|
||||
equipmentString += '<button id="visa-utrustning" class="Button pack__list-button hidden@no-js">Visa all utrustning</button></div>';
|
||||
outputString += equipmentString;
|
||||
outputString += `</section>`;
|
||||
outputString += `<script>document.getElementById('visa-utrustning').addEventListener('click', (event) => { let container = document.getElementById('equipment'); container.classList.remove('-collapsed'); container.removeChild(event.target); });</script>`;
|
||||
outputString += `<script>const pack = ${JSON.stringify(pack)};</script>`;
|
||||
return outputString;
|
||||
}
|
||||
return '';
|
||||
},
|
||||
checksum: function (filename) {
|
||||
const fileContent = fs.readFileSync(filename, 'utf8');
|
||||
return hashString(fileContent);
|
||||
}
|
||||
};
|
@ -0,0 +1,7 @@
|
||||
README.md
|
||||
.nojekyll
|
||||
_sass
|
||||
.git
|
||||
.cache
|
||||
.github
|
||||
.netlify
|
@ -0,0 +1,13 @@
|
||||
# Example for a .env configuration file
|
||||
# Replace with your values, then rename to `.env`
|
||||
|
||||
ELEVENTY_ENV=development
|
||||
WEBMENTIONIO_TOKEN=YOUR_WEBMENTIONIO_TOKEN_HERE
|
||||
STEAM_ID=YOUR_STEAM_ID_HERE
|
||||
STEAM_APIKEY=YOUR_STEAM_APIKEY_HERE
|
||||
THISDB_APIKEY=YOUR_THISDB_APIKEY_HERE
|
||||
THISDB_BUCKETID=YOUR_THISDB_BUCKETID_HERE
|
||||
STRAVA_CLIENTID=YOUR_STRAVA_CLIENTID_HERE
|
||||
STRAVA_SECRET=YOUR_STRAVA_SECRET_HERE
|
||||
BREWFATHER_ID=YOUR_BREWFATHER_ID_HERE
|
||||
BREWFATHER_APIKEY=YOUR_BREWFATHER_APIKEY_HERE
|
@ -0,0 +1,52 @@
|
||||
{
|
||||
"env": {
|
||||
"browser": true,
|
||||
"es6": true
|
||||
},
|
||||
"extends": "eslint:recommended",
|
||||
"rules": {
|
||||
"linebreak-style": ["error", "unix"],
|
||||
"quotes": [ "error", "single"],
|
||||
"array-bracket-spacing": 2,
|
||||
"brace-style": 2,
|
||||
"comma-dangle": 2,
|
||||
"comma-spacing": 1,
|
||||
"comma-style": [1, "last", { "exceptions": { "VariableDeclaration": true } }],
|
||||
"curly": 2,
|
||||
"dot-notation": [1, {"allowKeywords": false }],
|
||||
"eol-last": [2, "always"],
|
||||
"eqeqeq": 2,
|
||||
"indent": [1, 4],
|
||||
"key-spacing": 1,
|
||||
"keyword-spacing": 1,
|
||||
"new-cap": 0,
|
||||
"no-continue": 2,
|
||||
"no-empty": [2, {"allowEmptyCatch": true}],
|
||||
"no-multiple-empty-lines": [1, {"max": 2}],
|
||||
"no-eval": 2,
|
||||
"no-implied-eval": 2,
|
||||
"no-mixed-spaces-and-tabs": 2,
|
||||
"no-multi-str": 1,
|
||||
"no-new-func": 2,
|
||||
"no-plusplus": 2,
|
||||
"no-sequences": 2,
|
||||
"no-trailing-spaces": [1, { "skipBlankLines": true }],
|
||||
"no-undef": 2,
|
||||
"no-underscore-dangle": 0,
|
||||
"no-use-before-define": 2,
|
||||
"no-console": 1,
|
||||
"no-with": 2,
|
||||
"one-var": [1, "never"],
|
||||
"semi": 2,
|
||||
"semi-spacing": 1,
|
||||
"space-before-blocks": 1,
|
||||
"space-before-function-paren": [1, {"anonymous": "always", "named": "never"}],
|
||||
"space-in-parens": 1,
|
||||
"space-infix-ops": 1,
|
||||
"space-unary-ops": 1,
|
||||
"vars-on-top": 0,
|
||||
"wrap-iife": 2,
|
||||
"no-unused-vars": [1, {"vars": "local"}],
|
||||
"max-len": [1, {"code": 100, "tabWidth": 4, "ignoreTemplateLiterals": true, "ignoreRegExpLiterals": true, "ignoreStrings": false, "ignoreComments": true}]
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
name: Build 11ty on schedule (Netlify)
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 * * * *"
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Trigger build on Netlify
|
||||
run: curl -X POST ${{ secrets.NETLIFY_BUILD_HOOK }}
|
@ -0,0 +1,5 @@
|
||||
/node_modules/
|
||||
/_site/
|
||||
/.idea/
|
||||
/.cache/
|
||||
.env
|
@ -0,0 +1,3 @@
|
||||
[submodule "openring"]
|
||||
path = openring
|
||||
url = https://git.sr.ht/~sircmpwn/openring
|
@ -0,0 +1,61 @@
|
||||
{
|
||||
"extends": ["stylelint-config-standard", "stylelint-config-idiomatic-order"],
|
||||
"fix": true,
|
||||
"plugins": [
|
||||
"stylelint-scss"
|
||||
],
|
||||
"ignoreFiles": ["**/*.html"],
|
||||
"rules": {
|
||||
"at-rule-no-unknown": null,
|
||||
"scss/at-rule-no-unknown": true,
|
||||
"indentation": 4,
|
||||
"string-quotes": ["double", {"severity": "warning"}],
|
||||
"no-duplicate-selectors": true,
|
||||
"color-hex-case": ["lower", {"severity": "warning"}],
|
||||
"color-hex-length": "short",
|
||||
"color-named": ["always-where-possible"],
|
||||
"color-function-notation": ["legacy", {"severity": "warning"}],
|
||||
"selector-combinator-space-after": "always",
|
||||
"selector-attribute-quotes": "always",
|
||||
"selector-attribute-operator-space-before": "never",
|
||||
"selector-attribute-operator-space-after": "never",
|
||||
"selector-attribute-brackets-space-inside": "never",
|
||||
"declaration-block-trailing-semicolon": "always",
|
||||
"declaration-colon-space-before": "never",
|
||||
"declaration-colon-space-after": "always-single-line",
|
||||
"declaration-colon-newline-after": "always-multi-line",
|
||||
"number-leading-zero": "never",
|
||||
"function-url-quotes": "never",
|
||||
"font-weight-notation": "numeric",
|
||||
"font-family-name-quotes": "always-where-recommended",
|
||||
"comment-whitespace-inside": "always",
|
||||
"comment-empty-line-before": "always",
|
||||
"rule-empty-line-before": [
|
||||
"always",
|
||||
ignore: ["after-comment", "first-nested", "inside-block"]
|
||||
],
|
||||
"selector-pseudo-element-colon-notation": "single",
|
||||
"selector-pseudo-class-parentheses-space-inside": "never",
|
||||
"media-feature-range-operator-space-before": "never",
|
||||
"media-feature-range-operator-space-after": "always",
|
||||
"media-feature-parentheses-space-inside": "never",
|
||||
"media-feature-colon-space-before": "never",
|
||||
"media-feature-colon-space-after": "always",
|
||||
"no-descending-specificity": null,
|
||||
"no-eol-whitespace": [
|
||||
true,
|
||||
{
|
||||
"severity": "warning",
|
||||
ignore: ["empty-lines"]
|
||||
}
|
||||
],
|
||||
"block-no-empty": [
|
||||
true,
|
||||
{
|
||||
"severity": "warning",
|
||||
ignore: ["comments"]
|
||||
}
|
||||
]
|
||||
},
|
||||
"syntax": "scss"
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
---
|
||||
title: 404
|
||||
layout: page.njk
|
||||
templateClass: post-template
|
||||
type: page
|
||||
permalink: 404.html
|
||||
---
|
||||
<p class="lead">Sidan hittades inte.</p>
|
||||
<p><a href="/">Gå till huvudsidan</a> eller <a href="/kontakta-mig">kontakta mig</a> om du har frågor.</p>
|
||||
<p id="extra-url-info" class="hidden">Sidan kan eventuellt finnas arkiverad i <a class="url-not-found" data-url-prefix="https://web.archive.org/web/">Wayback Machine</a> eller så kan du <a class="url-not-found" data-url-prefix="https://duckduckgo.com/?q=">prova att söka efter sidan i en sökmotor</a>.</p>
|
||||
<script>
|
||||
[...document.querySelectorAll('.url-not-found')].forEach((element) => {
|
||||
element.href = element.getAttribute('data-url-prefix') + window.location.href;
|
||||
});
|
||||
document.getElementById('extra-url-info').classList.remove('hidden');
|
||||
</script>
|
@ -0,0 +1,5 @@
|
||||
# [gustavlindqvist.se](https://gustavlindqvist.se)
|
||||
[](https://app.netlify.com/sites/gustavlindqvist/deploys)
|
||||
|
||||
Source code for my personal website [gustavlindqvist.se](https://gustavlindqvist.se), built with [Eleventy](https://www.11ty.io).
|
||||
|
@ -0,0 +1,386 @@
|
||||
const fetch = require("@11ty/eleventy-fetch");
|
||||
|
||||
require('dotenv').config();
|
||||
|
||||
module.exports = async () => {
|
||||
const brewfatherId = process.env.BREWFATHER_ID;
|
||||
const apiKey = process.env.BREWFATHER_APIKEY;
|
||||
const rawAuthorizationHeader = Buffer.from(`${brewfatherId}:${apiKey}`);
|
||||
const authorizationHeader = rawAuthorizationHeader.toString('base64');
|
||||
|
||||
const asyncForEach = async function(array, callback) {
|
||||
for (let index = 0; index < array.length; index++) {
|
||||
await callback(array[index], index, array);
|
||||
}
|
||||
};
|
||||
|
||||
const calculateBalanceValue = (og, fg, ibu) => {
|
||||
og = og * 1000 - 1000;
|
||||
fg = fg * 1000 - 1000;
|
||||
const rte = (0.82 * fg) + (0.18 * og);
|
||||
return (0.8 * ibu / rte).toFixed(2);
|
||||
};
|
||||
|
||||
const convertSRMtoEBC = (srm) => {
|
||||
return Math.round(srm * 1.97);
|
||||
};
|
||||
|
||||
|
||||
const setEBCColor = (ebc) => {
|
||||
const mappingTable = [
|
||||
{
|
||||
'ebc': 8,
|
||||
"hexColor": '#f9e06c'
|
||||
},
|
||||
{
|
||||
'ebc': 12,
|
||||
"hexColor": '#eaaf1e'
|
||||
},
|
||||
{
|
||||
'ebc': 15,
|
||||
"hexColor": '#d68019'
|
||||
},
|
||||
{
|
||||
'ebc': 18,
|
||||
"hexColor": '#b7521a'
|
||||
},
|
||||
{
|
||||
'ebc': 20,
|
||||
"hexColor": '#9d3414'
|
||||
},
|
||||
{
|
||||
'ebc': 25,
|
||||
"hexColor": '#892515'
|
||||
},
|
||||
{
|
||||
'ebc': 30,
|
||||
"hexColor": '#731c0b'
|
||||
},
|
||||
{
|
||||
'ebc': 35,
|
||||
"hexColor": '#5b0b0a'
|
||||
},
|
||||
{
|
||||
'ebc': 40,
|
||||
"hexColor": '#450b0a'
|
||||
},
|
||||
{
|
||||
'ebc': 99999,
|
||||
"hexColor": '#240a0b'
|
||||
}
|
||||
];
|
||||
for (const mapping of mappingTable) {
|
||||
if (ebc < mapping.ebc) {
|
||||
return mapping.hexColor
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
};
|
||||
|
||||
const getBatches = async () => {
|
||||
const getBatch = async (batchId) => {
|
||||
try {
|
||||
const batch = await fetch('https://api.brewfather.app/v1/batches/' + batchId, {
|
||||
duration: "1h",
|
||||
type: "json",
|
||||
directory: ".cache",
|
||||
fetchOptions: {
|
||||
headers: {'Authorization': 'Basic ' + authorizationHeader}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
if (typeof batch.measuredOg && typeof |