The official discord link if you wish to join the discord: https://discord.gg/j5RKwCvAFu

The background art comes from Cherylann1960.

Module:Cite

From The Codex
Revision as of 18:01, 16 April 2024 by GiverOfThePeace (talk | contribs)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

Lua error in package.lua at line 80: module 'Dev:Docbunto' not found.


--- 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. When the module
--  is called without a type, it acts as a citation needed template.
--  @module             cite
--  @alias              p
--  @require            Dev:Date
--  @require            Dev:User error
--  @require            Dev:Yesno
--  @require            Module:Tags
--  @author             [[User:KockaAdmiralac|KockaAdmiralac]]
--  <nowiki>
local p = {}

-- Module dependencies
local Date = require('Dev:Date')
local userError = require('Dev:User error')
local yesno = require('Dev:Yesno')
local tags = require('Module:Tags')
local data = mw.loadData('Module:Cite/data')
local title = mw.title.getCurrentTitle()

--  Private logic.

--- 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 userError(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
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.
--  Twitter citations receive arguments in this order:
--  * Twitter snowflake
--  * Tweet author's handle
--  * Citation from the tweet
--  * (Optional) Timestamp of the tweet's archival
--  * (Optional) Whether the original tweet link is dead
--  @function           f.twitter
--  @param              {table} args Citation type arguments
--  @return             {string} Citation text
function f.twitter(args)
    -- Check validity of arguments
    if not args[1] or not tonumber(args[1]) then
        return err('Tweet snowflake invalid or not specified')
    end
    if not args[2] or not args[3] then
        return err('Tweet author or citation not specified')
    end
    -- Twitter snowflake date extraction
    -- Credits: https://github.com/client9/snowflake2time
    local snowflake = tonumber(args[1])
    local epoch = math.floor(snowflake / 4194304 + 1288834974657)
    local date1 = Date(math.floor(epoch / 1000))
    -- Format citation
    local deadurl = yesno(args[5], false)
    local archived = yesno(args[4], true)
    local str = {
        '\'\'',
        args[3],
        '\'\' - ['
    }
    if deadurl and archived then
        table.insert(str, 'https://web.archive.org/web/')
        table.insert(str, args[4])
        table.insert(str, '/')
    end
    table.insert(str, 'https://twitter.com/')
    table.insert(str, args[2])
    table.insert(str, '/status/')
    table.insert(str, args[1])
    if data.twitter[args[2]] then
        table.insert(str, ' ')
        table.insert(str, data.twitter[args[2]])
        table.insert(str, ' (@')
        table.insert(str, args[2])
        table.insert(str, ')')
    else
        table.insert(str, ' @')
        table.insert(str, args[2])
    end
    table.insert(str, ' on Twitter,] ')
    table.insert(str, date1:fmt('%B %d, %Y.'))
    if deadurl then
        if archived then
            table.insert(str, ' Archived on ')
            table.insert(str, Date(args[4]):fmt('%B %d, %Y.'))
        else
            table.insert(str, ' \'\'\'[deleted]\'\'\'')
        end
    end
    return table.concat(str)
end

--- Handles citations of text in the game.
--  Game citations receive arguments in this order:
--  * Quotation
--  * Quote author and the situation in which they made the quote
--  @function           f.game
--  @param              {table} args Citation type arguments
--  @return             {string} Citation text
function f.game(args)
    if not args[1] then
        return err('Quote not specified')
    end
    if not args[2] then
        return err('Quote author not specified')
    end
    return table.concat({
        '\'\'',
        tags.replace(args[1]),
        '\'\' - ',
        args[2]
    })
end

--- Handles citations of YouTube videos.
--  YouTube citations receive arguments in this order:
--  * YouTube video ID
--  * Video title
--  * (Optional) Time at which the video plays
--  @function           f.youtube
--  @param              {table} args Citation type arguments
--  @return             {string} Citation text
function f.youtube(args)
    if not args[1] or not args[2] then
        return err('Video ID or title not specified')
    end
    local str = {
        '\'\'',
        args[2],
        '\'\' - [https://youtu.be/',
        args[1]
    }
    if args[3] then
        table.insert(str, '?t=')
        table.insert(str, args[3])
    end
    table.insert(str, ' YouTube]')
    return table.concat(str)
end

--- Handles Tumblr citations.
--  Tumblr citations receive arguments in this order:
--  * Tumblr name of the author
--  * Tumblr post ID
--  * Citation from the post
--  * Date in YYYY-MM-DD format (or any other standard format)
--  @function           f.tumblr
--  @param              {table} args Citation type arguments
--  @return             {string} Citation text
function f.tumblr(args)
    if not args[1] or not args[3] then
        return err('Author or quote not specified')
    end
    if not args[2] or not tonumber(args[2]) then
        return err('Post ID invalid or not specified')
    end
    if not args[4] or not valid_date(args[4]) then
        return err('Date invalid or not specified')
    end
    return table.concat({
        '\'\'',
        tags.replace(args[3]),
        '\'\' - [http://',
        args[1],
        '.tumblr.com/post/',
        args[2],
        ' ',
        args[1],
        ' on Tumblr,] ',
        Date(args[4]):fmt('%B %d, %Y.')
    })
end

--- Handles citations of the game's code.
--  Code citations receive arguments in this order:
--  * GML script name
--  * (Optional) Starting line number
--  * (Optional) Ending line number
--  @function           f.code
--  @param              {table} args Citation type arguments
--  @return             {string} Citation text
--  @see                https://github.com/KockaAdmiralac/deltarune-viewer
function f.code(args)
    if not args[1] then
        return err('Script name not specified')
    end
    if (args[2] and not tonumber(args[2])) or (args[3] and not tonumber(args[3])) then
        return err('Line number is not a number')
    end
    local str = {
        args[1],
        ' script'
    }
    if args[2] then
        table.insert(str, ', line')
        if args[3] then
            table.insert(str, 's ')
            table.insert(str, args[2])
            table.insert(str, '–')
            table.insert(str, args[3])
        else
            table.insert(str, ' ')
            table.insert(str, args[2])
        end
    end
    return table.concat(str)
end

--- Handles citations from news sources.
--  News citations receive arguments in this order:
--  * Excerpt from the news
--  * News post title
--  * News site name
--  * News post URL
--  * News post date
--  * (Optional) First name of the author
--  * (Optional) Last name of the author
--  @function           f.news
--  @param              {table} args Citation type arguments
--  @return             {string} Citation text
function f.news(args)
    if not args[1] then
        return err('Relevant news post excerpt not specified')
    end
    if not args[2] then
        return err('News post title not specified')
    end
    if not args[3] then
        return err('News site name not specified')
    end
    if not args[4] then
        return err('News post URL not specified')
    end
    if not args[5] or not valid_date(args[5]) then
        return err('News post date invalid or not specified')
    end
    local author = {}
    if args[6] then
        if args[7] then
            table.insert(author, args[7])
            table.insert(author, ', ')
        end
        table.insert(author, args[6])
        table.insert(author, ', ')
    end
    return table.concat({
        '\'\'',
        args[1],
        '\'\' - [',
        args[4],
        ' ',
        args[2],
        '] (',
        table.concat(author),
        Date(args[5]):fmt('%B %d, %Y.'),
        ') \'\'',
        args[3],
        '\'\'.'
    })
end

-- Package items.

--- Template entrypoint for [[Template:Cite]].
--  @function           p.main
--  @param              {table} frame Scribunto frame object
--  @return             {string} Citation text
function p.main(frame)
    local args = frame:getParent().args
    local t = args[1]
    if t then
        if f[t] then
            local nargs = {}
            for i, v in ipairs(args) do
                if i > 1 then
                    nargs[i - 1] = v
                end
            end
            return f[t](nargs);
        else
            return err('Invalid citation type specified')
        end
    else
        -- {{cite}} was used
        local str = '<sup>&#91;[[Project:Manual of Style#Citation needed|<span title="This statement needs proper citation.">citation needed</span>]]&#93;</sup>'
        if title.namespace == 0 or title.namespace == 14 then
            return str .. '[[Category:Articles lacking sources]]'
        else
            return str
        end
    end
end

return p