Module:T

--

-- A feature-packed example generator for brace-based wikitext. -- -- @module T -- @alias p -- @version 0.6.4 -- @release experimental -- @requires Module:Arguments -- @requires Module:User error -- @requires Module:Yesno -- @author User:DarthKitty -- @author User:Speedit -- @author User:ExE Boss -- -- @todo Extract CSS to stylesheet; transition from data-attributes to classes. -- @todo Consider adding i18n for error messages, flags, &c. -- @todo Consider adding generator(s?) for magic words and parser functions.

local libraryUtil = require("libraryUtil") local checkType = libraryUtil.checkType

local getArgs = require("Dev:Arguments").getArgs local userError = require("Dev:User error") local yesno = require("Dev:Yesno")

local p = {}

-- Parses a parameter to get its components: its name (optional), and either its -- value or its description (but not both). -- -- @param {string} param --    A parameter. -- @returns {table} --    The components of a parameter.

local function parseParam(param) local tmp = param local name, value, description

-- the parameter's name is anything to the left of the first equals sign; -- the equals sign can be escaped, for wikis that don't have Template:= if tmp:find("=") or tmp:find(mw.text.nowiki("=")) then name, tmp = tmp :gsub(mw.text.nowiki("="), "=", 1) :match("^(.-)%s*=%s*(.-)$") end

-- if the remaining text is wrapped in matching quotes, then it's a literal -- value; otherwise, it's a description of the parameter local first = tmp:sub(1, 1) local last = tmp:sub(-1)

if (first == '"' and last == '"') or (first == "'" and last == "'") then value = tmp:sub(2, -2) elseif tmp == "" then description = "..." -- the description cannot be an empty string else description = tmp end

return { name = name, value = value, description = description } end

-- The heart of the module. Transforms a list of parameters into wikitext -- syntax. -- -- @param {string} mode --    Which kind of brace-based wikitext we're dealing with. -- @param {string} opener --    Text to insert between the two left-braces and the first parameter. -- @param[opt] {table|nil} params --    A sequentual table of parameters. -- @param[opt] {table|nil} options --    A table with configuration flags. -- @param[opt] {boolean|nil} options.multiline -- @param[opt] {boolean|nil} options.subst -- @returns {string} --    A blob of wikitext describing any brace-based syntax.

local function builder(mode, opener, params, options) checkType("builder", 2, opener, "string") checkType("builder", 3, params, "table", true) checkType("builder", 4, options, "table", true)

params = params or {} options = options or {}

local html = mw.html.create("code") :attr("data-t-role", "wrapper") :attr("data-t-mode", mode) :css("all", "unset") :css("font-family", "monospace")

local openerSpan = html:tag("span") :attr("data-t-role", "opener") :wikitext(mw.text.nowiki("{"):rep(2))

if options.subst then openerSpan:wikitext("subst:") end

openerSpan:wikitext(opener)

if options.multiline then html:attr("data-t-multiline", "data-t-multiline") end

for i, param in ipairs(params) do       if type(param) ~= "string" then error("invalid entry #" .. i .. " in parameter list", 3) end

local components = parseParam(param) local paramHtml = html:tag("span") :attr("data-t-role", "parameter") :attr("data-t-index", i)           :wikitext(mw.text.nowiki("|"))

if options.multiline then paramHtml:css("display", "block") end

if components.name then paramHtml:tag("span") :attr("data-t-role", "parameter-name") :css("font-weight", "bold") :wikitext(components.name)

paramHtml:wikitext(" = ") end

if components.value then paramHtml:tag("span") :attr("data-t-role", "parameter-value") :wikitext(components.value) end

if components.description then paramHtml:tag("span") :attr("data-t-role", "parameter-description") :css("opacity", "0.65") :wikitext(mw.text.nowiki("<")) :wikitext(components.description) :wikitext(mw.text.nowiki(">")) end end

html:tag("span") :attr("data-t-role", "closer") :wikitext(mw.text.nowiki("}"):rep(2))

return tostring(html) end

-- Resolves a transclusion according to the MediaWiki -- transclusion resolution algorithm. -- -- @param {string} title --       The name of the transclusion to resolve. -- @return {string} --        The resolved transclusion with the namespace prefix. -- @see w:Template:Transclude

local function resolveTransclusion(title) checkType("resolveTransclusion", 1, title, "string") local i = mw.ustring.find(title, "%:") if i == 1 then return mw.ustring.sub(title, 2) elseif i ~= nil then return title end return "Template:" .. title end

-- Generator for transclusion syntax, e.g. ``, ``, -- or ``. -- -- @param {string} title --    The name of the template to link to. -- @param[opt] {table|nil} params --    A sequentual table of parameters. -- @param[opt] {table|nil} options --    A table with configuration flags. -- @param[opt] {boolean|nil} options.multiline -- @param[opt] {boolean|nil} options.subst -- @returns {string} --    A blob of wikitext describing a template.

function p.transclusion(title, params, options) if type(title) ~= "string" or title == "" then error("no title specified", 2) end

return builder(       "transclusion",        "" .. title .. "",        params,        options    ) end

-- Generator for invocation syntax, e.g. ``. -- -- @param {string} title --    The name of the module to link to, without the namespace prefix. -- @param {string} func --    The name of the function to call. -- @param[opt] {table|nil} params --    A sequentual table of parameters. -- @param[opt] {table|nil} options --    A table with configuration flags. -- @param[opt] {boolean|nil} options.multiline -- @param[opt] {boolean|nil} options.subst -- @returns {string} --    A blob of wikitext describing a module.

function p.invocation(title, func, params, options) if type(title) ~= "string" or title == "" then error("no module specified", 2) end

if type(func) ~= "string" or func == "" then error("no function specified", 2) end

local link = "" .. title .. ""

return builder(       "invocation",        "#invoke:" .. link .. mw.text.nowiki("|") .. func,        params,        options    ) end

-- Entry point from the wikitext side. Determines which generator to use based -- on the provided arguments. -- -- @param {table|Frame} frame --    A frame object whose arguments will determine the correct generator --    to use. -- @returns {string} --    A blob of wikitext describing any brace-based syntax.

function p.main(frame) local args = getArgs(frame, {removeBlanks = false}) local mode, minimumArity

if yesno(args.invocation) or yesno(args.i) then mode = "invocation" minimumArity = 2 else mode = "transclusion" minimumArity = 1 end

local params = {} local options = { multiline = yesno(args.multiline) or yesno(args.m), subst = yesno(args.subst) or yesno(args.s), }

-- a dynamically-generated list of arguments to the generator -- required arguments are inserted before `params` and `options` local varargs = {params, options}

for i, value in ipairs(args) do       if i <= minimumArity then -- pass the first few values directly to the generator -- these are used to calculate the opener table.insert(varargs, i, value) else -- put the remaining values in a table, and pass it to the generator -- these are shown as parameters in the resulting wikitext params[#params + 1] = value end end

local success, response = pcall(p[mode], unpack(varargs))

return success and response or userError(response) end

return p