Модуль:TemplateDataDoc: различия между версиями

Материал из Тептар — свободной энциклопедии
Перейти к навигации Перейти к поиску
(Керла агӀо: «local docSubPage = mw.message.new( 'Templatedata-doc-subpage' ):plain(); p = {}; function getTemplateData( pageName ) local title = mw.title.makeTitle( 0, pageName );...»)
 
Нет описания правки
 
(не показана 1 промежуточная версия этого же участника)
Строка 1: Строка 1:
local docSubPage = mw.message.new( 'Templatedata-doc-subpage' ):plain();
local docSubPage = mw.message.new( 'Templatedata-doc-subpage' ):plain();


p = {};
local p = {};


function getTemplateData( pageName )
local lastNumber = 0;
local title = mw.title.makeTitle( 0, pageName );
 
if not title or not title.exists or not title:getContent() then
-- Enable/disable additional spacing for block-formatted templates
return nil;
local formatBlockSpaces = true;
end;


local rawData = mw.ustring.match( title:getContent(), '<templatedata%s*>(.*)</templatedata%s*>' );
local noDocNote = 'TemplateDataDoc: Запишите страницу для отображения заполненного шаблона.';
if not rawData then
return nil;
end


local status, data = pcall( mw.text.jsonDecode, rawData );
function p.processJson( json )
local status, data = pcall( mw.text.jsonDecode, json );
if status == false then
if status == false then
return nil;
return nil;
Строка 30: Строка 27:
'nocoord',
'nocoord',
'nocatcoord',
'nocatcoord',
'Ширина',
'ширина',
'Ширина изображения',
'ширина изображения',
'Ширина логотипа',
'ширина логотипа',
};
};
for _, param in ipairs( deprecatedParams ) do
for _, param in ipairs( deprecatedParams ) do
if data[ 'params' ][ param ] ~= nil then
if data[ 'params' ][ param ] ~= nil then
data[ 'params' ][ param ][ 'deprecated' ] = 'Бакъон бух бало яц.';
data[ 'params' ][ param ][ 'deprecated' ] = '-';
end
end
end
end
Строка 40: Строка 43:
end
end


function p.generateBlank( frame )
function p.getTemplateData( pageName )
local frame = mw.getCurrentFrame();
local title = mw.title.makeTitle( 0, pageName );
local getArgs = require( 'Module:Arguments' ).getArgs;
if not title or not title.exists or not title:getContent() then
local args = getArgs( frame );
return false;
local templateName = frame.args[ 1 ];
end;
table.remove( args, 1 );
 
local json = mw.ustring.match( title:getContent(), '<[Tt]emplate[Dd]ata%s*>(.*)</[Tt]emplate[Dd]ata%s*>' );
if not json then
return nil;
end
local docPage = 'Template:' .. templateName .. '/' .. docSubPage;
return p.processJson( json )
local templateData = getTemplateData( docPage );
end


local paramaterLength = 0;
function p.getValue( data, key )
for i, parameterName in ipairs( templateData[ 'paramOrder' ] ) do
if data[ key ] then
local parameterData = templateData[ 'params' ][ parameterName ] or {};
return data[ key ];
if not parameterData[ 'deprecated' ] then
end
local length = mw.ustring.len( parameterName );
if length > paramaterLength then
-- Numbered keys return as numbers
paramaterLength = length;
local nkey = tonumber( key );
end
if nkey ~= nil and data[ nkey ] then
end
return data[ nkey ];
end
end
return {};
end


