Difference between revisions of "Module:Card gallery/File/CG"

From Yugipedia
Jump to: navigation, search
(Cleanup. Deal with multiple releases)
(Restoring revision 5047626 by User:Becasita on 2022-08-22 19:06:13. "Use `Module:Card image name` directly.")
 
(15 intermediate revisions by one other user not shown)
Line 4: Line 4:
 
-- @author [[User:Becasita]]
 
-- @author [[User:Becasita]]
 
-- @contact [[User talk:Becasita]]
 
-- @contact [[User talk:Becasita]]
 +
 +
-- «
 +
-- <card number>; <set>; <rarity>; <edition>; <alt> :: <release> // <option1>::<value1>; <optionN>::<valueN>
 +
-- »
  
 
----------------
 
----------------
 
-- Load modules:
 
-- Load modules:
 
----------------
 
----------------
local DATA       = require( 'Module:Data' );
+
local DATA = require( 'Module:Data' );
local UTIL       = require( 'Module:Util' );
+
local UTIL = require( 'Module:Util' );
local getName    = require( 'Module:Name' ).main;
 
local getImgName = require( 'Module:Card image name' ).main;
 
  
---------------------
+
local StringBuffer = require( 'Module:StringBuffer' );
-- Utility functions:
+
local getCardImageName = require( 'Module:Card image name' );
---------------------
 
-- @name sortedPairsByKeys
 
-- @return {function [Iterator]}
 
local function sortedPairsByKeys( t, f )
 
local buffer = {};
 
  
for key in pairs( t ) do
+
-------------
table.insert( buffer, key );
+
-- Constants:
end
+
-------------
 +
local PAGENAME  = mw.title.getCurrentTitle().text;
  
table.sort( buffer, f );
+
local CARD_BACK_TCG    = 'Back-EN.png';
 +
local CARD_BACK_JP    = 'Back-JP.png';
 +
local CARD_BACK_AE    = 'Back-AE.png';
 +
local CARD_BACK_KR    = 'Back-KR.png';
 +
local LANGUAGE_ENGLISH = DATA.getLanguage( 'EN' );
 +
local OFFICIAL_PROXY  = DATA.getRelease( 'OP' );
  
local index = 0;
+
---------------
+
-- Helper vars:
return function()
+
---------------
index = index + 1;
+
-- These variables are only used by the init functions.
return buffer[ index ], buffer[ index ] and t[ buffer[ index ] ];
+
-- Therefore, even though they are re-assigned every time
end
+
-- a new instance is created (through `new()`), there's no
end
+
-- problem, because their useful lifetime is only inside
 +
-- that very function.
 +
-- This way, having them here kinda as static variables,
 +
-- supports encapsulation, since each instance of `File`
 +
-- doesn't need to have them.
 +
local _standard, _releases, _options;
  
--------------
+
--------------------
-- File class:
+
-- Helper functions:
--------------
+
--------------------
-- @name File
+
-- @description Boolean indicating if the file doesn't have an edition.
-- @classAttr counter -> [static] Counts the number of File instances.
+
local function hasNoEdition( t )
local File  = {};
+
local rg = ( t.region or t.parent:getRegion() ).index;
File.__index = File;
 
File.counter = 0;
 
  
-- @name new          -> File constructor.
+
return rg == 'JP'
-- @attr DATA        -> External data (the «_D» from other modules).
+
or rg == 'JA'
-- @attr flags        -> Control flags:
+
or rg == 'TC'
-- -- exists    => denotes if a file is to be printed.
+
or rg == 'SC'
-- -- noEdition => if it is a Japanese or Chinese print; thus, no edition (except DT ones).
+
or (
-- -- isOP      => if it is an Official Proxy.
+
rg == 'KR'
-- @attr number      -> The card number.
+
and t.setAbbr:match( 'RD/' )
-- @attr rg           -> The region index.
+
);
-- @attr set          -> The set name inputted.
 
-- @attr setEn        -> The English name for the set.
 
-- @attr setLn        -> The localized name for the set.
 
-- @attr setAbbr      -> The set abbreviation.
 
-- @attr rarity      -> The rarity name.
 
-- @attr r            -> The rarity abbreviation.
 
-- @attr edition      -> The full edition.
 
-- @attr ed          -> The edition abbreviation.
 
