Jump to content

Module:OOOInfoBox: Difference between revisions

From Out of Ore Wiki
No edit summary
Tag: Reverted
mNo edit summary
 
(17 intermediate revisions by 2 users not shown)
Line 1: Line 1:
-- Module:OOOInfoBox
local DATA = require('Module:OOO/Data')
local p = {}
local p = {}


-- Try JSON first (Template:Docs.json), then Lua table (Module:DocsJSON)
-- ---------------------------------------------------------------------------
local function load_docs()
-- Helpers
    local ok, data = pcall(mw.loadJsonData, 'Template:Docs.json')
-- ---------------------------------------------------------------------------
    if ok and type(data) == 'table' then
        return data, 'Template:Docs.json (loadJsonData)'
    end


    local t = mw.title.new('Template:Docs.json')
local function trim(s)
    if t then
  return s and mw.text.trim(tostring(s)) or s
        local content = t:getContent()
        if content and content ~= '' then
            local ok2, tbl = pcall(mw.text.jsonDecode, content)
            if ok2 and type(tbl) == 'table' then
                return tbl, 'Template:Docs.json (jsonDecode)'
            end
        end
    end
end
end


local DOCS, SOURCE = load_docs()
local function trim(s) return s and mw.text.trim(tostring(s)) or s end
local function pick(args, data, key)
local function pick(args, data, key)
    local v = args[key]; if v and trim(v) ~= '' then return trim(v) end
  local v = args[key]
    v = data[key]; if v ~= nil and tostring(v) ~= '' then return v end
  if v and trim(v) ~= '' then return trim(v) end
  v = data and data[key]
  if v ~= nil and tostring(v) ~= '' then return v end
end
end


local function warn(msg)
local function warn(msg)
    return '<div class="mw-message-box mw-message-box-warning"><b>OOOInfoBox</b>: '..msg..'</div>'
  return '<div class="mw-message-box mw-message-box-warning"><b>OOOInfoBox</b>: '..msg..'</div>'
end
 
local function render_image(name)
  if not name or name == '' then return nil end
  return string.format('[[File:%s|center|240px]]', mw.text.trim(name))
end
 
-- ---------------------------------------------------------------------------
-- Units
-- ---------------------------------------------------------------------------
 
local U = {
  m  = '&nbsp;m',
  m2 = '&nbsp;m<sup>2</sup>',
  m3 = '&nbsp;m<sup>3</sup>',
  mw = '&nbsp;MW',
  kw = '&nbsp;kW',
  pmin = '&nbsp;/&nbsp;min',
}
 
local function with_unit(v, unit)
  if v == nil or v == '' then return nil end
  return tostring(v) .. (unit or '')
end
 
local function format_price(v)
  if not v then return nil end
 
  local n = tonumber(v)
  if not n then return v end
 
  local s = tostring(math.floor(n))
  s = s:reverse():gsub("(%d%d%d)", "%1 "):reverse():gsub("^ ", "")
 
  return s .. ".-"
end
 
-- ---------------------------------------------------------------------------
-- Row helpers
-- ---------------------------------------------------------------------------
 
local function label_with_tip(label, tip)
  if not tip or tip == '' then return label end
  return string.format('<span title="%s">%s</span>', tip, label)
end
end


-- more forgiving key lookup + debug
local function add_row(tbl, label, value, tip)
local function find_record(art)
  if value ~= nil and tostring(value) ~= '' then
    -- direct lookups
     local tr = tbl:tag('tr')
     local rec = DOCS[art] or DOCS[tostring(tonumber(art) or art)]
    tr:tag('th'):wikitext(label_with_tip(label, tip))
     if rec then return rec end
     tr:tag('td'):wikitext(value)
  end
end


    -- trim/loose match on keys (handles stray spaces/non-visible chars)
local function add_group_header(tbl, text)
    local art_trim = trim(art)
  local tr = tbl:tag('tr'):addClass('ooo-infobox__group')
    for k, v in pairs(DOCS) do
  tr:tag('th'):attr('colspan', '2'):wikitext(text)
        if trim(tostring(k)) == art_trim then
