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

模块:Background

来自Blockland Wiki

此模块的文档可以在模块: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