Jump to content

Module:UI: Difference between revisions

From Horizon's End Wiki
m 1 revision imported
No edit summary
 
Line 1: Line 1:
local slot = require( [[Module:Inventory slot]] ).slot
local moduleSlot = require([[Module:Inventory slot]])
local addSlot = function( args, item, prefix, class, default )
local slot = moduleSlot.slot
 
local p = {}
 
local function addSlot( args, item, prefix, class, default )
local none, nostacksize
local none, nostacksize
prefix = prefix or ''
prefix = prefix or ''
Line 14: Line 18:
end
end


function experience_class(exp_value)
local function experience_class(exp_value)
 
local MINMAX = {
local MINMAX = {
  {-32768, 2},
  {-32768, 2},
Line 46: Line 49:
local n = assert( tonumber(exp_value), "Module:UI: Experience value is not a number (" .. exp_value .. ")" )
local n = assert( tonumber(exp_value), "Module:UI: Experience value is not a number (" .. exp_value .. ")" )
local idx = 2; exp = tonumber( exp_value )
local idx, exp = 2, tonumber( exp_value )
if exp <= MINMAX[#MINMAX][2] then
if exp <= MINMAX[#MINMAX][2] then
  for i = 1, #MINMAX do
  for i = 1, #MINMAX do
Line 58: Line 61:
return tostring( EXP_IMGS[idx] )
return tostring( EXP_IMGS[idx] )
end
end
local p = {}


-- Crafting table
-- Crafting table
Line 76: Line 77:
local row = input:tag( 'span' ):addClass( 'mcui-row' )
local row = input:tag( 'span' ):addClass( 'mcui-row' )
for _, letter in ipairs{ 'A', 'B', 'C' } do
for _, letter in ipairs{ 'A', 'B', 'C' } do
row:wikitext( addSlot( args, letter .. num, 'I' ) )
row:wikitext( addSlot( args, letter .. num, letter .. num ) )
end
end
end
end
local arrow = body:tag( 'span' ):addClass( 'mcui-arrow' ):tag( 'br' ):done()
local arrow = body:tag( 'span' ):addClass( 'mcui-arrow' ):tag( 'br' ):done()
if args.arrow or '' ~= '' then
arrow:css(
'background-image',
'{{FileUrl|' .. args.arrow .. ' (' .. args.Mod .. ').png}}'
)
end
body
body
Line 137: Line 132:
input:wikitext( addSlot( args, 'Input', 'I' ) )
input:wikitext( addSlot( args, 'Input', 'I' ) )
local fuel = input:tag( 'span' ):addClass( 'mcui-fuel' ):tag( 'br' ):done()
local fuel = input:tag( 'span' ):addClass( 'mcui-fuel' ):tag( 'br' ):done()
local fuelImg = args.FuelUsage or ''
local burning = args.Input or '' ~= '' and args.Fuel or '' ~= ''
local burning = args.Input or '' ~= '' and args.Fuel or '' ~= ''
if not burning then
if not burning then
fuel:addClass( 'mcui-inactive' )
fuel:addClass( 'mcui-inactive' )
if fuelImg ~= '' then
fuelImg = fuelImg .. ' (in-active)'
end
end
if fuelImg ~= '' then
fuel:css(
'background-image',
'{{FileUrl|' .. fuelImg .. ' (' .. args.Mod .. ').png}}'
)
end
end
input:wikitext( addSlot( args, 'Fuel', 'F' ) )
input:wikitext( addSlot( args, 'Fuel', 'F' ) )
local arrow = body:tag( 'span' ):addClass( 'mcui-arrow' ):tag( 'br' ):done()
local arrow = body:tag( 'span' ):addClass( 'mcui-arrow' ):tag( 'br' ):done()
local arrowImg = args.Progress or ''
if not burning or ( args.Output or '' ) == '' then
if not burning or ( args.Output or '' ) == '' then
arrow:addClass( 'mcui-inactive' )
arrow:addClass( 'mcui-inactive' )
if arrowImg ~= '' then
arrowImg = arrowImg .. ' (in-active)'
end
end
if arrowImg ~= '' then
arrow:css(
'background-image',
'{{FileUrl|' .. arrowImg .. ' Progress (' .. args.Mod .. ').png}}'
)
end
end
Line 177: Line 153:
-- Converts commas to dots, removes all spaces and splits the arguments with semicolon.
-- Converts commas to dots, removes all spaces and splits the arguments with semicolon.
local split = mw.text.split(string.gsub(args.Experience, '[ ,]', {[' '] = '', [','] = '.'}), ';', true)
local split = mw.text.split(string.gsub(args.Experience, '[ ,]', {[' '] = '', [','] = '.'}), ';', true)
local animated = body:tag('span'):attr('title', 'XP reward. If there is a fractional part, it means the recipe has a chance equal to the fractional part of rewarding an additional XP point.')
local animated = body:tag('span')
:addClass('animated mcui-experience')
:attr('title', 'XP reward. If there is a fractional part, it means the recipe has a chance equal to the fractional part of rewarding an additional XP point.')
:addClass('animated mcui-experience')
local isNotFirst = true
local isNotFirst = true
for i, v in ipairs(split) do
for i, v in ipairs(split) do
assert(tonumber(v), 'Module:UI: "' .. v .. '" is not a valid number')
local xp = tonumber(v)
local xp = tonumber(v)
assert(xp, 'Module:UI: "' .. v .. '" is not a valid number')
local xpWrapper = animated:tag('span')
local xpWrapper = animated:tag('span')
:addClass(isNotFirst and 'animated-active' or nil)
:addClass(isNotFirst and 'animated-active' or nil)
Line 255: Line 232:
local arrow = body:tag( 'span' ):addClass( 'mcui-stonecutterArrow' )
local arrow = body:tag( 'span' ):addClass( 'mcui-stonecutterArrow' )
if (args.arrow or '') ~= '' then
arrow:wikitext( addSlot( args, 'Output', '', 'invslot-plain mcui-stonecutterSprite' ) )
arrow:css(
'background-image',
'{{FileUrl|' .. args.arrow .. ' (' .. args.Mod .. ').png}}'
)
end
arrow:wikitext( addSlot( args, 'Output', '', 'invslot-plain mcui-stonecutterSprite' ) )
body
body
Line 294: Line 265:
tapestry:tag( 'span' ):tag( 'br' ):done()
tapestry:tag( 'span' ):tag( 'br' ):done()
local arrow = body:tag( 'span' ):addClass( 'mcui-loomArrow' )
local arrow = body:tag( 'span' ):addClass( 'mcui-loomArrow' )
if args.arrow or '' ~= '' then
arrow:css(
'background-image',
'{{FileUrl|' .. args.arrow .. ' (' .. args.Mod .. ').png}}'
)
end
local bannerSprite
local bannerSprite
if args.Sprite and #args.Sprite>0 then
if args.Sprite and #args.Sprite > 0 then
local animate = require( [[Module:AnimateSprite]] ).animate
local animate = require( [[Module:AnimateSprite]] ).animate
bannerSprite = animate{
bannerSprite = animate{
Line 341: Line 306:
local arrow = body:tag( 'span' ):addClass( 'mcui-arrow' )
local arrow = body:tag( 'span' ):addClass( 'mcui-arrow' )
if args.arrow or '' ~= '' then
arrow:css(
'background-image',
'{{FileUrl|' .. args.arrow .. ' (' .. args.Mod .. ').png}}'
)
end
body
body
Line 357: Line 316:


-- Smithing Table
-- Smithing Table
function p.smithing (f)  
function p.smithing(f)  
local args = f
local args = f
if f == mw.getCurrentFrame() then
if f == mw.getCurrentFrame() then
args = f:getParent().args
args = f:getParent().args
else
else
f = mw.getCurrentFrame()
f = mw.getCurrentFrame()
end
end
Line 378: Line 337:
margin: 3px 18px;
margin: 3px 18px;
vertical-align: top;]])
vertical-align: top;]])
if args.arrow or '' ~= '' then
arrow:css(
if args.crossed then
'background-image',
arrow:addClass('mcui-inactive')
'{{FileUrl|' .. args.arrow .. ' (' .. args.Mod .. ').png}}'
)
end
end
if args.crossed then arrow:addClass('mcui-inactive') end
body
body
Line 396: Line 351:


-- Anvil
-- Anvil
function p.anvil (f)  
function p.anvil(f)  
local args = f
local args = f
if f == mw.getCurrentFrame() then
if f == mw.getCurrentFrame() then
args = f:getParent().args
args = f:getParent().args
else
else
f = mw.getCurrentFrame()
f = mw.getCurrentFrame()
end
end


Line 410: Line 365:
width: 60px;
width: 60px;
height: 60px;]])
height: 60px;]])
                   
local title = body:tag( 'span' ):addClass('mcui-Anvil-inputbox mcui-input')
local inputbox = body:tag( 'span' ):addClass('mcui-Anvil-inputbox mcui-input')
local itemName
local itemName = mw.text.trim(args.title or '')
if args.Input1 and #args.Input1>0 then
if itemName ~= '' then
local animate = require( [[Module:AnimateText]] ).animate
-- Manual title override
itemName = animate{
itemName = itemName:gsub(';', ',,')
args.Input1:gsub('Damaged ', ''):gsub( ';' , ',,' )
elseif args.Input1 and #args.Input1 > 0 then
    }
-- Extract the item names from the first input slot.
-- The name for each item either
-- 1) the overridden title (in square brackets) or
-- 2) the item name without "Damaged" prefix.
itemName = {}
if args.parsed then
-- pre-parsed slot parameters, args.Input1 is a table
for _, slot in ipairs(args.Input1) do
if slot[1] then
error("Slot subframes are not supported by the anvil UI")
end
table.insert(itemName, slot.title or slot.name:gsub(moduleSlot.i18n.prefixes.damaged .. ' ', ''))
end
else
for _, slot in ipairs(moduleSlot.splitOnUnenclosedSemicolons(args.Input1)) do
local slotTitle = slot:match('%[([%w%s]-)%][%w%s]+')
if not slotTitle then
slotTitle = slot:gsub(moduleSlot.i18n.prefixes.damaged .. ' ', '')
end
table.insert(itemName, slotTitle)
end
end
end
if itemName ~= '' then
itemName = require( [[Module:AnimateText]] ).animate{ itemName }:gsub( 'class="animated"', 'class="mcui-Anvil-inputtext animated"' )
else
else
title:addClass( 'mcui-inactive' )
inputbox:addClass( 'mcui-inactive' )
itemName = '<br>'
itemName = '<br>'
end
end
title:wikitext( ( itemName:gsub( 'class="animated"', 'class="mcui-Anvil-inputtext animated"' ) ) )
inputbox:wikitext( ( itemName ) )
local anvilA = body:tag( 'span' ):addClass( 'mcui-anvil' ):css('margin-left', '18px')
local anvilA = body:tag( 'span' ):addClass( 'mcui-anvil' ):css('margin-left', '18px')
Line 443: Line 423:
margin: 4px 18px;
margin: 4px 18px;
vertical-align: top;]])
vertical-align: top;]])
if args.arrow or '' ~= '' then
arrow:css(
'background-image',
'{{FileUrl|' .. args.arrow .. ' (' .. args.Mod .. ').png}}'
)
end
if args.crossed then
if args.crossed then
Line 480: Line 454:
end
end


