diff --git a/Gemfile b/Gemfile index c46caed..46eb342 100644 --- a/Gemfile +++ b/Gemfile @@ -2,4 +2,5 @@ source 'https://rubygems.org' +gem 'addressable', '~> 2.8' gem 'i18n', '~> 1.14' diff --git a/examples/full.gmi b/examples/full.gmi index 0e9be72..bbb49e3 100644 --- a/examples/full.gmi +++ b/examples/full.gmi @@ -34,3 +34,15 @@ Chapter 2 paragraph text Gemtext epilogue + + + +## Related articles + +=> ./foo-bar 1) 2024-09-12 Foo Bar + + + +## Links + +=> https://example.com 2) Example Domain diff --git a/examples/full.html b/examples/full.html index 1b6df1b..94c2bf4 100644 --- a/examples/full.html +++ b/examples/full.html @@ -81,3 +81,23 @@ Chapter 2 paragraph text
HTTP epilogue
+

Related articles

+ +
    +
  1. + + + +Foo Bar +
  2. +
+

Links

+ +
    + +
diff --git a/examples/full.repub b/examples/full.repub index 3efd649..a91e0a2 100644 --- a/examples/full.repub +++ b/examples/full.repub @@ -58,3 +58,30 @@ article.epilogue do |custom| custom.for :html, 'HTTP epilogue' custom.for :gemtext, 'Gemtext epilogue' end + +article.footnotes_category 'Related articles' do |category| + category.footnote( + category: :self, + slug: 'foo-bar', + index: 1, + url: './foo-bar', + text: 'Foo Bar', + date: '2024-09-12', + ) +end + +article.footnotes_category 'Links' do |category| + category.footnote( + category: :link, + slug: 'example-com', + index: 2, + url: 'https://example.com', + text: 'Example Domain', + alt_urls: [ + { + name: 'web.archive.org', + url: 'https://web.archive.org/arch/example.com', + }, + ], + ) +end diff --git a/lib/repubmark.rb b/lib/repubmark.rb index dd6ce0a..314f721 100644 --- a/lib/repubmark.rb +++ b/lib/repubmark.rb @@ -1,12 +1,15 @@ # frozen_string_literal: true require 'cgi' +require 'date' require 'forwardable' require 'open3' require 'pathname' require 'uri' require 'yaml' +require 'addressable' + require_relative 'repubmark/config' require_relative 'repubmark/highlight' require_relative 'repubmark/titled_ref' @@ -19,6 +22,7 @@ require_relative 'repubmark/elems/article' # Always inside Article require_relative 'repubmark/elems/annotation' require_relative 'repubmark/elems/custom_logue' +require_relative 'repubmark/elems/footnotes_category' # Always inside Article, Chapter require_relative 'repubmark/elems/chapter' # Always inside Annotation, Blockquote, Chapter @@ -57,6 +61,9 @@ require_relative 'repubmark/elems/text' require_relative 'repubmark/elems/link' require_relative 'repubmark/elems/power' +# Always inside FootnotesCategory +require_relative 'repubmark/elems/footnote' + module Repubmark FORMATS = %i[gemtext html summary_plain word_count].freeze diff --git a/lib/repubmark/elems/article.rb b/lib/repubmark/elems/article.rb index 2922d34..2c14bae 100644 --- a/lib/repubmark/elems/article.rb +++ b/lib/repubmark/elems/article.rb @@ -8,6 +8,7 @@ module Repubmark def initialize(config) # rubocop:disable Lint/MissingSuper @parent = nil self.config = config + @footnotes_categories = [] end ################# @@ -32,6 +33,7 @@ module Repubmark @annotation&.to_html, @chapter&.to_html, @epilogue&.to_html, + *@footnotes_categories.map(&:to_html), ].compact.join.freeze end @@ -41,6 +43,7 @@ module Repubmark @annotation&.to_gemtext, @chapter&.to_gemtext, @epilogue&.to_gemtext, + *@footnotes_categories.map(&:to_gemtext), ].compact.join("\n\n\n").freeze end @@ -94,6 +97,13 @@ module Repubmark nil end + def footnotes_category(name) + footnotes_category = FootnotesCategory.new self, name + @footnotes_categories << footnotes_category + yield footnotes_category + nil + end + private def config=(config) diff --git a/lib/repubmark/elems/footnote.rb b/lib/repubmark/elems/footnote.rb new file mode 100644 index 0000000..ebf4688 --- /dev/null +++ b/lib/repubmark/elems/footnote.rb @@ -0,0 +1,74 @@ +# frozen_string_literal: true + +module Repubmark + module Elems + class Footnote < Base + EM_DASH = '—' + + parents :FootnotesCategory + + attr_reader :category, :slug, :index, :url, + :text, :descr, :date, :alt_urls + + def initialize(parent, **kwargs) + super parent + kwargs.each { |k, v| send :"#{k}=", v } + end + + ################# + # Basic methods # + ################# + + def to_html + result = '' + result += + %(
  • \n) + if date + result += %(\n) + result += %(\n" + result += "\n" + end + if url + scheme = Addressable::URI.parse(url).scheme&.downcase + if scheme + unless %w[http https].include? scheme + result += %(\n) + result += %(#{scheme}://\n) + result += "\n" + end + end + result += %(#{text}\n) + else + result += %(#{text}\n) + end + result += " — #{descr}\n" if descr + if alt_urls.any? + result += %[(#{ + alt_urls.map do |alt_url| + %(#{alt_url[:name]}) + end.join ', ' + })\n] + end + result += '
  • ' + result.freeze + end + + def to_gemtext + result = '' + result += url ? "=> #{url}" : '*' + result += " #{index})" + result += " #{Date.parse(date)}" if date + result += " #{text}" + result += " #{EM_DASH} #{descr}" if descr + result.freeze + end + + private + + attr_writer :category, :slug, :index, :url, + :text, :descr, :date, :alt_urls + end + end +end diff --git a/lib/repubmark/elems/footnotes_category.rb b/lib/repubmark/elems/footnotes_category.rb new file mode 100644 index 0000000..54aa4c9 --- /dev/null +++ b/lib/repubmark/elems/footnotes_category.rb @@ -0,0 +1,46 @@ +# frozen_string_literal: true + +module Repubmark + module Elems + class FootnotesCategory < Base + parents :Article + + def initialize(parent, title) + super parent + @title = title + @footnotes = [] + end + + ################# + # Basic methods # + ################# + + def to_html + <<~HTML +

    #@title

    + +
      + #{@footnotes.map(&:to_html).map(&:strip).join("\n")} +
    + HTML + end + + def to_gemtext + <<~GEMTEXT + ## #@title + + #{@footnotes.map(&:to_gemtext).map(&:strip).join("\n")} + GEMTEXT + end + + ################### + # Builder methods # + ################### + + def footnote(**kwargs) + @footnotes << Footnote.new(self, **kwargs) + nil + end + end + end +end