Modul:UKB

Fra Wikisida.no
Hopp til navigering Hopp til søk

Dokumentasjon for denne modulen kan opprettes på Modul:UKB/dok

require('strict')

local p = {}

local TNT = require('Module:TNT')
local I18NDATASET = 'I18n/UKB.tab'
local getArgs = require('Module:Arguments').getArgs

--- Get a localized message.
-- @param key The message key
-- @param ... Parameters to be passed to the message ($1, $2, etc.)
-- @return localized string
local function msg( key, ... )
	return TNT.format( I18NDATASET, key, ... )
end

--- Reverse a mapping to get a list of localized names => canonical names
-- @param mapping A table containing key-value pairs where the key is the canonical name and the value is an array table of aliases
-- @return A table of localized names => canonical names
local function mappingReverser(mapping)
	local ret = {}
	for canonical, synonyms in pairs(mapping) do
		for _, synonym in ipairs(synonyms) do
			ret[synonym] = canonical
		end
		local keyIsPresent, translations = pcall(msg, 'arg-' .. canonical)
		if keyIsPresent then
			translations = mw.text.split(translations, '|')
			for _, translation in ipairs(translations) do
				ret[translation] = canonical
			end
		end
	end
	return ret
end

--- Get the argument mapping for a type of item
-- @param itemType The mapping subtype to get. Either 'criteria' or 'rules'
-- @param returnType Which mapping to get; 'canonical' or 'translated'
-- @return A table of mappings
local function getArgumentMapping(itemType, returnType)
	-- if a new argument is added, it should also be added to the i18n module
	-- in [[c:Data:I18n/UKB.tab]]
	local argumentMapping = {
		['criteria'] = {
			['backlinks'] = { 'backlink' },
			['bytes'] = { 'byte' },
			['categories'] = { 'category' },
			['forwardlinks'] = { 'forwardlink' },
			['new'] = {},
			['existing'] = {},
			['namespaces'] = { 'namespace' },
			['pages'] = { 'page' },
			['sparql'] = {},
			['stub'] = {}, -- deprecated, not in i18n
			['templates'] = { 'template' }
		},
		['rules'] = {
			['bytes'] = { 'byte' },
			['bytebonus'] = {},
			['categoryremoval'] = {},
			['edit'] = {},
			['eligiblepage'] = {},
			['extlink'] = { 'exlink', 'externallink' },
			['image'] = { 'images' },
			['listbyte'] = { 'listbytes' },
			['newpage'] = {},
			['newredirect'] = {},
			['reference'] = { 'ref' },
			['section'] = {},
			['templateremoval'] = {},
			['wikidata'] = {},
			['word'] = { 'words' },
			['wordbonus'] = {}
		},
		['modifiers'] = {
			['aliases'] = {},
			['all'] = {},
			['description'] = {},
			['descriptions'] = {},
			['distinct'] = {},
			['ignore'] = {},
			['initialimagelimit'] = {},
			['labels'] = {},
			['max'] = {},
			['ownimage'] = {},
			['properties'] = {},
			['query'] = {},
			['requirereference'] = { 'require reference', 'require_reference' },
			['redirects'] = { 'redirect' },
			['site'] = {},
		}
	}

	if returnType == 'canonical' then
		return argumentMapping[itemType]
	end

	local translatedMap = {
		['criteria'] = mappingReverser(argumentMapping.criteria),
		['rules'] = mappingReverser(argumentMapping.rules),
		['modifiers'] = mappingReverser(argumentMapping.modifiers)
	}

	return translatedMap[itemType]
end

--[ Helper methods ] ------------------------------------------------------------------

--- Make an error string
-- @tparam string text Text to be wrapped in an error class
-- @treturn string The text wrapped in an error class
local function makeErrorString(text)
	local html = mw.html.create('strong')
		:addClass('error')
		:wikitext(text)
	return tostring(html)
end

--- Get an error string
-- @tparam string key A message key (from i18n)
-- @tparam string arg An argument to pass along to the message function
-- @treturn string An error message
local function getErrorString(key, arg)
	return makeErrorString(msg(key, arg))
end

