Luke Plant avatar Luke Plant committed d03643a

Removed section divs that are applied according to headings

CSS classes are applied to elements directly, additional divs no longer
inserted. This changes everything with regards to column layout, so some
tests have been ditched.

Comments (0)

Files changed (2)

semanticeditor/tests.py

 
     def test_no_headings(self):
         html = "<p>Test</p>"
-        outh = "<div><p>Test</p></div>"
-        self.assertEqual(outh, format_html(html, {}))
-
-    def test_creates_section_divs(self):
-        html = "<h1>Hello</h1><p>P 1</p><h2>Heading 2</h2> trailing text<h1>Another</h1><p>So</p> trail"
-        outh = "<div><h1>Hello</h1><div><p>P 1</p></div><div><h2>Heading 2</h2> trailing text</div></div><div><h1>Another</h1><div><p>So</p> trail</div></div>"
+        outh = "<p>Test</p>"
         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 = "<foo><bar><fribble>Some text <div><p>para</p> some more more </div><span> of </span> this stuff </fribble></bar></foo>"
+        outh = "<foo><bar><fribble>Some text <p>para</p> some more more <span> of </span> this stuff </fribble></bar></foo>"
         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=\"myclass\"><h1>Hello <em>you</em></h1><div class=\"c1 c2\"><h2>Hi</h2></div></div>"
+        outh = "<h1 class=\"myclass\">Hello <em>you</em></h1><h2 class=\"c1 c2\">Hi</h2>"
         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\"><div><h1>1</h1><div><p>para 1</p></div></div></div><div class=\"column lastcolumn\"><div><h1>2</h1></div><div><h1>3</h1></div></div></div>"
+        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>"
         self.assertEqual(outh, format_html(html, {'h1_1':[NEWROW],
                                                   'h1_2':[NEWCOL]}))
 
                                                               'h1_5':[NEWCOL]
                                                             })
 
-    def test_creates_section_divs_2(self):
-        html = \
-            "<h1>1</h1>" \
-            "<h1>2</h1>" \
-            "<h2>2.1</h2>" \
-            "<h2>2.2</h2>" \
-            "<h2>2.3</h2>" \
-            "<h2>2.4</h2>" \
-            "<h1>3</h1>" \
-            "<h1>4</h1>"
-
-        outh = \
-            "<div><h1>1</h1></div>" \
-            "<div><h1>2</h1>" \
-            "<div><h2>2.1</h2></div>" \
-            "<div><h2>2.2</h2></div>" \
-            "<div><h2>2.3</h2></div>" \
-            "<div><h2>2.4</h2></div>" \
-            "</div>" \
-            "<div><h1>3</h1></div>" \
-            "<div><h1>4</h1></div>"
-        self.assertEqual(outh, format_html(html, {}))
-
-
     def test_columns_2(self):
         html = \
             "<h1>1</h1>" \
             "<h1>4</h1>"
 
         outh = \
-            "<div><h1>1</h1></div>" \
-            "<div><h1>2</h1>" \
+            "<h1>1</h1>" \
+            "<h1>2</h1>" \
             "<div class=\"row columns2\">" \
             "<div class=\"column firstcolumn\">" \
-            "<div><h2>2.1</h2></div>" \
+            "<h2>2.1</h2>" \
             "</div>" \
             "<div class=\"column lastcolumn\">" \
-            "<div><h2>2.2</h2></div>" \
+            "<h2>2.2</h2>" \
             "</div>" \
             "</div>" \
             "<div class=\"row columns2\">" \
             "<div class=\"column firstcolumn\">" \
-            "<div><h2>2.3</h2></div>" \
+            "<h2>2.3</h2>" \
             "</div>" \
             "<div class=\"column lastcolumn\">" \
-            "<div><h2>2.4</h2></div>" \
-            "</div>" \
+            "<h2>2.4</h2>" \
             "</div>" \
             "</div>" \
             "<div class=\"row columns2\">" \
             "<div class=\"column firstcolumn\">" \
-            "<div><h1>3</h1></div>" \
+            "<h1>3</h1>" \
             "</div>" \
             "<div class=\"column lastcolumn\">" \
-            "<div><h1>4</h1></div>" \
+            "<h1>4</h1>" \
             "</div>" \
             "</div>"
         self.assertEqual(outh, format_html(html, {'h2_1':[NEWROW],
                                                   'h1_4':[NEWCOL],
                                                   }))
 
