Module:Find sources
-- This module implements {{find sources}} and other similar templates, and
 -- also provides a mechanism to easily create new source-finding templates.
 
 -- Define constants
 local ROOT_PAGE = 'Module:Find sources'
 local TEMPLATE_ROOT = ROOT_PAGE .. '/templates/' -- for template config modules
 local LINK_ROOT = ROOT_PAGE .. '/links/' -- for link config modules
 local CONFIG_PAGE = ROOT_PAGE .. '/config' -- for global config
 
 -- Load required modules
 local checkType = require('libraryUtil').checkType
 local cfg = mw.loadData(CONFIG_PAGE)
 
 local p = {}
 
 local function maybeLoadData(page)
 	local success, data = pcall(mw.loadData, page)
 	return success and data
 end
 
 local function substituteParams(msg, ...)
 	return mw.message.newRawMessage(msg, ...):plain()
 end
 
 local function renderSearchString(searchTerms, separator, transformFunc)
 	-- This takes a table of search terms and turns it into a search string
 	-- that can be used in a URL or in a display value. The transformFunc
 	-- parameter can be used to transform each search term in some way (for
 	-- example, URL-encoding them).
 	local searchStrings = {}
 	for i, s in ipairs(searchTerms) do
 		searchStrings[i] = s
 	end
 	if transformFunc then
 		for i, s in ipairs(searchStrings) do
 			searchStrings[i] = transformFunc(s)
 		end
 	end
 	return table.concat(searchStrings, separator)
 end
 
 function p._renderLink(code, searchTerms, display)
 	-- Renders the external link wikicode for one link, given the link code,
 	-- a table of search terms, and an optional display value.
 
 	-- Get link config.
 	local linkCfg = maybeLoadData(LINK_ROOT .. code)
 	if not linkCfg then
 		error(string.format(
 			&quot;invalid link code '%s'; no link config found at [[%s]]&quot;,
 			code,
 			LINK_ROOT .. code
 		))
 	end
 
 	-- Make URL.
 	local url
 	do
 		local separator = linkCfg.separator or &quot;+&quot;
 		local searchString = renderSearchString(
 			searchTerms,
 			separator,
 			mw.uri.encode
 		)
 		url = substituteParams(linkCfg.url, searchString)
 	end
 	
 	return string.format('[%s %s]', url, display or linkCfg.display)
 end
 
 function p._main(template, args)
 	-- The main access point from Lua.
 	checkType('_main', 1, template, 'string')
 	checkType('_main', 2, args, 'table', true)
 	args = args or {}
 	local title = mw.title.getCurrentTitle()
 
 	-- Get the template config.
 	local templateCfgPage = TEMPLATE_ROOT .. template
 	local templateCfg = maybeLoadData(templateCfgPage)
 	if not templateCfg then
 		error(string.format(
 			&quot;invalid template name '%s'; no template config found at [[%s]]&quot;,
 			template, templateCfgPage
 		))
 	end
 
 	-- Namespace check.
 	if not templateCfg.isUsedInMainspace and title.namespace == 0 then
 		local formatString = '&lt;strong class=&quot;error&quot;&gt;%s&lt;/strong&gt;'
 		if cfg['namespace-error-category'] then
 			formatString = formatString .. '[[%s:%s]]'
 		end
 		return string.format(
 			formatString,
 			cfg['namespace-error'],
 			mw.site.namespaces[14].name,
 			cfg['namespace-error-category']
 		)
 	end
 
 	-- Get the search terms from the arguments.
 	local searchTerms = {}
 	for i, s in ipairs(args) do
 		searchTerms[i] = s
 	end
 	searchTerms[1] = searchTerms[1] or title.subpageText
 	searchTerms[1] = '&quot;' .. searchTerms[1] .. '&quot;'
 
 	-- Make the intro link
 	local introLink
 	if templateCfg.introLink then
 		local code = templateCfg.introLink.code
 		local display = templateCfg.introLink.display or renderSearchString(
 			searchTerms,
 			'&amp;nbsp;'
 		)
 		introLink = p._renderLink(code, searchTerms, display)
 	else
 		introLink = ''
 	end
 
 	-- Make the other links
 	local links = {}
 	for i, t in ipairs(templateCfg.links) do
 		links[i] = p._renderLink(t.code, searchTerms, t.display)
 	end
 	local separator = templateCfg.separator or cfg['default-separator']
 	links = table.concat(links, separator)
 
 	-- Make the blurb.
 	local blurb = substituteParams(templateCfg.blurb, introLink, links)
 	local span = mw.html.create('span')
 	span
 		:addClass('plainlinks')
 		:addClass(templateCfg.class)
 		:cssText(templateCfg.style)
 		:wikitext(blurb)
 
 	return tostring(span)
 end
 
 setmetatable(p, { __index = function(t, template)
 	-- The main access point from #invoke.
 	-- Invocations will look like {{#invoke:Find sources|template name}},
 	-- where &quot;template name&quot; is a subpage of [[Module:Find sources/templates]].
 	return function(frame)
 		local args = require('Module:Arguments').getArgs(frame, {
 			wrappers = mw.site.namespaces[10].name .. ':' .. template
 		})
 		return t._main(template, args)
 	end
 end})