-- @attr releases    -> The card releases.
 
-- @attr rels        -> The card releases abbreviations (OP|GC|CT|RP).
 
-- @attr alt          -> The alt value.
 
-- @attr extension    -> The file extension.
 
-- @attr description  -> A short file description.
 
function File.new( std, rel, opt, data )
 
-- @attr _standard -> Contains the trimmed input args for the standard input {enum-like}.
 
-- @attr _release  -> Contains the trimmed input arg for the release (OP|GC|CT|RP).
 
-- @attr _options  -> Contains the trimmed input args for the options {map-like}.
 
File.counter    = File.counter + 1;
 
local fileData  = {};
 
fileData.DATA  = data;
 
fileData.errors = {};
 
fileData.flags  = {
 
exists    = true,
 
noEdition = false,
 
isOP      = false
 
};
 
fileData._standard = std or {};
 
fileData._release  = rel or {};
 
fileData._options  = opt or {};
 
 
 
return setmetatable( fileData, File ):init();
 
 
end
 
end
  
-- @name __tostring
+
-- @description Decides what kind of card backing to present.
-- @description [metamethod] Renders the File.
+
local function getCardBack( rg )
function File:__tostring()
+
return (
if not self.flags.exists then
+
( rg == 'JP' or rg == 'JA' or rg == 'TC' ) and CARD_BACK_JP
return '';
+
or
end
+
rg == 'AE' and CARD_BACK_AE
 
+
or
-- Build file:
+
rg == 'KR' and CARD_BACK_KR
local file    = {
+
or
getImgName(), self.setAbbr, self.rg
+
CARD_BACK_TCG
};
 
if self.r  then table.insert( file, self.r  ) end
 
if self.ed  then table.insert( file, self.ed  ) end
 
for rel in ipairs( self.rels ) do
 
table.insert( file, rel );
 
end
 
if self.flags.isOP then table.insert( file, 'OP' ) end
 
if self.alt then table.insert( file, self.alt ) end
 
 
 
-- Build caption: (this might get better implementation in the future)
 
local caption = {};
 
local temp1st = {};
 
if self.number then table.insert( temp1st, UTIL.link( self.number ) ) end
 
if self.rarity then
 
table.insert(
 
temp1st,
 
('(%s)'):format( UTIL.link( self.rarity, self.r ) )
 
);
 
end
 
if UTIL.count( temp1st ) ~= 0 then
 
table.insert( caption, table.concat( temp1st, ' ' ) );
 
end
 
if self.edition then table.insert( caption, UTIL.link( self.edition ) ) end
 
for release in ipairs( self.releases ) do
 
table.insert( caption, UTIL.link( release ) );
 
end
 
if self.flags.isOP then table.insert( caption, UTIL.link( 'Official Proxy' ) ) end
 
table.insert(
 
caption,
 
UTIL.italics(
 
UTIL.link(
 
self.set,
 
self.setEn:match( '%(2011%)' ) and self.setEn -- or self.setEn:match( '%(series%)' )
 
)
 
)
 
 
);
 
);
if self.description then table.insert( caption, self.description ) end
 
 
-- Stringify:
 
local fileString    = ('%s.%s'):format( table.concat( file, '-' ), self.extension );
 
local captionString = table.concat( caption, '<br />' );
 
 
-- Return concatenation:
 
return ('%s | %s'):format( fileString, captionString );
 
end
 
 
-- @name error
 
-- @description Generate consistent error messages.
 
function File:error( parameter )
 
self.flags.exists = false;
 
table.insert( self.errors, ('No %s given for file input number %d!'):format( parameter, File.counter ) );
 
 
return self;
 
end
 
 
-- @name initRegion
 
-- @description Sets the «rg» attribute.
 
function File:initRg()
 
self.rg = self.DATA.rg:upper();
 
self.flags.noEdition = self.rg == 'JP' or self.rg == 'JA' or self.rg == 'TC';
 
 
return self;
 
 
end
 
end
  
 
-- @name initNumber
 
-- @name initNumber
-- @description Sets the «number» and «setAbbr» attributes.
+
-- @description Sets the `number` and `setAbbr` attributes.
function File:initNumber()
+
local function initNumber( t )
local cardNumber = self._standard[ 1 ];
+
local cardNumber = _standard[ 1 ];
  
 
if cardNumber == '' then
 
