Merge branch 'master' into production

This commit is contained in:
Cotes Chung 2023-03-18 02:08:00 +08:00
commit 61d9c2247c
No known key found for this signature in database
GPG key ID: 0D9E54843167A808
104 changed files with 2198 additions and 1890 deletions

5
.browserslistrc Normal file
View file

@ -0,0 +1,5 @@
# https://github.com/browserslist/browserslist#browserslistrc
last 2 versions
> 0.2%
not dead

View file

@ -9,8 +9,11 @@ trim_trailing_whitespace = true
end_of_line = lf
insert_final_newline = true
[*.js]
indent_size = 4
[*.{js,css,scss}]
quote_type = single
[*.{yml,yaml}]
quote_type = double
[*.md]
trim_trailing_whitespace = false

View file

@ -1,66 +1,55 @@
# How to Contribute
We'd like to thank you for sparing time to improve this project! Here are some guidelines for contributing
:tada: We really appreciate you taking the time to improve this project! :tada:
To ensure that the blog design is not confused, this project does not accept suggestions for design changes, such as color scheme, fonts, typography, etc. If your request is about an enhancement, it is recommended to first submit a [_Feature Request_](https://github.com/cotes2020/jekyll-theme-chirpy/issues/new?labels=enhancement&template=feature_request.md) issue to discuss whether your idea fits the project.
To ensure that the blog design is not confusing, this project does not accept
suggestions for design changes, such as color scheme, fonts, typography, etc.
If your request is about an enhancement, it is recommended to first submit a
[Feature Request][pr-issue] issue to discuss whether your idea fits the project.
## Basic Process
Generally, contribute to the project by:
Basically, you can follow these steps to complete the contribution.
1. Fork this project on GitHub and clone it locally.
2. Create a new branch from the default branch and give it a descriptive name (format: `feature/<add-new-feat>` / `fix/<fix-a-bug>`).
3. After completing the development, submit a new _Pull Request_. Note that the commit message must follow the [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/), otherwise it will fail the PR check.
2. Create a new branch from the default branch and give it a descriptive name
(format: `feature/<add-new-feat>` or `fix/<fix-a-bug>`).
3. After completing development, create a [Conventional Commit][cc] with git.
(See also: ["Verify the commits"](#verify-the-commits))
4. Create a [Pull Request][gh-pr].
## Modifying JavaScript
## Make sure you can pass the CI tests
If your contribution involves JavaScript modification, please read the following sections.
This project has [CI][ci] turned on. In order for your [PR][gh-pr] to pass the test,
please read the following.
### Inline Scripts
If you need to add comments to the inline JavaScript (the code between the HTML tags `<script>` and `</script>`), please use `/* */` instead of two slashes `//`. Because the HTML will be compressed by [jekyll-compress-html](https://github.com/penibelst/jekyll-compress-html) during deployment, but it cannot handle the `//` properly, which will disrupt the structure of the compressed HTML.
### External Scripts
If you need to add/change/delete the JavaScript in the directory `_javascript/`, setting up [`Node.js`](https://nodejs.org/) and [`npx`](https://www.npmjs.com/package/npx) is a requirement. And then install the development dependencies:
### Check the core functionality
```console
$ npm i
bash ./tools/test
```
During JavaScript development, real-time debugging can be performed through the following commands:
Firstly, start a Jekyll server:
### Check the SASS syntax style
```console
$ bash tools/run
npm test
```
And then open a new terminal tab and run:
### Verify the commits
```console
# Type 'Ctrl + C' to stop
$ npx gulp dev
```
After debugging, run the command `npx gulp` (without any argument) will automatically output the compressed files to the directory `assets/js/dist/`.
## Verify the commit messages
If you want to make sure your commits pass the CI check, you can refer to the following steps.
Before you create a git commit, please complete the following setup.
Install `commitlint` & `husky`:
```console
$ npm i -g @commitlint/{cli,config-conventional} husky
npm i -g @commitlint/{cli,config-conventional} husky
```
And then enable `husky`:
```console
$ husky install
husky install
```
---
:tada: Your volunteering will make the open-source world more beautiful, thanks again! :tada:
[pr-issue]: https://github.com/cotes2020/jekyll-theme-chirpy/issues/new?labels=enhancement&template=feature_request.md
[gh-pr]: https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests
[cc]: https://www.conventionalcommits.org/
[ci]: https://en.wikipedia.org/wiki/Continuous_integration

View file

@ -3,21 +3,20 @@ name: Bug Report
about: Create a report to help us improve
---
<!-- NOTE: Please maintain all sections, otherwise the issue will be automatically closed :) -->
**NOTE:** Before you start, the following should be completed.
## Checklist
- Read [tutorial][tutorial] to understand the usage and the correct effect of functional design.
- Make sure no [similar issue(including closed ones)][issues] exists.
- Make sure the bug is found in the latest code of the `master` branch.
<!-- Please complete the following list of tasks, and then check it by changing the "[ ]" to "[x]" -->
- [ ] I have read the [tutorials](https://cotes2020.github.io/chirpy-demo/categories/tutorial/) and know the correct effect of the functional design.
- [ ] There are no similar reports on [existing issues](https://github.com/cotes2020/jekyll-theme-chirpy/issues?q=is%3Aissue) (including closed ones).
- [ ] I found the bug on the latest code of the `master` branch.
[tutorial]: https://cotes2020.github.io/chirpy-demo/categories/tutorial/
[issues]: https://github.com/cotes2020/jekyll-theme-chirpy/issues?q=is%3Aissue
## Describe the bug
<!-- A clear and concise description of what the bug is. -->
### To Reproduce
## To Reproduce
Steps to reproduce the behavior:
<!--
@ -27,15 +26,15 @@ Steps to reproduce the behavior:
4. See error
-->
### Expected behavior
## Expected behavior
<!-- A clear and concise description of what you expected to happen. -->
### Screenshots
## Logs/Screenshots
<!-- If applicable, add screenshots to help explain your problem. -->
<!-- If applicable, add logs/screenshots to help explain your problem. -->
### Environment
## Environment
| Command | Version |
|-----------------------------------|---------|
@ -45,21 +44,25 @@ Steps to reproduce the behavior:
| `bundle exec jekyll -v` | |
| `bundle info jekyll-theme-chirpy` | |
<!-- If necessary, uncomment and fill in the following list:
### Desktop
<!-- If necessary, uncomment and fill in the following list:
- OS: [e.g. macOS 10.15.6]
- Browser: [e.g. Chrome 85.0.4183.83 (64-bit)]
-->
<!-- If necessary, uncomment and fill in the following list:
### Smartphone
<!-- If necessary, uncomment and fill in the following list:
- Device: [e.g. iPhone 6]
- OS: [e.g. iOS 13.6.1]
- Browser: [e.g. Chrome 22]
-->
### Additional context
## Additional context
<!-- Add any other context about the problem here. -->

View file

@ -4,16 +4,14 @@ about: Suggest an idea for this project
labels: enhancement
---
<!-- NOTE: Please maintain all sections, otherwise the issue will be automatically closed :) -->
**NOTE:** Before you start, the following should be completed.
## Checklist
<!-- Please complete the following list of tasks, and then check it by changing the "[ ]" to "[x]" -->
- [ ] I have read the [contributing guidelines](https://github.com/cotes2020/jekyll-theme-chirpy/blob/master/.github/CONTRIBUTING.md).
- [ ] There is no similar request on [existing issues](https://github.com/cotes2020/jekyll-theme-chirpy/issues?q=is%3Aissue) (including closed ones).
- [ ] I have read the [project progress](https://github.com/cotes2020/jekyll-theme-chirpy/projects) and know the current progress of the project.
- [ ] I was in the `master` branch of the latest code.
- Read [tutorial][tutorial] to understand the usage and the correct effect of functional design.
- Make sure no [similar issue(including closed ones)][issues] exists.
- Make sure the request is based on the latest code in the `master` branch.
[tutorial]: https://cotes2020.github.io/chirpy-demo/categories/tutorial/
[issues]: https://github.com/cotes2020/jekyll-theme-chirpy/issues?q=is%3Aissue
## Is your feature request related to a problem? Please describe

28
.github/ISSUE_TEMPLATE/help_wanted.md vendored Normal file
View file

@ -0,0 +1,28 @@
---
name: Help Wanted
about: Need help that is not covered in the tutorial
labels: 'help wanted'
---
**NOTE:** Before you start, the following should be completed.
- Read [tutorial][tutorial] to understand the usage and the correct effect of functional design.
- Make sure no [similar issue(including closed ones)][issues] exists.
- Try to find the answer on [Jekyll Forum][forum] and [StackOverflow][stack_overflow].
[tutorial]: https://cotes2020.github.io/chirpy-demo/categories/tutorial/
[issues]: https://github.com/cotes2020/jekyll-theme-chirpy/issues?q=is%3Aissue
[forum]: https://talk.jekyllrb.com/
[stack_overflow]: https://stackoverflow.com/questions/tagged/jekyll
## Description
<!-- Please describe your need in detail. -->
## Operations you have already tried
<!-- Describe the effort you went through. -->
## Logs/Screenshots
<!-- If applicable, add logs/screenshots to help explain your problem. -->

View file

@ -1,19 +1,19 @@
---
name: Question
about: Ask whatever you want
about: Issues that differ from other templates
labels: question
---
<!-- NOTE: Please maintain all sections, otherwise the issue will be automatically closed :) -->
**NOTE:** Before you start, the following should be completed.
## Checklist
- Read [tutorial][tutorial] to understand the usage and the correct effect of functional design.
- Make sure no [similar issue(including closed ones)][issues] exists.
- Try to find the answer on [Jekyll Forum][forum] and [StackOverflow][stack_overflow].
<!-- Please complete the following list of tasks, and then check it by changing the "[ ]" to "[x]" -->
- [ ] I have read the [newlest tutorials](https://cotes2020.github.io/chirpy-demo/categories/tutorial/) and know the correct effect of the functional design.
- [ ] There is no similar question on [existing issues](https://github.com/cotes2020/jekyll-theme-chirpy/issues?q=is%3Aissue) (including closed ones).
- [ ] I have tried to find the answer on [Jekyll Forum](https://talk.jekyllrb.com/) and [StackOverflow](https://stackoverflow.com/questions/tagged/jekyll).
- [ ] My question is based on the latest code of the `master` branch.
[tutorial]: https://cotes2020.github.io/chirpy-demo/categories/tutorial/
[issues]: https://github.com/cotes2020/jekyll-theme-chirpy/issues?q=is%3Aissue
[forum]: https://talk.jekyllrb.com/
[stack_overflow]: https://stackoverflow.com/questions/tagged/jekyll
## Description

View file

@ -32,11 +32,3 @@ Please describe the tests that you ran to verify your changes. Provide instructi
- Ruby version: <!-- by running: `ruby -v` -->
- Bundler version: <!-- by running: `bundle -v`-->
- Jekyll version: <!-- by running: `bundle list | grep " jekyll "` -->
### Checklist
<!-- Select checkboxes by change the "[ ]" to "[x]" -->
- [ ] I have performed a self-review of my code
- [ ] I have commented on my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation
- [ ] My changes generate no new warnings

12
.github/SECURITY.md vendored Normal file
View file

@ -0,0 +1,12 @@
# Security Policy
## Supported Versions
| Version | Supported |
|---------| ------------------ |
| 5.x | :white_check_mark: |
| < 5.0.0 | :x: |
## Reporting a Vulnerability
If you find a vulnerability, please report it to `cotes.chung@gmail.com`. We will try our best to respond within a week. Thank you for your time!

View file

@ -1,18 +1,18 @@
name: 'CI'
name: "CI"
on:
push:
branches-ignore:
- 'production'
- 'docs'
- "production"
- "docs"
paths-ignore:
- '.github/**'
- '!.github/workflows/ci.yml'
- '.gitignore'
- 'README.md'
- 'LICENSE'
- ".github/**"
- "!.github/workflows/ci.yml"
- ".gitignore"
- "README.md"
- "LICENSE"
pull_request:
paths:
- '**'
- "**"
jobs:
build:
@ -34,5 +34,11 @@ jobs:
ruby-version: ${{ matrix.ruby }}
bundler-cache: true
- name: Setup Node
uses: actions/setup-node@v3
- name: Build Assets
run: npm i && npm run build
- name: Test Site
run: bash tools/test

44
.github/workflows/codeql.yml vendored Normal file
View file

@ -0,0 +1,44 @@
name: "CodeQL"
on:
push:
paths: ["**.js"]
pull_request:
paths: ["**.js"]
schedule:
- cron: "0 0 * * 5"
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: ["javascript"]
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
steps:
- name: Checkout repository
uses: actions/checkout@v3
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: "${{ matrix.language }}"
# Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v2
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
with:
category: "/language:${{ matrix.language }}"

View file

@ -1,18 +0,0 @@
name: Intercept bad issues
on:
issues:
types: [opened, edited]
jobs:
auto_close_issues:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v1
- name: Auto close issues that did not follow template
uses: lucasbento/auto-close-issues@v1.0.2
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
issue-close-message: ":wave: Hi @${issue.user.login},\n\nThis issue is being automatically closed because it does not follow the issue template. Please DO NOT open another similar issue, try to edit the current issue according to the template, then it will be reopened automatically."
closed-issues-label: "🙁 Not following issue template"

View file

@ -37,7 +37,7 @@ jobs:
- name: Setup Pages
id: pages
uses: actions/configure-pages@v1
uses: actions/configure-pages@v3
- name: Setup Ruby
uses: ruby/setup-ruby@v1

View file

@ -1,15 +1,15 @@
name: 'Style Lint'
name: "Style Lint"
on:
push:
branches-ignore:
- 'production'
- 'docs'
- "production"
- "docs"
paths:
- '_sass/**/*.scss'
- "_sass/**/*.scss"
pull_request:
paths:
- '_sass/**/*.scss'
- "_sass/**/*.scss"
jobs:
stylelint:

29
.gitignore vendored
View file

@ -1,21 +1,22 @@
# hidden files
.*
!.git*
!.editorconfig
!.nojekyll
!.husky
!.commitlintrc.json
!.versionrc.json
!.stylelintrc.json
# bundler cache
_site
# Bundler cache
.bundle
vendor
Gemfile.lock
# rubygem
# Jekyll cache
.jekyll-cache
_site
# RubyGems
*.gem
# npm dependencies
# NPM dependencies
node_modules
package-lock.json
# IDE configurations
.idea
.vscode
# Misc
assets/js/dist

3
.prettierrc Normal file
View file

@ -0,0 +1,3 @@
{
"trailingComma": "none"
}

View file

@ -11,6 +11,11 @@
"alpha-value-notation": "number",
"selector-not-notation": "simple",
"color-hex-length": "long",
"declaration-block-single-line-max-declarations": 3
"declaration-block-single-line-max-declarations": 3,
"scss/operator-no-newline-after": null,
"rule-empty-line-before": [
"always",
{ "ignore": ["after-comment", "first-nested", "inside-block"] }
]
}
}

View file

@ -2,6 +2,23 @@
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
## [5.6.0](https://github.com/cotes2020/jekyll-theme-chirpy/compare/v5.5.2...v5.6.0) (2023-03-17)
### Features
* change TOC plugin to `tocbot` ([#774](https://github.com/cotes2020/jekyll-theme-chirpy/issues/774)) ([02b7bd5](https://github.com/cotes2020/jekyll-theme-chirpy/commit/02b7bd5095a2affe5b4c5ed7b5b182baaf642ff3))
* **i18n:** add Greek Language Support. ([#903](https://github.com/cotes2020/jekyll-theme-chirpy/issues/903)) ([712a9b2](https://github.com/cotes2020/jekyll-theme-chirpy/commit/712a9b22401ce591cf4c0bb03fbdd1693fee30bb))
* **ux:** turn home page posts into clickable cards ([#895](https://github.com/cotes2020/jekyll-theme-chirpy/issues/895)) ([b85f633](https://github.com/cotes2020/jekyll-theme-chirpy/commit/b85f6330dea666350631c4461b742cdb54c5f052))
### Bug Fixes
* css selector string escaping vulnerability ([#888](https://github.com/cotes2020/jekyll-theme-chirpy/issues/888)) ([5c6ec9d](https://github.com/cotes2020/jekyll-theme-chirpy/commit/5c6ec9d06b6571e2c0efe6652078442dca8af477))
* mathematics cannot scroll horizontally ([#760](https://github.com/cotes2020/jekyll-theme-chirpy/issues/760)) ([4681df7](https://github.com/cotes2020/jekyll-theme-chirpy/commit/4681df715118a37ae1e91b588de0adb67f4e331a))
* notch status bar doesn't match theme color ([#918](https://github.com/cotes2020/jekyll-theme-chirpy/issues/918)) ([820ba62](https://github.com/cotes2020/jekyll-theme-chirpy/commit/820ba62e9e939090523a7077d01d01bd78ec84eb))
* some console snippets will be incompletely copied ([e8e4901](https://github.com/cotes2020/jekyll-theme-chirpy/commit/e8e4901e340dd7e5fc5f656dd3c7bcd6c97b886a))
## [5.5.2](https://github.com/cotes2020/jekyll-theme-chirpy/compare/v5.5.1...v5.5.2) (2023-01-30)

View file

@ -2,7 +2,7 @@
# Chirpy Jekyll Theme
A minimal, responsive, and powerful Jekyll theme for presenting professional writing.
A minimal, responsive and feature-rich Jekyll theme for technical writing.
[![Gem Version](https://img.shields.io/gem/v/jekyll-theme-chirpy?color=brightgreen)](https://rubygems.org/gems/jekyll-theme-chirpy)
[![CI](https://github.com/cotes2020/jekyll-theme-chirpy/actions/workflows/ci.yml/badge.svg)](https://github.com/cotes2020/jekyll-theme-chirpy/actions/workflows/ci.yml)
@ -10,14 +10,20 @@
[![GitHub license](https://img.shields.io/github/license/cotes2020/jekyll-theme-chirpy.svg)](https://github.com/cotes2020/jekyll-theme-chirpy/blob/master/LICENSE)
[![996.icu](https://img.shields.io/badge/link-996.icu-%23FF4D5B.svg)](https://996.icu)
[**Live Demo →**](https://cotes2020.github.io/chirpy-demo)
[**Live Demo →**][demo]
[![Devices Mockup](https://chirpy-img.netlify.app/commons/devices-mockup.png)](https://cotes2020.github.io/chirpy-demo)
[![Devices Mockup](https://chirpy-img.netlify.app/commons/devices-mockup.png)][demo]
</div>
## Features
<details>
<summary>
<i>Click to view features</i>
</summary>
<p>
- Dark / Light Theme Mode
- Localized UI language
- Pinned Posts
@ -37,70 +43,53 @@
- Page Views Reporting
- SEO & Performance Optimization
## Quick Start
Before starting, please follow the instructions in the [Jekyll Docs](https://jekyllrb.com/docs/installation/) to complete the installation of `Ruby`, `RubyGems`, `Jekyll`, and `Bundler`. In addition, [Git](https://git-scm.com/) is also required to be installed.
### Step 1. Creating a New Site
Create a new repository from the [**Chirpy Starter**](https://github.com/cotes2020/chirpy-starter/generate) and name it `<GH_USERNAME>.github.io`, where `GH_USERNAME` represents your GitHub username.
### Step 2. Installing Dependencies
Before running for the first time, go to the root directory of your site, and install dependencies as follows:
```console
$ bundle
```
### Step 3. Running Local Server
Run the following command in the root directory of the site:
```console
$ bundle exec jekyll s
```
Or run with Docker:
```console
$ docker run -it --rm \
--volume="$PWD:/srv/jekyll" \
-p 4000:4000 jekyll/jekyll \
jekyll serve
```
After a while, navigate to the site at <http://localhost:4000>.
</p>
</details>
## Documentation
For more details on usage, please refer to the tutorial on the [demo website](https://cotes2020.github.io/chirpy-demo/) / [wiki](https://github.com/cotes2020/jekyll-theme-chirpy/wiki). Note that the tutorial is based on the [latest release](https://github.com/cotes2020/jekyll-theme-chirpy/releases/latest), and the features of the default branch are usually ahead of the documentation.
To explore usage, development, and upgrade guide of the project, please refer to
the [Wiki][wiki].
## Contributing
Welcome to report bugs, improve code quality or submit a new feature. For more information, see [contributing guidelines](.github/CONTRIBUTING.md).
Welcome to report bugs, help improve the code or submit new features.
For more information, please see the ["Contributing Guidelines"][contribute-guide].
## Credits
This theme is mainly built with [Jekyll](https://jekyllrb.com/) ecosystem, [Bootstrap](https://getbootstrap.com/), [Font Awesome](https://fontawesome.com/) and some other wonderful tools (their copyright information can be found in the relevant files). The avatar and favicon design come from [Clipart Max](https://www.clipartmax.com/middle/m2i8b1m2K9Z5m2K9_ant-clipart-childrens-ant-cute/).
This theme is mainly built with [Jekyll][jekyllrb] ecosystem,
[Bootstrap][bootstrap], [Font Awesome][icons] and some other [wonderful tools][lib].
The avatar and favicon design come from [Clipart Max][image].
:tada: Thanks to all the volunteers who contributed to this project, their GitHub IDs are on [this list](https://github.com/cotes2020/jekyll-theme-chirpy/graphs/contributors). Also, I won't forget those guys who submitted the issues or unmerged PR because they reported bugs, shared ideas, or inspired me to write more readable documentation.
Thanks to all the [contributors][contributors]. Also, folks who submitted issues
or unmerged PRs should not be forgotten. Because they reported bugs, shared ideas,
or inspired me to write more readable documentation.
Last but not least, thank [JetBrains][jb] for providing the OSS development license.
Last but not least, thanks to [JetBrains][jetbrains] for providing the
_Open Source Development_ license.
## Sponsoring
If you like this theme or find it helpful, please consider sponsoring me, because it will encourage and help me better maintain the project, I will be very grateful!
If you'd like to sponsor this project, the following options are available.
[![Ko-fi](https://img.shields.io/badge/-Buy%20Me%20a%20Coffee-ff5f5f?logo=ko-fi&logoColor=white)](https://ko-fi.com/coteschung)
[![Wechat Pay](https://img.shields.io/badge/-Tip%20Me%20on%20WeChat-brightgreen?logo=wechat&logoColor=white)][cn-donation]
[![Alipay](https://img.shields.io/badge/-Tip%20Me%20on%20Alipay-blue?logo=alipay&logoColor=white)][cn-donation]
[![Wechat Pay](https://img.shields.io/badge/-Tip%20Me%20on%20WeChat-brightgreen?logo=wechat&logoColor=white)][donation]
[![Alipay](https://img.shields.io/badge/-Tip%20Me%20on%20Alipay-blue?logo=alipay&logoColor=white)][donation]
## License
This work is published under [MIT](https://github.com/cotes2020/jekyll-theme-chirpy/blob/master/LICENSE) License.
This work is published under [MIT][mit] License.
<!-- ReadMe links -->
[jb]: https://www.jetbrains.com/?from=jekyll-theme-chirpy
[cn-donation]: https://sponsor.cotes.page/
[jekyllrb]: https://jekyllrb.com/
[bootstrap]: https://getbootstrap.com/
[icons]: https://fontawesome.com/
[image]: https://www.clipartmax.com/middle/m2i8b1m2K9Z5m2K9_ant-clipart-childrens-ant-cute/
[demo]: https://cotes2020.github.io/chirpy-demo/
[wiki]: https://github.com/cotes2020/jekyll-theme-chirpy/wiki
[contribute-guide]: https://github.com/cotes2020/jekyll-theme-chirpy/blob/master/.github/CONTRIBUTING.md
[contributors]: https://github.com/cotes2020/jekyll-theme-chirpy/graphs/contributors
[lib]: https://github.com/cotes2020/chirpy-static-assets
[jetbrains]: https://www.jetbrains.com/?from=jekyll-theme-chirpy
[donation]: https://sponsor.cotes.page/
[mit]: https://github.com/cotes2020/jekyll-theme-chirpy/blob/master/LICENSE

View file

@ -5,14 +5,13 @@ theme: jekyll-theme-chirpy
# Change the following value to '/PROJECT_NAME' ONLY IF your site type is GitHub Pages Project sites
# and doesn't have a custom domain.
baseurl: ''
baseurl: ""
# The language of the webpage http://www.lingoes.net/en/translator/langcode.htm
# If it has the same name as one of the files in folder `_data/locales`, the layout language will also be changed,
# otherwise, the layout language will use the default value of 'en'.
lang: en
# Change to your timezone http://www.timezoneconverter.com/cgi-bin/findzone/findzone
timezone: Asia/Shanghai
@ -24,10 +23,10 @@ title: Chirpy # the main title
tagline: A text-focused Jekyll theme # it will display as the sub-title
description: >- # used by seo meta and the atom feed
A minimal, responsive, and powerful Jekyll theme for presenting professional writing.
A minimal, responsive and feature-rich Jekyll theme for technical writing.
# fill in the protocol & hostname for your site, e.g., 'https://username.github.io'
url: ''
url: ""
github:
username: github_username # change to your github username
@ -78,12 +77,12 @@ theme_mode: # [light|dark]
# will be added to all image (site avatar & posts' images) paths starting with '/'
#
# e.g. 'https://cdn.com'
img_cdn: 'https://chirpy-img.netlify.app'
img_cdn: "https://chirpy-img.netlify.app"
# the avatar on sidebar, support local or CORS resources
avatar: '/commons/avatar.jpg'
avatar: "/commons/avatar.jpg"
# boolean type, the global switch for ToC in posts.
# boolean type, the global switch for TOC in posts.
toc: true
comments:
@ -139,7 +138,7 @@ collections:
defaults:
- scope:
path: '' # An empty string here means all files in the project
path: "" # An empty string here means all files in the project
type: posts
values:
layout: post
@ -153,7 +152,7 @@ defaults:
values:
comments: false
- scope:
path: ''
path: ""
type: tabs # see `site.collections`
values:
layout: page
@ -180,13 +179,13 @@ compress_html:
envs: [development]
exclude:
- '*.gem'
- '*.gemspec'
- "*.gem"
- "*.gemspec"
- tools
- README.md
- CHANGELOG.md
- LICENSE
- gulpfile.js
- rollup.config.js
- node_modules
- package*.json

View file

@ -22,9 +22,9 @@ bootstrap:
css: https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/css/bootstrap.min.css
js: https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/js/bootstrap.bundle.min.js
bootstrap-toc:
css: https://cdn.jsdelivr.net/gh/afeld/bootstrap-toc@1.0.1/dist/bootstrap-toc.min.css
js: https://cdn.jsdelivr.net/gh/afeld/bootstrap-toc@1.0.1/dist/bootstrap-toc.min.js
toc:
css: https://cdn.jsdelivr.net/npm/tocbot@4.20.1/dist/tocbot.min.css
js: https://cdn.jsdelivr.net/npm/tocbot@4.20.1/dist/tocbot.min.js
fontawesome:
css: https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@6.2.1/css/all.min.css

View file

@ -5,47 +5,47 @@ webfonts: /assets/lib/fonts/main.css
# Libraries
jquery:
js: /assets/lib/jquery-3.6.0/jquery.min.js
js: /assets/lib/jquery/jquery.min.js
bootstrap:
css: /assets/lib/bootstrap-4.6.1/bootstrap.min.css
js: /assets/lib/bootstrap-4.6.1/bootstrap.bundle.min.js
css: /assets/lib/bootstrap/bootstrap.min.css
js: /assets/lib/bootstrap/bootstrap.bundle.min.js
bootstrap-toc:
css: /assets/lib/bootstrap-toc-1.0.1/bootstrap-toc.min.css
js: /assets/lib/bootstrap-toc-1.0.1/bootstrap-toc.min.js
toc:
css: /assets/lib/tocbot/tocbot.min.css
js: /assets/lib/tocbot/tocbot.min.js
fontawesome:
css: /assets/lib/fontawesome-free-6.2.1/css/all.min.css
css: /assets/lib/fontawesome-free/css/all.min.css
search:
js: /assets/lib/simple-jekyll-search-1.10.0/simple-jekyll-search.min.js
js: /assets/lib/simple-jekyll-search/simple-jekyll-search.min.js
mermaid:
js: /assets/lib/mermaid-9.1.7/mermaid.min.js
js: /assets/lib/mermaid/mermaid.min.js
dayjs:
js:
common: /assets/lib/dayjs-1.10.7/dayjs.min.js
locale: /assets/lib/dayjs-1.10.7/locale/en.min.js
relativeTime: /assets/lib/dayjs-1.10.7/plugin/relativeTime.min.js
localizedFormat: /assets/lib/dayjs-1.10.7/plugin/localizedFormat.min.js
common: /assets/lib/dayjs/dayjs.min.js
locale: /assets/lib/dayjs/locale/en.min.js
relativeTime: /assets/lib/dayjs/plugin/relativeTime.min.js
localizedFormat: /assets/lib/dayjs/plugin/localizedFormat.min.js
countup:
js: /assets/lib/countup.js-1.9.3/countUp.min.js
js: /assets/lib/countup.js/countUp.min.js
magnific-popup:
css: /assets/lib/magnific-popup-1.1.0/magnific-popup.css
js: /assets/lib/magnific-popup-1.1.0/jquery.magnific-popup.min.js
css: /assets/lib/magnific-popup/magnific-popup.css
js: /assets/lib/magnific-popup/jquery.magnific-popup.min.js
lazysizes:
js: /assets/lib/lazysizes-5.3.2/lazysizes.min.js
js: /assets/lib/lazysizes/lazysizes.min.js
clipboard:
js: /assets/lib/clipboard-2.0.9/clipboard.min.js
js: /assets/lib/clipboard/clipboard.min.js
polyfill:
js: /assets/lib/polyfill-v3-es6/polyfill.min.js
mathjax:
js: /assets/lib/mathjax-3.2.0/tex-chtml.js
js: /assets/lib/mathjax/tex-chtml.js

View file

@ -1,30 +1,28 @@
# The contact options.
-
type: github
icon: 'fab fa-github'
-
type: twitter
icon: 'fab fa-twitter'
-
type: email
icon: 'fas fa-envelope'
noblank: true # open link in current tab
-
type: rss
icon: 'fas fa-rss'
noblank: true
- type: github
icon: "fab fa-github"
- type: twitter
icon: "fab fa-twitter"
- type: email
icon: "fas fa-envelope"
noblank: true # open link in current tab
- type: rss
icon: "fas fa-rss"
noblank: true
# Uncomment and complete the url below to enable more contact options
# -
# type: mastodon
#
# - type: mastodon
# icon: 'fab fa-mastodon' # icons powered by <https://fontawesome.com/>
# url: '' # Fill with your Mastodon account page, rel="me" will be applied for verification
# -
# type: linkedin
#
# - type: linkedin
# icon: 'fab fa-linkedin' # icons powered by <https://fontawesome.com/>
# url: '' # Fill with your Linkedin homepage
# -
# type: stack-overflow
#
# - type: stack-overflow
# icon: 'fab fa-stack-overflow'
# url: '' # Fill with your stackoverflow homepage

93
_data/locales/el-GR.yml Normal file
View file

@ -0,0 +1,93 @@
# The layout text of site
# ----- Commons label -----
layout:
post: Δημοσίευση
category: Κατηγορία
tag: Ετικέτα
# The tabs of sidebar
tabs:
# format: <filename_without_extension>: <value>
home: Home
categories: Κατηγορίες
tags: Ετικέτες
archives: Αρχεία
about: Σχετικά
# the text displayed in the search bar & search results
search:
hint: αναζήτηση
cancel: Ακύρωση
no_results: Oops! Κανένα αποτέλεσμα δεν βρέθηκε.
panel:
lastmod: Σχετικά ενημερωμένα
trending_tags: Ετικέτες τάσης
toc: Περιεχόμενα
copyright:
# Shown at the bottom of the post
license:
template: Η δημοσίευση αυτή βρίσκεται υπο την άδεια :LICENSE_NAME Greekforce1821.
name: CC BY 4.0
link: https://creativecommons.org/licenses/by/4.0/
# Displayed in the footer
brief: Ορισμένα δικαιώματα reserved.
verbose: >-
Εκτός αλλού ή οπουδήποτε αλλού, τα blog posts σε αυτήν την σελίδα βρίσκονται υπο την άδεια
Creative Commons Attribution 4.0 International (CC BY 4.0) του δημιουργού.
meta: Αξιοποιώντας την :PLATFORM theme :THEME.
not_found:
statment: Συγνώμη, έχουμε τοποθετήσει λάθος αυτήν την διεύθυνση URL ή υποδεικνύει κάτι που δεν υπάρχει.
notification:
update_found: Υπάρχει διαθέσιμη μια νέα έκδοση του περιεχομένου.
update: Ενημέρωση
# ----- Posts related labels -----
post:
written_by: Από
posted: Δημοσιεύθηκε
updated: Ενημερώθηκε
words: λέξεις
pageview_measure: προβολές
read_time:
unit: Λεπτά
prompt: διαβάσματος
relate_posts: Περισσότερα
share: Κοινοποιήστε
button:
next: Νεότερα
previous: Παλαιότερα
copy_code:
succeed: Αντιγράφθηκε!
share_link:
title: Αντιγραφή συνδέσμου
succeed: Η διεύθυνση αντιγράφθηκε με επιτυχία!
# pinned prompt of posts list on homepage
pin_prompt: Pinned
# Date time format.
# See: <http://strftime.net/>, <https://day.js.org/docs/en/display/format>
df:
post:
strftime: '%b %e, %Y'
dayjs: 'll'
archives:
strftime: '%b'
dayjs: 'MMM'
# categories page
categories:
category_measure:
singular: Κατηγορία
plural: Κατηγορίες
post_measure:
singular: Δημοσίευση
plural: Δημοσιεύσεις

View file

@ -2,26 +2,24 @@
# Icons from <https://fontawesome.com/>
platforms:
-
type: Twitter
- type: Twitter
icon: "fab fa-twitter"
link: "https://twitter.com/intent/tweet?text=TITLE&url=URL"
-
type: Facebook
- type: Facebook
icon: "fab fa-facebook-square"
link: "https://www.facebook.com/sharer/sharer.php?title=TITLE&u=URL"
-
type: Telegram
- type: Telegram
icon: "fab fa-telegram"
link: "https://t.me/share/url?url=URL&text=TITLE"
# Uncomment below if you need to.
# -
# type: Linkedin
#
# - type: Linkedin
# icon: "fab fa-linkedin"
# link: "https://www.linkedin.com/sharing/share-offsite/?url=URL"
#
# -
# type: Weibo
# - type: Weibo
# icon: "fab fa-weibo"
# link: "http://service.weibo.com/share/share.php?title=TITLE&url=URL"

View file

@ -1,13 +1,17 @@
<!--
The Head
-->
<!-- The Head -->
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="theme-color" media="(prefers-color-scheme: light)" content="#f7f7f7">
<meta name="theme-color" media="(prefers-color-scheme: dark)" content="#1b1b1e">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<meta
name="viewport"
content="width=device-width, user-scalable=no initial-scale=1, shrink-to-fit=no, viewport-fit=cover"
>
{% if page.layout == 'home' or page.layout == 'post' %}
{% if site.google_analytics.pv.proxy_endpoint %}
<meta name="pv-proxy-endpoint" content="{{ site.google_analytics.pv.proxy_endpoint }}">
{% endif %}
@ -15,7 +19,6 @@
{% if site.google_analytics.pv.cache_path %}
<meta name="pv-cache-path" content="{{ site.google_analytics.pv.cache_path | relative_url }}">
{% endif %}
{% endif %}
{% capture seo_tags %}
@ -40,16 +43,14 @@
{% endif %}
{% assign seo_tags = seo_tags | replace: target, replacement %}
{% endunless %}
{% endif %}
{{ seo_tags }}
<title>
{%- unless page.layout == "home" -%}
{{ page.title | append: " | "}}
{%- unless page.layout == 'home' -%}
{{ page.title | append: ' | ' }}
{%- endunless -%}
{{ site.title }}
</title>
@ -57,23 +58,19 @@
{% include favicons.html %}
{% if site.resources.ignore_env != jekyll.environment and site.resources.self_hosted %}
<link href="{{ site.data.assets[origin].webfonts | relative_url }}" rel="stylesheet">
{% else %}
{% for cdn in site.data.assets[origin].cdns %}
<link rel="preconnect" href="{{ cdn.url }}" {{ cdn.args }}>
<link rel="dns-prefetch" href="{{ cdn.url }}" {{ cdn.args }}>
{% endfor %}
<link rel="stylesheet" href="{{ site.data.assets[origin].webfonts | relative_url }}">
{% endif %}
<!-- GA -->
{% if jekyll.environment == 'production'
and site.google_analytics.id != empty and site.google_analytics.id %}
{% if jekyll.environment == 'production' and site.google_analytics.id != empty and site.google_analytics.id %}
<link rel="preconnect" href="https://www.google-analytics.com" crossorigin="use-credentials">
<link rel="dns-prefetch" href="https://www.google-analytics.com">
@ -82,7 +79,11 @@
{% if site.google_analytics.pv.proxy_endpoint %}
{% assign proxy_url = site.google_analytics.pv.proxy_endpoint
| replace: "https://", "" | split: "/" | first | prepend: "https://" %}
| replace: 'https://', ''
| split: '/'
| first
| prepend: 'https://'
%}
<link rel="preconnect" href="{{ proxy_url }}" crossorigin="use-credentials">
<link rel="dns-prefetch" href="{{ proxy_url }}">
{% endif %}
@ -97,7 +98,7 @@
<link rel="stylesheet" href="{{ '/assets/css/style.css' | relative_url }}">
{% if site.toc and page.toc %}
<link rel="stylesheet" href="{{ site.data.assets[origin].bootstrap-toc.css | relative_url }}">
<link rel="stylesheet" href="{{ site.data.assets[origin].toc.css | relative_url }}">
{% endif %}
{% if page.layout == 'page' or page.layout == 'post' %}

View file

@ -1,6 +1,4 @@
<!--
JS selector for site.
-->
<!-- JS selector for site. -->
<!-- layout specified -->
@ -8,15 +6,16 @@
{% if site.google_analytics.pv.proxy_endpoint or site.google_analytics.pv.cache_path %}
<!-- pv-report needs countup.js -->
<script async src="{{ site.data.assets[origin].countup.js | relative_url }}"></script>
<script defer src="{{ '/assets/js/dist/pvreport.min.js' | relative_url }}"></script>
{% endif %}
{% endif %}
{% if page.layout == 'post' or page.layout == 'page' %}
<!-- image lazy-loading & popup & clipboard -->
{% assign _urls = site.data.assets[origin].magnific-popup.js
| append: ',' | append: site.data.assets[origin].lazysizes.js
| append: ',' | append: site.data.assets[origin].clipboard.js
{% assign _urls = site.data.assets[origin]['magnific-popup'].js
| append: ','
| append: site.data.assets[origin].lazysizes.js
| append: ','
| append: site.data.assets[origin].clipboard.js
%}
{% include jsdelivr-combine.html urls=_urls %}
@ -26,33 +25,31 @@
or page.layout == 'post'
or page.layout == 'archives'
or page.layout == 'category'
or page.layout == 'tag' %}
or page.layout == 'tag'
%}
{% assign locale = site.lang | split: '-' | first %}
{% assign _urls = site.data.assets[origin].dayjs.js.common
| append: ',' | append: site.data.assets[origin].dayjs.js.locale
| append: ','
| append: site.data.assets[origin].dayjs.js.locale
| replace: ':LOCALE', locale
| append: ',' | append: site.data.assets[origin].dayjs.js.relativeTime
| append: ',' | append: site.data.assets[origin].dayjs.js.localizedFormat
| append: ','
| append: site.data.assets[origin].dayjs.js.relativeTime
| append: ','
| append: site.data.assets[origin].dayjs.js.localizedFormat
%}
{% include jsdelivr-combine.html urls=_urls %}
{% endif %}
{% if page.layout == 'home'
or page.layout == 'categories'
or page.layout == 'post'
or page.layout == 'page' %}
{% case page.layout %}
{% when 'categories', 'post', 'page' %}
{% assign type = page.layout %}
{% elsif page.layout == 'archives'
or page.layout == 'category'
or page.layout == 'tag' %}
{% assign type = "misc" %}
{% when 'home', 'archives', 'category', 'tag' %}
{% assign type = 'misc' %}
{% else %}
{% assign type = "commons" %}
{% endif %}
{% assign type = 'commons' %}
{% endcase %}
{% capture script %}/assets/js/dist/{{ type }}.min.js{% endcapture %}
<script defer src="{{ script | relative_url }}"></script>
@ -63,11 +60,13 @@
/* see: <https://docs.mathjax.org/en/latest/options/input/tex.html#tex-options> */
MathJax = {
tex: {
inlineMath: [ /* start/end delimiter pairs for in-line math */
/* start/end delimiter pairs for in-line math */
inlineMath: [
['$', '$'],
['\\(', '\\)']
],
displayMath: [ /* start/end delimiter pairs for display math */
/* start/end delimiter pairs for display math */
displayMath: [
['$$', '$$'],
['\\[', '\\]']
]
@ -75,8 +74,7 @@
};
</script>
<script src="{{ site.data.assets[origin].polyfill.js | relative_url }}"></script>
<script id="MathJax-script" async src="{{ site.data.assets[origin].mathjax.js | relative_url }}">
</script>
<script id="MathJax-script" async src="{{ site.data.assets[origin].mathjax.js | relative_url }}"></script>
{% endif %}
<!-- commons -->
@ -95,5 +93,4 @@
{% if site.google_analytics.id != empty and site.google_analytics.id %}
{% include google-analytics.html %}
{% endif %}
{% endif %}

View file

@ -26,13 +26,12 @@
let self = this;
/* always follow the system prefers */
this.sysDarkPrefers.addEventListener("change", () => {
this.sysDarkPrefers.addEventListener('change', () => {
if (self.hasMode) {
if (self.isDarkMode) {
if (!self.isSysDarkPrefer) {
self.setDark();
}
} else {
if (self.isSysDarkPrefer) {
self.setLight();
@ -43,9 +42,7 @@
}
self.notify();
});
} /* constructor() */
get sysDarkPrefers() { return window.matchMedia("(prefers-color-scheme: dark)"); }
@ -62,8 +59,7 @@
/* get the current mode on screen */
get modeStatus() {
if (this.isDarkMode
|| (!this.hasMode && this.isSysDarkPrefer)) {
if (this.isDarkMode || (!this.hasMode && this.isSysDarkPrefer)) {
return ModeToggle.DARK_MODE;
} else {
return ModeToggle.LIGHT_MODE;
@ -93,37 +89,32 @@
}, "*");
}
flipMode() {
if (this.hasMode) {
if (this.isSysDarkPrefer) {
if (this.isLightMode) {
this.clearMode();
} else {
this.setLight();
}
} else {
if (this.isDarkMode) {
this.clearMode();
} else {
this.setDark();
}
}
} else {
if (this.isSysDarkPrefer) {
this.setLight();
} else {
this.setDark();
}
}
this.notify();
} /* flipMode() */
} /* ModeToggle */
const toggle = new ModeToggle();
function flipMode() {
if (toggle.hasMode) {
if (toggle.isSysDarkPrefer) {
if (toggle.isLightMode) {
toggle.clearMode();
} else {
toggle.setLight();
}
} else {
if (toggle.isDarkMode) {
toggle.clearMode();
} else {
toggle.setDark();
}
}
} else {
if (toggle.isSysDarkPrefer) {
toggle.setLight();
} else {
toggle.setDark();
}
}
toggle.notify();
} /* flipMode() */
const modeToggle = new ModeToggle();
</script>

View file

@ -64,34 +64,37 @@
{% endunless %}
{% for entry in site.data.contact %}
{% capture url %}
{%- if entry.type == 'github' -%}
https://github.com/{{ site.github.username }}
{%- elsif entry.type == 'twitter' -%}
https://twitter.com/{{ site.twitter.username }}
{%- elsif entry.type == 'email' -%}
{% case entry.type %}
{% when 'github', 'twitter' %}
{%- capture url -%}
https://{{ entry.type }}.com/{{ site[entry.type].username }}
{%- endcapture -%}
{% when 'email' %}
{% assign email = site.social.email | split: '@' %}
{%- capture url -%}
javascript:location.href = 'mailto:' + ['{{ email[0] }}','{{ email[1] }}'].join('@')
{%- elsif entry.type == 'rss' -%}
{{ "/feed.xml" | relative_url }}
{%- else -%}
{{ entry.url }}
{%- endif -%}
{% endcapture %}
{%- endcapture -%}
{% when 'rss' %}
{% assign url = '/feed.xml' | relative_url %}
{% else %}
{% assign url = entry.url %}
{% endcase %}
{% if url %}
<a href="{{ url }}" aria-label="{{ entry.type }}"
{% assign link_types = nil %}
{% assign link_types = '' %}
{% unless entry.noblank %}
{% assign link_types = link_types | append: " noopener" %}
target="_blank"
{% assign link_types = 'noopener noreferrer' %}
{% endunless %}
{% if entry.type == 'mastodon' %}
{% assign link_types = link_types | append: " me" %}
{% assign link_types = link_types | append: ' me' | strip %}
{% endif %}
{% if link_types %}rel="{{ link_types | lstrip }}"{% endif %}>
{% unless link_types == empty %}rel="{{ link_types }}"{% endunless %}>
<i class="{{ entry.icon }}"></i>
</a>
{% endif %}

View file

@ -6,11 +6,11 @@
{% endif %}
{% if enable_toc %}
<!-- BS-toc.js will be loaded at medium priority -->
<script src="{{ site.data.assets[origin].bootstrap-toc.js | relative_url }}"></script>
<div id="toc-wrapper" class="pl-0 pr-4 mb-5">
<div class="panel-heading pl-3 pt-2 mb-2">{{- site.data.locales[site.lang].panel.toc -}}</div>
<nav id="toc" data-toggle="toc"></nav>
<nav id="toc"></nav>
</div>
<!-- toc.js will be loaded at medium priority -->
<script src="{{ site.data.assets[origin].toc.js | relative_url }}"></script>
{% endif %}

3
_javascript/_copyright Normal file
View file

@ -0,0 +1,3 @@
Chirpy v<%= pkg.version %> (<%= pkg.homepage %>)
© 2019 <%= pkg.author %>
<%= pkg.license %> Licensed

View file

@ -0,0 +1,7 @@
import { basic, initSidebar, initTopbar } from './modules/layouts';
import { categoryCollapse } from './modules/plugins';
basic();
initSidebar();
initTopbar();
categoryCollapse();

5
_javascript/commons.js Normal file
View file

@ -0,0 +1,5 @@
import { basic, initSidebar, initTopbar } from './modules/layouts';
basic();
initSidebar();
initTopbar();

View file

@ -1,20 +0,0 @@
/**
* Reference: https://bootsnipp.com/snippets/featured/link-to-top-page
*/
$(function() {
$(window).scroll(() => {
if ($(this).scrollTop() > 50 &&
$("#sidebar-trigger").css("display") === "none") {
$("#back-to-top").fadeIn();
} else {
$("#back-to-top").fadeOut();
}
});
$("#back-to-top").click(() => {
$("body,html").animate({
scrollTop: 0
}, 800);
return false;
});
});

View file

@ -1,13 +0,0 @@
/**
* Listener for theme mode toggle
*/
$(function () {
$(".mode-toggle").click((e) => {
const $target = $(e.target);
let $btn = ($target.prop("tagName") === "button".toUpperCase() ?
$target : $target.parent());
$btn.blur(); // remove the clicking outline
flipMode();
});
});

View file

@ -1,38 +0,0 @@
/**
* A tool for smooth scrolling and topbar switcher
*/
const ScrollHelper = (function () {
const $body = $("body");
const ATTR_TOPBAR_VISIBLE = "data-topbar-visible";
const topbarHeight = $("#topbar-wrapper").outerHeight();
let scrollUpCount = 0; // the number of times the scroll up was triggered by ToC or anchor
let topbarLocked = false;
let orientationLocked = false;
return {
hideTopbar: () => $body.attr(ATTR_TOPBAR_VISIBLE, false),
showTopbar: () => $body.attr(ATTR_TOPBAR_VISIBLE, true),
// scroll up
addScrollUpTask: () => {
scrollUpCount += 1;
if (!topbarLocked) {
topbarLocked = true;
}
},
popScrollUpTask: () => scrollUpCount -= 1,
hasScrollUpTask: () => scrollUpCount > 0,
topbarLocked: () => topbarLocked === true,
unlockTopbar: () => topbarLocked = false,
getTopbarHeight: () => topbarHeight,
// orientation change
orientationLocked: () => orientationLocked === true,
lockOrientation: () => orientationLocked = true,
unLockOrientation: () => orientationLocked = false
};
}());

View file

@ -1,129 +0,0 @@
/**
* This script make #search-result-wrapper switch to unloaded or shown automatically.
*/
$(function () {
const btnSbTrigger = $("#sidebar-trigger");
const btnSearchTrigger = $("#search-trigger");
const btnCancel = $("#search-cancel");
const main = $("#main");
const topbarTitle = $("#topbar-title");
const searchWrapper = $("#search-wrapper");
const resultWrapper = $("#search-result-wrapper");
const results = $("#search-results");
const input = $("#search-input");
const hints = $("#search-hints");
const scrollBlocker = (function () {
let offset = 0;
return {
block() {
offset = window.scrollY;
$("html,body").scrollTop(0);
},
release() {
$("html,body").scrollTop(offset);
},
getOffset() {
return offset;
}
};
}());
/*--- Actions in mobile screens (Sidebar hidden) ---*/
const mobileSearchBar = (function () {
return {
on() {
btnSbTrigger.addClass("unloaded");
topbarTitle.addClass("unloaded");
btnSearchTrigger.addClass("unloaded");
searchWrapper.addClass("d-flex");
btnCancel.addClass("loaded");
},
off() {
btnCancel.removeClass("loaded");
searchWrapper.removeClass("d-flex");
btnSbTrigger.removeClass("unloaded");
topbarTitle.removeClass("unloaded");
btnSearchTrigger.removeClass("unloaded");
}
};
}());
const resultSwitch = (function () {
let visible = false;
return {
on() {
if (!visible) {
// the block method must be called before $(#main) unloaded.
scrollBlocker.block();
resultWrapper.removeClass("unloaded");
main.addClass("unloaded");
visible = true;
}
},
off() {
if (visible) {
results.empty();
if (hints.hasClass("unloaded")) {
hints.removeClass("unloaded");
}
resultWrapper.addClass("unloaded");
main.removeClass("unloaded");
// now the release method must be called after $(#main) display
scrollBlocker.release();
input.val("");
visible = false;
}
},
isVisible() {
return visible;
}
};
}());
function isMobileView() {
return btnCancel.hasClass("loaded");
}
btnSearchTrigger.click(function () {
mobileSearchBar.on();
resultSwitch.on();
input.focus();
});
btnCancel.click(function () {
mobileSearchBar.off();
resultSwitch.off();
});
input.focus(function () {
searchWrapper.addClass("input-focus");
});
input.focusout(function () {
searchWrapper.removeClass("input-focus");
});
input.on("input", () => {
if (input.val() === "") {
if (isMobileView()) {
hints.removeClass("unloaded");
} else {
resultSwitch.off();
}
} else {
resultSwitch.on();
if (isMobileView()) {
hints.addClass("unloaded");
}
}
});
});

View file

@ -1,28 +0,0 @@
/**
* Expand or close the sidebar in mobile screens.
*/
$(function () {
const sidebarUtil = (function () {
const ATTR_DISPLAY = "sidebar-display";
let isExpanded = false;
const body = $("body");
return {
toggle() {
if (isExpanded === false) {
body.attr(ATTR_DISPLAY, "");
} else {
body.removeAttr(ATTR_DISPLAY);
}
isExpanded = !isExpanded;
}
};
}());
$("#sidebar-trigger").click(sidebarUtil.toggle);
$("#mask").click(sidebarUtil.toggle);
});

View file

@ -1,6 +0,0 @@
/**
* Initial Bootstrap Tooltip.
*/
$(function () {
$("[data-toggle=\"tooltip\"]").tooltip();
});

View file

@ -1,89 +0,0 @@
/**
* Hide Header on scroll down
*/
$(function () {
const $searchInput = $("#search-input");
const delta = ScrollHelper.getTopbarHeight();
let didScroll;
let lastScrollTop = 0;
function hasScrolled() {
let st = $(this).scrollTop();
/* Make sure they scroll more than delta */
if (Math.abs(lastScrollTop - st) <= delta) {
return;
}
if (st > lastScrollTop) { // Scroll Down
ScrollHelper.hideTopbar();
if ($searchInput.is(":focus")) {
$searchInput.blur(); /* remove focus */
}
} else { // Scroll up
// has not yet scrolled to the bottom of the screen, that is, there is still space for scrolling
if (st + $(window).height() < $(document).height()) {
if (ScrollHelper.hasScrollUpTask()) {
return;
}
if (ScrollHelper.topbarLocked()) { // avoid redundant scroll up event from smooth scrolling
ScrollHelper.unlockTopbar();
} else {
if (ScrollHelper.orientationLocked()) { // avoid device auto scroll up on orientation change
ScrollHelper.unLockOrientation();
} else {
ScrollHelper.showTopbar();
}
}
}
}
lastScrollTop = st;
} // hasScrolled()
function handleLandscape() {
if ($(window).scrollTop() === 0) {
return;
}
ScrollHelper.lockOrientation();
ScrollHelper.hideTopbar();
}
if (screen.orientation) {
screen.orientation.onchange = () => {
const type = screen.orientation.type;
if (type === "landscape-primary" || type === "landscape-secondary") {
handleLandscape();
}
};
} else {
// for the browsers that not support `window.screen.orientation` API
$(window).on("orientationchange", () => {
if ($(window).width() < $(window).height()) { // before rotating, it is still in portrait mode.
handleLandscape();
}
});
}
$(window).scroll(() => {
if (didScroll) {
return;
}
didScroll = true;
});
setInterval(() => {
if (didScroll) {
hasScrolled();
didScroll = false;
}
}, 250);
});

View file

@ -1,67 +0,0 @@
/**
* Top bar title auto change while scrolling up/down in mobile screens.
*/
$(function () {
const titleSelector = "div.post>h1:first-of-type";
const $pageTitle = $(titleSelector);
const $topbarTitle = $("#topbar-title");
if ($pageTitle.length === 0 /* on Home page */
|| $pageTitle.hasClass("dynamic-title")
|| $topbarTitle.is(":hidden")) {/* not in mobile views */
return;
}
const defaultTitleText = $topbarTitle.text().trim();
let pageTitleText = $pageTitle.text().trim();
let hasScrolled = false;
let lastScrollTop = 0;
if ($("#page-category").length || $("#page-tag").length) {
/* The title in Category or Tag page will be "<title> <count_of_posts>" */
if (/\s/.test(pageTitleText)) {
pageTitleText = pageTitleText.replace(/[0-9]/g, "").trim();
}
}
// When the page is scrolled down and then refreshed, the topbar title needs to be initialized
if ($pageTitle.offset().top < $(window).scrollTop()) {
$topbarTitle.text(pageTitleText);
}
let options = {
rootMargin: '-48px 0px 0px 0px', // 48px equals to the topbar height (3rem)
threshold: [0, 1]
};
let observer = new IntersectionObserver((entries) => {
if (!hasScrolled) {
hasScrolled = true;
return;
}
let curScrollTop = $(window).scrollTop();
let isScrollDown = lastScrollTop < curScrollTop;
lastScrollTop = curScrollTop;
let heading = entries[0];
if (isScrollDown) {
if (heading.intersectionRatio === 0) {
$topbarTitle.text(pageTitleText);
}
} else {
if (heading.intersectionRatio === 1) {
$topbarTitle.text(defaultTitleText);
}
}
}, options);
observer.observe(document.querySelector(titleSelector));
/* Click title will scroll to top */
$topbarTitle.click(function () {
$("body,html").animate({scrollTop: 0}, 800);
});
});

View file

@ -1,5 +0,0 @@
/*!
* Chirpy v5.5.2 (https://github.com/cotes2020/jekyll-theme-chirpy/)
* © 2019 Cotes Chung
* MIT Licensed
*/

7
_javascript/misc.js Normal file
View file

@ -0,0 +1,7 @@
import { basic, initSidebar, initTopbar } from './modules/layouts';
import { initLocaleDatetime } from './modules/plugins';
basic();
initSidebar();
initTopbar();
initLocaleDatetime();

View file

@ -0,0 +1,26 @@
/**
* Reference: https://bootsnipp.com/snippets/featured/link-to-top-page
*/
export function back2top() {
$(window).on('scroll', () => {
if (
$(window).scrollTop() > 50 &&
$('#sidebar-trigger').css('display') === 'none'
) {
$('#back-to-top').fadeIn();
} else {
$('#back-to-top').fadeOut();
}
});
$('#back-to-top').on('click', () => {
$('body,html').animate(
{
scrollTop: 0
},
800
);
return false;
});
}

View file

@ -0,0 +1,36 @@
/**
* Tab 'Categories' expand/close effect.
*/
const childPrefix = 'l_';
const parentPrefix = 'h_';
const collapse = $('.collapse');
export function categoryCollapse() {
/* close up top-category */
collapse.on('hide.bs.collapse', function () {
/* Bootstrap collapse events. */ const parentId =
parentPrefix + $(this).attr('id').substring(childPrefix.length);
if (parentId) {
$(`#${parentId} .far.fa-folder-open`).attr(
'class',
'far fa-folder fa-fw'
);
$(`#${parentId} i.fas`).addClass('rotate');
$(`#${parentId}`).removeClass('hide-border-bottom');
}
});
/* expand the top category */
collapse.on('show.bs.collapse', function () {
const parentId =
parentPrefix + $(this).attr('id').substring(childPrefix.length);
if (parentId) {
$(`#${parentId} .far.fa-folder`).attr(
'class',
'far fa-folder-open fa-fw'
);
$(`#${parentId} i.fas`).removeClass('rotate');
$(`#${parentId}`).addClass('hide-border-bottom');
}
});
}

View file

@ -0,0 +1,118 @@
/**
* Clipboard functions
*
* Dependencies:
* - popper.js (https://github.com/popperjs/popper-core)
* - clipboard.js (https://github.com/zenorocha/clipboard.js)
*/
const btnSelector = '.code-header>button';
const ICON_SUCCESS = 'fas fa-check';
const ATTR_TIMEOUT = 'timeout';
const ATTR_TITLE_SUCCEED = 'data-title-succeed';
const ATTR_TITLE_ORIGIN = 'data-original-title';
const TIMEOUT = 2000; // in milliseconds
function isLocked(node) {
if ($(node)[0].hasAttribute(ATTR_TIMEOUT)) {
let timeout = $(node).attr(ATTR_TIMEOUT);
if (Number(timeout) > Date.now()) {
return true;
}
}
return false;
}
function lock(node) {
$(node).attr(ATTR_TIMEOUT, Date.now() + TIMEOUT);
}
function unlock(node) {
$(node).removeAttr(ATTR_TIMEOUT);
}
function getIcon(btn) {
let iconNode = $(btn).children();
return iconNode.attr('class');
}
const ICON_DEFAULT = getIcon(btnSelector);
function showTooltip(btn) {
const succeedTitle = $(btn).attr(ATTR_TITLE_SUCCEED);
$(btn).attr(ATTR_TITLE_ORIGIN, succeedTitle).tooltip('show');
}
function hideTooltip(btn) {
$(btn).tooltip('hide').removeAttr(ATTR_TITLE_ORIGIN);
}
function setSuccessIcon(btn) {
let btnNode = $(btn);
let iconNode = btnNode.children();
iconNode.attr('class', ICON_SUCCESS);
}
function resumeIcon(btn) {
let btnNode = $(btn);
let iconNode = btnNode.children();
iconNode.attr('class', ICON_DEFAULT);
}
export function initClipboard() {
// Initial the clipboard.js object
const clipboard = new ClipboardJS(btnSelector, {
target(trigger) {
let codeBlock = trigger.parentNode.nextElementSibling;
return codeBlock.querySelector('code .rouge-code');
}
});
$(btnSelector).tooltip({
trigger: 'hover',
placement: 'left'
});
clipboard.on('success', (e) => {
e.clearSelection();
const trigger = e.trigger;
if (isLocked(trigger)) {
return;
}
setSuccessIcon(trigger);
showTooltip(trigger);
lock(trigger);
setTimeout(() => {
hideTooltip(trigger);
resumeIcon(trigger);
unlock(trigger);
}, TIMEOUT);
});
/* --- Post link sharing --- */
$('#copy-link').on('click', (e) => {
let target = $(e.target);
if (isLocked(target)) {
return;
}
// Copy URL to clipboard
navigator.clipboard.writeText(window.location.href).then(() => {
const defaultTitle = target.attr(ATTR_TITLE_ORIGIN);
const succeedTitle = target.attr(ATTR_TITLE_SUCCEED);
// Switch tooltip title
target.attr(ATTR_TITLE_ORIGIN, succeedTitle).tooltip('show');
lock(target);
setTimeout(() => {
target.attr(ATTR_TITLE_ORIGIN, defaultTitle);
unlock(target);
}, TIMEOUT);
});
});
}

View file

@ -0,0 +1,68 @@
/**
* Top bar title auto change while scrolling up/down in mobile screens.
*/
const titleSelector = 'div.post>h1:first-of-type';
const $pageTitle = $(titleSelector);
const $topbarTitle = $('#topbar-title');
const defaultTitleText = $topbarTitle.text().trim();
export function convertTitle() {
if (
$pageTitle.length === 0 /* on Home page */ ||
$pageTitle.hasClass('dynamic-title') ||
$topbarTitle.is(':hidden')
) {
/* not in mobile views */
return;
}
let pageTitleText = $pageTitle.text().trim();
let hasScrolled = false;
let lastScrollTop = 0;
if ($('#page-category').length || $('#page-tag').length) {
/* The title in Category or Tag page will be "<title> <count_of_posts>" */
if (/\s/.test(pageTitleText)) {
pageTitleText = pageTitleText.replace(/[0-9]/g, '').trim();
}
}
// When the page is scrolled down and then refreshed, the topbar title needs to be initialized
if ($pageTitle.offset().top < $(window).scrollTop()) {
$topbarTitle.text(pageTitleText);
}
let options = {
rootMargin: '-48px 0px 0px 0px', // 48px equals to the topbar height (3rem)
threshold: [0, 1]
};
let observer = new IntersectionObserver((entries) => {
if (!hasScrolled) {
hasScrolled = true;
return;
}
let curScrollTop = $(window).scrollTop();
let isScrollDown = lastScrollTop < curScrollTop;
lastScrollTop = curScrollTop;
let heading = entries[0];
if (isScrollDown) {
if (heading.intersectionRatio === 0) {
$topbarTitle.text(pageTitleText);
}
} else {
if (heading.intersectionRatio === 1) {
$topbarTitle.text(defaultTitleText);
}
}
}, options);
observer.observe(document.querySelector(titleSelector));
/* Click title will scroll to top */
$topbarTitle.on('click', function () {
$('body,html').animate({ scrollTop: 0 }, 800);
});
}

View file

@ -0,0 +1,27 @@
/**
* Set up image stuff
*/
export function imgExtra() {
if ($('#core-wrapper img[data-src]') <= 0) {
return;
}
/* See: <https://github.com/dimsemenov/Magnific-Popup> */
$('.popup').magnificPopup({
type: 'image',
closeOnContentClick: true,
showCloseBtn: false,
zoom: {
enabled: true,
duration: 300,
easing: 'ease-in-out'
}
});
/* Stop shimmer when image loaded */
document.addEventListener('lazyloaded', function (e) {
const $img = $(e.target);
$img.parent().removeClass('shimmer');
});
}

View file

@ -0,0 +1,50 @@
/**
* Update month/day to locale datetime
*
* Requirement: <https://github.com/iamkun/dayjs>
*/
/* A tool for locale datetime */
class LocaleHelper {
static get attrTimestamp() {
return 'data-ts';
}
static get attrDateFormat() {
return 'data-df';
}
static get locale() {
return $('html').attr('lang').substring(0, 2);
}
static getTimestamp(elem) {
return Number(elem.attr(LocaleHelper.attrTimestamp)); // unix timestamp
}
static getDateFormat(elem) {
return elem.attr(LocaleHelper.attrDateFormat);
}
}
export function initLocaleDatetime() {
dayjs.locale(LocaleHelper.locale);
dayjs.extend(window.dayjs_plugin_localizedFormat);
$(`[${LocaleHelper.attrTimestamp}]`).each(function () {
const date = dayjs.unix(LocaleHelper.getTimestamp($(this)));
const text = date.format(LocaleHelper.getDateFormat($(this)));
$(this).text(text);
$(this).removeAttr(LocaleHelper.attrTimestamp);
$(this).removeAttr(LocaleHelper.attrDateFormat);
// setup tooltips
const tooltip = $(this).attr('data-toggle');
if (typeof tooltip === 'undefined' || tooltip !== 'tooltip') {
return;
}
const tooltipText = date.format('llll'); // see: https://day.js.org/docs/en/display/format#list-of-localized-formats
$(this).attr('data-original-title', tooltipText);
});
}

View file

@ -0,0 +1,21 @@
/**
* Add listener for theme mode toggle
*/
const $toggleElem = $('.mode-toggle');
export function modeWatcher() {
if ($toggleElem.length === 0) {
return;
}
$toggleElem.off().on('click', (e) => {
const $target = $(e.target);
let $btn =
$target.prop('tagName') === 'button'.toUpperCase()
? $target
: $target.parent();
modeToggle.flipMode(); // modeToggle: `_includes/mode-toggle.html`
$btn.trigger('blur'); // remove the clicking outline
});
}

View file

@ -0,0 +1,254 @@
/**
* Count page views form GA or local cache file.
*
* Dependencies:
* - jQuery
* - countUp.js <https://github.com/inorganik/countUp.js>
*/
const getInitStatus = (function () {
let hasInit = false;
return () => {
let ret = hasInit;
if (!hasInit) {
hasInit = true;
}
return ret;
};
})();
const PvOpts = (function () {
function getContent(selector) {
return $(selector).attr('content');
}
function hasContent(selector) {
let content = getContent(selector);
return typeof content !== 'undefined' && content !== false;
}
return {
getProxyMeta() {
return getContent('meta[name=pv-proxy-endpoint]');
},
getLocalMeta() {
return getContent('meta[name=pv-cache-path]');
},
hasProxyMeta() {
return hasContent('meta[name=pv-proxy-endpoint]');
},
hasLocalMeta() {
return hasContent('meta[name=pv-cache-path]');
}
};
})();
const PvStorage = (function () {
const Keys = {
KEY_PV: 'pv',
KEY_PV_SRC: 'pv_src',
KEY_CREATION: 'pv_created_date'
};
const Source = {
LOCAL: 'same-origin',
PROXY: 'cors'
};
function get(key) {
return localStorage.getItem(key);
}
function set(key, val) {
localStorage.setItem(key, val);
}
function saveCache(pv, src) {
set(Keys.KEY_PV, pv);
set(Keys.KEY_PV_SRC, src);
set(Keys.KEY_CREATION, new Date().toJSON());
}
return {
keysCount() {
return Object.keys(Keys).length;
},
hasCache() {
return localStorage.getItem(Keys.KEY_PV) !== null;
},
getCache() {
return JSON.parse(localStorage.getItem(Keys.KEY_PV));
},
saveLocalCache(pv) {
saveCache(pv, Source.LOCAL);
},
saveProxyCache(pv) {
saveCache(pv, Source.PROXY);
},
isExpired() {
let date = new Date(get(Keys.KEY_CREATION));
date.setHours(date.getHours() + 1); // per hour
return Date.now() >= date.getTime();
},
isFromLocal() {
return get(Keys.KEY_PV_SRC) === Source.LOCAL;
},
isFromProxy() {
return get(Keys.KEY_PV_SRC) === Source.PROXY;
},
newerThan(pv) {
return (
PvStorage.getCache().totalsForAllResults['ga:pageviews'] >
pv.totalsForAllResults['ga:pageviews']
);
},
inspectKeys() {
if (localStorage.length !== PvStorage.keysCount()) {
localStorage.clear();
return;
}
for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i);
switch (key) {
case Keys.KEY_PV:
case Keys.KEY_PV_SRC:
case Keys.KEY_CREATION:
break;
default:
localStorage.clear();
return;
}
}
}
};
})(); /* PvStorage */
function countUp(min, max, destId) {
if (min < max) {
let numAnim = new CountUp(destId, min, max);
if (!numAnim.error) {
numAnim.start();
} else {
console.error(numAnim.error);
}
}
}
function countPV(path, rows) {
let count = 0;
if (typeof rows !== 'undefined') {
for (let i = 0; i < rows.length; ++i) {
const gaPath = rows[parseInt(i, 10)][0];
if (gaPath === path) {
/* path format see: site.permalink */
count += parseInt(rows[parseInt(i, 10)][1], 10);
break;
}
}
}
return count;
}
function tacklePV(rows, path, elem, hasInit) {
let count = countPV(path, rows);
count = count === 0 ? 1 : count;
if (!hasInit) {
elem.text(new Intl.NumberFormat().format(count));
} else {
const initCount = parseInt(elem.text().replace(/,/g, ''), 10);
if (count > initCount) {
countUp(initCount, count, elem.attr('id'));
}
}
}
function displayPageviews(data) {
if (typeof data === 'undefined') {
return;
}
let hasInit = getInitStatus();
const rows = data.rows; /* could be undefined */
if ($('#post-list').length > 0) {
/* the Home page */
$('.post-preview').each(function () {
const path = $(this).find('a').attr('href');
tacklePV(rows, path, $(this).find('.pageviews'), hasInit);
});
} else if ($('.post').length > 0) {
/* the post */
const path = window.location.pathname;
tacklePV(rows, path, $('#pv'), hasInit);
}
}
function fetchProxyPageviews() {
if (PvOpts.hasProxyMeta()) {
$.ajax({
type: 'GET',
url: PvOpts.getProxyMeta(),
dataType: 'jsonp',
jsonpCallback: 'displayPageviews',
success: (data) => {
PvStorage.saveProxyCache(JSON.stringify(data));
},
error: (jqXHR, textStatus, errorThrown) => {
console.log(
'Failed to load pageviews from proxy server: ' + errorThrown
);
}
});
}
}
function fetchLocalPageviews(hasCache = false) {
return fetch(PvOpts.getLocalMeta())
.then((response) => response.json())
.then((data) => {
if (hasCache) {
// The cache from the proxy will sometimes be more recent than the local one
if (PvStorage.isFromProxy() && PvStorage.newerThan(data)) {
return;
}
}
displayPageviews(data);
PvStorage.saveLocalCache(JSON.stringify(data));
});
}
export function initPageviews() {
if ($('.pageviews').length <= 0) {
return;
}
PvStorage.inspectKeys();
if (PvStorage.hasCache()) {
displayPageviews(PvStorage.getCache());
if (PvStorage.isExpired()) {
if (PvOpts.hasLocalMeta()) {
fetchLocalPageviews(true).then(fetchProxyPageviews);
} else {
fetchProxyPageviews();
}
} else {
if (PvStorage.isFromLocal()) {
fetchProxyPageviews();
}
}
} else {
// no cached
if (PvOpts.hasLocalMeta()) {
fetchLocalPageviews().then(fetchProxyPageviews);
} else {
fetchProxyPageviews();
}
}
}

View file

@ -0,0 +1,122 @@
/**
* This script make #search-result-wrapper switch to unloaded or shown automatically.
*/
const $btnSbTrigger = $('#sidebar-trigger');
const $btnSearchTrigger = $('#search-trigger');
const $btnCancel = $('#search-cancel');
const $main = $('#main');
const $topbarTitle = $('#topbar-title');
const $searchWrapper = $('#search-wrapper');
const $resultWrapper = $('#search-result-wrapper');
const $results = $('#search-results');
const $input = $('#search-input');
const $hints = $('#search-hints');
const $viewport = $('html,body');
// class names
const C_LOADED = 'loaded';
const C_UNLOADED = 'unloaded';
const C_FOCUS = 'input-focus';
const C_FLEX = 'd-flex';
class ScrollBlocker {
static offset = 0;
static resultVisible = false;
static on() {
ScrollBlocker.offset = window.scrollY;
$viewport.scrollTop(0);
}
static off() {
$viewport.scrollTop(ScrollBlocker.offset);
}
}
/*--- Actions in mobile screens (Sidebar hidden) ---*/
class MobileSearchBar {
static on() {
$btnSbTrigger.addClass(C_UNLOADED);
$topbarTitle.addClass(C_UNLOADED);
$btnSearchTrigger.addClass(C_UNLOADED);
$searchWrapper.addClass(C_FLEX);
$btnCancel.addClass(C_LOADED);
}
static off() {
$btnCancel.removeClass(C_LOADED);
$searchWrapper.removeClass(C_FLEX);
$btnSbTrigger.removeClass(C_UNLOADED);
$topbarTitle.removeClass(C_UNLOADED);
$btnSearchTrigger.removeClass(C_UNLOADED);
}
}
class ResultSwitch {
static on() {
if (!ScrollBlocker.resultVisible) {
// the block method must be called before $(#main) unloaded.
ScrollBlocker.on();
$resultWrapper.removeClass(C_UNLOADED);
$main.addClass(C_UNLOADED);
ScrollBlocker.resultVisible = true;
}
}
static off() {
if (ScrollBlocker.resultVisible) {
$results.empty();
if ($hints.hasClass(C_UNLOADED)) {
$hints.removeClass(C_UNLOADED);
}
$resultWrapper.addClass(C_UNLOADED);
$main.removeClass(C_UNLOADED);
// now the release method must be called after $(#main) display
ScrollBlocker.off();
$input.val('');
ScrollBlocker.resultVisible = false;
}
}
}
function isMobileView() {
return $btnCancel.hasClass(C_LOADED);
}
export function displaySearch() {
$btnSearchTrigger.on('click', function () {
MobileSearchBar.on();
ResultSwitch.on();
$input.trigger('focus');
});
$btnCancel.on('click', function () {
MobileSearchBar.off();
ResultSwitch.off();
});
$input.on('focus', function () {
$searchWrapper.addClass(C_FOCUS);
});
$input.on('focusout', function () {
$searchWrapper.removeClass(C_FOCUS);
});
$input.on('input', () => {
if ($input.val() === '') {
if (isMobileView()) {
$hints.removeClass(C_UNLOADED);
} else {
ResultSwitch.off();
}
} else {
ResultSwitch.on();
if (isMobileView()) {
$hints.addClass(C_UNLOADED);
}
}
});
}

View file

@ -0,0 +1,25 @@
/**
* Expand or close the sidebar in mobile screens.
*/
const $body = $('body');
const ATTR_DISPLAY = 'sidebar-display';
class SidebarUtil {
static isExpanded = false;
static toggle() {
if (SidebarUtil.isExpanded === false) {
$body.attr(ATTR_DISPLAY, '');
} else {
$body.removeAttr(ATTR_DISPLAY);
}
SidebarUtil.isExpanded = !SidebarUtil.isExpanded;
}
}
export function sidebarExpand() {
$('#sidebar-trigger').on('click', SidebarUtil.toggle);
$('#mask').on('click', SidebarUtil.toggle);
}

View file

@ -0,0 +1,109 @@
/**
Safari doesn't support CSS `scroll-behavior: smooth`,
so here is a compatible solution for all browser to smooth scrolling
See: <https://css-tricks.com/snippets/jquery/smooth-scrolling/>
Warning: It must be called after all `<a>` tags (e.g., the dynamic TOC) are ready.
*/
import ScrollHelper from './utils/scroll-helper';
export function smoothScroll() {
const $topbarTitle = $('#topbar-title');
const REM = 16; // in pixels
const ATTR_SCROLL_FOCUS = 'scroll-focus';
const SCOPE = "a[href*='#']:not([href='#']):not([href='#0'])";
$(SCOPE).on('click', function (event) {
if (
this.pathname.replace(/^\//, '') !== location.pathname.replace(/^\//, '')
) {
return;
}
if (location.hostname !== this.hostname) {
return;
}
const hash = decodeURI(this.hash);
let toFootnoteRef = RegExp(/^#fnref:/).test(hash);
let toFootnote = toFootnoteRef ? false : RegExp(/^#fn:/).test(hash);
let selector = '#' + $.escapeSelector(hash.substring(1));
let $target = $(selector);
let isMobileViews = $topbarTitle.is(':visible');
let isPortrait = $(window).width() < $(window).height();
if (typeof $target === 'undefined') {
return;
}
event.preventDefault();
if (history.pushState) {
/* add hash to URL */
history.pushState(null, null, hash);
}
let curOffset = $(window).scrollTop();
let destOffset = ($target.offset().top -= REM / 2);
if (destOffset < curOffset) {
// scroll up
ScrollHelper.hideTopbar();
ScrollHelper.addScrollUpTask();
if (isMobileViews && isPortrait) {
destOffset -= ScrollHelper.getTopbarHeight();
}
} else {
// scroll down
if (isMobileViews && isPortrait) {
destOffset -= ScrollHelper.getTopbarHeight();
}
}
$('html').animate(
{
scrollTop: destOffset
},
500,
() => {
$target.trigger('focus');
/* clean up old scroll mark */
const $scroll_focus = $(`[${ATTR_SCROLL_FOCUS}=true]`);
if ($scroll_focus.length) {
$scroll_focus.attr(ATTR_SCROLL_FOCUS, 'false');
}
/* Clean :target links */
const $target_links = $(':target');
if ($target_links.length) {
/* element that visited by the URL with hash */
$target_links.attr(ATTR_SCROLL_FOCUS, 'false');
}
/* set scroll mark to footnotes */
if (toFootnote || toFootnoteRef) {
$target.attr(ATTR_SCROLL_FOCUS, 'true');
}
if ($target.is(':focus')) {
/* Checking if the target was focused */
return false;
} else {
$target.attr(
'tabindex',
'-1'
); /* Adding tabindex for elements not focusable */
$target.trigger('focus'); /* Set focus again */
}
if (ScrollHelper.hasScrollUpTask()) {
ScrollHelper.popScrollUpTask();
}
}
);
}); /* click() */
}

View file

@ -0,0 +1,11 @@
export function toc() {
// see: https://github.com/tscanlin/tocbot#usage
tocbot.init({
tocSelector: '#toc',
contentSelector: '.post-content',
ignoreSelector: '[data-toc-skip]',
headingSelector: 'h2, h3',
orderedList: false,
scrollSmooth: false
});
}

View file

@ -0,0 +1,6 @@
/**
* Initial Bootstrap Tooltip.
*/
export function loadTooptip() {
$('[data-toggle="tooltip"]').tooltip();
}

View file

@ -0,0 +1,93 @@
/**
* Hide Header on scroll down
*/
import ScrollHelper from './utils/scroll-helper';
const $searchInput = $('#search-input');
const delta = ScrollHelper.getTopbarHeight();
let didScroll;
let lastScrollTop = 0;
function hasScrolled() {
let st = $(window).scrollTop();
/* Make sure they scroll more than delta */
if (Math.abs(lastScrollTop - st) <= delta) {
return;
}
if (st > lastScrollTop) {
/* Scroll down */
ScrollHelper.hideTopbar();
if ($searchInput.is(':focus')) {
$searchInput.trigger('blur'); /* remove focus */
}
} else {
/* Scroll up */
// has not yet scrolled to the bottom of the screen, that is, there is still space for scrolling
if (st + $(window).height() < $(document).height()) {
if (ScrollHelper.hasScrollUpTask()) {
return;
}
if (ScrollHelper.topbarLocked()) {
// avoid redundant scroll up event from smooth scrolling
ScrollHelper.unlockTopbar();
} else {
if (ScrollHelper.orientationLocked()) {
// avoid device auto scroll up on orientation change
ScrollHelper.unLockOrientation();
} else {
ScrollHelper.showTopbar();
}
}
}
}
lastScrollTop = st;
} // hasScrolled()
function handleLandscape() {
if ($(window).scrollTop() === 0) {
return;
}
ScrollHelper.lockOrientation();
ScrollHelper.hideTopbar();
}
export function switchTopbar() {
const orientation = screen.orientation;
if (orientation) {
orientation.onchange = () => {
const type = orientation.type;
if (type === 'landscape-primary' || type === 'landscape-secondary') {
handleLandscape();
}
};
} else {
// for the browsers that not support `window.screen.orientation` API
$(window).on('orientationchange', () => {
if ($(window).width() < $(window).height()) {
// before rotating, it is still in portrait mode.
handleLandscape();
}
});
}
$(window).on('scroll', () => {
if (didScroll) {
return;
}
didScroll = true;
});
setInterval(() => {
if (didScroll) {
hasScrolled();
didScroll = false;
}
}, 250);
}

View file

@ -0,0 +1,64 @@
/**
* A tool for smooth scrolling and topbar switcher
*/
const ATTR_TOPBAR_VISIBLE = 'data-topbar-visible';
const $body = $('body');
const $topbarWrapper = $('#topbar-wrapper');
export default class ScrollHelper {
static scrollUpCount = 0; // the number of times the scroll up was triggered by ToC or anchor
static topbarIsLocked = false;
static orientationIsLocked = false;
static hideTopbar() {
$body.attr(ATTR_TOPBAR_VISIBLE, 'false');
}
static showTopbar() {
$body.attr(ATTR_TOPBAR_VISIBLE, 'true');
}
// scroll up
static addScrollUpTask() {
ScrollHelper.scrollUpCount += 1;
if (!ScrollHelper.topbarIsLocked) {
ScrollHelper.topbarIsLocked = true;
}
}
static popScrollUpTask() {
ScrollHelper.scrollUpCount -= 1;
}
static hasScrollUpTask() {
return ScrollHelper.scrollUpCount > 0;
}
static topbarLocked() {
return ScrollHelper.topbarIsLocked === true;
}
static unlockTopbar() {
ScrollHelper.topbarIsLocked = false;
}
static getTopbarHeight() {
return $topbarWrapper.outerHeight();
}
// orientation change
static orientationLocked() {
return ScrollHelper.orientationIsLocked === true;
}
static lockOrientation() {
ScrollHelper.orientationIsLocked = true;
}
static unLockOrientation() {
ScrollHelper.orientationIsLocked = false;
}
}

View file

@ -0,0 +1,3 @@
export { basic } from './layouts/basic';
export { initSidebar } from './layouts/sidebar';
export { initTopbar } from './layouts/topbar';

View file

@ -0,0 +1,7 @@
import { back2top } from '../components/back-to-top';
import { loadTooptip } from '../components/tooltip-loader';
export function basic() {
back2top();
loadTooptip();
}

View file

@ -0,0 +1,7 @@
import { modeWatcher } from '../components/mode-watcher';
import { sidebarExpand } from '../components/sidebar';
export function initSidebar() {
modeWatcher();
sidebarExpand();
}

View file

@ -0,0 +1,9 @@
import { convertTitle } from '../components/convert-title';
import { displaySearch } from '../components/search-display';
import { switchTopbar } from '../components/topbar-switcher';
export function initTopbar() {
convertTitle();
displaySearch();
switchTopbar();
}

View file

@ -0,0 +1,7 @@
export { categoryCollapse } from './components/category-collapse';
export { initClipboard } from './components/clipboard';
export { imgExtra } from './components/img-extra';
export { initLocaleDatetime } from './components/locale-datetime';
export { initPageviews } from './components/pageviews';
export { smoothScroll } from './components/smooth-scroll';
export { toc } from './components/toc';

9
_javascript/page.js Normal file
View file

@ -0,0 +1,9 @@
import { basic, initSidebar, initTopbar } from './modules/layouts';
import { imgExtra, initClipboard, smoothScroll } from './modules/plugins';
basic();
initSidebar();
initTopbar();
imgExtra();
initClipboard();
smoothScroll();

19
_javascript/post.js Normal file
View file

@ -0,0 +1,19 @@
import { basic, initSidebar, initTopbar } from './modules/layouts';
import {
imgExtra,
initLocaleDatetime,
initClipboard,
smoothScroll,
initPageviews,
toc
} from './modules/plugins';
basic();
initSidebar();
initTopbar();
imgExtra();
initLocaleDatetime();
initClipboard();
toc();
smoothScroll(); // must be called after toc is created
initPageviews();

View file

@ -1,30 +0,0 @@
/**
* Tab 'Categories' expand/close effect.
*/
$(function () {
const childPrefix = "l_";
const parentPrefix = "h_";
const collapse = $(".collapse");
/* close up top-category */
collapse.on("hide.bs.collapse", function () { /* Bootstrap collapse events. */
const parentId = parentPrefix + $(this).attr("id").substring(childPrefix.length);
if (parentId) {
$(`#${parentId} .far.fa-folder-open`).attr("class", "far fa-folder fa-fw");
$(`#${parentId} i.fas`).addClass("rotate");
$(`#${parentId}`).removeClass("hide-border-bottom");
}
});
/* expand the top category */
collapse.on("show.bs.collapse", function () {
const parentId = parentPrefix + $(this).attr("id").substring(childPrefix.length);
if (parentId) {
$(`#${parentId} .far.fa-folder`).attr("class", "far fa-folder-open fa-fw");
$(`#${parentId} i.fas`).removeClass("rotate");
$(`#${parentId}`).addClass("hide-border-bottom");
}
});
});

View file

@ -1,133 +0,0 @@
/**
* Clipboard functions
*
* Dependencies:
* - popper.js (https://github.com/popperjs/popper-core)
* - clipboard.js (https://github.com/zenorocha/clipboard.js)
*/
$(function () {
const btnSelector = '.code-header>button';
const ICON_SUCCESS = 'fas fa-check';
const ATTR_TIMEOUT = 'timeout';
const ATTR_TITLE_SUCCEED = 'data-title-succeed';
const ATTR_TITLE_ORIGIN = 'data-original-title';
const TIMEOUT = 2000; // in milliseconds
function isLocked(node) {
if ($(node)[0].hasAttribute(ATTR_TIMEOUT)) {
let timeout = $(node).attr(ATTR_TIMEOUT);
if (Number(timeout) > Date.now()) {
return true;
}
}
return false;
}
function lock(node) {
$(node).attr(ATTR_TIMEOUT, Date.now() + TIMEOUT);
}
function unlock(node) {
$(node).removeAttr(ATTR_TIMEOUT);
}
/* --- Copy code block --- */
// Initial the clipboard.js object
const clipboard = new ClipboardJS(btnSelector, {
target(trigger) {
let codeBlock = trigger.parentNode.nextElementSibling;
return codeBlock.querySelector('code .rouge-code');
}
});
$(btnSelector).tooltip({
trigger: 'hover',
placement: 'left'
});
function getIcon(btn) {
let iconNode = $(btn).children();
return iconNode.attr('class');
}
const ICON_DEFAULT = getIcon(btnSelector);
function showTooltip(btn) {
const succeedTitle = $(btn).attr(ATTR_TITLE_SUCCEED);
$(btn).attr(ATTR_TITLE_ORIGIN, succeedTitle).tooltip('show');
}
function hideTooltip(btn) {
$(btn).tooltip('hide').removeAttr(ATTR_TITLE_ORIGIN);
}
function setSuccessIcon(btn) {
let btnNode = $(btn);
let iconNode = btnNode.children();
iconNode.attr('class', ICON_SUCCESS);
}
function resumeIcon(btn) {
let btnNode = $(btn);
let iconNode = btnNode.children();
iconNode.attr('class', ICON_DEFAULT);
}
clipboard.on('success', (e) => {
e.clearSelection();
const trigger = e.trigger;
if (isLocked(trigger)) {
return;
}
setSuccessIcon(trigger);
showTooltip(trigger);
lock(trigger);
setTimeout(() => {
hideTooltip(trigger);
resumeIcon(trigger);
unlock(trigger);
}, TIMEOUT);
});
/* --- Post link sharing --- */
$('#copy-link').click((e) => {
let target = $(e.target);
if (isLocked(target)) {
return;
}
// Copy URL to clipboard
const url = window.location.href;
const $temp = $("<input>");
$("body").append($temp);
$temp.val(url).select();
document.execCommand("copy");
$temp.remove();
// Switch tooltip title
const defaultTitle = target.attr(ATTR_TITLE_ORIGIN);
const succeedTitle = target.attr(ATTR_TITLE_SUCCEED);
target.attr(ATTR_TITLE_ORIGIN, succeedTitle).tooltip('show');
lock(target);
setTimeout(() => {
target.attr(ATTR_TITLE_ORIGIN, defaultTitle);
unlock(target);
}, TIMEOUT);
});
});

View file

@ -1,28 +0,0 @@
/**
* Set up image stuff
*/
(function() {
if ($('#core-wrapper img[data-src]') <= 0) {
return;
}
/* See: <https://github.com/dimsemenov/Magnific-Popup> */
$('.popup').magnificPopup({
type: 'image',
closeOnContentClick: true,
showCloseBtn: false,
zoom: {
enabled: true,
duration: 300,
easing: 'ease-in-out'
}
});
/* Stop shimmer when image loaded */
document.addEventListener('lazyloaded', function(e) {
const $img = $(e.target);
$img.parent().removeClass('shimmer');
});
})();

View file

@ -1,43 +0,0 @@
/**
* Update month/day to locale datetime
*
* Requirement: <https://github.com/iamkun/dayjs>
*/
/* A tool for locale datetime */
const LocaleHelper = (function () {
const locale = $('html').attr('lang').substr(0, 2);
const attrTimestamp = 'data-ts';
const attrDateFormat = 'data-df';
return {
locale: () => locale,
attrTimestamp: () => attrTimestamp,
attrDateFormat: () => attrDateFormat,
getTimestamp: ($elem) => Number($elem.attr(attrTimestamp)), // unix timestamp
getDateFormat: ($elem) => $elem.attr(attrDateFormat)
};
}());
$(function () {
dayjs.locale(LocaleHelper.locale());
dayjs.extend(window.dayjs_plugin_localizedFormat);
$(`[${LocaleHelper.attrTimestamp()}]`).each(function () {
const date = dayjs.unix(LocaleHelper.getTimestamp($(this)));
const text = date.format(LocaleHelper.getDateFormat($(this)));
$(this).text(text);
$(this).removeAttr(LocaleHelper.attrTimestamp());
$(this).removeAttr(LocaleHelper.attrDateFormat());
// setup tooltips
const tooltip = $(this).attr('data-toggle');
if (typeof tooltip === 'undefined' || tooltip !== 'tooltip') {
return;
}
const tooltipText = date.format('llll'); // see: https://day.js.org/docs/en/display/format#list-of-localized-formats
$(this).attr('data-original-title', tooltipText);
});
});

View file

@ -1,250 +0,0 @@
/**
* Count page views form GA or local cache file.
*
* Dependencies:
* - jQuery
* - countUp.js <https://github.com/inorganik/countUp.js>
*/
const getInitStatus = (function () {
let hasInit = false;
return () => {
let ret = hasInit;
if (!hasInit) {
hasInit = true;
}
return ret;
};
}());
const PvOpts = (function () {
function getContent(selector) {
return $(selector).attr("content");
}
function hasContent(selector) {
let content = getContent(selector);
return (typeof content !== "undefined" && content !== false);
}
return {
getProxyMeta() {
return getContent("meta[name=pv-proxy-endpoint]");
},
getLocalMeta() {
return getContent("meta[name=pv-cache-path]");
},
hasProxyMeta() {
return hasContent("meta[name=pv-proxy-endpoint]");
},
hasLocalMeta() {
return hasContent("meta[name=pv-cache-path]");
}
};
}());
const PvStorage = (function () {
const Keys = {
KEY_PV: "pv",
KEY_PV_SRC: "pv_src",
KEY_CREATION: "pv_created_date"
};
const Source = {
LOCAL: "same-origin",
PROXY: "cors"
};
function get(key) {
return localStorage.getItem(key);
}
function set(key, val) {
localStorage.setItem(key, val);
}
function saveCache(pv, src) {
set(Keys.KEY_PV, pv);
set(Keys.KEY_PV_SRC, src);
set(Keys.KEY_CREATION, new Date().toJSON());
}
return {
keysCount() {
return Object.keys(Keys).length;
},
hasCache() {
return (localStorage.getItem(Keys.KEY_PV) !== null);
},
getCache() {
return JSON.parse(localStorage.getItem(Keys.KEY_PV));
},
saveLocalCache(pv) {
saveCache(pv, Source.LOCAL);
},
saveProxyCache(pv) {
saveCache(pv, Source.PROXY);
},
isExpired() {
let date = new Date(get(Keys.KEY_CREATION));
date.setHours(date.getHours() + 1); // per hour
return Date.now() >= date.getTime();
},
isFromLocal() {
return get(Keys.KEY_PV_SRC) === Source.LOCAL;
},
isFromProxy() {
return get(Keys.KEY_PV_SRC) === Source.PROXY;
},
newerThan(pv) {
return PvStorage.getCache().totalsForAllResults["ga:pageviews"] > pv.totalsForAllResults["ga:pageviews"];
},
inspectKeys() {
if (localStorage.length !== PvStorage.keysCount()) {
localStorage.clear();
return;
}
for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i);
switch (key) {
case Keys.KEY_PV:
case Keys.KEY_PV_SRC:
case Keys.KEY_CREATION:
break;
default:
localStorage.clear();
return;
}
}
}
};
}()); /* PvStorage */
function countUp(min, max, destId) {
if (min < max) {
let numAnim = new CountUp(destId, min, max);
if (!numAnim.error) {
numAnim.start();
} else {
console.error(numAnim.error);
}
}
}
function countPV(path, rows) {
let count = 0;
if (typeof rows !== "undefined") {
for (let i = 0; i < rows.length; ++i) {
const gaPath = rows[parseInt(i, 10)][0];
if (gaPath === path) { /* path format see: site.permalink */
count += parseInt(rows[parseInt(i, 10)][1], 10);
break;
}
}
}
return count;
}
function tacklePV(rows, path, elem, hasInit) {
let count = countPV(path, rows);
count = (count === 0 ? 1 : count);
if (!hasInit) {
elem.text(new Intl.NumberFormat().format(count));
} else {
const initCount = parseInt(elem.text().replace(/,/g, ""), 10);
if (count > initCount) {
countUp(initCount, count, elem.attr("id"));
}
}
}
function displayPageviews(data) {
if (typeof data === "undefined") {
return;
}
let hasInit = getInitStatus();
const rows = data.rows; /* could be undefined */
if ($("#post-list").length > 0) { /* the Home page */
$(".post-preview").each(function () {
const path = $(this).find("a").attr("href");
tacklePV(rows, path, $(this).find(".pageviews"), hasInit);
});
} else if ($(".post").length > 0) { /* the post */
const path = window.location.pathname;
tacklePV(rows, path, $("#pv"), hasInit);
}
}
function fetchProxyPageviews() {
if (PvOpts.hasProxyMeta()) {
$.ajax({
type: "GET",
url: PvOpts.getProxyMeta(),
dataType: "jsonp",
jsonpCallback: "displayPageviews",
success: (data) => {
PvStorage.saveProxyCache(JSON.stringify(data));
},
error: (jqXHR, textStatus, errorThrown) => {
console.log("Failed to load pageviews from proxy server: " + errorThrown);
}
});
}
}
function fetchLocalPageviews(hasCache = false) {
return fetch(PvOpts.getLocalMeta())
.then(response => response.json())
.then(data => {
if (hasCache) {
// The cache from the proxy will sometimes be more recent than the local one
if (PvStorage.isFromProxy() && PvStorage.newerThan(data)) {
return;
}
}
displayPageviews(data);
PvStorage.saveLocalCache(JSON.stringify(data));
});
}
$(function () {
if ($(".pageviews").length <= 0) {
return;
}
PvStorage.inspectKeys();
if (PvStorage.hasCache()) {
displayPageviews(PvStorage.getCache());
if (PvStorage.isExpired()) {
if (PvOpts.hasLocalMeta()) {
fetchLocalPageviews(true).then(fetchProxyPageviews);
} else {
fetchProxyPageviews();
}
} else {
if (PvStorage.isFromLocal()) {
fetchProxyPageviews();
}
}
} else { // no cached
if (PvOpts.hasLocalMeta()) {
fetchLocalPageviews().then(fetchProxyPageviews);
} else {
fetchProxyPageviews();
}
}
});

View file

@ -1,96 +0,0 @@
/**
Safari doesn't support CSS `scroll-behavior: smooth`,
so here is a compatible solution for all browser to smooth scrolling
See: <https://css-tricks.com/snippets/jquery/smooth-scrolling/>
Warning: It must be called after all `<a>` tags (e.g., the dynamic TOC) are ready.
*/
$(function () {
const $topbarTitle = $("#topbar-title");
const REM = 16; // in pixels
const ATTR_SCROLL_FOCUS = "scroll-focus";
$("a[href*='#']")
.not("[href='#']")
.not("[href='#0']")
.click(function (event) {
if (this.pathname.replace(/^\//, "") !==
location.pathname.replace(/^\//, "")) {
return;
}
if (location.hostname !== this.hostname) {
return;
}
const hash = decodeURI(this.hash);
let toFootnoteRef = RegExp(/^#fnref:/).test(hash);
let toFootnote = toFootnoteRef ? false : RegExp(/^#fn:/).test(hash);
let selector = hash.includes(":") ? hash.replace(/:/g, "\\:") : hash;
let $target = $(selector);
let isMobileViews = $topbarTitle.is(":visible");
let isPortrait = $(window).width() < $(window).height();
if (typeof $target === "undefined") {
return;
}
event.preventDefault();
if (history.pushState) { /* add hash to URL */
history.pushState(null, null, hash);
}
let curOffset = $(window).scrollTop();
let destOffset = $target.offset().top -= REM / 2;
if (destOffset < curOffset) { // scroll up
ScrollHelper.hideTopbar();
ScrollHelper.addScrollUpTask();
if (isMobileViews && isPortrait) {
destOffset -= ScrollHelper.getTopbarHeight();
}
} else { // scroll down
if (isMobileViews && isPortrait) {
destOffset -= ScrollHelper.getTopbarHeight();
}
}
$("html").animate({
scrollTop: destOffset
}, 500, () => {
$target.focus();
/* clean up old scroll mark */
if ($(`[${ATTR_SCROLL_FOCUS}=true]`).length) {
$(`[${ATTR_SCROLL_FOCUS}=true]`).attr(ATTR_SCROLL_FOCUS, false);
}
/* Clean :target links */
if ($(":target").length) { /* element that visited by the URL with hash */
$(":target").attr(ATTR_SCROLL_FOCUS, false);
}
/* set scroll mark to footnotes */
if (toFootnote || toFootnoteRef) {
$target.attr(ATTR_SCROLL_FOCUS, true);
}
if ($target.is(":focus")) { /* Checking if the target was focused */
return false;
} else {
$target.attr("tabindex", "-1"); /* Adding tabindex for elements not focusable */
$target.focus(); /* Set focus again */
}
if (ScrollHelper.hasScrollUpTask()) {
ScrollHelper.popScrollUpTask();
}
});
}); /* click() */
});

View file

@ -18,7 +18,7 @@ layout: compress
{% include head.html %}
<body data-spy="scroll" data-target="#toc" data-topbar-visible="true">
<body data-topbar-visible="true">
{% include sidebar.html %}

View file

@ -3,10 +3,10 @@ layout: page
# The Home page layout
---
{% assign pinned = site.posts | where: "pin", "true" %}
{% assign default = site.posts | where_exp: "item", "item.pin != true and item.hidden != true" %}
{% assign pinned = site.posts | where: 'pin', 'true' %}
{% assign default = site.posts | where_exp: 'item', 'item.pin != true and item.hidden != true' %}
{% assign posts = "" | split: "" %}
{% assign posts = '' | split: '' %}
<!-- Get pinned posts -->
@ -39,15 +39,15 @@ layout: page
{% endif %}
<div id="post-list">
{% for post in posts %}
<div class="post-preview">
<h1>
<a href="{{ post.url | relative_url }}">{{ post.title }}</a>
<div class="card post-preview">
<a href="{{ post.url | relative_url }}">
<div class="card-body">
<h1 class="card-title">
{{ post.title }}
</h1>
<div class="post-content">
<div class="card-text post-content">
<p>
{% include no-linenos.html content=post.content %}
{{ content | markdownify | strip_html | truncate: 200 | escape }}
@ -56,7 +56,6 @@ layout: page
<div class="post-meta text-muted d-flex">
<div class="mr-auto">
<!-- posted date -->
<i class="far fa-calendar fa-fw"></i>
{% include datetime.html date=post.date %}
@ -71,7 +70,6 @@ layout: page
{% endfor %}
</span>
{% endif %}
</div>
{% if post.pin %}
@ -80,14 +78,15 @@ layout: page
<span>{{ site.data.locales[site.lang].post.pin_prompt }}</span>
</div>
{% endif %}
</div> <!-- .post-meta -->
</div> <!-- .post-review -->
</div>
<!-- .post-meta -->
</div>
</a>
</div>
<!-- .post-review -->
{% endfor %}
</div> <!-- #post-list -->
</div>
<!-- #post-list -->
{% if paginator.total_pages > 0 %}
{% include post-paginator.html %}

View file

@ -133,13 +133,16 @@ _Image Caption_
### Size
In order to prevent the page content layout from shifting when the image is loaded, we should set the width and height for each image:
In order to prevent the page content layout from shifting when the image is loaded, we should set the width and height for each image.
```markdown
![Desktop View](/assets/img/sample/mockup.png){: width="700" height="400" }
```
{: .nolineno}
> For an SVG, you have to at least specify its _width_, otherwise it won't be rendered.
{: .prompt-info }
Starting from _Chirpy v5.0.0_, `height` and `width` support abbreviations (`height` → `h`, `width``w`). The following example has the same effect as the above:
```markdown

View file

@ -9,7 +9,7 @@ pin: true
## Prerequisites
Follow the instructions in the [Jekyll Docs](https://jekyllrb.com/docs/installation/) to complete the installation of `Ruby`, `RubyGems`, `Jekyll`, and `Bundler`. In addition, [Git](https://git-scm.com/) is also required to be installed.
Follow the instructions in the [Jekyll Docs](https://jekyllrb.com/docs/installation/) to complete the installation of the basic environment. [Git](https://git-scm.com/) also needs to be installed.
## Installation
@ -18,17 +18,17 @@ Follow the instructions in the [Jekyll Docs](https://jekyllrb.com/docs/installat
There are two ways to create a new repository for this theme:
- [**Using the Chirpy Starter**](#option-1-using-the-chirpy-starter) - Easy to upgrade, isolates irrelevant project files so you can focus on writing.
- [**Forking on GitHub**](#option-2-forking-on-github) - Convenient for custom development, but difficult to upgrade. Unless you are familiar with Jekyll and are determined to tweak or contribute to this project, this approach is not recommended.
- [**GitHub Fork**](#option-2-github-fork) - Convenient for custom development, but difficult to upgrade. Unless you are familiar with Jekyll and are determined to tweak or contribute to this project, this approach is not recommended.
#### Option 1. Using the Chirpy Starter
Create a new repository from the [**Chirpy Starter**][use-starter] and name it `<GH_USERNAME>.github.io`, where `GH_USERNAME` represents your GitHub username.
Sign in to GitHub and browse to [**Chirpy Starter**][starter], click the button <kbd>Use this template</kbd> > <kbd>Create a new repository</kbd>, and name the new repository `USERNAME.github.io`, where `USERNAME` represents your GitHub username.
#### Option 2. Forking on GitHub
#### Option 2. GitHub Fork
[Fork **Chirpy**](https://github.com/cotes2020/jekyll-theme-chirpy/fork) on GitHub and rename it to `<GH_USERNAME>.github.io`. Please note that the default branch code is in development. If you want the site to be stable, please switch to the [latest tag][latest-tag] and start writing.
Sign in to GitHub to [fork **Chirpy**](https://github.com/cotes2020/jekyll-theme-chirpy/fork), and then rename it to `USERNAME.github.io` (`USERNAME` means your username).
And then execute:
Next, clone your site to local machine. In order to build JavaScript files later, we need to install [Node.js][nodejs], and then run the tool:
```console
$ bash tools/init
@ -39,15 +39,14 @@ $ bash tools/init
The above command will:
1. Remove the files in `_posts`{: .filepath} from your repository.
2. If the option `--no-gh` is provided, the directory `.github`{: .filepath} will be deleted. Otherwise, set up the GitHub Action workflow by removing the extension `.hook`{: .filepath} of `.github/workflows/pages-deploy.yml.hook`{: .filepath}, and then remove the other files and directories in the folder `.github`{: .filepath}.
3. Create a new commit to save the changes automatically.
1. Check out the code to the [latest tag][latest-tag] (to ensure the stability of your site: as the code for the default branch is under development).
2. Remove non-essential sample files and take care of GitHub-related files.
3. Build JavaScript files and export to `assets/js/dist/`{: .filepath }, then make them tracked by Git.
4. Automatically create a new commit to save the changes above.
### Installing Dependencies
Before running for the first time, go to the root directory of your site, and install dependencies as follows:
Before running local server for the first time, go to the root directory of your site and run:
```console
$ bundle
@ -93,31 +92,32 @@ $ docker run -it --rm \
jekyll serve
```
After a while, the local service will be published at _<http://127.0.0.1:4000>_.
After a few seconds, the local service will be published at _<http://127.0.0.1:4000>_.
## Deployment
Before the deployment begins, check out the file `_config.yml`{: .filepath} and make sure the `url` is configured correctly. Furthermore, if you prefer the [**project site**](https://help.github.com/en/github/working-with-github-pages/about-github-pages#types-of-github-pages-sites) and don't use a custom domain, or you want to visit your website with a base URL on a web server other than **GitHub Pages**, remember to change the `baseurl` to your project name that starts with a slash, e.g, `/project-name`.
Now you can choose ONE of the following methods to deploy your Jekyll site.
Now you can choose _ONE_ of the following methods to deploy your Jekyll site.
### Deploy by Using GitHub Actions
Ensure your Jekyll site has the file `.github/workflows/pages-deploy.yml`{: .filepath}. Otherwise, create a new one and fill in the contents of the [sample file][workflow], and the value of the `on.push.branches` should be the same as your repo's default branch name. And then rename your repository to `<GH_USERNAME>.github.io` on GitHub.
There are a few things to get ready for.
Furthermore, if you have committed `Gemfile.lock`{: .filepath} to the repository and your local machine is not Linux, go the the root directory of your site and update the platform list:
- If you're on the GitHub Free plan, keep your site repository public.
- If you have committed `Gemfile.lock`{: .filepath} to the repository, and your local machine is not running Linux, go the the root of your site and update the platform list of the lock-file:
```console
$ bundle lock --add-platform x86_64-linux
```
Now publish your Jekyll site:
Next, configure the _Pages_ service.
1. Browse to your repository on GitHub. Select the tab _Settings_, then click _Pages_ in the left navigation bar. Then, in the **Source** section (under _Build and deployment_), select [**GitHub Actions**][pages-workflow-src] from the dropdown menu.
2. Push any commit to remote to trigger the GitHub Actions workflow. In the _Actions_ tab of your repository, you should see the workflow _Build and Deploy_ running. Once the build is complete and successful, the site should be deployed automatically.
2. Push any commits to GitHub to trigger the _Actions_ workflow. In the _Actions_ tab of your repository, you should see the workflow _Build and Deploy_ running. Once the build is complete and successful, the site will be deployed automatically.
3. Visit your website at the address indicated by GitHub.
At this point, you can go to the URL indicated by GitHub to access your site.
### Manually Build and Deploy
@ -141,31 +141,7 @@ $ docker run -it --rm \
Unless you specified the output path, the generated site files will be placed in folder `_site`{: .filepath} of the project's root directory. Now you should upload those files to the target server.
## Upgrading
It depends on how you use the theme:
- If you are using the theme gem (there will be `gem "jekyll-theme-chirpy"` in the `Gemfile`{: .filepath}), editing the `Gemfile`{: .filepath} and update the version number of the theme gem, for example:
```diff
- gem "jekyll-theme-chirpy", "~> 3.2", ">= 3.2.1"
+ gem "jekyll-theme-chirpy", "~> 3.3", ">= 3.3.0"
```
{: .nolineno file="Gemfile" }
And then execute the following command:
```console
$ bundle update jekyll-theme-chirpy
```
As the version upgrades, the critical files (for details, see the [Startup Template][starter]) and configuration options will change. Please refer to the [Upgrade Guide](https://github.com/cotes2020/jekyll-theme-chirpy/wiki/Upgrade-Guide) to keep your repo's files in sync with the latest version of the theme.
- If you forked from the source project (there will be `gemspec` in the `Gemfile`{: .filepath} of your site), then merge the [latest upstream tags][latest-tag] into your Jekyll site to complete the upgrade.
The merge is likely to conflict with your local modifications. Please be patient and careful to resolve these conflicts.
[nodejs]: https://nodejs.org/
[starter]: https://github.com/cotes2020/chirpy-starter
[use-starter]: https://github.com/cotes2020/chirpy-starter/generate
[workflow]: https://github.com/cotes2020/jekyll-theme-chirpy/blob/master/.github/workflows/pages-deploy.yml.hook
[pages-workflow-src]: https://docs.github.com/en/pages/getting-started-with-github-pages/configuring-a-publishing-source-for-your-github-pages-site#publishing-with-a-custom-github-actions-workflow
[latest-tag]: https://github.com/cotes2020/jekyll-theme-chirpy/tags

View file

@ -5,22 +5,22 @@
html {
@media (prefers-color-scheme: light) {
&:not([data-mode]),
&[data-mode="light"] {
&[data-mode='light'] {
@include light-scheme;
}
&[data-mode="dark"] {
&[data-mode='dark'] {
@include dark-scheme;
}
}
@media (prefers-color-scheme: dark) {
&:not([data-mode]),
&[data-mode="dark"] {
&[data-mode='dark'] {
@include dark-scheme;
}
&[data-mode="light"] {
&[data-mode='light'] {
@include light-scheme;
}
}
@ -29,10 +29,12 @@ html {
}
body {
background: var(--body-bg);
background: var(--main-bg);
padding: env(safe-area-inset-top) env(safe-area-inset-right)
env(safe-area-inset-bottom) env(safe-area-inset-left);
color: var(--text-color);
-webkit-font-smoothing: antialiased;
font-family: "Source Sans Pro", "Microsoft Yahei", sans-serif;
font-family: 'Source Sans Pro', 'Microsoft Yahei', sans-serif;
line-height: 1.75;
}
@ -90,7 +92,7 @@ img {
animation: fade-in 0.4s ease-in;
}
&[data-lqip="true"] {
&[data-lqip='true'] {
&.lazyload,
&.lazyloading {
-webkit-filter: blur(20px);
@ -98,7 +100,7 @@ img {
}
}
&:not([data-lqip="true"]) {
&:not([data-lqip='true']) {
&.lazyload,
&.lazyloading {
background: var(--img-bg);
@ -115,13 +117,21 @@ img {
}
@-webkit-keyframes fade-in {
from { opacity: 0; }
to { opacity: 1; }
from {
opacity: 0;
}
to {
opacity: 1;
}
}
@keyframes fade-in {
from { opacity: 0; }
to { opacity: 1; }
from {
opacity: 0;
}
to {
opacity: 1;
}
}
}
@ -130,7 +140,7 @@ blockquote {
padding-left: 1rem;
color: var(--blockquote-text-color);
&[class^="prompt-"] {
&[class^='prompt-'] {
border-left: 0;
position: relative;
padding: 1rem 1rem 1rem 3rem;
@ -153,15 +163,10 @@ blockquote {
}
}
@include prompt("tip", "\f0eb", "regular");
@include prompt("info", "\f06a");
@include prompt("warning", "\f06a");
@include prompt("danger", "\f071");
}
mjx-container {
overflow-x: auto;
overflow-y: hidden;
@include prompt('tip', '\f0eb', 'regular');
@include prompt('info', '\f06a');
@include prompt('warning', '\f06a');
@include prompt('danger', '\f071');
}
kbd {
@ -213,7 +218,8 @@ footer {
}
}
i { /* fontawesome icons */
/* fontawesome icons */
i {
&.far,
&.fas {
@extend %no-cursor;
@ -271,7 +277,7 @@ i { /* fontawesome icons */
}
}
[data-topbar-visible="true"] & > div {
[data-topbar-visible='true'] & > div {
top: 6rem;
}
}
@ -315,7 +321,7 @@ i { /* fontawesome icons */
/* [scroll-focus] added by `smooth-scroll.js` */
&:target:not([scroll-focus]),
&[scroll-focus="true"] > p {
&[scroll-focus='true'] > p {
background-color: var(--footnote-target-bg);
width: -moz-fit-content;
width: -webkit-fit-content;
@ -336,7 +342,7 @@ i { /* fontawesome icons */
/* [scroll-focus] added by `smooth-scroll.js` */
@at-root sup:target:not([scroll-focus]),
sup[scroll-focus=true] > a#{&} {
sup[scroll-focus='true'] > a#{&} {
background-color: var(--footnote-target-bg);
}
}
@ -419,10 +425,6 @@ i { /* fontawesome icons */
word-spacing: 1px;
a {
&:not(:last-child) {
margin-right: 2px;
}
&:not([class]):hover {
@extend %link-hover;
}
@ -485,7 +487,8 @@ i { /* fontawesome icons */
list-style-type: none;
padding-left: 0;
> i { /* checkbox icon */
/* checkbox icon */
> i {
width: 2rem;
margin-left: -1.25rem;
color: var(--checkbox-color);
@ -501,7 +504,7 @@ i { /* fontawesome icons */
}
}
input[type="checkbox"] {
input[type='checkbox'] {
margin: 0 0.5rem 0.2rem -1.3rem;
vertical-align: middle;
}
@ -555,7 +558,7 @@ i { /* fontawesome icons */
background: var(--img-bg);
&::before {
content: "";
content: '';
position: absolute;
background: var(--shimmer-bg);
height: 100%;
@ -565,13 +568,25 @@ i { /* fontawesome icons */
}
@-webkit-keyframes shimmer {
0% { -webkit-transform: translateX(-100%); transform: translateX(-100%); }
100% { -webkit-transform: translateX(100%); transform: translateX(100%); }
0% {
-webkit-transform: translateX(-100%);
transform: translateX(-100%);
}
100% {
-webkit-transform: translateX(100%);
transform: translateX(100%);
}
}
@keyframes shimmer {
0% { -webkit-transform: translateX(-100%); transform: translateX(-100%); }
100% { -webkit-transform: translateX(100%); transform: translateX(100%); }
0% {
-webkit-transform: translateX(-100%);
transform: translateX(-100%);
}
100% {
-webkit-transform: translateX(100%);
transform: translateX(100%);
}
}
}
@ -637,7 +652,8 @@ i { /* fontawesome icons */
@include no-text-decoration;
}
.tooltip-inner { /* Overrided BS4 Tooltip */
/* Overrided BS4 Tooltip */
.tooltip-inner {
font-size: 0.7rem;
max-width: 220px;
text-align: left;
@ -684,9 +700,15 @@ figure .mfp-title {
text-align: center;
}
/* MathJax */
mjx-container {
overflow-y: hidden;
min-width: auto !important;
}
/* --- sidebar layout --- */
$sidebar-display: "sidebar-display";
$sidebar-display: 'sidebar-display';
#sidebar {
@include pl-pr(0);
@ -772,6 +794,7 @@ $sidebar-display: "sidebar-display";
min-height: 3rem; /* avoid vertical shifting in multi-line words */
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
@ -815,10 +838,11 @@ $sidebar-display: "sidebar-display";
width: 100%;
}
&::after { /* the cursor */
/* the cursor */
&::after {
display: table;
visibility: hidden;
content: "";
content: '';
position: relative;
right: 1px;
width: $cursor-width;
@ -837,7 +861,8 @@ $sidebar-display: "sidebar-display";
@for $i from 1 through $tab-count {
$offset: $tab-count - $i;
$top: (-$offset * $tab-height) + (($tab-height - $tab-cursor-height) * 0.5);
$top: (-$offset * $tab-height) +
(($tab-height - $tab-cursor-height) * 0.5);
@if $i < $tab-count {
> li.active:nth-child(#{$i}),
@ -898,7 +923,7 @@ $sidebar-display: "sidebar-display";
@extend %no-cursor;
background-color: var(--sidebar-muted-color);
content: "";
content: '';
width: 3px;
height: 3px;
border-radius: 50%;
@ -941,13 +966,14 @@ $sidebar-display: "sidebar-display";
border-bottom: 1px solid rgba(0, 0, 0, 0.07);
background-color: var(--topbar-wrapper-bg);
[data-topbar-visible="false"] & {
[data-topbar-visible='false'] & {
top: -$topbar-height; /* same as topbar height. */
}
}
#topbar {
i { /* icons */
/* icons */
i {
color: #999999;
}
@ -963,7 +989,7 @@ $sidebar-display: "sidebar-display";
span {
&:not(:last-child) {
&::after {
content: "";
content: '';
padding: 0 0.3rem;
}
}
@ -991,7 +1017,8 @@ $sidebar-display: "sidebar-display";
}
}
#search-cancel { /* 'Cancel' link */
/* 'Cancel' link */
#search-cancel {
color: var(--link-color);
margin-left: 1rem;
display: none;
@ -1012,9 +1039,21 @@ $sidebar-display: "sidebar-display";
background: center;
&.form-control {
&::-moz-placeholder { @include input-placeholder; }
&::-webkit-input-placeholder { @include input-placeholder; }
&::placeholder { @include input-placeholder; }
&::-moz-placeholder {
@include input-placeholder;
}
&::-webkit-input-placeholder {
@include input-placeholder;
}
&:-ms-input-placeholder {
@include input-placeholder;
}
&::-ms-input-placeholder {
@include input-placeholder;
}
&::placeholder {
@include input-placeholder;
}
}
}
}
@ -1036,7 +1075,7 @@ $sidebar-display: "sidebar-display";
margin: 0 1.25rem 1rem 0;
&::before {
content: "#";
content: '#';
color: var(--text-muted-color);
padding-right: 0.2rem;
}
@ -1068,7 +1107,8 @@ $sidebar-display: "sidebar-display";
margin-bottom: 1rem;
}
i { /* icons */
/* icons */
i {
color: #818182;
margin-right: 0.15rem;
font-size: 80%;
@ -1115,10 +1155,7 @@ $sidebar-display: "sidebar-display";
#mask {
display: none;
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
inset: 0 0 0 0;
height: 100%;
width: 100%;
z-index: 1;
@ -1266,7 +1303,9 @@ $sidebar-display: "sidebar-display";
}
#core-wrapper {
min-height: calc(100vh - #{$topbar-height} - #{$footer-height-mobile}) !important;
min-height: calc(
100vh - #{$topbar-height} - #{$footer-height-mobile}
) !important;
h1 {
margin-top: 2.2rem;
@ -1274,7 +1313,7 @@ $sidebar-display: "sidebar-display";
}
.post-content {
> blockquote[class^="prompt-"] {
> blockquote[class^='prompt-'] {
@include ml-mr(-1.25rem);
border-radius: 0;
@ -1410,7 +1449,7 @@ $sidebar-display: "sidebar-display";
} /* max-width: 849px */
@media all and (max-width: 849px) and (orientation: portrait) {
[data-topbar-visible="false"] #topbar-wrapper {
[data-topbar-visible='false'] #topbar-wrapper {
top: 0;
}
}
@ -1600,7 +1639,9 @@ $sidebar-display: "sidebar-display";
}
#search-wrapper {
margin-right: calc(#{$main-content-max-width} * 0.25 - #{$search-max-width});
margin-right: calc(
#{$main-content-max-width} * 0.25 - #{$search-max-width}
);
}
#topbar,
@ -1615,7 +1656,9 @@ $sidebar-display: "sidebar-display";
}
#back-to-top {
right: calc((100vw - #{$sidebar-width-large} - #{$main-content-max-width}) / 2 + 2rem);
right: calc(
(100vw - #{$sidebar-width-large} - #{$main-content-max-width}) / 2 + 2rem
);
}
#sidebar {

View file

@ -7,7 +7,7 @@
%heading {
color: var(--heading-color);
font-weight: 400;
font-family: Lato, "Microsoft Yahei", sans-serif;
font-family: Lato, 'Microsoft Yahei', sans-serif;
}
%section {
@ -150,7 +150,7 @@
transform: translateX(-50%);
}
@mixin prompt($type, $fa-content, $fa-style: "solid") {
@mixin prompt($type, $fa-content, $fa-style: 'solid') {
&.prompt-#{$type} {
background-color: var(--prompt-#{$type}-bg);

View file

@ -2,28 +2,28 @@
* The syntax highlight.
*/
@import "colors/light-syntax";
@import "colors/dark-syntax";
@import 'colors/light-syntax';
@import 'colors/dark-syntax';
html {
@media (prefers-color-scheme: light) {
&:not([data-mode]),
&[data-mode="light"] {
&[data-mode='light'] {
@include light-syntax;
}
&[data-mode="dark"] {
&[data-mode='dark'] {
@include dark-syntax;
}
}
@media (prefers-color-scheme: dark) {
&:not([data-mode]),
&[data-mode="dark"] {
&[data-mode='dark'] {
@include dark-syntax;
}
&[data-mode="light"] {
&[data-mode='light'] {
@include light-syntax;
}
}
@ -66,13 +66,6 @@ html {
font-size: $code-font-size;
line-height: 1.4rem;
word-wrap: normal; /* Fixed Safari overflow-x */
/* set the dollar sign to non-selectable */
>.gp:first-child {
-webkit-user-select: none;
-moz-user-select: none;
user-select: none;
}
}
table {
@ -90,12 +83,14 @@ html {
-webkit-user-select: none;
-moz-user-select: none;
-o-user-select: none;
-ms-user-select: none;
user-select: none;
}
} /* .highlight */
code {
-webkit-hyphens: none;
-ms-hyphens: none;
hyphens: none;
&.highlighter-rouge {
@ -147,7 +142,7 @@ td.rouge-code {
/* Hide line numbers for default, console, and terminal code snippets */
div {
&[class^="highlighter-rouge"],
&[class^='highlighter-rouge'],
&.nolineno,
&.language-plaintext.highlighter-rouge,
&.language-console.highlighter-rouge,
@ -176,15 +171,14 @@ div {
$dot-size: 0.75rem;
$dot-margin: 0.5rem;
content: "";
content: '';
display: inline-block;
margin-left: 1rem;
width: $dot-size;
height: $dot-size;
border-radius: 50%;
background-color: var(--code-header-muted-color);
box-shadow:
($dot-size + $dot-margin) 0 0 var(--code-header-muted-color),
box-shadow: ($dot-size + $dot-margin) 0 0 var(--code-header-muted-color),
($dot-size + $dot-margin) * 2 0 0 var(--code-header-muted-color);
}
@ -256,7 +250,7 @@ div {
@media all and (max-width: 576px) {
.post-content {
> div[class^="language-"] {
> div[class^='language-'] {
@include ml-mr(-1.25rem);
border-radius: 0;

View file

@ -13,10 +13,13 @@
--clipboard-checked-color: #2bcc2b;
--filepath-text-color: #bdbdbd;
pre { color: #bfbfbf; } /* override Bootstrap */
/* override Bootstrap */
pre {
color: #bfbfbf;
}
.highlight {
.gp { color: #818c96; }
.highlight .gp {
color: #818c96;
}
/* syntax highlight colors from https://raw.githubusercontent.com/jwarby/pygments-css/master/monokai.css */

View file

@ -4,9 +4,8 @@
@mixin dark-scheme {
/* Framework color */
--body-bg: var(--main-bg);
--mask-bg: rgb(68, 69, 70);
--main-bg: rgb(27, 27, 30);
--mask-bg: rgb(68, 69, 70);
--main-border-color: rgb(44, 45, 45);
/* Common color */
@ -27,8 +26,7 @@
--checkbox-color: rgb(118, 120, 121);
--checkbox-checked-color: var(--link-color);
--img-bg: radial-gradient(circle, rgb(22, 22, 24) 0%, rgb(32, 32, 32) 100%);
--shimmer-bg:
linear-gradient(
--shimmer-bg: linear-gradient(
90deg,
rgba(255, 255, 255, 0) 0%,
rgba(58, 55, 55, 0.4) 50%,
@ -70,7 +68,8 @@
--btn-share-color: #6c757d;
--btn-share-hover-color: #bfc1ca;
--relate-post-date: var(--text-muted-color);
--card-bg: rgb(39, 40, 43);
--card-bg: #212121;
--card-hovor-bg: #3a3a3a;
--card-border-color: rgb(53, 53, 60);
--card-box-shadow: var(--main-bg);
--kbd-wrap-color: #6a6a6a;
@ -117,16 +116,6 @@
border-color: var(--main-border-color);
}
/* posts' toc, override BS */
nav[data-toggle="toc"] .nav-link.active,
nav[data-toggle="toc"] .nav-link.active:focus,
nav[data-toggle="toc"] .nav-link.active:hover,
nav[data-toggle="toc"] .nav > li > a:focus,
nav[data-toggle="toc"] .nav > li > a:hover {
color: var(--toc-highlight) !important;
border-left-color: var(--toc-highlight) !important;
}
/* categories */
.categories.card,
.list-group-item {
@ -151,8 +140,7 @@
}
#archives li:nth-child(odd) {
background-image:
linear-gradient(
background-image: linear-gradient(
to left,
rgb(26, 26, 30),
rgb(39, 39, 45),
@ -164,7 +152,8 @@
color-scheme: dark;
#disqus_thread { /* stylelint-disable-line selector-id-pattern */
/* stylelint-disable-next-line selector-id-pattern */
#disqus_thread {
color-scheme: none;
}
} /* dark-scheme */

View file

@ -76,7 +76,7 @@
--code-header-icon-color: #d1d1d1;
--clipboard-checked-color: #43c743;
[class^="prompt-"] {
[class^='prompt-'] {
--inline-code-bg: #fbfafa;
--highlighter-rouge-color: rgb(82, 82, 82);
}

View file

@ -4,9 +4,8 @@
@mixin light-scheme {
/* Framework color */
--body-bg: #fafafa;
--mask-bg: #c1c3c5;
--main-bg: white;
--mask-bg: #c1c3c5;
--main-border-color: #f3f3f3;
/* Common color */
@ -24,9 +23,12 @@
--btn-box-shadow: #eaeaea;
--checkbox-color: #c5c5c5;
--checkbox-checked-color: #07a8f7;
--img-bg: radial-gradient(circle, rgb(255, 255, 255) 0%, rgb(249, 249, 249) 100%);
--shimmer-bg:
linear-gradient(
--img-bg: radial-gradient(
circle,
rgb(255, 255, 255) 0%,
rgb(249, 249, 249) 100%
);
--shimmer-bg: linear-gradient(
90deg,
rgba(250, 250, 250, 0) 0%,
rgba(232, 230, 230, 1) 50%,
@ -59,8 +61,10 @@
--pin-color: #999fa4;
/* Posts */
--toc-highlight: #563d7c;
--btn-share-hover-color: var(--link-color);
--card-border-color: #f1f1f1;
--card-hovor-bg: #eeeeee;
--card-border-color: #ececec;
--card-box-shadow: rgba(234, 234, 234, 0.76);
--label-color: #616161;
--relate-post-date: rgba(30, 55, 70, 0.4);
@ -85,7 +89,7 @@
--prompt-danger-bg: rgb(248, 215, 218, 0.56);
--prompt-danger-icon-color: #df3c30;
[class^="prompt-"] {
[class^='prompt-'] {
--link-underline-color: rgb(219, 216, 216);
}

View file

@ -1,7 +1,7 @@
/*!
* The styles for Jekyll theme Chirpy
*
* Chirpy v5.5.2 (https://github.com/cotes2020/jekyll-theme-chirpy)
* Chirpy v5.6.0 (https://github.com/cotes2020/jekyll-theme-chirpy)
* © 2019 Cotes Chung
* MIT Licensed
*/

View file

@ -8,7 +8,7 @@
$timeline-width: 4px;
%timeline {
content: "";
content: '';
width: $timeline-width;
position: relative;
float: left;
@ -37,8 +37,9 @@
top: 24px;
}
&::after { /* Year dot */
content: "";
/* Year dot */
&::after {
content: '';
display: inline-block;
position: relative;
border-radius: 50%;
@ -63,7 +64,14 @@
&:nth-child(odd) {
background-color: var(--main-bg, #ffffff);
background-image: linear-gradient(to left, #ffffff, #fbfbfb, #fbfbfb, #fbfbfb, #ffffff);
background-image: linear-gradient(
to left,
#ffffff,
#fbfbfb,
#fbfbfb,
#fbfbfb,
#ffffff
);
}
&::before {
@ -109,7 +117,7 @@
&::before {
/* the dot before post title */
content: "";
content: '';
display: inline-block;
position: relative;
border-radius: 50%;

View file

@ -54,7 +54,8 @@
}
}
@media (hover: hover) { /* only works on desktop */
/* only works on desktop */
@media (hover: hover) {
.category-trigger:hover {
background-color: var(--categories-hover-bg);
}

View file

@ -13,31 +13,35 @@
line-height: 1.5rem;
padding: 0.6rem 0;
&::before { /* dot */
/* dot */
&::before {
background: #999999;
width: 5px;
height: 5px;
border-radius: 50%;
display: block;
content: "";
content: '';
position: relative;
top: 0.6rem;
margin-right: 0.5rem;
}
> a { /* post's title */
/* post's title */
> a {
@extend %no-bottom-border;
font-size: 1.1rem;
}
/* post's date */
> span:last-child {
white-space: nowrap;
} /* post's date */
}
}
}
#page-tag h1 > i { /* tag icon */
/* tag icon */
#page-tag h1 > i {
font-size: 1.2rem;
}

View file

@ -53,16 +53,26 @@
} /* .pagination */
#post-list {
margin-top: 1rem;
margin-top: 1.75rem;
padding-right: 0.5rem;
.post-preview {
padding-top: 1.5rem;
padding-bottom: 1rem;
border-bottom: 1px solid var(--main-border-color);
a:hover {
@extend %link-hover;
text-decoration: none;
}
.post-preview {
padding: 0.25rem;
border-radius: 0.75rem;
border: 1px solid var(--card-border-color);
background: var(--card-bg);
&:hover {
background: var(--card-hovor-bg);
box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
}
&:not(:last-child) {
margin-bottom: 1.75rem;
}
h1 {
@ -103,7 +113,6 @@
color: var(--post-list-text-color);
> p {
/* Make preview shorter on the homepage */
margin: 0;
overflow: hidden;
text-overflow: ellipsis;
@ -115,8 +124,19 @@
} /* .post-preview */
} /* #post-list */
@media (hover: hover) {
.post-preview {
transition: all 0.35s ease-in-out;
}
}
/* Hide SideBar and TOC */
@media all and (max-width: 830px) {
.post-preview {
margin-left: -0.5rem;
margin-right: -0.5rem;
}
.pagination {
justify-content: space-evenly;
@ -131,9 +151,12 @@
/* Sidebar is visible */
@media all and (min-width: 831px) {
#post-list {
margin-top: 1.5rem;
margin-top: 3rem;
.post-preview .post-meta {
.post-preview {
padding: 0.5rem;
.post-meta {
.pin {
background: var(--pin-bg);
border-radius: 5px;
@ -149,6 +172,7 @@
}
}
}
}
.pagination {
font-size: 0.85rem;

View file

@ -17,7 +17,7 @@
}
@mixin dot($pl: 0.25rem, $pr: 0.25rem) {
content: "\2022";
content: '\2022';
padding-left: $pl;
padding-right: $pr;
}
@ -26,17 +26,9 @@
color: var(--text-color);
}
%preview-margin {
margin: 0;
}
.preview-img {
@include align-center;
@extend %preview-margin;
@extend %rounded;
max-width: 100%;
&:not(.no-bg) {
img.lazyloaded {
background: var(--img-bg);
@ -48,7 +40,6 @@
-o-object-fit: cover;
object-fit: cover;
@extend %preview-margin;
@extend %rounded;
}
}
@ -100,6 +91,7 @@ h1 + .post-meta {
vertical-align: middle;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
.share-icons {
@ -259,24 +251,58 @@ h1 + .post-meta {
transition: top 0.2s ease-in-out;
-webkit-animation: fade-up 0.8s;
animation: fade-up 0.8s;
}
#toc {
ul.nav.navbar-nav {
margin: 0.5rem 0;
padding: 0;
ul {
list-style: none;
font-size: 0.85rem;
line-height: 1.25;
padding-left: 0;
li {
padding-top: 2px;
padding-bottom: 2px;
&:not(:last-child) {
margin: 0.4rem 0;
}
a {
padding: 0.2rem 0 0.2rem 1.25rem;
}
}
nav[data-toggle="toc"] {
.nav {
.nav > li > a.active {
font-weight: 600 !important;
/* Overwrite TOC plugin style */
.toc-link {
display: block;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
&:hover {
color: var(--toc-highlight);
text-decoration: none;
}
&::before {
display: none;
}
}
.is-active-link {
color: var(--toc-highlight) !important;
font-weight: 600;
&::before {
display: inline-block;
width: 1px;
left: -1px;
height: 1.25rem;
background-color: var(--toc-highlight) !important;
}
}
ul {
a {
padding-left: 2rem;
}
}
}
}
@ -333,7 +359,8 @@ nav[data-toggle="toc"] {
margin-bottom: 2rem;
}
#disqus_thread { /* stylelint-disable-line selector-id-pattern */
/* stylelint-disable-next-line selector-id-pattern */
#disqus_thread {
min-height: 8.5rem;
}
}
@ -346,7 +373,7 @@ nav[data-toggle="toc"] {
@include label(inherit, 400, inherit);
&::after {
content: ":";
content: ':';
}
}

View file

@ -1,6 +0,0 @@
/*!
* Chirpy v5.5.2 (https://github.com/cotes2020/jekyll-theme-chirpy/)
* © 2019 Cotes Chung
* MIT Licensed
*/
$(function(){$(window).scroll(()=>{50<$(this).scrollTop()&&"none"===$("#sidebar-trigger").css("display")?$("#back-to-top").fadeIn():$("#back-to-top").fadeOut()}),$("#back-to-top").click(()=>($("body,html").animate({scrollTop:0},800),!1))}),$(function(){$(".mode-toggle").click(o=>{o=$(o.target);(o.prop("tagName")==="button".toUpperCase()?o:o.parent()).blur(),flipMode()})});const ScrollHelper=function(){const o=$("body"),e="data-topbar-visible",t=$("#topbar-wrapper").outerHeight();let a=0,r=!1,l=!1;return{hideTopbar:()=>o.attr(e,!1),showTopbar:()=>o.attr(e,!0),addScrollUpTask:()=>{a+=1,r=r||!0},popScrollUpTask:()=>--a,hasScrollUpTask:()=>0<a,topbarLocked:()=>!0===r,unlockTopbar:()=>r=!1,getTopbarHeight:()=>t,orientationLocked:()=>!0===l,lockOrientation:()=>l=!0,unLockOrientation:()=>l=!1}}();$(function(){const o=$("#sidebar-trigger"),e=$("#search-trigger"),t=$("#search-cancel"),a=$("#main"),r=$("#topbar-title"),l=$("#search-wrapper"),n=$("#search-result-wrapper"),s=$("#search-results"),i=$("#search-input"),c=$("#search-hints"),d=function(){let o=0;return{block(){o=window.scrollY,$("html,body").scrollTop(0)},release(){$("html,body").scrollTop(o)},getOffset(){return o}}}(),p={on(){o.addClass("unloaded"),r.addClass("unloaded"),e.addClass("unloaded"),l.addClass("d-flex"),t.addClass("loaded")},off(){t.removeClass("loaded"),l.removeClass("d-flex"),o.removeClass("unloaded"),r.removeClass("unloaded"),e.removeClass("unloaded")}},f=function(){let o=!1;return{on(){o||(d.block(),n.removeClass("unloaded"),a.addClass("unloaded"),o=!0)},off(){o&&(s.empty(),c.hasClass("unloaded")&&c.removeClass("unloaded"),n.addClass("unloaded"),a.removeClass("unloaded"),d.release(),i.val(""),o=!1)},isVisible(){return o}}}();function u(){return t.hasClass("loaded")}e.click(function(){p.on(),f.on(),i.focus()}),t.click(function(){p.off(),f.off()}),i.focus(function(){l.addClass("input-focus")}),i.focusout(function(){l.removeClass("input-focus")}),i.on("input",()=>{""===i.val()?u()?c.removeClass("unloaded"):f.off():(f.on(),u()&&c.addClass("unloaded"))})}),$(function(){var o=function(){const o="sidebar-display";let e=!1;const t=$("body");return{toggle(){!1===e?t.attr(o,""):t.removeAttr(o),e=!e}}}();$("#sidebar-trigger").click(o.toggle),$("#mask").click(o.toggle)}),$(function(){$('[data-toggle="tooltip"]').tooltip()}),$(function(){const e=$("#search-input"),t=ScrollHelper.getTopbarHeight();let o,a=0;function r(){0!==$(window).scrollTop()&&(ScrollHelper.lockOrientation(),ScrollHelper.hideTopbar())}screen.orientation?screen.orientation.onchange=()=>{var o=screen.orientation.type;"landscape-primary"!==o&&"landscape-secondary"!==o||r()}:$(window).on("orientationchange",()=>{$(window).width()<$(window).height()&&r()}),$(window).scroll(()=>{o=o||!0}),setInterval(()=>{o&&(!function(){var o=$(this).scrollTop();if(!(Math.abs(a-o)<=t)){if(o>a)ScrollHelper.hideTopbar(),e.is(":focus")&&e.blur();else if(o+$(window).height()<$(document).height()){if(ScrollHelper.hasScrollUpTask())return;ScrollHelper.topbarLocked()?ScrollHelper.unlockTopbar():ScrollHelper.orientationLocked()?ScrollHelper.unLockOrientation():ScrollHelper.showTopbar()}a=o}}(),o=!1)},250)}),$(function(){var o="div.post>h1:first-of-type",e=$(o);const n=$("#topbar-title");if(0!==e.length&&!e.hasClass("dynamic-title")&&!n.is(":hidden")){const s=n.text().trim();let a=e.text().trim(),r=!1,l=0;($("#page-category").length||$("#page-tag").length)&&/\s/.test(a)&&(a=a.replace(/[0-9]/g,"").trim()),e.offset().top<$(window).scrollTop()&&n.text(a);new IntersectionObserver(o=>{var e,t;r?(t=$(window).scrollTop(),e=l<t,l=t,t=o[0],e?0===t.intersectionRatio&&n.text(a):1===t.intersectionRatio&&n.text(s)):r=!0},{rootMargin:"-48px 0px 0px 0px",threshold:[0,1]}).observe(document.querySelector(o)),n.click(function(){$("body,html").animate({scrollTop:0},800)})}}),$(function(){var o=$(".collapse");o.on("hide.bs.collapse",function(){var o="h_"+$(this).attr("id").substring("l_".length);o&&($(`#${o} .far.fa-folder-open`).attr("class","far fa-folder fa-fw"),$(`#${o} i.fas`).addClass("rotate"),$("#"+o).removeClass("hide-border-bottom"))}),o.on("show.bs.collapse",function(){var o="h_"+$(this).attr("id").substring("l_".length);o&&($(`#${o} .far.fa-folder`).attr("class","far fa-folder-open fa-fw"),$(`#${o} i.fas`).removeClass("rotate"),$("#"+o).addClass("hide-border-bottom"))})});

View file

@ -1,6 +0,0 @@
/*!
* Chirpy v5.5.2 (https://github.com/cotes2020/jekyll-theme-chirpy/)
* © 2019 Cotes Chung
* MIT Licensed
*/
$(function(){$(window).scroll(()=>{50<$(this).scrollTop()&&"none"===$("#sidebar-trigger").css("display")?$("#back-to-top").fadeIn():$("#back-to-top").fadeOut()}),$("#back-to-top").click(()=>($("body,html").animate({scrollTop:0},800),!1))}),$(function(){$(".mode-toggle").click(o=>{o=$(o.target);(o.prop("tagName")==="button".toUpperCase()?o:o.parent()).blur(),flipMode()})});const ScrollHelper=function(){const o=$("body"),e="data-topbar-visible",t=$("#topbar-wrapper").outerHeight();let l=0,r=!1,a=!1;return{hideTopbar:()=>o.attr(e,!1),showTopbar:()=>o.attr(e,!0),addScrollUpTask:()=>{l+=1,r=r||!0},popScrollUpTask:()=>--l,hasScrollUpTask:()=>0<l,topbarLocked:()=>!0===r,unlockTopbar:()=>r=!1,getTopbarHeight:()=>t,orientationLocked:()=>!0===a,lockOrientation:()=>a=!0,unLockOrientation:()=>a=!1}}();$(function(){const o=$("#sidebar-trigger"),e=$("#search-trigger"),t=$("#search-cancel"),l=$("#main"),r=$("#topbar-title"),a=$("#search-wrapper"),n=$("#search-result-wrapper"),s=$("#search-results"),i=$("#search-input"),c=$("#search-hints"),d=function(){let o=0;return{block(){o=window.scrollY,$("html,body").scrollTop(0)},release(){$("html,body").scrollTop(o)},getOffset(){return o}}}(),p={on(){o.addClass("unloaded"),r.addClass("unloaded"),e.addClass("unloaded"),a.addClass("d-flex"),t.addClass("loaded")},off(){t.removeClass("loaded"),a.removeClass("d-flex"),o.removeClass("unloaded"),r.removeClass("unloaded"),e.removeClass("unloaded")}},u=function(){let o=!1;return{on(){o||(d.block(),n.removeClass("unloaded"),l.addClass("unloaded"),o=!0)},off(){o&&(s.empty(),c.hasClass("unloaded")&&c.removeClass("unloaded"),n.addClass("unloaded"),l.removeClass("unloaded"),d.release(),i.val(""),o=!1)},isVisible(){return o}}}();function f(){return t.hasClass("loaded")}e.click(function(){p.on(),u.on(),i.focus()}),t.click(function(){p.off(),u.off()}),i.focus(function(){a.addClass("input-focus")}),i.focusout(function(){a.removeClass("input-focus")}),i.on("input",()=>{""===i.val()?f()?c.removeClass("unloaded"):u.off():(u.on(),f()&&c.addClass("unloaded"))})}),$(function(){var o=function(){const o="sidebar-display";let e=!1;const t=$("body");return{toggle(){!1===e?t.attr(o,""):t.removeAttr(o),e=!e}}}();$("#sidebar-trigger").click(o.toggle),$("#mask").click(o.toggle)}),$(function(){$('[data-toggle="tooltip"]').tooltip()}),$(function(){const e=$("#search-input"),t=ScrollHelper.getTopbarHeight();let o,l=0;function r(){0!==$(window).scrollTop()&&(ScrollHelper.lockOrientation(),ScrollHelper.hideTopbar())}screen.orientation?screen.orientation.onchange=()=>{var o=screen.orientation.type;"landscape-primary"!==o&&"landscape-secondary"!==o||r()}:$(window).on("orientationchange",()=>{$(window).width()<$(window).height()&&r()}),$(window).scroll(()=>{o=o||!0}),setInterval(()=>{o&&(!function(){var o=$(this).scrollTop();if(!(Math.abs(l-o)<=t)){if(o>l)ScrollHelper.hideTopbar(),e.is(":focus")&&e.blur();else if(o+$(window).height()<$(document).height()){if(ScrollHelper.hasScrollUpTask())return;ScrollHelper.topbarLocked()?ScrollHelper.unlockTopbar():ScrollHelper.orientationLocked()?ScrollHelper.unLockOrientation():ScrollHelper.showTopbar()}l=o}}(),o=!1)},250)}),$(function(){var o="div.post>h1:first-of-type",e=$(o);const n=$("#topbar-title");if(0!==e.length&&!e.hasClass("dynamic-title")&&!n.is(":hidden")){const s=n.text().trim();let l=e.text().trim(),r=!1,a=0;($("#page-category").length||$("#page-tag").length)&&/\s/.test(l)&&(l=l.replace(/[0-9]/g,"").trim()),e.offset().top<$(window).scrollTop()&&n.text(l);new IntersectionObserver(o=>{var e,t;r?(t=$(window).scrollTop(),e=a<t,a=t,t=o[0],e?0===t.intersectionRatio&&n.text(l):1===t.intersectionRatio&&n.text(s)):r=!0},{rootMargin:"-48px 0px 0px 0px",threshold:[0,1]}).observe(document.querySelector(o)),n.click(function(){$("body,html").animate({scrollTop:0},800)})}});

View file

@ -1,6 +0,0 @@
/*!
* Chirpy v5.5.2 (https://github.com/cotes2020/jekyll-theme-chirpy/)
* © 2019 Cotes Chung
* MIT Licensed
*/
$(function(){$(window).scroll(()=>{50<$(this).scrollTop()&&"none"===$("#sidebar-trigger").css("display")?$("#back-to-top").fadeIn():$("#back-to-top").fadeOut()}),$("#back-to-top").click(()=>($("body,html").animate({scrollTop:0},800),!1))}),$(function(){$(".mode-toggle").click(e=>{e=$(e.target);(e.prop("tagName")==="button".toUpperCase()?e:e.parent()).blur(),flipMode()})});const ScrollHelper=function(){const e=$("body"),t="data-topbar-visible",o=$("#topbar-wrapper").outerHeight();let a=0,l=!1,r=!1;return{hideTopbar:()=>e.attr(t,!1),showTopbar:()=>e.attr(t,!0),addScrollUpTask:()=>{a+=1,l=l||!0},popScrollUpTask:()=>--a,hasScrollUpTask:()=>0<a,topbarLocked:()=>!0===l,unlockTopbar:()=>l=!1,getTopbarHeight:()=>o,orientationLocked:()=>!0===r,lockOrientation:()=>r=!0,unLockOrientation:()=>r=!1}}(),LocaleHelper=($(function(){const e=$("#sidebar-trigger"),t=$("#search-trigger"),o=$("#search-cancel"),a=$("#main"),l=$("#topbar-title"),r=$("#search-wrapper"),n=$("#search-result-wrapper"),s=$("#search-results"),i=$("#search-input"),c=$("#search-hints"),d=function(){let e=0;return{block(){e=window.scrollY,$("html,body").scrollTop(0)},release(){$("html,body").scrollTop(e)},getOffset(){return e}}}(),p={on(){e.addClass("unloaded"),l.addClass("unloaded"),t.addClass("unloaded"),r.addClass("d-flex"),o.addClass("loaded")},off(){o.removeClass("loaded"),r.removeClass("d-flex"),e.removeClass("unloaded"),l.removeClass("unloaded"),t.removeClass("unloaded")}},u=function(){let e=!1;return{on(){e||(d.block(),n.removeClass("unloaded"),a.addClass("unloaded"),e=!0)},off(){e&&(s.empty(),c.hasClass("unloaded")&&c.removeClass("unloaded"),n.addClass("unloaded"),a.removeClass("unloaded"),d.release(),i.val(""),e=!1)},isVisible(){return e}}}();function f(){return o.hasClass("loaded")}t.click(function(){p.on(),u.on(),i.focus()}),o.click(function(){p.off(),u.off()}),i.focus(function(){r.addClass("input-focus")}),i.focusout(function(){r.removeClass("input-focus")}),i.on("input",()=>{""===i.val()?f()?c.removeClass("unloaded"):u.off():(u.on(),f()&&c.addClass("unloaded"))})}),$(function(){var e=function(){const e="sidebar-display";let t=!1;const o=$("body");return{toggle(){!1===t?o.attr(e,""):o.removeAttr(e),t=!t}}}();$("#sidebar-trigger").click(e.toggle),$("#mask").click(e.toggle)}),$(function(){$('[data-toggle="tooltip"]').tooltip()}),$(function(){const t=$("#search-input"),o=ScrollHelper.getTopbarHeight();let e,a=0;function l(){0!==$(window).scrollTop()&&(ScrollHelper.lockOrientation(),ScrollHelper.hideTopbar())}screen.orientation?screen.orientation.onchange=()=>{var e=screen.orientation.type;"landscape-primary"!==e&&"landscape-secondary"!==e||l()}:$(window).on("orientationchange",()=>{$(window).width()<$(window).height()&&l()}),$(window).scroll(()=>{e=e||!0}),setInterval(()=>{e&&(!function(){var e=$(this).scrollTop();if(!(Math.abs(a-e)<=o)){if(e>a)ScrollHelper.hideTopbar(),t.is(":focus")&&t.blur();else if(e+$(window).height()<$(document).height()){if(ScrollHelper.hasScrollUpTask())return;ScrollHelper.topbarLocked()?ScrollHelper.unlockTopbar():ScrollHelper.orientationLocked()?ScrollHelper.unLockOrientation():ScrollHelper.showTopbar()}a=e}}(),e=!1)},250)}),$(function(){var e="div.post>h1:first-of-type",t=$(e);const n=$("#topbar-title");if(0!==t.length&&!t.hasClass("dynamic-title")&&!n.is(":hidden")){const s=n.text().trim();let a=t.text().trim(),l=!1,r=0;($("#page-category").length||$("#page-tag").length)&&/\s/.test(a)&&(a=a.replace(/[0-9]/g,"").trim()),t.offset().top<$(window).scrollTop()&&n.text(a);new IntersectionObserver(e=>{var t,o;l?(o=$(window).scrollTop(),t=r<o,r=o,o=e[0],t?0===o.intersectionRatio&&n.text(a):1===o.intersectionRatio&&n.text(s)):l=!0},{rootMargin:"-48px 0px 0px 0px",threshold:[0,1]}).observe(document.querySelector(e)),n.click(function(){$("body,html").animate({scrollTop:0},800)})}}),function(){const e=$("html").attr("lang").substr(0,2),t="data-ts",o="data-df";return{locale:()=>e,attrTimestamp:()=>t,attrDateFormat:()=>o,getTimestamp:e=>Number(e.attr(t)),getDateFormat:e=>e.attr(o)}}());$(function(){dayjs.locale(LocaleHelper.locale()),dayjs.extend(window.dayjs_plugin_localizedFormat),$(`[${LocaleHelper.attrTimestamp()}]`).each(function(){var e=dayjs.unix(LocaleHelper.getTimestamp($(this))),t=e.format(LocaleHelper.getDateFormat($(this))),t=($(this).text(t),$(this).removeAttr(LocaleHelper.attrTimestamp()),$(this).removeAttr(LocaleHelper.attrDateFormat()),$(this).attr("data-toggle"));void 0!==t&&"tooltip"===t&&(t=e.format("llll"),$(this).attr("data-original-title",t))})});

View file

@ -1,6 +0,0 @@
/*!
* Chirpy v5.5.2 (https://github.com/cotes2020/jekyll-theme-chirpy/)
* © 2019 Cotes Chung
* MIT Licensed
*/
$(function(){$(window).scroll(()=>{50<$(this).scrollTop()&&"none"===$("#sidebar-trigger").css("display")?$("#back-to-top").fadeIn():$("#back-to-top").fadeOut()}),$("#back-to-top").click(()=>($("body,html").animate({scrollTop:0},800),!1))}),$(function(){$(".mode-toggle").click(e=>{e=$(e.target);(e.prop("tagName")==="button".toUpperCase()?e:e.parent()).blur(),flipMode()})});const ScrollHelper=function(){const e=$("body"),t="data-topbar-visible",o=$("#topbar-wrapper").outerHeight();let a=0,l=!1,r=!1;return{hideTopbar:()=>e.attr(t,!1),showTopbar:()=>e.attr(t,!0),addScrollUpTask:()=>{a+=1,l=l||!0},popScrollUpTask:()=>--a,hasScrollUpTask:()=>0<a,topbarLocked:()=>!0===l,unlockTopbar:()=>l=!1,getTopbarHeight:()=>o,orientationLocked:()=>!0===r,lockOrientation:()=>r=!0,unLockOrientation:()=>r=!1}}(),LocaleHelper=($(function(){const e=$("#sidebar-trigger"),t=$("#search-trigger"),o=$("#search-cancel"),a=$("#main"),l=$("#topbar-title"),r=$("#search-wrapper"),n=$("#search-result-wrapper"),s=$("#search-results"),i=$("#search-input"),c=$("#search-hints"),d=function(){let e=0;return{block(){e=window.scrollY,$("html,body").scrollTop(0)},release(){$("html,body").scrollTop(e)},getOffset(){return e}}}(),p={on(){e.addClass("unloaded"),l.addClass("unloaded"),t.addClass("unloaded"),r.addClass("d-flex"),o.addClass("loaded")},off(){o.removeClass("loaded"),r.removeClass("d-flex"),e.removeClass("unloaded"),l.removeClass("unloaded"),t.removeClass("unloaded")}},u=function(){let e=!1;return{on(){e||(d.block(),n.removeClass("unloaded"),a.addClass("unloaded"),e=!0)},off(){e&&(s.empty(),c.hasClass("unloaded")&&c.removeClass("unloaded"),n.addClass("unloaded"),a.removeClass("unloaded"),d.release(),i.val(""),e=!1)},isVisible(){return e}}}();function f(){return o.hasClass("loaded")}t.click(function(){p.on(),u.on(),i.focus()}),o.click(function(){p.off(),u.off()}),i.focus(function(){r.addClass("input-focus")}),i.focusout(function(){r.removeClass("input-focus")}),i.on("input",()=>{""===i.val()?f()?c.removeClass("unloaded"):u.off():(u.on(),f()&&c.addClass("unloaded"))})}),$(function(){var e=function(){const e="sidebar-display";let t=!1;const o=$("body");return{toggle(){!1===t?o.attr(e,""):o.removeAttr(e),t=!t}}}();$("#sidebar-trigger").click(e.toggle),$("#mask").click(e.toggle)}),$(function(){$('[data-toggle="tooltip"]').tooltip()}),$(function(){const t=$("#search-input"),o=ScrollHelper.getTopbarHeight();let e,a=0;function l(){0!==$(window).scrollTop()&&(ScrollHelper.lockOrientation(),ScrollHelper.hideTopbar())}screen.orientation?screen.orientation.onchange=()=>{var e=screen.orientation.type;"landscape-primary"!==e&&"landscape-secondary"!==e||l()}:$(window).on("orientationchange",()=>{$(window).width()<$(window).height()&&l()}),$(window).scroll(()=>{e=e||!0}),setInterval(()=>{e&&(!function(){var e=$(this).scrollTop();if(!(Math.abs(a-e)<=o)){if(e>a)ScrollHelper.hideTopbar(),t.is(":focus")&&t.blur();else if(e+$(window).height()<$(document).height()){if(ScrollHelper.hasScrollUpTask())return;ScrollHelper.topbarLocked()?ScrollHelper.unlockTopbar():ScrollHelper.orientationLocked()?ScrollHelper.unLockOrientation():ScrollHelper.showTopbar()}a=e}}(),e=!1)},250)}),$(function(){var e="div.post>h1:first-of-type",t=$(e);const n=$("#topbar-title");if(0!==t.length&&!t.hasClass("dynamic-title")&&!n.is(":hidden")){const s=n.text().trim();let a=t.text().trim(),l=!1,r=0;($("#page-category").length||$("#page-tag").length)&&/\s/.test(a)&&(a=a.replace(/[0-9]/g,"").trim()),t.offset().top<$(window).scrollTop()&&n.text(a);new IntersectionObserver(e=>{var t,o;l?(o=$(window).scrollTop(),t=r<o,r=o,o=e[0],t?0===o.intersectionRatio&&n.text(a):1===o.intersectionRatio&&n.text(s)):l=!0},{rootMargin:"-48px 0px 0px 0px",threshold:[0,1]}).observe(document.querySelector(e)),n.click(function(){$("body,html").animate({scrollTop:0},800)})}}),function(){const e=$("html").attr("lang").substr(0,2),t="data-ts",o="data-df";return{locale:()=>e,attrTimestamp:()=>t,attrDateFormat:()=>o,getTimestamp:e=>Number(e.attr(t)),getDateFormat:e=>e.attr(o)}}());$(function(){dayjs.locale(LocaleHelper.locale()),dayjs.extend(window.dayjs_plugin_localizedFormat),$(`[${LocaleHelper.attrTimestamp()}]`).each(function(){var e=dayjs.unix(LocaleHelper.getTimestamp($(this))),t=e.format(LocaleHelper.getDateFormat($(this))),t=($(this).text(t),$(this).removeAttr(LocaleHelper.attrTimestamp()),$(this).removeAttr(LocaleHelper.attrDateFormat()),$(this).attr("data-toggle"));void 0!==t&&"tooltip"===t&&(t=e.format("llll"),$(this).attr("data-original-title",t))})});

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1,6 +0,0 @@
/*!
* Chirpy v5.5.2 (https://github.com/cotes2020/jekyll-theme-chirpy/)
* © 2019 Cotes Chung
* MIT Licensed
*/
const getInitStatus=function(){let t=!1;return()=>{var e=t;return t=t||!0,e}}(),PvOpts=function(){function t(e){return $(e).attr("content")}function e(e){e=t(e);return void 0!==e&&!1!==e}return{getProxyMeta(){return t("meta[name=pv-proxy-endpoint]")},getLocalMeta(){return t("meta[name=pv-cache-path]")},hasProxyMeta(){return e("meta[name=pv-proxy-endpoint]")},hasLocalMeta(){return e("meta[name=pv-cache-path]")}}}(),PvStorage=function(){const a={KEY_PV:"pv",KEY_PV_SRC:"pv_src",KEY_CREATION:"pv_created_date"},t={LOCAL:"same-origin",PROXY:"cors"};function r(e){return localStorage.getItem(e)}function o(e,t){localStorage.setItem(e,t)}function n(e,t){o(a.KEY_PV,e),o(a.KEY_PV_SRC,t),o(a.KEY_CREATION,(new Date).toJSON())}return{keysCount(){return Object.keys(a).length},hasCache(){return null!==localStorage.getItem(a.KEY_PV)},getCache(){return JSON.parse(localStorage.getItem(a.KEY_PV))},saveLocalCache(e){n(e,t.LOCAL)},saveProxyCache(e){n(e,t.PROXY)},isExpired(){var e=new Date(r(a.KEY_CREATION));return e.setHours(e.getHours()+1),Date.now()>=e.getTime()},isFromLocal(){return r(a.KEY_PV_SRC)===t.LOCAL},isFromProxy(){return r(a.KEY_PV_SRC)===t.PROXY},newerThan(e){return PvStorage.getCache().totalsForAllResults["ga:pageviews"]>e.totalsForAllResults["ga:pageviews"]},inspectKeys(){if(localStorage.length!==PvStorage.keysCount())localStorage.clear();else for(let e=0;e<localStorage.length;e++)switch(localStorage.key(e)){case a.KEY_PV:case a.KEY_PV_SRC:case a.KEY_CREATION:break;default:return void localStorage.clear()}}}}();function countUp(e,t,a){e<t&&((a=new CountUp(a,e,t)).error?console.error(a.error):a.start())}function countPV(t,a){let r=0;if(void 0!==a)for(let e=0;e<a.length;++e)if(a[parseInt(e,10)][0]===t){r+=parseInt(a[parseInt(e,10)][1],10);break}return r}function tacklePV(e,t,a,r){t=0===(t=countPV(t,e))?1:t;r?(e=parseInt(a.text().replace(/,/g,""),10))<t&&countUp(e,t,a.attr("id")):a.text((new Intl.NumberFormat).format(t))}function displayPageviews(e){if(void 0!==e){let t=getInitStatus();const a=e.rows;0<$("#post-list").length?$(".post-preview").each(function(){var e=$(this).find("a").attr("href");tacklePV(a,e,$(this).find(".pageviews"),t)}):0<$(".post").length&&(e=window.location.pathname,tacklePV(a,e,$("#pv"),t))}}function fetchProxyPageviews(){PvOpts.hasProxyMeta()&&$.ajax({type:"GET",url:PvOpts.getProxyMeta(),dataType:"jsonp",jsonpCallback:"displayPageviews",success:e=>{PvStorage.saveProxyCache(JSON.stringify(e))},error:(e,t,a)=>{console.log("Failed to load pageviews from proxy server: "+a)}})}function fetchLocalPageviews(t=!1){return fetch(PvOpts.getLocalMeta()).then(e=>e.json()).then(e=>{t&&PvStorage.isFromProxy()&&PvStorage.newerThan(e)||(displayPageviews(e),PvStorage.saveLocalCache(JSON.stringify(e)))})}$(function(){$(".pageviews").length<=0||(PvStorage.inspectKeys(),PvStorage.hasCache()?(displayPageviews(PvStorage.getCache()),PvStorage.isExpired()?PvOpts.hasLocalMeta()?fetchLocalPageviews(!0).then(fetchProxyPageviews):fetchProxyPageviews():PvStorage.isFromLocal()&&fetchProxyPageviews()):PvOpts.hasLocalMeta()?fetchLocalPageviews().then(fetchProxyPageviews):fetchProxyPageviews())});

@ -1 +1 @@
Subproject commit e372141074f370c6f03b68b5264e7663f2b7477c
Subproject commit 24ebdb708f3f5451df953cb5f9deb3ad4433404a

View file

@ -1,10 +0,0 @@
#!/usr/bin/env node
"use strict";
const js = require('./tasks/js');
exports.default = js.build;
/* keep-alive develop mode, without uglify */
exports.dev = js.liveRebuild;

View file

@ -1,101 +0,0 @@
#!/usr/bin/env node
"use strict";
const { src, dest, watch, series, parallel} = require('gulp');
const concat = require('gulp-concat');
const rename = require("gulp-rename");
const uglify = require('gulp-uglify');
const insert = require('gulp-insert');
const fs = require('fs');
const JS_SRC = '_javascript';
const JS_DEST = `assets/js/dist`;
function concatJs(files, output) {
return src(files)
.pipe(concat(output))
.pipe(rename({ extname: '.min.js' }))
.pipe(dest(JS_DEST));
}
function minifyJs() {
return src(`${ JS_DEST }/*.js`)
.pipe(insert.prepend(fs.readFileSync(`${ JS_SRC }/copyright`, 'utf8')))
.pipe(uglify({output: {comments: /^!|@preserve|@license|@cc_on/i}}))
.pipe(insert.append('\n'))
.pipe(dest(JS_DEST));
}
const commonsJs = () => {
return concatJs(`${JS_SRC}/commons/*.js`, 'commons');
};
const homeJs = () => {
return concatJs([
`${JS_SRC}/commons/*.js`,
`${JS_SRC}/utils/locale-datetime.js`
],
'home'
);
};
const postJs = () => {
return concatJs([
`${JS_SRC}/commons/*.js`,
`${JS_SRC}/utils/img-extra.js`,
`${JS_SRC}/utils/locale-datetime.js`,
`${JS_SRC}/utils/clipboard.js`,
// 'smooth-scroll.js' must be called after ToC is ready
`${JS_SRC}/utils/smooth-scroll.js`
], 'post'
);
};
const categoriesJs = () => {
return concatJs([
`${JS_SRC}/commons/*.js`,
`${JS_SRC}/utils/category-collapse.js`
], 'categories'
);
};
const pageJs = () => {
return concatJs([
`${JS_SRC}/commons/*.js`,
`${JS_SRC}/utils/img-extra.js`,
`${JS_SRC}/utils/clipboard.js`,
`${JS_SRC}/utils/smooth-scroll.js`
], 'page'
);
};
const miscJs = () => {
return concatJs([
`${JS_SRC}/commons/*.js`,
`${JS_SRC}/utils/locale-datetime.js`
], 'misc'
);
};
// GA pageviews report
const pvreportJs = () => {
return concatJs(`${JS_SRC}/utils/pageviews.js`, 'pvreport');
};
const buildJs = parallel(
commonsJs, homeJs, postJs, categoriesJs, pageJs, miscJs, pvreportJs);
exports.build = series(buildJs, minifyJs);
exports.liveRebuild = () => {
buildJs();
watch([
`${ JS_SRC }/commons/*.js`,
`${ JS_SRC }/utils/*.js`
],
buildJs
);
};

View file

@ -2,11 +2,11 @@
Gem::Specification.new do |spec|
spec.name = "jekyll-theme-chirpy"
spec.version = "5.5.2"
spec.version = "5.6.0"
spec.authors = ["Cotes Chung"]
spec.email = ["cotes.chung@gmail.com"]
spec.summary = "Chirpy is a minimal, sidebar, responsive web design Jekyll theme that focuses on text presentation."
spec.summary = "A minimal, responsive and feature-rich Jekyll theme for technical writing."
spec.homepage = "https://github.com/cotes2020/jekyll-theme-chirpy"
spec.license = "MIT"

View file

@ -1,7 +1,7 @@
{
"name": "jekyll-theme-chirpy",
"version": "5.5.2",
"description": "A minimal, responsive, and powerful Jekyll theme for presenting professional writing.",
"version": "5.6.0",
"description": "A minimal, responsive and feature-rich Jekyll theme for technical writing.",
"repository": {
"type": "git",
"url": "git+https://github.com/cotes2020/jekyll-theme-chirpy.git"
@ -11,19 +11,25 @@
"bugs": {
"url": "https://github.com/cotes2020/jekyll-theme-chirpy/issues"
},
"homepage": "https://github.com/cotes2020/jekyll-theme-chirpy#readme",
"homepage": "https://github.com/cotes2020/jekyll-theme-chirpy/",
"scripts": {
"prebuild": "npx rimraf assets/js/dist",
"build": "NODE_ENV=production npx rollup -c --bundleConfigAsCjs",
"prewatch": "npx rimraf assets/js/dist",
"watch": "npx rollup -c --bundleConfigAsCjs -w",
"test": "npx stylelint _sass/**/*.scss",
"fixlint": "npx stylelint _sass/**/*.scss --fix"
"fixlint": "npm run test -- --fix"
},
"devDependencies": {
"gulp": "^4.0.2",
"gulp-concat": "^2.6.1",
"gulp-insert": "^0.5.0",
"gulp-rename": "^2.0.0",
"gulp-uglify": "^3.0.2",
"stylelint": "^14.14.0",
"stylelint-config-standard-scss": "^5.0.0",
"uglify-js": "^3.14.3"
"@babel/core": "^7.21.0",
"@babel/plugin-proposal-class-properties": "^7.18.6",
"@babel/preset-env": "^7.20.2",
"@rollup/plugin-babel": "^6.0.3",
"@rollup/plugin-terser": "^0.4.0",
"rimraf": "^4.4.0",
"rollup": "^3.19.1",
"rollup-plugin-license": "^3.0.1",
"stylelint": "^15.2.0",
"stylelint-config-standard-scss": "^7.0.1"
}
}

Some files were not shown because too many files have changed in this diff Show more