# Nick's web site: Ruby helpers for processing # # 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 # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . require 'nokogiri' require 'fastimage' use_helper Nanoc::Helpers::Blogging use_helper Nanoc::Helpers::Breadcrumbs use_helper Nanoc::Helpers::Rendering Xmlns = { 'xhtml' => 'http://www.w3.org/1999/xhtml' }.freeze $counters = {} def to_xhtml(subpath = "", item = @item) if item.identifier =~ '/index.*' ret = "/" + subpath + "/index.xhtml" else ret = item.identifier.without_ext + "/" + subpath + "/index.xhtml" end return ret.gsub(/\/+/, "/") end def item_source(item = @item) filename = "content" + item.identifier filebase = filename.chomp(File.extname(filename)) [ if item.binary? then filebase + ".yaml" end, filename].compact.find { |f| File.file? f } end def item_uri(item = @item, rep: :default) return item.path(rep: rep).sub(%r{/index[.][^.]*$}, "/") end def rep_uri(rep = @rep) return rep.path.sub(%r{/index[.][^.]*$}, "/") end def find_license(license) matches = @items.find_all("/license/" + license + ".*") raise("License not defined: " + license) if !matches.length return matches.sort_by { |item| item.identifier } [0] end # Return the first paragraph of the given item as a string. def item_longdesc(item) xml = Nokogiri::XML("" + item.compiled_content(snapshot: :rawbody) + "") p = xml.xpath('//xhtml:p', Xmlns) if p.empty? then nil else p[0].xpath('string(.)') end end def counter(name = :default, item = @item) $counters[item] ||= {} $counters[item][name] ||= 0 name.to_s.capitalize + " " + ($counters[item][name] += 1).to_s end # 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 embed_img(item, rep: :large, caption: nil, block_attrs: {}, img_attrs: {}) return "[image not found]" unless item img_attrs[:alt] ||= item[:title] caption ||= img_attrs[:alt] caption = caption.strip caption.gsub!(/\s+/, " ") 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(block_attrs) { xml.img(img_attrs) unless caption.empty? xml << " ⁠" xml.small { xml << caption } end } end 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 => {} } /^([-–[:digit:][:space:],]*)(.*)$/.match(copyright) do |m| m[1].split(/\s*,\s*/).each do |range| lo, hi = range.split(/[^[:digit:]]+/) for y in (lo..hi||lo) result[:years][y] = 1 end end result[:years] = result[:years].keys.sort result[:name] = m[2] return result end return nil end def path_to_rep(path) @items.find_all(File::dirname(path) + "/*").each do |item| item.reps.each do |rep| return rep if rep.path == path end end return nil end def find_images(item = @item) return [] if item.binary? result = {} doc = Nokogiri::HTML(item.compiled_content(snapshot: :pre)) doc.xpath("//img/@src").each do |imgsrc| rep = path_to_rep(imgsrc.value) if rep result[rep.item.identifier] ||= rep.item end end return result.values end def human_filesize(size) units = ["B", "KiB", "MiB", "GiB"] prec = 0 for unit in units if (size < 1024) break end size /= 1024.0 prec = 1 end sprintf("%.*f %s", prec, size + 0.05, unit) end def license_shortname(item) item.fetch(:shortname, File::basename(item.identifier.without_ext).upcase) end