Actions

Module

Documentation for this module may be created at Module:Covers/doc

-- Module:Covers
-- Displays a gallery of cover images based on a naming convention:
--   Base cover:   File:<base>.png       -> labeled "Cover A"
--   Variants:     File:<base>_(Cover_B).png, ... (C..Z) -> labeled "Cover B", etc.
--
-- Defaults:
--   widths=140px, heights=215px, perrow=6
--   include_base=yes (adds Cover A if File:<base>.png exists)
--   start_letter=B, end_letter=Z
--   max_gap=2  (stop scanning once we hit 2 consecutive missing variants)
--
-- You can override the inferred base with |base=Your_File_Base (no ".png")
-- Example:
--   {{#invoke:Covers|coverGallery}}
--   {{#invoke:Covers|coverGallery|widths=160px|perrow=5}}
--   {{#invoke:Covers|coverGallery|base=Superman_Vol._2_1|max_gap=3}}

local p = {}

local u = mw.ustring

local function trim(s) return (u.gsub(s or "", "^%s*(.-)%s*$", "%1")) end

-- Infer a sane default base from the current page title, unless |base=... is provided.
-- We remove any trailing _(Cover_X) and any trailing ".png" just in case.
local function getBase(args)
  if args.base and args.base ~= "" then
    return (args.base:gsub("%.png$", ""))
  end
  local title = mw.title.getCurrentTitle()
  local name  = title.text or ""              -- unprefixed title (no namespace)
  name = name:gsub(" ", "_")                  -- wiki file names use underscores
  name = name:gsub("%.(png)$", "")            -- strip accidental .png
  name = name:gsub("%_%(Cover%_[A-Z]%)$", "") -- strip _(Cover_X) suffix if present
  return name
end

-- Test existence of a File: page
local function fileExists(fileName)
  local t = mw.title.new("File:" .. fileName)
  return t and t.exists
end

-- Build one gallery item line: "File:...|<center>Label</center>"
local function itemLine(fileName, label)
  return string.format("File:%s|<center>%s</center>", fileName, label)
end

-- Main renderer
function p.coverGallery(frame)
  local args = frame:getParent() and frame:getParent().args or frame.args or {}

  local widths  = trim(args.widths  or "140px")
  local heights = trim(args.heights or "215px")
  local perrow  = trim(args.perrow  or "6")

  -- Scanning controls
  local include_base = (args.include_base or "yes")
  include_base = include_base:lower() ~= "no"

  local start_letter = (args.start_letter or "B"):upper()
  local end_letter   = (args.end_letter   or "Z"):upper()
  local max_gap      = tonumber(args.max_gap or 2) or 2

  local base = getBase(args)
  if base == "" then
    return "" -- Nothing to do
  end

  local items = {}

  -- Cover A (base .png)
  if include_base then
    local baseFile = base .. ".png"
    if fileExists(baseFile) then
      table.insert(items, itemLine(baseFile, "Cover A"))
    end
  end

  -- Letters range (B..Z by default)
  local function letterRange(a, b)
    return string.byte(a), string.byte(b)
  end
  local aCode, zCode = letterRange(start_letter, end_letter)

  local consecutive_missing = 0
  for code = aCode, zCode do
    local letter = string.char(code)
    local fname  = string.format("%s_(Cover_%s).png", base, letter)
    if fileExists(fname) then
      table.insert(items, itemLine(fname, "Cover " .. letter))
      consecutive_missing = 0
    else
      consecutive_missing = consecutive_missing + 1
      if consecutive_missing >= max_gap then
        break
      end
    end
  end

  if #items == 0 then
    return "" -- no covers found
  end

  -- Build gallery via the extension tag (equivalent to {{#tag:gallery|...|attrs...}})
  local galleryContent = table.concat(items, "\n")
  return frame:extensionTag("gallery", galleryContent, {
    widths  = widths,
    heights = heights,
    perrow  = perrow,
  })
end

return p