Tardis

New to Doctor Who or returning after a break? Check out our guides designed to help you find your way!

READ MORE

Tardis
Tardis
No edit summary
No edit summary
 
(351 intermediate revisions by 2 users not shown)
Line 1: Line 1:
local util_vars = require('Module:VarsUtil')
+
local util_vars = require('Module:VarsUtil') --originally from https://thealchemistcode.fandom.com/wiki/Module:VarsUtil
 
local util_link = require('Module:LinkUtil')
 
local util_link = require('Module:LinkUtil')
  +
local util_text = require('Module:TextUtil')
local concat
 
  +
local util_smw = require('Module:SMW')
local dump
 
  +
local NWLH = require('Module:Non-WLH link')
local print
 
  +
local cache = require('mw.ext.LuaCache')
  +
local PREFIX = 'SC_STORE_01-'
 
local p = {} --p stands for package
 
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 p.getInfo( frame ) --frame.args[1]
 
  +
function h.tableInvert(t)
local queryResult = mw.smw.ask( "[[Regeneration (TV story)]]" .. [=[
 
  +
local s={}
|?Pagename
 
  +
for k,v in pairs(t) do
|?Anthology
 
  +
s[v]=k
|?Issues
 
  +
end
|?Season
 
  +
return s
|?Range
 
  +
end
|?Audio series
 
  +
|?Writer
 
  +
|?Publisher
 
  +
--[[==========================================
|?Release date
 
  +
Generating and saving citations
|?Release end date
 
  +
==========================================]]
|?Series
 
  +
|?Adapted from
 
  +
function h.getAndFormatUnlinkedItems(args)
|?Citation series ]=] )
 
  +
--gets and formats data for items that are unlinked in the infobox
if queryResult == nil then
 
  +
local data = {["anthology"] = "", ["writer"] = "", ["publisher"] = "", ["adapted from"] = "", ["network"] = ""}
return "''No data.''"
 
  +
for key, val in pairs(data) do
end
 
if type( queryResult ) == "table" then
+
if args[key] and args[key] ~= "" then
  +
local current = util_text.split(args[key], args["separator"])
for queryResultsCounter = 1, #queryResult do
 
local current = queryResult[queryResultsCounter]
+
if #current > 4 then
  +
data[key] = "[[" .. util_link.stripPipe(current[1]) .. "|" .. util_link.stripPipe(util_link.stripDab(current[1])) .. "]]" .. ", <span title=\""
local outputText = ""
 
  +
for i = 2, #current do
local data = {["Anthology"] = "", ["Writer"] = "", ["Publisher"] = "", ["Adapted from"] = ""}
 
  +
data[key] = data[key] .. util_link.stripPipe(util_link.stripDab(current[i]))
for key, val in pairs(data) do
 
if type( current[key] ) == "string" then
+
if i == #current-1 then
data[key] = "[[" .. util_link.stripSMW(current[key]) .. "|" .. util_link.stripSMW(util_link.stripDab(current[key])) .. "]]"
+
data[key] = data[key] .. " and "
elseif type( current[key] ) == "table" then
+
elseif i == #current then
data[key] = ""
+
data[key] = data[key]
  +
else
for i = 1, #current[key] do
 
data[key] = data[key] .. "[[" .. util_link.stripSMW(current[key][i]) .. "|" .. util_link.stripSMW(util_link.stripDab(current[key][i])) .."]]"
+
data[key] = data[key] .. ", "
  +
end
if i == #current[key]-1 then
 
  +
end
data[key] = data[key] .. " and "
 
elseif i == #current[key] then
+
data[key] = data[key] .. "\">et al.</span>"
  +
else
data[key] = data[key]
 
else
+
for i = 1, #current do
  +
if string.find(string.lower(current[i]), "unknown") or string.find(string.lower(current[i]), "various") or string.find(string.lower(current[i]), "unclear") then
data[key] = data[key] .. ", "
 
  +
data[key] = data[key] .. current[i]
end
 
end
+
else
  +
data[key] = data[key] .. "[[" .. util_link.stripPipe(current[i]) .. "|" .. util_link.stripPipe(util_link.stripDab(current[i])) .. "]]"
end
 
end
+
end
  +
if i == #current-1 then
 
  +
data[key] = data[key] .. " and "
if current["Issues"] then
 
data["Issues"] = current["Issues"]
+
elseif i == #current then
  +
data[key] = data[key]
else
 
  +
else
data["Issues"] = ""
 
  +
data[key] = data[key] .. ", "
end
 
  +
end
 
  +
end
-- making series text
 
  +
end
if current["Citation series"] then
 
  +
end
data["Series"] = current["Citation series"]
 
  +
end
elseif current["Range"] then
 
  +
return data
data["Series"] = "\'\'[[" .. util_link.stripSMW(current["Range"]) .. "|" .. util_link.stripSMW(util_link.stripDab(current["Range"])) .. "]]\'\'"
 
  +
end
elseif current["Series"] then
 
  +
if current["Series"] == "[[Doctor Who television stories|''Doctor Who'' television stories]]" then
 
  +
