跳转到内容
打开/关闭菜单
  • 451 条目
  • 798 文件
  • 22 用户
  • 8938 编辑
Blockland Wiki
打开/关闭外观设置菜单
打开/关闭个人菜单
未登录
未登录用户的IP地址会在进行任意编辑后公开展示。

模块:Background:修订间差异

来自Blockland Wiki
创建页面,内容为“ local p = {} -- 清理参数 local function trim(value) return mw.text.trim(tostring(value or "")) end -- 透明度限制在 0 到 1 local function opacity(value, default) local number = tonumber(trim(value)) if not number then return default end number = math.max(0, math.min(1, number)) return tostring(number) end -- 只接受安全、常见的图片定位写法 local validPositions = { center = true, top = true, bottom = true, left = true,…”
 
无编辑摘要
 
第2行: 第2行:
local p = {}
local p = {}


-- 清理参数
local function trim(value)
local function trim(value)
return mw.text.trim(tostring(value or ""))
return mw.text.trim(tostring(value or ""))
end
end


-- 透明度限制在 0 到 1
local function getOpacity(value, default)
local function opacity(value, default)
local number = tonumber(trim(value))
local number = tonumber(trim(value))


if not number then
if not number or number ~= number then
return default
return default
end
end
第19行: 第17行:
end
end


-- 只接受安全、常见的图片定位写法
local positionNames = {
local validPositions = {
center = true,
center = true,
top = true,
top = true,
bottom = true,
bottom = true,
left = true,
left = true,
right = true,
right = true
}
 
local validPositions = {
center = true,
["center center"] = true,
["center top"] = true,
["center top"] = true,
["center bottom"] = true,
["center bottom"] = true,
第34行: 第36行:
["left bottom"] = true,
["left bottom"] = true,
["right bottom"] = true,
["right bottom"] = true,
["center center"] = true
top = true,
bottom = true,
left = true,
right = true
}
}


local function validPositionToken(token)
local function validPositionToken(value)
if token:match("^%-?%d+%.?%d*%%$") then
if positionNames[value] then
return true
return true
end
end


if token:match("^%-?%d+%.?%d*px$") then
if value == "0" then
return true
return true
end
end


return tonumber(token) == 0
return value:match("^%-?%d+%.?%d*%%$") ~= nil
or value:match("^%-?%d+%.?%d*px$") ~= nil
end
end


local function position(value)
local function getPosition(value)
value = trim(value)
value = trim(value)


第67行: 第73行:
end
end


-- 限制颜色格式,避免把任意内容拼入 CSS
local colorNames = {
local colorNames = {
white = true,
white = true,
第81行: 第86行:
}
}


local function safeColor(value)
local function getColor(value)
value = trim(value)
value = trim(value)


if value:match("^#%x%x%x$") or
if value:match("^#%x%x%x$")
value:match("^#%x%x%x%x%x%x$") then
or value:match("^#%x%x%x%x%x%x$") then
return value
return value
end
end
第98行: 第103行:
end
end


-- 解析外部图片 URL 或 Wiki 文件名
-- 支持直接 URL、普通文件名,以及被展开成 HTML 的文件链接。
local function getImageSource(frame, image)
local function getImageSource(frame, value)
image = trim(image)
value = trim(value)


if image == "" then
if value == "" then
return ""
return ""
end
end


-- 允许 HTTPS、HTTP 或协议相对的外链
-- 如果模板参数已被展开成图片 HTML,提取其 src。
if image:match("^https?://") or image:match("^//") then
local embedded =
return image
value:match('<img[^>]-src="([^"]+)"')
or value:match("<img[^>]-src='([^']+)'")
 
if embedded then
return embedded:gsub("&amp;", "&")
end
end


-- 内部文件:支持 File:、Image:、文件: 前缀
-- 去除尚未展开的 Wiki 文件链接标记及显示参数。
image = image:gsub("^[Ff]ile:", "")
value = value:gsub("^%[%[", "")
image = image:gsub("^[Ii]mage:", "")
value = value:gsub("%]%]$", "")
image = image:gsub("^文件:", "")
value = value:match("^([^|]+)") or value
value = trim(value)
 
-- 外部图片地址。
if value:match("^https?://%S+$")
or value:match("^//%S+$") then
return value
end
 
-- 已经是站内图片路径。
if value:match("^/images/")
or value:match("^/wiki/Special:FilePath/") then
return value
end
 
-- 转换成纯文件名,不要把文件链接语法交给 filepath。
value = value:gsub("^[Ff][Ii][Ll][Ee]:", "")
value = value:gsub("^[Ii][Mm][Aa][Gg][Ee]:", "")
value = value:gsub("^文件:", "")
value = value:gsub("^图像:", "")
value = trim(value)


local ok, result = pcall(function()
local ok, result = pcall(function()
return frame:callParserFunction("filepath", image)
return frame:callParserFunction("filepath", value)
end)
end)


if ok and result and result ~= "" then
if ok and result then
return result
result = trim(result)
 
if result ~= ""
and not result:find("<", 1, true)
and not result:find(">", 1, true) then
return result
end
end
end


第129行: 第164行:
function p.main(frame)
function p.main(frame)
local parent = frame:getParent()
local parent = frame:getParent()
local args = (parent or frame).args
local args = parent and parent.args or frame.args
 
local image = args["图片"]
or args["url"]
or args[1]
or ""


local image = args["图片"] or args["url"] or args[1] or ""
local src = getImageSource(frame, image)
local src = getImageSource(frame, image)


local imageOpacity = opacity(args["透明度"] or args["opacity"], 1)
local imageOpacity = getOpacity(
local imagePosition = position(args["位置"] or args["position"] or "center")
args["透明度"] or args["opacity"],
1
)
 
local imagePosition = getPosition(
args["位置"] or args["position"] or "center"
)
 
local baseColor = getColor(
args["背景色"] or args["颜色"] or args["color"]
)


local baseColor = safeColor(args["背景色"] or args["颜色"] or args["color"])
local shadeColor = getColor(
local shadeColor = safeColor(args["遮罩颜色"] or args["shadeColor"])
args["遮罩颜色"] or args["shadeColor"]
local shadeOpacity = opacity(args["遮罩透明度"] or args["shade"], 0)
)
 
local shadeOpacity = getOpacity(
args["遮罩透明度"] or args["shade"],
0
)


-- 无图片、无背景色、无有效遮罩时不输出
if src == "" and baseColor == "" then
if src == "" and baseColor == "" then
return ""
return ""
end
end


-- 最外层位于负层级;不需要 pointer-events
-- 整个背景位于较低层级,不应截获鼠标事件。
local rootStyle =
local rootStyle =
"position:fixed;" ..
"position:fixed;" ..
第154行: 第207行:
"height:100%;" ..
"height:100%;" ..
"z-index:-1;" ..
"z-index:-1;" ..
"overflow:hidden;"
"overflow:hidden;" ..
"pointer-events:none;"


if baseColor ~= "" then
if baseColor ~= "" then
rootStyle = rootStyle .. "background-color:" .. baseColor .. ";"
rootStyle = rootStyle ..
"background-color:" .. baseColor .. ";"
end
end


第165行: 第220行:
:cssText(rootStyle)
:cssText(rootStyle)


-- 图片层
-- 第一层:背景图片。
if src ~= "" then
if src ~= "" then
local img = mw.html.create("img")
local img = mw.html.create("img")
第177行: 第232行:
"width:100%;" ..
"width:100%;" ..
"height:100%;" ..
"height:100%;" ..
"max-width:none;" ..
"object-fit:cover;" ..
"object-fit:cover;" ..
"object-position:" .. imagePosition .. ";" ..
"object-position:" .. imagePosition .. ";" ..
"opacity:" .. imageOpacity .. ";"
"opacity:" .. imageOpacity .. ";" ..
"pointer-events:none;"
)
)


