Пример #1
0
 def setUp(self):
     self.wrapper_state = WrapperState({
         'header-one': {
             'element': 'h1'
         },
         'unstyled': {
             'element': 'div'
         },
     })
Пример #2
0
    def render(self, content_state):
        """
        Starts the export process on a given piece of content state.
        """
        self.wrapper_state = WrapperState(self.block_map)
        self.style_state = StyleState(self.style_map)
        entity_map = content_state.get('entityMap', {})

        for block in content_state.get('blocks', []):
            self.render_block(block, entity_map)

        self.wrapper_state.clean_up()

        return self.wrapper_state.to_string()
Пример #3
0
    def setUp(self):
        DOM.use(DOM.STRING)

        self.wrapper_state = WrapperState({
            'header-one': 'h1',
            'unstyled': 'div',
            'atomic': lambda props: props['children'],
            'ignore': None,
            'blockquote': blockquote,
            'ordered-list-item': {
                'element': list_item,
                'wrapper': ordered_list
            },
        })
Пример #4
0
    def render(self, content_state=None):
        """
        Starts the export process on a given piece of content state.
        """
        if content_state is None:
            content_state = {}

        blocks = content_state.get('blocks', [])
        wrapper_state = WrapperState(self.block_options, blocks)
        document = DOM.create_element()
        entity_map = content_state.get('entityMap', {})
        min_depth = 0

        for block in blocks:
            # Assume a depth of 0 if it's not specified, like Draft.js would.
            depth = block['depth'] if 'depth' in block else 0
            elt = self.render_block(block, entity_map, wrapper_state)

            if depth > min_depth:
                min_depth = depth

            # At level 0, append the element to the document.
            if depth == 0:
                DOM.append_child(document, elt)

        # If there is no block at depth 0, we need to add the wrapper that contains the whole tree to the document.
        if min_depth > 0 and wrapper_state.stack.length() != 0:
            DOM.append_child(document, wrapper_state.stack.tail().elt)

        return DOM.render(document)
Пример #5
0
    def render(self, content_state=None):
        """
        Starts the export process on a given piece of content state.
        """
        if content_state is None:
            content_state = {}

        wrapper_state = WrapperState(self.block_map)
        document = DOM.create_element()
        entity_map = content_state.get('entityMap', {})
        min_depth = 0

        for block in content_state.get('blocks', []):
            depth = block['depth']
            elt = self.render_block(block, entity_map, wrapper_state)

            if depth > min_depth:
                min_depth = depth

            # At level 0, append the element to the document.
            if depth == 0:
                DOM.append_child(document, elt)

        # If there is no block at depth 0, we need to add the wrapper that contains the whole tree to the document.
        if min_depth > 0 and wrapper_state.stack.length() != 0:
            DOM.append_child(document, wrapper_state.stack.tail().elt)

        return DOM.render(document)
Пример #6
0
    def render_block(self, block: Block, entity_map: EntityMap,
                     wrapper_state: WrapperState) -> Element:
        has_styles = "inlineStyleRanges" in block and block["inlineStyleRanges"]
        has_entities = "entityRanges" in block and block["entityRanges"]
        has_decorators = should_render_decorators(self.composite_decorators,
                                                  block["text"])

        if has_styles or has_entities:
            content = DOM.create_element()
            entity_state = EntityState(self.entity_options, entity_map)
            style_state = StyleState(
                self.style_options) if has_styles else None

            for (text, commands) in self.build_command_groups(block):
                for command in commands:
                    entity_state.apply(command)
                    if style_state:
                        style_state.apply(command)

                # Decorators are not rendered inside entities.
                if has_decorators and entity_state.has_no_entity():
                    decorated_node = render_decorators(
                        self.composite_decorators,
                        text,
                        block,
                        wrapper_state.blocks,
                    )
                else:
                    decorated_node = text

                if style_state:
                    styled_node = style_state.render_styles(
                        decorated_node, block, wrapper_state.blocks)
                else:
                    styled_node = decorated_node
                entity_node = entity_state.render_entities(
                    styled_node, block, wrapper_state.blocks)

                if entity_node is not None:
                    DOM.append_child(content, entity_node)

                    # Check whether there actually are two different nodes, confirming we are not inserting an upcoming entity.
                    if (styled_node != entity_node
                            and entity_state.has_no_entity()):
                        DOM.append_child(content, styled_node)
        # Fast track for blocks which do not contain styles nor entities, which is very common.
        elif has_decorators:
            content = render_decorators(
                self.composite_decorators,
                block["text"],
                block,
                wrapper_state.blocks,
            )
        else:
            content = block["text"]

        return wrapper_state.element_for(block, content)
