Edge Rewrite
// request.cf · coarse context

A page that knows where it met you.

Only coarse request metadata is shown. This demo does not display or persist visitor IP addresses.

Country
US
Cloudflare location
CMH
Connection
HTTP/2
Language
Not provided

Ray ID: a401e9fa5e2ea53e

Jump to content

Module:Goban/accessibility

From Wikipedia, the free encyclopedia
--------------------------------------------------------------------------------
-- Module:Goban/accessibility
--
-- Screen-reader support for [[Module:Goban]]: builds a plain-language
-- description of a Go board position from its tile codes, and wraps text in
-- {{Screen reader-only}} so it is voiced but not displayed.
--
--   describe(rows, cols, cells)  ->  description string
--       cells is a function(r, c) returning the tile code shown at row r,
--       column c (counted from the top left, 1-based).
--   hide(frame, text)            ->  wikitext hiding text visually
--   description(frame, rows, cols, cells, alt)
--                                ->  hidden wikitext describing the board,
--                                    using alt when given, else describe()
--------------------------------------------------------------------------------

local p = {}

-- Template that hides text visually while leaving it for screen readers.
local SR_ONLY_TEMPLATE = 'Screen reader-only'

-- Column letters in the usual Go notation, which skips "I".
local COLUMN_LETTERS = 'ABCDEFGHJKLMNOPQRSTUVWXYZ'

local COLOR_NAME = { b = 'black', w = 'white' }
local MARK_NAME = { S = 'square', T = 'triangle' }

-- Escape wikitext-significant characters in text that came from a cell code.
local function escape(s)
	return (s:gsub("[<>%[%]{}|&'=*#]", function(ch)
		return '&#' .. string.byte(ch) .. ';'
	end))
end

-- Coordinate of a cell in Go notation: column letter (A-T, no I) and row
-- number counted from the bottom of the diagram.
local function coordinate(r, c, rows)
	local letter = COLUMN_LETTERS:sub(c, c)
	if letter == '' then
		letter = 'column ' .. c .. ' '
	end
	return letter .. (rows - r + 1)
end

-- Build the plain-language description of the position.
function p.describe(rows, cols, cells)
	local black, white, moves, marks = {}, {}, {}, {}
	for r = 1, rows do
		for c = 1, cols do
			local code = cells(r, c)
			local at = coordinate(r, c, rows)
			local color, suffix = code:match('^([bw])([ST]?)$')
			local mcolor, mnumber = code:match('^([bw])(%d)$')
			if color then
				local list = (color == 'b') and black or white
				list[#list + 1] = at
				if suffix ~= '' then
					marks[#marks + 1] = MARK_NAME[suffix] .. ' on '
						.. COLOR_NAME[color] .. ' stone at ' .. at
				end
			elseif mcolor then
				moves[#moves + 1] = {
					n = tonumber(mnumber), color = COLOR_NAME[mcolor], at = at,
				}
			elseif code:match('^%d%d%d?$') then
				local n = tonumber(code)
				moves[#moves + 1] = {
					n = n, color = (n % 2 == 0) and 'white' or 'black', at = at,
				}
			elseif code == 'X' then
				marks[#marks + 1] = 'X at ' .. at
			elseif code:match('^%u$') then
				marks[#marks + 1] = 'letter ' .. code .. ' at ' .. at
			elseif code:match('^[udlr%-]*c$') then
				marks[#marks + 1] = 'circle at ' .. at
			elseif code == '' or code == '-' or code == 'x'
				or code:match('^[udlr]+$') then
				-- empty board: nothing to say
			else
				marks[#marks + 1] = escape(code) .. ' tile at ' .. at
			end
		end
	end
	table.sort(moves, function(a, b)
		if a.n ~= b.n then
			return a.n < b.n
		end
		return a.color < b.color
	end)

	local parts = {}
	if #black > 0 then
		parts[#parts + 1] = 'Black stones: ' .. table.concat(black, ', ') .. '.'
	end
	if #white > 0 then
		parts[#parts + 1] = 'White stones: ' .. table.concat(white, ', ') .. '.'
	end
	if #moves > 0 then
		local list = {}
		for i, m in ipairs(moves) do
			list[i] = m.n .. ' ' .. m.color .. ' ' .. m.at
		end
		parts[#parts + 1] = 'Moves: ' .. table.concat(list, '; ') .. '.'
	end
	if #marks > 0 then
		parts[#parts + 1] = 'Marks: ' .. table.concat(marks, '; ') .. '.'
	end
	local text = 'Go board, ' .. rows .. ' by ' .. cols
	if #parts == 0 then
		return text .. ', empty.'
	end
	return text .. '. ' .. table.concat(parts, ' ')
end

-- Wikitext that hides text visually but leaves it for screen readers.
-- Without a frame (unit tests) a bare span with the same class is returned.
function p.hide(frame, text)
	if frame and frame.expandTemplate then
		return frame:expandTemplate{ title = SR_ONLY_TEMPLATE, args = { text } }
	end
	return '<span class="sr-only">' .. text .. '</span>'
end

-- Hidden description of the board.  A non-blank alt replaces the generated
-- description.
function p.description(frame, rows, cols, cells, alt)
	local text = alt
	if text == nil or text:match('^%s*$') then
		text = p.describe(rows, cols, cells)
	else
		text = text:match('^%s*(.-)%s*$')
	end
	return p.hide(frame, text)
end

return p