-- See https://phabricator.wikimedia.org/diffusion/ETDA/browse/master/Specification.md?as=remarkup
-- See https://phabricator.wikimedia.org/diffusion/ETDA/browse/master/Specification.md?as=remarkup
-- We need a global format value for the 'block' and 'inline': [[phab:T205438]]
-- We need a global format value for the 'block' and 'inline': [[phab:T205438]]
local templateFormat = templateData[ 'format' ];
function p.convertFormatString( rawTemplateFormat )
local templateFormat = rawTemplateFormat or 'inline';
local isBlockFormatted = false;
local isBlockFormatted = false;
if templateFormat == 'block' then
if templateFormat == 'block' then
Строка 71: Строка 82:
templateFormat = '{{_|_=_}}';
templateFormat = '{{_|_=_}}';
end
end
return templateFormat, isBlockFormatted;
end
function p.getFormatParts( rawTemplateFormat, templateName )
local templateFormat, isBlockFormatted = p.convertFormatString( rawTemplateFormat );
local nameFormat = mw.ustring.match( templateFormat, '^[^|]+' );
local nameFormat = mw.ustring.match( templateFormat, '^[^|]+' );
local paramKeyFormat = mw.ustring.match( templateFormat, '%|[^=]+=' );
local paramKeyFormat = mw.ustring.match( templateFormat, '%|[^=]+=' );
Строка 76: Строка 93:
paramValueFormat = mw.ustring.sub( paramValueFormat, 2 );
paramValueFormat = mw.ustring.sub( paramValueFormat, 2 );
local endFormat = mw.ustring.match( templateFormat, '%}%}.*$' );
local endFormat = mw.ustring.match( templateFormat, '%}%}.*$' );
local startFormat = mw.ustring.gsub( nameFormat, '_', templateName );
return isBlockFormatted, startFormat, endFormat, paramKeyFormat, paramValueFormat;
end


local out = mw.ustring.gsub( nameFormat, '_', templateName );
function p.formatKeyValue( key, parameterData, formatData )
 
if parameterData[ 'deprecated' ] then
local lastNumber = 0;
return '';
for i, parameterName in ipairs( templateData[ 'paramOrder' ] ) do
end
local parameterData = templateData[ 'params' ][ parameterName ] or {};
local args = formatData.args;
if parameterData[ 'inherits' ] then
local parameterName = key;
parameterData = templateData[ 'params' ][ parameterData[ 'inherits' ] ] or {};
local nkey = tonumber(key);
-- Add additional spacing to string keys
if formatBlockSpaces and formatData.isBlockFormatted then
if nkey == nil or lastNumber ~= nkey - 1 then
while mw.ustring.len( key ) < formatData.parameterLength do
key = key .. ' ';
end
end
end
end
if not parameterData[ 'deprecated' ] then
local key = parameterName;
local nkey = tonumber(key);
if isBlockFormatted then
if nkey == nil or lastNumber ~= nkey - 1 then
while mw.ustring.len( key ) < paramaterLength do
key = key .. ' ';
end
end
end
if nkey ~= nil and lastNumber == nkey - 1 then
key = '';
lastNumber = nkey;
end
local value = '';
-- Remove numbering for adjacent numbered keys
if args[ 'description' ] == '1' and parameterData[ 'description' ] then
if nkey ~= nil and lastNumber == nkey - 1 then
value = parameterData[ 'description' ];
key = '';
if value ~= '' then
lastNumber = nkey;
value = '&lt;!-- ' .. value .. ' --&gt;';
end
end
 
elseif parameterData[ 'autovalue' ] then
local value = '';
value = parameterData[ 'autovalue' ];
if formatData.valueKey == 'example' and parameterData[ 'example' ] then
end
-- Example
if args[ parameterName ] and args[ parameterName ] ~= '' then
value = parameterData[ 'example' ];
value = args[ parameterName ];
else
if formatData.valueKey == 'description' and parameterData[ 'description' ] then
-- Description
value = parameterData[ 'description' ];
if value ~= '' then
value = '&lt;!-- ' .. value .. ' --&gt;';
end
end
elseif parameterData[ 'autovalue' ] then
-- Autovalue
value = parameterData[ 'autovalue' ];
end
if args[ '$' .. parameterName ] and args[ '$' .. parameterName ] ~= '' then
-- Custom values from template call
value = args[ '$' .. parameterName ];
end
end
local formattedKey = mw.ustring.gsub( formatData.paramKeyFormat, '_+', key, 1 );
if key == '' then
formattedKey = mw.ustring.gsub( formattedKey, '=', '' );
end
return formattedKey .. mw.ustring.gsub( formatData.paramValueFormat, '_', value, 1 );
end
function p.generateBlankCode( templateData, templateName, args )
if templateData == false then
return '{{' .. templateName .. '}}';
end
local formattedKey = mw.ustring.gsub( paramKeyFormat, '_+', key, 1 );
local parameterLength = 0;
if key == '' then
for i, parameterName in ipairs( templateData[ 'paramOrder' ] ) do
formattedKey = mw.ustring.gsub( formattedKey, '=', '' );
local parameterData = p.getValue( templateData[ 'params' ], parameterName );
if not parameterData[ 'deprecated' ] then
local length = mw.ustring.len( parameterName );
if length > parameterLength then
parameterLength = length;
end
end
out = out .. formattedKey;
end
end
 
