打开/关闭菜单
打开/关闭外观设置菜单
打开/关闭个人菜单
未登录
未登录用户的IP地址会在进行任意编辑后公开展示。

模块:Documentation:修订间差异

来自电棍ottowiki
 
(未显示同一用户的6个中间版本)
第1行: 第1行:
--源代码来自维基百科:https://zh.wikipedia.org/w/index.php?title=Module:Documentation&oldid=87749641
--该部分代码使用CC BY-SA 4.0许可证 (https://creativecommons.org/licenses/by-sa/4.0/)
-- This module implements {{documentation}}.
-- This module implements {{documentation}}.


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


-- Get the config table.
-- Get the config table.
第12行: 第14行:
-- Often-used functions.
-- Often-used functions.
local ugsub = mw.ustring.gsub
local ugsub = mw.ustring.gsub
local format = mw.ustring.format


-------------------------------------------------------------------------------
----------------------------------------------------------------------------
-- Helper functions
-- 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)
local function message(cfgKey, valArray, expectType)
    local msg = cfg[cfgKey]
local msg = cfg[cfgKey]
    expectType = expectType or 'string'
expectType = expectType or 'string'
    if type(msg) ~= expectType then
if type(msg) ~= expectType then
        error('message: type error in message cfg.' .. cfgKey .. ' (' ..
error('message: type error in message cfg.' .. cfgKey .. ' (' .. expectType .. ' expected, got ' .. type(msg) .. ')', 2)
            expectType .. ' expected, got ' .. type(msg) .. ')', 2)
end
    end
if not valArray then
    if not valArray then
return msg
        return msg
end
    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)
msg = ugsub(msg, '$([1-9][0-9]*)', getMessageVal)
    return ret
return msg
end
end


第40行: 第45行:


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 format('[[%s|%s]]', page, display)
    else
else
        return mw.ustring.format('[[%s]]', page)
return format('[[%s]]', page)
    end
end
end
end


第50行: 第55行:


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


第57行: 第62行:


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


第63行: 第68行:


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>'
return format('<span class="%s">(%s)</span>', message('toolbar-class'), table.concat(ret, ' | '))
end
end


p.makeToolbar = makeToolbar
p.makeToolbar = makeToolbar


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


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*$')
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


-------------------------------------------------------------------------------
----------------------------------------------------------------------------
-- Entry points
-- Entry points
-------------------------------------------------------------------------------
----------------------------------------------------------------------------


function p.nonexistent(frame)
function p.nonexistent(frame)
    if mw.title.getCurrentTitle().subpageText == 'testcases' then
return p.main(frame)
        return frame:expandTemplate{title = 'module test cases notice'}
    else
        return p.main(frame)
    end
end
end
-------------------------------------------------------------------------------
-- Main function
-------------------------------------------------------------------------------


p.main = makeInvokeFunc('_main')
p.main = makeInvokeFunc('_main')


function p._main(args)
function p._main(args)
    local env = p.getEnvironment(args)
local env = p.getEnvironment(args)
    local root = mw.html.create()
local root = mw.html.create()
    root
root
        :wikitext(p._getModuleWikitext(args, env))
:wikitext(p._getModuleWikitext(args, env))
        :wikitext(p.protectionTemplate(env))
:wikitext(p.protectionTemplate(env))
        :tag('div')
:wikitext(p.sandboxNotice(args, env))
            :attr('id', message('main-div-id'))
:tag('div')
            :addClass(message('main-div-classes'))
:addClass(message('container'))
            :newline()
:attr('role', 'complementary')
            :wikitext(p._startBox(args, env))
:attr('aria-labelledby', args.heading ~= '' and 'documentation-heading' or nil)
            :wikitext(p._content(args, env))
:attr('aria-label', args.heading == '' and 'Documentation' or nil)
            :tag('div')
:newline()
                :css('clear', 'both')
:tag('div')
                :newline()
:addClass(message('main-div-classes'))
                :done()
:newline()
            :done()