root:wikitext(tostring(img))
-- 关键:插入 HTML 节点,不再使用 wikitext(tostring(img))。
root:node(img)
end
end


-- 可选颜色遮罩
-- 第二层:颜色遮罩,用来降低背景的视觉干扰。
if shadeColor ~= "" and tonumber(shadeOpacity) > 0 then
if shadeColor ~= ""
and tonumber(shadeOpacity) > 0 then
 
local shade = mw.html.create("div")
local shade = mw.html.create("div")
:addClass("bl-page-bg-shade")
:addClass("bl-page-bg-shade")
第196行: 第256行:
"height:100%;" ..
"height:100%;" ..
"background-color:" .. shadeColor .. ";" ..
"background-color:" .. shadeColor .. ";" ..
"opacity:" .. shadeOpacity .. ";"
"opacity:" .. shadeOpacity .. ";" ..
"pointer-events:none;"
)
)


root:wikitext(tostring(shade))
root:node(shade)
end
end



2026年10月9日 (五) 23:13的最新版本

此模块的文档可以在模块:Background/doc创建


local p = {}

local function trim(value)
	return mw.text.trim(tostring(value or ""))
end

local function getOpacity(value, default)
	local number = tonumber(trim(value))

	if not number or number ~= number then
		return default
	end

	number = math.max(0, math.min(1, number))
	return tostring(number)
end

local positionNames = {
	center = true,
	top = true,
	bottom = true,
	left = true,
	right = true
}

local validPositions = {
	center = true,
	["center center"] = true,
	["center top"] = true,
	["center bottom"] = true,
	["left center"] = true,
	["right center"] = true,
	["left top"] = true,
	["right top"] = true,
	["left bottom"] = true,
	["right bottom"] = true,
	top = true,
	bottom = true,
	left = true,
	right = true
}

local function validPositionToken(value)
	if positionNames[value] then
		return true
	end

	if value == "0" then
		return true
	end

	return value:match("^%-?%d+%.?%d*%%$") ~= nil
		or value:match("^%-?%d+%.?%d*px$") ~= nil
end