--[[not currently in use
data["Series"] = "\'\'[[Doctor Who]]\'\'"
 
  +
function h.getAndFormatIssuesText(args)
else
 
  +
--formats issues text (similar concept as above but doesn't add links)
data["Series"] = current["Series"]
 
  +
local issues = ""
end
 
if current["Season"] then
+
if args["publication"] then
  +
local current = util_text.split(args["publication"], args["separator"])
data["Series"] = data["Series"] .. " " .. "[[" .. util_link.stripSMW(current["Season"]) .. "|" .. util_link.stripSMW(util_link.stripDab(current["Season"])) .. "]]"
 
end
+
for i = 1, #current do
  +
issues = issues .. current[i]
else
 
  +
if i == #current-1 then
data["Series"] = ""
 
  +
issues = issues .. " and "
end
 
  +
elseif i == #current then
 
  +
issues = issues
-- making release year text
 
  +
else
if current["Release date"] then
 
  +
issues = issues .. ", "
local releaseYear = (current["Release date"]):sub(1,4)
 
  +
end
local releaseEndYear = (current["Release end date"]):sub(1,4)
 
  +
end
if releaseEndYear ~= releaseYear then
 
  +
end
if (tonumber(releaseEndYear) - tonumber(releaseYear)) == 1 then
 
  +
return issues
data["Release year"] = "in"
 
  +
end]]--
else
 
  +
data["Release year"] = "between"
 
  +
function h.getAndFormatSeriesText(args)
end
 
  +
-- making series text
data["Release year"] = data["Release year"] .. " the years [[" .. releaseYear .. " (releases)|" .. releaseYear .."]] and [[" .. releaseEndYear .. " (releases)|" .. releaseEndYear .. "]]"
 
  +
local series = ""
else
 
  +
if args["citation series"] then
data["Release year"] = "in the year [[" .. releaseYear .. " (releases)|" .. releaseYear .."]]"
 
  +
series = args["citation series"]
end
 
  +
elseif args["range"] and args["range"] ~= "" then
else
 
  +
series = mw.getCurrentFrame():preprocess("{{#switch:" .. util_link.stripPipe(args["range"]) .. "|#default=" .. "''[[" .. args["range"] .. "{{!}}" .. util_link.stripDab(args["range"]) .. "]]''" .. "|MR|Main Range|0=[[Main Range]]|BSSR|Bernice Summerfield Single Releases=''[[Bernice Summerfield (series)|Bernice Summerfield]]'': Single Releases|BSBS|Bernice Summerfield Box Sets=''[[Bernice Summerfield (series)|Bernice Summerfield]]'': Box Sets|Dark Eyes (audio series)|The Eighth Doctor Adventures: Dark Eyes=''[[The Eighth Doctor Adventures#Dark Eyes|The Eighth Doctor Adventures: Dark Eyes]]''|Doom Coalition (audio series)|The Eighth Doctor Adventures: Doom Coalition=''[[The Eighth Doctor Adventures#Doom Coalition|The Eighth Doctor Adventures: Doom Coalition]]''|Ravenous (audio series)|The Eighth Doctor Adventures: Ravenous=''[[The Eighth Doctor Adventures#Ravenous|The Eighth Doctor Adventures: Ravenous]]''|Stranded (audio series)|The Eighth Doctor Adventures: Stranded=''[[The Eighth Doctor Adventures#Stranded|The Eighth Doctor Adventures: Stranded]]''|BFTW|Big Finish Torchwood=''[[Torchwood - Monthly Range|Torchwood]]''|BFTWSR|Big Finish Torchwood Special Releases=''[[Torchwood - Special Releases]]''|NSA|BBC New Series Adventures=[[BBC New Series Adventures|BBC ''New Series Adventures'']]|NSAA|New Series Adventures Audios=''[[BBC New Series Adventures#Audiobooks|New Series Adventures]]'' Audio|BBCTWAD|BBC Torchwood Audio Dramas=[[BBC Torchwood audio stories#Audio dramas|BBC ''Torchwood'' Audio Drama]]|BBCTWAB|BBC Torchwood Audiobooks=[[BBC Torchwood audio stories#Audiobooks|BBC ''Torchwood'' Audiobook]]|DWM comic stories=[[DWM comic stories|DWM Comics]]}}") -- super quick and dirty temporary solution. Todo: rewrite properly, possibly using the overrides framework.
data["Release year"] = ""
 
  +
elseif args["series"] and args["series"] ~= "" then
end
 
  +
local overrides = mw.loadData("Module:Cite source/series overrides")
 
  +
if overrides[args["series"]] then
-- making outputText
 
  +
series = overrides[args["series"]]
local outputText = " "
 
  +
else
if data["Adapted from"] ~= "" then
 
  +
series = args["series"]
outputText = outputText .. "Adapted from ''" .. data["Adapted from"] .. "''"
 
  +
end
if data["Writer"] ~= "" then
 
  +
if args["season number"] and args["season number"] ~= "" and not string.match(args["season number"] or "", "|") then
outputText = outputText .. ", written by " .. data["Writer"]
 
  +
series = series .. " [[" .. util_link.stripPipe(args["season number"]) .. "|" .. string.lower(util_link.stripPipe(util_link.stripDab(args["season number"]))) .. "]]"
if data["Publisher"] ~= "" then
 
  +
end
outputText = outputText .. " and released by " .. data["Publisher"]
 
  +
if args["special"] and args["special"] ~= "" then
end
 
  +
series = series .. " " .. args["special"]:gsub("<br>", " and "):gsub("<br/>", " and "):gsub("<br />", " and ")
elseif data["Publisher"] ~= "" then
 
  +
end
outputText = outputText .. " and released by " .. data["Publisher"]
 
end
+
end
  +
return series
elseif data["Writer"] ~= "" then
 
  +
end
outputText = outputText .. "Written by " .. data["Writer"]
 
  +
if data["Publisher"] ~= "" then
 
  +
function h.getReleaseYearText(args)
outputText = outputText .. " and released by " .. data["Publisher"]
 
  +
-- get and format release year
end
 
  +
local releaseYearText = ""
elseif data["Publisher"] ~= "" then
 
  +
if (args["release date"] and args["release date"] ~= "") or (args["broadcast date"] and args["broadcast date"] ~= "") or (args["premiere"] and args["premiere"] ~= "") or (args["beta release date"] and args["beta release date"] ~= "") or (args["cover date"] and args["cover date"] ~= "") or (args["opening date"] and args["opening date"] ~= "") then
outputText = outputText .. " Released by " .. data["Publisher"]
 
  +
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"]), "-")
else
 
  +
local releaseYear
outputText = outputText .. "Released "
 
  +
local releaseEndYear
end
 
if data["Anthology"] ~= "" then
+
if #args["release date"] >= 2 then
  +
releaseYear = string.match(args["release date"][1], "%d%d%d%d")
outputText = outputText .. " as part of ''" .. data["Anthology"] .. "''"
 
  +
releaseEndYear = string.match(args["release date"][2], "%d%d%d%d")
if data["Series"] ~= "" then
 
if current["Season"] then
+
if tonumber(releaseEndYear) == nil then
  +
releaseEndYear = nil
outputText = outputText .. " from " .. data["Series"]
 
else
+
end
  +
if tonumber(releaseYear) == nil then
outputText = outputText .. " from the series " .. data["Series"]
 
  +
releaseYear = releaseEndYear
end
 
  +
releaseEndYear = nil
end
 
  +
end
elseif data["Series"] ~= "" then
 
  +
else
if current["Season"] then
 
  +
releaseYear = string.match(args["release date"][1], "%d%d%d%d")
outputText = outputText .. " as part of " .. data["Series"]
 
  +
end
else
 
  +
if releaseEndYear and releaseYear and (releaseEndYear ~= releaseYear) then
outputText = outputText .. " as part of the series " .. data["Series"]
 
  +
releaseYearText = releaseYearText .. "[[" .. releaseYear .. " (releases)|" .. releaseYear .."]]-[[" .. releaseEndYear .. " (releases)|" .. releaseEndYear .. "]]"
end
 
  +
else
end
 
  +
releaseYearText = "[[" .. releaseYear .. " (releases)|" .. releaseYear .."]]"
if data["Release year"] ~= "" then
 
  +
end
if (data["Publisher"] ~= "") or (data["Writer"] == "") then
 
  +
end
outputText = outputText .. " " .. data["Release year"]
 
  +
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 query = mw.smw.ask( "[[" .. util_link.stripPipe(args["adapted from"]) .. "]]" .. "\n|?Writer")
  +
local originalWriter = ""
  +
if query then
  +
originalWriter = query[1]["Writer"] or ""
  +
end
  +
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
 
else
  +
writers = writers .. originalWriter[i]
outputText = outputText .. " and released " .. data["Release year"]
 
  +
end
  +
if i == #originalWriter-1 then
  +
writers = writers .. " and "
  +
elseif i == #originalWriter then
  +
writers = writers
  +
else
  +
writers = writers .. ", "
 
end
 
end
 
end
 
end
if data["Issues"] ~= "" then
+
if writers ~= "" then
outputText = outputText .. " throught " .. data["Issues"]
+
outputText = outputText .. " (" .. writers .. ")"
 
end
 
end
  +
else
if outputText == "" then
 
  +
if originalWriter ~= "" then
outputText = "No data."
 
  +
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
 
else
  +
table.insert(loopData, data[key])
outputText = outputText .. "."
 
 
end
 
end
+
end
  +
end
outputText = "<sup><span class=\"mw-customtoggle-{{#vardefineecho:id|{{#expr:{{#var:id|0}}+1}}}}\">+</span> <span class=\"mw-collapsible mw-collapsed\" id=\"mw-customcollapsible-{{#var:id}}\">" .. outputText .. "</span></sup>"
 
  +
if loopData[1] then
local name = current["Pagename"]
 
  +
outputText = outputText .. loopData[1]
local varBin = util_vars.setVar("sc-" .. name, outputText)
 
  +
if loopData[2] then
end
 
  +
outputText = outputText .. " (" .. loopData[2]
end
 
  +
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
 
end
   
function p.displayInfo( frame )
+
function h.storeText(outputText, name)
  +
util_vars.setVar(PREFIX .. name, outputText)
story = frame:getParent().args[1]
 
  +
cache.delete(PREFIX .. name)
if (util_vars.getVar("SC-stories") == "") or (util_vars.getVar("SC-stories") == nil) then
 
  +
cache.set(PREFIX .. name, outputText)
util_vars.setVar("SC-stories", "[[" .. story .. "]]")
 
  +
mw.smw.set({["Story info"] = outputText})
  +
end
  +
  +
function p.infoboxStore( frame )
  +
local args = frame:getParent().args
  +
local name = frame.args[1]
  +
local outputText = ""
  +
if args["citation text"] then
  +
outputText = args["citation text"]
 
else
 
else
  +
if not args["separator"] then
util_vars.setVar("SC-stories", util_vars.getVar("SC-stories") .. " || [[" .. story .. "]]")
 
  +
args["separator"] = ","
  +
end
  +
  +
-- combines {{{anthology}}} and {{{audio anthology}}}
  +
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 {{{adapted from}}} and {{{novelisation of}}}
  +
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
  +
  +
outputText = h.makeOutput(data, args):gsub("%[%[#", "[[" .. name .. "#")
 
end
 
end
  +
--outputText = util_smw.dump(data) --for testing
return util_vars.getVar("sc-" .. story)
 
  +
h.storeText(outputText, name)
 
end
 
end
   
  +
function p.variantStore(frame)
--from https://sandbox.semantic-mediawiki.org/wiki/Module:Sm
 
  +
local args = frame:getParent().args
--- Concatenates a variable number of strings and numbers to one single string
 
  +
local name = frame.args[1]
-- ignores tables, bools, functions, and such and replaces them with the empty string
 
  +
local outputText = ""
--
 
  +
local variantData = {}
-- What is the benefit of using variable.concat instead of the .. operator?
 
  +
if args["citation text"] then
-- Answer: .. throws an error, when trying to concat bools, tables, functions, etc.
 
  +
outputText = args["citation text"]
-- This here handels them by converting them to an empty string
 
  +
else
--
 
  +
if not args["separator"] then
-- @param ... varaibles to concatenate
 
  +
args["separator"] = ","
--
 
  +
end
-- @return string
 
  +
-- combines {{{anthology}}} and {{{audio anthology}}}
concat = function(...)
 
  +
if args["anthology"] and args["audio anthology"] then
local args = {...}
 
  +
args["anthology"] = args["anthology"] .. ", " .. args["audio anthology"]
if #args == 0 then
 
  +
elseif (not args["anthology"]) and args["audio anthology"] then
error('you must supply at least one argument to \'concat\' (got none)')
 
  +
args["anthology"] = args["audio anthology"]
end
 
  +
end
local firstArg = table.remove(args, 1)
 
  +
if type(firstArg) == 'string' or type(firstArg) == 'number' then
 
  +
-- combines {{{release year}}} and {{{release date}}}
firstArg = print(firstArg)
 
  +
if args["release year"] and args["release year"] ~= "" then
else
 
  +
args["release date"] = args["release year"]
firstArg = ''
 
end
+
end
  +
if #args == 0 then
 
  +
variantData = h.getAndFormatUnlinkedItems(args)
return firstArg
 
  +
--variantData["issues"] = h.getAndFormatIssuesText(args) not currently in use
else
 
  +
variantData["series"] = h.getAndFormatSeriesText(args)
return firstArg .. concat(unpack(args))
 
  +
variantData["release year"] = h.getReleaseYearText(args)
end
 
  +
  +
variantData["variant"] = args["variant"] or "variant"
  +
--data["original"] = args["original"] or ("''[[" .. util_link.stripPipe(name) .. "|" .. util_link.stripPipe(util_link.stripDab(name)) .. "]]''")
  +
  +
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 "")
  +
