Module:Comment

local p = {}

local Cite = require("Module:Cite") local Franchise = require("Module:Franchise") local utilsArg = require("Module:UtilsArg") local utilsError = require('Module:UtilsError') local utilsMarkup = require("Module:UtilsMarkup") local utilsTable = require("Module:UtilsTable")

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

local COMMENTER_IMAGE_SIZE = "80x80px"

-- Deprecated function p.Main(frame) return p.main(frame.args) end

function p.MultiComment(frame) local args, err = utilsArg.parse(frame:getParent.args, p.Templates.MultiComment) local errorCategories = err and utilsMarkup.categories(err.categories) or "" local result = p.multiComment(frame.args[1], frame.args[2], frame.args[3], frame.args[4], args.quotes, args.singleQuote) return result..errorCategories end

function p.SingleComment(frame) local args, err = utilsArg.parse(frame:getParent.args, p.Templates.SingleComment) local errorCategories = err and utilsMarkup.categories(err.categories) or "" local result = p.singleComment(frame.args[1], frame.args[2], frame.args[3], frame.args[4], args.quote, args._quote) return result..errorCategories end

function p.Documentation(frame) return p.documentation(frame) end

function p.main(args) local cType = args[1] -- "Comment Type," The template that is being called (Fi, Midna, Navi, etc.) This is typically set by default on every template to decrease user error, but some templates (like Template:Manual) allow for this to be changed to relate to different games. local cSubject = args[2] -- "Comment Subject," The header for the comment (Stalfos, Green Rupee, Zelda, etc.) Pulls from parameter 1 (or 2 if Template:Manual is used). local cContent = args[3] -- "Comment Content," The quote that is displayed in the comment. Pulls from parameter 2 (or 3 if Template:Manual is used) and automatically surrounds it with qutotation marks.

--The following comment types all regard Character comments in-game. These usually regard Link's partner or other character that gives general information on certain subjects, like a "tutorial" guide. Each of these characters have their own template, and the cType parameter is automatically set for every usage of the template.

if (cType == "Fishman") then --for Template:Fishman (The Wind Waker) returnTable = string.format("Fishman's Comment: \n{|\n|\n|%s \"%s\"\n|}  ", cSubject, cContent) elseif (cType == "Madame Couture") then --for Template:Madame Couture (Tri Force Heroes) returnTable = string.format("Madame Couture's Comment: \n{|\n|\n|%s \"%s\"\n|}  ", cSubject, cContent)

elseif (cType == "Monita") then --for Template:Monita (Nintendo Land) returnTable = string.format("Monita's Comment: \n{|\n|\n|%s \"%s\"\n|}  ", cSubject, cContent)

elseif (cType == "Navi") then --for Template:Navi (Ocarina of Time) returnTable = string.format("Navi's Comment: \n{|\n|\n|%s \"%s\"\n|}  ", cSubject, cContent)

elseif (cType == "Tatl") then --for Template:Tatl (Majora's Mask) returnTable = string.format("<div class=\"toccolours mw-collapsible\" data-expandtext=\"show comment ▼\" data-collapsetext=\"hide comment ▲\"><div style=\"font-size: 105%%\">Tatl's Comment: <div class=\"mw-collapsible-content\" style=\"border-radius: 25px; border: 6px solid ; background-color: ; display: flex\">\n{|\n|\n|<span style=\"font-size: 130%%; font-weight: bold;\">%s \"%s\"\n|}  ", cSubject, cContent)

elseif (cType == "Tingle") then --for Template:Tingle (the Wind Waker [not HD]) returnTable = string.format("<div class=\"toccolours mw-collapsible\" data-expandtext=\"show comment ▼\" data-collapsetext=\"hide comment ▲\"><div style=\"font-size: 105%%\">Tingle's Comment: <div class=\"mw-collapsible-content\" style=\"border-radius: 25px; border: 6px solid ; background-color: ; display: flex\">\n{|\n|\n|<span style=\"font-size: 130%%; font-weight: bold;\">%s \"%s\"\n|}  ", cSubject, cContent) --The following is for Template:Manual. On each page, the editor enters and the Game abbreviation replaces the "cType" variable, which was automatically set on the previous templates.