--- Parse and translate anonymous and named arguments
-- @tparam table frame A frame object
-- @tparam string|nil itemType An item type to return ('criteria', 'rules' or nil)
-- @treturn table A table of anonymous arguments (args)
-- @treturn table A table of named arguments (kwargs)
local function parseArgs(frame, itemType, translate)
	local args = {}
	local kwargs = {}
	local canonicalMap = getArgumentMapping(itemType, 'translated')
	if itemType == nil then
		canonicalMap = {}
	end
	local kwargsMap = getArgumentMapping('modifiers', 'translated')
	for k, v in pairs(getArgs(frame)) do
		v = mw.text.trim(frame:preprocess(v))
		if v ~= '' then
			if type(k) == 'number' then
				if k == 1 and canonicalMap[v] ~= nil and translate then
					args[1] = canonicalMap[v]
				else
					args[k] = v
				end
			else
				if kwargsMap[k] ~= nil and translate then
					kwargs[kwargsMap[k]] = v
				else
					kwargs[k] = v
				end
			end
		end
	end
	return args, kwargs
end

--- Turn an array table into a string in list form
-- @tparam table items An array of items
-- @tparam string itemType Maybe unnecessary?
-- @tparam string word The strings 'or' or 'and' (representing i18n message keys)
-- @treturn string A string with the table returned as a list
local function listify(items, itemType, word)
	word = word or 'or'
	if #items == 0 then
		return getErrorString('anon-argument-missing', itemType)
	end
	if #items == 1 then
		return items[1]
	end
	return mw.text.listToText(items, ', ', ' ' .. msg(word) .. ' ' )
end

--- Get link data for a link to a page in a specific namespace
-- @tparam table frame A frame object
-- @tparam string ns A canonical (English) namespace name; 'Template' and 'Category' supported
-- @tparam string page A page name
-- @treturn table A table containing: language code, link target and page name
local function makeNsLink(frame, ns, page)
	local linkTarget
	local nsNumbers = {
		['Template'] = 10,
		['Category'] = 14
	}
	local lang, pageName = mw.ustring.match(page, '^([a-z]+):(.+)$') -- FIXME: Better language code detection
	if lang then
		-- English namespace name is guaranteed to work, avoids need to maintain
		-- lists of namespace names in the module
		linkTarget = mw.ustring.format(':%s:%s:%s', lang, ns, pageName)
	else
		linkTarget = mw.ustring.format(':%s:%s', frame:callParserFunction('ns', nsNumbers[ns]), page)
	end
	return {
		['lang'] = lang,
		['linkTarget'] = linkTarget,
		['pageName'] = pageName or page
	}
end

--- Make a link to a single template, wrapped in curly brace syntax
-- @tparam table frame A frame object
-- @tparam template Name of a template (optionally with an interlanguage prefix)
-- @treturn string An HTML string linking to the template in question
local function makeTemplateLink(frame, template)
	local nsLink = makeNsLink(frame, 'Template', template)
	local wikitext = mw.text.nowiki('{{') .. mw.ustring.format('[[%s|%s]]', nsLink['linkTarget'], nsLink['pageName']) .. mw.text.nowiki('}}')
	local html = mw.html.create('span')
		:addClass('template-link')
		:css('font-family', 'monospace,monospace')
		:wikitext(wikitext)
	return tostring(html)
end

--- Make a link to a single category
-- @tparam table frame A frame object
-- @tparam category Name of a category (optionally with an interlanguage prefix)
-- @treturn string An HTML string linking to the category in question
local function makeCategoryLink(frame, category)
	local nsLink = makeNsLink(frame, 'Category', category)
	return mw.ustring.format('[[%s|%s]]', nsLink['linkTarget'], nsLink['pageName'])
end

--- Make a list of templates
-- @tparam table frame A frame object
-- @tparam table args An array of template names (optionally with interlanguage prefixes)
-- @treturn table A table of template links
local function makeTemplateList(frame, args)
    local templates = {}
    for i, v in ipairs(args) do
    	table.insert(templates, makeTemplateLink(frame, v))
    end
    setmetatable(templates, {
    	__tostring = function(self)
    		return listify(templates, 'templates')
		end
    })
    return templates
end

