Module:UI

-- -- Taken from: https://minecraft.gamepedia.com/Module:UI local getArgs = require('Module:Arguments').getArgs local loader = require('Module:Loader')

local string, table, _, uiText, yesno, inventorySlot, crafting, Interface = loader.require('String', 'Table', 'LibraryUtil', 'UIText', 'Yesno', 'Inventory slot', 'Crafting', 'UI/Core') local rarityAliases = loader.loadData('RarityTier/Aliases')

local aliasesCache = require('Module:Cache').slotAliasesCache

local slot = inventorySlot.slot local parseFrameText = inventorySlot.parseFrameText local formatUIText = uiText._main local curTitle = mw.title.getCurrentTitle

local p = {}

local addSlot = crafting.addSlot

-- Crafting table function p.craftingTable( frame ) local args = getArgs(frame) local body = mw.html.create('span'):addClass('mcui mcui-Crafting_Table pixel-image') local recipe = crafting.parseRecipe(args) local grid = crafting._craftingGrid(recipe) body:node(grid) local arrow = body:tag('span'):addClass('mcui-arrow'):tag('br'):done if args.arrow or  ~=  then arrow:css(			'background-image',			''		) end body:tag('span') :addClass('mcui-output') :wikitext(addSlot(recipe, '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 if curTitle.namespace == 0 and (args.A1 or args.A2 or args.A3 or args.B1 or args.B2 or args.B3 or args.C1 or args.C2 or args.C3) then return tostring(mw.html.create('div'):node(body)) .. ''	end return tostring(mw.html.create('div'):node(body)) end

-- Furnace function p.furnace( frame ) local args = getArgs(frame) 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 fuelImg = args.FuelUsage or '' local burning = args.Input or  ~=  and args.Fuel or  ~=  if not burning then fuel:addClass('mcui-inactive') if fuelImg ~= '' then fuelImg = fuelImg .. ' (in-active)' end end if fuelImg ~= '' then fuel:css(			'background-image',			''		) end input:wikitext(addSlot(args, 'Fuel', 'F')) local arrow = body:tag('span'):addClass('mcui-arrow'):tag('br'):done local arrowImg = args.Progress or '' if not burning or (args.Output or ) ==  then arrow:addClass('mcui-inactive') if arrowImg ~= '' then arrowImg = arrowImg .. ' (in-active)' end end if arrowImg ~= '' then arrow:css(			'background-image',			''		) end body :tag('span') :addClass('mcui-output') :wikitext(addSlot(args, 'Output', 'O', 'invslot-large')) return tostring(mw.html.create('div'):node(body)) end

-- Brewing Stand function p.brewingStand( frame ) local args = getArgs(frame) 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') 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

-- Melody function p.noteSequence(f) local args = getArgs(f) local sequence = args[1] return p._noteSequence(sequence) end

function p._noteSequence(sequence) local function simpleSlot(frames) frames = parseFrameText(frames, nil, false, {aliases = '', default = nil}) local function simpleMakeItem(name) return mw.html.create('span'):addClass('invslot-item invslot-item-image'):wikitext(''):done end local body = mw.html.create('span'):addClass('invslot animated') local activeFrame = 1 for i, frame in ipairs(frames) do			local item if frame[1] then item = body:tag('span'):addClass('animated-subframe') local subActiveFrame = 1 --FC-frame.randomise and random(#frame) or 1 for sI, sFrame in ipairs(frame) do					local sItem = simpleMakeItem(sFrame.name) item:node(sItem) if sI == subActiveFrame then sItem:addClass('animated-active') else sItem:addClass('hidden') end end else item = simpleMakeItem(frame.name) body:node(item) end if i == activeFrame and animated then item:addClass('animated-active') elseif animated then item:addClass('hidden') end end return mw.getCurrentFrame:preprocess(tostring(body)) end local colors = { 'Red', 'Yellow', 'Lime', 'Green', 'Purple', 'Blue', 'Light_Blue' }	local slots = {} for x = 1, 7, 1 do		slots[x] = {} for y = 1, 6, 1 do			if y == 5 then slots[x][y] = sequence:gsub('[' .. x .. ']', 'Block_of_Quartz;') else slots[x][y] = sequence:gsub('[' .. x .. ']', colors[x] .. '_Wool;') end if y == 5 then slots[x][y] = slots[x][y]:gsub('[%d%s]', colors[x] .. '_Stained_Clay;') else slots[x][y] = slots[x][y]:gsub('[%d%s]', colors[x] .. '_Stained_Glass_Pane;') end if y == 5 then for z = 1, y, 1 do slots[x][y] = colors[x] .. '_Stained_Clay;' .. slots[x][y] end for z = 1, 12-y, 1 do slots[x][y] = slots[x][y] .. colors[x] .. '_Stained_Clay;' end else for z = 1, y, 1 do slots[x][y] = colors[x] .. '_Stained_Glass_Pane;' .. slots[x][y] end for z = 1, 12-y, 1 do slots[x][y] = slots[x][y] .. colors[x] .. '_Stained_Glass_Pane;' end end slots[x][y] = slots[x][y]:gsub('[_]', ' ') end end local table = mw.html.create('table'):addClass('mcui mcui-Crafting_Table pixel-image'):css({margin = '0 auto', display = 'table', cursor = 'not-allowed'}) for y = 1, 6, 1 do		local row = table:tag('tr'):addClass('mcui-row') row:tag('td'):wikitext(simpleSlot('Gray Stained Glass Pane')):done for x = 1, 7, 1 do			row:tag('td'):wikitext(simpleSlot(slots[x][y])):done end row:tag('td'):wikitext(simpleSlot('Gray Stained Glass Pane')):done row:done end table:done return tostring(table) end

- -- Template: Collection UI -- -- Creates a main collection UI for a given item - function p.collectionUI(frame) return p._collectionUI(table.deepCopy(getArgs(frame))) end

- -- _collectionUI Helper functions - local recipeAliases = { ['r'] = 'Recipe', ['recipe'] = 'Recipe', ['recipes'] = 'Recipes', ['rs'] = 'Recipes', ['rec'] = 'Recipe', ['recs'] = 'Recipes', ['recip'] = 'Recipe', ['recips'] = 'Recipes', ['res'] = 'Recipes', ['re'] = 'Recipe', ['up'] = 'Upgrade', ['upg'] = 'Upgrade', ['u'] = 'Upgrade', ['upgr'] = 'Upgrade', ['upgra'] = 'Upgrade', ['upgrad'] = 'Upgrade', ['upgrade'] = 'Upgrade', }

-- 1/3 local variateStart = { -- { regex to match, subst } { '[Ee]xperience', '&8+' }, } -- 2/3 local function specialCaseColors(s) local matches = { ['[Mm]inion'] = 'blue', -- ['Enchanted Book'] = 'blue', ['[Uu]pgrade'] = 'green', ['[Cc]oming [Ss]oon'] = 'red', }

for k, v in pairs(matches) do		if s:match(k) then return uiText.getColorCode(v) end end if not s:match("%a") then return uiText.getColorCode('dark_aqua') end end -- 3/3 local variateEnd = { -- { regex to match, subst } { {'[Nn]one', '[Uu]pgrade'}, '' }, { '[Cc][Oo][Mm][Ii][Nn][Gg] [Ss][Oo][Oo][Nn]', ' &8(&4COMING SOON&8)' }, { '[Dd]warven [Ff]orge [Rr]ecipe', ' &6Dwarven Forge Recipe' }, { {'[Mm]inion', '[Rr]ecipes'}, ' &7Recipes' }, { '[Tt]rade', ' &7Trade' }, { '[Ff]oraging', '&7 Foraging Experience' }, { '[Ff]ishing', '&7 Fishing Experience' }, { '[Cc]ombat', '&7 Combat Experience' }, { '[Mm]ining', '&7 Mining Experience' }, { '[Ff]arming', '&7 Farming Experience' } }

local function getStartEnd(_type, expressions) for _,v in ipairs(expressions) do		for _,_exp in ipairs(type(v[1]) == 'table' and v[1] or {v[1]}) do			if not not _type:match(_exp) then return v[2] end end end end

local function parseCollectionData(ui, rewards, collection) local rows = 1 local firstRowPositions = { { {5, 3} },		{ {4, 3}, {6, 3} },		{ {4, 3}, {5, 3}, {6, 3} },		{ {3, 3}, {4, 3}, {6, 3}, {7, 3} },		{ {3, 3}, {4, 3}, {5, 3}, {6, 3}, {7, 3} },		{ {2, 3}, {3, 3}, {4, 3}, {6, 3}, {7, 3}, {8, 3} },		{ {2, 3}, {3, 3}, {4, 3}, {5, 3}, {6, 3}, {7, 3}, {8, 3} },		{ {1, 3}, {2, 3}, {3, 3}, {4, 3}, {6, 3}, {7, 3}, {8, 3}, {9, 3} },	}	local total = #rewards for i, v in ipairs(rewards) do		local x		local y		if i > 21 then break; end if total <= 8 then local pos = firstRowPositions[total][i] x, y = pos[2], pos[1] else x = 2+rows y = i % 9 y = y == 0 and 9 or y		end if i % 9 == 0 and i > 0 then rows = rows+1 end ui:setSlot(x, y, {			i == 1 and 'Yellow Stained Glass Pane,' .. i or 'Red Stained Glass Pane,' .. i,			link = 'none',			title = table.concat{ i == 1 and '&e' or '&c', collection, ' ', string._toRoman(i) },			class = v._goto and 'goto-' .. v._goto,			text = formatUIText(string.dedent			&7Progress: &e0&6%%			&f%s &e0&6/&e%s&7			%s:			%s			&eClick to view rewards!			):format( ('-'):rep(20), string._formatShortNum(string._toNumber(v.amount)), #v > 1 and '&7Rewards' or '&7Reward', table.concat(table.map(v, function(val) local s = val[1] local start_ = val.type and getStartEnd(val.type, variateStart) local end_ = val.type and getStartEnd(val.type, variateEnd) return table.concat{ start_ or '', -- 1/3 val.rarity .. s, -- 2/3 end_ or ' &r' .. (val.type or '&7Recipe'), -- 3/3 }				end), '/') )		})	end end

local function parseTextList(s) local rewards = {} local count = 0 for i, v in ipairs(string.split((s:gsub('\\,', '\255')), '\n')) do		local _type v = string.methods(v):_trim if v:getValue ~= '' then if v:charAt(1) == '*' then if v:charAt(2) == '*' then _type = 'item' else _type = 'amount' end end if _type == 'amount' then local amount, _goto = unpack(string.split(v:sub(2), '%s*,%s*')) count = count+1 rewards[count] = {} rewards[count].amount = amount rewards[count]._goto = _goto and _goto:gsub('^goto%-', '') elseif _type == 'item' then local item, type, rarity = unpack(string.split(v:sub(3), '%s*,%s*')) item = item or v:sub(3) rarity = (rarity and rarity ~= '') and uiText.getFormatting(rarity) local tooltip = aliasesCache:get(item) local tooltipRarity = tooltip and tooltip.title and tooltip.title:match('^&([a-z0-9])') table.push(rewards[count], { 					item, 					rarity = '&' .. (rarity or tooltipRarity or specialCaseColors(item) or 'f'),					type = (type and type ~= '') and (recipeAliases[type:lower] and '&7' .. recipeAliases[type:lower] or string.ucfirst(type))				}) end end end return rewards end - -- function: _collectionUI(args: table|frame) -- -- Invoked by. - function p._collectionUI(args) checkType(1, args, { 'table' }) assertTrue(args[1], 'No collection specified', 2) assertTrue(args[2], 'No collection rewards specified', 2) local slots = {} local collection = args[1] and args[1]:gsub('[Ee][Nn][Cc][Hh][Aa]?[Nn]?[Tt]?[Ee]?[Dd]? ?', ):gsub('[Bb][Ll][Oo][Cc][Kk] ?[Oo]?[Ff]? ?', ) local title = mw.title.getCurrentTitle local ui = Interface(collection and collection .. ' Collection' or title.rootText, args['id'], { 		text = (args['return_text'] or args['goback']),		link = (args['return_text'] or args['goback']) and (args['return_text'] or args['goback']):gsub('%s*[Cc]ollection', ''),		id = args['return_id'],	}, args['hide']) ui:setSlot(1, 5, {		args.title or collection,		link = 'none', 		title = '&e' .. collection .. ' Collection',		text = formatUIText(string.dedent		&7View all your %s Collection 		&7progress and rewards!		&7Total Collected: &e0		:format(collection))	}) ui:setSlot(6, 4, 'link', '') parseCollectionData(ui, parseTextList(args[2]), collection) -- pipeline(ui, table.dump, 0, error) return (tostring(ui):gsub('\255', ',')) end

- -- Template: Collection Rewards UI -- -- Creates a collection Rewards UI. - function p.collectionRewardsUI(frame) local args = getArgs(frame) return p._collectionRewardsUI(table.deepCopy(args)) end - -- function: _collectionRewardsUI(args) -- -- Invoked by - function p._collectionRewardsUI(args) checkType(1, args, { 'table' }) local collection = args[1] and args[1]:gsub('[Ee][Nn][Cc][Hh][Aa]?[Nn]?[Tt]?[Ee]?[Dd]? ?', ):gsub('[Bb][Ll][Oo][Cc][Kk] ?[Oo]?[Ff]? ?', ) local ui = Interface(collection .. ' ' .. string._toRoman(args[2] or 1) .. ' Rewards', args['id'], {		text = collection .. ' Collection',		link = args['return_text'] and args['return_text']:gsub('%s*[Cc]ollection', ''),		id = args['return_id'],	}, args['hide']) local rewards = {} local items = table.filter(string.split(args[3], '\n'), function(v)		return string.trim(v) ~= ''	end) local amount = #items local posistions = { -- X/Y posistion entries for slot amounts 1-9 { {3, 5} },		{ {3, 4}, {3, 6} },		{ {3, 3}, {3, 5}, {3, 7} },		{ {3, 2}, {3, 4}, {3, 6}, {3, 8} },		{ 			{2, 2}, {2, 4}, {2, 6}, {2, 8},			{4, 5},		},		{ 			{2, 2}, {2, 4}, {2, 6}, {2, 8},			{5, 4}, {5, 6},		},		{ 			{2, 2}, {2, 4}, {2, 6}, {2, 8},			{3, 3}, {3, 5}, {3, 7},		},		{ 			{2, 2}, {2, 4}, {2, 6}, {2, 8},			{4, 2}, {4, 4}, {4, 6}, {4, 8},		},		{ 			{2, 4}, {2, 5}, {2, 6},			{3, 4}, {3, 5}, {3, 6},			{4, 4}, {4, 5}, {4, 6},		},		{ 			{2, 4}, {2, 5}, {2, 6}, {2, 7},			{3, 4}, {3, 5}, {3, 6},			{4, 4}, {4, 5}, {4, 6},		},		{ 			{2, 3}, {2, 4}, {2, 5}, {2, 6}, {2, 7},			{3, 4}, {3, 5}, {3, 6},			{4, 4}, {4, 5}, {4, 6},		},	}	for i, line in ipairs(items) do		local item, id, title, desc = pipeline(line, string.trim, 2, string.sub, '%s*,%s*', string.unpackedSplit) local tooltip = aliasesCache:get(item) local itemText = desc or (tooltip and tooltip.text) local itemTitle = title or (tooltip and tooltip.title) local function addItem(x, y)			ui:setSlot(x, y, {				item, text = itemText and itemText .. '//&eClick to view recipe!', 				title = itemTitle, class= id and 'goto-' .. id:gsub('^goto%-', ),				link= id and 'none' or 			}) end addItem(unpack(posistions[amount][i])) end return tostring(ui) end

- -- Template: UIPage -- -- Imports (transclude) another UI page with custom options on its first UI. - function p.uipage(frame) local args = getArgs(frame) local page = args['page'] or args[1] return frame:preprocess(p._uipage(page, args)) end

function p._uipage(page, args) local function injectParamsBoolean(callStr, args) for param, value in pairs(args) do			if value then value = tostring(yesno(value)) callStr, n = callStr:gsub('|%s*' .. param .. '%s*=.-%f[|}]','|' .. param .. '=' .. value .. '\n') if n == 0 then callStr = callStr:sub(0,callStr:len-2) .. '|' .. param .. '=' .. value .. '}}' end end end return callStr end local function injectParamsString(callStr, args) for param, value in pairs(args) do			if value then callStr, n = callStr:gsub('|%s*' .. param .. '%s*=.-%f[|}]','|' .. param .. '=' .. value .. '\n') if n == 0 then callStr = callStr:sub(0,callStr:len-2) .. '|' .. param .. '=' .. value .. '}}' end end end return callStr end local return_id = args['return_id'] local extPageTitle = mw.title.new(page, 0) if not extPageTitle.exists then error('Specified page does not exist, page: ' .. page) end local extPageContent = extPageTitle:getContent local ls = string.matchAll(extPageContent,'%b{}') local finalStr = '' local firstUI = true if ls.n < 1 then return '' end for i = 1, ls.n, 1 do		local callStr = ls[i][1] local x, y = callStr:find('{{UI|') local x1, y1, subpage, subparams = callStr:find('{{UIPage|(.-)%f[|}](.-)}}') if x == 1 then if firstUI then -- first UI on page, apply all the custom settings to this UI				callStr = injectParamsBoolean(callStr, {					hide = yesno(args['hide'],true),					fill = args['fill'], 					rows = args['rows'],					cols = args['cols'],					noarrow = args['noarrow'],					noclose = args['noclose'],				}) callStr = injectParamsString(callStr, {					id = args['id'],					goback = (args['return_text'] or args['goback']),					goback_link = args['goback_link'],					return_id = args['return_id'],					arrow = (args['arrow'] or args['arrow_']),					close_ = (args['close'] or args['close_']),				}) firstUI = false end finalStr = finalStr .. callStr elseif x1 == 1 then subparams = subparams and subparams:gsub('^[|%s]*(.-)[|%s]*$','%1') or '' local subargs = {} for _, param in ipairs(string.split(subparams,'|')) do				if param:match('=') then param = string.split(param, '=') subargs[param[1]] = param[2] else -- no positional arguments for UIPage at the moment end end finalStr = finalStr .. p._uipage(subpage, subargs) end end return finalStr end

- -- Template: UI -- -- Creates a UI. - function p.ui(frame) local args = getArgs(frame, { removeBlanks = false }) local ui = Interface(args[1], args['id'], {		text = (args['return_text'] or args['goback_text'] or args['return'] or args['goback']),		link = (args['return_link'] or args['goback_link']),		id = (args['return_id'] or args['goback_id']),	}, yesno(args['hide'], false), args['fill'], args['rows'], args['cols'], args['noarrow'], args['noclose'], (args['arrow'] or args['arrow_']), (args['close'] or args['close_'])) local x, y, rowCount = 1, 1, 1 local maxRows, maxCols = tonumber(args['rows']) or 6, tonumber(args['cols']) or 9 local fill = yesno(args['fill'], true) local defaultnolink = yesno((args['defaultnolink'] or args['dnl']), false) local function decodeCommas(...) local t = { ... }		for i, v in ipairs(t) do			t[i] = t[i]:gsub('\255', ',') end return unpack(t) end local function setSlot(v, x, y)		local isCustom = v:match('(.-)') v = v:gsub('\\,', '\255') local val, id, title, text = decodeCommas(string.unpackedSplit(v, '%s*,%s*')) local _val = val local val, num = val:match('^%s*(.+)%s*[;]%s*(%d+)%s*$') val = val or _val local link_specify if not id or id == '' then id = nil else local _id = id			id, link_specify = id:match('^%s*(.+)%s*[;]%s*(.+)%s*$') id = id or _id end -- [IMPORTANT] Transform newlines into `/` if text and text:match('\n') then text = text:gsub('\n', '/') end ui:setSlot(x, y, isCustom and v or {			val .. (num and ', ' .. num or ),			class = (id and id ~= 'none') and 'goto-' .. id:gsub('^goto%-', ),			link = (id and id ~= 'none') and 'none' or (link_specify or (defaultnolink and 'none' or'')),			title = title,			text = text,		}, isCustom) end for i, v in ipairs(args) do		if i > 1 then if x > 6 then break; end if v:match('^%-$') then for y2 = y, maxCols do					if fill then ui:setBlankSlot(x, y2) else ui:setSlot(x, y2, '') end end x = x+1 y = 1 elseif y > maxCols then y = 1 x = x+1 else setSlot(v, x, y)				y = y+1 end end end for k, v in pairs(args) do		local k = tostring(k) if k:match('^%d+%s*,%s*%d+$') then local x, y = k:match('^(%d+)%s*,%s*(%d+)$') setSlot(v, tonumber(x), tonumber(y)) elseif k:match('^row%s*([1-6])%s*$') then local x = k:match('^row%s*([1-6])$') local endParams = v:match('%s*;%s*([%d%s,]-)%s*$') v = v:gsub('%s*;%s*([%d%s,]-)%s*$', '') endParams = endParams and string.split(endParams, '%s*,%s*') or {1, maxCols} if #endParams > 2 then for dummy, y in pairs(endParams) do					setSlot(v:gsub('%$n', y):gsub('\\%$n', '$n'), tonumber(x), tonumber(y)) end else for y = tonumber(endParams[1] or 1), tonumber(endParams[2] or maxCols), 1 do					setSlot(v:gsub('%$n', y):gsub('\\%$n', '$n'), tonumber(x), tonumber(y)) end end elseif k:match('^colu?m?n?%s*([1-9])%s*$') then local y = k:match('^colu?m?n?%s*([1-9])%s*$') local endParams = v:match('%s*;%s*([%d%s,]-)%s*$') v = v:gsub('%s*;%s*([%d%s,]-)%s*$', '') endParams = endParams and string.split(endParams, '%s*,%s*') or {1, maxRows} if #endParams > 2 then for dummy, x in pairs(endParams) do					setSlot(v:gsub('%$n', x):gsub('\\%$n', '$n'), tonumber(x), tonumber(y)) end else for x = tonumber(endParams[1] or 1), tonumber(endParams[2] or maxRows), 1 do					setSlot(v:gsub('%$n', x):gsub('\\%$n', '$n'), tonumber(x), tonumber(y)) end end end end return tostring(ui) end

function p.forgingTable( frame ) local args = getArgs(frame) local body = mw.html.create('span'):addClass('mcui mcui-Forging_Table pixel-image') -- CUSTOM: changing grid to use since wikia mobile doesn't support CSS local input = body:tag('table'):addClass('mcui-input') local row = input:tag('tr'):addClass('mcui-row') for num = 1, 6 do		if args[num] then local td = row:tag('td') td:wikitext(addSlot(args, num, 'I')) end end local arrow = body:tag('span'):addClass('mcui-arrow') :tag('span'):addClass('mcui-text mcui-overarrow-text'):wikitext(args.Time):done :tag('br') :done if args.arrow or  ~=  then arrow:css({			['background-image'] = '',			['margin-top'] = '0px'		}) end body :tag('span') :addClass('mcui-output') :wikitext(addSlot(args, 'Output', 'O', 'invslot-large')) return tostring(mw.html.create('div'):node(body)) end

p.createBlankUI = Interface p.slot = slot return p