Module:Charmap

local p = {} local unicode_convert = require('Module:Unicode convert') local numcr2namecr = mw.loadData('Module:Numcr2namecr') local yesno = require('Module:Yesno') local GB18030_cache = {}

-- Input e.g. " A  B  FF  ", output "10 11 255" local function hex2dec_words(s) local x = mw.text.split(mw.text.trim(s or ''), '%s+') if #x == 1 and x[1] ==  then return  end for i = 1, #x do		x[i] = tonumber(x[i], 16) or 0 end return table.concat(x, ' ') end

-- Wrapper to let us replace Template:UTF-8 and Template:UTF-16 local function template(title, args) if title == 'UTF-8' then return unicode_convert.getUTF8{ args = args } elseif title == 'UTF-16' then return unicode_convert.getUTF16{ args = args } elseif title == 'hex2dec' then return tostring(tonumber(args[1], 16)) elseif title == 'charmap/numcharref' then local format = args['base'] == 'dec' and '&amp;#%d; ' or '&amp;#x%02X; ' return format:format(tonumber(args[1], 16)) elseif title == 'charmap/showchar' then return '&#x' .. mw.text.trim(args[1]) .. ';'	elseif title == 'GB18030' then if GB18030_cache[args[1]] then if args['base'] == 'dec' then return mw.getCurrentFrame:expandTemplate{ title = 'GB18030/decimal', args = { GB18030_cache[args[1]] }} else return GB18030_cache[args[1]] end elseif os.clock > 7 then return "currently unavailable" else GB18030_cache[args[1]] = mw.getCurrentFrame:expandTemplate{title = 'GB18030', args = {args[1]}} if args['base'] == 'dec' then return hex2dec_words(GB18030_cache[args[1]]) else return GB18030_cache[args[1]] end end else return mw.getCurrentFrame:expandTemplate{ title = title, args = args } end end

local function _multiUTF(args) local code = args[1] or '' local encoding = args[2] or 'UTF-8' local output = '' local pstp_sep = encoding:sub(1, 8) == 'charmap/' and '' or ' ' local words = mw.text.split(mw.text.trim(code), '%s') if not (encoding == 'charmap/showchar' or encoding == 'hex2dec') then output = '|| ' for i = 1, #words do output = output .. template(encoding, {words[i], base = 'dec'}) if i == 4 then break end output = output .. pstp_sep end output = output .. '|| '	end for i = 1, #words do output = output .. template(encoding, {words[i]}) if i == 4 then break end output = output .. pstp_sep end return output end

local function paramCoalesce(args, arg1, arg2) if args[arg1] ~= nil and args[arg1] ~= '' then return args[arg1] end if arg2 ~= nil and args[arg2] ~= nil and args[arg2] ~= '' then return args[arg2] end return nil end

p.alt = function(frame, n)	n = n or frame.args[1] local args = frame:getParent.args if args['map' .. n] == '' or args['map' .. n] == nil then return ''	end local output = '|-\n| ' .. args['map' .. n]	local codes = {} for i = 1, 10 do if args['name' .. i] and args['name' .. i] ~= '' then local x = args['map' .. n .. 'char' .. i] or '' output = output .. ' || ' .. hex2dec_words(x) .. ' || ' .. frame:callParserFunction('uc', x)		end end return output .. '\n' end

