Final Fantasy Wiki
Advertisement

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

--This module creates the Navbox metatemplate. Requires Dev arguments  and FFWiki and  Array libraries.
Navbox = {}

local expT = require('Module:FFWiki').expandTemplate
local getArgs = require('Dev:Arguments').getArgs
array = require('Module:Array')
-- string = require('Module:String')
lseq = require('Module:HF').lettersequence

--Function that returns the output of all Navbox functions and ultimately displays the Navbox.
function Navbox.main(frame)
  local args = getArgs(frame)
  return Navbox.render(Navbox.parse(args))
end

--Helper function that creates metatables in Lua that will be output as HTML tables.
function Navbox.parse(f)
  local n = {}
  n.nested = not not (f[1] and f[1]:contains('nested'))
  n.editlink = f['editlink']
  n.above = f['above'] and { text = f['above'] } or false
  n.below = f['below'] and { text = f['below'] } or false
  n.position = f['position'] or 'bottom'
  n.width = f['width'] or (n.position=='bottom' and '100%' or '200px')
  n.class = f['class'] or 'series'
  n.collapsible = true
  n.collapsed = n.position=='bottom'

--Creates inheritance metatables allowing multiple styles and classes to pass to each Navbox element. Applies to entire Navbox.
  n.inherit = {}
  n.inherit.__index = n.inherit
  if f['meta class'] then n.inherit.class = f['meta class'] or '' end
  n.inherit.style = f['meta style'] or ''
  n.inherit.block = {}
  n.inherit.block.__index = n.inherit.block
  setmetatable(n.inherit.block, n.inherit)
  if f['blocks class'] then n.inherit.block.class = f['blocks class'] end
  if f['blocks style'] then n.inherit.block.style = n.inherit.block.style .. ';' .. f['blocks style'] end
  n.inherit.block.collapsible = true
  n.inherit.block.collapsed = false
  n.inherit.block.columns = false
  n.inherit.block.wraplinks = false
  n.inherit.block.normallists = false
  n.inherit.content = {}
  n.inherit.content.__index = n.inherit.content
  setmetatable(n.inherit.content, n.inherit)
  if f['contents class'] then n.inherit.content.class = f['contents class'] end
  if f['contents style'] then n.inherit.content.style = n.inherit.content.style .. ';' .. f['contents style'] end
  n.inherit.group = {}
  n.inherit.group.__index = n.inherit.group
  setmetatable(n.inherit.group, n.inherit)
  if f['groups class'] then n.inherit.group.class = f['groups class'] end
  if f['groups style'] then n.inherit.group.style = n.inherit.group.style .. ';' .. f['groups style'] end
  n.inherit.header = {}
  n.inherit.header.__index = n.inherit.header
  setmetatable(n.inherit.header, n.inherit)
  if f['headers class'] then n.inherit.header.class = f['headers class'] end
  if f['headers style'] then n.inherit.header.style = n.inherit.header.style .. ';' .. f['headers style'] end
  n.title = {}
  setmetatable(n.title, n.inherit)
  mw.log(n.title.style)
  n.title.text = f['title'] or 'Title?'
  if f['title class'] then n.title.class = f['title class'] end
  if f['title style'] then n.title.style = n.title.style .. ';' .. f['title style'] end  
  if n.above then
    setmetatable(n.above, n.inherit)
    if f['above class'] then n.above.class = f['above class'] end
    if f['above style'] then n.above.style = n.above.style .. ';' .. f['above style'] end
  end
  if n.below then
    setmetatable(n.below, n.inherit)
    if f['below class'] then n.below.class = f['below class'] end
    if f['below style'] then n.below.style = n.below.block.style .. ';' .. f['below style'] end
  end

--Creates an options parameter. 
--Options are: collapsible (collapsed and collapsible by default), collapsible groups (collapsible and uncollapsed by default), columns (false by default), list types.
  do
    local opt = ',' .. string.gsub(f['options'] or '', ' ', '') .. ','
    if string.find(opt, ',nocollapse,') then n.collapsible = false n.collapsed = false
    elseif string.find(opt, ',collapsed,') then n.collapsed = true
    elseif string.find(opt, ',uncollapsed,') then n.collapsed = false
    end
    if string.find(opt, ',uncollapsiblegroups,') then n.inherit.block.collapsible = false n.inherit.block.collapsed = false
    elseif string.find(opt, ',collapsedgroups,') then n.inherit.block.collapsed = true end
    if string.find(opt, ',columns,') then n.inherit.block.columns = true end
    if string.find(opt, ',normallists,') then n.inherit.block.normallists = true end
    if string.find(opt, ',wraplinks,') then n.inherit.block.wraplinks = true end
  end

