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

模块:Documentation:修订间差异

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


-- 依赖模块
-- Get required modules.
local getArgs = require('Module:Arguments').getArgs
local getArgs = require('Module:Arguments').getArgs


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


local p = {}
local p = {}


-- 常用函数
-- Often-used functions.
local ugsub = mw.ustring.gsub
local ugsub = mw.ustring.gsub
local format = mw.ustring.format
local format = mw.ustring.format


-- 获取配置消息并做 $1、$2 替换
----------------------------------------------------------------------------
local function message(key, vals, expectType)
-- Helper functions
    local msg = cfg[key]
--
    expectType = expectType or "string"
-- These are defined as local functions, but are made available in the p
    if type(msg) ~= expectType then
-- table for testing purposes.
        error("message: cfg 键 " .. key .. " 类型错误(需要 " .. expectType .. ",得到 " .. type(msg) .. ")", 2)
----------------------------------------------------------------------------
    end
 
    if not vals then return msg end
local function message(cfgKey, valArray, expectType)
    local function repl(n)
local msg = cfg[cfgKey]
        n = tonumber(n)
expectType = expectType or 'string'
        return vals[n] or error("message: 无法找到 $"..n.." 对应的值,cfg 键 "..key, 4)
if type(msg) ~= expectType then
    end
error('message: type error in message cfg.' .. cfgKey .. ' (' .. expectType .. ' expected, got ' .. type(msg) .. ')', 2)
    return ugsub(msg, "%$([1-9][0-9]*)", repl)
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
end


-- 生成维基链接
p.message = message
 
local function makeWikilink(page, display)
local function makeWikilink(page, display)
    if display then
if display then
        return format("[[%s|%s]]", page, display)
return format('[[%s|%s]]', page, display)
    else
else
        return format("[[%s]]", page)
return format('[[%s]]', page)
    end
end
end
 
p.makeWikilink = makeWikilink
 
local function makeCategoryLink(cat, sort)
local catns = mw.site.namespaces[14].name
return makeWikilink(catns .. ':' .. cat, sort)
end
end


-- 生成外部 URL 链接
p.makeCategoryLink = makeCategoryLink
 
local function makeUrlLink(url, display)
local function makeUrlLink(url, display)
    return format("[%s %s]", url, display)
return format('[%s %s]', url, display)
end
end


-- 生成工具栏格式 (小号)
p.makeUrlLink = makeUrlLink
 
local function makeToolbar(...)
local function makeToolbar(...)
    local parts = {}
local ret = {}
    for i = 1, select("#", ...) do
