diff --git a/docker_dev/Dockerfile b/docker_dev/Dockerfile
index 2060d63..22239a4 100644
--- a/docker_dev/Dockerfile
+++ b/docker_dev/Dockerfile
@@ -4,7 +4,7 @@ WORKDIR /app
COPY www/composer.* .
COPY www/vendor/* vendor/
-FROM php:apache
+FROM php:8.2-apache
WORKDIR /var/www/html
COPY --from=0 /app/ ./
@@ -15,6 +15,7 @@ RUN a2enmod headers
RUN docker-php-ext-install mysqli && docker-php-ext-enable mysqli
RUN mkdir raw
+RUN chmod a+wr raw
COPY www/ .
-RUN chmod -R a+rw $(ls -I vendor)
+RUN chmod -R a+rw $(ls -I vendor)
\ No newline at end of file
diff --git a/docker_dev/mysql_schema.sql b/docker_dev/mysql_schema.sql
index 97645e6..afc6d22 100644
--- a/docker_dev/mysql_schema.sql
+++ b/docker_dev/mysql_schema.sql
@@ -37,7 +37,10 @@ CREATE TABLE posts (
INDEX(host, post_created_at),
INDEX(host, post_updated_at),
- FULLTEXT(post_tags)
+ FULLTEXT(post_path),
+ FULLTEXT(post_tags),
+ FULLTEXT(post_title),
+ FULLTEXT(post_brief)
);
CREATE TABLE post_markdown (
diff --git a/dragon_fire.code-workspace b/dragon_fire.code-workspace
index 533b5a2..c6a4a66 100644
--- a/dragon_fire.code-workspace
+++ b/dragon_fire.code-workspace
@@ -7,5 +7,11 @@
"path": "../dragon_fire_content"
}
],
- "settings": {}
+ "settings": {
+ "conventionalCommits.scopes": [
+ "search",
+ "templates",
+ "css"
+ ]
+ }
}
\ No newline at end of file
diff --git a/www/.htaccess b/www/.htaccess
index d17abcd..05ae513 100644
--- a/www/.htaccess
+++ b/www/.htaccess
@@ -3,8 +3,8 @@ AddType text/plain .md
AddType text/plain .atom
AddType text/plain .rss
-# php_value upload_max_filesize 40M
-# php_value post_max_size 42M
+php_value upload_max_filesize 40M
+php_value post_max_size 42M
RewriteEngine On
RewriteBase /
diff --git a/www/composer.lock b/www/composer.lock
index d41c77f..b641d4b 100644
--- a/www/composer.lock
+++ b/www/composer.lock
@@ -184,33 +184,33 @@
},
{
"name": "laminas/laminas-escaper",
- "version": "2.13.0",
+ "version": "2.14.0",
"source": {
"type": "git",
"url": "https://github.com/laminas/laminas-escaper.git",
- "reference": "af459883f4018d0f8a0c69c7a209daef3bf973ba"
+ "reference": "0f7cb975f4443cf22f33408925c231225cfba8cb"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/laminas/laminas-escaper/zipball/af459883f4018d0f8a0c69c7a209daef3bf973ba",
- "reference": "af459883f4018d0f8a0c69c7a209daef3bf973ba",
+ "url": "https://api.github.com/repos/laminas/laminas-escaper/zipball/0f7cb975f4443cf22f33408925c231225cfba8cb",
+ "reference": "0f7cb975f4443cf22f33408925c231225cfba8cb",
"shasum": ""
},
"require": {
"ext-ctype": "*",
"ext-mbstring": "*",
- "php": "~8.1.0 || ~8.2.0 || ~8.3.0"
+ "php": "~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0"
},
"conflict": {
"zendframework/zend-escaper": "*"
},
"require-dev": {
- "infection/infection": "^0.27.0",
- "laminas/laminas-coding-standard": "~2.5.0",
+ "infection/infection": "^0.27.9",
+ "laminas/laminas-coding-standard": "~3.0.0",
"maglnet/composer-require-checker": "^3.8.0",
- "phpunit/phpunit": "^9.6.7",
- "psalm/plugin-phpunit": "^0.18.4",
- "vimeo/psalm": "^5.9"
+ "phpunit/phpunit": "^9.6.16",
+ "psalm/plugin-phpunit": "^0.19.0",
+ "vimeo/psalm": "^5.21.1"
},
"type": "library",
"autoload": {
@@ -242,7 +242,7 @@
"type": "community_bridge"
}
],
- "time": "2023-10-10T08:35:13+00:00"
+ "time": "2024-10-24T10:12:53+00:00"
},
{
"name": "laminas/laminas-feed",
@@ -519,16 +519,16 @@
},
{
"name": "league/commonmark",
- "version": "2.5.1",
+ "version": "2.5.3",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/commonmark.git",
- "reference": "ac815920de0eff6de947eac0a6a94e5ed0fb147c"
+ "reference": "b650144166dfa7703e62a22e493b853b58d874b0"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/ac815920de0eff6de947eac0a6a94e5ed0fb147c",
- "reference": "ac815920de0eff6de947eac0a6a94e5ed0fb147c",
+ "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/b650144166dfa7703e62a22e493b853b58d874b0",
+ "reference": "b650144166dfa7703e62a22e493b853b58d874b0",
"shasum": ""
},
"require": {
@@ -541,8 +541,8 @@
},
"require-dev": {
"cebe/markdown": "^1.0",
- "commonmark/cmark": "0.31.0",
- "commonmark/commonmark.js": "0.31.0",
+ "commonmark/cmark": "0.31.1",
+ "commonmark/commonmark.js": "0.31.1",
"composer/package-versions-deprecated": "^1.8",
"embed/embed": "^4.4",
"erusev/parsedown": "^1.0",
@@ -621,7 +621,7 @@
"type": "tidelift"
}
],
- "time": "2024-07-24T12:52:09+00:00"
+ "time": "2024-08-16T11:46:16+00:00"
},
{
"name": "league/config",
@@ -707,24 +707,24 @@
},
{
"name": "nette/schema",
- "version": "v1.3.0",
+ "version": "v1.3.2",
"source": {
"type": "git",
"url": "https://github.com/nette/schema.git",
- "reference": "a6d3a6d1f545f01ef38e60f375d1cf1f4de98188"
+ "reference": "da801d52f0354f70a638673c4a0f04e16529431d"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/nette/schema/zipball/a6d3a6d1f545f01ef38e60f375d1cf1f4de98188",
- "reference": "a6d3a6d1f545f01ef38e60f375d1cf1f4de98188",
+ "url": "https://api.github.com/repos/nette/schema/zipball/da801d52f0354f70a638673c4a0f04e16529431d",
+ "reference": "da801d52f0354f70a638673c4a0f04e16529431d",
"shasum": ""
},
"require": {
"nette/utils": "^4.0",
- "php": "8.1 - 8.3"
+ "php": "8.1 - 8.4"
},
"require-dev": {
- "nette/tester": "^2.4",
+ "nette/tester": "^2.5.2",
"phpstan/phpstan-nette": "^1.0",
"tracy/tracy": "^2.8"
},
@@ -763,9 +763,9 @@
],
"support": {
"issues": "https://github.com/nette/schema/issues",
- "source": "https://github.com/nette/schema/tree/v1.3.0"
+ "source": "https://github.com/nette/schema/tree/v1.3.2"
},
- "time": "2023-12-11T11:54:22+00:00"
+ "time": "2024-10-06T23:10:23+00:00"
},
{
"name": "nette/utils",
@@ -983,21 +983,21 @@
},
{
"name": "spatie/yaml-front-matter",
- "version": "2.0.9",
+ "version": "2.1.0",
"source": {
"type": "git",
"url": "https://github.com/spatie/yaml-front-matter.git",
- "reference": "cbe67e1cdd0a29a96d74ccab9400fe663e078392"
+ "reference": "5d0009289dd19a23e5f6cbb72c959a9fc1881e32"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/spatie/yaml-front-matter/zipball/cbe67e1cdd0a29a96d74ccab9400fe663e078392",
- "reference": "cbe67e1cdd0a29a96d74ccab9400fe663e078392",
+ "url": "https://api.github.com/repos/spatie/yaml-front-matter/zipball/5d0009289dd19a23e5f6cbb72c959a9fc1881e32",
+ "reference": "5d0009289dd19a23e5f6cbb72c959a9fc1881e32",
"shasum": ""
},
"require": {
- "php": "^7.0|^8.0",
- "symfony/yaml": "^3.0|^4.0|^5.0|^6.0|^7.0"
+ "php": "^8.0",
+ "symfony/yaml": "^6.0|^7.0"
},
"require-dev": {
"phpunit/phpunit": "^9.0"
@@ -1029,7 +1029,7 @@
"yaml"
],
"support": {
- "source": "https://github.com/spatie/yaml-front-matter/tree/2.0.9"
+ "source": "https://github.com/spatie/yaml-front-matter/tree/2.1.0"
},
"funding": [
{
@@ -1041,20 +1041,20 @@
"type": "github"
}
],
- "time": "2024-06-13T10:20:51+00:00"
+ "time": "2024-12-02T08:40:45+00:00"
},
{
"name": "symfony/deprecation-contracts",
- "version": "v3.5.0",
+ "version": "v3.5.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/deprecation-contracts.git",
- "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1"
+ "reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1",
- "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1",
+ "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6",
+ "reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6",
"shasum": ""
},
"require": {
@@ -1092,7 +1092,7 @@
"description": "A generic function and convention to trigger deprecation notices",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.0"
+ "source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.1"
},
"funding": [
{
@@ -1108,24 +1108,24 @@
"type": "tidelift"
}
],
- "time": "2024-04-18T09:32:20+00:00"
+ "time": "2024-09-25T14:20:29+00:00"
},
{
"name": "symfony/polyfill-ctype",
- "version": "v1.30.0",
+ "version": "v1.31.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-ctype.git",
- "reference": "0424dff1c58f028c451efff2045f5d92410bd540"
+ "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/0424dff1c58f028c451efff2045f5d92410bd540",
- "reference": "0424dff1c58f028c451efff2045f5d92410bd540",
+ "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638",
+ "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638",
"shasum": ""
},
"require": {
- "php": ">=7.1"
+ "php": ">=7.2"
},
"provide": {
"ext-ctype": "*"
@@ -1171,7 +1171,7 @@
"portable"
],
"support": {
- "source": "https://github.com/symfony/polyfill-ctype/tree/v1.30.0"
+ "source": "https://github.com/symfony/polyfill-ctype/tree/v1.31.0"
},
"funding": [
{
@@ -1187,24 +1187,24 @@
"type": "tidelift"
}
],
- "time": "2024-05-31T15:07:36+00:00"
+ "time": "2024-09-09T11:45:10+00:00"
},
{
"name": "symfony/polyfill-mbstring",
- "version": "v1.30.0",
+ "version": "v1.31.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-mbstring.git",
- "reference": "fd22ab50000ef01661e2a31d850ebaa297f8e03c"
+ "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/fd22ab50000ef01661e2a31d850ebaa297f8e03c",
- "reference": "fd22ab50000ef01661e2a31d850ebaa297f8e03c",
+ "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/85181ba99b2345b0ef10ce42ecac37612d9fd341",
+ "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341",
"shasum": ""
},
"require": {
- "php": ">=7.1"
+ "php": ">=7.2"
},
"provide": {
"ext-mbstring": "*"
@@ -1251,7 +1251,7 @@
"shim"
],
"support": {
- "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.30.0"
+ "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.31.0"
},
"funding": [
{
@@ -1267,24 +1267,24 @@
"type": "tidelift"
}
],
- "time": "2024-06-19T12:30:46+00:00"
+ "time": "2024-09-09T11:45:10+00:00"
},
{
"name": "symfony/polyfill-php80",
- "version": "v1.30.0",
+ "version": "v1.31.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php80.git",
- "reference": "77fa7995ac1b21ab60769b7323d600a991a90433"
+ "reference": "60328e362d4c2c802a54fcbf04f9d3fb892b4cf8"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/77fa7995ac1b21ab60769b7323d600a991a90433",
- "reference": "77fa7995ac1b21ab60769b7323d600a991a90433",
+ "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/60328e362d4c2c802a54fcbf04f9d3fb892b4cf8",
+ "reference": "60328e362d4c2c802a54fcbf04f9d3fb892b4cf8",
"shasum": ""
},
"require": {
- "php": ">=7.1"
+ "php": ">=7.2"
},
"type": "library",
"extra": {
@@ -1331,7 +1331,7 @@
"shim"
],
"support": {
- "source": "https://github.com/symfony/polyfill-php80/tree/v1.30.0"
+ "source": "https://github.com/symfony/polyfill-php80/tree/v1.31.0"
},
"funding": [
{
@@ -1347,24 +1347,24 @@
"type": "tidelift"
}
],
- "time": "2024-05-31T15:07:36+00:00"
+ "time": "2024-09-09T11:45:10+00:00"
},
{
"name": "symfony/polyfill-php81",
- "version": "v1.30.0",
+ "version": "v1.31.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php81.git",
- "reference": "3fb075789fb91f9ad9af537c4012d523085bd5af"
+ "reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/3fb075789fb91f9ad9af537c4012d523085bd5af",
- "reference": "3fb075789fb91f9ad9af537c4012d523085bd5af",
+ "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c",
+ "reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c",
"shasum": ""
},
"require": {
- "php": ">=7.1"
+ "php": ">=7.2"
},
"type": "library",
"extra": {
@@ -1407,7 +1407,7 @@
"shim"
],
"support": {
- "source": "https://github.com/symfony/polyfill-php81/tree/v1.30.0"
+ "source": "https://github.com/symfony/polyfill-php81/tree/v1.31.0"
},
"funding": [
{
@@ -1423,24 +1423,25 @@
"type": "tidelift"
}
],
- "time": "2024-06-19T12:30:46+00:00"
+ "time": "2024-09-09T11:45:10+00:00"
},
{
"name": "symfony/yaml",
- "version": "v7.1.1",
+ "version": "v7.2.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/yaml.git",
- "reference": "fa34c77015aa6720469db7003567b9f772492bf2"
+ "reference": "099581e99f557e9f16b43c5916c26380b54abb22"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/yaml/zipball/fa34c77015aa6720469db7003567b9f772492bf2",
- "reference": "fa34c77015aa6720469db7003567b9f772492bf2",
+ "url": "https://api.github.com/repos/symfony/yaml/zipball/099581e99f557e9f16b43c5916c26380b54abb22",
+ "reference": "099581e99f557e9f16b43c5916c26380b54abb22",
"shasum": ""
},
"require": {
"php": ">=8.2",
+ "symfony/deprecation-contracts": "^2.5|^3.0",
"symfony/polyfill-ctype": "^1.8"
},
"conflict": {
@@ -1478,7 +1479,7 @@
"description": "Loads and dumps YAML files",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/yaml/tree/v7.1.1"
+ "source": "https://github.com/symfony/yaml/tree/v7.2.0"
},
"funding": [
{
@@ -1494,26 +1495,26 @@
"type": "tidelift"
}
],
- "time": "2024-05-31T14:57:53+00:00"
+ "time": "2024-10-23T06:56:12+00:00"
},
{
"name": "twig/markdown-extra",
- "version": "v3.11.0",
+ "version": "v3.16.0",
"source": {
"type": "git",
"url": "https://github.com/twigphp/markdown-extra.git",
- "reference": "504557d60d80478260ebd2221a2b3332a480865d"
+ "reference": "25f23c02936f8c7157a8413154c06a462c9c20d3"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/twigphp/markdown-extra/zipball/504557d60d80478260ebd2221a2b3332a480865d",
- "reference": "504557d60d80478260ebd2221a2b3332a480865d",
+ "url": "https://api.github.com/repos/twigphp/markdown-extra/zipball/25f23c02936f8c7157a8413154c06a462c9c20d3",
+ "reference": "25f23c02936f8c7157a8413154c06a462c9c20d3",
"shasum": ""
},
"require": {
- "php": ">=7.2.5",
+ "php": ">=8.0.2",
"symfony/deprecation-contracts": "^2.5|^3",
- "twig/twig": "^3.0"
+ "twig/twig": "^3.13|^4.0"
},
"require-dev": {
"erusev/parsedown": "^1.7",
@@ -1554,7 +1555,7 @@
"twig"
],
"support": {
- "source": "https://github.com/twigphp/markdown-extra/tree/v3.11.0"
+ "source": "https://github.com/twigphp/markdown-extra/tree/v3.16.0"
},
"funding": [
{
@@ -1566,31 +1567,31 @@
"type": "tidelift"
}
],
- "time": "2024-08-07T17:34:09+00:00"
+ "time": "2024-09-03T20:17:35+00:00"
},
{
"name": "twig/twig",
- "version": "v3.11.0",
+ "version": "v3.16.0",
"source": {
"type": "git",
"url": "https://github.com/twigphp/Twig.git",
- "reference": "e80fb8ebba85c7341a97a9ebf825d7fd4b77708d"
+ "reference": "475ad2dc97d65d8631393e721e7e44fb544f0561"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/twigphp/Twig/zipball/e80fb8ebba85c7341a97a9ebf825d7fd4b77708d",
- "reference": "e80fb8ebba85c7341a97a9ebf825d7fd4b77708d",
+ "url": "https://api.github.com/repos/twigphp/Twig/zipball/475ad2dc97d65d8631393e721e7e44fb544f0561",
+ "reference": "475ad2dc97d65d8631393e721e7e44fb544f0561",
"shasum": ""
},
"require": {
- "php": ">=7.2.5",
+ "php": ">=8.0.2",
"symfony/deprecation-contracts": "^2.5|^3",
"symfony/polyfill-ctype": "^1.8",
"symfony/polyfill-mbstring": "^1.3",
- "symfony/polyfill-php80": "^1.22",
"symfony/polyfill-php81": "^1.29"
},
"require-dev": {
+ "phpstan/phpstan": "^2.0",
"psr/container": "^1.0|^2.0",
"symfony/phpunit-bridge": "^5.4.9|^6.4|^7.0"
},
@@ -1634,7 +1635,7 @@
],
"support": {
"issues": "https://github.com/twigphp/Twig/issues",
- "source": "https://github.com/twigphp/Twig/tree/v3.11.0"
+ "source": "https://github.com/twigphp/Twig/tree/v3.16.0"
},
"funding": [
{
@@ -1646,7 +1647,7 @@
"type": "tidelift"
}
],
- "time": "2024-08-08T16:15:16+00:00"
+ "time": "2024-11-29T08:27:05+00:00"
}
],
"packages-dev": [],
diff --git a/www/src/db_handler/analytics_interface.php b/www/src/db_handler/analytics_interface.php
index fbe3400..de629c9 100644
--- a/www/src/db_handler/analytics_interface.php
+++ b/www/src/db_handler/analytics_interface.php
@@ -1,4 +1,3 @@
-
\ No newline at end of file
diff --git a/www/src/db_handler/mysql_handler.php b/www/src/db_handler/mysql_handler.php
index def19df..bac80c9 100644
--- a/www/src/db_handler/mysql_handler.php
+++ b/www/src/db_handler/mysql_handler.php
@@ -1,33 +1,16 @@
-
true,
'created_at' => true,
'created_at DESC' => true,
- 'modified_at' => true,
- 'modified_at DESC' => true
+ 'updated_at' => true,
+ 'updated_at DESC' => true
];
if(!isset($allowed_ordering[$order_by])) {
@@ -343,6 +326,167 @@ class MySQLHandler
return $data['post_markdown'];
}
+
+ public function parse_search_query_string($text) {
+ $element_array = explode(' ', $text);
+
+ $return_text = '';
+ $return_tags = [];
+ $return_options = [];
+
+ foreach($element_array as $element) {
+ if(strlen($element) == 0)
+ continue;
+
+ if(preg_match('/^(\w+):(.+)$/', $element, $match)) {
+ if($match[1] == 'tags') {
+ $return_tags = array_merge($return_tags, explode(',', $match[2]));
+ } else {
+ $return_options[$match[1]] = $match[2];
+ }
+ } else {
+ $return_text .= $element . ' ';
+ }
+ }
+
+ return [
+ 'text' => $return_text,
+ 'tags' => $return_tags,
+ 'options' => $return_options
+ ];
+ }
+
+ public function search_posts($options) {
+ // Function to perform an arbitrary search across
+ // the database.
+ //
+ // "options" input is a Hash with the following
+ // possible keys:
+ // - query: This text will be interpreted
+ // as a combination of text to search as well as
+ // tags, order-by requirements, etc.
+ // - text: This text will be used as unmodified
+ // input to the FULLTEXT matching
+ // - tags: This may be either a list or a string of tags
+ // to use for searching
+ // - path: Which path to search within
+ // - order_by: What column (if any) to search by
+ // - limit: Number of results to return, at most
+ // - offset: Number of results to skip before returning
+
+ if(gettype($options) == 'string') {
+ $options = [
+ 'query' => $options
+ ];
+ }
+
+ // Arrays to construct the query selection later
+ $qry_selects = ['posts.*'];
+ $qry_select_data = [];
+ $qry_select_types = '';
+
+ $qry_wheres = [];
+ $qry_where_data = [];
+ $qry_where_types = '';
+
+ $options['text'] ??= '';
+
+ if(gettype($options['tags'] ?? null) == 'string') {
+ $options['tags'] = TagList\_str_to_raw_taglist($options['tags']);
+ } else {
+ $options['tags'] ??= [];
+ }
+
+ $options['limit'] = min($options['limit'] ?? 100, 100);
+
+ // This code will take a generic user-input string, and will process it
+ // to see if there are any special options to consider.
+ //
+ // These options will always be overridden by the original "options"
+ // array. Text and Tags will be merged. For the limit, the minimum will
+ // be chosen.
+ if(isset($options['query'])) {
+ $search_options = $this->parse_search_query_string($options['query']);
+
+ if(strlen($search_options['text']) > 0) {
+ $options['text'] ??= '';
+ $options['text'] .= ' ' . $search_options['text'];
+ }
+
+ $options['tags'] = array_merge($options['tags'], $search_options['tags']);
+
+ if(isset($search_options['limit'])) {
+ $options['limit'] = min($options['limit'], intval($search_options['limit']));
+ }
+ if(isset($search_options['offset'])) {
+ $options['offset'] = intval($options['offset']);
+ }
+
+ $options = array_merge($options, $search_options['options']);
+ }
+
+ // If we have any tags, construct a tag-matching query
+ if(count($options['tags']) > 0) {
+ $tag_search_string = TagList\create_db_search($options['tags'])['parameter_string'];
+
+ $qry_wheres []= "MATCH(post_tags) AGAINST (? IN BOOLEAN MODE)";
+ $qry_where_data []= $tag_search_string;
+ $qry_where_types .= 's';
+ }
+
+ // If we have any text query strings, we get to construct a rather fun, complex
+ // array of MATCH() AGAINST() text queries.
+ if(strlen($options['text']) > 0) {
+ $text_search_scores = [0];
+ $text_search_wheres = [];
+ foreach([['title', 6], ['brief', 4], ['markdown', 1]] as $arg) {
+ $text_search_scores []= "((MATCH(post_" . $arg[0] . ") AGAINST (?)) * " . $arg[1] . ')';
+ $qry_select_data []= $options['text'];
+ $qry_select_types .= 's';
+
+ $text_search_wheres []= "(MATCH(post_" . $arg[0] . ") AGAINST (?))";
+ $qry_where_data []= $options['text'];
+ $qry_where_types .= 's';
+ }
+
+ $qry_selects []= '(' . implode('+', $text_search_scores) . ') AS post_search_score';
+ $qry_wheres []= '(' . implode(' OR ', $text_search_wheres) . ')';
+ } else {
+ $qry_selects []= '0 AS post_search_score';
+ }
+
+ if(isset($options['path']) && strlen($options['path']) > 0) {
+ $qry_wheres []= "post_path LIKE ?";
+ $qry_where_data []= $options['path'] . '%';
+ $qry_where_types .= 's';
+ }
+
+ if(count($qry_wheres) == 0) {
+ throw new Exception("No search filtering options supplied!");
+ }
+
+ $options['offset'] ??= 0;
+
+ $qry =
+ "SELECT " . implode(', ', $qry_selects) . "
+ FROM posts
+ LEFT JOIN post_markdown ON posts.post_id = post_markdown.post_id
+ WHERE " . implode(' and ', $qry_wheres) . "
+ ORDER BY post_search_score DESC
+ LIMIT " . $options['limit'] . "
+ OFFSET " . $options['offset'];
+
+ $search_results = $this->_exec($qry, $qry_select_types . $qry_where_types,
+ ...array_merge($qry_select_data, $qry_where_data))->fetch_all(MYSQLI_ASSOC);
+
+ $outdata = [];
+ foreach($search_results AS $post_element) {
+ $outdata []=
+ $this->process_postdata($post_element);
+ }
+
+ return $outdata;
+ }
}
?>
\ No newline at end of file
diff --git a/www/src/db_handler/mysql_taglist_handling.php b/www/src/db_handler/mysql_taglist_handling.php
new file mode 100644
index 0000000..2acd999
--- /dev/null
+++ b/www/src/db_handler/mysql_taglist_handling.php
@@ -0,0 +1,81 @@
+ $search_modifiers,
+ 'parameter_string' => $search_params
+ ];
+}
+
+?>
\ No newline at end of file
diff --git a/www/src/db_handler/post.php b/www/src/db_handler/post.php
index 4e9494e..78935bb 100644
--- a/www/src/db_handler/post.php
+++ b/www/src/db_handler/post.php
@@ -1,4 +1,3 @@
-
'question',
'text/markdown' => 'markdown',
+ 'markdown' => 'markdown',
'blog' => 'markdown',
'blog_list' => 'rectangle-list',
'directory' => 'folder',
@@ -62,19 +68,27 @@ class Post implements ArrayAccess {
return $icon_mapping[$type] ?? 'unknown';
}
+ public static function deduce_template($type) {
+ $template_mapping = [
+ 'directory' => 'directory',
+ 'gallery' => 'gallery',
+ 'image' => 'image'
+ ];
+
+ return $template_mapping[$type] ?? 'vanilla';
+ }
+
function __construct($post_handler, $post_data, $site_defaults) {
$this->handler = $post_handler;
$this->content_html = null;
$this->content_markdown = null;
-
- $this->site_defaults = $site_defaults;
if(!isset($post_data) or !isset($post_data['id'])) {
$post_data = $this->_generate_404($post_data);
}
- $data = $post_data;
+ $data = array_merge($site_defaults, $post_data);
if($data['path'] == '') {
$data['path'] = '/';
@@ -87,23 +101,33 @@ class Post implements ArrayAccess {
$data['url'] ??= 'http://' . $post_data['host'] . $post_data['path'];
$data['basename'] ??= basename($data['path']);
-
$data['title'] ??= basename($data['path']);
$data['tags'] ??= [];
- $data['type'] ??= self::_deduce_type($post_data['path']);
+ $data['type'] ??= self::deduce_type($post_data['path']);
- $data['icon'] ??= self::_deduce_icon($data['type']);
+ $data['icon'] ??= self::deduce_icon($data['type']);
+ $data['template'] ??= self::deduce_template($data['type']);
- if(isset($sql_meta['media_url'])) {
- $data['thumb_url'] ??= $data['media_url'];
- }
+ $data['media_url'] ??= self::deduce_media_url($data['path']);
+ $data['media_preview_url'] ??= $data['media_url'];
- $data['preview_image'] ??= $data['banners'][0]['src'] ?? null;
+ // TODO: Try to check for thumb image automatically here
+ $data['preview_image'] ??= $data['media_preview_url'] ??
+ $data['banners'][0]['src'] ?? null;
$data['brief'] ??= $data['title'];
+ if($data['type'] == 'gallery') {
+ $data['search'] ??= [
+ 'path' => $data['path'],
+ 'tags' => [
+ '+type:image'
+ ]
+ ];
+ }
+
$this->data = $data;
}
@@ -128,11 +152,7 @@ class Post implements ArrayAccess {
return $this->data[$name];
}
- if(is_null($this->site_defaults)) {
- throw new RuntimeException("Post site defaults have not been set properly!");
- }
-
- return $this->site_defaults[$name] ?? null;
+ return null;
}
public function offsetGet($offset) : mixed {
@@ -142,9 +162,6 @@ class Post implements ArrayAccess {
if(isset($this->data[$offset])) {
return true;
}
- if(isset($this->site_defaults[$offset])) {
- return true;
- }
return !is_null($this->offsetGet($offset));
}
@@ -189,7 +206,14 @@ class Post implements ArrayAccess {
$out_data['html'] = $this->get_html();
}
if(isset($options['children'])) {
- die();
+ $children = $this->get_child_posts();
+ $child_arrays = [];
+
+ foreach($children AS $child) {
+ array_push($child_arrays, $child->to_array());
+ }
+
+ $out_data['children'] = $child_arrays;
}
return $out_data;
diff --git a/www/src/db_handler/post_handler.php b/www/src/db_handler/post_handler.php
index 06fd3fc..8c6ad3f 100644
--- a/www/src/db_handler/post_handler.php
+++ b/www/src/db_handler/post_handler.php
@@ -56,6 +56,17 @@ class PostHandler {
return $out_list;
}
+
+ public function search_posts($search_query) {
+ $search_results = $this->db->search_posts($search_query);
+
+ $out_list = [];
+ foreach($search_results as $search_result) {
+ array_push($out_list, new Post($this, $search_result, $this->site_defaults));
+ }
+
+ return $out_list;
+ }
}
?>
\ No newline at end of file
diff --git a/www/src/dbtest.php b/www/src/dbtest.php
index ffbc378..01c795a 100644
--- a/www/src/dbtest.php
+++ b/www/src/dbtest.php
@@ -61,6 +61,8 @@ $db_connection->execute_query("DELETE FROM posts;");
$sql_adapter = new MySQLHandler($db_connection, $SERVER_HOST);
$adapter = new PostHandler($sql_adapter);
+$adapter->site_defaults = [];
+
$sql_adapter->debugging = true;
function test_accounce($title) {
@@ -127,15 +129,23 @@ test_accounce("Setting post markdown...");
$sql_adapter->set_postdata([
'path' => '/testing/markdowntest',
'markdown' => 'Inline markdown test should work...',
- 'title' => "A Markdown Test"
+ 'title' => "A Markdown Test",
+ 'brief' => "The dragons explore markdown, sort of properly... Maybe.",
+ 'tags' => ['one', 'two', 'three', 'sexee']
]);
$post = $sql_adapter->get_postdata('/testing/markdowntest');
var_dump($sql_adapter->get_post_markdown($post['id']));
$sql_adapter->set_post_markdown($post['id'],
'
- This is one hell of a cute test!
+
+This is one hell of a cute test!
+
> Just checking in...
+
+{{
+template: fragments/blog/card.html
+}}
'
);
var_dump($sql_adapter->get_post_markdown($post['id']));
diff --git a/www/src/dergdown.php b/www/src/dergdown.php
index 8380be7..1f8dbcd 100644
--- a/www/src/dergdown.php
+++ b/www/src/dergdown.php
@@ -1,24 +1,97 @@
-
-
highlighter = new Highlighter();
+ $this->highlighter = null;
+ $this->BlockTypes['{'] []= 'DergInsert';
+
+ $this->dergInsertRenderer = null;
+ }
+
+ public function setDergRenderer($dergRenderer) {
+ $this->dergInsertRenderer = $dergRenderer;
+ }
+
+ protected function blockDergInsert($Line, $currentBlock) {
+ if (preg_match('/^{{\s?(.*)$/', $Line['body'], $match)) {
+ return array(
+ 'text' => $match[1] ?? ''
+ );
+ }
+ }
+
+ protected function blockDergInsertContinue($Line, $Block) {
+ if(isset($Block['complete'])) {
+ return;
+ }
+
+ if(preg_match('/(.*)}}/', $Line['body'], $match)) {
+ $Block['text'] .= "\n" . $match[1];
+ $Block['complete'] = true;
+ return $Block;
+ }
+
+ $Block['text'] .= "\n" . $Line['body'];
+
+ return $Block;
+ }
+
+ protected function blockDergInsertComplete($Block) {
+ try {
+ $parsed_data = Yaml::parse($Block['text']);
+ }
+ catch (Exception $ex) {
+ return array(
+ 'markup' => '
+
+
Error in a dergen template!
+ YAML could not be parsed properly:
+
+ ' . $ex->getMessage() . '
'
+ );
+ }
+
+ try {
+ if(!isset($this->dergInsertRenderer)) {
+ throw new Exception("No Dergen Renderer was set!");
+ }
+
+ $render_output = $this->dergInsertRenderer->dergRender($parsed_data);
+ } catch (Exception $ex) {
+ return array(
+ 'markup' => '
+
+
Error in a dergen template!
+ Rendering engine threw an error:
+
+ ' . $ex->getMessage() . '
'
+ );
+ }
+
+ return array(
+ 'markup' => $render_output
+ );
}
protected function blockFencedCodeComplete($block)
- {
+ {
if (! isset($block['element']['text']['attributes'])) {
return $block;
}
+ if(!isset($this->highlighter)) {
+ $this->highlighter = new Highlighter();
+ }
+
$code = $block['element']['text']['text'];
$languageClass = $block['element']['text']['attributes']['class'];
$language = explode('-', $languageClass);
diff --git a/www/src/router.php b/www/src/router.php
index c2f3dc7..6db72cc 100644
--- a/www/src/router.php
+++ b/www/src/router.php
@@ -4,19 +4,21 @@ $data_time_start = microtime(true);
require_once '../vendor/autoload.php';
-require_once 'setup_site_config.php';
-require_once 'setup_db.php';
+require_once 'setup/site_config.php';
+require_once 'setup/db.php';
require_once 'fontawesome.php';
require_once 'dergdown.php';
-require_once 'setup_twig.php';
+require_once 'setup/twig.php';
$REQUEST_URI = parse_url($_SERVER['REQUEST_URI']);
$REQUEST_PATH = $REQUEST_URI['path'];
parse_str($REQUEST_URI['query'] ?? '', $REQUEST_QUERY);
+require_once 'setup/permissions.php';
+
if(preg_match('/^\/api/', $REQUEST_PATH)) {
require_once 'serve/api.php';
}
diff --git a/www/src/serve/ajax.php b/www/src/serve/ajax.php
index 679afe0..29db538 100644
--- a/www/src/serve/ajax.php
+++ b/www/src/serve/ajax.php
@@ -17,10 +17,15 @@ if(isset($REQUEST_QUERY['page'])) {
$ajax_args['page'] = $adapter->get_post($REQUEST_QUERY['page']);
}
+if(isset($REQUEST_QUERY['search'])) {
+ $ajax_args['search_results'] = $post->handler->search_posts($REQUEST_QUERY['search']);
+}
+
$ajax_args['fa'] = $FONT_AWESOME_ARRAY;
$ajax_args['page'] ??= $SITE_CONFIG['site_defaults'];
+$ajax_args['post'] ??= $ajax_args['page'];
-echo $twig->render('/ajax/' . $AJAX_REQUEST_TEMPLATE, $ajax_args);
+echo $twig->render($AJAX_REQUEST_TEMPLATE, $ajax_args);
?>
\ No newline at end of file
diff --git a/www/src/serve/api.php b/www/src/serve/api.php
new file mode 100644
index 0000000..ca95af9
--- /dev/null
+++ b/www/src/serve/api.php
@@ -0,0 +1,94 @@
+get_post($match[2]);
+
+ if(!isset($post)) {
+ echo json_encode([
+ 'found' => false,
+ 'status' => 404
+ ]);
+ } else {
+ echo $post->to_json($REQUEST_QUERY);
+ }
+ break;
+ case 'db_post':
+ echo json_encode($sql_adapter->get_postdata($match[2]));
+ break;
+
+ case 'upload':
+ if(!access_can_upload()) {
+ http_response_code(401);
+ echo json_encode([
+ 'status' => '401 Unauthorized'
+ ]);
+
+ die();
+ }
+
+ if( !isset($_POST['path']) or
+ !isset($_FILES['file'])) {
+ echo json_encode([
+ 'status' => 'Missing paramters (must POST path and file!)'
+ ]);
+ die();
+ }
+
+ $file_path = sanitize_post_path($_POST['path']);
+
+ $physical_file_path = $SITE_CONFIG['upload']['file_path'] . $file_path;
+
+ $file_dir = dirname($physical_file_path);
+
+ if(!is_dir($file_dir)) {
+ mkdir(dirname($physical_file_path), recursive: true);
+ }
+
+ move_uploaded_file($_FILES['file']['tmp_name'], $physical_file_path);
+
+ $file_ext = pathinfo($file_path, PATHINFO_EXTENSION);
+
+ if($file_ext == 'md') {
+
+ $is_directory = false;
+ $original_file_path = $file_path;
+
+ if(basename($file_path) == 'README.md') {
+ $is_directory = true;
+ $file_path = dirname($file_path);
+ }
+
+ $post_matter = YamlFrontMatter::parse(file_get_contents($physical_file_path));
+
+ $post_data = $post_matter->matter();
+
+ $post_data['path'] = $file_path;
+ $post_data['markdown'] = $post_matter->body();
+
+ // TODO: This should be moved to an appropriately abstracted prep function
+
+
+ if($is_directory) {
+ $post_data['base'] ??= $original_file_path;
+ $post_data['type'] ??= 'directory';
+ }
+
+ $post_data['tags'] ??= [];
+ $post_data['tags'] []= "type:" . ($post_data['type'] ?? Post::deduce_type($file_path));
+ $post_data['tags'] []= "path:" . $file_path;
+
+ $sql_adapter->set_postdata($post_data);
+ }
+
+ break;
+}
+
+
+?>
\ No newline at end of file
diff --git a/www/src/serve/post.php b/www/src/serve/post.php
index 6430550..5cea531 100644
--- a/www/src/serve/post.php
+++ b/www/src/serve/post.php
@@ -2,7 +2,6 @@
require_once 'fontawesome.php';
-$post = $adapter->get_post($REQUEST_PATH);
function render_root_template($template, $args = []) {
global $twig;
@@ -13,8 +12,8 @@ function render_root_template($template, $args = []) {
$args['page'] ??= $SITE_CONFIG['site_defaults'];
$page = $args['page'];
- $page['base'] ??= $page['url'];
-
+ $page['base'] ??= $page['url'] ?? null;
+
$args['opengraph'] = [
"site_name" => $page['site_name'] ?? 'Nameless Site',
"title" => $page['title'] ?? 'Titleless',
@@ -23,7 +22,7 @@ function render_root_template($template, $args = []) {
];
$args['banners'] = json_encode($page['banners'] ?? []);
-
+
$args['age_gate'] = (!isset($_COOKIE['AgeConfirmed']))
&& isset($SITE_CONFIG['age_gate']);
@@ -34,9 +33,79 @@ function render_pathed_content_template($template, $args = []) {
render_root_template($template, $args);
}
-render_pathed_content_template('post_types/markdown.html', [
- 'page' => $post
-]);
+function render_post($post, $args = []) {
+ $template = $post['template']
+ ?? ($post['markdown'] == '' ? 'directory' : 'vanilla');
+
+
+ if(isset($post['search_tags'])) {
+ $post['search'] = $post['search_tags'];
+ }
+
+ if(isset($post['search'])) {
+ $args['search_results'] = $post->handler->search_posts($post['search']);
+ }
+
+ $args['page'] = $post;
+
+ render_pathed_content_template(
+ 'render_templates/' . $template . '.html',
+ $args);
+}
+
+if($REQUEST_PATH == '/upload') {
+ render_root_template('upload.html');
+ die();
+}
+if($REQUEST_PATH == '/search') {
+ $search_results = [];
+ $display_type = 'none';
+
+ $search_query = [
+ 'user_input' => $_GET['query'] ?? null,
+ 'user_type' => $_GET['search_type'] ?? 'all',
+ 'types' => [['All', 'all'], ['Images', 'image'], ['Blog Posts', 'blog']]
+ ];
+
+ if(isset($_GET['query']) && strlen($_GET['query']) > 0) {
+ $search_request['query'] = $_GET['query'];
+
+ if(isset($_GET['search_type']) && $_GET['search_type'] != 'all') {
+ $search_request['tags'] []= 'type:' . $_GET['search_type'];
+ }
+
+ $search_results = $adapter->search_posts($search_request);
+
+ $type_count = [];
+ if(count($search_results) > 0) {
+ foreach($search_results as $result) {
+ $type_count[$result['type']] ??= 0;
+ $type_count[$result['type']] += 1;
+ }
+
+ $display_type = array_search(max($type_count), $type_count);
+ }
+ } else {
+ $display_type = 'no_search';
+ }
+
+ $search_page = $SITE_CONFIG['site_defaults'];
+ $search_page['path'] = '/search';
+ $search_page['title'] = 'Search';
+ $search_page['basename'] = 'search';
+
+ render_root_template('search.html', [
+ 'search_results' => $search_results,
+ 'page' => $search_page,
+ 'display_type' => $display_type,
+ 'search_query' => $search_query
+ ]);
+ die();
+}
+
+$post = $adapter->get_post($REQUEST_PATH);
+render_post($post);
+
die();
if(!isset($post)) {
diff --git a/www/src/setup_db.php b/www/src/setup/db.php
similarity index 74%
rename from www/src/setup_db.php
rename to www/src/setup/db.php
index 70b3d88..30a7357 100644
--- a/www/src/setup_db.php
+++ b/www/src/setup/db.php
@@ -29,14 +29,27 @@ $sql_adapter = new MySQLHandler($db_connection, $SERVER_HOST);
$adapter = new PostHandler($sql_adapter);
require_once 'dergdown.php';
+require_once 'setup/derg_insert.php';
-function dergdown_to_html($text) {
+function dergdown_to_html($post) {
+ $DergInsert = new DergInsertRenderer($post);
$Parsedown = new Dergdown();
+ $Parsedown->setDergRenderer($DergInsert);
- return $Parsedown->text($text);
+ $markdown = $post->markdown;
+
+ if($markdown == '') {
+ $markdown = '
+{{
+template: fragments/directory/inline.html
+}}
+';
+ }
+
+ return $Parsedown->text($markdown);
}
function post_to_html($post) {
- return dergdown_to_html($post->markdown);
+ return dergdown_to_html($post);
}
$adapter->markdown_engine = "post_to_html";
diff --git a/www/src/setup/derg_insert.php b/www/src/setup/derg_insert.php
new file mode 100644
index 0000000..8051a3e
--- /dev/null
+++ b/www/src/setup/derg_insert.php
@@ -0,0 +1,43 @@
+twig = $twig;
+ $this->post = $post;
+ $this->postAdapter = $adapter;
+ }
+
+ public function dergRender($renderConfig) {
+ global $FONT_AWESOME_ARRAY;
+
+ if(!isset($renderConfig['template'])) {
+ throw new Exception("No template type given!");
+ }
+
+ $template = $renderConfig['template'];
+
+ $args = [
+ 'post' => $this->post,
+ 'page' => $this->post,
+ 'fa' => $FONT_AWESOME_ARRAY
+ ];
+
+ if(isset($renderConfig['post'])) {
+ $args['post'] = $this->postAdapter->get_post($renderConfig['post']);
+ }
+ if(isset($renderConfig['page'])) {
+ $args['page'] = $this->postAdapter->get_post($renderConfig['page']);
+ }
+
+ return $this->twig->render($template, $args);
+ }
+}
+
+?>
\ No newline at end of file
diff --git a/www/src/setup/permissions.php b/www/src/setup/permissions.php
new file mode 100644
index 0000000..654f5a8
--- /dev/null
+++ b/www/src/setup/permissions.php
@@ -0,0 +1,26 @@
+ true,
+ "upload" => false
+];
+
+$ACCESS_KEY = $REQUEST_QUERY['ACCESS_KEY']
+ ?? $_POST['ACCESS_KEY']
+ ?? $_COOKIE['ACCESS_KEY']
+ ?? '';
+
+if($ACCESS_KEY == $SITE_CONFIG['ACCESS_KEY']) {
+ $ACCESS_PERMISSIONS = [
+ "read" => true,
+ "upload" => true
+ ];
+}
+
+function access_can_upload() {
+ global $ACCESS_PERMISSIONS;
+
+ return $ACCESS_PERMISSIONS['upload'];
+}
+
+?>
\ No newline at end of file
diff --git a/www/src/setup_site_config.php b/www/src/setup/site_config.php
similarity index 100%
rename from www/src/setup_site_config.php
rename to www/src/setup/site_config.php
diff --git a/www/src/setup_twig.php b/www/src/setup/twig.php
similarity index 100%
rename from www/src/setup_twig.php
rename to www/src/setup/twig.php
diff --git a/www/static/banner.js b/www/static/banner.js
index 3da66de..5ef9ee3 100644
--- a/www/static/banner.js
+++ b/www/static/banner.js
@@ -1,6 +1,8 @@
const BANNER_TIME = 600 * 1000.0
-const BANNER_ANIMATION = "opacity 0.8s linear, transform 0.1s linear"
+const BANNER_ANIMATION = "opacity 0.8s linear, transform 1s linear"
+// const BANNER_ANIMATION = "opacity 0.8s linear"
+
class BannerHandler {
constructor(banner_container, banner_image, banner_link) {
@@ -32,7 +34,7 @@ class BannerHandler {
console.log("Starting tick")
- this.bannerUpdateTimer = setInterval(() => { this.updateTick() }, 100);
+ this.bannerUpdateTimer = setInterval(() => { this.updateTick() }, 1000);
}
stopUpdateTick() {
if(this.bannerUpdateTimer === null) {
@@ -71,7 +73,7 @@ class BannerHandler {
const bannerPercentage = (bannerPercentageFrom + (bannerPercentageTo - bannerPercentageFrom) * this.currentPhase)
const banner_top = (1-bannerPercentage) * bannerTranslateMax
- this.bannerDOM.style.transform = "translateY(" + banner_top + 'px' + ")"
+ this.bannerDOM.style.transform = "translateZ(0.1px) translateY(" + banner_top + 'px)'
}
fadeOut() {
diff --git a/www/static/dergstyle.css b/www/static/dergstyle.css
index 6ccc0f6..39810d1 100644
--- a/www/static/dergstyle.css
+++ b/www/static/dergstyle.css
@@ -1,4 +1,13 @@
+@import "styles/age_gate.css";
+@import "styles/post_navbar.css";
+@import "styles/search.css";
+@import "styles/toc.css";
+
+/********************
+ * GENERAL SETTINGS *
+ ********************
+ */
* {
box-sizing: border-box;
@@ -25,6 +34,12 @@ body {
--text_1: #FFFFFF;
--text_border: #A0A0A080;
+ --content-width: min(100vw, calc(20rem + 40vw));
+ --content-total-margin: calc(calc(100vw - var(--content-width)) / 2);
+
+ --content-padding: max(0.5rem, min(1rem, var(--content-total-margin)));
+ --content-margin: max(0px, calc(var(--content-total-margin) - 1rem));
+
color: var(--text_1);
background: var(--bg_1);
margin: 0px;
@@ -42,6 +57,17 @@ body {
}
}
+@media only screen and (max-width: 1000px) {
+ #toc_container {
+ display: none !important;
+ visibility: hidden !important;
+ }
+
+ #main_content_flexbox:before {
+ flex: 0.5 0 0 !important;
+ }
+}
+
:link {
color: var(--highlight_1);
font-style: italic;
@@ -56,45 +82,6 @@ a:hover {
color: var(--highlight_2);
}
-#age_gate_block {
- position: fixed;
- top: 0;
- left: 0;
- z-index: 1040;
- width: 100vw;
- height: 100vh;
- background-color: rgba(0, 0, 0, 0.4);
- backdrop-filter: blur(20px);
-
- transition: opacity 0.8s linear;
-}
-
-#age_gate_block div {
- background-color: var(--bg_3);
- border-radius: 1em;
-
- padding: 1em;
-
- width: 12em;
-
- margin:0 auto;
- display: table;
- position: absolute;
-
- left: 0;
- right:0;
- top: 50%;
- -webkit-transform:translateY(-50%);
- -moz-transform:translateY(-50%);
- -ms-transform:translateY(-50%);
- -o-transform:translateY(-50%);
- transform:translateY(-50%);
-}
-
-#age_gate_block p {
- min-height: 5em;
-}
-
#main_header {
overflow: hidden;
position: relative;
@@ -157,19 +144,31 @@ a:hover {
scroll-margin-top: 6rem;
}
+#main_content_flexbox {
+ width: 100vw;
+
+ display: flex;
+ justify-content: left;
+ align-items: flex-start;
+
+ padding-left: 1em;
+ padding-right: 1em;
+
+ &:before {
+ content: '';
+ flex: 0.2 0 0;
+ }
+}
+
#main_content_wrapper {
- --content-width: min(100vw, calc(20rem + 40vw));
- --content-total-margin: calc(calc(100vw - var(--content-width)) / 2);
+ /*padding: 0rem var(--content-padding) 1rem var(--content-padding);*/
+
+ /*width: var(--content-width);*/
+ flex: 0 1 var(--content-width);
- --content-padding: max(0.5rem, min(1rem, var(--content-total-margin)));
- --content-margin: max(0px, calc(var(--content-total-margin) - 1rem));
-
- padding: 0rem var(--content-padding) 1rem var(--content-padding);
- width: auto;
-
-
- margin-left: var(--content-margin);
- margin-right: var(--content-margin);
+ /*margin-left: calc(var(--content-margin));
+ //margin-right: var(--content-margin);*/
+
margin-top: 0px;
min-height: 100%;
@@ -206,137 +205,42 @@ body.htmx-request::before {
transition-delay: 0.5s;
}
-#post_file_bar {
- position: sticky;
+.folder_listing {
+ & input {
+ display: none;
+ }
- top: 0px;
+ & input ~ ul {
+ display: none;
+ }
+ & input:checked ~ ul {
+ display: block;
+ }
- background: var(--bg_2);
-
- box-shadow: 0px 5px 5px 0px #00000040;
- z-index: 5;
+ label > :nth-child(2) {
+ display: none;
+ }
+ input:checked ~ label > :nth-child(1) {
+ display: none;
+ }
+ input:checked ~ label > :nth-child(2) {
+ display: inline-block;
+ }
}
-
-#post_file_titles {
- display: flex;
- flex-direction: row;
- justify-content: left;
-
- list-style-type: none;
- padding: 0px;
-}
-
-#post_file_titles > li {
- padding: 0.2rem 0.8rem;
-
- font-style: bold;
- font-size: 1.5rem;
-
- background: var(--highlight_1);
-}
-
-#post_file_path {
- width: 100%;
- font-style: italic;
- padding-left: 0.5rem;
-
- background: var(--highlight_1);
-
- font-size: 1.1rem;
-
- display: flex;
- flex-direction: row;
- list-style-type: none;
-
- overflow-x: scroll;
- overflow-y: hidden;
-
- white-space: nowrap;
-}
-#post_file_path a {
- color: var(--text_1);
- padding-right: 0.2rem;
-}
-
-.navbar-expand {
- background-color: var(--bg_3);
-
- border: 0.15rem solid var(--highlight_1);
- border-top: none;
-
- border-radius: 0 0 0.2rem 0.2rem;
- overflow: clip;
-
- padding-bottom: 0.2rem;
- padding-left: 0.3rem;
-
- max-height: 80vh;
- overflow-y: scroll;
-}
-
-.navbar-full-path {
- width: 100%;
- font-style: italic;
- padding-left: 0.2rem;
-
- display: flex;
- flex-direction: row;
- list-style-type: none;
-
- overflow-x: scroll;
- overflow-y: hidden;
-
- white-space: nowrap;
-}
-.navbar-full-path li {
- margin-left: 0.3rem;
-}
-
-.navbar-folder-list span, .navbar-folder-list label {
- width: 1rem;
- display: inline-block;
+label.expandable {
cursor: pointer;
}
-.navbar-folder-list ul, .navbar-folder-list {
- list-style: none;
- padding-left: 0.4rem;
- margin-left: 0.4rem;
-
- border-left: 1px solid var(--text_border);
-}
-.navbar-folder-list a:hover {
- background-color: rgba(255, 255, 255, 0.1);
-}
-.folder-listing input {
+input.expandable {
display: none;
}
-.folder-listing input ~ ul {
+input.expandable + .expandable {
display: none;
}
-.folder-listing input:checked ~ ul {
+input.expandable:checked + .expandable {
display: block;
}
-.folder-listing label > :nth-child(2) {
- display: none;
-}
-.folder-listing input:checked ~ label > :nth-child(1) {
- display: none;
-}
-.folder-listing input:checked ~ label > :nth-child(2) {
- display: inline-block;
-}
-#navbar-expand-label {
- cursor: pointer;
-}
-#navbar-expander, .navbar-expand {
- display: none;
-}
-
-#navbar-expander:checked + .navbar-expand {
- display: block;
-}
article {
background: var(--bg_3);
@@ -345,17 +249,17 @@ article {
box-shadow: 3px 7px 7px 0px #00000040;
padding: 0.75rem;
-}
-
-article img {
- display: block;
- max-width: 100%;
-
- max-height: 70vh;
- margin: 1vmin;
- margin-right: auto;
- margin-left: auto;
+ & img {
+ display: block;
+ max-width: 100%;
+
+ max-height: 70vh;
+
+ margin: 1vmin;
+ margin-right: auto;
+ margin-left: auto;
+ }
}
#content_footer {
@@ -380,9 +284,8 @@ article img {
position: absolute;
bottom: 0px;
width: 100%;
-}
-
-#main_footer span {
- align-self: flex-end;
- width: 100%;
+ & span {
+ align-self: flex-end;
+ width: 100%;
+ }
}
diff --git a/www/static/modest.css b/www/static/modest.css
index d45177d..c1231e2 100644
--- a/www/static/modest.css
+++ b/www/static/modest.css
@@ -93,111 +93,111 @@ html {
article {
line-height: 1.5;
-}
-article p,
-.modest-p {
- font-size: 1rem;
- margin-bottom: 1.3rem;
-}
-
-article h1,
-.modest-h1,
-article h2,
-.modest-h2,
-article h3,
-.modest-h3,
-article h4,
-.modest-h4 {
- margin: 1.5em 0 .3em;
- font-weight: inherit;
- line-height: 1.42;
-
- padding-left: 1.5rem;
-}
-
-article > :first-child {
- margin-top: 0.3rem !important;
-}
-
-article h1,
-.modest-h1 {
- margin-top: 0;
- font-size: 1.998rem;
-}
-
-article h2,
-.modest-h2 {
- font-size: 1.427rem;
-}
-
-article h3,
-.modest-h3 {
- font-size: 1.299rem;
-}
-
-article h4,
-.modest-h4 {
- font-size: 1.1rem;
-}
-
-article h5,
-.modest-h5 {
- font-size: 1rem;
-}
-
-article h6,
-.modest-h6 {
- font-size: .88rem;
-}
-
-article small,
-.modest-small {
- font-size: .707rem;
-}
-
-/* https://github.com/mrmrs/fluidity */
-
-article h1,
-article h2,
-article h3 {
- border-bottom: 1px solid var(--text_border);
- padding-bottom: .3rem;
-}
-
-blockquote {
- padding-left: 0.8rem;
- margin-left: 0.8rem;
- margin-right: 4em;
-}
-
-blockquote {
- border-left: 4px solid var(--text_border);
- text-align: justify;
-}
-
-pre {
- border-radius: 0.5rem;
-
- box-shadow: 2px 5px 5px 0px #00000040;
-
- border-left: 4px solid #206475;
- background-color: var(--bg_2);
- margin-bottom: 1.3rem;
- margin-left: 0.8rem;
- margin-right: 4em;
-}
-pre code {
- border-radius: 0.5rem;
-}
-
-@media screen and (max-width: 32rem) {
- pre, blockquote {
- margin-right: 1.5em;
+ & p,
+ .modest-p {
+ font-size: 1rem;
+ margin-bottom: 1.3rem;
}
-}
-article ul,
-article ol {
- padding-left: 2em;
+ & h1,
+ .modest-h1,
+ & h2,
+ .modest-h2,
+ & h3,
+ .modest-h3,
+ & h4,
+ .modest-h4 {
+ margin: 1.5em 0 .3em;
+ font-weight: inherit;
+ line-height: 1.42;
+
+ padding-left: 1.5rem;
+ }
+
+ & > :first-child {
+ margin-top: 0.3rem !important;
+ }
+
+ & h1,
+ .modest-h1 {
+ margin-top: 0;
+ font-size: 1.998rem;
+ }
+
+ & h2,
+ .modest-h2 {
+ font-size: 1.427rem;
+ }
+
+ & h3,
+ .modest-h3 {
+ font-size: 1.299rem;
+ }
+
+ & h4,
+ .modest-h4 {
+ font-size: 1.1rem;
+ }
+
+ & h5,
+ .modest-h5 {
+ font-size: 1rem;
+ }
+
+ & h6,
+ .modest-h6 {
+ font-size: .88rem;
+ }
+
+ & small,
+ .modest-small {
+ font-size: .707rem;
+ }
+
+ /* https://github.com/mrmrs/fluidity */
+
+ & h1,
+ & h2,
+ & h3 {
+ border-bottom: 1px solid var(--text_border);
+ padding-bottom: .3rem;
+ }
+
+ blockquote {
+ padding-left: 0.8rem;
+ margin-left: 0.8rem;
+ margin-right: 4em;
+ }
+
+ blockquote {
+ border-left: 4px solid var(--text_border);
+ text-align: justify;
+ }
+
+ pre {
+ border-radius: 0.5rem;
+
+ box-shadow: 2px 5px 5px 0px #00000040;
+
+ border-left: 4px solid #206475;
+ background-color: var(--bg_2);
+ margin-bottom: 1.3rem;
+ margin-left: 0.8rem;
+ margin-right: 4em;
+ }
+ pre code {
+ border-radius: 0.5rem;
+ }
+
+ @media screen and (max-width: 32rem) {
+ pre, blockquote {
+ margin-right: 1.5em;
+ }
+ }
+
+ & ul,
+ & ol {
+ padding-left: 2em;
+ }
}
\ No newline at end of file
diff --git a/www/static/styles/age_gate.css b/www/static/styles/age_gate.css
new file mode 100644
index 0000000..bc4728f
--- /dev/null
+++ b/www/static/styles/age_gate.css
@@ -0,0 +1,42 @@
+/****************
+ * AGE GATE CSS *
+ ****************/
+
+ #age_gate_block {
+ position: fixed;
+ top: 0;
+ left: 0;
+ z-index: 1040;
+ width: 100vw;
+ height: 100vh;
+ background-color: rgba(0, 0, 0, 0.4);
+ backdrop-filter: blur(20px);
+
+ transition: opacity 0.8s linear;
+
+ & div {
+ background-color: var(--bg_3);
+ border-radius: 1em;
+
+ padding: 1em;
+
+ width: 12em;
+
+ margin:0 auto;
+ display: table;
+ position: absolute;
+
+ left: 0;
+ right:0;
+ top: 50%;
+ -webkit-transform:translateY(-50%);
+ -moz-transform:translateY(-50%);
+ -ms-transform:translateY(-50%);
+ -o-transform:translateY(-50%);
+ transform:translateY(-50%);
+ }
+
+ & p {
+ min-height: 5em;
+ }
+}
\ No newline at end of file
diff --git a/www/static/styles/post_navbar.css b/www/static/styles/post_navbar.css
new file mode 100644
index 0000000..d663dac
--- /dev/null
+++ b/www/static/styles/post_navbar.css
@@ -0,0 +1,122 @@
+
+
+.navbar {
+ position: sticky;
+
+ top: 0px;
+
+ background: var(--bg_2);
+
+ box-shadow: 0px 5px 5px 0px #00000040;
+ z-index: 5;
+
+ & > ._titles {
+ display: flex;
+ flex-direction: row;
+ justify-content: left;
+
+ height: 2.3rem;
+
+ list-style-type: none;
+ padding: 0px;
+
+ & li {
+ padding: 0.2rem 0.8rem;
+
+ font-style: bold;
+ font-size: 1.5rem;
+
+ background: var(--highlight_1);
+ }
+ }
+
+ & > ._path {
+ width: 100%;
+ height: 1.5rem;
+
+ font-style: italic;
+ padding-left: 0.5rem;
+
+ background: var(--highlight_1);
+
+ font-size: 1.1rem;
+
+ display: flex;
+ flex-direction: row;
+ list-style-type: none;
+
+ overflow-x: scroll;
+ overflow-y: hidden;
+
+ white-space: nowrap;
+
+ & a {
+ color: var(--text_1);
+ padding-right: 0.2rem;
+ }
+ }
+
+ & > ._expand_label {
+ cursor: pointer;
+ }
+ & > ._details {
+ background-color: var(--bg_3);
+
+ border: 0.15rem solid var(--highlight_1);
+ border-top: none;
+
+ border-radius: 0 0 0.2rem 0.2rem;
+ overflow: clip;
+
+ padding-bottom: 0.2rem;
+ padding-left: 0.3rem;
+
+ max-height: 80vh;
+ overflow-y: scroll;
+
+ & > ._full_path {
+ width: 100%;
+ font-style: italic;
+ padding-left: 0.2rem;
+
+ display: flex;
+ flex-direction: row;
+ list-style-type: none;
+
+ overflow-x: scroll;
+ overflow-y: hidden;
+
+ white-space: nowrap;
+
+ & li {
+ margin-left: 0.3rem;
+ }
+ }
+
+ & > ._folder_list {
+ list-style: none;
+ padding-left: 0.4rem;
+ margin-left: 0.4rem;
+
+ border-left: 1px solid var(--text_border);
+
+ & ul {
+ list-style: none;
+ padding-left: 0.4rem;
+ margin-left: 0.4rem;
+
+ border-left: 1px solid var(--text_border);
+ }
+
+ & a:hover {
+ background-color: rgba(255, 255, 255, 0.1);
+ }
+
+ & span, & label {
+ width: 1rem;
+ display: inline-block;
+ cursor: pointer;
+ }
+ }
+ }
+}
diff --git a/www/static/styles/search.css b/www/static/styles/search.css
new file mode 100644
index 0000000..0141876
--- /dev/null
+++ b/www/static/styles/search.css
@@ -0,0 +1,113 @@
+
+
+.dergen_search_form {
+ text-align: center;
+
+ & h1 {
+ padding-left: 0px;
+
+ border: none;
+ margin-bottom: 0.1em;
+ }
+
+ & input[type='text'] {
+ color: var(--text_1);
+
+ height: 2.5rem;
+ min-width: min(80%, 30em);
+
+ font-size: 1.2em;
+
+ background-color: #616161;
+
+ border: none;
+ border-radius: 1.25rem;
+
+ padding-left: 1.5rem;
+
+ margin-bottom: 0.8em;
+
+ box-shadow: 3px 3px 3px #00000033;
+ }
+
+ & button {
+ color: var(--text_1);
+ background-color: #515151;
+ border: none;
+
+ border-radius: 0.5rem;
+ cursor: pointer;
+
+ width: 15em;
+ height: 2.5em;
+ margin-left: 1em;
+ margin-right: 1em;
+ margin-bottom: 1em;
+
+ box-shadow: 3px 3px 2px #00000033;
+ }
+
+ button, input[type='text'] {
+ transition: background-color 0.3s;
+
+ &:hover {
+ background-color: #777777;
+ }
+ }
+
+ & ._search_type {
+ text-align: left;
+
+ margin-top: 1em;
+
+ & input {
+ display: none;
+ }
+ & label {
+ min-width: 5em;
+ text-align: center;
+
+ padding-left: 0.7em;
+ padding-right: 0.7em;
+
+ cursor: pointer;
+ display: inline-block;
+
+ border-bottom: 2px solid #FFFFFF50;
+ }
+ & input:checked + label {
+ border-bottom: 2px solid #FFFFFF;
+ }
+ }
+}
+
+
+.dergen_search_result_listing {
+ list-style: none;
+
+ margin-left: 0em;
+ padding-left: 0;
+
+ & > li {
+ margin-bottom: 2em;
+ }
+
+ & ._details {
+ list-style: none;
+ padding-left: 1.3em;
+
+ & ._path_details {
+ color: #AAAAAA;
+
+ & span {
+ font-size: 0.8rem;
+ min-width: 5rem;
+ display: inline-block;
+ }
+ }
+
+ & ._title {
+ font-size: 1.5rem;
+ }
+ }
+}
\ No newline at end of file
diff --git a/www/static/styles/toc.css b/www/static/styles/toc.css
new file mode 100644
index 0000000..bb2ea8e
--- /dev/null
+++ b/www/static/styles/toc.css
@@ -0,0 +1,60 @@
+
+.table_of_contents {
+ position: sticky;
+ float: left;
+
+ top: 0px;
+
+ flex: 0.1 0 15em;
+
+ padding-right: 0.3em;
+
+ padding-bottom: 0.5em;
+ border-radius: 0 0em 0em 0.5em;
+
+ box-shadow: -0.2em 0.2em 0.2em black;
+
+ --toc-fg: #cecece;
+ --toc-bg: #EE901544;
+
+ background-color: var(--bg_2);
+
+ & a {
+ display: inline-block;
+
+ transition: all 0.5s !important;
+
+ line-height: 1em;
+ text-align: justify;
+ font-size: 0.9rem;
+
+ padding-left: 0.2em;
+ padding-right: 0.2em;
+ padding-bottom: 0.2em;
+
+
+
+ color: var(--toc-fg) !important;
+ }
+
+ & .active {
+ background-color: var(--toc-bg);
+ border-bottom: 1px solid var(--highlight_1);
+
+ transition: all 0.2s;
+ }
+
+ & ol {
+ padding-left: 0.5em;
+
+ list-style: none;
+ }
+
+ & > li {
+ border-radius: 0.2em;
+ border-bottom: 1px solid transparent;
+
+ margin-bottom: 0.2em;
+ transition: all 1s;
+ }
+}
\ No newline at end of file
diff --git a/www/static/toc.js b/www/static/toc.js
new file mode 100644
index 0000000..dafbee5
--- /dev/null
+++ b/www/static/toc.js
@@ -0,0 +1,265 @@
+// Code By Webdevtrick ( https://webdevtrick.com )
+// https://webdevtrick.com/dynamic-table-of-contents/
+//
+// Modified by dergens
+
+let tocId = "toc";
+
+const TOC_IDENTIFIER = "toc";
+const TOC_MAIN_CONTENT_ID = "#content_article"
+const TOC_TOP_PIXEL_MARGIN = 300;
+
+class TocTracker {
+ known_heading_elements = [];
+
+ constructor() {
+ this.intersection_observer = null;
+
+ this.tracking_update_callbacks = [];
+ this.tracking_rebuild_callbacks = [];
+ }
+
+ _disconnectIntersectionObserver() {
+ if(this.intersection_observer) {
+ this.intersection_observer.disconnect();
+ this.intersection_observer = null;
+ }
+ }
+
+ _setupIntersectionObserver() {
+ let options = {
+ root: null,
+ rootMargin: `-${TOC_TOP_PIXEL_MARGIN}px 0px 0px 0px`,
+ threshold: [1]
+ };
+
+ this.intersection_observer = new IntersectionObserver(
+ (entries) => this._findActiveHeading(),
+ options
+ );
+
+ this.known_heading_elements.forEach((element) => {
+ this.intersection_observer.observe(element.dom);
+ });
+ }
+
+ _searchForHeadings() {
+ const main = document.querySelector(TOC_MAIN_CONTENT_ID);
+ if (!main) {
+ throw Error("A `main` tag section is required to query headings from.");
+ }
+
+ let headings = main.querySelectorAll("h1, h2, h3, h4, h5, h6");
+ headings.forEach((heading, index) => {
+ const heading_level = parseInt(heading.tagName.slice(-1));
+
+ this.known_heading_elements.push({
+ level: heading_level,
+ name: heading.innerHTML,
+ dom: heading,
+ });
+ });
+ }
+
+ _sortHeadings() {
+ this.known_heading_elements.sort((a, b) => {
+ return a.dom.getBoundingClientRect().y
+ - b.dom.getBoundingClientRect().y;
+ });
+ }
+
+ _generateHeadingPaths() {
+ let current_path = [];
+
+ this.known_heading_elements.forEach(heading => {
+ while((current_path.length != 0) && (current_path[current_path.length-1].level >= heading.level))
+ current_path.pop();
+
+ current_path.push(heading);
+
+ // The zero does nothing. But according to stackoverflow, it makes it faster.
+ // Why? No idea.
+ // Just leave it in I suppose.
+ heading.path = current_path.slice(0);
+
+ });
+ }
+
+ _fillHeadingIDs() {
+ this.known_heading_elements.forEach(heading => {
+ heading.id = heading.dom.id;
+ if(heading.id)
+ return;
+
+ heading.dom.id = heading.path.map(i =>
+ i.name.replace(/\W+/g, '-').trim()
+ ).join('--').toLowerCase();
+
+ heading.id = heading.dom.id;
+ });
+ }
+
+ _findActiveHeading() {
+ const arr = this.known_heading_elements;
+
+ if(arr.length == 0)
+ return;
+
+ // Start should always be higher
+ // End always lower
+ let start = 0, end = arr.length - 1;
+ let mid = Math.floor((start + end) / 2);
+
+ // Special case handling, if all items are above the scroll margin
+ if(arr[end].dom.getBoundingClientRect().y < TOC_TOP_PIXEL_MARGIN) {
+ start = end;
+ mid = end;
+ }
+
+ // Iterate until we find the boundary
+ while (mid > start) {
+
+ // Check if the mid is above our boundary ((lower Y))
+ if (arr[mid].dom.getBoundingClientRect().y < TOC_TOP_PIXEL_MARGIN)
+ start = mid;
+ else
+ end = mid;
+
+ // Find the mid index
+ mid = Math.floor((start + end) / 2);
+ }
+
+ this.tracking_update_callbacks.forEach(callback => callback(arr[start]));
+
+ return arr[start];
+ }
+
+ reloadHeadings() {
+ this.known_heading_elements = [];
+
+ this._disconnectIntersectionObserver();
+ this._searchForHeadings();
+ this._sortHeadings();
+ this._generateHeadingPaths();
+ this._fillHeadingIDs();
+
+ this.tracking_rebuild_callbacks.forEach(item => item());
+
+ this._setupIntersectionObserver();
+ }
+
+ onTrackingUpdate(callback) {
+ this.tracking_update_callbacks.push(callback);
+
+ return callback;
+ }
+
+ onTrackingRebuild(callback) {
+ this.tracking_rebuild_callbacks.push(callback);
+
+ return callback;
+ }
+}
+
+class TocNavBarUpdater {
+ constructor(toc_tracker) {
+ this.toc_tracker = toc_tracker;
+ this.navbar_dom = null;
+
+ this.added_navbar_elements = [];
+
+ this.toc_tracker.onTrackingRebuild(() =>
+ this.trackingRebuildCallback() );
+
+ this.toc_tracker.onTrackingUpdate((element) => this.trackingUpdateCallback(element) );
+ }
+
+ _removeNavbarElements() {
+ this.added_navbar_elements.forEach(dom => dom.remove());
+ this.added_navbar_elements = [];
+ }
+
+ trackingRebuildCallback() {
+ this._removeNavbarElements();
+
+ this.navbar_dom = document.querySelector('#main_navbar_path');
+ }
+
+ trackingUpdateCallback(element) {
+ this._removeNavbarElements();
+
+ const navbar_prev_node = this.navbar_dom.children[this.navbar_dom.children.length -1];
+
+ element.path.forEach(pathItem => {
+ let newNode = document.createElement('li');
+
+ const pathURL = location.pathname + '#' + pathItem.id + location.search;
+ newNode.innerHTML = " #" + pathItem.level + '' + pathItem.name + ''
+
+ this.navbar_dom.insertBefore(newNode, navbar_prev_node);
+ this.added_navbar_elements.push(newNode);
+ });
+
+ }
+}
+
+class TocSidemenu {
+ constructor(toc_tracker) {
+ this.toc_tracker = toc_tracker;
+
+ this.sidebar_dom = null;
+
+ this.sidebar_elements = {};
+ this.currently_active_entry = null;
+
+ toc_tracker.onTrackingRebuild(() => this.trackingRebuildCallback());
+ toc_tracker.onTrackingUpdate((element) => this.trackingUpdateCallback(element));
+ }
+
+ _clearSidebar() {
+ if(this.sidebar_dom) {
+ this.sidebar_dom.remove();
+ }
+
+ this.sidebar_elements = {};
+ this.currently_active_entry = null;
+ }
+
+ _generateSidebar() {
+ this.toc_tracker.known_heading_elements.forEach(element => {
+ let new_element = document.createElement('li');
+
+ const pathURL = location.pathname + '#' + element.id + location.search;
+
+ new_element.style = "padding-left: " + element.level * 0.8 + "em";
+ new_element.innerHTML = "" + element.name + "";
+
+ this.sidebar_elements[element.id] = new_element;
+
+ this.sidebar_dom.appendChild(new_element);
+ });
+ }
+
+ trackingRebuildCallback() {
+ this._clearSidebar();
+
+ this.sidebar_dom = document.createElement('ol');
+ document.querySelector('#toc').appendChild(this.sidebar_dom);
+
+ this._generateSidebar();
+ }
+
+ trackingUpdateCallback(entry) {
+ if(this.currently_active_entry) {
+ this.currently_active_entry.classList.remove('active');
+ }
+
+ let active_entry = this.sidebar_elements[entry.dom.id];
+ active_entry.classList.add('active');
+ this.currently_active_entry = active_entry;
+ }
+}
+
+let tracker = new TocTracker();
+let navbar_updater = new TocNavBarUpdater(tracker);
+let sidebar_updater = new TocSidemenu(tracker);
\ No newline at end of file
diff --git a/www/templates/ajax/compact_filelist/listing.html b/www/templates/ajax/compact_filelist/listing.html
deleted file mode 100644
index ccdc7c9..0000000
--- a/www/templates/ajax/compact_filelist/listing.html
+++ /dev/null
@@ -1,4 +0,0 @@
-
-{% for post in page.child_posts %}
-{{ include('ajax/compact_filelist/entry.html') }}
-{% endfor %}
\ No newline at end of file
diff --git a/www/templates/fragments/article_blop.html b/www/templates/fragments/blog/card.html
similarity index 93%
rename from www/templates/fragments/article_blop.html
rename to www/templates/fragments/blog/card.html
index 0000271..f084f0a 100644
--- a/www/templates/fragments/article_blop.html
+++ b/www/templates/fragments/blog/card.html
@@ -1,5 +1,4 @@
-
@@ -16,7 +15,7 @@
- {{post.excerpt}}
+ {{ post.brief }}
diff --git a/www/templates/fragments/blog/listing.html b/www/templates/fragments/blog/listing.html
new file mode 100644
index 0000000..e69de29
diff --git a/www/templates/fragments/decoration/blog_header.html b/www/templates/fragments/decoration/blog_header.html
new file mode 100644
index 0000000..7dd4d48
--- /dev/null
+++ b/www/templates/fragments/decoration/blog_header.html
@@ -0,0 +1,10 @@
+
+
+
+ {{ post.title }}
+
+
+ {% if post.authors %}
+ Written by {{ post.authors }}
+ {% endif %}
+
\ No newline at end of file
diff --git a/www/templates/fragments/filepath_bar.html b/www/templates/fragments/decoration/navbar.html
similarity index 77%
rename from www/templates/fragments/filepath_bar.html
rename to www/templates/fragments/decoration/navbar.html
index f4a43a2..3c769e3 100644
--- a/www/templates/fragments/filepath_bar.html
+++ b/www/templates/fragments/decoration/navbar.html
@@ -1,10 +1,10 @@
-
-