From 6a1243678aade48e41f1733f7937f9376746462a Mon Sep 17 00:00:00 2001 From: "Matthias@Dell" Date: Mon, 27 Nov 2023 11:49:07 +0100 Subject: [PATCH] add example --- example/.dependencies/de/de-only.html.d | 1 + example/.dependencies/de/index.html.d | 1 + example/.dependencies/en/en-only.html.d | 1 + example/.dependencies/en/index.html.d | 1 + example/.dependencies/style/main.css.d | 1 + example/.sitemap.pkl | 1 + example/Makefile | 375 ++++++++ example/build/de/de-only.html | 32 + example/build/de/index.html | 52 + example/build/en/en-only.html | 32 + example/build/en/index.html | 52 + .../build/favicon/android-chrome-192x192.png | Bin 0 -> 9181 bytes .../build/favicon/android-chrome-512x512.png | Bin 0 -> 44652 bytes .../favicon/apple-touch-icon-180x180.png | Bin 0 -> 8728 bytes example/build/favicon/favicon-16x16.png | Bin 0 -> 804 bytes example/build/favicon/favicon-32x32.png | Bin 0 -> 1229 bytes example/build/favicon/favicon-48x48.png | Bin 0 -> 1337 bytes example/build/favicon/favicon.ico | Bin 0 -> 15086 bytes example/build/favicon/mstile-150x150.png | Bin 0 -> 6535 bytes example/build/resources/example.svg | 64 ++ example/build/resources/favicon.png | 64 ++ example/build/script/test.js | 1 + example/build/sitemap.xml | 3 + example/build/style/main.css | 42 + example/html-preprocessor | 904 ++++++++++++++++++ example/nginx.conf | 34 + example/nginx.pid | 1 + example/src/common/index.html | 37 + example/src/de/de-only.html | 26 + example/src/en/en-only.html | 25 + example/src/include/head.html | 32 + example/src/include/index.de.md | 14 + example/src/include/index.en.md | 13 + example/src/include/style/images.sass | 9 + example/src/include/style/sidenav.sass | 12 + example/src/resources/example.svg | 64 ++ example/src/resources/favicon.png | 64 ++ example/src/script/test.js | 1 + example/src/style/main.sass | 15 + 39 files changed, 1974 insertions(+) create mode 100644 example/.dependencies/de/de-only.html.d create mode 100644 example/.dependencies/de/index.html.d create mode 100644 example/.dependencies/en/en-only.html.d create mode 100644 example/.dependencies/en/index.html.d create mode 100644 example/.dependencies/style/main.css.d create mode 100644 example/.sitemap.pkl create mode 100644 example/Makefile create mode 100644 example/build/de/de-only.html create mode 100644 example/build/de/index.html create mode 100644 example/build/en/en-only.html create mode 100644 example/build/en/index.html create mode 100644 example/build/favicon/android-chrome-192x192.png create mode 100644 example/build/favicon/android-chrome-512x512.png create mode 100644 example/build/favicon/apple-touch-icon-180x180.png create mode 100644 example/build/favicon/favicon-16x16.png create mode 100644 example/build/favicon/favicon-32x32.png create mode 100644 example/build/favicon/favicon-48x48.png create mode 100644 example/build/favicon/favicon.ico create mode 100644 example/build/favicon/mstile-150x150.png create mode 100644 example/build/resources/example.svg create mode 100644 example/build/resources/favicon.png create mode 100644 example/build/script/test.js create mode 100644 example/build/sitemap.xml create mode 100644 example/build/style/main.css create mode 100755 example/html-preprocessor create mode 100644 example/nginx.conf create mode 100644 example/nginx.pid create mode 100644 example/src/common/index.html create mode 100644 example/src/de/de-only.html create mode 100644 example/src/en/en-only.html create mode 100644 example/src/include/head.html create mode 100644 example/src/include/index.de.md create mode 100644 example/src/include/index.en.md create mode 100644 example/src/include/style/images.sass create mode 100644 example/src/include/style/sidenav.sass create mode 100644 example/src/resources/example.svg create mode 100644 example/src/resources/favicon.png create mode 100644 example/src/script/test.js create mode 100644 example/src/style/main.sass diff --git a/example/.dependencies/de/de-only.html.d b/example/.dependencies/de/de-only.html.d new file mode 100644 index 0000000..3d2a3a8 --- /dev/null +++ b/example/.dependencies/de/de-only.html.d @@ -0,0 +1 @@ +build/de/de-only.html: src/include/head.html src/de/de-only.html \ No newline at end of file diff --git a/example/.dependencies/de/index.html.d b/example/.dependencies/de/index.html.d new file mode 100644 index 0000000..aea605f --- /dev/null +++ b/example/.dependencies/de/index.html.d @@ -0,0 +1 @@ +build/de/index.html: src/include/head.html src/include/index.de.md src/common/index.html \ No newline at end of file diff --git a/example/.dependencies/en/en-only.html.d b/example/.dependencies/en/en-only.html.d new file mode 100644 index 0000000..eaaf51b --- /dev/null +++ b/example/.dependencies/en/en-only.html.d @@ -0,0 +1 @@ +build/en/en-only.html: src/include/head.html src/en/en-only.html \ No newline at end of file diff --git a/example/.dependencies/en/index.html.d b/example/.dependencies/en/index.html.d new file mode 100644 index 0000000..4536967 --- /dev/null +++ b/example/.dependencies/en/index.html.d @@ -0,0 +1 @@ +build/en/index.html: src/include/head.html src/include/index.en.md src/common/index.html \ No newline at end of file diff --git a/example/.dependencies/style/main.css.d b/example/.dependencies/style/main.css.d new file mode 100644 index 0000000..b81d5cc --- /dev/null +++ b/example/.dependencies/style/main.css.d @@ -0,0 +1 @@ +build/style/main.css: /home/matth/Projekte/web/bUwUma/example/src/style/main.sass /home/matth/Projekte/web/bUwUma/example/src/include/style/images.sass /home/matth/Projekte/web/bUwUma/example/src/include/style/sidenav.sass diff --git a/example/.sitemap.pkl b/example/.sitemap.pkl new file mode 100644 index 0000000..e2ecf72 --- /dev/null +++ b/example/.sitemap.pkl @@ -0,0 +1 @@ +€}”. \ No newline at end of file diff --git a/example/Makefile b/example/Makefile new file mode 100644 index 0000000..e76047f --- /dev/null +++ b/example/Makefile @@ -0,0 +1,375 @@ +# ABOUT +# - In this Makefile, 'building a file' means: +# - if the file has a '.html' extension: run the html preprocessor on the file and place the output in the output directory +# - elif the file has a '.sass' or '.scss' extension: run the sass compiler on the file and place the output in the output directory +# - else: copy the file to the output directory +# - Folder structure from source directories will be preserved in the output directory +# - Abbreviations: +# - FLS: files +# - DIR: directory +# - SRC: source +# - LANG: language +# - PP: preprocessor +# - DEP: dependency + +# +# NORMAL SETTINGS +# change these to fir your project +# + +# root dir for the project, most other paths are relative to PROJECT_DIR +# [absolute or relative to current working directory] +PROJECT_DIR = src + +# path where final website will be in +# [absolute or relative to current working directory] +OUT_DIR = build + +# SOURCE FILES: +# all SRC_FLS and all files (recursively) in the SRC_DIRS will be built +# all files in PROJECT_DIR (not recursively) are source files +# [relative to PROJECT_DIR] +SRC_DIRS = de en script +SRC_FLS = + +# CSS FILES: +# directories which may contain sass and scss to compile sass to a correspondig css in OUT_DIR/CSS_DIR (also css, it will simply be copied) +# [relative to PROJECT_DIR] +CSS_DIRS = style +CSS_FILES = + +# RESOURCE FILES: +# all RESOURCE_FLS and all files in the RESOURCE_DIRS will be copied to OUT_DIR +# [relative to PROJECT_DIR] +RESOURCE_DIRS = resources +RESOURCE_FLS = + +# MULTI-LANG SOURCE FILES: +# the files in COMMON_DIR will be built for all LANGS: +# for example: +# LANGS = de en +# PROJECT_DIR/COMMON_DIR/home.html +# -> OUT_DIR/de/home.html +# -> OUT_DIR/en/home.html +# foreach html-file in COMMON_DIR: +# foreach lang in LANGS: +# run HTML_PP_CMD with --var lang=lang on file and output to OUT_DIR without the COMMON_DIR prefix, so COMMON_DIR/subdir/file.html -> OUT_DIR/lang/subdir/file.html +# For all .html files, the proprocessor will make the variable `lang` available, for example lang=de +# All non-html files will handled the same way, but without the preprocessor being run on them. They are simply copied. +# leave COMMON_DIR blank to disable multi-lang feature +# [relative to PROJECT_DIR] +COMMON_DIR = common +LANGS = de en + +# FAVICON +# image from which the favicons will be generated +# leave FAVICON_SRC blank to not generate favicons +# [relative to PROJECT_DIR] +FAVICON_SRC = resources/favicon.png +# directory where all genreated favicons will be placed +# [relative to OUT_DIR] +FAVICON_DIR = favicon +# in addition to the ones below, a favicon.ico containing the 16x16, 32x32 and 48x48will be generated +# all apple-touch-icon-XXxXX.png sizes +APPLE_ICON_SIZES = 180x180 +# all mstile-XXxXX.png sizes +WINDOWS_ICON_SIZES = 150x150 +# all android-chrome-XXxXX.png sizes +ANDROID_ICON_SIZES = 192x192 512x512 +# all favicon-XXxXX.png sizes +FAVICON_ICON_SIZES = 16x16 32x32 48x48 + +# THUMBNAILS: +# thumbnails for all resource files having an extension in THUMB_FOR_TYPES will be generated and placed relative to THUMB_OUT_DIR +# leave THUMB_OUT_DIR blank to not generate thumbnails +# [relative to OUT_DIR] +THUMB_OUT_DIR = +# build thumbnails for these types: supported: mp3, flac, wav, pdf and all image formats that magick can handle +THUMB_FOR_TYPES = png gif jpg jpeg webp pdf mp4 mp3 flac wav +# filetype for the thumbnails. (pdfs will always have .jpg) +THUMB_TYPE = jpg +# size for the thumbnails (not respected by pdf) +THUMB_SIZE = 300 + +# SITEMAP +# leave SITEMAP blank to not generate a sitemap +# [relative to OUT_DIR] +SITEMAP = sitemap.xml +# base url of the website, without trailing / +WEBSITE_URL = https://example.com +# file required during build process for sitemap generation [absolute or relative to current working directory] +SITEMAP_TEMP_FILE = .sitemap.pkl +# comment to keep the file extension on sitemap entries +SITEMAP_REMOVE_EXT = 1 + +# PREPROCESSOR +# path to of the files that should be included +# [relative to PROJECT_DIR] +INCLUDE_DIR = include +# additional search paths passed to sass compiler +# [relative to PROJECT_DIR] +SASS_INCLUDE_DIRS = include/style + + +# ADVANCED +# the command to run the html preprocessor +HTML_PP_CMD = python3 html-preprocessor --exit-on light +# command to compile sass and scss files with +# --indented is added for sass and --no-indented for scss +# --source-maps-urls=absolute is appended for generating dependency files +SASS_CMD = sass --color + +# [absolute or relative to current working directory] +DEP_DIR = .dependencies + + +# +# NOT SETTINGS ANYMORE +# DO NOT CHANGE ANYTHING HERE UNLESS YOU KNOW WHAT YOU ARE DOING! +# +# all variables starting with _ are relative to PROJECT_DIR + +# make everything relative to PROJECT_DIR +_SRC_DIRS = $(addprefix $(PROJECT_DIR)/, $(SRC_DIRS)) +_SRC_FLS = $(addprefix $(PROJECT_DIR)/, $(SRC_FLS)) +_CSS_FLS = $(addprefix $(PROJECT_DIR)/, $(CSS_FLS)) +_CSS_DIRS = $(addprefix $(PROJECT_DIR)/, $(CSS_DIRS)) +_SASS_INCLUDE_DIRS = $(addprefix $(PROJECT_DIR)/, $(SASS_INCLUDE_DIRS)) +_RES_DIRS = $(addprefix $(PROJECT_DIR)/, $(RESOURCE_DIRS)) +_RES_FLS = $(addprefix $(PROJECT_DIR)/, $(RESOURCE_FLS)) +_COMMON_DIR = $(addprefix $(PROJECT_DIR)/, $(COMMON_DIR)) +_INCLUDE_DIR = $(addprefix $(PROJECT_DIR)/, $(INCLUDE_DIR)) + +# NORMAL SRC +# all SRC_DIRS + CSS_DIRS + all subdirs of each srcdir +_SRC_SUB_DIRS = $(foreach srcdir, $(_SRC_DIRS) $(_CSS_DIRS), $(shell find $(srcdir)/ -type d 2>/dev/null)) +# add files in project dir +_SRC_FLS += $(shell find $(PROJECT_DIR)/ -maxdepth 1 -type f) +# add files src dirs, recursively +_SRC_FLS += $(foreach srcdir, $(_SRC_DIRS), $(shell find $(srcdir)/ -type f 2>/dev/null)) +_CSS_FLS += $(foreach srcdir, $(_CSS_DIRS), $(shell find $(srcdir)/ -type f 2>/dev/null)) + +OUT_DIRS = $(OUT_DIR)/ $(patsubst $(PROJECT_DIR)/%, $(OUT_DIR)/%, $(_SRC_SUB_DIRS)) +# path of the (css/sass) source files after being processed +OUT_FLS = $(patsubst $(PROJECT_DIR)/%, $(OUT_DIR)/%, $(_SRC_FLS)) +OUT_FLS += $(patsubst $(PROJECT_DIR)/%, $(OUT_DIR)/%, $(foreach cssfile, $(_CSS_FLS), $(shell echo $(cssfile) | sed 's/\.s[ac]ss$$/.css/'))) + +# RESOURCES +_RES_SUB_DIRS = $(foreach srcdir, $(_RES_DIRS), $(shell find $(srcdir)/ -type d 2>/dev/null)) +_RES_FLS += $(foreach srcdir, $(_RES_DIRS), $(shell find $(srcdir)/ -type f 2>/dev/null)) +RES_OUT_DIRS = $(OUT_DIR)/ $(patsubst $(PROJECT_DIR)/%, $(OUT_DIR)/%, $(_RES_SUB_DIRS)) +RES_OUT_FLS = $(patsubst $(PROJECT_DIR)/%, $(OUT_DIR)/%, $(_RES_FLS)) + +# MULTILANG +ifdef COMMON_DIR +_ML_SRC_FLS = $(shell find $(_COMMON_DIR)/ -type f) +_ML_SRC_SUB_DIRS= $(shell find $(_COMMON_DIR)/ -type d) +# will contain one subdir for each lang, each of which contains every file from ML_SRC_FLS +ML_OUT_DIR = $(OUT_DIR) +ML_OUT_LANG_DIRS= $(foreach lang, $(LANGS), $(addprefix $(ML_OUT_DIR)/, $(lang))) +ML_OUT_DIRS = $(foreach lang, $(LANGS), $(patsubst $(_COMMON_DIR)/%, $(ML_OUT_DIR)/$(lang)/%, $(_ML_SRC_SUB_DIRS))) +ML_OUT_FLS = $(foreach lang, $(LANGS), $(patsubst $(_COMMON_DIR)/%, $(ML_OUT_DIR)/$(lang)/%, $(_ML_SRC_FLS))) +endif + +ifdef FAVICON_DIR +FAVICON_OUT_DIR = $(addprefix $(OUT_DIR)/,$(FAVICON_DIR)) +else +FAVICON_OUT_DIR = $(OUT_DIR) +endif + +ifdef FAVICON_SRC +_FAVICON = $(addprefix $(PROJECT_DIR)/,$(FAVICON_SRC)) +FAVICON_ICO = $(FAVICON_OUT_DIR)/favicon.ico +APPLE_ICONS = $(addsuffix .png,$(addprefix apple-touch-icon-,$(APPLE_ICON_SIZES))) +WINDOWS_ICONS = $(addsuffix .png,$(addprefix mstile-,$(WINDOWS_ICON_SIZES))) +ANDROID_ICONS = $(addsuffix .png,$(addprefix android-chrome-,$(ANDROID_ICON_SIZES))) +FAVICON_ICONS = $(addsuffix .png,$(addprefix favicon-,$(FAVICON_ICON_SIZES))) +FAVICONS_PNG = $(addprefix $(FAVICON_OUT_DIR)/,$(APPLE_ICONS) $(WINDOWS_ICONS) $(ANDROID_ICONS) $(FAVICON_ICONS)) +FAVICONS = $(FAVICONS_PNG) $(FAVICON_ICO) +endif + +ifdef THUMB_OUT_DIR +# files for which to generate thumbnails +_THUMB_FLS = $(filter $(foreach type, $(THUMB_FOR_TYPES), %.$(type)), $(_RES_FLS)) +THUMB_OUT_FLS = $(addsuffix .jpg, $(basename $(patsubst $(PROJECT_DIR)/%, $(OUT_DIR)/$(THUMB_OUT_DIR)/%, $(_THUMB_FLS)))) +THUMB_OUT_DIRS = $(sort $(dir $(THUMB_OUT_FLS))) # sort for removing duplicates +endif + +# needed for creating them +_DEP_DIRS = $(sort $(patsubst $(OUT_DIR)/%, $(DEP_DIR)/%, $(OUT_DIRS) $(ML_OUT_DIRS))) +# needed for reading +_DEP_FLS = $(shell find $(DEP_DIR) -type f -name '*.d' 2>/dev/null) + +ifdef SITEMAP + SITEMAP_OUT = $(addprefix $(OUT_DIR)/, $(SITEMAP)) + HTML_PP_CMD += --sitemap-temp-file "$(SITEMAP_TEMP_FILE)" --sitemap-base-url $(WEBSITE_URL) --sitemap-webroot-dir "$(OUT_DIR)" +endif +ifdef SITEMAP_REMOVE_EXT + HTML_PP_CMD += --sitemap-remove-ext +endif +# SASS, add load-paths +_SASS_CMD = $(SASS_CMD) $(foreach includedir, $(_SASS_INCLUDE_DIRS), --load-path=$(includedir)) --source-map-urls=absolute + +# PRINTING +FMT_VAR_SRC ="Variable '\e[1;34m%s\e[0m': \e[0;33m%s\e[0m\n" +FMT_VAR_OUT ="Variable '\e[1;34m%s\e[0m': \e[0;35m%s\e[0m\n" +FMT_DIR ="\e[1;34mMaking directory\e[0m: \e[0;35m%s\e[0m\n" +FMT_OUT_HTML ="\e[1;34mBuilding html\e[0m: \e[1;33m%s\e[0m at \e[1;35m%s\e[0m\n" +FMT_OUT_CSS ="\e[1;34mBuilding css\e[0m: \e[1;33m%s\e[0m at \e[1;35m%s\e[0m\n" +FMT_OUT_THUMB ="\e[1;34mGenerating thumbnail\e[0m: \e[1;33m%s\e[0m at \e[1;35m%s\e[0m\n" +FMT_OUT_SITEMAP ="\e[1;34mGenerating sitemap\e[0m: \e[1;35m%s\e[0m\n" +FMT_OUT_FAVICON ="\e[1;34mGenerating favicon\e[0m: \e[1;33m%s\e[0m at \e[1;35m%s\e[0m\n" +FMT_OUT_OTHER ="\e[1;34mBuilding\e[0m: \e[1;33m%s\e[0m at \e[1;35m%s\e[0m\n" +FMT_OUT_ML_HTML ="\e[1;34mBuilding html\e[0m in lang \e[1;34m%s\e[0m: \e[1;33m%s\e[0m at \e[1;35m%s\e[0m\n" +FMT_OUT_ML_OTHER ="\e[1;34mBuilding\e[0m in lang \e[1;34m%s\e[0m: \e[1;33m%s\e[0m at \e[1;35m%s\e[0m\n" +# .SUFFIXES: +# .SUFFIXES: .html .md + +.PHONY: default normal multilang resources sitemap favicons thumbnails print start stop clean cleaner + +.DEFAULT_GOAL = all + +# include all the dependency makefiles +include $(_DEP_FLS) + +all: normal multilang resources thumbnails sitemap favicons +normal: $(OUT_FLS) +sitemap: $(SITEMAP_OUT) +favicons: $(FAVICONS) $(FAVICON_ICO) +multilang: $(ML_OUT_FLS) +resources: $(RES_OUT_FLS) +thumbnails: $(THUMB_OUT_FLS) + +print: + @printf $(FMT_VAR_SRC) "PROJECT_DIR" "$(PROJECT_DIR)" + @printf $(FMT_VAR_OUT) "OUT_DIRS" "$(OUT_DIRS)" + @printf $(FMT_VAR_SRC) "_INCLUDE_DIR" "$(_INCLUDE_DIR)" + @printf $(FMT_VAR_SRC) "_SRC_FLS" "$(_SRC_FLS)" + @printf $(FMT_VAR_OUT) "OUT_FLS" "$(OUT_FLS)" + @printf $(FMT_VAR_SRC) "_RES_FLS" "$(_RES_FLS)" + @printf $(FMT_VAR_OUT) "RES_OUT_FLS" "$(RES_OUT_FLS)" + @printf $(FMT_VAR_OUT) "_CSS_FLS" "$(_CSS_FLS)" +ifdef COMMON_DIR + @printf $(FMT_VAR_SRC) "_ML_SRC_FLS" "$(_ML_SRC_FLS)" + @printf $(FMT_VAR_OUT) "ML_OUT_FLS" "$(ML_OUT_FLS)" +endif + @printf $(FMT_VAR_SRC) "_DEP_FLS" "$(_DEP_FLS)" +ifdef THUMB_OUT_DIR + @printf $(FMT_VAR_SRC) "THUMB_OUT_DIR" "$(THUMB_OUT_DIR)" + @printf $(FMT_VAR_OUT) "_THUMB_FLS" "$(_THUMB_FLS)" + @printf $(FMT_VAR_OUT) "THUMB_OUT_FLS" "$(THUMB_OUT_FLS)" + @printf $(FMT_VAR_OUT) "THUMB_OUT_DIRS" "$(THUMB_OUT_DIRS)" +endif + @# @printf $(FMT_VAR_SRC) "y" "$(y)" + +# DIRECTORIES +$(sort $(ML_OUT_DIRS) $(_DEP_DIRS) $(RES_OUT_DIRS) $(OUT_DIRS) $(THUMB_OUT_DIRS) $(FAVICON_OUT_DIR)): + @printf $(FMT_DIR) "$@" + @mkdir -p $@ + +# MULTILANG RULES +ifdef COMMON_DIR +# $@ is the target to trigger the rule, but all languages have to be built now +$(foreach out_dir, $(ML_OUT_LANG_DIRS), $(out_dir)/%.html): $(_COMMON_DIR)/%.html | $(ML_OUT_DIRS) $(_DEP_DIRS) + @RAW_TARGET=`echo $@ $(foreach lang, $(LANGS), | sed 's|$(ML_OUT_DIR)/$(lang)/||')`;\ + for lang in $(LANGS); do \ + target=$(ML_OUT_DIR)/$$lang/$$RAW_TARGET;\ + printf $(FMT_OUT_ML_HTML) "$$lang" "$<" "$$target"; \ + $(HTML_PP_CMD) --input "$<" --output "$$target" --var include_dir=$(_INCLUDE_DIR) --var lang=$$lang --output-deps "`echo $${target}.d | sed 's|$(OUT_DIR)/|$(DEP_DIR)/|'`"; \ + done + + +# rule for all not html files +$(foreach out_dir, $(ML_OUT_LANG_DIRS), $(out_dir)/%): $(_COMMON_DIR)/% | $(ML_OUT_DIRS) + @lang=`echo $(patsubst $(ML_OUT_DIR)/%, %, $@) | awk -F "/" '{print $$1}'`; \ + printf $(FMT_OUT_ML_OTHER) "$$lang" "$<" "$@" ; \ + cp $< $@ +endif + +ifdef FAVICONS +# must be first +$(FAVICON_ICO): $(_FAVICON) | $(FAVICON_OUT_DIR) + @printf $(FMT_OUT_FAVICON) "$<" "$@" + @convert "$<" -define icon:auto-resize=16,32,48 "$@" + +$(FAVICONS_PNG): $(_FAVICON) | $(FAVICON_OUT_DIR) + @printf $(FMT_OUT_FAVICON) "$<" "$@" + @# resize to 512x512 and pad with transparency in case resize did not resize to correct size + @size=$$(echo "$@" | grep -o -P '\d{2,4}x\d{2,4}');\ + convert "$<" -resize "$${size}" -background none -gravity center -extent "$${size}" "$@" +endif + + +# THUMBNAILS +$(OUT_DIR)/$(THUMB_OUT_DIR)/%.jpg: | $(THUMB_OUT_DIRS) + @fulltarget="$@"; \ + target="$(patsubst $(OUT_DIR)/$(THUMB_OUT_DIR)/%.jpg,%,$@)"; \ + sources=($(_THUMB_FLS)); \ + source=$$(printf "%s\n" $${sources[@]} | grep "$$target"'\.'); \ + printf $(FMT_OUT_THUMB) "$$source" "$$fulltarget"; \ + case "$${source##*.}" in \ + "mp4-use-magick-as-well") ffmpegthumbnailer -i "$$source" -o "$$fulltarget" -s 300 -q 5;; \ + "pdf") pdftoppm -f 1 -singlefile -jpeg -r 50 "$$source" "$${fulltarget%.*}";; \ + "mp3"|"flac"|"wav") ffmpeg -hide_banner -i "$$source" "$$fulltarget" -y >/dev/null;; \ + *) magick "$${source}[0]" -thumbnail '$(THUMB_SIZE)x$(THUMB_SIZE)>' "$@";; \ + esac + +# SITEMAP +ifdef SITEMAP_OUT +$(SITEMAP_OUT): $(OUT_FLS) $(ML_OUT_FLS) # build sitemap after all other files + @printf $(FMT_OUT_SITEMAP) "$@" + @$(HTML_PP_CMD) --sitemap-generate "$@" +endif + + +# +# (NORMAL/RE-)SOURCE RULES +# +$(OUT_DIR)/%.html: $(PROJECT_DIR)/%.html | $(OUT_DIRS) $(_DEP_DIRS) + @printf $(FMT_OUT_HTML) "$<" "$@"; + @$(HTML_PP_CMD) --input "$<" --output "$@" --var include_dir=$(_INCLUDE_DIR) --output-deps "$(subst $(DEP_DIR)/$(PROJECT_DIR), $(DEP_DIR), $(DEP_DIR)/$<.d)"; + @# remove comments and empty lines. two separate lines bc the substitution might create new empty lines + @#awk -i inplace '{FS="" sub(//,"")}1' $@ + @#awk -i inplace '{if (NF != 0) print}' $@ + + +# SASS +$(OUT_DIR)/%.css: $(PROJECT_DIR)/%.sass | $(OUT_DIRS) $(_DEP_DIRS) + @printf $(FMT_OUT_CSS) "$<" "$@"; + @$(_SASS_CMD) --indented "$<" "$@" || { rm "$@"; exit 1; } + @depfile=$(patsubst $(OUT_DIR)/%,$(DEP_DIR)/%,$@).d; echo -n "$@: " > "$$depfile"; \ + jq -r '.sources | @sh' $@.map | tr -d \' | sed 's|file://||g' >> "$$depfile"; \ + rm $@.map + @# generate a dependecy file from the source map and delete the map +# SCSS +$(OUT_DIR)/%.css: $(PROJECT_DIR)/%.scss | $(OUT_DIRS) $(_DEP_DIRS) + @printf $(FMT_OUT_CSS) "$<" "$@"; + @$(_SASS_CMD) --no-indented "$<" "$@" || { rm "$@"; exit 1; } + @# generate a dependecy file from the source map and delete the map + @depfile=$(patsubst $(OUT_DIR)/%,$(DEP_DIR)/%,$@).d; echo -n "$@: " > "$$depfile"; \ + jq -r '.sources | @sh' $@.map | tr -d \' | sed 's|file://||g' >> "$$depfile"; \ + rm $@.map + +# this rule must be last! +$(OUT_DIR)/%: $(PROJECT_DIR)/% | $(OUT_DIRS) $(RES_OUT_DIRS) + @printf $(FMT_OUT_OTHER) "$<" "$@" + @cp -r $< $@ + + + +# .DEFAULT: +# @echo "MISSING RULE: $@" + +start: + /usr/sbin/nginx -c nginx.conf -p $(shell pwd)& + firefox http://localhost:8080/ +stop: + killall nginx + +clean: + -@rm $(OUT_FLS) $(ML_OUT_FLS) $(SITEMAP_TEMP_FILE) $(SITEMAP) 2>/dev/null + -@rm -r $(DEP_DIR) 2>/dev/null + +cleaner: + -@rm -r $(OUT_DIR) + -@rm -r $(DEP_DIR) 2>/dev/null diff --git a/example/build/de/de-only.html b/example/build/de/de-only.html new file mode 100644 index 0000000..8d21727 --- /dev/null +++ b/example/build/de/de-only.html @@ -0,0 +1,32 @@ + + + + + + + + + + Coole Seite + + + + + + + + + + +
+

