├── .ruby-version ├── OSSMETADATA ├── .qlty ├── configs │ └── .shellcheckrc ├── .gitignore └── qlty.toml ├── .rspec ├── scripts ├── build ├── test ├── lint ├── devcontainer_postCreateCommand.sh ├── setup └── console ├── .ackrc ├── spec ├── fixtures │ ├── missing_definition │ │ ├── _data │ │ │ └── glossary.yml │ │ ├── page.md │ │ ├── _config.yml │ │ ├── _layouts │ │ │ └── default.html │ │ └── assets │ │ │ └── css │ │ │ └── style.css │ ├── missing_term │ │ ├── _data │ │ │ └── glossary.yml │ │ ├── page.md │ │ ├── _config.yml │ │ ├── _layouts │ │ │ └── default.html │ │ └── assets │ │ │ └── css │ │ │ └── style.css │ ├── empty_definition │ │ ├── _data │ │ │ └── glossary.yml │ │ ├── page.md │ │ ├── _config.yml │ │ ├── _layouts │ │ │ └── default.html │ │ └── assets │ │ │ └── css │ │ │ └── style.css │ ├── normal │ │ ├── page8.md │ │ ├── page1.md │ │ ├── page4.md │ │ ├── _config.yml │ │ ├── page2.md │ │ ├── page6.md │ │ ├── page3.md │ │ ├── page7.md │ │ ├── page5.md │ │ ├── _layouts │ │ │ └── default.html │ │ ├── _data │ │ │ └── glossary.yml │ │ └── assets │ │ │ └── css │ │ │ └── style.css │ ├── duplicate_term │ │ ├── _data │ │ │ └── glossary.yml │ │ ├── page.md │ │ ├── _config.yml │ │ ├── _layouts │ │ │ └── default.html │ │ └── assets │ │ │ └── css │ │ │ └── style.css │ ├── missing_glossary │ │ ├── page.md │ │ ├── _config.yml │ │ ├── _layouts │ │ │ └── default.html │ │ └── assets │ │ │ └── css │ │ │ └── style.css │ └── missing_tag_arg │ │ ├── _config.yml │ │ ├── page.md │ │ ├── _layouts │ │ └── default.html │ │ ├── _data │ │ └── glossary.yml │ │ └── assets │ │ └── css │ │ └── style.css ├── jekyll-glossary_tooltip.rb ├── jekyll-glossary_tooltip │ ├── options_parser_spec.rb │ └── tag_spec.rb └── spec_helper.rb ├── .devcontainer └── devcontainer.json ├── img ├── tooltip_screenshot.png └── tooltip_screenshot.xcf ├── lib ├── jekyll-glossary_tooltip │ ├── version.rb │ ├── errors.rb │ ├── options_parser.rb │ ├── jekyll-glossary_tooltip.css │ └── tag.rb └── jekyll-glossary_tooltip.rb ├── gemfiles ├── README.md ├── jekyll_4.x.x.gemfile └── jekyll_3.7.x.gemfile ├── CONTRIBUTING.md ├── .simplecov ├── Appraisals ├── SECURITY.md ├── .editorconfig ├── .github ├── dependabot.yml └── workflows │ ├── cd.yml │ ├── codeql-analysis.yml │ └── ci.yml ├── .codeclimate.yml ├── .gitignore ├── Rakefile ├── Gemfile ├── LICENSE.txt ├── jekyll-glossary_tooltip.gemspec ├── CHANGELOG.md ├── .rubocop.yml └── README.md /.ruby-version: -------------------------------------------------------------------------------- 1 | 3.1.0 2 | -------------------------------------------------------------------------------- /OSSMETADATA: -------------------------------------------------------------------------------- 1 | osslifecycle=active 2 | -------------------------------------------------------------------------------- /.qlty/configs/.shellcheckrc: -------------------------------------------------------------------------------- 1 | source-path=SCRIPTDIR -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --format documentation 2 | --color 3 | --require spec_helper 4 | -------------------------------------------------------------------------------- /scripts/build: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | set -eux 3 | bundle exec rake $* 4 | -------------------------------------------------------------------------------- /scripts/test: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | set -eux 3 | bundle exec rake spec $* 4 | -------------------------------------------------------------------------------- /scripts/lint: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | set -eux 3 | bundle exec rake rubocop $* 4 | -------------------------------------------------------------------------------- /.ackrc: -------------------------------------------------------------------------------- 1 | --ignore-dir=coverage/ 2 | --ignore-dir=spec/dest 3 | --ignore-dir=.jekyll-cache 4 | -------------------------------------------------------------------------------- /spec/fixtures/missing_definition/_data/glossary.yml: -------------------------------------------------------------------------------- 1 | - term: term_no_def 2 | url: term_no_def url 3 | -------------------------------------------------------------------------------- /.qlty/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !configs 3 | !configs/** 4 | !hooks 5 | !hooks/** 6 | !qlty.toml 7 | !.gitignore 8 | -------------------------------------------------------------------------------- /spec/fixtures/missing_term/_data/glossary.yml: -------------------------------------------------------------------------------- 1 | - term: term_defined 2 | definition: here 3 | url: ! 4 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "postCreateCommand": "bash scripts/devcontainer_postCreateCommand.sh" 3 | } 4 | -------------------------------------------------------------------------------- /img/tooltip_screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erikw/jekyll-glossary_tooltip/HEAD/img/tooltip_screenshot.png -------------------------------------------------------------------------------- /img/tooltip_screenshot.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erikw/jekyll-glossary_tooltip/HEAD/img/tooltip_screenshot.xcf -------------------------------------------------------------------------------- /spec/fixtures/empty_definition/_data/glossary.yml: -------------------------------------------------------------------------------- 1 | - term: term_no_def 2 | definition: 3 | url: term_no_def url 4 | -------------------------------------------------------------------------------- /spec/fixtures/normal/page8.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Fixture page 8" 3 | layout: default 4 | --- 5 | 6 | {% glossary term_with_url, display: José Álvarez %} 7 | -------------------------------------------------------------------------------- /lib/jekyll-glossary_tooltip/version.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Jekyll 4 | module GlossaryTooltip 5 | VERSION = "2.1.0" 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /spec/fixtures/duplicate_term/_data/glossary.yml: -------------------------------------------------------------------------------- 1 | - term: term_popular 2 | definition: first definition 3 | - term: term_popular 4 | definition: second definition 5 | -------------------------------------------------------------------------------- /spec/fixtures/duplicate_term/page.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Fixture page" 3 | layout: default 4 | --- 5 | 6 | # Fixture 7 | 8 | A simple fixture page to {% glossary term_popular %} the glossary. 9 | -------------------------------------------------------------------------------- /spec/fixtures/missing_glossary/page.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Fixture page" 3 | layout: default 4 | --- 5 | 6 | # Fixture 7 | 8 | A simple fixture page to {% glossary any_term %} the glossary. 9 | -------------------------------------------------------------------------------- /spec/fixtures/normal/page1.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Fixture page 1" 3 | layout: default 4 | --- 5 | 6 | # Fixture 7 | 8 | A simple fixture page to {% glossary term_with_url %} the glossary. 9 | -------------------------------------------------------------------------------- /spec/fixtures/normal/page4.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Fixture page 4" 3 | layout: default 4 | --- 5 | 6 | # Fixture 7 | 8 | A simple fixture page to {% glossary term with spaces %} the glossary. 9 | -------------------------------------------------------------------------------- /spec/fixtures/empty_definition/page.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Fixture page" 3 | layout: default 4 | --- 5 | 6 | # Fixture 7 | 8 | A simple fixture page to {% glossary term_no_def %} the glossary. 9 | -------------------------------------------------------------------------------- /spec/fixtures/missing_definition/page.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Fixture page" 3 | layout: default 4 | --- 5 | 6 | # Fixture 7 | 8 | A simple fixture page to {% glossary term_no_def %} the glossary. 9 | -------------------------------------------------------------------------------- /spec/fixtures/missing_term/page.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Fixture page" 3 | layout: default 4 | --- 5 | 6 | # Fixture 7 | 8 | A simple fixture page to {% glossary term_not_defined %} the glossary. 9 | -------------------------------------------------------------------------------- /spec/fixtures/normal/_config.yml: -------------------------------------------------------------------------------- 1 | # This file must exist, as Jekyll will print a warning during build otherwise. 2 | # Config can also be done in the #make_site helper. 3 | title: Fixture Site 4 | -------------------------------------------------------------------------------- /spec/fixtures/duplicate_term/_config.yml: -------------------------------------------------------------------------------- 1 | # This file must exist, as Jekyll will print a warning during build otherwise. 2 | # Config can also be done in the #make_site helper. 3 | title: Fixture Site 4 | -------------------------------------------------------------------------------- /spec/fixtures/missing_term/_config.yml: -------------------------------------------------------------------------------- 1 | # This file must exist, as Jekyll will print a warning during build otherwise. 2 | # Config can also be done in the #make_site helper. 3 | title: Fixture Site 4 | -------------------------------------------------------------------------------- /gemfiles/README.md: -------------------------------------------------------------------------------- 1 | These Gemfiles are used from the CI pipeline in the build matrix to test specific versions of dependencies. They are generated by [Appraisal](https://github.com/thoughtbot/appraisal). 2 | -------------------------------------------------------------------------------- /spec/fixtures/empty_definition/_config.yml: -------------------------------------------------------------------------------- 1 | # This file must exist, as Jekyll will print a warning during build otherwise. 2 | # Config can also be done in the #make_site helper. 3 | title: Fixture Site 4 | -------------------------------------------------------------------------------- /spec/fixtures/missing_definition/_config.yml: -------------------------------------------------------------------------------- 1 | # This file must exist, as Jekyll will print a warning during build otherwise. 2 | # Config can also be done in the #make_site helper. 3 | title: Fixture Site 4 | -------------------------------------------------------------------------------- /spec/fixtures/missing_glossary/_config.yml: -------------------------------------------------------------------------------- 1 | # This file must exist, as Jekyll will print a warning during build otherwise. 2 | # Config can also be done in the #make_site helper. 3 | title: Fixture Site 4 | -------------------------------------------------------------------------------- /spec/fixtures/missing_tag_arg/_config.yml: -------------------------------------------------------------------------------- 1 | # This file must exist, as Jekyll will print a warning during build otherwise. 2 | # Config can also be done in the #make_site helper. 3 | title: Fixture Site 4 | -------------------------------------------------------------------------------- /spec/fixtures/normal/page2.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Fixture page 2" 3 | layout: default 4 | --- 5 | 6 | # Fixture 7 | 8 | A simple fixture page to {% glossary term_without_url %} the glossary. 9 | 10 | -------------------------------------------------------------------------------- /spec/fixtures/normal/page6.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Fixture page 6" 3 | layout: default 4 | --- 5 | 6 | # Fixture 7 | 8 | A simple fixture page to {% glossary term_with_url_embedded_liquid %} the glossary. 9 | -------------------------------------------------------------------------------- /spec/fixtures/normal/page3.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Fixture page 3" 3 | layout: default 4 | --- 5 | 6 | # Fixture 7 | 8 | A simple fixture page to {% glossary TerM_Case_Insensitive %} the glossary. 9 | 10 | -------------------------------------------------------------------------------- /spec/fixtures/normal/page7.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Fixture page 6" 3 | layout: default 4 | --- 5 | 6 | # Fixture 7 | 8 | When a term is followed by a comma {% glossary term_without_url %}, no space before comma.. 9 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to contribute 2 | Please use GitHub tooling (issues, PRs) to disucssion and code contributions! 3 | 4 | When you open an PR, GitHub Actions will build your code, run tests, liters and so on. 5 | -------------------------------------------------------------------------------- /spec/fixtures/missing_tag_arg/page.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Fixture page" 3 | layout: default 4 | --- 5 | 6 | # Fixture 7 | 8 | A simple fixture page with a tag that forgot the argument {% glossary %} that is needed. 9 | -------------------------------------------------------------------------------- /spec/fixtures/normal/page5.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Fixture page 5" 3 | layout: default 4 | --- 5 | 6 | # Fixture 7 | 8 | A term can have custom display name: {% glossary term_with_url, display: term alt display name %}. 9 | -------------------------------------------------------------------------------- /spec/jekyll-glossary_tooltip.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | RSpec.describe Jekyll::GlossaryTooltip do 4 | it "has a version number" do 5 | expect(Jekyll::GlossaryTooltip::VERSION).not_to be_nil 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /scripts/devcontainer_postCreateCommand.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Devcontainer postCreateCommand. 3 | # Install dependencies for running this project in GitHub Codespaces. 4 | 5 | set -eux 6 | 7 | # For project. 8 | bundle install 9 | -------------------------------------------------------------------------------- /spec/fixtures/normal/_layouts/default.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{ page.title }} 5 | 6 | 7 | 8 | {{ content }} 9 | 10 | 11 | -------------------------------------------------------------------------------- /spec/fixtures/duplicate_term/_layouts/default.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{ page.title }} 5 | 6 | 7 | 8 | {{ content }} 9 | 10 | 11 | -------------------------------------------------------------------------------- /spec/fixtures/empty_definition/_layouts/default.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{ page.title }} 5 | 6 | 7 | 8 | {{ content }} 9 | 10 | 11 | -------------------------------------------------------------------------------- /spec/fixtures/missing_glossary/_layouts/default.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{ page.title }} 5 | 6 | 7 | 8 | {{ content }} 9 | 10 | 11 | -------------------------------------------------------------------------------- /spec/fixtures/missing_tag_arg/_layouts/default.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{ page.title }} 5 | 6 | 7 | 8 | {{ content }} 9 | 10 | 11 | -------------------------------------------------------------------------------- /spec/fixtures/missing_term/_layouts/default.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{ page.title }} 5 | 6 | 7 | 8 | {{ content }} 9 | 10 | 11 | -------------------------------------------------------------------------------- /spec/fixtures/missing_definition/_layouts/default.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{ page.title }} 5 | 6 | 7 | 8 | {{ content }} 9 | 10 | 11 | -------------------------------------------------------------------------------- /.simplecov: -------------------------------------------------------------------------------- 1 | # vi: ft=ruby 2 | 3 | SimpleCov.start do 4 | enable_coverage :branch # Add branch coverage statistics. 5 | minimum_coverage 90 # Minimum coverage percentage. 6 | command_name "test:bdd" # Must be set for codeclimat reporter 7 | end 8 | -------------------------------------------------------------------------------- /Appraisals: -------------------------------------------------------------------------------- 1 | # Override jekyll version from .gemspec 2 | 3 | appraise "jekyll-3.7.x" do 4 | gem "jekyll", "~> 3.7" 5 | gem "kramdown", "2.3.1" 6 | gem "kramdown-parser-gfm", "~> 1.0" 7 | end 8 | 9 | appraise "jekyll-4.x.x" do 10 | gem "jekyll", "~> 4.0" 11 | end 12 | -------------------------------------------------------------------------------- /scripts/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | IFS=$'\n\t' 4 | set -vx 5 | 6 | rvm install ruby-$(cat .ruby-version) 7 | gem install bundler -v 2.2.18 8 | bundle install 9 | 10 | 11 | # Qlty CLI tool. Ref: https://docs.qlty.sh/cli/quickstart 12 | curl https://qlty.sh | sh 13 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Supported Versions 4 | 5 | | Version | Supported | 6 | | ------- | ------------------ | 7 | | 1.x.x | :white_check_mark: | 8 | | < 1.0 | :x: | 9 | 10 | 11 | ## Reporting a Vulnerability 12 | Please open a GitHub Issue in this repository for any potential issues! 13 | -------------------------------------------------------------------------------- /lib/jekyll-glossary_tooltip.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_relative "jekyll-glossary_tooltip/options_parser" 4 | require_relative "jekyll-glossary_tooltip/tag" 5 | require_relative "jekyll-glossary_tooltip/version" 6 | 7 | module Jekyll 8 | # Module for the plugin. 9 | module GlossaryTooltip 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # Modified version of https://github.com/ruby/ruby/blob/master/.editorconfig 2 | 3 | root = true 4 | 5 | [*] 6 | end_of_line = lf 7 | indent_size = 4 8 | indent_style = space 9 | insert_final_newline = true 10 | tab_width = 4 11 | trim_trailing_whitespace = true 12 | 13 | [*.rb] 14 | indent_size = 2 15 | 16 | [*.gemspec] 17 | indent_size = 2 18 | 19 | [*.yml] 20 | indent_size = 2 21 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # Reference: https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 2 | # Validation: https://dependabot.com/docs/config-file/validator/ 3 | 4 | version: 2 5 | updates: 6 | - package-ecosystem: "bundler" 7 | directory: "/" 8 | schedule: 9 | interval: "monthly" 10 | #ignore: 11 | #- dependency-name: "rubocop" 12 | -------------------------------------------------------------------------------- /spec/fixtures/missing_tag_arg/_data/glossary.yml: -------------------------------------------------------------------------------- 1 | - term: term_with_url 2 | definition: term_with_url definition 3 | url: term_with_url url 4 | - term: term_without_url 5 | definition: term_without_url definition 6 | - term: TERM_CASE_INSENSITIVE 7 | definition: TERM_CASE_INSENSITIVE definition 8 | url: TERM_CASE_INSENSITIVE url 9 | - term: term with spaces 10 | definition: term with spaces definition 11 | -------------------------------------------------------------------------------- /scripts/console: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | require "bundler/setup" 5 | require "jekyll-glossary_tooltip" 6 | 7 | # You can add fixtures and/or initialization code here to make experimenting 8 | # with your gem easier. You can also use a different console, if you like. 9 | 10 | # (If you use this, don't forget to add pry to your Gemfile!) 11 | # require "pry" 12 | # Pry.start 13 | 14 | require "irb" 15 | IRB.start(__FILE__) 16 | -------------------------------------------------------------------------------- /spec/fixtures/normal/_data/glossary.yml: -------------------------------------------------------------------------------- 1 | - term: term_with_url 2 | definition: term_with_url definition 3 | url: term_with_url url 4 | - term: term_without_url 5 | definition: term_without_url definition 6 | - term: TERM_CASE_INSENSITIVE 7 | definition: TERM_CASE_INSENSITIVE definition 8 | url: TERM_CASE_INSENSITIVE url 9 | - term: term with spaces 10 | definition: term with spaces definition 11 | - term: term_with_url_embedded_liquid 12 | definition: term_with_url_embedded_liquid definition 13 | url: > 14 | {% link page2.md %} 15 | -------------------------------------------------------------------------------- /.codeclimate.yml: -------------------------------------------------------------------------------- 1 | # Reference: https://docs.codeclimate.com/docs/advanced-configuration#section-checks 2 | # Reference of plugins: https://docs.codeclimate.com/docs/list-of-engines 3 | 4 | version: "2" 5 | plugins: 6 | rubocop: 7 | enabled: true 8 | channel: rubocop-1-18-3 9 | fixme: 10 | enabled: true 11 | eslint: 12 | enabled: true 13 | duplication: 14 | enabled: true 15 | config: 16 | languages: 17 | - ruby 18 | - javascript 19 | ratings: 20 | paths: 21 | - "**.rb" 22 | - "**.js" 23 | exclude_paths: 24 | - scripts/ 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Contrary to ruby apps, ruby gems should not check in Gemfile.lock. 2 | # Reference: https://yehudakatz.com/2010/12/16/clarifying-the-roles-of-the-gemspec-and-gemfile/ 3 | Gemfile.lock 4 | # Same for Appraisals generated Gemfiles 5 | gemfiles/*gemfile.lock 6 | gemfiles/Gemfile*.lock 7 | 8 | # Bundle local config 9 | .bundle/ 10 | 11 | # Package gem from $(rake install) 12 | pkg/ 13 | 14 | # simplecov 15 | coverage/ 16 | 17 | # rspec 18 | .jekyll-cache/ 19 | .rspec_status 20 | spec/dest/ 21 | spec/reports/ 22 | 23 | # Jekyll - when switching from gh-pages-source branch 24 | _site/ 25 | -------------------------------------------------------------------------------- /gemfiles/jekyll_4.x.x.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "jekyll", "~> 4.0" 6 | 7 | group :development, :test do 8 | gem "appraisal", "~> 2.4", require: false 9 | gem "gem-release", "~> 2.0", require: false 10 | gem "rake", "~> 13.0", require: false 11 | gem "solargraph", require: false 12 | end 13 | 14 | group :test do 15 | gem "rspec", "~> 3.0" 16 | gem "rubocop", "~> 1.18", require: false 17 | gem "rubocop-rake", "~> 0.6", require: false 18 | gem "rubocop-rspec", "~> 3.5", require: false 19 | gem "simplecov", "~> 0.22" 20 | gem "json_pure", "~> 2.8", require: false 21 | end 22 | 23 | gemspec path: "../" 24 | -------------------------------------------------------------------------------- /gemfiles/jekyll_3.7.x.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "jekyll", "~> 3.7" 6 | gem "kramdown", "2.3.1" 7 | gem "kramdown-parser-gfm", "~> 1.0" 8 | 9 | group :development, :test do 10 | gem "appraisal", "~> 2.4", require: false 11 | gem "gem-release", "~> 2.0", require: false 12 | gem "rake", "~> 13.0", require: false 13 | gem "solargraph", require: false 14 | end 15 | 16 | group :test do 17 | gem "rspec", "~> 3.0" 18 | gem "rubocop", "~> 1.18", require: false 19 | gem "rubocop-rake", "~> 0.6", require: false 20 | gem "rubocop-rspec", "~> 3.5", require: false 21 | gem "simplecov", "~> 0.22" 22 | gem "json_pure", "~> 2.8", require: false 23 | end 24 | 25 | gemspec path: "../" 26 | -------------------------------------------------------------------------------- /.github/workflows/cd.yml: -------------------------------------------------------------------------------- 1 | name: Continuous Deployment # Well, semi-continuous 2 | 3 | on: 4 | push: 5 | tags: v[0-9]+.[0-9]+.[0-9]+ 6 | env: 7 | RUBY_VER: 3.0.1 8 | jobs: 9 | test: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v2 13 | - name: Set up Ruby 14 | uses: ruby/setup-ruby@v1 15 | with: 16 | ruby-version: ${{ env.RUBY_VER }} 17 | bundler-cache: true 18 | - name: Run CI Build with Rake 19 | run: bundle exec rake 20 | release-github: 21 | needs: [test] 22 | runs-on: ubuntu-latest 23 | steps: 24 | - uses: actions/checkout@v2 25 | - name: Build and publish gem to GitHub Packages 26 | if: contains(github.ref, 'refs/tags/v') 27 | uses: jstastny/publish-gem-to-github@master 28 | with: 29 | token: ${{ github.token }} 30 | owner: ${{ github.repository_owner }} 31 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | name: "CodeQL" 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | # The branches below must be a subset of the branches above 8 | branches: [ main ] 9 | schedule: 10 | - cron: '41 19 1 * *' 11 | 12 | jobs: 13 | analyze: 14 | name: Analyze 15 | runs-on: ubuntu-latest 16 | permissions: 17 | actions: read 18 | contents: read 19 | security-events: write 20 | 21 | strategy: 22 | fail-fast: false 23 | matrix: 24 | language: [ 'ruby' ] 25 | 26 | steps: 27 | - name: Checkout repository 28 | uses: actions/checkout@v2 29 | 30 | # Initializes the CodeQL tools for scanning. 31 | - name: Initialize CodeQL 32 | uses: github/codeql-action/init@v1 33 | with: 34 | languages: ${{ matrix.language }} 35 | 36 | - name: Perform CodeQL Analysis 37 | uses: github/codeql-action/analyze@v1 38 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Run $(rake -T) to list all tasks. 4 | 5 | # Include default tasks like build, release, install etc. See https://github.com/rubygems/rubygems/blob/master/bundler/lib/bundler/gem_helper.rb#L46 6 | require "bundler/gem_tasks" 7 | 8 | # rspec: Testing framework. Adds 'spec' rake task. 9 | require "rspec/core/rake_task" 10 | RSpec::Core::RakeTask.new(:spec) 11 | 12 | # rubocop: Linting. Adds 'rubocop' rake task. 13 | require "rubocop/rake_task" 14 | RuboCop::RakeTask.new(:rubocop) do |t| 15 | # See https://docs.rubocop.org/rubocop/usage/basic_usage.html 16 | t.options = ["--display-cop-names", "--parallel"] 17 | end 18 | 19 | desc "Run Qlty code analysis" 20 | task :qlty do 21 | sh "qlty smells --all" 22 | sh "qlty metrics --all --max-depth=2 --sort complexity --limit 10" 23 | # sh "qlty lint" # Just runs rubocop, not necessary as we have a task for this already 24 | end 25 | 26 | # default task: Add spec and rubocop to default tasks. 27 | task default: %i[spec rubocop] 28 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Put require=false on gem's that we don't need to import in code (cli exec only) 4 | 5 | source "https://rubygems.org" 6 | 7 | # Include dependencies from the .gemspec 8 | gemspec 9 | 10 | # Development dependencies 11 | # Should rather be here than in the .gemspec 12 | # Reference: https://github.com/rubygems/bundler/pull/7222 13 | # However there's an argument for using gemspec too: https://bundler.io/guides/creating_gem.html#testing-our-gem 14 | group :development, :test do 15 | gem "appraisal", "~> 2.4", require: false 16 | gem "gem-release", "~> 2.0", require: false 17 | gem "rake", "~> 13.0", require: false 18 | gem "solargraph", require: false 19 | end 20 | 21 | group :test do 22 | gem "json_pure", "~> 2.8", require: false # Solargraph pulls in v2.6.3 which fails build with " uninitialized constant JSON::Fragment" 23 | gem "rspec", "~> 3.0" 24 | gem "rubocop", "~> 1.18", require: false 25 | gem "rubocop-rake", "~> 0.6", require: false 26 | gem "rubocop-rspec", "~> 3.5", require: false 27 | gem "simplecov", "~> 0.22" 28 | end 29 | -------------------------------------------------------------------------------- /spec/fixtures/normal/assets/css/style.css: -------------------------------------------------------------------------------- 1 | /* Tooltip from https://www.w3schools.com/css/css_tooltip.asp */ 2 | .jekyll-glossary { 3 | position: relative; 4 | display: inline-block; 5 | border-bottom: 3px dotted blue; 6 | cursor: help; 7 | } 8 | 9 | .jekyll-glossary .jekyll-glossary-tooltip { 10 | visibility: hidden; 11 | width: 120px; 12 | background-color: black; 13 | color: #fff; 14 | text-align: center; 15 | font-size: 0.5em; 16 | padding: 5px; 17 | border-radius: 6px; 18 | 19 | /* Position the tooltip text - see examples below! */ 20 | position: absolute; 21 | z-index: 1; 22 | 23 | width: 160px; 24 | bottom: 100%; 25 | left: 50%; 26 | margin-left: -80px; /* Use half of the width to center the tooltip */ 27 | 28 | } 29 | 30 | /* Show the tooltip text when you mouse over the tooltip container */ 31 | .jekyll-glossary:hover .jekyll-glossary-tooltip { 32 | visibility: visible; 33 | } 34 | 35 | /* Style the source link (if there is one provided in the glossary entry). */ 36 | .jekyll-glossary-source-link:before { 37 | content: "[source]"; // "(reference)", "" or whatever you want. 38 | } 39 | -------------------------------------------------------------------------------- /spec/fixtures/missing_term/assets/css/style.css: -------------------------------------------------------------------------------- 1 | /* Tooltip from https://www.w3schools.com/css/css_tooltip.asp */ 2 | .jekyll-glossary { 3 | position: relative; 4 | display: inline-block; 5 | border-bottom: 3px dotted blue; 6 | cursor: help; 7 | } 8 | 9 | .jekyll-glossary .jekyll-glossary-tooltip { 10 | visibility: hidden; 11 | width: 120px; 12 | background-color: black; 13 | color: #fff; 14 | text-align: center; 15 | font-size: 0.5em; 16 | padding: 5px; 17 | border-radius: 6px; 18 | 19 | /* Position the tooltip text - see examples below! */ 20 | position: absolute; 21 | z-index: 1; 22 | 23 | width: 160px; 24 | bottom: 100%; 25 | left: 50%; 26 | margin-left: -80px; /* Use half of the width to center the tooltip */ 27 | 28 | } 29 | 30 | /* Show the tooltip text when you mouse over the tooltip container */ 31 | .jekyll-glossary:hover .jekyll-glossary-tooltip { 32 | visibility: visible; 33 | } 34 | 35 | /* Style the source link (if there is one provided in the glossary entry). */ 36 | .jekyll-glossary-source-link:before { 37 | content: "[source]"; // "(reference)", "" or whatever you want. 38 | } 39 | -------------------------------------------------------------------------------- /spec/fixtures/duplicate_term/assets/css/style.css: -------------------------------------------------------------------------------- 1 | /* Tooltip from https://www.w3schools.com/css/css_tooltip.asp */ 2 | .jekyll-glossary { 3 | position: relative; 4 | display: inline-block; 5 | border-bottom: 3px dotted blue; 6 | cursor: help; 7 | } 8 | 9 | .jekyll-glossary .jekyll-glossary-tooltip { 10 | visibility: hidden; 11 | width: 120px; 12 | background-color: black; 13 | color: #fff; 14 | text-align: center; 15 | font-size: 0.5em; 16 | padding: 5px; 17 | border-radius: 6px; 18 | 19 | /* Position the tooltip text - see examples below! */ 20 | position: absolute; 21 | z-index: 1; 22 | 23 | width: 160px; 24 | bottom: 100%; 25 | left: 50%; 26 | margin-left: -80px; /* Use half of the width to center the tooltip */ 27 | 28 | } 29 | 30 | /* Show the tooltip text when you mouse over the tooltip container */ 31 | .jekyll-glossary:hover .jekyll-glossary-tooltip { 32 | visibility: visible; 33 | } 34 | 35 | /* Style the source link (if there is one provided in the glossary entry). */ 36 | .jekyll-glossary-source-link:before { 37 | content: "[source]"; // "(reference)", "" or whatever you want. 38 | } 39 | -------------------------------------------------------------------------------- /spec/fixtures/empty_definition/assets/css/style.css: -------------------------------------------------------------------------------- 1 | /* Tooltip from https://www.w3schools.com/css/css_tooltip.asp */ 2 | .jekyll-glossary { 3 | position: relative; 4 | display: inline-block; 5 | border-bottom: 3px dotted blue; 6 | cursor: help; 7 | } 8 | 9 | .jekyll-glossary .jekyll-glossary-tooltip { 10 | visibility: hidden; 11 | width: 120px; 12 | background-color: black; 13 | color: #fff; 14 | text-align: center; 15 | font-size: 0.5em; 16 | padding: 5px; 17 | border-radius: 6px; 18 | 19 | /* Position the tooltip text - see examples below! */ 20 | position: absolute; 21 | z-index: 1; 22 | 23 | width: 160px; 24 | bottom: 100%; 25 | left: 50%; 26 | margin-left: -80px; /* Use half of the width to center the tooltip */ 27 | 28 | } 29 | 30 | /* Show the tooltip text when you mouse over the tooltip container */ 31 | .jekyll-glossary:hover .jekyll-glossary-tooltip { 32 | visibility: visible; 33 | } 34 | 35 | /* Style the source link (if there is one provided in the glossary entry). */ 36 | .jekyll-glossary-source-link:before { 37 | content: "[source]"; // "(reference)", "" or whatever you want. 38 | } 39 | -------------------------------------------------------------------------------- /spec/fixtures/missing_glossary/assets/css/style.css: -------------------------------------------------------------------------------- 1 | /* Tooltip from https://www.w3schools.com/css/css_tooltip.asp */ 2 | .jekyll-glossary { 3 | position: relative; 4 | display: inline-block; 5 | border-bottom: 3px dotted blue; 6 | cursor: help; 7 | } 8 | 9 | .jekyll-glossary .jekyll-glossary-tooltip { 10 | visibility: hidden; 11 | width: 120px; 12 | background-color: black; 13 | color: #fff; 14 | text-align: center; 15 | font-size: 0.5em; 16 | padding: 5px; 17 | border-radius: 6px; 18 | 19 | /* Position the tooltip text - see examples below! */ 20 | position: absolute; 21 | z-index: 1; 22 | 23 | width: 160px; 24 | bottom: 100%; 25 | left: 50%; 26 | margin-left: -80px; /* Use half of the width to center the tooltip */ 27 | 28 | } 29 | 30 | /* Show the tooltip text when you mouse over the tooltip container */ 31 | .jekyll-glossary:hover .jekyll-glossary-tooltip { 32 | visibility: visible; 33 | } 34 | 35 | /* Style the source link (if there is one provided in the glossary entry). */ 36 | .jekyll-glossary-source-link:before { 37 | content: "[source]"; // "(reference)", "" or whatever you want. 38 | } 39 | -------------------------------------------------------------------------------- /spec/fixtures/missing_tag_arg/assets/css/style.css: -------------------------------------------------------------------------------- 1 | /* Tooltip from https://www.w3schools.com/css/css_tooltip.asp */ 2 | .jekyll-glossary { 3 | position: relative; 4 | display: inline-block; 5 | border-bottom: 3px dotted blue; 6 | cursor: help; 7 | } 8 | 9 | .jekyll-glossary .jekyll-glossary-tooltip { 10 | visibility: hidden; 11 | width: 120px; 12 | background-color: black; 13 | color: #fff; 14 | text-align: center; 15 | font-size: 0.5em; 16 | padding: 5px; 17 | border-radius: 6px; 18 | 19 | /* Position the tooltip text - see examples below! */ 20 | position: absolute; 21 | z-index: 1; 22 | 23 | width: 160px; 24 | bottom: 100%; 25 | left: 50%; 26 | margin-left: -80px; /* Use half of the width to center the tooltip */ 27 | 28 | } 29 | 30 | /* Show the tooltip text when you mouse over the tooltip container */ 31 | .jekyll-glossary:hover .jekyll-glossary-tooltip { 32 | visibility: visible; 33 | } 34 | 35 | /* Style the source link (if there is one provided in the glossary entry). */ 36 | .jekyll-glossary-source-link:before { 37 | content: "[source]"; // "(reference)", "" or whatever you want. 38 | } 39 | -------------------------------------------------------------------------------- /spec/fixtures/missing_definition/assets/css/style.css: -------------------------------------------------------------------------------- 1 | /* Tooltip from https://www.w3schools.com/css/css_tooltip.asp */ 2 | .jekyll-glossary { 3 | position: relative; 4 | display: inline-block; 5 | border-bottom: 3px dotted blue; 6 | cursor: help; 7 | } 8 | 9 | .jekyll-glossary .jekyll-glossary-tooltip { 10 | visibility: hidden; 11 | width: 120px; 12 | background-color: black; 13 | color: #fff; 14 | text-align: center; 15 | font-size: 0.5em; 16 | padding: 5px; 17 | border-radius: 6px; 18 | 19 | /* Position the tooltip text - see examples below! */ 20 | position: absolute; 21 | z-index: 1; 22 | 23 | width: 160px; 24 | bottom: 100%; 25 | left: 50%; 26 | margin-left: -80px; /* Use half of the width to center the tooltip */ 27 | 28 | } 29 | 30 | /* Show the tooltip text when you mouse over the tooltip container */ 31 | .jekyll-glossary:hover .jekyll-glossary-tooltip { 32 | visibility: visible; 33 | } 34 | 35 | /* Style the source link (if there is one provided in the glossary entry). */ 36 | .jekyll-glossary-source-link:before { 37 | content: "[source]"; // "(reference)", "" or whatever you want. 38 | } 39 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2021 Erik Westrup 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Continuous Integration 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: main 7 | pull_request: 8 | branches: main 9 | 10 | # OIDC permissions for qlty 11 | permissions: 12 | contents: read 13 | id-token: write 14 | 15 | jobs: 16 | test: 17 | runs-on: ubuntu-latest 18 | 19 | strategy: 20 | matrix: 21 | ruby: [2.7.0, 3.0.1] 22 | gemfile: 23 | - gemfiles/jekyll_3.7.x.gemfile 24 | - gemfiles/jekyll_4.x.x.gemfile 25 | 26 | env: 27 | BUNDLE_GEMFILE: ${{ matrix.gemfile }} 28 | 29 | steps: 30 | - name: Checkout code 31 | uses: actions/checkout@v4 32 | 33 | - name: Set up Ruby 34 | uses: ruby/setup-ruby@v1 35 | with: 36 | ruby-version: ${{ matrix.ruby }} 37 | bundler-cache: true 38 | bundler: 2.2.18 39 | 40 | - name: Run tests 41 | run: bundle exec rake spec 42 | 43 | - name: Run linting 44 | run: bundle exec rake rubocop 45 | 46 | - name: Publish code coverage to qlty 47 | uses: qltysh/qlty-action/coverage@v2 48 | with: 49 | oidc: true 50 | format: simplecov 51 | files: coverage/.resultset.json 52 | -------------------------------------------------------------------------------- /lib/jekyll-glossary_tooltip/errors.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Jekyll 4 | module GlossaryTooltip 5 | module Errors 6 | class MissingTermDefinition < StandardError 7 | def initialize(term_name); super("Glossary entry for '#{term_name}' does not contain a definition!") end 8 | end 9 | class MissingTermEntry < StandardError 10 | def initialize(term_name); super("The term '#{term_name}' was not defined in the glossary") end 11 | end 12 | class MultipleTermEntries < StandardError 13 | def initialize(term_name); super("The term '#{term_name}' was defined multiple times in the glossary") end 14 | end 15 | class NoGlossaryFile < StandardError; def initialize; super("No data.glossary found") end end 16 | class OptionsNoTermNameInTag < StandardError 17 | def initialize; super("No term name argument for the glossary tag supplied") end 18 | end 19 | class OptionsBadTagArgumentFormat < StandardError 20 | def initialize(term_name); super("The glossary tag for term '#{term_name}' has a bad argument format") end 21 | end 22 | class OptionsUnknownTagArgument < StandardError 23 | def initialize(arg); super("An unknown tag argument #{arg} was encountered") end 24 | end 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /lib/jekyll-glossary_tooltip/options_parser.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "jekyll-glossary_tooltip/errors" 4 | 5 | module Jekyll 6 | module GlossaryTooltip 7 | # Stripped down & modified version of 8 | # https://github.com/ayastreb/jekyll-maps/blob/master/lib/jekyll-maps/options_parser.rb 9 | class OptionsParser 10 | ARGS_PATTERN = %r{\s*([\p{L}_][-\p{L}_]*):\s*([\p{L}_][^,\n\r]*)} 11 | ARGS_ALLOWED = %w[ 12 | display 13 | ].freeze 14 | 15 | class << self 16 | def parse(raw_options) 17 | options = { 18 | term_query: nil, 19 | display: nil 20 | } 21 | opt_segments = raw_options.strip.split(",") 22 | raise Errors::OptionsNoTermNameInTag unless opt_segments.length.positive? 23 | 24 | options[:term_query] = opt_segments[0] 25 | opt_segments.shift 26 | parse_segments(options, opt_segments) 27 | options 28 | end 29 | 30 | def parse_segments(options, opt_segments) 31 | opt_segments.each do |opt_segment| 32 | raise Errors::OptionsBadTagArgumentFormat, options[:term_name] unless opt_segment =~ ARGS_PATTERN 33 | 34 | arg_name = Regexp.last_match(1) 35 | arg_value = Regexp.last_match(2) 36 | raise Errors::OptionsUnknownTagArgument, arg_name unless ARGS_ALLOWED.include?(arg_name) 37 | 38 | options[arg_name.to_sym] = arg_value 39 | end 40 | end 41 | end 42 | end 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /spec/jekyll-glossary_tooltip/options_parser_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "jekyll-glossary_tooltip/options_parser" 4 | require "jekyll-glossary_tooltip/errors" 5 | 6 | P = Jekyll::GlossaryTooltip::OptionsParser 7 | E = Jekyll::GlossaryTooltip::Errors 8 | 9 | RSpec.describe Jekyll::GlossaryTooltip::OptionsParser do 10 | context "when providing valid term name" do 11 | let(:actual) { P.parse("a_term") } 12 | let(:expected) do 13 | { term_query: "a_term", 14 | display: nil } 15 | end 16 | 17 | it "parsing works" do 18 | expect(actual).to eq(expected) 19 | end 20 | end 21 | 22 | context "when providing valid term name and valid arg" do 23 | let(:actual) { P.parse("a_term, display: FancyName ") } 24 | let(:expected) do 25 | { term_query: "a_term", 26 | display: "FancyName" } 27 | end 28 | 29 | it "parsing works" do 30 | expect(actual).to eq(expected) 31 | end 32 | end 33 | 34 | context "when providing no term name query" do 35 | it "parsing raises error" do 36 | expect { P.parse(" ") }.to raise_error(E::OptionsNoTermNameInTag) 37 | end 38 | end 39 | 40 | context "when providing valid term name and invalid arg" do 41 | it "parsing raises error" do 42 | expect { P.parse("a_term, unknownArg: what") }.to raise_error(E::OptionsUnknownTagArgument) 43 | end 44 | end 45 | 46 | context "when providing invalid format" do 47 | it "parsing raises error" do 48 | expect { P.parse("a_term, display = FancyName") }.to raise_error(E::OptionsBadTagArgumentFormat) 49 | end 50 | end 51 | end 52 | -------------------------------------------------------------------------------- /jekyll-glossary_tooltip.gemspec: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_relative "lib/jekyll-glossary_tooltip/version" 4 | 5 | Gem::Specification.new do |spec| 6 | spec.name = "jekyll-glossary_tooltip" 7 | spec.version = Jekyll::GlossaryTooltip::VERSION 8 | spec.authors = ["Erik Westrup"] 9 | spec.email = ["erik.westrup@icloud.com"] 10 | 11 | spec.summary = "Jekyll plugin providing a glossary liquid tag that will show a tooltip of a term definition in your site." 12 | spec.description = "This plugin simplifies for your readers and you by making it easy to define terms or abbreviations that needs an explanation. Define a common dictionary of terms and their definition in a YAML file. Then inside markdown files you can use the provided glossary liquid tag to insert a tooltip for a defined word from the dictionary. The tooltip will show the term definition on mouse hover." 13 | spec.homepage = "https://github.com/erikw/jekyll-glossary_tooltip/" 14 | spec.license = "MIT" 15 | spec.required_ruby_version = [">= 2.7", "< 4"] 16 | 17 | spec.metadata["homepage_uri"] = spec.homepage 18 | spec.metadata["source_code_uri"] = "https://github.com/erikw/jekyll-glossary_tooltip/" 19 | spec.metadata["changelog_uri"] = "https://github.com/erikw/jekyll-glossary_tooltip/blob/main/CHANGELOG.md" 20 | 21 | # Specify which files should be added to the gem when it is released. 22 | # The `git ls-files -z` loads the files in the RubyGem that have been added into git. 23 | spec.files = Dir.chdir(File.expand_path(__dir__)) do 24 | `git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features)/}) } 25 | end 26 | spec.bindir = "exe" 27 | spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) } 28 | spec.require_paths = ["lib"] 29 | 30 | spec.add_dependency "jekyll", [">= 3.7", "< 5.0"] 31 | end 32 | -------------------------------------------------------------------------------- /.qlty/qlty.toml: -------------------------------------------------------------------------------- 1 | # This file was automatically generated by `qlty init`. 2 | # You can modify it to suit your needs. 3 | # We recommend you to commit this file to your repository. 4 | # 5 | # This configuration is used by both Qlty CLI and Qlty Cloud. 6 | # 7 | # Qlty CLI -- Code quality toolkit for developers 8 | # Qlty Cloud -- Fully automated Code Health Platform 9 | # 10 | # Try Qlty Cloud: https://qlty.sh 11 | # 12 | # For a guide to configuration, visit https://qlty.sh/d/config 13 | # Or for a full reference, visit https://qlty.sh/d/qlty-toml 14 | config_version = "0" 15 | 16 | exclude_patterns = [ 17 | "*_min.*", 18 | "*-min.*", 19 | "*.min.*", 20 | "**/.yarn/**", 21 | "**/*.d.ts", 22 | "**/assets/**", 23 | "**/bower_components/**", 24 | "**/build/**", 25 | "**/cache/**", 26 | "**/config/**", 27 | "**/db/**", 28 | "**/deps/**", 29 | "**/dist/**", 30 | "**/extern/**", 31 | "**/external/**", 32 | "**/generated/**", 33 | "**/Godeps/**", 34 | "**/gradlew/**", 35 | "**/mvnw/**", 36 | "**/node_modules/**", 37 | "**/protos/**", 38 | "**/seed/**", 39 | "**/target/**", 40 | "**/templates/**", 41 | "**/testdata/**", 42 | "**/vendor/**", 43 | ] 44 | 45 | test_patterns = [ 46 | "**/test/**", 47 | "**/spec/**", 48 | "**/*.test.*", 49 | "**/*.spec.*", 50 | "**/*_test.*", 51 | "**/*_spec.*", 52 | "**/test_*.*", 53 | "**/spec_*.*", 54 | ] 55 | 56 | [smells] 57 | mode = "comment" 58 | 59 | [smells.boolean_logic] 60 | threshold = 4 61 | 62 | [smells.file_complexity] 63 | threshold = 55 64 | 65 | [smells.return_statements] 66 | threshold = 4 67 | 68 | [smells.nested_control_flow] 69 | threshold = 4 70 | 71 | [smells.function_parameters] 72 | threshold = 4 73 | 74 | [smells.function_complexity] 75 | threshold = 5 76 | 77 | [[source]] 78 | name = "default" 79 | default = true 80 | 81 | [[plugin]] 82 | name = "rubocop" 83 | version = "1.80.2" 84 | package_file = "Gemfile" 85 | package_filters = ["rubocop"] 86 | -------------------------------------------------------------------------------- /lib/jekyll-glossary_tooltip/jekyll-glossary_tooltip.css: -------------------------------------------------------------------------------- 1 | /* Tooltip from https://www.w3schools.com/css/css_tooltip.asp */ 2 | .jekyll-glossary { 3 | position: relative; 4 | display: inline-block; 5 | border-bottom: 2px dotted #0074bd; 6 | cursor: help; 7 | } 8 | 9 | .jekyll-glossary .jekyll-glossary-tooltip { 10 | visibility: hidden; 11 | width: 120px; 12 | background-color: black; 13 | color: #fff; 14 | text-align: center; 15 | font-size: 0.5em; 16 | padding: 5px; 17 | border-radius: 6px; 18 | 19 | /* Position the tooltip text - see examples below! */ 20 | position: absolute; 21 | z-index: 1; 22 | 23 | width: 160px; 24 | bottom: 100%; 25 | left: 50%; 26 | margin-left: -80px; /* Use half of the width to center the tooltip */ 27 | 28 | } 29 | 30 | /* Show the tooltip text when you mouse over the tooltip container */ 31 | .jekyll-glossary:hover .jekyll-glossary-tooltip { 32 | visibility: visible; 33 | } 34 | 35 | /* Style the source link (if there is one provided in the glossary entry). */ 36 | .jekyll-glossary-source-link:before { 37 | content: "[source]"; // "(reference)", "" or whatever you want. 38 | } 39 | 40 | /* Arrow created with borders. */ 41 | .jekyll-glossary .jekyll-glossary-tooltip::after { 42 | content: " "; 43 | position: absolute; 44 | top: 100%; 45 | left: 50%; 46 | margin-left: -5px; 47 | border-width: 5px; 48 | border-style: solid; 49 | border-color: black transparent transparent transparent; 50 | } 51 | 52 | /* Animation from invisible to visible on hover. */ 53 | .jekyll-glossary .jekyll-glossary-tooltip { 54 | opacity: 0; 55 | transition: opacity 1s; 56 | } 57 | .jekyll-glossary:hover .jekyll-glossary-tooltip { 58 | opacity: 1; 59 | } 60 | 61 | /* HACK: hide surrounding parenthesis on definition. When Jekyll renders 62 | * post.excerpt, all HTML and CSS is stripped. The effect is that the extra 63 | * parenthesis that are added are hidden in the normal blog post with hoover, but 64 | * hidden in the post.except when html and css is stripped. Ref: 65 | * https://github.com/erikw/jekyll-glossary_tooltip/issues/7#issuecomment-2711471867 */ 66 | .jekyll-glossary-tooltip-hidden { 67 | display: none; 68 | } 69 | -------------------------------------------------------------------------------- /lib/jekyll-glossary_tooltip/tag.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "jekyll" 4 | require "jekyll-glossary_tooltip/errors" 5 | 6 | module Jekyll 7 | module GlossaryTooltip 8 | # Custom liquid tag implementation. 9 | class Tag < Liquid::Tag 10 | def initialize(tag_name, args, tokens) 11 | super 12 | @opts = OptionsParser.parse(args) 13 | end 14 | 15 | def render(context) 16 | entry = lookup_entry(context.registers[:site], @opts[:term_query]) 17 | @opts[:display] ||= @opts[:term_query] 18 | html = <<~HTML 19 | 20 | #{@opts[:display]} 21 | (#{entry["definition"]}#{render_tooltip_url(entry, context)}) 22 | 23 | HTML 24 | html.gsub("\n", "") 25 | end 26 | 27 | private 28 | 29 | LOG_TAG = "Glossary Tag:" 30 | 31 | def render_tooltip_url(entry, context) 32 | # The content of the anchor is set from the CSS class jekyll-glossary-source-link, 33 | # so that the plugin user can customize the text without touching ruby source. 34 | return "" if entry["url"].nil? 35 | 36 | url = Liquid::Template.parse(entry["url"]).render(context).strip 37 | "
" 38 | end 39 | 40 | def lookup_entry(site, term_name) 41 | entry = read_term_entry_from_config(site, term_name) 42 | raise Errors::MissingTermDefinition, term_name unless entry["definition"] 43 | 44 | entry["url"] = nil unless entry.key?("url") 45 | entry 46 | end 47 | 48 | # Retrieve a term from the glossary via the site. 49 | def read_term_entry_from_config(site, term_name) 50 | raise Errors::NoGlossaryFile unless site.data["glossary"] 51 | 52 | entries = site.data["glossary"].select do |entry| 53 | entry.key?("term") and term_name.casecmp(entry["term"]).zero? 54 | end 55 | 56 | case entries.length 57 | when 0 58 | raise Errors::MissingTermEntry, term_name 59 | when 1 60 | entries[0] 61 | else 62 | raise Errors::MultipleTermEntries, term_name 63 | end 64 | end 65 | end 66 | end 67 | end 68 | 69 | Liquid::Template.register_tag("glossary", Jekyll::GlossaryTooltip::Tag) 70 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6 | 7 | ## [Unreleased] 8 | 9 | ## [2.1.0] - 2025-10-30 10 | ### Added 11 | - Unicode support [#20](https://github.com/erikw/jekyll-glossary_tooltip/pull/20) 12 | ### Changed 13 | - Migrated CI from Travis to GitHub Actions. 14 | 15 | ## [2.0.0] - 2025-03-31 16 | ### Added 17 | - [BREAKING] Hidden with CSS parenthesis around term definition. These parenthesis will be revealed when jekyll produces a post.except as typically HTML and CSS stripped => the plain text rendering of the glossary tag will be " (term-description> )". [#7](https://github.com/erikw/jekyll-glossary_tooltip/issues/7) 18 | - To **upgrade** to new version, you need to update the CSS by re-copy the full contexts of [jekyll-glossary_tooltip.css](lib/jekyll-glossary_tooltip/jekyll-glossary_tooltip.css) to your side. The new CSS class `.jekyll-glossary-tooltip-hidden` is added and needed to hide parenthesis in the tooltip. 19 | 20 | ## [1.5.1] - 2025-03-10 21 | ### Fixed 22 | - Strip newlines from generated HTML to prevent unnecessary spaces to be added. [#9](https://github.com/erikw/jekyll-glossary_tooltip/issues/9) 23 | 24 | ## [1.5.0] - 2022-09-08 25 | ### Added 26 | - Support for embedded liquid tags in the url field. ([#3](https://github.com/erikw/jekyll-glossary_tooltip/issues/3]) 27 | 28 | ## [1.4.0] - 2021-08-18 29 | ### Changed 30 | * Bump local ruby to 3.1.0 31 | 32 | ## [1.3.1] - 2021-08-18 33 | ### Added 34 | - GitHub workflow to publish to GitHub Packages. 35 | 36 | ## [1.3.1] - 2021-08-18 37 | ### Fixed 38 | - gemspec dependency range stynax. 39 | 40 | ## [1.3.0] - 2021-08-07 41 | ### Changed 42 | - Open the souce link from a tooltip in a new tab. 43 | 44 | ## [1.2.0] - 2021-08-06 45 | ### Added 46 | - Bottom arrow to the tooltip. 47 | - Tooltip hover animation from invisible to visible. 48 | 49 | ### Changed 50 | - Restyle the glossary term bottom border style and color. 51 | 52 | ## [1.1.0] - 2021-08-06 53 | ### Added 54 | - Optional `display:` argument to set a different term display name, rather than using the term name as defined in the glossary file. Usage: `{% glossary term_name, display: Different Name To Display %}`. 55 | 56 | ## [1.0.0] - 2021-08-05 57 | - No changes from `v0.1.0` but just bumping to final first major release version! 58 | 59 | ## [0.1.0] - 2021-08-05 60 | - First release version. The plugin is fully working but I suspect that there might be a few point releases just to nail the release process. Once this is working, there will soon be an 1.0.0 release! 61 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "simplecov" # Must be before any application code. See conf at .simplecov 4 | 5 | require "jekyll" 6 | 7 | Jekyll.logger.log_level = :warn 8 | 9 | # Base directory for fixtures. 10 | SOURCE_DIR = File.expand_path("fixtures", __dir__) 11 | # Base directory for generated files from tests. 12 | DEST_DIR = File.expand_path("dest", __dir__) 13 | 14 | # Tag matching regex parts. 15 | R1 = %r{\s*} 16 | R2 = %r{\s*\s*} 17 | R_PAR_OPEN = %r{\s*\(\s*} 18 | R_PAR_CLOSE = %r{\s*\)\s*} 19 | R3 = %r{\s*\s*} 21 | R5 = %r{\s*} 22 | 23 | # Reference for RSpec config: https://www.rubydoc.info/github/rspec/rspec-core/RSpec/Core/Configuration 24 | # Reference on Jekyll helper functions: 25 | # - https://github.com/jekyll/jekyll-seo-tag/blob/master/spec/spec_helper.rb 26 | # - https://github.com/ayastreb/jekyll-maps/blob/master/spec/spec_helper.rb 27 | RSpec.configure do |config| 28 | config.example_status_persistence_file_path = ".rspec_status" 29 | config.order = :defined 30 | config.disable_monkey_patching! 31 | # Use rspec framework and the 'expect' syntax (instead of 'should') 32 | config.expect_with :rspec do |c| 33 | c.syntax = :expect 34 | end 35 | 36 | # Get absolute path to a file within a fixture in the source dir. 37 | def source_dir(fixture, *filesegments) 38 | File.join(SOURCE_DIR, fixture, *filesegments) 39 | end 40 | 41 | # Get absolute path to file within destination output dir. 42 | def dest_dir(*filesegments) 43 | File.join(DEST_DIR, *filesegments) 44 | end 45 | 46 | def config_defaults 47 | { 48 | "destination" => dest_dir, 49 | "gems" => ["jekyll-glossary_tooltip"] # Called "plugins" in Jekyll >=3.5.0 50 | }.freeze 51 | end 52 | 53 | # Create a Jekyll site from fixtures 54 | def make_site(options = {}) 55 | site_config = Jekyll.configuration(config_defaults.merge(options)) 56 | Jekyll::Site.new(site_config) 57 | end 58 | 59 | # Ensure that there was no site before each example group (=context). 60 | # Remove dest dir. For using in "after :all" blocks 61 | def remove_dest_dir 62 | FileUtils.rm_rf(dest_dir) 63 | end 64 | 65 | # Expect HTML tag components to match given content. 66 | def expect_tag_match(content, term_name, url: true, term_display: nil, href: nil) 67 | term_display ||= term_name 68 | 69 | regex = %r{#{R1}#{term_display}#{R2}#{R_PAR_OPEN}#{term_name} definition} 70 | if url 71 | href ||= "#{term_name} url" 72 | regex = Regexp.new(regex.source + %r{#{R3}#{href}#{R4}}.source) 73 | end 74 | regex = Regexp.new(regex.source + %r{#{R_PAR_CLOSE}#{R5}}.source) 75 | 76 | expect(content).to match(regex) 77 | end 78 | end 79 | -------------------------------------------------------------------------------- /spec/jekyll-glossary_tooltip/tag_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "fileutils" 4 | 5 | require "jekyll-glossary_tooltip/tag" 6 | require "jekyll-glossary_tooltip/errors" 7 | 8 | E = Jekyll::GlossaryTooltip::Errors # Make namespace referencing easier here. 9 | 10 | RSpec.describe Jekyll::GlossaryTooltip::Tag do 11 | after(:context) { remove_dest_dir } 12 | 13 | context "when a site is correctly configured" do 14 | before(:context) do 15 | site = make_site({ "source" => source_dir("normal") }) 16 | site.process 17 | end 18 | 19 | let(:page1) { File.read(dest_dir("page1.html")) } 20 | let(:page2) { File.read(dest_dir("page2.html")) } 21 | let(:page3) { File.read(dest_dir("page3.html")) } 22 | let(:page4) { File.read(dest_dir("page4.html")) } 23 | let(:page5) { File.read(dest_dir("page5.html")) } 24 | let(:page6) { File.read(dest_dir("page6.html")) } 25 | let(:page7) { File.read(dest_dir("page7.html")) } 26 | let(:page8) { File.read(dest_dir("page8.html")) } 27 | 28 | it "renders a glossary tag with a URL" do 29 | expect_tag_match(page1, "term_with_url") 30 | end 31 | 32 | it "renders a glossary tag without a URL" do 33 | expect_tag_match(page2, "term_without_url", url: false) 34 | end 35 | 36 | it "renders a glossary tag from case insensitive lookup" do 37 | expect_tag_match(page3, "TERM_CASE_INSENSITIVE", term_display: "TerM_Case_Insensitive") 38 | end 39 | 40 | it "renders a glossary tag having spaces" do 41 | expect_tag_match(page4, "term with spaces", url: false) 42 | end 43 | 44 | it "renders a glossary tag with alternative display name" do 45 | expect_tag_match(page5, "term_with_url", term_display: "term alt display name") 46 | end 47 | 48 | it "renders a glossary tag with URL rendered from embedded liquid tags" do 49 | expect_tag_match(page6, "term_with_url_embedded_liquid", href: "/page2.html") 50 | end 51 | 52 | it "renders no unnecessary space after tooltip" do 53 | # expect_tag_match(page7, "term_with_url", href: "/page2.html") 54 | term_name = "term_without_url" 55 | regex = %r{#{R1}#{term_name}#{R2}#{R_PAR_OPEN}#{term_name} definition} 56 | regex = Regexp.new(regex.source + %r{#{R_PAR_CLOSE}#{R5}, no space before comma.}.source) 57 | 58 | expect(page7).to match(regex) 59 | end 60 | 61 | it "renders a glossary tag with Unicode characters in display" do 62 | # verify that the parser accepts ‘display’ with accents/Unicode 63 | expect_tag_match(page8, "term_with_url", term_display: "José Álvarez") 64 | end 65 | end 66 | 67 | context "when a site is incorrectly configured (missing term definition)" do 68 | let(:site) { make_site({ "source" => source_dir("missing_definition") }) } 69 | 70 | it "building the site will raise an error" do 71 | expect { site.process }.to raise_error(E::MissingTermDefinition) 72 | end 73 | end 74 | 75 | context "when a site is incorrectly configured (empty term definition)" do 76 | let(:site) { make_site({ "source" => source_dir("empty_definition") }) } 77 | 78 | it "building the site will raise an error" do 79 | expect { site.process }.to raise_error(E::MissingTermDefinition) 80 | end 81 | end 82 | 83 | context "when a site is incorrectly configured (missing glossary file)" do 84 | let(:site) { make_site({ "source" => source_dir("missing_glossary") }) } 85 | 86 | it "building the site will raise an error" do 87 | expect { site.process }.to raise_error(E::NoGlossaryFile) 88 | end 89 | end 90 | 91 | context "when a site is incorrectly configured (missing term in glossary)" do 92 | let(:site) { make_site({ "source" => source_dir("missing_term") }) } 93 | 94 | it "building the site will raise an error" do 95 | expect { site.process }.to raise_error(E::MissingTermEntry) 96 | end 97 | end 98 | 99 | context "when a site is incorrectly configured (duplicate term in glossary)" do 100 | let(:site) { make_site({ "source" => source_dir("duplicate_term") }) } 101 | 102 | it "building the site will raise an error" do 103 | expect { site.process }.to raise_error(E::MultipleTermEntries) 104 | end 105 | end 106 | 107 | context "when a site is incorrectly configured (missing term arg in the tag)" do 108 | let(:site) { make_site({ "source" => source_dir("missing_tag_arg") }) } 109 | 110 | it "building the site will raise an error" do 111 | expect { site.process }.to raise_error(E::OptionsNoTermNameInTag) 112 | end 113 | end 114 | end 115 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | # See pages related at https://docs.rubocop.org/rubocop/1.12/cops_layout.html 2 | 3 | # Rubocop plugins 4 | plugins: 5 | - rubocop-rake 6 | - rubocop-rspec 7 | 8 | inherit_mode: 9 | merge: 10 | - Exclude # Merge my AllCops.Exclude with default excludes from https://github.com/rubocop/rubocop/blob/master/config/default.yml 11 | 12 | AllCops: 13 | TargetRubyVersion: 3.0 14 | Include: 15 | - lib/**/*.rb 16 | - spec/**/*.rb 17 | Exclude: 18 | # Travis: during build there will be a lot of rubocop config files in this path which will cause build failure as the refer to gems which are not installed by this project. 19 | # See https://github.com/rubocop/rubocop/issues/9832 20 | - gemfiles/vendor/bundle/**/* 21 | NewCops: disable 22 | 23 | Gemspec/DeprecatedAttributeAssignment: 24 | Enabled: true 25 | Gemspec/RequireMFA: 26 | Enabled: true 27 | 28 | Layout/LineEndStringConcatenationIndentation: 29 | Enabled: true 30 | Layout/EmptyLineBetweenDefs: 31 | Enabled: true 32 | Exclude: 33 | - lib/jekyll-glossary_tooltip/errors.rb 34 | Layout/SpaceBeforeBrackets: 35 | Enabled: true 36 | 37 | Lint/AmbiguousAssignment: 38 | Enabled: true 39 | Lint/DeprecatedConstants: 40 | Enabled: true 41 | Lint/DuplicateBranch: 42 | Enabled: true 43 | Lint/DuplicateRegexpCharacterClassElement: 44 | Enabled: true 45 | Lint/EmptyBlock: 46 | Enabled: true 47 | Lint/EmptyClass: 48 | Enabled: true 49 | Lint/AmbiguousOperatorPrecedence: 50 | Enabled: true 51 | Lint/AmbiguousRange: 52 | Enabled: true 53 | Lint/IncompatibleIoSelectWithFiberScheduler: 54 | Enabled: true 55 | Lint/RequireRelativeSelfPath: 56 | Enabled: true 57 | Lint/UselessRuby2Keywords: 58 | Enabled: true 59 | Lint/EmptyInPattern: 60 | Enabled: true 61 | Lint/LambdaWithoutLiteralBlock: 62 | Enabled: true 63 | Layout/LineLength: 64 | Max: 120 65 | Lint/NoReturnInBeginEndBlocks: 66 | Enabled: true 67 | Lint/NumberedParameterAssignment: 68 | Enabled: true 69 | Lint/OrAssignmentToConstant: 70 | Enabled: true 71 | Lint/RedundantDirGlobSort: 72 | Enabled: true 73 | Lint/SymbolConversion: 74 | Enabled: true 75 | Lint/ToEnumArguments: 76 | Enabled: true 77 | Lint/TripleQuotes: 78 | Enabled: true 79 | Lint/UnexpectedBlockArity: 80 | Enabled: true 81 | Lint/UnmodifiedReduceAccumulator: 82 | Enabled: true 83 | Lint/UnreachableCode: 84 | Severity: error 85 | Lint/UselessAccessModifier: 86 | Enabled: false 87 | 88 | Metrics/AbcSize: 89 | Enabled: true 90 | Metrics/BlockLength: 91 | Enabled: true 92 | Max: 100 93 | Metrics/MethodLength: 94 | Enabled: true 95 | Max: 25 96 | 97 | Naming/FileName: 98 | Enabled: false 99 | Exclude: 100 | - lib/jekyll-glossary_tooltip.rb 101 | Naming/InclusiveLanguage: 102 | Enabled: true 103 | Naming/BlockForwarding: 104 | Enabled: true 105 | 106 | Security/IoMethods: 107 | Enabled: true 108 | 109 | Style/ArgumentsForwarding: 110 | Enabled: true 111 | Style/CollectionCompact: 112 | Enabled: true 113 | Style/Documentation: 114 | Enabled: true 115 | Exclude: 116 | - lib/jekyll-glossary_tooltip/errors.rb 117 | Style/DocumentDynamicEvalDefinition: 118 | Enabled: true 119 | Style/EndlessMethod: 120 | Enabled: true 121 | Style/HashConversion: 122 | Enabled: true 123 | Style/HashExcept: 124 | Enabled: true 125 | Style/IfWithBooleanLiteralBranches: 126 | Enabled: true 127 | Style/InPatternThen: 128 | Enabled: true 129 | Style/MultilineInPatternThen: 130 | Enabled: true 131 | Style/NegatedIfElseCondition: 132 | Enabled: true 133 | Style/NilLambda: 134 | Enabled: true 135 | Style/QuotedSymbols: 136 | Enabled: true 137 | Style/RedundantArgument: 138 | Enabled: true 139 | Style/RegexpLiteral: 140 | Enabled: false 141 | Style/SingleLineMethods: 142 | Enabled: true 143 | Exclude: 144 | - lib/jekyll-glossary_tooltip/errors.rb 145 | Style/StringChars: 146 | Enabled: true 147 | Style/StringLiterals: 148 | Enabled: true 149 | EnforcedStyle: double_quotes 150 | Style/StringLiteralsInInterpolation: 151 | Enabled: true 152 | EnforcedStyle: double_quotes 153 | Style/SwapValues: 154 | Enabled: true 155 | Style/FileRead: 156 | Enabled: true 157 | Style/FileWrite: 158 | Enabled: true 159 | Style/MapToHash: 160 | Enabled: true 161 | Style/NumberedParameters: 162 | Enabled: true 163 | Style/NumberedParametersLimit: 164 | Enabled: true 165 | Style/OpenStructUse: 166 | Enabled: true 167 | Style/RedundantSelfAssignmentBranch: 168 | Enabled: true 169 | Style/SelectByRegexp: 170 | Enabled: true 171 | 172 | 173 | # Reference: https://github.com/rubocop/rubocop-rake/blob/master/config/default.yml 174 | Rake: 175 | Enabled: true 176 | 177 | # Reference: 178 | # https://docs.rubocop.org/rubocop-rspec/cops_rspec.html 179 | # https://github.com/rubocop/rubocop-rspec/blob/master/config/default.yml 180 | RSpec: 181 | Enabled: true 182 | RSpec/IdenticalEqualityAssertion: 183 | Enabled: true 184 | RSpec/BeforeAfterAll: 185 | Enabled: false 186 | RSpec/ExcessiveDocstringSpacing: 187 | Enabled: true 188 | RSpec/SubjectDeclaration: 189 | Enabled: true 190 | RSpec/MultipleMemoizedHelpers: 191 | Enabled: true 192 | Max: 10 193 | RSpec/SpecFilePathFormat: 194 | # Disable to match structure of lib/. For jekyll plugin inclusion convention features, not having lib/jekyll/sth.rb but lib/jekyll-sth.rb 195 | Enabled: false 196 | RSpec/SpecFilePathSuffix: 197 | # Disable to match structure of lib/. For jekyll plugin inclusion convention features, not having lib/jekyll/sth.rb but lib/jekyll-sth.rb 198 | Enabled: false 199 | RSpec/IndexedLet: 200 | Enabled: false 201 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Jekyll Glossary Tooltip Tag Plugin [![Post on X](https://img.shields.io/twitter/url/http/shields.io.svg?style=social)](https://x.com/intent/tweet?text=Get%20a%20nifty%20tooltip%20for%20term%20definitions%20in%20your%20Jekyll%20blog%20with%20this%20plugin&url=https://github.com/erikw/jekyll-glossary_tooltip&via=erik_westrup&hashtags=jekyll,plugin) 2 | [![Gem Version](https://badge.fury.io/rb/jekyll-glossary_tooltip.svg)](https://badge.fury.io/rb/jekyll-glossary_tooltip) 3 | [![Gem Downloads](https://img.shields.io/gem/dt/jekyll-glossary_tooltip?label=gem%20downloads)](https://rubygems.org/gems/jekyll-glossary_tooltip) 4 | [![Continuous Integration](https://github.com/erikw/jekyll-glossary_tooltip/actions/workflows/ci.yml/badge.svg)](https://github.com/erikw/jekyll-glossary_tooltip/actions/workflows/ci.yml) 5 | [![Continuous Deployment](https://github.com/erikw/jekyll-glossary_tooltip/actions/workflows/cd.yml/badge.svg)](https://github.com/erikw/jekyll-glossary_tooltip/actions/workflows/cd.yml) 6 | [![CodeQL](https://github.com/erikw/jekyll-glossary_tooltip/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/erikw/jekyll-glossary_tooltip/actions/workflows/codeql-analysis.yml) 7 | [![pages-build-deployment](https://github.com/erikw/jekyll-glossary_tooltip/actions/workflows/pages/pages-build-deployment/badge.svg)](https://github.com/erikw/jekyll-glossary_tooltip/actions/workflows/pages/pages-build-deployment) 8 | [![Code Climate Maintainability](https://qlty.sh/gh/erikw/projects/jekyll-glossary_tooltip/maintainability)](https://qlty.sh/gh/erikw/projects/jekyll-glossary_tooltip) 9 | [![Code Climate Test Coverage](https://qlty.sh/gh/erikw/projects/jekyll-glossary_tooltip/test_coverage)](https://qlty.sh/gh/erikw/projects/jekyll-glossary_tooltip) 10 | [![SLOC](https://sloc.xyz/github/erikw/jekyll-glossary_tooltip?lower=true)](#) 11 | [![Number of programming languages used](https://img.shields.io/github/languages/count/erikw/jekyll-glossary_tooltip)](#) 12 | [![Top programming languages used](https://img.shields.io/github/languages/top/erikw/jekyll-glossary_tooltip)](#) 13 | [![License](https://img.shields.io/github/license/erikw/jekyll-glossary_tooltip)](LICENSE.txt) 14 | [![OSS Lifecycle](https://img.shields.io/osslifecycle/erikw/jekyll-glossary_tooltip)](https://github.com/Netflix/osstracker) 15 | 16 |

17 | 18 | Open in GitHub Codespaces 19 |

20 | 21 | > [!TIP] 22 | > :point_right: **Live demo**: https://erikw.github.io/jekyll-glossary_tooltip/ 23 | 24 | 25 | Screenshot of the glossary tooltip term definition 26 | 27 | This plugin simplifies things for your website visitors and you by making it easy to define terms or abbreviations that need an explanation. Define a common dictionary of terms and their definition in a YAML file. Then, inside the markdown file, you can use the provided glossary liquid tag to insert a tooltip for a defined word from the dictionary. The tooltip will show the term definition on mouse hover. 28 | 29 | It's also possible to provide an optional URL for, for example, a term definition source reference. To also support mobile devices, this URL link is placed inside the tooltip pop-up itself, rather than making the term itself clickable. This is so that on a mobile device, you will first tap the word to get the hover tooltip, then click the link in the tooltip if desired. 30 | 31 | 32 | # Installation 33 | 1. Add this gem to your Jekyll site's Gemfile in the `:jekyll_plugins` group: 34 | * On CLI (in project root directory): 35 | ```shell 36 | bundle add --group jekyll_plugins jekyll-glossary_tooltip 37 | ``` 38 | * Or manually: 39 | ```ruby 40 | group :jekyll_plugins do 41 | [...] 42 | gem 'jekyll-glossary_tooltip' 43 | end 44 | ``` 45 | 1. Run `$ bundle install`. 46 | 1. In your site's `_config.yml`, enable the plugin: 47 | ```yml 48 | plugins: 49 | - jekyll-glossary_tooltip 50 | ``` 51 | 1. Create a `_data/glossary.yml` file, according to the 'Glossary Term Definition File' section below, with your terms. 52 | 1. Use the liquid tag in a page like `{% glossary term_name %}` 53 | 1. Add CSS styling for the tooltip from [jekyll-glossary_tooltip.css](lib/jekyll-glossary_tooltip/jekyll-glossary_tooltip.css). You need to make sure that the pages where you will use the glossary tag have this styling applied. Typically, this would mean 1) copying this file to your `assets/css/` directory, 2) editing your theme's template for blog posts (or what pages you desire) to include this CSS in the header like ``. However, you could also copy this file's content into your `main.css` or `main.scss` or however you build your site's CSS. 54 | 1. Now just build your site, and you will get nice, nice term definition tooltips on mouse hover (or mobile, tap) for your terms! 55 | ```shell 56 | bundle exec jekyll build 57 | ``` 58 | 59 | # Usage 60 | ## Glossary Term Definition File 61 | Create a file `_data/glossary.yml` to host your shared term definition entries. This file should contain a list of term entries in the format of: 62 | 63 | ```markdown 64 | - term: a_term_name # Can contain spaces 65 | definition: A description of the term 66 | url: https://jekyllrb.com/ # Is optional 67 | - term: another term 68 | definition: Some other term definition text 69 | ``` 70 | 71 | This could look something like: 72 | ```markdown 73 | - term: Jekyll 74 | definition: A Static Site Generator (SSG) built with Ruby. Widely adopted as of GitHub Pages inclusion. 75 | url: https://jekyllrb.com/ 76 | - term: SSG 77 | definition: A Static Site Generator compiles the website before deployment. Then the generated web content is simply retrieved as-is by the client without any code running at retrieval time. 78 | - term: Jamstack 79 | definition: JavaScript + API + Markup - a way of building and hosting websites. 80 | url: https://jamstack.org/ 81 | - term: EmbeddedLiquidURL 82 | definition: This definition has a URL with embedded liquid tags to make it dynamic at build time. Note special YAML syntax for being able to use liquid (1.) 83 | url: > 84 | {{ site.baseurl }}{% link page2.md %} 85 | ``` 86 | 87 | 1. See [here](https://documentation.platformos.com/use-cases/using-liquid-markup-yaml) for notes on using Liquid in YAML. 88 | 89 | 90 | ## Tag Usage 91 | On any page where you've made sure to include the needed CSS styling, you can use the glossary tag simply like 92 | 93 | ```markdown 94 | Here I'm talking about {% glossary term_name %} in a blog post. 95 | 96 | The term name can contain spaces like {% glossary operating system %}. 97 | 98 | Even if the term is defined in _data/glossary.yml as 'term_name', the matching is case-insensitive 99 | meaning that I can look it up using {% glossary TeRM_NaME %}. Note that the term is displayed as 100 | defined in the tag rather than the definition, here meaning 'TeRM_NaME'. 101 | 102 | The case-styling above works as there's still a case-insensitive match. But what about when you 103 | actually want to display the term differently? Maybe the term is defined as "cat," but you want to 104 | use the plural "cats"? Then you can supply an optional `display` argument. The syntax is: 105 | {% glossary , display: %} 106 | 107 | This could be, e.g. 108 | {% glossary cat, display: cats %} 109 | {% glossary some term, display: some other display text %} 110 | ``` 111 | 112 | > [!WARNING] 113 | > A term name can not contain a `,`, as this is the argument separator character. 114 | 115 | 116 | ## CSS Style Override 117 | Simply modify the rules [jekyll-glossary_tooltip.css](lib/jekyll-glossary_tooltip/jekyll-glossary_tooltip.css) that you copied to your project. The tooltip is based on this [tutorial](https://www.w3schools.com/css/css_tooltip.asp). View the generated HTML output to see the HTML tags that are styled, or check the [tag.rb](lib/jekyll-glossary_tooltip/tag.rb) implementation in the method `render()`. 118 | 119 | 120 | ## Page listing all terms 121 | Thanks to this user-submitted [idea](https://github.com/erikw/jekyll-glossary_tooltip/issues/6) we have a way to create a page listing all terms with definitions and URLs. 122 | 123 | :point_right: **Live demo**: https://erikw.github.io/jekyll-glossary_tooltip/glossary.html 124 | 125 | Steps: 126 | 1. Install [jekyll-liquify](https://github.com/gemfarmer/jekyll-liquify) to your site according to its README instructions. 127 | 1. Create a new page in your site similar to [glossary.md](https://github.com/erikw/jekyll-glossary_tooltip/blob/gh-pages-source/glossary.md?plain=1) 128 | 1. Build your site and access your new page listing all terms! 129 | 130 | # Development 131 | The structure of this plugin was inspired by [https://ayastreb.me/writing-a-jekyll-plugin/](https://ayastreb.me/writing-a-jekyll-plugin/), the plugin jekyll-sitemap and the [Bundler Gem tutorial](https://bundler.io/guides/creating_gem.html). 132 | 133 | After checking out the repo; 134 | 1. Install [RVM](https://rvm.io/rvm/install) and install a supported Ruby version (see .gemspec) 135 | 1. run `scripts/setup` to install dependencies 136 | 1. run `scripts/test` to run the tests 137 | * Run a specific test with `bundle exec rspec -e "name of test example"` 138 | 1. You can also run `scripts/console` for an interactive prompt that will allow you to experiment. 139 | 140 | To install this gem onto your local machine, run `bundle exec rake install`. 141 | 142 | 143 | ## Local development 144 | Following the setup at [how-to-specify-local-ruby-gems-in-your-gemfile](https://rossta.net/blog/how-to-specify-local-ruby-gems-in-your-gemfile.html), these are the steps needed to build a jekyll site with a local clone of this plugin for local testing. 145 | 146 | 1. Clone this repo to your machine, say at `~/src/jekyll-glossary_tooltip` 147 | 1. In your Jekyll project's `Gemfile`: 148 | - Remove the `gem 'jekyll-glossary_tooltip'` part 149 | - Add instead `gem 'jekyll-glossary_tooltip', github: 'erikw/jekyll-glossary_tooltip', branch: 'main'` 150 | 1. Configure bundler to use a local path for this gem in this project: 151 | - `$ bundle config --local local.jekyll-glossary_tooltip ~/src/jekyll-glossary_tooltip` 152 | 1. Update the project: `$ bundle install` 153 | 1. Now the project will build with the local clone of this plugin when issuing e.g., `bundle exec jekyll build` 154 | 1. When you're done: 155 | - Remove the local override with: `$ bundle config --delete local.jekyll-glossary_tooltip` 156 | - Optionally restore the original gem included in `Gemfile` or keep building from a branch in the GitHub repo. 157 | 158 | ## Releasing 159 | Instructions for releasing on rubygems.org are below. Optionally make a GitHub [release](https://github.com/erikw/jekyll-glossary_tooltip/releases) after this for the pushed git tag. 160 | 161 | ## Using bundler/gem_tasks rake tasks 162 | Following instructions from [bundler.io](https://bundler.io/guides/creating_gem.html#releasing-the-gem): 163 | ```shell 164 | vi -p lib/jekyll-glossary_tooltip/version.rb CHANGELOG.md 165 | bundle exec rake build 166 | ver=$(ruby -r ./lib/jekyll-glossary_tooltip/version.rb -e 'puts Jekyll::GlossaryTooltip::VERSION') 167 | 168 | # Optional: test locally by including in another project 169 | gem install pkg/jekyll-glossary_tooltip-$ver.gem 170 | 171 | bundle exec rake release 172 | ``` 173 | 174 | ## [recommended] Using gem-release gem extension 175 | Using [gem-release](https://github.com/svenfuchs/gem-release): 176 | ```shell 177 | vi CHANGELOG.md && git add CHANGELOG.md && git commit -m "Update CHANGELOG.md" && git push 178 | gem signin 179 | gem bump --version minor --tag --push --release --sign 180 | ``` 181 | For `--version`, use `major|minor|patch` as needed. 182 | 183 | ## Multi-versions 184 | * For Ruby, just use RVM to switch between supported Ruby versions specified in `.gemspec`. 185 | * To run with different jekyll versions, [Appraisal](https://github.com/thoughtbot/appraisal) is used with [`Appraisals`](Appraisals) to generate different [`gemfiles/`](gemfiles/) 186 | - To use a specific Gemfile, run like 187 | ```shell 188 | BUNDLE_GEMFILE=gemfiles/jekyll_4.x.x.gemfile bundle exec rake spec 189 | bundle exec appraisal jekyll-4.x.x rake spec 190 | ``` 191 | - To run `rake spec` for all gemfiles: 192 | ```shell 193 | bundle exec appraisal rake spec 194 | ``` 195 | - To generate new/updated gemfiles from `Appraisals` 196 | ```shell 197 | bundle exec appraisal install 198 | bundle exec appraisal generate 199 | ``` 200 | 201 | ## Live Demo GitHub Pages 202 | The live demo source is in the branch [`gh-pages-source`](https://github.com/erikw/jekyll-glossary_tooltip/tree/gh-pages-source). Check its `README.md`! 203 | 204 | # Contributing 205 | Bug reports and pull requests are welcome on GitHub at https://github.com/erikw/jekyll-glossary_tooltip. 206 | 207 | # License 208 | The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT). 209 | 210 | # Acknowledgement 211 | Thanks to [ayastreb/jekyll-maps](https://github.com/ayastreb/jekyll-maps) for inspiration on project structure and options parsing! 212 | 213 | # More Jekyll 214 | Check out my other Jekyll repositories [here](https://github.com/erikw?tab=repositories&q=jekyll-&type=&language=&sort=). 215 | --------------------------------------------------------------------------------