diff --git a/.browserslistrc b/.browserslistrc new file mode 100644 index 0000000..afe4650 --- /dev/null +++ b/.browserslistrc @@ -0,0 +1,5 @@ +# https://github.com/browserslist/browserslist#browserslistrc + +last 2 versions +> 0.2% +not dead diff --git a/.editorconfig b/.editorconfig index f27e9a9..2b740bf 100644 --- a/.editorconfig +++ b/.editorconfig @@ -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 diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index ef81bb0..54e7a6f 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -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/` / `fix/`). -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/` or `fix/`). +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. - -### Inline Scripts - -If you need to add comments to the inline JavaScript (the code between the HTML tags ``), 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: +This project has [CI][ci] turned on. In order for your [PR][gh-pr] to pass the test, +please read the following. +### 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 diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 7c79a52..c69cfb3 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -3,21 +3,20 @@ name: Bug Report about: Create a report to help us improve --- - +**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. - - -- [ ] 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 -### To Reproduce +## To Reproduce Steps to reproduce the behavior: -### Expected behavior +## Expected behavior -### Screenshots +## Logs/Screenshots - + -### Environment +## Environment | Command | Version | |-----------------------------------|---------| @@ -45,21 +44,25 @@ Steps to reproduce the behavior: | `bundle exec jekyll -v` | | | `bundle info jekyll-theme-chirpy` | | + + -### Additional context +## Additional context diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index 7d866cf..0b8cb58 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -4,16 +4,14 @@ about: Suggest an idea for this project labels: enhancement --- - +**NOTE:** Before you start, the following should be completed. -## Checklist - - -- [ ] 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 diff --git a/.github/ISSUE_TEMPLATE/help_wanted.md b/.github/ISSUE_TEMPLATE/help_wanted.md new file mode 100644 index 0000000..180240e --- /dev/null +++ b/.github/ISSUE_TEMPLATE/help_wanted.md @@ -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 + + + +## Operations you have already tried + + + +## Logs/Screenshots + + diff --git a/.github/ISSUE_TEMPLATE/question.md b/.github/ISSUE_TEMPLATE/question.md index c38a93a..fb6c1d7 100644 --- a/.github/ISSUE_TEMPLATE/question.md +++ b/.github/ISSUE_TEMPLATE/question.md @@ -1,19 +1,19 @@ --- name: Question -about: Ask whatever you want +about: Issues that differ from other templates labels: question --- - +**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]. - - -- [ ] 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 diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index d26834f..8e41f4f 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -32,11 +32,3 @@ Please describe the tests that you ran to verify your changes. Provide instructi - Ruby version: - Bundler version: - Jekyll version: - -### Checklist - - -- [ ] 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 diff --git a/.github/SECURITY.md b/.github/SECURITY.md new file mode 100644 index 0000000..5704dc7 --- /dev/null +++ b/.github/SECURITY.md @@ -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! diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fcfc88a..b51c183 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -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: @@ -26,7 +26,7 @@ jobs: - name: Checkout uses: actions/checkout@v3 with: - fetch-depth: 0 # for posts's lastmod + fetch-depth: 0 # for posts's lastmod - name: Setup Ruby uses: ruby/setup-ruby@v1 @@ -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 diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000..dda3e8e --- /dev/null +++ b/.github/workflows/codeql.yml @@ -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 }}" diff --git a/.github/workflows/issue-interceptor.yml b/.github/workflows/issue-interceptor.yml deleted file mode 100644 index 22bafdc..0000000 --- a/.github/workflows/issue-interceptor.yml +++ /dev/null @@ -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" diff --git a/.github/workflows/pages-deploy.yml.hook b/.github/workflows/pages-deploy.yml.hook index 356d42a..a0de59d 100644 --- a/.github/workflows/pages-deploy.yml.hook +++ b/.github/workflows/pages-deploy.yml.hook @@ -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 diff --git a/.github/workflows/style-lint.yml b/.github/workflows/style-lint.yml index 2f1cdd0..9500ac4 100644 --- a/.github/workflows/style-lint.yml +++ b/.github/workflows/style-lint.yml @@ -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: diff --git a/.gitignore b/.gitignore index 9735d33..0124b68 100644 --- a/.gitignore +++ b/.gitignore @@ -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 diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..36b3563 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,3 @@ +{ + "trailingComma": "none" +} diff --git a/.stylelintrc.json b/.stylelintrc.json index 66c0ffb..09b3c87 100644 --- a/.stylelintrc.json +++ b/.stylelintrc.json @@ -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"] } + ] } } diff --git a/CHANGELOG.md b/CHANGELOG.md index 4864fbc..43f0011 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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) diff --git a/README.md b/README.md index 11774ad..9b36789 100644 --- a/README.md +++ b/README.md @@ -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,97 +10,86 @@ [![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] ## Features -- Dark/Light Theme Mode -- Localized UI language -- Pinned Posts -- Hierarchical Categories -- Trending Tags -- Table of Contents -- Last Modified Date of Posts -- Syntax Highlighting -- Mathematical Expressions -- Mermaid Diagram & Flowchart -- Dark/Light Mode Images -- Embed Videos -- Disqus/Utterances/Giscus Comments -- Search -- Atom Feeds -- Google Analytics -- Page Views Reporting -- SEO & Performance Optimization +
+ + Click to view features + +

-## Quick Start + - Dark / Light Theme Mode + - Localized UI language + - Pinned Posts + - Hierarchical Categories + - Trending Tags + - Table of Contents + - Last Modified Date of Posts + - Syntax Highlighting + - Mathematical Expressions + - Mermaid Diagram & Flowchart + - Dark / Light Mode Images + - Embed Videos + - Disqus / Utterances / Giscus Comments + - Search + - Atom Feeds + - Google Analytics + - Page Views Reporting + - SEO & Performance Optimization -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 `.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 . +

