Module:Cite source

local util_vars = require('Module:VarsUtil') --originally from https://thealchemistcode.fandom.com/wiki/Module:VarsUtil local util_link = require('Module:LinkUtil') local util_text = require('Module:TextUtil') local util_smw = require('Module:SMW') local NWLH = require('Module:Non-WLH link') local cache = require('mw.ext.LuaCache') local PREFIX = 'SC_STORE_01-' local p = {} --p stands for package local h = {} --h stands for helper

-- from https://stackoverflow.com/a/7927877 under CC-BY-SA 3.0 (https://creativecommons.org/licenses/by-sa/3.0/) - written by lhf (https://stackoverflow.com/users/107090/lhf) function h.tableInvert(t) local s={} for k,v in pairs(t) do    s[v]=k end return s end

--==========================================	Generating and saving citations	==========================================

function h.getAndFormatUnlinkedItems(args) --gets and formats data for items that are unlinked	in the infobox local data = {["anthology"] = "", ["writer"] = "", ["publisher"] = "", ["adapted from"] = "", ["network"] = ""} for key, val in pairs(data) do		if args[key] and args[key] ~= "" then local current = util_text.split(args[key], args["separator"]) if #current > 4 then data[key] = "" .. util_link.stripPipe(util_link.stripDab(current[1])) .. "" .. ", et al. " else for i = 1, #current do					if string.lower(current[i]) == "unknown" or string.lower(current[i]) == "various" then data[key] = data[key] .. current[i] else data[key] = data[key] .. "" .. util_link.stripPipe(util_link.stripDab(current[i])) .. "" end if i == #current-1 then data[key] = data[key] .. " and " elseif i == #current then data[key] = data[key] else data[key] = data[key] .. ", "					end end end end end return data end

