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")

-- 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 local url = mw.uri.fullUrl(link) local isInterwiki = not url or url.host ~= mw.uri.new(mw.title.getCurrentTitle:fullUrl).host if noBacklink and not isInterwiki then local pageExists = utilsPage.exists(link) or utilsString.startsWith(link, "Special") if not pageExists then url:extend({				action = "edit",				redlink = "1"			}) end local externalLink = h.externalLink(tostring(url), text) local formattedLink = utilsMarkup.class(pageExists and "plainlinks" or "plainlinks new", 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 p.sectionLink(page, section, display) local target = page if section then target = target .. "#" .. section end return p.link(target, display) 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.file(name, options) options = options or {} local size = options.size local link = options.link local caption = options.caption or name if not utilsString.startsWith(name, "File:") then name = "File:" .. name end local linkParts = utilsTable.compact({		name,		size,		link and "link="..link,		caption,	}) local fileLink = "") .. "" return fileLink end

function p.gameFile(game, subject, type, options) local parts = utilsTable._filter(utilsString.notEmpty)({game, subject, type}) local filename = table.concat(parts, " ") .. ".png" if utilsPage.exists("File:" .. filename) then return p.file(filename, options) else local uploadUrl = mw.uri.fullUrl("Special:Upload") uploadUrl:extend({			wpDestFile = filename		}) local options = utilsTable.merge({}, options, {			link = tostring(uploadUrl)		}) return p.file("File:No Image Upload.png", options) end 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.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) if utilsString.startsWith(link, "[[Category:") then		return link	end	if utilsString.startsWith(link, "[[File:") then		return link -- not dealing with this now, but it's doable	end	link = mw.text.trim(link, "][")	link = string.gsub(link, "^:", "")	local pipe = string.find(link, "|")	local page = string.sub(link, 1, pipe and pipe - 1)	local display = pipe and string.sub(link, pipe + 1)	return p.link(page, display or page, true) 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

local fileOptions = { 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.", },	} }

p.Schemas = { 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. " .. "It also does not register on Special:WantedPages if the page doesn't exist." },	},	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 = fileOptions },	gameFile = { game = { type = "string", required = true, desc = "A game code." },		subject = { type = "string", required = true, },		type = { type = "string", required = true, enum = {"", "Artwork", "Icon", "Model", "Render", "Screenshot", "Sprite", "Texture"}, },		options = fileOptions, },	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.", },	} }

p.Documentation = { 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 = "Link to page that doesn't exist", args = {"Flippityfloppityfloo"}, expect = "Flippityfloppityfloo" },			{				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 ]], },			{				desc = "Looks like an internal red link, but it's actually an external link. This is to avoid Special:WantedPages", args = { "Flippityfloppityfloo", "foobar", true }, expect = [[ foobar ]], },		},	},	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 = "", },		},	},	gameFile = { params = {"game", "subject", "type", "options"}, returns = "A  of wikitext that renders a thumbnail.", cases = { {				args = {"TWW", "Great Fairy Figurine", "Model", { link = "Great Fairy", size = "100px" }},				expect = "", },			{				desc = "If file does not exist, show 'click to upload' thumbnail which links to Special:Upload.", args = {"TWWHD", "Great Fairy Figurine", "Model", { link = "Great Fairy", size = "100px" }},				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 = "" },		},	},	killBacklinks = { params = {"wikitext"}, returns = "The same wikitext but with all the internal links formatted as external links, to avoid additions to Special:WhatLinksHere.", cases = { {				desc = "Escapes both normal links and red links", 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", {},				}			}		}	} }

return p