Module:Exclusive
This module is the core of the dynamic platform exclusivity system.
Important note: This module relies entirely on the database Module:Exclusive/data. The database is not updated automatically and instead requires periodic manual updates. Please see its documentation for instructions.
Usage
Wikitext
The module can be called from wikitext with the functions listed below.
getInfo
{{#invoke:Exclusive| getInfo | <entity name> | invert = y/yes/1 | pagenot = <entity name> }}
This function is generally only intended for templates like {{exclusive}}. It queries the exclusivity information database and stores the result to a set of dplvars. These are named in the format ex_<platform>
, e.g. ex_d
for Desktop, and they are either empty or set to y
. The function does not produce any output.
eicons
{{#invoke:Exclusive| eicons | <combined parameters> }}
This function is generally only intended for {{eicons}}. It returns the HTML code for the icons denoting platform exclusivity.
This function expects only a single parameter in the following format: @page:<entity name>@invert:y/yes/1@pagenot:<entity name>@small:y/yes/1@lang:<language>@jdcom3:<version overrides>
This is to improve performance on repeated calls.
jdcom3
is expected to in the format <j>:<d>:<c>:<o>:<m>:<3>
, with each platform either y/yes/1 / n/no/0
or an empty string.
Other modules
The module can be called from another module with the functions listed below.
simpleEicons
require('Module:Exclusive').simpleEicons('<entity name>', '<language>', true/false)
Returns the HTML code for the icons denoting platform exclusivity. The third parameter controls whether to use small icons.
Code | Variable result
|
---|---|
local exclusive = require('Module:Exclusive')
local result = exclusive.simpleEicons('Shimmer')
|
'<span class="eico i1 i2 i4" title="Desktop, Console and Mobile versions"><b></b><i></i><span>(Desktop, Console and Mobile versions)</span></span>'
|
---Holds the tables with the l10n information for the different languages, taken from the l10n submodule.
local l10n_data = mw.loadData('Module:Exclusive/l10n')
---Database with exclusivity info.
---luacache.get() is relative slow, therefore we load data from luacache only once per page.
local exclusive_info = mw.loadData('Module:Exclusive/loaddata')
local bit32 = require('bit32')
local trim = mw.text.trim
---Default content language of the wiki (i.e. `$wgLanguageCode`, not the value of the
---`uselang` URL parameter, and not the user's language preference setting).
local contentLanguage = mw.getContentLanguage()
---Holds the arguments from the template call.
local args_table
---The current language. Determines which l10n table to use.
local lang
---Return the l10n string associated with the `key`.
---@param key string
---@return string
local function l10n(key)
if l10n_data[lang] then
return l10n_data[lang][key] or l10n_data['en'][key]
else
return l10n_data['en'][key]
end
end
---Return a trimmed version of the value of the template parameter with the specified `key`.
---Return `nil` if the parameter is empty or unset.
---@param key string|number
---@return string|nil
local function getArg(key)
local value = trim(args_table[key] or '')
return (value ~= '') and value or nil
end
---Convert a string of parameters in a `@param1:value^@param2:value^` format to a table.
---@param paramstr string
---@return table
local function parse(paramstr)
local args = {}
for s in string.gmatch(paramstr, '%b@^') do
local k,v = string.match(s, '^@(.-):(.*)^$')
args[k] = v
end
return args
end
---Split the `str` on each `div` in it and return the result as a table.
---This is much much faster then `mw.text.split`.
---Credit: http://richard.warburton.it.
---@param div string
---@param str string
---@return table|boolean
local function explode(div,str)
if (div=='') then return false end
local pos,arr = 0,{}
-- for each divider found
for st,sp in function() return string.find(str,div,pos,true) end do
arr[#arr + 1] = string.sub(str,pos,st-1) -- Attach chars left of current divider
pos = sp + 1 -- Jump past current divider
end
arr[#arr + 1] = string.sub(str,pos) -- Attach chars right of last divider
return arr
end
---Return the integer defined in the database for the specified `page`.
---Perform some standardization on the `page` for that first.
---@param page string
---@return number
local function readFromDb(page)
-- standardize pagename: remove section parts ('x#section' -> 'x') and replace underscores with spaces
page = contentLanguage:ucfirst(string.gsub(string.gsub(page or '', '#.*', ''), '_', ' '))
return exclusive_info[page] or 0
end
---Override the exclusivity information in `info`
---with the content of `jdcom3`.
---@param info number
---@param jdcom3 string Expected format: `<j>:<d>:<c>:<o>:<m>:<3>`, with each platform either a Boolean string ("y", "0", etc.) or an empty string
---@return number
local function override(info, jdcom3)
for k, v in pairs(explode(':', jdcom3)) do
if v ~= '' then
if v == '1' or v == 'y' or v == 'yes' then
info = bit32.replace(info, 1, k-1)
elseif v == '0' or v == 'n' or v == 'no' then
info = bit32.replace(info, 0, k-1)
end
end
end
return info
-- Example to demonstrate the behavior of this function:
-- Goal: Change "dcom" to "dco3".
-- info = 30 ("dcom"), jdcom3 = "::::no:yes"
-- decimal 30 = binary 011110
-- The first "no" is at index 4 in the jdcom3 string, so
-- replace the corresponding bit with a 0: 011110 -> 001110.
-- The "yes" is at index 5, so replace the bit at index 5
-- with a 1: 001110 -> 101110.
-- The result is info = 46 ("dco3").
end
---Main function to retrieve exclusivity information.
---@param page string The entity to get the info about.
---@param invert boolean Whether to invert the exclusivity info.
---@param pagenot string The entity whose exclusivity info to subtract from the main one's.
---@param jdcom3 string Manual exclusivity info to override the fetched one with.
---@return number info An integer that holds the exclusivity information.
local function getInfo(page, invert, pagenot, jdcom3)
local info = 0
-- A piece of exclusivity information is a set of Boolean values, one
-- for each platform. This is represented as bits of the `info` integer.
-- Each platform (Japanese console, Desktop, Console, Old-gen console,
-- Mobile, and 3DS – "jdcom3") is assigned one bit, in this order.
-- This means that, for instance, an `info` value of 2 would represent
-- Desktop-only exclusivity ("d"):
-- decimal 2 = binary 000010
-- 3mocdj -> "d"
-- Similarly, an `info` value of 40 would represent Old-gen and 3DS exclusivity ("o3"):
-- decimal 40 = binary 101000
-- 3mocdj -> "o3"
-- See Module:Exclusive/data for a quick overview of all values.
-- This system allows using bitwise operations (https://en.wikipedia.org/wiki/Bitwise_operation)
-- instead of the historically used string processing, resulting in much lower script execution times.
-- get info about page
if page then
info = readFromDb(page)
if invert then
-- invert jdcom3 and set j=0 (always force-off Japanese console when inverting)
-- (3E in hexadecimal is 111110 in binary, i.e. "dcom3")
info = bit32.band(bit32.bnot(info), 0x3E)
end
if pagenot then
-- exclude some versions, depending on pagenot
local info_not = readFromDb(pagenot)
info = bit32.band(info, bit32.bnot(info_not))
end
-- The "invert" and "pagenot" functionalities above utilize
-- bit masking (https://en.wikipedia.org/wiki/Mask_(computing)).
-- The following example demonstrates the operations:
-- 1. assume info=22 ("dcm", binary 010110)
-- 2. invert:
-- 2a. not(010110) = 101001 ("jo3", the inverse of "dcm")
-- 2b. and(101001, 111110) = 101000 ("o3", forced-off "j")
-- 3. assume info_not=8 ("o", binary 001000)
-- 3a. not(001000) = 110111
-- 3b. and(101000, 110111) = 100000 ("3")
-- An initial exclusivity info of "dcm" was inverted to "o3",
-- then "o" was subtracted from it, resulting in the final
-- exclusivity information of "3".
end
-- override if needed
if jdcom3 == nil or jdcom3 == ':::::' then
return info
else
return override(info, jdcom3)
end
end
---Return an HTML span tag whose `class` attribute is set
---according to the exclusivity `info`.
---@param info number
---@param _small boolean Whether to add the "s" class, for small icons
---@return string
local function eicons(info, _small)
local class = _small and 'eico s' or 'eico'
local hovertext
if bit32.btest(info, 0x01) then
-- Japanese console is set, so simply display that
-- ("j" is always alone or not set at all – "dcj", for instance, doesn't exist)
return '<span class="'..class..' j" title="'..l10n('text_j')..'"></span>';
end
-- for each platform of dcom3, add the class and load the hovertext if the platform if set
-- (e.g. for info=50 ("dm3"), append "i1 i4 i5" to the class and load the
-- "text_1", "text_4", and "text_5" l10n strings)
local v = {}
for i = 1, 5 do -- 0 is "j"
if bit32.btest(info, 2^i) then
class = class .. " i" .. i
v[#v+1] = l10n('text_' .. i)
end
end
local hovertext = mw.text.listToText(v, l10n('list_separator'), l10n('list_conjunction'))
hovertext = string.format(contentLanguage:convertPlural(#v, l10n('version_plural_forms')), hovertext)
return '<span class="'..class..'" title="'..hovertext..'"><b></b><i></i><span>('..hovertext..')</span></span>';
end
---Check if the `infoToCheck` integer is a valid number for output.
---It is considered invalid if it represents an empty exclusivity ("")
---or a "full" exclusivity, i.e. all platforms being set ("dcom3" or "jdcom3").
---@param infoToCheck number
---@return boolean
local function infoIsInvalid(infoToCheck)
return infoToCheck == 0x00 or infoToCheck == 0x3E or infoToCheck == 0x3F
end
-----------------------------------------------------------------
-- main return object
return {
-- for templates; get all exclusive info and set it in dplvars.
-- parameters: $1 = pagename
getInfo = function(frame)
args_table = frame.args -- cache
local info = getInfo(getArg(1), getArg('invert'), getArg('pagenot'))
if infoIsInvalid(info) then
frame:callParserFunction{ name = '#dplvar:set', args = {
'ex_j', '',
'ex_d', '',
'ex_c', '',
'ex_o', '',
'ex_m', '',
'ex_3', '',
'ex_cached', 'y'
} }
else
frame:callParserFunction{ name = '#dplvar:set', args = {
'ex_j', bit32.btest(info, 2^0) and 'y' or '',
'ex_d', bit32.btest(info, 2^1) and 'y' or '',
'ex_c', bit32.btest(info, 2^2) and 'y' or '',
'ex_o', bit32.btest(info, 2^3) and 'y' or '',
'ex_m', bit32.btest(info, 2^4) and 'y' or '',
'ex_3', bit32.btest(info, 2^5) and 'y' or '',
'ex_cached', 'y'
} }
end
end,
-- for {{eicons}}
eicons = function(frame)
args_table = parse(frame.args[1])-- cache
lang = getArg('lang') -- set lang for l10n
local info = getInfo(getArg('page'), getArg('invert'), getArg('pagenot'), getArg('jdcom3'))
if infoIsInvalid(info) then
return frame:expandTemplate{ title = 'error', args = { l10n('eicons_error_text'), l10n('eicons_error_cate'), from = 'Eicons' } }
end
return eicons(info, getArg('small'))
end,
-- simplified version of the eicons function above, for other modules such as Module:Item
simpleEicons = function(page, language, small)
local info = getInfo(page)
if infoIsInvalid(info) then
return ''
end
lang = language -- set lang for l10n
return eicons(info, small)
end,
}