Jump to: navigation, search

Module:Sprite: Difference between revisions

m (1 revision)
 
(Updated Module)
Line 4: Line 4:
if f == mw.getCurrentFrame() then  
if f == mw.getCurrentFrame() then  
args = require( 'Module:ProcessArgs' ).merge( true )
args = require( 'Module:ProcessArgs' ).merge( true )
else
f = mw.getCurrentFrame()
end
end
Line 12: Line 14:
size = 16,
size = 16,
pos = 1,
pos = 1,
link = '',
align = 'text-top'
align = 'text-top',
class = '',
text = '',
title = ''
}
}
local defaultStyle = mw.clone( default )
local defaultStyle = default
if args.settings then
if args.settings then
local settings = mw.loadData( 'Module:' .. args.settings )
local settings = mw.loadData( 'Module:' .. args.settings )
if not settings.stylesheet then
-- Make a separate clone of the current default settings
defaultStyle = mw.clone( default )
end
for k, v in pairs( settings ) do
for k, v in pairs( settings ) do
default[k] = v
default[k] = v
if settings.stylesheet then
defaultStyle[k] = v
end
end
end
end
end
local name = args.name or default.name
local setting = function( arg )
local scale = args.scale or default.scale
return args[arg] or default[arg]
local autoScale = args.autoscale or default.autoscale
end
local sheetWidth = args.sheetsize or default.sheetsize
local size = args.size or default.size
local sprite = mw.html.create( 'span' ):addClass( 'sprite' )
local pos = math.abs( args.pos or default.pos ) - 1
sprite:tag( 'br' )
local link = args.link or default.link
local align = args.align or default.align
-- mw.html's css method performs very slow escaping, which doubles the time it takes
local class = args.class or default.class
-- to run, so we'll construct the styles manually, and put them in the cssText
local text = args.text or default.text
-- method, which only does html escaping (which isn't slow)
local title = args.title or default.title
local styles = {}
local css = args.css or default.css
local className = args.classname or default.classname
if setting( 'stylesheet' ) then
 
sprite:addClass(
setting( 'classname' ) or
mw.ustring.lower( setting( 'name' ):gsub( ' ', '-' ) ) .. '-sprite'
)
else
table.insert( styles, 'background-image:{{FileUrl|' .. (
setting( 'image' ) or setting( 'name' ) .. 'Sprite.png'
) .. '}}' )
end
local class = setting( 'class' )
if class then
sprite:addClass( class )
end
local size = setting( 'size' )
local pos = math.abs( setting( 'pos' ) ) - 1
local sheetWidth = setting( 'sheetsize' )
local tiles = sheetWidth / size
local tiles = sheetWidth / size
local left = pos % tiles * size
local left = pos % tiles * size
local top = math.floor( pos / tiles ) * size
local top = math.floor( pos / tiles ) * size
local scale = setting( 'scale' )
local autoScale = setting( 'autoscale' )
local styles = {}
if default.stylesheet then
class = ( className or mw.ustring.lower( name ) .. '-sprite ' ) .. class
else
table.insert( styles, 'background-image:{{FileUrl|' .. ( args.image or default.image or name .. 'Sprite.png' ) .. '}}' )
end
if left > 0 or top > 0 then
if left > 0 or top > 0 then
table.insert( styles, 'background-position:-' .. left * scale .. 'px -' .. top * scale .. 'px' )
table.insert( styles, 'background-position:-' .. left * scale .. 'px -' .. top * scale .. 'px' )
Line 61: Line 72:
end
end
if size ~= defaultStyle.size or ( not autoScale and scale ~= defaultStyle.scale ) then
if size ~= defaultStyle.size or ( not autoScale and scale ~= defaultStyle.scale ) then
table.insert( styles, 'height:' .. size * scale .. 'px;width:' .. size * scale .. 'px' )
table.insert( styles, 'height:' .. size * scale .. 'px' )
table.insert( styles, 'width:' .. size * scale .. 'px' )
end
end
local align = setting( 'align' )
if align ~= defaultStyle.align then
if align ~= defaultStyle.align then
table.insert( styles, 'vertical-align:' .. align )
table.insert( styles, 'vertical-align:' .. align )
end
end
if css then
table.insert( styles, setting( 'css' ) )
table.insert( styles, css )
sprite:cssText( table.concat( styles, ';' ) )
local text = setting( 'text' )
local root
local spriteText
if text then
root = mw.html.create( 'span' ):addClass( 'nowrap' )
spriteText = mw.html.create( 'span' ):addClass( 'sprite-text' ):wikitext( text )
end
end
if title ~= '' then
title = ' title="' .. title .. '"'
local title = setting( 'title' )
if title then
( root or sprite ):attr( 'title', title )
end
end
local sprite = table.concat( {
if not root then
'<span',
root = mw.html.create( '' )
'class="sprite ' .. class .. '"',
end
'style="' .. table.concat( styles, ';' ) .. '"',
root:node( sprite )
title,
if spriteText then
'><br></span>'
root:node( spriteText )
}, ' ' )
sprite = sprite:gsub( '%s+([">])', '%1' )
if text ~= '' then
text = '<span class="sprite-text nowrap"' .. title .. '>' .. text .. '</span>'
end
end
if link ~= '' then
local link = setting( 'link' ) or ''
if link ~= '' and mw.ustring.lower( link ) ~= 'none' then
-- External link
if link:find( '//' ) then
if link:find( '//' ) then
-- External link
return '[' .. link .. ' ' .. tostring( root ) .. ']'
return '[' .. link .. ' ' .. sprite .. text .. ']'
else
-- Internal link
return '[[' .. link .. '|' .. sprite .. text .. ']]'
end
end
else
return sprite .. text
-- Internal link
local linkPrefix = setting( 'linkprefix' ) or ''
return '[[' .. linkPrefix .. link .. '|' .. tostring( root ) .. ']]'
end
end
return tostring( root )
end
end


Line 103: Line 122:
if f == mw.getCurrentFrame() then
if f == mw.getCurrentFrame() then
args = require( 'Module:ProcessArgs' ).merge( true )
args = require( 'Module:ProcessArgs' ).merge( true )
else
f = mw.getCurrentFrame()
end
end
local category = ''
local categories = {}
if tonumber( args[1] ) then
local idData = args.iddata
args.pos = args[1]
if not idData then
else
local default = {}
local default = {}
if args.settings then
if args.settings then
Line 115: Line 135:
local name = args.name or default.name
local name = args.name or default.name
local ids = mw.loadData( 'Module:' .. ( args.ids or default.ids or 'Sprite/' .. name ) )
local ids = mw.loadData( 'Module:' .. ( args.ids or default.ids or name .. '/IDs' ) )
local id = mw.text.trim( args[1] or '' )
ids = ids.ids or ids
local pos = ids[id] or ids[mw.ustring.lower( id ):gsub( '[%s%+]', '-' )]
local id = mw.text.trim( tostring( args[1] or '' ) )
if not pos then
idData = ids[id] or ids[mw.ustring.lower( id ):gsub( '[%s%+]', '-' )]
category = '[[Category:Pages with missing sprites]]'
end
local title = mw.title.getCurrentTitle()
-- Remove categories on language pages, talk pages, and in User/UserWiki/UserProfile namespaces
local disallowCats = args.nocat or title.isTalkPage or title.nsText:find( '^User' )
if idData then
if type( idData ) == 'table' then
if idData.deprecated and not disallowCats then
table.insert( categories, f:expandTemplate{ title = 'Translation category', args = { 'Pages using deprecated sprite names', project = 0 } } )
end
args.pos = idData.pos
else
args.pos = idData
end
end
args.pos = pos
elseif not disallowCats then
table.insert( categories, f:expandTemplate{ title = 'Translation category', args = { 'Pages with missing sprites', project = 0 } } )
end
end
return p.base( args ) .. category
return p.base( args ), table.concat( categories, '' )
end
end


Line 140: Line 174:
args[1] = args.id or args[1]
args[1] = args.id or args[1]
args.link = link
args.link = args.link or link
args.text = text
args.text = text
Line 147: Line 181:


function p.doc( f )
function p.doc( f )
local args = require( 'Module:ProcessArgs' ).norm( f.args )
local args = f
local idTable = mw.title.new( 'Module:Sprite/' .. args.name ):getContent()
if f == mw.getCurrentFrame() then
idTable = idTable:gsub( '(\n%s*%-%-%s*.-%s*%-%-%s*\n)', '%1,' ):gsub( '^return {', '' ):gsub( '}$', '' )
args = f.args
else
f = mw.getCurrentFrame()
end
local settingsPage = mw.text.trim( args[1] )
local settings = mw.loadData( 'Module:' .. settingsPage )
local idsPage = 'Module:' .. ( settings.ids or settings.name .. '/IDs' )
local html = {}
local getProtection = function( title, action, extra )
local ids = {}
local protections = { 'edit' }
local posKeys = {}
if extra then
local section = ''
table.insert( protections, extra )
for line in mw.text.gsplit( idTable, ',' ) do
end
line = mw.text.trim( line )
id = line:match( '^%[[\'"](.+)[\'"]%]' ) or line:match( '^%w+' ) or ''
pos = line:match( '=%s*(%d+)%s*,?$' ) or ''
section = line:match( '^%-%-%s*(.+)%s*%-%-$' ) or section
if id ~= '' and pos ~= '' then
local addProtection = function( protection )
if ids[pos] then
if protection == 'autoconfirmed' then
if type( ids[pos].id ) == 'table' then
protection = 'editsemiprotected'
table.insert( ids[pos].id, id )
elseif protection == 'sysop' then
else
protection = 'editprotected'
ids[pos].id = { ids[pos].id, id }
end
else
ids[pos] = { id = id, section = section }
table.insert( posKeys, pos )
end
end
table.insert( protections, protection )
end
end
end
local list = {}
local direct = title.protectionLevels[action]
local listHead = '<ul class="spritedoc-multicolumn">'
for _, protection in ipairs( direct ) do
local listFoot = '</ul>'
addProtection( protection )
local lastSection = ''
end
for i, pos in ipairs( posKeys ) do
local cascading = title.cascadingProtection.restrictions[action] or {}
local id = ids[pos].id
if #cascading > 0 then
local newSection = mw.text.trim( ids[pos].section )
table.insert( protections, 'protect' )
end
for _, protection in ipairs( cascading ) do
addProtection( protection )
end
if newSection ~= lastSection or i == 1 then
return table.concat( protections, ',' )
if newSection ~= lastSection then
end
if lastSection ~= '' then
table.insert( list, listFoot )
local body
end
if args.refresh then
body = mw.html.create( '' )
table.insert( list, '\n===' .. newSection .. '===\n' )
else
lastSection = newSection
local idsTitle = mw.title.new( idsPage )
end
local spritesheet = settings.image or settings.name .. 'Sprite.png'
table.insert( list, listHead )
local spriteTitle = mw.title.new( 'File:' .. spritesheet )
end
local idsProtection = getProtection( idsTitle, 'edit' )
table.insert( list, '<li><table><tr><td data-pos="' .. pos .. '">' )
local spriteProtection = getProtection( spriteTitle, 'upload', 'upload,reupload' )
if type( id ) == 'table' then
body = mw.html.create( 'div' ):attr( {
for i, id2 in ipairs( id ) do
id = 'spritedoc',
if i == 1 then
['data-idspage'] = idsTitle.id,
args[1] = id2
['data-idsprotection'] = idsProtection,
table.insert( list, p.sprite( args ) .. '</td><td><div class="sprite-id"><code>' .. id2 .. '</code></div>' )
['data-idstimestamp'] = f:callParserFunction( 'REVISIONTIMESTAMP', idsPage ),
else
['data-spritesheet'] = spritesheet,
table.insert( list, '<div class="sprite-id"><code>' .. id2 .. '</code></div>' )
['data-spriteprotection'] = spriteProtection,
end
['data-pos'] = settings.pos or 1,
end
['data-refreshtext'] = mw.text.nowiki( '{{#invoke:sprite|doc|' .. settingsPage .. '|refresh=1}}' )
else
} )
args[1] = id
end
table.insert( list, p.sprite( args ) .. '</td><td><div class="sprite-id"><code>' .. id .. '</code></div>' )
local data = mw.loadData( idsPage )
local sections = {}
for _, sectionData in ipairs( data.sections or { 'Uncategorized' } ) do
local sectionTag = body:tag( 'div' ):addClass( 'spritedoc-section' ):attr( 'data-section-id', sectionData.id )
-- https://phabricator.wikimedia.org/T73594
sectionTag:wikitext( '<h3>', sectionData[1], '</h3>' )
sections[sectionData.id] = { boxes = sectionTag:tag( 'ul' ):addClass( 'spritedoc-boxes' ) }
end
local keyedData = {}
for name, idData in pairs( data.ids ) do
table.insert( keyedData, {
sortKey = mw.ustring.lower( name ),
name = name,
data = idData
} )
end
table.sort( keyedData, function( a, b )
return a.sortKey < b.sortKey
end )
for _, data in ipairs( keyedData ) do
local idData = data.data
local pos = idData.pos
local section = sections[idData.section]
local names = section[pos]
if not names then
local box = section.boxes:tag( 'li' ):addClass( 'spritedoc-box' ):attr( 'data-pos', pos )
box:tag( 'div' ):addClass( 'spritedoc-image' )
:wikitext( p.base{ pos = pos, settings = settingsPage } )
names = box:tag( 'ul' ):addClass( 'spritedoc-names' )
section[pos] = names
end
end
table.insert( list, '</td></tr></table></li>' )
local nameElem = mw.html.create( 'li' ):addClass( 'spritedoc-name' )
local codeElem = nameElem:tag( 'code' ):wikitext( data.name )
if i == #posKeys then
if idData.deprecated then
table.insert( list, listFoot )
codeElem:addClass( 'spritedoc-deprecated' )
end
end
names:wikitext( tostring( nameElem ) )
end
end
local out = table.concat( list )
if args.refresh then
if not args.refresh then
return '', tostring( body )
out = f:preprocess( '{{#widget:stylesheet|page=Sprite doc}}' ) .. '<div id="sprite-doc" data-details=\'{"name":"' .. args.name .. '","size":' .. ( args.size or 16 ) .. '}\'>' .. out .. '</div>'
end
end
return f:callParserFunction( '#widget:Stylesheet', { page = 'SpriteDoc' } ), tostring( body )
return out
end
end
return p
return p

Revision as of 14:11, 12 May 2017

Template-info.png Documentation

This module implements {{sprite}}. It should generally be invoked directly on template pages, rather than using the sprite template.

Parent arguments are automatically merged with directly passed arguments (the latter overwriting the former) and all arguments are normalised to trim whitespace and set empty arguments to nil.

Dependencies

See also


local p = {}
function p.base( f )
	local args = f
	if f == mw.getCurrentFrame() then 
		args = require( 'Module:ProcessArgs' ).merge( true )
	else
		f = mw.getCurrentFrame()
	end
	
	-- Default settings
	local default = {
		scale = 1,
		sheetsize = 256,
		size = 16,
		pos = 1,
		align = 'text-top'
	}
	
	local defaultStyle = default
	if args.settings then
		local settings = mw.loadData( 'Module:' .. args.settings )
		if not settings.stylesheet then
			-- Make a separate clone of the current default settings
			defaultStyle = mw.clone( default )
		end
		for k, v in pairs( settings ) do
			default[k] = v
		end
	end
	
	local setting = function( arg )
		return args[arg] or default[arg]
	end
	
	local sprite = mw.html.create( 'span' ):addClass( 'sprite' )
	sprite:tag( 'br' )
	
	-- mw.html's css method performs very slow escaping, which doubles the time it takes
	-- to run, so we'll construct the styles manually, and put them in the cssText
	-- method, which only does html escaping (which isn't slow)
	local styles = {}
	
	if setting( 'stylesheet' ) then
		sprite:addClass(
			setting( 'classname' ) or
			mw.ustring.lower( setting( 'name' ):gsub( ' ', '-' ) ) .. '-sprite'
		)
	else
		table.insert( styles, 'background-image:{{FileUrl|' .. (
			setting( 'image' ) or setting( 'name' ) .. 'Sprite.png'
		) .. '}}' )
	end
	local class = setting( 'class' )
	if class then
		sprite:addClass( class )
	end
	
	local size = setting( 'size' )
	local pos = math.abs( setting( 'pos' ) ) - 1
	local sheetWidth = setting( 'sheetsize' )
	local tiles = sheetWidth / size
	local left = pos % tiles * size
	local top = math.floor( pos / tiles ) * size
	local scale = setting( 'scale' )
	local autoScale = setting( 'autoscale' )
	
	if left > 0 or top > 0 then
		table.insert( styles, 'background-position:-' .. left * scale .. 'px -' .. top * scale .. 'px' )
	end
	if not autoScale and scale ~= defaultStyle.scale then
		table.insert( styles, 'background-size:' .. sheetWidth * scale .. 'px auto' )
	end
	if size ~= defaultStyle.size or ( not autoScale and scale ~= defaultStyle.scale ) then
		table.insert( styles, 'height:' .. size * scale .. 'px' )
		table.insert( styles, 'width:' .. size * scale .. 'px' )
	end
	local align = setting( 'align' )
	if align ~= defaultStyle.align then
		table.insert( styles, 'vertical-align:' .. align )
	end
	table.insert( styles, setting( 'css' ) )
	sprite:cssText( table.concat( styles, ';' ) )
	
	local text = setting( 'text' )
	local root
	local spriteText
	if text then
		root = mw.html.create( 'span' ):addClass( 'nowrap' )
		spriteText = mw.html.create( 'span' ):addClass( 'sprite-text' ):wikitext( text )
	end
	
	local title = setting( 'title' )
	if title then
		( root or sprite ):attr( 'title', title )
	end
	
	if not root then
		root = mw.html.create( '' )
	end
	root:node( sprite )
	if spriteText then
		root:node( spriteText )
	end
	
	local link = setting( 'link' ) or ''
	if link ~= '' and mw.ustring.lower( link ) ~= 'none' then
		-- External link
		if link:find( '//' ) then
			return '[' .. link .. ' ' .. tostring( root ) .. ']'
		end
		
		-- Internal link
		local linkPrefix = setting( 'linkprefix' ) or ''
		return '[[' .. linkPrefix .. link .. '|' .. tostring( root ) .. ']]'
	end
	
	return tostring( root )
end

function p.sprite( f )
	local args = f
	if f == mw.getCurrentFrame() then
		args = require( 'Module:ProcessArgs' ).merge( true )
	else
		f = mw.getCurrentFrame()
	end
	
	local categories = {}
	local idData = args.iddata
	if not idData then
		local default = {}
		if args.settings then
			default = mw.loadData( 'Module:' .. args.settings )
		end
		
		local name = args.name or default.name
		local ids = mw.loadData( 'Module:' .. ( args.ids or default.ids or name .. '/IDs' ) )
		ids = ids.ids or ids
		local id = mw.text.trim( tostring( args[1] or '' ) )
		idData = ids[id] or ids[mw.ustring.lower( id ):gsub( '[%s%+]', '-' )]
	end
	
	local title = mw.title.getCurrentTitle()
	-- Remove categories on language pages, talk pages, and in User/UserWiki/UserProfile namespaces
	local disallowCats = args.nocat or title.isTalkPage or title.nsText:find( '^User' )
	if idData then
		if type( idData ) == 'table' then
			if idData.deprecated and not disallowCats then
				table.insert( categories, f:expandTemplate{ title = 'Translation category', args = { 'Pages using deprecated sprite names', project = 0 } } )
			end
			
			args.pos = idData.pos
		else
			args.pos = idData
		end
	elseif not disallowCats then
		table.insert( categories, f:expandTemplate{ title = 'Translation category', args = { 'Pages with missing sprites', project = 0 } } )
	end
	
	return p.base( args ), table.concat( categories, '' )
end

function p.link( f )
	local args = f
	if f == mw.getCurrentFrame() then
		args = require( 'Module:ProcessArgs' ).merge( true )
	end
	
	local link = args[1]
	if args[1] and not args.id then
		link = args[1]:match( '^(.-)%+' ) or args[1]
	end
	local text = args.text or args[2] or link
	
	args[1] = args.id or args[1]
	args.link = args.link or link
	args.text = text
	
	return p.sprite( args )
end

function p.doc( f )
	local args = f
	if f == mw.getCurrentFrame() then
		args = f.args
	else
		f = mw.getCurrentFrame()
	end
	local settingsPage = mw.text.trim( args[1] )
	local settings = mw.loadData( 'Module:' .. settingsPage )
	local idsPage = 'Module:' .. ( settings.ids or settings.name .. '/IDs' )
	
	local getProtection = function( title, action, extra )
		local protections = { 'edit' }
		if extra then
			table.insert( protections, extra )
		end
		
		local addProtection = function( protection )
			if protection == 'autoconfirmed' then
				protection = 'editsemiprotected'
			elseif protection == 'sysop' then
				protection = 'editprotected'
			end
			
			table.insert( protections, protection )
		end
		
		local direct = title.protectionLevels[action]
		for _, protection in ipairs( direct ) do
			addProtection( protection )
		end
		local cascading = title.cascadingProtection.restrictions[action] or {}
		if #cascading > 0 then
			table.insert( protections, 'protect' )
		end
		for _, protection in ipairs( cascading ) do
			addProtection( protection )
		end
		
		return table.concat( protections, ',' )
	end
	
	local body
	if args.refresh then
		body = mw.html.create( '' )
	else
		local idsTitle = mw.title.new( idsPage )
		local spritesheet = settings.image or settings.name .. 'Sprite.png'
		local spriteTitle = mw.title.new( 'File:' .. spritesheet )
		local idsProtection = getProtection( idsTitle, 'edit' )
		local spriteProtection = getProtection( spriteTitle, 'upload', 'upload,reupload' )
		body = mw.html.create( 'div' ):attr( {
			id = 'spritedoc',
			['data-idspage'] = idsTitle.id,
			['data-idsprotection'] = idsProtection,
			['data-idstimestamp'] = f:callParserFunction( 'REVISIONTIMESTAMP', idsPage ),
			['data-spritesheet'] = spritesheet,
			['data-spriteprotection'] = spriteProtection,
			['data-pos'] = settings.pos or 1,
			['data-refreshtext'] = mw.text.nowiki( '{{#invoke:sprite|doc|' .. settingsPage .. '|refresh=1}}' )
		} )
	end
	
	local data = mw.loadData( idsPage )
	
	local sections = {}
	for _, sectionData in ipairs( data.sections or { 'Uncategorized' } ) do
		local sectionTag = body:tag( 'div' ):addClass( 'spritedoc-section' ):attr( 'data-section-id', sectionData.id )
		-- https://phabricator.wikimedia.org/T73594
		sectionTag:wikitext( '<h3>', sectionData[1], '</h3>' )
		sections[sectionData.id] = { boxes = sectionTag:tag( 'ul' ):addClass( 'spritedoc-boxes' ) }
	end
	
	local keyedData = {}
	for name, idData in pairs( data.ids ) do
		table.insert( keyedData, {
			sortKey = mw.ustring.lower( name ),
			name = name,
			data = idData
		} )
	end
	table.sort( keyedData, function( a, b )
		return a.sortKey < b.sortKey
	end )
	
	for _, data in ipairs( keyedData ) do
		local idData = data.data
		local pos = idData.pos
		local section = sections[idData.section]
		local names = section[pos]
		if not names then
			local box = section.boxes:tag( 'li' ):addClass( 'spritedoc-box' ):attr( 'data-pos', pos )
			box:tag( 'div' ):addClass( 'spritedoc-image' )
				:wikitext( p.base{ pos = pos, settings = settingsPage } )
			
			names = box:tag( 'ul' ):addClass( 'spritedoc-names' )
			section[pos] = names
		end
		local nameElem = mw.html.create( 'li' ):addClass( 'spritedoc-name' )
		local codeElem = nameElem:tag( 'code' ):wikitext( data.name )
		
		if idData.deprecated then
			codeElem:addClass( 'spritedoc-deprecated' )
		end
		names:wikitext( tostring( nameElem ) )
	end
	
	if args.refresh then
		return '', tostring( body )
	end
	return f:callParserFunction( '#widget:Stylesheet', { page = 'SpriteDoc' } ), tostring( body )
end
return p


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