:wikitext(p._startBox(args, env))
        :wikitext(p._endBox(args, env))
:wikitext(p._content(args, env))
        :wikitext(p.addTrackingCategories(env))
:tag('div')
    return tostring(root)
:addClass(message('clear'))
:done()
:newline()
:done()
:wikitext(p._endBox(args, env))
:done()
:wikitext(p.addTrackingCategories(env))
return mw.getCurrentFrame():extensionTag('templatestyles', '', {src=cfg['templatestyles']}) .. tostring(root)
end
end


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


function p.getEnvironment(args)
function p.getEnvironment(args)
    local env, envFuncs = {}, {}
local env, envFuncs = {}, {}
    setmetatable(env, {
 
        __index = function (t, key)
setmetatable(env, {
            local envFunc = envFuncs[key]
__index = function (t, key)
            if envFunc then
local envFunc = envFuncs[key]
                local success, val = pcall(envFunc)
if envFunc then
                if success then
local success, val = pcall(envFunc)
                    env[key] = val
if success then
                    return val
env[key] = val
                end
return val
            end
end
            return nil
end
        end
return nil
    })
end
})
 
function envFuncs.title()
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()
local subjectSpace = env.subjectSpace
local title = env.title
local subpage = title.subpageText
if subpage == message('doc-subpage') then
return mw.title.makeTitle(subjectSpace, title.baseText)
else
return mw.title.makeTitle(subjectSpace, title.text)
end
end
 
function envFuncs.docTitle()
local title = env.title
local docname = args[1]
local docpage
if docname then
docpage = docname
else
docpage = env.docpageBase .. '/' .. message('doc-subpage')
end
return mw.title.new(docpage)
end
function envFuncs.protectionLevels()
return env.title.protectionLevels
end


    function envFuncs.title()
function envFuncs.subjectSpace()
        local titleArg = args.page
return mw.site.namespaces[env.title.namespace].subject.id
        if titleArg then
end
            return mw.title.new(titleArg)
        else
            return mw.title.getCurrentTitle()
        end
    end


    function envFuncs.templateTitle()
function envFuncs.docSpace()
        local subjectSpace = env.subjectSpace
local subjectSpace = env.subjectSpace
        local title = env.title
if subjectSpace == 0 or subjectSpace == 6 or subjectSpace == 8 or subjectSpace == 14 then
        local subpage = title.subpageText
return subjectSpace + 1
        if subpage == message('sandbox-subpage') or subpage == message('testcases-subpage') then
else
            return mw.title.makeTitle(subjectSpace, title.baseText)
return subjectSpace
        else
end
            return mw.title.makeTitle(subjectSpace, title.text)
end
        end
    end


    function envFuncs.docTitle()
function envFuncs.docpageBase()
        local title = env.title
local templateTitle = env.templateTitle
        local docname = args[1]
local docSpace = env.docSpace
        local docpage
local docSpaceText = mw.site.namespaces[docSpace].name
        if docname then
return docSpaceText .. ':' .. templateTitle.text
            docpage = docname
end
        else
            docpage = env.docpageBase .. '/' .. message('doc-subpage')
return env
        end
end
        return mw.title.new(docpage)
    end


    function envFuncs.sandboxTitle()
----------------------------------------------------------------------------
        return mw.title.new(env.docpageBase .. '/' .. message('sandbox-subpage'))
-- Auxiliary templates
    end
----------------------------------------------------------------------------


    function envFuncs.testcasesTitle()
p.getModuleWikitext = makeInvokeFunc('_getModuleWikitext')
        return mw.title.new(env.docpageBase .. '/' .. message('testcases-subpage'))
    end


    function envFuncs.printTitle()
function p._getModuleWikitext(args, env)
        return env.templateTitle:subPageTitle(message('print-subpage'))
local currentTitle = mw.title.getCurrentTitle()
    end
