Module:Infobox television season name

require('strict')

local match = require("Module:String")._match

local p = {}

--- Returns a formatted link to the list of episodes article.

--- @param listOfEpisodesArticle string

--- @return string

local function getListOfEpisodesLink(listOfEpisodesArticle)

local listOfEpisodesPage = mw.title.new(listOfEpisodesArticle, 0)

if listOfEpisodesPage and listOfEpisodesPage.exists and listOfEpisodesPage.redirectTarget ~= mw.title.getCurrentTitle() then

return string.format("List of episodes", listOfEpisodesArticle)

end

end

--- Returns an article link.

--- @param article string The article's title.

--- @param pipedLink string The piped link.

--- @return string

local function getArticleLink(article, pipedLink)

if not pipedLink or pipedLink == "" then

return "" .. article .. ""

end

return "" .. pipedLink .. ""

end

--- Returns the show name and season number from a title.

--- @param showName string The show's title.

--- @return nil | number | string, nil | number | string

local function getShowNameAndSeasonNumberFromShowName(showName)

local _, _, showNameModified, seasonNumber = string.find(showName, "(.*)%s+(%d+)$")

return showNameModified, seasonNumber

end

--- Returns the current season number from the disambiguation.

--- @param disambiguation string The article's disambiguation.

--- @return string

local function getCurrentSeasonNumberFromDisambiguation(disambiguation)

return match(disambiguation , "%d+", 1, -1, false, "")

end

--- Returns the type of word used for "season" in the title.

---

--- The returned value can be one of three options: "season", "series", "story arc" or "specials".

--- @param title string The article's title.

--- @return string

local function getSeasonType(title)

for _, seasonType in pairs({"season", "series", "story arc", "specials"}) do

if string.find(title, seasonType) then

return seasonType

end

end

return "season"

end

--- Returns the season number from the title.

--- @param title string The article's title.

--- @return string | nil

local function getSeasonNumber(title)

return match(title , "%d+", 1, -1, false, "")

end

--- Returns the disambiguation from the title.

--- @param title string The article's title.

--- @return string | nil

local function getDisambiguation(title)

local disambiguation = match(title, "%s%((.-)%)", 1, -1, false, "")

if disambiguation and disambiguation == "" then

return nil

end

return disambiguation

end

--- Returns the title without its disambiguation.

--- @param title string The article's title.

--- @return string | nil

local function getTitleWithoutDisambiguation(title)

local disambiguation = getDisambiguation(title)

if disambiguation then

return string.gsub(title, "%(" .. disambiguation .. "%)", "")

end

return title

end

--- Returns the TV program's disambiguation.

--- @param disambiguation string The disambiguation used in the season's article title.

--- @return string

local function getTVProgramDisambiguation(disambiguation)

if not disambiguation then

return ""

end

-- Check if the disambiguation is normal 'season #' or 'series #'.

-- If so, remove disambiguation.

if string.match(disambiguation, "^season %d*$") or string.match(disambiguation, "^series %d*$") then

return ""

end

local disambiguationStyle = " (%s)"

-- Check if the disambiguation is extended and has 'TV series' and isn't just season #.

-- Only leave the TV series disambiguation, not including the season #.

-- Example: Teenage Mutant Ninja Turtles (1987 TV series, season 5) will return '1987 TV series'.

if string.find(disambiguation, "TV series") then

local shortDisambiguation, _ = disambiguation:match("^(.*),")

if shortDisambiguation then

return string.format(disambiguationStyle, shortDisambiguation)

end

end

-- Check if the disambiguation is extended with country adjective.

-- Example: The Office (American season 2) will return "American season 2".

-- Keep only country adjective.

local countryDisambiguation = disambiguation:match("^(.*) season %d*") or disambiguation:match("^(.*) series %d*")

local data = mw.loadData("Module:Country adjective")

local valid_result = data.getCountryFromAdj[countryDisambiguation]

-- Check if the country adjective is valid.

if valid_result then

-- Add 'TV series' suffix

return string.format(disambiguationStyle, countryDisambiguation .. " TV series")

end

-- Not a known disambiguation style. Use whatever was used in the title or manually added.

-- Note: might not be a valid style link.

return string.format(disambiguationStyle, disambiguation)

end

--- Returns the show's name from the title.

--- @param title string The article's title.

--- @return string

local function getShowName(title)

local name, _ = mw.ustring.gsub(title, "season %d*$", "")

name, _ = mw.ustring.gsub(name, "series %d*$", "")

name, _ = mw.ustring.gsub(name, "specials", "")

name, _ = mw.ustring.gsub(name, "story arc %d*$", "")

name = string.match(name, "^%s*(.-)%s*$") -- Trim spaces.

return name

end

--- Returns "true" if the given link is valid; nil otherwise.

