Module:Cite
Generates citations across the wiki to ensure standard formatting. The module accepts several different types of citations and processes each differently, based on the f table's functions.
f.twitter(args)(function)- Handles Twitter citations.
- Parameter:
argsCitation type arguments (table) - Returns: Citation text (string)
- See also:
f.bluesky(args)(function)- Handles Bluesky citations.
- Parameter:
argsCitation type arguments (table) - Returns: Citation text (string)
- See also: Template:Cite bluesky
f.game(frame_args)(function)- Handles citations of text in the game.
- Parameter:
frame_argsCitation type arguments (table) - Returns: Citation text (string)
- See also: Template:Cite game
f.youtube(args)(function)- Handles citations of YouTube videos.
- Parameter:
argsCitation type arguments (table) - Returns: Citation text (string)
- See also: Template:Cite youtube
f.tumblr(args)(function)- Handles Tumblr citations.
- Parameter:
argsCitation type arguments (table) - Returns: Citation text (string)
- See also: Template:Cite tumblr
f.code(args)(function)- Handles citations of the game's code.
- Parameter:
argsCitation type arguments (table) - Returns: Citation text (string)
- See also:
f.news(args)(function)- Handles citations from news sources.
- Parameter:
argsCitation type arguments (table) - Returns: Citation text (string)
- See also: Template:Cite news
f.twitch(args)(function)- Handles citations from Twitch clips.
- Parameter:
argsCitation type arguments (table) - Returns: Citation text (string)
- See also: uty:Template:Cite twitch
err(text)(function • local)- Wrapper for
userErrorthat places the page under the Pages with user errors category. - Parameter:
textText to display as an error (string) - Returns: Wikitext with the error and category (string)
valid_date(d)(function • local)- Checks whether a given date string is a valid date.
- Parameter:
dDate string to check (string) - Returns: Whether the string is a valid date (boolean)
f(table • local)- Table of possible citation types and the way they are processed. Each type has a function associated with it that gets passed the arguments after the type, does validation of these arguments and returns the whole citation text.
--- Generates citations across the wiki to ensure standard formatting.
-- The module accepts several different types of citations and processes each
-- differently, based on the <code>f</code> table's functions.
-- @module cite
-- @alias p
-- @require Module:Date
-- @require Module:User error
-- @require Module:Yesno
-- @require Module:Tags
-- @require Module:Cite/data
-- @author [[User:KockaAdmiralac|KockaAdmiralac]]
-- @author [[User:Ceruleanwarbler2|Ceruleanwarbler2]]
-- @author [[User:Jacky720|Jacky720]]
-- @see [[:Category:Citation templates]]
-- <nowiki>
local p = {}
require('strict')
-- Module dependencies
local Date = require('Module:Date')
local yesno = require('Module:Yesno')
local tags = require('Module:Tags')
local data = mw.loadData('Module:Cite/data')
-- Private logic.
local DATE_FORMAT = '%B %d, %Y.'
--- Wrapper for <code>userError</code> that places the page under the Pages with
-- user errors category.
-- @function err
-- @param {string} text Text to display as an error
-- @return {string} Wikitext with the error and category
-- @local
local function err(text)
return require('Module:User error')(text, 'Pages with user errors')
end
--- Checks whether a given date string is a valid date.
-- @function valid_date
-- @param {string} d Date string to check
-- @return {bool} Whether the string is a valid date
-- @local
local function valid_date(d)
return pcall(function()
Date(d)
end)
end
--- Table of possible citation types and the way they are processed.
-- Each type has a function associated with it that gets passed the arguments
-- after the type, does validation of these arguments and returns the whole
-- citation text.
-- @table cite_functions
-- @alias f
local f = {}
--- Handles Twitter citations.
-- @function f.twitter
-- @param {table} args Citation type arguments
-- @return {string} Citation text
-- @see [[Template:Cite twitter]]
-- @see [[github:client9/snowflake2time]]
function f.twitter(args)
-- Check validity of arguments
if not args.id or not tonumber(args.id) then
return err('Tweet snowflake invalid or not specified')
end
if not args.author or not args['text'] then
return err('Tweet author or citation not specified')
end
if args.wayback and not tonumber(args.wayback) then
return err('Archive ID invalid')
end
if args.archivetoday and (not args.archivetimestamp or not valid_date(args.archivetimestamp)) then
return err('Date of archivation not specified')
end
-- Twitter snowflake date extraction
local snowflake = tonumber(args.id)
local epoch = math.floor(snowflake / 4194304 + 1288834974657)
local date1 = Date(math.floor(epoch / 1000))
-- Format citation
local date2
if args.archivetimestamp then
date2 = Date(args.archivetimestamp):fmt(DATE_FORMAT)
elseif args.wayback then
date2 = Date(args.wayback):fmt(DATE_FORMAT)
end
local deadurl = yesno(args['deleted'], false)
local url
if deadurl and args.archivetoday then
url = string.format('https://archive.today/%s/', args.archivetoday)
elseif deadurl and args.wayback then
url = string.format(
'https://web.archive.org/web/%s/https://twitter.com/%s/status/%s',
args.wayback,
args.author,
args.id
)
else
url = string.format('https://twitter.com/%s/status/%s', args.author, args.id)
end
local str = {
'<q cite="',
url,
'">',
args.text,
'</q> — <cite class="twitter">[',
url
}
if data.twitter[args.author] then
table.insert(str, ' ')
table.insert(str, data.twitter[args.author])
table.insert(str, ' (@')
table.insert(str, args.author)
table.insert(str, ')')
else
table.insert(str, ' @')
table.insert(str, args.author)
end
table.insert(str, ' on Twitter,]</cite> ')
table.insert(str, date1:fmt(DATE_FORMAT))
if deadurl then
if args.wayback or args.archivetoday then
table.insert(str, ' Archived on ')
table.insert(str, date2)
else
table.insert(str, ' \'\'\'[deleted]\'\'\'')
end
end
return table.concat(str)
end
--- Handles Bluesky citations.
-- @function f.bluesky
-- @param {table} args Citation type arguments
-- @return {string} Citation text
-- @see [[Template:Cite bluesky]]
function f.bluesky(args)
if not args.author or not args.text then
return err('Author or quote not specified')
end
if not args.id then
return err('Post ID not specified')
end
if not args.timestamp or not valid_date(args.timestamp) then
return err('Date invalid or not specified')
end
if args.wayback and not tonumber(args.wayback) then
return err('Archive ID invalid')
end
if args.archivetoday and (not args.archivetimestamp or not valid_date(args.archivetimestamp)) then
return err('Date of archivation not specified')
end
local archivedate
if args.archivetimestamp then
archivedate = Date(args.archivetimestamp):fmt(DATE_FORMAT)
elseif args.wayback then
archivedate = Date(args.wayback):fmt(DATE_FORMAT)
end
local deadurl = yesno(args['deleted'], false)
local url
if deadurl and args.archivetoday then
url = string.format('https://archive.today/%s/', args.archivetoday)
elseif deadurl and args.wayback then
url = string.format(
'https://web.archive.org/web/%s/https://bsky.app/profile/%s/post/%s',
args.wayback,
args.author,
args.id
)
else
url = string.format('https://bsky.app/profile/%s/post/%s', args.author, args.id)
end
local str = string.format(
'<q cite="%s">%s</q> — <cite class="bluesky">[%s @%s on Bluesky,]</cite> %s',
url,
tags.replace(args.text),
url,
args.author,
Date(args.timestamp):fmt(DATE_FORMAT)
)
if deadurl then
if args.wayback or args.archivetoday then
str = string.format(
'%s Archived on %s',
str,
archivedate
)
else
str = string.format(
'%s \'\'\'[deleted]\'\'\'',
str
)
end
end
return str
end
--- Handles citations of text in the game.
-- @function f.game
-- @param {table} frame_args Citation type arguments
-- @return {string} Citation text
-- @see [[Template:Cite game]]
function f.game(frame_args)
local args = {}
for i, v in ipairs(frame_args) do
-- frame.args is a metatable, so we cannot use #args or table.concat
-- on it before we convert it to a real table.
args[i] = v
end
local num_args = #args
if num_args == 0 then
return err('Quote not specified')
end
if num_args == 1 then
return err('Quote author not specified')
end
local author = args[num_args]
args[num_args] = nil
return string.format(
'<q>%s</q> — <cite class="game">%s</cite>',
tags.replace(table.concat(args, '<br />')),
author
)
end
--- Handles citations of YouTube videos.
-- @function f.youtube
-- @param {table} args Citation type arguments
-- @return {string} Citation text
-- @see [[Template:Cite youtube]]
function f.youtube(args)
if not args.id or not args.title then
return err('Video ID or title not specified')
end
local url
if args.timestamp then
url = string.format('https://youtu.be/%s?t=%s', args.id, args.timestamp)
else
url = string.format('https://youtu.be/%s', args.id)
end
local duration
local seconds = tonumber(args.timestamp)
if seconds ~= nil then
local minutes = math.floor(seconds / 60)
seconds = seconds - minutes * 60
local hours = math.floor(minutes / 60)
minutes = minutes - hours * 60
if hours > 0 then
duration = string.format(
' (%02d:%02d:%02d)',
hours,
minutes,
seconds
)
else
duration = string.format(
' (%02d:%02d)',
minutes,
seconds
)
end
else
duration = ''
end
return string.format(
'<cite class="youtube">[%s %s]</cite>%s on YouTube',
url,
args.title,
duration
)
end
--- Handles Tumblr citations.
-- @function f.tumblr
-- @param {table} args Citation type arguments
-- @return {string} Citation text
-- @see [[Template:Cite tumblr]]
function f.tumblr(args)
if not args.author or not args.text then
return err('Author or quote not specified')
end
if not args.id or not tonumber(args.id) then
return err('Post ID invalid or not specified')
end
if not args.timestamp or not valid_date(args.timestamp) then
return err('Date invalid or not specified')
end
local url = string.format(
'https://%s.tumblr.com/post/%s',
-- Temporary solution to avoid showing reblogger's name as author name
-- on inaccessible posts.
-- To be replaced by proper archival parameters.
args.reblogger or args.author,
args.id
)
return string.format(
'<q cite="%s">%s</q> — <cite class="tumblr">[%s %s on Tumblr,]</cite> %s',
url,
tags.replace(args.text),
url,
args.author,
Date(args.timestamp):fmt(DATE_FORMAT)
)
end
--- Handles citations of the game's code.
-- @function f.code
-- @param {table} args Citation type arguments
-- @return {string} Citation text
-- @see [[Template:Cite code]]
-- @see [[code:|Code repository]]
function f.code(args)
local hasLink = args.chapter == nil or tonumber(args.chapter) < 5
if not args.script then
return err('Script name not specified')
end
if args.line and not tonumber(args.line) then
return err('Starting line number is not a number')
end
if args.line2 and not tonumber(args.line2) then
return err('Ending line number is not a number')
end
local str = {'<cite class="code">'}
if hasLink then
table.insert(str, '[[code:')
if args.chapter ~= nil then
table.insert(str, 'ch')
table.insert(str, args.chapter)
table.insert(str, '/')
end
table.insert(str, args.script)
if args.line then
table.insert(str, '#L')
table.insert(str, args.line)
end
table.insert(str, '|')
end
table.insert(str, args.script)
table.insert(str, ' script')
if hasLink then
table.insert(str, ']]')
end
table.insert(str, '</cite>')
if args.line then
table.insert(str, ', line')
if args.line2 then
table.insert(str, 's ')
table.insert(str, args.line)
table.insert(str, '–')
table.insert(str, args.line2)
else
table.insert(str, ' ')
table.insert(str, args.line)
end
end
if args.chapter ~= nil then
table.insert(str, ', Chapter ')
table.insert(str, args.chapter)
end
return table.concat(str)
end
--- Handles citations from news sources.
-- @function f.news
-- @param {table} args Citation type arguments
-- @return {string} Citation text
-- @see [[Template:Cite news]]
function f.news(args)
if not args.text then
return err('Relevant news post excerpt not specified')
end
if not args.title then
return err('News post title not specified')
end
if not args.site then
return err('News site name not specified')
end
if not args.url then
return err('News post URL not specified')
end
if not args.timestamp or not valid_date(args.timestamp) then
return err('News post date invalid or not specified')
end
local author = {}
if args.firstname then
if args.lastname then
table.insert(author, args.lastname)
table.insert(author, ', ')
end
table.insert(author, args.firstname)
table.insert(author, ', ')
end
return string.format(
'<q cite="%s">%s</q> — [%s %s] (%s%s) <cite class="news">%s</cite>.',
args.url,
tags.replace(args.text),
args.url,
args.title,
table.concat(author),
Date(args.timestamp):fmt(DATE_FORMAT),
args.site
)
end
--- Handles citations from Twitch clips.
-- @function f.twitch
-- @param {table} args Citation type arguments
-- @return {string} Citation text
-- @see [[uty:Template:Cite twitch]]
function f.twitch(args)
if not args.id then
return err('Clip URL segment not specified')
end
if not args.title then
return err('Clip title not specified')
end
if not args.author then
return err('Streamer username not specified')
end
if not args.timestamp or not valid_date(args.timestamp) then
return err('Stream date invalid or not specified')
end
return string.format(
'<cite class="twitch">[https://clips.twitch.tv/%s %s,]</cite> a clip from [https://twitch.tv/%s @%s\'s] Twitch stream on %s.',
args.id,
args.title,
mw.ustring.lower(args.author),
args.author,
Date(args.timestamp):fmt(DATE_FORMAT)
)
end
-- Package items.
for mname, method in pairs(f) do
p[mname] = function(frame)
return method(frame:getParent().args)
end
end
return p