Commits

Luke Plant committed dd6c854

Fixed issue #21 - added inner div to column, solely for styling purposes

  • Participants
  • Parent commits 32d450e

Comments (0)

Files changed (2)

semanticeditor/tests.py

 
     def test_no_headings(self):
         html = '<p>Test</p>'
-        outh = '<div class="row"><div><p>Test</p></div></div>'
+        outh = '<div class="row"><div><div><p>Test</p></div></div></div>'
         self.assertEqual(outh, format_html(html, {}))
 
     def test_unknown_block_elements(self):
         know about
         """
         html = '<foo>Test</foo>'
-        outh = '<div class="row"><div><foo>Test</foo></div></div>'
+        outh = '<div class="row"><div><div><foo>Test</foo></div></div></div>'
         self.assertEqual(outh, format_html(html, {}))
 
     def test_existing_divs(self):
         html = "<div><foo><bar><fribble><div><div>Some text <p>para</p> some more</div><div> more <span> of </span> this stuff </div></div></fribble></bar></foo></div>"
-        outh = '<div class="row"><div><foo><bar><fribble>Some text <p>para</p> some more more <span> of </span> this stuff </fribble></bar></foo></div></div>'
+        outh = '<div class="row"><div><div><foo><bar><fribble>Some text <p>para</p> some more more <span> of </span> this stuff </fribble></bar></foo></div></div></div>'
         self.assertEqual(outh, format_html(html, {}))
 
     def test_add_css_classes(self):
         html = "<h1>Hello <em>you</em></h1><h2>Hi</h2>"
-        outh = '<div class="row"><div><h1 class=\"myclass\">Hello <em>you</em></h1><h2 class=\"c1 c2\">Hi</h2></div></div>'
+        outh = '<div class="row"><div><div><h1 class=\"myclass\">Hello <em>you</em></h1><h2 class=\"c1 c2\">Hi</h2></div></div></div>'
         self.assertEqual(outh, format_html(html, {'h1_1':[PC('myclass')],
                                                   'h2_1':[PC('c1'), PC('c2')]}))
 
 
     def test_columns_1(self):
         html = "<h1>1</h1><p>para 1</p><h1>2</h1><h1>3</h1>"
-        outh = "<div class=\"row columns2\"><div class=\"column firstcolumn\"><h1>1</h1><p>para 1</p></div><div class=\"column lastcolumn\"><h1>2</h1><h1>3</h1></div></div>"
+        outh = "<div class=\"row columns2\"><div class=\"column firstcolumn\"><div><h1>1</h1><p>para 1</p></div></div><div class=\"column lastcolumn\"><div><h1>2</h1><h1>3</h1></div></div></div>"
         self.assertEqual(outh, format_html(html, {'newrow_h1_1':[NEWROW],
                                                   'newcol_h1_2':[NEWCOL]}))
 
     def test_columns_with_double_width(self):
         html = "<h1>1</h1><p>para 1</p><h1>2</h1>"
-        outh = "<div class=\"row columns3\"><div class=\"column firstcolumn doublewidth\"><h1>1</h1><p>para 1</p></div><div class=\"column lastcolumn\"><h1>2</h1></div></div>"
+        outh = "<div class=\"row columns3\"><div class=\"column firstcolumn doublewidth\"><div><h1>1</h1><p>para 1</p></div></div><div class=\"column lastcolumn\"><div><h1>2</h1></div></div></div>"
         self.assertEqual(outh, format_html(html, {'newrow_h1_1':[NEWROW],
                                                   'newcol_h1_1':[NEWCOL, PC('doublewidth', column_equiv=2)],
                                                   'newcol_h1_2':[NEWCOL]}))
 
     def test_columns_with_double_width_2(self):
         html = "<h1>1</h1><p>para 1</p><h1>2</h1>"
-        outh = "<div class=\"row columns3\"><div class=\"column firstcolumn\"><h1>1</h1><p>para 1</p></div><div class=\"column lastcolumn doublewidth\"><h1>2</h1></div></div>"
+        outh = "<div class=\"row columns3\"><div class=\"column firstcolumn\"><div><h1>1</h1><p>para 1</p></div></div><div class=\"column lastcolumn doublewidth\"><div><h1>2</h1></div></div></div>"
         self.assertEqual(outh, format_html(html, {'newrow_h1_1':[NEWROW],
                                                   'newcol_h1_1':[NEWCOL],
                                                   'newcol_h1_2':[NEWCOL, PC('doublewidth', column_equiv=2)]}))
         outh = \
             "<div class=\"row\">" \
             "<div>" \
+            "<div>" \
             "<h1>1</h1>" \
             "<h1>2</h1>" \
             "</div>" \
             "</div>" \
-            "<div class=\"row columns2\">" \
-            "<div class=\"column firstcolumn\">" \
-            "<h2>2.1</h2>" \
-            "</div>" \
-            "<div class=\"column lastcolumn\">" \
-            "<h2>2.2</h2>" \
-            "</div>" \
             "</div>" \
             "<div class=\"row columns2\">" \
             "<div class=\"column firstcolumn\">" \
-            "<h2>2.3</h2>" \
+            "<div>" \
+            "<h2>2.1</h2>" \
+            "</div>" \
             "</div>" \
             "<div class=\"column lastcolumn\">" \
-            "<h2>2.4</h2>" \
+            "<div>" \
+            "<h2>2.2</h2>" \
+            "</div>" \
             "</div>" \
             "</div>" \
             "<div class=\"row columns2\">" \
             "<div class=\"column firstcolumn\">" \
+            "<div>" \
+            "<h2>2.3</h2>" \
+            "</div>" \
+            "</div>" \
+            "<div class=\"column lastcolumn\">" \
+            "<div>" \
+            "<h2>2.4</h2>" \
+            "</div>" \
+            "</div>" \
+            "</div>" \
+            "<div class=\"row columns2\">" \
+            "<div class=\"column firstcolumn\">" \
+            "<div>" \
             "<h1>3</h1>" \
             "</div>" \
+            "</div>" \
             "<div class=\"column lastcolumn\">" \
+            "<div>" \
             "<h1>4</h1>" \
             "</div>" \
+            "</div>" \
             "</div>"
         self.assertEqual(outh, format_html(html, {'newrow_h2_1':[NEWROW],
                                                   'newcol_h2_2':[NEWCOL],
 
     def test_layout_with_styling(self):
         html = "<h1>1</h1><p>para 1</p><h1>2</h1><h1>3</h1>"
-        outh = "<div class=\"row columns2 fancyrow\"><div class=\"column firstcolumn\"><h1>1</h1><p>para 1</p></div><div class=\"column lastcolumn fancycol\"><h1>2</h1><h1>3</h1></div></div>"
+        outh = "<div class=\"row columns2 fancyrow\"><div class=\"column firstcolumn\"><div><h1>1</h1><p>para 1</p></div></div><div class=\"column lastcolumn\"><div class=\"fancycol\"><h1>2</h1><h1>3</h1></div></div></div>"
         self.assertEqual(outh, format_html(html, {'newrow_h1_1':[NEWROW, PC('fancyrow')],
                                                   'newcol_h1_2':[NEWCOL, PC('fancycol')]}))
 
     def test_columns_single_col(self):
         html = "<h1>1</h1><p>para 1</p><h2>2</h2>"
-        outh = "<div class=\"row\"><div><h1>1</h1><p>para 1</p><h2>2</h2></div></div>"
+        outh = "<div class=\"row\"><div><div><h1>1</h1><p>para 1</p><h2>2</h2></div></div></div>"
         self.assertEqual(outh, format_html(html, {'h1_1':[NEWROW]}))
 
 class TestElementTreeUtils(TestCase):
         self.assertEqual(presentation, pres2)
 
     def test_extract_2(self):
+        # Full featured, proper test
         html = """
