Module:Sprite: Difference between revisions
Jump to navigation
Jump to search
mw.html seems to use a lot of memory. Converting elements to wikitext string where possible before inserting them into an existing HtmlBuilder significantly reduces that memory usage for some reason, even though that just happens later anyway on output. |
m 91 revisions imported |
||
(38 intermediate revisions by 6 users not shown) | |||
Line 7: | Line 7: | ||
f = mw.getCurrentFrame() | f = mw.getCurrentFrame() | ||
end | end | ||
local data = args.data and mw.loadData( 'Module:' .. args.data ) or {} | |||
local settings = data.settings | |||
-- Default settings | -- Default settings | ||
Line 18: | Line 21: | ||
local defaultStyle = default | local defaultStyle = default | ||
if | if settings then | ||
if not settings.stylesheet then | if not settings.stylesheet then | ||
-- Make a separate clone of the current default settings | -- Make a separate clone of the current default settings | ||
Line 34: | Line 36: | ||
local sprite = mw.html.create( 'span' ):addClass( 'sprite' ) | local sprite = mw.html.create( 'span' ):addClass( 'sprite' ) | ||
-- 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 = {} | |||
-- for tint | |||
local classname = setting( 'classname' ) or mw.ustring.lower( setting( 'name' ):gsub( ' ', '-' ) ) .. '-sprite' | |||
local css_image = "background" | |||
if setting( 'formask' ) then | |||
classname = classname .. '-mask' | |||
css_image = "mask" | |||
end | end | ||
sprite:addClass( | sprite:addClass( classname ) | ||
local class = setting( 'class' ) | |||
if class then | |||
sprite:addClass( class ) | |||
end | end | ||
local | local width = setting( 'width' ) or setting( 'size' ) | ||
local | local height = setting( 'height' ) or setting( 'size' ) | ||
local | local sheetWidth = setting( 'sheetsize' ) | ||
local | local tiles = sheetWidth / width | ||
local | local pos = setting( 'pos' ) - 1 | ||
local scale = setting( 'scale' ) | local scale = setting( 'scale' ) | ||
if left | local autoScale = setting( 'autoscale' ) | ||
if pos then | |||
local left = pos % tiles * width * scale | |||
local top = math.floor( pos / tiles ) * height * scale | |||
if css_image == 'mask' then | |||
styles[#styles + 1] = '-webkit-mask-position:-' .. left .. 'px -' .. top .. 'px' | |||
end | |||
styles[#styles + 1] = css_image .. '-position:-' .. left .. 'px -' .. top .. 'px' | |||
end | end | ||
if not | |||
if not autoScale and scale ~= defaultStyle.scale then | |||
if css_image == 'mask' then | |||
styles[#styles + 1] = '-webkit-mask-size:' .. sheetWidth * scale .. 'px auto' | |||
end | |||
styles[#styles + 1] = css_image .. '-size:' .. sheetWidth * scale .. 'px auto' | |||
end | end | ||
if size ~= defaultStyle.size or ( not | if height ~= defaultStyle.size or width ~= defaultStyle.size or ( not autoScale and scale ~= defaultStyle.scale ) then | ||
styles[#styles + 1] = 'height:' .. height * scale .. 'px' | |||
styles[#styles + 1] = 'width:' .. width * scale .. 'px' | |||
end | end | ||
local align = setting( 'align' ) | |||
if align ~= defaultStyle.align then | |||
styles[#styles + 1] = 'vertical-align:' .. align | |||
end | end | ||
styles[#styles + 1] = setting( 'css' ) | |||
sprite:cssText( table.concat( styles, ';' ) ) | |||
local text = setting( 'text' ) | |||
local root | local root | ||
local spriteText | local spriteText | ||
if | if text then | ||
if not args['wrap'] then | |||
spriteText = mw.html.create( 'span' ):addClass( 'sprite-text' ):wikitext( | root = mw.html.create( 'span' ):addClass( 'nowrap' ) | ||
end | |||
spriteText = mw.html.create( 'span' ):addClass( 'sprite-text' ):wikitext( text ) | |||
end | end | ||
local title = setting( 'title' ) | |||
( root or sprite ):attr( 'title', | if title then | ||
( root or sprite ):attr( 'title', title ) | |||
end | end | ||
Line 92: | Line 115: | ||
end | end | ||
local link = setting( 'link' ) | local link = setting( 'link' ) or '' | ||
if link and mw.ustring.lower( link ) ~= 'none' then | if link ~= '' and mw.ustring.lower( link ) ~= 'none' then | ||
-- External link | -- External link | ||
if link:find( '//' ) then | if link:find( '//' ) then | ||
Line 111: | Line 134: | ||
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 data = args.data and mw.loadData( 'Module:' .. args.data ) or {} | |||
local categories = {} | local categories = {} | ||
if | local idData = args.iddata | ||
args. | if not idData then | ||
local name = args.name or data.settings.name | |||
local id = mw.text.trim( tostring( args[1] or '' ) ) | |||
idData = data.ids[id] or data.ids[mw.ustring.lower( id ):gsub( '[%s%+]', '-' )] | |||
if | end | ||
if | 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 idData.deprecated then | |||
args.class = ( args.class or '' ) .. ' sprite-deprecated' | |||
if not disallowCats then | |||
categories[#categories + 1] = f:expandTemplate{ title = 'Translation category', args = { 'Pages using deprecated sprite names', project = 0 } } | |||
end | end | ||
end | end | ||
args.pos = idData.pos | |||
elseif not disallowCats then | |||
categories[#categories + 1] = f:expandTemplate{ title = 'Translation category', args = { 'Pages with missing sprites', project = 0 } } | |||
end | end | ||
return p.base( args ), table.concat( categories | return p.base( args ), table.concat( categories ) | ||
end | end | ||
Line 156: | Line 176: | ||
link = args[1]:match( '^(.-)%+' ) or args[1] | link = args[1]:match( '^(.-)%+' ) or args[1] | ||
end | end | ||
local text = args.text or args[2] or link | local text | ||
if not args.notext then | |||
text = args.text or args[2] or link | |||
end | |||
args[1] = args.id or args[1] | args[1] = args.id or args[1] | ||
Line 172: | Line 195: | ||
f = mw.getCurrentFrame() | f = mw.getCurrentFrame() | ||
end | end | ||
local | local dataPage = mw.text.trim( args[1] ) | ||
local | local data = mw.loadData( 'Module:' .. dataPage ) | ||
local | |||
local getProtection = function( title, action, extra ) | |||
local protections = { 'edit' } | |||
if extra then | |||
protections[#protections + 1] = extra | |||
end | |||
local addProtection = function( protection ) | |||
if protection == 'autoconfirmed' then | |||
protection = 'editsemiprotected' | |||
elseif protection == 'sysop' then | |||
protection = 'editprotected' | |||
end | |||
protections[#protections + 1] = protection | |||
end | |||
local direct = title.protectionLevels[action] or {} | |||
for _, protection in ipairs( direct ) do | |||
addProtection( protection ) | |||
end | |||
local cascading = title.cascadingProtection.restrictions[action] or {} | |||
if #cascading > 0 then | |||
protections[#protections + 1] = 'protect' | |||
end | |||
for _, protection in ipairs( cascading ) do | |||
addProtection( protection ) | |||
end | |||
return table.concat( protections, ',' ) | |||
end | |||
local | local spriteStyle = '' | ||
if | if data.settings.url and data.settings.url.style then | ||
spriteStyle = data.settings.url.style | |||
end | end | ||
local data = mw. | local dataTitle = mw.title.new( 'Module:' .. dataPage ) | ||
-- Temporary until this is updated | |||
local spritesheet = data.settings.image or data.settings.name .. 'Sprite.png' | |||
local spriteTitle = mw.title.new( 'File:' .. spritesheet ) | |||
local dataProtection = getProtection( dataTitle, 'edit' ) | |||
local spriteProtection = getProtection( spriteTitle, 'upload', 'upload,reupload' ) | |||
local body = mw.html.create( 'div' ):attr( { | |||
id = 'spritedoc', | |||
['data-dataprotection'] = dataProtection, | |||
['data-datatimestamp'] = f:callParserFunction( 'REVISIONTIMESTAMP', 'Module:' .. dataPage ), | |||
['data-datapage'] = 'Module:' .. dataPage, | |||
['data-spritesheet'] = spritesheet, | |||
['data-spriteprotection'] = spriteProtection, | |||
['data-refreshtext'] = mw.text.nowiki( '{{#invoke:sprite|doc|' .. dataPage .. '|refresh=1}}' ), | |||
['data-settings'] = mw.text.jsonEncode( data.settings ), | |||
} ) | |||
local sections = {} | local sections = {} | ||
for _, sectionData in ipairs( data.sections or { 'Uncategorized' } ) do | for _, sectionData in ipairs( data.sections or { name = 'Uncategorized' } ) do | ||
local sectionTag = body:tag( 'div' ):addClass( 'spritedoc-section' ):attr( 'data-section-id', sectionData.id ) | local sectionTag = body:tag( 'div' ):addClass( 'spritedoc-section' ):attr( 'data-section-id', sectionData.id ) | ||
sectionTag:tag( 'h3' ):wikitext( sectionData.name ) | |||
sectionTag: | |||
sections[sectionData.id] = { boxes = sectionTag:tag( 'ul' ):addClass( 'spritedoc-boxes' ) } | sections[sectionData.id] = { boxes = sectionTag:tag( 'ul' ):addClass( 'spritedoc-boxes' ) } | ||
end | end | ||
local keyedData = {} | local keyedData = {} | ||
local i = 1 | |||
for name, idData in pairs( data.ids ) do | for name, idData in pairs( data.ids ) do | ||
keyedData[i] = { | |||
sortKey = mw.ustring.lower( name ), | sortKey = mw.ustring.lower( name ), | ||
name = name, | name = name, | ||
data = idData | data = idData | ||
} | } | ||
i = i + 1 | |||
end | end | ||
table.sort( keyedData, function( a, b ) | table.sort( keyedData, function( a, b ) | ||
Line 221: | Line 280: | ||
local box = section.boxes:tag( 'li' ):addClass( 'spritedoc-box' ):attr( 'data-pos', pos ) | local box = section.boxes:tag( 'li' ):addClass( 'spritedoc-box' ):attr( 'data-pos', pos ) | ||
box:tag( 'div' ):addClass( 'spritedoc-image' ) | box:tag( 'div' ):addClass( 'spritedoc-image' ) | ||
:wikitext( p.base{ pos = pos, | :wikitext( p.base{ pos = pos, data = dataPage, nourl = spriteStyle ~= '' } ) | ||
names = box:tag( 'ul' ):addClass( 'spritedoc-names' ) | names = box:tag( 'ul' ):addClass( 'spritedoc-names' ) | ||
Line 236: | Line 295: | ||
if args.refresh then | if args.refresh then | ||
return tostring( body ) | return '', '', tostring( body ) | ||
end | end | ||
local styles = f:extensionTag( 'templatestyles', '', { src = 'Sprite/doc.css' } ) | |||
return styles, spriteStyle, tostring( body ) | |||
end | end | ||
return p | return p |
Latest revision as of 12:26, 4 July 2024
Documentation for this module may be created at Module:Sprite/doc
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
local data = args.data and mw.loadData( 'Module:' .. args.data ) or {}
local settings = data.settings
-- Default settings
local default = {
scale = 1,
sheetsize = 256,
size = 16,
pos = 1,
align = 'text-top'
}
local defaultStyle = default
if settings then
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' )
-- 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 = {}
-- for tint
local classname = setting( 'classname' ) or mw.ustring.lower( setting( 'name' ):gsub( ' ', '-' ) ) .. '-sprite'
local css_image = "background"
if setting( 'formask' ) then
classname = classname .. '-mask'
css_image = "mask"
end
sprite:addClass( classname )
local class = setting( 'class' )
if class then
sprite:addClass( class )
end
local width = setting( 'width' ) or setting( 'size' )
local height = setting( 'height' ) or setting( 'size' )
local sheetWidth = setting( 'sheetsize' )
local tiles = sheetWidth / width
local pos = setting( 'pos' ) - 1
local scale = setting( 'scale' )
local autoScale = setting( 'autoscale' )
if pos then
local left = pos % tiles * width * scale
local top = math.floor( pos / tiles ) * height * scale
if css_image == 'mask' then
styles[#styles + 1] = '-webkit-mask-position:-' .. left .. 'px -' .. top .. 'px'
end
styles[#styles + 1] = css_image .. '-position:-' .. left .. 'px -' .. top .. 'px'
end
if not autoScale and scale ~= defaultStyle.scale then
if css_image == 'mask' then
styles[#styles + 1] = '-webkit-mask-size:' .. sheetWidth * scale .. 'px auto'
end
styles[#styles + 1] = css_image .. '-size:' .. sheetWidth * scale .. 'px auto'
end
if height ~= defaultStyle.size or width ~= defaultStyle.size or ( not autoScale and scale ~= defaultStyle.scale ) then
styles[#styles + 1] = 'height:' .. height * scale .. 'px'
styles[#styles + 1] = 'width:' .. width * scale .. 'px'
end
local align = setting( 'align' )
if align ~= defaultStyle.align then
styles[#styles + 1] = 'vertical-align:' .. align
end
styles[#styles + 1] = setting( 'css' )
sprite:cssText( table.concat( styles, ';' ) )
local text = setting( 'text' )
local root
local spriteText
if text then
if not args['wrap'] then
root = mw.html.create( 'span' ):addClass( 'nowrap' )
end
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 data = args.data and mw.loadData( 'Module:' .. args.data ) or {}
local categories = {}
local idData = args.iddata
if not idData then
local name = args.name or data.settings.name
local id = mw.text.trim( tostring( args[1] or '' ) )
idData = data.ids[id] or data.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 idData.deprecated then
args.class = ( args.class or '' ) .. ' sprite-deprecated'
if not disallowCats then
categories[#categories + 1] = f:expandTemplate{ title = 'Translation category', args = { 'Pages using deprecated sprite names', project = 0 } }
end
end
args.pos = idData.pos
elseif not disallowCats then
categories[#categories + 1] = 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
if not args.notext then
text = args.text or args[2] or link
end
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 dataPage = mw.text.trim( args[1] )
local data = mw.loadData( 'Module:' .. dataPage )
local getProtection = function( title, action, extra )
local protections = { 'edit' }
if extra then
protections[#protections + 1] = extra
end
local addProtection = function( protection )
if protection == 'autoconfirmed' then
protection = 'editsemiprotected'
elseif protection == 'sysop' then
protection = 'editprotected'
end
protections[#protections + 1] = protection
end
local direct = title.protectionLevels[action] or {}
for _, protection in ipairs( direct ) do
addProtection( protection )
end
local cascading = title.cascadingProtection.restrictions[action] or {}
if #cascading > 0 then
protections[#protections + 1] = 'protect'
end
for _, protection in ipairs( cascading ) do
addProtection( protection )
end
return table.concat( protections, ',' )
end
local spriteStyle = ''
if data.settings.url and data.settings.url.style then
spriteStyle = data.settings.url.style
end
local dataTitle = mw.title.new( 'Module:' .. dataPage )
-- Temporary until this is updated
local spritesheet = data.settings.image or data.settings.name .. 'Sprite.png'
local spriteTitle = mw.title.new( 'File:' .. spritesheet )
local dataProtection = getProtection( dataTitle, 'edit' )
local spriteProtection = getProtection( spriteTitle, 'upload', 'upload,reupload' )
local body = mw.html.create( 'div' ):attr( {
id = 'spritedoc',
['data-dataprotection'] = dataProtection,
['data-datatimestamp'] = f:callParserFunction( 'REVISIONTIMESTAMP', 'Module:' .. dataPage ),
['data-datapage'] = 'Module:' .. dataPage,
['data-spritesheet'] = spritesheet,
['data-spriteprotection'] = spriteProtection,
['data-refreshtext'] = mw.text.nowiki( '{{#invoke:sprite|doc|' .. dataPage .. '|refresh=1}}' ),
['data-settings'] = mw.text.jsonEncode( data.settings ),
} )
local sections = {}
for _, sectionData in ipairs( data.sections or { name = 'Uncategorized' } ) do
local sectionTag = body:tag( 'div' ):addClass( 'spritedoc-section' ):attr( 'data-section-id', sectionData.id )
sectionTag:tag( 'h3' ):wikitext( sectionData.name )
sections[sectionData.id] = { boxes = sectionTag:tag( 'ul' ):addClass( 'spritedoc-boxes' ) }
end
local keyedData = {}
local i = 1
for name, idData in pairs( data.ids ) do
keyedData[i] = {
sortKey = mw.ustring.lower( name ),
name = name,
data = idData
}
i = i + 1
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, data = dataPage, nourl = spriteStyle ~= '' } )
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
local styles = f:extensionTag( 'templatestyles', '', { src = 'Sprite/doc.css' } )
return styles, spriteStyle, tostring( body )
end
return p