function p.legacySmithing (f)  
function p.legacySmithing(f)  
local args = f
local args = f
if f == mw.getCurrentFrame() then
if f == mw.getCurrentFrame() then
args = f:getParent().args
args = f:getParent().args
else
else
f = mw.getCurrentFrame()
f = mw.getCurrentFrame()
end
end


Line 509: Line 483:
margin: 4px 18px;
margin: 4px 18px;
vertical-align: top;]])
vertical-align: top;]])
if args.arrow or '' ~= '' then
arrow:css(
if args.crossed then
'background-image',
arrow:addClass('mcui-inactive')
'{{FileUrl|' .. args.arrow .. ' (' .. args.Mod .. ').png}}'
)
end
end
if args.crossed then arrow:addClass('mcui-inactive') end
body
body
Line 523: Line 493:
:wikitext( addSlot( args, 'Output', 'O', 'invslot' ) )
:wikitext( addSlot( args, 'Output', 'O', 'invslot' ) )


return tostring( mw.html.create( 'div' ):node( body ) )
end
function p.cartography( f )
local args = f
if f == mw.getCurrentFrame() then
args = f:getParent().args
else
f = mw.getCurrentFrame()
end
local body = mw.html.create( 'span' ):addClass( 'mcui mcui-Cartography_Table pixel-image' )
local input = body:tag( 'span' ):addClass( 'mcui-input' )
input:wikitext( addSlot( args, 'Input1', 'I1' ) )
local plus = input:tag( 'span' ):addClass( 'mcui-plus' ):tag( 'br' ):done()
input:wikitext( addSlot( args, 'Input2', 'I2' ) )
local arrow = body:tag( 'span' ):addClass( 'mcui-arrow' ):tag( 'br' ):done()
body
:tag( 'span' )
:addClass( 'mcui-output' )
:wikitext( addSlot( args, 'Output', 'O', 'invslot-large' ) )
return tostring( mw.html.create( 'div' ):node( body ) )
return tostring( mw.html.create( 'div' ):node( body ) )
end
end


