Module:Chemistry: Difference between revisions
From Space Station 14 Wiki
No edit summary |
fix collapsible config unique counter not persisting between module calls |
||
(14 intermediate revisions by the same user not shown) | |||
Line 2: | Line 2: | ||
local getArgs = require("Module:Arguments").getArgs | local getArgs = require("Module:Arguments").getArgs | ||
local yesNo = require("Module:Yesno") | local yesNo = require("Module:Yesno") | ||
local Color = require("Module:Color") | |||
local VariablesLua = mw.ext.VariablesLua | |||
-- local reagents_table = mw.loadJsonData("Module:Chemistry/data/auto/reagents.json"); | -- local reagents_table = mw.loadJsonData("Module:Chemistry/data/auto/reagents.json"); | ||
-- local reactions_table = mw.loadJsonData("Module:Chemistry/data/auto/reactions.json"); | -- local reactions_table = mw.loadJsonData("Module:Chemistry/data/auto/reactions.json"); | ||
local reagent_card_templatestyles_src = "Template:Reagent card/styles.css" | |||
-- ============================== | |||
local are_template_styles_loaded = false | |||
-- ============================== | -- ============================== | ||
Line 149: | Line 157: | ||
-- ============================== | -- ============================== | ||
-- | -- Creates a template styles node, loading styles from `src`. | ||
local function create_template_styles_node(frame, src) | |||
function | return frame:extensionTag("templatestyles", "", { src = src }) | ||
end | |||
if | |||
local function ensure_frame(maybe_frame) | |||
if type(maybe_frame) == "table" and maybe_frame.expandTemplate then | |||
return maybe_frame -- valid frame | |||
else | else | ||
return mw.getCurrentFrame() | |||
end | end | ||
end | end | ||
-- ============================== | -- ============================== | ||
local collapsible_config_uniq_counter_var_name = "module-collapsible-config-uniq-counter" | |||
-- Generates a config for custom collapsible elements using a unique ID. | |||
-- The ID must be unique for the current page. | |||
-- | |||
-- Returns 2 values: a class for a toggle element and an ID for a collapsible element. | |||
local function generate_collapsible_config(unique_id) | |||
-- Ensures that in case of duplicates of collapsibles there would be basically no collisions. | |||
local unique_counter = VariablesLua.var(collapsible_config_uniq_counter_var_name, 0) | |||
unique_counter = VariablesLua.vardefineecho(collapsible_config_uniq_counter_var_name, unique_counter + 1) | |||
return ("mw-customtoggle-" .. unique_id .. "-" .. unique_counter), | |||
("mw-customcollapsible-" .. unique_id .. "-" .. unique_counter) | |||
end | |||
-- Calculates text color of a reagent header based on its background color. | -- Calculates text color of a reagent header based on its background color. | ||
-- Returns either `black` or `white`. | -- Returns either `black` or `white`. | ||
-- Uses the same algorithm the game uses. | -- Uses the same algorithm the game uses. | ||
function calculate_reagent_header_text_color( | local function calculate_reagent_header_text_color(col_obj) | ||
local r, g, b = | local r, g, b = col_obj:rgba() | ||
return 0.2126 * r + 0.7152 * g + 0.0722 * b > 0.5 | return 0.2126 * r + 0.7152 * g + 0.0722 * b > 0.5 | ||
and "black" | and "black" | ||
Line 172: | Line 197: | ||
end | end | ||
-- Creates a collapsible section to use in reagent card. | |||
-- @param label Label to use as a "header" for the section, as well as a collapse/show button. | |||
-- @param parent_el Element to use as parent for node creation. | |||
-- @param collapsible_id A unique ID to use to bind collapsible elements. | |||
local function reagent_card_collapsible_section(label, parent_el, collapsible_id) | |||
local toggle_class, collapsible_id = generate_collapsible_config(collapsible_id) | |||
local label_el = parent_el:tag("p") | |||
:wikitext(label) | |||
:addClass("reagent-card-spoiler-section-label " .. toggle_class) | |||
local content_el = parent_el:tag("div") | |||
:attr("id", collapsible_id) | |||
:addClass("reagent-card-spoiler-section mw-collapsible mw-collapsed") | |||
return content_el | |||
end | |||
-- Creates a text section to use in reagent card. | |||
-- @param parent_el Element to use as parent for node creation. | |||
local function reagent_card_text_section(parent_el) | |||
return parent_el:tag("p") | |||
:addClass("reagent-card-text-section") | |||
end | |||
local function auto_reagent_card(frame, args) | local function auto_reagent_card(frame, args) | ||
Line 183: | Line 231: | ||
local product_id = args.id | local product_id = args.id | ||
local color = args.color | local color = args.color | ||
local recipes = args.recipes | local recipes = args.recipe or args.recipes | ||
local description = args.desc | local description = args.desc | ||
local physical_description = args.physicalDesc | local physical_description = args.physicalDesc | ||
local effects = args.effects or args.metabolisms | local effects = args.effect or args.effects or args.metabolisms | ||
local plant_effects = args.plantMetabolism or args.plantMetabolisms -- todo; label spelled "Plant Metabolism" | |||
local sources = args.source or args.sources -- todo; label spelled "Sources" | |||
-- =============== | -- =============== | ||
Line 194: | Line 244: | ||
if product_id then | if product_id then | ||
card_container_el:attr("id", " | card_container_el:attr("id", "chem_" .. product_id) | ||
end | end | ||
local header_el = card_container_el:tag(" | local header_el = card_container_el:tag("p") | ||
:addClass("reagent-card-header") | :addClass("reagent-card-header") | ||
Line 204: | Line 254: | ||
if color then | if color then | ||
header_el:css("background-color", color); | local success, col_obj = Color.tryColor(color) | ||
if success then | |||
header_el:css("background-color", color); | |||
header_label_el:css("color", calculate_reagent_header_text_color(col_obj)) | |||
end | |||
end | end | ||
local collapsible_id_base = product_id or product_name | |||
if sources and #sources > 0 then | |||
local section = reagent_card_collapsible_section("Sources", card_container_el, collapsible_id_base .. "-sources") | |||
if type(sources) == "string" then section:wikitext(sources) else section:node(sources) end | |||
end | |||
if recipes and #recipes > 0 then | if recipes and #recipes > 0 then | ||
local | local section = reagent_card_collapsible_section("Recipe", card_container_el, collapsible_id_base .. "-recipes") | ||
if type(recipes) == "string" then section:wikitext(recipes) else section:node(recipes) end | |||
end | |||
if effects and #effects > 0 then | |||
local section = reagent_card_collapsible_section("Effects", card_container_el, collapsible_id_base .. "-effects") | |||
if type(effects) == "string" then section:wikitext(effects) else section:node(effects) end | |||
end | end | ||
if | if plant_effects and #plant_effects > 0 then | ||
local | local section = reagent_card_collapsible_section("Plant Metabolism", card_container_el, collapsible_id_base .. "-plant-effects") | ||
if type(plant_effects) == "string" then section:wikitext(plant_effects) else section:node(plant_effects) end | |||
end | end | ||
if description then | if description then | ||
local | local section = reagent_card_text_section(card_container_el) | ||
:wikitext(description) | :wikitext(description) | ||
end | end | ||
if physical_description then | if physical_description then | ||
local | local section = reagent_card_text_section(card_container_el) | ||
: | :addClass("cursive") | ||
:wikitext("Seems to be " .. physical_description .. ".") | |||
end | |||
if not are_template_styles_loaded then | |||
card_container_el:node(create_template_styles_node(frame, reagent_card_templatestyles_src)) | |||
are_template_styles_loaded = true | |||
end | end | ||
Line 252: | Line 321: | ||
function p.reagent_card(frame) | function p.reagent_card(frame) | ||
local args = getArgs(frame) | local args = getArgs(frame) | ||
frame = ensure_frame(frame) | |||
if args[1] == nil then | if args[1] == nil then | ||
return manual_reagent_card(frame, args) | return manual_reagent_card(frame, args) | ||
Line 267: | Line 338: | ||
function p.reaction_card(frame) | function p.reaction_card(frame) | ||
local args = getArgs(frame) | local args = getArgs(frame) | ||
frame = ensure_frame(frame) | |||
if args[1] == nil then | if args[1] == nil then | ||
return manual_reaction_card(frame, args) | return manual_reaction_card(frame, args) |
Latest revision as of 11:50, 29 May 2025
Module documentation
|
---|
View or edit this documentation • (about module documentation) |
local p = {} --p stands for package
local getArgs = require("Module:Arguments").getArgs
local yesNo = require("Module:Yesno")
local Color = require("Module:Color")
local VariablesLua = mw.ext.VariablesLua
-- local reagents_table = mw.loadJsonData("Module:Chemistry/data/auto/reagents.json");
-- local reactions_table = mw.loadJsonData("Module:Chemistry/data/auto/reactions.json");
local reagent_card_templatestyles_src = "Template:Reagent card/styles.css"
-- ==============================
local are_template_styles_loaded = false
-- ==============================
local function assert_not_nil(value, error_message)
if value == nil then
if error_message == nil then
error("value is nil")
else
error(error_message)
end
end
end
local function starts_with(str, substr)
return string.sub(str, 1, string.len(substr)) == substr
end
local function ends_with(str, substr)
local substr_length = string.len(substr)
return string.sub(str, string.len(str) - substr_length + 1, string.len(str)) == substr
end
local function starts_with_insensitive(str, substr)
return starts_with(string.lower(str), string.lower(substr))
end
local function ends_with_insensitive(str, substr)
return ends_with(string.lower(str), string.lower(substr))
end
local function table_filter(tbl, filterFn)
local out = {}
for k, v in pairs(tbl) do
if filterFn(v, k, tbl) then out[k] = v end
end
return out
end
local function num_table_reduce(tbl, reduceFn, initial_value)
local out = initial_value
for i, v in ipairs(tbl) do
out = reduceFn(out, v, i, tbl)
end
return out
end
local function table_find(tbl, findFn)
for k, v in pairs(tbl) do
if findFn(v, k, tbl) then return v end
end
end
local function table_contains(tbl, findFn)
return table_find(tbl, findFn) ~= nil
end
local function table_contains_value(tbl, value)
return table_find(tbl, function(iter_value)
return iter_value == value
end) ~= nil
end
local function table_find_key(tbl, findFn)
for k, v in pairs(tbl) do
if findFn(v, k, tbl) then return k end
end
end
local function table_find_index(tbl, findFn)
for i, v in ipairs(tbl) do
if findFn(v, i, tbl) then return i end
end
end
local function table_map(tbl, mapFn)
local res = {}
for k, v in pairs(tbl) do
table.insert(res, mapFn(v, k, tbl))
end
return res
end
local function numeric_table_length(t)
local count = 0
for _ in ipairs(t) do count = count + 1 end
return count
end
local function table_length(t)
local count = 0
for _ in pairs(t) do count = count + 1 end
return count
end
-- Returns keys of a table.
local function table_keys(tbl)
local arr = {}
for key, _ in pairs(tbl) do
table.insert(arr, key)
end
return arr
end
-- Sorts a table into a new table.
-- An alternative to cases such as when table.sort is not working,
-- like when trying to sort JSON tables (thanks fuckass lua).
local function table_to_sorted(tbl, sortfn)
local keys = {}
for key, _ in pairs(tbl) do
table.insert(keys, key)
end
table.sort(keys, function(keyA, keyB) return sortfn(tbl[keyA], tbl[keyB]) end)
local t2 = {}
for _, key in ipairs(keys) do
table.insert(t2, tbl[key])
end
return t2
end
-- Given a table and a property key, attempts to retrieve said property.
-- If property does not exists, creates it with by assigning a value from `create_value_fn`.
-- The created value is then returned.
function get_table_prop_or_create(tbl, prop_key, create_value_fn)
local value = tbl[prop_key]
if value == nil then
value = create_value_fn()
tbl[prop_key] = value
end
return value;
end
-- ==============================
-- Creates a template styles node, loading styles from `src`.
local function create_template_styles_node(frame, src)
return frame:extensionTag("templatestyles", "", { src = src })
end
local function ensure_frame(maybe_frame)
if type(maybe_frame) == "table" and maybe_frame.expandTemplate then
return maybe_frame -- valid frame
else
return mw.getCurrentFrame()
end
end
-- ==============================
local collapsible_config_uniq_counter_var_name = "module-collapsible-config-uniq-counter"
-- Generates a config for custom collapsible elements using a unique ID.
-- The ID must be unique for the current page.
--
-- Returns 2 values: a class for a toggle element and an ID for a collapsible element.
local function generate_collapsible_config(unique_id)
-- Ensures that in case of duplicates of collapsibles there would be basically no collisions.
local unique_counter = VariablesLua.var(collapsible_config_uniq_counter_var_name, 0)
unique_counter = VariablesLua.vardefineecho(collapsible_config_uniq_counter_var_name, unique_counter + 1)
return ("mw-customtoggle-" .. unique_id .. "-" .. unique_counter),
("mw-customcollapsible-" .. unique_id .. "-" .. unique_counter)
end
-- Calculates text color of a reagent header based on its background color.
-- Returns either `black` or `white`.
-- Uses the same algorithm the game uses.
local function calculate_reagent_header_text_color(col_obj)
local r, g, b = col_obj:rgba()
return 0.2126 * r + 0.7152 * g + 0.0722 * b > 0.5
and "black"
or "white"
end
-- Creates a collapsible section to use in reagent card.
-- @param label Label to use as a "header" for the section, as well as a collapse/show button.
-- @param parent_el Element to use as parent for node creation.
-- @param collapsible_id A unique ID to use to bind collapsible elements.
local function reagent_card_collapsible_section(label, parent_el, collapsible_id)
local toggle_class, collapsible_id = generate_collapsible_config(collapsible_id)
local label_el = parent_el:tag("p")
:wikitext(label)
:addClass("reagent-card-spoiler-section-label " .. toggle_class)
local content_el = parent_el:tag("div")
:attr("id", collapsible_id)
:addClass("reagent-card-spoiler-section mw-collapsible mw-collapsed")
return content_el
end
-- Creates a text section to use in reagent card.
-- @param parent_el Element to use as parent for node creation.
local function reagent_card_text_section(parent_el)
return parent_el:tag("p")
:addClass("reagent-card-text-section")
end
local function auto_reagent_card(frame, args)
error("not impl")
end
local function manual_reagent_card(frame, args)
local product_name = args.name
assert_not_nil(product_name, "failed to generate manual reagent card: product name not provided")
local product_id = args.id
local color = args.color
local recipes = args.recipe or args.recipes
local description = args.desc
local physical_description = args.physicalDesc
local effects = args.effect or args.effects or args.metabolisms
local plant_effects = args.plantMetabolism or args.plantMetabolisms -- todo; label spelled "Plant Metabolism"
local sources = args.source or args.sources -- todo; label spelled "Sources"
-- ===============
local card_container_el = mw.html.create("div")
:addClass("reagent-card")
if product_id then
card_container_el:attr("id", "chem_" .. product_id)
end
local header_el = card_container_el:tag("p")
:addClass("reagent-card-header")
local header_label_el = header_el:tag("span")
:wikitext(product_name)
if color then
local success, col_obj = Color.tryColor(color)
if success then
header_el:css("background-color", color);
header_label_el:css("color", calculate_reagent_header_text_color(col_obj))
end
end
local collapsible_id_base = product_id or product_name
if sources and #sources > 0 then
local section = reagent_card_collapsible_section("Sources", card_container_el, collapsible_id_base .. "-sources")
if type(sources) == "string" then section:wikitext(sources) else section:node(sources) end
end
if recipes and #recipes > 0 then
local section = reagent_card_collapsible_section("Recipe", card_container_el, collapsible_id_base .. "-recipes")
if type(recipes) == "string" then section:wikitext(recipes) else section:node(recipes) end
end
if effects and #effects > 0 then
local section = reagent_card_collapsible_section("Effects", card_container_el, collapsible_id_base .. "-effects")
if type(effects) == "string" then section:wikitext(effects) else section:node(effects) end
end
if plant_effects and #plant_effects > 0 then
local section = reagent_card_collapsible_section("Plant Metabolism", card_container_el, collapsible_id_base .. "-plant-effects")
if type(plant_effects) == "string" then section:wikitext(plant_effects) else section:node(plant_effects) end
end
if description then
local section = reagent_card_text_section(card_container_el)
:wikitext(description)
end
if physical_description then
local section = reagent_card_text_section(card_container_el)
:addClass("cursive")
:wikitext("Seems to be " .. physical_description .. ".")
end
if not are_template_styles_loaded then
card_container_el:node(create_template_styles_node(frame, reagent_card_templatestyles_src))
are_template_styles_loaded = true
end
return card_container_el
end
local function auto_reaction_card(frame, args)
error("not impl")
end
local function manual_reaction_card(frame, args)
error("not impl")
end
-- ==============================
-- Generates a reagent card element, containing
-- info about a reagent such as description, recipes, effects, etc.
--
-- Has 2 modes depending on what is passed:
-- - Automatic - when first argument is a reagent ID. This triggers automatic data lookup.
-- - Manual - when there are only named arguments. This allows to fill every parameter manually.
function p.reagent_card(frame)
local args = getArgs(frame)
frame = ensure_frame(frame)
if args[1] == nil then
return manual_reagent_card(frame, args)
else
return auto_reagent_card(frame, args)
end
end
-- Generates a reaction card element, containing
-- info about a chemical reaction.
--
-- Has 2 modes depending on what is passed:
-- - Automatic - when first argument is a reagent ID. This triggers automatic data lookup.
-- - Manual - when there are only named arguments. This allows to fill every parameter manually.
function p.reaction_card(frame)
local args = getArgs(frame)
frame = ensure_frame(frame)
if args[1] == nil then
return manual_reaction_card(frame, args)
else
return auto_reaction_card(frame, args)
end
end
return p