'''template engine independent utils'''
from lxml.html import fragment_fromstring, fragments_fromstring, tostring
from lxml.etree import ParserError


def check_active(url, element, full_path, css_class):
    '''check url "active" status, apply css_class to html element'''
    # check non empty href parameter
    if url.attrib.get('href'):
        # skip "root" url
        if url.attrib['href'] != '/':
            # compare href parameter with full path
            if full_path.startswith(url.attrib['href']):
                # check parent tag has "class" attribute or it is empty
                if element.attrib.get('class'):
                    # prevent multiple "class" adding
                    if not css_class in element.attrib['class']:
                        # append "active" class
                        element.attrib['class'] += ' ' + css_class
                else:
                    # create or set "class" attribute
                    element.attrib['class'] = css_class
                return True
    return False


def check_fragment(tree, full_path, css_class, parent_tag):
    '''check html element fragment for "active" urls'''
    # flag for prevent html rebuilding, when no "active" urls found
    processed = False
    # if parent_tag is False\None\empty\a\self
    # "active" status will be applied directly to <a>
    if not parent_tag or parent_tag in ('a', 'self'):
        # xpath query to get all <a>
        urls = tree.xpath('//a')
        # check "active" status for all urls
        for url in urls:
            if check_active(url, url, full_path, css_class):
                processed = True
    # otherwise css_class must be applied to parent_tag
    else:
        # xpath query to get all parents tags
        tree_tags = tree.xpath('//%s' % parent_tag)
        # check all html elements for "active" <a>
        for element in tree_tags:
            # xpath query to get all <a> inside current tag
            urls = element.xpath('.//a')
            # check "active" status for all urls
            for url in urls:
                if check_active(url, element, full_path, css_class):
                    # flag for rebuilding html tree
                    processed = True
                    # stop checking other <a> inside current parent_tag
                    break
    # do not rebuild html if no "active" urls inside parent_tag
    if processed:
        # build html from tree
        return tostring(tree)
    # no "active" urls found
    return False


def check_html(content, full_path, css_class, parent_tag):
    '''build html tree from content inside template tag'''
    # valid html root tag
    try:
        # build html elements tree from html fragment
        tree = fragment_fromstring(content)
        # check html fragment for "active" urls
        check_content = check_fragment(
            tree, full_path, css_class, parent_tag
        )
        # prevent rebuild if no "active" urls
        if not check_content is False:
            content = check_content
    # not valid html root tag
    except ParserError:
        tree = fragments_fromstring(content)
        # python 3 lxml fix for empty byte strings
        tree = [element for element in tree if not isinstance(element, str)]
        # new clean html fragment
        rebuild_content = ''
        # flag for rebuilding html tree
        processed = False
        # check all multiple tags
        for element in tree:
            # check html fragment for "active" urls
            check_content = check_fragment(
                element, full_path, css_class, parent_tag
            )
            # concatenation new html fragment with rendered tag
            if not check_content is False:
                rebuild_content = '%s%s' % (
                    rebuild_content,
                    check_content
                )
                processed = True
            else:
                rebuild_content = '%s%s' % (
                    rebuild_content,
                    tostring(element)
                )
        if processed:
            content = rebuild_content
    return content

@@ -3135,136 +3135,8 @@
 nt)%0A
-        # python 3 lxml fix for empty byte strings%0A        tree = %5Belement for element in tree if not isinstance(element, str)%5D%0A
     
@@ -3167,37 +3167,38 @@
 gment%0A        re
-build
+render
 _content = ''%0A  
@@ -3195,32 +3195,33 @@
 nt = ''%0A        
+ 
 # flag for rebui
@@ -3215,36 +3215,68 @@
 lag for 
-rebuilding html tree
+prevent html rebuilding, when no %22active%22 urls found
 %0A       
@@ -3362,32 +3362,127 @@
 e:%0A            #
+ lxml fix for empty byte strings%0A            if not isinstance(element, str):%0A                #
  check html frag
@@ -3508,32 +3508,36 @@
 rls%0A            
+    
 check_content = 
@@ -3564,24 +3564,28 @@
             
+  
+  
 element, ful
@@ -3618,34 +3618,41 @@
 tag%0A            
-)%0A
+    )%0A   
             # co
@@ -3643,24 +3643,25 @@
             
+ 
 # concatenat
@@ -3664,20 +3664,16 @@
 enation 
-new 
 html fra
@@ -3681,27 +3681,14 @@
 ment
- with rendered tag%0A
+s%0A    
     
@@ -3734,39 +3734,44 @@
                 
-rebuild
+    rerender
 _content = '%25s%25s
@@ -3788,39 +3788,44 @@
                 
-rebuild
+    rerender
 _content,%0A      
@@ -3810,32 +3810,36 @@
 render_content,%0A
+    
                 
@@ -3864,34 +3864,83 @@
                 
+ 
+   
 )%0A
+                    # %22active%22 url found%0A    
                 
@@ -3960,32 +3960,36 @@
 rue%0A            
+    
 else:%0A          
@@ -3986,39 +3986,44 @@
                 
+  
-rebuild
+  rerender
 _content = '%25s%25s
@@ -4048,23 +4048,28 @@
         
-rebuild
+    rerender
 _content
@@ -4062,32 +4062,36 @@
 render_content,%0A
+    
                 
@@ -4108,24 +4108,176 @@
 ng(element)%0A
+                    )%0A            else:%0A                rerender_content = '%25s%25s' %25 (%0A                    rerender_content,%0A                    element%0A
             
@@ -4328,21 +4328,22 @@
 ent = re
-build
+render
 _content

Refactor lxml fragments_fromstring fixes