if cardNumber == '' then
return self:error( 'set abbreviation' );
+
return t:error( 'set abbreviation' );
 
end
 
end
  
if cardNumber and cardNumber:match( '^%w-%-%w-$' ) then
+
if cardNumber and cardNumber:match( '^[/%w]-%-%w-$' ) then
 
-- Input like «TLM-EN012».
 
-- Input like «TLM-EN012».
self.number  = cardNumber:upper();
+
t.number  = cardNumber:upper();
self.setAbbr = self.number:match( '^(%w-)%-%w-$' );
+
t.setAbbr = t.number:match( '^([/%w]-)%-%w-$' );
 
else
 
else
 
-- Input like «S1».
 
-- Input like «S1».
self.number  = nil;
+
t.number  = nil;
self.setAbbr = cardNumber:upper();
+
t.setAbbr = cardNumber:upper();
 
end
 
end
 
return self;
 
 
end
 
end
  
 
-- @name initSet
 
-- @name initSet
-- @description Sets the «set», «setEn» and «setLn» attributes.
+
-- @description Sets the `set`, `setEn` and `setLn` attributes.
function File:initSet()
+
local function initSet( t )
local set = self._standard[ 2 ];
+
local set = _standard[ 2 ];
  
 
if set == '' then
 
if set == '' then
return self:error( 'set name' );
+
return t:error( 'set name' );
 
end
 
end
  
self.set  = set;
+
t.set  = set;
self.setEn = getName( set ) or set;
+
t.setEn = DATA.getName( set, LANGUAGE_ENGLISH ) or set; --TODO: either UTIL.trim or... check later
self.setLn = getName( set, self.DATA.language );
+
t.setLn = DATA.getName( set, t.parent:getLanguage() );
 
 
return self;
 
 
end
 
end
  
-- @name initRelease
+
-- @name initReleases
-- @description Sets the «releases» and «rels» attributes.
+
-- @description Sets the `releases` attribute.
function File:initRelease()
+
local function initReleases( t )
local releases = {}; -- Unsorted; each release is a key, to prevent duplicates.
+
local releasesAsKeys = {}; -- Unsorted; each release is a key, to prevent duplicates.
 
+
for _, value in ipairs( _releases ) do
for _, value in ipairs( self._release ) do
 
 
local release = DATA.getRelease( value );
 
local release = DATA.getRelease( value );
 
if release then
 
if release then
releases[ release ] = true;
+
releasesAsKeys[ release.full ] = release;
 
else
 
else
table.insert(
+
t.parent:error(
self.errors,
+
('Invalid release value %s given for file input number %d!'):format( value, t.id )
('Invalid release value %s given for file input number %d!'):format( value, File.counter )
 
 
);
 
);
 
end
 
end
 
end
 
end
  
for releaseAsKey in pairs( releases ) do
+
local releases = {};
 +
for releaseAsKey in pairs( releasesAsKeys ) do
 
table.insert( releases, releaseAsKey );
 
table.insert( releases, releaseAsKey );
 
end
 
end
 +
table.sort( releases );
  
table.sort( releases );
+
t.releases = {};
 
+
for _, releaseFull in ipairs( releases ) do
self.releases = {};
+
t.flags.isOP = (
self.rels    = {};
+
releaseFull == OFFICIAL_PROXY.full
 
 
for release in ipairs( releases ) do
 
local rel = DATA.getRel( release ):upper();
 
self.flags.isOP = (
 
rel == 'OP'
 
or
 
table.insert( self.releases, release ) -- Insert the release name (this returns nil).
 
 
or
 
or
table.insert( self.rels, rel ) -- Insert the release abbreviation (this returns nil).
+
table.insert( t.releases, releasesAsKeys[ releaseFull ] )
 
or
 
or
self.flags.isOP
+
t.flags.isOP
 
);
 
);
 
end
 
end
 
return self;
 
 
end
 
end
  
 
-- @name initRarity
 
-- @name initRarity
-- @description Sets the «rarity» and «r» attributes.
+
-- @description Sets the `rarity` attribute.
function File:initRarity()
+
local function initRarity( t )
local rarity = not self.flags.isOP and self._standard[ 3 ];
+
t.rarity = DATA.getRarity( _standard[ 3 ] );
  
