summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/scripts/make4ht/domfilters/make4ht-collapsetoc.lua
diff options
context:
space:
mode:
Diffstat (limited to 'Master/texmf-dist/scripts/make4ht/domfilters/make4ht-collapsetoc.lua')
-rw-r--r--Master/texmf-dist/scripts/make4ht/domfilters/make4ht-collapsetoc.lua198
1 files changed, 198 insertions, 0 deletions
diff --git a/Master/texmf-dist/scripts/make4ht/domfilters/make4ht-collapsetoc.lua b/Master/texmf-dist/scripts/make4ht/domfilters/make4ht-collapsetoc.lua
new file mode 100644
index 00000000000..0b77b9dbbba
--- /dev/null
+++ b/Master/texmf-dist/scripts/make4ht/domfilters/make4ht-collapsetoc.lua
@@ -0,0 +1,198 @@
+local domobject = require "luaxml-domobject"
+
+local filter = require "make4ht-filter"
+local log = logging.new "collapsetoc"
+
+
+local toc_levels = {"partToc", "chapterToc", "sectionToc", "subsectionToc", "subsubsectionToc"}
+
+local debug_print = function(...) log:debug(...) end
+
+
+-- return toc element type and it's id
+local function get_id(el)
+ local name = el:get_attribute "class"
+ local id
+ local a = el:query_selector "a" or {}
+ local first = a[1]
+ if first then
+ local href = first:get_attribute "href"
+ id = href:match("#(.+)$")
+ end
+ return name, id
+end
+
+local function remove_sections(part_elements, currentpart)
+ -- we need to remove toc entries from the previous part if the
+ -- current document isn't part of it
+ if currentpart == false then
+ for _, part in ipairs(part_elements) do
+ part:remove_node()
+ end
+ end
+end
+
+local function make_toc_selector(toc_levels)
+ local level_classes = {}
+ for _, l in ipairs(toc_levels) do
+ level_classes[#level_classes+1] = "." .. l
+ end
+ return table.concat(level_classes, ", ")
+end
+
+local function find_toc_levels(toc)
+ -- find toc levels used in the document
+ -- it ecpects that sectioning levels appears in the TOC in the descending order
+ local levels, used = {}, {}
+ local level = 1
+ -- we still expect the standard class names
+ local toc_selector = make_toc_selector(toc_levels)
+ for _, el in ipairs(toc:query_selector(toc_selector)) do
+ local class = el:get_attribute("class")
+ if not used[class] then
+ table.insert(levels, class)
+ used[class] = level
+ level = level + 1
+ end
+ end
+ return levels, used
+end
+
+local function remove_levels(toc, matched_ids)
+ -- sort the matched sections according to their levels
+ local levels, level_numbers = find_toc_levels(toc)
+ debug_print("remove levels", #levels)
+ -- for _, level in ipairs(levels) do
+ -- print(level, level_numbers[level], matched_ids[level])
+ -- end
+ local keep_branch = false
+ local matched_levels = {}
+ local toc_selector = make_toc_selector(toc_levels)
+ for _, el in ipairs(toc:query_selector(toc_selector)) do
+ local name, id = get_id(el)
+ -- get the current toc hiearchy level
+ local level = level_numbers[name]
+ -- get the matched id for the current level
+ local level_id = matched_ids[name]
+ local matched = level_id == id
+ local remove = true
+ -- we will use this for toc elements at lower hiearchy than is the top sectioning level on the page
+ if matched then keep_branch = true end
+ -- find the parent level to the current section level
+ local parent_level = toc_levels[level - 1]
+ local parent_matched = matched_levels[parent_level]
+ if matched then
+ debug_print("match",name, id, level_id, level, #levels)
+ keep_branch = true
+ remove = false
+ elseif level==1 then
+ -- don't remove the top level
+ debug_print("part",name, id, level_id, level)
+ remove = false
+ matched_levels = {}
+ if not matched then keep_branch = false end
+ elseif keep_branch then
+ -- if level >= (#levels - 1) then
+ if level > matched_ids._levels then
+ debug_print("level",name, id, level_id, level, parent_level, parent_matched)
+ remove = false
+ elseif matched_ids.ids[id] then
+ debug_print("matched id",name, id, level_id, level, parent_level, parent_matched)
+ remove = false
+ elseif parent_matched then
+ debug_print("parent_matched",name, id, level_id, level, parent_level, parent_matched)
+ keep_branch = false
+ remove = false
+ end
+ -- else
+ -- print("remove", name, id, level_id, level, #matched_ids)
+ -- end
+ elseif parent_matched then
+ debug_print("parent_matched alternative",name, id, level_id, level, parent_level, parent_matched)
+ remove = false
+ else
+ debug_print("else",name, id, level_id, level, keep_branch)
+ keep_branch = false
+ end
+ matched_levels[name] = matched
+ if remove then
+ el:remove_node()
+ --print(name,id, level_id, matched)
+ end
+ end
+
+end
+
+
+
+
+
+-- local process = filter{ function(s)
+ -- local dom = domobject.parse(s)
+local function collapse_toc(dom, par)
+ -- set options
+ local options = get_filter_settings "collapsetoc"
+ local toc_query = par.toc_query or options.toc_query or ".tableofcontents"
+ local title_query = par.title_query or options.title_query or ".partHead a, .chapterHead a, .sectionHead a, .subsectionHead a"
+ toc_levels = par.toc_levels or options.toc_levels or toc_levels
+ -- keep track of current id of each sectioning level
+ local current_ids, matched_ids = {}, {_levels = 0, ids = {}}
+ -- search sectioning elements
+ local titles = dom:query_selector(title_query)
+ local section_ids = {}
+ for _, x in ipairs(titles) do
+ -- get their id attributes and save them in a table
+ section_ids[#section_ids+1] = x:get_attribute("id")
+ end
+
+ -- we need to retrieve the first table of contents
+ local toctables = dom:query_selector(toc_query) or {}
+ -- process only when we got a TOC
+ debug_print("toc query", toc_query, #toctables)
+ if #toctables > 0 then
+ local tableofcontents = toctables[1]
+ -- all toc entries are in span elements
+ local toc = tableofcontents:query_selector("span")
+ local currentpart = false
+ local part_elements = {}
+ for _, el in ipairs(toc) do
+ -- get sectioning level and id of the current TOC entry
+ local name, id = get_id(el)
+ -- set the id of the current sectioning level
+ current_ids[name] = id
+ for _, sectid in ipairs(section_ids) do
+ -- detect if the current TOC entry match some sectioning element in the current document
+ if id == sectid then
+ currentpart = true
+ -- save the current id as a matched id
+ matched_ids.ids[id] = true
+ -- copy the TOC hiearchy for the current toc level
+ for i, level in ipairs(toc_levels) do
+ -- print("xxx",i, level, current_ids[level])
+ matched_ids[level] = current_ids[level]
+ -- set the maximum matched level
+ if i > matched_ids._levels then matched_ids._levels = i end
+ if level == name then break end
+ end
+ debug_print("match", id)
+ end
+ end
+ end
+ remove_levels(tableofcontents, matched_ids)
+
+ -- remove sections from the last part
+ -- remove_sections(part_elements,currentpart)
+ -- remove unneeded br elements
+ local br = tableofcontents:query_selector("br")
+ for _, el in ipairs(br) do el:remove_node() end
+ -- remove unneded whitespace
+ for _, el in ipairs(tableofcontents:get_children()) do
+ if el:is_text() then el:remove_node() end
+ end
+ end
+ return dom
+end
+
+return collapse_toc
+
+-- Make:match("html$", process)