--- Make a list of categories
-- @tparam table frame A frame object
-- @tparam table args An array of category names (optionally with interlanguage prefixes)
-- @treturn table A table of category links
local function makeCategoryList(frame, args)
    local categories = {}
    for i, v in ipairs(args) do
        v = mw.text.trim(v)
        if v ~= '' then
            table.insert(categories, makeCategoryLink(frame, v))
        end
    end
    setmetatable(categories, {
    	__tostring = function(self)
    		return listify(categories, 'categories')
		end
    })
    return categories
end

--- Make a list of templates
-- @tparam table args An array of page names (optionally with interlanguage prefixes)
-- @treturn table A table of page links
local function makePageList(args)
    local pages = {}
    for i, v in ipairs(args) do
        v = mw.text.trim(v)
        if v ~= '' then
            local lang, page = string.match(v, '^([a-z]+):(.+)$')
            if lang then
                table.insert(pages, string.format('[[:%s:%s|%s]]', lang, page, page))
            else
                table.insert(pages, string.format('[[:%s]]', v))
            end
        end
    end
    setmetatable(pages, {
    	__tostring = function(self)
    		return listify(pages, 'pages')
		end
    })
    return pages
end

--- Make a list of namespaces
-- @tparam table args An array of namespace IDs
-- @treturn table A table of namespace names
local function makeNsList(args)
    local namespaces = {}
    local namespaceName = msg('article')
    for _, namespaceId in ipairs(args) do
        namespaceId = mw.text.trim(namespaceId)
        if namespaceId ~= '' then
            if namespaceId ~= "0" then
                namespaceName = '{{lc:{{ns:' .. namespaceId .. '}}}}'
            end
            table.insert(namespaces, namespaceName)
        end
    end
    setmetatable(namespaces, {
    	__tostring = function(self)
    		return listify(namespaces, 'namespaces')
		end
    })
    return namespaces
end

--[ Criterion format methods ]-------------------------------------------------------------

local criterion = {}