if not self.flags.isOP and rarity == '' then
+
if not t.file and not t.flags.isOP and not t.rarity then
return self:error( 'rarity' );
+
return t:error( 'rarity' );
 
end
 
end
 
self.rarity = DATA.getRarity( rarity );
 
self.r      = self.rarity and DATA.getR( self.rarity );
 
 
return self;
 
 
end
 
end
  
 
-- @name initEdtion
 
-- @name initEdtion
-- @description Sets the «edition» and «ed» attributes.
+
-- @description Sets the `edition` attribute.
function File:initEdition()
+
local function initEdition( t )
local edition = self._standard[ self.flags.isOP and 3 or 4 ];
+
local edition = _standard[ t.flags.isOP and 3 or 4 ];
  
self.edition = DATA.getEdition( edition );
+
t.edition = DATA.getEdition( edition );
self.ed      = self.edition and DATA.getEd( self.edition ):upper();
 
  
if not self.flags.noEdition and not self.edition then
+
if not t.file and not hasNoEdition( t ) and not t.edition then
return self:error( 'edition' );
+
return t:error( 'edition' );
 
end
 
end
 
return self;
 
 
end
 
end
  
 
-- @name initAlt
 
-- @name initAlt
 
-- @description Set the «alt» attribute.
 
-- @description Set the «alt» attribute.
function File:initAlt()
+
local function initAlt( t )
 
local index = 5;
 
local index = 5;
if self.flags.isOP then index = index - 1 end
+
if t.flags.isOP then index = index - 1 end
if not self.ed    then index = index - 1 end
+
if not t.edition then index = index - 1 end
 
 
self.alt = self._standard[ index ];
 
  
return self;
+
t.alt = UTIL.trim( _standard[ index ] );
 
end
 
end
  
 
-- @name initOptions
 
-- @name initOptions
-- @description Sets the file extension.
+
-- @description Sets any possible options (`region`, `extension` and `description`).
function File:initOptions()
+
local function initOptions( t )
 +
-- Region:
 +
t.region = DATA.getRegion( _options[ 'region' ] );
 +
 
 +
if _options[ 'region' ] and not t.region then
 +
t.parent:error(
 +
('Invalid custom region value %s given for file input number %d!'):format(
 +
_options[ 'region' ],
 +
t.id
 +
)
 +
);
 +
end
 +
 
 
-- Extension:
 
-- Extension:
local extension = self._options[ 'extension' ];
+
local extension = _options[ 'extension' ];
self.extension  = UTIL.isString( extension ) and extension:lower() or 'png';
+
t.extension  = UTIL.isString( extension ) and extension:lower() or 'png';
  
 
-- Description:
 
-- Description:
self.description = self._options[ 'description' ];
+
t.description = _options[ 'description' ];
  
return self;
+
-- File:
 +
t.file = _options[ 'file' ];
 
end
 
end
  
 
-- @name init
 
-- @name init
 
-- @description Initializes the attributes of the File instance.
 
-- @description Initializes the attributes of the File instance.
function File:init()
+
local function init( t )
return self
+
initOptions( t );
:initRg()
+
initNumber( t );
:initNumber()
+
initSet( t );
:initSet()
+
initReleases( t );
:initRelease()
+
initRarity( t );
:initRarity()
+
initEdition( t );
:initEdition()
+
initAlt( t );
:initAlt()
+
return t;
:initOptions()
+
end
 +
 
 +
local function buildFile( t )
 +
local file = StringBuffer()
 +
:add( getCardImageName( PAGENAME ) )
 +
:add( t.setAbbr )
 +
:add( ( t.region or t.parent:getRegion() ).index )
 +
:add( t.rarity and t.rarity.abbr )
 +
:add( t.edition and t.edition.abbr )
 +
 
 +
for _, release in ipairs( t.releases ) do
 +
file:add( release.abbr )
 +
end
 +
 
 +
file
 +
:add( t.flags.isOP and OFFICIAL_PROXY.abbr )
 +
:add( t.alt )
 +
:flush( '-' )
 +
:add( t.extension )
 +
:flush( '.' )
 +
 
 +
return file:toString()
 +
end
 +
 
 +
--------------
 +
-- File class:
 +
--------------
 +
-- @name File
 +
-- @attr counter -> [static] Counts the number of File instances.
 +
