Compare commits

...

24 Commits

Author SHA1 Message Date
dd30002f2d
Modularize spec tests for HTML5-modified converter 2023-11-24 20:37:47 +08:00
ac2c00567e
Add spec tests for video block 2023-11-24 20:35:36 +08:00
6e2c022c45
Add image block for HTML5-modified 2023-11-24 20:31:25 +08:00
bb0e461197
Update fallback text for video block 2023-11-24 20:29:20 +08:00
e0aed2313d
Add autoplay, preload, and metadata attribute for video block 2023-11-24 20:28:52 +08:00
e7e973d1c6
Add sidebar block for HTML5-modified 2023-11-24 11:47:28 +08:00
d868c0964c
Add video block for HTML5-modified 2023-11-24 11:43:33 +08:00
09a93481f0
Add basic Asciidoc conversion with API test for chat block 2023-11-24 11:42:33 +08:00
51d4aa1e99
Update flake description 2023-11-24 11:42:08 +08:00
d704253410
Refactor common functions 2023-11-24 11:34:10 +08:00
a57804d519
Update project README 2023-11-24 11:33:32 +08:00
b2d8534999
Update Rubocop configuration 2023-11-24 11:29:28 +08:00
36dffaf3e4
Add audio block for HTML5-modified 2023-11-24 11:28:07 +08:00
153e110489
Add admonition block for HTML5-modified 2023-11-24 11:23:06 +08:00
74c48a345f
Add spec tests for HTML5-modified converter 2023-11-23 10:48:08 +08:00
a6283b72a1
Convert to Nokogiri for HTML manipulation 2023-11-23 10:11:20 +08:00
66233f04e6
Update flake and devshell 2023-11-17 22:11:13 +08:00
83aad53d74
Add forked bundix to the devshell
Just to make it easier to set up for next time.
2023-11-17 22:02:56 +08:00
887d2a675e
Update dependencies and generate proper gemset.nix
I keep forgetting that I've been generating the gemset.nix file from the
nixpkgs bundix instead of the forked bundix. It is not easy to install
some of the more problematic gems like Nokogiri in this way.
2023-11-17 22:01:24 +08:00
c6f2363ca3
Prepare for next development version 2023-11-16 20:17:25 +08:00
71fe2602c2
Update default custom converter name 2023-11-16 20:16:48 +08:00
586cc8daee
Update chat block attributes 2023-11-16 20:15:57 +08:00
5cb366fb2a
Add modified version of built-in HTML5 converter 2023-11-16 19:40:15 +08:00
f0b69b8af7
Release v1.2.1 2023-11-08 11:40:07 +08:00
19 changed files with 924 additions and 97 deletions

View File

@ -1,5 +1,14 @@
Metrics/BlockLength:
Enabled: false
Metrics/ClassLength:
Enabled: false
Metrics/MethodLength:
Enabled: false
Metrics/AbcSize:
Enabled: false
Style/ClassAndModuleChildren:
Enabled: false

View File

@ -1,8 +1,10 @@
PATH
remote: .
specs:
asciidoctor-foodogsquared-extensions (1.1.0)
asciidoctor-foodogsquared-extensions (1.3.0)
asciidoctor (~> 2.0)
mime-types (~> 3.0)
nokogiri (~> 1.0)
rugged (~> 1.0)
GEM
@ -13,16 +15,21 @@ GEM
diff-lcs (1.5.0)
json (2.6.3)
language_server-protocol (3.17.0.3)
mime-types (3.5.1)
mime-types-data (~> 3.2015)
mime-types-data (3.2023.1003)
nokogiri (1.15.4-x86_64-linux)
racc (~> 1.4)
parallel (1.23.0)
parser (3.2.2.3)
parser (3.2.2.4)
ast (~> 2.4.1)
racc
prettier_print (1.2.1)
racc (1.7.1)
prism (0.17.1)
racc (1.7.3)
rainbow (3.1.1)
rake (13.0.6)
regexp_parser (2.8.1)
rexml (3.2.5)
rake (13.1.0)
regexp_parser (2.8.2)
rexml (3.2.6)
rspec (3.12.0)
rspec-core (~> 3.12.0)
rspec-expectations (~> 3.12.0)
@ -36,29 +43,27 @@ GEM
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.12.0)
rspec-support (3.12.1)
rubocop (1.54.1)
rubocop (1.57.2)
json (~> 2.3)
language_server-protocol (>= 3.17.0)
parallel (~> 1.10)
parser (>= 3.2.2.3)
parser (>= 3.2.2.4)
rainbow (>= 2.2.2, < 4.0)
regexp_parser (>= 1.8, < 3.0)
rexml (>= 3.2.5, < 4.0)
rubocop-ast (>= 1.28.0, < 2.0)
rubocop-ast (>= 1.28.1, < 2.0)
ruby-progressbar (~> 1.7)
unicode-display_width (>= 2.4.0, < 3.0)
rubocop-ast (1.29.0)
rubocop-ast (1.30.0)
parser (>= 3.2.1.0)
ruby-lsp (0.6.2)
ruby-lsp (0.12.3)
language_server-protocol (~> 3.17.0)
sorbet-runtime
syntax_tree (>= 6.1.1, < 7)
prism (>= 0.17.1, < 0.18)
sorbet-runtime (>= 0.5.5685)
ruby-progressbar (1.13.0)
rugged (1.6.3)
sorbet-runtime (0.5.10902)
syntax_tree (6.1.1)
prettier_print (>= 1.2.0)
unicode-display_width (2.4.2)
rugged (1.7.1)
sorbet-runtime (0.5.11139)
unicode-display_width (2.5.0)
PLATFORMS
x86_64-linux
@ -71,4 +76,4 @@ DEPENDENCIES
ruby-lsp
BUNDLED WITH
2.4.14
2.4.20

