Module:CargoboxBuilder

From Sekaipedia

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

local navbar    = require('Module:Navbar')._navbar
local cargo     = mw.ext.cargo

local CargoboxBuilder = {}
CargoboxBuilder.__index = CargoboxBuilder
CargoboxBuilder.__tostring = CargoboxBuilder.tostring

local tagmap = {
	th = 'th',
	td = 'td',
	argth = 'th',
	argtd = 'td'
}

--- Create the infobox
-- @return obj metatable
--             A metatable describing the infobox
function CargoboxBuilder.new()
	local obj = setmetatable({
		name = '',
		cargo_table = '',
		cargo_fields = {},
		cargo_where = '',
		params = {},
		param_names = {},
		raw_vals = {},
		cargo_vals = {},
		proc_vals = {},
		layout = {},
		infobox = nil,
		infobox_css = {},
	}, CargoboxBuilder)

	return obj
end

--- Set the infobox name, for use with navbar links
-- @param arg string
--            Name of the template, not nil or empty
-- @return self
--         The current object
function CargoboxBuilder:setName(arg)
	if arg == nil or arg == '' then
		error("Template name must not be nil or empty")
	end
	
	self.name = arg
	
	return self
end

--- Set the width of the infobox
-- @param arg string
--            Width of the infobox, should be a valid value for the CSS "width" 
--            property, not nil or empty
-- @return self
--         The current object
function CargoboxBuilder:setWidth(arg)
	if arg == nil or arg == '' then
		error("Width must not be nil or empty")
	end
	
	self.infobox_css['width'] = arg
	
	return self
end

--- Sets the infobox params
-- @param ... {{ name, func, default, should_hide }, ...}
--        name    string
--                The name of the parameter, not nil, cannot be duplicate
--        func    function, table or string
--                A function that accepts the parameter as an argument and
--                returns a string, OR
--                A table that has the parameter as a key, OR
--                An empty string
--        default string or nil
--                The default value if no argument is given
-- @return self
--         The current object
function CargoboxBuilder:setParams(...)
	for i, v in ipairs(...) do
		if v.name == nil or v.name == "" then
			error("name must not be nil or empty")
		end
		if self.param_names[v.name] then
			error("name cannot be duplicate")
		end
		
		if v.field ~= nil and v.field ~= '' then
			table.insert(self.cargo_fields, v.field)
		end
		
		self.params[v.name] = {
			['type'] = type(v.func),
			func = v.func,
			field = v.field,
			default = v.default
		}
		table.insert(self.param_names, v.name)
	end
	
	return self
end

--- Sets the infobox arguments
-- @param args Frame
--             A frame object, passed in when invoked
-- @return self
--         The current object
function CargoboxBuilder:setArgs(args)
	for k,v in pairs(args) do
		if v ~= '' then
			self.raw_vals[k] = v
		end
	end
	
	return self
end

function CargoboxBuilder:setTable(table_name)
	self.cargo_table = table_name or ''
	
	return self
end

function CargoboxBuilder:setWhere(where)
	self.cargo_where = where or ''
	
	return self
end

--- Gets the raw argument values passed
-- @return args table
--              A table containing the args
function CargoboxBuilder:getRawVals()
	return self.raw_vals
end

--- Gets the cargo argument values
-- @return args table
--              A table containing the args
function CargoboxBuilder:getCargoVals()
	return self.cargo_vals
end

--- Gets the argument values after being processed
-- @return args table
--              A table containing the args
function CargoboxBuilder:getProcessedVals()
	return self.proc_vals
end

--- Gets the argument values of the table, for either raw or processed
-- @param which string
--              A string that determines which argument values to return
-- @retun args table
--             A table containing the args
function CargoboxBuilder:getVals(which)
	if which == 'raw' then
		return self:getRawVals()
	elseif which == 'cargo' then
		return self:getCargoVals()
	elseif which == 'processed' then
		return self:getProcessedVals()
	end
	
	return {}
end

function CargoboxBuilder:addHeader(cols, options)
	table.insert(self.layout, {
		type = 'header',
		cols = cols,
		options = options
	})

	return self
end

function CargoboxBuilder:addImage(cols, options)
	table.insert(self.layout, {
		type = 'image',
		cols = cols,
		options = options
	})

	return self
end

function CargoboxBuilder:addRow(cols, options)
	table.insert(self.layout, {
		type = 'row',
		cols = cols,
		options = options
	})

	return self
end

