visitElementAfter method

  1. @override
void visitElementAfter(
  1. Element<Node> element
)
override

Called when an Element has been reached, after its children have been visited.

Will not be called if visitElementBefore returns false.

Implementation

@override
void visitElementAfter(Element element) {
  final current = _tree.removeLast();
  final type = element.type;
  final attributes = element.attributes;

  HtmlElement node;

  if (_isCodeBlock(type)) {
    final code = HtmlElement('code', current.children);

    if (attributes['language'] != null) {
      var language = attributes['language']!;
      if (encodeHtml) {
        language = language.toHtmlText();
      }
      code.attributes['class'] = 'language-$language';
    }

    node = HtmlElement('pre', [code]);
  } else {
    var tag = _htmlTagMap[type] ?? type;

    if (_isHealine(type)) {
      tag = 'h${attributes['level']}';
    }

    if (_isSelfClosing(element)) {
      node = HtmlElement.empty(tag);

      if (type == 'image') {
        node.attributes.addAll({
          'src': attributes['destination']!,
          if (attributes['description'] != null)
            'alt': attributes['description']!,
          if (attributes['title'] != null) 'title': attributes['title']!,
        });
      }
    } else {
      node = HtmlElement(tag, current.children);

      if (_isHealine(type)) {
        node.generatedId = attributes['generatedId'];
      } else if (type == 'orderedList' && attributes['start'] != null) {
        node.attributes['start'] = attributes['start']!;
      } else if (type == 'listItem' && attributes['taskListItem'] != null) {
        final checkboxInput = HtmlElement.empty('input');
        node.attributes['class'] = 'task-list-item';
        checkboxInput.attributes.addAll({
          'type': 'checkbox',
          if (attributes['taskListItem'] == 'checked') 'checked': '',
        });

        // Add a whitespace between input and text.
        // This whitespace should not be added in Markdown AST tree, because
        // some output targets such as Flutter might not want this whitespace.
        node.children?.insertAll(0, [checkboxInput, HtmlText(' ')]);
      } else if (type == 'tableHeadCell' || type == 'tableBodyCell') {
        if (attributes['textAlign'] != null) {
          node.attributes['align'] = attributes['textAlign']!;
        }
      } else if (_isLink(type)) {
        node.attributes.addAll({
          if (attributes['destination'] != null)
            'href': attributes['destination']!,
          if (attributes['title'] != null) 'title': attributes['title']!,
        });

        if (attributes['text'] != null) {
          node.children!
            ..clear()
            ..add(HtmlText(attributes['text']!));
        }
      } else if (type == 'footnote') {
        final label = attributes['label'];
        final link = HtmlElement('a', [
          HtmlText(attributes['number']!)
        ], attributes: {
          'href': '#fn:$label',
        });
        node.attributes.addAll({
          'id': 'fnref:$label',
          'class': 'footnote',
        });
        node.children!.add(link);
      } else if (type == 'footnoteReference') {
        final number = element.attributes['number'];
        final label = attributes['label'];
        // ignore the ones are not connected to footnote.
        if (number == null) {
          return;
        }
        final link = HtmlElement('a', [
          HtmlText('↩')
        ], attributes: {
          'class': 'footnote-reverse',
          'href': '#fnref:$label',
        });
        node.attributes['id'] = 'fn:$label';
        node.children!.add(link);
        _footnoteReferences[number] = node;
        // Do not write to tree.
        return;
      }
    }
  }

  _lastVisitElement = type;
  _tree.last.children.add(node);
}