View File

@ -14,6 +14,8 @@ Who knows, parts of the set might develop into a more presentable and stable ext
The extension is required to be used with Ruby 3.0 and above.
(At least until I can test and confirm it works on the previous versions.)
For now, it explicitly supports CRuby and that's it. footnote:[Mainly due to Nokogiri but I'll look for other HTML processors.]
In order to use it, you have to install it first in your Ruby environment.
It is available as a gem in RubyGems as `asciidoctor-foodogsquared-extensions` so you can add the following line in `Gemfile`.

View File

@ -1,6 +1,6 @@
Gem::Specification.new do |s|
s.name = 'asciidoctor-foodogsquared-extensions'
s.version = '1.1.0'
s.version = '1.3.0'
s.licenses = ['MIT']
s.summary = "foo-dogsquared's custom Asciidoctor extensions"
s.description = <<-DESC
@ -18,6 +18,8 @@ Gem::Specification.new do |s|
s.files = Dir['lib/**/*', '*.gemspec', 'LICENSE', 'CHANGELOG.adoc', 'README.adoc']
s.add_runtime_dependency 'asciidoctor', '~> 2.0'
s.add_runtime_dependency 'mime-types', '~> 3.0'
s.add_runtime_dependency 'nokogiri', '~> 1.0'
s.add_runtime_dependency 'rugged', '~> 1.0'
s.add_development_dependency 'rspec', '~> 3.0'

27
flake.lock generated
View File

