Module:Inventory slot

-- Initially taken from: https://minecraft.gamepedia.com/Module:Inventory_slot

local p = {}

local getArgs = require('Module:Arguments').getArgs local loader = require('Module:Loader')

local string, table, yesno, currency, random, uitext = loader.require('String', 'Table', 'Yesno', 'Currency', 'Random', 'UIText')

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

local pageName = mw.title.getCurrentTitle.text local i18n = { prefixes = { any = 'Any', matching = 'Matching', all = 'All', },	suffixes = { },	disableLink = { ['Go Back']=1; } } p.i18n = i18n

--Merges a list, or inserts a string	or table into a table -- local function mergeList( parentTable, content ) if content[1] then -- Merge list into table for _, v in ipairs( content ) do			parentTable[#parentTable + 1] = v		end else -- Add strings or tables to table parentTable[#parentTable + 1] = content end end

-- Creates the HTML for an item local function makeItem( frame, i, args ) local item = mw.html.create( 'span' ):addClass( 'invslot-item' ) if args.imgclass then item:addClass( args.imgclass ) end if frame.image_id then item:attr('data-iid', frame.image_id) end if frame.name == '' then return item end local category local title = string.trim( args.title or frame.title or '' ) local name = frame.name or '' local num = frame.num or 1 local num2 = frame.num2 local image = args.image or frame.image or nil local description = args.text or frame.text or '' -- [IMPORTANT] Transform newlines into `/` if description and description:match("\n") then description = description:gsub("\n", "/") end local forceNum = yesno(args.forcenum) local img, idData local checkIfColoredLeather = function(pStr) if pStr:match('%s*([Hh][Ee][Ll][Mm][Ee][Tt])-(#?[0-9A-Fa-f]+)') then return pStr:match('(.*)%s([Hh][Ee][Ll][Mm][Ee][Tt])-(#?[0-9A-Fa-f]+)') elseif pStr:match('%s*([Cc][Hh][Ee][Ss][Tt][Pp][Ll][Aa][Tt][Ee])-(#?[0-9A-Fa-f]+)') then return pStr:match('(.*)%s([Cc][Hh][Ee][Ss][Tt][Pp][Ll][Aa][Tt][Ee])-(#?[0-9A-Fa-f]+)') elseif pStr:match('%s*([Ll][Ee][Gg][Gg][Ii][Nn][Gg][Ss])-(#?[0-9A-Fa-f]+)') then return pStr:match('(.*)%s([Ll][Ee][Gg][Gg][Ii][Nn][Gg][Ss])-(#?[0-9A-Fa-f]+)') elseif pStr:match('%s*([Bb][Oo][Oo][Tt][Ss])-(#?[0-9A-Fa-f]+)') then return pStr:match('(.*)%s([Bb][Oo][Oo][Tt][Ss])-(#?[0-9A-Fa-f]+)') end return nil end

-- Check if color data is in the name (otherwise check below if it's in the image) if checkIfColoredLeather(name) then name, template, color = checkIfColoredLeather(name) end if image then -- Check if image name is colored leather syntax (ignore first name param that's returned) if checkIfColoredLeather(image) then _, template, color = checkIfColoredLeather(image) end if string.anyMatched(image, '\.gif$', '\.webp$', '\.png', '\.apng', '\.jpg$', '\.jpeg$') then img = image else img = image .. '.png' end else img = name .. '.png' end local link = args.link or frame.link or '' if link == '' then link = name == 'none' and nil or name for _, suffix in pairs( i18n.suffixes ) do link = link:gsub( ' ' .. suffix .. '$', '' ) end elseif ((not i18n.disableLink[name] and not args.link) or (args.link or ''):lower == 'none') and link:lower == 'none' then link = nil end local rtName = mw.title.makeTitle(0, link or ) and mw.title.makeTitle(0, link or ).redirectTarget or false rtName = rtName and rtName.text or false if link == pageName or rtName == pageName or link == 'none' then link = nil end title = uitext.applyReplacements(title) local formattedTitle local plainTitle if title == '' then plainTitle = name formattedTitle = name elseif title:lower ~= 'none' then formattedTitle = title plainTitle = title local formatPattern = '&[0-9a-fk-or]' if plainTitle:match( formatPattern ) then plainTitle = plainTitle:gsub( formatPattern, '' ) end if plainTitle == '' then plainTitle = name end elseif link then if img then formattedTitle = '' else plainTitle = '' end end description = uitext.applyReplacements(description) if description:lower == 'none' then description = '' end item:attr{ ['data-minetip-title'] = formattedTitle and formattedTitle:gsub('"', '&quot;') or nil,		['data-minetip-text'] = description and description:gsub('"', '&quot;') or nil, }	if template then item:tag('div'):cssText('top: 2px;left: 2px;position: absolute;'):wikitext(mw.getCurrentFrame:expandTemplate{ title = template:sub(1,1):upper .. template:sub(2):lower, args = { color, size = 32, i = 1 } }):done elseif img then -- & is re-escaped because mw.html treats attributes -- as plain text, but MediaWiki doesn't		local escapedTitle = ( plainTitle or '' ):gsub( '&', '&#38;' ) item:addClass( 'invslot-item-image' ) :wikitext( '' ) else local image = args.spritesheet .. 'Sprite.png' if link then item:wikitext(  )		end		local image		item:node( image )			item:wikitext( '[[', link, '|' )	end	if tonumber(num2) ~= tonumber(num) then		if (num2 and num2 >= 10000) then num2 = string._formatShortNum(tonumber(num2)):lower end	else		num2 = nil	end	if tonumber(num) and num >= 10000 then num = string._formatShortNum(tonumber(num)):lower end	if forceNum or (not num2 and num and tonumber(num) ~= 1) or (num and num2) then		local ret_ = tostring(num2 and ('%s-%s'):format(num, num2) or num)		if img and link then			item:wikitext( '[[', link, '|' )		end		local number = item			:tag( 'span' )				:addClass( 'invslot-stacksize' )				:attr{ title = plainTitle }		if numStyle then			number:cssText( numStyle )		end		-- apply shrinking when needed		local a = num2 and 1 or 0		local function setf(f, r)			number:css('font-size', f .. 'px'):css('right', (r - 2) .. 'px')		end		if (tostring(num):len+a > 7) or (num2 and tostring(num2):len > 7) then			setf(5.4, 1.2)		elseif (tostring(num):len+a > 6) or (num2 and tostring(num2):len > 6) then			setf(6.1, 1.3)		elseif (tostring(num):len+a > 5) or (num2 and tostring(num2):len > 5) then			setf(7.2, 1.6)		elseif (tostring(num):len+a > 3) or (num2 and tostring(num2):len > 3) or (num2 and ret_:len > 4) then			setf(9, 1)		elseif num2 or ret_:len > 4 then			setf(12, 0)		else			setf(15, 0)		end		if ret_:len > 4 then			ret_ = ret_:gsub('-','- ')		end		number:wikitext( ret_ )		if img and link then			item:wikitext(  )		end	end	if idData and link then		item:wikitext( ']]' )	end	if link then		item:wikitext()			:tag('span'):cssText('position:absolute; width:100%; height:100%; top:0px; left:0px;'):done			:wikitext()	end	return item end

-- Main entry point function p.slot( f ) local args = getArgs(f) if not args.parsed then args[1] = string.trim( args[1] and (args[1]:gsub("(%s)%s+", "%1")) or '' ) end local frames if args.parsed then frames = args[1] elseif args[1] ~= '' then local randomise = args.class == 'invslot-large' and 'never' or nil frames = p.parseFrameText( args[1], randomise, false ) end local animated = frames and #frames > 1 local imgClass = args.imgclass local numStyle = args.numstyle local body = mw.html.create( 'span' ):addClass( 'invslot noselect' ):css{ ['vertical-align'] = args.align } if animated then body:addClass( 'animated' ) end if args.class then body:addClass( args.class ) end if args.style then body:cssText( args.style ) end if ( args.default or  ) ~=  then body:css( 'background-image', f:expandTemplate{ title = i18n.templateFileUrl, args = { args.default .. '.png' } } ) end if not frames then return tostring(body:tag('span'):addClass('invslot-item'):done) end local activeFrame = frames.randomise == true and random.number{ #frames } or 1 for i, frame in ipairs( frames ) do		local item -- Table is a list, must contain subframes if frame[1] then item = body:tag( 'span' ):addClass( 'animated-subframe' ) local subActiveFrame = frame.randomise and random.number{ #frame } or 1 for sI, sFrame in ipairs( frame ) do				local sItem = makeItem( sFrame, sI, args ) item:node( sItem ) if sI == subActiveFrame then sItem:addClass( 'animated-active' ) else -- CUSTOM: "hidden" class is needed to hide it on mobile (since wikia doesn't allow CSS / JS on mobile) sItem:addClass('hidden') end end else item = makeItem( frame, i, args ) body:node( item ) end if i == activeFrame and animated then item:addClass( 'animated-active' ) elseif animated then -- CUSTOM: "hidden" class is needed to hide it on mobile (since wikia doesn't allow CSS / JS on mobile) item:addClass('hidden') end end return tostring( body ) end

--Parses the frame text into a table of frames and subframes,	expanding aliases (and optionally retaining a reference), and	deciding if the slot can be randomised -- function p.parseFrameText( framesText, randomise, aliasReference ) local frames = { randomise = randomise } local subframes = {} local subframe local expandedAliases framesText = framesText:gsub( '\\;', '⍮' );-- gsub here allows us to escape ; character local splitFrames = string.split( string.trim( framesText ), '%s*;%s*' ) for _, frameText in ipairs( splitFrames ) do		frameText = frameText:gsub('⍮', ';') -- undo-escape now that semicolon regex check is done frameText = frameText:gsub( '^%s*{%s*', function			subframe = true			return ''		end ) if subframe then frameText = frameText:gsub( '%s*}%s*$', function				subframe = 'last'				return ''			end ) end local frame = p.makeFrame( frameText ) local newFrame = frame -- if aliases then local id = frame.name local alias = currency.currencySlot( frameText, nil, nil, true ) or aliasesCache:get(id) if alias then newFrame = p.getAlias( alias, frame ) if aliasReference then local curFrame = #frames + 1 local aliasData = { frame = frame, length = #newFrame } if subframe then if not subframes.aliasReference then subframes.aliasReference = {} end subframes.aliasReference[#subframes + 1] = aliasData else if not expandedAliases then expandedAliases = {} end expandedAliases[curFrame] = aliasData end end end -- end if subframe then mergeList( subframes, newFrame ) -- Randomise starting frame for "Any *" aliases, as long as the alias is the only subframe if frames.randomise ~= 'never' and subframes.randomise == nil and i18n.prefixes.any and frame.name:match( '^' .. i18n.prefixes.any .. ' ' ) then subframes.randomise = true else subframes.randomise = false end if frames.randomise ~= 'never' then frames.randomise = false end if subframe == 'last' then -- No point having a subframe containing a single frame, -- or the subframe being the only frame if #subframes == 1 or #splitFrames == i and #frames == 0 then mergeList( frames, subframes ) else table.insert( frames, subframes ) end subframes = {} subframe = nil end else -- Randomise starting frame for "Any *" aliases, as long as the alias is the only frame if frames.randomise == nil and i18n.prefixes.any and frame.name:match( '^' .. i18n.prefixes.any .. ' ' ) then frames.randomise = true elseif frames.randomise ~= 'never' then frames.randomise = false end mergeList( frames, newFrame ) end end frames.aliasReference = expandedAliases return frames end

--Returns a new table with the parts of the parent frame	added to the alias -- function p.getAlias( aliasFrames, parentFrame ) -- If alias is just a name, return the parent frame with the new name if type( aliasFrames ) == 'string' then local expandedFrame = mw.clone( parentFrame ) expandedFrame.name = aliasFrames return { expandedFrame } end -- Single frame alias, put in list if aliasFrames.name then aliasFrames = { aliasFrames } end local expandedFrames = {} for i, aliasFrame in ipairs( aliasFrames ) do		local expandedFrame if type( aliasFrame ) == 'string' then expandedFrame = { name = aliasFrame } else expandedFrame = table.deepCopy( aliasFrame, true ) end expandedFrame.title = expandedFrame.title or parentFrame.title expandedFrame.num = expandedFrame.num or parentFrame.num expandedFrame.num2 = expandedFrame.num2 or parentFrame.num2 expandedFrame.text = expandedFrame.text or parentFrame.text expandedFrame.image = expandedFrame.image or parentFrame.image expandedFrames[i] = expandedFrame end return expandedFrames end

function p.expandAlias( parentFrame, alias ) return p.getAlias( alias, parentFrame ) end

function p.stringifyFrame( frame ) if not frame.name then return '' end return string.format(		'[%s]%s:%s,%s[%s]',		frame.title or ,		frame.name,		frame.num or ,		frame.text or ''	) end

function p.stringifyFrames( frames ) for i, frame in ipairs( frames ) do		frames[i] = p.stringifyFrame( frame ) end return table.concat( frames, ';' ) end

-- Splits up the frame text into its parts function p.makeFrame( frameText ) local c = currency.currencySlot( frameText ) frameText = c and c[1] or frameText -- Simple frame with no parts if type(frameText) ~= 'string' then error(type(frameText)) end -- [ESC] allow escaping special characters - we'll convert normal character after we parse frameText = frameText:gsub( '\\\\', '⑊' ):gsub( '\\%[', '⁅' ):gsub( '\\%]', '⁆' ):gsub( '\\:', '⫶' ) if not frameText:match( '[%[:,]' ) then return { name = string.trim( frameText ), }	end frameText = frameText:gsub( '%s*([%[%]:,;])%s*', '%1' ) local frame = {} frame.title = frameText:match( '^%[([^%]]+)%]' ) frame.image = frameText:match( '([^:%]]+):' ) local nameStart = ( frameText:find( ':' ) or frameText:find( '%]' ) or 0 ) + 1 if nameStart - 1 == #frameText then nameStart = 1 end frame.name = frameText:sub( nameStart, ( frameText:find( '[,%[]', nameStart ) or 0 ) - 1 ) -- supports m-n syntax frame.num = math.floor( frameText:match( ',%s*(%d+)' ) or 1 ) frame.num2 = math.floor( frameText:match( ',%s*%d+%s*[%-%–]%s*(%d+)%s*$' ) or frame.num ) frame.text = frameText:match( '%[([^%]]+)%]$' ) -- [/ESC] un-replace the characters we used for escaped characters earlier if frame.title then frame.title = frame.title:gsub('⑊','\\'):gsub('⁅','['):gsub('⁆',']'):gsub('⫶',':') end if frame.name then frame.name = frame.name:gsub('⑊','\\'):gsub('⁅','['):gsub('⁆',']'):gsub('⫶',':') end if frame.text then frame.text = frame.text:gsub('⑊','\\'):gsub('⁅','['):gsub('⁆',']'):gsub('⫶',':') end -- [IMPORTANT] Transform newlines into `/` if frame.text and frame.text:match("\n") then mw.log(frame.text) frame.text = frame.text:gsub("\n", "/") end return frame end

function p.getParts( frameText ) return p.makeFrame( frameText ) end

return p