Module:Franchise

From Zelda Wiki, the Zelda encyclopedia
Jump to navigation Jump to search

Lua interface for Data:Franchise.

This is the main module for the following templates: In addition, this module exports the following functions.

Template functions

Article

{{#invoke:Franchise|Article|<game>|nowarn=}}

Parameters

ParameterStatusDescription
1gamerequiredThe code for a game or other entry in Data:Franchise.
nowarnoptional

If present, the template will not issue an editor warning when game is invalid. Use this when a template needs custom error handling (see examples).

However, an editor warning will still be issued if a game code is written with incorrect casing (e.g. oot instead of OoT)

Examples

#InputResultCategories added
1
{{#invoke:Franchise|Article|OoT}}
The Legend of Zelda: Ocarina of Time
2
{{#invoke:Franchise|Article|OoT (Himekawa)}}
The Legend of Zelda: Ocarina of Time (Himekawa)
Error handling
3
{{#invoke:Franchise|Article|oot}}
The Legend of Zelda: Ocarina of Time
4
{{#invoke:Franchise|Article|notAGame}}
Error: Invalid entry notAGame
5
{{#invoke:Franchise|Article|}}
Error: No game provided
6
{{#iferror:{{#invoke:Franchise|Article|invalid game}}|custom error handling}}
custom error handling

BaseGame

{{#invoke:Franchise|BaseGame|<game>|nowarn=}}

Parameters

ParameterStatusDescription
1gamerequiredThe code for a game entry in Data:Franchise.
nowarnoptional

If present, the template will not issue an editor warning when game is invalid. Use this when a template needs custom error handling (see examples).

However, an editor warning will still be issued if a game code is written with incorrect casing (e.g. oot instead of OoT)

Examples

#InputResultCategories added
7
{{#invoke:Franchise|BaseGame|TWW}}
TWW
8
{{#invoke:Franchise|BaseGame|TWWHD}}
TWW
Error handling
9
{{#invoke:Franchise|BaseGame|notAGame}}
Error: Invalid entry notAGame
10
{{#invoke:Franchise|BaseGame|twwhd}}
TWW
11
{{#invoke:Franchise|BaseGame|}}
Error: No game provided
12
{{#iferror:{{#invoke:Franchise|BaseGame|invalid game}}|custom error handling}}
custom error handling

Display

{{#invoke:Franchise|Display|<game>|nowarn=}}

Parameters

ParameterStatusDescription
1gamerequiredThe code for a game or other Zelda-related title defined at Data:Franchise.
nowarnoptional

If present, the template will not issue an editor warning when game is invalid. Use this when a template needs custom error handling (see examples).

However, an editor warning will still be issued if a game code is written with incorrect casing (e.g. oot instead of OoT)

Examples

#InputResultCategories added
13
{{#invoke:Franchise|Display|LA}}
Link's Awakening
14
{{#invoke:Franchise|Display|LANS}}
Link's Awakening for Nintendo Switch
15
{{#invoke:Franchise|Display|LA (Cagiva)}}
Link's Awakening (Cagiva)
16
{{#invoke:Franchise|Display|invalid game}}
Error: Invalid entry invalid game
17
{{#invoke:Franchise|Display|}}
Error: No game provided

Link

{{#invoke:Franchise|Link|<game>|nowarn=}}

Parameters

ParameterStatusDescription
1gamerequiredThe code for a game or other Zelda-related title defined at Data:Franchise.
nowarnoptional

If present, the template will not issue an editor warning when game is invalid. Use this when a template needs custom error handling (see examples).

However, an editor warning will still be issued if a game code is written with incorrect casing (e.g. oot instead of OoT)

Examples

#InputResultCategories added
18
{{#invoke:Franchise|Link|OoT}}
Ocarina of Time
19
{{#invoke:Franchise|Link|OoT (Himekawa)}}
Ocarina of Time (Himekawa)
Error handling
20
{{#invoke:Franchise|Link|oot}}
Ocarina of Time
21
{{#invoke:Franchise|Link|notAGame}}
Error: Invalid entry notAGame
22
{{#invoke:Franchise|Link|}}
Error: No game provided
23
{{#iferror:{{#invoke:Franchise|Link|invalid game}}|custom error handling}}
custom error handling

ShortName

{{#invoke:Franchise|ShortName|<game>|nowarn=}}

Parameters

ParameterStatusDescription
1gamerequiredThe code for a game or other Zelda-related title defined at Data:Franchise.
nowarnoptional

If present, the template will not issue an editor warning when game is invalid. Use this when a template needs custom error handling (see examples).

However, an editor warning will still be issued if a game code is written with incorrect casing (e.g. oot instead of OoT)

Examples

#InputResultCategories added
24
{{#invoke:Franchise|ShortName|OoT}}
Ocarina of Time
25
{{#invoke:Franchise|ShortName|OoT (Himekawa)}}
Ocarina of Time (Himekawa)
26
{{#invoke:Franchise|ShortName|E}}
Encyclopedia
Error handling
27
{{#invoke:Franchise|ShortName|oot}}
Ocarina of Time
28
{{#invoke:Franchise|ShortName|notAGame}}
Error: Invalid entry notAGame
29
{{#invoke:Franchise|ShortName|}}
Error: No game provided
Custom error handling
30
{{#iferror:{{#invoke:Franchise|ShortName|invalid game}}|[[Dodongo]]|[[Dodongo#{{#invoke:Franchise|ShortName|invalid game}}]]}}
Dodongo
31
{{#iferror:{{#invoke:Franchise|ShortName|oot}}|[[Dodongo]]|[[Dodongo#{{#invoke:Franchise|ShortName|oot}}]]}}
Dodongo#Ocarina of Time
32
{{#iferror:{{#invoke:Franchise|ShortName|OoT}}|[[Dodongo]]|[[Dodongo#{{#invoke:Franchise|ShortName|OoT}}]]}}
Dodongo#Ocarina of Time


Module functions - all media

enum

enum(options)

See also enumGames.

Returns

Examples

#InputOutputStatus
33
local enum = Franchise.enum()
return utilsTable.take(enum, 7)
{
  "TLoZ",
  "TAoL",
  "ALttP",
  "LA",
  "LADX",
  "LANS",
  "OoT",
}
Green check.svg
34
return Franchise.enum().reference
"[[Data:Franchise]]"
Green check.svg
When includeSeries is true, then Series is the first item in the enum.
35
local enum = Franchise.enum({
  includeSeries = true,
})
return utilsTable.take(enum, 3)
{"Series", "TLoZ", "TAoL"}
Green check.svg
When includeNonfiction is true, then books such as Encyclopedia are in the list.
36
local enum = Franchise.enum({
  includeNonfiction = true,
})
return utilsTable.includes(enum, "E")
true
Green check.svg
When includeGroups is true, then collective terms such as A Link to the Past & Four Swords are included in the list.
37
local enum = Franchise.enum({
  includeGroups = true,
})
return utilsTable.includes(enum, "ALttP&FS")
true
Green check.svg

article

article(code)

Returns

  • Wiki article name for the title

Examples

#InputOutputStatus
38
article("LA")
"The Legend of Zelda: Link's Awakening"
Green check.svg
39
article("la")
"The Legend of Zelda: Link's Awakening"
Green check.svg
40
article("LANS")
"The Legend of Zelda: Link's Awakening (Nintendo Switch)"
Green check.svg
41
article("TAoL")
"Zelda II: The Adventure of Link"
Green check.svg
42
article("TLoZ")
"The Legend of Zelda"
Green check.svg
43
article("Series")
"The Legend of Zelda (Series)"
Green check.svg
44
article("E")
"The Legend of Zelda: Encyclopedia"
Green check.svg
45
article("SSB4")
"Super Smash Bros. for Nintendo 3DS / Wii U"
Green check.svg

canonicity

canonicity(code)

Returns

  • A string: canon, ambiguous, or non-canon.

Examples

#InputOutputStatus
46
canonicity("LA")
"canon"
Green check.svg
47
canonicity("la")
"canon"
Green check.svg
48
canonicity("CoH")
"ambiguous"
Green check.svg
49
canonicity("LA (Cagiva)")
"non-canon"
Green check.svg
50
canonicity("E")
"canon"
Green check.svg
51
canonicity("fake")
nil
Green check.svg

canonOrder

canonOrder(code)

Returns

Examples

#InputOutputResult
52
canonOrder("LA")
4
Green check.svg
53
canonOrder("LADX")
5
Green check.svg
54
canonOrder("Not a Game")
nil
Green check.svg
Non-ficition books such as Encyclopedia have no position in the canon order.
55
canonOrder("E")
nil
Green check.svg

code

code(code)

Returns

  • The same code but correctly formatted, or nil if no such code exists

Examples

#InputOutputStatus
56
code("alttp")
"ALttP"
Green check.svg
57
code("fakegame")
nil
Green check.svg

display

display(code)

Returns

  • Formatted text for the title.

Examples

#InputOutputResultStatus
58
display("LA")
"''Link's Awakening''"
Link's Awakening
Green check.svg
59
display("la")
"''Link's Awakening''"
Link's Awakening
Green check.svg
60
display("LANS")
"''Link's Awakening'' for Nintendo Switch"
Link's Awakening for Nintendo Switch
Green check.svg
61
display("E")
"''Encyclopedia''"
Encyclopedia
Green check.svg
62
display("ALttP&FS")
"''A Link to the Past & Four Swords''"
A Link to the Past & Four Swords
Green check.svg
63
display("Series")
"''The Legend of Zelda'' series"
The Legend of Zelda series
Green check.svg
64
display("fakeGame")
nil
Green check.svg

isCanon

isCanon(code)

Returns

  • True if title is canon, else false.

Examples

#InputOutputResult
65
isCanon("LANS")
true
Green check.svg
66
isCanon("lans")
true
Green check.svg
67
isCanon("CoH")
false
Green check.svg
68
isCanon("SSBU")
false
Green check.svg
69
isCanon("E")
true
Green check.svg
70
isCanon("Series")
true
Green check.svg

link

link(code)

Returns

  • Formatted link used in infoboxes and so on.

Examples

#InputOutputResultStatus
71
link("LA")
"''[[The Legend of Zelda: Link's Awakening|Link's Awakening]]''"
Link's Awakening
Green check.svg
72
link("la")
"''[[The Legend of Zelda: Link's Awakening|Link's Awakening]]''"
Link's Awakening
Green check.svg
73
link("LADX")
"''[[The Legend of Zelda: Link's Awakening DX|Link's Awakening DX]]''"
Link's Awakening DX
Green check.svg
74
link("LANS")
"[[The Legend of Zelda: Link's Awakening (Nintendo Switch)|''Link's Awakening'' for Nintendo Switch]]"
Link's Awakening for Nintendo Switch
Green check.svg
For books, comics and manga, see also phraseLink.
75
link("LA (Cagiva)")
"[[The Legend of Zelda: Link's Awakening (Cagiva)|''Link's Awakening'' (Cagiva)]]"
Link's Awakening (Cagiva)
Green check.svg
76
link("E")
"''[[The Legend of Zelda: Encyclopedia|Encyclopedia]]''"
Encyclopedia
Green check.svg
77
link("ALttP&FS")
"''[[The Legend of Zelda: A Link to the Past & Four Swords|A Link to the Past & Four Swords]]''"
A Link to the Past & Four Swords
Green check.svg
78
link("Series")
"[[The Legend of Zelda (Series)|''The Legend of Zelda'' series]]"
The Legend of Zelda series
Green check.svg
79
link("fakeGame")
nil
Green check.svg

logo(code)

Returns

  • Filename for the title's logo.

Examples

#InputOutputResultStatus
80
logo("TWW")
"File:TWW English Logo.png"
File:TWW English Logo.png
Green check.svg
81
logo("tww")
"File:TWW English Logo.png"
File:TWW English Logo.png
Green check.svg
82
logo("E")
"File:The Legend of Zelda Encyclopedia Cover.png"
File:The Legend of Zelda Encyclopedia Cover.png
Green check.svg
83
logo("Series")
"File:Zelda Logo TP.png"
File:Zelda Logo TP.png
Green check.svg
84
logo("fakeGame")
nil
Green check.svg

releaseDate

releaseDate(code)

Returns

  • The "main" release date for a title.

Examples

#InputOutputStatus
85
releaseDate("LA")
"1993-08-06"
Green check.svg
86
releaseDate("LANS")
"2019-09-20"
Green check.svg
87
releaseDate("LA (Cagiva)")
"1994-05-01"
Green check.svg
88
releaseDate("E")
"2018-06-19"
Green check.svg
89
releaseDate("ALttP&FS")
"2002-12-02"
Green check.svg
90
releaseDate("Series")
nil
Green check.svg
91
releaseDate("fakeGame")
nil
Green check.svg

shortName

shortName(code)

Returns

  • Short name for franchise title used in category names. Usually the subtitle.

Examples

#InputOutputResultStatus
92
shortName("LA")
"Link's Awakening"
Link's Awakening
Green check.svg
93
shortName("la")
"Link's Awakening"
Link's Awakening
Green check.svg
94
shortName("LANS")
"Link's Awakening (Nintendo Switch)"
Link's Awakening (Nintendo Switch)
Green check.svg
95
shortName("LA (Cagiva)")
"Link's Awakening (Cagiva)"
Link's Awakening (Cagiva)
Green check.svg
96
shortName("E")
"Encyclopedia"
Encyclopedia
Green check.svg
97
shortName("ALttP&FS")
"A Link to the Past & Four Swords"
A Link to the Past & Four Swords
Green check.svg
98
shortName("Series")
"The Legend of Zelda Series"
The Legend of Zelda Series
Green check.svg
99
shortName("fakeGame")
nil
Green check.svg


Module functions - games

type

type(code)

Returns

  • One of: "main", "remake", "spin-off", "" (other games)

Examples

#InputOutputResultStatus
100
type("BotW")
"main"
main
Green check.svg
101
type("HWAoC")
"spin-off"
spin-off
Green check.svg
102
type("SSBU")
""
Green check.svg

enumGames

enumGames(includeSeries)

Returns

Examples

#InputOutputStatus
103
local enum = Franchise.enumGames()
return utilsTable.take(enum, 3)
{"TLoZ", "TAoL", "ALttP"}
Green check.svg
104
return Franchise.enumGames().reference
"[[Data:Franchise]]"
Green check.svg
When includeSeries is true, then Series is the first item in the enum.
105
local enum = Franchise.enumGames(true)
return utilsTable.take(enum, 3)
{"Series", "TLoZ", "TAoL"}
Green check.svg

baseGame

baseGame(code)

Returns

  • If code is a remake, returns the code for the original game, else returns code as-is.

Examples

#InputOutputStatus
106
baseGame("LANS")
"LA"
Green check.svg
107
baseGame("LA")
"LA"
Green check.svg
108
baseGame("fake")
nil
Green check.svg

family

family(code)

Returns

  • A grouping name used for certain non-canon games on the Main Page.

Examples

#InputOutputStatus
109
family("OoT")
""
Green check.svg
110
family("LCT")
""
Green check.svg
111
family("FPTRR")
"Tingle"
Green check.svg
112
family("HWDE")
"Hyrule Warriors"
Green check.svg

graphics

graphics(code)

Returns

  • A string: 2D or 3D.

Examples

#InputOutputStatus
113
graphics("LA")
"2D"
Green check.svg
114
graphics("la")
"2D"
Green check.svg
115
graphics("LANS")
"3D"
Green check.svg
116
graphics("fake")
nil
Green check.svg

hasRemakes

hasRemakes(code)

Returns

  • True if game has at least one remake, remaster, or enhanced port. Else false.

Examples

#InputOutputResult
117
hasRemakes("LA")
true
Green check.svg
118
hasRemakes("LADX")
true
Green check.svg
119
hasRemakes("ST")
false
Green check.svg
120
hasRemakes("fakeGame")
false
Green check.svg

isRemake

isRemake(code)

Returns

  • True if game is a remake, remaster, or enhanced port. Else false.

Examples

#InputOutputResult
121
isRemake("LANS")
true
Green check.svg
122
isRemake("LADX")
true
Green check.svg
123
isRemake("LA")
false
Green check.svg
124
isRemake("HWDE")
true
Green check.svg

remakes

remakes(code)

Returns

  • List of remakes for a specific game, or a table of all remakes if no game specified

Examples

#InputOutputResult
125
remakes("LA")
{"LADX", "LANS"}
Green check.svg
126
remakes("LADX")
{"LANS"}
Green check.svg
127
remakes("ST")
{}
Green check.svg
128
remakes("fake")
{}
Green check.svg

superseder

superseder(code)

Returns

  • The game code of the latest canon version of the game corresponding to code

Examples

#InputOutputResultStatus
129
superseder("LA")
"LANS"
LANS
Green check.svg

nil is returned the game has no newer canon versions or does not exist in Data:Franchise.

FSAE is not returned for FS as it not considered a canon remake.

130
superseder("FS")
nil
Green check.svg
131
superseder("BotW")
nil
Green check.svg
132
superseder("notACode")
nil
Green check.svg


Module functions - books

publisher

publisher(code)

Returns

  • The book's publisher - in North America if available.

Examples

#InputOutputResultStatus
133
publisher("OoT (Himekawa)")
"VIZ Media"
VIZ Media
Green check.svg
134
publisher("E")
"Dark Horse Books"
Dark Horse Books
Green check.svg

phraseLink

phraseLink(code)

Returns

  • Formatted link to page and authors.

Examples

#InputOutputResultStatus
135
phraseLink("TLoZ (Ran)")
"[[The Legend of Zelda (Ran)|''The Legend of Zelda'' manga]] by [[Maru Ran]]"
The Legend of Zelda manga by Maru Ran
Green check.svg
136
phraseLink("tloz (ran)")
"[[The Legend of Zelda (Ran)|''The Legend of Zelda'' manga]] by [[Maru Ran]]"
The Legend of Zelda manga by Maru Ran
Green check.svg
137
phraseLink("AOV")
"''[[The Legend of Zelda: An Original Version|The Legend of Zelda: An Original Version]]''"
The Legend of Zelda: An Original Version
Green check.svg
138
phraseLink("fake")
nil
Green check.svg
Nonfiction books such as Encyclopedia are not supported by this function.
139
phraseLink("E")
""
Green check.svg

local p = {}
local h = {}

local utilsString = require("Module:UtilsString")
local utilsTable = require("Module:UtilsTable")

local cache = mw.loadData("Module:Franchise/Cache")

local CATEGORY_INVALID_ARGS = "Category:"..require("Module:Constants/category/invalidArgs")

-- Many templates need "Series" as if it were a game. Since it does not fit into the data model of Data:Franchise, it is manually defined here.
local series = {
	article = "The Legend of Zelda (Series)",
	shortName = "The Legend of Zelda Series",
	logo = "File:Zelda Logo TP.png",
	link = "[[The Legend of Zelda (Series)|''The Legend of Zelda'' series]]",
	display = "''The Legend of Zelda'' series",
	canonicity = "canon",
	type = "main",
	code = "Series",
}

function p.Article(frame)
	return h.templateQuery("article", frame)
end
function p.BaseGame(frame)
	return h.templateQuery("baseGame", frame)
end
function p.Display(frame)
	return h.templateQuery("display", frame)
end
function p.Link(frame)
	return h.templateQuery("link", frame)
end
function p.ShortName(frame)
	return h.templateQuery("shortName", frame)
end
function h.templateQuery(fn, frame)
	local args = frame.args
	local game, nowarn = args[1], args.nowarn
	if game == nil or game == "" then
		return "", h.error("No game provided", CATEGORY_INVALID_ARGS, nowarn)
	end
	game = utilsString.trim(game)
	local value = p[fn](game)
	if not value then
		return "", h.error(string.format("Invalid entry <code>%s</code>", game), CATEGORY_INVALID_ARGS, nowarn, "See [[Data:Franchise]] for a list of valid entries.")
	end
	local correctGameCode = p.code(game)
	if game ~= correctGameCode then
		local utilsError = require("Module:UtilsError")
		utilsError.warn(string.format("<code>%s</code> should be written as <code>%s</code>", game, correctGameCode))
		return value, "[["..CATEGORY_INVALID_ARGS.."]]"
	else
		return value
	end
end
function h.error(errorMsg, category, nowarn, additionalWarningInfo)
	local utilsError = require("Module:UtilsError")
	local warnMsg = not nowarn and additionalWarningInfo and (errorMsg.. ". ".. additionalWarningInfo)
	return utilsError.error(errorMsg, warnMsg).."[["..category.."]]"
end

-- [[Guidelines:Main]]
function p.ListTitlesByCanonicity(frame)
	local utilsLayout = require("Module:UtilsLayout")

	local canonicity = frame.args[1]
	local rows = {}
	for i, code in ipairs(p.enum()) do
		if canonicity == p.canonicity(code) then
			local link = p.link(code)
			local releaseDate = p.releaseDate(code)
			table.insert(rows, {link, releaseDate})
		end
	end
	local wikitable = utilsLayout.table({
		sortable = true,
		headers = {"Media", "Release Date"},
		rows = rows,
	})
	return wikitable
end
function p.ListAllTitles(frame)
	local utilsLayout = require("Module:UtilsLayout")
	local canonicityStatuses = {
		["canon"] = "Canon",
		["non-canon"] = "Non-canon",
		["ambiguous"] = "Ambiguously-canon",
	}
	
	local rows = {}
	for i, code in ipairs(p.enum()) do
		local link = p.link(code)
		local releaseDate = p.releaseDate(code)
		local canonicity = p.canonicity(code)
		local canonicityStatus = canonicityStatuses[canonicity]
		local supersededBy = h.get(code, "supersededBy")
		if canonicityStatus and supersededBy ~= nil and supersededBy ~= "" then
			local supersederLink = p.link(supersededBy)
			canonicityStatus = canonicityStatus .. string.format(" <small>(superseded by %s)</small>", supersederLink)
		end
		table.insert(rows, {link, code, releaseDate, canonicityStatus})
	end
	local rows = utilsTable.sortBy(rows, 3) -- sort by release date
	local wikitable = utilsLayout.table({
		sortable = true,
		headers = {"Media", "Abbreviation", "Release Date", "Status"},
		rows = rows,
	})
	return wikitable
end

-- Template:Franchise/Store *
function p.AddToPreview(frame)
	-- Performance optimization
	local utilsArg = require("Module:UtilsArg")
	local utilsVar = require("Module:UtilsVar")
	local utilsMarkup = require("Module:UtilsMarkup")
	local templateData = mw.loadData("Module:Franchise/TemplateData")
	local orderCounter = utilsVar.counter("canonOrder")
	
	local entryType = frame.args[1]
	local args, err = utilsArg.parse(frame:getParent().args, templateData["Franchise/Store " .. entryType])
	if err then
		return utilsMarkup.categories(err.categories)
	end
	args = utilsTable.merge({}, args, {
		entryType = entryType,
		link = p.deriveLink(entryType, args),
		display = p.deriveDisplay(entryType, args),
	})
	if entryType == "Game" or entryType == "Book" or entryType == "TV" then
		args.canonOrder = orderCounter.value()
	else
		args.canonOrder = "—"
	end
	if entryType == "Book" then
		args.phraseLink = p.derivePhraseLink(args)
	end
	utilsVar.add("rows", args)
end
function p.StoreOrder(frame)
	-- Performance optimization
	local utilsVar = require("Module:UtilsVar")
	local orderCounter = utilsVar.counter("canonOrder")
	return orderCounter.increment()
end
function p.StoreLink(frame)
	return p.deriveLink(frame.args[1], frame:getParent().args)
end
function p.StoreDisplay(frame)
	return p.deriveDisplay(frame.args[1], frame:getParent().args)
end
function p.StorePhraseLink(frame)
	return p.derivePhraseLink(frame:getParent().args)
end
function p.deriveDisplay(entryType, args)
	if args.display ~= nil and args.display ~= "" then
		return args.display
	elseif entryType == "Book" then
		return h.deriveBookFields(args).display
	else
		return ("''%s''"):format(args.shortName)
	end
end
function p.deriveLink(entryType, args)
	if args.display ~= nil and args.display ~= "" then
		return args.link
	elseif entryType == "Book" then
		return h.deriveBookFields(args).link
	else
		return ("''[[%s|%s]]''"):format(args.article, args.shortName)
	end
end
function p.derivePhraseLink(args)
	return h.deriveBookFields(args).phraseLink
end

function p.Preview(frame)
	-- Performance optimization
	local utilsMarkup = require("Module:UtilsMarkup")
	local utilsLayout = require("Module:UtilsLayout")
	local utilsVar = require("Module:UtilsVar")
	
	local previewColumns = {
		common = {"canonOrder", "code", "link", "display", "logo", "releaseDate", "canonicity"},
		Game = {"type", "graphics", "family", "remakeOf", "supersededBy"},
		Book = {"type", "phraseLink", "publisher", "authors", "basedOn"},
		Nonfiction = {"publisher", "titles"},
		TV = {"type"},
		Group = {"games"},
	}
	previewColumns.Game = utilsTable.concat(previewColumns.common, previewColumns.Game)
	previewColumns.Book = utilsTable.concat(previewColumns.common, previewColumns.Book)
	previewColumns.TV = utilsTable.concat(previewColumns.common, previewColumns.TV)
	previewColumns.Nonfiction = utilsTable.concat(previewColumns.common, previewColumns.Nonfiction)
	previewColumns.Group = utilsTable.concat(previewColumns.common, previewColumns.Group)
	
	local rows = utilsVar.get("rows")
	for _, row in ipairs(rows) do
		row.logo = utilsMarkup.link(row.logo)
	end
	local rowGroups = utilsTable.groupBy(rows, "entryType")
	local titles = utilsLayout.table({
		sortable = true,
		headers = previewColumns.common,
		rows = utilsTable.map(rows, utilsTable._toArray(previewColumns.common, ""))
	})
	local games = utilsLayout.table({
		sortable = true,
		headers = previewColumns.Game,
		rows = utilsTable.map(rowGroups.Game, utilsTable._toArray(previewColumns.Game, ""))
	})
	local books = utilsLayout.table({
		sortable = true,
		headers = previewColumns.Book,
		rows = utilsTable.map(rowGroups.Book, utilsTable._toArray(previewColumns.Book, ""))
	})
	local tv = utilsLayout.table({
		sortable = true,
		headers = previewColumns.TV,
		rows = utilsTable.map(rowGroups.TV, utilsTable._toArray(previewColumns.TV, ""))
	})
	local nonfiction = utilsLayout.table({
		sortable = true,
		headers = previewColumns.Nonfiction,
		rows = utilsTable.map(rowGroups.Nonfiction or {}, utilsTable._toArray(previewColumns.Nonfiction, ""))
	})
	local groups = utilsLayout.table({
		sortable = true,
		headers = previewColumns.Group,
		rows = utilsTable.map(rowGroups.Group or {}, utilsTable._toArray(previewColumns.Group, ""))
	})
	local preview = utilsLayout.tabs({
		{
			label = "All Titles",
			content = titles,
		},
		{
			label = "Games",
			content = games,
		},
		{
			label = "Books (fiction)",
			content = books,
		},
		{
			label = "Books (nonfiction)",
			content = nonfiction
		},
		{
			label = "Movies and TV Shows",
			content = tv,
		},
		{
			label = "Groups",
			content = groups,
		}
	}, { columns = 15 })
	return preview
end

function p.UploadField(frame)
	-- Performance optimization
	local utilsMarkup = require("Module:UtilsMarkup")
	local utilsVar = require("Module:UtilsVar")
	
	local rows = utilsVar.get("rows")
	local groups = utilsTable.groupBy(rows, "entryType")
	local mainGames, otherGames = utilsTable.partition(groups["Game"], {
		canonicity = "canon"
	})
	local gamesByBaseGame = utilsTable.groupBy(mainGames, function(game)
		return p.baseGame(game.code)
	end)
	local books = groups["Book"]
	local tvShows = groups["TV"]
	
	local sortedMainGames = {}
	for _, mainGame in ipairs(utilsTable.reverse(mainGames)) do
		for _, game in ipairs(gamesByBaseGame[mainGame.code] or {}) do
			table.insert(sortedMainGames, game)
		end
	end
	
	local result = ""
	result = result .. "**|None\n"
	result = result .. "**Series|The Legend of Zelda Series\n"
	
	result = h.append(result, "Main Series", sortedMainGames)
	result = h.append(result, "Other Games", otherGames)
	result = h.append(result, "Books, Comics, and Manga", books)
	result = h.append(result, "TV Shows", tvShows)
	return utilsMarkup.pre(result)
end
function h.append(result, title, entries)
	result = result .. "\n*"..title.."\n"
	for _, entry in ipairs(entries) do
		result = result .. string.format("**%s|%s\n", entry.code, entry.shortName)
	end
	return result
end

function h.deriveBookFields(args)
	local ListPages = require("Module:List Pages")
	local utilsString = require("Module:UtilsString")

	local subtitle, display, link, phraseLink
	local parens = string.find(args.shortName, "%s%([^)]+%)")
	if parens then
		subtitle = string.sub(args.shortName, 1, parens - 1)
		local descriptor = string.sub(args.shortName, parens)
		display = ("''%s''%s"):format(subtitle, descriptor)
		link = ("[[%s|%s]]"):format(args.article, display)
		local authors = ListPages.main(utilsString.split(args.authors))
		phraseLink = ("[[%s|''%s'' %s]] by %s"):format(args.article, subtitle, args.type, authors)
	else
		display = ("''%s''"):format(args.shortName)
		link = ("''[[%s|%s]]''"):format(args.article, args.shortName)
		phraseLink = link
	end
	return {
		display = display,
		link = link,
		phraseLink = phraseLink,
	}
end

-- QUERIES: ALL

function p.enum(options)
	if not options then
		return cache.enum
	end
	enum = utilsTable.clone(cache.enum) -- clone the read-only cache item so that we can modify it
	if options.includeSeries then
		table.insert(enum, 1, "Series")
	end
	if options.includeNonfiction then
		local codes = utilsTable.map(cache.nonfiction, "code")
		enum = utilsTable.concat(codes, enum)
	end
	if options.includeGroups then
		-- insert "groups" so as to not disrupt the release order. This matters for Template:Media (e.g. the Fighter page, which uses SSB4)
		for _, group in ipairs(cache.groups) do
			local i = 1
			repeat
				i = i + 1
			until i == #enum or p.isCanon(enum[i]) == p.isCanon(group.code) and p.releaseDate(enum[i]) and p.releaseDate(enum[i]) >= p.releaseDate(group.code)
			table.insert(enum, i, group.code)
		end
	end
	enum.reference = "[[Data:Franchise]]"
	return enum
end

function p.article(code)
	return h.get(code, "article")
end

function p.canonicity(code)
	return h.get(code, "canonicity")
end

function p.canonOrder(code)
	local canonOrder = h.get(code, "canonOrder")
	return canonOrder and tonumber(canonOrder)
end

function p.code(code)
	return h.get(code, "code")
end

function p.display(code)
	return h.get(code, "display")
end

function p.isCanon(code)
	return p.canonicity(code) == "canon"
end

function p.link(code)
	return h.get(code, "link")
end

function p.logo(code)
	return h.get(code, "logo")
end

function p.releaseDate(code)
	return h.get(code, "releaseDate")
end

function p.shortName(code)
	return h.get(code, "shortName")
end

function p.type(code)
	return h.get(code, "type")
end

-- QUERIES: GAMES

function p.enumGames(includeSeries)
	if includeSeries then
		local enum = utilsTable.concat({"Series"}, cache.enumGames)
		enum.reference = "[[Data:Franchise]]"
		return enum
	end
	return cache.enumGames
end

function p.baseGame(code)
	local baseGames = h.get(code, "remakeOf")
	if baseGames == nil then -- game not found
		return nil
	elseif baseGames == "" then -- game has no remakes
		return code
	else
		return utilsString.split(baseGames)[1]
	end
end

function p.family(code)
	return h.get(code, "family")
end

function p.graphics(code)
	return h.get(code, "graphics")
end

function p.hasRemakes(code)
	return utilsTable.hasKey(cache.remakes, string.lower(code))
end

function p.isRemake(code)
	local remakeOf = h.get(code, "remakeOf")
	return remakeOf ~= nil and remakeOf ~= ""
end

function p.remakes(code)
	return utilsTable.clone(cache.remakes[string.lower(code)]) or {}
end

function p.superseder(code)
	local supersededBy = h.get(code, "supersededBy")
	if supersededBy == "" then
		supersededBy = nil
	end
	return supersededBy
end

-- QUERIES: BOOKS
function p.publisher(code)
	return h.get(code, "publisher")
end

function p.phraseLink(code)
	return h.get(code, "phraseLink")
end

function h.get(code, prop)
	code = string.lower(code)
	if code == "series" then
		return series[prop]
	end
	local title = cache.titlesByCode[code]
	if not title then
		return nil -- we return nil here to indicate an invalid code
	else
		return title[prop] or "" -- we return "" here to indicate the absence of an optional property on a valid code
	end
end

local nowarnParam = {
	desc = "<p>If present, the template will not issue an [[Module:Error|editor warning]] when <code>game</code> is invalid. Use this when a template needs custom error handling (see examples).</p><p>However, an editor warning will still be issued if a game code is written with incorrect casing (e.g. <code>oot</code> instead of <code>OoT</code>)</p>",
}

function p.Documentation()
	return {
		sections = {
			{
				heading = "Template functions",
				section = {
					Article = {
						frameParams = {
							[1] = {
								name = "game",
								required = true,
								desc = "The code for a game or other entry in [[Data:Franchise]].",
							},
							nowarn = nowarnParam,
						},
						cases = {
							resultOnly = true,
							{
								args = {"OoT"},
							},
							{
								args = {"OoT (Himekawa)"},
							},
							{
								desc = "Error handling",
								args = {"oot"},
							},
							{
								args = {"notAGame"},
							},
							{
								args = {""},
							},
							{
								input = "{{#iferror:{{#invoke:Franchise|Article|invalid game}}|custom error handling}}",
							},
						},
					},
					BaseGame = {
						frameParams = {
							[1] = {
								name = "game",
								required = true,
								desc = "The code for a game entry in [[Data:Franchise]].",
							},
							nowarn = nowarnParam,
						},
						cases = {
							resultOnly = true,
							{
								args = {"TWW"},
							},
							{
								args = {"TWWHD"},
							},
							{
								desc = "Error handling",
								args = {"notAGame"},
							},
							{
								args = {"twwhd"},
							},
							{
								args = {""},
							},
							{
								input = "{{#iferror:{{#invoke:Franchise|BaseGame|invalid game}}|custom error handling}}",
							},
						},
					},
					Display = {
						frameParams = {
							[1] = {
								name = "game",
								required = true,
								desc = "The code for a game or other Zelda-related title defined at [[Data:Franchise]].",
							},
							nowarn = nowarnParam,
						},
						cases = {
							resultOnly = true,
							{
								args = {"LA"},	
							},
							{
								args = {"LANS"},
							},
							{
								args = {"LA (Cagiva)"},
							},
							{
								args = {"invalid game"},
							},
							{
								args = {""},
							},
						},
					},
					Link = {
						frameParams = {
							[1] = {
								name = "game",
								required = true,
								desc = "The code for a game or other Zelda-related title defined at [[Data:Franchise]].",
							},
							nowarn = nowarnParam,
						},
						cases = {
							resultOnly = true,
							{
								args = {"OoT"},
							},
							{
								args = {"OoT (Himekawa)"},
							},
							{
								desc = "Error handling",
								args = {"oot"},
							},
							{
								args = {"notAGame"},
							},
							{
								args = {""},
							},
							{
								input = "{{#iferror:{{#invoke:Franchise|Link|invalid game}}|custom error handling}}",
							},
						},
					},
					ShortName = {
						frameParams = {
							[1] = {
								name = "game",
								required = true,
								desc = "The code for a game or other Zelda-related title defined at [[Data:Franchise]].",
							},
							nowarn = nowarnParam,
						},
						cases = {
							resultOnly = true,
							{
								args = {"OoT"},	
							},
							{
								args = {"OoT (Himekawa)"},
							},
							{
								args = {"E"},
							},
							{
								desc = "Error handling",
								args = {"oot"},
							},
							{
								args = {"notAGame"},
							},
							{
								args = {""},
							},
							{
								desc = "Custom error handling",
								input = "{{#iferror:{{#invoke:Franchise|ShortName|invalid game}}|[[Dodongo]]|[[Dodongo#{{#invoke:Franchise|ShortName|invalid game}}]]}}",
							},
							{
								input = "{{#iferror:{{#invoke:Franchise|ShortName|oot}}|[[Dodongo]]|[[Dodongo#{{#invoke:Franchise|ShortName|oot}}]]}}",
							},
							{
								input = "{{#iferror:{{#invoke:Franchise|ShortName|OoT}}|[[Dodongo]]|[[Dodongo#{{#invoke:Franchise|ShortName|OoT}}]]}}",
							},
						}
					}
				},
			},
			{
				heading = "Module functions - all media",
				section = {
					enum = {
						desc = "See also {{Sect|enumGames}}.",
						params = {"options"},
						returns = "An array of all codes in [[Guidelines:Main#Canon_Order|canon order]], plus a <code>reference</code> key so that it can be used for [[Module:Documentation|documentation]] and [[Module:UtilsArg|validation]].",
						cases = {
							outputOnly = true,
							{
								snippet = 1,
								expect = {"TLoZ", "TAoL", "ALttP", "LA", "LADX", "LANS", "OoT"},
							},
							{
								snippet = 2,
								expect = "[[Data:Franchise]]",
							},
							{
								snippet = "IncludeSeries",
								desc = "When <code>includeSeries</code> is true, then <code>Series</code> is the first item in the enum.",
								expect = {"Series", "TLoZ", "TAoL"},
							},
							{
								snippet = "IncludeNonfiction",
								desc = "When <code>includeNonfiction</code> is true, then books such as {{E}} are in the list.",
								expect = true,
							},
							{
								snippet = "IncludeGroups",
								desc = "When <code>includeGroups</code> is true, then collective terms such as {{ALttP&FS}} are included in the list.",
								expect = true,
							},
						},
					},
					canonOrder = {
						params = {"code"},
						returns = "Position of the franchise title in the [[Guidelines:Main#Canon Order|Canon Order]].",
						cases = {
							{
								args = {"LA"},
								expect = 4,
							},
							{
								args = {"LADX"},
								expect = 5,
							},
							{
								args = {"Not a Game"},
								expect = nil,
							},
							{
								desc = "Non-ficition books such as {{E}} have no position in the canon order.",
								args = {"E"},
								expect = nil,
							},
						},
					},
					code = {
						params = {"code"},
						returns = "The same code but correctly formatted, or <code>nil</code> if no such code exists",
						cases = {
							outputOnly = true,
							{
								args = {"alttp"},
								expect = "ALttP",
							},
							{
								args = {"fakegame"},
								expect = nil,
							},
						},
					},
					shortName = {
						params = {"code"},
						returns = "Short name for franchise title used in [[:Category:Games|category names]]. Usually the subtitle.",
						cases = {
							{
								args = {"LA"},
								expect = "Link's Awakening",
							},
							{
								args = {"la"},
								expect = "Link's Awakening"
							},
							{
								args = {"LANS"},
								expect = "Link's Awakening (Nintendo Switch)",
							},
							{
								args = {"LA (Cagiva)"},
								expect = "Link's Awakening (Cagiva)",
							},
							{
								args = {"E"},
								expect = "Encyclopedia",
							},
							{
								args = {"ALttP&FS"},
								expect = "A Link to the Past & Four Swords",
							},
							{
								args = {"Series"},
								expect = "The Legend of Zelda Series"
							},
							{
								args = {"fakeGame"},
								expect = nil,
							},
						}
					},
					link = {
						params = {"code"},
						returns = "Formatted link used in infoboxes and so on.",
						cases = {
							{
								args = {"LA"},
								expect = "''[[The Legend of Zelda: Link's Awakening|Link's Awakening]]''",
							},
							{
								args = {"la"},
								expect = "''[[The Legend of Zelda: Link's Awakening|Link's Awakening]]''",
							},
							{
								args = {"LADX"},
								expect = "''[[The Legend of Zelda: Link's Awakening DX|Link's Awakening DX]]''",
							},
							{
								args = {"LANS"},
								expect = "[[The Legend of Zelda: Link's Awakening (Nintendo Switch)|''Link's Awakening'' for Nintendo Switch]]",
							},
							{
								desc = "For books, comics and manga, see also {{Sect|phraseLink}}.",
								args = {"LA (Cagiva)"},
								expect = "[[The Legend of Zelda: Link's Awakening (Cagiva)|''Link's Awakening'' (Cagiva)]]",
							},
							{
								args = {"E"},
								expect = "''[[The Legend of Zelda: Encyclopedia|Encyclopedia]]''",
							},
							{
								args = {"ALttP&FS"},
								expect = "''[[The Legend of Zelda: A Link to the Past & Four Swords|A Link to the Past & Four Swords]]''"
							},
							{
								args = {"Series"},
								expect = "[[The Legend of Zelda (Series)|''The Legend of Zelda'' series]]"
							},
							{
								args = {"fakeGame"},
								expect = nil,
							},
						}
					},
					isCanon = {
						params = {"code"},
						returns = "True if title is canon, else false.",
						cases = {
							{
								args = {"LANS"},
								expect = true,
							},
							{
								args = {"lans"},
								expect = true,
							},
							{
								args = {"CoH"},
								expect = false,
							},
							{
								args = {"SSBU"},
								expect = false,
							},
							{
								args = {"E"},
								expect = true,
							},
							{
								args = {"Series"},
								expect = true,
							},
						},
					},
					display = {
						params = {"code"},
						returns = "Formatted text for the title.",
						cases = {
							{
								args = {"LA"},
								expect = "''Link's Awakening''",
							},
							{
								args = {"la"},
								expect = "''Link's Awakening''",
							},
							{
								args = {"LANS"},
								expect = "''Link's Awakening'' for Nintendo Switch",
							},
							{
								args = {"E"},
								expect = "''Encyclopedia''"
							},
							{
								args = {"ALttP&FS"},
								expect = "''A Link to the Past & Four Swords''",
							},
							{
								args = {"Series"},
								expect = "''The Legend of Zelda'' series"
							},
							{
								args = {"fakeGame"},
								expect = nil,
							},
						}
					},
					logo = {
						params = {"code"},
						returns = "Filename for the title's logo.",
						cases = {
							{
								args = {"TWW"},
								expect = "File:TWW English Logo.png",
							},
							{
								args = {"tww"},
								expect = "File:TWW English Logo.png"
							},
							{
								args = {"E"},
								expect = "File:The Legend of Zelda Encyclopedia Cover.png"
							},
							{
								args = {"Series"},
								expect = "File:Zelda Logo TP.png"
							},
							{
								args = {"fakeGame"},
								expect = nil
							}
						}
					},
					article = {
						params = {"code"},
						returns = "Wiki article name for the title",
						cases = {
							outputOnly = true,
							{
								args = {"LA"},
								expect = "The Legend of Zelda: Link's Awakening",
							},
							{
								args = {"la"},
								expect = "The Legend of Zelda: Link's Awakening",
							},
							{
								args = {"LANS"},
								expect = "The Legend of Zelda: Link's Awakening (Nintendo Switch)",
							},
							{
								args = {"TAoL"},
								expect = "Zelda II: The Adventure of Link",
							},
							{
								args = {"TLoZ"},
								expect = "The Legend of Zelda",
							},
							{
								args = {"Series"},
								expect = "The Legend of Zelda (Series)",
							},
							{
								args = {"E"},
								expect = "The Legend of Zelda: Encyclopedia"
							},
							{
								args = {"SSB4"},
								expect = "Super Smash Bros. for Nintendo 3DS / Wii U",
							},
						},
					},
					canonicity = {
						params = {"code"},
						returns = "A string: <code>canon</code>, <code>ambiguous</code>, or <code>non-canon</code>.",
						cases = {
							outputOnly = true,
							{
								args = {"LA"},
								expect = "canon",
							},
							{
								args = {"la"},
								expect = "canon",
							},
							{
								args = {"CoH"},
								expect = "ambiguous",
							},
							{
								args = {"LA (Cagiva)"},
								expect = "non-canon",
							},
							{
								args = {"E"},
								expect = "canon",
							},
							{
								args = {"fake"},
								expect = nil,
							}
						},
					},
					releaseDate = {
						params = {"code"},
						returns = 'The "main" release date for a title.',
						cases = {
							outputOnly = true,
							{
								args = {"LA"},
								expect = "1993-08-06",
							},
							{
								args = {"LANS"},
								expect = "2019-09-20",
							},
							{
								args = {"LA (Cagiva)"},
								expect = "1994-05-01",
							},
							{
								args = {"E"},
								expect = "2018-06-19",
							},
							{
								args = {"ALttP&FS"},
								expect = "2002-12-02",
							},
							{
								args = {"Series"},
								expect = nil
							},
							{
								args = {"fakeGame"},
								expect = nil,
							},
						},
					},
				},
			},
			{
				heading = "Module functions - games",
				section = {
					enumGames = {
						params = {"includeSeries"},
						returns = "An array of all game codes in [[Guidelines:Main#Canon_Order|canon order]], plus a <code>reference</code> key so that it can be used for [[Module:Documentation|documentation]] and [[Module:UtilsArg|validation]].",
						cases = {
							outputOnly = true,
							{
								snippet = "1",
								expect = {"TLoZ", "TAoL", "ALttP"},
							},
							{
								snippet = "2",
								expect = "[[Data:Franchise]]",
							},
							{
								snippet = "IncludeSeries",
								desc = "When <code>includeSeries</code> is true, then <code>Series</code> is the first item in the enum.",
								expect = {"Series", "TLoZ", "TAoL"},
							},
						},
					},
					baseGame = {
						params = {"code"},
						returns = "If <code>code</code> is a remake, returns the code for the original game, else returns <code>code</code> as-is.",
						cases = {
							outputOnly = true,
							{
								args = {"LANS"},
								expect = "LA",
							},
							{
								args = {"LA"},
								expect = "LA",
							},
							{
								args = {"fake"},
								expect = nil,
							},
						},
					},
					family = {
						params = {"code"},
						returns = "A grouping name used for certain non-canon games on the [[Main Page]].",
						cases = {
							outputOnly = true,
							{
								args = {"OoT"},
								expect = "",
							},
							{
								args = {"LCT"},
								expect = "",
							},
							{
								args = {"FPTRR"},
								expect = "Tingle",
							},
							{
								args = {"HWDE"},
								expect = "Hyrule Warriors",
							},
						},
					},
					graphics = {
						params = {"code"},
						returns = "A string: <code>2D</code> or <code>3D</code>.",
						cases = {
							outputOnly = true,
							{
								args = {"LA"},
								expect = "2D",
							},
							{
								args = {"la"},
								expect = "2D",
							},
							{
								args = {"LANS"},
								expect = "3D",
							},
							{
								args = {"fake"},
								expect = nil,
							},
						},
					},
					hasRemakes = {
						params = {"code"},
						returns = "True if game has at least one remake, remaster, or enhanced port. Else false.",
						cases = {
							{
								args = {"LA"},
								expect = true,
							},
							{
								args = {"LADX"},
								expect = true,
							},
							{
								args = {"ST"},
								expect = false
							},
							{
								args = {"fakeGame"},
								expect = false,
							},
						},
					},
					isRemake = {
						params = {"code"},
						returns = "True if game is a remake, remaster, or enhanced port. Else false.",
						cases = {
							{
								args = {"LANS"},
								expect = true,
							},
							{
								args = {"LADX"},
								expect = true,
							},
							{
								args = {"LA"},
								expect = false,
							},
							{
								args = {"HWDE"},
								expect = true,
							}
						},
					},
					remakes = {
						params = {"code"},
						returns = "List of remakes for a specific game, or a table of all remakes if no game specified",
						cases = {
							{
								args = {"LA"},
								expect = {"LADX", "LANS"},
							},
							{
								args = {"LADX"},
								expect = {"LANS"},
							},
							{
								args = {"ST"},
								expect = {}
							},
							{
								args = {"fake"},
								expect = {},
							},
						},
					},
					superseder = {
						params = {"code"},
						returns = "The game code of the latest canon version of the game corresponding to <code>code</code>",
						cases = {
							{
								args = {"LA"},
								expect = "LANS",
							},
							{
								desc = "<p><code>nil</code> is returned the game has no newer canon versions or does not exist in [[Data:Franchise]].</p><p>FSAE is not returned for FS as it not considered a canon remake.</p>",
								args = {"FS"},
								expect = nil,
							},
							{
								args = {"BotW"},
								expect = nil,
							},
							{
								args = {"notACode"},
								expect = nil,
							},
						},
					},
					type = {
						params = {"code"},
						returns = 'One of: <code>"main"</code>, <code>"remake"</code>, <code>"spin-off"</code>, <code>""</code> (other games)',
						cases = {
							{
								args = {"BotW"},
								expect = "main",
							},
							{
								args = {"HWAoC"},
								expect = "spin-off",
							},
							{
								args = {"SSBU"},
								expect = "",
							},
						},
					},
				},
			},
			{
				heading = "Module functions - books",
				section = {
					publisher = {
						params = {"code"},
						returns = "The book's publisher - in North America if available.",
						cases = {
							{
								args = {"OoT (Himekawa)"},
								expect = "VIZ Media",
							},
							{
								args = {"E"},
								expect = "Dark Horse Books",
							},
						},
					},
					phraseLink = {
						params = {"code"},
						returns = "Formatted link to page and authors.",
						cases = {
							{
								args = {"TLoZ (Ran)"},
								expect = "[[The Legend of Zelda (Ran)|''The Legend of Zelda'' manga]] by [[Maru Ran]]",
							},
							{
								args = {"tloz (ran)"},
								expect = "[[The Legend of Zelda (Ran)|''The Legend of Zelda'' manga]] by [[Maru Ran]]",
							},
							{
								args = {"AOV"},
								expect = "''[[The Legend of Zelda: An Original Version|The Legend of Zelda: An Original Version]]''",
							},
							{
								args = {"fake"},
								expect = nil,
							},
							{
								desc = "Nonfiction books such as {{E}} are not supported by this function.",
								args = {"E"},
								expect = "",
							},
						},
					},
				},
			},
		},
	}
end

return p