if currentTitle.contentModel ~= 'Scribunto' then return end
pcall(require, currentTitle.prefixedText)
local moduleWikitext = package.loaded["Module:Module wikitext"]
if moduleWikitext then
return moduleWikitext.main()
end
end


    function envFuncs.protectionLevels()
function p.sandboxNotice(args, env)
        return env.title.protectionLevels
return ''
    end
end


    function envFuncs.subjectSpace()
function p.protectionTemplate(env)
        return mw.site.namespaces[env.title.namespace].subject.id
local protectionLevels = env.protectionLevels
    end
if not protectionLevels then
return nil
end
local editProt = protectionLevels.edit and protectionLevels.edit[1]
local moveProt = protectionLevels.move and protectionLevels.move[1]
if editProt then
return require('Module:Protection banner')._main{
message('protection-reason-edit'), small = true
}
elseif moveProt and moveProt ~= 'autoconfirmed' then
return require('Module:Protection banner')._main{
action = 'move', small = true
}
else
return nil
end
end


    function envFuncs.docSpace()
----------------------------------------------------------------------------
        return mw.site.namespaces[env.title.namespace].subject.id
-- Start box
    end
----------------------------------------------------------------------------


    function envFuncs.docpageBase()
p.startBox = makeInvokeFunc('_startBox')
        local title = env.title
        local subject = mw.site.namespaces[title.namespace].subject.id
        if subject == title.namespace then
            return title.prefixedText
        else
            return title.prefixedText:match('^(.-)/')
        end
    end


    function envFuncs.compareUrl()
function p._startBox(args, env)
        if env.sandboxTitle and env.sandboxTitle.exists then
env = env or p.getEnvironment(args)
            return mw.title.makeTitle(4, 'Special:ComparePages'):fullUrl{
local links
                page = env.sandboxTitle.prefixedText,
local content = args.content
                target = env.templateTitle.prefixedText
if not content or args[1] then
            }
local linksData = p.makeStartBoxLinksData(args, env)
        else
if linksData then
            return nil
links = p.renderStartBoxLinks(linksData)
        end
end
    end
end
local data = p.makeStartBoxData(args, env, links)
if data then
return p.renderStartBox(data)
else
return nil
end
end


    return env
function p.makeStartBoxLinksData(args, env)
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 preload = args.preload
if not preload then
if subjectSpace == 828 then
preload = message('module-preload')
else
preload = message('docpage-preload')
end
end
return {
title = title,
docTitle = docTitle,
viewLinkDisplay = message('view-link-display'),
editLinkDisplay = message('edit-link-display'),
historyLinkDisplay = message('history-link-display'),
purgeLinkDisplay = message('purge-link-display'),
preload = preload,
createLinkDisplay = message('create-link-display')
}
end
end


-------------------------------------------------------------------------------
function p.renderStartBoxLinks(data)
-- End box (link box) construction
local docTitle = data.docTitle
-------------------------------------------------------------------------------
local title = data.title
local viewLink = makeWikilink(docTitle.prefixedText, data.viewLinkDisplay)
local editLink = makeWikilink('特殊:编辑页面/' .. docTitle.prefixedText, data.editLinkDisplay)
local historyLink = makeWikilink('特殊:页面历史/' .. docTitle.prefixedText, data.historyLinkDisplay)
local purgeLink = makeWikilink('特殊:清除缓存/' .. title.prefixedText, data.purgeLinkDisplay)
local createLink = makeUrlLink(docTitle:canonicalUrl{action = 'edit', preload = data.preload}, data.createLinkDisplay)
if docTitle.exists then
return viewLink .. ' ' .. editLink .. ' ' .. historyLink .. ' ' .. purgeLink
else
return createLink .. ' ' .. purgeLink
end
end


function p._endBox(args, env)
function p.makeStartBoxData(args, env, links)
    -- Get environment data.
local subjectSpace = env.subjectSpace
    env = env or p.getEnvironment(args)
if not subjectSpace then
    local subjectSpace = env.subjectSpace
subjectSpace = 2
    local docTitle = env.docTitle
