Module:Map

From Secrets of Grindea Wiki
Jump to navigation Jump to search

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

local p = {}

local func = require('Module:Func')

local function icon(root, index, x, y, link, caption, item)
  -- Assumes that item is either nil, or contains the correct sprite. See the main function

  -- Icon span anchor - sets the position of the elements
  local anchor = root
    :tag('div')
    :css('position', 'absolute')
    :css('left', tostring(x) .. '%')
    :css('top', tostring(y) .. '%')
    :css('pointer-events', 'none')

  -- Icon span offset - shifts the position of the elements so that they're centered on the top-left corner
  -- Image span
  local image = anchor
    :tag('span')
    :css('position', 'absolute')
    :css('pointer-events', 'auto')
    :addClass('pixelart')
  
  local str
  if link then 
    str = 'link=' .. link .. '|'
  else 
    str = 'link=|'
  end
  if item then
    str = '[[Image:MapChest.png|' .. str
    image:css('transform', 'translate(-50%, -50%) scale(1.3,1.3)')
  else
    str = '[[Image:circle.png|' .. str
    image:css('transform', 'translate(-50%, -50%)')
  end
  image:wikitext(str .. caption .. ']]')

  -- Inner content - text or sprite
  local content = anchor
    :tag('span')  
    :css('position', 'absolute')
    :css('transform', 'translate(-50%, -50%)')
  if item then
    content
      :addClass('pixelart')
      :wikitext('[[Image:' ..item.. '|link=|25x25px|' ..  caption .. ']]')
  else
    content
      :css('color', 'white')
      :css('font-weight', 'bold')
      :wikitext(tostring(index))
  end
end

local nil_if_empty = func.nil_if_empty
local limit_range  = func.limit_range
local is_empty     = func.is_empty

