跳转到内容

Module:T

来自Undertale Wiki

-- <nowiki>
--------------------------------------------------------------------------------
-- 一個功能豐富的範例生成器,用於基於大括號的 Wikitext。
--
-- @module T
-- @alias p
-- @version 0.6.4
-- @release 實驗性
-- @requires [[Global Lua Modules/Arguments|Module:Arguments]]
-- @requires [[Global Lua Modules/User error|Module:User error]]
-- @requires [[Global Lua Modules/Yesno|Module:Yesno]]
-- @author [[User:DarthKitty]]
-- @author [[User:Speedit]]
-- @author [[User:ExE Boss]]
--
-- @todo 將 CSS 抽出為樣式表;從 data-attributes 過渡到使用 class。
-- @todo 考慮為錯誤訊息、旗標等新增 i18n(國際化)功能。
-- @todo 考慮新增用於魔術字與解析器函數的生成器。
--------------------------------------------------------------------------------

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

local getArgs = require("Module:Arguments").getArgs
local userError = require("Module:User error")
local yesno = require("Module: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

--------------------------------------------------------------------------------
-- 模組的核心。將一組參數轉換為 Wikitext 語法。
--
-- @param {string} mode
--     處理哪種類型的大括號語法(例如模板或模組呼叫)。
-- @param {string} opener
--     插入在兩個左大括號與第一個參數之間的文字。
-- @param[opt] {table|nil} params
--     一個順序排列的參數表。
-- @param[opt] {table|nil} options
--     包含設定標誌的選項表。
-- @param[opt] {boolean|nil} options.multiline
--     是否使用多行格式。
-- @param[opt] {boolean|nil} options.subst
--     是否加上 `subst:` 前綴。
-- @returns {string}
--     一段描述大括號語法的 Wikitext 字串。
--------------------------------------------------------------------------------
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

--------------------------------------------------------------------------------
-- 根據 MediaWiki 的轉入(transclusion)解析邏輯解析一個模板名稱。
--
-- @param {string} title
--        要解析的轉入名稱。
-- @return {string}
--         已加上命名空間前綴的轉入名稱。
-- @see [[wikipedia: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

--------------------------------------------------------------------------------
-- 用來產生模板轉入語法的生成器,例如 `{{foo}}`、`{{:foo}}`,
-- 或 `{{Template:Foo}}`。
--
-- @param {string} title
--     要連結的模板名稱。
-- @param[opt] {table|nil} params
--     一個順序排列的參數表。
-- @param[opt] {table|nil} options
--     包含設定選項的表。
-- @param[opt] {boolean|nil} options.multiline
--     是否顯示為多行格式。
-- @param[opt] {boolean|nil} options.subst
--     是否加上 `subst:` 前綴。
-- @returns {string}
--     一段描述模板語法的 Wikitext 字串。

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

    return builder(
        "transclusion",
        "[[:" .. resolveTransclusion(title) .. "|" .. title .. "]]",
        params,
        options
    )
end

--------------------------------------------------------------------------------
-- 用來產生模組呼叫語法的生成器,例如 `{{#invoke:foo|bar}}`。
--
-- @param {string} title
--     要連結的模組名稱,不包含命名空間前綴。
-- @param {string} func
--     要呼叫的函數名稱。
-- @param[opt] {table|nil} params
--     一個順序排列的參數表。
-- @param[opt] {table|nil} options
--     包含設定選項的表。
-- @param[opt] {boolean|nil} options.multiline
--     是否顯示為多行格式。
-- @param[opt] {boolean|nil} options.subst
--     是否加上 `subst:` 前綴。
-- @returns {string}
--     一段描述模組語法的 Wikitext 字串。
--------------------------------------------------------------------------------
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 = "[[Module:" .. title .. "|" .. title .. "]]"

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

--------------------------------------------------------------------------------
-- 從 Wikitext 呼叫此模組的入口點。根據提供的引數決定使用哪一個生成器。
--
-- @param {table|Frame} frame
--     一個 frame 物件,將依據其引數決定使用正確的生成器。
-- @returns {string}
--     一段描述任何基於大括號語法的 Wikitext 字串。
--------------------------------------------------------------------------------
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