Module:Chapter
Module:Chapter is used for looking up and formatting information on manga chapters.
It is implemented by Template:Chapter and Template:Images by chapter category.
Table data
The module returns a table, referred to as a Chapter
table in this documentation. It has the following attributes:
- pageName
- The chapter's page name on this site
- seriesLink
- The chapter's series' page name on this site
- seriesName
- The name of the chapter's series
- subseries
- The name of the chapter's subseries (Yu-Gi-Oh! Duelist or Yu-Gi-Oh! Millennium World for relevant chapters of the original Yu-Gi-Oh!)
- arcLink
- The page name of the chapter's arc, if applicable
- arcName
- The name of the chapter's arc, if applicable
- number
- The chapter's number in the series
- subseriesNumber
- The chapter's number in the subseries, if applicable
- chapterWord
- The word used to describe chapters in the series e.g. "Duel" for Yu-Gi-Oh!, "Rank" for Yu-Gi-Oh! ZEXAL, etc. Special chapters may have their own chapter word.
- isSpecial
- If the chapter is a special chapter (one that doesn't follow the standard numbering)
- name
- The English name of the chapter
Table methods
- Chapter:new(args)
- Look up chapter information for the supplied arguments and return them as an instance of the
Chapter
table. - Arguments may be the series name and chapter number e.g.
{ series = 'Yu-Gi-Oh! GX', number = 5 }
or the page name e.g.{ page = 'Summon the Dark Ruler!!' }
- Chapter:formatAsName()
- Format the instance of the Chapter table as its English name. The name will link to the chapter's page and be enclosed in quotes.
- Chapter:formatAsNumber()
- Format the instance of the Chapter table as its chapter number, if it has one. The number will link to the chapter's page.
- Chapter:formatAsRef()
- Format the instance of the Chapter table as its name in reference format.
- Chapter:getImageCategoryName()
- Get the name that should be used for the category containing images for the chapter
- Chapter:formatImageCategoryNavLink
- Format the instance of the Chapter tale as a link to its image category as the link should be formatted in previous/next links in such a category's navigation menu
- Chapter:getPrev(prev)
- Get the previous chapter in the series as an instance of the
Chapter
table - If the
prev
param is unused, this will automatically get the previous numbered chapter in the series. - If
prev
is a number, this will look up details for the chapter in the same series of that number. - If
prev
is used and not a number, this will look up details for the chapter whose page name matches the value. - Chapter:getNext(next)
- Get the next chapter in the series as an instance of the
Chapter
table - If the
next
param is unused, this will automatically get the next numbered chapter in the series. - If
next
is a number, this will look up details for the chapter in the same series of that number. - If
next
is used and not a number, this will look up details for the chapter whose page name matches the value.
Template methods
These methods are meant to be invoked by templates whose parameters will be passed into them.
- Chapter.chapter(frame)
- Render a link to a chapter, with various formatting options. See Template:Chapter for more details.
- Chapter.imageCategory(frame)
- Render the page content for a category for images from a given chapter. See Template:Images by chapter category for more details.
local Chapter = {
pageName = nil,
seriesLink = nil,
seriesName = nil,
subseries = nil,
number = nil,
subseriesNumber = nil,
chapterWord = 'Chapter',
isSpecial = false,
name = nil,
}
local DUELIST_OFFSET = 59
local MW_OFFSET = 278
local TRANSCEND_GAME_OFFSET = 343
local seriesData = {
['Yu-Gi-Oh!'] = { link = 'Yu-Gi-Oh! (manga)', chapterWord = 'Duel' },
['Yu-Gi-Oh! R'] = { link = 'Yu-Gi-Oh! R', chapterWord = 'Duel Round' },
['Yu-Gi-Oh! GX'] = { link = 'Yu-Gi-Oh! GX (manga)', chapterWord = 'Chapter' },
['Yu-Gi-Oh! 5D\'s'] = { link = 'Yu-Gi-Oh! 5D\'s (manga)', chapterWord = 'Ride' },
['Yu-Gi-Oh! ZEXAL'] = { link = 'Yu-Gi-Oh! ZEXAL (manga)', chapterWord = 'Rank' },
['Yu-Gi-Oh! D Team ZEXAL'] = { link = 'Yu-Gi-Oh! D-Team ZEXAL', chapterWord = 'Chapter' },
['Yu-Gi-Oh! ARC-V'] = { link = 'Yu-Gi-Oh! ARC-V (manga)', chapterWord = 'Scale' },
['Yu-Gi-Oh! ARC-V The Strongest Duelist Yuya!!'] = { link = 'Yu-Gi-Oh! ARC-V The Strongest Duelist Yuya!!', chapterWord = 'Chapter' },
['Yu-Gi-Oh! SEVENS Luke! Explosive Supremacy Legend!!'] = { link = 'Yu-Gi-Oh! SEVENS Luke! Explosive Supremacy Legend!!', chapterWord = 'Supremacy' },
['Yu-Gi-Oh! Rush Duel LP'] = { link = 'Yu-Gi-Oh! Rush Duel LP', chapterWord = 'Broadcast' },
['Yu-Gi-Oh! OCG Structures'] = { link = 'Yu-Gi-Oh! OCG Structures', chapterWord = 'Chapter' },
['Nururin Charisma! GO! GO! Gokibore!!'] = { link = 'Nururin Charisma! GO! GO! Gokibore!!', chapterWord = 'Chapter' },
['Yu-Gi-Oh! GO RUSH!!'] = { link = 'Yu-Gi-Oh! GO RUSH!! (manga)', chapterWord = 'Rush' },
['Yu-Gi-Oh! OCG Stories'] = { link = 'Yu-Gi-Oh! OCG Stories', chapterWord = 'Chapter' },
}
-- Create a new instance of a chapter object
-- @param args table (include either [`series` and `number`] OR [`page`] to look up the chapter)
-- @return Chapter
function Chapter:new(args)
args = args or {}
-- Lookup information on the chapter
local chapterData = self.lookup(args)
-- Create a new instance of a chapter object
local c = mw.clone(Chapter)
-- If nothing was found, return the empty object
-- @todo: fill in some defaults
if (not chapterData.pageName) then
return c
end
-- Get data on the series
local seriesData = seriesData[chapterData.series] or {}
-- Fill in the chapter data
c.pageName = chapterData.pageName
c.name = chapterData.name
c.seriesName = chapterData.series
c.seriesLink = seriesData.link or chapterData.series
c.chapterWord = chapterData.chapterWord or seriesData.chapterWord or 'Chapter'
c.number = chapterData.number
c.isSpecial = not chapterData.number
c:setSubseriesData()
return c
end
-- Lookup data for a chapter
-- @param args table (include either [`series` and `number`] OR [`page`] to look up the chapter)
-- @return table
function Chapter.lookup(args)
local query = ''
if args.page then
query = query .. '[[' .. args.page .. ']]'
end
if args.series then
query = query .. '[[Chapter series::' .. args.series .. ']]'
end
if args.number then
query = query .. '[[Chapter number::' .. args.number .. ']]'
end
local result = mw.smw.ask{
query,
'? = pageName#',
'?Chapter series = series',
'?Chapter number = number',
'?Chapter word# = chapterWord',
'?English name = name',
'?Release date# = releaseDate',
'?Release date = releaseDateFormatted',
}
-- Get the first result. If nothing is found, use an empty table.
result = result and result[1] or {}
-- If the page name was passed into the template, make sure it gets used
-- So if it's a redirect, it gets tracked in Special:WhatLinksHere
if (args.page) then
result.pageName = args.page
end
return result
end
-- Fill in the Yu-Gi-Oh! Duelist or Yu-Gi-Oh! Millennium World information
-- if applicable for Yu-Gi-Oh! chapters
function Chapter:setSubseriesData()
-- Exit early if the series isn't Yu-Gi-Oh!
if self.seriesName ~= 'Yu-Gi-Oh!' then return end
-- If the chapter is in the Duelist range
-- Set the subseries to "Yu-Gi-Oh! Duelist" and calculate its number
if (self.number and self.number > DUELIST_OFFSET and self.number <= MW_OFFSET) then
self.subseries = 'Yu-Gi-Oh! Duelist'
self.subseriesNumber = self.number - DUELIST_OFFSET
end
-- If the chapter is in the Millennium World range
-- Set the subseries to "Yu-Gi-Oh! Millennium World" and calculate its number
if (self.number and self.number > MW_OFFSET and self.number <= TRANSCEND_GAME_OFFSET) then
self.subseries = 'Yu-Gi-Oh! Millennium World'
self.subseriesNumber = self.number - MW_OFFSET
end
end
-- Normalize input
-- @param table args
-- @return args
function normalizeArgs(args)
-- If a parameter contains an empty string, ignore it
local normalizedArgs = {}
for param, value in pairs(args) do
if (value and mw.text.trim(value) ~= '') then
normalizedArgs[param] = value
end
end
-- Relabel the unnamed params
if (normalizedArgs[1]) then normalizedArgs.series = normalizedArgs[1] end
if (normalizedArgs[2]) then normalizedArgs.number = normalizedArgs[2] end
if (normalizedArgs[3]) then normalizedArgs.format = normalizedArgs[3] end
local series = normalizedArgs.series
-- If the series does not exist unless a 'Yu-Gi-Oh!' prefix is added, add the prefix
if (series and not seriesData[series] and seriesData['Yu-Gi-Oh! ' .. series]) then
normalizedArgs.series = 'Yu-Gi-Oh! ' .. series
end
-- If the supplied series is 'Yu-Gi-Oh! Duelist',
-- change to 'Yu-Gi-Oh!' and use overall series numbering
if (normalizedArgs.series and normalizedArgs.series == 'Yu-Gi-Oh! Duelist') then
normalizedArgs.series = 'Yu-Gi-Oh!'
normalizedArgs.number = normalizedArgs.number and (normalizedArgs.number + DUELIST_OFFSET) or nil
end
-- If the supplied series is 'Yu-Gi-Oh! Millennium World',
-- change to 'Yu-Gi-Oh!' and use overall series numbering
if (normalizedArgs.series and normalizedArgs.series == 'Yu-Gi-Oh! Millennium World') then
normalizedArgs.series = 'Yu-Gi-Oh!'
normalizedArgs.number = normalizedArgs.number and (normalizedArgs.number + MW_OFFSET) or nil
end
return normalizedArgs
end
-- Check if the input is okay
-- @param args table - The input from the template parameters
-- @return table - An array of error messages
function validateArgs(args)
args = args or {}
local errors = {}
if (not args.page and (not args.series or not args.number)) then
table.insert(errors, 'Either a page name or both a series and number must be specified.')
end
if (args.number and tonumber(args.number) == nil) then
table.insert(errors, 'Number (<code>' .. args.number .. '</code>) must be numeric.')
end
if (args.format and args.format ~= 'ref' and args.format ~= 'number') then
table.insert(errors, 'Format (<code>' .. args.format .. '</code>) was not recognized.')
end
return errors
end
-- Render the chapter as its name, linked and quoted
-- @return string
function Chapter:formatAsName()
return '"[[' .. self.pageName .. '|' .. (self.name or self.pageName) .. ']]"'
end
-- Render the chapter as its number, linked
-- @return string
function Chapter:formatAsNumber()
return '[[' .. self.pageName .. '|' .. (self.number or self.pageName) .. ']]'
end
-- Render the chapter in reference format
-- @return string
function Chapter:formatAsRef()
local args = args or {}
local output = ''
if (self.seriesName) then
output = output .. '<i>' .. self.seriesName .. '</i>'
end
if (self.chapterWord) then
output = output .. ' ' .. self.chapterWord
end
if self.number then
output = output .. ' ' .. self.number
end
if self.subseries then
output = output .. ' (<i>' .. self.subseries .. '</i> ' .. self.chapterWord .. ' ' .. self.subseriesNumber .. ')'
end
output = output .. ': ' .. self:formatAsName()
return mw.text.trim(output)
end
-- Get the name of the category containing images for this chapter
-- @return string
function Chapter:getImageCategoryName()
local output = ''
if (self.seriesName) then output = output .. self.seriesName end
if (self.chapterWord) then output = output .. ' ' .. self.chapterWord end
if (self.number) then output = output .. ' ' .. self.number end
output = output .. ' images'
return output
end
-- Get the link to the image category as it is formatted in a navigation menu
-- @return string
function Chapter:formatImageCategoryNavLink()
local output = ''
if (self.chapterWord) then output = output .. self.chapterWord end
if (self.number) then output = output .. ' ' .. self.number end
output = output .. ': "[[:Category:' .. self:getImageCategoryName() .. '|' .. self.name .. ']]"'
return output
end
-- Get the previous chapter
-- @param prev {string|number|nil}
-- The page name or chapter number of the previous chapter
-- Leave blank to automatically find the previous chapter
-- @return Chapter
function Chapter:getPrev(prev)
-- If the first chapter, there is no previous one
if self.number == 1 then return {} end
-- If a non-numeric `prev` is supplied, find the chapter based on that page name.
if (prev and not tonumber(prev)) then
return Chapter:new({ page = prev })
end
-- If a numeric `prev` is supplied, find the chapter based on that number.
if (prev and tonumber(prev)) then
return Chapter:new({ series = self.seriesName, number = tonumber(prev) })
end
-- Automatically find the previous chapter, by subtracting 1 from the current number
if (self.number) then
return Chapter:new({ series = self.seriesName, number = self.number - 1 })
end
return nil
end
-- Get the next chapter
-- @param next {string|number|nil}
-- The page name or chapter number of the next chapter
-- Leave blank to automatically find the next chapter
-- @return Chapter
function Chapter:getNext(next)
-- If a non-numeric `next` is supplied, find the chapter based on that page name.
if (next and not tonumber(next)) then
return Chapter:new({ page = next })
end
-- If a numeric `next` is supplied, find the chapter based on that number.
if (next and tonumber(next)) then
return Chapter:new({ series = self.seriesName, number = tonumber(next) })
end
-- Find the next chapter, by adding 1 to the current number
if (self.number) then
return Chapter:new({ series = self.seriesName, number = self.number + 1 })
end
return nil
end
-- Function callable by templates for getting a chapter, formatted in various ways
-- @param frame
-- @return string
function Chapter.chapter(frame)
-- Get the template parameters and format them
local args = normalizeArgs(frame:getParent().args)
-- Show error if there is a problem with the input.
local errors = validateArgs(args)
if (#errors > 0) then
return renderErrors('chapter', errors)
end
-- Find the chapter data
local c = Chapter:new(args)
-- Show error if the chapter was not found.
if (not c.pageName) then
return renderErrors('chapter', { 'chapter not found' })
end
-- Return the output
if (args.format and args.format == 'ref') then return c:formatAsRef() end
if (args.format and args.format == 'number') then return c:formatAsNumber() end
return c:formatAsName()
end
function renderErrors(templateName, errors)
local output = '<span style="color: #d33;">Error rendering <code>{{' .. templateName .. '}}</code>'
.. '<abbr title="' .. (table.concat(errors, ' ')) .. '">🛈</abbr>'
.. '</span>'
if (mw.title.getCurrentTitle().nsText ~= 'Template') then
output = output ..'[[Category:Pages with validation errors]]'
end
return output
end
-- Function callable by templates for getting a chapter and rendering the
-- wikitext for an images category for the chapter
--
-- @param frame
-- @return string
function Chapter.imageCategory(frame)
-- Get the template parameters and normarize them
local args = normalizeArgs(frame:getParent().args)
-- Look up the current, previous and next chapters
local curr = Chapter:new(args)
if (not curr or not curr.pageName) then
return '<div style="color: #d33;">Could not find chapter</div>' ..
'[[Category:Pages with validation errors]]'
end
local prev = curr:getPrev(args.prev)
local next = curr:getNext(args.next)
-- Intro text
local output = 'This category is for images from ' .. curr:formatAsRef() .. '.'
-- Navigation
output = output .. '<div class="toccolours" style="clear: both; display: flex; margin-bottom: .5em;">'
if (prev and prev.pageName) then
local prevText = prev:formatImageCategoryNavLink()
output = output .. '<div style="flex: 1; text-align: left;">←' .. prevText .. '</div>'
end
if (next and next.pageName) then
local nextText = next:formatImageCategoryNavLink()
output = output .. '<div style="flex: 1; text-align: right;">' .. nextText .. '→</div>'
end
output = output .. '</div>'
-- Category
output = output .. '[[Category:' .. curr.seriesName .. ' images by chapter|' .. (curr.number or curr.chapterWord) .. ']]'
return output
end
return Chapter