--- A link is valid in the following cases:

--- -- A season article exists.

--- -- A redirect exists to a season section.

---

--- A link is invalid in the following cases:

--- -- A season article or redirect do not exist.

--- -- A redirect exists, but it is a general redirect and not for any specific season section.

---

--- Note: Return values are not booleans as the returned value is used in template space.

--- @param title string The article's title.

--- @return string | nil

local function isLinkValid(title)

local article = mw.title.new(title)

-- Article or redirect do not exist; Not a valid link.

if not article or not article.exists then

return nil

end

local redirectTarget = article.redirectTarget

-- Article exists and is not a redirect; Valid link.

if not redirectTarget then

return "true"

end

local fullLink = redirectTarget.fullText

local isSection = fullLink:find("#")

-- Article is a section redirect; Valid link.

if isSection then

return "true"

end

-- Article is a general redirect; Not a valid link.

return nil

end

--- Returns a season article title and a piped link.

---

--- The following are the supported season naming styles:

--- -- Style:

--- Example: Lost season 2.

--- Example: Doctor Who series 2.

--- -- Style: ( TV series)

--- Example: The Office (American TV series) season 2.

--- Example: Teenage Mutant Ninja Turtles (1987 TV series) season 2

--- Example: X Factor (British TV series) series 2.

--- Example: Love Island (British TV series) series 2

--- -- Style: ( TV series)

--- Example: Love Island (2015 TV series) series 2

--- -- Style: ( )

--- Example: Big Brother 2 (American season).

--- @param title string The article's title.

--- @param seasonNumberDiff number The number difference between the current season and the other season.

--- @return string, string

local function getArticleTitleAndPipedLink(title, seasonNumberDiff)

local seasonType = getSeasonType(title)

local currentSeasonNumber = getSeasonNumber(title)

if tonumber(currentSeasonNumber) == nil then

return "", nil

end

local seasonNumber = currentSeasonNumber + seasonNumberDiff

local modifiedTitle, numberOfReplacements = string.gsub(title, "%d+$", seasonNumber)

local pipedLink = seasonType:gsub("^%l", string.upper) .. " " .. seasonNumber

local disambiguation = getDisambiguation(title)

-- Titles such as "Big Brother 2 (American season) and Teenage Mutant Ninja Turtles (1987 TV series) season 2".

if disambiguation then

local titleWithoutDisambiguation = string.gsub(title, disambiguation, "_DAB_")

modifiedTitle, numberOfReplacements = string.gsub(titleWithoutDisambiguation, "%d+", seasonNumber)

-- Articles, such as "Hawaii Five-0 (2010 TV series) season 2", that have a number

-- as part of their title will need an additional fix in order for that number not to change.

if numberOfReplacements > 1 then

local titleFix = string.match(title, "%d+", 1)

modifiedTitle = string.gsub(modifiedTitle, "%d+", titleFix, 1)

end

modifiedTitle = string.gsub(modifiedTitle, "_DAB_", disambiguation)

return modifiedTitle, pipedLink

-- Titles such as "Big Brother Brasil 2".

elseif not string.find(title, seasonType) then

return modifiedTitle, nil

-- Invalid usages of TV series articles with the television season infobox.

elseif disambiguation and string.find(disambiguation, "TV series") and not (string.find(disambiguation, ", season") or string.find(disambiguation, ", series")) then

return "", nil

-- Standard titles such as "Lost season 1".

else

return modifiedTitle, pipedLink

end

end

--- Returns the article's title either from args (usually from /testcases) or from the page itself.

--- @param frame table The frame invoking the module.

--- @return string

local function getTitle(frame)

local getArgs = require("Module:Arguments").getArgs

local args = getArgs(frame)

if args.italic_title then

return "no"

end

local title = args.title

if not title then

title = mw.title.getCurrentTitle().text

end

return title

end

--- Returns "true" if the given season link is valid; nil otherwise.

--- @param frame table The frame invoking the module.

--- @param seasonNumberDiff number The number difference between the current season and the other season.

--- @return string | nil

local function isSeasonLinkValid(frame, seasonNumberDiff)

local title = getTitle(frame)

local articleTitle, _ = getArticleTitleAndPipedLink(title, seasonNumberDiff)

return isLinkValid(articleTitle)

end

--- Returns a season article link.

--- @param frame table The frame invoking the module.

--- @param seasonNumberDiff number The number difference between the current season and the other season.

--- @return string

local function getSeasonArticleLink(frame, seasonNumberDiff)

local title = getTitle(frame)

local articleTitle, pipedLink = getArticleTitleAndPipedLink(title, seasonNumberDiff)

return getArticleLink(articleTitle, pipedLink)

end

--- Returns "true" if the season link for the next season is valid; nil otherwise.

