processNode function

void processNode(
  1. Node node,
  2. Map<String, dynamic> attributes,
  3. Delta delta, {
  4. bool addSpanAttrs = false,
  5. List<CustomHtmlPart>? customBlocks,
  6. List<String>? removeTheseAttributesFromSpan,
})

Processes a DOM node, converting it into Quill Delta operations.

Recursively processes the DOM nodes, converting text nodes, inline styles, links, and custom HTML blocks into Quill Delta operations.

Parameters:

  • node: The DOM node to process.
  • attributes: The current Delta attributes to apply.
  • delta: The Delta object to push operations into.
  • addSpanAttrs: Whether to add attributes from tags.
  • customBlocks: Optional list of custom HTML block definitions.

Example:

final htmlNode = dom.Element.tag('p')..append(dom.Text('Hello, <strong><em>World</em></strong>!'));
final delta = Delta();
processNode(htmlNode, {}, delta);
print(delta.toJson()); // Output: [{"insert": "Hello, "}, {"insert": "World", "attributes": {"italic": true, "bold": true}}, {"insert": "!"}]

Implementation

void processNode(
  dom.Node node,
  Map<String, dynamic> attributes,
  Delta delta, {
  bool addSpanAttrs = false,
  List<CustomHtmlPart>? customBlocks,
  List<String>? removeTheseAttributesFromSpan,
}) {
  if (node is dom.Text) {
    delta.insert(node.text, attributes.isEmpty ? null : attributes);
  } else if (node is dom.Element) {
    Map<String, dynamic> newAttributes = Map.from(attributes);

    // Apply inline styles based on tag type
    if (node.isStrong) newAttributes['bold'] = true;
    if (node.isItalic) newAttributes['italic'] = true;
    if (node.isUnderline) newAttributes['underline'] = true;
    if (node.isStrike) newAttributes['strike'] = true;
    if (node.isSubscript) newAttributes['script'] = 'sub';
    if (node.isSuperscript) newAttributes['script'] = 'super';

    // Use custom block definitions if provided
    if (customBlocks != null && customBlocks.isNotEmpty) {
      for (var customBlock in customBlocks) {
        if (customBlock.matches(node)) {
          final operations =
              customBlock.convert(node, currentAttributes: newAttributes);
          operations.forEach((Operation op) {
            delta.insert(op.data, op.attributes);
          });
          continue;
        }
      }
    } else {
      // Handle <span> tags
      if (node.isSpan) {
        final spanAttributes =
            parseStyleAttribute(node.getSafeAttribute('style'));
        if (addSpanAttrs) {
          newAttributes.remove('align');
          newAttributes.remove('direction');
          newAttributes.remove('indent');
          if (removeTheseAttributesFromSpan != null &&
              removeTheseAttributesFromSpan.isNotEmpty) {
            for (final attr in removeTheseAttributesFromSpan) {
              newAttributes.remove(attr);
            }
          }
          newAttributes = {...spanAttributes, ...newAttributes};
        }
      }

      // Handle <img> tags
      if (node.isImg) {
        final String src = node.attributes['src'] ?? '';
        final String styles = node.attributes['style'] ?? '';
        final String align = node.attributes['align'] ?? '';
        final attributes = parseImageStyleAttribute(styles, align);
        if (src.isNotEmpty) {
          delta.insert(
            {'image': src},
            styles.isEmpty
                ? null
                : {
                    'style': attributes.entries
                        .map((entry) => '${entry.key}:${entry.value}')
                        .toList()
                        .join(';'),
                  },
          );
        }
      }

      // Handle <video> tags
      if (node.isVideo) {
        final String? src = node.getAttribute('src');
        final String? sourceSrc = node.nodes
            .where((node) => node.nodeType == dom.Node.ELEMENT_NODE)
            .firstOrNull
            ?.attributes['src'];
        if (src != null && src.isNotEmpty ||
            sourceSrc != null && sourceSrc.isNotEmpty) {
          delta.insert({'video': src ?? sourceSrc});
        }
      }

      // Handle <a> tags (links)
      if (node.isLink) {
        final String? src = node.attributes['href'];
        if (src != null) {
          newAttributes.remove('indent');
          newAttributes['link'] = src;
        }
      }

      // Handle <br> tags (line breaks)
      if (node.isBreakLine) {
        newAttributes.remove('align');
        newAttributes.remove('direction');
        newAttributes.remove('indent');
        delta.insert('\n');
      }
    }

    // Recursively process child nodes
    for (final child in node.nodes) {
      processNode(child, newAttributes, delta, addSpanAttrs: addSpanAttrs);
    }
  }
}