local File  = {};
 +
File.__index = File;
 +
File.counter = 0;
 +
 
 +
-- @name new        -> File constructor.
 +
-- @attr flags      -> Control flags:
 +
-- -- hasErrors -> Denotes if a file has errors. Used when parsing the gathered content.
 +
-- -- isOP      -> If it is an Official Proxy.
 +
-- @attr number      -> The card number.
 +
-- @attr set        -> The set name inputted.
 +
-- @attr setEn      -> The English name for the set.
 +
-- @attr setLn      -> The localized name for the set.
 +
-- @attr setAbbr    -> The set abbreviation.
 +
-- @attr rarity      -> The rarity.
 +
-- @attr edition    -> The edition.
 +
-- @attr releases    -> The card releases.
 +
-- @attr alt        -> The alt value.
 +
-- @attr extension  -> The file extension.
 +
-- @attr description -> A short file description.
 +
function File.new( cardGallery, std, rel, opt )
 +
_standard = std or {};
 +
_releases = rel or {};
 +
_options  = opt or {};
 +
 
 +
File.counter  = File.counter + 1;
 +
local fileData = {
 +
id    = File.counter;
 +
parent = cardGallery;
 +
flags  = {
 +
hasErrors = false,
 +
isOP      = nil,
 +
},
 +
};
 +
 
 +
return init( setmetatable( fileData, File ) );
 +
end
 +
 
 +
-- @name error
 +
-- @description Generate consistent error messages.
 +
function File:error( parameter )
 +
self.flags.hasErrors = true;
 +
self.parent:error(
 +
('No %s found for file input number %d!'):format( parameter, self.id )
 +
)
 +
 
 +
return self;
 +
end
 +
 
 +
-- @name render
 +
-- @description Renders the File by parsing the info gathered.
 +
function File:render()
 +
if self.flags.hasErrors then
 +
return ('%s | File #%d'):format( getCardBack( self.parent:getRegion().index ), self.id );
 +
end
 +
 
 +
-- Build file:
 +
local file = ( self.file or buildFile( self ) ):gsub( '[/:]' , '' )
 +
 
 +
-- Build caption:
 +
local caption = StringBuffer()
 +
:add( self.number and UTIL.link( self.number ) )
 +
:add(
 +
self.rarity and ('(%s)'):format(
 +
UTIL.link( self.rarity.full, self.rarity.abbr )
 +
)
 +
)
 +
:flush( ' ' )
 +
:add( self.edition and UTIL.link( self.edition.full ) )
 +
 
 +
for _, release in ipairs( self.releases ) do
 +
caption:add( UTIL.link( release.full ) );
 +
end
 +
 
 +
caption
 +
:add( self.flags.isOP and UTIL.link( OFFICIAL_PROXY.full ) )
 +
:add(
 +
UTIL.italic(
 +
UTIL.link(
 +
self.set,
 +
self.setEn:match( '%(2011%)' ) and self.setEn -- or self.setEn:match( '%(series%)' )
 +
)
 +
)
 +
)
 +
:add( self.description )
 +
:flush( '<br />' )
 
;
 
;
 +
 +
return ('%s | %s'):format( file, caption:toString() );
 
end
 
end
  
Line 312: Line 331:
 
-- Return:
 
-- Return:
 
----------
 
----------
 +
-- @exports The `File` class.
 
return File;
 
return File;
 
-- </pre>
 
-- </pre>

Latest revision as of 16:27, 16 March 2023

-- <pre>
-- @name Card gallery/File/CG
-- @description Card Gallery file class for card game entries.
-- @author [[User:Becasita]]
-- @contact [[User talk:Becasita]]

-- «
-- <card number>; <set>; <rarity>; <edition>; <alt> :: <release> // <option1>::<value1>; <optionN>::<valueN>
-- »

----------------
-- Load modules:
----------------
local DATA = require( 'Module:Data' );
local UTIL = require( 'Module:Util' );

local StringBuffer = require( 'Module:StringBuffer' );
local getCardImageName = require( 'Module:Card image name' );

-------------
-- Constants:
-------------
local PAGENAME  = mw.title.getCurrentTitle().text;

