Module:etymon/tree
පෙනුම
- මෙම module සතුව උපදෙස් උප පිටුවක් නොපවතියි. Please නිර්මාණය කරන්න.
- ප්රයෝජනවත් සබැඳි: root page • root page’s subpages • සබැඳි • transclusions • testcases • sandbox
local export = {}
local html_create = mw.html.create
local max = math.max
local function create_vertical_connector()
return html_create('span'):addClass('etytree-connector-vertical')
end
local function create_abbr(text, title, glossary)
local abbr = html_create('abbr')
:attr('title', title)
:wikitext(text)
if glossary then
abbr = '[[Appendix:Glossary#' .. glossary .. '|' .. tostring(abbr) .. ']]'
end
return html_create('span'):addClass('etytree-label'):node(abbr)
end
local function create_uncertainty_marker()
return html_create('abbr')
:addClass('etytree-unc')
:attr('title', 'uncertain')
:wikitext('?')
end
local function create_label_container()
return html_create('span'):addClass('etytree-label-container')
end
local function render_label(term_block, keyword_info, keyword_modifiers, is_uncertain, is_group_child, term_labels)
-- Skip label for invisible keywords
local has_label = keyword_info and keyword_info.abbrev and not is_group_child and not keyword_info.invisible
-- For group children, keyword uncertainty is shown on the group label, not on individual terms
local keyword_uncertain = keyword_modifiers and keyword_modifiers.unc and not is_group_child
local show_term_uncertainty = is_uncertain
-- Check if we have term-specific labels
local has_term_labels = term_labels and #term_labels > 0
if not has_label and not show_term_uncertainty and not keyword_uncertain and not has_term_labels then
return
end
local label_span = create_label_container()
if has_label then
local glossary_title = keyword_info.glossary
and keyword_info.glossary:gsub("_", " ")
or keyword_info.abbrev
label_span:node(create_abbr(
keyword_info.abbrev,
glossary_title,
keyword_info.glossary
))
-- Show uncertainty marker if term or keyword is uncertain
if show_term_uncertainty or keyword_uncertain then
label_span:node(create_uncertainty_marker())
end
else
-- No label, but term or keyword is uncertain
if show_term_uncertainty or keyword_uncertain then
label_span:node(create_uncertainty_marker())
end
end
-- Add term-specific labels
if has_term_labels then
for _, label_info in ipairs(term_labels) do
label_span:node(create_abbr(label_info.abbrev, label_info.title, label_info.glossary))
end
end
term_block:node(label_span)
end
local function render_term_block(node_data, format_term_func, is_toplevel)
local link_content = html_create()
link_content
:tag('span')
:addClass('etyl')
:wikitext(node_data.lang:getCanonicalName())
:done()
local term_text = format_term_func(node_data, is_toplevel)
if term_text then
link_content
:wikitext(' ')
:tag('span')
:addClass('etytree-term')
:wikitext(term_text)
:done()
end
local block = html_create('div'):addClass('etytree-block'):node(link_content)
-- Add duplicate styling if this is a duplicate node
if node_data.is_duplicate then
block:addClass('etytree-duplicate')
end
return block
end
local function create_dotted_connector()
return html_create('span'):addClass('etytree-connector-dotted')
end
-- Create an L-shaped connector for nodes with hidden ancestry (duplicate or no_child_categories)
local function create_duplicate_connector()
local container = html_create('div'):addClass('etytree-duplicate-connector')
local inner_wrapper = container:tag('div')
inner_wrapper:tag('span'):addClass('etytree-dup-right')
inner_wrapper:tag('span'):addClass('etytree-dup-horiz')
inner_wrapper:tag('span'):addClass('etytree-dup-left')
inner_wrapper:tag('span'):addClass('etytree-dup-arrow'):wikitext('▲')
return container
end
local function render_group_label(connecting_line, keyword_info, keyword_modifiers)
local has_abbrev = keyword_info and keyword_info.abbrev
local keyword_uncertain = keyword_modifiers and keyword_modifiers.unc
-- Nothing to show if no abbrev and no uncertainty
if not has_abbrev and not keyword_uncertain then
return
end
local label_span = connecting_line:tag('span'):addClass('etytree-group-label')
if has_abbrev then
local glossary_title = keyword_info.glossary
and keyword_info.glossary:gsub("_", " ")
or keyword_info.abbrev
label_span:node(create_abbr(keyword_info.abbrev, glossary_title, nil))
end
-- Add uncertainty marker if keyword has <unc> modifier
if keyword_uncertain then
label_span:node(create_uncertainty_marker())
end
end
local function add_branch_connector(column, index, total)
if index == 1 then
column:tag('span'):addClass('etytree-branch-left')
elseif index == total then
column:tag('span'):addClass('etytree-branch-right')
else
column:tag('span'):addClass('etytree-connector-vertical-short')
column:tag('span'):addClass('etytree-branch-mid')
end
end
function export.render(opts)
opts = opts or {}
local data_tree = opts.data_tree
local format_term_func = opts.format_term_func
-- Forward declaration
local render_term
-- Render a container (keyword + its terms)
local function render_container(container, is_toplevel)
local keyword_info = container.keyword_info
local keyword_modifiers = container.keyword_modifiers or {}
local is_group = keyword_info and keyword_info.is_group
local terms = container.terms or {}
-- Skip invisible keywords entirely
if keyword_info and keyword_info.invisible then
return nil, 0, 0
end
if #terms == 0 then
return nil, 0, 0
end
-- For no_child_categories keywords (calque, semantic loan, etc.), don't render term's children
local skip_child_rendering = keyword_info and keyword_info.no_child_categories
-- Render each term in the container
local rendered_terms = {}
local container_height = 0
local container_width = 0
for _, term in ipairs(terms) do
-- Collect term-specific labels
local term_labels = {}
if term.bor then
table.insert(term_labels, { abbrev = "bor.", title = "borrowed", glossary = "loanword" })
end
local term_tree, term_height, term_width = render_term(term, keyword_info, keyword_modifiers, is_group, false, skip_child_rendering, term_labels)
table.insert(rendered_terms, {
tree = term_tree,
height = term_height,
width = term_width,
is_uncertain = term.is_uncertain,
})
container_height = max(container_height, term_height)
container_width = container_width + term_width
end
local rendered_html
local has_connector = false
if #rendered_terms == 1 then
-- Single term: just return it directly
rendered_html = rendered_terms[1].tree
container_height = rendered_terms[1].height
container_width = rendered_terms[1].width
else
-- Multiple terms: group them together
local subtree_container = html_create('div'):addClass('etytree-branch-group')
for i, term_data in ipairs(rendered_terms) do
local column = html_create('div'):addClass('etytree-branch')
column:node(term_data.tree)
add_branch_connector(column, i, #rendered_terms)
subtree_container:node(column)
end
local connecting_line = create_vertical_connector()
-- Add group label for group keywords
if is_group then
render_group_label(connecting_line, keyword_info, keyword_modifiers)
end
rendered_html = html_create()
:node(subtree_container)
:node(connecting_line)
has_connector = true
end
return rendered_html, container_height, container_width, has_connector
end
-- Render a term node
render_term = function(term_node, keyword_info, keyword_modifiers, is_group_child, is_toplevel_term, skip_child_rendering, term_labels)
local tree_width, tree_height = 0, 0
local subtrees = {}
-- Process term's children (which are containers)
local has_hidden_children = false
if not term_node.is_duplicate and not skip_child_rendering then
for _, container in ipairs(term_node.children or {}) do
local subtree, sub_height, sub_width, subtree_has_connector = render_container(container, is_toplevel_term)
if subtree then
table.insert(subtrees, {
tree = subtree,
height = sub_height,
width = sub_width,
has_connector = subtree_has_connector,
})
tree_height = max(tree_height, sub_height)
tree_width = tree_width + sub_width
end
end
elseif skip_child_rendering then
-- Check if there are any visible children
-- When stop_recursion is true, children aren't parsed, but has_visible_children flag is set
if term_node.has_visible_children then
has_hidden_children = true
elseif term_node.children and #term_node.children > 0 then
-- Fallback: check parsed children for visibility
for _, container in ipairs(term_node.children) do
local child_keyword_info = container.keyword_info
if not (child_keyword_info and child_keyword_info.invisible) then
has_hidden_children = true
break
end
end
end
end
local is_toplevel_node = (keyword_info == nil)
local term_block = render_term_block(term_node, format_term_func, is_toplevel_node)
render_label(term_block, keyword_info, keyword_modifiers, term_node.is_uncertain, is_group_child, term_labels or {})
local term_html = html_create()
if #subtrees == 0 then
local show_connector = (term_node.is_duplicate and term_node.original_has_children) or has_hidden_children
if show_connector then
term_html:node(create_duplicate_connector())
end
term_html:node(term_block)
tree_width = tree_width + 1
elseif #subtrees == 1 then
term_html:node(subtrees[1].tree)
if not subtrees[1].has_connector then
term_html:node(create_vertical_connector())
end
term_html:node(term_block)
else
-- Multiple containers: need to merge them
local subtree_container = html_create('div'):addClass('etytree-branch-group')
for i, subtree_data in ipairs(subtrees) do
local column = html_create('div'):addClass('etytree-branch')
column:node(subtree_data.tree)
add_branch_connector(column, i, #subtrees)
subtree_container:node(column)
end
local connecting_line = create_vertical_connector()
term_html
:node(subtree_container)
:node(connecting_line)
:node(term_block)
end
return term_html, tree_height + 1, tree_width
end
local final_tree, final_height, final_width = render_term(data_tree, nil, nil, false, true)
local container = html_create('div')
:addClass('etytree-body')
:node(final_tree)
return tostring(html_create('div')
:addClass('etytree NavFrame')
:attr('data-etytree-height', final_height)
:attr('data-etytree-width', final_width)
:tag('div')
:addClass('NavHead')
:tag('div')
:wikitext('නිරුක්ති ශාඛා')
:done()
:done()
:tag('div')
:addClass('NavContent')
:node(container)
:done())
end
return export