end
  +
h.storeText(outputText, name .. "-" .. variantData["variant"])
 
end
 
end
   
  +
--- This dumps the variable (converts it into a string representation of itself)
 
  +
--[[==========================================
--
 
  +
Displaying citations
-- @param entity mixed, value to dump
 
  +
==========================================]]
-- @param indent string, can bu used to set an indentation
 
  +
-- @param omitType bool, set to true to omit the (<TYPE>) in front of the value
 
  +
function h.generatePreciseCite(args)
--
 
  +
local preciseCite = ""
-- @return string
 
  +
if args["precisecite"] and args["precisecite"] ~= "" then
dump = function(entity, indent, omitType)
 
  +
preciseCite = " " .. args["precisecite"]
local entity = entity
 
  +
else
local indent = indent and indent or ''
 
  +
local info = {["edition"] = "", ["chapter"] = "", ["page"] = "", ["timestamp"] = ""}
local omitType = omitType
 
if type( entity ) == 'table' then
+
if args["ed"] and args["ed"] ~= "" then
  +
info["edition"] = " Edition: " .. args["ed"]
local subtable
 
  +
end
if not omitType then
 
  +
if args["chapt"] and args["chapt"] ~= "" then
subtable = '(table)[' .. #entity .. ']:'
 
  +