Пример #7
0
    def setUp(self):
        DOM.use(DOM.STRING)

        self.wrapper_state = WrapperState(
            Options.map_blocks(
                {
                    "header-one": "h1",
                    "unstyled": "div",
                    "atomic": lambda props: props["children"],
                    "ignore": None,
                    "blockquote": blockquote,
                    "ordered-list-item": {
                        "element": list_item,
                        "wrapper": ordered_list,
                    },
                }
            ),
            [],
        )
Пример #8
0
    def test_element_for_data(self):
        blocks = [
            {
                'key': '5s7g9',
                'text': 'test',
                'type': 'unstyled',
                'depth': 0,
                'inlineStyleRanges': [],
                'entityRanges': [],
            },
        ]

        def unstyled(props):
            self.assertEqual(props['blocks'], blocks)
            self.assertEqual(props['block'], blocks[0])

        WrapperState(Options.map_blocks({'unstyled': unstyled}),
                     blocks).element_for(blocks[0], 'test')
Пример #9
0
    def test_element_for_data(self):
        blocks = [
            {
                "key": "5s7g9",
                "text": "test",
                "type": "unstyled",
                "depth": 0,
                "inlineStyleRanges": [],
                "entityRanges": [],
            }
        ]

        def unstyled(props):
            self.assertEqual(props["blocks"], blocks)
            self.assertEqual(props["block"], blocks[0])

        WrapperState(
            Options.map_blocks({"unstyled": unstyled}), blocks
        ).element_for(blocks[0], "test")
Пример #10
0
class HTML:
    """
    Entry point of the exporter. Combines entity, wrapper and style state
    to generate the right HTML nodes.
    """
    def __init__(self, config=None):
        if config is None:
            config = {}

        self.entity_decorators = config.get('entity_decorators', {})
        self.block_map = config.get('block_map', BLOCK_MAP)
        self.style_map = config.get('style_map', STYLE_MAP)

    def render(self, content_state):
        """
        Starts the export process on a given piece of content state.
        """
        self.wrapper_state = WrapperState(self.block_map)
        self.style_state = StyleState(self.style_map)
        entity_map = content_state.get('entityMap', {})

        for block in content_state.get('blocks', []):
            self.render_block(block, entity_map)

        self.wrapper_state.clean_up()

        return self.wrapper_state.to_string()

    def render_block(self, block, entity_map):
        element = self.wrapper_state.element_for(block)
        entity_state = EntityState(self.entity_decorators, entity_map)

        for (text, commands) in self.build_command_groups(block):
            for command in commands:
                entity_state.apply(command)
                self.style_state.apply(command)

            style_node = self.style_state.create_node(text)
            entity_state.render_entitities(element, style_node)

    def build_command_groups(self, block):
        """
        Creates block modification commands, grouped by start index,
        with the text to apply them on.
        """
        text = block.get('text')

        commands = self.build_commands(block)
        # Tried using itertools.tee but for some reason that failed. Oh well.
        grouped = Command.grouped_by_index(commands)
        listed = list(Command.grouped_by_index(commands))
        sliced = []

        i = 0
        for start_index, commands in grouped:
            next_group = listed[i + 1] if i + 1 < len(listed) else False
            stop_index = next_group[0] if next_group else 0

            sliced.append((text[start_index:stop_index], list(commands)))
            i += 1

        return sliced

    def build_commands(self, block):
        """
        Build all of the manipulation commands for a given block.
        - One pair to set the text.
        - Multiple pairs for styles.
        - Multiple pairs for entities.
        """
        text_commands = Command.start_stop('text', 0, len(block.get('text')))
        style_commands = self.build_style_commands(block)
        entity_commands = self.build_entity_commands(block)

        return text_commands + style_commands + entity_commands

    def build_style_commands(self, block):
        ranges = block.get('inlineStyleRanges', [])
        return Command.from_ranges(ranges, 'inline_style', 'style')

    def build_entity_commands(self, block):
        ranges = block.get('entityRanges', [])
        return Command.from_ranges(ranges, 'entity', 'key')