function p.main(frame)
  local args   = frame.args

  local image  = nil_if_empty(args['image']) or 'Placeholder.png'
  local width  = tonumber(args['imageWidth']) or 64
  local height = tonumber(args['imageHeight']) or 64
  local anchor = nil_if_empty(args['anchor'])
  local legend = nil_if_empty(args['legend']) or 'Locations'
  local groups = limit_range(1, 5, tonumber(args['groups']) or 3)

  local offsetX = tonumber(args['imageStartX']) or 0
  local offsetY = tonumber(args['imageStartY']) or 0

  -- Doesn't really work as I'd like it to, but it will have to do for now
  local imageScale = limit_range(0.1, 5, tonumber(args['imageScale']) or 1)

  width  = width * imageScale
  height = height * imageScale

  -- Specified in %, refers to max-width
  local containerWidth = limit_range(0, 100, tonumber(args['containerWidth']) or 100)

  -- Specified in px
  local containerHeight = limit_range(0, 1080, tonumber(args['containerHeight']) or 720)

  -- Specified in %, refers to width
  local elementWidth = limit_range(0, 90, tonumber(args['elementWidth']) or 90)

  -- Specified in px
  local devContainerHeight = height
  if devContainerHeight > containerHeight then
    devContainerHeight = containerHeight
  end

  -- Other Params
  local positions = {}

  local trueIndex = 1
  for i = 1,32 do
    -- Get the positions
    local arg = 'pos' .. tostring(i)
    if not is_empty(args[arg]) then
      positions[trueIndex] = {
          ['index']   = i,
          ['caption'] = args[arg],
          ['link']    = nil_if_empty(args[arg .. 'link']),
          ['x']       = nil_if_empty(args[arg .. 'x']) or 50,
          ['y']       = nil_if_empty(args[arg .. 'y']) or 50,
          ['item']    = nil_if_empty(args[arg .. 'item'])
        }
      trueIndex = trueIndex + 1
    end
  end

  -- Container for the element
  local root = mw.html.create('div')
  root
    :css('border', '1px #ccc solid')
    :css('width', elementWidth .. '%')
    :css('padding', '8px')
    :css('margin-left', 'auto')
    :css('margin-right', 'auto')
    :css('margin-top', '8px')
    :css('margin-bottom', '8px')

  -- Create anchor
  if anchor ~= nil then
    root
      :attr('id', anchor)
  end

  -- Map container:
  -- Div tag for hiding overflow
  -- Div tag with width and height set to image size (for icon positions)
  -- Span tag for JavaScript position changing

  local container = root
    :tag('div')
    :css('width', containerWidth .. '%')
    :css('max-height', containerHeight .. 'px') -- To fix placement inside containers
    :css('min-height', devContainerHeight .. 'px') -- To fix placement inside containers
    :css('max-width', width .. 'px')
    :css('margin-left', 'auto')
    :css('margin-right', 'auto')
    :css('overflow', 'hidden')
    :css('border', '1px solid blue')
    :css('position', 'relative') -- To fix placement inside containers

    :tag('div')
    :css('position', 'relative')
    :css('left', '0px')
    :css('top', '0px')
    :css('width', width .. 'px')
    :css('height', height .. 'px')
    :css('overflow', 'hidden')
    :css('position', 'absolute') -- To fix placement inside containers
    :addClass('overview-map')


    :tag('span')
    :css('position', 'absolute')
    :css('width', '100%')
    :css('height', '100%')
    :css('left', offsetX .. 'px')
    :css('top', offsetY .. 'px')
    :addClass('overview-map-pos')

  -- Image wrapper

  container
    :tag('span')
    :css('position', 'absolute')
    :css('left', '0px')
    :css('top', '0px')
    :wikitext('[[Image:' .. image .. '|' .. math.floor(width) .. 'x' .. math.floor(height) .. 'px|link=]]')

  -- Create icons
  -- Also processes the item fields and turns them to sprites, or nil, for repeated use in cell content

  for i=1,#positions do
    value = positions[i]
    item = value['item']
    if item ~= nil then
    -- Try to retrieve sprite
      local module = require('Module:Item')
      if module then
        item = module.data({["args"] = {item, "sprite", ["softErrors"] = true}})
        if item == "Error" then
          item = 'Placeholder.png'
        end
      else
        item = nil
      end
    end
    icon(container, value['index'], value['x'], value['y'], value['link'], value['caption'], item)
    value['item'] = item
  end

  -- Create notice about drag feature
  root
    :tag('div')
    :css('margin-left', 'auto')
    :css('margin-right', 'auto')
    :css('margin-top', '4px')
    :css('margin-bottom', '8px')
    :css('text-align', 'center')
    :css('font-style', 'italic')
    :wikitext('You can move the map around by dragging')

  -- Create legend 

  local legendTable = root
    :tag('div')
    :css('margin', '0px')
    :css('padding', '16px 0px 16px 0px')
    :tag('table')
    :css('border', '2px solid black')
    :css('width', '100%')
    :css('line-height', '1.0')
    :css('table-layout', 'fixed')

  -- Legend title ( wikitext bold styling is escaped)

  legendTable
    :tag('tr')
    :tag('td')
    :attr('colspan', tostring(groups))
    :css('text-align', 'center')
    :wikitext('\'\'\'' .. legend .. '\'\'\'')


  -- Contents (balanced across groups)
  -- Will not generate more cells than needed

  local current = 0

  for i=1,#positions do
    value = positions[i]
    if current == 0 then
      legendTable = legendTable
        :tag('tr')
    end

    local cellContent


    if value['item'] ~= nil then
      -- Use sprite
      cellContent = frame:expandTemplate{ title = 'ItemLink', args = { image = value['item'], label = value['caption'], article = value['link'] } }
    else
      cellContent = '\'\'\'' .. value['index'] .. '\'\'\'. '
      if value['link'] ~= nil then
        cellContent = cellContent .. '[[' .. value['link'] .. '|' .. value['caption'] .. ']]'
      else
        cellContent = cellContent .. value['caption']
      end
    end

    legendTable
      :tag('td')
      :css('vertical-align', 'top')
      :wikitext(cellContent)

    current = current + 1
    if current >= groups then
      legendTable = legendTable
        :done()
      current = 0
    end
  end
  
  return tostring(root)
end

return p