+
## 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. - - -[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 diff --git a/_config.yml b/_config.yml index 81090d3..25ab1a4 100644 --- a/_config.yml +++ b/_config.yml @@ -5,60 +5,59 @@ 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 # jekyll-seo-tag settings › https://github.com/jekyll/jekyll-seo-tag/blob/master/docs/usage.md # ↓ -------------------------- -title: Chirpy # the main title +title: Chirpy # the main title -tagline: A text-focused Jekyll theme # it will display as the sub-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. +description: >- # used by seo meta and the atom feed + 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 + username: github_username # change to your github username twitter: - username: twitter_username # change to your twitter username + username: twitter_username # change to your twitter username social: # Change to your full name. # It will be displayed as the default author of the posts and the copyright owner in the Footer name: your_full_name - email: example@domain.com # change to your email address + email: example@domain.com # change to your email address links: # The first element serves as the copyright owner's link - - https://twitter.com/username # change to your twitter homepage - - https://github.com/username # change to your github homepage + - https://twitter.com/username # change to your twitter homepage + - https://github.com/username # change to your github homepage # Uncomment below to add more social links # - https://www.facebook.com/username # - https://www.linkedin.com/in/username -google_site_verification: # fill in to your verification string +google_site_verification: # fill in to your verification string # ↑ -------------------------- # The end of `jekyll-seo-tag` settings google_analytics: - id: # fill in your Google Analytics ID + id: # fill in your Google Analytics ID # Google Analytics pageviews report settings pv: - proxy_endpoint: # fill in the Google Analytics superProxy endpoint of Google App Engine - cache_path: # the local PV cache data, friendly to visitors from GFW region + proxy_endpoint: # fill in the Google Analytics superProxy endpoint of Google App Engine + cache_path: # the local PV cache data, friendly to visitors from GFW region # Prefer color scheme setting. # @@ -71,51 +70,51 @@ google_analytics: # light - Use the light color scheme # dark - Use the dark color scheme # -theme_mode: # [light|dark] +theme_mode: # [light|dark] # The CDN endpoint for images. # Notice that once it is assigned, the CDN url # 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: - active: # The global switch for posts comments, e.g., 'disqus'. Keep it empty means disable + active: # The global switch for posts comments, e.g., 'disqus'. Keep it empty means disable # The active options are as follows: disqus: - shortname: # fill with the Disqus shortname. › https://help.disqus.com/en/articles/1717111-what-s-a-shortname + shortname: # fill with the Disqus shortname. › https://help.disqus.com/en/articles/1717111-what-s-a-shortname # utterances settings › https://utteranc.es/ utterances: - repo: # / - issue_term: # < url | pathname | title | ...> + repo: # / + issue_term: # < url | pathname | title | ...> # Giscus options › https://giscus.app giscus: - repo: # / + repo: # / repo_id: category: category_id: - mapping: # optional, default to 'pathname' - input_position: # optional, default to 'bottom' - lang: # optional, default to the value of `site.lang` + mapping: # optional, default to 'pathname' + input_position: # optional, default to 'bottom' + lang: # optional, default to the value of `site.lang` reactions_enabled: # optional, default to the value of `1` # Self-hosted static assets, optional › https://github.com/cotes2020/chirpy-static-assets assets: self_host: - enabled: # boolean, keep empty means false + enabled: # boolean, keep empty means false # specify the Jekyll environment, empty means both # only works if `assets.self_host.enabled` is 'true' - env: # [development|production] + env: # [development|production] pwa: - enabled: true # the option for PWA feature + enabled: true # the option for PWA feature paginate: 10 @@ -123,7 +122,7 @@ paginate: 10 kramdown: syntax_highlighter: rouge - syntax_highlighter_opts: # Rouge Options › https://github.com/jneen/rouge#full-options + syntax_highlighter_opts: # Rouge Options › https://github.com/jneen/rouge#full-options css_class: highlight # default_lang: console span: @@ -139,12 +138,12 @@ 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 - comments: true # Enable comments in posts. - toc: true # Display TOC column in posts. + comments: true # Enable comments in posts. + toc: true # Display TOC column in posts. # DO NOT modify the following parameter unless you are confident enough # to update the code of all other post links in this project. permalink: /posts/:title/ @@ -153,8 +152,8 @@ defaults: values: comments: false - scope: - path: '' - type: tabs # see `site.collections` + path: "" + type: tabs # see `site.collections` values: layout: page permalink: /:title/ @@ -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 diff --git a/_data/assets/cross_origin.yml b/_data/assets/cross_origin.yml index a3a8dfb..01d558f 100644 --- a/_data/assets/cross_origin.yml +++ b/_data/assets/cross_origin.yml @@ -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 diff --git a/_data/assets/self_host.yml b/_data/assets/self_host.yml index 817c78e..3327e48 100644 --- a/_data/assets/self_host.yml +++ b/_data/assets/self_host.yml @@ -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 diff --git a/_data/contact.yml b/_data/contact.yml index a94e555..76b667a 100644 --- a/_data/contact.yml +++ b/_data/contact.yml @@ -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 # 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 # url: '' # Fill with your Linkedin homepage -# - -# type: stack-overflow +# +# - type: stack-overflow # icon: 'fab fa-stack-overflow' # url: '' # Fill with your stackoverflow homepage diff --git a/_data/locales/el-GR.yml b/_data/locales/el-GR.yml new file mode 100644 index 0000000..fbc7c2b --- /dev/null +++ b/_data/locales/el-GR.yml @@ -0,0 +1,93 @@ +# The layout text of site + +# ----- Commons label ----- + +layout: + post: Δημοσίευση + category: Κατηγορία + tag: Ετικέτα + +# The tabs of sidebar +tabs: + # format: : + 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: , +df: + post: + strftime: '%b %e, %Y' + dayjs: 'll' + archives: + strftime: '%b' + dayjs: 'MMM' + +# categories page +categories: + category_measure: + singular: Κατηγορία + plural: Κατηγορίες + post_measure: + singular: Δημοσίευση + plural: Δημοσιεύσεις \ No newline at end of file diff --git a/_data/share.yml b/_data/share.yml index 1206f4d..c1d4d63 100644 --- a/_data/share.yml +++ b/_data/share.yml @@ -2,26 +2,24 @@ # Icons from 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" diff --git a/_includes/head.html b/_includes/head.html index e2f6336..edec4b1 100644 --- a/_includes/head.html +++ b/_includes/head.html @@ -1,13 +1,17 @@ - + - + + + + + {% if page.layout == 'home' or page.layout == 'post' %} - {% if site.google_analytics.pv.proxy_endpoint %} {% endif %} @@ -15,7 +19,6 @@ {% if site.google_analytics.pv.cache_path %} {% endif %} - {% endif %} {% capture seo_tags %} @@ -40,40 +43,34 @@ {% endif %} {% assign seo_tags = seo_tags | replace: target, replacement %} - {% endunless %} - {% endif %} {{ seo_tags }} - {%- unless page.layout == "home" -%} - {{ page.title | append: " | "}} - {%- endunless -%} + {%- unless page.layout == 'home' -%} + {{ page.title | append: ' | ' }} + {%- endunless -%} {{ site.title }} {% include favicons.html %} {% if site.resources.ignore_env != jekyll.environment and site.resources.self_hosted %} - {% else %} - {% for cdn in site.data.assets[origin].cdns %} {% endfor %} - {% endif %} - {% 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 %} @@ -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://' + %} {% endif %} @@ -97,7 +98,7 @@ {% if site.toc and page.toc %} - + {% endif %} {% if page.layout == 'page' or page.layout == 'post' %} diff --git a/_includes/js-selector.html b/_includes/js-selector.html index c760968..05ed99e 100644 --- a/_includes/js-selector.html +++ b/_includes/js-selector.html @@ -1,6 +1,4 @@ - + @@ -8,51 +6,50 @@ {% if site.google_analytics.pv.proxy_endpoint or site.google_analytics.pv.cache_path %} - {% endif %} {% endif %} {% if page.layout == 'post' or page.layout == 'page' %} - {% 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 %} {% endif %} {% if page.layout == 'home' - or page.layout == 'post' - or page.layout == 'archives' - or page.layout == 'category' - or page.layout == 'tag' %} - + or page.layout == 'post' + or page.layout == 'archives' + or page.layout == 'category' + 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 - | 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.locale + | replace: ':LOCALE', locale + | 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' %} - {% assign type = page.layout %} -{% elsif page.layout == 'archives' - or page.layout == 'category' - or page.layout == 'tag' %} - {% assign type = "misc" %} -{% else %} - {% assign type = "commons" %} -{% endif %} +{% case page.layout %} + {% when 'categories', 'post', 'page' %} + {% assign type = page.layout %} + {% when 'home', 'archives', 'category', 'tag' %} + {% assign type = 'misc' %} + {% else %} + {% assign type = 'commons' %} +{% endcase %} {% capture script %}/assets/js/dist/{{ type }}.min.js{% endcapture %} @@ -60,23 +57,24 @@ {% if page.math %} - + {% endif %} @@ -95,5 +93,4 @@ {% if site.google_analytics.id != empty and site.google_analytics.id %} {% include google-analytics.html %} {% endif %} - {% endif %} diff --git a/_includes/mode-toggle.html b/_includes/mode-toggle.html index 63b2538..ada0061 100644 --- a/_includes/mode-toggle.html +++ b/_includes/mode-toggle.html @@ -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(); diff --git a/_includes/sidebar.html b/_includes/sidebar.html index c32efb3..c570597 100644 --- a/_includes/sidebar.html +++ b/_includes/sidebar.html @@ -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: '@' %} - javascript:location.href = 'mailto:' + ['{{ email[0] }}','{{ email[1] }}'].join('@') - {%- elsif entry.type == 'rss' -%} - {{ "/feed.xml" | relative_url }} - {%- else -%} - {{ entry.url }} - {%- endif -%} - {% endcapture %} + {%- capture url -%} + javascript:location.href = 'mailto:' + ['{{ email[0] }}','{{ email[1] }}'].join('@') + {%- endcapture -%} + {% when 'rss' %} + {% assign url = '/feed.xml' | relative_url %} + {% else %} + {% assign url = entry.url %} + {% endcase %} {% if url %} + {% unless link_types == empty %}rel="{{ link_types }}"{% endunless %}> + {% endif %} diff --git a/_includes/toc.html b/_includes/toc.html index 6d88167..0d1967c 100644 --- a/_includes/toc.html +++ b/_includes/toc.html @@ -6,11 +6,11 @@ {% endif %} {% if enable_toc %} - - +
+
{{- site.data.locales[site.lang].panel.toc -}}
+ +
-
-
{{- site.data.locales[site.lang].panel.toc -}}
- -
+ + {% endif %} diff --git a/_javascript/_copyright b/_javascript/_copyright new file mode 100644 index 0000000..dedc8ed --- /dev/null +++ b/_javascript/_copyright @@ -0,0 +1,3 @@ +Chirpy v<%= pkg.version %> (<%= pkg.homepage %>) +© 2019 <%= pkg.author %> +<%= pkg.license %> Licensed diff --git a/_javascript/categories.js b/_javascript/categories.js new file mode 100644 index 0000000..15d8251 --- /dev/null +++ b/_javascript/categories.js @@ -0,0 +1,7 @@ +import { basic, initSidebar, initTopbar } from './modules/layouts'; +import { categoryCollapse } from './modules/plugins'; + +basic(); +initSidebar(); +initTopbar(); +categoryCollapse(); diff --git a/_javascript/commons.js b/_javascript/commons.js new file mode 100644 index 0000000..05a9765 --- /dev/null +++ b/_javascript/commons.js @@ -0,0 +1,5 @@ +import { basic, initSidebar, initTopbar } from './modules/layouts'; + +basic(); +initSidebar(); +initTopbar(); diff --git a/_javascript/commons/back-to-top.js b/_javascript/commons/back-to-top.js deleted file mode 100644 index e876147..0000000 --- a/_javascript/commons/back-to-top.js +++ /dev/null @@ -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; - }); -}); diff --git a/_javascript/commons/mode-toggle.js b/_javascript/commons/mode-toggle.js deleted file mode 100644 index 6581f08..0000000 --- a/_javascript/commons/mode-toggle.js +++ /dev/null @@ -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(); - }); -}); diff --git a/_javascript/commons/scroll-helper.js b/_javascript/commons/scroll-helper.js deleted file mode 100644 index 419418c..0000000 --- a/_javascript/commons/scroll-helper.js +++ /dev/null @@ -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 - }; - -}()); diff --git a/_javascript/commons/search-display.js b/_javascript/commons/search-display.js deleted file mode 100644 index ddc73c1..0000000 --- a/_javascript/commons/search-display.js +++ /dev/null @@ -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"); - } - } - }); - -}); diff --git a/_javascript/commons/sidebar.js b/_javascript/commons/sidebar.js deleted file mode 100644 index 121353f..0000000 --- a/_javascript/commons/sidebar.js +++ /dev/null @@ -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); -}); diff --git a/_javascript/commons/tooltip-loader.js b/_javascript/commons/tooltip-loader.js deleted file mode 100644 index 0b2f0b1..0000000 --- a/_javascript/commons/tooltip-loader.js +++ /dev/null @@ -1,6 +0,0 @@ -/** - * Initial Bootstrap Tooltip. - */ -$(function () { - $("[data-toggle=\"tooltip\"]").tooltip(); -}); diff --git a/_javascript/commons/topbar-switcher.js b/_javascript/commons/topbar-switcher.js deleted file mode 100644 index 91c857e..0000000 --- a/_javascript/commons/topbar-switcher.js +++ /dev/null @@ -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); -}); diff --git a/_javascript/commons/topbar-title.js b/_javascript/commons/topbar-title.js deleted file mode 100644 index fd27a7d..0000000 --- a/_javascript/commons/topbar-title.js +++ /dev/null @@ -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 " <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); - }); - -}); diff --git a/_javascript/copyright b/_javascript/copyright deleted file mode 100644 index 4775b32..0000000 --- a/_javascript/copyright +++ /dev/null @@ -1,5 +0,0 @@ -/*! - * Chirpy v5.5.2 (https://github.com/cotes2020/jekyll-theme-chirpy/) - * © 2019 Cotes Chung - * MIT Licensed - */ diff --git a/_javascript/misc.js b/_javascript/misc.js new file mode 100644 index 0000000..c7a19d6 --- /dev/null +++ b/_javascript/misc.js @@ -0,0 +1,7 @@ +import { basic, initSidebar, initTopbar } from './modules/layouts'; +import { initLocaleDatetime } from './modules/plugins'; + +basic(); +initSidebar(); +initTopbar(); +initLocaleDatetime(); diff --git a/_javascript/modules/components/back-to-top.js b/_javascript/modules/components/back-to-top.js new file mode 100644 index 0000000..2519a9f --- /dev/null +++ b/_javascript/modules/components/back-to-top.js @@ -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; + }); +} diff --git a/_javascript/modules/components/category-collapse.js b/_javascript/modules/components/category-collapse.js new file mode 100644 index 0000000..d6027a1 --- /dev/null +++ b/_javascript/modules/components/category-collapse.js @@ -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'); + } + }); +} diff --git a/_javascript/modules/components/clipboard.js b/_javascript/modules/components/clipboard.js new file mode 100644 index 0000000..cff2d09 --- /dev/null +++ b/_javascript/modules/components/clipboard.js @@ -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); + }); + }); +} diff --git a/_javascript/modules/components/convert-title.js b/_javascript/modules/components/convert-title.js new file mode 100644 index 0000000..649f7be --- /dev/null +++ b/_javascript/modules/components/convert-title.js @@ -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); + }); +} diff --git a/_javascript/modules/components/img-extra.js b/_javascript/modules/components/img-extra.js new file mode 100644 index 0000000..47b8404 --- /dev/null +++ b/_javascript/modules/components/img-extra.js @@ -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'); + }); +} diff --git a/_javascript/modules/components/locale-datetime.js b/_javascript/modules/components/locale-datetime.js new file mode 100644 index 0000000..7bab64b --- /dev/null +++ b/_javascript/modules/components/locale-datetime.js @@ -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); + }); +} diff --git a/_javascript/modules/components/mode-watcher.js b/_javascript/modules/components/mode-watcher.js new file mode 100644 index 0000000..7b2298a --- /dev/null +++ b/_javascript/modules/components/mode-watcher.js @@ -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 + }); +} diff --git a/_javascript/modules/components/pageviews.js b/_javascript/modules/components/pageviews.js new file mode 100644 index 0000000..99e72ce --- /dev/null +++ b/_javascript/modules/components/pageviews.js @@ -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(); + } + } +} diff --git a/_javascript/modules/components/search-display.js b/_javascript/modules/components/search-display.js new file mode 100644 index 0000000..2e38f5e --- /dev/null +++ b/_javascript/modules/components/search-display.js @@ -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); + } + } + }); +} diff --git a/_javascript/modules/components/sidebar.js b/_javascript/modules/components/sidebar.js new file mode 100644 index 0000000..9d8567e --- /dev/null +++ b/_javascript/modules/components/sidebar.js @@ -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); +} diff --git a/_javascript/modules/components/smooth-scroll.js b/_javascript/modules/components/smooth-scroll.js new file mode 100644 index 0000000..09f75d0 --- /dev/null +++ b/_javascript/modules/components/smooth-scroll.js @@ -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() */ +} diff --git a/_javascript/modules/components/toc.js b/_javascript/modules/components/toc.js new file mode 100644 index 0000000..ba0415d --- /dev/null +++ b/_javascript/modules/components/toc.js @@ -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 + }); +} diff --git a/_javascript/modules/components/tooltip-loader.js b/_javascript/modules/components/tooltip-loader.js new file mode 100644 index 0000000..809487a --- /dev/null +++ b/_javascript/modules/components/tooltip-loader.js @@ -0,0 +1,6 @@ +/** + * Initial Bootstrap Tooltip. + */ +export function loadTooptip() { + $('[data-toggle="tooltip"]').tooltip(); +} diff --git a/_javascript/modules/components/topbar-switcher.js b/_javascript/modules/components/topbar-switcher.js new file mode 100644 index 0000000..f3eebb7 --- /dev/null +++ b/_javascript/modules/components/topbar-switcher.js @@ -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); +} diff --git a/_javascript/modules/components/utils/scroll-helper.js b/_javascript/modules/components/utils/scroll-helper.js new file mode 100644 index 0000000..78ad9c8 --- /dev/null +++ b/_javascript/modules/components/utils/scroll-helper.js @@ -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; + } +} diff --git a/_javascript/modules/layouts.js b/_javascript/modules/layouts.js new file mode 100644 index 0000000..28f7962 --- /dev/null +++ b/_javascript/modules/layouts.js @@ -0,0 +1,3 @@ +export { basic } from './layouts/basic'; +export { initSidebar } from './layouts/sidebar'; +export { initTopbar } from './layouts/topbar'; diff --git a/_javascript/modules/layouts/basic.js b/_javascript/modules/layouts/basic.js new file mode 100644 index 0000000..fb36a8b --- /dev/null +++ b/_javascript/modules/layouts/basic.js @@ -0,0 +1,7 @@ +import { back2top } from '../components/back-to-top'; +import { loadTooptip } from '../components/tooltip-loader'; + +export function basic() { + back2top(); + loadTooptip(); +} diff --git a/_javascript/modules/layouts/sidebar.js b/_javascript/modules/layouts/sidebar.js new file mode 100644 index 0000000..8795693 --- /dev/null +++ b/_javascript/modules/layouts/sidebar.js @@ -0,0 +1,7 @@ +import { modeWatcher } from '../components/mode-watcher'; +import { sidebarExpand } from '../components/sidebar'; + +export function initSidebar() { + modeWatcher(); + sidebarExpand(); +} diff --git a/_javascript/modules/layouts/topbar.js b/_javascript/modules/layouts/topbar.js new file mode 100644 index 0000000..76549bf --- /dev/null +++ b/_javascript/modules/layouts/topbar.js @@ -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(); +} diff --git a/_javascript/modules/plugins.js b/_javascript/modules/plugins.js new file mode 100644 index 0000000..8d65435 --- /dev/null +++ b/_javascript/modules/plugins.js @@ -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'; diff --git a/_javascript/page.js b/_javascript/page.js new file mode 100644 index 0000000..0d497f0 --- /dev/null +++ b/_javascript/page.js @@ -0,0 +1,9 @@ +import { basic, initSidebar, initTopbar } from './modules/layouts'; +import { imgExtra, initClipboard, smoothScroll } from './modules/plugins'; + +basic(); +initSidebar(); +initTopbar(); +imgExtra(); +initClipboard(); +smoothScroll(); diff --git a/_javascript/post.js b/_javascript/post.js new file mode 100644 index 0000000..4b472bc --- /dev/null +++ b/_javascript/post.js @@ -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(); diff --git a/_javascript/utils/category-collapse.js b/_javascript/utils/category-collapse.js deleted file mode 100644 index 965bcfd..0000000 --- a/_javascript/utils/category-collapse.js +++ /dev/null @@ -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"); - } - }); - -}); diff --git a/_javascript/utils/clipboard.js b/_javascript/utils/clipboard.js deleted file mode 100644 index 1b5001b..0000000 --- a/_javascript/utils/clipboard.js +++ /dev/null @@ -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); - - }); - -}); diff --git a/_javascript/utils/img-extra.js b/_javascript/utils/img-extra.js deleted file mode 100644 index 90a3f49..0000000 --- a/_javascript/utils/img-extra.js +++ /dev/null @@ -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'); - }); - -})(); diff --git a/_javascript/utils/locale-datetime.js b/_javascript/utils/locale-datetime.js deleted file mode 100644 index 80d86c9..0000000 --- a/_javascript/utils/locale-datetime.js +++ /dev/null @@ -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); - }); -}); diff --git a/_javascript/utils/pageviews.js b/_javascript/utils/pageviews.js deleted file mode 100644 index 1e875d8..0000000 --- a/_javascript/utils/pageviews.js +++ /dev/null @@ -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(); - } - } - -}); diff --git a/_javascript/utils/smooth-scroll.js b/_javascript/utils/smooth-scroll.js deleted file mode 100644 index f8aba66..0000000 --- a/_javascript/utils/smooth-scroll.js +++ /dev/null @@ -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() */ -}); diff --git a/_layouts/default.html b/_layouts/default.html index 6d1b0a5..a98c230 100644 --- a/_layouts/default.html +++ b/_layouts/default.html @@ -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 %} diff --git a/_layouts/home.html b/_layouts/home.html index 9d9eae3..42b9fe2 100644 --- a/_layouts/home.html +++ b/_layouts/home.html @@ -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 --> @@ -29,7 +29,7 @@ layout: page {% assign default_beg = 0 %} {% endif %} -{% assign default_num = paginator.posts | size | minus: pinned_num %} +{% assign default_num = paginator.posts | size | minus: pinned_num %} {% assign default_end = default_beg | plus: default_num | minus: 1 %} {% if default_num > 0 %} @@ -39,55 +39,54 @@ layout: page {% endif %} <div id="post-list"> + {% for post in posts %} + <div class="card post-preview"> + <a href="{{ post.url | relative_url }}"> + <div class="card-body"> + <h1 class="card-title"> + {{ post.title }} + </h1> -{% for post in posts %} + <div class="card-text post-content"> + <p> + {% include no-linenos.html content=post.content %} + {{ content | markdownify | strip_html | truncate: 200 | escape }} + </p> + </div> - <div class="post-preview"> - <h1> - <a href="{{ post.url | relative_url }}">{{ post.title }}</a> - </h1> + <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 %} - <div class="post-content"> - <p> - {% include no-linenos.html content=post.content %} - {{ content | markdownify | strip_html | truncate: 200 | escape }} - </p> + <!-- categories --> + {% if post.categories.size > 0 %} + <i class="far fa-folder-open fa-fw"></i> + <span> + {% for category in post.categories %} + {{ category }} + {%- unless forloop.last -%},{%- endunless -%} + {% endfor %} + </span> + {% endif %} + </div> + + {% if post.pin %} + <div class="pin"> + <i class="fas fa-thumbtack fa-fw"></i> + <span>{{ site.data.locales[site.lang].post.pin_prompt }}</span> + </div> + {% endif %} + </div> + <!-- .post-meta --> + </div> + </a> </div> - - <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 %} - - <!-- categories --> - {% if post.categories.size > 0 %} - <i class="far fa-folder-open fa-fw"></i> - <span> - {% for category in post.categories %} - {{ category }} - {%- unless forloop.last -%},{%- endunless -%} - {% endfor %} - </span> - {% endif %} - - </div> - - {% if post.pin %} - <div class="pin"> - <i class="fas fa-thumbtack fa-fw"></i> - <span>{{ site.data.locales[site.lang].post.pin_prompt }}</span> - </div> - {% endif %} - - </div> <!-- .post-meta --> - - </div> <!-- .post-review --> - -{% endfor %} - -</div> <!-- #post-list --> + <!-- .post-review --> + {% endfor %} +</div> +<!-- #post-list --> {% if paginator.total_pages > 0 %} {% include post-paginator.html %} diff --git a/_posts/2019-08-08-write-a-new-post.md b/_posts/2019-08-08-write-a-new-post.md index 2254d1a..4812cee 100644 --- a/_posts/2019-08-08-write-a-new-post.md +++ b/_posts/2019-08-08-write-a-new-post.md @@ -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 diff --git a/_posts/2019-08-09-getting-started.md b/_posts/2019-08-09-getting-started.md index 0677cfe..120ab7f 100644 --- a/_posts/2019-08-09-getting-started.md +++ b/_posts/2019-08-09-getting-started.md @@ -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 -``` + ```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 diff --git a/_sass/addon/commons.scss b/_sass/addon/commons.scss index e65d336..e16184f 100644 --- a/_sass/addon/commons.scss +++ b/_sass/addon/commons.scss @@ -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); } } @@ -389,7 +395,7 @@ i { /* fontawesome icons */ } } } /* tbody */ - }/* table */ + } /* table */ } /* --- post --- */ @@ -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); @@ -706,8 +728,8 @@ $sidebar-display: "sidebar-display"; } /* Hide scrollbar for IE, Edge and Firefox */ - -ms-overflow-style: none; /* IE and Edge */ - scrollbar-width: none; /* Firefox */ + -ms-overflow-style: none; /* IE and Edge */ + scrollbar-width: none; /* Firefox */ a { @extend %sidebar-links; @@ -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 { diff --git a/_sass/addon/module.scss b/_sass/addon/module.scss index 1fc9ae5..44f21d2 100644 --- a/_sass/addon/module.scss +++ b/_sass/addon/module.scss @@ -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); diff --git a/_sass/addon/syntax.scss b/_sass/addon/syntax.scss index 6667380..e3a93bb 100644 --- a/_sass/addon/syntax.scss +++ b/_sass/addon/syntax.scss @@ -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,18 +66,11 @@ 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 { td pre { - overflow: visible; /* Fixed iOS safari overflow-x */ + overflow: visible; /* Fixed iOS safari overflow-x */ word-break: normal; /* Fixed iOS safari linenos code break */ } } @@ -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; diff --git a/_sass/addon/variables.scss b/_sass/addon/variables.scss index 99614a5..1db532e 100644 --- a/_sass/addon/variables.scss +++ b/_sass/addon/variables.scss @@ -4,23 +4,23 @@ /* sidebar */ -$sidebar-width: 260px !default; /* the basic width */ -$sidebar-width-small: 210px !default; /* screen width: >= 850px, <= 1199px (iPad landscape) */ -$sidebar-width-large: 350px !default; /* screen width: >= 1650px */ +$sidebar-width: 260px !default; /* the basic width */ +$sidebar-width-small: 210px !default; /* screen width: >= 850px, <= 1199px (iPad landscape) */ +$sidebar-width-large: 350px !default; /* screen width: >= 1650px */ /* tabs of sidebar */ -$tab-count: 5 !default; /* backward compatible (version <= 4.0.2) */ +$tab-count: 5 !default; /* backward compatible (version <= 4.0.2) */ $tab-height: 3rem !default; $tab-cursor-height: 1.6rem !default; -$cursor-width: 2px !default; /* the cursor width of the selected tab */ +$cursor-width: 2px !default; /* the cursor width of the selected tab */ /* other framework sizes */ $topbar-height: 3rem !default; $search-max-width: 210px !default; $footer-height: 5rem !default; -$footer-height-mobile: 6rem !default; /* screen width: <= 576px */ +$footer-height-mobile: 6rem !default; /* screen width: <= 576px */ $main-content-max-width: 1250px !default; $bottom-min-height: 35rem !default; diff --git a/_sass/colors/dark-syntax.scss b/_sass/colors/dark-syntax.scss index 3912ed3..8c22d24 100644 --- a/_sass/colors/dark-syntax.scss +++ b/_sass/colors/dark-syntax.scss @@ -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 */ diff --git a/_sass/colors/dark-typography.scss b/_sass/colors/dark-typography.scss index 761a903..dc6f543 100644 --- a/_sass/colors/dark-typography.scss +++ b/_sass/colors/dark-typography.scss @@ -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,13 +26,12 @@ --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( - 90deg, - rgba(255, 255, 255, 0) 0%, - rgba(58, 55, 55, 0.4) 50%, - rgba(255, 255, 255, 0) 100% - ); + --shimmer-bg: linear-gradient( + 90deg, + rgba(255, 255, 255, 0) 0%, + rgba(58, 55, 55, 0.4) 50%, + rgba(255, 255, 255, 0) 100% + ); /* Sidebar */ --sidebar-bg: radial-gradient(circle, #242424 0%, #1d1f27 100%); @@ -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,20 +140,20 @@ } #archives li:nth-child(odd) { - background-image: - linear-gradient( - to left, - rgb(26, 26, 30), - rgb(39, 39, 45), - rgb(39, 39, 45), - rgb(39, 39, 45), - rgb(26, 26, 30) - ); + background-image: linear-gradient( + to left, + rgb(26, 26, 30), + rgb(39, 39, 45), + rgb(39, 39, 45), + rgb(39, 39, 45), + rgb(26, 26, 30) + ); } 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 */ diff --git a/_sass/colors/light-syntax.scss b/_sass/colors/light-syntax.scss index aeac91f..a4dde91 100644 --- a/_sass/colors/light-syntax.scss +++ b/_sass/colors/light-syntax.scss @@ -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); } diff --git a/_sass/colors/light-typography.scss b/_sass/colors/light-typography.scss index 7bfb514..f04629b 100644 --- a/_sass/colors/light-typography.scss +++ b/_sass/colors/light-typography.scss @@ -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,14 +23,17 @@ --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( - 90deg, - rgba(250, 250, 250, 0) 0%, - rgba(232, 230, 230, 1) 50%, - rgba(250, 250, 250, 0) 100% - ); + --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%, + rgba(250, 250, 250, 0) 100% + ); /* Sidebar */ --sidebar-bg: #eeeeee; @@ -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); } diff --git a/_sass/jekyll-theme-chirpy.scss b/_sass/jekyll-theme-chirpy.scss index 38c43e0..25e1a14 100644 --- a/_sass/jekyll-theme-chirpy.scss +++ b/_sass/jekyll-theme-chirpy.scss @@ -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 */ diff --git a/_sass/layout/archives.scss b/_sass/layout/archives.scss index 70cf379..3a2e86b 100644 --- a/_sass/layout/archives.scss +++ b/_sass/layout/archives.scss @@ -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%; diff --git a/_sass/layout/categories.scss b/_sass/layout/categories.scss index c818e33..31c8e8e 100644 --- a/_sass/layout/categories.scss +++ b/_sass/layout/categories.scss @@ -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); } diff --git a/_sass/layout/category-tag.scss b/_sass/layout/category-tag.scss index 4488671..3b25db5 100644 --- a/_sass/layout/category-tag.scss +++ b/_sass/layout/category-tag.scss @@ -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; } diff --git a/_sass/layout/home.scss b/_sass/layout/home.scss index fd0f146..cdf1f09 100644 --- a/_sass/layout/home.scss +++ b/_sass/layout/home.scss @@ -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 { + text-decoration: none; + } - a:hover { - @extend %link-hover; + .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,20 +151,24 @@ /* Sidebar is visible */ @media all and (min-width: 831px) { #post-list { - margin-top: 1.5rem; + margin-top: 3rem; - .post-preview .post-meta { - .pin { - background: var(--pin-bg); - border-radius: 5px; - line-height: 1.4rem; - height: 1.3rem; - margin-top: 3px; - padding-left: 1px; - padding-right: 6px; + .post-preview { + padding: 0.5rem; - > span { - display: inline; + .post-meta { + .pin { + background: var(--pin-bg); + border-radius: 5px; + line-height: 1.4rem; + height: 1.3rem; + margin-top: 3px; + padding-left: 1px; + padding-right: 6px; + + > span { + display: inline; + } } } } diff --git a/_sass/layout/post.scss b/_sass/layout/post.scss index ac445d7..2690cf4 100644 --- a/_sass/layout/post.scss +++ b/_sass/layout/post.scss @@ -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 { @@ -124,7 +116,7 @@ h1 + .post-meta { &:hover { text-decoration: none; - >i { + > i { @extend %btn-share-hovor; } } @@ -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; + } -nav[data-toggle="toc"] { - .nav { - .nav > li > a.active { - font-weight: 600 !important; + a { + padding: 0.2rem 0 0.2rem 1.25rem; + } + } + + /* 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: ':'; } } diff --git a/assets/js/dist/categories.min.js b/assets/js/dist/categories.min.js deleted file mode 100644 index 2c9d0e6..0000000 --- a/assets/js/dist/categories.min.js +++ /dev/null @@ -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"))})}); diff --git a/assets/js/dist/commons.min.js b/assets/js/dist/commons.min.js deleted file mode 100644 index 019d141..0000000 --- a/assets/js/dist/commons.min.js +++ /dev/null @@ -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)})}}); diff --git a/assets/js/dist/home.min.js b/assets/js/dist/home.min.js deleted file mode 100644 index b85d184..0000000 --- a/assets/js/dist/home.min.js +++ /dev/null @@ -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))})}); diff --git a/assets/js/dist/misc.min.js b/assets/js/dist/misc.min.js deleted file mode 100644 index b85d184..0000000 --- a/assets/js/dist/misc.min.js +++ /dev/null @@ -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))})}); diff --git a/assets/js/dist/page.min.js b/assets/js/dist/page.min.js deleted file mode 100644 index f3d8617..0000000 --- a/assets/js/dist/page.min.js +++ /dev/null @@ -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 r=0,a=!1,l=!1;return{hideTopbar:()=>e.attr(t,!1),showTopbar:()=>e.attr(t,!0),addScrollUpTask:()=>{r+=1,a=a||!0},popScrollUpTask:()=>--r,hasScrollUpTask:()=>0<r,topbarLocked:()=>!0===a,unlockTopbar:()=>a=!1,getTopbarHeight:()=>o,orientationLocked:()=>!0===l,lockOrientation:()=>l=!0,unLockOrientation:()=>l=!1}}();$(function(){const e=$("#sidebar-trigger"),t=$("#search-trigger"),o=$("#search-cancel"),r=$("#main"),a=$("#topbar-title"),l=$("#search-wrapper"),n=$("#search-result-wrapper"),i=$("#search-results"),s=$("#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"),a.addClass("unloaded"),t.addClass("unloaded"),l.addClass("d-flex"),o.addClass("loaded")},off(){o.removeClass("loaded"),l.removeClass("d-flex"),e.removeClass("unloaded"),a.removeClass("unloaded"),t.removeClass("unloaded")}},u=function(){let e=!1;return{on(){e||(d.block(),n.removeClass("unloaded"),r.addClass("unloaded"),e=!0)},off(){e&&(i.empty(),c.hasClass("unloaded")&&c.removeClass("unloaded"),n.addClass("unloaded"),r.removeClass("unloaded"),d.release(),s.val(""),e=!1)},isVisible(){return e}}}();function h(){return o.hasClass("loaded")}t.click(function(){p.on(),u.on(),s.focus()}),o.click(function(){p.off(),u.off()}),s.focus(function(){l.addClass("input-focus")}),s.focusout(function(){l.removeClass("input-focus")}),s.on("input",()=>{""===s.val()?h()?c.removeClass("unloaded"):u.off():(u.on(),h()&&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,r=0;function a(){0!==$(window).scrollTop()&&(ScrollHelper.lockOrientation(),ScrollHelper.hideTopbar())}screen.orientation?screen.orientation.onchange=()=>{var e=screen.orientation.type;"landscape-primary"!==e&&"landscape-secondary"!==e||a()}:$(window).on("orientationchange",()=>{$(window).width()<$(window).height()&&a()}),$(window).scroll(()=>{e=e||!0}),setInterval(()=>{e&&(!function(){var e=$(this).scrollTop();if(!(Math.abs(r-e)<=o)){if(e>r)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()}r=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 i=n.text().trim();let r=t.text().trim(),a=!1,l=0;($("#page-category").length||$("#page-tag").length)&&/\s/.test(r)&&(r=r.replace(/[0-9]/g,"").trim()),t.offset().top<$(window).scrollTop()&&n.text(r);new IntersectionObserver(e=>{var t,o;a?(o=$(window).scrollTop(),t=l<o,l=o,o=e[0],t?0===o.intersectionRatio&&n.text(r):1===o.intersectionRatio&&n.text(i)):a=!0},{rootMargin:"-48px 0px 0px 0px",threshold:[0,1]}).observe(document.querySelector(e)),n.click(function(){$("body,html").animate({scrollTop:0},800)})}}),$("#core-wrapper img[data-src]")<=0||($(".popup").magnificPopup({type:"image",closeOnContentClick:!0,showCloseBtn:!1,zoom:{enabled:!0,duration:300,easing:"ease-in-out"}}),document.addEventListener("lazyloaded",function(e){$(e.target).parent().removeClass("shimmer")})),$(function(){var e=".code-header>button";const t="timeout",a="data-title-succeed",l="data-original-title";function n(e){if($(e)[0].hasAttribute(t)){e=$(e).attr(t);if(Number(e)>Date.now())return 1}}function i(e){$(e).attr(t,Date.now()+2e3)}function s(e){$(e).removeAttr(t)}var o=new ClipboardJS(e,{target(e){return e.parentNode.nextElementSibling.querySelector("code .rouge-code")}});$(e).tooltip({trigger:"hover",placement:"left"});const r=$(e).children().attr("class");o.on("success",e=>{e.clearSelection();const t=e.trigger;var o;n(t)||(e=t,$(e).children().attr("class","fas fa-check"),e=t,o=$(e).attr(a),$(e).attr(l,o).tooltip("show"),i(t),setTimeout(()=>{var e;e=t,$(e).tooltip("hide").removeAttr(l),e=t,$(e).children().attr("class",r),s(t)},2e3))}),$("#copy-link").click(e=>{let t=$(e.target);if(!n(t)){var e=window.location.href,o=$("<input>");$("body").append(o),o.val(e).select(),document.execCommand("copy"),o.remove();const r=t.attr(l);e=t.attr(a);t.attr(l,e).tooltip("show"),i(t),setTimeout(()=>{t.attr(l,r),s(t)},2e3)}})}),$(function(){const e=$("#topbar-title"),s="scroll-focus";$("a[href*='#']").not("[href='#']").not("[href='#0']").click(function(a){if(this.pathname.replace(/^\//,"")===location.pathname.replace(/^\//,"")&&location.hostname===this.hostname){var l=decodeURI(this.hash);let t=RegExp(/^#fnref:/).test(l),o=!t&&RegExp(/^#fn:/).test(l);var n=l.includes(":")?l.replace(/:/g,"\\:"):l;let r=$(n);var n=e.is(":visible"),i=$(window).width()<$(window).height();if(void 0!==r){a.preventDefault(),history.pushState&&history.pushState(null,null,l);a=$(window).scrollTop();let e=r.offset().top-=8;e<a&&(ScrollHelper.hideTopbar(),ScrollHelper.addScrollUpTask()),n&&i&&(e-=ScrollHelper.getTopbarHeight()),$("html").animate({scrollTop:e},500,()=>{if(r.focus(),$(`[${s}=true]`).length&&$(`[${s}=true]`).attr(s,!1),$(":target").length&&$(":target").attr(s,!1),(o||t)&&r.attr(s,!0),r.is(":focus"))return!1;r.attr("tabindex","-1"),r.focus(),ScrollHelper.hasScrollUpTask()&&ScrollHelper.popScrollUpTask()})}}})}); diff --git a/assets/js/dist/post.min.js b/assets/js/dist/post.min.js deleted file mode 100644 index 05eda48..0000000 --- a/assets/js/dist/post.min.js +++ /dev/null @@ -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(t=>{t=$(t.target);(t.prop("tagName")==="button".toUpperCase()?t:t.parent()).blur(),flipMode()})});const ScrollHelper=function(){const t=$("body"),e="data-topbar-visible",o=$("#topbar-wrapper").outerHeight();let a=0,r=!1,l=!1;return{hideTopbar:()=>t.attr(e,!1),showTopbar:()=>t.attr(e,!0),addScrollUpTask:()=>{a+=1,r=r||!0},popScrollUpTask:()=>--a,hasScrollUpTask:()=>0<a,topbarLocked:()=>!0===r,unlockTopbar:()=>r=!1,getTopbarHeight:()=>o,orientationLocked:()=>!0===l,lockOrientation:()=>l=!0,unLockOrientation:()=>l=!1}}(),LocaleHelper=($(function(){const t=$("#sidebar-trigger"),e=$("#search-trigger"),o=$("#search-cancel"),a=$("#main"),r=$("#topbar-title"),l=$("#search-wrapper"),n=$("#search-result-wrapper"),i=$("#search-results"),s=$("#search-input"),c=$("#search-hints"),d=function(){let t=0;return{block(){t=window.scrollY,$("html,body").scrollTop(0)},release(){$("html,body").scrollTop(t)},getOffset(){return t}}}(),p={on(){t.addClass("unloaded"),r.addClass("unloaded"),e.addClass("unloaded"),l.addClass("d-flex"),o.addClass("loaded")},off(){o.removeClass("loaded"),l.removeClass("d-flex"),t.removeClass("unloaded"),r.removeClass("unloaded"),e.removeClass("unloaded")}},u=function(){let t=!1;return{on(){t||(d.block(),n.removeClass("unloaded"),a.addClass("unloaded"),t=!0)},off(){t&&(i.empty(),c.hasClass("unloaded")&&c.removeClass("unloaded"),n.addClass("unloaded"),a.removeClass("unloaded"),d.release(),s.val(""),t=!1)},isVisible(){return t}}}();function h(){return o.hasClass("loaded")}e.click(function(){p.on(),u.on(),s.focus()}),o.click(function(){p.off(),u.off()}),s.focus(function(){l.addClass("input-focus")}),s.focusout(function(){l.removeClass("input-focus")}),s.on("input",()=>{""===s.val()?h()?c.removeClass("unloaded"):u.off():(u.on(),h()&&c.addClass("unloaded"))})}),$(function(){var t=function(){const t="sidebar-display";let e=!1;const o=$("body");return{toggle(){!1===e?o.attr(t,""):o.removeAttr(t),e=!e}}}();$("#sidebar-trigger").click(t.toggle),$("#mask").click(t.toggle)}),$(function(){$('[data-toggle="tooltip"]').tooltip()}),$(function(){const e=$("#search-input"),o=ScrollHelper.getTopbarHeight();let t,a=0;function r(){0!==$(window).scrollTop()&&(ScrollHelper.lockOrientation(),ScrollHelper.hideTopbar())}screen.orientation?screen.orientation.onchange=()=>{var t=screen.orientation.type;"landscape-primary"!==t&&"landscape-secondary"!==t||r()}:$(window).on("orientationchange",()=>{$(window).width()<$(window).height()&&r()}),$(window).scroll(()=>{t=t||!0}),setInterval(()=>{t&&(!function(){var t=$(this).scrollTop();if(!(Math.abs(a-t)<=o)){if(t>a)ScrollHelper.hideTopbar(),e.is(":focus")&&e.blur();else if(t+$(window).height()<$(document).height()){if(ScrollHelper.hasScrollUpTask())return;ScrollHelper.topbarLocked()?ScrollHelper.unlockTopbar():ScrollHelper.orientationLocked()?ScrollHelper.unLockOrientation():ScrollHelper.showTopbar()}a=t}}(),t=!1)},250)}),$(function(){var t="div.post>h1:first-of-type",e=$(t);const n=$("#topbar-title");if(0!==e.length&&!e.hasClass("dynamic-title")&&!n.is(":hidden")){const i=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(t=>{var e,o;r?(o=$(window).scrollTop(),e=l<o,l=o,o=t[0],e?0===o.intersectionRatio&&n.text(a):1===o.intersectionRatio&&n.text(i)):r=!0},{rootMargin:"-48px 0px 0px 0px",threshold:[0,1]}).observe(document.querySelector(t)),n.click(function(){$("body,html").animate({scrollTop:0},800)})}}),$("#core-wrapper img[data-src]")<=0||($(".popup").magnificPopup({type:"image",closeOnContentClick:!0,showCloseBtn:!1,zoom:{enabled:!0,duration:300,easing:"ease-in-out"}}),document.addEventListener("lazyloaded",function(t){$(t.target).parent().removeClass("shimmer")})),function(){const t=$("html").attr("lang").substr(0,2),e="data-ts",o="data-df";return{locale:()=>t,attrTimestamp:()=>e,attrDateFormat:()=>o,getTimestamp:t=>Number(t.attr(e)),getDateFormat:t=>t.attr(o)}}());$(function(){dayjs.locale(LocaleHelper.locale()),dayjs.extend(window.dayjs_plugin_localizedFormat),$(`[${LocaleHelper.attrTimestamp()}]`).each(function(){var t=dayjs.unix(LocaleHelper.getTimestamp($(this))),e=t.format(LocaleHelper.getDateFormat($(this))),e=($(this).text(e),$(this).removeAttr(LocaleHelper.attrTimestamp()),$(this).removeAttr(LocaleHelper.attrDateFormat()),$(this).attr("data-toggle"));void 0!==e&&"tooltip"===e&&(e=t.format("llll"),$(this).attr("data-original-title",e))})}),$(function(){var t=".code-header>button";const e="timeout",r="data-title-succeed",l="data-original-title";function n(t){if($(t)[0].hasAttribute(e)){t=$(t).attr(e);if(Number(t)>Date.now())return 1}}function i(t){$(t).attr(e,Date.now()+2e3)}function s(t){$(t).removeAttr(e)}var o=new ClipboardJS(t,{target(t){return t.parentNode.nextElementSibling.querySelector("code .rouge-code")}});$(t).tooltip({trigger:"hover",placement:"left"});const a=$(t).children().attr("class");o.on("success",t=>{t.clearSelection();const e=t.trigger;var o;n(e)||(t=e,$(t).children().attr("class","fas fa-check"),t=e,o=$(t).attr(r),$(t).attr(l,o).tooltip("show"),i(e),setTimeout(()=>{var t;t=e,$(t).tooltip("hide").removeAttr(l),t=e,$(t).children().attr("class",a),s(e)},2e3))}),$("#copy-link").click(t=>{let e=$(t.target);if(!n(e)){var t=window.location.href,o=$("<input>");$("body").append(o),o.val(t).select(),document.execCommand("copy"),o.remove();const a=e.attr(l);t=e.attr(r);e.attr(l,t).tooltip("show"),i(e),setTimeout(()=>{e.attr(l,a),s(e)},2e3)}})}),$(function(){const t=$("#topbar-title"),s="scroll-focus";$("a[href*='#']").not("[href='#']").not("[href='#0']").click(function(r){if(this.pathname.replace(/^\//,"")===location.pathname.replace(/^\//,"")&&location.hostname===this.hostname){var l=decodeURI(this.hash);let e=RegExp(/^#fnref:/).test(l),o=!e&&RegExp(/^#fn:/).test(l);var n=l.includes(":")?l.replace(/:/g,"\\:"):l;let a=$(n);var n=t.is(":visible"),i=$(window).width()<$(window).height();if(void 0!==a){r.preventDefault(),history.pushState&&history.pushState(null,null,l);r=$(window).scrollTop();let t=a.offset().top-=8;t<r&&(ScrollHelper.hideTopbar(),ScrollHelper.addScrollUpTask()),n&&i&&(t-=ScrollHelper.getTopbarHeight()),$("html").animate({scrollTop:t},500,()=>{if(a.focus(),$(`[${s}=true]`).length&&$(`[${s}=true]`).attr(s,!1),$(":target").length&&$(":target").attr(s,!1),(o||e)&&a.attr(s,!0),a.is(":focus"))return!1;a.attr("tabindex","-1"),a.focus(),ScrollHelper.hasScrollUpTask()&&ScrollHelper.popScrollUpTask()})}}})}); diff --git a/assets/js/dist/pvreport.min.js b/assets/js/dist/pvreport.min.js deleted file mode 100644 index 4886fd8..0000000 --- a/assets/js/dist/pvreport.min.js +++ /dev/null @@ -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())}); diff --git a/assets/lib b/assets/lib index e372141..24ebdb7 160000 --- a/assets/lib +++ b/assets/lib @@ -1 +1 @@ -Subproject commit e372141074f370c6f03b68b5264e7663f2b7477c +Subproject commit 24ebdb708f3f5451df953cb5f9deb3ad4433404a diff --git a/gulpfile.js/index.js b/gulpfile.js/index.js deleted file mode 100644 index 14692fb..0000000 --- a/gulpfile.js/index.js +++ /dev/null @@ -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; diff --git a/gulpfile.js/tasks/js.js b/gulpfile.js/tasks/js.js deleted file mode 100644 index 3db0065..0000000 --- a/gulpfile.js/tasks/js.js +++ /dev/null @@ -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 - ); -}; diff --git a/jekyll-theme-chirpy.gemspec b/jekyll-theme-chirpy.gemspec index 6d138f7..1a98ecf 100644 --- a/jekyll-theme-chirpy.gemspec +++ b/jekyll-theme-chirpy.gemspec @@ -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" diff --git a/package.json b/package.json index fbd6b10..6427099 100644 --- a/package.json +++ b/package.json @@ -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" } } diff --git a/rollup.config.js b/rollup.config.js new file mode 100644 index 0000000..f662358 --- /dev/null +++ b/rollup.config.js @@ -0,0 +1,42 @@ +import babel from '@rollup/plugin-babel'; +import terser from '@rollup/plugin-terser'; +import license from 'rollup-plugin-license'; +import path from 'path'; + +const JS_SRC = '_javascript'; +const JS_DIST = 'assets/js/dist'; +const isProd = process.env.NODE_ENV === 'production'; + +function build(filename) { + return { + input: [`${JS_SRC}/${filename}.js`], + output: { + file: `${JS_DIST}/${filename}.min.js`, + format: 'iife', + name: 'Chirpy', + sourcemap: !isProd + }, + plugins: [ + babel({ + babelHelpers: 'bundled', + presets: ['@babel/env'], + plugins: ['@babel/plugin-proposal-class-properties'] + }), + license({ + banner: { + commentStyle: 'ignored', + content: { file: path.join(__dirname, JS_SRC, '_copyright') } + } + }), + isProd && terser() + ] + }; +} + +export default [ + build('commons'), + build('categories'), + build('page'), + build('post'), + build('misc') +]; diff --git a/tools/init b/tools/init index 1848f9b..af3ce76 100755 --- a/tools/init +++ b/tools/init @@ -1,12 +1,18 @@ #!/usr/bin/env bash # -# Init the evrionment for new user. +# Init the environment for new user. set -eu +# CLI Dependencies +CLI=("git" "npm") + ACTIONS_WORKFLOW=pages-deploy.yml -TEMP_SUFFIX="to-delete" # temporary file suffixes that make `sed -i` compatible with BSD and Linux +# temporary file suffixes that make `sed -i` compatible with BSD and Linux +TEMP_SUFFIX="to-delete" + +_no_gh=false help() { echo "Usage:" @@ -18,14 +24,32 @@ help() { echo " -h, --help Print this help information." } -check_status() { +# BSD and GNU compatible sed +_sedi() { + regex=$1 + file=$2 + sed -i.$TEMP_SUFFIX "$regex" "$file" + rm -f "$file".$TEMP_SUFFIX +} + +_check_cli() { + for i in "${!CLI[@]}"; do + cli="${CLI[$i]}" + if ! command -v "$cli" &>/dev/null; then + echo "Command '$cli' not found! Hint: you should install it." + exit 1 + fi + done +} + +_check_status() { if [[ -n $(git status . -s) ]]; then echo "Error: Commit unstaged files first, and then run this tool again." - exit -1 + exit 1 fi } -check_init() { +_check_init() { local _has_inited=false if [[ ! -d .github ]]; then # using option `--no-gh` @@ -33,7 +57,8 @@ check_init() { else if [[ -f .github/workflows/$ACTIONS_WORKFLOW ]]; then # on BSD, the `wc` could contains blank - local _count="$(find .github/workflows/ -type f -name "*.yml" | wc -l)" + local _count + _count=$(find .github/workflows/ -type f -name "*.yml" | wc -l) if [[ ${_count//[[:blank:]]/} == 1 ]]; then _has_inited=true fi @@ -46,8 +71,14 @@ check_init() { fi } +check_env() { + _check_cli + _check_status + _check_init +} + checkout_latest_tag() { - tag=$(git describe --tags $(git rev-list --tags --max-count=1)) + tag=$(git describe --tags "$(git rev-list --tags --max-count=1)") git reset --hard "$tag" } @@ -62,25 +93,31 @@ init_files() { mv ./${ACTIONS_WORKFLOW}.hook .github/workflows/${ACTIONS_WORKFLOW} ## Cleanup image settings in site config - sed -i.$TEMP_SUFFIX "s/^img_cdn:.*/img_cdn:/;s/^avatar:.*/avatar:/" _config.yml - rm -f _config.yml.$TEMP_SUFFIX + _sedi "s/^img_cdn:.*/img_cdn:/;s/^avatar:.*/avatar:/" _config.yml fi # remove the other fies rm -rf _posts/* - # save changes - git add -A - git commit -m "chore: initialize the environment" -q + # build assest + npm i && npm run build - echo "[INFO] Initialization successful!" + # track the js output + _sedi "/^assets.*\/dist/d" .gitignore } -check_status +commit() { + git add -A + git commit -m "chore: initialize the environment" -q + echo -e "\n[INFO] Initialization successful!\n" +} -check_init - -_no_gh=false +main() { + check_env + checkout_latest_tag + init_files + commit +} while (($#)); do opt="$1" @@ -101,6 +138,4 @@ while (($#)); do esac done -checkout_latest_tag - -init_files +main diff --git a/tools/release b/tools/release index b9cdfbc..48bff39 100755 --- a/tools/release +++ b/tools/release @@ -27,9 +27,11 @@ GEM_SPEC="jekyll-theme-chirpy.gemspec" NODE_CONFIG="package.json" +JS_DIST="assets/js/dist" +BACKUP_PATH="$(mktemp -d)" + FILES=( "_sass/jekyll-theme-chirpy.scss" - "_javascript/copyright" "$GEM_SPEC" "$NODE_CONFIG" ) @@ -69,17 +71,24 @@ _check_git() { } _check_src() { - if [[ ! -f $1 && ! -d $1 ]]; then - echo -e "Error: Missing file \"$1\"!\n" - exit 1 - fi + for i in "${!FILES[@]}"; do + _src="${FILES[$i]}" + if [[ ! -f $_src && ! -d $_src ]]; then + echo -e "Error: Missing file \"$_src\"!\n" + exit 1 + fi + done + } _check_command() { - if ! command -v "$1" &>/dev/null; then - echo "Command '$1' not found" - exit 1 - fi + for i in "${!TOOLS[@]}"; do + cli="${TOOLS[$i]}" + if ! command -v "$cli" &>/dev/null; then + echo "Command '$cli' not found!" + exit 1 + fi + done } _check_node_packages() { @@ -89,29 +98,22 @@ _check_node_packages() { } check() { + _check_command _check_git - - for i in "${!FILES[@]}"; do - _check_src "${FILES[$i]}" - done - - for i in "${!TOOLS[@]}"; do - _check_command "${TOOLS[$i]}" - done - + _check_src _check_node_packages } -_bump_file() { +_bump_files() { for i in "${!FILES[@]}"; do - if [[ ${FILES[$i]} == $NODE_CONFIG ]]; then + if [[ ${FILES[$i]} == "$NODE_CONFIG" ]]; then continue fi sed -i "s/v[[:digit:]]\+\.[[:digit:]]\+\.[[:digit:]]\+/v$1/" "${FILES[$i]}" done - npx gulp + npm run build } _bump_gemspec() { @@ -127,7 +129,7 @@ _bump_gemspec() { # # 2. Create a commit to save the changes. bump() { - _bump_file "$1" + _bump_files "$1" _bump_gemspec "$1" if [[ $opt_pre = false && -n $(git status . -s) ]]; then @@ -151,7 +153,10 @@ build_gem() { echo -e "Build the gem package for v$_version\n" cleanup_config rm -f ./*.gem + git add "$JS_DIST" -f # add JS dist to gem gem build "$GEM_SPEC" + cp "$JS_DIST"/* "$BACKUP_PATH" + git restore --staged "$JS_DIST" # resume the git status resume_config } @@ -203,6 +208,9 @@ main() { else release "$_version" fi + + # restore the dist files for future development + mkdir -p "$JS_DIST" && cp "$BACKUP_PATH"/* "$JS_DIST" } while (($#)); do