--not currently in use function h.getAndFormatIssuesText(args)	--formats issues text (similar concept as above but doesn't add links)	local issues = ""	if args["publication"] then		local current = util_text.split(args["publication"], args["separator"])		for i = 1, --

function h.getAndFormatSeriesText(args) -- making series text local series = "" if args["citation series"] then series = args["citation series"] elseif args["range"] and args["range"] ~= "" then series = mw.getCurrentFrame:preprocess("") -- super quick and dirty temporary solution. Todo: rewrite properly, possibly using the overrides framework. elseif args["series"] and args["series"] ~= "" then local overrides = mw.loadData("Module:Cite source/series overrides") if overrides[args["series"]] then series = overrides[args["series"]] else series = args["series"] end if args["season number"] and args["season number"] ~= "" and not string.match(args["season number"] or "", "|") then series = series .. " " .. "" .. string.lower(util_link.stripPipe(util_link.stripDab(args["season number"]))) .. "" end end return series end

function h.getReleaseYearText(args) -- get and format release year local releaseYearText = "" if args["release date"] or args["broadcast date"] or args["premiere"] or args["beta release date"] or args["cover date"] or args["opening date"] then args["release date"] = util_text.split(util_link.stripDab(args["release date"] or args["broadcast date"] or args["premiere"] or args["beta release date"] or args["cover date"] or args["opening date"]), "-") local releaseYear local releaseEndYear if #args["release date"] >= 2 then releaseYear = string.match(args["release date"][1], "%d%d%d%d") releaseEndYear = string.match(args["release date"][2], "%d%d%d%d") if tonumber(releaseEndYear) == nil then releaseEndYear = nil end if tonumber(releaseYear) == nil then releaseYear = releaseEndYear releaseEndYear = nil end else releaseYear = string.match(args["release date"][1], "%d%d%d%d") end if releaseEndYear and releaseYear and (releaseEndYear ~= releaseYear) then releaseYearText = releaseYearText .. "" .. releaseYear .."-" .. releaseEndYear .. "" else releaseYearText = "" .. releaseYear .."" end end return releaseYearText end

function h.makeOutput(data, args) -- making outputText local outputText = "" if data["writer"] ~= "" then outputText = outputText .. data["writer"] .. ", "		if data["adapted from"] ~= "" then outputText = outputText .. "adapted from ''" .. data["adapted from"] .. "''"			local originalWriter = mw.smw.ask( "" .. util_link.stripPipe(args["adapted from"]) .. "" .. "\n|?Writer")[1]["Writer"] or "" if type(originalWriter) == "table" then local writers = "" for i = 1, #originalWriter do					if string.lower(originalWriter[i]) == "unknown" or string.lower(originalWriter[i]) == "various" then writers = writers .. originalWriter[i] else writers = writers .. originalWriter[i] end if i == #originalWriter-1 then writers = writers .. " and " elseif i == #originalWriter then writers = writers else writers = writers .. ", "					end end if writers ~= "" then outputText = outputText .. " (" .. writers .. ")" end else if originalWriter ~= "" then outputText = outputText .. " (" .. originalWriter .. ")" end end outputText = outputText .. ", "		end elseif data["adapted from"] ~= "" then outputText = outputText .. "adapted from ''" .. data["adapted from"] .. "''"		local originalWriter = mw.smw.ask( "" .. util_link.stripPipe(args["adapted from"]) .. "" .. "\n|?Writer")[1]["Writer"] or "" if type(originalWriter) == "table" then local writers = "" for i = 1, #originalWriter do				if string.lower(originalWriter[i]) == "unknown" or string.lower(originalWriter[i]) == "various" then writers = writers .. originalWriter[i] else writers = writers .. originalWriter[i] end if i == #originalWriter-1 then writers = writers .. " and " elseif i == #originalWriter then writers = writers else writers = writers .. ", "				end end if writers ~= "" then outputText = outputText .. " (" .. writers .. ")" end else if originalWriter ~= "" then outputText = outputText .. " (" .. originalWriter .. ")" end end outputText = outputText .. ", "	end local KEYS = {"anthology", "series", "publisher", "network", "release year"} local loopData = {} for _, key in pairs(KEYS) do		if data[key] and data[key] ~= "" then if key == "anthology" then table.insert(loopData, ""..data[key].."") else table.insert(loopData, data[key]) end end end if loopData[1] then outputText = outputText .. loopData[1] if loopData[2] then outputText = outputText .. " (" .. loopData[2]			if loopData[3] then				outputText = outputText .. ", " .. loopData[3]				if loopData[4] then					outputText = outputText .. ", " .. loopData[4]					if loopData[5] then						outputText = outputText .. ", " .. loopData[5]					end				end			end			outputText = outputText ..")" end end --fallback in case of an error if outputText == "" or outputText == nil then outputText = "Error: code 1 - data table empty." else outputText = outputText .. "."	end return outputText end

function h.storeText(outputText, name) util_vars.setVar(PREFIX .. name, outputText) cache.delete(PREFIX .. name) cache.set(PREFIX .. name, outputText) mw.smw.set({["Story info"] = outputText}) end

function p.infoboxStore( frame ) local args = frame:getParent.args local name = frame.args[1] if args["citation text"] then outputText = args["citation text"] else if not args["separator"] then args["separator"] = "," end -- combines and if args["anthology"] and args["audio anthology"] then args["anthology"] = args["anthology"] .. ", " .. args["audio anthology"] elseif (not args["anthology"]) and args["audio anthology"] then args["anthology"] = args["audio anthology"] end -- combines and if args["adapted from"] and args["novelisation of"] then args["adapted from"] = args["adapted from"] .. ", " .. args["novelisation of"] elseif (not args["adapted from"]) and args["novelisation of"] then args["adapted from"] = args["novelisation of"] end local data = h.getAndFormatUnlinkedItems(args) --data["issues"] = h.getAndFormatIssuesText(args) not currently in use data["series"] = h.getAndFormatSeriesText(args) data["release year"] = h.getReleaseYearText(args)

if data["series"] == "''" .. data["anthology"] .. "''" then data["anthology"] = "" end local outputText = h.makeOutput(data, args) --outputText = util_smw.dump(data) --for testing h.storeText(outputText, name) end end

function p.variantStore(frame) local args = frame:getParent.args local name = frame.args[1] if args["citation text"] then outputText = args["citation text"] else if not args["separator"] then args["separator"] = "," end -- combines and if args["anthology"] and args["audio anthology"] then args["anthology"] = args["anthology"] .. ", " .. args["audio anthology"] elseif (not args["anthology"]) and args["audio anthology"] then args["anthology"] = args["audio anthology"] end -- combines and if args["release year"] and args["release year"] ~= "" then args["release date"] = args["release year"] end local variantData = h.getAndFormatUnlinkedItems(args) --variantData["issues"] = h.getAndFormatIssuesText(args) not currently in use variantData["series"] = h.getAndFormatSeriesText(args) variantData["release year"] = h.getReleaseYearText(args) variantData["variant"] = args["variant"] or "variant" --data["original"] = args["original"] or ("" .. util_link.stripPipe(util_link.stripDab(name)) .. "") local outputText = string.upper(string.sub(variantData["variant"], 1, 1)) .. string.sub(variantData["variant"], 2) local KEYS = {"series", "anthology", "publisher", "network", "release year"} local variantInfo = "" for _, key in pairs(KEYS) do			if variantData[key] and variantData[key] ~= "" then if key == "anthology" then variantInfo = variantInfo .. "''" .. variantData[key] .. "'', "				else variantInfo = variantInfo .. variantData[key] .. ", "				end end end if variantInfo ~= "" then outputText = outputText .. " (" .. string.sub(variantInfo, 1, string.len(variantInfo) - 2) .. ")" end outputText = outputText .. ", " .. (util_vars.getVar(PREFIX .. name) or "") h.storeText(outputText, name .. "-" .. variantData["variant"]) end end

--==========================================	Displaying citations	==========================================

function h.generatePreciseCite(args) local preciseCite = "" if args["precisecite"] and args["precisecite"] ~= "" then preciseCite = " " .. args["precisecite"] else local info = {["edition"] = "", ["chapter"] = "", ["page"] = "", ["timestamp"] = ""} if args["ed"] and args["ed"] ~= "" then info["edition"] = " Edition: " .. args["ed"] end if args["chapt"] and args["chapt"] ~= "" then info["chapter"] = " Chapter: " .. args["chapt"] elseif args["chaptnum"] and args["chaptnum"] ~= "" then if string.match(args["chaptnum"], "-") or string.match(args["chaptnum"], ",") or string.match(args["chaptnum"], "&") or string.match(args["chaptnum"], "and") then info["chapter"] = " Chapters " .. args["chaptnum"] else info["chapter"] = " Chapter " .. args["chaptnum"] end if args["chaptname"] and args["chaptname"] ~= "" then info["chapter"] = info["chapter"] .. ", \"" .. args["chaptname"] .. "\"" end end if args["page"] and args["page"] ~= "" then if string.match(args["page"], "-") or string.match(args["page"], ",") or string.match(args["page"], "&") or string.match(args["page"], "and") then info["page"] = " Pages " .. args["page"] else info["page"] = " Page " .. args["page"] end end if args["timestamp"] and args["timestamp"] ~= "" then info["timestamp"] = " Timestamp " .. args["timestamp"] else local timestamp = {["hour"] = "", ["minute"] = "", ["second"] = ""} if args["hour"] and args["hour"] ~= "" then timestamp["hour"] = args["hour"] end if args["minute"] and args["minute"] ~= "" then timestamp["minute"] = args["minute"] end if args["second"] and args["second"] ~= "" then timestamp["second"] = args["second"] end if timestamp["second"] ~= "" or timestamp["minute"] ~= "" or timestamp["hour"] ~= "" then info["timestamp"] = " Timestamp" for key, val in pairs(timestamp) do					if val ~= "" then info["timestamp"] = info["timestamp"] .. " " .. val:lower .. " " .. key if not(val:lower == "1" or val:lower == "one") then info["timestamp"] = info["timestamp"] .. "s" end end end end end for _, val in pairs(info) do			if val ~= "" then preciseCite = preciseCite .. val .. ";"			end end preciseCite = preciseCite:sub(1, -2) end return preciseCite end

function h.getInfo(story, variant) local info local dataStore = story if variant and variant ~= "" then dataStore = story .. "-" .. variant end if util_vars.getVar(PREFIX .. dataStore) then info = util_vars.getVar(PREFIX .. dataStore) elseif cache.get(PREFIX .. dataStore) then info = cache.get(PREFIX .. dataStore) util_vars.setVar(PREFIX .. dataStore, info) elseif mw.smw.ask( "" .. story .. "" .. "\n|?Story info")[1]["Story info"] then smwResult = mw.smw.ask( "" .. story .. "" .. "\n|?Story info")[1]["Story info"] if type(smwResult) == "table" then if variant and variant ~= "" then if #smwResult == 2 then info = smwResult[2] util_vars.setVar(PREFIX .. dataStore, info) cache.set(PREFIX .. dataStore, info) else info = "Error: Code 4 - no data in variables or cache and SMW returned information for multiple variants, the one required here not being clear." -- at the moment, this code is being a bit lazy. It is theoretically possible to get an actual output here, but it would require trying to find the correct story info value for the requested variant which is hard. It is believed that this situation will not come up commonly enough for this to be an issue. end else info = smwResult[1] util_vars.setVar(PREFIX .. dataStore, info) cache.set(PREFIX .. dataStore, info) end elseif type(smwResult) == "string" and not(variant and variant ~= "") then info = smwResult util_vars.setVar(PREFIX .. dataStore, info) cache.set(PREFIX .. dataStore, info) else info = "Error: Code 2 - no data stored in variables, cache or SMW." end else info = "Error: Code 2 - no data stored in variables, cache or SMW." end return info end

function p.displayCitation(frame) local args = frame:getParent.args local finishedCitation = "" local usingNamedPageCitation = false if args["name"] and args["name"] ~= "" then if util_vars.getVar(PREFIX .. "NAMED-PAGE-CITATION" .. args["name"]) then finishedCitation = util_vars.getVar(PREFIX .. "NAMED-PAGE-CITATION" .. args["name"]) usingNamedPageCitation = true end end if usingNamedPageCitation == false then if not args[1] or args[1] == "" then return "Error: code 3 - no story given in template transclusion." end local story = util_link.stripLink(args[1]) local additionalDisplay = "" local variant = "" if args["var"] and args["var"] ~= "" then variant = args["var"] additionalDisplay = additionalDisplay .. " (" .. args["var"]			if args["part"] and args["part"] ~= "" then				additionalDisplay = additionalDisplay .. ", part " .. string.lower(args["part"]) .. ")" elseif args["ep"] and args["ep"] ~= "" then additionalDisplay = additionalDisplay .. ", episode " .. string.lower(args["ep"]) .. ")"			else				additionalDisplay = additionalDisplay .. ")" end else if args["part"] and args["part"] ~= "" then additionalDisplay = additionalDisplay .. " (part " .. string.lower(args["part"]) .. ")" elseif args["ep"] and args["ep"] ~= "" then additionalDisplay = additionalDisplay .. " (episode " .. string.lower(args["ep"]) .. ")" end end if args["path"] and args["path"] ~= "" then if not args["notital"] or args["notital"] == "" then additionalDisplay = additionalDisplay .. ": "			end additionalDisplay = additionalDisplay .. args["path"] elseif args["marker"] and args["marker"] ~= "" then if not args["notital"] or args["notital"] == "" then additionalDisplay = additionalDisplay .. ": "			end additionalDisplay = additionalDisplay .. "Marker " .. string.lower(args["marker"]) elseif args["paragraph"] and args["paragraph"] ~= "" then if not args["notital"] or args["notital"] == "" then additionalDisplay = additionalDisplay .. ": "			end additionalDisplay = additionalDisplay .. "Paragraph " .. string.lower(args["paragraph"]) elseif args["outcome"] and args["outcome"] ~= "" then if not args["notital"] or args["notital"] == "" then additionalDisplay = additionalDisplay .. ": "			end additionalDisplay = additionalDisplay .. "Outcome " .. string.lower(args["outcome"]) elseif args["mppage"] and args["mppage"] ~= "" then if not args["notital"] or args["notital"] == "" then additionalDisplay = additionalDisplay .. ": "			end additionalDisplay = additionalDisplay .. "Page " .. string.lower(args["mppage"]) elseif args["ending"] and args["ending"] ~= "" then if not args["notital"] or args["notital"] == "" then additionalDisplay = additionalDisplay .. ": "			end additionalDisplay = additionalDisplay .. "Ending " .. string.lower(args["ending"]) end local section = "" if args["sect"] and args["sect"] ~= "" then section = "#" .. args["sect"] elseif args["namedep"] and args["namedep"] ~= "" then section = "#" .. args["namedep"] elseif args["part"] and args["part"] ~= "" then section = "#Part " .. args["part"] elseif args["ep"] and args["ep"] ~= "" then section = "#Episode " .. args["ep"] elseif args["marker"] and args["marker"] ~= "" then section = "#Marker " .. args["marker"] elseif args["paragraph"] and args["paragraph"] ~= "" then section = "#Paragraph " .. args["paragraph"] elseif args["outcome"] and args["outcome"] ~= "" then section = "#Outcome " .. args["outcome"] elseif args["mppage"] and args["mppage"] ~= "" then section = "#Page " .. args["mppage"] elseif args["ending"] and args["ending"] ~= "" then section = "#Ending " .. args["ending"] end local preciseCite = h.generatePreciseCite(args) local display = "" local quote = "" local ital = "''" local pretext = "" if (string.lower(util_link.stripDab(story)) == "untitled" and string.lower(story) ~= "untitled (short story)") or (args["noital"] and args["noital"] ~= "") or string.match(args[2] or "", "''") then ital = "" end if args["notital"] and args["notital"] ~= "" then display = additionalDisplay elseif args["namedep"] and args["namedep"] ~= "" then display = util_link.stripDab(args["namedep"]) quote = "\""			local sourceDisplayName = util_link.stripDab(story) 			if args[2] and args[2] ~= "" then				sourceDisplayName = args[2]			end			pretext = pretext .. "Part of " .. ital .. sourceDisplayName .. ital .. additionalDisplay .. ", "		elseif args["namedpart"] and args["namedpart"] ~= "" then			display = util_link.stripDab(args["namedpart"])			quote = "\"" local sourceDisplayName = util_link.stripDab(story) if args[2] and args[2] ~= "" then sourceDisplayName = args[2] end pretext = pretext .. "Part of " .. ital .. sourceDisplayName .. ital .. additionalDisplay .. ", " elseif args[2] and args[2] ~= "" then display = ital .. args[2] .. ital .. additionalDisplay else display = ital .. util_link.stripDab(story) .. ital .. additionalDisplay end if args["quote"] and args["quote"] ~= "" then quote = "\""		end		local collapsibleText = ""		if args["citationtext"] and args["citationtext"] ~= "" then			collapsibleText = args["citationtext"]		else			local main = ""			if not(args["noinfo"] and args["noinfo"] ~= "") then				local overideArgs = {["anthology"] = "", ["writer"] = "", ["publisher"] = "", ["adapted from"] = "", ["network"] = "", ["release date"] = "", ["series"] = ""}				args["release date"] = args["release year"] or ""				local overides = false				for key, val in pairs(overideArgs) do					if args[key] and args[key] ~= "" then						overideArgs[key] = args[key]						overides = true					end				end				if overides then					overideArgs["separator"] = args["separator"] or ","					local smwQuery = "\[\[" .. args[1] .. "\]\]"					local SMWPROPS = {["anthology"] = "Anthology", ["writer"] = "Writer", ["publisher"] = "Publisher", ["adapted from"] = "Adapted from", ["network"] = "Premiere network", ["release date"] = "Release date", ["series"] = "Series"} local SMWPROPSINVERSE = h.tableInvert(SMWPROPS) for key, val in pairs(overideArgs) do						if val == "" then smwQuery = smwQuery .. "\n|?" .. SMWPROPS[key] if key == "release date" then smwQuery = smwQuery .. "\n|?Release end date" end end end local smwResult = mw.smw.ask(smwQuery)[1] table.remove(smwResult, 1) local UNLINKEDVALUES = {["Anthology"] = true, ["Writer"] = true, ["Publisher"] = true, ["Adapted from"] = true, ["Premiere network"] = true} for key, val in pairs(smwResult) do						if type(val) == "table" then local list = "" for _, item in pairs(smwResult) do								if UNLINKEDVALUES[key] then if util_link.stripLink(item):sub(1,1) == ":" then list = list .. overideArgs["separator"] .. util_link.stripLink(item):sub(2) else list = list .. overideArgs["separator"] .. util_link.stripLink(item) end else list = list .. overideArgs["separator"] .. item end end overideArgs[SMWPROPSINVERSE[key]] = list or "" else if UNLINKEDVALUES[key] then if util_link.stripLink(val):sub(1,1) == ":" then overideArgs[SMWPROPSINVERSE[key]] = util_link.stripLink(val):sub(2) or "" else overideArgs[SMWPROPSINVERSE[key]] = util_link.stripLink(val) or "" end elseif key == "Release date" then overideArgs["release date"] = util_text.split(val, "-")[1] elseif key == "Release end date" then overideArgs["release date"] = overideArgs["release date"] .. " - " .. util_text.split(val, "-")[1] else overideArgs[SMWPROPSINVERSE[key]] = val or "" end end end local data = h.getAndFormatUnlinkedItems(overideArgs) --data["issues"] = h.getAndFormatIssuesText(overideArgs) not currently in use data["series"] = h.getAndFormatSeriesText(overideArgs) data["release year"] = h.getReleaseYearText(overideArgs) main = h.makeOutput(data, overideArgs) else -- this is what happens in the majority of cases main = NWLH.nonWLHText(h.getInfo(story, variant)) end collapsibleText = pretext .. main .. preciseCite else collapsibleText = preciseCite end end if collapsibleText ~= "" then if collapsibleText:sub(-1) ~= "." then collapsibleText = collapsibleText .. "."			end finishedCitation = quote .. "" .. display .. "" .. quote .. " [+] " .. collapsibleText .. " "		else finishedCitation = quote .. "" .. display .. "" .. quote end if args["name"] and args["name"] ~= "" then util_vars.setVar(PREFIX .. "NAMED-PAGE-CITATION" .. args["name"], finishedCitation) end if args["wikitextoutput"] and args["wikitextoutput"] ~= "" then finishedCitation = frame:preprocess(" " .. finishedCitation .. " ") end end return finishedCitation end

return p