local function getPosition(value)
	value = trim(value)

	if validPositions[value] then
		return value
	end

	local x, y = value:match("^(%S+)%s+(%S+)$")

	if x and y
		and validPositionToken(x)
		and validPositionToken(y) then
		return x .. " " .. y
	end

	return "center center"
end

local colorNames = {
	white = true,
	black = true,
	transparent = true,
	red = true,
	blue = true,
	green = true,
	yellow = true,
	gray = true,
	grey = true,
	navy = true
}

local function getColor(value)
	value = trim(value)

	if value:match("^#%x%x%x$")
		or value:match("^#%x%x%x%x%x%x$") then
		return value
	end

	local lower = value:lower()

	if colorNames[lower] then
		return lower
	end

	return ""
end

-- 支持直接 URL、普通文件名,以及被展开成 HTML 的文件链接。
local function getImageSource(frame, value)
	value = trim(value)

	if value == "" then
		return ""
	end

	-- 如果模板参数已被展开成图片 HTML,提取其 src。
	local embedded =
		value:match('<img[^>]-src="([^"]+)"')
		or value:match("<img[^>]-src='([^']+)'")

	if embedded then
		return embedded:gsub("&amp;", "&")
	end

	-- 去除尚未展开的 Wiki 文件链接标记及显示参数。
	value = value:gsub("^%[%[", "")
	value = value:gsub("%]%]$", "")
	value = value:match("^([^|]+)") or value
	value = trim(value)

	-- 外部图片地址。
	if value:match("^https?://%S+$")
		or value:match("^//%S+$") then
		return value
	end

	-- 已经是站内图片路径。
	if value:match("^/images/")
		or value:match("^/wiki/Special:FilePath/") then
		return value
	end

	-- 转换成纯文件名,不要把文件链接语法交给 filepath。
	value = value:gsub("^[Ff][Ii][Ll][Ee]:", "")
	value = value:gsub("^[Ii][Mm][Aa][Gg][Ee]:", "")
	value = value:gsub("^文件:", "")
	value = value:gsub("^图像:", "")
	value = trim(value)

	local ok, result = pcall(function()
		return frame:callParserFunction("filepath", value)
	end)

	if ok and result then
		result = trim(result)

		if result ~= ""
			and not result:find("<", 1, true)
			and not result:find(">", 1, true) then
			return result
		end
	end

	return ""
end

function p.main(frame)
	local parent = frame:getParent()
	local args = parent and parent.args or frame.args

	local image = args["图片"]
		or args["url"]
		or args[1]
		or ""

	local src = getImageSource(frame, image)

	local imageOpacity = getOpacity(
		args["透明度"] or args["opacity"],
		1
	)

	local imagePosition = getPosition(
		args["位置"] or args["position"] or "center"
	)

	local baseColor = getColor(
		args["背景色"] or args["颜色"] or args["color"]
	)

	local shadeColor = getColor(
		args["遮罩颜色"] or args["shadeColor"]
	)

	local shadeOpacity = getOpacity(
		args["遮罩透明度"] or args["shade"],
		0
	)

	if src == "" and baseColor == "" then
		return ""
	end

	-- 整个背景位于较低层级,不应截获鼠标事件。
	local rootStyle =
		"position:fixed;" ..
		"top:0;" ..
		"left:0;" ..
		"width:100%;" ..
		"height:100%;" ..
		"z-index:-1;" ..
		"overflow:hidden;" ..
		"pointer-events:none;"

	if baseColor ~= "" then
		rootStyle = rootStyle ..
			"background-color:" .. baseColor .. ";"
	end

	local root = mw.html.create("div")
		:addClass("bl-page-bg-layer")
		:attr("aria-hidden", "true")
		:cssText(rootStyle)

	-- 第一层:背景图片。
	if src ~= "" then
		local img = mw.html.create("img")
			:addClass("bl-page-bg-image")
			:attr("src", src)
			:attr("alt", "")
			:cssText(
				"position:absolute;" ..
				"top:0;" ..
				"left:0;" ..
				"width:100%;" ..
				"height:100%;" ..
				"max-width:none;" ..
				"object-fit:cover;" ..
				"object-position:" .. imagePosition .. ";" ..
				"opacity:" .. imageOpacity .. ";" ..
				"pointer-events:none;"
			)

		-- 关键:插入 HTML 节点,不再使用 wikitext(tostring(img))。
		root:node(img)
	end

	-- 第二层:颜色遮罩,用来降低背景的视觉干扰。
	if shadeColor ~= ""
		and tonumber(shadeOpacity) > 0 then

		local shade = mw.html.create("div")
			:addClass("bl-page-bg-shade")
			:cssText(
				"position:absolute;" ..
				"top:0;" ..
				"left:0;" ..
				"width:100%;" ..
				"height:100%;" ..
				"background-color:" .. shadeColor .. ";" ..
				"opacity:" .. shadeOpacity .. ";" ..
				"pointer-events:none;"
			)

		root:node(shade)
	end

	return tostring(root)
end

return p