elseif (cType == "TLoZ") then --for The Legend of Zelda manual returnTable = string.format("<div class=\"toccolours mw-collapsible\" data-expandtext=\"show comment ▼\" data-collapsetext=\"hide comment ▲\"><div style=\"font-size: 105%%\">The Legend of Zelda Manual Comment: <div class=\"mw-collapsible-content\" style=\"border-radius: 25px; border: 6px solid ; background-color: ; display: flex\">\n{|\n|\n|<span style=\"font-size: 130%%; font-weight: bold;\">%s \"%s\"\n|}  ", cSubject, cContent)

elseif (cType == "TAoL") then --for The Adventure of Link manual returnTable = string.format("<div class=\"toccolours mw-collapsible\" data-expandtext=\"show comment ▼\" data-collapsetext=\"hide comment ▲\"><div style=\"font-size: 105%%\">The Adventure of Link Manual Comment: <div class=\"mw-collapsible-content\" style=\"border-radius: 25px; border: 6px solid ; background-color: ; display: flex\">\n{|\n|\n|<span style=\"font-size: 130%%; font-weight: bold;\">%s \"%s\"\n|}  ", cSubject, cContent) elseif (cType == "ZG&W") then --for the Zelda (Game & Watch) manual returnTable = string.format("<div class=\"toccolours mw-collapsible\" data-expandtext=\"show comment ▼\" data-collapsetext=\"hide comment ▲\"><div style=\"font-size: 105%%\">Zelda (Game & Watch) Manual Comment: <div class=\"mw-collapsible-content\" style=\"border-radius: 25px; border: 6px solid ; background-color: ; display: flex\">\n{|\n|\n|<span style=\"font-size: 130%%; font-weight: bold;\">%s \"%s\"\n|}  ", cSubject, cContent)

elseif (cType == "ALttP") then --for the A Link to the Past manual returnTable = string.format("<div class=\"toccolours mw-collapsible\" data-expandtext=\"show comment ▼\" data-collapsetext=\"hide comment ▲\"><div style=\"font-size: 105%%\">A Link to the Past Manual Comment: <div class=\"mw-collapsible-content\" style=\"border-radius: 25px; border: 6px solid ; background-color: ; display: flex\">\n{|\n|\n|<span style=\"font-size: 130%%; font-weight: bold;\">%s \"%s\"\n|}  ", cSubject, cContent) elseif (cType == "ALttPGBA") then --for the A Link to the Past portion of the A Link to the Past & Four Swords manual returnTable = string.format("<div class=\"toccolours mw-collapsible\" data-expandtext=\"show comment ▼\" data-collapsetext=\"hide comment ▲\"><div style=\"font-size: 105%%\">A Link to the Past (Game Boy Advance) Manual Comment: <div class=\"mw-collapsible-content\" style=\"border-radius: 25px; border: 6px solid ; background-color: ; display: flex\">\n{|\n|\n|<span style=\"font-size: 130%%; font-weight: bold;\">%s \"%s\"\n|}  ", cSubject, cContent)

elseif (cType == "LA") then --for the Link's Awakening manual returnTable = string.format("<div class=\"toccolours mw-collapsible\" data-expandtext=\"show comment ▼\" data-collapsetext=\"hide comment ▲\"><div style=\"font-size: 105%%\">Link's Awakening Manual Comment: <div class=\"mw-collapsible-content\" style=\"border-radius: 25px; border: 6px solid ; background-color: ; display: flex\">\n{|\n|\n|<span style=\"font-size: 130%%; font-weight: bold;\">%s \"%s\"\n|}  ", cSubject, cContent)

elseif (cType == "OoT") then --for the Ocarina of Time manual returnTable = string.format("<div class=\"toccolours mw-collapsible\" data-expandtext=\"show comment ▼\" data-collapsetext=\"hide comment ▲\"><div style=\"font-size: 105%%\">Ocarina of Time Manual Comment: <div class=\"mw-collapsible-content\" style=\"border-radius: 25px; border: 6px solid ; background-color: ; display: flex\">\n{|\n|\n|<span style=\"font-size: 130%%; font-weight: bold;\">%s \"%s\"\n|}  ", cSubject, cContent)