Пример #11
0
class TestWrapperState(unittest.TestCase):
    def setUp(self):
        self.wrapper_state = WrapperState({
            'header-one': {
                'element': 'h1'
            },
            'unstyled': {
                'element': 'div'
            },
        })

    def test_init(self):
        self.assertIsInstance(self.wrapper_state, WrapperState)

    def test_element_for_text(self):
        self.assertEqual(
            DOM.get_text_content(
                self.wrapper_state.element_for({
                    'key': '5s7g9',
                    'text': 'Header',
                    'type': 'header-one',
                    'depth': 0,
                    'inlineStyleRanges': [],
                    'entityRanges': []
                })), '')

    def test_element_for_tag(self):
        self.assertEqual(
            DOM.get_tag_name(
                self.wrapper_state.element_for({
                    'key': '5s7g9',
                    'text': 'Header',
                    'type': 'header-one',
                    'depth': 0,
                    'inlineStyleRanges': [],
                    'entityRanges': []
                })), 'h1')

    def test_element_for_raises(self):
        with self.assertRaises(BlockException):
            self.wrapper_state.element_for({
                'key': '5s7g9',
                'text': 'Header',
                'type': 'header-two',
                'depth': 0,
                'inlineStyleRanges': [],
                'entityRanges': []
            })

    def test_to_string_empty(self):
        self.assertEqual(self.wrapper_state.to_string(), '')

    def test_to_string_elts(self):
        self.wrapper_state.element_for({
            'key': '5s7g9',
            'text': 'Header',
            'type': 'header-one',
            'depth': 0,
            'inlineStyleRanges': [],
            'entityRanges': []
        })

        self.assertEqual(self.wrapper_state.to_string(), '<h1></h1>')

    def test_str_empty(self):
        self.assertEqual(str(self.wrapper_state), '<WrapperState: >')

    def test_str_elts(self):
        self.wrapper_state.element_for({
            'key': '5s7g9',
            'text': 'Header',
            'type': 'header-one',
            'depth': 0,
            'inlineStyleRanges': [],
            'entityRanges': []
        })

        self.assertEqual(str(self.wrapper_state), '<WrapperState: <h1></h1>>')

    def test_map_element_options_full(self):
        self.assertEqual(Options.map([
            'ul',
            {
                'className': 'bullet-list'
            },
        ]), [
            'ul',
            {
                'className': 'bullet-list'
            },
        ])

    def test_map_element_options_half(self):
        self.assertEqual(Options.map([
            'ul',
        ]), [
            'ul',
            {},
        ])

    def test_map_element_options_simplest(self):
        self.assertEqual(Options.map('ul'), [
            'ul',
            {},
        ])
 def setUp(self):
     self.wrapper_state = WrapperState(block_map)
class TestWrapperState(unittest.TestCase):
    def setUp(self):
        self.wrapper_state = WrapperState(block_map)

    def test_init(self):
        self.assertIsInstance(self.wrapper_state, WrapperState)

    def test_element_for_text(self):
        self.assertEqual(self.wrapper_state.element_for({
            'key': '5s7g9',
            'text': 'Header',
            'type': 'header-one',
            'depth': 0,
            'inlineStyleRanges': [],
            'entityRanges': []
        }).text, None)

    def test_element_for_tag(self):
        self.assertEqual(self.wrapper_state.element_for({
            'key': '5s7g9',
            'text': 'Header',
            'type': 'header-one',
            'depth': 0,
            'inlineStyleRanges': [],
            'entityRanges': []
        }).tag, 'h1')

    def test_element_for_raises(self):
        with self.assertRaises(BlockException):
            self.wrapper_state.element_for({
                'key': '5s7g9',
                'text': 'Header',
                'type': 'header-two',
                'depth': 0,
                'inlineStyleRanges': [],
                'entityRanges': []
            })

    def test_to_string_empty(self):
        self.assertEqual(self.wrapper_state.to_string(), '')

    def test_to_string_elts(self):
        self.wrapper_state.element_for({
            'key': '5s7g9',
            'text': 'Header',
            'type': 'header-one',
            'depth': 0,
            'inlineStyleRanges': [],
            'entityRanges': []
        })

        self.assertEqual(self.wrapper_state.to_string(), '<h1></h1>')

    def test_str_empty(self):
        self.assertEqual(str(self.wrapper_state), '<WrapperState: >')

    def test_str_elts(self):
        self.wrapper_state.element_for({
            'key': '5s7g9',
            'text': 'Header',
            'type': 'header-one',
            'depth': 0,
            'inlineStyleRanges': [],
            'entityRanges': []
        })

        self.assertEqual(str(self.wrapper_state), '<WrapperState: <h1></h1>>')