return p
return p

Latest revision as of 14:53, 27 September 2026

This template was ported from https://minecraft.wiki. It is licensed under CC BY-NC-SA 3.0

This module implements common UI templates, to avoid overhead from excessive #invoke calls.

Dependencies


local moduleSlot = require([[Module:Inventory slot]])
local slot = moduleSlot.slot

local p = {}

local function addSlot( args, item, prefix, class, default )
	local none, nostacksize
	prefix = prefix or ''
	if #prefix == 0 then
		none = 'none'
		nostacksize = ((item == '' or nil) and '') or (args and args[item] and args[item]:gsub( '[,%d]', '' ) or '')
	end
	return slot{
		nostacksize or args[item], mod = args.Mod, link = none or args[prefix .. 'link'],
		title = none or args[prefix .. 'title'], class = class, default = default,
		parsed = args.parsed
	}
end

local function experience_class(exp_value)
	local MINMAX = {
	  {-32768, 2},
	  {3, 6},
	  {7, 16},
	  {17, 36},
	  {37, 72},
	  {73, 148},
	  {149, 306},
	  {307, 616},
	  {617, 1236},
	  {1237, 2476},
	  {2477, 32767}
	}

	local EXP_IMGS = {
		"xp-2",
		"xp-6",
		"xp-16",
		"xp-36",
		"xp-72",
		"xp-148",
		"xp-306",
		"xp-616",
		"xp-1236",
		"xp-2476",
		"xp-32767",
	}
	
	local n = assert( tonumber(exp_value), "Module:UI: Experience value is not a number (" .. exp_value .. ")" )
	
	local idx, exp = 2, tonumber( exp_value )
	if exp <= MINMAX[#MINMAX][2] then
	  for i = 1, #MINMAX do
		if MINMAX[i][1] < exp and MINMAX[i][2] >= exp then
		  idx = i
		  break
		end
	  end
	end
	
	return tostring( EXP_IMGS[idx] )
end

-- Crafting table
function p.craftingTable( f )
	local args = f
	if f == mw.getCurrentFrame() then
		args = f:getParent().args
	else
		f = mw.getCurrentFrame()
	end
	
	local body = mw.html.create( 'span' ):addClass( 'mcui mcui-Crafting_Table pixel-image' )
	
	local input = body:tag( 'span' ):addClass( 'mcui-input' )
	for num = 1, 3 do
		local row = input:tag( 'span' ):addClass( 'mcui-row' )
		for _, letter in ipairs{ 'A', 'B', 'C' } do
			row:wikitext( addSlot( args, letter .. num, letter .. num ) )
		end
	end
	
	local arrow = body:tag( 'span' ):addClass( 'mcui-arrow' ):tag( 'br' ):done()
	
	body
		:tag( 'span' )
			:addClass( 'mcui-output' )
			:wikitext( addSlot( args, 'Output', 'O', 'invslot-large' ) )
	
	local shapeless = args.shapeless or ''
	local fixed = args.fixed or ''
	if shapeless ~= '' or fixed ~= '' then
		local icon = body:tag( 'span' )
			:addClass( 'mcui-icons' )
			:tag( 'span' )
				:tag( 'br' )
			:done()
		if shapeless ~= '' then
			icon:addClass( 'mcui-shapeless' )
				:attr( 'title',
					'This recipe is shapeless; the inputs may be placed in any arrangement in the crafting grid.'
				)
		elseif fixed ~= '' then
			local notFixed = args.notfixed or ''
			local exceptFixed = ''
			if notFixed ~= '' then
				exceptFixed = '; except for ' .. notFixed .. ', which can go anywhere'
			end
			
			icon:addClass( 'mcui-fixed' )
				:attr( 'title',
					'This recipe is fixed; the input arrangement may not be moved or mirrored in the crafting grid' .. exceptFixed .. '.'
				)
		end
	end
	
	return tostring( mw.html.create( 'div' ):node( body ) )
end

-- Furnace
function p.furnace( f )
	local args = f
	if f == mw.getCurrentFrame() then
		args = f:getParent().args
	else
		f = mw.getCurrentFrame()
	end
	
	local body = mw.html.create( 'span' ):addClass( 'mcui mcui-Furnace pixel-image' )
	
	local input = body:tag( 'span' ):addClass( 'mcui-input' )
	input:wikitext( addSlot( args, 'Input', 'I' ) )
	local fuel = input:tag( 'span' ):addClass( 'mcui-fuel' ):tag( 'br' ):done()
	local burning = args.Input or '' ~= '' and args.Fuel or '' ~= ''
	if not burning then
		fuel:addClass( 'mcui-inactive' )
	end
	
	input:wikitext( addSlot( args, 'Fuel', 'F' ) )
	
	local arrow = body:tag( 'span' ):addClass( 'mcui-arrow' ):tag( 'br' ):done()
	if not burning or ( args.Output or '' ) == '' then
		arrow:addClass( 'mcui-inactive' )
	end
	
	body
		:tag( 'span' )
			:addClass( 'mcui-output' )
			:wikitext( addSlot( args, 'Output', 'O', 'invslot-large' ) )
			
	args.Experience = args.Experience or ''
	if args.Experience ~= '' then
		-- Converts commas to dots, removes all spaces and splits the arguments with semicolon.
		local split = mw.text.split(string.gsub(args.Experience, '[ ,]', {[' '] = '', [','] = '.'}), ';', true)
		local animated = body:tag('span')
				:attr('title', 'XP reward. If there is a fractional part, it means the recipe has a chance equal to the fractional part of rewarding an additional XP point.')
				:addClass('animated mcui-experience')
		local isNotFirst = true
		for i, v in ipairs(split) do
			local xp = tonumber(v)
			assert(xp, 'Module:UI: "' .. v .. '" is not a valid number')
			local xpWrapper = animated:tag('span')
					:addClass(isNotFirst and 'animated-active' or nil)
			xpWrapper:tag('span')
					:addClass(experience_class(xp) .. ' mcui-experience-orb')
			xpWrapper:tag('span')
					:addClass('mcui-experience-text')
					:wikitext(('&nbsp;' and xp > 72 or '') .. xp)
			isNotFirst = false
		end
	end
	
	return tostring( mw.html.create( 'div' ):node( body ) )
end

-- Brewing Stand
function p.brewingStand( f )
	local args = f
	if f == mw.getCurrentFrame() then
		args = f:getParent().args
	else
		f = mw.getCurrentFrame()
	end
	
	local body = mw.html.create( 'span' ):addClass( 'mcui mcui-Brewing_Stand pixel-image' )
	
	local input = body:tag( 'span' ):addClass( 'mcui-input' )
	input:tag( 'span' ):addClass( 'mcui-bubbling' ):tag( 'br' )
	input:wikitext( addSlot( args, 'Input', 'I' ) )
	input:tag( 'span' ):addClass( 'mcui-arrow' ):tag( 'br' )
	if ( args.Input or '' ) == '' or
		( ( args.Output1 or '' ) == '' and ( args.Output2 or '' ) == '' and ( args.Output3 or '' ) == '' )
	then
		input:addClass( 'mcui-inactive' )
	end
	
	body
		:tag( 'span' )
			:addClass( 'mcui-paths' )
			:tag( 'br' )
	
	if ( ( args.Base1 or '' ) ~= '' or ( args.Base2 or '' ) ~= '' or ( args.Base3 or '' ) ~= '' ) then
		local base = body:tag( 'span' ):addClass( 'mcui-base' )
		local arrows = body:tag( 'span' ):addClass( 'mcui-arrowDown' )
		for i = 1, 3 do
			base:wikitext( addSlot( args, 'Base' .. i, 'B' .. i, 'mcui-base' .. i ) )
			arrows:tag( 'span' ):addClass( 'mcui-arrowDown' .. i ):tag( 'br' )
		end
	end
	
	local output = body:tag( 'span' ):addClass( 'mcui-output' )
	for i = 1, 3 do
		output:wikitext( addSlot( args, 'Output' .. i, 'O' .. i, 'mcui-output' .. i ) )
	end
	
	return tostring( mw.html.create( 'div' ):node( body ) )
end

-- Stonecutter
function p.stonecutter( f )
	local args = f
	if f == mw.getCurrentFrame() then
		args = f:getParent().args
	else
		f = mw.getCurrentFrame()
	end
	
	local body = mw.html.create( 'span' ):addClass( 'mcui mcui-Stonecutter pixel-image' )
	
	local input = body:tag( 'span' ):addClass( 'mcui-input' )
	input:wikitext( addSlot( args, 'Input', 'I' ) )
	
	local arrow = body:tag( 'span' ):addClass( 'mcui-stonecutterArrow' )
	arrow:wikitext( addSlot( args, 'Output', '', 'invslot-plain mcui-stonecutterSprite' ) )
	
	body
		:tag( 'span' )
			:addClass( 'mcui-output' )
			:wikitext( addSlot( args, 'Output', 'O', 'invslot-large' ) )
	
	return tostring( mw.html.create( 'div' ):node( body ) )
end

-- Loom
function p.loom( f )
	local args = f
	if f == mw.getCurrentFrame() then
		args = f:getParent().args
	else
		f = mw.getCurrentFrame()
	end
	
	local body = mw.html.create( 'span' ):addClass( 'mcui mcui-Loom pixel-image' )
	
	local tapestry = body:tag( 'span' ):addClass( 'mcui-tapestry' )
	if args.Banner and #args.Banner>0 then
		tapestry:wikitext( addSlot( args, 'Banner', 'B', 'mcui-inputBanner' ) )
	end
	if args.Dye and #args.Dye>0 then
		tapestry:wikitext( addSlot( args, 'Dye', 'D', 'mcui-inputDye' ) )
	end
	if args.Pattern and #args.Pattern>0 then
		tapestry:wikitext( addSlot( args, 'Pattern', 'P', 'mcui-inputPattern' ) )
	end
	tapestry:tag( 'span' ):tag( 'br' ):done()
	local arrow = body:tag( 'span' ):addClass( 'mcui-loomArrow' )
	
	local bannerSprite
	if args.Sprite and #args.Sprite > 0 then
		local animate = require( [[Module:AnimateSprite]] ).animate
		bannerSprite = animate{
			args.Sprite,
			name = 'SlotSprite'
		}
	else
		bannerSprite = '<br>'
	end
	
	arrow
		:tag( 'span' )
			:addClass( 'mcui-bannerSprite' )
			:wikitext( bannerSprite )
	
	body
		:tag( 'span' )
			:addClass( 'mcui-output' )
			:wikitext( addSlot( args, 'Output', 'O', 'invslot-large' ) )
	
	return tostring( mw.html.create( 'div' ):node( body ) )
end

-- Grindstone
function p.grindstone( f )
	local args = f
	if f == mw.getCurrentFrame() then
		args = f:getParent().args
	else
		f = mw.getCurrentFrame()
	end
	
	local body = mw.html.create( 'span' ):addClass( 'mcui mcui-Grindstone pixel-image' )
	
	local grindstone = body:tag( 'span' ):addClass( 'mcui-grindstone' )
	grindstone:wikitext( addSlot( args, 'Input1', 'I1', 'mcui-input1' ) )
	grindstone:wikitext( addSlot( args, 'Input2', 'I2', 'mcui-input2' ) )
		
	local arrow = body:tag( 'span' ):addClass( 'mcui-arrow' )
	
	body
		:tag( 'span' )
			:addClass( 'mcui-output' )
			:wikitext( addSlot( args, 'Output', 'O', 'invslot-large' ) )
	
	return tostring( mw.html.create( 'div' ):node( body ) )
end

-- Smithing Table
function p.smithing(f) 
	local args = f
	if f == mw.getCurrentFrame() then
		args = f:getParent().args
	else
		f = mw.getCurrentFrame()
	end
	
	local body = mw.html.create( 'span' ):addClass( 'mcui mcui-Smithing_Table pixel-image' )
	
	local smithingTable = body:tag( 'span' ):addClass( 'mcui-smithingTable' )
	smithingTable:wikitext( addSlot( args, 'Input1', 'I1', 'mcui-input1' ) )
	smithingTable:wikitext( addSlot( args, 'Input2', 'I2', 'mcui-input2' ) )
	smithingTable:wikitext( addSlot( args, 'Input3', 'I3', 'mcui-input3' ) )
		
	local arrow = body:tag( 'span' ):addClass( 'mcui-arrow' )
	arrow:cssText([[display: inline-block;
					width: 44px;
					height: 30px;
					margin: 3px 18px;
					vertical-align: top;]])
	
	if args.crossed then
		arrow:addClass('mcui-inactive')
	end
	
	body
		:tag( 'span' )
			:addClass( 'mcui-output' )
			:wikitext( addSlot( args, 'Output', 'O', 'invslot' ) )
	
	return tostring( mw.html.create( 'div' ):node( body ) )
end

-- Anvil
function p.anvil(f) 
	local args = f
	if f == mw.getCurrentFrame() then
		args = f:getParent().args
	else
		f = mw.getCurrentFrame()
	end

	local body = mw.html.create( 'span' ):addClass( 'mcui mcui-Anvil pixel-image' )
	
	local hammer = body:tag( 'span' ):addClass('mcui-hammer')
	hammer:cssText([[display: block;
					 width: 60px;
					 height: 60px;]])
	
	local inputbox = body:tag( 'span' ):addClass('mcui-Anvil-inputbox mcui-input')
	local itemName = mw.text.trim(args.title or '')
	if itemName ~= '' then
		-- Manual title override
		itemName = itemName:gsub(';', ',,')
	elseif args.Input1 and #args.Input1 > 0 then
		-- Extract the item names from the first input slot.
		-- The name for each item either
		-- 1) the overridden title (in square brackets) or
		-- 2) the item name without "Damaged" prefix.
		itemName = {}
		if args.parsed then
			-- pre-parsed slot parameters, args.Input1 is a table
			for _, slot in ipairs(args.Input1) do
				if slot[1] then
					error("Slot subframes are not supported by the anvil UI")
				end
				table.insert(itemName, slot.title or slot.name:gsub(moduleSlot.i18n.prefixes.damaged .. ' ', ''))
			end
		else
			for _, slot in ipairs(moduleSlot.splitOnUnenclosedSemicolons(args.Input1)) do
				local slotTitle = slot:match('%[([%w%s]-)%][%w%s]+')
				if not slotTitle then
					slotTitle = slot:gsub(moduleSlot.i18n.prefixes.damaged .. ' ', '')
				end
				table.insert(itemName, slotTitle)
			end
		end
	end
	
	if itemName ~= '' then
		itemName = require( [[Module:AnimateText]] ).animate{ itemName }:gsub( 'class="animated"', 'class="mcui-Anvil-inputtext animated"' )
	else
		inputbox:addClass( 'mcui-inactive' )
		itemName = '<br>'
	end
	inputbox:wikitext( ( itemName ) )
	
	local anvilA = body:tag( 'span' ):addClass( 'mcui-anvil' ):css('margin-left', '18px')
	anvilA:wikitext( addSlot( args, 'Input1', 'I1', 'mcui-input1' ) )
	
	local plus = body:tag( 'span' ):addClass( 'mcui-plus' )
	plus:cssText([[display: inline-block;
				   width: 26px;
				   height: 26px;
				   margin: 6px 18px;
				   vertical-align: top;]])
	
	local anvilB = body:tag( 'span' ):addClass( 'mcui-anvil' )
	anvilB:wikitext( addSlot( args, 'Input2', 'I2', 'mcui-input2' ) )
		
	local arrow = body:tag( 'span' ):addClass( 'mcui-arrow' )
    arrow:cssText([[display: inline-block;
					width: 44px;
					height: 30px;
					margin: 4px 18px;
					vertical-align: top;]])
	
	if args.crossed then
		arrow:addClass('mcui-inactive')
	end
	
	body
		:tag( 'span' )
			:addClass( 'mcui-output' )
			:css( 'margin-right', '18px' )
			:wikitext( addSlot( args, 'Output', 'O', 'invslot' ) )
	
	if args.cost then
		local cost = body:tag('span'):addClass('mcui-Anvil-cost')
		if args.expensive then
			cost:addClass('mcui-Anvil-cost-expensive')
		else
			cost:addClass('mcui-Anvil-cost-normal')
		end
		
		if args.cost == 'expensive' then
			cost:wikitext('Too expensive')
		else
			cost:wikitext('Enchantment Cost: ' .. args.cost)
		end
	end
	
	local wrapper = mw.html.create( 'div' )
	wrapper:wikitext( require( [[Module:TSLoader]] ).call( "Anvil/styles.css" ) )
	return tostring( wrapper:node( body ) )