local isBlockFormatted, startFormat, endFormat, paramKeyFormat, paramValueFormat = p.getFormatParts( templateData[ 'format' ], templateName );
local out = startFormat;


out = out .. mw.ustring.gsub( paramValueFormat, '_', value, 1 );
lastNumber = 0;
for i, parameterName in ipairs( templateData[ 'paramOrder' ] ) do
local parameterData = p.getValue( templateData[ 'params' ], parameterName );
if parameterData[ 'inherits' ] then
parameterData = p.getValue( templateData[ 'params' ], parameterData[ 'inherits' ] );
end
end
out = out .. p.formatKeyValue( parameterName, parameterData, {
args = args,
valueKey = ( args[ 'description' ] and 'description' or nil ),
isBlockFormatted = isBlockFormatted,
parameterLength = parameterLength,
paramKeyFormat = paramKeyFormat,
paramValueFormat = paramValueFormat,
} );
end
end
out = out .. endFormat;
return out .. endFormat;
 
return frame:extensionTag{ name = 'pre', content = out };
end
end


function p.generateExample( frame )
function p.generateBlank( frame )
local frame = mw.getCurrentFrame();
local frame = mw.getCurrentFrame();
local args = frame.args;
local getArgs = require( 'Module:Arguments' ).getArgs;
local args = getArgs( frame );
local templateName = frame.args[ 1 ];
local templateName = frame.args[ 1 ];
table.remove( args, 1 );
local docPage = 'Template:' .. templateName .. '/' .. docSubPage;
local docPage = 'Template:' .. templateName .. '/' .. docSubPage;
local templateData = getTemplateData( docPage );
local templateData = p.getTemplateData( docPage );
local out = p.generateBlankCode( templateData, templateName, args );
 
local previewNote = ''
if templateData == false and frame:preprocess('{{REVISIONID}}') == '' then
previewNote = '<div class="warningbox">' .. noDocNote .. '</div>';
end
return previewNote .. frame:extensionTag{ name = 'pre', content = out };
end
 
function p.generateExampleCode( templateData, templateName, args )
if templateData == false then
return '{{' .. templateName .. '}}';
end
local paramaterLength = 0;
local parameterLength = 0;
for i, parameterName in ipairs( templateData[ 'paramOrder' ] ) do
for i, parameterName in ipairs( templateData[ 'paramOrder' ] ) do
local parameterData = templateData[ 'params' ][ parameterName ] or {};
local parameterData = p.getValue( templateData[ 'params' ], parameterName );
if parameterData[ 'example' ] and not parameterData[ 'deprecated' ] then
if parameterData[ 'example' ] and not parameterData[ 'deprecated' ] then
local length = mw.ustring.len( parameterName );
local length = mw.ustring.len( parameterName );
if length > paramaterLength then
if length > parameterLength then
paramaterLength = length;
parameterLength = length;
end
end
end
end
end
end


-- See https://phabricator.wikimedia.org/diffusion/ETDA/browse/master/Specification.md?as=remarkup
local isBlockFormatted, startFormat, endFormat, paramKeyFormat, paramValueFormat = p.getFormatParts( templateData[ 'format' ], templateName );
-- We need a global format value for the 'block' and 'inline': [[phab:T205438]]
local out = startFormat;
local templateFormat = templateData[ 'format' ];
local isBlockFormatted = false;
if templateFormat == 'block' then
templateFormat = '{{_\n| _ = _\n}}';
isBlockFormatted = true;
elseif templateFormat == 'inline' then
templateFormat = '{{_|_=_}}';
end
local nameFormat = mw.ustring.match( templateFormat, '^[^|]+' );
local paramKeyFormat = mw.ustring.match( templateFormat, '%|[^=]+=' );
local paramValueFormat = mw.ustring.match( templateFormat, '=[^}]+' );
paramValueFormat = mw.ustring.sub( paramValueFormat, 2 );
local endFormat = mw.ustring.match( templateFormat, '%}%}.*$' );
 
local out = mw.ustring.gsub( nameFormat, '_', templateName );