elseif (cType == "MM") then --for the Majora's Mask manual returnTable = string.format("<div class=\"toccolours mw-collapsible\" data-expandtext=\"show comment ▼\" data-collapsetext=\"hide comment ▲\"><div style=\"font-size: 105%%\">Majora's Mask Manual Comment: <div class=\"mw-collapsible-content\" style=\"border-radius: 25px; border: 6px solid ; background-color: ; display: flex\">\n{|\n|\n|<span style=\"font-size: 130%%; font-weight: bold;\">%s \"%s\"\n|}  ", cSubject, cContent) elseif (cType == "OoS") then --for the Oracle of Seasons manual returnTable = string.format("<div class=\"toccolours mw-collapsible\" data-expandtext=\"show comment ▼\" data-collapsetext=\"hide comment ▲\"><div style=\"font-size: 105%%\">Oracle of Seasons Manual Comment: <div class=\"mw-collapsible-content\" style=\"border-radius: 25px; border: 6px solid ; background-color: ; display: flex\">\n{|\n|\n|<span style=\"font-size: 130%%; font-weight: bold;\">%s \"%s\"\n|}  ", cSubject, cContent) elseif (cType == "OoA") then --for the Oracle of Ages manual returnTable = string.format("<div class=\"toccolours mw-collapsible\" data-expandtext=\"show comment ▼\" data-collapsetext=\"hide comment ▲\"><div style=\"font-size: 105%%\">Oracle of Ages Manual Comment: <div class=\"mw-collapsible-content\" style=\"border-radius: 25px; border: 6px solid ; background-color: ; display: flex\">\n{|\n|\n|<span style=\"font-size: 130%%; font-weight: bold;\">%s \"%s\"\n|}  ", cSubject, cContent) elseif (cType == "FS") then --for the Four Swords portion of the A Link to the Past & Four Swords manual returnTable = string.format("<div class=\"toccolours mw-collapsible\" data-expandtext=\"show comment ▼\" data-collapsetext=\"hide comment ▲\"><div style=\"font-size: 105%%\">Four Swords Manual Comment: <div class=\"mw-collapsible-content\" style=\"border-radius: 25px; border: 6px solid ; background-color: ; display: flex\">\n{|\n|\n|<span style=\"font-size: 130%%; font-weight: bold;\">%s \"%s\"\n|}  ", cSubject, cContent) elseif (cType == "FSA") then --for the Four Swords Adventures manual returnTable = string.format("<div class=\"toccolours mw-collapsible\" data-expandtext=\"show comment ▼\" data-collapsetext=\"hide comment ▲\"><div style=\"font-size: 105%%\">Four Swords Adventures Manual Comment: <div class=\"mw-collapsible-content\" style=\"border-radius: 25px; border: 6px solid ; background-color: ; display: flex\">\n{|\n|\n|<span style=\"font-size: 130%%; font-weight: bold;\">%s \"%s\"\n|}  ", cSubject, cContent) elseif (cType == "TMC") then --for the The Minish Cap manual returnTable = string.format("<div class=\"toccolours mw-collapsible\" data-expandtext=\"show comment ▼\" data-collapsetext=\"hide comment ▲\"><div style=\"font-size: 105%%\">The Minish Cap Manual Comment: <div class=\"mw-collapsible-content\" style=\"border-radius: 25px; border: 6px solid ; background-color: ; display: flex\">\n{|\n|\n|<span style=\"font-size: 130%%; font-weight: bold;\">%s \"%s\"\n|}  ", cSubject, cContent) elseif (cType == "ST") then --for the Spirit Tracks manual returnTable = string.format("<div class=\"toccolours mw-collapsible\" data-expandtext=\"show comment ▼\" data-collapsetext=\"hide comment ▲\"><div style=\"font-size: 105%%\">Spirit Tracks Manual Comment: <div class=\"mw-collapsible-content\" style=\"border-radius: 25px; border: 6px solid ; background-color: ; display: flex\">\n{|\n|\n|<span style=\"font-size: 130%%; font-weight: bold;\">%s \"%s\"\n|}  ", cSubject, cContent)

