fix audit mode, language switch, metadata

This commit is contained in:
milahu 2021-05-25 18:52:28 +02:00
parent 941210e351
commit de56fc2e37
9 changed files with 226 additions and 105 deletions

3
src/alchi-book/.babelrc Normal file
View File

@ -0,0 +1,3 @@
{
"plugins": ["preval"]
}

View File

@ -3,3 +3,4 @@ package-lock.json
trash
translate
pnpm-lock.yaml
backup/

View File

@ -61,6 +61,8 @@ module.exports = function(eleventyConfig) {
// make staticConfig reusable outside of eleventy
if (!eleventyConfig) return staticConfig;
const appRoot = require('app-root-path').path;
// https://v0-6-0.11ty.dev/docs/collections/
eleventyConfig.addCollection("pages", collection => (
collection
@ -179,41 +181,45 @@ module.exports = function(eleventyConfig) {
// https://www.npmjs.com/package/eleventy-plugin-transformdom
/*
for (let a = 0; a < element.attributes.length; a++) {
const attr = element.attributes[a];
elementNew.setAttribute(attr.name, attr.value); // copy attribute
}
*/
function getElementTransformer(selector, options = {}) {
const defaultOptions = {
name: 'div',
attributes: (e => Object.fromEntries(Array.from(e.attributes).map(a => [a.name, a.value]))),
class: (e => e.localName),
};
options = Object.assign(defaultOptions, options);
const getName = (typeof options.name == 'function') ? (e => options.name(e)) : (() => options.name);
const getAttributes = (
(typeof options.attributes == 'function') ? (e => options.attributes(e)) :
(typeof options.attributes == 'object') ? (() => options.attributes) :
(() => false)
);
const getExtraClass = (
(typeof options.class == 'function') ? (e => options.class(e)) :
(typeof options.class == 'string') ? (() => options.class) :
(() => false)
);
const domTransformer = {
selector,
transform: ({ elements, document }) => {
const defaultOptions = {
name: 'div',
attributes: (e => Object.fromEntries(Array.from(e.attributes).map(a => [a.name, a.value]))),
class: (e => e.localName),
};
options = Object.assign(defaultOptions, options);
const getName = (typeof options.name == 'function') ? (e => options.name(e, document)) : (() => options.name);
const getAttributes = (
(typeof options.attributes == 'function') ? (e => options.attributes(e, document)) :
(typeof options.attributes == 'object') ? (() => options.attributes) :
(() => false)
);
const getExtraClass = (
(typeof options.class == 'function') ? (e => options.class(e, document)) :
(typeof options.class == 'string') ? (() => options.class) :
(() => false)
);
for (let e = 0; e < elements.length; e++) {
const element = elements[e];
const nameNew = getName(element);
if (!nameNew) continue; // no replace
if (!nameNew || nameNew == e.localName) continue; // no replace
const attributesNew = getAttributes(element) || {};
const extraClassNew = getExtraClass(element);
const elementNew = document.createElement(nameNew);
for (let a = 0; a < element.attributes.length; a++) {
const attr = element.attributes[a];
elementNew.setAttribute(attr.name, attr.value); // copy attribute
}
for (const [name, value] of Object.entries(attributesNew)) {
elementNew.setAttribute(name, value);
}
@ -271,19 +277,62 @@ module.exports = function(eleventyConfig) {
return domTransformer;
}
const metadata = require(appRoot + '/src/_data/metadata.js');
eleventyConfig.addPlugin(transformDomPlugin, [
getElementTransformer('page', { class: 'page-element' }),
// <langs> -> <div class="langs">
getElementTransformer('langs', {
attributes: (e, document) => {
//e.children.forEach(cn => {
Array.prototype.forEach.apply(e.children, [ cn => {
// <en> -> <div lang="en"> etc.
const elementNew = document.createElement('div');
const langCode = cn.localName;
if (cn.attributes) {
for (let a = 0; a < cn.attributes.length; a++) {
const attr = cn.attributes[a];
elementNew.setAttribute(attr.name, attr.value); // copy attribute
}
}
elementNew.setAttribute('lang', langCode);
if (langCode != metadata.defaultLanguage) {
elementNew.setAttribute('class', 'hidden');
}
elementNew.innerHTML = cn.innerHTML;
cn.replaceWith(elementNew);
} ]);
}}
),
getElementTransformer('nw', { name: 'span', class: 'nowrap-element' }),
// TODO use language list from metadata.json
...(['de', 'en']).map(langKey =>
/*
// use language list from metadata.json
...(metadata.languages.split(' ')).map(langKey =>
getElementTransformer(langKey,
{ name: 'span', attributes: (e => ({ lang: e.localName })), class: (e => {
e.parentNode.classList.add('langs');
return false; // no extra class for the new element
}) })
//{ name: 'span', attributes: (e => ({ lang: e.localName })), class: (e => {
// <span> tag is too limiting, e.g. cannot contain <pre> tags -> use <div> tags
{
name: 'div',
attributes: e => {
const attrs = {};
attrs.lang = e.localName;
return attrs;
},
class: e => {
e.parentNode.classList.add('langs');
if (e.localName != metadata.defaultLanguage) {
return 'hidden';
}
return false; // no extra class for the new element
},
})
),
*/
getSvgInlineTransformer(),
]);

View File

@ -7,6 +7,9 @@
"wcag/h37": "off",
"void-style": "off",
"prefer-tbody": "off",
"doctype-style": "off"
"doctype-style": "off",
"svg-focusable": "off",
"wcag/h67": "off",
"element-permitted-content": "error"
}
}

View File

@ -4,6 +4,7 @@
"license": "CC0-1.0",
"private": true,
"description": "",
"type-zzzzzz": "module",
"scripts": {
"build": "cross-env NODE_ENV=production node scripts/npm-run-dev.js",
"build:vite": "vite build",
@ -17,6 +18,7 @@
"dev:vite": "vite",
"test": "npm-run-all -s test:**",
"test:a:html": "html-validate --config=config/htmlvalidate.config.json src/pages/",
"test-build": "html-validate --config=config/htmlvalidate.config.json build/index.html",
"postinstall": "patch-package"
},
"eleventy": {
@ -24,10 +26,13 @@
},
"devDependencies": {
"@11ty/eleventy": "github:11ty/eleventy#8a5162785c58864ef65308c0f0d890cade0b407f",
"@babel/core": "*",
"@fontsource/noto-mono": "*",
"@fontsource/noto-sans": "*",
"@rollup/plugin-babel": "*",
"@vitejs/plugin-legacy": "*",
"app-root-path": "*",
"babel-plugin-preval": "*",
"clean-css": "*",
"concurrently": "*",
"cross-env": "*",
@ -45,10 +50,12 @@
"npm-run-all": "*",
"open": "*",
"sirv-cli": "*",
"vite": "*"
"vite": "*",
"vite-babel-plugin": "*"
},
"dependencies": {
"diff": "*",
"he": "*",
"patch-package": "github:milahu/patch-package#pnpm-support-2",
"xregexp": "*"
}

View File

@ -25,36 +25,42 @@ const version = glob.sync(appRoot + '/src/pages/page-*.html')
console.log(`metadata.js: version = ${version}`)
module.exports = {
"languages": "de en", // TODO add new languages here
"primarylanguage": "de", // TODO change to en when en translation is complete
"title": "Your Blog Name", // TODO
const metadata = {};
//"version": "2021-03-15",
version,
metadata.languages = "en de hu"; // TODO add new languages here
"basename": {
"de": "pallas.ich-und-meine-sechs-freunde"
},
metadata.defaultLanguage = "en";
metadata.primarylanguage = metadata.defaultLanguage; // TODO rename to defaultLanguage
"url": "https://example.com/",
"description": "TODO",
"keywords": "TODO",
"feed": {
"subtitle": "I am writing about my experiences as a naval navel-gazer.",
"filename": "feed.xml",
"path": "/feed/feed.xml",
"id": "https://example.com/"
},
"jsonfeed": {
"path": "/feed/feed.json",
"url": "https://example.com/feed/feed.json"
},
"author": {
"name": "Milan Hauth",
"email": "milahu@gmail.com",
"url": ""
},
};
// TODO translate
metadata.basename = {};
metadata.basename.de = "pallas.ich-und-meine-sechs-freunde";
metadata.title = "Your Blog Name"; // TODO
metadata.version = version;
metadata.url = "https://example.com/";
// TODO SEO
metadata.description = "description"; // TODO
metadata.keywords = "keywords"; // TODO
metadata.feed = {};
metadata.feed.subtitle = "I am writing about my experiences as a naval navel-gazer.";
metadata.feed.filename = "feed.xml";
metadata.feed.path = "/feed/feed.xml";
metadata.feed.id = "https://example.com/";
metadata.jsonfeed = {};
metadata.jsonfeed.path = "/feed/feed.json";
metadata.jsonfeed.url = "https://example.com/feed/feed.json";
metadata.author = {};
metadata.author.name = "Milan Hauth";
metadata.author.email = "milahu@gmail.com";
metadata.author.url = "https://github.com/milahu";
//export default metadata;
module.exports = metadata;

View File

@ -11,9 +11,6 @@ span.tt {
}
body {
font-family: var(--sans-font);
font-size: var(--font-size);
line-height: 125%; /* default 121% in firefox */
/* display: flex; */
}
@ -74,14 +71,14 @@ body {
}
#book-title {
font-size: 14mm;
font-size: 4.66rem;
line-height: 100%;
height: auto;
text-align: center;
}
#book-subtitle {
font-size: 8mm;
font-size: 2.66rem;
line-height: 100%;
height: auto;
text-align: center;
@ -90,8 +87,8 @@ body {
#book-subsubtitle {
font-size: 5mm;
line-height: 5mm;
font-size: 1.66rem;
line-height: 100%;
height: auto;
text-align: center;
margin-bottom: 4mm;
@ -178,7 +175,8 @@ a {
top: 0; left: 0; right: 0;
background-color: white;
font-size: 110%;
font-size: calc(1.1 * var(--font-size));
z-index: 1; /* otherwise .pallas-sign-list is above the screen-menu */
}
button {
@ -579,7 +577,10 @@ button.active {
p { margin-bottom: 0.5em; }
/*p { margin-bottom: 0.5em; }*/
.page-element > .langs,
.page-element > .up > .langs,
.page-element > .low > .langs { margin-bottom: 0.5em; }
.smile {
display: inline-block;
@ -661,26 +662,45 @@ pre {
#pages-container.audit-mode p {
font-size: 110%; /* add room for manual annotations */
line-height: 220%; /* add room for manual annotations */
/*line-height: 270%; /* add room for manual annotations */
html {
font-family: var(--sans-font);
font-size: var(--font-size);
line-height: 125%; /* default 121% in firefox */
}
#pages-container.audit-mode {
/* audit mode: add room for manual annotations */
html.audit-mode {
font-size: calc(1.1 * var(--font-size));
}
html.audit-mode .langs > div {
line-height: 220%;
}
html.audit-mode #pages-container {
/* 1.4142 = Math.sqrt(2) */
--page-width: calc(1.4142 * var(--page-width-base));
--page-height: calc(1.4142 * var(--page-height-base));
}
#pages-container.audit-mode td {
padding-bottom: 1.5em;
html.audit-mode #pages-container td {
padding-bottom: 1.4em;
}
code {
border: none;
outline: none;
padding: 0;
margin: 0;
}
}
.langs > div {
display: inline;
}
.langs > .hidden {
display: none;
}

View File

@ -1,5 +1,12 @@
// TODO split into smaller files
// this is transformed by babel-plugin-preval
// see /config/vite.config.js and /.babelrc
const metadata = preval`
module.exports = require("../_data/metadata.js");
`;
console.log('metadata', metadata);
function get_meta() {
@ -503,7 +510,10 @@ function add_footers() {
const booklet_last_idx = (Math.ceil(num_pages / 4) * 4) - 1;
page_array.forEach((page_div, page_idx) => {
if (page_idx == 0 || page_idx == booklet_last_idx) return; // skip first and last page
// TODO translate
const span_page = `<span>Seite ${page_idx + 1}</span>`;
const span_license = `<span>${meta['license.shortname']}</span>`;
//const span_title = `<span>${meta.filename}</span>`;
const span_title = (page_idx % 2 == 0)
@ -549,25 +559,35 @@ function set_language(lang) {
current_lang = lang;
document.querySelectorAll('.langs').forEach(fragment => {
const children = Array.from(fragment.children);
const fragmentLanguages = children.reduce((acc, node) => {
if (node.lang) acc.add(node.lang);
return acc;
}, new Set());
if (fragment.localName == 'tr') {
console.dir(fragment);
// TODO more efficient?
// only change css class of body -> show/hide lang fragments with css
let langFound = 0;
Array.prototype.forEach.apply(fragment.children, [node => {
if (node.lang) {
if (node.lang == lang) {
node.classList.remove('hidden');
langFound++;
}
else node.classList.add('hidden');
}
}]);
if (langFound == 0) {
// set default lang
Array.prototype.forEach.apply(fragment.children, [node => {
if (node.lang) {
if (node.lang == metadata.defaultLanguage) {
node.classList.remove('hidden');
langFound++;
}
else node.classList.add('hidden');
}
}]);
}
//console.dir(fragment);
if (fragmentLanguages.has(lang)) {
children.forEach(node => {
if (node.lang) node.style.display = 'none';
});
children.forEach(node => {
if (node.lang == lang) node.style.display = ''; // TODO inline-block?
});
else if (langFound > 1) {
console.warn(`found multiple translations for lang "${lang}" in`, fragment);
}
});
});
document.querySelectorAll('.language-menu button').forEach(b => b.classList.remove('active'));
document.querySelector('#language-button-'+lang).classList.add('active');
@ -779,12 +799,12 @@ function handleChangeAuditMode(target) {
state.auditMode = target.checked;
//console.log(`audit mode: target.checked = ${target.checked}`);
if (state.auditMode) {
document.querySelector('#pages-container').classList.add('audit-mode');
document.querySelector('html').classList.add('audit-mode');
setPrintStyle({ print_orient: 'portrait' });
// TODO force "linear" layout
}
else {
document.querySelector('#pages-container').classList.remove('audit-mode');
document.querySelector('html').classList.remove('audit-mode');
setPrintStyle(); // reset
}
}

View File

@ -2,6 +2,7 @@
import { defineConfig } from "vite";
import legacy from "@vitejs/plugin-legacy";
import babel from "@rollup/plugin-babel";
// defined in .eleventy.js
const eleventyDirOutput = process.env.NODE_ELEVENTY_DIR_OUTPUT;
@ -14,13 +15,13 @@ if (!bundlerEntryFiles || bundlerEntryFiles.length == 0) throw new Error('error:
// This is critical: overwrite default index.html entry
// https://vitejs.dev/guide/build.html#multi-page-app
const assetPath = path => path.replace(/\.[^./]+$/, '');
const rollupOptions = {
input: Object.fromEntries(bundlerEntryFiles.map(entryFile => (
[assetPath(entryFile), (bundlerEntryDir + entryFile)]
))),
plugins: [],
// disable "filenames with hash" for assets
// so we can commit the "build" folder
// and dont have to delete/rename the old build files
@ -31,8 +32,8 @@ const rollupOptions = {
chunkFileNames: `assets/[name].js`,
assetFileNames: `assets/[name].[ext]`
},
};
// debug
Object.entries(rollupOptions.input).forEach(([key, path]) => {
console.log(`build.rollupOptions.input[${JSON.stringify(key)}]: ${path}`);
@ -42,13 +43,24 @@ console.log(`build.outDir: ${eleventyDirOutput}`);
// https://vitejs.dev/config/
export default defineConfig({
// This is not critical, but I include it because there are more HTML transforms via plugins, that templates must handle
// TODO: For legacy() to work without a hitch, we set a known @babel/standalone version in package.json
// Remove that once https://github.com/vitejs/vite/issues/2442 is fixed
//plugins: [legacy()],
// disabled the legacy plugin cos it generates "filenames with hash" for asset files
// https://github.com/vitejs/vite/issues/378
// sync with: alchi-book/config/eleventy.config.js footTags(file) {
plugins: [
// This is not critical, but I include it because there are more HTML transforms via plugins, that templates must handle
// TODO: For legacy() to work without a hitch, we set a known @babel/standalone version in package.json
// Remove that once https://github.com/vitejs/vite/issues/2442 is fixed
//legacy(),
// disabled the legacy plugin cos it generates "filenames with hash" for asset files
// https://github.com/vitejs/vite/issues/378
// sync with: alchi-book/config/eleventy.config.js footTags(file) {
babel({
// https://github.com/rollup/plugins/tree/master/packages/babel
// babel config is in /.babelrc
babelHelpers: "bundled",
include: ["src/js/main.js"],
}),
],
build: {
// This is important: Generate directly to _site and then assetsDir.