-<div class="row columns3"><div class="column firstcolumn"><h1>Hello Jane</h1><p>Some fancy content, entered using WYMeditor</p><p>Another paragraph</p><p>Hello</p></div><div class="column"><h1>Another &lt;heading&gt;</h1><h2>this is a test</h2><h2>hello1</h2><h3>hello2</h3><h3>hello3</h3><h3>hello4</h3></div><div class="column lastcolumn"><h1>hello5</h1><h2>hello6</h2><p>asdasd</p><p>asdxx</p></div></div>
+<div class="row columns3"><div class="column firstcolumn"><div class="myclass"><h1>Hello Jane</h1><p>Some fancy content, entered using WYMeditor</p><p>Another paragraph</p><p>Hello</p></div></div><div class="column doublewidth"><div><h1>Another &lt;heading&gt;</h1><h2>this is a test</h2><h2>hello1</h2><h3>hello2</h3><h3>hello3</h3><h3>hello4</h3></div></div><div class="column lastcolumn"><div><h1>hello5</h1><h2>hello6</h2><p>asdasd</p><p>asdxx</p></div></div></div>
 """
         pres = {'newrow_h1_1':set([NEWROW]),
-                'newcol_h1_1':set([NEWCOL]),
+                'newcol_h1_1':set([NEWCOL, PC('myclass')]),
                 'h1_1':set(),
-                'newcol_h1_2':set([NEWCOL]),
+                'newcol_h1_2':set([NEWCOL, PC('doublewidth', column_equiv=2)]),
                 'h1_2':set(),
                 'p_1': set(),
                 'p_2': set(),
     def test_extract_3(self):
         # Tests some other boundary conditions e.g. 1 column row,
         html = """