local lim = select('#', ...)
        parts[#parts+1] = select(i, ...)
if lim < 1 then
    end
return nil
    if #parts == 0 then return "" end
end
    return "(" .. table.concat(parts, | ") .. ")"
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
end


-- 入口:处理 frame.args 并调用 _main
----------------------------------------------------------------------------
local function makeInvoke(fn)
-- Entry points
    return function(frame)
----------------------------------------------------------------------------
        local args = getArgs(frame, {
            valueFunc = function(k, v)
                if type(v)=="string" then
                    v = v:match("^%s*(.-)%s*$")
                    if k=="heading" or v~="" then return v end
                    return nil
                end
                return v
            end
        })
        return p[fn](args)
    end
end


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


p.main = makeInvoke("_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))
            :addClass(message("container"))
:tag('div')
            :newline()
:addClass(message('container'))
            :wikitext(p._startBox(args, env))
:attr('role', 'complementary')
            :wikitext(p._content(args, env))
:attr('aria-labelledby', args.heading ~= '' and 'documentation-heading' or nil)
            :tag("div")
:attr('aria-label', args.heading == '' and 'Documentation' or nil)
                :addClass(message("clear"))
:newline()
                :done()
:tag('div')
            :done()
:addClass(message('main-div-classes'))
        :wikitext(p._endBox(args, env))
:newline()
        :wikitext(p.addTrackingCategories(env))
:wikitext(p._startBox(args, env))
    return tostring(root)
: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
end


-- 环境信息
----------------------------------------------------------------------------
-- Environment settings
----------------------------------------------------------------------------
 
function p.getEnvironment(args)
function p.getEnvironment(args)
    local env, funcs = {}, {}
local env, envFuncs = {}, {}
    setmetatable(env, {
 
        __index = function(t, k)
setmetatable(env, {
            if funcs[k] then
__index = function (t, key)
                local ok, v = pcall(funcs[k])
local envFunc = envFuncs[key]
                if ok then
if envFunc then
                    env[k] = v
local success, val = pcall(envFunc)
                    return v
if success then
                end
env[key] = val
            end
return val
        end
end
    })
end
return nil
end
})


    funcs.title = function()
function envFuncs.title()
        if args.page then
local title
            return mw.title.new(args.page)
local titleArg = args.page
        else
if titleArg then
            return mw.title.getCurrentTitle()
title = mw.title.new(titleArg)
        end
else
    end
title = mw.title.getCurrentTitle()
end
return title
end


    funcs.subjectSpace = function()
function envFuncs.templateTitle()
        return mw.site.namespaces[env.title.namespace].subject.id
local subjectSpace = env.subjectSpace
    end
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


    funcs.templateTitle = function()
function envFuncs.docTitle()
        local title = env.title
local title = env.title
        return mw.title.makeTitle(env.subjectSpace, title.baseText or title.text)
local docname = args[1]
    end
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


    funcs.docTitle = function()
function envFuncs.subjectSpace()
        local base = env.templateTitle.prefixedText
return mw.site.namespaces[env.title.namespace].subject.id
        local sub = args[1] or (base .. "/" .. message("doc-subpage"))
end
        return mw.title.new(sub)
    end


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


    return env
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
end


-- 生成上方标题和工具链接
function p.sandboxNotice(args, env)
p.startBox = makeInvoke("_startBox")
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)
function p._startBox(args, env)
    -- icon + “模块文档”等标题
env = env or p.getEnvironment(args)
    local heading = ""
local links
    local space = env.subjectSpace
local content = args.content
    if space == 828 then
if not content or args[1] then
        heading = message("documentation-icon-wikitext") .. " " .. message("module-namespace-heading")
local linksData = p.makeStartBoxLinksData(args, env)
    elseif space == 10 then
if linksData then
        heading = message("documentation-icon-wikitext") .. " " .. message("template-namespace-heading")
links = p.renderStartBoxLinks(linksData)
    else
end
        heading = message("documentation-icon-wikitext") .. " " .. message("other-namespaces-heading")
end
    end
local data = p.makeStartBoxData(args, env, links)
 
if data then
    -- 开始构建 start box
return p.renderStartBox(data)
    local linksData = p.makeStartBoxLinksData(args, env)
else
    local links = linksData and p.renderStartBoxLinks(linksData) or ""
return nil
    return '<div class="' .. message("start-box-class") .. '">'
end
        .. '<span class="' .. message("main-div-heading-class") .. '">' .. heading .. '</span>'
        .. '<span class="' .. message("start-box-link-classes") .. '">' .. links .. '</span>'
        .. '</div>'
end
end


function p.makeStartBoxLinksData(args, env)
function p.makeStartBoxLinksData(args, env)
    local title = env.templateTitle
local subjectSpace = env.subjectSpace
    local doc = env.docTitle
local title = env.title
    if not title or not doc then return nil end
local docTitle = env.docTitle
if not title or not docTitle then
return nil
end
if docTitle.isRedirect then
docTitle = docTitle.redirectTarget
end


    local purge = makeWikilink("Special:Purge/" .. title.prefixedText, message("purge-link-display"))
local preload = args.preload
    if doc.exists then
if not preload then
        local view = makeWikilink(doc.prefixedText, message("view-link-display"))
if subjectSpace == 828 then
        local edit = makeWikilink("Special:EditPage/" .. doc.prefixedText, message("edit-link-display"))
preload = message('module-preload')
        local hist = makeWikilink("Special:PageHistory/" .. doc.prefixedText, message("history-link-display"))
else
        return { view=view, edit=edit, history=hist, purge=purge }
preload = message('docpage-preload')
    else
end
        local url = doc:canonicalUrl{ action="edit", preload=message("module-preload") }
end
        local create = makeUrlLink(url, message("create-link-display"))
        return { create=create, purge=purge }
return {
    end
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)
function p.renderStartBoxLinks(data)
    if data.create then
local docTitle = data.docTitle
        return makeToolbar(data.create, data.purge)
local title = data.title
    else
local viewLink = makeWikilink(docTitle.prefixedText, data.viewLinkDisplay)
        return makeToolbar(data.view, data.edit, data.history, data.purge)
local editLink = makeWikilink('特殊:编辑页面/' .. docTitle.prefixedText, data.editLinkDisplay)
    end
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
end


-- 文档主体内容
function p.makeStartBoxData(args, env, links)
p.content = makeInvoke("_content")
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)
function p._content(args, env)
    local doc = env.docTitle