local lastNumber = 0;
lastNumber = 0;
for i, parameterName in ipairs( templateData[ 'paramOrder' ] ) do
for i, parameterName in ipairs( templateData[ 'paramOrder' ] ) do
local parameterData = templateData[ 'params' ][ parameterName ] or {};
local parameterData = p.getValue( templateData[ 'params' ], parameterName );
if parameterData[ 'inherits' ] then
if parameterData[ 'inherits' ] then
parameterData = templateData[ 'params' ][ parameterData[ 'inherits' ] ] or {};
parameterData = p.getValue( templateData[ 'params' ], parameterData[ 'inherits' ] );
end
end
if parameterData[ 'example' ] and not parameterData[ 'deprecated' ] then
if parameterData[ 'example' ] then
local key = parameterName;
out = out .. p.formatKeyValue( parameterName, parameterData, {
local nkey = tonumber( key );
args = args,
if isBlockFormatted then
valueKey = 'example',
if nkey == nil or lastNumber ~= nkey - 1 then
isBlockFormatted = isBlockFormatted,
while mw.ustring.len( key ) < paramaterLength do
parameterLength = parameterLength,
key = key .. ' ';
paramKeyFormat = paramKeyFormat,
end
paramValueFormat = paramValueFormat,
end
} );
end
if nkey ~= nil and lastNumber == nkey - 1 then
key = '';
lastNumber = nkey;
end
 
local value = parameterData[ 'example' ];
 
local formattedKey = mw.ustring.gsub( paramKeyFormat, '_+', key, 1 );
if key == '' then
formattedKey = mw.ustring.gsub( formattedKey, '=', '' );
end
out = out .. formattedKey;
 
out = out .. mw.ustring.gsub( paramValueFormat, '_', value, 1 );
end
end
end
end
out = out .. endFormat;
return out .. endFormat;
end
 
function p.generateExample( frame )
local frame = mw.getCurrentFrame();
local args = frame.args;
local templateName = frame.args[ 1 ];
local docPage = 'Template:' .. templateName .. '/' .. docSubPage;
local templateData = p.getTemplateData( docPage );
local out = p.generateExampleCode( templateData, templateName, args );
local previewNote = ''
if templateData == false and frame:preprocess('{{REVISIONID}}') == '' then
previewNote = '<div class="warningbox">' .. noDocNote .. '</div>';
end
return frame:preprocess( out ) .. frame:extensionTag{ name = 'pre', content = out };
return previewNote .. frame:preprocess( out ) .. frame:extensionTag{ name = 'pre', content = out };
end
end


return p;
return p;

Текущая версия от 20:39, 4 февраля 2022

Модуль для работы с TemplateData и автоматической генерации заготовок для копирования и примеров использования шаблонов на основе её.

Для корректной сортировки параметров в TemplateData должен содержаться массив paramOrder. При редактировании через визуальный интерфейс он добавляется в момент перетаскивания параметров вверх-вниз в списке.

Методы[править код]

Модуль для работы с TemplateData и автоматической генерации заготовок для копирования и примеров использования шаблонов на основе её.

Для корректной сортировки параметров в TemplateData должен содержаться массив paramOrder. При редактировании через визуальный интерфейс он добавляется в момент перетаскивания параметров вверх-вниз в списке.

Методы[править код]

Внешние[править код]

  • generateBlank( frame ) — вывод заготовки для вставки шаблона в статью (см. {{заготовка шаблона}})
  • generateExample( frame ) — вывод примера использования шаблона (см. {{пример шаблона}})

Внутренние[править код]

  • getTemplateData( pageName ) — парсинг TemplateData с указанной страницы


Внутренние[править код]

  • getTemplateData( pageName ) — парсинг TemplateData с указанной страницы

local docSubPage = mw.message.new( 'Templatedata-doc-subpage' ):plain();

local p = {};

local lastNumber = 0;

-- Enable/disable additional spacing for block-formatted templates
local formatBlockSpaces = true;

local noDocNote = 'TemplateDataDoc: Запишите страницу для отображения заполненного шаблона.';