--- Gets the content associated with a parameter
-- @param param_name string
--                   The param name, not nil or empty
-- @return content string
--                 A string containing the content
function CargoboxBuilder:getContent(param_name)
	if param_name == nil or param_name == "" then
		error("Param must not be nil or empty")
	end
	
	if self.proc_vals[param_name] then
		return self.proc_vals[param_name]
	end
	
	local content = nil
	local current_param = self.params[param_name]
	
	if current_param == nil then
		error(string.format("No such param with name: %s", param_name))
	end
	
	local cargo_value = self.cargo_vals[current_param['field']]
	if cargo_value == '' then
		cargo_value = nil
	end
	
	local raw_param_value = self.raw_vals[param_name] or 
		cargo_value or
		current_param.default
	
	if raw_param_value == nil then
		return raw_param_value
	end
	
	if current_param['type'] == 'function' then
		content = current_param.func(raw_param_value)
	elseif current_param['type'] == 'table' then
		content = current_param.func[raw_param_value]
	else
		content = raw_param_value
	end
	
	self.proc_vals[param_name] = content
	
	return content
end

--- Determines if the row should be shown based of a list of param names
-- @param param_names { param_name, ... }
--        param_name string
--                   The param name
-- @return should_show boolean
function CargoboxBuilder:shouldShow(param_names)
    if param_names and #param_names > 0 then
    	local actual_values = {}
	    for i,v in ipairs(param_names) do
	        table.insert(actual_values, self:getContent(v))
	    end
        
        if #actual_values == 0 then
            return false
        end
    end
    
    return true
end

--- Adds a header
-- @param arg { content, attr, colspan, rowspan, css }
--        content string or nil
--                The wikitext to be rendered
--        attr    {...} or nil 
--                The attributes of the cell in table form
--        colspan number or nil
--                The colspan of the cell
--        roswpan number or nil
--                The rowspan of the cell
--        css     {...} or nil
--                The css of the cell in table form
-- @param options { hideIfEmpty, subheader }
--        hideIfEmpty { param_name, ... }
--        param_name string
--                   The param_name that will be used to check if corresponding 
--                   content is nil
--       subheader boolean
--                 Whether the header is a subheader or not
-- @return self
--         The current object
function CargoboxBuilder:generateHeader(arg, options)
	local is_subheader = false
    if options then
        if not self:shouldShow(options.hideIfEmpty) then
        	return nil
        end
		if options.subheader then
			is_subheader = true
		end
    end
	
	local _cell = self.infobox:tag('tr'):tag('th'):attr('colspan', 30)
	_cell:addClass('theme-primary')
	_cell:css('text-align', 'center')
	
	if not is_subheader then
		_cell:css({
			['font-size'] = '1.15em',
			['line-height'] = '1.5em'
		})
	end
	
	if arg.attr then
		_cell:attr(arg.attr)
	end
	if arg.colspan then
		_cell:attr('colspan', arg.colspan)
	end
	if arg.rowspan then
		_cell:attr('rowspan', arg.rowspan)
	end
	if arg.css then
		_cell:css(arg.css)
	end
	
	if arg.tag == 'th' then
		_cell:wikitext(arg.content)
	elseif arg.tag == 'argth' then
		_cell:wikitext(self:getContent(arg.content))
	end
end


--- Adds an image, or switchable images
-- @param cols { { tag, content, title }, ... }
--        tag     "artd" or "td"
--                Whether or not an it is based off a parameter
--        content string
--                The content or the parameter name
--        title   string or nil
--                The title, if using switchable images
-- @param options { hideIfEmpty }
--        hideIfEmpty { param_name, ... }
--        param_name string
--                   The param_name that will be used to check if corresponding 
--                   content is nil
-- @return self
--         The current object
function CargoboxBuilder:generateImage(cols, options)
	if options then
        if not self:shouldShow(options.hideIfEmpty) then
        	return nil
    	end
    end
	
	local _cell = self.infobox:tag('tr')
		:tag('td')
		:css('text-align', 'center')
		:attr('colspan', 30)
	local content = '?'
	
	local actual_args = {}
	for _,v in ipairs(cols) do
		local c = v.content
		if v.tag == 'argtd' then
			c = self:getContent(c)
		end
		
		if c ~= nil then
			table.insert(actual_args, { title = v.title, content = c })
		end
	end
	
	if #actual_args == 0 then
		_cell:tag('span'):addClass('plainlinks')
			:wikitext(
				string.format("[%s?action=edit ? (edit)]",
					tostring(mw.uri.fullUrl(mw.title.getCurrentTitle().text))
				)
			)
		return self
	elseif #actual_args < 2 then
		content = actual_args[1].content
	else
		local t = {}
		for _,v in ipairs(actual_args) do
			table.insert(t, v.title .. '=' .. v.content)
		end
		
		content = mw.getCurrentFrame():callParserFunction({
					name = '#tag',
					args = { 'tabber', table.concat(t, '|-|') }
				})
	end
	
	_cell:wikitext(content)
