// detachable_elements.ysl2
// compute what elements are required by pages
// and decide where to cut when removing/attaching
// pages elements on page switch
// returns all directly or indirectly refered elements
def "func:refered_elements" {
const "descend", "$elems/descendant-or-self::svg:*";
const "clones", "$descend[self::svg:use]";
const "originals", "//svg:*[concat('#',@id) = $clones/@xlink:href]";
result "$descend | func:refered_elements($originals)";
def "func:all_related_elements" {
const "page_overlapping_geometry", "func:overlapping_geometry($page)";
const "page_overlapping_elements", "//svg:*[@id = $page_overlapping_geometry/@Id]";
const "page_sub_elements", "func:refered_elements($page | $page_overlapping_elements)";
result "$page_sub_elements";
def "func:required_elements" {
result """func:all_related_elements($pages[1])
| func:required_elements($pages[position()!=1])""";
const "required_elements",
"""//svg:defs/descendant-or-self::svg:*
| func:required_elements($hmi_pages)/ancestor-or-self::svg:*""";
const "discardable_elements", "//svg:*[not(@id = $required_elements/@id)]";
def "func:sumarized_elements" {
const "short_list", "$elements[not(ancestor::*/@id = $elements/@id)]";
const "filled_groups", """$short_list/parent::svg:*[
not(@id = $discardable_elements/@id) and
not(@id = $short_list/descendant-or-self::*[not(self::svg:g)]/@id)
const "groups_to_add", "$filled_groups[not(ancestor::*/@id = $filled_groups/@id)]";
result "$groups_to_add | $short_list[not(ancestor::svg:g/@id = $filled_groups/@id)]";
def "func:detachable_elements" {
result """func:sumarized_elements(func:all_related_elements($pages[1]))
| func:detachable_elements($pages[position()!=1])""";
// Avoid nested detachables
const "_detachable_elements", "func:detachable_elements($hmi_pages)";
const "detachable_elements", "$_detachable_elements[not(ancestor::*/@id = $_detachable_elements/@id)]";