Initial microblog
|
@ -0,0 +1,8 @@
|
|||
.DS_Store
|
||||
Thumbs.db
|
||||
db.json
|
||||
*.log
|
||||
node_modules/
|
||||
package-lock.json
|
||||
public/
|
||||
.deploy*/
|
|
@ -0,0 +1,73 @@
|
|||
image: node:alpine # Use latest version of Node.js on Alpine
|
||||
|
||||
cache:
|
||||
paths:
|
||||
- node_modules/
|
||||
|
||||
variables:
|
||||
NODE_ENV: "production"
|
||||
|
||||
# Rename to 'pages' for gitlab pages
|
||||
build:
|
||||
stage: build
|
||||
|
||||
before_script:
|
||||
- apk update && apk add git
|
||||
- npm install --include=optional --force
|
||||
|
||||
script:
|
||||
# Generate site
|
||||
- npm run build
|
||||
|
||||
rules:
|
||||
# Only trigger through push & "Run pipeline" events not in "site" branch; Skip in renovate job
|
||||
- if: '$RENOVATE != "true" && $CI_COMMIT_REF_NAME != "site" && ($CI_PIPELINE_SOURCE == "push" || $CI_PIPELINE_SOURCE == "web")'
|
||||
when: always
|
||||
|
||||
artifacts:
|
||||
paths:
|
||||
- public/
|
||||
expire_in: 30 days
|
||||
|
||||
test:
|
||||
stage: test
|
||||
|
||||
script:
|
||||
# /microblog/ should exist and non-empty
|
||||
- sh check-homepage.sh
|
||||
|
||||
rules:
|
||||
- if: '$RENOVATE != "true" && $CI_COMMIT_REF_NAME == "master" && ($CI_PIPELINE_SOURCE == "push" || $CI_PIPELINE_SOURCE == "web")'
|
||||
when: always
|
||||
|
||||
artifacts:
|
||||
paths:
|
||||
- public/
|
||||
expire_in: 30 days
|
||||
|
||||
deploy:
|
||||
stage: deploy
|
||||
|
||||
before_script:
|
||||
- apk update && apk add openssh-client rsync
|
||||
- mkdir -p ~/.ssh
|
||||
- chmod 700 ~/.ssh
|
||||
- echo "$SSH_KNOWN_HOSTS" > ~/.ssh/known_hosts
|
||||
- chmod 644 ~/.ssh/known_hosts
|
||||
- echo "$SSH_KEY_1" > ~/.ssh/id_host_1
|
||||
- chmod 600 ~/.ssh/id_host_1
|
||||
- echo "$SSH_KEY_2" > ~/.ssh/id_host_2
|
||||
- chmod 600 ~/.ssh/id_host_2
|
||||
- echo "$SSH_CONFIG" > ~/.ssh/config
|
||||
- chmod 600 ~/.ssh/config
|
||||
|
||||
script:
|
||||
- rsync -azvh --delete public/microblog/ host-1:/var/www/microblog/
|
||||
- rsync -azvh --delete public/microblog/ host-2:/var/www/microblog/
|
||||
|
||||
rules:
|
||||
- if: '$RENOVATE != "true" && $CI_COMMIT_REF_NAME == "microblog" && ($CI_PIPELINE_SOURCE == "push" || $CI_PIPELINE_SOURCE == "web")'
|
||||
when: always
|
||||
|
||||
include:
|
||||
- template: Security/Secret-Detection.gitlab-ci.yml
|
|
@ -0,0 +1,4 @@
|
|||
package-lock=false
|
||||
optional=true
|
||||
lockfile=false
|
||||
force=true
|
|
@ -0,0 +1,14 @@
|
|||
# Snyk (https://snyk.io) policy file, patches or ignores known vulnerabilities.
|
||||
version: v1.13.5
|
||||
# ignores vulnerabilities until expiry date; change duration by modifying expiry date
|
||||
ignore:
|
||||
SNYK-JS-HTMLMINIFIER-3091181:
|
||||
- "*":
|
||||
reason: Patch unavailable
|
||||
expires: "2024-06-30T00:00:00.000Z"
|
||||
SNYK-JS-HEXO-5889980:
|
||||
- "*":
|
||||
reason: "https://github.com/hexojs/hexo/pull/5251"
|
||||
expires: "2024-06-30T00:00:00.000Z"
|
||||
# patches apply the minimum changes required to fix a vulnerability
|
||||
patch:
|
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"editor.tabSize": 2,
|
||||
"files.eol": "\n",
|
||||
"files.trimTrailingWhitespace": true,
|
||||
"files.insertFinalNewline": true,
|
||||
"files.trimFinalNewlines": true,
|
||||
"standard.run": "onSave",
|
||||
"[markdown]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"editor.formatOnSave": true
|
||||
},
|
||||
"[yaml]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"editor.formatOnSave": true
|
||||
},
|
||||
"[json]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[jsonc]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2016-2018 GeekPlux <geekplux@gmail.com> ([Typing](https://github.com/geekplux/hexo-theme-typing) theme)
|
||||
Copyright (c) 2018-2024 Ming Di Leom
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -0,0 +1,83 @@
|
|||
# [mdleom.com](https://mdleom.com/)
|
||||
|
||||
[![Website Status](https://img.shields.io/website?url=https%3A%2F%2Fmdleom.com)](https://stats.uptimerobot.com/1394zup2LQ)
|
||||
[![Netlify Status](https://api.netlify.com/api/v1/badges/3f4b970a-c258-454c-97d6-3a8164f63262/deploy-status)](https://app.netlify.com/sites/curben/deploys)
|
||||
[![Build Status](https://gitlab.com/curben/blog/badges/master/pipeline.svg)](https://gitlab.com/curben/blog/-/jobs)
|
||||
[![Hexo version](https://img.shields.io/badge/hexo-hexojs/hexo-brightgreen.svg)](https://github.com/hexojs/hexo)
|
||||
|
||||
### Plugins
|
||||
|
||||
- Installed [hexo-yam](https://github.com/curbengh/hexo-yam) (author) to pre-compress static assets (html, css, js and svg).
|
||||
- Installed [hexo-nofollow](https://github.com/curbengh/hexo-nofollow) for SEO purpose to prevent search engines from following external links.
|
||||
- [copy-button.js](/themes/chameleon/scripts/copy-button.js) - A [filter](https://hexo.io/api/filter) plugin to add a copy button to each code block.
|
||||
- [feed](/themes/chameleon/scripts/feed) - A generator plugin to generate [an RSS feed](https://en.wikipedia.org/wiki/Web_feed). Adapted from [hexo-generator-feed](https://github.com/hexojs/hexo-generator-feed) for simpler use case.
|
||||
- [heading-link.js](/themes/chameleon/scripts/heading-link.js) - A filter plugin to add a link button next to each heading.
|
||||
- [image.js](/scripts/image.js) - A [tag](https://hexo.io/api/tag) plugin to easily embed images in a post with responsive image support.
|
||||
- [link.js](/themes/chameleon/scripts/link.js) - A [helper](https://hexo.io/api/helper) plugin to add a link. Modified from [link_to.js](https://github.com/hexojs/hexo/blob/master/lib/plugins/helper/link_to.js) to remove title attribute and 'external' option.
|
||||
- [openGraph](/themes/chameleon/scripts/openGraph.js) - A helper plugin to add [Open Graph](https://ogp.me/) tags. Modified from [open_graph.js](https://github.com/hexojs/hexo/blob/master/lib/plugins/helper/open_graph.js) to use additional [Open Graph](https://www.ogp.me/) tags and [WHATWG URL API](https://nodejs.org/api/url.html#url_the_whatwg_url_api).
|
||||
- [sitemap](/scripts/sitemap) - A [generator](https://hexo.io/api/generator) plugin to generate [a sitemap](https://en.wikipedia.org/wiki/Sitemaps). Modified from [hexo-generator-sitemap](https://github.com/hexojs/hexo-generator-sitemap) with this [patch](https://github.com/hexojs/hexo-generator-sitemap/pull/26) to include tags/categories and [remove](https://github.com/pyyzcwg2833/hexo-generator-sitemap/commit/a92dbbb83cc39ff60d43faa5cd688a56574a3889) [index.html](https://github.com/hexojs/hexo-generator-sitemap/pull/59) from the URL.
|
||||
|
||||
## Changes
|
||||
|
||||
The following are the major changes I made compared to the upstream.
|
||||
|
||||
### [Hexo](https://gitlab.com/pages/hexo) site
|
||||
|
||||
- Updated [.gitlab-ci.yml](.gitlab-ci.yml) to use the latest version of [Node.js](https://hub.docker.com/_/node/) in Alpine docker image.
|
||||
- [All packages](package.json) are installed from their respective master branch, instead of npm published version.
|
||||
- Removed unused packages, [hexo-generator-category](https://github.com/hexojs/hexo-generator-category) and [hexo-renderer-stylus](https://github.com/hexojs/hexo-renderer-stylus) from the [default packages](https://github.com/hexojs/hexo-starter/blob/571320ba41a83e065d7560e050eb3fa63ad74a57/package.json#L9-L17).
|
||||
- Replaced the bundled Landscape theme with Typing theme.
|
||||
- Add `lastUpdated` to the front-matter to manually set updated time of a post, instead of using `post.updated`.
|
||||
- This is no longer necessary, thanks to [`updated_option`](https://github.com/hexojs/hexo/pull/4278) feature.
|
||||
|
||||
### [Chameleon theme](/themes/chameleon)
|
||||
|
||||
Chameleon is a fork of [Typing](https://github.com/geekplux/hexo-theme-typing) theme, rewrote from scratch with the following changes/features:
|
||||
|
||||
- [sanitize.css](https://github.com/csstools/sanitize.css/) and [autoprefixer](https://github.com/csstools/sanitize.css/) for consistent cross-browser styling.
|
||||
- Utilise [relative length](https://www.w3schools.com/CSSref/css_units.asp) instead of absolute length in the css.
|
||||
- [`prefers-color-scheme`](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme) to apply light/dark theme according to the OS preference.
|
||||
- Add a [Copy](https://clipboardjs.com/) button to each code block.
|
||||
- Removed jQuery, fancyBox, web analytics, comment plugins, donation links and [typo.css](https://github.com/sofish/typo.css).
|
||||
- Homepage shows recent posts and tags.
|
||||
|
||||
## Installation
|
||||
|
||||
Refer to [this post](https://mdleom.com/2018/09/21/how-to-create-a-hexo-blog/) for more detailed instruction.
|
||||
|
||||
## License
|
||||
|
||||
Copyright (c) 2018-2024 Ming Di Leom
|
||||
|
||||
The content of this blog is licensed under a [CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/) license, and the underlying source code used to format and display that content is licensed under an [MIT license](LICENSE.md), unless indicated otherwise.
|
||||
|
||||
## Website Mirrors
|
||||
|
||||
- https://curben.pages.dev/
|
||||
- https://curben.netlify.app/
|
||||
- https://curben.gitlab.io/
|
||||
- https://curbengh.github.io/
|
||||
- https://xw226dvxac7jzcpsf4xb64r4epr6o5hgn46dxlqk7gnjptakik6xnzqd.onion/
|
||||
- http://mdleom.i2p/ ([address helper](http://mdleom.i2p/?i2paddresshelper=-NjUAy6H3wkgRfB3rBwGrpS56L2P~RHRDnD8HnRV1mLSKFdbzxHTMsGLo-mdgGq360Kni2Ec0qhRzm-IUc8X4Y0Ug1eYvcEp2ubXwLe5JJg7yZJOdGxqdy5y5VbdHfIuUe2ooG3MNA4v6b4pGk7pUQ7hnTkUi0EObD~79ik4AY-vSsxIFrc8kJxtbRMCQ3NQRhAuvS1A14rSVk0wv50YwKS23y~FUIQWyG8ZpjTVYu50n~oBnJtVKSAHbCMWRcnJx6iGFsbTRh4ZsRtDh0drwfeRkvaQQqQmf6nZOc4-GLxZ0RT5QlS5gdPXL4V7eaIETbNJAIeYr2NzcpwVHs~zp93Ga-p7dlH3TsJX5gJSyqJWCc64vvmkxf7Vseh3uGaa4xqiLjTH5XsOyFQLp5D6myt-yH7ggReZbs70NKqG1Mj5iRLhIC3Q~pJ6LkPnMBJN6QeLNYNWcOPXkMzRfsavvH2l3yxdpkn41BLM2-7bBUdJNXfu4OhGAR22O0gFngjUBQAEAAcAAA==&update=true))
|
||||
- http://ggucqf2jmtfxcw7us5sts3x7u2qljseocfzlhzebfpihkyvhcqfa.b32.i2p/
|
||||
|
||||
## Repository Mirrors
|
||||
|
||||
- https://github.com/curbengh
|
||||
- https://pagure.io/projects/curben/%2A
|
||||
- https://codeberg.org/curben
|
||||
- https://git.disroot.org/curben
|
||||
- https://git.nixnet.services/curben
|
||||
- https://gitea.com/curben
|
||||
- https://framagit.org/curben
|
||||
|
||||
## Uptime Status
|
||||
|
||||
- https://stats.uptimerobot.com/1394zup2LQ
|
||||
|
||||
---
|
||||
|
||||
Useful links:
|
||||
|
||||
- [Hexo Docs](https://hexo.io/docs/)
|
||||
- [GitLab Pages](https://docs.gitlab.com/ee/user/project/pages/index.html)
|
|
@ -0,0 +1,111 @@
|
|||
# Hexo Configuration
|
||||
## Docs: https://hexo.io/docs/configuration.html
|
||||
## Source: https://github.com/hexojs/hexo/
|
||||
|
||||
# Site
|
||||
title: Ming Di Leom's Blog
|
||||
subtitle:
|
||||
description:
|
||||
author: "Ming Di Leom"
|
||||
language: en-GB
|
||||
timezone:
|
||||
|
||||
# URL
|
||||
## If your site is put in a subdirectory, set url as 'http://yoursite.com/child' and root as '/child/'
|
||||
url: https://mdleom.com
|
||||
root: /
|
||||
permalink: microblog/:title/
|
||||
permalink_defaults:
|
||||
pretty_urls:
|
||||
trailing_index: false
|
||||
trailing_html: false
|
||||
|
||||
# Directory
|
||||
source_dir: source
|
||||
public_dir: public
|
||||
tag_dir: tags
|
||||
tag_generator:
|
||||
enable_index_page: true
|
||||
archive_dir: microblog
|
||||
category_dir: categories
|
||||
code_dir: ""
|
||||
i18n_dir: :lang
|
||||
skip_render:
|
||||
include:
|
||||
- "_headers" # Netlify header rule
|
||||
- "_redirects" # Netlify redirects rule
|
||||
|
||||
# Writing
|
||||
new_post_name: :title.md # File name of new posts
|
||||
default_layout: post
|
||||
titlecase: false # Transform title into titlecase
|
||||
external_link:
|
||||
enable: false # Open external links in new tab
|
||||
filename_case: 0
|
||||
render_drafts: false
|
||||
post_asset_folder: false
|
||||
relative_link: false
|
||||
future: true
|
||||
highlight:
|
||||
enable: true
|
||||
line_number: false
|
||||
auto_detect: false
|
||||
tab_replace: " "
|
||||
hljs: true
|
||||
wrap: false
|
||||
|
||||
# Tag
|
||||
tag_map:
|
||||
|
||||
# Date / Time format
|
||||
## Hexo uses Moment.js to parse and display date
|
||||
## You can customize the date format as defined in
|
||||
## http://momentjs.com/docs/#/displaying/format/
|
||||
date_format: YYYY-MM-DD
|
||||
time_format: HH:mm:ss
|
||||
updated_option: empty
|
||||
|
||||
# Pagination
|
||||
## Set per_page to 0 to disable pagination
|
||||
per_page: 10
|
||||
pagination_dir: page
|
||||
|
||||
marked:
|
||||
descriptionLists: false
|
||||
modifyAnchors: 1
|
||||
|
||||
# Extensions
|
||||
## Plugins: https://hexo.io/plugins/
|
||||
## Themes: https://hexo.io/themes/
|
||||
theme: chameleon
|
||||
|
||||
feed:
|
||||
icon: "/svg/favicon.svg"
|
||||
|
||||
# theme config
|
||||
theme_config:
|
||||
# Header
|
||||
menu:
|
||||
Home: /
|
||||
Blog: /blog/
|
||||
Microblog: /microblog/
|
||||
About: /about/
|
||||
Feed: /atom.xml
|
||||
GitLab: https://gitlab.com/curben/blog
|
||||
|
||||
footer:
|
||||
Disclaimer: /disclaimer/
|
||||
Status: https://stats.uptimerobot.com/1394zup2LQ
|
||||
Onion: https://xw226dvxac7jzcpsf4xb64r4epr6o5hgn46dxlqk7gnjptakik6xnzqd.onion/
|
||||
Eepsite: https://gitlab.com/curben/blog#mirrors
|
||||
|
||||
# Load forkawesome icons?
|
||||
icons: false
|
||||
|
||||
# https://github.com/curbengh/hexo-yam
|
||||
minify:
|
||||
svg:
|
||||
plugins:
|
||||
cleanupIds: false
|
||||
zstd:
|
||||
enable: true
|
|
@ -0,0 +1,16 @@
|
|||
#!/bin/sh
|
||||
|
||||
## Homepage should exists and non-empty
|
||||
|
||||
HOMEPAGE="public/microblog/index.html"
|
||||
|
||||
if [ ! -f "$HOMEPAGE" ]; then
|
||||
echo "Error: homepage doesn't exist"
|
||||
exit 1
|
||||
else
|
||||
FILE_SIZE=$(ls -s "$HOMEPAGE" | cut -d" " -f1)
|
||||
if [ "$FILE_SIZE" = 0 ]; then
|
||||
echo "Error: homepage is empty"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
|
@ -0,0 +1,4 @@
|
|||
[context.production]
|
||||
environment = { NODE_ENV = "production" }
|
||||
publish = "public"
|
||||
command = "npm run build"
|
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
"name": "blog",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"hexo": {
|
||||
"version": "7.2.0"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "hexo generate",
|
||||
"snyk": "snyk auth $SNYK_TOKEN && snyk-protect && snyk test && snyk monitor",
|
||||
"renovate": "renovate --platform 'gitlab' --autodiscover false --onboarding false --update-lock-files false --labels 'renovate' --require-config='ignored' \"$CI_PROJECT_PATH\""
|
||||
},
|
||||
"dependencies": {
|
||||
"hexo": "^7.0.0",
|
||||
"hexo-filter-nofollow": "hexojs/hexo-filter-nofollow",
|
||||
"hexo-generator-archive": "hexojs/hexo-generator-archive",
|
||||
"hexo-generator-index": "hexojs/hexo-generator-index",
|
||||
"hexo-generator-tag": "hexojs/hexo-generator-tag",
|
||||
"hexo-renderer-ejs": "hexojs/hexo-renderer-ejs",
|
||||
"hexo-renderer-marked": "hexojs/hexo-renderer-marked",
|
||||
"hexo-yam": "curbengh/hexo-yam"
|
||||
},
|
||||
"devDependencies": {
|
||||
"hexo-server": "hexojs/hexo-server"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
title: {{ title }}
|
||||
tags:
|
||||
---
|
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
title: {{ title }}
|
||||
date: {{ date }}
|
||||
---
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: {{ title }}
|
||||
date: {{ date }}
|
||||
tags:
|
||||
---
|
|
@ -0,0 +1,42 @@
|
|||
'use strict'
|
||||
/* global hexo */
|
||||
|
||||
/*
|
||||
* Embed an image with responsive images in a post
|
||||
* https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images
|
||||
* Image is resized on-the-fly using Statically (https://statically.io/)
|
||||
* Usage: ![alt](/path/to/img "title")
|
||||
*/
|
||||
|
||||
const { join } = require('path').posix
|
||||
|
||||
hexo.extend.filter.register('marked:renderer', (renderer) => {
|
||||
renderer.image = (href, title, alt) => {
|
||||
if (!alt) alt = ''
|
||||
if (!title) title = alt
|
||||
|
||||
if (href.endsWith('.svg')) return `<img class="svg" src="${href}" alt="${alt}" title="${title}">`
|
||||
|
||||
// embed external image
|
||||
if (href.startsWith('http')) return `<img src="${href}" alt="${alt}" title="${title}">`
|
||||
|
||||
const fLink = (path, width) => {
|
||||
const query = new URLSearchParams('f=auto')
|
||||
if (typeof width === 'number') query.set('width', width)
|
||||
const url = new URL('http://example.com/' + join('img', path) + '?' + query)
|
||||
|
||||
return url.pathname + url.search
|
||||
}
|
||||
|
||||
return `<a href="${join('/img', href)}">` +
|
||||
`<img srcset="${fLink(href, 320)} 320w,` +
|
||||
`${fLink(href, 468)} 468w,` +
|
||||
`${fLink(href, 768)} 768w,` +
|
||||
`${fLink(href)} 800w"` +
|
||||
' sizes="(max-width: 320px) 320px,' +
|
||||
'(max-width: 468px) 468px,' +
|
||||
'(max-width: 768px) 768px,' +
|
||||
'800px"' +
|
||||
` src="${fLink(href)}" title="${title}" alt="${alt}" loading="lazy"></a>`
|
||||
}
|
||||
})
|
|
@ -0,0 +1,48 @@
|
|||
const { escapeHTML: escape } = require('hexo-util')
|
||||
|
||||
// https://github.com/markedjs/marked/blob/b6773fca412c339e0cedd56b63f9fa1583cfd372/src/Lexer.js#L8-L24
|
||||
// Replace dashes only
|
||||
const smartypants = (str) => {
|
||||
return str
|
||||
// em-dashes
|
||||
.replace(/---/g, '\u2014')
|
||||
// en-dashes
|
||||
.replace(/--/g, '\u2013')
|
||||
// opening singles
|
||||
.replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
|
||||
// closing singles & apostrophes
|
||||
.replace(/'/g, '\u2019')
|
||||
// opening doubles
|
||||
.replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
|
||||
// closing doubles
|
||||
.replace(/"/g, '\u201d')
|
||||
// ellipses
|
||||
.replace(/\.{3}/g, '\u2026')
|
||||
// right arrow
|
||||
.replace(/->/g, '\u2192')
|
||||
}
|
||||
|
||||
hexo.extend.filter.register('marked:tokenizer', function (tokenizer) {
|
||||
const { smartypants: isSmarty } = this.config.marked
|
||||
tokenizer.inlineText = function (src) {
|
||||
const { rules } = this
|
||||
|
||||
// https://github.com/markedjs/marked/blob/b6773fca412c339e0cedd56b63f9fa1583cfd372/src/Tokenizer.js#L643-L658
|
||||
const cap = rules.inline.text.exec(src)
|
||||
if (cap) {
|
||||
let text
|
||||
if (this.lexer.state.inRawBlock) {
|
||||
text = cap[0]
|
||||
} else {
|
||||
text = escape(isSmarty ? smartypants(cap[0]) : cap[0])
|
||||
}
|
||||
return {
|
||||
// `type` value is a corresponding renderer method
|
||||
// https://marked.js.org/using_pro#inline-level-renderer-methods
|
||||
type: 'text',
|
||||
raw: cap[0],
|
||||
text
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Page not found
|
||||
layout: page
|
||||
sitemap: false
|
||||
---
|
|
@ -0,0 +1,10 @@
|
|||
/*
|
||||
Content-Security-Policy: default-src 'self'; child-src 'none'; connect-src 'none'; font-src 'none'; frame-src 'none'; img-src 'self'; manifest-src 'self'; media-src 'none'; object-src 'none'; prefetch-src 'none'; script-src 'self'; style-src 'self'; worker-src 'none'; base-uri 'none'; form-action https://duckduckgo.com https://duckduckgogg42xjoc72x3sjasowoarfbgcmvfimaftt6twagswzczad.onion; frame-ancestors 'none'; block-all-mixed-content; sandbox allow-forms allow-scripts
|
||||
Expires: 0
|
||||
Permissions-Policy: accelerometer=(), ambient-light-sensor=(), autoplay=(), battery=(), camera=(), display-capture=(), document-domain=(), encrypted-media=(), execution-while-not-rendered=(), execution-while-out-of-viewport=(), fullscreen=(), gamepad=(), geolocation=(), gyroscope=(), hid=(), magnetometer=(), microphone=(), midi=(), payment=(), picture-in-picture=(), publickey-credentials-get=(), screen-wake-lock=(), serial=(), speaker=(), speaker-selection=(), usb=(), web-share=(self), xr-spatial-tracking=(), interest-cohort=()
|
||||
Referrer-Policy: no-referrer
|
||||
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
|
||||
X-Content-Type-Options: nosniff
|
||||
X-Frame-Options: DENY
|
||||
X-XSS-Protection: 1; mode=block
|
||||
X-Robots-Tag: noindex
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
title: SSSD cache
|
||||
date: 2024-07-15
|
||||
---
|
||||
|
||||
If sssd unable to starts after upgrade/downgrade, clear its cache by deleting all files in the "/var/lib/sss/db/" folder. Encountered [this issue](https://bugzilla.redhat.com/show_bug.cgi?id=1576597) when upgrading/switching AlmaLinux 8 and CentOS Stream 9 to AlmaLinux 9.
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
title: Failing task
|
||||
date: 2024-07-16
|
||||
---
|
||||
|
||||
If a scheduled task has been failing for a while and no one bats an eye, just disable it.
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
title: TLS 1.3 in AWS services
|
||||
date: 2024-07-18
|
||||
---
|
||||
|
||||
At least 30% of `PUT` traffic in a Cloudtrail bucket is still TLS 1.2. Don't set [`minimum_tls_version=1.3`](https://docs.aws.amazon.com/cdk/api/v2/python/aws_cdk.aws_s3/Bucket.html) on that bucket yet.
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
title: Opt-out from ChatGPT model training
|
||||
date: 2024-07-22
|
||||
---
|
||||
|
||||
In OpenAI ChatGPT, it is now possible to disable [model training](https://help.openai.com/en/articles/7730893-data-controls-faq) while still keeping the chat history. [Previously](https://openai.com/index/new-ways-to-manage-your-data-in-chatgpt/), both had to be disabled together.
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
title: Huawei USB 4G + Creta
|
||||
date: 2024-08-23
|
||||
---
|
||||
|
||||
Huawei USB 4G modem E3372h-608 firmware 10.0.5.1 works out-of-the-box as a tethering connection with GL.iNet Creta AR750 firmware 4.3.17 running OpenWrt 22.03.4 r20123-38ccc47687.
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
title: Uninstall CentOS kernel
|
||||
date: 2024-09-08
|
||||
---
|
||||
|
||||
After converting from CentOS to AlmaLinux, uninstall CentOS kernels which would otherwise prevent dnf from installing newer Alma kernel due to higher version of CentOS kernel.
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
title: eSIM RSP certificate
|
||||
date: 2024-09-15
|
||||
---
|
||||
|
||||
If you are using any of these [removable eSIMs](https://euicc-manual.osmocom.org/docs/lpa/known-card/#product), when adding a new profile, check the SM-DP+ address has `81370f5125d0b1d408d4c3b232e6d25e795bebfb` (GSM Association - RSP2 Root CI1) as one of its issuer(s) in the [registry.csv](https://github.com/CursedHardware/gsma-rsp-certificates/blob/main/registry.csv) and does not contain an [ARA-M applet](https://osmocom.org/projects/sim-card-related/wiki/ESIM_profile_database) (which [complicates things](https://sysmocom.de/manuals/sysmoeuicc-manual.pdf) p. 26).
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
title: Splunk Connect for Syslog
|
||||
date: 2024-09-24
|
||||
---
|
||||
|
||||
In Splunk Connect for Syslog ([SC4S](https://splunk.github.io/splunk-connect-for-syslog/main/)), [`SC4S_DEST_GLOBAL_ALTERNATES`](https://splunk.github.io/splunk-connect-for-syslog/1.110.1/configuration/#configuration-of-alternate-destinations) has been deprecated since version 2. Instead, enable [`SC4S_ARCHIVE_GLOBAL`](https://splunk.github.io/splunk-connect-for-syslog/3.30.1/configuration/#archive-file-configuration) which stores a copy of events locally, not only for archiving (if that's what you intend) but also useful for troubleshooting dropped events.
|
||||
|
||||
If [`compliance_meta_by_source.conf`](https://splunk.github.io/splunk-connect-for-syslog/3.30.1/configuration/#override-index-or-metadata-based-on-host-ip-or-subnet-compliance-overrides) is not working, you may have to create a [custom post-filter](https://splunk.github.io/splunk-connect-for-syslog/3.30.1/create-parser/#create-a-parser_1) in "/opt/sc4s/local/config/filters/" instead.
|
|
@ -0,0 +1,6 @@
|
|||
# Reverse proxy to cdn
|
||||
/img/* https://curben.gitlab.io/blog/:splat 200
|
||||
/files/* https://curben.gitlab.io/blog/:splat 200
|
||||
# https://statically.discourse.group/t/mobile-version-not-working-on-screenshot/225
|
||||
# /screenshot/* https://cdn.statically.io/screenshot/device=mobile/curben.netlify.app/:splat 200
|
||||
/screenshot/* https://cdn.statically.io/screenshot/curben.netlify.app/:splat 200
|
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 15 KiB |
After Width: | Height: | Size: 7.1 KiB |
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" version="1" width="933.333" height="933.333" viewBox="0 0 700.000000 700.000000">
|
||||
<path style="fill:#3D73B0" d="M482.3 117.3c-1.8 4-4.7 10.3-6.4 14.2-1.7 3.8-4.8 10.8-6.9 15.5-2.1 4.7-4.9 11-6.3 14-1.3 3-5 11.3-8.2 18.5-3.1 7.1-7.2 16.3-9.1 20.3-1.9 4.1-3.4 7.5-3.4 7.7 0 .2-1.3 3.3-3 6.8-1.6 3.6-3 6.7-3 7 0 .3-1.8 4.3-3.9 8.9-2.1 4.5-7.6 16.6-12.1 26.8-7.4 16.7-12.8 28.8-18 40.6-1.1 2.3-5.1 11.5-9.1 20.5-3.9 9-8.7 19.7-10.5 23.7-1.9 4.1-3.4 7.5-3.4 7.7 0 .2-1.6 3.7-3.5 7.8s-3.5 7.7-3.5 8c0 .3-1.7 4.3-3.9 8.9-2.1 4.5-5.5 12.1-7.6 16.8-2.1 4.7-5 11.2-6.5 14.5-1.5 3.3-3.7 8.2-4.9 11-1.3 2.7-3 6.7-4 8.8l-1.7 3.8-3.1-6.8c-6.4-13.8-10.2-22.3-18.7-41.3-4.8-10.7-9.6-21.3-10.6-23.5-1-2.2-8.1-18.2-15.8-35.5-7.7-17.3-19.7-44.3-26.7-60-7-15.7-15.4-34.4-18.6-41.7-3.2-7.3-6.8-15.4-8-18-4.7-10.4-5.9-13.1-9.9-22.3-5-11.3-7.2-16.2-11.5-25.5-1.7-3.9-4-9-5.1-11.5-1-2.4-3.3-7.4-5-11-3.1-6.3-3.2-6.4-4.2-3.9-.6 1.4-2.9 6.5-5.1 11.3-2.3 4.9-4.1 9-4.1 9.2 0 .2-1.4 3.4-3.1 7.1-1.7 3.8-15 33.6-29.6 66.3-26.5 59.7-28.1 63.3-32.3 72.5-1.2 2.7-10.6 23.7-20.7 46.5-10.2 22.8-19.5 43.7-20.8 46.5-1.2 2.7-11.8 26.6-23.4 53-11.7 26.4-22.2 50-23.3 52.5-1.1 2.5-9.9 22.3-19.5 44S6.2 577.8 5.5 579.5c-.7 1.6-2.2 5.1-3.4 7.7l-2 4.8 12.2.1c13.1.1 232.5.1 249.2 0l10-.1 1.4-3.2c.8-1.8 2.4-5.6 3.7-8.3 1.2-2.8 8.4-19 15.9-36 16.9-38.3 42-94.3 47.1-105l3.7-8 4 8.5c2.1 4.7 7.5 16.6 11.9 26.5 4.4 9.9 8.9 20 10 22.5 1.1 2.5 9 20.2 17.5 39.5 8.5 19.2 16.7 37.8 18.4 41.3 1.6 3.5 2.9 6.5 2.9 6.7 0 1.6 7.2 15.3 8.2 15.7 2.7.9 280.3.2 281.9-.8 1.5-.8 1.4-1.4-.7-5.9-2.1-4.6-8-18-22.9-52-3.1-7.2-6.7-15.3-8-18-1.2-2.8-5.7-12.9-10-22.5-4.2-9.6-8.6-19.5-9.7-21.9-1.1-2.5-5.6-12.6-10-22.5-4.3-10-8.8-20.1-10-22.6-1.1-2.5-3.6-8.1-5.5-12.5-2-4.4-4.5-10.3-5.8-13-1.2-2.8-4.8-10.9-8-18-3.1-7.2-6.6-15-7.7-17.5-1.2-2.5-3.5-7.9-5.3-12-1.8-4.1-4.4-10-5.8-13-1.3-3-4.9-11.1-8-18-7-16.1-9.5-21.6-11.8-26.5-1-2.2-19.6-44.3-41.4-93.5-21.8-49.2-40.2-90-40.8-90.7-.9-.8-2 .6-4.4 6z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 2.0 KiB |
|
@ -0,0 +1,6 @@
|
|||
# This file only applies to mdleom.com via web server's internal rewrite
|
||||
User-agent: *
|
||||
Disallow: /404
|
||||
Disallow: /404.html
|
||||
|
||||
Sitemap: https://mdleom.com/sitemap.xml
|
|
@ -0,0 +1,3 @@
|
|||
# This file only applies to mirrors, see "robots-mdleom.com.txt"
|
||||
User-agent: *
|
||||
Disallow: /
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"name": "",
|
||||
"short_name": "",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/android-chrome-192x192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "/android-chrome-512x512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png"
|
||||
}
|
||||
],
|
||||
"theme_color": "#3f72af",
|
||||
"background_color": "#3f72af",
|
||||
"start_url": "https://mdleom.com/",
|
||||
"display": "standalone"
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
<!-- https://shields.io/badge/codeberg-curben-3f72af?logo=codeberg&style=for-the-badge -->
|
||||
<!-- Shields.io - https://shields.io/ - https://github.com/badges/shields - License: https://github.com/badges/shields/blob/master/LICENSE (Icons & Code: CC0 License) -->
|
||||
<!-- Codeberg icon is a trademark of Codeberg e.V. -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="184.5" height="28" role="img" aria-label="CODEBERG: CURBEN"><title>CODEBERG: CURBEN</title><g shape-rendering="crispEdges"><rect width="107" height="28" fill="#555"/><rect x="107" width="77.5" height="28" fill="#3f72af"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="100"><image x="9" y="7" width="14" height="14" xlink:href="data:image/svg+xml;base64,PHN2ZyBmaWxsPSIjMjE4NUQwIiByb2xlPSJpbWciIHZpZXdCb3g9IjAgMCAyNCAyNCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48dGl0bGU+Q29kZWJlcmc8L3RpdGxlPjxwYXRoIGQ9Ik0xMS45NTUuNDlBMTIgMTIgMCAwIDAgMCAxMi40OWExMiAxMiAwIDAgMCAxLjgzMiA2LjM3M0wxMS44MzggNS45MjhhLjE4Ny4xNCAwIDAgMSAuMzI0IDBsMTAuMDA2IDEyLjkzNUExMiAxMiAwIDAgMCAyNCAxMi40OWExMiAxMiAwIDAgMC0xMi0xMiAxMiAxMiAwIDAgMC0uMDQ1IDB6bS4zNzUgNi40NjdsNC40MTYgMTYuNTUzYTEyIDEyIDAgMCAwIDUuMTM3LTQuMjEzeiIvPjwvc3ZnPg=="/><text transform="scale(.1)" x="620" y="175" textLength="660" fill="#fff">CODEBERG</text><text transform="scale(.1)" x="1457.5" y="175" textLength="535" fill="#fff" font-weight="bold">CURBEN</text></g></svg>
|
After Width: | Height: | Size: 1.5 KiB |
|
@ -0,0 +1 @@
|
|||
<svg version="1" xmlns="http://www.w3.org/2000/svg" width="1024" height="1024" viewBox="0 0 736.000000 736.000000" fill="#3f72af"><path d="M436.1 282.7c-40.9 92.3-74.6 168.1-75 168.5-.3.4-32.5-71.3-71.5-159.3-39-87.9-71.2-159.9-71.5-159.9C217.6 132 2 616.9.2 622.1c-.2.6 53.9.8 142.4.7l142.8-.3 37.5-84.7c20.7-46.6 37.9-84.4 38.2-84 .4.4 17.5 38.6 38 85l37.3 84.2h149.9c142.3 0 149.9-.1 149.4-1.8-.2-.9-39.7-90.1-87.7-198.2-47.9-108.1-98.3-221.6-111.8-252.2-13.6-30.7-24.9-55.8-25.2-55.8-.3 0-34 75.5-74.9 167.7z"/></svg>
|
After Width: | Height: | Size: 521 B |
|
@ -0,0 +1,4 @@
|
|||
<!-- https://shields.io/badge/github-curbengh-3f72af?logo=github&style=for-the-badge -->
|
||||
<!-- Shields.io - https://shields.io/ - https://github.com/badges/shields - License: https://github.com/badges/shields/blob/master/LICENSE (Icons & Code: CC0 License) -->
|
||||
<!-- GitHub icon is a trademark of GitHub Inc -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="188" height="28"><g shape-rendering="crispEdges"><path fill="#555" d="M0 0h89v28H0z"/><path fill="#3f72af" d="M89 0h99v28H89z"/></g><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="100"><image x="9" y="7" width="14" height="14" xlink:href="data:image/svg+xml;base64,PHN2ZyBmaWxsPSJ3aGl0ZXNtb2tlIiByb2xlPSJpbWciIHZpZXdCb3g9IjAgMCAyNCAyNCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48dGl0bGU+R2l0SHViIGljb248L3RpdGxlPjxwYXRoIGQ9Ik0xMiAuMjk3Yy02LjYzIDAtMTIgNS4zNzMtMTIgMTIgMCA1LjMwMyAzLjQzOCA5LjggOC4yMDUgMTEuMzg1LjYuMTEzLjgyLS4yNTguODItLjU3NyAwLS4yODUtLjAxLTEuMDQtLjAxNS0yLjA0LTMuMzM4LjcyNC00LjA0Mi0xLjYxLTQuMDQyLTEuNjFDNC40MjIgMTguMDcgMy42MzMgMTcuNyAzLjYzMyAxNy43Yy0xLjA4Ny0uNzQ0LjA4NC0uNzI5LjA4NC0uNzI5IDEuMjA1LjA4NCAxLjgzOCAxLjIzNiAxLjgzOCAxLjIzNiAxLjA3IDEuODM1IDIuODA5IDEuMzA1IDMuNDk1Ljk5OC4xMDgtLjc3Ni40MTctMS4zMDUuNzYtMS42MDUtMi42NjUtLjMtNS40NjYtMS4zMzItNS40NjYtNS45MyAwLTEuMzEuNDY1LTIuMzggMS4yMzUtMy4yMi0uMTM1LS4zMDMtLjU0LTEuNTIzLjEwNS0zLjE3NiAwIDAgMS4wMDUtLjMyMiAzLjMgMS4yMy45Ni0uMjY3IDEuOTgtLjM5OSAzLS40MDUgMS4wMi4wMDYgMi4wNC4xMzggMyAuNDA1IDIuMjgtMS41NTIgMy4yODUtMS4yMyAzLjI4NS0xLjIzLjY0NSAxLjY1My4yNCAyLjg3My4xMiAzLjE3Ni43NjUuODQgMS4yMyAxLjkxIDEuMjMgMy4yMiAwIDQuNjEtMi44MDUgNS42MjUtNS40NzUgNS45Mi40Mi4zNi44MSAxLjA5Ni44MSAyLjIyIDAgMS42MDYtLjAxNSAyLjg5Ni0uMDE1IDMuMjg2IDAgLjMxNS4yMS42OS44MjUuNTdDMjAuNTY1IDIyLjA5MiAyNCAxNy41OTIgMjQgMTIuMjk3YzAtNi42MjctNS4zNzMtMTItMTItMTIiLz48L3N2Zz4="/> <text x="530" y="175" transform="scale(.1)" textLength="480">GITHUB</text><text x="1385" y="175" font-weight="bold" transform="scale(.1)" textLength="750">CURBENGH</text></g> </svg>
|
After Width: | Height: | Size: 2.0 KiB |
|
@ -0,0 +1,4 @@
|
|||
<!-- https://shields.io/badge/gitlab-curben-3f72af?logo=gitlab&style=for-the-badge -->
|
||||
<!-- Shields.io - https://shields.io/ - https://github.com/badges/shields - License: https://github.com/badges/shields/blob/master/LICENSE (Icons & Code: CC0 License) -->
|
||||
<!-- GitLab icon is a trademark of GitLab Inc -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="166" height="28"><g shape-rendering="crispEdges"><path fill="#555" d="M0 0h87v28H0z"/><path fill="#3f72af" d="M87 0h79v28H87z"/></g><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="100"><image x="9" y="7" width="14" height="14" xlink:href="data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMjQgMjQiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTIzLjk1NiAxMy41ODdsLTEuMzQ0LTQuMTMzYTQ1NDkuODE0IDQ1NDkuODE0IDAgMCAwLTIuNjYzLTguMTg5LjQ1Ni40NTYgMCAwIDAtLjg3IDBsLTIuNjU4IDguMTg5SDcuNTg1TDQuOTIgMS4yNjVhLjQ1Ni40NTYgMCAwIDAtLjg3IDBBNDU0OS44MTQgNDU0OS44MTQgMCAwIDAgLjA0NCAxMy41ODdhLjkwOC45MDggMCAwIDAgLjMzNiAxLjAyTDEyIDIzLjA1NGwxMS42Mi04LjQ0N2EuOTA4LjkwOCAwIDAgMCAuMzM2LTEuMDIiIGZpbGw9IiNmYzZkMjYiLz48cGF0aCBkPSJNMTIgMjMuMDU0bDQuNDIxLTEzLjZINy41OHoiIGZpbGw9IiNlMjQzMjkiLz48cGF0aCBkPSJNNy41NzkgOS40NTRIMS4zODhMMTIgMjMuMDU0eiIgZmlsbD0iI2ZjNmQyNiIvPjxwYXRoIGQ9Ik0xLjM4OCA5LjQ1NEwuMDQ0IDEzLjU4N2EuOTA4LjkwOCAwIDAgMCAuMzM2IDEuMDJMMTIgMjMuMDU0eiIgZmlsbD0iI2ZjYTMyNiIvPjxwYXRoIGQ9Ik03LjU3OSA5LjQ1NEw0LjkyIDEuMjY1YS40NTYuNDU2IDAgMCAwLS44NyAwTDEuMzg4IDkuNDU0eiIgZmlsbD0iI2UyNDMyOSIvPjxwYXRoIGQ9Ik0xNi40MjEgOS40NTRoNi4xOTFMMTIgMjMuMDU0eiIgZmlsbD0iI2ZjNmQyNiIvPjxwYXRoIGQ9Ik0yMi42MTIgOS40NTRsMS4zNDQgNC4xMzNhLjkwOC45MDggMCAwIDEtLjMzNiAxLjAyTDEyIDIzLjA1NHoiIGZpbGw9IiNmY2EzMjYiLz48cGF0aCBkPSJNMTYuNDIxIDkuNDU0bDIuNjU4LTguMTg5YS40NTYuNDU2IDAgMCAxIC44NyAwbDIuNjYzIDguMTg5eiIgZmlsbD0iI2UyNDMyOSIvPjwvc3ZnPg=="/> <text x="520" y="175" transform="scale(.1)" textLength="460">GITLAB</text><text x="1265" y="175" font-weight="bold" transform="scale(.1)" textLength="550">CURBEN</text></g> </svg>
|
After Width: | Height: | Size: 2.0 KiB |
|
@ -0,0 +1,4 @@
|
|||
<!-- https://shields.io/badge/linkedin-mdleom-3f72af?logo=linkedin&style=for-the-badge -->
|
||||
<!-- Shields.io - https://shields.io/ - https://github.com/badges/shields - License: https://github.com/badges/shields/blob/master/LICENSE (Icons & Code: CC0 License) -->
|
||||
<!-- LinkedIn icon is a trademark of LinkedIn Corporation -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="180" height="28" role="img" aria-label="LINKEDIN: MDLEOM"><title>LINKEDIN: MDLEOM</title><g shape-rendering="crispEdges"><rect width="102" height="28" fill="#555"/><rect x="102" width="78" height="28" fill="#3f72af"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="100"><image x="9" y="7" width="14" height="14" xlink:href="data:image/svg+xml;base64,PHN2ZyBmaWxsPSJ3aGl0ZXNtb2tlIiByb2xlPSJpbWciIHZpZXdCb3g9IjAgMCAyNCAyNCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48dGl0bGU+TGlua2VkSW4gaWNvbjwvdGl0bGU+PHBhdGggZD0iTTIwLjQ0NyAyMC40NTJoLTMuNTU0di01LjU2OWMwLTEuMzI4LS4wMjctMy4wMzctMS44NTItMy4wMzctMS44NTMgMC0yLjEzNiAxLjQ0NS0yLjEzNiAyLjkzOXY1LjY2N0g5LjM1MVY5aDMuNDE0djEuNTYxaC4wNDZjLjQ3Ny0uOSAxLjYzNy0xLjg1IDMuMzctMS44NSAzLjYwMSAwIDQuMjY3IDIuMzcgNC4yNjcgNS40NTV2Ni4yODZ6TTUuMzM3IDcuNDMzYy0xLjE0NCAwLTIuMDYzLS45MjYtMi4wNjMtMi4wNjUgMC0xLjEzOC45Mi0yLjA2MyAyLjA2My0yLjA2MyAxLjE0IDAgMi4wNjQuOTI1IDIuMDY0IDIuMDYzIDAgMS4xMzktLjkyNSAyLjA2NS0yLjA2NCAyLjA2NXptMS43ODIgMTMuMDE5SDMuNTU1VjloMy41NjR2MTEuNDUyek0yMi4yMjUgMEgxLjc3MUMuNzkyIDAgMCAuNzc0IDAgMS43Mjl2MjAuNTQyQzAgMjMuMjI3Ljc5MiAyNCAxLjc3MSAyNGgyMC40NTFDMjMuMiAyNCAyNCAyMy4yMjcgMjQgMjIuMjcxVjEuNzI5QzI0IC43NzQgMjMuMiAwIDIyLjIyMiAwaC4wMDN6Ii8+PC9zdmc+"/><text fill="#fff" x="595" y="175" transform="scale(.1)" textLength="610">LINKEDIN</text><text fill="#fff" x="1410" y="175" font-weight="bold" transform="scale(.1)" textLength="540">MDLEOM</text></g></svg>
|
After Width: | Height: | Size: 1.9 KiB |
|
@ -0,0 +1,4 @@
|
|||
<!-- https://shields.io/badge/npm-curben-3f72af?logo=npm&style=for-the-badge -->
|
||||
<!-- Shields.io - https://shields.io/ - https://github.com/badges/shields - License: https://github.com/badges/shields/blob/master/LICENSE (Icons & Code: CC0 License) -->
|
||||
<!-- npm icon is a trademark of npm Inc -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="145.5" height="28"><g shape-rendering="crispEdges"><path fill="#555" d="M0 0h66.5v28H0z"/><path fill="#3f72af" d="M66.5 0h79v28H66.5z"/></g><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="100"><image x="9" y="7" width="14" height="14" xlink:href="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0MCA0MCI+PHBhdGggZD0iTTAgMGg0MHY0MEgwVjB6IiBmaWxsPSIjY2IwMDAwIi8+PHBhdGggZmlsbD0iI2ZmZiIgZD0iTTcgN2gyNnYyNmgtN1YxNGgtNnYxOUg3eiIvPjwvc3ZnPgo="/> <text x="417.5" y="175" transform="scale(.1)" textLength="255">NPM</text><text x="1060" y="175" font-weight="bold" transform="scale(.1)" textLength="550">CURBEN</text></g> </svg>
|
After Width: | Height: | Size: 1.1 KiB |
|
@ -0,0 +1,2 @@
|
|||
package-lock=false
|
||||
optional=false
|
|
@ -0,0 +1,56 @@
|
|||
# Chameleon theme
|
||||
|
||||
Chameleon is a fork of [Typing](https://github.com/geekplux/hexo-theme-typing) theme, rewrote from scratch with the following changes/features:
|
||||
|
||||
- [sanitize.css](https://github.com/csstools/sanitize.css/) and [autoprefixer](https://github.com/csstools/sanitize.css/) for consistent cross-browser styling.
|
||||
- Utilise [relative length](https://www.w3schools.com/CSSref/css_units.asp) instead of absolute length in the css.
|
||||
- [`prefers-color-scheme`](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme) to apply light/dark theme according to the OS preference.
|
||||
- Add a [Copy](https://clipboardjs.com/) button to each code block.
|
||||
- Removed jQuery, fancyBox, web analytics, comment plugins, donation links and [typo.css](https://github.com/sofish/typo.css).
|
||||
- Homepage shows index of posts (same as /archives).
|
||||
|
||||
## Options
|
||||
|
||||
Configure this theme from your **site**'s configuration:
|
||||
|
||||
``` yml
|
||||
# _config.yml
|
||||
theme_config:
|
||||
# Header menu
|
||||
menu:
|
||||
Home: /
|
||||
Archives: /archives/
|
||||
Feed: /atom.xml
|
||||
|
||||
# Footer menu
|
||||
footer:
|
||||
GitLab: https://gitlab.com/curben/blog
|
||||
|
||||
# Load forkawesome icons?
|
||||
icons: false
|
||||
```
|
||||
|
||||
## Codeblock highlight
|
||||
|
||||
Following configurations need to be updated.
|
||||
|
||||
``` yml
|
||||
# _config.yml
|
||||
highlight:
|
||||
enable: true
|
||||
line_number: false
|
||||
hljs: true
|
||||
wrap: false
|
||||
|
||||
prismjs:
|
||||
enable: false
|
||||
```
|
||||
|
||||
## Build
|
||||
|
||||
Build "source/css/_source.css" into "source/css/chameleon.css"
|
||||
|
||||
```
|
||||
$ npm install
|
||||
$ npm run build
|
||||
```
|
|
@ -0,0 +1,33 @@
|
|||
'use strict'
|
||||
|
||||
/*
|
||||
* Normalize chameleon.css using sanitize.css
|
||||
* Add browser prefixes using autoprefixer
|
||||
*/
|
||||
|
||||
const { readFile, writeFile } = require('fs').promises
|
||||
const { join, resolve } = require('path')
|
||||
|
||||
const deps = ['autoprefixer', 'postcss', 'postcss-normalize']
|
||||
deps.forEach((dep) => {
|
||||
try {
|
||||
require.resolve(dep)
|
||||
} catch (err) {
|
||||
console.error(`Missing "${dep}" dependency. Please install it by running "npm install ${dep}".`)
|
||||
}
|
||||
})
|
||||
|
||||
const autoprefixer = require('autoprefixer')
|
||||
const normalize = require('postcss-normalize')
|
||||
const postcss = require('postcss')
|
||||
|
||||
const build = async () => {
|
||||
const cssPath = resolve(__dirname, '../source/css/')
|
||||
const cssSource = join(cssPath, '_source.css')
|
||||
const cssSave = join(cssPath, 'chameleon.css')
|
||||
const inCss = await readFile(cssSource)
|
||||
const { css: outCss } = await postcss([normalize, autoprefixer]).process(inCss, { from: cssSource })
|
||||
await writeFile(cssSave, outCss)
|
||||
}
|
||||
|
||||
build()
|
|
@ -0,0 +1 @@
|
|||
en-GB.yml
|
|
@ -0,0 +1,22 @@
|
|||
categories: Categories
|
||||
search: Search
|
||||
tags: Tags
|
||||
tagcloud: Tag Cloud
|
||||
tweets: Tweets
|
||||
prev: Prev
|
||||
next: Next
|
||||
comment: Comments
|
||||
archive_a: Archives
|
||||
archive_b: "Archives: %s"
|
||||
page: Page %d
|
||||
recent_posts: Recent Posts
|
||||
newer: Newer
|
||||
older: Older
|
||||
share: Share
|
||||
powered_by: Powered by
|
||||
rss_feed: RSS Feed
|
||||
category: Category
|
||||
tag: Tag
|
||||
date_published: Published Date
|
||||
date_updated: Last Updated
|
||||
source: Source
|
|
@ -0,0 +1,9 @@
|
|||
<% if (is_post()) { %>
|
||||
<%- js([
|
||||
{ src: '/libs/clipboard-2.0.6.min.js', defer: true },
|
||||
'/js/copy-button.js'
|
||||
]) %>
|
||||
<% } %>
|
||||
|
||||
<%/* javascript of Chameleon theme */%>
|
||||
<%- js('/js/chameleon.js') %>
|
|
@ -0,0 +1,9 @@
|
|||
<article class="archive-article archive-type-<%= post.layout %>">
|
||||
<div class="archive-article-inner">
|
||||
<header class="archive-article-header">
|
||||
<span class="archive-article-date"><%- partial('post/date') %></span>
|
||||
<%- partial('post/title', {class_name: 'archive-article-title'}) %>
|
||||
<span><%- post.more %></span>
|
||||
</header>
|
||||
</div>
|
||||
</article>
|
|
@ -0,0 +1,22 @@
|
|||
<% page.posts.each((post, i) => { %>
|
||||
<section class="archives-wrap">
|
||||
<div class="archives">
|
||||
<%- partial('archive-post', { post }) %>
|
||||
<% }) %>
|
||||
<% if (page.posts.length) { %>
|
||||
</div>
|
||||
</section>
|
||||
<% } %>
|
||||
<% if (page.total > 1) { %>
|
||||
<nav id="page-nav" class="page-nav">
|
||||
<%
|
||||
const prev_text = '« ' + __('prev')
|
||||
const next_text = __('next') + ' »'
|
||||
%>
|
||||
<%- paginator({
|
||||
prev_text: prev_text,
|
||||
next_text: next_text,
|
||||
escape: false
|
||||
}) %>
|
||||
</nav>
|
||||
<% } %>
|
|
@ -0,0 +1,53 @@
|
|||
<article id="<%= post.layout %>-<%= post.slug %>" class="h-entry article article-type-<%= post.layout %>" itemprop="blogPost" itemscope itemtype="https://schema.org/BlogPosting">
|
||||
<%- partial('_partial/header', {}, { cache: true }) %>
|
||||
<hr class="header-hr">
|
||||
<div class="article-inner">
|
||||
<%- partial('post/gallery') %>
|
||||
<% if (post.link || post.title) { %>
|
||||
<header class="article-header">
|
||||
<%- partial('post/title', {class_name: 'p-name entry-title article-title'}) %>
|
||||
</header>
|
||||
<% } %>
|
||||
<% if (post.excerpt) { %>
|
||||
<p class="p-summary"><%- post.excerpt %></p>
|
||||
<% } %>
|
||||
<div class="e-content article-entry" itemprop="articleBody">
|
||||
<%- toc(page.content, {list_number: false}) %>
|
||||
<%- post.more %>
|
||||
</div>
|
||||
<footer class="article-footer">
|
||||
<ul class="article-meta">
|
||||
<% if (post.updated) { %>
|
||||
<li>
|
||||
<span class="label"><%= __('date_updated') %>:</span>
|
||||
<%- partial('post/updated-date') %>
|
||||
</li>
|
||||
<% } %>
|
||||
<li>
|
||||
<span class="label"><%= __('date_published') %>:</span>
|
||||
<%- partial('post/date') %>
|
||||
</li>
|
||||
<% if (post.categories && post.categories.length) { %>
|
||||
<li>
|
||||
<span class="label"><%= __('category') %>:</span>
|
||||
<%- partial('post/category') %>
|
||||
</li>
|
||||
<% } %>
|
||||
<% if (post.tags && post.tags.length) { %>
|
||||
<li>
|
||||
<span class="label"><%= __('tag') %>:</span>
|
||||
<%- partial('post/tag') %>
|
||||
</li>
|
||||
<% } %>
|
||||
<li>
|
||||
<span class="label"><%= __('source') %>:</span>
|
||||
<%- link('https://gitlab.com/curben/blog/-/blob/microblog/source/' + post.source, post.source.replace('_posts/', '')) %>
|
||||
</li>
|
||||
<hr/>
|
||||
</ul>
|
||||
</footer>
|
||||
</div>
|
||||
<% if (!index) { %>
|
||||
<%- partial('post/nav') %>
|
||||
<% } %>
|
||||
</article>
|
|
@ -0,0 +1,16 @@
|
|||
<%/* Display footer on every page */%>
|
||||
<%/* Use different CSS layout for different page layout */%>
|
||||
<% if (is_post()) { %>
|
||||
<footer id="footer" class="post-footer footer">
|
||||
<hr>
|
||||
<% } else { %>
|
||||
<footer id="footer" class="archive-footer footer">
|
||||
<hr>
|
||||
<% } %>
|
||||
<div id="copyright" class="copyright">
|
||||
© 2018-<%= new Date().getUTCFullYear() %> <%= config.author %>. <%- link('https://gitlab.com/curben/blog', 'Powered by ') %> <%- link('https://hexo.io/', 'Hexo') %> with <%- link('https://gitlab.com/curben/blog/tree/master/themes/chameleon', 'Chameleon') %> theme. <br/>Content is available under <a href="https://creativecommons.org/licenses/by-sa/4.0/" rel="license" itemprop="license">CC-BY-SA 4.0</a>, unless indicated otherwise.<br/>
|
||||
<% for (const i in theme.footer) { %>
|
||||
<a class="main-nav-link" href="<%- url_for(theme.footer[i]) %>"><%= i %></a>
|
||||
<% } %>
|
||||
</div>
|
||||
</footer>
|
|
@ -0,0 +1,66 @@
|
|||
<head>
|
||||
<meta charset="utf-8">
|
||||
<%
|
||||
let title = page.title
|
||||
|
||||
if (is_archive()) {
|
||||
title = titleCase(config.archive_dir)
|
||||
|
||||
if (is_month()) {
|
||||
title += ': ' + page.year + '/' + page.month
|
||||
} else if (is_year()) {
|
||||
title += ': ' + page.year
|
||||
} else if (page.current_url) {
|
||||
const pageNum = Number(page.current_url.charAt(page.current_url.length - 2))
|
||||
if (Number.isInteger(pageNum)) title += ': Page ' + pageNum
|
||||
}
|
||||
} else if (is_category()) {
|
||||
title = __('category') + ': ' + page.category
|
||||
} else if (is_tag()) {
|
||||
title = __('tag') + ': ' + page.tag
|
||||
}
|
||||
%>
|
||||
<title><% if (title) { %><%= title %> | <% } %><%= config.title %></title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="canonical" href="<%- full_url_for(path) %>">
|
||||
<%/* Add Open Graph meta tags for easier sharing on social networking sites */%>
|
||||
<%/* Modified from original source for compatibility with my blog */%>
|
||||
<%- openGraph() %>
|
||||
<% if (config.feed) { %>
|
||||
<%- feed_tag() %>
|
||||
<% } else if (theme.rss) { %>
|
||||
<%- feed_tag(theme.rss) %>
|
||||
<% } %>
|
||||
<%/* Favicon */%>
|
||||
<%/* https://evilmartians.com/chronicles/how-to-favicon-in-2021-six-files-that-fit-most-needs */%>
|
||||
<link rel="icon" href="<%- url_for('/favicon.ico') %>">
|
||||
<link rel="icon" href="<%- url_for('/svg/favicon.svg') %>" type="image/svg+xml" sizes="any">
|
||||
<link rel="apple-touch-icon" href="<%- url_for('/apple-touch-icon-180x180.png') %>">
|
||||
<link rel="manifest" href="<%- url_for('/site.webmanifest') %>">
|
||||
|
||||
<link rel="sitemap" type="application/xml" title="Sitemap" href="<%- url_for('/sitemap.xml') %>">
|
||||
|
||||
<%/* CSS of Chameleon theme */%>
|
||||
<%- css('/css/chameleon.css') %>
|
||||
|
||||
<% if (theme.icons) { %>
|
||||
<%- css('/libs/forkawesome/css/forkawesome-1.1.7.min.css') %>
|
||||
<% } %>
|
||||
<% if (is_post() && page.series) { %>
|
||||
<%
|
||||
if (page.series !== 'first' && page.next?.path?.startsWith(`${config.archive_dir}/`)) {
|
||||
const pageTitle = page.next.title
|
||||
%>
|
||||
<%/* page.next is not a typo */%>
|
||||
<link rel="prev" href="<%- url_for(page.next.path) %>" title="<%= pageTitle %>">
|
||||
<% } %>
|
||||
<%
|
||||
if (page.series !== 'last' && page.prev?.path?.startsWith(`${config.archive_dir}/`)) {
|
||||
const pageTitle = page.prev.title
|
||||
%>
|
||||
<link rel="next" href="<%- url_for(page.prev.path) %>" title="<%= pageTitle %>">
|
||||
<% } %>
|
||||
<% } %>
|
||||
|
||||
<meta name="yandex-verification" content="e026a03a5dd06730" />
|
||||
</head>
|
|
@ -0,0 +1,51 @@
|
|||
<header id="header" class="header">
|
||||
<%/* Nav menu for desktop */%>
|
||||
<nav class="main-nav">
|
||||
<% if (!theme.menu) { %><a class="main-nav-link" href="<%- config.root %>">Home</a>
|
||||
<% } else { %>
|
||||
<% for (const i in theme.menu) { %>
|
||||
<a class="main-nav-link" href="<%- url_for(theme.menu[i]) %>"><%= i %></a>
|
||||
<% }} %>
|
||||
|
||||
<div class="search-container">
|
||||
<form id="searchForm" method="post" action="https://duckduckgo.com/" rel="noopener external nofollow noreferrer" role="search">
|
||||
<input type="search" name="q" class="search-box" placeholder="Search..." title="Powered by DuckDuckGo" enterkeyhint="search">
|
||||
<input type="hidden" name="sites" value="<%- config.url.replace(/^http(s)?:\/\//, '') %>">
|
||||
<input type="hidden" name="kd" value="-1">
|
||||
<input type="hidden" name="kg" value="p">
|
||||
<input type="hidden" name="kae" value="#000">
|
||||
<a class="search-button" id="search-click">
|
||||
<img src="/svg/search.svg">
|
||||
</a>
|
||||
</form>
|
||||
</div>
|
||||
</nav>
|
||||
<%/* Nav menu for mobile */%>
|
||||
<nav class="mobile-nav">
|
||||
<a class="site-title" href="<%- config.root %>"><%- config.url.replace(/^http(s)?:\/\//, '') %></a>
|
||||
<ul class="mobile-nav-menu">
|
||||
<label for="mobile-menu-toggle"><a class="no-underline" id="menu-button">☰</a></label>
|
||||
<input id="mobile-menu-toggle" type="checkbox"/>
|
||||
<ul id="mobile-nav-link">
|
||||
<div class="search-container">
|
||||
<form id="searchFormMob" method="post" action="https://duckduckgo.com/" rel="noopener external nofollow noreferrer" role="search">
|
||||
<input type="search" name="q" class="search-box" placeholder="Search..." title="Powered by DuckDuckGo" enterkeyhint="search">
|
||||
<input type="hidden" name="sites" value="<%- config.url.replace(/^http(s)?:\/\//, '') %>">
|
||||
<input type="hidden" name="kd" value="-1">
|
||||
<input type="hidden" name="kg" value="p">
|
||||
<input type="hidden" name="kae" value="#000">
|
||||
<a class="search-button" id="search-click-mobile">
|
||||
<img src="/svg/search.svg">
|
||||
</a>
|
||||
</form>
|
||||
</div>
|
||||
<i>Powered by DuckDuckGo</i>
|
||||
<% if (!theme.menu) { %><a class="mobile-nav-link-a" href="<%- config.root %>">Home</a>
|
||||
<% } else { %>
|
||||
<% for (const i in theme.menu) { %>
|
||||
<a class="mobile-nav-link-a" href="<%- url_for(theme.menu[i]) %>"><%= i %></a>
|
||||
<% }} %>
|
||||
</ul>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
|
@ -0,0 +1,10 @@
|
|||
<% if (post.categories && post.categories.length) { %>
|
||||
<div class="article-category">
|
||||
<%- list_categories(post.categories, {
|
||||
show_count: false,
|
||||
class: 'article-category',
|
||||
style: 'none',
|
||||
separator: '/'
|
||||
}) %>
|
||||
</div>
|
||||
<% } %>
|
|
@ -0,0 +1,6 @@
|
|||
<%/* Date format is specified to skip timezone conversion */%>
|
||||
<time <% if (is_post()) { %>class="dt-published"<% } %> datetime="<%= date(post.date, 'YYYY-MM-DD[T00:00:00.000Z]') %>" itemprop="datePublished">
|
||||
<%= date(post.date, 'D') %>
|
||||
<a href="/<%= config.archive_dir %>/<%= date(post.date, 'YYYY') %>/<%= date(post.date, 'MM') %>/" title="See <%= date(post.date, 'MMMM') %> <%= date(post.date, 'YYYY') %>'s posts"><%= date(post.date, ' MMM') %></a>
|
||||
<a href="/<%= config.archive_dir %>/<%= date(post.date, 'YYYY') %>/" title="See <%= date(post.date, 'YYYY') %>'s posts"><%= date(post.date, ' YYYY') %></a>
|
||||
</time>
|
|
@ -0,0 +1,11 @@
|
|||
<% if (post.photos && post.photos.length) { %>
|
||||
<div class="article-gallery">
|
||||
<div class="article-gallery-photos">
|
||||
<% post.photos.forEach((photo, i) => { %>
|
||||
<a class="article-gallery-img" href="<%- url_for(photo) %>" rel="gallery_<%= post._id %>">
|
||||
<img src="<%- url_for(photo) %>" itemprop="image">
|
||||
</a>
|
||||
<% }) %>
|
||||
</div>
|
||||
</div>
|
||||
<% } %>
|
|
@ -0,0 +1,20 @@
|
|||
<% if (post.prev || post.next) { %>
|
||||
<nav id="article-nav" class="article-nav">
|
||||
<span class="article-nav-link-wrap newer">
|
||||
<% if (post.prev) { %>
|
||||
<strong><%= __('newer') %></strong>
|
||||
<a href="<%- url_for(post.prev.path) %>">
|
||||
<div class="article-nav-title"><%= post.prev.title %></div>
|
||||
</a>
|
||||
<% } %>
|
||||
</span>
|
||||
<span class="article-nav-link-wrap older">
|
||||
<% if (post.next) { %>
|
||||
<strong><%= __('older') %></strong>
|
||||
<a href="<%- url_for(post.next.path) %>">
|
||||
<div class="article-nav-title"><%= post.next.title %></div>
|
||||
</a>
|
||||
<% } %>
|
||||
</span>
|
||||
</nav>
|
||||
<% } %>
|
|
@ -0,0 +1,7 @@
|
|||
<% if (post.tags && post.tags.length) { %>
|
||||
<%- list_tags(post.tags, {
|
||||
show_count: false,
|
||||
class: 'article-tag',
|
||||
transform: titleCase
|
||||
}) %>
|
||||
<% } %>
|
|
@ -0,0 +1,18 @@
|
|||
<% if (post.link) { %>
|
||||
<h1 itemprop="name">
|
||||
<a class="<%= class_name %>" href="<%- url_for(post.link) %>" itemprop="url"><%= post.title %></a>
|
||||
</h1>
|
||||
<% } else if (post.title) { %>
|
||||
<% if (index) { %>
|
||||
<h1 itemprop="name">
|
||||
<a class="<%= class_name %>" href="<%- url_for(post.path) %>"><%= post.title %></a>
|
||||
</h1>
|
||||
<% } else { %>
|
||||
<h1 class="<%= class_name %>" itemprop="headline name"><%= post.title %>
|
||||
<%/* Share button */%>
|
||||
<a id="btnshare">
|
||||
<img src="/svg/share.svg">
|
||||
</a>
|
||||
</h1>
|
||||
<% } %>
|
||||
<% } %>
|
|
@ -0,0 +1,3 @@
|
|||
<%/* User-specified updated date */%>
|
||||
<%/* default post.updated is (almost) always is the time 'hexo generate' is executed */%>
|
||||
<time class="dt-updated" datetime="<%= date(post.updated, 'YYYY-MM-DD[T00:00:00.000Z]') %>" itemprop="dateModified"><%= date(post.updated, 'D MMM YYYY') %></time>
|
|
@ -0,0 +1,5 @@
|
|||
<div class="archive-container">
|
||||
<%- partial('_partial/header', {}, { cache: true }) %>
|
||||
<hr class="header-hr"/>
|
||||
<%- partial('_partial/archive', {pagination: config.archive, index: true}) %>
|
||||
</div>
|
|
@ -0,0 +1,5 @@
|
|||
<div class="archive-container">
|
||||
<%- partial('_partial/header', {}, { cache: true }) %>
|
||||
<hr class="header-hr">
|
||||
<%- partial('_partial/archive', {pagination: config.category, index: true}) %>
|
||||
</div>
|
|
@ -0,0 +1,15 @@
|
|||
<div class="archive-container">
|
||||
<%- partial('_partial/header', {}, { cache: true }) %>
|
||||
<hr class="header-hr"/>
|
||||
<h2>Recent posts:</h2>
|
||||
<%- list_posts({
|
||||
amount: 10,
|
||||
class: 'index'
|
||||
}) %>
|
||||
<h2>Tags:</h2>
|
||||
<%- list_tags({
|
||||
amount: 0,
|
||||
class: 'index',
|
||||
transform: titleCase
|
||||
}) %>
|
||||
</div>
|
|
@ -0,0 +1,16 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="<%= config.language %>">
|
||||
<%- partial('_partial/head', {}) %>
|
||||
<%/* set /about page to use "home" page layout */%>
|
||||
<% if (is_current('about/index.html')) { %>
|
||||
<body class="about-body">
|
||||
<% } else {%>
|
||||
<body>
|
||||
<% } %>
|
||||
<div id="container" class="container">
|
||||
<%- body %>
|
||||
</div>
|
||||
<%- partial('_partial/footer') %>
|
||||
<%- partial('_partial/after-footer') %>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1 @@
|
|||
<%- partial('_partial/article', {post: page, index: false}) %>
|
|
@ -0,0 +1 @@
|
|||
<%- partial('_partial/article', {post: page, index: false}) %>
|
|
@ -0,0 +1,10 @@
|
|||
<div class="archive-container">
|
||||
<%- partial('_partial/header', {}, { cache: true }) %>
|
||||
<hr class="header-hr"/>
|
||||
<h2>Tags:</h2>
|
||||
<%- list_tags({
|
||||
amount: 0,
|
||||
class: 'index',
|
||||
transform: titleCase
|
||||
}) %>
|
||||
</div>
|
|
@ -0,0 +1,5 @@
|
|||
<div class="archive-container">
|
||||
<%- partial('_partial/header', {}, { cache: true }) %>
|
||||
<hr class="header-hr">
|
||||
<%- partial('_partial/archive', {pagination: config.tag, index: true}) %>
|
||||
</div>
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"name": "hexo-theme-chameleon",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "node build/script.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"autoprefixer": "^10.0.0",
|
||||
"postcss": "^8.0.6",
|
||||
"postcss-normalize": "^10.0.0",
|
||||
"sanitize.css": "^13.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18.12.0"
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 291 KiB |
After Width: | Height: | Size: 92 KiB |
|
@ -0,0 +1,16 @@
|
|||
'use strict'
|
||||
/* global hexo */
|
||||
|
||||
/*
|
||||
* Add "Copy" button to code snippet
|
||||
*/
|
||||
|
||||
hexo.extend.filter.register('after_render:html', (data) => {
|
||||
const copyBtn = '<button class="copy-button">Copy</button>'
|
||||
|
||||
// Regex is based on https://github.com/hexojs/hexo/pull/3697
|
||||
return data.replace(/<pre>(?!<\/pre>).+?<\/pre>/gs, (str) => {
|
||||
if (!str.includes(copyBtn)) return str.replace('</pre>', copyBtn + '</pre>')
|
||||
return str
|
||||
})
|
||||
})
|
|
@ -0,0 +1,55 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<feed xmlns="http://www.w3.org/2005/Atom">
|
||||
<title>{{ config.title }}</title>
|
||||
{% if icon %}<icon>{{ icon }}</icon>{% endif %}
|
||||
{% if config.subtitle %}<subtitle>{{ config.subtitle }}</subtitle>{% endif %}
|
||||
<link href="{{ feed_url | uriencode }}" rel="self"/>
|
||||
{% if config.feed.hub %}<link href="{{ config.feed.hub | uriencode }}" rel="hub"/>{% endif %}
|
||||
<link href="{{ url | uriencode }}"/>
|
||||
<updated>{% if posts.first().updated %}{{ posts.first().updated | formatDate }}{% else %}{{ posts.first().date | formatDate }}{% endif %}</updated>
|
||||
<id>{{ url | uriencode }}</id>
|
||||
{% if config.author %}
|
||||
<author>
|
||||
<name>{{ config.author }}</name>
|
||||
{% if config.email %}<email>{{ config.email }}</email>{% endif %}
|
||||
</author>
|
||||
{% endif %}
|
||||
<generator uri="https://hexo.io/">Hexo</generator>
|
||||
{% for post in posts.toArray() %}
|
||||
<entry>
|
||||
<title>{{ post.title }}</title>
|
||||
<link href="{{ post.permalink }}"/>
|
||||
<id>{{ post.permalink }}</id>
|
||||
<published>{{ post.date | formatDate }}</published>
|
||||
<updated>{% if post.updated %}{{ post.updated | formatDate }}{% else %}{{ post.date | formatDate }}{% endif %}</updated>
|
||||
{% if config.feed.content and post.content %}
|
||||
<content type="html"><![CDATA[{{ post.content | noControlChars | safe }}]]></content>
|
||||
{% endif %}
|
||||
{% if post.description %}
|
||||
<summary type="html">{{ post.description }}</summary>
|
||||
{% elif post.intro %}
|
||||
<summary type="html">{{ post.intro }}</summary>
|
||||
{% elif post.excerpt %}
|
||||
<summary type="html">{{ post.excerpt }}</summary>
|
||||
{% elif post.content %}
|
||||
{% set short_content = post.content.substring(0, config.feed.content_limit) %}
|
||||
{% if config.feed.content_limit_delim %}
|
||||
{% set delim_pos = short_content.lastIndexOf(config.feed.content_limit_delim) %}
|
||||
{% if delim_pos > -1 %}
|
||||
<summary type="html">{{ short_content.substring(0, delim_pos) }}</summary>
|
||||
{% else %}
|
||||
<summary type="html">{{ short_content }}</summary>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<summary type="html">{{ short_content }}</summary>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if post.image %}
|
||||
<content src="{{ post.image | fullUrlFor }}" type="image" />
|
||||
{% endif %}
|
||||
{% for tag in post.tags.toArray() %}
|
||||
<category term="{{ tag.name }}" scheme="{{ tag.path | fullUrlFor }}"/>
|
||||
{% endfor %}
|
||||
</entry>
|
||||
{% endfor %}
|
||||
</feed>
|
|
@ -0,0 +1,58 @@
|
|||
'use strict'
|
||||
|
||||
const nunjucks = require('nunjucks')
|
||||
const env = new nunjucks.Environment()
|
||||
const { join } = require('path')
|
||||
const { readFileSync } = require('fs')
|
||||
const moment = require('moment')
|
||||
const { encodeURL, full_url_for } = require('hexo-util')
|
||||
|
||||
env.addFilter('uriencode', str => {
|
||||
return encodeURL(str)
|
||||
})
|
||||
|
||||
env.addFilter('noControlChars', str => {
|
||||
return str.replace(/[\x00-\x1F\x7F]/g, '') // eslint-disable-line no-control-regex
|
||||
})
|
||||
|
||||
env.addFilter('formatDate', str => {
|
||||
return moment(str).format('YYYY-MM-DD[T00:00:00.000Z]')
|
||||
})
|
||||
|
||||
const atomTmplSrc = join(__dirname, './.atom.xml')
|
||||
const atomTmpl = nunjucks.compile(readFileSync(atomTmplSrc, 'utf8'), env)
|
||||
|
||||
module.exports = function (locals) {
|
||||
const { config } = this
|
||||
const { feed, url } = config
|
||||
const { icon: iconCfg, limit, order_by, path } = feed
|
||||
const template = atomTmpl
|
||||
|
||||
env.addFilter('fullUrlFor', str => {
|
||||
return full_url_for.call(this, str)
|
||||
})
|
||||
|
||||
let posts = locals.posts.sort(order_by || '-date')
|
||||
posts = posts.filter(post => {
|
||||
return post.draft !== true
|
||||
})
|
||||
|
||||
if (limit) posts = posts.limit(limit)
|
||||
|
||||
const icon = iconCfg ? full_url_for.call(this, iconCfg) : ''
|
||||
|
||||
const feed_url = full_url_for.call(this, path);
|
||||
|
||||
const data = template.render({
|
||||
config,
|
||||
url,
|
||||
icon,
|
||||
posts,
|
||||
feed_url
|
||||
})
|
||||
|
||||
return {
|
||||
path,
|
||||
data
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
/* global hexo */
|
||||
'use strict'
|
||||
|
||||
/*
|
||||
* Modified from the hexo version,
|
||||
* https://github.com/hexojs/hexo-generator-feed
|
||||
* to remove timezone
|
||||
*/
|
||||
|
||||
const { extname } = require('path')
|
||||
|
||||
const config = hexo.config.feed = Object.assign({
|
||||
type: 'atom',
|
||||
limit: 20,
|
||||
hub: '',
|
||||
content: true,
|
||||
content_limit: 140,
|
||||
content_limit_delim: '',
|
||||
order_by: '-date'
|
||||
}, hexo.config.feed)
|
||||
|
||||
// Set default feed path
|
||||
if (!config.path) {
|
||||
config.path = config.type + '.xml'
|
||||
}
|
||||
|
||||
// Add extension name if don't have
|
||||
if (!extname(config.path)) {
|
||||
config.path += '.xml'
|
||||
}
|
||||
|
||||
hexo.extend.generator.register('feed', require('./generator'))
|
|
@ -0,0 +1,33 @@
|
|||
'use strict'
|
||||
/* global hexo */
|
||||
|
||||
/*
|
||||
* Add Link button next to a heading
|
||||
* based on https://github.com/hexojs/hexo-renderer-marked/blob/master/lib/renderer.js
|
||||
*/
|
||||
|
||||
const { slugize, stripHTML, unescapeHTML: unescape } = require('hexo-util')
|
||||
|
||||
const anchorId = (str, transformOption) => {
|
||||
return slugize(stripHTML(unescape(str.replace('.', ''))).trim(), { transform: transformOption })
|
||||
}
|
||||
|
||||
hexo.extend.filter.register('marked:renderer', function (renderer) {
|
||||
const { config } = this
|
||||
const headingId = {}
|
||||
renderer.heading = function (text, level) {
|
||||
const { modifyAnchors } = config.marked
|
||||
const transformOption = modifyAnchors
|
||||
let id = anchorId(text, transformOption)
|
||||
|
||||
// Add a number after id if repeated
|
||||
if (headingId[id]) {
|
||||
id += `-${headingId[id]++}`
|
||||
} else {
|
||||
headingId[id] = 1
|
||||
}
|
||||
|
||||
// add headerlink
|
||||
return `<h${level} id="${id}">${text} <a href="#${id}" class="headerlink" title="${stripHTML(text)}">§</a></h${level}>`
|
||||
}
|
||||
})
|
|
@ -0,0 +1,26 @@
|
|||
'use strict'
|
||||
/* global hexo */
|
||||
|
||||
/*
|
||||
* Modified from the hexo version,
|
||||
* https://github.com/hexojs/hexo/blob/master/lib/plugins/helper/link_to.js
|
||||
* to remove title attribute and 'external' option
|
||||
*/
|
||||
|
||||
const { htmlTag } = require('hexo-util')
|
||||
|
||||
hexo.extend.helper.register('link', (path, text) => {
|
||||
const urlFor = hexo.extend.helper.get('url_for').bind(hexo)
|
||||
|
||||
if (!text) text = path.replace(/^https?:\/\/|\/$/g, '')
|
||||
|
||||
const attrs = Object.assign({
|
||||
href: urlFor(path)
|
||||
})
|
||||
|
||||
if (attrs.class && Array.isArray(attrs.class)) {
|
||||
attrs.class = attrs.class.join(' ')
|
||||
}
|
||||
|
||||
return htmlTag('a', attrs, text)
|
||||
})
|
|
@ -0,0 +1,94 @@
|
|||
'use strict'
|
||||
/* global hexo */
|
||||
|
||||
/*
|
||||
* Modified from the hexo version,
|
||||
* https://github.com/hexojs/hexo/blob/master/lib/plugins/helper/open_graph.js
|
||||
* to include https://github.com/hexojs/hexo/pull/3674
|
||||
* and use WHATWG URL API
|
||||
* https://nodejs.org/api/url.html#url_the_whatwg_url_api
|
||||
*/
|
||||
|
||||
const moment = require('moment')
|
||||
const { escapeHTML, htmlTag, prettyUrls, stripHTML } = require('hexo-util')
|
||||
const fullUrlFor = require('hexo-util').full_url_for
|
||||
|
||||
function meta (name, content) {
|
||||
return `${htmlTag('meta', {
|
||||
name,
|
||||
content
|
||||
})}\n`
|
||||
}
|
||||
|
||||
function og (name, content) {
|
||||
return `${htmlTag('meta', {
|
||||
property: name,
|
||||
content
|
||||
})}\n`
|
||||
}
|
||||
|
||||
function openGraphHelper () {
|
||||
const { config, page } = this
|
||||
let description = page.excerpt || ''
|
||||
const author = config.author
|
||||
const keywords = page.tags || ''
|
||||
const title = page.title || config.title || ''
|
||||
const type = (this.is_post() ? 'article' : 'website')
|
||||
const url = prettyUrls(this.url, config.pretty_urls)
|
||||
const screenshot = '/screenshot/' + prettyUrls(this.path, config.pretty_urls)
|
||||
const siteName = config.title || ''
|
||||
const published = page.date || ''
|
||||
const updated = page.updated || ''
|
||||
const language = config.language.replace('-', '_')
|
||||
let result = ''
|
||||
|
||||
if (description) {
|
||||
description = escapeHTML(stripHTML(description)
|
||||
.trim()
|
||||
.replace(/\n/g, ' ')
|
||||
.substring(0, 200))
|
||||
|
||||
result += meta('description', description)
|
||||
}
|
||||
|
||||
result += og('article:author', author)
|
||||
|
||||
if (keywords) {
|
||||
keywords.forEach(tag => {
|
||||
result += og('article:tag', tag.name)
|
||||
})
|
||||
}
|
||||
|
||||
result += og('og:type', type)
|
||||
result += og('og:title', title)
|
||||
result += og('og:url', url)
|
||||
|
||||
if (siteName) {
|
||||
result += og('og:site_name', siteName)
|
||||
}
|
||||
|
||||
if (description) {
|
||||
result += og('og:description', description)
|
||||
}
|
||||
|
||||
result += og('og:locale', language)
|
||||
|
||||
result += og('og:image', fullUrlFor.call(this, screenshot))
|
||||
|
||||
if (published) {
|
||||
if ((moment.isMoment(published) || moment.isDate(published)) && !isNaN(published.valueOf())) {
|
||||
// Skip timezone conversion
|
||||
result += og('article:published_time', moment(published).format('YYYY-MM-DD[T00:00:00.000Z]'))
|
||||
}
|
||||
}
|
||||
|
||||
if (updated) {
|
||||
if ((moment.isMoment(updated) || moment.isDate(updated)) && !isNaN(updated.valueOf())) {
|
||||
result += og('article:modified_time', moment(updated).format('YYYY-MM-DD[T00:00:00.000Z]'))
|
||||
}
|
||||
}
|
||||
|
||||
return result.trim()
|
||||
}
|
||||
|
||||
hexo.extend.helper.register('openGraph', openGraphHelper)
|
|
@ -0,0 +1,7 @@
|
|||
Copyright (c) 2014 Tommy Chen, Alex Chauvin
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@ -0,0 +1,32 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||
{% for post in posts %}
|
||||
<url>
|
||||
<loc>{{ post.permalink }}</loc>
|
||||
{% if post.updated %}
|
||||
<lastmod>{{ post.updated | formatDate }}</lastmod>
|
||||
{% elif post.date %}
|
||||
<lastmod>{{ post.date | formatDate }}</lastmod>
|
||||
{% endif %}
|
||||
</url>
|
||||
{% endfor %}
|
||||
|
||||
{# home page #}
|
||||
<url>
|
||||
<loc>{{ config.root | fullUrlFor }}</loc>
|
||||
<lastmod>{% if posts[0].updated %}{{ posts[0].updated | formatDate }}{% else %}{{ posts[0].date | formatDate }}{% endif %}</lastmod>
|
||||
</url>
|
||||
|
||||
{# archive page #}
|
||||
<url>
|
||||
<loc>{{ config.archive_dir | fullUrlFor + '/' }}</loc>
|
||||
<lastmod>{% if posts[0].updated %}{{ posts[0].updated | formatDate }}{% else %}{{ posts[0].date | formatDate }}{% endif %}</lastmod>
|
||||
</url>
|
||||
|
||||
{% for tag in tags %}
|
||||
<url>
|
||||
<loc>{{ tag.permalink }}</loc>
|
||||
<lastmod>{% if tag.posts.first().updated %}{{ tag.posts.first().updated | formatDate }}{% else %}{{ tag.posts.first().date | formatDate }}{% endif %}</lastmod>
|
||||
</url>
|
||||
{% endfor %}
|
||||
</urlset>
|
|
@ -0,0 +1,47 @@
|
|||
'use strict'
|
||||
|
||||
const micromatch = require('micromatch')
|
||||
const template = require('./template')
|
||||
|
||||
const isMatch = (path, patterns) => {
|
||||
if (patterns && patterns.length) {
|
||||
if (micromatch.isMatch(path, patterns, { matchBase: true })) return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
module.exports = function (locals) {
|
||||
const { config } = this
|
||||
const { sitemap, skip_render } = config
|
||||
const { path } = sitemap
|
||||
const skipRenderList = ['*.js', '*.css']
|
||||
|
||||
if (Array.isArray(skip_render)) {
|
||||
skipRenderList.push(...skip_render)
|
||||
} else if (skip_render != null) {
|
||||
skipRenderList.push(skip_render)
|
||||
}
|
||||
|
||||
const posts = [].concat(locals.posts.toArray(), locals.pages.toArray())
|
||||
.filter((post) => {
|
||||
return post.sitemap !== false && !isMatch(post.source, skipRenderList)
|
||||
})
|
||||
.sort((a, b) => {
|
||||
return b.date - a.date
|
||||
})
|
||||
|
||||
// configuration dictionary
|
||||
const xmlConfig = {
|
||||
config,
|
||||
posts,
|
||||
tags: locals.tags.toArray()
|
||||
}
|
||||
|
||||
const data = template(config).render(xmlConfig)
|
||||
|
||||
return {
|
||||
path,
|
||||
data
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
'use strict'
|
||||
/* global hexo */
|
||||
|
||||
/*
|
||||
* Add sitemap.xml
|
||||
* Based on https://github.com/hexojs/hexo-generator-sitemap/pull/26
|
||||
*/
|
||||
|
||||
const pathFn = require('path')
|
||||
|
||||
const config = hexo.config.sitemap = Object.assign({
|
||||
path: 'sitemap.xml'
|
||||
}, hexo.config.sitemap)
|
||||
|
||||
if (!pathFn.extname(config.path)) {
|
||||
config.path += '.xml'
|
||||
}
|
||||
|
||||
hexo.extend.generator.register('sitemap', require('./generator'))
|
|
@ -0,0 +1,30 @@
|
|||
'use strict'
|
||||
|
||||
const { join } = require('path')
|
||||
const { readFileSync } = require('fs')
|
||||
const moment = require('moment')
|
||||
const { full_url_for } = require('hexo-util')
|
||||
let sitemapTmpl = ''
|
||||
|
||||
module.exports = function (config) {
|
||||
if (sitemapTmpl) return sitemapTmpl
|
||||
|
||||
const nunjucks = require('nunjucks')
|
||||
const env = new nunjucks.Environment(null, {
|
||||
autoescape: false,
|
||||
watch: false
|
||||
})
|
||||
|
||||
env.addFilter('formatDate', str => {
|
||||
return moment(str).format('YYYY-MM-DD[T00:00:00.000Z]').substring(0, 10)
|
||||
})
|
||||
|
||||
env.addFilter('fullUrlFor', str => {
|
||||
return full_url_for.call({ config }, str)
|
||||
})
|
||||
|
||||
const sitemapSrc = config.sitemap.template || join(__dirname, '.sitemap.xml')
|
||||
sitemapTmpl = nunjucks.compile(readFileSync(sitemapSrc, 'utf8'), env)
|
||||
|
||||
return sitemapTmpl
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
'use strict'
|
||||
/* global hexo */
|
||||
|
||||
/*
|
||||
* Simpler version of titlecase() https://github.com/rvagg/titlecase
|
||||
* Does not ignore words with a dot
|
||||
*/
|
||||
|
||||
hexo.extend.helper.register('titleCase', (str) => {
|
||||
return str.replace(/[\w]+[^\s]*/g, (match) => {
|
||||
return match[0].toUpperCase() + match.substring(1).toLowerCase()
|
||||
})
|
||||
})
|
|
@ -0,0 +1,920 @@
|
|||
@charset "utf-8";
|
||||
@import "sanitize";
|
||||
@import "sanitize/forms";
|
||||
|
||||
:root {
|
||||
--main-bg-color: black;
|
||||
--main-font-color: #e6e6e6;
|
||||
--alt-bg-color: #333;
|
||||
--link-underline: #3f72af;
|
||||
--code-add-bg-color: #144212;
|
||||
--code-del-bg-color: #600;
|
||||
}
|
||||
|
||||
html {
|
||||
cursor: unset;
|
||||
height: 100%;
|
||||
line-height: 1.15;
|
||||
}
|
||||
|
||||
body {
|
||||
color: var(--main-font-color);
|
||||
background: var(--main-bg-color);
|
||||
font-family: sans-serif;
|
||||
font-size: 1.2em;
|
||||
/* https://stackoverflow.com/a/6654996 */
|
||||
min-height: 100%;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
/*
|
||||
* Restrict sizing to the page width in all browsers (opinionated).
|
||||
* sanitize/page
|
||||
*/
|
||||
iframe,
|
||||
img,
|
||||
input,
|
||||
select,
|
||||
textarea {
|
||||
height: auto;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
font-family: serif;
|
||||
font-weight: bold;
|
||||
line-height: 1.35;
|
||||
margin-top: 1em;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 1.5em;
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
|
||||
/* Disable line break for site title */
|
||||
a.site-title {
|
||||
font-family: serif;
|
||||
font-size: 1.5em;
|
||||
font-weight: bold;
|
||||
line-height: 1.35;
|
||||
margin: 0.5em 0;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1.3em;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
a {
|
||||
background: none;
|
||||
color: var(--link-underline);
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:hover,
|
||||
a:focus {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
a:visited {
|
||||
color: purple;
|
||||
}
|
||||
|
||||
a:active {
|
||||
color: #faa700;
|
||||
}
|
||||
|
||||
button,
|
||||
input,
|
||||
select,
|
||||
textarea {
|
||||
font-size: 100%;
|
||||
line-height: 1.15;
|
||||
}
|
||||
|
||||
hr {
|
||||
background-image: -webkit-linear-gradient(0deg,transparent,var(--main-font-color),transparent);
|
||||
border: none;
|
||||
box-sizing: content-box;
|
||||
height: 1px;
|
||||
margin-bottom: 1em;
|
||||
margin-left: 25%;
|
||||
overflow: visible;
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
.archive-container hr,
|
||||
.article hr {
|
||||
width: 100%;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.article .article-footer,
|
||||
.article .article-footer hr {
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
.header-hr {
|
||||
background-image: -webkit-linear-gradient(90deg,var(--main-font-color),var(--main-font-color),transparent);
|
||||
}
|
||||
|
||||
figcaption,
|
||||
small,
|
||||
pre div.caption {
|
||||
font-size: 0.9em;
|
||||
color: #888;
|
||||
}
|
||||
|
||||
/* code caption */
|
||||
figcaption,
|
||||
pre div.caption {
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
pre figcaption a,
|
||||
pre div.caption a {
|
||||
float: right;
|
||||
}
|
||||
|
||||
dl,
|
||||
form,
|
||||
hr,
|
||||
ol,
|
||||
p,
|
||||
pre,
|
||||
table,
|
||||
ul,
|
||||
blockquote {
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
ul.index-list {
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
ol {
|
||||
list-style: decimal;
|
||||
}
|
||||
|
||||
ol.toc {
|
||||
list-style: disc;
|
||||
}
|
||||
|
||||
ol.toc-child {
|
||||
list-style: circle;
|
||||
margin-bottom: unset;
|
||||
}
|
||||
|
||||
li ol,
|
||||
li ul,
|
||||
ol,
|
||||
ul {
|
||||
margin-bottom: 0.8em;
|
||||
}
|
||||
|
||||
li ul,
|
||||
ul {
|
||||
list-style: disc;
|
||||
}
|
||||
|
||||
table caption,
|
||||
table td,
|
||||
table th,
|
||||
td,
|
||||
th {
|
||||
border: 1px solid #ddd;
|
||||
padding: 0.5em 1em;
|
||||
}
|
||||
|
||||
table th,
|
||||
th,
|
||||
table tr:nth-child(even),
|
||||
tr:nth-child(even) {
|
||||
background: var(--alt-bg-color);
|
||||
}
|
||||
|
||||
em,
|
||||
caption,
|
||||
legend {
|
||||
font-weight: inherit;
|
||||
}
|
||||
|
||||
code,
|
||||
kbd,
|
||||
pre,
|
||||
samp {
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
kbd {
|
||||
border: 1px solid var(--alt-bg-color);
|
||||
border-radius: 3px;
|
||||
/* https://github.com/alvaromontoro/almond.css */
|
||||
padding: 0.125rem 0.25rem;
|
||||
}
|
||||
|
||||
/* set bg colour of single backtick wrap */
|
||||
p code {
|
||||
background: var(--alt-bg-color);
|
||||
}
|
||||
|
||||
/* set codeblock width to minimum */
|
||||
pre {
|
||||
border-left: 0.1em solid var(--link-underline);
|
||||
padding-left: 1em;
|
||||
max-width: max-content;
|
||||
}
|
||||
|
||||
mark {
|
||||
background: var(--alt-bg-color);
|
||||
color: unset;
|
||||
}
|
||||
|
||||
/* Hide post's excerpt in article */
|
||||
.p-summary {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.main-nav-link {
|
||||
margin-right: 0.5em;
|
||||
padding-bottom: 0.5em;
|
||||
display: inline-block;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
b,
|
||||
strong {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
background: var(--alt-bg-color);
|
||||
border-left: 0.1em solid #666;
|
||||
padding-left: 1em;
|
||||
margin: 1em 3em 1em 0em;
|
||||
max-width: max-content;
|
||||
}
|
||||
|
||||
.page-nav {
|
||||
margin-top: 2em;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.footer {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.container {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.copyright,
|
||||
.footer-content,
|
||||
.footer-links {
|
||||
text-align: center;
|
||||
font-size: 0.95em;
|
||||
max-width: 80ch;
|
||||
padding: 0 2ch;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.copyright li,
|
||||
.footer-content li,
|
||||
.footer-links li {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.footer-content {
|
||||
margin: 0 auto;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.footer-links {
|
||||
font-size: 0.9em;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.footer-links a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.footer-links li {
|
||||
margin-right: 1em;
|
||||
}
|
||||
|
||||
/* Wrap count in brackets */
|
||||
span.index-list-count:before {
|
||||
content: ' (';
|
||||
}
|
||||
|
||||
span.index-list-count:after {
|
||||
content: ')';
|
||||
}
|
||||
|
||||
/* align badge in /about/ */
|
||||
.about-body .article .article-entry img.svg {
|
||||
/* display same line */
|
||||
display: inline;
|
||||
/* align to the left */
|
||||
margin: auto 0 .5em;
|
||||
}
|
||||
|
||||
/* https://jrl.ninja/etc/1/ */
|
||||
.archive-container {
|
||||
position: relative;
|
||||
max-width: 80ch;
|
||||
padding: 2ch;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.archive-container article {
|
||||
margin-bottom: 1em;
|
||||
line-height: 1em;
|
||||
}
|
||||
|
||||
.archive-container a,
|
||||
.archive-container a:hover {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.archive-container .header,
|
||||
.article .header {
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
.archive-container .archive-article-header > a,
|
||||
.archive-container .archive-article-header > h1 {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.archive-container .archive-article-header h1 {
|
||||
font-size: 1.2em;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.archive-container .archive-article-date {
|
||||
font-size: 0.8em;
|
||||
}
|
||||
|
||||
.archive-container .extend,
|
||||
.archive-container .page-number {
|
||||
margin-right: 1em;
|
||||
}
|
||||
|
||||
.article {
|
||||
max-width: 80ch;
|
||||
padding: 2ch;
|
||||
margin: auto;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.article .article-entry {
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.article .article-entry img {
|
||||
display: block;
|
||||
margin: auto;
|
||||
margin-bottom: 0.5em;
|
||||
max-height: 30em;
|
||||
}
|
||||
|
||||
.article .article-entry blockquote footer {
|
||||
text-align: center;
|
||||
font-size: 80%;
|
||||
}
|
||||
|
||||
.article .article-entry blockquote footer cite {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.article .article-entry blockquote footer cite:before {
|
||||
content: "—";
|
||||
padding: 0 0.5em;
|
||||
}
|
||||
|
||||
.article .article-entry blockquote footer strong {
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.article .article-entry table caption,
|
||||
.article .article-entry table td,
|
||||
.article .article-entry table th,
|
||||
.article .article-entry td,
|
||||
.article .article-entry th {
|
||||
word-break: normal;
|
||||
}
|
||||
|
||||
.article .article-entry .pullquote {
|
||||
width: 45%;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.article .article-entry .pullquote.left {
|
||||
float: left;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.article .article-entry .pullquote.right {
|
||||
float: right;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.article .article-entry .video-container {
|
||||
position: relative;
|
||||
padding-top: 56.25%;
|
||||
margin-bottom: 1em;
|
||||
height: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.article .article-entry .video-container iframe {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.article .article-nav {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.article .article-nav-link-wrap {
|
||||
display: inline-block;
|
||||
padding: 0 1em;
|
||||
width: 49%;
|
||||
box-sizing: border-box;
|
||||
vertical-align: top;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.article .article-nav-link-wrap:hover {
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
.article .article-nav-link-wrap.newer {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.article .article-nav-link-wrap.older {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.article .article-meta {
|
||||
font-size: 0.9em;
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.article .article-meta .label {
|
||||
margin-right: 0.5em;
|
||||
}
|
||||
|
||||
.article .article-meta .article-category,
|
||||
.article .article-meta .article-tag-list {
|
||||
display: inline-block;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.article .article-meta .article-tag-list-item {
|
||||
display: inline-block;
|
||||
margin-right: 1em;
|
||||
}
|
||||
|
||||
.article .article-meta .article-tag-list-item:before {
|
||||
content: "#";
|
||||
}
|
||||
|
||||
.article .article-gallery img {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.article-type-page .article-footer {
|
||||
display: none;
|
||||
}
|
||||
|
||||
table caption,
|
||||
table td,
|
||||
td {
|
||||
border: 1px solid #ddd;
|
||||
}
|
||||
|
||||
/*
|
||||
* Visual Studio 2015 dark style
|
||||
* Author: Nicolas LLOBERA <nllobera@gmail.com>
|
||||
* https://github.com/highlightjs/highlight.js/blob/master/src/styles/vs2015.css
|
||||
*/
|
||||
|
||||
.hljs {
|
||||
display: block;
|
||||
overflow-x: auto;
|
||||
/* padding: 0.5em;
|
||||
background: #1E1E1E;
|
||||
color: #DCDCDC; */
|
||||
}
|
||||
|
||||
.hljs-keyword,
|
||||
.hljs-literal,
|
||||
.hljs-symbol,
|
||||
.hljs-name {
|
||||
color: #569CD6;
|
||||
}
|
||||
.hljs-link {
|
||||
color: #569CD6;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.hljs-built_in,
|
||||
.hljs-type {
|
||||
color: #4EC9B0;
|
||||
}
|
||||
|
||||
.hljs-number,
|
||||
.hljs-class {
|
||||
color: #689E44;
|
||||
}
|
||||
|
||||
.hljs-string,
|
||||
.hljs-meta-string {
|
||||
color: #D69D85;
|
||||
}
|
||||
|
||||
.hljs-regexp,
|
||||
.hljs-template-tag {
|
||||
color: #9A5334;
|
||||
}
|
||||
|
||||
.hljs-subst,
|
||||
.hljs-function,
|
||||
.hljs-title,
|
||||
.hljs-params,
|
||||
.hljs-formula {
|
||||
color: #808080;
|
||||
}
|
||||
|
||||
.hljs-comment,
|
||||
.hljs-quote {
|
||||
color: #57A64A;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.hljs-doctag {
|
||||
color: #608B4E;
|
||||
}
|
||||
|
||||
.hljs-meta,
|
||||
.hljs-meta-keyword,
|
||||
.hljs-tag {
|
||||
color: #9B9B9B;
|
||||
}
|
||||
|
||||
.hljs-variable,
|
||||
.hljs-template-variable {
|
||||
color: #BD63C5;
|
||||
}
|
||||
|
||||
.hljs-attr,
|
||||
.hljs-attribute,
|
||||
.hljs-builtin-name {
|
||||
color: #9CDCFE;
|
||||
}
|
||||
|
||||
.hljs-section {
|
||||
color: gold;
|
||||
}
|
||||
|
||||
.hljs-emphasis {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.hljs-strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/*.hljs-code {
|
||||
font-family:'Monospace';
|
||||
}*/
|
||||
|
||||
.hljs-bullet,
|
||||
.hljs-selector-tag,
|
||||
.hljs-selector-id,
|
||||
.hljs-selector-class,
|
||||
.hljs-selector-attr,
|
||||
.hljs-selector-pseudo {
|
||||
color: #D7BA7D;
|
||||
}
|
||||
|
||||
.hljs-addition {
|
||||
background-color: var(--code-add-bg-color);
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.hljs-deletion {
|
||||
background-color: var(--code-del-bg-color);
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
/* hide mobile nav menu and its checkbox */
|
||||
/* when it's not mobile */
|
||||
.mobile-nav,
|
||||
#mobile-menu-toggle,
|
||||
#mobile-nav-link {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* set line-height in /archives page */
|
||||
.mobile-nav-menu {
|
||||
line-height: 1.8;
|
||||
}
|
||||
|
||||
/* overlap other elements */
|
||||
#mobile-nav-link {
|
||||
background: var(--main-bg-color);
|
||||
color: var(--main-font-color);
|
||||
border: 2px solid #999;
|
||||
border-radius: 5px;
|
||||
right: 0;
|
||||
margin-left: 5px;
|
||||
padding: 0.5em;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.mobile-nav-link-a {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#menu-button {
|
||||
font-size: 2em;
|
||||
}
|
||||
|
||||
/* search bar */
|
||||
.search-container {
|
||||
border-radius: 5px;
|
||||
width: 10em;
|
||||
background: var(--alt-bg-color);
|
||||
outline: 0;
|
||||
float: right;
|
||||
height: 1.8em;
|
||||
position: relative;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
/* inline-block to position them in same line */
|
||||
.search-box,
|
||||
.search-button{
|
||||
display:inline-block;
|
||||
}
|
||||
|
||||
/* hide the border and fill the height to parent's */
|
||||
.search-box {
|
||||
border: 0;
|
||||
border-radius: 5px;
|
||||
height:1.8em;
|
||||
width: 85%;
|
||||
padding-left: 0.2em;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/*
|
||||
* Override sanitize/forms
|
||||
* to prevent conflict with
|
||||
* .search-box padding-left
|
||||
*/
|
||||
input {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* highlight the text box when focused */
|
||||
.search-box:focus {
|
||||
border: 1px solid var(--link-underline);
|
||||
}
|
||||
|
||||
/* align the button to center */
|
||||
.search-button {
|
||||
width: 10%;
|
||||
position: absolute;
|
||||
top: 45%;
|
||||
transform: translate(30%, -50%);
|
||||
}
|
||||
|
||||
/* fill the svg with parent's font color */
|
||||
svg {
|
||||
fill: currentColor;
|
||||
}
|
||||
|
||||
/* don't underline specific <a> tag */
|
||||
.no-underline {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
/* fill the search, permalink and share icons with link's color */
|
||||
svg#search,
|
||||
svg#link,
|
||||
svg#share {
|
||||
fill: var(--link-underline);
|
||||
}
|
||||
|
||||
/* use font color when hover */
|
||||
svg#search:hover,
|
||||
svg#link:hover,
|
||||
svg#share:hover {
|
||||
fill: currentColor;
|
||||
}
|
||||
|
||||
/* hide share button by default
|
||||
unhide (via JS) if Web Share API is supported */
|
||||
.article-title a#btnshare {
|
||||
display: none;
|
||||
height: 1em;
|
||||
width: 1em;
|
||||
}
|
||||
|
||||
.article-title a#btnshare img {
|
||||
display: block;
|
||||
margin: auto;
|
||||
margin-bottom: 0.5em;
|
||||
max-height: 30em;
|
||||
}
|
||||
|
||||
.article .article-entry a.headerlink,
|
||||
.article-title a#btnshare {
|
||||
margin-left: 0.5em;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.article-title {
|
||||
font-size: 1.8rem;
|
||||
}
|
||||
|
||||
/* hide desktop nav */
|
||||
.main-nav {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* display mobile nav */
|
||||
.mobile-nav {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
/* reduce height of mobile header */
|
||||
height: 1.5em;
|
||||
}
|
||||
|
||||
/* display links when menu button is clicked */
|
||||
/* use grid to display each link in new line */
|
||||
#mobile-menu-toggle:checked + #mobile-nav-link {
|
||||
display: grid;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 468px) {
|
||||
.archive-container .archive-footer,
|
||||
.archive-container .post-footer {
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
.archive-container .archive-article-header > a,
|
||||
.archive-container .archive-article-header > h1 {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 320px) {
|
||||
.article {
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
.archive-container .archive-footer,
|
||||
.archive-container .post-footer {
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
.footer,
|
||||
.footer-content {
|
||||
font-size: 0.9em;
|
||||
}
|
||||
}
|
||||
|
||||
.copy-button {
|
||||
border: 1px solid #999;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
font-size: 0.8em;
|
||||
line-height: 1em;
|
||||
margin-top: 1em;
|
||||
padding: 5px;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.copy-button:hover {
|
||||
background: var(--main-font-color);
|
||||
color: var(--main-bg-color);
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--main-bg-color: black;
|
||||
--main-font-color: #e6e6e6;
|
||||
--alt-bg-color: #333;
|
||||
--code-add-bg-color: #144212;
|
||||
--code-del-bg-color: #600;
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: light) {
|
||||
:root {
|
||||
--main-bg-color: #f1f1f1;
|
||||
--main-font-color: #333;
|
||||
--alt-bg-color: #d8d8d8;
|
||||
--code-add-bg-color: #e6ffec;
|
||||
--code-del-bg-color: #ffebe9;
|
||||
}
|
||||
|
||||
/*
|
||||
* Visual Studio-like style based on original C# coloring
|
||||
* Author: Jason Diamond <jason@diamond.name>
|
||||
* https://github.com/highlightjs/highlight.js/blob/master/src/styles/vs.css
|
||||
*/
|
||||
|
||||
.hljs {
|
||||
display: block;
|
||||
overflow-x: auto;
|
||||
/* padding: 0.5em;
|
||||
background: white;
|
||||
color: black; */
|
||||
}
|
||||
|
||||
.hljs-comment,
|
||||
.hljs-quote,
|
||||
.hljs-variable {
|
||||
color: #008000;
|
||||
}
|
||||
|
||||
.hljs-keyword,
|
||||
.hljs-selector-tag,
|
||||
.hljs-built_in,
|
||||
.hljs-name,
|
||||
.hljs-tag {
|
||||
color: #00f;
|
||||
}
|
||||
|
||||
.hljs-string,
|
||||
.hljs-title,
|
||||
.hljs-section,
|
||||
.hljs-attribute,
|
||||
.hljs-literal,
|
||||
.hljs-template-tag,
|
||||
.hljs-template-variable,
|
||||
.hljs-type {
|
||||
color: #a31515;
|
||||
}
|
||||
|
||||
.hljs-selector-attr,
|
||||
.hljs-selector-pseudo,
|
||||
.hljs-meta {
|
||||
color: #2b91af;
|
||||
}
|
||||
|
||||
.hljs-doctag {
|
||||
color: #808080;
|
||||
}
|
||||
|
||||
.hljs-attr {
|
||||
color: #f00;
|
||||
}
|
||||
|
||||
.hljs-symbol,
|
||||
.hljs-bullet,
|
||||
.hljs-link {
|
||||
color: #00b0e8;
|
||||
}
|
||||
|
||||
|
||||
.hljs-emphasis {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.hljs-strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
// Search button function
|
||||
document.getElementById('search-click').addEventListener('click', () => {
|
||||
searchForm.submit()
|
||||
}, false)
|
||||
|
||||
document.getElementById('search-click-mobile').addEventListener('click', () => {
|
||||
searchFormMob.submit()
|
||||
}, false)
|
||||
|
||||
// Hide mobile menu when click outside of the menu
|
||||
document.addEventListener('click', (evt) => {
|
||||
const mainNavDisplay = window.getComputedStyle(document.getElementsByClassName('main-nav')[0]).getPropertyValue('display')
|
||||
const mobileNav = document.getElementById('mobile-nav-link')
|
||||
const mobileToggle = document.getElementById('mobile-menu-toggle')
|
||||
const isClickedOutside = !mobileNav.contains(evt.target)
|
||||
|
||||
// Exit if not in mobile view or menu button is clicked or menu is currently hidden
|
||||
// Menu button click triggers `menu-button` and `mobile-menu-toggle`
|
||||
if (mainNavDisplay !== 'none' ||
|
||||
evt.target.id === 'menu-button' || evt.target.id === 'mobile-menu-toggle' ||
|
||||
mobileToggle.checked === false) return
|
||||
|
||||
if (isClickedOutside) {
|
||||
mobileToggle.checked = false
|
||||
}
|
||||
}, false)
|
||||
|
||||
// Web Share API
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/Navigator/share
|
||||
// Only available on supporting browsers and HTTPS
|
||||
if (navigator.share && document.location.protocol === 'https:') {
|
||||
const shareBtn = document.getElementById('btnshare')
|
||||
|
||||
// Unhide share-button if supported
|
||||
shareBtn.style.display = 'inline-block'
|
||||
|
||||
shareBtn.addEventListener('click', async () => {
|
||||
const query = (selector) => {
|
||||
return document.querySelector(selector)
|
||||
}
|
||||
|
||||
const title = query('meta[property="og:title"]') ? query('meta[property="og:title"]').content : ''
|
||||
const text = query('meta[property="og:description"]') ? query('meta[property="og:description"]').content : ''
|
||||
const url = query('link[rel="canonical"]') ? query('link[rel="canonical"]').href : document.location.href
|
||||
|
||||
await navigator.share({ title, text, url })
|
||||
}, false)
|
||||
}
|
||||
|
||||
// Use duckduckgo's onion address when the site is accessed via .onion
|
||||
if (document.location.hostname.endsWith('.onion')) {
|
||||
const searchForms = document.querySelectorAll('form#searchForm, form#searchFormMob')
|
||||
|
||||
searchForms.forEach((form) => {
|
||||
form.setAttribute('action', 'https://duckduckgogg42xjoc72x3sjasowoarfbgcmvfimaftt6twagswzczad.onion/')
|
||||
})
|
||||
}
|
||||
|
||||
// Remove navigation link of current page
|
||||
const navLink = document.querySelectorAll('a.main-nav-link, a.mobile-nav-link-a')
|
||||
navLink.forEach((ele) => {
|
||||
const eleHref = new URL(ele.href)
|
||||
const docPath = document.location.pathname
|
||||
if (eleHref.pathname !== '/' && eleHref.pathname === docPath) {
|
||||
const span = document.createElement('span')
|
||||
span.className = ele.className
|
||||
span.textContent = ele.textContent
|
||||
ele.outerHTML = span.outerHTML
|
||||
}
|
||||
})
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Copy button
|
||||
* Following functions only execute after
|
||||
* the 'document' is ready or
|
||||
* <script src> is executed.
|
||||
*/
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
// Copy button
|
||||
const copyCode = new ClipboardJS('.copy-button', {
|
||||
target: (trigger) => {
|
||||
return trigger.previousElementSibling
|
||||
}
|
||||
})
|
||||
|
||||
copyCode.on('success', (event) => {
|
||||
event.clearSelection()
|
||||
event.trigger.textContent = 'Copied'
|
||||
window.setTimeout(() => {
|
||||
event.trigger.textContent = 'Copy'
|
||||
}, 2000)
|
||||
})
|
||||
|
||||
copyCode.on('error', (event) => {
|
||||
event.trigger.textContent = 'Press "Ctrl + C" to copy'
|
||||
window.setTimeout(() => {
|
||||
event.trigger.textContent = 'Copy'
|
||||
}, 2000)
|
||||
})
|
||||
}, false)
|
After Width: | Height: | Size: 470 KiB |
|
@ -0,0 +1,9 @@
|
|||
<!--
|
||||
Fork Awesome 1.1.7
|
||||
License - https://forkaweso.me/Fork-Awesome/license
|
||||
Copyright 2018 Dave Gandy & Fork Awesome
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
-->
|
||||
<svg id="link" xmlns="http://www.w3.org/2000/svg" viewBox="15 15 1635 1635"><path d="M1456 1216c0-26-10-50-28-68l-208-208c-18-18-43-28-68-28-29 0-52 11-72 32 33 33 72 61 72 112 0 53-43 96-96 96-51 0-79-39-112-72-21 20-33 43-33 73 0 25 10 50 28 68l206 207c18 18 43 27 68 27s50-9 68-26l147-146c18-18 28-42 28-67zM753 511c0-25-10-50-28-68L519 236c-18-18-43-28-68-28s-50 10-68 27L236 381c-18 18-28 42-28 67 0 26 10 50 28 68l208 208c18 18 43 27 68 27 29 0 52-10 72-31-33-33-72-61-72-112 0-53 43-96 96-96 51 0 79 39 112 72 21-20 33-43 33-73zm895 705c0 76-31 150-85 203l-147 146c-54 54-127 83-203 83-77 0-150-30-204-85l-206-207c-54-54-83-127-83-203 0-79 32-154 88-209l-88-88c-55 56-129 88-208 88-76 0-150-30-204-84L100 652c-55-55-84-127-84-204 0-76 31-150 85-203L248 99c54-54 127-83 203-83 77 0 150 30 204 85l206 207c54 54 83 127 83 203 0 79-32 154-88 209l88 88c55-56 129-88 208-88 76 0 150 30 204 84l208 208c55 55 84 127 84 204z" fill="#3f72af"/></svg>
|
|
@ -0,0 +1,2 @@
|
|||
<!-- Font Awesome Free 5.5.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) -->
|
||||
<svg id="search" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M505 442.7L405.3 343c-4.5-4.5-10.6-7-17-7H372c27.6-35.3 44-79.7 44-128C416 93.1 322.9 0 208 0S0 93.1 0 208s93.1 208 208 208c48.3 0 92.7-16.4 128-44v16.3c0 6.4 2.5 12.5 7 17l99.7 99.7c9.4 9.4 24.6 9.4 33.9 0l28.3-28.3c9.4-9.4 9.4-24.6.1-34zM208 336c-70.7 0-128-57.2-128-128 0-70.7 57.2-128 128-128 70.7 0 128 57.2 128 128 0 70.7-57.2 128-128 128z" fill="#3f72af"/></svg>
|
After Width: | Height: | Size: 633 B |
|
@ -0,0 +1,11 @@
|
|||
<!--
|
||||
Fork Awesome 1.1.7
|
||||
License - https://forkaweso.me/Fork-Awesome/license
|
||||
Copyright 2018 Dave Gandy & Fork Awesome
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
-->
|
||||
<svg id="share" viewBox="0 50 1635 1635" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M1216 1024c177 0 320 143 320 320s-143 320-320 320-320-143-320-320c0-11 1-23 2-34l-360-180c-57 53-134 86-218 86-177 0-320-143-320-320s143-320 320-320c84 0 161 33 218 86l360-180c-1-11-2-23-2-34 0-177 143-320 320-320s320 143 320 320-143 320-320 320c-84 0-161-33-218-86L638 862c1 11 2 23 2 34s-1 23-2 34l360 180c57-53 134-86 218-86z" fill="#3f72af"/>
|
||||
</svg>
|