Module:Listbox

Revision as of 00:59, June 22, 2020 by Locke (talk | contribs) (saving WIP for gallery layout. realized this will be much easier if I first convert Tab (and maybe Gallery too))
Want an adless experience? Log in or Create an account.

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

local Box = require( 'Module:Box' ).Box

function getCategoryProps( categoryName )
  local categoryContent = mw.title.new( categoryName, 'Category' ):getContent()
  local props = {}

  for match in string.gmatch( categoryContent, '{{Prop|([^}]*)}}' ) do
    local prop, value = unpack(mw.text.split( match, '|' ))

    -- table if multiple values, value if one value, true if no values (i.e. flag)
    props[prop] = value and string.match( value, ',' ) and mw.text.split( value, ',' ) or value or true
  end

  return props
end

function getPageTree( categoryName, expandVariants )
  -- get all pages in the category
  local pages = mw.ext.dpl.getPages{ category = categoryName, ordermethod = 'sortkey', order = 'ascending' }

  -- organize them according to metadata
  local pageTree = { default = {} }
  for _, page in ipairs( pages ) do
    -- get metadata
    if page.extra then
      -- remove extra from sortkey
      page.sortkey = string.sub( page.sortkey, 1, -string.len( page.extra ) - 2 )

      local success, result = pcall( mw.text.jsonDecode, page.extra )
      if success then
        page.extra = result
      else
        page.sortkey = page.text .. mw.getCurrentFrame():expandTemplate{ title = 'Tt', args = { 'PARSING ERROR: Please ensure the argument to \'extra\' in this page\'s invocation of \'Cat\' is valid JSON' } }
        page.extra = {}
      end
    else page.extra = {}
    end

    -- add page to group
    page.__index = page -- allow variants to fall back to page
    page.extra.__index = page.extra -- allow variants' extra to fall back to page's extra
    local variants = expandVariants and page.extra.variants or {{}} -- that's a table containing a single table (variant) with no properties (overrides)
    for _, variant in ipairs( variants ) do
      setmetatable( variant, page.extra )
      if not variant.group then variant.group = 'default' end
      if not pageTree[variant.group] then pageTree[variant.group] = {} end
      pageTree[variant.group][#pageTree[variant.group] + 1] = setmetatable( { extra = variant }, page )
    end
  end

  return pageTree
end

function buildGallery( categoryProps, pageTree )
  local tabGroupId = 'TODO' -- mw.getCurrentFrame():expandTemplate{ title = 'Zelda', args = { game } } .. category -- or strip whitespace from self.subject

  -- from TabSettings: {{#vardefine:TabsID|{{{1|}}}}}{{#vardefine:TabsDefault|{{#if:{{{2|}}}|{{{2|}}}|1}}}}  
  -- from CD/Decorator: {{#vardefine:cd-dec|gallery}}{{#vardefine:cd-tabindex|0}}{{TabSettings|{{{2|{{PAGENAME}}}}}}}<div class{{=}}"tab"><ul></ul></div><div class{{=}}"tabcontent">

  local content = mw.html.create( 'div' )
  content:tag( 'div' )
    :addClass( 'tab' )
    :tag( 'ul' )
  local tabcontent = content:tag( 'div' )
    :addClass( 'tabcontent' )

  -- from CD/Header:
--{{#if: {{{1|}}}
--  | {{Tab|{{#vardefineecho:cd-tabindex|{{#expr:{{#var:cd-tabindex}}+1}}}}|{{{1|}}}}}
--<div class{{=}}"_toggle {{#var:TabsID}}tab{{#var:cd-tabindex}} {{#var:TabsID}}tabs" {{#ifeq:{{#var:TabsDefault}}|{{#var:cd-tabindex}}|style{{=}}"display: none;"|}}></div><div class{{=}}"_toggle {{#var:TabsID}}tab{{#var:cd-tabindex}} {{#var:TabsID}}tabs" {{#ifeq:{{#var:TabsDefault}}|{{#var:cd-tabindex}}||style{{=}}"display: none;"}}>
--  | <div>
--  }}{{Gallery|size={{#ifeq:{{{size|}}}|large|95|62}}|pad=1|square={{{square|true}}}}}

  -- from CD:
--{{#if:{{{caption1|}}}
--  | {{#forargs:caption|key|val
--    | {{#ifexpr:{{#var:key}}>1|{{N}}}}{{Gallery/Box
--      | file = {{{file{{#var:key}}|{{{file|}}}}}}
--      | link = {{{link{{#var:key}}|{{{link|}}}}}}
--      | tip = {{{tip{{#var:key}}|{{{tip|{{#var:val}}}}}}}}
--      | caption = [[{{{link{{#var:key}}|{{{link|}}}}}}|{{#var:val}}]]}}
--    }}
--  | {{Gallery/Box|file={{{file|}}}|link={{{link|}}}|tip={{{tip|{{{caption|{{{link|}}}}}}}}}|caption=[[{{{link|}}}|{{{caption|{{{link|}}}}}}]]}}
--  }}

  -- from CD/End: {{Gallery/Bottom}}</div>
  return content
end

function renderHList( parent, pages )
  local list = parent:addClass( 'hlist' )
    :tag( 'ul' )
  for _, page in ipairs( pages ) do
    list:tag( 'li' )
      :wikitext( '[[' .. page.text .. '|' .. (page.extra.name or page.sortkey or page.text) .. ']]' )
  end
end

function buildTable( categoryProps, pageTree )
  local content = mw.html.create( 'table' )
  if #pageTree.default then
    local defaultCell = content:tag( 'tr' )
      :tag( 'td' )
      :addClass( 'odd' )
      :attr( 'colspan', '2' )
      :css( 'text-align', 'center' )
    renderHList( defaultCell, pageTree.default )
  end
  if categoryProps.groups then
    -- coerce it to a table for easier processing
    local groups = type(categoryProps.groups) == 'table' and categoryProps.groups or { categoryProps.groups }
    for index, group in ipairs(groups) do
      if( pageTree[group] ) then
        local row = content:tag( 'tr' )
        row:tag( 'th' )
          :addClass( 'label' )
          :wikitext( group )
        local cell = row:tag( 'td' )
          :addClass( (index + (#pageTree.default and 1 or 0)) % 2 == 0 and 'even' or 'odd' )
        renderHList( cell, pageTree[group] )
      end
    end
  end

  return content
end

local Navbox = Box.new()
Navbox.__index = Navbox
setmetatable( Navbox, Box )

function Navbox.new( format, args )
  local subject = args[1] .. ' ' .. args[2]
  args.class = 'navbox'
  args.title = subject
  args.edit = subject
  args.hide = 'show' -- TODO count number of navboxes, hide after the second or third (could relegate to the calling template)
  local obj = Box.new( 'light', args )
  obj.format = format
  obj.subject = subject
  obj.categories = { args[1], subject, args[2] }
  return setmetatable( obj, Navbox )
end

function Navbox:renderContent()
  local categoryProps = getCategoryProps( self.subject )
  local pageTree = getPageTree( self.subject, self.format == 'gallery' )
  local build = setmetatable( {
    gallery = buildGallery
  }, {
    __index = function() return buildTable end -- default
  } )
  return build[self.format]( categoryProps, pageTree )
end

local p, mt = {}, {}

function p._main( format, args )
  local navbox = Navbox.new( format, args )
  return navbox:render()
end

-- translates p.function( frame ) to p._main( function, args )
function mt.__index( table, key )
  return function ( frame )
    return table._main( key, frame.args )
  end
end

-- for use in the debug console:
-- =p.list(p.debugframe)
p.debugframe = {
  args = {
    [1] = "The Legend of Zelda",
    [2] = "Enemies",
  }
}

return setmetatable( p, mt )