end
            return v
 
        end
local function add_group(tbl, header, rows)
  local has = false
 
  for _, r in ipairs(rows) do
    if r and r[2] ~= nil and tostring(r[2]) ~= '' then
      has = true
      break
     end
     end
     return nil
  end
 
  if not has then return end
 
  add_group_header(tbl, header)
 
  for _, r in ipairs(rows) do
    if r then
      local label, value, tip = r[1], r[2], r[3]
      if value ~= nil and tostring(value) ~= '' then
        add_row(tbl, label, value, tip)
      end
     end
  end
end
 
-- ---------------------------------------------------------------------------
-- Dimension builder
-- ---------------------------------------------------------------------------
 
local function join_dims(a, b, c, unit)
  local parts = {}
 
  if a then parts[#parts+1] = tostring(a) end
  if b then parts[#parts+1] = tostring(b) end
  if c then parts[#parts+1] = tostring(c) end
 
  if #parts == 0 then return nil end
 
  return table.concat(parts, ' × ') .. (unit or '')
end
end
-- ---------------------------------------------------------------------------
-- Main renderer
-- ---------------------------------------------------------------------------


function p.building(frame)
function p.building(frame)
    local args = frame:getParent().args or {}
 
     local art = trim(args.art or args.artNumber or args.id)
  local parent = frame:getParent() or frame
    if not art or art == '' then
  local args = parent.args or {}
        return warn('Use |art=ART_NUMBER (e.g. 400113).')
 
     end
  local out = mw.html.create('div')
     if type(DOCS) ~= 'table' or next(DOCS) == nil then
 
        return warn('Could not load data. Tried: '..(SOURCE or '(unknown)')..'.')
  out:wikitext(frame:extensionTag(
     'templatestyles','',{ src = 'Template:OOOInfoBox/styles.css' }
  ))
 
  local domain = trim(args.type or args.domain or 'Buildings')
  local art   = trim(args.art or args.artNumber or args.id)
 
  if not art or art == '' then
    out:wikitext(warn('Use |art=ART_NUMBER (e.g. 400113).'))
     return tostring(out)
  end
 
  local data = DATA.get(domain, art)
 
  if not data then
    out:wikitext(warn(('No record for %s:%s.'):format(domain, art)))
     return tostring(out)
  end
 
  -- -------------------------------------------------------------------------
  -- Fields
  -- -------------------------------------------------------------------------
 
  local title  = pick(args, data, 'displayName') or ('Item '..art)
  local image  = pick(args, data, 'image')
  local caption = pick(args, data, 'description') or pick(args, data, 'caption')
 
  local box = mw.html.create('table')
      :addClass('ooo-infobox')
      :addClass('ooo-infobox--' .. string.lower(domain))
 
  -- optional width
  local w = tonumber(args.width)
  if w and w >= 240 and w <= 600 then
    box:css('width', w .. 'px')
  end
 
  -- alignment modifiers
  local align = trim(args.align)
 
  if align == 'left' then
    box:addClass('ooo-infobox--left')
  elseif align == 'center' then
    box:addClass('ooo-infobox--center')
  elseif align == 'sidebar' then
    box:addClass('ooo-infobox--sidebar')
  end
 
  -- additional classes
  local extra_class = trim(args.class)
 
  if extra_class then
    for c in mw.text.gsplit(extra_class,'%s+') do
      box:addClass(c)
     end
     end
  end
  -- -------------------------------------------------------------------------
  -- Title
  -- -------------------------------------------------------------------------
  box:tag('tr')
    :tag('th')
    :addClass('ooo-infobox__title')
    :attr('colspan','2')
    :wikitext(title)
  -- -------------------------------------------------------------------------
  -- Image
  -- -------------------------------------------------------------------------
  local img = render_image(image)
  if img then
    box:tag('tr')
      :tag('td')
      :addClass('ooo-infobox__image')
      :attr('colspan','2')
      :wikitext(img)
  end
  if caption and caption ~= '' then
  -- Section title
  add_group_header(box, 'Description')
  -- Section content
  local tr = box:tag('tr')
  tr:tag('td')
    :attr('colspan','2')
    :css('text-align','left')
    :css('padding','4px 4px')
    :wikitext(caption)
end
  -- -------------------------------------------------------------------------
  -- General
  -- -------------------------------------------------------------------------
  add_group(box,'General Information',{
    {'Art Number', art},
    {'Category',  pick(args,data,'category')},
    {'Subcategory',pick(args,data,'subcategory')},
    {'Price',      format_price(pick(args,data,'price'))},
  })
  -- -------------------------------------------------------------------------
  -- Power
  -- -------------------------------------------------------------------------
  add_group(box,'Power',{
    {'Power', with_unit(pick(args,data,'power_kw'),U.kw)},
  })
  -- -------------------------------------------------------------------------
  -- Performance
  -- -------------------------------------------------------------------------
  add_group(box,'Performance',{
    {'Throughput (m³/h)', pick(args,data,'throughput_m3h')},
    {'Capacity',          pick(args,data,'capacity')},
  })
  -- -------------------------------------------------------------------------
  -- Input / Output
  -- -------------------------------------------------------------------------
  add_group(box,'I/O',{
    {'Conveyor inputs',  pick(args,data,'beltInputs')},
    {'Conveyor outputs', pick(args,data,'beltOutputs')},
    {'Pipeline inputs',  pick(args,data,'pipeInputs')},
    {'Pipeline outputs', pick(args,data,'pipeOutputs')},
  })
  -- -------------------------------------------------------------------------
  -- Dimensions
  -- -------------------------------------------------------------------------
  local sx,sy,sz
  if data.size and type(data.size) == 'table' then
    sx,sy,sz = data.size.x, data.size.y, data.size.z
  end


    local data = find_record(art)
  local size_w = pick(args,data,'size_width') or sx
    if not data then
  local size_l = pick(args,data,'size_length') or sy
        -- show a few keys to diagnose quickly
  local size_h = pick(args,data,'size_height') or sz
        local samples, count = {}, 0
  local size_note = pick(args,data,'size_note')
        for k, _ in pairs(DOCS) do
            count = count + 1
            if #samples < 5 then table.insert(samples, '"'..tostring(k)..'"') end
        end
        local sample_txt = (#samples > 0) and table.concat(samples, ', ') or '(no keys)'
        return warn(('Key "%s" not found in %s. Sample keys: %s (count: %d)')
            :format(art, SOURCE or '(unknown)', sample_txt, count))
    end


    local box = mw.html.create('table'):addClass('infobox')
  add_group(box,'Dimensions',{
    local title = pick(args, data, 'displayName') or ('Item '..art)
    {'Overall (W × H × L)', join_dims(size_w,size_l,size_h,U.m), 'Width × Height × Length'},
     box:tag('tr'):tag('th'):attr('colspan','2'):wikitext(title)
     {'Note',   size_note},
  })


    local function row(label, key, fixed)
  out:node(box)
        local v = fixed or pick(args, data, key)
        if v ~= nil and tostring(v) ~= '' then
            local tr = box:tag('tr')
            tr:tag('th'):wikitext(label)
            tr:tag('td'):wikitext(v)
        end
    end


    row('Art Number', nil, art)
  return tostring(out)
    row('Type', 'type')
    row('Throughput (m³/h)', 'throughput_m3h')
    row('Power (kW)', 'power_kw')
    row('Capacity', 'capacity')


    box:wikitext('<!-- OOOInfoBox source: '..(SOURCE or 'unknown')..' -->')
    return tostring(box)
end
end


return p
return p

Latest revision as of 09:00, 5 March 2026

Documentation for this module may be created at Module:OOOInfoBox/doc

-- Module:OOOInfoBox

local DATA = require('Module:OOO/Data')
local p = {}

-- ---------------------------------------------------------------------------
-- Helpers
-- ---------------------------------------------------------------------------

local function trim(s)
  return s and mw.text.trim(tostring(s)) or s
end

local function pick(args, data, key)
  local v = args[key]
  if v and trim(v) ~= '' then return trim(v) end
  v = data and data[key]
  if v ~= nil and tostring(v) ~= '' then return v end
end

local function warn(msg)
  return '<div class="mw-message-box mw-message-box-warning"><b>OOOInfoBox</b>: '..msg..'</div>'
end

local function render_image(name)
  if not name or name == '' then return nil end
  return string.format('[[File:%s|center|240px]]', mw.text.trim(name))
end

-- ---------------------------------------------------------------------------
-- Units
-- ---------------------------------------------------------------------------

local U = {
  m  = '&nbsp;m',
  m2 = '&nbsp;m<sup>2</sup>',
  m3 = '&nbsp;m<sup>3</sup>',
  mw = '&nbsp;MW',
  kw = '&nbsp;kW',
  pmin = '&nbsp;/&nbsp;min',
}

local function with_unit(v, unit)
  if v == nil or v == '' then return nil end
  return tostring(v) .. (unit or '')
end

local function format_price(v)
  if not v then return nil end

  local n = tonumber(v)
  if not n then return v end

  local s = tostring(math.floor(n))
  s = s:reverse():gsub("(%d%d%d)", "%1 "):reverse():gsub("^ ", "")

  return s .. ".-"
end

-- ---------------------------------------------------------------------------
-- Row helpers
-- ---------------------------------------------------------------------------

local function label_with_tip(label, tip)
  if not tip or tip == '' then return label end
  return string.format('<span title="%s">%s</span>', tip, label)
end

local function add_row(tbl, label, value, tip)
  if value ~= nil and tostring(value) ~= '' then
    local tr = tbl:tag('tr')
    tr:tag('th'):wikitext(label_with_tip(label, tip))
    tr:tag('td'):wikitext(value)
  end
end

local function add_group_header(tbl, text)
  local tr = tbl:tag('tr'):addClass('ooo-infobox__group')
  tr:tag('th'):attr('colspan', '2'):wikitext(text)
end

local function add_group(tbl, header, rows)
  local has = false

  for _, r in ipairs(rows) do
    if r and r[2] ~= nil and tostring(r[2]) ~= '' then
      has = true
      break
    end
  end

  if not has then return end

  add_group_header(tbl, header)

  for _, r in ipairs(rows) do
    if r then
      local label, value, tip = r[1], r[2], r[3]
      if value ~= nil and tostring(value) ~= '' then
        add_row(tbl, label, value, tip)
      end
    end
  end
end

-- ---------------------------------------------------------------------------
-- Dimension builder
-- ---------------------------------------------------------------------------

local function join_dims(a, b, c, unit)
  local parts = {}

  if a then parts[#parts+1] = tostring(a) end
  if b then parts[#parts+1] = tostring(b) end
  if c then parts[#parts+1] = tostring(c) end

  if #parts == 0 then return nil end

  return table.concat(parts, ' × ') .. (unit or '')
end

-- ---------------------------------------------------------------------------
-- Main renderer
-- ---------------------------------------------------------------------------

function p.building(frame)

  local parent = frame:getParent() or frame
  local args = parent.args or {}

  local out = mw.html.create('div')

  out:wikitext(frame:extensionTag(
    'templatestyles','',{ src = 'Template:OOOInfoBox/styles.css' }
  ))

  local domain = trim(args.type or args.domain or 'Buildings')
  local art    = trim(args.art or args.artNumber or args.id)

  if not art or art == '' then
    out:wikitext(warn('Use |art=ART_NUMBER (e.g. 400113).'))
    return tostring(out)
  end

  local data = DATA.get(domain, art)

  if not data then
    out:wikitext(warn(('No record for %s:%s.'):format(domain, art)))
    return tostring(out)
  end

  -- -------------------------------------------------------------------------
  -- Fields
  -- -------------------------------------------------------------------------

  local title   = pick(args, data, 'displayName') or ('Item '..art)
  local image   = pick(args, data, 'image')
  local caption = pick(args, data, 'description') or pick(args, data, 'caption')

  local box = mw.html.create('table')
      :addClass('ooo-infobox')
      :addClass('ooo-infobox--' .. string.lower(domain))

  -- optional width
  local w = tonumber(args.width)
  if w and w >= 240 and w <= 600 then
    box:css('width', w .. 'px')
  end

  -- alignment modifiers
  local align = trim(args.align)

  if align == 'left' then
    box:addClass('ooo-infobox--left')
  elseif align == 'center' then
    box:addClass('ooo-infobox--center')
  elseif align == 'sidebar' then
    box:addClass('ooo-infobox--sidebar')
  end

  -- additional classes
  local extra_class = trim(args.class)

  if extra_class then
    for c in mw.text.gsplit(extra_class,'%s+') do
      box:addClass(c)
    end
  end

  -- -------------------------------------------------------------------------
  -- Title
  -- -------------------------------------------------------------------------

  box:tag('tr')
     :tag('th')
     :addClass('ooo-infobox__title')
     :attr('colspan','2')
     :wikitext(title)

  -- -------------------------------------------------------------------------
  -- Image
  -- -------------------------------------------------------------------------

  local img = render_image(image)

  if img then
    box:tag('tr')
       :tag('td')
       :addClass('ooo-infobox__image')
       :attr('colspan','2')
       :wikitext(img)
  end

  if caption and caption ~= '' then

  -- Section title
  add_group_header(box, 'Description')

  -- Section content
  local tr = box:tag('tr')
  tr:tag('td')
    :attr('colspan','2')
    :css('text-align','left')
    :css('padding','4px 4px')
    :wikitext(caption)

end

  -- -------------------------------------------------------------------------
  -- General
  -- -------------------------------------------------------------------------

  add_group(box,'General Information',{
    {'Art Number', art},
    {'Category',   pick(args,data,'category')},
    {'Subcategory',pick(args,data,'subcategory')},
    {'Price',      format_price(pick(args,data,'price'))},
  })

  -- -------------------------------------------------------------------------
  -- Power
  -- -------------------------------------------------------------------------

  add_group(box,'Power',{
    {'Power', with_unit(pick(args,data,'power_kw'),U.kw)},
  })

  -- -------------------------------------------------------------------------
  -- Performance
  -- -------------------------------------------------------------------------

  add_group(box,'Performance',{
    {'Throughput (m³/h)', pick(args,data,'throughput_m3h')},
    {'Capacity',          pick(args,data,'capacity')},
  })

  -- -------------------------------------------------------------------------
  -- Input / Output
  -- -------------------------------------------------------------------------

  add_group(box,'I/O',{
    {'Conveyor inputs',  pick(args,data,'beltInputs')},
    {'Conveyor outputs', pick(args,data,'beltOutputs')},
    {'Pipeline inputs',  pick(args,data,'pipeInputs')},
    {'Pipeline outputs', pick(args,data,'pipeOutputs')},
  })

  -- -------------------------------------------------------------------------
  -- Dimensions
  -- -------------------------------------------------------------------------

  local sx,sy,sz

  if data.size and type(data.size) == 'table' then
    sx,sy,sz = data.size.x, data.size.y, data.size.z
  end

  local size_w = pick(args,data,'size_width')  or sx
  local size_l = pick(args,data,'size_length') or sy
  local size_h = pick(args,data,'size_height') or sz
  local size_note = pick(args,data,'size_note')

  add_group(box,'Dimensions',{
    {'Overall (W × H × L)', join_dims(size_w,size_l,size_h,U.m), 'Width × Height × Length'},
    {'Note',    size_note},
  })

  out:node(box)

  return tostring(out)

end

return p