-    def test_columns_3(self):
-        # This checks that we are allowed to nest columns within a
-        # 'row1col' div, and tests applying columns to paragraphs.
-        # See example in presentation.py
-        html = \
-            "<h1>1</h1>" \
-            "<h1>2</h1>" \
-            "<h1>3</h1>" \
-            "<p>P1</p>" \
-            "<p>P2</p>" \
-            "<p>P3</p>" \
-            "<h1>4</h1>"
-        pres = {
-            'h1_1':[NEWROW],
-            'h1_2':[NEWCOL],
-            'h1_3':[NEWROW],
-            'p_1':[NEWROW],
-            'p_2':[NEWCOL],
-            'p_3':[NEWCOL],
-            'h1_4':[NEWROW],
-            }
-
-        outh = \
-            "<div class=\"row columns2\">" \
-              "<div class=\"column firstcolumn\">" \
-                "<div><h1>1</h1></div>" \
-              "</div>" \
-              "<div class=\"column lastcolumn\">" \
-                "<div><h1>2</h1></div>" \
-              "</div>" \
-            "</div>" \
-            "<div class=\"row columns1\">" \
-              "<div class=\"column firstcolumn lastcolumn\">" \
-                "<div><h1>3</h1>" \
-                  "<div class=\"row columns3\">" \
-                    "<div class=\"column firstcolumn\">" \
-                      "<div><p>P1</p></div>" \
-                    "</div>" \
-                    "<div class=\"column\">" \
-                      "<div><p>P2</p></div>" \
-                    "</div>" \
-                    "<div class=\"column lastcolumn\">" \
-                      "<div><p>P3</p></div>" \
-                    "</div>" \
-                  "</div>" \
-                "</div>" \
-              "</div>" \
-            "</div>" \
-            "<div class=\"row columns1\">" \
-              "<div class=\"column firstcolumn lastcolumn\">" \
-                "<div><h1>4</h1></div>" \
-              "</div>" \
-            "</div>"
-
-        actualhtml = format_html(html, pres)
-
-        self.assertEqual(outh, actualhtml)
-
-        # Check that if we add NEWCOL to '4', we get BadStructure
-        pres_bad1 = pres.copy()
-        pres_bad1.update({'h1_4':[NEWCOL]})
-        self.assertRaises(BadStructure, format_html, html, pres_bad1)
-
 
     def test_columns_missing_newrow(self):
         html = "<h1>1</h1><p>para 1</p><h1>2</h1><h1>3</h1>"
         self.assertRaises(BadStructure, format_html, html, {'h1_2':[NEWCOL]})
 
-    def test_columns_nested_newcols(self):
-        """
-        Check that attempting to add columns at a different level
-        will generate an error
-        """
-        html = "<h1>1</h1><h1>2</h1><h2>1.1</h2><h1>3</h1>"
-        self.assertRaises(BadStructure, format_html, html, {'h1_1':[NEWROW],
-                                                            'h1_2':[NEWCOL],
-                                                            'h2_1':[NEWCOL]})
-    def test_columns_nested_newrow(self):
-        """
-        Check that attempting to add new row at a different level
-        will generate an error
-        """
-        html = "<h1>1</h1><h1>2</h1><h2>1.1</h2>"
-        self.assertRaises(BadStructure, format_html, html, {'h1_1':[NEWROW],
-                                                            'h1_2':[NEWCOL],
-                                                            'h2_1':[NEWROW]})
-
 
 class TestElementTreeUtils(TestCase):
     def test_get_parent(self):
 
 class TestExtractPresentation(TestCase):
     def test_extract_presentation(self):
