Module:UtilsMarkup/Link

local p = {} local h = {}

local utilsMarkup = require("Module:UtilsMarkup/Format") local utilsPage = require("Module:UtilsPage") local utilsString = require("Module:UtilsString") local utilsTable = require("Module:UtilsTable")

function p.anchor(id, display) return string.format(' %s ', id, id, display or id) end

function p.category(name, sortKey) if not utilsString.startsWith(name, "Category:") then name = "Category:" .. name end return h.internalLink(name, sortKey) end

function p.categories(categoryNames) local categoryLinks = utilsTable.map(categoryNames, p.category) return table.concat(categoryLinks) end

function p.containsLink(wikitext, external) return wikitext ~= nil and not not wikitext:find("%[%[.*%]%]") end

function p.file(name, options) options = options or {} local size = options.size local link = options.link local caption = options.caption local class = options.class local noBacklink = options.noBacklink if not utilsString.startsWith(name, "File:") then name = "File:" .. name end if link and noBacklink then link = tostring(mw.uri.fullUrl(link)) end local linkParts = utilsTable.compact({		name,		size,		link and "link="..link,		class and "class="..class,		caption,	}) local fileLink = "") .. "" return fileLink end

function p.killBacklinks(wikitext) local link = string.match(wikitext, "%[%^%*%]%]") if not link then return wikitext end local startPos, endPos = string.find(wikitext, link, 1, true) local before = string.sub(wikitext, 1, startPos -1) local after = string.sub(wikitext, endPos + 1) local externalLink = p.killBacklink(link) return before .. externalLink .. p.killBacklinks(after) end

function p.killBacklink(link) local linkParts = utilsString.trim(link, "][") local pipe = string.find(linkParts, "|") local page = string.sub(linkParts, 1, pipe and pipe - 1) local display = pipe and string.sub(linkParts, pipe + 1) local startsWithColon = utilsString.startsWith(page, ":") page = string.gsub(page, "^:", "") local title = mw.title.new(page) if not title or title.interwiki ~= "" or not startsWithColon and (title.nsText == "Category" or title.nsText == "File") then return link end return p.link(page, display or page, true) end

-- If backlink == false then this print an external link that looks like an internal one -- (useful for creating links that do not appear in Special:WhatLinksHere) function p.link(link, text, noBacklink) if not link then return "" end link = mw.text.trim(link) if link:find('http') then return h.externalLink(link, text) end if noBacklink then local url = utilsPage.fullUrl(link) local externalLink = h.externalLink(url, text or link) local formattedLink = utilsMarkup.class("plainlinks", externalLink) return formattedLink end local title = mw.title.new(link) or link if title.nsText == "File" or title.nsText == "Category" then link = ":" .. link if text == "" then text = title.text end end return h.internalLink(link, text) end function h.internalLink(page, display) if not page then return "" end if not display then return ("%s"):format(page) end return ('%s'):format(page, display) end function h.externalLink(page, display) if not page then return "" end if not display then return page end return ('[%s %s]'):format(page, display) end

function p.sectionLink(page, section, display) local target = page if section then target = target .. "#" .. section end return p.link(target, display) end

local categoryPattern = "%[%[Category:[^%]]*%]%]" function p.stripCategories(wikitext) local categories = {} for category in string.gmatch(wikitext, categoryPattern) do		local categoryWithoutSortKey = string.gsub(category, "|[^%]]*%]%]", "]]") local categoryLink = string.gsub(categoryWithoutSortKey, "Category", ":Category") if not utilsTable.keyOf(categories, categoryLink) then table.insert(categories, categoryLink) end end return string.gsub(wikitext, categoryPattern, ""), categories end

function p.Schemas return { anchor = { id = { type = "string", required = true, },			display = { type = "string", },		},		link = { page = { type = "string", required = true, desc = "The link target. Can be the name of a page on the wiki or an external URL.", },			display = { type = "string", default = "page", desc = "The text to display for the link.", },			noBacklink = { type = "boolean", desc = 					When, renders an internal link such that it does not register on the .. " Special:WhatLinksHere " .. of the target page.					Use only when certain that the page exists. This method cannot render red-links (i.e. distinguish non-existent pages).				, },		},		sectionLink = { page = { type = "string", required = true, desc = "The name of a wiki page to target.", },			section = { type = "string", desc = "The name of the page section to target.", },			display = { type = "string", default = "page", desc = "The text to display for the link.", },		},		file = { name = { type = "string", required = true, desc = "A file name, with or without the  prefix.", },			options = { type = "record", properties = { {						name = "size", type = "string", desc = "Image size in pixels.", },					{						name = "link", type = "string", desc = "Name of a page on the wiki or an external URL for the image thumbnail to link to.", },					{						name = "caption", type = "string", desc = "Alt text for the image.", },					{						name = "class", type = "string", desc = "Sets the HTML classes of the image element.", },					{						name = "noBacklink", type = "boolean", desc = "When, the   property is rendered such that it does not register on the Special:WhatLinksHere of the target page. " .. "It also does not register on Special:WantedPages if the page doesn't exist.", }				}			},		},		category = { name = { type = "string", required = true, desc = "The name of the category to link to (with or without namespace prefix).", },			sortKey = { type = "string", desc = "A custom for the category entry.", }		},		categories = { names = { type = "array", required = true, items = { type = "string" }, desc = "An array of category names, with or without the namespace prefix.", }		},		killBacklinks = { wikitext = { type = "string", required = true, desc = "A string of wiki markup.", },		},		stripCategories = { wikitext = { type = "string", required = true, desc = "A string of wiki markup.", },		}	} end