--Creates an array for blocks.
  n.blocks = array.new()

  i = 1
  while true do
    local l = lseq(i)
    if not
      (f['content' .. l .. '1']
    or f['nested' .. l]
    or f['nestedplain' .. l])
    then break end
    local blk = { }

 --Creating inheritance metatables for block content. Applies only to that block.
    blk.header = f['block' .. l] and { text = f['block' .. l] } or false
    blk.title = f['header' .. l] and { text = f['header' .. l] } or false
    setmetatable(blk, n.inherit.block)
    blk.inherit = {}
    blk.inherit.__index = blk.inherit
    setmetatable(blk.inherit, n.inherit.block)
    if f[l..' class'] then blk.inherit.class = f['contents'..l..' class'] end
    if f[l..' style'] then blk.inherit.style = blk.inherit.style .. ';' .. f['contents'..l..' style'] end
    blk.inherit.content = {}
    blk.inherit.content.__index = blk.inherit.content
    setmetatable(blk.inherit.content, n.inherit.content)
    if f['contents'..l..' class'] then blk.inherit.content.class = f['contents'..l..' class'] end
    if f['contents'..l..' style'] then blk.inherit.content.style = blk.inherit.content.style .. ';' .. f['contents'..l..' style'] end
    blk.inherit.group = {}
    blk.inherit.group.__index = blk.inherit.group
    setmetatable(blk.inherit.group, n.inherit.group)
    if f['groups'..l..' class'] then blk.inherit.group.class = f['groups'..l..' class'] end
    if f['groups'..l..' style'] then blk.inherit.group.style = n.inherit.group.style .. ';' .. f['groups'..l..' style'] end
    if blk.title then
      setmetatable(blk.title, n.inherit.header)
      if f['header' .. l .. ' class'] then blk.title.class = f['header' .. l .. ' class'] end
      if f['header' .. l .. ' style'] then blk.title.style = blk.title.style .. ';' .. f['header' .. l .. ' style'] end
    end
    if blk.header then
      setmetatable(blk.header, n.inherit.header)
      if f['block' .. l .. ' class'] then blk.header.class = f['block' .. l .. ' class'] end
      if f['block' .. l .. ' style'] then blk.header.style = blk.header.style .. ';' .. f['block' .. l .. ' style'] end
    end

    blk.nest = f['nested' .. l] and frame:expandTemplate({ title = f['nested' .. l], args =  {'nested'}}) or (f['nestedplain' .. l] or false)
    do
      local opt = ',' .. string.gsub(f['options' .. l] or '', ' ', '') .. ','
      if string.find(opt, ',nocollapse,') then blk.collapsible = false blk.collapsed = false
      elseif string.find(opt, ',collapsed,') then blk.collapsed = true
      elseif string.find(opt, ',uncollapsed,') then blk.collapsed = false
      end
      if string.find(opt, ',columns,') then blk.columns = true end
      if string.find(opt, ',normallists,') then blk.normallists = true end
      if string.find(opt, ',wraplinks,') then blk.wraplinks = true end
    end

--Creates inheritance for and defines groups and their contents within Navbox blocks. Applies only to the groups.
    blk.groups = array.new()

    local j = 1
    while true do
      local k = l .. j
      if not f['content' .. k] then break end
      grp = {}
      setmetatable(grp, n.inherit.group)
      grp.header = f['group' .. k] and { text = f['group' .. k] } or false
      if grp.header then setmetatable(grp.header, blk.inherit.group) end
      if f['group' .. k .. ' class'] then grp.header.class = f['group' .. k .. ' class'] end
      if f['group' .. k .. ' style'] then grp.header.style = grp.header.style .. ';' .. f['group' .. k .. ' style'] end
      grp.content = f['content' .. k] and { text = f['content' .. k] } or false
      if grp.content then setmetatable(grp.content, blk.inherit.content) end
      if f['content' .. k .. ' class'] then grp.content.class = f['content' .. k .. ' class'] end
      if f['content' .. k .. ' style'] then grp.content.style = grp.content.style .. ';' .. f['content' .. k .. ' style'] end
      blk.groups:push(grp)
      j = j + 1
    end
    n.blocks:push(blk)
    i = i + 1
  end
  return n
