| Latest revision |
Your text |
| Line 1: |
Line 1: |
| local p = {} | | local p = {} |
|
| |
| local CleanTT = require('Module:tt').CleanTT | | local CleanTT = require('Module:tt').CleanTT |
| local lib = require('Module:Feature') | | local lib = require('Module:Feature') |
| local ne = lib.isNotEmpty | | local ne = lib.isNotEmpty |
|
| |
| local block = false | | local block = false |
| | | --check for if the entry is the first on the level |
| -- Check if the entry is the first on the level | |
| local first = {true, true, true} | | local first = {true, true, true} |
| | | --check for if the entry is right after the end of a deeper level |
| -- Check if the entry is right after the end of a deeper level | |
| local previous = {false, false, false} | | local previous = {false, false, false} |
|
| |
|
| | | --escape characters |
| -- Escape characters for Lua strings | |
| local function clean(txt) | | local function clean(txt) |
| | txt = txt:gsub('&(%a+;)','&%1'):gsub('\'','\\''):gsub('<', '<'):gsub('\'', '\\\'') |
| return txt | | return txt |
| :gsub('\\', '\\\\')
| |
| :gsub('\'', '\\\'')
| |
| end | | end |
|
| |
|
| |
| -- Parse DPL data
| |
| local function parseInfo(info, frame)
| |
| if not info then
| |
| return {}
| |
| end
| |
|
| |
| if ne(frame.args.cleanTT) then
| |
| info = CleanTT(info)
| |
| end
| |
|
| |
| return lib.parseTemplateFormat(
| |
| info,
| |
| ';;',
| |
| '$$$',
| |
| '=='
| |
| )
| |
| end
| |
|
| |
|
| |
| -- Get grouped JSON data
| |
| local function getJsonGroups(frame)
| |
| local groups = {}
| |
|
| |
| local i = 1
| |
|
| |
| while true do
| |
| local groupName = frame.args['group' .. i]
| |
| local groupInfo = frame.args['group' .. i .. '_info']
| |
|
| |
| -- Stop when neither argument exists.
| |
| if groupName == nil and groupInfo == nil then
| |
| break
| |
| end
| |
|
| |
| if ne(groupName) and groupInfo ~= nil then
| |
|
| |
| local info = parseInfo(groupInfo, frame)
| |
| local entries = {}
| |
|
| |
| -- Collect entries
| |
| for _, entry in lib.spairs(
| |
| info,
| |
| function(tVal, a, b)
| |
| local aPage = tostring(tVal[a].page or a)
| |
| local bPage = tostring(tVal[b].page or b)
| |
|
| |
| return aPage < bPage
| |
| end
| |
| ) do
| |
|
| |
| if ne(entry.page) then
| |
| entries[#entries + 1] = entry.page
| |
| end
| |
|
| |
| end
| |
|
| |
| -- Only add the group if it actually contains entries.
| |
| if #entries > 0 then
| |
| groups[groupName] = entries
| |
| end
| |
| end
| |
|
| |
| i = i + 1
| |
| end
| |
|
| |
| return groups
| |
| end
| |
|
| |
|
| |
| -- Format grouped data as readable JSON
| |
| function p.FormatJSONGroups(groups)
| |
| local result = {}
| |
|
| |
| table.insert(result, '{')
| |
|
| |
| -- Get group names
| |
| local groupNames = {}
| |
|
| |
| for name in pairs(groups) do
| |
| groupNames[#groupNames + 1] = name
| |
| end
| |
|
| |
| table.sort(groupNames)
| |
|
| |
| -- Build each group
| |
| for i, name in ipairs(groupNames) do
| |
|
| |
| local entries = groups[name]
| |
| local groupComma = (i < #groupNames) and ',' or ''
| |
|
| |
| table.insert(
| |
| result,
| |
| '\t' ..
| |
| mw.text.jsonEncode(name) ..
| |
| ': ['
| |
| )
| |
|
| |
| for j, entry in ipairs(entries) do
| |
|
| |
| local entryComma = (j < #entries) and ',' or ''
| |
|
| |
| table.insert(
| |
| result,
| |
| '\t\t' ..
| |
| mw.text.jsonEncode(entry) ..
| |
| entryComma
| |
| )
| |
|
| |
| end
| |
|
| |
| table.insert(
| |
| result,
| |
| '\t]' .. groupComma
| |
| )
| |
| end
| |
|
| |
| table.insert(result, '}')
| |
|
| |
| return table.concat(result, '\n')
| |
| end
| |
|
| |
|
| |
|
| function p.main(frame) | | function p.main(frame) |
| local info = {} | | local info = {} |
| local dataKeys = {} | | local dataKeys = {} |
| local rawKeys = {}
| |
|
| |
| -- ============================================================
| |
| -- JSON MODE
| |
| -- ============================================================
| |
|
| |
| if tostring(frame.args.json) == '1' then
| |
|
| |
| -- --------------------------------------------------------
| |
| -- Grouped JSON mode
| |
| -- --------------------------------------------------------
| |
|
| |
| local groups = getJsonGroups(frame)
| |
|
| |
| -- If groups were supplied, output the complete JSON object.
| |
| if next(groups) ~= nil then
| |
|
| |
| local jsonText = p.FormatJSONGroups(groups)
| |
|
| |
| return frame:extensionTag(
| |
| 'syntaxhighlight',
| |
| jsonText,
| |
| {
| |
| lang = 'json',
| |
| copy = ''
| |
| }
| |
| )
| |
| end
| |
|
| |
|
| |
| -- --------------------------------------------------------
| |
| -- Simple JSON array mode
| |
| -- --------------------------------------------------------
| |
|
| |
| if frame.args.info then
| |
|
| |
| info = parseInfo(frame.args.info, frame)
| |
|
| |
| local jsonEntries = {}
| |
|
| |
| for _, entry in lib.spairs(
| |
| info,
| |
| function(tVal, a, b)
| |
| local aPage = tostring(tVal[a].page or a)
| |
| local bPage = tostring(tVal[b].page or b)
| |
|
| |
| return aPage < bPage
| |
| end
| |
| ) do
| |
|
| |
| if ne(entry.page) then
| |
| jsonEntries[#jsonEntries + 1] = entry.page
| |
| end
| |
|
| |
| end
| |
|
| |
| local jsonText = mw.text.jsonEncode(jsonEntries)
| |
|
| |
| return frame:extensionTag(
| |
| 'syntaxhighlight',
| |
| jsonText,
| |
| {
| |
| lang = 'json',
| |
| copy = ''
| |
| }
| |
| )
| |
| end
| |
|
| |
| return ''
| |
| end
| |
|
| |
|
| |
| -- ============================================================
| |
| -- NORMAL LUA MODE
| |
| -- ============================================================
| |
|
| |
| -- Parse DPL data
| |
| if frame.args.info then | | if frame.args.info then |
| | | if ne(frame.args.cleanTT) then frame.args.info = CleanTT(frame.args.info) end -- remove TTs |
| info = parseInfo(frame.args.info, frame) | | info = lib.parseTemplateFormat(frame.args.info, ';;', '$$$', '==') |
| | | else return '' end |
| else
| | if frame.args.keys then dataKeys = p.ParseKeys(frame.args.keys) end |
| return ''
| | if ne(frame.args.block) then block = true end |
| end
| |
| | |
| | |
| -- Parse keys
| |
| if frame.args.keys then
| |
| dataKeys = p.ParseKeys(frame.args.keys) | |
| end | |
| | |
| | |
| -- Parse unquoted/raw keys
| |
| if frame.args.unquoted then | |
| rawKeys = p.ParseKeys(frame.args.unquoted)
| |
| end
| |
| | |
| | |
| -- Block formatting
| |
| if ne(frame.args.block) then | |
| block = true
| |
| end
| |
| | |
| | |
| local sortkey = frame.args.sortkey or 'page' | | local sortkey = frame.args.sortkey or 'page' |
| local subtable = frame.args.subtable | | local subtable = frame.args.subtable |
| | | |
| | | local result = mw.html.create('pre'):css({['min-height'] = '25px',['line-height'] = '16px'}):wikitext('return {\n') |
| -- Constant value mode | | local items = {} |
| -- If |value=day is supplied, every page gets that value.
| | for _,entry in lib.spairs(info, function(tVal, a, b) if sortkey == 'INPUT' then return a < b else return (tVal[a][sortkey] or '') < (tVal[b][sortkey] or '') end end) do |
| local constantValue = frame.args.value | |
| local valueUnquoted = false
| |
| | |
| if constantValue ~= nil then
| |
| | |
| for _, rawKey in ipairs(rawKeys) do
| |
| | |
| if rawKey == 'value' then
| |
| valueUnquoted = true
| |
| break
| |
| end
| |
| | |
| end
| |
| | |
| end
| |
| | |
| | |
| -- Check whether a key should be output as raw Lua
| |
| local function isUnquotedKey(key)
| |
| | |
| for _, rawKey in ipairs(rawKeys) do
| |
| | |
| if rawKey == key then
| |
| return true
| |
| end
| |
| | |
| end
| |
| | |
| return false
| |
| end
| |
| | |
| | |
| -- Build result
| |
| local result = mw.html.create()
| |
| :wikitext('return {\n')
| |
| | |
| | |
| for _, entry in lib.spairs( | |
| info,
| |
| function(tVal, a, b)
| |
| | |
| if sortkey == 'INPUT' then
| |
| return a < b
| |
| else
| |
| return (tVal[a][sortkey] or '') <
| |
| (tVal[b][sortkey] or '')
| |
| end
| |
| | |
| end
| |
| ) do
| |
| | |
| if ne(entry.page) then | | if ne(entry.page) then |
|
| |
| entry.page = clean(entry.page) | | entry.page = clean(entry.page) |
| | | if #dataKeys == 0 then |
| | | result:wikitext('\t\'', entry.page, '\',\n') |
| -- ----------------------------------------------------
| |
| -- Constant value mode
| |
| -- ----------------------------------------------------
| |
| | |
| if constantValue ~= nil and #dataKeys == 0 then | |
| | |
| local value
| |
| | |
| if valueUnquoted then
| |
| value = clean(constantValue)
| |
| else
| |
| value = '\'' ..
| |
| clean(constantValue) ..
| |
| '\''
| |
| end
| |
| | |
| result:wikitext(
| |
| '\t[\'',
| |
| entry.page,
| |
| '\'] = ',
| |
| value,
| |
| ',\n'
| |
| )
| |
| | |
| | |
| -- ----------------------------------------------------
| |
| -- No data keys
| |
| -- ----------------------------------------------------
| |
| | |
| elseif #dataKeys == 0 then
| |
| | |
| result:wikitext( | |
| '\t\'',
| |
| entry.page,
| |
| '\',\n'
| |
| )
| |
| | |
| | |
| -- ----------------------------------------------------
| |
| -- Normal nested table mode
| |
| -- ----------------------------------------------------
| |
| | |
| else | | else |
|
| |
| -- Reset formatting state for every entry
| |
| first = {true, true, true} | | first = {true, true, true} |
| previous = {false, false, false} | | previous = {false, false, false} |
|
| |
| local valid = false | | local valid = false |
| local values = mw.html.create() | | local values = mw.html.create() |
| | | values:wikitext('\t[\'', entry.page, '\']') |
| | | |
| -- Entry name
| | --build entry |
| values:wikitext( | |
| '\t[\'',
| |
| entry.page,
| |
| '\'] = {'
| |
| ) | |
| | |
| | |
| -- Build entry | |
| local function parseLevels(K, level) | | local function parseLevels(K, level) |
|
| |
| level = level or 1 | | level = level or 1 |
| | | if type(K) == 'string' and ne(entry[K]) then |
| if type(K) == 'string' then | |
| | |
| valid = true | | valid = true |
| | | values:node(p.BuildEntry(entry[K], K, level, subtable)) |
| -- Use the value if present.
| |
| -- BuildEntry handles missing values.
| |
| values:node( | |
| p.BuildEntry(
| |
| entry[K],
| |
| K,
| |
| level,
| |
| subtable,
| |
| isUnquotedKey(K)
| |
| )
| |
| )
| |
| | |
| | |
| elseif type(K) == 'table' then | | elseif type(K) == 'table' then |
|
| |
| level = level + 1 | | level = level + 1 |
|
| |
| for _, K2 in ipairs(K) do | | for _, K2 in ipairs(K) do |
| parseLevels(K2, level) | | parseLevels(K2, level) |
| end | | end |
| | | values:wikitext((block and ('\n' .. string.rep('\t', level-1)) or ' '),'},\n') |
| values:wikitext( | |
| '\n',
| |
| string.rep('\t', level - 1),
| |
| '},'
| |
| )
| |
| | |
| end | | end |
| end | | end |
|
| |
|
| |
| parseLevels(dataKeys, 1) | | parseLevels(dataKeys, 1) |
| | | |
| | | --return table is any valid data, return plain string otherwise |
| -- Return table if there is valid data | |
| if valid then | | if valid then |
|
| |
| values:wikitext('\n')
| |
| result:node(values) | | result:node(values) |
|
| |
| else | | else |
| | | result:wikitext('\t\'', entry.page, '\',\n') |
| result:wikitext( | |
| '\t\'',
| |
| entry.page,
| |
| '\',\n'
| |
| )
| |
| | |
| end | | end |
| end | | end |
| end
| | end |
| end | | end |
|
| |
|
| |
| result:wikitext('}') | | result:wikitext('}') |
| | | |
| | |
| -- ============================================================ | |
| -- LUA SYNTAX HIGHLIGHT
| |
| -- ============================================================
| |
| | |
| local luaText = tostring(result)
| |
| | |
| -- Decode HTML entities introduced by mw.html
| |
| luaText = mw.text.decode(luaText)
| |
| | |
| | |
| local output = frame:extensionTag(
| |
| 'syntaxhighlight',
| |
| luaText,
| |
| {
| |
| lang = 'lua',
| |
| copy = ''
| |
| }
| |
| )
| |
| | |
| | |
| -- ============================================================
| |
| -- COPY BUTTON
| |
| -- ============================================================
| |
| | |
| -- Keep Module:CopyText for normal Lua output.
| |
| if frame.args.copy then | | if frame.args.copy then |
| | | result = require('Module:CopyText')._main({ tostring(result), frame.args.copy, 'Copy Table' }) |
| output = require('Module:CopyText')._main({ | |
| output,
| |
| frame.args.copy,
| |
| 'Copy Table'
| |
| })
| |
| | |
| end | | end |
| | | |
| | | return result |
| return output | |
| end | | end |
|
| |
|
| | | --format data |
| -- ================================================================
| | function p.BuildEntry(value, key, level, subtable) |
| -- FORMAT DATA
| |
| -- ================================================================ | |
| | |
| function p.BuildEntry(value, key, level, subtable, unquoted) | |
| | |
| level = level or 1 | | level = level or 1 |
|
| |
| local container = mw.html.create() | | local container = mw.html.create() |
| | | if ne(value) then |
| | | if subtable and value:find(subtable) then |
| -- Default missing values
| | value = lib.split(clean(value), subtable) |
| if value == nil or value == '' then | | value = '{ \'' .. table.concat(value, '\', \'') .. '\' }' |
| | |
| if key == 'number' then | |
| | |
| value = '0' | |
| unquoted = true
| |
| | |
| else | | else |
| | | value = '\'' .. clean(value) .. '\'' |
| value = '' | |
| unquoted = false
| |
| | |
| end | | end |
| end
| | if first[level] then |
| | | container:wikitext(' = {', (block and ('\n' .. string.rep('\t', level)) or ' '), clean(key), ' = ', value) |
| | | first[level] = false |
| -- Format value
| | if level > 2 then first[2] = false end |
| if subtable and value ~= '' and value:find(subtable) then
| | if level > 1 then first[1] = false end |
| | | elseif (not first[level]) then |
| value = lib.split(
| | container:wikitext((block and (',\n' .. string.rep('\t', level)) or ', '), clean(key), ' = ', value) |
| clean(value),
| | previous[level] = false |
| subtable
| |
| )
| |
| | |
| value = '{ \'' ..
| |
| table.concat(value, '\', \'') ..
| |
| ' }'
| |
| | |
| | |
| elseif unquoted and value ~= '' then
| |
| | |
| -- Raw Lua value
| |
| value = clean(value)
| |
| | |
| | |
| else
| |
| | |
| -- String value
| |
| value = '\'' ..
| |
| clean(value) ..
| |
| '\'' | |
| | |
| end
| |
| | |
| | |
| -- Format key as ['key']
| |
| local formattedKey =
| |
| '[\'' .. clean(key) .. '\']' | |
| | |
| | |
| -- First item on this level
| |
| if first[level] then
| |
| | |
| container:wikitext(
| |
| '\n',
| |
| string.rep('\t', level),
| |
| formattedKey,
| |
| ' = ',
| |
| value,
| |
| ',' | |
| )
| |
| | |
| first[level] = false
| |
| | |
| | |
| if level > 2 then
| |
| first[2] = false
| |
| end | | end |
|
| |
|
| |
| if level > 1 then
| |
| first[1] = false
| |
| end
| |
|
| |
|
| |
| -- Subsequent item on this level
| |
| else
| |
|
| |
| container:wikitext(
| |
| '\n',
| |
| string.rep('\t', level),
| |
| formattedKey,
| |
| ' = ',
| |
| value,
| |
| ','
| |
| )
| |
|
| |
| previous[level] = false
| |
|
| |
| end | | end |
|
| |
|
| |
| return container | | return container |
| end | | end |
|
| |
|
| | | --parse data keys into specified levels |
| -- ================================================================ | |
| -- PARSE DATA KEYS INTO SPECIFIED LEVELS
| |
| -- ================================================================
| |
| | |
| function p.ParseKeys(keys) | | function p.ParseKeys(keys) |
|
| |
| keys = lib.split(keys, ';;') | | keys = lib.split(keys, ';;') |
|
| |
| for i, key in pairs(keys) do | | for i, key in pairs(keys) do |
|
| |
| if key:find('//') then | | if key:find('//') then |
|
| |
| local keys2 = lib.split(key, '//') | | local keys2 = lib.split(key, '//') |
|
| |
| for i2, key2 in pairs(keys2) do | | for i2, key2 in pairs(keys2) do |
|
| |
| if key2:find('@@') then | | if key2:find('@@') then |
| keys2[i2] = lib.split(key, '@@') | | keys2[i2] = lib.split(key, '@@') |
| end | | end |
|
| |
| end | | end |
|
| |
| keys[i] = keys2 | | keys[i] = keys2 |
| end | | end |
| end | | end |
|
| |
| return keys | | return keys |
| end | | end |
|
| |
|
| |
|
| return p | | return p |