+<div class="row"><div><div><h1>1</h1><h2>1.1</h2><h2>1.2</h2></div></div></div>
+"""
+        pres = {'h1_1': set(),
+                'newrow_h1_1':set([NEWROW]),
+                'newcol_h1_1':set([NEWCOL]),
+                'h2_1': set(),
+                'h2_2': set(),
+                }
+        pres2, html2 = extract_presentation(html)
+        self.assertEqual(pres, pres2)
+
+    def test_extract_no_inner_col_div_1(self):
+        # Tests that we can extract column structure if we don't have 
+        # an inner column div.
+        # This is important for the case where LayoutDetails.use_inner_column_div = False
+        
+        # Single col structure
+        html = """
 <div class="row"><div><h1>1</h1><h2>1.1</h2><h2>1.2</h2></div></div>
 """
         pres = {'h1_1': set(),
                 }
         pres2, html2 = extract_presentation(html)
         self.assertEqual(pres, pres2)
+        
+    def test_extract_no_inner_col_div_2(self):
+        # Tests that we can extract column structure if we don't have 
+        # an inner column div.
+        # This is important for the case where LayoutDetails.use_inner_column_div = False
+        
+        # Double col structure
+        html = """
+<div class="row"><div class="column firstcolumn"><h1>1</h1></div><div class="column lastcolumn"><h2>1.1</h2></div></div>
+"""
+        pres = {'h1_1': set(),
+                'newrow_h1_1':set([NEWROW]),
+                'newcol_h1_1':set([NEWCOL]),
+                'h2_1': set(),
+                'newcol_h2_1':set([NEWCOL])
+                }
+        pres2, html2 = extract_presentation(html)
+        self.assertEqual(pres, pres2)
+  

semanticeditor/utils/presentation.py

     COLUMN_CLASS = "column"
 
     # Public interface:
-    max_columns = 4
+    max_columns = 4 # max number of columns to allow
+    use_inner_column_div = True # True to wrap all column content in a inner div
     def row_classes(self, logical_column_count, actual_column_count):
         """
         Returns a list of CSS classes to be used for a row containing
         logical_column_count 'logical' columns, actual_column_count 'actual'
         columns.  'actual' columns are present in the HTML structure, but some
-        might be e.g. double width, so are counted as two logical columns.
+        might be, for example, double width, so are counted as two logical columns.
         """
         retval = [self.ROW_CLASS]
         if actual_column_count > 1:
         """
         Returns a list of CSS classes to be used for a column which is number
         column_num out of column_count.
+        (see above regarding logical/actual)
         """
         if actual_column_count == 1:
             # No classes
         """
         return ""
 
+    def outer_column_classes(self, presinfo):
+        """
+        Given a list a PresentationInfo objects, return the ones that should be applied to
+        the outer column div.
+        """
+        return [pi for pi in presinfo if pi.column_equiv is not None]
+
+    def inner_column_classes(self, presinfo):
+        """
+        Given a list a PresentationInfo objects, return the ones that should be applied to
+        the inner column div.
+        (Never called if use_inner_column_div = False)
+        """
+        return [pi for pi in presinfo if pi.column_equiv is None]
+
 ### Parsing ###
 import htmlentitydefs
 def fixentities(htmltext):
     return LayoutDetails()
 
 
+### Structure related ###
+
 class StructureItem(object):
     __metaclass__ = struct
     level = 0     #    level is the 'outline level' in the document i.e. an integer
                                                      i + 1,
                                                      logical_column_count,
                                                      actual_column_count) + \
-                    _get_classes_from_presinfo(col.presinfo)
+                    _get_classes_from_presinfo(layout_strategy.outer_column_classes(col.presinfo))
             if classes:
                 coldiv.set('class', ' '.join(classes))
+            if layout_strategy.use_inner_column_div:
+                contentdiv = ET.Element('div')
+                coldiv.append(contentdiv)
+                inner_classes = _get_classes_from_presinfo(layout_strategy.inner_column_classes(col.presinfo))
+                if inner_classes:
+                    contentdiv.set('class', ' '.join(inner_classes))
+            else:
+                contentdiv = coldiv
             for n in col.nodes:
-                coldiv.append(n)
+                contentdiv.append(n)
             rowdiv.append(coldiv)
 
             logical_column_num += _layout_column_width(col)
             else:
                 node.remove(n)
 