elseif (cType == "LCT") then --for the Link's Crossbow Training manual returnTable = string.format("<div class=\"toccolours mw-collapsible\" data-expandtext=\"show comment ▼\" data-collapsetext=\"hide comment ▲\"><div style=\"font-size: 105%%\">Link's Crossbow Training Manual Comment: <div class=\"mw-collapsible-content\" style=\"border-radius: 24px; border: 6px solid ; background-color: ; display: flex\">\n{|\n|\n|<span style=\"font-size: 130%%; font-weight: bold;\">%s \"%s\"\n|}  ", cSubject, cContent)

else --Activates when someone enters an invalid value for "cType" utilsError.warn(string.format("%s is not a valid game, see here for all usable games. If this is a mistake, please contact a staff member on the Discord server.", cType)) end return returnTable end

function p.multiComment(game, commenter, commenterFileName, quoteBorderColor, quotes, singleQuote) local categories = "" if singleQuote then quotes = { { quote = singleQuote } }		utilsError.warn(" parameter is deprecated. Please use   instead.") categories = categories.."" end return p.printComments(game, commenter, commenterFileName, quoteBorderColor, quotes) .. categories end

function p.singleComment(game, commenter, commenterFileName, quoteBorderColor, quote, _quote) local quote = { quote = _quote or quote -- holdover from when comment templates took in a subject parameter first, which is no longer used }	return p.printComments(game, commenter, commenterFileName, quoteBorderColor, {quote}) end function p.printComments(game, commenter, commenterFileName, quoteBorderColor, quotes) local categories = "" if #quotes == 0 then utilsError.warn("Please provide at least one quote.") categories = categories.."" return categories end local img = utilsMarkup.file(commenterFileName, {		size = COMMENTER_IMAGE_SIZE,		link = commenter,		caption = commenter.." says:"	}) local commentsHeading = string.format("%s's %s ", commenter, #quotes == 1 and "Comment" or "Comments") local commenterModifier = string.gsub(string.lower(commenter), " ", "-") local content = mw.html.create("div") :addClass("mw-collapsible comments comments--"..commenterModifier) :tag("span") :addClass("comments__heading") -- Custom class names use BEM syntax https://getbem.com/ :wikitext(commentsHeading) :done :tag("div") :addClass("mw-collapsible-content comments__content") local quote = quotes[1] local additionalQuotes = utilsTable.tail(quotes) local firstComment = content:tag("div") :addClass("comments__first-comment") if quote.context then firstComment:tag("span") :addClass("comments__quote-context") :wikitext(quote.context) end firstComment:tag("div") :addClass("comments__commenter") :wikitext(img) local coloredQuote = Cite.color(quote.quote, commenter, game) firstComment:tag("blockquote") :addClass("comments__quote-bubble") -- We use an inline style here so that folks can make new comment templates -- without having to edit two additional CSS files, which few know how to or have the rights to do		:css("border-color", quoteBorderColor) :wikitext(coloredQuote) if #additionalQuotes > 0 then local collapsedContent = content:tag("div") :addClass("mw-collapsible mw-collapsed comments__additional-comments") :attr("id", "mw-customcollapsible-additionalComments"..commenter) :tag("span") :addClass("comments__toggle-more mw-customtoggle-additionalComments"..commenter) :wikitext("show more...") :done :tag("div") :addClass("mw-collapsible-content comments__additional-comments-content") for i, quote in ipairs(additionalQuotes) do			collapsedContent:tag("div") :addClass("comments__spacer") :attr("aria-hidden", "true") if quote.context then collapsedContent:tag("span") :addClass("comments__quote-context") :wikitext(quote.context) elseif commenter ~= "Fi" then -- we make an exception for Fi because for bosses she has several comments separated by "tell me more" prompts -- It would be redundant to show "tell me more" for multiple quotes utilsError.warn("The  parameter should be used when there are more than one comment.") categories = categories .. string.format("", Franchise.shortName(Franchise.baseGame(game))) end local coloredQuote = Cite.color(quote.quote, commenter, game)

collapsedContent:tag("blockquote") :addClass("comments__quote-bubble") :css("border-color", quoteBorderColor) :wikitext(coloredQuote) end end local result = tostring(content:allDone) return result..categories end