info["chapter"] = " Chapter: " .. args["chapt"]
end
 
  +
elseif args["chaptnum"] and args["chaptnum"] ~= "" then
indent = indent .. '\t'
 
  +
if string.match(args["chaptnum"], "-") or string.match(args["chaptnum"], ",") or string.match(args["chaptnum"], "&") or string.match(args["chaptnum"], "and") then
for k, v in pairs( entity ) do
 
  +
info["chapter"] = " Chapters " .. args["chaptnum"]
subtable = concat(subtable, '\n', indent, k, ': ', dump(v, indent, omitType))
 
  +
else
end
 
  +
info["chapter"] = " Chapter " .. args["chaptnum"]
return subtable
 
  +
end
elseif type( entity ) == 'nil' or type( entity ) == 'function' or type( entity ) == 'boolean' then
 
  +
if args["chaptname"] and args["chaptname"] ~= "" then
return ( not omitType and '(' .. type(entity) .. ') ' or '' ) .. print(entity)
 
  +
info["chapter"] = info["chapter"] .. ", \"" .. args["chaptname"] .. "\""
elseif type( entity ) == 'string' then
 
  +
end
entity = mw.ustring.gsub(mw.ustring.gsub(entity, "\\'", "'"), "'", "\\'")
 
  +
end
return concat(omitType or '(string) ', '\'', entity, '\'')
 
  +
if args["page"] and args["page"] ~= "" then
else
 
  +
if string.match(args["page"], "-") or string.match(args["page"], ",") or string.match(args["page"], "&") or string.match(args["page"], "and") then
-- number value expected
 
  +
info["page"] = " Pages " .. args["page"]
return concat(omitType or '(' .. type( entity ) .. ') ', entity)
 
  +
else
end
 
  +
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
 
end
   
  +
function h.getInfo(story, variant)
--- This function prints a variable depending on its type:
 
  +
local info
-- * tables get concatenated by a comma
 
  +
local dataStore = story
-- * bools get printed as true or false
 
  +
if variant and variant ~= "" then
-- * strings and numbers get simple returned as string
 
  +
dataStore = story .. "-" .. variant
-- * functions and nils return as emtpy string
 
  +
end
-- @return string
 
  +
if dataStore == "##" then
print = function(v)
 
  +
info = "Error: code 3 - no source given in template transclusion."
if type( v ) == 'table' then
 
  +
elseif util_vars.getVar(PREFIX .. dataStore) then
return table.concat(v, ',')
 
  +
info = util_vars.getVar(PREFIX .. dataStore)
elseif type( v ) == 'boolean' then
 
  +
elseif cache.get(PREFIX .. dataStore) then
return ( v and 'true' or 'false' )
 
  +
info = cache.get(PREFIX .. dataStore)
elseif type(v) == 'string' or type(v) == 'number' then
 
  +
util_vars.setVar(PREFIX .. dataStore, info)
return tostring(v)
 
  +
elseif mw.smw.ask( "[[" .. story .. "]]" .. "\n|?Story info")[1]["Story info"] then
else
 
  +
smwResult = mw.smw.ask( "[[" .. story .. "]]" .. "\n|?Story info")[1]["Story info"]
return ''
 
  +