env = env or p.getEnvironment(args)
    local content = args.content
local docTitle = env.docTitle
    if not content and doc and doc.exists then
local content = args.content
        content = mw.getCurrentFrame():expandTemplate{ title = doc.prefixedText }
if not content and docTitle and docTitle.exists then
    end
content = args._content or mw.getCurrentFrame():expandTemplate{title = docTitle.prefixedText}
    return "\n" .. (content or "") .. "\n"
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


-- 末尾仅保留分类提示
----------------------------------------------------------------------------
p.endBox = makeInvoke("_endBox")
-- End box
----------------------------------------------------------------------------
 
p.endBox = makeInvokeFunc('_endBox')
 
function p._endBox(args, env)
function p._endBox(args, env)
    local space = env.subjectSpace
env = env or p.getEnvironment(args)
    local doc = env.docTitle
local subjectSpace = env.subjectSpace
    if not space or not doc then return nil end
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 doc.exists or space==2 or space==10 or space==828 then
if linkBox then
        if not args.content and not args[1] then
text = text .. linkBox
            local catBlurb = p.makeCategoriesBlurb(args, env)
else
            if catBlurb and catBlurb ~= "" then
text = text .. (p.makeDocPageBlurb(args, env) or '')
                return '<div role="note" class="' .. message("end-box-class")
end
                    .. ' ' .. message("end-box-plainlinks") .. '">' .. catBlurb .. '</div>'
            end
local box = mw.html.create('div')
        end
box:attr('role', 'note')
    end
:addClass(message('end-box-class'))
    return nil
:addClass(message('end-box-plainlinks'))
end
:wikitext(text)
:done()


function p.makeCategoriesBlurb(args, env)
return '\n' .. tostring(box)
    local doc = env.docTitle
    if not doc then return nil end
    local link = makeWikilink(doc.prefixedText, message("doc-link-display"))
    return message("add-categories-blurb", { link })
end
end


-- 跟踪分类
function p.makeDocPageBlurb(args, env)
function p.addTrackingCategories(env)
local docTitle = env.docTitle
    local title = env.title
if not docTitle or not docTitle.exists then
    local space = env.subjectSpace
return nil
    if not title or not space then return "" end
end
    local sub = title.subpageText
local docLink = makeWikilink(docTitle.prefixedText)
    if message("display-strange-usage-category", nil, "boolean")
local editDisplay = message('edit-link-display')
      and ( sub == message("doc-subpage")
local editLink = makeWikilink('特殊:编辑页面/' .. docTitle.prefixedText, editDisplay)
            or (space~=828 and sub==message("testcases-subpage")) )
local historyDisplay = message('history-link-display')
    then
local historyLink = makeWikilink('特殊:页面历史/' .. docTitle.prefixedText, historyDisplay)
        return makeWikilink(mw.site.namespaces[14].name .. ":" .. message("strange-usage-category"))
return message('transcluded-from-blurb', {docLink}) .. ' ' .. makeToolbar(editLink, historyLink) .. '<br />'
    end
    return ""
end
end


-- 获取模块自身 wikitext(保持原版逻辑)
----------------------------------------------------------------------------
function p._getModuleWikitext(args, env)
-- Tracking categories
    local t = mw.title.getCurrentTitle()
----------------------------------------------------------------------------
    if t.contentModel ~= "Scribunto" then return end
    pcall(require, t.prefixedText)
    local mwtext = package.loaded["Module:Module wikitext"]
    if mwtext then return mwtext.main() end
end


-- 保护图标(保持原版)
function p.addTrackingCategories(env)
function p.protectionTemplate(env)
local title = env.title
    local prot = env.protectionLevels
local subjectSpace = env.subjectSpace
    if not prot then return nil end
if not title or not subjectSpace then
    if prot.edit and prot.edit[1] then
return nil
        return require("Module:Protection banner")._main{ message = message("protection-reason-edit"), small = true }
end
    end
local subpage = title.subpageText
    return nil
if message('display-strange-usage-category', nil, 'boolean') and subpage == message('doc-subpage') then
return makeCategoryLink(message('strange-usage-category'))
end
return ''
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