local CARD_BACK_TCG    = 'Back-EN.png';
local CARD_BACK_JP     = 'Back-JP.png';
local CARD_BACK_AE     = 'Back-AE.png';
local CARD_BACK_KR     = 'Back-KR.png';
local LANGUAGE_ENGLISH = DATA.getLanguage( 'EN' );
local OFFICIAL_PROXY   = DATA.getRelease( 'OP' );

---------------
-- Helper vars:
---------------
-- These variables are only used by the init functions.
-- Therefore, even though they are re-assigned every time
-- a new instance is created (through `new()`), there's no
-- problem, because their useful lifetime is only inside
-- that very function.
-- This way, having them here kinda as static variables,
-- supports encapsulation, since each instance of `File`
-- doesn't need to have them.
local _standard, _releases, _options;

--------------------
-- Helper functions:
--------------------
-- @description Boolean indicating if the file doesn't have an edition.
local function hasNoEdition( t )
	local rg = ( t.region or t.parent:getRegion() ).index;

	return rg == 'JP'
		or rg == 'JA'
		or rg == 'TC'
		or rg == 'SC'
		or (
			rg == 'KR'
			and t.setAbbr:match( 'RD/' )
		);
end

-- @description Decides what kind of card backing to present.
local function getCardBack( rg )
	return (
		( rg == 'JP' or rg == 'JA' or rg == 'TC' ) and CARD_BACK_JP
		or
		rg == 'AE' and CARD_BACK_AE
		or
		rg == 'KR' and CARD_BACK_KR
		or
		CARD_BACK_TCG
	);
end

-- @name initNumber
-- @description Sets the `number` and `setAbbr` attributes.
local function initNumber( t )
	local cardNumber = _standard[ 1 ];

	if cardNumber == '' then
		return t:error( 'set abbreviation' );
	end

	if cardNumber and cardNumber:match( '^[/%w]-%-%w-$' ) then
		-- Input like «TLM-EN012».
		t.number  = cardNumber:upper();
		t.setAbbr = t.number:match( '^([/%w]-)%-%w-$' );
	else
		-- Input like «S1».
		t.number  = nil;
		t.setAbbr = cardNumber:upper();
	end
end

-- @name initSet
-- @description Sets the `set`, `setEn` and `setLn` attributes.
local function initSet( t )
	local set = _standard[ 2 ];

	if set == '' then
		return t:error( 'set name' );
	end

	t.set   = set;
	t.setEn = DATA.getName( set, LANGUAGE_ENGLISH ) or set; --TODO: either UTIL.trim or... check later
	t.setLn = DATA.getName( set, t.parent:getLanguage() );
end

-- @name initReleases
-- @description Sets the `releases` attribute.
local function initReleases( t )
	local releasesAsKeys = {}; -- Unsorted; each release is a key, to prevent duplicates.
	for _, value in ipairs( _releases ) do
		local release = DATA.getRelease( value );
		if release then
			releasesAsKeys[ release.full ] = release;
		else
			t.parent:error(
				('Invalid release value %s given for file input number %d!'):format( value, t.id )
			);
		end
	end

	local releases = {};
	for releaseAsKey in pairs( releasesAsKeys ) do
		table.insert( releases, releaseAsKey );
	end
	table.sort( releases );

	t.releases = {};
	for _, releaseFull in ipairs( releases ) do
		t.flags.isOP = (
			releaseFull == OFFICIAL_PROXY.full
			or
			table.insert( t.releases, releasesAsKeys[ releaseFull ] )
			or
			t.flags.isOP
		);
	end
end

-- @name initRarity
-- @description Sets the `rarity` attribute.
local function initRarity( t )
	t.rarity = DATA.getRarity( _standard[ 3 ] );

	if not t.file and not t.flags.isOP and not t.rarity then
		return t:error( 'rarity' );
	end
end

-- @name initEdtion
-- @description Sets the `edition` attribute.
local function initEdition( t )
	local edition = _standard[ t.flags.isOP and 3 or 4 ];

	t.edition = DATA.getEdition( edition );

	if not t.file and not hasNoEdition( t ) and not t.edition then
		return t:error( 'edition' );
	end
end

-- @name initAlt
-- @description Set the «alt» attribute.
local function initAlt( t )
	local index = 5;
	if t.flags.isOP  then index = index - 1 end
	if not t.edition then index = index - 1 end

	t.alt = UTIL.trim( _standard[ index ] );
end