if type(smwResult) == "table" then
end
 
  +
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 source 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 [[" .. story .. "|" .. 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 [[" .. story .. "|" .. ital .. sourceDisplayName .. ital .. additionalDisplay .. "]], "
  +
elseif args[2] and args[2] ~= "" then
  +
display = ital .. args[2] .. ital .. additionalDisplay
  +
elseif story == "A Bloody (And Public) Domaine (short story)" then --annoying edgecase
  +
display = ital .. "A Bloody (And Public) Domaine" .. 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
  +
if util_vars.getVar("CS-NWLH") == "false" then
  +
main = h.getInfo(story, variant)
  +
else
  +
main = NWLH.nonWLHText(h.getInfo(story, variant))
  +
end
  +
end
  +
collapsibleText = pretext .. main .. preciseCite
  +
else
  +
collapsibleText = preciseCite
  +
end
  +
end
  +
  +
if collapsibleText ~= "" then
  +
if collapsibleText:sub(-1) ~= "." then
  +
collapsibleText = collapsibleText .. "."
  +
end
  +
finishedCitation = quote .. "[[" .. story .. section .. "|" .. display .. "]]" .. quote .. " <sup title=\"Toggle " .. display:gsub("''", "") .. " citation\" class=\"mw-customtoggle-cs<id>\" aria-label=\"Toggle " .. display:gsub("''", "") .. " citation\">[+]</sup><span class=\"mw-collapsible mw-collapsed cse\" id=\"mw-customcollapsible-cs<id>\">" .. collapsibleText .. "</span>"
  +
else
  +
finishedCitation = quote .. "[[" .. story .. section .. "|" .. 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("<pre>" .. finishedCitation .. "</pre>")
  +
end
  +
end
  +
local id = tostring((tonumber(util_vars.getVar("id")) or 0) + 1)
  +
util_vars.setVar("id", id)
  +
finishedCitation = finishedCitation:gsub("<id>", id)
  +
return finishedCitation
  +
 
end
 
end
   

Latest revision as of 12:28, 21 January 2024

visit Module:Cite_source/doc to edit this page

Module:Cite source is the Lua module that forms the guts of {{cite source}}. You should aim to be familiar with Lua before editing it.

Broadly, the module is broken up into 2 major sections. The first is for formatting and storing information gathered from {{Infobox Doctor Who Story SMW}}. The second is for accessing this information and producing citations. The main method used by this module for storing data is LuaCache with Semantic MediaWiki as a backup. Variables is used for caching queries on individual pages.

The module opens by importing other util libraries. Currently, these are named in snake_case for some reason but this may be changed to camelCase in future. PREFIX is a constant used by Lua Cache and Variables to name the data stores used by the module. In the case of something going seriously wrong, increment the number in the prefix. This will reset all data stored by the module. The p table stores the functions returned by the module. These are accessible by the wider wiki. The h table stores the functions only used by other functions in this module.

Following this, there is a short function taken from Stack Overflow that inverts tables, swapping keys and values.

Generating and saving citations[]

The first major part of this module deals with gathering information from {{Infobox Doctor Who Story}}, formatting it into citations and saving it. The main functions here are p.infoboxStore and p.variantStore, the others being called from these 2. The general idea is that each of these 2 functions put together a table containing raw data from the infobox that is then processed by various helper functions to produce the output. These will now be discussed in more detail in the same order as in the module.

h.getAndFormatUnlinkedItems[]

This function deals with anything that is entered into the infobox unlinked and comma-separated in the case of multiple entries. Namely, these are the source's anthology, writer, publisher, network and the source that it was adapted from. None of these will be applicable to every source but they are all handled at the same time by this function.

The function takes as an argument the full table of infobox arguments. A data table is then created with blank entries for each piece of data handled by this function. The keys in this table must match the corresponding keys in the args table and hence the names of the parameters in {{Infobox Doctor Who Story}}.

The function then iterates through each entry in this data table and checks if there's a non-empty corresponding entry in the args table. This way, it processes only the entries in the data that actually have information in the infobox. The potentially comma-separated string passed to the infobox is then converted into a table. This occurs whenever there are actually multiple values for infobox field or not. Either way, we get a table, even if it only has 1 entry.

If this table has more than 4 items, only the first will be assigned to the relevant entry in the data table, followed by "et al.". This phrase is wrapped in a span with the other values in the table placed in the span's title attribute, displaying them when "et al." is hovered over on desktop. Otherwise (in most cases), all values will be listed in the relevant data table entry. Each of these are linked with the display text set to the undabbed page name.

The data table is then returned. This table will be added to by subsequent functions before being converted into an output string.

h.getAndFormatIssuesText (unused)[]

Following this is an old unused function which formatted issues for comics. However, this information is not currently being displayed in finished citations and so it has been removed.

It worked very similarly to the above but without links (and without the whole "et al." thing when more than 4 items are given.

h.getAndFormatSeriesText[]

This function creates the bit of text stating the source's series. There are multiple places that this could come from in the infobox, none of which are ideal or one-size-fits-tall, which are dealt with here. Therefore, it takes the args table as an argument.

The simplest case is if citation series is specified. This is an override field that allows the exact series to be used to be specified. No further formatting occurs here.

The largely more complex fallback is if range is specified. In this case, the field is linked and that is used.

Failing this, series is used. This infobox field is primarily used for navigation and so is unideal but is often the best that we have. The overrides (not variable is a data module, Module:Cite source/series overrides, that holds common overrides that should be applied automatically. For example, "[[Doctor Who television stories|''Doctor Who'' television stories]]" is very commonly given in the series infobox field and this data module contains a rule to automatically convert this to "''[[Doctor Who]]''". If an override does exist, it is used. Otherwise, the raw text passed to series is used. series is a freeform variable which should already have links and hence no additional formatting is applied. Finally, the season number entry in the args table is checked and, if this exists, the season/series is added to the end of the series text.

The series text produced is this return. Note that, if no series is specified anywhere, this may be blank and this is fine.

h.getReleaseYearText[]

This function formats the release year presented in the infobox. This should be presented unlinked in accordance with {{date link}} and hence a lot of formatting must be done here.

Firstly, all date fields from the infobox are combined, using the first non-empty field in the following list:

  • release date
  • broadcast date
  • premiere
  • beta release date
  • cover date

These fields are run through util_link.stripDab in order to remove parenthetical notes and are then split on "-" in order to separate the start and end release dates of serialised sources, these 2 dates being placed in a table.

If this table has 2 or more entries, it can be assumed that there is both a release date and a release end date. These are extracted and assigned to their respective variables. :gsub("%s+", "") strips any whitespace while :sub(-4) extracts the final 4 characters of the date which should be the year. One simple way to check is to see if the string contained the year can be converted into a number. This is done and, if it can't, the dates are re-adjusted accordingly.

If the table only has 1 entry, the source only has a single release date and the same process as above occurs on this, without the check to see if the year is a number.

The release date(s) are then formatted with links added and the resulting string is returned.

h.makeOutput[]

This is a key function in this half of the module which combines all of the processed data from all previous functions into a cohesive string of text. It takes as arguments a data table containing all of the processed data produced by the previous functions and an args table containing the raw arguments from the infobox.

If data["writer"] is non-empty, this is added to the output. A check is then made to see if data["adapted from"] is non-empty. If it is, this to is added to the output. Moreover, a SMW query is used to check if the source from which the current source was adapted from has a writer. If it does, this added to the output in brackets. If there was no writer, this bit is skipped and only the "adapted from" information is added to the output.

The remaining data is then formatted so that most of it is placed in brackets with the first piece of data outside of the brackets. For example, "Doctor Who season 1 (BBC tv, 1963-1964)". To achieve this, all possible pieces of data are looped through and, if they exist, they are added to a table called loopData. It is at this stage that italics are added to the source's anthology, if it was published within one. The remainder of the output is then built up through a series of if statements.

Following this, if the outputText variable is, for whatever reason, blank, it is set to an error message. Otherwise, a full stop is added. outputText is then returned.

h.storeText[]

This little function deals with storing the output of this half of the module. The first line stores it to Extension:Variables. The next line deletes any previous text for this source stored in Extension:LuaCache and the line following this stores the up to date output to LuaCache. The final line stores the output to Extension:Semantic MediaWiki in Property:Story info.

This function returns nothing.

p.infoboxStore[]

This function is the main way in which all of those discussed so far will be called. It is integrated into {{Infobox Doctor Who Story SMW}} and will execute on any pages with this infobox. It is responsible for combining all prior functions to take all of the arguments from the infobox and process them into a cohesive string of text which is then stored.

It takes as an argument the frame object. If you are unsure what this is, have a look at this blog post. The arguments passed to the infobox are then extracted and assigned to the args table. The next line takes the first unamed argument passed to the module when it is invoked in the template. This is not the first argument passed to the infobox, rather something hardcoded and passed into the module directly by the template. This first unamed argument will be {{PAGENAME}}, a magic word that evaluates to the name of the page that the infobox is used on.

Following this, the citation text argument is checked and, if it exists, pretty much everything else is skipped and just this is used for the output.

Otherwise, a full citation is put together. Firstly, the separator argument is checked. This is used for whenever there is a list of multiple items. They are split on commas by default but other separators can be set if necessary, such as if commas appear in one of the items in the list.

The arguments passed into the various formatting functions documented above are actually not entirely the raw arguments passed into the infobox. A bit of pre-processing occurs here first. Firstly, anthology and audio anthology are combined. The same happens to adapted from and novelisation of.

Following this, the bulk of the processing to produce the finished additional info occurs by calling each of the functions documented above. The data table is created by h.getAndFormatUnlinkedItems(args) and then added to by each of the other 2 functions. Note the commented out line. This information is not currently included in the additional information but may be re-included in the future.

Following this, h.makeOutput is run and the result of this is passed to h.storeText to be stored. Nothing is returned from the function.

p.variantStore[]

This function is similar to the one above and is used to produce citations for variants of a source that are covered on the same page as the main version, such as animated reconstructions, narrated soundtracks or audiobook readings. It is called solely from {{store variant data}}.

This function begins identically to the one above

Following the combination of anthology and audio anthology, the release year and release date variables are combined. Note that, while only the year is required, the full date can be specified, hence this step. Each of the processing functions discussed above are then called, only with the processed data being saved into the variantData table instead of the data table.

Next data["variant"] is defined. This is the name by which the variant citation will be accessible through {{cite source}} and will also be displayed in the finished citation.

Following this, the finished citation text is generated. First, the string in data["variant"] has its first letter capitalised. Then, a table called KEYS is defined, containing the keys of all pieces of data from variantData to be included in the final output. These are then iterated through and the corresponding keys in variantData are checked. A comma separated list is then generated and, if this list is not empty, it is surrounded by brackets and added to the finished citation. Finally, the finished citation generated by p.infoboxStore is retrieved from variables and added. The finished citation is then stored through h.storeText.

Nothing is returned from this function.

Displaying citations[]

The second major part of this module deals with requesting the stored citation for the provided source and adding to that all of the additional information given when {{cite source}} is used. It's main function is p.displayCitation with the other 2 being called from it. Each function will now be discussed in the order they're presented in the function.

h.generatePreciseCite[]

This function takes a number of arguments providing information to make citations precise and formats this into a string which can be appended to the output. It starts by defining preciseCite as an empty string. This variable will be added to and returned at the end.

What follows is a series of if statements to put everything together. The top level if checks if the precisecite argument is provided to the {{cite source}} template call. If it is, this overrides all other arguments and forms the entirety of the precise cite text. Otherwise, a table is defined with keys for each piece of information that can be included in this output. Each of these potential pieces of information is then gone through in order:

  • The ed argument is for the source's edition.
  • The chapt argument is used when providing just a named chapter without chapter numbers. If chapter numbers are provided, these should be in the chaptnum argument and can be suplemented with a chapter name provided as chaptname. For chaptnum, a few characters are checked for in the input string. If these are present, a pluralising "s" is added to the output.
  • The page argument is for the page number and uses the same pluralisation method as chaptnum.
  • The timestamp argument is for custom timestamps and overrides all other timestamp-related arguments. Otherwise, a table is defined with keys for each of the 3 values that can form the timestamp. The parameters for each of these are then checked for with if statements. If at least one of them exists, they are iterated through in a loop and formatted into a string. Pluralising "s"s are added unless the value is either "1" or "one".

The pieces of information in the table are then iterated through, added to the output delimited by "; ". Finally, the trailing "; " is removed and the finished string is returned.

h.getInfo[]

This function deals with requesting the stored citation text. The dataStore variable is the key used across each method of storing data to identify the source, or (where relevant) the variant of a source.

The first place that is checked is Variables. This is local to the page and very fast, used if the citation has already been fetched elsewhere in the page. Failing that, LuaCache is checked. If it is available here, Variables is set. If, for whatever reason, LuaCache does not have the required data, Semantic MediaWiki is checked as a failsafe. This option is more complicated so requires some depth.

Semantic MediaWiki cannot have multiple keys per page to store variants. The way variants can be detected is by checking the data type of the SMW result. If it is a table, it includes variants. This is fine if there is only one variant but it is very hard to distinguish multiple variants so the code currently doesn't. This should come up so rarely, however, that it is likely fine to never fix this. If it is the case, an error is given. If the data type is string, we have no variants to worry about and things are simple. Regardless of the result, the requested data is saved to Variables and LuaCache.

If data was unable to be found in any location, an error is given. Finally, the info variables containing the requested info is returned.

p.displayCitation[]

This function, by far the biggest (taking up over 200 lines), is called from {{cite source}} and combines the stored citation with all of the extra information provided to the template. It begins by setting up the frame and initialising some basic variables. The name argument is then checked. If it has a value, this citation is either referencing a previous named citation or defining a new one. If it is referencing a previous citation, this is used as the finishedCitation. Otherwise, a new one is generated from the information provided.

The first unamed argument is checked. This should contain the source that is being cited's name. If it doesn't exist, an error is returned. Otherwise, linking brackets are stripped. These are allowed to be present for linking autosuggest.

The link's additionalDisplay text is then dealt with. This is shown as part of the link's display text alongside the source's name. There are a number of things that can be included in it which are checked in a series of if elseif statements. The only complication is the notital argument which causes the source's title not to display. If this argument is set, colons are not included for multipath source details.

The section of the page being linked to is then dealt with, again using an if elseif statement. The preciseCite is then generated using h.generatePreciseCite as discussed earlier.

If the a named part or episode is being cited, this is then checked for. Italics are included by default but are removed if necessary. This is achieved using the ital variable which is always concatenated into the output but can either contain "''" or nothing. Similarly, quote is blank by default but is set to """ (a quotation mark symbol) if necessary. If there is a named episode or part being cited, this is set as the display text. Otherwise, the user-defined display text in the second unamed argument is used. Otherwise, the undabbed page name of the cited source is used. Overriding all of these, if the notital argument is set, no total is display. In any case, the additionalDisplay is concatenated on. The pretext variable is also built here, displayed before the main bit of the collapsible text and containing information on where the part or episode being cited is from.

The next block of code deals with the actual text in the collapsible portion of the citation. If the citationtext argument is set, this is used. Otherwise, any arguments that override individual pieces of data in the citation are checked for and, if any are present, the other pieces of data are requested through Semantic MediaWiki and a new citation is built up using the same functions as are discussed earlier. This process involves building up a new SMW query for the data not overridden, cleaning up the result of this query and formatting the results. However, in the majority of cases, no overrides will be provided and none of this will happen. In these cases, h.getInfo is called to retrieve the citation saved in the source's infobox. The collapsibleText is then built from the three elements: the pretext built earlier, the main text built here and the preciseCite text. If the noinfo argument was set, only preciseCite is included in the collapsible.

Then, if the collapsibleText does not end with a ".", one is added. All of the text previously defined above is then concatenated into one final output string containing all of the required syntax to make the collapsible work. This makes use of collapsible elements where unique class and ID pair to make the [+] uncollapse the collapsible involves a number that increments automatically with every use of the template using Variables. If there is no collapsible text to display, an output without the collapsible is produced instead.

If a named citation is being defined, that is done here. Finally, the debug argument wikitextoutput is checked. If it is set, the output is provided as pure, unprocessed wikitext. The finishedCitation is then returned.


The final thing to occur in the module is that the p table, containing all of the functions intended to be executed through templates, is returned.

visit Module:Cite_source/doc to edit this page

Authors and acknowledgements[]

This module was primarily authored by User:Bongolium500. He would like to thank the frequenters of the #lua and #smw-on-fandom channels on the Fandom Discord server for their invaluable advice and contributions. In particular, User:RheingoldRiver enabled extensions on this wiki to facilitate this project (she used to be a Wiki Representative), reviewed earlier versions of the code and helped talk through ideas to get this project finished.

Thanks must also be given to every editor who ever provided feedback on this project and to User:Scrooge MacDuck who permitted Bongolium to test {{Infobox Doctor Who Story SMW}} before he was an admin.

Finally, acknowledgement must be given to the authors and contributors of each of the modules imported at the top of this module, as well as to lhf on Stack Overflow for their table inverting function.

Note to future contributors: please add your own name and that of any others who helped you here.

Code[]


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(current[1]) .. "|" .. util_link.stripPipe(util_link.stripDab(current[1])) .. "]]" .. ", <span title=\""
				for i = 2, #current do
					data[key] = data[key] .. util_link.stripPipe(util_link.stripDab(current[i]))
					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
				data[key] = data[key] .. "\">et al.</span>"
			else
				for i = 1, #current do
					if string.find(string.lower(current[i]), "unknown") or string.find(string.lower(current[i]), "various") or string.find(string.lower(current[i]), "unclear") then
						data[key] = data[key] .. current[i]
					else
						data[key] = data[key] .. "[[" .. util_link.stripPipe(current[i]) .. "|" .. 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, #current do
			issues = issues .. current[i]
			if i == #current-1 then
				issues = issues .. " and "
			elseif i == #current then
				issues = issues
			else
				issues = issues .. ", "
			end
		end
	end
	return issues
end]]--

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("{{#switch:" .. util_link.stripPipe(args["range"]) .. "|#default=" .. "''[[" .. args["range"] .. "{{!}}" .. util_link.stripDab(args["range"]) .. "]]''" .. "|MR|Main Range|0=[[Main Range]]|BSSR|Bernice Summerfield Single Releases=''[[Bernice Summerfield (series)|Bernice Summerfield]]'': Single Releases|BSBS|Bernice Summerfield Box Sets=''[[Bernice Summerfield (series)|Bernice Summerfield]]'': Box Sets|Dark Eyes (audio series)|The Eighth Doctor Adventures: Dark Eyes=''[[The Eighth Doctor Adventures#Dark Eyes|The Eighth Doctor Adventures: Dark Eyes]]''|Doom Coalition (audio series)|The Eighth Doctor Adventures: Doom Coalition=''[[The Eighth Doctor Adventures#Doom Coalition|The Eighth Doctor Adventures: Doom Coalition]]''|Ravenous (audio series)|The Eighth Doctor Adventures: Ravenous=''[[The Eighth Doctor Adventures#Ravenous|The Eighth Doctor Adventures: Ravenous]]''|Stranded (audio series)|The Eighth Doctor Adventures: Stranded=''[[The Eighth Doctor Adventures#Stranded|The Eighth Doctor Adventures: Stranded]]''|BFTW|Big Finish Torchwood=''[[Torchwood - Monthly Range|Torchwood]]''|BFTWSR|Big Finish Torchwood Special Releases=''[[Torchwood - Special Releases]]''|NSA|BBC New Series Adventures=[[BBC New Series Adventures|BBC ''New Series Adventures'']]|NSAA|New Series Adventures Audios=''[[BBC New Series Adventures#Audiobooks|New Series Adventures]]'' Audio|BBCTWAD|BBC Torchwood Audio Dramas=[[BBC Torchwood audio stories#Audio dramas|BBC ''Torchwood'' Audio Drama]]|BBCTWAB|BBC Torchwood Audiobooks=[[BBC Torchwood audio stories#Audiobooks|BBC ''Torchwood'' Audiobook]]|DWM comic stories=[[DWM comic stories|DWM Comics]]}}") -- 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 .. " [[" .. util_link.stripPipe(args["season number"]) .. "|" .. string.lower(util_link.stripPipe(util_link.stripDab(args["season number"]))) .. "]]"
		end
		if args["special"] and args["special"] ~= "" then
			series = series .. " " .. args["special"]:gsub("<br>", " and "):gsub("<br/>", " and "):gsub("<br />", " and ")
		end
	end
	return series
end

function h.getReleaseYearText(args)
	-- get and format release year
	local releaseYearText = ""
	if (args["release date"] and args["release date"] ~= "") or (args["broadcast date"] and args["broadcast date"] ~= "") or (args["premiere"] and args["premiere"] ~= "") or (args["beta release date"] and args["beta release date"] ~= "") or (args["cover date"] and args["cover date"] ~= "") or (args["opening date"] and 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 .. " (releases)|" .. releaseYear .."]]-[[" .. releaseEndYear .. " (releases)|" .. releaseEndYear .. "]]"
		else
			releaseYearText = "[[" .. releaseYear .. " (releases)|" .. 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 query = mw.smw.ask( "[[" .. util_link.stripPipe(args["adapted from"]) .. "]]" .. "\n|?Writer")
			local originalWriter = ""
			if query then
				originalWriter = query[1]["Writer"] or ""
			end
			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]
	local outputText = ""
	if args["citation text"] then
		outputText = args["citation text"]
	else
		if not args["separator"] then
			args["separator"] = ","
		end
		
		-- combines {{{anthology}}} and {{{audio anthology}}}
		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 {{{adapted from}}} and {{{novelisation of}}}
		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
		
		outputText = h.makeOutput(data, args):gsub("%[%[#", "[[" .. name .. "#")
	end
	--outputText = util_smw.dump(data) --for testing
	h.storeText(outputText, name)
end

function p.variantStore(frame)
	local args = frame:getParent().args
	local name = frame.args[1]
	local outputText = ""
	local variantData = {}
	if args["citation text"] then
		outputText = args["citation text"]
	else
		if not args["separator"] then
			args["separator"] = ","
		end
		-- combines {{{anthology}}} and {{{audio anthology}}}
		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 {{{release year}}} and {{{release date}}}
		if args["release year"] and args["release year"] ~= "" then
			args["release date"] = args["release year"]
		end
		
		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(name) .. "|" .. util_link.stripPipe(util_link.stripDab(name)) .. "]]''")
		
		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 "")
	end
	h.storeText(outputText, name .. "-" .. variantData["variant"])
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 dataStore == "##" then
		info = "Error: code 3 - no source given in template transclusion."
	elseif 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 source 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 [[" .. story .. "|" .. 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 [[" .. story .. "|" .. ital .. sourceDisplayName .. ital .. additionalDisplay .. "]], "
		elseif args[2] and args[2] ~= "" then
			display = ital .. args[2] .. ital .. additionalDisplay
		elseif story == "A Bloody (And Public) Domaine (short story)" then --annoying edgecase
			display = ital .. "A Bloody (And Public) Domaine" .. 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
					if util_vars.getVar("CS-NWLH") == "false" then
						main = h.getInfo(story, variant)
					else
						main = NWLH.nonWLHText(h.getInfo(story, variant))
					end
				end
				collapsibleText = pretext .. main .. preciseCite
			else
				collapsibleText = preciseCite
			end
		end
		
		if collapsibleText ~= "" then
			if collapsibleText:sub(-1) ~= "." then
				collapsibleText = collapsibleText .. "."
			end
			finishedCitation = quote .. "[[" .. story .. section .. "|" .. display .. "]]" .. quote .. " <sup title=\"Toggle " .. display:gsub("''", "") .. " citation\" class=\"mw-customtoggle-cs<id>\" aria-label=\"Toggle " .. display:gsub("''", "") .. " citation\">[+]</sup><span class=\"mw-collapsible mw-collapsed cse\" id=\"mw-customcollapsible-cs<id>\">" .. collapsibleText .. "</span>"
		else
			finishedCitation = quote .. "[[" .. story .. section .. "|" .. 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("<pre>" .. finishedCitation .. "</pre>")
		end
	end
	local id = tostring((tonumber(util_vars.getVar("id")) or 0) + 1)
	util_vars.setVar("id", id)
	finishedCitation = finishedCitation:gsub("<id>", id)
	return finishedCitation

end

return p