function p.Documentation return { anchor = { params = {"id", "display"}, returns = "A link that refers to itself. Useful for linking to specific sections of a page without necessarily using headings.", cases = { {					args = {"foo"}, expect = ' foo ', },				{					args = {"bar", "Bar"}, expect = ' Bar ', },				{					args = {"foo bar"}, expect = ' foo bar ', },			},		},		link = { params = {"page", "display", "noBacklink"}, returns = "A string of wikitext that evaluates to a link. Will display a red link if the target is an internal link to a page that doesn't exist.", cases = { {					desc = "Internal link", args = { "Princess Zelda" }, expect = "Princess Zelda", },				{					desc = "Piped link", args = { "Princess Zelda", "Zelda" }, expect = "Zelda", },				{					desc = "Category link", args = { "Category:Items" }, expect = "Category:Items", },				{					desc = "Category link without prefix", args = { "Category:Items", "" }, expect = "Items", },				{					desc = "Category link with display text", args = { "Category:Items in Breath of the Wild", "Items in Breath of the Wild" }, expect = "Items in Breath of the Wild", },				{					desc = "File link (to file description)", args = { "File:MM Deku Butler Artwork.png" }, expect = "File:MM Deku Butler Artwork.png", },				{					desc = "External link", args = { "https://www.mariowiki.com/Thwomp", "Thwomp" }, expect = "Thwomp", },				{					desc = "Looks like an internal link, but it's actually an external link. This is to avoid Special:WhatLinksHere", args = { "Princess Zelda", "Zelda", true }, expect = [[ Zelda ]], },				{					-- Doing red links here would require checking page existence, which requires a database read and is considered an "expensive function" by MediaWiki -- Given how often this function can be called on any given page, it's probably best to avoid this desc = "When  is set to , red links are not rendered for pages which do not exist, for performance reasons.", args = { "Flippityfloppityfloo", nil, true }, expect = [[ Flippityfloppityfloo ]], },			},		},		sectionLink = { params = {"page", "section", "display"}, returns = "A string of wikitext rendering a section link.", cases = { {					args = {"Link", "Ocarina of Time", "Hero of Time"}, expect = "Hero of Time", },				{					args = {"Link", nil, "Hero of Time"}, expect = "Hero of Time", },				{					args = {"Link", ""}, expect = "Link", },			},		},		file = { params = {"name", "options"}, returns = "A  of wikitext that renders a thumbnail for the file.", cases = { {					args = { "File:ALttP Ganon Sprite.png" }, expect = "", },				{					args = { "ALttP Ganon Sprite.png", { link = "", caption = "Ganon" }},					expect = "", },				{					args = { "File:TWW Great Fairy Figurine Model.png", { link = "Great Fairy", size = "100px", }},					expect = "", },				{					args = { "File:TWW Great Fairy Figurine Model.png", { link = "Great Fairy", size = "100px", noBacklink = true, }},					expect = "", },				{					args = { "File:TWW Great Fairy Figurine Model.png", { link = "Great Fairy", size = "100px", class = "notpageimage", }},					expect = "", },			},		},		category = { params = {"name", "sortKey"}, returns = "A category link—the kind that adds a category to a page. For the other kind that links to the actual category page, use .", cases = { outputOnly = true, {					desc = "Category link from category name", args = { "Items" }, expect = "" },				{					desc = "Category link from category name with namespace prefix", args = { "Category:Items" }, expect = "", },				{					desc = "Category link with sort key", args = { "Items", "*" }, expect = "", },			},		},		categories = { params = {"names"}, returns = "A concatenated string of category links.", cases = { outputOnly = true, {					args = { {"Link", "Category:Zelda", "Category:Ganon"} }, expect = "" },				{					args = { {} }, expect = "" },			},		},		containsLink = { params = {"wikitext"}, returns = "Given a string of wikitext, returns  if it contains an internal or interwiki link, false otherwise.", cases = { {					args = {"There is a Link"}, expect = true, },				{					args = {"[Link] is not a link but rather an indication of replaced words in a quote."}, expect = false, },			},		},		killBacklinks = { params = {"wikitext"}, returns = "The same wikitext but with all the internal links formatted as external links, to avoid additions to Special:WhatLinksHere.", cases = { {					-- See "link" function for peformance reasons -- It might still be possible for us to support red links here if the performance requirements aren't as stringent as the "link" function -- There doesn't seem to be a pressing need at the moment, though desc = "Escapes basic article links - unfortunately non-existent pages are not rendered as red links for performance reasons", args = { "Link, Zelda, and Jean-Guy Rubber Boots" }, expect = ' Link, Zelda , and Jean-Guy Rubber Boots ' },				{					desc = "Does not escape file links or category links", args = { " and "}, expect = " and " },				{					desc = "Escapes visible file and category links", args = { "Category:Link, Zelda, and File:SS Blessed Butterfly Icon.png"}, expect = ' Category:Link, ' .. ' Zelda, ' .. 'and ' },				{					desc = "Does not affect interwiki links", args = { "Chozo" }, expect = "Chozo" }			},		},		stripCategories = { params = {"wikitext"}, returns = { "The string of wiki markup with all the category links removed (not including visual links to category description pages).", "A  array of direct links to the removed categories.", },			cases = { outputOnly = true, {					desc = "Basic use case", args = {"<>"}, expect = { "<>",						{"Category:Link", "Category:Princess Zelda"}, },				},				{					desc = "Does not affect visual links to category description pages", args = {"Category:Link and Category:Princess Zelda"}, expect = { "Category:Link and Category:Princess Zelda", {},					}				}			}		}	} end

return p