p.head = function(frame) local output = '|- style="text-align:center;"\n' codes = {} -- May contain nils if bad input infos = {} -- boolean array names = {} -- string array args = frame:getParent.args for i = 1, 10 do if args['name' .. i] and args['name' .. i] ~= '' then -- The parser function uc: preserves strip markers. codes[1 + #names] = frame:callParserFunction('uc', mw.text.trim(args[i])) infos[1 + #names] = paramCoalesce(args, 'Info' .. i, 'Info') == 'yes' names[1 + #names] = frame:callParserFunction('uc', args['name' .. i]) end end if #names > 0 then output = output .. "! " .. frame:preprocess('') .. (next(codes) == nil and 'Character' or 'Unicode') .. " name" for i, n in ipairs(names) do			-- Display the character in smallcaps output = output .. '\n| colspan="2" class="smallcaps-cm" | ' .. n		end end

output = output .. '\n|-\n! style="text-align:left;" | Encodings || decimal || hex' .. string.rep('|| dec || hex', #names - 1) .. '\n|-\n| Unicode' for i, n in ipairs(names) do		local code = codes[i] or ''; -- padleft, get to form U+XXXX or U+XXXX+XXXX. code = (#code >= 4) and code:gsub(' ', '+') or ('0000' .. (code or '')):sub(-4) output = output .. ' || ' .. _multiUTF{codes[i] or '0', 'hex2dec'} .. ' || ' ..			(infos[i] and 'U+%s' or 'U+%s'):format(code, code) end output = output .. '\n|-\n| UTF-8' for i, n in ipairs(names) do output = output .. _multiUTF{codes[i]} end local outsideBMP = false -- Do we need to show surrogate pairs? for i, n in ipairs(names) do		if (tonumber(codes[i] or '', 16) or 0) > 0xFFFF then outsideBMP = true break end end if outsideBMP then output = output .. '\n|-\n| UTF-16' for i, n in ipairs(names) do output = output .. _multiUTF{codes[i], 'UTF-16'} end end if yesno(args['IncludeGB']) then output = output .. '\n|-\n| GB 18030' for i, n in ipairs(names) do output = output .. _multiUTF{codes[i], 'GB18030'} end end if #names > 4 then output = output .. '\n|- class="template-charmap-numchr"\n| Numeric character reference' else output = output .. '\n|-\n| Numeric character reference' end for i, n in ipairs(names) do output = output .. _multiUTF{codes[i], 'charmap/numcharref'} end return output end

p.named = function(frame, n)	n = n or frame.args[1] local args = frame:getParent.args local refchars = {} local empty = true; local namedref = (n == 'html') and 'Named character reference' or args['namedref' .. n] or ''; for i = 1, 10 do if args['name' .. i] and args['name' .. i] ~= '' then local x = (n == 'html') and (numcr2namecr[tonumber(args[i], 16)] or '') or args['ref' .. n .. 'char' .. i] or '' empty = empty and #x == 0 refchars[1 + #refchars] = x		end end if empty then return '' end return '|- style="text-align:center"\n| ' .. namedref .. (' || colspan="2" | %s'):rep(#refchars):format(unpack(refchars)) .. '\n' end

-- Draw a preview block, possibly with multiple styles function preview(args, i)	local result = '' for j = 1, math.huge do local label = paramCoalesce(args, 'style' .. i .. '-' .. j .. 'label', 'style' .. j .. 'label') if not label then break end local div = mw.html.create('div'):tag('div') div:tag('span'):wikitext(label) div:tag('span') :cssText(args['style' .. j]):cssText(args['style' .. i .. '-' .. j]) :wikitext(paramCoalesce(args, 'image' .. i .. '-' .. j, 'image' .. i) or				_multiUTF{args[i] or '20', 'charmap/showchar'}) result = result .. tostring(div) end -- Setting the font-size on the cell changes its padding. local size = (result == '') and (paramCoalesce(args, 'size' .. i, 'size') or '150%') or '100%' local cell = ' || colspan="2" style="font-size:' .. size .. '" | ' if result == '' then return cell .. (paramCoalesce(args, 'image' .. i) or _multiUTF{args[i] or '20', 'charmap/showchar'}) else return cell .. ' ' .. result .. ' '	end end

p.main = function(frame) -- text-align:center: '' local output = {'{| class="wikitable template-charmap" style="text-align:right"\n', '|+Character information\n', '|- style="text-align:center"\n', -- Otherwise mobile site tries aligning preview cells right '! scope="row" | Preview'} local args = frame:getParent.args for i = 1, 10 do		-- Header row with the symbol in a large font or an image if args['name' .. i] and args['name' .. i] ~= '' then output[1 + #output] = preview(args, i)		end end output[1 + #output] = '\n' .. p.head(frame) .. '\n' .. p.named(frame, 'html') for i = 1, 10 do		output[1 + #output] = p.alt(frame, i)	end for i = 1, 5 do		output[1 + #output] = p.named(frame, i)	end return table.concat(output) .. '|}' end

return p