end
    if not subjectSpace or not docTitle then
local data = {}
        return nil
    end
local heading = args.heading
if heading == '' then
return nil
end
if heading then
data.heading = heading
elseif subjectSpace == 10 then
data.heading = message('documentation-icon-wikitext') .. ' ' .. message('template-namespace-heading')
elseif subjectSpace == 828 then
data.heading = message('documentation-icon-wikitext') .. ' ' .. message('module-namespace-heading')
elseif subjectSpace == 6 then
data.heading = message('file-namespace-heading')
else
data.heading = message('other-namespaces-heading')
end
local headingStyle = args['heading-style']
if headingStyle then
data.headingStyleText = headingStyle
else
data.headingClass = message('main-div-heading-class')
end
if links then
data.linksClass = message('start-box-link-classes')
data.links = links
end
return data
end
 
function p.renderStartBox(data)
local sbox = mw.html.create('div')
sbox
:addClass(message('start-box-class'))
:newline()
:tag('span')
:addClass(data.headingClass)
:attr('id', 'documentation-heading')
:cssText(data.headingStyleText)
:wikitext(data.heading)
local links = data.links
if links then
sbox:wikitext(' ' .. links)
end
return tostring(sbox)
end
 
----------------------------------------------------------------------------
-- Documentation content
----------------------------------------------------------------------------
 
p.content = makeInvokeFunc('_content')
 
function p._content(args, env)
env = env or p.getEnvironment(args)
local docTitle = env.docTitle
local content = args.content
if not content and docTitle and docTitle.exists then
content = args._content or mw.getCurrentFrame():expandTemplate{title = docTitle.prefixedText}
end
return '\n' .. (content or '') .. '\n'
end
 
p.contentTitle = makeInvokeFunc('_contentTitle')
 
function p._contentTitle(args, env)
env = env or p.getEnvironment(args)
local docTitle = env.docTitle
if not args.content and docTitle and docTitle.exists then
return docTitle.prefixedText
else
return ''
end
end
 
----------------------------------------------------------------------------
-- End box
----------------------------------------------------------------------------


    -- Decide if end box should be shown.
p.endBox = makeInvokeFunc('_endBox')
    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}}.
function p._endBox(args, env)
    local fmargs = {}
env = env or p.getEnvironment(args)
    fmargs.id = message('fmbox-id')
local subjectSpace = env.subjectSpace
    fmargs.image = 'none'
local docTitle = env.docTitle
    fmargs.style = message('fmbox-style')
if not subjectSpace or not docTitle or not docTitle.exists then
    fmargs.textstyle = message('fmbox-textstyle')
return nil
end
local linkBox = args['link box']
if linkBox == 'off' then
return nil
end


    -- Assemble the fmbox text field.
local text = ''
    local text = ''
if linkBox then
    if linkBox then
text = text .. linkBox
        text = text .. linkBox
else
    else
text = text .. (p.makeDocPageBlurb(args, env) or '')
        if subjectSpace == 2 or subjectSpace == 10 or subjectSpace == 828 then
end
            if not args.content and not args[1] then
                text = text .. (p.makeCategoriesBlurb(args, env) or '')
local box = mw.html.create('div')
            end
box:attr('role', 'note')
        end
:addClass(message('end-box-class'))
    end
:addClass(message('end-box-plainlinks'))
    fmargs.text = text
:wikitext(text)
:done()


    return messageBox.main('fmbox', fmargs)
return '\n' .. tostring(box)
end
end


function p.makeCategoriesBlurb(args, env)
function p.makeDocPageBlurb(args, env)
    local docTitle = env.docTitle
local docTitle = env.docTitle
    if not docTitle then
if not docTitle or not docTitle.exists then
        return nil
return nil
    end
end
    local docPathLink = makeWikilink(docTitle.prefixedText, message('doc-link-display'))
local docLink = makeWikilink(docTitle.prefixedText)
    return message('add-categories-blurb', {docPathLink})
