Module:Documentation: Difference between revisions

From Path of Exile Wiki
Jump to navigation Jump to search
en>Mr. Stradivarius
(use "ugsub" rather than "gsub" as the abbreviation for mw.ustring.gsub, to make it clearer that it isn't the regular string.gsub)
(Clear floats)
 
(99 intermediate revisions by 22 users not shown)
Line 3: Line 3:
-- Get required modules.
-- Get required modules.
local getArgs = require('Module:Arguments').getArgs
local getArgs = require('Module:Arguments').getArgs
local htmlBuilder = require('Module:HtmlBuilder')
local messageBox = require('Module:Message box')


-- Get the config table.
-- Get the config table.
Line 21: Line 19:
----------------------------------------------------------------------------
----------------------------------------------------------------------------


local function message(cfgKey, expectType, valArray)
local function message(cfgKey, valArray, expectType)
--[[
    --[[
-- Gets a message from the cfg table and formats it if appropriate.
    -- Gets a message from the cfg table and formats it if appropriate.
-- The function raises an error if the value from the cfg table is not
    -- The function raises an error if the value from the cfg table is not
-- of the type expectType.
    -- of the type expectType. The default type for expectType is 'string'.
-- If the table valArray is present, strings such as $1, $2 etc. in the
    -- If the table valArray is present, strings such as $1, $2 etc. in the
-- message are substituted with values from the table keys [1], [2] etc.
    -- message are substituted with values from the table keys [1], [2] etc.
-- For example, if the message cfg.fooMessage had the value 'Foo $2 bar $1.',
    -- For example, if the message "foo-message" had the value 'Foo $2 bar $1.',
-- message('fooMessage', 'string', {'baz', 'qux'}) would return "Foo qux bar baz."
    -- message('foo-message', {'baz', 'qux'}) would return "Foo qux bar baz."
--]]
    --]]
local msg = cfg[cfgKey]
    local msg = cfg[cfgKey]
if expectType and type(msg) ~= expectType then
    expectType = expectType or 'string'
error('message: type error in message cfg.' .. cfgKey .. ' (' .. expectType .. ' expected, got ' .. type(msg) .. ')', 2)
    if type(msg) ~= expectType then
end
        error('message: type error in message cfg.' .. cfgKey .. ' (' .. expectType .. ' expected, got ' .. type(msg) .. ')', 2)
if not valArray then
    end
return msg
    if not valArray then
end
        return msg
    end


local function getMessageVal(match)
    local function getMessageVal(match)
match = tonumber(match)
        match = tonumber(match)
return valArray[match] or error('message: no value found for key $' .. match .. ' in message cfg.' .. cfgKey, 4)
        return valArray[match] or error('message: no value found for key $' .. match .. ' in message cfg.' .. cfgKey, 4)
end
    end


local ret = ugsub(msg, '$([1-9][0-9]*)', getMessageVal)
    local ret = ugsub(msg, '$([1-9][0-9]*)', getMessageVal)
return ret
    return ret
end
end


Line 51: Line 50:


local function makeWikilink(page, display)
local function makeWikilink(page, display)
if display then
    if display then
return mw.ustring.format('[[%s|%s]]', page, display)
        return mw.ustring.format('[[%s|%s]]', page, display)
else
    else
return mw.ustring.format('[[%s]]', page)
        return mw.ustring.format('[[%s]]', page)
end
    end
end
end


Line 61: Line 60:


local function makeCategoryLink(cat, sort)
local function makeCategoryLink(cat, sort)
local catns = mw.site.namespaces[14].name
    local catns = mw.site.namespaces[14].name
return makeWikilink(catns .. ':' .. cat, sort)
    return makeWikilink(catns .. ':' .. cat, sort)
end
end


Line 68: Line 67:


local function makeUrlLink(url, display)
local function makeUrlLink(url, display)
return mw.ustring.format('[%s %s]', url, display)
    return mw.ustring.format('[%s %s]', url, display)
end
end


Line 74: Line 73:


local function makeToolbar(...)
local function makeToolbar(...)
local ret = {}
    local ret = {}
local lim = select('#', ...)
    local lim = select('#', ...)
if lim < 1 then
    if lim < 1 then
return nil
        return nil
end
    end
for i = 1, lim do
    for i = 1, lim do
ret[#ret + 1] = select(i, ...)
        ret[#ret + 1] = select(i, ...)
end
    end
return '<small style="font-style: normal;">(' .. table.concat(ret, ' &#124; ') .. ')</small>'
    local toolbar = mw.html.create('span')
end
    toolbar
        :addClass(message('end-box-toolbar-classes'))
        :wikitext('(' .. table.concat(ret, ' &#124; ') .. ')')
    return tostring(toolbar)
end  


p.makeToolbar = makeToolbar
p.makeToolbar = makeToolbar
local function err(msg)
return string.format(
'<strong class="error">%s %s</strong>%s',
message('errorPrefix', 'string'),
msg,
makeCategoryLink(message('errorCategory', 'string'))
)
end
p.err = err


----------------------------------------------------------------------------
----------------------------------------------------------------------------
Line 103: Line 95:


local function makeInvokeFunc(funcName)
local function makeInvokeFunc(funcName)
return function (frame)
    return function (frame)
local args = getArgs(frame, {
        local args = getArgs(frame, {
valueFunc = function (key, value)
            valueFunc = function (key, value)
if type(value) == 'string' then
                if type(value) == 'string' then
value = value:match('^%s*(.-)%s*$') -- Remove whitespace.
                    value = value:match('^%s*(.-)%s*$') -- Remove whitespace.
if key == 'heading' or value ~= '' then
                    if key == 'heading' or value ~= '' then
return value
                        return value
else
                    else
return nil
                        return nil
end
                    end
else
                else
return value
                    return value
end
                end
end
            end
})
        })
return p[funcName](args)
        return p[funcName](args)
end
    end
end
end


Line 129: Line 121:


function p._main(args)
function p._main(args)
local env = p.getEnvironment(args)
    --[[
local root = htmlBuilder.create()
    -- This function defines logic flow for the module.
root
    -- @args - table of arguments passed by the user
.wikitext(p.protectionTemplate(env))
    --
.wikitext(p.sandboxNotice(args, env))
    -- Messages:
-- This div tag is from {{documentation/start box}}, but moving it here
    -- 'main-div-id' --> 'template-documentation'
-- so that we don't have to worry about unclosed tags.
    -- 'main-div-classes' --> 'template-documentation iezoomfix'
.tag('div')
    --]]
.attr('id', message('mainDivId', 'string'))
    local env = p.getEnvironment(args)
.addClass(message('mainDivClasses', 'string'))
    local root = mw.html.create()
.newline()
    root
.wikitext(p._startBox(args, env))
        :tag('div')
.wikitext(p._content(args, env))
            :cssText('clear: both;')
.tag('div')
            :done()
.css('clear', 'both') -- So right or left floating items don't stick out of the doc box.
        :wikitext(p.sandboxNotice(args, env))
.newline()
        -- This div tag is from {{documentation/start box}}, but moving it here
.done()
        -- so that we don't have to worry about unclosed tags.
.done()
        :tag('div')
.wikitext(p._endBox(args, env))
            :attr('id', message('main-div-id'))
.newline()
            :addClass(message('main-div-classes'))
.wikitext(p.addTrackingCategories(env))
            :addClass('clearfix')
return tostring(root)
            :newline()
            :wikitext(p._startBox(args, env))
            :wikitext(p._content(args, env))
            :done()
        :wikitext(p._endBox(args, env))
        :wikitext(p.addTrackingCategories(env))
    return tostring(root)
end
end


Line 158: Line 156:


function p.getEnvironment(args)
function p.getEnvironment(args)
--[[
    --[[
-- Returns a table with information about the environment, including title objects and other namespace- or
    -- Returns a table with information about the environment, including title objects and other namespace- or
-- path-related data.
    -- path-related data.
--
    -- @args - table of arguments passed by the user
-- Title objects include:
    --
-- env.title - the page we are making documentation for (usually the current title)
    -- Title objects include:
-- env.templateTitle - the template (or module, file, etc.)
    -- env.title - the page we are making documentation for (usually the current title)
-- env.docTitle - the /doc subpage.
    -- env.templateTitle - the template (or module, file, etc.)
-- env.sandboxTitle - the /sandbox subpage.
    -- env.docTitle - the /doc subpage.
-- env.testcasesTitle - the /testcases subpage.
    -- env.sandboxTitle - the /sandbox subpage.
-- env.printTitle - the print version of the template, located at the /Print subpage.
    -- env.testcasesTitle - the /testcases subpage.
--
    --
-- Data includes:
    -- Data includes:
-- env.subjectSpace - the number of the title's subject namespace.
    -- env.subjectSpace - the number of the title's subject namespace.
-- env.docSpace - the number of the namespace the title puts its documentation in.
    -- env.docSpace - the number of the namespace the title puts its documentation in.
-- env.docpageRoot - the text of the base page of the /doc, /sandbox and /testcases pages, with namespace.
    -- env.docpageBase - the text of the base page of the /doc, /sandbox and /testcases pages, with namespace.
-- env.compareUrl - URL of the Special:ComparePages page comparing the sandbox with the template.
    -- env.compareUrl - URL of the Special:ComparePages page comparing the sandbox with the template.
--  
    --  
-- All table lookups are passed through pcall so that errors are caught. If an error occurs, the value
    -- All table lookups are passed through pcall so that errors are caught. If an error occurs, the value
-- returned will be nil.
    -- returned will be nil.
--]]
    --]]
   
local env, envFuncs = {}, {}
    local env, envFuncs = {}, {}


-- Set up the metatable. If triggered we call the corresponding function in the envFuncs table. The value
    -- Set up the metatable. If triggered we call the corresponding function in the envFuncs table. The value
-- returned by that function is memoized in the env table so that we don't call any of the functions
    -- returned by that function is memoized in the env table so that we don't call any of the functions
-- more than once. (Nils won't be memoized.)
    -- more than once. (Nils won't be memoized.)
setmetatable(env, {
    setmetatable(env, {
__index = function (t, key)
        __index = function (t, key)
local envFunc = envFuncs[key]
            local envFunc = envFuncs[key]
if envFunc then
            if envFunc then
local success, val = pcall(envFunc)
                local success, val = pcall(envFunc)
if success then
                if success then
env[key] = val -- Memoise the value.
                    env[key] = val -- Memoise the value.
return val
                    return val
end
                end
end
            end
return nil
            return nil
end
        end
})
    })


function envFuncs.title()
    function envFuncs.title()
-- The title object for the current page, or a test page passed with args.page.
        -- The title object for the current page, or a test page passed with args.page.
local title
        local title
local titleArg = args.page
        local titleArg = args.page
if titleArg then
        if titleArg then
title = mw.title.new(titleArg)
            title = mw.title.new(titleArg)
if not title then
        else
error(message('titleArgError', 'string', {titleArg}))
            title = mw.title.getCurrentTitle()
end
        end
else
        return title
title = mw.title.getCurrentTitle()
    end
end
return title
end


function envFuncs.templateTitle()
    function envFuncs.templateTitle()
-- The template (or module, etc.) title object.
        --[[
local title = env.title
        -- The template (or module, etc.) title object.
local subpage = title.subpageText
        -- Messages:
if subpage == message('sandboxSubpage', 'string') or subpage == message('testcasesSubpage', 'string') then
        -- 'sandbox-subpage' --> 'sandbox'
return title.basePageTitle
        -- 'testcases-subpage' --> 'testcases'
else
        --]]
return title
        local subjectSpace = env.subjectSpace
end
        local title = env.title
end
        local subpage = title.subpageText
        if subpage == message('sandbox-subpage') or subpage == message('testcases-subpage') then
            return mw.title.makeTitle(subjectSpace, title.baseText)
        else
            return mw.title.makeTitle(subjectSpace, title.text)
        end
    end


function envFuncs.docTitle()
    function envFuncs.docTitle()
-- Title object of the /doc subpage.
        --[[
local title = env.title
        -- Title object of the /doc subpage.
local docname = args[1] -- User-specified doc page.
        -- Messages:
local docpage
        -- 'doc-subpage' --> 'doc'
if docname then
        --]]
docpage = docname
        local title = env.title
else
        local docname = args[1] -- User-specified doc page.
docpage = env.docpageRoot .. '/' .. message('docSubpage', 'string')
        local docpage
end
        if docname then
return mw.title.new(docpage)
            docpage = docname
end
        else
            docpage = env.docpageBase .. '/' .. message('doc-subpage')
function envFuncs.sandboxTitle()
        end
-- Title object for the /sandbox subpage.
        return mw.title.new(docpage)
return mw.title.new(env.docpageRoot .. '/' .. message('sandboxSubpage', 'string'))
    end
end
   
    function envFuncs.sandboxTitle()
function envFuncs.testcasesTitle()
        --[[
-- Title object for the /testcases subpage.
        -- Title object for the /sandbox subpage.
return mw.title.new(env.docpageRoot .. '/' .. message('testcasesSubpage', 'string'))
        -- Messages:
end
        -- 'sandbox-subpage' --> 'sandbox'
        --]]
function envFuncs.printTitle()
        return mw.title.new(env.docpageBase .. '/' .. message('sandbox-subpage'))
-- Title object for the /Print subpage.
    end
return env.templateTitle:subPageTitle(message('printSubpage', 'string'))
   
end
    function envFuncs.testcasesTitle()
        --[[
        -- Title object for the /testcases subpage.
        -- Messages:
        -- 'testcases-subpage' --> 'testcases'
        --]]
        return mw.title.new(env.docpageBase .. '/' .. message('testcases-subpage'))
    end


function envFuncs.subjectSpace()
    function envFuncs.subjectSpace()
-- The subject namespace number.
        -- The subject namespace number.
return mw.site.namespaces[env.title.namespace].subject.id
        return mw.site.namespaces[env.title.namespace].subject.id
end
    end


function envFuncs.docSpace()
    function envFuncs.docSpace()
-- The documentation namespace number. For most namespaces this is the same as the
        -- The documentation namespace number. For most namespaces this is the same as the
-- subject namespace. However, pages in the Article, File, MediaWiki or Category
        -- subject namespace. However, pages in the Article, File, MediaWiki, Category or Widget
-- namespaces must have their /doc, /sandbox and /testcases pages in talk space.
        -- namespaces must have their /doc, /sandbox and /testcases pages in talk space.
local subjectSpace = env.subjectSpace
        local subjectSpace = env.subjectSpace
if subjectSpace == 0 or subjectSpace == 6 or subjectSpace == 8 or subjectSpace == 14 then
        if subjectSpace == 0 or subjectSpace == 6 or subjectSpace == 8 or subjectSpace == 14 or subjectSpace == 274 then
return subjectSpace + 1
            return subjectSpace + 1
else
        else
return subjectSpace
            return subjectSpace
end
        end
end
    end


function envFuncs.docpageRoot()
    function envFuncs.docpageBase()
-- The base page of the /doc, /sandbox, and /testcases subpages.
        -- The base page of the /doc, /sandbox, and /testcases subpages.
-- For some namespaces this is the talk page, rather than the template page.
        -- For some namespaces this is the talk page, rather than the template page.
local templateTitle = env.templateTitle
        local templateTitle = env.templateTitle
local docSpace = env.docSpace
        local docSpace = env.docSpace
local docSpaceText = mw.site.namespaces[docSpace].name
        local docSpaceText = mw.site.namespaces[docSpace].name
-- Assemble the link. docSpace is never the main namespace, so we can hardcode the colon.
        -- Assemble the link. docSpace is never the main namespace, so we can hardcode the colon.
return docSpaceText .. ':' .. templateTitle.text
        return docSpaceText .. ':' .. templateTitle.text
end
    end
   
function envFuncs.compareUrl()
    function envFuncs.compareUrl()
-- Diff link between the sandbox and the main template using [[Special:ComparePages]].
        -- Diff link between the sandbox and the main template using [[Special:ComparePages]].
local templateTitle = env.templateTitle
        local templateTitle = env.templateTitle
local sandboxTitle = env.sandboxTitle
        local sandboxTitle = env.sandboxTitle
local compareUrl = mw.uri.fullUrl(
        if templateTitle.exists and sandboxTitle.exists then
'Special:ComparePages',
            local compareUrl = mw.uri.fullUrl(
{page1 = templateTitle.prefixedText, page2 = sandboxTitle.prefixedText}
                'Special:ComparePages',
)
                {page1 = templateTitle.prefixedText, page2 = sandboxTitle.prefixedText}
return tostring(compareUrl)
            )
end
            return tostring(compareUrl)
        else
            return nil
        end
    end    


return env
    return env
end
end  


----------------------------------------------------------------------------
----------------------------------------------------------------------------
Line 299: Line 311:


function p.sandboxNotice(args, env)
function p.sandboxNotice(args, env)
local title = env.title
    --[=[
local sandboxTitle = env.sandboxTitle
    -- Generates a sandbox notice for display above sandbox pages.
local templateTitle = env.templateTitle
    -- @args - a table of arguments passed by the user
if not (title and sandboxTitle and templateTitle and mw.title.equals(title, sandboxTitle)) then
    -- @env - environment table containing title objects, etc., generated with p.getEnvironment
return nil
    --
end
    -- Messages:
local omargs = {} -- Args for {{ombox}}.
    -- 'sandbox-notice-image' --> '[[File:Sandbox.svg|50px|alt=|link=]]'
-- Get the image wikitext.
    -- 'sandbox-notice-blurb' --> 'This is the $1 for $2.'
omargs.image = message('sandboxNoticeImage', 'string')
    -- 'sandbox-notice-diff-blurb' --> 'This is the $1 for $2 ($3).'
-- Get the text. We start with the opening blurb, which is something like
    -- 'sandbox-notice-pagetype-template' --> '[[Wikipedia:Template test cases|template sandbox]] page'
-- "This is the template sandbox for [[Template:Foo]] (diff)."
    -- 'sandbox-notice-pagetype-module' --> '[[Wikipedia:Template test cases|module sandbox]] page'
local text = ''
    -- 'sandbox-notice-pagetype-other' --> 'sandbox page'
local frame = mw.getCurrentFrame()
    -- 'sandbox-notice-compare-link-display' --> 'diff'
local isPreviewing = frame:preprocess('{{REVISIONID}}') == '' -- True if the page is being previewed.
    -- 'sandbox-notice-testcases-blurb' --> 'See also the companion subpage for $1.'
local templateLink = makeWikilink(templateTitle.prefixedText)
    -- 'sandbox-notice-testcases-link-display' --> 'test cases'
local compareUrl = env.compareUrl
    -- 'sandbox-category' --> 'Template sandboxes'
if isPreviewing or not compareUrl then
    --]=]
-- 'This is the [[Wikipedia:Template test cases|template sandbox]] page for $1.'
    local title = env.title
text = text .. message('sandboxNoticeBlurb', 'string', {templateLink})
    local sandboxTitle = env.sandboxTitle
else
    local templateTitle = env.templateTitle
-- 'This is the [[Wikipedia:Template test cases|template sandbox]] page for $1 ($2).'
    local subjectSpace = env.subjectSpace
local compareDisplay = message('sandboxNoticeCompareLinkDisplay', 'string')
    if not (subjectSpace and title and sandboxTitle and templateTitle and mw.title.equals(title, sandboxTitle)) then
local compareLink = makeUrlLink(compareUrl, compareDisplay)
        return nil
text = text .. message('sandboxNoticeDiffBlurb', 'string', {templateLink, compareLink})
    end
end
    -- Build the table of arguments to pass to {{ombox}}. We need just two fields, "image" and "text".
-- Get the test cases page blurb if the page exists.
    local mboxargs = {
local testcasesTitle = env.testcasesTitle
        type = 'content',
if testcasesTitle and testcasesTitle.exists then
        image = message('sandbox-notice-image'),
local testcasesLinkDisplay = message('sandboxNoticeTestcasesLinkDisplay', 'string')
    }
local testcasesLink = makeWikilink(testcasesTitle.prefixedText, testcasesLinkDisplay)
    -- Get the text. We start with the opening blurb, which is something like
text = text .. '<br />' .. message('sandboxNoticeTestcasesBlurb', 'string', {testcasesLink})
    -- "This is the template sandbox for [[Template:Foo]] (diff)."
end
    local pagetype
-- Add the page to [[Category:Template sandboxes]].
    if subjectSpace == 10 then
text = text .. makeCategoryLink(message('sandboxCategory', 'string'))
        pagetype = message('sandbox-notice-pagetype-template')
omargs.text = text
    elseif subjectSpace == 828 then
return messageBox.main('ombox', omargs)
        pagetype = message('sandbox-notice-pagetype-module')
end
    else
 
        pagetype = message('sandbox-notice-pagetype-other')
function p.protectionTemplate(env)
    end
local title = env.title
    local templateLink = makeWikilink(templateTitle.prefixedText)
local protectionTemplate = message('protectionTemplate', 'string')
    local compareUrl = env.compareUrl
if not (protectionTemplate and title.namespace == 10) then
    if compareUrl then
-- Don't display the protection template if we are not in the template namespace.
        local compareDisplay = message('sandbox-notice-compare-link-display')
return nil
        local compareLink = mw.html.create('span')
end
            :addClass('plainlinks')
local frame = mw.getCurrentFrame()
            :wikitext(makeUrlLink(compareUrl, compareDisplay))
local function getProtectionLevel(protectionType, page)
        mboxargs.title = message('sandbox-notice-diff-blurb', {pagetype, templateLink, tostring(compareLink)})
-- Gets the protection level for page, or for the current page if page is not specified.
    else
local level = frame:callParserFunction('PROTECTIONLEVEL', protectionType, page)
        mboxargs.title = message('sandbox-notice-blurb', {pagetype, templateLink})
if level ~= '' then
    end
return level
    -- Get the test cases page blurb if the page exists. This is something like
else
    -- "See also the companion subpage for [[Template:Foo/testcases|test cases]]."
return nil -- The parser function returns the blank string if there is no match.
    local testcasesTitle = env.testcasesTitle
end
    if testcasesTitle and testcasesTitle.exists then
end
        if testcasesTitle.contentModel == "Scribunto" then
local prefixedTitle = title.prefixedText
            local testcasesLinkDisplay = message('sandbox-notice-testcases-link-display')
if getProtectionLevel('move', prefixedTitle) == 'sysop' or getProtectionLevel('edit', prefixedTitle) then
            local testcasesRunLinkDisplay = message('sandbox-notice-testcases-run-link-display')
-- The page is full-move protected, or full, template, or semi-protected.
            local testcasesLink = makeWikilink(testcasesTitle.prefixedText, testcasesLinkDisplay)
return frame:expandTemplate{title = protectionTemplate, args = message('protectionTemplateArgs', 'table')}
            local testcasesRunLink = makeWikilink(testcasesTitle.prefixedText, testcasesRunLinkDisplay)
end
            mboxargs.text = message('sandbox-notice-testcases-run-blurb', {testcasesLink, testcasesRunLink})
return nil
        else
            local testcasesLinkDisplay = message('sandbox-notice-testcases-link-display')
            local testcasesLink = makeWikilink(testcasesTitle.prefixedText, testcasesLinkDisplay)
            mboxargs.text = message('sandbox-notice-testcases-blurb', {testcasesLink})
        end
    end
    local ret = {
        mw.getCurrentFrame():expandTemplate{title = message('template-message-box'), args = mboxargs},
        makeCategoryLink(message('sandbox-category')) -- Add the sandbox to the sandbox category.
    }
    return table.concat(ret)
end
end


Line 369: Line 391:


function p._startBox(args, env)
function p._startBox(args, env)
-- Generate [view][edit][history][purge] or [create] links.
    --[[
local links
    -- This function generates the start box.
local content = args.content
    -- @args - a table of arguments passed by the user
if not content then
    -- @env - environment table containing title objects, etc., generated with p.getEnvironment
-- No need to include the links if the documentation is on the template page itself.
    --
local linksData = p.makeStartBoxLinksData(args, env)
    -- The actual work is done by p.makeStartBoxLinksData and p.renderStartBoxLinks which make
if type(linksData) == 'table' then
    -- the [view] [edit] [history] [purge] links, and by p.makeStartBoxData and p.renderStartBox
links = p.renderStartBoxLinks(linksData)
    -- which generate the box HTML.
else
    --]]
-- linksData is nil or an error message.
    env = env or p.getEnvironment(args)
return linksData
    local links
end
    local docTitle = env.docTitle
end
    if not args.content or docTitle.exists then
-- Generate the start box html.
        -- No need to include the links if the documentation is on the template page itself.
local data = p.makeStartBoxData(args, env, links)
        local linksData = p.makeStartBoxLinksData(args, env)
if type(data) == 'table' then
        if linksData then
return p.renderStartBox(data)
            links = p.renderStartBoxLinks(linksData)
elseif type(data) == 'string' then
        end
-- data is an error message.
    end
return data
    -- Generate the start box html.
else
    local data = p.makeStartBoxData(args, env, links)
-- User specified no heading.
    if data then
return nil
        return p.renderStartBox(data)
end
    else
        -- User specified no heading.
        return nil
    end
end
end


function p.makeStartBoxLinksData(args, env)
function p.makeStartBoxLinksData(args, env)
local data = {}
    --[[
-- Get title objects.
    -- Does initial processing of data to make the [view] [edit] [history] [purge] links.
local title = env.title
    -- @args - a table of arguments passed by the user
local docTitle = env.docTitle
    -- @env - environment table containing title objects, etc., generated with p.getEnvironment
if not title or not docTitle then
    --
return nil
    -- Messages:
end
    -- 'view-link-display' --> 'view'
data.title = title
    -- 'edit-link-display' --> 'edit'
data.docTitle = docTitle
    -- 'history-link-display' --> 'history'
-- View, display, edit, and purge links if /doc exists.
    -- 'purge-link-display' --> 'purge'
data.viewLinkDisplay = message('viewLinkDisplay', 'string')
    -- 'module-preload' --> 'Template:Documentation/preload-module-doc'
data.editLinkDisplay = message('editLinkDisplay', 'string')
    -- 'docpage-preload' --> 'Template:Documentation/preload'
data.historyLinkDisplay = message('historyLinkDisplay', 'string')
    -- 'create-link-display' --> 'create'
data.purgeLinkDisplay = message('purgeLinkDisplay', 'string')
    --]]
-- Create link if /doc doesn't exist.
    local subjectSpace = env.subjectSpace
local preload = args.preload
    local title = env.title
if not preload then
    local docTitle = env.docTitle
if env.subjectSpace == 6 then -- File namespace
    if not title or not docTitle then
preload = message('fileDocpagePreload', 'string')
        return nil
else
    end
preload = message('docpagePreload', 'string')
    if docTitle.isRedirect then
end
        docTitle = docTitle.redirectTarget
end
    end
data.preload = preload
 
data.createLinkDisplay = message('createLinkDisplay', 'string')
    local data = {}
return data
    data.title = title
    data.docTitle = docTitle
    -- View, display, edit, and purge links if /doc exists.
    data.viewLinkDisplay = message('view-link-display')
    data.editLinkDisplay = message('edit-link-display')
    data.historyLinkDisplay = message('history-link-display')
    data.purgeLinkDisplay = message('purge-link-display')
    -- Create link if /doc doesn't exist.
    local preload = args.preload
    if not preload then
        if subjectSpace == 828 then -- Module namespace
            preload = message('module-preload')
        else
            preload = message('docpage-preload')
        end
    end
    data.preload = preload
    data.createLinkDisplay = message('create-link-display')
    return data
end
end


function p.renderStartBoxLinks(data)
function p.renderStartBoxLinks(data)
-- Render the [view][edit][history][purge] or [create] links.
    --[[
local ret
    -- Generates the [view][edit][history][purge] or [create][purge] links from the data table.
local docTitle = data.docTitle
    -- @data - a table of data generated by p.makeStartBoxLinksData
local title = data.title
    --]]
if docTitle.exists then
   
local viewLink = makeWikilink(docTitle.prefixedText, data.viewLinkDisplay)
    local function escapeBrackets(s)
local editLink = makeUrlLink(docTitle:fullUrl{action = 'edit'}, data.editLinkDisplay)
        -- Escapes square brackets with HTML entities.
local historyLink = makeUrlLink(docTitle:fullUrl{action = 'history'}, data.historyLinkDisplay)
        s = s:gsub('%[', '&#91;') -- Replace square brackets with HTML entities.
local purgeLink = makeUrlLink(title:fullUrl{action = 'purge'}, data.purgeLinkDisplay)
        s = s:gsub('%]', '&#93;')
ret = '[%s] [%s] [%s] [%s]'
        return s
ret = ret:gsub('%[', '&#91;') -- Replace square brackets with HTML entities.
    end
ret = ret:gsub('%]', '&#93;')
 
ret = mw.ustring.format(ret, viewLink, editLink, historyLink, purgeLink)
    local ret
else
    local docTitle = data.docTitle
ret = makeUrlLink(docTitle:fullUrl{action = 'edit', preload = data.preload}, data.createLinkDisplay)
    local title = data.title
end
    local purgeLink = makeUrlLink(title:fullUrl{action = 'purge'}, data.purgeLinkDisplay)
    if docTitle.exists then
        local viewLink = makeWikilink(docTitle.prefixedText, data.viewLinkDisplay)
        local editLink = makeUrlLink(docTitle:fullUrl{action = 'edit'}, data.editLinkDisplay)
        local historyLink = makeUrlLink(docTitle:fullUrl{action = 'history'}, data.historyLinkDisplay)
        local purgeLink = makeUrlLink(title:fullUrl{action = 'purge'}, data.purgeLinkDisplay)
        ret = '[%s] [%s] [%s] [%s]'
        ret = escapeBrackets(ret)
        ret = mw.ustring.format(ret, viewLink, editLink, historyLink, purgeLink)
    else
        local createLink = makeUrlLink(docTitle:fullUrl{action = 'edit', preload = data.preload}, data.createLinkDisplay)
        ret = '[%s] [%s]'
        ret = escapeBrackets(ret)
        ret = mw.ustring.format(ret, createLink, purgeLink)
    end
    return ret
end
end


function p.makeStartBoxData(args, env, links)
function p.makeStartBoxData(args, env, links)
local subjectSpace = env.subjectSpace
    --[=[
if not subjectSpace then
    -- Does initial processing of data to pass to the start-box render function, p.renderStartBox.
return nil
    -- @args - a table of arguments passed by the user
end
    -- @env - environment table containing title objects, etc., generated with p.getEnvironment
local data = {}
    -- @links - a string containing the [view][edit][history][purge] links - could be nil if there's an error.
    --
-- Heading
    -- Messages:
local heading = args.heading -- Blank values are not removed.
    -- 'documentation-icon-wikitext' --> '[[File:Test Template Info-Icon - Version (2).svg|50px|link=|alt=]]'
if heading == '' then
    -- 'template-namespace-heading' --> 'Template documentation'
-- Don't display the start box if the heading arg is defined but blank.
    -- 'module-namespace-heading' --> 'Module documentation'
return nil
    -- 'widget-namespace-heading' --> 'Widget documentation'
end
    -- 'file-namespace-heading' --> 'Summary'
if heading then
    -- 'other-namespaces-heading' --> 'Documentation'
data.heading = heading
    -- 'start-box-classes' --> 'template-documentation__start'
elseif subjectSpace == 10 then -- Template namespace
    -- 'start-box-heading-classes' --> 'template-documentation__heading'
data.heading = message('documentationIconWikitext', 'string') .. ' ' .. message('templateNamespaceHeading', 'string')
    -- 'start-box-links-id' --> 'doc_editlinks'
elseif subjectSpace == 828 then -- Module namespace
    -- 'start-box-links-classes' --> 'mw-editsection-like plainlinks'
data.heading = message('documentationIconWikitext', 'string') .. ' ' .. message('moduleNamespaceHeading', 'string')
    -- 'testcases-create-link-display' --> 'create'
elseif subjectSpace == 6 then -- File namespace
    --]=]
data.heading = message('fileNamespaceHeading', 'string')
    local subjectSpace = env.subjectSpace
else
    if not subjectSpace then
data.heading = message('otherNamespacesHeading', 'string')
        -- Default to an "other namespaces" namespace, so that we get at least some output
end
        -- if an error occurs.
        subjectSpace = 2
-- Heading CSS
    end
local headingStyle = args['heading-style']
    local data = {}
if headingStyle then
   
data.headingStyleText = headingStyle
    -- Heading
elseif subjectSpace == 10 then
    local heading = args.heading -- Blank values are not removed.
-- We are in the template or template talk namespaces.
    if heading == '' then
data.headingFontWeight = 'bold'
        -- Don't display the start box if the heading arg is defined but blank.
data.headingFontSize = '125%'
        return nil
else
    end
data.headingFontSize = '150%'
    if heading then
end
        data.heading = heading
    elseif subjectSpace == 10 then -- Template namespace
-- [view][edit][history][purge] or [create] links.
        data.heading = message('documentation-icon-wikitext') .. ' ' .. message('template-namespace-heading')
if links then
    elseif subjectSpace == 828 then -- Module namespace
data.linksClass = message('startBoxLinkclasses', 'string')
        data.heading = message('documentation-icon-wikitext') .. ' ' .. message('module-namespace-heading')
data.linksId = message('startBoxLinkId', 'string')
    elseif subjectSpace == 274 then -- Widget namespace
data.links = links
        data.heading = message('documentation-icon-wikitext') .. ' ' .. message('widget-namespace-heading')
end
    elseif subjectSpace == 6 then -- File namespace
        data.heading = message('file-namespace-heading')
return data
    else
        data.heading = message('other-namespaces-heading')
    end
 
    -- CSS
    data.startClass = message('start-box-classes');
   
    -- Heading CSS
    data.headingClass = message('start-box-heading-classes');
    local headingStyle = args['heading-style']
    if headingStyle then
        data.headingStyleText = headingStyle
    end
   
    -- Data for the [view][edit][history][purge] or [create] links.
    if links then
        data.linksId = message('start-box-links-id')
        data.linksClass = message('start-box-links-classes')
        data.links = links
    end
   
    return data
end
end


function p.renderStartBox(data)
function p.renderStartBox(data)
-- Renders the start box html.
    -- Renders the start box html.
local sbox = htmlBuilder.create('div')
    -- @data - a table of data generated by p.makeStartBoxData.
sbox
    local sbox = mw.html.create('div')
.css('padding-bottom', '3px')
    sbox
.css('border-bottom', '1px solid #aaa')
        :addClass(data.startClass)
.css('margin-bottom', '1ex')
        :newline()
.newline()
        :tag('span')
.tag('span')
            :addClass(data.headingClass)
.cssText(data.headingStyleText)
            :cssText(data.headingStyleText)
.css('font-weight', data.headingFontWeight)
            :wikitext(data.heading)
.css('font-size', data.headingFontSize)
    local links = data.links
.wikitext(data.heading)
    if links then
local links = data.links
        sbox:tag('span')
if links then
            :attr('id', data.linksId)
sbox.tag('span')
            :addClass(data.linksClass)
.addClass(data.linksClass)
            :wikitext(links)
.attr('id', data.linksId)
    end
.wikitext(links)
    return tostring(sbox)
end
return tostring(sbox)
end
end


Line 520: Line 597:


function p._content(args, env)
function p._content(args, env)
-- Get the /doc title object
    -- Displays the documentation contents
local docTitle = env.docTitle
    -- @args - a table of arguments passed by the user
if not docTitle then
    -- @env - environment table containing title objects, etc., generated with p.getEnvironment
return nil
    env = env or p.getEnvironment(args)
end
    local docTitle = env.docTitle
-- Get the documentation content.
    local content = ''
local content = args.content
    if docTitle and docTitle.exists then
if not content and docTitle.exists then
        content = args._content or mw.getCurrentFrame():expandTemplate{title = docTitle.prefixedText}
local frame = mw.getCurrentFrame()
    end
content = frame:preprocess('{{ ' .. docTitle.prefixedText .. ' }}')
    if args.content then
end
        content = args.content .. '\n' .. content
-- The line breaks below are necessary so that "=== Headings ===" at the start and end
    end
-- of docs are interpreted correctly.
    -- The line breaks below are necessary so that "=== Headings ===" at the start and end
return '\n' .. (content or '') .. '\n'  
    -- of docs are interpreted correctly.
    return '\n' .. content .. '\n'  
end
end


Line 543: Line 621:


function p._endBox(args, env)
function p._endBox(args, env)
-- This function generates the end box (also known as the link box).
    --[=[
    -- This function generates the end box (also known as the link box).
-- Get environment data.
    -- @args - a table of arguments passed by the user
local subjectSpace = env.subjectSpace
    -- @env - environment table containing title objects, etc., generated with p.getEnvironment
local docTitle = env.docTitle
    --
if not subjectSpace or not docTitle then
    -- Messages:
return nil
    -- 'end-box-id' --> 'documentation-meta-data'
end
    -- 'end-box-classes' --> 'template-documentation__end'
    --]=]
-- Check whether we should output the end box at all. Add the end
   
-- box by default if the documentation exists or if we are in the
    -- Get environment data.
-- user, module or template namespaces.
    env = env or p.getEnvironment(args)
if linkBox == 'off'
    local subjectSpace = env.subjectSpace
or not (
    local docTitle = env.docTitle
docTitle.exists
    if not subjectSpace or not docTitle then
or subjectSpace == 2
        return nil
or subjectSpace == 828
    end
or subjectSpace == 10
       
)
    -- Check whether we should output the end box at all. Add the end
then
    -- box by default if the documentation exists or if we are in the
return nil
    -- user, module or template namespaces.
end
    local linkBox = args['link box']
    if linkBox == 'off'
        or not (
            docTitle.exists
            or subjectSpace == 2
            or subjectSpace == 828
            or subjectSpace == 10
        )
    then
        return nil
    end


-- Assemble the arguments for {{fmbox}}.
    -- Assemble the end box text
local fmargs = {}
    local text = ''
fmargs.id = message('fmboxId', 'string') -- Sets 'documentation-meta-data'
    if linkBox then
fmargs.image = message('fmboxImageNone', 'string') -- Sets 'none'
        text = text .. linkBox
fmargs.style = message('fmboxStyle', 'string') -- Sets 'background-color: #ecfcf4'
    else
fmargs.textstyle = message('fmboxTextstyle', 'string') -- 'font-style: italic;'
        text = text .. (p.makeDocPageBlurb(args, env) or '') -- "This documentation is transcluded from [[Foo]]."
        if subjectSpace == 2 or subjectSpace == 10 or subjectSpace == 828 then
            -- We are in the user, template or module namespaces.
            -- Add sandbox and testcases links.
            -- "Editors can experiment in this template's sandbox and testcases pages."
            text = text .. (p.makeExperimentBlurb(args, env) or '')
            text = text .. '<br>'
            if not args[1] and (not args.content or docTitle.exists) then
                -- "Please add categories to the /doc subpage."
                -- Don't show this message with inline docs or with an explicitly specified doc page,
                -- as then it is unclear where to add the categories.
                text = text .. (p.makeCategoriesBlurb(args, env) or '')
            end
            text = text .. ' ' .. (p.makeSubpagesBlurb(args, env) or '') --"Subpages of this template"
        end
    end


-- Assemble the fmbox text field.
    local endId = message('end-box-id')
local text = ''
    local endClass = message('end-box-classes')
if linkBox then
    local ebox = mw.html.create('div')
-- Use custom link box content if it is defined.
    ebox
text = text .. linkBox
        :attr('id', endId)
else
        :addClass(endClass)
text = text .. (p.makeDocPageBlurb(args, env) or '')
        :newline()
-- Add links to /sandbox and /testcases when appropriate.
        :wikitext(text)
if subjectSpace == 2 or subjectSpace == 828 or subjectSpace == 10 then
    return tostring(ebox)
-- We are in the user, module or template namespaces.
text = text .. p.makeEndBoxExperimentBlurb(args, env)
text = text .. '<br />'
-- Show the categories text, but not if "content" fed or "docname fed"
-- since then it is unclear where to add the categories.
if not content and not docnameFed then
text = text .. (p.makeCategoriesBlurb(args, env) or '')
end
-- Show the "subpages" link.
if subjectSpace ~= 6 then -- Don't show the link in file space.
text = text .. ' ' .. (p.makeSubpagesBlurb(args, env) or '')
end
-- Show the "print" link if it exists.
local printBlurb = p.makePrintBlurb(args, env)
if printBlurb then
text = text .. '<br />' .. printBlurb
end
end
end
fmargs.text = text
 
-- Return the fmbox output.
return messageBox.main('fmbox', fmargs)
end
end


function p.makePrintBlurb(args, env)
function p.makeDocPageBlurb(args, env)
-- Get the /Print title object
    --[=[
local printTitle = env.printTitle
    -- Makes the blurb "This documentation is transcluded from [[Template:Foo]] (edit, history)".
if not printTitle then
    -- @args - a table of arguments passed by the user
return nil
    -- @env - environment table containing title objects, etc., generated with p.getEnvironment
end
    --
-- Make the print blurb.
    -- Messages:
local ret
    -- 'edit-link-display' --> 'edit'
if printTitle.exists then
    -- 'history-link-display' --> 'history'
local printLink = makeWikilink(printTitle.prefixedText, message('printLinkDisplay', 'string'))
    -- 'transcluded-from-blurb' -->
ret = message('printBlurb', 'string', {printLink})
    -- 'The above [[Wikipedia:Template documentation|documentation]]
local displayPrintCategory = message('displayPrintCategory', 'boolean')
    -- is [[Help:Transclusion|transcluded]] from $1.'
if displayPrintCategory then
    -- 'module-preload' --> 'Template:Documentation/preload-module-doc'
ret = ret .. makeCategoryLink(message('printCategory', 'string'))
    -- 'create-link-display' --> 'create'
end
    -- 'create-module-doc-blurb' -->
end
    -- 'You might want to $1 a documentation page for this [[Wikipedia:Lua|Scribunto module]].'
return ret
    --]=]
    local docTitle = env.docTitle
    if not docTitle then
        return nil
    end
    local ret
    if docTitle.exists then
        -- /doc exists; link to it.
        local docLink = makeWikilink(docTitle.prefixedText)
        local editUrl = docTitle:fullUrl{action = 'edit'}
        local editDisplay = message('edit-link-display')
        local editLink = makeUrlLink(editUrl, editDisplay)
        local historyUrl = docTitle:fullUrl{action = 'history'}
        local historyDisplay = message('history-link-display')
        local historyLink = makeUrlLink(historyUrl, historyDisplay)
        ret = message('transcluded-from-blurb', {docLink})
            .. ' '
            .. makeToolbar(editLink, historyLink)
            .. '<br>'
    elseif env.subjectSpace == 10 then
        -- /doc does not exist for this template; ask to create it.
        local createUrl = docTitle:fullUrl{action = 'edit', preload = message('docpage-preload')}
        local createDisplay = message('create-link-display')
        local createLink = makeUrlLink(createUrl, createDisplay)
        ret = message('create-template-doc-blurb', {createLink})
            .. '<br>'
    elseif env.subjectSpace == 828 then
        -- /doc does not exist for this module; ask to create it.
        local createUrl = docTitle:fullUrl{action = 'edit', preload = message('module-preload')}
        local createDisplay = message('create-link-display')
        local createLink = makeUrlLink(createUrl, createDisplay)
        ret = message('create-module-doc-blurb', {createLink})
            .. '<br>'
    end
    return ret
end
end


function p.makeSubpagesBlurb(args, env)
function p.makeExperimentBlurb(args, env)
-- Get the template title object
    --[[
local subjectSpace = env.subjectSpace
    -- Renders the text "Editors can experiment in this template's sandbox (edit | diff) and testcases (edit) pages."
local templateTitle = env.templateTitle
    -- @args - a table of arguments passed by the user
if not subjectSpace or not templateTitle then
    -- @env - environment table containing title objects, etc., generated with p.getEnvironment
return nil
    --
end
    -- Messages:
-- Make the subpages blurb.
    -- 'sandbox-link-display' --> 'sandbox'
local pagetype
    -- 'sandbox-edit-link-display' --> 'edit'
if subjectSpace == 10 then
    -- 'compare-link-display' --> 'diff'
pagetype = message('templatePagetype', 'string')
    -- 'module-sandbox-preload' --> 'Template:Documentation/preload-module-sandbox'
elseif subjectSpace == 828 then
    -- 'template-sandbox-preload' --> 'Template:Documentation/preload-sandbox'
pagetype = message('modulePagetype', 'string')
    -- 'sandbox-create-link-display' --> 'create'
else
    -- 'mirror-edit-summary' --> 'Create sandbox version of $1'
pagetype = message('defaultPagetype', 'string')
    -- 'mirror-link-display' --> 'mirror'
end
    -- 'mirror-link-preload' --> 'Template:Documentation/mirror'
return makeWikilink(
    -- 'sandbox-link-display' --> 'sandbox'
'Special:PrefixIndex/' .. templateTitle.prefixedText .. '/',
    -- 'testcases-link-display' --> 'testcases'
message('subpagesLinkDisplay', 'string', {pagetype})
    -- 'testcases-edit-link-display'--> 'edit'
)
    -- 'template-sandbox-preload' --> 'Template:Documentation/preload-sandbox'
    -- 'testcases-create-link-display' --> 'create'
    -- 'testcases-link-display' --> 'testcases'
    -- 'testcases-edit-link-display' --> 'edit'
    -- 'module-testcases-preload' --> 'Template:Documentation/preload-module-testcases'
    -- 'template-testcases-preload' --> 'Template:Documentation/preload-testcases'
    -- 'experiment-blurb-module' --> 'Editors can experiment in this module's $1 and $2 pages.'
    -- 'experiment-blurb-template' --> 'Editors can experiment in this template's $1 and $2 pages.'
    --]]
    local subjectSpace = env.subjectSpace
    local templateTitle = env.templateTitle
    local sandboxTitle = env.sandboxTitle
    local testcasesTitle = env.testcasesTitle
    local templatePage = templateTitle.prefixedText
    if not subjectSpace or not templateTitle or not sandboxTitle or not testcasesTitle then
        return nil
    end
    -- Make links.
    local sandboxLinks, testcasesLinks
    if sandboxTitle.exists then
        local sandboxPage = sandboxTitle.prefixedText
        local sandboxDisplay = message('sandbox-link-display')
        local sandboxLink = makeWikilink(sandboxPage, sandboxDisplay)
        local sandboxEditUrl = sandboxTitle:fullUrl{action = 'edit'}
        local sandboxEditDisplay = message('sandbox-edit-link-display')
        local sandboxEditLink = makeUrlLink(sandboxEditUrl, sandboxEditDisplay)
        local compareUrl = env.compareUrl
        local compareLink
        if compareUrl then
            local compareDisplay = message('compare-link-display')
            compareLink = makeUrlLink(compareUrl, compareDisplay)
        end
        sandboxLinks = sandboxLink .. ' ' .. makeToolbar(sandboxEditLink, compareLink)
    else
        local sandboxPreload
        if subjectSpace == 828 then
            sandboxPreload = message('module-sandbox-preload')
        else
            sandboxPreload = message('template-sandbox-preload')
        end
        local sandboxCreateUrl = sandboxTitle:fullUrl{action = 'edit', preload = sandboxPreload}
        local sandboxCreateDisplay = message('sandbox-create-link-display')
        local sandboxCreateLink = makeUrlLink(sandboxCreateUrl, sandboxCreateDisplay)
        local mirrorSummary = message('mirror-edit-summary', {makeWikilink(templatePage)})
        local mirrorPreload = message('mirror-link-preload')
        local mirrorUrl = sandboxTitle:fullUrl{action = 'edit', preload = mirrorPreload, summary = mirrorSummary}
        if subjectSpace == 828 then
            mirrorUrl = sandboxTitle:fullUrl{action = 'edit', preload = templateTitle.prefixedText, summary = mirrorSummary}
        end
        local mirrorDisplay = message('mirror-link-display')
        local mirrorLink = makeUrlLink(mirrorUrl, mirrorDisplay)
        sandboxLinks = message('sandbox-link-display') .. ' ' .. makeToolbar(sandboxCreateLink, mirrorLink)
    end
    if testcasesTitle.exists then
        local testcasesPage = testcasesTitle.prefixedText
        local testcasesDisplay = message('testcases-link-display')
        local testcasesLink = makeWikilink(testcasesPage, testcasesDisplay)
        local testcasesEditUrl = testcasesTitle:fullUrl{action = 'edit'}
        local testcasesEditDisplay = message('testcases-edit-link-display')
        local testcasesEditLink = makeUrlLink(testcasesEditUrl, testcasesEditDisplay)
        -- for Modules, add testcases run link if exists
        if testcasesTitle.contentModel == "Scribunto" then
            local testcasesRunLinkDisplay = message('testcases-run-link-display')
            local testcasesRunLink = makeWikilink(testcasesTitle.prefixedText, testcasesRunLinkDisplay)
            testcasesLinks = testcasesLink .. ' ' .. makeToolbar(testcasesEditLink, testcasesRunLink)
        else
            testcasesLinks = testcasesLink .. ' ' .. makeToolbar(testcasesEditLink)
        end
    else
        local testcasesPreload
        if subjectSpace == 828 then
            testcasesPreload = message('module-testcases-preload')
        else
            testcasesPreload = message('template-testcases-preload')
        end
        local testcasesCreateUrl = testcasesTitle:fullUrl{action = 'edit', preload = testcasesPreload}
        local testcasesCreateDisplay = message('testcases-create-link-display')
        local testcasesCreateLink = makeUrlLink(testcasesCreateUrl, testcasesCreateDisplay)
        testcasesLinks = message('testcases-link-display') .. ' ' .. makeToolbar(testcasesCreateLink)
    end
    local messageName
    if subjectSpace == 828 then
        messageName = 'experiment-blurb-module'
    else
        messageName = 'experiment-blurb-template'
    end
    return message(messageName, {sandboxLinks, testcasesLinks})
end
end


function p.makeCategoriesBlurb(args, env)
function p.makeCategoriesBlurb(args, env)
-- Get the title object.
    --[[
local docTitle = env.docTitle
    -- Generates the text "Please add categories to the /doc subpage."
if not docTitle then
    -- @args - a table of arguments passed by the user
return nil
    -- @env - environment table containing title objects, etc., generated with p.getEnvironment
end
    -- Messages:
-- Make the blurb.
    -- 'doc-link-display' --> '/doc'
local docPathLink = makeWikilink(docTitle.prefixedText, message('docLinkDisplay', 'string'))
    -- 'add-categories-blurb' --> 'Please add categories to the $1 subpage.'
return message('addCategoriesBlurb', 'string', {docPathLink})
    --]]
    local docTitle = env.docTitle
    if not docTitle then
        return nil
    end
    local docPathLink = makeWikilink(docTitle.prefixedText, message('doc-link-display'))
    return message('add-categories-blurb', {docPathLink})
end
end


function p.makeDocPageBlurb(args, env)
function p.makeSubpagesBlurb(args, env)
-- Get the title object.
    --[[
local docTitle = env.docTitle
    -- Generates the "Subpages of this template" link.
if not docTitle then
    -- @args - a table of arguments passed by the user
return nil
    -- @env - environment table containing title objects, etc., generated with p.getEnvironment
end
   
-- Make the blurb.
    -- Messages:
local ret
    -- 'template-pagetype' --> 'template'
if docTitle.exists then
    -- 'module-pagetype' --> 'module'
-- /doc exists; link to it.
    -- 'default-pagetype' --> 'page'
local docLink = makeWikilink(docTitle.prefixedText)
    -- 'subpages-link-display' --> 'Subpages of this $1'
local editUrl = docTitle:fullUrl{action = 'edit'}
    --]]
local editDisplay = message('editLinkDisplay', 'string')
    local subjectSpace = env.subjectSpace
local editLink = makeUrlLink(editUrl, editDisplay)
    local templateTitle = env.templateTitle
local historyUrl = docTitle:fullUrl{action = 'history'}
    if not subjectSpace or not templateTitle then
local historyDisplay = message('historyLinkDisplay', 'string')
        return nil
local historyLink = makeUrlLink(historyUrl, historyDisplay)
    end
ret = message('transcludedFromBlurb', 'string', {docLink})
    local pagetype
.. ' '
    if subjectSpace == 10 then
.. makeToolbar(editLink, historyLink)
        pagetype = message('template-pagetype')
.. '<br />'
    elseif subjectSpace == 828 then
elseif env.subjectSpace == 828 then
        pagetype = message('module-pagetype')
-- /doc does not exist; ask to create it.
    else
local createUrl = docTitle:fullUrl{action = 'edit', preload = message('modulePreload', 'string')}
        pagetype = message('default-pagetype')
local createDisplay = message('createLinkDisplay', 'string')
    end
local createLink = makeUrlLink(createUrl, createDisplay)
    local subpagesLink = makeWikilink(
ret = message('createModuleDocBlurb', 'string', {createLink})
        'Special:PrefixIndex/' .. templateTitle.prefixedText .. '/',
.. '<br />'
        message('subpages-link-display', {pagetype})
end
    )
return ret
    return message('subpages-blurb', {subpagesLink})
end
 
function p.makeEndBoxExperimentBlurb(args, env)
-- Renders the text "Editors can experiment in this template's sandbox (edit | diff) and testcases (edit) pages."
-- Get environment data.
local subjectSpace = env.subjectSpace
local templateTitle = env.templateTitle
local sandboxTitle = env.sandboxTitle
local testcasesTitle = env.testcasesTitle
local templatePage = templateTitle.prefixedText
if not subjectSpace or not templateTitle or not sandboxTitle or not testcasesTitle then
return nil
end
-- Make links.
local sandboxLinks, testcasesLinks
if sandboxTitle.exists then
local sandboxPage = sandboxTitle.prefixedText
local sandboxDisplay = message('sandboxLinkDisplay', 'string')
local sandboxLink = makeWikilink(sandboxPage, sandboxDisplay)
local sandboxEditUrl = sandboxTitle:fullUrl{action = 'edit'}
local sandboxEditDisplay = message('sandboxEditLinkDisplay', 'string')
local sandboxEditLink = makeUrlLink(sandboxEditUrl, sandboxEditDisplay)
local compareUrl = env.compareUrl
local compareLink
if compareUrl then
local compareDisplay = message('compareLinkDisplay', 'string')
compareLink = makeUrlLink(compareUrl, compareDisplay)
end
sandboxLinks = sandboxLink .. ' ' .. makeToolbar(sandboxEditLink, compareLink)
else
local sandboxPreload
if subjectSpace == 828 then
sandboxPreload = message('moduleSandboxPreload', 'string')
else
sandboxPreload = message('templateSandboxPreload', 'string')
end
local sandboxCreateUrl = sandboxTitle:fullUrl{action = 'edit', preload = sandboxPreload}
local sandboxCreateDisplay = message('sandboxCreateLinkDisplay', 'string')
local sandboxCreateLink = makeUrlLink(sandboxCreateUrl, sandboxCreateDisplay)
local mirrorSummary = message('mirrorEditSummary', 'string', {makeWikilink(templatePage)})
local mirrorUrl = sandboxTitle:fullUrl{action = 'edit', preload = templatePage, summary = mirrorSummary}
local mirrorDisplay = message('mirrorLinkDisplay', 'string')
local mirrorLink = makeUrlLink(mirrorUrl, mirrorDisplay)
sandboxLinks = message('sandboxLinkDisplay', 'string') .. ' ' .. makeToolbar(sandboxCreateLink, mirrorLink)
end
if testcasesTitle.exists then
local testcasesPage = testcasesTitle.prefixedText
local testcasesDisplay = message('testcasesLinkDisplay', 'string')
local testcasesLink = makeWikilink(testcasesPage, testcasesDisplay)
local testcasesEditUrl = testcasesTitle:fullUrl{action = 'edit'}
local testcasesEditDisplay = message('testcasesEditLinkDisplay', 'string')
local testcasesEditLink = makeUrlLink(testcasesEditUrl, testcasesEditDisplay)
testcasesLinks = testcasesLink .. ' ' .. makeToolbar(testcasesEditLink)
else
local testcasesPreload
if subjectSpace == 828 then
testcasesPreload = message('moduleTestcasesPreload', 'string')
else
testcasesPreload = message('templateTestcasesPreload', 'string')
end
local testcasesCreateUrl = testcasesTitle:fullUrl{action = 'edit', preload = testcasesPreload}
local testcasesCreateDisplay = message('testcasesCreateLinkDisplay', 'string')
local testcasesCreateLink = makeUrlLink(testcasesCreateUrl, testcasesCreateDisplay)
testcasesLinks = message('testcasesLinkDisplay', 'string') .. ' ' .. makeToolbar(testcasesCreateLink)
end
local messageName
if subjectSpace == 828 then
messageName = 'experimentBlurbModule'
else
messageName = 'experimentBlurbTemplate'
end
return message(messageName, 'string', {sandboxLinks, testcasesLinks})
end
end


Line 768: Line 902:


function p.addTrackingCategories(env)
function p.addTrackingCategories(env)
-- Check if {{documentation}} is transcluded on a /doc or /testcases page.
    --[[
local title = env.title
    -- Check if {{documentation}} is transcluded on a /doc or /testcases page.
local ret = ''
    -- @env - environment table containing title objects, etc., generated with p.getEnvironment
local subpage = title.subpageText
   
if message('displayStrangeUsageCategory', 'boolean') and (subpage == message('docSubpage', 'string') or subpage == message('testcasesSubpage', 'string')) then
    -- Messages:
local sort = (title.namespace == 0 and message('strangeUsageCategoryMainspaceSort', 'string') or '') .. title.prefixedText -- Sort on namespace.
    -- 'display-strange-usage-category' --> true
ret = ret .. makeCategoryLink(message('strangeUsageCategory', 'string'), sort)
    -- 'doc-subpage' --> 'doc'
end
    -- 'testcases-subpage' --> 'testcases'
return ret
    -- 'strange-usage-category' --> 'Wikipedia pages with strange ((documentation)) usage'
    --
    -- /testcases pages in the module namespace are not categorised, as they may have
    -- {{documentation}} transcluded automatically.
    --]]
    local title = env.title
    local subjectSpace = env.subjectSpace
    if not title or not subjectSpace then
        return nil
    end
    local subpage = title.subpageText
    local ret = ''
    if message('display-strange-usage-category', nil, 'boolean')
        and (
            subpage == message('doc-subpage')
            or subjectSpace ~= 828 and subpage == message('testcases-subpage')
        )
    then
        ret = ret .. makeCategoryLink(message('strange-usage-category'))
    end
    return ret
end
end


return p
return p

Latest revision as of 02:28, 15 June 2023

Module documentation[view] [edit] [history] [purge]

Lua logo

This module depends on the following other modules:

This module displays a section containing documentation for templates, modules, or other pages. It implements the {{documentation}} template.

Usage

In most cases, you should use the {{documentation}} template. Refer to the template's documentation for usage instructions and parameters.

To use this module from another module, first require it:

local documentation = require('Module:Documentation').main

Then call the function with a table of arguments:

documentation{content = 'Some documentation', ['link box'] = 'My custom link box'}
This module was adapted from Module:Documentation on Wikipedia.
Adaptation is noted for reference and attribution only. This module may differ from the original in function or in usage.

-- This module implements {{documentation}}.

-- Get required modules.
local getArgs = require('Module:Arguments').getArgs

-- Get the config table.
local cfg = mw.loadData('Module:Documentation/config')

local p = {}

-- Often-used functions.
local ugsub = mw.ustring.gsub

----------------------------------------------------------------------------
-- Helper functions
--
-- These are defined as local functions, but are made available in the p
-- table for testing purposes.
----------------------------------------------------------------------------

local function message(cfgKey, valArray, expectType)
    --[[
    -- Gets a message from the cfg table and formats it if appropriate.
    -- The function raises an error if the value from the cfg table is not
    -- of the type expectType. The default type for expectType is 'string'.
    -- If the table valArray is present, strings such as $1, $2 etc. in the
    -- message are substituted with values from the table keys [1], [2] etc.
    -- For example, if the message "foo-message" had the value 'Foo $2 bar $1.',
    -- message('foo-message', {'baz', 'qux'}) would return "Foo qux bar baz."
    --]]
    local msg = cfg[cfgKey]
    expectType = expectType or 'string'
    if type(msg) ~= expectType then
        error('message: type error in message cfg.' .. cfgKey .. ' (' .. expectType .. ' expected, got ' .. type(msg) .. ')', 2)
    end
    if not valArray then
        return msg
    end

    local function getMessageVal(match)
        match = tonumber(match)
        return valArray[match] or error('message: no value found for key $' .. match .. ' in message cfg.' .. cfgKey, 4)
    end

    local ret = ugsub(msg, '$([1-9][0-9]*)', getMessageVal)
    return ret
end

p.message = message

local function makeWikilink(page, display)
    if display then
        return mw.ustring.format('[[%s|%s]]', page, display)
    else
        return mw.ustring.format('[[%s]]', page)
    end
end

p.makeWikilink = makeWikilink

local function makeCategoryLink(cat, sort)
    local catns = mw.site.namespaces[14].name
    return makeWikilink(catns .. ':' .. cat, sort)
end

p.makeCategoryLink = makeCategoryLink

local function makeUrlLink(url, display)
    return mw.ustring.format('[%s %s]', url, display)
end

p.makeUrlLink = makeUrlLink

local function makeToolbar(...)
    local ret = {}
    local lim = select('#', ...)
    if lim < 1 then
        return nil
    end
    for i = 1, lim do
        ret[#ret + 1] = select(i, ...)
    end
    local toolbar = mw.html.create('span')
    toolbar
        :addClass(message('end-box-toolbar-classes'))
        :wikitext('(' .. table.concat(ret, ' &#124; ') .. ')')
    return tostring(toolbar)
end 

p.makeToolbar = makeToolbar

----------------------------------------------------------------------------
-- Argument processing
----------------------------------------------------------------------------

local function makeInvokeFunc(funcName)
    return function (frame)
        local args = getArgs(frame, {
            valueFunc = function (key, value)
                if type(value) == 'string' then
                    value = value:match('^%s*(.-)%s*$') -- Remove whitespace.
                    if key == 'heading' or value ~= '' then
                        return value
                    else
                        return nil
                    end
                else
                    return value
                end
            end
        })
        return p[funcName](args)
    end
end

----------------------------------------------------------------------------
-- Main function
----------------------------------------------------------------------------

p.main = makeInvokeFunc('_main')

function p._main(args)
    --[[
    -- This function defines logic flow for the module.
    -- @args - table of arguments passed by the user
    -- 
    -- Messages:
    -- 'main-div-id' --> 'template-documentation'
    -- 'main-div-classes' --> 'template-documentation iezoomfix'
    --]]
    local env = p.getEnvironment(args)
    local root = mw.html.create()
    root
        :tag('div')
            :cssText('clear: both;')
            :done()
        :wikitext(p.sandboxNotice(args, env))
         -- This div tag is from {{documentation/start box}}, but moving it here
         -- so that we don't have to worry about unclosed tags.
        :tag('div')
            :attr('id', message('main-div-id'))
            :addClass(message('main-div-classes'))
            :addClass('clearfix')
            :newline()
            :wikitext(p._startBox(args, env))
            :wikitext(p._content(args, env))
            :done()
        :wikitext(p._endBox(args, env))
        :wikitext(p.addTrackingCategories(env))
    return tostring(root)
end

----------------------------------------------------------------------------
-- Environment settings
----------------------------------------------------------------------------

function p.getEnvironment(args)
    --[[
    -- Returns a table with information about the environment, including title objects and other namespace- or
    -- path-related data.
    -- @args - table of arguments passed by the user
    --
    -- Title objects include:
    -- env.title - the page we are making documentation for (usually the current title)
    -- env.templateTitle - the template (or module, file, etc.)
    -- env.docTitle - the /doc subpage.
    -- env.sandboxTitle - the /sandbox subpage.
    -- env.testcasesTitle - the /testcases subpage.
    --
    -- Data includes:
    -- env.subjectSpace - the number of the title's subject namespace.
    -- env.docSpace - the number of the namespace the title puts its documentation in.
    -- env.docpageBase - the text of the base page of the /doc, /sandbox and /testcases pages, with namespace.
    -- env.compareUrl - URL of the Special:ComparePages page comparing the sandbox with the template.
    -- 
    -- All table lookups are passed through pcall so that errors are caught. If an error occurs, the value
    -- returned will be nil.
    --]]
    
    local env, envFuncs = {}, {}

    -- Set up the metatable. If triggered we call the corresponding function in the envFuncs table. The value
    -- returned by that function is memoized in the env table so that we don't call any of the functions
    -- more than once. (Nils won't be memoized.)
    setmetatable(env, {
        __index = function (t, key)
            local envFunc = envFuncs[key]
            if envFunc then
                local success, val = pcall(envFunc)
                if success then
                    env[key] = val -- Memoise the value.
                    return val
                end
            end
            return nil
        end
    })  

    function envFuncs.title()
        -- The title object for the current page, or a test page passed with args.page.
        local title
        local titleArg = args.page
        if titleArg then
            title = mw.title.new(titleArg)
        else
            title = mw.title.getCurrentTitle()
        end
        return title
    end

    function envFuncs.templateTitle()
        --[[
        -- The template (or module, etc.) title object.
        -- Messages:
        -- 'sandbox-subpage' --> 'sandbox'
        -- 'testcases-subpage' --> 'testcases'
        --]]
        local subjectSpace = env.subjectSpace
        local title = env.title
        local subpage = title.subpageText
        if subpage == message('sandbox-subpage') or subpage == message('testcases-subpage') then
            return mw.title.makeTitle(subjectSpace, title.baseText)
        else
            return mw.title.makeTitle(subjectSpace, title.text)
        end
    end

    function envFuncs.docTitle()
        --[[
        -- Title object of the /doc subpage.
        -- Messages:
        -- 'doc-subpage' --> 'doc'
        --]]
        local title = env.title
        local docname = args[1] -- User-specified doc page.
        local docpage
        if docname then
            docpage = docname
        else
            docpage = env.docpageBase .. '/' .. message('doc-subpage')
        end
        return mw.title.new(docpage)
    end
    
    function envFuncs.sandboxTitle()
        --[[
        -- Title object for the /sandbox subpage.
        -- Messages:
        -- 'sandbox-subpage' --> 'sandbox'
        --]]
        return mw.title.new(env.docpageBase .. '/' .. message('sandbox-subpage'))
    end
    
    function envFuncs.testcasesTitle()
        --[[
        -- Title object for the /testcases subpage.
        -- Messages:
        -- 'testcases-subpage' --> 'testcases'
        --]]
        return mw.title.new(env.docpageBase .. '/' .. message('testcases-subpage'))
    end

    function envFuncs.subjectSpace()
        -- The subject namespace number.
        return mw.site.namespaces[env.title.namespace].subject.id
    end

    function envFuncs.docSpace()
        -- The documentation namespace number. For most namespaces this is the same as the
        -- subject namespace. However, pages in the Article, File, MediaWiki, Category or Widget
        -- namespaces must have their /doc, /sandbox and /testcases pages in talk space.
        local subjectSpace = env.subjectSpace
        if subjectSpace == 0 or subjectSpace == 6 or subjectSpace == 8 or subjectSpace == 14 or subjectSpace == 274 then
            return subjectSpace + 1
        else
            return subjectSpace
        end
    end

    function envFuncs.docpageBase()
        -- The base page of the /doc, /sandbox, and /testcases subpages.
        -- For some namespaces this is the talk page, rather than the template page.
        local templateTitle = env.templateTitle
        local docSpace = env.docSpace
        local docSpaceText = mw.site.namespaces[docSpace].name
        -- Assemble the link. docSpace is never the main namespace, so we can hardcode the colon.
        return docSpaceText .. ':' .. templateTitle.text
    end
    
    function envFuncs.compareUrl()
        -- Diff link between the sandbox and the main template using [[Special:ComparePages]].
        local templateTitle = env.templateTitle
        local sandboxTitle = env.sandboxTitle
        if templateTitle.exists and sandboxTitle.exists then
            local compareUrl = mw.uri.fullUrl(
                'Special:ComparePages',
                {page1 = templateTitle.prefixedText, page2 = sandboxTitle.prefixedText}
            )
            return tostring(compareUrl)
        else
            return nil
        end
    end     

    return env
end 

----------------------------------------------------------------------------
-- Auxiliary templates
----------------------------------------------------------------------------

function p.sandboxNotice(args, env)
    --[=[
    -- Generates a sandbox notice for display above sandbox pages.
    -- @args - a table of arguments passed by the user
    -- @env - environment table containing title objects, etc., generated with p.getEnvironment
    -- 
    -- Messages:
    -- 'sandbox-notice-image' --> '[[File:Sandbox.svg|50px|alt=|link=]]'
    -- 'sandbox-notice-blurb' --> 'This is the $1 for $2.'
    -- 'sandbox-notice-diff-blurb' --> 'This is the $1 for $2 ($3).'
    -- 'sandbox-notice-pagetype-template' --> '[[Wikipedia:Template test cases|template sandbox]] page'
    -- 'sandbox-notice-pagetype-module' --> '[[Wikipedia:Template test cases|module sandbox]] page'
    -- 'sandbox-notice-pagetype-other' --> 'sandbox page'
    -- 'sandbox-notice-compare-link-display' --> 'diff'
    -- 'sandbox-notice-testcases-blurb' --> 'See also the companion subpage for $1.'
    -- 'sandbox-notice-testcases-link-display' --> 'test cases'
    -- 'sandbox-category' --> 'Template sandboxes'
    --]=]
    local title = env.title
    local sandboxTitle = env.sandboxTitle
    local templateTitle = env.templateTitle
    local subjectSpace = env.subjectSpace
    if not (subjectSpace and title and sandboxTitle and templateTitle and mw.title.equals(title, sandboxTitle)) then
        return nil
    end
    -- Build the table of arguments to pass to {{ombox}}. We need just two fields, "image" and "text".
    local mboxargs = {
        type = 'content',
        image = message('sandbox-notice-image'),
    }
    -- Get the text. We start with the opening blurb, which is something like
    -- "This is the template sandbox for [[Template:Foo]] (diff)."
    local pagetype
    if subjectSpace == 10 then
        pagetype = message('sandbox-notice-pagetype-template')
    elseif subjectSpace == 828 then
        pagetype = message('sandbox-notice-pagetype-module')
    else
        pagetype = message('sandbox-notice-pagetype-other')
    end
    local templateLink = makeWikilink(templateTitle.prefixedText)
    local compareUrl = env.compareUrl
    if compareUrl then
        local compareDisplay = message('sandbox-notice-compare-link-display')
        local compareLink = mw.html.create('span')
            :addClass('plainlinks')
            :wikitext(makeUrlLink(compareUrl, compareDisplay))
        mboxargs.title = message('sandbox-notice-diff-blurb', {pagetype, templateLink, tostring(compareLink)})
    else
        mboxargs.title = message('sandbox-notice-blurb', {pagetype, templateLink})
    end
    -- Get the test cases page blurb if the page exists. This is something like
    -- "See also the companion subpage for [[Template:Foo/testcases|test cases]]."
    local testcasesTitle = env.testcasesTitle
    if testcasesTitle and testcasesTitle.exists then
        if testcasesTitle.contentModel == "Scribunto" then
            local testcasesLinkDisplay = message('sandbox-notice-testcases-link-display')
            local testcasesRunLinkDisplay = message('sandbox-notice-testcases-run-link-display')
            local testcasesLink = makeWikilink(testcasesTitle.prefixedText, testcasesLinkDisplay)
            local testcasesRunLink = makeWikilink(testcasesTitle.prefixedText, testcasesRunLinkDisplay)
            mboxargs.text = message('sandbox-notice-testcases-run-blurb', {testcasesLink, testcasesRunLink})
        else
            local testcasesLinkDisplay = message('sandbox-notice-testcases-link-display')
            local testcasesLink = makeWikilink(testcasesTitle.prefixedText, testcasesLinkDisplay)
            mboxargs.text = message('sandbox-notice-testcases-blurb', {testcasesLink})
        end
    end
    local ret = {
        mw.getCurrentFrame():expandTemplate{title = message('template-message-box'), args = mboxargs},
        makeCategoryLink(message('sandbox-category')) -- Add the sandbox to the sandbox category.
    }
    return table.concat(ret)
end

----------------------------------------------------------------------------
-- Start box
----------------------------------------------------------------------------

p.startBox = makeInvokeFunc('_startBox')

function p._startBox(args, env)
    --[[
    -- This function generates the start box.
    -- @args - a table of arguments passed by the user
    -- @env - environment table containing title objects, etc., generated with p.getEnvironment
    -- 
    -- The actual work is done by p.makeStartBoxLinksData and p.renderStartBoxLinks which make
    -- the [view] [edit] [history] [purge] links, and by p.makeStartBoxData and p.renderStartBox
    -- which generate the box HTML.
    --]]
    env = env or p.getEnvironment(args)
    local links
    local docTitle = env.docTitle
    if not args.content or docTitle.exists then
        -- No need to include the links if the documentation is on the template page itself.
        local linksData = p.makeStartBoxLinksData(args, env)
        if linksData then
            links = p.renderStartBoxLinks(linksData)
        end
    end
    -- Generate the start box html.
    local data = p.makeStartBoxData(args, env, links)
    if data then
        return p.renderStartBox(data)
    else
        -- User specified no heading.
        return nil
    end
end

function p.makeStartBoxLinksData(args, env)
    --[[
    -- Does initial processing of data to make the [view] [edit] [history] [purge] links.
    -- @args - a table of arguments passed by the user
    -- @env - environment table containing title objects, etc., generated with p.getEnvironment
    -- 
    -- Messages:
    -- 'view-link-display' --> 'view'
    -- 'edit-link-display' --> 'edit'
    -- 'history-link-display' --> 'history'
    -- 'purge-link-display' --> 'purge'
    -- 'module-preload' --> 'Template:Documentation/preload-module-doc'
    -- 'docpage-preload' --> 'Template:Documentation/preload'
    -- 'create-link-display' --> 'create'
    --]]
    local subjectSpace = env.subjectSpace
    local title = env.title
    local docTitle = env.docTitle
    if not title or not docTitle then
        return nil
    end
    if docTitle.isRedirect then 
        docTitle = docTitle.redirectTarget
    end

    local data = {}
    data.title = title
    data.docTitle = docTitle
    -- View, display, edit, and purge links if /doc exists.
    data.viewLinkDisplay = message('view-link-display')
    data.editLinkDisplay = message('edit-link-display')
    data.historyLinkDisplay = message('history-link-display')
    data.purgeLinkDisplay = message('purge-link-display')
    -- Create link if /doc doesn't exist.
    local preload = args.preload
    if not preload then
        if subjectSpace == 828 then -- Module namespace
            preload = message('module-preload')
        else
            preload = message('docpage-preload')
        end
    end
    data.preload = preload
    data.createLinkDisplay = message('create-link-display')
    return data
end

function p.renderStartBoxLinks(data)
    --[[
    -- Generates the [view][edit][history][purge] or [create][purge] links from the data table.
    -- @data - a table of data generated by p.makeStartBoxLinksData
    --]]
    
    local function escapeBrackets(s)
        -- Escapes square brackets with HTML entities.
        s = s:gsub('%[', '&#91;') -- Replace square brackets with HTML entities.
        s = s:gsub('%]', '&#93;')
        return s
    end

    local ret
    local docTitle = data.docTitle
    local title = data.title
    local purgeLink = makeUrlLink(title:fullUrl{action = 'purge'}, data.purgeLinkDisplay)
    if docTitle.exists then
        local viewLink = makeWikilink(docTitle.prefixedText, data.viewLinkDisplay)
        local editLink = makeUrlLink(docTitle:fullUrl{action = 'edit'}, data.editLinkDisplay)
        local historyLink = makeUrlLink(docTitle:fullUrl{action = 'history'}, data.historyLinkDisplay)
        local purgeLink = makeUrlLink(title:fullUrl{action = 'purge'}, data.purgeLinkDisplay)
        ret = '[%s] [%s] [%s] [%s]'
        ret = escapeBrackets(ret)
        ret = mw.ustring.format(ret, viewLink, editLink, historyLink, purgeLink)
    else
        local createLink = makeUrlLink(docTitle:fullUrl{action = 'edit', preload = data.preload}, data.createLinkDisplay)
        ret = '[%s] [%s]'
        ret = escapeBrackets(ret)
        ret = mw.ustring.format(ret, createLink, purgeLink)
    end
    return ret
end

function p.makeStartBoxData(args, env, links)
    --[=[
    -- Does initial processing of data to pass to the start-box render function, p.renderStartBox.
    -- @args - a table of arguments passed by the user
    -- @env - environment table containing title objects, etc., generated with p.getEnvironment
    -- @links - a string containing the [view][edit][history][purge] links - could be nil if there's an error.
    --
    -- Messages:
    -- 'documentation-icon-wikitext' --> '[[File:Test Template Info-Icon - Version (2).svg|50px|link=|alt=]]'
    -- 'template-namespace-heading' --> 'Template documentation'
    -- 'module-namespace-heading' --> 'Module documentation'
    -- 'widget-namespace-heading' --> 'Widget documentation'
    -- 'file-namespace-heading' --> 'Summary'
    -- 'other-namespaces-heading' --> 'Documentation'
    -- 'start-box-classes' --> 'template-documentation__start'
    -- 'start-box-heading-classes' --> 'template-documentation__heading'
    -- 'start-box-links-id' --> 'doc_editlinks'
    -- 'start-box-links-classes' --> 'mw-editsection-like plainlinks'
    -- 'testcases-create-link-display' --> 'create'
    --]=]
    local subjectSpace = env.subjectSpace
    if not subjectSpace then
        -- Default to an "other namespaces" namespace, so that we get at least some output
        -- if an error occurs.
        subjectSpace = 2
    end
    local data = {}
    
    -- Heading
    local heading = args.heading -- Blank values are not removed.
    if heading == '' then
        -- Don't display the start box if the heading arg is defined but blank.
        return nil
    end
    if heading then
        data.heading = heading
    elseif subjectSpace == 10 then -- Template namespace
        data.heading = message('documentation-icon-wikitext') .. ' ' .. message('template-namespace-heading')
    elseif subjectSpace == 828 then -- Module namespace
        data.heading = message('documentation-icon-wikitext') .. ' ' .. message('module-namespace-heading')
    elseif subjectSpace == 274 then -- Widget namespace
        data.heading = message('documentation-icon-wikitext') .. ' ' .. message('widget-namespace-heading')
    elseif subjectSpace == 6 then -- File namespace
        data.heading = message('file-namespace-heading')
    else
        data.heading = message('other-namespaces-heading')
    end

    -- CSS
    data.startClass = message('start-box-classes');
    
    -- Heading CSS
    data.headingClass = message('start-box-heading-classes');
    local headingStyle = args['heading-style']
    if headingStyle then
        data.headingStyleText = headingStyle
    end
    
    -- Data for the [view][edit][history][purge] or [create] links.
    if links then
        data.linksId = message('start-box-links-id')
        data.linksClass = message('start-box-links-classes')
        data.links = links
    end
    
    return data
end

function p.renderStartBox(data)
    -- Renders the start box html.
    -- @data - a table of data generated by p.makeStartBoxData.
    local sbox = mw.html.create('div')
    sbox
        :addClass(data.startClass)
        :newline()
        :tag('span')
            :addClass(data.headingClass)
            :cssText(data.headingStyleText)
            :wikitext(data.heading)
    local links = data.links
    if links then
        sbox:tag('span')
            :attr('id', data.linksId)
            :addClass(data.linksClass)
            :wikitext(links)
    end
    return tostring(sbox)
end

----------------------------------------------------------------------------
-- Documentation content
----------------------------------------------------------------------------

p.content = makeInvokeFunc('_content')

function p._content(args, env)
    -- Displays the documentation contents
    -- @args - a table of arguments passed by the user
    -- @env - environment table containing title objects, etc., generated with p.getEnvironment
    env = env or p.getEnvironment(args)
    local docTitle = env.docTitle
    local content = ''
    if docTitle and docTitle.exists then
        content = args._content or mw.getCurrentFrame():expandTemplate{title = docTitle.prefixedText}
    end
    if args.content then
        content = args.content .. '\n' .. content
    end
    -- The line breaks below are necessary so that "=== Headings ===" at the start and end
    -- of docs are interpreted correctly.
    return '\n' .. content .. '\n' 
end

----------------------------------------------------------------------------
-- End box
----------------------------------------------------------------------------

p.endBox = makeInvokeFunc('_endBox')

function p._endBox(args, env)
    --[=[
    -- This function generates the end box (also known as the link box).
    -- @args - a table of arguments passed by the user
    -- @env - environment table containing title objects, etc., generated with p.getEnvironment
    -- 
    -- Messages:
    -- 'end-box-id' --> 'documentation-meta-data'
    -- 'end-box-classes' --> 'template-documentation__end'
    --]=]
    
    -- Get environment data.
    env = env or p.getEnvironment(args)
    local subjectSpace = env.subjectSpace
    local docTitle = env.docTitle
    if not subjectSpace or not docTitle then
        return nil
    end
        
    -- Check whether we should output the end box at all. Add the end
    -- box by default if the documentation exists or if we are in the
    -- user, module or template namespaces.
    local linkBox = args['link box']
    if linkBox == 'off'
        or not (
            docTitle.exists
            or subjectSpace == 2
            or subjectSpace == 828
            or subjectSpace == 10
        )
    then
        return nil
    end

    -- Assemble the end box text
    local text = ''
    if linkBox then
        text = text .. linkBox
    else
        text = text .. (p.makeDocPageBlurb(args, env) or '') -- "This documentation is transcluded from [[Foo]]." 
        if subjectSpace == 2 or subjectSpace == 10 or subjectSpace == 828 then
            -- We are in the user, template or module namespaces.
            -- Add sandbox and testcases links.
            -- "Editors can experiment in this template's sandbox and testcases pages."
            text = text .. (p.makeExperimentBlurb(args, env) or '')
            text = text .. '<br>'
            if not args[1] and (not args.content or docTitle.exists) then
                -- "Please add categories to the /doc subpage."
                -- Don't show this message with inline docs or with an explicitly specified doc page,
                -- as then it is unclear where to add the categories.
                text = text .. (p.makeCategoriesBlurb(args, env) or '')
            end
            text = text .. ' ' .. (p.makeSubpagesBlurb(args, env) or '') --"Subpages of this template"
        end
    end

    local endId = message('end-box-id')
    local endClass = message('end-box-classes')
    local ebox = mw.html.create('div')
    ebox
        :attr('id', endId)
        :addClass(endClass)
        :newline()
        :wikitext(text)
    return tostring(ebox)
end

function p.makeDocPageBlurb(args, env)
    --[=[
    -- Makes the blurb "This documentation is transcluded from [[Template:Foo]] (edit, history)".
    -- @args - a table of arguments passed by the user
    -- @env - environment table containing title objects, etc., generated with p.getEnvironment
    -- 
    -- Messages:
    -- 'edit-link-display' --> 'edit'
    -- 'history-link-display' --> 'history'
    -- 'transcluded-from-blurb' --> 
    -- 'The above [[Wikipedia:Template documentation|documentation]] 
    -- is [[Help:Transclusion|transcluded]] from $1.'
    -- 'module-preload' --> 'Template:Documentation/preload-module-doc'
    -- 'create-link-display' --> 'create'
    -- 'create-module-doc-blurb' -->
    -- 'You might want to $1 a documentation page for this [[Wikipedia:Lua|Scribunto module]].'
    --]=]
    local docTitle = env.docTitle
    if not docTitle then
        return nil
    end
    local ret
    if docTitle.exists then
        -- /doc exists; link to it.
        local docLink = makeWikilink(docTitle.prefixedText)
        local editUrl = docTitle:fullUrl{action = 'edit'}
        local editDisplay = message('edit-link-display')
        local editLink = makeUrlLink(editUrl, editDisplay)
        local historyUrl = docTitle:fullUrl{action = 'history'}
        local historyDisplay = message('history-link-display')
        local historyLink = makeUrlLink(historyUrl, historyDisplay)
        ret = message('transcluded-from-blurb', {docLink})
            .. ' '
            .. makeToolbar(editLink, historyLink)
            .. '<br>'
    elseif env.subjectSpace == 10 then
        -- /doc does not exist for this template; ask to create it.
        local createUrl = docTitle:fullUrl{action = 'edit', preload = message('docpage-preload')}
        local createDisplay = message('create-link-display')
        local createLink = makeUrlLink(createUrl, createDisplay)
        ret = message('create-template-doc-blurb', {createLink})
            .. '<br>'
    elseif env.subjectSpace == 828 then
        -- /doc does not exist for this module; ask to create it.
        local createUrl = docTitle:fullUrl{action = 'edit', preload = message('module-preload')}
        local createDisplay = message('create-link-display')
        local createLink = makeUrlLink(createUrl, createDisplay)
        ret = message('create-module-doc-blurb', {createLink})
            .. '<br>'
    end
    return ret
end

function p.makeExperimentBlurb(args, env)
    --[[
    -- Renders the text "Editors can experiment in this template's sandbox (edit | diff) and testcases (edit) pages."
    -- @args - a table of arguments passed by the user
    -- @env - environment table containing title objects, etc., generated with p.getEnvironment
    -- 
    -- Messages:
    -- 'sandbox-link-display' --> 'sandbox'
    -- 'sandbox-edit-link-display' --> 'edit'
    -- 'compare-link-display' --> 'diff'
    -- 'module-sandbox-preload' --> 'Template:Documentation/preload-module-sandbox'
    -- 'template-sandbox-preload' --> 'Template:Documentation/preload-sandbox'
    -- 'sandbox-create-link-display' --> 'create'
    -- 'mirror-edit-summary' --> 'Create sandbox version of $1'
    -- 'mirror-link-display' --> 'mirror'
    -- 'mirror-link-preload' --> 'Template:Documentation/mirror'
    -- 'sandbox-link-display' --> 'sandbox'
    -- 'testcases-link-display' --> 'testcases'
    -- 'testcases-edit-link-display'--> 'edit'
    -- 'template-sandbox-preload' --> 'Template:Documentation/preload-sandbox'
    -- 'testcases-create-link-display' --> 'create'
    -- 'testcases-link-display' --> 'testcases'
    -- 'testcases-edit-link-display' --> 'edit'
    -- 'module-testcases-preload' --> 'Template:Documentation/preload-module-testcases'
    -- 'template-testcases-preload' --> 'Template:Documentation/preload-testcases'
    -- 'experiment-blurb-module' --> 'Editors can experiment in this module's $1 and $2 pages.'
    -- 'experiment-blurb-template' --> 'Editors can experiment in this template's $1 and $2 pages.'
    --]]
    local subjectSpace = env.subjectSpace
    local templateTitle = env.templateTitle
    local sandboxTitle = env.sandboxTitle
    local testcasesTitle = env.testcasesTitle
    local templatePage = templateTitle.prefixedText
    if not subjectSpace or not templateTitle or not sandboxTitle or not testcasesTitle then
        return nil
    end
    -- Make links.
    local sandboxLinks, testcasesLinks
    if sandboxTitle.exists then
        local sandboxPage = sandboxTitle.prefixedText
        local sandboxDisplay = message('sandbox-link-display')
        local sandboxLink = makeWikilink(sandboxPage, sandboxDisplay)
        local sandboxEditUrl = sandboxTitle:fullUrl{action = 'edit'}
        local sandboxEditDisplay = message('sandbox-edit-link-display')
        local sandboxEditLink = makeUrlLink(sandboxEditUrl, sandboxEditDisplay)
        local compareUrl = env.compareUrl
        local compareLink
        if compareUrl then
            local compareDisplay = message('compare-link-display')
            compareLink = makeUrlLink(compareUrl, compareDisplay)
        end
        sandboxLinks = sandboxLink .. ' ' .. makeToolbar(sandboxEditLink, compareLink)
    else
        local sandboxPreload
        if subjectSpace == 828 then
            sandboxPreload = message('module-sandbox-preload')
        else
            sandboxPreload = message('template-sandbox-preload')
        end
        local sandboxCreateUrl = sandboxTitle:fullUrl{action = 'edit', preload = sandboxPreload}
        local sandboxCreateDisplay = message('sandbox-create-link-display')
        local sandboxCreateLink = makeUrlLink(sandboxCreateUrl, sandboxCreateDisplay)
        local mirrorSummary = message('mirror-edit-summary', {makeWikilink(templatePage)})
        local mirrorPreload = message('mirror-link-preload')
        local mirrorUrl = sandboxTitle:fullUrl{action = 'edit', preload = mirrorPreload, summary = mirrorSummary}
        if subjectSpace == 828 then
            mirrorUrl = sandboxTitle:fullUrl{action = 'edit', preload = templateTitle.prefixedText, summary = mirrorSummary}
        end
        local mirrorDisplay = message('mirror-link-display')
        local mirrorLink = makeUrlLink(mirrorUrl, mirrorDisplay)
        sandboxLinks = message('sandbox-link-display') .. ' ' .. makeToolbar(sandboxCreateLink, mirrorLink)
    end
    if testcasesTitle.exists then
        local testcasesPage = testcasesTitle.prefixedText
        local testcasesDisplay = message('testcases-link-display')
        local testcasesLink = makeWikilink(testcasesPage, testcasesDisplay)
        local testcasesEditUrl = testcasesTitle:fullUrl{action = 'edit'}
        local testcasesEditDisplay = message('testcases-edit-link-display')
        local testcasesEditLink = makeUrlLink(testcasesEditUrl, testcasesEditDisplay)
        -- for Modules, add testcases run link if exists
        if testcasesTitle.contentModel == "Scribunto" then
            local testcasesRunLinkDisplay = message('testcases-run-link-display')
            local testcasesRunLink = makeWikilink(testcasesTitle.prefixedText, testcasesRunLinkDisplay)
            testcasesLinks = testcasesLink .. ' ' .. makeToolbar(testcasesEditLink, testcasesRunLink)
        else
            testcasesLinks = testcasesLink .. ' ' .. makeToolbar(testcasesEditLink)
        end
    else
        local testcasesPreload
        if subjectSpace == 828 then
            testcasesPreload = message('module-testcases-preload')
        else
            testcasesPreload = message('template-testcases-preload')
        end
        local testcasesCreateUrl = testcasesTitle:fullUrl{action = 'edit', preload = testcasesPreload}
        local testcasesCreateDisplay = message('testcases-create-link-display')
        local testcasesCreateLink = makeUrlLink(testcasesCreateUrl, testcasesCreateDisplay)
        testcasesLinks = message('testcases-link-display') .. ' ' .. makeToolbar(testcasesCreateLink)
    end
    local messageName
    if subjectSpace == 828 then
        messageName = 'experiment-blurb-module'
    else
        messageName = 'experiment-blurb-template'
    end
    return message(messageName, {sandboxLinks, testcasesLinks})
end

function p.makeCategoriesBlurb(args, env)
    --[[
    -- Generates the text "Please add categories to the /doc subpage."
    -- @args - a table of arguments passed by the user
    -- @env - environment table containing title objects, etc., generated with p.getEnvironment
    -- Messages:
    -- 'doc-link-display' --> '/doc'
    -- 'add-categories-blurb' --> 'Please add categories to the $1 subpage.'
    --]]
    local docTitle = env.docTitle
    if not docTitle then
        return nil
    end
    local docPathLink = makeWikilink(docTitle.prefixedText, message('doc-link-display'))
    return message('add-categories-blurb', {docPathLink})
end

function p.makeSubpagesBlurb(args, env)
    --[[
    -- Generates the "Subpages of this template" link.
    -- @args - a table of arguments passed by the user
    -- @env - environment table containing title objects, etc., generated with p.getEnvironment
    
    -- Messages:
    -- 'template-pagetype' --> 'template'
    -- 'module-pagetype' --> 'module'
    -- 'default-pagetype' --> 'page'
    -- 'subpages-link-display' --> 'Subpages of this $1'
    --]]
    local subjectSpace = env.subjectSpace
    local templateTitle = env.templateTitle
    if not subjectSpace or not templateTitle then
        return nil
    end
    local pagetype
    if subjectSpace == 10 then
        pagetype = message('template-pagetype')
    elseif subjectSpace == 828 then
        pagetype = message('module-pagetype')
    else
        pagetype = message('default-pagetype')
    end
    local subpagesLink = makeWikilink(
        'Special:PrefixIndex/' .. templateTitle.prefixedText .. '/',
        message('subpages-link-display', {pagetype})
    )
    return message('subpages-blurb', {subpagesLink})
end

----------------------------------------------------------------------------
-- Tracking categories
----------------------------------------------------------------------------

function p.addTrackingCategories(env)
    --[[
    -- Check if {{documentation}} is transcluded on a /doc or /testcases page.
    -- @env - environment table containing title objects, etc., generated with p.getEnvironment
    
    -- Messages:
    -- 'display-strange-usage-category' --> true
    -- 'doc-subpage' --> 'doc'
    -- 'testcases-subpage' --> 'testcases'
    -- 'strange-usage-category' --> 'Wikipedia pages with strange ((documentation)) usage'
    -- 
    -- /testcases pages in the module namespace are not categorised, as they may have
    -- {{documentation}} transcluded automatically.
    --]]
    local title = env.title
    local subjectSpace = env.subjectSpace
    if not title or not subjectSpace then
        return nil
    end
    local subpage = title.subpageText
    local ret = ''
    if message('display-strange-usage-category', nil, 'boolean')
        and (
            subpage == message('doc-subpage')
            or subjectSpace ~= 828 and subpage == message('testcases-subpage')
        )
    then
        ret = ret .. makeCategoryLink(message('strange-usage-category'))
    end
    return ret
end

return p