Jump to: navigation, search

Module:Sprite

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.