local editDisplay = message('edit-link-display')
local editLink = makeWikilink('特殊:编辑页面/' .. docTitle.prefixedText, editDisplay)
local historyDisplay = message('history-link-display')
local historyLink = makeWikilink('特殊:页面历史/' .. docTitle.prefixedText, historyDisplay)
return message('transcluded-from-blurb', {docLink}) .. ' ' .. makeToolbar(editLink, historyLink) .. '<br />'
end
end


----------------------------------------------------------------------------
-- Tracking categories
-- Tracking categories
----------------------------------------------------------------------------
function p.addTrackingCategories(env)
function p.addTrackingCategories(env)
    local title = env.title
local title = env.title
    local subjectSpace = env.subjectSpace
local subjectSpace = env.subjectSpace
    if not title or not subjectSpace then
if not title or not subjectSpace then
        return nil
return nil
    end
end
    local subpage = title.subpageText
local subpage = title.subpageText
    local ret = ''
if message('display-strange-usage-category', nil, 'boolean') and subpage == message('doc-subpage') then
    if message('display-strange-usage-category', nil, 'boolean')
return makeCategoryLink(message('strange-usage-category'))
            and (
end
                subpage == message('doc-subpage')
return ''
                or (subjectSpace ~= 828 and subpage == message('testcases-subpage'))
            )
    then
        ret = ret .. p.makeCategoryLink(message('strange-usage-category'))
    end
    return ret
end
end


return p
return p

2025年6月17日 (二) 23:01的最新版本

--源代码来自维基百科:https://zh.wikipedia.org/w/index.php?title=Module:Documentation&oldid=87749641
--该部分代码使用CC BY-SA 4.0许可证 (https://creativecommons.org/licenses/by-sa/4.0/)

-- 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
local format = mw.ustring.format

----------------------------------------------------------------------------
-- 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)
	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

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

p.message = message

local function makeWikilink(page, display)
	if display then
		return format('[[%s|%s]]', page, display)
	else
		return 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 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
	return format('<span class="%s">(%s)</span>', message('toolbar-class'), table.concat(ret, ' | '))
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

----------------------------------------------------------------------------
-- Entry points
----------------------------------------------------------------------------

function p.nonexistent(frame)
	return p.main(frame)
end

p.main = makeInvokeFunc('_main')

function p._main(args)
	local env = p.getEnvironment(args)
	local root = mw.html.create()
	root
		:wikitext(p._getModuleWikitext(args, env))
		:wikitext(p.protectionTemplate(env))
		:wikitext(p.sandboxNotice(args, env))
		:tag('div')
			:addClass(message('container'))
			:attr('role', 'complementary')
			:attr('aria-labelledby', args.heading ~= '' and 'documentation-heading' or nil)
			:attr('aria-label', args.heading == '' and 'Documentation' or nil)
			:newline()
			:tag('div')
				:addClass(message('main-div-classes'))
				:newline()
				:wikitext(p._startBox(args, env))
				:wikitext(p._content(args, env))
				:tag('div')
					:addClass(message('clear'))
					:done()
				:newline()
				:done()
			:wikitext(p._endBox(args, env))
			:done()
		:wikitext(p.addTrackingCategories(env))
	return mw.getCurrentFrame():extensionTag('templatestyles', '', {src=cfg['templatestyles']}) .. tostring(root)
end

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