+def _find_row_col_divs(root, node, layout_strategy):
+    """
+    Finds the row and column divs that a node belongs to.
+    Returns a 3 tuple (row_div, col_div, inner_col_div)
+
+    col_div is None if the node is not the first content
+    node within that column.
+
+    row_div is None if the node is not the first content node
+    within that row.
+
+    inner_col_div is None if there is no inner column div,
+    or if col_div is None
+    """
+    # Keep going up until we find a 'row' div or 'column' div
+    # that are parent/child.
+
+    p = get_parent(root, node)
+    gp = None
+
+    p_is_col, gp_is_row = False, False
+    row_div, col_div, inner_col_div = None, None, None
+
+    if p is not None and p.tag == 'div' and get_index(p, node) == 0:
+        # We only care if node is the first child of the column div
+        c_classes = _get_classes_for_node(p)
+        p_is_col = any(layout_strategy.is_column_class(c) for c in c_classes)
+
+        gp = get_parent(root, p)
+        if gp is not None and gp.tag == 'div' and get_index(gp, p) == 0:
+            # We only locate row divs if col is first col within row
+            r_classes = _get_classes_for_node(gp)
+            gp_is_row = any(layout_strategy.is_row_class(c) for c in r_classes)
+
+            # We can't always tell if something is a col (especially for single
+            # column structure), but by identfying the row we can tell we are in
+            # a column structure.
+            if gp_is_row:
+                p_is_col = True
+
+    if gp_is_row:
+        row_div = gp
+    if p_is_col:
+        col_div = p
+
+    if not p_is_col:
+        if p is not None and p.tag == 'div' and get_index(p, node) == 0:
+            # Try to go up one
+            row_div, col_div, inner_col_div = _find_row_col_divs(root, p, layout_strategy)
+            if inner_col_div is None and col_div is not None:
+                # We now know that current parent 'p' is inner_col_div
+                inner_col_div = p
+            return (row_div, col_div, inner_col_div)
+
+    return (row_div, col_div, inner_col_div)
+
 def extract_presentation(html):
     """
     Takes HTML with formatting applied and returns presentation elements (a
         # and will be removed again at end of format_html
         si.node.set('id', si.sect_id)
 
-        # Parent/grandparent of section - newcol/newrow
-        p = get_parent(root, si.node)
-        if p is not None and p.tag == 'div' and (get_index(p, si.node) == 0):
-            # We only care if si.node is the first child of the column div
-            gp = get_parent(root, p)
-            if gp is not None and gp.tag == 'div':
-                # We can't always tell if something is a row/col, but hopefully
-                # we can identify one, which will tell us we are in a column
-                # structure.
-                r_classes = _get_classes_for_node(gp)
-                c_classes = _get_classes_for_node(p)
-                gp_is_row = any(layout_strategy.is_row_class(c) for c in r_classes)
-                p_is_col = any(layout_strategy.is_column_class(c) for c in c_classes)
+        # Try to find 'row' and 'column' divs that this node belongs to.
+        # Columns can have inner divs for styling purposes.  Some CSS classes
+        # will be applied to the outer column div, some to the inner column div.
 
-                if gp_is_row or p_is_col:
-                    # New column
-                    col_pres = set([NEWCOL] + [PresentationClass(c) for c in c_classes if not layout_strategy.is_column_class(c)])
-                    pres[_NEWCOL_PREFIX + si.sect_id] = col_pres
-                    if get_index(gp, p) == 0:
-                       # first column, therefore new row
-                       row_pres = set([NEWROW] + [PresentationClass(c) for c in r_classes if not layout_strategy.is_row_class(c)])
-                       pres[_NEWROW_PREFIX + si.sect_id] = row_pres
+        row_node, col_node, inner_col_node = _find_row_col_divs(root, si.node, layout_strategy)
 
+        if row_node is not None:
+            r_classes = _get_classes_for_node(row_node)
+            row_pres = set([NEWROW] + [PresentationClass(c) for c in r_classes if not layout_strategy.is_row_class(c)])
+            pres[_NEWROW_PREFIX + si.sect_id] = row_pres
+
+        if col_node is not None:
+            c_classes = _get_classes_for_node(col_node)
+            if inner_col_node is not None:
+                c_classes.extend(_get_classes_for_node(inner_col_node))
+            col_pres = set([NEWCOL] + [PresentationClass(c) for c in c_classes if not layout_strategy.is_column_class(c)])
+            pres[_NEWCOL_PREFIX + si.sect_id] = col_pres
 
     _strip_presentation(root)
     out_html = _html_extract(root)