diff options
Diffstat (limited to 'Master/texmf-dist/scripts/make4ht/domfilters/make4ht-collapsetoc.lua')
-rw-r--r-- | Master/texmf-dist/scripts/make4ht/domfilters/make4ht-collapsetoc.lua | 198 |
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) |