end

--A function that converts Lua tables into HTML.
function Navbox.render(nav)

--Converts Lua to HTML for the outer Navbox.
  local container = mw.html.create('div')
  local main = mw.html.create('div')
  local title = mw.html.create('div')
  local contents = mw.html.create('table')
  container:addClass('navbox-container'..
  ((nav.position=='left' or nav.position=='right' or nav.position=='bottom') and (' ' .. nav.position) or ''))
    :css('width', nav.width)
    :node(main)
  if nav.editlink then container:attr('id', nav.editlink .. '-nav'):addClass('rel-nav') end
  main:addClass('navbox'):node(title):node(contents)
  if nav.nested then main:addClass('nested') end
  if nav.collapsible then main:addClass('collapsible slide') end
  if nav.collapsed then main:addClass('collapsed') end
  title:addClass('title header ' .. (nav.title.class and (nav.title.class ..'a') or (nav.class .. ((not nav.nested or (nav.blocks[1] and nav.blocks[1].nest or false)) and 'a' or 'b'))))
  if not nav.nested then title:addClass('maintitle') end
  if nav.editlink then title:tag('div'):addClass('editlink'):wikitext(expT({ title = 'tnav', args = { nav.editlink }})) end
  title:tag('div'):addClass('titletext'):wikitext(nav.title.text)

  function spaceV(col, tag) if tag then return mw.html.create(tag):addClass('space-v') end return mw.html.create('tr'):addClass('space-v'):tag('td'):attr('colspan', col or 1):done() end

  function spaceH(row) return mw.html.create('td'):addClass('space-h'):attr('rowspan', row or 1) end

  contents:addClass('contents'):attr('cellpadding', '0'):attr('cellspacing', '0')

--Converts Lua to HTML for above.
  if(nav.above) then
    contents:node(spaceV())
          :tag('tr'):tag('td'):addClass('abovebelow ' .. (nav.above.class and (nav.above.class .. 'a') or (nav.class .. 'b'))):wikitext(nav.above.text)
  end

--Converts Lua to HTML for blocks.
  local blocksarray = mw.html.create('td')
  contents:tag('tr'):node(blocksarray)
  blocksarray:addClass('brickcont')
  for i=1,#nav.blocks do
    blocksarray:node(spaceV(1, 'div'))
    if nav.blocks[i].nest then
      blocksarray:wikitext(nav.blocks[i].nest)
    else
      local blockcontainer = mw.html.create('div')
      blocksarray:node(blockcontainer)
      local blocktable = mw.html.create('table')
      local title = nav.blocks[i].title and mw.html.create('div') or nil
      if title then
        blockcontainer:node(title):node(spaceV(1, 'div'))
        title:addClass('navheader header')
          :addClass(nav.blocks[i].title.class
            and nav.blocks[i].title.class .. 'a'
            or nav.class .. 'b')
          :cssText(nav.blocks[i].title.style)
          :tag('div')
            :addClass('headertext')
            :wikitext(nav.blocks[i].title.text)
        if nav.blocks[i].collapsible then blockcontainer:addClass('collapsible slide') end
        if nav.blocks[i].collapsed then blockcontainer:addClass('collapsed') end
      end
      if not nav.blocks[i].normallists then blockcontainer:addClass('formatlist') end
      if not nav.blocks[i].wraplinks then blockcontainer:addClass('nowraplinks') end

      blockcontainer:node(blocktable)
      blocktable:addClass('brick'):attr('cellpadding', '0'):attr('cellspacing', '0')
      
      if nav.blocks[i].columns then
        blockcontainer:addClass('colcont')
      else
        blockcontainer:addClass('standardcont')
      end
