Module:Set list

From Yugipedia
Jump to: navigation, search
Module documentation[view] [edit] [history] [purge]

Implements {{Set list}}.

See also Module:Set list/data.

--  <pre>
--  Module for {{Set list}},
--  to be used like {{#invoke:Set list|main}}.
--  @@@ for ideas.
-------------------
local SetList = {};

-----------------
--  Load modules:
-----------------
local getArgs       = require( 'Module:Arguments' ).getArgs;
local getCardType   = require( 'Module:Card type' ).main;

--------------
--  Load data:
--------------
local _D            = {};
local data          = mw.loadData( 'Module:Set list/data' );
local rg_list       = data.rg_list;         --  Region code table.
local region_list   = data.region_list;     --  Region name table.
local ln_list       = data.ln_list;         --  Language code table.
local language_list = data.language_list;   --  Language name table.
local rarity_list   = data.rarity_list;     --  Rarity name table.
local abbr_list     = data.abbr_list;       --  Abbr disable codes table.

----------------------
--  Utility functions:
----------------------
--  mw functions:
local split = mw.text.split;
local HTML  = mw.html.create;

--  Trim function:
--  Trims white space from front and tail of string.
--  If the input is only white space, returns nil.
local function _trim( s )
	if s and not s:match( '^%s*$' ) then
		return mw.text.trim( s ); -- If not nil nor empty.
	end
end

--  Link function:
--  If a string is passed, links it;
--  uses a label, when given.
--  If a table is passed, links every instance of it;
--  uses a table of labels, respectively for each entry, when given.
local function _link( v, ... )
	local labels = type( ... or nil ) == 'table' and ... or { ... };  --  Make sure it's a table.
	local function __link( v, label )
		return ('[[%s|%s]]'):format( v:gsub( '#', '' ), _trim( label ) or split( v, '%s*%(' )[1] );
	end
	if type( v ) == 'string' then
		return __link( v, labels[1] );
	elseif type( v ) == 'table' then
		local t = {};
		for key, value in ipairs( v ) do
			table.insert( t, __link( value, labels[key] ));
		end
		return t;
	else
		return v; -- @@@ error()
	end
end

--  Table count function:
--  Given a table, counts how many values it has.
local function _count( t ) -- @@@ error() for wrong type()
	local counter = 0;
	for key, value in pairs( t ) do
		counter = counter + 1;
	end
	return counter;
end

local function _error( message, default, category ) -- @@@ input list of categories.
	local err   = HTML( 'div' ):css( 'padding-left', '1.6em' )
					:tag( 'strong' ):addClass( 'error' ):wikitext( ('Error: %s'):format( message ) ):done()
				:allDone();
	local cat   = ('[[Category:%s]]'):format( category or '((Set list)) transclusion to be checked' );
	table.insert( _D.errors, table.concat( { tostring( err ), cat } ) );
	return default or '';
end

-------------------------------
--  Getter/generator functions:
-------------------------------
--  Info function:
--  Handles region/language info and flags.
local function getInfo()
	_D.rg       = _D.args['region'] and _D.args['region']:lower() or _error( 'A «region» is always needed!', 'en' );
	_D.region   = region_list[_D.rg];
	_D.ln       = ln_list[_D.rg];
	_D.language = language_list[_D.ln];
	--  Flags:
	_D.flags    = {};
	_D.flags.qty        = _D.args['qty'] and true;
	_D.flags.reprint    = _D.args['print'] and true;
	_D.flags.noAbbr     = _D.args['abbr'] and abbr_list[_D.args['abbr']:lower()] and true;
	_D.flags.column     = _D.args['col'] and true;
	_D.flags.notEnglish = _D.ln ~= 'en';
	_D.flags.italics    = ((_D.ln == 'ja') or (_D.ln == 'zh') or (_D.ln == 'ko')) and 'normal' or 'italic'; 
end

--  Local name function:
--  Returns the localized name.
local function getNameLocal( name )
	if not mw.smw then
		return _error( 'mw.smw module not found' );
	end
	local _page = name:gsub( '#', '' );
	local _query = { ('[[%s]]'):format( _page ), ('?%s name='):format( _D.language ), limit = 1, mainlabel = '-' };
	local query = mw.smw.ask( _query );
	if not query or _count( query ) == 0 or _count( query[1] ) == 0 then
		return;
	end
	return query[1][1];
end

--  Name function:
--  Returns two values:
--  The English name; the localized name.
local function getSetName( name )
	local t = split( name, '%s*%(' );
	local _name = _D.args['set'] or _trim( t[3] ) and table.concat( t, ' (', 1, 2 ) or t[1];
	local _nameLocal = _D.flags['notEnglish'] and getNameLocal( _name );
	return _name, _nameLocal;
end

--  Quotes function:
--  Wraps a name with quotes.
local function wrapQuotes( name, std )
	if not _trim( name ) then
		return '';  --  Return empty string.
	end
	return (std or (_D.ln ~= 'ja' and _D.ln ~= 'zh')) and table.concat( { '"', name, '"' } )
		or table.concat( { '「', name, '」' } );
end

--  Rarity function:
--  Receives a string of rarities.
local function getRarity( s )
	local t = s and split( s, ',' ) or {};
	local rarities = {};
	for _, rarity in ipairs( t ) do
		_rarity = _trim( rarity:lower():gsub( ' rare$', '' ) );
		if _rarity and rarity_list[_rarity] then
			table.insert( rarities, _link( rarity_list[_rarity] ) );
		end
	end
	return table.concat( rarities, '<br />' );
end

--  Page header function:
--  Builds page header (HTML).
local function getHeader( setName, setNameLocal )
	local CSS = {
		{   --  First «span» tag (set name).
			['font-size']   = '120%',
			['font-weight'] = 'bold',
			['font-style']  = 'italic'
		},
		{   --  Second «span» tag (local set name).
			['font-weight'] ='bold',
			['font-style']  = _D.flags['italics']
			
		}
	};
	
	local header = HTML( 'div' ):css( 'text-align', 'center' )
		:tag( 'span' ):css( CSS[1] )
			:wikitext( _link( setName, (setName:match( '%(2011%)' ) or setName:match( '%(series%)' )) and setName ) )
		:done();
	if _trim( setNameLocal ) then
		header:tag( 'br' ):done()
		:tag( 'span' ):css( CSS[2] )
			:wikitext( setNameLocal )
		:done();
	end
	header:tag( 'br' ):done()
		:wikitext( _D.region )
	:allDone();
	
	return tostring( header );
end

--  Categories function:
--  Generates categories.
local function getCategory( ns )
	--  @@@ Make it similar to _link.
	return ('[[Category:%s %s]]'):format( _D.region, ns );
end

local function tracking( frameArgs )
	local _parameters = {
		1,
		['region']  = 'region',
		['set']     = 'set',
		['abbr']    = 'abbr',
		['rarity']  = 'rarity',
		['qty']     = 'qty',
		['print']   = 'print',
		['col']     = 'col'
	}
	for key, value in pairs( frameArgs ) do
		if not( _parameters[key] ) or not( _trim( value ) ) then
			--  There are parameters not being used or used empty.
			return _error( 'Instance of unsupported or empty parameters!' , nil, '((Set list)) transclusion with parameters to be checked' )
		end
	end
	return '';
end

-----------------------
--  Set list functions:
-----------------------
--  Header entry function:
--  Builds the section header.
--  Fetches section info.
local function getSetListHeader( entry )
	local _header            = entry:match('header::(.-);')
							or entry:match('header::(.+)' );
	local _sectionNoAbbr     = entry:match(  'abbr::(.-);')
							or entry:match(  'abbr::(.+)' );
	local _sectionRarityList = entry:match('rarity::(.-);')
							or entry:match('rarity::(.+)' );
	local _sectionQty        = entry:match(   'qty::(.-);')
							or entry:match(   'qty::(.+)' );
	local _sectionReprint    = entry:match( 'print::(.-);')
							or entry:match( 'print::(.+)' );
	local _sectionColumn     = entry:match(   'col::(.-);')
							or entry:match(   'col::(.+)' );
	local sectionNoAbbr = _trim( _sectionNoAbbr ) and abbr_list[_trim( _sectionNoAbbr )] and true;
		
	return _trim( _header ), sectionNoAbbr, _sectionRarityList, _sectionQty, _sectionReprint, _trim( _sectionColumn );
end

--  Card entry function:
--  Builds the card row.
local function getSetListCard( entry, sectionNoAbbr, sectionRarityList, sectionQty, sectionReprint, sectionColumn )
	--  Intialize vars:
	local _number, _name, _rarityList, _qty, _reprint;
	local _nameAlt, _nameLocalAlt, _rarityAlt, _typeAlt, _description, _column;
	--  General values (from args or section):
	local noAbbr     = sectionNoAbbr or _D.flags['noAbbr'];
	local rarityList = sectionRarityList or _D.args['rarity'];
	local qty        = (sectionQty or _D.flags['qty']) and (
		(
			tonumber( sectionQty or _D.args['qty'] ) or sectionQty:match('%d%d-%-%d%d-') or _D.args['qty']:match('%d%d-%-%d%d-')
		) or 1
	);
	local reprint    = (sectionReprint or _D.args['print'])
		and (
			(
				(
					tonumber( sectionReprint or _D.args['print'] )
					or ( sectionReprint or _D.args['print'] ):match( 'true' )
				)
			and 'Reprint'
			) or (sectionReprint or _D.args['print'])
		)
	local column     = sectionColumn or _D.args['col'];
	
	--  Local values:
	--  # Split info:
	local valuesStandard    = split( split( entry, '//' )[1] , ';');
	local valuesAlternate   =        split( entry, '//' )[2];
	
	--  # Deal with standard values:
	if noAbbr then
		_name       = _trim( valuesStandard[1] );
		_rarityList = _trim( valuesStandard[2] );
		_qty        = _trim( valuesStandard[3] );
		_reprint    = _trim( valuesStandard[4] );
	else
		_number     = _trim( valuesStandard[1] );
		_name       = _trim( valuesStandard[2] );
		_rarityList = _trim( valuesStandard[3] );
		_qty        = _trim( valuesStandard[4] );
		_reprint    = _trim( valuesStandard[5] );
	end
	
	--  # Deal with alternate values:
	if valuesAlternate then
		_nameAlt      = valuesAlternate:match(       'name::(.-);')
					 or valuesAlternate:match(       'name::(.+)' );
		_nameLocalAlt = valuesAlternate:match('name%-local::(.-);')
					 or valuesAlternate:match('name%-local::(.+)' );
		_rarityAlt    = valuesAlternate:match(     'rarity::(.-);')
					 or valuesAlternate:match(     'rarity::(.+)' );
		_typeAlt      = valuesAlternate:match(   'category::(.-);')
					 or valuesAlternate:match(   'category::(.+)' );
		_description  = valuesAlternate:match('description::(.-);')
					 or valuesAlternate:match('description::(.+)' );
		_column       = column and
						(valuesAlternate:match(table.concat( { column:lower(), '::(.-);' } )) 
					  or valuesAlternate:match(table.concat( { column:lower(), '::(.+)'  } )));
	end
	
	--  Build table row:
	--  # Initialize:
	local row = HTML( 'tr' );
	--  # Card number:
	if not noAbbr then
		row:tag( 'td' ):wikitext(
			_number and (
				(_number:match( '?' ) or not _name) and _number or _link( _number )
			) or '&nbsp;'
		):done();
	end
	--  # Card name:
	row:tag( 'td' )
		:wikitext( _trim( table.concat( { _nameAlt or wrapQuotes( _link( _name, (_name or ''):match( 'Token %(' ) and _name ), true ), _description or '' }, ' ' ) ) )
	:done();
	--  # Card local name:
	if _D.flags['notEnglish'] then
		row:tag( 'td' ):attr( 'lang', _D.ln ):wikitext( _nameLocalAlt or _name and wrapQuotes( getNameLocal( _name ) ) ):done();
	end
	--  # Card rarity:
	row:tag( 'td' ):wikitext( _rarityAlt or getRarity( _rarityList or rarityList ) ):done();
	--  # Card type:
	row:tag( 'td' ):wikitext( _typeAlt or _name and getCardType( _name ) ):done();
	--  # Card qty:
	if qty then
		row:tag( 'td' ):wikitext( _qty and (tonumber( _qty ) or _qty:match('%d%d-%-%d%d-')) or qty or 1 ):done();
	end
	--  # Card print:
	if reprint then
		row:tag( 'td' ):wikitext( _reprint or reprint ):done();
	end
	--  # Extra column:
	if column then
		row:tag( 'td' ):wikitext( _column ):done();
	end
	row:allDone();
	
	return tostring( row );
end

--  Wrap function.
--  Given HTML table rows,
--  wraps it with «table» tags and builds the table header.
local function wrapTable( header, t, noAbbr, qty, reprint, column )
	local noAbbr    = noAbbr or _D.flags['noAbbr'];
	local qty       = qty or _D.flags['qty'];
	local reprint   = reprint or _D.flags['reprint'];
	local column    = column or _D.flags['column'];
	
	--  Build HTML table:
	--  # Initialize:
	local HTMLtableHeader   = HTML( 'table' ):attr( 'id', header or 'Top_table' ):addClass( 'wikitable' ):addClass( 'sortable' ):addClass( 'card-list' ):addClass( 'set_list' );
	local HTMLtableCaption  = header and HTML( 'caption' ):wikitext( header ):done();
	local HTMLtableRow      = HTML( 'tr' );
	--  # Card number:
	if not noAbbr then
		HTMLtableRow:tag( 'th' ):attr( 'scope', 'col' ):wikitext( 'Card number' ):done();
	end
	--  # Card name:
	HTMLtableRow:tag( 'th' ):attr( 'scope', 'col' ):wikitext( _D.flags['notEnglish'] and 'English name' or 'Name' ):done();
	--  # Card local name:
	if _D.flags['notEnglish'] then
		HTMLtableRow:tag( 'th' ):attr( 'scope', 'col' ):wikitext( table.concat( { _D.language, ' name' } ) ):done();
	end
	--  # Card rarity:
	HTMLtableRow:tag( 'th' ):attr( 'scope', 'col' ):wikitext( 'Rarity' ):done();
	--  # Card type:
	HTMLtableRow:tag( 'th' ):attr( 'scope', 'col' ):wikitext( 'Category' ):done();
	--  # Card qty:
	if qty then
		HTMLtableRow:tag( 'th' ):attr( 'scope', 'col' ):wikitext( 'Qty' ):done();
	end
	--  # Card print:
	if reprint then
		HTMLtableRow:tag( 'th' ):attr( 'scope', 'col' ):wikitext( 'Print' ):done();
	end
	--  # Extra column:
	if column then
		HTMLtableRow:tag( 'th' ):attr( 'scope', 'col' ):wikitext( _trim( _D.args['col'] ) ):done();
	end
	HTMLtableRow:done();    --  Close the </tr>.
	
	--  Concat everything:
	local HTMLtable = (header and HTMLtableHeader:node( tostring( HTMLtableCaption ) ) or HTMLtableHeader)
		:node( tostring( HTMLtableRow ) )
		:node( table.concat( t ) )  --  Add the table content (all card rows).
		:allDone();    --  Close table (</table>).

	return tostring( HTMLtable );
end

--  Main set list function:
--  Designs the card table.
local function getSetList()
	local sections  = _D.args[1] and split( _D.args[1], '!:' ) or _error( 'No list info given!', { ';' } );
	local SetListTable = {};    --  Contains list of processed sections.
	for _, section in ipairs( sections ) do
		local _section;
		local sectionSetListTable = {}; --  Contains list of processed card entries.
		local _header, _sectionNoAbbr, _sectionRarityList, _sectionQty, _sectionReprint;  --  Store section info, applied to all section entries.
		if not _trim( section ) then
			--  Empty section; skip.
		else
			--  It's a section.
			local entries = split( section, '\n' );
			for index, entry in ipairs( entries ) do
				if not _trim( entry ) then
					--  Emtpy entry; skip.
				elseif index == 1 and entry:match( 'header::' ) then
					--  Contains header info.
					_header, _sectionNoAbbr, _sectionRarityList, _sectionQty, _sectionReprint, _sectionColumn = getSetListHeader( entry );
				else
					--  Card info.
					table.insert( sectionSetListTable, getSetListCard( entry, _sectionNoAbbr, _sectionRarityList, _sectionQty, _sectionReprint, _sectionColumn ) );
				end
			end
			--  Process table for each section.
			_section = wrapTable( _header, sectionSetListTable, _sectionNoAbbr, _sectionQty, _sectionReprint, _sectionColumn );
		end
		table.insert( SetListTable, _section );
	end
	return table.concat( SetListTable, '\n' );
end

-------------------
--  Main functions:
-------------------
--  Main function:
--  To be called through #invoke.
function SetList.main( frame )
	_D.errors       = {};
	_D.args         = getArgs( frame, { trim = true, removeBlanks = true, parentOnly = true } );
	local PAGENAME  = mw.title.getCurrentTitle().text;     --  {{PAGENAME}}
	local NAMESPACE = mw.title.getCurrentTitle().nsText;   --  {{NAMESPACE}}
	
	getInfo();
	local _setName, _setNameLocal = getSetName( PAGENAME );
	local header    = getHeader( _setName, _setNameLocal );
	local category  = getCategory( NAMESPACE )
	local setList   = getSetList();
	local track     = tracking( frame:getParent().args );
	
	--  Return value:
	local ret = {};
	if _trim( NAMESPACE ) then
		if _count( _D.errors ) > 0 then 
			for key, value in ipairs( _D.errors ) do
				table.insert( ret, value );
			end
		end
		table.insert( ret, header );
		table.insert( ret, category );
		table.insert( ret, setList );
	else
		--  When on the set page.
		return setList;
	end
	
	return table.concat( ret );
end


return SetList;
--  </pre>