|
|
| Line 1: |
Line 1: |
| local p = {} | | local p = {} |
|
| |
| local cargo = mw.ext.cargo
| |
|
| |
| local function esc(value)
| |
| if not value then
| |
| return ''
| |
| end
| |
| value = tostring(value)
| |
| value = value:gsub('\\', '\\\\')
| |
| value = value:gsub('"', '\\"')
| |
| return value
| |
| end
| |
|
| |
| local function cargoQuery(tables, fields, args)
| |
| args = args or {}
| |
| local ok, result = pcall(function()
| |
| return cargo.query(tables, fields, args)
| |
| end)
| |
| if ok and result then
| |
| return result
| |
| end
| |
| return {}
| |
| end
| |
|
| |
| local function trim(s)
| |
| if s == nil then
| |
| return nil
| |
| end
| |
| s = tostring(s)
| |
| s = mw.text.trim(s)
| |
| if s == '' then
| |
| return nil
| |
| end
| |
| return s
| |
| end
| |
|
| |
| local function formatYear(dateValue)
| |
| dateValue = trim(dateValue)
| |
| if not dateValue then
| |
| return nil
| |
| end
| |
| return tostring(dateValue):match('^(%d%d%d%d)')
| |
| end
| |
|
| |
| local function addUnique(list, seen, value)
| |
| value = trim(value)
| |
| if value and not seen[value] then
| |
| seen[value] = true
| |
| table.insert(list, value)
| |
| end
| |
| end
| |
|
| |
| local function addSet(set, value)
| |
| value = trim(value)
| |
| if value then
| |
| set[value] = true
| |
| end
| |
| end
| |
|
| |
| local function sorted(list)
| |
| table.sort(list, function(a, b)
| |
| return tostring(a):lower() < tostring(b):lower()
| |
| end)
| |
| return list
| |
| end
| |
|
| |
| local function getCharacter(pageName)
| |
| pageName = trim(pageName)
| |
| if not pageName then
| |
| return nil
| |
| end
| |
|
| |
| local rows = cargoQuery(
| |
| 'Characters',
| |
| 'Page,DisplayName,BirthDate,DeathDate,Status,Gender',
| |
| {
| |
| where = 'Page="' .. esc(pageName) .. '"',
| |
| limit = 1
| |
| }
| |
| )
| |
|
| |
| return rows[1]
| |
| end
| |
|
| |
| local function getDisplayName(pageName)
| |
| local c = getCharacter(pageName)
| |
| if c and trim(c.DisplayName) then
| |
| return trim(c.DisplayName)
| |
| end
| |
| return pageName
| |
| end
| |
|
| |
| local function makeLinkedName(pageName)
| |
| return '[[' .. pageName .. '|' .. getDisplayName(pageName) .. ']]'
| |
| end
| |
|
| |
| local function linkList(list)
| |
| local out = {}
| |
| for _, pageName in ipairs(list or {}) do
| |
| table.insert(out, makeLinkedName(pageName))
| |
| end
| |
| return table.concat(out, '<br>')
| |
| end
| |
|
| |
| local function getParents(person)
| |
| person = trim(person)
| |
| if not person then
| |
| return {}, nil
| |
| end
| |
|
| |
| local rows = cargoQuery(
| |
| 'ParentChild',
| |
| 'Child,Parent1,Parent2,UnionID,RelationshipType,BirthOrder',
| |
| {
| |
| where = 'Child="' .. esc(person) .. '"',
| |
| limit = 20
| |
| }
| |
| )
| |
|
| |
| local parents = {}
| |
| local seen = {}
| |
|
| |
| for _, row in ipairs(rows) do
| |
| addUnique(parents, seen, row.Parent1)
| |
| addUnique(parents, seen, row.Parent2)
| |
| end
| |
|
| |
| return sorted(parents), rows[1]
| |
| end
| |
|
| |
| local function getChildren(person)
| |
| person = trim(person)
| |
| if not person then
| |
| return {}
| |
| end
| |
|
| |
| local rows = cargoQuery(
| |
| 'ParentChild',
| |
| 'Child,Parent1,Parent2,UnionID,RelationshipType,BirthOrder',
| |
| {
| |
| where = 'Parent1="' .. esc(person) .. '" OR Parent2="' .. esc(person) .. '"',
| |
| limit = 200
| |
| }
| |
| )
| |
|
| |
| table.sort(rows, function(a, b)
| |
| local aOrder = tonumber(a.BirthOrder) or 9999
| |
| local bOrder = tonumber(b.BirthOrder) or 9999
| |
| if aOrder == bOrder then
| |
| return tostring(a.Child):lower() < tostring(b.Child):lower()
| |
| end
| |
| return aOrder < bOrder
| |
| end)
| |
|
| |
| local children = {}
| |
| local seen = {}
| |
|
| |
| for _, row in ipairs(rows) do
| |
| addUnique(children, seen, row.Child)
| |
| end
| |
|
| |
| return children
| |
| end
| |
|
| |
| local function getPartners(person)
| |
| person = trim(person)
| |
| if not person then
| |
| return {}, {}
| |
| end
| |
|
| |
| local rows = cargoQuery(
| |
| 'Unions',
| |
| 'UnionID,Partner1,Partner2,UnionType,Status,MarriageDate,DivorceDate,EngagementDate',
| |
| {
| |
| where = 'Partner1="' .. esc(person) .. '" OR Partner2="' .. esc(person) .. '"',
| |
| limit = 50
| |
| }
| |
| )
| |
|
| |
| local partners = {}
| |
| local seen = {}
| |
|
| |
| for _, row in ipairs(rows) do
| |
| local p1 = trim(row.Partner1)
| |
| local p2 = trim(row.Partner2)
| |
|
| |
| if p1 == person then
| |
| addUnique(partners, seen, p2)
| |
| elseif p2 == person then
| |
| addUnique(partners, seen, p1)
| |
| end
| |
| end
| |
|
| |
| return sorted(partners), rows
| |
| end
| |
|
| |
| local function getSiblingGeneration(person)
| |
| person = trim(person)
| |
| if not person then
| |
| return {}
| |
| end
| |
|
| |
| local targetRows = cargoQuery(
| |
| 'ParentChild',
| |
| 'Child,Parent1,Parent2,UnionID,RelationshipType,BirthOrder',
| |
| {
| |
| where = 'Child="' .. esc(person) .. '"',
| |
| limit = 10
| |
| }
| |
| )
| |
|
| |
| if not targetRows[1] then
| |
| return { person }
| |
| end
| |
|
| |
| local targetUnion = trim(targetRows[1].UnionID)
| |
| local targetP1 = trim(targetRows[1].Parent1)
| |
| local targetP2 = trim(targetRows[1].Parent2)
| |
|
| |
| local whereParts = {}
| |
| if targetUnion then
| |
| table.insert(whereParts, 'UnionID="' .. esc(targetUnion) .. '"')
| |
| end
| |
| if targetP1 and targetP2 then
| |
| table.insert(whereParts, '(Parent1="' .. esc(targetP1) .. '" AND Parent2="' .. esc(targetP2) .. '")')
| |
| end
| |
|
| |
| if #whereParts == 0 then
| |
| return { person }
| |
| end
| |
|
| |
| local rows = cargoQuery(
| |
| 'ParentChild',
| |
| 'Child,Parent1,Parent2,UnionID,RelationshipType,BirthOrder',
| |
| {
| |
| where = table.concat(whereParts, ' OR '),
| |
| limit = 100
| |
| }
| |
| )
| |
|
| |
| table.sort(rows, function(a, b)
| |
| local aOrder = tonumber(a.BirthOrder) or 9999
| |
| local bOrder = tonumber(b.BirthOrder) or 9999
| |
| if aOrder == bOrder then
| |
| return tostring(a.Child):lower() < tostring(b.Child):lower()
| |
| end
| |
| return aOrder < bOrder
| |
| end)
| |
|
| |
| local people = {}
| |
| local seen = {}
| |
| for _, row in ipairs(rows) do
| |
| addUnique(people, seen, row.Child)
| |
| end
| |
|
| |
| if #people == 0 then
| |
| table.insert(people, person)
| |
| end
| |
|
| |
| return people
| |
| end
| |
|
| |
| local function getRelationshipTypeForChild(person)
| |
| person = trim(person)
| |
| if not person then
| |
| return nil
| |
| end
| |
|
| |
| local rows = cargoQuery(
| |
| 'ParentChild',
| |
| 'RelationshipType',
| |
| {
| |
| where = 'Child="' .. esc(person) .. '"',
| |
| limit = 1
| |
| }
| |
| )
| |
|
| |
| if rows[1] and trim(rows[1].RelationshipType) then
| |
| return trim(rows[1].RelationshipType)
| |
| end
| |
|
| |
| return nil
| |
| end
| |
|
| |
| local function buildCoupleGroups(people)
| |
| local groups = {}
| |
| local used = {}
| |
| local working = {}
| |
|
| |
| for _, person in ipairs(people or {}) do
| |
| table.insert(working, person)
| |
| end
| |
| sorted(working)
| |
|
| |
| for _, person in ipairs(working) do
| |
| if not used[person] then
| |
| local partners = getPartners(person)
| |
| local matchedPartner = nil
| |
| local matchedUnion = nil
| |
|
| |
| for _, partner in ipairs(partners) do
| |
| for _, candidate in ipairs(working) do
| |
| if candidate == partner and not used[candidate] then
| |
| matchedPartner = partner
| |
| break
| |
| end
| |
| end
| |
| if matchedPartner then
| |
| local rows = cargoQuery(
| |
| 'Unions',
| |
| 'UnionID,Partner1,Partner2,UnionType,MarriageDate,EngagementDate',
| |
| {
| |
| where = '(Partner1="' .. esc(person) .. '" AND Partner2="' .. esc(matchedPartner) .. '") OR (Partner1="' .. esc(matchedPartner) .. '" AND Partner2="' .. esc(person) .. '")',
| |
| limit = 1
| |
| }
| |
| )
| |
| matchedUnion = rows[1]
| |
| break
| |
| end
| |
| end
| |
|
| |
| if matchedPartner then
| |
| used[person] = true
| |
| used[matchedPartner] = true
| |
| local unionType = trim(matchedUnion and matchedUnion.UnionType) or 'Marriage'
| |
| local year = nil
| |
| if unionType == 'Engagement' then
| |
| year = formatYear(matchedUnion and matchedUnion.EngagementDate)
| |
| else
| |
| year = formatYear(matchedUnion and matchedUnion.MarriageDate)
| |
| end
| |
|
| |
| table.insert(groups, {
| |
| type = 'couple',
| |
| left = person,
| |
| right = matchedPartner,
| |
| unionType = unionType,
| |
| marriageYear = year
| |
| })
| |
| else
| |
| used[person] = true
| |
| table.insert(groups, {
| |
| type = 'single',
| |
| person = person
| |
| })
| |
| end
| |
| end
| |
| end
| |
|
| |
| return groups
| |
| end
| |
|
| |
| local function buildFocusBranches(person)
| |
| person = trim(person)
| |
| if not person then
| |
| return {}
| |
| end
| |
|
| |
| local branches = {}
| |
|
| |
| local unions = cargoQuery(
| |
| 'Unions',
| |
| 'UnionID,Partner1,Partner2,UnionType,Status,MarriageDate,EngagementDate',
| |
| {
| |
| where = 'Partner1="' .. esc(person) .. '" OR Partner2="' .. esc(person) .. '"',
| |
| limit = 100
| |
| }
| |
| )
| |
|
| |
| for _, union in ipairs(unions) do
| |
| local p1 = trim(union.Partner1)
| |
| local p2 = trim(union.Partner2)
| |
| local partner = nil
| |
|
| |
| if p1 == person then
| |
| partner = p2
| |
| else
| |
| partner = p1
| |
| end
| |
|
| |
| local childRows = cargoQuery(
| |
| 'ParentChild',
| |
| 'Child,BirthOrder',
| |
| {
| |
| where = 'UnionID="' .. esc(union.UnionID) .. '"',
| |
| limit = 100
| |
| }
| |
| )
| |
|
| |
| table.sort(childRows, function(a, b)
| |
| local aOrder = tonumber(a.BirthOrder) or 9999
| |
| local bOrder = tonumber(b.BirthOrder) or 9999
| |
| if aOrder == bOrder then
| |
| return tostring(a.Child):lower() < tostring(b.Child):lower()
| |
| end
| |
| return aOrder < bOrder
| |
| end)
| |
|
| |
| local children = {}
| |
| for _, row in ipairs(childRows) do
| |
| table.insert(children, row.Child)
| |
| end
| |
|
| |
| local unionType = trim(union.UnionType) or 'Marriage'
| |
| local year = nil
| |
| if unionType == 'Engagement' then
| |
| year = formatYear(union.EngagementDate)
| |
| else
| |
| year = formatYear(union.MarriageDate)
| |
| end
| |
|
| |
| table.insert(branches, {
| |
| person = person,
| |
| partner = partner,
| |
| unionType = unionType,
| |
| marriageYear = year,
| |
| children = children
| |
| })
| |
| end
| |
|
| |
| local orphanRows = cargoQuery(
| |
| 'ParentChild',
| |
| 'Child,BirthOrder,RelationshipType',
| |
| {
| |
| where = '(Parent1="' .. esc(person) .. '" OR Parent2="' .. esc(person) .. '") AND (UnionID="" OR UnionID IS NULL)',
| |
| limit = 100
| |
| }
| |
| )
| |
|
| |
| if #orphanRows > 0 then
| |
| table.sort(orphanRows, function(a, b)
| |
| local aOrder = tonumber(a.BirthOrder) or 9999
| |
| local bOrder = tonumber(b.BirthOrder) or 9999
| |
| if aOrder == bOrder then
| |
| return tostring(a.Child):lower() < tostring(b.Child):lower()
| |
| end
| |
| return aOrder < bOrder
| |
| end)
| |
|
| |
| local children = {}
| |
| for _, row in ipairs(orphanRows) do
| |
| table.insert(children, row.Child)
| |
| end
| |
|
| |
| table.insert(branches, {
| |
| person = person,
| |
| partner = nil,
| |
| unionType = 'Illegitimate',
| |
| marriageYear = nil,
| |
| children = children
| |
| })
| |
| end
| |
|
| |
| return branches
| |
| end
| |
|
| |
| local function cardStyle()
| |
| return table.concat({
| |
| 'width:120px',
| |
| 'min-height:56px',
| |
| 'padding:8px 10px',
| |
| 'border:1px solid #cab8aa',
| |
| 'background:#fffdf9',
| |
| 'border-radius:10px',
| |
| 'box-shadow:0 2px 4px rgba(0,0,0,0.08)',
| |
| 'text-align:center',
| |
| 'box-sizing:border-box',
| |
| 'font-size:0.92em',
| |
| 'line-height:1.2',
| |
| 'display:flex',
| |
| 'flex-direction:column',
| |
| 'justify-content:center'
| |
| }, ';')
| |
| end
| |
|
| |
| local function yearsStyle()
| |
| return 'margin-top:4px;font-size:0.74em;color:#7a6b60;'
| |
| end
| |
|
| |
| local function makeCard(pageName)
| |
| pageName = trim(pageName)
| |
| if not pageName then
| |
| return ''
| |
| end
| |
|
| |
| local c = getCharacter(pageName)
| |
| local displayName = getDisplayName(pageName)
| |
| local birthYear = c and formatYear(c.BirthDate) or nil
| |
| local deathYear = c and formatYear(c.DeathDate) or nil
| |
|
| |
| local years = ''
| |
| if birthYear or deathYear then
| |
| years = '<div style="' .. yearsStyle() .. '">' .. (birthYear or '?') .. '–' .. (deathYear or '') .. '</div>'
| |
| end
| |
|
| |
| return '<div style="' .. cardStyle() .. '">[[' .. pageName .. '|' .. displayName .. ']]' .. years .. '</div>'
| |
| end
| |
|
| |
| local function makeRelationLabel(unionType, marriageYear)
| |
| local label = ''
| |
|
| |
| if unionType == 'Marriage' and marriageYear then
| |
| label = 'm. ' .. marriageYear
| |
| elseif unionType == 'Marriage' then
| |
| label = 'm.'
| |
| elseif unionType == 'Engagement' and marriageYear then
| |
| label = 'eng. ' .. marriageYear
| |
| elseif unionType == 'Engagement' then
| |
| label = 'eng.'
| |
| elseif unionType == 'Affair' then
| |
| label = 'affair'
| |
| elseif unionType == 'Liaison' then
| |
| label = 'liaison'
| |
| elseif unionType == 'Illegitimate' then
| |
| label = 'issue'
| |
| elseif unionType and unionType ~= '' then
| |
| if marriageYear then
| |
| label = unionType .. ' ' .. marriageYear
| |
| else
| |
| label = unionType
| |
| end
| |
| end
| |
|
| |
| return label
| |
| end
| |
|
| |
| local function makeCouple(left, right, marriageYear, unionType)
| |
| local label = makeRelationLabel(unionType, marriageYear)
| |
|
| |
| if not right then
| |
| local html = {}
| |
| table.insert(html, '<div style="display:inline-flex;flex-direction:column;align-items:center;">')
| |
| table.insert(html, makeCard(left))
| |
| if label ~= '' then
| |
| table.insert(html, '<div style="font-size:0.7em;color:#a08c7d;margin-top:4px;">' .. label .. '</div>')
| |
| end
| |
| table.insert(html, '</div>')
| |
| return table.concat(html)
| |
| end
| |
|
| |
| local html = {}
| |
| table.insert(html, '<div style="display:inline-flex;flex-direction:row;align-items:center;gap:8px;">')
| |
| table.insert(html, makeCard(left))
| |
| table.insert(html, '<div style="display:flex;flex-direction:column;align-items:center;">')
| |
| if label ~= '' then
| |
| table.insert(html, '<div style="font-size:0.7em;color:#7a6b60;margin-bottom:4px;white-space:nowrap;">' .. label .. '</div>')
| |
| end
| |
| table.insert(html, '<div style="width:40px;height:2px;background:#bdaea0;"></div>')
| |
| table.insert(html, '</div>')
| |
| table.insert(html, makeCard(right))
| |
| table.insert(html, '</div>')
| |
|
| |
| return table.concat(html)
| |
| end
| |
|
| |
| local function makeCoupleRow(groups)
| |
| if not groups or #groups == 0 then
| |
| return ''
| |
| end
| |
|
| |
| local html = {}
| |
| table.insert(html, '<div style="display:flex;justify-content:center;align-items:flex-start;gap:22px;flex-wrap:wrap;width:100%;">')
| |
| for _, group in ipairs(groups) do
| |
| if group.type == 'single' then
| |
| table.insert(html, makeCouple(group.person, nil, nil, nil))
| |
| else
| |
| table.insert(html, makeCouple(group.left, group.right, group.marriageYear, group.unionType))
| |
| end
| |
| end
| |
| table.insert(html, '</div>')
| |
| return table.concat(html)
| |
| end
| |
|
| |
| local function makeFocusBranches(branches)
| |
| if not branches or #branches == 0 then
| |
| return ''
| |
| end
| |
|
| |
| local html = {}
| |
| table.insert(html, '<div style="position:relative;width:100%;padding-top:24px;">')
| |
| table.insert(html, '<div style="position:absolute;top:10px;left:12%;right:12%;height:2px;background:#bdaea0;"></div>')
| |
| table.insert(html, '<div style="position:relative;z-index:1;display:flex;justify-content:center;align-items:flex-start;gap:24px;flex-wrap:wrap;width:100%;">')
| |
|
| |
| for _, branch in ipairs(branches) do
| |
| table.insert(html, '<div style="display:flex;flex-direction:column;align-items:center;justify-content:flex-start;gap:8px;min-width:150px;flex:0 1 auto;">')
| |
| table.insert(html, '<div style="position:relative;display:inline-flex;justify-content:flex-start;align-items:center;">')
| |
| table.insert(html, '<div style="position:absolute;top:-18px;left:60px;width:2px;height:16px;background:#b8a79a;z-index:2;"></div>')
| |
| table.insert(html, makeCouple(branch.person, branch.partner, branch.marriageYear, branch.unionType))
| |
| table.insert(html, '</div>')
| |
|
| |
| if branch.children and #branch.children > 0 then
| |
| table.insert(html, '<div style="width:2px;height:16px;background:#b8a79a;margin:0 auto;"></div>')
| |
| table.insert(html, '<div style="display:flex;justify-content:center;align-items:flex-start;gap:8px;flex-wrap:wrap;max-width:260px;">')
| |
| for _, child in ipairs(branch.children) do
| |
| table.insert(html, makeCard(child))
| |
| end
| |
| table.insert(html, '</div>')
| |
| end
| |
|
| |
| table.insert(html, '</div>')
| |
| end
| |
|
| |
| table.insert(html, '</div>')
| |
| table.insert(html, '</div>')
| |
| return table.concat(html)
| |
| end
| |
|
| |
|
| function p.connected(frame) | | function p.connected(frame) |
| local args = frame.args | | return 'CONNECTED OK' |
| local parentArgs = frame:getParent() and frame:getParent().args or {}
| |
| local root = trim(args.root or parentArgs.root or args[1] or parentArgs[1])
| |
| | |
| if not root then
| |
| return 'Error: no root provided. Use root=Character Name'
| |
| end
| |
| | |
| local visited = {}
| |
| local queue = {}
| |
| local head = 1
| |
| | |
| visited[root] = true
| |
| table.insert(queue, root)
| |
| | |
| while head <= #queue do
| |
| local current = queue[head]
| |
| head = head + 1
| |
| | |
| local neighbors = {}
| |
| local parents = getParents(current)
| |
| local children = getChildren(current)
| |
| local partners = getPartners(current)
| |
| | |
| for _, person in ipairs(parents) do
| |
| addSet(neighbors, person)
| |
| end
| |
| for _, person in ipairs(children) do
| |
| addSet(neighbors, person)
| |
| end
| |
| for _, person in ipairs(partners) do
| |
| addSet(neighbors, person)
| |
| end
| |
| | |
| for neighbor, _ in pairs(neighbors) do
| |
| if neighbor and not visited[neighbor] then
| |
| visited[neighbor] = true
| |
| table.insert(queue, neighbor)
| |
| end
| |
| end
| |
| end
| |
| | |
| local people = {}
| |
| for name, _ in pairs(visited) do
| |
| table.insert(people, name)
| |
| end
| |
| sorted(people)
| |
| | |
| local lines = {}
| |
| table.insert(lines, "'''Connected component for " .. getDisplayName(root) .. "'''")
| |
| table.insert(lines, '* Total people found: ' .. tostring(#people))
| |
| for _, person in ipairs(people) do
| |
| table.insert(lines, '* ' .. makeLinkedName(person))
| |
| end
| |
| | |
| return table.concat(lines, '\n')
| |
| end | | end |
|
| |
|
| function p.profile(frame) | | function p.profile(frame) |
| local args = frame.args | | return 'PROFILE OK' |
| local parentArgs = frame:getParent() and frame:getParent().args or {}
| |
| local root = trim(args.root or parentArgs.root or args[1] or parentArgs[1])
| |
| | |
| if not root then
| |
| return 'Error: no root provided. Use root=Character Name'
| |
| end
| |
| | |
| local parents = getParents(root)
| |
| local siblings = getSiblingGeneration(root)
| |
| local partners = getPartners(root)
| |
| local children = getChildren(root)
| |
| | |
| local siblingList = {}
| |
| for _, person in ipairs(siblings) do
| |
| if person ~= root then
| |
| table.insert(siblingList, person)
| |
| end
| |
| end
| |
| | |
| local lines = {}
| |
| table.insert(lines, '{| class="wikitable" style="width:100%; max-width:900px;"')
| |
| table.insert(lines, '|-')
| |
| table.insert(lines, '! colspan="2" | Family profile for ' .. getDisplayName(root))
| |
| table.insert(lines, '|-')
| |
| table.insert(lines, '! style="width:20%;" | Person')
| |
| table.insert(lines, '| ' .. makeLinkedName(root))
| |
| table.insert(lines, '|-')
| |
| table.insert(lines, '! Parents')
| |
| table.insert(lines, '| ' .. (#parents > 0 and linkList(parents) or '—'))
| |
| table.insert(lines, '|-')
| |
| table.insert(lines, '! Siblings')
| |
| table.insert(lines, '| ' .. (#siblingList > 0 and linkList(siblingList) or '—'))
| |
| table.insert(lines, '|-')
| |
| table.insert(lines, '! Partners')
| |
| table.insert(lines, '| ' .. (#partners > 0 and linkList(partners) or '—'))
| |
| table.insert(lines, '|-')
| |
| table.insert(lines, '! Children')
| |
| table.insert(lines, '| ' .. (#children > 0 and linkList(children) or '—'))
| |
| table.insert(lines, '|}')
| |
| return table.concat(lines, '\n')
| |
| end | | end |
|
| |
|
| Line 713: |
Line 12: |
| local args = frame.args | | local args = frame.args |
| local parentArgs = frame:getParent() and frame:getParent().args or {} | | local parentArgs = frame:getParent() and frame:getParent().args or {} |
| local root = trim(args.root or parentArgs.root or args[1] or parentArgs[1]) | | local root = args.root or parentArgs.root or args[1] or parentArgs[1] or 'NONE' |
| | |
| if not root then
| |
| return 'Error: no root provided. Use root=Character Name'
| |
| end
| |
| | |
| local parents = getParents(root)
| |
| local grandparents = {}
| |
| local gpSeen = {}
| |
| | |
| for _, parentName in ipairs(parents) do
| |
| local parentParents = getParents(parentName)
| |
| for _, gp in ipairs(parentParents) do
| |
| addUnique(grandparents, gpSeen, gp)
| |
| end
| |
| end
| |
| sorted(grandparents)
| |
| | |
| local grandparentGroups = buildCoupleGroups(grandparents)
| |
| local parentGroups = buildCoupleGroups(parents)
| |
| local branches = buildFocusBranches(root)
| |
| | |
| local html = {}
| |
| table.insert(html, '<div style="border:1px solid #cdbfb2;background:#f8f4ee;padding:28px 24px;margin:20px 0;border-radius:14px;text-align:center;">')
| |
| table.insert(html, '<div style="text-align:center;font-weight:700;font-size:1.2em;margin-bottom:28px;color:#4e4036;">Family tree for ' .. getDisplayName(root) .. '</div>')
| |
| | |
| if #grandparentGroups > 0 then
| |
| table.insert(html, '<div style="display:flex;justify-content:center;align-items:flex-start;width:100%;margin:22px 0;">')
| |
| table.insert(html, makeCoupleRow(grandparentGroups))
| |
| table.insert(html, '</div>')
| |
| end
| |
| | |
| if #grandparentGroups > 0 and #parentGroups > 0 then
| |
| table.insert(html, '<div style="width:2px;height:28px;background:#b8a79a;margin:0 auto;"></div>')
| |
| end
| |
| | |
| if #parentGroups > 0 then
| |
| table.insert(html, '<div style="display:flex;justify-content:center;align-items:flex-start;width:100%;margin:22px 0;">')
| |
| table.insert(html, makeCoupleRow(parentGroups))
| |
| table.insert(html, '</div>')
| |
| end
| |
| | |
| if #parentGroups > 0 then
| |
| table.insert(html, '<div style="width:2px;height:28px;background:#b8a79a;margin:0 auto;"></div>')
| |
| end
| |
| | |
| table.insert(html, '<div style="display:flex;justify-content:center;align-items:flex-start;width:100%;margin:22px 0;">')
| |
| table.insert(html, '<div style="display:inline-flex;align-items:center;justify-content:center;">' .. makeCard(root) .. '</div>')
| |
| table.insert(html, '</div>')
| |
| | |
| if #branches > 0 then
| |
| table.insert(html, '<div style="width:2px;height:28px;background:#b8a79a;margin:0 auto;"></div>')
| |
| table.insert(html, '<div style="display:flex;justify-content:center;align-items:flex-start;width:100%;margin:22px 0;">')
| |
| table.insert(html, makeFocusBranches(branches))
| |
| table.insert(html, '</div>')
| |
| end
| |
|
| |
|
| table.insert(html, '</div>') | | return '<div style="padding:20px;border:4px solid red;font-size:20px;">FOCUS TREE LOADED FOR: ' .. tostring(root) .. '</div>' |
| return table.concat(html)
| |
| end | | end |
|
| |
|
| return p | | return p |