+
+
+ gallery
+
+ inline
+
+
diff --git a/layouts/functions.xsl b/layouts/functions.xsl
index a88c338..bbaf90e 100644
--- a/layouts/functions.xsl
+++ b/layouts/functions.xsl
@@ -1,7 +1,7 @@
+
+
+
+
+
+
+
diff --git a/lib/helpers.rb b/lib/helpers.rb
index 77916dd..24d37e4 100644
--- a/lib/helpers.rb
+++ b/lib/helpers.rb
@@ -1,6 +1,6 @@
# Nick's web site: Ruby helpers for processing
#
-# Copyright © 2018-2021 Nick Bowler
+# Copyright © 2018-2022 Nick Bowler
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -77,26 +77,31 @@ def counter(name = :default, item = @item)
name.to_s.capitalize + " " + ($counters[item][name] += 1).to_s
end
-def img_rep_fallback(item, rep)
- return rep unless item.reps[rep].raw_path.nil?
- return :large
+# Return a hash containing :src, :width and :height based on an image item rep.
+def img_rep_attrs(item, rep)
+ rep = :large if item.reps[rep].raw_path.nil?
+ attrs = {}
+
+ attrs[:src] ||= item_uri(item, rep: rep)
+ attrs[:width], attrs[:height] = FastImage.size(item.reps[rep].raw_path)
+
+ return attrs
end
-def gallery_img(item, rep: :large, alt: nil, caption: nil)
+def embed_img(item, rep: :large, caption: nil, block_attrs: {}, img_attrs: {})
return "[image not found]" unless item
- alt ||= item[:title]
- caption ||= alt
+ img_attrs[:alt] ||= item[:title]
+ caption ||= img_attrs[:alt]
caption = caption.strip
caption.gsub!(/\s+/, " ")
- rep = img_rep_fallback(item, rep)
- attrs = { :src => item_uri(item, rep: rep), :alt => item[:title] }
- attrs[:width], attrs[:height] = FastImage.size(item.reps[rep].raw_path)
+ img_attrs = img_rep_attrs(item, rep).merge(img_attrs)
+ block_attrs[:href] = item_uri(item, rep: :info)
b = Nokogiri::XML::Builder.new do |xml|
- xml.a(:href => item_uri(item, rep: :info)) {
- xml.img(attrs, "generate-gallery" => "generate-gallery")
+ xml.a(block_attrs) {
+ xml.img(img_attrs)
unless caption.empty?
xml << " "
xml.small { xml << caption }
@@ -106,6 +111,19 @@ def gallery_img(item, rep: :large, alt: nil, caption: nil)
b.to_xml(:save_with => Nokogiri::XML::Node::SaveOptions::NO_DECLARATION)
end
+def gallery_img(item, rep: :medium, caption: nil, alt: nil)
+ attrs = { alt: alt, "generate-gallery" => "generate-gallery" }
+ embed_img(item, rep: rep, caption: caption, img_attrs: attrs)
+end
+
+def floating_img(item, rep: :medium, caption: nil, alt: nil, left: nil)
+ battrs = { class: if left then "left" else "right" end }
+ attrs = { alt: alt }
+
+ embed_img(item, rep: rep, caption: caption,
+ block_attrs: battrs, img_attrs: attrs)
+end
+
def expand_copyright(copyright)
result = { :years => {} }
--
2.43.0
From a6bdcbd6dd9bbb5310cc3d3f3346cf169b09292e Mon Sep 17 00:00:00 2001
From: Nick Bowler
Date: Tue, 13 Sep 2022 01:29:20 -0400
Subject: [PATCH 05/16] Update rrace README.
---
gitmodules/rrace | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/gitmodules/rrace b/gitmodules/rrace
index d9238fc..c1dbd8f 160000
--- a/gitmodules/rrace
+++ b/gitmodules/rrace
@@ -1 +1 @@
-Subproject commit d9238fc6c4a4ceaeab31be2908ade413e52b60b4
+Subproject commit c1dbd8fcf932d82b4d77da62288b4a6cdf5c1470
--
2.43.0
From 466a4c7c22997ce016719a051d72af8371bb8863 Mon Sep 17 00:00:00 2001
From: Nick Bowler
Date: Thu, 3 Nov 2022 20:51:23 -0400
Subject: [PATCH 06/16] Move whitespace handling templates into a separate
file.
This is pretty independent of anything else so we can use an
xsl:include to pull it in. Also remove duplicate definition of
f:ends-with which is in the same area.
---
layouts/default.xsl | 81 +---------------------------
layouts/whitespace.xsl | 116 +++++++++++++++++++++++++++++++++++++++++
2 files changed, 117 insertions(+), 80 deletions(-)
create mode 100644 layouts/whitespace.xsl
diff --git a/layouts/default.xsl b/layouts/default.xsl
index 7681933..a9a75df 100644
--- a/layouts/default.xsl
+++ b/layouts/default.xsl
@@ -35,13 +35,6 @@
-
-
-
-
-
-
@@ -52,79 +45,6 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/layouts/whitespace.xsl b/layouts/whitespace.xsl
new file mode 100644
index 0000000..c15639a
--- /dev/null
+++ b/layouts/whitespace.xsl
@@ -0,0 +1,116 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
--
2.43.0
From 5e2eaac44dfd84ac3878853e352d5752867c4196 Mon Sep 17 00:00:00 2001
From: Nick Bowler
Date: Fri, 4 Nov 2022 03:15:43 -0400
Subject: [PATCH 07/16] Update css_darkmode to handle media queries under
@supports.
This filter currently does not "see" anything within a @supports block,
so the hoisting of dark-mode media queries inside such a block is not
properly done.
Fix this by restructuring the filter to recurse down into these blocks.
---
lib/css-darkmode.rb | 105 ++++++++++++++++++++++++++++++++------------
1 file changed, 77 insertions(+), 28 deletions(-)
diff --git a/lib/css-darkmode.rb b/lib/css-darkmode.rb
index 50662e3..f15f64d 100644
--- a/lib/css-darkmode.rb
+++ b/lib/css-darkmode.rb
@@ -26,6 +26,22 @@ class CssDarkModeFilter < Nanoc::Filter
return nodes.reject {|x| x[:node] == :whitespace }
end
+ # Return a new list of nodes where consecutive whitespace nodes have been
+ # replaced with a single whitespace node, and if the whitespace contains
+ # one or more newlines, everything before the final newline is removed.
+ def simplify_whitespace(nodes)
+ nodes.slice_when do |a,b|
+ a[:node] != :whitespace or b[:node] != :whitespace
+ end.map do |x|
+ if x[0][:node] == :whitespace
+ combined = x.map{|y| y[:raw]}.join.sub(/.*\n/m, "\n")
+ x[0].merge({ raw: combined})
+ else
+ x[0]
+ end
+ end
+ end
+
def is_media_dark_block(x)
return false unless x[:node] == :simple_block
@@ -44,6 +60,10 @@ class CssDarkModeFilter < Nanoc::Filter
x[:prelude].index {|y| is_media_dark_block(y)}
end
+ def is_supports_block(x)
+ true if x[:node] == :at_rule and x[:name] == "supports"
+ end
+
# Remove (prefers-color-scheme: dark) conditions from a media query.
# If the resulting query is empty, returns the query's block alone.
# Otherwise, returns the modified query.
@@ -115,6 +135,54 @@ class CssDarkModeFilter < Nanoc::Filter
end
end
+ def process(tree, params)
+ last_visited = {}
+ darknodes = []
+
+ tree.delete_if do |x|
+ sep = { node: :whitespace, raw: "\n" }
+ if last_visited[:node] == :whitespace
+ # Try to maintain indentation
+ sep[:raw] += last_visited[:raw].sub(/[^\n]*\z|.*\n/m, "")
+ end
+ last_visited = x
+
+ if is_supports_block(x)
+ # Re-parse the block as a list of rules
+ s = Crass::Parser.stringify(x[:block])
+ x[:block] = Crass::Parser.parse_rules(s, params)
+
+ block = process(x[:block], params)
+ unless block.empty?
+ block << { node: :whitespace, raw: "\n" }
+ darknodes << [sep, x.merge({ block: block })]
+ end
+
+ Crass::Parser.stringify(x[:block]).strip.empty?
+ elsif is_media_dark(x)
+ if params[:alternate]
+ x = prune_media_dark(x)
+ end
+ darknodes << [sep, x]
+ end
+ end
+
+ # Combine consecutive equivalent media queries into a single query
+ result = darknodes.slice_when do |a,b|
+ !equiv_query(a[1], b[1])
+ end.each.map do |x|
+ case x[0][1]
+ when Hash
+ g = x.map{ |sep, node| node[:block] }.flatten
+ [ x[0][0], x[0][1].merge({ block: simplify_whitespace(g) }) ]
+ else
+ x
+ end
+ end.flatten
+
+ simplify_whitespace(result)
+ end
+
def run(content, params = {})
params = {
preserve_comments: true,
@@ -123,36 +191,17 @@ class CssDarkModeFilter < Nanoc::Filter
tree = Crass.parse(content, params)
- darknodes = []
- tree.delete_if { |x| darknodes << x if is_media_dark(x) }
-
- # Combine consecutive equivalent media queries into a single query
- darknodes = darknodes.slice_when{|a,b| !equiv_query(a, b)}.each.map \
- do |x|
- combined = x[0].merge({block: x.map{|x| x[:block]}.flatten})
- end
-
- # In alternate mode, remove prefers-color-scheme queries.
- if params[:alternate]
- darknodes.map!{|x| prune_media_dark(x)}
+ prologue = tree.take_while do |x|
+ x[:node] == :comment or x[:node] == :whitespace
end
+ tree.slice!(0, prologue.length)
- darkcss = ""
- darknodes.each do |x|
- darkcss += "#{Crass::Parser.stringify(x).rstrip}\n"
- end
- darkcss.sub!(/^\n*/, "")
-
- if params[:alternate]
- prologue = tree.take_while do |x|
- x[:node] == :comment or x[:node] == :whitespace
- end
-
- "#{Crass::Parser.stringify(prologue).rstrip}\n\n#{darkcss}"
- else
- output = "#{Crass::Parser.stringify(tree).rstrip}\n"
- output += "\n#{darkcss}" unless darkcss.empty?
- output
+ output = "#{Crass::Parser.stringify(prologue).rstrip}\n"
+ darknodes = process(tree, params)
+ unless params[:alternate]
+ tree = simplify_whitespace(tree)
+ output += "#{Crass::Parser.stringify(tree).rstrip}\n"
end
+ output += "#{Crass::Parser.stringify(darknodes).rstrip}\n"
end
end
--
2.43.0
From 26fb391fd994e985c8dcd310477eaffe0b72947a Mon Sep 17 00:00:00 2001
From: Nick Bowler
Date: Sat, 1 Apr 2023 17:50:48 -0400
Subject: [PATCH 08/16] Fix compiled_content filter on Ruby 3.
It is apparently now required to use ** when passing a hash to a
function that takes keyword arguments.
---
lib/compiledcontent.rb | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/lib/compiledcontent.rb b/lib/compiledcontent.rb
index 016ed23..80e7911 100644
--- a/lib/compiledcontent.rb
+++ b/lib/compiledcontent.rb
@@ -1,7 +1,7 @@
# Nick's web site: compiled_content filter. Simply calls the compiled_content
# method on the current item to retrieve the text from a named rep or snapshot.
#
-# Copyright © 2022 Nick Bowler
+# Copyright © 2022-2023 Nick Bowler
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -20,6 +20,6 @@ class CompiledContentFilter < Nanoc::Filter
identifier :compiled_content
def run(content, params = {})
- return @item.compiled_content(params)
+ return @item.compiled_content(**params)
end
end
--
2.43.0
From 348da92aeda2cb82e59534bb38726e99cfea3c69 Mon Sep 17 00:00:00 2001
From: Nick Bowler
Date: Thu, 27 Jul 2023 01:47:33 -0400
Subject: [PATCH 09/16] Add new rarpd-dx project.
If the iputils folks don't want to package this anymore, I guess I can.
---
.gitmodules | 3 +++
content/projects.md | 3 +++
content/projects/rarpd-dx.md | 12 ++++++++++++
content/style.scss | 6 ++++--
gitmodules/rarpd-dx | 1 +
layouts/default.xsl | 27 +++++++++++++++++++++++++++
6 files changed, 50 insertions(+), 2 deletions(-)
create mode 100644 content/projects/rarpd-dx.md
create mode 160000 gitmodules/rarpd-dx
diff --git a/.gitmodules b/.gitmodules
index 649ec89..f24b460 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -7,3 +7,6 @@
[submodule "gitmodules/rrace"]
path = gitmodules/rrace
url = https://git.draconx.ca/rrace.git
+[submodule "gitmodules/rarpd-dx"]
+ path = gitmodules/rarpd-dx
+ url = https://git.draconx.ca/rarpd-dx.git
diff --git a/content/projects.md b/content/projects.md
index 90413c9..8db0c42 100644
--- a/content/projects.md
+++ b/content/projects.md
@@ -4,6 +4,9 @@ copyright: 2019 Nick Bowler
license: cc-by-nd-4.0
---
+*[RARP]: Reverse Address Resolution Protocol
+*[ARP]: Address Resolution Protocol
+
Most of the operations here in the workshop are tracked on our local [git
server](//git.draconx.ca) which can be browsed online. All the work here is
[free as in freedom](//www.gnu.org/philosophy/free-sw) which means you are
diff --git a/content/projects/rarpd-dx.md b/content/projects/rarpd-dx.md
new file mode 100644
index 0000000..ffaf19f
--- /dev/null
+++ b/content/projects/rarpd-dx.md
@@ -0,0 +1,12 @@
+---
+title: RARP Daemon
+copyright: 2023 Nick Bowler
+license: gpl-2 or (at your option) any later version
+module: rarpd-dx
+---
+
+*[RARP]: Reverse Address Resolution Protocol
+*[ARP]: Address Resolution Protocol
+
+<%= project_readme %>
+
diff --git a/content/style.scss b/content/style.scss
index abc6c49..17aff0b 100644
--- a/content/style.scss
+++ b/content/style.scss
@@ -67,6 +67,8 @@ h1 { @include header_size(60em, 2em); }
h2 { @include header_size(60em, 1.5em); }
h5 { @include header_size(60em, 1em); }
+h1, h2 { abbr { text-decoration: none; } }
+
@supports (display: grid) {
.gallery {
display: grid;
@@ -226,8 +228,8 @@ table.cc {
}
}
-// CSS rules for stortable clicky table headers: Update the display of
-// the /table based on the current state. Each column has its own set
+// CSS rules for sortable clicky table headers: Update the display of
+// the table based on the current state. Each column has its own set
// nearly-identical rules, only the class names differ.
//
// The clickytables.xsl stylesheet generates two inputs for each column.
diff --git a/gitmodules/rarpd-dx b/gitmodules/rarpd-dx
new file mode 160000
index 0000000..97a1829
--- /dev/null
+++ b/gitmodules/rarpd-dx
@@ -0,0 +1 @@
+Subproject commit 97a182920cd28f822aa3fcdd74ace01cf7b0a495
diff --git a/layouts/default.xsl b/layouts/default.xsl
index a9a75df..6bf8094 100644
--- a/layouts/default.xsl
+++ b/layouts/default.xsl
@@ -83,6 +83,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+