function p.processJson( json )
	local status, data = pcall( mw.text.jsonDecode, json );
	if status == false then
		return nil;
	end

	if not data[ 'paramOrder' ] then
		data[ 'paramOrder' ] = {};
		for paramName, paramData in pairs( data[ 'params' ] ) do
			table.insert( data[ 'paramOrder' ], paramName );
		end
	end
	
	local deprecatedParams = {
		'nocat',
		'nocoord',
		'nocatcoord',
		'Ширина',
		'ширина',
		'Ширина изображения',
		'ширина изображения',
		'Ширина логотипа',
		'ширина логотипа',
	};
	for _, param in ipairs( deprecatedParams ) do
		if data[ 'params' ][ param ] ~= nil then
			data[ 'params' ][ param ][ 'deprecated' ] = '-';
		end
	end

	return data;
end

function p.getTemplateData( pageName )
	local title = mw.title.makeTitle( 0, pageName );
	if not title or not title.exists or not title:getContent() then
		return false;
	end;

	local json = mw.ustring.match( title:getContent(), '<[Tt]emplate[Dd]ata%s*>(.*)</[Tt]emplate[Dd]ata%s*>' );
	if not json then
		return nil;
	end
	
	return p.processJson( json )
end

function p.getValue( data, key )
	if data[ key ] then
		return data[ key ];
	end
	
	-- Numbered keys return as numbers
	local nkey = tonumber( key );
	if nkey ~= nil and data[ nkey ] then
		return data[ nkey ];
	end
	
	return {};
end

-- See https://phabricator.wikimedia.org/diffusion/ETDA/browse/master/Specification.md?as=remarkup
-- We need a global format value for the 'block' and 'inline': [[phab:T205438]]
function p.convertFormatString( rawTemplateFormat )
	local templateFormat = rawTemplateFormat or 'inline';
	local isBlockFormatted = false;
	if templateFormat == 'block' then
		templateFormat = '{{_\n| _ = _\n}}';
		isBlockFormatted = true;
	elseif templateFormat == 'inline' then
		templateFormat = '{{_|_=_}}';
	end
	
	return templateFormat, isBlockFormatted;
end

function p.getFormatParts( rawTemplateFormat, templateName )
	local templateFormat, isBlockFormatted = p.convertFormatString( rawTemplateFormat );
	local nameFormat = mw.ustring.match( templateFormat, '^[^|]+' );
	local paramKeyFormat = mw.ustring.match( templateFormat, '%|[^=]+=' );
	local paramValueFormat = mw.ustring.match( templateFormat, '=[^}]+' );
	paramValueFormat = mw.ustring.sub( paramValueFormat, 2 );
	local endFormat = mw.ustring.match( templateFormat, '%}%}.*$' );
	local startFormat = mw.ustring.gsub( nameFormat, '_', templateName );
	
	return isBlockFormatted, startFormat, endFormat, paramKeyFormat, paramValueFormat;
end

function p.formatKeyValue( key, parameterData, formatData )
	if parameterData[ 'deprecated' ] then
		return '';
	end
	local args = formatData.args;
	local parameterName = key;
	local nkey = tonumber(key);
	
	-- Add additional spacing to string keys
	if formatBlockSpaces and formatData.isBlockFormatted then
		if nkey == nil or lastNumber ~= nkey - 1 then
			while mw.ustring.len( key ) < formatData.parameterLength do
				key = key .. ' ';
			end
		end
	end
	
	-- Remove numbering for adjacent numbered keys
	if nkey ~= nil and lastNumber == nkey - 1 then
		key = '';
		lastNumber = nkey;
	end

	local value = '';
	if formatData.valueKey == 'example' and parameterData[ 'example' ] then
		-- Example
		value = parameterData[ 'example' ];
	else
		if formatData.valueKey == 'description' and parameterData[ 'description' ] then
			-- Description
			value = parameterData[ 'description' ];
			if value ~= '' then
				value = '&lt;!-- ' .. value .. ' --&gt;';
			end
		elseif parameterData[ 'autovalue' ] then
			-- Autovalue
			value = parameterData[ 'autovalue' ];
		end
		if args[ '$' .. parameterName ] and args[ '$' .. parameterName ] ~= '' then
			-- Custom values from template call
			value = args[ '$' .. parameterName ];
		end
	end

	local formattedKey = mw.ustring.gsub( formatData.paramKeyFormat, '_+', key, 1 );
	if key == '' then
		formattedKey = mw.ustring.gsub( formattedKey, '=', '' );
	end

	return formattedKey .. mw.ustring.gsub( formatData.paramValueFormat, '_', value, 1 );