@ -38,7 +38,8 @@
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs",
"ruby-nix": "ruby-nix"
"ruby-nix": "ruby-nix",
"ruby-nix-bundix": "ruby-nix-bundix"
}
},
"ruby-nix": {
@ -50,17 +51,37 @@
"locked": {
"lastModified": 1688825976,
"narHash": "sha256-7EvZu+/+uNZ0kfSgQQIrRGg8RXcUN9UgRsobjbxEdBQ=",
"owner": "sagittaros",
"owner": "inscapist",
"repo": "ruby-nix",
"rev": "911d5e8584453e1aff0fb85c93aa163dcdd0f3f0",
"type": "github"
},
"original": {
"owner": "sagittaros",
"owner": "inscapist",
"repo": "ruby-nix",
"type": "github"
}
},
"ruby-nix-bundix": {
"inputs": {
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1688825865,
"narHash": "sha256-7HP6amLkY3u2NvPguanIW/gugUvGsmY5OMY6tKpjMrA=",
"owner": "inscapist",
"repo": "bundix",
"rev": "5cb01869cb09fb367c02527b1f66707fb9277076",
"type": "github"
},
"original": {
"owner": "inscapist",
"repo": "bundix",
"type": "github"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,

View File

@ -1,12 +1,15 @@
{
description = "Basic flake template for setting up development shells";
description = "Flake for setting up asciidoctor-foodogsquared-extensions";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
flake-utils.url = "github:numtide/flake-utils";
ruby-nix.url = "github:sagittaros/ruby-nix";
ruby-nix.url = "github:inscapist/ruby-nix";
ruby-nix.inputs.nixpkgs.follows = "nixpkgs";
ruby-nix-bundix.url = "github:inscapist/bundix";
ruby-nix-bundix.inputs.nixpkgs.follows = "nixpkgs";
};
outputs = inputs@{ self, ruby-nix, nixpkgs, ... }:
@ -14,10 +17,24 @@
in inputs.flake-utils.lib.eachSystem systems (system:
let
pkgs = import nixpkgs { inherit system; };
gems = ruby-nix.lib pkgs {
name = "asciidoctor-foodogsquared-extensions";
ruby = pkgs.ruby_3_1;
gemset = ./gemset.nix;
};
in
{
devShells.default =
import ./shell.nix { inherit pkgs ruby-nix; };
import ./shell.nix {
inherit pkgs;
extraBuildInputs = [
gems.env
gems.ruby
];
extraPackages = [
inputs.ruby-nix-bundix.packages."${system}".default
];
};
formatter = pkgs.treefmt;
});

View File

@ -5,19 +5,22 @@
source = {
remotes = ["https://rubygems.org"];
sha256 = "0yblqlbix3is5ihiqrpbfazb44in7ichfkjzdbsqibp48paanpl3";
target = "ruby";
type = "gem";
};
targets = [];
version = "2.0.20";
};
asciidoctor-foodogsquared-extensions = {
dependencies = ["asciidoctor" "rugged"];
dependencies = ["asciidoctor" "mime-types" "nokogiri" "rugged"];
groups = ["default"];
platforms = [];
source = {
path = ./.;
path = ".";
type = "path";
};
version = "1.1.0";
targets = [];
version = "1.3.0";
};
ast = {
groups = ["default" "lint"];
@ -25,8 +28,10 @@
source = {
remotes = ["https://rubygems.org"];
sha256 = "04nc8x27hlzlrr5c2gn7mar4vdr0apw5xg22wp6m8dx3wqr04a0y";
target = "ruby";
type = "gem";
};
targets = [];
version = "2.4.2";
};
diff-lcs = {
@ -35,8 +40,10 @@
source = {
remotes = ["https://rubygems.org"];
sha256 = "0rwvjahnp7cpmracd8x732rjgnilqv2sx7d1gfrysslc3h039fa9";
target = "ruby";
type = "gem";
};
targets = [];
version = "1.5.0";
};
json = {
@ -45,8 +52,10 @@
source = {
remotes = ["https://rubygems.org"];
sha256 = "0nalhin1gda4v8ybk6lq8f407cgfrj6qzn234yra4ipkmlbfmal6";
target = "ruby";
type = "gem";
};
targets = [];
version = "2.6.3";
};
language_server-protocol = {
@ -55,18 +64,62 @@
source = {
remotes = ["https://rubygems.org"];
sha256 = "0gvb1j8xsqxms9mww01rmdl78zkd72zgxaap56bhv8j45z05hp1x";
target = "ruby";
type = "gem";
};
targets = [];
version = "3.17.0.3";
};
mime-types = {
dependencies = ["mime-types-data"];
groups = ["default"];
platforms = [];
source = {
remotes = ["https://rubygems.org"];
sha256 = "0q8d881k1b3rbsfcdi3fx0b5vpdr5wcrhn88r2d9j7zjdkxp5mw5";
target = "ruby";
type = "gem";
};
targets = [];
version = "3.5.1";
};
mime-types-data = {
groups = ["default"];
platforms = [];
source = {
remotes = ["https://rubygems.org"];
sha256 = "0yjv0apysnrhbc70ralinfpcqn9382lxr643swp7a5sdwpa9cyqg";
target = "ruby";
type = "gem";
};
targets = [];
version = "3.2023.1003";
};
nokogiri = {
dependencies = ["racc"];
groups = ["default"];
platforms = [];
source = null;
targets = [{
remotes = ["https://rubygems.org"];
sha256 = "0hhqzm7p4lww7v3i33im26bmiryfqr0p3iknbadyv5ypf8yysb47";
target = "x86_64-linux";
targetCPU = "x86_64";
targetOS = "linux";
type = "gem";
}];
version = "1.15.4";
};
parallel = {
groups = ["default" "lint"];
platforms = [];
source = {
remotes = ["https://rubygems.org"];
sha256 = "0jcc512l38c0c163ni3jgskvq1vc3mr8ly5pvjijzwvfml9lf597";
target = "ruby";
type = "gem";
};
targets = [];
version = "1.23.0";
};
parser = {
@ -75,30 +128,36 @@
platforms = [];
source = {
remotes = ["https://rubygems.org"];
sha256 = "1swigds85jddb5gshll1g8lkmbcgbcp9bi1d4nigwvxki8smys0h";
sha256 = "0r69dbh6h6j4d54isany2ir4ni4gf2ysvk3k44awi6amz18nggpd";
target = "ruby";
type = "gem";
};
version = "3.2.2.3";
targets = [];
version = "3.2.2.4";
};
prettier_print = {
prism = {
groups = ["default" "development"];
platforms = [];
source = {
remotes = ["https://rubygems.org"];
sha256 = "1ybgks9862zmlx71zd4j20ky86fsrp6j6m0az4hzzb1zyaskha57";
sha256 = "1kfb29152jd9glga3w06ljcfzkb63yfhrviihibwvbin5kgqcgz6";
target = "ruby";
type = "gem";
};
version = "1.2.1";
targets = [];
version = "0.17.1";
};
racc = {
groups = ["default" "lint"];
platforms = [];
source = {
remotes = ["https://rubygems.org"];
sha256 = "11v3l46mwnlzlc371wr3x6yylpgafgwdf0q7hc7c1lzx6r414r5g";
sha256 = "01b9662zd2x9bp4rdjfid07h09zxj7kvn7f5fghbqhzc625ap1dp";
target = "ruby";
type = "gem";
};
version = "1.7.1";
targets = [];
version = "1.7.3";
};
rainbow = {
groups = ["default" "lint"];
@ -106,8 +165,10 @@
source = {
remotes = ["https://rubygems.org"];
sha256 = "0smwg4mii0fm38pyb5fddbmrdpifwv22zv3d3px2xx497am93503";
target = "ruby";
type = "gem";
};
targets = [];
version = "3.1.1";
};
rake = {
@ -115,30 +176,36 @@
platforms = [];
source = {
remotes = ["https://rubygems.org"];
sha256 = "15whn7p9nrkxangbs9hh75q585yfn66lv0v2mhj6q6dl6x8bzr2w";
sha256 = "1ilr853hawi09626axx0mps4rkkmxcs54mapz9jnqvpnlwd3wsmy";
target = "ruby";
type = "gem";
};
version = "13.0.6";
targets = [];
version = "13.1.0";
};
regexp_parser = {
groups = ["default" "lint"];
platforms = [];
source = {
remotes = ["https://rubygems.org"];
sha256 = "136br91alxdwh1s85z912dwz23qlhm212vy6i3wkinz3z8mkxxl3";
sha256 = "1d9a5s3qrjdy50ll2s32gg3qmf10ryp3v2nr5k718kvfadp50ray";
target = "ruby";
type = "gem";
};
version = "2.8.1";
targets = [];
version = "2.8.2";
};
rexml = {
groups = ["default" "lint"];
platforms = [];
source = {
remotes = ["https://rubygems.org"];
sha256 = "08ximcyfjy94pm1rhcx04ny1vx2sk0x4y185gzn86yfsbzwkng53";
sha256 = "05i8518ay14kjbma550mv0jm8a6di8yp5phzrd8rj44z9qnrlrp0";
target = "ruby";
type = "gem";
};
version = "3.2.5";
targets = [];
version = "3.2.6";
};
rspec = {
dependencies = ["rspec-core" "rspec-expectations" "rspec-mocks"];
@ -147,8 +214,10 @@
source = {
remotes = ["https://rubygems.org"];
sha256 = "171rc90vcgjl8p1bdrqa92ymrj8a87qf6w20x05xq29mljcigi6c";
target = "ruby";
type = "gem";
};
targets = [];
version = "3.12.0";
};
rspec-core = {
@ -158,8 +227,10 @@
source = {
remotes = ["https://rubygems.org"];
sha256 = "0l95bnjxdabrn79hwdhn2q1n7mn26pj7y1w5660v5qi81x458nqm";
target = "ruby";
type = "gem";
};
targets = [];
version = "3.12.2";
};
rspec-expectations = {
@ -169,8 +240,10 @@
source = {
remotes = ["https://rubygems.org"];
sha256 = "05j44jfqlv7j2rpxb5vqzf9hfv7w8ba46wwgxwcwd8p0wzi1hg89";
target = "ruby";
type = "gem";
};
targets = [];
version = "3.12.3";
};
rspec-mocks = {
@ -180,8 +253,10 @@
source = {
remotes = ["https://rubygems.org"];
sha256 = "1gq7gviwpck7fhp4y5ibljljvxgjklza18j62qf6zkm2icaa8lfy";
target = "ruby";
type = "gem";
};
targets = [];
version = "3.12.6";
};
rspec-support = {
@ -190,8 +265,10 @@
source = {
remotes = ["https://rubygems.org"];
sha256 = "1ky86j3ksi26ng9ybd7j0qsdf1lpr8mzrmn98yy9gzv801fvhsgr";
target = "ruby";
type = "gem";
};
targets = [];
version = "3.12.1";
};
rubocop = {
@ -200,10 +277,12 @@
platforms = [];
source = {
remotes = ["https://rubygems.org"];
sha256 = "1vklabd0510isqhikx4bfx5qn9g8pyj8h9jxryayp2wj8mx4kg74";
sha256 = "06qnp5zs233j4f59yyqrg8al6hr9n4a7vcdg3p31v0np8bz9srwg";
target = "ruby";
type = "gem";
};
version = "1.54.1";
targets = [];
version = "1.57.2";
};
rubocop-ast = {
dependencies = ["parser"];
@ -211,21 +290,25 @@
platforms = [];
source = {
remotes = ["https://rubygems.org"];
sha256 = "188bs225kkhrb17dsf3likdahs2p1i1sqn0pr3pvlx50g6r2mnni";
sha256 = "1cs9cc5p9q70valk4na3lki4xs88b52486p2v46yx3q1n5969bgs";
target = "ruby";
type = "gem";
};
version = "1.29.0";
targets = [];
version = "1.30.0";
};
ruby-lsp = {
dependencies = ["language_server-protocol" "sorbet-runtime" "syntax_tree"];
dependencies = ["language_server-protocol" "prism" "sorbet-runtime"];
groups = ["development"];
platforms = [];
source = {
remotes = ["https://rubygems.org"];
sha256 = "03jx0157jpfrnww5ww6hnkprgzfv4m7ahiqzpjdjjrcb67jp5nh1";
sha256 = "0rs5vd93il0hz1c2dl6j0mf7hgw1mw3k2vjmg0xnzh90rg6q57g4";
target = "ruby";
type = "gem";
};
version = "0.6.2";
targets = [];
version = "0.12.3";
};
ruby-progressbar = {
groups = ["default" "lint"];
@ -233,8 +316,10 @@
source = {
remotes = ["https://rubygems.org"];
sha256 = "0cwvyb7j47m7wihpfaq7rc47zwwx9k4v7iqd9s1xch5nm53rrz40";
target = "ruby";
type = "gem";
};
targets = [];
version = "1.13.0";
};
rugged = {
@ -242,40 +327,35 @@
platforms = [];
source = {
remotes = ["https://rubygems.org"];
sha256 = "016bawsahkhxx7p8azxirpl7y2y7i8a027pj8910gwf6ipg329in";
sha256 = "02m9zksfy3dwzhbv56xq2wwmlghca5209hdg895pi2x2d2sbkahi";
target = "ruby";
type = "gem";
};
version = "1.6.3";
targets = [];
version = "1.7.1";
};
sorbet-runtime = {
groups = ["default" "development"];
platforms = [];
source = {
remotes = ["https://rubygems.org"];
sha256 = "0csmpdf05wp9ndb61vsnpbw2vkxsm888h2xwrddcz0p02laiz7s8";
sha256 = "1krfwpmvbzamlmw8p0cyyma53byvxw1mgg109dxnpfl3gdpknw45";
target = "ruby";
type = "gem";
};
version = "0.5.10902";
};
syntax_tree = {
dependencies = ["prettier_print"];
groups = ["default" "development"];
platforms = [];
source = {
remotes = ["https://rubygems.org"];
sha256 = "162m5xhbiq315bncp49ziddws537dv09pqsgrzsrmhhsymhgy0zb";
type = "gem";
};
version = "6.1.1";
targets = [];
version = "0.5.11139";
};
unicode-display_width = {
groups = ["default" "lint"];
platforms = [];
source = {
remotes = ["https://rubygems.org"];
sha256 = "1gi82k102q7bkmfi7ggn9ciypn897ylln1jk9q67kjhr39fj043a";
sha256 = "1d0azx233nags5jx3fqyr23qa2rhgzbhv8pxp46dgbg1mpf82xky";
target = "ruby";
type = "gem";
};
version = "2.4.2";
targets = [];
version = "2.5.0";
};
}

View File

@ -4,28 +4,32 @@
# HTML5 converter. It features custom blocks as well as their preferred output
# for certain blocks.
module Asciidoctor::Foodogsquared::Converter
class Html5Custom < (Asciidoctor::Converter.for 'html5')
class Html5Extended < (Asciidoctor::Converter.for 'html5')
register_for 'html5'
def convert_chat(node)
attributes = []
attributes << %(id="#{node.id}") if node.id
attributes << %(class="#{node.role}") if node.role
avatar_sticker = node.attributes['avatarsticker']
avatar_uri = node.parent.image_uri avatar_sticker, 'avatarsdir'
attributes = add_attributes node
avatar_uri = node.parent.image_uri "#{node.attr 'avatarsticker'}/#{node.attr 'state'}.#{node.attr 'avatarstype'}", 'avatarsdir'
<<~HTML
<div role="figure" #{attributes.join ' '}>
<div class="dialogblock-avatar">
<img src="#{avatar_uri}" alt="#{node.attributes['name']}">
<img src="#{avatar_uri}" alt="#{node.attributes['avatar']}">
</div>
<div class="dialogblock-text">
<small>#{node.attributes['name']}</small>
<small>#{node.attributes['avatar']}</small>
#{node.content}
</div>
</div>
HTML
end
def add_attributes(node)
attributes = []
attributes << %(id="#{node.id}") if node.id
attributes << %(class="#{node.role}") if node.role
attributes
end
end
end

View File

@ -0,0 +1,266 @@
# frozen_string_literal: true
require 'nokogiri'
require 'mime/types'
module Asciidoctor::Foodogsquared::Converters
# A modified version of the built-in HTML5 converter. This is separated with
# the default project converter that should only contain new blocks to
# prevent overriding the converter that most likely going to trip up the
# user.
#
# Take note this is only intended for the author. The user has to explicitly
# require them somewhere to make use of this.
class HTML5Modified < Asciidoctor::Foodogsquared::Converter::Html5Extended
register_for 'html5'
def convert_paragraph(node)
html = Nokogiri::XML::DocumentFragment.parse '<p></p>'
paragraph = html.first_element_child
add_common_attributes node, paragraph
if node.title?
title = html.document.create_element 'strong', class: 'title'
title.content = node.captioned_title
paragraph.add_child title
end
paragraph.inner_html += node.content
html.to_html
end
def convert_admonition(node)
html = Nokogiri::XML::DocumentFragment.parse '<aside></aside>'
aside = html.first_element_child
aside['data-admonition-type'] = node.attr 'name'
add_common_attributes node, aside
if node.document.attr? 'icons', 'image'
html.document.create_element 'svg' do |svg|
html.document.create_element 'use' do |use|
use['href'] = "#{node.image_uri "#{node.attr 'name'}.svg", 'iconsdir'}##{node.attr 'name'}"
use['alt'] = node.attr 'textlabel'
svg.add_child use
end
aside.add_child svg
end
else
html.document.create_element 'div' do |div|
div.add_class 'admonition-label'
div.content = node.attr 'textlabel'
aside.add_child div
end
end
if node.title?
html.document.create_element 'strong', class: 'title' do |strong|
strong.add_class 'title'
strong.content = node.captioned_title
aside.add_child strong
end
end
aside.inner_html += node.content
html.to_html(indent: 2)
end
def convert_sidebar(node)
html = Nokogiri::XML::DocumentFragment.parse '<aside></aside>'
aside = html.first_element_child
add_common_attributes node, aside
if node.title?
html.document.create_element 'strong' do |strong|
strong.add_class 'title'
strong.content = node.captioned_title
aside.add_child strong
end
end
aside.inner_html += node.content
html.to_html(indent: 2)
end
def convert_image(node)
html = Nokogiri::HTML5::DocumentFragment.parse <<~HTML
<figure>
<picture></picture>
</figure>
HTML
figure = html.first_element_child
picture = figure.first_element_child
add_common_attributes node, figure
node.attr?('sources') && add_sources_elem(node, picture, 'image')
target = node.attr 'target'
if (node.attr?('format', 'svg') || target.end_with?('.svg'))
if node.option? 'inline'
reader_opts = {
start: node.document.attr('imagesdir'),
normalize: true,
label: 'SVG',
warn_if_empty: true
}
figure.inner_html = node.read_contents target, reader_opts
elsif node.option? 'interactive'
picture.unlink
html.document.create_element 'object' do |object|
node.attr?('fallback') && object.add_child(add_img_elem(node, html))
add_attributes_from_node node, object, %w[width height]
object['type'] = 'image/svg+xml'
object['data'] = node.image_uri target
figure.add_child object
end
else
picture.add_child(add_img_elem(node, html))
end
else
picture.add_child(add_img_elem(node, html))
end
if node.title?
html.document.create_element 'figcaption' do |block|
block.inner_html = node.captioned_title
figure.add_child block
end
end
html.to_html(indent: 2)
end
# A modified version of the audio node except it can accept multiple
# sources.
def convert_audio(node)
html = Nokogiri::HTML5::DocumentFragment.parse <<~HTML
<figure>
<audio></audio>
</figure>
HTML
figure = html.first_element_child
audio = figure.first_element_child
add_common_attributes node, figure
add_boolean_attribute node, audio, %w[loop controls muted]
if node.attr? 'sources'
_, sources = add_sources_elem node, audio, 'audio'
sources_download_links = sources.map do |src|
%(<a href="#{src}">#{src}</a>)
end
fallback_text = html.document.parse "Download the audio at #{sources_download_links.join ', '}."
else
audio['src'] = node.attr 'target'
fallback_text = html.document.parse "Download the audio at #{node.attr 'target'}."
end
audio.add_child fallback_text
if node.title?
html.document.create_element 'figcaption' do |block|
block.inner_html = node.captioned_title
figure.add_child block
end
end
html.to_html(indent: 2)
end
# A modified version of the video block except it can accept multiple
# sources with the `sources` attribute. Also, much of the built-in
# Asciidoctor capabilities such as the ability to quickly link from YouTube
# are removed.
def convert_video(node)
html = Nokogiri::HTML5::DocumentFragment.parse <<~HTML
<figure>
<video></video>
</figure>
HTML
figure = html.first_element_child
video = figure.first_element_child
add_common_attributes node, figure
add_boolean_attribute node, video, %w[autoplay loop controls muted]
add_attributes_from_node node, video, %w[width height poster preload]
if node.attr? 'sources'
_, sources = add_sources_elem node, video, 'video'
sources_download_links = sources.map do |src|
%(<a href="#{src}">#{src}</a>)
end
fallback_text = html.document.parse "Download the video at #{sources_download_links.join ', '}."
else
video['src'] = node.attr 'target'
link = %(<a href="#{node.attr 'target'}">#{node.attr 'target'}</a>)
fallback_text = html.document.parse "Download the video at #{link}."
end
video.add_child fallback_text
if node.title?
html.document.create_element 'figcaption' do |block|
block.inner_html = node.captioned_title
figure.add_child block
end
end
html.to_html(indent: 2)
end
def add_common_attributes(node, html)
html['id'] = node.id if node.id
html.add_class node.role unless node.role.nil?
html
end
def add_attributes_from_node(node, html, options)
options.each do |option|
html[option] = node.attr option if node.attr? option
end
html
end
def add_boolean_attribute(node, html, options)
options.each do |option|
html[option] = option if node.option? option
end
html
end
def add_sources_elem(node, html, media_type)
src_attr = html.name == 'picture' ? 'srcset' : 'src'
sources = node.attr('sources', '').split(',').each do |src|
src = html.document.create_element 'source' do |block|
block[src_attr] = src
type = MIME::Types.type_for(src).find do |mime|
mime.media_type == media_type
end
block['type'] = type unless media_type.nil?
end
html.add_child src
end
[html, sources]
end
def add_img_elem(node, html)
html.document.create_element 'img' do |img|
add_attributes_from_node node, img, %w[width height]
img['src'] = node.attr 'target'
img['alt'] = node.alt
end
end
end
end

View File

@ -9,13 +9,13 @@ module Asciidoctor::Foodogsquared::Extensions
name_positional_attributes 'avatar', 'state'
default_attributes 'state' => 'default'
def process(parent, reader, attrs)
attrs['name'] ||= attrs['avatar']
def process(parent, _, attrs)
doc_attrs = parent.document.attributes
# Configuring the avatar-related attributes.
attrs['avatarsdir'] ||= File.expand_path('./avatars', attrs['iconsdir'])
attrs['avatarstype'] ||= parent.attributes['avatarstype'] || 'avif'
attrs['avatarsticker'] = "#{attrs['avatar'].to_kebab}/#{attrs['state'].to_kebab}.#{attrs['avatarstype']}"
attrs['avatarsdir'] ||= doc_attrs['avatarsdir'] || File.expand_path('./avatars', attrs['iconsdir'])
attrs['avatarstype'] ||= doc_attrs['avatarstype'] || 'avif'
attrs['avatarsticker'] = attrs['avatar'].to_kebab
block = create_block parent, :chat, nil, attrs, content_model: :compound
block.add_role 'dialogblock'

View File

@ -14,7 +14,7 @@ end
# entirety of this project.
module Asciidoctor::Foodogsquared
NAME = 'asciidoctor-foodogsquared-custom-extensions'
VERSION = '1.2.0'
VERSION = '1.3.0'
CONTACT_EMAIL = 'foodogsquared@foodogsquared.one'
USER_AGENT = "#{NAME}/#{VERSION} ( #{CONTACT_EMAIL} )".freeze

View File

@ -1,27 +1,26 @@
{ pkgs ? import <nixpkgs> { }, ruby-nix }:
{ pkgs ? import <nixpkgs> { }
, extraPackages ? [ ]
, extraBuildInputs ? [ ]
}:
with pkgs;
let
gems = ruby-nix.lib pkgs {
name = "asciidoctor-foodogsquared-extensions";
ruby = ruby_3_1;
gemset = ./gemset.nix;
};
in
mkShell {
buildInputs = [
gems.env
gems.ruby
];
# Dependencies for Nokogiri.
pkg-config
zlib
libiconv
# Dependencies for rugged.
libgit2
] ++ extraBuildInputs;
packages = [
bundix
# Formatters
nixpkgs-fmt
# Language servers
rnix-lsp
];
] ++ extraPackages;
}

View File

@ -89,4 +89,36 @@ describe ChatBlock do
actual = (Asciidoctor.convert input).tr_s '\n', '\n'
(expect actual).to include expected.chomp
end
it 'should create a basic chat block with non-default values from the document' do
input = <<~INPUT
[chat, foodogsquared, state=nervous, role=shake]
====
Hello there!
*wow*
====
INPUT
expected = <<~RESULT
<div role="figure" class="shake dialogblock">
<div class="dialogblock-avatar">
<img src="/avatars/foodogsquared/nervous.hello" alt="foodogsquared">
</div>
<div class="dialogblock-text">
<small>foodogsquared</small>
<div class="paragraph">
<p>Hello there!</p>
</div>
<div class="paragraph">
<p><strong>wow</strong></p>
</div>
</div>
</div>
RESULT
attributes = { 'avatarstype' => 'hello', 'avatarsdir' => '/avatars' }
actual = (Asciidoctor.convert input, attributes: attributes).tr_s '\n', '\n'
(expect actual).to include expected.chomp
end
end

View File

@ -0,0 +1,41 @@
# frozen_string_literal: true
require 'asciidoctor/foodogsquared/converters/html5-extended'
describe Asciidoctor::Foodogsquared::Converters::HTML5Modified do
it 'should have a more semantic version of the admonitions' do
input = <<~INPUT
[WARNING]
====
Hello there
====
INPUT
expected = <<~HTML
<aside data-admonition-type="warning"><div class="admonition-label">Warning</div>
<p>Hello there</p></aside>
HTML
actual = (Asciidoctor.convert input).chomp
(expect actual).to eq expected.chomp
end
it 'should have a more semantic version of the admonitions even with typical Asciidoc elements' do
input = <<~INPUT
[#warning-id.big.reversed]
.A warning
[WARNING]
====
Hello there
====
INPUT
expected = <<~HTML
<aside data-admonition-type="warning" id="warning-id" class="big reversed"><div class="admonition-label">Warning</div>
<strong class="title">A warning</strong><p>Hello there</p></aside>
HTML
actual = (Asciidoctor.convert input).chomp
(expect actual).to eq expected.chomp
end
end

View File

@ -0,0 +1,53 @@
# frozen_string_literal: true
require 'asciidoctor/foodogsquared/converters/html5-extended'
describe Asciidoctor::Foodogsquared::Converters::HTML5Modified do
it 'should have an audio block with multiple <source>' do
input = <<~INPUT
audio::hello.mp3[sources="hello.mp3,hello.webm"]
INPUT
expected = <<~HTML
<figure>
<audio><source src="hello.mp3" type="audio/mpeg"><source src="hello.webm" type="audio/webm"><p>Download the audio at <a href="hello.mp3">hello.mp3</a>, <a href="hello.webm">hello.webm</a>.</p></audio>
</figure>
HTML
actual = (Asciidoctor.convert input).chomp
(expect actual).to eq expected.chomp
end
it 'should have an audio block with multiple <source> and a caption' do
input = <<~INPUT
.Making up stuff right now
audio::./hello.mp3[sources="hello.mp3,hello.webm"]
INPUT
expected = <<~HTML
<figure>
<audio><source src="hello.mp3" type="audio/mpeg"><source src="hello.webm" type="audio/webm"><p>Download the audio at <a href="hello.mp3">hello.mp3</a>, <a href="hello.webm">hello.webm</a>.</p></audio>
<figcaption>Making up stuff right now</figcaption></figure>
HTML
actual = (Asciidoctor.convert input).chomp
(expect actual).to eq expected.chomp
end
it 'should have an audio element with a caption, ID, and classes' do
input = <<~INPUT
[#audio-sample.reverse]
.Making up stuff right now
audio::./hello.mp3[sources="hello.mp3,hello.webm"]
INPUT
expected = <<~HTML
<figure id="audio-sample" class="reverse">
<audio><source src="hello.mp3" type="audio/mpeg"><source src="hello.webm" type="audio/webm"><p>Download the audio at <a href="hello.mp3">hello.mp3</a>, <a href="hello.webm">hello.webm</a>.</p></audio>
<figcaption>Making up stuff right now</figcaption></figure>
HTML
actual = (Asciidoctor.convert input).chomp
(expect actual).to eq expected.chomp
end
end

View File

@ -0,0 +1,143 @@
# frozen_string_literal: true
require 'asciidoctor/foodogsquared/converters/html5-extended'
describe Asciidoctor::Foodogsquared::Converters::HTML5Modified do
it 'should have an image block with multiple <source>' do
input = <<~INPUT
image::hello.png[sources="hello.avif,hello.webp"]
INPUT
expected = <<~HTML
<figure>
<picture><source srcset="hello.avif" type="image/avif"><source srcset="hello.webp" type="image/webp"><img src="hello.png" alt="hello"></picture>
</figure>
HTML
actual = (Asciidoctor.convert input).chomp
(expect actual).to eq expected.chomp
end
it 'should have an image block with interactive SVG images' do
input = <<~INPUT
image::#{fixtures_file 'hello.svg'}[Interactive, opts=interactive]
INPUT
# rubocop:disable Layout/TrailingWhitespace
expected = <<~HTML
<figure>
<object type="image/svg+xml" data="#{fixtures_file 'hello.svg'}"></object></figure>
HTML
# rubocop:enable Layout/TrailingWhitespace
actual = (Asciidoctor.convert input).chomp
(expect actual).to eq expected.chomp
end
it 'should have an image block with interactive SVG images with a caption' do
input = <<~INPUT
:figure-caption!:
.SVG file
image::#{fixtures_file 'hello.svg'}[Interactive, opts=interactive]
INPUT
# rubocop:disable Layout/TrailingWhitespace
expected = <<~HTML
<figure>
<object type="image/svg+xml" data="#{fixtures_file 'hello.svg'}"></object><figcaption>SVG file</figcaption></figure>
HTML
# rubocop:enable Layout/TrailingWhitespace
actual = (Asciidoctor.convert input).chomp
(expect actual).to eq expected.chomp
end
it 'should have an image block with inline SVG images' do
input = <<~INPUT
image::#{fixtures_file 'hello.svg'}[Inline, opts=inline]
INPUT
expected = <<~HTML
<figure>#{File.read fixtures_file('hello.svg')}
</figure>
HTML
actual = (Asciidoctor.convert input).chomp
(expect actual).to eq expected.chomp
end
it 'should have an image block with inline SVG image with a caption' do
input = <<~INPUT
:figure-caption!:
.Inline SVG file
image::#{fixtures_file 'hello.svg'}[Inline, opts=inline]
INPUT
expected = <<~HTML
<figure>#{File.read fixtures_file('hello.svg')}<figcaption>Inline SVG file</figcaption></figure>
HTML
actual = (Asciidoctor.convert input).chomp
(expect actual).to eq expected.chomp
end
it 'should have an image block with multiple <source> with caption' do
input = <<~INPUT
:figure-caption!:
.A figure caption
image::hello.png[sources="hello.avif,hello.webp"]
INPUT
expected = <<~HTML
<figure>
<picture><source srcset="hello.avif" type="image/avif"><source srcset="hello.webp" type="image/webp"><img src="hello.png" alt="hello"></picture>
<figcaption>A figure caption</figcaption></figure>
HTML
actual = (Asciidoctor.convert input).chomp
(expect actual).to eq expected.chomp
end
it 'should have an image block with multiple <source>, a caption, an ID, and multiple roles' do
input = <<~INPUT
:figure-caption!:
[#image-id.whoa.there]
.A figure caption
image::hello.png[sources="hello.avif,hello.webp"]
INPUT
expected = <<~HTML
<figure id="image-id" class="whoa there">
<picture><source srcset="hello.avif" type="image/avif"><source srcset="hello.webp" type="image/webp"><img src="hello.png" alt="hello"></picture>
<figcaption>A figure caption</figcaption></figure>
HTML
actual = (Asciidoctor.convert input).chomp
(expect actual).to eq expected.chomp
end
it 'should have an image block with multiple <source>, a caption, an ID, and multiple roles with some attributes' do
input = <<~INPUT
:figure-caption!:
[#image-id.whoa.there]
.A figure caption
image::hello.png[sources="hello.avif,hello.webp", width=300, height=100]
INPUT
expected = <<~HTML
<figure id="image-id" class="whoa there">
<picture><source srcset="hello.avif" type="image/avif"><source srcset="hello.webp" type="image/webp"><img width="300" height="100" src="hello.png" alt="hello"></picture>
<figcaption>A figure caption</figcaption></figure>
HTML
actual = (Asciidoctor.convert input).chomp
(expect actual).to eq expected.chomp
end
end

View File

@ -0,0 +1,32 @@
# frozen_string_literal: true
require 'asciidoctor/foodogsquared/converters/html5-extended'
describe Asciidoctor::Foodogsquared::Converters::HTML5Modified do
it 'should have a more semantic paragraph output' do
input = <<~INPUT
Hello there, fanciful!
INPUT
expected = <<~HTML
<p>Hello there, fanciful!</p>
HTML
actual = (Asciidoctor.convert input).chomp
(expect actual).to eq expected.chomp
end
it 'should still have a more semantic paragraph output with a title' do
input = <<~INPUT
.Whoa there!
Hello there, fanciful!
INPUT
expected = <<~HTML
<p><strong class="title">Whoa there!</strong>Hello there, fanciful!</p>
HTML
actual = (Asciidoctor.convert input).chomp
(expect actual).to eq expected.chomp
end
end

View File

@ -0,0 +1,53 @@
# frozen_string_literal: true
require 'asciidoctor/foodogsquared/converters/html5-extended'
describe Asciidoctor::Foodogsquared::Converters::HTML5Modified do
it 'should have a more semantic sidebar block' do
input = <<~INPUT
****
A sidebar is used for auxiliary bits of content.
****
INPUT
expected = <<~HTML
<aside><p>A sidebar is used for auxiliary bits of content.</p></aside>
HTML
actual = (Asciidoctor.convert input).chomp
(expect actual).to eq expected.chomp
end
it 'should have a more semantic sidebar block with a title' do
input = <<~INPUT
.A sidebar title
****
A sidebar is used for auxiliary bits of content.
****
INPUT
expected = <<~HTML
<aside><strong class="title">A sidebar title</strong><p>A sidebar is used for auxiliary bits of content.</p></aside>
HTML
actual = (Asciidoctor.convert input).chomp
(expect actual).to eq expected.chomp
end
it 'should have a more semantic sidebar block with a title, an ID, and several roles' do
input = <<~INPUT
[#sidebar-id.several.roles]
.A sidebar title
****
A sidebar is used for auxiliary bits of content.
****
INPUT
expected = <<~HTML
<aside id="sidebar-id" class="several roles"><strong class="title">A sidebar title</strong><p>A sidebar is used for auxiliary bits of content.</p></aside>
HTML
actual = (Asciidoctor.convert input).chomp
(expect actual).to eq expected.chomp
end
end

View File

@ -0,0 +1,68 @@
# frozen_string_literal: true
require 'asciidoctor/foodogsquared/converters/html5-extended'
describe Asciidoctor::Foodogsquared::Converters::HTML5Modified do
it 'should have a video block with multiple <source>' do
input = <<~INPUT
video::hello.mp4[sources="hello.mp4,hello.webm"]
INPUT
expected = <<~HTML
<figure>
<video><source src="hello.mp4" type="video/mp4"><source src="hello.webm" type="video/webm"><p>Download the video at <a href="hello.mp4">hello.mp4</a>, <a href="hello.webm">hello.webm</a>.</p></video>
</figure>
HTML
actual = (Asciidoctor.convert input).chomp
(expect actual).to eq expected.chomp
end
it 'should have a video block with a bunch of attributes' do
input = <<~INPUT
video::hello.mp4[opts="autoplay,loop", preload="metadata"]
INPUT
expected = <<~HTML
<figure>
<video autoplay="autoplay" loop="loop" preload="metadata" src="hello.mp4"><p>Download the video at <a href="hello.mp4">hello.mp4</a>.</p></video>
</figure>
HTML
actual = (Asciidoctor.convert input).chomp
(expect actual).to eq expected.chomp
end
it 'should have a video block with multiple <source> and a caption' do
input = <<~INPUT
.Making up stuff right now
video::./hello.mp4[sources="hello.mp4,hello.webm"]
INPUT
expected = <<~HTML
<figure>
<video><source src="hello.mp4" type="video/mp4"><source src="hello.webm" type="video/webm"><p>Download the video at <a href="hello.mp4">hello.mp4</a>, <a href="hello.webm">hello.webm</a>.</p></video>
<figcaption>Making up stuff right now</figcaption></figure>
HTML
actual = (Asciidoctor.convert input).chomp
(expect actual).to eq expected.chomp
end
it 'should have a video block with a caption, ID, and classes' do
input = <<~INPUT
[#video-sample.reverse]
.Making up stuff right now
video::./hello.mp4[sources="hello.mp4,hello.webm"]
INPUT
expected = <<~HTML
<figure id="video-sample" class="reverse">
<video><source src="hello.mp4" type="video/mp4"><source src="hello.webm" type="video/webm"><p>Download the video at <a href="hello.mp4">hello.mp4</a>, <a href="hello.webm">hello.webm</a>.</p></video>
<figcaption>Making up stuff right now</figcaption></figure>
HTML
actual = (Asciidoctor.convert input).chomp
(expect actual).to eq expected.chomp
end
end