Hallo

+
+ Beispielbild +

+ Diese Seite ist nur auf deutsch verfügbar + Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam. +

+
+
+ + diff --git a/example/build/de/index.html b/example/build/de/index.html new file mode 100644 index 0000000..0bf66fa --- /dev/null +++ b/example/build/de/index.html @@ -0,0 +1,52 @@ + + + + + + + + + + Index + + + + + + + + + + +
+ +
+
+

Willkommen auf der Deutschen Version

+

Das Navigationsmenü wurde anhand der Überschriften erstellt und einige extra links aus src/common/index.html wurden eingefügt.

+

Dieser Abschnitt ist sehr interessant

+

Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est.

+

Diese Ãœberschrift bekommt keinen Eintrag, weil sie keine id hat

+

Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.

+

Dieser Abschnitt hat im Menü einen custom Namen

+

Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam.

+
+ + diff --git a/example/build/en/en-only.html b/example/build/en/en-only.html new file mode 100644 index 0000000..a6df37d --- /dev/null +++ b/example/build/en/en-only.html @@ -0,0 +1,32 @@ + + + + + + + + + + English only + + + + + + + + + + +
+

Hello there!

+
+ Example image +

+ This site is only available in engisch. + Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam. +

+
+
+ + diff --git a/example/build/en/index.html b/example/build/en/index.html new file mode 100644 index 0000000..04c020e --- /dev/null +++ b/example/build/en/index.html @@ -0,0 +1,52 @@ + + + + + + + + + + Index + + + + + + + + + + +
+ +
+
+