end

function p.legacySmithing(f) 
	local args = f
	if f == mw.getCurrentFrame() then
		args = f:getParent().args
	else
		f = mw.getCurrentFrame()
	end

	local body = mw.html.create( 'span' ):addClass( 'mcui mcui-Smithing_Table pixel-image' )
	
	local smithingA = body:tag( 'span' ):addClass( 'mcui-smithingTable' )
	smithingA:wikitext( addSlot( args, 'Input1', 'I1', 'mcui-input1' ) )
	
	local plus = body:tag( 'span' ):addClass( 'mcui-plus' )
	plus:cssText([[display: inline-block;
				   width: 26px;
				   height: 26px;
				   margin: 6px 18px;
				   vertical-align: top;]])
	
	local smithingB = body:tag( 'span' ):addClass( 'mcui-smithingTable' )	
	smithingB:wikitext( addSlot( args, 'Input2', 'I2', 'mcui-input2 invslot-default-smithing' ) )
		
	local arrow = body:tag( 'span' ):addClass( 'mcui-arrow' )
	arrow:cssText([[display: inline-block;
					width: 44px;
					height: 30px;
					margin: 4px 18px;
					vertical-align: top;]])
	
	if args.crossed then
		arrow:addClass('mcui-inactive')
	end
	
	body
		:tag( 'span' )
			:addClass( 'mcui-output' )
			:wikitext( addSlot( args, 'Output', 'O', 'invslot' ) )

	return tostring( mw.html.create( 'div' ):node( body ) )
end

function p.cartography( f )
	local args = f
	if f == mw.getCurrentFrame() then
		args = f:getParent().args
	else
		f = mw.getCurrentFrame()
	end
	
	local body = mw.html.create( 'span' ):addClass( 'mcui mcui-Cartography_Table pixel-image' )
	
	local input = body:tag( 'span' ):addClass( 'mcui-input' )
	input:wikitext( addSlot( args, 'Input1', 'I1' ) )
	local plus = input:tag( 'span' ):addClass( 'mcui-plus' ):tag( 'br' ):done()
	input:wikitext( addSlot( args, 'Input2', 'I2' ) )
	
	local arrow = body:tag( 'span' ):addClass( 'mcui-arrow' ):tag( 'br' ):done()
	
	body
		:tag( 'span' )
			:addClass( 'mcui-output' )
			:wikitext( addSlot( args, 'Output', 'O', 'invslot-large' ) )
	
	return tostring( mw.html.create( 'div' ):node( body ) )
end

return p