--Begins a table, for which all contents of the block will be contained.
      local table = { rows = {} }
      if nav.blocks[i].header then
        table.header = mw.html.create('th')
          :addClass('group')
          :cssText(nav.blocks[i].header.style)
          :wikitext(nav.blocks[i].header.text)
        local cls = ''
        if nav.blocks[i].header.class then
          cls = nav.blocks[i].header.class .. 'a'
        elseif nav.blocks.header and nav.blocks.header.class then
          cls = nav.blocks.header.class .. 'b'
        else
          cls = nav.class  .. 'b'
        end
        table.header:addClass(cls)
      end
--Converts Lua to HTML for groups.
      for j=1,#nav.blocks[i].groups do
        table.rows[j] = {}
        if nav.blocks[i].groups[j].header then
          table.rows[j].header = mw.html.create('th')
            :addClass((nav.blocks[i].columns and 'col' or (nav.blocks[i].groups[j].header and 'sub' or '')) .. 'group')
            :cssText(nav.blocks[i].groups[j].header.style)
          local cls = ''
          if nav.blocks[i].groups[j].header.class then
            cls = nav.blocks[i].groups[j].header.class .. 'a'
          elseif nav.blocks[i].header and nav.blocks[i].header.class then
            cls = nav.blocks[i].header.class .. 'b'
          elseif nav.blocks[i].title and nav.blocks[i].title.class then
            cls = nav.blocks[i].title.class .. 'b'
          else
            cls = nav.class .. 'b'
          end
          table.rows[j].header:addClass(cls):wikitext(nav.blocks[i].groups[j].header.text)
        end
        table.rows[j].content = mw.html.create('td')
          :addClass(nav.blocks[i].columns and 'navcol' or 'cell')
          :wikitext('\n' .. nav.blocks[i].groups[j].content.text)
          :cssText(nav.blocks[i].groups[j].content.style)
        local cls = ''
        if nav.blocks[i].groups[j].content.class then
          cls = nav.blocks[i].groups[j].content.class .. 'a'
        end
        table.rows[j].content:addClass(cls)
      end

--If the block is a column block, makes calculations for column Navbox block rows and contents.
      local gHeadEx = false
      for k=1, #table.rows do
        if table.rows[k].header then gHeadEx = true break end
      end
      if nav.blocks[i].columns then
        local hTr = gHeadEx and mw.html.create('tr') or nil
        local cTr = mw.html.create('tr')
        local rows = hTr and 3 or 1
        local cols = #table.rows + (table.header and 1 or 0)
        local wid = math.floor(100 / cols)
        if hTr then blocktable:node(hTr):node(spaceV((cols*2)-1)) end
        blocktable:node(cTr)
        if table.header then
          (hTr or cTr):node(table.header):node(spaceH(rows))
          table.header:attr('rowspan', rows)
          table.header:css('width', wid .. '%')
        end
        for k=1, #table.rows do
          if k ~= 1 then (hTr or cTr):node(spaceH(rows)) end
          if hTr then if table.rows[k].header then
            hTr:node(table.rows[k].header)
          else
            hTr:tag('td')
          end end
          table.rows[k].content:css('width', wid .. '%')
          cTr:node(table.rows[k].content)
        end
      else
        local cols = 1 + (table.header and 1 or 0) + (gHeadEx and 1 or 0)
        for k=1, #table.rows do
          contentcols = 1
          local tr = mw.html.create('tr')
          if k ~= 1 then blocktable:node(spaceV()) end
          blocktable:node(tr)
          if k==1 and table.header then
            tr:node(table.header):node(spaceH((#table.rows*2)-1))
            table.header:attr('rowspan', (#table.rows*2)-1)
          end
          if table.rows[k].header then
            tr:node(table.rows[k].header):node(spaceH())
          else contentcols = contentcols + 1
          end
          tr:node(table.rows[k].content)
          table.rows[k].content
            :attr('colspan', (contentcols*2) - 1)
            :addClass(((table.header or table.rows[k].header) and 'with' or 'no') .. 'groups')
        end
      end
      
    end
  end

--Converts Lua to HTML for below.
  if(nav.below) then
    contents:node(spaceV())
    :tag('tr'):tag('td'):addClass('abovebelow ' .. (nav.below.class and (nav.below.class .. 'a') or (nav.class .. 'b'))):wikitext(nav.below.text)
  end
  return nav.nested and tostring(main) or tostring(container)
end

return Navbox
Advertisement