end


--- Generates a row, with columns up to 30 columns spanned
-- @param cols    { { tag, content, hide, attr, colspan, rowspan, css }, ... }
--        tag       "th", "td", "argth", "argtd"
--                  A string containing one of the above, "th" or "td" uses 
--                  content as the wikitext, "argth" or "argtd" uses content as
--                  the parameter name to produce the suitable content
--        content   string
--                  Content to be used as wikitext or a parameter name
--        attr      {...} or nil
--                  The attributes of the cell in table form
--        colspan   number or nil
--                  The colspan of the cell
--        rowspan   number or nil
--                  The rowspan of the cell
--        css       {...} or nil
--                  The css of the cell in table form
--        param_css { func , ... }
--        func function
--             A function that returns css in table form
-- @param options { hideIfEmpty }
--        hideIfEmpty { param_name, ... }
--        param_name string
--                   The param_name that will be used to check if corresponding 
--                   content is nil
--        default_css {...} or nil
--                    The css of all cells in table form
-- @return self
--         The current object
function CargoboxBuilder:generateRow(cols, options)
	local default_css = nil
    if options then
        if not self:shouldShow(options.hideIfEmpty) then
        	return nil
        end
		if options.default_css then
			default_css = options.default_css
		end
    end
	
	local _row = self.infobox:tag('tr')
	for _,v in ipairs(cols) do
		local _cell = _row:tag(tagmap[v.tag] or 'td')
						:attr('colspan', 30 / #cols)
		
		if v.attr then
			_cell:attr(v.attr)
		end
		if v.colspan then
			_cell:attr('colspan', v.colspan)
		end
		if v.rowspan then
			_cell:attr('rowspan', v.rowspan)
		end
		if default_css then
			_cell:css(default_css)
		end
		if v.css then
			_cell:css(v.css)
		end
		
		if v.tag == 'th' or v.tag == 'td' then
			_cell:wikitext(v.content)
		elseif v.tag == 'argth' or v.tag == 'argtd' then
			local content = self:getContent(v.content)
			if content then
				if v.param_css and #v.param_css > 0 then
					for _,func in ipairs(v.param_css) do
						local cell_css = func(self.raw_vals[v.content])
						if cell_css then
							_cell:css(cell_css)
						end
					end
				end
			
				_cell:wikitext(content)
			else
				local url = mw.uri.fullUrl(mw.title.getCurrentTitle().text)
				if v.cargo_field ~= nil and v.cargo_field ~= '' then
					url = ''
				end
				
				_cell:tag('span'):addClass('plainlinks')
				:wikitext(string.format(
					"[%s?action=edit ? (edit)]",
					tostring(url)
				))
			end
		end
	end
end

--- Generates the infobox
-- @return nil
function CargoboxBuilder:generateInfobox()
	if self.cargo_table ~= '' and
		#self.cargo_fields > 0 and
		self.cargo_where ~= ''  then
			self.cargo_vals = cargo.query(
				self.cargo_table,
				table.concat(self.cargo_fields, ','),
				{ 
					where = self.cargo_where,
					limit = 1
				}
			)[1]
			or {}
	end

	self.infobox = mw.html.create('table'):addClass('infobox')
		:css(self.infobox_css)
	
	local spacer = self.infobox:tag('tr')
	for i=1,30,1 do
		spacer:tag('td')
			:attr('colspan', 1)
			:css('width', 'calc(100% / 30)')
	end
	
	for i,row_layout in ipairs(self.layout) do
		local row_type = row_layout['type']
		local row_cols = row_layout['cols']
		local row_options = row_layout['options']
		
		if row_type == 'header' then
			self:generateHeader(row_cols, row_options)
		elseif row_type == 'subheader' then
			self:generateHeader(row_cols, row_options)
		elseif row_type == 'image' then
			self:generateImage(row_cols, row_options)
		elseif row_type == 'row' then
			self:generateRow(row_cols, row_options)
		end
	end
	
	self:generateRow({
		{
			tag = 'td',
			content = navbar({ self.name, plain = 1, noedit = 1 }),
			colspan = 30,
			css = { ['text-align'] = 'center' }
		}
	})
	
end

--- Returns the infobox as a string
-- @return string
--         The html of the infobox
function CargoboxBuilder:tostring()
	if self.infobox == nil then
		self:generateInfobox()
	end
	
	return tostring(self.infobox)
end


return CargoboxBuilder
Cookies help us deliver our services. By using our services, you agree to our use of cookies.