Module:Franchise

local p = {} local h = {}

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

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

-- 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", display = "The Legend of Zelda series", canonicity = "canon", code = "Series", }

function p.Article(frame) return h.templateQuery("article", frame) end function p.BaseGame(frame) return h.templateQuery("baseGame", 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:"..Constants.catInvalidArgs, nowarn) end game = utilsString.trim(game) local value = p[fn](game) if not value then return "", h.error(string.format("Invalid entry ", game), "Category:"..Constants.catInvalidArgs, 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(" should be written as  ", game, correctGameCode)) return value, "" 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 supersededBy ~= nil and supersededBy ~= "" then local supersederLink = p.link(supersededBy) canonicityStatus = canonicityStatus .. string.format(" (superseded by %s) ", 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 doc = mw.loadData("Module:Franchise/Documentation/Data") local orderCounter = utilsVar.counter("canonOrder") local entryType = frame.args[1] local args, err = utilsArg.parse(frame:getParent.args, doc.Templates["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"):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", "authors", "basedOn"}, Nonfiction = {"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 remakes = utilsTable.groupBy(mainGames, "remakeOf") local books = groups["Book"] local tvShows = groups["TV"] local sortedMainGames = {} for _, mainGame in ipairs(utilsTable.reverse(mainGames)) do		if not mainGame.remakeOf then table.insert(sortedMainGames, mainGame) for _, remake in ipairs(remakes[mainGame.code] or {}) do				table.insert(sortedMainGames, remake) end 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"):format(args.article, display)		local authors = ListPages.main(utilsString.split(args.authors))		phraseLink = ("%s %s by %s"):format(args.article, subtitle, args.type, authors)	else		display = ("%s"):format(args.shortName)		link = ("%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 Figher 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]) >= 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.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 baseGame = h.get(code, "remakeOf") if baseGame == nil then -- game not found return nil elseif baseGame == "" then -- game has no remake return code else return baseGame 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) return p.type(code) == "remake" end

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

-- QUERIES: BOOKS

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] return title and title[prop] end

return p