Пример #14
0
 def __init__(self, config={}):
     self.entity_decorators = config.get('entity_decorators', {})
     self.wrapper_state = WrapperState(config.get('block_map', BLOCK_MAP))
     self.style_state = StyleState(config.get('style_map', STYLE_MAP))
Пример #15
0
class TestWrapperState(unittest.TestCase):
    def setUp(self):
        DOM.use(DOM.STRING)

        self.wrapper_state = WrapperState(
            Options.map_blocks(
                {
                    "header-one": "h1",
                    "unstyled": "div",
                    "atomic": lambda props: props["children"],
                    "ignore": None,
                    "blockquote": blockquote,
                    "ordered-list-item": {
                        "element": list_item,
                        "wrapper": ordered_list,
                    },
                }
            ),
            [],
        )

    def test_init(self):
        self.assertIsInstance(self.wrapper_state, WrapperState)

    def test_element_for_data(self):
        blocks = [
            {
                "key": "5s7g9",
                "text": "test",
                "type": "unstyled",
                "depth": 0,
                "inlineStyleRanges": [],
                "entityRanges": [],
            }
        ]

        def unstyled(props):
            self.assertEqual(props["blocks"], blocks)
            self.assertEqual(props["block"], blocks[0])

        WrapperState(
            Options.map_blocks({"unstyled": unstyled}), blocks
        ).element_for(blocks[0], "test")

    def test_element_for_simple_content(self):
        self.assertEqual(
            DOM.render_debug(
                self.wrapper_state.element_for(
                    {
                        "key": "5s7g9",
                        "text": "Header",
                        "type": "header-one",
                        "depth": 0,
                        "inlineStyleRanges": [],
                        "entityRanges": [],
                    },
                    "Header",
                )
            ),
            "<h1>Header</h1>",
        )

    def test_element_for_element_content(self):
        self.assertEqual(
            DOM.render_debug(
                self.wrapper_state.element_for(
                    {
                        "key": "5s7g9",
                        "text": "Paragraph",
                        "type": "unstyled",
                        "depth": 0,
                        "inlineStyleRanges": [],
                        "entityRanges": [],
                    },
                    DOM.create_element("strong", {}, "Paragraph"),
                )
            ),
            "<div><strong>Paragraph</strong></div>",
        )

    def test_element_for_dismiss_content(self):
        self.assertEqual(
            DOM.render_debug(
                self.wrapper_state.element_for(
                    {
                        "key": "5s7g9",
                        "text": "Paragraph",
                        "type": "ignore",
                        "depth": 0,
                        "inlineStyleRanges": [],
                        "entityRanges": [],
                    },
                    DOM.create_element("img", {"src": "/example.png"}),
                )
            ),
            "<fragment></fragment>",
        )

    def test_element_for_no_block(self):
        self.assertEqual(
            DOM.render_debug(
                self.wrapper_state.element_for(
                    {
                        "key": "5s7g9",
                        "text": "Paragraph",
                        "type": "atomic",
                        "depth": 0,
                        "inlineStyleRanges": [],
                        "entityRanges": [],
                    },
                    DOM.create_element("img", {"src": "/example.png"}),
                )
            ),
            '<img src="/example.png"/>',
        )

    def test_element_for_component(self):
        self.assertEqual(
            DOM.render_debug(
                self.wrapper_state.element_for(
                    {
                        "key": "5s7g9",
                        "text": "Paragraph",
                        "type": "blockquote",
                        "depth": 0,
                        "data": {"cite": "http://example.com/"},
                        "inlineStyleRanges": [],
                        "entityRanges": [],
                    },
                    "Test",
                )
            ),
            '<blockquote cite="http://example.com/">Test</blockquote>',
        )

    def test_element_for_component_wrapper(self):
        self.assertEqual(
            DOM.render_debug(
                self.wrapper_state.element_for(
                    {
                        "key": "5s7g9",
                        "text": "Test",
                        "type": "ordered-list-item",
                        "depth": 0,
                        "data": {},
                        "inlineStyleRanges": [],
                        "entityRanges": [],
                    },
                    "Test",
                )
            ),
            '<ol class="list--depth-0"><li class="list-item--depth-0">Test</li></ol>',
        )

    def test_str_elts(self):
        self.assertEqual(
            DOM.render_debug(
                self.wrapper_state.element_for(
                    {
                        "key": "5s7g9",
                        "text": "Header",
                        "type": "header-one",
                        "depth": 0,
                        "inlineStyleRanges": [],
                        "entityRanges": [],
                    },
                    "",
                )
            ),
            "<h1></h1>",
        )

    def test_str(self):
        self.assertEqual(str(self.wrapper_state), "<WrapperState: []>")