--- Formatter function for the backlinks criterion
-- @tparam table args Anonymous arguments to the module
-- @tparam table kwargs Keyword arguments to the module
-- @treturn string A message corresponding to the criterion
function criterion.backlinks(args, kwargs, frame)
	local pageList = makePageList(args)
	return msg('criterion-backlinks', #pageList, tostring(pageList))
end

--- Formatter function for the bytes criterion
-- @tparam table args Anonymous arguments to the module
-- @tparam table kwargs Keyword arguments to the module
-- @treturn string A message corresponding to the criterion
function criterion.bytes(args, kwargs, frame)
	return msg('criterion-bytes', args[1])
end

--- Formatter function for the categories criterion
-- @tparam table args Anonymous arguments to the module
-- @tparam table kwargs Keyword arguments to the module
-- @treturn string A message corresponding to the criterion
function criterion.categories(args, kwargs, frame)
	local categoryList = makeCategoryList(frame, args)
	local ret = msg('criterion-categories', #categoryList, tostring(categoryList))

	if kwargs.ignore ~= nil then
		local ignoredCats = mw.text.split(kwargs.ignore, ',')
		ignoredCats = makeCategoryList(frame, ignoredCats)
		ret = ret .. msg('categories-except', #ignoredCats, tostring(ignoredCats))
	end

    return ret
end

--- Formatter function for the existing criterion
-- @tparam table args Anonymous arguments to the module
-- @tparam table kwargs Keyword arguments to the module
-- @treturn string A message corresponding to the criterion
function criterion.existing(args, kwargs, frame)
	return msg('criterion-existing')
end

--- Formatter function for the forwardlinks criterion
-- @tparam table args Anonymous arguments to the module
-- @tparam table kwargs Keyword arguments to the module
-- @treturn string A message corresponding to the criterion
function criterion.forwardlinks(args, kwargs, frame)
	local pages = makePageList(args)
    return msg('criterion-forwardlinks', #pages, tostring(pages))
end

--- Formatter function for the namespaces criterion
-- @tparam table args Anonymous arguments to the module
-- @tparam table kwargs Keyword arguments to the module
-- @treturn string A message corresponding to the criterion
function criterion.namespaces(args, kwargs, frame)
	local nsList = makeNsList(args)
	local message

	if #nsList == 1 and args[1] == '0' then
		message = msg('criterion-namespace-0')
	else
		message = msg('criterion-namespace', #nsList, tostring(nsList))
	end

	if kwargs.site ~= nil then
		return msg('page-at-site', message, mw.ustring.format('[https://%s %s]', kwargs.site, kwargs.site))
	end

	return message
end

--- Formatter function for the new page criterion
-- @tparam table args Anonymous arguments to the module
-- @tparam table kwargs Keyword arguments to the module
-- @treturn string A message corresponding to the criterion
function criterion.new(args, kwargs, frame)
	if kwargs.redirects ~= nil then
		return msg('criterion-new-with-redirects')
	end
	return msg('criterion-new')
end

--- Formatter function for the pages (page list) criterion
-- @tparam table args Anonymous arguments to the module
-- @tparam table kwargs Keyword arguments to the module
-- @treturn string A message corresponding to the criterion
function criterion.pages(args, kwargs, frame)
	local pages = makePageList(args)
	return msg('criterion-pages', #pages, tostring(pages))
end

--- Formatter function for the SPARQL criterion
-- @tparam table args Anonymous arguments to the module
-- @tparam table kwargs Keyword arguments to the module
-- @treturn string A message corresponding to the criterion
function criterion.sparql(args, kwargs, frame)
	local query = ''
	if kwargs.distinct ~= nil then
		query = 'SELECT DISTINCT ?item WHERE {\n  ' .. kwargs.query .. '\n}'
	else
		query = 'SELECT ?item WHERE {\n  ' .. kwargs.query .. '\n}'
	end
    local url = 'http://query.wikidata.org/#' .. mw.uri.encode(query, 'PATH')

    if kwargs.description ~= nil then
        return msg('criterion-sparql-with-explanation', kwargs.description, url)
    end

    return msg('criterion-sparql', url)
end

--- Formatter function for the templates criterion
-- @tparam table args Anonymous arguments to the module
-- @tparam table kwargs Keyword arguments to the module
-- @treturn string A message corresponding to the criterion
function criterion.templates(args, kwargs, frame)
	local templates = makeTemplateList(frame, args)
	return msg('criterion-templates', #templates, tostring(templates))
end

--- Main function for getting criterion messages
-- @tparam table frame A frame object
-- @treturn string A string representing the criterion (or an error message string)
function p.criterion(frame)
	local args, kwargs = parseArgs(frame, 'criteria', true)
	local criterionArg = table.remove(args, 1)
	local permittedCriteria = getArgumentMapping('criteria', 'canonical')

    if criterionArg == nil or criterionArg == '' then
        return frame:preprocess(getErrorString('argument-missing', 'criterion'))
    elseif permittedCriteria[criterionArg] == nil or criterion[criterionArg] == nil then
    	return frame:preprocess(getErrorString('invalid-criterion', criterionArg))
    end

    -- Use manual description if given
    if kwargs.description ~= nil and criterionArg ~= 'sparql' then
        return kwargs.description
    end

	return frame:preprocess(criterion[criterionArg](args, kwargs, frame))
end

--[ Rule format methods ]-------------------------------------------------------------

local rule = {}

--- Formatter function for custom rules
-- @tparam number points A number of points; may by a float
-- @tparam table args Anonymous arguments to the module
-- @tparam table kwargs Keyword arguments to the module
-- @treturn string A message corresponding to the rule
function rule.custom(points, args, kwargs, frame)
	return msg('rule-custom', points, kwargs.description)
end

--- Formatter function for image rules
-- @tparam number points A number of points; may by a float
-- @tparam table args Anonymous arguments to the module
-- @tparam table kwargs Keyword arguments to the module
-- @treturn string A message corresponding to the rule
function rule.image(points, args, kwargs, frame)
    local out
    local tplargs = {
        ['points'] = points,
    }
    if kwargs.initialimagelimit ~= nil then
    	out = msg('rule-image-limited', points, kwargs.initialimagelimit)
    else
        out = msg('rule-image', points)
    end
    if kwargs.ownimage ~= nil then
        out = out .. ' ' .. msg('rule-image-own', kwargs.ownimage)
    end
    return out
end

--- Formatter function for Wikidata rules
-- @tparam number points A number of points; may by a float
-- @tparam table args Anonymous arguments to the module
-- @tparam table kwargs Keyword arguments to the module
-- @treturn string A message corresponding to the rule
function rule.wikidata(points, args, kwargs, frame)
    local out
    local params
    local argTypes = { msg('properties'), msg('labels'), msg('aliases'), msg('descriptions') }
    local results = {}
    if kwargs.properties == nil and kwargs.labels == nil and kwargs.aliases == nil and kwargs.descriptions == nil then
        return getErrorString('argument-missing', listify(argTypes))
    end
    if kwargs.properties ~= nil then
        params = mw.text.split(kwargs.properties, ',')
        for k, v in pairs(params) do
            params[k] = string.format('[[:d:Property:%s|%s]]', v, v)
        end
        table.insert(results, listify(params))
    end
    if kwargs.labels ~= nil then
        params = mw.text.split(kwargs.labels, ',')
        table.insert(results, msg('label') .. ' (' .. listify(params) .. ')')
    end
    if kwargs.aliases ~= nil then
        params = mw.text.split(kwargs.aliases, ',')
        table.insert(results, msg('alias') .. ' (' .. listify(params) .. ')')
    end
    if kwargs.descriptions ~= nil then
        params = mw.text.split(kwargs.descriptions, ',')
        table.insert(results, msg('description') .. ' (' .. listify(params) .. ')')
    end
    results = table.concat( results, ' ' .. msg('and') .. ' ' )
    if kwargs.all ~= nil then
        out = msg('rule-wikidata-all', points, results)
    else
        out = msg('rule-wikidata-first', points, results)
    end
    if kwargs.requireReference ~= nil then
        out = out .. ' ' .. msg('rule-wikidata-require-reference')
    end
    return out
end

--- Formatter function for reference rules
-- @tparam number points A number of points; may by a float
-- @tparam table args Anonymous arguments to the module
-- @tparam table kwargs Keyword arguments to the module
-- @treturn string A message corresponding to the rule
function rule.reference(points, args, kwargs, frame)
	return msg('rule-reference', points, args[1])
end

--- Formatter function for template removal rules
-- @tparam number points A number of points; may by a float
-- @tparam table args Anonymous arguments to the module
-- @tparam table kwargs Keyword arguments to the module
-- @treturn string A message corresponding to the rule
function rule.templateremoval(points, args, kwargs, frame)
	local templateList = makeTemplateList(frame, args)
    return msg('rule-templateremoval', points, #templateList, tostring(templateList))
end

--- Formatter function for category removal rules
-- @tparam number points A number of points; may by a float
-- @tparam table args Anonymous arguments to the module
-- @tparam table kwargs Keyword arguments to the module
-- @treturn string A message corresponding to the rule
function rule.categoryremoval(points, args, kwargs, frame)
	local categoryList = makeCategoryList(args)
	return msg('rule-categoryremoval', points, #categoryList, tostring(categoryList))
end

--- Formatter function for section adding rules
-- @tparam number points A number of points; may by a float
-- @tparam table args Anonymous arguments to the module
-- @tparam table kwargs Keyword arguments to the module
-- @treturn string A message corresponding to the rule
function rule.section(points, args, kwargs, frame)
    if kwargs.description ~= nil then
    	return msg('rule-section-desc', points, kwargs.description)
    end
    return msg('rule-section', points, #args, listify(args))
end

--- Formatter function for byte bonus rules
-- @tparam number points A number of points; may by a float
-- @tparam table args Anonymous arguments to the module
-- @tparam table kwargs Keyword arguments to the module
-- @treturn string A message corresponding to the rule
function rule.bytebonus(points, args, kwargs, frame)
	return msg('rule-bytebonus', points, args[1])
end

--- Formatter function for word bonus rules
-- @tparam number points A number of points; may by a float
-- @tparam table args Anonymous arguments to the module
-- @tparam table kwargs Keyword arguments to the module
-- @treturn string A message corresponding to the rule
function rule.wordbonus(points, args, kwargs, frame)
	return msg('rule-wordbonus', points, args[1])
end

--- Main function for getting criterion messages
-- @tparam table frame A frame object
-- @treturn string A string representing the rule (or an error message string)
function p.rule(frame)
	local args, kwargs = parseArgs(frame, 'rules', true)
	local ruleArg = table.remove(args, 1)
	local points = table.remove(args, 1)
	local permittedRules = getArgumentMapping('rules', 'canonical')

    if ruleArg == nil or ruleArg == '' then
        return frame:preprocess(getErrorString('argument-missing', 'rule'))
    elseif permittedRules[ruleArg] == nil then
    	return frame:preprocess(getErrorString('invalid-rule', ruleArg))
    end

    if kwargs.description ~= nil then
        ruleArg = 'custom'
    end

    -- All rules requires argument 1: number of points awarded
    if points == nil then
        return frame:preprocess(getErrorString('argument-missing', '1 (number of points)'))
    end

	points = mw.language.getContentLanguage():formatNum(tonumber(points))

    -- If there's a rule formatter function, use it.
    -- Otherwise, use the string from the messages table.
    local out
    if rule[ruleArg] ~= nil then
        out = rule[ruleArg](points, args, kwargs, frame)
    else
    	-- It shouldn't be necessary to check if the message exists here, because
    	-- of the previous check against permittedRules above
        out = msg('rule-' .. ruleArg, points)
    end

    if kwargs.site ~= nil then
        out = msg('rule-site', out, mw.ustring.format('[https://%s %s]', kwargs.site, kwargs.site))
    end

    if kwargs.max ~= nil then
        out = msg('base-rule-max', out, mw.language.getContentLanguage():formatNum(tonumber(kwargs.max)))
    end

    return frame:preprocess(out)
end

--- Function to generate documentation for a module or template using this module
-- Not implemented yet
function p.generateDocs(frame)
	-- Generate documentation subpage for templates using the module
end

--- Function to get warnings about duplicate or invalid i18n values
-- Not implemented yet
function p.getI18nWarnings(frame)
	-- Function to be used on /doc page, to report any duplicate arguments
	-- from the i18n, and potentially other things that should be fixed in the
	-- i18n for the current language.
end

--- Get a single message string from the module's i18n, localized into the page
--- if possible
-- @tparam table frame A frame object
-- @treturn string A formatted message (or an HTML error string if the key doesn't exist)
function p.getMessage(frame)
	local args, kwargs = parseArgs(frame, nil, false)
	local key = table.remove(args, 1)
	local exists, message = pcall(msg, key, args)
	if exists then
		if mw.isSubsting() then
			-- substitute magic words etc. if the module proper is being substed
			message = mw.ustring.gsub( message, '{{(#?%a+):', '{{subst:%1:' )
		end
		return frame:preprocess(message)
	else
		return getErrorString('message-key-missing', key)
	end
end

--- Function to get i18n data for use by the bot
-- @treturn string A JSON-encoded string of all keys and (localized) values from the i18n dataset
function p.getAllI18n()
	local lang = mw.title.getCurrentTitle().pageLang:getCode()
	local sensible = {}
	local i18n = mw.ext.data.get(I18NDATASET, lang)['data']
	for _,v in ipairs(i18n) do
		-- turn the array of message objects into a sensible key->value mapping
		sensible[v[1]] = v[2]
	end
	return mw.text.jsonEncode(sensible)
end

--- Function to get a prize ribbon with a specified color
-- Takes three parameters: A file name or a color, a width, and a link target
-- @tparam table frame A frame object
-- @treturn string
function p.getRibbon(frame)
	local args = getArgs(frame)
	local color = args[1] or 'white'
	local width = args[2] or '20px'
	local link = args[3] or 'User:UKBot'
	local alt = msg('ribbon')
	if string.find( color, '%.%a%a%a%a?$' ) then
		return '[[File:' .. color .. '|' .. width .. '|alt=' .. alt .. '|link=' .. link .. ']]'
	end
	local svgContent = [[
<defs>
	<linearGradient id="path10250_1_" x1="301.8" x2="456.98" y1="663.18" y2="609.32" gradientTransform="translate(-301.52 -390.52)" gradientUnits="userSpaceOnUse">
	 <stop offset="0"/>
	 <stop stop-opacity="0" offset="1"/>
	</linearGradient>
</defs>
<filter id="filter3" x="-.17993" y="-.11726" width="1.3779" height="1.2582">
	<feGaussianBlur stdDeviation="4"/>
</filter>
<filter id="filter2" x="-.093808" y="-.094418" width="1.1878" height="1.1882">
	<feGaussianBlur stdDeviation="5"/>
</filter>
<g enable-background="new" filter="url(#filter2)" opacity=".8">
	<polygon points="145.65 230.41 122.62 201.35 100.76 231.31 91.545 195.39 59.394 213.88 65.582 177.31 28.69 181.12 49.217 150.23 13.965 138.71 45.281 118.84 17.764 93.973 54.454 88.559 39.429 54.651 75.149 64.627 75.215 27.54 103.79 51.183 118.94 17.329 135.42 50.55 163.03 25.782 164.58 62.837 199.87 51.439 186.21 85.921 223.09 89.863 196.59 115.81 228.68 134.41 193.91 147.34 215.66 177.38 178.65 175.05 186.29 211.34 153.43 194.15"/>
	<polygon points="145.65 230.41 122.62 201.35 100.76 231.31 91.545 195.39 59.394 213.88 65.582 177.31 28.69 181.12 49.217 150.23 13.965 138.71 45.281 118.84 17.764 93.973 54.454 88.559 39.429 54.651 75.149 64.627 75.215 27.54 103.79 51.183 118.94 17.329 135.42 50.55 163.03 25.782 164.58 62.837 199.87 51.439 186.21 85.921 223.09 89.863 196.59 115.81 228.68 134.41 193.91 147.34 215.66 177.38 178.65 175.05 186.29 211.34 153.43 194.15" fill="none" stroke="#000" stroke-linecap="round" stroke-miterlimit="7.5" stroke-width="7"/>
</g>
<path d="m124.48 175.04 28.859-1.697 44.129 85.395-30.805-8.571-30.813 27.146z" fill="url(#path10250_1_)"/>
<g enable-background="new" filter="url(#filter3)" opacity=".8">
	<polygon points="195.77 267.59 164.96 259.02 134.15 286.16 122.78 183.89 151.64 182.19"/>
	<polygon points="195.77 267.59 164.96 259.02 134.15 286.16 122.78 183.89 151.64 182.19" fill="none" stroke="#000" stroke-linecap="round" stroke-miterlimit="7.5" stroke-width="5"/>
	<polygon points="100.53 312.47 71.422 283.52 38.911 295.39 81.015 189.72 116.66 193.12"/>
	<polygon points="100.53 312.47 71.422 283.52 38.911 295.39 81.015 189.72 116.66 193.12" fill="none" stroke="#000" stroke-linecap="round" stroke-miterlimit="7.5" stroke-width="5"/>
</g>
<g fill="COLOR" stroke="#000" stroke-linecap="round" stroke-miterlimit="7.5">
	<path d="m123.03 174.57 28.859-1.697 44.127 85.393-30.804-8.57-30.813 27.145z" stroke-width="5.6384"/>
	<path d="m78.469 182.21 35.65 3.396-16.138 119.34-29.106-28.943-32.511 11.866z" stroke-width="5.6384"/>
	<path d="m186.29 211.34-32.865-17.186-7.774 36.264-23.035-29.066-21.853 29.965-9.22-35.924-32.152 18.486 6.188-36.566-36.891 3.812 20.526-30.889-35.253-11.525 31.316-19.87-27.517-24.865 36.69-5.415-15.025-33.908 35.721 9.977 0.065-37.087 28.575 23.643 15.145-33.855 16.487 33.221 27.605-24.768 1.551 37.055 35.291-11.398-13.654 34.481 36.877 3.942-26.5 25.946 32.086 18.601-34.763 12.927 21.746 30.043-37.014-2.332z" stroke-width="7" style="paint-order:normal"/>
</g>
]]
	svgContent = mw.ustring.gsub(svgContent, 'COLOR', color)
	local svg = mw.svg.new()
	svg:setAttribute('viewBox', '0 0 245 320')
	svg:setContent(svgContent)
	svg:setImgAttribute('width', width)
	svg:setImgAttribute('alt', alt)
	return '[[' .. link .. '|' .. svg:toImage() .. ']]'
end

return p