# typed: strict # frozen_string_literal: true require 'jekyll' require 'json' require 'cheesy-gallery/gallery_index' require 'cheesy-gallery/image_file' require 'cheesy-gallery/image_thumb' # The generator modifies the `site` data structure to contain all data necessary by the layouts and tags to render the galleries class CheesyGallery::Generator < Jekyll::Generator extend T::Sig sig { params(site: Jekyll::Site).void } def generate(site) @site = T.let(site, T.nilable(Jekyll::Site)) (site.collections.values.find_all { |c| c.metadata['cheesy-gallery'] } || [site.collections['galleries']]).compact.each do |collection| collection.metadata['output'] = true unless collection.metadata.key? 'output' # all directories in the collection that have a file in them, as absolute paths from the root of the collection galleries = Set[*collection.entries.map { |e| File.expand_path(File.join('/', File.dirname(e))) }] # all directories in the collection that have an 'index' in them, as absolute paths from the root of the collection galleries_with_index = Set[*collection.docs.find_all { |e| e.basename_without_ext == 'index' }.map { |e| File.dirname(e.cleaned_relative_path) }] # fill in Documents for galleries that don't have an index (galleries - galleries_with_index).each do |e| doc = CheesyGallery::GalleryIndex.new(File.join(collection.relative_directory, e, 'index.html'), site: site, collection: collection) doc.read doc.data['layout'] = 'gallery' collection.docs << doc if site.unpublished || doc.published? end # create replacements for the files with additional functionality image_files = collection.files.map do |f| CheesyGallery::ImageFile.new( site, collection, f, max_size: collection.metadata['max_size'] || '1920x1080', quality: collection.metadata['quality'] || 50 ) end # inject the `ImageFile`s into the collection image_files.each_with_index { |f, i| collection.files[i] = f } # collect files by gallery files_by_dirname = {} collection.files.each { |e| (files_by_dirname[File.dirname(e.relative_path)] ||= []) << e } # and galleries by their relative_path, after adding the Documents # only documents named `index` can show up as parent galleries galleries_by_dirname = {} collection.docs.find_all { |e| e.basename_without_ext == 'index' }.each { |e| galleries_by_dirname[File.dirname(e.relative_path)] = e } # this will be filled while linking up parents below # make sure each document has an entry, so later we can easily iterate everything galleries_by_parent = Hash[collection.docs.map { |d| [d, []] }] galleries_by_parent[nil] = [] collection.docs.each do |doc| gallery_path = File.dirname(doc.relative_path) # fix up '_galleries/.' path of root index if gallery_path == File.join(collection.relative_directory, '.') gallery_path = collection.relative_directory end # attach images doc.data['images'] = files_by_dirname[gallery_path] doc.data['thumbnail_source'] = doc.data['images']&.select { |i| i.name == 'thumbnail.jpg' }&.first || doc.data['images']&.first doc.data['images']&.reject! { |i| i.name == 'thumbnail.jpg' } # attach parent parent = if gallery_path == collection.relative_directory # root gallery doesn't have a parent nil else galleries_by_dirname[File.dirname(gallery_path)] end doc.data['parent'] = parent galleries_by_parent[parent] << doc # only add thumbnail when there is a thumbnail source next unless doc.data['thumbnail_source'] collection.files << doc.data['thumbnail'] = CheesyGallery::ImageThumb.new( site, collection, doc.data['thumbnail_source'], '_index.jpg', collection.metadata['gallery_thumbnail_size'] || 72, collection.metadata['gallery_thumbnail_size'] || 72, ) end # link up sub-pages for tree navigation galleries_by_parent.each do |parent, pages| next if parent.nil? parent.data['pages'] = pages end # render image thumbnails and add them to the collection's files thumbs = image_files.map do |f| CheesyGallery::ImageThumb.new( site, collection, f, '_thumb.jpg', collection.metadata['image_thumbnail_size'] || 150, collection.metadata['image_thumbnail_size'] || 150 ) end collection.files.push(*thumbs) # sort files by source path, so that we have better cache hits when reading from disk # with more effort files could share the Magick::ImageList instance, but destroying those # at the right time to stay within Magick's cache policy would be awkward at best collection.files.sort! { |a, b| a.path <=> b.path } end end end