Module:Sprite: Difference between revisions

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 args.settings then
if settings then
local settings = mw.loadData( 'Module:' .. args.settings )
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' )
sprite:tag( 'br' )
if setting( 'stylesheet' ) then
-- mw.html's css method performs very slow escaping, which doubles the time it takes
sprite:addClass(
-- to run, so we'll construct the styles manually, and put them in the cssText
setting( 'classname' ) or
-- method, which only does html escaping (which isn't slow)
mw.ustring.lower( setting( 'name' ):gsub( ' ', '-' ) ) .. '-sprite'
local styles = {}
)
else
-- for tint
sprite:css(
local classname = setting( 'classname' ) or mw.ustring.lower( setting( 'name' ):gsub( ' ', '-' ) ) .. '-sprite'
'background-image',
local css_image = "background"
'{{FileUrl|' .. ( setting( 'image' ) or setting( 'name' ) .. 'Sprite.png' ) .. '}}'
if setting( 'formask' ) then
)
classname = classname .. '-mask'
css_image = "mask"
end
end
if setting( 'class' ) then
sprite:addClass( setting( 'class' ) )
sprite:addClass( classname )
local class = setting( 'class' )
if class then
sprite:addClass( class )
end
end
local size = setting( 'size' )
local width = setting( 'width' ) or setting( 'size' )
local pos = math.abs( setting( 'pos' ) ) - 1
local height = setting( 'height' ) or setting( 'size' )
local tiles = setting( 'sheetsize' ) / size
local sheetWidth = setting( 'sheetsize' )
local left = pos % tiles * size
local tiles = sheetWidth / width
local top = math.floor( pos / tiles ) * size
local pos = setting( 'pos' ) - 1
local scale = setting( 'scale' )
local scale = setting( 'scale' )
if left > 0 or top > 0 then
local autoScale = setting( 'autoscale' )
sprite:css( 'background-position', '-' .. left * scale .. 'px -' .. top * scale .. 'px' )
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 setting( 'autoscale' ) and scale ~= defaultStyle.scale then
sprite:css( 'background-size', setting( 'sheetsize' ) * scale .. 'px auto' )
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 setting( 'autoscale' ) and scale ~= defaultStyle.scale ) then
if height ~= defaultStyle.size or width ~= defaultStyle.size or ( not autoScale and scale ~= defaultStyle.scale ) then
sprite:css( 'height', size * scale .. 'px;width:' .. size * scale .. 'px' )
styles[#styles + 1] = 'height:' .. height * scale .. 'px'
styles[#styles + 1] = 'width:' .. width * scale .. 'px'
end
end
if setting( 'align' ) ~= defaultStyle.align then
sprite:css( 'vertical-align', setting( 'align' ) )
local align = setting( 'align' )
end
if align ~= defaultStyle.align then
if css then
styles[#styles + 1] = 'vertical-align:' .. align
sprite:cssText( css )
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 setting( 'text' ) then
if text then
root = mw.html.create( 'span' ):addClass( 'nowrap' )
if not args['wrap'] then
spriteText = mw.html.create( 'span' ):addClass( 'sprite-text' ):wikitext( setting( 'text' ) )
root = mw.html.create( 'span' ):addClass( 'nowrap' )
end
spriteText = mw.html.create( 'span' ):addClass( 'sprite-text' ):wikitext( text )
end
end
if setting( 'title' ) then
local title = setting( 'title' )
( root or sprite ):attr( 'title', setting( '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 tonumber( args[1] ) then
local idData = args.iddata
args.pos = args[1]
if not idData then
table.insert( categories, '[[Category:Pages using sprite positions]]' )
local name = args.name or data.settings.name
else
local id = mw.text.trim( tostring( args[1] or '' ) )
local idData = args.iddata
idData = data.ids[id] or data.ids[mw.ustring.lower( id ):gsub( '[%s%+]', '-' )]
if not idData then
end
local default = {}
if args.settings then
local title = mw.title.getCurrentTitle()
default = mw.loadData( 'Module:' .. args.settings )
-- 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
local name = args.name or default.name
local ids = mw.loadData( 'Module:' .. ( args.ids or default.ids or name .. '/IDs' ) ).ids
local id = mw.text.trim( args[1] or '' )
idData = ids[id] or ids[mw.ustring.lower( id ):gsub( '[%s%+]', '-' )]
end
end
local allowCats = not mw.title.getCurrentTitle().isSubpage
args.pos = idData.pos
if not idData and allowCats then
elseif not disallowCats then
table.insert( categories, '[[Category:Pages with missing sprites]]' )
categories[#categories + 1] = f:expandTemplate{ title = 'Translation category', args = { 'Pages with missing sprites', project = 0 } }
else
if idData.deprecated and allowCats then
table.insert( categories, '[[Category:Pages using deprecated sprite names]]' )
end
args.pos = idData.pos
end
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 settingsPage = mw.text.trim( args[1] )
local dataPage = mw.text.trim( args[1] )
local settings = mw.loadData( 'Module:' .. settingsPage )
local data = mw.loadData( 'Module:' .. dataPage )
local idsPage = 'Module:' .. ( settings.ids or settings.name .. '/IDs' )
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 body
local spriteStyle = ''
if args.refresh then
if data.settings.url and data.settings.url.style then
body = mw.html.create()
spriteStyle = data.settings.url.style
else
local spriteSheet = settings.image or settings.name .. 'Sprite.png'
body = mw.html.create( 'div' ):attr( {
id = 'spritedoc',
['data-idspage'] = '{{PAGEID:' .. idsPage .. '}}',
['data-idstimestamp'] = '{{REVISIONTIMESTAMP:' .. idsPage .. '}}',
['data-spritesheet'] = spriteSheet,
['data-pos'] = settings.pos or 1,
['data-refreshtext'] = mw.text.nowiki( '{{#invoke:sprite2|doc|' .. settingsPage .. '|refresh=1}}' )
} )
end
end
local data = mw.loadData( idsPage )
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 )
-- https://phabricator.wikimedia.org/T73594
sectionTag:tag( 'h3' ):wikitext( sectionData.name )
sectionTag:wikitext( '<h3>', sectionData[1], '</h3>' )
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
table.insert( keyedData, {
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, settings = settingsPage } )
: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
return f:callParserFunction( '#widget:Stylesheet', { page = 'SpriteDoc' } ), tostring( body )
local styles = f:extensionTag( 'templatestyles', '', { src = 'Sprite/doc.css' } )
return styles, spriteStyle, tostring( body )
end
end
return p
return p