Module:Unicode chart: Difference between revisions
Appearance
Content deleted Content added
Cobaltcigs (talk | contribs) read age module |
Cobaltcigs (talk | contribs) heavily refactored |
||
Line 19: | Line 19: | ||
infoMode = false, |
infoMode = false, |
||
} |
} |
||
local refGrammar = { |
|||
order = { "white", "combining", "control", "format", "reserved", "nonchar" }, |
|||
white = { |
|||
format = 'White area%s within light green cell%s show%s %s of %sotherwise invisible [[whitespace character]]%s.', |
|||
singular = { '', '', 's', 'the size', 'an ', '' }, |
|||
plural = { 's', 's', '', 'sizes', '', 's' }, |
|||
count = 0, |
|||
}, |
|||
combining = { |
|||
format = 'Yellow cell%s with [[dotted circle]]%s (◌) indicate%s %s[[combining character]]%s.', |
|||
singular = { '', '', 's', 'a ', '' }, |
|||
plural = { 's', 's', '', '','s' }, |
|||
count = 0, |
|||
}, |
|||
control = { |
|||
format = 'Light blue cell%s indicate%s %snon-printable [[control character]]%s.', |
|||
singular = { '', 's', 'a ', '' }, |
|||
plural = { 's', '', '', 's' }, |
|||
count = 0, |
|||
}, |
|||
format = { |
|||
format = 'Pink cell%s indicate%s %s[[format character]]%s.', |
|||
singular = { '', 's', 'a ', '' }, |
|||
plural = { 's', '', '', 's' }, |
|||
count = 0, |
|||
}, |
|||
reserved = { |
|||
format = 'Gray cell%s indicate%s %sunassigned (reserved) code point%s.', |
|||
singular = { '', 's', 'an ', '' }, |
|||
plural = { 's', '', '', 's' }, |
|||
count = 0, |
|||
}, |
|||
nonchar = { |
|||
format = 'Black cell%s indicate%s %s[[noncharacter]]%s (code point%s that %s guaranteed never to be assigned as %sencoded character%s in the Unicode Standard).', |
|||
singular = { '','s','a ', '', '', 'is','an ', '' }, |
|||
plural = { 's','', '','s','s','are', '','s' }, |
|||
count = 0, |
|||
}, |
|||
} |
|||
local infoTable = {} |
local infoTable = {} |
||
local err = { |
local err = { |
||
format = function(...) return error(string.format(...), 0) end, |
|||
blockName = 'Unrecognized block name "%s" does not match those defined in [[Module:Unicode data/blocks]].', |
|||
refGarbage = 'Refs contain non-ref content: "%s"', |
|||
} |
|||
function debug(...) |
function debug(...) |
||
Line 177: | Line 219: | ||
else return string.format('<ref name="version">As of Unicode version %s.</ref>', mw.text.nowiki(mVersion)) end |
else return string.format('<ref name="version">As of Unicode version %s.</ref>', mw.text.nowiki(mVersion)) end |
||
end |
end |
||
function makeAutoRefs(count) |
|||
function makeAutoRefs() |
|||
if not config.showRefs then return '' end |
if not config.showRefs then return '' end |
||
local |
local refs = {} |
||
for i,refType in ipairs(refGrammar.order) do |
|||
white = '<ref name="white">White area%s within light green cell%s show%s size of otherwise invisible whitespace character%s.</ref>', |
|||
local g = refGrammar[refType] |
|||
control = '<ref name="control">Light blue cell%s indicate%s non-printable control character%s.</ref>', |
|||
local refText = nil |
|||
format = '<ref name="format">Pink cell%s indicate%s non-printable format character%s.</ref>', |
|||
if(g.count == 1) then refText = string.format(g.format, unpack(g.singular)) end |
|||
reserved = '<ref name="reserved">Gray cell%s indicate%s unassigned (reserved) code point%s.</ref>', |
|||
if(g.count >= 2) then refText = string.format(g.format, unpack(g.plural)) end |
|||
nonchar = '<ref name="nonchar">Black cell%s indicate%s noncharacter%s (code point%s that %s guaranteed never to be assigned as %sencoded character%s in the Unicode Standard).</ref>', |
|||
if(refText) then |
|||
combining = '<ref name="combining"></ref>', |
|||
table.insert(refs, string.format('<ref name="%s">%s</ref>', refType, refText)) |
|||
} |
|||
end |
|||
local refs = '' |
|||
end |
|||
if count.white == 1 then refs = refs..string.format(fmt.white, '', '','s', '' ) end |
|||
return table.concat(refs) |
|||
if count.white >= 2 then refs = refs..string.format(fmt.white, 's','s', '', 's') end |
|||
if count.control == 1 then refs = refs..string.format(fmt.control, '', 's', '' ) end |
|||
if count.control >= 2 then refs = refs..string.format(fmt.control, 's', '', 's') end |
|||
if count.format == 1 then refs = refs..string.format(fmt.format, '', 's', '' ) end |
|||
if count.format >= 2 then refs = refs..string.format(fmt.format, 's', '', 's') end |
|||
if count.reserved == 1 then refs = refs..string.format(fmt.reserved, '', 's', '' ) end |
|||
if count.reserved >= 2 then refs = refs..string.format(fmt.reserved, 's', '', 's') end |
|||
if count.nonchar == 1 then refs = refs..string.format(fmt.nonchar, '','s', '', '', 'is','an ', '' ) end |
|||
if count.nonchar >= 2 then refs = refs..string.format(fmt.nonchar,'s','', 's','s','are', '','s' ) end |
|||
return refs |
|||
end |
end |
||
--TODO: remove any garbage around/between refs and downgrade this to a warning |
|||
function sanitizeUserRefs(refTxt) |
function sanitizeUserRefs(refTxt) |
||
if not config.showRefs then return '' end |
if not config.showRefs then return '' end |
||
Line 235: | Line 272: | ||
function makeInfoRow(info) |
function makeInfoRow(info) |
||
local alii = makeAliasList(info.n) |
local alii = makeAliasList(info.n) |
||
local html = '<div class="html"><ul><li>'..table.concat(getEntities(info.n), '</li><li>')..'</li></ul></div>' |
local html = '<div class="html"><ul><li>'..table.concat(getEntities(info.n), '</li><li>')..'</li></ul></div>' |
||
Line 241: | Line 277: | ||
local utf16 = '<div class="utf16"><ul><li>'..table.concat(getUtf16toStr(info.n), '</li><li>')..'</li></ul></div>' |
local utf16 = '<div class="utf16"><ul><li>'..table.concat(getUtf16toStr(info.n), '</li><li>')..'</li></ul></div>' |
||
local age = getAge(info.n) |
local age = getAge(info.n) |
||
if(age) then age = string.format('<div class="age">Introduced in Unicode %s.</div>', age) else age = '' end |
if(age) then age = string.format('<div class="age">Introduced in Unicode version %s.</div>', age) else age = '' end |
||
local cat = info.category |
local cat = info.category |
||
if(cat == 'control') then info.name = mw.text.nowiki('<control>') end |
if(cat == 'control') then info.name = mw.text.nowiki('<control>') end |
||
local doBox = cat=='control' or cat=='format' or cat=='space separator' or |
local doBox = cat=='control' or cat=='format' or cat=='space separator' or |
||
mw.ustring.match(info.display, '<span%s+class="small') |
mw.ustring.match(info.display, '<span%s+class="small') |
||
Line 267: | Line 301: | ||
return nil |
return nil |
||
end |
end |
||
function makeGridCell(n, range) |
|||
local uPlus = string.format("U+%04X", n) |
|||
local char = mw.ustring.char(n) |
|||
local cfFmt = '<td title="%s" class="char%s%s"><div>\n%s\n</div></td>' |
|||
local isControlN, isFormatN = isControl(n), isFormat(n) |
|||
local aliases = getControlAliases(n) |
|||
local charName = table.last(aliases) or mUnicode.lookup_name(n) |
|||
if isControlN then charName = charName or "<control>" end |
|||
local box = getParamNx("box", n, true) |
|||
local cBox = ' box' |
|||
if box == "no" then cBox = '' end |
|||
local title = uPlus..' '..charName |
|||
local display = mDisplay[n] or getParamNx("display", n, false) or table.last(getControlAbbrs(n)) or char |
|||
if isControlN or isFormatN then display = makeSpan(display, title, true) end |
|||
local sCode = nil |
|||
if config.useFontCss then sCode = mUnicode.lookup_script(n) end |
|||
--default dir="ltr" need not be specified |
|||
local sDir = '' |
|||
if mUnicode.is_rtl(char) then sDir = ' dir="rtl"' end |
|||
local sClass = "" |
|||
local linkThis = getTarget(n) |
|||
local cell = '' |
|||
local generateInfoPanel = true |
|||
--3 types of empty cells |
|||
if(n < range.first or n > range.last) then |
|||
--fill extra spaces surrounding an irregular (non-multiple of 16) range of displayed chars |
|||
cell = '<td class="excluded"></td>' |
|||
generateInfoPanel = false |
|||
elseif string.match(charName, '<reserved') then |
|||
refGrammar.reserved.count = refGrammar.reserved.count + 1 |
|||
cell = string.format('<td title="%s RESERVED" class="reserved"></td>', uPlus) |
|||
generateInfoPanel = false |
|||
elseif string.match(charName, '<noncharacter') then |
|||
refGrammar.nonchar.count = refGrammar.nonchar.count + 1 |
|||
cell = string.format('<td title="%s NONCHARACTER" class="nonchar"></td>', uPlus) |
|||
generateInfoPanel = false |
|||
--actual chars |
|||
elseif mUnicode.is_whitespace(n) then |
|||
refGrammar.white.count = refGrammar.white.count + 1 |
|||
local cellFmt = '<td title="%s" class="char whitespace"%s><div>\n%s\n</div></td>' |
|||
display = makeSpan(display, title, false) |
|||
cell = string.format(cellFmt, title, sDir, makeLink(linkThis, makeSpan(char, title, false))) |
|||
elseif isControlN then |
|||
refGrammar.control.count = refGrammar.control.count + 1 |
|||
cell = string.format(cfFmt, title, " control", cBox, makeLink(linkThis, display)) |
|||
elseif isFormatN then |
|||
refGrammar.format.count = refGrammar.format.count + 1 |
|||
cell = string.format(cfFmt, title, " format", cBox, makeLink(linkThis, display)) |
|||
elseif string.match(charName, "VARIATION SELECTOR") then |
|||
if string.match(charName, "MONGOLIAN") then |
|||
display = table.last(getControlAbbrs(n)) |
|||
else |
|||
display = mw.ustring.gsub(charName, "VARIATION SELECTOR%-", "VS ") |
|||
end |
|||
local cellFmt = '<td title="%s" class="char box vs"><div>\n%s\n</div></td>' |
|||
display = makeSpan(display, title, true) |
|||
cell = string.format(cellFmt, title, makeLink(linkThis, display)) |
|||
else |
|||
if sCode then sClass = sClass..string.format(' script-%s', sCode) end |
|||
if box == "yes" then sClass = sClass..' box' end |
|||
isCombining = mUnicode.is_combining(n) |
|||
if isCombining then |
|||
count.combining = count.combining + 1 |
|||
sClass = sClass.." combining" |
|||
display = "◌"..char |
|||
end |
|||
display = makeSpan(display, title, true) |
|||
local cellFmt = '<td title="%s" class="char%s"%s><div>\n%s\n</div></td>' |
|||
cell = string.format(cellFmt, title, sClass, sDir, makeLink(linkThis,display)) |
|||
end |
|||
if(config.infoMode and generateInfoPanel) then |
|||
local printable = mUnicode.is_printable(n) |
|||
local category = getCategory(n) |
|||
local info = { |
|||
n = n, |
|||
char = char, |
|||
name = charName, |
|||
sCode = sCode, |
|||
display = display, |
|||
uPlus = uPlus, |
|||
printable = printable, |
|||
category = category, |
|||
aliases = aliases |
|||
} |
|||
table.insert(infoTable, makeInfoRow(info)) |
|||
end |
|||
return cell |
|||
end |
|||
function p.main( frame ) |
function p.main( frame ) |
||
Line 281: | Line 404: | ||
config.showRefs = not(userRefs=='off' or userRefs=='no') |
config.showRefs = not(userRefs=='off' or userRefs=='no') |
||
local pdf = args["pdf"] or string.format('https://www.unicode.org/charts/PDF/U%04X.pdf', defaultRange.first) |
local pdf = args["pdf"] or string.format('https://www.unicode.org/charts/PDF/U%04X.pdf', defaultRange.first) |
||
local cfFmt = '<td title="%s" class="char%s%s"><div>\n%s\n</div></td>' |
|||
local ranges = parseRanges(args["ranges"] or args["range"] or '') |
local ranges = parseRanges(args["ranges"] or args["range"] or '') |
||
if #ranges == 0 then ranges = { defaultRange } end |
if #ranges == 0 then ranges = { defaultRange } end |
||
local tableBody = {} |
local tableBody = {} |
||
local count = { white=0, control=0, format=0, reserved=0, nonchar=0, combining=0 } |
|||
for i,range in ipairs(ranges) do |
for i,range in ipairs(ranges) do |
||
local |
local firstRowStart = range.first - (range.first % 16) |
||
local |
local lastRowStart = range.last - (range.last % 16) |
||
for |
for rowStart = firstRowStart, lastRowStart, 16 do |
||
local dataRow = {} |
local dataRow = {} |
||
local rowOpen, rowClose = '<tr>', '</tr>' |
local rowOpen, rowClose = '<tr>', '</tr>' |
||
local rowHeader = string.format('<th class="row">U+%03Xx</th>', |
local rowHeader = string.format('<th class="row">U+%03Xx</th>', rowStart/16) |
||
for c = 0,15 do table.insert(dataRow, makeGridCell(rowStart+c, range)) end |
|||
for c = 0,15,1 do |
|||
local n = (r+c) |
|||
local uPlus = string.format("U+%04X", n) |
|||
local char = mw.ustring.char(n) |
|||
local isControlN, isFormatN = isControl(n), isFormat(n) |
|||
local aliases = getControlAliases(n) |
|||
local charName = table.last(aliases) or mUnicode.lookup_name(n) |
|||
if isControlN then charName = charName or "<control>" end |
|||
local box = getParamNx("box", n, true) |
|||
local cBox = ' box' |
|||
if box == "no" then cBox = '' end |
|||
local title = uPlus..' '..charName |
|||
local display = mDisplay[n] or getParamNx("display", n, false) or table.last(getControlAbbrs(n)) or char |
|||
if isControlN or isFormatN then |
|||
display = makeSpan(display, title, true) |
|||
end |
|||
local sCode = nil |
|||
if config.useFontCss then sCode = mUnicode.lookup_script(n) end |
|||
local sDir = '' |
|||
if mUnicode.is_rtl(char) then sDir = ' dir="rtl"' end |
|||
local sClass = "" |
|||
local linkThis = getTarget(n) |
|||
local cell = '' |
|||
local doInfo = true |
|||
if(n < first or n > last) then |
|||
cell = '<td class="excluded"></td>' |
|||
doInfo = false |
|||
elseif string.match(charName, '<reserved') then |
|||
count.reserved = count.reserved + 1 |
|||
cell = string.format('<td title="%s RESERVED" class="reserved"></td>', uPlus) |
|||
doInfo = false |
|||
elseif string.match(charName, '<noncharacter') then |
|||
count.nonchar = count.nonchar + 1 |
|||
cell = string.format('<td title="%s NONCHARACTER" class="nonchar"></td>', uPlus) |
|||
doInfo = false |
|||
elseif isControlN then |
|||
count.control = count.control + 1 |
|||
cell = string.format(cfFmt, title, " control", cBox, makeLink(linkThis, display)) |
|||
elseif isFormatN then |
|||
count.format = count.format + 1 |
|||
cell = string.format(cfFmt, title, " format", cBox, makeLink(linkThis, display)) |
|||
elseif string.match(charName, "VARIATION SELECTOR") then |
|||
if string.match(charName, "MONGOLIAN") then |
|||
display = table.last(getControlAbbrs(n)) |
|||
else |
|||
display = mw.ustring.gsub(charName, "VARIATION SELECTOR%-", "VS ") |
|||
end |
|||
local cellFmt = '<td title="%s" class="char box vs"><div>\n%s\n</div></td>' |
|||
display = makeSpan(display, title, true) |
|||
cell = string.format(cellFmt, title, makeLink(linkThis, display)) |
|||
elseif mUnicode.is_whitespace(n) then |
|||
count.white = count.white + 1 |
|||
local cellFmt = '<td title="%s" class="char whitespace"%s><div>\n%s\n</div></td>' |
|||
display = makeSpan(display, title, false) |
|||
cell = string.format(cellFmt, title, sDir, makeLink(linkThis, makeSpan(char, title, false))) |
|||
else |
|||
if sCode then sClass = sClass..string.format(' script-%s', sCode) end |
|||
if box == "yes" then sClass = sClass..' box' end |
|||
isCombining = mUnicode.is_combining(n) |
|||
if isCombining then |
|||
count.combining = count.combining + 1 |
|||
sClass = sClass.." combining" |
|||
display = "◌"..char |
|||
end |
|||
display = makeSpan(display, title, true) |
|||
local cellFmt = '<td title="%s" class="char%s"%s><div>\n%s\n</div></td>' |
|||
cell = string.format(cellFmt, title, sClass, sDir, makeLink(linkThis,display)) |
|||
end |
|||
if(config.infoMode and doInfo) then |
|||
local printable = mUnicode.is_printable(n) |
|||
local category = getCategory(n) |
|||
local info = { |
|||
n = n, |
|||
char = char, |
|||
name = charName, |
|||
sCode = sCode, |
|||
display = display, |
|||
uPlus = uPlus, |
|||
printable = printable, |
|||
category = category, |
|||
aliases = aliases |
|||
} |
|||
table.insert(infoTable, makeInfoRow(info)) |
|||
end |
|||
table.insert(dataRow, cell) |
|||
end |
|||
local rowHtml = {rowOpen, rowHeader, table.concat(dataRow), rowClose} |
local rowHtml = {rowOpen, rowHeader, table.concat(dataRow), rowClose} |
||
table.insert(tableBody, table.concat(rowHtml)) |
table.insert(tableBody, table.concat(rowHtml)) |
||
Line 390: | Line 425: | ||
local tableOpen, tableClose = string.format(tableOpenFmt, state), '</table>' |
local tableOpen, tableClose = string.format(tableOpenFmt, state), '</table>' |
||
local |
local allRefs = table.concat({ makeVersionRef(), makeAutoRefs(), sanitizeUserRefs(userRefs) }) |
||
local autoRefs = makeAutoRefs(count) |
|||
userRefs = sanitizeUserRefs(userRefs) |
|||
local refs = table.concat({ versionRef, autoRefs, userRefs }) |
|||
local titleBar = string.format('<div class="title">[[%s|%s]]%s</div>', blockNameLink, blockNameDisplay, |
local titleBar = string.format('<div class="title">[[%s|%s]]%s</div>', blockNameLink, blockNameDisplay, allRefs) |
||
local fmtpdf = '<div class="pdf-link">[%s Official Unicode Consortium code chart] (PDF)</div>' |
local fmtpdf = '<div class="pdf-link">[%s Official Unicode Consortium code chart] (PDF)</div>' |
||
if pdf then titleBar = titleBar..string.format(fmtpdf, pdf) end |
if pdf then titleBar = titleBar..string.format(fmtpdf, pdf) end |
||
Line 408: | Line 440: | ||
local notesFooter = '' |
local notesFooter = '' |
||
if config.showRefs and string.len( |
if config.showRefs and string.len(allRefs) > 0 then |
||
notesFooter = '<tr><td class="notes" colspan="17">'.."'''Notes:'''{{reflist}}"..'</td></tr>' |
notesFooter = '<tr><td class="notes" colspan="17">'.."'''Notes:'''{{reflist}}"..'</td></tr>' |
||
end |
end |
Revision as of 00:09, 19 September 2019
![]() | This module is rated as pre-alpha. It is unfinished, and may or may not be in active development. It should not be used from article namespace pages. Modules remain pre-alpha until the original editor (or someone who takes one over if it is abandoned for some time) is satisfied with the basic structure. |
![]() | This module uses TemplateStyles: |
Implements {{unicode chart}}
local mArguments = require('Module:Arguments')
local mTableTools = require('Module:TableTools')
local mUnicode = require('Module:Unicode data')
local mAge = require('Module:Unicode data/age')
local mAliases = require('Module:Unicode data/aliases')
local mBlocks = require('Module:Unicode data/blocks')
local mCategory = require('Module:Unicode data/category')
local mControl = require('Module:Unicode data/control')
local mScripts = require('Module:Unicode data/scripts')
local mVersion = require('Module:Unicode data/version')
local mEntities = require('Module:Unicode chart/entities')
local mDisplay = require('Module:Unicode chart/display')
local p = {}
local args = {}
local config = {
useFontCss = true,
showRefs = true,
infoMode = false,
}
local refGrammar = {
order = { "white", "combining", "control", "format", "reserved", "nonchar" },
white = {
format = 'White area%s within light green cell%s show%s %s of %sotherwise invisible [[whitespace character]]%s.',
singular = { '', '', 's', 'the size', 'an ', '' },
plural = { 's', 's', '', 'sizes', '', 's' },
count = 0,
},
combining = {
format = 'Yellow cell%s with [[dotted circle]]%s (◌) indicate%s %s[[combining character]]%s.',
singular = { '', '', 's', 'a ', '' },
plural = { 's', 's', '', '','s' },
count = 0,
},
control = {
format = 'Light blue cell%s indicate%s %snon-printable [[control character]]%s.',
singular = { '', 's', 'a ', '' },
plural = { 's', '', '', 's' },
count = 0,
},
format = {
format = 'Pink cell%s indicate%s %s[[format character]]%s.',
singular = { '', 's', 'a ', '' },
plural = { 's', '', '', 's' },
count = 0,
},
reserved = {
format = 'Gray cell%s indicate%s %sunassigned (reserved) code point%s.',
singular = { '', 's', 'an ', '' },
plural = { 's', '', '', 's' },
count = 0,
},
nonchar = {
format = 'Black cell%s indicate%s %s[[noncharacter]]%s (code point%s that %s guaranteed never to be assigned as %sencoded character%s in the Unicode Standard).',
singular = { '','s','a ', '', '', 'is','an ', '' },
plural = { 's','', '','s','s','are', '','s' },
count = 0,
},
}
local infoTable = {}
local err = {
format = function(...) return error(string.format(...), 0) end,
blockName = 'Unrecognized block name "%s" does not match those defined in [[Module:Unicode data/blocks]].',
refGarbage = 'Refs contain non-ref content: "%s"',
}
function debug(...)
local a = {...}
if type(a[1]) ~= "string" then mw.log(a[1]) return end
local _,c = string.gsub(string.gsub(a[1], "%%%%", ""), "%%", "")
for i = 1,math.max(#a, c+1) do
if (type(a[i]) == "nil" or type(a[i]) == "boolean") then a[i] = tostring(a[i]) end
end
return mw.log(string.format(unpack(a)))
end
table.concat2 = function(t1,t2) for i=1,#t2 do t1[#t1+1] = t2[i] end return t1 end
table.last = function(t) if t then return t[#t] else return nil end end
string.formatAll = function(fmt, t)
for i=1,#t do t[i] = string.format(fmt, t[i]) end
return t
end
function getUtf8(n)
local t = {}
for b in mw.ustring.char(n):gmatch('.') do table.insert(t, b:byte()) end
return t
end
function getUtf16(n)
if(n < 0 or n > 0x10FFFF) then return nil end
if(n < 0xD7FF) or (n > 0xDFFF and n < 0x10000) then return { n } end
local u = (n - 0x10000)
local low = u % 0x400
local high = (u - low) / 0x400
return { 0xD800 + high, 0xDC00 + low }
end
function getUtf16toStr(n)
t = getUtf16(n)
for i=1,#t do t[i] = string.format("0x%04X", t[i]) end
return t
end
function getUtf8toStr(n) return string.formatAll("0x%02X", getUtf8(n) ) end
function getUtf16toStr(n) return string.formatAll("0x%04X", getUtf16(n)) end
function makeRange(a,b) return {first=math.min(a,b),last=math.max(a,b)} end
function rangeContains(r, n) return (n >= r.first and n <= r.last) end
function rangeCombine(r1,r2) return {first=math.min(r1.first,r2.first), last=math.max(r1.last,r2.last)} end
function rangesMergeable(r1,r2)
if not r1 or not r2 then return false end
return rangeContains(r1, r2.first-1) or rangeContains(r1, r2.last+1) or
rangeContains(r2, r1.first-1) or rangeContains(r2, r1.last+1)
end
function rangeSort(r1,r2)
if r1 and not r2 then return true end
if not r1 then return false end
if r1.first == r2.first then return r1.last < r2.last end
return r1.first < r2.first
end
function parseHex(s) if s then return tonumber(s,16) else return nil end end
function parseRanges(str)
local r = {}
for a,b in mw.ustring.gmatch(str, "[UuXx0+-]*([a-fA-F0-9]+)[-–][UuXx0+-]*([a-fA-F0-9]+)") do
table.insert(r, makeRange(parseHex(a),parseHex(b)))
end
for i = #r,2,-1 do for j = i-1,1,-1 do if rangesMergeable(r[i], r[j]) then
r[j] = rangeCombine(r[i], r[j]) r[i] = nil
end end end
r2 = {}
for k,v in pairs(r) do table.insert(r2,v) end
table.sort(r2, rangeSort)
return r2
end
-- Official way to match property values that are strings (including block names):
-- Ignore case, whitespace, underscore ('_'), hyphens, and any initial prefix string "is".
-- http://www.unicode.org/reports/tr44/#UAX44-LM3
local function propertyValueKey(val)
return val:lower():gsub('^is', ''):gsub('[-_%s]+', '')
end
function getDefaultRange(blockName)
blockName = propertyValueKey(blockName)
for i,b in ipairs(mBlocks) do
if blockName == propertyValueKey(b[3]) then return makeRange(b[1],b[2]) end
end
end
function getAge(n)
local a = mAge.singles[n]
if(a) then return a end
for k,v in pairs(mAge.ranges) do
if n >= v[1] and n <= v[2] then return v[3] end
end
return nil
end
function getCategory(n)
local cc = mUnicode.lookup_category(n)
local cat = mCategory.long_names[cc]
if cat then return string.gsub(string.lower(cat), "_", " ") else return nil end
end
function getControlAbbrs(n) return getAliasValues(n, "abbreviation") end
function getControlAliases(n) return table.concat2(getAliasValues(n, "control"), getAliasValues(n, "figment")) end
function getAliasValues(n, key)
local b,r = mAliases[n], {}
if b then for i,t in ipairs(b) do
if(not key or t[1] == key) then table.insert(r, t[2]) end
end end
return r
end
function getAnchorId(n) return string.format("info-%04X", n) end
function getTarget(n)
if(config.infoMode) then return "#"..getAnchorId(n) end
local t = getParamNx("link", n, true)
if(t=="yes") then t = char end
--"ifexist" is a deleted feature, now recognized equal to "no" to avoid linking to the article [[Ifexist]], which incidentally doesn't exist.
if(t=="no" or t=="ifexist") then t = nil end
if(t=="wikt") then t = ":wikt:"..mw.ustring.char(n) end
return t
end
function getNamedEntity(n)
local e = mEntities[n]
if e then return string.gsub(e, "&", "&") else return nil end
end
function getEntities(n)
local entH = getNamedEntity(n)
local entN = string.format('&#%d;', n)
local entXN = string.format('&#x%X;', n)
local t = {}
if(entH) then table.insert(t, entH) end
table.insert(t, entN)
table.insert(t, entXN)
return t
end
function isControl(n) return mUnicode.lookup_control(n) == "control" end
function isFormat(n) return mUnicode.lookup_control(n) == "format" end
function isBadTitle(str)
if str == nil then return true end
if type(str) == "number" then str = mw.ustring.char(str) end
if not mUnicode.is_valid_pagename(str) then return true end
if mw.ustring.match(str, "[\<\>]") then return true end
if #str == 1 and mw.ustring.match(str, "[\/\.\:\_̸]") then return true end
return false
end
function makeVersionRef()
if(not config.showRefs or mVersion == nil or mVersion == '') then return ''
else return string.format('<ref name="version">As of Unicode version %s.</ref>', mw.text.nowiki(mVersion)) end
end
function makeAutoRefs()
if not config.showRefs then return '' end
local refs = {}
for i,refType in ipairs(refGrammar.order) do
local g = refGrammar[refType]
local refText = nil
if(g.count == 1) then refText = string.format(g.format, unpack(g.singular)) end
if(g.count >= 2) then refText = string.format(g.format, unpack(g.plural)) end
if(refText) then
table.insert(refs, string.format('<ref name="%s">%s</ref>', refType, refText))
end
end
return table.concat(refs)
end
--TODO: remove any garbage around/between refs and downgrade this to a warning
function sanitizeUserRefs(refTxt)
if not config.showRefs then return '' end
local trim1 = mw.text.killMarkers(refTxt)
local trim2 = mw.ustring.gsub(trim1, '%s', '')
if string.len(trim2) > 0 then err.format(err.refGarbage, mw.text.nowiki(trim1))
else return refTxt end
end
function makeSpan(str, title, repl)
local c,t = '',''
if title then t = string.format(' title="%s"', title) end
if repl then
local s,x = mw.ustring.gsub(str, '%s+', '\n')
if x > 0 then c = string.format(' class="small-%s"', x) str = s end
end
return string.format('<span %s%s>%s</span>', c, t, str)
end
function makeLink(a, b)
if not a or (isBadTitle(a) and not config.infoMode) then return (b or '') end
if not b then b = a end
return string.format("[[%s|%s]]",a,b)
end
function makeAliasList(n)
if not mAliases[n] then return '' end
local t = {}
table.insert(t, '<div class="alias"><ul>')
for k,v in ipairs(mAliases[n]) do
local tr = string.format('<li class="%s">%s</li>', v[1], v[2])
table.insert(t, tr)
end
table.insert(t, '</ul></div>')
return table.concat(t)
end
function makeInfoRow(info)
local alii = makeAliasList(info.n)
local html = '<div class="html"><ul><li>'..table.concat(getEntities(info.n), '</li><li>')..'</li></ul></div>'
local utf8 = '<div class="utf8"><ul><li>'..table.concat(getUtf8toStr(info.n), '</li><li>')..'</li></ul></div>'
local utf16 = '<div class="utf16"><ul><li>'..table.concat(getUtf16toStr(info.n), '</li><li>')..'</li></ul></div>'
local age = getAge(info.n)
if(age) then age = string.format('<div class="age">Introduced in Unicode version %s.</div>', age) else age = '' end
local cat = info.category
if(cat == 'control') then info.name = mw.text.nowiki('<control>') end
local doBox = cat=='control' or cat=='format' or cat=='space separator' or
mw.ustring.match(info.display, '<span%s+class="small')
local class = ''
if config.useFontCss then class = class..'script-'..info.sCode end
if(doBox) then class = class.." box" end
local charInfo = '<div class="char">'..table.concat({utf8, utf16, html, age})..'</div>'
local titleBarFmt = '<div><div class="title">%s %s</div><div class="category">%s</div></div>'
local titleBar = string.format(titleBarFmt, info.uPlus, info.name, info.category)
local fmt = '<tr class="info-row" id="%s"><th class="thumb %s">%s</th><td colspan="16" class="info">%s%s%s</td></tr>'
return string.format(fmt, getAnchorId(info.n), class, info.display, titleBar, alii, charInfo)
end
function getParamNx(key, n, c)
local key4 = string.format("%s_%04X", key, n)
if args[key4] then return args[key4] end
if c then
local key3 = string.format("%s_%03Xx", key, math.floor(n/16))
return args[key3] or args[key]
end
return nil
end
function makeGridCell(n, range)
local uPlus = string.format("U+%04X", n)
local char = mw.ustring.char(n)
local cfFmt = '<td title="%s" class="char%s%s"><div>\n%s\n</div></td>'
local isControlN, isFormatN = isControl(n), isFormat(n)
local aliases = getControlAliases(n)
local charName = table.last(aliases) or mUnicode.lookup_name(n)
if isControlN then charName = charName or "<control>" end
local box = getParamNx("box", n, true)
local cBox = ' box'
if box == "no" then cBox = '' end
local title = uPlus..' '..charName
local display = mDisplay[n] or getParamNx("display", n, false) or table.last(getControlAbbrs(n)) or char
if isControlN or isFormatN then display = makeSpan(display, title, true) end
local sCode = nil
if config.useFontCss then sCode = mUnicode.lookup_script(n) end
--default dir="ltr" need not be specified
local sDir = ''
if mUnicode.is_rtl(char) then sDir = ' dir="rtl"' end
local sClass = ""
local linkThis = getTarget(n)
local cell = ''
local generateInfoPanel = true
--3 types of empty cells
if(n < range.first or n > range.last) then
--fill extra spaces surrounding an irregular (non-multiple of 16) range of displayed chars
cell = '<td class="excluded"></td>'
generateInfoPanel = false
elseif string.match(charName, '<reserved') then
refGrammar.reserved.count = refGrammar.reserved.count + 1
cell = string.format('<td title="%s RESERVED" class="reserved"></td>', uPlus)
generateInfoPanel = false
elseif string.match(charName, '<noncharacter') then
refGrammar.nonchar.count = refGrammar.nonchar.count + 1
cell = string.format('<td title="%s NONCHARACTER" class="nonchar"></td>', uPlus)
generateInfoPanel = false
--actual chars
elseif mUnicode.is_whitespace(n) then
refGrammar.white.count = refGrammar.white.count + 1
local cellFmt = '<td title="%s" class="char whitespace"%s><div>\n%s\n</div></td>'
display = makeSpan(display, title, false)
cell = string.format(cellFmt, title, sDir, makeLink(linkThis, makeSpan(char, title, false)))
elseif isControlN then
refGrammar.control.count = refGrammar.control.count + 1
cell = string.format(cfFmt, title, " control", cBox, makeLink(linkThis, display))
elseif isFormatN then
refGrammar.format.count = refGrammar.format.count + 1
cell = string.format(cfFmt, title, " format", cBox, makeLink(linkThis, display))
elseif string.match(charName, "VARIATION SELECTOR") then
if string.match(charName, "MONGOLIAN") then
display = table.last(getControlAbbrs(n))
else
display = mw.ustring.gsub(charName, "VARIATION SELECTOR%-", "VS ")
end
local cellFmt = '<td title="%s" class="char box vs"><div>\n%s\n</div></td>'
display = makeSpan(display, title, true)
cell = string.format(cellFmt, title, makeLink(linkThis, display))
else
if sCode then sClass = sClass..string.format(' script-%s', sCode) end
if box == "yes" then sClass = sClass..' box' end
isCombining = mUnicode.is_combining(n)
if isCombining then
count.combining = count.combining + 1
sClass = sClass.." combining"
display = "◌"..char
end
display = makeSpan(display, title, true)
local cellFmt = '<td title="%s" class="char%s"%s><div>\n%s\n</div></td>'
cell = string.format(cellFmt, title, sClass, sDir, makeLink(linkThis,display))
end
if(config.infoMode and generateInfoPanel) then
local printable = mUnicode.is_printable(n)
local category = getCategory(n)
local info = {
n = n,
char = char,
name = charName,
sCode = sCode,
display = display,
uPlus = uPlus,
printable = printable,
category = category,
aliases = aliases
}
table.insert(infoTable, makeInfoRow(info))
end
return cell
end
function p.main( frame )
for k, v in pairs(mArguments.getArgs(frame)) do args[k] = v end
config.infoMode = (args["info"] or 'no'):lower() ~= "no"
config.useFontCss = (args["fonts"] or args["font"] or 'yes'):lower() ~= "no"
local blockName = args["block"] or args["name"] or args[1]
local defaultRange = getDefaultRange(blockName)
if defaultRange == nil then err.format(err.blockName, blockName) end
local state = args["state"] or "expanded"
local blockNameLink = args["link_block"] or args["link_name"] or blockName.." (Unicode block)"
local blockNameDisplay = args["display_block"] or args["display_name"] or blockName
local userRefs = args["refs"] or args["notes"] or args["ref"] or args["note"] or ""
config.showRefs = not(userRefs=='off' or userRefs=='no')
local pdf = args["pdf"] or string.format('https://www.unicode.org/charts/PDF/U%04X.pdf', defaultRange.first)
local ranges = parseRanges(args["ranges"] or args["range"] or '')
if #ranges == 0 then ranges = { defaultRange } end
local tableBody = {}
for i,range in ipairs(ranges) do
local firstRowStart = range.first - (range.first % 16)
local lastRowStart = range.last - (range.last % 16)
for rowStart = firstRowStart, lastRowStart, 16 do
local dataRow = {}
local rowOpen, rowClose = '<tr>', '</tr>'
local rowHeader = string.format('<th class="row">U+%03Xx</th>', rowStart/16)
for c = 0,15 do table.insert(dataRow, makeGridCell(rowStart+c, range)) end
local rowHtml = {rowOpen, rowHeader, table.concat(dataRow), rowClose}
table.insert(tableBody, table.concat(rowHtml))
end
end
local tableOpenFmt = '<table class="wikitable nounderlines unicode-chart collapsible %s">'
local tableOpen, tableClose = string.format(tableOpenFmt, state), '</table>'
local allRefs = table.concat({ makeVersionRef(), makeAutoRefs(), sanitizeUserRefs(userRefs) })
local titleBar = string.format('<div class="title">[[%s|%s]]%s</div>', blockNameLink, blockNameDisplay, allRefs)
local fmtpdf = '<div class="pdf-link">[%s Official Unicode Consortium code chart] (PDF)</div>'
if pdf then titleBar = titleBar..string.format(fmtpdf, pdf) end
local titleBarRow = '<tr><th class="title-bar" colspan="17">'..titleBar..'</th></tr>'
local columnHeaders = { '<tr>', '<th class="empty"></th>' }
for c = 0,15,1 do table.insert(columnHeaders, string.format('<th class="column">%X</th>', c)) end
table.insert(columnHeaders, '</tr>')
local infoFooter = ''
if(config.infoMode) then infoFooter = table.concat(infoTable) end
local notesFooter = ''
if config.showRefs and string.len(allRefs) > 0 then
notesFooter = '<tr><td class="notes" colspan="17">'.."'''Notes:'''{{reflist}}"..'</td></tr>'
end
local tStyles = frame:extensionTag{ name = 'templatestyles', args = { src = 'Unicode chart/styles.css'} }
local cStyles = ''
if config.useFontCss then
cStyles = frame:extensionTag{ name = 'templatestyles', args = { src = 'Unicode chart/script styles.css'} }
end
local html = table.concat({
tStyles, cStyles, tableOpen, titleBarRow,
table.concat(columnHeaders), table.concat(tableBody),
infoFooter, notesFooter, tableClose
})
return frame:preprocess(html)
end
return p