Module:Definitions: Difference between revisions
From SpiritVale Wiki
More actions
No edit summary |
No edit summary |
||
| Line 5: | Line 5: | ||
-- Module:Definitions/Definitions.json | -- Module:Definitions/Definitions.json | ||
-- | -- | ||
-- JSON shape | -- JSON shape: | ||
-- { | -- { | ||
-- "Schema": 1, | -- "Schema": 1, | ||
| Line 17: | Line 17: | ||
-- - Domains + keys are discovered from JSON (no hardcoded domain lists). | -- - Domains + keys are discovered from JSON (no hardcoded domain lists). | ||
-- - CSS namespace: ONLY "sv-def" (plus sv-def--* modifiers). | -- - CSS namespace: ONLY "sv-def" (plus sv-def--* modifiers). | ||
-- - | -- - Output classes: sv-def, sv-def-icon, sv-def-icon-img, sv-def-text. | ||
-- | |||
local p = {} | local p = {} | ||
| Line 25: | Line 24: | ||
-- ============================================================================= | -- ============================================================================= | ||
-- | -- Helpers | ||
-- ============================================================================= | -- ============================================================================= | ||
| Line 103: | Line 102: | ||
end | end | ||
-- Case-insensitive match across top-level keys | -- Case-insensitive match across top-level keys that are tables | ||
local want = lc(domain) | local want = lc(domain) | ||
for k, v in pairs(db) do | for k, v in pairs(db) do | ||
if type(v) == "table" and lc(k) == want then | if type(v) == "table" and lc(k) == want then | ||
return k | |||
end | end | ||
end | end | ||
| Line 124: | Line 121: | ||
-- ============================================================================= | -- ============================================================================= | ||
-- Icon rendering | -- Icon rendering | ||
-- - Blank icon: | -- - Blank icon: render nothing (no generic dot/circle) | ||
-- - Missing file: | -- - Missing file: render "?" badge (no redlink image) | ||
-- ============================================================================= | -- ============================================================================= | ||
| Line 131: | Line 128: | ||
icon = trim(icon) | icon = trim(icon) | ||
if icon == "" then | if icon == "" then | ||
return "" | return "" | ||
| Line 143: | Line 139: | ||
local t = mw.title.new(fileTitle) | local t = mw.title.new(fileTitle) | ||
if not t or not t.exists then | if not t or not t.exists then | ||
return '<span class="sv-def-icon sv-def-icon--missing" aria-hidden="true">?</span>' | return '<span class="sv-def-icon sv-def-icon--missing" aria-hidden="true">?</span>' | ||
end | end | ||
return '<span class="sv-def-icon-img">[[' .. fileTitle .. '|16px|link=]]</span>' | return '<span class="sv-def-icon-img">[[' .. fileTitle .. '|16px|link=]]</span>' | ||
end | end | ||
| Line 179: | Line 173: | ||
local d_lc = lc(domain) | local d_lc = lc(domain) | ||
-- Missing record: show a visible hint but NO tooltip/link data. | |||
if rec == nil then | |||
return | |||
'<span class="sv-def sv-def--missing sv-def--' .. enc_attr(d_lc) .. '"' .. | |||
' data-sv-def-domain="' .. enc_attr(domain) .. '"' .. | |||
' data-sv-def-key="' .. enc_attr(key) .. '"' .. | |||
'>' .. | |||
'<span class="sv-def-icon sv-def-icon--missing" aria-hidden="true">?</span>' .. | |||
'<span class="sv-def-text">' .. mw.text.nowiki(name) .. '</span>' .. | |||
'</span>' | |||
end | |||
local attrs = { | local attrs = { | ||
| Line 186: | Line 192: | ||
} | } | ||
-- Only include tooltip/link attributes when populated. | |||
if defn ~= "" then | if defn ~= "" then | ||
attrs[#attrs + 1] = 'data-sv-def-tip="' .. enc_attr(defn) .. '"' | attrs[#attrs + 1] = 'data-sv-def-tip="' .. enc_attr(defn) .. '"' | ||
| Line 195: | Line 202: | ||
local title_attr = (defn ~= "") and (' title="' .. enc_attr(defn) .. '"') or "" | local title_attr = (defn ~= "") and (' title="' .. enc_attr(defn) .. '"') or "" | ||
return | |||
'<span ' .. table.concat(attrs, " ") .. title_attr .. '>' .. | '<span ' .. table.concat(attrs, " ") .. title_attr .. '>' .. | ||
icon_html(icon) .. | icon_html(icon) .. | ||
'<span class="sv-def-text">' .. mw.text.nowiki(name) .. '</span>' .. | '<span class="sv-def-text">' .. mw.text.nowiki(name) .. '</span>' .. | ||
'</span>' | '</span>' | ||
end | end | ||
| Line 214: | Line 213: | ||
-- ============================================================================= | -- ============================================================================= | ||
function p.def(frame) | function p.def(frame) | ||
local a = frame.args or {} | local a = frame.args or {} | ||
| Line 220: | Line 218: | ||
end | end | ||
function p.render(domain, key) | function p.render(domain, key) | ||
return render(domain, key) | return render(domain, key) | ||
Revision as of 20:13, 18 February 2026
Module:Definitions
This module implements the Definitions v1 system used by Template:Def. It resolves a (Domain, Key) pair against a JSON database and outputs a small inline term with optional tooltip text.
Purpose
- Provide consistent, reusable definitions across the wiki.
- Keep usage explicit to avoid collisions (no guessing).
- Allow pages to show definitions without requiring navigation.
Usage
The public interface is the template Template:Def:
{{def|Domain|Key}}
Examples:
{{def|Stat|Vit}}
{{def|Element|Fire}}
{{def|Cast|Ground}}
{{def|Target|Summon}}
System components
1) Template:def
Page: Template:def
This template calls Module:Definitions and passes the provided parameters.
2) Module:Definitions
Page: Module:Definitions
Responsibilities:
- Load the JSON database.
- Resolve
DomainandKey. - Output HTML with
sv-defclasses anddata-sv-def-*attributes.
Tooltip rule:
- If
Definitionis empty, no tooltip is shown.
Link rule:
- If
Linkis non-empty, the term text is rendered as a real<a>(clickable). - If
Linkis empty, the term text is rendered as plain text (tooltip-only).
3) Definitions database (JSON)
Page: Module:Definitions/Definitions.json
This page is the Definitions v1 database.
Schema:
{
"Schema": 1,
"UpdatedAt": "YYYY-MM-DD",
"Stat": {
"Vit": { "Name": "Vitality", "Definition": "...", "Icon": "", "Link": "" }
},
"Element": {
"Fire": { "Name": "Fire", "Definition": "", "Icon": "", "Link": "" }
}
}
Rules:
- Top-level keys are Domains (example:
Stat,Element). - Each Domain contains Keys (example:
Vit,Fire). - Keys are CamelCase and should match the intended enum key names.
NameandDefinitionare user-facing fields.IconandLinkmay be blank.
Supported v1 domains:
CastDamageElementAuraEventStanceStatTarget
Styling
CSS is provided by TemplateStyles:
- Page:
Template:def/styles.css - Namespace:
sv-defonly
JavaScript
Tooltip behavior is implemented in site JavaScript:
- Page:
MediaWiki:Common.js
The script reads:
data-sv-def-tip(tooltip text)data-sv-def-link(reserved / optional)
Fallback behavior
- Invalid arguments render an error placeholder.
- Missing records render a missing placeholder.
- If
Nameis empty, the module displays a humanized version of the Key. - If
Definitionis empty, tooltip behavior is disabled. - If an icon file is missing, the module displays
?instead of a redlink image.
-- Module:Definitions
-- Core resolver + renderer for {{def|Domain|Key}} (Definitions v1).
--
-- Data source (static):
-- Module:Definitions/Definitions.json
--
-- JSON shape:
-- {
-- "Schema": 1,
-- "UpdatedAt": "YYYY-MM-DD",
-- "Stat": { "Vit": { "Name":"...", "Definition":"...", "Icon":"", "Link":"" }, ... },
-- "Target": { ... },
-- ...
-- }
--
-- Notes:
-- - Domains + keys are discovered from JSON (no hardcoded domain lists).
-- - CSS namespace: ONLY "sv-def" (plus sv-def--* modifiers).
-- - Output classes: sv-def, sv-def-icon, sv-def-icon-img, sv-def-text.
local p = {}
local DATA_TITLE = "Module:Definitions/Definitions.json"
-- =============================================================================
-- Helpers
-- =============================================================================
local function trim(s)
if type(s) ~= "string" then return "" end
return mw.text.trim(s)
end
local function lc(s)
return string.lower(tostring(s or ""))
end
local function enc_attr(s)
return mw.text.encode(tostring(s or ""), '"<>&')
end
local function humanize(key)
key = tostring(key or "")
key = key:gsub("([a-z0-9])([A-Z])", "%1 %2")
key = key:gsub("([A-Z]+)([A-Z][a-z])", "%1 %2")
key = key:gsub("%s+", " ")
return mw.text.trim(key)
end
local function find_ci(t, key)
if type(t) ~= "table" then return nil end
if t[key] ~= nil then return t[key] end
local want = lc(key)
for k, v in pairs(t) do
if lc(k) == want then return v end
end
return nil
end
-- =============================================================================
-- DB load (cached)
-- =============================================================================
local DB = nil
local function load_db()
if DB ~= nil then return DB end
local title = mw.title.new(DATA_TITLE)
if not title then
DB = {}
return DB
end
local raw = title:getContent()
if type(raw) ~= "string" or raw == "" then
DB = {}
return DB
end
local ok, obj = pcall(mw.text.jsonDecode, raw)
if not ok or type(obj) ~= "table" then
DB = {}
return DB
end
DB = obj
return DB
end
-- =============================================================================
-- Domain + record resolution (dynamic)
-- =============================================================================
local function norm_domain(db, domain)
domain = trim(domain)
if domain == "" then return nil end
-- Exact match first
if type(db[domain]) == "table" then
return domain
end
-- Case-insensitive match across top-level keys that are tables
local want = lc(domain)
for k, v in pairs(db) do
if type(v) == "table" and lc(k) == want then
return k
end
end
return nil
end
local function get_record(db, domain, key)
local dom = db[domain]
if type(dom) ~= "table" then return nil end
return find_ci(dom, key)
end
-- =============================================================================
-- Icon rendering
-- - Blank icon: render nothing (no generic dot/circle)
-- - Missing file: render "?" badge (no redlink image)
-- =============================================================================
local function icon_html(icon)
icon = trim(icon)
if icon == "" then
return ""
end
local fileTitle = icon
if not fileTitle:match("^[Ff]ile:") then
fileTitle = "File:" .. fileTitle
end
local t = mw.title.new(fileTitle)
if not t or not t.exists then
return '<span class="sv-def-icon sv-def-icon--missing" aria-hidden="true">?</span>'
end
return '<span class="sv-def-icon-img">[[' .. fileTitle .. '|16px|link=]]</span>'
end
-- =============================================================================
-- Render
-- =============================================================================
local function render(domain, key)
local db = load_db()
domain = norm_domain(db, domain)
key = trim(key)
if not domain or key == "" then
return '<span class="sv-def sv-def--error">?</span>'
end
local rec = get_record(db, domain, key)
local name, defn, icon, link = "", "", "", ""
if type(rec) == "table" then
name = trim(rec.Name)
defn = trim(rec.Definition)
icon = trim(rec.Icon)
link = trim(rec.Link)
end
if name == "" then
name = humanize(key)
end
local d_lc = lc(domain)
-- Missing record: show a visible hint but NO tooltip/link data.
if rec == nil then
return
'<span class="sv-def sv-def--missing sv-def--' .. enc_attr(d_lc) .. '"' ..
' data-sv-def-domain="' .. enc_attr(domain) .. '"' ..
' data-sv-def-key="' .. enc_attr(key) .. '"' ..
'>' ..
'<span class="sv-def-icon sv-def-icon--missing" aria-hidden="true">?</span>' ..
'<span class="sv-def-text">' .. mw.text.nowiki(name) .. '</span>' ..
'</span>'
end
local attrs = {
'class="sv-def sv-def--' .. enc_attr(d_lc) .. '"',
'data-sv-def-domain="' .. enc_attr(domain) .. '"',
'data-sv-def-key="' .. enc_attr(key) .. '"',
}
-- Only include tooltip/link attributes when populated.
if defn ~= "" then
attrs[#attrs + 1] = 'data-sv-def-tip="' .. enc_attr(defn) .. '"'
end
if link ~= "" then
attrs[#attrs + 1] = 'data-sv-def-link="' .. enc_attr(link) .. '"'
end
local title_attr = (defn ~= "") and (' title="' .. enc_attr(defn) .. '"') or ""
return
'<span ' .. table.concat(attrs, " ") .. title_attr .. '>' ..
icon_html(icon) ..
'<span class="sv-def-text">' .. mw.text.nowiki(name) .. '</span>' ..
'</span>'
end
-- =============================================================================
-- Public API
-- =============================================================================
function p.def(frame)
local a = frame.args or {}
return render(a[1] or a.Domain or a.domain, a[2] or a.Key or a.key)
end
function p.render(domain, key)
return render(domain, key)
end
return p