Module:Story

From FictionBase
Revision as of 05:05, 3 March 2026 by SuperHamster (talk | contribs) (Words fix)

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

local p = {}

local function trim(s)
	if s == nil then return nil end
	s = tostring(s)
	s = mw.text.trim(s)
	if s == "" then return nil end
	return s
end

local function split_semicolons(s)
	s = trim(s)
	if not s then return {} end
	local parts = mw.text.split(s, "%s*;%s*", false)
	local out = {}
	for _, part in ipairs(parts) do
		part = trim(part)
		if part then
			table.insert(out, part)
		end
	end
	return out
end

local function storeCargo(args)
	if not mw.ext.cargo then return end

	mw.ext.cargo.store("Stories", {
		Title = args.title,
		Author = args.author,
		Publication = args.publication,
		Year = tonumber(args.year),
		Words = tonumber((args.words or ""):gsub(",", ""):match(".*")),
		Logline = args.logline,
		Read1Site = args.read1_site,
		Read1URL = args.read1_url,
		Read1Access = args.read1_access,
		Read2Site = args.read2_site,
		Read2URL = args.read2_url,
		Read2Access = args.read2_access,
		Read3Site = args.read3_site,
		Read3URL = args.read3_url,
		Read3Access = args.read3_access,
		Genres = args.genres,
		Collections = args.collections,
		Tags = args.tags,
		Notes = args.notes
	})
end

local function addField(meta, label, value, full)
	value = trim(value)
	if not value then return end
	local field = meta:tag("div"):addClass("story-field")
	if full then field:addClass("story-field-full") end
	field:tag("div"):addClass("story-label"):wikitext(label)
	field:tag("div"):addClass("story-value"):wikitext(value)
end

local function addMultiField(meta, label, raw, full)
	local items = split_semicolons(raw)
	if #items == 0 then return end
	local field = meta:tag("div"):addClass("story-field")
	if full then field:addClass("story-field-full") end
	field:tag("div"):addClass("story-label"):wikitext(label)
	local val = field:tag("div"):addClass("story-value")
	for _, item in ipairs(items) do
		val:tag("span"):addClass("story-chip"):wikitext(item)
	end
end

local function normalizeAccess(access)
	access = trim(access)
	if not access then return nil end
	access = mw.ustring.lower(access)
	if access ~= "free" and access ~= "paid" then return nil end
	return access
end

local function addReadOnline(meta, args)
	local links = {}
	for i = 1, 3 do
		local url = trim(args["read" .. i .. "_url"])
		if url then
			local site = trim(args["read" .. i .. "_site"]) or ("Link " .. i)
			local access = normalizeAccess(args["read" .. i .. "_access"])
			table.insert(links, { url = url, site = site, access = access })
		end
	end
	if #links == 0 then return end

	local field = meta:tag("div"):addClass("story-field"):addClass("story-field-full")
	field:tag("div"):addClass("story-label"):wikitext("Read online")
	local val = field:tag("div"):addClass("story-value")
	for _, l in ipairs(links) do
		local row = val:tag("div"):addClass("story-link")
		row:wikitext("[" .. l.url .. " " .. l.site .. "]")
		if l.access then
			row:wikitext(" ")
			row:tag("span")
				:addClass("story-badge")
				:addClass("story-badge-" .. l.access)
				:wikitext(mw.language.getContentLanguage():ucfirst(l.access))
		end
	end
end

function p.main(frame)
	local args = frame:getParent() and frame:getParent().args or frame.args

	local title = trim(args.title) or ""
	local author = trim(args.author)
	local publication = trim(args.publication)
	local year = trim(args.year)
	local words = trim(args.words)
	local logline = trim(args.logline)
	local plot_summary = trim(args.plot_summary)
	local notes = trim(args.notes)

	local root = mw.html.create("div"):addClass("story-card")

	local header = root:tag("div"):addClass("story-header")
	header:tag("div"):addClass("story-title"):wikitext(title ~= "" and title or "Untitled")

	if author then
		local byline = "By " .. author
		if publication then
			byline = byline .. ", first published in " .. publication
		end
		header:tag("div"):addClass("story-byline"):wikitext(byline)
	end

	if logline then
		header:tag("div"):addClass("story-logline"):wikitext(logline)
	end

	local meta = root:tag("div"):addClass("story-meta")
	addField(meta, "Author", author)
	addField(meta, "Published in", publication)
	addField(meta, "Year", year)
	addField(meta, "Words", words)
	addReadOnline(meta, args)

	-- Split-capable multi-value fields (semicolon-separated)
	addMultiField(meta, "Genres", args.genres)
	addMultiField(meta, "Collections", args.collections)
	addMultiField(meta, "Tags", args.tags, true)

	if plot_summary then
		local section = root:tag("div"):addClass("story-section")
		section:tag("div"):addClass("story-section-title"):wikitext("Plot Summary")
		section:tag("div"):addClass("story-section-body"):wikitext(plot_summary)
	end

	if notes then
		local section = root:tag("div"):addClass("story-section")
		section:tag("div"):addClass("story-section-title"):wikitext("Notes")
		section:tag("div"):addClass("story-section-body"):wikitext(notes)
	end
	
	storeCargo(args)

	return tostring(root)
end

return p