-- @name initOptions
-- @description Sets any possible options (`region`, `extension` and `description`).
local function initOptions( t )
	-- Region:
	t.region = DATA.getRegion( _options[ 'region' ] );

	if _options[ 'region' ] and not t.region then
		t.parent:error(
			('Invalid custom region value %s given for file input number %d!'):format(
				_options[ 'region' ],
				t.id
			)
		);
	end

	-- Extension:
	local extension = _options[ 'extension' ];
	t.extension  = UTIL.isString( extension ) and extension:lower() or 'png';

	-- Description:
	t.description = _options[ 'description' ];

	-- File:
	t.file = _options[ 'file' ];
end

-- @name init
-- @description Initializes the attributes of the File instance.
local function init( t )
	initOptions( t );
	initNumber( t );
	initSet( t );
	initReleases( t );
	initRarity( t );
	initEdition( t );
	initAlt( t );
	return t;
end

local function buildFile( t )
	local file = StringBuffer()
		:add( getCardImageName( PAGENAME ) )
		:add( t.setAbbr )
		:add( ( t.region or t.parent:getRegion() ).index )
		:add( t.rarity and t.rarity.abbr )
		:add( t.edition and t.edition.abbr )

	for _, release in ipairs( t.releases ) do
		file:add( release.abbr )
	end

	file
		:add( t.flags.isOP and OFFICIAL_PROXY.abbr )
		:add( t.alt )
		:flush( '-' )
		:add( t.extension )
		:flush( '.' )

	return file:toString()
end

--------------
-- File class:
--------------
-- @name File
-- @attr counter -> [static] Counts the number of File instances.
local File   = {};
File.__index = File;
File.counter = 0;

-- @name new         -> File constructor.
-- @attr flags       -> Control flags:
-- -- hasErrors -> Denotes if a file has errors. Used when parsing the gathered content.
-- -- isOP      -> If it is an Official Proxy.
-- @attr number      -> The card number.
-- @attr set         -> The set name inputted.
-- @attr setEn       -> The English name for the set.
-- @attr setLn       -> The localized name for the set.
-- @attr setAbbr     -> The set abbreviation.
-- @attr rarity      -> The rarity.
-- @attr edition     -> The edition.
-- @attr releases    -> The card releases.
-- @attr alt         -> The alt value.
-- @attr extension   -> The file extension.
-- @attr description -> A short file description.
function File.new( cardGallery, std, rel, opt )
	_standard = std or {};
	_releases = rel or {};
	_options  = opt or {};

	File.counter   = File.counter + 1;
	local fileData = {
		id     = File.counter;
		parent = cardGallery;
		flags  = {
			hasErrors = false,
			isOP      = nil,
		},
	};

	return init( setmetatable( fileData, File ) );
end

-- @name error
-- @description Generate consistent error messages.
function File:error( parameter )
	self.flags.hasErrors = true;
	self.parent:error(
		('No %s found for file input number %d!'):format( parameter, self.id )
	)

	return self;
end

-- @name render
-- @description Renders the File by parsing the info gathered.
function File:render()
	if self.flags.hasErrors then
		return ('%s | File #%d'):format( getCardBack( self.parent:getRegion().index ), self.id );
	end

	-- Build file:
	local file = ( self.file or buildFile( self ) ):gsub( '[/:]' , '' )

	-- Build caption:
	local caption = StringBuffer()
		:add( self.number and UTIL.link( self.number ) )
		:add(
			self.rarity and ('(%s)'):format(
				UTIL.link( self.rarity.full, self.rarity.abbr )
			)
		)
		:flush( ' ' )
		:add( self.edition and UTIL.link( self.edition.full ) )

	for _, release in ipairs( self.releases ) do
		caption:add( UTIL.link( release.full ) );
	end

	caption
		:add( self.flags.isOP and UTIL.link( OFFICIAL_PROXY.full ) )
		:add(
			UTIL.italic(
				UTIL.link(
					self.set,
					self.setEn:match( '%(2011%)' ) and self.setEn -- or self.setEn:match( '%(series%)' )
				)
			)
		)
		:add( self.description )
		:flush( '<br />' )
	;

	return ('%s | %s'):format( file, caption:toString() );
end

----------
-- Return:
----------
-- @exports The `File` class.
return File;
-- </pre>