function p.documentation(frame) -- Only the documentation needs these so we require them here as an optimization local Documentation = require("Module:Documentation") local utilsString = require("Module:UtilsString") local args = utilsTable.mapValues(frame.args, utilsString.trim) args = utilsTable.mapValues(args, utilsString.nilIfEmpty) local templateName = args[1] local game = args[2] local commenter = args[3] local examples = args.examples local gameName = Franchise.shortName(game) local gameDisplay = Franchise.display(game) local topics = { args["bosses"] == "true" and args["enemies"] ~= "true" and string.format("bosses", gameName), args["characters"] == "true" and string.format("characters", gameName), args["dungeons"] == "true" and args["dungeons"] ~= "true" and string.format("dungeons", gameName), args["enemies"] == "true" and string.format("enemies", gameName), args["locations"] == "true" and string.format("locations", gameName), }	topics = utilsTable.compact(topics) local customTopics = args["custom-topics"] if customTopics then customTopics = utilsString.split(customTopics) topics = utilsTable.concat(topics, customTopics) end topics = mw.text.listToText(topics) local templatePurpose = string.format("This template displays quote bubbles containing %s's comments on %s in %s.", commenter, topics, gameDisplay) local templateSpec = utilsTable.merge(p.Templates[templateName], {		purpose = templatePurpose 	}) local templateDoc = Documentation.printTemplateDoc("Module:Comments", templateName, templateSpec) if examples then templateDoc = templateDoc .. utilsMarkup.heading(3, "Examples") .. "\n" templateDoc = templateDoc .. frame:preprocess(examples) end -- Guidelines templateDoc = templateDoc .. utilsMarkup.heading(2, "Guidelines") .. "\n" local customPlacementGuidelines = args["guidelines-placement"] local customFormattingGuidelines = args["guidelines-formatting"] if not utilsString.isEmpty(customPlacementGuidelines) then templateDoc = templateDoc .. frame:preprocess(customPlacementGuidelines) else templateDoc = templateDoc .. string.format("This template should be placed immediately underneath the %s heading, or under the first heading if the article is about %s specifically. There should be one line of space between the template transclusion and the article content.", gameDisplay, gameDisplay) local remakes = Franchise.remakes(game) local latestRemake = remakes[#remakes] if latestRemake then templateDoc = templateDoc .. string.format(" All quotes should reference the updated remake of the game, %s. If the comment from a previous iteration of the game is notably different, it can be included as a separate quote.", Franchise.link(latestRemake)) end end templateDoc = templateDoc .. "\n\n" if not utilsString.isEmpty(customFormattingGuidelines) then templateDoc = templateDoc .. frame:preprocess(customFormattingGuidelines) else templateDoc = templateDoc .. frame:preprocess("Use  tags to separate blocks of dialogue. A block of dialogue consists of all the text that appears before the player must prompt the dialogue to continue using their controller.") end return templateDoc end

local quoteDesc = string.format("The complete description given by %s on the subject, quoted verbatim. Use Template:Color, Template:Icon, Template:Player Name, and Template:Typo as needed.", mw.title.getCurrentTitle.baseText)

p.Templates = { SingleComment = { format = "inline", params = { [1] = {				name = "quote", required = true, type = "content", desc = quoteDesc, trim = true, nilIfEmpty = true, },			[2] = { -- necessary holdover from when the comment templates took two arguments here name = "_quote", deprecated = true, type = "content", trim = true, },		},	},	MultiComment = { format = "block", repeatedGroup = { name = "quotes", params = {"context", "quote"}, counts = {1, 2, 3, 4, 5, 6, 7, 8}, },		params = { [1] = {				name = "subject", type = "string", deprecated = true, -- Displaying the subject name is redundant - the subject is obvious from context inline = true, },			[2] = {				name = "singleQuote", type = "string", desc = "Use  instead.", deprecated = true, inline = true, },			quote = { required = true, type = "content", desc = quoteDesc, trim = true, nilIfEmpty = true, },			context = { type = "string", desc = "A description of where or when the quote appears. Only needed when there are multiple quotes in different contexts. Generally not needed for the first quote.", type = "string", trim = true, nilIfEmpty = true, },		},	} }

return p