Welcome to the english version

+

The navigation menu was generated from the headings and some custom links defined in src/common/index.html.

+

This section is super interesting

+

Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est.

+

This heading does not get an entry, because it has no id

+

Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.

+

This section has a custom name in the menu

+

Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam.

+
+ + diff --git a/example/build/favicon/android-chrome-192x192.png b/example/build/favicon/android-chrome-192x192.png new file mode 100644 index 0000000000000000000000000000000000000000..3166f324f9a87957534673d1a6cd48e49b682956 GIT binary patch literal 9181 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 b#My$=$<-?3P>lR>5kyf|O{QGRIQahoVUP3- literal 0 HcmV?d00001 diff --git a/example/build/favicon/android-chrome-512x512.png b/example/build/favicon/android-chrome-512x512.png new file mode 100644 index 0000000000000000000000000000000000000000..7264364a6527f7a7172e335ed9fdb1e76586965a GIT binary patch literal 44652 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

wC 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 literal 0 HcmV?d00001 diff --git a/example/build/favicon/apple-touch-icon-180x180.png b/example/build/favicon/apple-touch-icon-180x180.png new file mode 100644 index 0000000000000000000000000000000000000000..8605ddd8dd936c5ea953c9e777cb0d583fe7c3bf GIT binary patch literal 8728 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 literal 0 HcmV?d00001 diff --git a/example/build/favicon/favicon-32x32.png b/example/build/favicon/favicon-32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..1846c2931ee1fd1a4b7489f66e212e2c6d38377b GIT binary patch literal 1229 zcmZ{hX-w017{`CKl;h7m?t*}VC@^R#FvcN*lxv(~!$7pOlvWy!7U-xhVx;0Z%?L13 zl^Hh`m=|u|3?0Zs1~E>Ou_GI@fg-dZhXc|Lhwd=F-%C0I~w ziV$Lvl*rGd)nmC#m~`i#KGd^3ND?zs3Hj8Ukli(eEYhppPYL;iOUQ(XkZ>&_j>@L? zjgf>H^k<|9;y{O9kGVNaPC~85ty`FxK}7}3&2i)ibUIX4!o&oGKq|%HAaZiRVu8=c z>?}^5LP!XFd@wM8i3wc1h)tVdVS#Jcu&{s|H_+aWM~^Tyg_ke!>J^?p$FpY`8Nsey z*suXsR%mFzix&`y;NpVNP^70rC`3U4#9~B7!p#kqmS8X-mt%e&i;E~PM^qFJAI8uS zE?)*k!Nvv>2|7FB=m;Yt#Kxkh2P;-!?OJfTu(N}g7gnx>p&^(|7#qXX6b~PwuMc5i zC@w~LIP&w6nTeDXl$IhW2*JUS$&j3kt5W8h(LWk zZr_HzJ)E52-~e}bczS}(Mqwe=twVD&-oC}&y)ZKahl79sT)&RIJUBaJd>l(lP%6>i zkMrm8<_)T_v5xkA-WiQr~ z+h-oljF0168KG*S&?=pya98B5SJ>LR%TqxdPclA_Zq<0W3x|fSnYy1ZO&{oOE%fWw zW*byxWcS3_|F~v))|>KL-Ez9QtVP!n!QQ6bZ@OFGTjSf?XSDcJcksP6oqoTr=XAwv z{v+%Ma{8n-yOB5WTUA%Cp+$dwkSj%bh;uS2M<2ho1AXyS|3{Y?XMFj?VPjdIEWhCQ zi+s1286;-+9sNfzyqVe1cA?wnfo`Lf!1`%*ZncWZjFr?om$EwU*DB+UWm2K(kpI_% z-;7@{FZo-hiIg^5PTu@w;oyq#FSq_V@xfo~ZdJ`Jy}4#9_#)X!n`DBsO!Lmr%<8RunYlR~Ca mi_4`%tyTxfON%NbVx=@dQNHc?R3w`|B1v&+{EHhzntuT(Av`kx literal 0 HcmV?d00001 diff --git a/example/build/favicon/favicon-48x48.png b/example/build/favicon/favicon-48x48.png new file mode 100644 index 0000000000000000000000000000000000000000..b4fcd78d77b08fcef6102879ab91b95de78185a8 GIT binary patch literal 1337 zcmZ{kYfO`86vy8Rfl_Fx6a)&y%5;JXmf8xf9k;g93MjV?6`bW#5up}nUA;u?pdu(L ziXu0;6j4N@Orx^J;3^J~ffa@6oH~avjS!ZQ;RH4Ry?j}+4?a2P`R81Ic|M$zrzB== z1f9mF5d=Y3$fa?la%Y`tMc&G%cGpPZq{-u=31au>1Ys}|#0;4-ye5bpWaAqpL5L~| z!ZoM%hgdN|P+r7DCCEUY^XD-*h^{VBsc2}xg9l)-KoC%?F*F2MSBOOD>4CE|Xf(Wk z4?8InKML_|Ii(xPzHy7*I!_g6?rO3)cP!PJiF*AeFQGEOe8ylQHjRgy^Z5x&^ zM_?eNQry3fb?d<8V)bh5+Xp5SmoFnD1F^9P56AK2pir=D7xMGb+zg`;$;nu_5PSAu z8O7>*uAMFkWJl$7AsEd&I>Vu6zr zQc|#UC**R7#W-;SG8z2*z~LYy1lHE@^1`xZ@brYeJ!)%_nF(KC*xKUdOWeDM_I4yC z!QUS>HJCdWZf@AR6(2redKw)a=-!$3s~eo9xI)G)+lJ0dD2(gW@GOhxPdI~H<_#3Y&@Ll;xjW(DyW-(TaeXe zYcFK}m8EK9a&LPz2i5+Vy_k_95O)-fuw1r7JX@ z9v0&v9#;zsB>eEs+l4~!o1ZnAgeG-r>UZx%4#U0eJAXFbS!fwKG01=PcWGabj}=4s z{l)B-nz&>95rZsG)hC>sv=5?vTh@8DxXH~r^68-l_L^h-$IQ7GJymjcZ^;0&;EcJ- zjqXC-oaoe4SJ*_m)X0wRYUv7&XrCvMcqJElR!8m4l^82&MMbOIi`c`p*8l8at;%Kh z)ikm<7ROu61GJ%r$csfQi)Y?aQw8ned>!*bT@!0~)ZZjX;FoWq+-pNvWZa0)=DphD+~Sw0b**F literal 0 HcmV?d00001 diff --git a/example/build/favicon/favicon.ico b/example/build/favicon/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..bec07106d753949a5328515dd299085c9af13051 GIT binary patch literal 15086 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¨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 literal 0 HcmV?d00001 diff --git a/example/build/resources/example.svg b/example/build/resources/example.svg new file mode 100644 index 0000000..b3ae576 --- /dev/null +++ b/example/build/resources/example.svg @@ -0,0 +1,64 @@ + + + + + + + + + bUwUma--- + + diff --git a/example/build/resources/favicon.png b/example/build/resources/favicon.png new file mode 100644 index 0000000..905b16a --- /dev/null +++ b/example/build/resources/favicon.png @@ -0,0 +1,64 @@ + + + + + + + + + bUwUma--- + + diff --git a/example/build/script/test.js b/example/build/script/test.js new file mode 100644 index 0000000..5546561 --- /dev/null +++ b/example/build/script/test.js @@ -0,0 +1 @@ +console.log("This script will indeed be loaded!"); diff --git a/example/build/sitemap.xml b/example/build/sitemap.xml new file mode 100644 index 0000000..0f8724e --- /dev/null +++ b/example/build/sitemap.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/example/build/style/main.css b/example/build/style/main.css new file mode 100644 index 0000000..44188d5 --- /dev/null +++ b/example/build/style/main.css @@ -0,0 +1,42 @@ +body { + background-color: #eee; +} + +h1 { + color: blue; +} + +p { + border: solid 1px #cc0; +} + +.img_text img { + width: 30%; + height: auto; + float: left; +} +.img_text p { + width: 69%; + float: left; +} + +.sidenav { + border: solid 2px blue; +} +.sidenav ul { + width: fit-content; +} +.sidenav li { + display: none; +} +.sidenav:hover li { + display: block; +} +.sidenav .menudrop { + display: block; +} +.sidenav:hover .menudrop, .sidenav li { + display: none; +} + +/*# sourceMappingURL=main.css.map */ diff --git a/example/html-preprocessor b/example/html-preprocessor new file mode 100755 index 0000000..b376783 --- /dev/null +++ b/example/html-preprocessor @@ -0,0 +1,904 @@ +#!/bin/python3 +import os +from os import path +import re +from sys import argv +from collections.abc import Callable +import argparse +import pickle + +""" +TODO: +- more testing +- reintroduce the nav_selected class on nav feature +""" +""" +************************************************************ SETTINGS ************************************************************ +""" +sidenav_format = """\ +

+
    + + #sidenav-content +
+
+""" +sidenav_content_link = "
  • #name
  • " +sidenav_content_section = """\ +
  • #name
  • +""" + +exit_on_include_failure = False + +sitemap_begin = """\ + +\n""" +sitemap_end = "" + +""" +************************************************************ REGULAR EXPRESSIONS ************************************************************ +""" +# SIDENAV +# heading with id +re_sidenav_heading = r"(.+)" +# custom entry +re_sidenav_custom = r"href=(?:\"|\')([^\"\' ]+)(?:\"|\') +name=(?:\"|\')(.+)(?:\"|\')" + +# commas +re_set_map = r"([a-zA-Z0-9_]+) *\? *\{( *(?:[a-zA-Z0-9_*]+ *: *[^,]*, *)+[a-zA-Z0-9_*]+ *: *[^,]*) *,? *\}" +# semicolons +re_set_map_alt = r"([a-zA-Z0-9_]+) *\? *\{( *(?:[a-zA-Z0-9_*]+ *: *[^;]* *; *)+[a-zA-Z0-9_*]+ *: *[^;]*) *;? *\}" + +""" #$(myvar) """ +re_variable_use = r"#\$\(([a-zA-Z0-9_]+)\)" + +""" only in comments """ +re_preprocessor_command = r"[\t ]*#([a-zA-Z]+) *(.*)[\t ]*" + +# https://www.w3.org/TR/NOTE-datetime +re_w3cdate = r"\d{4}-(?)]-\d{2}" +r"\d{4}-(?:0[1-9]|1[0-2])-(?:[0-2]\d|3[01])(T(?:[01]\d|2[0-3]):[0-5]\d:[0-5]\d([\+\-](?:0\d|1[0-2]):[0-5]\d)?)?" + +COMMENT_BEGIN = "" + + +""" +************************************************************ GLOBALS ************************************************************ +""" +glob_dependcies: list[str] = [] + +exit_codes = { + "FileNotFound": 2, + "MarkdownConversionError": 3, +} +error_levels = { + "light": 0, + "serious": 1, + "critical": 2, +} +exit_on_error_level = error_levels["serious"] + +# url that the currently processed file have +current_file_url = "" + + +""" +************************************************************ UTILITY ************************************************************ +""" + +RED = '\033[91m' +GREEN = '\033[92m' +YELLOW = '\033[93m' +BLUE = '\033[94m' +MAGENTA = '\033[95m' +CYAN = '\033[96m' +GRAY = '\033[97m' +RESET = '\033[0m' +BOLD = '\033[1m' +WHITE = '\033[37m' + + +DEBUG = False +def pdebug(*args, **keys): + fname, *_args = args + if DEBUG: print(f"{CYAN}{fname}{GRAY}", *_args, RESET, **keys) + +TRACE = False +def ptrace(*args, **keys): + fname, *_args = args + if TRACE: print(f"{BLUE}{fname}{GRAY}", *_args, RESET, **keys) + +def error(*args, level:int=exit_on_error_level, exit_code:int=1, **keys): + fname, *_args = args + if level >= exit_on_error_level: + print(f"{RED}ERROR: {fname}{RESET}", *_args, RESET, **keys) + exit(exit_code) + else: + print(f"{YELLOW}WARNING: {fname}{RESET}", *_args, RESET, **keys) + +def line_is_link_to_path(line, path): + # check if the line is a link to html thats currently being processed + match = re.search(r"(.+)", line) + if match: + # get filename + match = re.match(r"[a-zA-Z0-9_\-]+\.html", match.groups()[1]) + if match and match.group() in path: + return True + return False + +def pos2line(s: str, pos:int): + return s[:pos].count('\n') + 1 + + +def generate_dependecy_file(filename:str, deps:list[str]): + line1 = f"{filename}:" + s = "" + for dep in deps: + line1 += f" {dep}" + s += f"{dep}:\n" + return line1 #+ "\n" + s + +def evaluate_condition(input_string) -> bool: + words = re.split(r"(==|!=|&&|\|\|)", input_string.replace(" ", "")) + for i in range(len(words)): + if words[i] not in ["==", "!=", "&&", "||"]: + words[i] = '"' + words[i].replace('"', r'\"') + '"' + + condition = "".join(words).replace("&&", " and ").replace("||", " or ") + ptrace("evaluate_conditon", f"Evaluating condition {condition}") + try: + return eval(condition) + except SyntaxError: + error("evaluate_conditon", f"Pythonized condition is invalid: {condition}", level=error_levels["light"]) + return False + +""" +************************************************************ SITEMAP ************************************************************ +""" +class Sitemap: + urls:dict = {} + def __init__(self, url=None): + self.url = url + self.priority = None + self.changefreq = None + self.lastmod = None + + def set_url(self, url): + self.url = url + + def set_priority(self, priority): + try: + priority = float(priority) + except ValueError: + error("Sitemap.set_priority", f"invalid priority: '{priority}'", level=error_levels["serious"]) + if not (type(priority) == float and 0.0 <= priority and priority <= 1.0): + error("Sitemap.set_priority", f"invalid priority: '{priority}'", level=error_levels["serious"]) + self.priority = priority + + def set_changefreq(self, changefreq): + if not (type(changefreq) == str and changefreq in ["always", "hourly", "daily", "weekly", "monthly", "yearly", "never"]): + error("Sitemap.set_changefreq", f"invalid changefreq: '{changefreq}'", level=error_levels["serious"]) + self.changefreq = changefreq + + def set_lastmod(self, lastmod): + if not (type(lastmod) == str and re.fullmatch(re_w3cdate, lastmod)): + error("Sitemap.set_lastmod", f"invalid lastmod: '{lastmod}'", level=error_levels["serious"]) + self.lastmod = lastmod + + def get_entry(self): + s = f"\n\t{self.url}" + if self.priority is not None: s += f"\n\t{self.priority}" + if self.changefreq is not None: s += f"\n\t{self.changefreq}" + if self.lastmod is not None: s += f"\n\t{self.lastmod}" + s += "\n" + return s + + def __repr__(self) -> str: + return f"Sitemap(url={self.url}, priority={self.priority}, changefreq={self.changefreq}, lastmod={self.lastmod})" + + @staticmethod + def gen_sidemap(): + s = sitemap_begin + for url in Sitemap.urls.values(): + s += "\t" + url.get_entry().replace("\n", "\n\t").strip("\t") + "\n" + s += sitemap_end + return s + + @staticmethod + def cmd_sitemap(args:str, variables:dict[str,str]) -> str: + space = args.find(" ") + if space < 0: + space = len(args) + cmd = args[:space] + cmd_args = "" + + if 0 < space and space < len(args) - 1: + cmd_args = args[space+1:].strip(" ") + pdebug("cmd_sitemap", f"cmd='{cmd}' cmd_args='{cmd_args}'") + if not current_file_url in Sitemap.urls: + Sitemap.urls[current_file_url] = Sitemap() + if cmd == "include": + if cmd_args: + Sitemap.urls[current_file_url].set_url(cmd_args) + else: + Sitemap.urls[current_file_url].set_url(current_file_url) + elif cmd == "priority": + Sitemap.urls[current_file_url].set_priority(cmd_args) + elif cmd == "changefreq": + Sitemap.urls[current_file_url].set_changefreq(cmd_args) + elif cmd == "lastmod": + Sitemap.urls[current_file_url].set_lastmod(cmd_args) + else: + error("cmd_sitemap", f"Invalid command '{cmd}'", error_levels["serious"]) + ptrace("cmd_sitemap", f"Sitemap[{current_file_url}] is now: {Sitemap.urls[current_file_url]}") + return "" + + +""" +************************************************************ SIDENAV ************************************************************ +""" +def replace_and_respect_indent(string, replace, replacement): + """ + replace all occurences of 'replace' with 'replacement', add the whitespaces in front of 'replace' to every line of 'replacement' + """ + i = string.find(replace) + while i >= 0: + line_begin = string.rfind("\n", 0, i) + 1 + indent = string[line_begin:i] + string = string[:line_begin] + replacement.replace("\n", "\n" + indent) + string[i+len(replace):] + i = string.find(replace) + return string + +class Sidenav: + class Link: + def __init__(self, name: str, link: str): + self.link = link + self.name = name + def __repr__(self): + return f"Link: name={self.name}, link={self.link}" + + def get(self): + return sidenav_content_link.replace("#name", self.name).replace("#link", self.link) + class Section: + def __init__(self, name: str): + self.name = name + self.links = [] + def add_link(self, link): + self.links.append(link) + def __repr__(self): + return f"Section: name={self.name}" + def get(self): + links = "".join([ link.get() + "\n" for link in self.links ]) + return replace_and_respect_indent(sidenav_content_section.replace("#name", self.name), "#links", links) + entries: list[Link|Section] = [] + skip_next = False + custom_name = None + @staticmethod + def addEntry(name: str, link: str): + if Sidenav.skip_next: + Sidenav.skip_next = None + return + if Sidenav.custom_name: + name = Sidenav.custom_name + Sidenav.custom_name = None + if len(Sidenav.entries) > 0 and type(Sidenav.entries[-1]) == Sidenav.Section: + Sidenav.entries[-1].add_link(Sidenav.Link(name, link)) + else: + Sidenav.entries.append(Sidenav.Link(name, link)) + @staticmethod + def addSection(name): + Sidenav.entries.append(Sidenav.Section(name)) + @staticmethod + def setCustomName(name: str): + Sidenav.custom_name = name + @staticmethod + def skipNext(): + Sidenav.skip_next = True + @staticmethod + def generate() -> str: + pdebug("Sidenav.generate", f"found the following entries: {Sidenav.entries}") + entries = "".join([entry.get() + "\n" for entry in Sidenav.entries]) + return replace_and_respect_indent(sidenav_format, "#sidenav-content", entries) + @staticmethod + def cmd_sidenav(args:str, variables:dict[str,str]) -> str: + space = args.find(" ") + if space < 0: + space = len(args) + cmd = args[:space] + cmd_args = "" + if 0 < space and space < len(args) - 1: + cmd_args = args[space+1:].strip(" ") + pdebug("cmd_sidenav", f"cmd='{cmd}' cmd_args='{cmd_args}'") + if cmd == "skip": + Sidenav.skipNext() + elif cmd == "section": + Sidenav.addSection(cmd_args) + elif cmd == "name": + Sidenav.setCustomName(cmd_args) + elif cmd == "custom": + match = re.fullmatch(re_sidenav_custom, cmd_args) + if match: + Sidenav.addEntry(match.groups()[1], match.groups()[0]) + else: + error("cmd_sidenav", f"Invalid argument for command 'custom': '{cmd_args}'", level=error_levels["light"]) + elif cmd == "include": + return Sidenav.generate() + else: + error("cmd_sidenav", f"Invalid command: '{cmd}'", level=error_levels["light"]) + + return "" + + +""" +************************************************************ COMMANDS ************************************************************ +All these commands take one arg with trimmed whitespaces. +The arg may be anything + +They all need to return a string, which will be placed +into the source file at the place where the command was. +""" +def cmd_include(args: str, variables:dict[str, str]={}) -> str: + args = args.split(' ') + pdebug("cmd_include", f"args='{args}', variables='{variables}'") + filename = args[0] + content = "" + try: + with open(filename) as file: + content = file.read() + if len(args) > 1: # if section was specified + target_section = args[1] + p = HTMLParser(content, {}) + p.pos["start"] = p.pos["end"] = -1 + while p.i < len(p): # at start of new line or end of comment + p.find_line_end() + ptrace("cmd_include", f"Processing at i={p.i} in line {pos2line(p.file, p.i)}: '{p[p.i:p.pos['line_end']]}'") + if not p.find_comment_begin(): continue + if not p.find_comment_end(): continue + p.replace_multiline_comments() + + match = p.find_command() + if match: + command = match.groups()[0] + cmd_args = match.groups()[1].replace('\t', ' ').strip(' ') + pdebug("cmd_include", f"Found command '{command}' with args '{cmd_args}'") + if command == "section": + if cmd_args.startswith(target_section): + p.pos["start"] = max(p.pos["cmt_end"] + len(COMMENT_END), p.pos["line_end"] + 1) + elif p.pos["start"] >= 0: #end + p.pos["end"] = max(p.pos["cmt_end"] + len(COMMENT_END), p.pos["line_end"] + 1) + # p.pos["end"] = p.pos["cmt_beg"] + p.replace_command_with_output("") + p.command_end() # remove the command (+comment) + if p.pos["start"] >= 0 and p.pos["end"] > 0: break + continue + # section cmd in multiline comment is not supported, so simply jump to end of comment + p.i = p.pos["cmt_end"] + len(COMMENT_END) + p.pos["cmt_beg"] = -1 + p.pos["cmd_beg"] = -1 + p.pos["cmt_end"] = -1 + p.pos["cmd_end"] = -1 + if p.pos["start"] >= 0: + if p.pos["end"] < 0: + p.pos["end"] = len(p) + content = p[p.pos["start"]:p.pos["end"]] + else: + error("cmd_include", f"Could not find section {target_section} in file {filename}") + except FileNotFoundError: + error("cmd_include", f"Could not open file '{filename}'", level=error_levels["serious"], exit_code=exit_codes["FileNotFound"]) + content = f"" + if filename.endswith(".md"): + try: + from markdown import markdown + content = markdown(content, output_format="xhtml") + except: + error("cmd_include", f"Could convert markdown to html for file '{filename}'. Is python-markdown installed?", level=error_levels["critical"], exit_code=exit_codes["MarkdownConversionError"]) + content = f"" + glob_dependcies.append(filename) + return content + +def cmd_section(args: str, variables:dict[str, str]={}) -> str: + return "" + +def cmd_return(args: str, variables:dict[str, str]={}) -> str: + # re_set_map = r"([a-zA-Z0-9_]+)\?\{(([a-zA-Z0-9_]+:.+,)*([a-zA-Z0-9_]+:.+))\}" + # + space = args.find(' ') + pdebug("cmd_set", f"varname='{args[:space]}, 'arg='{args[space+1:]}', variables='{variables}'") + if not (space > 0 and space < len(args)-1): + variables[args] = "" + pdebug("cmd_set", f"Setting to empty string: {args}") + else: + varname = args[:space] + variables[varname] = "" + # check if map assignment with either , or ; + separator = ',' + match = re.fullmatch(re_set_map, args[space+1:].strip(' ')) + if not match: + match = re.fullmatch(re_set_map_alt, args[space+1:].strip(' ')) + separator = ';' + if match: + pdebug("cmd_set", f"Map {match.group()}") + depends = match.groups()[0] + if not depends in variables: + pdebug("cmd_set", f"Setting from map, but depends='{depends}' is not in variables") + return "" + depends_val = variables[depends] + for option in match.groups()[1].split(separator): + option = option.strip(" ") + pdebug("cmd_set", f"Found option {option}") + colon = option.find(':') # we will find one, regex guarantees + if option[:colon].strip(" ") == depends_val or option[:colon].strip(" ") == "*": + variables[varname] = option[colon+1:].strip(" ") + + else: # simple asignment + value = args[space+1:].strip(" ") + variables[varname] = value + pdebug("cmd_set", f"Assignment {varname} -> {value}") + return variables[varname] + return "" + +def cmd_set(args: str, variables:dict[str, str]={}) -> str: + cmd_return(args, variables) + return "" + +def cmd_unset(args: str, variables:dict[str, str]={}) -> str: + variable = args.strip(' ') + if variable not in variables: + pdebug("cmd_unset", f"variable '{variable}' is not set", level=error_levels["light"]) + else: + variables.pop(variable) + return "" + +def cmd_default(args: str, variables:dict[str, str]={}) -> str: + separator = args.find(' ') + if args[:separator] not in variables: + cmd_return(args, variables) + return "" + + +def cmd_comment(args: str, variables:dict[str, str]={}) -> str: + return f"" +def cmd_uncomment(args: str, variables:dict[str, str]={}) -> str: + return args + +def cmd_error(args: str, variables:dict[str, str]={}) -> str: + error("cmd_error", f"Encounted 'error' command: {args}", level=error_levels["critical"]) + return "" +def cmd_warning(args: str, variables:dict[str, str]={}) -> str: + error("cmd_warning", f"Encounted 'warning' command: {args}", level=error_levels["light"]) + return "" + + +command2function:dict[str, Callable[[str, dict[str,str]], str]] = { + "include": cmd_include, + "section": cmd_section, + "return": cmd_return, + "set": cmd_set, + "unset": cmd_unset, + "default": cmd_default, + "comment": cmd_comment, + "uncomment": cmd_uncomment, + "sidenav": Sidenav.cmd_sidenav, + "sitemap": Sitemap.cmd_sitemap, + "warning": cmd_warning, + "error": cmd_error, +} + +""" +************************************************************ PARSING ************************************************************ +""" + +class Parser(): + """ + General purpose parser class + It has states and positions in a text, which are updated when portions of the text are replaced or removed + """ + def __init__(self, file): + self.file = file + self.pos: dict[str, int] = {} + self.state: dict[str, bool] = {} + + def remove(self, start, stop, ignore_bounds=[]): + """remove range [start, stop) of text and update positions""" + delete_length = stop - start + nl, esl = "\n", "\\n" + + ptrace("Parser.remove", f"Deleting range [{start}, {stop}) of length {delete_length}: '{self.file[start:stop].replace(nl, esl)}'") + assert(stop >= start) + assert(stop <= len(self.file)) + self.file = self.file[:start] + self.file[stop:] + for k,pos in self.pos.items(): + if pos >= stop: self.pos[k] -= delete_length + elif pos > start and not k in ignore_bounds: error("Parser.remove", f"Position {k}={pos} within deleted range [{start},{stop})", level=error_levels["light"]) + + def replace(self, start, stop, replacement, ignore_bounds=[]): + assert(stop >= start) + assert(stop <= len(self.file)) + ptrace("Parser.replace", f"Replacing range [{start}, {stop}): '{self.file[start:stop]}' with '{replacement}'") + self.file = self.file[:start] + replacement + self.file[stop:] + length_difference = stop - start - len(replacement) + for k,pos in self.pos.items(): + if pos >= stop: self.pos[k] -= length_difference + elif pos > start and k not in ignore_bounds: error("Parser.replace", f"Position {k}={pos} within replaced range [{start},{stop})", level=error_levels["light"]) + + def __getitem__(self, key): + return self.file[key] + + def __len__(self): + return len(self.file) + + +class HTMLParser(Parser): + """ + Parse a html file + Each function operates the positon indicated by i until the position "line_end" + """ + def __init__(self, file, variables:dict[str, str], remove_comments=False): + super().__init__(file) + self.i = 0 + self.variables = variables + self.pos["cmt_beg"] = -1 + self.pos["cmt_end"] = -1 + self.pos["cmd_beg"] = -1 + self.pos["cmd_end"] = -1 + self.pos["line_end"] = -1 + self.pos["conditional_block_beg"] = -1 # char pos of the first char of the last block, if waiting for elif, else or endif + self.state["cmd_in_cmt"] = False + self.state["last_condition"] = False # if the last if condition was true + self.remove_comments = remove_comments + + + def use_variables(self): + """replace variable usages in the current line""" + self.replace(self.i, self.pos["line_end"], substitute_variables(self[self.i:self.pos["line_end"]], self.variables)) + ptrace("HTMLParser.use_variables", f"Line after variable substitution:", self.file[self.i:self.pos["line_end"]]) + + def add_sidenav_headings(self): + """check if heading for sidenav in line""" + match = re.search(re_sidenav_heading, self[self.i:self.pos["line_end"]]) + if match: + Sidenav.addEntry(match.groups()[1], f"#{match.groups()[0]}") + ptrace("HTMLParser.add_sidenav_headings:", f"Found heading with id:", match.groups()) + + def get_leading_whitespaces(self): + """returns the whitespaces at the start of the line""" + # find last newline + line_beg = self.file.rfind("\n", 0, self.i) + if line_beg < 0: line_beg = 0 + else: line_beg += 1 # start after newline + match = re.match(r"^([ \t]*)", self.file[line_beg:self.pos['line_end']]) + if not match: return "" + else: return match.groups()[0] + + + # Parsing functions + def find_line_end(self): + """ + line_end -> position of next newline char or EOF + """ + self.pos["line_end"] = self.file.find('\n', self.i+1) + if self.pos["line_end"] < 0: self.pos["line_end"] = len(self) + + + def find_comment_begin(self) -> bool: + """ + find the beginning of a comment in the current line + if comment begin was found, jump into the comment, return True + cmt_beg -> beginning of COMMENT_BEGIN + i -> first character after COMMENT_BEGIN / line_end + 1 + + """ + # look for comment begin + if self.pos["cmt_beg"] < 0: # if not in comment, find next comment + self.pos["cmt_beg"] = self.file.find(COMMENT_BEGIN, self.i, self.pos["line_end"]) + if self.pos["cmt_beg"] < 0: + self.i = self.pos["line_end"] + 1 + return False + else: + # jump to comment_begin + old_i = self.i + self.i = self.pos["cmt_beg"] + len(COMMENT_BEGIN) # after comment begin + ptrace(f"HTMLParser.find_comment_begin", f"Found comment begin, jumping from pos {old_i} to {self.i}") + return True + return True # still in previous comment + + + def find_comment_end(self): + """ + call after find_comment_begin returns true to update the cmt_end + call continue when returning false + cmt_end -> beginning of COMMENT_END / --- + cmt_beg -> --- / -1 when invalid comment + """ + # in comment, i at the character after COMMENT_BEGIN + self.pos["cmt_end"] = self.file.find(COMMENT_END, self.i) #, self.pos["line_end"]) + # sanity checks + if self.pos["cmt_end"] < 0: + error("HTMLParser.find_comment_end", f"Comment starting in line {pos2line(self.file, self.pos['cmt_beg'])} is never ended.", level=error_levels["serious"]) + return False + else: + tmp_next_begin = self.file.find(COMMENT_BEGIN, self.i) + if 0 < tmp_next_begin and tmp_next_begin < self.pos["cmt_end"]: + error("HTMLParser.find_comment_end", f"Found next comment begin before the comment starting in line {pos2line(self.file, self.pos['cmt_beg'])} is ended! Skipping comment. Comment without proper closing tags: '{self.file[self.i:self.pos['line_end']]}'", level=error_levels["light"]) + self.pos["cmt_beg"] = -1 + return False + return True + + + def replace_multiline_comments(self): + """ + if in a multiline comment, turn every line into a separate comment + """ + # not a multiline comment + if self.pos["line_end"] > self.pos["cmt_end"]: return + indent = self.get_leading_whitespaces() + self.replace(self.pos["cmt_beg"], self.pos["cmt_end"], self.file[self.pos["cmt_beg"]:self.pos["cmt_end"]].replace("\n", "-->\n" + indent + " + + + + +
    + +
    + + + + diff --git a/example/src/de/de-only.html b/example/src/de/de-only.html new file mode 100644 index 0000000..b69993e --- /dev/null +++ b/example/src/de/de-only.html @@ -0,0 +1,26 @@ + + + + + + +
    +

    Hallo

    +
    + #$(example_img) +

    + Diese Seite ist nur auf deutsch verfügbar + Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam. +

    +
    +
    + + + diff --git a/example/src/en/en-only.html b/example/src/en/en-only.html new file mode 100644 index 0000000..01800da --- /dev/null +++ b/example/src/en/en-only.html @@ -0,0 +1,25 @@ + + + + + + +
    +

    Hello there!

    +
    + #$(example_img) +

    + This site is only available in engisch. + Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam. +

    +
    +
    + + + diff --git a/example/src/include/head.html b/example/src/include/head.html new file mode 100644 index 0000000..601dc26 --- /dev/null +++ b/example/src/include/head.html @@ -0,0 +1,32 @@ + + + + + + + + + #$(title) + + + + + + + + + + + + diff --git a/example/src/include/index.de.md b/example/src/include/index.de.md new file mode 100644 index 0000000..c0ec312 --- /dev/null +++ b/example/src/include/index.de.md @@ -0,0 +1,14 @@ +# Willkommen auf der Deutschen Version + + +Das Navigationsmenü wurde anhand der Überschriften erstellt und einige extra links aus `src/common/index.html` wurden eingefügt. + +

    Dieser Abschnitt ist sehr interessant

    +Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est. + +## Diese Ãœberschrift bekommt keinen Eintrag, weil sie keine id hat +Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. + + +

    Dieser Abschnitt hat im Menü einen custom Namen

    +Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam. diff --git a/example/src/include/index.en.md b/example/src/include/index.en.md new file mode 100644 index 0000000..c6fa9ef --- /dev/null +++ b/example/src/include/index.en.md @@ -0,0 +1,13 @@ +# Welcome to the english version + +The navigation menu was generated from the headings and some custom links defined in `src/common/index.html`. + +

    This section is super interesting

    +Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est. + +## This heading does not get an entry, because it has no id +Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. + + +

    This section has a custom name in the menu

    +Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam. diff --git a/example/src/include/style/images.sass b/example/src/include/style/images.sass new file mode 100644 index 0000000..614d7af --- /dev/null +++ b/example/src/include/style/images.sass @@ -0,0 +1,9 @@ +.img_text + img + width: 30% + height: auto + float: left + + p + width: 69% + float: left diff --git a/example/src/include/style/sidenav.sass b/example/src/include/style/sidenav.sass new file mode 100644 index 0000000..fe9f3a3 --- /dev/null +++ b/example/src/include/style/sidenav.sass @@ -0,0 +1,12 @@ +.sidenav + border: solid 2px blue + ul + width: fit-content + li + display: none + &:hover li + display: block + .menudrop + display: block + &:hover .menudrop, li + display: none diff --git a/example/src/resources/example.svg b/example/src/resources/example.svg new file mode 100644 index 0000000..b3ae576 --- /dev/null +++ b/example/src/resources/example.svg @@ -0,0 +1,64 @@ + + + + + + + + + bUwUma--- + + diff --git a/example/src/resources/favicon.png b/example/src/resources/favicon.png new file mode 100644 index 0000000..905b16a --- /dev/null +++ b/example/src/resources/favicon.png @@ -0,0 +1,64 @@ + + + + + + + + + bUwUma--- + + diff --git a/example/src/script/test.js b/example/src/script/test.js new file mode 100644 index 0000000..5546561 --- /dev/null +++ b/example/src/script/test.js @@ -0,0 +1 @@ +console.log("This script will indeed be loaded!"); diff --git a/example/src/style/main.sass b/example/src/style/main.sass new file mode 100644 index 0000000..bc74a05 --- /dev/null +++ b/example/src/style/main.sass @@ -0,0 +1,15 @@ +// This sass file be compiled to css + +body + background-color: #eee + +h1 + color: blue + +p + border: solid 1px #cc0 + + + +@import "images" +@import "sidenav"