--- @param frame table The frame invoking the module.

--- @return string | nil

function p.isNextSeasonLinkValid(frame)

return isSeasonLinkValid(frame, 1)

end

--- Returns "true" if the season link for the previous season is valid; nil otherwise.

--- @param frame table The frame invoking the module.

--- @return string | nil

function p.isPrevSeasonLinkValid(frame)

return isSeasonLinkValid(frame, -1)

end

--- Returns "true" if the season link for the previous or next season is valid; nil otherwise.

--- @param frame table The frame invoking the module.

--- @return string | nil

function p.isPrevOrNextSeasonLinkValid(frame)

if p.isPrevSeasonLinkValid(frame) == "true" then

return "true"

end

return p.isNextSeasonLinkValid(frame)

end

--- Returns the next season article title.

--- @param frame table The frame invoking the module.

--- @return string

function p.getNextSeasonArticle(frame)

return getSeasonArticleLink(frame, 1)

end

--- Returns the previous season article title.

--- @param frame table The frame invoking the module.

--- @return string

function p.getPrevSeasonArticle(frame)

return getSeasonArticleLink(frame, -1)

end

--- Returns the type of season word used - "season" or "series".

--- @param frame table The frame invoking the module.

--- @return string

function p.getSeasonWord(frame)

local title = getTitle(frame)

title = getTitleWithoutDisambiguation(title)

local seasonType = getSeasonType(title)

return seasonType

end

--- Returns an {{Italic title}} instance if title qualifies or a blank string.

--- @param frame table

--- @return string

function p.getItalicTitle(frame)

local getArgs = require("Module:Arguments").getArgs

local args = getArgs(frame)

-- If italic_title is set then "no" is the only valid value.

-- Don't set an italic title.

if args.italic_title then

return ""

end

local title = getTitle(frame)

title = getShowName(getTitleWithoutDisambiguation(title))

-- If the infobox is used on List of articles don't set an italic title.

-- TODO: this can be fixed in the future but current usages use a manual display title.

if string.find(title, "List of") then

return ""

end

return frame:expandTemplate{title = "Italic title", args = {string = title}}

end

--- Returns the text used for the |above= field of the infobox.

---

--- @param frame table

--- @return string

function p.getAboveTitle(frame)

local getArgs = require("Module:Arguments").getArgs

local args = getArgs(frame)

local title = getTitle(frame)

title = getShowName(getTitleWithoutDisambiguation(title))

return title

end

--- Returns the text used for the |subheader= field of the infobox.

---

--- The text is returned in the format of "Season #" or "Series #",

--- depending on either what the article disambiguation uses, or on the manually entered parameters of the infobox.

--- @param frame table The frame invoking the module.

--- @return string | nil

function p.getSubHeader(frame)

local getArgs = require("Module:Arguments").getArgs

local args = getArgs(frame)

local seasonType

local seasonNumber

if args.season_number then

seasonType = "Season"

seasonNumber = args.season_number

elseif args.series_number then

seasonType = "Series"

seasonNumber = args.series_number

end

if not seasonNumber then

local title = getTitle(frame)

local titleWithoutDisambiguation = getTitleWithoutDisambiguation(title)

seasonNumber = getSeasonNumber(titleWithoutDisambiguation)

seasonType = getSeasonType(titleWithoutDisambiguation)

-- For pages like "Doctor Who specials (2008–2010)".

if seasonType == "specials" then

local disambiguation = getDisambiguation(title) or ""

return disambiguation .. " " .. seasonType

end

seasonType = seasonType:sub(1, 1):upper() .. seasonType:sub(2)

end

if seasonNumber and seasonNumber ~= "" then

return seasonType .. " " .. seasonNumber

end

return nil

end

--- Returns a formatted link to the list of episodes article.

---

--- The returned link is in the style of:

--- [List of episodes |List of episodes]

---

--- The link will only return if the page exists.

--- @param frame table The frame invoking the module.

--- @return string | nil

function p.getListOfEpisodes(frame)

local getArgs = require("Module:Arguments").getArgs

local args = getArgs(frame)

if args.link then

-- Parameter should be unformatted.

if string.find(args.link, "%[") then

local delink = require("Module:Delink")._delink

args.link = delink({args.link, wikilinks = "target"})

end

return getListOfEpisodesLink(args.link)

end

local title = getTitle(frame)

local showName = getShowName(getTitleWithoutDisambiguation(title))

if showName then

local disambiguation = getDisambiguation(title)

if disambiguation then

disambiguation = " (" .. disambiguation .. ")"

end

local listOfEpisodesArticle = string.format("List of %s%s episodes", showName, disambiguation or "")

return getListOfEpisodesLink(listOfEpisodesArticle)

end

end

return p