end

function p.generateBlankCode( templateData, templateName, args )
	if templateData == false then
		return '{{' .. templateName .. '}}';
	end
	
	local parameterLength = 0;
	for i, parameterName in ipairs( templateData[ 'paramOrder' ] ) do
		local parameterData = p.getValue( templateData[ 'params' ], parameterName );
		if not parameterData[ 'deprecated' ] then
			local length = mw.ustring.len( parameterName );
			if length > parameterLength then
				parameterLength = length;
			end
		end
	end

	local isBlockFormatted, startFormat, endFormat, paramKeyFormat, paramValueFormat = p.getFormatParts( templateData[ 'format' ], templateName );
	local out = startFormat;

	lastNumber = 0;
	for i, parameterName in ipairs( templateData[ 'paramOrder' ] ) do
		local parameterData = p.getValue( templateData[ 'params' ], parameterName );
		if parameterData[ 'inherits' ] then
			parameterData = p.getValue( templateData[ 'params' ], parameterData[ 'inherits' ] );
		end
		
		out = out .. p.formatKeyValue( parameterName, parameterData, {
			args = args,
			valueKey = ( args[ 'description' ] and 'description' or nil ),
			isBlockFormatted = isBlockFormatted,
			parameterLength = parameterLength,
			paramKeyFormat = paramKeyFormat,
			paramValueFormat = paramValueFormat,
		} );
	end
	
	return out .. endFormat;
end

function p.generateBlank( frame )
	local frame = mw.getCurrentFrame();
	local getArgs = require( 'Module:Arguments' ).getArgs;
	local args = getArgs( frame );
	local templateName = frame.args[ 1 ];
	table.remove( args, 1 );
	
	local docPage = 'Template:' .. templateName .. '/' .. docSubPage;
	local templateData = p.getTemplateData( docPage );
	local out = p.generateBlankCode( templateData, templateName, args );

	local previewNote = ''
	if templateData == false and frame:preprocess('{{REVISIONID}}') == '' then
		previewNote = '<div class="warningbox">' .. noDocNote .. '</div>';
	end
	
	return previewNote .. frame:extensionTag{ name = 'pre', content = out };
end

function p.generateExampleCode( templateData, templateName, args )
	if templateData == false then
		return '{{' .. templateName .. '}}';
	end
	
	local parameterLength = 0;
	for i, parameterName in ipairs( templateData[ 'paramOrder' ] ) do
		local parameterData = p.getValue( templateData[ 'params' ], parameterName );
		if parameterData[ 'example' ] and not parameterData[ 'deprecated' ] then
			local length = mw.ustring.len( parameterName );
			if length > parameterLength then
				parameterLength = length;
			end
		end
	end

	local isBlockFormatted, startFormat, endFormat, paramKeyFormat, paramValueFormat = p.getFormatParts( templateData[ 'format' ], templateName );
	local out = startFormat;

	lastNumber = 0;
	for i, parameterName in ipairs( templateData[ 'paramOrder' ] ) do
		local parameterData = p.getValue( templateData[ 'params' ], parameterName );
		if parameterData[ 'inherits' ] then
			parameterData = p.getValue( templateData[ 'params' ], parameterData[ 'inherits' ] );
		end
		
		if parameterData[ 'example' ] then
			out = out .. p.formatKeyValue( parameterName, parameterData, {
				args = args,
				valueKey = 'example',
				isBlockFormatted = isBlockFormatted,
				parameterLength = parameterLength,
				paramKeyFormat = paramKeyFormat,
				paramValueFormat = paramValueFormat,
			} );
		end
	end
	
	return out .. endFormat;
end	

function p.generateExample( frame )
	local frame = mw.getCurrentFrame();
	local args = frame.args;
	local templateName = frame.args[ 1 ];
	local docPage = 'Template:' .. templateName .. '/' .. docSubPage;
	local templateData = p.getTemplateData( docPage );
	local out = p.generateExampleCode( templateData, templateName, args );
	
	local previewNote = ''
	if templateData == false and frame:preprocess('{{REVISIONID}}') == '' then
		previewNote = '<div class="warningbox">' .. noDocNote .. '</div>';
	end
	
	return previewNote .. frame:preprocess( out ) .. frame:extensionTag{ name = 'pre', content = out };
end

return p;