function p.getEnvironment(args)
	local env, envFuncs = {}, {}

	setmetatable(env, {
		__index = function (t, key)
			local envFunc = envFuncs[key]
			if envFunc then
				local success, val = pcall(envFunc)
				if success then
					env[key] = val
					return val
				end
			end
			return nil
		end
	})	

	function envFuncs.title()
		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()
		local subjectSpace = env.subjectSpace
		local title = env.title
		local subpage = title.subpageText
		if subpage == message('doc-subpage') then
			return mw.title.makeTitle(subjectSpace, title.baseText)
		else
			return mw.title.makeTitle(subjectSpace, title.text)
		end
	end

	function envFuncs.docTitle()
		local title = env.title
		local docname = args[1]
		local docpage
		if docname then
			docpage = docname
		else
			docpage = env.docpageBase .. '/' .. message('doc-subpage')
		end
		return mw.title.new(docpage)
	end
	
	function envFuncs.protectionLevels()
		return env.title.protectionLevels
	end

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

	function envFuncs.docSpace()
		local subjectSpace = env.subjectSpace
		if subjectSpace == 0 or subjectSpace == 6 or subjectSpace == 8 or subjectSpace == 14 then
			return subjectSpace + 1
		else
			return subjectSpace
		end
	end

	function envFuncs.docpageBase()
		local templateTitle = env.templateTitle
		local docSpace = env.docSpace
		local docSpaceText = mw.site.namespaces[docSpace].name
		return docSpaceText .. ':' .. templateTitle.text
	end
	
	return env
end	

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

p.getModuleWikitext = makeInvokeFunc('_getModuleWikitext')

function p._getModuleWikitext(args, env)
	local currentTitle = mw.title.getCurrentTitle()
	if currentTitle.contentModel ~= 'Scribunto' then return end
	pcall(require, currentTitle.prefixedText)
	local moduleWikitext = package.loaded["Module:Module wikitext"]
	if moduleWikitext then
		return moduleWikitext.main()
	end
end

function p.sandboxNotice(args, env)
	return ''
end

function p.protectionTemplate(env)
	local protectionLevels = env.protectionLevels
	if not protectionLevels then
		return nil
	end
	local editProt = protectionLevels.edit and protectionLevels.edit[1]
	local moveProt = protectionLevels.move and protectionLevels.move[1]
	if editProt then
		return require('Module:Protection banner')._main{
			message('protection-reason-edit'), small = true
		}
	elseif moveProt and moveProt ~= 'autoconfirmed' then
		return require('Module:Protection banner')._main{
			action = 'move', small = true
		}
	else
		return nil
	end
end

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

p.startBox = makeInvokeFunc('_startBox')

function p._startBox(args, env)
	env = env or p.getEnvironment(args)
	local links
	local content = args.content
	if not content or args[1] then
		local linksData = p.makeStartBoxLinksData(args, env)
		if linksData then
			links = p.renderStartBoxLinks(linksData)
		end
	end
	local data = p.makeStartBoxData(args, env, links)
	if data then
		return p.renderStartBox(data)
	else
		return nil
	end
end

function p.makeStartBoxLinksData(args, env)
	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 preload = args.preload
	if not preload then
		if subjectSpace == 828 then
			preload = message('module-preload')
		else
			preload = message('docpage-preload')
		end
	end
	
	return {
		title = title,
		docTitle = docTitle,
		viewLinkDisplay = message('view-link-display'),
		editLinkDisplay = message('edit-link-display'),
		historyLinkDisplay = message('history-link-display'),
		purgeLinkDisplay = message('purge-link-display'),
		preload = preload,
		createLinkDisplay = message('create-link-display')
	}
end

function p.renderStartBoxLinks(data)
	local docTitle = data.docTitle
	local title = data.title
	local viewLink = makeWikilink(docTitle.prefixedText, data.viewLinkDisplay)
	local editLink = makeWikilink('特殊:编辑页面/' .. docTitle.prefixedText, data.editLinkDisplay)
	local historyLink = makeWikilink('特殊:页面历史/' .. docTitle.prefixedText, data.historyLinkDisplay)
	local purgeLink = makeWikilink('特殊:清除缓存/' .. title.prefixedText, data.purgeLinkDisplay)
	local createLink = makeUrlLink(docTitle:canonicalUrl{action = 'edit', preload = data.preload}, data.createLinkDisplay)
	
	if docTitle.exists then
		return viewLink .. ' ' .. editLink .. ' ' .. historyLink .. ' ' .. purgeLink
	else
		return createLink .. ' ' .. purgeLink
	end
end

