Module:Inventory slot: Difference between revisions

Actually, we were just using an old version of minetip. Updated to support an even newer version of minetip. Now passes along idData to the main sprite function rather than using the base function, so this gets its tracking categories.
Reverted my changes on this for now
Tag: Manual revert
 
(81 intermediate revisions by 9 users not shown)
Line 1: Line 1:
local p = {}
local p = {}


local i18n = {
modLink = '$1:$2',
moduleAliases = [[Module:Inventory slot/Aliases]],
moduleRandom = [[Module:Random]],
-- List of special prefixes which should be handled by
-- other modules (such as being moved outside links)
prefixes = {
any = 'Any',
matching = 'Matching',
damaged = 'Damaged',
unwaxed = 'Unwaxed',
},
suffixes = {
be = 'BE',
lce = 'LCE',
},
}
p.i18n = i18n
local random = require( i18n.moduleRandom ).random
local aliases = mw.loadData( i18n.moduleAliases )
local pageName = mw.title.getCurrentTitle().text
--[[Splits a given text into fragments separated by semicolons that are not
    inside square brackets. Written by AttemptToCallNil for the Russian wiki
--]]
local function splitOnUnenclosedSemicolons(text)
local semicolon, lbrace, rbrace = (";[]"):byte(1, 3)
local nesting = false
local splitStart = 1
local frameIndex = 1
local frames = {}
for index = 1, text:len() do
local byte = text:byte(index)
if byte == semicolon and not nesting then
frames[frameIndex] = text:sub(splitStart, index - 1)
frameIndex = frameIndex + 1
splitStart = index + 1
elseif byte == lbrace then
assert(not nesting, "Excessive square brackets found")
nesting = true
elseif byte == rbrace then
assert(nesting, "Unbalanced square brackets found")
nesting = false
end
end
assert(not nesting, "Unbalanced square brackets found")
frames[frameIndex] = text:sub(splitStart, text:len())
for index = 1, #frames do
frames[index] = (frames[index]:gsub("^%s+", ""):gsub("%s+$", "")) -- faster mw.text.trim
end
return frames
end
-- Performs a simple recursive clone of a table's values
local function cloneTable( origTable )
local newTable = {}
for k, v in pairs( origTable ) do
if type( v ) == 'table' then
v = cloneTable( v )
end
newTable[k] = v
end
return newTable
end
--[[Merges a list, or inserts a string
or table into a table
--]]
local function mergeList( parentTable, content )
local i = #parentTable + 1
if content[1] then
-- Merge list into table
for _, v in ipairs( content ) do
parentTable[i] = v
i = i + 1
end
else
-- Add strings or tables to table
parentTable[i] = 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.name == '' then
return item
end
local category
local title = frame.title or mw.text.trim( args.title or '' )
local num = frame.num
local description = frame.text
local mod = frame.mod or 'Minecraft'
local name = frame.name
if frame.name:match( '%.gif$' ) and frame.name:match( '%.png$' ) then
name = frame.name
mod = ''
end
local minecraft = mod == "Minecraft" or mod == "minecraft"
local img = frame.img
if not img then
if name:match( '%.gif$' ) or name:match( '%.png$' ) then
img = name
-- Remove file extension from name
name = name:sub( 0, -5 )
elseif minecraft then
img = 'Invicon ' .. name .. '.png'
elseif mod then
img = mod .. ' ' .. name .. '.png'
else
img = name
end
end
if name:match( ' Revision %d+' ) then
name = name:gsub( ' Revision %d+', '' )
end
local link = args.link or ''
if link == '' then
link = i18n.modLink:gsub( '%$1', mod ):gsub( '%$2', name )
elseif link:lower() == 'none' then
link = nil
end
if link == pageName then
link = nil
end
local formattedTitle
local plainTitle
if title == '' then
plainTitle = name
elseif title:lower() ~= 'none' then
plainTitle = title:gsub( '\\\\', '\' ):gsub( '\\&', '&' )
local formatPatterns = {'&[0-9a-jl-qs-vyzr]', '&#%x%x%x%x%x%x', '&$%x%x%x'}
for _, formatPattern in ipairs( formatPatterns ) do
if plainTitle:match( formatPattern ) then
formattedTitle = title
plainTitle = plainTitle:gsub( formatPattern, '' )
end
end
if plainTitle == '' then
plainTitle = name
else
plainTitle = plainTitle:gsub( '\', '\\' ):gsub( '&', '&' )
end
elseif link then
formattedTitle = ''
end
item:attr{
['data-minetip-title'] = formattedTitle,
['data-minetip-text'] = description
}
-- & is re-escaped because mw.html treats attributes
-- as plain text, but MediaWiki doesn't
local escapedTitle = ( plainTitle or '' ):gsub( '&', '&' )
local altText = img .. ': Inventory sprite for ' .. name .. ' in Minecraft as shown in-game'
if link then
altText = altText .. ' linking to ' .. link
end
if formattedTitle or plainTitle or link then
altText = altText .. ' with description: ' .. ( formattedTitle or plainTitle or link )
if description then
altText = altText .. ' ' .. description:gsub( '/', ' ' )
end
altText = altText:gsub( '&[0-9a-jl-qs-wr]', '' )
end
item:addClass( 'invslot-item-image' )
:wikitext( '[[File:', img, '|32x32px|link=', link or '', '|alt=', altText, '|', escapedTitle, ']]' )
if num and num > 1 and num < 1000 then
if link then
item:wikitext( '[[', link, '|' )
end
local number = item
:tag( 'span' )
:addClass( 'invslot-stacksize' )
:attr{ title = plainTitle }
:wikitext( num )
if args.numstyle then
number:cssText( args.numstyle )
end
if link then
item:wikitext( ']]' )
end
end
item:wikitext( category )
return item
end
-- Main entry point
function p.slot( f )
function p.slot( f )
local args = f.args or f
local args = f.args or f
Line 7: Line 218:
end
end
args[1] = mw.text.trim( args[1] or '' )
if not args.parsed then
args[1] = mw.text.trim( args[1] or '' )
end
-- Comment this next line out if you're not using aliases
local modData = {
local aliases = mw.loadData( 'Module:Inventory slot/Aliases' )
aliases = args.modaliases or '',
default = args.mod
local modAliases = args.modaliases or ''
}
if modAliases ~= '' then
if modData.aliases ~= '' then
modAliases = mw.loadData( 'Module:' .. modAliases )
modData.aliases = mw.loadData( 'Module:' .. modData.aliases )
else
else
modAliases = nil
modData.aliases = nil
end
if args.mod == '' then
modData.default = nil
end
end
if aliases or modAliases then
local frames
local frames = {}
if args.parsed then
for frame in mw.text.gsplit( args[1], '%s*;%s*' ) do
frames = args[1]
local frameParts = p.getParts( frame, args.mod )
elseif args[1] ~= '' then
local randomise = args.class == 'invslot-large' and 'never' or nil
local id = frameParts.name
frames = p.parseFrameText( args[1], randomise, false, modData )
if frameParts.mod then
id = frameParts.mod .. ':' .. id
end
local alias
if modAliases and modAliases[id] then
alias = modAliases[id]
elseif aliases and aliases[id] then
alias = aliases[id]
end
if alias then
table.insert( frames, p.expandAlias( frameParts, alias ) )
else
table.insert( frames, frame )
end
end
args[1] = table.concat( frames, ';' )
end
end
local animated = frames and #frames > 1
local sprite
local ids = mw.loadData( [[Module:InvSprite/IDs]] ).ids
local modIds = {}
local animated = args[1]:find( ';' )
local pageName = mw.title.getCurrentTitle().text
local imgClass = args.imgclass
local imgClass = args.imgclass
local numStyle = args.numstyle
local numStyle = args.numstyle
Line 62: Line 254:
if args.style then
if args.style then
body:cssText( args.style )
body:cssText( args.style )
end
if ( args.default or '' ) ~= '' then
body:addClass( 'invslot-default-' .. string.lower( args.default ):gsub( ' ', '-' ) )
end
end
if ( args.default or '' ) ~= '' then
--mw.logObject( frames )
body:css( 'background-image', '{{FileUrl|' .. args.default .. '.png}}' )
if not frames then
return tostring( body )
end
end
local first = true
local activeFrame = frames.randomise == true and random( #frames ) or 1
for frame in mw.text.gsplit( args[1], '%s*;%s*' ) do
for i, frame in ipairs( frames ) do
local item
local item
if frame ~= '' or frame == '' and animated then
-- Table is a list, must contain subframes
item = body:tag( 'span' ):addClass( 'invslot-item' )
if frame[1] then
if imgClass then
item = body:tag( 'span' ):addClass( 'animated-subframe' )
item:addClass( imgClass )
local subActiveFrame = frame.randomise == true and random( #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' )
end
end
end
else
item = makeItem( frame, i, args )
body:node( item )
end
if i == activeFrame and animated then
item:addClass( 'animated-active' )
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, modData )
local frames = { randomise = randomise }
local subframes = {}
local subframe
local expandedAliases
local splitFrames = splitOnUnenclosedSemicolons( framesText )
for i, frameText in ipairs( splitFrames ) do
frameText = frameText:gsub( '^%s*{%s*', function()
subframe = true
return ''
end )
if subframe then
frameText = frameText:gsub( '%s*}%s*$', function()
subframe = 'last'
return ''
end )
end
end
local frame = p.makeFrame( frameText, modData and modData.default )
if frame == '' then
local newFrame = frame
( item or body ):tag( 'br' )
if aliases or modData.aliases then
else
local id = frame.name
local category
if frame.mod then
local parts = p.getParts( frame, args.mod )
id = frame.mod .. ':' .. id
local title = parts.title or mw.text.trim( args.title or '' )
end
local mod = parts.mod
local name = parts.name
local num = parts.num
local description = parts.text
local img, idData
local alias = modData and modData.aliases and modData.aliases[id] or
if mod then
aliases and aliases[id]
local modData = modIds[mod]
if alias then
if not modData and mw.title.new( 'Module:InvSprite/Mods/' .. mod .. '/IDs' ).exists then
newFrame = p.getAlias( alias, frame )
modData = mw.loadData( 'Module:InvSprite/Mods/' .. mod .. '/IDs' )
if aliasReference then
modIds[mod] = modData
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
if modData and modData[name] then
end
idData = modData[name]
end
else
img = name .. ' (' .. mod .. ')'
if subframe then
end
mergeList( subframes, newFrame )
elseif ids[name] then
-- Randomise starting frame for "Any *" aliases, as long as the alias is the only subframe
idData = ids[name]
if frames.randomise ~= 'never' and subframes.randomise == nil and
frame.name:match( '^' .. i18n.prefixes.any .. ' ' ) then
subframes.randomise = true
else
else
img = name
subframes.randomise = false
end
end
if frames.randomise ~= 'never' then
local link = args.link or ''
frames.randomise = false
if link == '' then
if mod then
link = 'Mods/' .. mod .. '/' .. name
else
link = name
end
elseif link:lower() == 'none' then
link = nil
end
if link == pageName then
link = nil
end
end
if subframe == 'last' then
local formattedTitle
-- No point having a subframe containing a single frame,
local plainTitle
-- or the subframe being the only frame
if title == '' then
if #subframes == 1 or #splitFrames == i and #frames == 0 then
plainTitle = name
local lastFrame = #frames
elseif title:lower() ~= 'none' then
mergeList( frames, subframes )
plainTitle = title:gsub( '\\\\', '&#92;' ):gsub( '\\&', '&#38;' )
-- Inherit the randomise flag if it's the only frame
local formatPattern = '&[0-9a-fk-or]'
if #splitFrames == 1 then
if plainTitle:match( formatPattern ) then
frames.randomise = subframes.randomise
formattedTitle = title
end
plainTitle = plainTitle:gsub( formatPattern, '' )
end
-- Append alias reference data, if present
if aliasReference and subframes.aliasReference then
plainTitle:gsub( '&#92;', '\\' ):gsub( '&#38;', '&' )
if not expandedAliases then
elseif link then
expandedAliases = {}
if img then
end
formattedTitle = ''
for i, aliasRefData in pairs(subframes.aliasReference) do
expandedAliases[lastFrame + i] = aliasRefData
end
end
else
else
plainTitle = ''
table.insert( frames, subframes )
end
end
subframes = {}
subframe = nil
end
end
else
item:attr{
-- Randomise starting frame for "Any *" aliases, as long as the alias is the only frame
['data-minetip-title'] = formattedTitle,
if frames.randomise ~= 'never' and frame.name:match( '^' .. i18n.prefixes.any .. ' ' ) then
['data-minetip-text'] = description
frames.randomise = true
}
if img then
item:addClass( 'invslot-item-image' )
:wikitext( '[[File:Grid ', img, '.png|32x32px|link=', link or '', '|', plainTitle or '', ']]' )
else
else
if not sprite then
frames.randomise = false
sprite = require( [[Module:Sprite]] ).sprite
end
local image
if mod then
image = args.spritesheet or mod .. 'Sprite.png'
end
if link then
item:wikitext( '[[', link, '|' )
end
local image, spriteCat = sprite{
iddata = idData, title = plainTitle,
image = image, settings = 'InvSprite'
}
item:wikitext( image )
category = spriteCat
end
end
mergeList( frames, newFrame )
if num and num > 1 and num < 1000 then
if img and link then
item:wikitext( '[[', link, '|' )
end
local number = item
:tag( 'span' )
:addClass( 'invslot-stacksize' )
:attr{ title = plainTitle }
:wikitext( num )
if numStyle then
number:cssText( numStyle )
end
if img and link then
item:wikitext( ']]' )
end
end
if idData and link then
item:wikitext( ']]' )
end
item:wikitext( category )
end
if first then
if animated and item then
item:addClass( 'active' )
end
first = false
end
end
end
end
return body
frames.aliasReference = expandedAliases
return frames
end
end


function p.expandAlias( frameParts, alias )
--[[Returns a new table with the parts of the parent frame
-- If the frame has no parts, we can just return the alias as-is
added to the alias
if not frameParts.title and not frameParts.mod and not frameParts.num and not frameParts.text then
--]]
return 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
end
local expandedFrames = {}
local expandedFrames = {}
for aliasFrame in mw.text.gsplit( alias, '%s*;%s*' ) do
for i, aliasFrame in ipairs( aliasFrames ) do
local aliasParts = p.getParts( aliasFrame )
local expandedFrame
aliasParts.title = frameParts.title or aliasParts.title or ''
if type( aliasFrame ) == 'string' then
aliasParts.mod = frameParts.mod or aliasParts.mod or 'Minecraft'
expandedFrame = { name = aliasFrame }
aliasParts.num = frameParts.num or aliasParts.num or ''
else
aliasParts.text = frameParts.text or aliasParts.text or ''
expandedFrame = cloneTable( aliasFrame )
end
expandedFrame.title = parentFrame.title or expandedFrame.title
expandedFrame.mod = parentFrame.mod or expandedFrame.mod
expandedFrame.num = parentFrame.num or expandedFrame.num
expandedFrame.text = parentFrame.text or expandedFrame.text
table.insert( expandedFrames, string.format(
expandedFrames[i] = expandedFrame
'[%s]%s:%s,%s[%s]',
aliasParts.title, aliasParts.mod, aliasParts.name, aliasParts.num, aliasParts.text
) )
end
end
return table.concat( expandedFrames, ';' )
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]',
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
end


function p.getParts( frame, mod )
-- Splits up the frame text into its parts
local parts = {}
function p.makeFrame( frameText, mod )
parts.title = frame:match( '^%[%s*([^%]]+)%s*%]' )
-- Simple frame with no parts
if not frameText:match( '[%[:,]' ) then
return {
mod = mod,
name = mw.text.trim( frameText ),
}
end
frameText = frameText:gsub( '%s*([%[%]:,;])%s*', '%1' )
parts.mod = mw.text.trim( frame:match( '([^:%]]+):' ) or mod or '' )
local frame = {}
frame.title = frameText:match( '^%[([^%]]+)%]' )
frame.mod = frameText:match( '([^:%]]+):' ) or mod
local vanilla = { v = 1, vanilla = 1, mc = 1, minecraft = 1 }
local vanilla = { v = 1, vanilla = 1, mc = 1, minecraft = 1 }
if parts.mod == '' or vanilla[mw.ustring.lower( parts.mod )] then
if frame.mod and vanilla[mw.ustring.lower( frame.mod )] or frame.mod == '' then
parts.mod = nil
frame.mod = nil
end
end
local nameStart = ( frame:find( ':' ) or frame:find( '%]' ) or 0 ) + 1
local nameStart = ( frameText:find( ':' ) or frameText:find( '%]' ) or 0 ) + 1
if nameStart - 1 == #frame then
if nameStart - 1 == #frameText then
nameStart = 1
nameStart = 1
end
end
parts.name = mw.text.trim( frame:sub( nameStart, ( frame:find( '[,%[]', nameStart ) or 0 ) - 1 ) )
frame.name = frameText:sub( nameStart, ( frameText:find( '[,%[]', nameStart ) or 0 ) - 1 )
parts.num = math.floor( frame:match( ',%s*(%d+)' ) or 0 )
frame.num = math.floor( frameText:match( ',(%d+)' ) or 0 )
if parts.num == 0 then
if frame.num == 0 then
parts.num = nil
frame.num = nil
end
end
parts.text = frame:match( '%[%s*([^%]]+)%s*%]$' )
frame.text = frameText:match( '%[([^%]]+)%]$' )
return parts
return frame
end
function p.getParts( frameText, mod )
return p.makeFrame( frameText, mod )
end
end
   
   
return p
return p