Module:Quotation
Documentation for this module may be created at Module:Quotation/doc
--------------------------------------------------------------------------------
-- Module:Quotation
-- Unified quotation system for blockquotes, pull quotes, and poetry.
--
-- INSTALLATION:
-- 1. Create page: Module:Quotation
-- 2. Paste this entire file
-- 3. Create TemplateStyles page: Module:Quotation/styles.css
--
-- USAGE:
-- {{#invoke:Quotation|blockquote|text=Quote here|author=Name}}
-- {{#invoke:Quotation|quotebox|quote=Quote here|author=Name|align=right}}
-- {{#invoke:Quotation|cquote|text=Quote here}}
-- {{#invoke:Quotation|poem|text=Poetry here}}
--
-- @author Remilia Wiki
-- @license MIT
--------------------------------------------------------------------------------
local p = {}
--------------------------------------------------------------------------------
-- UTILITY FUNCTIONS
--------------------------------------------------------------------------------
local function trim(s)
if not s then return nil end
return tostring(s):match("^%s*(.-)%s*$")
end
local function isEmpty(s)
if s == nil then return true end
if type(s) == 'string' then
return trim(s) == ''
end
return false
end
local function hasValue(s)
return not isEmpty(s)
end
local function getArg(args, name, default)
local val = args[name]
if isEmpty(val) then return default end
return trim(val)
end
local function getFrameArgs(frame)
local args = {}
local parentArgs = frame:getParent() and frame:getParent().args or {}
local directArgs = frame.args or {}
for k, v in pairs(directArgs) do args[k] = v end
for k, v in pairs(parentArgs) do args[k] = v end
return args
end
--- Build attribution line
local function buildAttribution(author, source, title)
local parts = {}
if hasValue(author) then
table.insert(parts, author)
end
if hasValue(title) then
table.insert(parts, "''" .. title .. "''")
end
if hasValue(source) then
if hasValue(title) then
table.insert(parts, "(" .. source .. ")")
else
table.insert(parts, "''" .. source .. "''")
end
end
if #parts == 0 then
return nil
end
return table.concat(parts, ', ')
end
--------------------------------------------------------------------------------
-- BLOCKQUOTE
--------------------------------------------------------------------------------
function p.blockquote(frame)
local args = getFrameArgs(frame)
local text = getArg(args, 'text') or getArg(args, 1)
if isEmpty(text) then
return '<span class="error">Error: Quote text required</span>'
end
local author = getArg(args, 'author')
local source = getArg(args, 'source')
local title = getArg(args, 'title')
local character = getArg(args, 'character')
-- Build the blockquote
local bq = mw.html.create('blockquote')
:addClass('quote-block')
-- Quote text
bq:tag('div')
:addClass('quote-text')
:wikitext(text)
-- Attribution
local attribution = buildAttribution(author, source, title)
if hasValue(attribution) or hasValue(character) then
local attrDiv = bq:tag('div')
:addClass('quote-attribution')
local attrText = ''
if hasValue(character) then
attrText = character
if hasValue(attribution) then
attrText = attrText .. ' in ' .. attribution
end
else
attrText = attribution
end
attrDiv:wikitext('— ' .. attrText)
end
return tostring(bq)
end
--------------------------------------------------------------------------------
-- QUOTE BOX (Pull Quote)
--------------------------------------------------------------------------------
function p.quotebox(frame)
local args = getFrameArgs(frame)
local quote = getArg(args, 'quote') or getArg(args, 1)
if isEmpty(quote) then
return '<span class="error">Error: Quote text required</span>'
end
local author = getArg(args, 'author')
local source = getArg(args, 'source')
local align = getArg(args, 'align', 'right')
local width = getArg(args, 'width', '30%')
-- Validate alignment
if align ~= 'left' and align ~= 'right' and align ~= 'center' and align ~= 'none' then
align = 'right'
end
-- Build the quote box
local box = mw.html.create('div')
:addClass('quote-box')
:addClass('quote-box-' .. align)
-- Apply width as inline style (CSS can't handle arbitrary values)
if hasValue(width) then
box:css('width', width)
end
-- Quote text
box:tag('div')
:addClass('quote-box-text')
:wikitext(quote)
-- Attribution
local attribution = buildAttribution(author, source)
if hasValue(attribution) then
box:tag('div')
:addClass('quote-box-attribution')
:wikitext('— ' .. attribution)
end
return tostring(box)
end
--------------------------------------------------------------------------------
-- CQUOTE (Centered quote with decorative marks)
--------------------------------------------------------------------------------
function p.cquote(frame)
local args = getFrameArgs(frame)
local text = getArg(args, 'text') or getArg(args, 1)
if isEmpty(text) then
return '<span class="error">Error: Quote text required</span>'
end
local author = getArg(args, 'author')
local source = getArg(args, 'source')
-- Build the centered quote
local container = mw.html.create('div')
:addClass('quote-centered')
-- Opening quote mark
container:tag('div')
:addClass('quote-mark quote-mark-open')
:wikitext('"')
-- Quote text
container:tag('div')
:addClass('quote-centered-text')
:wikitext(text)
-- Closing quote mark
container:tag('div')
:addClass('quote-mark quote-mark-close')
:wikitext('"')
-- Attribution
local attribution = buildAttribution(author, source)
if hasValue(attribution) then
container:tag('div')
:addClass('quote-centered-attribution')
:wikitext('— ' .. attribution)
end
return tostring(container)
end
--------------------------------------------------------------------------------
-- POEM QUOTE
--------------------------------------------------------------------------------
function p.poem(frame)
local args = getFrameArgs(frame)
local text = getArg(args, 'text') or getArg(args, 1)
if isEmpty(text) then
return '<span class="error">Error: Poem text required</span>'
end
local author = getArg(args, 'author')
local source = getArg(args, 'source') or getArg(args, 'title')
local align = getArg(args, 'align', 'left')
-- Build the poem quote
local container = mw.html.create('div')
:addClass('quote-poem')
:addClass('quote-poem-' .. align)
-- Poem text (preserve line breaks)
container:tag('div')
:addClass('quote-poem-text')
:newline()
:wikitext('<poem>' .. text .. '</poem>')
-- Attribution
local attribution = buildAttribution(author, source)
if hasValue(attribution) then
container:tag('div')
:addClass('quote-poem-attribution')
:wikitext('— ' .. attribution)
end
return tostring(container)
end
--------------------------------------------------------------------------------
-- VERSE (Simple verse formatting, no box)
--------------------------------------------------------------------------------
function p.verse(frame)
local args = getFrameArgs(frame)
local text = getArg(args, 'text') or getArg(args, 1)
if isEmpty(text) then
return '<span class="error">Error: Verse text required</span>'
end
local container = mw.html.create('div')
:addClass('quote-verse')
container:wikitext('<poem>' .. text .. '</poem>')
return tostring(container)
end
return p