Пример #16
0
class TestWrapperState(unittest.TestCase):
    def setUp(self):
        DOM.use(DOM.STRING)

        self.wrapper_state = WrapperState(
            {
                'header-one': 'h1',
                'unstyled': 'div',
                'atomic': lambda props: props['children'],
                'ignore': None,
                'blockquote': blockquote,
                'ordered-list-item': {
                    'element': list_item,
                    'wrapper': ordered_list
                },
            }, [])

    def test_init(self):
        self.assertIsInstance(self.wrapper_state, WrapperState)

    def test_element_for_data(self):
        blocks = [
            {
                'key': '5s7g9',
                'text': 'test',
                'type': 'unstyled',
                'depth': 0,
                'inlineStyleRanges': [],
                'entityRanges': [],
            },
        ]

        def unstyled(props):
            self.assertEqual(props['blocks'], blocks)
            self.assertEqual(props['block'], blocks[0])

        WrapperState({
            'unstyled': unstyled
        }, blocks).element_for(blocks[0], 'test')

    def test_element_for_simple_content(self):
        self.assertEqual(
            DOM.render_debug(
                self.wrapper_state.element_for(
                    {
                        'key': '5s7g9',
                        'text': 'Header',
                        'type': 'header-one',
                        'depth': 0,
                        'inlineStyleRanges': [],
                        'entityRanges': []
                    }, 'Header')), '<h1>Header</h1>')

    def test_element_for_element_content(self):
        self.assertEqual(
            DOM.render_debug(
                self.wrapper_state.element_for(
                    {
                        'key': '5s7g9',
                        'text': 'Paragraph',
                        'type': 'unstyled',
                        'depth': 0,
                        'inlineStyleRanges': [],
                        'entityRanges': []
                    }, DOM.create_element('strong', {}, 'Paragraph'))),
            '<div><strong>Paragraph</strong></div>')

    def test_element_for_dismiss_content(self):
        self.assertEqual(
            DOM.render_debug(
                self.wrapper_state.element_for(
                    {
                        'key': '5s7g9',
                        'text': 'Paragraph',
                        'type': 'ignore',
                        'depth': 0,
                        'inlineStyleRanges': [],
                        'entityRanges': []
                    }, DOM.create_element('img', {'src': '/example.png'}))),
            '<fragment></fragment>')

    def test_element_for_no_block(self):
        self.assertEqual(
            DOM.render_debug(
                self.wrapper_state.element_for(
                    {
                        'key': '5s7g9',
                        'text': 'Paragraph',
                        'type': 'atomic',
                        'depth': 0,
                        'inlineStyleRanges': [],
                        'entityRanges': []
                    }, DOM.create_element('img', {'src': '/example.png'}))),
            '<img src="/example.png"/>')

    def test_element_for_component(self):
        self.assertEqual(
            DOM.render_debug(
                self.wrapper_state.element_for(
                    {
                        'key': '5s7g9',
                        'text': 'Paragraph',
                        'type': 'blockquote',
                        'depth': 0,
                        'data': {
                            'cite': 'http://example.com/',
                        },
                        'inlineStyleRanges': [],
                        'entityRanges': []
                    }, 'Test')),
            '<blockquote cite="http://example.com/">Test</blockquote>')

    def test_element_for_component_wrapper(self):
        self.assertEqual(
            DOM.render_debug(
                self.wrapper_state.element_for(
                    {
                        'key': '5s7g9',
                        'text': 'Test',
                        'type': 'ordered-list-item',
                        'depth': 0,
                        'data': {},
                        'inlineStyleRanges': [],
                        'entityRanges': []
                    }, 'Test')),
            '<ol class="list--depth-0"><li class="list-item--depth-0">Test</li></ol>'
        )

    def test_str_elts(self):
        self.assertEqual(
            DOM.render_debug(
                self.wrapper_state.element_for(
                    {
                        'key': '5s7g9',
                        'text': 'Header',
                        'type': 'header-one',
                        'depth': 0,
                        'inlineStyleRanges': [],
                        'entityRanges': []
                    }, '')), '<h1></h1>')

    def test_str(self):
        self.assertEqual(str(self.wrapper_state), '<WrapperState: []>')