FANDOM


--  <pre>
--  Module for {{Set list}},
--  to be used like {{#invoke:Set list|main}}.
--  @@@ for ideas.
-------------------
local SetList = {};
 
-----------------
--  Load modules:
-----------------
local getArgs       = require( 'Dev: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 valuee 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.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',
        ['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 _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, _trim( _sectionColumn );
end
 
--  Card entry function:
--  Builds the card row.
local function getSetListCard( entry, sectionNoAbbr, sectionRarityList, sectionQty, sectionColumn )
    --  Intialize vars:
    local _number, _name, _rarityList, _qty;
    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 1);
    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] );
    else
        _number     = _trim( valuesStandard[1] );
        _name       = _trim( valuesStandard[2] );
        _rarityList = _trim( valuesStandard[3] );
        _qty        = _trim( valuesStandard[4] );
    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( '?' ) and _number or _link( _number )) ):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( tonumber( _qty ) or qty or 1 ):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, column )
    local noAbbr    = noAbbr or _D.flags['noAbbr'];
    local qty       = qty or _D.flags['qty'];
    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' );
    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
    --  # 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;  --  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, _sectionColumn = getSetListHeader( entry );
                else
                    --  Card info.
                    table.insert( sectionSetListTable, getSetListCard( entry, _sectionNoAbbr, _sectionRarityList, _sectionQty, _sectionColumn ) );
                end
            end
            --  Process table for each section.
            _section = wrapTable( _header, sectionSetListTable, _sectionNoAbbr, _sectionQty, _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>
*Disclosure: Some of the links above are affiliate links, meaning, at no additional cost to you, Fandom will earn a commission if you click through and make a purchase. Community content is available under CC-BY-SA unless otherwise noted.

Fandom may earn an affiliate commission on sales made from links on this page.

Stream the best stories.

Fandom may earn an affiliate commission on sales made from links on this page.

Get Disney+