function p.makeStartBoxData(args, env, links)
	local subjectSpace = env.subjectSpace
	if not subjectSpace then
		subjectSpace = 2
	end
	local data = {}
	
	local heading = args.heading
	if heading == '' then
		return nil
	end
	if heading then
		data.heading = heading
	elseif subjectSpace == 10 then
		data.heading = message('documentation-icon-wikitext') .. ' ' .. message('template-namespace-heading')
	elseif subjectSpace == 828 then
		data.heading = message('documentation-icon-wikitext') .. ' ' .. message('module-namespace-heading')
	elseif subjectSpace == 6 then
		data.heading = message('file-namespace-heading')
	else
		data.heading = message('other-namespaces-heading')
	end
	
	local headingStyle = args['heading-style']
	if headingStyle then
		data.headingStyleText = headingStyle
	else
		data.headingClass = message('main-div-heading-class')
	end
	
	if links then
		data.linksClass = message('start-box-link-classes')
		data.links = links
	end
	
	return data
end

function p.renderStartBox(data)
	local sbox = mw.html.create('div')
	sbox
		:addClass(message('start-box-class'))
		:newline()
		:tag('span')
			:addClass(data.headingClass)
			:attr('id', 'documentation-heading')
			:cssText(data.headingStyleText)
			:wikitext(data.heading)
	local links = data.links
	if links then
		sbox:wikitext(' ' .. links)
	end
	return tostring(sbox)
end

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

p.content = makeInvokeFunc('_content')

function p._content(args, env)
	env = env or p.getEnvironment(args)
	local docTitle = env.docTitle
	local content = args.content
	if not content and docTitle and docTitle.exists then
		content = args._content or mw.getCurrentFrame():expandTemplate{title = docTitle.prefixedText}
	end
	return '\n' .. (content or '') .. '\n' 
end

p.contentTitle = makeInvokeFunc('_contentTitle')

function p._contentTitle(args, env)
	env = env or p.getEnvironment(args)
	local docTitle = env.docTitle
	if not args.content and docTitle and docTitle.exists then
		return docTitle.prefixedText
	else
		return ''
	end
end

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

p.endBox = makeInvokeFunc('_endBox')

function p._endBox(args, env)
	env = env or p.getEnvironment(args)
	local subjectSpace = env.subjectSpace
	local docTitle = env.docTitle
	if not subjectSpace or not docTitle or not docTitle.exists then
		return nil
	end
	
	local linkBox = args['link box']
	if linkBox == 'off' then
		return nil
	end

	local text = ''
	if linkBox then
		text = text .. linkBox
	else
		text = text .. (p.makeDocPageBlurb(args, env) or '')
	end
	
	local box = mw.html.create('div')
	box:attr('role', 'note')
		:addClass(message('end-box-class'))
		:addClass(message('end-box-plainlinks'))
		:wikitext(text)
		:done()

	return '\n' .. tostring(box)
end

function p.makeDocPageBlurb(args, env)
	local docTitle = env.docTitle
	if not docTitle or not docTitle.exists then
		return nil
	end
	local docLink = makeWikilink(docTitle.prefixedText)
	local editDisplay = message('edit-link-display')
	local editLink = makeWikilink('特殊:编辑页面/' .. docTitle.prefixedText, editDisplay)
	local historyDisplay = message('history-link-display')
	local historyLink = makeWikilink('特殊:页面历史/' .. docTitle.prefixedText, historyDisplay)
	return message('transcluded-from-blurb', {docLink}) .. ' ' .. makeToolbar(editLink, historyLink) .. '<br />'
end

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

function p.addTrackingCategories(env)
	local title = env.title
	local subjectSpace = env.subjectSpace
	if not title or not subjectSpace then
		return nil
	end
	local subpage = title.subpageText
	if message('display-strange-usage-category', nil, 'boolean') and subpage == message('doc-subpage') then
		return makeCategoryLink(message('strange-usage-category'))
	end
	return ''
end

return p