-        html = "<div class=\"foo\"><h1>Heading 1</h1><div class=\"bar baz\"><h2>Heading 2</h2><div class=\"whatsit\"><p>Some paragraph</p></div></div></div>"
+        html = "<h1 class=\"foo\">Heading 1</h1><h2 class=\"bar baz\">Heading 2</h2><p class=\"whatsit\">Some paragraph</p>"
         pres, html2 = extract_presentation(html)
         self.assertEqual({'h1_1':set([PC('foo')]),
                           'h2_1':set([PC('bar'), PC('baz')]),
 
     def test_extract_2(self):
         html = """
-<div class="row columns3"><div class="column firstcolumn"><div><h1>Hello Jane</h1><div><p>Some fancy content, entered using WYMeditor</p></div><div><p>Another paragraph</p></div><div><p>Hello</p></div></div></div><div class="column"><div><h1>Another &lt;heading&gt;</h1><div><h2>this is a test</h2></div><div><h2>hello1</h2><div><h3>hello2</h3></div><div><h3>hello3</h3></div><div><h3>hello4</h3></div></div></div></div><div class="column lastcolumn"><div><h1>hello5</h1><div><h2>hello6</h2><p>asdasd</p><p>asdxx</p></div></div></div></div>
+<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>
 """
         pres = {'h1_1':set([NEWROW]),
                 'p_1': set(),
         # Tests some other boundary conditions e.g. 1 column row,
         # multiple sections within the column.
         html = """
-<div><h1>1</h1><div class="row columns1"><div class="column firstcolumn lastcolumn"><div><h2>1.1</h2></div><div><h2>1.2</h2></div></div></div></div>
+<h1>1</h1><div class="row columns1"><div class="column firstcolumn lastcolumn"><h2>1.1</h2><h2>1.2</h2></div></div>
 """
         pres = {'h1_1': set(),
                 'h2_1':set([NEWROW]),

semanticeditor/utils/presentation.py

 
 # == Formatting HTML ==
 #
-# The user is allowed to assign presentation to different sections.
-# The sections are identified by headings, so that formatting will be
-# consistent with the logical structure of the document.
-#
-# This imposes a certain div structure on the HTML.  Consider the following
-# document:
-#
-# - H1 - Section 1
-#   - H2 - Section 1.1
-#   - P
-#   - H2 - Section 1.2
-# - H1 - Section 2
-#   etc
-#
-# If the user wants 'Section 1' in a blue, bordered box, the only
-# (practical) way to do it in CSS is to create a div around *all* of
-# section 1 (including Section 1.1 and Section 1.2) and apply a CSS
-# class to it. The div structures must therefore nest according to the
-# logical structure of the document.
-#
-# If the user decided that column 1 should contain Section 1 up to
-# Section 1.1, and that column 2 should contain Section 1.2 up to
-# Section 2, this would require a div structure incompatible with the
-# above. Thus the column layout is limited by the logical structure of
-# the document.
-#
-# While the above is a real constraint, we try to be as flexible as
-# possible within it.  The user may want to be able to apply column
-# breaks to paragraphs, not just section headings.  In order to
-# support this, consider the following
-#
-# - H1 - NEWROW  - 2 col row
-# - H1 - NEWCOL
-# - H1 - NEWROW  - simply to finish the existing column structure
-#  - p - NEWROW  - this is where the column structure actual starts
-#  - p - NEWCOL
-#
-# The first 'p' will be contained in the 'row1col' div that wraps the
-# preceding H1 and its contents. So, for the case of being at the
-# beginning of a single column row, we allow a nested column
-# structure.  This imposes a constraint on what follows - a H1
-# following the second 'p' cannot have 'NEWCOL' because that would
-# make the previous H1 into the start of a *2* column row!
-
+# The user is allowed to assign presentation to different sections and block
+# elements, including use of 'new row' and 'new column' commands.
 
 def format_html(html, styleinfo, return_tree=False, pretty_print=False):
     """
     _strip_presentation(root)
     _assert_sane_sections(root, structure)
 
-    section_nodes = {}
-    headers = [si for si in structure
-               if si.tag.lower() in headingdef]
-
-    # Cut the HTML up into sections
-
-    # First deal with headers only.  This makes life simple,
-    # as headers always produce nested structures, and the
-    # indexes passed to wrap_elements_in_tag don't need
-    # adjusting for the changes we have made.
-    for idx, si in enumerate(headers):
-        # We can no longer assume that parent = root, because the divs
-        # we insert will change that.  However, the divs we insert
-        # will keep sub-section headings on the same level.
-        parent = get_parent(root, si.node)
-
-        thisidx = get_index(parent, si.node)
-        first_elem = thisidx
-
-        # if a heading, then the 'scope' of each section is from
-        # heading node to before the next heading with a level the
-        # same or higher
-        nextnodes = [(si2.level, si2.node) for si2 in headers[idx+1:] if si2.level <= si.level]
-        if not nextnodes:
-            # scope extends to end
-            # Bug in elementtree - throws AssertionError if we try
-            # to set a slice with [something:None]. So we use len()
-            # instead of None
-            last_elem = len(parent)
-        else:
-            # scope extends to node before n
-            nextnode = nextnodes[0][1]
-            nn_parent = get_parent(root, nextnode)
-            if nn_parent is parent:
-                # Same level, can find index
-                last_elem = get_index(parent, nextnode)
-            else:
-                # Different level, (due to having been enclosed in a
-                # div already), just go to end
-                last_elem = len(parent)
-
-        newdiv = wrap_elements_in_tag(parent, first_elem, last_elem, "div")
-        section_nodes[si.sect_id] = newdiv
-
-    # Now deal with everything else
-    for idx, si in enumerate(structure):
-        if si.tag.lower() not in headingdef:
-            # Normal block level - these simply get a div that wraps
-            # them.
-            parent = get_parent(root, si.node)
-            thisidx = get_index(parent, si.node)
-            newdiv = wrap_elements_in_tag(parent, thisidx, thisidx + 1, "div")
-            section_nodes[si.sect_id] = newdiv
-
     # Apply normal CSS classes.
-    for sect_id, newdiv in section_nodes.items():
+    for si in structure:
         # Apply css styles
-        classes = [s.name for s in styleinfo[sect_id] if s.prestype == "class"]
+        classes = [s.name for s in styleinfo[si.sect_id] if s.prestype == "class"]
         classes.sort()
         if classes:
-            newdiv.set("class", " ".join(classes))
+            si.node.set("class", " ".join(classes))
 
     # Apply row/column commands
-    _apply_commands(root, section_nodes, styleinfo, structure, layout_strategy=layout_strategy)
+    _apply_commands(root, styleinfo, structure, layout_strategy=layout_strategy)
 
     # Pretty print
     if pretty_print:
                                "Please move the heading out of the '%(element)s'"
                                " element that contains it." % dict(name=si.name, element=parent.tag.upper()))
 
-def _apply_commands(root, section_nodes, styleinfo, structure, layout_strategy=None):
+def _apply_commands(root, styleinfo, structure, layout_strategy=None):
     # Rules:
     #  - No nesting of columns within columns
     #  - Within a given row, newcolumn must be applied to
     #    divs that are at the same level.
     #  - No columns allowed if newrow has not been started.
 
-    # 'structure' has the sections in document order
-    sections = [(si.level, si.sect_id, section_nodes[si.sect_id])
-                for si in structure]
-
-    # Inverted dict
-    known_nodes = _invert_dict(section_nodes)
+    # dict from node -> sect_id
+    known_nodes = dict((si.node, si.sect_id) for si in structure)
 
     # Preprocess:
     #  - insert 'newcolumn' on everything that has 'newrow'
             return sect_id
     return None
 
+# TODO - many of assumptions made in this function are now wrong, and requirements
+# have changed, this is in major flux
+
 def _add_rows_and_columns(topnode, known_nodes, styleinfo, layout_strategy=None):
     # This is the most involved and tricky part.  See the comments
     # above the 'format_html' function.
 
 def extract_presentation(html):
     """
-    Return the presentation elements used to format some HTML,
-    as a dictionary with keys = section names, values = set
-    of classes/commands.
+    Takes HTML with formatting applied and returns presentation elements (a
+    dictionary with keys = section names, values = set of classes/commands) and
+    the HTML without formatting (ready to be used in an editor)
     """
     # TODO: this function is not brilliantly well defined e.g.  should
     # there be an entry in the dictionary for sections with no
     pres = {}
     for si in structure:
         pres[si.sect_id] = set()
-        section_node = get_parent(root, si.node)
-        if section_node is None or section_node.tag != 'div':
-            # Not in standard format, we can't say anything about it
-            continue
 
         # Section - extract classes
-        for c in _get_classes_for_node(section_node):
+        for c in _get_classes_for_node(si.node):
             pres[si.sect_id].add(PresentationClass(c))
+            if 'class' in si.node.attrib:
+                del si.node.attrib['class']
 
         # Parent/grandparent of section - newcol/newrow
-        p = get_parent(root, section_node)
+        p = get_parent(root, si.node)
         if p is not None and p.tag == 'div':
-            # We only care if section_node is the first child of the div
-            if get_index(p, section_node) == 0:
+            # We only care if si.node is the first child of the div
+            if get_index(p, si.node) == 0:
                 classes = _get_classes_for_node(p)
                 if layout_strategy.is_column_class(classes):
                     pres[si.sect_id].add(NEWCOL)
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.