#!/usr/bin/perl -I/home/phil/perl/cpan/DitaValidate/lib/  -I/home/phil/perl/cpan/DataDFA/lib/ -I/home/phil/perl/cpan/DataNFA/lib/ -I/home/phil/perl/cpan/DataTableText/lib/
# Edit data held in the XML format.
# Philip R Brenan at gmail dot com, Appa Apps Ltd Inc, 2016-2019
#-------------------------------------------------------------------------------
# podDocumentation
# cd /home/phil/perl/cpan/DataEditXml/; perl Build.PL && perl Build test && sudo perl Build install
# perl -d:NYTProf -Ilib test.pl && nytprofhtml --open
# $source =~ s(\x{a0}) ( )gs; The no break space problem
# Do not line number if xtrf is already taken!
# Pretty print with line numbers so we can get out of adding line numbers to existing xml which is currently not reliable
# When deleting content should putFirstCut check that the cut node is not above the target node ?.
# add*asTree
# change wrap and other commands that use %attributes rather than @context to use @context
# swap c and cc
# root finds the root node possibly obviating the need for the parser attribute?
# Stop unwrap from confessing to being on the root node
# Parse DTDs so that we can separate a classification map from a conventional map
# #b means the method acts on one node and returns a string - its braceable

package Data::Edit::Xml;
our $VERSION = 20200201;
use v5.26;
use warnings FATAL => qw(all);
use strict;
use Carp qw(confess cluck);
use Data::Dump qw(dump);
use Data::Table::Text qw(:all);
#se Dita::Validate;
use XML::Parser;                                                                # https://metacpan.org/pod/XML::Parser
use Storable qw(dclone freeze retrieve store thaw);
use utf8;

#D1 Construction                                                                # Create a parse tree, either by parsing a L<file or string|/file or string> B<or> L<node by node|/Node by Node> B<or> from another L<parse tree|/Parse tree>.

#D2 File or String                                                              # Construct a parse tree from a file or a string.

our $lastParseError;                                                            # The last parse error encountered

sub new(;$@)                                                                    #IS Create a new parse tree - call this method statically as in Data::Edit::Xml::new(file or string) to parse a file or string B<or> with no parameters and then use L</input>, L</inputFile>, L</inputString>, L</errorFile>  to provide specific parameters for the parse, then call L</parse> to perform the parse and return the parse tree.
 {my ($fileNameOrString, @options) = @_;                                        # Optional file name or string from which to construct the parse tree, hash of other options.
  shift @_ while @_ && ref($_[0]);                                              # Remove any leading references to find the actual string or file to be parsed
  my ($source, %options) = @_;                                                  # Assign parameters - the one higher up is for documentation only
  checkKeys(\%options,                                                          # Check report options
   {lineNumbers => <<'END',
Save the line number.column number at which tag starts and ends on
the xtrf attribute of each node if true.
END
    input       => <<'END',
Specifies the string or file name containing the source xml to be parsed if true
END
    inputString => <<'END',
Specifies the string of xml to be parsed if true
END
    inputFile   => <<'END',
Specifies the name of the input file containing the source xml to be parsed if true.
END
    errorsFile  => <<'END',
Specifies the file to which any parse errors will be written to. By default this file is named: B<zzzParseErrors/out.data>.
END
    });
  if (@_)
   {my $x = bless {input=>@_};                                                  # Create L<XML> editor with a string or file
    $x->parser = $x;                                                            # Parser root node
    return $x->parse;                                                           # Parse
   }
  my $x = bless {};                                                             # Create empty L<XML> editor
  $x->parser = $x;                                                              # Parser root node
  $x                                                                            # Parser
 }

sub cdata()                                                                     # The name of the tag to be used to represent text - this tag must not also be used as a command tag otherwise the parser will L<confess>.
 {'CDATA'
 }

sub reduceParseErroMessage($)                                                   #P Reduce the parse failure message to the bare essentials
 {my ($e) = @_;                                                                 # Error message
  if ($e =~ m((not well-formed.*?byte\s*\d+))is) {return $1}
  $e                                                                            # Return full  message of it cannot be further reduced
 }

sub parse($)                                                                    # Parse input L<XML> specified via: L<inputFile|/inputFile>, L<input|/input> or L<inputString|/inputString>.
 {my ($parser) = @_;                                                            # Parser created by L</new>

  if (my $s = $parser->input)                                                   # Source to be parsed is a file or a string
   {if ($s =~ /\n/s or !-e $s)                                                  # Parse as a string because it does not look like a file name
     {$parser->inputString = $s;
     }
    else                                                                        # Parse a file
     {$parser->inputFile = $s;
      $parser->inputString = readFile($s);
     }
   }
  elsif (my $f = $parser->inputFile)                                            # Source to be parsed is a file
   {$parser->inputString = readFile($f);
   }
  elsif ($parser->inputString) {}                                               # Source to be parsed is a string
  else                                                                          # Unknown string
   {confess "Supply a string or file to be parsed";
   }

  my $xmlParser = new XML::Parser(Style => 'Tree');                             # Extend Larry Wall's excellent L<XML> parser
  my $D = $parser->inputString;                                                 # String to be parsed
  my $d = $parser->lineNumbers ? &addLineNumbers($D) : $D;                      # Add line numbers if requested
     $d =~ s(&(?!(amp|gt|lt|quot|#x?\d+);)) (&amp;)gs;                          # Correct free floating ampersands

  my $x = eval {$xmlParser->parse($d)};                                         # Parse string
  $lastParseError = reduceParseErroMessage($@);                                 # Record any error

  if (!$x)                                                                      # Error in parse: write a message to STDERR and to a file if possible
   {my $f = $parser->inputFile ? "Source file is:\n".                           # Source details if a file
            $parser->inputFile."\n" : '';
    my $s = $lastParseError;
    eval {confess "Parse failed at:\n"};                                        # Stack of parse failure
    my $e = "$d\n$f\n$@\n$s\n";                                                 # Error
    # warn "Xml parse error: $e";                                               # Write a description of the error to STDERR before attempting to write to a file
    my $badFile  = $parser->errorsFile ||                                       # File name to write error analysis to
                   fullFileName(filePathExt(qw(zzzParseErrors out data)));
    unlink $badFile if -e $badFile;                                             # Remove existing errors file
    writeFile($badFile, $e);                                                    # Write a description of the error to the errorsFile
    confess "Xml parse error, see file:\n$badFile\n$s\n";                       # Complain helpfully if parse failed
   }

  $parser->tree($x);                                                            # Structure parse results as a tree

  if (my @c = @{$parser->content})                                              # Set additional attributes on root node
   {confess "No XML" if !@c;
    confess "More than one outer-most tag" if @c > 1;
    my $c = $c[0];
    $parser->tag        = $c->tag;
    $parser->attributes = $c->attributes;
    $parser->content    = $c->content;
    $parser->parent     = undef;
    $parser->indexNode;
   }

  $parser                                                                       # Parse details
 }

sub tree($$)                                                                    #P Build a tree representation of the parsed L<XML> which can be easily traversed to look for things.
 {my ($parent, $parse) = @_;                                                    # The parent node, the remaining parse
  while(@$parse)
   {my $tag  = shift @$parse;                                                   # Tag for node
    my $node = bless {parser=>$parent->parser};                                 # New node
    if ($tag eq cdata)
     {confess cdata.' tag encountered';                                         # We use this tag for text and so it cannot be used as a user tag in the document
     }
    elsif ($tag eq '0')                                                         # Text
     {my $s = shift @$parse;
      if ($s !~ /\A\s*\Z/)                                                      # Ignore entirely blank strings
       {$s = replaceSpecialChars($s);                                           # Restore special characters in the text
        $node->tag  = cdata;                                                    # Save text. ASSUMPTION: CDATA is not used as a tag anywhere.
        $node->text = $s;
        push @{$parent->content}, $node;                                        # Save on parents content list
       }
     }
    else                                                                        # Node
     {my $children   = shift @$parse;
      my $attributes = shift @$children;
      $node->tag = $tag;                                                        # Save tag
      $_ = replaceSpecialChars($_) for values %$attributes;                     # Restore in text with L<XML> special characters
      $node->attributes = $attributes;                                          # Save attributes
      push @{$parent->content}, $node;                                          # Save on parents content list
      $node->tree($children) if $children;                                      # Add nodes below this node
     }
   }
  $parent->indexNode;                                                           # Index this node
 }

sub addLineNumbers($)                                                           #P Add line numbers to the source
 {my ($string) = @_;                                                            # Source string
  my @s = split //, ($string =~ s(<!--linted:.*\Z) ()gsr);                      # Remove any trailing lint comments
  my $state = 0;                                                                # 0 - outside any tag, 1 - inside a tag
  my $Line  = 1;                                                                # Current tag start position
  my $Col   = 0;
  my $line  = 1;                                                                # Current position
  my $col   = 0;
  for my $i(keys @s)                                                            # Each input character
   {my $c = $s[$i];                                                             # current character
    ++$col;
    if ($c eq qq(\n))                                                           # Line/column number
     {++$line;
      $col = 0;
     }
    elsif ($state == 0)                                                         # Looking for the start of a tag
     {if ($c eq q(<) and ($s[$i+1]//'') =~ m(\A[^/?!]\Z)s)                      # Exclude non tags
       {($Line, $Col) = ($line, $col);                                          # Record start position
        $state = 1;                                                             # Looking for end
       }
     }
    elsif ($c eq q(>))                                                          # At end of tag
     {if ($state == 1)                                                          # Inside a tag
       {my $m = join '', ' xtrf="', $Line,  '.', $Col, ':',                     # Line number uses an increment
            ($line == $Line ? q() : ($line-$Line).q(.)), $col, '"';
        if (($s[$i-1]//'') ne q(/))                                             # Tag with content
         {$s[$i]   = $m.'>';
         }
        else                                                                    # Tag with no content
         {$s[$i-1] = $m.'/';
         }
        $state = 0;                                                             # Looking for the next tag
       }
     }
   }
  join '', @s                                                                   # New source string
 }

#D2 Node by Node                                                                # Construct a parse tree node by node.

sub newText($$)                                                                 # Create a new text node.
 {my (undef, $text) = @_;                                                       # Any reference to this package, content of new text node
  my $node = bless {};                                                          # New node
  $node->parser = $node;                                                        # Root node of this parse
  $node->tag    = cdata;                                                        # Text node
  $node->text   = $text;                                                        # Content of node
  $node                                                                         # Return new non text node
 }

sub newTag($$%)                                                                 # Create a new non text node.
 {my (undef, $command, %attributes) = @_;                                       # Any reference to this package, the tag for the node, attributes as a hash.
  my $node = bless {};                                                          # New node
  $node->parser = $node;                                                        # Root node of this parse
  $node->tag    = $command;                                                     # Tag for node
  $node->attributes = \%attributes;                                             # Attributes for node
  $node                                                                         # Return new node
 }

sub newTree($%)                                                                 # Create a new tree.
 {my ($command, %attributes) = @_;                                              # The name of the root node in the tree, attributes of the root node in the tree as a hash.
  &newTag(undef, @_)
 }

sub dupTag($@)                                                                  #C Create a new non text node by duplicating the tag of an existing node.
 {my ($node, @context) = @_;                                                    # Node, optional context
  return undef if @context and !$node->at(@context);                            # Not in specified context
  my $new = bless {};                                                           # New node
  $new->parser = $node->parser;                                                 # Root node of this parse
  $new->tag    = $node->tag;                                                    # Tag for node
  $new                                                                          # Return new node
 }

sub disconnectLeafNode($)                                                       #P Remove a leaf node from the parse tree and make it into its own parse tree.
 {my ($node) = @_;                                                              # Leaf node to disconnect.
  $node->parent = undef;                                                        # No parent
  $node->parser = $node;                                                        # Own parse tree
 }

sub reindexNode($)                                                              #P Index the children of a node so that we can access them by tag and number.
 {my ($node) = @_;                                                              # Node to index.
  delete $node->{indexes};                                                      # Delete the indexes
  for my $n($node->contents)                                                    # Index content
   {push @{$node->indexes->{$n->tag}}, $n;                                      # Indices to sub nodes
   }
 }

sub indexNode($)                                                                #P Merge multiple text segments and set parent and parser after changes to a node
 {my ($node) = @_;                                                              # Node to index.
  return unless keys @{$node->{content}};
  my @contents = @{$node->content};                                             # Contents of the node

#  eval {grep {$_->{tag} eq cdata} @contents};
#  $@ and confess "$@\n";

  if ((grep {$_->{tag} eq cdata} @contents) > 1)                                # Make parsing easier for the user by concatenating successive text nodes - NB: this statement has been optimized
   {my (@c, @t);                                                                # New content, pending intermediate texts list
    for(@contents)                                                              # Each node under the current node
     {if ($_->{tag} eq cdata)                                                   # Text node. NB: optimized
       {push @t, $_;                                                            # Add the text node to pending intermediate texts list
       }
      elsif (@t == 1)                                                           # Non text element encountered with one pending intermediate text
       {push @c, @t, $_;                                                        # Save the text node and the latest non text node
        @t = ();                                                                # Empty pending intermediate texts list
       }
      elsif (@t  > 1)                                                           # Non text element encountered with two or more pending intermediate texts
       {my $t = shift @t;                                                       # Reuse the first text node
        $t->text .= join '', map {$_->text} @t;                                 # Concatenate the remaining text nodes
        $_->disconnectLeafNode for @t;                                          # Disconnect the remain text nodes as they are no longer needed
        push @c, $t, $_;                                                        # Save the resulting text node and the latest non text node
        @t = ();                                                                # Empty pending intermediate texts list
       }
      else {push @c, $_}                                                        # Non text node encountered without immediately preceding text
     }

    if    (@t == 0) {}                                                          # No action required if no pending text at the end
    elsif (@t == 1) {push @c, @t}                                               # Just one text node
    else                                                                        # More than one text node - remove leading and trailing blank text nodes
     {my $t = shift @t;                                                         # Reuse the first text node
      $t->text .= join '', map {$_->text} @t;                                   # Concatenate the remaining text nodes
      $_->disconnectLeafNode for @t;                                            # Disconnect the remain text nodes as they are no longer needed
      push @c, $t;                                                              # Save resulting text element
     }

    @contents      =  @c;                                                       # The latest content of the node
    $node->content = \@c;                                                       # Node contents with concatenated text elements
   }

  my $parser = $node->parser;                                                   # Parser for this node
  for(@contents)                                                                # Index content
   {$_->{parent} = $node;                                                       # Point to parent
    $_->{parser} = $parser;                                                     # Point to parser
   }
 }

sub replaceSpecialChars($)                                                      #S Replace < > " & with &lt; &gt; &quot; &amp; Larry Wall's excellent L<Xml parser> unfortunately replaces &lt; &gt; &quot; &amp; etc. with their expansions in text by default and does not seem to provide an obvious way to stop this behavior, so we have to put them back again using this method.
 {my ($string) = @_;                                                            # String to be edited.
  $string =~ s/\&/&amp;/g;                                                      # At this point all & that prefix variables should have been expanded, so any that are left are are real &s which should be replaced with &amp;
  $string =~ s/\</&lt;/gr =~ s/\>/&gt;/gr =~ s/\"/&quot;/gr                     # Replace the special characters that we can replace.
 }

sub undoSpecialChars($)                                                         #S Reverse the results of calling L<replaceSpecialChars|/replaceSpecialChars>.
 {my ($string) = @_;                                                            # String to be edited.
  $string =~ s/&amp;/\&/g;                                                      # At this point all & that prefix variables should have been expanded, so any that are left are are real &s which should be replaced with &amp;
  $string =~ s/&lt;/\</gr =~ s/&gt;/\>/gr =~ s/&quot;/\"/gr                     # Replace the special characters that we can replace.
 }

#D2 Parse tree attributes                                                       # Attributes of a node in a parse tree. For instance the attributes associated with an L<XML> tag are held in the L<attributes|/attributes> attribute. It should not be necessary to use these attributes directly unless you are writing an extension to this module.  Otherwise you should probably use the methods documented in other sections to manipulate the parse tree as they offer a safer interface at a higher level.

genHash(__PACKAGE__,                                                            # L<XML> parser definition
  content=>[],                                                                  # Content of command: the nodes immediately below the specified B<$node> in the order in which they appeared in the source text, see also L</Contents>.
  numbers=>[],                                                                  # Nodes by number.
  data=>{},                                                                     # A hash added to the node for use by the programmer during transformations. The data in this hash will not be printed by any of the L<printed|/Print> methods and so can be used to add data to the L<parse|/parse> tree that will not be seen in any output L<XML> produced from the L<parse|/parse> tree.
  attributes=>{},                                                               # The attributes of the specified B<$node>, see also: L</Attributes>.  The frequently used attributes: class, id, href, outputclass can be accessed by an L<lvalueMethod> method as in: $node->id = 'c1'.
  conditions=>{},                                                               # Conditional strings attached to a node, see L</Conditions>.
  forestNumbers=>{},                                                            # Index to node by forest number as set by L<numberForest|/numberForest>.
  indexes=>{},                                                                  # Indexes to sub commands by tag in the order in which they appeared in the source text.
  labels=>{},                                                                   # The labels attached to a node to provide addressability from other nodes, see: L</Labels>.
  depthProfileLast=>undef,                                                      # The last known depth profile for this node as set by L<setDepthProfiles|/setDepthProfiles>.
  errorsFile=>undef,                                                            # Error listing file. Use this parameter to explicitly set the name of the file that will be used to write any L<parse|/parse> errors to. By default this file is named: B<zzzParseErrors/out.data>.
  inputFile=>undef,                                                             # Source file of the L<parse|/parse> if this is the L<parser|/parse> root node. Use this parameter to explicitly set the file to be L<parsed|/parse>.
  input=>undef,                                                                 # Source of the L<parse|/parse> if this is the L<parser|/parse> root node. Use this parameter to specify some input either as a string or as a file name for the L<parser|/parse> to convert into a L<parse|/parse> tree.
  inputString=>undef,                                                           # Source string of the L<parse|/parse> if this is the L<parser|/parse> root node. Use this parameter to explicitly set the string to be L<parsed|/parse>.
  lineNumbers=>undef,                                                           # If true then save the line number.column number at which tag starts and ends on the xtrf attribute of each node.
  numbering=>undef,                                                             # Last number used to number a node in this L<parse|/parse> tree.
  number=>undef,                                                                # Number of the specified B<$node>, see L<findByNumber|/findByNumber>.
  parent=>undef,                                                                # Parent node of the specified B<$node> or B<undef> if the L<parser|/parse> root node. See also L</Traversal> and L</Navigation>. Consider as read only.
  parser=>undef,                                                                # L<Parser|/parse> details: the root node of a tree is the L<parser|/parse> node for that tree. Consider as read only.
  representationLast=>undef,                                                    # The last representation set for this node by one of: L<setRepresentationAsTagsAndText|/setRepresentationAsTagsAndText>.
  tag=>undef,                                                                   # Tag name for the specified B<$node>, see also L</Traversal> and L</Navigation>. Consider as read only.
  text=>undef,                                                                  # Text of the specified B<$node> but only if it is a text node otherwise B<undef>, i.e. the tag is cdata() <=> L</isText> is true.
 );

#D2 Parse tree                                                                  # Construct a L<parse|/parse> tree from another L<parse|/parse> tree.

sub renew($@)                                                                   #C Returns a renewed copy of the L<parse|/parse> tree by first printing it and then reparsing it, optionally checking that the starting node is in a specified context: use this method if you have added nodes via the L</"Put as text"> methods and wish to traverse their L<parse|/parse> tree.\mReturns the starting node of the new L<parse|/parse> tree or B<undef> if the optional context constraint was supplied but not satisfied.
 {my ($node, @context) = @_;                                                    # Node to renew from, optional context
  return undef if @context and !$node->at(@context);                            # Not in specified context
  my $x = new($node->string);                                                   # Reconstruct parse tree from node
  $x->inputFile = $node->root->inputFile;                                       # Convey the input file name if present so that relative references can be resolved in the new parse tree
  $x                                                                            # Return new parse tree
 }

sub clone($)                                                                    #C Return a clone of the entire L<parse|/parse> tree which is created using the fast L<Storable::dclone> method. The L<parse|/parse> tree is cloned without converting it to string and reparsing it so this method will not L<renew|/renew> any nodes added L<as text|/Put as text>.\mReturns the starting node of the new L<parse|/parse> tree.
 {my ($tree) = @_;                                                              # Parse tree
  my $new = dclone($tree);
# $new->parent = undef;
  $new->parser = $new;
  $new->cut;
 }

sub equals($$)                                                                  #Y Return the first node if the two L<parse|/parse> trees have identical representations via L<string|/string>, else B<undef>.
 {my ($node1, $node2) = @_;                                                     # Parse tree 1, parse tree 2.
  $node1->string eq $node2->string ? $node1 : undef                             # Test
 }

sub equalsIgnoringAttributes($$@)                                               # Return the first node if the two L<parse|/parse> trees have identical representations via L<string|/string> if the specified attributes are ignored, else B<undef>.
 {my ($node1, $node2, @attributes) = @_;                                        # Parse tree 1, parse tree 2, attributes to ignore during comparison
  my $p = $node1->clone;                                                        # Clone the parse trees so we can modify them
  my $q = $node2->clone;
  for my $x($p, $q)                                                             # Remove specified attributes from clones of parse trees
   {$x->by(sub
     {$_->deleteAttrs(@attributes);
     })
   }
  $p->string eq $q->string ? $node1 : undef                                     # Compare the reduced parse trees
 }

sub normalizeWhiteSpace($)                                                      #PS Normalize white space, remove comments DOCTYPE and L<XML> processors from a string
 {my ($string) = @_;                                                            # String to normalize
  $string =~ s(<\?.*?\?>)     ( )gs;                                            # Processors
  $string =~ s(<!--.*?-->)    ( )gs;                                            # Comments
  $string =~ s(<!DOCTYPE.+?>) ( )gs;                                            # Doctype
  $string =~ s(\s+)           ( )gs;                                            # White space
  $string
 }

sub diff($$;$)                                                                  # Return () if the dense string representations of the two nodes are equal, else up to the first N (default 16) characters of the common prefix before the point of divergence and the remainder of the string representation of each node from the point of divergence. All <!-- ... --> comments are ignored during this comparison and all spans of white space are reduced to a single blank.
 {my ($first, $second, $N) = @_;                                                # First node, second node, maximum length of difference strings to return
  $first = new($first)   unless ref $first;                                     # Auto vivify the first node if necessary
  $second = new($second) unless ref $second;                                    # Auto vivify the second node if necessary
  $N //= 16;
  my $a = normalizeWhiteSpace(-s $first);                                       # Convert to normalized strings
  my $b = normalizeWhiteSpace(-s $second);
  return () if length($a) == length($b) and $a eq $b;                           # Equal strings

  my @a = split //, $a;                                                         # Split into characters
  my @b = split //, $b;
  my @c;                                                                        # Common prefix
  while(@a and @b and $a[0] eq $b[0])                                           # Remove equal prefix characters
   {push @c, shift @a; shift @b;                                                # Save common prefix
   }

  $#a = $N-1 if $N and @a > $N;                                                 # Truncate remainder if necessary
  $#b = $N-1 if $N and @b > $N;
  if ($N) {shift @c while @c > $N}

 (join ('', @c), join('', @a), join('', @b))                                    # Return common prefix and diverging strings
 }

sub save($$)                                                                    # Save a copy of the L<parse|/parse> tree to a file which can be L<restored|/restore> and return the saved node.  This method uses L<Storable> which is fast but produces large files that do not compress well.  Use L<writeCompressedFile|/writeCompressedFile> to produce smaller save files at the cost of more time.
 {my ($node, $file) = @_;                                                       # Parse tree, file.
  makePath($file);
  store $node, $file;
  $node
 }

sub restore($)                                                                  #SY Return a L<parse|/parse> tree from a copy saved in a file by L<save|/save>.
 {my ($file) = @_;                                                              # File
  -e $file or confess "Cannot restore from a non existent file:\n$file";
  retrieve $file
 }

sub expandIncludes($)                                                           #U Expand the includes mentioned in a L<parse|/parse> tree: any tag that ends in B<include> is assumed to be an include directive.  The file to be included is named on the B<href> keyword.  If the file to be included is a relative file name, i.e. it does not begin with B</> then this file is made absolute relative to the file from which this L<parse|/parse> tree was obtained.
 {my ($x) = @_;                                                                 # Parse tree
  $x->by(sub                                                                    # Look for include statements
   {my ($o) = @_;
    if ($o->at(qr(include\Z)))                                                  # Include statement
     {my $href = $o->attr(q(href));                                             # Remove dots and slashes from front of file name
      my $in   = $x->inputFile;
      my $file = absFromAbsPlusRel($in, $href);
      my $i = eval{Data::Edit::Xml::new($file)};                                # Parse the new source file

      if ($@)                                                                   # Report any errors encountered in expansion
       {confess "Failed to expand href $href in file $in\n$@";
       }

      $i->expandIncludes;                                                       # Rescan for any internal includes
      $o->replaceWith($i);                                                      # Replace include statement
     }
   });
 }

#D1 Print                                                                       # Create a string representation of the L<parse|/parse> tree with optional selection of nodes via L<conditions|/Conditions>.\mNormally use the methods in L<Pretty|/Pretty> to format the L<XML> in a readable yet reparseable manner; use L<Dense|/Dense> string to format the L<XML> densely in a reparseable manner; use the other methods to produce unreparseable strings conveniently formatted to assist various specialized operations such as debugging CDATA, using labels or creating tests. A number of the L<file test operators|/opString> can also be conveniently used to print L<parse|/parse> trees in these formats.

#D2 Pretty                                                                      # Pretty print the L<parse|/parse> tree.

sub prettyString($;$)                                                           #IU Return a readable string representing a node of a L<parse|/parse> tree and all the nodes below it. Or use L<-p|/opString> $node
 {my ($node, $depth) = @_;                                                      # Start node, optional depth.
  $depth //= 0;                                                                 # Start depth if none supplied

# return $node->text.($node->isLast ? q() : qq(\n)) if $node->isText;           # Add a new line after contiguous blocks of text to offset next node

  if ($node->isText)                                                            # Text block
   {my $t = $node->text =~ s( *\n) (\n)gsr;                                     # Collapse spaces before new line
    return $t if $node->isLast;                                                 # The following tag will deal with the new line
    return $t.qq(\n) unless $t =~ m(\n\Z)s;                                     # Separate from next tag with a new line
    return $t;                                                                  # Text already has a new line and so no additional separator required
   }

  my $t = $node->tag;                                                           # Not text so it has a tag
  my $content = $node->content;                                                 # Sub nodes
  my $space   = "  "x($depth//0);
  return $space.'<'.$t.$node->printAttributes.'/>'."\n" if !@$content;          # No sub nodes

  my $s = $space.'<'.$t.$node->printAttributes.'>'.                             # Has sub nodes
    ($node->first->isText ? '' : "\n");                                         # Continue text on the same line, otherwise place nodes on following lines
  $s .= $_->prettyString($depth+1) for @$content;                               # Recurse to get the sub content
  $s .= $node->last->isText ? ((grep{!$_->isText} @$content)                    # Continue text on the same line, otherwise place nodes on following lines
                            ? "\n$space": "") : $space;
  my $r = $s .  '</'.$t.'>'."\n";                                               # Closing tag
  return $r if $depth;                                                          # Return from sub tree
  $r =~ s(>\n( *[.,;:\)] *)) (>$1\n)gsr                                         # Overall result moves some punctuation through one new line to be closer to its tag
     =~ s(\n\s*\n) (\n)gsr                                                      # Remove blank lines
 }

sub prettyStringHtml2($$)                                                       #P Return a string of html representing a node of a L<parse|/parse> tree and all the nodes below it. Or use L<-p|/opString> $node
 {my ($node, $depth) = @_;                                                      # Start node, optional depth.
  $depth //= 0;                                                                 # Start depth if none supplied

  if ($node->isText)                                                            # Text block
   {my $t =                                                                     # Wrap text in span on one line
     qq(<span class="xmlText">)
     .nws($node->text)
     .qq(</span>);
    return $t;                                                                  # Text already has a new line and so no additional separator required
   }

  my $t       = qq(<span class="xmlTag">).$node->tag.q(</span>);                # Not text so it has a tag
  my $content = $node->content;                                                 # Sub nodes

  my $space   = qq(<span class="xmlLineStartTag">)                              # Space before text
   .("&nbsp;"x(4*($depth//0)))
   .qq(</span>);

  return $space                                                                 # No sub nodes
   .q(<span class="xmlLt">&lt;</span>)
   .$t
   .$node->printAttributesHtml
   .q(<span class="xmlSlashGt">/&gt;</span>)
   ."\n" if !@$content;

  my $s = $space                                                                # Has sub nodes
   .q(<span class="xmlLt">&lt;</span>)
   .$t
   .$node->printAttributesHtml
   .q(<span class="xmlGt">&gt;</span>)
   .($node->first->isText ? '' : "\n");                                         # Continue text on the same line, otherwise place nodes on following lines

  $s .= $_->prettyStringHtml2($depth+1) for @$content;                          # Recurse to get the sub content

  $s .= $node->last->isText ? ((grep{!$_->isText} @$content)                    # Continue text on the same line, otherwise place nodes on following lines
                            ? "\n$space": "") : $space;

  my $r = $s                                                                    # Closing tag
   .q(<span class="xmlLtSlash">&lt;/</span>)
   .$t
   .q(<span class="xmlGt">&gt;</span>)
   ."\n";

  return $r if $depth;                                                          # Return from sub tree

  my $h = join "\n", map {qq(<div class="xmlLine">$_</div>)} split m/\n/, $r;   # Wrap div around each line
  qq($h\n)
 }

sub prettyStringHtml($@)                                                        # Return a string of L<html> representing a node of a L<parse|/parse> tree and all the nodes below it if the node is in the specified context.
 {my ($node, @context) = @_;                                                    # Node, optional context
  return undef if @context and !$node->at(@context);                            # Check optional context
  prettyStringHtml2($node, 0);                                                  # Print as html
 }

sub prettyStringDitaHeaders($)                                                  #U Return a readable string representing the L<parse|/parse> tree below the specified B<$node> with appropriate headers. Or use L<-x|/opString> $node
 {my ($node) = @_;                                                              # Start node
# cluck "Please use: ditaPrettyPrintWithHeaders...redirecting";
  $node->ditaPrettyPrintWithHeaders
 }

sub prettyStringNumbered($;$)                                                   #U Return a readable string representing a node of a L<parse|/parse> tree and all the nodes below it with a L<number|/number> attached to each tag. The node numbers can then be used as described in L<Order|/Order> to monitor changes to the L<parse|/parse> tree.
 {my ($node, $depth) = @_;                                                      # Start node, optional depth.
  $depth //= 0;                                                                 # Start depth if none supplied

  my $N = $node->number;                                                        # Node number if present

  if ($node->isText)                                                            # Text node
   {my $n = $node->next;
    my $s = !defined($n) || $n->isText ? '' : "\n";                             # Add a new line after contiguous blocks of text to offset next node
    return ($N ? "($N)" : '').$node->text.$s;                                   # Number text
   }

  my $t = $node->tag;                                                           # Number tag in a way which allows us to skip between start and end tags in L<Geany|http://www.geany.org> using the ctrl+up and ctrl+down arrows
  my $i = $N && !defined($node->id) ? " id=\"$N\""  : '';                       # Use id to hold tag
  my $content = $node->content;                                                 # Sub nodes
  my $space   = "  "x($depth//0);
  return $space.'<'.$t.$i.$node->printAttributes.'/>'."\n" if !@$content;       # No sub nodes

  my $s = $space.'<'.$t.$i.$node->printAttributes.'>'.                          # Has sub nodes
    ($node->first->isText ? '' : "\n");                                         # Continue text on the same line, otherwise place nodes on following lines
  $s .= $_->prettyStringNumbered($depth+1) for @$content;                       # Recurse to get the sub content
  $s .= $node->last->isText ? ((grep{!$_->isText} @$content)                    # Continue text on the same line, otherwise place nodes on following lines
                            ? "\n$space": "") : $space;
  my $r = $s .  '</'.$t.'>'."\n";                                               # Closing tag
  return $r if $depth;                                                          # Return from sub tree
  $r =~ s(>\n( *[.,;:\)] *)) (>$1\n)gsr                                         # Overall result moves some punctuation through one new line to be closer to its tag
 }

sub prettyStringCDATA($;$)                                                      #U Return a readable string representing a node of a L<parse|/parse> tree and all the nodes below it with the text fields wrapped with <CDATA>...</CDATA>.
 {my ($node, $depth) = @_;                                                      # Start node, optional depth.
  $depth //= 0;                                                                 # Start depth if none supplied

  if ($node->isText)                                                            # Text node
   {my $n = $node->next;
    my $s = !defined($n) || $n->isText ? '' : "\n";                             # Add a new line after contiguous blocks of text to offset next node
    return '<'.cdata.'>'.$node->text.'</'.cdata.'>'.$s;
   }

  my $t = $node->tag;                                                           # Not text so it has a tag
  my $content = $node->content;                                                 # Sub nodes
  my $space   = "  "x($depth//0);
  return $space.'<'.$t.$node->printAttributes.'/>'."\n" if !@$content;          # No sub nodes

  my $s = $space.'<'.$t.$node->printAttributes.'>'.                             # Has sub nodes
    ($node->first->isText ? '' : "\n");                                         # Continue text on the same line, otherwise place nodes on following lines
  $s .= $_->prettyStringCDATA($depth+2) for @$content;                          # Recurse to get the sub content
  $s .= $node->last->isText ? ((grep{!$_->isText} @$content)                    # Continue text on the same line, otherwise place nodes on following lines
                            ? "\n$space": "") : $space;
  my $r = $s .  '</'.$t.'>'."\n";                                               # Closing tag
  return $r if $depth;                                                          # Return from sub tree
  $r =~ s(>\n( *[.,;:\)] *)) (>$1\n)gsr                                         # Overall result moves some punctuation through one new line to be closer to its tag
 }

sub prettyStringEnd($)                                                          #PU Return a readable string representing a node of a L<parse|/parse> tree and all the nodes below it as a here document
 {my ($node) = @_;                                                              # Start node
  my $s = -p $node;                                                             # Pretty string representation
'  ok -p $x eq <<END;'. "\n".(-p $node). "\nEND"                                # Here document
 }

sub prettyStringContent($)                                                      #U Return a readable string representing all the nodes below a node of a L<parse|/parse> tree.
 {my ($node) = @_;                                                              # Start node.
  my $s = '';
  $s .= $_->prettyString for $node->contents;                                   # Recurse to get the sub content
  $s
 }

sub prettyStringContentNumbered($)                                              #U Return a readable string representing all the nodes below a node of a L<parse|/parse> tree with numbering added.
 {my ($node) = @_;                                                              # Start node.
  my $s = '';
  $s .= $_->prettyStringNumbered for $node->contents;                           # Recurse to get the sub content
  $s
 }

sub xmlHeader($)                                                                #S Add the standard L<XML> header to a string
 {my ($string) = @_;                                                            # String to which a standard L<XML> header should be prefixed
  <<END
<?xml version="1.0" encoding="UTF-8"?>
$string
END
 }

#D2 Html/Json                                                                   # Represent the L<parse|/parse> tree using html or Json

sub htmlTables($)                                                               # Return a string of html representing a L<parse|/parse> tree.
 {my ($node) = @_;                                                              # Start node of parse tree

  my $bgColor = sub                                                             # Create a random background color
   {join q(), q(bgcolor="#), (map {sprintf("%02x", $_)} @_), q(");
   };

  my $idNumber = 0;
  my $id       = sub                                                            # Create the next id number
   {++$idNumber
   };

  my @actions = qw(dup rename cut paste unwrap wrap wrapContent);

  my @nodes;
  my %count;
  my @depth;
  my @path;
  my $height = 0;

  $node->down(sub                                                               # Map xml
   {my ($n) = @_;
    return if $n->isFirstText;
    push @nodes, $n;
    $count{$_}++ for @_;
    push @path, join ' ', $n->path;
    my $h = scalar @_;
    push @depth, $h;
    $height = max($height, $h);
   });

  my @h = q(<html>);                                                            # Generated html

  push @h, qq(<table cellspacing="0" cellpadding="2">);                         # Create table
  for my $i(keys @nodes)
   {my $n          = $nodes[$i];
    my $tag        = -t $n;
    my $line       = $i + 1;
    my $rowspan    = $count{$n};
    my $colspan    = $height-$depth[$i]+1;
    my @color      =  map {int(rand()*255)} 1..3;
    my $color      = &$bgColor(map {127+$_/2} @color);
    my $colorLight = &$bgColor(map {191+$_/4} @color);
    my $click = join '',
     q/onclick="mimClick(event)" path="/, $path[$i], q/"/;

    my ($i1, $i2, $i3, $i4, $i5, $i6, $i7) =
      map
       {qq( onmouseover="mimMouseOver(event)" onmouseout="mimMouseOut(event)").
        qq( onkeydown="mimKeyDown(event)")
       }
      1..7;
    push @h, qq(<tr><td $i1 $color $click align="right">$line</td>);
    push @h,     qq(<td $i2 $color $click rowspan="$rowspan" width="10px"></td>);
    push @h,     qq(<td $i3 $color $click colspan="$colspan" contentEditable="true">$tag</td>) if $colspan;

    if (my $t = $n->isText)                                                     # Text element
     {push @h, qq(<td $i4 contenteditable="true" $colorLight>).$t->text.q(</td>);
     }
    elsif (my $T = $n->firstText)                                               # Text element first below non text element
     {push @h, qq(<td $i5 contenteditable="true" $colorLight>).$T->text.q(</td>);
     }
    elsif ($tag =~ m(\A(b|cmd|info|li|p)\Z)s)
     {push @h, qq(<td $i6 contenteditable="true" $colorLight> </td>);
     }
    else
     {push @h, qq(<td $i7></td>);
     }
   }
  push @h, qq(</table>);                                                        # End of table

  push @h, <<END;
<script>

function say()                                                                  // Say strings
 {console.log(Array.from(arguments).map(a=>a.toString()).join(''))
 }

var mimMouseOverLastColor;

function mimMouseOver(event)
 {const target = event.target;
  mimMouseOverLastColor  = target.style["background-color"];
  target.style["background-color"] = "white";
 }

function mimMouseOut(event)
 {const target = event.target;
  target.style["background-color"] = mimMouseOverLastColor;
 }

function mimClick(event)
 {console.log(event);
 }

function mimKeyDown(event)
 {const target = event.target;
  console.log(event.key+ " "+event.code);
  event.preventDefault();

  if      (event.code == "KeyD")
   {alert("duplicate path "+target.getAttribute("path"));
   }
  else if (event.code == "KeyR")
   {const response = prompt("Enter new name");
    target.innerText = response;
   }
  else if (event.code == "KeyU")
   {alert("unwrap path "+target.getAttribute("path"));
   }
 }

</script>
</html>
END

  join '', map {qq($_\n)} @h
 }

sub jsonString2($)                                                              #P Return a Json representation of a parse tree
 {my ($node) = @_;                                                              # Start node of parse tree

  my $n = {tag=>$node->tag,                                                     # New node
   (keys(%{$node->attributes}) ? (attributes => $node->attributes) : ()),
   ($node->isText              ? (text       => $node->text)       : ())};

  for my $c($node->contents)                                                    # Add content, hoisting any initial text node to the text field.
   {push @{$n->{contents}}, $c->jsonString2;
   }

  $n
 }

sub jsonString($)                                                               #U Return a Json representation of a parse tree
 {my ($node) = @_;                                                              # Start node of parse tree
  encodeJson($node->jsonString2) =~ s(([\}\]],)) ($1\n)gsr;                     # Encode Perl to Json with some line breaks added.
 }

sub xmlToJson($;$)                                                              #S Return a Json string representing valid L<XML> contained in a file or string, optionally double escaping escape characters.
 {my ($xml, $escape) = @_;                                                      # File name or string of valid html, double escape the escaped characters if true
  ref($xml) and confess "Expected a string or a file name";
  my $j = jsonString(new($xml));                                                # Json string
  $j =~ s(\\) (\\\\)gs if $escape;                                              # Escape if requested
  $j
 }

sub jsonToXml2($)                                                               #P Convert a json string to an L<XML> parse tree
 {my ($node) = @_;                                                              # Start node of parse tree
  my $a = $$node{attributes};                                                   # Attributes
  my $n = bless {tag=>$$node{tag},(keys(%$a) ? (attributes => $a) : ())};       # Tag and attributes
     $$n{text} = trim($$node{text}) if $$node{text};                            # Avoid endless blank line expansion of text
  push @{$$n{content}}, &jsonToXml2($_) for @{$$node{contents}};                # Add content
  $n
 }

sub jsonToXml($)                                                                # Convert a json string representing an L<XML> parse tree into an L<XML> parse tree
 {my ($json) = @_;                                                              # Json string
  my $p = decodeJson($json);                                                    # Json represented as Perl
  my $x = jsonToXml2($p);                                                       # Parse tree - enough to print
  my $s = string($x);                                                           # Parse tree as string
  new($s);                                                                      # Recreate full parse tree from string
 }

#D2 Dense                                                                       # Print the L<parse|/parse> tree densely for reuse by computers rather than humans.

sub string($)                                                                   #U Return a dense string representing a node of a L<parse|/parse> tree and all the nodes below it. Or use L<-s|/opString> B<$node>.
 {my ($node) = @_;                                                              # Start node.
  return $node->{text} if $node->{tag} eq cdata;                                # Text node
  my $content = $node->content;                                                 # Sub nodes
  my $attr    = keys %{$node->{attributes}};                                    # Number of attributes

  return  '<'.$node->{tag}.                       '/>'
    if !@$content and !keys %{$node->{attributes}};                             # No sub nodes or attributes

  return  '<'.$node->{tag}.$node->printAttributes.'/>'
    if !@$content;                                                              # No sub nodes

  join '', '<', $node->{tag}, $node->printAttributes, '>',                      # Has sub nodes
   (map {$_->{tag} eq cdata ? $_->{text} : $_->string} @$content),              # Recurse to get the sub content
           '</', $node->{tag}, '>'
 }

sub stringAsMd5Sum($)                                                           #U Return the L<md5> of the dense L<string|/string> representing a node of a L<parse|/parse> tree minus its L<id> and all the nodes below it. Or use L<-g|/opString> B<$node>. The id of the top most node is not included in the md5sum to equate parse trees that would otherwise only differ by the arbitrary root node id value.
 {my ($node) = @_;                                                              # Node.
  $node->id = undef if my $i = $node->id;                                       # Save id
  my $md5 = stringMd5Sum($node->string);                                        # Md5 sum of string content minus id. The various printing methods will of course all produce different md5 sums but the md5 sum is big enough to accommodate the variations without collisions.
  $node->id = $i if $i;                                                         # Restore id if present
  $md5                                                                          # Return md5 sum
 }

sub stringQuoted($)                                                             #U Return a quoted string representing a L<parse|/parse> tree a node of a L<parse|/parse> tree and all the nodes below it. Or use L<-o|/opString> B<$node>.
 {my ($node) = @_;                                                              # Start node
  "'".$node->string."'"
 }

sub stringReplacingIdsWithLabels($)                                             # Return a string representing the specified L<parse|/parse> tree with the id attribute of each node set to the L<Labels|/Labels> attached to each node.
 {my ($node) = @_;                                                              # Start node.
  return $node->text if $node->isText;                                          # Text node
  my $t = $node->tag;                                                           # Not text so it has a tag
  my $content = $node->content;                                                 # Sub nodes
  return '<'.$t.$node->printAttributesReplacingIdsWithLabels.'/>' if !@$content;# No sub nodes

  my $s = '<'.$t.$node->printAttributesReplacingIdsWithLabels.'>';              # Has sub nodes
  $s .= $_->stringReplacingIdsWithLabels for @$content;                         # Recurse to get the sub content
  return $s.'</'.$t.'>';
 }

sub stringExtendingIdsWithLabels($)                                             # Return a string representing the specified L<parse|/parse> tree with the id attribute of each node extended by the L<Labels|/Labels> attached to each node.
 {my ($node) = @_;                                                              # Start node.
  return $node->text if $node->isText;                                          # Text node
  my $t = $node->tag;                                                           # Not text so it has a tag
  my $content = $node->content;                                                 # Sub nodes
  return '<'.$t.$node->printAttributesExtendingIdsWithLabels.'/>' if !@$content;# No sub nodes

  my $s = '<'.$t.$node->printAttributesExtendingIdsWithLabels.'>';              # Has sub nodes
  $s .= $_->stringExtendingIdsWithLabels for @$content;                         # Recurse to get the sub content
  return $s.'</'.$t.'>';
 }

sub stringContent($)                                                            #U Return a string representing all the nodes below a node of a L<parse|/parse> tree.
 {my ($node) = @_;                                                              # Start node.
  my $s = '';
  $s .= $_->string for $node->contents;                                         # Recurse to get the sub content
  $s
 }

sub stringContentOrText($)                                                      #U Return a string representing all the nodes below a node of a L<parse|/parse> tree or the text of the current node if it is a text node.
 {my ($node) = @_;                                                              # Start node.
  return $node->text if $node->isText;
  stringContent($node);
 }

sub stringNode($)                                                               #U Return a string representing the specified B<$node> showing the attributes, labels and node number.
 {my ($node) = @_;                                                              # Node.
  my $s = '';

  if ($node->isText)                                                            # Text node
   {$s = 'CDATA='.$node->text;
   }
  else                                                                          # Non text node
   {$s = $node->tag.$node->printAttributes;
   }

  if (my $n = $node->number)                                                    # Node number if present
   {$s .= "($n)"
   }

  if (my @l = $node->getLabels)                                                 # Labels
   {$s .= " ${_}:".$l[$_] for keys @l;
   }

  $s
 }

sub stringTagsAndText($)                                                        #U Return a string showing just the tags and text at and below a specified B<$node>.
 {my ($node) = @_;                                                              # Node.
  my @s;                                                                        # String or each node
  for my $n($node->byList)                                                      # Each node
   {push @s, $n->isText ? trim($n->text) : $n->tag;                             # Representation of node
   }
  return @s if wantarray;                                                       # Representations as array
  join ' ', @s                                                                  # Representation as string
 }

sub stringText($)                                                               #U Return a string showing just the text of the text nodes (separated by blanks) at and below a specified B<$node>.
 {my ($node) = @_;                                                              # Node.
  my @s;                                                                        # String or each node
  for my $n($node->byList)                                                      # Each node
   {push @s, trim($n->text) if $n->isText;                                      # Text of text nodes
   }
  return @s if wantarray;                                                       # Representations as array
  join ' ', @s                                                                  # Representation as string
 }

sub setRepresentationAsTagsAndText($)                                           #U Sets the L<representationLast|/representationLast> for every node in the specified B<$tree> via L<stringTagsAndText|/stringTagsAndText>.
 {my ($tree) = @_;                                                              # Tree of nodes.
  $tree->by(sub                                                                 # Each node
   {$_->representationLast = $_->stringTagsAndText;                             # Set representationLast for each node
   });
  $tree
 }

sub setRepresentationAsText($)                                                  #U Sets the L<representationLast|/representationLast> for every node in the specified B<$tree> via L<stringText|/stringText>.
 {my ($tree) = @_;                                                              # Tree of nodes.
  $tree->by(sub                                                                 # Each node
   {$_->representationLast = $_->stringText;                                    # Set representationLast for each node
   });
  $tree
 }

sub matchNodesByRepresentation($)                                               #U Creates a hash of arrays of nodes that have the same representation in the specified B<$tree>. Set L<representation|/representationLast> for each node in the tree before calling this method.
 {my ($tree) = @_;                                                              # Tree to examine
  my %map;                                                                      # Map of arrays of nodes that have the same representation.
  for my $n($tree->byList)                                                      # Each node
   {push @{$map{$n->representationLast}}, $n;                                   # Classify node by representation
   }
  \%map                                                                         # Return results
 }

#D2 Conditions                                                                  # Print a subset of the L<parse|/parse> tree determined by the conditions attached to it.

sub stringWithConditions($@)                                                    # Return a string representing the specified B<$node> of a L<parse|/parse> tree and all the nodes below it subject to conditions to select or reject some nodes.
 {my ($node, @conditions) = @_;                                                 # Start node, conditions to be regarded as in effect.
  return $node->text if $node->isText;                                          # Text node
  my %c = %{$node->conditions};                                                 # Process conditions if any for this node
  return '' if keys %c and @conditions and !grep {$c{$_}} @conditions;          # Return if conditions are in effect and no conditions match
  my $t = $node->tag;                                                           # Not text so it has a tag
  my $content = $node->content;                                                 # Sub nodes

  my $s = ''; $s .= $_->stringWithConditions(@conditions) for @$content;        # Recurse to get the sub content
  return '<'.$t.$node->printAttributes.'/>' if !@$content or $s =~ /\A\s*\Z/;   # No sub nodes or none selected
  '<'.$t.$node->printAttributes.'>'.$s.'</'.$t.'>';                             # Has sub nodes
 }

sub condition($$@)                                                              #CY  Return the B<$node> if it has the specified B<$condition> and is in the optional B<@context>, else return B<undef>
 {my ($node, $condition, @context) = @_;                                        # Node, condition to check, optional context
  return undef if @context and !$node->at(@context);                            # Check optional context
  $node->conditions->{$condition} ? $node : undef                               # Return node if it has the specified condition, else B<undef>
 }

sub anyCondition($@)                                                            #Y Return the B<$node> if it has any of the specified B<@conditions>, else return B<undef>
 {my ($node, @conditions) = @_;                                                 # Node, conditions to check
  $node->conditions->{$_} ? return $node : undef for @conditions;               # Return node if any of the specified conditions are present
  undef                                                                         # No conditions present
 }

sub allConditions($@)                                                           #Y  Return the B<$node> if it has all of the specified B<@conditions>, else return B<undef>
 {my ($node, @conditions) = @_;                                                 # Node, conditions to check
  !$node->conditions->{$_} ? return undef : undef for @conditions;              # Return node if any of the specified conditions are missing
  $node                                                                         # All conditions present
 }

sub addConditions($@)                                                           # Given a B<$node> add the specified B<@conditions> and return the node.
 {my ($node, @conditions) = @_;                                                 # Node, conditions to add.
  $node->conditions->{$_}++ for @conditions;
  $node
 }

sub deleteConditions($@)                                                        # Given a B<$node> delete any B<@conditions> applied to the $node and return the $node.
 {my ($node, @conditions) = @_;                                                 # Node, conditions to add.
  delete $node->conditions->{$_} for @conditions;
  $node
 }

sub listConditions($)                                                           # Return a list of conditions applied to a B<$node>.
 {my ($node) = @_;                                                              # Node.
  sort keys %{$node->conditions}
 }

#D1 Attributes                                                                  # Get or set the attributes of nodes in the L<parse|/parse> tree. L<Well Known Attributes|/Well Known Attributes>  can be set directly via L<lvalueMethod> B<sub>s. To set or get the values of other attributes use L<Get or Set Attributes|/Get or Set Attributes>. To delete or rename attributes see: L<Other Operations on Attributes|/Other Operations on Attributes>.

#D2 Well Known Attributes                                                       # Get or set these node attributes via L<lvalueMethod> B<sub>s as in:\m  $x->href = "#ref";

if (0) {
genHash(__PACKAGE__,                                                            # Well known attributes useful in respect to L<XML> that conforms to the L<Dita> standard.
  audience    => undef,                                                         # Attribute B<audience> for a node as an L<lvalueMethod> B<sub>.    Use B<audienceX()> to return B<q()> rather than B<undef>.
  class       => undef,                                                         # Attribute B<class> for a node as an L<lvalueMethod> B<sub>.       Use B<classX()> to return B<q()> rather than B<undef>.
  guid        => undef,                                                         # Attribute B<guid> for a node as an L<lvalueMethod> B<sub>.        Use B<guidX()> to return B<q()> rather than B<undef>.
  href        => undef,                                                         # Attribute B<href> for a node as an L<lvalueMethod> B<sub>.        Use B<hrefX()> to return B<q()> rather than B<undef>.
  id          => undef,                                                         # Attribute B<id> for a node as an L<lvalueMethod> B<sub>.          Use B<idX()> to return B<q()> rather than B<undef>.
  lang        => undef,                                                         # Attribute B<lang> for a node as an L<lvalueMethod> B<sub>.        Use B<langX()> to return B<q()> rather than B<undef>.
  navtitle    => undef,                                                         # Attribute B<navtitle> for a node as an L<lvalueMethod> B<sub>.    Use B<navtitleX()> to return B<q()> rather than B<undef>.
  otherprops  => undef,                                                         # Attribute B<otherprops> for a node as an L<lvalueMethod> B<sub>.  Use B<otherpropsX()> to return B<q()> rather than B<undef>.
  outputclass => undef,                                                         # Attribute B<outputclass> for a node as an L<lvalueMethod> B<sub>. Use B<outputclassX()> to return B<q()> rather than B<undef>.
  props       => undef,                                                         # Attribute B<props> for a node as an L<lvalueMethod> B<sub>.       Use B<propsX()> to return B<q()> rather than B<undef>.
  style       => undef,                                                         # Attribute B<style> for a node as an L<lvalueMethod> B<sub>.       Use B<styleX()> to return B<q()> rather than B<undef>.
  type        => undef,                                                         # Attribute B<type> for a node as an L<lvalueMethod> B<sub>.        Use B<typeX()> to return B<q()> rather than B<undef>.
  xtrc        => undef,                                                         # Attribute B<xtrc> for a node as an L<lvalueMethod> B<sub>.        Use B<classX()> to return B<q()> rather than B<undef>.
  xtrf        => undef,                                                         # Attribute B<xtrf> for a node as an L<lvalueMethod> B<sub>.        Use B<classX()> to return B<q()> rather than B<undef>.
 );
}

BEGIN                                                                           # The above documents the attributes created below using the L<attr|_/attr> method.
 {for my $a(qw(audience class guid href id lang navtitle),                      # Return well known attributes as an assignable value
            qw(otherprops outputclass props style type xtrf))
   {eval 'sub '.$a. '($) :lvalue {&attr($_[0], q('.$a.'))}'.
         'sub '.$a.'X($)         {&attr($_[0], q('.$a.')) // q()}';
    $@ and confess "Cannot create well known attribute $a\n$@";

    my $A = ucfirst $a;
    for my $c(qw(at first next prev last))                                      # Commands to attach to attributes
     {my $cmd = 'sub '.$c.$A.'($)'.
      ' {my ($node, $attrValue, @context) = @_;'.
      '  my $A = $node->attr(q('.$a.'));'.
      '  return $node if $node->'.$c.'(@context) and $A and $A eq $attrValue;'.
      ' undef;}';
      eval $cmd;
      $@ and confess "Cannot create well known attribute $a\n$@";
     }
   }
 }

#D2 Get or Set Attributes                                                       # Get or set the attributes of nodes.
sub attr($$) :lvalue                                                            # Return the value of an attribute of the current node as an L<lvalueMethod> B<sub>.
 {my ($node, $attribute) = @_;                                                  # Node in parse tree, attribute name.
  $node->attributes->{$attribute}
 }

sub attrX($$)                                                                   #I Return the value of the specified B<$attribute> of the specified B<$node> or B<q()> if the B<$node> does not have such an attribute.
 {my ($node, $attribute) = @_;                                                  # Node in parse tree, attribute name.
  $node->attributes->{$attribute} // ''
 }

sub set($%)                                                                     #U Set the values of some attributes in a node and return the node. Identical in effect to L<setAttr|/setAttr>.
 {my ($node, %values) = @_;                                                     # Node in parse tree, (attribute name=>new value)*
  s/["<>]/ /gs for grep {$_} values %values;                                    # We cannot have these characters in an attribute
  $node->attributes->{$_} = $values{$_} for keys %values;                       # Set attributes
  $node
 }
#a deleteAttrs
#b <b/>
#c at b
#c set id bb class cc href hh
#d Set some attributes.

sub addAttr($%)                                                                 #U Check the specified B<$node> for the specified B<%attributes> and add any that are not already set.  Returns the current node.
 {my ($node, %values) = @_;                                                     # Node in parse tree, (attribute name=>new value)*
  s/["<>]/ /gs for grep {$_} values %values;                                    # We cannot have these characters in an attribute
  $node->attributes->{$_} //= $values{$_} for keys %values;                     # Set attributes
  $node
 }

sub setAttr($%)                                                                 #U Set the values of some attributes in a node and return the node. Identical in effect to L<set|/set>.
 {my ($node, %values) = @_;                                                     # Node in parse tree, (attribute name=>new value)*
  s/["<>]/ /gs for grep {$_} values %values;                                    # We cannot have these characters in an attribute
  $node->attributes->{$_} = $values{$_} for keys %values;                       # Set attributes
  $node
 }

#D2 Other Operations on Attributes                                              # Perform operations other than get or set on the attributes of a node
sub attrs($@)                                                                   # Return the values of the specified attributes of the current node as a list
 {my ($node, @attributes) = @_;                                                 # Node in parse tree, attribute names.
  my @v;
  my $a = $node->attributes;
  push @v, $a->{$_} for @attributes;
  @v
 }

sub attrCount($@)                                                               #U Return the number of attributes in the specified B<$node>, optionally ignoring the specified names from the count.
 {my ($node, @exclude) = @_;                                                    # Node in parse tree, optional attribute names to exclude from the count.
  my $a = $node->attributes;                                                    # Attributes
  return scalar grep {defined $a->{$_}} keys %$a if @exclude == 0;              # Count all attributes
  my %e = map{$_=>1} @exclude;                                                  # Hash of attributes to be excluded
  scalar grep {defined $a->{$_} and !$e{$_}} keys %$a;                          # Count attributes that are not excluded
 }

sub getAttrs($)                                                                 # Return a sorted list of all the attributes on the specified B<$node>.
 {my ($node) = @_;                                                              # Node in parse tree.
  my $a = $node->attributes;                                                    # Attributes
  grep {defined $a->{$_}} sort keys %$a                                         # Attributes
 }

sub deleteAttr($$;$)                                                            #U Delete the named attribute in the specified B<$node>, optionally check its value first, returning the value of the attribute or B<undef> if the attribute does not exist on this node.
 {my ($node, $attr, $value) = @_;                                               # Node, attribute name, optional attribute value to check first.
  @_ > 3 and confess
   "A maximum of three parameters are allowed or try deleteAttrs";              # Deflect to deleteAttrs if too many parameters
  my $a = $node->attributes;                                                    # Attributes hash
  if (@_ == 3)
   {if (defined($a->{$attr}) and $a->{$attr} eq $value)                         # Delete user key if it has the right value
     {delete $a->{$attr}; return $value;                                        # Delete user key if it has the right value
     }
    return undef;                                                               # Attribute has wrong value so return undef
   }
  else
   {return delete $a->{$attr};                                                  # Delete user key unconditionally
   }
 }
#a
#b <b class="help"/><b class="ref"/>
#c deleteAttr class help
#d Delete an attribute if has a specific value.

sub deleteAttrs($@)                                                             #U Delete the specified attributes of the specified B<$node> without checking their values and return the node.
 {my ($node, @attrs) = @_;                                                      # Node, Names of the attributes to delete
  my $a = $node->attributes;                                                    # Attributes hash
  delete $a->{$_} for @attrs;
  $node
 }
#a deleteAttr
#b <b id="b"/><c id="c" class="c" href="c"/>
#c at c
#c deleteAttrs id class
#d Delete the named attributes.
sub deleteAttrsInTree($@)                                                       #U Delete the specified attributes of the specified B<$node> and all the nodes under it and return the specified B<$node>.
 {my ($node, @attrs) = @_;                                                      # Node, Names of the attributes to delete
  $node->by(sub                                                                 # Traverse the parse tree
   {my ($o) = @_;
    my $a = $o->attributes;                                                     # Attributes hash
    delete $a->{$_} for @attrs;                                                 # Delete the specified attributes
   });
  $node
 }

sub attrsNone($@)                                                               #CU Check that the specified B<$node> has no attributes. Return the specified $node if no attributes were found else B<undef>. Invented by MfM.
 {my ($node, @context) = @_;                                                    # Node, optional context
  return undef if @context and !$node->at(@context);                            # Check optional context
  my $a = $node->attributes;                                                    # Attributes hash
  keys(%$a) ? undef : $node                                                     # Fail if there are attributes
 }
#a attrs
#b <b/><c id="cc"/>
#c attrsNone b a
#c set id had_no_attributes
#d Continue if there are no attributes present.

sub deleteAttrValueAtInTree($$$@)                                               #C Delete all instances of the specified B<$attribute> with the specified B<$value> in the specified B<@context> in the specified B<$tree> and return the modified B<$tree>. An undefined B<$value> will cause the attribute to be deleted without first confirming its value. An empty context will remove the attribute from every node in the B<$tree>.
 {my ($tree, $attribute, $value, @context) = @_;                                # Tree, attribute name, attribute value or B<undef> for all values, optional context
  $tree->by(sub                                                                 # Traverse the parse tree
   {my ($o) = @_;
    return if @context and !$o->at(@context);                                   # Not in specified context
    $o->deleteAttr($attribute, $value);                                         # Delete the named attribute if its value matches
   });
  $tree                                                                         # Return the modified parse tree
 }

sub renameAttr($$$@)                                                            #CU Rename attribute B<$old> to B<$new> in the specified B<$node> with optional context B<@context> regardless of whether attribute B<$new> already exists or not and return the B<$node>. To prevent inadvertent changes to an existing attribute use L<changeAttr|/changeAttr>.
 {my ($node, $old, $new, @context) = @_;                                        # Node, existing attribute name, new attribute name, optional context.
  return undef if @context and !$node->at(@context);                            # Check optional context
  my $a = $node->attributes;                                                    # Attributes hash
  if (defined($a->{$old}))                                                      # Check old attribute exists
   {my $value = $a->{$old};                                                     # Existing value
    $a->{$new} = $value;                                                        # Change the attribute name
    delete $a->{$old};
   }
  $node
 }
#a attrs set deleteAttrs
#b <b class="bb"/>
#c renameAttr class href
#d Rename an attribute.

sub renameAttrXtr($@)                                                           #CU Rename the attributes B<@attr> as far as possible to xtrc or xtrf.  Returns an array of the attributes that could not be so renamed.
 {my ($node, @attr) = @_;                                                       # Node, attributes to rename if they exist
  my $a = $node->attributes;                                                    # Attributes hash
  my @a;                                                                        # Attributes unable to rename
  for my $attr(@attr)                                                           # Each attribute to rename
   {next unless defined $a->{$attr};                                            # Requested attribute not present
    if    (!defined($a->{xtrc}))                                                # xtrc
     {$a->{xtrc} = delete $a->{$attr};
     }
    elsif (!defined($a->{xtrf}))                                                # xtrf
     {$a->{xtrf} = delete $a->{$attr};
     }
    else                                                                        # Cannot rename
     {push @a, $attr;
     }
   }
  @a                                                                            # Unrenameable attributes
 }

sub changeAttr($$$@)                                                            #CU Rename attribute B<$old> to B<$new> in the specified B<$node> with optional context B<@context> unless attribute B<$new> is already set and return the B<$node>. To make changes regardless of whether the new attribute already exists use L<renameAttr|/renameAttr>.
 {my ($node, $old, $new, @context) = @_;                                        # Node, existing attribute name, new attribute name, optional context.
  return undef if @context and !$node->at(@context);                            # Check optional context
  exists $node->attributes->{$new} ? $node : $node->renameAttr($old, $new)      # Check old attribute exists
 }

sub changeOrDeleteAttr($$$@)                                                    #CU Rename attribute B<$old> to B<$new> in the specified B<$node> in the optional B<@context> unless attribute B<$new> is already set in which case delete attribute B<$old>. Return B<$node> regardless of what action was taken. To make changes regardless of whether the new attribute already exists use L<renameAttr|/renameAttr>.
 {my ($node, $old, $new, @context) = @_;                                        # Node, existing attribute name, new attribute name, optional context.
  return undef if @context and !$node->at(@context);                            # Check optional context
  exists $node->attributes->{$new} ? $node->deleteAttr($old) :                  # Delete or rename attribute
                                     $node->renameAttr($old, $new)
 }

sub renameAttrValue($$$$$@)                                                     #CU Rename attribute B<$old> to B<$new> with new value B<$newValue> in the specified B<$node> in the optional B<@context> regardless of whether attribute B<$new> already exists or not as long as the attribute B<$old> has the value B<$oldValue>. Return the B<$node> regardless of what changes were made. To prevent inadvertent changes to existing attributes use L<changeAttrValue|/changeAttrValue>.
 {my ($node, $old, $oldValue, $new, $newValue, @context) = @_;                  # Node, existing attribute name, existing attribute value, new attribute name, new attribute value, optional context.
  return undef if @context and !$node->at(@context);                            # Check optional context
  my $a = $node->attributes;                                                    # Attributes hash
  if (defined($a->{$old}) and $a->{$old} eq $oldValue)                          # Check old attribute exists and has the specified value
   {$a->{$new} = $newValue;                                                     # Change the attribute name
    delete $a->{$old};
   }
  $node
 }

sub changeAttrValue($$$$$@)                                                     #CU Rename attribute B<$old> to B<$new> with new value B<$newValue> on the specified B<$node> in the optional B<@context> unless attribute B<$new> is already set or the value of the B<$old> attribute is not B<$oldValue>. Return the B<$node> regardless of what changes were made.  To make changes regardless of whether the new attribute already exists use L<renameAttrValue|/renameAttrValue>.
 {my ($node, $old, $oldValue, $new, $newValue, @context) = @_;                  # Node, existing attribute name, existing attribute value, new attribute name, new attribute value, optional context.
  return undef if @context and !$node->at(@context);                            # Check optional context
  exists $node->attributes->{$new} ? $node :                                    # Check old attribute exists
    $node->renameAttrValue($old, $oldValue, $new, $newValue)
 }

sub changeAttributeValue($$$@)                                                  #CU Apply a sub to the value of an attribute of the specified B<$node>.  The value to be changed is supplied and returned in: L<$_>.
 {my ($node, $attribute, $sub, @context) = @_;                                  # Node, attribute name, change sub, optional context.
  return () if @context and !$node->at(@context);                               # Check optional context
  return unless local $_ = $node->attr($attribute);                             # Check that the attribute has a value we can change
  &$sub;                                                                        # Change the attribute
  $node->set($attribute, $_)                                                    # Update the attribute's value
 }

sub changeOrDeleteAttrValue($$$$$@)                                             #CU Rename attribute B<$old> to B<$new> with new value B<$newValue> on the specified B<$node> in the optional B<@context> unless attribute B<$new> is already set or the value of the B<$old> attribute is not B<$oldValue> in which cases the B<$old> attribute is deleted. Return the B<$node> regardless of any changes made.  To make changes regardless of whether the new attribute already exists use L<renameAttrValue|/renameAttrValue>.
 {my ($node, $old, $oldValue, $new, $newValue, @context) = @_;                  # Node, existing attribute name, existing attribute value, new attribute name, new attribute value, optional context.
  return undef if @context and !$node->at(@context);                            # Check optional context
  exists $node->attributes->{$new} ? $node->deleteAttr($old) :                  # Check old attribute exists
    $node->renameAttrValue($old, $oldValue, $new, $newValue)
 }

sub copyAttrs($$@)                                                              # Copy all the attributes of the source node to the target node, or, just the named attributes if the optional list of attributes to copy is supplied, overwriting any existing attributes in the target node and return the source node.
 {my ($source, $target, @attr) = @_;                                            # Source node, target node, optional list of attributes to copy
  my $s = $source->attributes;                                                  # Source attributes hash
  my $t = $target->attributes;                                                  # Target attributes hash
  if (@attr)                                                                    # Named attributes
   {$t->{$_} = $s->{$_} for @attr;                                              # Transfer each named attribute
   }
  else                                                                          # All attributes
   {$t->{$_} = $s->{$_} for sort keys %$s;                                      # Transfer each source attribute
   }
  $source                                                                       # Return source node
 }

sub copyAttrsFromParent($@)                                                     #U Copy all the attributes from the parent (if there is one) of the current B<$node> to $node  or just the named B<@attributes> and return $node. If $node is the top of the parse tree then return B<undef> as it does not have a parent.
 {my ($node, @attr) = @_;                                                       # Node, attributes to copy from parent
  return undef unless my $parent = $node->parent;
  my $s = $parent->attributes;                                                  # Source attributes hash
  my $t = $node->attributes;                                                    # Target attributes hash
  if (@attr)                                                                    # Named attributes
   {$t->{$_} = $s->{$_} for @attr;                                              # Transfer each named attribute
   }
  else                                                                          # All attributes
   {$t->{$_} = $s->{$_} for sort keys %$s;                                      # Transfer all attributes from parent
   }
  $node                                                                         # Return node
 }

sub copyAttrsToParent($@)                                                       #U Copy all the attributes of the specified B<$node> to its parent (if there is one) or just the named B<@attributes> and return $node. If $node is the top of the parse tree then return B<undef> as it does not have a parent.
 {my ($node, @attr) = @_;                                                       # Node, attributes to copy from parent
  return undef unless my $parent = $node->parent;
  my $s = $node->attributes;                                                    # Source attributes hash
  my $t = $parent->attributes;                                                  # Target attributes hash
  if (@attr)                                                                    # Named attributes
   {$t->{$_} = $s->{$_} for @attr;                                              # Transfer each named attribute
   }
  else                                                                          # All attributes
   {$t->{$_} = $s->{$_} for sort keys %$s;                                      # Transfer all attributes from parent
   }
  $node                                                                         # Return node
 }

sub copyNewAttrs($$@)                                                           # Copy all the attributes of the source node to the target node, or, just the named attributes if the optional list of attributes to copy is supplied, without overwriting any existing attributes in the target node and return the source node.
 {my ($source, $target, @attr) = @_;                                            # Source node, target node, optional list of attributes to copy
  my $s = $source->attributes;                                                  # Source attributes hash
  my $t = $target->attributes;                                                  # Target attributes hash
  if (@attr)                                                                    # Named attributes
   {$t->{$_} = $s->{$_} for grep {!exists $t->{$_}} @attr;                      # Transfer each named attribute not already present in the target
   }
  else                                                                          # All attributes
   {$t->{$_} = $s->{$_} for grep {!exists $t->{$_}} sort keys %$s;              # Transfer each source attribute not already present in the target
   }
  $source                                                                       # Return source node
 }

sub moveAttrs($$@)                                                              # Move all the attributes of the source node to the target node, or, just the named attributes if the optional list of attributes to move is supplied, overwriting any existing attributes in the target node and return the source node.
 {my ($source, $target, @attr) = @_;                                            # Source node, target node, attributes to move
  my $s = $source->attributes;                                                  # Source attributes hash
  my $t = $target->attributes;                                                  # Target attributes hash
  if (@attr)                                                                    # Named attributes
   {$t->{$_} = delete $s->{$_} for @attr;                                       # Transfer each named attribute and delete from the source node
   }
  else                                                                          # All attributes
   {$t->{$_} = delete $s->{$_} for sort keys %$s;                               # Transfer each source attribute and delete from source node
   }
  $source                                                                       # Return source node
 }

sub moveNewAttrs($$@)                                                           # Move all the attributes of the source node to the target node, or, just the named attributes if the optional list of attributes to copy is supplied, without overwriting any existing attributes in the target node and return the source node.
 {my ($source, $target, @attr) = @_;                                            # Source node, target node, optional list of attributes to move
  my $s = $source->attributes;                                                  # Source attributes hash
  my $t = $target->attributes;                                                  # Target attributes hash
  if (@attr)                                                                    # Named attributes
   {$t->{$_} = delete $s->{$_} for grep {!exists $t->{$_}} @attr;               # Transfer each named attribute and delete it from the source node as long as it does not already exist in the target
   }
  else                                                                          # All attributes
   {$t->{$_} = delete $s->{$_} for grep {!exists $t->{$_}} sort keys %$s;       # Transfer every attribute and delete it from the source node as long as it does not already exist in the target
   }
  $source                                                                       # Return source node
 }

#D1 Traversal                                                                   # Traverse the L<parse|/parse> tree in various orders applying a B<sub> to each node.

#D2 Post-order                                                                  # This order allows you to edit children before their parents.

sub by2($$@)                                                                    #P Post-order traversal of a L<parse|/parse> tree
 {my ($node, $sub, @context) = @_;                                              # Starting node, sub to call for each sub node, accumulated context.
  $_->by2($sub, $node, @context) for $node->contents;                           # Recurse to process sub nodes in deeper context
  &$sub(local $_ = $node, @context);                                            # Process specified node last
  $node
 }

sub By22($$)                                                                    #P Post-order traversal of a L<parse|/parse> tree or sub tree calling the specified B<sub> at each node and returning the specified starting node. The B<sub> is passed references to the current node and all of its L<ancestors|/ancestry>. A reference to the current node is also made available via L<$_>. This is equivalent to the L<x=|/opBy> operator.
 {my ($node, $sub) = @_;                                                        # Starting node, sub to call for each sub node
  $node->by2($sub);                                                             # Recurse through nodes
  $node
 }

# Doubles performance of by!  IT is tempting to think that removing all the parameters would speed things up a lot - it does not as most parse trees are not very deep.

sub by($$)                                                                      #I Post-order traversal of a L<parse|/parse> tree or sub tree calling the specified B<sub> at each node and returning the specified starting node. The B<sub> is passed references to the current node and all of its L<ancestors|/ancestry>. A reference to the current node is also made available via L<$_>. This is equivalent to the L<x=|/opBy> operator.
 {my ($node, $sub) = @_;                                                        # Starting node, sub to call for each sub node

  my $by; $by = sub                                                             # Recurse to process sub nodes in deeper context
   {$_ = $_[0];                                                                 # Save active node in $_
    if (my $c = $_->{content})                                                  # Contents reference
     {my @c = @$c;                                                              # For some reason we cannot place this directly in a for loop reliably
      for(@c)                                                                   # Each node under the current node
       {if (my $d = $_->{content})                                              # Contents reference
         {my @d = @$d;                                                          # Content
          my $D = $_;                                                           # Save $_ so we can use it in the following loop
          for(@d)                                                               # Each node under the current node
           {if (my $e = $_->{content})                                          # Contents reference
             {my @e = @$e;                                                      # For some reason we cannot place this directly in a for loop reliably
              my $E = $_;                                                       # Save $_ so we can use it in the following loop
              for(@e)                                                           # Each node under the current node
               {if (my $f = $_->{content})                                      # Contents reference
                 {my @f = @$f;                                                  # Content
                  my $F = $_;                                                   # Save $_ so we can use it in the following loop
                  for(@f)                                                       # Each node under the current node
                   {if (my $g = $_->{content})                                  # Contents reference
                     {my @g = @$g;                                              # Content
                      my $G = $_;                                               # Save $_ so we can use it in the following loop
                      for(@g)                                                   # Each node under the current node
                       {if (my $h = $_->{content})                              # Contents reference
                         {my @h = @$h;                                          # Content
                          my $H = $_;                                           # Save $_ so we can use it in the following loop
                          for(@h)                                               # Each node under the current node
                           {if (my $i = $_->{content})                          # Contents reference
                             {my @i = @$i;                                      # Content
                              my $I = $_;                                       # Save $_ so we can use it in the following loop
                              for(@i)                                           # Each node under the current node
                               {if (my $j = $_->{content})                      # Contents reference
                                 {my @j = @$j;                                  # Content
                                  my $J = $_;                                   # Save $_ so we can use it in the following loop
                                  for(@j) {                                     # Each node under the current node
                                    &$by($_, $J, $I, $H, $G, $F, $E, $D, @_);   # Recurse
                                   }
                                 }
                                &$sub($_, $I, $H, $G, $F, $E, $D, @_);          # Process current node in post order
                               }
                             }
                            &$sub($_, $H, $G, $F, $E, $D, @_);                  # Process current node in post order
                           }
                         }
                        &$sub($_, $G, $F, $E, $D, @_);                          # Process current node in post order
                       }
                     }
                    &$sub($_, $F, $E, $D, @_);                                  # Process current node in post order
                   }
                 }
                &$sub($_, $E, $D, @_);                                          # Process current node in post order
               }
             }
            &$sub($_, $D, @_);                                                  # Process current node in post order
           }
         }
        &$sub($_, @_);                                                          # Process current node in post order
       }
     }
    &$sub(@_);                                                                  # Process current node in post order
   };

  &$by($node);                                                                  # Recurse through nodes

  $node
 }

sub byX($$)                                                                     #C Post-order traversal of a L<parse|/parse> tree calling the specified B<sub> at each node as long as this sub does not L<die>. The traversal is halted if the called sub does  L<die> on any call with the reason in L<?@|http://perldoc.perl.org/perlvar.html#Error-Variables> The B<sub> is passed references to the current node and all of its L<ancestors|/ancestry> up to the node on which this sub was called. A reference to the current node is also made available via L<$_>.\mReturns the start node regardless of the outcome of calling B<sub>.
 {my ($node, $sub) = @_;                                                        # Start node, sub to call
  eval {$node->byX2($sub)};                                                     # Trap any errors that occur
  $node
 }

sub byX2($$@)                                                                   #P Post-order traversal of a L<parse|/parse> tree or sub tree calling the specified B<sub> within L<eval>B<{}> at each node and returning the specified starting node. The B<sub> is passed references to the current node and all of its L<ancestors|/ancestry>. The value of the current node is also made available via L<$_>.
 {my ($node, $sub, @context) = @_;                                              # Starting node, sub to call, accumulated context.
  $_->byX2($sub, $node, @context) for $node->contents;                          # Recurse to process sub nodes in deeper context
  &$sub(local $_ = $node, @context);                                            # Process specified node last
 }

sub byX22($$@)                                                                  #P Post-order traversal of a L<parse|/parse> tree or sub tree calling the specified B<sub> within L<eval>B<{}> at each node and returning the specified starting node. The B<sub> is passed references to the current node and all of its L<ancestors|/ancestry>. The value of the current node is also made available via L<$_>.
 {my ($node, $sub, @context) = @_;                                              # Starting node, sub to call, accumulated context.
  $_->byX($sub, $node, @context) for $node->contents;                           # Recurse to process sub nodes in deeper context
  eval {&$sub(local $_ = $node, @context)};                                     # Process specified node last
  $node
 }

sub byList($@)                                                                  #C Return a list of all the nodes at and below a specified B<$node> in post-order or the empty list if the B<$node> is not in the optional B<@context>.
 {my ($node, @context) = @_;                                                    # Starting node, optional context
  return () if @context and !$node->at(@context);                               # Check optional context
  my @n;                                                                        # Nodes under specified node
  $node->by(sub{push @n, $_});                                                  # Retrieve nodes in post-order
  @n                                                                            # Return list of nodes
 }

sub byReverse($$@)                                                              # Reverse post-order traversal of a L<parse|/parse> tree or sub tree calling the specified B<sub> at each node and returning the specified starting B<$node>. The B<sub> is passed references to the current node and all of its L<ancestors|/ancestry>. The value of the current node is also made available via L<$_>.
 {my ($node, $sub, @context) = @_;                                              # Starting node, sub to call for each sub node, accumulated context.
  $_->byReverse($sub, $node, @context) for reverse $node->contents;             # Recurse to process sub nodes in deeper context
  &$sub(local $_ = $node, @context);                                            # Process specified node last
  $node
 }

sub byReverseX($$@)                                                             # Reverse post-order traversal of a L<parse|/parse> tree or sub tree below the specified B<$node> calling the specified B<sub> within L<eval>B<{}> at each node and returning the specified starting B<$node>. The B<sub> is passed references to the current node and all of its L<ancestors|/ancestry>. The value of the current node is also made available via L<$_>.
 {my ($node, $sub, @context) = @_;                                              # Starting node, sub to call for each sub node, accumulated context.
  $_->byReverseX($sub, $node, @context) for reverse $node->contents;            # Recurse to process sub nodes in deeper context
  &$sub(local $_ = $node, @context);                                            # Process specified node last
  $node
 }

sub byReverseList($@)                                                           #C Return a list of all the nodes at and below a specified B<$node> in reverse preorder or the empty list if the specified B<$node> is not in the optional B<@context>.
 {my ($node, @context) = @_;                                                    # Starting node, optional context
  return () if @context and !$node->at(@context);                               # Check optional context
  my @n;                                                                        # Nodes
  $node->byReverse(sub{push @n, $_});                                           # Retrieve nodes in reverse post-order
  @n                                                                            # Return list of nodes
 }

#D2 Pre-order                                                                   # This order allows you to edit children after their parents

sub down($$@)                                                                   # Pre-order traversal down through a L<parse|/parse> tree or sub tree calling the specified B<sub> at each node and returning the specified starting node. The B<sub> is passed references to the current node and all of its L<ancestors|/ancestry>. The value of the current node is also made available via L<$_>.
 {my ($node, $sub, @context) = @_;                                              # Starting node, sub to call for each sub node, accumulated context.
  &$sub(local $_ = $node, @context);                                            # Process specified node first
  $_->down($sub, $node, @context) for $node->contents;                          # Recurse to process sub nodes in deeper context
  $node
 }

sub downX($$)                                                                   # Pre-order traversal of a L<parse|/parse> tree calling the specified B<sub> at each node as long as this sub does not L<die>. The traversal is halted for the entire L<parse|/parse> tree if the called sub does L<die> with the reason returned in L<?@|http://perldoc.perl.org/perlvar.html#Error-Variables>. The B<sub> is passed references to the current node and all of its L<ancestors|/ancestry> up to the node on which this sub was called. A reference to the current node is also made available via L<$_>.\mReturns the start node regardless of the outcome of calling B<sub>.
 {my ($node, $sub) = @_;                                                        # Start node, sub to call
  eval {$node->downX2($sub)};                                                   # Trap any errors that occur
  $node
 }

sub downX2($$@)                                                                 #P Pre-order traversal of a L<parse|/parse> tree or sub tree calling the specified B<sub> within L<eval>B<{}> at each node and returning the specified starting node. The B<sub> is passed references to the current node and all of its L<ancestors|/ancestry>. The value of the current node is also made available via L<$_>.
 {my ($node, $sub, @context) = @_;                                              # Starting node, sub to call, accumulated context.
  &$sub(local $_ = $node, @context);                                            # Process specified node last
  $_->downX2($sub, $node, @context) for $node->contents;                        # Recurse to process sub nodes in deeper context
 }

sub downToDie($$)                                                               # Pre-order traversal of a L<parse|/parse> tree calling the specified B<sub> at each node as long as this sub does not L<die>. The traversal of the current sub tree is halted and continue with the next sibling or parent if the called sub does L<die>. The B<sub> is passed references to the current node and all of its L<ancestors|/ancestry> up to the node on which this sub was called. A reference to the current node is also made available via L<$_>.\mReturns the start node regardless of the outcome of calling B<sub>.
 {my ($node, $sub) = @_;                                                        # Start node, sub to call
  $node->downToDie2($sub);
  $node
 }

sub downToDie2($$@)                                                             #P Pre-order traversal of a L<parse|/parse> tree calling the specified B<sub> at each node as long as this sub does not L<die>. The traversal of the current sub tree is halted and continue with the next sibling or parent if the called sub does L<die>. The B<sub> is passed references to the current node and all of its L<ancestors|/ancestry> up to the node on which this sub was called. A reference to the current node is also made available via L<$_>.\mReturns the start node regardless of the outcome of calling B<sub>.
 {my ($node, $sub, @context) = @_;                                              # Starting node, sub to call, accumulated context.
  eval {&$sub(local $_ = $node, @context)};                                     # Process specified node last
  unless($@)                                                                    # Skip sub tree if sub died while processing parent
   {$_->downToDie2($sub, $node, @context) for $node->contents;                  # Recurse to process sub nodes in deeper context
   }
 }

sub downList($@)                                                                #C Return a list of all the nodes at and below a specified B<$node> in pre-order or the empty list if the B<$node> is not in the optional B<@context>.
 {my ($node, @context) = @_;                                                    # Starting node, optional context
  return () if @context and !$node->at(@context);                               # Check optional context
  my @n;                                                                        # Nodes under specified node
  $node->down(sub{push @n, $_});                                                # Retrieve nodes in pre-order
  @n                                                                            # Return list of nodes
 }

sub downReverse($$@)                                                            # Reverse pre-order traversal down through a L<parse|/parse> tree or sub tree calling the specified B<sub> at each node and returning the specified starting node. The B<sub> is passed references to the current node and all of its L<ancestors|/ancestry>. The value of the current node is also made available via L<$_>.
 {my ($node, $sub, @context) = @_;                                              # Starting node, sub to call for each sub node, accumulated context.
  &$sub(local $_ = $node, @context);                                            # Process specified node first
  $_->downReverse($sub, $node, @context) for reverse $node->contents;           # Recurse to process sub nodes in deeper context
  $node
 }

sub downReverseX($$@)                                                           # Reverse pre-order traversal down through a L<parse|/parse> tree or sub tree calling the specified B<sub> within L<eval>B<{}> at each node and returning the specified starting node. The B<sub> is passed references to the current node and all of its L<ancestors|/ancestry>. The value of the current node is also made available via L<$_>.
 {my ($node, $sub, @context) = @_;                                              # Starting node, sub to call for each sub node, accumulated context.
  &$sub(local $_ = $node, @context);                                            # Process specified node first
  $_->downReverseX($sub, $node, @context) for reverse $node->contents;          # Recurse to process sub nodes in deeper context
  $node
 }

sub downReverseList($@)                                                         #C Return a list of all the nodes at and below a specified B<$node> in reverse pre-order or the empty list if the B<$node> is not in the optional B<@context>.
 {my ($node, @context) = @_;                                                    # Starting node, optional context
  return () if @context and !$node->at(@context);                               # Check optional context
  my @n;                                                                        # Nodes under specified node
  $node->downReverse(sub{push @n, $_});                                         # Retrieve nodes in reverse pre-order
  @n                                                                            # Return list of nodes
 }

#D2 Pre and Post order                                                          # Visit the parent first, then the children, then the parent again.

sub through($$$@)                                                               # Traverse L<parse|/parse> tree visiting each node twice calling the specified sub B<$before> as we go down past the node and sub B<$after> as we go up past the node, finally return the specified starting node. The subs B<$before, $after> are passed references to the current node and all of its L<ancestors|/ancestry>. The value of the current node is also made available via L<$_>.
 {my ($node, $before, $after, @context) = @_;                                   # Starting node, sub to call when we meet a node, sub to call we leave a node, accumulated context.
  &$before(local $_ = $node, @context) if $before;                              # Process specified node first with before()
  $_->through($before, $after, $node, @context) for $node->contents;            # Recurse to process sub nodes in deeper context
  &$after(local $_ = $node, @context) if $after;                                # Process specified node last with after()
  $node
 }

sub throughX($$$@)                                                              # Identical to L<through|/through> except the B<$before, $after> subs are called in an L<eval> block to prevent L<die> terminating the traversal of the full tree.
 {my ($node, $before, $after, @context) = @_;                                   # Starting node, sub to call when we meet a node, sub to call we leave a node, accumulated context.
  &$before(local $_ = $node, @context);                                         # Process specified node first with before()
  $_->throughX($before, $after, $node, @context) for $node->contents;           # Recurse to process sub nodes in deeper context
  &$after(local $_ = $node, @context);                                          # Process specified node last with after()
  $node
 }

#D2 Range                                                                       # Ranges of nodes

sub from($@)                                                                    # Return a list consisting of the specified node and its following siblings optionally including only those nodes that match one of the tags in the specified list.
 {my ($start, @match) = @_;                                                     # Start node, optional list of tags to match
  my $p = $start->parent;                                                       # Parent node
  confess "No parent" unless $p;                                                # Not possible on a root node
  my @c = $p->contents;                                                         # Content
  shift @c while @c and $c[ 0] != $start;                                       # Position on start node
  if (@match)                                                                   # Select matching nodes if requested
   {my %m = map {$_=>1} @match;
    return grep {$m{$_->tag}} @c;
   }
  @c                                                                            # Elements in the specified range
 }

sub to($@)                                                                      # Return a list of the sibling nodes preceding the specified node optionally including only those nodes that match one of the tags in the specified list.
 {my ($end, @match) = @_;                                                       # End node, optional list of tags to match
  my $q = $end->parent;                                                         # Parent node
  confess "No parent" unless $q;                                                # Not possible on a root node
  my @c = $q->contents;                                                         # Content
  pop @c while @c and $c[-1] != $end;                                           # Position on end
  if (@match)                                                                   # Select matching nodes if requested
   {my %m = map {$_=>1} @match;
    return grep {$m{$_->tag}} @c;
   }
  @c                                                                            # Elements in the specified range
 }

sub fromTo($$@)                                                                 # Return a list of the nodes between the specified start and end nodes optionally including only those nodes that match one of the tags in the specified list.
 {my ($start, $end, @match) = @_;                                               # Start node, end node, optional list of tags to match
  my $p = $start->parent;                                                       # Parent node
  confess "No parent" unless $p;                                                # Not possible on a root node
  my $q = $end->parent;                                                         # Parent node
  confess "No parent" unless $q;                                                # Not possible on a root node
  confess "Not siblings" unless $p == $q;                                       # Not possible unless the two nodes are siblings under the same parent
  my @c = $p->contents;                                                         # Content
  shift @c while @c and $c[ 0] != $start;                                       # Position on start node
  pop   @c while @c and $c[-1] != $end;                                         # Position on end
  if (@match)                                                                   # Select matching nodes if requested
   {my %m = map {$_=>1} @match;
    return grep {$m{$_->tag}} @c;
   }
  @c                                                                            # Elements in the specified range
 }

#D1 Location                                                                    # Locate the line numbers and columns of a specified node and write that information as a L<Oxygen Message|/https://www.oxygenxml.com/doc/versions/20.1/ug-author/topics/linked-output-messages-of-external-engine.html>.

sub parseLineLocation($)                                                        #PS Parse a line location
 {my ($loc) = @_;                                                               # Location
  my ($l, $c, $L, $C) = split m/[.:]/, $loc;                                    # Position of node in source

  for my $n($l, $c, $L)                                                         # Check that some-one else is not using xtrf for some other reason
   {return () unless $n and $n =~ m(\A\d+\Z)s;
   }
  return () if $L and $L !~ m(\A\d+\Z)s;

  unless(defined $C)                                                            # Same line
   {$C = $L;
    $L = 0;
   }
  $L += $l;                                                                     # Final line

 ($l, $c, $L, $C)                                                               # Return parsed line location
 }

sub lineLocation($)                                                             #U Return the line number.column location of this tag in its source file or string if the source was parsed with the L<line number|/lineNumber> option on.
 {my ($node) = @_;                                                              # Node
  my $loc  = $node->attr(q(xtrf));                                              # Location of node in source
  return q() unless $loc;                                                       # No location specified
  my @c = my ($l, $c, $L, $C) = parseLineLocation $loc;                         # Position of node in source
  for(@c)
   {return q() unless defined $_;
   }
  if ($l eq $L)                                                                 # All on one line
   {return qq(on line $l from $c to $C)                                         # Single line
   }
  qq(from line $l at $c to line $L at $C)                                       # Spans two or more lines
 }

sub location($;$)                                                               #U Return the line number.column plus file location of this tag in its source file or string if the source was parsed with the L<line number|/lineNumber> option on.
 {my ($node, $file) = @_;                                                       # Node, optionally the location of the source.
  my $lmsg   = $node->lineLocation();                                           # Line location
     $lmsg   = q( ).$lmsg if $lmsg;
  my $parser = $node->parser;                                                   # Parser associated with this node
  my $fmsg   = sub                                                              # Description of the containing file
   {return qq( in file: ).$file if $file;                                       # In the specified file
    return qq( in file: ).$parser->inputFile if $parser->inputFile;             # Position of node in source
    q()                                                                         # Unknown location
   }->();
  $lmsg.$fmsg                                                                   # Return location
 }

sub closestLocation($)                                                          #U Return the nearest node with line number.column information
 {my ($node) = @_;                                                              # Node
  return $node if $node->attr(q(xtrf));                                         # Node has a location so return it.
  my $best;                                                                     # Best location so far
  if ($node->parser->lineNumbers)                                               # Search through tree for closest node if line numbering is in effect
   {my $before = 1;                                                             # Best node before (after) the specified node
    for my $p($node->parser->downList)                                          # Preorder traversal
     {if ($before or !$best)                                                    # Before or at the node
       {$best = $p if $p->attr(q(xtrf));
       }
      $before = 0 if $p == $node and $before;                                   # Pass the node
     }
   }
  $best                                                                         # Return best node if known
 }

sub approxLocation($;$)                                                         #U Return the line number.column location of the node nearest to this node in the source file if the source was parsed with the L<line number|/lineNumber> option on.
 {my ($node, $file) = @_;                                                       # Node, optionally the location of the source.
  return location(@_) if $node->attr(q(xtrf));                                  # Node has a location so return it.

  my $best = $node->closestLocation;                                            # Search through tree for closest node if line numbering is in effect
  return location($best, $file) if $best;                                       # Nearby node has a location so return it.
  my $f = $file // $node->parser->inputFile;                                    # Parser input file if known
  return qq( in file: ).$f if $f;                                               # Position of node in source
  q()                                                                           # Unknown location
 }

sub formatOxygenMessage($$$@)                                                   #U Write an error message in Oxygen format
 {my ($node, $level, $url, @message) = @_;                                      # Node, error level [F|E|W], explanatory Url, message text
  my ($line, $col, $Line, $Col) = sub                                           # Position in Oxygen format
   {my $best = $node->closestLocation;                                          # Closest node with line number information
    return parseLineLocation $best->xtrfX if $best;                             # Return numbers
    (1,1,0,0)
   }->();
  my $m = nws(join '', @message);
  my $u = $url ? qq( $url) : q();
  <<END;                                                                        # To get this message to Oxygen simply write on STDOUT or STDERR
Type: $level
Line: $line
Column: $col
EndLine: $Line
EndColumn: $Col
AdditionalInfoURL:$u
Description: $m
END
 }

#D1 Position                                                                    # Confirm that the position L<navigated|/Navigation> to is the expected position.

sub atPositionMatch($$)                                                         #P Confirm that a string matches a match expression.
 {my ($tag, $match) = @_;                                                       # Starting node, ancestry.
  return 1 unless $match;                                                       # Undefined match means anything matches
  return $tag eq $match unless  ref $match;                                     # Match scalar
  return $tag =~ m($match)s if  ref($match) =~ m(regexp)i;                      # Match regular expression
  return $$match{$tag}      if  ref($match) =~ m(hash)i;                        # Match hash key
                            if (ref($match) =~ m(array)i)                       # Match array
   {my %m = map {$_=>1} @$tag;
    return $m{$tag}
   }
  confess "Unknown match type";                                                 # Do not know how to match
 }

sub at($@)                                                                      #CIYU Confirm that the specified B<$node> has the specified L<ancestry|/ancestry>. Ancestry is specified by providing the expected tags that the B<$node>'s parent, the parent's parent etc. must match at each level. If B<undef> is specified then any tag is assumed to match at that level. If a regular expression is specified then the current parent node tag must match the regular expression at that level. If all supplied tags match successfully then the starting node is returned else B<undef>.
 {my ($node, @context) = @_;                                                    # Node, ancestry.
  for(my $x = shift @_; $x; $x = $x->parent)                                    # Up through parents
   {return $node unless @_;                                                     # OK if no more required context
    next if atPositionMatch(-t $x, shift @_);                                   # Match tag against context
    return undef                                                                # Error if required does not match actual
   }
  !@_ ? $node : undef                                                           # Top of the tree is OK as long as there is no more required context
 }
#b <b/>
#b <c/>
#c at b
#c set id idb href bbb
#d Continue if we are in the specified context.

sub atText($$@)                                                                 #CU Confirm that we are on a text node whose text value matches a regular expression in the optional B<@context>. Return the specified B<$node> on success else B<undef>.
 {my ($node, $re, @context) = @_;                                               # Text node, regular expression to match, context
  return undef if !$node->isText(@context);                                     # Not a text node in the specified context
  $node->text =~ m($re) ? $node : undef                                         # Success if the text matches
 }
#b <b>bb</b><c>cc</c>
#c atText cc c
#c up
#c set id CC
#d Continue if we are on a text node whose value matches a regular expression

sub atStringContentMatches($$@)                                                 #CU Confirm that we are on a B<$node> whose contents, represented as a string, matches the specified regular expression B<$re> in the optional B<@context>. Return the specified B<$node> on success else B<undef>.
 {my ($node, $re, @context) = @_;                                               # Text node, regular expression to match, context
  return undef if @context and !$node->at(@context);                            # Check optional context
  $node->stringContent =~ m($re) ? $node : undef                                # Success if the content, as a string, matches the re
 }
#b <b><c>cc</c><d>dd</d></b>
#c atStringContentMatches qr(dd)
#c set id yes
#d Continue if we are on a node whose content represented as a string matches a regular expression

sub atTop($)                                                                    #U Return the current node if it is the root == top of a parse tree else return B<undef>.
 {my ($node) = @_;                                                              # Node
  return $node unless $node->parent;                                            # Has no parent so must be at the top
  undef                                                                         # Has a parent and so is not at the top
 }
#a at
#b <b/>
#c atTop
#c set  id top
#d Continue if we are at the top.

sub attrAt($$@)                                                                 #CU Return the specified B<$node> if it has the specified B<$attribute> and the $node is in the optional B<@context> else return B<undef>.
 {my ($node, $attribute, @context) = @_;                                        # Starting node, attribute, context
  return undef if @context and !$node->at(@context);                            # Not in specified context
  defined($node->attributes->{$attribute}) ? $node : undef                      # Node has attribute
 }
#a set
#b <b id='b'/><b id='bb'/>
#c attrValueAt id bb
#c set class here
#d Continue if an attribute has a specific value.

sub attrValueAt($$$@)                                                           #CU Return the specified B<$node> if it has the specified B<$attribute> with the specified B<$value> and the $node is in the optional B<@context> else return B<undef>.
 {my ($node, $attribute, $value, @context) = @_;                                # Starting node, attribute, wanted value of attribute, context
  return undef if @context and !$node->at(@context);                            # Not in specified context
  my $v = $node->attributes->{$attribute};                                      # Actual value of attribute
  if (defined($value) and defined($v))                                          # Compare attribute actual and wanted values
   {if (my $r = ref($value))                                                    # Compare with a reference
     {if ($r =~ m(regExp)is)                                                    # Regular expression
       {return $v =~ m($value) ? $node : undef;
       }
      elsif ($r =~ m(Hash)is)                                                   # Key of a hash
       {return $$value{$v}     ? $node : undef;
       }
      confess "Attribute value check expressed via unknown reference type";     # Unknown matching method
     }
    else                                                                        # Match on value
     {return $value eq $v      ? $node : undef;
     }
   }
  undef                                                                         # One of actual and wanted values was B<undef> so return B<undef>.
 }
#a set
#b <b id='b'/><b id='bb'/>
#c attrValueAt id bb
#c set class here
#d Continue if an attribute has a specific value.

sub not($@)                                                                     #U Return the specified B<$node> if it does not match any of the specified tags, else B<undef>
 {my ($node, @tags) = @_;                                                       # Node, tags not to match
  my %tags = map {$_=>1} @tags;                                                 # Tags not to match
  return $node unless $tags{$node->tag};                                        # Ok if node does not have one of the specified tags
  undef                                                                         # Matched one of the tags that it should not match
 }

sub atOrBelow($@)                                                               #CYU Confirm that the node or one of its ancestors has the specified context as recognized by L<at|/at> and return the first node that matches the context or B<undef> if none do.
 {my ($start, @context) = @_;                                                   # Starting node, ancestry.
  for(my $x = $start; $x; $x = $x->parent)                                      # Up through parents
   {return $x if $x->at(@context);                                              # Return this node if the context matches
   }
  undef                                                                         # No node that matches the context
 }

sub adjacent($$)                                                                # Return the first node if it is adjacent to the second node else B<undef>.
 {my ($first, $second) = @_;                                                    # First node, second node
  my ($n, $p) = ($first->next, $first->prev);                                   # Nodes adjacent to the first node
  return $first if $n && $n == $second or $p && $p == $second;                  # Adjacent nodes
  undef
 }

sub ancestry($)                                                                 #U Return a list containing: (the specified B<$node>, its parent, its parent's parent etc..). Or use L<upn|/upn> to go up the specified number of levels.
 {my ($node) = @_;                                                              # Starting node.
  my @a;
  for(my $x = $node; $x; $x = $x->parent)                                       # Up through parents
   {push @a, $x;
   }
  @a                                                                            # Return ancestry
 }

sub context($)                                                                  #bU Return a string containing the tag of the starting node and the tags of all its ancestors separated by single spaces.
 {my ($node) = @_;                                                              # Starting node.
  my @a;                                                                        # Ancestors
  for(my $p = $node; $p; $p = $p->parent)
   {push @a, $p->tag;
    @a < 100 or confess "Overly deep tree!";
   }
  join ' ', @a
 }
#a
#b <b> <c> <d context/> </c> </b>
#c
#d Get the context of node <b>d</b> by typing <b>context</b> or <b>gc</b> against its closing bracket and pressing <b>Save</b>.
BEGIN {*gc = *context}

sub containsSingleText($@)                                                      #CU Return the single text element below the specified B<$node> else return B<undef>.
 {my ($node, @context) = @_;                                                    # Node, optional context
  return undef if @context and !$node->at(@context);                            # Not in specified context
  my $t = $node->hasSingleChild;                                                # Child element
  $t ? $t->isText : $t                                                          # Child element must be text
 }

sub depth($)                                                                    #U Returns the depth of the specified B<$node>, the  depth of a root node is zero.
 {my ($node) = @_;                                                              # Node.
  my $a = 0;
  for(my $x = $node->parent; $x; $x = $x->parent) {++$a}                        # Up through parents
  $a                                                                            # Return depth
 }

sub depthProfile($)                                                             #U Returns the depth profile of the tree rooted at the specified B<$node>.
 {my ($node) = @_;                                                              # Node.
  my @d;                                                                        # Depth profile as an array
  $node->by(sub                                                                 # Each node
   {push @d, scalar @_;                                                         # Depth of node
   });
  return @d if wantarray;                                                       # Depth profile as an array
  join ' ', @d                                                                  # Depth profile as a string
 }

sub setDepthProfile($)                                                          #U Sets the L<depthProfile|/depthProfile> for every node in the specified B<$tree>. The last set L<depthProfile|/depthProfile> for a specific niode can be retrieved from L<depthProfileLast|/depthProfileLast>.
 {my ($tree) = @_;                                                              # Tree of nodes.
  $tree->by(sub                                                                 # Each node
   {$_->depthProfileLast = $_->depthProfile;                                    # Set depth profile for node.
   });
  $tree
 }

sub height($)                                                                   #U Returns the height of the tree rooted at the specified B<$node>.
 {my ($node) = @_;                                                              # Node.
  my $h = 0;                                                                    # Height of tree so far
  $node->by(sub                                                                 # Each node
   {$h = scalar(@_) if scalar(@_) > $h;                                         # Highest height so far
   });
  $h                                                                            # Return height
 }

sub isFirst($@)                                                                 #BCYU Return the specified B<$node> if it is first under its parent and optionally has the specified context, else return B<undef>
 {my ($node, @context) = @_;                                                    # Node, optional context
  return undef if @context and !$node->at(@context);                            # Not in specified context
  my $parent = $node->parent;                                                   # Parent
  return $node unless defined($parent);                                         # The top most node is always first
  $node == $parent->first ? $node : undef                                       # First under parent
 }
#a isLast
#b <b/><c><d/></c><e/>
#c isFirst
#c set id first
#d Continue if we are at a first child.

sub isNotFirst($@)                                                              #CU Return the specified B<$node> if it is not first under its parent and optionally has the specified context, else return B<undef>
 {my ($node, @context) = @_;                                                    # Node, optional context
  return undef if @context and !$node->at(@context);                            # Not in specified context
  my $parent = $node->parent;                                                   # Parent
  return undef unless defined($parent);                                         # The top most node is always first
  $node != $parent->first ? $node : undef                                       # Not first under parent
 }
#a isNotFirst
#b <b/><c><d/></c><e/>
#c isNotFirst
#c set id notFirst
#d Continue if we are not at a first child.

sub isFirstN($$@)                                                               #CU Return the first B<$N> nodes as an array if the first B<$N> tags of the parent of B<$node> finish at the specified B<$node> and have the specified tags in the sequence specified by B<@context>. If B<@context> contains more then B<$N> entries, the remainder are checked as the context of the parent of B<$node>. Returns an array of nodes found or an empty array if the specified B<$node> does not match these conditions.
 {my ($node, $N, @context) = @_;                                                # Node, number of tags to be first, first tags and optional context
  return () unless my $parent = $node->parent;                                  # Must have a parent
  @context >= $N or confess "Not enough context";                               # Must have enough tags to match N
  my @tags; push @tags, shift @context for 1..$N;                               # Separate context into first n tags and parent context
  return () if @context and !$parent->at(@context);                             # Not in specified context
  my @nodes = $parent->contents;                                                # Parent content
  pop @nodes while @nodes and $nodes[-1] != $node;                              # Remove following nodes
  return () unless @nodes == $N;                                                # Nodes are not first
  for(1..$N)                                                                    # Confirm tags match
   {next if $nodes[-$_]->tag eq $tags[-$_];                                     # Confirm tag matches
    return ()                                                                   # Return empty list as tags failed to match
   }
  @nodes                                                                        # Return matching nodes on success
 }

sub isFirstToDepth($$@)                                                         #CU Return the specified B<$node> if it is first to the specified depth else return B<undef>
 {my ($node, $depth, @context) = @_;                                            # Node, depth, optional context
  return undef if @context and !$node->at(@context);                            # Not in specified context
  my $p = $node;                                                                # Start
  for(1..$depth-1)                                                              # Check each ancestor is first to the specified depth but one
   {return undef unless $p->isFirst and $p = $p->parent;                        # Check node is first and that we can move up
   }
  $p->isFirst                                                                   # Confirm that the last ancestor so reached is a first node
 }

sub firstIs($@)                                                                 #CU Return the specified B<$node> if it has one or more child nodes and the first child node has the specified B<@context> otherwise B<undef>.
 {my ($node, @context) = @_;                                                    # Node, optional context
  return undef unless my $first = $node->first;                                 # Must have a first child
  return undef if @context and !$first->at(@context);                           # First child must match any supplied context
  $node                                                                         # Success
 }
#a firstIs
#b <b><c><d/><e/></c></b>
#c firstIs d
#c set id yes
#d Continue if there is a first child with the specified context

sub isLast($@)                                                                  #BCYU Return the specified B<$node> if it is last under its parent and optionally has the specified context, else return B<undef>
 {my ($node, @context) = @_;                                                    # Node, optional context
  return undef if @context and !$node->at(@context);                            # Not in specified context
  my $parent = $node->parent;                                                   # Parent
  return $node unless defined($parent);                                         # The top most node is always last
  $node == $parent->last ? $node : undef                                        # Last under parent
 }
#a isFirst
#b <b/><c><d/></c><e/>
#c isLast
#c set id last
#d Continue if we are at a last child.

sub isNotLast($@)                                                               #CU Return the specified B<$node> if it is not last under its parent and optionally has the specified context, else return B<undef>
 {my ($node, @context) = @_;                                                    # Node, optional context
  return undef if @context and !$node->at(@context);                            # Not in specified context
  my $parent = $node->parent;                                                   # Parent
  return undef unless defined($parent);                                         # The top most node is always last
  $node != $parent->last ? $node : undef                                        # Not last under parent
 }
#a isLast isNotFirst
#b <b/><c><d/></c><e/>
#c isNotLast
#c set id notLast
#d Continue if we are not at a last child.

sub isLastN($$@)                                                                #CU Return the last B<$N> nodes as an array if the last B<$N> tags of the parent of B<$node> start at the specified B<$node> and have the specified tags in the sequence specified by B<@context>. If B<@context> contains more then B<$N> entries, the remainder are checked as the context of the parent of B<$node>. Returns an array of nodes found or an empty array if the specified B<$node> does not match these conditions.
 {my ($node, $N, @context) = @_;                                                # Node, number of tags to be last, last tags and optional context
  return () unless my $parent = $node->parent;                                  # Must have a parent
  @context >= $N or confess "Not enough context";                               # Must have enough tags to match N
  my @tags; push @tags, shift @context for 1..$N;                               # Separate context into last n tags and parent context
  return () if @context and !$parent->at(@context);                             # Not in specified context
  my @nodes = $parent->contents;                                                # Parent content
  shift @nodes while @nodes and $nodes[0] != $node;                             # Remove preceding nodes
  return () unless @nodes == $N;                                                # Nodes are not last
  for(1..$N)                                                                    # Confirm tags match
   {next if $nodes[-$_]->tag eq $tags[-$_];                                     # Confirm tag matches
    return ()                                                                   # Return empty list as tags failed to match
   }
  @nodes                                                                        # Return matching nodes on success
 }

sub isLastToDepth($$@)                                                          #CU Return the specified B<$node> if it is last to the specified depth else return B<undef>
 {my ($node, $depth, @context) = @_;                                            # Node, depth, optional context
  return undef if @context and !$node->at(@context);                            # Not in specified context
  my $p = $node;                                                                # Start
  for(1..$depth-1)                                                              # Check each ancestor is last to the specified depth but one
   {return undef unless $p->isLast and $p = $p->parent;                         # Check node is last and that we can move up
   }
  $p->isLast ? $node : undef                                                    # Confirm that the last ancestor so reached is a last node
 }

sub lastIs($@)                                                                  #CU Return the specified B<$node> if it has one or more child nodes and the last child node has the specified B<@context> otherwise B<undef>.
 {my ($node, @context) = @_;                                                    # Node, optional context
  return undef unless my $last = $node->last;                                   # Must have a last child
  return undef if @context and !$last->at(@context);                            # Last child must match any supplied context
  $node                                                                         # Success
 }
#a lastIs
#b <b><c><d/><e/></c></b>
#c lastIs e
#c set id yes
#d Continue if there is a last child with the specified context

sub nextIs($@)                                                                  #CU Return the specified B<$node> if there is a following node with the specified B<@context>. Returns B<undef> if the $node is last.
 {my ($node, @context) = @_;                                                    # Node, optional context
  return undef unless my $next = $node->next;                                   # Must have a following node
  return undef if @context and !$next->at(@context);                            # Following node must match any supplied context
  $node                                                                         # Success
 }
#a nextIs
#b <b/><c/><d/><e/>
#c nextIs d
#c set id yes
#d Continue if the following node has the expected context

sub nextN($$@)                                                                  #CU Return B<$N> nodes as an array starting at B<$node> inclusive if they match the first B<$N> tags of B<@context>. If B<@context> contains more then B<$N> entries, the remainder are checked as the context of the parent of B<$node>. Returns an array of nodes found or an empty array if the specified B<$node> does not match these conditions.
 {my ($node, $N, @context) = @_;                                                # Node, number of tags to be last, last tags and optional context
  return () unless my $parent = $node->parent;                                  # Must have a parent
  @context >= $N or confess "Not enough context";                               # Must have enough tags to match N
  my @tags; push @tags, shift @context for 1..$N;                               # Separate context into last n tags and parent context
  return () if @context and !$parent->at(@context);                             # Not in specified context
  my @nodes = $parent->contents;                                                # Parent content
  shift @nodes while @nodes and $nodes[0] != $node;                             # Remove preceding nodes
  return () unless @nodes >= $N;                                                # Enough nodes to match
  pop @nodes while @nodes and @nodes > $N;                                      # Nodes requested
  for(1..$N)                                                                    # Confirm tags match
   {next if $nodes[-$_]->tag eq $tags[-$_];                                     # Confirm tag matches
    return ()                                                                   # Return empty list as tags failed to match
   }
  @nodes                                                                        # Return matching nodes on success
 }

sub prevIs($@)                                                                  #CU Return the specified B<$node> if there is a previous node with the specified B<@context>. Returns B<undef> if the $node is first.
 {my ($node, @context) = @_;                                                    # Node, optional context
  return undef unless my $prev = $node->prev;                                   # Must have a previous node
  return undef if @context and !$prev->at(@context);                            # Previous node must match any supplied context
  $node                                                                         # Success
 }
#a prevIs
#b <b/><c/><d/><e/>
#c prevIs c
#c set id yes
#d Continue if the previous node has the expected context

sub prevN($$@)                                                                  #CU Return B<$N> nodes as an array ending at B<$node> inclusive if they match the first B<$N> tags of B<@context>. If B<@context> contains more then B<$N> entries, the remainder are checked as the context of the parent of B<$node>. Returns an array of nodes found or an empty array if the specified B<$node> does not match these conditions.
 {my ($node, $N, @context) = @_;                                                # Node, number of tags to be first, first tags and optional context
  return () unless my $parent = $node->parent;                                  # Must have a parent
  @context >= $N or confess "Not enough context";                               # Must have enough tags to match N
  my @tags; push @tags, shift @context for 1..$N;                               # Separate context into first n tags and parent context
  return () if @context and !$parent->at(@context);                             # Not in specified context
  my @nodes = $parent->contents;                                                # Parent content
  pop @nodes while @nodes and $nodes[-1] != $node;                              # Remove following nodes
  return () unless @nodes >= $N;                                                # Nodes are not first
  shift @nodes while @nodes and @nodes > $N;                                    # Nodes requested
  for(1..$N)                                                                    # Confirm tags match
   {next if $nodes[-$_]->tag eq $tags[-$_];                                     # Confirm tag matches
    return ()                                                                   # Return empty list as tags failed to match
   }
  @nodes                                                                        # Return matching nodes on success
 }

sub isOnlyChild($@)                                                             #CYU Return the specified B<$node> if it is the only node under its parent ignoring any surrounding blank text.
 {my ($node, @context) = @_;                                                    # Node, optional context
  return undef if @context and !$node->at(@context);                            # Not in specified context
  my $parent = $node->parent;                                                   # Find parent
  return $node unless $parent;                                                  # The root node is an only child
  my @c = $parent->contents;                                                    # Contents of parent
  return $node if @c == 1;                                                      # Only child if only one child
  shift @c while @c and $c[ 0]->isBlankText;                                    # Ignore leading blank text
  pop   @c while @c and $c[-1]->isBlankText;                                    # Ignore trailing blank text
  return $node if @c == 1;                                                      # Only child if only one child after leading and trailing blank text has been ignored
  undef                                                                         # Not the only child
 }
#a isOnlyChild
#b <b><c/></b><d><e/><e/></d>
#c isOnlyChild
#c set id onlyChild
#d Continue if we are at an only child.

sub isOnlyChildToDepth($$@)                                                     #CU Return the specified B<$node> if it and its ancestors are L<only children|/isOnlyChild> to the specified depth else return B<undef>. isOnlyChildToDepth(1) is the same as L<isOnlychild|/isOnlyChild>
 {my ($node, $depth, @context) = @_;                                            # Node, depth to which each parent node must also be an only child, optional context
  return undef if @context and !$node->at(@context);                            # Not in specified context
  my $p = $node;                                                                # Walk up through ancestors
  for(1..$depth-1)                                                              # Walk up through ancestors to the specified depth but one
   {return undef unless $p->isOnlyChild and $p = $p->parent;                    # Confirm we are an only child and can move up one more level
   }
  $p->isOnlyChild;                                                              # Confirm that we are still on an only child
 }

sub isOnlyChildN($$@)                                                           #CU Return the specified B<$node> if it is an only child of B<$depth> ancestors with L<only children|/hasSingleChild> in the opptional B<@context> else return B<undef>. isOnlyChildN(1) is the same as L<isOnlychild|/isOnlyChild>.
 {my ($node, $depth, @context) = @_;                                            # Node, depth to which each parent node must also be an only child, optional context
  return undef if @context and !$node->at(@context);                            # Not in specified context
  my $p = $node;                                                                # Walk up through ancestors
  for(1..$depth-1)                                                              # Walk up through ancestors to the specified depth but one
   {return undef unless $p->isOnlyChild and $p = $p->parent;                    # Confirm we are an only child and can move up one more level
   }
  $p->isOnlyChild ? $node : undef                                               # Confirm that we are still on an only child
 }

sub isOnlyChildText($@)                                                         #CU Return the specified B<$node> if it is a text node and it is an only child else and its parent is in the specified optional context else return B<undef>.
 {my ($node, @context) = @_;                                                    # Node, optional context
  if (my $parent = $node->parent)                                               # Parent required
   {return undef if @context and !$parent->at(@context);                        # Not in specified context
    return $node if $node->isText and $node->isOnlyChild;                       # Confirm that the node is a text node and an only child
   }
  undef                                                                         # No parent so cannot be an only child
 }

sub hasSingleChild($@)                                                          #CYU Return the only child of the specified B<$node> if the child is the only node under its parent ignoring any surrounding blank text and has the optional specified context, else return B<undef>.
 {my ($node, @context) = @_;                                                    # Node, optional context
  return undef unless my $child = $node->first(@context);                       # A possible child node
  return undef unless $child->isOnlyChild;                                      # Not an only child
  $child                                                                        # Return the only child
 }
#a hasSingleChild
#b <b><c/></b><d><e/><e/></d>
#c hasSingleChild
#c set id hasSingleChild
#d Continue if we have a single child.

sub hasSingleChildText($@)                                                      #CU Return the only child of the specified B<$node> if the child is a text node and the child is the only node under its parent ignoring any surrounding blank text and the child has the optional specified context, else return B<undef>.
 {my ($node, @context) = @_;                                                    # Node, optional context
  return undef unless my $child = $node->hasSingleChild(@context);              # A possible single child node
  return undef unless $child->isText;                                           # Not a text node
  $child                                                                        # Return the only text child
 }

sub hasSingleChildToDepth($$@)                                                  #CU Return the descendant of the specified B<$node> if it has single children to the specified depth in the specified optional B<@context> else return B<undef>.  L<hasSingleChildToDepth(0)|/hasSingleChildToDepth> is equivalent to L<hasSingleChild|/hasSingleChild>.
 {my ($node, $depth, @context) = @_;                                            # Node, depth, optional context
  return $node->isOnlyChild if $depth < 1;                                      # Validate depth
  my $c = 0;                                                                    # Depth count
  my $p;                                                                        # Current position
  for
   ($p = $node->first;                                                          # Descend firstly
    $p and $p->isOnlyChild and ++$c < $depth;
    $p = $p->first)
   {}
  $p ? $p->isOnlyChild(@context) : $p                                           # Check the context of the descendant single child
 }

sub isEmpty($@)                                                                 #CYU Confirm that the specified B<$node> is empty, that is: the specified B<$node> has no content, not even a blank string of text. To test for blank nodes, see L<isAllBlankText|/isAllBlankText>.
 {my ($node, @context) = @_;                                                    # Node, optional context
  return undef if @context and !$node->at(@context);                            # Not in specified context
  !$node->first ? $node : undef                                                 # If it has no first descendant it must be empty
 }
#a cutIfEmpty
#b <b><c/></b>
#c isEmpty
#c set id empty
#d Continue if we have no children.

sub hasContent($@)                                                              #CYU Confirm that the specified B<$node> has content. Return the specified node if it has content else return B<undef> if it does not.
 {my ($node, @context) = @_;                                                    # Node, optional context
  return undef if @context and !$node->at(@context);                            # Not in specified context
  $node->first ? $node : undef                                                  # If it has a first descendant it has content
 }
#a hasContent
#b <b><c/></b>
#c hasContent
#c set id yes
#d Continue if we have children.

sub over($$@)                                                                   #CYU Confirm that the string representing the tags at the level below the specified B<$node> match a regular expression where each pair of tags is separated by a single space. Use L<contentAsTags|/contentAsTags> to visualize the tags at the next level.
 {my ($node, $re, @context) = @_;                                               # Node, regular expression, optional context.
  return undef if @context and !$node->at(@context);                            # Not in specified context
  $node->contentAsTags =~ m/$re/ ? $node : undef
 }

sub over2($$@)                                                                  #CYU Confirm that the string representing the tags at the level below the specified B<$node> match a regular expression where each pair of tags have two spaces between them and the first tag is preceded by a single space and the last tag is followed by a single space.  This arrangement simplifies the regular expression used to detect combinations like p+ q? . Use L<contentAsTags2|/contentAsTags2> to visualize the tags at the next level.
 {my ($node, $re, @context) = @_;                                               # Node, regular expression, optional context.
  return undef if @context and !$node->at(@context);                            # Not in specified context
  $node->contentAsTags2 =~ m/$re/ ? $node : undef
 }
#a over
#b <b><c/><c/></b><b><c/></b>
#c over2 \A(\sc\s){2}\Z
#c set id over2
#d Continue if our children's tag match a regular expression.

sub overAllTags($@)                                                             #U Return the specified b<$node> if all of it's child nodes L<match|/atPositionMatch> the specified <@tags> else return B<undef>.
 {my ($node, @tags) = @_;                                                       # Node, tags.
  my @c = $node->contents;                                                      # Node contents
  while(@tags and @c)                                                           # Match node contents against tags
   {return undef unless atPositionMatch(-t shift @c, shift @tags);              # Continue unless we fail to match
   }
  return $node if @c == 0 and @tags == 0;                                       # Child nodes and tags matched exactly
  undef                                                                         # Wrong number of tags
 }

BEGIN{*oat=*overAllTags}

sub overFirstTags($@)                                                           #U Return the specified b<$node> if the first of it's child nodes L<match|/atPositionMatch> the specified <@tags> else return B<undef>.
 {my ($node, @tags) = @_;                                                       # Node, tags.
  my @c = $node->contents;                                                      # Node contents
  while(@tags and @c)                                                           # Match node contents against tags
   {return undef unless atPositionMatch(-t shift @c, shift @tags);              # Continue unless we fail to match
   }
  return $node if @c >= 0 and @tags == 0;                                       # The first child nodes match the specified tags
  undef                                                                         # Wrong number of tags
 }

BEGIN{*oft=*overFirstTags}

sub overLastTags($@)                                                            #U Return the specified b<$node> if the last of it's child nodes L<match|/atPositionMatch> the specified <@tags> else return B<undef>.
 {my ($node, @tags) = @_;                                                       # Node, tags.
  my @c = $node->contents;                                                      # Node contents
  while(@tags and @c)                                                           # Match node contents against tags
   {return undef unless atPositionMatch(-t pop @c, pop @tags);                  # Continue unless we fail to match
   }
  return $node if @c >= 0 and @tags == 0;                                       # The last child nodes match the specified tags
  undef                                                                         # Wrong number of tags
 }

BEGIN{*olt=*overLastTags}

sub matchAfter($$@)                                                             #CY Confirm that the string representing the tags following the specified B<$node> matches a regular expression where each pair of tags is separated by a single space. Use L<contentAfterAsTags|/contentAfterAsTags> to visualize these tags.
 {my ($node, $re, @context) = @_;                                               # Node, regular expression, optional context.
  return undef if @context and !$node->at(@context);                            # Not in specified context
  $node->contentAfterAsTags =~ m/$re/ ? $node : undef
 }

sub matchAfter2($$@)                                                            #CY Confirm that the string representing the tags following the specified B<$node> matches a regular expression where each pair of tags have two spaces between them and the first tag is preceded by a single space and the last tag is followed by a single space.  This arrangement simplifies the regular expression used to detect combinations like p+ q? Use L<contentAfterAsTags2|/contentAfterAsTags2> to visualize these tags.
 {my ($node, $re, @context) = @_;                                               # Node, regular expression, optional context.
  return undef if @context and !$node->at(@context);                            # Not in specified context
  $node->contentAfterAsTags2 =~ m/$re/ ? $node : undef
 }

sub matchBefore($$@)                                                            #CY Confirm that the string representing the tags preceding the specified B<$node> matches a regular expression where each pair of tags is separated by a single space. Use L<contentBeforeAsTags|/contentBeforeAsTags> to visualize these tags.
 {my ($node, $re, @context) = @_;                                               # Node, regular expression, optional context.
  return undef if @context and !$node->at(@context);                            # Not in specified context
  $node->contentBeforeAsTags =~ m/$re/ ? $node : undef
 }

sub matchBefore2($$@)                                                           #CY Confirm that the string representing the tags preceding the specified B<$node> matches a regular expression where each pair of tags have two spaces between them and the first tag is preceded by a single space and the last tag is followed by a single space.  This arrangement simplifies the regular expression used to detect combinations like p+ q?  Use L<contentBeforeAsTags2|/contentBeforeAsTags2> to visualize these tags.
 {my ($node, $re, @context) = @_;                                               # Node, regular expression, optional context.
  return undef if @context and !$node->at(@context);                            # Not in specified context
  $node->contentBeforeAsTags2 =~ m/$re/ ? $node : undef
 }

sub parentage($)                                                                #U Return a reference to an array of the nodes along the path from the root to the specified B<$Node> inclusive.
 {my ($node) = @_;                                                              # Node.
  my @p;                                                                        # Path
  for(my $p = $node; $p; $p = $p->parent)                                       # Go up
   {push @p, $p;                                                                # Save position
   }
  [reverse @p]                                                                  # Return path from root
 }

BEGIN{*p=*parentage}

sub path($)                                                                     #U Return a list of strings representing the path to a node from the root of the parse tree which can then be reused by L<go|/go> to retrieve the node as long as the structure of the L<parse|/parse> tree has not changed along the path.
 {my ($node) = @_;                                                              # Node.
  my @p;                                                                        # Path
  for(my $p = $node; $p and $p->parent; $p = $p->parent)                        # Go up
   {my $i = $p->index;                                                          # Position in parent index
    push @p, $i if $i;                                                          # Save position unless default
    push @p, $p->tag;                                                           # Save index
   }
  reverse @p                                                                    # Return path from root
 }

sub pathString($)                                                               #bU Return a string representing the L<path|/path> to the specified B<$node> from the root of the parse tree.
 {my ($node) = @_;                                                              # Node.
  join ' ', path($node)                                                         # String representation
 }

#D2 Match                                                                       # Locate adjacent nodes that match horizontally and vertically

sub an($$@)                                                                     #CU Return the next node if the specified B<$node> has the tag specified by B<$current> and the next node is in the specified B<@context>.
 {my ($node, $current, @context) = @_;                                          # Node, tag node must match, optional context of the next node.
  return undef unless $node->at($current);                                      # Check node has the right tag
  $node->next(@context)                                                         # Next node if it matches the context else B<undef>
 }

sub ap($$@)                                                                     #CU Return the previous node if the specified B<$node> has the tag specified by B<$current> and the previous node is in the specified B<@context>.
 {my ($node, $current, @context) = @_;                                          # Node, tag node must match, optional context of the previous node.
  return undef unless $node->at($current);                                      # Check node has the right tag
  $node->prev(@context)                                                         # Previous node if it matches the context else B<undef>
 }

sub apn($$$@)                                                                   #KU Return (previous node, next node) if the B<$previous> and B<$current> nodes have the specified tags and the next node is in the specified B<@context> else return B<()>.  The specified B<@context> must have at least one element otherwise B<()> is returned.
 {my ($node, $prev, $current, @context) = @_;                                   # Current node, tag for the previous node, tag for specified node, context for the next node.
  return () if !@context or !$node->at($current) or                             # Check context
                $node->isLast or $node->isFirst;                                # Check existence of surrounding nodes
  my $p = $node->prev($prev);                                                   # Previous node
  my $n = $node->next(@context);                                                # Next node
  return ($p, $n) if $p and $n;                                                 # Successful match
  ()                                                                            # Match failed
 }

sub matchesFirst($@)                                                            #U Return the specified B<$node> if its children L<match|/atPositionMatch> the specified <@sequence> forwards from the first child else return B<undef>.
 {my ($node, @sequence) = @_;                                                   # Node, sequence.
  my @c = $node->contents;                                                      # Child nodes
  while(@sequence and @c)                                                       # Match node contents against tags
   {return undef unless atPositionMatch(-t shift @c, shift @sequence);          # Continue unless we fail to match
   }
  return $node unless @sequence;                                                # The following nodes match the specified tags
  undef                                                                         # Wrong number of tags
 }

sub matchesLast($@)                                                             #U Return the specified B<$node> if its children L<match|/atPositionMatch> the specified <@sequence> backwards from the last child else return B<undef>.
 {my ($node, @sequence) = @_;                                                   # Node, sequence.
  my @c = reverse $node->contents;                                              # Child nodes
  while(@sequence and @c)                                                       # Match node contents against tags
   {return undef unless atPositionMatch(-t shift @c, shift @sequence);          # Continue unless we fail to match
   }
  return $node unless @sequence;                                                # The following nodes match the specified tags
  undef                                                                         # Wrong number of tags
 }

sub matchesNext($@)                                                             #U Return the specified B<$node> if its following siblings L<match|/atPositionMatch> the specified <@sequence> else return B<undef>.
 {my ($node, @sequence) = @_;                                                   # Node, sequence.
  my @c = $node->contentAfter;                                                  # Following node
  while(@sequence and @c)                                                       # Match node contents against tags
   {return undef unless atPositionMatch(-t shift @c, shift @sequence);          # Continue unless we fail to match
   }
  return $node unless @sequence;                                                # The following nodes match the specified tags
  undef                                                                         # Wrong number of tags
 }

sub matchesPrev($@)                                                             #U Return the specified B<$node> if the siblings before $node L<match|/atPositionMatch> the specified <@sequence> with the first element of @sequence nearest to $node and the last element furthest else return B<undef>.
 {my ($node, @sequence) = @_;                                                   # Node, sequence.
  my @c = reverse $node->contentBefore;                                         # Prior nodes
  while(@sequence and @c)                                                       # Match node contents against tags
   {return undef unless atPositionMatch(-t shift @c, shift @sequence);          # Continue unless we fail to match
   }
  return $node unless @sequence;                                                # The prior nodes match the specified tags
  undef                                                                         # Wrong number of tags
 }

#D2 Child of, Parent of, Sibling of                                             # Nodes that are directly above, below or adjacent to another node.

sub parentOf($$@)                                                               #C Returns the specified B<$parent> node if it is the parent of the specified B<$child> node and the B<$parent> node is in the specified optional context.
 {my ($parent, $child, @context) = @_;                                          # Parent, child, optional context
  return undef if @context and !$parent->at(@context);                          # Check context
  return $parent if $child->parent == $parent;                                  # Check child has the parent as its parent
  undef                                                                         # Wrong parent
 }

sub childOf($$@)                                                                #C Returns the specified B<$child> node if it is a child of the specified B<$parent> node and the B<$child> node is in the specified optional context.
 {my ($child, $parent, @context) = @_;                                          # Child, parent, optional context
  return undef if @context and !$child->at(@context);                           # Check context
  return $child if $child->parent == $parent;                                   # Check child has the parent as its parent
  undef                                                                         # Wrong parent
 }

sub succeedingSiblingOf($$@)                                                    #C Returns the specified B<$child> node if it has the same parent as B<$sibling> and occurs after B<$sibling> and has the optionally specified context else returns B<undef>.
 {my ($child, $sibling, @context) = @_;                                         # Child, sibling thought to occur before child, optional context
  return undef if @context and !child->at(@context);                            # Check context
  return undef unless $child->parent == $sibling->parent;                       # Check child has the parent as its prior sibling
  $child->after($sibling);                                                      # Check child occurs after prior sibling
 }

sub precedingSiblingOf($$@)                                                     #C Returns the specified B<$child> node if it has the same parent as B<$sibling> and occurs before B<$sibling> and has the optionally specified context else returns B<undef>.
 {my ($child, $sibling, @context) = @_;                                         # Child, sibling thought to occur after child, optional context
  return undef if @context and !child->at(@context);                            # Check context
  return undef unless $child->parent == $sibling->parent;                       # Check child has the parent as its prior sibling
  $child->before($sibling);                                                     # Check child occurs after prior sibling
 }

#D1 Navigation                                                                  # Move around in the L<parse|/parse> tree.

sub go($@)                                                                      #IYU Return the node reached from the specified B<$node> via the specified L<path|/path>: (index positionB<?>)B<*> where index is the tag of the next node to be chosen and position is the optional zero based position within the index of those tags under the current node. Position defaults to zero if not specified. Position can also be negative to index back from the top of the index array. B<*> can be used as the last position to retrieve all nodes with the final tag.
 {my ($node, @path) = @_;                                                       # Node, search specification.
  my $p = $node;                                                                # Current node
  while(@path)                                                                  # Position specification
   {my $i = shift @path;                                                        # Index name
    return undef unless $p;                                                     # There is no node of the named type under this node
    reindexNode($p);                                                            # Create index for this node
    my $q = $p->indexes->{$i};                                                  # Index
    return undef unless defined $q;                                             # Complain if no such index
    if (@path)                                                                  # Position within index
     {if ($path[0] =~ /\A([-+]?\d+)\Z/)                                         # Numeric position in index from start
       {shift @path;
        $p = $q->[$1]
       }
      elsif (@path == 1 and $path[0] =~ /\A\*\Z/)                               # Final index wanted
       {return @$q;
       }
      else {$p = $q->[0]}                                                       # Step into first sub node by default
     }
    else {$p = $q->[0]}                                                         # Step into first sub node by default on last step
   }
  $p
 }
#a up
#b <b><c/><c><d/><d/><d/><d/></c><c/></b>
#c go c 1 d 2
#c set id arrived_here
#d Follow a path from the current node.
sub c($$)                                                                       #U Return an array of all the nodes with the specified tag below the specified B<$node>.
 {my ($node, $tag) = @_;                                                        # Node, tag.
  reindexNode($node);                                                           # Create index for this node
  my $c = $node->indexes->{$tag};                                               # Index for specified tags
  $c ? @$c : ()                                                                 # Contents as an array
 }

sub cText($)                                                                    #U Return an array of all the text nodes immediately below the specified B<$node>.
 {my ($node) = @_;                                                              # Node.
  reindexNode($node);                                                           # Create index for this node
  $node->c(cdata);                                                              # Index for text data
 }

sub findById($$)                                                                #U Find a node in the parse tree under the specified B<$node> with the specified B<$id>.
 {my ($node, $id) = @_;                                                         # Parse tree, id desired.
  my $i;                                                                        # Node found
  eval {$node->by(sub                                                           # Look for an instance of such a node
   {if ($_->idX eq $id) {$i = $_; die}                                          # Found the node - die to stop the search from going further
   })};
  $i                                                                            # Node found if any
 }

sub matchesNode($$@)                                                            # Return the B<$first> node if it matches the B<$second> node's tag and the specified B<@attributes> else return B<undef>.
 {my ($first, $second, @attributes) = @_;                                       # First node, second node, attributes to match on
  return undef unless -t $first eq -t $second;                                  # Check tags match
  my $f = $first->attributes;                                                   # Attributes for first node
  my $s = $second->attributes;                                                  # Attributes for second node
  for my $a(@attributes)
   {return undef unless defined($f->{$a}) and defined($s->{$a}) and
                                $f->{$a}  eq          $s->{$a};
   }
  $first                                                                        # Nodes match on specified attributes
 }

sub matchesSubTree($$@)                                                         # Return the B<$first> node if it L<matches|/matchesNode> the B<$second> node and the nodes under the first node match the corresponding nodes under the second node, else return B<undef>.
 {my ($first, $second, @attributes) = @_;                                       # First node, second node, attributes to match on
  return undef unless &matchesNode(@_);                                         # Check nodes match
  my @f = @$first;                                                              # Children for first node
  my @s = @$second;                                                             # Children for second node
  return undef unless @f == @s;                                                 # Wrong number of children
  while(@f)                                                                     # Match each child
   {return undef unless (shift @f)->matchesNode(shift @s, @attributes);         # Children match
   }
  $first                                                                        # Sub trees match
 }

sub findMatchingSubTrees($$@)                                                   # Find nodes in the parse tree whose sub tree matches the specified B<$subTree> excluding any of the specified B<$attributes>.
 {my ($node, $subTree, @attributes) = @_;                                       # Parse tree, parse tree to match, attributes to match on
  my @i;                                                                        # Node found
  my $t = -t $subTree;                                                          # Quick reject
  $node->by(sub                                                                 # Each node in the tree
   {return unless -t $_ eq $t;                                                  # Quick reject
    push @i, $_ if $_->matchesSubTree($subTree, @attributes);                   # Found a matching sub tree
   });
  @i                                                                            # Node found if any
 }

#D2 First                                                                       # Find nodes that are first amongst their siblings.

sub first($@)                                                                   #BCYU Return the first node below the specified B<$node> optionally checking the first node's context.  See L<addFirst|/addFirst> to ensure that an expected node is in position.
 {my ($node, @context) = @_;                                                    # Node, optional context.
  return $node->content->[0] unless @context;                                   # Return first node if no context specified
  my ($c) = $node->contents;                                                    # First node
  $c ? $c->at(@context) : undef;                                                # Return first node if in specified context
 }
#a
#b <b><c/></b><b><d/></b>
#c   at b
#c   first c
#c   set id firstChild
#d Go to our first child optionally checking its context.

sub firstn($$@)                                                                 #CU Return the B<$n>'th first node below the specified B<$node> optionally checking its context or B<undef> if there is no such node.  B<firstn(1)> is identical in effect to L<first|/first>.
 {my ($node, $N, @context) = @_;                                                # Node, number of times to go first, optional context.
  return undef if @context and !$node->at(@context);                            # Check the context if supplied
  for(1..$N)                                                                    # Go first the specified number of times
   {$node = $node->first;                                                       # Go first
    last unless $node;                                                          # Cannot go further
   }
  $node
 }

sub firstText($@)                                                               #CU Return the first node under the specified B<$node> if it is in the optional and it is a text node otherwise B<undef>.
 {my ($node, @context) = @_;                                                    # Node, optional context.
  return undef if @context and !$node->at(@context);                            # Check the context if supplied
  my $l = &first($node);                                                        # First node
  $l ? $l->isText : undef                                                       # Test whether the first node exists and is a text node
 }

sub firstTextMatches($$@)                                                       #CU Return the first node under the specified B<$node> if: it is a text mode; its text matches the specified regular expression; the specified B<$node> is in the optional specified context. Else return B<undef>.
 {my ($node, $match, @context) = @_;                                            # Node, regular expression the text must match, optional context of specified node.
  return undef if @context and !$node->at(@context);                            # Check context
  if (my $t = $node->firstText)                                                 # First node is text
   {return $t->matchesText($match);                                             # First text node matches the specified regular expression
   }
  undef                                                                         # First node is not text or does not match the specified regular expression
 }

sub firstBy($@)                                                                 #U Return a list of the first instance of each specified tag encountered in a post-order traversal from the specified B<$node> or a hash of all first instances if no tags are specified.
 {my ($node, @tags) = @_;                                                       # Node, tags to search for.
  my %tags;                                                                     # Tags found first
  $node->byReverse(sub {$tags{$_->tag} = $_});                                  # Save first instance of each node
  return %tags unless @tags;                                                    # Return hash of all tags encountered first unless @tags filter was specified
  map {$tags{$_}} @tags;                                                        # Nodes in the requested order
 }

sub firstDown($@)                                                               #U Return a list of the first instance of each specified tag encountered in a pre-order traversal from the specified B<$node> or a hash of all first instances if no tags are specified.
 {my ($node, @tags) = @_;                                                       # Node, tags to search for.
  my %tags;                                                                     # Tags found first
  $node->downReverse(sub {$tags{$_->tag} = $_});                                # Save first instance of each node
  return %tags unless @tags;                                                    # Return hash of all tags encountered first unless @tags filter was specified
  map {$tags{$_}} @tags;                                                        # Nodes in the requested order
 }

sub firstIn($@)                                                                 #YU Return the first child node matching one of the named tags under the specified parent node.
 {my ($node, @tags) = @_;                                                       # Parent node, child tags to search for.
  my %tags = map {$_=>1} @tags;                                                 # Hashify tags
  for($node->contents)                                                          # Search forwards through contents
   {return $_ if $tags{$_->tag};                                                # Find first tag with the specified name
   }
  return undef                                                                  # No such node
 }

sub firstNot($@)                                                                #U Return the first child node that does not match any of the named B<@tags> under the specified parent B<$node>. Return B<undef> if there is no such child node.
 {my ($node, @tags) = @_;                                                       # Parent node, child tags to avoid.
  my %tags = map {$_=>1} @tags;                                                 # Hashify tags
  for($node->contents)                                                          # Search forwards through contents
   {return $_ unless $tags{$_->tag};                                            # Find first tag that fails to match
   }
  return undef                                                                  # No such node
 }

sub firstInIndex($@)                                                            #CYU Return the specified B<$node> if it is first in its index and optionally L<at|/at> the specified context else B<undef>
 {my ($node, @context) = @_;                                                    # Node, optional context.
  return undef if @context and !$node->at(@context);                            # Check the context if supplied
  my $parent = $node->parent;                                                   # Parent
  return undef unless $parent;                                                  # The root node is not first in anything
  my @c = $parent->c($node->tag);                                               # Index containing node
  @c && $c[0] == $node ? $node : undef                                          # First in index ?
 }

sub firstOf($@)                                                                 #U Return an array of the nodes that are continuously first under their specified parent node and that match the specified list of tags.
 {my ($node, @tags) = @_;                                                       # Node, tags to search for.
  my %tags = map {$_=>1} @tags;                                                 # Hashify tags
  my @l;                                                                        # Matching last nodes
  for($node->contents)                                                          # Search through contents
   {return @l unless $tags{$_->tag};                                            # Nonmatching tag
    push @l, $_;                                                                # Save continuously matching tag in correct order
   }
  return @l                                                                     # All tags match
 }

sub firstWhile($@)                                                              #U Go first from the specified B<$node> and continue deeper firstly as long as each first child node matches one of the specified B<@tags>. Return the deepest such node encountered or else return B<undef> if no such node is encountered.
 {my ($node, @tags) = @_;                                                       # Node, tags to search for.
  my %tags = map {$_=>1} @tags;                                                 # Hashify tags
  my $p;                                                                        # Current position
  for(my $f = $node->first; $f and $tags{-t $f}; $f = $f->first) {$p = $f}      # Go ever firstly
  $p
 }

sub firstUntil($@)                                                              #CU Go first from the specified B<$node> and continue deeper firstly until a first child node matches the specified B<@context> or return B<undef> if there is no such node.  Return the first child of the specified B<$node> if no B<@context> is specified.
 {my ($node, @context) = @_;                                                    # Node, context to search for.
  for(my $p = $node->first; $p; $p = $p->first)                                 # Check each first child node below the B<$node>
   {return $p if $p->at(@context);                                              # Return the node if it matches the specified context
   }
  undef
 }

sub firstUntilText($@)                                                          #CU Go first from the specified B<$node> and continue deeper firstly until a text node is encountered whose parent matches the specified B<@context> or return B<undef> if there is no such node.
 {my ($node, @context) = @_;                                                    # Node, context to search for.
  for(my $p = $node->first; $p; $p = $p->first)                                 # Check each first child node below the B<$node>
   {return $p if $p->isText and $p->parent->at(@context);                       # Return the node if it is text and its parent matches the specified context
   }
  undef
 }

sub firstContextOf($@)                                                          #CYU Return the first node encountered in the specified context in a depth first post-order traversal of the L<parse|/parse> tree.
 {my ($node, @context) = @_;                                                    # Node, array of tags specifying context.
  my $x;                                                                        # Found node if found
  eval                                                                          # Trap the die which signals success
   {$node->by(sub                                                               # Traverse  L<parse|/parse> tree in depth first order
     {my ($o) = @_;
      if ($o->at(@context))                                                     # Does this node match the supplied context?
       {$x = $o;                                                                # Success
        die "success!";                                                         # Halt the search
       }
     });
   };
  confess $@ if $@ and  $@ !~ /success!/;                                       # Report any suppressed error messages at this point
  $x                                                                            # Return node found if we are still alive
 }

sub firstSibling($@)                                                            #CYU Return the first sibling of the specified B<$node> in the optional B<@context> else B<undef>
 {my ($node, @context) = @_;                                                    # Node, array of tags specifying context.
  return undef if @context and !$node->at(@context);                            # Not in specified context
  my $p = $node->parent;                                                        # Parent node
  $p->first                                                                     # Return first sibling
 }

#D2 Last                                                                        # Find nodes that are last amongst their siblings.

sub last($@)                                                                    #BCYU Return the last node below the specified B<$node> optionally checking the last node's context. See L<addLast|/addLast> to ensure that an expected node is in position.
 {my ($node, @context) = @_;                                                    # Node, optional context.
  return $node->content->[-1] unless @context;                                  # Return last node if no context specified
  my ($c) = reverse $node->contents;                                            # Last node
  $c ? $c->at(@context) : undef;                                                # Return last node if in specified context
 }
#a
#b <b><c/><d/></b><b><c/></b>
#c   at b
#c   last d
#c   set id lastChild
#d Go to our last child optionally checking its context.

sub lastn($$@)                                                                  #CU Return the B<$n>'th last node below the specified B<$node> optionally checking its context or B<undef> if there is no such node.  B<lastn(1)> is identical in effect to L<last|/last>.
 {my ($node, $N, @context) = @_;                                                # Node, number of times to go last, optional context.
  return undef if @context and !$node->at(@context);                            # Check the context if supplied
  for(1..$N)                                                                    # Go last the specified number of times
   {$node = $node->last;                                                        # Go last
    last unless $node;                                                          # Cannot go further
   }
  $node
 }

sub lastText($@)                                                                #CU Return the last node under the specified B<$node> if it is in the optional and it is a text node otherwise B<undef>.
 {my ($node, @context) = @_;                                                    # Node, optional context.
  return undef if @context and !$node->at(@context);                            # Check the context if supplied
  my $l = &last($node);                                                         # Last node
  $l ? $l->isText : undef                                                       # Test whether the first node exists and is a text node
 }

sub lastTextMatches($$@)                                                        #CU Return the last node under the specified B<$node> if: it is a text mode; its text matches the specified regular expression; the specified B<$node> is in the optional specified context. Else return B<undef>.
 {my ($node, $match, @context) = @_;                                            # Node, regular expression the text must match, optional context of specified  node.
  return undef if @context and !$node->at(@context);                            # Check context
  if (my $t = $node->lastText)                                                  # Last node is text
   {return $t->matchesText($match);                                             # Last text node matches the specified regular expression
   }
  undef                                                                         # Last node is not text or does not match the specified regular expression
 }

sub lastBy($@)                                                                  #U Return a list of the last instance of each specified tag encountered in a post-order traversal from the specified B<$node> or a hash of all last instances if no tags are specified.
 {my ($node, @tags) = @_;                                                       # Node, tags to search for.
  my %tags;                                                                     # Tags found first
  $node->by(sub {$tags{$_->tag} = $_});                                         # Save last instance of each node
  return %tags unless @tags;                                                    # Return hash of all tags encountered last unless @tags filter was specified
  map {$tags{$_}} @tags;                                                        # Nodes in the requested order
 }

sub lastDown($@)                                                                #U Return a list of the last instance of each specified tag encountered in a pre-order traversal from the specified B<$node> or a hash of all last instances if no tags are specified.
 {my ($node, @tags) = @_;                                                       # Node, tags to search for.
  my %tags;                                                                     # Tags found first
  $node->down(sub {$tags{$_->tag} = $_});                                       # Save last instance of each node
  return %tags unless @tags;                                                    # Return hash of all tags encountered last unless @tags filter was specified
  map {$tags{$_}} @tags;                                                        # Nodes in the requested order
 }

sub lastIn($@)                                                                  #YU Return the last child node matching one of the named tags under the specified parent node.
 {my ($node, @tags) = @_;                                                       # Parent node, child tags to search for.
  my %tags = map {$_=>1} @tags;                                                 # Hashify tags
  for(reverse $node->contents)                                                  # Search backwards through contents
   {return $_ if $tags{$_->tag};                                                # Find last tag with the specified name
   }
  return undef                                                                  # No such node
 }

sub lastNot($@)                                                                 #U Return the last child node that does not match any of the named B<@tags> under the specified parent B<$node>. Return B<undef> if there is no such child node.
 {my ($node, @tags) = @_;                                                       # Parent node, child tags to avoid.
  my %tags = map {$_=>1} @tags;                                                 # Hashify tags
  for(reverse $node->contents)                                                  # Search backwards through contents
   {return $_ unless $tags{$_->tag};                                            # Find last tag that fails to match
   }
  return undef                                                                  # No such node
 }

sub lastOf($@)                                                                  #U Return an array of the nodes that are continuously last under their specified parent node and that match the specified list of tags.
 {my ($node, @tags) = @_;                                                       # Node, tags to search for.
  my %tags = map {$_=>1} @tags;                                                 # Hashify tags
  my @l;                                                                        # Matching last nodes
  for(reverse $node->contents)                                                  # Search backwards through contents
   {return @l unless $tags{$_->tag};                                            # Nonmatching tag
    unshift @l, $_;                                                             # Save continuously matching tag in correct order
   }
  return
   @l                                                                           # All tags match
 }

sub lastInIndex($@)                                                             #CYU Return the specified B<$node> if it is last in its index and optionally L<at|/at> the specified context else B<undef>
 {my ($node, @context) = @_;                                                    # Node, optional context.
  return undef if @context and !$node->at(@context);                            # Check the context if supplied
  my $parent = $node->parent;                                                   # Parent
  return undef unless $parent;                                                  # The root node is not first in anything
  my @c = $parent->c($node->tag);                                               # Index containing node
  @c && $c[-1] == $node ? $node : undef                                         # Last in index ?
 }

sub lastContextOf($@)                                                           #CYU Return the last node encountered in the specified context in a depth first reverse pre-order traversal of the L<parse|/parse> tree.
 {my ($node, @context) = @_;                                                    # Node, array of tags specifying context.
  my $x;                                                                        # Found node if found
  eval                                                                          # Trap the die which signals success
   {$node->downReverse(sub                                                      # Traverse  L<parse|/parse> tree in depth first order
     {my ($o) = @_;
      if ($o->at(@context))                                                     # Does this node match the supplied context?
       {$x = $o;                                                                # Success
        die "success!";                                                         # Halt the search
       }
     });
   };
  confess $@ if $@ and  $@ !~ /success!/;                                       # Report any suppressed error messages at this point
  $x                                                                            # Return node found if we are still alive
 }

sub lastSibling($@)                                                             #CYU Return the last sibling of the specified B<$node> in the optional B<@context> else B<undef>
 {my ($node, @context) = @_;                                                    # Node, array of tags specifying context.
  return undef if @context and !$node->at(@context);                            # Not in specified context
  my $p = $node->parent;                                                        # Parent node
  $p->last                                                                      # Return last sibling
 }

sub lastWhile($@)                                                               #U Go last from the specified B<$node> and continue deeper lastly as long as each last child node matches one of the specified B<@tags>. Return the deepest such node encountered or else return B<undef> if no such node is encountered.
 {my ($node, @tags) = @_;                                                       # Node, tags to search for.
  my %tags = map {$_=>1} @tags;                                                 # Hashify tags
  my $p;                                                                        # Current position
  for(my $l = $node->last; $l and $tags{-t $l}; $l = $l->first) {$p = $l}       # Go ever lastly
  $p
 }

sub lastUntil($@)                                                               #CU Go last from the specified B<$node> and continue deeper lastly until a last child node matches the specified B<@context> or return B<undef> if there is no such node.  Return the last child of the specified B<$node> if no B<@context> is specified.
 {my ($node, @context) = @_;                                                    # Node, context to search for.
  for(my $p = $node->last; $p; $p = $p->last)                                   # Check each last child node below the B<$node>
   {return $p if $p->at(@context);                                              # Return the node if it matches the specified context
   }
  undef
 }

sub lastUntilText($@)                                                           #CU Go last from the specified B<$node> and continue deeper lastly until a last child text node matches the specified B<@context> or return B<undef> if there is no such node.
 {my ($node, @context) = @_;                                                    # Node, context to search for.
  for(my $p = $node->last; $p; $p = $p->last)                                   # Check each last child node below the B<$node>
   {return $p if $p->isText and $p->parent->at(@context);                       # Return the node if it matches the specified context
   }
  undef
 }

#D2 Next                                                                        # Find sibling nodes after the specified B<$node>.

sub next($@)                                                                    #BCYU Return the node next to the specified B<$node>, optionally checking the next node's context. See L<addNext|/addNext> to ensure that an expected node is in position.
 {my ($node, @context) = @_;                                                    # Node, optional context.
  return undef if $node->isLast;                                                # No node follows the last node at a level or the top most node
  my @c = $node->parent->contents;                                              # Content array of parent
  while(@c)                                                                     # Test until no more nodes left to test
   {my $c = shift @c;                                                           # Each node
    if ($c == $node)                                                            # Current node
     {my $n = shift @c;                                                         # Next node
      return undef if @context and !$n->at(@context);                           # Next node is not in specified context
      return $n;                                                                # Found node
     }
   }
  confess "Node not found in parent";                                           # Something wrong with parent/child relationship
 }
#a isFirst
#b <b/><c/><b/><d/>
#c   at   b
#c   next c
#c   set id c_after_b
#d Go to the next sibling optionally checking its context.

sub nextn($$@)                                                                  #CU Return the B<$n>'th next node after the specified B<$node> optionally checking its context or B<undef> if there is no such node.  B<nextn(1)> is identical in effect to L<next|/next>.
 {my ($node, $N, @context) = @_;                                                # Node, number of times to go next, optional context.
  return undef if @context and !$node->at(@context);                            # Check the context if supplied
  for(1..$N)                                                                    # Go next the specified number of times
   {$node = $node->next;                                                        # Go next
    last unless $node;                                                          # Cannot go further
   }
  $node
 }

sub nextText($@)                                                                #CU Return the node after the specified B<$node> if it is in the optional and it is a text node otherwise B<undef>.
 {my ($node, @context) = @_;                                                    # Node, optional context.
  return undef if @context and !$node->at(@context);                            # Check the context if supplied
  my $l = &next($node);                                                         # Next node
  $l ? $l->isText : undef                                                       # Test whether the first node exists and is a text node
 }

sub nextTextMatches($$@)                                                        #CU Return the next node to the specified B<$node> if: it is a text mode; its text matches the specified regular expression; the specified B<$node> is in the optional specified context. Else return B<undef>.
 {my ($node, $match, @context) = @_;                                            # Node, regular expression the text must match, optional context of specified node.
  return undef if @context and !$node->at(@context);                            # Check context
  if (my $t = $node->nextText)                                                  # Next node is text
   {return $t->matchesText($match);                                             # Next text node matches the specified regular expression
   }
  undef                                                                         # Next node is not text or does not match the specified regular expression
 }

sub nextIn($@)                                                                  #YU Return the nearest sibling after the specified B<$node> that matches one of the named tags or B<undef> if there is no such sibling node.
 {my ($node, @tags) = @_;                                                       # Node, tags to search for.
  my %tags = map {$_=>1} @tags;                                                 # Hashify tags
  my $parent = $node->parent;                                                   # Parent node
  return undef unless $parent;                                                  # No nodes follow the root node
  my @c = $parent->contents;                                                    # Search forwards through contents
  shift @c while @c and $c[0] != $node;                                         # Move up to starting node
  shift @c;                                                                     # Move over starting node
  for(@c)                                                                       # Each subsequent node
   {return $_ if $tags{$_->tag};                                                # Find first tag with the specified name in the remaining nodes
   }
  return undef                                                                  # No such node
 }

sub nextOn($@)                                                                  #U Step forwards as far as possible from the specified B<$node> while remaining on nodes with the specified tags. In scalar context return the last such node reached or the starting node if no such steps are possible. In array context return the start node and any following matching nodes.
 {my ($node, @tags) = @_;                                                       # Start node, tags identifying nodes that can be step on to context.
  return wantarray ? ($node) : $node if $node->isLast;                          # Easy case
  my $parent = $node->parent;                                                   # Parent node
  confess "No parent" unless $parent;                                           # Not possible on a root node
  my @c = $parent->contents;                                                    # Content
  shift @c while @c and $c[0] != $node;                                         # Position on current node
  confess "Node not found in parent" unless @c;                                 # Something wrong with parent/child relationship
  my %tags = map {$_=>1} @tags;                                                 # Hashify tags of acceptable commands
  if (wantarray)                                                                # Return node and following matching nodes if array wanted
   {my @a = (shift @c);
    push @a, shift @c while @c and $tags{$c[0]->tag};                           # Proceed forwards staying on acceptable tags
    @a                                                                          # Current node and matching following nodes
   }
  else
   {shift @c while @c > 1 and $tags{$c[1]->tag};                                # Proceed forwards but staying on acceptable tags
    return $c[0]                                                                # Current node or last acceptable tag reached while staying on acceptable tags
   }
 }

sub nextWhile($@)                                                               #U Go to the next sibling of the specified B<$node> and continue forwards while the tag of each sibling node matches one of the specified B<@tags>. Return the first sibling node that does not match else B<undef> if there is no such sibling.
 {my ($node, @tags) = @_;                                                       # Node, child tags to avoid.
  my %tags = map {$_=>1} @tags;                                                 # Hashify tags
  for($node->contentAfter)                                                      # Search forwards through siblings
   {return $_ unless $tags{$_->tag};                                            # Find first tag that fails to match
   }
  return undef                                                                  # No such node
 }

sub nextUntil($@)                                                               #U Go to the next sibling of the specified B<$node> and continue forwards until the tag of a sibling node matches one of the specified B<@tags>. Return the matching sibling node else B<undef> if there is no such sibling node.
 {my ($node, @tags) = @_;                                                       # Node, tags to look for.
  my %tags = map {$_=>1} @tags;                                                 # Hashify tags
  for($node->contentAfter)                                                      # Search forwards through following siblings
   {return $_ if $tags{$_->tag};                                                # Find next node that matches on of the supplied tags
   }
  undef                                                                         # No such node
 }

#D2 Prev                                                                        # Find sibling nodes before the specified B<$node>.

sub prev($@)                                                                    #BCYU Return the node before the specified B<$node>, optionally checking the previous node's context. See L<addLast|/addLast> to ensure that an expected node is in position.
 {my ($node, @context) = @_;                                                    # Node, optional context.
  return undef if $node->isFirst;                                               # No node follows the last node at a level or the top most node
  my @c = $node->parent->contents;                                              # Content array of parent
  while(@c)                                                                     # Test until no more nodes left to test
   {my $c = pop @c;                                                             # Each node
    if ($c == $node)                                                            # Current node
     {my $n = pop @c;                                                           # Prior node
      return undef if @context and !$n->at(@context);                           # Prior node is not in specified context
      return $n;                                                                # Found node
     }
   }
  confess "Node not found in parent";                                           # Something wrong with parent/child relationship
 }
#a
#b <b/><c/><b/><d/>
#c   at   d
#c   prev b
#c   set id b_before_d
#d Go to the previous sibling optionally checking its context.

sub prevText($@)                                                                #CU Return the node before the specified B<$node> if it is in the optional and it is a text node otherwise B<undef>.
 {my ($node, @context) = @_;                                                    # Node, optional context.
  return undef if @context and !$node->at(@context);                            # Check the context if supplied
  my $l = &prev($node);                                                         # Previous node
  $l ? $l->isText : undef                                                       # Test whether the first node exists and is a text node
 }

sub prevn($$@)                                                                  #CU Return the B<$n>'th previous node after the specified B<$node> optionally checking its context or B<undef> if there is no such node.  B<prevn(1)> is identical in effect to L<prev|/prev>.
 {my ($node, $N, @context) = @_;                                                # Node, number of times to go prev, optional context.
  return undef if @context and !$node->at(@context);                            # Check the context if supplied
  for(1..$N)                                                                    # Go previous the specified number of times
   {$node = $node->prev;                                                        # Go previous
    return undef unless $node;                                                  # Cannot go further
   }
  $node
 }
#a
#b <b/><c/><d/><e/>
#c   last
#c   prevn 2
#c   set id yes
#d Go backwards

sub prevTextMatches($$@)                                                        #CU Return the previous node to the specified B<$node> if: it is a text mode; its text matches the specified regular expression; the specified B<$node> is in the optional specified context. Else return B<undef>.
 {my ($node, $match, @context) = @_;                                            # Node, regular expression the text must match, optional context of specified node.
  return undef if @context and !$node->at(@context);                            # Check context
  if (my $t = $node->prevText)                                                  # Previous node is text
   {return $t->matchesText($match);                                             # Previous text node matches the specified regular expression
   }
  undef                                                                         # Previous node is not text or does not match the specified regular expression
 }

sub prevIn($@)                                                                  #YU Return the nearest sibling node before the specified B<$node> which matches one of the named tags or B<undef> if there is no such sibling node.
 {my ($node, @tags) = @_;                                                       # Node, tags to search for.
  my %tags = map {$_=>1} @tags;                                                 # Hashify tags
  my $parent = $node->parent;                                                   # Parent node
  return undef unless $parent;                                                  # No nodes follow the root node
  my @c = reverse $parent->contents;                                            # Reverse through contents
  shift @c while @c and $c[0] != $node;                                         # Move down to starting node
  shift @c;                                                                     # Move over starting node
  for(@c)                                                                       # Each subsequent node
   {return $_ if $tags{$_->tag};                                                # Find first tag with the specified name in the remaining nodes
   }
  return undef                                                                  # No such node
 }

sub prevOn($@)                                                                  #U Step backwards as far as possible while remaining on nodes with the specified tags. In scalar context return the last such node reached or the starting node if no such steps are possible. In array context return the start node and any preceding matching nodes.
 {my ($node, @tags) = @_;                                                       # Start node, tags identifying nodes that can be step on to context.
  return wantarray ? ($node) : $node if $node->isFirst;                         # Easy case
  my $parent = $node->parent;                                                   # Parent node
  confess "No parent" unless $parent;                                           # Not possible on a root node
  my @c = reverse $parent->contents;                                            # Content backwards
  shift @c while @c and $c[0] != $node;                                         # Position on current node
  confess "Node not found in parent" unless @c;                                 # Something wrong with parent/child relationship
  my %tags = map {$_=>1} @tags;                                                 # Hashify tags of acceptable commands
  if (wantarray)                                                                # Return node and following matching nodes if array wanted
   {my @a = (shift @c);
    push @a, shift @c while @c and $tags{$c[0]->tag};                           # Proceed forwards staying on acceptable tags
    @a                                                                          # Current node and matching following nodes
   }
  else
   {shift @c while @c > 1 and $tags{$c[1]->tag};                                # Proceed forwards but staying on acceptable tags
    return $c[0]                                                                # Current node or last acceptable tag reached while staying on acceptable tags
   }
 }

sub prevWhile($@)                                                               #U Go to the previous sibling of the specified B<$node> and continue backwards while the tag of each sibling node matches one of the specified B<@tags>. Return the first sibling node that does not match else B<undef> if there is no such sibling.
 {my ($node, @tags) = @_;                                                       # Parent node, child tags to avoid.
  my %tags = map {$_=>1} @tags;                                                 # Hashify tags
  for(reverse $node->contentBefore)                                             # Search backwards through siblings
   {return $_ unless $tags{$_->tag};                                            # Find first tag that fails to match
   }
  return undef                                                                  # No such node
 }

sub prevUntil($@)                                                               #U Go to the previous sibling of the specified B<$node> and continue backwards until the tag of a sibling node matches one of the specified B<@tags>. Return the matching sibling node else B<undef> if there is no such sibling node.
 {my ($node, @tags) = @_;                                                       # Node, tags to look for.
  my %tags = map {$_=>1} @tags;                                                 # Hashify tags
  for($node->contentBefore)                                                     # Search forwards through following siblings
   {return $_ if $tags{$_->tag};                                                # Find next node that matches on of the supplied tags
   }
  undef                                                                         # No such node
 }

#D2 Up                                                                          # Methods for moving up the L<parse|/parse> tree from a node.

sub top($@)                                                                     #CYU Return the top of the parse tree containing the current B<$node> after optionally checking that the $node is in the optional B<@context>.
 {my ($node, @context) = @_;                                                    # Start node, optional context
  return undef if @context and !$node->at(@context);                            # Check the context if supplied
  for (my $p = $node;; $p = $p->parent)                                         # Walk up the parse tree
   {return $p unless $p->parent;                                                # Continue up the parse tree unless we are at the top - if not, let us hope that Zorn's lemma applies soon rather than later
   }
 }

BEGIN {*root = *top}

#a
#b <b><c/></b>
#c top
#c set id top
#d Go to the top of the parse tree.

sub up($@)                                                                      #CYU Return the parent of the current node optionally checking the parent node's context or return B<undef> if the specified B<$node> is the root of the L<parse|/parse> tree.   See L<addWrapWith|/addWrapWith> to ensure that an expected node is in position.
 {my ($node, @context) = @_;                                                    # Start node, optional context of parent.
  return $node->parent unless @context;                                         # Parent with no context check
  my $p = $node->parent;
  $p->at(@context) ? $p : undef;                                                # Check context of parent
 }
#a
#b <b><c/></b>
#c   at c
#c   up
#c   set id above_c
#d Go up one level.

sub upn($$@)                                                                    #CU Go up the specified number of levels from the specified B<$node> and return the node reached optionally checking the parent node's context or B<undef> if there is no such node.L<upn(1)|/up> is identical in effect to L<up|/up>.  Or use L<ancestry|/ancestry> to get the path back to the root node.
 {my ($node, $levels, @context) = @_;                                           # Start node, number of levels to go up, optional context.
  for(my $c = 0; $node and $c < $levels; $node = $node->parent, ++$c) {}        # Number of levels move up
  return $node unless @context;                                                 # Return node reached unless context check required
  $node ? $node->at(@context) : undef;                                          # Check context
 }

sub upWhile($@)                                                                 #YU Go up one level from the specified B<$node> and then continue up while each node matches on of the specified <@tags>. Return the last matching node or B<undef> if no node matched any of the specified B<@tags>.
 {my ($node, @tags) = @_;                                                       # Start node, tags to match
  my %tags = map {$_=>1} @tags;                                                 # Hashify tags
  my $lastMatch;                                                                # Last good match
  for(my $p = $node->parent; $p; $p = $p->parent)                               # Go up
   {last unless $tags{-t $p};                                                   # Found an ancestor that does not match
    $lastMatch = $p;
   }
  $lastMatch                                                                    # Last good match
 }

sub upWhileFirst($@)                                                            #CU Move up from the specified B<$node> as long as each node is a first node or return B<undef> if the specified B<$node> is not a first node.
 {my ($node, @context) = @_;                                                    # Start node, optional context
  return undef if @context && !$node->at(@context) or !$node->isFirst;          # Check the context if supplied and that the node is first
  my $lastMatch = $node;                                                        # First node
  for(my $p = $node->parent; $p; $p = $p->parent)                               # Go up
   {return $lastMatch unless $p->isFirst;                                       # Return last node which was first
    $lastMatch = $p                                                             # Update last matching position
   }
  $lastMatch                                                                    # Root node matches
 }

sub upWhileLast($@)                                                             #CU Move up from the specified B<$node> as long as each node is a last node or return B<undef> if the specified B<$node> is not a last node.
 {my ($node, @context) = @_;                                                    # Start node, optional context
  return undef if @context && !$node->at(@context) or !$node->isLast;           # Check the context if supplied and that the node is last
  my $lastMatch = $node;                                                        # Last node
  for(my $p = $node->parent; $p; $p = $p->parent)                               # Go up
   {return $lastMatch unless $p->isLast;                                        # Return last node which was last
    $lastMatch = $p                                                             # Update last matching position
   }
  $lastMatch                                                                    # Root node matches
 }

sub upWhileIsOnlyChild($@)                                                      #CU Move up from the specified B<$node> as long as each node is an only child or return B<undef> if the specified B<$node> is not an only child.
 {my ($node, @context) = @_;                                                    # Start node, optional context
  return undef if @context && !$node->at(@context) or !$node->isOnlyChild;      # Check the context if supplied and that the node is an only child
  my $lastMatch = $node;                                                        # Last node
  for(my $p = $node->parent; $p; $p = $p->parent)                               # Go up
   {return $lastMatch unless $p->isOnlyChild;                                   # Return last node which was an only child
    $lastMatch = $p                                                             # Update last matching position
   }
  $lastMatch                                                                    # Root node matches
 }

sub upUntil($@)                                                                 #CYU Find the first node going up from B<$node> that matches the specified B<@context>. The first such node will be the specified $node if no @context is specified or the specified $node matches the specified @context>.
 {my ($node, @context) = @_;                                                    # Start node, context.
  for(my $p = $node; $p; $p = $p->parent)                                       # Go up
   {return $p if $p->at(@context);                                              # Return node which satisfies the condition
   }
  return undef                                                                  # Not found
 }
#a upUntil
#b <b><c><d/></c></b>
#c at d
#c upUntil c b
#c set id up
#d Go up while we are not in the specified context

sub upUntilFirst($@)                                                            #CU Move up from the specified B<$node> until we reach the root or a first node.
 {my ($node, @context) = @_;                                                    # Start node, optional context
  return undef if @context and !$node->at(@context);                            # Check the context if supplied
  for(my $p = $node; $p; $p = $p->parent)                                       # Go up
   {return $p if $p->isFirst;                                                   # Return first first node
   }
  undef                                                                         # This should not happen
 }

sub upUntilLast($@)                                                             #CU Move up from the specified B<$node> until we reach the root or a last node.
 {my ($node, @context) = @_;                                                    # Start node, optional context
  return undef if @context and !$node->at(@context);                            # Check the context if supplied
  for(my $p = $node; $p; $p = $p->parent)                                       # Go up
   {return $p if $p->isLast;                                                    # Return first last node
   }
  undef                                                                         # This should not happen
 }

sub upUntilIsOnlyChild($@)                                                      #CU Move up from the specified B<$node> until we reach the root or another only child.
 {my ($node, @context) = @_;                                                    # Start node, optional context
  return undef if @context and !$node->at(@context);                            # Check the context if supplied and that the node is an only child
  for(my $p = $node; $p; $p = $p->parent)                                       # Go up
   {return $p if $p->isOnlyChild;                                               # Return last node which was an only child
   }
  undef                                                                         # This should not happen
 }

sub upThru($@)                                                                  #YU Go up the specified path from the specified B<$node> returning the node at the top or B<undef> if no such node exists.
 {my ($node, @tags) = @_;                                                       # Start node, tags identifying path.
  while(@tags)                                                                  # Go up through the tags
   {$node = $node->parent;                                                      # Go up on level
    return undef unless $node and $node->at(shift @tags);                       # Failed to match next tag
   }
  $node                                                                         # Reached the top of the path
 }

#D2 down                                                                        # Methods for moving down through the L<parse|/parse> tree from a node.

sub downWhileFirst($@)                                                          #CU Move down from the specified B<$node> as long as each lower node is a first node.
 {my ($node, @context) = @_;                                                    # Start node, optional context
  return undef if @context and !$node->at(@context);                            # Check the context if supplied
  for(my $p = $node->first; $p; $p = $p->first)                                 # Go down firstly
   {return $p unless $p->first;                                                 # Return node unless there is another one below it
   }
  $node->isFirst                                                                # Leaf node
 }

BEGIN{*firstLeaf=*downWhileFirst}

sub downWhileLast($@)                                                           #CU Move down from the specified B<$node> as long as each lower node is a last node.
 {my ($node, @context) = @_;                                                    # Start node, optional context
  return undef if @context and !$node->at(@context);                            # Check the context if supplied
  for(my $p = $node->last; $p; $p = $p->last)                                   # Go down lastly
   {return $p unless $p->last;                                                  # Return node unless there is another one below it
   }
  $node->isLast                                                                 # Leaf node
 }

BEGIN{*lastLeaf=*downWhileLast}

sub downWhileHasSingleChild($@)                                                 #CU Move down from the specified B<$node> as long as it has a single child else return undef.
 {my ($node, @context) = @_;                                                    # Start node, optional context
  return undef if @context and !$node->at(@context);                            # Check the context if supplied
  my $q;
  for(my $p = $node; $p; $q = $p, $p = $p->first)
   {last unless $p->hasSingleChild;
   }
  $q
 }

#D1 Editing                                                                     # Edit the data in the L<parse|/parse> tree and change the structure of the L<parse|/parse> tree by L<wrapping and unwrapping|/Wrap and unwrap> nodes, by L<replacing|/Replace> nodes, by L<cutting and pasting|/Cut and Put> nodes, by L<concatenating|/Fusion> nodes, by L<splitting|/Fission> nodes, by adding new L<text|/Put as text> nodes or L<swapping|/swap> nodes.

sub change($$@)                                                                 #CIYU Change the name of the specified B<$node>, optionally  confirming that the B<$node> is in a specified context and return the B<$node>.
 {my ($node, $name, @context) = @_;                                             # Node, new name, optional context.
  return undef if @context and !$node->at(@context);                            # Not in specified context
  $node->tag = $name;                                                           # Change name
  if (my $parent = $node->parent) {$parent->indexNode}                          # Reindex parent
  $node
 }
#a
#b <b/>
#c change c b a
#d Change a tag.
BEGIN{*cc=*change}

sub changeKids($$@)                                                             #CU Change the names of all the immediate children of the specified B<$node>, if they match the optional B<@context>, to the specified B<$tag> and return the B<$node>.
 {my ($node, $name, @context) = @_;                                             # Node, new name, optional context.
  if (!@context)                                                                # No context - change all immediate children
   {$_->tag = $name for $node->contents;                                        # Change each node regardless
   }
  else                                                                          # Check context of each child before changing name
   {for my $c($node->contents)                                                  # Each immediate child
     {$c->tag = $name if $c->at(@context);                                      # Change node if in specified context
     }
   }
  $node->indexNode;                                                             # Reindex node
  $node                                                                         # Return node
 }

BEGIN{*ck=*change}

sub changeText($$$$@)                                                           #CU Change the content of the specified text B<$node> that matches a regular expression B<$rf> presented as a string to a string B<$rt> in the optional B<@context> and return the specified $node else return B<undef>. For a more efficient non unitary method see L<editText>.
 {my ($node, $rf, $rt, $flags, @context) = @_;                                  # Text node, from re, to string, re flags, optional context
  confess "Parameter 2 should be a string" if ref($rf);                         # Check usage
  return undef unless $node->isText(@context);                                  # Check that this is a text node in the specified context.
  my $t = $node->text;                                                          # Text to change
  my $c = qq(\$t =~ s($rf) ($rt)$flags);                                        # Create regular expression
  my $r = eval qq(\$t =~ s($rf) ($rt)$flags);                                   # Evaluate regular expression
  confess "$@\n" if $@;                                                         # Confess to failures
  $node->text = $t;                                                             # Update text
  $r ? $node : undef                                                            # Return node on success or undef on fail
 }
#a editText
#b <b>ababa</b>
#c changeText b(.)b $1C$1 s
#d Change text using a regular expression, target string and flags.

sub changeTextToSpace($$@)                                                      #CU Change each instance of the content of the specified text B<$node> that matches a regular expression B<$re> to one space and return the specified $node else return B<undef>.
 {my ($node, $re, @context) = @_;                                               # Text node, regular expression, optional context
  return undef unless $node->isText(@context);                                  # Check that this is a text node in the specified context.
  $node->text =~ s($re) ( )gs ? $node : undef                                   # Change text to spaces
 }
#a
#b <b>ababa</b>
#c changeTextToSpace b
#d Change text to space using a regular expression.

sub dupPutNext($@)                                                              #CU Duplicate the specified B<$tree> in the optional B<@context>, place the new tree next after $tree and return the root of the new tree on success else B<undef>.
 {my ($tree, @context) = @_;                                                    # Node, optional context.
  return undef if @context and !$tree->at(@context);                            # Not in specified context
  putNext($tree, clone($tree));                                                 # Place new node and return it
 }

BEGIN{*r=*dupPutNext}                                                           # As used in editWithPerl

sub dupPutPrev($@)                                                              #CU Duplicate the specified B<$tree> in the optional B<@context>, place the new tree before $tree and return the root of the new tree on success else B<undef>.
 {my ($tree, @context) = @_;                                                    # Node, optional context.
  return undef if @context and !$tree->at(@context);                            # Not in specified context
  putPrev($tree, clone($tree));                                                 # Place new node and return it
 }

sub dupPutNextN($$@)                                                            #CU Duplicate the specified B<$tree> B<$N> times in the optional B<@context>, placing each copy after $tree and return the last new node created on success else B<undef>.
 {my ($node, $N, @context) = @_;                                                # Node, number of duplications, optional context.
  return undef if @context and !$node->at(@context);                            # Not in specified context
  return undef unless my $parent = $node->parent;                               # Cannot duplicate the root node
  my $r = $node->renew;                                                         # Make a copy of the node and its descendants
  my $f = freeze($r);                                                           # Freeze parse tree restricted to node and its contents
  my $c = $parent->content;                                                     # Content array of parent
  my $i = $node->position;                                                      # Position in content array
  my @n = map {my $r = thaw($f); $r->parent = $parent; $r} 1..$N;               # New nodes after original node - copy on write might be useful
  splice(@$c, $i+1, 0, @n);                                                     # Insert new nodes after original node
  $parent->indexNode;                                                           # Rebuild indices for parent
 }

BEGIN{*rN=*dupPutNextN}

our $selectionStart;                                                            # Selection starts here
our $selectionEnd;                                                              # Selection ends here

sub setSelectionStart($@)                                                       #CU Set the selection to start at the specified B<$node> in the optional B<@context> and return the specified B<$node> on success else B<undef>..
 {my ($node, @context) = @_;                                                    # Node, optional context.
  return undef if @context and !$node->at(@context);                            # Not in specified context
  $selectionStart = $node;                                                      # Start selection
 }

BEGIN{*ss=*setSelectionStart}

sub setSelectionEnd($@)                                                         #CU Set the selection to end at the specified B<$node> in the optional B<@context> and return the specified B<$node> on success else B<undef>..
 {my ($node, @context) = @_;                                                    # Node, optional context.
  return undef if @context and !$node->at(@context);                            # Not in specified context
  $selectionEnd = $node;                                                        # End selection
 }

BEGIN{*se=*setSelectionEnd}

sub moveSelectionFirst($@)                                                      #CU Move the current selection (if there is one) so that it is first under the specified B<$node> in the optional B<@context> and return the specified B<$node> on success else B<undef>.
 {my ($node, @context) = @_;                                                    # Node, optional context.
  return undef if @context and !$node->at(@context);                            # Not in specified context
  return undef unless $selectionStart || $selectionEnd;                         # No selection
  my $s = $selectionStart || $selectionEnd;                                     # Selection start
  my $e = $selectionEnd   || $selectionStart;                                   # Selection end
  $s->moveBlockFirst($e, $node);                                                # Move selection
  $s->putFirstAsComment(q(start));                                              # Show selection start
  $e->putLastAsComment(q(end));                                                 # Show selection end
  $node;                                                                        # Success
 }

BEGIN{*mf=*moveSelectionFirst}

sub moveSelectionAfter($@)                                                      #CU Move the current selection (if there is one) after the specified B<$node> in the optional B<@context> and return the specified B<$node> on success else B<undef>.
 {my ($node, @context) = @_;                                                    # Node, optional context.
  return undef if @context and !$node->at(@context);                            # Not in specified context
  return undef unless $selectionStart || $selectionEnd;                         # No selection
  my $s = $selectionStart || $selectionEnd;                                     # Selection start
  my $e = $selectionEnd   || $selectionStart;                                   # Selection end
  $s->moveBlockAfter($e, $node);                                                # Move selection
  $node;                                                                        # Success
 }

BEGIN{*ma=*moveSelectionAfter}

sub moveSelectionBefore($@)                                                     #CU Move the current selection (if there is one) before the specified B<$node> in the optional B<@context> and return the specified B<$node> on success else B<undef>.
 {my ($node, @context) = @_;                                                    # Node, optional context.
  return undef if @context and !$node->at(@context);                            # Not in specified context
  return undef unless $selectionStart || $selectionEnd;                         # No selection
  my $s = $selectionStart || $selectionEnd;                                     # Selection start
  my $e = $selectionEnd   || $selectionStart;                                   # Selection end
  $s->moveBlockBefore($e, $node);                                               # Move selection
  $node;                                                                        # Success
 }

BEGIN{*mb=*moveSelectionBefore}

sub moveSelectionLast($@)                                                       #CU Move the current selection (if there is one) so that it is last under the specified B<$node> in the optional B<@context> and return the specified B<$node> on success else B<undef>.
 {my ($node, @context) = @_;                                                    # Node, optional context.
  return undef if @context and !$node->at(@context);                            # Not in specified context
  return undef unless $selectionStart || $selectionEnd;                         # No selection
  my $s = $selectionStart || $selectionEnd;                                     # Selection start
  my $e = $selectionEnd   || $selectionStart;                                   # Selection end
  $s->moveBlockLast($e, $node);                                                 # Move selection
  $node;                                                                        # Success
 }

BEGIN{*ml=*moveSelectionLast}

#D2 Cut and Put                                                                 # Cut and paste nodes in the L<parse|/parse> tree.

sub cut($@)                                                                     #CUI Cut out and return the specified B<$node> so that it can be reinserted else where in the L<parse|/parse> tree.
 {my ($node, @context) = @_;                                                    # Node to cut out, optional context
  return undef if @context and !$node->at(@context);                            # Not in specified context
  my $parent = $node->parent;                                                   # Parent node
  # confess "Already cut out" unless $parent;                                   # We have to let thing be cut out more than once or supply an isCutOut() method
  return $node unless $parent;                                                  # Uppermost node is already cut out
  my $c = $parent->content;                                                     # Content array of parent
  my $i = $node->position;                                                      # Position in content array
  splice(@$c, $i, 1);                                                           # Remove node
  $parent->indexNode;                                                           # Rebuild indices
  $node->disconnectLeafNode;                                                    # Disconnect node no longer in L<parse|/parse> tree
  $node                                                                         # Return node
 }
#a unwrap
#b <b>C</b>
#c cut b
#d Delete a node and all its contents.

BEGIN{*x=*cut}

our @saveLastCutOut;                                                            # Save the last node cut out on the cut out stack

sub cutFirst($@)                                                                #CU Cut out the first node below the specified B<$node> if it is in the optional B<@context> and push it on the cut out stack. Return $node on success else return B<undef>. The cut out node can be reinserted using one of the putCutOut(First|Last|Next|Prev) methods.
 {my ($node, @context) = @_;                                                    # Node, optional context
  return undef unless my $first = $node->first;                                 # First child node must exist
  return undef if @context and !$first->at(@context);                           # First child node not in specified context
  push @saveLastCutOut, $first->cut;                                            # Cut out node saving it on the cut out stack
  $node                                                                         # Return current node
 }

sub putCutOutFirst($@)                                                          #CU Pop the last node placed on the cut out stack by one of the cut(First|Last|Next|Prev) methods and place it first under the specified B<$node> if $node is in the optional B<@context>. Return $node on success else return B<undef>.
 {my ($node, @context) = @_;                                                    # Node, optional context
  return undef if @context and !$node->at(@context);                            # Node not in specified context
  return undef unless my $cut = pop @saveLastCutOut;                            # Last cut out node
  $node->putFirst($cut);                                                        # Insert cut out node
  $node                                                                         # Return current node
 }

sub cutLast($@)                                                                 #CU Cut out the last node below the specified B<$node> if it is in the optional B<@context> and push it on the cut out stack. Return $node on success else return B<undef>.  The cut out node can be reinserted using one of the putCutOut(First|Last|Next|Prev) methods.
 {my ($node, @context) = @_;                                                    # Node, optional context
  return undef unless my $last = $node->last;                                   # Last child node must exist
  return undef if @context and !$last->at(@context);                            # Last child node not in specified context
  push @saveLastCutOut, $last->cut;                                             # Cut out node saving it on the cut out stack
  $node                                                                         # Return current node
 }

sub putCutOutLast($@)                                                           #CU Pop the last node placed on the cut out stack by one of the cut(First|Last|Next|Prev) methods and place it last under the specified B<$node> if $node is in the optional B<@context>. Return $node on success else return B<undef>.
 {my ($node, @context) = @_;                                                    # Node, optional context
  return undef if @context and !$node->at(@context);                            # Node not in specified context
  return undef unless my $cut = pop @saveLastCutOut;                            # Last cut out node
  $node->putLast($cut);                                                         # Insert cut out node
  $node                                                                         # Return current node
 }

sub cutNext($@)                                                                 #CU Cut out the next node beyond the specified B<$node> if it is in the optional B<@context> and push it on the cut out stack. Return the current node on success else return B<undef>.  The cut out node can be reinserted using one of the putCutOut(First|Last|Next|Prev) methods.
 {my ($node, @context) = @_;                                                    # Node, optional context
  return undef unless my $next = $node->next;                                   # Next node must exist
  return undef if @context and !$next->at(@context);                            # Not in specified context
  push @saveLastCutOut, $next->cut;                                             # Cut out node saving it on the cut out stack
  $node                                                                         # Return current node
 }
#a cut
#b <b/><c/><d/>
#c first
#c cutNext c a
#c next d
#c putCutOutNext
#d Cut out the next node and reinsert it

sub putCutOutNext($@)                                                           #CU Pop the last node placed on the cut out stack by one of the cut(First|Last|Next|Prev) methods and place it next after the specified B<$node> if $node is in the optional B<@context>. Return $node on success else return B<undef>.
 {my ($node, @context) = @_;                                                    # Node, optional context
  return undef if @context and !$node->at(@context);                            # Node not in specified context
  return undef unless my $cut = pop @saveLastCutOut;                            # Last cut out node
  $node->putNext($cut);                                                         # Insert cut out node
  $node                                                                         # Return current node
 }

sub cutPrev($@)                                                                 #CU Cut out the previous node before the specified B<$node> if it is in the optional B<@context> and push it on the cut out stack. Return the current node on success else return B<undef>.  The cut out node can be reinserted using one of the putCutOut(First|Last|Next|Prev) methods.
 {my ($node, @context) = @_;                                                    # Node, optional context
  return undef unless my $prev = $node->prev;                                   # Previous node must exist
  return undef if @context and !$prev->at(@context);                            # Not in specified context
  push @saveLastCutOut, $prev->cut;                                             # Cut out node saving it on the cut out stack
  $node                                                                         # Return current node
 }
#a cut
#b <b/><c/><d/>
#c last
#c cutPrev c a
#c prev b
#c putCutOutPrev
#d Cut out the previous node and reinsert it

sub putCutOutPrev($@)                                                           #CU Pop the last node placed on the cut out stack by one of the cut(First|Last|Next|Prev) methods and place it before the specified B<$node> if $node is in the optional B<@context>. Return $node on success else return B<undef>.
 {my ($node, @context) = @_;                                                    # Node, optional context
  return undef if @context and !$node->at(@context);                            # Node not in specified context
  return undef unless my $cut = pop @saveLastCutOut;                            # Last cut out node
  $node->putPrev($cut);                                                         # Insert cut out node
  $node                                                                         # Return current node
 }

sub cutIfEmpty($@)                                                              #CU Cut out and return the specified B<$node> so that it can be reinserted else where in the L<parse|/parse> tree if it is empty.
 {my ($node, @context) = @_;                                                    # Node to cut out, optional context
  return undef if @context and !$node->at(@context);                            # Not in specified context
  return $node->cut if $node->isAllBlankText;                                   # Cut node if it has no content or all blank content
  undef                                                                         # Cannot cut out node
 }
#a cut unwrap
#b <b/><c/><b/><d>text</d>
#c cutIfEmpty
#d Remove a node if it is empty.

sub deleteContent($@)                                                           #CU Delete the content of the specified B<$node>.
 {my ($node, @context) = @_;                                                    # Node, optional context
  return undef if @context and !$node->at(@context);                            # Not in specified context
  my @c = $node->contents;                                                      # Content
  $_->parent = undef for @c;                                                    # Remove parent link from each child
  $node->content = [];                                                          # Delete content
  $node                                                                         # Return node
 }

sub putFirst($$@)                                                               #C Place a L<cut out|/cut> or L<new|/new> node at the front of the content of the specified B<$node> and return the new node. See L<putFirstCut|/putFirstCut> to cut and put first in one operation. See L<addFirst|/addFirst> to perform this operation conditionally.
 {my ($old, $new, @context) = @_;                                               # Original node, new node, optional context.
  return undef if @context and !$old->at(@context);                             # Not in specified context
  $new->parent and confess "Please cut out the node before moving it";          # The node must have be cut out first
  $new->parser == $new and $old->parser == $new and                             # Prevent a root node from being inserted into a sub tree
    confess "Recursive insertion attempted";
  $new->parser = $old->parser;                                                  # Assign the new node to the old parser
  unshift @{$old->content}, $new;                                               # Content array of original node
  $old->indexNode;                                                              # Rebuild indices for node
  $new                                                                          # Return the new node
 }

sub putFirstCut($$@)                                                            #C Cut out the B<$second> node, place it first under the B<$first> node and return the B<$second> node.
 {my ($first, $second, @context) = @_;                                          # First node, second node, optional context.
  $first->putFirst($second->cut, @context)                                      # Place second node relative to the first node if in the specified context and return the second node.
 }

sub putLast($$@)                                                                #CI Place a L<cut out|/cut> or L<new|/new> node last in the content of the specified B<$node> and return the new node.  See L<putLastCut|/putLastCut> to cut and put last in one operation.  See L<addLast|/addLast> to perform this operation conditionally.
 {my ($old, $new, @context) = @_;                                               # Original node, new node, optional context.
  return undef if @context and !$old->at(@context);                             # Not in specified context
  $new->parent and confess "Please cut out the node before moving it";          # The node must have be cut out first
  $new->parser == $new and $old->parser == $new and                             # Prevent a root node from being inserted into a sub tree
    confess "Recursive insertion attempted";
  $new->parser = $old->parser;                                                  # Assign the new node to the old parser
  push @{$old->content}, $new;                                                  # Content array of original node
  $old->indexNode;                                                              # Rebuild indices for node
  $new                                                                          # Return the new node
 }

sub putLastCut($$@)                                                             #C Cut out the B<$second> node, place it last under the B<$first> node and return the B<$second> node.
 {my ($first, $second, @context) = @_;                                          # First node, second node, optional context.
  $first->putLast($second->cut, @context)                                       # Place second node relative to the first node if in the specified context and return the second node.
 }

sub putNext($$@)                                                                #C Place a L<cut out|/cut> or L<new|/new> node just after the specified B<$node> and return the new node. See L<putNextCut|/putNextCut> to cut and put next in one operation.  See L<addNext|/addNext> to perform this operation conditionally.
 {my ($old, $new, @context) = @_;                                               # Original node, new node, optional context.
  return undef if @context and !$old->at(@context);                             # Not in specified context
  return undef unless my $parent = $old->parent;                                # Parent node
  $parent or confess "Cannot place a node after the outermost node";            # The originating node must have a parent
  $new->parent and confess "Please cut out the node before moving it";          # The node must have be cut out first
  $new->parser == $new and $old->parser == $new and                             # Prevent a root node from being inserted into a sub tree
    confess "Recursive insertion attempted";
  $new->parser = $old->parser;                                                  # Assign the new node to the old parser
  my $c = $parent->content;                                                     # Content array of parent
  my $i = $old->position;                                                       # Position in content array
  splice(@$c, $i+1, 0, $new);                                                   # Insert new node after original node
  $new->parent = $parent;                                                       # Return node
  $parent->indexNode;                                                           # Rebuild indices for parent
  $new                                                                          # Return the new node
 }

sub putNextCut($$@)                                                             #C Cut out the B<$second> node, place it after the B<$first> node and return the B<$second> node.
 {my ($first, $second, @context) = @_;                                          # First node, second node, optional context.
  $first->putNext($second->cut, @context)                                       # Place second node relative to the first node if in the specified context and return the second node.
 }

sub putPrev($$@)                                                                #C Place a L<cut out|/cut> or L<new|/new> node just before the specified B<$node> and return the new node.  See L<putPrevCut|/putPrevCut> to cut and put previous in one operation.  See L<addPrev|/addPrev> to perform this operation conditionally.
 {my ($old, $new, @context) = @_;                                               # Original node, new node, optional context.
  return undef if @context and !$old->at(@context);                             # Not in specified context
  return undef unless my $parent = $old->parent;                                # Parent node
  $parent or confess "Cannot place a node before the outermost node";           # The originating node must have a parent
  $new->parent and confess "Please cut out the node before moving it";          # The node must have be cut out first
  $new->parser == $new and $old->parser == $new and                             # Prevent a root node from being inserted into a sub tree
    confess "Recursive insertion attempted";
  $new->parser = $old->parser;                                                  # Assign the new node to the old parser
  my $c = $parent->content;                                                     # Content array of parent
  my $i = $old->position;                                                       # Position in content array
  splice(@$c, $i, 0, $new);                                                     # Insert new node before original node
  $new->parent = $parent;                                                       # Return node
  $parent->indexNode;                                                           # Rebuild indices for parent
  $new                                                                          # Return the new node
 }

sub putPrevCut($$@)                                                             #C Cut out the B<$second> node, place it before the B<$first> node and return the B<$second> node.
 {my ($first, $second, @context) = @_;                                          # First node, second node, optional context.
  $first->putPrev($second->cut, @context)                                       # Place second node relative to the first node if in the specified context and return the second node.
 }

#D2 Put Siblings or Contents                                                    # Move a node and its siblings up and down the parse tree.

sub putSiblingsFirst($@)                                                        #CU Move the siblings preceding the specified B<$node> in the optional B<@context> down one level and place them first under the specified B<$node> preceding any existing content.  Return the specified B<$node>.
 {my ($node, @context) = @_;                                                    # Node whose start should be moved, optional context of node.
  return undef if @context and !$node->at(@context);                            # Not in specified context
  for my $n(reverse $node->contentBefore)                                       # Each node preceding the specified node
   {$node->putFirstCut($n);                                                     # Place node first
   }
  $node                                                                         # Success
 }

sub putSiblingsLast($@)                                                         #CU Move the siblings following the specified B<$node> in the optional B<@context> down one level so that they are last under the specified B<$node> following any existing content. Return the specified B<$node>.
 {my ($node, @context) = @_;                                                    # Node whose start should be moved, optional context of node.
  return undef if @context and !$node->at(@context);                            # Not in specified context
  for my $n($node->contentAfter)                                                # Each node following the specified node
   {$node->putLastCut($n);                                                      # Place node last
   }
  $node                                                                         # Success
 }

sub putSiblingsAfterParent($@)                                                  #CU Move the specified B<$node> and its following siblings up one level and place them after the parent of the specified B<$node> if the specified B<$node> is in the optional B<@context>.  Return the specified B<$node> if the move was made successfully, else confess that the specified move is not possible.
 {my ($node, @context) = @_;                                                    # Node whose start should be moved, optional context of node.
  return undef if @context and !$node->at(@context);                            # Not in specified context
  confess "Cannot put after root of parse tree" unless my $p = $node->parent;   # Fail on root node
  for my $n(reverse @$p)                                                        # Each node from last to first under parent
   {$p->putNextCut($n);                                                         # Place node after parent
    last if $node == $n;                                                        # Stop when we reach the specified node
   }
  $node                                                                         # Success
 }

sub putSiblingsBeforeParent($@)                                                 #CU Move the specified B<$node> and its preceding siblings up one level and place them before the parent of the specified B<$node> if the specified B<$node> is in the optional B<@context>.  Return the specified B<$node> if the move was made successfully, else confess that the specified move is not possible.
 {my ($node, @context) = @_;                                                    # Node whose start should be moved, optional context of node.
  return undef if @context and !$node->at(@context);                            # Not in specified context
  confess "Cannot put before root of parse tree" unless my $p = $node->parent;  # Fail on root node
  for my $n($p->contents)                                                       # Each node from last to first under parent
   {$p->putPrevCut($n);                                                         # Place node after parent
    last if $node == $n;                                                        # Stop when we reach the specified node
   }
  $node                                                                         # Success
 }

sub putContentAfter($@)                                                         #CU Move the content of the specified B<$node> and place it after that node if that node is in the optional B<@context>.  Return the specified B<$node> or confess if the move is not possible.
 {my ($node, @context) = @_;                                                    # Node whose start should be moved, optional context of node.
  return undef if @context and !$node->at(@context);                            # Not in specified context
  confess "Cannot put content after root of parse tree" unless $node->parent;   # Fail on root node
  for my $n(reverse @$node)                                                     # Each content node from last to first
   {$node->putNextCut($n);                                                      # Move contained node after parent
   }
  $node                                                                         # Success
 }

sub putContentBefore($@)                                                        #CU Move the content of the specified B<$node> and place it before that node if that node is in the optional B<@context>.  Return the specified B<$node> or confess if the move is not possible.
 {my ($node, @context) = @_;                                                    # Node whose start should be moved, optional context of node.
  return undef if @context and !$node->at(@context);                            # Not in specified context
  confess "Cannot put content before root of parse tree" unless $node->parent;  # Fail on root node
  for my $n(my @c = @$node)                                                     # Each content node from first to last
   {$node->putPrevCut($n);                                                      # Move contained node before parent
   }
  $node                                                                         # Success
 }

#D2 Move                                                                        # Move nodes around in the L<parse|/parse> tree by cutting and pasting them.

sub moveFirst($@)                                                               #CU Move the specified node so that is is the first sibling under its parent. Returns the specified $node on success otherwise B<undef>.
 {my ($node, @context) = @_;                                                    # Node to  be moved, optional context of node.
  return undef if @context and !$node->at(@context);                            # Not in specified context
  return $node if $node->isFirst;                                               # Node is already in position
  return undef unless my $parent = $node->parent;                               # Parent node
  $parent->putFirstCut($node);                                                  # Move node
  $node                                                                         # Success
 }

sub moveLast($@)                                                                #CU Move the specified node so that is is the last sibling under its parent. Returns the specified $node on success otherwise B<undef>.
 {my ($node, @context) = @_;                                                    # Node to  be moved, optional context of node.
  return undef if @context and !$node->at(@context);                            # Not in specified context
  return $node if $node->isLast;                                                # Node is already in position
  return undef unless my $parent = $node->parent;                               # Parent node
  $parent->putLastCut($node);                                                   # Move node
  $node                                                                         # Success
 }

sub moveStartFirst($@)                                                          #CU Move the start of a B<$node> to contain all of its preceding siblings as children. Returns B<$node> if the move was made successfully, else confess that the specified move is impossible.
 {my ($node, @context) = @_;                                                    # Node whose start should be moved, optional context of node.
  return undef if @context and !$node->at(@context);                            # Not in specified context

  if ($node->isFirst)                                                           # Node is already in position
   {return $node;
   }
  for my $p(reverse $node->contentBefore)                                       # Each node before the start tag
   {$node->putFirstCut($p);                                                     # Move node
   }
  $node                                                                         # Success
 }

sub moveStartAfter($$@)                                                         #C Move the start end of a B<$node> to just after the specified B<$target> node assuming that the B<$target> node is either a preceding sibling or a child of B<$node>. Returns B<$node> if the move was made successfully, else confess that the specified move is impossible.
 {my ($node, $to, @context) = @_;                                               # Node whose start should be moved, target node, optional context of node.
  return undef if @context and !$node->at(@context);                            # Not in specified context

  if ($node->prev && $node->prev == $to)                                        # Node is already in position
   {return $node;
   }
  elsif ($to->precedingSiblingOf($node))                                        # Target is outside and before node, so move start up to it
   {for my $p(reverse $node->contentBefore)                                     # Each node before the start node up to the target
     {last if $p == $to;                                                        # Reached target
      $node->putFirstCut($p);                                                   # Move node
     }
    return $node;                                                               # Success
   }
  elsif ($to->parent and $to->parent == $node)                                  # Contained by node
   {for my $p((reverse $to->contentBefore), $to)                                # Nodes from target
     {$node->putPrevCut($p);                                                    # Move node
     }
    return $node;                                                               # Success
   }
  confess "To node is not a child or preceding sibling of node";                # Cannot do the requested operation
 }

sub moveStartBefore($$@)                                                        #C Move the start of a B<$node> to just before the specified B<$target> node assuming that the B<$target> node is either a preceding sibling or a child of B<$node>. Returns B<$node> if the move was made successfully, else confess that the specified move is impossible.
 {my ($node, $to, @context) = @_;                                               # Node whose start should be moved, target node, optional context of node.
  return undef if @context and !$node->at(@context);                            # Not in specified context

  if ($node->first && $node->first == $to)                                       # Node is already in position
   {return $node;
   }
  elsif ($to->precedingSiblingOf($node))                                        # Target is outside and before node, so move start up to it
   {for my $p(reverse $node->contentBefore)                                     # Each node after the end tag up to the target
     {$node->putFirstCut($p);                                                   # Move node
      last if $p == $to;                                                        # Reached target
     }
    return $node;                                                               # Success
   }
  elsif ($to->parent and $to->parent == $node)                                  # Contained by node
   {for my $p($to->contentBefore)                                               # Nodes from target
     {$node->putPrevCut($p);                                                    # Move node
     }
    return $node;                                                               # Success
   }
  confess "To node is not a child or preceding sibling of node";                # Cannot do the requested operation
 }

sub moveEndLast($@)                                                             #CU Move the end of a B<$node> to contain all of its following siblings as children. Returns B<$node> if the move was made successfully, else confess that the specified move is impossible.
 {my ($node, @context) = @_;                                                    # Node whose end should be moved, optional context of node.
  return undef if @context and !$node->at(@context);                            # Not in specified context

  if ($node->isLast)                                                            # Node is already in position
   {return $node;
   }
  for my $p($node->contentAfter)                                                # Each node after the end tag
   {$node->putLastCut($p);                                                      # Move node
   }
  return $node;                                                                 # Success
 }

sub moveEndAfter($$@)                                                           #CI Move the end of a B<$node> to just after the specified B<$target> node assuming that the B<$target> node is either a subsequent sibling or a child of B<$node>. Returns B<$node> if the move was made successfully, else confess that the specified move is impossible.
 {my ($node, $to, @context) = @_;                                               # Node whose end should be moved, target node, optional context of node.
  return undef if @context and !$node->at(@context);                            # Not in specified context

  if ($node->last && $node->last == $to)                                        # Node is already in position
   {return $node;
   }
  elsif ($to->succeedingSiblingOf($node))                                       # Target is outside and beyond node, so move end down to it
   {for my $p($node->contentAfter)                                              # Each node after the end node up to the target
     {$node->putLastCut($p);                                                    # Move node
      last if $p == $to;                                                        # Reached target
     }
    return $node;                                                               # Success
   }
  elsif ($to->parent and $to->parent == $node)                                  # Contained by node
   {for my $p(reverse $to->contentAfter)                                        # Nodes from target
     {$node->putNextCut($p);                                                    # Move node
     }
    return $node;                                                               # Success
   }
  confess "To node is not a child or succeeding sibling of node";               # Cannot do the requested operation
 }

sub moveEndBefore($$@)                                                          #C Move the end of a B<$node> to just before the specified B<$target> node assuming that the B<$target> node is either a subsequent sibling or a child of B<$node>. Returns B<$node> if the move was made successfully, else confess that the specified move is impossible.
 {my ($node, $to, @context) = @_;                                               # Node whose end should be moved, target node, optional context of node.
  return undef if @context and !$node->at(@context);                            # Not in specified context

  if ($node->next && $node->next == $to)                                        # Node is already in position
   {return $node;
   }
  elsif ($to->succeedingSiblingOf($node))                                       # Target is outside and beyond node, so move end down to it
   {for my $p($node->contentAfter)                                              # Each node after the end tag up to the target
     {last if $p == $to;                                                        # Reached target
      $node->putLastCut($p);                                                    # Move node
     }
    return $node;                                                               # Success
   }
  elsif ($to->parent and $to->parent == $node)                                  # Contained by node
   {for my $p((reverse $to->contentAfter), $to)                                 # Nodes from target
     {$node->putNextCut($p);                                                    # Move node
     }
    return $node;                                                               # Success
   }
  confess "To is not a child or succeeding sibling of node";                    # Cannot do the requested operation
 }

# Move a single node                                                            # Move a single node into a new position close to its current position.

sub putNextFirstCut($@)                                                         #CU Move the specified B<$node> so it is first in the next node with the optional context. Return $node on success else return B<undef> on failure.
 {my ($node, @context) = @_;                                                    # Node, context
  return undef unless my $next = $node->next;                                   # The next node
  return undef if @context and !$next->at(@context);                            # Not in specified context
  $next->putFirstCut($node);                                                    # Move node into position
 }

sub putNextFirstCut2($@)                                                        #CU Move the specified B<$node> so it is first in the first node with the specified optional B<@context> of the next node. Return $node on success else return B<undef> on failure.
 {my ($node, @context) = @_;                                                    # Node, context
  return undef unless my $next = $node->next;                                   # The next node
  return undef unless my $first = $next->first;                                 # The First node under the next node
  return undef if @context and !$first->at(@context);                           # Not in specified context
  $first->putFirstCut($node);                                                   # Move node into position
 }

sub putPrevLastCut($@)                                                          #CU Move the specified B<$node> so it is last in the preceding node with the optional context. Return $node on success else return B<undef> on failure.
 {my ($node, @context) = @_;                                                    # Node, context
  return undef unless my $prev = $node->prev;                                   # The previous node
  return undef if @context and !$prev->at(@context);                            # Not in specified context
  $prev->putLastCut($node);                                                     # Move node into position
 }
#a
#b <b/><c/>
#c putPrevLastCut b
#d Put last in the previous node.
sub putPrevLastCut2($@)                                                         #CU Move the specified B<$node> so it is last in the last node with the specified optional context of the preceding node. Return the specified $node on success else return B<undef> on failure.
 {my ($node, @context) = @_;                                                    # Node, context
  return undef unless my $prev = $node->prev;                                   # The previous node
  return undef unless my $last = $prev->last;                                   # The last node under the previous node
  return undef if @context and !$last->at(@context);                            # Not in specified context
  $last->putLastCut($node);                                                     # Move node into position
 }

sub putUpNextCut($@)                                                            #CU Move the specified B<$node>, in the optional B<@context>, which must be last under its parent, so that it is next after its parent. Return $node on success else return B<undef> on failure.
 {my ($node, @context) = @_;                                                    # Node, context
  return undef if @context and !$node->at(@context);                            # Not in specified context
  return undef unless $node->isLast;                                            # The node must be last
  return undef unless my $parent = $node->parent;                               # The parent node
  $parent->putNextCut($node);                                                   # Move node into position
 }
#a putPrevLastCut
#b <b><c/></b>
#c putUpNextCut c
#d Cut out a last child and place it after its parent.

sub putUpNextCut2($@)                                                           #CU Move the specified B<$node>, in the optional B<@context>, if $node is last after its parent which must also be last under its parent, so that $node is next after its grandparent. Return $node on success else return B<undef> on failure.
 {my ($node, @context) = @_;                                                    # Node, context
  return undef if @context and !$node->at(@context);                            # Not in specified context
  return undef unless $node->isLast;                                            # The node must be last
  return undef unless my $parent = $node->parent;                               # Not in specified context
  return undef unless $parent->isLast;                                          # The parent must also be last
  return undef unless my $qarent = $parent->parent;                             # The parent parent node
  $qarent->putNextCut($node);                                                   # Move node into position
 }

sub putUpPrevCut($@)                                                            #CU Move the specified B<$node> in the optional B<@context>, if $node is first under its parent, so that it is prior to its parent. Return $node on success else return B<undef> on failure.
 {my ($node, @context) = @_;                                                    # Node, context
  return undef if @context and !$node->at(@context);                            # Not in specified context
  return undef unless $node->isFirst;                                            # The node must be last
  return undef unless my $parent = $node->parent;                               # The parent node
  $parent->putPrevCut($node);                                                   # Move node into position
 }

sub putUpPrevCut2($@)                                                           #CU Move the specified B<$node>, in the optional B<@context>, if $node is first under its parent which must also be first under its parent, so that $node is prior to its grandparent. Return the specified B<$node> on success else return B<undef> on failure.
 {my ($node, @context) = @_;                                                    # Node, context
  return undef if @context and !$node->at(@context);                            # Not in specified context
  return undef unless $node->isFirst;                                           # The node must be first
  return undef unless my $parent = $node->parent;                               # Not in specified context
  return undef unless $parent->isFirst;                                         # The parent must also be first
  return undef unless my $qarent = $parent->parent;                             # The parent parent node
  $qarent->putPrevCut($node);                                                   # Move node into position
 }

# Move a block                                                                  # Move a block of siblings to a new position

sub moveBlockFirst($$$@)                                                        #C Move the block of siblings starting with B<$start> in the optional context and ending with B<$end> first under the specified B<$parent> node. Returns B<$node> if the move was made successfully, else confess that the specified move is impossible.
 {my ($start, $end, $parent, @context) = @_;                                    # First sibling, last sibling, parent to move first under, optional context
  return undef if @context and !$start->at(@context);                           # Not in specified context
  my $w = $start->wrapTo($end, q(grab));                                        # Grab the nodes in the block
  $parent->putFirstCut($w);                                                     # Move block
  $w->unwrap;                                                                   # Unwrap block in new position
  $start                                                                        # Success
 }

sub moveBlockLast($$$@)                                                         #C Move the block of siblings starting with B<$start> in the optional context and ending with B<$end> last under the specified B<$parent> node. Returns B<$node> if the move was made successfully, else confess that the specified move is impossible.
 {my ($start, $end, $parent, @context) = @_;                                    # First sibling, last sibling, parent to move last under, optional context
  return undef if @context and !$start->at(@context);                           # Not in specified context
  my $w = $start->wrapTo($end, q(grab));                                        # Grab the nodes in the block
  $parent->putLastCut($w);                                                      # Move block
  $w->unwrap;                                                                   # Unwrap block in new position
  $start                                                                        # Success
 }

sub moveBlockAfter($$$@)                                                        #C Move the block of siblings starting with B<$start> in the optional context and ending with B<$end> after the specified B<$after> node. Returns B<$node> if the move was made successfully, else confess that the specified move is impossible.
 {my ($start, $end, $after, @context) = @_;                                     # First sibling, last sibling, node to move after, optional context
  return undef if @context and !$start->at(@context);                           # Not in specified context
  my $w = $start->wrapTo($end, q(grab));                                        # Grab the nodes in the block
  $after->putNextCut($w);                                                       # Move block
  $w->unwrap;                                                                   # Unwrap block in new position
  $start                                                                        # Success
 }

sub moveBlockBefore($$$@)                                                       #C Move the block of siblings starting with B<$start> in the optional context and ending with B<$end> before the specified B<$after> node. Returns B<$node> if the move was made successfully, else confess that the specified move is impossible.
 {my ($start, $end, $before, @context) = @_;                                    # First sibling, last sibling, node to move before, optional context
  return undef if @context and !$start->at(@context);                           # Not in specified context
  my $w = $start->wrapTo($end, q(grab));                                        # Grab the nodes in the block
  $before->putPrevCut($w);                                                      # Move block
  $w->unwrap;                                                                   # Unwrap block in new position
  $start                                                                        # Success
 }

sub moveBlockToLastFirst($$@)                                                   #C Move the siblings starting with B<$start> in the optional context first under the specified B<$parent> node. Returns B<$node> if the move was made successfully, else confess that the specified move is impossible.
 {my ($start, $parent, @context) = @_;                                          # Start sibling, parent to move first under, optional context
  return undef if @context and !$start->at(@context);                           # Not in specified context
  my $w = $start->wrapToLast(q(grab));                                          # Grab the nodes in the block
  $parent->putFirstCut($w);                                                     # Move block
  $w->unwrap;                                                                   # Unwrap block in new position
  $start                                                                        # Success
 }

sub moveBlockToLastLast($$@)                                                    #C Move the block of siblings starting with B<$start> in the optional context last under the specified B<$parent> node. Returns B<$node> if the move was made successfully, else confess that the specified move is impossible.
 {my ($start, $parent, @context) = @_;                                          # First sibling, parent to move last under, optional context
  return undef if @context and !$start->at(@context);                           # Not in specified context
  my $w = $start->wrapToLast(q(grab));                                          # Grab the nodes in the block
  $parent->putLastCut($w);                                                      # Move block
  $w->unwrap;                                                                   # Unwrap block in new position
  $start                                                                        # Success
 }

sub moveBlockToLastAfter($$@)                                                   #C Move the block of siblings starting with B<$start> in the optional context after the specified B<$after> node. Returns B<$node> if the move was made successfully, else confess that the specified move is impossible.
 {my ($start, $after, @context) = @_;                                           # First sibling, node to move after, optional context
  return undef if @context and !$start->at(@context);                           # Not in specified context
  my $w = $start->wrapToLast(q(grab));                                          # Grab the nodes in the block
  $after->putNextCut($w);                                                       # Move block
  $w->unwrap;                                                                   # Unwrap block in new position
  $start                                                                        # Success
 }

sub moveBlockToLastBefore($$@)                                                  #C Move the block of siblings starting with B<$start> in the optional context before the specified B<$after> node. Returns B<$node> if the move was made successfully, else confess that the specified move is impossible.
 {my ($start, $before, @context) = @_;                                          # First sibling, node to move before, optional context
  return undef if @context and !$start->at(@context);                           # Not in specified context
  my $w = $start->wrapToLast(q(grab));                                          # Grab the nodes in the block
  $before->putPrevCut($w);                                                      # Move block
  $w->unwrap;                                                                   # Unwrap block in new position
  $start                                                                        # Success
 }

sub moveBlockFromFirstFirst($$@)                                                #C Move the siblings starting with B<$start> in the optional context first under the specified B<$parent> node. Returns B<$node> if the move was made successfully, else confess that the specified move is impossible.
 {my ($start, $parent, @context) = @_;                                          # Start sibling, parent to move first under, optional context
  return undef if @context and !$start->at(@context);                           # Not in specified context
  my $w = $start->wrapFromFirst(q(grab));                                       # Grab the nodes in the block
  $parent->putFirstCut($w);                                                     # Move block
  $w->unwrap;                                                                   # Unwrap block in new position
  $start                                                                        # Success
 }

sub moveBlockFromFirstLast($$@)                                                 #C Move the block of siblings starting with B<$start> in the optional context last under the specified B<$parent> node. Returns B<$node> if the move was made successfully, else confess that the specified move is impossible.
 {my ($start, $parent, @context) = @_;                                          # First sibling, parent to move last under, optional context
  return undef if @context and !$start->at(@context);                           # Not in specified context
  my $w = $start->wrapFromFirst(q(grab));                                       # Grab the nodes in the block
  $parent->putLastCut($w);                                                      # Move block
  $w->unwrap;                                                                   # Unwrap block in new position
  $start                                                                        # Success
 }

sub moveBlockFromFirstAfter($$@)                                                #C Move the block of siblings starting with B<$start> in the optional context after the specified B<$after> node. Returns B<$node> if the move was made successfully, else confess that the specified move is impossible.
 {my ($start, $after, @context) = @_;                                           # First sibling, node to move after, optional context
  return undef if @context and !$start->at(@context);                           # Not in specified context
  my $w = $start->wrapFromFirst(q(grab));                                       # Grab the nodes in the block
  $after->putNextCut($w);                                                       # Move block
  $w->unwrap;                                                                   # Unwrap block in new position
  $start                                                                        # Success
 }

sub moveBlockFromFirstBefore($$@)                                               #C Move the block of siblings starting with B<$start> in the optional context before the specified B<$after> node. Returns B<$node> if the move was made successfully, else confess that the specified move is impossible.
 {my ($start, $before, @context) = @_;                                          # First sibling, node to move before, optional context
  return undef if @context and !$start->at(@context);                           # Not in specified context
  my $w = $start->wrapFromFirst(q(grab));                                       # Grab the nodes in the block
  $before->putPrevCut($w);                                                      # Move block
  $w->unwrap;                                                                   # Unwrap block in new position
  $start                                                                        # Success
 }

#D2 Add selectively                                                             # Add new nodes unless they already exist.

sub addFirst($$@)                                                               #CU Add a new node L<first|/first> below the specified B<$node> with the specified B<$tag> unless a node with that tag already exists in that position. Return the new node if it was created unless return the pre-existing node.
 {my ($node, $tag, @context) = @_;                                              # Node, tag of new node, optional context
  return undef if @context and !$node->at(@context);                            # Not in specified context
  if (my $f = $node->first)                                                     # Existing first node
   {return $f if $f->tag eq $tag;                                               # Return existing first node with matching tag
   }
  $node->putFirst($node->newTag($tag))                                          # Create a new node, place it first below the specified node and return the new node.
 }

#a
#b <b/>
#c addFirst c b
#d Add a first child if needed.

sub addNext($$@)                                                                #CU Add a new node L<next|/next> the specified B<$node> and return the new node unless a node with that tag already exists in which case return the existing B<$node>.
 {my ($node, $tag, @context) = @_;                                              # Node, tag of new node, optional context
  return undef if @context and !$node->at(@context);                            # Not in specified context
  if (my $n = $node->next)                                                      # Existing next node
   {return $n if $n->tag eq $tag;                                               # Return existing next node with matching tag
   }
  $node->putNext($node->newTag($tag))                                           # Create a new node, place it next to the specified node and return the new node.
 }

#a addFirst
#b <b/>
#c addNext c b
#d Add a next child if needed.

sub addPrev($$@)                                                                #CU Add a new node L<before|/prev> the specified B<$node> with the specified B<$tag> unless a node with that tag already exists in that position. Return the new node if it was created unless return the pre-existing node.
 {my ($node, $tag, @context) = @_;                                              # Node, tag of new node, optional context
  return undef if @context and !$node->at(@context);                            # Not in specified context
  if (my $p = $node->prev)                                                      # Existing previous node
   {return $p if $p->tag eq $tag;                                               # Return existing previous node with matching tag
   }
  $node->putPrev($node->newTag($tag))                                           # Create a new node, place it before the specified node and return the new node.
 }

sub addLast($$@)                                                                #CU Add a new node L<last|/last> below the specified B<$node> with the specified B<$tag> unless a node with that tag already exists in that position. Return the new node if it was created unless return the pre-existing node..
 {my ($node, $tag, @context) = @_;                                              # Node, tag of new node, optional context
  return undef if @context and !$node->at(@context);                            # Not in specified context
  if (my $l = $node->last)                                                      # Existing last node
   {return $l if $l->tag eq $tag;                                               # Return existing first node with matching tag
   }
  $node->putLast($node->newTag($tag))                                           # Create a new node, place it last below the specified node and return the new node.
 }

sub addWrapWith($$@)                                                            #CU L<Wrap|/wrap> the specified B<$node> with the specified tag if the node is not already wrapped with such a tag and return the new node unless a node with that tag already exists in which case return the existing B<$node>.
 {my ($node, $tag, @context) = @_;                                              # Node, tag of new node, optional context
  return undef if @context and !$node->at(@context);                            # Not in specified context
  if (my $l = $node->parent)                                                    # Existing wrapping node
   {return $l if $l->tag eq $tag;                                               # Return existing first node with matching tag
   }
  $node->wrapWith($tag)                                                         # Wrap with the specified node
 }

sub addSingleChild($$@)                                                         #CU Wrap the content of a specified B<$node> in a new node with the specified B<$tag> unless the content is already wrapped in a single child with the specified B<$tag>.
 {my ($node, $tag, @context) = @_;                                              # Node, tag of new node, optional context
  return undef if @context and !$node->at(@context);                            # Not in specified context
  if (my $c = $node->hasSingleChild)                                            # Return the existing child if it is an only child and has the right tag
   {return $c if -t $c eq $tag;
   }
  &wrapContentWith(@_);                                                         # Normal wrap content with new node
 }

#D2 Add text selectively                                                        # Add new text unless it already exists.

sub addFirstAsText($$@)                                                         #CU Add a new text node first below the specified B<$node> and return the new node unless a text node already exists there and starts with the same text in which case return the existing B<$node>.
 {my ($node, $text, @context) = @_;                                             # Node, text, optional context
  return undef if @context and !$node->at(@context);                            # Not in specified context
  if (my $f = $node->first)                                                     # Existing first node
   {return $f if $f->isText and $f->text =~ m(\A$text);                         # Return existing first node if is a text node with the same starting text
   }
  $node->putFirstAsText($text)                                                  # Create a new text node, place it first below the specified node and return the new text node.
 }

sub addNextAsText($$@)                                                          #CU Add a new text node after the specified B<$node> and return the new node unless a text node already exists there and starts with the same text in which case return the existing B<$node>.
 {my ($node, $text, @context) = @_;                                             # Node, text, optional context
  return undef if @context and !$node->at(@context);                            # Not in specified context
  if (my $n = $node->next)                                                      # Existing next node
   {return $n if $n->isText and $n->text =~ m(\A$text);                         # Return existing next node if is a text node with the same starting text
   }
  $node->putNextAsText($text)                                                   # Create a new text node, place it after the specified node and return the new text node.
 }

sub addPrevAsText($$@)                                                          #CU Add a new text node before the specified B<$node> and return the new node unless a text node already exists there and ends with the same text in which case return the existing B<$node>.
 {my ($node, $text, @context) = @_;                                             # Node, text, optional context
  return undef if @context and !$node->at(@context);                            # Not in specified context
  if (my $p = $node->prev)                                                      # Existing previous node
   {return $p if $p->isText and $p->text =~ m($text\Z);                         # Return existing previous node if is a text node with the same ending text
   }
  $node->putPrevAsText($text)                                                   # Create a new text node, place it before the specified node and return the new text node.
 }

sub addLastAsText($$@)                                                          #CU Add a new text node last below the specified B<$node> and return the new node unless a text node already exists there and ends with the same text in which case return the existing B<$node>.
 {my ($node, $text, @context) = @_;                                             # Node, text, optional context
  return undef if @context and !$node->at(@context);                            # Not in specified context
  if (my $l = $node->last)                                                      # Existing last node
   {return $l if $l->isText and $l->text =~ m($text\Z);                         # Return existing last node if is a text node with the same ending text
   }
  $node->putLastAsText($text)                                                   # Create a new text node, place it last below the specified node and return the new text node.
 }

sub joinWithText($$$@)                                                          #CU Insert some text between the children of the current B<$node> as specified by B<$text> between all the children of the current $node as long as they all have a tag of B<$over>. Return the current $node on success or B<undef> on failure.
 {my ($node, $text, $over, @context) = @_;                                      # Node, text, child tag, optional context
  return undef if @context and !$node->at(@context);                            # Not in specified context
  if ($node->over2(qr(\A(\s$over\s){2,}\Z)))                                    # Check tags of children
   {my @c = @$node;                                                             # Children of node
    pop @c;                                                                     # Insert between children
    $_->putNextAsText($text) for @c;                                            # Insertion text offset from existing children
    return $node;                                                               # Success
   }
  undef                                                                         # Not all children have required tag
 }
#a
#b <b/><b/><b/><b/>
#c joinWithText plus b a
#d Insert text between each child.

#D2 Fission                                                                     # Split the parent L<before|/splitBefore> or L<after|/splitAfter> the specified sibling.

sub splitBefore($@)                                                             #CU Split the parent node into two identical nodes except all the siblings before the specified B<$node> are retained by the existing parent while any following siblings become siblings of the new parent node which is placed after the existing parent. The new parent is returned.
 {my ($node, @context) = @_;                                                    # Node to split before, optional context
  return undef if @context and !$node->at(@context);                            # Not in specified context
  my $p = $node->parent;                                                        # Parent
  $p or confess "Cannot split before the root node";                            # Complain if we try to split before the root node
  my @c = ($node, $node->contentAfter);                                         # Content of new node
  my $q = $p->dupTag;                                                           # New node
  $q->putFirstCut($_) for reverse @c;                                           # Move each node after to new parent avoiding text eliding which will occur if the preceding element is text and one of the siblings is also a text segment
  $p->putNext($q);                                                              # Place new node after parent
 }

sub splitAfter($@)                                                              #CU Split the parent node into two identical nodes except all the siblings after the specified B<$node> are retained by the existing parent while any preceding siblings become siblings of the new parent node which is placed before the existing parent. The new parent is returned
 {my ($node, @context) = @_;                                                    # Node to split before, optional context
  return undef if @context and !$node->at(@context);                            # Not in specified context
  my $p = $node->parent;                                                        # Parent
  $p or confess "Cannot split before the root node";                            # Complain if we try to split before the root node
  my @c = ($node->contentBefore, $node);                                        # Content of new node
  my $q = $p->dupTag;                                                           # New node
  $q->putLastCut($_) for @c;                                                    # Move each node before to new parent
  $p->putPrev($q);                                                              # Place new node before parent
 }

#D2 Fusion                                                                      # Join consecutive nodes

sub concatenate($$@)                                                            #C Concatenate two successive nodes and return the B<$target> node.
 {my ($target, $source, @context) = @_;                                         # Target node to replace, node to concatenate, optional context of $target
  return undef if @context and !$target->at(@context);                          # Not in specified context
  $source->parser or confess "Cannot concatenate the root node";                # Complain if we try and concatenate the root
  if ($source = $target->next)
   {$target->content = [$target->contents, $source->contents];                  # Concatenate (target, source) to target
   }
  elsif ($source = $target->prev)
   {$target->content = [$source->contents, $target->contents];                  # Concatenate (source, target) to target
   }
  else
   {confess "Cannot concatenate non consecutive nodes";                         # Complain if the nodes are not adjacent
   }
  $source->content = [];                                                        # Concatenate
  $target->indexNode;                                                           # Index target node
  $source->indexNode;                                                           # Index source node
  $source->cut;
  $target                                                                       # Return new node
 }

sub concatenateSiblings($@)                                                     #CU Concatenate the nodes that precede and follow the specified B<$node> in the optioonal B<@context> as long as they have the same tag as the specified B<$node> and return the specified B<$node>.
 {my ($node, @context) = @_;                                                    # Concatenate around this node, optional context.
  return undef if @context and !$node->at(@context);                            # Not in specified context
  my $t = $node->tag;                                                           # The tag to match
  while(my $p = $node->prev)
   {last unless $p->tag eq $t;                                                  # Stop when the siblings no longer match
    $node->concatenate($p)
   }
  while(my $n = $node->next)
   {last unless $n->tag eq $t;                                                  # Stop when the siblings no longer match
    $node->concatenate($n) if $n->tag eq $t
   }
  $node                                                                         # Return concatenating node
 }

sub mergeDuplicateChildWithParent($@)                                           #CU Merge a parent node with its only child if their tags are the same and their attributes do not collide other than possibly the id in which case the parent id is used. Any labels on the child are transferred to the parent. The child node is then unwrapped and the parent node is returned.
 {my ($parent, @context) = @_;                                                  # Parent this node, optional context.
  return undef if @context and !$parent->at(@context);                          # Not in specified context
  return undef unless my $child = $parent->hasSingleChild;                      # Not an only child
  return undef unless $child->tag eq $parent->tag;                              # Tags differ
  my %c = %{$child->attributes};                                                # Child attributes
  my %p = %{$parent->attributes};                                               # Parent attributes
  $p{id} = $c{id} unless $p{id};                                                # Transfer child id unless parent already has one
  delete $c{id};                                                                # Remove child id
  for(sort keys %c)                                                             # Remaining attributes
   {return undef if $p{$_} and $p{$_} ne $c{$_};                                # Attributes collide
    $p{$_} = $c{$_};                                                            # Transfer non colliding attribute
   }
  $parent->attributes = \%p;                                                    # Transfer the attributes en masses as none of them collide
  $child->copyLabels($parent);                                                  # Copy child labels to parent
  $child->unwrap;                                                               # Unwrap child
  $parent                                                                       # Return original node
 }

#D2 Put as text                                                                 # Add text to the L<parse|/parse> tree.

sub putFirstAsText($$@)                                                         #CU Add a new text node first under a parent and return the new text node.
 {my ($node, $text, @context) = @_;                                             # The parent node, the string to be added which might contain unparsed Xml as well as text, optional context.
  return undef if @context and !$node->at(@context);                            # Not in specified context
  $node->putFirst(my $t = $node->newText($text));                               # Add new text node
  $t                                                                            # Return new node
 }

sub putTextFirst($@)                                                            #U Given a B<$node> place some B<@text> first under the $node and return the new text node else return B<undef> if this is not possible.
 {my ($node, @text) = @_;                                                       # The parent node, the text to be added
  $node->putFirst(my $t = $node->newText(join ' ', @text));                     # Add new text node
  $t                                                                            # Return new node
 }
#a
#b <b><c/></b>
#c at b
#c putTextFirst I shall count the seconds
#d Put some text first under the current node.
sub putLastAsText($$@)                                                          #CU Add a new text node last under a parent and return the new text node.
 {my ($node, $text, @context) = @_;                                             # The parent node, the string to be added which might contain unparsed Xml as well as text, optional context.
  return undef if @context and !$node->at(@context);                            # Not in specified context
  $node->putLast(my $t = $node->newText($text));                                # Add new text node
  $t                                                                            # Return new node
 }

sub putTextLast($@)                                                             #U Given a B<$node> place some B<@text> last under the $node and return the new text node else return B<undef> if this is not possible.
 {my ($node, @text) = @_;                                                       # The parent node, the text to be added
  $node->putLast(my $t = $node->newText(join ' ', @text));                      # Add new text node
  $t                                                                            # Return new node
 }
#a
#b <b><c/></b>
#c at b
#c putTextLast Each second will be an hour
#d Put some text last under the current node.

sub putNextAsText($$@)                                                          #CU Add a new text node following the specified B<$node> and return the new text node.
 {my ($node, $text, @context) = @_;                                             # The parent node, the string to be added which might contain unparsed Xml as well as text, optional context.
  return undef if @context and !$node->at(@context);                            # Not in specified context
  $node->putNext(my $t = $node->newText($text));                                # Add new text node
  $t                                                                            # Return new node
 }

sub putTextNext($@)                                                             #U Given a B<$node> place some B<@text> next to the $node and return the new text node else return B<undef> if this is not possible.
 {my ($node, @text) = @_;                                                       # The parent node, the text to be added
  $node->putNext(my $t = $node->newText(join ' ', @text));                      # Add new text node
  $t                                                                            # Return new node
 }

sub putPrevAsText($$@)                                                          #CU Add a new text node following the specified B<$node> and return the new text node
 {my ($node, $text, @context) = @_;                                             # The parent node, the string to be added which might contain unparsed Xml as well as text, optional context.
  return undef if @context and !$node->at(@context);                            # Not in specified context
  $node->putPrev(my $t = $node->newText($text));                                # Add new text node
  $t                                                                            # Return new node
 }

sub putTextPrev($@)                                                             #U Given a B<$node> place some B<@text> prior to the $node and return the new text node else return B<undef> if this is not possible.
 {my ($node, @text) = @_;                                                       # The parent node, the text to be added
  $node->putPrev(my $t = $node->newText(join ' ', @text));                      # Add new text node
  $t                                                                            # Return new node
 }

#D2 Put as tree                                                                 # Add parsed text to the L<parse|/parse> tree.

sub putFirstAsTree($$@)                                                         #C Put parsed text first under the specified B<$node> parent and return a reference to the parsed tree. Confess if the text cannot be parsed successfully.
 {my ($node, $text, @context) = @_;                                             # The parent node, the string to be parsed and added, context
  return undef if @context and !$node->at(@context);                            # Not in specified context
  $node->putFirst(new($text))                                                   # Add new parse tree first
 }

sub putLastAsTree($$@)                                                          #C Put parsed text last under the specified B<$node> parent and return a reference to the parsed tree. Confess if the text cannot be parsed successfully.
 {my ($node, $text, @context) = @_;                                             # The parent node, the string to be parsed and added, context
  return undef if @context and !$node->at(@context);                            # Not in specified context
  $node->putLast(new($text))                                                    # Add new parse tree last
 }

sub putNextAsTree($$@)                                                          #C Put parsed text after the specified B<$node> parent and return a reference to the parsed tree. Confess if the text cannot be parsed successfully.
 {my ($node, $text, @context) = @_;                                             # The parent node, the string to be parsed and added, context
  return undef if @context and !$node->at(@context);                            # Not in specified context
  $node->putNext(new($text))                                                    # Add new parse tree
 }

sub putPrevAsTree($$@)                                                          #C Put parsed text before the specified B<$parent> parent and return a reference to the parsed tree. Confess if the text cannot be parsed successfully.
 {my ($node, $text, @context) = @_;                                             # The parent node, the string to be parsed and added, context
  return undef if @context and !$node->at(@context);                            # Not in specified context
  $node->putPrev(new($text))                                                    # Add new parse tree
 }

#D2 Put as comment                                                              # Add some comments to the parse tree relative to the specified B<$node> in the optional B<$context> and return the specified B<$node>.

sub putFirstAsComment($$@)                                                      #CU Put a comment first under the specified B<$node> and return the specified B<$node>.
 {my ($node, $text, @context) = @_;                                             # Parent node, comment, context
  return undef if @context and !$node->at(@context);                            # Not in specified context
  $node->putFirstAsText(qq(<!-- $text -->));                                    # Put comment first
 }

sub putLastAsComment($$@)                                                       #CU Put a comment last under the specified B<$node> and return the specified B<$node>.
 {my ($node, $text, @context) = @_;                                             # Parent node, comment, context
  return undef if @context and !$node->at(@context);                            # Not in specified context
  $node->putLastAsText(qq(<!-- $text -->));                                     # Put comment last
 }

sub putNextAsComment($$@)                                                       #CU Put a comment after the specified B<$node> and return the specified B<$node>
 {my ($node, $text, @context) = @_;                                             # Node, comment, context
  return undef if @context and !$node->at(@context);                            # Not in specified context
  $node->putNextAsText(qq(<!-- $text -->));                                     # Put comment as next node
 }

sub putPrevAsComment($$@)                                                       #CU Put a comment before the specified B<$parent> parentand return the specified B<$node>
 {my ($node, $text, @context) = @_;                                             # Node, comment, context
  return undef if @context and !$node->at(@context);                            # Not in specified context
  $node->putPrevAsText(qq(<!-- $text -->));                                     # Put comment as previous node
 }

#D2 Break in and out                                                            # Break nodes out of nodes or push them back

sub breakIn($@)                                                                 #CU Concatenate the nodes following and preceding the start node, unwrapping nodes whose tag matches the start node and return the start node. To concatenate only the preceding nodes, use L<breakInBackwards|/breakInBackwards>, to concatenate only the following nodes, use L<breakInForwards|/breakInForwards>.
 {my ($start, @context) = @_;                                                   # The start node, optional context.
  return undef if @context and !$start->at(@context);                           # Not in specified context
  $start->breakInBackwards;                                                     # The nodes before the start node
  $start->breakInForwards                                                       # The nodes following the start node
 }

sub breakInForwards($@)                                                         #CU Concatenate the nodes following the start node, unwrapping nodes whose tag matches the start node and return the start node in the manner of L<breakIn|/breakIn>.
 {my ($start, @context) = @_;                                                   # The start node, optional context..
  return undef if @context and !$start->at(@context);                           # Not in specified context
  my $tag     = $start->tag;                                                    # The start node tag
  for my $item($start->contentAfter)                                            # Each item following the start node
   {$start->putLast($item->cut);                                                # Concatenate item
    if ($item->tag eq $tag)                                                     # Unwrap items with the same tag as the start node
     {$item->unwrap;                                                            # Start a new clone of the parent
     }
   }
  $start                                                                        # Return the start node
 }

sub breakInBackwards($@)                                                        #CU Concatenate the nodes preceding the start node, unwrapping nodes whose tag matches the start node and return the start node in the manner of L<breakIn|/breakIn>.
 {my ($start, @context) = @_;                                                   # The start node, optional context..
  return undef if @context and !$start->at(@context);                           # Not in specified context
  my $tag     = $start->tag;                                                    # The start node tag
  for my $item(reverse $start->contentBefore)                                   # Each item preceding the start node reversing from the start node
   {$start->putFirst($item->cut);                                               # Concatenate item
    if ($item->tag eq $tag)                                                     # Unwrap items with the same tag as the start node
     {$item->unwrap;                                                            # Start a new clone of the parent
     }
   }
  $start                                                                        # Return the start node
 }

sub breakOut($@)                                                                #U Lift child nodes with the specified tags under the specified parent node splitting the parent node into clones and return the cut out original node.
 {my ($parent, @tags) = @_;                                                     # The parent node, the tags of the modes to be broken out.
  my %tags       = map {$_=>1} @tags;                                           # Tags to break out
  my %attributes = %{$parent->attributes};                                      # Attributes of parent
  my $parentTag  = $parent->tag;                                                # The tag of the parent
  my $p;                                                                        # Clone of parent currently being built
  for my $item($parent->contents)                                               # Each item
   {if ($tags{$item->tag})                                                      # Item to break out
     {$parent->putPrev($item->cut);                                             # Position item broken out
      $p = undef;                                                               # Start a new clone of the parent
     }
    else                                                                        # Item to remain in situ
     {if (!defined($p))                                                         # Create a new parent clone
       {$parent->putPrev($p = $parent->newTag($parent->tag, %attributes));      # Position new parent clone
       }
      $p->putLast($item->cut);                                                  # Move current item into parent clone
     }
   }
  $parent->cut                                                                  # Remove the original copy of the parent from which the clones were made
 }

sub breakOutChild($@)                                                           #CU Lift the specified B<$node> up one level splitting its parent. Return the specified B<$node> on success or B<undef> if the operation is not possible.
 {my ($node, @context) = @_;                                                    # Node to break out, optional context
  return undef if @context and !$node->at(@context);                            # Not in specified context
  my $parent = $node->parent;                                                   # Parent node
  return undef unless $parent;                                                  # Cannot break out the root node
  my $grandParent = $parent->parent;                                            # Parent of parent node
  return undef unless $grandParent;                                             # Cannot break out of the root node
  my %attributes = %{$parent->attributes};                                      # Attributes of parent
  my $parentTag  = $parent->tag;                                                # The tag of the parent
  my $prevParent = $parent->putPrev($parent->newTag($parent->tag, %attributes));# Position new parent clone
  my $p;                                                                        # Clone of parent currently being built
  for my $item($node->contentBefore)                                            # Each preceding sibling
   {$prevParent->putLast($item->cut);                                           # Reposition preceding sibling
   }
  $parent->putPrev($node->cut);                                                 # Reposition child
  $node                                                                         # Return broken out node
 }

#D2 Split and Zip                                                               # Move a node up the parse tree by splitting its parent nodes

sub splitParentAfter($@)                                                        #CU Finish and restart the parent of the specified B<$node> just after the specified B<$node> and return the newly created parent node on success or B<undef> on failure.
 {my ($node, @context) = @_;                                                    # The splitting node, optional context.
  return undef if @context and !$node->at(@context);                            # Not in specified context
  return undef unless my $p = $node->parent;                                    # Parent to split
  my @c = $p->contents;                                                         # Siblings to keep
  my @C;                                                                        # Siblings to reparent
  unshift @C, pop @c while @c && $c[-1] != $node;                               # Nodes to keep as is
  $p->content = [@c];                                                           # Content for existing tag
  my $P = $p->dupTag;                                                           # Duplicate  a node
  $p->putNext($P);                                                              # Insert new node
  $P->content = [@C];                                                           # Content for new tag
  $_->indexNode for $p, $P;                                                     # Reindex parent nodes
  $P
 }

BEGIN{*spa=*splitParentAfter}

sub splitParentBefore($@)                                                       #CU Finish and restart the parent of the specified B<$node> just before the specified B<$node> and return the newly created parent node on success or B<undef> on failure.
 {my ($node, @context) = @_;                                                    # The splitting node, optional context.
  return undef if @context and !$node->at(@context);                            # Not in specified context
  return undef unless my $p = $node->parent;                                    # Parent to split
  my @C = $p->contents;                                                         # Siblings to keep
  my @c;                                                                        # Siblings to reparent
  push @C, shift @c while @c && $c[-1] != $node;                                # Nodes to keep as is
  $p->content = [@c];                                                           # Content for existing tag
  my $P = $p->dupTag;                                                           # Duplicate  a node
  $p->putPrev($P);                                                              # Insert new node
  $P->content = [@C];                                                           # Content for new tag
  $_->indexNode for $p, $P;                                                     # Reindex parent nodes
  $P
 }

BEGIN{*spb=*splitParentBefore}

sub splitTo($$@)                                                                #C Lift the specified B<$node> up until it splits the specified B<$parent> node. Return the specified B<$node> on success or B<undef> if the operation is not possible.
 {my ($node, $parent, @context) = @_;                                           # Node to break out, ancestral node to split, optional context
  return undef if @context and !$node->at(@context);                            # Not in specified context
  return undef if $node == $parent;                                             # Not breakable
  return undef unless $node->parent;                                            # The node has to have a parent to split
  return undef unless $parent->parent;                                          # The parent will have to have a parent as well if the parent is to be splittable.
  my @p = $node->belowPath($parent);                                            # Path to parent
  return undef unless @p;                                                       # No path to parent
  $node->breakOutChild for 1..@p;                                               # Break up along the path
  return $node;                                                                 # Return child in new position
 }

sub adoptChild($$@)                                                             #C Lift the specified B<$child> node up until it is an immediate child of the specified B<$parent> node by splitting any intervening nodes. Return the specified B<$child> node on success or B<undef> if the operation is not possible.
 {my ($parent, $child, @context) = @_;                                          # Adopting parent node, child node to adopt, optional context of child
  return undef if @context and !$child->at(@context);                           # Child not in specified context
  return undef if $child == $parent;                                            # Cannot adopt oneself
  return undef unless $child->parent;                                           # Cannot adopt the root
  my @p = $child->belowPath($parent);                                           # Path to parent
  return undef unless @p;                                                       # No path to parent
  pop @p;                                                                       # Split up to the parent but not the parent
  $child->breakOutChild for 1..@p;                                              # Break up along the path
  return $child;                                                                # Return child in new position
 }

sub zipDownOnce($@)                                                             #CU Push a node down one level by making it a child of a node formed by merging the preceding and following siblings if they have the same tag.
 {my ($node, @context) = @_;                                                    # Node, optional context
  return undef if @context and !$node->at(@context);                            # Node not in specified context
  my $n = $node->next;                                                          # Next siblings
  my $p = $node->prev;                                                          # Previous sibling
  return undef unless $n and $p and $n->tag eq $p->tag;                         # Next and previous siblings must have same tag
  $p->putLastCut($_) for $node, @{$n->content};                                 # Extend previous sibling
  $n->cut;                                                                      # Remove next sibling
  $node                                                                         # Return node in new position
 }

sub zipDown($@)                                                                 #CU Push a node down as many levels as possible by making it a child of a node formed by merging the preceding and following siblings if they have the same tag.
 {my ($node, @context) = @_;                                                    # Node, optional context
  return undef if @context and !$node->at(@context);                            # Node not in specified context
  undef while $node->zipDownOnce;                                               # Zip down for as long as possible
  $node                                                                         # Return node in new position
 }

sub splitAndWrapFromStart($$$@)                                                 #CU Split the child nodes of B<$parent> on the child nodes with a tag of B<$split> wrapping the splitting node and all preceding nodes from the previous splitting node or the start with the specified B<$wrap>ping node with optional B<%attributes>.  Returns an array of the wrapping nodes created.
 {my ($parent, $split, $wrap, %attributes) = @_;                                # Parent node, tag of splitting nodes, tag for wrapping node, attributes for wrapping nodes
  my @s = $parent->c($split);                                                   # Splitting nodes
  my @w;                                                                        # Wrapping nodes
  for my $i(reverse keys @s)                                                    # Each splitting node
   {my $s = $s[$i];
    if ($i)                                                                     # From previous split
     {push @w, $s[$i-1]->next->wrapTo($s, $wrap, %attributes);                  # Save wrapping node
     }
    else                                                                        # From start
     {push @w, $s->wrapFromFirst($wrap, %attributes);                           # Save wrapping node
     }
   }
  @w
 }

sub splitAndWrapToEnd($$$@)                                                     #CU Split the sequence of child nodes under the specified B<$parent> node on those child nodes whose tag value is B<$split> wrapping the splitting node and all following nodes up until the next splitting node or the end of the sequence with newly created nodes whose tag is B<$wrap> with optional attributes B<%attributes>.  Returns an array of the wrapping nodes so created.
 {my ($parent, $split, $wrap, %attributes) = @_;                                # Parent node, tag of splitting nodes, tag for wrapping node, attributes for wrapping nodes
  my @s = $parent->c($split);                                                   # Splitting nodes
  my @w;                                                                        # Wrapping nodes
  for my $i(keys @s)                                                            # Each splitting node
   {my $s = $s[$i];
    if ($i != $#s)                                                              # More splits to come
     {push @w, $s->wrapTo($s[$i+1]->prev, $wrap, %attributes);                  # Save wrapping node
     }
    else                                                                        # Last splitting node
     {push @w, $s->wrapToLast($wrap, %attributes);                              # Save wrapping node
     }
   }
  @w
 }

#D2 Replace                                                                     # Replace nodes in the L<parse|/parse> tree with nodes or text

sub replaceWith($$@)                                                            #C Replace a node (and all its content) with a L<new node|/newTag> (and all its content) and return the new node. If the node to be replaced is the root of the L<parse|/parse> tree then no action is taken other then returning the new node.
 {my ($old, $new, @context) = @_;                                               # Old node, new node, optional context..
  return undef if @context and !$old->at(@context);                             # Not in specified context
  $new->parent and confess "Please cut out the node before moving it";          # The node must have be cut out first
  $new->parser == $new and $old->parser == $new and                             # Prevent a root node from being inserted into a sub tree
    confess "Recursive replacement attempted";

  if (my $parent = $old->parent)                                                # Parent node of old node
   {my $c = $parent->content;                                                   # Content array of parent
    if (defined(my $i = $old->position))                                        # Position of old node in content array of parent
     {splice(@$c, $i, 1, $new);                                                 # Replace old node with new node
      $old->parent = undef;                                                     # Cut out node
      $parent->indexNode;                                                       # Rebuild indices for parent
      return $new;                                                              # Return new node
     }
    confess "Should not happen";
   }
  else                                                                          # Replacing the root - merge the new node rather than replacing it so that we do not loose addressability to the old tree
   {$old->tag        = $new->tag;
    $old->content    = $new->content;
    $old->attributes = $new->attributes;
    return $old;
   }
 }

sub replaceWithText($$@)                                                        #CU Replace a node (and all its content) with a new text node and return the new node.
 {my ($old, $text, @context) = @_;                                              # Old node, text of new node, optional context.
  return undef if @context and !$old->at(@context);                             # Not in specified context
  my $n = $old->replaceWith($old->newText($text));                              # Create a new text node, replace the old node and return the result
  $n
 }

sub replaceWithBlank($@)                                                        #CU Replace a node (and all its content) with a new blank text node and return the new node.
 {my ($old, @context) = @_;                                                     # Old node, optional context.
  return undef if @context and !$old->at(@context);                             # Not in specified context
  my $n = $old->replaceWithText(' ');                                           # Create a new text node, replace the old node with a new blank text node and return the result
  $n
 }

sub replaceContentWithMovedContent($@)                                          # Replace the content of a specified target node with the contents of the specified source nodes removing the content from each source node and return the target node.
 {my ($node, @nodes) = @_;                                                      # Target node, source nodes
  my @content;                                                                  # Target content array
  for my $source(@nodes)                                                        # Each source node
   {push @content, $source->contents;                                           # Build target content array
    $source->content = undef;                                                   # Move content
    $source->indexNode;                                                         # Rebuild indices
   }
  $node->content = [@content];                                                  # Insert new content
  $node->indexNode;                                                             # Rebuild indices
  $node                                                                         # Return target node
 }

sub replaceContentWith($@)                                                      # Replace the content of a node with the specified nodes and return the replaced content
 {my ($node, @content) = @_;                                                    # Node whose content is to be replaced, new content
  my @c = $node->contents;                                                      # Content
  $node->content = [@content];                                                  # Insert new content
  $node->indexNode;                                                             # Rebuild indices
  @c                                                                            # Return old content
 }

sub replaceContentWithText($@)                                                  #U Replace the content of a node with the specified texts and return the replaced content
 {my ($node, @text) = @_;                                                       # Node whose content is to be replaced, texts to form new content
  my @c = $node->contents;                                                      # Content
  $node->content = [map {$node->newText($_)} @text];                            # Insert new content
  $node->indexNode;                                                             # Rebuild indices
  @c                                                                            # Return old content
 }

#D2 Swap                                                                        # Swap nodes both singly and in blocks

sub invert($@)                                                                  #CU Swap a parent and child node where the child is the only child of the parent and return the parent.
 {my ($parent, @context) = @_;                                                  # Parent, context
  return undef unless my $child = $parent->hasSingleChild(@context);            # Single child
  my $grandParent = $parent->parent;                                            # Grandparent
  $grandParent or confess "Cannot invert the outer most node";                  # Cannot invert root
  $parent->unwrap;                                                              # Unwrap parent
  my $content = $child->content;                                                # Child content
  $child->content  = [$parent];                                                 # Place parent under child
  $parent->content = $content;                                                  # Place child content under parent
  $child->parent   = $grandParent;                                              # Grand parent
  $parent->parent  = $child;                                                    # Parent
  $_->indexNode for $child, $parent, $grandParent;                              # Index modified nodes
  $parent                                                                       # Return parent
 }

sub invertFirst($@)                                                             #CU Swap a parent and child node where the child is the first child of the parent by placing the parent last in the child. Return the parent.
 {my ($parent, @context) = @_;                                                  # Parent, context
  return undef if @context and !$parent->at(@context);                          # Not in specified context
  return undef unless my $child = $parent->first;                               # First child
  my $grandParent = $parent->parent;                                            # Grandparent
  $grandParent or confess "Cannot invertFirst the outer most node";             # Cannot invert root
  my $i = $parent->index;                                                       # Position of parent in grandparent
  $parent->cut;                                                                 # Cut out parent
  $child->cut;                                                                  # Cut out child
  $child->putLast($parent);                                                     # Put parent last under child
  $grandParent->content->[$i] = $child;                                         # Put child in position in grandparent
  $_->indexNode for $child, $parent, $grandParent;                              # Index modified nodes
  $child->parent = $grandParent;                                                # Grand parent
  $parent                                                                       # Return parent
 }

sub invertLast($@)                                                              #CU Swap a parent and child node where the child is the last child of the parent by placing the parent first in the child. Return the parent.
 {my ($parent, @context) = @_;                                                  # Parent, context
  return undef if @context and !$parent->at(@context);                          # Not in specified context
  return undef unless my $child = $parent->last;                                # Last child
  my $grandParent = $parent->parent;                                            # Grandparent
  $grandParent or confess "Cannot invert the outer most node";                  # Cannot invert root
  my $i = $parent->index;                                                       # Position of parent in grandparent
  $parent->cut;                                                                 # Cut out parent
  $child->cut;                                                                  # Cut out child
  $child->putFirst($parent);                                                    # Put parent last under child
  $grandParent->content->[$i] = $child;                                         # Put child in position in grandparent
  $child->parent = $grandParent;                                                # Grand parent
  $_->indexNode for $child, $parent, $grandParent;                              # Index modified nodes
  $parent                                                                       # Return parent
 }

sub swap($$@)                                                                   #C Swap two nodes optionally checking that the first node is in the specified context and return the first node.
 {my ($first, $second, @context) = @_;                                          # First node, second node, optional context
  return undef if @context and !$first->at(@context);                           # First node not in specified context
  confess "First node is above second node" if $first->above($second);          # Check that the first node is not above the second node otherwise the result of the swap would not be a tree
  confess "First node is below second node" if $first->below($second);          # Check that the first node is not above the second node otherwise the result of the swap would not be a tree
  return $first if $first == $second;                                           # Do nothing if the first node is the second node
  my $f = $first ->wrapWith(q(temp));                                           # Wrap first node for transfer
  my $s = $second->wrapWith(q(temp));                                           # Wrap second node for transfer
  $f->putLast($second->cut);                                                    # Transfer second node
  $s->putLast($first ->cut);                                                    # Transfer first node
  $_->unwrap for $f, $s;                                                        # Remove wrapping
  $first                                                                        # Return first node
 }

sub swapFirstSibling($@)                                                        #CU Swap B<$node> with its first sibling node and return B<$node>.
 {my ($node, @context) = @_;                                                    # Node, optional context
  return undef if @context and !$node->at(@context);                            # Node not in specified context
  return undef unless my $parent = $node->parent;                               # Cannot swap root node
  my ($f1, $f2) = @$parent;                                                     # First two nodes
  return $node if $node == $f1;                                                 # Already the first node
  $node->putNextCut($f1);                                                       # Move first node into position
  return $node if $node == $f2;                                                 # Was second, now first
  $parent->putFirstCut($node);                                                  # Move node first
 }

BEGIN{*sfs=*swapFirstSibling}

sub swapNext($@)                                                                #CU Swap B<$node> with its following node if $B<$node> matches the first element of the specified context and the next node matches the rest.  Return the node that originally followed B<$node> on success or B<undef> on failure.
 {my ($node, @context) = @_;                                                    # Node, optional context
  my $next = $node->next;                                                       # Next node
  return undef unless $next;                                                    # No following node so no swap possible
  if (@context)                                                                 # Check supplied context
   {my ($c, $d, @c) = @context;
    return undef unless        $node->at($c, @c);                               # Node not in context
    return undef unless !$d or $next->at($d, @c);                               # Following node not in context
   }
  $next->putNextCut($node);                                                     # Swap nodes
  $next                                                                         # Return other node
 }

BEGIN{*sn=*swapNext}

sub swapPrev($@)                                                                #CU Swap B<$node> with its preceding node if $B<$node> matches the first element of the specified context and the previous node matches the rest.  Return the node that originally followed B<$node> on success or B<undef> on failure.
 {my ($node, @context) = @_;                                                    # Node, optional context
  my $prev = $node->prev;                                                       # Previous node
  return undef unless $prev;                                                    # No previous node so no swap possible
  if (@context)                                                                 # Check supplied context
   {my ($c, $d, @c) = @context;
    return undef unless        $node->at($c, @c);                               # Node not in context
    return undef unless !$d or $prev->at($d, @c);                               # Following node not in context
   }
  $prev->putPrevCut($node);                                                     # Swap nodes
  $prev                                                                         # Return other node
 }

BEGIN{*sp=*swapPrev}

sub swapLastSibling($@)                                                         #CU Swap B<$node> with its last sibling node and return B<$node>.
 {my ($node, @context) = @_;                                                    # Node, optional context
  return undef if @context and !$node->at(@context);                            # Node not in specified context
  return undef unless my $parent = $node->parent;                               # Cannot swap root node
  my ($l1, $l2) = reverse @$parent;                                             # Last two nodes
  return $node if $node == $l1;                                                 # Already the last node
  $node->putPrevCut($l1);                                                       # Move last node into position
  return $node if $node == $l2;                                                 # Was second last, now last
  $parent->putLastCut($node);                                                   # Move node last
 }

BEGIN{*sls=*swapLastSibling}

sub swapTags($$@)                                                               #C Swap the tags of two nodes optionally checking that the first node is in the specified context and return (B<$first>, B<$second>) nodes.
 {my ($first, $second, @context) = @_;                                          # First node, second node, optional context
  return undef if @context and !$first->at(@context);                           # First node not in specified context
  my ($t, $T) = ($first->tag, $second->tag);                                    # Tags
  $first ->change($T);
  $second->change($t);
 ($first, $second)                                                              # Return nodes
 }

sub swapTagWithParent($@)                                                       #CU Swap the tags of the specified B<$node> and its parent optionally checking that the B<$node> is in the specified context and return (parent of B<$node>, B<$node>) or () if there is no such parent node.
 {my ($node, @context) = @_;                                                    # Node, optional context
  return () if @context and !$node->at(@context);                               # Node not in specified context
  return () unless my $p = $node->parent;                                       # No parent for node to swap with
  $p->swapTags($node)                                                           # Swap tags
 }

sub reorder($$$@)                                                               #CU If the current B<$node> has a context of (B<$first>, @context) and the preceding node has a context of (B<$second>, B<@context>), then swap the current node with the previous node.Return the current node regardless
 {my ($node, $first, $second, @context) = @_;                                   # Node, first tag, second tag, optional context
  return undef unless $node->at($first, @context);                              # Node not in specified context
  return undef unless my $prev = $node->prev;                                   # No previous node
  return undef unless $prev->at($second, @context);                             # Previous node not in specified context
  $node->putNextCut($prev);                                                     # Reorder nodes
  $node                                                                         # Return the current node
 }
#a
#b <c/><b/>
#c reorder b c
#d Reorder two adjacent nodes if they are out of order.

#D2 Wrap and unwrap                                                             # Wrap and unwrap nodes to alter the depth of the L<parse|/parse> tree
#D3 Wrap                                                                        # Wrap nodes to deepen the L<parse|/parse> tree

sub wrapWith($$@)                                                               #IU Wrap the specified B<$node> in a new node created from the specified B<$tag> in the optional B<@context> forcing the specified $node down to deepen the L<parse|/parse> tree - return the new wrapping node or B<undef> if this is not possible. See L<addWrapWith|/addWrapWith> to perform this operation conditionally.
 {my ($node, $tag, @context) = @_;                                              # Node, tag for the new node or tag, optional context
  return undef if @context and !$node->at(@context);                            # Node not in specified context
  my $new = newTag(undef, $tag);                                                # Create wrapping node
  if (my $par  = $node->parent)                                                 # Parent node exists
   {my $c = $par->content;                                                      # Content array of parent
    my $i = $node->position;                                                    # Position in content array
    splice(@$c, $i, 1, $new);                                                   # Replace node
    $new->parser  = $node->parser;                                              # Assign the new node to the old parser
    $node->parent =  $new;                                                      # Set parent of original node as wrapping node
    $new->parent  =  $par;                                                      # Set parent of wrapping node
    $new->content = [$node];                                                    # Create content for wrapping node
    $par->indexNode;                                                            # Rebuild indices for parent
   }
  else                                                                          # At  the top - no parent
   {$new->parser = $new;                                                        # Assign the new node to the old parser
    $node->by(sub{$_->parser = $new});                                          # Set parser node so ditaTopicHeaders works - not entirely satisfactory but then w have to assume that the parser node is important enough to maintain.
    $new->content = [$node];                                                    # Create content for wrapping node
    $node->parent =  $new;                                                      # Set parent of original node as wrapping node
    $new->parent  =  undef;                                                     # Set parent of wrapping node - there is none
   }
  $new->indexNode;                                                              # Create index for wrapping node
  $new                                                                          # Return wrapping node
 }
#a unwrap addWrapWith
#b <c/>
#c wrapWith b c
#d Wrap a child with a new parent.

sub wrapWithDup($@)                                                             #U Wrap the specified B<$node> in a new node with the same tag as $node in the optional B<@context> making $node an L<only child|/isOnlyChild> of the new node. Return the new wrapping node or B<undef> if this is not possible.
 {my ($node, @context) = @_;                                                    # Node, optional context
  $node->wrapWith($node->tag, @context);
 }

sub wrapUp($@)                                                                  #U Wrap the specified B<$node> in a sequence of new nodes created from the specified B<@tags> forcing the original node down - deepening the L<parse|/parse> tree - return the array of wrapping nodes if want array else the last wrapping node.
 {my ($node, @tags) = @_;                                                       # Node to wrap, tags to wrap the node with - with the uppermost tag rightmost.
  my @n = map {$node = $node->wrapWith($_)} @tags;                              # Wrap up
  wantarray ? @n : @n ? $n[-1] : undef;
 }

sub wrapWithN($$@)                                                              #CU Wrap this B<$node> with the first B<$N> elements of B<@context>. If B<@context> contains more then B<$N> entries, the remainder are checked as the context of B<$node>. Returns the upper most wrapping node or B<undef> if the specified B<$node> does not match these conditions.
 {my ($node, $N, @context) = @_;                                                # Node, number of tags wrapping tags, tags and optional context
  @context >= $N or confess "Not enough context";                               # Must have at least enough tags to match N e
  my @tags; push @tags, shift @context for 1..$N;                               # Separate context into wrapping tags and context
  return undef if @context and !$node->at(@context);                            # Not in specified context
  for my $tag(@tags)                                                            # Wrap with each wrapping tag
   {$node = $node->wrapWith($tag);                                              # Wrap
   }
  $node                                                                         # Return last wrapping node
 }

sub wrapWithAll($@)                                                             #CU Wrap this B<$node> wrapped with the specified B<@tags> and return the last wrapping node.
 {my ($node, @tags) = @_;                                                       # Node, tags
  for my $tag(@tags)                                                            # Wrap with each wrapping tag
   {$node = $node->wrapWith($tag);                                              # Wrap
   }
  $node                                                                         # Return last wrapping node
 }

BEGIN{*ww=*wrapWithAll}

sub wrapDown($@)                                                                #U Wrap the content of the specified B<$node> in a sequence of new nodes forcing the original node up - deepening the L<parse|/parse> tree - return the array of wrapping nodes if want array else the last wrapping node.
 {my ($node, @tags) = @_;                                                       # Node to wrap, tags to wrap the node with - with the uppermost tag rightmost.
  my @n = map {$node = $node->wrapContentWith($_)} @tags;                       # Wrap down
  wantarray ? @n : @n ? $n[-1] : undef;
 }

sub wrapContentWith($$@)                                                        #U Wrap the content of the specified B<$node> in a new node created from the specified B<$tag> and B<%attributes>: the specified B<$node> then contains just the new node which, in turn, contains all the content of the specified B<$node>.\mReturns the new wrapping node.
 {my ($old, $tag, %attributes) = @_;                                            # Node, tag for new node, attributes for new node.
  my $new = newTag(undef, $tag, %attributes);                                   # Create wrapping node
  $new->parser  = $old->parser;                                                 # Assign the new node to the old parser
  $new->content = $old->content;                                                # Transfer content
  $old->content = [$new];                                                       # Insert new node
  $new->indexNode;                                                              # Create indices for new node
  $old->indexNode;                                                              # Rebuild indices for old mode
  $new                                                                          # Return new node
 }

BEGIN{*wcw=*wrapContentWith}

sub wrapContentWithDup($@)                                                      #U Wrap the content if the specified B<$node> in a new node with the same tag as $node in the optional B<@context> making the new node an L<only child|/isOnlyChild> of $node. Return the new wrapping node or B<undef> if this is not possible.
 {my ($node, @context) = @_;                                                    # Node, optional context
  $node->wrapContentWith($node->tag, @context);
 }

sub wrapSiblingsBefore($$@)                                                     #U If there are any siblings before the specified B<$node>, wrap them with a new node created from the specified B<$tag> and B<%attributes> and return the newly created node.\mReturns B<undef> if the specified B<$node> is the first node under its parent.
 {my ($node, $tag, %attributes) = @_;                                           # Node to wrap before, tag for new node, attributes for new node.
  my $first = $node->firstSibling;                                              # First sibling
  return undef if $node == $first;                                              # We are the first sibling so no wrapping is possible
  $first->wrapTo($node->prev, $tag, %attributes);                               # Wrap the preceding nodes
 }

sub wrapFromFirst($$@)                                                          #U Wrap this B<$node> and any preceding siblings with a new node created from the specified B<$tag> and B<%attributes> and return the wrapping node.
 {my ($node, $tag, %attributes) = @_;                                           # Node to wrap before, tag for new node, attributes for new node.
  $node->firstSibling->wrapTo($node, $tag, %attributes);                        # Wrap this node and any preceding nodes
 }

sub wrapFromFirstOrLastIn($$@)                                                  #U Wrap inclusively from the first sibling node to the specified B<$node> or from the last prior sibling node whose tag matches one of the tags in B<@targets> to the specified B<$node> using B<$tag> as the tag of the wrapping node and return the wrapping node.
 {my ($node, $tag, @targets) = @_;                                              # Node at which to start to wrap, wrapping tag, tags at which to end the wrap.
  my $target = $node->prevIn(@targets) // $node->firstSibling;                  # Target node
  $node->wrapFrom($target, $tag);                                               # Wrap from the target node to the specified node inclusively
 }

sub wrapFirstN($$$@)                                                            #U Wrap the first B<$N> nodes under this B<$node> in the optional B<@context> with the specified B<$tag> and return the new node or B<undef> if there are no such nodes to wrap.
 {my ($node, $N, $tag, @context) = @_;                                          # Node, number of nodes to wrap, wrapping tag, optional context
  return () if @context and !$node->at(@context);                               # Node not in specified context
  my @c = $node->contents;                                                      # Content
  pop @c while @c and @c > $N;                                                  # Remove excess content
  return undef unless @c == $N;                                                 # Check we have enough nodes to wrap
  $c[0]->wrapTo($c[-1], $tag);                                                  # Wrap the first $N nodes and return the wrapping node
 }

sub wrapSiblingsBetween($$$@)                                                   # If there are any siblings between the specified B<$node>s, wrap them with a new node created from the specified B<$tag> and B<%attributes>. Return the wrapping node else B<undef> if there are no nodes to wrap.
 {my ($first, $last, $tag, %attributes) = @_;                                   # First sibling, last sibling, tag for new node, attributes for new node.
  my $parent = $first->parent;                                                  # Check parentage
     $parent or confess "Cannot wrap between siblings using the root node";
     $parent == $last->parent or confess "Not siblings";

  my ($i, $j) = map {$_->position} my ($f, $l) = ($first, $last);               # Get the node indexes so we can order them
  return $f->next->wrapWith(          $tag, %attributes) if $i == $j - 2;       # One intervening node
  return $l->next->wrapWith(          $tag, %attributes) if $j == $i - 2;
  return $f->next->wrapTo  ($l->prev, $tag, %attributes) if $i <  $j - 2;       # Several intervening nodes
  return $l->next->wrapTo  ($f->prev, $tag, %attributes) if $j <  $i - 2;
  undef                                                                         # No intervening nodes
 }

sub wrapSiblingsAfter($$@)                                                      #U If there are any siblings after the specified B<$node>, wrap them with a new node created from the specified B<$tag> and B<%attributes> and return the newly created node.\mReturns B<undef> if the specified B<$node> is the last node under its parent.
 {my ($node, $tag, %attributes) = @_;                                           # Node to wrap before, tag for new node, attributes for new node.
  my $last = $node->lastSibling;                                                # Last sibling
  return undef if $node == $last;                                               # We are the last sibling so no wrapping is possible
  $node->next->wrapTo($last, $tag, %attributes);                                # Wrap the following nodes
 }

sub wrapNext($$@)                                                               #U Wrap this B<$node> and the following sibling with a new node created from the specified B<$tag>. If the optional context is specified then it should match the first node, next node and the context of the parent of the B<$node> as far as it is specified.  Return the new node created or B<undef> on failure.
 {my ($node, $tag, @context) = @_;                                              # Node to wrap before, tag for new node, context.
  return undef unless my $next   = $node->next;                                 # Next node
  return undef unless my $parent = $node->parent;                               # Parent node
  return undef if @context and $node->tag ne shift @context;                    # Node context
  return undef if @context and $next->tag ne shift @context;                    # Next node context
  return undef if @context and $parent->tag ne shift @context;                  # Parent context
  $node->wrapTo($next, $tag);                                                   # Wrap this node and the next node
 }

sub wrapNextN($$$@)                                                             #U Wrap this B<$node>and the next B<$N>-1 nodes following this B<$node> in the optional B<@context> with the specified B<$tag> and return the new node or B<undef> if there are no such nodes to wrap.
 {my ($node, $N, $tag, @context) = @_;                                          # Node, number of nodes to wrap, wrapping tag, optional context
  return () if @context and !$node->at(@context);                               # Node not in specified context
  my @c = ($node, $node->contentAfter);                                         # Content after node
  pop   @c while @c and @c > $N;                                                # Remove excess content
  return undef unless @c == $N;                                                 # Check we have enough nodes to wrap
  $c[0]->wrapTo($c[-1], $tag);                                                  # Wrap the next $N nodes and return the wrapping node
 }

sub wrapPrev($$@)                                                               #U Wrap this B<$node> and the preceding sibling with a new node created from the specified B<$tag>. If the optional context is specified then it should match the first node, previous node and the context of the parent of the B<$node> as far as it is specified.  Return the new node created or B<undef> on failure.
 {my ($node, $tag, @context) = @_;                                              # Node to wrap before, tag for new node, context.
  return undef unless my $prev   = $node->prev;                                 # Next node
  return undef unless my $parent = $node->parent;                               # Parent node
  return undef if @context and $node->tag ne shift @context;                    # Node context
  return undef if @context and $prev->tag ne shift @context;                    # Next node context
  return undef if @context and $parent->tag ne shift @context;                  # Parent context
  $prev->wrapTo($node, $tag);                                                   # Wrap this node and the previous node
 }

sub wrapPrevN($$$@)                                                             #U Wrap this B<$node>and the previous B<$N>-1 nodes following this B<$node> in the optional B<@context> with the specified B<$tag> and return the new node or B<undef> if there are no such nodes to wrap.
 {my ($node, $N, $tag, @context) = @_;                                          # Node, number of nodes to wrap, wrapping tag, optional context
  return () if @context and !$node->at(@context);                               # Node not in specified context
  my @c = ($node->contentBefore, $node);                                        # Content after node
  shift @c while @c and @c > $N;                                                # Remove excess content
  return undef unless @c == $N;                                                 # Check we have enough nodes to wrap
  $c[0]->wrapTo($c[-1], $tag);                                                  # Wrap the Prev $N nodes and return the wrapping node
 }

sub wrapToLast($$@)                                                             #U Wrap this B<$node> and any following siblings with a new node created from the specified B<$tag> and B<%attributes> and return the wrapping node.
 {my ($node, $tag, %attributes) = @_;                                           # Node to wrap before, tag for new node, attributes for new node.
  $node->wrapTo($node->lastSibling, $tag, %attributes);                         # Wrap this node and any preceding nodes
 }

sub wrapToLastOrFirstIn($$@)                                                    #U Wrap this B<$node> and any following siblings, up to and including the first sibling that matches one of the specified B<@find> nodes or all following siblings if no such match occurs, with a new node created from the specified B<$tag> and return the new wrapping node.
 {my ($node, $tag, @targets) = @_;                                              # Node at which to start to wrap, wrapping tag, tags at which to end the wrap.
  my $target = $node->nextIn(@targets) // $node->lastSibling;                   # Target node
  $node->wrapTo($target, $tag);                                                 # Wrap this node up to and including the target
 }

sub wrapLastN($$$@)                                                             #U Wrap the last B<$N> nodes under this B<$node> in the optional B<@context> with the specified B<$tag> and return the new node or B<undef> if there are no such nodes to wrap.
 {my ($node, $N, $tag, @context) = @_;                                          # Node, number of nodes to wrap, wrapping tag, optional context
  return () if @context and !$node->at(@context);                               # Node not in specified context
  my @c = $node->contents;                                                      # Content
  shift @c while @c and @c > $N;                                                # Remove excess content
  return undef unless @c == $N;                                                 # Check we have enough nodes to wrap
  $c[0]->wrapTo($c[-1], $tag);                                                  # Wrap the Last $N nodes and return the wrapping node
 }

sub wrapTo($$$@)                                                                #Y Wrap all the nodes from the B<$start> node to the B<$end> node inclusive with a new node created from the specified B<$tag> and B<%attributes> and return the new node.\mReturn B<undef> if the B<$start> and B<$end> nodes are not siblings - they must have the same parent for this method to work.
 {my ($start, $end, $tag, %attributes) = @_;                                    # Start node, end node, tag for the wrapping node, attributes for the wrapping node
  @_ >= 3 or confess "At least three parameters required";                      # The tag of the new node must be supplied!
  my $parent = $start->parent;                                                  # Parent
  confess "Start node has no parent" unless $parent;                            # Not possible unless the start node has a parent
  confess "End node has a different parent" unless $parent == $end->parent;     # Not possible unless the start and end nodes have the same parent
  my $s = $start->position;                                                     # Start position
  my $e = $end->position;                                                       # End position
  confess "End node precedes start node" if $e < $s;                            # End must not precede start node
  $start->putPrev(my $new = $start->newTag($tag, %attributes));                 # Create and insert wrapping node
  my @c = $parent->contents;                                                    # Content of parent
  $_ <  @c ? $new->putLast($c[$_]->cut) : undef for $s+1..$e+1;                 # Move the nodes from start to end into the new node remembering that the new node has already been inserted
  $new                                                                          # Return new node
 }

sub wrapFrom($$$%)                                                              #Y Wrap all the nodes from the B<$start> node to the B<$end> node with a new node created from the specified B<$tag> and B<%attributes> and return the new node.  Return B<undef> if the B<$start> and B<$end> nodes are not siblings - they must have the same parent for this method to work.
 {my ($end, $start, $tag, %attributes) = @_;                                    # End node, start node, tag for the wrapping node, attributes for the wrapping node
  $start->wrapTo($end, $tag, %attributes);                                      # Invert and wrapTo
 }

#D3 Unwrap                                                                      # Unwrap nodes to reduce the depth of the L<parse|/parse> tree

sub unwrap($@)                                                                  #CIYU Unwrap the specified B<$node> if in the optional B<@context> by replacing the node with its contents. Returns the parent node on success, otherwise B<undef> if an attempt is made to unwrap a text node or the root node.
 {my ($node, @context) = @_;                                                    # Node to unwrap, optional context.
  return undef if @context && !$node->at(@context) or $node->isText;            # Not in specified context or a text node
  my $parent = $node->parent;                                                   # Parent node
#  $parent or confess "Cannot unwrap the outer most node";                      # Root nodes cannot be unwrapped
  return undef unless $parent;                                                  # Root nodes cannot be unwrapped
  if ($node->isEmpty)                                                           # Empty nodes can just be cut out
   {$node->cut;
   }
  else
   {my $p = $parent->content;                                                   # Content array of parent
    my $n = $node->content;                                                     # Content array of node
    my $i = $node->position;                                                    # Position of node in parent
    splice(@$p, $i, 1, @$n);                                                    # Replace node with its content
    $parent->indexNode;                                                         # Rebuild indices for parent
    $node->disconnectLeafNode;                                                  # Disconnect node from parse tree
   }
  $parent                                                                       # Return the parent node
 }
#b <b id="b">C</b>
#c unwrap b
#d Delete a node retaining its content.

BEGIN{*u=*unwrap}

sub unwrapParentOfOnlyChild($@)                                                 #CU Unwrap the parent of the specified B<$node> in the optional B<@context> when the B<$node> is the only child of its parent. Return the specified B<$node> regardless unless the node is not in the optional context in which case return B<undef>.
 {my ($node, @context) = @_;                                                    # Node, optional context
  if ($node->isOnlyChild(@context))                                             # Only child of parent
   {$node->parent->unwrap;                                                      # Unwrap parent
    return $node;                                                               # Success
   }
  undef                                                                         # Not in specified context
 }

sub unwrapOnlyChild($@)                                                         #CU Unwrap the specified B<$node> in the optional B<@context> when the B<$node> is the only child of its parent. Return the specified B<$node> regardless unless the node is not in the optional context in which case return B<undef>.
 {my ($node, @context) = @_;                                                    # Node, optional context
  if ($node->isOnlyChild(@context))                                             # Only child of parent
   {$node->unwrap;                                                              # Unwrap parent
    return $node;                                                               # Success
   }
  undef                                                                         # Not in specified context
 }

sub unwrapParentsWithSingleChild($@)                                            #CU Unwrap any immediate ancestors of the specified B<$node> in the optional B<@context> which have only a single child and return the specified B<$node> regardless unless the node is not in the optional context in which case return B<undef>.
 {my ($node, @context) = @_;                                                    # Node, optional context
  return undef if @context && !$node->at(@context) or $node->isText;            # Not in specified context or a text node
  my @p;                                                                        # Parents with single child
  for(my $p = $node->parent; $p; $p = $p->parent)                               # Check each parent
   {$node->isOnlyChild and $p->parent ? push @p, $p : last                      # Locate parents with single child
   }
  $_->unwrap for @p;                                                            # Unwrap parents with single child
  $node                                                                         # Return node
 }

sub unwrapContentsKeepingText($@)                                               #CYU Unwrap all the non text nodes below the specified B<$node> adding a leading and a trailing space to prevent unwrapped content from being elided and return the specified B<$node> else B<undef> if not in the optional B<@context>.
 {my ($node, @context) = @_;                                                    # Node to unwrap, optional context.
  return undef if @context and !$node->at(@context);                            # Not in specified context
  $node->by(sub
   {if (!$_->isText and $_ != $node)                                            # Keep interior text nodes
     {$_->putPrevAsText(" ");                                                   # Separate from preceding content
      $_->putNextAsText(" ");                                                   # Separate from following content
      $node->addLabels($_->id) if $_->id;                                       # Transfer any id as a label to the specified B<$node>
      $_->copyLabels($node);                                                    # Transfer any labels to the specified node
      $_->unwrap;                                                               # Unwrap non text tag
     }
   });
  $node                                                                         # Return the node to show success
 }

sub wrapRuns($$@)                                                               #CU Wrap consecutive runs of children under the specified parent B<$node> that are not already wrapped with B<$wrap>. Returns an array of any wrapping nodes created.  Returns () if the specified B<$node> is not in the optional B<@context>.
 {my ($node, $wrap, @context) = @_;                                             # Node to unwrap, tag of wrapping node, optional context.
  return () if @context and !$node->at(@context);                               # Not in specified context
  my @wrap;                                                                     # Wrapping nodes created
  my @c = @$node;                                                               # Children
  my $w;                                                                        # The latest wrapping node
  for my $child(@c)                                                             # Each child node
   {if ($child->tag eq $wrap)                                                   # Wrapped nodes
     {$w = undef;                                                               # NO wrapper required
     }
    else                                                                        # A child that should be wrapped
     {unless($w)                                                                # No current wrapper
       {$child->putPrev($w = $node->newTag($wrap));                             # Add a wrapper
        push @wrap, $w;                                                         # Add a start node unless we are already have a node of the right type
       }
      $w->putLast($child->cut);                                                 # Put unwrapped child in last wrapper created
     }
   }
  @wrap                                                                         # Return array of new wrapping nodes
 }

#D1 Contents                                                                    # The children of each node.

sub contents($@)                                                                #K Return a list of all the nodes contained by the specified B<$node> or an empty list if the node is empty or not in the optional B<@context>.
 {my ($node, @context) = @_;                                                    # Node, optional context.
  return () if @context and !$node->at(@context);                               # Optionally check the context
  my $c = $node->content;                                                       # Contents reference
  $c ? @$c : ()                                                                 # Contents as a list
 }

sub contentAfter($@)                                                            #KU Return a list of all the sibling nodes following the specified B<$node> or an empty list if the specified B<$node> is last or not in the optional B<@context>.
 {my ($node, @context) = @_;                                                    # Node, optional context.
  return () if @context and !$node->at(@context);                               # Optionally check the context
  my $parent = $node->parent;                                                   # Parent
  return () if !$parent;                                                        # The uppermost node has no content beyond it
  my @c = $parent->contents;                                                    # Contents of parent
  while(@c)                                                                     # Test until no more nodes left to test
   {my $c = shift @c;                                                           # Position of current node
    return @c if $c == $node                                                    # Nodes beyond this node if it is the searched for node
   }
  confess "Node not found in parent";                                           # Something wrong with parent/child relationship
 }

BEGIN{*ca=*contentAfter}

sub contentBefore($@)                                                           #KU Return a list of all the sibling nodes preceding the specified B<$node> (in the normal sibling order) or an empty list if the specified B<$node> is last or not in the optional B<@context>.
 {my ($node, @context) = @_;                                                    # Node, optional context.
  return () if @context and !$node->at(@context);                               # Optionally check the context
  my $parent = $node->parent;                                                   # Parent
  return () if !$parent;                                                        # The uppermost node has no content beyond it
  my @c = $parent->contents;                                                    # Contents of parent
  while(@c)                                                                     # Test until no more nodes left to test
   {my $c = pop @c;                                                             # Position of current node
    return @c if $c == $node                                                    # Nodes beyond this node if it is the searched for node
   }
  confess "Node not found in parent";                                           # Something wrong with parent/child relationship
 }

BEGIN{*cb=*contentBefore}

sub contentAsTags($@)                                                           #KYU Return a string containing the tags of all the child nodes of the specified B<$node> separated by single spaces or the empty string if the node is empty or B<undef> if the node does not match the optional context. Use L<over|/over> to test the sequence of tags with a regular expression.
 {my ($node, @context) = @_;                                                    # Node, optional context.
  return undef if @context and !$node->at(@context);                            # Optionally check the context
  join ' ', map {$_->tag} $node->contents
 }

sub contentAsTags2($@)                                                          #KYU Return a string containing the tags of all the child nodes of the specified B<$node> separated by two spaces with a single space preceding the first tag and a single space following the last tag or the empty string if the node is empty or B<undef> if the node does not match the optional context. Use L<over2|/over2> to test the sequence of tags with a regular expression. Use L<over2|/over2> to test the sequence of tags with a regular expression.
 {my ($node, @context) = @_;                                                    # Node, optional context.
  return undef if @context and !$node->at(@context);                            # Optionally check the context
  join '', map {' '.$_->tag.' '} $node->contents
 }

sub contentAfterAsTags($@)                                                      #KU Return a string containing the tags of all the sibling nodes following the specified B<$node> separated by single spaces or the empty string if the node is empty or B<undef> if the node does not match the optional context. Use L<matchAfter|/matchAfter> to test the sequence of tags with a regular expression.
 {my ($node, @context) = @_;                                                    # Node, optional context.
  return undef if @context and !$node->at(@context);                            # Optionally check the context
  join ' ', map {$_->tag} $node->contentAfter
 }

sub contentAfterAsTags2($@)                                                     #KU Return a string containing the tags of all the sibling nodes following the specified B<$node> separated by two spaces with a single space preceding the first tag and a single space following the last tag or the empty string if the node is empty or B<undef> if the node does not match the optional context. Use L<matchAfter2|/matchAfter2> to test the sequence of tags with a regular expression.
 {my ($node, @context) = @_;                                                    # Node, optional context.
  return undef if @context and !$node->at(@context);                            # Optionally check the context
  join '', map {' '.$_->tag.' '} $node->contentAfter
 }

sub contentBeforeAsTags($@)                                                     #KU Return a string containing the tags of all the sibling nodes preceding the specified B<$node> separated by single spaces or the empty string if the node is empty or B<undef> if the node does not match the optional context. Use L<matchBefore|/matchBefore> to test the sequence of tags with a regular expression.
 {my ($node, @context) = @_;                                                    # Node, optional context.
  return undef if @context and !$node->at(@context);                            # Optionally check the context
  join ' ', map {$_->tag} $node->contentBefore
 }

sub contentBeforeAsTags2($@)                                                    #KU Return a string containing the tags of all the sibling nodes preceding the specified B<$node> separated by two spaces with a single space preceding the first tag and a single space following the last tag or the empty string if the node is empty or B<undef> if the node does not match the optional context.  Use L<matchBefore2|/matchBefore2> to test the sequence of tags with a regular expression.
 {my ($node, @context) = @_;                                                    # Node, optional context.
  return undef if @context and !$node->at(@context);                            # Optionally check the context
  join '', map {' '.$_->tag.' '} $node->contentBefore
 }

sub position($)                                                                 #U Return the index of the specified B<$node> in the content of the parent of the B<$node>.
 {my ($node) = @_;                                                              # Node.
  my @c = $node->parent->contents;                                              # Each node in parent content
  for(keys @c)                                                                  # Test each node
   {return $_ if $c[$_] == $node;                                               # Return index position of node which counts from zero
   }
  confess "Node not found in parent";                                           # Something wrong with parent/child relationship
 }

sub index($)                                                                    #U Return the index of the specified B<$node> in its parent index. Use L<position|/position> to find the position of a node under its parent.
 {my ($node) = @_;                                                              # Node.
  my $p = $node->parent;                                                        # Parent
  $p or confess "Cannot find index of root node";                               # Complain if we try to find the index of the root node
  if (my @c = $p->c($node->tag))                                                # Each node in parent index
   {for(keys @c)                                                                # Test each node
     {return $_ if $c[$_] == $node;                                             # Return index position of node which counts from zero
     }
   }
  confess "Node not found in parent";                                           # Something wrong with parent/child relationship
 }

sub present($@)                                                                 #U Return the count of the number of the specified tag types present immediately under a node or a hash {tag} = count for all the tags present under the node if no names are specified.
 {my ($node, @names) = @_;                                                      # Node, possible tags immediately under the node.
  reindexNode($node);                                                           # Create index for this node
  my %i = %{$node->indexes};                                                    # Index of child nodes
  return map {$_=>scalar @{$i{$_}}} keys %i unless @names;                      # Hash of all names
  grep {$i{$_}} @names                                                          # Count of tag types present
 }

sub editText($@) :lvalue                                                        #C Return the text of a text B<$node> as an lvalue string reference that can be modified by a regular expression, else as a reference to a dummy string that will be ignored. For a unitary version of this method see: L<changeText>
 {my ($node, @context) = @_;                                                    # Node to test, optional context
  my $dummy = "";                                                               # An empty string reference
  return $dummy if @context and !$node->at(@context);                           # Not in specified context
  return $node->text if $node->isText;                                          # Return reference to text if on a text node
  $dummy                                                                        # Nor on a text node
 }

sub isText($@)                                                                  #UCY Return the specified B<$node> if the specified B<$node> is a text node, optionally in the specified context, else return B<undef>.
 {my ($node, @context) = @_;                                                    # Node to test, optional context
  if (@context)                                                                 # Optionally check context
   {my $p = $node->parent;                                                      # Parent
    return undef if !$p or !$p->at(@context);                                   # Parent must match context
   }
  $node->tag eq cdata ? $node : undef
 }

sub isFirstText($@)                                                             #UCY Return the specified B<$node> if the specified B<$node> is a text node, the first node under its parent and that the parent is optionally in the specified context, else return B<undef>.
 {my ($node, @context) = @_;                                                    # Node to test, optional context for parent
  return undef unless $node->isText(@context) and $node->isFirst;               # Check that this node is a text node, that it is first, and, optionally check context of parent
  $node                                                                         # Return the node as it passes all tests
 }

sub isLastText($@)                                                              #UCY Return the specified B<$node> if the specified B<$node> is a text node, the last node under its parent and that the parent is optionally in the specified context, else return B<undef>.
 {my ($node, @context) = @_;                                                    # Node to test, optional context for parent
  return undef unless $node->isText(@context) and $node->isLast;                # Check that this node is a text node, that it is last, and, optionally check context of parent
  $node                                                                         # Return the node as it passes all tests
 }

sub matchTree($@)                                                               #UC Return a list of nodes that match the specified tree of match expressions, else B<()> if one or more match expressions fail to match nodes in the tree below the specified start node. A match expression consists of [parent node tag, [match expressions to be matched by children of parent]|tags of child nodes to match starting at the first node]. Match expressions for a single item do need to be surrounded with [] and can be merged into their predecessor. The outermost match expression should not be enclosed in [].
 {my ($node, @match) = @_;                                                      # Node to start matching from, tree of match expressions.
  my ($tag, @S) = @match;                                                       # Tag we must match, sub tag specifications
  my @nodes;                                                                    # Matching nodes
  return () unless atPositionMatch(-t $node, $tag);                             # Match tag of node
  push @nodes, $node;                                                           # The current node matches so save it
  if (my @s = map{ref($_) ? $_ : [$_]} @S)                                      # Wrap sub tags that are not references with [] so that the caller does not have to because it is a bit tedious wrapping each word in quotes and [] brackets.
   {my @c = $node->contents;                                                    # Contents of node
    return () unless @s <= @c;                                                  # Confirm that there is a node to match against for each match expression
    for my $c(@c)                                                               # Confirm each sub tag matches its sub tag expression
     {push @nodes, my @m = $c->matchTree(@{shift @s});                          # Save sub match
      return () unless @m;                                                      # No sub match so return an empty list
     }
   }
  @nodes                                                                        # The nodes that matched each match expression
 }

sub matchesText($$@)                                                            #UC Returns an array of regular expression matches in the text of the specified B<$node> if it is text node and it matches the specified regular expression and optionally has the specified context otherwise returns an empty array.
 {my ($node, $re, @context) = @_;                                               # Node to test, regular expression, optional context
  return () unless $node->isText(@context);                                     # Check that this is a text node
  $node->text =~ m($re);                                                        # Return array of matches - do not add 'g' as a modifier to the regular expression as the pos() feature of Perl regular expressions will then cause matches to fail that otherwise would not.
 }

sub isBlankText($@)                                                             #UCY Return the specified B<$node> if the specified B<$node> is a text node, optionally in the specified context, and contains nothing other than white space else return B<undef>. See also: L<isAllBlankText|/isAllBlankText>
 {my ($node, @context) = @_;                                                    # Node to test, optional context
  return undef if @context and !$node->at(@context);                            # Optionally check context
  $node->isText && $node->text =~ /\A\s*\Z/s ? $node : undef
 }

sub isAllBlankText($@)                                                          #UCY Return the specified B<$node> if the specified B<$node>, optionally in the specified context, does not contain anything or if it does contain something it is all white space else return B<undef>. See also: L<bitsNodeTextBlank|/bitsNodeTextBlank>
 {my ($node, @context) = @_;                                                    # Node to test, optional context
  return undef if @context and !$node->at(@context);                            # Optionally check context
  if ($node->isText)                                                            # If this is a text node test the text of the node
   {return $node->text =~ m(\A\s*\Z)s ? $node : undef;
   }
  my @c = $node->contents;
  return $node if @c == 0;                                                      # No content
  return undef if @c  > 1;                                                      # Content other than text (adjacent text elements are merged so there can only be one)
  $node->stringContent =~ m(\A\s*\Z)s ? $node : undef
 }

sub isOnlyChildBlankText($@)                                                    #UC Return the specified B<$node> if it is a blank text node and an only child else return B<undef>.
 {my ($node, @context) = @_;                                                    # Node to test, optional context
  return undef unless &isOnlyChildText(@_);                                     # Confirm that this is an only text child
  $node->text =~ m(\A\s*\Z)s;                                                   # All blank
 }

sub bitsNodeTextBlank($)                                                        #U Return a bit string that shows if there are any non text nodes, text nodes or blank text nodes under a node. An empty string is returned if there are no child nodes.
 {my ($node) = @_;                                                              # Node to test.
  my ($n, $t, $b) = (0,0,0);                                                    # Non text, text, blank text count
  my @c = $node->contents;                                                      # Contents of node
  return '' unless @c;                                                          # Return empty string if no children

  for(@c)                                                                       # Contents of node
   {if ($_->isText)                                                             # Text node
     {++$t;
      ++$b if $_->isBlankText;                                                  # Blank text node
     }
    else                                                                        # Non text node
     {++$n;
     }
   }
  join '', map {$_ ? 1 : 0} ($n, $t, $b);                                       # Multiple content so there must be some tags present because L<indexNode|/indexNode> concatenates contiguous text
 }

#D1 Number                                                                      # Number the nodes of a parse tree so that they can be easily retrieved by number - either by a person reading the source L<XML> or programmatically.

sub findByNumber($$)                                                            #UY Find the node with the specified number as made visible by L<prettyStringNumbered|/prettyStringNumbered> in the L<parse|/parse> tree containing the specified B<$node> and return the found node or B<undef> if no such node exists.
 {my ($node, $number) = @_;                                                     # Node in the parse tree to search, number of the node required.
  $node->parser->numbers->[$number]
 }

sub findByNumbers($@)                                                           #U Find the nodes with the specified numbers as made visible by L<prettyStringNumbered|/prettyStringNumbered> in the L<parse|/parse> tree containing the specified B<$node> and return the found nodes in a list with B<undef> for nodes that do not exist.
 {my ($node, @numbers) = @_;                                                    # Node in the parse tree to search, numbers of the nodes required.
  map {$node->findByNumber($_)} @numbers                                        # Node corresponding to each number
 }

sub numberNode($)                                                               #UP Ensure that the specified B<$node> has a number.
 {my ($node) = @_;                                                              # Node
  my $n = $node->number = ++($node->parser->numbering);                         # Number node
  $node->parser->numbers->[$n] = $node                                          # Index the nodes in a parse tree
 }

sub numberTree($)                                                               #U Number the nodes in a L<parse|/parse> tree in pre-order so they are numbered in the same sequence that they appear in the source. You can see the numbers by printing the tree with L<prettyStringNumbered|/prettyStringNumbered>.  Nodes can be found using L<findByNumber|/findByNumber>.  This method differs from L<forestNumberTrees|/forestNumberTrees> in that avoids overwriting the B<id=> attribute of each node by using a system attribute instead; this system attribute can then be made visible on the id attribute of each node by printing the parse tree with L<prettyStringNumbered|/prettyStringNumbered>.
 {my ($node) = @_;                                                              # Node
  my $parser = $node->parser;                                                   # Top of tree
  my $n = 0;                                                                    # Node number
  $parser->down(sub {$parser->numbers->[$_->number = ++$n] = $_});              # Number the nodes in a parse tree in pre-order so they are numbered in the same sequence that they appear in the source
 }

sub indexIds($)                                                                 #U Return a map of the ids at and below the specified B<$node>.
 {my ($node) = @_;                                                              # Node
  my %ids;                                                                      # Id map
  $node->by(sub                                                                 # Map the nodes
   {if (my $id = $_->id)                                                        # Ignore text nodes and nodes that already have an id
     {$ids{$id} and confess "Duplicate id $id";                                 # Confess to duplicate id under the specified node
      $ids{$id} = $_;                                                           # Index node by id
     }
   });
  \%ids                                                                         # Return the index
 }

sub numberTreesJustIds($$)                                                      #U Number the ids of the nodes in a L<parse|/parse> tree in pre-order so they are numbered in the same sequence that they appear in the source. You can see the numbers by printing the tree with L<prettyStringNumbered()|/prettyStringNumbered>. This method differs from L<numberTree|/numberTree> in that only non text nodes without ids are numbered. The number applied to each node consists of the concatenation of the specified prefix, an underscore and a number that is unique within the specifed L<parse|/parse> tree. Consequently the ids across several trees trees can be made unique by supplying different prefixes for each tree.  Nodes can be found using L<findByNumber|/findByNumber>.  Returns the specified B<$node>.
 {my ($node, $prefix) = @_;                                                     # Node, prefix for each id at and under the specified B<$node>
  my $parser = $node->parser;                                                   # Root node
  my $n = 0;                                                                    # Node number
  $node->down(sub                                                               # Number the nodes in the parse tree in pre-order so they are numbered in the same sequence that they appear in the source. Add the prefix to each node number.
   {if (!$_->isText and !$_->id)                                                # Ignore text nodes and nodes that already have an id
     {$_->id = $prefix.++$n;                                                    # Set id with prefix
     }
   }) if $prefix;
  $node->down(sub                                                               # Number the nodes in the parse tree in pre-order so they are numbered in the same sequence that they appear in the source. Add the prefix to each node number.
   {if (!$_->isText and !$_->id)                                                # Ignore text nodes and nodes that already have an id
     {$_->id = ++$n;                                                            # Set id without prefix
     }
   }) unless $prefix;
  $node                                                                         # Return the specified node
 }

#D1 Forest Numbers                                                              # Number the nodes of several parse trees so that they can be easily retrieved by forest number - either by a person reading the source L<XML> or programmatically.

sub forestNumberTrees($$)                                                       #U Number the ids of the nodes in a L<parse|/parse> tree in pre-order so they are numbered in the same sequence that they appear in the source. You can see the numbers by printing the tree with L<prettyString|/prettyString>. This method differs from L<numberTree|/numberTree> in that only non text nodes are numbered and nodes with existing B<id=> attributes have the value of their B<id=> attribute transferred to a L<label|/Labels>. The number applied to each node consists of the concatenation of the specified tree number, an underscore and a number that is unique within the specified L<parse|/parse> tree. Consequently the ids across several trees can be made unique by supplying a different tree number for each tree.  Nodes can be found subsequently using L<findByForestNumber|/findByForestNumber>.  Returns the specified B<$node>.
 {my ($node, $prefix) = @_;                                                     # Node in parse tree to be numbered, tree number
  my $parser = $node->parser;                                                   # Root node
  defined($prefix) or confess "prefix required";                                # Typically the prefix separates parallel processes and so it is a good idea to have one
  $prefix =~ m(\A\d+\Z)s or confess "prefix must be an integer \\d+";           # Insist on a numeric prefix
  $parser->down(sub                                                             # Number the nodes in the parse tree in pre-order so they are numbered in the same sequence that they appear in the source
   {if (!$_->isText)                                                            # Number non text nodes
     {$_->addLabels($_->id) if $_->id;                                          # Make any existing id a label
      my $n  = $prefix.q(_).(scalar(keys %{$parser->forestNumbers}) + 1);       # Id number
      $_->id = $n;                                                              # Set id
      $parser->forestNumbers->{$n} = $_;                                        # Index node
     }
   });
  $node                                                                         # Return the specified node
 }

sub findByForestNumber($$$)                                                     #U Find the node with the specified L<forest number|/forestNumberTrees> as made visible on the id attribute by L<prettyStringNumbered|/prettyStringNumbered> in the L<parse|/parse> tree containing the specified B<$node> and return the found node or B<undef> if no such node exists.
 {my ($node, $tree, $id) = @_;                                                  # Node in the parse tree to search, forest number, id number of the node required.
  $node->parser->forestNumbers->{$tree.q(_).$id}
 }

#D1 Order                                                                       # Check the order and relative position of nodes in a parse tree.

sub above($$@)                                                                  #CY Return the first node if the first node is above the second node optionally checking that the first node is in the specified context otherwise return B<undef>
 {my ($first, $second, @context) = @_;                                          # First node, second node, optional context
  return undef if @context and !$first->at(@context);                           # Not in specified context
  return undef if $first == $second;                                            # A node cannot be above itself
  my @f = $first ->ancestry;
  my @s = $second->ancestry;
  pop @f, pop @s while @f and @s and $f[-1] == $s[-1];                          # Find first different ancestor
  !@f ? $first : undef                                                          # Node is above target if its ancestors are all ancestors of target
 }

sub abovePath($$)                                                               # Return the nodes along the path from the first node down to the second node when the first node is above the second node else return B<()>.
 {my ($first, $second) = @_;                                                    # First node, second node
  return ($first) if $first == $second;                                         # A node cannot be above itself
  my @f = $first ->ancestry;
  my @s = $second->ancestry;
  pop @f, pop @s while @f and @s and $f[-1] == $s[-1];                          # Find first different ancestor
  !@f ? ($first, reverse @s) : ()                                               # Node is above target if its ancestors are all ancestors of target
 }

sub below($$@)                                                                  #CY Return the first node if the first node is below the second node optionally checking that the first node is in the specified context otherwise return B<undef>
 {my ($first, $second, @context) = @_;                                          # First node, second node, optional context
  $second->above($first, @context);                                             # The second node is above the first node if the first node is below the second node
 }

sub belowPath($$)                                                               # Return the nodes along the path from the first node up to the second node when the first node is below the second node else return B<()>.
 {my ($first, $second) = @_;                                                    # First node, second node
  reverse $second->abovePath($first)
 }

sub after($$@)                                                                  #CY Return the first node if it occurs after the second node in the L<parse|/parse> tree optionally checking that the first node is in the specified context or else B<undef> if the node is L<above|/above>, L<below|/below> or L<before|/before> the target.
 {my ($first, $second, @context) = @_;                                          # First node, second node, optional context
  return undef if @context and !$first->at(@context);                           # First node not in specified context
  my @n = $first ->ancestry;
  my @t = $second->ancestry;
  pop @n, pop @t while @n and @t and $n[-1] == $t[-1];                          # Find first different ancestor
  return undef unless @n and @t;                                                # Undef if we cannot decide
  $n[-1]->position > $t[-1]->position                                           # Node relative to target at first common ancestor
 }

sub before($$@)                                                                 #CY Return the first node if it occurs before the second node in the L<parse|/parse> tree optionally checking that the first node is in the specified context or else B<undef> if the node is L<above|/above>, L<below|/below> or L<before|/before> the target.
 {my ($first, $second, @context) = @_;                                          # First node, second node, optional context
  $second->after($first, @context);                                             # The first node is before the second node if the second node is after the first node
 }

sub disordered($@)                                                              # Return the first node that is out of the specified order when performing a pre-ordered traversal of the L<parse|/parse> tree.
 {my ($node, @nodes) = @_;                                                      # Node, following nodes.
  my $c = $node;                                                                # Node we are currently checking for
  $node->parser->down(sub {$c = shift @nodes while $c and $_ == $c});           # Preorder traversal from root looking for each specified node
  $c                                                                            # Disordered if we could not find this node
 }

sub commonAncestor($@)                                                          #Y Find the most recent common ancestor of the specified nodes or B<undef> if there is no common ancestor.
 {my ($node, @nodes) = @_;                                                      # Node, @nodes
  return $node unless @nodes;                                                   # A single node is it its own common ancestor
  my @n = $node->ancestry;                                                      # The common ancestor so far
  for(@nodes)                                                                   # Each node
   {my @t = $_->ancestry;                                                       # Ancestry of latest node
    my @c;                                                                      # Common ancestors
    while(@n and @t and $n[-1] == $t[-1])                                       # Find common ancestors
     {push @c, pop @n; pop @t;                                                  # Save common ancestor
     }
    return undef unless @c;                                                     # No common ancestors
    @n = reverse @c;                                                            # Update common ancestry so far
   }
  $n[0]                                                                         # Most recent common ancestor
 }

sub commonAdjacentAncestors($$)                                                 # Given two nodes, find a pair of adjacent ancestral siblings if such a pair exists else return B<()>.
 {my ($first, $second) = @_;                                                    # First node, second node
  my @f = $first->ancestry;                                                     # Ancestors of the first node
  my @s = $second->ancestry;                                                    # Ancestors of the second node
  while(@f and @s and $f[-1] == $s[-1])                                         # Remove common ancestors
   {pop @f; pop @s;
   }
  return () unless @f and @s;                                                   # No common ancestors
  my ($f, $s) = ($f[-1], $s[-1]);                                               # Possible common pair of ancestral siblings
  return ($f, $s) if $f->adjacent($s);                                          # Return first diverging siblings if they exist
  ()                                                                            # No such pair exists
 }

sub ordered($@)                                                                 #Y Return the first node if the specified nodes are all in order when performing a pre-ordered traversal of the L<parse|/parse> tree else return B<undef>.
 {my ($node, @nodes) = @_;                                                      # Node, following nodes.
  &disordered(@_) ? undef : $node
 }

#D1 Patching                                                                    # Analyze two similar L<parse|/parse> trees and create a patch that transforms the first L<parse|/parse> tree into the second as long as each tree has the same tag and id structure with each id being unique.

sub createPatch($$)                                                             # Create a patch that moves the source L<parse|/parse> tree to the target L<parse|/parse> tree node as long as they have the same tag and id structure with each id being unique.
 {my ($a, $A) = @_;                                                             # Source parse tree, target parse tree
  my @patches;
  my $I = $A->indexIds;
  $a->by(sub
   {my ($o) = @_;
    return unless my $id = $o->id;
    if (my $O = $I->{$id})
     {for my $a($o->getAttrs)
       {my ($v, $V)= ($o->attr($a), $O->attr($a));                              # Values
        if (defined($V) and $v ne $V)
         {push @patches, [q(changeValue), $id, $a, $v, $V]                      # Set attribute
         }
        elsif (!defined($V))
         {push @patches, [q(deleteAttr), $id, $a, $v]                           # Set attribute
         }
       }
      for my $A($O->getAttrs)
       {next if $o->attr($A);
        push @patches, [q(createAttr), $id, $A, $O->attr($A)];                  # Set attribute
       }

      if (my $f = $o->first)                                                    # First following text
       {if ($f->isText)
         {if (my $F = $O->first)
           {if ($F->isText)
             {if ($f->text ne $F->text)
               {push @patches, [q(firstText), $id, $f->text, $F->text];
               }
             }
            else
             {confess "Text expected first after node $id:\n".$f->text;
             }
           }
          else
           {confess "Node expected first after node $id:\n".$f->text;
           }
         }
       }

      if (my $n = $o->next)                                                     # Following text
       {if ($n->isText)
         {if (my $N = $O->next)
           {if ($N->isText)
             {if ($n->text ne $N->text)
               {push @patches, [q(nextText), $id, $n->text, $N->text];
               }
             }
            else
             {confess "Text expected after node $id:\n".$n->text;
             }
           }
          else
           {confess "Node expected after node $id:\n".$n->text;
           }
         }
       }

      if (my $p = $o->prev)                                                     # Preceding text
       {if ($p->isText)
         {if (my $P = $O->prev)
           {if ($P->isText)
             {if ($p->text ne $P->text)
               {push @patches, [q(prevText), $id, $p->text, $P->text];
               }
             }
            else
             {confess "Text expected before node $id:\n".$p->text;
             }
           }
          else
           {confess "Node expected before node $id:\n".$p->text;
           }
         }
       }

      if (my $l = $o->last)                                                     # Last preceding text
       {if ($l->isText)
         {if (my $L = $O->last)
           {if ($L->isText)
             {if ($l->text ne $L->text)
               {push @patches, [q(lastText), $id, $l->text, $L->text];
               }
             }
            else
             {confess "Text expected last before node $id:\n".$l->text;
             }
           }
          else
           {confess "Node expected last before node $id:\n".$l->text;
           }
         }
       }
     }
    else
     {confess "No matching id for $id";
     }
   });
  bless [@patches], q(Data::Edit::Xml::Patch);                                  # Return patch
 }

sub Data::Edit::Xml::Patch::install($$)                                         # Replay a patch created by L<createPatch|/createPatch> against a L<parse|/parse> tree that has the same tag and id structure with each id being unique.
 {my ($patches, $a) = @_;                                                       # Patch, parse tree
  my $i = $a->indexIds;
  for my $patch(@$patches)
   {my ($c, $id) = @$patch;
    my $o = $i->{$id} or confess "No node with id $id";
    if ($c eq q(changeValue))
     {my (undef, undef, $a, $v, $V) = @$patch;
      $o->attr($a) eq $v or confess "Expected $a=$v but got $a=".$o->attr($a);
      $o->setAttr($a, $V);
     }
    elsif ($c eq q(deleteAttr))
     {my (undef, undef, $a, $v) = @$patch;
      $o->attr($a) eq $v or confess "Expected $a=$v but got $a=".$o->attr($a);
      $o->deleteAttr($a);
     }
    elsif ($c eq q(createAttr))
     {my (undef, undef, $a, $v) = @$patch;
      $o->setAttr($a, $v);
     }
    else
     {my (undef, undef, $t, $T) = @$patch;
      my $update = sub
       {my ($o, $pos) = @_;
        $o                   or confess      "Node expected $pos node $id";
        my $s = $o->text;
        $s                   or confess "Text node expected $pos node $id";
        $s eq $t or $s eq $T or confess      "Text expected $pos node $id should be:\n$s";
        $o->text = $T unless $s eq $T;
       };
      if    ($c eq q(firstText)) {$update->($o->first, q(first after))}
      elsif ($c eq q(nextText))  {$update->($o->next,  q(after))}
      elsif ($c eq q(prevText))  {$update->($o->prev,  q(before))}
      elsif ($c eq q(lastText))  {$update->($o->last,  q(last before))}
      else                       {confess "Unknown command $c"}
     }
   }
 }

#D1 Propogating                                                                 # Propagate parent node attributes through a parse tree.

sub propagate($$@)                                                              #UC Propagate L<new attributes|/copyNewAttrs> from nodes that match the specified tag to all their child nodes, then L<unwrap|/unwrap> all the nodes that match the specified tag. Return the specified parse tree.
 {my ($tree, $tag, @context) = @_;                                              # Parse tree, tag of nodes whose attributes are to be propagated, optional context for parse tree
  return undef if @context and !$tree->at(@context);                            # Not in specified context
  $tree->by(sub                                                                 # Copy new attributes from each matching parent for each node in the parse tree
   {my ($node, @parents) = @_;                                                  # Node, parents
    for my $parent(@parents)                                                    # For each parent of the node being visited
     {$parent->copyNewAttrs($node) if -t $parent eq $tag;                       # Copy the new attributes of a parent with the specified tag
     }
   });
  $tree->by(sub                                                                 # Unwrap nodes with the specified tag unless they are the starting node
   {my ($node) = @_;                                                            # Node
    $node->unwrap if $node != $tree and -t $node eq $tag;                       # Unwrap the node if it matches the specified tag
   });
  $tree                                                                         # Return the parse tree
 }

#D1 Table of Contents                                                           # Analyze and generate tables of contents.

sub tocNumbers($@)                                                              #U Table of Contents number the nodes in a L<parse|/parse> tree.
 {my ($node, @match) = @_;                                                      # Node, optional list of tags to descend into else all tags will be descended into
  my $toc = {};
  my $match = @match ? {map{$_=>1} @match} : undef;                             # Tags to match or none
  my @context;

  my $tree; $tree = sub                                                         # Number the nodes below the current node
   {my ($node) = @_;
    my $n = 0;
    for($node->contents)                                                        # Each node below the current node
     {next if $match and !$match->{$_->tag};                                    # Skip non matching nodes
      push @context, ++$n;                                                      # New scope
      $toc->{"@context"} = $_;                                                  # Toc number for tag
      &$tree($_);                                                               # Number sub tree
      pop @context;                                                             # End scope
     }
   };

  &$tree($node);                                                                # Descend through the tree numbering matching nodes
  $toc                                                                          # Return {toc number} = <tag>
 }

#D1 Labels                                                                      # Label nodes so that they can be cross referenced and linked by L<Data::Edit::Xml::Lint>

sub addLabels($@)                                                               #U Add the named labels to the specified B<$node> and return the number of labels added. Labels that are not L<defined|https://perldoc.perl.org/functions/defined.html> will be ignored.
 {my ($node, @labels) = @_;                                                     # Node in parse tree, names of labels to add.
  my $l = $node->labels;                                                        # Labels on node
  my $n = keys %$l;                                                             # Count the number of labels at the start
  $l->{$_}++ for grep {trim($_)} @labels;                                       # Labels must be defined to be added. Remove leading and trailing white space to prevent Data::Edit::Xml::Lint complaining about it: justified by the assumption that it is a data entry error.
  keys(%$l) - $n                                                                # Number of labels added
 }

sub countLabels($)                                                              #U Return the count of the number of labels at a node.
 {my ($node) = @_;                                                              # Node in parse tree.
  scalar keys %{$node->labels}                                                  # Count of labels
 }

sub labelsInTree($)                                                             #U Return a hash of all the labels in a tree
 {my ($tree) = @_;                                                              # Parse tree.
  my %labels;                                                                   # Labels found
  $tree->by(sub                                                                 # Labels for each node in the parse tree
   {for($_->getLabels)                                                          # Each label
     {$labels{$_}++
     }
   });
  \%labels                                                                      # Hash of labels in the tree
 }

sub getLabels($)                                                                #U Return the names of all the labels set on a node.
 {my ($node) = @_;                                                              # Node in parse tree.
  sort keys %{$node->labels}
 }

sub deleteLabels($@)                                                            #U Delete the specified labels in the specified B<$node> or all labels if no labels have are specified and return that node.
 {my ($node, @labels) = @_;                                                     # Node in parse tree, names of the labels to be deleted
  my $n = keys %{$node->{labels}};                                              # Number of labels at start
  $node->{labels} = {} unless @labels;                                          # Delete all the labels if no labels supplied
  delete @{$node->{labels}}{@labels};                                           # Delete specified labels
  $n - keys %{$node->{labels}}                                                  # Number of labels deleted
 }

sub copyLabels($$)                                                              # Copy all the labels from the source node to the target node and return the source node.
 {my ($source, $target) = @_;                                                   # Source node, target node.
  $target->addLabels($source->getLabels);                                       # Copy all the labels from the source to the target
 }

sub moveLabels($$)                                                              # Move all the labels from the source node to the target node and return the source node.
 {my ($source, $target) = @_;                                                   # Source node, target node.
  $target->addLabels($source->getLabels);                                       # Copy all the labels from the source to the target
  $source->deleteLabels;                                                        # Delete all the labels from the source
 }

sub copyLabelsAndIdsInTree($$)                                                  # Copy all the labels and ids in the source parse tree to the matching nodes in the target parse tree. Nodes are matched via L<path|/path>. Return the number of labels and ids copied.
 {my ($source, $target) = @_;                                                   # Source node, target node.
  my $n = 0;                                                                    # Count modifications
  $target->by(sub                                                               # Scan the target
   {my ($o) = @_;
    my @p = $o->path;                                                           # Matching node in source
    if (my $q = $source->go(@p))
     {$n += $q->copyLabels($o) + $o->addLabels($q->id);                         # Copy labels and id from source to target
     }
   });
  $n                                                                            # Number of labels added
 }

sub giveEveryIdAGuid($$)                                                        #U Give a guid to every node in the specified B<$tree> that has an id attribute, saving any existing id attribute as a label, and return the count of the number of such replacements made.
 {my ($tree, $genGuid) = @_;                                                    # Tree, a sub that accepts a number and a node and returns a new Guid each time it is called
  my $n = 0;                                                                    # count the number of ids replaced with guids
  $tree->by(sub                                                                 # Scan the tree
   {my ($o) = @_;
    if (my $i = $o->id)                                                         # Ignore this node unless it has an id
     {if ($i !~ m(\AGUID)is)                                                    # Ignore this node if it already has a guid
       {$o->addLabels($i);                                                      # Make the current id into a label.
        $o->id = $genGuid->(++$n, $o);                                          # Replace the existing id with the next guid
       }
     }
   });
  $n                                                                            # Number of ids replaced with guids
 }

sub createGuidId($@)                                                            #CU Create an id for the specified B<$node> in the optional B<@context> from the md5Sum of its content moving any existing id to the labels associated with the B<$node> and return the existing B<$node>.
 {my ($node, @context) = @_;                                                    # Node, optional context
  return undef if @context and !$node->at(@context);                            # Not in specified context
  if (my $i = $node->id)                                                        # Save any current id if  any
   {$node->addLabels($i);                                                       # Save id as a label
   }
  $node->id = guidFromMd5 $node->stringAsMd5Sum;                                # Guid from MD5 of content
  $node                                                                         # Return existing node
 }
#a
#b <b><c/></b>
#c createGuidId b
#d Use the content of the current node to create a globally unique id for this node.

#D1 Operators                                                                   # Operator access to methods use the assign versions to avoid 'useless use of operator in void context' messages. Use the non assign versions to return the results of the underlying method call.  Thus '/' returns the wrapping node, whilst '/=' does not.  Assign operators always return their left hand side even though the corresponding method usually returns the modification on the right.

use overload
  '='        => sub{$_[0]},
  '**'       => \&opNew,
  '-X'       => \&opString,
  '@{}'      => \&opContents,
  '<='       => \&opAt,
  '>>'       => \&opPutFirst,
  '>>='      => \&opPutFirstAssign,
  '<<'       => \&opPutLast,
  '<<='      => \&opPutLastAssign,
  '>'        => \&opPutNext,
  '+='       => \&opPutNextAssign,
  '+'        => \&opPutNext,
  '<'        => \&opPutPrev,
  '-='       => \&opPutPrevAssign,
  '-'        => \&opPutPrev,
  'x='       => \&opBy,
  'x'        => \&opBy,
  '>='       => \&opGo,
  '*'        => \&opWrapContentWith,                                            # doc
  '*='       => \&opWrapContentWith,
  '/'        => \&opWrapWith,
  '/='       => \&opWrapWith,
  '%'        => \&opAttr,
  '--'       => \&opCut,                                                        # Better using -X
  '++'       => \&opUnwrap,                                                     # doc # Better using -u
  "fallback" => 1;

sub opString($$)                                                                # -A: L<printNode|/printNode>\m-B: L<bitsNodeTextBlank|/bitsNodeTextBlank>\m-b: L<isAllBlankText|/isAllBlankText>\m-C: L<stringContent|/stringContent>\m-c: L<context|/context>\m-d: L<depth|/depth>\m-e: L<prettyStringEnd|/prettyStringEnd>\m-f: L<first node|/first>\m-g: L<createGuidId|/createGuidId>\m-k: L<createGuidId|/createGuidId>\m-l: L<last node|/last>\m-M: L<stringAsMd5Sum|/stringAsMd5Sum>\m-O: L<contentAsTags2|/contentAsTags2>\m-o: L<contentAsTags|/contentAsTags>\m-p: L<prettyString|/prettyString>\m-R: L<requiredCleanup|/requiredCleanup>\m-r: L<stringTagsAndText|/stringTagsAndText>\m-S: L<printStack|/printStack>\m-s: L<string|/string>\m-T: L<isText|/isText>\m-t: L<tag|/tag>\m-u: L<unwrap|/unwrap>\m-W: L<id|/id>\m-w: L<stringQuoted|/stringQuoted>\m-X: L<prettyStringDitaHeaders|/prettyStringDitaHeaders>\m-x: L<cut|/cut>\m-z: L<prettyStringNumbered|/prettyStringNumbered>.
 {my ($node, $op) = @_;                                                         # Node, monadic operator.
  $op or confess;
  return $node->printNode                    if $op eq 'A';
  return $node->bitsNodeTextBlank            if $op eq 'B';  # Not much use
  return $node->isAllBlankText               if $op eq 'b';
  return $node->stringContent                if $op eq 'C';
  return $node->context                      if $op eq 'c';
  return $node->depth                        if $op eq 'd';  # Not much use
  return $node->prettyStringEnd              if $op eq 'e';  # Not much use
  return $node->first                        if $op eq 'f';  # Not much use
  return $node->createGuidId                 if $op eq 'g';
  return $node->createGuidId                 if $op eq 'k';  # Not in use
  return $node->last                         if $op eq 'l';  # Not much use
  return $node->stringAsMd5Sum               if $op eq 'M';
  return $node->contentAsTags2               if $op eq 'O';
  return $node->contentAsTags                if $op eq 'o';
  return $node->prettyString                 if $op eq 'p';
  return $node->requiredCleanUp              if $op eq 'R';  # Not much use
  return $node->stringTagsAndText            if $op eq 'r';  # Not much use
  return $node->printStack                   if $op eq 'S';
  return $node->string                       if $op eq 's';
  return $node->isText                       if $op eq 'T';
  return $node->tag                          if $op eq 't';
  return $node->unwrap                       if $op eq 'u';
  return $node->id                           if $op eq 'W';  # Not much use
  return $node->stringQuoted                 if $op eq 'w';  # Not much use
  return $node->prettyStringDitaHeaders      if $op eq 'X';
  return $node->cut                          if $op eq 'x';
  return $node->prettyStringNumbered         if $op eq 'z';  # Not much use
  confess "Unknown operator: $op";
 }

sub opContents($)                                                               # @{} : nodes immediately below a node.
 {my ($node) = @_;                                                              # Node.
  $node->content
 }

sub opAt($$)                                                                    # <= : Check that a node is in the context specified by the referenced array of words.
 {my ($node, $context) = @_;                                                    # Node, reference to array of words specifying the parents of the desired node.
  ref($context) =~ m/array/is or
    confess "Array of words required to specify the context";
  $node->at(@$context);
 }

sub opNew($$)                                                                   # ** : create a new node from the text on the right hand side: if the text contains a non word character \W the node will be create as text, else it will be created as a tag
 {my ($node, $text) = @_;                                                       # Node, name node of node to create or text of new text element
  return $text                if ref($text) eq __PACKAGE__;                     # The right hand side is already a node
  return $node->newTag($text) unless $text =~ m/\W/s;                           # Create a new node as tag
  $node->newText($text)                                                         # Create a new node as text if nothing lse worked
 }

sub opPutFirst($$)                                                              # >> : put a node or string first under a node and return the new node.
 {my ($node, $text) = @_;                                                       # Node, node or text to place first under the node.
  $node->putFirst(my $new = opNew($node, $text));
  $new
 }

sub opPutFirstAssign($$)                                                        # >>= : put a node or string first under a node.
 {my ($node, $text) = @_;                                                       # Node, node or text to place first under the node.
  opPutFirst($node, $text);
  $node
 }

sub opPutLast($$)                                                               # << : put a node or string last under a node and return the new node.
 {my ($node, $text) = @_;                                                       # Node, node or text to place last under the node.
  $node->putLast(my $new = opNew($node, $text));
  $new
 }

sub opPutLastAssign($$)                                                         # <<= : put a node or string last under a node.
 {my ($node, $text) = @_;                                                       # Node, node or text to place last under the node.
  opPutLast($node, $text);
  $node
 }

sub opPutNext($$)                                                               # > + : put a node or string after the specified B<$node> and return the new node.
 {my ($node, $text) = @_;                                                       # Node, node or text to place after the first node.
  $node->putNext(my $new = opNew($node, $text));
  $new
 }

sub opPutNextAssign($$)                                                         # += : put a node or string after the specified B<$node>.
 {my ($node, $text) = @_;                                                       # Node, node or text to place after the first node.
  opPutNext($node, $text);
  $node
 }

sub opPutPrev($$)                                                               # < - : put a node or string before the specified B<$node> and return the new node.
 {my ($node, $text) = @_;                                                       # Node, node or text to place before the first node.
  $node->putPrev(my $new = opNew($node, $text));
  $new
 }

sub opPutPrevAssign($$)                                                         # -= : put a node or string before the specified B<$node>,
 {my ($node, $text) = @_;                                                       # Node, node or text to place before the first node.
  opPutPrev($node, $text);
  $node
 }

sub opBy($$)                                                                    # x= : Traverse a L<parse|/parse> tree in post-order.
 {my ($node, $code) = @_;                                                       # Parse tree, code to execute against each node.
  ref($code) =~ m/code/is or
    confess "sub reference required on right hand side";
  $node->by($code);
 }

sub opGo($$)                                                                    # >= : Search for a node via a specification provided as a reference to an array of words each number.  Each word represents a tag name, each number the index of the previous tag or zero by default.
 {my ($node, $go) = @_;                                                         # Node, reference to an array of search parameters.
  return $node->go(@$go) if ref($go);
  $node->go($go)
 }

sub opAttr($$)                                                                  # % : Get the value of an attribute of the specified B<$node>.
 {my ($node, $attr) = @_;                                                       # Node, reference to an array of words and numbers specifying the node to search for.
  return map {$node->attr($_)} @$attr if ref($attr);
  $node->attr($attr)
 }

sub opWrapWith($$)                                                              # / : Wrap node with a tag, returning the wrapping node.
 {my ($node, $tag) = @_;                                                        # Node, tag.
  return $node->wrapUp(@$tag) if ref($tag);
  $node->wrapWith($tag)
 }

sub opWrapContentWith($$)                                                       # * : Wrap content with a tag, returning the wrapping node.
 {my ($node, $tag) = @_;                                                        # Node, tag.
  return $node->wrapDown(@$tag) if ref($tag);
  $node->wrapContentWith($tag)
 }

sub opCut($)                                                                    # -- : Cut out a node.
 {my ($node) = @_;                                                              # Node.
  $node->cut
 }

sub opUnwrap($)                                                                 # ++ : Unwrap a node.
 {my ($node) = @_;                                                              # Node.
  $node->unwrap
 }

#D1 Reuse                                                                       # Identify common components that can be reused

sub subMd5Tree($)                                                               # Return the L<md5> of the L<stringContent> of the parse tree.
 {my ($node) = @_;                                                              # Node
  if (my $s = $node->stringContent)                                             # Md5 of something with content
   {return (stringMd5Sum($s), $s);
   }
  ()                                                                            # A text element must be wrapped in an non text element - to get the md5 sum of a text element call via the wrapping node - otherwise we get duplicates.
 }

sub subMd5($%)                                                                  # Return a hash {L<md5> of parse tree}{string representation of sub tree}++ to locate common parse trees under B<$node> that could be included via a conref.  L<Md5>s are only calculated for nodes whose tags match the keys of the B<$tags> hash that have truthful values. The attributes of the root node of each sub tree are ignored in the computation as they can be supplied by the conreffing element.
 {my ($node, %tags) = @_;                                                       # Node, hash of acceptable tags.

  my %hash;                                                                     # Hash of md5 sums to content

  my $M; $M = sub($)                                                            # Md5 sum of current parse tree
   {my ($p) = @_;                                                               # Node
    my $tag = -t $p;
    if ($tags{$tag})                                                            # Tag matches
     {if (my ($m, $s) = $p->subMd5Tree)                                         # Md5 of something with content
       {$hash{$tag}{$m}{$s}++;
       }
     }

    if (my @c = $p->contents)                                                   # Each sub tree
     {$M->($_) for @c;
     }
   };

  $M->($node);                                                                  # Start at current node
  return \%hash;                                                                # Resulting hash
 }

#D1 Statistics                                                                  # Statistics describing the L<parse|/parse> tree.

sub count($@)                                                                   #U Return the count of the number of instances of the specified tags under the specified B<$node>, either by tag in array context or in total in scalar context.
 {my ($node, @names) = @_;                                                      # Node, possible tags immediately under the node.
  if (wantarray)                                                                # In array context return the count for each tag specified
   {my @c;                                                                      # Count for the corresponding tag
    reindexNode($node);                                                         # Create index for this node
    my %i = %{$node->indexes};                                                  # Index of child nodes
    for(@names)
     {if (my $i = $i{$_}) {push @c, scalar(@$i)} else {push @c, 0};             # Save corresponding count
     }
    return @c;                                                                  # Return count for each tag specified
   }
  else                                                                          # In scalar context count the total number of instances of the named tags
   {if (@names)
     {my $c = 0;                                                                # Tag count
      reindexNode($node);                                                       # Create index for this node
      my %i = %{$node->indexes};                                                # Index of child nodes
      for(@names)
       {if (my $i = $i{$_}) {$c += scalar(@$i)}
       }
      return $c;
     }
    else                                                                        # In scalar context, with no tags specified, return the number of nodes under the specified B<$node>
     {my @c = $node->contents;
      return scalar(@c);                                                        # Count of all tags including CDATA
     }
   }
  confess "This should not happen"
 }

sub countTags($)                                                                #U Count the number of tags in a L<parse|/parse> tree.
 {my ($node) = @_;                                                              # Parse tree.
  my $n = 0;
  $node->by(sub{++$n});                                                         # Count tags including CDATA
  $n                                                                            # Number of tags encountered
 }

sub countTagNames2($$)                                                          #P Return a reference to a hash showing the number of instances of each tag on and below the specified B<$node>.
 {my ($node, $count) = @_;                                                      # Node, count of tags so far.
  $$count{$node->tag}++;                                                        # Add current tag
  $_->countTagNames2($count) for $node->contents;                               # Each contained node
 }

sub countTagNames($@)                                                           #U Return a reference to a hash showing the number of instances of each tag on and below the specified B<$node> excluding any tags named in B<@exclude>.
 {my ($node, @exclude) = @_;                                                    # Node, tags to exclude from the count
  my $count = {};                                                               # Counts
  $node->countTagNames2($count);                                                # Count includes this node
  delete $$count{$_} for @exclude;                                              # Delete excluded nodes
  $count                                                                        # Count
 }

sub countNonEmptyTags2($$)                                                      #P Count the instances of non empty tags on and below the specified B<$node>.
 {my ($node, $count) = @_;                                                      # Node, count of tags so far.
  $$count{$node->tag}++ unless $node->isAllBlankText;                           # Add current tag
  $_->countNonEmptyTags2($count) for $node->contents;                           # Each contained node
 }

sub countNonEmptyTags($@)                                                       #U Return a reference to a hash showing the number of instances of each non empty tag on and below the specified B<$node> excluding any tags named in B<@exclude>.
 {my ($node, @exclude) = @_;                                                    # Node, tags to exclude from the count
  my $count = {};                                                               # Counts
  $node->countNonEmptyTags2($count);                                            # Count includes this node
  delete $$count{$_} for @exclude;                                              # Delete excluded nodes
  $count                                                                        # Count
 }

sub countTexts2($$)                                                             #P Count the instances of non empty texts on and below the specified B<$node>.
 {my ($node, $count) = @_;                                                      # Node, texts so far
  if ($node->isText)                                                            # Check we are on a text node
   {$$count{$node->text}++;                                                     # Add current text
   }
  else                                                                          # Not on a text node
   {$_->countTexts2($count) for $node->contents;                                # Each contained node
   }
 }

sub countTexts($)                                                               #U Return a reference to a hash showing the incidence of texts on and below the specified B<$node>.
 {my ($node) = @_;                                                              # Node
  my $count = {};                                                               # Counts
  $node->countTexts2($count);                                                   # Count includes this node
  $count                                                                        # Count
 }

sub countWords($)                                                               #UP Count instances of words in texts
 {my ($node) = @_;                                                              # Node
  my $c = countTexts($node);                                                    # Gather text
  my %w;                                                                        # Words
  for my $t(keys %$c)                                                           # Each text
   {$w{$_}++ for split /[.,;]?\s+/, $t;                                         # Count words in each text
   }
  \%w                                                                           # Return words
 }

sub countAttrNames($;$)                                                         #U Return a reference to a hash showing the number of instances of each attribute on and below the specified B<$node>.
 {my ($node, $count) = @_;                                                      # Node, attribute count so far
  $count //= {};                                                                # Counts
  $$count{$_}++ for $node->getAttrs;                                            # Attributes from current tag
  $_->countAttrNames($count) for $node->contents;                               # Each contained node
  $count                                                                        # Count
 }

sub countAttrNamesOnTagExcluding($@)                                            #U Count the number of attributes owned by the specified B<$node> that are not in the specified list.
 {my ($node, @attr) = @_;                                                       # Node, attributes to ignore
  my %attr = map{$_=>1} @attr;                                                  # Set of attributes to ignore
  my $count;                                                                    # Count
  $count++ for grep {!$attr{$_}} $node->getAttrs;                               # Attributes from current tag
  $count                                                                        # Count
 }

sub countAttrValues($;$)                                                        #U Return a reference to a hash showing the number of instances of each attribute value on and below the specified B<$node>.
 {my ($node, $count) = @_;                                                      # Node, count of attributes so far.
  $count //= {};                                                                # Counts
  $count->{$node->attr($_)}++ for $node->getAttrs;                              # Attribute values from current tag
  $_->countAttrValues($count) for $node->contents;                              # Each contained node
  $count                                                                        # Count
 }

sub countAttrNamesAndValues($;$)                                                #U Return a reference to a hash showing the number of instances of each attribute name and value on and below the specified B<$node>.
 {my ($node, $count) = @_;                                                      # Node, count of attributes so far.
  $count //= {};                                                                # Counts
  $count->{$_}{$node->attr($_)}++ for $node->getAttrs;                          # Attribute names and values from current tag
  $_->countAttrNamesAndValues($count) for $node->contents;                      # Each contained node
  $count                                                                        # Count
 }

sub countOutputClasses($$)                                                      #U Count instances of outputclass attributes
 {my ($node, $count) = @_;                                                      # Node, count so far.
  $count //= {};                                                                # Counts
  my $a = $node->attr(qw(outputclass));                                         # Outputclass attribute
  $$count{$a}++ if $a ;                                                         # Add current output class
  &countOutputClasses($_, $count) for $node->contents;                          # Each contained node
  $count                                                                        # Count
 }

sub countReport($@)                                                             #U Count tags, attributes, words below the specified node
 {my ($node, @context) = @_;                                                    # Node to count from, optional context
  return undef if @context and !$node->at(@context);                            # Optionally check context
  my $c = $node->countTagNames;                                                 # Count tag types
  formatTable([sort {$$a[1] cmp $$b[1]} map {[$$c{$_}, $_]} sort keys %$c],
              [qw(Tag Count)]);
 }

BEGIN{*cr=*countReport}

sub changeReasonCommentSelectionSpecification :lvalue                           #S Provide a specification to select L<change reason comments|/crc> to be inserted as text into a L<parse|/parse> tree. A specification can be either:\m=over\m=item the name of a code to be accepted,\m=item a regular expression which matches the codes to be accepted,\m=item a hash whose keys are defined for the codes to be accepted or\m=item B<undef> (the default) to specify that no such comments should be accepted.\m=back
 {CORE::state $r;
  $r
 }

sub crc($$;$)                                                                   # Insert a comment consisting of a code and an optional reason as text into the L<parse|/parse> tree to indicate the location of changes to the L<parse|/parse> tree.  As such comments tend to become very numerous, only comments whose codes matches the specification provided in L<changeReasonCommentSelectionSpecification|/changeReasonCommentSelectionSpecification> are accepted for insertion. Subsequently these comments can be easily located using:\mB<grep -nr "<!-->I<code>B<">\mon the file containing a printed version of the L<parse|/parse> tree. Please note that these comments will be removed if the output file is reparsed.\mReturns the specified B<$node>.
 {my ($node, $code, $reason) = @_;                                              # Node being changed, reason code, optional text description of change
  if (sub                                                                       # Whether to make a change entry in the L<parse|/parse> tree
   {my $s = changeReasonCommentSelectionSpecification;                          # Change selection specification
    return undef unless $s;                                                     # Do not record change reasons unless a change selection has been supplied
    my $r = ref $s;                                                             # Change selection has been supplied
    return 1 if $r and $r =~ m(Regexp) and $code =~ m($s);                      # Requested change matches the supplied regular expression
    return 1 if $r and $r =~ m(HASH)   and $s->{$code};                         # Requested change is a key in the supplied hash
    return 1 if $s and $s eq $code;                                             # Requested change equal to the supplied name
    undef                                                                       # No match so do not crate a change entry in the L<parse|/parse> tree
   }->())
   {my $message = $reason ? "<!--$code - $reason -->" : "<!--$code-->";         # Insert message either eliding it with existing text or creating a new text node
    if ($node->isText)                                                          # If we are on a text node we can simply add the comment at the front
     {$node->text = $message.$node->text;
     }
    else
     {my $P = $node->prev;                                                      # At the end of a previous text node?
      if ($P and $P->isText)
       {$P->text = $node->text.$message;
       }
      else
       {my $N = $node->next;                                                    # At the start of a following text node?
        if ($N and $N->isText)
         {$N->text = $message.$node->text;
         }
        elsif ($node->parent)                                                   # Not a text node, no text node on either side, not the root
         {$node->putPrevAsText($message);
         }
        elsif (my $f = $node->first)                                            # Root node but not text
         {if ($f->isText)                                                       # At front of first text node
           {$f->text = $message.$node->text;
           }
          else                                                                  # No first node or first node is not text, place first
           {$node->putFirstAsText($message);
           }
         }
       }
     }
   }
  $node
 }

sub howFirst($)                                                                 #U Return the depth to which the specified B<$node> is L<first|/isFirst> else B<0>.
 {my ($node) = @_;                                                              # Node
  my $i = 0;                                                                    # Count first depth
  for(my $p = $node; $p; $p = $p->parent)                                       # Go up
   {last unless $p->isFirst;                                                    # Go up while first
    ++$i;
   }
  $i
 }

sub howLast($)                                                                  #U Return the depth to which the specified B<$node> is L<last|/isLast> else B<0>.
 {my ($node) = @_;                                                              # Node
  my $i = 0;                                                                    # Count last depth
  for(my $p = $node; $p; $p = $p->parent)                                       # Go up
   {last unless $p->isLast;                                                     # Go up while last
    ++$i;
   }
  $i
 }

sub howOnlyChild($)                                                             #U Return the depth to which the specified B<$node> is an L<only child|/isOnlyChild> else B<0>.
 {my ($node) = @_;                                                              # Node
  my $i = 0;                                                                    # Count only child depth
  for(my $p = $node; $p; $p = $p->parent)                                       # Go up
   {last unless $p->isOnlyChild;                                                # Go up while only child
    ++$i;
   }
  $i
 }

sub howFar($$)                                                                  #U Return how far the first node is from the second node along a path through their common ancestor.
 {my ($first, $second) = @_;                                                    # First node, second node
  my $p = $first->commonAncestor($second);                                      # Find their common ancestor
  return 0 if $first == $second;                                                # Same node
  return 1 if $first->adjacent($second);                                        # Adjacent nodes
  $p->howFarAbove($first) + $p->howFarAbove($second) -                          # Sum of the paths to their common ancestor plus any adjustment
    ($first->commonAdjacentAncestors($second) ? 1 : 0);                         # If the nodes share a pair of common ancestral siblings the path is one step shorter
 }

sub howFarAbove($$)                                                             #U Return how far the first node is  L<above|/above> the second node is or B<0> if the first node is not strictly L<above|/above> the second node.
 {my ($above, $below) = @_;                                                     # First node above, second node below
  for(my ($i, $p) = (1, $below->parent); $p; $p = $p->parent, ++$i)             # Go up from below to above
   {return $i if $above == $p;                                                  # Return the height if we have reached the node above
   }
  0
 }

sub howFarBelow($$)                                                             #U Return how far the first node is  L<below|/below> the second node is or B<0> if the first node is not strictly L<below|/below> the second node.
 {my ($below, $above) = @_;                                                     # First node below, second node above
  $above->howFarAbove($below)                                                   # Use howFarABove with arguments inverted
 }

#D1 Required clean up                                                           # Insert required clean up tags.

sub createRequiredCleanUp($@)                                                   #P Create a required clean up $node with the specified B<@text>
 {my ($node, @text) = @_;                                                       # Node, clean up messages
  my $text = join ' ', @text;                                                   # Combined text
  my $r = $node->newTag(q(required-cleanup));                                   # Create required clean up node
  $r->putFirstAsText($text);                                                    # Add text
  $r                                                                            # Return required clean up node
 }

sub requiredCleanUp($;$)                                                        #U Replace a B<$node> with a required cleanup node with special characters replaced by symbols and with the optional B<$outputclass>.\mReturns the specified B<$node>.
 {my ($node, $outputclass) = @_;                                                # Node, optional outputclass attribute of required cleanup tag
  my $text = replaceSpecialChars($node->prettyString);                          # Replace xml chars with symbols
  my $r = $node->createRequiredCleanUp($text);                                  # Create required clean up node
     $r->outputclass = $outputclass if $outputclass;                            # Add outputclass if supplied
  $node->replaceWith($r);                                                       # Replace current node
  $r                                                                            # Return required clean up node
 }

sub replaceWithRequiredCleanUp($@)                                              #U Replace a B<$node> with required cleanup B<@text> and return the new node
 {my ($node, @text) = @_;                                                       # Node to be replace, clean up message
  my $text = join ' ', @text;                                                   # Combined text
  my $r = $node->createRequiredCleanUp($text);                                  # Create required clean up node
  $node->replaceWith($r);
  $r
 }

sub putFirstRequiredCleanUp($@)                                                 #U Place a required cleanup first under a specified B<$node> using the specified B<@text>  and return the required clean up node.
 {my ($node, @text) = @_;                                                       # Node, clean up message
  my $text = join ' ', @text;                                                   # Combined text
  my $r = $node->createRequiredCleanUp($text);                                  # Create required clean up node
  $node->putFirst($r);                                                          # Insert required clean up node
  $r                                                                            # Return required clean up node
 }

sub putLastRequiredCleanUp($@)                                                  #U Place a required cleanup last under a specified B<$node> using the specified B<@text>  and return the required clean up node.
 {my ($node, @text) = @_;                                                       # Node, clean up message
  my $text = join ' ', @text;                                                   # Combined text
  my $r = $node->createRequiredCleanUp($text);                                  # Create required clean up node
  $node->putLast($r);                                                           # Insert required clean up node
  $r                                                                            # Return required clean up node
 }

sub putNextRequiredCleanUp($@)                                                  #IU Place a required cleanup next after a specified B<$node> using the specified B<@text>  and return the required clean up node.
 {my ($node, @text) = @_;                                                       # Node, clean up message
  my $text = join ' ', @text;                                                   # Combined text
  if ($node->parent)                                                            # Place after non root node
   {my $r = $node->createRequiredCleanUp($text);                                # Create required clean up node
    $node->putNext($r);                                                         # Insert required clean up node
    return $r;                                                                  # Return required clean up node
   }
  else                                                                          # Place last under a root node
   {return $node->putLastRequiredCleanUp($text);
   }
 }
#a putFirstRequiredCleanUp
#b <b><c/></b>
#c at c b
#c putNextRequiredCleanUp remove tag c
#d Create a required cleanup tag with the specified text and put it next.

sub putPrevRequiredCleanUp($$)                                                  #U Place a required cleanup before a specified B<$node> using the specified B<@text>  and return the required clean up node.
 {my ($node, $text) = @_;                                                       # Node, clean up message
  if ($node->parent)                                                            # Place before non root node
   {my $r = $node->createRequiredCleanUp($text);                                # Create required clean up node
    $node->putPrev($r);                                                         # Insert required clean up node
    return $r;                                                                  # Return required clean up node
   }
  else                                                                          # Place first under a root node
   {return $node->putFirstRequiredCleanUp($text);
   }
 }

#D1 Conversions                                                                 # Methods useful for conversions from word, L<html> and L<Dita> to L<Dita>.

#D2 Dita Conversions                                                            # Methods useful for enhancing L<Dita>.

sub ditaGetConRef($$;$$)                                                        #S Get the value of a B<conref> attribute given a valid L<Dita> B<$ref>erence found in the file named B<$sourceFile>. Optionally, if the regular expression B<$matchContext> is supplied then nodes whose context (single blank separated) tags match this regular expression will be considered first as a target of the conref, if such a search fails then any unique node with the matching id will be the considered next as a possible target unless the optional B<$matchContextOnly> parameter is true. Note: all the conrefs in the included content will also be expanded creating the possibility of endless circular expansions.
 {my ($ref, $sourceFile, $matchContext, $matchContextOnly) = @_;                # Dita ref value of conref, source file containing conref, optional regular expression of acceptable context, confine search to acceptable context.
  my ($rf, $rt, $ri) = parseDitaRef($ref, $sourceFile);                         # Parse the reference
# -e $rf or confess "No such file:\n$rf\n";                                     # Confess if the target file does not exist
  return undef unless $rf and -e $rf;                                           # Insist that the file be present

  my $x = eval {new($rf)};                                                      # Parse target file
  return undef unless $x;                                                       # Any errors will show up in the Lint reports

  if ($ri)                                                                      # Id present - we want part of the target
   {if ($matchContext)                                                          # Matching requested
     {my $tree;                                                                 # Parse tree representing conreffed content
      $x->by(sub                                                                # Scan parse tree for id
       {my ($o) = @_;                                                           # Each node
        if ($o->idX eq $ri and $o->context =~ m($matchContext))                 # Node with matching id and matching tag if tag matching requested
         {my $r = $o->ditaExpandAllConRefs($rf);                                # Expand any conrefs referenced by the conref
          $tree = $r && $o != $r ? $r->cut : $o->cut;                           # Parse tree representing conref
         }
       });

      return $tree if $tree;                                                    # Take last matching id if any
     }

    return undef if $matchContextOnly;                                          # Broader search not allowed

    my $tree;                                                                   # Broaden the search if the target has not been found so far and a broader search is permissible.
    $x->by(sub                                                                  # Scan parse tree for id
     {my ($o) = @_;                                                             # Each node
      if ($o->idX eq $ri)                                                       # Node with matching id and matching tag if tag matching requested
       {my $r = $o->ditaExpandAllConRefs($rf);                                  # Expand any conrefs referenced by the conref
        $tree = $r && $o != $r ? $r->cut : $o->cut;                             # Parse tree representing conref
       }
     });

    return $tree;                                                               # Conreffed content or fail
   }
  else                                                                          # No id specified so include the entire topic
   {$x->by(sub                                                                  # Scan the new content for conrefs to expand - let us hope that there is an end to their conref chain else we will hit the call depth limit
     {my ($o) = @_;                                                             # Each node
      $o->ditaExpandAllConRefs($rf);                                            # Expand any conrefs in the content referenced by the conref
     });

    $x
   }                                                                            # Id missing, we want the entire target
# cluck "No such id in file: $ref\n$rf\n" unless @c;                            # No such id
# cluck "Too many possibilities for in file: $ref\n$rf\n".dump(\@c)."\n" if @c; # Too many possibilities
 }

sub ditaReplaceConref($$)                                                       # Replace a conref on the specified B<$node> in the specified source file B<$sourceFile> with the parse tree of the referenced content. Returns the referenced content.  Confesses if there is no conref to replace on $node.
 {my ($node, $sourceFile) = @_;                                                 # Node with conref attribute, source file containing the node.
  my $c = $node->attr(q(conref));                                               # Conref attribute
  $c or confess "No conref attribute: ".-A $node;                               # Check there is a conref
  if (my $v = ditaGetConRef($c, $sourceFile))                                   # Parse tree
   {if (my $id = $node->id)                                                     # The node has an id which should be retained
     {my $r = $node->replaceWith($v);
      $r->id = $id;
      return $r;
     }
    else                                                                        # The node does not have an id
     {return $node->replaceWith($v);
     }
   }
  undef
 }

sub ditaReplaceAnyConref($@)                                                    #U Replace a conref with the referenced content if we are in the optional context and the target of the conref can be located. Returns true regardless of any processing performed to allow L<PCD> processing to continue.
 {my ($node, @context) = @_;                                                    # Node with conref attribute, optional context.
  return undef if @context and !$node->at(@context);                            # Not in specified context
  return $node unless my $c = $node->attr(q(conref));                           # Continue if there is no conref attribute
  if (my $inputFile = $node->root->inputFile)                                   # Input file
   {if (my $v = ditaGetConRef($c, $inputFile))                                  # Locate conref
     {if (my $id = $node->id)                                                   # The node has an id which should be retained
       {my $r = $node->replaceWith($v);                                         # Replace current node with referenced node and return new node
        $r->id = $id;
        return $r;
       }
      else                                                                      # No id to retain on node
       {return $node->replaceWith($v);                                          # Replace current node with referenced node and return new node
       }
     }
   }
  undef                                                                         # Unable to replace conref
 }

sub ditaReplaceAnyConrefIdeallyWithMatchingTag($@)                              #U Replace a conref with the referenced content if we are in the optional context and the target of the conref can be located. If multiple possible targets exists, priority will be given to any that match the tag of $node. Returns the root of the parse tree containing the new content or B<undef> if no new content was located.
 {my ($node, @context) = @_;                                                    # Node with conref attribute, optional context.
  return undef if @context and !$node->at(@context);                            # Not in specified context
  return $node unless my $c = $node->attr(q(conref));                           # Continue if there is no conref attribute
  if (my $inputFile = $node->root->inputFile)                                   # Input file
   {my $tag = quotemeta $node->tag;                                             # Tag of conreffing node
    if (my $v = ditaGetConRef($c, $inputFile, qr(\A$tag(\s|\Z))))               # Locate conref with, ideally, the target node having the same tag as the source node
     {if (my $id = $node->id)                                                   # The node has an id which should be retained
       {my $r = $node->replaceWith($v);                                         # Replace current node with referenced node and return new node
        $r->id = $id;
        return $r;
       }
      else                                                                      # No id to retain on node
       {return $node->replaceWith($v);                                          # Replace current node with referenced node and return new node
       }
     }
   }
  undef                                                                         # Unable to replace conref
 }

sub ditaReplaceAnyConrefInContext($$@)                                          #U Replace a conref with the referenced content if we are in the optional context and the target of the conref can be located and the L<context> of the target node matches the regular expression B<$context>. Returns the root of the parse tree containing the new content or B<undef> if no new content was located.
 {my ($node, $context, @context) = @_;                                          # Node with conref attribute, tags to match, optional context.
  return undef if @context and !$node->at(@context);                            # Not in specified context
  return $node unless my $c = $node->attr(q(conref));                           # Continue if there is no conref attribute
  if (my $inputFile = $node->root->inputFile)                                   # Input file
   {if (my $v = ditaGetConRef($c, $inputFile, $context, 1))                     # Locate conref requiring that the target node match the specified regular expression
     {if (my $id = $node->id)                                                   # The node has an id which should be retained
       {my $r = $node->replaceWith($v);                                         # Replace current node with referenced node and return new node
        $r->id = $id;
        return $r;
       }
      else                                                                      # No id to retain on node
       {return $node->replaceWith($v);                                          # Replace current node with referenced node and return new node
       }
     }
   }
  undef                                                                         # Unable to replace conref
 }

sub ditaExpandAllConRefs($$)                                                    #U Expand all the conrefs in the specified B<$parseTree> relative to the specified B<$sourceFile>.
 {my ($tree, $sourceFile) = @_;                                                 # Parse tree, Source file we are expanding in

  $tree->by(sub                                                                 # Search parse tree for conrefs to expand
   {my ($o) = @_;
    if ($o->attr_conref)                                                        # Located a conref
     {my $id = $o->id;
      my $r  = $o->ditaReplaceConref($sourceFile);                              # Expand conref
      $r->id = $id if $r && defined $id;
      if ($o == $tree)                                                          # Expanded
       {$tree = $r;
       }
     }
   });

  $tree
 }

sub ditaAbsoluteHref($$)                                                        # Return the absolute value of the href of a specified B<$node> relative to the specified B<$sourceFile> or B<undef> if the node has no B<href> attribute.
 {my ($node, $sourceFile) = @_;                                                 # Node containing href, source file
  if (my $h = $node->href)                                                      # Node has href
   {return absFromAbsPlusRel($sourceFile, $h);
   }
  undef                                                                         # No href
 }

sub ditaListToChoices($@)                                                       #UC Change the specified B<$list> to B<choices>.
 {my ($list, @context) = @_;                                                    # Node, optional context
  $list->tag =~ m(\A(ol|sl|ul)\Z)s or confess "Not a list";                     # Not a list
  return undef if @context and !$list->at(@context);                            # Not in specified context
  for my $li($list->c_li)                                                       # All li
   {$li->change(q(choice));                                                     # Change li to choice
   }
  $list->change(q(choices)) ;                                                   # Change tag of node to choices
 }

sub ditaListToSteps($@)                                                         #UC Change the specified B<$node> to B<steps> and its contents to B<cmd\step> optionally only in the specified context.
 {my ($list, @context) = @_;                                                    # Node, optional context
  return undef if @context and !$list->at(@context);                            # Not in specified context
  for(@$list)                                                                   # Each li
   {$_->change(qw(  cmd))->wrapWith(q(step));                                   # li -> cmd\step
    $_->unwrap(qw(p cmd)) for @$_;                                              # Unwrap any contained p
   }
  $list->change(q(steps));
 }

sub ditaListToStepsUnordered($@)                                                #UC Change the specified B<$node> to B<steps-unordered> and its contents to B<cmd\step> optionally only in the specified context.
 {my ($list, @context) = @_;                                                    # Node, optional context
  my $steps = $list->ditaListToSteps(@context);                                 # Change to steps
  $steps->change(q(steps-unordered)) if $steps;                                 # Change to steps unordered
  $steps
 }

sub ditaListToSubSteps($@)                                                      #UC Change the specified B<$node> to B<substeps> and its contents to B<cmd\step> optionally only in the specified context.
 {my ($list, @context) = @_;                                                    # Node, optional context
  return undef if @context and !$list->at(@context);                            # Not in specified context
  for(@$list)                                                                   # Each li
   {$_->change(qw(  cmd))->wrapWith(q(substep));                                # li -> cmd\step
    $_->unwrap(qw(p cmd)) for @$_;                                              # Unwrap any contained p
   }
  $list->change(q(substeps));
 }

sub ditaStepsToList($$@)                                                        #UC Change the specified B<$node> to a node with name B<$tag> or to B<ol> if B<$tag> is not supplied and its B<cmd\step> content to B<li> to create a list optionally only in the specified context.
 {my ($steps, $tag, @context) = @_;                                             # Node, new tag if not ol, optional context
  return undef if @context and !$steps->at(@context);                           # Not in specified context

  for(@$steps)                                                                  # Content
   {$_->change(q(li));                                                          # Change content to li
    $_->unwrap for $_->c_cmd;                                                   # Unwrap cmd
   }
  $steps->change($tag//q(ol));                                                  # Change tag of node to ol unless an alternative has been supplied
 }

sub ditaStepsToChoices($@)                                                      #UC Change the specified B<$node> to B<choices>.
 {my ($steps, @context) = @_;                                                   # Node, optional context
  return undef if @context and !$steps->at(@context);                           # Not in specified context
  for my $step(@$steps)                                                         # Steps
   {$step->change(q(choice));                                                   # Change step to choice
    for my $o(@$step)
     {my $t = $o->tag;
      if ($t =~ m(\Acmd\Z)s)
       {$o->change(q(p));
       }
      elsif ($t =~ m(\A(stepresult|stepxmp)\Z)s)
       {$o->unwrap;
       }
     }
   }
  $steps->change(q(choices)) ;                                                  # Change tag of node to choices
 }

sub ditaConvertSubStepsToSteps($@)                                              #UC Change the B<$substeps> to B<steps> and return the B<steps> on success or B<undef> on failure.
 {my ($substeps, @context) = @_;                                                # Substeps, optional context
  $substeps->at_substeps or confess "Not on substeps";                          # Not on substeps
  return undef if @context and !$substeps->at(@context);                        # Not in specified context
  for my $s(@$substeps)                                                         # Substeps
   {$s->change_step;                                                            # Change substep to step
   }
  $substeps->change_steps;                                                      # Change to steps
 }

sub ditaListToTable($@)                                                         #UC Convert a list to a table in situ - as designed by MiM.
 {my ($node, @context) = @_;                                                    # List node, optional context
  return undef if @context and !$node->at(@context);                            # Not in specified context

  my $cols = 0;                                                                 # Number of columns
  for my $l($node->c_li)
   {my $n = $l->c_p;
    $cols = max($cols, $n);
   }

  my $table       = $node->change_table;                                        # Table structure
  my $tgroup      = $table->wrapContentWith_tgroup->set(cols=>$cols);
  my $tbody       = $tgroup->wrapContentWith_tbody;
  my $headEntries = q(<entry/>) x $cols;
     $tgroup->putFirst(Data::Edit::Xml::new(<<END));
<thead>
  <row>
  $headEntries
  </row>
</thead>
END

  for my $i(reverse 1..$cols)                                                   # Add colspec
   {$tgroup->putFirst($node->newTag
     (qw(colspec colwidth 1*), colname=>q(c).$i, colnum=>$i));
   }

  for my $l($tbody->c_li)                                                       # Convert li to row and the p to entry
   {$l->change_row;
    for my $p($l->c_p)
     {$p->change_entry
     }
   }

  $node                                                                         # Table thus constructed
 }

sub ditaMergeLists($@)                                                          #UC Merge the specified B<$node> with the preceding or following list or steps or substeps if possible and return the specified B<$node> regardless.
 {my ($node, @context) = @_;                                                    # Node, optional context
  return undef if @context and !$node->at(@context);                            # Not in specified context
  $node->ditaMergeListsOnce for 1..2                                            # Do the merge twice to pick up all the stragglers
 }

sub ditaMergeListsOnce($)                                                       #UP Merge the specified B<$node> with the preceding or following list or steps or substeps if possible and return the specified B<$node> regardless.
 {my ($node) = @_;                                                              # Node
  if ($node->at(qr(\A(ol|ul|steps|substeps)\Z)))                                # Lists
   {if (my $p = $node->prev(-t $node))                                          # Merge two lists or steps or substeps
     {$p->putLast($node->cut);
      $node->unwrap;
     }
    elsif ($node->at(qr(\A(ol|sl|ul))))                                         # List with preceding or following list elements
     {if    (my $p = $node->prev(q(li)))
       {$node->putFirst($p->cut);
       }
      elsif (my $n = $node->next(q(li)))
       {$node->putLast($n->cut);
       }
     }
    elsif ($node->at(q(steps)))                                                 # Steps with preceding or following step
     {if    (my $p = $node->prev(qr(step|stepsection)))
       {$node->putFirst($p->cut);
       }
      elsif (my $n = $node->next(q(step)))
       {$node->putLast($n->cut);
       }
     }
    elsif ($node->at(q(substeps)))                                              # Substeps with preceding or following steps
     {if    (my $p = $node->prev(q(substep)))
       {$node->putFirst($p->cut);
       }
      elsif (my $n = $node->next(q(substep)))
       {$node->putLast($n->cut);
       }
     }
    elsif ($node->at(q(step))    and !$node->up(q(steps)))                      # Step not in steps
     {$node->wrapWith(q(steps));
     }
    elsif ($node->at(q(substep)) and !$node->up(q(substeps)))                   # Substep not in substeps
     {$node->wrapWith(q(steps));
     }
    elsif ($node->at(q(li))      and !$node->up(qr(\A(ol|sl|ul)\Z)))            # Li not in list
     {$node->wrapWith(q(ol));
     }
    elsif ($node->at(q(cmd))     and  $node->up(q(steps)))                      # Cmd under steps
     {$node->wrapWith(q(step));
     }
    elsif ($node->at(q(cmd))     and  $node->up(q(substeps)))                   # Cmd under substeps
     {$node->wrapWith(q(substep));
     }
    elsif ($node->not(q(li))     and  $node->up(qr(\A(ol|sl|ul)\Z)))            # Something under a list which is not an li
     {$node->wrapWith(q(li));
     }
    elsif ($node->not(qw(step stepsection)) and  $node->up(q(steps)))           # Something under steps which is not a step or stepsection
     {if (my $p = $node->prev(q(stepsection)))
       {$p->putLast($node->cut);
       }
     else
       {$node->wrapWith(q(stepsection));
       }
     }
   }
  elsif ($node->at(q(li)))                                                      # Free floating list element
   {if (my $p = $node->parent)
     {if ($p->not(qw(ol sl ul)))
       {sub
         {if (my $prev = $node->prev)
           {if ($prev->at(qr(\A(ol|sl|ul)\Z)))
             {$prev->putLastCut($node);
              return;
             }
           }

          if (my $next = $node->next)
           {if ($next->at(qr(\A(ol|sl|ul)\Z)))
             {$next->putFirstCut($node);
              return;
             }
           }
          $node->wrapWith(q(ol));
         }->();
       }
     }
   }

  $node                                                                         # Return the specified B<$node>
 }

sub mergeLikeElements($@)                                                       #UC Merge two of the same elements into one, retaining the order of any children. Return the original B<$node> if the request succeeds, else return B<undef>.
 {my ($node, @context) = @_;                                                    # Node, optional context
  if (my $a = $node->mergeLikePrev(@context))
   {return $a;
   }
  if (my $b = $node->mergeLikeNext(@context))
   {return $b;
   }
  undef;
 }

sub mergeLikeNext($@)                                                           #UC Merge a B<$node> in an optional context with the next node if the two have the same tag by placing the next node first in the current B<$node> and unwrapping the next node. Return B<undef> if the request fails else the current B<$node>. Identical to L<mergeLikeElements|/mergeLikeElements>
 {my ($node, @context) = @_;                                                    # Node, optional context
  return undef if @context and !$node->at(@context);                            # Not in specified context
  if (my $next = $node->next(@context ? ($context[0]) : (-t $node)))            # Check following node has the same context as far as it is known
   {my $t = $node->lastText && $next->firstText;                                # Check whether we will be merging two text items
    $node->putLastCut($next);
    $next->putPrevAsText(q( )) if $t;                                           # Separate the two nodes with some white space if they contain just text
    $next->unwrap;
    return $node;
   }
  undef
 }

BEGIN{*mln=*mergeLikeNext}

sub mergeLikePrev($@)                                                           #UC Merge a B<$node> in an optional context with the previous node if the two have the same tag by placing the previous node first in the current B<$node> and unwrapping the previous node. Return B<undef> if the request fails else return the specified B<$node>.
 {my ($node, @context) = @_;                                                    # Node, optional context
  return undef if @context and !$node->at(@context);                            # Not in specified context
  if (my $prev = $node->prev(@context ? ($context[0]) : (-t $node)))            # Check following node has the same context as far as it is known
   {my $t = $node->firstText && $prev->lastText;                                # Check whether we will be merging two text items
    $node->putFirstCut($prev);
    $prev->putNextAsText(q( )) if $t;                                           # Separate the two nodes with some white space if they contain just text
    $prev->unwrap;
    return $node;
   }
  undef
 }

#b <b id="1">bbbb</b><b id="2">BBBB</b>
#c mergeLikePrev b
#d Merge the current node into the previous node if it has the same tag name.

BEGIN{*mlp=*mergeLikePrev}

sub mergeOnlyChildLikeNext($@)                                                  #UC Merge a B<$node> if it is the only child of its parent with a preceding node of the same name that is also the only child of its parent and return the specified B<$node> or B<undef> if the request fails.
 {my ($node, @context) = @_;                                                    # Node, optional context
  return undef if @context and !$node->at(@context);                            # Not in specified context
  return undef unless $node->isOnlyChild;                                       # Node is an only child
  return undef unless my $parent = $node->parent;                               # Parent node
  return undef unless my $Parent = $parent->next(-t $parent);                   # Prior parent
  return undef unless $Parent->hasSingleChild(-t $node);                        # Prior single child
  mergeLikeNext($_) for $parent, $node;                                         # Merge
  $node                                                                         # Original node with content merged in
 }

BEGIN{*mocln=*mergeOnlyChildLikeNext}

sub mergeOnlyChildLikePrev($@)                                                  #UC Merge a B<$node> if it is the only child of its parent with a preceding node of the same name that is also the only child of its parent and return the specified B<$node> or B<undef> if the request fails.
 {my ($node, @context) = @_;                                                    # Node, optional context
  return undef if @context and !$node->at(@context);                            # Not in specified context
  return undef unless $node->isOnlyChild;                                       # Node is an only child
  return undef unless my $parent = $node->parent;                               # Parent node
  return undef unless my $Parent = $parent->prev(-t $parent);                   # Prior parent
  return undef unless $Parent->hasSingleChild(-t $node);                        # Prior single child
  mergeLikePrev($_) for $parent, $node;                                         # Merge
  $node                                                                         # Original node with content merged in
 }

BEGIN{*moclp=*mergeOnlyChildLikePrev}

sub mergeOnlyChildLikePrevLast($@)                                              #UC Merge a B<$node> if it is the only child of its parent with a preceding node with the same tag that is the last child of its parent and return the previous B<$node> or B<undef> if the request fails.
 {my ($node, @context) = @_;                                                    # Node, optional context
  return undef if @context and !$node->at(@context);                            # Not in specified context
  return undef unless $node->isOnlyChild;                                       # Node is an only child
  return undef unless my $parent = $node->parent;                               # Parent node
  return undef unless my $Parent = $parent->prev(-t $parent);                   # Prior parent
  return undef unless my $cousin = $Parent->last(-t $node);                     # Prior matching last child
  mergeLikeNext($_) for $Parent, $cousin;                                       # Merge
  $cousin                                                                       # Original node with content merged in
 }

sub ditaMaximumNumberOfEntriesInATGroupRow($)                                   #U Return the maximum number of entries in the rows of the specified B<$table> or B<undef> if not a table.
 {my ($tgroup) = @_;                                                            # TGroup node
  $tgroup->at_tgroup or confess "Not a tgroup node: ".$tgroup->tag;             # Confirm we are on a tgroup
  my $N = 0;                                                                    # Maximum number of entries in a row
  $tgroup->by(sub                                                               # Traverse tgroup
   {if (my ($r, $hb, $g) = @_)
     {if ($r->at_row_thead_tgroup or $r->at_row_tbody_tgroup)                   # Check this row is in the current table
       {if ($g == $tgroup)
         {$N = max($N, $r->ditaNumberOfColumnsInRow);                           # Maximum number of entries in a row so far
         }
       }
     }
   });

  $N
 }

sub ditaNumberOfColumnsInRow($)                                                 #UP Return estimate of the number of columns in a row
 {my ($row) = @_;                                                               # Row
  my @e = $row->c_entry;
  my $spans = 0;                                                                # Number of additional spanned columns
  for my $e(@e)                                                                 # Number of pad entries in this row
   {if (my $a = $e->attr_namest)                                                # Span start
     {if (my $b = $e->attr_nameend)                                             # Span end
       {s(\A\D+) ()gs for $a, $b;                                               # Remove non digits from front of column name on the assumptions (as is so often the case) that the column names are numbered sequentially.
        $spans += ($b||0) - ($a||0);                                            # Additional columns
       }
     }
   }

  $spans + @e
 }

sub ditaTGroupStatistics($)                                                     #U Return statistics about the rows in a given table
 {my ($tgroup) = @_;                                                            # Table group node
  $tgroup->at_tgroup or confess "Not a tgroup node: ".$tgroup->tag;             # Confirm we are on a tgroup

  my $maxHeadMinusPadding;                                                      # Maximum number of entries in a head row after an padding entries have been removed
  my $maxHead;                                                                  # Maximum number of entries in a head row regardless of padding rows
  my $minHead;                                                                  # Minimum number of entries in a head row regardless of padding rows
  my $maxBodyMinusPadding;                                                      # Maximum number of entries in a body row after an padding entries have been removed
  my $maxBody;                                                                  # Maximum number of entries in a body row regardless of padding rows
  my $minBody;                                                                  # Minimum number of entries in a body row regardless of padding rows
  my $colSpec = 0;                                                              # Colspec
  my $rows = 0;                                                                 # Number of rows in table = head and body combined rows

  $tgroup->by(sub                                                               # Traverse table group
   {if (my ($r, $h, $g) = @_)
     {if ($r->at_row_thead_tgroup and $g == $tgroup)                            # Heading row in current table group
       {if (my @n = $r->c_entry)                                                # Entries in this heading row
         {my $n = $r->ditaNumberOfColumnsInRow;                                 # Number of entries in a heading row so far
          $maxHead = max($maxHead//0,  $n);                                     # Maximum number of entries in a heading row so far
          $minHead = min($minHead//$n, $n);                                     # Maximum number of entries in a heading row so far
          while(@n)                                                             # Remove padding entries
           {if ($n[-1]->isAllBlankText) {pop @n; next}
            last;
           }
          $maxHeadMinusPadding = max($maxHeadMinusPadding//0, $n);              # Maximum number of non padding entries in a heading row so far
         }
        ++$rows;
       }
      elsif ($r->at_row_tbody_tgroup and $g == $tgroup)                         # Body row in current table group
       {if (my @n = $r->c_entry)                                                # Entries in this body row
         {my $n = $r->ditaNumberOfColumnsInRow;                                 # Number of entries in a body row so far
          $maxBody = max($maxBody//0,  $n);                                     # Maximum number of entries in a body row so far
          $minBody = min($minBody//$n, $n);                                     # Maximum number of entries in a body row so far
          while(@n)                                                             # Remove padding entries
           {if ($n[-1]->isAllBlankText) {pop @n; next}
            last;
           }
          $maxBodyMinusPadding = max($maxBodyMinusPadding//0, $n);              # Maximum number of non padding entries in a body row so far
         }
        ++$rows;
       }
      elsif ($r->at_colspec_tgroup and $h == $tgroup)                           # Colspec
       {++$colSpec;
       }
     }
   });

  genHash(q(Data::Edit::Xml::Table::Statistics),                                # Statistics about a table
    colsAttribute       => $tgroup->attr_cols,                                  # Column attribute
    maxHeadMinusPadding => $maxHeadMinusPadding,                                # Maximum number of entries in a head row after an padding entries have been removed
    maxHead             => $maxHead,                                            # Maximum number of entries in a head row regardless of padding rows
    minHead             => $minHead,                                            # Maximum number of entries in a head row regardless of padding rows
    maxBodyMinusPadding => $maxBodyMinusPadding,                                # Maximum number of entries in a body row after an padding entries have been removed
    maxBody             => $maxBody,                                            # Maximum number of entries in a body row regardless of padding rows
    minBody             => $minBody,                                            # Maximum number of entries in a body row regardless of padding rows
    colSpec             => $colSpec,                                            # Number of colspec entries
    rows                => $rows,                                               # Number of rows
   );
 }

sub ditaAddPadEntriesToTGroupRows($$)                                           #UP Adding padding entries to a tgroup to make sure every row has the same number of entries
 {my ($tgroup, $nEntries) = @_;                                                 # TGroup node, number of entries
  $tgroup->at_tgroup or confess "Not a tgroup node: ".$tgroup->tag;             # Confirm we are on a tgroup
  $tgroup->by(sub                                                               # Traverse tgroup
   {if (my ($r, $hb, $g) = @_)
     {if ($r->at_row_thead_tgroup || $r->at_row_tbody_tgroup and $g == $tgroup) # Check this row is in the current tgroup
       {my $cols = $r->ditaNumberOfColumnsInRow;                                # Number of columns
        for($cols..$nEntries-1)                                                 # Number of pad entries required in this row
         {$r->putLast($r->newTag(q(entry)));                                    # Add new padding entry
         }
       }
     }
   });
 }

sub ditaAddColSpecToTGroup($$)                                                  #U Add the specified B<$number> of column specification to a specified B<$tgroup> which does not have any already.
 {my ($tgroup, $number) = @_;                                                   # Tgroup node, number of colspecs to add
  $tgroup->at_tgroup or confess "Not a tgroup node: ".$tgroup->tag;             # Confirm we are on a tgroup node
  $tgroup->set(cols=>$number);                                                  # Set cols attribute
  my @c = $tgroup->c_colspec;                                                   # Existing colspecs
  $_->unwrap for @c;                                                            # Remove existing colspecs
  for my $col(reverse 1..$number)                                               # Add colspecs
   {$tgroup->putFirst($tgroup->newTag(q(colspec),                               # Colspec
      colname=>"c$col", colnum=>"$col", colwidth=>"1*"));
   }
 }

sub ditaFixTGroupColSpec($)                                                     #U Fix the colspec attribute and colspec nodes of the specified B<$tgroup>.
 {my ($tgroup) = @_;                                                            # Tgroup node
  $tgroup->at_tgroup or confess "Not a tgroup node: ".$tgroup->tag;             # Check we are on a tgroup
  my $N = $tgroup->ditaMaximumNumberOfEntriesInATGroupRow;                      # Maximum number of entries in a row
  $tgroup->ditaAddColSpecToTGroup($N);                                          # Add colspecs
 }

sub ditaRemoveTGroupTrailingEmptyEntries($)                                     #U Remove empty trailing entry
 {my ($tgroup) = @_;                                                            # Table node
  $tgroup->at_tgroup or confess "Not a tgroup node: ".$tgroup->tag;             # Confirm we are on a tgroup node
   {for my $hb($tgroup->c_thead, $tgroup->c_tbody)                              # THead and TBody in TGroup
     {for my $r($hb->c_row)                                                     # Rows in THead and TBody
       {my @e = $r->c_entry;                                                    # Entries in Row
        while(@e)                                                               # Each entry
         {if ($e[-1]->isAllBlankText)                                           # Trailing blank entry
           {$e[-1]->cut;                                                        # Remove trailing blank entry
            pop @e;                                                             # Remove entry
            next;                                                               # Next trailing entry
           }
          last;                                                                 # Not blank
         }
       }
     }
   }
 }

sub fixTGroup($)                                                                #UC Fix the specified B<$tgroup> so that each row has the same number of entries with this number reflected in the tgroup.cols= attribute and colspec nodes.
 {my ($tgroup) = @_;                                                            # TGroup node
  $tgroup->at_tgroup or confess "Not a tgroup node: ".$tgroup->tag;             # Check we are on a tgroup

  $tgroup->by(sub
   {my ($o, $p) = @_;                                                           # Misplaced rows
    if ($o->at_row_tgroup)
     {if ($p == $tgroup)
       {my $b = $o->wrapWith_tbody;
        $b->mergeLikePrev;
       }
     }
   });

  my $stats     = $tgroup->ditaTGroupStatistics;                                # Statistics for tgroup
  my $cols      = $stats->colsAttribute;
  my $maxCols   = max($stats->maxHead//0, $stats->maxBody//0);
  my $maxColsMP = max($stats->maxHeadMinusPadding//0,
                          $stats->maxBodyMinusPadding//0);
  if (($stats->maxHead//0) == $maxCols and                                      # The right combination
      ($stats->minHead//0) == $maxCols and
      ($stats->maxBody//0) == $maxCols and
      ($stats->minBody//0) == $maxCols and
       $stats->colSpec     == $maxCols)
   {if (!$cols or $cols != $maxCols)                                            # Cols wrong but everything else ok
     {$tgroup->ditaAddColSpecToTGroup($maxCols);
     }
   }
  else                                                                          # Repad columns
   {$tgroup->ditaRemoveTGroupTrailingEmptyEntries;
    $tgroup->ditaAddPadEntriesToTGroupRows($maxColsMP);
    $tgroup->ditaAddColSpecToTGroup($maxColsMP);
   }
  $tgroup->ditaFixTGroupColSpec;
 }

sub fixTable($)                                                                 #U Fix the specified B<$table> so that each row has the same number of entries with this number reflected in the tgroup.cols= attribute and colspec nodes.
 {my ($table) = @_;                                                             # Table node
  $table->at_table or confess "Not a table node: ".$table->tag;                 # Check we are on a table

  if ($table->isAllBlankText)                                                   # Add the text of a minimal table declaration if the table is blank
   {$table->putFirstAsTree(<<END);
<tgroup cols="1">
  <tbody>
    <row><entry/></row>
  </tbody>
</tgroup>
END
   }
  else                                                                          # Try to fix a non blank table
   {for my $g($table->c_tgroup)                                                 # Each tgroup
     {$g->fixTGroup;
     }
#   if (!$table->go_title)                                                      # Add previous p as title if no title present and the p does not have prohibited items in it
#    {if (my $p = $table->prev_p)                                               # Holding off on this addition as it destroys some existing conversions
#      {my $c = $p->countTagNames;
#       if (!$$c{xref})
#        {$table->putFirstCut($p->change_title);
#        }
#      }
#    }
   }
  $table
 }

sub fixEntryColSpan($)                                                          #U Fix the colspan on an entry assumed to be under row, tbody, tgroup with @cols and colspecs' set
 {my ($entry) = @_;                                                             # Entry
  $entry->at_entry_row_tbody_tgroup or confess "Not a usable entry node";       # Check we are on an entry of the right kind

  my $colspan = $entry->attr_colspan;                                           # colspan
  return $entry unless $colspan;
  $colspan =~ m(\A\d+\Z)s or confess "Unexpected colspan value $colspan";

  my (undef, $row, $tbody, $tgroup) = $entry->ancestry;                         # cols
  my $cols = $tgroup->attr_cols or
    confess "No \@cols on tgroup ". (-A $tgroup). "\n";

  my @cols = grep {defined $_} map {$_->attr_colname} $tgroup->c_colspec;       # colspec
  @cols == $cols or confess "cols/colspec mismatch ".(-p $tgroup). "\n";
  $colspan > 0 and $colspan <= $cols or
    confess "Colspan $colspan out of range $cols";
  my %cols = map {$cols[$_]=>$_} keys @cols;

  $entry->deleteAttr_colspan;
  return $entry if $cols == 1 or $colspan == 1;                                 # Degenerate cases

  if ($entry->isOnlyChild)
   {$entry->set(namest=>$cols[0], nameend=>$cols[-1]);
   }
  elsif ($entry->isFirst)                                                       # Easy case`
   {$entry->set(namest=>$cols[0], nameend=>$cols[$colspan-1]);
   }
  elsif ($entry->isLast)                                                        # Easy case
   {$entry->set(namest=>$cols[-$colspan], nameend=>$cols[-1]);
   }
  else                                                                          # Normal case
   {sub
     {my @c = $entry->contentBefore;
      for my $e(@c)
       {if (my $end = $e->attr_nameend)
         {if (my $c = $cols{$end})
           {$entry->set(namest=>$cols[$c+1], nameend=>$cols[$c+$colspan]);
            return;
           }
         }
       }
      my $c = @c;
      $entry->set(namest=>$cols[$c], nameend=>$cols[$c+$colspan-1]);
     }->();
   }
 }

sub fixEntryRowSpan($)                                                          #U Fix the rowspan on an entry
 {my ($entry) = @_;                                                             # Entry
  $entry->at_entry or confess "Not an entry node";                              # Check we are on entry

  my $rowspan = $entry->attr_rowspan;                                           # rowspan
  return $entry unless $rowspan;

  $rowspan =~ m(\A\d+\Z)s or confess "Unexpected rowspan $rowspan value";
  $rowspan > 0 or confess "Rowspan $rowspan out of range";
  $entry->set(morerows=>$rowspan-1);
  $entry->deleteAttr_rowspan;
  $entry
 }

sub ditaConvertDlToUl($)                                                        #U Convert a L<Dita> B<$dl> to a B<ul> if each B<dlentry> has only B<dt> or B<dl> elements but not both.  Return B<undef> if such a conversion is not possible else return the new B<ul> node.
 {my ($dl) = @_;                                                                # Dl
  return undef unless $dl->at_dl;                                               # Check start tag

  my @e; my @l;
  for my $e($dl->c_dlentry)                                                     # Each dlentry
   {my @t = $e->c_dt;                                                           # dt elements
    my @d = $e->c_dd;                                                           # dd elements
    return undef if @d and @t;                                                  # Mixed entry suppresses the conversion
    push @e, $e;                                                                # Unwrap thee elements later
    push @l, @t, @d;                                                            # Convert these elements to li
   }

  $_->unwrap    for @e;                                                         # Unwrap dlentry
  $_->change_li for @l;                                                         # Change dt/dd to li
  $dl->change_ul;                                                               # Change dl to ul and return
 }

sub ditaConvertOlToSubSteps($@)                                                 #U Convert a L<Dita> B<$ul> to B<substeps> else B<undef> if this is not possible.
 {my ($ol, @context) = @_;                                                      # Ul, context
  return undef if !$ol->at(q(ol), @context);                                    # Not in specified context

  for my $e($ol->c_li)                                                          # Each li
   {$e->change_cmd__wrapWith_substep;                                           # Change li to cmd\substep
   }

  $ol->change_substeps;                                                         # Change ol to substeps
 }

sub ditaConvertUlToSubSteps($@)                                                 #U Convert a L<Dita> B<$ol> to B<substeps> else B<undef> if this is not possible.
 {my ($ul, @context) = @_;                                                      # Ul, context
  return undef if !$ul->at(q(ul), @context);                                    # Not in specified context

  for my $e($ul->c_li)                                                          # Each li
   {$e->change_cmd__wrapWith_substep;                                           # Change li to cmd\substep
   }

  $ul->change_substeps;                                                         # Change ul to substeps
 }

sub ditaConvertFromHtmlDl($)                                                    #U Convert a B<Html> B<$dl> to a L<Dita> B<dl> or return B<undef> if this is not possible.
 {my ($dl) = @_;                                                                # Dl
  return undef unless $dl->at_dl;                                               # Check start tag

  my $t;                                                                        # Last entry
  for my $e(reverse @$dl)                                                       # Each dt/dd
   {if ($e->at_dt)                                                              # dt
     {if ($t)                                                                   # Wrap to last entry with dlentry
       {$t = $e->wrapTo($t->prev, q(dlentry));
       }
      else                                                                      # Wrap to end with dlentry
       {$t = $e->wrapToLast(q(dlentry));
       }
     }
   }
  $dl
 }

sub ditaConvertSimpleTableToTable($)                                            #U Convert a L<Dita> B<simpletable> to a B<table>.
 {my ($simpleTable) = @_;                                                       # Simple table
  $simpleTable->at_simpletable or confess "Not on a simple table";              # Not a simpletable

  $simpleTable->by(sub                                                          # Traverse table making changes
   {if (my ($s) = @_)
     {if    ($s->at_simpletable)                                                # Simple table
       {$s->change_table;                                                       # Change to table
        my $g = $s->wrapContentWith_tgroup;                                     # Wrap content with tgroup
        my $b = $g->wrapContentWith_tbody;                                      # Wrap content with tbody
        if (my $h = $b->go_thead)                                               # Move thead out of tbody
         {$b->putPrevCut($h);
         }
        $s->fixTable;                                                           # Fix remaining problems
        $s->deleteAttr_relcolwidth;
       }
      elsif ($s->at_sthead)                                                     # thead
       {$s->change_thead;
        $s->wrapContentWith_row;
       }
      elsif ($s->at_stentry)                                                    # entry
       {$s->change_entry;
       }
      elsif ($s->at_strow)                                                      # row
       {$s->change_row;
       }
     }
   });

  $simpleTable                                                                  # Simple table is now a normal table
 }

sub ditaCouldConvertConceptToTask($)                                            #U Check whether a concept could be converted to a task
 {my ($concept) = @_;                                                           # Concept to check
  return () unless $concept->at_concept;                                        # Not a concept
  my $body = $concept->go_conbody;                                              # No conbody
  return () unless $body;
  $body->c_ol;                                                                  # Ol to break on
 }

sub ditaConvertConceptToTask($@)                                                #U Convert a Dita B<concept> to a B<task> by representing B<ol> as B<steps>. Return B<undef> if the conversion is not possible because there are no B<ol> else return the specified B<$concept> as as B<task>.
 {my ($node, @context) = @_;                                                    # Node, optional context

  return undef if @context and !$node->at(@context);                            # Node not in specified context
  return undef unless my $concept = $node->upUntil_concept;

  my @ol = $concept->ditaCouldConvertConceptToTask;                             # Breaking ol
  return undef unless @ol;

  my $body = $concept->go_conbody;                                              # Conbody guaranteed by prior check
  if (my $p = $ol[0]->prev)                                                     # Items before first ol become context
   {$p->wrapFromFirst_context;
   }
  else
   {$body->putFirst($body->newTag_context);
   }

  for my $i(grep {$_ > 0} keys @ol)                                             # Items between ol become stepsection
   {my $o = $ol[$i];
    my $O = $ol[$i-1];
    if ($o != $O)
     {$O->wrapSiblingsBetween($o, q(stepsection));
     }
   }

  if (my $p = $ol[-1]->next)                                                    # Items after last ol become results
   {$p->wrapToLast_result;
   }
  else
   {$body->putLast($body->newTag_result);
   }

  $ol[0]->wrapTo($ol[-1], q(steps));                                            # Range of ol becomes steps

  for my $ol(@ol)                                                               # Each ol becomes steps
   {for my $li($ol->c_li)
     {my @note;
      while(my $first = $li->first(qr(\A(fig|image|note)\Z)))
       {push @note, $first->cut;
       }

       if (my $first = $li->first)                                              # Change something to cmd
       {if ($first->isText)
         {$first->wrapWith_cmd;
         }
        else
         {$first->change_cmd;
         }
       }
      if (my $first = $li->first)                                               # Remainder after cmd becomes info
       {if (my $last = $li->last)
         {if ($first != $last)
           {if (my $next = $first->next)
             {$next->wrapTo($last, q(info));
             }
           }
         }
       }

      if (my $cmd = $li->first_cmd)                                             # Ameliorate any trailing items in cmd by putting them first in context
       {while(my $last = $cmd->last(qr(\A(fig|image|note)\Z)))
         {my $info = $li->addLast_info;
             $info->putFirstCut($last);
         }
        if ($cmd->over2(qr(\A( li )+\Z)))                                       # Li in cmd to substeps
         {my $choices = $cmd->wrapContentWith_ol->ditaListToChoices;
          $cmd->putNextCut($choices);
          $cmd->putFirstAsText(q(Choose one of the following:));
         }
       }

      if (@note)                                                                # Preceding notes, images, figures
       {for my $n(reverse @note)
         {$li->putFirst($n);
          if (!$n->at_note)
           {$n->wrapWith_note;
           }
         }
       }

      $li->change_step;                                                         # li to step
      if (!$li->go_cmd)                                                         # Make sure there is a command for the step
       {if (my $n = $li->go_note)
         {$n->putNextAsTree(q(<cmd/>));
         }
       else
         {$li->putFirstAsTree(q(<cmd/>));
         }
       }
     }
    $ol->unwrap;
   }

  $concept->change_task;
  $body   ->change_taskbody;

  $concept->go_taskbody_result__hasSingleChild_section__unwrap__change_example; # Unwrap section only child of result and make result into an example
  $concept->go_taskbody_result__hasSingleChild_example__parent__unwrap;         # Unwrap result around example

  $concept
 }

BEGIN{*ct=*ditaConvertConceptToTask}

sub ditaConvertReferenceToConcept($)                                            #U Convert a Dita B<reference> to a B<concept> by unwrapping sections. Return B<undef> if the conversion is not possible because there are no B<ol> else return the specified B<$reference> as as B<concept>.
 {my ($reference) = @_;                                                         # Reference
  $reference->at_reference or confess "Not a reference";                        # Check we are on reference

  my $body = $reference->go_refbody;
     $body or confess "No refbody under reference";

  $reference->change_concept;                                                   # Change topic to concept
  $body     ->change_conbody;

  $reference->by(sub                                                            # Unwrap sections
   {$_->unwrap_section;
   });

  $reference                                                                    # Return reference as a concept
 }

sub ditaConvertReferenceToTask($)                                               #U Convert a Dita B<reference> to a B<task> in situ by representing B<ol> as B<steps>. Return B<undef> if the conversion is not possible because there are no such B<ol> else return the specified B<$reference> as as B<task>.
 {my ($reference) = @_;                                                         # Reference
  $reference->at_reference or confess "Not a reference";                        # Check we are on reference

  my $body = $reference->go_refbody;
     $body or confess "No refbody under reference";

# my $ol;                                                                       # Check that the necessary ol is present
# $body->by(sub
#  {my ($o) = @_;
#   if ($o->at_ol_section_refbody)
#    {++$ol;
#    }
#  });
# return undef unless $ol;

  if (my $concept = $reference->ditaConvertReferenceToConcept)
   {return $concept->ditaConvertConceptToTask;
   }
  confess "Unable to convert reference to task";
 }

sub ditaConvertConceptToReference($)                                            #U Convert a Dita B<concept> to a B<reference>. Return B<undef> if the conversion is not possible else return the specified B<$concept> as as B<reference>.
 {my ($concept) = @_;                                                           # Concept
  $concept->at_concept or confess "Not a concept";                              # Check we are on concept

  my $body = $concept->go_conbody;
     $body or confess "No conbody under concept";

  if (my @s = $body->c_section)                                                 # Sections in concept
   {if (my $p = $s[0]->prev)
     {$body->wrapContentWith_section->moveEndAfter($p);
     }
    if (my $n = $s[-1]->next)
     {$body->wrapContentWith_section->moveStartBefore($n);
     }
   }
  else
   {$body->wrapContentWith_section;
   }

  $concept->change_reference;
  $body   ->change_refbody;

#  if (!$concept->id)
#   {$concept->createGuidId;
#   }

  $concept
 }

sub ditaConvertTopicToTask($)                                                   #U Convert a topic that is not already a task into a task
 {my ($x) = @_;                                                                 # Topic parse tree

  if ($x->at_concept)                                                           # Convert concept
   {$x->ditaConvertConceptToTask;
   }
  elsif ($x->at_reference)                                                      # Convert reference
   {$x->ditaConvertReferenceToTask;
   }

  $x->at_task                                                                   # Succeeded if we now have a task
 }

sub ditaConvertSectionToConcept($)                                              #U Convert a Dita B<$section> to a B<concept>. Return B<undef> if the conversion is not possible else return the specified B<$section> as as B<concept>.
 {my ($section) = @_;                                                           # Section
  $section->at_section or confess "Not a section, its a ",-t $section;          # Check we are on a section

  my $title = $section->first_title;                                            # Locate any existing title

  my $concept = $section->change_concept;                                       # Change section to concept
     $concept->wrapContentWith_conbody;

  if ($title)                                                                   # Move title into position
   {$concept->putFirstCut($title);
   }
  else
   {$concept->putFirst($concept->newTag_title);
   }

  $concept->createGuidId unless $concept->id;                                   # Create an id for the topic

  $concept                                                                      # Return concept
 }

BEGIN{*sc=*ditaConvertSectionToConcept}

sub ditaConvertConceptToSection($)                                              #U Convert a Dita B<concept> to a B<$section> . Return B<undef> if the conversion is not possible else return the specified B<$section> as as B<concept>.
 {my ($concept) = @_;                                                           # Section
  $concept->at_concept or confess "Not a concept, its a ",-t $concept;          # Check we are on a concept

  if (my $conbody = $concept->go_conbody)                                       # Unwrap conbody
   {$conbody->unwrap;
   }

  if (my $title = $concept->first_title)                                        # Remove any existing blank title
   {if ($title->isAllBlankText)
     {$title->cut;
     }
   }

  my $section = $concept->change_section;                                       # Change concept to section

  $section                                                                      # Return section
 }

BEGIN{*cs=*ditaConvertConceptToSection}

sub ditaConvertSectionToReference($)                                            #U Convert a Dita B<$section> to a B<reference>. Return B<undef> if the conversion is not possible else return the specified B<$section> as as B<reference>.
 {my ($section) = @_;                                                           # Section
  if (my $c = $section->ditaConvertSectionToConcept)                            # First convert section to concept
   {return $c->ditaConvertConceptToReference;                                   # Second convert concept to reference
   }
  undef                                                                         # Conversion not possible
 }

sub ditaConvertSectionToTask($)                                                 #U Convert a Dita B<$section> to a B<task>. Return B<undef> if the conversion is not possible else return the specified B<$section> as as B<task>.
 {my ($section) = @_;                                                           # Section
  if (my $c = $section->ditaConvertSectionToConcept)                            # First convert section to concept
   {return $c->ditaConvertConceptToTask;                                        # Second convert concept to task
   }
  undef                                                                         # Conversion not possible
 }

sub ditaObviousChanges($)                                                       #U Make obvious changes to a L<parse|/parse> tree to make it look more like L<Dita>.
 {my ($node) = @_;                                                              # Node

  $node->by(sub                                                                 # Do the obvious conversions
   {my ($o) = @_;

    my %change =                                                                # Tags that should be changed
     (a            => q(xref),
      book         => q(bookmap),
      code         => q(codeph),
      command      => q(codeph),                                                # Needs approval from Micalea
      emphasis     => q(b),
      figure       => q(fig),
      guibutton    => q(uicontrol),
      guilabel     => q(uicontrol),
      guimenu      => q(uicontrol),
      itemizedlist => q(ul),                                                    # PS2-570
      link         => q(xref),
      listitem     => q(li),
      menuchoice   => q(uicontrol),
      orderedlist  => q(ol),
      para         => q(p),
      quote        => q(q),
      replaceable  => q(varname),
      span         => q(div),                                                   # Span to div at 2019.05.19 18:59:50
      subscript    => q(sub),
      variablelist => q(dl),
      varlistentry => q(dlentry),
     );

    my %deleteAttributesDependingOnValue =                                      # Attributes that should be deleted if they have specified values
     (b=>[[qw(role bold)], [qw(role underline)]],
     );

    my @deleteAttributesUnconditionally =                                       # Attributes that should be deleted unconditionally from all tags that have them
#    qw(version xml:id xmlns xmlns:xi xmlns:xl xmlns:d);
     qw(xml:id xmlns xmlns:xi xmlns:xl xmlns:d);

    my %renameAttributes =                                                      # Attributes that should be renamed
     (xref      => [[qw(linkend href)],       [qw(xrefstyle outputclass)]],     # 2018.06.14 added xrefstyle->outputclass
      fig       => [[qw(role outputclass)],   [qw(xml:id id)]],
      example   => [[qw(role outputclass)]],
      imagedata => [[qw(contentwidth scale)], [qw(fileref href)]],
      image     => [[qw(src href)]],                                            # Image source at 2019.05.19 19:04:50
     );

    for my $old(sort keys %change)                                              # Perform requested tag changes
     {$o->change($change{$old}) if $o->at($old);
     }

    for my $tag(sort keys %deleteAttributesDependingOnValue)                    # Delete specified attributes if they have the right values
     {if ($o->at($tag))
       {$o->deleteAttr(@$_) for @{$deleteAttributesDependingOnValue{$tag}};
       }
     }

    $o->deleteAttrs(@deleteAttributesUnconditionally);                          # Delete attributes unconditionally from all tags that have them

    for my $tag(sort keys %renameAttributes)                                    # Rename specified attributes
     {if ($o->at($tag))
       {$o->renameAttr(@$_) for @{$renameAttributes{$tag}};
       }
     }
   });

  $node->addFirst(q(title)) if $node->at(qr(\A(concept|reference|task)\Z));     # Make sure we have a title
  $node
 }

sub ditaXrefs($)                                                                #U Make obvious changes to all the B<xref>s found in a L<parse|/parse> tree to make them more useful in L<Dita>.
 {my ($x) = @_;                                                                 # Parse tree

  $x->by(sub                                                                    # NEX116 at 2018.12.28 01:10:40
   {my ($r, $b) = @_;
    if ($r->at_xref)
     {if (my $h = $r->href)
       {if ($h =~ m(\Ahttps?://|\Awww\.|\.com\Z|\.net\Z|\.org\Z)s)
         {$r->set(scope=>q(external), format=>q(html));
         }
       }
      if ($b and my $tag = -t $b)                                               # Wrap the xref with a p if immediately under a body
       {if ($tag =~ m(body\Z)s)
         {$r->wrapWith_p;
         }
       }
     }
   });
 }

sub ditaSampleConcept(%)                                                        #S Sample concept
 {my (@options) = @_;                                                           # Options for concept
  shift @options while @options && ref $options[0];                             # Remove any leading references to find the actual string or file to be parsed

  my %options  = @options;
  checkKeys(\%options,                                                          # Check options
    {title     =>q(Title of the concept),
     metadata  =>q(Metadata of the concept ),
     body      =>q(Body of the concept ),
    });

  my $title    = $options{title}  // "Title unknown - please provide one using the title keyword";
  my $prolog   = $options{prolog} // q();
  my $body     = $options{body}   // "<p>Please provide the body of this concept using the body keyword</p>";

  my $metadata = sub
   {my $m = $options{metadata};
    return '' unless $m;
    qq(<prolog><metadata>$m</metadata></prolog>\n)
   }->();

  my $concept  = new(<<END);
<concept>
  <title id="title">$title</title>
  $metadata
  <conbody>$body</conbody>
</concept>
END

  $concept->createGuidId;
  $concept
 }

sub ditaSampleTask(%)                                                           #S Sample task
 {my (@options) = @_;                                                           # Options for task
  shift @options while @options && ref $options[0];                             # Remove any leading references to find the actual string or file to be parsed
  my %options  = @options;
  checkKeys(\%options,                                                          # Check options
    {title     =>q(Title of the task),
    });

  my $title    = $options{title}  // "Title unknown";
  my $task  = new(<<END);
<task>
  <title id="title">$title</title>
  <taskbody>
    <context/>
    <steps/>
    <result/>
  </taskbody>
</task>
END
  $task->createGuidId;
  $task
 }

sub ditaSampleBookMap(%)                                                        #S Sample bookmap
 {my (@options) = @_;                                                           # Options for bookmap

  shift @options while @options && ref $options[0];                             # Remove any leading references to find the actual string or file to be parsed
  my %options  = @options;
  checkKeys(\%options,                                                          # Check bookmap options
    {appendices=>q(Appendices),
     author    =>q(Author of the document),
     chapters  =>q(Chapter and  topicrefs),
     metadata  =>q(Meta data),
     notices   =>q(Name of file containing notices),
     title     =>q(Title of the document),
     year      =>q(Copyright year),
    });

  my $author   = sub                                                            # Author
   {my $a = $options{author};
    return qq(<author>$a</author>) if $a;
    q(<author/>)
   }->();

  my $title    = sub                                                            # Title
   {my $t = $options{title};
    return qq(<mainbooktitle>$t</mainbooktitle>) if $t;
    q(<mainbooktitle/>)
   }->();

  my $year     = sub                                                            # Year
   {my $y = $options{year};
    return qq(<year>$y</year>) if $y;
    q(<year/>)
   }->();

  my $chapters = sub
   {my $c = $options{chapters};
    return '' unless $c;
    return -p $c if ref($c);
    $c
   }->();

  my $appendices = sub
   {my $c = $options{appendices};
    return '' unless $c;
    return -p $c if ref($c);
    $c
   }->();

  my $notices = sub
   {my $n = $options{notices};
#   return ' <notices/>' unless $n;
    return ' <notices/>' unless $n;
    qq(<notices href="$n" navtitle="Notices"/>)
   }->();

  my $metadata = sub
   {my $m = $options{metadata};
    return '' unless $m;
    qq(<metadata>$m</metadata>\n)
   }->();

  my $bookMap = new(<<END);                                                     # Sample bookmap
<bookmap>
  <booktitle>
    $title
  </booktitle>
  <bookmeta>
    <shortdesc/>
    $author
    <source/>
    $metadata
    <category/>
    <keywords>
      <keyword/>
    </keywords>
    <prodinfo>
      <prodname product=""/>
      <vrmlist>
        <vrm version=""/>
      </vrmlist>
      <prognum/>
      <brand/>
    </prodinfo>
    <bookchangehistory>
      <approved>
        <revisionid/>
      </approved>
    </bookchangehistory>
    <bookrights>
      <copyrfirst>
        $year
      </copyrfirst>
      <bookowner/>
    </bookrights>
  </bookmeta>
 <frontmatter>
  $notices
  <booklists>
  <toc/>
  </booklists>
  <preface/>
</frontmatter>
$chapters
<appendices>
$appendices
</appendices>
<reltable>
    <relheader>
        <relcolspec/>
        <relcolspec/>
    </relheader>
    <relrow>
        <relcell/>
        <relcell/>
    </relrow>
    <relrow>
        <relcell/>
        <relcell/>
    </relrow>
</reltable>
</bookmap>
END
  $bookMap->createGuidId;                                                       # Id for bookmap
  $bookMap
 }

sub isADitaMap($)                                                               #U Return the specified B<$node> if this node is a L<Dita> map else return B<undef>
 {my ($node) = @_;                                                              # Node to test
  $node->tag =~ m(map\Z) ? $node : undef                                        # Map
 }

sub topicTypeAndBody($)                                                         #P Topic type and corresponding body.
 {my ($type) = @_;                                                              # Type from qw(bookmap concept reference task)
  return qw(bookmap     BookMap)               if $type =~ /\Abookmap/i;
  return qw(concept     Concept    conbody)    if $type =~ /\Aconcept/i;
  return qw(glossentry  Glossary   glossentry) if $type =~ /\Aglossentry/i;
  return qw(map         Map)                   if $type =~ /\Amap/i;
  return (q(map),     q(Subject Scheme Map))   if $type =~ /\AsubjectScheme/i;
  return qw(reference   Reference  refbody)    if $type =~ /\Areference/i;
  return qw(task        Task       taskbody)   if $type =~ /\Atask/i;
  return qw(topic       Topic      body)       if $type =~ /\Atopic/i;
  return qw(unknown     Unknown    unknown);
  confess "Unknown document type: $type\n";
 }

sub ditaRoot($)                                                                 #U Return the specified B<$node> if it a L<Dita> root node else return B<undef>.
 {my ($node) = @_;                                                              # Node to check
  return $node if $node->tag =~
    m(\A(bookmap|concept|glossentry|map|subjectScheme|reference|task|topic)\Z)s;
 }

sub ditaTopicHeaders($;$)                                                       #U Add L<XML> headers for the dita document type indicated by the specified L<parse|/parse> tree
 {my ($node, $String)  = @_;                                                    # Node in parse tree, suffix string
  my $string  = $String // q();
  my $parse   = $node->parser;
  my  $t      = $parse->tag;
  my ($n, $N) = topicTypeAndBody $t;
  my $r = xmlHeader(<<END) =~ s(\s*\Z) (\n)sr;
<!DOCTYPE $t PUBLIC "-//OASIS//DTD DITA $N//EN" "$n.dtd" []>
$string
END
  $r
 }

sub ditaPrettyPrintWithHeaders($)                                               #U Add L<XML> headers for the dita document type indicated by the specified L<parse|/parse> tree to a pretty print of the parse tree.
 {my ($node)  = @_;                                                             # Node in parse tree
  my $s = -p $node;

  if ($node->isADitaMap and $s =~ m(<topicsubject)i)                            # An exception to the general rule
   {return xmlHeader <<END.$s
<!DOCTYPE map PUBLIC "-//OASIS//DTD DITA Classification Map//EN" "classifyMap.dtd">
END
   }

  $node->ditaTopicHeaders($s);
 }

#sub ditaFindFirstFailure($@)                                                    #U Return the first node that fails to conform to the L<Dita> standard starting at the specified B<$node>. Return B<undef> if no failing node was found.
# {my ($node, @context) = @_;                                                    # Node, optional context
#  return undef if @context and !$node->at(@context);                            # Not in specified context
#
#  my $r = Dita::Validate::firstFailure($node);                                  # Validate node along path from parent
#  return $r if $r;                                                              # Specified node is invalid
#  for my $n($node->contents)                                                    # Check children
#   {my $r = Dita::Validate::firstFailure($n);                                   # Validate child
#    return $r if $r;                                                            # Return invalid child node
#   }
#  undef                                                                         # No failing node found
# }
###a
###b <concept><title/><conbody><p/><q/></conbody></concept>
###c atTop
###c ditaFindFirstFailure
###c set id failed
###d Find the first failing node

sub ditaCutTopicmetaFromAClassificationMap($@)                                  #U Remove a topicmeta node from a classification map. Dita::Validate was built from conventional maps and so does not recognize all the situations where topicmeta is invalid in a classification map
 {my ($node, @context) = @_;                                                    # Node, optional context
  return undef if @context and !$node->at(@context);                            # Not in specified context

  if (@context and $context[0]=~ m(topicmeta)i)                                 # Pending DTD parsing we have to do this manually
   {if ($node->isAllBlankText and !$node->isFirst and !$node->prev_title)
     {$node->unwrap;
     }
    return $node;
   }
  undef
 }

#sub ditaCutIfEmptyAndFirstFailingChild($@)                                      #U Remove this node if is in the specified optional context, empty and the first child under its parent which fails to conform to the L<Dita> standard. Return $node if the node is valid else B<undef>.
# {my ($node, @context) = @_;                                                    # Node, optional context
#  return undef if @context and !$node->at(@context);                            # Not in specified context
#
#  my     $r = Dita::Validate::validateChild($node);                             # Validate node along path from parent
#  if    ($r == 0) {                   return undef}                             # Not a Dita node
#  elsif ($r == 1) {                   return undef}                             # Not reachable
#  elsif ($r == 2) {$node->cutIfEmpty; return undef}                             # Reachable but in error: cut if empty, stop the PCD
#  else            {                   return $node}                             # Reachable and valid
# }
##a
##b <concept><title/><p/></concept>
##c ditaCutIfEmptyAndFirstFailingChild p
##d Remove first empty child that fails to conform to Dita.

sub ditaAddTopicReport($$)                                                      # Place a report into a dita topic using required clean up
 {my ($tree, $report) = @_;                                                     # Topic, report
  $tree->downToDie(sub
   {my ($o) = @_;
    if ($o->at_conbody)
     {$o->putFirstRequiredCleanUp($report);
      die;
     }
    elsif ($o->at_refbody)
     {my $c = $o->addFirst_section;
      $c->putFirstRequiredCleanUp($report);
      die;
     }
    elsif ($o->at_taskbody)
     {my $c = $o->addFirst_context;
      $c->putFirstRequiredCleanUp($report);
      die;
     }
   });
 }

our $defaultBrowser;                                                            # Name of default browser executable
our $defaultEditorHelp =                                                        # Editor help Url
  q(https://philiprbrenan.github.io/data_edit_xml_edit_commands.html);

sub help($)                                                                     #U Get help for a node and the editor
 {my ($node) = @_;                                                              # Node
  my $t = -t $node;                                                             # Node in question
  my $l = substr($t, 0, 1);                                                     # Index page
  my $u = qq(http://docs.oasis-open.org/dita/dita/v1.3/errata02/os/complete/part3-all-inclusive/contentmodels/cmlt${l}.html#cmlt${l}__$t);
  return $u unless $defaultBrowser;                                             # Return the url unless we have a default browser
  qx($defaultBrowser $u & $defaultBrowser $defaultEditorHelp &)                 # Display url in default browser
 }

BEGIN{*h=*help}

#D2 Html and word conversions                                                   # Methods useful for converting Word and L<Html> to L<Dita>

sub htmlHeadersToSections($)                                                    #U Position sections just before html header tags so that subsequently the document can be divided into L<sections|/divideDocumentIntoSections>.
 {my ($tree) = @_;                                                              # Parse tree

  $tree->by(sub                                                                 # Move each section definition upwards so that its parent is another section. Intervening container tags such as <div> or <span> should have been unwrapped by this point as they might move the section further back than is desired.
   {my ($o) = @_;
    if ($o->tag =~ m(\Ah(\d)\Z)i)
     {my $level = $1;
      $o->putPrev($o->newTag(q(section), level=>$level));
     }
   });
 }

sub getSectionHeadingLevel($)                                                   #UP Get the heading level from a section tag.
 {my ($o) = @_;                                                                 # Node
  return undef unless $o->at(qq(section));
  $o->attr(qq(level))
 }

sub unwrapSingleParentsOfSection($)                                             #UP Unwrap single parents of section: in word documents the header is often buried in a list to gain a section number - here we remove these unnecessary items
 {my ($tree) = @_;                                                              # Parse tree

  $tree->by(sub
   {my ($o) = @_;
    if ($o->at(qq(section)))
     {$o->unwrapParentsWithSingleChild;
     }
   });
 }

sub extendSectionToNextSection($)                                               #UP Extend a section tag until it meets the next section tag
 {my ($tree) = @_;                                                              # Parse tree

  $tree->by(sub                                                                 # Place each non heading node in its corresponding heading node
   {my ($o) = @_;
    if (my $h = getSectionHeadingLevel($o))
     {while(my $n = $o->next)
       {last if getSectionHeadingLevel($n);
        $n->cut;
        $o->putLast($n);
       }
     }
   });
 }

sub structureAdjacentSectionsByLevel($)                                         #UP Structure adjacent sections by level
 {my ($tree) = @_;                                                              # Parse tree

  $tree->by(sub                                                                 # Place each sub section in its containing section
   {my ($o) = @_;
    if (my $h = getSectionHeadingLevel($o))
     {while(my $n = $o->next)
       {my $i = getSectionHeadingLevel($n);
        last if !defined($i) or $i <= $h;
        $o->putLast($n->cut);
       }
     }
   });
 }

sub divideDocumentIntoSections($$)                                              # Divide a L<parse|/parse> tree into sections by moving non B<section> tags into their corresponding B<section> so that the B<section> tags expand until they are contiguous. The sections are then cut out by applying the specified sub to each B<section> tag in the L<parse|/parse> tree. The specified sub will receive the containing B<topicref> and the B<section> to be cut out as parameters allowing a reference to the cut out section to be inserted into the B<topicref>.
 {my ($node, $cutSub) = @_;                                                     # Parse tree, cut out sub

  $node->unwrapSingleParentsOfSection;                                          # In word documents the header is often buried in a list to gain a section number - here we remove these unnecessary items
  $node->extendSectionToNextSection;                                            # Place each non heading node in its corresponding heading node
  $node->structureAdjacentSectionsByLevel;                                      # Place each sub section in its containing section

  $node->by(sub                                                                 # Wrap each section in topicrefs
   {my ($o) = @_;
    if ($o->at(qq(section)))
     {my $t = $o->wrapWith(q(topicref));                                        # Topic ref
      $t->putLast($_->cut) for $o->c(q(topicref));                              # Move topics out of section into containing topic
     };
   });

  $node->by(sub                                                                 # Cut out each section
   {my ($o, $p) = @_;
    if ($o->at(qq(section)))
     {$p->at(q(topicref)) or confess "Section not in topicref";
      $cutSub->($p, $o);
     }
   });
 }

sub divideHtmlDocumentIntoSections($)                                           #U Divide a L<parse|/parse> tree representing an html document into sections based on the heading tags.
 {my ($tree) = @_;                                                              # Parse tree
  $tree->htmlHeadersToSections;
  $tree->extendSectionToNextSection;                                            # Place each non heading node in its corresponding heading node
  $tree->structureAdjacentSectionsByLevel;                                      # Place each sub section in its containing section
  $tree->by(sub{$_->change(q(title), qr(\Ah\d\Z)i)});                           # Move each section definition upwards so that its parent is another section. Intervening container tags such as <div> or <span> should have been unwrapped by this point as they might move the section further back than is desired.
  $tree->by(sub{$_->renameAttr_level_outputclass if $_->at_section});           # Change level to outputclass
 }

sub ditaParagraphToNote($;$)                                                    #U Convert all <p> nodes to <note> if the paragraph starts with 'Note:', optionally wrapping the content of the <note> with a <p>
 {my ($node, $wrapNoteContentWithParagaph) = @_;                                # Parse tree, wrap the <note> content with a <p> if true
  my $count = 0;                                                                # Count the number of changes
  $node->by(sub                                                                 # Each node
   {my ($o, $p) = @_;                                                           # Text, p
    if ($o->isText_p)                                                           # Text under p
     {if ($o->text =~ m(\A\s*(Attention|Caution|Danger|Fastpath|Important|Notices?|Notes?|Remember|Restriction|Tip|Trouble|Warning)\s*:?\s*)i) # Note
       {my $note = $1;
        $p->change(q(note));                                                    # Change to note
        $p->wrapContentWith(q(p)) if $wrapNoteContentWithParagaph;              # Wrap content if required
        $o->text =~        s(\A\s*$note\s*:?\s*) ()i;                           # Remove note text
        $o->text = ucfirst($o->text);                                           # Uppercase leading character
        $note =~ s(s\Z) ()gs;                                                   # Remove optional trailing s
        $p->set(type=>lc $note) unless $note =~ m(\Anote\Z)is;                  # Set type
        ++$count;
       }
     }
   });
  $count
 }

sub wordStyles($)                                                               #U Extract style information from a parse tree representing a word document.
 {my ($x) = @_;                                                                 # Parse tree
  my $styles;                                                                   # Styles encountered
  $x->by(sub                                                                    # Each node
   {my ($o, $p) = @_;
    if ($o->at(qw(text:list-level-style-bullet text:list-style)))               # Bulleted lists
     {my ($level, $name) = ($o->attr(q(text:level)), $p->attr(q(style:name)));
      if ($level and $name)
       {$styles->{bulletedList}{$name}{$level}++;
       }
     }
   });
  $styles                                                                       # Return styles encountered
 }

sub htmlTableToDita($)                                                          #U Convert an L<html table> to a L<Dita> table.
 {my ($table) = @_;                                                             # Html table node

  $table->wrapContentWith(q(tgroup));                                           # tgroup
  my %transforms =                                                              # Obvious transformations
   (td=>q(entry),
    th=>q(entry),
    tr=>q(row),
   );

  $table->by(sub
   {if (my $c = $transforms{-t $_}) {$_->change($c)}
   });

  my $N = 0;                                                                    # Number of columns in widest row
  $table->by(sub
   {my ($r, undef, $group, $Table) = @_;
    if ($r->at(qw(row), undef, qw(tgroup table)) and $Table == $table)          # In this table, not in an embedded sub table
     {my $n = $r->c(q(entry));
      $N = $n if $n > $N;
     }
   });

  $table->by(sub                                                                # Fix colspecs
   {my ($group, $Table) = @_;
    if ($group->at(qw(tgroup table)) and $Table == $table)                      # In this table, not in an embedded sub table
     {$group->setAttr   (q(cols), $N);
      $_->unwrap for $group->c(q(colspec));
      $group->putFirst($group->newTag                                           # Insert colspecs
       (qw(colspec colname), qq(c$_), q(colnum), $_, qw(colwidth 1*)))
        for reverse 1..$N;
     }
   });

  $table->by(sub                                                                # Span last element of each row to fill row
   {my ($r, undef, $group, $Table) = @_;
    if ($r->at(qw(row), undef, qw(tgroup table)) and $Table == $table)
     {my @e = $r->c(q(entry));
      my $n = @e;
      $e[-1]->setAttr(namest=>qq(c$n), nameend=>qq(c$N)) if @e < $N;
     }
   });
 }

sub ditaSyntaxDiagramFromDocBookCmdSynopsis($)                                  #U Convert doc book cmdsynopsis to Dita syntax diagram
 {package ditaSyntaxDiagramFromDocBookCmdSynopsis;                              # Allows us to package the entire conversion as one method
  use Carp;
  my ($x) = @_;                                                                 # Parse tree

  sub command   {q(command)}                                                    # Constants encountered
  sub ellipsis  {q(<sep>&#x2026;</sep>)}
  sub importance{q(importance)}
  sub opt       {q(opt)}
  sub optional  {q(optional)}
  sub plain     {q(plain)}
  sub rep       {q(rep)}
  sub req       {q(req)}
  sub repeat    {q(repeat)}
  sub required  {q(required)}

  sub ditaOptional($)                                                           # Return the specified B<$node> if it is optional else return B<undef>.
   {my ($node) = @_;                                                            # Node to test
    return $node if $node->attrX_importance eq optional;
    undef
   }

  sub ditaNonOptionalGroupSeq($)                                                # Return the specified B<$node> if it is groupseq but not optional else return B<undef>.
   {my ($node) = @_;                                                            # Node to test
    return $node if $node->at_groupseq and !ditaOptional($node);
    undef
   }

  sub ditaOptionalGroupChoice($)                                                # Return the specified B<$node> if it is groupchoice and optional else return B<undef>.
   {my ($node) = @_;                                                            # Node to test
    return $node if $node->at_groupchoice and ditaOptional($node);
    undef
   }

  sub ditaRepeated($)                                                           # Return the specified B<$node> if it is repeated else return B<undef>.
   {my ($node) = @_;                                                            # Node to test
    if (my $s = $node->last_seq)
     {if ($s->string eq ellipsis)
       {return $node;
       }
     }
    undef
   }

  sub docBookChoiceAttribute($)                                                 # Return the value of the B<choice> attribute of the B<arg> or B<group>  specified by B<$node> if we are on a B<arg> or B<group> node else confess.
   {my ($node) = @_;                                                            # Node to test
    $node->at_arg || $node->at_group or
      confess "Not an arg or group node".-A $node;
    $node->attrX_choice
   }

  $x->by(sub
   {my ($o, $p) = @_;

    if ($o->at_cmdsynopsis)                                                     # cmdsynopsis
     {$o->change_syntaxdiagram__wrapContentWith_groupseq;
      $o->renameAttr(qw(xml:id id));
     }

    if ($o->at_command)                                                         # command
     {$o->change_kwd;
      $o->outputclass = command;
     }

    $o->change_var_replaceable;                                                 # replaceable

    if ($o->isText_arg)                                                         # Text under arg becomes kwd
     {$o->wrapWith_kwd
     }

    if ($o->at_arg)                                                             # arg
     {my $c = docBookChoiceAttribute($o);

      if (!$c or $c eq opt)                                                     # Choice
       {$o->set(importance=>optional);
        $o->change_groupseq;
       }
      elsif ($c eq plain)
       {$o->change_groupseq;
       }
      elsif ($c eq q(req))
       {$o->change_groupchoice;
       }
      else
       {confess "Unprogrammed arg choice=$c\n";
       }

      if (my $r = $o->attr_rep)                                                 # Repetition
       {if ($r eq repeat)
         {$o->putLastAsTree(ellipsis);
          $o->wrapContentWith_groupseq unless $o->at_groupseq;                  # PS2-658 - wrap the repeated sequence unless it is already wrapped
         }
       }

      $o->deleteAttrs_choice_rep;                                               # Attributes processed
     }


# Brackets are used to distinguish between optional, required, or plain arguments. Usually square brackets are placed around optional arguments, [-f | -g], and curly brackets are placed around required arguments, {-f | -g}. Plain arguments are required, but are not decorated with brackets.

    if ($o->at_group)                                                           # group
     {my $c = docBookChoiceAttribute($o);

      if (!$c or $c eq opt)                                                     # Choice
       {$o->set(importance=>optional);
        $o->change_groupchoice;
       }
      elsif ($c eq plain)
       {$o->change_groupseq;
       }
      elsif ($c eq req)
       {$o->change_groupchoice;
       }
      else
       {confess "Unprogrammed group choice=$c\n";
       }

      if (my $r = $o->attr_rep)                                                 # Repetition
       {if ($r eq repeat)
         {$o->putLastAsTree(ellipsis);
          $o->wrapContentWith_groupseq unless $o->at_groupseq;                  # PS2-658 - wrap the repeated sequence unless it is already wrapped
         }
       }

      $o->deleteAttrs_choice_rep;                                               # Attributes processed
     }
   });

  for(1..9)                                                                     # Flatten the syntax diagram where possible
   {my $w = 0;

    $x->by(sub                                                                  # Consolidate groups with only one item in them
     {my ($k, $g) = @_;
      if ($k->isOnlyChild(qr(\A(kwd|var)\Z), qr(\A(groupseq|groupchoice)\Z)))
       {$g->unwrap; ++$w;
        if (ditaOptional($g))                                                   # If group is optional then the child must be optional
         {$k->set(importance=>optional);
         }
       }
     });

    $x->by(sub                                                                  # groupseq single child of groupchoice importance=optional goes to groupseq importance=optional
     {my ($s, $c) = @_;
      if ($s->isOnlyChild_groupseq_groupchoice)
       {if (ditaOptional($c))
         {if (!ditaRepeated($s))
           {$c->unwrap; ++$w;
            $s->set(importance=>optional);                                      # The wrapping choice forces this seq to be optional
           }
         }
       }
     });

    $x->by(sub                                                                  # A non optional groupseq under a non optional groupseq can be unwrapped
     {my ($s, $t) = @_;
      if ($s->at_groupseq_groupseq)
       {if   (ditaNonOptionalGroupSeq($s))
         {if (ditaNonOptionalGroupSeq($t))
           {if (!ditaRepeated($s))
             {$s->unwrap; ++$w;
             }
           }
         }
       }
     });

    $x->by(sub                                                                  # An only child group choice under a non optional groupseq: unwrap the groupseq as unnecessary
     {my ($c, $s) = @_;
      if ($c->isOnlyChild_groupchoice_groupseq)
       {if (ditaNonOptionalGroupSeq($s))
         {$s->unwrap; ++$w;
         }
       }
     });

    $x->by(sub                                                                  # An only child group choice under a non optional groupseq: unwrap the groupseq as unnecessary
     {my ($c, $d) = @_;
      if ($c->isOnlyChild_groupchoice_groupchoice)
       {if ($c->attrX_importance eq $d->attrX_importance)
         {$c->unwrap; ++$w;
         }
        elsif (ditaOptionalGroupChoice($c))
         {$d->unwrap; ++$w;
         }
        elsif (ditaOptionalGroupChoice($d))
         {$c->unwrap; ++$w;
         }
       }
     });

    $x->by(sub                                                                  # groupseq importance=optional under groupchoice importance=optional: remove importance from groupseq as it is redundant and leads to over bracketing
     {my ($s, $c) = @_;
      if ($c and $c->at_groupchoice)
       {if   (ditaOptional($c) and ditaOptional($s))
         {$s->deleteAttrs_importance;
         }
       }
     });

    $x->by(sub                                                                  # importance=required always produces the same results as importance=>undef in the html5 transformation so we assume that it can be safely removed
     {my ($o) = @_;
      if ($o->attrX_importance eq required)
       {$o->deleteAttr_importance;
       }
     });

    last unless $w;
   }

  $x
 } # ditaSyntaxDiagramFromDocBookCmdSynopsis

sub ditaSyntaxDiagramToBasicRepresentation($)                                   #U Convert Dita syntax diagrams into Micaela's Basic Version.
 {my ($x) = @_;                                                                 # Parse tree

  $x->down(sub
   {my ($o, $p) = @_;

    my $i = $o->attrX_importance;                                               # Add brackets
    if ($o->at_groupchoice)                                                     # Groupchoice has brackets and separators
     {if ($i eq q(optional))
       {$o->putPrevAsText(q( [));
        $o->putNextAsText(q(] ));
       }
      else
       {$o->putPrevAsText(q( {));
        $o->putNextAsText(q(} ));
       }
      my ($c, @c) = @$o;
      $_->putPrevAsText(q( | )) for @c;
     }
    elsif ($i eq q(optional))                                                   # Non group choice just has square brackets
     {$o->putPrevAsText(q( [));
      $o->putNextAsText(q(] ));
     }

    if ($o->at_syntaxdiagram)                                                   # Map tags
     {$o->change_p;
      $o->outputclass = q(syntaxdiagram);
     }
    elsif ($o->at_kwd and $o->outputclassX eq q(command))
     {$o->change_cmdname;
     }
    elsif ($o->at_kwd)
     {$o->change_codeph;
     }
    elsif ($o->at_var)
     {$o->change_userinput;
     }
    elsif ($o->at_sep)
     {$o->change_codeph;
     }
    elsif ($o->at_groupseq or $o->at_groupchoice)
     {$o->unwrap;
     }

    $o->deleteAttrs_outputclass_importance;

   });

 } # ditaSyntaxDiagramToBasicRepresentation

sub ditaUnderPNotConbody                                                        #P Return a hash of items that L<Dita> permits under B<p> but not directly under B<conbody>
{{
  "abbreviated-form" => 1,
  "apiname"          => 1,
  "b"                => 1,
  "boolean"          => 1,
  "CDATA"            => 1,
  "cite"             => 1,
  "cmdname"          => 1,
  "codeph"           => 1,
  "equation-inline"  => 1,
  "filepath"         => 1,
  "fn"               => 1,
  "i"                => 1,
  "indexterm"        => 1,
  "indextermref"     => 1,
  "keyword"          => 1,
  "line-through"     => 1,
  "markupname"       => 1,
  "menucascade"      => 1,
  "msgnum"           => 1,
  "msgph"            => 1,
  "numcharref"       => 1,
  "option"           => 1,
  "overline"         => 1,
  "parameterentity"  => 1,
  "parmname"         => 1,
  "ph"               => 1,
  "q"                => 1,
  "state"            => 1,
  "sub"              => 1,
  "sup"              => 1,
  "synph"            => 1,
  "systemoutput"     => 1,
  "term"             => 1,
  "text"             => 1,
  "textentity"       => 1,
  "tm"               => 1,
  "tt"               => 1,
  "u"                => 1,
  "uicontrol"        => 1,
  "userinput"        => 1,
  "varname"          => 1,
  "wintitle"         => 1,
  "xmlatt"           => 1,
  "xmlelement"       => 1,
  "xmlnsname"        => 1,
  "xmlpi"            => 1,
  "xref"             => 1,
}} # ditaUnderPNotConbody

sub ditaWrapWithPUnderConbody($)                                                #U Wrap items immediately under L<DITA> B<conbody> with B<p> or merge with any previous B<p> if the item in question does not fit under B<conbody> but does fit under B<p>. Return the current node if it is B<conbody> else return B<undef>.
 {my ($conbody) = @_;                                                           # Section
  $conbody->at_conbody or confess "Not a conbody, its a ". -t $conbody;         # Check we are on a conbody
  my $fit = ditaUnderPNotConbody;                                               # Tags that need to be wrapped with B<p> if they occur directly under B<conbody>.
  my $p;                                                                        # Last p encountered
  my @c = @$conbody;                                                            # Each direct child of conbody
  for my $c(@c)                                                                 # Each direct child of conbody
   {if ($$fit{-t $c})                                                           # Better as a p
     {if ($p)                                                                   # Prior tag is p
       {$p->putLastCut($c);                                                     # Merge
       }
      else                                                                      # Start new p
       {$p = $c->wrapWith_p;                                                    # Wrap with p and make new p
       }
     }
    elsif ($c->at_p)                                                            # Tag is p - so make it the last p
     {$p = $c;                                                                  # P tag becomes last p
     }
    else                                                                        # Not on a p and not better wrapped in p
     {$p = undef;                                                               # New p required
     }
   }
 }

#D1 PCD                                                                         # Please Change Dita Language Features

our %savedNodes;                                                                #E Hash of saved nodes

sub putNodeAs($$@)                                                              #CU Return the specified B<$node> after saving it under the specified B<$name> if we are in the optional B<@context>.
 {my ($node, $name, @context) = @_;                                             # Node, save name, optional context
  return $node if @context and !$node->at(@context);                            # Not in specified context
  $savedNodes{$name} = $node;                                                   # Save a reference to the node under the specified name and return the currentr node
 }

BEGIN{*put=*putNodeAs}

sub getNodeAs($$@)                                                              #CU Return the specified B<$node> unless it is possible to return the node saved with L<putNodeAs> under the specified B<name> and we are optionally in the specified B<@context>.
 {my ($node, $name, @context) = @_;                                             # Default node, name of saved node, optional context
  return $node if @context and !$node->at(@context);                            # Not in specified context
  $savedNodes{$name} // $node;                                                  # Return saved node defaulting to current node
 }

BEGIN{*get=*getNodeAs}

sub reportNodeAttributes($$@)                                                   #CU Print the attributes of the specified B<$node> identified by the specified B<$label> if we are in the optional B<@context>.
 {my ($node, $label, @context) = @_;                                            # Node node, label, optional context
  return '' if @context and !$node->at(@context);                               # Not in specified context
  join ' ', $label, $node->printAttributes;                                     # Print label and attributes
 }

BEGIN{*rna=*reportNodeAttributes}

sub reportNodeContext($$@)                                                      #CU Print the context of the specified B<$node> identified by the specified B<$label> if we are in the optional B<@context>.
 {my ($node, $label, @context) = @_;                                            # Node node, label, optional context
  return '' if @context and !$node->at(@context);                               # Not in specified context
  join ' ', $label, $node->context;                                             # Print label and context
 }

BEGIN{*rnc=*reportNodeContext}

sub reportNode($$@)                                                             #CU Print the parse tree starting at the specified B<$node> identified by the specified B<$label> if we are in the optional B<@context>.
 {my ($node, $label, @context) = @_;                                            # Node node, label, optional context
  return '' if @context and !$node->at(@context);                               # Not in specified context
  join '', $label, "\n", $node->prettyString;                                   # Print label and parse tree
 }

BEGIN{*rn=*reportNode}

#D1 Debug                                                                       # Debugging methods

sub sss($@)                                                                     #U Put, after the current B<$node>, the specified B<@text> and return the current $node.
 {my ($node, @text) = @_;                                                       # Node, text
  my $text = join ' ', @text;                                                   # Text to insert
  $node->putNextAsText($text);                                                  # Insert text after current node
  $node
 }
#a
#b <b><c><d/></c></b>
#c at d c
#c sss we reached here
#d Say something special so we can see where we were.

sub printAttributes($)                                                          #U Print the attributes of a node.
 {my ($node) = @_;                                                              # Node whose attributes are to be printed.
  return q() unless keys %{$node->attributes};                                  # No attributes

  my %a = %{$node->attributes};                                                 # Attributes
  my $s = '';
  for(sort keys %a)                                                             # Each attribute
   {next unless defined(my $v = $a{$_});
    $s .= $_.'="'.$v.'" ';                                                      # Attributes enclosed in "" in alphabetical order
   }
  chop($s);
  length($s) ? ' '.$s : '';
 }

sub printAttributesHtml($)                                                      #U Print the attributes of a node as html.
 {my ($node) = @_;                                                              # Node whose attributes are to be printed.
  return q() unless keys %{$node->attributes};                                  # No attributes

  my %a = %{$node->attributes};                                                 # Attributes
  my $s = '';
  for(sort keys %a)                                                             # Each attribute
   {next unless defined(my $v = $a{$_});                                        # Attributes enclosed in "" in alphabetical order
    $s .= qq(<span class="xmlAttr">$_</span>).
          qq(<span class="xmlEquals">=</span>).
          qq(<span class="xmlValue">"$v"</span> );
   }
  chop($s);
  length($s) ? ' '.$s : '';
 }

sub printStack($@)                                                              #UC Print the attributes of a node and each of its parents
 {my ($node, @context) = @_;                                                    # Node whose attributes are to be printed, optional context.
  return undef if @context and !$node->at(@context);                            # Not in specified context

  my @s;                                                                        # Resulting text
  my @p = reverse $node->ancestry;                                              # Each ancestor
  for my $i(keys @p)                                                            # Print and indent
   {my $p = $p[$i];
    push @s, q(  )x$i.$p->printNode;                                            # Print node with indentation
   }
  join "\n", @s, '';                                                            # Return ancestry
 }

sub printNode($@)                                                               #UC Print the tag and attributes of a node.
 {my ($node, @context) = @_;                                                    # Node whose attributes are to be printed, optional context.
  return undef if @context and !$node->at(@context);                            # Not in specified context
  my %a = %{$node->attributes};                                                 # Attributes
  my $t = $node->tag;                                                           # Tag
  my $s = '';                                                                   # Attributes string
  for(sort keys %a)                                                             # Each attribute
   {next unless defined(my $v = $a{$_});                                        # Each defined attribute
    $s .= qq( ${_}="$v");                                                       # Attributes enclosed in "" in alphabetical order
   }
  qq($t$s)                                                                      # Result
 }

sub printNodeAsSingleton($@)                                                    #UC Print the tag and attributes of a node as if it were a single node regardless of any child nodes it might have
 {my ($node, @context) = @_;                                                    # Node whose attributes are to be printed, optional context.
  return undef if @context and !$node->at(@context);                            # Not in specified context
  q(<).printNode($node).q(/>);
 }

sub printAttributesReplacingIdsWithLabels($)                                    #UP Print the attributes of a node replacing the id with the labels.
 {my ($node) = @_;                                                              # Node whose attributes are to be printed.
  my %a = %{$node->attributes};                                                 # Clone attributes
  my %l = %{$node->labels};                                                     # Clone labels
  delete $a{id};                                                                # Delete id
  $a{id} = join ', ', sort keys %l if keys %l;                                  # Replace id with labels in cloned attributes
  defined($a{$_}) ? undef : delete $a{$_} for keys %a;                          # Remove undefined attributes
  return '' unless keys %a;                                                     # No attributes
  my $s = ' '; $s .= $_.'="'.$a{$_}.'" ' for sort keys %a; chop($s);            # Attributes enclosed in "" in alphabetical order
  $s
 }

sub printAttributesExtendingIdsWithLabels($)                                    #UP Print the attributes of a node extending the id with the labels.
 {my ($node) = @_;                                                              # Node whose attributes are to be printed.
  my %a = %{$node->attributes};                                                 # Clone attributes
  my %l = %{$node->labels};                                                     # Clone labels
  my $i = $a{id} ? $a{id}.q(, ) : q();                                          # Format id
  $a{id} = join '', $i, join ', ', sort keys %l if keys %l;                     # Extend id with labels in cloned attributes
  defined($a{$_}) ? undef : delete $a{$_} for keys %a;                          # Remove undefined attributes
  return '' unless keys %a;                                                     # No attributes
  my $s = ' '; $s .= $_.'="'.$a{$_}.'" ' for sort keys %a; chop($s);            # Attributes enclosed in "" in alphabetical order
  $s
 }

sub checkParentage($)                                                           #UP Check the parent pointers are correct in a L<parse|/parse> tree.
 {my ($x) = @_;                                                                 # Parse tree.
  $x->by(sub
   {my ($o) = @_;
   for($o->contents)
     {my $p = $_->parent;
      $p == $o or confess "No parent: ". $_->tag;
      $p and $p == $o or confess "Wrong parent: ".$o->tag. ", ". $_->tag;
     }
   });
 }

sub checkParser($)                                                              #UP Check that every node has a L<parse|/parse>r.
 {my ($x) = @_;                                                                 # Parse tree.
  $x->by(sub
   {$_->parser or confess "No parser for ". $_->tag;
    $_->parser == $x or confess "Wrong parser for ". $_->tag;
   })
 }

sub goFish($@)                                                                  #U A debug version of L<go|/go> that returns additional information explaining any failure to reach the node identified by the L<path|/path>.\mReturns ([B<reachable tag>...], B<failing tag>, [B<possible tag>...]) where:\m=over\m=item B<reachable tag>\mthe path elements successfully traversed;\m=item B<failing tag>\mthe failing element;\m=item B<possible tag>\mthe possibilities at the point where the path failed if it failed else B<undef>.\m=back\mParameters:
 {my ($node, @path) = @_;                                                       # Node, search specification.
  my $p = $node;                                                                # Current node
  my @p;                                                                        # Elements of the path successfully processed
  while(@path)                                                                  # Position specification
   {my $i = shift @path;                                                        # Index name
    return ([@p], $i, [sort keys %{$p->indexes}]) unless $p;                    # There is no node of the named type under this node
    reindexNode($p);                                                            # Create index for this node
    my $q = $p->indexes->{$i};                                                  # Index
    return ([@p], $i, [sort keys %{$p->indexes}]) unless defined $q;            # Complain if no such index
    push @p, $i;
    if (@path)                                                                  # Position within index
     {if ($path[0] =~ /\A([-+]?\d+)\Z/)                                         # Numeric position in index from start
       {my $n = shift @path;                                                    # Next path item
        my $N = scalar(@$q);                                                    # Dimension of index
        return ([@p], $n, [0..$N]) unless defined($p = $q->[$n]);               # Complain if no such index
        push @p, $n;                                                            # Save successfully processed index
       }
      elsif (@path == 1 and $path[0] =~ /\A\*\Z/)                               # Final index wanted
       {return [@p];
       }
      else {$p = $q->[0]}                                                       # Step into first sub node by default
     }
    else {$p = $q->[0]}                                                         # Step into first sub node by default on last step
   }
  [@p]                                                                          # Success!
 }

sub nn($)                                                                       #P Replace new lines in a string with N to make testing easier.
 {my ($s) = @_;                                                                 # String.
  $s =~ s/\n/N/gsr
 }

#D1 Documentation                                                               # Update documentation describing this module

sub extractDocumentationFlags($$)                                               #P Generate documentation for a method with a user flag.
 {my ($flags, $method) = @_;                                                    # Flags, method name.
  my $b = "${method}NonBlank";                                                  # Not blank method name - used for a small number of navigation methods
  my $x = "${method}NonBlankX";                                                 # Not blank, die on B<undef> method name
  my $m = $method;                                                              # Second action method
     $m =~ s/\Afirst/next/gs;
     $m =~ s/\Alast/prev/gs;
  my @doc; my @code;

  if ($flags =~ m/C/is)                                                         # Context flag for a method that returns a single node or B<undef> if in the wrong context
   {push @doc, <<'END' if $flags =~ m/C/s;
Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.
END
    push @doc, <<'END' if $flags =~ m/c/s;
Use the required B<$tag> parameter to specify the expected tag on the specified
B<$node> using a single match expression as understood by method L<at|/at>. Use
the optional B<@context> parameter to test the context as understood by method
L<at|/at> of the parent node of the specified B<$node>. If either test fails
this method returns B<undef> immediately.
END
   }
  if ($flags =~ m/K/s)                                                          # Context flag for a method that returns an array of nodes or the empty array if in the wrong context
   {push @doc, <<'END';
Use the B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>.  If a context is supplied and
B<$node> is not in this context then this method returns an empty list B<()>
immediately.
END
   }

  if ($flags =~ m/B/s)                                                          # Skip blank text
   {push @doc, <<END;
Use B<$b> to skip a (rare) initial blank text CDATA. Use B<$x> to die rather
then receive a returned B<undef> or false result.
END
    push @code, <<END;
sub $b
 {my \$r = &$method(\$_[0]);
  return undef unless \$r;
  if (\$r->isBlankText)
   {shift \@_;
    return &$m(\$r, \@_)
   }
  else
   {return &$m(\@_);
   }
 }

sub $x
 {my \$r = &$b(\@_);
  die '$method' unless defined(\$r);
  \$r
 }
END
   }

  [join("\n", @doc), join("\n", @code), $flags =~ m/B/ ? [$b, $x] : ()]
 }

sub writeContentToGitHub($$)                                                    #P Upload the contents of a string to a file on github
 {my ($file, $content) = @_;                                                    # File on github, file contents
  my $g = <<'END';                                                              # I am the only person who does this so there is no point in adding GitHub::Crud to the prerequisites list
use GitHub::Crud;
my $target = writeFile(undef, $content);
GitHub::Crud::writeFileFromFileUsingSavedToken
 (q(philiprbrenan), q(philiprbrenan.github.io), $file, $target);
GitHub::Crud::writeFileFromFileUsingSavedToken
 (q(ryffine), q(ryffine.github.io), $file, $target);
END

  my $r = eval $g;
  confess "$@" if $@;
  $r
 }

sub writeFileToGitHub($$)                                                       #P Upload the contents of the specified local file to a file on github
 {my ($file, $localFile) = @_;                                                  # File name on github, local file name
  my $g = <<'END';                                                              # I am the only person who does this so there is no point in adding GitHub::Crud to the prerequisites list
use GitHub::Crud;
GitHub::Crud::writeFileFromFileUsingSavedToken
 (q(philiprbrenan), q(philiprbrenan.github.io), $file, $localFile);
GitHub::Crud::writeFileFromFileUsingSavedToken
 (q(ryffine), q(ryffine.github.io), $file, $localFile);
END

  my $r = eval $g;
  confess "$@" if $@;
  $r
 }

sub processModuleDescription($)                                                 #P Process module description to write a description of editing Xml to GitHub
 {my ($d) = @_;                                                                 # Module description

  my $b  = q(<b>);
  my $bb = q(</b>);
  my $th = q(<th align="left">);

  my @h  = (<<END);                                                             # Editor commands description
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title></title>
<style>
.l0 {
  background-color: #f0fff0
}
.l1 {
  background-color: #fff0ff
}
</style>
</head>
<body>

<div style="width: 80em">

<h1>Methods in Data::Edit::Xml that can be used to Edit Dita</h1>
END

  my $methods = sub                                                             # Format methods either the immediately useful ones or all of them
   {my ($isUseful) = @_;                                                        # Immediately useful methods

    return unless my $M = $d->{methods};                                        # Method descriptions

    push my @h, (<<END);                                                        # Table header
<table border="0" cellpadding="20">
<tr>${th}Line${th}Method long name${th}aka${th}Parameter${th}Description
END

    my $line = 0;                                                               # Method number

    for my $method(sort keys %$M)
     {my $m = $$M{$method};
      next if $isUseful and !$m->{isUseful};                                    # Only immediately useful methods requested
      next unless $m->{unitary};                                                # Only unitary methods can be used in the editor or the pcd language

      my $name = $m->{name};
      my $synonym = sub
       {return q() unless my $S = $m->{synonyms};
        my ($s) = sort keys %$S;
        return $s;
       }->();

      my $c = $m->{comment};                                                    # Format the comment so that it breaks
         $c = formatString($c, 80);

      my $p = $m->{parameters};
      shift @$p;                                                                # We are unitary so this is feasible
      ++$line;

      my $class = q(class="l).($line % 2).q(");                                 # Row color

      push @h, qq(<tr $class><td>$line<td>$name<td><b>$synonym</b><td><td>$c\n);
      if (@$p)
       {for my $p(@$p)
         {my ($parm, $desc) = @$p;
          push @h, qq(<tr $class><td><td><td><td>$parm<td>$desc\n);
         }
       }
     }

    push @h, (<<END);
</table>
END
    @h
   };

  push @h, <<END, &$methods(1), <<END2, &$methods(0);
<h2>Immediately useful methods</h2>
<p>The following methods are frequently use to make simple changes:
END
<h2>All methods</h2>
<p>Here is a listing of all methods currently available:
END2

  my $dts = dateTimeStamp;
  push @h, <<END;

<h1>Editing Dita using Geany or PCD</h1>

<p>L[dex] is a Perl extension module that can help you edit L[Dita] either
interactively using L[Geany] or in batch using L[PCD] files.

<h2>Configuring Geany to Edit Dita and PCD</h2>

<p>Please obtain a version of L[Ubuntu] and follow the instructions at
L[pcdInstall].

<h2>Editing Dita interactively with Geany</h2>

<p>You can use many of the methods found in L[dex] one at a time directly from
the L[Geany] editor when it has been configured for L[Dita] editing. The
methods executed are recorded in log visible in another L[Geany] tab so that
they can be edited directly into a program that is being developed to use
L[dex] directly from L[Perl] or from L[pcd].

<p>The table below lists the methods that can be used in L[Geany].  Simply type
the method name and any parameters up against a closing $b&gt;$bb or
$b&gt;$bb as shown on line $b 13 $bb like this:

<p><img src="/images/mlp_start.png"/>

<p>and press $b shift+Enter $bb to merge the two lists:

<p><img src="/images/mlp_finish.png"/>


<h2>Editing Dita in batch using PCD</h2>

<p>L[PCD] files contain a list of L[dex] commands, one per line, that are applied
to each node of each of the parse trees produced by parsing all of the L[DITA]
files found in the same directory tree as the L[PCD] file.

<p><a href="https://github.com/philiprbrenan/pleaseChangeDita">This
repository</a> contains two L[DITA] l[concept]s and one L[PCD] file. The L[PCD]
file unwraps a <b>b</b> tag found under another <b>b</b> tag.

<p>There are four types of line in a L[PCD] file:

<p><pre>
1
2   # Comment
3   Unwrap b under b and delete empty paragraphs
4        unwrap b b
5        cutIfEmpty p
</pre></p>

<table cellpadding="20" border="0">
<tr><th>Line<th>Name<th>Description

<tr><td>1<td>Blank lines      <td>Blank lines are ignored

<tr><td>2<td>Comment lines    <td>If the first non space character is <b>#</b>
then this is a comment line and it is ignored.

<tr><td>3<td>Description lines<td>If the first character is not a space it is a
description line whose purpose is to communicate with other humans what the
following commands aim to do.

<tr><td>4<td>Command lines    <td>If the first character is a space then the
rest of the line is command line: a method name chosen from L[dex] followed by
any parameters separated from each other by one or more spaces.

</table>

<p>Each L[PCD] file can contain zero or more description lines. Each description
line can be followed by zero or more command lines called a "block of command
lines" or "block" for short. L[PCD] executes each block against each node in
each of the parse trees constructed by parsing all of the L[DITA] files found
in the same folder as the L[PCD] files.  Execution of the block continues line
by line until the end of the block or the L[dex] method fails. If the block
completes successfully, the description line for that block is printed out to
show that it "fired".

<p>L[Geany] on L[Ubuntu] has been configured as an L[IDE] to make editing both
L[PCD] and test L[DITA] files a pleasant experience with all the usual
facilities provided by such an L[IDE] such as help choosing command names,
testing the syntax of L[PCD] files once they are written, then executing
them against L[DITA] files and showing the execution results.</p>

<p>You can also place l[PCD] files into the same L[S3] Bucket or L[github] repo
as your L[dita] files and then process them in bulk with L[ssxr].
<p>Updated: $dts
</div>
</body>
</html>
END

  my $h2 = join "\n", @h;                                                       # Html
  my $h1 = expandWellKnownUrlsInHtmlFormat  ($h2);
  my $h  = expandWellKnownUrlsInHtmlFromPerl($h1);
     $h  =~ s(([LB]) (<) ([^>]*) (>)) ($1&lt;$3&gt;)xgs;                        # Fix angle brackets

  if (1)                                                                        # Simplified documentation
   {my @l = readFile($INC{"Data/Edit/Xml.pm"});

    my $method; my @also; my @before; my @code;
    for my $l(@l)
     {if ($l =~ m(\Asub\s*(\w+)))                                               # Method for which there is simplified documentation
       {$method = $1;
        @before = (); @code = ();
       }
      if ($l =~ m(\A#a\s*(.*?)\s*\Z))                                           #a see also
       {push @also, split /\s+/, $1;
       }
      if ($l =~ m(\A#b\s*(.*?)\s*\Z))                                           #b before file with the root unwrapped
       {push @before, $1;
       }
      if ($l =~ m(\A#c\s*(.*?)\s*\Z))                                           #c Example pcd
       {push @code, $1;
       }
      if ($l =~ m(\A#d\s*(.*?)\s*\Z))                                           #d Simplified documentation
       {$d->{methods}{$method}{example} =
          genHash(q(Data::Edit::Xml::Example),                                  # Description of a pcd method
            method => $method,                                                  # Method
            also   => [@also],                                                  # See also
            before => [@before],                                                # Before file content minus root tag which will be added as <a>
            code   => [@code],                                                  # Pcd code
            doc    => $1,                                                       # One line summary
           );
       }
     }
   }
####TEST####
#  writeContentToGitHub(q(data_edit_xml_edit_commands.html), $h);
#  writeFileToGitHub(q(images/mlp_start.png),  q(/home/phil/r/oxygenWorkshop/doc/selfServiceXref/images/4/s011256.png));
#  writeFileToGitHub(q(images/mlp_finish.png), q(/home/phil/r/oxygenWorkshop/doc/selfServiceXref/images/4/s011321.png));
 } # processModuleDescription

#D1 Compression                                                                 # Read and write files of compressed xml.  These methods provide a compact, efficient way to store and retrieve parse trees to/from files.

sub writeCompressedFile($$)                                                     #U Write the parse tree starting at B<$node> as compressed L<XML> to the specified B<$file>. Use L<readCompressedFile|/readCompressedFile>  to read the B<$file>.
 {my ($node, $file) = @_;                                                       # Parse tree node, file to write to.
  makePath($file);
  open my $F, "| gzip>$file" or                                                 # Compress via gzip
    confess "Cannot open file for write because:\n$file\n$!\n";
  binmode($F, ":utf8");                                                         # Input to gzip encoded as utf8
  print  {$F} -s $node;
  close  ($F);
  -e $file or confess "Failed to write to file:\n$file\n";
  $file
 }

sub readCompressedFile($)                                                       #S Read the specified B<$file> containing compressed L<XML> and return the root node.  Use L<writeCompressedFile|/writeCompressedFile> to write the B<$file>.
 {my ($file) = @_;                                                              # File to read.
  defined($file) or
    confess "Cannot read undefined file\n";
  $file =~ m(\n) and
    confess "File name contains a new line:\n=$file=\n";
  -e $file or
    confess "Cannot read file because it does not exist, file:\n$file\n";
  open(my $F, "gunzip < $file|") or                                             # Unzip input file
    confess "Cannot open file for input, file:\n$file\n$!\n$?\n";
  local $/ = undef;
  my $string = <$F>;
  new($string)                                                                  # Reparse resulting string to recover parse tree
 }

#D1 Autoload                                                                    # Allow methods with constant parameters to be called as B<method_p1_p2>...(variable parameters) whenever it is easier to type underscores than (qw()).

sub DESTROY {}                                                                  # TO avoid seeing it in AUTOLOAD

our $AUTOLOAD;                                                                  # The method to be autoloaded appears here

sub  AUTOLOAD                                                                   # Allow methods with constant parameters to be called as B<method_p1_p2>...(variable parameters) whenever it is easier to type underscores than (qw()).
 {#return if $AUTOLOAD =~ m(Destroy)is;                                          # Perl internal
  my $q = shift;                                                                # Object package
  if ($AUTOLOAD =~ m(__)s)                                                      # Chain of calls separated by q(__)
   {no strict q(refs);                                                          # So we can call a sub by name
    my @calls = split /__/, $AUTOLOAD;                                          # Calls in chain
    for my $call(@calls)                                                        # Each call in chain
     {my ($p, @p) = split /_/, $call;                                           # Call, parameters for call
      confess "No such method : $p" unless $q->can($p);                         # Check that the method name is valid
      $q = $p->($q, @p);                                                        # Make call
#     return undef unless $q;                                                   # Abort chain of calls if any call returns undef
      return undef unless defined $q;                                           # Abort chain of calls if any call returns undef
     }
    return $q;                                                                  # Return result of chain
   }
  else                                                                          # Optimized single call
   {my ($p, @p) = split /_/, $AUTOLOAD;                                         # Break out parameters
    confess "No such method : $p" unless $q->can($p);                           # Check that the method name is valid
    unshift @_, $q, @p;                                                         # Insert static parameters into parameter list
    goto &$p;                                                                   # Call desired routine
   }
 }

#D0

#-------------------------------------------------------------------------------
# Export - eeee
#-------------------------------------------------------------------------------

use Exporter qw(import);

use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);

# containingFolder

@ISA         = qw(Exporter);
@EXPORT      = qw(formatTable);
@EXPORT_OK   = qw(
$defaultBrowser
$defaultEditorHelp
$lastParseError
%savedNodes
@saveLastCutOut
$selectionEnd
$selectionStart
);
%EXPORT_TAGS = (all=>[@EXPORT, @EXPORT_OK]);

# podDocumentation

#Install If you receive the message:\m  Expat.xs:12:19: fatal error: expat.h: No such file or directory\mduring installation, install libexpat1-dev:\m  sudo apt install libexpat1-dev

=pod

=encoding utf-8

=head1 Name

Data::Edit::Xml - Edit data held in the XML format.

=head1 Synopsis

Create a L<new|/new> XML parse tree:

  my $a = Data::Edit::Xml::new q(<a><b><c/></b><d><c/></d></a>);

use:

  say STDERR -p $a;

to L<print|/print>:

  <a>
    <b>
      <c/>
    </b>
    <d>
      <c/>
    </d>
  </a>

L<Cut|/cut> out B<c> under B<b> but not under B<d> in the created tree
by L<traversing|/Traversal> in post-order L<applying|/by> a B<sub> to each node
to L<cut|/cut> out B<c> when we are L<at|/at> B<c> under B<b> under B<a>.

  $a -> by(sub {$_ -> cut_c_b_a});

Or if you know when you are going:

  $a -> go_b_c__cut;

To get:

  <a>
    <b/>
    <d>
      <c/>
    </d>
  </a>

=head2 Bullets to unordered list

To transform a series of bullets into an unordered list, parse the input XML:

  my $a = Data::Edit::Xml::new(<<END);
<a>
<p>• Minimum 1 number</p>
<p>•   No leading, trailing, or embedded spaces</p>
<p>• Not case-sensitive</p>
</a>
END

Traverse the resulting parse tree, removing bullets and leading white space,
changing B<p> to B<li> and B<a> to B<ul>:

  $a->change_ul->by(sub
   {$_->up__change_li if $_->text_p and $_->text =~ s/\A•\s*//s
   });

Print to get:

  ok -p $a eq <<END;
<ul>
  <li>Minimum 1 number</li>
  <li>No leading, trailing, or embedded spaces</li>
  <li>Not case-sensitive</li>
</ul>
END

=head2 XSLT Transformation Example from Wikipedia

Given sample data for an B<XSLT> transformation from
L<Wikipedia|https://en.wikipedia.org/w/index.php?title=XSLT#Example_1_(transforming_XML_to_XML)>

 my $x = Data::Edit::Xml::new(<<END);
<persons>
  <person username="JS1"><name>John</name><surname>Smith</surname></person>
  <person username="MI1"><name>Morka</name><surname>Ismincius</surname></person>
</persons>
END

We can transform it by the rather shorter:

 $x->by(sub
  {$_->unwrap_name;
   $_->cut_surname;
   $_->change_name_person__change_root_persons;
  });

To get:

 ok -p $x eq <<END;
<persons>
  <name username="JS1">John</name>
  <name username="MI1">Morka</name>
</persons>
END

=head2 Convert ol to steps

Given an ordered list:

  my $a = Data::Edit::Xml::new(<<END);
<ol>
  <li>A</li>
  <li>B</li>
  <li>C</li>
</ol>
END

A single call transforms the ordered list:

  $a->ditaConvertOlToSubSteps;

to L<Dita> steps:

  ok -p $a eq <<END;
<substeps>
  <substep>
    <cmd>A</cmd>
  </substep>
  <substep>
    <cmd>B</cmd>
  </substep>
  <substep>
    <cmd>C</cmd>
  </substep>
</substeps>
END

=head1 Description

Edit data held in the XML format.


Version 20200201.


The following sections describe the methods in each functional area of this
module.  For an alphabetic listing of all methods by name see L<Index|/Index>.



=head1 Immediately useful methods

These methods are the ones most likely to be of immediate use to anyone using
this module for the first time:


L<at($node, @context)|/at($node, @context)>

Confirm that the specified B<$node> has the specified L<ancestry|/ancestry>. Ancestry is specified by providing the expected tags that the B<$node>'s parent, the parent's parent etc. must match at each level. If B<undef> is specified then any tag is assumed to match at that level. If a regular expression is specified then the current parent node tag must match the regular expression at that level. If all supplied tags match successfully then the starting node is returned else B<undef>.

L<attrX($node, $attribute)|/attrX($node, $attribute)>

Return the value of the specified B<$attribute> of the specified B<$node> or B<q()> if the B<$node> does not have such an attribute.

L<by($node, $sub)|/by($node, $sub)>

Post-order traversal of a L<parse|/parse> tree or sub tree calling the specified B<sub> at each node and returning the specified starting node. The B<sub> is passed references to the current node and all of its L<ancestors|/ancestry>. A reference to the current node is also made available via L<$_>. This is equivalent to the L<x=|/opBy> operator.

L<change($node, $name, @context)|/change($node, $name, @context)>

Change the name of the specified B<$node>, optionally  confirming that the B<$node> is in a specified context and return the B<$node>.

L<cut($node, @context)|/cut($node, @context)>

Cut out and return the specified B<$node> so that it can be reinserted else where in the L<parse|/parse> tree.

L<go($node, @path)|/go($node, @path)>

Return the node reached from the specified B<$node> via the specified L<path|/path>: (index positionB<?>)B<*> where index is the tag of the next node to be chosen and position is the optional zero based position within the index of those tags under the current node. Position defaults to zero if not specified. Position can also be negative to index back from the top of the index array. B<*> can be used as the last position to retrieve all nodes with the final tag.

L<moveEndAfter($node, $to, @context)|/moveEndAfter($node, $to, @context)>

Move the end of a B<$node> to just after the specified B<$target> node assuming that the B<$target> node is either a subsequent sibling or a child of B<$node>. Returns B<$node> if the move was made successfully, else confess that the specified move is impossible.

L<new($fileNameOrString, @options)|/new($fileNameOrString, @options)>

Create a new parse tree - call this method statically as in Data::Edit::Xml::new(file or string) to parse a file or string B<or> with no parameters and then use L</input>, L</inputFile>, L</inputString>, L</errorFile>  to provide specific parameters for the parse, then call L</parse> to perform the parse and return the parse tree.

L<prettyString($node, $depth)|/prettyString($node, $depth)>

Return a readable string representing a node of a L<parse|/parse> tree and all the nodes below it. Or use L<-p|/opString> $node

L<putLast($old, $new, @context)|/putLast($old, $new, @context)>

Place a L<cut out|/cut> or L<new|/new> node last in the content of the specified B<$node> and return the new node.  See L<putLastCut|/putLastCut> to cut and put last in one operation.  See L<addLast|/addLast> to perform this operation conditionally.

L<putNextRequiredCleanUp($node, @text)|/putNextRequiredCleanUp($node, @text)>

Place a required cleanup next after a specified B<$node> using the specified B<@text>  and return the required clean up node.

L<unwrap($node, @context)|/unwrap($node, @context)>

Unwrap the specified B<$node> if in the optional B<@context> by replacing the node with its contents. Returns the parent node on success, otherwise B<undef> if an attempt is made to unwrap a text node or the root node.

L<wrapWith($node, $tag, @context)|/wrapWith($node, $tag, @context)>

Wrap the specified B<$node> in a new node created from the specified B<$tag> in the optional B<@context> forcing the specified $node down to deepen the L<parse|/parse> tree - return the new wrapping node or B<undef> if this is not possible. See L<addWrapWith|/addWrapWith> to perform this operation conditionally.




=head1 Construction

Create a parse tree, either by parsing a L<file or string|/file or string> B<or> L<node by node|/Node by Node> B<or> from another L<parse tree|/Parse tree>.

=head2 File or String

Construct a parse tree from a file or a string.

=head3 new($fileNameOrString, @options)

Create a new parse tree - call this method statically as in Data::Edit::Xml::new(file or string) to parse a file or string B<or> with no parameters and then use L</input>, L</inputFile>, L</inputString>, L</errorFile>  to provide specific parameters for the parse, then call L</parse> to perform the parse and return the parse tree.

     Parameter          Description
  1  $fileNameOrString  Optional file name or string from which to construct the parse tree
  2  @options           Hash of other options.

B<Example:>


    my $a = Data::Edit::Xml::𝗻𝗲𝘄(<<END);                                               
  <a>
    <b>
      <c id="42" match="mm"/>
    </b>
    <d>
      <e/>
    </d>
  </a>
  END
  
    ok -p $a eq <<END;                                                                          
  <a>
    <b>
      <c id="42" match="mm"/>
    </b>
    <d>
      <e/>
    </d>
  </a>
  END
  

This is a static method and so should be invoked as:

  Data::Edit::Xml::new


=head3 cdata()

The name of the tag to be used to represent text - this tag must not also be used as a command tag otherwise the parser will L<confess|http://perldoc.perl.org/Carp.html#SYNOPSIS/>.


B<Example:>


    ok Data::Edit::Xml::𝗰𝗱𝗮𝘁𝗮 eq q(CDATA);                                        
  

=head3 parse($parser)

Parse input L<Xml|https://en.wikipedia.org/wiki/XML> specified via: L<inputFile|/inputFile>, L<input|/input> or L<inputString|/inputString>.

     Parameter  Description
  1  $parser    Parser created by L</new>

B<Example:>


    my $x = Data::Edit::Xml::new;                                                 
  
       $x->inputString = <<END;                                                   
  <a id="aa"><b id="bb"><c id="cc"/></b></a>
  END
  
       $x->𝗽𝗮𝗿𝘀𝗲;                                                                 
  
       ok -p $x eq <<END;                                                         
  <a id="aa">
    <b id="bb">
      <c id="cc"/>
    </b>
  </a>
  END
  

=head2 Node by Node

Construct a parse tree node by node.

=head3 newText(undef, $text)

Create a new text node.

     Parameter  Description
  1  undef      Any reference to this package
  2  $text      Content of new text node

B<Example:>


    ok -p $x eq <<END;                                                             
  <a class="aa" id="1">
    <b class="bb" id="2"/>
  </a>
  END
  
    $x->putLast($x->𝗻𝗲𝘄𝗧𝗲𝘅𝘁("t"));                                                
  
    ok -p $x eq <<END;                                                            
  <a class="aa" id="1">
    <b class="bb" id="2"/>
  t
  </a>
  END
  

=head3 newTag(undef, $command, %attributes)

Create a new non text node.

     Parameter    Description
  1  undef        Any reference to this package
  2  $command     The tag for the node
  3  %attributes  Attributes as a hash.

B<Example:>


    my $x = Data::Edit::Xml::newTree("a", id=>1, class=>"aa");                     
  
    $x->putLast($x->𝗻𝗲𝘄𝗧𝗮𝗴("b", id=>2, class=>"bb"));                             
  
    ok -p $x eq <<END;                                                             
  <a class="aa" id="1">
    <b class="bb" id="2"/>
  </a>
  END
  

=head3 newTree($command, %attributes)

Create a new tree.

     Parameter    Description
  1  $command     The name of the root node in the tree
  2  %attributes  Attributes of the root node in the tree as a hash.

B<Example:>


    my $x = Data::Edit::Xml::𝗻𝗲𝘄𝗧𝗿𝗲𝗲("a", id=>1, class=>"aa");                     
  
    ok -s $x eq '<a class="aa" id="1"/>';                                         
  

=head3 dupTag($node, @context)

Create a new non text node by duplicating the tag of an existing node.

     Parameter  Description
  1  $node      Node
  2  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b><c/></b>
  </a>
  END
  
    my $d = $a->go_b__dupTag;
    ok -p $d eq <<END;
  <b/>
  END
  

=head3 replaceSpecialChars($string)

Replace < > " & with &lt; &gt; &quot; &amp; Larry Wall's excellent L<Xml parser> unfortunately replaces &lt; &gt; &quot; &amp; etc. with their expansions in text by default and does not seem to provide an obvious way to stop this behavior, so we have to put them back again using this method.

     Parameter  Description
  1  $string    String to be edited.

B<Example:>


    ok Data::Edit::Xml::𝗿𝗲𝗽𝗹𝗮𝗰𝗲𝗦𝗽𝗲𝗰𝗶𝗮𝗹𝗖𝗵𝗮𝗿𝘀(q(<">)) eq q(&lt;&quot;&gt;);         
  

This is a static method and so should be invoked as:

  Data::Edit::Xml::replaceSpecialChars


=head3 undoSpecialChars($string)

Reverse the results of calling L<replaceSpecialChars|/replaceSpecialChars>.

     Parameter  Description
  1  $string    String to be edited.

B<Example:>


    ok Data::Edit::Xml::𝘂𝗻𝗱𝗼𝗦𝗽𝗲𝗰𝗶𝗮𝗹𝗖𝗵𝗮𝗿𝘀(q(&lt;&quot;&gt;)) eq q(<">);            
  

This is a static method and so should be invoked as:

  Data::Edit::Xml::undoSpecialChars


=head2 Parse tree attributes

Attributes of a node in a parse tree. For instance the attributes associated with an L<Xml|https://en.wikipedia.org/wiki/XML> tag are held in the L<attributes|/attributes> attribute. It should not be necessary to use these attributes directly unless you are writing an extension to this module.  Otherwise you should probably use the methods documented in other sections to manipulate the parse tree as they offer a safer interface at a higher level.

=head2 Parse tree

Construct a L<parse|/parse> tree from another L<parse|/parse> tree.

=head3 renew($node, @context)

Returns a renewed copy of the L<parse|/parse> tree by first printing it and then reparsing it, optionally checking that the starting node is in a specified context: use this method if you have added nodes via the L</"Put as text"> methods and wish to traverse their L<parse|/parse> tree.

Returns the starting node of the new L<parse|/parse> tree or B<undef> if the optional context constraint was supplied but not satisfied.

     Parameter  Description
  1  $node      Node to renew from
  2  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new("<a/>", inputFile=>q(aaa.xml));
    $a->putFirstAsText(qq(<b/>));
    ok !$a->go(q(b));
    my $A = $a->𝗿𝗲𝗻𝗲𝘄;
    ok -t $A->go(q(b)) eq q(b);
    ok $A->root->inputFile eq $a->root->inputFile;
  

=head3 clone($tree)

Return a clone of the entire L<parse|/parse> tree which is created using the fast L<Storable::dclone> method. The L<parse|/parse> tree is cloned without converting it to string and reparsing it so this method will not L<renew|/renew> any nodes added L<as text|/Put as text>.

Returns the starting node of the new L<parse|/parse> tree.

     Parameter  Description
  1  $tree      Parse tree

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


   {my $a = Data::Edit::Xml::new("<a> </a>");                                      
  
    my $A = $a->𝗰𝗹𝗼𝗻𝗲;                                                             
  
    ok -s $A eq q(<a/>);                                                           
  
    ok $a->equals($A);                                                             
  
   {my $x = Data::Edit::Xml::new(<<END);                                           
  <x>
    <a>aaa
      <b>bbb</b>
      ccc
      <d>ddd</d>
      eee
    </a>
  </x>
  END
  
    my $y = $x->𝗰𝗹𝗼𝗻𝗲;                                                             
  
    ok !$x->diff($y);                                                              
  

=head3 equals($node1, $node2)

Return the first node if the two L<parse|/parse> trees have identical representations via L<string|/string>, else B<undef>.

     Parameter  Description
  1  $node1     Parse tree 1
  2  $node2     Parse tree 2.

B<Example:>


   {my $a = Data::Edit::Xml::new("<a> </a>");                                      
  
    my $A = $a->clone;                                                             
  
    ok -s $A eq q(<a/>);                                                           
  
    ok $a->𝗲𝗾𝘂𝗮𝗹𝘀($A);                                                             
  

=head3 equalsIgnoringAttributes($node1, $node2, @attributes)

Return the first node if the two L<parse|/parse> trees have identical representations via L<string|/string> if the specified attributes are ignored, else B<undef>.

     Parameter    Description
  1  $node1       Parse tree 1
  2  $node2       Parse tree 2
  3  @attributes  Attributes to ignore during comparison

B<Example:>


   {my $a = Data::Edit::Xml::new(<<END);                                          
  <a>
    <b   id="1" outputclass="1" name="b">
      <c id="2" outputclass="2" name="c"/>
    </b>
  </a>
  END
  
    my $A = Data::Edit::Xml::new(<<END);                                          
  <a>
    <b   id="11" outputclass="11" name="b">
      <c id="22" outputclass="22" name="c"/>
    </b>
  </a>
  END
  
    ok !$a->equals($A);                                                           
  
    ok !$a->𝗲𝗾𝘂𝗮𝗹𝘀𝗜𝗴𝗻𝗼𝗿𝗶𝗻𝗴𝗔𝘁𝘁𝗿𝗶𝗯𝘂𝘁𝗲𝘀($A, qw(id));                                 
  
    ok  $a->𝗲𝗾𝘂𝗮𝗹𝘀𝗜𝗴𝗻𝗼𝗿𝗶𝗻𝗴𝗔𝘁𝘁𝗿𝗶𝗯𝘂𝘁𝗲𝘀($A, qw(id outputclass));                     
  

=head3 diff($first, $second, $N)

Return () if the dense string representations of the two nodes are equal, else up to the first N (default 16) characters of the common prefix before the point of divergence and the remainder of the string representation of each node from the point of divergence. All <!-- ... --> comments are ignored during this comparison and all spans of white space are reduced to a single blank.

     Parameter  Description
  1  $first     First node
  2  $second    Second node
  3  $N         Maximum length of difference strings to return

B<Example:>


   {my $x = Data::Edit::Xml::new(<<END);                                           
  <x>
    <a>aaa
      <b>bbb</b>
      ccc
      <d>ddd</d>
      eee
    </a>
  </x>
  END
  
    ok !$x->𝗱𝗶𝗳𝗳($x);                                                             
  
    my $y = $x->clone;                                                             
  
    ok !$x->𝗱𝗶𝗳𝗳($y);                                                              
  
    $y->first->putLast($x->newTag(q(f)));                                         
  
    ok nws(<<END) eq nws(-p $y);                                                  
  <x>
    <a>aaa
      <b>bbb</b>
      ccc
      <d>ddd</d>
      eee
      <f/>
    </a>
  </x>
  END
  
    is_deeply [$x->𝗱𝗶𝗳𝗳($y)],    ["<d>ddd</d> eee <", "/a></x>", "f/></a></x>"];  
  
    is_deeply [𝗱𝗶𝗳𝗳(-p $x, $y)], ["<d>ddd</d> eee <", "/a></x>", "f/></a></x>"];  
  
    is_deeply [$x->𝗱𝗶𝗳𝗳(-p $y)], ["<d>ddd</d> eee <", "/a></x>", "f/></a></x>"];  
  
    my $X = writeFile(undef, -p $x);                                              
  
    my $Y = writeFile(undef, -p $y);                                              
  
    is_deeply [𝗱𝗶𝗳𝗳($X, $Y)],    ["<d>ddd</d> eee <", "/a></x>", "f/></a></x>"];  
  

=head3 save($node, $file)

Save a copy of the L<parse|/parse> tree to a file which can be L<restored|/restore> and return the saved node.  This method uses L<Storable|https://metacpan.org/pod/Storable> which is fast but produces large files that do not compress well.  Use L<writeCompressedFile|/writeCompressedFile> to produce smaller save files at the cost of more time.

     Parameter  Description
  1  $node      Parse tree
  2  $file      File.

B<Example:>


      $y->𝘀𝗮𝘃𝗲($f);                                                                
  
      my $Y = Data::Edit::Xml::restore($f);                                        
  
      ok $Y->equals($y);                                                           
  

=head3 restore($file)

Return a L<parse|/parse> tree from a copy saved in a file by L<save|/save>.

     Parameter  Description
  1  $file      File

B<Example:>


      $y->save($f);                                                                
  
      my $Y = Data::Edit::Xml::𝗿𝗲𝘀𝘁𝗼𝗿𝗲($f);                                        
  
      ok $Y->equals($y);                                                           
  

This is a static method and so should be invoked as:

  Data::Edit::Xml::restore


=head3 expandIncludes($x)

Expand the includes mentioned in a L<parse|/parse> tree: any tag that ends in B<include> is assumed to be an include directive.  The file to be included is named on the B<href> keyword.  If the file to be included is a relative file name, i.e. it does not begin with B</> then this file is made absolute relative to the file from which this L<parse|/parse> tree was obtained.

     Parameter  Description
  1  $x         Parse tree

B<Example:>


    my @files =                                                                   
  
     (owf("in1/a.xml", q(<a id="a"><include href="../in2/b.xml"/></a>)),          
  
      owf("in2/b.xml", q(<b id="b"><include href="c.xml"/></b>)),                 
  
      owf("in2/c.xml", q(<c id="c"/>)));                                          
  
    my $x = Data::Edit::Xml::new(fpf(currentDirectory, $files[0]));               
  
       $x->𝗲𝘅𝗽𝗮𝗻𝗱𝗜𝗻𝗰𝗹𝘂𝗱𝗲𝘀;                                                        
  
    ok <<END eq -p $x;                                                            
  <a id="a">
    <b id="b">
      <c id="c"/>
    </b>
  </a>
  END
  

=head1 Print

Create a string representation of the L<parse|/parse> tree with optional selection of nodes via L<conditions|/Conditions>.

Normally use the methods in L<Pretty|/Pretty> to format the L<Xml|https://en.wikipedia.org/wiki/XML> in a readable yet reparseable manner; use L<Dense|/Dense> string to format the L<Xml|https://en.wikipedia.org/wiki/XML> densely in a reparseable manner; use the other methods to produce unreparseable strings conveniently formatted to assist various specialized operations such as debugging CDATA, using labels or creating tests. A number of the L<file test operators|/opString> can also be conveniently used to print L<parse|/parse> trees in these formats.

=head2 Pretty

Pretty print the L<parse|/parse> tree.

=head3 prettyString($node, $depth)

Return a readable string representing a node of a L<parse|/parse> tree and all the nodes below it. Or use L<-p|/opString> $node

     Parameter  Description
  1  $node      Start node
  2  $depth     Optional depth.

B<Example:>


   {my $s = <<END;                                                                     
  <a>
    <b>
      <A/>
      <B/>
    </b>
    <c>
      <C/>
      <D/>
    </c>
  </a>
  END
  
    my $a = Data::Edit::Xml::new($s);                                              
  
    ok $s eq $a->𝗽𝗿𝗲𝘁𝘁𝘆𝗦𝘁𝗿𝗶𝗻𝗴;                                                    
  
    ok $s eq -p $a;                                                               
  
   {my $a = Data::Edit::Xml::new(<<END);                                          
  <a>
    <b>bbb</b>.
    <c>ccc</c>.
  </a>
  END
  

=head3 prettyStringHtml($node, @context)

Return a string of L<html|https://en.wikipedia.org/wiki/HTML> representing a node of a L<parse|/parse> tree and all the nodes below it if the node is in the specified context.

     Parameter  Description
  1  $node      Node
  2  @context   Optional context

B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a id="1">
    <b id="2" b="B">
      <c/>
    </b>
    <d id="3" d="D">
      Some text
    </d>
  </a>
  END
  
    ok $a->𝗽𝗿𝗲𝘁𝘁𝘆𝗦𝘁𝗿𝗶𝗻𝗴𝗛𝘁𝗺𝗹 eq <<END;
  <div class="xmlLine"><span class="xmlLineStartTag"></span><span class="xmlLt">&lt;</span><span class="xmlTag">a</span> <span class="xmlAttr">id</span><span class="xmlEquals">=</span><span class="xmlValue">"1"</span><span class="xmlGt">&gt;</span></div>
  <div class="xmlLine"><span class="xmlLineStartTag">&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="xmlLt">&lt;</span><span class="xmlTag">b</span> <span class="xmlAttr">b</span><span class="xmlEquals">=</span><span class="xmlValue">"B"</span> <span class="xmlAttr">id</span><span class="xmlEquals">=</span><span class="xmlValue">"2"</span><span class="xmlGt">&gt;</span></div>
  <div class="xmlLine"><span class="xmlLineStartTag">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="xmlLt">&lt;</span><span class="xmlTag">c</span><span class="xmlSlashGt">/&gt;</span></div>
  <div class="xmlLine"><span class="xmlLineStartTag">&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="xmlLtSlash">&lt;/</span><span class="xmlTag">b</span><span class="xmlGt">&gt;</span></div>
  <div class="xmlLine"><span class="xmlLineStartTag">&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="xmlLt">&lt;</span><span class="xmlTag">d</span> <span class="xmlAttr">d</span><span class="xmlEquals">=</span><span class="xmlValue">"D"</span> <span class="xmlAttr">id</span><span class="xmlEquals">=</span><span class="xmlValue">"3"</span><span class="xmlGt">&gt;</span><span class="xmlText">Some text</span><span class="xmlLtSlash">&lt;/</span><span class="xmlTag">d</span><span class="xmlGt">&gt;</span></div>
  <div class="xmlLine"><span class="xmlLineStartTag"></span><span class="xmlLtSlash">&lt;/</span><span class="xmlTag">a</span><span class="xmlGt">&gt;</span></div>
  END
  

=head3 prettyStringDitaHeaders($node)

Return a readable string representing the L<parse|/parse> tree below the specified B<$node> with appropriate headers. Or use L<-x|/opString> $node

     Parameter  Description
  1  $node      Start node

B<Example:>


  if (1)                                                                           
   {my $a = Data::Edit::Xml::new(<<END);
  <concept/>
  END
  
    ok $a->ditaPrettyPrintWithHeaders eq <<END;
  <?xml version="1.0" encoding="UTF-8"?>
  <!DOCTYPE concept PUBLIC "-//OASIS//DTD DITA Concept//EN" "concept.dtd" []>
  <concept/>
  END
  
   }
  

=head3 prettyStringNumbered($node, $depth)

Return a readable string representing a node of a L<parse|/parse> tree and all the nodes below it with a L<number|/number> attached to each tag. The node numbers can then be used as described in L<Order|/Order> to monitor changes to the L<parse|/parse> tree.

     Parameter  Description
  1  $node      Start node
  2  $depth     Optional depth.

B<Example:>


   {my $s = <<END;                                                                     
  <a>
    <b>
      <A/>
      <B/>
    </b>
    <c>
      <C/>
      <D/>
    </c>
  </a>
  END
  
    $a->numberTree;                                                                  
  
    ok $a->𝗽𝗿𝗲𝘁𝘁𝘆𝗦𝘁𝗿𝗶𝗻𝗴𝗡𝘂𝗺𝗯𝗲𝗿𝗲𝗱 eq <<END;                                             
  <a id="1">
    <b id="2">
      <A id="3"/>
      <B id="4"/>
    </b>
    <c id="5">
      <C id="6"/>
      <D id="7"/>
    </c>
  </a>
  END
  

=head3 prettyStringCDATA($node, $depth)

Return a readable string representing a node of a L<parse|/parse> tree and all the nodes below it with the text fields wrapped with <CDATA>...</CDATA>.

     Parameter  Description
  1  $node      Start node
  2  $depth     Optional depth.

B<Example:>


    my $a = Data::Edit::Xml::new("<a><b>A</b></a>");                              
  
    my $b = $a->first;                                                            
  
       $b->first->replaceWithBlank;                                               
  
    ok $a->𝗽𝗿𝗲𝘁𝘁𝘆𝗦𝘁𝗿𝗶𝗻𝗴𝗖𝗗𝗔𝗧𝗔 eq <<END;                                              
  <a>
      <b><CDATA> </CDATA></b>
  </a>
  END
  

=head3 prettyStringContent($node)

Return a readable string representing all the nodes below a node of a L<parse|/parse> tree.

     Parameter  Description
  1  $node      Start node.

B<Example:>


   {my $s = <<END;                                                                     
  <a>
    <b>
      <A/>
      <B/>
    </b>
    <c>
      <C/>
      <D/>
    </c>
  </a>
  END
  
    ok $a->𝗽𝗿𝗲𝘁𝘁𝘆𝗦𝘁𝗿𝗶𝗻𝗴𝗖𝗼𝗻𝘁𝗲𝗻𝘁 eq <<END;                                          
  <b>
    <A/>
    <B/>
  </b>
  <c>
    <C/>
    <D/>
  </c>
  END
  

=head3 prettyStringContentNumbered($node)

Return a readable string representing all the nodes below a node of a L<parse|/parse> tree with numbering added.

     Parameter  Description
  1  $node      Start node.

B<Example:>


   {my $s = <<END;                                                                
  <a>
    <b>
      <c/>
    </b>
  </a>
  END
  
    my $a = Data::Edit::Xml::new($s);                                             
  
    $a->numberTree;                                                               
  
    ok $a->𝗽𝗿𝗲𝘁𝘁𝘆𝗦𝘁𝗿𝗶𝗻𝗴𝗖𝗼𝗻𝘁𝗲𝗻𝘁𝗡𝘂𝗺𝗯𝗲𝗿𝗲𝗱 eq <<END;                                  
  <b id="2">
    <c id="3"/>
  </b>
  END
  
    ok $a->go(qw(b))->𝗽𝗿𝗲𝘁𝘁𝘆𝗦𝘁𝗿𝗶𝗻𝗴𝗖𝗼𝗻𝘁𝗲𝗻𝘁𝗡𝘂𝗺𝗯𝗲𝗿𝗲𝗱 eq <<END;                                  
  <c id="3"/>
  END
  

=head3 xmlHeader($string)

Add the standard L<Xml|https://en.wikipedia.org/wiki/XML> header to a string

     Parameter  Description
  1  $string    String to which a standard L<Xml|https://en.wikipedia.org/wiki/XML> header should be prefixed

B<Example:>


  ok 𝘅𝗺𝗹𝗛𝗲𝗮𝗱𝗲𝗿("<a/>") eq <<END;                                                  
  <?xml version="1.0" encoding="UTF-8"?>
  <a/>
  END
  

This is a static method and so should be invoked as:

  Data::Edit::Xml::xmlHeader


=head2 Html/Json

Represent the L<parse|/parse> tree using html or Json

=head3 htmlTables($node)

Return a string of html representing a L<parse|/parse> tree.

     Parameter  Description
  1  $node      Start node of parse tree

B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <task id="t1">
    <title>Title Text</title>
    <taskbody>
      <context>To add a new configuration:</context>
      <steps>
        <step><cmd>Click<b>Next</b>to go to the next page</cmd></step>
        <step><cmd>On the Ports page, complete the following fields</cmd></step>
        <step><cmd>Click to exit without saving</cmd></step>
      </steps>
      <result>
        <p>good!</p>
      </result>
    </taskbody>
  </task>
  END
  
    ok stringMd5Sum($a->𝗵𝘁𝗺𝗹𝗧𝗮𝗯𝗹𝗲𝘀 =~ s(#\w+) ()gsr) eq q(1e8555c2ea191a54361cbd8cc5baa94b);
    ok stringMd5Sum($a->jsonString)                  eq q(3afa81dc2b2a249834fbac53a1ebd5f1);
  

=head3 jsonString($node)

Return a Json representation of a parse tree

     Parameter  Description
  1  $node      Start node of parse tree

B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <task id="t1">
    <title>Title Text</title>
    <taskbody>
      <context>To add a new configuration:</context>
      <steps>
        <step><cmd>Click<b>Next</b>to go to the next page</cmd></step>
        <step><cmd>On the Ports page, complete the following fields</cmd></step>
        <step><cmd>Click to exit without saving</cmd></step>
      </steps>
      <result>
        <p>good!</p>
      </result>
    </taskbody>
  </task>
  END
  
    ok stringMd5Sum($a->htmlTables =~ s(#\w+) ()gsr) eq q(1e8555c2ea191a54361cbd8cc5baa94b);
    ok stringMd5Sum($a->𝗷𝘀𝗼𝗻𝗦𝘁𝗿𝗶𝗻𝗴)                  eq q(3afa81dc2b2a249834fbac53a1ebd5f1);
  

=head3 xmlToJson($xml, $escape)

Return a Json string representing valid L<Xml|https://en.wikipedia.org/wiki/XML> contained in a file or string, optionally double escaping escape characters.

     Parameter  Description
  1  $xml       File name or string of valid html
  2  $escape    Double escape the escaped characters if true

B<Example:>


    my $x = <<END;
  <a a="1">t1
    <b b="2" c="3"/>
  t2
  </a>
  END
  
    ok 𝘅𝗺𝗹𝗧𝗼𝗝𝘀𝗼𝗻($x) eq <<'END';
  {
     "attributes" : {
        "a" : "1"
     },
  
     "contents" : [
        {
           "tag" : "CDATA",
           "text" : "t1
  "
        },
  
        {
           "attributes" : {
              "b" : "2",
              "c" : "3"
           },
  
           "tag" : "b"
        },
  
        {
           "tag" : "CDATA",
           "text" : "
t2
"
        }
     ],
  
     "tag" : "a"
  }
  END
  
    ok 𝘅𝗺𝗹𝗧𝗼𝗝𝘀𝗼𝗻($x, 1) eq <<'END';
  {
     "attributes" : {
        "a" : "1"
     },
  
     "contents" : [
        {
           "tag" : "CDATA",
           "text" : "t1\
  "
        },
  
        {
           "attributes" : {
              "b" : "2",
              "c" : "3"
           },
  
           "tag" : "b"
        },
  
        {
           "tag" : "CDATA",
           "text" : "\
t2\
"
        }
     ],
  
     "tag" : "a"
  }
  END
  

This is a static method and so should be invoked as:

  Data::Edit::Xml::xmlToJson


=head3 jsonToXml($json)

Convert a json string representing an L<Xml|https://en.wikipedia.org/wiki/XML> parse tree into an L<Xml|https://en.wikipedia.org/wiki/XML> parse tree

     Parameter  Description
  1  $json      Json string

B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a id="a">
    <b id="b" out="out">b1
      <c id="c"/>
      b2
      <d id="d"/>
      b3
    </b>
  </a>
  END
  
    my                $json = $a->jsonString;
    ok   stringMd5Sum($json) eq q(5bb9140c7076978dfd5f600d68a82164);
    ok -p 𝗷𝘀𝗼𝗻𝗧𝗼𝗫𝗺𝗹  ($json) eq <<END;
  <a id="a">
    <b id="b" out="out">b1
      <c id="c"/>
  b2
      <d id="d"/>
  b3
    </b>
  </a>
  END
  

=head2 Dense

Print the L<parse|/parse> tree densely for reuse by computers rather than humans.

=head3 string($node)

Return a dense string representing a node of a L<parse|/parse> tree and all the nodes below it. Or use L<-s|/opString> B<$node>.

     Parameter  Description
  1  $node      Start node.

B<Example:>


    ok -p $a eq <<END;                                                                          
  <a>
    <b>
      <c id="42" match="mm"/>
    </b>
    <d>
      <e/>
    </d>
  </a>
  END
  
    ok -s $a eq '<a><b><c id="42" match="mm"/></b><d><e/></d></a>';                
  

=head3 stringAsMd5Sum($node)

Return the L<md5 sum|https://en.wikipedia.org/wiki/MD5> of the dense L<string|/string> representing a node of a L<parse|/parse> tree minus its L<id> and all the nodes below it. Or use L<-g|/opString> B<$node>. The id of the top most node is not included in the md5sum to equate parse trees that would otherwise only differ by the arbitrary root node id value.

     Parameter  Description
  1  $node      Node.

B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b>
      c
    </b>
  </a>
  END
  
    ok $a->𝘀𝘁𝗿𝗶𝗻𝗴𝗔𝘀𝗠𝗱𝟱𝗦𝘂𝗺 eq q(390bf05d8f5671cc6a4771834840d695);
    ok ((-k $a)->id       eq q(GUID-390bf05d-8f56-71cc-6a47-71834840d695));
    ok $a->id             ne (-k $a->first)->id;
  

=head3 stringQuoted($node)

Return a quoted string representing a L<parse|/parse> tree a node of a L<parse|/parse> tree and all the nodes below it. Or use L<-o|/opString> B<$node>.

     Parameter  Description
  1  $node      Start node

B<Example:>


   {my $s = <<END;                                                                     
  <a>
    <b>
      <A/>
      <B/>
    </b>
    <c>
      <C/>
      <D/>
    </c>
  </a>
  END
  
    ok $a->𝘀𝘁𝗿𝗶𝗻𝗴𝗤𝘂𝗼𝘁𝗲𝗱 eq q('<a><b><A/><B/></b><c><C/><D/></c></a>');            
  

=head3 stringReplacingIdsWithLabels($node)

Return a string representing the specified L<parse|/parse> tree with the id attribute of each node set to the L<Labels|/Labels> attached to each node.

     Parameter  Description
  1  $node      Start node.

B<Example:>


    ok $x->𝘀𝘁𝗿𝗶𝗻𝗴𝗥𝗲𝗽𝗹𝗮𝗰𝗶𝗻𝗴𝗜𝗱𝘀𝗪𝗶𝘁𝗵𝗟𝗮𝗯𝗲𝗹𝘀 eq '<a><b><c/></b></a>';                                             
  
    $b->addLabels(1..4);                                                          
  
    $c->addLabels(5..8);                                                          
  
    ok $x->𝘀𝘁𝗿𝗶𝗻𝗴𝗥𝗲𝗽𝗹𝗮𝗰𝗶𝗻𝗴𝗜𝗱𝘀𝗪𝗶𝘁𝗵𝗟𝗮𝗯𝗲𝗹𝘀 eq '<a><b id="1, 2, 3, 4"><c id="5, 6, 7, 8"/></b></a>';             
  
    my $s = $x->𝘀𝘁𝗿𝗶𝗻𝗴𝗥𝗲𝗽𝗹𝗮𝗰𝗶𝗻𝗴𝗜𝗱𝘀𝗪𝗶𝘁𝗵𝗟𝗮𝗯𝗲𝗹𝘀;                                     
  
    ok $s eq '<a><b id="1, 2, 3, 4"><c id="5, 6, 7, 8"/></b></a>';                
  

=head3 stringExtendingIdsWithLabels($node)

Return a string representing the specified L<parse|/parse> tree with the id attribute of each node extended by the L<Labels|/Labels> attached to each node.

     Parameter  Description
  1  $node      Start node.

B<Example:>


   {my $a = Data::Edit::Xml::new(<<END);                                          
  <a id="a">
    <b id="b">
      <c id="c"/>
    </b>
    <b id="B">
      <c id="C"/>
    </b>
  </a>
  END
  
    my $N = 0; $a->by(sub{$_->addLabels((-t $_).++$N)});                          
  
    ok -p (new $a->𝘀𝘁𝗿𝗶𝗻𝗴𝗘𝘅𝘁𝗲𝗻𝗱𝗶𝗻𝗴𝗜𝗱𝘀𝗪𝗶𝘁𝗵𝗟𝗮𝗯𝗲𝗹𝘀) eq <<END;                         
  <a id="a, a5">
    <b id="b, b2">
      <c id="c, c1"/>
    </b>
    <b id="B, b4">
      <c id="C, c3"/>
    </b>
  </a>
  END
  

=head3 stringContent($node)

Return a string representing all the nodes below a node of a L<parse|/parse> tree.

     Parameter  Description
  1  $node      Start node.

B<Example:>


   {my $s = <<END;                                                                     
  <a>
    <b>
      <A/>
      <B/>
    </b>
    <c>
      <C/>
      <D/>
    </c>
  </a>
  END
  
    ok $a->𝘀𝘁𝗿𝗶𝗻𝗴𝗖𝗼𝗻𝘁𝗲𝗻𝘁 eq "<b><A/><B/></b><c><C/><D/></c>";                     
  

=head3 stringContentOrText($node)

Return a string representing all the nodes below a node of a L<parse|/parse> tree or the text of the current node if it is a text node.

     Parameter  Description
  1  $node      Start node.

B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>AAAA</a>
  END
  
    ok $a->stringContent              eq q(AAAA);
    ok $a->𝘀𝘁𝗿𝗶𝗻𝗴𝗖𝗼𝗻𝘁𝗲𝗻𝘁𝗢𝗿𝗧𝗲𝘅𝘁        eq q(AAAA);
    ok $a->first__stringContent       eq q();
    ok $a->first__stringContentOrText eq q(AAAA);
  

=head3 stringNode($node)

Return a string representing the specified B<$node> showing the attributes, labels and node number.

     Parameter  Description
  1  $node      Node.

B<Example:>


    ok $x->stringReplacingIdsWithLabels eq '<a><b><c/></b></a>';                                                
  
    my $b = $x->go(q(b));                                                            
  
    $b->addLabels(1..2);                                                             
  
    $b->addLabels(3..4);                                                             
  
    ok $x->stringReplacingIdsWithLabels eq '<a><b id="1, 2, 3, 4"><c/></b></a>';                                 
  
    $b->numberTree;                                                               
  
    ok $b->𝘀𝘁𝗿𝗶𝗻𝗴𝗡𝗼𝗱𝗲 eq "b(2) 0:1 1:2 2:3 3:4";                                  
  

=head3 stringTagsAndText($node)

Return a string showing just the tags and text at and below a specified B<$node>.

     Parameter  Description
  1  $node      Node.

B<Example:>


  if (1)                                                                                 
   {my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b>
      <c>cc
        <d/>
  dd
      </c>
    </b>
    <B>
      <c>cc
        <d/>
  dd
      </c>
    </B>
  </a>
  END
  
   my $b = $a->first_b; my $B = $a->last_B;
   my $c = $b->first_c; my $C = $B->first_c;
   my $d = $c->first_d; my $D = $C->first_d;
  
   $a->setDepthProfile;
  
   ok $b->depthProfileLast eq q(3 3 3 2 1);
   ok $b->depthProfileLast eq $B->depthProfileLast;
  
  # Represent using tags and text
   $a->setRepresentationAsTagsAndText;
   is_deeply [$b->𝘀𝘁𝗿𝗶𝗻𝗴𝗧𝗮𝗴𝘀𝗔𝗻𝗱𝗧𝗲𝘅𝘁],   [qw(cc d dd c b)];
   is_deeply [$B->𝘀𝘁𝗿𝗶𝗻𝗴𝗧𝗮𝗴𝘀𝗔𝗻𝗱𝗧𝗲𝘅𝘁],   [qw(cc d dd c B)];
   ok         $b->representationLast  eq qq(cc d dd c b);
   ok         $B->representationLast  eq qq(cc d dd c B);
   ok         $c->representationLast  eq qq(cc d dd c);
   ok         $C->representationLast  eq qq(cc d dd c);
   ok dump($b->representationLast) ne dump($B->representationLast);
   is_deeply  $c->representationLast,
              $C->representationLast;
  
   my $m  = $a->matchNodesByRepresentation;
  
   my $bb = $b->representationLast;
   is_deeply $m->{$bb}, [$b];
  
   my $cc = $c->representationLast;
   is_deeply $m->{$cc}, [$c, $C];
  
  # Represent using just text
   $a->setRepresentationAsText;
   is_deeply [$b->stringText],          [qw(cc dd)];
   is_deeply [$B->stringText],          [qw(cc dd)];
   ok         $b->representationLast  eq qq(cc dd);
   ok         $B->representationLast  eq qq(cc dd);
   is_deeply  $b->representationLast,
              $B->representationLast;
   is_deeply  $c->representationLast,
              $C->representationLast;
  
   my $M  = $a->matchNodesByRepresentation;
   my $BB = $b->representationLast;
   is_deeply $M->{$BB}, [$c, $b, $C, $B];
  
   my $CC = $c->representationLast;
   is_deeply $M->{$BB}, [$c, $b, $C, $B];
  
   ok $b->representationLast eq $c->representationLast;
  }
  
  if (1)                                                                          
   {my $a = Data::Edit::Xml::new(q(<a>aaaa</a>));
    ok $a->first->isOnlyChildText;
   }
  

=head3 stringText($node)

Return a string showing just the text of the text nodes (separated by blanks) at and below a specified B<$node>.

     Parameter  Description
  1  $node      Node.

B<Example:>


  if (1)                                                                                 
   {my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b>
      <c>cc
        <d/>
  dd
      </c>
    </b>
    <B>
      <c>cc
        <d/>
  dd
      </c>
    </B>
  </a>
  END
  
   my $b = $a->first_b; my $B = $a->last_B;
   my $c = $b->first_c; my $C = $B->first_c;
   my $d = $c->first_d; my $D = $C->first_d;
  
   $a->setDepthProfile;
  
   ok $b->depthProfileLast eq q(3 3 3 2 1);
   ok $b->depthProfileLast eq $B->depthProfileLast;
  
  # Represent using tags and text
   $a->setRepresentationAsTagsAndText;
   is_deeply [$b->stringTagsAndText],   [qw(cc d dd c b)];
   is_deeply [$B->stringTagsAndText],   [qw(cc d dd c B)];
   ok         $b->representationLast  eq qq(cc d dd c b);
   ok         $B->representationLast  eq qq(cc d dd c B);
   ok         $c->representationLast  eq qq(cc d dd c);
   ok         $C->representationLast  eq qq(cc d dd c);
   ok dump($b->representationLast) ne dump($B->representationLast);
   is_deeply  $c->representationLast,
              $C->representationLast;
  
   my $m  = $a->matchNodesByRepresentation;
  
   my $bb = $b->representationLast;
   is_deeply $m->{$bb}, [$b];
  
   my $cc = $c->representationLast;
   is_deeply $m->{$cc}, [$c, $C];
  
  # Represent using just text
   $a->setRepresentationAsText;
   is_deeply [$b->𝘀𝘁𝗿𝗶𝗻𝗴𝗧𝗲𝘅𝘁],          [qw(cc dd)];
   is_deeply [$B->𝘀𝘁𝗿𝗶𝗻𝗴𝗧𝗲𝘅𝘁],          [qw(cc dd)];
   ok         $b->representationLast  eq qq(cc dd);
   ok         $B->representationLast  eq qq(cc dd);
   is_deeply  $b->representationLast,
              $B->representationLast;
   is_deeply  $c->representationLast,
              $C->representationLast;
  
   my $M  = $a->matchNodesByRepresentation;
   my $BB = $b->representationLast;
   is_deeply $M->{$BB}, [$c, $b, $C, $B];
  
   my $CC = $c->representationLast;
   is_deeply $M->{$BB}, [$c, $b, $C, $B];
  
   ok $b->representationLast eq $c->representationLast;
  }
  
  if (1)                                                                          
   {my $a = Data::Edit::Xml::new(q(<a>aaaa</a>));
    ok $a->first->isOnlyChildText;
   }
  

=head3 setRepresentationAsTagsAndText($tree)

Sets the L<representationLast|/representationLast> for every node in the specified B<$tree> via L<stringTagsAndText|/stringTagsAndText>.

     Parameter  Description
  1  $tree      Tree of nodes.

B<Example:>


  if (1)                                                                                 
   {my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b>
      <c>cc
        <d/>
  dd
      </c>
    </b>
    <B>
      <c>cc
        <d/>
  dd
      </c>
    </B>
  </a>
  END
  
   my $b = $a->first_b; my $B = $a->last_B;
   my $c = $b->first_c; my $C = $B->first_c;
   my $d = $c->first_d; my $D = $C->first_d;
  
   $a->setDepthProfile;
  
   ok $b->depthProfileLast eq q(3 3 3 2 1);
   ok $b->depthProfileLast eq $B->depthProfileLast;
  
  # Represent using tags and text
   $a->𝘀𝗲𝘁𝗥𝗲𝗽𝗿𝗲𝘀𝗲𝗻𝘁𝗮𝘁𝗶𝗼𝗻𝗔𝘀𝗧𝗮𝗴𝘀𝗔𝗻𝗱𝗧𝗲𝘅𝘁;
   is_deeply [$b->stringTagsAndText],   [qw(cc d dd c b)];
   is_deeply [$B->stringTagsAndText],   [qw(cc d dd c B)];
   ok         $b->representationLast  eq qq(cc d dd c b);
   ok         $B->representationLast  eq qq(cc d dd c B);
   ok         $c->representationLast  eq qq(cc d dd c);
   ok         $C->representationLast  eq qq(cc d dd c);
   ok dump($b->representationLast) ne dump($B->representationLast);
   is_deeply  $c->representationLast,
              $C->representationLast;
  
   my $m  = $a->matchNodesByRepresentation;
  
   my $bb = $b->representationLast;
   is_deeply $m->{$bb}, [$b];
  
   my $cc = $c->representationLast;
   is_deeply $m->{$cc}, [$c, $C];
  
  # Represent using just text
   $a->setRepresentationAsText;
   is_deeply [$b->stringText],          [qw(cc dd)];
   is_deeply [$B->stringText],          [qw(cc dd)];
   ok         $b->representationLast  eq qq(cc dd);
   ok         $B->representationLast  eq qq(cc dd);
   is_deeply  $b->representationLast,
              $B->representationLast;
   is_deeply  $c->representationLast,
              $C->representationLast;
  
   my $M  = $a->matchNodesByRepresentation;
   my $BB = $b->representationLast;
   is_deeply $M->{$BB}, [$c, $b, $C, $B];
  
   my $CC = $c->representationLast;
   is_deeply $M->{$BB}, [$c, $b, $C, $B];
  
   ok $b->representationLast eq $c->representationLast;
  }
  
  if (1)                                                                          
   {my $a = Data::Edit::Xml::new(q(<a>aaaa</a>));
    ok $a->first->isOnlyChildText;
   }
  

=head3 setRepresentationAsText($tree)

Sets the L<representationLast|/representationLast> for every node in the specified B<$tree> via L<stringText|/stringText>.

     Parameter  Description
  1  $tree      Tree of nodes.

B<Example:>


  if (1)                                                                                 
   {my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b>
      <c>cc
        <d/>
  dd
      </c>
    </b>
    <B>
      <c>cc
        <d/>
  dd
      </c>
    </B>
  </a>
  END
  
   my $b = $a->first_b; my $B = $a->last_B;
   my $c = $b->first_c; my $C = $B->first_c;
   my $d = $c->first_d; my $D = $C->first_d;
  
   $a->setDepthProfile;
  
   ok $b->depthProfileLast eq q(3 3 3 2 1);
   ok $b->depthProfileLast eq $B->depthProfileLast;
  
  # Represent using tags and text
   $a->setRepresentationAsTagsAndText;
   is_deeply [$b->stringTagsAndText],   [qw(cc d dd c b)];
   is_deeply [$B->stringTagsAndText],   [qw(cc d dd c B)];
   ok         $b->representationLast  eq qq(cc d dd c b);
   ok         $B->representationLast  eq qq(cc d dd c B);
   ok         $c->representationLast  eq qq(cc d dd c);
   ok         $C->representationLast  eq qq(cc d dd c);
   ok dump($b->representationLast) ne dump($B->representationLast);
   is_deeply  $c->representationLast,
              $C->representationLast;
  
   my $m  = $a->matchNodesByRepresentation;
  
   my $bb = $b->representationLast;
   is_deeply $m->{$bb}, [$b];
  
   my $cc = $c->representationLast;
   is_deeply $m->{$cc}, [$c, $C];
  
  # Represent using just text
   $a->𝘀𝗲𝘁𝗥𝗲𝗽𝗿𝗲𝘀𝗲𝗻𝘁𝗮𝘁𝗶𝗼𝗻𝗔𝘀𝗧𝗲𝘅𝘁;
   is_deeply [$b->stringText],          [qw(cc dd)];
   is_deeply [$B->stringText],          [qw(cc dd)];
   ok         $b->representationLast  eq qq(cc dd);
   ok         $B->representationLast  eq qq(cc dd);
   is_deeply  $b->representationLast,
              $B->representationLast;
   is_deeply  $c->representationLast,
              $C->representationLast;
  
   my $M  = $a->matchNodesByRepresentation;
   my $BB = $b->representationLast;
   is_deeply $M->{$BB}, [$c, $b, $C, $B];
  
   my $CC = $c->representationLast;
   is_deeply $M->{$BB}, [$c, $b, $C, $B];
  
   ok $b->representationLast eq $c->representationLast;
  }
  
  if (1)                                                                          
   {my $a = Data::Edit::Xml::new(q(<a>aaaa</a>));
    ok $a->first->isOnlyChildText;
   }
  

=head3 matchNodesByRepresentation($tree)

Creates a hash of arrays of nodes that have the same representation in the specified B<$tree>. Set L<representation|/representationLast> for each node in the tree before calling this method.

     Parameter  Description
  1  $tree      Tree to examine

B<Example:>


  if (1)                                                                                 
   {my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b>
      <c>cc
        <d/>
  dd
      </c>
    </b>
    <B>
      <c>cc
        <d/>
  dd
      </c>
    </B>
  </a>
  END
  
   my $b = $a->first_b; my $B = $a->last_B;
   my $c = $b->first_c; my $C = $B->first_c;
   my $d = $c->first_d; my $D = $C->first_d;
  
   $a->setDepthProfile;
  
   ok $b->depthProfileLast eq q(3 3 3 2 1);
   ok $b->depthProfileLast eq $B->depthProfileLast;
  
  # Represent using tags and text
   $a->setRepresentationAsTagsAndText;
   is_deeply [$b->stringTagsAndText],   [qw(cc d dd c b)];
   is_deeply [$B->stringTagsAndText],   [qw(cc d dd c B)];
   ok         $b->representationLast  eq qq(cc d dd c b);
   ok         $B->representationLast  eq qq(cc d dd c B);
   ok         $c->representationLast  eq qq(cc d dd c);
   ok         $C->representationLast  eq qq(cc d dd c);
   ok dump($b->representationLast) ne dump($B->representationLast);
   is_deeply  $c->representationLast,
              $C->representationLast;
  
   my $m  = $a->𝗺𝗮𝘁𝗰𝗵𝗡𝗼𝗱𝗲𝘀𝗕𝘆𝗥𝗲𝗽𝗿𝗲𝘀𝗲𝗻𝘁𝗮𝘁𝗶𝗼𝗻;
  
   my $bb = $b->representationLast;
   is_deeply $m->{$bb}, [$b];
  
   my $cc = $c->representationLast;
   is_deeply $m->{$cc}, [$c, $C];
  
  # Represent using just text
   $a->setRepresentationAsText;
   is_deeply [$b->stringText],          [qw(cc dd)];
   is_deeply [$B->stringText],          [qw(cc dd)];
   ok         $b->representationLast  eq qq(cc dd);
   ok         $B->representationLast  eq qq(cc dd);
   is_deeply  $b->representationLast,
              $B->representationLast;
   is_deeply  $c->representationLast,
              $C->representationLast;
  
   my $M  = $a->𝗺𝗮𝘁𝗰𝗵𝗡𝗼𝗱𝗲𝘀𝗕𝘆𝗥𝗲𝗽𝗿𝗲𝘀𝗲𝗻𝘁𝗮𝘁𝗶𝗼𝗻;
   my $BB = $b->representationLast;
   is_deeply $M->{$BB}, [$c, $b, $C, $B];
  
   my $CC = $c->representationLast;
   is_deeply $M->{$BB}, [$c, $b, $C, $B];
  
   ok $b->representationLast eq $c->representationLast;
  }
  
  if (1)                                                                          
   {my $a = Data::Edit::Xml::new(q(<a>aaaa</a>));
    ok $a->first->isOnlyChildText;
   }
  

=head2 Conditions

Print a subset of the L<parse|/parse> tree determined by the conditions attached to it.

=head3 stringWithConditions($node, @conditions)

Return a string representing the specified B<$node> of a L<parse|/parse> tree and all the nodes below it subject to conditions to select or reject some nodes.

     Parameter    Description
  1  $node        Start node
  2  @conditions  Conditions to be regarded as in effect.

B<Example:>


    my $a = Data::Edit::Xml::new(<<END);                                          
  <a>
    <b>
      <c/>
      <d/>
    </b>
  </a>
  END
  
    my $b = $a >= 'b';                                                            
  
    my ($c, $d) = $b->contents;                                                   
  
    $b->addConditions(qw(bb BB));                                                       
  
    $c->addConditions(qw(cc CC));                                                                                      
  
    ok $a->𝘀𝘁𝗿𝗶𝗻𝗴𝗪𝗶𝘁𝗵𝗖𝗼𝗻𝗱𝗶𝘁𝗶𝗼𝗻𝘀         eq '<a><b><c/><d/></b></a>';              
  
    ok $a->𝘀𝘁𝗿𝗶𝗻𝗴𝗪𝗶𝘁𝗵𝗖𝗼𝗻𝗱𝗶𝘁𝗶𝗼𝗻𝘀(qw(bb)) eq '<a><b><d/></b></a>';                  
  
    ok $a->𝘀𝘁𝗿𝗶𝗻𝗴𝗪𝗶𝘁𝗵𝗖𝗼𝗻𝗱𝗶𝘁𝗶𝗼𝗻𝘀(qw(cc)) eq '<a/>';                                
  

=head3 condition($node, $condition, @context)

Return the B<$node> if it has the specified B<$condition> and is in the optional B<@context>, else return B<undef>

     Parameter   Description
  1  $node       Node
  2  $condition  Condition to check
  3  @context    Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    $b->addConditions(qw(bb BB));                                                       
  
    $c->addConditions(qw(cc CC));                                                                                      
  
    ok  $c->𝗰𝗼𝗻𝗱𝗶𝘁𝗶𝗼𝗻(q(cc));                                                     
  
    ok !$c->𝗰𝗼𝗻𝗱𝗶𝘁𝗶𝗼𝗻(q(dd));                                                     
  
    ok  $c->𝗰𝗼𝗻𝗱𝗶𝘁𝗶𝗼𝗻(q(cc), qw(c b a));                                          
  

=head3 anyCondition($node, @conditions)

Return the B<$node> if it has any of the specified B<@conditions>, else return B<undef>

     Parameter    Description
  1  $node        Node
  2  @conditions  Conditions to check

B<Example:>


    $b->addConditions(qw(bb BB));                                                       
  
    $c->addConditions(qw(cc CC));                                                                                      
  
    ok  $b->𝗮𝗻𝘆𝗖𝗼𝗻𝗱𝗶𝘁𝗶𝗼𝗻(qw(bb cc));                                              
  
    ok !$b->𝗮𝗻𝘆𝗖𝗼𝗻𝗱𝗶𝘁𝗶𝗼𝗻(qw(cc CC));                                              
  

=head3 allConditions($node, @conditions)

Return the B<$node> if it has all of the specified B<@conditions>, else return B<undef>

     Parameter    Description
  1  $node        Node
  2  @conditions  Conditions to check

B<Example:>


    $b->addConditions(qw(bb BB));                                                       
  
    $c->addConditions(qw(cc CC));                                                                                      
  
    ok  $b->𝗮𝗹𝗹𝗖𝗼𝗻𝗱𝗶𝘁𝗶𝗼𝗻𝘀(qw(bb BB));                                             
  
    ok !$b->𝗮𝗹𝗹𝗖𝗼𝗻𝗱𝗶𝘁𝗶𝗼𝗻𝘀(qw(bb cc));                                             
  

=head3 addConditions($node, @conditions)

Given a B<$node> add the specified B<@conditions> and return the node.

     Parameter    Description
  1  $node        Node
  2  @conditions  Conditions to add.

B<Example:>


    $b->𝗮𝗱𝗱𝗖𝗼𝗻𝗱𝗶𝘁𝗶𝗼𝗻𝘀(qw(bb BB));                                                       
  
    ok join(' ', $b->listConditions) eq 'BB bb';                                        
  

=head3 deleteConditions($node, @conditions)

Given a B<$node> delete any B<@conditions> applied to the $node and return the $node.

     Parameter    Description
  1  $node        Node
  2  @conditions  Conditions to add.

B<Example:>


    ok join(' ', $b->listConditions) eq 'BB bb';                                        
  
    $b->𝗱𝗲𝗹𝗲𝘁𝗲𝗖𝗼𝗻𝗱𝗶𝘁𝗶𝗼𝗻𝘀(qw(BB));                                                 
  
    ok join(' ', $b->listConditions) eq 'bb';                                     
  

=head3 listConditions($node)

Return a list of conditions applied to a B<$node>.

     Parameter  Description
  1  $node      Node.

B<Example:>


    $b->addConditions(qw(bb BB));                                                       
  
    ok join(' ', $b->𝗹𝗶𝘀𝘁𝗖𝗼𝗻𝗱𝗶𝘁𝗶𝗼𝗻𝘀) eq 'BB bb';                                        
  

=head1 Attributes

Get or set the attributes of nodes in the L<parse|/parse> tree. L<Well Known Attributes|/Well Known Attributes>  can be set directly via L<lvalue method|http://perldoc.perl.org/perlsub.html#Lvalue-subroutines> B<sub>s. To set or get the values of other attributes use L<Get or Set Attributes|/Get or Set Attributes>. To delete or rename attributes see: L<Other Operations on Attributes|/Other Operations on Attributes>.

=head2 Well Known Attributes

Get or set these node attributes via L<lvalue method|http://perldoc.perl.org/perlsub.html#Lvalue-subroutines> B<sub>s as in:

  $x->href = "#ref";

=head2 Get or Set Attributes

Get or set the attributes of nodes.

=head3 attr($node, $attribute)

Return the value of an attribute of the current node as an L<lvalue method|http://perldoc.perl.org/perlsub.html#Lvalue-subroutines> B<sub>.

     Parameter   Description
  1  $node       Node in parse tree
  2  $attribute  Attribute name.

B<Example:>


    my $x = Data::Edit::Xml::new(my $s = <<END);                                  
  <a number="1"/>
  END
  
    ok $x->𝗮𝘁𝘁𝗿(qq(number)) == 1;                                                 
  
       $x->𝗮𝘁𝘁𝗿(qq(number))  = 2;                                                 
  
    ok $x->𝗮𝘁𝘁𝗿(qq(number)) == 2;                                                 
  
    ok -s $x eq '<a number="2"/>';                                                
  

=head3 attrX($node, $attribute)

Return the value of the specified B<$attribute> of the specified B<$node> or B<q()> if the B<$node> does not have such an attribute.

     Parameter   Description
  1  $node       Node in parse tree
  2  $attribute  Attribute name.

B<Example:>


  if (1)                                                                          
   {my $a = Data::Edit::Xml::new(q(<a><b name="bb"/></a>));
  
    my  $b = $a->first;
    ok  $b->attrX_name eq q(bb);
    ok !$b->attrX_bbb;
   }
  

=head3 set($node, %values)

Set the values of some attributes in a node and return the node. Identical in effect to L<setAttr|/setAttr>.

     Parameter  Description
  1  $node      Node in parse tree
  2  %values    (attribute name=>new value)*

B<Example:>


    ok q(<a a="1" b="1" id="aa"/>) eq -s $a;                                      
  
    $a->𝘀𝗲𝘁(a=>11, b=>undef, c=>3, d=>4, e=>5);                                   
  
   }                                                                              
  

=head3 addAttr($node, %values)

Check the specified B<$node> for the specified B<%attributes> and add any that are not already set.  Returns the current node.

     Parameter  Description
  1  $node      Node in parse tree
  2  %values    (attribute name=>new value)*

B<Example:>


    my $a = Data::Edit::Xml::new(q(<a id="a"/>));
  
    $a->𝗮𝗱𝗱𝗔𝘁𝘁𝗿(id=>"b", class=>"c");
  
    ok -p $a eq qq(<a class="c" id="a"/>
);
  

=head3 setAttr($node, %values)

Set the values of some attributes in a node and return the node. Identical in effect to L<set|/set>.

     Parameter  Description
  1  $node      Node in parse tree
  2  %values    (attribute name=>new value)*

B<Example:>


    ok -s $x eq '<a number="2"/>';                                                 
  
    $x->𝘀𝗲𝘁𝗔𝘁𝘁𝗿(first=>1, second=>2, last=>undef);                                
  
    ok -s $x eq '<a first="1" number="2" second="2"/>';                               
  

=head2 Other Operations on Attributes

Perform operations other than get or set on the attributes of a node

=head3 attrs($node, @attributes)

Return the values of the specified attributes of the current node as a list

     Parameter    Description
  1  $node        Node in parse tree
  2  @attributes  Attribute names.

B<Example:>


    ok -s $x eq '<a first="1" number="2" second="2"/>';                               
  
    is_deeply [$x->𝗮𝘁𝘁𝗿𝘀(qw(third second first ))], [undef, 2, 1];                
  

=head3 attrCount($node, @exclude)

Return the number of attributes in the specified B<$node>, optionally ignoring the specified names from the count.

     Parameter  Description
  1  $node      Node in parse tree
  2  @exclude   Optional attribute names to exclude from the count.

B<Example:>


    ok -s $x eq '<a first="1" number="2" second="2"/>';                               
  
    ok $x->𝗮𝘁𝘁𝗿𝗖𝗼𝘂𝗻𝘁 == 3;                                                        
  
    ok $x->𝗮𝘁𝘁𝗿𝗖𝗼𝘂𝗻𝘁(qw(first second third)) == 1;                                
  

=head3 getAttrs($node)

Return a sorted list of all the attributes on the specified B<$node>.

     Parameter  Description
  1  $node      Node in parse tree.

B<Example:>


    ok -s $x eq '<a first="1" number="2" second="2"/>';                               
  
    is_deeply [$x->𝗴𝗲𝘁𝗔𝘁𝘁𝗿𝘀], [qw(first number second)];                          
  

=head3 deleteAttr($node, $attr, $value)

Delete the named attribute in the specified B<$node>, optionally check its value first, returning the value of the attribute or B<undef> if the attribute does not exist on this node.

     Parameter  Description
  1  $node      Node
  2  $attr      Attribute name
  3  $value     Optional attribute value to check first.

B<Example:>


    ok -s $x eq '<a delete="me" number="2"/>';                                    
  
    $x->𝗱𝗲𝗹𝗲𝘁𝗲𝗔𝘁𝘁𝗿(qq(delete));                                                   
  
    ok -s $x eq '<a number="2"/>';                                                 
  

=head3 deleteAttrs($node, @attrs)

Delete the specified attributes of the specified B<$node> without checking their values and return the node.

     Parameter  Description
  1  $node      Node
  2  @attrs     Names of the attributes to delete

B<Example:>


    ok -s $x eq '<a first="1" number="2" second="2"/>';                               
  
    $x->𝗱𝗲𝗹𝗲𝘁𝗲𝗔𝘁𝘁𝗿𝘀(qw(first second third number));                               
  
    ok -s $x eq '<a/>';                                                           
  

=head3 deleteAttrsInTree($node, @attrs)

Delete the specified attributes of the specified B<$node> and all the nodes under it and return the specified B<$node>.

     Parameter  Description
  1  $node      Node
  2  @attrs     Names of the attributes to delete

B<Example:>


    ok -p $a eq <<END;                                                           
  <a class="2" id="0">
    <b class="1" id="1">
      <c class="0" id="0">
        <d class="1" id="1"/>
        <e class="2" id="0"/>
        <e class="0" id="1"/>
        <f class="1" id="0"/>
        <f class="2" id="1"/>
      </c>
    </b>
  </a>
  END
  
    $a->deleteAttrsInTree_class;                                                  
  
    ok -p $a eq <<END                                                             
  <a id="0">
    <b id="1">
      <c id="0">
        <d id="1"/>
        <e id="0"/>
        <e id="1"/>
        <f id="0"/>
        <f id="1"/>
      </c>
    </b>
  </a>
  END
  

=head3 attrsNone($node, @context)

Check that the specified B<$node> has no attributes. Return the specified $node if no attributes were found else B<undef>. Invented by MfM.

     Parameter  Description
  1  $node      Node
  2  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(q(<a><b/><c id="cc"/></a>));
    ok $a->first__attrsNone;
    ok !$a->last__attrsNone;
  

=head3 deleteAttrValueAtInTree($tree, $attribute, $value, @context)

Delete all instances of the specified B<$attribute> with the specified B<$value> in the specified B<@context> in the specified B<$tree> and return the modified B<$tree>. An undefined B<$value> will cause the attribute to be deleted without first confirming its value. An empty context will remove the attribute from every node in the B<$tree>.

     Parameter   Description
  1  $tree       Tree
  2  $attribute  Attribute name
  3  $value      Attribute value or B<undef> for all values
  4  @context    Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
   <b>
     <c id="c"/>
     <c id="d"/>
   </b>
   <d>
     <c id="c"/>
     <c id="d"/>
   </d>
  </a>
  END
  
    $a->deleteAttrValueAtInTree_id_c_c_b;
    ok -p $a eq <<END;
  <a>
    <b>
      <c/>
      <c id="d"/>
    </b>
    <d>
      <c id="c"/>
      <c id="d"/>
    </d>
  </a>
  END
  

=head3 renameAttr($node, $old, $new, @context)

Rename attribute B<$old> to B<$new> in the specified B<$node> with optional context B<@context> regardless of whether attribute B<$new> already exists or not and return the B<$node>. To prevent inadvertent changes to an existing attribute use L<changeAttr|/changeAttr>.

     Parameter  Description
  1  $node      Node
  2  $old       Existing attribute name
  3  $new       New attribute name
  4  @context   Optional context.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    ok $x->printAttributes eq qq( no="1" word="first");                            
  
    $x->𝗿𝗲𝗻𝗮𝗺𝗲𝗔𝘁𝘁𝗿(qw(no number));                                                
  
    ok $x->printAttributes eq qq( number="1" word="first");                        
  

=head3 renameAttrXtr($node, @attr)

Rename the attributes B<@attr> as far as possible to xtrc or xtrf.  Returns an array of the attributes that could not be so renamed.

     Parameter  Description
  1  $node      Node
  2  @attr      Attributes to rename if they exist

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(q(<a a="1" b="2" c="3" d="4"/>));
    my @a = $a->renameAttrXtr_a_b_c;
    ok      -A $a eq q(a c="3" d="4" xtrc="1" xtrf="2");
    is_deeply [@a], [qw(c)];
  
    my $b = Data::Edit::Xml::new(q(<b a="1" b="2" c="3"  d="4" xtrf="5"/>));
    my @b = $b->renameAttrXtr_a_b_c;
    ok      -A $b eq q(b b="2" c="3" d="4" xtrc="1" xtrf="5");
    is_deeply [@b], [qw(b c)];
  

=head3 changeAttr($node, $old, $new, @context)

Rename attribute B<$old> to B<$new> in the specified B<$node> with optional context B<@context> unless attribute B<$new> is already set and return the B<$node>. To make changes regardless of whether the new attribute already exists use L<renameAttr|/renameAttr>.

     Parameter  Description
  1  $node      Node
  2  $old       Existing attribute name
  3  $new       New attribute name
  4  @context   Optional context.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    ok $x->printAttributes eq qq( number="1" word="first");                        
  
    $x->𝗰𝗵𝗮𝗻𝗴𝗲𝗔𝘁𝘁𝗿(qw(number word));                                              
  
    ok $x->printAttributes eq qq( number="1" word="first");                        
  

=head3 changeOrDeleteAttr($node, $old, $new, @context)

Rename attribute B<$old> to B<$new> in the specified B<$node> in the optional B<@context> unless attribute B<$new> is already set in which case delete attribute B<$old>. Return B<$node> regardless of what action was taken. To make changes regardless of whether the new attribute already exists use L<renameAttr|/renameAttr>.

     Parameter  Description
  1  $node      Node
  2  $old       Existing attribute name
  3  $new       New attribute name
  4  @context   Optional context.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a a="1"/>
  END
  
    $a->changeOrDeleteAttr_a_b;
  
    ok -p $a eq <<END;
  <a b="1"/>
  END
  
    my $a = Data::Edit::Xml::new(<<END);
  <a a="1" b="2"/>
  END
  
    $a->changeOrDeleteAttr_a_b;
  
    ok -p $a eq <<END;
  <a b="2"/>
  END
  

=head3 renameAttrValue($node, $old, $oldValue, $new, $newValue, @context)

Rename attribute B<$old> to B<$new> with new value B<$newValue> in the specified B<$node> in the optional B<@context> regardless of whether attribute B<$new> already exists or not as long as the attribute B<$old> has the value B<$oldValue>. Return the B<$node> regardless of what changes were made. To prevent inadvertent changes to existing attributes use L<changeAttrValue|/changeAttrValue>.

     Parameter  Description
  1  $node      Node
  2  $old       Existing attribute name
  3  $oldValue  Existing attribute value
  4  $new       New attribute name
  5  $newValue  New attribute value
  6  @context   Optional context.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    ok $x->printAttributes eq qq( number="1" word="first");                        
  
    $x->𝗿𝗲𝗻𝗮𝗺𝗲𝗔𝘁𝘁𝗿𝗩𝗮𝗹𝘂𝗲(qw(number 1 numeral I));                                  
  
    ok $x->printAttributes eq qq( numeral="I" word="first");                       
  

=head3 changeAttrValue($node, $old, $oldValue, $new, $newValue, @context)

Rename attribute B<$old> to B<$new> with new value B<$newValue> on the specified B<$node> in the optional B<@context> unless attribute B<$new> is already set or the value of the B<$old> attribute is not B<$oldValue>. Return the B<$node> regardless of what changes were made.  To make changes regardless of whether the new attribute already exists use L<renameAttrValue|/renameAttrValue>.

     Parameter  Description
  1  $node      Node
  2  $old       Existing attribute name
  3  $oldValue  Existing attribute value
  4  $new       New attribute name
  5  $newValue  New attribute value
  6  @context   Optional context.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    ok $x->printAttributes eq qq( numeral="I" word="first");                       
  
    $x->𝗰𝗵𝗮𝗻𝗴𝗲𝗔𝘁𝘁𝗿𝗩𝗮𝗹𝘂𝗲(qw(word second greek mono));                              
  
    ok $x->printAttributes eq qq( numeral="I" word="first");                      
  
    $x->𝗰𝗵𝗮𝗻𝗴𝗲𝗔𝘁𝘁𝗿𝗩𝗮𝗹𝘂𝗲(qw(word first greek mono));                               
  
    ok $x->printAttributes eq qq( greek="mono" numeral="I");                      
  

=head3 changeAttributeValue($node, $attribute, $sub, @context)

Apply a sub to the value of an attribute of the specified B<$node>.  The value to be changed is supplied and returned in: L<$_>.

     Parameter   Description
  1  $node       Node
  2  $attribute  Attribute name
  3  $sub        Change sub
  4  @context    Optional context.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


   {my $a = Data::Edit::Xml::new(<<END);                                          
  <a aa="abc"/>
  END
  
    $a->𝗰𝗵𝗮𝗻𝗴𝗲𝗔𝘁𝘁𝗿𝗶𝗯𝘂𝘁𝗲𝗩𝗮𝗹𝘂𝗲(q(aa), sub{s(b) (B)});                               
  
    ok -p $a eq <<END;                                                            
  <a aa="aBc"/>
  END
  

=head3 changeOrDeleteAttrValue($node, $old, $oldValue, $new, $newValue, @context)

Rename attribute B<$old> to B<$new> with new value B<$newValue> on the specified B<$node> in the optional B<@context> unless attribute B<$new> is already set or the value of the B<$old> attribute is not B<$oldValue> in which cases the B<$old> attribute is deleted. Return the B<$node> regardless of any changes made.  To make changes regardless of whether the new attribute already exists use L<renameAttrValue|/renameAttrValue>.

     Parameter  Description
  1  $node      Node
  2  $old       Existing attribute name
  3  $oldValue  Existing attribute value
  4  $new       New attribute name
  5  $newValue  New attribute value
  6  @context   Optional context.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a a="1"/>
  END
  
    $a->changeOrDeleteAttrValue_a_0_b_1;
  
    ok -p $a eq <<END;
  <a a="1"/>
  END
  
    $a->changeOrDeleteAttrValue_a_1_b_3;
  
    ok -p $a eq <<END;
  <a b="3"/>
  END
  
    my $a = Data::Edit::Xml::new(<<END);
  <a a="1" b="2"/>
  END
  
    $a->changeOrDeleteAttrValue_a_0_b_2;
    ok -p $a eq <<END;
  <a b="2"/>
  END
  
    my $a = Data::Edit::Xml::new(<<END);
  <a a="1"/>
  END
  
    $a->changeOrDeleteAttrValue_a_1_b_3;
  
    ok -p $a eq <<END;
  <a b="3"/>
  END
  

=head3 copyAttrs($source, $target, @attr)

Copy all the attributes of the source node to the target node, or, just the named attributes if the optional list of attributes to copy is supplied, overwriting any existing attributes in the target node and return the source node.

     Parameter  Description
  1  $source    Source node
  2  $target    Target node
  3  @attr      Optional list of attributes to copy

B<Example:>


   {my $x = Data::Edit::Xml::new(<<END);                                           
  <x>
    <a a="1" b="2"/>
    <b b="3" c="4"/>
    <c/>
  </x>
  END
  
    my ($a, $b, $c) = $x->contents;                                                
  
    $a->𝗰𝗼𝗽𝘆𝗔𝘁𝘁𝗿𝘀($b, qw(aa bb));                                                 
  
    ok <<END eq -p $x;                                                            
  <x>
    <a a="1" b="2"/>
    <b b="3" c="4"/>
    <c/>
  </x>
  END
  
    $a->𝗰𝗼𝗽𝘆𝗔𝘁𝘁𝗿𝘀($b);                                                            
  
    ok <<END eq -p $x;                                                            
  <x>
    <a a="1" b="2"/>
    <b a="1" b="2" c="4"/>
    <c/>
  </x>
  END
  

=head3 copyAttrsFromParent($node, @attr)

Copy all the attributes from the parent (if there is one) of the current B<$node> to $node  or just the named B<@attributes> and return $node. If $node is the top of the parse tree then return B<undef> as it does not have a parent.

     Parameter  Description
  1  $node      Node
  2  @attr      Attributes to copy from parent

B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a id="a" a="a">
    <b id="b" b="b"/>
  </a>
  END
    my $b = $a->first;
    $b->copyAttrsFromParent_id;
    ok <<END eq -p $a;
  <a a="a" id="a">
    <b b="b" id="a"/>
  </a>
  END
  
    $b->𝗰𝗼𝗽𝘆𝗔𝘁𝘁𝗿𝘀𝗙𝗿𝗼𝗺𝗣𝗮𝗿𝗲𝗻𝘁;
  
    ok <<END eq -p $a;
  <a a="a" id="a">
    <b a="a" b="b" id="a"/>
  </a>
  END
  

=head3 copyAttrsToParent($node, @attr)

Copy all the attributes of the specified B<$node> to its parent (if there is one) or just the named B<@attributes> and return $node. If $node is the top of the parse tree then return B<undef> as it does not have a parent.

     Parameter  Description
  1  $node      Node
  2  @attr      Attributes to copy from parent

B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a a="a">
    <b id="b" b="b"/>
  </a>
  END
    my $b = $a->first;
    $b->copyAttrsToParent_id;
    ok <<END eq -p $a;
  <a a="a" id="b">
    <b b="b" id="b"/>
  </a>
  END
  
    $b->𝗰𝗼𝗽𝘆𝗔𝘁𝘁𝗿𝘀𝗧𝗼𝗣𝗮𝗿𝗲𝗻𝘁;
  
    ok <<END eq -p $a;
  <a a="a" b="b" id="b">
    <b b="b" id="b"/>
  </a>
  END
  

=head3 copyNewAttrs($source, $target, @attr)

Copy all the attributes of the source node to the target node, or, just the named attributes if the optional list of attributes to copy is supplied, without overwriting any existing attributes in the target node and return the source node.

     Parameter  Description
  1  $source    Source node
  2  $target    Target node
  3  @attr      Optional list of attributes to copy

B<Example:>


   {my $x = Data::Edit::Xml::new(<<END);                                           
  <x>
    <a a="1" b="2"/>
    <b b="3" c="4"/>
    <c/>
  </x>
  END
  
    my ($a, $b, $c) = $x->contents;                                                
  
    $a->𝗰𝗼𝗽𝘆𝗡𝗲𝘄𝗔𝘁𝘁𝗿𝘀($b, qw(aa bb));                                              
  
    ok <<END eq -p $x;                                                            
  <x>
    <a a="1" b="2"/>
    <b b="3" c="4"/>
    <c/>
  </x>
  END
  
    $a->𝗰𝗼𝗽𝘆𝗡𝗲𝘄𝗔𝘁𝘁𝗿𝘀($b);                                                         
  
    ok <<END eq -p $x;                                                            
  <x>
    <a a="1" b="2"/>
    <b a="1" b="3" c="4"/>
    <c/>
  </x>
  END
  

=head3 moveAttrs($source, $target, @attr)

Move all the attributes of the source node to the target node, or, just the named attributes if the optional list of attributes to move is supplied, overwriting any existing attributes in the target node and return the source node.

     Parameter  Description
  1  $source    Source node
  2  $target    Target node
  3  @attr      Attributes to move

B<Example:>


   {my $x = Data::Edit::Xml::new(<<END);                                           
  <x>
    <a a="1" b="2"/>
    <b b="3" c="4"/>
    <c/>
  </x>
  END
  
    my ($a, $b, $c) = $x->contents;                                                
  
    $a->𝗺𝗼𝘃𝗲𝗔𝘁𝘁𝗿𝘀($c, qw(aa bb));                                                 
  
    ok <<END eq -p $x;                                                            
  <x>
    <a a="1" b="2"/>
    <b a="1" b="2" c="4"/>
    <c/>
  </x>
  END
  
    $b->𝗺𝗼𝘃𝗲𝗔𝘁𝘁𝗿𝘀($c);                                                            
  
    ok <<END eq -p $x;                                                            
  <x>
    <a a="1" b="2"/>
    <b/>
    <c a="1" b="2" c="4"/>
  </x>
  END
  

=head3 moveNewAttrs($source, $target, @attr)

Move all the attributes of the source node to the target node, or, just the named attributes if the optional list of attributes to copy is supplied, without overwriting any existing attributes in the target node and return the source node.

     Parameter  Description
  1  $source    Source node
  2  $target    Target node
  3  @attr      Optional list of attributes to move

B<Example:>


   {my $x = Data::Edit::Xml::new(<<END);                                           
  <x>
    <a a="1" b="2"/>
    <b b="3" c="4"/>
    <c/>
  </x>
  END
  
    my ($a, $b, $c) = $x->contents;                                                
  
    $b->𝗺𝗼𝘃𝗲𝗡𝗲𝘄𝗔𝘁𝘁𝗿𝘀($c, qw(aa bb));                                              
  
    ok <<END eq -p $x;                                                            
  <x>
    <a a="1" b="2"/>
    <b a="1" b="3" c="4"/>
    <c/>
  </x>
  END
  
    $b->𝗺𝗼𝘃𝗲𝗡𝗲𝘄𝗔𝘁𝘁𝗿𝘀($c);                                                         
  
    ok <<END eq -p $x;                                                             
  <x>
    <a a="1" b="2"/>
    <b/>
    <c a="1" b="3" c="4"/>
  </x>
  END
  
    ok <<END eq -p $x;                                                             
  <x>
    <c a="1" b="3" c="4"/>
    <b/>
    <a a="1" b="2"/>
  </x>
  END
  

=head1 Traversal

Traverse the L<parse|/parse> tree in various orders applying a B<sub> to each node.

=head2 Post-order

This order allows you to edit children before their parents.

=head3 by($node, $sub)

Post-order traversal of a L<parse|/parse> tree or sub tree calling the specified B<sub> at each node and returning the specified starting node. The B<sub> is passed references to the current node and all of its L<ancestors|/ancestry>. A reference to the current node is also made available via L<$_>. This is equivalent to the L<x=|/opBy> operator.

     Parameter  Description
  1  $node      Starting node
  2  $sub       Sub to call for each sub node

B<Example:>


    ok -p $a eq <<END;                                                                          
  <a>
    <b>
      <c id="42" match="mm"/>
    </b>
    <d>
      <e/>
    </d>
  </a>
  END
  
     {my $s; $a->𝗯𝘆(sub{$s .= $_->tag}); ok $s eq "cbeda"                         
  

=head3 byX($node, $sub)

Post-order traversal of a L<parse|/parse> tree calling the specified B<sub> at each node as long as this sub does not L<die|http://perldoc.perl.org/functions/die.html>. The traversal is halted if the called sub does  L<die|http://perldoc.perl.org/functions/die.html> on any call with the reason in L<?@|http://perldoc.perl.org/perlvar.html#Error-Variables> The B<sub> is passed references to the current node and all of its L<ancestors|/ancestry> up to the node on which this sub was called. A reference to the current node is also made available via L<$_>.

Returns the start node regardless of the outcome of calling B<sub>.

     Parameter  Description
  1  $node      Start node
  2  $sub       Sub to call

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    ok -p $a eq <<END;                                                                          
  <a>
    <b>
      <c id="42" match="mm"/>
    </b>
    <d>
      <e/>
    </d>
  </a>
  END
  
     {my $s; $a->𝗯𝘆𝗫(sub{$s .= $_->tag}); ok $s eq "cbeda"                        
  
  sub 𝗯𝘆𝗫($$)                                                                    
   {my ($node, $sub) = @_;                                                        # Start node, sub to call
    eval {$node->byX2($sub)};                                                     # Trap any errors that occur
    $node
   }
  

=head3 byList($node, @context)

Return a list of all the nodes at and below a specified B<$node> in post-order or the empty list if the B<$node> is not in the optional B<@context>.

     Parameter  Description
  1  $node      Starting node
  2  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);                                               
  <a>
    <b>
      <c id="42" match="mm"/>
    </b>
    <d>
      <e/>
    </d>
  </a>
  END
  
    ok -c $e eq q(e d a);                                                          
  
    my $a = Data::Edit::Xml::new(<<END);
  <a>
   <b>
    <c/>
    <d/>
   </b>
   <e/>
   <f>
    <g/>
    <h/>
   </f>
  </a>
  END
  
    ok q(c d b e g h f a) eq join ' ', map{-t $_} $a->𝗯𝘆𝗟𝗶𝘀𝘁;
    ok q(h g f e d c b a) eq join ' ', map{-t $_} $a->byReverseList;
    ok q(a b c d e f g h) eq join ' ', map{-t $_} $a->downList;
    ok q(a f h g e b d c) eq join ' ', map{-t $_} $a->downReverseList;
  

=head3 byReverse($node, $sub, @context)

Reverse post-order traversal of a L<parse|/parse> tree or sub tree calling the specified B<sub> at each node and returning the specified starting B<$node>. The B<sub> is passed references to the current node and all of its L<ancestors|/ancestry>. The value of the current node is also made available via L<$_>.

     Parameter  Description
  1  $node      Starting node
  2  $sub       Sub to call for each sub node
  3  @context   Accumulated context.

B<Example:>


    ok -p $a eq <<END;                                                                          
  <a>
    <b>
      <c id="42" match="mm"/>
    </b>
    <d>
      <e/>
    </d>
  </a>
  END
  
     {my $s; $a->𝗯𝘆𝗥𝗲𝘃𝗲𝗿𝘀𝗲(sub{$s .= $_->tag}); ok $s eq "edcba"                   
  

=head3 byReverseX($node, $sub, @context)

Reverse post-order traversal of a L<parse|/parse> tree or sub tree below the specified B<$node> calling the specified B<sub> within L<eval|http://perldoc.perl.org/functions/eval.html>B<{}> at each node and returning the specified starting B<$node>. The B<sub> is passed references to the current node and all of its L<ancestors|/ancestry>. The value of the current node is also made available via L<$_>.

     Parameter  Description
  1  $node      Starting node
  2  $sub       Sub to call for each sub node
  3  @context   Accumulated context.

B<Example:>


    ok -p $a eq <<END;                                                                          
  <a>
    <b>
      <c id="42" match="mm"/>
    </b>
    <d>
      <e/>
    </d>
  </a>
  END
  
     {my $s; $a->byReverse(sub{$s .= $_->tag}); ok $s eq "edcba"                   
  

=head3 byReverseList($node, @context)

Return a list of all the nodes at and below a specified B<$node> in reverse preorder or the empty list if the specified B<$node> is not in the optional B<@context>.

     Parameter  Description
  1  $node      Starting node
  2  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);                                               
  <a>
    <b>
      <c id="42" match="mm"/>
    </b>
    <d>
      <e/>
    </d>
  </a>
  END
  
      my ($E, $D, $C, $B) = $a->𝗯𝘆𝗥𝗲𝘃𝗲𝗿𝘀𝗲𝗟𝗶𝘀𝘁;                                    
  
      ok -A $C eq q(c id="42" match="mm");                                        
  
    my $a = Data::Edit::Xml::new(<<END);
  <a>
   <b>
    <c/>
    <d/>
   </b>
   <e/>
   <f>
    <g/>
    <h/>
   </f>
  </a>
  END
  
    ok q(c d b e g h f a) eq join ' ', map{-t $_} $a->byList;
    ok q(h g f e d c b a) eq join ' ', map{-t $_} $a->𝗯𝘆𝗥𝗲𝘃𝗲𝗿𝘀𝗲𝗟𝗶𝘀𝘁;
    ok q(a b c d e f g h) eq join ' ', map{-t $_} $a->downList;
    ok q(a f h g e b d c) eq join ' ', map{-t $_} $a->downReverseList;
  

=head2 Pre-order

This order allows you to edit children after their parents

=head3 down($node, $sub, @context)

Pre-order traversal down through a L<parse|/parse> tree or sub tree calling the specified B<sub> at each node and returning the specified starting node. The B<sub> is passed references to the current node and all of its L<ancestors|/ancestry>. The value of the current node is also made available via L<$_>.

     Parameter  Description
  1  $node      Starting node
  2  $sub       Sub to call for each sub node
  3  @context   Accumulated context.

B<Example:>


     {my $s; $a->𝗱𝗼𝘄𝗻(sub{$s .= $_->tag}); ok $s eq "abcde"                        
  

=head3 downX($node, $sub)

Pre-order traversal of a L<parse|/parse> tree calling the specified B<sub> at each node as long as this sub does not L<die|http://perldoc.perl.org/functions/die.html>. The traversal is halted for the entire L<parse|/parse> tree if the called sub does L<die|http://perldoc.perl.org/functions/die.html> with the reason returned in L<?@|http://perldoc.perl.org/perlvar.html#Error-Variables>. The B<sub> is passed references to the current node and all of its L<ancestors|/ancestry> up to the node on which this sub was called. A reference to the current node is also made available via L<$_>.

Returns the start node regardless of the outcome of calling B<sub>.

     Parameter  Description
  1  $node      Start node
  2  $sub       Sub to call

B<Example:>


     {my $s; $a->down(sub{$s .= $_->tag}); ok $s eq "abcde"                        
  
  sub 𝗱𝗼𝘄𝗻𝗫($$)                                                                  
   {my ($node, $sub) = @_;                                                        # Start node, sub to call
    eval {$node->downX2($sub)};                                                   # Trap any errors that occur
    $node
   }
  

=head3 downToDie($node, $sub)

Pre-order traversal of a L<parse|/parse> tree calling the specified B<sub> at each node as long as this sub does not L<die|http://perldoc.perl.org/functions/die.html>. The traversal of the current sub tree is halted and continue with the next sibling or parent if the called sub does L<die|http://perldoc.perl.org/functions/die.html>. The B<sub> is passed references to the current node and all of its L<ancestors|/ancestry> up to the node on which this sub was called. A reference to the current node is also made available via L<$_>.

Returns the start node regardless of the outcome of calling B<sub>.

     Parameter  Description
  1  $node      Start node
  2  $sub       Sub to call

B<Example:>


  
    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b>
      <c/>
    </b>
    <d>
      <e/>
    </d>
  </a>
  END
  
    my @a;
    $a->𝗱𝗼𝘄𝗻𝗧𝗼𝗗𝗶𝗲(sub
     {confess if -t $_ eq q(b);
      push @a, -t $_;
     });
  
    is_deeply [@a], [qw(a d e)];
  
    my @b;
    $a->𝗱𝗼𝘄𝗻𝗧𝗼𝗗𝗶𝗲(sub
     {confess if -t $_ eq q(c);
      push @b, -t $_;
     });
  
    is_deeply [@b], [qw(a b d e)];
  

=head3 downList($node, @context)

Return a list of all the nodes at and below a specified B<$node> in pre-order or the empty list if the B<$node> is not in the optional B<@context>.

     Parameter  Description
  1  $node      Starting node
  2  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
   <b>
    <c/>
    <d/>
   </b>
   <e/>
   <f>
    <g/>
    <h/>
   </f>
  </a>
  END
  
    ok q(c d b e g h f a) eq join ' ', map{-t $_} $a->byList;
    ok q(h g f e d c b a) eq join ' ', map{-t $_} $a->byReverseList;
    ok q(a b c d e f g h) eq join ' ', map{-t $_} $a->𝗱𝗼𝘄𝗻𝗟𝗶𝘀𝘁;
    ok q(a f h g e b d c) eq join ' ', map{-t $_} $a->downReverseList;
  

=head3 downReverse($node, $sub, @context)

Reverse pre-order traversal down through a L<parse|/parse> tree or sub tree calling the specified B<sub> at each node and returning the specified starting node. The B<sub> is passed references to the current node and all of its L<ancestors|/ancestry>. The value of the current node is also made available via L<$_>.

     Parameter  Description
  1  $node      Starting node
  2  $sub       Sub to call for each sub node
  3  @context   Accumulated context.

B<Example:>


    ok -p $a eq <<END;                                                                          
  <a>
    <b>
      <c id="42" match="mm"/>
    </b>
    <d>
      <e/>
    </d>
  </a>
  END
  
     {my $s; $a->𝗱𝗼𝘄𝗻𝗥𝗲𝘃𝗲𝗿𝘀𝗲(sub{$s .= $_->tag}); ok $s eq "adebc"                 
  

=head3 downReverseX($node, $sub, @context)

Reverse pre-order traversal down through a L<parse|/parse> tree or sub tree calling the specified B<sub> within L<eval|http://perldoc.perl.org/functions/eval.html>B<{}> at each node and returning the specified starting node. The B<sub> is passed references to the current node and all of its L<ancestors|/ancestry>. The value of the current node is also made available via L<$_>.

     Parameter  Description
  1  $node      Starting node
  2  $sub       Sub to call for each sub node
  3  @context   Accumulated context.

B<Example:>


    ok -p $a eq <<END;                                                                          
  <a>
    <b>
      <c id="42" match="mm"/>
    </b>
    <d>
      <e/>
    </d>
  </a>
  END
  
     {my $s; $a->downReverse(sub{$s .= $_->tag}); ok $s eq "adebc"                 
  

=head3 downReverseList($node, @context)

Return a list of all the nodes at and below a specified B<$node> in reverse pre-order or the empty list if the B<$node> is not in the optional B<@context>.

     Parameter  Description
  1  $node      Starting node
  2  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
   <b>
    <c/>
    <d/>
   </b>
   <e/>
   <f>
    <g/>
    <h/>
   </f>
  </a>
  END
  
    ok q(c d b e g h f a) eq join ' ', map{-t $_} $a->byList;
    ok q(h g f e d c b a) eq join ' ', map{-t $_} $a->byReverseList;
    ok q(a b c d e f g h) eq join ' ', map{-t $_} $a->downList;
    ok q(a f h g e b d c) eq join ' ', map{-t $_} $a->𝗱𝗼𝘄𝗻𝗥𝗲𝘃𝗲𝗿𝘀𝗲𝗟𝗶𝘀𝘁;
  

=head2 Pre and Post order

Visit the parent first, then the children, then the parent again.

=head3 through($node, $before, $after, @context)

Traverse L<parse|/parse> tree visiting each node twice calling the specified sub B<$before> as we go down past the node and sub B<$after> as we go up past the node, finally return the specified starting node. The subs B<$before, $after> are passed references to the current node and all of its L<ancestors|/ancestry>. The value of the current node is also made available via L<$_>.

     Parameter  Description
  1  $node      Starting node
  2  $before    Sub to call when we meet a node
  3  $after     Sub to call we leave a node
  4  @context   Accumulated context.

B<Example:>


     {my $s; my $n = sub{$s .= $_->tag}; $a->𝘁𝗵𝗿𝗼𝘂𝗴𝗵($n, $n);                      
  
      ok $s eq "abccbdeeda"                                                        
  

=head3 throughX($node, $before, $after, @context)

Identical to L<through|/through> except the B<$before, $after> subs are called in an L<eval|http://perldoc.perl.org/functions/eval.html> block to prevent L<die|http://perldoc.perl.org/functions/die.html> terminating the traversal of the full tree.

     Parameter  Description
  1  $node      Starting node
  2  $before    Sub to call when we meet a node
  3  $after     Sub to call we leave a node
  4  @context   Accumulated context.

B<Example:>


     {my $s; my $n = sub{$s .= $_->tag}; $a->through($n, $n);                      
  
      ok $s eq "abccbdeeda"                                                        
  

=head2 Range

Ranges of nodes

=head3 from($start, @match)

Return a list consisting of the specified node and its following siblings optionally including only those nodes that match one of the tags in the specified list.

     Parameter  Description
  1  $start     Start node
  2  @match     Optional list of tags to match

B<Example:>


    ok -z $a eq <<END;                                                                   
  <a id="1">
    <b id="2">
      <c id="3">
        <e id="4"/>
      </c>
      <d id="5">
        <e id="6"/>
      </d>
      <c id="7">
        <d id="8">
          <e id="9"/>
        </d>
      </c>
      <d id="10">
        <e id="11"/>
      </d>
      <c id="12">
        <d id="13">
          <e id="14"/>
        </d>
      </c>
    </b>
  </a>
  END
  
     {my ($d, $c, $D) = $a->findByNumbers(5, 7, 10);                                
  
      my @f = $d->𝗳𝗿𝗼𝗺;                                                           
  
      ok @f == 4;                                                                 
  
      ok $d == $f[0];                                                             
  
      my @F = $d->𝗳𝗿𝗼𝗺(qw(c));                                                    
  
      ok @F == 2;                                                                 
  
      ok $F[1]->number == 12;                                                     
  
      ok $D == $t[-1];                                                            
  

=head3 to($end, @match)

Return a list of the sibling nodes preceding the specified node optionally including only those nodes that match one of the tags in the specified list.

     Parameter  Description
  1  $end       End node
  2  @match     Optional list of tags to match

B<Example:>


    ok -z $a eq <<END;                                                                   
  <a id="1">
    <b id="2">
      <c id="3">
        <e id="4"/>
      </c>
      <d id="5">
        <e id="6"/>
      </d>
      <c id="7">
        <d id="8">
          <e id="9"/>
        </d>
      </c>
      <d id="10">
        <e id="11"/>
      </d>
      <c id="12">
        <d id="13">
          <e id="14"/>
        </d>
      </c>
    </b>
  </a>
  END
  
     {my ($d, $c, $D) = $a->findByNumbers(5, 7, 10);                                
  
      my @t = $D->𝘁𝗼;                                                             
  
      ok @t == 4;                                                                 
  
      my @T = $D->𝘁𝗼(qw(c));                                                      
  
      ok @T == 2;                                                                 
  
      ok $T[1]->number == 7;                                                      
  

=head3 fromTo($start, $end, @match)

Return a list of the nodes between the specified start and end nodes optionally including only those nodes that match one of the tags in the specified list.

     Parameter  Description
  1  $start     Start node
  2  $end       End node
  3  @match     Optional list of tags to match

B<Example:>


    ok -z $a eq <<END;                                                                   
  <a id="1">
    <b id="2">
      <c id="3">
        <e id="4"/>
      </c>
      <d id="5">
        <e id="6"/>
      </d>
      <c id="7">
        <d id="8">
          <e id="9"/>
        </d>
      </c>
      <d id="10">
        <e id="11"/>
      </d>
      <c id="12">
        <d id="13">
          <e id="14"/>
        </d>
      </c>
    </b>
  </a>
  END
  
     {my ($d, $c, $D) = $a->findByNumbers(5, 7, 10);                                
  
      my @r = $d->𝗳𝗿𝗼𝗺𝗧𝗼($D);                                                     
  
      ok @r == 3;                                                                 
  
      my @R = $d->𝗳𝗿𝗼𝗺𝗧𝗼($D, qw(c));                                              
  
      ok @R == 1;                                                                 
  
      ok $R[0]->number == 7;                                                      
  
      ok !$D->𝗳𝗿𝗼𝗺𝗧𝗼($d);                                                         
  
      ok 1 == $d->𝗳𝗿𝗼𝗺𝗧𝗼($d);                                                     
  

=head1 Location

Locate the line numbers and columns of a specified node and write that information as a L<Oxygen Message|/https://www.oxygenxml.com/doc/versions/20.1/ug-author/topics/linked-output-messages-of-external-engine.html>.

=head2 lineLocation($node)

Return the line number.column location of this tag in its source file or string if the source was parsed with the L<line number|/lineNumber> option on.

     Parameter  Description
  1  $node      Node

B<Example:>


    my $a = Data::Edit::Xml::new(<<END, lineNumbers=>1, inputFile=>q(aaa.xml));
  <?xml version="1.0" encoding="UTF-8"?>
  <!DOCTYPE concept PUBLIC "-//OASIS//DTD DITA Concept//EN" "concept.dtd">
  <test id="t1">
   <title>Test_</title>
    <testbody>
      <setup>
        <p>Place the boiling water and fresh tea in the pot.</p>
      </setup>
      <checks>
        <p>Make sure the pot is on an insulated surface.</p>
      </checks>
      <run>
        <p>Stir with a spoon then let brew for 5 minutes.</p>
      </run>
      <results>
        <p>Pour the tea into a cup.</p>
      </results>
      <outcome>
        <p>An enjoyable cup of tea.</p>
      </outcome>
    </testbody>
  </test>
  END
  
    ok -p $a eq <<END;
  <test id="t1" xtrf="3.1:14">
    <title xtrf="4.2:8">Test_</title>
    <testbody xtrf="5.3:12">
      <setup xtrf="6.5:11">
        <p xtrf="7.7:9">Place the boiling water and fresh tea in the pot.</p>
      </setup>
      <checks xtrf="9.5:12">
        <p xtrf="10.7:9">Make sure the pot is on an insulated surface.</p>
      </checks>
      <run xtrf="12.5:9">
        <p xtrf="13.7:9">Stir with a spoon then let brew for 5 minutes.</p>
      </run>
      <results xtrf="15.5:13">
        <p xtrf="16.7:9">Pour the tea into a cup.</p>
      </results>
      <outcome xtrf="18.5:13">
        <p xtrf="19.7:9">An enjoyable cup of tea.</p>
      </outcome>
    </testbody>
  </test>
  END
  
    ok $a->go_testbody_run_p__location eq q( on line 13 from 7 to 9 in file: aaa.xml);
  
    my $p = $a->go_testbody_run_p;
    $p->putNext(my $q = $p->newTag_hello);
    ok $p->𝗹𝗶𝗻𝗲𝗟𝗼𝗰𝗮𝘁𝗶𝗼𝗻 eq q(on line 13 from 7 to 9);
  
    ok $q->location eq q( in file: aaa.xml);
    ok $q->closestLocation == $p;
    ok $q->approxLocation eq q( on line 13 from 7 to 9 in file: aaa.xml);
  
    ok $q->formatOxygenMessage(q(E), q(), q(Hello detected)) eq <<END;
  Type: E
  Line: 13
  Column: 7
  EndLine: 13
  EndColumn: 9
  AdditionalInfoURL:
  Description: Hello detected
  END
  

=head2 location($node, $file)

Return the line number.column plus file location of this tag in its source file or string if the source was parsed with the L<line number|/lineNumber> option on.

     Parameter  Description
  1  $node      Node
  2  $file      Optionally the location of the source.

B<Example:>


    my $a = Data::Edit::Xml::new(<<END, lineNumbers=>1, inputFile=>q(aaa.xml));
  <?xml version="1.0" encoding="UTF-8"?>
  <!DOCTYPE concept PUBLIC "-//OASIS//DTD DITA Concept//EN" "concept.dtd">
  <test id="t1">
   <title>Test_</title>
    <testbody>
      <setup>
        <p>Place the boiling water and fresh tea in the pot.</p>
      </setup>
      <checks>
        <p>Make sure the pot is on an insulated surface.</p>
      </checks>
      <run>
        <p>Stir with a spoon then let brew for 5 minutes.</p>
      </run>
      <results>
        <p>Pour the tea into a cup.</p>
      </results>
      <outcome>
        <p>An enjoyable cup of tea.</p>
      </outcome>
    </testbody>
  </test>
  END
  
    ok -p $a eq <<END;
  <test id="t1" xtrf="3.1:14">
    <title xtrf="4.2:8">Test_</title>
    <testbody xtrf="5.3:12">
      <setup xtrf="6.5:11">
        <p xtrf="7.7:9">Place the boiling water and fresh tea in the pot.</p>
      </setup>
      <checks xtrf="9.5:12">
        <p xtrf="10.7:9">Make sure the pot is on an insulated surface.</p>
      </checks>
      <run xtrf="12.5:9">
        <p xtrf="13.7:9">Stir with a spoon then let brew for 5 minutes.</p>
      </run>
      <results xtrf="15.5:13">
        <p xtrf="16.7:9">Pour the tea into a cup.</p>
      </results>
      <outcome xtrf="18.5:13">
        <p xtrf="19.7:9">An enjoyable cup of tea.</p>
      </outcome>
    </testbody>
  </test>
  END
  
    ok $a->go_testbody_run_p__location eq q( on line 13 from 7 to 9 in file: aaa.xml);
  
    my $p = $a->go_testbody_run_p;
    $p->putNext(my $q = $p->newTag_hello);
    ok $p->lineLocation eq q(on line 13 from 7 to 9);
  
    ok $q->𝗹𝗼𝗰𝗮𝘁𝗶𝗼𝗻 eq q( in file: aaa.xml);
    ok $q->closestLocation == $p;
    ok $q->approxLocation eq q( on line 13 from 7 to 9 in file: aaa.xml);
  
    ok $q->formatOxygenMessage(q(E), q(), q(Hello detected)) eq <<END;
  Type: E
  Line: 13
  Column: 7
  EndLine: 13
  EndColumn: 9
  AdditionalInfoURL:
  Description: Hello detected
  END
  

=head2 closestLocation($node)

Return the nearest node with line number.column information

     Parameter  Description
  1  $node      Node

B<Example:>


    my $a = Data::Edit::Xml::new(<<END, lineNumbers=>1, inputFile=>q(aaa.xml));
  <?xml version="1.0" encoding="UTF-8"?>
  <!DOCTYPE concept PUBLIC "-//OASIS//DTD DITA Concept//EN" "concept.dtd">
  <test id="t1">
   <title>Test_</title>
    <testbody>
      <setup>
        <p>Place the boiling water and fresh tea in the pot.</p>
      </setup>
      <checks>
        <p>Make sure the pot is on an insulated surface.</p>
      </checks>
      <run>
        <p>Stir with a spoon then let brew for 5 minutes.</p>
      </run>
      <results>
        <p>Pour the tea into a cup.</p>
      </results>
      <outcome>
        <p>An enjoyable cup of tea.</p>
      </outcome>
    </testbody>
  </test>
  END
  
    ok -p $a eq <<END;
  <test id="t1" xtrf="3.1:14">
    <title xtrf="4.2:8">Test_</title>
    <testbody xtrf="5.3:12">
      <setup xtrf="6.5:11">
        <p xtrf="7.7:9">Place the boiling water and fresh tea in the pot.</p>
      </setup>
      <checks xtrf="9.5:12">
        <p xtrf="10.7:9">Make sure the pot is on an insulated surface.</p>
      </checks>
      <run xtrf="12.5:9">
        <p xtrf="13.7:9">Stir with a spoon then let brew for 5 minutes.</p>
      </run>
      <results xtrf="15.5:13">
        <p xtrf="16.7:9">Pour the tea into a cup.</p>
      </results>
      <outcome xtrf="18.5:13">
        <p xtrf="19.7:9">An enjoyable cup of tea.</p>
      </outcome>
    </testbody>
  </test>
  END
  
    ok $a->go_testbody_run_p__location eq q( on line 13 from 7 to 9 in file: aaa.xml);
  
    my $p = $a->go_testbody_run_p;
    $p->putNext(my $q = $p->newTag_hello);
    ok $p->lineLocation eq q(on line 13 from 7 to 9);
  
    ok $q->location eq q( in file: aaa.xml);
    ok $q->𝗰𝗹𝗼𝘀𝗲𝘀𝘁𝗟𝗼𝗰𝗮𝘁𝗶𝗼𝗻 == $p;
    ok $q->approxLocation eq q( on line 13 from 7 to 9 in file: aaa.xml);
  
    ok $q->formatOxygenMessage(q(E), q(), q(Hello detected)) eq <<END;
  Type: E
  Line: 13
  Column: 7
  EndLine: 13
  EndColumn: 9
  AdditionalInfoURL:
  Description: Hello detected
  END
  

=head2 approxLocation($node, $file)

Return the line number.column location of the node nearest to this node in the source file if the source was parsed with the L<line number|/lineNumber> option on.

     Parameter  Description
  1  $node      Node
  2  $file      Optionally the location of the source.

B<Example:>


    my $a = Data::Edit::Xml::new(<<END, lineNumbers=>1, inputFile=>q(aaa.xml));
  <?xml version="1.0" encoding="UTF-8"?>
  <!DOCTYPE concept PUBLIC "-//OASIS//DTD DITA Concept//EN" "concept.dtd">
  <test id="t1">
   <title>Test_</title>
    <testbody>
      <setup>
        <p>Place the boiling water and fresh tea in the pot.</p>
      </setup>
      <checks>
        <p>Make sure the pot is on an insulated surface.</p>
      </checks>
      <run>
        <p>Stir with a spoon then let brew for 5 minutes.</p>
      </run>
      <results>
        <p>Pour the tea into a cup.</p>
      </results>
      <outcome>
        <p>An enjoyable cup of tea.</p>
      </outcome>
    </testbody>
  </test>
  END
  
    ok -p $a eq <<END;
  <test id="t1" xtrf="3.1:14">
    <title xtrf="4.2:8">Test_</title>
    <testbody xtrf="5.3:12">
      <setup xtrf="6.5:11">
        <p xtrf="7.7:9">Place the boiling water and fresh tea in the pot.</p>
      </setup>
      <checks xtrf="9.5:12">
        <p xtrf="10.7:9">Make sure the pot is on an insulated surface.</p>
      </checks>
      <run xtrf="12.5:9">
        <p xtrf="13.7:9">Stir with a spoon then let brew for 5 minutes.</p>
      </run>
      <results xtrf="15.5:13">
        <p xtrf="16.7:9">Pour the tea into a cup.</p>
      </results>
      <outcome xtrf="18.5:13">
        <p xtrf="19.7:9">An enjoyable cup of tea.</p>
      </outcome>
    </testbody>
  </test>
  END
  
    ok $a->go_testbody_run_p__location eq q( on line 13 from 7 to 9 in file: aaa.xml);
  
    my $p = $a->go_testbody_run_p;
    $p->putNext(my $q = $p->newTag_hello);
    ok $p->lineLocation eq q(on line 13 from 7 to 9);
  
    ok $q->location eq q( in file: aaa.xml);
    ok $q->closestLocation == $p;
    ok $q->𝗮𝗽𝗽𝗿𝗼𝘅𝗟𝗼𝗰𝗮𝘁𝗶𝗼𝗻 eq q( on line 13 from 7 to 9 in file: aaa.xml);
  
    ok $q->formatOxygenMessage(q(E), q(), q(Hello detected)) eq <<END;
  Type: E
  Line: 13
  Column: 7
  EndLine: 13
  EndColumn: 9
  AdditionalInfoURL:
  Description: Hello detected
  END
  

=head2 formatOxygenMessage($node, $level, $url, @message)

Write an error message in Oxygen format

     Parameter  Description
  1  $node      Node
  2  $level     Error level [F|E|W]
  3  $url       Explanatory Url
  4  @message   Message text

B<Example:>


    my $a = Data::Edit::Xml::new(<<END, lineNumbers=>1, inputFile=>q(aaa.xml));
  <?xml version="1.0" encoding="UTF-8"?>
  <!DOCTYPE concept PUBLIC "-//OASIS//DTD DITA Concept//EN" "concept.dtd">
  <test id="t1">
   <title>Test_</title>
    <testbody>
      <setup>
        <p>Place the boiling water and fresh tea in the pot.</p>
      </setup>
      <checks>
        <p>Make sure the pot is on an insulated surface.</p>
      </checks>
      <run>
        <p>Stir with a spoon then let brew for 5 minutes.</p>
      </run>
      <results>
        <p>Pour the tea into a cup.</p>
      </results>
      <outcome>
        <p>An enjoyable cup of tea.</p>
      </outcome>
    </testbody>
  </test>
  END
  
    ok -p $a eq <<END;
  <test id="t1" xtrf="3.1:14">
    <title xtrf="4.2:8">Test_</title>
    <testbody xtrf="5.3:12">
      <setup xtrf="6.5:11">
        <p xtrf="7.7:9">Place the boiling water and fresh tea in the pot.</p>
      </setup>
      <checks xtrf="9.5:12">
        <p xtrf="10.7:9">Make sure the pot is on an insulated surface.</p>
      </checks>
      <run xtrf="12.5:9">
        <p xtrf="13.7:9">Stir with a spoon then let brew for 5 minutes.</p>
      </run>
      <results xtrf="15.5:13">
        <p xtrf="16.7:9">Pour the tea into a cup.</p>
      </results>
      <outcome xtrf="18.5:13">
        <p xtrf="19.7:9">An enjoyable cup of tea.</p>
      </outcome>
    </testbody>
  </test>
  END
  
    ok $a->go_testbody_run_p__location eq q( on line 13 from 7 to 9 in file: aaa.xml);
  
    my $p = $a->go_testbody_run_p;
    $p->putNext(my $q = $p->newTag_hello);
    ok $p->lineLocation eq q(on line 13 from 7 to 9);
  
    ok $q->location eq q( in file: aaa.xml);
    ok $q->closestLocation == $p;
    ok $q->approxLocation eq q( on line 13 from 7 to 9 in file: aaa.xml);
  
    ok $q->𝗳𝗼𝗿𝗺𝗮𝘁𝗢𝘅𝘆𝗴𝗲𝗻𝗠𝗲𝘀𝘀𝗮𝗴𝗲(q(E), q(), q(Hello detected)) eq <<END;
  Type: E
  Line: 13
  Column: 7
  EndLine: 13
  EndColumn: 9
  AdditionalInfoURL:
  Description: Hello detected
  END
  

=head1 Position

Confirm that the position L<navigated|/Navigation> to is the expected position.

=head2 at($node, @context)

Confirm that the specified B<$node> has the specified L<ancestry|/ancestry>. Ancestry is specified by providing the expected tags that the B<$node>'s parent, the parent's parent etc. must match at each level. If B<undef> is specified then any tag is assumed to match at that level. If a regular expression is specified then the current parent node tag must match the regular expression at that level. If all supplied tags match successfully then the starting node is returned else B<undef>.

     Parameter  Description
  1  $node      Node
  2  @context   Ancestry.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


   {my $a = Data::Edit::Xml::new(<<END);                                          
  <a>
    <b>
      <c> <d/> </c>
      <c> <e/> </c>
      <c> <f/> </c>
    </b>
  </a>
  END
  
    ok  $a->go(qw(b c -1 f))->𝗮𝘁(qw(f c b a));                                    
  
    ok  $a->go(qw(b c  1 e))->𝗮𝘁(undef, qr(c|d), undef, qq(a));                   
  
    ok $d->context eq q(d c b a);                                                  
  
    ok  $d->𝗮𝘁(qw(d c b), undef);                                                 
  
    ok !$d->𝗮𝘁(qw(d c b), undef, undef);                                          
  
    ok !$d->𝗮𝘁(qw(d e b));                                                        
  

=head2 atText($node, $re, @context)

Confirm that we are on a text node whose text value matches a regular expression in the optional B<@context>. Return the specified B<$node> on success else B<undef>.

     Parameter  Description
  1  $node      Text node
  2  $re        Regular expression to match
  3  @context   Context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);                                             
  <a>abcdcba</a>
  END
  

=head2 atStringContentMatches($node, $re, @context)

Confirm that we are on a B<$node> whose contents, represented as a string, matches the specified regular expression B<$re> in the optional B<@context>. Return the specified B<$node> on success else B<undef>.

     Parameter  Description
  1  $node      Text node
  2  $re        Regular expression to match
  3  @context   Context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b>
      <c>cc</c>
      <d>dd</d>
    </b>
  </a>
  END
  
    $a->by(sub
     {my ($o) = @_;
      if (!$o->atStringContentMatches_dd)
       {$o->id = "no";
       }
     });
  
    ok -p $a eq <<END;
  <a>
    <b>
      <c id="no">cc</c>
      <d>dd</d>
    </b>
  </a>
  END
  

=head2 atTop($node)

Return the current node if it is the root == top of a parse tree else return B<undef>.

     Parameter  Description
  1  $node      Node

B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a><b/></a>
  END
  
    ok  $a->𝗮𝘁𝗧𝗼𝗽;
    ok !$a->first__atTop;
  

=head2 attrAt($node, $attribute, @context)

Return the specified B<$node> if it has the specified B<$attribute> and the $node is in the optional B<@context> else return B<undef>.

     Parameter   Description
  1  $node       Starting node
  2  $attribute  Attribute
  3  @context    Context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


  if (1)                                                                           
   {my $a = Data::Edit::Xml::new(q(<a><b c="C"/></a>));
    my $b = $a->first;
    ok !$a->attrAt_id;
    ok  $b->attrAt_c_b_a;
    ok !$b->attrAt_b_b_a;
    ok !$b->attrValueAt_c_C_c_a;
    ok  $b->attrValueAt_c_C_b_a;
   }
  

=head2 attrValueAt($node, $attribute, $value, @context)

Return the specified B<$node> if it has the specified B<$attribute> with the specified B<$value> and the $node is in the optional B<@context> else return B<undef>.

     Parameter   Description
  1  $node       Starting node
  2  $attribute  Attribute
  3  $value      Wanted value of attribute
  4  @context    Context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


  if (1)                                                                           
   {my $a = Data::Edit::Xml::new(q(<a><b c="C"/></a>));
    my $b = $a->first;
    ok !$a->attrAt_id;
    ok  $b->attrAt_c_b_a;
    ok !$b->attrAt_b_b_a;
    ok !$b->attrValueAt_c_C_c_a;
    ok  $b->attrValueAt_c_C_b_a;
   }
  

=head2 not($node, @tags)

Return the specified B<$node> if it does not match any of the specified tags, else B<undef>

     Parameter  Description
  1  $node      Node
  2  @tags      Tags not to match

B<Example:>


   {my $a = Data::Edit::Xml::new(<<END);                                          
  <a>
    <b/>
  </a>
  END
  
    ok $a->first->not_a_c;                                                        
  

=head2 atOrBelow($start, @context)

Confirm that the node or one of its ancestors has the specified context as recognized by L<at|/at> and return the first node that matches the context or B<undef> if none do.

     Parameter  Description
  1  $start     Starting node
  2  @context   Ancestry.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    ok $d->context eq q(d c b a);                                                  
  
    ok  $d->𝗮𝘁𝗢𝗿𝗕𝗲𝗹𝗼𝘄(qw(d c b a));                                               
  
    ok  $d->𝗮𝘁𝗢𝗿𝗕𝗲𝗹𝗼𝘄(qw(  c b a));                                               
  
    ok  $d->𝗮𝘁𝗢𝗿𝗕𝗲𝗹𝗼𝘄(qw(    b a));                                               
  
    ok !$d->𝗮𝘁𝗢𝗿𝗕𝗲𝗹𝗼𝘄(qw(  c   a));                                               
  

=head2 adjacent($first, $second)

Return the first node if it is adjacent to the second node else B<undef>.

     Parameter  Description
  1  $first     First node
  2  $second    Second node

B<Example:>


   {my $a = Data::Edit::Xml::new(<<END);                                                       
  <a>
    <b>
      <c>
        <d/>
      </c>
    </b>
    <b>
      <c/>
    </b>
    <e>
      <f/>
    </e>
  </a>
  END
  
    my ($d, $c, $b, $C, $B, $f, $e) = $a->byList;                                              
  
    ok !$a->𝗮𝗱𝗷𝗮𝗰𝗲𝗻𝘁($B);                                                         
  
    ok  $b->𝗮𝗱𝗷𝗮𝗰𝗲𝗻𝘁($B);                                                         
  

=head2 ancestry($node)

Return a list containing: (the specified B<$node>, its parent, its parent's parent etc..). Or use L<upn|/upn> to go up the specified number of levels.

     Parameter  Description
  1  $node      Starting node.

B<Example:>


    $a->numberTree;                                                                  
  
    ok $a->prettyStringNumbered eq <<END;                                             
  <a id="1">
    <b id="2">
      <A id="3"/>
      <B id="4"/>
    </b>
    <c id="5">
      <C id="6"/>
      <D id="7"/>
    </c>
  </a>
  END
  
    is_deeply [map {-t $_} $a->findByNumber(7)->𝗮𝗻𝗰𝗲𝘀𝘁𝗿𝘆], [qw(D c a)];           
  

=head2 context($node)

Return a string containing the tag of the starting node and the tags of all its ancestors separated by single spaces.

     Parameter  Description
  1  $node      Starting node.

B<Example:>


    ok -p $a eq <<END;                                                                          
  <a>
    <b>
      <c id="42" match="mm"/>
    </b>
    <d>
      <e/>
    </d>
  </a>
  END
  
    ok $a->go(qw(d e))->𝗰𝗼𝗻𝘁𝗲𝘅𝘁 eq 'e d a';                                       
  

=head2 containsSingleText($node, @context)

Return the single text element below the specified B<$node> else return B<undef>.

     Parameter  Description
  1  $node      Node
  2  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


   {my $a = Data::Edit::Xml::new("<a><b>bb</b><c>cc<d/>ee</c></a>");              
  
    ok  $a->go(q(b))->𝗰𝗼𝗻𝘁𝗮𝗶𝗻𝘀𝗦𝗶𝗻𝗴𝗹𝗲𝗧𝗲𝘅𝘁->text eq q(bb);                          
  
    ok !$a->go(q(c))->𝗰𝗼𝗻𝘁𝗮𝗶𝗻𝘀𝗦𝗶𝗻𝗴𝗹𝗲𝗧𝗲𝘅𝘁;                                         
  

=head2 depth($node)

Returns the depth of the specified B<$node>, the  depth of a root node is zero.

     Parameter  Description
  1  $node      Node.

B<Example:>


    ok -z $a eq <<END;                                                                   
  <a id="1">
    <b id="2">
      <c id="3">
        <e id="4"/>
      </c>
      <d id="5">
        <e id="6"/>
      </d>
      <c id="7">
        <d id="8">
          <e id="9"/>
        </d>
      </c>
      <d id="10">
        <e id="11"/>
      </d>
      <c id="12">
        <d id="13">
          <e id="14"/>
        </d>
      </c>
    </b>
  </a>
  END
  
    ok 0 == $a->𝗱𝗲𝗽𝘁𝗵;                                                            
  
    ok 4 == $a->findByNumber(14)->𝗱𝗲𝗽𝘁𝗵;                                          
  
  if (1)                                                                            
   {my $a = Data::Edit::Xml::new(q(<a><b><c><d/></c><e/></b></a>));
  
    ok -p $a eq <<END;
  <a>
    <b>
      <c>
        <d/>
      </c>
      <e/>
    </b>
  </a>
  END
  
   my ($d, $c, $e, $b) = $a->byList;
   ok $a->height == 4;
   ok $a->𝗱𝗲𝗽𝘁𝗵  == 0;
   ok $c->𝗱𝗲𝗽𝘁𝗵  == 2;
   ok $c->height == 2;
   ok $e->𝗱𝗲𝗽𝘁𝗵  == 2;
   ok $e->height == 1;
  
   is_deeply [$a->depthProfile], [qw(4 3 3 2 1)];
  }
  
  if (1)                                                                                 
   {my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b>
      <c>cc
        <d/>
  dd
      </c>
    </b>
    <B>
      <c>cc
        <d/>
  dd
      </c>
    </B>
  </a>
  END
  
   my $b = $a->first_b; my $B = $a->last_B;
   my $c = $b->first_c; my $C = $B->first_c;
   my $d = $c->first_d; my $D = $C->first_d;
  
   $a->setDepthProfile;
  
   ok $b->depthProfileLast eq q(3 3 3 2 1);
   ok $b->depthProfileLast eq $B->depthProfileLast;
  
  # Represent using tags and text
   $a->setRepresentationAsTagsAndText;
   is_deeply [$b->stringTagsAndText],   [qw(cc d dd c b)];
   is_deeply [$B->stringTagsAndText],   [qw(cc d dd c B)];
   ok         $b->representationLast  eq qq(cc d dd c b);
   ok         $B->representationLast  eq qq(cc d dd c B);
   ok         $c->representationLast  eq qq(cc d dd c);
   ok         $C->representationLast  eq qq(cc d dd c);
   ok dump($b->representationLast) ne dump($B->representationLast);
   is_deeply  $c->representationLast,
              $C->representationLast;
  
   my $m  = $a->matchNodesByRepresentation;
  
   my $bb = $b->representationLast;
   is_deeply $m->{$bb}, [$b];
  
   my $cc = $c->representationLast;
   is_deeply $m->{$cc}, [$c, $C];
  
  # Represent using just text
   $a->setRepresentationAsText;
   is_deeply [$b->stringText],          [qw(cc dd)];
   is_deeply [$B->stringText],          [qw(cc dd)];
   ok         $b->representationLast  eq qq(cc dd);
   ok         $B->representationLast  eq qq(cc dd);
   is_deeply  $b->representationLast,
              $B->representationLast;
   is_deeply  $c->representationLast,
              $C->representationLast;
  
   my $M  = $a->matchNodesByRepresentation;
   my $BB = $b->representationLast;
   is_deeply $M->{$BB}, [$c, $b, $C, $B];
  
   my $CC = $c->representationLast;
   is_deeply $M->{$BB}, [$c, $b, $C, $B];
  
   ok $b->representationLast eq $c->representationLast;
  }
  
  if (1)                                                                          
   {my $a = Data::Edit::Xml::new(q(<a>aaaa</a>));
    ok $a->first->isOnlyChildText;
   }
  

=head2 depthProfile($node)

Returns the depth profile of the tree rooted at the specified B<$node>.

     Parameter  Description
  1  $node      Node.

B<Example:>


  if (1)                                                                            
   {my $a = Data::Edit::Xml::new(q(<a><b><c><d/></c><e/></b></a>));
  
    ok -p $a eq <<END;
  <a>
    <b>
      <c>
        <d/>
      </c>
      <e/>
    </b>
  </a>
  END
  
   my ($d, $c, $e, $b) = $a->byList;
   ok $a->height == 4;
   ok $a->depth  == 0;
   ok $c->depth  == 2;
   ok $c->height == 2;
   ok $e->depth  == 2;
   ok $e->height == 1;
  
   is_deeply [$a->𝗱𝗲𝗽𝘁𝗵𝗣𝗿𝗼𝗳𝗶𝗹𝗲], [qw(4 3 3 2 1)];
  }
  
  if (1)                                                                                 
   {my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b>
      <c>cc
        <d/>
  dd
      </c>
    </b>
    <B>
      <c>cc
        <d/>
  dd
      </c>
    </B>
  </a>
  END
  
   my $b = $a->first_b; my $B = $a->last_B;
   my $c = $b->first_c; my $C = $B->first_c;
   my $d = $c->first_d; my $D = $C->first_d;
  
   $a->setDepthProfile;
  
   ok $b->depthProfileLast eq q(3 3 3 2 1);
   ok $b->depthProfileLast eq $B->depthProfileLast;
  
  # Represent using tags and text
   $a->setRepresentationAsTagsAndText;
   is_deeply [$b->stringTagsAndText],   [qw(cc d dd c b)];
   is_deeply [$B->stringTagsAndText],   [qw(cc d dd c B)];
   ok         $b->representationLast  eq qq(cc d dd c b);
   ok         $B->representationLast  eq qq(cc d dd c B);
   ok         $c->representationLast  eq qq(cc d dd c);
   ok         $C->representationLast  eq qq(cc d dd c);
   ok dump($b->representationLast) ne dump($B->representationLast);
   is_deeply  $c->representationLast,
              $C->representationLast;
  
   my $m  = $a->matchNodesByRepresentation;
  
   my $bb = $b->representationLast;
   is_deeply $m->{$bb}, [$b];
  
   my $cc = $c->representationLast;
   is_deeply $m->{$cc}, [$c, $C];
  
  # Represent using just text
   $a->setRepresentationAsText;
   is_deeply [$b->stringText],          [qw(cc dd)];
   is_deeply [$B->stringText],          [qw(cc dd)];
   ok         $b->representationLast  eq qq(cc dd);
   ok         $B->representationLast  eq qq(cc dd);
   is_deeply  $b->representationLast,
              $B->representationLast;
   is_deeply  $c->representationLast,
              $C->representationLast;
  
   my $M  = $a->matchNodesByRepresentation;
   my $BB = $b->representationLast;
   is_deeply $M->{$BB}, [$c, $b, $C, $B];
  
   my $CC = $c->representationLast;
   is_deeply $M->{$BB}, [$c, $b, $C, $B];
  
   ok $b->representationLast eq $c->representationLast;
  }
  
  if (1)                                                                          
   {my $a = Data::Edit::Xml::new(q(<a>aaaa</a>));
    ok $a->first->isOnlyChildText;
   }
  

=head2 setDepthProfile($tree)

Sets the L<depthProfile|/depthProfile> for every node in the specified B<$tree>. The last set L<depthProfile|/depthProfile> for a specific niode can be retrieved from L<depthProfileLast|/depthProfileLast>.

     Parameter  Description
  1  $tree      Tree of nodes.

B<Example:>


  if (1)                                                                                 
   {my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b>
      <c>cc
        <d/>
  dd
      </c>
    </b>
    <B>
      <c>cc
        <d/>
  dd
      </c>
    </B>
  </a>
  END
  
   my $b = $a->first_b; my $B = $a->last_B;
   my $c = $b->first_c; my $C = $B->first_c;
   my $d = $c->first_d; my $D = $C->first_d;
  
   $a->𝘀𝗲𝘁𝗗𝗲𝗽𝘁𝗵𝗣𝗿𝗼𝗳𝗶𝗹𝗲;
  
   ok $b->depthProfileLast eq q(3 3 3 2 1);
   ok $b->depthProfileLast eq $B->depthProfileLast;
  
  # Represent using tags and text
   $a->setRepresentationAsTagsAndText;
   is_deeply [$b->stringTagsAndText],   [qw(cc d dd c b)];
   is_deeply [$B->stringTagsAndText],   [qw(cc d dd c B)];
   ok         $b->representationLast  eq qq(cc d dd c b);
   ok         $B->representationLast  eq qq(cc d dd c B);
   ok         $c->representationLast  eq qq(cc d dd c);
   ok         $C->representationLast  eq qq(cc d dd c);
   ok dump($b->representationLast) ne dump($B->representationLast);
   is_deeply  $c->representationLast,
              $C->representationLast;
  
   my $m  = $a->matchNodesByRepresentation;
  
   my $bb = $b->representationLast;
   is_deeply $m->{$bb}, [$b];
  
   my $cc = $c->representationLast;
   is_deeply $m->{$cc}, [$c, $C];
  
  # Represent using just text
   $a->setRepresentationAsText;
   is_deeply [$b->stringText],          [qw(cc dd)];
   is_deeply [$B->stringText],          [qw(cc dd)];
   ok         $b->representationLast  eq qq(cc dd);
   ok         $B->representationLast  eq qq(cc dd);
   is_deeply  $b->representationLast,
              $B->representationLast;
   is_deeply  $c->representationLast,
              $C->representationLast;
  
   my $M  = $a->matchNodesByRepresentation;
   my $BB = $b->representationLast;
   is_deeply $M->{$BB}, [$c, $b, $C, $B];
  
   my $CC = $c->representationLast;
   is_deeply $M->{$BB}, [$c, $b, $C, $B];
  
   ok $b->representationLast eq $c->representationLast;
  }
  
  if (1)                                                                          
   {my $a = Data::Edit::Xml::new(q(<a>aaaa</a>));
    ok $a->first->isOnlyChildText;
   }
  

=head2 height($node)

Returns the height of the tree rooted at the specified B<$node>.

     Parameter  Description
  1  $node      Node.

B<Example:>


  if (1)                                                                            
   {my $a = Data::Edit::Xml::new(q(<a><b><c><d/></c><e/></b></a>));
  
    ok -p $a eq <<END;
  <a>
    <b>
      <c>
        <d/>
      </c>
      <e/>
    </b>
  </a>
  END
  
   my ($d, $c, $e, $b) = $a->byList;
   ok $a->𝗵𝗲𝗶𝗴𝗵𝘁 == 4;
   ok $a->depth  == 0;
   ok $c->depth  == 2;
   ok $c->𝗵𝗲𝗶𝗴𝗵𝘁 == 2;
   ok $e->depth  == 2;
   ok $e->𝗵𝗲𝗶𝗴𝗵𝘁 == 1;
  
   is_deeply [$a->depthProfile], [qw(4 3 3 2 1)];
  }
  
  if (1)                                                                                 
   {my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b>
      <c>cc
        <d/>
  dd
      </c>
    </b>
    <B>
      <c>cc
        <d/>
  dd
      </c>
    </B>
  </a>
  END
  
   my $b = $a->first_b; my $B = $a->last_B;
   my $c = $b->first_c; my $C = $B->first_c;
   my $d = $c->first_d; my $D = $C->first_d;
  
   $a->setDepthProfile;
  
   ok $b->depthProfileLast eq q(3 3 3 2 1);
   ok $b->depthProfileLast eq $B->depthProfileLast;
  
  # Represent using tags and text
   $a->setRepresentationAsTagsAndText;
   is_deeply [$b->stringTagsAndText],   [qw(cc d dd c b)];
   is_deeply [$B->stringTagsAndText],   [qw(cc d dd c B)];
   ok         $b->representationLast  eq qq(cc d dd c b);
   ok         $B->representationLast  eq qq(cc d dd c B);
   ok         $c->representationLast  eq qq(cc d dd c);
   ok         $C->representationLast  eq qq(cc d dd c);
   ok dump($b->representationLast) ne dump($B->representationLast);
   is_deeply  $c->representationLast,
              $C->representationLast;
  
   my $m  = $a->matchNodesByRepresentation;
  
   my $bb = $b->representationLast;
   is_deeply $m->{$bb}, [$b];
  
   my $cc = $c->representationLast;
   is_deeply $m->{$cc}, [$c, $C];
  
  # Represent using just text
   $a->setRepresentationAsText;
   is_deeply [$b->stringText],          [qw(cc dd)];
   is_deeply [$B->stringText],          [qw(cc dd)];
   ok         $b->representationLast  eq qq(cc dd);
   ok         $B->representationLast  eq qq(cc dd);
   is_deeply  $b->representationLast,
              $B->representationLast;
   is_deeply  $c->representationLast,
              $C->representationLast;
  
   my $M  = $a->matchNodesByRepresentation;
   my $BB = $b->representationLast;
   is_deeply $M->{$BB}, [$c, $b, $C, $B];
  
   my $CC = $c->representationLast;
   is_deeply $M->{$BB}, [$c, $b, $C, $B];
  
   ok $b->representationLast eq $c->representationLast;
  }
  
  if (1)                                                                          
   {my $a = Data::Edit::Xml::new(q(<a>aaaa</a>));
    ok $a->first->isOnlyChildText;
   }
  

=head2 isFirst($node, @context)

Return the specified B<$node> if it is first under its parent and optionally has the specified context, else return B<undef>

     Parameter  Description
  1  $node      Node
  2  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.

Use B<isFirstNonBlank> to skip a (rare) initial blank text CDATA. Use B<isFirstNonBlankX> to die rather
then receive a returned B<undef> or false result.



B<Example:>


    ok -p $a eq <<END;                                                                          
  <a>
    <b>
      <c id="42" match="mm"/>
    </b>
    <d>
      <e/>
    </d>
  </a>
  END
  
    ok $a->go(q(b))->𝗶𝘀𝗙𝗶𝗿𝘀𝘁;                                                     
  
   {my $a = Data::Edit::Xml::new(<<END);                                                       
  <a>
    <b>
      <c>
        <d/>
      </c>
    </b>
    <b>
      <c/>
    </b>
    <e>
      <f/>
    </e>
  </a>
  END
  
    my ($d, $c, $b, $C, $B, $f, $e) = $a->byList;                                              
  
    ok  $a->𝗶𝘀𝗙𝗶𝗿𝘀𝘁;                                                              
  

=head2 isNotFirst($node, @context)

Return the specified B<$node> if it is not first under its parent and optionally has the specified context, else return B<undef>

     Parameter  Description
  1  $node      Node
  2  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a><b/><c/><d/></a>
  END
  
    my ($b, $c, $d) = $a->byList;
    ok $c->𝗶𝘀𝗡𝗼𝘁𝗙𝗶𝗿𝘀𝘁;
    ok $b->isNotLast;
  

=head2 isFirstN($node, $N, @context)

Return the first B<$N> nodes as an array if the first B<$N> tags of the parent of B<$node> finish at the specified B<$node> and have the specified tags in the sequence specified by B<@context>. If B<@context> contains more then B<$N> entries, the remainder are checked as the context of the parent of B<$node>. Returns an array of nodes found or an empty array if the specified B<$node> does not match these conditions.

     Parameter  Description
  1  $node      Node
  2  $N         Number of tags to be first
  3  @context   First tags and optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $x = Data::Edit::Xml::new(<<END);
  <x>
    <a/>
    <b/>
    <c/>
    <d/>
    <e/>
  </x>
  END
  
    my ($a, $b, $c, $d, $e) = $x->byList;
  
    is_deeply [$b->isFirstN_2_a_b_x], [$a, $b];
    ok !$b->isFirstN_2_b_x;
  
    is_deeply [$d->isLastN_2_d_e_x], [$d, $e];
    ok !$d->isLastN_2_d_x;
  
    is_deeply [$b->nextN_3_b_c_d], [$b, $c, $d];
    is_deeply [$d->prevN_3_b_c_d], [$b, $c, $d];
  

=head2 isFirstToDepth($node, $depth, @context)

Return the specified B<$node> if it is first to the specified depth else return B<undef>

     Parameter  Description
  1  $node      Node
  2  $depth     Depth
  3  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


   {my $a = Data::Edit::Xml::new(<<END);                                              
  <a>
    <b>
      <c>
        <d/>
      </c>
    </b>
    <e>
      <f/>
    </e>
  </a>
  END
  
    my ($d, $c, $b, $f, $e) = $a->byList;                                             
  
    ok  $d->𝗶𝘀𝗙𝗶𝗿𝘀𝘁𝗧𝗼𝗗𝗲𝗽𝘁𝗵(4);                                                    
  
    ok !$f->𝗶𝘀𝗙𝗶𝗿𝘀𝘁𝗧𝗼𝗗𝗲𝗽𝘁𝗵(2);                                                    
  
    ok  $f->𝗶𝘀𝗙𝗶𝗿𝘀𝘁𝗧𝗼𝗗𝗲𝗽𝘁𝗵(1);                                                    
  
    ok !$f->𝗶𝘀𝗙𝗶𝗿𝘀𝘁𝗧𝗼𝗗𝗲𝗽𝘁𝗵(3);                                                    
  

=head2 firstIs($node, @context)

Return the specified B<$node> if it has one or more child nodes and the first child node has the specified B<@context> otherwise B<undef>.

     Parameter  Description
  1  $node      Node
  2  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b/><c/><d/><e/>
  </a>
  END
  
    ok  $a->go_c__nextIs_d;
    ok !$a->go_c__nextIs_c;
    ok  $a->go_c__prevIs_b;
    ok !$a->go_c__prevIs_e;
  
    ok  $a->firstIs_b;
    ok !$a->firstIs_c;
  
    ok  $a->lastIs_e;
    ok !$a->lastIs_d;
  

=head2 isLast($node, @context)

Return the specified B<$node> if it is last under its parent and optionally has the specified context, else return B<undef>

     Parameter  Description
  1  $node      Node
  2  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.

Use B<isLastNonBlank> to skip a (rare) initial blank text CDATA. Use B<isLastNonBlankX> to die rather
then receive a returned B<undef> or false result.



B<Example:>


    ok -p $a eq <<END;                                                                          
  <a>
    <b>
      <c id="42" match="mm"/>
    </b>
    <d>
      <e/>
    </d>
  </a>
  END
  
    ok $a->go(q(d))->𝗶𝘀𝗟𝗮𝘀𝘁;                                                      
  
   {my $a = Data::Edit::Xml::new(<<END);                                                       
  <a>
    <b>
      <c>
        <d/>
      </c>
    </b>
    <b>
      <c/>
    </b>
    <e>
      <f/>
    </e>
  </a>
  END
  
    my ($d, $c, $b, $C, $B, $f, $e) = $a->byList;                                              
  
    ok  $a->𝗶𝘀𝗟𝗮𝘀𝘁;                                                               
  

=head2 isNotLast($node, @context)

Return the specified B<$node> if it is not last under its parent and optionally has the specified context, else return B<undef>

     Parameter  Description
  1  $node      Node
  2  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a><b/><c/><d/></a>
  END
  
    my ($b, $c, $d) = $a->byList;
    ok $c->isNotFirst;
    ok $b->𝗶𝘀𝗡𝗼𝘁𝗟𝗮𝘀𝘁;
  

=head2 isLastN($node, $N, @context)

Return the last B<$N> nodes as an array if the last B<$N> tags of the parent of B<$node> start at the specified B<$node> and have the specified tags in the sequence specified by B<@context>. If B<@context> contains more then B<$N> entries, the remainder are checked as the context of the parent of B<$node>. Returns an array of nodes found or an empty array if the specified B<$node> does not match these conditions.

     Parameter  Description
  1  $node      Node
  2  $N         Number of tags to be last
  3  @context   Last tags and optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $x = Data::Edit::Xml::new(<<END);
  <x>
    <a/>
    <b/>
    <c/>
    <d/>
    <e/>
  </x>
  END
  
    my ($a, $b, $c, $d, $e) = $x->byList;
  
    is_deeply [$b->isFirstN_2_a_b_x], [$a, $b];
    ok !$b->isFirstN_2_b_x;
  
    is_deeply [$d->isLastN_2_d_e_x], [$d, $e];
    ok !$d->isLastN_2_d_x;
  
    is_deeply [$b->nextN_3_b_c_d], [$b, $c, $d];
    is_deeply [$d->prevN_3_b_c_d], [$b, $c, $d];
  

=head2 isLastToDepth($node, $depth, @context)

Return the specified B<$node> if it is last to the specified depth else return B<undef>

     Parameter  Description
  1  $node      Node
  2  $depth     Depth
  3  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


   {my $a = Data::Edit::Xml::new(<<END);                                              
  <a>
    <b>
      <c>
        <d/>
      </c>
    </b>
    <e>
      <f/>
    </e>
  </a>
  END
  
    my ($d, $c, $b, $f, $e) = $a->byList;                                             
  
    ok  $c->𝗶𝘀𝗟𝗮𝘀𝘁𝗧𝗼𝗗𝗲𝗽𝘁𝗵(1);                                                     
  
    ok !$c->𝗶𝘀𝗟𝗮𝘀𝘁𝗧𝗼𝗗𝗲𝗽𝘁𝗵(3);                                                     
  
    ok  $d->𝗶𝘀𝗟𝗮𝘀𝘁𝗧𝗼𝗗𝗲𝗽𝘁𝗵(2);                                                     
  
    ok !$d->𝗶𝘀𝗟𝗮𝘀𝘁𝗧𝗼𝗗𝗲𝗽𝘁𝗵(4);                                                     
  

=head2 lastIs($node, @context)

Return the specified B<$node> if it has one or more child nodes and the last child node has the specified B<@context> otherwise B<undef>.

     Parameter  Description
  1  $node      Node
  2  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b/><c/><d/><e/>
  </a>
  END
  
    ok  $a->go_c__nextIs_d;
    ok !$a->go_c__nextIs_c;
    ok  $a->go_c__prevIs_b;
    ok !$a->go_c__prevIs_e;
  
    ok  $a->firstIs_b;
    ok !$a->firstIs_c;
  
    ok  $a->lastIs_e;
    ok !$a->lastIs_d;
  

=head2 nextIs($node, @context)

Return the specified B<$node> if there is a following node with the specified B<@context>. Returns B<undef> if the $node is last.

     Parameter  Description
  1  $node      Node
  2  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b/><c/><d/><e/>
  </a>
  END
  
    ok  $a->go_c__nextIs_d;
    ok !$a->go_c__nextIs_c;
    ok  $a->go_c__prevIs_b;
    ok !$a->go_c__prevIs_e;
  
    ok  $a->firstIs_b;
    ok !$a->firstIs_c;
  
    ok  $a->lastIs_e;
    ok !$a->lastIs_d;
  

=head2 nextN($node, $N, @context)

Return B<$N> nodes as an array starting at B<$node> inclusive if they match the first B<$N> tags of B<@context>. If B<@context> contains more then B<$N> entries, the remainder are checked as the context of the parent of B<$node>. Returns an array of nodes found or an empty array if the specified B<$node> does not match these conditions.

     Parameter  Description
  1  $node      Node
  2  $N         Number of tags to be last
  3  @context   Last tags and optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $x = Data::Edit::Xml::new(<<END);
  <x>
    <a/>
    <b/>
    <c/>
    <d/>
    <e/>
  </x>
  END
  
    my ($a, $b, $c, $d, $e) = $x->byList;
  
    is_deeply [$b->isFirstN_2_a_b_x], [$a, $b];
    ok !$b->isFirstN_2_b_x;
  
    is_deeply [$d->isLastN_2_d_e_x], [$d, $e];
    ok !$d->isLastN_2_d_x;
  
    is_deeply [$b->nextN_3_b_c_d], [$b, $c, $d];
    is_deeply [$d->prevN_3_b_c_d], [$b, $c, $d];
  

=head2 prevIs($node, @context)

Return the specified B<$node> if there is a previous node with the specified B<@context>. Returns B<undef> if the $node is first.

     Parameter  Description
  1  $node      Node
  2  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b/><c/><d/><e/>
  </a>
  END
  
    ok  $a->go_c__nextIs_d;
    ok !$a->go_c__nextIs_c;
    ok  $a->go_c__prevIs_b;
    ok !$a->go_c__prevIs_e;
  
    ok  $a->firstIs_b;
    ok !$a->firstIs_c;
  
    ok  $a->lastIs_e;
    ok !$a->lastIs_d;
  

=head2 prevN($node, $N, @context)

Return B<$N> nodes as an array ending at B<$node> inclusive if they match the first B<$N> tags of B<@context>. If B<@context> contains more then B<$N> entries, the remainder are checked as the context of the parent of B<$node>. Returns an array of nodes found or an empty array if the specified B<$node> does not match these conditions.

     Parameter  Description
  1  $node      Node
  2  $N         Number of tags to be first
  3  @context   First tags and optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $x = Data::Edit::Xml::new(<<END);
  <x>
    <a/>
    <b/>
    <c/>
    <d/>
    <e/>
  </x>
  END
  
    my ($a, $b, $c, $d, $e) = $x->byList;
  
    is_deeply [$b->isFirstN_2_a_b_x], [$a, $b];
    ok !$b->isFirstN_2_b_x;
  
    is_deeply [$d->isLastN_2_d_e_x], [$d, $e];
    ok !$d->isLastN_2_d_x;
  
    is_deeply [$b->nextN_3_b_c_d], [$b, $c, $d];
    is_deeply [$d->prevN_3_b_c_d], [$b, $c, $d];
  

=head2 isOnlyChild($node, @context)

Return the specified B<$node> if it is the only node under its parent ignoring any surrounding blank text.

     Parameter  Description
  1  $node      Node
  2  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


   {my $a = Data::Edit::Xml::new(<<END);                                              
  <a>
    <b>
      <c>
        <d/>
      </c>
    </b>
    <e>
      <f/>
    </e>
  </a>
  END
  
    my ($d, $c, $b, $f, $e) = $a->byList;                                             
  
    ok  $d->𝗶𝘀𝗢𝗻𝗹𝘆𝗖𝗵𝗶𝗹𝗱;                                                          
  
    ok !$d->𝗶𝘀𝗢𝗻𝗹𝘆𝗖𝗵𝗶𝗹𝗱(qw(b));                                                   
  
   {my $a = Data::Edit::Xml::new(<<END);                                                       
  <a>
    <b>
      <c>
        <d/>
      </c>
    </b>
    <b>
      <c/>
    </b>
    <e>
      <f/>
    </e>
  </a>
  END
  
    my ($d, $c, $b, $C, $B, $f, $e) = $a->byList;                                              
  
    ok  $a->𝗶𝘀𝗢𝗻𝗹𝘆𝗖𝗵𝗶𝗹𝗱;                                                          
  
    my $a = Data::Edit::Xml::new(<<END);                                           
  <a>
    <b>
      <c>
        <d>
          <e/>
        </d>
      </c>
    </b>
    <f>
      <g/>
    </f>
  </a>
  END
  

=head2 isOnlyChildToDepth($node, $depth, @context)

Return the specified B<$node> if it and its ancestors are L<only children|/isOnlyChild> to the specified depth else return B<undef>. isOnlyChildToDepth(1) is the same as L<isOnlychild|/isOnlyChild>

     Parameter  Description
  1  $node      Node
  2  $depth     Depth to which each parent node must also be an only child
  3  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


   {my $a = Data::Edit::Xml::new(<<END);                                              
  <a>
    <b>
      <c>
        <d/>
      </c>
    </b>
    <e>
      <f/>
    </e>
  </a>
  END
  
    my ($d, $c, $b, $f, $e) = $a->byList;                                             
  
    ok  $d->𝗶𝘀𝗢𝗻𝗹𝘆𝗖𝗵𝗶𝗹𝗱𝗧𝗼𝗗𝗲𝗽𝘁𝗵(1, qw(d c b a));                                   
  
    ok  $d->𝗶𝘀𝗢𝗻𝗹𝘆𝗖𝗵𝗶𝗹𝗱𝗧𝗼𝗗𝗲𝗽𝘁𝗵(2, qw(d c b a));                                   
  
    ok !$d->𝗶𝘀𝗢𝗻𝗹𝘆𝗖𝗵𝗶𝗹𝗱𝗧𝗼𝗗𝗲𝗽𝘁𝗵(3, qw(d c b a));                                   
  

=head2 isOnlyChildN($node, $depth, @context)

Return the specified B<$node> if it is an only child of B<$depth> ancestors with L<only children|/hasSingleChild> in the opptional B<@context> else return B<undef>. isOnlyChildN(1) is the same as L<isOnlychild|/isOnlyChild>.

     Parameter  Description
  1  $node      Node
  2  $depth     Depth to which each parent node must also be an only child
  3  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);                                           
  <a>
    <b>
      <c>
        <d>
          <e/>
        </d>
      </c>
    </b>
    <f>
      <g/>
    </f>
  </a>
  END
  

=head2 isOnlyChildText($node, @context)

Return the specified B<$node> if it is a text node and it is an only child else and its parent is in the specified optional context else return B<undef>.

     Parameter  Description
  1  $node      Node
  2  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


  if (1)                                                                          
   {my $a = Data::Edit::Xml::new(q(<a>aaaa</a>));
    ok $a->first->𝗶𝘀𝗢𝗻𝗹𝘆𝗖𝗵𝗶𝗹𝗱𝗧𝗲𝘅𝘁;
   }
  

=head2 hasSingleChild($node, @context)

Return the only child of the specified B<$node> if the child is the only node under its parent ignoring any surrounding blank text and has the optional specified context, else return B<undef>.

     Parameter  Description
  1  $node      Node
  2  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


   {my $a = Data::Edit::Xml::new(<<END);                                           
  <a>
    <b   id="b" b="bb">
      <b id="c" c="cc"/>
    </b>
  </a>
  END
  
    my ($c, $b) = $a->byList;                                                      
  
    is_deeply [$b->id, $c->id], [qw(b c)];                                         
  
    ok $c == $b->𝗵𝗮𝘀𝗦𝗶𝗻𝗴𝗹𝗲𝗖𝗵𝗶𝗹𝗱;                                                   
  
    ok $b == $a->𝗵𝗮𝘀𝗦𝗶𝗻𝗴𝗹𝗲𝗖𝗵𝗶𝗹𝗱;                                                   
  

=head2 hasSingleChildText($node, @context)

Return the only child of the specified B<$node> if the child is a text node and the child is the only node under its parent ignoring any surrounding blank text and the child has the optional specified context, else return B<undef>.

     Parameter  Description
  1  $node      Node
  2  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>aa
    <b>bb</b>
  </a>
  END
    ok !$a->𝗵𝗮𝘀𝗦𝗶𝗻𝗴𝗹𝗲𝗖𝗵𝗶𝗹𝗱𝗧𝗲𝘅𝘁;
    ok  $a->last->𝗵𝗮𝘀𝗦𝗶𝗻𝗴𝗹𝗲𝗖𝗵𝗶𝗹𝗱𝗧𝗲𝘅𝘁;
  

=head2 hasSingleChildToDepth($node, $depth, @context)

Return the descendant of the specified B<$node> if it has single children to the specified depth in the specified optional B<@context> else return B<undef>.  L<hasSingleChildToDepth(0)|/hasSingleChildToDepth> is equivalent to L<hasSingleChild|/hasSingleChild>.

     Parameter  Description
  1  $node      Node
  2  $depth     Depth
  3  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


   {my $a = Data::Edit::Xml::new(<<END);                                                        
  <a>
    <b>
      <c/>
      <d/>
      <e>
        <j/>
      </e>
      <f/>
    </b>
    <g>
      <h>
        <i>
          <k/>
          <l/>
        </i>
      </h>
    </g>
  </a>
  END
  
    my ($c, $d, $j, $e, $f, $b, $k, $l, $i, $h, $g) = $a->byList;                             
  
    ok $h == $g->𝗵𝗮𝘀𝗦𝗶𝗻𝗴𝗹𝗲𝗖𝗵𝗶𝗹𝗱𝗧𝗼𝗗𝗲𝗽𝘁𝗵(1);                                        
  
    ok $i == $g->hasSingleChildToDepth_2_i_h_g;                                   
  
    ok      !$g->hasSingleChildToDepth_2_i_h_G;                                   
  
    ok      !$g->𝗵𝗮𝘀𝗦𝗶𝗻𝗴𝗹𝗲𝗖𝗵𝗶𝗹𝗱𝗧𝗼𝗗𝗲𝗽𝘁𝗵(0);                                        
  
    ok      !$g->𝗵𝗮𝘀𝗦𝗶𝗻𝗴𝗹𝗲𝗖𝗵𝗶𝗹𝗱𝗧𝗼𝗗𝗲𝗽𝘁𝗵(3);                                        
  
    ok $i == $i->𝗵𝗮𝘀𝗦𝗶𝗻𝗴𝗹𝗲𝗖𝗵𝗶𝗹𝗱𝗧𝗼𝗗𝗲𝗽𝘁𝗵(0);                                        
  

=head2 isEmpty($node, @context)

Confirm that the specified B<$node> is empty, that is: the specified B<$node> has no content, not even a blank string of text. To test for blank nodes, see L<isAllBlankText|/isAllBlankText>.

     Parameter  Description
  1  $node      Node
  2  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $x = Data::Edit::Xml::new(<<END);                                           
  <a>
  
  </a>
  END
  
    ok $x->𝗶𝘀𝗘𝗺𝗽𝘁𝘆;                                                               
  
   {my $a = Data::Edit::Xml::new(<<END);                                              
  <a>
    <b>
      <c>
        <d/>
      </c>
    </b>
    <e>
      <f/>
    </e>
  </a>
  END
  
    my ($d, $c, $b, $f, $e) = $a->byList;                                             
  
    ok  $d->𝗶𝘀𝗘𝗺𝗽𝘁𝘆;                                                              
  
    my $a = Data::Edit::Xml::new(q(<a><b><c/></b></a>));
  
    my ($c, $b) = $a->byList;
  
    ok $c->𝗶𝘀𝗘𝗺𝗽𝘁𝘆;
    ok $b->hasContent;
  

=head2 hasContent($node, @context)

Confirm that the specified B<$node> has content. Return the specified node if it has content else return B<undef> if it does not.

     Parameter  Description
  1  $node      Node
  2  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(q(<a><b><c/></b></a>));
  
    my ($c, $b) = $a->byList;
  
    ok $c->isEmpty;
    ok $b->𝗵𝗮𝘀𝗖𝗼𝗻𝘁𝗲𝗻𝘁;
  

=head2 over($node, $re, @context)

Confirm that the string representing the tags at the level below the specified B<$node> match a regular expression where each pair of tags is separated by a single space. Use L<contentAsTags|/contentAsTags> to visualize the tags at the next level.

     Parameter  Description
  1  $node      Node
  2  $re        Regular expression
  3  @context   Optional context.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


   {my $x = Data::Edit::Xml::new(<<END);                                                       
  <a>
    <b>
      <c/><d/><e/><f/><g/>
    </b>
  </a>
  END
  
    ok $x->go(q(b))->𝗼𝘃𝗲𝗿(qr(d.+e));                                              
  

=head2 over2($node, $re, @context)

Confirm that the string representing the tags at the level below the specified B<$node> match a regular expression where each pair of tags have two spaces between them and the first tag is preceded by a single space and the last tag is followed by a single space.  This arrangement simplifies the regular expression used to detect combinations like p+ q? . Use L<contentAsTags2|/contentAsTags2> to visualize the tags at the next level.

     Parameter  Description
  1  $node      Node
  2  $re        Regular expression
  3  @context   Optional context.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


   {my $x = Data::Edit::Xml::new(<<END);                                                       
  <a>
    <b>
      <c/><d/><e/><f/><g/>
    </b>
  </a>
  END
  
    ok $x->go(q(b))->𝗼𝘃𝗲𝗿𝟮(qr(\A c  d  e  f  g \Z));                              
  
    ok $x->go(q(b))->contentAsTags  eq q(c d e f g) ;                             
  

=head2 overAllTags($node, @tags)

Return the specified b<$node> if all of it's child nodes L<match|/atPositionMatch> the specified <@tags> else return B<undef>.

     Parameter  Description
  1  $node      Node
  2  @tags      Tags.

B<Example:>


  if (1)                                                                            
   {my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b/>
    <c/>
    <d/>
  </a>
  END
  
    ok  $a->overAllTags_b_c_d;
    ok !$a->overAllTags_b_c;
    ok !$a->overAllTags_b_c_d_e;
    ok  $a->oat_b_c_d;
    ok !$a->oat_B_c_d;
  
    ok  $a->overFirstTags_b_c_d;
    ok  $a->overFirstTags_b_c;
    ok !$a->overFirstTags_b_c_d_e;
    ok  $a->oft_b_c;
    ok !$a->oft_B_c;
  
    ok  $a->overLastTags_b_c_d;
    ok  $a->overLastTags_c_d;
    ok !$a->overLastTags_b_c_d_e;
    ok  $a->olt_c_d;
    ok !$a->olt_C_d;
   }
  

B<oat> is a synonym for L<overAllTags|/overAllTags>.


=head2 overFirstTags($node, @tags)

Return the specified b<$node> if the first of it's child nodes L<match|/atPositionMatch> the specified <@tags> else return B<undef>.

     Parameter  Description
  1  $node      Node
  2  @tags      Tags.

B<Example:>


  if (1)                                                                            
   {my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b/>
    <c/>
    <d/>
  </a>
  END
  
    ok  $a->overAllTags_b_c_d;
    ok !$a->overAllTags_b_c;
    ok !$a->overAllTags_b_c_d_e;
    ok  $a->oat_b_c_d;
    ok !$a->oat_B_c_d;
  
    ok  $a->overFirstTags_b_c_d;
    ok  $a->overFirstTags_b_c;
    ok !$a->overFirstTags_b_c_d_e;
    ok  $a->oft_b_c;
    ok !$a->oft_B_c;
  
    ok  $a->overLastTags_b_c_d;
    ok  $a->overLastTags_c_d;
    ok !$a->overLastTags_b_c_d_e;
    ok  $a->olt_c_d;
    ok !$a->olt_C_d;
   }
  

B<oft> is a synonym for L<overFirstTags|/overFirstTags>.


=head2 overLastTags($node, @tags)

Return the specified b<$node> if the last of it's child nodes L<match|/atPositionMatch> the specified <@tags> else return B<undef>.

     Parameter  Description
  1  $node      Node
  2  @tags      Tags.

B<Example:>


  if (1)                                                                            
   {my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b/>
    <c/>
    <d/>
  </a>
  END
  
    ok  $a->overAllTags_b_c_d;
    ok !$a->overAllTags_b_c;
    ok !$a->overAllTags_b_c_d_e;
    ok  $a->oat_b_c_d;
    ok !$a->oat_B_c_d;
  
    ok  $a->overFirstTags_b_c_d;
    ok  $a->overFirstTags_b_c;
    ok !$a->overFirstTags_b_c_d_e;
    ok  $a->oft_b_c;
    ok !$a->oft_B_c;
  
    ok  $a->overLastTags_b_c_d;
    ok  $a->overLastTags_c_d;
    ok !$a->overLastTags_b_c_d_e;
    ok  $a->olt_c_d;
    ok !$a->olt_C_d;
   }
  

B<olt> is a synonym for L<overLastTags|/overLastTags>.


=head2 matchAfter($node, $re, @context)

Confirm that the string representing the tags following the specified B<$node> matches a regular expression where each pair of tags is separated by a single space. Use L<contentAfterAsTags|/contentAfterAsTags> to visualize these tags.

     Parameter  Description
  1  $node      Node
  2  $re        Regular expression
  3  @context   Optional context.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


   {my $x = Data::Edit::Xml::new(<<END);                                                       
  <a>
    <b>
      <c/><d/><e/><f/><g/>
    </b>
  </a>
  END
  
    ok $x->go(qw(b e))->𝗺𝗮𝘁𝗰𝗵𝗔𝗳𝘁𝗲𝗿  (qr(\Af g\Z));                                
  

=head2 matchAfter2($node, $re, @context)

Confirm that the string representing the tags following the specified B<$node> matches a regular expression where each pair of tags have two spaces between them and the first tag is preceded by a single space and the last tag is followed by a single space.  This arrangement simplifies the regular expression used to detect combinations like p+ q? Use L<contentAfterAsTags2|/contentAfterAsTags2> to visualize these tags.

     Parameter  Description
  1  $node      Node
  2  $re        Regular expression
  3  @context   Optional context.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


   {my $x = Data::Edit::Xml::new(<<END);                                                       
  <a>
    <b>
      <c/><d/><e/><f/><g/>
    </b>
  </a>
  END
  
    ok $x->go(qw(b e))->𝗺𝗮𝘁𝗰𝗵𝗔𝗳𝘁𝗲𝗿𝟮 (qr(\A f  g \Z));                             
  

=head2 matchBefore($node, $re, @context)

Confirm that the string representing the tags preceding the specified B<$node> matches a regular expression where each pair of tags is separated by a single space. Use L<contentBeforeAsTags|/contentBeforeAsTags> to visualize these tags.

     Parameter  Description
  1  $node      Node
  2  $re        Regular expression
  3  @context   Optional context.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


   {my $x = Data::Edit::Xml::new(<<END);                                                       
  <a>
    <b>
      <c/><d/><e/><f/><g/>
    </b>
  </a>
  END
  
    ok $x->go(qw(b e))->𝗺𝗮𝘁𝗰𝗵𝗕𝗲𝗳𝗼𝗿𝗲 (qr(\Ac d\Z));                                
  

=head2 matchBefore2($node, $re, @context)

Confirm that the string representing the tags preceding the specified B<$node> matches a regular expression where each pair of tags have two spaces between them and the first tag is preceded by a single space and the last tag is followed by a single space.  This arrangement simplifies the regular expression used to detect combinations like p+ q?  Use L<contentBeforeAsTags2|/contentBeforeAsTags2> to visualize these tags.

     Parameter  Description
  1  $node      Node
  2  $re        Regular expression
  3  @context   Optional context.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


   {my $x = Data::Edit::Xml::new(<<END);                                                       
  <a>
    <b>
      <c/><d/><e/><f/><g/>
    </b>
  </a>
  END
  
    ok $x->go(qw(b e))->𝗺𝗮𝘁𝗰𝗵𝗕𝗲𝗳𝗼𝗿𝗲𝟮(qr(\A c  d \Z));                             
  

=head2 parentage($node)

Return a reference to an array of the nodes along the path from the root to the specified B<$Node> inclusive.

     Parameter  Description
  1  $node      Node.

B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b>
      <c>
        <d/>
      </c>
    </b>
  </a>
  END
  
    my ($d, $c, $b) = $a->byList;
  
    is_deeply $a->go_b_c_d__parentage, [$a, $b, $c, $d];
  

B<p> is a synonym for L<parentage|/parentage>.


=head2 path($node)

Return a list of strings representing the path to a node from the root of the parse tree which can then be reused by L<go|/go> to retrieve the node as long as the structure of the L<parse|/parse> tree has not changed along the path.

     Parameter  Description
  1  $node      Node.

B<Example:>


    my $x = Data::Edit::Xml::new(<<END);                                                  
  <a       id='a1'>
    <b     id='b1'>
      <c   id='c1'/>
      <c   id='c2'/>
      <d   id='d1'>
        <e id='e1'/>
      </d>
      <c   id='c3'/>
      <c   id='c4'/>
      <d   id='d2'>
        <e id='e2'/>
      </d>
      <c   id='c5'/>
      <c   id='c6'/>
    </b>
  </a>
  END
  
    is_deeply [$x->go(qw(b d 1 e))->𝗽𝗮𝘁𝗵], [qw(b d 1 e)];                         
  
    $x->by(sub {ok $x->go($_->𝗽𝗮𝘁𝗵) == $_});                                      
  

=head2 pathString($node)

Return a string representing the L<path|/path> to the specified B<$node> from the root of the parse tree.

     Parameter  Description
  1  $node      Node.

B<Example:>


    ok -z $a eq <<END;                                                                   
  <a id="1">
    <b id="2">
      <c id="3">
        <e id="4"/>
      </c>
      <d id="5">
        <e id="6"/>
      </d>
      <c id="7">
        <d id="8">
          <e id="9"/>
        </d>
      </c>
      <d id="10">
        <e id="11"/>
      </d>
      <c id="12">
        <d id="13">
          <e id="14"/>
        </d>
      </c>
    </b>
  </a>
  END
  
    ok $a->findByNumber(9)->𝗽𝗮𝘁𝗵𝗦𝘁𝗿𝗶𝗻𝗴 eq 'b c 1 d e';                            
  

=head2 Match

Locate adjacent nodes that match horizontally and vertically

=head3 an($node, $current, @context)

Return the next node if the specified B<$node> has the tag specified by B<$current> and the next node is in the specified B<@context>.

     Parameter  Description
  1  $node      Node
  2  $current   Tag node must match
  3  @context   Optional context of the next node.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


   {my $a = Data::Edit::Xml::new(<<END);                                                        
  <a>
    <b>
      <c/>
      <d/>
      <e>
        <j/>
      </e>
      <f/>
    </b>
    <g>
      <h>
        <i>
          <k/>
          <l/>
        </i>
      </h>
    </g>
  </a>
  END
  
    my ($c, $d, $j, $e, $f, $b, $k, $l, $i, $h, $g) = $a->byList;                             
  
    ok  $e == $d->an_d_e_b_a;                                                     
  
    ok  $f == $e->an_e;                                                           
  
    ok !$f->an_f;                                                                 
  

=head3 ap($node, $current, @context)

Return the previous node if the specified B<$node> has the tag specified by B<$current> and the previous node is in the specified B<@context>.

     Parameter  Description
  1  $node      Node
  2  $current   Tag node must match
  3  @context   Optional context of the previous node.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


   {my $a = Data::Edit::Xml::new(<<END);                                                        
  <a>
    <b>
      <c/>
      <d/>
      <e>
        <j/>
      </e>
      <f/>
    </b>
    <g>
      <h>
        <i>
          <k/>
          <l/>
        </i>
      </h>
    </g>
  </a>
  END
  
    my ($c, $d, $j, $e, $f, $b, $k, $l, $i, $h, $g) = $a->byList;                             
  
    ok  $c == $d->ap_d_c_b_a;                                                     
  
    ok  $c == $d->ap_d;                                                           
  
    ok !$c->ap_c;                                                                 
  

=head3 apn($node, $prev, $current, @context)

Return (previous node, next node) if the B<$previous> and B<$current> nodes have the specified tags and the next node is in the specified B<@context> else return B<()>.  The specified B<@context> must have at least one element otherwise B<()> is returned.

     Parameter  Description
  1  $node      Current node
  2  $prev      Tag for the previous node
  3  $current   Tag for specified node
  4  @context   Context for the next node.

Use the B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>.  If a context is supplied and
B<$node> is not in this context then this method returns an empty list B<()>
immediately.



B<Example:>


   {my $a = Data::Edit::Xml::new(<<END);                                                        
  <a>
    <b>
      <c/>
      <d/>
      <e>
        <j/>
      </e>
      <f/>
    </b>
    <g>
      <h>
        <i>
          <k/>
          <l/>
        </i>
      </h>
    </g>
  </a>
  END
  
    my ($c, $d, $j, $e, $f, $b, $k, $l, $i, $h, $g) = $a->byList;                             
  
    is_deeply[$c, $e], [$d->apn_c_d_e_b_a];                                       
  

=head3 matchesFirst($node, @sequence)

Return the specified B<$node> if its children L<match|/atPositionMatch> the specified <@sequence> forwards from the first child else return B<undef>.

     Parameter  Description
  1  $node      Node
  2  @sequence  Sequence.

B<Example:>


  if (1)                                                                             
   {my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b/>
    <c/>
    <d/>
    <e/>
    <f/>
  </a>
  END
    my ($b, $c, $d, $e, $f) = $a->byList;
  
    ok   $a->matchesFirst_b_c_d_e_f;
    ok  !$a->matchesFirst_c;
    ok   $a->matchesLast_f_e_d_c_b;
    ok  !$a->matchesLast_f_d;
    ok   $c->matchesNext_d_e_f;
    ok  !$d->matchesNext_e_f_a;
    ok   $e->matchesPrev_d_c_b;
    ok  !$e->matchesPrev_d_c_b_a;
   }
  

=head3 matchesLast($node, @sequence)

Return the specified B<$node> if its children L<match|/atPositionMatch> the specified <@sequence> backwards from the last child else return B<undef>.

     Parameter  Description
  1  $node      Node
  2  @sequence  Sequence.

B<Example:>


  if (1)                                                                             
   {my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b/>
    <c/>
    <d/>
    <e/>
    <f/>
  </a>
  END
    my ($b, $c, $d, $e, $f) = $a->byList;
  
    ok   $a->matchesFirst_b_c_d_e_f;
    ok  !$a->matchesFirst_c;
    ok   $a->matchesLast_f_e_d_c_b;
    ok  !$a->matchesLast_f_d;
    ok   $c->matchesNext_d_e_f;
    ok  !$d->matchesNext_e_f_a;
    ok   $e->matchesPrev_d_c_b;
    ok  !$e->matchesPrev_d_c_b_a;
   }
  

=head3 matchesNext($node, @sequence)

Return the specified B<$node> if its following siblings L<match|/atPositionMatch> the specified <@sequence> else return B<undef>.

     Parameter  Description
  1  $node      Node
  2  @sequence  Sequence.

B<Example:>


  if (1)                                                                             
   {my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b/>
    <c/>
    <d/>
    <e/>
    <f/>
  </a>
  END
    my ($b, $c, $d, $e, $f) = $a->byList;
  
    ok   $a->matchesFirst_b_c_d_e_f;
    ok  !$a->matchesFirst_c;
    ok   $a->matchesLast_f_e_d_c_b;
    ok  !$a->matchesLast_f_d;
    ok   $c->matchesNext_d_e_f;
    ok  !$d->matchesNext_e_f_a;
    ok   $e->matchesPrev_d_c_b;
    ok  !$e->matchesPrev_d_c_b_a;
   }
  

=head3 matchesPrev($node, @sequence)

Return the specified B<$node> if the siblings before $node L<match|/atPositionMatch> the specified <@sequence> with the first element of @sequence nearest to $node and the last element furthest else return B<undef>.

     Parameter  Description
  1  $node      Node
  2  @sequence  Sequence.

B<Example:>


  if (1)                                                                             
   {my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b/>
    <c/>
    <d/>
    <e/>
    <f/>
  </a>
  END
    my ($b, $c, $d, $e, $f) = $a->byList;
  
    ok   $a->matchesFirst_b_c_d_e_f;
    ok  !$a->matchesFirst_c;
    ok   $a->matchesLast_f_e_d_c_b;
    ok  !$a->matchesLast_f_d;
    ok   $c->matchesNext_d_e_f;
    ok  !$d->matchesNext_e_f_a;
    ok   $e->matchesPrev_d_c_b;
    ok  !$e->matchesPrev_d_c_b_a;
   }
  

=head2 Child of, Parent of, Sibling of

Nodes that are directly above, below or adjacent to another node.

=head3 parentOf($parent, $child, @context)

Returns the specified B<$parent> node if it is the parent of the specified B<$child> node and the B<$parent> node is in the specified optional context.

     Parameter  Description
  1  $parent    Parent
  2  $child     Child
  3  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


   {my $a = Data::Edit::Xml::new(<<END);                                                        
  <a>
    <b>
      <c/>
      <d/>
      <e>
        <j/>
      </e>
      <f/>
    </b>
    <g>
      <h>
        <i>
          <k/>
          <l/>
        </i>
      </h>
    </g>
  </a>
  END
  
    ok $e->𝗽𝗮𝗿𝗲𝗻𝘁𝗢𝗳($j);                                                          
  

=head3 childOf($child, $parent, @context)

Returns the specified B<$child> node if it is a child of the specified B<$parent> node and the B<$child> node is in the specified optional context.

     Parameter  Description
  1  $child     Child
  2  $parent    Parent
  3  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


   {my $a = Data::Edit::Xml::new(<<END);                                                        
  <a>
    <b>
      <c/>
      <d/>
      <e>
        <j/>
      </e>
      <f/>
    </b>
    <g>
      <h>
        <i>
          <k/>
          <l/>
        </i>
      </h>
    </g>
  </a>
  END
  
    ok $j->𝗰𝗵𝗶𝗹𝗱𝗢𝗳($e);                                                           
  

=head3 succeedingSiblingOf($child, $sibling, @context)

Returns the specified B<$child> node if it has the same parent as B<$sibling> and occurs after B<$sibling> and has the optionally specified context else returns B<undef>.

     Parameter  Description
  1  $child     Child
  2  $sibling   Sibling thought to occur before child
  3  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
   <b>
    <c/>
    <d/>
    <e/>
   </b>
   <B>
    <C/>
    <D/>
    <E/>
   </B>
  </a>
  END
  
    my ($c, $d, $e, $b, $C, $D, $B) = $a->byList;
    ok !$e->𝘀𝘂𝗰𝗰𝗲𝗲𝗱𝗶𝗻𝗴𝗦𝗶𝗯𝗹𝗶𝗻𝗴𝗢𝗳($e);
    ok  $e->𝘀𝘂𝗰𝗰𝗲𝗲𝗱𝗶𝗻𝗴𝗦𝗶𝗯𝗹𝗶𝗻𝗴𝗢𝗳($d);
    ok  $e->𝘀𝘂𝗰𝗰𝗲𝗲𝗱𝗶𝗻𝗴𝗦𝗶𝗯𝗹𝗶𝗻𝗴𝗢𝗳($c);
    ok !$e->𝘀𝘂𝗰𝗰𝗲𝗲𝗱𝗶𝗻𝗴𝗦𝗶𝗯𝗹𝗶𝗻𝗴𝗢𝗳($b);
    ok !$e->𝘀𝘂𝗰𝗰𝗲𝗲𝗱𝗶𝗻𝗴𝗦𝗶𝗯𝗹𝗶𝗻𝗴𝗢𝗳($B);
    ok !$e->𝘀𝘂𝗰𝗰𝗲𝗲𝗱𝗶𝗻𝗴𝗦𝗶𝗯𝗹𝗶𝗻𝗴𝗢𝗳($C);
  
    ok !$c->precedingSiblingOf($c);
    ok  $c->precedingSiblingOf($d);
    ok  $c->precedingSiblingOf($e);
    ok !$c->precedingSiblingOf($b);
    ok !$c->precedingSiblingOf($B);
    ok !$c->precedingSiblingOf($C);
  

=head3 precedingSiblingOf($child, $sibling, @context)

Returns the specified B<$child> node if it has the same parent as B<$sibling> and occurs before B<$sibling> and has the optionally specified context else returns B<undef>.

     Parameter  Description
  1  $child     Child
  2  $sibling   Sibling thought to occur after child
  3  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
   <b>
    <c/>
    <d/>
    <e/>
   </b>
   <B>
    <C/>
    <D/>
    <E/>
   </B>
  </a>
  END
  
    my ($c, $d, $e, $b, $C, $D, $B) = $a->byList;
    ok !$e->succeedingSiblingOf($e);
    ok  $e->succeedingSiblingOf($d);
    ok  $e->succeedingSiblingOf($c);
    ok !$e->succeedingSiblingOf($b);
    ok !$e->succeedingSiblingOf($B);
    ok !$e->succeedingSiblingOf($C);
  
    ok !$c->𝗽𝗿𝗲𝗰𝗲𝗱𝗶𝗻𝗴𝗦𝗶𝗯𝗹𝗶𝗻𝗴𝗢𝗳($c);
    ok  $c->𝗽𝗿𝗲𝗰𝗲𝗱𝗶𝗻𝗴𝗦𝗶𝗯𝗹𝗶𝗻𝗴𝗢𝗳($d);
    ok  $c->𝗽𝗿𝗲𝗰𝗲𝗱𝗶𝗻𝗴𝗦𝗶𝗯𝗹𝗶𝗻𝗴𝗢𝗳($e);
    ok !$c->𝗽𝗿𝗲𝗰𝗲𝗱𝗶𝗻𝗴𝗦𝗶𝗯𝗹𝗶𝗻𝗴𝗢𝗳($b);
    ok !$c->𝗽𝗿𝗲𝗰𝗲𝗱𝗶𝗻𝗴𝗦𝗶𝗯𝗹𝗶𝗻𝗴𝗢𝗳($B);
    ok !$c->𝗽𝗿𝗲𝗰𝗲𝗱𝗶𝗻𝗴𝗦𝗶𝗯𝗹𝗶𝗻𝗴𝗢𝗳($C);
  

=head1 Navigation

Move around in the L<parse|/parse> tree.

=head2 go($node, @path)

Return the node reached from the specified B<$node> via the specified L<path|/path>: (index positionB<?>)B<*> where index is the tag of the next node to be chosen and position is the optional zero based position within the index of those tags under the current node. Position defaults to zero if not specified. Position can also be negative to index back from the top of the index array. B<*> can be used as the last position to retrieve all nodes with the final tag.

     Parameter  Description
  1  $node      Node
  2  @path      Search specification.

B<Example:>


    my $x = Data::Edit::Xml::new(my $s = <<END);                                   
  <aa>
    <a>
      <b/>
        <c id="1"/><c id="2"/><c id="3"/><c id="4"/>
      <d/>
    </a>
  </aa>
  END
  
    ok $x->𝗴𝗼(qw(a c))   ->id == 1;                                               
  
    ok $x->𝗴𝗼(qw(a c -2))->id == 3;                                               
  
    ok $x->𝗴𝗼(qw(a c *)) == 4;                                                    
  
    ok 1234 == join '', map {$_->id} $x->𝗴𝗼(qw(a c *));                           
  

=head2 c($node, $tag)

Return an array of all the nodes with the specified tag below the specified B<$node>.

     Parameter  Description
  1  $node      Node
  2  $tag       Tag.

B<Example:>


   {my $x = Data::Edit::Xml::new(<<END);                                           
  <a>
    <b id="b1"><𝗰 id="1"/></b>
    <d id="d1"><𝗰 id="2"/></d>
    <e id="e1"><𝗰 id="3"/></e>
    <b id="b2"><𝗰 id="4"/></b>
    <d id="d2"><𝗰 id="5"/></d>
    <e id="e2"><𝗰 id="6"/></e>
  </a>
  END
  
    is_deeply [map{$_->id} $x->𝗰(q(d))],  [qw(d1 d2)];                             
  

=head2 cText($node)

Return an array of all the text nodes immediately below the specified B<$node>.

     Parameter  Description
  1  $node      Node.

B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b>b1
      <c/>
      b2
      <d/>
      b3
    </b>
    <e>
      <f/>
    </e>
  </a>
  END
  
    my (undef, $c, undef, $d, undef, $b, $f, $e) = $a->byList;
    is_deeply ["b".."f"], [map {-t $_} ($b, $c, $d, $e, $f)];
  
    ok  $b->𝗰𝗧𝗲𝘅𝘁 == 3;
  
    my @b = $b->𝗰𝗧𝗲𝘅𝘁;
    ok "b1 b2 b3" eq join " ", map {trim $_->text} @b;
    ok !$e->𝗰𝗧𝗲𝘅𝘁;
    ok !$a->𝗰𝗧𝗲𝘅𝘁;
  

=head2 findById($node, $id)

Find a node in the parse tree under the specified B<$node> with the specified B<$id>.

     Parameter  Description
  1  $node      Parse tree
  2  $id        Id desired.

B<Example:>


    ok -p $a eq <<END;                                                             
  <a id="i1">
    <b id="i2"/>
    <c id="i3"/>
    <B id="i4">
      <c id="i5"/>
    </B>
    <c id="i6"/>
    <b id="i7"/>
  </a>
  END
  
    ok -t $a->findById_i4 eq q(B);                                                
  
    ok -t $a->findById_i5 eq q(c);                                                
  

=head2 matchesNode($first, $second, @attributes)

Return the B<$first> node if it matches the B<$second> node's tag and the specified B<@attributes> else return B<undef>.

     Parameter    Description
  1  $first       First node
  2  $second      Second node
  3  @attributes  Attributes to match on

B<Example:>


  if (1)                                                                            
   {my $a = Data::Edit::Xml::new(<<END);
  <a       id="1">
    <b     id="2"   name="b">
      <c   id="3"   name="c"/>
    </b>
    <c     id="4">
      <b   id="5"   name="b">
        <c id="6"   name="c"/>
      </b>
    </c>
  </a>
  END
  
    my ($c, $b, $C, $B) = $a->byList;
    ok  $b->id == 2;
    ok  $c->id == 3;
    ok  $B->id == 5;
    ok  $C->id == 6;
    ok  $c->𝗺𝗮𝘁𝗰𝗵𝗲𝘀𝗡𝗼𝗱𝗲($C, qw(name));
    ok !$c->𝗺𝗮𝘁𝗰𝗵𝗲𝘀𝗡𝗼𝗱𝗲($C, qw(id name));
    ok  $c->matchesSubTree($C, qw(name));
    ok  $b->matchesSubTree($B, qw(name));
    ok !$c->matchesSubTree($C, qw(id name));
    ok !$b->matchesSubTree($C, qw(name));
  
    is_deeply [$a->findMatchingSubTrees($b, qw(name))], [$b, $B];
    is_deeply [$a->findMatchingSubTrees($c, qw(name))], [$c, $C];
    is_deeply [$a->findMatchingSubTrees(new(q(<c/>)))], [$c, $C];
    is_deeply [$a->findMatchingSubTrees(new(q(<b><c/></b>)))], [$b, $B];
    is_deeply [$a->findMatchingSubTrees(new(q(<b id="2"><c id="3"/></b>)), q(id))], [$b];
   }
  

=head2 matchesSubTree($first, $second, @attributes)

Return the B<$first> node if it L<matches|/matchesNode> the B<$second> node and the nodes under the first node match the corresponding nodes under the second node, else return B<undef>.

     Parameter    Description
  1  $first       First node
  2  $second      Second node
  3  @attributes  Attributes to match on

B<Example:>


  if (1)                                                                            
   {my $a = Data::Edit::Xml::new(<<END);
  <a       id="1">
    <b     id="2"   name="b">
      <c   id="3"   name="c"/>
    </b>
    <c     id="4">
      <b   id="5"   name="b">
        <c id="6"   name="c"/>
      </b>
    </c>
  </a>
  END
  
    my ($c, $b, $C, $B) = $a->byList;
    ok  $b->id == 2;
    ok  $c->id == 3;
    ok  $B->id == 5;
    ok  $C->id == 6;
    ok  $c->matchesNode($C, qw(name));
    ok !$c->matchesNode($C, qw(id name));
    ok  $c->𝗺𝗮𝘁𝗰𝗵𝗲𝘀𝗦𝘂𝗯𝗧𝗿𝗲𝗲($C, qw(name));
    ok  $b->𝗺𝗮𝘁𝗰𝗵𝗲𝘀𝗦𝘂𝗯𝗧𝗿𝗲𝗲($B, qw(name));
    ok !$c->𝗺𝗮𝘁𝗰𝗵𝗲𝘀𝗦𝘂𝗯𝗧𝗿𝗲𝗲($C, qw(id name));
    ok !$b->𝗺𝗮𝘁𝗰𝗵𝗲𝘀𝗦𝘂𝗯𝗧𝗿𝗲𝗲($C, qw(name));
  
    is_deeply [$a->findMatchingSubTrees($b, qw(name))], [$b, $B];
    is_deeply [$a->findMatchingSubTrees($c, qw(name))], [$c, $C];
    is_deeply [$a->findMatchingSubTrees(new(q(<c/>)))], [$c, $C];
    is_deeply [$a->findMatchingSubTrees(new(q(<b><c/></b>)))], [$b, $B];
    is_deeply [$a->findMatchingSubTrees(new(q(<b id="2"><c id="3"/></b>)), q(id))], [$b];
   }
  

=head2 findMatchingSubTrees($node, $subTree, @attributes)

Find nodes in the parse tree whose sub tree matches the specified B<$subTree> excluding any of the specified B<$attributes>.

     Parameter    Description
  1  $node        Parse tree
  2  $subTree     Parse tree to match
  3  @attributes  Attributes to match on

B<Example:>


  if (1)                                                                            
   {my $a = Data::Edit::Xml::new(<<END);
  <a       id="1">
    <b     id="2"   name="b">
      <c   id="3"   name="c"/>
    </b>
    <c     id="4">
      <b   id="5"   name="b">
        <c id="6"   name="c"/>
      </b>
    </c>
  </a>
  END
  
    my ($c, $b, $C, $B) = $a->byList;
    ok  $b->id == 2;
    ok  $c->id == 3;
    ok  $B->id == 5;
    ok  $C->id == 6;
    ok  $c->matchesNode($C, qw(name));
    ok !$c->matchesNode($C, qw(id name));
    ok  $c->matchesSubTree($C, qw(name));
    ok  $b->matchesSubTree($B, qw(name));
    ok !$c->matchesSubTree($C, qw(id name));
    ok !$b->matchesSubTree($C, qw(name));
  
    is_deeply [$a->𝗳𝗶𝗻𝗱𝗠𝗮𝘁𝗰𝗵𝗶𝗻𝗴𝗦𝘂𝗯𝗧𝗿𝗲𝗲𝘀($b, qw(name))], [$b, $B];
    is_deeply [$a->𝗳𝗶𝗻𝗱𝗠𝗮𝘁𝗰𝗵𝗶𝗻𝗴𝗦𝘂𝗯𝗧𝗿𝗲𝗲𝘀($c, qw(name))], [$c, $C];
    is_deeply [$a->𝗳𝗶𝗻𝗱𝗠𝗮𝘁𝗰𝗵𝗶𝗻𝗴𝗦𝘂𝗯𝗧𝗿𝗲𝗲𝘀(new(q(<c/>)))], [$c, $C];
    is_deeply [$a->𝗳𝗶𝗻𝗱𝗠𝗮𝘁𝗰𝗵𝗶𝗻𝗴𝗦𝘂𝗯𝗧𝗿𝗲𝗲𝘀(new(q(<b><c/></b>)))], [$b, $B];
    is_deeply [$a->𝗳𝗶𝗻𝗱𝗠𝗮𝘁𝗰𝗵𝗶𝗻𝗴𝗦𝘂𝗯𝗧𝗿𝗲𝗲𝘀(new(q(<b id="2"><c id="3"/></b>)), q(id))], [$b];
   }
  

=head2 First

Find nodes that are first amongst their siblings.

=head3 first($node, @context)

Return the first node below the specified B<$node> optionally checking the first node's context.  See L<addFirst|/addFirst> to ensure that an expected node is in position.

     Parameter  Description
  1  $node      Node
  2  @context   Optional context.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.

Use B<firstNonBlank> to skip a (rare) initial blank text CDATA. Use B<firstNonBlankX> to die rather
then receive a returned B<undef> or false result.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);                                                   
  <a         id="11">
    <b       id="12">
       <c    id="13"/>
       <d    id="14"/>
       <b    id="15">
          <c id="16"/>
          <d id="17"/>
          <e id="18"/>
          <f id="19"/>
          <g id="20"/>
       </b>
       <f    id="21"/>
       <g    id="22"/>
    </b>
    <b       id="23">
       <c    id="24"/>
       <d    id="25"/>
       <b    id="26">
          <c id="27"/>
          <d id="28"/>
          <e id="29"/>
          <f id="30"/>
          <g id="31"/>
       </b>
       <f    id="32"/>
       <g    id="33"/>
    </b>
  </a>
  END
  
    ok  $a->go(q(b))->𝗳𝗶𝗿𝘀𝘁->id == 13;                                            
  
    ok  $a->go(q(b))->𝗳𝗶𝗿𝘀𝘁(qw(c b a));                                           
  
    ok !$a->go(q(b))->𝗳𝗶𝗿𝘀𝘁(qw(b a));                                             
  

=head3 firstn($node, $N, @context)

Return the B<$n>'th first node below the specified B<$node> optionally checking its context or B<undef> if there is no such node.  B<firstn(1)> is identical in effect to L<first|/first>.

     Parameter  Description
  1  $node      Node
  2  $N         Number of times to go first
  3  @context   Optional context.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


  if (1)                                                                           
   {my $a = Data::Edit::Xml::new(<<END);
  <a><b><c><d/><e/><f/></c></b></a>
  END
    ok -p $a eq <<END;
  <a>
    <b>
      <c>
        <d/>
        <e/>
        <f/>
      </c>
    </b>
  </a>
  END
    ok  -t $a->firstn_0 eq q(a);
    ok  -t $a->firstn_1 eq q(b);
    ok  -t $a->firstn_2 eq q(c);
    ok  -t $a->firstn_3 eq q(d);
  
    ok  -t $a->firstn_3__nextn_0 eq q(d);
    ok  -t $a->firstn_3__nextn_1 eq q(e);
    ok  -t $a->firstn_3__nextn_2 eq q(f);
   }
  

=head3 firstText($node, @context)

Return the first node under the specified B<$node> if it is in the optional and it is a text node otherwise B<undef>.

     Parameter  Description
  1  $node      Node
  2  @context   Optional context.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new("<a>AA<b/>BB<c/>CC<d/><e/><f/>DD<g/>HH</a>");
    ok -p $a eq <<END;
  <a>AA
    <b/>
  BB
    <c/>
  CC
    <d/>
    <e/>
    <f/>
  DD
    <g/>
  HH
  </a>
  END
    ok  $a->firstText_a__text eq q(AA);
    ok !$a->go_c__firstText_c_a;
    ok !$a->go_c__firstText_c_b;
    ok  $a->lastText__text eq q(HH);
    ok  $a->lastText_a__text eq q(HH);
    ok !$a->go_c__lastText;
    ok  $a->go_c__nextText_c_a__text eq q(CC);
    ok !$a->go_e__nextText;
    ok  $a->go_c__prevText_c__text eq q(BB);
    ok !$a->go_e__prevText;
  

=head3 firstTextMatches($node, $match, @context)

Return the first node under the specified B<$node> if: it is a text mode; its text matches the specified regular expression; the specified B<$node> is in the optional specified context. Else return B<undef>.

     Parameter  Description
  1  $node      Node
  2  $match     Regular expression the text must match
  3  @context   Optional context of specified node.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


   {my $a = Data::Edit::Xml::new(<<END);                                              
  <a>
    <b>bb<c>cc</c>BB
    </b>
  </a>
  END
  
    my ($bb, $cc, $c, $BB, $b) = $a->byList;                                       
  
    ok $bb->matchesText(qr(bb));                                                  
  
    ok $b->at_b_a &&  $b->𝗳𝗶𝗿𝘀𝘁𝗧𝗲𝘅𝘁𝗠𝗮𝘁𝗰𝗵𝗲𝘀(qr(bb));                               
  
    ok                $b->𝗳𝗶𝗿𝘀𝘁𝗧𝗲𝘅𝘁𝗠𝗮𝘁𝗰𝗵𝗲𝘀(qr(bb), qw(b a));                      
  
    ok $c->at_c_b &&  $c->𝗳𝗶𝗿𝘀𝘁𝗧𝗲𝘅𝘁𝗠𝗮𝘁𝗰𝗵𝗲𝘀(qr(cc));                               
  
    ok $c->at_c_b && !$c->𝗳𝗶𝗿𝘀𝘁𝗧𝗲𝘅𝘁𝗠𝗮𝘁𝗰𝗵𝗲𝘀(qr(bb));                               
  

=head3 firstBy($node, @tags)

Return a list of the first instance of each specified tag encountered in a post-order traversal from the specified B<$node> or a hash of all first instances if no tags are specified.

     Parameter  Description
  1  $node      Node
  2  @tags      Tags to search for.

B<Example:>


    my $a = Data::Edit::Xml::new(<<END);                                                   
  <a         id="11">
    <b       id="12">
       <c    id="13"/>
       <d    id="14"/>
       <b    id="15">
          <c id="16"/>
          <d id="17"/>
          <e id="18"/>
          <f id="19"/>
          <g id="20"/>
       </b>
       <f    id="21"/>
       <g    id="22"/>
    </b>
    <b       id="23">
       <c    id="24"/>
       <d    id="25"/>
       <b    id="26">
          <c id="27"/>
          <d id="28"/>
          <e id="29"/>
          <f id="30"/>
          <g id="31"/>
       </b>
       <f    id="32"/>
       <g    id="33"/>
    </b>
  </a>
  END
  
     {my %f = $a->𝗳𝗶𝗿𝘀𝘁𝗕𝘆;                                                        
  
      ok $f{b}->id == 12;                                                         
  

=head3 firstDown($node, @tags)

Return a list of the first instance of each specified tag encountered in a pre-order traversal from the specified B<$node> or a hash of all first instances if no tags are specified.

     Parameter  Description
  1  $node      Node
  2  @tags      Tags to search for.

B<Example:>


     {my %f = $a->𝗳𝗶𝗿𝘀𝘁𝗗𝗼𝘄𝗻;                                                      
  
      ok $f{b}->id == 15;                                                         
  

=head3 firstIn($node, @tags)

Return the first child node matching one of the named tags under the specified parent node.

     Parameter  Description
  1  $node      Parent node
  2  @tags      Child tags to search for.

B<Example:>


    ok $a->prettyStringCDATA eq <<'END';                                                  
  <a><CDATA> </CDATA>
      <A/>
  <CDATA>  </CDATA>
      <C/>
  <CDATA>  </CDATA>
      <E/>
  <CDATA>  </CDATA>
      <G/>
  <CDATA>  </CDATA>
  </a>
  END
  
    ok $a->𝗳𝗶𝗿𝘀𝘁𝗜𝗻(qw(b B c C))->tag eq qq(C);                                    
  

=head3 firstNot($node, @tags)

Return the first child node that does not match any of the named B<@tags> under the specified parent B<$node>. Return B<undef> if there is no such child node.

     Parameter  Description
  1  $node      Parent node
  2  @tags      Child tags to avoid.

B<Example:>


   {my $a = Data::Edit::Xml::new(<<END);                                                 
  <a>
    <b/>
    <c/>
    <d/>
    <e/>
    <f/>
  </a>
  END
  
    my ($b, $c, $d, $e, $f) = $a->byList;                                                
  
    ok $c == $a->firstNot_a_b;                                                    
  

=head3 firstInIndex($node, @context)

Return the specified B<$node> if it is first in its index and optionally L<at|/at> the specified context else B<undef>

     Parameter  Description
  1  $node      Node
  2  @context   Optional context.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    ok -z $a eq <<END;                                                                   
  <a id="1">
    <b id="2">
      <c id="3">
        <e id="4"/>
      </c>
      <d id="5">
        <e id="6"/>
      </d>
      <c id="7">
        <d id="8">
          <e id="9"/>
        </d>
      </c>
      <d id="10">
        <e id="11"/>
      </d>
      <c id="12">
        <d id="13">
          <e id="14"/>
        </d>
      </c>
    </b>
  </a>
  END
  
    ok  $a->findByNumber (5)->𝗳𝗶𝗿𝘀𝘁𝗜𝗻𝗜𝗻𝗱𝗲𝘅;                                       
  
    ok !$a->findByNumber(7) ->𝗳𝗶𝗿𝘀𝘁𝗜𝗻𝗜𝗻𝗱𝗲𝘅;                                       
  

=head3 firstOf($node, @tags)

Return an array of the nodes that are continuously first under their specified parent node and that match the specified list of tags.

     Parameter  Description
  1  $node      Node
  2  @tags      Tags to search for.

B<Example:>


   {my $a = Data::Edit::Xml::new(<<END);                                           
  <a><b><c/><d/><d/><e/><d/><d/><c/></b></a>
  END
  
    is_deeply [qw(c d d)], [map {-t $_} $a->go(q(b))->𝗳𝗶𝗿𝘀𝘁𝗢𝗳(qw(c d))];          
  

=head3 firstWhile($node, @tags)

Go first from the specified B<$node> and continue deeper firstly as long as each first child node matches one of the specified B<@tags>. Return the deepest such node encountered or else return B<undef> if no such node is encountered.

     Parameter  Description
  1  $node      Node
  2  @tags      Tags to search for.

B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b>
      <c>
        <d>
          <e>
            <f/>
          </e>
        </d>
      </c>
    </b>
    <B>
      <C>
        <D>
          <E>
            <F/>
          </E>
        </D>
      </C>
    </B>
  </a>
  END
    my ($f, $e, $d, $c, $b, $F, $E, $D, $C, $B) = $a->byList;
  
    ok eval qq(-t \$$_ eq q($_)), $_ for qw(a b c d e f B C D E F);
  
    ok  $d == $a->firstWhile_a_d_c_b;
    ok  $f == $a->firstWhile_a_d_c_b_e_f_g_h;
    ok !$b->firstWhile_a;
  
    ok  $e == $a->firstUntil_e_d;
    ok       !$c->firstUntil_c;
    ok       !$b->firstUntil_a;
  
    ok  $D == $a->lastWhile_a_D_C_B;
    ok  $F == $a->lastWhile_a_D_C_B_E_F_G_H;
    ok !$B->lastWhile_a;
  
    ok  $E == $a->lastUntil_E_D;
    ok       !$C->lastUntil_C;
    ok !$B->lastUntil_a;
  

=head3 firstUntil($node, @context)

Go first from the specified B<$node> and continue deeper firstly until a first child node matches the specified B<@context> or return B<undef> if there is no such node.  Return the first child of the specified B<$node> if no B<@context> is specified.

     Parameter  Description
  1  $node      Node
  2  @context   Context to search for.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b>
      <c>
        <d>
          <e>
            <f/>
          </e>
        </d>
      </c>
    </b>
    <B>
      <C>
        <D>
          <E>
            <F/>
          </E>
        </D>
      </C>
    </B>
  </a>
  END
    my ($f, $e, $d, $c, $b, $F, $E, $D, $C, $B) = $a->byList;
  
    ok eval qq(-t \$$_ eq q($_)), $_ for qw(a b c d e f B C D E F);
  
    ok  $d == $a->firstWhile_a_d_c_b;
    ok  $f == $a->firstWhile_a_d_c_b_e_f_g_h;
    ok !$b->firstWhile_a;
  
    ok  $e == $a->firstUntil_e_d;
    ok       !$c->firstUntil_c;
    ok       !$b->firstUntil_a;
  
    ok  $D == $a->lastWhile_a_D_C_B;
    ok  $F == $a->lastWhile_a_D_C_B_E_F_G_H;
    ok !$B->lastWhile_a;
  
    ok  $E == $a->lastUntil_E_D;
    ok       !$C->lastUntil_C;
    ok !$B->lastUntil_a;
  

=head3 firstUntilText($node, @context)

Go first from the specified B<$node> and continue deeper firstly until a text node is encountered whose parent matches the specified B<@context> or return B<undef> if there is no such node.

     Parameter  Description
  1  $node      Node
  2  @context   Context to search for.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b>
      cccc
      <d/>
      <e/>
    </b>
    <f>
      <g/>
      hhhh
      <i/>
    </f>
    <j>
      <k/>
      <l/>
      mmmm
    </j>
  </a>
  END
  
    ok $a->𝗳𝗶𝗿𝘀𝘁𝗨𝗻𝘁𝗶𝗹𝗧𝗲𝘅𝘁->text =~ m(cccc)s;
    ok $a->lastUntilText ->text =~ m(mmmm)s;
  
  

=head3 firstContextOf($node, @context)

Return the first node encountered in the specified context in a depth first post-order traversal of the L<parse|/parse> tree.

     Parameter  Description
  1  $node      Node
  2  @context   Array of tags specifying context.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $x = Data::Edit::Xml::new(<<END);                                           
  <a        id="a1">
    <b1     id="b1">
       <c   id="c1">
         <d id="d1">DD11</d>
         <e id="e1">EE11</e>
      </c>
    </b1>
    <b2     id="b2">
       <c   id="c2">
         <d id="d2">DD22</d>
         <e id="e2">EE22</e>
      </c>
    </b2>
    <b3     id="b3">
       <c   id="c3">
         <d id="d3">DD33</d>
         <e id="e3">EE33</e>
      </c>
    </b3>
  </a>
  END
  
    ok $x->𝗳𝗶𝗿𝘀𝘁𝗖𝗼𝗻𝘁𝗲𝘅𝘁𝗢𝗳(qw(d c))         ->id     eq qq(d1);                    
  
    ok $x->𝗳𝗶𝗿𝘀𝘁𝗖𝗼𝗻𝘁𝗲𝘅𝘁𝗢𝗳(qw(e c b2))      ->id     eq qq(e2);                    
  
    ok $x->𝗳𝗶𝗿𝘀𝘁𝗖𝗼𝗻𝘁𝗲𝘅𝘁𝗢𝗳(qw(CDATA d c b2))->string eq qq(DD22);                  
  

=head3 firstSibling($node, @context)

Return the first sibling of the specified B<$node> in the optional B<@context> else B<undef>

     Parameter  Description
  1  $node      Node
  2  @context   Array of tags specifying context.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);                                                   
  <a         id="11">
    <b       id="12">
       <c    id="13"/>
       <d    id="14"/>
       <b    id="15">
          <c id="16"/>
          <d id="17"/>
          <e id="18"/>
          <f id="19"/>
          <g id="20"/>
       </b>
       <f    id="21"/>
       <g    id="22"/>
    </b>
    <b       id="23">
       <c    id="24"/>
       <d    id="25"/>
       <b    id="26">
          <c id="27"/>
          <d id="28"/>
          <e id="29"/>
          <f id="30"/>
          <g id="31"/>
       </b>
       <f    id="32"/>
       <g    id="33"/>
    </b>
  </a>
  END
  
    ok  $a->go(qw(b b))->𝗳𝗶𝗿𝘀𝘁𝗦𝗶𝗯𝗹𝗶𝗻𝗴->id == 13;                                   
  

=head2 Last

Find nodes that are last amongst their siblings.

=head3 last($node, @context)

Return the last node below the specified B<$node> optionally checking the last node's context. See L<addLast|/addLast> to ensure that an expected node is in position.

     Parameter  Description
  1  $node      Node
  2  @context   Optional context.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.

Use B<lastNonBlank> to skip a (rare) initial blank text CDATA. Use B<lastNonBlankX> to die rather
then receive a returned B<undef> or false result.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);                                                   
  <a         id="11">
    <b       id="12">
       <c    id="13"/>
       <d    id="14"/>
       <b    id="15">
          <c id="16"/>
          <d id="17"/>
          <e id="18"/>
          <f id="19"/>
          <g id="20"/>
       </b>
       <f    id="21"/>
       <g    id="22"/>
    </b>
    <b       id="23">
       <c    id="24"/>
       <d    id="25"/>
       <b    id="26">
          <c id="27"/>
          <d id="28"/>
          <e id="29"/>
          <f id="30"/>
          <g id="31"/>
       </b>
       <f    id="32"/>
       <g    id="33"/>
    </b>
  </a>
  END
  
    ok  $a->go(q(b))->𝗹𝗮𝘀𝘁 ->id == 22;                                            
  
    ok  $a->go(q(b))->𝗹𝗮𝘀𝘁(qw(g b a));                                            
  
    ok !$a->go(q(b))->𝗹𝗮𝘀𝘁(qw(b a));                                              
  
    ok !$a->go(q(b))->𝗹𝗮𝘀𝘁(qw(b a));                                              
  

=head3 lastn($node, $N, @context)

Return the B<$n>'th last node below the specified B<$node> optionally checking its context or B<undef> if there is no such node.  B<lastn(1)> is identical in effect to L<last|/last>.

     Parameter  Description
  1  $node      Node
  2  $N         Number of times to go last
  3  @context   Optional context.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


  if (1)                                                                           
   {my $a = Data::Edit::Xml::new(<<END);
  <a><b><c><d/><e/><f/></c></b>
     <B><C><D/><E/><F/></C></B></a>
  END
    ok -p $a eq <<END;
  <a>
    <b>
      <c>
        <d/>
        <e/>
        <f/>
      </c>
    </b>
    <B>
      <C>
        <D/>
        <E/>
        <F/>
      </C>
    </B>
  </a>
  END
  
    ok  -t $a->lastn_0 eq q(a);
    ok  -t $a->lastn_1 eq q(B);
    ok  -t $a->lastn_2 eq q(C);
    ok  -t $a->lastn_3 eq q(F);
  
    ok  -t $a->lastn_3__prevn_0 eq q(F);
    ok  -t $a->lastn_3__prevn_1 eq q(E);
    ok  -t $a->lastn_3__prevn_2 eq q(D);
   }
  

=head3 lastText($node, @context)

Return the last node under the specified B<$node> if it is in the optional and it is a text node otherwise B<undef>.

     Parameter  Description
  1  $node      Node
  2  @context   Optional context.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new("<a>AA<b/>BB<c/>CC<d/><e/><f/>DD<g/>HH</a>");
    ok -p $a eq <<END;
  <a>AA
    <b/>
  BB
    <c/>
  CC
    <d/>
    <e/>
    <f/>
  DD
    <g/>
  HH
  </a>
  END
    ok  $a->firstText_a__text eq q(AA);
    ok !$a->go_c__firstText_c_a;
    ok !$a->go_c__firstText_c_b;
    ok  $a->lastText__text eq q(HH);
    ok  $a->lastText_a__text eq q(HH);
    ok !$a->go_c__lastText;
    ok  $a->go_c__nextText_c_a__text eq q(CC);
    ok !$a->go_e__nextText;
    ok  $a->go_c__prevText_c__text eq q(BB);
    ok !$a->go_e__prevText;
  

=head3 lastTextMatches($node, $match, @context)

Return the last node under the specified B<$node> if: it is a text mode; its text matches the specified regular expression; the specified B<$node> is in the optional specified context. Else return B<undef>.

     Parameter  Description
  1  $node      Node
  2  $match     Regular expression the text must match
  3  @context   Optional context of specified  node.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


   {my $a = Data::Edit::Xml::new(<<END);                                              
  <a>
    <b>bb<c>cc</c>BB
    </b>
  </a>
  END
  
    my ($bb, $cc, $c, $BB, $b) = $a->byList;                                       
  
    ok $BB->matchesText(qr(BB));                                                  
  
    ok $b->at_b_a &&  $b->𝗹𝗮𝘀𝘁𝗧𝗲𝘅𝘁𝗠𝗮𝘁𝗰𝗵𝗲𝘀(qr(BB));                                
  
    ok                $b->𝗹𝗮𝘀𝘁𝗧𝗲𝘅𝘁𝗠𝗮𝘁𝗰𝗵𝗲𝘀(qr(BB), qw(b a));                       
  
    ok $c->at_c_b &&  $c->𝗹𝗮𝘀𝘁𝗧𝗲𝘅𝘁𝗠𝗮𝘁𝗰𝗵𝗲𝘀(qr(cc));                                
  
    ok $c->at_c_b && !$c->𝗹𝗮𝘀𝘁𝗧𝗲𝘅𝘁𝗠𝗮𝘁𝗰𝗵𝗲𝘀(qr(bb));                                
  

=head3 lastBy($node, @tags)

Return a list of the last instance of each specified tag encountered in a post-order traversal from the specified B<$node> or a hash of all last instances if no tags are specified.

     Parameter  Description
  1  $node      Node
  2  @tags      Tags to search for.

B<Example:>


    my $a = Data::Edit::Xml::new(<<END);                                                   
  <a         id="11">
    <b       id="12">
       <c    id="13"/>
       <d    id="14"/>
       <b    id="15">
          <c id="16"/>
          <d id="17"/>
          <e id="18"/>
          <f id="19"/>
          <g id="20"/>
       </b>
       <f    id="21"/>
       <g    id="22"/>
    </b>
    <b       id="23">
       <c    id="24"/>
       <d    id="25"/>
       <b    id="26">
          <c id="27"/>
          <d id="28"/>
          <e id="29"/>
          <f id="30"/>
          <g id="31"/>
       </b>
       <f    id="32"/>
       <g    id="33"/>
    </b>
  </a>
  END
  
     {my %l = $a->𝗹𝗮𝘀𝘁𝗕𝘆;                                                         
  
      ok $l{b}->id == 23;                                                         
  

=head3 lastDown($node, @tags)

Return a list of the last instance of each specified tag encountered in a pre-order traversal from the specified B<$node> or a hash of all last instances if no tags are specified.

     Parameter  Description
  1  $node      Node
  2  @tags      Tags to search for.

B<Example:>


     {my %l = $a->𝗹𝗮𝘀𝘁𝗗𝗼𝘄𝗻;                                                       
  
      ok $l{b}->id == 26;                                                         
  

=head3 lastIn($node, @tags)

Return the last child node matching one of the named tags under the specified parent node.

     Parameter  Description
  1  $node      Parent node
  2  @tags      Child tags to search for.

B<Example:>


    ok $a->prettyStringCDATA eq <<'END';                                                  
  <a><CDATA> </CDATA>
      <A/>
  <CDATA>  </CDATA>
      <C/>
  <CDATA>  </CDATA>
      <E/>
  <CDATA>  </CDATA>
      <G/>
  <CDATA>  </CDATA>
  </a>
  END
  
    ok $a->𝗹𝗮𝘀𝘁𝗜𝗻(qw(e E f F))->tag eq qq(E);                                     
  

=head3 lastNot($node, @tags)

Return the last child node that does not match any of the named B<@tags> under the specified parent B<$node>. Return B<undef> if there is no such child node.

     Parameter  Description
  1  $node      Parent node
  2  @tags      Child tags to avoid.

B<Example:>


   {my $a = Data::Edit::Xml::new(<<END);                                                 
  <a>
    <b/>
    <c/>
    <d/>
    <e/>
    <f/>
  </a>
  END
  
    my ($b, $c, $d, $e, $f) = $a->byList;                                                
  
    ok $d == $a->lastNot_e_f;                                                     
  

=head3 lastOf($node, @tags)

Return an array of the nodes that are continuously last under their specified parent node and that match the specified list of tags.

     Parameter  Description
  1  $node      Node
  2  @tags      Tags to search for.

B<Example:>


   {my $a = Data::Edit::Xml::new(<<END);                                           
  <a><b><c/><d/><d/><e/><d/><d/><c/></b></a>
  END
  
    is_deeply [qw(d d c)], [map {-t $_} $a->go(q(b))->𝗹𝗮𝘀𝘁𝗢𝗳 (qw(c d))];          
  

=head3 lastInIndex($node, @context)

Return the specified B<$node> if it is last in its index and optionally L<at|/at> the specified context else B<undef>

     Parameter  Description
  1  $node      Node
  2  @context   Optional context.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    ok -z $a eq <<END;                                                                   
  <a id="1">
    <b id="2">
      <c id="3">
        <e id="4"/>
      </c>
      <d id="5">
        <e id="6"/>
      </d>
      <c id="7">
        <d id="8">
          <e id="9"/>
        </d>
      </c>
      <d id="10">
        <e id="11"/>
      </d>
      <c id="12">
        <d id="13">
          <e id="14"/>
        </d>
      </c>
    </b>
  </a>
  END
  
    ok  $a->findByNumber(10)->𝗹𝗮𝘀𝘁𝗜𝗻𝗜𝗻𝗱𝗲𝘅;                                        
  
    ok !$a->findByNumber(7) ->𝗹𝗮𝘀𝘁𝗜𝗻𝗜𝗻𝗱𝗲𝘅;                                        
  

=head3 lastContextOf($node, @context)

Return the last node encountered in the specified context in a depth first reverse pre-order traversal of the L<parse|/parse> tree.

     Parameter  Description
  1  $node      Node
  2  @context   Array of tags specifying context.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $x = Data::Edit::Xml::new(<<END);                                           
  <a        id="a1">
    <b1     id="b1">
       <c   id="c1">
         <d id="d1">DD11</d>
         <e id="e1">EE11</e>
      </c>
    </b1>
    <b2     id="b2">
       <c   id="c2">
         <d id="d2">DD22</d>
         <e id="e2">EE22</e>
      </c>
    </b2>
    <b3     id="b3">
       <c   id="c3">
         <d id="d3">DD33</d>
         <e id="e3">EE33</e>
      </c>
    </b3>
  </a>
  END
  
    ok $x-> 𝗹𝗮𝘀𝘁𝗖𝗼𝗻𝘁𝗲𝘅𝘁𝗢𝗳(qw(d c))         ->id     eq qq(d3);                    
  
    ok $x-> 𝗹𝗮𝘀𝘁𝗖𝗼𝗻𝘁𝗲𝘅𝘁𝗢𝗳(qw(e c b2     )) ->id     eq qq(e2);                    
  
    ok $x-> 𝗹𝗮𝘀𝘁𝗖𝗼𝗻𝘁𝗲𝘅𝘁𝗢𝗳(qw(CDATA e c b2))->string eq qq(EE22);                  
  

=head3 lastSibling($node, @context)

Return the last sibling of the specified B<$node> in the optional B<@context> else B<undef>

     Parameter  Description
  1  $node      Node
  2  @context   Array of tags specifying context.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);                                                   
  <a         id="11">
    <b       id="12">
       <c    id="13"/>
       <d    id="14"/>
       <b    id="15">
          <c id="16"/>
          <d id="17"/>
          <e id="18"/>
          <f id="19"/>
          <g id="20"/>
       </b>
       <f    id="21"/>
       <g    id="22"/>
    </b>
    <b       id="23">
       <c    id="24"/>
       <d    id="25"/>
       <b    id="26">
          <c id="27"/>
          <d id="28"/>
          <e id="29"/>
          <f id="30"/>
          <g id="31"/>
       </b>
       <f    id="32"/>
       <g    id="33"/>
    </b>
  </a>
  END
  
    ok  $a->go(qw(b b))->𝗹𝗮𝘀𝘁𝗦𝗶𝗯𝗹𝗶𝗻𝗴 ->id == 22;                                   
  

=head3 lastWhile($node, @tags)

Go last from the specified B<$node> and continue deeper lastly as long as each last child node matches one of the specified B<@tags>. Return the deepest such node encountered or else return B<undef> if no such node is encountered.

     Parameter  Description
  1  $node      Node
  2  @tags      Tags to search for.

B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b>
      <c>
        <d>
          <e>
            <f/>
          </e>
        </d>
      </c>
    </b>
    <B>
      <C>
        <D>
          <E>
            <F/>
          </E>
        </D>
      </C>
    </B>
  </a>
  END
    my ($f, $e, $d, $c, $b, $F, $E, $D, $C, $B) = $a->byList;
  
    ok eval qq(-t \$$_ eq q($_)), $_ for qw(a b c d e f B C D E F);
  
    ok  $d == $a->firstWhile_a_d_c_b;
    ok  $f == $a->firstWhile_a_d_c_b_e_f_g_h;
    ok !$b->firstWhile_a;
  
    ok  $e == $a->firstUntil_e_d;
    ok       !$c->firstUntil_c;
    ok       !$b->firstUntil_a;
  
    ok  $D == $a->lastWhile_a_D_C_B;
    ok  $F == $a->lastWhile_a_D_C_B_E_F_G_H;
    ok !$B->lastWhile_a;
  
    ok  $E == $a->lastUntil_E_D;
    ok       !$C->lastUntil_C;
    ok !$B->lastUntil_a;
  

=head3 lastUntil($node, @context)

Go last from the specified B<$node> and continue deeper lastly until a last child node matches the specified B<@context> or return B<undef> if there is no such node.  Return the last child of the specified B<$node> if no B<@context> is specified.

     Parameter  Description
  1  $node      Node
  2  @context   Context to search for.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b>
      <c>
        <d>
          <e>
            <f/>
          </e>
        </d>
      </c>
    </b>
    <B>
      <C>
        <D>
          <E>
            <F/>
          </E>
        </D>
      </C>
    </B>
  </a>
  END
    my ($f, $e, $d, $c, $b, $F, $E, $D, $C, $B) = $a->byList;
  
    ok eval qq(-t \$$_ eq q($_)), $_ for qw(a b c d e f B C D E F);
  
    ok  $d == $a->firstWhile_a_d_c_b;
    ok  $f == $a->firstWhile_a_d_c_b_e_f_g_h;
    ok !$b->firstWhile_a;
  
    ok  $e == $a->firstUntil_e_d;
    ok       !$c->firstUntil_c;
    ok       !$b->firstUntil_a;
  
    ok  $D == $a->lastWhile_a_D_C_B;
    ok  $F == $a->lastWhile_a_D_C_B_E_F_G_H;
    ok !$B->lastWhile_a;
  
    ok  $E == $a->lastUntil_E_D;
    ok       !$C->lastUntil_C;
    ok !$B->lastUntil_a;
  

=head3 lastUntilText($node, @context)

Go last from the specified B<$node> and continue deeper lastly until a last child text node matches the specified B<@context> or return B<undef> if there is no such node.

     Parameter  Description
  1  $node      Node
  2  @context   Context to search for.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b>
      cccc
      <d/>
      <e/>
    </b>
    <f>
      <g/>
      hhhh
      <i/>
    </f>
    <j>
      <k/>
      <l/>
      mmmm
    </j>
  </a>
  END
  
    ok $a->firstUntilText->text =~ m(cccc)s;
    ok $a->𝗹𝗮𝘀𝘁𝗨𝗻𝘁𝗶𝗹𝗧𝗲𝘅𝘁 ->text =~ m(mmmm)s;
  
  

=head2 Next

Find sibling nodes after the specified B<$node>.

=head3 next($node, @context)

Return the node next to the specified B<$node>, optionally checking the next node's context. See L<addNext|/addNext> to ensure that an expected node is in position.

     Parameter  Description
  1  $node      Node
  2  @context   Optional context.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.

Use B<nextNonBlank> to skip a (rare) initial blank text CDATA. Use B<nextNonBlankX> to die rather
then receive a returned B<undef> or false result.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);                                                   
  <a         id="11">
    <b       id="12">
       <c    id="13"/>
       <d    id="14"/>
       <b    id="15">
          <c id="16"/>
          <d id="17"/>
          <e id="18"/>
          <f id="19"/>
          <g id="20"/>
       </b>
       <f    id="21"/>
       <g    id="22"/>
    </b>
    <b       id="23">
       <c    id="24"/>
       <d    id="25"/>
       <b    id="26">
          <c id="27"/>
          <d id="28"/>
          <e id="29"/>
          <f id="30"/>
          <g id="31"/>
       </b>
       <f    id="32"/>
       <g    id="33"/>
    </b>
  </a>
  END
  
    ok  $a->go(qw(b b e))->𝗻𝗲𝘅𝘁 ->id == 19;                                       
  
    ok  $a->go(qw(b b e))->𝗻𝗲𝘅𝘁(qw(f b b a));                                     
  
    ok !$a->go(qw(b b e))->𝗻𝗲𝘅𝘁(qw(f b a));                                       
  

=head3 nextn($node, $N, @context)

Return the B<$n>'th next node after the specified B<$node> optionally checking its context or B<undef> if there is no such node.  B<nextn(1)> is identical in effect to L<next|/next>.

     Parameter  Description
  1  $node      Node
  2  $N         Number of times to go next
  3  @context   Optional context.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


  if (1)                                                                           
   {my $a = Data::Edit::Xml::new(<<END);
  <a><b><c><d/><e/><f/></c></b></a>
  END
    ok -p $a eq <<END;
  <a>
    <b>
      <c>
        <d/>
        <e/>
        <f/>
      </c>
    </b>
  </a>
  END
    ok  -t $a->firstn_0 eq q(a);
    ok  -t $a->firstn_1 eq q(b);
    ok  -t $a->firstn_2 eq q(c);
    ok  -t $a->firstn_3 eq q(d);
  
    ok  -t $a->firstn_3__nextn_0 eq q(d);
    ok  -t $a->firstn_3__nextn_1 eq q(e);
    ok  -t $a->firstn_3__nextn_2 eq q(f);
   }
  

=head3 nextText($node, @context)

Return the node after the specified B<$node> if it is in the optional and it is a text node otherwise B<undef>.

     Parameter  Description
  1  $node      Node
  2  @context   Optional context.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new("<a>AA<b/>BB<c/>CC<d/><e/><f/>DD<g/>HH</a>");
    ok -p $a eq <<END;
  <a>AA
    <b/>
  BB
    <c/>
  CC
    <d/>
    <e/>
    <f/>
  DD
    <g/>
  HH
  </a>
  END
    ok  $a->firstText_a__text eq q(AA);
    ok !$a->go_c__firstText_c_a;
    ok !$a->go_c__firstText_c_b;
    ok  $a->lastText__text eq q(HH);
    ok  $a->lastText_a__text eq q(HH);
    ok !$a->go_c__lastText;
    ok  $a->go_c__nextText_c_a__text eq q(CC);
    ok !$a->go_e__nextText;
    ok  $a->go_c__prevText_c__text eq q(BB);
    ok !$a->go_e__prevText;
  

=head3 nextTextMatches($node, $match, @context)

Return the next node to the specified B<$node> if: it is a text mode; its text matches the specified regular expression; the specified B<$node> is in the optional specified context. Else return B<undef>.

     Parameter  Description
  1  $node      Node
  2  $match     Regular expression the text must match
  3  @context   Optional context of specified node.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


   {my $a = Data::Edit::Xml::new(<<END);                                              
  <a>
    <b>bb<c>cc</c>BB
    </b>
  </a>
  END
  
    ok $cc->matchesText(qr(cc));                                                   
  
    ok $c->at_c_b &&  $c->𝗻𝗲𝘅𝘁𝗧𝗲𝘅𝘁𝗠𝗮𝘁𝗰𝗵𝗲𝘀(qr(BB));                                
  
    ok $b->at_b   && !$b->𝗻𝗲𝘅𝘁𝗧𝗲𝘅𝘁𝗠𝗮𝘁𝗰𝗵𝗲𝘀(qr(BB));                                
  

=head3 nextIn($node, @tags)

Return the nearest sibling after the specified B<$node> that matches one of the named tags or B<undef> if there is no such sibling node.

     Parameter  Description
  1  $node      Node
  2  @tags      Tags to search for.

B<Example:>


    ok $a->prettyStringCDATA eq <<'END';                                                  
  <a><CDATA> </CDATA>
      <A/>
  <CDATA>  </CDATA>
      <C/>
  <CDATA>  </CDATA>
      <E/>
  <CDATA>  </CDATA>
      <G/>
  <CDATA>  </CDATA>
  </a>
  END
  
    ok $a->firstIn(qw(b B c C))->𝗻𝗲𝘅𝘁𝗜𝗻(qw(A G))->tag eq qq(G);                   
  

=head3 nextOn($node, @tags)

Step forwards as far as possible from the specified B<$node> while remaining on nodes with the specified tags. In scalar context return the last such node reached or the starting node if no such steps are possible. In array context return the start node and any following matching nodes.

     Parameter  Description
  1  $node      Start node
  2  @tags      Tags identifying nodes that can be step on to context.

B<Example:>


    ok -p $a eq <<END;                                                             
  <a>
    <b>
      <c id="1"/>
      <d id="2"/>
      <c id="3"/>
      <d id="4"/>
      <e id="5"/>
    </b>
  </a>
  END
  
    ok $c->id == 1;                                                                
  
    ok $e->id == 5;                                                                
  
    ok $c->𝗻𝗲𝘅𝘁𝗢𝗻(qw(d))  ->id == 2;                                              
  
    ok $c->𝗻𝗲𝘅𝘁𝗢𝗻(qw(c d))->id == 4;                                              
  
    ok $e->𝗻𝗲𝘅𝘁𝗢𝗻(qw(c d))     == $e;                                             
  

=head3 nextWhile($node, @tags)

Go to the next sibling of the specified B<$node> and continue forwards while the tag of each sibling node matches one of the specified B<@tags>. Return the first sibling node that does not match else B<undef> if there is no such sibling.

     Parameter  Description
  1  $node      Node
  2  @tags      Child tags to avoid.

B<Example:>


   {my $a = Data::Edit::Xml::new(<<END);                                                 
  <a>
    <b/>
    <c/>
    <d/>
    <e/>
    <f/>
  </a>
  END
  
    my ($b, $c, $d, $e, $f) = $a->byList;                                                
  
    ok $e == $b->nextWhile_c_d;                                                   
  
    ok $c == $b->𝗻𝗲𝘅𝘁𝗪𝗵𝗶𝗹𝗲;                                                       
  

=head3 nextUntil($node, @tags)

Go to the next sibling of the specified B<$node> and continue forwards until the tag of a sibling node matches one of the specified B<@tags>. Return the matching sibling node else B<undef> if there is no such sibling node.

     Parameter  Description
  1  $node      Node
  2  @tags      Tags to look for.

B<Example:>


   {my $a = Data::Edit::Xml::new(<<END);                                                 
  <a>
    <b/>
    <c/>
    <d/>
    <e/>
    <f/>
  </a>
  END
  
    my ($b, $c, $d, $e, $f) = $a->byList;                                                
  
    ok $e == $b->nextUntil_e_f;                                                   
  
    ok      !$b->𝗻𝗲𝘅𝘁𝗨𝗻𝘁𝗶𝗹;                                                       
  

=head2 Prev

Find sibling nodes before the specified B<$node>.

=head3 prev($node, @context)

Return the node before the specified B<$node>, optionally checking the previous node's context. See L<addLast|/addLast> to ensure that an expected node is in position.

     Parameter  Description
  1  $node      Node
  2  @context   Optional context.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.

Use B<prevNonBlank> to skip a (rare) initial blank text CDATA. Use B<prevNonBlankX> to die rather
then receive a returned B<undef> or false result.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);                                                   
  <a         id="11">
    <b       id="12">
       <c    id="13"/>
       <d    id="14"/>
       <b    id="15">
          <c id="16"/>
          <d id="17"/>
          <e id="18"/>
          <f id="19"/>
          <g id="20"/>
       </b>
       <f    id="21"/>
       <g    id="22"/>
    </b>
    <b       id="23">
       <c    id="24"/>
       <d    id="25"/>
       <b    id="26">
          <c id="27"/>
          <d id="28"/>
          <e id="29"/>
          <f id="30"/>
          <g id="31"/>
       </b>
       <f    id="32"/>
       <g    id="33"/>
    </b>
  </a>
  END
  
    ok  $a->go(qw(b b e))->𝗽𝗿𝗲𝘃 ->id == 17;                                       
  
    ok  $a->go(qw(b b e))->𝗽𝗿𝗲𝘃(qw(d b b a));                                     
  
    ok !$a->go(qw(b b e))->𝗽𝗿𝗲𝘃(qw(d b a));                                       
  

=head3 prevText($node, @context)

Return the node before the specified B<$node> if it is in the optional and it is a text node otherwise B<undef>.

     Parameter  Description
  1  $node      Node
  2  @context   Optional context.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new("<a>AA<b/>BB<c/>CC<d/><e/><f/>DD<g/>HH</a>");
    ok -p $a eq <<END;
  <a>AA
    <b/>
  BB
    <c/>
  CC
    <d/>
    <e/>
    <f/>
  DD
    <g/>
  HH
  </a>
  END
    ok  $a->firstText_a__text eq q(AA);
    ok !$a->go_c__firstText_c_a;
    ok !$a->go_c__firstText_c_b;
    ok  $a->lastText__text eq q(HH);
    ok  $a->lastText_a__text eq q(HH);
    ok !$a->go_c__lastText;
    ok  $a->go_c__nextText_c_a__text eq q(CC);
    ok !$a->go_e__nextText;
    ok  $a->go_c__prevText_c__text eq q(BB);
    ok !$a->go_e__prevText;
  

=head3 prevn($node, $N, @context)

Return the B<$n>'th previous node after the specified B<$node> optionally checking its context or B<undef> if there is no such node.  B<prevn(1)> is identical in effect to L<prev|/prev>.

     Parameter  Description
  1  $node      Node
  2  $N         Number of times to go prev
  3  @context   Optional context.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


  if (1)                                                                           
   {my $a = Data::Edit::Xml::new(<<END);
  <a><b><c><d/><e/><f/></c></b>
     <B><C><D/><E/><F/></C></B></a>
  END
    ok -p $a eq <<END;
  <a>
    <b>
      <c>
        <d/>
        <e/>
        <f/>
      </c>
    </b>
    <B>
      <C>
        <D/>
        <E/>
        <F/>
      </C>
    </B>
  </a>
  END
  
    ok  -t $a->lastn_0 eq q(a);
    ok  -t $a->lastn_1 eq q(B);
    ok  -t $a->lastn_2 eq q(C);
    ok  -t $a->lastn_3 eq q(F);
  
    ok  -t $a->lastn_3__prevn_0 eq q(F);
    ok  -t $a->lastn_3__prevn_1 eq q(E);
    ok  -t $a->lastn_3__prevn_2 eq q(D);
   }
  

=head3 prevTextMatches($node, $match, @context)

Return the previous node to the specified B<$node> if: it is a text mode; its text matches the specified regular expression; the specified B<$node> is in the optional specified context. Else return B<undef>.

     Parameter  Description
  1  $node      Node
  2  $match     Regular expression the text must match
  3  @context   Optional context of specified node.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


   {my $a = Data::Edit::Xml::new(<<END);                                              
  <a>
    <b>bb<c>cc</c>BB
    </b>
  </a>
  END
  
    ok $cc->matchesText(qr(cc));                                                   
  
    ok $c->at_c_b &&  $c->𝗽𝗿𝗲𝘃𝗧𝗲𝘅𝘁𝗠𝗮𝘁𝗰𝗵𝗲𝘀(qr(bb));                                
  
    ok $b->at_b   && !$b->𝗽𝗿𝗲𝘃𝗧𝗲𝘅𝘁𝗠𝗮𝘁𝗰𝗵𝗲𝘀(qr(bb));                                
  

=head3 prevIn($node, @tags)

Return the nearest sibling node before the specified B<$node> which matches one of the named tags or B<undef> if there is no such sibling node.

     Parameter  Description
  1  $node      Node
  2  @tags      Tags to search for.

B<Example:>


    ok $a->prettyStringCDATA eq <<'END';                                                  
  <a><CDATA> </CDATA>
      <A/>
  <CDATA>  </CDATA>
      <C/>
  <CDATA>  </CDATA>
      <E/>
  <CDATA>  </CDATA>
      <G/>
  <CDATA>  </CDATA>
  </a>
  END
  
    ok $a->lastIn(qw(e E f F))->𝗽𝗿𝗲𝘃𝗜𝗻(qw(A G))->tag eq qq(A);                    
  

=head3 prevOn($node, @tags)

Step backwards as far as possible while remaining on nodes with the specified tags. In scalar context return the last such node reached or the starting node if no such steps are possible. In array context return the start node and any preceding matching nodes.

     Parameter  Description
  1  $node      Start node
  2  @tags      Tags identifying nodes that can be step on to context.

B<Example:>


    ok -p $a eq <<END;                                                             
  <a>
    <b>
      <c id="1"/>
      <d id="2"/>
      <c id="3"/>
      <d id="4"/>
      <e id="5"/>
    </b>
  </a>
  END
  
    ok $c->id == 1;                                                                
  
    ok $e->id == 5;                                                                
  
    ok $e->𝗽𝗿𝗲𝘃𝗢𝗻(qw(d))  ->id == 4;                                              
  
    ok $e->𝗽𝗿𝗲𝘃𝗢𝗻(qw(c d))     == $c;                                             
  

=head3 prevWhile($node, @tags)

Go to the previous sibling of the specified B<$node> and continue backwards while the tag of each sibling node matches one of the specified B<@tags>. Return the first sibling node that does not match else B<undef> if there is no such sibling.

     Parameter  Description
  1  $node      Parent node
  2  @tags      Child tags to avoid.

B<Example:>


   {my $a = Data::Edit::Xml::new(<<END);                                                 
  <a>
    <b/>
    <c/>
    <d/>
    <e/>
    <f/>
  </a>
  END
  
    my ($b, $c, $d, $e, $f) = $a->byList;                                                
  
    ok $c == $f->prevWhile_e_d;                                                   
  
    ok $b == $c->𝗽𝗿𝗲𝘃𝗪𝗵𝗶𝗹𝗲;                                                       
  

=head3 prevUntil($node, @tags)

Go to the previous sibling of the specified B<$node> and continue backwards until the tag of a sibling node matches one of the specified B<@tags>. Return the matching sibling node else B<undef> if there is no such sibling node.

     Parameter  Description
  1  $node      Node
  2  @tags      Tags to look for.

B<Example:>


   {my $a = Data::Edit::Xml::new(<<END);                                                 
  <a>
    <b/>
    <c/>
    <d/>
    <e/>
    <f/>
  </a>
  END
  
    my ($b, $c, $d, $e, $f) = $a->byList;                                                
  
    ok $b == $f->prevUntil_a_b;                                                   
  
    ok      !$c->𝗽𝗿𝗲𝘃𝗨𝗻𝘁𝗶𝗹;                                                       
  

=head2 Up

Methods for moving up the L<parse|/parse> tree from a node.

=head3 top($node, @context)

Return the top of the parse tree containing the current B<$node> after optionally checking that the $node is in the optional B<@context>.

     Parameter  Description
  1  $node      Start node
  2  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a><b><c><d/></c></b></a>
  END
  
    $a->by(sub
     {ok $a == $_->root;
      ok $a == $_->𝘁𝗼𝗽;
     });
  

=head3 up($node, @context)

Return the parent of the current node optionally checking the parent node's context or return B<undef> if the specified B<$node> is the root of the L<parse|/parse> tree.   See L<addWrapWith|/addWrapWith> to ensure that an expected node is in position.

     Parameter  Description
  1  $node      Start node
  2  @context   Optional context of parent.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


  if (1)                                                                             
   {my $a = Data::Edit::Xml::new(<<END);
  <a><b><c><b><b><b><b><c/></b></b></b></b></c></b></a>
  END
  
    $a->numberTree;
    ok -z $a eq <<END;
  <a id="1">
    <b id="2">
      <c id="3">
        <b id="4">
          <b id="5">
            <b id="6">
              <b id="7">
                <c id="8"/>
              </b>
            </b>
          </b>
        </b>
      </c>
    </b>
  </a>
  END
  
    my $c = $a->findByNumber(8);
    ok -t $c eq q(c);
    ok  $c->up_b__number == 7;
    ok  $c->upn_2__number == 6;
    ok  $c->upWhile_b__number == 4;
    ok  $c->upWhile_a_b__number == 4;
    ok  $c->upWhile_b_c__number == 2;
  
    ok  $c->upUntil__number == 8;
    ok  $c->upUntil_b_c__number == 4;
   }
  

=head3 upn($node, $levels, @context)

Go up the specified number of levels from the specified B<$node> and return the node reached optionally checking the parent node's context or B<undef> if there is no such node.L<upn(1)|/up> is identical in effect to L<up|/up>.  Or use L<ancestry|/ancestry> to get the path back to the root node.

     Parameter  Description
  1  $node      Start node
  2  $levels    Number of levels to go up
  3  @context   Optional context.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


  if (1)                                                                             
   {my $a = Data::Edit::Xml::new(<<END);
  <a><b><c><b><b><b><b><c/></b></b></b></b></c></b></a>
  END
  
    $a->numberTree;
    ok -z $a eq <<END;
  <a id="1">
    <b id="2">
      <c id="3">
        <b id="4">
          <b id="5">
            <b id="6">
              <b id="7">
                <c id="8"/>
              </b>
            </b>
          </b>
        </b>
      </c>
    </b>
  </a>
  END
  
    my $c = $a->findByNumber(8);
    ok -t $c eq q(c);
    ok  $c->up_b__number == 7;
    ok  $c->upn_2__number == 6;
    ok  $c->upWhile_b__number == 4;
    ok  $c->upWhile_a_b__number == 4;
    ok  $c->upWhile_b_c__number == 2;
  
    ok  $c->upUntil__number == 8;
    ok  $c->upUntil_b_c__number == 4;
   }
  
  if (1)                                                                          
   {my $a = Data::Edit::Xml::new(<<END);                                          
  <a><b><c><d><e/></d></c></b></a>
  END
  
    my ($e, $d, $c, $b) = $a->byList;                                             
  
    ok $e = $e->upn_0_e_d_c_b_a;                                                  
    ok $d = $e->upn_1_d_c_b_a;                                                    
    ok $c = $e->upn_2_c_b_a;                                                      
    ok $b = $e->upn_3_b_a;                                                        
    ok $a = $e->upn_4_a;                                                          
    ok     !$e->upn_5;                                                            
  
    is_deeply [$e, $d, $c, $b, $a], [$e->ancestry];
   }
  
   {my $a = Data::Edit::Xml::new(<<END);                                          
  <a><b><c><d><e/></d></c></b></a>
  END
  
    my ($e, $d, $c, $b) = $a->byList;                                             
  
    ok $e = $e->upn_0_e_d_c_b_a;                                                  
  
    ok $d = $e->upn_1_d_c_b_a;                                                    
  
    ok $c = $e->upn_2_c_b_a;                                                      
  
    ok $b = $e->upn_3_b_a;                                                        
  
    ok $a = $e->upn_4_a;                                                          
  
    ok     !$e->upn_5;                                                            
  

=head3 upWhile($node, @tags)

Go up one level from the specified B<$node> and then continue up while each node matches on of the specified <@tags>. Return the last matching node or B<undef> if no node matched any of the specified B<@tags>.

     Parameter  Description
  1  $node      Start node
  2  @tags      Tags to match

B<Example:>


  if (1)                                                                             
   {my $a = Data::Edit::Xml::new(<<END);
  <a><b><c><b><b><b><b><c/></b></b></b></b></c></b></a>
  END
  
    $a->numberTree;
    ok -z $a eq <<END;
  <a id="1">
    <b id="2">
      <c id="3">
        <b id="4">
          <b id="5">
            <b id="6">
              <b id="7">
                <c id="8"/>
              </b>
            </b>
          </b>
        </b>
      </c>
    </b>
  </a>
  END
  
    my $c = $a->findByNumber(8);
    ok -t $c eq q(c);
    ok  $c->up_b__number == 7;
    ok  $c->upn_2__number == 6;
    ok  $c->upWhile_b__number == 4;
    ok  $c->upWhile_a_b__number == 4;
    ok  $c->upWhile_b_c__number == 2;
  
    ok  $c->upUntil__number == 8;
    ok  $c->upUntil_b_c__number == 4;
   }
  

=head3 upWhileFirst($node, @context)

Move up from the specified B<$node> as long as each node is a first node or return B<undef> if the specified B<$node> is not a first node.

     Parameter  Description
  1  $node      Start node
  2  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


   {my $a = Data::Edit::Xml::new(<<END);                                                        
  <a>
    <b>
      <c/>
      <d/>
      <e>
        <j/>
      </e>
      <f/>
    </b>
    <g>
      <h>
        <i>
          <k/>
          <l/>
        </i>
      </h>
    </g>
  </a>
  END
  
    my ($c, $d, $j, $e, $f, $b, $k, $l, $i, $h, $g) = $a->byList;                             
  
    ok  $h == $i->𝘂𝗽𝗪𝗵𝗶𝗹𝗲𝗙𝗶𝗿𝘀𝘁;                                                   
  
    ok  $a == $c->𝘂𝗽𝗪𝗵𝗶𝗹𝗲𝗙𝗶𝗿𝘀𝘁;                                                   
  
    ok !$d->𝘂𝗽𝗪𝗵𝗶𝗹𝗲𝗙𝗶𝗿𝘀𝘁;                                                         
  

=head3 upWhileLast($node, @context)

Move up from the specified B<$node> as long as each node is a last node or return B<undef> if the specified B<$node> is not a last node.

     Parameter  Description
  1  $node      Start node
  2  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


   {my $a = Data::Edit::Xml::new(<<END);                                                        
  <a>
    <b>
      <c/>
      <d/>
      <e>
        <j/>
      </e>
      <f/>
    </b>
    <g>
      <h>
        <i>
          <k/>
          <l/>
        </i>
      </h>
    </g>
  </a>
  END
  
    my ($c, $d, $j, $e, $f, $b, $k, $l, $i, $h, $g) = $a->byList;                             
  
    ok  $j == $j->𝘂𝗽𝗪𝗵𝗶𝗹𝗲𝗟𝗮𝘀𝘁;                                                    
  
    ok  $a == $l->𝘂𝗽𝗪𝗵𝗶𝗹𝗲𝗟𝗮𝘀𝘁;                                                    
  
    ok !$d->𝘂𝗽𝗪𝗵𝗶𝗹𝗲𝗟𝗮𝘀𝘁;                                                          
  
    ok  $i == $k->upUntilLast;                                                    
  

=head3 upWhileIsOnlyChild($node, @context)

Move up from the specified B<$node> as long as each node is an only child or return B<undef> if the specified B<$node> is not an only child.

     Parameter  Description
  1  $node      Start node
  2  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


   {my $a = Data::Edit::Xml::new(<<END);                                                        
  <a>
    <b>
      <c/>
      <d/>
      <e>
        <j/>
      </e>
      <f/>
    </b>
    <g>
      <h>
        <i>
          <k/>
          <l/>
        </i>
      </h>
    </g>
  </a>
  END
  
    my ($c, $d, $j, $e, $f, $b, $k, $l, $i, $h, $g) = $a->byList;                             
  
    ok  $h == $i->𝘂𝗽𝗪𝗵𝗶𝗹𝗲𝗜𝘀𝗢𝗻𝗹𝘆𝗖𝗵𝗶𝗹𝗱;                                             
  
    ok  $j == $j->𝘂𝗽𝗪𝗵𝗶𝗹𝗲𝗜𝘀𝗢𝗻𝗹𝘆𝗖𝗵𝗶𝗹𝗱;                                             
  
    ok !$d->𝘂𝗽𝗪𝗵𝗶𝗹𝗲𝗜𝘀𝗢𝗻𝗹𝘆𝗖𝗵𝗶𝗹𝗱;                                                   
  

=head3 upUntil($node, @context)

Find the first node going up from B<$node> that matches the specified B<@context>. The first such node will be the specified $node if no @context is specified or the specified $node matches the specified @context>.

     Parameter  Description
  1  $node      Start node
  2  @context   Context.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


  if (1)                                                                             
   {my $a = Data::Edit::Xml::new(<<END);
  <a><b><c><b><b><b><b><c/></b></b></b></b></c></b></a>
  END
  
    $a->numberTree;
    ok -z $a eq <<END;
  <a id="1">
    <b id="2">
      <c id="3">
        <b id="4">
          <b id="5">
            <b id="6">
              <b id="7">
                <c id="8"/>
              </b>
            </b>
          </b>
        </b>
      </c>
    </b>
  </a>
  END
  
    my $c = $a->findByNumber(8);
    ok -t $c eq q(c);
    ok  $c->up_b__number == 7;
    ok  $c->upn_2__number == 6;
    ok  $c->upWhile_b__number == 4;
    ok  $c->upWhile_a_b__number == 4;
    ok  $c->upWhile_b_c__number == 2;
  
    ok  $c->upUntil__number == 8;
    ok  $c->upUntil_b_c__number == 4;
   }
  

=head3 upUntilFirst($node, @context)

Move up from the specified B<$node> until we reach the root or a first node.

     Parameter  Description
  1  $node      Start node
  2  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


   {my $a = Data::Edit::Xml::new(<<END);                                                        
  <a>
    <b>
      <c/>
      <d/>
      <e>
        <j/>
      </e>
      <f/>
    </b>
    <g>
      <h>
        <i>
          <k/>
          <l/>
        </i>
      </h>
    </g>
  </a>
  END
  
    my ($c, $d, $j, $e, $f, $b, $k, $l, $i, $h, $g) = $a->byList;                             
  
    ok  $b == $d->𝘂𝗽𝗨𝗻𝘁𝗶𝗹𝗙𝗶𝗿𝘀𝘁;                                                   
  

=head3 upUntilLast($node, @context)

Move up from the specified B<$node> until we reach the root or a last node.

     Parameter  Description
  1  $node      Start node
  2  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


   {my $a = Data::Edit::Xml::new(<<END);                                                        
  <a>
    <b>
      <c/>
      <d/>
      <e>
        <j/>
      </e>
      <f/>
    </b>
    <g>
      <h>
        <i>
          <k/>
          <l/>
        </i>
      </h>
    </g>
  </a>
  END
  
    my ($c, $d, $j, $e, $f, $b, $k, $l, $i, $h, $g) = $a->byList;                             
  

=head3 upUntilIsOnlyChild($node, @context)

Move up from the specified B<$node> until we reach the root or another only child.

     Parameter  Description
  1  $node      Start node
  2  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


   {my $a = Data::Edit::Xml::new(<<END);                                                        
  <a>
    <b>
      <c/>
      <d/>
      <e>
        <j/>
      </e>
      <f/>
    </b>
    <g>
      <h>
        <i>
          <k/>
          <l/>
        </i>
      </h>
    </g>
  </a>
  END
  
    my ($c, $d, $j, $e, $f, $b, $k, $l, $i, $h, $g) = $a->byList;                             
  
    ok  $i == $k->𝘂𝗽𝗨𝗻𝘁𝗶𝗹𝗜𝘀𝗢𝗻𝗹𝘆𝗖𝗵𝗶𝗹𝗱;                                             
  

=head3 upThru($node, @tags)

Go up the specified path from the specified B<$node> returning the node at the top or B<undef> if no such node exists.

     Parameter  Description
  1  $node      Start node
  2  @tags      Tags identifying path.

B<Example:>


   {my $a = Data::Edit::Xml::new(<<END);                                          
  <a>
    <b>
      <c/>
      <d>
        <e/>
        <f/>
      </d>
    </b>
  </a>
  END
  
    my ($c, $e, $f, $d, $b) = $a->byList;                                         
  
    ok -t $f                eq q(f);                                              
  
    ok -t $f->𝘂𝗽𝗧𝗵𝗿𝘂        eq q(f);                                              
  
    ok -t $f->𝘂𝗽𝗧𝗵𝗿𝘂(qw(d)) eq q(d);                                              
  
    ok -t eval{$f->𝘂𝗽𝗧𝗵𝗿𝘂(qw(d))->last->prev} eq q(e);                            
  
    ok !  eval{$f->𝘂𝗽𝗧𝗵𝗿𝘂(qw(d b))->next};                                        
  

=head2 down

Methods for moving down through the L<parse|/parse> tree from a node.

=head3 downWhileFirst($node, @context)

Move down from the specified B<$node> as long as each lower node is a first node.

     Parameter  Description
  1  $node      Start node
  2  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


   {my $a = Data::Edit::Xml::new(<<END);                                                        
  <a>
    <b>
      <c/>
      <d/>
      <e>
        <j/>
      </e>
      <f/>
    </b>
    <g>
      <h>
        <i>
          <k/>
          <l/>
        </i>
      </h>
    </g>
  </a>
  END
  
    my ($c, $d, $j, $e, $f, $b, $k, $l, $i, $h, $g) = $a->byList;                             
  
    ok  $k == $g->𝗱𝗼𝘄𝗻𝗪𝗵𝗶𝗹𝗲𝗙𝗶𝗿𝘀𝘁;                                                 
  
    ok  $c == $a->𝗱𝗼𝘄𝗻𝗪𝗵𝗶𝗹𝗲𝗙𝗶𝗿𝘀𝘁;                                                 
  
    ok  $c == $c->𝗱𝗼𝘄𝗻𝗪𝗵𝗶𝗹𝗲𝗙𝗶𝗿𝘀𝘁;                                                 
  
    ok       !$d->𝗱𝗼𝘄𝗻𝗪𝗵𝗶𝗹𝗲𝗙𝗶𝗿𝘀𝘁;                                                 
  

B<firstLeaf> is a synonym for L<downWhileFirst|/downWhileFirst>.


=head3 downWhileLast($node, @context)

Move down from the specified B<$node> as long as each lower node is a last node.

     Parameter  Description
  1  $node      Start node
  2  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


   {my $a = Data::Edit::Xml::new(<<END);                                                        
  <a>
    <b>
      <c/>
      <d/>
      <e>
        <j/>
      </e>
      <f/>
    </b>
    <g>
      <h>
        <i>
          <k/>
          <l/>
        </i>
      </h>
    </g>
  </a>
  END
  
    my ($c, $d, $j, $e, $f, $b, $k, $l, $i, $h, $g) = $a->byList;                             
  
    ok  $l == $a->𝗱𝗼𝘄𝗻𝗪𝗵𝗶𝗹𝗲𝗟𝗮𝘀𝘁;                                                  
  
    ok  $l == $g->𝗱𝗼𝘄𝗻𝗪𝗵𝗶𝗹𝗲𝗟𝗮𝘀𝘁;                                                  
  
    ok       !$d->𝗱𝗼𝘄𝗻𝗪𝗵𝗶𝗹𝗲𝗟𝗮𝘀𝘁;                                                  
  

B<lastLeaf> is a synonym for L<downWhileLast|/downWhileLast>.


=head3 downWhileHasSingleChild($node, @context)

Move down from the specified B<$node> as long as it has a single child else return undef.

     Parameter  Description
  1  $node      Start node
  2  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    ok  $h == $g->𝗱𝗼𝘄𝗻𝗪𝗵𝗶𝗹𝗲𝗛𝗮𝘀𝗦𝗶𝗻𝗴𝗹𝗲𝗖𝗵𝗶𝗹𝗱;                                        
  
    ok  $h == $h->𝗱𝗼𝘄𝗻𝗪𝗵𝗶𝗹𝗲𝗛𝗮𝘀𝗦𝗶𝗻𝗴𝗹𝗲𝗖𝗵𝗶𝗹𝗱;                                        
  
    ok       !$i->𝗱𝗼𝘄𝗻𝗪𝗵𝗶𝗹𝗲𝗛𝗮𝘀𝗦𝗶𝗻𝗴𝗹𝗲𝗖𝗵𝗶𝗹𝗱;                                        
  

=head1 Editing

Edit the data in the L<parse|/parse> tree and change the structure of the L<parse|/parse> tree by L<wrapping and unwrapping|/Wrap and unwrap> nodes, by L<replacing|/Replace> nodes, by L<cutting and pasting|/Cut and Put> nodes, by L<concatenating|/Fusion> nodes, by L<splitting|/Fission> nodes, by adding new L<text|/Put as text> nodes or L<swapping|/swap> nodes.

=head2 change($node, $name, @context)

Change the name of the specified B<$node>, optionally  confirming that the B<$node> is in a specified context and return the B<$node>.

     Parameter  Description
  1  $node      Node
  2  $name      New name
  3  @context   Optional context.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


   {my $a = Data::Edit::Xml::new('<a/>');                                         
  
    $a->𝗰𝗵𝗮𝗻𝗴𝗲(qq(b));                                                            
  
    ok -s $a eq '<b/>';                                                           
  

B<cc> is a synonym for L<change|/change>.


B<ck> is a synonym for L<change|/change>.


=head2 changeKids($node, $name, @context)

Change the names of all the immediate children of the specified B<$node>, if they match the optional B<@context>, to the specified B<$tag> and return the B<$node>.

     Parameter  Description
  1  $node      Node
  2  $name      New name
  3  @context   Optional context.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b>
      <c/>
      <d/>
      <e/>
    </b>
    <f>
      <g/>
      <h>
        <i/>
      </h>
    </f>
    <j>
      <k/>
      <l>
        <m/>
      </l>
    </j>
  </a>
  END
  
    $a->𝗰𝗵𝗮𝗻𝗴𝗲𝗞𝗶𝗱𝘀(q(B), qr(\A(b|j|k|l|m)\Z), q(a));
  
    ok -p $a eq <<END;
  <a>
    <B>
      <c/>
      <d/>
      <e/>
    </B>
    <f>
      <g/>
      <h>
        <i/>
      </h>
    </f>
    <B>
      <k/>
      <l>
        <m/>
      </l>
    </B>
  </a>
  END
  
    $a->go_f__changeKids_F;
  
    ok -p $a eq <<END;
  <a>
    <B>
      <c/>
      <d/>
      <e/>
    </B>
    <f>
      <F/>
      <F>
        <i/>
      </F>
    </f>
    <B>
      <k/>
      <l>
        <m/>
      </l>
    </B>
  </a>
  END
  

=head2 changeText($node, $rf, $rt, $flags, @context)

Change the content of the specified text B<$node> that matches a regular expression B<$rf> presented as a string to a string B<$rt> in the optional B<@context> and return the specified $node else return B<undef>. For a more efficient non unitary method see L<editText>.

     Parameter  Description
  1  $node      Text node
  2  $rf        From re
  3  $rt        To string
  4  $flags     Re flags
  5  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);                                             
  <a>abcdcba</a>
  END
  

=head2 changeTextToSpace($node, $re, @context)

Change each instance of the content of the specified text B<$node> that matches a regular expression B<$re> to one space and return the specified $node else return B<undef>.

     Parameter  Description
  1  $node      Text node
  2  $re        Regular expression
  3  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);                                             
  <a>abcdcba</a>
  END
  

=head2 dupPutNext($tree, @context)

Duplicate the specified B<$tree> in the optional B<@context>, place the new tree next after $tree and return the root of the new tree on success else B<undef>.

     Parameter  Description
  1  $tree      Node
  2  @context   Optional context.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b>
      <c/>
    </b>
    <d>
      <e/>
    </d>
  </a>
  END
  
    $a->first__dupPutNext_b;
  
    ok -p $a eq <<END;
  <a>
    <b>
      <c/>
    </b>
    <b>
      <c/>
    </b>
    <d>
      <e/>
    </d>
  </a>
  END
  
    $a->last__dupPutPrev_d;
  
    ok -p $a eq <<END;
  <a>
    <b>
      <c/>
    </b>
    <b>
      <c/>
    </b>
    <d>
      <e/>
    </d>
    <d>
      <e/>
    </d>
  </a>
  END
  

B<r> is a synonym for L<dupPutNext|/dupPutNext>.


=head2 dupPutPrev($tree, @context)

Duplicate the specified B<$tree> in the optional B<@context>, place the new tree before $tree and return the root of the new tree on success else B<undef>.

     Parameter  Description
  1  $tree      Node
  2  @context   Optional context.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b>
      <c/>
    </b>
    <d>
      <e/>
    </d>
  </a>
  END
  
    $a->first__dupPutNext_b;
  
    ok -p $a eq <<END;
  <a>
    <b>
      <c/>
    </b>
    <b>
      <c/>
    </b>
    <d>
      <e/>
    </d>
  </a>
  END
  
    $a->last__dupPutPrev_d;
  
    ok -p $a eq <<END;
  <a>
    <b>
      <c/>
    </b>
    <b>
      <c/>
    </b>
    <d>
      <e/>
    </d>
    <d>
      <e/>
    </d>
  </a>
  END
  

=head2 dupPutNextN($node, $N, @context)

Duplicate the specified B<$tree> B<$N> times in the optional B<@context>, placing each copy after $tree and return the last new node created on success else B<undef>.

     Parameter  Description
  1  $node      Node
  2  $N         Number of duplications
  3  @context   Optional context.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b>
      <c/>
    </b>
    <d>
      <e/>
      <f/>
      <g/>
    </d>
  </a>
  END
  
    $a->go_b__dupPutNextN_2;
  
    ok -p $a eq <<END;
  <a>
    <b>
      <c/>
    </b>
    <b>
      <c/>
    </b>
    <b>
      <c/>
    </b>
    <d>
      <e/>
      <f/>
      <g/>
    </d>
  </a>
  END
  

B<rN> is a synonym for L<dupPutNextN|/dupPutNextN>.


=head2 setSelectionStart($node, @context)

Set the selection to start at the specified B<$node> in the optional B<@context> and return the specified B<$node> on success else B<undef>..

     Parameter  Description
  1  $node      Node
  2  @context   Optional context.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b>
      <c/>
    </b>
    <b>
      <c/>
    </b>
    <d>
      <e/>
      <f/>
      <g/>
    </d>
  </a>
  END
    $a->go_b_1__setSelectionStart;
    $a->go_d__setSelectionEnd;
    $a->first__moveSelectionBefore;
  
    ok -p $a eq <<END;
  <a>
    <b>
      <c/>
    </b>
    <d>
      <e/>
      <f/>
      <g/>
    </d>
    <b>
      <c/>
    </b>
  </a>
  END
  
    $a->go_d_e__setSelectionStart;
    $a->go_d_g__setSelectionEnd;
    $a->go_b_c__moveSelectionAfter;
    ok -p $a eq <<END;
  <a>
    <b>
      <c/>
      <e/>
      <f/>
      <g/>
    </b>
    <d/>
    <b>
      <c/>
    </b>
  </a>
  END
  
    $a->go_b_e__setSelectionStart;
    $a->go_b_f__setSelectionEnd;
    $a->last__moveSelectionFirst;
  
    ok -p $a eq <<END;
  <a>
    <b>
      <c/>
      <g/>
    </b>
    <d/>
    <b>
      <e><!-- start --></e>
      <f><!-- end --></f>
      <c/>
    </b>
  </a>
  END
  
    $a->go_d__setSelectionStart;
    $a->last__setSelectionEnd;
    $a->go_b__moveSelectionLast;
  
    ok -p $a eq <<END;
  <a>
    <b>
      <c/>
      <g/>
      <d/>
      <b>
        <e><!-- start --></e>
        <f><!-- end --></f>
        <c/>
      </b>
    </b>
  </a>
  END
  

B<ss> is a synonym for L<setSelectionStart|/setSelectionStart>.


=head2 setSelectionEnd($node, @context)

Set the selection to end at the specified B<$node> in the optional B<@context> and return the specified B<$node> on success else B<undef>..

     Parameter  Description
  1  $node      Node
  2  @context   Optional context.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b>
      <c/>
    </b>
    <b>
      <c/>
    </b>
    <d>
      <e/>
      <f/>
      <g/>
    </d>
  </a>
  END
    $a->go_b_1__setSelectionStart;
    $a->go_d__setSelectionEnd;
    $a->first__moveSelectionBefore;
  
    ok -p $a eq <<END;
  <a>
    <b>
      <c/>
    </b>
    <d>
      <e/>
      <f/>
      <g/>
    </d>
    <b>
      <c/>
    </b>
  </a>
  END
  
    $a->go_d_e__setSelectionStart;
    $a->go_d_g__setSelectionEnd;
    $a->go_b_c__moveSelectionAfter;
    ok -p $a eq <<END;
  <a>
    <b>
      <c/>
      <e/>
      <f/>
      <g/>
    </b>
    <d/>
    <b>
      <c/>
    </b>
  </a>
  END
  
    $a->go_b_e__setSelectionStart;
    $a->go_b_f__setSelectionEnd;
    $a->last__moveSelectionFirst;
  
    ok -p $a eq <<END;
  <a>
    <b>
      <c/>
      <g/>
    </b>
    <d/>
    <b>
      <e><!-- start --></e>
      <f><!-- end --></f>
      <c/>
    </b>
  </a>
  END
  
    $a->go_d__setSelectionStart;
    $a->last__setSelectionEnd;
    $a->go_b__moveSelectionLast;
  
    ok -p $a eq <<END;
  <a>
    <b>
      <c/>
      <g/>
      <d/>
      <b>
        <e><!-- start --></e>
        <f><!-- end --></f>
        <c/>
      </b>
    </b>
  </a>
  END
  

B<se> is a synonym for L<setSelectionEnd|/setSelectionEnd>.


=head2 moveSelectionFirst($node, @context)

Move the current selection (if there is one) so that it is first under the specified B<$node> in the optional B<@context> and return the specified B<$node> on success else B<undef>.

     Parameter  Description
  1  $node      Node
  2  @context   Optional context.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b>
      <c/>
    </b>
    <b>
      <c/>
    </b>
    <d>
      <e/>
      <f/>
      <g/>
    </d>
  </a>
  END
    $a->go_b_1__setSelectionStart;
    $a->go_d__setSelectionEnd;
    $a->first__moveSelectionBefore;
  
    ok -p $a eq <<END;
  <a>
    <b>
      <c/>
    </b>
    <d>
      <e/>
      <f/>
      <g/>
    </d>
    <b>
      <c/>
    </b>
  </a>
  END
  
    $a->go_d_e__setSelectionStart;
    $a->go_d_g__setSelectionEnd;
    $a->go_b_c__moveSelectionAfter;
    ok -p $a eq <<END;
  <a>
    <b>
      <c/>
      <e/>
      <f/>
      <g/>
    </b>
    <d/>
    <b>
      <c/>
    </b>
  </a>
  END
  
    $a->go_b_e__setSelectionStart;
    $a->go_b_f__setSelectionEnd;
    $a->last__moveSelectionFirst;
  
    ok -p $a eq <<END;
  <a>
    <b>
      <c/>
      <g/>
    </b>
    <d/>
    <b>
      <e><!-- start --></e>
      <f><!-- end --></f>
      <c/>
    </b>
  </a>
  END
  
    $a->go_d__setSelectionStart;
    $a->last__setSelectionEnd;
    $a->go_b__moveSelectionLast;
  
    ok -p $a eq <<END;
  <a>
    <b>
      <c/>
      <g/>
      <d/>
      <b>
        <e><!-- start --></e>
        <f><!-- end --></f>
        <c/>
      </b>
    </b>
  </a>
  END
  

B<mf> is a synonym for L<moveSelectionFirst|/moveSelectionFirst>.


=head2 moveSelectionAfter($node, @context)

Move the current selection (if there is one) after the specified B<$node> in the optional B<@context> and return the specified B<$node> on success else B<undef>.

     Parameter  Description
  1  $node      Node
  2  @context   Optional context.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b>
      <c/>
    </b>
    <b>
      <c/>
    </b>
    <d>
      <e/>
      <f/>
      <g/>
    </d>
  </a>
  END
    $a->go_b_1__setSelectionStart;
    $a->go_d__setSelectionEnd;
    $a->first__moveSelectionBefore;
  
    ok -p $a eq <<END;
  <a>
    <b>
      <c/>
    </b>
    <d>
      <e/>
      <f/>
      <g/>
    </d>
    <b>
      <c/>
    </b>
  </a>
  END
  
    $a->go_d_e__setSelectionStart;
    $a->go_d_g__setSelectionEnd;
    $a->go_b_c__moveSelectionAfter;
    ok -p $a eq <<END;
  <a>
    <b>
      <c/>
      <e/>
      <f/>
      <g/>
    </b>
    <d/>
    <b>
      <c/>
    </b>
  </a>
  END
  
    $a->go_b_e__setSelectionStart;
    $a->go_b_f__setSelectionEnd;
    $a->last__moveSelectionFirst;
  
    ok -p $a eq <<END;
  <a>
    <b>
      <c/>
      <g/>
    </b>
    <d/>
    <b>
      <e><!-- start --></e>
      <f><!-- end --></f>
      <c/>
    </b>
  </a>
  END
  
    $a->go_d__setSelectionStart;
    $a->last__setSelectionEnd;
    $a->go_b__moveSelectionLast;
  
    ok -p $a eq <<END;
  <a>
    <b>
      <c/>
      <g/>
      <d/>
      <b>
        <e><!-- start --></e>
        <f><!-- end --></f>
        <c/>
      </b>
    </b>
  </a>
  END
  

B<ma> is a synonym for L<moveSelectionAfter|/moveSelectionAfter>.


=head2 moveSelectionBefore($node, @context)

Move the current selection (if there is one) before the specified B<$node> in the optional B<@context> and return the specified B<$node> on success else B<undef>.

     Parameter  Description
  1  $node      Node
  2  @context   Optional context.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b>
      <c/>
    </b>
    <b>
      <c/>
    </b>
    <d>
      <e/>
      <f/>
      <g/>
    </d>
  </a>
  END
    $a->go_b_1__setSelectionStart;
    $a->go_d__setSelectionEnd;
    $a->first__moveSelectionBefore;
  
    ok -p $a eq <<END;
  <a>
    <b>
      <c/>
    </b>
    <d>
      <e/>
      <f/>
      <g/>
    </d>
    <b>
      <c/>
    </b>
  </a>
  END
  
    $a->go_d_e__setSelectionStart;
    $a->go_d_g__setSelectionEnd;
    $a->go_b_c__moveSelectionAfter;
    ok -p $a eq <<END;
  <a>
    <b>
      <c/>
      <e/>
      <f/>
      <g/>
    </b>
    <d/>
    <b>
      <c/>
    </b>
  </a>
  END
  
    $a->go_b_e__setSelectionStart;
    $a->go_b_f__setSelectionEnd;
    $a->last__moveSelectionFirst;
  
    ok -p $a eq <<END;
  <a>
    <b>
      <c/>
      <g/>
    </b>
    <d/>
    <b>
      <e><!-- start --></e>
      <f><!-- end --></f>
      <c/>
    </b>
  </a>
  END
  
    $a->go_d__setSelectionStart;
    $a->last__setSelectionEnd;
    $a->go_b__moveSelectionLast;
  
    ok -p $a eq <<END;
  <a>
    <b>
      <c/>
      <g/>
      <d/>
      <b>
        <e><!-- start --></e>
        <f><!-- end --></f>
        <c/>
      </b>
    </b>
  </a>
  END
  

B<mb> is a synonym for L<moveSelectionBefore|/moveSelectionBefore>.


=head2 moveSelectionLast($node, @context)

Move the current selection (if there is one) so that it is last under the specified B<$node> in the optional B<@context> and return the specified B<$node> on success else B<undef>.

     Parameter  Description
  1  $node      Node
  2  @context   Optional context.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b>
      <c/>
    </b>
    <b>
      <c/>
    </b>
    <d>
      <e/>
      <f/>
      <g/>
    </d>
  </a>
  END
    $a->go_b_1__setSelectionStart;
    $a->go_d__setSelectionEnd;
    $a->first__moveSelectionBefore;
  
    ok -p $a eq <<END;
  <a>
    <b>
      <c/>
    </b>
    <d>
      <e/>
      <f/>
      <g/>
    </d>
    <b>
      <c/>
    </b>
  </a>
  END
  
    $a->go_d_e__setSelectionStart;
    $a->go_d_g__setSelectionEnd;
    $a->go_b_c__moveSelectionAfter;
    ok -p $a eq <<END;
  <a>
    <b>
      <c/>
      <e/>
      <f/>
      <g/>
    </b>
    <d/>
    <b>
      <c/>
    </b>
  </a>
  END
  
    $a->go_b_e__setSelectionStart;
    $a->go_b_f__setSelectionEnd;
    $a->last__moveSelectionFirst;
  
    ok -p $a eq <<END;
  <a>
    <b>
      <c/>
      <g/>
    </b>
    <d/>
    <b>
      <e><!-- start --></e>
      <f><!-- end --></f>
      <c/>
    </b>
  </a>
  END
  
    $a->go_d__setSelectionStart;
    $a->last__setSelectionEnd;
    $a->go_b__moveSelectionLast;
  
    ok -p $a eq <<END;
  <a>
    <b>
      <c/>
      <g/>
      <d/>
      <b>
        <e><!-- start --></e>
        <f><!-- end --></f>
        <c/>
      </b>
    </b>
  </a>
  END
  

B<ml> is a synonym for L<moveSelectionLast|/moveSelectionLast>.


=head2 Cut and Put

Cut and paste nodes in the L<parse|/parse> tree.

=head3 cut($node, @context)

Cut out and return the specified B<$node> so that it can be reinserted else where in the L<parse|/parse> tree.

     Parameter  Description
  1  $node      Node to cut out
  2  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


      ok -p $a eq <<END;                                                            
  <a id="aa">
    <b id="bb">
      <c id="cc"/>
    </b>
  </a>
  END
  
      my $c = $a->go(qw(b c))->𝗰𝘂𝘁;                                                
  
      ok -p $a eq <<END;                                                          
  <a id="aa">
    <b id="bb"/>
  </a>
  END
  

B<x> is a synonym for L<cut|/cut>.


=head3 cutFirst($node, @context)

Cut out the first node below the specified B<$node> if it is in the optional B<@context> and push it on the cut out stack. Return $node on success else return B<undef>. The cut out node can be reinserted using one of the putCutOut(First|Last|Next|Prev) methods.

     Parameter  Description
  1  $node      Node
  2  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b/>
    <c/>
    <d/>
    <e/>
    <f/>
  </a>
  END
  
    my ($b, $c, $d, $e, $f) = $a->byList;
    $b->cutNext_c_a;
    ok -p $a eq <<END;
  <a>
    <b/>
    <d/>
    <e/>
    <f/>
  </a>
  END
  
    $f->cutPrev_e_a;
    ok -p $a eq <<END;
  <a>
    <b/>
    <d/>
    <f/>
  </a>
  END
    $a->cutFirst_b;
    ok -p $a eq <<END;
  <a>
    <d/>
    <f/>
  </a>
  END
    $a->cutLast_f;
    ok -p $a eq <<END;
  <a>
    <d/>
  </a>
  END
  
    ok @saveLastCutOut == 4;
  
    $a->putCutOutFirst;
    ok -p $a eq <<END;
  <a>
    <f/>
    <d/>
  </a>
  END
  
    $a->putCutOutLast;
    ok -p $a eq <<END;
  <a>
    <f/>
    <d/>
    <b/>
  </a>
  END
  
    $d->putCutOutPrev;
    ok -p $a eq <<END;
  <a>
    <f/>
    <e/>
    <d/>
    <b/>
  </a>
  END
  
    $d->putCutOutNext;
    ok -p $a eq <<END;
  <a>
    <f/>
    <e/>
    <d/>
    <c/>
    <b/>
  </a>
  END
  
  

=head3 putCutOutFirst($node, @context)

Pop the last node placed on the cut out stack by one of the cut(First|Last|Next|Prev) methods and place it first under the specified B<$node> if $node is in the optional B<@context>. Return $node on success else return B<undef>.

     Parameter  Description
  1  $node      Node
  2  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b/>
    <c/>
    <d/>
    <e/>
    <f/>
  </a>
  END
  
    my ($b, $c, $d, $e, $f) = $a->byList;
    $b->cutNext_c_a;
    ok -p $a eq <<END;
  <a>
    <b/>
    <d/>
    <e/>
    <f/>
  </a>
  END
  
    $f->cutPrev_e_a;
    ok -p $a eq <<END;
  <a>
    <b/>
    <d/>
    <f/>
  </a>
  END
    $a->cutFirst_b;
    ok -p $a eq <<END;
  <a>
    <d/>
    <f/>
  </a>
  END
    $a->cutLast_f;
    ok -p $a eq <<END;
  <a>
    <d/>
  </a>
  END
  
    ok @saveLastCutOut == 4;
  
    $a->𝗽𝘂𝘁𝗖𝘂𝘁𝗢𝘂𝘁𝗙𝗶𝗿𝘀𝘁;
    ok -p $a eq <<END;
  <a>
    <f/>
    <d/>
  </a>
  END
  
    $a->putCutOutLast;
    ok -p $a eq <<END;
  <a>
    <f/>
    <d/>
    <b/>
  </a>
  END
  
    $d->putCutOutPrev;
    ok -p $a eq <<END;
  <a>
    <f/>
    <e/>
    <d/>
    <b/>
  </a>
  END
  
    $d->putCutOutNext;
    ok -p $a eq <<END;
  <a>
    <f/>
    <e/>
    <d/>
    <c/>
    <b/>
  </a>
  END
  
  

=head3 cutLast($node, @context)

Cut out the last node below the specified B<$node> if it is in the optional B<@context> and push it on the cut out stack. Return $node on success else return B<undef>.  The cut out node can be reinserted using one of the putCutOut(First|Last|Next|Prev) methods.

     Parameter  Description
  1  $node      Node
  2  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b/>
    <c/>
    <d/>
    <e/>
    <f/>
  </a>
  END
  
    my ($b, $c, $d, $e, $f) = $a->byList;
    $b->cutNext_c_a;
    ok -p $a eq <<END;
  <a>
    <b/>
    <d/>
    <e/>
    <f/>
  </a>
  END
  
    $f->cutPrev_e_a;
    ok -p $a eq <<END;
  <a>
    <b/>
    <d/>
    <f/>
  </a>
  END
    $a->cutFirst_b;
    ok -p $a eq <<END;
  <a>
    <d/>
    <f/>
  </a>
  END
    $a->cutLast_f;
    ok -p $a eq <<END;
  <a>
    <d/>
  </a>
  END
  
    ok @saveLastCutOut == 4;
  
    $a->putCutOutFirst;
    ok -p $a eq <<END;
  <a>
    <f/>
    <d/>
  </a>
  END
  
    $a->putCutOutLast;
    ok -p $a eq <<END;
  <a>
    <f/>
    <d/>
    <b/>
  </a>
  END
  
    $d->putCutOutPrev;
    ok -p $a eq <<END;
  <a>
    <f/>
    <e/>
    <d/>
    <b/>
  </a>
  END
  
    $d->putCutOutNext;
    ok -p $a eq <<END;
  <a>
    <f/>
    <e/>
    <d/>
    <c/>
    <b/>
  </a>
  END
  
  

=head3 putCutOutLast($node, @context)

Pop the last node placed on the cut out stack by one of the cut(First|Last|Next|Prev) methods and place it last under the specified B<$node> if $node is in the optional B<@context>. Return $node on success else return B<undef>.

     Parameter  Description
  1  $node      Node
  2  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b/>
    <c/>
    <d/>
    <e/>
    <f/>
  </a>
  END
  
    my ($b, $c, $d, $e, $f) = $a->byList;
    $b->cutNext_c_a;
    ok -p $a eq <<END;
  <a>
    <b/>
    <d/>
    <e/>
    <f/>
  </a>
  END
  
    $f->cutPrev_e_a;
    ok -p $a eq <<END;
  <a>
    <b/>
    <d/>
    <f/>
  </a>
  END
    $a->cutFirst_b;
    ok -p $a eq <<END;
  <a>
    <d/>
    <f/>
  </a>
  END
    $a->cutLast_f;
    ok -p $a eq <<END;
  <a>
    <d/>
  </a>
  END
  
    ok @saveLastCutOut == 4;
  
    $a->putCutOutFirst;
    ok -p $a eq <<END;
  <a>
    <f/>
    <d/>
  </a>
  END
  
    $a->𝗽𝘂𝘁𝗖𝘂𝘁𝗢𝘂𝘁𝗟𝗮𝘀𝘁;
    ok -p $a eq <<END;
  <a>
    <f/>
    <d/>
    <b/>
  </a>
  END
  
    $d->putCutOutPrev;
    ok -p $a eq <<END;
  <a>
    <f/>
    <e/>
    <d/>
    <b/>
  </a>
  END
  
    $d->putCutOutNext;
    ok -p $a eq <<END;
  <a>
    <f/>
    <e/>
    <d/>
    <c/>
    <b/>
  </a>
  END
  
  

=head3 cutNext($node, @context)

Cut out the next node beyond the specified B<$node> if it is in the optional B<@context> and push it on the cut out stack. Return the current node on success else return B<undef>.  The cut out node can be reinserted using one of the putCutOut(First|Last|Next|Prev) methods.

     Parameter  Description
  1  $node      Node
  2  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b/>
    <c/>
    <d/>
    <e/>
    <f/>
  </a>
  END
  
    my ($b, $c, $d, $e, $f) = $a->byList;
    $b->cutNext_c_a;
    ok -p $a eq <<END;
  <a>
    <b/>
    <d/>
    <e/>
    <f/>
  </a>
  END
  
    $f->cutPrev_e_a;
    ok -p $a eq <<END;
  <a>
    <b/>
    <d/>
    <f/>
  </a>
  END
    $a->cutFirst_b;
    ok -p $a eq <<END;
  <a>
    <d/>
    <f/>
  </a>
  END
    $a->cutLast_f;
    ok -p $a eq <<END;
  <a>
    <d/>
  </a>
  END
  
    ok @saveLastCutOut == 4;
  
    $a->putCutOutFirst;
    ok -p $a eq <<END;
  <a>
    <f/>
    <d/>
  </a>
  END
  
    $a->putCutOutLast;
    ok -p $a eq <<END;
  <a>
    <f/>
    <d/>
    <b/>
  </a>
  END
  
    $d->putCutOutPrev;
    ok -p $a eq <<END;
  <a>
    <f/>
    <e/>
    <d/>
    <b/>
  </a>
  END
  
    $d->putCutOutNext;
    ok -p $a eq <<END;
  <a>
    <f/>
    <e/>
    <d/>
    <c/>
    <b/>
  </a>
  END
  
  

=head3 putCutOutNext($node, @context)

Pop the last node placed on the cut out stack by one of the cut(First|Last|Next|Prev) methods and place it next after the specified B<$node> if $node is in the optional B<@context>. Return $node on success else return B<undef>.

     Parameter  Description
  1  $node      Node
  2  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b/>
    <c/>
    <d/>
    <e/>
    <f/>
  </a>
  END
  
    my ($b, $c, $d, $e, $f) = $a->byList;
    $b->cutNext_c_a;
    ok -p $a eq <<END;
  <a>
    <b/>
    <d/>
    <e/>
    <f/>
  </a>
  END
  
    $f->cutPrev_e_a;
    ok -p $a eq <<END;
  <a>
    <b/>
    <d/>
    <f/>
  </a>
  END
    $a->cutFirst_b;
    ok -p $a eq <<END;
  <a>
    <d/>
    <f/>
  </a>
  END
    $a->cutLast_f;
    ok -p $a eq <<END;
  <a>
    <d/>
  </a>
  END
  
    ok @saveLastCutOut == 4;
  
    $a->putCutOutFirst;
    ok -p $a eq <<END;
  <a>
    <f/>
    <d/>
  </a>
  END
  
    $a->putCutOutLast;
    ok -p $a eq <<END;
  <a>
    <f/>
    <d/>
    <b/>
  </a>
  END
  
    $d->putCutOutPrev;
    ok -p $a eq <<END;
  <a>
    <f/>
    <e/>
    <d/>
    <b/>
  </a>
  END
  
    $d->𝗽𝘂𝘁𝗖𝘂𝘁𝗢𝘂𝘁𝗡𝗲𝘅𝘁;
    ok -p $a eq <<END;
  <a>
    <f/>
    <e/>
    <d/>
    <c/>
    <b/>
  </a>
  END
  
  

=head3 cutPrev($node, @context)

Cut out the previous node before the specified B<$node> if it is in the optional B<@context> and push it on the cut out stack. Return the current node on success else return B<undef>.  The cut out node can be reinserted using one of the putCutOut(First|Last|Next|Prev) methods.

     Parameter  Description
  1  $node      Node
  2  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b/>
    <c/>
    <d/>
    <e/>
    <f/>
  </a>
  END
  
    my ($b, $c, $d, $e, $f) = $a->byList;
    $b->cutNext_c_a;
    ok -p $a eq <<END;
  <a>
    <b/>
    <d/>
    <e/>
    <f/>
  </a>
  END
  
    $f->cutPrev_e_a;
    ok -p $a eq <<END;
  <a>
    <b/>
    <d/>
    <f/>
  </a>
  END
    $a->cutFirst_b;
    ok -p $a eq <<END;
  <a>
    <d/>
    <f/>
  </a>
  END
    $a->cutLast_f;
    ok -p $a eq <<END;
  <a>
    <d/>
  </a>
  END
  
    ok @saveLastCutOut == 4;
  
    $a->putCutOutFirst;
    ok -p $a eq <<END;
  <a>
    <f/>
    <d/>
  </a>
  END
  
    $a->putCutOutLast;
    ok -p $a eq <<END;
  <a>
    <f/>
    <d/>
    <b/>
  </a>
  END
  
    $d->putCutOutPrev;
    ok -p $a eq <<END;
  <a>
    <f/>
    <e/>
    <d/>
    <b/>
  </a>
  END
  
    $d->putCutOutNext;
    ok -p $a eq <<END;
  <a>
    <f/>
    <e/>
    <d/>
    <c/>
    <b/>
  </a>
  END
  
  

=head3 putCutOutPrev($node, @context)

Pop the last node placed on the cut out stack by one of the cut(First|Last|Next|Prev) methods and place it before the specified B<$node> if $node is in the optional B<@context>. Return $node on success else return B<undef>.

     Parameter  Description
  1  $node      Node
  2  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b/>
    <c/>
    <d/>
    <e/>
    <f/>
  </a>
  END
  
    my ($b, $c, $d, $e, $f) = $a->byList;
    $b->cutNext_c_a;
    ok -p $a eq <<END;
  <a>
    <b/>
    <d/>
    <e/>
    <f/>
  </a>
  END
  
    $f->cutPrev_e_a;
    ok -p $a eq <<END;
  <a>
    <b/>
    <d/>
    <f/>
  </a>
  END
    $a->cutFirst_b;
    ok -p $a eq <<END;
  <a>
    <d/>
    <f/>
  </a>
  END
    $a->cutLast_f;
    ok -p $a eq <<END;
  <a>
    <d/>
  </a>
  END
  
    ok @saveLastCutOut == 4;
  
    $a->putCutOutFirst;
    ok -p $a eq <<END;
  <a>
    <f/>
    <d/>
  </a>
  END
  
    $a->putCutOutLast;
    ok -p $a eq <<END;
  <a>
    <f/>
    <d/>
    <b/>
  </a>
  END
  
    $d->𝗽𝘂𝘁𝗖𝘂𝘁𝗢𝘂𝘁𝗣𝗿𝗲𝘃;
    ok -p $a eq <<END;
  <a>
    <f/>
    <e/>
    <d/>
    <b/>
  </a>
  END
  
    $d->putCutOutNext;
    ok -p $a eq <<END;
  <a>
    <f/>
    <e/>
    <d/>
    <c/>
    <b/>
  </a>
  END
  
  

=head3 cutIfEmpty($node, @context)

Cut out and return the specified B<$node> so that it can be reinserted else where in the L<parse|/parse> tree if it is empty.

     Parameter  Description
  1  $node      Node to cut out
  2  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b>bb</b>
    <c/>
    <d></d>
    <e>ee</e>
  </a>
  END
  
    my (undef, $b, $c, $d, undef, $e) = $a->byList;
    is_deeply [q(b)..q(e)], [map {-t $_} ($b, $c, $d, $e)];
  
    $c->𝗰𝘂𝘁𝗜𝗳𝗘𝗺𝗽𝘁𝘆;
    ok -p $a eq <<END;
  <a>
    <b>bb</b>
    <d/>
    <e>ee</e>
  </a>
  END
  
    $d->𝗰𝘂𝘁𝗜𝗳𝗘𝗺𝗽𝘁𝘆;
    ok -p $a eq <<END;
  <a>
    <b>bb</b>
    <e>ee</e>
  </a>
  END
  
    ok !$_->𝗰𝘂𝘁𝗜𝗳𝗘𝗺𝗽𝘁𝘆 for $a, $b, $e;
  

=head3 deleteContent($node, @context)

Delete the content of the specified B<$node>.

     Parameter  Description
  1  $node      Node
  2  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


   {my $a = Data::Edit::Xml::new(<<END);                                              
  <a>
    <b>bb<c>cc</c>BB
    </b>
  </a>
  END
  
    $b->𝗱𝗲𝗹𝗲𝘁𝗲𝗖𝗼𝗻𝘁𝗲𝗻𝘁;                                                            
  
    ok -p $a eq <<END;                                                            
  <a>
    <b/>
  </a>
  END
  

=head3 putFirst($old, $new, @context)

Place a L<cut out|/cut> or L<new|/new> node at the front of the content of the specified B<$node> and return the new node. See L<putFirstCut|/putFirstCut> to cut and put first in one operation. See L<addFirst|/addFirst> to perform this operation conditionally.

     Parameter  Description
  1  $old       Original node
  2  $new       New node
  3  @context   Optional context.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


      ok -p $a eq <<END;                                                            
  <a id="aa">
    <b id="bb">
      <c id="cc"/>
    </b>
  </a>
  END
  
      my $c = $a->go(qw(b c))->cut;                                                
  
      $a->𝗽𝘂𝘁𝗙𝗶𝗿𝘀𝘁($c);                                                           
  
      ok -p $a eq <<END;                                                           
  <a id="aa">
    <c id="cc"/>
    <b id="bb"/>
  </a>
  END
  

=head3 putFirstCut($first, $second, @context)

Cut out the B<$second> node, place it first under the B<$first> node and return the B<$second> node.

     Parameter  Description
  1  $first     First node
  2  $second    Second node
  3  @context   Optional context.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


   {my $a = Data::Edit::Xml::new(<<END);                                          
  <a>
    <b>
      <c/>
      <d/>
    </b>
  </a>
  END
  
    my ($c, $d, $b) = $a->byList;                                                 
  
    $c->𝗽𝘂𝘁𝗙𝗶𝗿𝘀𝘁𝗖𝘂𝘁($d, qw(c b a));                                               
  
    ok -p $a eq <<END;                                                            
  <a>
    <b>
      <c>
        <d/>
      </c>
    </b>
  </a>
  END
  

=head3 putLast($old, $new, @context)

Place a L<cut out|/cut> or L<new|/new> node last in the content of the specified B<$node> and return the new node.  See L<putLastCut|/putLastCut> to cut and put last in one operation.  See L<addLast|/addLast> to perform this operation conditionally.

     Parameter  Description
  1  $old       Original node
  2  $new       New node
  3  @context   Optional context.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


      ok -p $a eq <<END;                                                           
  <a id="aa">
    <c id="cc"/>
    <b id="bb"/>
  </a>
  END
  
      $a->𝗽𝘂𝘁𝗟𝗮𝘀𝘁($a->go(qw(c))->cut);                                            
  
      ok -p $a eq <<END;                                                           
  <a id="aa">
    <b id="bb"/>
    <c id="cc"/>
  </a>
  END
  

=head3 putLastCut($first, $second, @context)

Cut out the B<$second> node, place it last under the B<$first> node and return the B<$second> node.

     Parameter  Description
  1  $first     First node
  2  $second    Second node
  3  @context   Optional context.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


   {my $a = Data::Edit::Xml::new(<<END);                                          
  <a>
    <b>
      <c/>
      <d/>
    </b>
  </a>
  END
  
    my ($c, $d, $b) = $a->byList;                                                 
  
    $a->𝗽𝘂𝘁𝗟𝗮𝘀𝘁𝗖𝘂𝘁($d, qw(a));                                                    
  
    ok -p $a eq <<END;                                                            
  <a>
    <b>
      <c/>
    </b>
    <d/>
  </a>
  END
  

=head3 putNext($old, $new, @context)

Place a L<cut out|/cut> or L<new|/new> node just after the specified B<$node> and return the new node. See L<putNextCut|/putNextCut> to cut and put next in one operation.  See L<addNext|/addNext> to perform this operation conditionally.

     Parameter  Description
  1  $old       Original node
  2  $new       New node
  3  @context   Optional context.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


      ok -p $a eq <<END;                                                           
  <a id="aa">
    <b id="bb"/>
    <c id="cc"/>
  </a>
  END
  
      $a->go(qw(c))->𝗽𝘂𝘁𝗡𝗲𝘅𝘁($a->go(q(b))->cut);                                  
  
      ok -p $a eq <<END;                                                           
  <a id="aa">
    <c id="cc"/>
    <b id="bb"/>
  </a>
  END
  

=head3 putNextCut($first, $second, @context)

Cut out the B<$second> node, place it after the B<$first> node and return the B<$second> node.

     Parameter  Description
  1  $first     First node
  2  $second    Second node
  3  @context   Optional context.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


   {my $a = Data::Edit::Xml::new(<<END);                                          
  <a>
    <b>
      <c/>
      <d/>
    </b>
  </a>
  END
  
    my ($c, $d, $b) = $a->byList;                                                 
  
    $d->𝗽𝘂𝘁𝗡𝗲𝘅𝘁𝗖𝘂𝘁($c, qw(d b a));                                                
  
    ok -p $a eq <<END;                                                            
  <a>
    <b>
      <d/>
      <c/>
    </b>
  </a>
  END
  

=head3 putPrev($old, $new, @context)

Place a L<cut out|/cut> or L<new|/new> node just before the specified B<$node> and return the new node.  See L<putPrevCut|/putPrevCut> to cut and put previous in one operation.  See L<addPrev|/addPrev> to perform this operation conditionally.

     Parameter  Description
  1  $old       Original node
  2  $new       New node
  3  @context   Optional context.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


      ok -p $a eq <<END;                                                           
  <a id="aa">
    <c id="cc"/>
    <b id="bb"/>
  </a>
  END
  
      $a->go(qw(c))->𝗽𝘂𝘁𝗣𝗿𝗲𝘃($a->go(q(b))->cut);                                  
  
      ok -p $a eq <<END;                                                          
  <a id="aa">
    <b id="bb"/>
    <c id="cc"/>
  </a>
  END
  

=head3 putPrevCut($first, $second, @context)

Cut out the B<$second> node, place it before the B<$first> node and return the B<$second> node.

     Parameter  Description
  1  $first     First node
  2  $second    Second node
  3  @context   Optional context.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


   {my $a = Data::Edit::Xml::new(<<END);                                          
  <a>
    <b>
      <c/>
      <d/>
    </b>
  </a>
  END
  
    my ($c, $d, $b) = $a->byList;                                                 
  
    $c->𝗽𝘂𝘁𝗣𝗿𝗲𝘃𝗖𝘂𝘁($d, qw(c b a));                                                
  
    ok -p $a eq <<END;                                                            
  <a>
    <b>
      <d/>
      <c/>
    </b>
  </a>
  END
  

=head2 Put Siblings or Contents

Move a node and its siblings up and down the parse tree.

=head3 putSiblingsFirst($node, @context)

Move the siblings preceding the specified B<$node> in the optional B<@context> down one level and place them first under the specified B<$node> preceding any existing content.  Return the specified B<$node>.

     Parameter  Description
  1  $node      Node whose start should be moved
  2  @context   Optional context of node.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b/><c/><d/><e/><f/>
  </a>
  END
  
   my ($b, $c, $d, $e, $f) = $a->byList;
  
   $d->𝗽𝘂𝘁𝗦𝗶𝗯𝗹𝗶𝗻𝗴𝘀𝗙𝗶𝗿𝘀𝘁;
   ok -p $a eq <<END;
  <a>
    <d>
      <b/>
      <c/>
    </d>
    <e/>
    <f/>
  </a>
  END
  

=head3 putSiblingsLast($node, @context)

Move the siblings following the specified B<$node> in the optional B<@context> down one level so that they are last under the specified B<$node> following any existing content. Return the specified B<$node>.

     Parameter  Description
  1  $node      Node whose start should be moved
  2  @context   Optional context of node.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b/><c/><d/><e/><f/>
  </a>
  END
  
   my ($b, $c, $d, $e, $f) = $a->byList;
  
   $d->𝗽𝘂𝘁𝗦𝗶𝗯𝗹𝗶𝗻𝗴𝘀𝗟𝗮𝘀𝘁;
   ok -p $a eq <<END;
  <a>
    <b/>
    <c/>
    <d>
      <e/>
      <f/>
    </d>
  </a>
  END
  

=head3 putSiblingsAfterParent($node, @context)

Move the specified B<$node> and its following siblings up one level and place them after the parent of the specified B<$node> if the specified B<$node> is in the optional B<@context>.  Return the specified B<$node> if the move was made successfully, else confess that the specified move is not possible.

     Parameter  Description
  1  $node      Node whose start should be moved
  2  @context   Optional context of node.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b>
      <c/>d<e/>
    </b>
  </a>
  END
  
   my ($c, $d, $e, $b) = $a->byList;
  
   $d->𝗽𝘂𝘁𝗦𝗶𝗯𝗹𝗶𝗻𝗴𝘀𝗔𝗳𝘁𝗲𝗿𝗣𝗮𝗿𝗲𝗻𝘁;
  
   ok -p $a eq <<END;
  <a>
    <b>
      <c/>
    </b>
  d
    <e/>
  </a>
  END
  

=head3 putSiblingsBeforeParent($node, @context)

Move the specified B<$node> and its preceding siblings up one level and place them before the parent of the specified B<$node> if the specified B<$node> is in the optional B<@context>.  Return the specified B<$node> if the move was made successfully, else confess that the specified move is not possible.

     Parameter  Description
  1  $node      Node whose start should be moved
  2  @context   Optional context of node.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b>
      <c/>d<e/>
    </b>
  </a>
  END
  
   my ($c, $d, $e, $b) = $a->byList;
  
   $d->𝗽𝘂𝘁𝗦𝗶𝗯𝗹𝗶𝗻𝗴𝘀𝗕𝗲𝗳𝗼𝗿𝗲𝗣𝗮𝗿𝗲𝗻𝘁;
  
   ok -p $a eq <<END;
  <a>
    <c/>
  d
    <b>
      <e/>
    </b>
  </a>
  END
  

=head3 putContentAfter($node, @context)

Move the content of the specified B<$node> and place it after that node if that node is in the optional B<@context>.  Return the specified B<$node> or confess if the move is not possible.

     Parameter  Description
  1  $node      Node whose start should be moved
  2  @context   Optional context of node.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b>
      <c/><d/>
    </b>
  </a>
  END
  
   my ($c, $d, $b) = $a->byList;
  
   $b->𝗽𝘂𝘁𝗖𝗼𝗻𝘁𝗲𝗻𝘁𝗔𝗳𝘁𝗲𝗿;
   ok -p $a eq <<END;
  <a>
    <b/>
    <c/>
    <d/>
  </a>
  END
  

=head3 putContentBefore($node, @context)

Move the content of the specified B<$node> and place it before that node if that node is in the optional B<@context>.  Return the specified B<$node> or confess if the move is not possible.

     Parameter  Description
  1  $node      Node whose start should be moved
  2  @context   Optional context of node.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b>
      <c/><d/>
    </b>
  </a>
  END
  
   my ($c, $d, $b) = $a->byList;
  
   $b->𝗽𝘂𝘁𝗖𝗼𝗻𝘁𝗲𝗻𝘁𝗕𝗲𝗳𝗼𝗿𝗲;
   ok -p $a eq <<END;
  <a>
    <c/>
    <d/>
    <b/>
  </a>
  END
  

=head2 Move

Move nodes around in the L<parse|/parse> tree by cutting and pasting them.

=head3 moveFirst($node, @context)

Move the specified node so that is is the first sibling under its parent. Returns the specified $node on success otherwise B<undef>.

     Parameter  Description
  1  $node      Node to  be moved
  2  @context   Optional context of node.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a><b/><c/><d/></a>
  END
  
    my ($b, $c, $d) = $a->byList;
    $d->𝗺𝗼𝘃𝗲𝗙𝗶𝗿𝘀𝘁;
    $b->moveLast;
  
    ok -p $a eq <<END;
  <a>
    <d/>
    <c/>
    <b/>
  </a>
  END
  

=head3 moveLast($node, @context)

Move the specified node so that is is the last sibling under its parent. Returns the specified $node on success otherwise B<undef>.

     Parameter  Description
  1  $node      Node to  be moved
  2  @context   Optional context of node.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a><b/><c/><d/></a>
  END
  
    my ($b, $c, $d) = $a->byList;
    $d->moveFirst;
    $b->𝗺𝗼𝘃𝗲𝗟𝗮𝘀𝘁;
  
    ok -p $a eq <<END;
  <a>
    <d/>
    <c/>
    <b/>
  </a>
  END
  

=head3 moveStartFirst($node, @context)

Move the start of a B<$node> to contain all of its preceding siblings as children. Returns B<$node> if the move was made successfully, else confess that the specified move is impossible.

     Parameter  Description
  1  $node      Node whose start should be moved
  2  @context   Optional context of node.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
   <b/>
   <c/>
   <d>
    <e/>
   </d>
  </a>
  END
  
    my ($b, $c, $e, $d) = $a->byList;
  
    $d->𝗺𝗼𝘃𝗲𝗦𝘁𝗮𝗿𝘁𝗙𝗶𝗿𝘀𝘁;
    ok -p $a eq <<END;
  <a>
    <d>
      <b/>
      <c/>
      <e/>
    </d>
  </a>
  END
  

=head3 moveStartAfter($node, $to, @context)

Move the start end of a B<$node> to just after the specified B<$target> node assuming that the B<$target> node is either a preceding sibling or a child of B<$node>. Returns B<$node> if the move was made successfully, else confess that the specified move is impossible.

     Parameter  Description
  1  $node      Node whose start should be moved
  2  $to        Target node
  3  @context   Optional context of node.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
   <b/>
   <c/>
   <d>
    <e/>
   </d>
  </a>
  END
  
    my ($b, $c, $e, $d) = $a->byList;
  
    $d->𝗺𝗼𝘃𝗲𝗦𝘁𝗮𝗿𝘁𝗔𝗳𝘁𝗲𝗿($b);
    ok -p $a eq <<END;
  <a>
    <b/>
    <d>
      <c/>
      <e/>
    </d>
  </a>
  END
  
    $d->𝗺𝗼𝘃𝗲𝗦𝘁𝗮𝗿𝘁𝗔𝗳𝘁𝗲𝗿($c);
    ok -t $d eq q(d);
    ok -t $c eq q(c);
    ok -p $a eq <<END;
  <a>
    <b/>
    <c/>
    <d>
      <e/>
    </d>
  </a>
  END
  
    $d->𝗺𝗼𝘃𝗲𝗦𝘁𝗮𝗿𝘁𝗔𝗳𝘁𝗲𝗿($e);
    ok -p $a eq <<END;
  <a>
    <b/>
    <c/>
    <e/>
    <d/>
  </a>
  END
  

=head3 moveStartBefore($node, $to, @context)

Move the start of a B<$node> to just before the specified B<$target> node assuming that the B<$target> node is either a preceding sibling or a child of B<$node>. Returns B<$node> if the move was made successfully, else confess that the specified move is impossible.

     Parameter  Description
  1  $node      Node whose start should be moved
  2  $to        Target node
  3  @context   Optional context of node.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
   <b/>
   <c/>
   <d>
    <e/>
   </d>
  </a>
  END
  
    my ($b, $c, $e, $d) = $a->byList;
  
    $d->𝗺𝗼𝘃𝗲𝗦𝘁𝗮𝗿𝘁𝗕𝗲𝗳𝗼𝗿𝗲($c);
    ok -p $a eq <<END;
  <a>
    <b/>
    <d>
      <c/>
      <e/>
    </d>
  </a>
  END
  
    $d->𝗺𝗼𝘃𝗲𝗦𝘁𝗮𝗿𝘁𝗕𝗲𝗳𝗼𝗿𝗲($e);
    ok -t $d eq q(d);
    ok -t $e eq q(e);
    ok -p $a eq <<END;
  <a>
    <b/>
    <c/>
    <d>
      <e/>
    </d>
  </a>
  END
  
    $d->moveEndBefore($e);
    ok -t $d eq q(d);
    ok -t $e eq q(e);
    ok -p $a eq <<END;
  <a>
    <b/>
    <c/>
    <d/>
    <e/>
  </a>
  END
  

=head3 moveEndLast($node, @context)

Move the end of a B<$node> to contain all of its following siblings as children. Returns B<$node> if the move was made successfully, else confess that the specified move is impossible.

     Parameter  Description
  1  $node      Node whose end should be moved
  2  @context   Optional context of node.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
   <b>
    <c/>
   </b>
   <d/>
   <e/>
  </a>
  END
  
    my ($c, $b, $d, $e) = $a->byList;
  
    $b->𝗺𝗼𝘃𝗲𝗘𝗻𝗱𝗟𝗮𝘀𝘁;
    ok -p $a eq <<END;
  <a>
    <b>
      <c/>
      <d/>
      <e/>
    </b>
  </a>
  END
  

=head3 moveEndAfter($node, $to, @context)

Move the end of a B<$node> to just after the specified B<$target> node assuming that the B<$target> node is either a subsequent sibling or a child of B<$node>. Returns B<$node> if the move was made successfully, else confess that the specified move is impossible.

     Parameter  Description
  1  $node      Node whose end should be moved
  2  $to        Target node
  3  @context   Optional context of node.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
   <b>
    <c/>
   </b>
   <d/>
   <e/>
  </a>
  END
  
    my ($c,  $b, $d, $e) = $a->byList;
  
    $b->𝗺𝗼𝘃𝗲𝗘𝗻𝗱𝗔𝗳𝘁𝗲𝗿($d);
    ok -p $a eq <<END;
  <a>
    <b>
      <c/>
      <d/>
    </b>
    <e/>
  </a>
  END
  
    $b->𝗺𝗼𝘃𝗲𝗘𝗻𝗱𝗔𝗳𝘁𝗲𝗿($c);
    ok -p $a eq <<END;
  <a>
    <b>
      <c/>
    </b>
    <d/>
    <e/>
  </a>
  END
  

=head3 moveEndBefore($node, $to, @context)

Move the end of a B<$node> to just before the specified B<$target> node assuming that the B<$target> node is either a subsequent sibling or a child of B<$node>. Returns B<$node> if the move was made successfully, else confess that the specified move is impossible.

     Parameter  Description
  1  $node      Node whose end should be moved
  2  $to        Target node
  3  @context   Optional context of node.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
   <b>
    <c/>
   </b>
   <d/>
   <e/>
  </a>
  END
  
    my ($c,  $b, $d, $e) = $a->byList;
  
    $b->𝗺𝗼𝘃𝗲𝗘𝗻𝗱𝗕𝗲𝗳𝗼𝗿𝗲($e);
    ok -p $a eq <<END;
  <a>
    <b>
      <c/>
      <d/>
    </b>
    <e/>
  </a>
  END
  
    $b->𝗺𝗼𝘃𝗲𝗘𝗻𝗱𝗕𝗲𝗳𝗼𝗿𝗲($d);
    ok -p $a eq <<END;
  <a>
    <b>
      <c/>
    </b>
    <d/>
    <e/>
  </a>
  END
  

=head3 putNextFirstCut($node, @context)

Move the specified B<$node> so it is first in the next node with the optional context. Return $node on success else return B<undef> on failure.

     Parameter  Description
  1  $node      Node
  2  @context   Context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b/>
    <c>
      <d/>
    </c>
  </a>
  END
  
    my ($b, $d, $c) = $a->byList;
  
    ok !$b->putNextFirstCut_c_x;
  
    $b->putNextFirstCut_c_a;
    ok  -p $a eq <<END;
  <a>
    <c>
      <b/>
      <d/>
    </c>
  </a>
  END
  

=head3 putNextFirstCut2($node, @context)

Move the specified B<$node> so it is first in the first node with the specified optional B<@context> of the next node. Return $node on success else return B<undef> on failure.

     Parameter  Description
  1  $node      Node
  2  @context   Context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b/>
    <c>
      <d/>
    </c>
  </a>
  END
  
    my ($b, $d, $c) = $a->byList;
  
    ok !$b->putNextFirstCut2_c_a;
  
    $b->putNextFirstCut2_d_c_a;
  
    ok  -p $a eq <<END;
  <a>
    <c>
      <d>
        <b/>
      </d>
    </c>
  </a>
  END
  

=head3 putPrevLastCut($node, @context)

Move the specified B<$node> so it is last in the preceding node with the optional context. Return $node on success else return B<undef> on failure.

     Parameter  Description
  1  $node      Node
  2  @context   Context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b>
      <c/>
    </b>
     <d/>
  </a>
  END
  
    my ($c, $b, $d) = $a->byList;
  
    ok !$d->putPrevLastCut_b_c;
  
    $d->putPrevLastCut_b_a;
    ok  -p $a eq <<END;
  <a>
    <b>
      <c/>
      <d/>
    </b>
  </a>
  END
  

=head3 putPrevLastCut2($node, @context)

Move the specified B<$node> so it is last in the last node with the specified optional context of the preceding node. Return the specified $node on success else return B<undef> on failure.

     Parameter  Description
  1  $node      Node
  2  @context   Context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b>
      <c/>
    </b>
     <d/>
  </a>
  END
  
    my ($c, $b, $d) = $a->byList;
  
    ok !$d->putPrevLastCut2_c_a;
  
    $d->putPrevLastCut2_c_b_a;
    ok  -p $a eq <<END;
  <a>
    <b>
      <c>
        <d/>
      </c>
    </b>
  </a>
  END
  

=head3 putUpNextCut($node, @context)

Move the specified B<$node>, in the optional B<@context>, which must be last under its parent, so that it is next after its parent. Return $node on success else return B<undef> on failure.

     Parameter  Description
  1  $node      Node
  2  @context   Context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b>
      <c>
        <d/>
      </c>
    </b>
  </a>
  END
  
    $a->by(sub{$_->putUpNextCut_d_c_b_a});
  
    ok -p $a eq <<END
  <a>
    <b>
      <c/>
      <d/>
    </b>
  </a>
  END
  

=head3 putUpNextCut2($node, @context)

Move the specified B<$node>, in the optional B<@context>, if $node is last after its parent which must also be last under its parent, so that $node is next after its grandparent. Return $node on success else return B<undef> on failure.

     Parameter  Description
  1  $node      Node
  2  @context   Context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b>
      <c>
        <d/>
      </c>
    </b>
  </a>
  END
  
    $a->by(sub{$_->putUpNextCut2_d_c_b_a});
  
    ok -p $a eq <<END
  <a>
    <b>
      <c/>
    </b>
    <d/>
  </a>
  END
  

=head3 putUpPrevCut($node, @context)

Move the specified B<$node> in the optional B<@context>, if $node is first under its parent, so that it is prior to its parent. Return $node on success else return B<undef> on failure.

     Parameter  Description
  1  $node      Node
  2  @context   Context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b>
      <c>
        <d/>
      </c>
    </b>
  </a>
  END
  
    $a->by(sub{$_->putUpPrevCut_d_c_b_a});
  
    ok -p $a eq <<END
  <a>
    <b>
      <d/>
      <c/>
    </b>
  </a>
  END
  

=head3 putUpPrevCut2($node, @context)

Move the specified B<$node>, in the optional B<@context>, if $node is first under its parent which must also be first under its parent, so that $node is prior to its grandparent. Return the specified B<$node> on success else return B<undef> on failure.

     Parameter  Description
  1  $node      Node
  2  @context   Context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b>
      <c>
        <d/>
      </c>
    </b>
  </a>
  END
  
    $a->by(sub{$_->putUpPrevCut2_d_c_b_a});
  
    ok -p $a eq <<END
  <a>
    <d/>
    <b>
      <c/>
    </b>
  </a>
  END
  

=head3 moveBlockFirst($start, $end, $parent, @context)

Move the block of siblings starting with B<$start> in the optional context and ending with B<$end> first under the specified B<$parent> node. Returns B<$node> if the move was made successfully, else confess that the specified move is impossible.

     Parameter  Description
  1  $start     First sibling
  2  $end       Last sibling
  3  $parent    Parent to move first under
  4  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $x = Data::Edit::Xml::new(<<END);
  <x>
    <a/>
    <b/>
    <c/>
    <d/>
    <e/>
    <f>
      <g/>
    </f>
    <h/>
  </x>
  END
    my ($a, $b, $c, $d, $e, $g, $f, $h) = $x->byList;
  
    $c->𝗺𝗼𝘃𝗲𝗕𝗹𝗼𝗰𝗸𝗙𝗶𝗿𝘀𝘁($d, $f, qw(c x));
  
    ok -p $x eq <<END;
  <x>
    <a/>
    <b/>
    <e/>
    <f>
      <c/>
      <d/>
      <g/>
    </f>
    <h/>
  </x>
  END
  

=head3 moveBlockLast($start, $end, $parent, @context)

Move the block of siblings starting with B<$start> in the optional context and ending with B<$end> last under the specified B<$parent> node. Returns B<$node> if the move was made successfully, else confess that the specified move is impossible.

     Parameter  Description
  1  $start     First sibling
  2  $end       Last sibling
  3  $parent    Parent to move last under
  4  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $x = Data::Edit::Xml::new(<<END);
  <x>
    <a/>
    <b/>
    <c/>
    <d/>
    <e/>
    <f>
      <g/>
    </f>
    <h/>
  </x>
  END
    my ($a, $b, $c, $d, $e, $g, $f, $h) = $x->byList;
  
    $c->𝗺𝗼𝘃𝗲𝗕𝗹𝗼𝗰𝗸𝗟𝗮𝘀𝘁($d, $f, qw(c x));
  
    ok -p $x eq <<END;
  <x>
    <a/>
    <b/>
    <e/>
    <f>
      <g/>
      <c/>
      <d/>
    </f>
    <h/>
  </x>
  END
  

=head3 moveBlockAfter($start, $end, $after, @context)

Move the block of siblings starting with B<$start> in the optional context and ending with B<$end> after the specified B<$after> node. Returns B<$node> if the move was made successfully, else confess that the specified move is impossible.

     Parameter  Description
  1  $start     First sibling
  2  $end       Last sibling
  3  $after     Node to move after
  4  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $x = Data::Edit::Xml::new(q(<x><a/><b/><c/><d/><e/><f/><g/><h/></x>));
  
    my ($a, $b, $c, $d, $e, $f, $g, $h) = $x->byList;
  
    $c->𝗺𝗼𝘃𝗲𝗕𝗹𝗼𝗰𝗸𝗔𝗳𝘁𝗲𝗿($d, $f, qw(c x));
  
    ok -C $x eq q(<a/><b/><e/><f/><c/><d/><g/><h/>);
  

=head3 moveBlockBefore($start, $end, $before, @context)

Move the block of siblings starting with B<$start> in the optional context and ending with B<$end> before the specified B<$after> node. Returns B<$node> if the move was made successfully, else confess that the specified move is impossible.

     Parameter  Description
  1  $start     First sibling
  2  $end       Last sibling
  3  $before    Node to move before
  4  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $x = Data::Edit::Xml::new(q(<x><a/><b/><c/><d/><e/><f/><g/><h/></x>));
  
    my ($a, $b, $c, $d, $e, $f, $g, $h) = $x->byList;
  
    $f->𝗺𝗼𝘃𝗲𝗕𝗹𝗼𝗰𝗸𝗕𝗲𝗳𝗼𝗿𝗲($g, $b, qw(f x));
  
    ok -C $x eq q(<a/><f/><g/><b/><c/><d/><e/><h/>);
  

=head3 moveBlockToLastFirst($start, $parent, @context)

Move the siblings starting with B<$start> in the optional context first under the specified B<$parent> node. Returns B<$node> if the move was made successfully, else confess that the specified move is impossible.

     Parameter  Description
  1  $start     Start sibling
  2  $parent    Parent to move first under
  3  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $x = Data::Edit::Xml::new(<<END);
  <x>
    <a/>
    <b/>
    <c/>
  </x>
  END
    my ($a, $b, $c) = $x->byList;
  
    $b->𝗺𝗼𝘃𝗲𝗕𝗹𝗼𝗰𝗸𝗧𝗼𝗟𝗮𝘀𝘁𝗙𝗶𝗿𝘀𝘁($a);
  
    ok -p $x eq <<END;
  <x>
    <a>
      <b/>
      <c/>
    </a>
  </x>
  END
  

=head3 moveBlockToLastLast($start, $parent, @context)

Move the block of siblings starting with B<$start> in the optional context last under the specified B<$parent> node. Returns B<$node> if the move was made successfully, else confess that the specified move is impossible.

     Parameter  Description
  1  $start     First sibling
  2  $parent    Parent to move last under
  3  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $x = Data::Edit::Xml::new(<<END);
  <x>
    <a>
      <b/>
      <c/>
      <d/>
    </a>
    <e>
      <f/>
      <g/>
      <h/>
    </e>
  </x>
  END
    my ($b, $c, $d, $a, $f, $g, $h, $e) = $x->byList;
  
    $c->𝗺𝗼𝘃𝗲𝗕𝗹𝗼𝗰𝗸𝗧𝗼𝗟𝗮𝘀𝘁𝗟𝗮𝘀𝘁($e);
  
    ok -p $x eq <<END;
  <x>
    <a>
      <b/>
    </a>
    <e>
      <f/>
      <g/>
      <h/>
      <c/>
      <d/>
    </e>
  </x>
  END
  

=head3 moveBlockToLastAfter($start, $after, @context)

Move the block of siblings starting with B<$start> in the optional context after the specified B<$after> node. Returns B<$node> if the move was made successfully, else confess that the specified move is impossible.

     Parameter  Description
  1  $start     First sibling
  2  $after     Node to move after
  3  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $x = Data::Edit::Xml::new(<<END);
  <x>
    <a>
      <b/>
      <c/>
      <d/>
    </a>
    <e/>
  </x>
  END
    my ($b, $c, $d, $a, $e) = $x->byList;
  
    $c->𝗺𝗼𝘃𝗲𝗕𝗹𝗼𝗰𝗸𝗧𝗼𝗟𝗮𝘀𝘁𝗔𝗳𝘁𝗲𝗿($e);
  
    ok -p $x eq <<END;
  <x>
    <a>
      <b/>
    </a>
    <e/>
    <c/>
    <d/>
  </x>
  END
  

=head3 moveBlockToLastBefore($start, $before, @context)

Move the block of siblings starting with B<$start> in the optional context before the specified B<$after> node. Returns B<$node> if the move was made successfully, else confess that the specified move is impossible.

     Parameter  Description
  1  $start     First sibling
  2  $before    Node to move before
  3  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $x = Data::Edit::Xml::new(<<END);
  <x>
    <a/>
    <b>
      <c/>
      <d/>
      <e/>
    </b>
  </x>
  END
    my ($a, $c, $d, $e, $b) = $x->byList;
  
    $d->𝗺𝗼𝘃𝗲𝗕𝗹𝗼𝗰𝗸𝗧𝗼𝗟𝗮𝘀𝘁𝗕𝗲𝗳𝗼𝗿𝗲($b);
  
    ok -p $x eq <<END;
  <x>
    <a/>
    <d/>
    <e/>
    <b>
      <c/>
    </b>
  </x>
  END
  

=head3 moveBlockFromFirstFirst($start, $parent, @context)

Move the siblings starting with B<$start> in the optional context first under the specified B<$parent> node. Returns B<$node> if the move was made successfully, else confess that the specified move is impossible.

     Parameter  Description
  1  $start     Start sibling
  2  $parent    Parent to move first under
  3  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $x = Data::Edit::Xml::new(<<END);
  <x>
    <a>
      <b/>
      <c/>
      <d/>
    </a>
    <e>
      <f/>
      <g/>
      <h/>
    </e>
  </x>
  END
    my ($b, $c, $d, $a, $f, $g, $h, $e) = $x->byList;
  
    $g->𝗺𝗼𝘃𝗲𝗕𝗹𝗼𝗰𝗸𝗙𝗿𝗼𝗺𝗙𝗶𝗿𝘀𝘁𝗙𝗶𝗿𝘀𝘁($a);
  
    ok -p $x eq <<END;
  <x>
    <a>
      <f/>
      <g/>
      <b/>
      <c/>
      <d/>
    </a>
    <e>
      <h/>
    </e>
  </x>
  END
  

=head3 moveBlockFromFirstLast($start, $parent, @context)

Move the block of siblings starting with B<$start> in the optional context last under the specified B<$parent> node. Returns B<$node> if the move was made successfully, else confess that the specified move is impossible.

     Parameter  Description
  1  $start     First sibling
  2  $parent    Parent to move last under
  3  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $x = Data::Edit::Xml::new(<<END);
  <x>
    <a/>
    <b/>
    <c/>
  </x>
  END
    my ($a, $b, $c) = $x->byList;
  
    $b->𝗺𝗼𝘃𝗲𝗕𝗹𝗼𝗰𝗸𝗙𝗿𝗼𝗺𝗙𝗶𝗿𝘀𝘁𝗟𝗮𝘀𝘁($c);
  
    ok -p $x eq <<END;
  <x>
    <c>
      <a/>
      <b/>
    </c>
  </x>
  END
  

=head3 moveBlockFromFirstAfter($start, $after, @context)

Move the block of siblings starting with B<$start> in the optional context after the specified B<$after> node. Returns B<$node> if the move was made successfully, else confess that the specified move is impossible.

     Parameter  Description
  1  $start     First sibling
  2  $after     Node to move after
  3  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $x = Data::Edit::Xml::new(<<END);
  <x>
    <a>
      <b/>
      <c/>
      <d/>
    </a>
    <e/>
  </x>
  END
    my ($b, $c, $d, $a, $e) = $x->byList;
  
    $c->𝗺𝗼𝘃𝗲𝗕𝗹𝗼𝗰𝗸𝗙𝗿𝗼𝗺𝗙𝗶𝗿𝘀𝘁𝗔𝗳𝘁𝗲𝗿($e);
  
    ok -p $x eq <<END;
  <x>
    <a>
      <d/>
    </a>
    <e/>
    <b/>
    <c/>
  </x>
  END
  

=head3 moveBlockFromFirstBefore($start, $before, @context)

Move the block of siblings starting with B<$start> in the optional context before the specified B<$after> node. Returns B<$node> if the move was made successfully, else confess that the specified move is impossible.

     Parameter  Description
  1  $start     First sibling
  2  $before    Node to move before
  3  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $x = Data::Edit::Xml::new(<<END);
  <x>
    <a/>
    <b>
      <c/>
      <d/>
      <e/>
    </b>
  </x>
  END
    my ($a, $c, $d, $e, $b) = $x->byList;
  
    $d->𝗺𝗼𝘃𝗲𝗕𝗹𝗼𝗰𝗸𝗙𝗿𝗼𝗺𝗙𝗶𝗿𝘀𝘁𝗕𝗲𝗳𝗼𝗿𝗲($b);
  
    ok -p $x eq <<END;
  <x>
    <a/>
    <c/>
    <d/>
    <b>
      <e/>
    </b>
  </x>
  END
  

=head2 Add selectively

Add new nodes unless they already exist.

=head3 addFirst($node, $tag, @context)

Add a new node L<first|/first> below the specified B<$node> with the specified B<$tag> unless a node with that tag already exists in that position. Return the new node if it was created unless return the pre-existing node.

     Parameter  Description
  1  $node      Node
  2  $tag       Tag of new node
  3  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


   {my $a = Data::Edit::Xml::newTree(q(a));                                       
  
    $a->addFirst_b for 1..2;                                                      
  
    ok -p $a eq <<END;                                                             
  <a>
    <b/>
  </a>
  END
  

=head3 addNext($node, $tag, @context)

Add a new node L<next|/next> the specified B<$node> and return the new node unless a node with that tag already exists in which case return the existing B<$node>.

     Parameter  Description
  1  $node      Node
  2  $tag       Tag of new node
  3  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    ok -p $a eq <<END;                                                             
  <a>
    <b/>
    <e/>
  </a>
  END
  
    $a->addFirst_b__addNext_c;                                                    
  
    ok -p $a eq <<END;                                                             
  <a>
    <b/>
    <c/>
    <e/>
  </a>
  END
  

=head3 addPrev($node, $tag, @context)

Add a new node L<before|/prev> the specified B<$node> with the specified B<$tag> unless a node with that tag already exists in that position. Return the new node if it was created unless return the pre-existing node.

     Parameter  Description
  1  $node      Node
  2  $tag       Tag of new node
  3  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    ok -p $a eq <<END;                                                             
  <a>
    <b/>
    <c/>
    <e/>
  </a>
  END
  
    $a->addLast_e__addPrev_d;                                                     
  
    ok -p $a eq <<END;                                                            
  <a>
    <b/>
    <c/>
    <d/>
    <e/>
  </a>
  END
  

=head3 addLast($node, $tag, @context)

Add a new node L<last|/last> below the specified B<$node> with the specified B<$tag> unless a node with that tag already exists in that position. Return the new node if it was created unless return the pre-existing node..

     Parameter  Description
  1  $node      Node
  2  $tag       Tag of new node
  3  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    ok -p $a eq <<END;                                                             
  <a>
    <b/>
  </a>
  END
  
    $a->𝗮𝗱𝗱𝗟𝗮𝘀𝘁(qw(e)) for 1..2;                                                  
  
    ok -p $a eq <<END;                                                             
  <a>
    <b/>
    <e/>
  </a>
  END
  

=head3 addWrapWith($node, $tag, @context)

L<Wrap|/wrap> the specified B<$node> with the specified tag if the node is not already wrapped with such a tag and return the new node unless a node with that tag already exists in which case return the existing B<$node>.

     Parameter  Description
  1  $node      Node
  2  $tag       Tag of new node
  3  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


   {my $a = Data::Edit::Xml::new(q(<a><b/></a>));                                 
  
    my $b = $a->first;                                                            
  
    $b->addWrapWith_c for 1..2;                                                   
  
    ok -p $a eq <<END;                                                             
  <a>
    <c>
      <b/>
    </c>
  </a>
  END
  

=head3 addSingleChild($node, $tag, @context)

Wrap the content of a specified B<$node> in a new node with the specified B<$tag> unless the content is already wrapped in a single child with the specified B<$tag>.

     Parameter  Description
  1  $node      Node
  2  $tag       Tag of new node
  3  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    ok -p $a eq <<END;                                                             
  <a>
    <c>
      <b/>
    </c>
  </a>
  END
  
    $a->addSingleChild_d for 1..2;                                                
  
    ok -p $a eq <<END;                                                            
  <a>
    <d>
      <c>
        <b/>
      </c>
    </d>
  </a>
  END
  

=head2 Add text selectively

Add new text unless it already exists.

=head3 addFirstAsText($node, $text, @context)

Add a new text node first below the specified B<$node> and return the new node unless a text node already exists there and starts with the same text in which case return the existing B<$node>.

     Parameter  Description
  1  $node      Node
  2  $text      Text
  3  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


   {my $a = Data::Edit::Xml::newTree(q(a));                                       
  
    $a->𝗮𝗱𝗱𝗙𝗶𝗿𝘀𝘁𝗔𝘀𝗧𝗲𝘅𝘁(q(aaaa)) for 1..2;                                         
  
    ok -s $a eq q(<a>aaaa</a>);                                                    
  

=head3 addNextAsText($node, $text, @context)

Add a new text node after the specified B<$node> and return the new node unless a text node already exists there and starts with the same text in which case return the existing B<$node>.

     Parameter  Description
  1  $node      Node
  2  $text      Text
  3  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


   {my $a = Data::Edit::Xml::new(q(<a><b/></a>));                                 
  
    $a->go(q(b))->𝗮𝗱𝗱𝗡𝗲𝘅𝘁𝗔𝘀𝗧𝗲𝘅𝘁(q(bbbb)) for 1..2;                                
  
    ok -p $a eq <<END;                                                             
  <a>
    <b/>
  bbbb
  </a>
  END
  

=head3 addPrevAsText($node, $text, @context)

Add a new text node before the specified B<$node> and return the new node unless a text node already exists there and ends with the same text in which case return the existing B<$node>.

     Parameter  Description
  1  $node      Node
  2  $text      Text
  3  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    ok -p $a eq <<END;                                                             
  <a>
    <b/>
  bbbb
  </a>
  END
  
    $a->go(q(b))->𝗮𝗱𝗱𝗣𝗿𝗲𝘃𝗔𝘀𝗧𝗲𝘅𝘁(q(aaaa)) for 1..2;                                
  
    ok -p $a eq <<END;                                                            
  <a>aaaa
    <b/>
  bbbb
  </a>
  END
  

=head3 addLastAsText($node, $text, @context)

Add a new text node last below the specified B<$node> and return the new node unless a text node already exists there and ends with the same text in which case return the existing B<$node>.

     Parameter  Description
  1  $node      Node
  2  $text      Text
  3  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    ok -s $a eq q(<a>aaaa</a>);                                                    
  
    $a->𝗮𝗱𝗱𝗟𝗮𝘀𝘁𝗔𝘀𝗧𝗲𝘅𝘁(q(dddd)) for 1..2;                                          
  
    ok -s $a eq q(<a>aaaadddd</a>);                                               
  

=head3 joinWithText($node, $text, $over, @context)

Insert some text between the children of the current B<$node> as specified by B<$text> between all the children of the current $node as long as they all have a tag of B<$over>. Return the current $node on success or B<undef> on failure.

     Parameter  Description
  1  $node      Node
  2  $text      Text
  3  $over      Child tag
  4  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b/>
    <b/>
    <b/>
  </a>
  END
  
    $a->joinWithText_CC_b_a;
  
    ok -p $a eq <<END;
  <a>
    <b/>
  CC
    <b/>
  CC
    <b/>
  </a>
  END
  

=head2 Fission

Split the parent L<before|/splitBefore> or L<after|/splitAfter> the specified sibling.

=head3 splitBefore($node, @context)

Split the parent node into two identical nodes except all the siblings before the specified B<$node> are retained by the existing parent while any following siblings become siblings of the new parent node which is placed after the existing parent. The new parent is returned.

     Parameter  Description
  1  $node      Node to split before
  2  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b>
      <c/>
      <d/>
      <e/>
    </b>
  </a>
  END
  
    $a->go_b_d__splitBefore;
  
    ok -p $a eq <<END;
  <a>
    <b>
      <c/>
    </b>
    <b>
      <d/>
      <e/>
    </b>
  </a>
  END
  

=head3 splitAfter($node, @context)

Split the parent node into two identical nodes except all the siblings after the specified B<$node> are retained by the existing parent while any preceding siblings become siblings of the new parent node which is placed before the existing parent. The new parent is returned

     Parameter  Description
  1  $node      Node to split before
  2  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b>
      <c/>
      <d/>
      <e/>
    </b>
  </a>
  END
  
    $a->go_b_d__splitAfter;
  
    ok -p $a eq <<END;
  <a>
    <b>
      <c/>
      <d/>
    </b>
    <b>
      <e/>
    </b>
  </a>
  END
  

=head2 Fusion

Join consecutive nodes

=head3 concatenate($target, $source, @context)

Concatenate two successive nodes and return the B<$target> node.

     Parameter  Description
  1  $target    Target node to replace
  2  $source    Node to concatenate
  3  @context   Optional context of $target

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


   {my $s = <<END;                                                                     
  <a>
    <b>
      <A/>
      <B/>
    </b>
    <c>
      <C/>
      <D/>
    </c>
  </a>
  END
  
    my $a = Data::Edit::Xml::new($s);                                              
  
    $a->go(q(b))->𝗰𝗼𝗻𝗰𝗮𝘁𝗲𝗻𝗮𝘁𝗲($a->go(q(c)));                                      
  
    my $t = <<END;                                                                
  <a>
    <b>
      <A/>
      <B/>
      <C/>
      <D/>
    </b>
  </a>
  END
  
    ok $t eq -p $a;                                                               
  

=head3 concatenateSiblings($node, @context)

Concatenate the nodes that precede and follow the specified B<$node> in the optioonal B<@context> as long as they have the same tag as the specified B<$node> and return the specified B<$node>.

     Parameter  Description
  1  $node      Concatenate around this node
  2  @context   Optional context.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    ok -p $a eq <<END;                                                            
  <a>
    <b>
      <c id="1"/>
    </b>
    <b>
      <c id="2"/>
    </b>
    <b>
      <c id="3"/>
    </b>
    <b>
      <c id="4"/>
    </b>
  </a>
  END
  
    $a->go(qw(b 3))->𝗰𝗼𝗻𝗰𝗮𝘁𝗲𝗻𝗮𝘁𝗲𝗦𝗶𝗯𝗹𝗶𝗻𝗴𝘀;                                        
  
    ok -p $a eq <<END;                                                            
  <a>
    <b>
      <c id="1"/>
      <c id="2"/>
      <c id="3"/>
      <c id="4"/>
    </b>
  </a>
  END
  

=head3 mergeDuplicateChildWithParent($parent, @context)

Merge a parent node with its only child if their tags are the same and their attributes do not collide other than possibly the id in which case the parent id is used. Any labels on the child are transferred to the parent. The child node is then unwrapped and the parent node is returned.

     Parameter  Description
  1  $parent    Parent this node
  2  @context   Optional context.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


   {my $a = Data::Edit::Xml::new(<<END);                                           
  <a>
    <b   id="b" b="bb">
      <b id="c" c="cc"/>
    </b>
  </a>
  END
  
    my ($c, $b) = $a->byList;                                                      
  
    is_deeply [$b->id, $c->id], [qw(b c)];                                         
  
    ok $c == $b->hasSingleChild;                                                   
  
    $b->𝗺𝗲𝗿𝗴𝗲𝗗𝘂𝗽𝗹𝗶𝗰𝗮𝘁𝗲𝗖𝗵𝗶𝗹𝗱𝗪𝗶𝘁𝗵𝗣𝗮𝗿𝗲𝗻𝘁;                                            
  
    ok -p $a eq <<END;                                                            
  <a>
    <b b="bb" c="cc" id="b"/>
  </a>
  END
  
    ok $b == $a->hasSingleChild;                                                   
  

=head2 Put as text

Add text to the L<parse|/parse> tree.

=head3 putFirstAsText($node, $text, @context)

Add a new text node first under a parent and return the new text node.

     Parameter  Description
  1  $node      The parent node
  2  $text      The string to be added which might contain unparsed Xml as well as text
  3  @context   Optional context.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    ok -p $x eq <<END;                                                            
  <a id="aa">
    <b id="bb">
      <c id="cc"/>
    </b>
  </a>
  END
  
    $x->go(qw(b c))->𝗽𝘂𝘁𝗙𝗶𝗿𝘀𝘁𝗔𝘀𝗧𝗲𝘅𝘁("<d id=\"dd\">DDDD</d>");                     
  
    ok -p $x eq <<END;                                                             
  <a id="aa">
    <b id="bb">
      <c id="cc"><d id="dd">DDDD</d></c>
    </b>
  </a>
  END
  

=head3 putTextFirst($node, @text)

Given a B<$node> place some B<@text> first under the $node and return the new text node else return B<undef> if this is not possible.

     Parameter  Description
  1  $node      The parent node
  2  @text      The text to be added

B<Example:>


    my $a = Data::Edit::Xml::new(q(<a><b><c/></b></a>));
  
    my $b = $a->first;
  
    $b->putTextPrev(qw(Before the B node));
    $b->putTextNext(qw(After the B node));
    $b->𝗽𝘂𝘁𝗧𝗲𝘅𝘁𝗙𝗶𝗿𝘀𝘁(qw(First under the B node));
    $b->putTextLast(qw(Last under the B node));
  
    ok -p $a eq <<END;
  <a>Before the B node
    <b>First under the B node
      <c/>
  Last under the B node
    </b>
  After the B node
  </a>
  END
  

=head3 putLastAsText($node, $text, @context)

Add a new text node last under a parent and return the new text node.

     Parameter  Description
  1  $node      The parent node
  2  $text      The string to be added which might contain unparsed Xml as well as text
  3  @context   Optional context.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    ok -p $x eq <<END;                                                             
  <a id="aa">
    <b id="bb">
      <c id="cc"><d id="dd">DDDD</d></c>
    </b>
  </a>
  END
  
    $x->go(qw(b c))->𝗽𝘂𝘁𝗟𝗮𝘀𝘁𝗔𝘀𝗧𝗲𝘅𝘁("<e id=\"ee\">EEEE</e>");                      
  
    ok -p $x eq <<END;                                                             
  <a id="aa">
    <b id="bb">
      <c id="cc"><d id="dd">DDDD</d><e id="ee">EEEE</e></c>
    </b>
  </a>
  END
  

=head3 putTextLast($node, @text)

Given a B<$node> place some B<@text> last under the $node and return the new text node else return B<undef> if this is not possible.

     Parameter  Description
  1  $node      The parent node
  2  @text      The text to be added

B<Example:>


    my $a = Data::Edit::Xml::new(q(<a><b><c/></b></a>));
  
    my $b = $a->first;
  
    $b->putTextPrev(qw(Before the B node));
    $b->putTextNext(qw(After the B node));
    $b->putTextFirst(qw(First under the B node));
    $b->𝗽𝘂𝘁𝗧𝗲𝘅𝘁𝗟𝗮𝘀𝘁(qw(Last under the B node));
  
    ok -p $a eq <<END;
  <a>Before the B node
    <b>First under the B node
      <c/>
  Last under the B node
    </b>
  After the B node
  </a>
  END
  

=head3 putNextAsText($node, $text, @context)

Add a new text node following the specified B<$node> and return the new text node.

     Parameter  Description
  1  $node      The parent node
  2  $text      The string to be added which might contain unparsed Xml as well as text
  3  @context   Optional context.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    ok -p $x eq <<END;                                                             
  <a id="aa">
    <b id="bb">
      <c id="cc"><d id="dd">DDDD</d><e id="ee">EEEE</e></c>
    </b>
  </a>
  END
  
    $x->go(qw(b c))->𝗽𝘂𝘁𝗡𝗲𝘅𝘁𝗔𝘀𝗧𝗲𝘅𝘁("<n id=\"nn\">NNNN</n>");                      
  
    ok -p $x eq <<END;                                                              
  <a id="aa">
    <b id="bb">
      <c id="cc"><d id="dd">DDDD</d><e id="ee">EEEE</e></c>
  <n id="nn">NNNN</n>
    </b>
  </a>
  END
  

=head3 putTextNext($node, @text)

Given a B<$node> place some B<@text> next to the $node and return the new text node else return B<undef> if this is not possible.

     Parameter  Description
  1  $node      The parent node
  2  @text      The text to be added

B<Example:>


    my $a = Data::Edit::Xml::new(q(<a><b><c/></b></a>));
  
    my $b = $a->first;
  
    $b->putTextPrev(qw(Before the B node));
    $b->𝗽𝘂𝘁𝗧𝗲𝘅𝘁𝗡𝗲𝘅𝘁(qw(After the B node));
    $b->putTextFirst(qw(First under the B node));
    $b->putTextLast(qw(Last under the B node));
  
    ok -p $a eq <<END;
  <a>Before the B node
    <b>First under the B node
      <c/>
  Last under the B node
    </b>
  After the B node
  </a>
  END
  

=head3 putPrevAsText($node, $text, @context)

Add a new text node following the specified B<$node> and return the new text node

     Parameter  Description
  1  $node      The parent node
  2  $text      The string to be added which might contain unparsed Xml as well as text
  3  @context   Optional context.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    ok -p $x eq <<END;                                                              
  <a id="aa">
    <b id="bb">
      <c id="cc"><d id="dd">DDDD</d><e id="ee">EEEE</e></c>
  <n id="nn">NNNN</n>
    </b>
  </a>
  END
  
    $x->go(qw(b c))->𝗽𝘂𝘁𝗣𝗿𝗲𝘃𝗔𝘀𝗧𝗲𝘅𝘁("<p id=\"pp\">PPPP</p>");                      
  
    ok -p $x eq <<END;                                                            
  <a id="aa">
    <b id="bb"><p id="pp">PPPP</p>
      <c id="cc"><d id="dd">DDDD</d><e id="ee">EEEE</e></c>
  <n id="nn">NNNN</n>
    </b>
  </a>
  END
  

=head3 putTextPrev($node, @text)

Given a B<$node> place some B<@text> prior to the $node and return the new text node else return B<undef> if this is not possible.

     Parameter  Description
  1  $node      The parent node
  2  @text      The text to be added

B<Example:>


    my $a = Data::Edit::Xml::new(q(<a><b><c/></b></a>));
  
    my $b = $a->first;
  
    $b->𝗽𝘂𝘁𝗧𝗲𝘅𝘁𝗣𝗿𝗲𝘃(qw(Before the B node));
    $b->putTextNext(qw(After the B node));
    $b->putTextFirst(qw(First under the B node));
    $b->putTextLast(qw(Last under the B node));
  
    ok -p $a eq <<END;
  <a>Before the B node
    <b>First under the B node
      <c/>
  Last under the B node
    </b>
  After the B node
  </a>
  END
  

=head2 Put as tree

Add parsed text to the L<parse|/parse> tree.

=head3 putFirstAsTree($node, $text, @context)

Put parsed text first under the specified B<$node> parent and return a reference to the parsed tree. Confess if the text cannot be parsed successfully.

     Parameter  Description
  1  $node      The parent node
  2  $text      The string to be parsed and added
  3  @context   Context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


  if (1)                                                                             
   {my $a = Data::Edit::Xml::new(q(<a/>));
  
    ok -p $a eq <<END;
  <a/>
  END
  
    my $b = $a->𝗽𝘂𝘁𝗙𝗶𝗿𝘀𝘁𝗔𝘀𝗧𝗿𝗲𝗲(q(<b/>));
    ok -p $a eq <<END;
  <a>
    <b/>
  </a>
  END
  
    $b->putNextAsTree(q(<c/>));
    ok -p $a eq <<END;
  <a>
    <b/>
    <c/>
  </a>
  END
  
    my $e = $a->putLastAsTree(q(<e/>));
    ok -p $a eq <<END;
  <a>
    <b/>
    <c/>
    <e/>
  </a>
  END
  
    $e->putPrevAsTree(q(<d/>));
    ok -p $a eq <<END;
  <a>
    <b/>
    <c/>
    <d/>
    <e/>
  </a>
  END
   }
  

=head3 putLastAsTree($node, $text, @context)

Put parsed text last under the specified B<$node> parent and return a reference to the parsed tree. Confess if the text cannot be parsed successfully.

     Parameter  Description
  1  $node      The parent node
  2  $text      The string to be parsed and added
  3  @context   Context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


  if (1)                                                                             
   {my $a = Data::Edit::Xml::new(q(<a/>));
  
    ok -p $a eq <<END;
  <a/>
  END
  
    my $b = $a->putFirstAsTree(q(<b/>));
    ok -p $a eq <<END;
  <a>
    <b/>
  </a>
  END
  
    $b->putNextAsTree(q(<c/>));
    ok -p $a eq <<END;
  <a>
    <b/>
    <c/>
  </a>
  END
  
    my $e = $a->𝗽𝘂𝘁𝗟𝗮𝘀𝘁𝗔𝘀𝗧𝗿𝗲𝗲(q(<e/>));
    ok -p $a eq <<END;
  <a>
    <b/>
    <c/>
    <e/>
  </a>
  END
  
    $e->putPrevAsTree(q(<d/>));
    ok -p $a eq <<END;
  <a>
    <b/>
    <c/>
    <d/>
    <e/>
  </a>
  END
   }
  

=head3 putNextAsTree($node, $text, @context)

Put parsed text after the specified B<$node> parent and return a reference to the parsed tree. Confess if the text cannot be parsed successfully.

     Parameter  Description
  1  $node      The parent node
  2  $text      The string to be parsed and added
  3  @context   Context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


  if (1)                                                                             
   {my $a = Data::Edit::Xml::new(q(<a/>));
  
    ok -p $a eq <<END;
  <a/>
  END
  
    my $b = $a->putFirstAsTree(q(<b/>));
    ok -p $a eq <<END;
  <a>
    <b/>
  </a>
  END
  
    $b->𝗽𝘂𝘁𝗡𝗲𝘅𝘁𝗔𝘀𝗧𝗿𝗲𝗲(q(<c/>));
    ok -p $a eq <<END;
  <a>
    <b/>
    <c/>
  </a>
  END
  
    my $e = $a->putLastAsTree(q(<e/>));
    ok -p $a eq <<END;
  <a>
    <b/>
    <c/>
    <e/>
  </a>
  END
  
    $e->putPrevAsTree(q(<d/>));
    ok -p $a eq <<END;
  <a>
    <b/>
    <c/>
    <d/>
    <e/>
  </a>
  END
   }
  

=head3 putPrevAsTree($node, $text, @context)

Put parsed text before the specified B<$parent> parent and return a reference to the parsed tree. Confess if the text cannot be parsed successfully.

     Parameter  Description
  1  $node      The parent node
  2  $text      The string to be parsed and added
  3  @context   Context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


  if (1)                                                                             
   {my $a = Data::Edit::Xml::new(q(<a/>));
  
    ok -p $a eq <<END;
  <a/>
  END
  
    my $b = $a->putFirstAsTree(q(<b/>));
    ok -p $a eq <<END;
  <a>
    <b/>
  </a>
  END
  
    $b->putNextAsTree(q(<c/>));
    ok -p $a eq <<END;
  <a>
    <b/>
    <c/>
  </a>
  END
  
    my $e = $a->putLastAsTree(q(<e/>));
    ok -p $a eq <<END;
  <a>
    <b/>
    <c/>
    <e/>
  </a>
  END
  
    $e->𝗽𝘂𝘁𝗣𝗿𝗲𝘃𝗔𝘀𝗧𝗿𝗲𝗲(q(<d/>));
    ok -p $a eq <<END;
  <a>
    <b/>
    <c/>
    <d/>
    <e/>
  </a>
  END
   }
  

=head2 Put as comment

Add some comments to the parse tree relative to the specified B<$node> in the optional B<$context> and return the specified B<$node>.

=head3 putFirstAsComment($node, $text, @context)

Put a comment first under the specified B<$node> and return the specified B<$node>.

     Parameter  Description
  1  $node      Parent node
  2  $text      Comment
  3  @context   Context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b>
      <c/>
    </b>
  </a>
  END
  
    $a->𝗽𝘂𝘁𝗙𝗶𝗿𝘀𝘁𝗔𝘀𝗖𝗼𝗺𝗺𝗲𝗻𝘁(q(First));
    $a->putLastAsComment (q(Last));
    $a->go_b_c->putPrevAsComment(q(Before C));
    $a->go_b_c->putNextAsComment(q(After C));
  
    ok -p $a eq <<END;
  <a><!-- First -->
    <b><!-- Before C -->
      <c/>
  <!-- After C -->
    </b>
  <!-- Last -->
  </a>
  END
  

=head3 putLastAsComment($node, $text, @context)

Put a comment last under the specified B<$node> and return the specified B<$node>.

     Parameter  Description
  1  $node      Parent node
  2  $text      Comment
  3  @context   Context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b>
      <c/>
    </b>
  </a>
  END
  
    $a->putFirstAsComment(q(First));
    $a->𝗽𝘂𝘁𝗟𝗮𝘀𝘁𝗔𝘀𝗖𝗼𝗺𝗺𝗲𝗻𝘁 (q(Last));
    $a->go_b_c->putPrevAsComment(q(Before C));
    $a->go_b_c->putNextAsComment(q(After C));
  
    ok -p $a eq <<END;
  <a><!-- First -->
    <b><!-- Before C -->
      <c/>
  <!-- After C -->
    </b>
  <!-- Last -->
  </a>
  END
  

=head3 putNextAsComment($node, $text, @context)

Put a comment after the specified B<$node> and return the specified B<$node>

     Parameter  Description
  1  $node      Node
  2  $text      Comment
  3  @context   Context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b>
      <c/>
    </b>
  </a>
  END
  
    $a->putFirstAsComment(q(First));
    $a->putLastAsComment (q(Last));
    $a->go_b_c->putPrevAsComment(q(Before C));
    $a->go_b_c->𝗽𝘂𝘁𝗡𝗲𝘅𝘁𝗔𝘀𝗖𝗼𝗺𝗺𝗲𝗻𝘁(q(After C));
  
    ok -p $a eq <<END;
  <a><!-- First -->
    <b><!-- Before C -->
      <c/>
  <!-- After C -->
    </b>
  <!-- Last -->
  </a>
  END
  

=head3 putPrevAsComment($node, $text, @context)

Put a comment before the specified B<$parent> parentand return the specified B<$node>

     Parameter  Description
  1  $node      Node
  2  $text      Comment
  3  @context   Context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b>
      <c/>
    </b>
  </a>
  END
  
    $a->putFirstAsComment(q(First));
    $a->putLastAsComment (q(Last));
    $a->go_b_c->𝗽𝘂𝘁𝗣𝗿𝗲𝘃𝗔𝘀𝗖𝗼𝗺𝗺𝗲𝗻𝘁(q(Before C));
    $a->go_b_c->putNextAsComment(q(After C));
  
    ok -p $a eq <<END;
  <a><!-- First -->
    <b><!-- Before C -->
      <c/>
  <!-- After C -->
    </b>
  <!-- Last -->
  </a>
  END
  

=head2 Break in and out

Break nodes out of nodes or push them back

=head3 breakIn($start, @context)

Concatenate the nodes following and preceding the start node, unwrapping nodes whose tag matches the start node and return the start node. To concatenate only the preceding nodes, use L<breakInBackwards|/breakInBackwards>, to concatenate only the following nodes, use L<breakInForwards|/breakInForwards>.

     Parameter  Description
  1  $start     The start node
  2  @context   Optional context.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


      ok -p $a eq <<END;                                                           
  <a>
    <d/>
    <b>
      <c/>
      <c/>
    </b>
    <e/>
    <b>
      <c/>
      <c/>
    </b>
    <d/>
  </a>
  END
  
      $a->go(qw(b 1))->𝗯𝗿𝗲𝗮𝗸𝗜𝗻;                                                   
  
      ok -p $a eq <<END;                                                          
  <a>
    <b>
      <d/>
      <c/>
      <c/>
      <e/>
      <c/>
      <c/>
      <d/>
    </b>
  </a>
  END
  

=head3 breakInForwards($start, @context)

Concatenate the nodes following the start node, unwrapping nodes whose tag matches the start node and return the start node in the manner of L<breakIn|/breakIn>.

     Parameter  Description
  1  $start     The start node
  2  @context   Optional context..

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


      ok -p $a eq <<END;                                                          
  <a>
    <d/>
    <b>
      <c/>
      <c/>
    </b>
    <e/>
    <b>
      <c/>
      <c/>
    </b>
    <d/>
  </a>
  END
  
      $a->go(q(b))->𝗯𝗿𝗲𝗮𝗸𝗜𝗻𝗙𝗼𝗿𝘄𝗮𝗿𝗱𝘀;                                              
  
      ok -p $a eq <<END;                                                          
  <a>
    <d/>
    <b>
      <c/>
      <c/>
      <e/>
      <c/>
      <c/>
      <d/>
    </b>
  </a>
  END
  

=head3 breakInBackwards($start, @context)

Concatenate the nodes preceding the start node, unwrapping nodes whose tag matches the start node and return the start node in the manner of L<breakIn|/breakIn>.

     Parameter  Description
  1  $start     The start node
  2  @context   Optional context..

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


      ok -p $a eq <<END;                                                          
  <a>
    <d/>
    <b>
      <c/>
      <c/>
    </b>
    <e/>
    <b>
      <c/>
      <c/>
    </b>
    <d/>
  </a>
  END
  
      $a->go(qw(b 1))->𝗯𝗿𝗲𝗮𝗸𝗜𝗻𝗕𝗮𝗰𝗸𝘄𝗮𝗿𝗱𝘀;                                          
  
      ok -p $a eq <<END;                                                          
  <a>
    <b>
      <d/>
      <c/>
      <c/>
      <e/>
      <c/>
      <c/>
    </b>
    <d/>
  </a>
  END
  

=head3 breakOut($parent, @tags)

Lift child nodes with the specified tags under the specified parent node splitting the parent node into clones and return the cut out original node.

     Parameter  Description
  1  $parent    The parent node
  2  @tags      The tags of the modes to be broken out.

B<Example:>


    my $A = Data::Edit::Xml::new("<a><b><d/><c/><c/><e/><c/><c/><d/></b></a>");   
  
      $a->go(q(b))->𝗯𝗿𝗲𝗮𝗸𝗢𝘂𝘁($a, qw(d e));                                        
  
      ok -p $a eq <<END;                                                           
  <a>
    <d/>
    <b>
      <c/>
      <c/>
    </b>
    <e/>
    <b>
      <c/>
      <c/>
    </b>
    <d/>
  </a>
  END
  

=head3 breakOutChild($node, @context)

Lift the specified B<$node> up one level splitting its parent. Return the specified B<$node> on success or B<undef> if the operation is not possible.

     Parameter  Description
  1  $node      Node to break out
  2  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b>
      <c/>
      <d/>
      <e/>
    </b>
  </a>
  END
  
    my ($c, $d, $e, $b) = $a->byList;
    $d->𝗯𝗿𝗲𝗮𝗸𝗢𝘂𝘁𝗖𝗵𝗶𝗹𝗱;
  
    ok -p $a eq <<END;
  <a>
    <b>
      <c/>
    </b>
    <d/>
    <b>
      <e/>
    </b>
  </a>
  END
  

=head2 Split and Zip

Move a node up the parse tree by splitting its parent nodes

=head3 splitParentAfter($node, @context)

Finish and restart the parent of the specified B<$node> just after the specified B<$node> and return the newly created parent node on success or B<undef> on failure.

     Parameter  Description
  1  $node      The splitting node
  2  @context   Optional context.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b>
      <c/>
      <d/>
      <e/>
    </b>
  </a>
  END
  
    $a->go_b_d__splitParentAfter;
  
    ok -p $a eq <<END;
  <a>
    <b>
      <c/>
      <d/>
    </b>
    <b>
      <e/>
    </b>
  </a>
  END
  

B<spa> is a synonym for L<splitParentAfter|/splitParentAfter>.


=head3 splitParentBefore($node, @context)

Finish and restart the parent of the specified B<$node> just before the specified B<$node> and return the newly created parent node on success or B<undef> on failure.

     Parameter  Description
  1  $node      The splitting node
  2  @context   Optional context.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b>
      <c/>
      <d/>
      <e/>
    </b>
  </a>
  END
  
    $a->go_b_d__splitParentBefore;
  
    ok -p $a ne <<END;
  <a>
    <b>
      <c/>
    </b>
    <b>
      <d/>
      <e/>
    </b>
  </a>
  END
  

B<spb> is a synonym for L<splitParentBefore|/splitParentBefore>.


=head3 splitTo($node, $parent, @context)

Lift the specified B<$node> up until it splits the specified B<$parent> node. Return the specified B<$node> on success or B<undef> if the operation is not possible.

     Parameter  Description
  1  $node      Node to break out
  2  $parent    Ancestral node to split
  3  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b>
      <c>
        <d>
          <e/>
        </d>
      </c>
    </b>
  </a>
  END
  
    my ($e, $d, $c, $b) = $a->byList;
  
    ok !$a->𝘀𝗽𝗹𝗶𝘁𝗧𝗼($a);
    ok !$b->𝘀𝗽𝗹𝗶𝘁𝗧𝗼($a);
    ok  $e->𝘀𝗽𝗹𝗶𝘁𝗧𝗼($b);
  
    ok -p $a eq <<END;
  <a>
    <b>
      <c>
        <d/>
      </c>
    </b>
    <e/>
    <b>
      <c>
        <d/>
      </c>
    </b>
  </a>
  END
  
    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b>
      <c>
        <d>
          <e/>
        </d>
      </c>
    </b>
  </a>
  END
  
    my ($e, $d, $c, $b) = $a->byList;
  
    ok !$a->𝘀𝗽𝗹𝗶𝘁𝗧𝗼($a);
    ok !$b->𝘀𝗽𝗹𝗶𝘁𝗧𝗼($a);
    ok  $e->𝘀𝗽𝗹𝗶𝘁𝗧𝗼($b);
  
    ok -p $a eq <<END;
  <a>
    <b>
      <c>
        <d/>
      </c>
    </b>
    <e/>
    <b>
      <c>
        <d/>
      </c>
    </b>
  </a>
  END
  
    ok $e->zipDownOnce;                                                           # Invalidates b
    ok -p $a eq <<END;
  <a>
    <b>
      <c>
        <d/>
      </c>
      <e/>
      <c>
        <d/>
      </c>
    </b>
  </a>
  END
  
   ok  $e->𝘀𝗽𝗹𝗶𝘁𝗧𝗼($a->first);                                                    # b invalidated by zipDownOnce
   $e->zipDown;
  
   ok -p $a eq <<END;
  <a>
    <b>
      <c>
        <d>
          <e/>
        </d>
      </c>
    </b>
  </a>
  END
  

=head3 adoptChild($parent, $child, @context)

Lift the specified B<$child> node up until it is an immediate child of the specified B<$parent> node by splitting any intervening nodes. Return the specified B<$child> node on success or B<undef> if the operation is not possible.

     Parameter  Description
  1  $parent    Adopting parent node
  2  $child     Child node to adopt
  3  @context   Optional context of child

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b>
      <c>
        <d>
          <e/>
        </d>
      </c>
    </b>
  </a>
  END
  
    my ($e, $d, $c, $b) = $a->byList;
  
    $a->𝗮𝗱𝗼𝗽𝘁𝗖𝗵𝗶𝗹𝗱($e);
    ok -p $a eq <<END;
  <a>
    <b>
      <c>
        <d/>
      </c>
    </b>
    <e/>
    <b>
      <c>
        <d/>
      </c>
    </b>
  </a>
  END
  
    $e->zipDown;
    ok -p $a eq <<END;
  <a>
    <b>
      <c>
        <d>
          <e/>
        </d>
      </c>
    </b>
  </a>
  END
  
  

=head3 zipDownOnce($node, @context)

Push a node down one level by making it a child of a node formed by merging the preceding and following siblings if they have the same tag.

     Parameter  Description
  1  $node      Node
  2  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b>
      <c>
        <d>
          <e/>
        </d>
      </c>
    </b>
  </a>
  END
  
    my ($e, $d, $c, $b) = $a->byList;
  
    ok !$a->splitTo($a);
    ok !$b->splitTo($a);
    ok  $e->splitTo($b);
  
    ok -p $a eq <<END;
  <a>
    <b>
      <c>
        <d/>
      </c>
    </b>
    <e/>
    <b>
      <c>
        <d/>
      </c>
    </b>
  </a>
  END
  
    ok $e->𝘇𝗶𝗽𝗗𝗼𝘄𝗻𝗢𝗻𝗰𝗲;                                                           # Invalidates b
    ok -p $a eq <<END;
  <a>
    <b>
      <c>
        <d/>
      </c>
      <e/>
      <c>
        <d/>
      </c>
    </b>
  </a>
  END
  
   ok  $e->splitTo($a->first);                                                    # b invalidated by 𝘇𝗶𝗽𝗗𝗼𝘄𝗻𝗢𝗻𝗰𝗲
   $e->zipDown;
  
   ok -p $a eq <<END;
  <a>
    <b>
      <c>
        <d>
          <e/>
        </d>
      </c>
    </b>
  </a>
  END
  

=head3 zipDown($node, @context)

Push a node down as many levels as possible by making it a child of a node formed by merging the preceding and following siblings if they have the same tag.

     Parameter  Description
  1  $node      Node
  2  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b>
      <c>
        <d>
          <e/>
        </d>
      </c>
    </b>
  </a>
  END
  
    my ($e, $d, $c, $b) = $a->byList;
  
    $a->adoptChild($e);
    ok -p $a eq <<END;
  <a>
    <b>
      <c>
        <d/>
      </c>
    </b>
    <e/>
    <b>
      <c>
        <d/>
      </c>
    </b>
  </a>
  END
  
    $e->𝘇𝗶𝗽𝗗𝗼𝘄𝗻;
    ok -p $a eq <<END;
  <a>
    <b>
      <c>
        <d>
          <e/>
        </d>
      </c>
    </b>
  </a>
  END
  
  
    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b>
      <c>
        <d>
          <e/>
        </d>
      </c>
    </b>
  </a>
  END
  
    my ($e, $d, $c, $b) = $a->byList;
  
    ok !$a->splitTo($a);
    ok !$b->splitTo($a);
    ok  $e->splitTo($b);
  
    ok -p $a eq <<END;
  <a>
    <b>
      <c>
        <d/>
      </c>
    </b>
    <e/>
    <b>
      <c>
        <d/>
      </c>
    </b>
  </a>
  END
  
    ok $e->zipDownOnce;                                                           # Invalidates b
    ok -p $a eq <<END;
  <a>
    <b>
      <c>
        <d/>
      </c>
      <e/>
      <c>
        <d/>
      </c>
    </b>
  </a>
  END
  
   ok  $e->splitTo($a->first);                                                    # b invalidated by zipDownOnce
   $e->𝘇𝗶𝗽𝗗𝗼𝘄𝗻;
  
   ok -p $a eq <<END;
  <a>
    <b>
      <c>
        <d>
          <e/>
        </d>
      </c>
    </b>
  </a>
  END
  

=head3 splitAndWrapFromStart($parent, $split, $wrap, %attributes)

Split the child nodes of B<$parent> on the child nodes with a tag of B<$split> wrapping the splitting node and all preceding nodes from the previous splitting node or the start with the specified B<$wrap>ping node with optional B<%attributes>.  Returns an array of the wrapping nodes created.

     Parameter    Description
  1  $parent      Parent node
  2  $split       Tag of splitting nodes
  3  $wrap        Tag for wrapping node
  4  %attributes  Attributes for wrapping nodes

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


  
    my $A = Data::Edit::Xml::new(<<END);
  <a>
    <PP/>
    <b>
      <c/>
    </b>
    <QQ/>
    <b>
      <d/>
    </b>
    <RR/>
    <b>
      <e/>
    </b>
    <SS/>
  </a>
  END
  
    my $a = $A->clone;
    ok 3 == $a->𝘀𝗽𝗹𝗶𝘁𝗔𝗻𝗱𝗪𝗿𝗮𝗽𝗙𝗿𝗼𝗺𝗦𝘁𝗮𝗿𝘁(qw(b B));
  
    ok -p $a eq <<END;
  <a>
    <B>
      <PP/>
      <b>
        <c/>
      </b>
    </B>
    <B>
      <QQ/>
      <b>
        <d/>
      </b>
    </B>
    <B>
      <RR/>
      <b>
        <e/>
      </b>
    </B>
    <SS/>
  </a>
  END
  
  
    my $b = $A->clone;
    ok 3 == $b->splitAndWrapToEnd(qw(b B));
  
    ok -p $b eq <<END;
  <a>
    <PP/>
    <B>
      <b>
        <c/>
      </b>
      <QQ/>
    </B>
    <B>
      <b>
        <d/>
      </b>
      <RR/>
    </B>
    <B>
      <b>
        <e/>
      </b>
      <SS/>
    </B>
  </a>
  END
  

=head3 splitAndWrapToEnd($parent, $split, $wrap, %attributes)

Split the sequence of child nodes under the specified B<$parent> node on those child nodes whose tag value is B<$split> wrapping the splitting node and all following nodes up until the next splitting node or the end of the sequence with newly created nodes whose tag is B<$wrap> with optional attributes B<%attributes>.  Returns an array of the wrapping nodes so created.

     Parameter    Description
  1  $parent      Parent node
  2  $split       Tag of splitting nodes
  3  $wrap        Tag for wrapping node
  4  %attributes  Attributes for wrapping nodes

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


  
    my $A = Data::Edit::Xml::new(<<END);
  <a>
    <PP/>
    <b>
      <c/>
    </b>
    <QQ/>
    <b>
      <d/>
    </b>
    <RR/>
    <b>
      <e/>
    </b>
    <SS/>
  </a>
  END
  
    my $a = $A->clone;
    ok 3 == $a->splitAndWrapFromStart(qw(b B));
  
    ok -p $a eq <<END;
  <a>
    <B>
      <PP/>
      <b>
        <c/>
      </b>
    </B>
    <B>
      <QQ/>
      <b>
        <d/>
      </b>
    </B>
    <B>
      <RR/>
      <b>
        <e/>
      </b>
    </B>
    <SS/>
  </a>
  END
  
  
    my $b = $A->clone;
    ok 3 == $b->𝘀𝗽𝗹𝗶𝘁𝗔𝗻𝗱𝗪𝗿𝗮𝗽𝗧𝗼𝗘𝗻𝗱(qw(b B));
  
    ok -p $b eq <<END;
  <a>
    <PP/>
    <B>
      <b>
        <c/>
      </b>
      <QQ/>
    </B>
    <B>
      <b>
        <d/>
      </b>
      <RR/>
    </B>
    <B>
      <b>
        <e/>
      </b>
      <SS/>
    </B>
  </a>
  END
  

=head2 Replace

Replace nodes in the L<parse|/parse> tree with nodes or text

=head3 replaceWith($old, $new, @context)

Replace a node (and all its content) with a L<new node|/newTag> (and all its content) and return the new node. If the node to be replaced is the root of the L<parse|/parse> tree then no action is taken other then returning the new node.

     Parameter  Description
  1  $old       Old node
  2  $new       New node
  3  @context   Optional context..

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


     {my $x = Data::Edit::Xml::new(qq(<a><b><c id="cc"/></b></a>));               
  
      $x->go(qw(b c))->𝗿𝗲𝗽𝗹𝗮𝗰𝗲𝗪𝗶𝘁𝗵($x->newTag(qw(d id dd)));                      
  
      ok -s $x eq '<a><b><d id="dd"/></b></a>';                                   
  

=head3 replaceWithText($old, $text, @context)

Replace a node (and all its content) with a new text node and return the new node.

     Parameter  Description
  1  $old       Old node
  2  $text      Text of new node
  3  @context   Optional context.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


     {my $x = Data::Edit::Xml::new(qq(<a><b><c id="cc"/></b></a>));               
  
      $x->go(qw(b c))->𝗿𝗲𝗽𝗹𝗮𝗰𝗲𝗪𝗶𝘁𝗵𝗧𝗲𝘅𝘁(qq(BBBB));                                 
  
      ok -s $x eq '<a><b>BBBB</b></a>';                                           
  

=head3 replaceWithBlank($old, @context)

Replace a node (and all its content) with a new blank text node and return the new node.

     Parameter  Description
  1  $old       Old node
  2  @context   Optional context.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


     {my $x = Data::Edit::Xml::new(qq(<a><b><c id="cc"/></b></a>));               
  
      $x->go(qw(b c))->𝗿𝗲𝗽𝗹𝗮𝗰𝗲𝗪𝗶𝘁𝗵𝗕𝗹𝗮𝗻𝗸;                                          
  
      ok -s $x eq '<a><b> </b></a>';                                              
  

=head3 replaceContentWithMovedContent($node, @nodes)

Replace the content of a specified target node with the contents of the specified source nodes removing the content from each source node and return the target node.

     Parameter  Description
  1  $node      Target node
  2  @nodes     Source nodes

B<Example:>


    my $a = Data::Edit::Xml::new(<<END);                                          
  <a>
    <b>
       <b1/>
       <b2/>
    </b>
    <c>
       <c1/>
       <c2/>
    </c>
    <d>
       <d1/>
       <d2/>
    </d>
  </a>
  END
  
    my ($b, $c, $d) = $a->contents;                                               
  
    $d->𝗿𝗲𝗽𝗹𝗮𝗰𝗲𝗖𝗼𝗻𝘁𝗲𝗻𝘁𝗪𝗶𝘁𝗵𝗠𝗼𝘃𝗲𝗱𝗖𝗼𝗻𝘁𝗲𝗻𝘁($c, $b);                                   
  
    ok -p $a eq <<END;                                                            
  <a>
    <b/>
    <c/>
    <d>
      <c1/>
      <c2/>
      <b1/>
      <b2/>
    </d>
  </a>
  END
  
    my $a = Data::Edit::Xml::new(<<END);                                          
  <a>
    <d>
       <b>
         <b1/>
         <b2/>
      </b>
      <c>
         <c1/>
         <c2/>
      </c>
    </d>
  </a>
  END
  
    my ($d)     = $a->contents;                                                   
  
    my ($b, $c) = $d->contents;                                                   
  
    $d->𝗿𝗲𝗽𝗹𝗮𝗰𝗲𝗖𝗼𝗻𝘁𝗲𝗻𝘁𝗪𝗶𝘁𝗵𝗠𝗼𝘃𝗲𝗱𝗖𝗼𝗻𝘁𝗲𝗻𝘁($c, $b);                                   
  
    ok -p $a eq <<END;                                                            
  <a>
    <d>
      <c1/>
      <c2/>
      <b1/>
      <b2/>
    </d>
  </a>
  END
  

=head3 replaceContentWith($node, @content)

Replace the content of a node with the specified nodes and return the replaced content

     Parameter  Description
  1  $node      Node whose content is to be replaced
  2  @content   New content

B<Example:>


     {my $x = Data::Edit::Xml::new(qq(<a><b/><c/></a>));                          
  
      $x->𝗿𝗲𝗽𝗹𝗮𝗰𝗲𝗖𝗼𝗻𝘁𝗲𝗻𝘁𝗪𝗶𝘁𝗵(map {$x->newTag($_)} qw(B C));                       
  
      ok -s $x eq '<a><B/><C/></a>';                                              
  

=head3 replaceContentWithText($node, @text)

Replace the content of a node with the specified texts and return the replaced content

     Parameter  Description
  1  $node      Node whose content is to be replaced
  2  @text      Texts to form new content

B<Example:>


     {my $x = Data::Edit::Xml::new(qq(<a><b/><c/></a>));                          
  
      $x->𝗿𝗲𝗽𝗹𝗮𝗰𝗲𝗖𝗼𝗻𝘁𝗲𝗻𝘁𝗪𝗶𝘁𝗵𝗧𝗲𝘅𝘁(qw(b c));                                        
  
      ok -s $x eq '<a>bc</a>';                                                    
  

=head2 Swap

Swap nodes both singly and in blocks

=head3 invert($parent, @context)

Swap a parent and child node where the child is the only child of the parent and return the parent.

     Parameter  Description
  1  $parent    Parent
  2  @context   Context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


   {my $a = Data::Edit::Xml::new(<<END);                                          
  <a>
    <b id="b">
      <c id="c">
        <d/>
        <e/>
      </c>
    </b>
  </a>
  END
  
    $a->first->𝗶𝗻𝘃𝗲𝗿𝘁;                                                            
  
    ok -p $a eq <<END;                                                            
  <a>
    <c id="c">
      <b id="b">
        <d/>
        <e/>
      </b>
    </c>
  </a>
  END
  
    $a->first->𝗶𝗻𝘃𝗲𝗿𝘁;                                                            
  
    ok -p $a eq <<END;                                                            
  <a>
    <b id="b">
      <c id="c">
        <d/>
        <e/>
      </c>
    </b>
  </a>
  END
  

=head3 invertFirst($parent, @context)

Swap a parent and child node where the child is the first child of the parent by placing the parent last in the child. Return the parent.

     Parameter  Description
  1  $parent    Parent
  2  @context   Context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


   {my $a = Data::Edit::Xml::new(<<END);                                           
  <a>
    <b>
      <c>
        <d/>
        <e/>
      </c>
      <f/>
      <g/>
    </b>
  </a>
  END
  
    ok -p $a eq <<END;                                                             
  <a>
    <c>
      <d/>
      <e/>
      <b>
        <f/>
        <g/>
      </b>
    </c>
  </a>
  END
  

=head3 invertLast($parent, @context)

Swap a parent and child node where the child is the last child of the parent by placing the parent first in the child. Return the parent.

     Parameter  Description
  1  $parent    Parent
  2  @context   Context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


   {my $a = Data::Edit::Xml::new(<<END);                                           
  <a>
    <b>
      <c>
        <d/>
        <e/>
      </c>
      <f/>
      <g/>
    </b>
  </a>
  END
  
    ok -p $a eq <<END;                                                             
  <a>
    <c>
      <d/>
      <e/>
      <b>
        <f/>
        <g/>
      </b>
    </c>
  </a>
  END
  
    ok -p $a eq <<END;                                                            
  <a>
    <b>
      <c>
        <d/>
        <e/>
      </c>
      <f/>
      <g/>
    </b>
  </a>
  END
  

=head3 swap($first, $second, @context)

Swap two nodes optionally checking that the first node is in the specified context and return the first node.

     Parameter  Description
  1  $first     First node
  2  $second    Second node
  3  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    ok <<END eq -p $x;                                                             
  <x>
    <a a="1" b="2"/>
    <b/>
    <c a="1" b="3" c="4"/>
  </x>
  END
  
    $a->𝘀𝘄𝗮𝗽($c);                                                                 
  
    ok <<END eq -p $x;                                                             
  <x>
    <c a="1" b="3" c="4"/>
    <b/>
    <a a="1" b="2"/>
  </x>
  END
  

=head3 swapFirstSibling($node, @context)

Swap B<$node> with its first sibling node and return B<$node>.

     Parameter  Description
  1  $node      Node
  2  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b/>
    <c/>
    <d/>
    <e/>
    <f/>
    <g/>
  </a>
  END
  
    $a->go_f__swapFirstSibling;
  
    ok -p $a eq <<END;
  <a>
    <f/>
    <c/>
    <d/>
    <e/>
    <b/>
    <g/>
  </a>
  END
  
    $a->go_c__swapLastSibling;
  
    ok -p $a eq <<END;
  <a>
    <f/>
    <g/>
    <d/>
    <e/>
    <b/>
    <c/>
  </a>
  END
  

B<sfs> is a synonym for L<swapFirstSibling|/swapFirstSibling>.


=head3 swapNext($node, @context)

Swap B<$node> with its following node if $B<$node> matches the first element of the specified context and the next node matches the rest.  Return the node that originally followed B<$node> on success or B<undef> on failure.

     Parameter  Description
  1  $node      Node
  2  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b>
      <c>
        <d/>
        <e/>
      </c>
      <f>
        <g/>
      </f>
    </b>
  </a>
  END
  
    my ($d, $e, $c, $g, $f, $b) = $a->byList;
  
    ok -t $b eq q(b);
  
    $d->𝘀𝘄𝗮𝗽𝗡𝗲𝘅𝘁; $g->swapPrev;
    ok $d->after($e);
    ok -p $a eq <<END;
  <a>
    <b>
      <c>
        <e/>
        <d/>
      </c>
      <f>
        <g/>
      </f>
    </b>
  </a>
  END
  
    $d->swapPrev; $c->𝘀𝘄𝗮𝗽𝗡𝗲𝘅𝘁;
    ok $f->before($c);
    ok -p $a eq <<END;
  <a>
    <b>
      <f>
        <g/>
      </f>
      <c>
        <d/>
        <e/>
      </c>
    </b>
  </a>
  END
  

B<sn> is a synonym for L<swapNext|/swapNext>.


=head3 swapPrev($node, @context)

Swap B<$node> with its preceding node if $B<$node> matches the first element of the specified context and the previous node matches the rest.  Return the node that originally followed B<$node> on success or B<undef> on failure.

     Parameter  Description
  1  $node      Node
  2  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b>
      <c>
        <d/>
        <e/>
      </c>
      <f>
        <g/>
      </f>
    </b>
  </a>
  END
  
    my ($d, $e, $c, $g, $f, $b) = $a->byList;
  
    ok -t $b eq q(b);
  
    $d->swapNext; $g->𝘀𝘄𝗮𝗽𝗣𝗿𝗲𝘃;
    ok $d->after($e);
    ok -p $a eq <<END;
  <a>
    <b>
      <c>
        <e/>
        <d/>
      </c>
      <f>
        <g/>
      </f>
    </b>
  </a>
  END
  
    $d->𝘀𝘄𝗮𝗽𝗣𝗿𝗲𝘃; $c->swapNext;
    ok $f->before($c);
    ok -p $a eq <<END;
  <a>
    <b>
      <f>
        <g/>
      </f>
      <c>
        <d/>
        <e/>
      </c>
    </b>
  </a>
  END
  

B<sp> is a synonym for L<swapPrev|/swapPrev>.


=head3 swapLastSibling($node, @context)

Swap B<$node> with its last sibling node and return B<$node>.

     Parameter  Description
  1  $node      Node
  2  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b/>
    <c/>
    <d/>
    <e/>
    <f/>
    <g/>
  </a>
  END
  
    $a->go_f__swapFirstSibling;
  
    ok -p $a eq <<END;
  <a>
    <f/>
    <c/>
    <d/>
    <e/>
    <b/>
    <g/>
  </a>
  END
  
    $a->go_c__swapLastSibling;
  
    ok -p $a eq <<END;
  <a>
    <f/>
    <g/>
    <d/>
    <e/>
    <b/>
    <c/>
  </a>
  END
  

B<sls> is a synonym for L<swapLastSibling|/swapLastSibling>.


=head3 swapTags($first, $second, @context)

Swap the tags of two nodes optionally checking that the first node is in the specified context and return (B<$first>, B<$second>) nodes.

     Parameter  Description
  1  $first     First node
  2  $second    Second node
  3  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b>
      <c/>
    </b>
  </a>
  END
  
    $a->𝘀𝘄𝗮𝗽𝗧𝗮𝗴𝘀($a->first);
    ok -p $a eq <<END;
  <b>
    <a>
      <c/>
    </a>
  </b>
  END
  
    my ($C, $A) = $a->downWhileFirst->swapTagWithParent;
    ok -t $A eq q(a);
    ok -t $C eq q(c);
    ok -p $a eq <<END;
  <b>
    <c>
      <a/>
    </c>
  </b>
  END
  

=head3 swapTagWithParent($node, @context)

Swap the tags of the specified B<$node> and its parent optionally checking that the B<$node> is in the specified context and return (parent of B<$node>, B<$node>) or () if there is no such parent node.

     Parameter  Description
  1  $node      Node
  2  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b>
      <c/>
    </b>
  </a>
  END
  
    $a->swapTags($a->first);
    ok -p $a eq <<END;
  <b>
    <a>
      <c/>
    </a>
  </b>
  END
  
    my ($C, $A) = $a->downWhileFirst->𝘀𝘄𝗮𝗽𝗧𝗮𝗴𝗪𝗶𝘁𝗵𝗣𝗮𝗿𝗲𝗻𝘁;
    ok -t $A eq q(a);
    ok -t $C eq q(c);
    ok -p $a eq <<END;
  <b>
    <c>
      <a/>
    </c>
  </b>
  END
  

=head3 reorder($node, $first, $second, @context)

If the current B<$node> has a context of (B<$first>, @context) and the preceding node has a context of (B<$second>, B<@context>), then swap the current node with the previous node.Return the current node regardless

     Parameter  Description
  1  $node      Node
  2  $first     First tag
  3  $second    Second tag
  4  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <d>
      <c/>
    </d>
    <b>
      <c/>
    </b>
  </a>
  END
  
    $a->by(sub {$_->reorder_b_d_a});
  
    ok -p $a eq <<END;
  <a>
    <b>
      <c/>
    </b>
    <d>
      <c/>
    </d>
  </a>
  END
  
    $a->by(sub {$_->reorder_b_d_a});
  
    ok -p $a eq <<END;
  <a>
    <b>
      <c/>
    </b>
    <d>
      <c/>
    </d>
  </a>
  END
  

=head2 Wrap and unwrap

Wrap and unwrap nodes to alter the depth of the L<parse|/parse> tree

=head3 Wrap

Wrap nodes to deepen the L<parse|/parse> tree

=head4 wrapWith($node, $tag, @context)

Wrap the specified B<$node> in a new node created from the specified B<$tag> in the optional B<@context> forcing the specified $node down to deepen the L<parse|/parse> tree - return the new wrapping node or B<undef> if this is not possible. See L<addWrapWith|/addWrapWith> to perform this operation conditionally.

     Parameter  Description
  1  $node      Node
  2  $tag       Tag for the new node or tag
  3  @context   Optional context

B<Example:>


    ok -p $x eq <<END;                                                            
  <a>
    <b>
      <c id="11"/>
    </b>
  </a>
  END
  
    $x->go(qw(b c))->𝘄𝗿𝗮𝗽𝗪𝗶𝘁𝗵(qw(C));                                             
  
    ok -p $x eq <<END;                                                            
  <a>
    <b>
      <C>
        <c id="11"/>
      </C>
    </b>
  </a>
  END
  

=head4 wrapWithDup($node, @context)

Wrap the specified B<$node> in a new node with the same tag as $node in the optional B<@context> making $node an L<only child|/isOnlyChild> of the new node. Return the new wrapping node or B<undef> if this is not possible.

     Parameter  Description
  1  $node      Node
  2  @context   Optional context

B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a><b><c/></b></a>
  END
  
    my ($c, $b) = $a->byList;
  
    $c->𝘄𝗿𝗮𝗽𝗪𝗶𝘁𝗵𝗗𝘂𝗽->id = 'c';
    ok -p $a eq <<END;
  <a>
    <b>
      <c id="c">
        <c/>
      </c>
    </b>
  </a>
  END
  
    $b->wrapContentWithDup->id = 'b';
    ok -p $a eq <<END;
  <a>
    <b>
      <b id="b">
        <c id="c">
          <c/>
        </c>
      </b>
    </b>
  </a>
  END
  

=head4 wrapUp($node, @tags)

Wrap the specified B<$node> in a sequence of new nodes created from the specified B<@tags> forcing the original node down - deepening the L<parse|/parse> tree - return the array of wrapping nodes if want array else the last wrapping node.

     Parameter  Description
  1  $node      Node to wrap
  2  @tags      Tags to wrap the node with - with the uppermost tag rightmost.

B<Example:>


    my $c = Data::Edit::Xml::newTree("c", id=>33);                                
  
    my ($b, $a) = $c->𝘄𝗿𝗮𝗽𝗨𝗽(qw(b a));                                            
  
    ok -p $a eq <<'END';                                                          
  <a>
    <b>
      <c id="33"/>
    </b>
  </a>
  END
  

=head4 wrapWithN($node, $N, @context)

Wrap this B<$node> with the first B<$N> elements of B<@context>. If B<@context> contains more then B<$N> entries, the remainder are checked as the context of B<$node>. Returns the upper most wrapping node or B<undef> if the specified B<$node> does not match these conditions.

     Parameter  Description
  1  $node      Node
  2  $N         Number of tags wrapping tags
  3  @context   Tags and optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <d/>
  </a>
  END
  
    ok !$a->go_d__wrapWithN_2_c_b_a;
    $a->by(sub{$_->wrapWithN_2_c_b_d_a});
  
    ok -p $a eq <<END;
  <a>
    <b>
      <c>
        <d/>
      </c>
    </b>
  </a>
  END
  

=head4 wrapWithAll($node, @tags)

Wrap this B<$node> wrapped with the specified B<@tags> and return the last wrapping node.

     Parameter  Description
  1  $node      Node
  2  @tags      Tags

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <d/>
  </a>
  END
  
    $a->go_d__wrapWithAll_c_b;
  
    ok -p $a eq <<END;
  <a>
    <b>
      <c>
        <d/>
      </c>
    </b>
  </a>
  END
  
    my $a = Data::Edit::Xml::new(q(<a id="c4"/>));
    my $b = Data::Edit::Xml::new(q(<a id="c5"/>));
  
    ok -M $a eq -M $b;
  

B<ww> is a synonym for L<wrapWithAll|/wrapWithAll>.


=head4 wrapDown($node, @tags)

Wrap the content of the specified B<$node> in a sequence of new nodes forcing the original node up - deepening the L<parse|/parse> tree - return the array of wrapping nodes if want array else the last wrapping node.

     Parameter  Description
  1  $node      Node to wrap
  2  @tags      Tags to wrap the node with - with the uppermost tag rightmost.

B<Example:>


    my $a = Data::Edit::Xml::newTree("a", id=>33);                                
  
    my ($b, $c) = $a->𝘄𝗿𝗮𝗽𝗗𝗼𝘄𝗻(qw(b c));                                          
  
    ok -p $a eq <<END;                                                            
  <a id="33">
    <b>
      <c/>
    </b>
  </a>
  END
  

=head4 wrapContentWith($old, $tag, %attributes)

Wrap the content of the specified B<$node> in a new node created from the specified B<$tag> and B<%attributes>: the specified B<$node> then contains just the new node which, in turn, contains all the content of the specified B<$node>.

Returns the new wrapping node.

     Parameter    Description
  1  $old         Node
  2  $tag         Tag for new node
  3  %attributes  Attributes for new node.

B<Example:>


    ok -p $x eq <<END;                                                            
  <a>
    <b>
      <c/>
      <c/>
      <c/>
    </b>
  </a>
  END
  
    $x->go(q(b))->𝘄𝗿𝗮𝗽𝗖𝗼𝗻𝘁𝗲𝗻𝘁𝗪𝗶𝘁𝗵(qw(D id DD));                                   
  
    ok -p $x eq <<END;                                                            
  <a>
    <b>
      <D id="DD">
        <c/>
        <c/>
        <c/>
      </D>
    </b>
  </a>
  END
  
    ok -p $a eq <<END;                                                            
  <a>
    <b id="1"/>
    <c id="2"/>
    <d id="3"/>
    <c id="4"/>
    <d id="5"/>
    <e id="6"/>
    <b id="7"/>
    <c id="8"/>
    <d id="9"/>
    <f id="10"/>
  </a>
  END
  

B<wcw> is a synonym for L<wrapContentWith|/wrapContentWith>.


=head4 wrapContentWithDup($node, @context)

Wrap the content if the specified B<$node> in a new node with the same tag as $node in the optional B<@context> making the new node an L<only child|/isOnlyChild> of $node. Return the new wrapping node or B<undef> if this is not possible.

     Parameter  Description
  1  $node      Node
  2  @context   Optional context

B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a><b><c/></b></a>
  END
  
    my ($c, $b) = $a->byList;
  
    $c->wrapWithDup->id = 'c';
    ok -p $a eq <<END;
  <a>
    <b>
      <c id="c">
        <c/>
      </c>
    </b>
  </a>
  END
  
    $b->𝘄𝗿𝗮𝗽𝗖𝗼𝗻𝘁𝗲𝗻𝘁𝗪𝗶𝘁𝗵𝗗𝘂𝗽->id = 'b';
    ok -p $a eq <<END;
  <a>
    <b>
      <b id="b">
        <c id="c">
          <c/>
        </c>
      </b>
    </b>
  </a>
  END
  

=head4 wrapSiblingsBefore($node, $tag, %attributes)

If there are any siblings before the specified B<$node>, wrap them with a new node created from the specified B<$tag> and B<%attributes> and return the newly created node.

Returns B<undef> if the specified B<$node> is the first node under its parent.

     Parameter    Description
  1  $node        Node to wrap before
  2  $tag         Tag for new node
  3  %attributes  Attributes for new node.

B<Example:>


   {my $a = Data::Edit::Xml::new(q(<a><b/><c/><d/></a>));                         
  
    my ($b, $c, $d) = $a->byList;                                                 
  
    $c->𝘄𝗿𝗮𝗽𝗦𝗶𝗯𝗹𝗶𝗻𝗴𝘀𝗕𝗲𝗳𝗼𝗿𝗲(q(X));                                                 
  
    ok -p $a eq <<END;                                                            
  <a>
    <X>
      <b/>
    </X>
    <c/>
    <d/>
  </a>
  END
  

=head4 wrapFromFirst($node, $tag, %attributes)

Wrap this B<$node> and any preceding siblings with a new node created from the specified B<$tag> and B<%attributes> and return the wrapping node.

     Parameter    Description
  1  $node        Node to wrap before
  2  $tag         Tag for new node
  3  %attributes  Attributes for new node.

B<Example:>


  if (1)                                                                          
   {my $a = Data::Edit::Xml::new(q(<a><b/><c/><d/></a>));
  
    ok -p $a eq <<END;
  <a>
    <b/>
    <c/>
    <d/>
  </a>
  END
  
    $a->go_c->wrapFromFirst_B;
    ok -p $a eq <<END;
  <a>
    <B>
      <b/>
      <c/>
    </B>
    <d/>
  </a>
  END
   }
  

=head4 wrapFromFirstOrLastIn($node, $tag, @targets)

Wrap inclusively from the first sibling node to the specified B<$node> or from the last prior sibling node whose tag matches one of the tags in B<@targets> to the specified B<$node> using B<$tag> as the tag of the wrapping node and return the wrapping node.

     Parameter  Description
  1  $node      Node at which to start to wrap
  2  $tag       Wrapping tag
  3  @targets   Tags at which to end the wrap.

B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a><b/><c/><d/><e/><f/></a>
  END
  
    $a->go_d__wrapFromFirstOrLastIn_g_a_b_c;
    ok -p $a eq <<END;
  <a>
    <b/>
    <g>
      <c/>
      <d/>
    </g>
    <e/>
    <f/>
  </a>
  END
  
    $a->go_e__wrapFromFirstOrLastIn_i_A_B_C;
    ok -p $a eq <<END;
  <a>
    <i>
      <b/>
      <g>
        <c/>
        <d/>
      </g>
      <e/>
    </i>
    <f/>
  </a>
  END
  

=head4 wrapFirstN($node, $N, $tag, @context)

Wrap the first B<$N> nodes under this B<$node> in the optional B<@context> with the specified B<$tag> and return the new node or B<undef> if there are no such nodes to wrap.

     Parameter  Description
  1  $node      Node
  2  $N         Number of nodes to wrap
  3  $tag       Wrapping tag
  4  @context   Optional context

B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b/>
    <c/>
    <d/>
  </a>
  END
  
    $a->wrapFirstN_2_Z;
  
    ok -p $a eq <<END;
  <a>
    <Z>
      <b/>
      <c/>
    </Z>
    <d/>
  </a>
  END
  

=head4 wrapSiblingsBetween($first, $last, $tag, %attributes)

If there are any siblings between the specified B<$node>s, wrap them with a new node created from the specified B<$tag> and B<%attributes>. Return the wrapping node else B<undef> if there are no nodes to wrap.

     Parameter    Description
  1  $first       First sibling
  2  $last        Last sibling
  3  $tag         Tag for new node
  4  %attributes  Attributes for new node.

B<Example:>


   {my $a = Data::Edit::Xml::new(q(<a><b/><c/><d/></a>));                         
  
    my ($b, $c, $d) = $a->byList;                                                 
  
    $b->𝘄𝗿𝗮𝗽𝗦𝗶𝗯𝗹𝗶𝗻𝗴𝘀𝗕𝗲𝘁𝘄𝗲𝗲𝗻($d, q(Y));                                            
  
    ok -p $a eq <<END;                                                            
  <a>
    <b/>
    <Y>
      <c/>
    </Y>
    <d/>
  </a>
  END
  

=head4 wrapSiblingsAfter($node, $tag, %attributes)

If there are any siblings after the specified B<$node>, wrap them with a new node created from the specified B<$tag> and B<%attributes> and return the newly created node.

Returns B<undef> if the specified B<$node> is the last node under its parent.

     Parameter    Description
  1  $node        Node to wrap before
  2  $tag         Tag for new node
  3  %attributes  Attributes for new node.

B<Example:>


   {my $a = Data::Edit::Xml::new(q(<a><b/><c/><d/></a>));                         
  
    my ($b, $c, $d) = $a->byList;                                                 
  
    $c->𝘄𝗿𝗮𝗽𝗦𝗶𝗯𝗹𝗶𝗻𝗴𝘀𝗔𝗳𝘁𝗲𝗿(q(Y));                                                  
  
    ok -p $a eq <<END;                                                            
  <a>
    <b/>
    <c/>
    <Y>
      <d/>
    </Y>
  </a>
  END
  

=head4 wrapNext($node, $tag, @context)

Wrap this B<$node> and the following sibling with a new node created from the specified B<$tag>. If the optional context is specified then it should match the first node, next node and the context of the parent of the B<$node> as far as it is specified.  Return the new node created or B<undef> on failure.

     Parameter  Description
  1  $node      Node to wrap before
  2  $tag       Tag for new node
  3  @context   Context.

B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b/>
    <c/>
    <d/>
  </a>
  END
  
    my ($b, $c, $d) = $a->byList;
  
    $b->wrapNext_e;
    ok  -p $a eq <<END;
  <a>
    <e>
      <b/>
      <c/>
    </e>
    <d/>
  </a>
  END
  

=head4 wrapNextN($node, $N, $tag, @context)

Wrap this B<$node>and the next B<$N>-1 nodes following this B<$node> in the optional B<@context> with the specified B<$tag> and return the new node or B<undef> if there are no such nodes to wrap.

     Parameter  Description
  1  $node      Node
  2  $N         Number of nodes to wrap
  3  $tag       Wrapping tag
  4  @context   Optional context

B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b/>
    <c/>
    <d/>
    <e/>
  </a>
  END
  
    $a->go_c__wrapNextN_2_Z;
  
    ok -p $a eq <<END;
  <a>
    <b/>
    <Z>
      <c/>
      <d/>
    </Z>
    <e/>
  </a>
  END
  

=head4 wrapPrev($node, $tag, @context)

Wrap this B<$node> and the preceding sibling with a new node created from the specified B<$tag>. If the optional context is specified then it should match the first node, previous node and the context of the parent of the B<$node> as far as it is specified.  Return the new node created or B<undef> on failure.

     Parameter  Description
  1  $node      Node to wrap before
  2  $tag       Tag for new node
  3  @context   Context.

B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b/>
    <c/>
    <d/>
  </a>
  END
  
    my ($b, $c, $d) = $a->byList;
  
    $c->wrapPrev_e;
    ok  -p $a eq <<END;
  <a>
    <e>
      <b/>
      <c/>
    </e>
    <d/>
  </a>
  END
  

=head4 wrapPrevN($node, $N, $tag, @context)

Wrap this B<$node>and the previous B<$N>-1 nodes following this B<$node> in the optional B<@context> with the specified B<$tag> and return the new node or B<undef> if there are no such nodes to wrap.

     Parameter  Description
  1  $node      Node
  2  $N         Number of nodes to wrap
  3  $tag       Wrapping tag
  4  @context   Optional context

B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b/>
    <c/>
    <d/>
    <e/>
  </a>
  END
  
    $a->go_d__wrapPrevN_2_Z;
  
    ok -p $a eq <<END;
  <a>
    <b/>
    <Z>
      <c/>
      <d/>
    </Z>
    <e/>
  </a>
  END
  

=head4 wrapToLast($node, $tag, %attributes)

Wrap this B<$node> and any following siblings with a new node created from the specified B<$tag> and B<%attributes> and return the wrapping node.

     Parameter    Description
  1  $node        Node to wrap before
  2  $tag         Tag for new node
  3  %attributes  Attributes for new node.

B<Example:>


  if (1)                                                                          
   {my $a = Data::Edit::Xml::new(q(<a><b/><c/><d/></a>));
  
    ok -p $a eq <<END;
  <a>
    <b/>
    <c/>
    <d/>
  </a>
  END
  
    $a->go_c->wrapToLast_D;
    ok -p $a eq <<END;
  <a>
    <b/>
    <D>
      <c/>
      <d/>
    </D>
  </a>
  END
   }
  

=head4 wrapToLastOrFirstIn($node, $tag, @targets)

Wrap this B<$node> and any following siblings, up to and including the first sibling that matches one of the specified B<@find> nodes or all following siblings if no such match occurs, with a new node created from the specified B<$tag> and return the new wrapping node.

     Parameter  Description
  1  $node      Node at which to start to wrap
  2  $tag       Wrapping tag
  3  @targets   Tags at which to end the wrap.

B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a><b/><c/><d/><e/><f/></a>
  END
  
    $a->go_e__wrapToLastOrFirstIn_g_h;
    ok -p $a eq <<END;
  <a>
    <b/>
    <c/>
    <d/>
    <g>
      <e/>
      <f/>
    </g>
  </a>
  END
  
    $a->go_d__wrapToLastOrFirstIn_h_g_c;
    ok -p $a eq <<END;
  <a>
    <b/>
    <c/>
    <h>
      <d/>
      <g>
        <e/>
        <f/>
      </g>
    </h>
  </a>
  END
  
    $a->go_c__wrapToLastOrFirstIn_i_c;
    ok -p $a eq <<END;
  <a>
    <b/>
    <i>
      <c/>
      <h>
        <d/>
        <g>
          <e/>
          <f/>
        </g>
      </h>
    </i>
  </a>
  END
  

=head4 wrapLastN($node, $N, $tag, @context)

Wrap the last B<$N> nodes under this B<$node> in the optional B<@context> with the specified B<$tag> and return the new node or B<undef> if there are no such nodes to wrap.

     Parameter  Description
  1  $node      Node
  2  $N         Number of nodes to wrap
  3  $tag       Wrapping tag
  4  @context   Optional context

B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b/>
    <c/>
    <d/>
  </a>
  END
  
    $a->wrapLastN_2_Z;
  
    ok -p $a eq <<END;
  <a>
    <b/>
    <Z>
      <c/>
      <d/>
    </Z>
  </a>
  END
  

=head4 wrapTo($start, $end, $tag, %attributes)

Wrap all the nodes from the B<$start> node to the B<$end> node inclusive with a new node created from the specified B<$tag> and B<%attributes> and return the new node.

Return B<undef> if the B<$start> and B<$end> nodes are not siblings - they must have the same parent for this method to work.

     Parameter    Description
  1  $start       Start node
  2  $end         End node
  3  $tag         Tag for the wrapping node
  4  %attributes  Attributes for the wrapping node

B<Example:>


    my $x = Data::Edit::Xml::new(my $s = <<END);                                   
  <aa>
    <a>
      <b/>
        <c id="1"/><c id="2"/><c id="3"/><c id="4"/>
      <d/>
    </a>
  </aa>
  END
  
    $x->go(qw(a c))->𝘄𝗿𝗮𝗽𝗧𝗼($x->go(qw(a c -1)), qq(C), id=>1234);                 
  
    ok -p $x eq <<END;                                                            
  <aa>
    <a>
      <b/>
      <C id="1234">
        <c id="1"/>
        <c id="2"/>
        <c id="3"/>
        <c id="4"/>
      </C>
      <d/>
    </a>
  </aa>
  END
  
    my $C = $x->go(qw(a C));                                                      
  
    $C->𝘄𝗿𝗮𝗽𝗧𝗼($C, qq(D));                                                        
  
    ok -p $x eq <<END;                                                            
  <aa>
    <a>
      <b/>
      <D>
        <C id="1234">
          <c id="1"/>
          <c id="2"/>
          <c id="3"/>
          <c id="4"/>
        </C>
      </D>
      <d/>
    </a>
  </aa>
  END
  
    ok -p $a eq <<END;                                                            
  <a>
    <b>
      <D id="DD">
        <c id="0"/>
        <c id="1"/>
      </D>
      <E id="EE">
        <c id="2"/>
      </E>
      <F id="FF">
        <c id="3"/>
      </F>
    </b>
  </a>
  END
  

=head4 wrapFrom($end, $start, $tag, %attributes)

Wrap all the nodes from the B<$start> node to the B<$end> node with a new node created from the specified B<$tag> and B<%attributes> and return the new node.  Return B<undef> if the B<$start> and B<$end> nodes are not siblings - they must have the same parent for this method to work.

     Parameter    Description
  1  $end         End node
  2  $start       Start node
  3  $tag         Tag for the wrapping node
  4  %attributes  Attributes for the wrapping node

B<Example:>


   {my $a = Data::Edit::Xml::new(my $s = <<END);                                  
  <a>
    <b>
      <c id="0"/><c id="1"/><c id="2"/><c id="3"/>
    </b>
  </a>
  END
  
    my $b = $a->first;                                                            
  
    my @c = $b->contents;                                                         
  
    $c[1]->𝘄𝗿𝗮𝗽𝗙𝗿𝗼𝗺($c[0], qw(D id DD));                                          
  
    ok -p $a eq <<END;                                                            
  <a>
    <b>
      <D id="DD">
        <c id="0"/>
        <c id="1"/>
      </D>
      <c id="2"/>
      <c id="3"/>
    </b>
  </a>
  END
  

=head3 Unwrap

Unwrap nodes to reduce the depth of the L<parse|/parse> tree

=head4 unwrap($node, @context)

Unwrap the specified B<$node> if in the optional B<@context> by replacing the node with its contents. Returns the parent node on success, otherwise B<undef> if an attempt is made to unwrap a text node or the root node.

     Parameter  Description
  1  $node      Node to unwrap
  2  @context   Optional context.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


  if (1)                                                                          
   {my $x = Data::Edit::Xml::new(q(<a>A<b/>B</a>));
    my $b = $x->go_b;
    $b->putFirst($x->newText(' c '));
    ok -s $x eq q(<a>A<b> c </b>B</a>);
    $b->𝘂𝗻𝘄𝗿𝗮𝗽;
    ok -s $x eq q(<a>A c B</a>);
   }
  

B<u> is a synonym for L<unwrap|/unwrap>.


=head4 unwrapParentOfOnlyChild($node, @context)

Unwrap the parent of the specified B<$node> in the optional B<@context> when the B<$node> is the only child of its parent. Return the specified B<$node> regardless unless the node is not in the optional context in which case return B<undef>.

     Parameter  Description
  1  $node      Node
  2  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b>
      <c/>
    </b>
  </a>
  END
  
    $a->go_b_c->𝘂𝗻𝘄𝗿𝗮𝗽𝗣𝗮𝗿𝗲𝗻𝘁𝗢𝗳𝗢𝗻𝗹𝘆𝗖𝗵𝗶𝗹𝗱;
  
    ok -p $a eq <<END;
  <a>
    <c/>
  </a>
  END
  

=head4 unwrapOnlyChild($node, @context)

Unwrap the specified B<$node> in the optional B<@context> when the B<$node> is the only child of its parent. Return the specified B<$node> regardless unless the node is not in the optional context in which case return B<undef>.

     Parameter  Description
  1  $node      Node
  2  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b>
      <c/>
    </b>
    <d/>
  </a>
  END
  
    $a->go_b->𝘂𝗻𝘄𝗿𝗮𝗽𝗢𝗻𝗹𝘆𝗖𝗵𝗶𝗹𝗱;
  
    ok -p $a eq <<END;
  <a>
    <b>
      <c/>
    </b>
    <d/>
  </a>
  END
  
    $a->go_b_c->𝘂𝗻𝘄𝗿𝗮𝗽𝗢𝗻𝗹𝘆𝗖𝗵𝗶𝗹𝗱;
  
    ok -p $a eq <<END;
  <a>
    <b/>
    <d/>
  </a>
  END
  

=head4 unwrapParentsWithSingleChild($node, @context)

Unwrap any immediate ancestors of the specified B<$node> in the optional B<@context> which have only a single child and return the specified B<$node> regardless unless the node is not in the optional context in which case return B<undef>.

     Parameter  Description
  1  $node      Node
  2  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


   {my $a = Data::Edit::Xml::new(<<END);                                          
  <a>
    <b>
      <c>
        <d/>
      </c>
    </b>
    <e/>
  </a>
  END
  
    $a->go(qw(b c d))->𝘂𝗻𝘄𝗿𝗮𝗽𝗣𝗮𝗿𝗲𝗻𝘁𝘀𝗪𝗶𝘁𝗵𝗦𝗶𝗻𝗴𝗹𝗲𝗖𝗵𝗶𝗹𝗱;                              
  
    ok -p $a eq <<END;                                                            
  <a>
    <d/>
    <e/>
  </a>
  END
  

=head4 unwrapContentsKeepingText($node, @context)

Unwrap all the non text nodes below the specified B<$node> adding a leading and a trailing space to prevent unwrapped content from being elided and return the specified B<$node> else B<undef> if not in the optional B<@context>.

     Parameter  Description
  1  $node      Node to unwrap
  2  @context   Optional context.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


      ok -p $x eq <<END;                                                          
  <a>
    <b>
      <c>
        <d>DD</d>
  EE
        <f>FF</f>
      </c>
    </b>
  </a>
  END
  
      $x->go(qw(b))->𝘂𝗻𝘄𝗿𝗮𝗽𝗖𝗼𝗻𝘁𝗲𝗻𝘁𝘀𝗞𝗲𝗲𝗽𝗶𝗻𝗴𝗧𝗲𝘅𝘁;                                   
  
      ok -p $x eq <<END;                                                          
  <a>
    <b>  DD EE FF  </b>
  </a>
  END
  

=head4 wrapRuns($node, $wrap, @context)

Wrap consecutive runs of children under the specified parent B<$node> that are not already wrapped with B<$wrap>. Returns an array of any wrapping nodes created.  Returns () if the specified B<$node> is not in the optional B<@context>.

     Parameter  Description
  1  $node      Node to unwrap
  2  $wrap      Tag of wrapping node
  3  @context   Optional context.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    ok -p $a eq <<END;                                                             
  <a id="i1">
    <b id="i2"/>
    <c id="i3"/>
    <B id="i4">
      <c id="i5"/>
    </B>
    <c id="i6"/>
    <b id="i7"/>
  </a>
  END
  
    $a->𝘄𝗿𝗮𝗽𝗥𝘂𝗻𝘀(q(B));                                                           
  
    ok -p $a eq <<END;                                                            
  <a id="i1">
    <B>
      <b id="i2"/>
      <c id="i3"/>
    </B>
    <B id="i4">
      <c id="i5"/>
    </B>
    <B>
      <c id="i6"/>
      <b id="i7"/>
    </B>
  </a>
  END
  

=head1 Contents

The children of each node.

=head2 contents($node, @context)

Return a list of all the nodes contained by the specified B<$node> or an empty list if the node is empty or not in the optional B<@context>.

     Parameter  Description
  1  $node      Node
  2  @context   Optional context.

Use the B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>.  If a context is supplied and
B<$node> is not in this context then this method returns an empty list B<()>
immediately.



B<Example:>


   {my $x = Data::Edit::Xml::new(<<END);                                           
  <a>
    <b id="b1"><c id="1"/></b>
    <d id="d1"><c id="2"/></d>
    <e id="e1"><c id="3"/></e>
    <b id="b2"><c id="4"/></b>
    <d id="d2"><c id="5"/></d>
    <e id="e2"><c id="6"/></e>
  </a>
  END
  
    is_deeply [map{$_->id} $x->𝗰𝗼𝗻𝘁𝗲𝗻𝘁𝘀], [qw(b1 d1 e1 b2 d2 e2)];                 
  

=head2 contentAfter($node, @context)

Return a list of all the sibling nodes following the specified B<$node> or an empty list if the specified B<$node> is last or not in the optional B<@context>.

     Parameter  Description
  1  $node      Node
  2  @context   Optional context.

Use the B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>.  If a context is supplied and
B<$node> is not in this context then this method returns an empty list B<()>
immediately.



B<Example:>


   {my $x = Data::Edit::Xml::new(<<END);                                                       
  <a>
    <b>
      <c/><d/><e/><f/><g/>
    </b>
  </a>
  END
  
    ok 'f g' eq join ' ', map {$_->tag} $x->go(qw(b e))->𝗰𝗼𝗻𝘁𝗲𝗻𝘁𝗔𝗳𝘁𝗲𝗿;            
  

B<ca> is a synonym for L<contentAfter|/contentAfter>.


=head2 contentBefore($node, @context)

Return a list of all the sibling nodes preceding the specified B<$node> (in the normal sibling order) or an empty list if the specified B<$node> is last or not in the optional B<@context>.

     Parameter  Description
  1  $node      Node
  2  @context   Optional context.

Use the B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>.  If a context is supplied and
B<$node> is not in this context then this method returns an empty list B<()>
immediately.



B<Example:>


   {my $x = Data::Edit::Xml::new(<<END);                                                       
  <a>
    <b>
      <c/><d/><e/><f/><g/>
    </b>
  </a>
  END
  
    ok 'c d' eq join ' ', map {$_->tag} $x->go(qw(b e))->𝗰𝗼𝗻𝘁𝗲𝗻𝘁𝗕𝗲𝗳𝗼𝗿𝗲;           
  

B<cb> is a synonym for L<contentBefore|/contentBefore>.


=head2 contentAsTags($node, @context)

Return a string containing the tags of all the child nodes of the specified B<$node> separated by single spaces or the empty string if the node is empty or B<undef> if the node does not match the optional context. Use L<over|/over> to test the sequence of tags with a regular expression.

     Parameter  Description
  1  $node      Node
  2  @context   Optional context.

Use the B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>.  If a context is supplied and
B<$node> is not in this context then this method returns an empty list B<()>
immediately.



B<Example:>


   {my $x = Data::Edit::Xml::new(<<END);                                                       
  <a>
    <b>
      <c/><d/><e/><f/><g/>
    </b>
  </a>
  END
  
    ok $x->go(q(b))->𝗰𝗼𝗻𝘁𝗲𝗻𝘁𝗔𝘀𝗧𝗮𝗴𝘀 eq 'c d e f g';                                
  

=head2 contentAsTags2($node, @context)

Return a string containing the tags of all the child nodes of the specified B<$node> separated by two spaces with a single space preceding the first tag and a single space following the last tag or the empty string if the node is empty or B<undef> if the node does not match the optional context. Use L<over2|/over2> to test the sequence of tags with a regular expression. Use L<over2|/over2> to test the sequence of tags with a regular expression.

     Parameter  Description
  1  $node      Node
  2  @context   Optional context.

Use the B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>.  If a context is supplied and
B<$node> is not in this context then this method returns an empty list B<()>
immediately.



B<Example:>


    ok $x->go(q(b))->𝗰𝗼𝗻𝘁𝗲𝗻𝘁𝗔𝘀𝗧𝗮𝗴𝘀𝟮 eq q( c  d  e  f  g );                        
  

=head2 contentAfterAsTags($node, @context)

Return a string containing the tags of all the sibling nodes following the specified B<$node> separated by single spaces or the empty string if the node is empty or B<undef> if the node does not match the optional context. Use L<matchAfter|/matchAfter> to test the sequence of tags with a regular expression.

     Parameter  Description
  1  $node      Node
  2  @context   Optional context.

Use the B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>.  If a context is supplied and
B<$node> is not in this context then this method returns an empty list B<()>
immediately.



B<Example:>


   {my $x = Data::Edit::Xml::new(<<END);                                                       
  <a>
    <b>
      <c/><d/><e/><f/><g/>
    </b>
  </a>
  END
  
    ok 'f g' eq join ' ', map {$_->tag} $x->go(qw(b e))->contentAfter;            
  
    ok $x->go(qw(b e))->𝗰𝗼𝗻𝘁𝗲𝗻𝘁𝗔𝗳𝘁𝗲𝗿𝗔𝘀𝗧𝗮𝗴𝘀 eq 'f g';                              
  

=head2 contentAfterAsTags2($node, @context)

Return a string containing the tags of all the sibling nodes following the specified B<$node> separated by two spaces with a single space preceding the first tag and a single space following the last tag or the empty string if the node is empty or B<undef> if the node does not match the optional context. Use L<matchAfter2|/matchAfter2> to test the sequence of tags with a regular expression.

     Parameter  Description
  1  $node      Node
  2  @context   Optional context.

Use the B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>.  If a context is supplied and
B<$node> is not in this context then this method returns an empty list B<()>
immediately.



B<Example:>


   {my $x = Data::Edit::Xml::new(<<END);                                                       
  <a>
    <b>
      <c/><d/><e/><f/><g/>
    </b>
  </a>
  END
  
    ok $x->go(qw(b e))->𝗰𝗼𝗻𝘁𝗲𝗻𝘁𝗔𝗳𝘁𝗲𝗿𝗔𝘀𝗧𝗮𝗴𝘀𝟮 eq q( f  g );                         
  

=head2 contentBeforeAsTags($node, @context)

Return a string containing the tags of all the sibling nodes preceding the specified B<$node> separated by single spaces or the empty string if the node is empty or B<undef> if the node does not match the optional context. Use L<matchBefore|/matchBefore> to test the sequence of tags with a regular expression.

     Parameter  Description
  1  $node      Node
  2  @context   Optional context.

Use the B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>.  If a context is supplied and
B<$node> is not in this context then this method returns an empty list B<()>
immediately.



B<Example:>


   {my $x = Data::Edit::Xml::new(<<END);                                                       
  <a>
    <b>
      <c/><d/><e/><f/><g/>
    </b>
  </a>
  END
  
    ok 'c d' eq join ' ', map {$_->tag} $x->go(qw(b e))->contentBefore;           
  
    ok $x->go(qw(b e))->𝗰𝗼𝗻𝘁𝗲𝗻𝘁𝗕𝗲𝗳𝗼𝗿𝗲𝗔𝘀𝗧𝗮𝗴𝘀 eq 'c d';                             
  

=head2 contentBeforeAsTags2($node, @context)

Return a string containing the tags of all the sibling nodes preceding the specified B<$node> separated by two spaces with a single space preceding the first tag and a single space following the last tag or the empty string if the node is empty or B<undef> if the node does not match the optional context.  Use L<matchBefore2|/matchBefore2> to test the sequence of tags with a regular expression.

     Parameter  Description
  1  $node      Node
  2  @context   Optional context.

Use the B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>.  If a context is supplied and
B<$node> is not in this context then this method returns an empty list B<()>
immediately.



B<Example:>


   {my $x = Data::Edit::Xml::new(<<END);                                                       
  <a>
    <b>
      <c/><d/><e/><f/><g/>
    </b>
  </a>
  END
  
    ok $x->go(qw(b e))->𝗰𝗼𝗻𝘁𝗲𝗻𝘁𝗕𝗲𝗳𝗼𝗿𝗲𝗔𝘀𝗧𝗮𝗴𝘀𝟮 eq q( c  d );                        
  

=head2 position($node)

Return the index of the specified B<$node> in the content of the parent of the B<$node>.

     Parameter  Description
  1  $node      Node.

B<Example:>


    my $a = Data::Edit::Xml::new(<<END);                                                   
  <a         id="11">
    <b       id="12">
       <c    id="13"/>
       <d    id="14"/>
       <b    id="15">
          <c id="16"/>
          <d id="17"/>
          <e id="18"/>
          <f id="19"/>
          <g id="20"/>
       </b>
       <f    id="21"/>
       <g    id="22"/>
    </b>
    <b       id="23">
       <c    id="24"/>
       <d    id="25"/>
       <b    id="26">
          <c id="27"/>
          <d id="28"/>
          <e id="29"/>
          <f id="30"/>
          <g id="31"/>
       </b>
       <f    id="32"/>
       <g    id="33"/>
    </b>
  </a>
  END
  
    ok $a->go(qw(b 1 b))->id == 26;                                               
  
    ok $a->go(qw(b 1 b))->𝗽𝗼𝘀𝗶𝘁𝗶𝗼𝗻 == 2;                                          
  

=head2 index($node)

Return the index of the specified B<$node> in its parent index. Use L<position|/position> to find the position of a node under its parent.

     Parameter  Description
  1  $node      Node.

B<Example:>


    my $a = Data::Edit::Xml::new(<<END);                                                   
  <a         id="11">
    <b       id="12">
       <c    id="13"/>
       <d    id="14"/>
       <b    id="15">
          <c id="16"/>
          <d id="17"/>
          <e id="18"/>
          <f id="19"/>
          <g id="20"/>
       </b>
       <f    id="21"/>
       <g    id="22"/>
    </b>
    <b       id="23">
       <c    id="24"/>
       <d    id="25"/>
       <b    id="26">
          <c id="27"/>
          <d id="28"/>
          <e id="29"/>
          <f id="30"/>
          <g id="31"/>
       </b>
       <f    id="32"/>
       <g    id="33"/>
    </b>
  </a>
  END
  
    ok $a->go(qw(b 1))->id == 23;                                                 
  
    ok $a->go(qw(b 1))->𝗶𝗻𝗱𝗲𝘅 == 1;                                               
  

=head2 present($node, @names)

Return the count of the number of the specified tag types present immediately under a node or a hash {tag} = count for all the tags present under the node if no names are specified.

     Parameter  Description
  1  $node      Node
  2  @names     Possible tags immediately under the node.

B<Example:>


    is_deeply {$a->first->𝗽𝗿𝗲𝘀𝗲𝗻𝘁}, {c=>2, d=>2, e=>1};                           
  

=head2 editText($node, @context)

Return the text of a text B<$node> as an lvalue string reference that can be modified by a regular expression, else as a reference to a dummy string that will be ignored. For a unitary version of this method see: L<changeText>

     Parameter  Description
  1  $node      Node to test
  2  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a><b>bbbb</b><c/></a>
  END
  
    $a->by(sub{$_->𝗲𝗱𝗶𝘁𝗧𝗲𝘅𝘁 =~ s(bbbb) (BBBB)gs});
  
    ok -p $a eq <<END;
  <a>
    <b>BBBB</b>
    <c/>
  </a>
  END
  

=head2 isText($node, @context)

Return the specified B<$node> if the specified B<$node> is a text node, optionally in the specified context, else return B<undef>.

     Parameter  Description
  1  $node      Node to test
  2  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    ok $a->prettyStringCDATA eq <<END;                                              
  <a>
      <b><CDATA> </CDATA></b>
  </a>
  END
  
    ok $b->first->𝗶𝘀𝗧𝗲𝘅𝘁;                                                         
  
    ok $b->first->𝗶𝘀𝗧𝗲𝘅𝘁(qw(b a));                                                
  
    my $a = Data::Edit::Xml::new(<<END);                                             
  <a>abcdcba</a>
  END
  

=head2 isFirstText($node, @context)

Return the specified B<$node> if the specified B<$node> is a text node, the first node under its parent and that the parent is optionally in the specified context, else return B<undef>.

     Parameter  Description
  1  $node      Node to test
  2  @context   Optional context for parent

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


   {my $x = Data::Edit::Xml::new(<<END);                                           
  <x>
    <a>aaa
      <b>bbb</b>
      ccc
      <d>ddd</d>
      eee
    </a>
  </x>
  END
  
    my $a = $x->first;                                                            
  
    my ($ta, $b, $tc, $d, $te) = $a->contents;                                    
  
    ok $ta      ->𝗶𝘀𝗙𝗶𝗿𝘀𝘁𝗧𝗲𝘅𝘁(qw(a x));                                           
  
    ok $b->first->𝗶𝘀𝗙𝗶𝗿𝘀𝘁𝗧𝗲𝘅𝘁(qw(b a x));                                         
  
    ok $b->prev ->𝗶𝘀𝗙𝗶𝗿𝘀𝘁𝗧𝗲𝘅𝘁(qw(a x));                                           
  
    ok $d->last ->𝗶𝘀𝗙𝗶𝗿𝘀𝘁𝗧𝗲𝘅𝘁(qw(d a x));                                         
  

=head2 isLastText($node, @context)

Return the specified B<$node> if the specified B<$node> is a text node, the last node under its parent and that the parent is optionally in the specified context, else return B<undef>.

     Parameter  Description
  1  $node      Node to test
  2  @context   Optional context for parent

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


   {my $x = Data::Edit::Xml::new(<<END);                                           
  <x>
    <a>aaa
      <b>bbb</b>
      ccc
      <d>ddd</d>
      eee
    </a>
  </x>
  END
  
    ok $d->next ->𝗶𝘀𝗟𝗮𝘀𝘁𝗧𝗲𝘅𝘁 (qw(a x));                                           
  
    ok $d->last ->𝗶𝘀𝗟𝗮𝘀𝘁𝗧𝗲𝘅𝘁 (qw(d a x));                                         
  
    ok $te      ->𝗶𝘀𝗟𝗮𝘀𝘁𝗧𝗲𝘅𝘁 (qw(a x));                                           
  

=head2 matchTree($node, @match)

Return a list of nodes that match the specified tree of match expressions, else B<()> if one or more match expressions fail to match nodes in the tree below the specified start node. A match expression consists of [parent node tag, [match expressions to be matched by children of parent]|tags of child nodes to match starting at the first node]. Match expressions for a single item do need to be surrounded with [] and can be merged into their predecessor. The outermost match expression should not be enclosed in [].

     Parameter  Description
  1  $node      Node to start matching from
  2  @match     Tree of match expressions.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


  if (1)                                                                          
   {my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b>
      <c/>
      <d/>
    </b>
    <e>
      <f>
        <g/>
      </f>
    </e>
  </a>
  END
    my ($c, $d, $b, $g, $f, $e) = $a->byList;
  
    is_deeply [$b, $c, $d], [$b->𝗺𝗮𝘁𝗰𝗵𝗧𝗿𝗲𝗲(qw(b c d))];
    is_deeply [$e, $f, $g], [$e->𝗺𝗮𝘁𝗰𝗵𝗧𝗿𝗲𝗲(qr(\Ae\Z), [qw(f g)])];
    is_deeply [$c],         [$c->𝗺𝗮𝘁𝗰𝗵𝗧𝗿𝗲𝗲(qw(c))];
    is_deeply [$a, $b, $c, $d, $e, $f, $g],
              [$a->𝗺𝗮𝘁𝗰𝗵𝗧𝗿𝗲𝗲({a=>1}, [qw(b c d)], [qw(e), [qw(f g)]])];
   }
  

=head2 matchesText($node, $re, @context)

Returns an array of regular expression matches in the text of the specified B<$node> if it is text node and it matches the specified regular expression and optionally has the specified context otherwise returns an empty array.

     Parameter  Description
  1  $node      Node to test
  2  $re        Regular expression
  3  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $x = Data::Edit::Xml::new(<<END);                                          
  <a>
    <b>
      <c>CDECD</c>
    </b>
  </a>
  END
  
    my $c = $x->go(qw(b c))->first;                                               
  
    ok !$c->𝗺𝗮𝘁𝗰𝗵𝗲𝘀𝗧𝗲𝘅𝘁(qr(\AD));                                                 
  
    ok  $c->𝗺𝗮𝘁𝗰𝗵𝗲𝘀𝗧𝗲𝘅𝘁(qr(\AC), qw(c b a));                                      
  
    ok !$c->𝗺𝗮𝘁𝗰𝗵𝗲𝘀𝗧𝗲𝘅𝘁(qr(\AD), qw(c b a));                                      
  
    is_deeply [qw(E)], [$c->𝗺𝗮𝘁𝗰𝗵𝗲𝘀𝗧𝗲𝘅𝘁(qr(CD(.)CD))];                            
  

=head2 isBlankText($node, @context)

Return the specified B<$node> if the specified B<$node> is a text node, optionally in the specified context, and contains nothing other than white space else return B<undef>. See also: L<isAllBlankText|/isAllBlankText>

     Parameter  Description
  1  $node      Node to test
  2  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    ok $a->prettyStringCDATA eq <<END;                                              
  <a>
      <b><CDATA> </CDATA></b>
  </a>
  END
  
    ok $b->first->𝗶𝘀𝗕𝗹𝗮𝗻𝗸𝗧𝗲𝘅𝘁;                                                    
  

=head2 isAllBlankText($node, @context)

Return the specified B<$node> if the specified B<$node>, optionally in the specified context, does not contain anything or if it does contain something it is all white space else return B<undef>. See also: L<bitsNodeTextBlank|/bitsNodeTextBlank>

     Parameter  Description
  1  $node      Node to test
  2  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


   {my $a = Data::Edit::Xml::new(<<END);                                          
  <a>
    <b>
      <c>
        <z/>
      </c>
    </b>
    <d/>
  </a>
  END
  
    $a->by(sub{$_->replaceWithBlank(qw(z))});                                     
  
    my ($b, $c, $d) = $a->firstBy(qw(b c d));                                     
  
    ok  $c->𝗶𝘀𝗔𝗹𝗹𝗕𝗹𝗮𝗻𝗸𝗧𝗲𝘅𝘁;                                                       
  
    ok  $c->𝗶𝘀𝗔𝗹𝗹𝗕𝗹𝗮𝗻𝗸𝗧𝗲𝘅𝘁(qw(c b a));                                            
  
    ok !$c->𝗶𝘀𝗔𝗹𝗹𝗕𝗹𝗮𝗻𝗸𝗧𝗲𝘅𝘁(qw(c a));                                              
  

=head2 isOnlyChildBlankText($node, @context)

Return the specified B<$node> if it is a blank text node and an only child else return B<undef>.

     Parameter  Description
  1  $node      Node to test
  2  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


  if (1)                                                                          
   {my $a = Data::Edit::Xml::new(q(<a>aaaa</a>));
    $a->first->text = q( );
    ok  $a->prettyStringCDATA eq qq(<a><CDATA> </CDATA></a>
);
    ok  $a->first->𝗶𝘀𝗢𝗻𝗹𝘆𝗖𝗵𝗶𝗹𝗱𝗕𝗹𝗮𝗻𝗸𝗧𝗲𝘅𝘁;
    ok !$a->𝗶𝘀𝗢𝗻𝗹𝘆𝗖𝗵𝗶𝗹𝗱𝗕𝗹𝗮𝗻𝗸𝗧𝗲𝘅𝘁;
   }
  
  if (1)                                                                          
   {my $a = Data::Edit::Xml::new(q(<a/>));
    my $b = $a->new(q(<b/>));
    ok -p $a eq qq(<a/>
);
    ok -p $b eq qq(<b/>
);
   }
  

=head2 bitsNodeTextBlank($node)

Return a bit string that shows if there are any non text nodes, text nodes or blank text nodes under a node. An empty string is returned if there are no child nodes.

     Parameter  Description
  1  $node      Node to test.

B<Example:>


    ok $x->prettyStringCDATA eq <<END;                                            
  <a>
      <b>
          <C/>
      </b>
      <c>
          <D/>
  <CDATA>
       E
      </CDATA>
      </c>
      <d>
          <F/>
  <CDATA> </CDATA>
          <H/>
      </d>
      <e/>
  </a>
  END
  
    ok '100' eq -B $x;                                                            
  
    ok '100' eq -B $x->go(q(b));                                                  
  
    ok '110' eq -B $x->go(q(c));                                                  
  
    ok '111' eq -B $x->go(q(d));                                                  
  
    ok !-B $x->go(qw(e));                                                         
  

=head1 Number

Number the nodes of a parse tree so that they can be easily retrieved by number - either by a person reading the source L<Xml|https://en.wikipedia.org/wiki/XML> or programmatically.

=head2 findByNumber($node, $number)

Find the node with the specified number as made visible by L<prettyStringNumbered|/prettyStringNumbered> in the L<parse|/parse> tree containing the specified B<$node> and return the found node or B<undef> if no such node exists.

     Parameter  Description
  1  $node      Node in the parse tree to search
  2  $number    Number of the node required.

B<Example:>


  if (1)                                                                            
   {my $a = Data::Edit::Xml::new(<<END);
  <a><b><c/></b><d><e/></d></a>
  END
  
    $a->numberTree;
    ok -z $a eq <<END;
  <a id="1">
    <b id="2">
      <c id="3"/>
    </b>
    <d id="4">
      <e id="5"/>
    </d>
  </a>
  END
  
    ok -t $a->findByNumber_4 eq q(d);
    ok    $a->findByNumber_3__up__number == 2;
   }
  
    $a->numberTree;                                                                  
  
    ok $a->prettyStringNumbered eq <<END;                                             
  <a id="1">
    <b id="2">
      <A id="3"/>
      <B id="4"/>
    </b>
    <c id="5">
      <C id="6"/>
      <D id="7"/>
    </c>
  </a>
  END
  
    ok q(D) eq -t $a->𝗳𝗶𝗻𝗱𝗕𝘆𝗡𝘂𝗺𝗯𝗲𝗿(7);                                            
  

=head2 findByNumbers($node, @numbers)

Find the nodes with the specified numbers as made visible by L<prettyStringNumbered|/prettyStringNumbered> in the L<parse|/parse> tree containing the specified B<$node> and return the found nodes in a list with B<undef> for nodes that do not exist.

     Parameter  Description
  1  $node      Node in the parse tree to search
  2  @numbers   Numbers of the nodes required.

B<Example:>


    $a->numberTree;                                                                  
  
    ok $a->prettyStringNumbered eq <<END;                                             
  <a id="1">
    <b id="2">
      <A id="3"/>
      <B id="4"/>
    </b>
    <c id="5">
      <C id="6"/>
      <D id="7"/>
    </c>
  </a>
  END
  
    is_deeply [map {-t $_} $a->𝗳𝗶𝗻𝗱𝗕𝘆𝗡𝘂𝗺𝗯𝗲𝗿𝘀(1..3)], [qw(a b A)];                 
  

=head2 numberTree($node)

Number the nodes in a L<parse|/parse> tree in pre-order so they are numbered in the same sequence that they appear in the source. You can see the numbers by printing the tree with L<prettyStringNumbered|/prettyStringNumbered>.  Nodes can be found using L<findByNumber|/findByNumber>.  This method differs from L<forestNumberTrees|/forestNumberTrees> in that avoids overwriting the B<id=> attribute of each node by using a system attribute instead; this system attribute can then be made visible on the id attribute of each node by printing the parse tree with L<prettyStringNumbered|/prettyStringNumbered>.

     Parameter  Description
  1  $node      Node

B<Example:>


    $a->𝗻𝘂𝗺𝗯𝗲𝗿𝗧𝗿𝗲𝗲;                                                                
  
    ok -z $a eq <<END;                                                             
  <a id="1">
    <b id="2">
      <c id="42" match="mm"/>
    </b>
    <d id="4">
      <e id="5"/>
    </d>
  </a>
  END
  
  if (1)                                                                            
   {my $a = Data::Edit::Xml::new(<<END);
  <a><b><c/></b><d><e/></d></a>
  END
  
    $a->𝗻𝘂𝗺𝗯𝗲𝗿𝗧𝗿𝗲𝗲;
    ok -z $a eq <<END;
  <a id="1">
    <b id="2">
      <c id="3"/>
    </b>
    <d id="4">
      <e id="5"/>
    </d>
  </a>
  END
  
    ok -t $a->findByNumber_4 eq q(d);
    ok    $a->findByNumber_3__up__number == 2;
   }
  

=head2 indexIds($node)

Return a map of the ids at and below the specified B<$node>.

     Parameter  Description
  1  $node      Node

B<Example:>


   {my $a = Data::Edit::Xml::new(<<END);                                          
  <a id="A">
    <b id="B">
      <c id="C"/>
      <d id="D">
        <e id="E"/>
        <f id="F"/>
      </d>
    </b>
  </a>
  END
  
    my $i = $a->𝗶𝗻𝗱𝗲𝘅𝗜𝗱𝘀;                                                         
  
    ok $i->{C}->tag eq q(c);                                                      
  
    ok $i->{E}->tag eq q(e);                                                      
  

=head2 numberTreesJustIds($node, $prefix)

Number the ids of the nodes in a L<parse|/parse> tree in pre-order so they are numbered in the same sequence that they appear in the source. You can see the numbers by printing the tree with L<prettyStringNumbered()|/prettyStringNumbered>. This method differs from L<numberTree|/numberTree> in that only non text nodes without ids are numbered. The number applied to each node consists of the concatenation of the specified prefix, an underscore and a number that is unique within the specifed L<parse|/parse> tree. Consequently the ids across several trees trees can be made unique by supplying different prefixes for each tree.  Nodes can be found using L<findByNumber|/findByNumber>.  Returns the specified B<$node>.

     Parameter  Description
  1  $node      Node
  2  $prefix    Prefix for each id at and under the specified B<$node>

B<Example:>


   {my $a = Data::Edit::Xml::new(<<END);                                          
  <a>A
    <b id="bb">B
      <c/>
      <d>D
        <e id="ee"/>
          E
        <f/>
          F
      </d>
      G
    </b>
    H
  </a>
  END
  
    $a->𝗻𝘂𝗺𝗯𝗲𝗿𝗧𝗿𝗲𝗲𝘀𝗝𝘂𝘀𝘁𝗜𝗱𝘀(q(T));                                                 
  
    my $A = Data::Edit::Xml::new(<<END);                                          
  <a id="T1">A
    <b id="bb">B
      <c id="T2"/>
      <d id="T3">D
        <e id="ee"/>
          E
        <f id="T4"/>
          F
      </d>
      G
    </b>
    H
  </a>
  END
  
    ok -p $a eq -p $A;                                                            
  

=head1 Forest Numbers

Number the nodes of several parse trees so that they can be easily retrieved by forest number - either by a person reading the source L<Xml|https://en.wikipedia.org/wiki/XML> or programmatically.

=head2 forestNumberTrees($node, $prefix)

Number the ids of the nodes in a L<parse|/parse> tree in pre-order so they are numbered in the same sequence that they appear in the source. You can see the numbers by printing the tree with L<prettyString|/prettyString>. This method differs from L<numberTree|/numberTree> in that only non text nodes are numbered and nodes with existing B<id=> attributes have the value of their B<id=> attribute transferred to a L<label|/Labels>. The number applied to each node consists of the concatenation of the specified tree number, an underscore and a number that is unique within the specified L<parse|/parse> tree. Consequently the ids across several trees can be made unique by supplying a different tree number for each tree.  Nodes can be found subsequently using L<findByForestNumber|/findByForestNumber>.  Returns the specified B<$node>.

     Parameter  Description
  1  $node      Node in parse tree to be numbered
  2  $prefix    Tree number

B<Example:>


   {my $a = Data::Edit::Xml::new(<<END);                                          
  <a>
    <b id="b">
      <c/>
    </b>
    <b id="B">
      <d/>
      <e/>
    </b>
  </a>
  END
  
    my $e = $a->go(qw(b -1 e));                                                   
  
    $e->𝗳𝗼𝗿𝗲𝘀𝘁𝗡𝘂𝗺𝗯𝗲𝗿𝗧𝗿𝗲𝗲𝘀(1);                                                     
  
    ok -p $a eq <<END;                                                             
  <a id="1_1">
    <b id="1_2">
      <c id="1_3"/>
    </b>
    <b id="1_4">
      <d id="1_5"/>
      <e id="1_6"/>
    </b>
  </a>
  END
  

=head2 findByForestNumber($node, $tree, $id)

Find the node with the specified L<forest number|/forestNumberTrees> as made visible on the id attribute by L<prettyStringNumbered|/prettyStringNumbered> in the L<parse|/parse> tree containing the specified B<$node> and return the found node or B<undef> if no such node exists.

     Parameter  Description
  1  $node      Node in the parse tree to search
  2  $tree      Forest number
  3  $id        Id number of the node required.

B<Example:>


    ok -p $a eq <<END;                                                             
  <a id="1_1">
    <b id="1_2">
      <c id="1_3"/>
    </b>
    <b id="1_4">
      <d id="1_5"/>
      <e id="1_6"/>
    </b>
  </a>
  END
  
    my $B = $e->𝗳𝗶𝗻𝗱𝗕𝘆𝗙𝗼𝗿𝗲𝘀𝘁𝗡𝘂𝗺𝗯𝗲𝗿(1, 4);                                         
  
    is_deeply [$B->getLabels], ["B"];                                             
  

=head1 Order

Check the order and relative position of nodes in a parse tree.

=head2 above($first, $second, @context)

Return the first node if the first node is above the second node optionally checking that the first node is in the specified context otherwise return B<undef>

     Parameter  Description
  1  $first     First node
  2  $second    Second node
  3  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $x = Data::Edit::Xml::new(<<END);                                                  
  <a       id='a1'>
    <b     id='b1'>
      <c   id='c1'/>
      <c   id='c2'/>
      <d   id='d1'>
        <e id='e1'/>
      </d>
      <c   id='c3'/>
      <c   id='c4'/>
      <d   id='d2'>
        <e id='e2'/>
      </d>
      <c   id='c5'/>
      <c   id='c6'/>
    </b>
  </a>
  END
  
    ok $b->id eq 'b1';                                                             
  
    ok $e->id eq "e1";                                                                    
  
    ok $E->id eq "e2";                                                                 
  
    ok  $b->𝗮𝗯𝗼𝘃𝗲($e);                                                            
  
    ok !$E->𝗮𝗯𝗼𝘃𝗲($e);                                                            
  

=head2 abovePath($first, $second)

Return the nodes along the path from the first node down to the second node when the first node is above the second node else return B<()>.

     Parameter  Description
  1  $first     First node
  2  $second    Second node

B<Example:>


    my $x = Data::Edit::Xml::new(<<END);                                                  
  <a       id='a1'>
    <b     id='b1'>
      <c   id='c1'/>
      <c   id='c2'/>
      <d   id='d1'>
        <e id='e1'/>
      </d>
      <c   id='c3'/>
      <c   id='c4'/>
      <d   id='d2'>
        <e id='e2'/>
      </d>
      <c   id='c5'/>
      <c   id='c6'/>
    </b>
  </a>
  END
  
    my ($a, $b, $c, $d, $e) = $x->firstDown(@tags);                                
  
    is_deeply [$b, $d, $e], [$b->𝗮𝗯𝗼𝘃𝗲𝗣𝗮𝘁𝗵($e)];                                  
  
    is_deeply [],   [$c->𝗮𝗯𝗼𝘃𝗲𝗣𝗮𝘁𝗵($d)];                                          
  

=head2 below($first, $second, @context)

Return the first node if the first node is below the second node optionally checking that the first node is in the specified context otherwise return B<undef>

     Parameter  Description
  1  $first     First node
  2  $second    Second node
  3  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $x = Data::Edit::Xml::new(<<END);                                                  
  <a       id='a1'>
    <b     id='b1'>
      <c   id='c1'/>
      <c   id='c2'/>
      <d   id='d1'>
        <e id='e1'/>
      </d>
      <c   id='c3'/>
      <c   id='c4'/>
      <d   id='d2'>
        <e id='e2'/>
      </d>
      <c   id='c5'/>
      <c   id='c6'/>
    </b>
  </a>
  END
  
    ok $d->id eq 'd1';                                                             
  
    ok $e->id eq "e1";                                                                    
  
    ok !$d->𝗯𝗲𝗹𝗼𝘄($e);                                                            
  

=head2 belowPath($first, $second)

Return the nodes along the path from the first node up to the second node when the first node is below the second node else return B<()>.

     Parameter  Description
  1  $first     First node
  2  $second    Second node

B<Example:>


    my $x = Data::Edit::Xml::new(<<END);                                                  
  <a       id='a1'>
    <b     id='b1'>
      <c   id='c1'/>
      <c   id='c2'/>
      <d   id='d1'>
        <e id='e1'/>
      </d>
      <c   id='c3'/>
      <c   id='c4'/>
      <d   id='d2'>
        <e id='e2'/>
      </d>
      <c   id='c5'/>
      <c   id='c6'/>
    </b>
  </a>
  END
  
    my ($a, $b, $c, $d, $e) = $x->firstDown(@tags);                                
  
    is_deeply [$e, $d, $b], [$e->𝗯𝗲𝗹𝗼𝘄𝗣𝗮𝘁𝗵($b)];                                  
  
    is_deeply [$c], [$c->𝗯𝗲𝗹𝗼𝘄𝗣𝗮𝘁𝗵($c)];                                          
  

=head2 after($first, $second, @context)

Return the first node if it occurs after the second node in the L<parse|/parse> tree optionally checking that the first node is in the specified context or else B<undef> if the node is L<above|/above>, L<below|/below> or L<before|/before> the target.

     Parameter  Description
  1  $first     First node
  2  $second    Second node
  3  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $x = Data::Edit::Xml::new(<<END);                                                  
  <a       id='a1'>
    <b     id='b1'>
      <c   id='c1'/>
      <c   id='c2'/>
      <d   id='d1'>
        <e id='e1'/>
      </d>
      <c   id='c3'/>
      <c   id='c4'/>
      <d   id='d2'>
        <e id='e2'/>
      </d>
      <c   id='c5'/>
      <c   id='c6'/>
    </b>
  </a>
  END
  
    ok $c->id eq 'c1';                                                             
  
    ok $e->id eq "e1";                                                                    
  
    ok $e->𝗮𝗳𝘁𝗲𝗿($c);                                                             
  

=head2 before($first, $second, @context)

Return the first node if it occurs before the second node in the L<parse|/parse> tree optionally checking that the first node is in the specified context or else B<undef> if the node is L<above|/above>, L<below|/below> or L<before|/before> the target.

     Parameter  Description
  1  $first     First node
  2  $second    Second node
  3  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $x = Data::Edit::Xml::new(<<END);                                                  
  <a       id='a1'>
    <b     id='b1'>
      <c   id='c1'/>
      <c   id='c2'/>
      <d   id='d1'>
        <e id='e1'/>
      </d>
      <c   id='c3'/>
      <c   id='c4'/>
      <d   id='d2'>
        <e id='e2'/>
      </d>
      <c   id='c5'/>
      <c   id='c6'/>
    </b>
  </a>
  END
  
    ok $e->id eq "e1";                                                                    
  
    ok $E->id eq "e2";                                                                 
  
    ok $e->𝗯𝗲𝗳𝗼𝗿𝗲($E);                                                            
  

=head2 disordered($node, @nodes)

Return the first node that is out of the specified order when performing a pre-ordered traversal of the L<parse|/parse> tree.

     Parameter  Description
  1  $node      Node
  2  @nodes     Following nodes.

B<Example:>


    my $x = Data::Edit::Xml::new(<<END);                                                  
  <a       id='a1'>
    <b     id='b1'>
      <c   id='c1'/>
      <c   id='c2'/>
      <d   id='d1'>
        <e id='e1'/>
      </d>
      <c   id='c3'/>
      <c   id='c4'/>
      <d   id='d2'>
        <e id='e2'/>
      </d>
      <c   id='c5'/>
      <c   id='c6'/>
    </b>
  </a>
  END
  
    ok $b->id eq 'b1';                                                             
  
    ok $c->id eq 'c1';                                                             
  
    ok $d->id eq 'd1';                                                             
  
    ok $e->id eq "e1";                                                                    
  
    ok  $e->𝗱𝗶𝘀𝗼𝗿𝗱𝗲𝗿𝗲𝗱($c        )->id eq "c1";                                   
  
    ok  $b->𝗱𝗶𝘀𝗼𝗿𝗱𝗲𝗿𝗲𝗱($c, $e, $d)->id eq "d1";                                   
  
    ok !$c->𝗱𝗶𝘀𝗼𝗿𝗱𝗲𝗿𝗲𝗱($e);                                                       
  

=head2 commonAncestor($node, @nodes)

Find the most recent common ancestor of the specified nodes or B<undef> if there is no common ancestor.

     Parameter  Description
  1  $node      Node
  2  @nodes     @nodes

B<Example:>


    ok -z $a eq <<END;                                                                   
  <a id="1">
    <b id="2">
      <c id="3">
        <e id="4"/>
      </c>
      <d id="5">
        <e id="6"/>
      </d>
      <c id="7">
        <d id="8">
          <e id="9"/>
        </d>
      </c>
      <d id="10">
        <e id="11"/>
      </d>
      <c id="12">
        <d id="13">
          <e id="14"/>
        </d>
      </c>
    </b>
  </a>
  END
  
     {my ($b, $e, @n) = $a->findByNumbers(2, 4, 6, 9);                            
  
      ok $e == $e->𝗰𝗼𝗺𝗺𝗼𝗻𝗔𝗻𝗰𝗲𝘀𝘁𝗼𝗿;                                                
  
      ok $e == $e->𝗰𝗼𝗺𝗺𝗼𝗻𝗔𝗻𝗰𝗲𝘀𝘁𝗼𝗿($e);                                            
  
      ok $b == $e->𝗰𝗼𝗺𝗺𝗼𝗻𝗔𝗻𝗰𝗲𝘀𝘁𝗼𝗿($b);                                            
  
      ok $b == $e->𝗰𝗼𝗺𝗺𝗼𝗻𝗔𝗻𝗰𝗲𝘀𝘁𝗼𝗿(@n);                                            
  

=head2 commonAdjacentAncestors($first, $second)

Given two nodes, find a pair of adjacent ancestral siblings if such a pair exists else return B<()>.

     Parameter  Description
  1  $first     First node
  2  $second    Second node

B<Example:>


   {my $a = Data::Edit::Xml::new(<<END);                                                       
  <a>
    <b>
      <c>
        <d/>
      </c>
    </b>
    <b>
      <c/>
    </b>
    <e>
      <f/>
    </e>
  </a>
  END
  
    my ($d, $c, $b, $C, $B, $f, $e) = $a->byList;                                              
  
    is_deeply [$d->𝗰𝗼𝗺𝗺𝗼𝗻𝗔𝗱𝗷𝗮𝗰𝗲𝗻𝘁𝗔𝗻𝗰𝗲𝘀𝘁𝗼𝗿𝘀($C)], [$b, $B];                                                       
  

=head2 ordered($node, @nodes)

Return the first node if the specified nodes are all in order when performing a pre-ordered traversal of the L<parse|/parse> tree else return B<undef>.

     Parameter  Description
  1  $node      Node
  2  @nodes     Following nodes.

B<Example:>


    my $x = Data::Edit::Xml::new(<<END);                                                  
  <a       id='a1'>
    <b     id='b1'>
      <c   id='c1'/>
      <c   id='c2'/>
      <d   id='d1'>
        <e id='e1'/>
      </d>
      <c   id='c3'/>
      <c   id='c4'/>
      <d   id='d2'>
        <e id='e2'/>
      </d>
      <c   id='c5'/>
      <c   id='c6'/>
    </b>
  </a>
  END
  
    ok $e->id eq "e1";                                                                    
  
    ok $E->id eq "e2";                                                                 
  
    ok  $e->𝗼𝗿𝗱𝗲𝗿𝗲𝗱($E);                                                          
  
    ok !$E->𝗼𝗿𝗱𝗲𝗿𝗲𝗱($e);                                                          
  
    ok  $e->𝗼𝗿𝗱𝗲𝗿𝗲𝗱($e);                                                          
  
    ok  $e->𝗼𝗿𝗱𝗲𝗿𝗲𝗱;                                                              
  

=head1 Patching

Analyze two similar L<parse|/parse> trees and create a patch that transforms the first L<parse|/parse> tree into the second as long as each tree has the same tag and id structure with each id being unique.

=head2 createPatch($a, $A)

Create a patch that moves the source L<parse|/parse> tree to the target L<parse|/parse> tree node as long as they have the same tag and id structure with each id being unique.

     Parameter  Description
  1  $a         Source parse tree
  2  $A         Target parse tree

B<Example:>


   {my $a = Data::Edit::Xml::new(<<END);                                           
  <a>Aaaaa
    <b b1="b1" b2="b2">Bbbbb
      <c c1="c1" />Ccccc
      <d d1="d1" >Ddddd
        <e  e1="e1" />
          Eeeee
        <f  f1="f1" />
          Fffff
      </d>
      Ggggg
    </b>
    Hhhhhh
  </a>
  END
  
    my $A = Data::Edit::Xml::new(<<END);                                           
  <a>AaaAaaA
    <b b1="b1" b3="B3">BbbBbbB
      <c c1="C1" />Ccccc
      <d d2="D2" >DddDddD
        <e  e3="E3" />
          EeeEeeE
        <f  f1="F1" />
          FffFffF
      </d>
      GggGggG
    </b>
    Hhhhhh
  </a>
  END
  
    $a->numberTreesJustIds(q(a));                                                  
  
    $A->numberTreesJustIds(q(a));                                                  
  
    my $patches = $a->𝗰𝗿𝗲𝗮𝘁𝗲𝗣𝗮𝘁𝗰𝗵($A);                                             
  
    $patches->install($a);                                                         
  
    ok !$a->diff  ($A);                                                            
  
    ok  $a->equals($A);                                                            
  

=head2 Data::Edit::Xml::Patch::install($patches, $a)

Replay a patch created by L<createPatch|/createPatch> against a L<parse|/parse> tree that has the same tag and id structure with each id being unique.

     Parameter  Description
  1  $patches   Patch
  2  $a         Parse tree

B<Example:>


   {my $a = Data::Edit::Xml::new(<<END);                                           
  <a>Aaaaa
    <b b1="b1" b2="b2">Bbbbb
      <c c1="c1" />Ccccc
      <d d1="d1" >Ddddd
        <e  e1="e1" />
          Eeeee
        <f  f1="f1" />
          Fffff
      </d>
      Ggggg
    </b>
    Hhhhhh
  </a>
  END
  
    my $A = Data::Edit::Xml::new(<<END);                                           
  <a>AaaAaaA
    <b b1="b1" b3="B3">BbbBbbB
      <c c1="C1" />Ccccc
      <d d2="D2" >DddDddD
        <e  e3="E3" />
          EeeEeeE
        <f  f1="F1" />
          FffFffF
      </d>
      GggGggG
    </b>
    Hhhhhh
  </a>
  END
  
    $a->numberTreesJustIds(q(a));                                                  
  
    $A->numberTreesJustIds(q(a));                                                  
  
    my $patches = $a->createPatch($A);                                             
  
    $patches->install($a);                                                         
  
    ok !$a->diff  ($A);                                                            
  
    ok  $a->equals($A);                                                            
  

=head1 Propogating

Propagate parent node attributes through a parse tree.

=head2 propagate($tree, $tag, @context)

Propagate L<new attributes|/copyNewAttrs> from nodes that match the specified tag to all their child nodes, then L<unwrap|/unwrap> all the nodes that match the specified tag. Return the specified parse tree.

     Parameter  Description
  1  $tree      Parse tree
  2  $tag       Tag of nodes whose attributes are to be propagated
  3  @context   Optional context for parse tree

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


   {my $a = Data::Edit::Xml::new(<<END);                                          
  <a>
    <b b="B">
      <b c="C">
        <c/>
        <b d="D">
          <d/>
          <b e="E">
            <e/>
          </b>
        </b>
      </b>
    </b>
  </a>
  END
  
    $a->𝗽𝗿𝗼𝗽𝗮𝗴𝗮𝘁𝗲(q(b));                                                          
  
    ok -p $a eq <<END;                                                            
  <a>
    <c b="B" c="C"/>
    <d b="B" c="C" d="D"/>
    <e b="B" c="C" d="D" e="E"/>
  </a>
  END
  

=head1 Table of Contents

Analyze and generate tables of contents.

=head2 tocNumbers($node, @match)

Table of Contents number the nodes in a L<parse|/parse> tree.

     Parameter  Description
  1  $node      Node
  2  @match     Optional list of tags to descend into else all tags will be descended into

B<Example:>


    ok $a->prettyStringNumbered eq <<END;                                             
  <a id="1">
    <b id="2">
      <A id="3"/>
      <B id="4"/>
    </b>
    <c id="5">
      <C id="6"/>
      <D id="7"/>
    </c>
  </a>
  END
  
      my $t = $a->𝘁𝗼𝗰𝗡𝘂𝗺𝗯𝗲𝗿𝘀();                                                   
  
      is_deeply {map {$_=>$t->{$_}->tag} keys %$t},                               
  
       {"1"  =>"b",                                                               
  
        "1 1"=>"A",                                                               
  
        "1 2"=>"B",                                                               
  
        "2"  =>"c",                                                               
  
        "2 1"=> "C",                                                              
  
        "2 2"=>"D"                                                                
  
       }                                                                          
  

=head1 Labels

Label nodes so that they can be cross referenced and linked by L<Data::Edit::Xml::Lint>

=head2 addLabels($node, @labels)

Add the named labels to the specified B<$node> and return the number of labels added. Labels that are not L<defined|https://perldoc.perl.org/functions/defined.html> will be ignored.

     Parameter  Description
  1  $node      Node in parse tree
  2  @labels    Names of labels to add.

B<Example:>


    ok $x->stringReplacingIdsWithLabels eq '<a><b><c/></b></a>';                                                
  
    my $b = $x->go(q(b));                                                            
  
    ok $b->countLabels == 0;                                                        
  
    $b->𝗮𝗱𝗱𝗟𝗮𝗯𝗲𝗹𝘀(1..2);                                                             
  
    $b->𝗮𝗱𝗱𝗟𝗮𝗯𝗲𝗹𝘀(3..4);                                                             
  
    ok $x->stringReplacingIdsWithLabels eq '<a><b id="1, 2, 3, 4"><c/></b></a>';                                 
  

=head2 countLabels($node)

Return the count of the number of labels at a node.

     Parameter  Description
  1  $node      Node in parse tree.

B<Example:>


    ok $x->stringReplacingIdsWithLabels eq '<a><b><c/></b></a>';                                                
  
    my $b = $x->go(q(b));                                                            
  
    ok $b->𝗰𝗼𝘂𝗻𝘁𝗟𝗮𝗯𝗲𝗹𝘀 == 0;                                                        
  
    $b->addLabels(1..2);                                                             
  
    $b->addLabels(3..4);                                                             
  
    ok $x->stringReplacingIdsWithLabels eq '<a><b id="1, 2, 3, 4"><c/></b></a>';                                 
  
    ok $b->𝗰𝗼𝘂𝗻𝘁𝗟𝗮𝗯𝗲𝗹𝘀 == 4;                                                      
  

=head2 labelsInTree($tree)

Return a hash of all the labels in a tree

     Parameter  Description
  1  $tree      Parse tree.

B<Example:>


    ok -p (new $A->stringExtendingIdsWithLabels) eq <<END;                         
  <a id="aa, a, a5">
    <b id="bb, b, b2">
      <c id="cc, c, c1"/>
    </b>
    <b id="B, b4">
      <c id="C, c3"/>
    </b>
  </a>
  END
  
    is_deeply [sort keys %{$A->𝗹𝗮𝗯𝗲𝗹𝘀𝗜𝗻𝗧𝗿𝗲𝗲}],                                    
  
      ["B", "C", "a", "a5", "b", "b2", "b4", "c", "c1", "c3"];                    
  

=head2 getLabels($node)

Return the names of all the labels set on a node.

     Parameter  Description
  1  $node      Node in parse tree.

B<Example:>


    ok $x->stringReplacingIdsWithLabels eq '<a><b><c/></b></a>';                                                
  
    my $b = $x->go(q(b));                                                            
  
    ok $b->countLabels == 0;                                                        
  
    $b->addLabels(1..2);                                                             
  
    $b->addLabels(3..4);                                                             
  
    ok $x->stringReplacingIdsWithLabels eq '<a><b id="1, 2, 3, 4"><c/></b></a>';                                 
  
    is_deeply [1..4], [$b->𝗴𝗲𝘁𝗟𝗮𝗯𝗲𝗹𝘀];                                            
  

=head2 deleteLabels($node, @labels)

Delete the specified labels in the specified B<$node> or all labels if no labels have are specified and return that node.

     Parameter  Description
  1  $node      Node in parse tree
  2  @labels    Names of the labels to be deleted

B<Example:>


    ok $x->stringReplacingIdsWithLabels eq '<a><b id="1, 2, 3, 4"><c id="1, 2, 3, 4"/></b></a>';              
  
    $b->𝗱𝗲𝗹𝗲𝘁𝗲𝗟𝗮𝗯𝗲𝗹𝘀(1,4) for 1..2;                                               
  
    ok $x->stringReplacingIdsWithLabels eq '<a><b id="2, 3"><c id="1, 2, 3, 4"/></b></a>';                    
  

=head2 copyLabels($source, $target)

Copy all the labels from the source node to the target node and return the source node.

     Parameter  Description
  1  $source    Source node
  2  $target    Target node.

B<Example:>


    ok $x->stringReplacingIdsWithLabels eq '<a><b id="1, 2, 3, 4"><c/></b></a>';                                 
  
    $b->𝗰𝗼𝗽𝘆𝗟𝗮𝗯𝗲𝗹𝘀($c) for 1..2;                                                  
  
    ok $x->stringReplacingIdsWithLabels eq '<a><b id="1, 2, 3, 4"><c id="1, 2, 3, 4"/></b></a>';              
  

=head2 moveLabels($source, $target)

Move all the labels from the source node to the target node and return the source node.

     Parameter  Description
  1  $source    Source node
  2  $target    Target node.

B<Example:>


    ok $x->stringReplacingIdsWithLabels eq '<a><b id="2, 3"><c id="1, 2, 3, 4"/></b></a>';                    
  
    $b->𝗺𝗼𝘃𝗲𝗟𝗮𝗯𝗲𝗹𝘀($c) for 1..2;                                                  
  
    ok $x->stringReplacingIdsWithLabels eq '<a><b><c id="1, 2, 3, 4"/></b></a>';                             
  

=head2 copyLabelsAndIdsInTree($source, $target)

Copy all the labels and ids in the source parse tree to the matching nodes in the target parse tree. Nodes are matched via L<path|/path>. Return the number of labels and ids copied.

     Parameter  Description
  1  $source    Source node
  2  $target    Target node.

B<Example:>


    ok -p (new $a->stringExtendingIdsWithLabels) eq <<END;                         
  <a id="a, a5">
    <b id="b, b2">
      <c id="c, c1"/>
    </b>
    <b id="B, b4">
      <c id="C, c3"/>
    </b>
  </a>
  END
  
    ok -p (new $A->stringExtendingIdsWithLabels) eq <<END;                        
  <a id="aa">
    <b id="bb">
      <c id="cc"/>
    </b>
    <b>
      <c/>
    </b>
  </a>
  END
  
    ok $a->𝗰𝗼𝗽𝘆𝗟𝗮𝗯𝗲𝗹𝘀𝗔𝗻𝗱𝗜𝗱𝘀𝗜𝗻𝗧𝗿𝗲𝗲($A) == 10;                                      
  
    ok -p (new $A->stringExtendingIdsWithLabels) eq <<END;                         
  <a id="aa, a, a5">
    <b id="bb, b, b2">
      <c id="cc, c, c1"/>
    </b>
    <b id="B, b4">
      <c id="C, c3"/>
    </b>
  </a>
  END
  

=head2 giveEveryIdAGuid($tree, $genGuid)

Give a guid to every node in the specified B<$tree> that has an id attribute, saving any existing id attribute as a label, and return the count of the number of such replacements made.

     Parameter  Description
  1  $tree      Tree
  2  $genGuid   A sub that accepts a number and a node and returns a new Guid each time it is called

B<Example:>


  if (1)                                                                          
   {my $a = Data::Edit::Xml::new(<<END);
  <a id="a">
    <b id="b">
      <c id="c"/>
    </b>
    <d/>
  </a>
  END
    my $n  =0;
    $a->𝗴𝗶𝘃𝗲𝗘𝘃𝗲𝗿𝘆𝗜𝗱𝗔𝗚𝘂𝗶𝗱(sub
     {my ($n, $o) = @_;
      qq(GUID-$n)
     });
  
    ok -p $a eq <<END;
  <a id="GUID-3">
    <b id="GUID-2">
      <c id="GUID-1"/>
    </b>
    <d/>
  </a>
  END
  
   ok $a->stringReplacingIdsWithLabels eq q(<a id="a"><b id="b"><c id="c"/></b><d/></a>);
   ok $a->stringExtendingIdsWithLabels eq q(<a id="GUID-3, a"><b id="GUID-2, b"><c id="GUID-1, c"/></b><d/></a>);
  
   }
  

=head2 createGuidId($node, @context)

Create an id for the specified B<$node> in the optional B<@context> from the md5Sum of its content moving any existing id to the labels associated with the B<$node> and return the existing B<$node>.

     Parameter  Description
  1  $node      Node
  2  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b>
      c
    </b>
  </a>
  END
  
    ok $a->𝗰𝗿𝗲𝗮𝘁𝗲𝗚𝘂𝗶𝗱𝗜𝗱->id eq q(GUID-390bf05d-8f56-71cc-6a47-71834840d695);
  

=head1 Operators

Operator access to methods use the assign versions to avoid 'useless use of operator in void context' messages. Use the non assign versions to return the results of the underlying method call.  Thus '/' returns the wrapping node, whilst '/=' does not.  Assign operators always return their left hand side even though the corresponding method usually returns the modification on the right.

=head2 opString($node, $op)

-A: L<printNode|/printNode>

-B: L<bitsNodeTextBlank|/bitsNodeTextBlank>

-b: L<isAllBlankText|/isAllBlankText>

-C: L<stringContent|/stringContent>

-c: L<context|/context>

-d: L<depth|/depth>

-e: L<prettyStringEnd|/prettyStringEnd>

-f: L<first node|/first>

-g: L<createGuidId|/createGuidId>

-k: L<createGuidId|/createGuidId>

-l: L<last node|/last>

-M: L<stringAsMd5Sum|/stringAsMd5Sum>

-O: L<contentAsTags2|/contentAsTags2>

-o: L<contentAsTags|/contentAsTags>

-p: L<prettyString|/prettyString>

-R: L<requiredCleanup|/requiredCleanup>

-r: L<stringTagsAndText|/stringTagsAndText>

-S: L<printStack|/printStack>

-s: L<string|/string>

-T: L<isText|/isText>

-t: L<tag|/tag>

-u: L<unwrap|/unwrap>

-W: L<id|/id>

-w: L<stringQuoted|/stringQuoted>

-X: L<prettyStringDitaHeaders|/prettyStringDitaHeaders>

-x: L<cut|/cut>

-z: L<prettyStringNumbered|/prettyStringNumbered>.

     Parameter  Description
  1  $node      Node
  2  $op        Monadic operator.

B<Example:>


    my $a = Data::Edit::Xml::new(<<END);                                           
  <a>
    <b><c>ccc</c></b>
    <d><e>eee</e></d>
  </a>
  END
  
    my $a = Data::Edit::Xml::new(<<END);                                               
  <a>
    <b>
      <c id="42" match="mm"/>
    </b>
    <d>
      <e/>
    </d>
  </a>
  END
  
    my ($c, $b, $e, $d) = $a->byList;                                             
  
    ok $c->printNode eq q(c id="42" match="mm");                                   
  
    ok -A $c eq q(c id="42" match="mm");                                          
  
    ok -b $e;                                                                     
  
    ok -c $e eq q(e d a);                                                          
  
    ok -f $b eq $c;                                                               
  
    ok -l $a eq $d;                                                               
  
    ok -O $a, q( b  d );                                                          
  
    ok -o $a, q(b d);                                                             
  
    ok -w $a eq q('<a><b><c id="42" match="mm"/></b><d><e/></d></a>');            
  
    ok -p $a eq <<END;                                                                          
  <a>
    <b>
      <c id="42" match="mm"/>
    </b>
    <d>
      <e/>
    </d>
  </a>
  END
  
    ok -s $a eq '<a><b><c id="42" match="mm"/></b><d><e/></d></a>';                
  
    ok -t $a eq 'a';                                                              
  
    $a->numberTree;                                                                
  
    ok -z $a eq <<END;                                                             
  <a id="1">
    <b id="2">
      <c id="42" match="mm"/>
    </b>
    <d id="4">
      <e id="5"/>
    </d>
  </a>
  END
  
  if (1)                                                                           
   {my $a = Data::Edit::Xml::new(<<END);
  <concept/>
  END
  
    ok $a->ditaPrettyPrintWithHeaders eq <<END;
  <?xml version="1.0" encoding="UTF-8"?>
  <!DOCTYPE concept PUBLIC "-//OASIS//DTD DITA Concept//EN" "concept.dtd" []>
  <concept/>
  END
  
   }
  

=head2 opContents($node)

@{} : nodes immediately below a node.

     Parameter  Description
  1  $node      Node.

B<Example:>


    my $a = Data::Edit::Xml::new(<<END);                                           
  <a>
    <b><c>ccc</c></b>
    <d><e>eee</e></d>
  </a>
  END
  
    my ($b, $d) =  @$a;                                                           
  
    ok -c $b eq q(b a);                                                           
  
    my ($c)     =  @$b;                                                           
  
    ok -c $c eq q(c b a);                                                         
  

=head2 opAt($node, $context)

<= : Check that a node is in the context specified by the referenced array of words.

     Parameter  Description
  1  $node      Node
  2  $context   Reference to array of words specifying the parents of the desired node.

B<Example:>


    ok -p $a eq <<END;                                                                          
  <a>
    <b>
      <c id="42" match="mm"/>
    </b>
    <d>
      <e/>
    </d>
  </a>
  END
  
    ok (($a >= [qw(d e)]) <= [qw(e d a)]);                                         
  

=head2 opNew($node, $text)

** : create a new node from the text on the right hand side: if the text contains a non word character \W the node will be create as text, else it will be created as a tag

     Parameter  Description
  1  $node      Node
  2  $text      Name node of node to create or text of new text element

B<Example:>


    my $a = Data::Edit::Xml::new("<a/>");                                         
  
    my $b = $a ** q(b);                                                           
  
    ok -s $b eq "<b/>";                                                           
  

=head2 opPutFirst($node, $text)

>> : put a node or string first under a node and return the new node.

     Parameter  Description
  1  $node      Node
  2  $text      Node or text to place first under the node.

B<Example:>


    ok -p $a eq <<END;                                                            
  <a/>
  END
  
    my $f = $a >> qq(first);                                                      
  
    ok -p $a eq <<END;                                                             
  <a>
    <first/>
  </a>
  END
  

=head2 opPutFirstAssign($node, $text)

>>= : put a node or string first under a node.

     Parameter  Description
  1  $node      Node
  2  $text      Node or text to place first under the node.

B<Example:>


    ok -p $a eq <<END;                                                            
  <a/>
  END
  
    $a >>= qq(first);                                                             
  
    ok -p $a eq <<END;                                                             
  <a>
    <first/>
  </a>
  END
  

=head2 opPutLast($node, $text)

<< : put a node or string last under a node and return the new node.

     Parameter  Description
  1  $node      Node
  2  $text      Node or text to place last under the node.

B<Example:>


    ok -p $a eq <<END;                                                             
  <a>
    <first/>
  </a>
  END
  
    my $l = $a << qq(last);                                                       
  
    ok -p $a eq <<END;                                                             
  <a>
    <first/>
    <last/>
  </a>
  END
  

=head2 opPutLastAssign($node, $text)

<<= : put a node or string last under a node.

     Parameter  Description
  1  $node      Node
  2  $text      Node or text to place last under the node.

B<Example:>


    ok -p $a eq <<END;                                                             
  <a>
    <first/>
  </a>
  END
  
    $a <<= qq(last);                                                              
  
    ok -p $a eq <<END;                                                             
  <a>
    <first/>
    <last/>
  </a>
  END
  

=head2 opPutNext($node, $text)

> + : put a node or string after the specified B<$node> and return the new node.

     Parameter  Description
  1  $node      Node
  2  $text      Node or text to place after the first node.

B<Example:>


    ok -p $a eq <<END;                                                             
  <a>
    <first/>
    <last/>
  </a>
  END
  
    $f += qq(next);                                                               
  
    ok -p $a eq <<END;                                                              
  <a>
    <first/>
    <next/>
    <last/>
  </a>
  END
  

=head2 opPutNextAssign($node, $text)

+= : put a node or string after the specified B<$node>.

     Parameter  Description
  1  $node      Node
  2  $text      Node or text to place after the first node.

B<Example:>


    ok -p $a eq <<END;                                                             
  <a>
    <first/>
    <last/>
  </a>
  END
  
    my $f = -f $a;                                                                
  
    $f += qq(next);                                                               
  
    ok -p $a eq <<END;                                                              
  <a>
    <first/>
    <next/>
    <last/>
  </a>
  END
  

=head2 opPutPrev($node, $text)

< - : put a node or string before the specified B<$node> and return the new node.

     Parameter  Description
  1  $node      Node
  2  $text      Node or text to place before the first node.

B<Example:>


    ok -p $a eq <<END;                                                              
  <a>
    <first/>
    <next/>
    <last/>
  </a>
  END
  
    $l -= qq(prev);                                                               
  
    ok -p $a eq <<END;                                                            
  <a>
    <first/>
    <next/>
    <prev/>
    <last/>
  </a>
  END
  

=head2 opPutPrevAssign($node, $text)

-= : put a node or string before the specified B<$node>,

     Parameter  Description
  1  $node      Node
  2  $text      Node or text to place before the first node.

B<Example:>


    ok -p $a eq <<END;                                                              
  <a>
    <first/>
    <next/>
    <last/>
  </a>
  END
  
    my $l = -l $a;                                                                
  
    $l -= qq(prev);                                                               
  
    ok -p $a eq <<END;                                                            
  <a>
    <first/>
    <next/>
    <prev/>
    <last/>
  </a>
  END
  

=head2 opBy($node, $code)

x= : Traverse a L<parse|/parse> tree in post-order.

     Parameter  Description
  1  $node      Parse tree
  2  $code      Code to execute against each node.

B<Example:>


    ok -p $a eq <<END;                                                                          
  <a>
    <b>
      <c id="42" match="mm"/>
    </b>
    <d>
      <e/>
    </d>
  </a>
  END
  
     {my $s; $a x= sub{$s .= -t $_}; ok $s eq "cbeda"                             
  

=head2 opGo($node, $go)

>= : Search for a node via a specification provided as a reference to an array of words each number.  Each word represents a tag name, each number the index of the previous tag or zero by default.

     Parameter  Description
  1  $node      Node
  2  $go        Reference to an array of search parameters.

B<Example:>


    ok -p $a eq <<END;                                                                          
  <a>
    <b>
      <c id="42" match="mm"/>
    </b>
    <d>
      <e/>
    </d>
  </a>
  END
  
    ok (($a >= [qw(d e)]) <= [qw(e d a)]);                                         
  

=head2 opAttr($node, $attr)

% : Get the value of an attribute of the specified B<$node>.

     Parameter  Description
  1  $node      Node
  2  $attr      Reference to an array of words and numbers specifying the node to search for.

B<Example:>


    my $a = Data::Edit::Xml::new('<a number="1"/>');                              
  
    ok $a %  qq(number) == 1;                                                     
  

=head2 opWrapWith($node, $tag)

/ : Wrap node with a tag, returning the wrapping node.

     Parameter  Description
  1  $node      Node
  2  $tag       Tag.

B<Example:>


    my $c = Data::Edit::Xml::new("<c/>");                                         
  
    my $b = $c / qq(b);                                                           
  
    ok -s $b eq "<b><c/></b>";                                                    
  
    my $a = $b / qq(a);                                                           
  
    ok -s $a eq "<a><b><c/></b></a>";                                             
  

=head2 opWrapContentWith($node, $tag)

* : Wrap content with a tag, returning the wrapping node.

     Parameter  Description
  1  $node      Node
  2  $tag       Tag.

B<Example:>


   {my $a = Data::Edit::Xml::new(<<END);                                          
  <a>
    <b>
      <c/>
      <d/>
    </b>
  </a>
  END
  
    my ($c, $d, $b) = $a->byList;                                                 
  
    $b *= q(B);                                                                   
  
    ok -p $a eq <<END;                                                            
  <a>
    <b>
      <B>
        <c/>
        <d/>
      </B>
    </b>
  </a>
  END
  

=head2 opCut($node)

-- : Cut out a node.

     Parameter  Description
  1  $node      Node.

B<Example:>


    my $x = Data::Edit::Xml::new(<<END);                                          
  <a>
    <b><c/></b>
  </a>
  END
  
    my $b = $x >= qq(b);                                                          
  
     --$b;                                                                        
  
    ok -s $x eq "<a/>";                                                           
  
    ok -s $b eq "<b><c/></b>";                                                    
  

=head2 opUnwrap($node)

++ : Unwrap a node.

     Parameter  Description
  1  $node      Node.

B<Example:>


   {my $a = Data::Edit::Xml::new(<<END);                                          
  <a>
    <b>
      <c/>
      <d/>
    </b>
  </a>
  END
  
    my ($c, $d, $b) = $a->byList;                                                 
  
    $b++;                                                                         
  
    ok -p $a eq <<END;                                                            
  <a>
    <c/>
    <d/>
  </a>
  END
  

=head1 Reuse

Identify common components that can be reused

=head2 subMd5Tree($node)

Return the L<md5 sum|https://en.wikipedia.org/wiki/MD5> of the L<stringContent> of the parse tree.

     Parameter  Description
  1  $node      Node

B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
   <b>bb</b>
   <b><c>bb</c></b>
   <d>dd</d>
   <d>dd</d>
  </a>
  END
  
    is_deeply [$a->first->𝘀𝘂𝗯𝗠𝗱𝟱𝗧𝗿𝗲𝗲], [q(21ad0bd836b90d08f4cf640b4c298e7c), q(bb)];
  
    is_deeply subMd5($a, map {$_=>1} qw(b c)),
     {b => {"21ad0bd836b90d08f4cf640b4c298e7c" => {    bb      => 1},
            "95c1bde7f88ced04c1b1fbcab120ed96" => {"<c>bb</c>" => 1},
            },
      c => {"21ad0bd836b90d08f4cf640b4c298e7c" => {    bb      => 1}},
     };
  # owf(q(/home/phil/z/z/z/out.xml), dump(subMd5($a, map{$_=>1} qw(b c))));
  

=head2 subMd5($node, %tags)

Return a hash {L<md5 sum|https://en.wikipedia.org/wiki/MD5> of parse tree}{string representation of sub tree}++ to locate common parse trees under B<$node> that could be included via a conref.  L<md5 sum|https://en.wikipedia.org/wiki/MD5>s are only calculated for nodes whose tags match the keys of the B<$tags> hash that have truthful values. The attributes of the root node of each sub tree are ignored in the computation as they can be supplied by the conreffing element.

     Parameter  Description
  1  $node      Node
  2  %tags      Hash of acceptable tags.

B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
   <b>bb</b>
   <b><c>bb</c></b>
   <d>dd</d>
   <d>dd</d>
  </a>
  END
  
    is_deeply [$a->first->subMd5Tree], [q(21ad0bd836b90d08f4cf640b4c298e7c), q(bb)];
  
    is_deeply 𝘀𝘂𝗯𝗠𝗱𝟱($a, map {$_=>1} qw(b c)),
     {b => {"21ad0bd836b90d08f4cf640b4c298e7c" => {    bb      => 1},
            "95c1bde7f88ced04c1b1fbcab120ed96" => {"<c>bb</c>" => 1},
            },
      c => {"21ad0bd836b90d08f4cf640b4c298e7c" => {    bb      => 1}},
     };
  # owf(q(/home/phil/z/z/z/out.xml), dump(𝘀𝘂𝗯𝗠𝗱𝟱($a, map{$_=>1} qw(b c))));
  

=head1 Statistics

Statistics describing the L<parse|/parse> tree.

=head2 count($node, @names)

Return the count of the number of instances of the specified tags under the specified B<$node>, either by tag in array context or in total in scalar context.

     Parameter  Description
  1  $node      Node
  2  @names     Possible tags immediately under the node.

B<Example:>


    my $x = Data::Edit::Xml::new(<<END);                                           
  <a>
  
  </a>
  END
  
    ok $x->𝗰𝗼𝘂𝗻𝘁 == 0;                                                            
  

=head2 countTags($node)

Count the number of tags in a L<parse|/parse> tree.

     Parameter  Description
  1  $node      Parse tree.

B<Example:>


      ok -p $a eq <<END;                                                            
  <a id="aa">
    <b id="bb">
      <c id="cc"/>
    </b>
  </a>
  END
  
      ok $a->𝗰𝗼𝘂𝗻𝘁𝗧𝗮𝗴𝘀 == 3;                                                      
  

=head2 countTagNames($node, @exclude)

Return a reference to a hash showing the number of instances of each tag on and below the specified B<$node> excluding any tags named in B<@exclude>.

     Parameter  Description
  1  $node      Node
  2  @exclude   Tags to exclude from the count

B<Example:>


    my $x = Data::Edit::Xml::new(<<END);
  <a A="A1" B="B1" C="C1">
    <b  B="B1" C="C1">
      <c  C="C1">
      </c>
      <c/>
    </b>
    <b  B="B2">
      <c C="C2"/>
    </b>
  </a>
  END
  
    is_deeply $x->𝗰𝗼𝘂𝗻𝘁𝗧𝗮𝗴𝗡𝗮𝗺𝗲𝘀,
     {a => 1, b => 2, c => 3};
  
    is_deeply $x->countAttrNames,
     {A => 1, B => 3, C => 4};
  
    is_deeply $x->countAttrValues,
     {A1 => 1, B1 => 2, B2 => 1, C1 => 3, C2 => 1};
  
    is_deeply $x->countAttrNamesAndValues,
     {A => {A1 => 1}, B => {B1 => 2, B2 => 1}, C => {C1 => 3, C2 => 1}};
  

=head2 countNonEmptyTags($node, @exclude)

Return a reference to a hash showing the number of instances of each non empty tag on and below the specified B<$node> excluding any tags named in B<@exclude>.

     Parameter  Description
  1  $node      Node
  2  @exclude   Tags to exclude from the count

B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b>
      <c>
        <d/>
      </c>
    </b>
  </a>
  END
  
    is_deeply $a->𝗰𝗼𝘂𝗻𝘁𝗡𝗼𝗻𝗘𝗺𝗽𝘁𝘆𝗧𝗮𝗴𝘀(qw(c)), {a=>1, b=>1};
    is_deeply $a->go_b->𝗰𝗼𝘂𝗻𝘁𝗡𝗼𝗻𝗘𝗺𝗽𝘁𝘆𝗧𝗮𝗴𝘀,  {b=>1, c=>1};
  

=head2 countTexts($node)

Return a reference to a hash showing the incidence of texts on and below the specified B<$node>.

     Parameter  Description
  1  $node      Node

B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <concept>
    <title/>
    <conbody/>
  </concept>
  END
  
    is_deeply $a->𝗰𝗼𝘂𝗻𝘁𝗧𝗲𝘅𝘁𝘀, {};
  

=head2 countAttrNames($node, $count)

Return a reference to a hash showing the number of instances of each attribute on and below the specified B<$node>.

     Parameter  Description
  1  $node      Node
  2  $count     Attribute count so far

B<Example:>


    my $x = Data::Edit::Xml::new(<<END);
  <a A="A1" B="B1" C="C1">
    <b  B="B1" C="C1">
      <c  C="C1">
      </c>
      <c/>
    </b>
    <b  B="B2">
      <c C="C2"/>
    </b>
  </a>
  END
  
    is_deeply $x->countTagNames,
     {a => 1, b => 2, c => 3};
  
    is_deeply $x->𝗰𝗼𝘂𝗻𝘁𝗔𝘁𝘁𝗿𝗡𝗮𝗺𝗲𝘀,
     {A => 1, B => 3, C => 4};
  
    is_deeply $x->countAttrValues,
     {A1 => 1, B1 => 2, B2 => 1, C1 => 3, C2 => 1};
  
    is_deeply $x->countAttrNamesAndValues,
     {A => {A1 => 1}, B => {B1 => 2, B2 => 1}, C => {C1 => 3, C2 => 1}};
  

=head2 countAttrNamesOnTagExcluding($node, @attr)

Count the number of attributes owned by the specified B<$node> that are not in the specified list.

     Parameter  Description
  1  $node      Node
  2  @attr      Attributes to ignore

B<Example:>


   {my $a = Data::Edit::Xml::new(q(<a a="1" b="2" c="3" d="4" e="5"/>));          
  

=head2 countAttrValues($node, $count)

Return a reference to a hash showing the number of instances of each attribute value on and below the specified B<$node>.

     Parameter  Description
  1  $node      Node
  2  $count     Count of attributes so far.

B<Example:>


    my $x = Data::Edit::Xml::new(<<END);
  <a A="A1" B="B1" C="C1">
    <b  B="B1" C="C1">
      <c  C="C1">
      </c>
      <c/>
    </b>
    <b  B="B2">
      <c C="C2"/>
    </b>
  </a>
  END
  
    is_deeply $x->countTagNames,
     {a => 1, b => 2, c => 3};
  
    is_deeply $x->countAttrNames,
     {A => 1, B => 3, C => 4};
  
    is_deeply $x->𝗰𝗼𝘂𝗻𝘁𝗔𝘁𝘁𝗿𝗩𝗮𝗹𝘂𝗲𝘀,
     {A1 => 1, B1 => 2, B2 => 1, C1 => 3, C2 => 1};
  
    is_deeply $x->countAttrNamesAndValues,
     {A => {A1 => 1}, B => {B1 => 2, B2 => 1}, C => {C1 => 3, C2 => 1}};
  

=head2 countAttrNamesAndValues($node, $count)

Return a reference to a hash showing the number of instances of each attribute name and value on and below the specified B<$node>.

     Parameter  Description
  1  $node      Node
  2  $count     Count of attributes so far.

B<Example:>


    my $x = Data::Edit::Xml::new(<<END);
  <a A="A1" B="B1" C="C1">
    <b  B="B1" C="C1">
      <c  C="C1">
      </c>
      <c/>
    </b>
    <b  B="B2">
      <c C="C2"/>
    </b>
  </a>
  END
  
    is_deeply $x->countTagNames,
     {a => 1, b => 2, c => 3};
  
    is_deeply $x->countAttrNames,
     {A => 1, B => 3, C => 4};
  
    is_deeply $x->countAttrValues,
     {A1 => 1, B1 => 2, B2 => 1, C1 => 3, C2 => 1};
  
    is_deeply $x->𝗰𝗼𝘂𝗻𝘁𝗔𝘁𝘁𝗿𝗡𝗮𝗺𝗲𝘀𝗔𝗻𝗱𝗩𝗮𝗹𝘂𝗲𝘀,
     {A => {A1 => 1}, B => {B1 => 2, B2 => 1}, C => {C1 => 3, C2 => 1}};
  

=head2 countOutputClasses($node, $count)

Count instances of outputclass attributes

     Parameter  Description
  1  $node      Node
  2  $count     Count so far.

B<Example:>


    my $a = Data::Edit::Xml::newTree("a", id=>1, class=>2, href=>3, outputclass=>4); 
  
    is_deeply { 4 => 1 }, $a->𝗰𝗼𝘂𝗻𝘁𝗢𝘂𝘁𝗽𝘂𝘁𝗖𝗹𝗮𝘀𝘀𝗲𝘀;                                 
  

=head2 countReport($node, @context)

Count tags, attributes, words below the specified node

     Parameter  Description
  1  $node      Node to count from
  2  @context   Optional context

B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b>aaa bbb ccc</b>
    <c>AAA BBB CCC</c>
    <c>aaa AAA</c>
  </a>
  END
  
    my $t = $a->𝗰𝗼𝘂𝗻𝘁𝗥𝗲𝗽𝗼𝗿𝘁;
  
    ok $t eq <<END;
     Tag  Count
  1    3  CDATA
  2    1  a
  3    1  b
  4    2  c
  END
  

B<cr> is a synonym for L<countReport|/countReport>.


=head2 changeReasonCommentSelectionSpecification()

Provide a specification to select L<change reason comments|/crc> to be inserted as text into a L<parse|/parse> tree. A specification can be either:

=over

=item the name of a code to be accepted,

=item a regular expression which matches the codes to be accepted,

=item a hash whose keys are defined for the codes to be accepted or

=item B<undef> (the default) to specify that no such comments should be accepted.

=back


B<Example:>


    𝗰𝗵𝗮𝗻𝗴𝗲𝗥𝗲𝗮𝘀𝗼𝗻𝗖𝗼𝗺𝗺𝗲𝗻𝘁𝗦𝗲𝗹𝗲𝗰𝘁𝗶𝗼𝗻𝗦𝗽𝗲𝗰𝗶𝗳𝗶𝗰𝗮𝘁𝗶𝗼𝗻 = {ccc=>1, ddd=>1};                  
  
    𝗰𝗵𝗮𝗻𝗴𝗲𝗥𝗲𝗮𝘀𝗼𝗻𝗖𝗼𝗺𝗺𝗲𝗻𝘁𝗦𝗲𝗹𝗲𝗰𝘁𝗶𝗼𝗻𝗦𝗽𝗲𝗰𝗶𝗳𝗶𝗰𝗮𝘁𝗶𝗼𝗻 = undef;                             
  

This is a static method and so should be invoked as:

  Data::Edit::Xml::changeReasonCommentSelectionSpecification


=head2 crc($node, $code, $reason)

Insert a comment consisting of a code and an optional reason as text into the L<parse|/parse> tree to indicate the location of changes to the L<parse|/parse> tree.  As such comments tend to become very numerous, only comments whose codes matches the specification provided in L<changeReasonCommentSelectionSpecification|/changeReasonCommentSelectionSpecification> are accepted for insertion. Subsequently these comments can be easily located using:

B<grep -nr "<!-->I<code>B<">

on the file containing a printed version of the L<parse|/parse> tree. Please note that these comments will be removed if the output file is reparsed.

Returns the specified B<$node>.

     Parameter  Description
  1  $node      Node being changed
  2  $code      Reason code
  3  $reason    Optional text description of change

B<Example:>


   {my $a = Data::Edit::Xml::new("<a><b/></a>");                                  
  
    my ($b) = $a->contents;                                                       
  
    changeReasonCommentSelectionSpecification = {ccc=>1, ddd=>1};                  
  
    $b->putFirst(my $c = $b->newTag(q(c)));                                       
  
    $c->𝗰𝗿𝗰($_) for qw(aaa ccc);                                                  
  
    ok <<END eq -p $a;                                                            
  <a>
    <b><!--ccc-->
      <c/>
    </b>
  </a>
  END
  
    changeReasonCommentSelectionSpecification = undef;                             
  
    $c->putFirst(my $d = $c->newTag(q(d)));                                       
  
    $d->𝗰𝗿𝗰($_) for qw(aaa ccc);                                                  
  
    ok <<END eq -p $a;                                                            
  <a>
    <b><!--ccc-->
      <c>
        <d/>
      </c>
    </b>
  </a>
  END
  

=head2 howFirst($node)

Return the depth to which the specified B<$node> is L<first|/isFirst> else B<0>.

     Parameter  Description
  1  $node      Node

B<Example:>


   {my $a = Data::Edit::Xml::new(<<END);                                                       
  <a>
    <b>
      <c>
        <d/>
      </c>
    </b>
    <b>
      <c/>
    </b>
    <e>
      <f/>
    </e>
  </a>
  END
  
    my ($d, $c, $b, $C, $B, $f, $e) = $a->byList;                                              
  
    ok $d->𝗵𝗼𝘄𝗙𝗶𝗿𝘀𝘁     == 4;                                                     
  

=head2 howLast($node)

Return the depth to which the specified B<$node> is L<last|/isLast> else B<0>.

     Parameter  Description
  1  $node      Node

B<Example:>


   {my $a = Data::Edit::Xml::new(<<END);                                                       
  <a>
    <b>
      <c>
        <d/>
      </c>
    </b>
    <b>
      <c/>
    </b>
    <e>
      <f/>
    </e>
  </a>
  END
  
    my ($d, $c, $b, $C, $B, $f, $e) = $a->byList;                                              
  
    ok $f->𝗵𝗼𝘄𝗟𝗮𝘀𝘁      == 3;                                                     
  

=head2 howOnlyChild($node)

Return the depth to which the specified B<$node> is an L<only child|/isOnlyChild> else B<0>.

     Parameter  Description
  1  $node      Node

B<Example:>


   {my $a = Data::Edit::Xml::new(<<END);                                                       
  <a>
    <b>
      <c>
        <d/>
      </c>
    </b>
    <b>
      <c/>
    </b>
    <e>
      <f/>
    </e>
  </a>
  END
  
    my ($d, $c, $b, $C, $B, $f, $e) = $a->byList;                                              
  
    ok $d->𝗵𝗼𝘄𝗢𝗻𝗹𝘆𝗖𝗵𝗶𝗹𝗱 == 2;                                                     
  

=head2 howFar($first, $second)

Return how far the first node is from the second node along a path through their common ancestor.

     Parameter  Description
  1  $first     First node
  2  $second    Second node

B<Example:>


   {my $a = Data::Edit::Xml::new(<<END);                                                       
  <a>
    <b>
      <c>
        <d/>
      </c>
    </b>
    <b>
      <c/>
    </b>
    <e>
      <f/>
    </e>
  </a>
  END
  
    my ($d, $c, $b, $C, $B, $f, $e) = $a->byList;                                              
  
    is_deeply [$d->commonAdjacentAncestors($C)], [$b, $B];                                                       
  
    ok $d->𝗵𝗼𝘄𝗙𝗮𝗿($d) == 0;                                                       
  
    ok $d->𝗵𝗼𝘄𝗙𝗮𝗿($a) == 3;                                                       
  
    ok $b->𝗵𝗼𝘄𝗙𝗮𝗿($B) == 1;                                                       
  
    ok $d->𝗵𝗼𝘄𝗙𝗮𝗿($f) == 5;                                                       
  
    ok $d->𝗵𝗼𝘄𝗙𝗮𝗿($C) == 4;                                                       
  

=head2 howFarAbove($above, $below)

Return how far the first node is  L<above|/above> the second node is or B<0> if the first node is not strictly L<above|/above> the second node.

     Parameter  Description
  1  $above     First node above
  2  $below     Second node below

B<Example:>


   {my $a = Data::Edit::Xml::new(<<END);                                                       
  <a>
    <b>
      <c>
        <d/>
      </c>
    </b>
    <b>
      <c/>
    </b>
    <e>
      <f/>
    </e>
  </a>
  END
  
    my ($d, $c, $b, $C, $B, $f, $e) = $a->byList;                                              
  
    ok  $a->𝗵𝗼𝘄𝗙𝗮𝗿𝗔𝗯𝗼𝘃𝗲($d) == 3;                                                 
  
    ok !$d->𝗵𝗼𝘄𝗙𝗮𝗿𝗔𝗯𝗼𝘃𝗲($c);                                                      
  

=head2 howFarBelow($below, $above)

Return how far the first node is  L<below|/below> the second node is or B<0> if the first node is not strictly L<below|/below> the second node.

     Parameter  Description
  1  $below     First node below
  2  $above     Second node above

B<Example:>


   {my $a = Data::Edit::Xml::new(<<END);                                                       
  <a>
    <b>
      <c>
        <d/>
      </c>
    </b>
    <b>
      <c/>
    </b>
    <e>
      <f/>
    </e>
  </a>
  END
  
    my ($d, $c, $b, $C, $B, $f, $e) = $a->byList;                                              
  
    ok  $d->𝗵𝗼𝘄𝗙𝗮𝗿𝗕𝗲𝗹𝗼𝘄($a) == 3;                                                 
  
    ok !$c->𝗵𝗼𝘄𝗙𝗮𝗿𝗕𝗲𝗹𝗼𝘄($d);                                                      
  

=head1 Required clean up

Insert required clean up tags.

=head2 requiredCleanUp($node, $outputclass)

Replace a B<$node> with a required cleanup node with special characters replaced by symbols and with the optional B<$outputclass>.

Returns the specified B<$node>.

     Parameter     Description
  1  $node         Node
  2  $outputclass  Optional outputclass attribute of required cleanup tag

B<Example:>


   {my $a = Data::Edit::Xml::new(<<END);                                          
  <a>
    <b>
      <c>
        ccc
      </c>
    </b>
  </a>
  END
  
    my ($b) = $a->contents;                                                       
  
    $b->𝗿𝗲𝗾𝘂𝗶𝗿𝗲𝗱𝗖𝗹𝗲𝗮𝗻𝗨𝗽(q(33));                                                   
  
    ok -p $a eq <<END;                                                            
  <a>
    <required-cleanup outputclass="33">&lt;b&gt;
    &lt;c&gt;
        ccc
      &lt;/c&gt;
  &lt;/b&gt;
  </required-cleanup>
  </a>
  END
  

=head2 replaceWithRequiredCleanUp($node, @text)

Replace a B<$node> with required cleanup B<@text> and return the new node

     Parameter  Description
  1  $node      Node to be replace
  2  @text      Clean up message

B<Example:>


   {my $a = Data::Edit::Xml::new(<<END);                                          
  <a>
    <b/>
  </a>
  END
  
    my ($b) = $a->contents;                                                       
  
    $b->𝗿𝗲𝗽𝗹𝗮𝗰𝗲𝗪𝗶𝘁𝗵𝗥𝗲𝗾𝘂𝗶𝗿𝗲𝗱𝗖𝗹𝗲𝗮𝗻𝗨𝗽(q(bb));                                        
  
    ok -p $a eq <<END;                                                            
  <a>
    <required-cleanup>bb</required-cleanup>
  </a>
  END
  

=head2 putFirstRequiredCleanUp($node, @text)

Place a required cleanup first under a specified B<$node> using the specified B<@text>  and return the required clean up node.

     Parameter  Description
  1  $node      Node
  2  @text      Clean up message

B<Example:>


   {my $a = Data::Edit::Xml::new(<<END);                                          
  <a>
    <b/>
  </a>
  END
  
    $a->𝗽𝘂𝘁𝗙𝗶𝗿𝘀𝘁𝗥𝗲𝗾𝘂𝗶𝗿𝗲𝗱𝗖𝗹𝗲𝗮𝗻𝗨𝗽(qq(1111
));                                      
  
    ok -p $a eq <<END;                                                             
  <a>
    <required-cleanup>1111
  </required-cleanup>
    <b/>
  </a>
  END
  

=head2 putLastRequiredCleanUp($node, @text)

Place a required cleanup last under a specified B<$node> using the specified B<@text>  and return the required clean up node.

     Parameter  Description
  1  $node      Node
  2  @text      Clean up message

B<Example:>


    ok -p $a eq <<END;                                                             
  <a>
    <required-cleanup>1111
  </required-cleanup>
    <b/>
  </a>
  END
  
    $a->𝗽𝘂𝘁𝗟𝗮𝘀𝘁𝗥𝗲𝗾𝘂𝗶𝗿𝗲𝗱𝗖𝗹𝗲𝗮𝗻𝗨𝗽(qq(4444
));                                       
  
    ok -p $a eq <<END;                                                             
  <a>
    <required-cleanup>1111
  </required-cleanup>
    <b/>
    <required-cleanup>4444
  </required-cleanup>
  </a>
  END
  

=head2 putNextRequiredCleanUp($node, @text)

Place a required cleanup next after a specified B<$node> using the specified B<@text>  and return the required clean up node.

     Parameter  Description
  1  $node      Node
  2  @text      Clean up message

B<Example:>


    ok -p $a eq <<END;                                                             
  <a>
    <required-cleanup>1111
  </required-cleanup>
    <b/>
    <required-cleanup>4444
  </required-cleanup>
  </a>
  END
  
    $a->go(q(b))->𝗽𝘂𝘁𝗡𝗲𝘅𝘁𝗥𝗲𝗾𝘂𝗶𝗿𝗲𝗱𝗖𝗹𝗲𝗮𝗻𝗨𝗽(qq(3333
));                             
  
    ok -p $a eq <<END;                                                              
  <a>
    <required-cleanup>1111
  </required-cleanup>
    <b/>
    <required-cleanup>3333
  </required-cleanup>
    <required-cleanup>4444
  </required-cleanup>
  </a>
  END
  

=head2 putPrevRequiredCleanUp($node, $text)

Place a required cleanup before a specified B<$node> using the specified B<@text>  and return the required clean up node.

     Parameter  Description
  1  $node      Node
  2  $text      Clean up message

B<Example:>


    ok -p $a eq <<END;                                                              
  <a>
    <required-cleanup>1111
  </required-cleanup>
    <b/>
    <required-cleanup>3333
  </required-cleanup>
    <required-cleanup>4444
  </required-cleanup>
  </a>
  END
  
    $a->go(q(b))->𝗽𝘂𝘁𝗣𝗿𝗲𝘃𝗥𝗲𝗾𝘂𝗶𝗿𝗲𝗱𝗖𝗹𝗲𝗮𝗻𝗨𝗽(qq(2222
));                             
  
    ok -p $a eq <<END;                                                            
  <a>
    <required-cleanup>1111
  </required-cleanup>
    <required-cleanup>2222
  </required-cleanup>
    <b/>
    <required-cleanup>3333
  </required-cleanup>
    <required-cleanup>4444
  </required-cleanup>
  </a>
  END
  

=head1 Conversions

Methods useful for conversions from word, L<html|https://en.wikipedia.org/wiki/HTML> and L<Dita|http://docs.oasis-open.org/dita/dita/v1.3/os/part2-tech-content/dita-v1.3-os-part2-tech-content.html> to L<Dita|http://docs.oasis-open.org/dita/dita/v1.3/os/part2-tech-content/dita-v1.3-os-part2-tech-content.html>.

=head2 Dita Conversions

Methods useful for enhancing L<Dita|http://docs.oasis-open.org/dita/dita/v1.3/os/part2-tech-content/dita-v1.3-os-part2-tech-content.html>.

=head3 ditaGetConRef($ref, $sourceFile, $matchContext, $matchContextOnly)

Get the value of a B<conref> attribute given a valid L<Dita|http://docs.oasis-open.org/dita/dita/v1.3/os/part2-tech-content/dita-v1.3-os-part2-tech-content.html> B<$ref>erence found in the file named B<$sourceFile>. Optionally, if the regular expression B<$matchContext> is supplied then nodes whose context (single blank separated) tags match this regular expression will be considered first as a target of the conref, if such a search fails then any unique node with the matching id will be the considered next as a possible target unless the optional B<$matchContextOnly> parameter is true. Note: all the conrefs in the included content will also be expanded creating the possibility of endless circular expansions.

     Parameter          Description
  1  $ref               Dita ref value of conref
  2  $sourceFile        Source file containing conref
  3  $matchContext      Optional regular expression of acceptable context
  4  $matchContextOnly  Confine search to acceptable context.

B<Example:>


    my $dir = temporaryFolder;
    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b id="b">
      <c/>
    </b>
  </a>
  END
  
    my $target = fpe($dir, qw(target xml));
    owf($target, -p $a);
  
    my $b = 𝗱𝗶𝘁𝗮𝗚𝗲𝘁𝗖𝗼𝗻𝗥𝗲𝗳("target.xml#a/b", $dir);
    ok -p $b eq <<END;
  <b id="b">
    <c/>
  </b>
  END
  
    my $A = Data::Edit::Xml::new(<<END);
  <a>
    <b id="B" conref="target.xml#a/b"/>
  </a>
  END
    my $target2 = fpe($dir, qw(target2 xml));
    owf($target2, -p $A);
  
    my $B = Data::Edit::Xml::new(<<END);
  <a>
    <b id="C" conref="target2.xml#a/B"/>
  </a>
  END
  
    $B->ditaExpandAllConRefs($dir);
    ok -p $B eq <<END;
  <a>
    <b id="C">
      <c/>
    </b>
  </a>
  END
  
    my $CF = owf(fpe($dir, qw(source xml)), <<END);
  <a>
    <b id="D" conref="target2.xml#a/B"/>
  </a>
  END
  
    my $C = Data::Edit::Xml::new($CF);
  
    $C->first__ditaReplaceAnyConref;
    ok -p $C eq <<END;
  <a>
    <b id="D">
      <c/>
    </b>
  </a>
  END
  
    my $DF = owf(fpe($dir, qw(source xml)), <<END);
  <a id="E" conref="target2.xml#a/B"/>
  END
  
    my $D = Data::Edit::Xml::new($DF);
  
    $D->ditaReplaceAnyConref;
    ok -p $D eq <<END;
  <b id="E">
    <c/>
  </b>
  END
  
    my $EF = owf(fpe($dir, qw(source xml)), <<END);
  <a id="F" conref="target2.xml"/>
  END
  
    my $E = Data::Edit::Xml::new($EF);
  
    $E->ditaReplaceAnyConref;
  
    ok -p $E eq <<END;
  <a id="F">
    <b id="B">
      <c/>
    </b>
  </a>
  END
  
    clearFolder($dir, 3);
  

This is a static method and so should be invoked as:

  Data::Edit::Xml::ditaGetConRef


=head3 ditaReplaceConref($node, $sourceFile)

Replace a conref on the specified B<$node> in the specified source file B<$sourceFile> with the parse tree of the referenced content. Returns the referenced content.  Confesses if there is no conref to replace on $node.

     Parameter    Description
  1  $node        Node with conref attribute
  2  $sourceFile  Source file containing the node.

B<Example:>


    my $dir = temporaryFolder;
    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b id="b">
      <c/>
    </b>
  </a>
  END
  
    my $target = fpe($dir, qw(target xml));
    owf($target, -p $a);
  
    my $b = ditaGetConRef("target.xml#a/b", $dir);
    ok -p $b eq <<END;
  <b id="b">
    <c/>
  </b>
  END
  
    my $A = Data::Edit::Xml::new(<<END);
  <a>
    <b id="B" conref="target.xml#a/b"/>
  </a>
  END
    my $target2 = fpe($dir, qw(target2 xml));
    owf($target2, -p $A);
  
    my $B = Data::Edit::Xml::new(<<END);
  <a>
    <b id="C" conref="target2.xml#a/B"/>
  </a>
  END
  
    $B->ditaExpandAllConRefs($dir);
    ok -p $B eq <<END;
  <a>
    <b id="C">
      <c/>
    </b>
  </a>
  END
  
    my $CF = owf(fpe($dir, qw(source xml)), <<END);
  <a>
    <b id="D" conref="target2.xml#a/B"/>
  </a>
  END
  
    my $C = Data::Edit::Xml::new($CF);
  
    $C->first__ditaReplaceAnyConref;
    ok -p $C eq <<END;
  <a>
    <b id="D">
      <c/>
    </b>
  </a>
  END
  
    my $DF = owf(fpe($dir, qw(source xml)), <<END);
  <a id="E" conref="target2.xml#a/B"/>
  END
  
    my $D = Data::Edit::Xml::new($DF);
  
    $D->ditaReplaceAnyConref;
    ok -p $D eq <<END;
  <b id="E">
    <c/>
  </b>
  END
  
    my $EF = owf(fpe($dir, qw(source xml)), <<END);
  <a id="F" conref="target2.xml"/>
  END
  
    my $E = Data::Edit::Xml::new($EF);
  
    $E->ditaReplaceAnyConref;
  
    ok -p $E eq <<END;
  <a id="F">
    <b id="B">
      <c/>
    </b>
  </a>
  END
  
    clearFolder($dir, 3);
  

=head3 ditaReplaceAnyConref($node, @context)

Replace a conref with the referenced content if we are in the optional context and the target of the conref can be located. Returns true regardless of any processing performed to allow L<Dita::Pcd|https://metacpan.org/pod/Dita::PCD> processing to continue.

     Parameter  Description
  1  $node      Node with conref attribute
  2  @context   Optional context.

B<Example:>


    my $dir = temporaryFolder;
    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b id="b">
      <c/>
    </b>
  </a>
  END
  
    my $target = fpe($dir, qw(target xml));
    owf($target, -p $a);
  
    my $b = ditaGetConRef("target.xml#a/b", $dir);
    ok -p $b eq <<END;
  <b id="b">
    <c/>
  </b>
  END
  
    my $A = Data::Edit::Xml::new(<<END);
  <a>
    <b id="B" conref="target.xml#a/b"/>
  </a>
  END
    my $target2 = fpe($dir, qw(target2 xml));
    owf($target2, -p $A);
  
    my $B = Data::Edit::Xml::new(<<END);
  <a>
    <b id="C" conref="target2.xml#a/B"/>
  </a>
  END
  
    $B->ditaExpandAllConRefs($dir);
    ok -p $B eq <<END;
  <a>
    <b id="C">
      <c/>
    </b>
  </a>
  END
  
    my $CF = owf(fpe($dir, qw(source xml)), <<END);
  <a>
    <b id="D" conref="target2.xml#a/B"/>
  </a>
  END
  
    my $C = Data::Edit::Xml::new($CF);
  
    $C->first__ditaReplaceAnyConref;
    ok -p $C eq <<END;
  <a>
    <b id="D">
      <c/>
    </b>
  </a>
  END
  
    my $DF = owf(fpe($dir, qw(source xml)), <<END);
  <a id="E" conref="target2.xml#a/B"/>
  END
  
    my $D = Data::Edit::Xml::new($DF);
  
    $D->𝗱𝗶𝘁𝗮𝗥𝗲𝗽𝗹𝗮𝗰𝗲𝗔𝗻𝘆𝗖𝗼𝗻𝗿𝗲𝗳;
    ok -p $D eq <<END;
  <b id="E">
    <c/>
  </b>
  END
  
    my $EF = owf(fpe($dir, qw(source xml)), <<END);
  <a id="F" conref="target2.xml"/>
  END
  
    my $E = Data::Edit::Xml::new($EF);
  
    $E->𝗱𝗶𝘁𝗮𝗥𝗲𝗽𝗹𝗮𝗰𝗲𝗔𝗻𝘆𝗖𝗼𝗻𝗿𝗲𝗳;
  
    ok -p $E eq <<END;
  <a id="F">
    <b id="B">
      <c/>
    </b>
  </a>
  END
  
    clearFolder($dir, 3);
  

=head3 ditaReplaceAnyConrefIdeallyWithMatchingTag($node, @context)

Replace a conref with the referenced content if we are in the optional context and the target of the conref can be located. If multiple possible targets exists, priority will be given to any that match the tag of $node. Returns the root of the parse tree containing the new content or B<undef> if no new content was located.

     Parameter  Description
  1  $node      Node with conref attribute
  2  @context   Optional context.

B<Example:>


    my $dir = temporaryFolder;
    my $target = Data::Edit::Xml::new(<<END);
  <a>
    <c id="b"/>
    <b id="b"/>
  </a>
  END
  
    owf(fpe($dir, qw(target xml)), -p $target);
  
    my $s = Data::Edit::Xml::new(q(<a conref="target.xml#a/b"/>));
    $s->inputFile = fpe($dir, qw(source xml));
  
    my $s1 = $s->clone->change_c;
       $s1->𝗱𝗶𝘁𝗮𝗥𝗲𝗽𝗹𝗮𝗰𝗲𝗔𝗻𝘆𝗖𝗼𝗻𝗿𝗲𝗳𝗜𝗱𝗲𝗮𝗹𝗹𝘆𝗪𝗶𝘁𝗵𝗠𝗮𝘁𝗰𝗵𝗶𝗻𝗴𝗧𝗮𝗴;
    ok -p $s1 eq <<END;
  <c id="b"/>
  END
  
    my  $s2 = $s->clone->change_b;
    ok !$s2->ditaReplaceAnyConrefInContext(qr(\Ac A\Z));
        $s2->ditaReplaceAnyConrefInContext(qr(\Ac a\Z));
    ok -p $s2 eq <<END;
  <c id="b"/>
  END
  
    my $b = ditaGetConRef("target.xml#a/b", $dir, qr(\Ab\Z));
    ok -p $b eq <<END;
  <b id="b"/>
  END
  
    my $c = ditaGetConRef("target.xml#a/b", $dir, qr(\Ac a\Z));
    ok -p $c eq <<END;
  <c id="b"/>
  END
  
    my $d = ditaGetConRef("target.xml#a/b", $dir, qr(\Ad\Z));
    ok -p $c eq <<END;
  <c id="b"/>
  END
  
    ok !ditaGetConRef("target.xml#a/b", $dir, qr(\A(d|e)\Z), 1);
  
    clearFolder($dir, 3);
  

=head3 ditaReplaceAnyConrefInContext($node, $context, @context)

Replace a conref with the referenced content if we are in the optional context and the target of the conref can be located and the L<context> of the target node matches the regular expression B<$context>. Returns the root of the parse tree containing the new content or B<undef> if no new content was located.

     Parameter  Description
  1  $node      Node with conref attribute
  2  $context   Tags to match
  3  @context   Optional context.

B<Example:>


    my $dir = temporaryFolder;
    my $target = Data::Edit::Xml::new(<<END);
  <a>
    <c id="b"/>
    <b id="b"/>
  </a>
  END
  
    owf(fpe($dir, qw(target xml)), -p $target);
  
    my $s = Data::Edit::Xml::new(q(<a conref="target.xml#a/b"/>));
    $s->inputFile = fpe($dir, qw(source xml));
  
    my $s1 = $s->clone->change_c;
       $s1->ditaReplaceAnyConrefIdeallyWithMatchingTag;
    ok -p $s1 eq <<END;
  <c id="b"/>
  END
  
    my  $s2 = $s->clone->change_b;
    ok !$s2->𝗱𝗶𝘁𝗮𝗥𝗲𝗽𝗹𝗮𝗰𝗲𝗔𝗻𝘆𝗖𝗼𝗻𝗿𝗲𝗳𝗜𝗻𝗖𝗼𝗻𝘁𝗲𝘅𝘁(qr(\Ac A\Z));
        $s2->𝗱𝗶𝘁𝗮𝗥𝗲𝗽𝗹𝗮𝗰𝗲𝗔𝗻𝘆𝗖𝗼𝗻𝗿𝗲𝗳𝗜𝗻𝗖𝗼𝗻𝘁𝗲𝘅𝘁(qr(\Ac a\Z));
    ok -p $s2 eq <<END;
  <c id="b"/>
  END
  
    my $b = ditaGetConRef("target.xml#a/b", $dir, qr(\Ab\Z));
    ok -p $b eq <<END;
  <b id="b"/>
  END
  
    my $c = ditaGetConRef("target.xml#a/b", $dir, qr(\Ac a\Z));
    ok -p $c eq <<END;
  <c id="b"/>
  END
  
    my $d = ditaGetConRef("target.xml#a/b", $dir, qr(\Ad\Z));
    ok -p $c eq <<END;
  <c id="b"/>
  END
  
    ok !ditaGetConRef("target.xml#a/b", $dir, qr(\A(d|e)\Z), 1);
  
    clearFolder($dir, 3);
  

=head3 ditaExpandAllConRefs($tree, $sourceFile)

Expand all the conrefs in the specified B<$parseTree> relative to the specified B<$sourceFile>.

     Parameter    Description
  1  $tree        Parse tree
  2  $sourceFile  Source file we are expanding in

B<Example:>


    my $dir = temporaryFolder;
    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b id="b">
      <c/>
    </b>
  </a>
  END
  
    my $target = fpe($dir, qw(target xml));
    owf($target, -p $a);
  
    my $b = ditaGetConRef("target.xml#a/b", $dir);
    ok -p $b eq <<END;
  <b id="b">
    <c/>
  </b>
  END
  
    my $A = Data::Edit::Xml::new(<<END);
  <a>
    <b id="B" conref="target.xml#a/b"/>
  </a>
  END
    my $target2 = fpe($dir, qw(target2 xml));
    owf($target2, -p $A);
  
    my $B = Data::Edit::Xml::new(<<END);
  <a>
    <b id="C" conref="target2.xml#a/B"/>
  </a>
  END
  
    $B->𝗱𝗶𝘁𝗮𝗘𝘅𝗽𝗮𝗻𝗱𝗔𝗹𝗹𝗖𝗼𝗻𝗥𝗲𝗳𝘀($dir);
    ok -p $B eq <<END;
  <a>
    <b id="C">
      <c/>
    </b>
  </a>
  END
  
    my $CF = owf(fpe($dir, qw(source xml)), <<END);
  <a>
    <b id="D" conref="target2.xml#a/B"/>
  </a>
  END
  
    my $C = Data::Edit::Xml::new($CF);
  
    $C->first__ditaReplaceAnyConref;
    ok -p $C eq <<END;
  <a>
    <b id="D">
      <c/>
    </b>
  </a>
  END
  
    my $DF = owf(fpe($dir, qw(source xml)), <<END);
  <a id="E" conref="target2.xml#a/B"/>
  END
  
    my $D = Data::Edit::Xml::new($DF);
  
    $D->ditaReplaceAnyConref;
    ok -p $D eq <<END;
  <b id="E">
    <c/>
  </b>
  END
  
    my $EF = owf(fpe($dir, qw(source xml)), <<END);
  <a id="F" conref="target2.xml"/>
  END
  
    my $E = Data::Edit::Xml::new($EF);
  
    $E->ditaReplaceAnyConref;
  
    ok -p $E eq <<END;
  <a id="F">
    <b id="B">
      <c/>
    </b>
  </a>
  END
  
    clearFolder($dir, 3);
  

=head3 ditaAbsoluteHref($node, $sourceFile)

Return the absolute value of the href of a specified B<$node> relative to the specified B<$sourceFile> or B<undef> if the node has no B<href> attribute.

     Parameter    Description
  1  $node        Node containing href
  2  $sourceFile  Source file

B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a href="bbb.txt">
  </a>
  END
  
    ok $a->𝗱𝗶𝘁𝗮𝗔𝗯𝘀𝗼𝗹𝘂𝘁𝗲𝗛𝗿𝗲𝗳(q(/home/aaa/)) eq q(/home/aaa/bbb.txt);
  

=head3 ditaListToChoices($list, @context)

Change the specified B<$list> to B<choices>.

     Parameter  Description
  1  $list      Node
  2  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <ol>
    <li>
      <p/>
    </li>
    <li>
      <p/>
    </li>
  </ol>
  END
  
    $a->𝗱𝗶𝘁𝗮𝗟𝗶𝘀𝘁𝗧𝗼𝗖𝗵𝗼𝗶𝗰𝗲𝘀;
    ok -p $a eq <<END;
  <choices>
    <choice>
      <p/>
    </choice>
    <choice>
      <p/>
    </choice>
  </choices>
  END
  

=head3 ditaListToSteps($list, @context)

Change the specified B<$node> to B<steps> and its contents to B<cmd\step> optionally only in the specified context.

     Parameter  Description
  1  $list      Node
  2  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    ok -p $a eq <<END;                                                              
  <dita>
    <ol>
      <li>
        <p>aaa</p>
      </li>
      <li>
        <p>bbb</p>
      </li>
    </ol>
  </dita>
  END
  
    $a->first->𝗱𝗶𝘁𝗮𝗟𝗶𝘀𝘁𝗧𝗼𝗦𝘁𝗲𝗽𝘀;                                                   
  
    ok -p $a eq <<END;                                                            
  <dita>
    <steps>
      <step>
        <cmd>aaa</cmd>
      </step>
      <step>
        <cmd>bbb</cmd>
      </step>
    </steps>
  </dita>
  END
  

=head3 ditaListToStepsUnordered($list, @context)

Change the specified B<$node> to B<steps-unordered> and its contents to B<cmd\step> optionally only in the specified context.

     Parameter  Description
  1  $list      Node
  2  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    ok -p $a eq <<END;                                                             
  <dita>
    <ol>
      <li>aaa</li>
      <li>bbb</li>
    </ol>
  </dita>
  END
  
    $a->first->𝗱𝗶𝘁𝗮𝗟𝗶𝘀𝘁𝗧𝗼𝗦𝘁𝗲𝗽𝘀𝗨𝗻𝗼𝗿𝗱𝗲𝗿𝗲𝗱;                                          
  
    ok -p $a eq <<END;                                                            
  <dita>
    <steps-unordered>
      <step>
        <cmd>aaa</cmd>
      </step>
      <step>
        <cmd>bbb</cmd>
      </step>
    </steps-unordered>
  </dita>
  END
  

=head3 ditaListToSubSteps($list, @context)

Change the specified B<$node> to B<substeps> and its contents to B<cmd\step> optionally only in the specified context.

     Parameter  Description
  1  $list      Node
  2  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


   {my $a = Data::Edit::Xml::new(<<END);                                          
  <dita>
    <ol>
      <li>aaa</li>
      <li>bbb</li>
    </ol>
  </dita>
  END
  
    $a->first->𝗱𝗶𝘁𝗮𝗟𝗶𝘀𝘁𝗧𝗼𝗦𝘂𝗯𝗦𝘁𝗲𝗽𝘀;                                                
  
    ok -p $a eq <<END;                                                            
  <dita>
    <substeps>
      <substep>
        <cmd>aaa</cmd>
      </substep>
      <substep>
        <cmd>bbb</cmd>
      </substep>
    </substeps>
  </dita>
  END
  

=head3 ditaStepsToList($steps, $tag, @context)

Change the specified B<$node> to a node with name B<$tag> or to B<ol> if B<$tag> is not supplied and its B<cmd\step> content to B<li> to create a list optionally only in the specified context.

     Parameter  Description
  1  $steps     Node
  2  $tag       New tag if not ol
  3  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    ok -p $a eq <<END;                                                              
  <dita>
    <ol>
      <li>
        <p>aaa</p>
      </li>
      <li>
        <p>bbb</p>
      </li>
    </ol>
  </dita>
  END
  
    $a->first->𝗱𝗶𝘁𝗮𝗦𝘁𝗲𝗽𝘀𝗧𝗼𝗟𝗶𝘀𝘁;                                                   
  
    ok -p $a eq <<END;                                                             
  <dita>
    <ol>
      <li>aaa</li>
      <li>bbb</li>
    </ol>
  </dita>
  END
  

=head3 ditaStepsToChoices($steps, @context)

Change the specified B<$node> to B<choices>.

     Parameter  Description
  1  $steps     Node
  2  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <steps>
    <step>
      <cmd>Command</cmd>
      <stepresult>
  Step result
  </stepresult>
      <stepxmp>
  Step example
  </stepxmp>
    </step>
    <step>
      <cmd>Command</cmd>
      <stepresult>
  Step result
  </stepresult>
      <stepxmp>
  Step example
  </stepxmp>
    </step>
  </steps>
  END
  
    $a->𝗱𝗶𝘁𝗮𝗦𝘁𝗲𝗽𝘀𝗧𝗼𝗖𝗵𝗼𝗶𝗰𝗲𝘀;
  
    ok -p $a eq <<END;
  <choices>
    <choice>
      <p>Command</p>
  Step result
  Step example
    </choice>
    <choice>
      <p>Command</p>
  Step result
  Step example
    </choice>
  </choices>
  END
  

=head3 ditaConvertSubStepsToSteps($substeps, @context)

Change the B<$substeps> to B<steps> and return the B<steps> on success or B<undef> on failure.

     Parameter  Description
  1  $substeps  Substeps
  2  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <substeps>
    <substep>
      <cmd>C1</cmd>
    </substep>
    <substep>
      <cmd>C2</cmd>
    </substep>
  </substeps>
  END
  
    $a->𝗱𝗶𝘁𝗮𝗖𝗼𝗻𝘃𝗲𝗿𝘁𝗦𝘂𝗯𝗦𝘁𝗲𝗽𝘀𝗧𝗼𝗦𝘁𝗲𝗽𝘀;
  
    ok -p $a eq <<END;
  <steps>
    <step>
      <cmd>C1</cmd>
    </step>
    <step>
      <cmd>C2</cmd>
    </step>
  </steps>
  END
  

=head3 ditaListToTable($node, @context)

Convert a list to a table in situ - as designed by MiM.

     Parameter  Description
  1  $node      List node
  2  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


  if (1)                                                                          
   {my $a = Data::Edit::Xml::new(<<END);
  <ul id="Table3" outputclass="fcRowList">
      <li>
          <p id="1">1111</p>
          <p id="2">2222</p>
          <p id="3">3333</p>
          <p id="4">4444</p>
      </li>
      <li>
          <p id="tableToListA1" outputclass="1">aaaa1111</p>
          <p id="tableToListA2" outputclass="1">aaaa2222</p>
          <p id="tableToListA3" outputclass="1">aaaa3333</p>
          <p id="tableToListA4" outputclass="1">aaaa4444</p>
      </li>
      <li>
          <p id="tableToListB1" outputclass="1">bbbb1111</p>
          <p id="tableToListB2" outputclass="1">bbbb2222</p>
          <p id="tableToListB3" outputclass="1">bbbb3333</p>
      </li>
      <li>
          <p id="tableToListC1" outputclass="1">cccc1111</p>
          <p id="tableToListC2" outputclass="1">cccc2222</p>
          <p id="tableToListC3" outputclass="1">cccc3333</p>
          <p id="tableToListC4" outputclass="1">cccc4444</p>
          <p id="tableToListC5" outputclass="1">cccc5555</p>
      </li>
  </ul>
  END
    $a->𝗱𝗶𝘁𝗮𝗟𝗶𝘀𝘁𝗧𝗼𝗧𝗮𝗯𝗹𝗲;
    ok nws(-p $a) eq nws(<<END)
  <table id="Table3" outputclass="fcRowList">
    <tgroup cols="5">
      <colspec colname="c1" colnum="1" colwidth="1*"/>
      <colspec colname="c2" colnum="2" colwidth="1*"/>
      <colspec colname="c3" colnum="3" colwidth="1*"/>
      <colspec colname="c4" colnum="4" colwidth="1*"/>
      <colspec colname="c5" colnum="5" colwidth="1*"/>
      <thead>
        <row>
          <entry/>
          <entry/>
          <entry/>
          <entry/>
          <entry/>
        </row>
      </thead>
      <tbody>
        <row>
          <entry id="1">1111</entry>
          <entry id="2">2222</entry>
          <entry id="3">3333</entry>
          <entry id="4">4444</entry>
        </row>
        <row>
          <entry id="tableToListA1" outputclass="1">aaaa1111</entry>
          <entry id="tableToListA2" outputclass="1">aaaa2222</entry>
          <entry id="tableToListA3" outputclass="1">aaaa3333</entry>
          <entry id="tableToListA4" outputclass="1">aaaa4444</entry>
        </row>
        <row>
          <entry id="tableToListB1" outputclass="1">bbbb1111</entry>
          <entry id="tableToListB2" outputclass="1">bbbb2222</entry>
          <entry id="tableToListB3" outputclass="1">bbbb3333</entry>
        </row>
        <row>
          <entry id="tableToListC1" outputclass="1">cccc1111</entry>
          <entry id="tableToListC2" outputclass="1">cccc2222</entry>
          <entry id="tableToListC3" outputclass="1">cccc3333</entry>
          <entry id="tableToListC4" outputclass="1">cccc4444</entry>
          <entry id="tableToListC5" outputclass="1">cccc5555</entry>
        </row>
      </tbody>
    </tgroup>
  </table>
  END
   }
  

=head3 ditaMergeLists($node, @context)

Merge the specified B<$node> with the preceding or following list or steps or substeps if possible and return the specified B<$node> regardless.

     Parameter  Description
  1  $node      Node
  2  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


   {my $a = Data::Edit::Xml::new(<<END);                                          
  <a>
    <li id="1"/>
    <ol/>
    <ol>
      <li id="2"/>
      <li id="3"/>
    </ol>
  </a>
  END
  
    $a x= sub{$_->𝗱𝗶𝘁𝗮𝗠𝗲𝗿𝗴𝗲𝗟𝗶𝘀𝘁𝘀};                                                
  
    ok -p $a eq <<END;                                                            
  <a>
    <ol>
      <li id="1"/>
      <li id="2"/>
      <li id="3"/>
    </ol>
  </a>
  END
  

=head3 mergeLikeElements($node, @context)

Merge two of the same elements into one, retaining the order of any children. Return the original B<$node> if the request succeeds, else return B<undef>.

     Parameter  Description
  1  $node      Node
  2  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <x><a>aa</a><b>bb</b><b>cc</b><b>dd</b><b>ee</b></x>
  END
  
    ok -p $a eq <<END;
  <x>
    <a>aa</a>
    <b>bb</b>
    <b>cc</b>
    <b>dd</b>
    <b>ee</b>
  </x>
  END
  
    $a->by(sub{$_->𝗺𝗲𝗿𝗴𝗲𝗟𝗶𝗸𝗲𝗘𝗹𝗲𝗺𝗲𝗻𝘁𝘀});
    ok -p $a eq <<END;
  <x>
    <a>aa</a>
    <b>bb cc dd ee</b>
  </x>
  END
  
  

=head3 mergeLikeNext($node, @context)

Merge a B<$node> in an optional context with the next node if the two have the same tag by placing the next node first in the current B<$node> and unwrapping the next node. Return B<undef> if the request fails else the current B<$node>. Identical to L<mergeLikeElements|/mergeLikeElements>

     Parameter  Description
  1  $node      Node
  2  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b>bb</b>
    <b>dd</b>
  </a>
  END
  
    $a->first->𝗺𝗲𝗿𝗴𝗲𝗟𝗶𝗸𝗲𝗡𝗲𝘅𝘁;
  
    ok -p $a eq <<END;
  <a>
    <b>bb dd</b>
  </a>
  END
  

B<mln> is a synonym for L<mergeLikeNext|/mergeLikeNext>.


=head3 mergeLikePrev($node, @context)

Merge a B<$node> in an optional context with the previous node if the two have the same tag by placing the previous node first in the current B<$node> and unwrapping the previous node. Return B<undef> if the request fails else return the specified B<$node>.

     Parameter  Description
  1  $node      Node
  2  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b>bb</b>
    <b>dd</b>
  </a>
  END
  
    $a->last->𝗺𝗲𝗿𝗴𝗲𝗟𝗶𝗸𝗲𝗣𝗿𝗲𝘃;
  
    ok -p $a eq <<END;
  <a>
    <b>bb dd</b>
  </a>
  END
  

B<mlp> is a synonym for L<mergeLikePrev|/mergeLikePrev>.


=head3 mergeOnlyChildLikeNext($node, @context)

Merge a B<$node> if it is the only child of its parent with a preceding node of the same name that is also the only child of its parent and return the specified B<$node> or B<undef> if the request fails.

     Parameter  Description
  1  $node      Node
  2  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b id="1">
       <c/>
    </b>
    <b id="2">
       <c/>
    </b>
  </a>
  END
  
    $a->go_b_c__mergeOnlyChildLikeNext;
    ok -p $a eq <<END;
  <a>
    <b id="1">
      <c/>
    </b>
  </a>
  END
  

B<mocln> is a synonym for L<mergeOnlyChildLikeNext|/mergeOnlyChildLikeNext>.


=head3 mergeOnlyChildLikePrev($node, @context)

Merge a B<$node> if it is the only child of its parent with a preceding node of the same name that is also the only child of its parent and return the specified B<$node> or B<undef> if the request fails.

     Parameter  Description
  1  $node      Node
  2  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b id="1">
       <c/>
    </b>
    <b id="2">
       <c/>
    </b>
  </a>
  END
  
    $a->go_b_1_c__mergeOnlyChildLikePrev;
    ok -p $a eq <<END;
  <a>
    <b id="2">
      <c/>
    </b>
  </a>
  END
  

B<moclp> is a synonym for L<mergeOnlyChildLikePrev|/mergeOnlyChildLikePrev>.


=head3 mergeOnlyChildLikePrevLast($node, @context)

Merge a B<$node> if it is the only child of its parent with a preceding node with the same tag that is the last child of its parent and return the previous B<$node> or B<undef> if the request fails.

     Parameter  Description
  1  $node      Node
  2  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b><c/><d>1</d></b>
    <b>    <d>2</d></b>
  </a>
  END
  
    my $c = $a->last__last__mergeOnlyChildLikePrevLast;
    ok -p $a eq <<END;
  <a>
    <b>
      <c/>
      <d>1 2</d>
    </b>
  </a>
  END
  
    ok -p $c eq <<END
  <d>1 2</d>
  END
  

=head3 ditaMaximumNumberOfEntriesInATGroupRow($tgroup)

Return the maximum number of entries in the rows of the specified B<$table> or B<undef> if not a table.

     Parameter  Description
  1  $tgroup    TGroup node

B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <table>
    <tgroup cols="1">
      <colspec/>
      <thead>
        <row>
          <entry>aaaa</entry>
          <entry/>
        </row>
        <row>
          <entry>bbbb</entry>
        </row>
      </thead>
      <tbody>
        <row>
          <entry>cccc</entry>
          <entry/>
          <entry/>
        </row>
        <row>
          <entry>dddd</entry>
        </row>
      </tbody>
    </tgroup>
  </table>
  END
  
    my $g = $a->go_tgroup;
  
    ok $g->𝗱𝗶𝘁𝗮𝗠𝗮𝘅𝗶𝗺𝘂𝗺𝗡𝘂𝗺𝗯𝗲𝗿𝗢𝗳𝗘𝗻𝘁𝗿𝗶𝗲𝘀𝗜𝗻𝗔𝗧𝗚𝗿𝗼𝘂𝗽𝗥𝗼𝘄 == 3;
  
    is_deeply $g->ditaTGroupStatistics,
  bless({
    colsAttribute => 1,
    colSpec => 1,
    maxBody => 3,
    maxBodyMinusPadding => 3,
    maxHead => 2,
    maxHeadMinusPadding => 2,
    minBody => 1,
    minHead => 1,
    rows    => 4,
  }, "Data::Edit::Xml::Table::Statistics");
  
    $g->ditaRemoveTGroupTrailingEmptyEntries;
  
    ok -p $a eq <<END;
  <table>
    <tgroup cols="1">
      <colspec/>
      <thead>
        <row>
          <entry>aaaa</entry>
        </row>
        <row>
          <entry>bbbb</entry>
        </row>
      </thead>
      <tbody>
        <row>
          <entry>cccc</entry>
        </row>
        <row>
          <entry>dddd</entry>
        </row>
      </tbody>
    </tgroup>
  </table>
  END
  

=head3 ditaTGroupStatistics($tgroup)

Return statistics about the rows in a given table

     Parameter  Description
  1  $tgroup    Table group node

B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <table>
    <tgroup cols="1">
      <colspec/>
      <thead>
        <row>
          <entry>aaaa</entry>
          <entry/>
        </row>
        <row>
          <entry>bbbb</entry>
        </row>
      </thead>
      <tbody>
        <row>
          <entry>cccc</entry>
          <entry/>
          <entry/>
        </row>
        <row>
          <entry>dddd</entry>
        </row>
      </tbody>
    </tgroup>
  </table>
  END
  
    my $g = $a->go_tgroup;
  
    ok $g->ditaMaximumNumberOfEntriesInATGroupRow == 3;
  
    is_deeply $g->𝗱𝗶𝘁𝗮𝗧𝗚𝗿𝗼𝘂𝗽𝗦𝘁𝗮𝘁𝗶𝘀𝘁𝗶𝗰𝘀,
  bless({
    colsAttribute => 1,
    colSpec => 1,
    maxBody => 3,
    maxBodyMinusPadding => 3,
    maxHead => 2,
    maxHeadMinusPadding => 2,
    minBody => 1,
    minHead => 1,
    rows    => 4,
  }, "Data::Edit::Xml::Table::Statistics");
  
    $g->ditaRemoveTGroupTrailingEmptyEntries;
  
    ok -p $a eq <<END;
  <table>
    <tgroup cols="1">
      <colspec/>
      <thead>
        <row>
          <entry>aaaa</entry>
        </row>
        <row>
          <entry>bbbb</entry>
        </row>
      </thead>
      <tbody>
        <row>
          <entry>cccc</entry>
        </row>
        <row>
          <entry>dddd</entry>
        </row>
      </tbody>
    </tgroup>
  </table>
  END
  

=head3 ditaAddColSpecToTGroup($tgroup, $number)

Add the specified B<$number> of column specification to a specified B<$tgroup> which does not have any already.

     Parameter  Description
  1  $tgroup    Tgroup node
  2  $number    Number of colspecs to add

B<Example:>


  if (1)                                                                           
   {my $a = Data::Edit::Xml::new(<<END);
  <table>
    <tgroup>
      <tbody>
        <row><entry/></row>
        <row><entry/><entry/></row>
        <row><entry/><entry/><entry/></row>
        <row><entry/><entry/></row>
        <row/>
      </tbody>
    </tgroup>
  </table>
  END
  
    ok 3 == $a->go_tgroup->ditaMaximumNumberOfEntriesInATGroupRow;
    $a->first->𝗱𝗶𝘁𝗮𝗔𝗱𝗱𝗖𝗼𝗹𝗦𝗽𝗲𝗰𝗧𝗼𝗧𝗚𝗿𝗼𝘂𝗽(3);
  
    ok -p $a eq <<END
  <table>
    <tgroup cols="3">
      <colspec colname="c1" colnum="1" colwidth="1*"/>
      <colspec colname="c2" colnum="2" colwidth="1*"/>
      <colspec colname="c3" colnum="3" colwidth="1*"/>
      <tbody>
        <row>
          <entry/>
        </row>
        <row>
          <entry/>
          <entry/>
        </row>
        <row>
          <entry/>
          <entry/>
          <entry/>
        </row>
        <row>
          <entry/>
          <entry/>
        </row>
        <row/>
      </tbody>
    </tgroup>
  </table>
  END
   }
  

=head3 ditaFixTGroupColSpec($tgroup)

Fix the colspec attribute and colspec nodes of the specified B<$tgroup>.

     Parameter  Description
  1  $tgroup    Tgroup node

B<Example:>


  if (1)                                                                          
   {my $a = Data::Edit::Xml::new(<<END);
  <table>
    <tgroup>
      <tbody>
        <row><entry/></row>
        <row><entry/><entry/></row>
        <row><entry/><entry/><entry/></row>
        <row><entry/><entry/></row>
        <row>
          <entry>
            <table>
              <tbody>
                <row><entry/><entry/><entry/><entry/><entry/><entry/><entry/></row>
              </tbody>
            </table>
          </entry>
        </row>
     </tbody>
   </tgroup>
  </table>
  END
  
    $a->go_tgroup->𝗱𝗶𝘁𝗮𝗙𝗶𝘅𝗧𝗚𝗿𝗼𝘂𝗽𝗖𝗼𝗹𝗦𝗽𝗲𝗰;
  
    ok -p $a eq <<END
  <table>
    <tgroup cols="3">
      <colspec colname="c1" colnum="1" colwidth="1*"/>
      <colspec colname="c2" colnum="2" colwidth="1*"/>
      <colspec colname="c3" colnum="3" colwidth="1*"/>
      <tbody>
        <row>
          <entry/>
        </row>
        <row>
          <entry/>
          <entry/>
        </row>
        <row>
          <entry/>
          <entry/>
          <entry/>
        </row>
        <row>
          <entry/>
          <entry/>
        </row>
        <row>
          <entry>
            <table>
              <tbody>
                <row>
                  <entry/>
                  <entry/>
                  <entry/>
                  <entry/>
                  <entry/>
                  <entry/>
                  <entry/>
                </row>
              </tbody>
            </table>
          </entry>
        </row>
      </tbody>
    </tgroup>
  </table>
  END
   }
  

=head3 ditaRemoveTGroupTrailingEmptyEntries($tgroup)

Remove empty trailing entry

     Parameter  Description
  1  $tgroup    Table node

B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <table>
    <tgroup cols="1">
      <colspec/>
      <thead>
        <row>
          <entry>aaaa</entry>
          <entry/>
        </row>
        <row>
          <entry>bbbb</entry>
        </row>
      </thead>
      <tbody>
        <row>
          <entry>cccc</entry>
          <entry/>
          <entry/>
        </row>
        <row>
          <entry>dddd</entry>
        </row>
      </tbody>
    </tgroup>
  </table>
  END
  
    my $g = $a->go_tgroup;
  
    ok $g->ditaMaximumNumberOfEntriesInATGroupRow == 3;
  
    is_deeply $g->ditaTGroupStatistics,
  bless({
    colsAttribute => 1,
    colSpec => 1,
    maxBody => 3,
    maxBodyMinusPadding => 3,
    maxHead => 2,
    maxHeadMinusPadding => 2,
    minBody => 1,
    minHead => 1,
    rows    => 4,
  }, "Data::Edit::Xml::Table::Statistics");
  
    $g->𝗱𝗶𝘁𝗮𝗥𝗲𝗺𝗼𝘃𝗲𝗧𝗚𝗿𝗼𝘂𝗽𝗧𝗿𝗮𝗶𝗹𝗶𝗻𝗴𝗘𝗺𝗽𝘁𝘆𝗘𝗻𝘁𝗿𝗶𝗲𝘀;
  
    ok -p $a eq <<END;
  <table>
    <tgroup cols="1">
      <colspec/>
      <thead>
        <row>
          <entry>aaaa</entry>
        </row>
        <row>
          <entry>bbbb</entry>
        </row>
      </thead>
      <tbody>
        <row>
          <entry>cccc</entry>
        </row>
        <row>
          <entry>dddd</entry>
        </row>
      </tbody>
    </tgroup>
  </table>
  END
  

=head3 fixTGroup($tgroup)

Fix the specified B<$tgroup> so that each row has the same number of entries with this number reflected in the tgroup.cols= attribute and colspec nodes.

     Parameter  Description
  1  $tgroup    TGroup node

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <table>
    <tgroup cols="1">
      <colspec/>
      <thead>
        <row>
          <entry>aaaa</entry>
        </row>
        <row>
          <entry>bbbb</entry>
        </row>
      </thead>
      <tbody>
        <row>
          <entry>cccc</entry>
        </row>
        <row>
          <entry>dddd</entry>
        </row>
      </tbody>
    </tgroup>
  </table>
  END
  
    my $g = $a->go_tgroup;
  
    $g->ditaAddPadEntriesToTGroupRows(2);
  
    is_deeply $g->ditaTGroupStatistics,
  bless({
    colsAttribute => 1,
    colSpec => 1,
    maxBody => 2,
    maxBodyMinusPadding => 2,
    maxHead => 2,
    maxHeadMinusPadding => 2,
    minBody => 2,
    minHead => 2,
    rows    => 4,
  }, "Data::Edit::Xml::Table::Statistics");
  
    $g->𝗳𝗶𝘅𝗧𝗚𝗿𝗼𝘂𝗽;
  
    ok -p $a eq <<END;
  <table>
    <tgroup cols="2">
      <colspec colname="c1" colnum="1" colwidth="1*"/>
      <colspec colname="c2" colnum="2" colwidth="1*"/>
      <thead>
        <row>
          <entry>aaaa</entry>
          <entry/>
        </row>
        <row>
          <entry>bbbb</entry>
          <entry/>
        </row>
      </thead>
      <tbody>
        <row>
          <entry>cccc</entry>
          <entry/>
        </row>
        <row>
          <entry>dddd</entry>
          <entry/>
        </row>
      </tbody>
    </tgroup>
  </table>
  END
  
    is_deeply $g->ditaTGroupStatistics,
  bless({
    colsAttribute => 2,
    colSpec => 2,
    maxBody => 2,
    maxBodyMinusPadding => 2,
    maxHead => 2,
    maxHeadMinusPadding => 2,
    minBody => 2,
    minHead => 2,
    rows    => 4,
  
  }, "Data::Edit::Xml::Table::Statistics");
  
    $a->fixTable;
    is_deeply $g->ditaTGroupStatistics,
  bless({
    colsAttribute => 2,
    colSpec => 2,
    maxBody => 2,
    maxBodyMinusPadding => 2,
    maxHead => 2,
    maxHeadMinusPadding => 2,
    minBody => 2,
    minHead => 2,
    rows    => 4,
  }, "Data::Edit::Xml::Table::Statistics");
  
  

=head3 fixTable($table)

Fix the specified B<$table> so that each row has the same number of entries with this number reflected in the tgroup.cols= attribute and colspec nodes.

     Parameter  Description
  1  $table     Table node

B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <table>
    <tgroup cols="1">
      <colspec/>
      <thead>
        <row>
          <entry>aaaa</entry>
        </row>
        <row>
          <entry>bbbb</entry>
        </row>
      </thead>
      <tbody>
        <row>
          <entry>cccc</entry>
        </row>
        <row>
          <entry>dddd</entry>
        </row>
      </tbody>
    </tgroup>
  </table>
  END
  
    my $g = $a->go_tgroup;
  
    $g->ditaAddPadEntriesToTGroupRows(2);
  
    is_deeply $g->ditaTGroupStatistics,
  bless({
    colsAttribute => 1,
    colSpec => 1,
    maxBody => 2,
    maxBodyMinusPadding => 2,
    maxHead => 2,
    maxHeadMinusPadding => 2,
    minBody => 2,
    minHead => 2,
    rows    => 4,
  }, "Data::Edit::Xml::Table::Statistics");
  
    $g->fixTGroup;
  
    ok -p $a eq <<END;
  <table>
    <tgroup cols="2">
      <colspec colname="c1" colnum="1" colwidth="1*"/>
      <colspec colname="c2" colnum="2" colwidth="1*"/>
      <thead>
        <row>
          <entry>aaaa</entry>
          <entry/>
        </row>
        <row>
          <entry>bbbb</entry>
          <entry/>
        </row>
      </thead>
      <tbody>
        <row>
          <entry>cccc</entry>
          <entry/>
        </row>
        <row>
          <entry>dddd</entry>
          <entry/>
        </row>
      </tbody>
    </tgroup>
  </table>
  END
  
    is_deeply $g->ditaTGroupStatistics,
  bless({
    colsAttribute => 2,
    colSpec => 2,
    maxBody => 2,
    maxBodyMinusPadding => 2,
    maxHead => 2,
    maxHeadMinusPadding => 2,
    minBody => 2,
    minHead => 2,
    rows    => 4,
  
  }, "Data::Edit::Xml::Table::Statistics");
  
    $a->𝗳𝗶𝘅𝗧𝗮𝗯𝗹𝗲;
    is_deeply $g->ditaTGroupStatistics,
  bless({
    colsAttribute => 2,
    colSpec => 2,
    maxBody => 2,
    maxBodyMinusPadding => 2,
    maxHead => 2,
    maxHeadMinusPadding => 2,
    minBody => 2,
    minHead => 2,
    rows    => 4,
  }, "Data::Edit::Xml::Table::Statistics");
  
  

=head3 fixEntryColSpan($entry)

Fix the colspan on an entry assumed to be under row, tbody, tgroup with @cols and colspecs' set

     Parameter  Description
  1  $entry     Entry

B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <table>
    <tgroup cols="7">
      <colspec colname="a"/>
      <colspec colname="b"/>
      <colspec colname="c"/>
      <colspec colname="d"/>
      <colspec colname="e"/>
      <colspec colname="f"/>
      <colspec colname="g"/>
      <tbody>
        <row>
          <entry colspan="7"/>
        </row>
        <row>
          <entry colspan="2"/>
          <entry colspan="3"/>
          <entry colspan="2"/>
        </row>
        <row>
          <entry/>
          <entry rowspan="2"/>
          <entry colspan="2"/>
          <entry colspan="2"/>
          <entry/>
        </row>
      </tbody>
    </tgroup>
  </table>
  END
  
    $a->by(sub
     {my ($e) = @_;
      if ($e->at_entry)
       {$e->𝗳𝗶𝘅𝗘𝗻𝘁𝗿𝘆𝗖𝗼𝗹𝗦𝗽𝗮𝗻;
        $e->fixEntryRowSpan;
       }
     });
  
    ok -p $a eq <<END;
  <table>
    <tgroup cols="7">
      <colspec colname="a"/>
      <colspec colname="b"/>
      <colspec colname="c"/>
      <colspec colname="d"/>
      <colspec colname="e"/>
      <colspec colname="f"/>
      <colspec colname="g"/>
      <tbody>
        <row>
          <entry nameend="g" namest="a"/>
        </row>
        <row>
          <entry nameend="b" namest="a"/>
          <entry nameend="e" namest="c"/>
          <entry nameend="g" namest="f"/>
        </row>
        <row>
          <entry/>
          <entry morerows="1"/>
          <entry nameend="d" namest="c"/>
          <entry nameend="f" namest="e"/>
          <entry/>
        </row>
      </tbody>
    </tgroup>
  </table>
  END
  

=head3 fixEntryRowSpan($entry)

Fix the rowspan on an entry

     Parameter  Description
  1  $entry     Entry

B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <table>
    <tgroup cols="7">
      <colspec colname="a"/>
      <colspec colname="b"/>
      <colspec colname="c"/>
      <colspec colname="d"/>
      <colspec colname="e"/>
      <colspec colname="f"/>
      <colspec colname="g"/>
      <tbody>
        <row>
          <entry colspan="7"/>
        </row>
        <row>
          <entry colspan="2"/>
          <entry colspan="3"/>
          <entry colspan="2"/>
        </row>
        <row>
          <entry/>
          <entry rowspan="2"/>
          <entry colspan="2"/>
          <entry colspan="2"/>
          <entry/>
        </row>
      </tbody>
    </tgroup>
  </table>
  END
  
    $a->by(sub
     {my ($e) = @_;
      if ($e->at_entry)
       {$e->fixEntryColSpan;
        $e->𝗳𝗶𝘅𝗘𝗻𝘁𝗿𝘆𝗥𝗼𝘄𝗦𝗽𝗮𝗻;
       }
     });
  
    ok -p $a eq <<END;
  <table>
    <tgroup cols="7">
      <colspec colname="a"/>
      <colspec colname="b"/>
      <colspec colname="c"/>
      <colspec colname="d"/>
      <colspec colname="e"/>
      <colspec colname="f"/>
      <colspec colname="g"/>
      <tbody>
        <row>
          <entry nameend="g" namest="a"/>
        </row>
        <row>
          <entry nameend="b" namest="a"/>
          <entry nameend="e" namest="c"/>
          <entry nameend="g" namest="f"/>
        </row>
        <row>
          <entry/>
          <entry morerows="1"/>
          <entry nameend="d" namest="c"/>
          <entry nameend="f" namest="e"/>
          <entry/>
        </row>
      </tbody>
    </tgroup>
  </table>
  END
  

=head3 ditaConvertDlToUl($dl)

Convert a L<Dita|http://docs.oasis-open.org/dita/dita/v1.3/os/part2-tech-content/dita-v1.3-os-part2-tech-content.html> B<$dl> to a B<ul> if each B<dlentry> has only B<dt> or B<dl> elements but not both.  Return B<undef> if such a conversion is not possible else return the new B<ul> node.

     Parameter  Description
  1  $dl        Dl

B<Example:>


  
    my $a = Data::Edit::Xml::new(<<END);
  <dl>
    <dlentry><dt>A</dt><dt>B</dt><dt>C</dt></dlentry>
    <dlentry><dd>a</dd><dd>b</dd><dd>b</dd></dlentry>
  </dl>
  END
  
    $a->𝗱𝗶𝘁𝗮𝗖𝗼𝗻𝘃𝗲𝗿𝘁𝗗𝗹𝗧𝗼𝗨𝗹;
  
    ok -p $a eq <<END;
  <ul>
    <li>A</li>
    <li>B</li>
    <li>C</li>
    <li>a</li>
    <li>b</li>
    <li>b</li>
  </ul>
  END
  
    my $b = Data::Edit::Xml::new(<<END);
  <dl>
    <dlentry><dt>A</dt><dd>a</dd></dlentry>
  </dl>
  END
  
    ok !$a->𝗱𝗶𝘁𝗮𝗖𝗼𝗻𝘃𝗲𝗿𝘁𝗗𝗹𝗧𝗼𝗨𝗹;
  

=head3 ditaConvertOlToSubSteps($ol, @context)

Convert a L<Dita|http://docs.oasis-open.org/dita/dita/v1.3/os/part2-tech-content/dita-v1.3-os-part2-tech-content.html> B<$ul> to B<substeps> else B<undef> if this is not possible.

     Parameter  Description
  1  $ol        Ul
  2  @context   Context

B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <ol>
    <li>A</li>
    <li>B</li>
    <li>C</li>
  </ol>
  END
  
    $a->𝗱𝗶𝘁𝗮𝗖𝗼𝗻𝘃𝗲𝗿𝘁𝗢𝗹𝗧𝗼𝗦𝘂𝗯𝗦𝘁𝗲𝗽𝘀;
  
    ok -p $a eq <<END;
  <substeps>
    <substep>
      <cmd>A</cmd>
    </substep>
    <substep>
      <cmd>B</cmd>
    </substep>
    <substep>
      <cmd>C</cmd>
    </substep>
  </substeps>
  END
  

=head3 ditaConvertUlToSubSteps($ul, @context)

Convert a L<Dita|http://docs.oasis-open.org/dita/dita/v1.3/os/part2-tech-content/dita-v1.3-os-part2-tech-content.html> B<$ol> to B<substeps> else B<undef> if this is not possible.

     Parameter  Description
  1  $ul        Ul
  2  @context   Context

B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <ul>
    <li>A</li>
    <li>B</li>
    <li>C</li>
  </ul>
  END
  
    $a->𝗱𝗶𝘁𝗮𝗖𝗼𝗻𝘃𝗲𝗿𝘁𝗨𝗹𝗧𝗼𝗦𝘂𝗯𝗦𝘁𝗲𝗽𝘀;
  
    ok -p $a eq <<END;
  <substeps>
    <substep>
      <cmd>A</cmd>
    </substep>
    <substep>
      <cmd>B</cmd>
    </substep>
    <substep>
      <cmd>C</cmd>
    </substep>
  </substeps>
  END
  

=head3 ditaConvertFromHtmlDl($dl)

Convert a B<Html> B<$dl> to a L<Dita|http://docs.oasis-open.org/dita/dita/v1.3/os/part2-tech-content/dita-v1.3-os-part2-tech-content.html> B<dl> or return B<undef> if this is not possible.

     Parameter  Description
  1  $dl        Dl

B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <dl>
    <dt>t11</dt>
    <dd>d11</dd>
    <dd>d12</dd>
    <dt>t21</dt>
    <dd>d21</dd>
    <dd>d22</dd>
    <dt>t31</dt>
    <dd>d31</dd>
    <dd>d32</dd>
  </dl>
  END
  
    $a->𝗱𝗶𝘁𝗮𝗖𝗼𝗻𝘃𝗲𝗿𝘁𝗙𝗿𝗼𝗺𝗛𝘁𝗺𝗹𝗗𝗹;
  
    ok -p $a eq <<END;
  <dl>
    <dlentry>
      <dt>t11</dt>
      <dd>d11</dd>
      <dd>d12</dd>
    </dlentry>
    <dlentry>
      <dt>t21</dt>
      <dd>d21</dd>
      <dd>d22</dd>
    </dlentry>
    <dlentry>
      <dt>t31</dt>
      <dd>d31</dd>
      <dd>d32</dd>
    </dlentry>
  </dl>
  END
  

=head3 ditaConvertSimpleTableToTable($simpleTable)

Convert a L<Dita|http://docs.oasis-open.org/dita/dita/v1.3/os/part2-tech-content/dita-v1.3-os-part2-tech-content.html> B<simpletable> to a B<table>.

     Parameter     Description
  1  $simpleTable  Simple table

B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
      <simpletable>
        <sthead>
          <stentry>Symbol</stentry>
          <stentry>Meaning</stentry>
        </sthead>
        <strow>
          <stentry>aaaa</stentry>
          <stentry>bbbb</stentry>
        </strow>
      </simpletable>
  END
  
    $a->𝗱𝗶𝘁𝗮𝗖𝗼𝗻𝘃𝗲𝗿𝘁𝗦𝗶𝗺𝗽𝗹𝗲𝗧𝗮𝗯𝗹𝗲𝗧𝗼𝗧𝗮𝗯𝗹𝗲;
  
    ok -p $a eq <<END;
  <table>
    <tgroup cols="2">
      <colspec colname="c1" colnum="1" colwidth="1*"/>
      <colspec colname="c2" colnum="2" colwidth="1*"/>
      <thead>
        <row>
          <entry>Symbol</entry>
          <entry>Meaning</entry>
        </row>
      </thead>
      <tbody>
        <row>
          <entry>aaaa</entry>
          <entry>bbbb</entry>
        </row>
      </tbody>
    </tgroup>
  </table>
  END
  

=head3 ditaCouldConvertConceptToTask($concept)

Check whether a concept could be converted to a task

     Parameter  Description
  1  $concept   Concept to check

B<Example:>


    my $c = Data::Edit::Xml::new(<<END);
  <concept id="c1">
    <title>Title Text</title>
    <conbody>
      <p>Concept 11</p>
      <section>
        <p>A section 11</p>
      </section>
      <section>
        <p>A section 22</p>
      </section>
      <p>Concept 222</p>
    </conbody>
  </concept>
  END
  
    my $r = $c->ditaConvertConceptToReference;
  
    ok -p $c eq <<END;
  <reference id="c1">
    <title>Title Text</title>
    <refbody>
      <section>
        <p>Concept 11</p>
      </section>
      <section>
        <p>A section 11</p>
      </section>
      <section>
        <p>A section 22</p>
      </section>
      <section>
        <p>Concept 222</p>
      </section>
    </refbody>
  </reference>
  END
  
    my $C = $c->ditaConvertReferenceToConcept;
    ok -p $C eq <<END;
  <concept id="c1">
    <title>Title Text</title>
    <conbody>
      <p>Concept 11</p>
      <p>A section 11</p>
      <p>A section 22</p>
      <p>Concept 222</p>
    </conbody>
  </concept>
  END
  
    ok !$C->𝗱𝗶𝘁𝗮𝗖𝗼𝘂𝗹𝗱𝗖𝗼𝗻𝘃𝗲𝗿𝘁𝗖𝗼𝗻𝗰𝗲𝗽𝘁𝗧𝗼𝗧𝗮𝘀𝗸;
  
    my $ref = Data::Edit::Xml::new(<<END);
  <reference id="GUID-0cab6472-6f2a-e46a-9b6d-16649416ab53">
      <title>Testing the S3 Feature for PM 4 1</title>
      <refbody>
          <section>
              <p>To test the S# feature:</p>
              <ol>
                  <li>
                      <p>Set the PCIe link state to L1.2.</p>
                  </li>
                  <li>
                      <p>Issue the command: PM 4 1 command.</p>
                  </li>
              </ol>
          </section>
      </refbody>
  </reference>
  END
  
    my $concept = $ref->ditaConvertReferenceToConcept;
  
    ok -p $concept eq <<END;
  <concept id="GUID-0cab6472-6f2a-e46a-9b6d-16649416ab53">
    <title>Testing the S3 Feature for PM 4 1</title>
    <conbody>
      <p>To test the S# feature:</p>
      <ol>
        <li>
          <p>Set the PCIe link state to L1.2.</p>
        </li>
        <li>
          <p>Issue the command: PM 4 1 command.</p>
        </li>
      </ol>
    </conbody>
  </concept>
  END
  
    ok $concept->𝗱𝗶𝘁𝗮𝗖𝗼𝘂𝗹𝗱𝗖𝗼𝗻𝘃𝗲𝗿𝘁𝗖𝗼𝗻𝗰𝗲𝗽𝘁𝗧𝗼𝗧𝗮𝘀𝗸;
    my $task = $concept->ditaConvertConceptToTask;
  
    ok -p $task eq <<END
  <task id="GUID-0cab6472-6f2a-e46a-9b6d-16649416ab53">
    <title>Testing the S3 Feature for PM 4 1</title>
    <taskbody>
      <context>
        <p>To test the S# feature:</p>
      </context>
      <steps>
        <step>
          <cmd>Set the PCIe link state to L1.2.</cmd>
        </step>
        <step>
          <cmd>Issue the command: PM 4 1 command.</cmd>
        </step>
      </steps>
      <result/>
    </taskbody>
  </task>
  END
  

=head3 ditaConvertConceptToTask($node, @context)

Convert a Dita B<concept> to a B<task> by representing B<ol> as B<steps>. Return B<undef> if the conversion is not possible because there are no B<ol> else return the specified B<$concept> as as B<task>.

     Parameter  Description
  1  $node      Node
  2  @context   Optional context

B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <concept id="x1">
    <title/>
    <conbody>
      <p>context</p>
      <ol>
        <li> <note>note</note><image/><fig/> <p>cmd</p>  <p>info</p>  </li>
        <li>cmd text<p>info</p>  </li>
      </ol>
      <p>between</p>
      <ol>
        <li>
          <p>cmd <note>first in info</note><note>second in info</note></p>
          <p>info</p>
        </li>
        <li> cmd text <p>info</p> </li>
        <li>
          <ol> <li>substep 1</li> <li>substep 2</li> </ol>
        </li>
      </ol>
      <p>results</p>
    </conbody>
  </concept>
  END
  
   $a->𝗱𝗶𝘁𝗮𝗖𝗼𝗻𝘃𝗲𝗿𝘁𝗖𝗼𝗻𝗰𝗲𝗽𝘁𝗧𝗼𝗧𝗮𝘀𝗸;
  
   ok nws(-p $a) eq nws(<<END);
  <task id="x1">
    <title/>
    <taskbody>
      <context>
        <p>context</p>
      </context>
      <steps>
        <step>
          <note>note</note>
          <note>
            <image/>
          </note>
          <note>
            <fig/>
          </note>
          <cmd>cmd</cmd>
          <info>
            <p>info</p>
          </info>
        </step>
        <step>
          <cmd>cmd text</cmd>
          <info>
            <p>info</p>
          </info>
        </step>
        <stepsection>
          <p>between</p>
        </stepsection>
        <step>
          <cmd>cmd </cmd>
          <info>
            <note>first in info</note>
            <note>second in info</note>
            <p>info</p>
          </info>
        </step>
        <step>
          <cmd> cmd text </cmd>
          <info>
            <p>info</p>
          </info>
        </step>
        <step>
          <cmd>Choose one of the following:</cmd>
          <choices>
            <choice>substep 1</choice>
            <choice>substep 2</choice>
          </choices>
        </step>
      </steps>
      <result>
        <p>results</p>
      </result>
  
    </taskbody>
  </task>
  END
  
    my $ref = Data::Edit::Xml::new(<<END);
  <reference id="GUID-0cab6472-6f2a-e46a-9b6d-16649416ab53">
      <title>Testing the S3 Feature for PM 4 1</title>
      <refbody>
          <section>
              <p>To test the S# feature:</p>
              <ol>
                  <li>
                      <p>Set the PCIe link state to L1.2.</p>
                  </li>
                  <li>
                      <p>Issue the command: PM 4 1 command.</p>
                  </li>
              </ol>
          </section>
      </refbody>
  </reference>
  END
  
    my $concept = $ref->ditaConvertReferenceToConcept;
  
    ok -p $concept eq <<END;
  <concept id="GUID-0cab6472-6f2a-e46a-9b6d-16649416ab53">
    <title>Testing the S3 Feature for PM 4 1</title>
    <conbody>
      <p>To test the S# feature:</p>
      <ol>
        <li>
          <p>Set the PCIe link state to L1.2.</p>
        </li>
        <li>
          <p>Issue the command: PM 4 1 command.</p>
        </li>
      </ol>
    </conbody>
  </concept>
  END
  
    ok $concept->ditaCouldConvertConceptToTask;
    my $task = $concept->𝗱𝗶𝘁𝗮𝗖𝗼𝗻𝘃𝗲𝗿𝘁𝗖𝗼𝗻𝗰𝗲𝗽𝘁𝗧𝗼𝗧𝗮𝘀𝗸;
  
    ok -p $task eq <<END
  <task id="GUID-0cab6472-6f2a-e46a-9b6d-16649416ab53">
    <title>Testing the S3 Feature for PM 4 1</title>
    <taskbody>
      <context>
        <p>To test the S# feature:</p>
      </context>
      <steps>
        <step>
          <cmd>Set the PCIe link state to L1.2.</cmd>
        </step>
        <step>
          <cmd>Issue the command: PM 4 1 command.</cmd>
        </step>
      </steps>
      <result/>
    </taskbody>
  </task>
  END
  

B<ct> is a synonym for L<ditaConvertConceptToTask|/ditaConvertConceptToTask>.


=head3 ditaConvertReferenceToConcept($reference)

Convert a Dita B<reference> to a B<concept> by unwrapping sections. Return B<undef> if the conversion is not possible because there are no B<ol> else return the specified B<$reference> as as B<concept>.

     Parameter   Description
  1  $reference  Reference

B<Example:>


    my $ref = Data::Edit::Xml::new(<<END);
  <reference id="GUID-0cab6472-6f2a-e46a-9b6d-16649416ab53">
      <title>Testing the S3 Feature for PM 4 1</title>
      <refbody>
          <section>
              <p>To test the S# feature:</p>
              <ol>
                  <li>
                      <p>Set the PCIe link state to L1.2.</p>
                  </li>
                  <li>
                      <p>Issue the command: PM 4 1 command.</p>
                  </li>
              </ol>
          </section>
      </refbody>
  </reference>
  END
  
    my $concept = $ref->𝗱𝗶𝘁𝗮𝗖𝗼𝗻𝘃𝗲𝗿𝘁𝗥𝗲𝗳𝗲𝗿𝗲𝗻𝗰𝗲𝗧𝗼𝗖𝗼𝗻𝗰𝗲𝗽𝘁;
  
    ok -p $concept eq <<END;
  <concept id="GUID-0cab6472-6f2a-e46a-9b6d-16649416ab53">
    <title>Testing the S3 Feature for PM 4 1</title>
    <conbody>
      <p>To test the S# feature:</p>
      <ol>
        <li>
          <p>Set the PCIe link state to L1.2.</p>
        </li>
        <li>
          <p>Issue the command: PM 4 1 command.</p>
        </li>
      </ol>
    </conbody>
  </concept>
  END
  
    ok $concept->ditaCouldConvertConceptToTask;
    my $task = $concept->ditaConvertConceptToTask;
  
    ok -p $task eq <<END
  <task id="GUID-0cab6472-6f2a-e46a-9b6d-16649416ab53">
    <title>Testing the S3 Feature for PM 4 1</title>
    <taskbody>
      <context>
        <p>To test the S# feature:</p>
      </context>
      <steps>
        <step>
          <cmd>Set the PCIe link state to L1.2.</cmd>
        </step>
        <step>
          <cmd>Issue the command: PM 4 1 command.</cmd>
        </step>
      </steps>
      <result/>
    </taskbody>
  </task>
  END
  

=head3 ditaConvertReferenceToTask($reference)

Convert a Dita B<reference> to a B<task> in situ by representing B<ol> as B<steps>. Return B<undef> if the conversion is not possible because there are no such B<ol> else return the specified B<$reference> as as B<task>.

     Parameter   Description
  1  $reference  Reference

B<Example:>


    my $ref = Data::Edit::Xml::new(<<END);
  <reference id="GUID-0cab6472-6f2a-e46a-9b6d-16649416ab53">
      <title>Testing the S3 Feature for PM 4 1</title>
      <refbody>
          <section>
              <p>To test the S# feature:</p>
              <ol>
                  <li>
                      <p>Set the PCIe link state to L1.2.</p>
                  </li>
                  <li>
                      <p>Issue the command: PM 4 1 command.</p>
                  </li>
              </ol>
          </section>
      </refbody>
  </reference>
  END
  
    my $task = $ref->𝗱𝗶𝘁𝗮𝗖𝗼𝗻𝘃𝗲𝗿𝘁𝗥𝗲𝗳𝗲𝗿𝗲𝗻𝗰𝗲𝗧𝗼𝗧𝗮𝘀𝗸;
  
    ok -p $task eq <<END
  <task id="GUID-0cab6472-6f2a-e46a-9b6d-16649416ab53">
    <title>Testing the S3 Feature for PM 4 1</title>
    <taskbody>
      <context>
        <p>To test the S# feature:</p>
      </context>
      <steps>
        <step>
          <cmd>Set the PCIe link state to L1.2.</cmd>
        </step>
        <step>
          <cmd>Issue the command: PM 4 1 command.</cmd>
        </step>
      </steps>
      <result/>
    </taskbody>
  </task>
  END
  

=head3 ditaConvertConceptToReference($concept)

Convert a Dita B<concept> to a B<reference>. Return B<undef> if the conversion is not possible else return the specified B<$concept> as as B<reference>.

     Parameter  Description
  1  $concept   Concept

B<Example:>


    my $c = Data::Edit::Xml::new(<<END);
  <concept id="c1">
    <title>Title Text</title>
    <conbody>
      <p>Concept 11</p>
      <section>
        <p>A section 11</p>
      </section>
      <section>
        <p>A section 22</p>
      </section>
      <p>Concept 222</p>
    </conbody>
  </concept>
  END
  
    my $r = $c->𝗱𝗶𝘁𝗮𝗖𝗼𝗻𝘃𝗲𝗿𝘁𝗖𝗼𝗻𝗰𝗲𝗽𝘁𝗧𝗼𝗥𝗲𝗳𝗲𝗿𝗲𝗻𝗰𝗲;
  
    ok -p $c eq <<END;
  <reference id="c1">
    <title>Title Text</title>
    <refbody>
      <section>
        <p>Concept 11</p>
      </section>
      <section>
        <p>A section 11</p>
      </section>
      <section>
        <p>A section 22</p>
      </section>
      <section>
        <p>Concept 222</p>
      </section>
    </refbody>
  </reference>
  END
  
    my $C = $c->ditaConvertReferenceToConcept;
    ok -p $C eq <<END;
  <concept id="c1">
    <title>Title Text</title>
    <conbody>
      <p>Concept 11</p>
      <p>A section 11</p>
      <p>A section 22</p>
      <p>Concept 222</p>
    </conbody>
  </concept>
  END
  
    ok !$C->ditaCouldConvertConceptToTask;
  

=head3 ditaConvertTopicToTask($x)

Convert a topic that is not already a task into a task

     Parameter  Description
  1  $x         Topic parse tree

B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <concept id="c1">
    <title/>
    <conbody>
      <p>results</p>
      <ol><li/>Click</ol>
    </conbody>
  </concept>
  END
  
   $a->𝗱𝗶𝘁𝗮𝗖𝗼𝗻𝘃𝗲𝗿𝘁𝗧𝗼𝗽𝗶𝗰𝗧𝗼𝗧𝗮𝘀𝗸;
  
   ok nws(-p $a) eq nws(<<END);
  <task id="c1">
    <title/>
    <taskbody>
      <context>
        <p>results</p>
      </context>
      <steps>
        <step>
          <cmd/>
        </step>
  Click
      </steps>
      <result/>
    </taskbody>
  </task>
  END
  

=head3 ditaConvertSectionToConcept($section)

Convert a Dita B<$section> to a B<concept>. Return B<undef> if the conversion is not possible else return the specified B<$section> as as B<concept>.

     Parameter  Description
  1  $section   Section

B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <section id="a">
    <title>title</title>
    <p>text</p>
  </section>
  END
  
    ok -p $a->𝗱𝗶𝘁𝗮𝗖𝗼𝗻𝘃𝗲𝗿𝘁𝗦𝗲𝗰𝘁𝗶𝗼𝗻𝗧𝗼𝗖𝗼𝗻𝗰𝗲𝗽𝘁 eq <<END;
  <concept id="a">
    <title>title</title>
    <conbody>
      <p>text</p>
    </conbody>
  </concept>
  END
  
    ok -p $a->ditaConvertConceptToSection eq <<END;
  <section id="a">
    <title>title</title>
    <p>text</p>
  </section>
  END
  

B<sc> is a synonym for L<ditaConvertSectionToConcept|/ditaConvertSectionToConcept>.


=head3 ditaConvertConceptToSection($concept)

Convert a Dita B<concept> to a B<$section> . Return B<undef> if the conversion is not possible else return the specified B<$section> as as B<concept>.

     Parameter  Description
  1  $concept   Section

B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <section id="a">
    <title>title</title>
    <p>text</p>
  </section>
  END
  
    ok -p $a->ditaConvertSectionToConcept eq <<END;
  <concept id="a">
    <title>title</title>
    <conbody>
      <p>text</p>
    </conbody>
  </concept>
  END
  
    ok -p $a->𝗱𝗶𝘁𝗮𝗖𝗼𝗻𝘃𝗲𝗿𝘁𝗖𝗼𝗻𝗰𝗲𝗽𝘁𝗧𝗼𝗦𝗲𝗰𝘁𝗶𝗼𝗻 eq <<END;
  <section id="a">
    <title>title</title>
    <p>text</p>
  </section>
  END
  

B<cs> is a synonym for L<ditaConvertConceptToSection|/ditaConvertConceptToSection>.


=head3 ditaConvertSectionToReference($section)

Convert a Dita B<$section> to a B<reference>. Return B<undef> if the conversion is not possible else return the specified B<$section> as as B<reference>.

     Parameter  Description
  1  $section   Section

B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <section id="a">
    <title>title</title>
    <p>text</p>
  </section>
  END
  
    ok -p $a->𝗱𝗶𝘁𝗮𝗖𝗼𝗻𝘃𝗲𝗿𝘁𝗦𝗲𝗰𝘁𝗶𝗼𝗻𝗧𝗼𝗥𝗲𝗳𝗲𝗿𝗲𝗻𝗰𝗲 eq <<END
  <reference id="a">
    <title>title</title>
    <refbody>
      <section>
        <p>text</p>
      </section>
    </refbody>
  </reference>
  END
  

=head3 ditaConvertSectionToTask($section)

Convert a Dita B<$section> to a B<task>. Return B<undef> if the conversion is not possible else return the specified B<$section> as as B<task>.

     Parameter  Description
  1  $section   Section

B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <section id="a">
    <title>title</title>
    <ol>
      <li>step1</li>
      <li>step2</li>
    </ol>
  </section>
  END
  
    ok -p $a->𝗱𝗶𝘁𝗮𝗖𝗼𝗻𝘃𝗲𝗿𝘁𝗦𝗲𝗰𝘁𝗶𝗼𝗻𝗧𝗼𝗧𝗮𝘀𝗸 eq <<END
  <task id="a">
    <title>title</title>
    <taskbody>
      <context/>
      <steps>
        <step>
          <cmd>step1</cmd>
        </step>
        <step>
          <cmd>step2</cmd>
        </step>
      </steps>
      <result/>
    </taskbody>
  </task>
  END
  

=head3 ditaObviousChanges($node)

Make obvious changes to a L<parse|/parse> tree to make it look more like L<Dita|http://docs.oasis-open.org/dita/dita/v1.3/os/part2-tech-content/dita-v1.3-os-part2-tech-content.html>.

     Parameter  Description
  1  $node      Node

B<Example:>


   {my $a = Data::Edit::Xml::new(<<END);                                          
  <dita>
    <ol>
      <li><para>aaa</para></li>
      <li><para>bbb</para></li>
    </ol>
  </dita>
  END
  
    $a->𝗱𝗶𝘁𝗮𝗢𝗯𝘃𝗶𝗼𝘂𝘀𝗖𝗵𝗮𝗻𝗴𝗲𝘀;                                                       
  
    ok -p $a eq <<END;                                                              
  <dita>
    <ol>
      <li>
        <p>aaa</p>
      </li>
      <li>
        <p>bbb</p>
      </li>
    </ol>
  </dita>
  END
  

=head3 ditaXrefs($x)

Make obvious changes to all the B<xref>s found in a L<parse|/parse> tree to make them more useful in L<Dita|http://docs.oasis-open.org/dita/dita/v1.3/os/part2-tech-content/dita-v1.3-os-part2-tech-content.html>.

     Parameter  Description
  1  $x         Parse tree

B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <body>
   <xref href="http://www.org">a</xref>
  </body>
  END
  
    $a->𝗱𝗶𝘁𝗮𝗫𝗿𝗲𝗳𝘀;
  
    ok -p $a eq <<END;
  <body>
    <p>
      <xref format="html" href="http://www.org" scope="external">a</xref>
    </p>
  </body>
  END
  

=head3 ditaSampleConcept(@options)

Sample concept

     Parameter  Description
  1  @options   Options for concept

B<Example:>


    ok Data::Edit::Xml::𝗱𝗶𝘁𝗮𝗦𝗮𝗺𝗽𝗹𝗲𝗖𝗼𝗻𝗰𝗲𝗽𝘁
     (title=>q(New Concept),
     )->prettyString eq <<END;
  <concept id="GUID-a5405560-67e7-0cd1-9188-9a2546d13a37">
    <title id="title">New Concept</title>
    <conbody>
      <p>Please provide the body of this concept using the body keyword</p>
    </conbody>
  </concept>
  END
  

This is a static method and so should be invoked as:

  Data::Edit::Xml::ditaSampleConcept


=head3 ditaSampleTask(@options)

Sample task

     Parameter  Description
  1  @options   Options for task

B<Example:>


    ok Data::Edit::Xml::𝗱𝗶𝘁𝗮𝗦𝗮𝗺𝗽𝗹𝗲𝗧𝗮𝘀𝗸
     (title=>q(New Task),
     )->prettyString eq <<END;
  <task id="GUID-2ac5c2a6-2475-d5d4-4ce6-d68fdf2102e3">
    <title id="title">New Task</title>
    <taskbody>
      <context/>
      <steps/>
      <result/>
    </taskbody>
  </task>
  END
  

This is a static method and so should be invoked as:

  Data::Edit::Xml::ditaSampleTask


=head3 ditaSampleBookMap(@options)

Sample bookmap

     Parameter  Description
  1  @options   Options for bookmap

B<Example:>


  
    my $a = Data::Edit::Xml::𝗱𝗶𝘁𝗮𝗦𝗮𝗺𝗽𝗹𝗲𝗕𝗼𝗼𝗸𝗠𝗮𝗽
     (author=>q(mim),
      chapters=>Data::Edit::Xml::new(<<END));
  <a>
    <b>
      <c/>
    </b>
  </a>
  END
  
    ok $a->isADitaMap;
    ok !$a->first->isADitaMap;
  
    ok -p $a eq <<END;
  <bookmap id="GUID-3b04e859-bc04-ac2d-e4bb-f0f99b2a8f90">
    <booktitle>
      <mainbooktitle/>
    </booktitle>
    <bookmeta>
      <shortdesc/>
      <author>mim</author>
      <source/>
      <category/>
      <keywords>
        <keyword/>
      </keywords>
      <prodinfo>
        <prodname product=""/>
        <vrmlist>
          <vrm version=""/>
        </vrmlist>
        <prognum/>
        <brand/>
      </prodinfo>
      <bookchangehistory>
        <approved>
          <revisionid/>
        </approved>
      </bookchangehistory>
      <bookrights>
        <copyrfirst>
          <year/>
        </copyrfirst>
        <bookowner/>
      </bookrights>
    </bookmeta>
    <frontmatter>
      <notices/>
      <booklists>
        <toc/>
      </booklists>
      <preface/>
    </frontmatter>
    <a>
      <b>
        <c/>
      </b>
    </a>
    <appendices/>
    <reltable>
      <relheader>
        <relcolspec/>
        <relcolspec/>
      </relheader>
      <relrow>
        <relcell/>
        <relcell/>
      </relrow>
      <relrow>
        <relcell/>
        <relcell/>
      </relrow>
    </reltable>
  </bookmap>
  END
  

This is a static method and so should be invoked as:

  Data::Edit::Xml::ditaSampleBookMap


=head3 isADitaMap($node)

Return the specified B<$node> if this node is a L<Dita|http://docs.oasis-open.org/dita/dita/v1.3/os/part2-tech-content/dita-v1.3-os-part2-tech-content.html> map else return B<undef>

     Parameter  Description
  1  $node      Node to test

B<Example:>


  
    my $a = Data::Edit::Xml::ditaSampleBookMap
     (author=>q(mim),
      chapters=>Data::Edit::Xml::new(<<END));
  <a>
    <b>
      <c/>
    </b>
  </a>
  END
  
    ok $a->𝗶𝘀𝗔𝗗𝗶𝘁𝗮𝗠𝗮𝗽;
    ok !$a->first->𝗶𝘀𝗔𝗗𝗶𝘁𝗮𝗠𝗮𝗽;
  
    ok -p $a eq <<END;
  <bookmap id="GUID-3b04e859-bc04-ac2d-e4bb-f0f99b2a8f90">
    <booktitle>
      <mainbooktitle/>
    </booktitle>
    <bookmeta>
      <shortdesc/>
      <author>mim</author>
      <source/>
      <category/>
      <keywords>
        <keyword/>
      </keywords>
      <prodinfo>
        <prodname product=""/>
        <vrmlist>
          <vrm version=""/>
        </vrmlist>
        <prognum/>
        <brand/>
      </prodinfo>
      <bookchangehistory>
        <approved>
          <revisionid/>
        </approved>
      </bookchangehistory>
      <bookrights>
        <copyrfirst>
          <year/>
        </copyrfirst>
        <bookowner/>
      </bookrights>
    </bookmeta>
    <frontmatter>
      <notices/>
      <booklists>
        <toc/>
      </booklists>
      <preface/>
    </frontmatter>
    <a>
      <b>
        <c/>
      </b>
    </a>
    <appendices/>
    <reltable>
      <relheader>
        <relcolspec/>
        <relcolspec/>
      </relheader>
      <relrow>
        <relcell/>
        <relcell/>
      </relrow>
      <relrow>
        <relcell/>
        <relcell/>
      </relrow>
    </reltable>
  </bookmap>
  END
  

=head3 ditaRoot($node)

Return the specified B<$node> if it a L<Dita|http://docs.oasis-open.org/dita/dita/v1.3/os/part2-tech-content/dita-v1.3-os-part2-tech-content.html> root node else return B<undef>.

     Parameter  Description
  1  $node      Node to check

B<Example:>


    my $a = Data::Edit::Xml::new(q(<concept><a/></concept>));
    ok  $a->𝗱𝗶𝘁𝗮𝗥𝗼𝗼𝘁;
    ok !$a->first->𝗱𝗶𝘁𝗮𝗥𝗼𝗼𝘁;
  

=head3 ditaTopicHeaders($node, $String)

Add L<Xml|https://en.wikipedia.org/wiki/XML> headers for the dita document type indicated by the specified L<parse|/parse> tree

     Parameter  Description
  1  $node      Node in parse tree
  2  $String    Suffix string

B<Example:>


    ok Data::Edit::Xml::new(q(<concept/>))->𝗱𝗶𝘁𝗮𝗧𝗼𝗽𝗶𝗰𝗛𝗲𝗮𝗱𝗲𝗿𝘀 eq <<END;            
  <?xml version="1.0" encoding="UTF-8"?>
  <!DOCTYPE concept PUBLIC "-//OASIS//DTD DITA Concept//EN" "concept.dtd" []>
  END
  

=head3 ditaPrettyPrintWithHeaders($node)

Add L<Xml|https://en.wikipedia.org/wiki/XML> headers for the dita document type indicated by the specified L<parse|/parse> tree to a pretty print of the parse tree.

     Parameter  Description
  1  $node      Node in parse tree

B<Example:>


  if (1)                                                                          
   {my $a = Data::Edit::Xml::new(q(<concept/>));
  
    ok $a->𝗱𝗶𝘁𝗮𝗣𝗿𝗲𝘁𝘁𝘆𝗣𝗿𝗶𝗻𝘁𝗪𝗶𝘁𝗵𝗛𝗲𝗮𝗱𝗲𝗿𝘀 eq <<END;
  <?xml version="1.0" encoding="UTF-8"?>
  <!DOCTYPE concept PUBLIC "-//OASIS//DTD DITA Concept//EN" "concept.dtd" []>
  <concept/>
  END
   }
  

=head3 ditaCutTopicmetaFromAClassificationMap($node, @context)

Remove a topicmeta node from a classification map. Dita::Validate was built from conventional maps and so does not recognize all the situations where topicmeta is invalid in a classification map

     Parameter  Description
  1  $node      Node
  2  @context   Optional context

=head3 ditaAddTopicReport($tree, $report)

Place a report into a dita topic using required clean up

     Parameter  Description
  1  $tree      Topic
  2  $report    Report

B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <task id="c1">
    <title/>
    <taskbody>
    </taskbody>
  </task>
  END
  
    $a->𝗱𝗶𝘁𝗮𝗔𝗱𝗱𝗧𝗼𝗽𝗶𝗰𝗥𝗲𝗽𝗼𝗿𝘁(q(Please note the following discrepancies));
    ok -p $a eq <<END;
  <task id="c1">
    <title/>
    <taskbody>
      <context>
        <required-cleanup>Please note the following discrepancies</required-cleanup>
      </context>
    </taskbody>
  </task>
  END
  

=head3 help($node)

Get help for a node and the editor

     Parameter  Description
  1  $node      Node

B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <steps/>
    <dd/>
  </a>
  END
  
    my $u = $a->go_dd->𝗵𝗲𝗹𝗽;
    ok $u =~ m(\Ahttp://docs.oasis-open.org/dita/dita/v1.3/errata02/os/complete/part3-all-inclusive/contentmodels/cmltd.html#cmltd__dd\Z);
  
    my $U = $a->go_steps->𝗵𝗲𝗹𝗽;
    ok $U =~ m(\Ahttp://docs.oasis-open.org/dita/dita/v1.3/errata02/os/complete/part3-all-inclusive/contentmodels/cmlts.html#cmlts__steps\Z);
  

B<h> is a synonym for L<help|/help>.


=head2 Html and word conversions

Methods useful for converting Word and L<html|https://en.wikipedia.org/wiki/HTML> to L<Dita|http://docs.oasis-open.org/dita/dita/v1.3/os/part2-tech-content/dita-v1.3-os-part2-tech-content.html>

=head3 htmlHeadersToSections($tree)

Position sections just before html header tags so that subsequently the document can be divided into L<sections|/divideDocumentIntoSections>.

     Parameter  Description
  1  $tree      Parse tree

B<Example:>


   {my $x = Data::Edit::Xml::new(<<END);                                           
  <x>
  <h1>h1</h1>
    H1
  <h2>h2</h2>
    H2
  <h3>h3</h3>
    H3
  <h3>h3</h3>
    H3
  <h2>h2</h2>
    H2
  <h4>h4</h4>
    H4
  </x>
  END
  
  $x->𝗵𝘁𝗺𝗹𝗛𝗲𝗮𝗱𝗲𝗿𝘀𝗧𝗼𝗦𝗲𝗰𝘁𝗶𝗼𝗻𝘀;                                                       
  
    $x->divideDocumentIntoSections(sub                                             
  
     {my ($topicref, $section) = @_;                                               
  
      my $file = keys %file;                                                       
  
      $topicref->href = $file;                                                     
  
      $file{$file} = -p $section;                                                  
  
      $section->cut;                                                               
  
     });                                                                           
  
    ok -p $x eq <<END;                                                             
  <x>
    <topicref href="0">
      <topicref href="1">
        <topicref href="2"/>
        <topicref href="3"/>
      </topicref>
      <topicref href="4">
        <topicref href="5"/>
      </topicref>
    </topicref>
  </x>
  END
  

=head3 divideDocumentIntoSections($node, $cutSub)

Divide a L<parse|/parse> tree into sections by moving non B<section> tags into their corresponding B<section> so that the B<section> tags expand until they are contiguous. The sections are then cut out by applying the specified sub to each B<section> tag in the L<parse|/parse> tree. The specified sub will receive the containing B<topicref> and the B<section> to be cut out as parameters allowing a reference to the cut out section to be inserted into the B<topicref>.

     Parameter  Description
  1  $node      Parse tree
  2  $cutSub    Cut out sub

B<Example:>


   {my $x = Data::Edit::Xml::new(<<END);                                           
  <x>
  <h1>h1</h1>
    H1
  <h2>h2</h2>
    H2
  <h3>h3</h3>
    H3
  <h3>h3</h3>
    H3
  <h2>h2</h2>
    H2
  <h4>h4</h4>
    H4
  </x>
  END
  
  $x->htmlHeadersToSections;                                                       
  
    $x->𝗱𝗶𝘃𝗶𝗱𝗲𝗗𝗼𝗰𝘂𝗺𝗲𝗻𝘁𝗜𝗻𝘁𝗼𝗦𝗲𝗰𝘁𝗶𝗼𝗻𝘀(sub                                             
  
     {my ($topicref, $section) = @_;                                               
  
      my $file = keys %file;                                                       
  
      $topicref->href = $file;                                                     
  
      $file{$file} = -p $section;                                                  
  
      $section->cut;                                                               
  
     });                                                                           
  
    ok -p $x eq <<END;                                                             
  <x>
    <topicref href="0">
      <topicref href="1">
        <topicref href="2"/>
        <topicref href="3"/>
      </topicref>
      <topicref href="4">
        <topicref href="5"/>
      </topicref>
    </topicref>
  </x>
  END
  

=head3 divideHtmlDocumentIntoSections($tree)

Divide a L<parse|/parse> tree representing an html document into sections based on the heading tags.

     Parameter  Description
  1  $tree      Parse tree

B<Example:>


  
    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <h1>HH11</h1>    text1
      <h2>HH22</h2>  text2
      <h2>HH22</h2>  text3
        <h4>HH44</h4>text4
      <h1>HH11</h1>  text5
  </a>
  END
  
    $a->𝗱𝗶𝘃𝗶𝗱𝗲𝗛𝘁𝗺𝗹𝗗𝗼𝗰𝘂𝗺𝗲𝗻𝘁𝗜𝗻𝘁𝗼𝗦𝗲𝗰𝘁𝗶𝗼𝗻𝘀;
  
    ok nws(-p $a) eq nws(<<END);
  <a>
    <section outputclass="1">
      <title>HH11</title>
  text1
      <section outputclass="2">
        <title>HH22</title>
  text2
      </section>
      <section outputclass="2">
        <title>HH22</title>
  text3
        <section outputclass="4">
          <title>HH44</title>
  text4
        </section>
      </section>
    </section>
    <section outputclass="1">
      <title>HH11</title>
  text5
    </section>
  </a>
  END
  

=head3 ditaParagraphToNote($node, $wrapNoteContentWithParagaph)

Convert all <p> nodes to <note> if the paragraph starts with 'Note:', optionally wrapping the content of the <note> with a <p>

     Parameter                     Description
  1  $node                         Parse tree
  2  $wrapNoteContentWithParagaph  Wrap the <note> content with a <p> if true

B<Example:>


   {my $a = Data::Edit::Xml::new(<<END);                                          
  <a>
    <p> Note: see over for details.</p>
  </a>
  END
  
    $a->𝗱𝗶𝘁𝗮𝗣𝗮𝗿𝗮𝗴𝗿𝗮𝗽𝗵𝗧𝗼𝗡𝗼𝘁𝗲(1);                                                   
  
    ok -p $a eq <<END;                                                            
  <a>
    <note>
      <p>See over for details.</p>
    </note>
  </a>
  END
  

=head3 wordStyles($x)

Extract style information from a parse tree representing a word document.

     Parameter  Description
  1  $x         Parse tree

B<Example:>


   {my $a = Data::Edit::Xml::new(<<END);                                          
  <a>
   <text:list-style style:name="aa">
     <text:list-level-style-bullet text:level="2"/>
   </text:list-style>
  </a>
  END
  
    my $styles = $a->𝘄𝗼𝗿𝗱𝗦𝘁𝘆𝗹𝗲𝘀;                                                  
  
    is_deeply $styles, {bulletedList=>{aa=>{2=>1}}};                              
  

=head3 htmlTableToDita($table)

Convert an L<html table> to a L<Dita|http://docs.oasis-open.org/dita/dita/v1.3/os/part2-tech-content/dita-v1.3-os-part2-tech-content.html> table.

     Parameter  Description
  1  $table     Html table node

B<Example:>


   {my $a = Data::Edit::Xml::new(<<END);                                          
   <table>
     <thead>
      <tr>
         <th>Month</th>
         <th>Savings</th>
         <th>Phone</th>
         <th>Comment</th>
      </tr>
     </thead>
     <tbody>
      <tr>
         <td>January</td>
         <td>100</td>
         <td>555-1212</td>
      </tr>
      <tr>
         <td>February</td>
         <td>80</td>
      </tr>
     </tbody>
  </table>
  END
  
    $a->𝗵𝘁𝗺𝗹𝗧𝗮𝗯𝗹𝗲𝗧𝗼𝗗𝗶𝘁𝗮;                                                          
  
    ok -p $a eq <<END;                                                            
  <table>
    <tgroup cols="4">
      <colspec colname="c1" colnum="1" colwidth="1*"/>
      <colspec colname="c2" colnum="2" colwidth="1*"/>
      <colspec colname="c3" colnum="3" colwidth="1*"/>
      <colspec colname="c4" colnum="4" colwidth="1*"/>
      <thead>
        <row>
          <entry>Month</entry>
          <entry>Savings</entry>
          <entry>Phone</entry>
          <entry>Comment</entry>
        </row>
      </thead>
      <tbody>
        <row>
          <entry>January</entry>
          <entry>100</entry>
          <entry nameend="c4" namest="c3">555-1212</entry>
        </row>
        <row>
          <entry>February</entry>
          <entry nameend="c4" namest="c2">80</entry>
        </row>
      </tbody>
    </tgroup>
  </table>
  END
  

=head3 ditaSyntaxDiagramFromDocBookCmdSynopsis({package ditaSyntaxDiagramFromDocBookCmdSynopsis;)

Convert doc book cmdsynopsis to Dita syntax diagram

     Parameter                                          Description
  1  {package ditaSyntaxDiagramFromDocBookCmdSynopsis;  Allows us to package the entire conversion as one method

B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <cmdsynopsis>
    <command>fs</command>
    <arg choice="plain">list</arg>
    <group choice="opt">
      <arg choice="plain">--pending</arg>
      <arg choice="plain">--running</arg>
    </group>
    <group choice="opt">
      <arg choice="plain">--online</arg>
      <arg choice="plain">
        --offline
        <group choice="req">
          <arg choice="plain">http</arg>
          <arg choice="plain">https</arg>
          <arg choice="plain">ftp</arg>
          <arg choice="plain">ssh</arg>
        </group>
      </arg>
      <arg choice="plain">--special</arg>
      <arg choice="plain">--snap</arg>
    </group>
    <group choice="opt">
      <arg choice="plain">--real</arg>
      <arg choice="plain">--virtual</arg>
      <arg choice="plain">--absolute</arg>
    </group>
    <arg choice="opt">--titled</arg>
    <group choice="opt">
      <arg choice="plain">--total</arg>
      <arg choice="plain">--space</arg>
    </group>
    <arg choice="opt">
      --rename
      <replaceable>new-name</replaceable>
    </arg>
    <arg choice="opt">
      --sort
      <replaceable>sort procedure</replaceable>
    </arg>
    <arg choice="opt">--raw</arg>
    <arg choice="opt">
      --limit
      <replaceable>limits</replaceable>
    </arg>
    <arg choice="opt">--page</arg>
    <arg choice="opt" rep="repeat">
      <replaceable>files</replaceable>
    </arg>
  </cmdsynopsis>
  END
  
    $a->𝗱𝗶𝘁𝗮𝗦𝘆𝗻𝘁𝗮𝘅𝗗𝗶𝗮𝗴𝗿𝗮𝗺𝗙𝗿𝗼𝗺𝗗𝗼𝗰𝗕𝗼𝗼𝗸𝗖𝗺𝗱𝗦𝘆𝗻𝗼𝗽𝘀𝗶𝘀;
  
    ok -p $a eq <<END;
  <syntaxdiagram>
    <groupseq>
      <kwd outputclass="command">fs</kwd>
      <kwd>list</kwd>
      <groupchoice importance="optional">
        <kwd>--pending</kwd>
        <kwd>--running</kwd>
      </groupchoice>
      <groupchoice importance="optional">
        <kwd>--online</kwd>
        <groupseq>
          <kwd>
        --offline
        </kwd>
          <groupchoice>
            <kwd>http</kwd>
            <kwd>https</kwd>
            <kwd>ftp</kwd>
            <kwd>ssh</kwd>
          </groupchoice>
        </groupseq>
        <kwd>--special</kwd>
        <kwd>--snap</kwd>
      </groupchoice>
      <groupchoice importance="optional">
        <kwd>--real</kwd>
        <kwd>--virtual</kwd>
        <kwd>--absolute</kwd>
      </groupchoice>
      <kwd importance="optional">--titled</kwd>
      <groupchoice importance="optional">
        <kwd>--total</kwd>
        <kwd>--space</kwd>
      </groupchoice>
      <groupseq importance="optional">
        <kwd>
      --rename
      </kwd>
        <var>new-name</var>
      </groupseq>
      <groupseq importance="optional">
        <kwd>
      --sort
      </kwd>
        <var>sort procedure</var>
      </groupseq>
      <kwd importance="optional">--raw</kwd>
      <groupseq importance="optional">
        <kwd>
      --limit
      </kwd>
        <var>limits</var>
      </groupseq>
      <kwd importance="optional">--page</kwd>
      <groupseq importance="optional">
        <var>files</var>
        <sep>…</sep>
      </groupseq>
    </groupseq>
  </syntaxdiagram>
  END
  
    $a->ditaSyntaxDiagramToBasicRepresentation;
  
    ok nws(-p $a) eq nws(<<END);
  <p>
    <cmdname>fs</cmdname>
    <codeph>list</codeph>
  
   [ <codeph>--pending</codeph> | <codeph>--running</codeph> ]
  
   [ <codeph>--online</codeph> |
     <codeph> --offline </codeph> { <codeph>http</codeph> | <codeph>https</codeph>  | <codeph>ftp</codeph>  | <codeph>ssh</codeph> }  |
     <codeph>--special</codeph> |
     <codeph>--snap</codeph>
   ]
  
   [ <codeph>--real</codeph> |
     <codeph>--virtual</codeph> |
     <codeph>--absolute</codeph>
   ]
  
   [ <codeph>--titled</codeph> ]
   [ <codeph>--total</codeph>  |  <codeph>--space</codeph> ]
   [ <codeph> --rename </codeph>  <userinput>new-name</userinput> ]
   [ <codeph> --sort </codeph>    <userinput>sort procedure</userinput> ]
   [ <codeph>--raw</codeph> ]
   [ <codeph> --limit </codeph>     <userinput>limits</userinput> ]
   [ <codeph>--page</codeph>  ]
   [ <userinput>files</userinput> <codeph>…</codeph> ]
  </p>
  END
  

=head3 ditaSyntaxDiagramToBasicRepresentation($x)

Convert Dita syntax diagrams into Micaela's Basic Version.

     Parameter  Description
  1  $x         Parse tree

B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <cmdsynopsis>
    <command>fs</command>
    <arg choice="plain">list</arg>
    <group choice="opt">
      <arg choice="plain">--pending</arg>
      <arg choice="plain">--running</arg>
    </group>
    <group choice="opt">
      <arg choice="plain">--online</arg>
      <arg choice="plain">
        --offline
        <group choice="req">
          <arg choice="plain">http</arg>
          <arg choice="plain">https</arg>
          <arg choice="plain">ftp</arg>
          <arg choice="plain">ssh</arg>
        </group>
      </arg>
      <arg choice="plain">--special</arg>
      <arg choice="plain">--snap</arg>
    </group>
    <group choice="opt">
      <arg choice="plain">--real</arg>
      <arg choice="plain">--virtual</arg>
      <arg choice="plain">--absolute</arg>
    </group>
    <arg choice="opt">--titled</arg>
    <group choice="opt">
      <arg choice="plain">--total</arg>
      <arg choice="plain">--space</arg>
    </group>
    <arg choice="opt">
      --rename
      <replaceable>new-name</replaceable>
    </arg>
    <arg choice="opt">
      --sort
      <replaceable>sort procedure</replaceable>
    </arg>
    <arg choice="opt">--raw</arg>
    <arg choice="opt">
      --limit
      <replaceable>limits</replaceable>
    </arg>
    <arg choice="opt">--page</arg>
    <arg choice="opt" rep="repeat">
      <replaceable>files</replaceable>
    </arg>
  </cmdsynopsis>
  END
  
    $a->ditaSyntaxDiagramFromDocBookCmdSynopsis;
  
    ok -p $a eq <<END;
  <syntaxdiagram>
    <groupseq>
      <kwd outputclass="command">fs</kwd>
      <kwd>list</kwd>
      <groupchoice importance="optional">
        <kwd>--pending</kwd>
        <kwd>--running</kwd>
      </groupchoice>
      <groupchoice importance="optional">
        <kwd>--online</kwd>
        <groupseq>
          <kwd>
        --offline
        </kwd>
          <groupchoice>
            <kwd>http</kwd>
            <kwd>https</kwd>
            <kwd>ftp</kwd>
            <kwd>ssh</kwd>
          </groupchoice>
        </groupseq>
        <kwd>--special</kwd>
        <kwd>--snap</kwd>
      </groupchoice>
      <groupchoice importance="optional">
        <kwd>--real</kwd>
        <kwd>--virtual</kwd>
        <kwd>--absolute</kwd>
      </groupchoice>
      <kwd importance="optional">--titled</kwd>
      <groupchoice importance="optional">
        <kwd>--total</kwd>
        <kwd>--space</kwd>
      </groupchoice>
      <groupseq importance="optional">
        <kwd>
      --rename
      </kwd>
        <var>new-name</var>
      </groupseq>
      <groupseq importance="optional">
        <kwd>
      --sort
      </kwd>
        <var>sort procedure</var>
      </groupseq>
      <kwd importance="optional">--raw</kwd>
      <groupseq importance="optional">
        <kwd>
      --limit
      </kwd>
        <var>limits</var>
      </groupseq>
      <kwd importance="optional">--page</kwd>
      <groupseq importance="optional">
        <var>files</var>
        <sep>…</sep>
      </groupseq>
    </groupseq>
  </syntaxdiagram>
  END
  
    $a->𝗱𝗶𝘁𝗮𝗦𝘆𝗻𝘁𝗮𝘅𝗗𝗶𝗮𝗴𝗿𝗮𝗺𝗧𝗼𝗕𝗮𝘀𝗶𝗰𝗥𝗲𝗽𝗿𝗲𝘀𝗲𝗻𝘁𝗮𝘁𝗶𝗼𝗻;
  
    ok nws(-p $a) eq nws(<<END);
  <p>
    <cmdname>fs</cmdname>
    <codeph>list</codeph>
  
   [ <codeph>--pending</codeph> | <codeph>--running</codeph> ]
  
   [ <codeph>--online</codeph> |
     <codeph> --offline </codeph> { <codeph>http</codeph> | <codeph>https</codeph>  | <codeph>ftp</codeph>  | <codeph>ssh</codeph> }  |
     <codeph>--special</codeph> |
     <codeph>--snap</codeph>
   ]
  
   [ <codeph>--real</codeph> |
     <codeph>--virtual</codeph> |
     <codeph>--absolute</codeph>
   ]
  
   [ <codeph>--titled</codeph> ]
   [ <codeph>--total</codeph>  |  <codeph>--space</codeph> ]
   [ <codeph> --rename </codeph>  <userinput>new-name</userinput> ]
   [ <codeph> --sort </codeph>    <userinput>sort procedure</userinput> ]
   [ <codeph>--raw</codeph> ]
   [ <codeph> --limit </codeph>     <userinput>limits</userinput> ]
   [ <codeph>--page</codeph>  ]
   [ <userinput>files</userinput> <codeph>…</codeph> ]
  </p>
  END
  

=head3 ditaWrapWithPUnderConbody($conbody)

Wrap items immediately under L<Dita|http://docs.oasis-open.org/dita/dita/v1.3/os/part2-tech-content/dita-v1.3-os-part2-tech-content.html> B<conbody> with B<p> or merge with any previous B<p> if the item in question does not fit under B<conbody> but does fit under B<p>. Return the current node if it is B<conbody> else return B<undef>.

     Parameter  Description
  1  $conbody   Section

B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <conbody>text<q>quote</q><p>new paragraph</p>
  more text</conbody>
  END
  
    $a->𝗱𝗶𝘁𝗮𝗪𝗿𝗮𝗽𝗪𝗶𝘁𝗵𝗣𝗨𝗻𝗱𝗲𝗿𝗖𝗼𝗻𝗯𝗼𝗱𝘆;
  
    my $b = new(<<END);
  <conbody><p>text<q>quote</q></p><p>new paragraph
  more text</p></conbody>
  END
  
    ok $a->equals($b);
  

=head1 PCD

Please Change Dita Language Features

=head2 putNodeAs($node, $name, @context)

Return the specified B<$node> after saving it under the specified B<$name> if we are in the optional B<@context>.

     Parameter  Description
  1  $node      Node
  2  $name      Save name
  3  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b>
      <c>
        <d id="d"/>
      </c>
    </b>
  </a>
  END
  
    my ($d, $c, $b) = $a->byList;
  
    $d->𝗽𝘂𝘁𝗡𝗼𝗱𝗲𝗔𝘀(q(D));
    ok $a->getNodeAs(q(D)) == $d;
    %Data::Edit::Xml::savedNodes = ();
    ok $a->getNodeAs(q(D)) == $a;
  
    ok $a->reportNode_AAAA eq <<END;
  AAAA
  <a>
    <b>
      <c>
        <d id="d"/>
      </c>
    </b>
  </a>
  END
    ok $d->reportNodeAttributes_BBBB eq q(BBBB  id="d");
    ok $d->reportNodeAttributes_CCCC eq q(CCCC  id="d");
    ok $d->reportNodeContext_DDDD    eq q(DDDD d c b a);
  

B<put> is a synonym for L<putNodeAs|/putNodeAs>.


=head2 getNodeAs($node, $name, @context)

Return the specified B<$node> unless it is possible to return the node saved with L<putNodeAs> under the specified B<name> and we are optionally in the specified B<@context>.

     Parameter  Description
  1  $node      Default node
  2  $name      Name of saved node
  3  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b>
      <c>
        <d id="d"/>
      </c>
    </b>
  </a>
  END
  
    my ($d, $c, $b) = $a->byList;
  
    $d->putNodeAs(q(D));
    ok $a->𝗴𝗲𝘁𝗡𝗼𝗱𝗲𝗔𝘀(q(D)) == $d;
    %Data::Edit::Xml::savedNodes = ();
    ok $a->𝗴𝗲𝘁𝗡𝗼𝗱𝗲𝗔𝘀(q(D)) == $a;
  
    ok $a->reportNode_AAAA eq <<END;
  AAAA
  <a>
    <b>
      <c>
        <d id="d"/>
      </c>
    </b>
  </a>
  END
    ok $d->reportNodeAttributes_BBBB eq q(BBBB  id="d");
    ok $d->reportNodeAttributes_CCCC eq q(CCCC  id="d");
    ok $d->reportNodeContext_DDDD    eq q(DDDD d c b a);
  

B<get> is a synonym for L<getNodeAs|/getNodeAs>.


=head2 reportNodeAttributes($node, $label, @context)

Print the attributes of the specified B<$node> identified by the specified B<$label> if we are in the optional B<@context>.

     Parameter  Description
  1  $node      Node node
  2  $label     Label
  3  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b>
      <c>
        <d id="d"/>
      </c>
    </b>
  </a>
  END
  
    my ($d, $c, $b) = $a->byList;
  
    $d->putNodeAs(q(D));
    ok $a->getNodeAs(q(D)) == $d;
    %Data::Edit::Xml::savedNodes = ();
    ok $a->getNodeAs(q(D)) == $a;
  
    ok $a->reportNode_AAAA eq <<END;
  AAAA
  <a>
    <b>
      <c>
        <d id="d"/>
      </c>
    </b>
  </a>
  END
    ok $d->reportNodeAttributes_BBBB eq q(BBBB  id="d");
    ok $d->reportNodeAttributes_CCCC eq q(CCCC  id="d");
    ok $d->reportNodeContext_DDDD    eq q(DDDD d c b a);
  

B<rna> is a synonym for L<reportNodeAttributes|/reportNodeAttributes>.


=head2 reportNodeContext($node, $label, @context)

Print the context of the specified B<$node> identified by the specified B<$label> if we are in the optional B<@context>.

     Parameter  Description
  1  $node      Node node
  2  $label     Label
  3  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b>
      <c>
        <d id="d"/>
      </c>
    </b>
  </a>
  END
  
    my ($d, $c, $b) = $a->byList;
  
    $d->putNodeAs(q(D));
    ok $a->getNodeAs(q(D)) == $d;
    %Data::Edit::Xml::savedNodes = ();
    ok $a->getNodeAs(q(D)) == $a;
  
    ok $a->reportNode_AAAA eq <<END;
  AAAA
  <a>
    <b>
      <c>
        <d id="d"/>
      </c>
    </b>
  </a>
  END
    ok $d->reportNodeAttributes_BBBB eq q(BBBB  id="d");
    ok $d->reportNodeAttributes_CCCC eq q(CCCC  id="d");
    ok $d->reportNodeContext_DDDD    eq q(DDDD d c b a);
  

B<rnc> is a synonym for L<reportNodeContext|/reportNodeContext>.


=head2 reportNode($node, $label, @context)

Print the parse tree starting at the specified B<$node> identified by the specified B<$label> if we are in the optional B<@context>.

     Parameter  Description
  1  $node      Node node
  2  $label     Label
  3  @context   Optional context

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b>
      <c>
        <d id="d"/>
      </c>
    </b>
  </a>
  END
  
    my ($d, $c, $b) = $a->byList;
  
    $d->putNodeAs(q(D));
    ok $a->getNodeAs(q(D)) == $d;
    %Data::Edit::Xml::savedNodes = ();
    ok $a->getNodeAs(q(D)) == $a;
  
    ok $a->reportNode_AAAA eq <<END;
  AAAA
  <a>
    <b>
      <c>
        <d id="d"/>
      </c>
    </b>
  </a>
  END
    ok $d->reportNodeAttributes_BBBB eq q(BBBB  id="d");
    ok $d->reportNodeAttributes_CCCC eq q(CCCC  id="d");
    ok $d->reportNodeContext_DDDD    eq q(DDDD d c b a);
  

B<rn> is a synonym for L<reportNode|/reportNode>.


=head1 Debug

Debugging methods

=head2 sss($node, @text)

Put, after the current B<$node>, the specified B<@text> and return the current $node.

     Parameter  Description
  1  $node      Node
  2  @text      Text

B<Example:>


    my $a = Data::Edit::Xml::new(q(<a><b><c/></b></a>));
  
    $a->go_b_c__sss_aa_bb_cc;
  
    ok -p $a eq <<END;
  <a>
    <b>
      <c/>
  aa bb cc
    </b>
  </a>
  END
  

=head2 printAttributes($node)

Print the attributes of a node.

     Parameter  Description
  1  $node      Node whose attributes are to be printed.

B<Example:>


    my $x = Data::Edit::Xml::new(my $s = <<END);                                  
  <a no="1" word="first"/>
  END
  
    ok $x->𝗽𝗿𝗶𝗻𝘁𝗔𝘁𝘁𝗿𝗶𝗯𝘂𝘁𝗲𝘀 eq qq( no="1" word="first");                            
  

=head2 printAttributesHtml($node)

Print the attributes of a node as html.

     Parameter  Description
  1  $node      Node whose attributes are to be printed.

B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a id="1">
    <b id="2" b="B">
      <c/>
    </b>
    <d id="3" d="D">
      Some text
    </d>
  </a>
  END
  
    ok $a->prettyStringHtml eq <<END;
  <div class="xmlLine"><span class="xmlLineStartTag"></span><span class="xmlLt">&lt;</span><span class="xmlTag">a</span> <span class="xmlAttr">id</span><span class="xmlEquals">=</span><span class="xmlValue">"1"</span><span class="xmlGt">&gt;</span></div>
  <div class="xmlLine"><span class="xmlLineStartTag">&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="xmlLt">&lt;</span><span class="xmlTag">b</span> <span class="xmlAttr">b</span><span class="xmlEquals">=</span><span class="xmlValue">"B"</span> <span class="xmlAttr">id</span><span class="xmlEquals">=</span><span class="xmlValue">"2"</span><span class="xmlGt">&gt;</span></div>
  <div class="xmlLine"><span class="xmlLineStartTag">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="xmlLt">&lt;</span><span class="xmlTag">c</span><span class="xmlSlashGt">/&gt;</span></div>
  <div class="xmlLine"><span class="xmlLineStartTag">&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="xmlLtSlash">&lt;/</span><span class="xmlTag">b</span><span class="xmlGt">&gt;</span></div>
  <div class="xmlLine"><span class="xmlLineStartTag">&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="xmlLt">&lt;</span><span class="xmlTag">d</span> <span class="xmlAttr">d</span><span class="xmlEquals">=</span><span class="xmlValue">"D"</span> <span class="xmlAttr">id</span><span class="xmlEquals">=</span><span class="xmlValue">"3"</span><span class="xmlGt">&gt;</span><span class="xmlText">Some text</span><span class="xmlLtSlash">&lt;/</span><span class="xmlTag">d</span><span class="xmlGt">&gt;</span></div>
  <div class="xmlLine"><span class="xmlLineStartTag"></span><span class="xmlLtSlash">&lt;/</span><span class="xmlTag">a</span><span class="xmlGt">&gt;</span></div>
  END
  

=head2 printStack($node, @context)

Print the attributes of a node and each of its parents

     Parameter  Description
  1  $node      Node whose attributes are to be printed
  2  @context   Optional context.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a id="a">
    <b id="b">
      <c>
        <d id="d"/>
      </c>
    </b>
  </a>
  END
  
    my $d = $a->firstUntil_d;
    ok $d->𝗽𝗿𝗶𝗻𝘁𝗦𝘁𝗮𝗰𝗸 eq <<END;
  a id="a"
    b id="b"
      c
        d id="d"
  END
    ok -S $d eq <<END;
  a id="a"
    b id="b"
      c
        d id="d"
  END
  

=head2 printNode($node, @context)

Print the tag and attributes of a node.

     Parameter  Description
  1  $node      Node whose attributes are to be printed
  2  @context   Optional context.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);                                               
  <a>
    <b>
      <c id="42" match="mm"/>
    </b>
    <d>
      <e/>
    </d>
  </a>
  END
  
    ok $c->𝗽𝗿𝗶𝗻𝘁𝗡𝗼𝗱𝗲 eq q(c id="42" match="mm");                                   
  

=head2 printNodeAsSingleton($node, @context)

Print the tag and attributes of a node as if it were a single node regardless of any child nodes it might have

     Parameter  Description
  1  $node      Node whose attributes are to be printed
  2  @context   Optional context.

Use the optional B<@context> parameter to test the context of the specified
B<$node> as understood by method L<at|/at>. If the context is supplied and
B<$node> is not in this context then this method returns B<undef> immediately.



B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a id="a" outputclass="aa">
    <b/>
  </a>
  END
  
    ok $a->𝗽𝗿𝗶𝗻𝘁𝗡𝗼𝗱𝗲𝗔𝘀𝗦𝗶𝗻𝗴𝗹𝗲𝘁𝗼𝗻 eq q(<a id="a" outputclass="aa"/>);
  

=head2 goFish($node, @path)

A debug version of L<go|/go> that returns additional information explaining any failure to reach the node identified by the L<path|/path>.

Returns ([B<reachable tag>...], B<failing tag>, [B<possible tag>...]) where:

=over

=item B<reachable tag>

the path elements successfully traversed;

=item B<failing tag>

the failing element;

=item B<possible tag>

the possibilities at the point where the path failed if it failed else B<undef>.

=back

Parameters:

     Parameter  Description
  1  $node      Node
  2  @path      Search specification.

B<Example:>


   {my $a = Data::Edit::Xml::new(<<END);                                          
  <a>
    <b>
      <c>
        <d/>
      </c>
      <c/>
    </b>
    <b/>
  </a>
  END
  
      my ($good, $fail, $possible) = $a->𝗴𝗼𝗙𝗶𝘀𝗵(qw(b c D));                       
  
      ok  $fail eq q(D);                                                          
  
      is_deeply $good,     [qw(b c)];                                             
  
      is_deeply $possible, [q(d)];                                                
  

=head1 Documentation

Update documentation describing this module

=head1 Compression

Read and write files of compressed xml.  These methods provide a compact, efficient way to store and retrieve parse trees to/from files.

=head2 writeCompressedFile($node, $file)

Write the parse tree starting at B<$node> as compressed L<Xml|https://en.wikipedia.org/wiki/XML> to the specified B<$file>. Use L<readCompressedFile|/readCompressedFile>  to read the B<$file>.

     Parameter  Description
  1  $node      Parse tree node
  2  $file      File to write to.

B<Example:>


    my $a = Data::Edit::Xml::new(q(<a>).(q(<b>𝝱</b>)x1e3).q(</a>));                
  
    my $file = $a->𝘄𝗿𝗶𝘁𝗲𝗖𝗼𝗺𝗽𝗿𝗲𝘀𝘀𝗲𝗱𝗙𝗶𝗹𝗲(q(zzz.xml.zip));                            
  
    my $A = readCompressedFile($file);                                             
  
    ok $a->equals($A);                                                             
  

=head2 readCompressedFile($file)

Read the specified B<$file> containing compressed L<Xml|https://en.wikipedia.org/wiki/XML> and return the root node.  Use L<writeCompressedFile|/writeCompressedFile> to write the B<$file>.

     Parameter  Description
  1  $file      File to read.

B<Example:>


    my $a = Data::Edit::Xml::new(q(<a>).(q(<b>𝝱</b>)x1e3).q(</a>));                
  
    my $file = $a->writeCompressedFile(q(zzz.xml.zip));                            
  
    my $A = 𝗿𝗲𝗮𝗱𝗖𝗼𝗺𝗽𝗿𝗲𝘀𝘀𝗲𝗱𝗙𝗶𝗹𝗲($file);                                             
  
    ok $a->equals($A);                                                             
  

This is a static method and so should be invoked as:

  Data::Edit::Xml::readCompressedFile


=head1 Autoload

Allow methods with constant parameters to be called as B<method_p1_p2>...(variable parameters) whenever it is easier to type underscores than (qw()).

=head2 AUTOLOAD()

Allow methods with constant parameters to be called as B<method_p1_p2>...(variable parameters) whenever it is easier to type underscores than (qw()).


B<Example:>


   {my $a = Data::Edit::Xml::new(<<END);                                          
  <a>
    <b>
      <c/>
    </b>
  </a>
  END
  
    my ($c, $b) = $a->byList;                                                     
  
    ok  $c->at_c_b_a;                                                             
  
    ok !$c->at_b;                                                                 
  
    ok  -t $c->change_d_c_b eq q(d);                                              
  
    ok !   $c->change_d_b;                                                        
  
  if (1)                                                                          
   {my $a = Data::Edit::Xml::new(<<END);
  <a><b><c/><d/><e/><f/></b></a>
  END
  
    ok -t $a->first_b__first_c__next__next_e__next eq q(f);
    ok   !$a->first_b__first_c__next__next_f;
   }
  


=head2 Data::Edit::Xml Definition


Well known attributes useful in respect to L<Xml|https://en.wikipedia.org/wiki/XML> that conforms to the L<Dita|http://docs.oasis-open.org/dita/dita/v1.3/os/part2-tech-content/dita-v1.3-os-part2-tech-content.html> standard.




=head3 Output fields


B<attributes> - The attributes of the specified B<$node>, see also: L</Attributes>.  The frequently used attributes: class, id, href, outputclass can be accessed by an L<lvalue method|http://perldoc.perl.org/perlsub.html#Lvalue-subroutines> method as in: $node->id = 'c1'.

B<audience> - Attribute B<audience> for a node as an L<lvalue method|http://perldoc.perl.org/perlsub.html#Lvalue-subroutines> B<sub>.    Use B<audienceX()> to return B<q()> rather than B<undef>.

B<class> - Attribute B<class> for a node as an L<lvalue method|http://perldoc.perl.org/perlsub.html#Lvalue-subroutines> B<sub>.       Use B<classX()> to return B<q()> rather than B<undef>.

B<conditions> - Conditional strings attached to a node, see L</Conditions>.

B<content> - Content of command: the nodes immediately below the specified B<$node> in the order in which they appeared in the source text, see also L</Contents>.

B<data> - A hash added to the node for use by the programmer during transformations. The data in this hash will not be printed by any of the L<printed|/Print> methods and so can be used to add data to the L<parse|/parse> tree that will not be seen in any output L<Xml|https://en.wikipedia.org/wiki/XML> produced from the L<parse|/parse> tree.

B<depthProfileLast> - The last known depth profile for this node as set by L<setDepthProfiles|/setDepthProfiles>.

B<errorsFile> - Error listing file. Use this parameter to explicitly set the name of the file that will be used to write any L<parse|/parse> errors to. By default this file is named: B<zzzParseErrors/out.data>.

B<forestNumbers> - Index to node by forest number as set by L<numberForest|/numberForest>.

B<guid> - Attribute B<guid> for a node as an L<lvalue method|http://perldoc.perl.org/perlsub.html#Lvalue-subroutines> B<sub>.        Use B<guidX()> to return B<q()> rather than B<undef>.

B<href> - Attribute B<href> for a node as an L<lvalue method|http://perldoc.perl.org/perlsub.html#Lvalue-subroutines> B<sub>.        Use B<hrefX()> to return B<q()> rather than B<undef>.

B<id> - Attribute B<id> for a node as an L<lvalue method|http://perldoc.perl.org/perlsub.html#Lvalue-subroutines> B<sub>.          Use B<idX()> to return B<q()> rather than B<undef>.

B<indexes> - Indexes to sub commands by tag in the order in which they appeared in the source text.

B<input> - Source of the L<parse|/parse> if this is the L<parser|/parse> root node. Use this parameter to specify some input either as a string or as a file name for the L<parser|/parse> to convert into a L<parse|/parse> tree.

B<inputFile> - Source file of the L<parse|/parse> if this is the L<parser|/parse> root node. Use this parameter to explicitly set the file to be L<parsed|/parse>.

B<inputString> - Source string of the L<parse|/parse> if this is the L<parser|/parse> root node. Use this parameter to explicitly set the string to be L<parsed|/parse>.

B<labels> - The labels attached to a node to provide addressability from other nodes, see: L</Labels>.

B<lang> - Attribute B<lang> for a node as an L<lvalue method|http://perldoc.perl.org/perlsub.html#Lvalue-subroutines> B<sub>.        Use B<langX()> to return B<q()> rather than B<undef>.

B<lineNumbers> - If true then save the line number.column number at which tag starts and ends on the xtrf attribute of each node.

B<navtitle> - Attribute B<navtitle> for a node as an L<lvalue method|http://perldoc.perl.org/perlsub.html#Lvalue-subroutines> B<sub>.    Use B<navtitleX()> to return B<q()> rather than B<undef>.

B<number> - Number of the specified B<$node>, see L<findByNumber|/findByNumber>.

B<numbering> - Last number used to number a node in this L<parse|/parse> tree.

B<numbers> - Nodes by number.

B<otherprops> - Attribute B<otherprops> for a node as an L<lvalue method|http://perldoc.perl.org/perlsub.html#Lvalue-subroutines> B<sub>.  Use B<otherpropsX()> to return B<q()> rather than B<undef>.

B<outputclass> - Attribute B<outputclass> for a node as an L<lvalue method|http://perldoc.perl.org/perlsub.html#Lvalue-subroutines> B<sub>. Use B<outputclassX()> to return B<q()> rather than B<undef>.

B<parent> - Parent node of the specified B<$node> or B<undef> if the L<parser|/parse> root node. See also L</Traversal> and L</Navigation>. Consider as read only.

B<parser> - L<Parser|/parse> details: the root node of a tree is the L<parser|/parse> node for that tree. Consider as read only.

B<props> - Attribute B<props> for a node as an L<lvalue method|http://perldoc.perl.org/perlsub.html#Lvalue-subroutines> B<sub>.       Use B<propsX()> to return B<q()> rather than B<undef>.

B<representationLast> - The last representation set for this node by one of: L<setRepresentationAsTagsAndText|/setRepresentationAsTagsAndText>.

B<style> - Attribute B<style> for a node as an L<lvalue method|http://perldoc.perl.org/perlsub.html#Lvalue-subroutines> B<sub>.       Use B<styleX()> to return B<q()> rather than B<undef>.

B<tag> - Tag name for the specified B<$node>, see also L</Traversal> and L</Navigation>. Consider as read only.

B<text> - Text of the specified B<$node> but only if it is a text node otherwise B<undef>, i.e. the tag is cdata() <=> L</isText> is true.

B<type> - Attribute B<type> for a node as an L<lvalue method|http://perldoc.perl.org/perlsub.html#Lvalue-subroutines> B<sub>.        Use B<typeX()> to return B<q()> rather than B<undef>.

B<xtrc> - Attribute B<xtrc> for a node as an L<lvalue method|http://perldoc.perl.org/perlsub.html#Lvalue-subroutines> B<sub>.        Use B<classX()> to return B<q()> rather than B<undef>.

B<xtrf> - Attribute B<xtrf> for a node as an L<lvalue method|http://perldoc.perl.org/perlsub.html#Lvalue-subroutines> B<sub>.        Use B<classX()> to return B<q()> rather than B<undef>.



=head2 Data::Edit::Xml::Example Definition


Description of a pcd method




=head3 Output fields


B<also> - See also

B<before> - Before file content minus root tag which will be added as <a>

B<code> - Pcd code

B<doc> - One line summary

B<method> - Method



=head2 Data::Edit::Xml::Table::Statistics Definition


Statistics about a table




=head3 Output fields


B<colSpec> - Number of colspec entries

B<colsAttribute> - Column attribute

B<maxBody> - Maximum number of entries in a body row regardless of padding rows

B<maxBodyMinusPadding> - Maximum number of entries in a body row after an padding entries have been removed

B<maxHead> - Maximum number of entries in a head row regardless of padding rows

B<maxHeadMinusPadding> - Maximum number of entries in a head row after an padding entries have been removed

B<minBody> - Maximum number of entries in a body row regardless of padding rows

B<minHead> - Maximum number of entries in a head row regardless of padding rows

B<rows> - Number of rows



=head1 Attributes


The following is a list of all the attributes in this package.  A method coded
with the same name in your package will over ride the method of the same name
in this package and thus provide your value for the attribute in place of the
default value supplied for this attribute by this package.

=head2 Replaceable Attribute List


DESTROY 


=head2 DESTROY

TO avoid seeing it in AUTOLOAD




=head1 Private Methods

=head2 reduceParseErroMessage($e)

Reduce the parse failure message to the bare essentials

     Parameter  Description
  1  $e         Error message

=head2 tree($parent, $parse)

Build a tree representation of the parsed L<Xml|https://en.wikipedia.org/wiki/XML> which can be easily traversed to look for things.

     Parameter  Description
  1  $parent    The parent node
  2  $parse     The remaining parse

=head2 addLineNumbers($string)

Add line numbers to the source

     Parameter  Description
  1  $string    Source string

=head2 disconnectLeafNode($node)

Remove a leaf node from the parse tree and make it into its own parse tree.

     Parameter  Description
  1  $node      Leaf node to disconnect.

=head2 reindexNode($node)

Index the children of a node so that we can access them by tag and number.

     Parameter  Description
  1  $node      Node to index.

=head2 indexNode($node)

Merge multiple text segments and set parent and parser after changes to a node

     Parameter  Description
  1  $node      Node to index.

=head2 normalizeWhiteSpace($string)

Normalize white space, remove comments DOCTYPE and L<Xml|https://en.wikipedia.org/wiki/XML> processors from a string

     Parameter  Description
  1  $string    String to normalize

This is a static method and so should be invoked as:

  Data::Edit::Xml::normalizeWhiteSpace


=head2 prettyStringHtml2($node, $depth)

Return a string of html representing a node of a L<parse|/parse> tree and all the nodes below it. Or use L<-p|/opString> $node

     Parameter  Description
  1  $node      Start node
  2  $depth     Optional depth.

=head2 prettyStringEnd($node)

Return a readable string representing a node of a L<parse|/parse> tree and all the nodes below it as a here document

     Parameter  Description
  1  $node      Start node

=head2 jsonString2($node)

Return a Json representation of a parse tree

     Parameter  Description
  1  $node      Start node of parse tree

=head2 jsonToXml2($node)

Convert a json string to an L<Xml|https://en.wikipedia.org/wiki/XML> parse tree

     Parameter  Description
  1  $node      Start node of parse tree

=head2 by2($node, $sub, @context)

Post-order traversal of a L<parse|/parse> tree

     Parameter  Description
  1  $node      Starting node
  2  $sub       Sub to call for each sub node
  3  @context   Accumulated context.

=head2 By22($node, $sub)

Post-order traversal of a L<parse|/parse> tree or sub tree calling the specified B<sub> at each node and returning the specified starting node. The B<sub> is passed references to the current node and all of its L<ancestors|/ancestry>. A reference to the current node is also made available via L<$_>. This is equivalent to the L<x=|/opBy> operator.

     Parameter  Description
  1  $node      Starting node
  2  $sub       Sub to call for each sub node

=head2 byX2($node, $sub, @context)

Post-order traversal of a L<parse|/parse> tree or sub tree calling the specified B<sub> within L<eval|http://perldoc.perl.org/functions/eval.html>B<{}> at each node and returning the specified starting node. The B<sub> is passed references to the current node and all of its L<ancestors|/ancestry>. The value of the current node is also made available via L<$_>.

     Parameter  Description
  1  $node      Starting node
  2  $sub       Sub to call
  3  @context   Accumulated context.

=head2 byX22($node, $sub, @context)

Post-order traversal of a L<parse|/parse> tree or sub tree calling the specified B<sub> within L<eval|http://perldoc.perl.org/functions/eval.html>B<{}> at each node and returning the specified starting node. The B<sub> is passed references to the current node and all of its L<ancestors|/ancestry>. The value of the current node is also made available via L<$_>.

     Parameter  Description
  1  $node      Starting node
  2  $sub       Sub to call
  3  @context   Accumulated context.

=head2 downX2($node, $sub, @context)

Pre-order traversal of a L<parse|/parse> tree or sub tree calling the specified B<sub> within L<eval|http://perldoc.perl.org/functions/eval.html>B<{}> at each node and returning the specified starting node. The B<sub> is passed references to the current node and all of its L<ancestors|/ancestry>. The value of the current node is also made available via L<$_>.

     Parameter  Description
  1  $node      Starting node
  2  $sub       Sub to call
  3  @context   Accumulated context.

=head2 downToDie2($node, $sub, @context)

Pre-order traversal of a L<parse|/parse> tree calling the specified B<sub> at each node as long as this sub does not L<die|http://perldoc.perl.org/functions/die.html>. The traversal of the current sub tree is halted and continue with the next sibling or parent if the called sub does L<die|http://perldoc.perl.org/functions/die.html>. The B<sub> is passed references to the current node and all of its L<ancestors|/ancestry> up to the node on which this sub was called. A reference to the current node is also made available via L<$_>.

Returns the start node regardless of the outcome of calling B<sub>.

     Parameter  Description
  1  $node      Starting node
  2  $sub       Sub to call
  3  @context   Accumulated context.

=head2 parseLineLocation($loc)

Parse a line location

     Parameter  Description
  1  $loc       Location

This is a static method and so should be invoked as:

  Data::Edit::Xml::parseLineLocation


=head2 atPositionMatch($tag, $match)

Confirm that a string matches a match expression.

     Parameter  Description
  1  $tag       Starting node
  2  $match     Ancestry.

=head2 numberNode($node)

Ensure that the specified B<$node> has a number.

     Parameter  Description
  1  $node      Node

=head2 countTagNames2($node, $count)

Return a reference to a hash showing the number of instances of each tag on and below the specified B<$node>.

     Parameter  Description
  1  $node      Node
  2  $count     Count of tags so far.

=head2 countNonEmptyTags2($node, $count)

Count the instances of non empty tags on and below the specified B<$node>.

     Parameter  Description
  1  $node      Node
  2  $count     Count of tags so far.

=head2 countTexts2($node, $count)

Count the instances of non empty texts on and below the specified B<$node>.

     Parameter  Description
  1  $node      Node
  2  $count     Texts so far

=head2 countWords($node)

Count instances of words in texts

     Parameter  Description
  1  $node      Node

B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <a>
    <b>aaa bbb ccc</b>
    <c>AAA BBB CCC</c>
    <c>aaa AAA</c>
  </a>
  END
  
    my $t = $a->𝗰𝗼𝘂𝗻𝘁𝗪𝗼𝗿𝗱𝘀;
  
    is_deeply $t, { AAA => 2, aaa => 2, bbb => 1, BBB => 1, CCC => 1, ccc => 1 };
  

=head2 createRequiredCleanUp($node, @text)

Create a required clean up $node with the specified B<@text>

     Parameter  Description
  1  $node      Node
  2  @text      Clean up messages

=head2 ditaMergeListsOnce($node)

Merge the specified B<$node> with the preceding or following list or steps or substeps if possible and return the specified B<$node> regardless.

     Parameter  Description
  1  $node      Node

=head2 ditaNumberOfColumnsInRow($row)

Return estimate of the number of columns in a row

     Parameter  Description
  1  $row       Row

=head2 ditaAddPadEntriesToTGroupRows($tgroup, $nEntries)

Adding padding entries to a tgroup to make sure every row has the same number of entries

     Parameter  Description
  1  $tgroup    TGroup node
  2  $nEntries  Number of entries

B<Example:>


    my $a = Data::Edit::Xml::new(<<END);
  <table>
    <tgroup cols="1">
      <colspec/>
      <thead>
        <row>
          <entry>aaaa</entry>
        </row>
        <row>
          <entry>bbbb</entry>
        </row>
      </thead>
      <tbody>
        <row>
          <entry>cccc</entry>
        </row>
        <row>
          <entry>dddd</entry>
        </row>
      </tbody>
    </tgroup>
  </table>
  END
  
    my $g = $a->go_tgroup;
  
    $g->𝗱𝗶𝘁𝗮𝗔𝗱𝗱𝗣𝗮𝗱𝗘𝗻𝘁𝗿𝗶𝗲𝘀𝗧𝗼𝗧𝗚𝗿𝗼𝘂𝗽𝗥𝗼𝘄𝘀(2);
  
    is_deeply $g->ditaTGroupStatistics,
  bless({
    colsAttribute => 1,
    colSpec => 1,
    maxBody => 2,
    maxBodyMinusPadding => 2,
    maxHead => 2,
    maxHeadMinusPadding => 2,
    minBody => 2,
    minHead => 2,
    rows    => 4,
  }, "Data::Edit::Xml::Table::Statistics");
  
    $g->fixTGroup;
  
    ok -p $a eq <<END;
  <table>
    <tgroup cols="2">
      <colspec colname="c1" colnum="1" colwidth="1*"/>
      <colspec colname="c2" colnum="2" colwidth="1*"/>
      <thead>
        <row>
          <entry>aaaa</entry>
          <entry/>
        </row>
        <row>
          <entry>bbbb</entry>
          <entry/>
        </row>
      </thead>
      <tbody>
        <row>
          <entry>cccc</entry>
          <entry/>
        </row>
        <row>
          <entry>dddd</entry>
          <entry/>
        </row>
      </tbody>
    </tgroup>
  </table>
  END
  
    is_deeply $g->ditaTGroupStatistics,
  bless({
    colsAttribute => 2,
    colSpec => 2,
    maxBody => 2,
    maxBodyMinusPadding => 2,
    maxHead => 2,
    maxHeadMinusPadding => 2,
    minBody => 2,
    minHead => 2,
    rows    => 4,
  
  }, "Data::Edit::Xml::Table::Statistics");
  
    $a->fixTable;
    is_deeply $g->ditaTGroupStatistics,
  bless({
    colsAttribute => 2,
    colSpec => 2,
    maxBody => 2,
    maxBodyMinusPadding => 2,
    maxHead => 2,
    maxHeadMinusPadding => 2,
    minBody => 2,
    minHead => 2,
    rows    => 4,
  }, "Data::Edit::Xml::Table::Statistics");
  
  

=head2 topicTypeAndBody($type)

Topic type and corresponding body.

     Parameter  Description
  1  $type      Type from qw(bookmap concept reference task)

=head2 getSectionHeadingLevel($o)

Get the heading level from a section tag.

     Parameter  Description
  1  $o         Node

=head2 unwrapSingleParentsOfSection($tree)

Unwrap single parents of section: in word documents the header is often buried in a list to gain a section number - here we remove these unnecessary items

     Parameter  Description
  1  $tree      Parse tree

=head2 extendSectionToNextSection($tree)

Extend a section tag until it meets the next section tag

     Parameter  Description
  1  $tree      Parse tree

=head2 structureAdjacentSectionsByLevel($tree)

Structure adjacent sections by level

     Parameter  Description
  1  $tree      Parse tree

=head2 ditaUnderPNotConbody()

Return a hash of items that L<Dita|http://docs.oasis-open.org/dita/dita/v1.3/os/part2-tech-content/dita-v1.3-os-part2-tech-content.html> permits under B<p> but not directly under B<conbody>


=head2 printAttributesReplacingIdsWithLabels($node)

Print the attributes of a node replacing the id with the labels.

     Parameter  Description
  1  $node      Node whose attributes are to be printed.

=head2 printAttributesExtendingIdsWithLabels($node)

Print the attributes of a node extending the id with the labels.

     Parameter  Description
  1  $node      Node whose attributes are to be printed.

=head2 checkParentage($x)

Check the parent pointers are correct in a L<parse|/parse> tree.

     Parameter  Description
  1  $x         Parse tree.

=head2 checkParser($x)

Check that every node has a L<parse|/parse>r.

     Parameter  Description
  1  $x         Parse tree.

=head2 nn($s)

Replace new lines in a string with N to make testing easier.

     Parameter  Description
  1  $s         String.

=head2 extractDocumentationFlags($flags, $method)

Generate documentation for a method with a user flag.

     Parameter  Description
  1  $flags     Flags
  2  $method    Method name.

=head2 writeContentToGitHub($file, $content)

Upload the contents of a string to a file on github

     Parameter  Description
  1  $file      File on github
  2  $content   File contents

=head2 writeFileToGitHub($file, $localFile)

Upload the contents of the specified local file to a file on github

     Parameter   Description
  1  $file       File name on github
  2  $localFile  Local file name

=head2 processModuleDescription($d)

Process module description to write a description of editing Xml to GitHub

     Parameter  Description
  1  $d         Module description


=head1 Synonyms

B<ca> is a synonym for L<contentAfter|/contentAfter> - Return a list of all the sibling nodes following the specified B<$node> or an empty list if the specified B<$node> is last or not in the optional B<@context>.

B<cb> is a synonym for L<contentBefore|/contentBefore> - Return a list of all the sibling nodes preceding the specified B<$node> (in the normal sibling order) or an empty list if the specified B<$node> is last or not in the optional B<@context>.

B<cc> is a synonym for L<change|/change> - Change the name of the specified B<$node>, optionally  confirming that the B<$node> is in a specified context and return the B<$node>.

B<ck> is a synonym for L<change|/change> - Change the name of the specified B<$node>, optionally  confirming that the B<$node> is in a specified context and return the B<$node>.

B<cr> is a synonym for L<countReport|/countReport> - Count tags, attributes, words below the specified node

B<cs> is a synonym for L<ditaConvertConceptToSection|/ditaConvertConceptToSection> - Convert a Dita B<concept> to a B<$section> .

B<ct> is a synonym for L<ditaConvertConceptToTask|/ditaConvertConceptToTask> - Convert a Dita B<concept> to a B<task> by representing B<ol> as B<steps>.

B<firstLeaf> is a synonym for L<downWhileFirst|/downWhileFirst> - Move down from the specified B<$node> as long as each lower node is a first node.

B<get> is a synonym for L<getNodeAs|/getNodeAs> - Return the specified B<$node> unless it is possible to return the node saved with L<putNodeAs> under the specified B<name> and we are optionally in the specified B<@context>.

B<h> is a synonym for L<help|/help> - Get help for a node and the editor

B<lastLeaf> is a synonym for L<downWhileLast|/downWhileLast> - Move down from the specified B<$node> as long as each lower node is a last node.

B<ma> is a synonym for L<moveSelectionAfter|/moveSelectionAfter> - Move the current selection (if there is one) after the specified B<$node> in the optional B<@context> and return the specified B<$node> on success else B<undef>.

B<mb> is a synonym for L<moveSelectionBefore|/moveSelectionBefore> - Move the current selection (if there is one) before the specified B<$node> in the optional B<@context> and return the specified B<$node> on success else B<undef>.

B<mf> is a synonym for L<moveSelectionFirst|/moveSelectionFirst> - Move the current selection (if there is one) so that it is first under the specified B<$node> in the optional B<@context> and return the specified B<$node> on success else B<undef>.

B<ml> is a synonym for L<moveSelectionLast|/moveSelectionLast> - Move the current selection (if there is one) so that it is last under the specified B<$node> in the optional B<@context> and return the specified B<$node> on success else B<undef>.

B<mln> is a synonym for L<mergeLikeNext|/mergeLikeNext> - Merge a B<$node> in an optional context with the next node if the two have the same tag by placing the next node first in the current B<$node> and unwrapping the next node.

B<mlp> is a synonym for L<mergeLikePrev|/mergeLikePrev> - Merge a B<$node> in an optional context with the previous node if the two have the same tag by placing the previous node first in the current B<$node> and unwrapping the previous node.

B<mocln> is a synonym for L<mergeOnlyChildLikeNext|/mergeOnlyChildLikeNext> - Merge a B<$node> if it is the only child of its parent with a preceding node of the same name that is also the only child of its parent and return the specified B<$node> or B<undef> if the request fails.

B<moclp> is a synonym for L<mergeOnlyChildLikePrev|/mergeOnlyChildLikePrev> - Merge a B<$node> if it is the only child of its parent with a preceding node of the same name that is also the only child of its parent and return the specified B<$node> or B<undef> if the request fails.

B<oat> is a synonym for L<overAllTags|/overAllTags> - Return the specified b<$node> if all of it's child nodes L<match|/atPositionMatch> the specified <@tags> else return B<undef>.

B<oft> is a synonym for L<overFirstTags|/overFirstTags> - Return the specified b<$node> if the first of it's child nodes L<match|/atPositionMatch> the specified <@tags> else return B<undef>.

B<olt> is a synonym for L<overLastTags|/overLastTags> - Return the specified b<$node> if the last of it's child nodes L<match|/atPositionMatch> the specified <@tags> else return B<undef>.

B<p> is a synonym for L<parentage|/parentage> - Return a reference to an array of the nodes along the path from the root to the specified B<$Node> inclusive.

B<put> is a synonym for L<putNodeAs|/putNodeAs> - Return the specified B<$node> after saving it under the specified B<$name> if we are in the optional B<@context>.

B<r> is a synonym for L<dupPutNext|/dupPutNext> - Duplicate the specified B<$tree> in the optional B<@context>, place the new tree next after $tree and return the root of the new tree on success else B<undef>.

B<rN> is a synonym for L<dupPutNextN|/dupPutNextN> - Duplicate the specified B<$tree> B<$N> times in the optional B<@context>, placing each copy after $tree and return the last new node created on success else B<undef>.

B<rn> is a synonym for L<reportNode|/reportNode> - Print the parse tree starting at the specified B<$node> identified by the specified B<$label> if we are in the optional B<@context>.

B<rna> is a synonym for L<reportNodeAttributes|/reportNodeAttributes> - Print the attributes of the specified B<$node> identified by the specified B<$label> if we are in the optional B<@context>.

B<rnc> is a synonym for L<reportNodeContext|/reportNodeContext> - Print the context of the specified B<$node> identified by the specified B<$label> if we are in the optional B<@context>.

B<sc> is a synonym for L<ditaConvertSectionToConcept|/ditaConvertSectionToConcept> - Convert a Dita B<$section> to a B<concept>.

B<se> is a synonym for L<setSelectionEnd|/setSelectionEnd> - Set the selection to end at the specified B<$node> in the optional B<@context> and return the specified B<$node> on success else B<undef>.

B<sfs> is a synonym for L<swapFirstSibling|/swapFirstSibling> - Swap B<$node> with its first sibling node and return B<$node>.

B<sls> is a synonym for L<swapLastSibling|/swapLastSibling> - Swap B<$node> with its last sibling node and return B<$node>.

B<sn> is a synonym for L<swapNext|/swapNext> - Swap B<$node> with its following node if $B<$node> matches the first element of the specified context and the next node matches the rest.

B<sp> is a synonym for L<swapPrev|/swapPrev> - Swap B<$node> with its preceding node if $B<$node> matches the first element of the specified context and the previous node matches the rest.

B<spa> is a synonym for L<splitParentAfter|/splitParentAfter> - Finish and restart the parent of the specified B<$node> just after the specified B<$node> and return the newly created parent node on success or B<undef> on failure.

B<spb> is a synonym for L<splitParentBefore|/splitParentBefore> - Finish and restart the parent of the specified B<$node> just before the specified B<$node> and return the newly created parent node on success or B<undef> on failure.

B<ss> is a synonym for L<setSelectionStart|/setSelectionStart> - Set the selection to start at the specified B<$node> in the optional B<@context> and return the specified B<$node> on success else B<undef>.

B<u> is a synonym for L<unwrap|/unwrap> - Unwrap the specified B<$node> if in the optional B<@context> by replacing the node with its contents.

B<wcw> is a synonym for L<wrapContentWith|/wrapContentWith> - Wrap the content of the specified B<$node> in a new node created from the specified B<$tag> and B<%attributes>: the specified B<$node> then contains just the new node which, in turn, contains all the content of the specified B<$node>.

B<ww> is a synonym for L<wrapWithAll|/wrapWithAll> - Wrap this B<$node> wrapped with the specified B<@tags> and return the last wrapping node.

B<x> is a synonym for L<cut|/cut> - Cut out and return the specified B<$node> so that it can be reinserted else where in the L<parse|/parse> tree.



=head1 Index


1 L<above|/above> - Return the first node if the first node is above the second node optionally checking that the first node is in the specified context otherwise return B<undef>

2 L<abovePath|/abovePath> - Return the nodes along the path from the first node down to the second node when the first node is above the second node else return B<()>.

3 L<addAttr|/addAttr> - Check the specified B<$node> for the specified B<%attributes> and add any that are not already set.

4 L<addConditions|/addConditions> - Given a B<$node> add the specified B<@conditions> and return the node.

5 L<addFirst|/addFirst> - Add a new node L<first|/first> below the specified B<$node> with the specified B<$tag> unless a node with that tag already exists in that position.

6 L<addFirstAsText|/addFirstAsText> - Add a new text node first below the specified B<$node> and return the new node unless a text node already exists there and starts with the same text in which case return the existing B<$node>.

7 L<addLabels|/addLabels> - Add the named labels to the specified B<$node> and return the number of labels added.

8 L<addLast|/addLast> - Add a new node L<last|/last> below the specified B<$node> with the specified B<$tag> unless a node with that tag already exists in that position.

9 L<addLastAsText|/addLastAsText> - Add a new text node last below the specified B<$node> and return the new node unless a text node already exists there and ends with the same text in which case return the existing B<$node>.

10 L<addLineNumbers|/addLineNumbers> - Add line numbers to the source

11 L<addNext|/addNext> - Add a new node L<next|/next> the specified B<$node> and return the new node unless a node with that tag already exists in which case return the existing B<$node>.

12 L<addNextAsText|/addNextAsText> - Add a new text node after the specified B<$node> and return the new node unless a text node already exists there and starts with the same text in which case return the existing B<$node>.

13 L<addPrev|/addPrev> - Add a new node L<before|/prev> the specified B<$node> with the specified B<$tag> unless a node with that tag already exists in that position.

14 L<addPrevAsText|/addPrevAsText> - Add a new text node before the specified B<$node> and return the new node unless a text node already exists there and ends with the same text in which case return the existing B<$node>.

15 L<addSingleChild|/addSingleChild> - Wrap the content of a specified B<$node> in a new node with the specified B<$tag> unless the content is already wrapped in a single child with the specified B<$tag>.

16 L<addWrapWith|/addWrapWith> - L<Wrap|/wrap> the specified B<$node> with the specified tag if the node is not already wrapped with such a tag and return the new node unless a node with that tag already exists in which case return the existing B<$node>.

17 L<adjacent|/adjacent> - Return the first node if it is adjacent to the second node else B<undef>.

18 L<adoptChild|/adoptChild> - Lift the specified B<$child> node up until it is an immediate child of the specified B<$parent> node by splitting any intervening nodes.

19 L<after|/after> - Return the first node if it occurs after the second node in the L<parse|/parse> tree optionally checking that the first node is in the specified context or else B<undef> if the node is L<above|/above>, L<below|/below> or L<before|/before> the target.

20 L<allConditions|/allConditions> - Return the B<$node> if it has all of the specified B<@conditions>, else return B<undef>

21 L<an|/an> - Return the next node if the specified B<$node> has the tag specified by B<$current> and the next node is in the specified B<@context>.

22 L<ancestry|/ancestry> - Return a list containing: (the specified B<$node>, its parent, its parent's parent etc.

23 L<anyCondition|/anyCondition> - Return the B<$node> if it has any of the specified B<@conditions>, else return B<undef>

24 L<ap|/ap> - Return the previous node if the specified B<$node> has the tag specified by B<$current> and the previous node is in the specified B<@context>.

25 L<apn|/apn> - Return (previous node, next node) if the B<$previous> and B<$current> nodes have the specified tags and the next node is in the specified B<@context> else return B<()>.

26 L<approxLocation|/approxLocation> - Return the line number.

27 L<at|/at> - Confirm that the specified B<$node> has the specified L<ancestry|/ancestry>.

28 L<atOrBelow|/atOrBelow> - Confirm that the node or one of its ancestors has the specified context as recognized by L<at|/at> and return the first node that matches the context or B<undef> if none do.

29 L<atPositionMatch|/atPositionMatch> - Confirm that a string matches a match expression.

30 L<atStringContentMatches|/atStringContentMatches> - Confirm that we are on a B<$node> whose contents, represented as a string, matches the specified regular expression B<$re> in the optional B<@context>.

31 L<atText|/atText> - Confirm that we are on a text node whose text value matches a regular expression in the optional B<@context>.

32 L<atTop|/atTop> - Return the current node if it is the root == top of a parse tree else return B<undef>.

33 L<attr|/attr> - Return the value of an attribute of the current node as an L<lvalue method|http://perldoc.perl.org/perlsub.html#Lvalue-subroutines> B<sub>.

34 L<attrAt|/attrAt> - Return the specified B<$node> if it has the specified B<$attribute> and the $node is in the optional B<@context> else return B<undef>.

35 L<attrCount|/attrCount> - Return the number of attributes in the specified B<$node>, optionally ignoring the specified names from the count.

36 L<attrs|/attrs> - Return the values of the specified attributes of the current node as a list

37 L<attrsNone|/attrsNone> - Check that the specified B<$node> has no attributes.

38 L<attrValueAt|/attrValueAt> - Return the specified B<$node> if it has the specified B<$attribute> with the specified B<$value> and the $node is in the optional B<@context> else return B<undef>.

39 L<attrX|/attrX> - Return the value of the specified B<$attribute> of the specified B<$node> or B<q()> if the B<$node> does not have such an attribute.

40 L<AUTOLOAD|/AUTOLOAD> - Allow methods with constant parameters to be called as B<method_p1_p2>.

41 L<before|/before> - Return the first node if it occurs before the second node in the L<parse|/parse> tree optionally checking that the first node is in the specified context or else B<undef> if the node is L<above|/above>, L<below|/below> or L<before|/before> the target.

42 L<below|/below> - Return the first node if the first node is below the second node optionally checking that the first node is in the specified context otherwise return B<undef>

43 L<belowPath|/belowPath> - Return the nodes along the path from the first node up to the second node when the first node is below the second node else return B<()>.

44 L<bitsNodeTextBlank|/bitsNodeTextBlank> - Return a bit string that shows if there are any non text nodes, text nodes or blank text nodes under a node.

45 L<breakIn|/breakIn> - Concatenate the nodes following and preceding the start node, unwrapping nodes whose tag matches the start node and return the start node.

46 L<breakInBackwards|/breakInBackwards> - Concatenate the nodes preceding the start node, unwrapping nodes whose tag matches the start node and return the start node in the manner of L<breakIn|/breakIn>.

47 L<breakInForwards|/breakInForwards> - Concatenate the nodes following the start node, unwrapping nodes whose tag matches the start node and return the start node in the manner of L<breakIn|/breakIn>.

48 L<breakOut|/breakOut> - Lift child nodes with the specified tags under the specified parent node splitting the parent node into clones and return the cut out original node.

49 L<breakOutChild|/breakOutChild> - Lift the specified B<$node> up one level splitting its parent.

50 L<by|/by> - Post-order traversal of a L<parse|/parse> tree or sub tree calling the specified B<sub> at each node and returning the specified starting node.

51 L<by2|/by2> - Post-order traversal of a L<parse|/parse> tree

52 L<By22|/By22> - Post-order traversal of a L<parse|/parse> tree or sub tree calling the specified B<sub> at each node and returning the specified starting node.

53 L<byList|/byList> - Return a list of all the nodes at and below a specified B<$node> in post-order or the empty list if the B<$node> is not in the optional B<@context>.

54 L<byReverse|/byReverse> - Reverse post-order traversal of a L<parse|/parse> tree or sub tree calling the specified B<sub> at each node and returning the specified starting B<$node>.

55 L<byReverseList|/byReverseList> - Return a list of all the nodes at and below a specified B<$node> in reverse preorder or the empty list if the specified B<$node> is not in the optional B<@context>.

56 L<byReverseX|/byReverseX> - Reverse post-order traversal of a L<parse|/parse> tree or sub tree below the specified B<$node> calling the specified B<sub> within L<eval|http://perldoc.perl.org/functions/eval.html>B<{}> at each node and returning the specified starting B<$node>.

57 L<byX|/byX> - Post-order traversal of a L<parse|/parse> tree calling the specified B<sub> at each node as long as this sub does not L<die|http://perldoc.perl.org/functions/die.html>.

58 L<byX2|/byX2> - Post-order traversal of a L<parse|/parse> tree or sub tree calling the specified B<sub> within L<eval|http://perldoc.perl.org/functions/eval.html>B<{}> at each node and returning the specified starting node.

59 L<byX22|/byX22> - Post-order traversal of a L<parse|/parse> tree or sub tree calling the specified B<sub> within L<eval|http://perldoc.perl.org/functions/eval.html>B<{}> at each node and returning the specified starting node.

60 L<c|/c> - Return an array of all the nodes with the specified tag below the specified B<$node>.

61 L<cdata|/cdata> - The name of the tag to be used to represent text - this tag must not also be used as a command tag otherwise the parser will L<confess|http://perldoc.perl.org/Carp.html#SYNOPSIS/>.

62 L<change|/change> - Change the name of the specified B<$node>, optionally  confirming that the B<$node> is in a specified context and return the B<$node>.

63 L<changeAttr|/changeAttr> - Rename attribute B<$old> to B<$new> in the specified B<$node> with optional context B<@context> unless attribute B<$new> is already set and return the B<$node>.

64 L<changeAttributeValue|/changeAttributeValue> - Apply a sub to the value of an attribute of the specified B<$node>.

65 L<changeAttrValue|/changeAttrValue> - Rename attribute B<$old> to B<$new> with new value B<$newValue> on the specified B<$node> in the optional B<@context> unless attribute B<$new> is already set or the value of the B<$old> attribute is not B<$oldValue>.

66 L<changeKids|/changeKids> - Change the names of all the immediate children of the specified B<$node>, if they match the optional B<@context>, to the specified B<$tag> and return the B<$node>.

67 L<changeOrDeleteAttr|/changeOrDeleteAttr> - Rename attribute B<$old> to B<$new> in the specified B<$node> in the optional B<@context> unless attribute B<$new> is already set in which case delete attribute B<$old>.

68 L<changeOrDeleteAttrValue|/changeOrDeleteAttrValue> - Rename attribute B<$old> to B<$new> with new value B<$newValue> on the specified B<$node> in the optional B<@context> unless attribute B<$new> is already set or the value of the B<$old> attribute is not B<$oldValue> in which cases the B<$old> attribute is deleted.

69 L<changeReasonCommentSelectionSpecification|/changeReasonCommentSelectionSpecification> - Provide a specification to select L<change reason comments|/crc> to be inserted as text into a L<parse|/parse> tree.

70 L<changeText|/changeText> - Change the content of the specified text B<$node> that matches a regular expression B<$rf> presented as a string to a string B<$rt> in the optional B<@context> and return the specified $node else return B<undef>.

71 L<changeTextToSpace|/changeTextToSpace> - Change each instance of the content of the specified text B<$node> that matches a regular expression B<$re> to one space and return the specified $node else return B<undef>.

72 L<checkParentage|/checkParentage> - Check the parent pointers are correct in a L<parse|/parse> tree.

73 L<checkParser|/checkParser> - Check that every node has a L<parse|/parse>r.

74 L<childOf|/childOf> - Returns the specified B<$child> node if it is a child of the specified B<$parent> node and the B<$child> node is in the specified optional context.

75 L<clone|/clone> - Return a clone of the entire L<parse|/parse> tree which is created using the fast L<Storable::dclone> method.

76 L<closestLocation|/closestLocation> - Return the nearest node with line number.

77 L<commonAdjacentAncestors|/commonAdjacentAncestors> - Given two nodes, find a pair of adjacent ancestral siblings if such a pair exists else return B<()>.

78 L<commonAncestor|/commonAncestor> - Find the most recent common ancestor of the specified nodes or B<undef> if there is no common ancestor.

79 L<concatenate|/concatenate> - Concatenate two successive nodes and return the B<$target> node.

80 L<concatenateSiblings|/concatenateSiblings> - Concatenate the nodes that precede and follow the specified B<$node> in the optioonal B<@context> as long as they have the same tag as the specified B<$node> and return the specified B<$node>.

81 L<condition|/condition> - Return the B<$node> if it has the specified B<$condition> and is in the optional B<@context>, else return B<undef>

82 L<containsSingleText|/containsSingleText> - Return the single text element below the specified B<$node> else return B<undef>.

83 L<contentAfter|/contentAfter> - Return a list of all the sibling nodes following the specified B<$node> or an empty list if the specified B<$node> is last or not in the optional B<@context>.

84 L<contentAfterAsTags|/contentAfterAsTags> - Return a string containing the tags of all the sibling nodes following the specified B<$node> separated by single spaces or the empty string if the node is empty or B<undef> if the node does not match the optional context.

85 L<contentAfterAsTags2|/contentAfterAsTags2> - Return a string containing the tags of all the sibling nodes following the specified B<$node> separated by two spaces with a single space preceding the first tag and a single space following the last tag or the empty string if the node is empty or B<undef> if the node does not match the optional context.

86 L<contentAsTags|/contentAsTags> - Return a string containing the tags of all the child nodes of the specified B<$node> separated by single spaces or the empty string if the node is empty or B<undef> if the node does not match the optional context.

87 L<contentAsTags2|/contentAsTags2> - Return a string containing the tags of all the child nodes of the specified B<$node> separated by two spaces with a single space preceding the first tag and a single space following the last tag or the empty string if the node is empty or B<undef> if the node does not match the optional context.

88 L<contentBefore|/contentBefore> - Return a list of all the sibling nodes preceding the specified B<$node> (in the normal sibling order) or an empty list if the specified B<$node> is last or not in the optional B<@context>.

89 L<contentBeforeAsTags|/contentBeforeAsTags> - Return a string containing the tags of all the sibling nodes preceding the specified B<$node> separated by single spaces or the empty string if the node is empty or B<undef> if the node does not match the optional context.

90 L<contentBeforeAsTags2|/contentBeforeAsTags2> - Return a string containing the tags of all the sibling nodes preceding the specified B<$node> separated by two spaces with a single space preceding the first tag and a single space following the last tag or the empty string if the node is empty or B<undef> if the node does not match the optional context.

91 L<contents|/contents> - Return a list of all the nodes contained by the specified B<$node> or an empty list if the node is empty or not in the optional B<@context>.

92 L<context|/context> - Return a string containing the tag of the starting node and the tags of all its ancestors separated by single spaces.

93 L<copyAttrs|/copyAttrs> - Copy all the attributes of the source node to the target node, or, just the named attributes if the optional list of attributes to copy is supplied, overwriting any existing attributes in the target node and return the source node.

94 L<copyAttrsFromParent|/copyAttrsFromParent> - Copy all the attributes from the parent (if there is one) of the current B<$node> to $node  or just the named B<@attributes> and return $node.

95 L<copyAttrsToParent|/copyAttrsToParent> - Copy all the attributes of the specified B<$node> to its parent (if there is one) or just the named B<@attributes> and return $node.

96 L<copyLabels|/copyLabels> - Copy all the labels from the source node to the target node and return the source node.

97 L<copyLabelsAndIdsInTree|/copyLabelsAndIdsInTree> - Copy all the labels and ids in the source parse tree to the matching nodes in the target parse tree.

98 L<copyNewAttrs|/copyNewAttrs> - Copy all the attributes of the source node to the target node, or, just the named attributes if the optional list of attributes to copy is supplied, without overwriting any existing attributes in the target node and return the source node.

99 L<count|/count> - Return the count of the number of instances of the specified tags under the specified B<$node>, either by tag in array context or in total in scalar context.

100 L<countAttrNames|/countAttrNames> - Return a reference to a hash showing the number of instances of each attribute on and below the specified B<$node>.

101 L<countAttrNamesAndValues|/countAttrNamesAndValues> - Return a reference to a hash showing the number of instances of each attribute name and value on and below the specified B<$node>.

102 L<countAttrNamesOnTagExcluding|/countAttrNamesOnTagExcluding> - Count the number of attributes owned by the specified B<$node> that are not in the specified list.

103 L<countAttrValues|/countAttrValues> - Return a reference to a hash showing the number of instances of each attribute value on and below the specified B<$node>.

104 L<countLabels|/countLabels> - Return the count of the number of labels at a node.

105 L<countNonEmptyTags|/countNonEmptyTags> - Return a reference to a hash showing the number of instances of each non empty tag on and below the specified B<$node> excluding any tags named in B<@exclude>.

106 L<countNonEmptyTags2|/countNonEmptyTags2> - Count the instances of non empty tags on and below the specified B<$node>.

107 L<countOutputClasses|/countOutputClasses> - Count instances of outputclass attributes

108 L<countReport|/countReport> - Count tags, attributes, words below the specified node

109 L<countTagNames|/countTagNames> - Return a reference to a hash showing the number of instances of each tag on and below the specified B<$node> excluding any tags named in B<@exclude>.

110 L<countTagNames2|/countTagNames2> - Return a reference to a hash showing the number of instances of each tag on and below the specified B<$node>.

111 L<countTags|/countTags> - Count the number of tags in a L<parse|/parse> tree.

112 L<countTexts|/countTexts> - Return a reference to a hash showing the incidence of texts on and below the specified B<$node>.

113 L<countTexts2|/countTexts2> - Count the instances of non empty texts on and below the specified B<$node>.

114 L<countWords|/countWords> - Count instances of words in texts

115 L<crc|/crc> - Insert a comment consisting of a code and an optional reason as text into the L<parse|/parse> tree to indicate the location of changes to the L<parse|/parse> tree.

116 L<createGuidId|/createGuidId> - Create an id for the specified B<$node> in the optional B<@context> from the md5Sum of its content moving any existing id to the labels associated with the B<$node> and return the existing B<$node>.

117 L<createPatch|/createPatch> - Create a patch that moves the source L<parse|/parse> tree to the target L<parse|/parse> tree node as long as they have the same tag and id structure with each id being unique.

118 L<createRequiredCleanUp|/createRequiredCleanUp> - Create a required clean up $node with the specified B<@text>

119 L<cText|/cText> - Return an array of all the text nodes immediately below the specified B<$node>.

120 L<cut|/cut> - Cut out and return the specified B<$node> so that it can be reinserted else where in the L<parse|/parse> tree.

121 L<cutFirst|/cutFirst> - Cut out the first node below the specified B<$node> if it is in the optional B<@context> and push it on the cut out stack.

122 L<cutIfEmpty|/cutIfEmpty> - Cut out and return the specified B<$node> so that it can be reinserted else where in the L<parse|/parse> tree if it is empty.

123 L<cutLast|/cutLast> - Cut out the last node below the specified B<$node> if it is in the optional B<@context> and push it on the cut out stack.

124 L<cutNext|/cutNext> - Cut out the next node beyond the specified B<$node> if it is in the optional B<@context> and push it on the cut out stack.

125 L<cutPrev|/cutPrev> - Cut out the previous node before the specified B<$node> if it is in the optional B<@context> and push it on the cut out stack.

126 L<Data::Edit::Xml::Patch::install|/Data::Edit::Xml::Patch::install> - Replay a patch created by L<createPatch|/createPatch> against a L<parse|/parse> tree that has the same tag and id structure with each id being unique.

127 L<deleteAttr|/deleteAttr> - Delete the named attribute in the specified B<$node>, optionally check its value first, returning the value of the attribute or B<undef> if the attribute does not exist on this node.

128 L<deleteAttrs|/deleteAttrs> - Delete the specified attributes of the specified B<$node> without checking their values and return the node.

129 L<deleteAttrsInTree|/deleteAttrsInTree> - Delete the specified attributes of the specified B<$node> and all the nodes under it and return the specified B<$node>.

130 L<deleteAttrValueAtInTree|/deleteAttrValueAtInTree> - Delete all instances of the specified B<$attribute> with the specified B<$value> in the specified B<@context> in the specified B<$tree> and return the modified B<$tree>.

131 L<deleteConditions|/deleteConditions> - Given a B<$node> delete any B<@conditions> applied to the $node and return the $node.

132 L<deleteContent|/deleteContent> - Delete the content of the specified B<$node>.

133 L<deleteLabels|/deleteLabels> - Delete the specified labels in the specified B<$node> or all labels if no labels have are specified and return that node.

134 L<depth|/depth> - Returns the depth of the specified B<$node>, the  depth of a root node is zero.

135 L<depthProfile|/depthProfile> - Returns the depth profile of the tree rooted at the specified B<$node>.

136 L<diff|/diff> - Return () if the dense string representations of the two nodes are equal, else up to the first N (default 16) characters of the common prefix before the point of divergence and the remainder of the string representation of each node from the point of divergence.

137 L<disconnectLeafNode|/disconnectLeafNode> - Remove a leaf node from the parse tree and make it into its own parse tree.

138 L<disordered|/disordered> - Return the first node that is out of the specified order when performing a pre-ordered traversal of the L<parse|/parse> tree.

139 L<ditaAbsoluteHref|/ditaAbsoluteHref> - Return the absolute value of the href of a specified B<$node> relative to the specified B<$sourceFile> or B<undef> if the node has no B<href> attribute.

140 L<ditaAddColSpecToTGroup|/ditaAddColSpecToTGroup> - Add the specified B<$number> of column specification to a specified B<$tgroup> which does not have any already.

141 L<ditaAddPadEntriesToTGroupRows|/ditaAddPadEntriesToTGroupRows> - Adding padding entries to a tgroup to make sure every row has the same number of entries

142 L<ditaAddTopicReport|/ditaAddTopicReport> - Place a report into a dita topic using required clean up

143 L<ditaConvertConceptToReference|/ditaConvertConceptToReference> - Convert a Dita B<concept> to a B<reference>.

144 L<ditaConvertConceptToSection|/ditaConvertConceptToSection> - Convert a Dita B<concept> to a B<$section> .

145 L<ditaConvertConceptToTask|/ditaConvertConceptToTask> - Convert a Dita B<concept> to a B<task> by representing B<ol> as B<steps>.

146 L<ditaConvertDlToUl|/ditaConvertDlToUl> - Convert a L<Dita|http://docs.oasis-open.org/dita/dita/v1.3/os/part2-tech-content/dita-v1.3-os-part2-tech-content.html> B<$dl> to a B<ul> if each B<dlentry> has only B<dt> or B<dl> elements but not both.

147 L<ditaConvertFromHtmlDl|/ditaConvertFromHtmlDl> - Convert a B<Html> B<$dl> to a L<Dita|http://docs.oasis-open.org/dita/dita/v1.3/os/part2-tech-content/dita-v1.3-os-part2-tech-content.html> B<dl> or return B<undef> if this is not possible.

148 L<ditaConvertOlToSubSteps|/ditaConvertOlToSubSteps> - Convert a L<Dita|http://docs.oasis-open.org/dita/dita/v1.3/os/part2-tech-content/dita-v1.3-os-part2-tech-content.html> B<$ul> to B<substeps> else B<undef> if this is not possible.

149 L<ditaConvertReferenceToConcept|/ditaConvertReferenceToConcept> - Convert a Dita B<reference> to a B<concept> by unwrapping sections.

150 L<ditaConvertReferenceToTask|/ditaConvertReferenceToTask> - Convert a Dita B<reference> to a B<task> in situ by representing B<ol> as B<steps>.

151 L<ditaConvertSectionToConcept|/ditaConvertSectionToConcept> - Convert a Dita B<$section> to a B<concept>.

152 L<ditaConvertSectionToReference|/ditaConvertSectionToReference> - Convert a Dita B<$section> to a B<reference>.

153 L<ditaConvertSectionToTask|/ditaConvertSectionToTask> - Convert a Dita B<$section> to a B<task>.

154 L<ditaConvertSimpleTableToTable|/ditaConvertSimpleTableToTable> - Convert a L<Dita|http://docs.oasis-open.org/dita/dita/v1.3/os/part2-tech-content/dita-v1.3-os-part2-tech-content.html> B<simpletable> to a B<table>.

155 L<ditaConvertSubStepsToSteps|/ditaConvertSubStepsToSteps> - Change the B<$substeps> to B<steps> and return the B<steps> on success or B<undef> on failure.

156 L<ditaConvertTopicToTask|/ditaConvertTopicToTask> - Convert a topic that is not already a task into a task

157 L<ditaConvertUlToSubSteps|/ditaConvertUlToSubSteps> - Convert a L<Dita|http://docs.oasis-open.org/dita/dita/v1.3/os/part2-tech-content/dita-v1.3-os-part2-tech-content.html> B<$ol> to B<substeps> else B<undef> if this is not possible.

158 L<ditaCouldConvertConceptToTask|/ditaCouldConvertConceptToTask> - Check whether a concept could be converted to a task

159 L<ditaCutTopicmetaFromAClassificationMap|/ditaCutTopicmetaFromAClassificationMap> - Remove a topicmeta node from a classification map.

160 L<ditaExpandAllConRefs|/ditaExpandAllConRefs> - Expand all the conrefs in the specified B<$parseTree> relative to the specified B<$sourceFile>.

161 L<ditaFixTGroupColSpec|/ditaFixTGroupColSpec> - Fix the colspec attribute and colspec nodes of the specified B<$tgroup>.

162 L<ditaGetConRef|/ditaGetConRef> - Get the value of a B<conref> attribute given a valid L<Dita|http://docs.oasis-open.org/dita/dita/v1.3/os/part2-tech-content/dita-v1.3-os-part2-tech-content.html> B<$ref>erence found in the file named B<$sourceFile>.

163 L<ditaListToChoices|/ditaListToChoices> - Change the specified B<$list> to B<choices>.

164 L<ditaListToSteps|/ditaListToSteps> - Change the specified B<$node> to B<steps> and its contents to B<cmd\step> optionally only in the specified context.

165 L<ditaListToStepsUnordered|/ditaListToStepsUnordered> - Change the specified B<$node> to B<steps-unordered> and its contents to B<cmd\step> optionally only in the specified context.

166 L<ditaListToSubSteps|/ditaListToSubSteps> - Change the specified B<$node> to B<substeps> and its contents to B<cmd\step> optionally only in the specified context.

167 L<ditaListToTable|/ditaListToTable> - Convert a list to a table in situ - as designed by MiM.

168 L<ditaMaximumNumberOfEntriesInATGroupRow|/ditaMaximumNumberOfEntriesInATGroupRow> - Return the maximum number of entries in the rows of the specified B<$table> or B<undef> if not a table.

169 L<ditaMergeLists|/ditaMergeLists> - Merge the specified B<$node> with the preceding or following list or steps or substeps if possible and return the specified B<$node> regardless.

170 L<ditaMergeListsOnce|/ditaMergeListsOnce> - Merge the specified B<$node> with the preceding or following list or steps or substeps if possible and return the specified B<$node> regardless.

171 L<ditaNumberOfColumnsInRow|/ditaNumberOfColumnsInRow> - Return estimate of the number of columns in a row

172 L<ditaObviousChanges|/ditaObviousChanges> - Make obvious changes to a L<parse|/parse> tree to make it look more like L<Dita|http://docs.oasis-open.org/dita/dita/v1.3/os/part2-tech-content/dita-v1.3-os-part2-tech-content.html>.

173 L<ditaParagraphToNote|/ditaParagraphToNote> - Convert all <p> nodes to <note> if the paragraph starts with 'Note:', optionally wrapping the content of the <note> with a <p>

174 L<ditaPrettyPrintWithHeaders|/ditaPrettyPrintWithHeaders> - Add L<Xml|https://en.wikipedia.org/wiki/XML> headers for the dita document type indicated by the specified L<parse|/parse> tree to a pretty print of the parse tree.

175 L<ditaRemoveTGroupTrailingEmptyEntries|/ditaRemoveTGroupTrailingEmptyEntries> - Remove empty trailing entry

176 L<ditaReplaceAnyConref|/ditaReplaceAnyConref> - Replace a conref with the referenced content if we are in the optional context and the target of the conref can be located.

177 L<ditaReplaceAnyConrefIdeallyWithMatchingTag|/ditaReplaceAnyConrefIdeallyWithMatchingTag> - Replace a conref with the referenced content if we are in the optional context and the target of the conref can be located.

178 L<ditaReplaceAnyConrefInContext|/ditaReplaceAnyConrefInContext> - Replace a conref with the referenced content if we are in the optional context and the target of the conref can be located and the L<context> of the target node matches the regular expression B<$context>.

179 L<ditaReplaceConref|/ditaReplaceConref> - Replace a conref on the specified B<$node> in the specified source file B<$sourceFile> with the parse tree of the referenced content.

180 L<ditaRoot|/ditaRoot> - Return the specified B<$node> if it a L<Dita|http://docs.oasis-open.org/dita/dita/v1.3/os/part2-tech-content/dita-v1.3-os-part2-tech-content.html> root node else return B<undef>.

181 L<ditaSampleBookMap|/ditaSampleBookMap> - Sample bookmap

182 L<ditaSampleConcept|/ditaSampleConcept> - Sample concept

183 L<ditaSampleTask|/ditaSampleTask> - Sample task

184 L<ditaStepsToChoices|/ditaStepsToChoices> - Change the specified B<$node> to B<choices>.

185 L<ditaStepsToList|/ditaStepsToList> - Change the specified B<$node> to a node with name B<$tag> or to B<ol> if B<$tag> is not supplied and its B<cmd\step> content to B<li> to create a list optionally only in the specified context.

186 L<ditaSyntaxDiagramFromDocBookCmdSynopsis|/ditaSyntaxDiagramFromDocBookCmdSynopsis> - Convert doc book cmdsynopsis to Dita syntax diagram

187 L<ditaSyntaxDiagramToBasicRepresentation|/ditaSyntaxDiagramToBasicRepresentation> - Convert Dita syntax diagrams into Micaela's Basic Version.

188 L<ditaTGroupStatistics|/ditaTGroupStatistics> - Return statistics about the rows in a given table

189 L<ditaTopicHeaders|/ditaTopicHeaders> - Add L<Xml|https://en.wikipedia.org/wiki/XML> headers for the dita document type indicated by the specified L<parse|/parse> tree

190 L<ditaUnderPNotConbody|/ditaUnderPNotConbody> - Return a hash of items that L<Dita|http://docs.oasis-open.org/dita/dita/v1.3/os/part2-tech-content/dita-v1.3-os-part2-tech-content.html> permits under B<p> but not directly under B<conbody>

191 L<ditaWrapWithPUnderConbody|/ditaWrapWithPUnderConbody> - Wrap items immediately under L<Dita|http://docs.oasis-open.org/dita/dita/v1.3/os/part2-tech-content/dita-v1.3-os-part2-tech-content.html> B<conbody> with B<p> or merge with any previous B<p> if the item in question does not fit under B<conbody> but does fit under B<p>.

192 L<ditaXrefs|/ditaXrefs> - Make obvious changes to all the B<xref>s found in a L<parse|/parse> tree to make them more useful in L<Dita|http://docs.oasis-open.org/dita/dita/v1.3/os/part2-tech-content/dita-v1.3-os-part2-tech-content.html>.

193 L<divideDocumentIntoSections|/divideDocumentIntoSections> - Divide a L<parse|/parse> tree into sections by moving non B<section> tags into their corresponding B<section> so that the B<section> tags expand until they are contiguous.

194 L<divideHtmlDocumentIntoSections|/divideHtmlDocumentIntoSections> - Divide a L<parse|/parse> tree representing an html document into sections based on the heading tags.

195 L<down|/down> - Pre-order traversal down through a L<parse|/parse> tree or sub tree calling the specified B<sub> at each node and returning the specified starting node.

196 L<downList|/downList> - Return a list of all the nodes at and below a specified B<$node> in pre-order or the empty list if the B<$node> is not in the optional B<@context>.

197 L<downReverse|/downReverse> - Reverse pre-order traversal down through a L<parse|/parse> tree or sub tree calling the specified B<sub> at each node and returning the specified starting node.

198 L<downReverseList|/downReverseList> - Return a list of all the nodes at and below a specified B<$node> in reverse pre-order or the empty list if the B<$node> is not in the optional B<@context>.

199 L<downReverseX|/downReverseX> - Reverse pre-order traversal down through a L<parse|/parse> tree or sub tree calling the specified B<sub> within L<eval|http://perldoc.perl.org/functions/eval.html>B<{}> at each node and returning the specified starting node.

200 L<downToDie|/downToDie> - Pre-order traversal of a L<parse|/parse> tree calling the specified B<sub> at each node as long as this sub does not L<die|http://perldoc.perl.org/functions/die.html>.

201 L<downToDie2|/downToDie2> - Pre-order traversal of a L<parse|/parse> tree calling the specified B<sub> at each node as long as this sub does not L<die|http://perldoc.perl.org/functions/die.html>.

202 L<downWhileFirst|/downWhileFirst> - Move down from the specified B<$node> as long as each lower node is a first node.

203 L<downWhileHasSingleChild|/downWhileHasSingleChild> - Move down from the specified B<$node> as long as it has a single child else return undef.

204 L<downWhileLast|/downWhileLast> - Move down from the specified B<$node> as long as each lower node is a last node.

205 L<downX|/downX> - Pre-order traversal of a L<parse|/parse> tree calling the specified B<sub> at each node as long as this sub does not L<die|http://perldoc.perl.org/functions/die.html>.

206 L<downX2|/downX2> - Pre-order traversal of a L<parse|/parse> tree or sub tree calling the specified B<sub> within L<eval|http://perldoc.perl.org/functions/eval.html>B<{}> at each node and returning the specified starting node.

207 L<dupPutNext|/dupPutNext> - Duplicate the specified B<$tree> in the optional B<@context>, place the new tree next after $tree and return the root of the new tree on success else B<undef>.

208 L<dupPutNextN|/dupPutNextN> - Duplicate the specified B<$tree> B<$N> times in the optional B<@context>, placing each copy after $tree and return the last new node created on success else B<undef>.

209 L<dupPutPrev|/dupPutPrev> - Duplicate the specified B<$tree> in the optional B<@context>, place the new tree before $tree and return the root of the new tree on success else B<undef>.

210 L<dupTag|/dupTag> - Create a new non text node by duplicating the tag of an existing node.

211 L<editText|/editText> - Return the text of a text B<$node> as an lvalue string reference that can be modified by a regular expression, else as a reference to a dummy string that will be ignored.

212 L<equals|/equals> - Return the first node if the two L<parse|/parse> trees have identical representations via L<string|/string>, else B<undef>.

213 L<equalsIgnoringAttributes|/equalsIgnoringAttributes> - Return the first node if the two L<parse|/parse> trees have identical representations via L<string|/string> if the specified attributes are ignored, else B<undef>.

214 L<expandIncludes|/expandIncludes> - Expand the includes mentioned in a L<parse|/parse> tree: any tag that ends in B<include> is assumed to be an include directive.

215 L<extendSectionToNextSection|/extendSectionToNextSection> - Extend a section tag until it meets the next section tag

216 L<extractDocumentationFlags|/extractDocumentationFlags> - Generate documentation for a method with a user flag.

217 L<findByForestNumber|/findByForestNumber> - Find the node with the specified L<forest number|/forestNumberTrees> as made visible on the id attribute by L<prettyStringNumbered|/prettyStringNumbered> in the L<parse|/parse> tree containing the specified B<$node> and return the found node or B<undef> if no such node exists.

218 L<findById|/findById> - Find a node in the parse tree under the specified B<$node> with the specified B<$id>.

219 L<findByNumber|/findByNumber> - Find the node with the specified number as made visible by L<prettyStringNumbered|/prettyStringNumbered> in the L<parse|/parse> tree containing the specified B<$node> and return the found node or B<undef> if no such node exists.

220 L<findByNumbers|/findByNumbers> - Find the nodes with the specified numbers as made visible by L<prettyStringNumbered|/prettyStringNumbered> in the L<parse|/parse> tree containing the specified B<$node> and return the found nodes in a list with B<undef> for nodes that do not exist.

221 L<findMatchingSubTrees|/findMatchingSubTrees> - Find nodes in the parse tree whose sub tree matches the specified B<$subTree> excluding any of the specified B<$attributes>.

222 L<first|/first> - Return the first node below the specified B<$node> optionally checking the first node's context.

223 L<firstBy|/firstBy> - Return a list of the first instance of each specified tag encountered in a post-order traversal from the specified B<$node> or a hash of all first instances if no tags are specified.

224 L<firstContextOf|/firstContextOf> - Return the first node encountered in the specified context in a depth first post-order traversal of the L<parse|/parse> tree.

225 L<firstDown|/firstDown> - Return a list of the first instance of each specified tag encountered in a pre-order traversal from the specified B<$node> or a hash of all first instances if no tags are specified.

226 L<firstIn|/firstIn> - Return the first child node matching one of the named tags under the specified parent node.

227 L<firstInIndex|/firstInIndex> - Return the specified B<$node> if it is first in its index and optionally L<at|/at> the specified context else B<undef>

228 L<firstIs|/firstIs> - Return the specified B<$node> if it has one or more child nodes and the first child node has the specified B<@context> otherwise B<undef>.

229 L<firstn|/firstn> - Return the B<$n>'th first node below the specified B<$node> optionally checking its context or B<undef> if there is no such node.

230 L<firstNot|/firstNot> - Return the first child node that does not match any of the named B<@tags> under the specified parent B<$node>.

231 L<firstOf|/firstOf> - Return an array of the nodes that are continuously first under their specified parent node and that match the specified list of tags.

232 L<firstSibling|/firstSibling> - Return the first sibling of the specified B<$node> in the optional B<@context> else B<undef>

233 L<firstText|/firstText> - Return the first node under the specified B<$node> if it is in the optional and it is a text node otherwise B<undef>.

234 L<firstTextMatches|/firstTextMatches> - Return the first node under the specified B<$node> if: it is a text mode; its text matches the specified regular expression; the specified B<$node> is in the optional specified context.

235 L<firstUntil|/firstUntil> - Go first from the specified B<$node> and continue deeper firstly until a first child node matches the specified B<@context> or return B<undef> if there is no such node.

236 L<firstUntilText|/firstUntilText> - Go first from the specified B<$node> and continue deeper firstly until a text node is encountered whose parent matches the specified B<@context> or return B<undef> if there is no such node.

237 L<firstWhile|/firstWhile> - Go first from the specified B<$node> and continue deeper firstly as long as each first child node matches one of the specified B<@tags>.

238 L<fixEntryColSpan|/fixEntryColSpan> - Fix the colspan on an entry assumed to be under row, tbody, tgroup with @cols and colspecs' set

239 L<fixEntryRowSpan|/fixEntryRowSpan> - Fix the rowspan on an entry

240 L<fixTable|/fixTable> - Fix the specified B<$table> so that each row has the same number of entries with this number reflected in the tgroup.

241 L<fixTGroup|/fixTGroup> - Fix the specified B<$tgroup> so that each row has the same number of entries with this number reflected in the tgroup.

242 L<forestNumberTrees|/forestNumberTrees> - Number the ids of the nodes in a L<parse|/parse> tree in pre-order so they are numbered in the same sequence that they appear in the source.

243 L<formatOxygenMessage|/formatOxygenMessage> - Write an error message in Oxygen format

244 L<from|/from> - Return a list consisting of the specified node and its following siblings optionally including only those nodes that match one of the tags in the specified list.

245 L<fromTo|/fromTo> - Return a list of the nodes between the specified start and end nodes optionally including only those nodes that match one of the tags in the specified list.

246 L<getAttrs|/getAttrs> - Return a sorted list of all the attributes on the specified B<$node>.

247 L<getLabels|/getLabels> - Return the names of all the labels set on a node.

248 L<getNodeAs|/getNodeAs> - Return the specified B<$node> unless it is possible to return the node saved with L<putNodeAs> under the specified B<name> and we are optionally in the specified B<@context>.

249 L<getSectionHeadingLevel|/getSectionHeadingLevel> - Get the heading level from a section tag.

250 L<giveEveryIdAGuid|/giveEveryIdAGuid> - Give a guid to every node in the specified B<$tree> that has an id attribute, saving any existing id attribute as a label, and return the count of the number of such replacements made.

251 L<go|/go> - Return the node reached from the specified B<$node> via the specified L<path|/path>: (index positionB<?>)B<*> where index is the tag of the next node to be chosen and position is the optional zero based position within the index of those tags under the current node.

252 L<goFish|/goFish> - A debug version of L<go|/go> that returns additional information explaining any failure to reach the node identified by the L<path|/path>.

253 L<hasContent|/hasContent> - Confirm that the specified B<$node> has content.

254 L<hasSingleChild|/hasSingleChild> - Return the only child of the specified B<$node> if the child is the only node under its parent ignoring any surrounding blank text and has the optional specified context, else return B<undef>.

255 L<hasSingleChildText|/hasSingleChildText> - Return the only child of the specified B<$node> if the child is a text node and the child is the only node under its parent ignoring any surrounding blank text and the child has the optional specified context, else return B<undef>.

256 L<hasSingleChildToDepth|/hasSingleChildToDepth> - Return the descendant of the specified B<$node> if it has single children to the specified depth in the specified optional B<@context> else return B<undef>.

257 L<height|/height> - Returns the height of the tree rooted at the specified B<$node>.

258 L<help|/help> - Get help for a node and the editor

259 L<howFar|/howFar> - Return how far the first node is from the second node along a path through their common ancestor.

260 L<howFarAbove|/howFarAbove> - Return how far the first node is  L<above|/above> the second node is or B<0> if the first node is not strictly L<above|/above> the second node.

261 L<howFarBelow|/howFarBelow> - Return how far the first node is  L<below|/below> the second node is or B<0> if the first node is not strictly L<below|/below> the second node.

262 L<howFirst|/howFirst> - Return the depth to which the specified B<$node> is L<first|/isFirst> else B<0>.

263 L<howLast|/howLast> - Return the depth to which the specified B<$node> is L<last|/isLast> else B<0>.

264 L<howOnlyChild|/howOnlyChild> - Return the depth to which the specified B<$node> is an L<only child|/isOnlyChild> else B<0>.

265 L<htmlHeadersToSections|/htmlHeadersToSections> - Position sections just before html header tags so that subsequently the document can be divided into L<sections|/divideDocumentIntoSections>.

266 L<htmlTables|/htmlTables> - Return a string of html representing a L<parse|/parse> tree.

267 L<htmlTableToDita|/htmlTableToDita> - Convert an L<html table> to a L<Dita|http://docs.oasis-open.org/dita/dita/v1.3/os/part2-tech-content/dita-v1.3-os-part2-tech-content.html> table.

268 L<index|/index> - Return the index of the specified B<$node> in its parent index.

269 L<indexIds|/indexIds> - Return a map of the ids at and below the specified B<$node>.

270 L<indexNode|/indexNode> - Merge multiple text segments and set parent and parser after changes to a node

271 L<invert|/invert> - Swap a parent and child node where the child is the only child of the parent and return the parent.

272 L<invertFirst|/invertFirst> - Swap a parent and child node where the child is the first child of the parent by placing the parent last in the child.

273 L<invertLast|/invertLast> - Swap a parent and child node where the child is the last child of the parent by placing the parent first in the child.

274 L<isADitaMap|/isADitaMap> - Return the specified B<$node> if this node is a L<Dita|http://docs.oasis-open.org/dita/dita/v1.3/os/part2-tech-content/dita-v1.3-os-part2-tech-content.html> map else return B<undef>

275 L<isAllBlankText|/isAllBlankText> - Return the specified B<$node> if the specified B<$node>, optionally in the specified context, does not contain anything or if it does contain something it is all white space else return B<undef>.

276 L<isBlankText|/isBlankText> - Return the specified B<$node> if the specified B<$node> is a text node, optionally in the specified context, and contains nothing other than white space else return B<undef>.

277 L<isEmpty|/isEmpty> - Confirm that the specified B<$node> is empty, that is: the specified B<$node> has no content, not even a blank string of text.

278 L<isFirst|/isFirst> - Return the specified B<$node> if it is first under its parent and optionally has the specified context, else return B<undef>

279 L<isFirstN|/isFirstN> - Return the first B<$N> nodes as an array if the first B<$N> tags of the parent of B<$node> finish at the specified B<$node> and have the specified tags in the sequence specified by B<@context>.

280 L<isFirstText|/isFirstText> - Return the specified B<$node> if the specified B<$node> is a text node, the first node under its parent and that the parent is optionally in the specified context, else return B<undef>.

281 L<isFirstToDepth|/isFirstToDepth> - Return the specified B<$node> if it is first to the specified depth else return B<undef>

282 L<isLast|/isLast> - Return the specified B<$node> if it is last under its parent and optionally has the specified context, else return B<undef>

283 L<isLastN|/isLastN> - Return the last B<$N> nodes as an array if the last B<$N> tags of the parent of B<$node> start at the specified B<$node> and have the specified tags in the sequence specified by B<@context>.

284 L<isLastText|/isLastText> - Return the specified B<$node> if the specified B<$node> is a text node, the last node under its parent and that the parent is optionally in the specified context, else return B<undef>.

285 L<isLastToDepth|/isLastToDepth> - Return the specified B<$node> if it is last to the specified depth else return B<undef>

286 L<isNotFirst|/isNotFirst> - Return the specified B<$node> if it is not first under its parent and optionally has the specified context, else return B<undef>

287 L<isNotLast|/isNotLast> - Return the specified B<$node> if it is not last under its parent and optionally has the specified context, else return B<undef>

288 L<isOnlyChild|/isOnlyChild> - Return the specified B<$node> if it is the only node under its parent ignoring any surrounding blank text.

289 L<isOnlyChildBlankText|/isOnlyChildBlankText> - Return the specified B<$node> if it is a blank text node and an only child else return B<undef>.

290 L<isOnlyChildN|/isOnlyChildN> - Return the specified B<$node> if it is an only child of B<$depth> ancestors with L<only children|/hasSingleChild> in the opptional B<@context> else return B<undef>.

291 L<isOnlyChildText|/isOnlyChildText> - Return the specified B<$node> if it is a text node and it is an only child else and its parent is in the specified optional context else return B<undef>.

292 L<isOnlyChildToDepth|/isOnlyChildToDepth> - Return the specified B<$node> if it and its ancestors are L<only children|/isOnlyChild> to the specified depth else return B<undef>.

293 L<isText|/isText> - Return the specified B<$node> if the specified B<$node> is a text node, optionally in the specified context, else return B<undef>.

294 L<joinWithText|/joinWithText> - Insert some text between the children of the current B<$node> as specified by B<$text> between all the children of the current $node as long as they all have a tag of B<$over>.

295 L<jsonString|/jsonString> - Return a Json representation of a parse tree

296 L<jsonString2|/jsonString2> - Return a Json representation of a parse tree

297 L<jsonToXml|/jsonToXml> - Convert a json string representing an L<Xml|https://en.wikipedia.org/wiki/XML> parse tree into an L<Xml|https://en.wikipedia.org/wiki/XML> parse tree

298 L<jsonToXml2|/jsonToXml2> - Convert a json string to an L<Xml|https://en.wikipedia.org/wiki/XML> parse tree

299 L<labelsInTree|/labelsInTree> - Return a hash of all the labels in a tree

300 L<last|/last> - Return the last node below the specified B<$node> optionally checking the last node's context.

301 L<lastBy|/lastBy> - Return a list of the last instance of each specified tag encountered in a post-order traversal from the specified B<$node> or a hash of all last instances if no tags are specified.

302 L<lastContextOf|/lastContextOf> - Return the last node encountered in the specified context in a depth first reverse pre-order traversal of the L<parse|/parse> tree.

303 L<lastDown|/lastDown> - Return a list of the last instance of each specified tag encountered in a pre-order traversal from the specified B<$node> or a hash of all last instances if no tags are specified.

304 L<lastIn|/lastIn> - Return the last child node matching one of the named tags under the specified parent node.

305 L<lastInIndex|/lastInIndex> - Return the specified B<$node> if it is last in its index and optionally L<at|/at> the specified context else B<undef>

306 L<lastIs|/lastIs> - Return the specified B<$node> if it has one or more child nodes and the last child node has the specified B<@context> otherwise B<undef>.

307 L<lastn|/lastn> - Return the B<$n>'th last node below the specified B<$node> optionally checking its context or B<undef> if there is no such node.

308 L<lastNot|/lastNot> - Return the last child node that does not match any of the named B<@tags> under the specified parent B<$node>.

309 L<lastOf|/lastOf> - Return an array of the nodes that are continuously last under their specified parent node and that match the specified list of tags.

310 L<lastSibling|/lastSibling> - Return the last sibling of the specified B<$node> in the optional B<@context> else B<undef>

311 L<lastText|/lastText> - Return the last node under the specified B<$node> if it is in the optional and it is a text node otherwise B<undef>.

312 L<lastTextMatches|/lastTextMatches> - Return the last node under the specified B<$node> if: it is a text mode; its text matches the specified regular expression; the specified B<$node> is in the optional specified context.

313 L<lastUntil|/lastUntil> - Go last from the specified B<$node> and continue deeper lastly until a last child node matches the specified B<@context> or return B<undef> if there is no such node.

314 L<lastUntilText|/lastUntilText> - Go last from the specified B<$node> and continue deeper lastly until a last child text node matches the specified B<@context> or return B<undef> if there is no such node.

315 L<lastWhile|/lastWhile> - Go last from the specified B<$node> and continue deeper lastly as long as each last child node matches one of the specified B<@tags>.

316 L<lineLocation|/lineLocation> - Return the line number.

317 L<listConditions|/listConditions> - Return a list of conditions applied to a B<$node>.

318 L<location|/location> - Return the line number.

319 L<matchAfter|/matchAfter> - Confirm that the string representing the tags following the specified B<$node> matches a regular expression where each pair of tags is separated by a single space.

320 L<matchAfter2|/matchAfter2> - Confirm that the string representing the tags following the specified B<$node> matches a regular expression where each pair of tags have two spaces between them and the first tag is preceded by a single space and the last tag is followed by a single space.

321 L<matchBefore|/matchBefore> - Confirm that the string representing the tags preceding the specified B<$node> matches a regular expression where each pair of tags is separated by a single space.

322 L<matchBefore2|/matchBefore2> - Confirm that the string representing the tags preceding the specified B<$node> matches a regular expression where each pair of tags have two spaces between them and the first tag is preceded by a single space and the last tag is followed by a single space.

323 L<matchesFirst|/matchesFirst> - Return the specified B<$node> if its children L<match|/atPositionMatch> the specified <@sequence> forwards from the first child else return B<undef>.

324 L<matchesLast|/matchesLast> - Return the specified B<$node> if its children L<match|/atPositionMatch> the specified <@sequence> backwards from the last child else return B<undef>.

325 L<matchesNext|/matchesNext> - Return the specified B<$node> if its following siblings L<match|/atPositionMatch> the specified <@sequence> else return B<undef>.

326 L<matchesNode|/matchesNode> - Return the B<$first> node if it matches the B<$second> node's tag and the specified B<@attributes> else return B<undef>.

327 L<matchesPrev|/matchesPrev> - Return the specified B<$node> if the siblings before $node L<match|/atPositionMatch> the specified <@sequence> with the first element of @sequence nearest to $node and the last element furthest else return B<undef>.

328 L<matchesSubTree|/matchesSubTree> - Return the B<$first> node if it L<matches|/matchesNode> the B<$second> node and the nodes under the first node match the corresponding nodes under the second node, else return B<undef>.

329 L<matchesText|/matchesText> - Returns an array of regular expression matches in the text of the specified B<$node> if it is text node and it matches the specified regular expression and optionally has the specified context otherwise returns an empty array.

330 L<matchNodesByRepresentation|/matchNodesByRepresentation> - Creates a hash of arrays of nodes that have the same representation in the specified B<$tree>.

331 L<matchTree|/matchTree> - Return a list of nodes that match the specified tree of match expressions, else B<()> if one or more match expressions fail to match nodes in the tree below the specified start node.

332 L<mergeDuplicateChildWithParent|/mergeDuplicateChildWithParent> - Merge a parent node with its only child if their tags are the same and their attributes do not collide other than possibly the id in which case the parent id is used.

333 L<mergeLikeElements|/mergeLikeElements> - Merge two of the same elements into one, retaining the order of any children.

334 L<mergeLikeNext|/mergeLikeNext> - Merge a B<$node> in an optional context with the next node if the two have the same tag by placing the next node first in the current B<$node> and unwrapping the next node.

335 L<mergeLikePrev|/mergeLikePrev> - Merge a B<$node> in an optional context with the previous node if the two have the same tag by placing the previous node first in the current B<$node> and unwrapping the previous node.

336 L<mergeOnlyChildLikeNext|/mergeOnlyChildLikeNext> - Merge a B<$node> if it is the only child of its parent with a preceding node of the same name that is also the only child of its parent and return the specified B<$node> or B<undef> if the request fails.

337 L<mergeOnlyChildLikePrev|/mergeOnlyChildLikePrev> - Merge a B<$node> if it is the only child of its parent with a preceding node of the same name that is also the only child of its parent and return the specified B<$node> or B<undef> if the request fails.

338 L<mergeOnlyChildLikePrevLast|/mergeOnlyChildLikePrevLast> - Merge a B<$node> if it is the only child of its parent with a preceding node with the same tag that is the last child of its parent and return the previous B<$node> or B<undef> if the request fails.

339 L<moveAttrs|/moveAttrs> - Move all the attributes of the source node to the target node, or, just the named attributes if the optional list of attributes to move is supplied, overwriting any existing attributes in the target node and return the source node.

340 L<moveBlockAfter|/moveBlockAfter> - Move the block of siblings starting with B<$start> in the optional context and ending with B<$end> after the specified B<$after> node.

341 L<moveBlockBefore|/moveBlockBefore> - Move the block of siblings starting with B<$start> in the optional context and ending with B<$end> before the specified B<$after> node.

342 L<moveBlockFirst|/moveBlockFirst> - Move the block of siblings starting with B<$start> in the optional context and ending with B<$end> first under the specified B<$parent> node.

343 L<moveBlockFromFirstAfter|/moveBlockFromFirstAfter> - Move the block of siblings starting with B<$start> in the optional context after the specified B<$after> node.

344 L<moveBlockFromFirstBefore|/moveBlockFromFirstBefore> - Move the block of siblings starting with B<$start> in the optional context before the specified B<$after> node.

345 L<moveBlockFromFirstFirst|/moveBlockFromFirstFirst> - Move the siblings starting with B<$start> in the optional context first under the specified B<$parent> node.

346 L<moveBlockFromFirstLast|/moveBlockFromFirstLast> - Move the block of siblings starting with B<$start> in the optional context last under the specified B<$parent> node.

347 L<moveBlockLast|/moveBlockLast> - Move the block of siblings starting with B<$start> in the optional context and ending with B<$end> last under the specified B<$parent> node.

348 L<moveBlockToLastAfter|/moveBlockToLastAfter> - Move the block of siblings starting with B<$start> in the optional context after the specified B<$after> node.

349 L<moveBlockToLastBefore|/moveBlockToLastBefore> - Move the block of siblings starting with B<$start> in the optional context before the specified B<$after> node.

350 L<moveBlockToLastFirst|/moveBlockToLastFirst> - Move the siblings starting with B<$start> in the optional context first under the specified B<$parent> node.

351 L<moveBlockToLastLast|/moveBlockToLastLast> - Move the block of siblings starting with B<$start> in the optional context last under the specified B<$parent> node.

352 L<moveEndAfter|/moveEndAfter> - Move the end of a B<$node> to just after the specified B<$target> node assuming that the B<$target> node is either a subsequent sibling or a child of B<$node>.

353 L<moveEndBefore|/moveEndBefore> - Move the end of a B<$node> to just before the specified B<$target> node assuming that the B<$target> node is either a subsequent sibling or a child of B<$node>.

354 L<moveEndLast|/moveEndLast> - Move the end of a B<$node> to contain all of its following siblings as children.

355 L<moveFirst|/moveFirst> - Move the specified node so that is is the first sibling under its parent.

356 L<moveLabels|/moveLabels> - Move all the labels from the source node to the target node and return the source node.

357 L<moveLast|/moveLast> - Move the specified node so that is is the last sibling under its parent.

358 L<moveNewAttrs|/moveNewAttrs> - Move all the attributes of the source node to the target node, or, just the named attributes if the optional list of attributes to copy is supplied, without overwriting any existing attributes in the target node and return the source node.

359 L<moveSelectionAfter|/moveSelectionAfter> - Move the current selection (if there is one) after the specified B<$node> in the optional B<@context> and return the specified B<$node> on success else B<undef>.

360 L<moveSelectionBefore|/moveSelectionBefore> - Move the current selection (if there is one) before the specified B<$node> in the optional B<@context> and return the specified B<$node> on success else B<undef>.

361 L<moveSelectionFirst|/moveSelectionFirst> - Move the current selection (if there is one) so that it is first under the specified B<$node> in the optional B<@context> and return the specified B<$node> on success else B<undef>.

362 L<moveSelectionLast|/moveSelectionLast> - Move the current selection (if there is one) so that it is last under the specified B<$node> in the optional B<@context> and return the specified B<$node> on success else B<undef>.

363 L<moveStartAfter|/moveStartAfter> - Move the start end of a B<$node> to just after the specified B<$target> node assuming that the B<$target> node is either a preceding sibling or a child of B<$node>.

364 L<moveStartBefore|/moveStartBefore> - Move the start of a B<$node> to just before the specified B<$target> node assuming that the B<$target> node is either a preceding sibling or a child of B<$node>.

365 L<moveStartFirst|/moveStartFirst> - Move the start of a B<$node> to contain all of its preceding siblings as children.

366 L<new|/new> - Create a new parse tree - call this method statically as in Data::Edit::Xml::new(file or string) to parse a file or string B<or> with no parameters and then use L</input>, L</inputFile>, L</inputString>, L</errorFile>  to provide specific parameters for the parse, then call L</parse> to perform the parse and return the parse tree.

367 L<newTag|/newTag> - Create a new non text node.

368 L<newText|/newText> - Create a new text node.

369 L<newTree|/newTree> - Create a new tree.

370 L<next|/next> - Return the node next to the specified B<$node>, optionally checking the next node's context.

371 L<nextIn|/nextIn> - Return the nearest sibling after the specified B<$node> that matches one of the named tags or B<undef> if there is no such sibling node.

372 L<nextIs|/nextIs> - Return the specified B<$node> if there is a following node with the specified B<@context>.

373 L<nextn|/nextn> - Return the B<$n>'th next node after the specified B<$node> optionally checking its context or B<undef> if there is no such node.

374 L<nextN|/nextN> - Return B<$N> nodes as an array starting at B<$node> inclusive if they match the first B<$N> tags of B<@context>.

375 L<nextOn|/nextOn> - Step forwards as far as possible from the specified B<$node> while remaining on nodes with the specified tags.

376 L<nextText|/nextText> - Return the node after the specified B<$node> if it is in the optional and it is a text node otherwise B<undef>.

377 L<nextTextMatches|/nextTextMatches> - Return the next node to the specified B<$node> if: it is a text mode; its text matches the specified regular expression; the specified B<$node> is in the optional specified context.

378 L<nextUntil|/nextUntil> - Go to the next sibling of the specified B<$node> and continue forwards until the tag of a sibling node matches one of the specified B<@tags>.

379 L<nextWhile|/nextWhile> - Go to the next sibling of the specified B<$node> and continue forwards while the tag of each sibling node matches one of the specified B<@tags>.

380 L<nn|/nn> - Replace new lines in a string with N to make testing easier.

381 L<normalizeWhiteSpace|/normalizeWhiteSpace> - Normalize white space, remove comments DOCTYPE and L<Xml|https://en.wikipedia.org/wiki/XML> processors from a string

382 L<not|/not> - Return the specified B<$node> if it does not match any of the specified tags, else B<undef>

383 L<numberNode|/numberNode> - Ensure that the specified B<$node> has a number.

384 L<numberTree|/numberTree> - Number the nodes in a L<parse|/parse> tree in pre-order so they are numbered in the same sequence that they appear in the source.

385 L<numberTreesJustIds|/numberTreesJustIds> - Number the ids of the nodes in a L<parse|/parse> tree in pre-order so they are numbered in the same sequence that they appear in the source.

386 L<opAt|/opAt> - <= : Check that a node is in the context specified by the referenced array of words.

387 L<opAttr|/opAttr> - % : Get the value of an attribute of the specified B<$node>.

388 L<opBy|/opBy> - x= : Traverse a L<parse|/parse> tree in post-order.

389 L<opContents|/opContents> - @{} : nodes immediately below a node.

390 L<opCut|/opCut> - -- : Cut out a node.

391 L<opGo|/opGo> - >= : Search for a node via a specification provided as a reference to an array of words each number.

392 L<opNew|/opNew> - ** : create a new node from the text on the right hand side: if the text contains a non word character \W the node will be create as text, else it will be created as a tag

393 L<opPutFirst|/opPutFirst> - >> : put a node or string first under a node and return the new node.

394 L<opPutFirstAssign|/opPutFirstAssign> - >>= : put a node or string first under a node.

395 L<opPutLast|/opPutLast> - << : put a node or string last under a node and return the new node.

396 L<opPutLastAssign|/opPutLastAssign> - <<= : put a node or string last under a node.

397 L<opPutNext|/opPutNext> - > + : put a node or string after the specified B<$node> and return the new node.

398 L<opPutNextAssign|/opPutNextAssign> - += : put a node or string after the specified B<$node>.

399 L<opPutPrev|/opPutPrev> - < - : put a node or string before the specified B<$node> and return the new node.

400 L<opPutPrevAssign|/opPutPrevAssign> - -= : put a node or string before the specified B<$node>,

401 L<opString|/opString> - -A: L<printNode|/printNode>

-B: L<bitsNodeTextBlank|/bitsNodeTextBlank>

-b: L<isAllBlankText|/isAllBlankText>

-C: L<stringContent|/stringContent>

-c: L<context|/context>

-d: L<depth|/depth>

-e: L<prettyStringEnd|/prettyStringEnd>

-f: L<first node|/first>

-g: L<createGuidId|/createGuidId>

-k: L<createGuidId|/createGuidId>

-l: L<last node|/last>

-M: L<stringAsMd5Sum|/stringAsMd5Sum>

-O: L<contentAsTags2|/contentAsTags2>

-o: L<contentAsTags|/contentAsTags>

-p: L<prettyString|/prettyString>

-R: L<requiredCleanup|/requiredCleanup>

-r: L<stringTagsAndText|/stringTagsAndText>

-S: L<printStack|/printStack>

-s: L<string|/string>

-T: L<isText|/isText>

-t: L<tag|/tag>

-u: L<unwrap|/unwrap>

-W: L<id|/id>

-w: L<stringQuoted|/stringQuoted>

-X: L<prettyStringDitaHeaders|/prettyStringDitaHeaders>

-x: L<cut|/cut>

-z: L<prettyStringNumbered|/prettyStringNumbered>.

402 L<opUnwrap|/opUnwrap> - ++ : Unwrap a node.

403 L<opWrapContentWith|/opWrapContentWith> - * : Wrap content with a tag, returning the wrapping node.

404 L<opWrapWith|/opWrapWith> - / : Wrap node with a tag, returning the wrapping node.

405 L<ordered|/ordered> - Return the first node if the specified nodes are all in order when performing a pre-ordered traversal of the L<parse|/parse> tree else return B<undef>.

406 L<over|/over> - Confirm that the string representing the tags at the level below the specified B<$node> match a regular expression where each pair of tags is separated by a single space.

407 L<over2|/over2> - Confirm that the string representing the tags at the level below the specified B<$node> match a regular expression where each pair of tags have two spaces between them and the first tag is preceded by a single space and the last tag is followed by a single space.

408 L<overAllTags|/overAllTags> - Return the specified b<$node> if all of it's child nodes L<match|/atPositionMatch> the specified <@tags> else return B<undef>.

409 L<overFirstTags|/overFirstTags> - Return the specified b<$node> if the first of it's child nodes L<match|/atPositionMatch> the specified <@tags> else return B<undef>.

410 L<overLastTags|/overLastTags> - Return the specified b<$node> if the last of it's child nodes L<match|/atPositionMatch> the specified <@tags> else return B<undef>.

411 L<parentage|/parentage> - Return a reference to an array of the nodes along the path from the root to the specified B<$Node> inclusive.

412 L<parentOf|/parentOf> - Returns the specified B<$parent> node if it is the parent of the specified B<$child> node and the B<$parent> node is in the specified optional context.

413 L<parse|/parse> - Parse input L<Xml|https://en.wikipedia.org/wiki/XML> specified via: L<inputFile|/inputFile>, L<input|/input> or L<inputString|/inputString>.

414 L<parseLineLocation|/parseLineLocation> - Parse a line location

415 L<path|/path> - Return a list of strings representing the path to a node from the root of the parse tree which can then be reused by L<go|/go> to retrieve the node as long as the structure of the L<parse|/parse> tree has not changed along the path.

416 L<pathString|/pathString> - Return a string representing the L<path|/path> to the specified B<$node> from the root of the parse tree.

417 L<position|/position> - Return the index of the specified B<$node> in the content of the parent of the B<$node>.

418 L<precedingSiblingOf|/precedingSiblingOf> - Returns the specified B<$child> node if it has the same parent as B<$sibling> and occurs before B<$sibling> and has the optionally specified context else returns B<undef>.

419 L<present|/present> - Return the count of the number of the specified tag types present immediately under a node or a hash {tag} = count for all the tags present under the node if no names are specified.

420 L<prettyString|/prettyString> - Return a readable string representing a node of a L<parse|/parse> tree and all the nodes below it.

421 L<prettyStringCDATA|/prettyStringCDATA> - Return a readable string representing a node of a L<parse|/parse> tree and all the nodes below it with the text fields wrapped with <CDATA>.

422 L<prettyStringContent|/prettyStringContent> - Return a readable string representing all the nodes below a node of a L<parse|/parse> tree.

423 L<prettyStringContentNumbered|/prettyStringContentNumbered> - Return a readable string representing all the nodes below a node of a L<parse|/parse> tree with numbering added.

424 L<prettyStringDitaHeaders|/prettyStringDitaHeaders> - Return a readable string representing the L<parse|/parse> tree below the specified B<$node> with appropriate headers.

425 L<prettyStringEnd|/prettyStringEnd> - Return a readable string representing a node of a L<parse|/parse> tree and all the nodes below it as a here document

426 L<prettyStringHtml|/prettyStringHtml> - Return a string of L<html|https://en.wikipedia.org/wiki/HTML> representing a node of a L<parse|/parse> tree and all the nodes below it if the node is in the specified context.

427 L<prettyStringHtml2|/prettyStringHtml2> - Return a string of html representing a node of a L<parse|/parse> tree and all the nodes below it.

428 L<prettyStringNumbered|/prettyStringNumbered> - Return a readable string representing a node of a L<parse|/parse> tree and all the nodes below it with a L<number|/number> attached to each tag.

429 L<prev|/prev> - Return the node before the specified B<$node>, optionally checking the previous node's context.

430 L<prevIn|/prevIn> - Return the nearest sibling node before the specified B<$node> which matches one of the named tags or B<undef> if there is no such sibling node.

431 L<prevIs|/prevIs> - Return the specified B<$node> if there is a previous node with the specified B<@context>.

432 L<prevN|/prevN> - Return B<$N> nodes as an array ending at B<$node> inclusive if they match the first B<$N> tags of B<@context>.

433 L<prevn|/prevn> - Return the B<$n>'th previous node after the specified B<$node> optionally checking its context or B<undef> if there is no such node.

434 L<prevOn|/prevOn> - Step backwards as far as possible while remaining on nodes with the specified tags.

435 L<prevText|/prevText> - Return the node before the specified B<$node> if it is in the optional and it is a text node otherwise B<undef>.

436 L<prevTextMatches|/prevTextMatches> - Return the previous node to the specified B<$node> if: it is a text mode; its text matches the specified regular expression; the specified B<$node> is in the optional specified context.

437 L<prevUntil|/prevUntil> - Go to the previous sibling of the specified B<$node> and continue backwards until the tag of a sibling node matches one of the specified B<@tags>.

438 L<prevWhile|/prevWhile> - Go to the previous sibling of the specified B<$node> and continue backwards while the tag of each sibling node matches one of the specified B<@tags>.

439 L<printAttributes|/printAttributes> - Print the attributes of a node.

440 L<printAttributesExtendingIdsWithLabels|/printAttributesExtendingIdsWithLabels> - Print the attributes of a node extending the id with the labels.

441 L<printAttributesHtml|/printAttributesHtml> - Print the attributes of a node as html.

442 L<printAttributesReplacingIdsWithLabels|/printAttributesReplacingIdsWithLabels> - Print the attributes of a node replacing the id with the labels.

443 L<printNode|/printNode> - Print the tag and attributes of a node.

444 L<printNodeAsSingleton|/printNodeAsSingleton> - Print the tag and attributes of a node as if it were a single node regardless of any child nodes it might have

445 L<printStack|/printStack> - Print the attributes of a node and each of its parents

446 L<processModuleDescription|/processModuleDescription> - Process module description to write a description of editing Xml to GitHub

447 L<propagate|/propagate> - Propagate L<new attributes|/copyNewAttrs> from nodes that match the specified tag to all their child nodes, then L<unwrap|/unwrap> all the nodes that match the specified tag.

448 L<putContentAfter|/putContentAfter> - Move the content of the specified B<$node> and place it after that node if that node is in the optional B<@context>.

449 L<putContentBefore|/putContentBefore> - Move the content of the specified B<$node> and place it before that node if that node is in the optional B<@context>.

450 L<putCutOutFirst|/putCutOutFirst> - Pop the last node placed on the cut out stack by one of the cut(First|Last|Next|Prev) methods and place it first under the specified B<$node> if $node is in the optional B<@context>.

451 L<putCutOutLast|/putCutOutLast> - Pop the last node placed on the cut out stack by one of the cut(First|Last|Next|Prev) methods and place it last under the specified B<$node> if $node is in the optional B<@context>.

452 L<putCutOutNext|/putCutOutNext> - Pop the last node placed on the cut out stack by one of the cut(First|Last|Next|Prev) methods and place it next after the specified B<$node> if $node is in the optional B<@context>.

453 L<putCutOutPrev|/putCutOutPrev> - Pop the last node placed on the cut out stack by one of the cut(First|Last|Next|Prev) methods and place it before the specified B<$node> if $node is in the optional B<@context>.

454 L<putFirst|/putFirst> - Place a L<cut out|/cut> or L<new|/new> node at the front of the content of the specified B<$node> and return the new node.

455 L<putFirstAsComment|/putFirstAsComment> - Put a comment first under the specified B<$node> and return the specified B<$node>.

456 L<putFirstAsText|/putFirstAsText> - Add a new text node first under a parent and return the new text node.

457 L<putFirstAsTree|/putFirstAsTree> - Put parsed text first under the specified B<$node> parent and return a reference to the parsed tree.

458 L<putFirstCut|/putFirstCut> - Cut out the B<$second> node, place it first under the B<$first> node and return the B<$second> node.

459 L<putFirstRequiredCleanUp|/putFirstRequiredCleanUp> - Place a required cleanup first under a specified B<$node> using the specified B<@text>  and return the required clean up node.

460 L<putLast|/putLast> - Place a L<cut out|/cut> or L<new|/new> node last in the content of the specified B<$node> and return the new node.

461 L<putLastAsComment|/putLastAsComment> - Put a comment last under the specified B<$node> and return the specified B<$node>.

462 L<putLastAsText|/putLastAsText> - Add a new text node last under a parent and return the new text node.

463 L<putLastAsTree|/putLastAsTree> - Put parsed text last under the specified B<$node> parent and return a reference to the parsed tree.

464 L<putLastCut|/putLastCut> - Cut out the B<$second> node, place it last under the B<$first> node and return the B<$second> node.

465 L<putLastRequiredCleanUp|/putLastRequiredCleanUp> - Place a required cleanup last under a specified B<$node> using the specified B<@text>  and return the required clean up node.

466 L<putNext|/putNext> - Place a L<cut out|/cut> or L<new|/new> node just after the specified B<$node> and return the new node.

467 L<putNextAsComment|/putNextAsComment> - Put a comment after the specified B<$node> and return the specified B<$node>

468 L<putNextAsText|/putNextAsText> - Add a new text node following the specified B<$node> and return the new text node.

469 L<putNextAsTree|/putNextAsTree> - Put parsed text after the specified B<$node> parent and return a reference to the parsed tree.

470 L<putNextCut|/putNextCut> - Cut out the B<$second> node, place it after the B<$first> node and return the B<$second> node.

471 L<putNextFirstCut|/putNextFirstCut> - Move the specified B<$node> so it is first in the next node with the optional context.

472 L<putNextFirstCut2|/putNextFirstCut2> - Move the specified B<$node> so it is first in the first node with the specified optional B<@context> of the next node.

473 L<putNextRequiredCleanUp|/putNextRequiredCleanUp> - Place a required cleanup next after a specified B<$node> using the specified B<@text>  and return the required clean up node.

474 L<putNodeAs|/putNodeAs> - Return the specified B<$node> after saving it under the specified B<$name> if we are in the optional B<@context>.

475 L<putPrev|/putPrev> - Place a L<cut out|/cut> or L<new|/new> node just before the specified B<$node> and return the new node.

476 L<putPrevAsComment|/putPrevAsComment> - Put a comment before the specified B<$parent> parentand return the specified B<$node>

477 L<putPrevAsText|/putPrevAsText> - Add a new text node following the specified B<$node> and return the new text node

478 L<putPrevAsTree|/putPrevAsTree> - Put parsed text before the specified B<$parent> parent and return a reference to the parsed tree.

479 L<putPrevCut|/putPrevCut> - Cut out the B<$second> node, place it before the B<$first> node and return the B<$second> node.

480 L<putPrevLastCut|/putPrevLastCut> - Move the specified B<$node> so it is last in the preceding node with the optional context.

481 L<putPrevLastCut2|/putPrevLastCut2> - Move the specified B<$node> so it is last in the last node with the specified optional context of the preceding node.

482 L<putPrevRequiredCleanUp|/putPrevRequiredCleanUp> - Place a required cleanup before a specified B<$node> using the specified B<@text>  and return the required clean up node.

483 L<putSiblingsAfterParent|/putSiblingsAfterParent> - Move the specified B<$node> and its following siblings up one level and place them after the parent of the specified B<$node> if the specified B<$node> is in the optional B<@context>.

484 L<putSiblingsBeforeParent|/putSiblingsBeforeParent> - Move the specified B<$node> and its preceding siblings up one level and place them before the parent of the specified B<$node> if the specified B<$node> is in the optional B<@context>.

485 L<putSiblingsFirst|/putSiblingsFirst> - Move the siblings preceding the specified B<$node> in the optional B<@context> down one level and place them first under the specified B<$node> preceding any existing content.

486 L<putSiblingsLast|/putSiblingsLast> - Move the siblings following the specified B<$node> in the optional B<@context> down one level so that they are last under the specified B<$node> following any existing content.

487 L<putTextFirst|/putTextFirst> - Given a B<$node> place some B<@text> first under the $node and return the new text node else return B<undef> if this is not possible.

488 L<putTextLast|/putTextLast> - Given a B<$node> place some B<@text> last under the $node and return the new text node else return B<undef> if this is not possible.

489 L<putTextNext|/putTextNext> - Given a B<$node> place some B<@text> next to the $node and return the new text node else return B<undef> if this is not possible.

490 L<putTextPrev|/putTextPrev> - Given a B<$node> place some B<@text> prior to the $node and return the new text node else return B<undef> if this is not possible.

491 L<putUpNextCut|/putUpNextCut> - Move the specified B<$node>, in the optional B<@context>, which must be last under its parent, so that it is next after its parent.

492 L<putUpNextCut2|/putUpNextCut2> - Move the specified B<$node>, in the optional B<@context>, if $node is last after its parent which must also be last under its parent, so that $node is next after its grandparent.

493 L<putUpPrevCut|/putUpPrevCut> - Move the specified B<$node> in the optional B<@context>, if $node is first under its parent, so that it is prior to its parent.

494 L<putUpPrevCut2|/putUpPrevCut2> - Move the specified B<$node>, in the optional B<@context>, if $node is first under its parent which must also be first under its parent, so that $node is prior to its grandparent.

495 L<readCompressedFile|/readCompressedFile> - Read the specified B<$file> containing compressed L<Xml|https://en.wikipedia.org/wiki/XML> and return the root node.

496 L<reduceParseErroMessage|/reduceParseErroMessage> - Reduce the parse failure message to the bare essentials

497 L<reindexNode|/reindexNode> - Index the children of a node so that we can access them by tag and number.

498 L<renameAttr|/renameAttr> - Rename attribute B<$old> to B<$new> in the specified B<$node> with optional context B<@context> regardless of whether attribute B<$new> already exists or not and return the B<$node>.

499 L<renameAttrValue|/renameAttrValue> - Rename attribute B<$old> to B<$new> with new value B<$newValue> in the specified B<$node> in the optional B<@context> regardless of whether attribute B<$new> already exists or not as long as the attribute B<$old> has the value B<$oldValue>.

500 L<renameAttrXtr|/renameAttrXtr> - Rename the attributes B<@attr> as far as possible to xtrc or xtrf.

501 L<renew|/renew> - Returns a renewed copy of the L<parse|/parse> tree by first printing it and then reparsing it, optionally checking that the starting node is in a specified context: use this method if you have added nodes via the L</"Put as text"> methods and wish to traverse their L<parse|/parse> tree.

502 L<reorder|/reorder> - If the current B<$node> has a context of (B<$first>, @context) and the preceding node has a context of (B<$second>, B<@context>), then swap the current node with the previous node.

503 L<replaceContentWith|/replaceContentWith> - Replace the content of a node with the specified nodes and return the replaced content

504 L<replaceContentWithMovedContent|/replaceContentWithMovedContent> - Replace the content of a specified target node with the contents of the specified source nodes removing the content from each source node and return the target node.

505 L<replaceContentWithText|/replaceContentWithText> - Replace the content of a node with the specified texts and return the replaced content

506 L<replaceSpecialChars|/replaceSpecialChars> - Replace < > " & with &lt; &gt; &quot; &amp; Larry Wall's excellent L<Xml parser> unfortunately replaces &lt; &gt; &quot; &amp; etc.

507 L<replaceWith|/replaceWith> - Replace a node (and all its content) with a L<new node|/newTag> (and all its content) and return the new node.

508 L<replaceWithBlank|/replaceWithBlank> - Replace a node (and all its content) with a new blank text node and return the new node.

509 L<replaceWithRequiredCleanUp|/replaceWithRequiredCleanUp> - Replace a B<$node> with required cleanup B<@text> and return the new node

510 L<replaceWithText|/replaceWithText> - Replace a node (and all its content) with a new text node and return the new node.

511 L<reportNode|/reportNode> - Print the parse tree starting at the specified B<$node> identified by the specified B<$label> if we are in the optional B<@context>.

512 L<reportNodeAttributes|/reportNodeAttributes> - Print the attributes of the specified B<$node> identified by the specified B<$label> if we are in the optional B<@context>.

513 L<reportNodeContext|/reportNodeContext> - Print the context of the specified B<$node> identified by the specified B<$label> if we are in the optional B<@context>.

514 L<requiredCleanUp|/requiredCleanUp> - Replace a B<$node> with a required cleanup node with special characters replaced by symbols and with the optional B<$outputclass>.

515 L<restore|/restore> - Return a L<parse|/parse> tree from a copy saved in a file by L<save|/save>.

516 L<save|/save> - Save a copy of the L<parse|/parse> tree to a file which can be L<restored|/restore> and return the saved node.

517 L<set|/set> - Set the values of some attributes in a node and return the node.

518 L<setAttr|/setAttr> - Set the values of some attributes in a node and return the node.

519 L<setDepthProfile|/setDepthProfile> - Sets the L<depthProfile|/depthProfile> for every node in the specified B<$tree>.

520 L<setRepresentationAsTagsAndText|/setRepresentationAsTagsAndText> - Sets the L<representationLast|/representationLast> for every node in the specified B<$tree> via L<stringTagsAndText|/stringTagsAndText>.

521 L<setRepresentationAsText|/setRepresentationAsText> - Sets the L<representationLast|/representationLast> for every node in the specified B<$tree> via L<stringText|/stringText>.

522 L<setSelectionEnd|/setSelectionEnd> - Set the selection to end at the specified B<$node> in the optional B<@context> and return the specified B<$node> on success else B<undef>.

523 L<setSelectionStart|/setSelectionStart> - Set the selection to start at the specified B<$node> in the optional B<@context> and return the specified B<$node> on success else B<undef>.

524 L<splitAfter|/splitAfter> - Split the parent node into two identical nodes except all the siblings after the specified B<$node> are retained by the existing parent while any preceding siblings become siblings of the new parent node which is placed before the existing parent.

525 L<splitAndWrapFromStart|/splitAndWrapFromStart> - Split the child nodes of B<$parent> on the child nodes with a tag of B<$split> wrapping the splitting node and all preceding nodes from the previous splitting node or the start with the specified B<$wrap>ping node with optional B<%attributes>.

526 L<splitAndWrapToEnd|/splitAndWrapToEnd> - Split the sequence of child nodes under the specified B<$parent> node on those child nodes whose tag value is B<$split> wrapping the splitting node and all following nodes up until the next splitting node or the end of the sequence with newly created nodes whose tag is B<$wrap> with optional attributes B<%attributes>.

527 L<splitBefore|/splitBefore> - Split the parent node into two identical nodes except all the siblings before the specified B<$node> are retained by the existing parent while any following siblings become siblings of the new parent node which is placed after the existing parent.

528 L<splitParentAfter|/splitParentAfter> - Finish and restart the parent of the specified B<$node> just after the specified B<$node> and return the newly created parent node on success or B<undef> on failure.

529 L<splitParentBefore|/splitParentBefore> - Finish and restart the parent of the specified B<$node> just before the specified B<$node> and return the newly created parent node on success or B<undef> on failure.

530 L<splitTo|/splitTo> - Lift the specified B<$node> up until it splits the specified B<$parent> node.

531 L<sss|/sss> - Put, after the current B<$node>, the specified B<@text> and return the current $node.

532 L<string|/string> - Return a dense string representing a node of a L<parse|/parse> tree and all the nodes below it.

533 L<stringAsMd5Sum|/stringAsMd5Sum> - Return the L<md5 sum|https://en.wikipedia.org/wiki/MD5> of the dense L<string|/string> representing a node of a L<parse|/parse> tree minus its L<id> and all the nodes below it.

534 L<stringContent|/stringContent> - Return a string representing all the nodes below a node of a L<parse|/parse> tree.

535 L<stringContentOrText|/stringContentOrText> - Return a string representing all the nodes below a node of a L<parse|/parse> tree or the text of the current node if it is a text node.

536 L<stringExtendingIdsWithLabels|/stringExtendingIdsWithLabels> - Return a string representing the specified L<parse|/parse> tree with the id attribute of each node extended by the L<Labels|/Labels> attached to each node.

537 L<stringNode|/stringNode> - Return a string representing the specified B<$node> showing the attributes, labels and node number.

538 L<stringQuoted|/stringQuoted> - Return a quoted string representing a L<parse|/parse> tree a node of a L<parse|/parse> tree and all the nodes below it.

539 L<stringReplacingIdsWithLabels|/stringReplacingIdsWithLabels> - Return a string representing the specified L<parse|/parse> tree with the id attribute of each node set to the L<Labels|/Labels> attached to each node.

540 L<stringTagsAndText|/stringTagsAndText> - Return a string showing just the tags and text at and below a specified B<$node>.

541 L<stringText|/stringText> - Return a string showing just the text of the text nodes (separated by blanks) at and below a specified B<$node>.

542 L<stringWithConditions|/stringWithConditions> - Return a string representing the specified B<$node> of a L<parse|/parse> tree and all the nodes below it subject to conditions to select or reject some nodes.

543 L<structureAdjacentSectionsByLevel|/structureAdjacentSectionsByLevel> - Structure adjacent sections by level

544 L<subMd5|/subMd5> - Return a hash {L<md5 sum|https://en.wikipedia.org/wiki/MD5> of parse tree}{string representation of sub tree}++ to locate common parse trees under B<$node> that could be included via a conref.

545 L<subMd5Tree|/subMd5Tree> - Return the L<md5 sum|https://en.wikipedia.org/wiki/MD5> of the L<stringContent> of the parse tree.

546 L<succeedingSiblingOf|/succeedingSiblingOf> - Returns the specified B<$child> node if it has the same parent as B<$sibling> and occurs after B<$sibling> and has the optionally specified context else returns B<undef>.

547 L<swap|/swap> - Swap two nodes optionally checking that the first node is in the specified context and return the first node.

548 L<swapFirstSibling|/swapFirstSibling> - Swap B<$node> with its first sibling node and return B<$node>.

549 L<swapLastSibling|/swapLastSibling> - Swap B<$node> with its last sibling node and return B<$node>.

550 L<swapNext|/swapNext> - Swap B<$node> with its following node if $B<$node> matches the first element of the specified context and the next node matches the rest.

551 L<swapPrev|/swapPrev> - Swap B<$node> with its preceding node if $B<$node> matches the first element of the specified context and the previous node matches the rest.

552 L<swapTags|/swapTags> - Swap the tags of two nodes optionally checking that the first node is in the specified context and return (B<$first>, B<$second>) nodes.

553 L<swapTagWithParent|/swapTagWithParent> - Swap the tags of the specified B<$node> and its parent optionally checking that the B<$node> is in the specified context and return (parent of B<$node>, B<$node>) or () if there is no such parent node.

554 L<through|/through> - Traverse L<parse|/parse> tree visiting each node twice calling the specified sub B<$before> as we go down past the node and sub B<$after> as we go up past the node, finally return the specified starting node.

555 L<throughX|/throughX> - Identical to L<through|/through> except the B<$before, $after> subs are called in an L<eval|http://perldoc.perl.org/functions/eval.html> block to prevent L<die|http://perldoc.perl.org/functions/die.html> terminating the traversal of the full tree.

556 L<to|/to> - Return a list of the sibling nodes preceding the specified node optionally including only those nodes that match one of the tags in the specified list.

557 L<tocNumbers|/tocNumbers> - Table of Contents number the nodes in a L<parse|/parse> tree.

558 L<top|/top> - Return the top of the parse tree containing the current B<$node> after optionally checking that the $node is in the optional B<@context>.

559 L<topicTypeAndBody|/topicTypeAndBody> - Topic type and corresponding body.

560 L<tree|/tree> - Build a tree representation of the parsed L<Xml|https://en.wikipedia.org/wiki/XML> which can be easily traversed to look for things.

561 L<undoSpecialChars|/undoSpecialChars> - Reverse the results of calling L<replaceSpecialChars|/replaceSpecialChars>.

562 L<unwrap|/unwrap> - Unwrap the specified B<$node> if in the optional B<@context> by replacing the node with its contents.

563 L<unwrapContentsKeepingText|/unwrapContentsKeepingText> - Unwrap all the non text nodes below the specified B<$node> adding a leading and a trailing space to prevent unwrapped content from being elided and return the specified B<$node> else B<undef> if not in the optional B<@context>.

564 L<unwrapOnlyChild|/unwrapOnlyChild> - Unwrap the specified B<$node> in the optional B<@context> when the B<$node> is the only child of its parent.

565 L<unwrapParentOfOnlyChild|/unwrapParentOfOnlyChild> - Unwrap the parent of the specified B<$node> in the optional B<@context> when the B<$node> is the only child of its parent.

566 L<unwrapParentsWithSingleChild|/unwrapParentsWithSingleChild> - Unwrap any immediate ancestors of the specified B<$node> in the optional B<@context> which have only a single child and return the specified B<$node> regardless unless the node is not in the optional context in which case return B<undef>.

567 L<unwrapSingleParentsOfSection|/unwrapSingleParentsOfSection> - Unwrap single parents of section: in word documents the header is often buried in a list to gain a section number - here we remove these unnecessary items

568 L<up|/up> - Return the parent of the current node optionally checking the parent node's context or return B<undef> if the specified B<$node> is the root of the L<parse|/parse> tree.

569 L<upn|/upn> - Go up the specified number of levels from the specified B<$node> and return the node reached optionally checking the parent node's context or B<undef> if there is no such node.

570 L<upThru|/upThru> - Go up the specified path from the specified B<$node> returning the node at the top or B<undef> if no such node exists.

571 L<upUntil|/upUntil> - Find the first node going up from B<$node> that matches the specified B<@context>.

572 L<upUntilFirst|/upUntilFirst> - Move up from the specified B<$node> until we reach the root or a first node.

573 L<upUntilIsOnlyChild|/upUntilIsOnlyChild> - Move up from the specified B<$node> until we reach the root or another only child.

574 L<upUntilLast|/upUntilLast> - Move up from the specified B<$node> until we reach the root or a last node.

575 L<upWhile|/upWhile> - Go up one level from the specified B<$node> and then continue up while each node matches on of the specified <@tags>.

576 L<upWhileFirst|/upWhileFirst> - Move up from the specified B<$node> as long as each node is a first node or return B<undef> if the specified B<$node> is not a first node.

577 L<upWhileIsOnlyChild|/upWhileIsOnlyChild> - Move up from the specified B<$node> as long as each node is an only child or return B<undef> if the specified B<$node> is not an only child.

578 L<upWhileLast|/upWhileLast> - Move up from the specified B<$node> as long as each node is a last node or return B<undef> if the specified B<$node> is not a last node.

579 L<wordStyles|/wordStyles> - Extract style information from a parse tree representing a word document.

580 L<wrapContentWith|/wrapContentWith> - Wrap the content of the specified B<$node> in a new node created from the specified B<$tag> and B<%attributes>: the specified B<$node> then contains just the new node which, in turn, contains all the content of the specified B<$node>.

581 L<wrapContentWithDup|/wrapContentWithDup> - Wrap the content if the specified B<$node> in a new node with the same tag as $node in the optional B<@context> making the new node an L<only child|/isOnlyChild> of $node.

582 L<wrapDown|/wrapDown> - Wrap the content of the specified B<$node> in a sequence of new nodes forcing the original node up - deepening the L<parse|/parse> tree - return the array of wrapping nodes if want array else the last wrapping node.

583 L<wrapFirstN|/wrapFirstN> - Wrap the first B<$N> nodes under this B<$node> in the optional B<@context> with the specified B<$tag> and return the new node or B<undef> if there are no such nodes to wrap.

584 L<wrapFrom|/wrapFrom> - Wrap all the nodes from the B<$start> node to the B<$end> node with a new node created from the specified B<$tag> and B<%attributes> and return the new node.

585 L<wrapFromFirst|/wrapFromFirst> - Wrap this B<$node> and any preceding siblings with a new node created from the specified B<$tag> and B<%attributes> and return the wrapping node.

586 L<wrapFromFirstOrLastIn|/wrapFromFirstOrLastIn> - Wrap inclusively from the first sibling node to the specified B<$node> or from the last prior sibling node whose tag matches one of the tags in B<@targets> to the specified B<$node> using B<$tag> as the tag of the wrapping node and return the wrapping node.

587 L<wrapLastN|/wrapLastN> - Wrap the last B<$N> nodes under this B<$node> in the optional B<@context> with the specified B<$tag> and return the new node or B<undef> if there are no such nodes to wrap.

588 L<wrapNext|/wrapNext> - Wrap this B<$node> and the following sibling with a new node created from the specified B<$tag>.

589 L<wrapNextN|/wrapNextN> - Wrap this B<$node>and the next B<$N>-1 nodes following this B<$node> in the optional B<@context> with the specified B<$tag> and return the new node or B<undef> if there are no such nodes to wrap.

590 L<wrapPrev|/wrapPrev> - Wrap this B<$node> and the preceding sibling with a new node created from the specified B<$tag>.

591 L<wrapPrevN|/wrapPrevN> - Wrap this B<$node>and the previous B<$N>-1 nodes following this B<$node> in the optional B<@context> with the specified B<$tag> and return the new node or B<undef> if there are no such nodes to wrap.

592 L<wrapRuns|/wrapRuns> - Wrap consecutive runs of children under the specified parent B<$node> that are not already wrapped with B<$wrap>.

593 L<wrapSiblingsAfter|/wrapSiblingsAfter> - If there are any siblings after the specified B<$node>, wrap them with a new node created from the specified B<$tag> and B<%attributes> and return the newly created node.

594 L<wrapSiblingsBefore|/wrapSiblingsBefore> - If there are any siblings before the specified B<$node>, wrap them with a new node created from the specified B<$tag> and B<%attributes> and return the newly created node.

595 L<wrapSiblingsBetween|/wrapSiblingsBetween> - If there are any siblings between the specified B<$node>s, wrap them with a new node created from the specified B<$tag> and B<%attributes>.

596 L<wrapTo|/wrapTo> - Wrap all the nodes from the B<$start> node to the B<$end> node inclusive with a new node created from the specified B<$tag> and B<%attributes> and return the new node.

597 L<wrapToLast|/wrapToLast> - Wrap this B<$node> and any following siblings with a new node created from the specified B<$tag> and B<%attributes> and return the wrapping node.

598 L<wrapToLastOrFirstIn|/wrapToLastOrFirstIn> - Wrap this B<$node> and any following siblings, up to and including the first sibling that matches one of the specified B<@find> nodes or all following siblings if no such match occurs, with a new node created from the specified B<$tag> and return the new wrapping node.

599 L<wrapUp|/wrapUp> - Wrap the specified B<$node> in a sequence of new nodes created from the specified B<@tags> forcing the original node down - deepening the L<parse|/parse> tree - return the array of wrapping nodes if want array else the last wrapping node.

600 L<wrapWith|/wrapWith> - Wrap the specified B<$node> in a new node created from the specified B<$tag> in the optional B<@context> forcing the specified $node down to deepen the L<parse|/parse> tree - return the new wrapping node or B<undef> if this is not possible.

601 L<wrapWithAll|/wrapWithAll> - Wrap this B<$node> wrapped with the specified B<@tags> and return the last wrapping node.

602 L<wrapWithDup|/wrapWithDup> - Wrap the specified B<$node> in a new node with the same tag as $node in the optional B<@context> making $node an L<only child|/isOnlyChild> of the new node.

603 L<wrapWithN|/wrapWithN> - Wrap this B<$node> with the first B<$N> elements of B<@context>.

604 L<writeCompressedFile|/writeCompressedFile> - Write the parse tree starting at B<$node> as compressed L<Xml|https://en.wikipedia.org/wiki/XML> to the specified B<$file>.

605 L<writeContentToGitHub|/writeContentToGitHub> - Upload the contents of a string to a file on github

606 L<writeFileToGitHub|/writeFileToGitHub> - Upload the contents of the specified local file to a file on github

607 L<xmlHeader|/xmlHeader> - Add the standard L<Xml|https://en.wikipedia.org/wiki/XML> header to a string

608 L<xmlToJson|/xmlToJson> - Return a Json string representing valid L<Xml|https://en.wikipedia.org/wiki/XML> contained in a file or string, optionally double escaping escape characters.

609 L<zipDown|/zipDown> - Push a node down as many levels as possible by making it a child of a node formed by merging the preceding and following siblings if they have the same tag.

610 L<zipDownOnce|/zipDownOnce> - Push a node down one level by making it a child of a node formed by merging the preceding and following siblings if they have the same tag.

=head1 Installation

This module is written in 100% Pure Perl and, thus, it is easy to read,
comprehend, use, modify and install via B<cpan>:

  sudo cpan install Data::Edit::Xml

=head1 Author

L<philiprbrenan@gmail.com|mailto:philiprbrenan@gmail.com>

L<http://www.appaapps.com|http://www.appaapps.com>

=head1 Copyright

Copyright (c) 2016-2019 Philip R Brenan.

This module is free software. It may be used, redistributed and/or modified
under the same terms as Perl itself.

=cut


sub firstNonBlank
 {my $r = &first($_[0]);
  return undef unless $r;
  if ($r->isBlankText)
   {shift @_;
    return &next($r, @_)
   }
  else
   {return &next(@_);
   }
 }

sub firstNonBlankX
 {my $r = &firstNonBlank(@_);
  die 'first' unless defined($r);
  $r
 }

sub isFirstNonBlank
 {my $r = &isFirst($_[0]);
  return undef unless $r;
  if ($r->isBlankText)
   {shift @_;
    return &isFirst($r, @_)
   }
  else
   {return &isFirst(@_);
   }
 }

sub isFirstNonBlankX
 {my $r = &isFirstNonBlank(@_);
  die 'isFirst' unless defined($r);
  $r
 }

sub isLastNonBlank
 {my $r = &isLast($_[0]);
  return undef unless $r;
  if ($r->isBlankText)
   {shift @_;
    return &isLast($r, @_)
   }
  else
   {return &isLast(@_);
   }
 }

sub isLastNonBlankX
 {my $r = &isLastNonBlank(@_);
  die 'isLast' unless defined($r);
  $r
 }

sub lastNonBlank
 {my $r = &last($_[0]);
  return undef unless $r;
  if ($r->isBlankText)
   {shift @_;
    return &prev($r, @_)
   }
  else
   {return &prev(@_);
   }
 }

sub lastNonBlankX
 {my $r = &lastNonBlank(@_);
  die 'last' unless defined($r);
  $r
 }

sub nextNonBlank
 {my $r = &next($_[0]);
  return undef unless $r;
  if ($r->isBlankText)
   {shift @_;
    return &next($r, @_)
   }
  else
   {return &next(@_);
   }
 }

sub nextNonBlankX
 {my $r = &nextNonBlank(@_);
  die 'next' unless defined($r);
  $r
 }

sub prevNonBlank
 {my $r = &prev($_[0]);
  return undef unless $r;
  if ($r->isBlankText)
   {shift @_;
    return &prev($r, @_)
   }
  else
   {return &prev(@_);
   }
 }

sub prevNonBlankX
 {my $r = &prevNonBlank(@_);
  die 'prev' unless defined($r);
  $r
 }


# Tests and documentation

sub test
 {my $p = __PACKAGE__;
  binmode($_, ":utf8") for *STDOUT, *STDERR;
  return if eval "eof(${p}::DATA)";
  my $s = eval "join('', <${p}::DATA>)";
  $@ and die $@;
  eval $s;
  $@ and die $@;
  1
 }

test unless caller;

1;
# podDocumentation
__DATA__
use Test::More;
use warnings FATAL=>qw(all);
use strict;
use Data::Table::Text qw(:all);
use Time::HiRes qw(time);
use Carp qw(confess);

my $startTime = time;

#goto latestTest;

if ($^O !~ m(bsd|linux)i)
 {plan skip_all => 'Not supported';
 }

Test::More->builder->output("/dev/null")                                        # Show only errors during testing
  if ((caller(1))[0]//'Data::Edit::Xml') eq "Data::Edit::Xml";

sub sample1{my $x = Data::Edit::Xml::new(); $x->input = <<END; $x->parse}       # Sample test XML
<foo start="yes">
  <head id="a" key="aaa bbb" start="123">Hello
    <em>there</em>
  </head>
  <bar>Howdy
    <ref/>
  </bar>do
doo
  <head id="A" key="AAAA BBBB" start="123">HHHHello
    <b>to you</b>
  </head>
  <tail>
    <foot id="11"/>
    <middle id="mm"/>
    <foot id="22"/>
  </tail>
</foo>
END

if (1) {
  my $x = Data::Edit::Xml::new;                                                 #Tparse
     $x->inputString = <<END;                                                   #Tparse
<a id="aa"><b id="bb"><c id="cc"/></b></a>
END
     $x->parse;                                                                 #Tparse
     ok -p $x eq <<END;                                                         #Tparse
<a id="aa">
  <b id="bb">
    <c id="cc"/>
  </b>
</a>
END
 }

sub sample2
 {my $x = Data::Edit::Xml::new;                                                 #TinputString
     $x->inputString = <<END;                                                   #TinputString
<a id="aa"><b id="bb"><c id="cc"/></b></a>
END
     $x->parse;                                                                 #TinputString
 }

if (1)                                                                          # Parse and string
 {my $x = sample1;
  if (my $s = $x->string)
   {ok $s eq trim(<<END);
<foo start="yes"><head id="a" key="aaa bbb" start="123">Hello
    <em>there</em></head><bar>Howdy
    <ref/></bar>do
doo
  <head id="A" key="AAAA BBBB" start="123">HHHHello
    <b>to you</b></head><tail><foot id="11"/><middle id="mm"/><foot id="22"/></tail></foo>
END
#   ok nn($x->prettyString)  eq '<foo start="yes">N  <head id="a" key="aaa bbb" start="123">HelloN    N    <em>thereN    </em>N  </head>N  <bar>HowdyN    N    <ref/>N  </bar>NdoNdooN  N  <head id="A" key="AAAA BBBB" start="123">HHHHelloN    N    <b>to youN    </b>N  </head>N  <tail>N    <foot id="11"/>N    <middle id="mm"/>N    <foot id="22"/>N  </tail>N</foo>N';
    ok nn($x->stringContent) eq '<head id="a" key="aaa bbb" start="123">HelloN    <em>there</em></head><bar>HowdyN    <ref/></bar>doNdooN  <head id="A" key="AAAA BBBB" start="123">HHHHelloN    <b>to you</b></head><tail><foot id="11"/><middle id="mm"/><foot id="22"/></tail>';
    ok $x->attr(qq(start))   eq "yes";
       $x->id  = 11;
    ok $x->id == 11;
       $x->deleteAttr(qq(id));
    ok !$x->id;
    ok join(' ', $x->go(qw(head))->attrs(qw(id start))) eq "a 123";
#   ok nn($x->prettyStringContent) eq '<head id="a" key="aaa bbb" start="123">HelloN    N  <em>thereN  </em>N</head>N<bar>HowdyN    N  <ref/>N</bar>NdoNdooN  N<head id="A" key="AAAA BBBB" start="123">HHHHelloN    N  <b>to youN  </b>N</head>N<tail>N  <foot id="11"/>N  <middle id="mm"/>N  <foot id="22"/>N</tail>N';
    ok $x->countTags == 17;
    ok $x->go(qw(head 1))->countTags == 4;
   }
  if (1)                                                                        # Conditions
   {my $m = $x->go(qw(tail middle));
    $m->addConditions(qw(middle MIDDLE));                                       # Add
    ok join(' ', $m->listConditions) eq 'MIDDLE middle';                        # List
    $m->deleteConditions(qw(MIDDLE));                                           # Remove
    ok join('', $m->listConditions) eq 'middle';
    $_->addConditions(qw(foot)) for $x->go(qw(tail foot *));

    ok $x->stringWithConditions(qw(middle)) eq trim(<<END);
<foo start="yes"><head id="a" key="aaa bbb" start="123">Hello
    <em>there</em></head><bar>Howdy
    <ref/></bar>do
doo
  <head id="A" key="AAAA BBBB" start="123">HHHHello
    <b>to you</b></head><tail><middle id="mm"/></tail></foo>
END

    ok $x->stringWithConditions(qw(foot))  eq trim(<<END);
<foo start="yes"><head id="a" key="aaa bbb" start="123">Hello
    <em>there</em></head><bar>Howdy
    <ref/></bar>do
doo
  <head id="A" key="AAAA BBBB" start="123">HHHHello
    <b>to you</b></head><tail><foot id="11"/><foot id="22"/></tail></foo>
END

    ok $x->stringWithConditions(qw(none)) eq trim(<<END);
<foo start="yes"><head id="a" key="aaa bbb" start="123">Hello
    <em>there</em></head><bar>Howdy
    <ref/></bar>do
doo
  <head id="A" key="AAAA BBBB" start="123">HHHHello
    <b>to you</b></head><tail/></foo>
END

    ok $x->stringWithConditions(qw(foot middle)) eq $x->string;
    ok $x->stringWithConditions eq $x->string;
   }

  if (my $h = $x->go(qw(head))) {ok $h->id eq q(a)} else {ok 0}                 # Attributes and sub nodes

 # Contents
  is_deeply [map {$_->tag} $x->contents]                       , [qw(head   bar    CDATA   head   tail)];
  is_deeply [map {$_->tag} $x->go(qw(head))   ->contentAfter],  [qw(bar    CDATA  head    tail)];
  is_deeply [map {$_->tag} $x->go(qw(head), 1)->contentBefore], [qw(head   bar    CDATA)];

  ok $x->contentAsTags  eq join ' ', qw(head bar CDATA head tail);
  ok $x->go(qw(head),0)->contentAfterAsTags eq join ' ', qw(     bar CDATA head tail);
  ok $x->go(qw(head),1)->contentBeforeAsTags eq join ' ', qw(head bar CDATA);

  ok $x->over(qr(\Ahead bar CDATA head tail\Z));
  ok $x->go(qw(head),0)->matchAfter (qr(\Abar CDATA head tail\Z));
  ok $x->go(qw(head),1)->matchBefore(qr(\Ahead bar CDATA\Z));

  ok $x->c(qw(head)) == 2;
  ok $x->go(qw(tail))->present(qw(foot middle aaa bbb)) == 2;                  # Presence of the specified tags
  ok $x->go(qw(tail))->present(qw(foot aaa bbb)) == 1;
  ok $x->go(qw(tail))->present(qw(     aaa bbb)) == 0;
  ok $x->go(qw(tail foot))->present(qw(aaa bbb)) == 0;
  if (1)
   {my $c = $x->count(qw(head tail aaa));
    ok $c == 3;
    my @c = $x->count(qw(head tail aaa));
    ok "@c" eq "2 1 0";
    my $t = $x->count(qw(CDATA));
    ok $t == 1;
    my $T = $x->count;
    ok $T == 5;
   }

  if (1)                                                                        # First child
   {ok $x->go(qw(tail))->firstIn(qw(foot middle))->id ==    11;
    ok $x->go(qw(tail))-> lastIn(qw(feet middle))->id eq qq(mm);
   }

  ok $x->go(qw(head *)) == 2;
  ok $x->go(qw(head),1)->position == 3;

  ok $x->go(qw(tail))->first->id == 11;
  ok $x->go(qw(tail))->last ->id == 22;
  ok $x->go(qw(tail))->first->isFirst;
  ok $x->go(qw(tail))->last ->isLast;

  ok !$x->go(qw(tail))->last->isOnlyChild;

  ok $x->go(qw(tail))->first->next->id eq 'mm';
  ok $x->go(qw(tail))->last->prev->prev->isFirst;

  ok $x->go(qw(head))->go(qw(em))->first->at(qw(CDATA em head foo));            # At

  if (1)                                                                        # Through
   {my @t;
    $x->first->by(sub {my ($o) = @_; push @t, $o->tag});
    is_deeply [@t], [qw(CDATA  CDATA  em  head)];
   }

  if (1)
   {my @t;
    $x->last->by(sub {my ($o) = @_; push @t, $o->tag});
    is_deeply [@t], [qw(foot middle foot tail)];
   }

# Editing - outermost - wrapWith

  ok sample1->wrapWith("out")->string eq trim(<<END);
<out><foo start="yes"><head id="a" key="aaa bbb" start="123">Hello
    <em>there</em></head><bar>Howdy
    <ref/></bar>do
doo
  <head id="A" key="AAAA BBBB" start="123">HHHHello
    <b>to you</b></head><tail><foot id="11"/><middle id="mm"/><foot id="22"/></tail></foo></out>
END

  ok sample1->wrapContentWith("out")->parent->string eq trim(<<END);
<foo start="yes"><out><head id="a" key="aaa bbb" start="123">Hello
    <em>there</em></head><bar>Howdy
    <ref/></bar>do
doo
  <head id="A" key="AAAA BBBB" start="123">HHHHello
    <b>to you</b></head><tail><foot id="11"/><middle id="mm"/><foot id="22"/></tail></out></foo>
END

# Editing - inner - wrapWith
  ok sample1->go(qw(tail))->go(qw(middle))->wrapWith("MIDDLE")->parent->parent->string eq trim(<<END);
<foo start="yes"><head id="a" key="aaa bbb" start="123">Hello
    <em>there</em></head><bar>Howdy
    <ref/></bar>do
doo
  <head id="A" key="AAAA BBBB" start="123">HHHHello
    <b>to you</b></head><tail><foot id="11"/><MIDDLE><middle id="mm"/></MIDDLE><foot id="22"/></tail></foo>
END

 ok sample1->go(qw(tail))->go(qw(middle))->wrapContentWith("MIDDLE")->parent->parent->parent->string eq trim(<<END);
<foo start="yes"><head id="a" key="aaa bbb" start="123">Hello
    <em>there</em></head><bar>Howdy
    <ref/></bar>do
doo
  <head id="A" key="AAAA BBBB" start="123">HHHHello
    <b>to you</b></head><tail><foot id="11"/><middle id="mm"><MIDDLE/></middle><foot id="22"/></tail></foo>
END

# Editing - cut/put

  if (1)
   {my $a = sample2;
    ok $a->go(qw(b))  ->id eq q(bb);
    ok $a->go(qw(b c))->id eq q(cc);

    ok -p $a eq <<END;                                                          #TputFirst #Tcut #TcountTags
<a id="aa">
  <b id="bb">
    <c id="cc"/>
  </b>
</a>
END
    ok $a->countTags == 3;                                                      #TcountTags
    my $c = $a->go(qw(b c))->cut;                                               #TputFirst #Tcut

    ok -p $a eq <<END;                                                          #Tcut
<a id="aa">
  <b id="bb"/>
</a>
END
    $a->putFirst($c);                                                           #TputFirst

    ok -p $a eq <<END;                                                          #TputFirst #TputLast
<a id="aa">
  <c id="cc"/>
  <b id="bb"/>
</a>
END

    $a->putLast($a->go(qw(c))->cut);                                            #TputLast

    ok -p $a eq <<END;                                                          #TputLast #TputNext
<a id="aa">
  <b id="bb"/>
  <c id="cc"/>
</a>
END

    $a->go(qw(c))->putNext($a->go(q(b))->cut);                                  #TputNext

    ok -p $a eq <<END;                                                          #TputNext #TputPrev
<a id="aa">
  <c id="cc"/>
  <b id="bb"/>
</a>
END

    $a->go(qw(c))->putPrev($a->go(q(b))->cut);                                  #TputPrev

    ok -p $a eq <<END;                                                          #TputPrev
<a id="aa">
  <b id="bb"/>
  <c id="cc"/>
</a>
END
   }

# Editing - unwrap

  ok sample2->go(qw(b))->unwrap->string eq '<a id="aa"><c id="cc"/></a>';
  ok sample2->go(qw(b c))->putFirst(sample2)->parent->parent->parent->string eq '<a id="aa"><b id="bb"><c id="cc"><a id="aa"><b id="bb"><c id="cc"/></b></a></c></b></a>';
  ok sample2->go(qw(b c))->replaceWith(sample2)->go(qw(b c))->upUntil(qw(a b))->string eq '<a id="aa"><b id="bb"><c id="cc"/></b></a>';

  if (1)
   {my $x = sample2;
    $x->go(qw(b c))->unwrap;
    ok -s $x eq '<a id="aa"><b id="bb"/></a>';
    $x->go(q(b))->unwrap;
    ok -s $x eq '<a id="aa"/>';
   }

  if (1) {
    my $x = Data::Edit::Xml::new(qq(<a><b><c><d>DD</d>EE<f>FF</f></c></b></a>));#TunwrapContentsKeepingText
    ok -p $x eq <<END;                                                          #TunwrapContentsKeepingText
<a>
  <b>
    <c>
      <d>DD</d>
EE
      <f>FF</f>
    </c>
  </b>
</a>
END
    $x->go(qw(b))->unwrapContentsKeepingText;                                   #TunwrapContentsKeepingText
    ok -p $x eq <<END;                                                          #TunwrapContentsKeepingText
<a>
  <b>  DD EE FF  </b>
</a>
END
   }

  if (1)
   {my $x = Data::Edit::Xml::new(qq(<a><b><c id="cc"/></b></a>));               #TreplaceWith
    $x->go(qw(b c))->replaceWith($x->newTag(qw(d id dd)));                      #TreplaceWith
    ok -s $x eq '<a><b><d id="dd"/></b></a>';                                   #TreplaceWith
   }

  if (1)
   {my $x = Data::Edit::Xml::new(qq(<a><b><c id="cc"/></b></a>));               #TreplaceWithText
    $x->go(qw(b c))->replaceWithText(qq(BBBB));                                 #TreplaceWithText
    ok -s $x eq '<a><b>BBBB</b></a>';                                           #TreplaceWithText
   }

  if (1)
   {my $x = Data::Edit::Xml::new(qq(<a><b/><c/></a>));                          #TreplaceContentWith
    $x->replaceContentWith(map {$x->newTag($_)} qw(B C));                       #TreplaceContentWith
    ok -s $x eq '<a><B/><C/></a>';                                              #TreplaceContentWith
   }

  if (1)
   {my $x = Data::Edit::Xml::new(qq(<a><b/><c/></a>));                          #TreplaceContentWithText
    $x->replaceContentWithText(qw(b c));                                        #TreplaceContentWithText
    ok -s $x eq '<a>bc</a>';                                                    #TreplaceContentWithText
   }

  if (1)
   {my $x = Data::Edit::Xml::new(qq(<a><b><c id="cc"/></b></a>));               #TreplaceWithBlank
    $x->go(qw(b c))->replaceWithBlank;                                          #TreplaceWithBlank
    ok -s $x eq '<a><b> </b></a>';                                              #TreplaceWithBlank
   }

# Editing - tag /attributes

  ok  sample2->go(q(b))->change(qw(B b a))->parent->string eq '<a id="aa"><B id="bb"><c id="cc"/></B></a>';
  ok !sample2->go(q(b))->change(qw(B c a));
  ok  sample2->go(q(b))->setAttr(aa=>11, bb=>22)->parent->string eq '<a id="aa"><b aa="11" bb="22" id="bb"><c id="cc"/></b></a>';
  ok  sample2->go(qw(b c))->setAttr(aa=>11, bb=>22)->parent->parent->string eq '<a id="aa"><b id="bb"><c aa="11" bb="22" id="cc"/></b></a>';
  ok  sample2->deleteAttr_id eq 'aa';
  ok  sample2->renameAttr(qw(id ID))->string eq '<a ID="aa"><b id="bb"><c id="cc"/></b></a>';
  ok  sample2->changeAttr(qw(ID id))->id eq qq(aa);

  ok  sample2->renameAttrValue(qw(id aa ID AA))->string eq '<a ID="AA"><b id="bb"><c id="cc"/></b></a>';
  ok  sample2->changeAttrValue(qw(ID AA id aa))->id eq qq(aa);
 }

if (1) {
  my $a = Data::Edit::Xml::new(<<END);                                          #TstringWithConditions
<a>
  <b>
    <c/>
    <d/>
  </b>
</a>
END

  my $b = $a >= 'b';                                                            #TstringWithConditions
  my ($c, $d) = $b->contents;                                                   #TstringWithConditions

  $b->addConditions(qw(bb BB));                                                 #TstringWithConditions #TaddConditions #TlistConditions  #Tcondition #TanyCondition #TallConditions
  $c->addConditions(qw(cc CC));                                                 #TstringWithConditions                                   #Tcondition #TanyCondition #TallConditions

  ok  $c->condition(q(cc));                                                     #Tcondition
  ok !$c->condition(q(dd));                                                     #Tcondition
  ok  $c->condition(q(cc), qw(c b a));                                          #Tcondition

  ok  $b->anyCondition(qw(bb cc));                                              #TanyCondition
  ok !$b->anyCondition(qw(cc CC));                                              #TanyCondition
  ok  $b->allConditions(qw(bb BB));                                             #TallConditions
  ok !$b->allConditions(qw(bb cc));                                             #TallConditions

  ok join(' ', $b->listConditions) eq 'BB bb';                                  #TdeleteConditions     #TaddConditions #TlistConditions
  $b->deleteConditions(qw(BB));                                                 #TdeleteConditions
  ok join(' ', $b->listConditions) eq 'bb';                                     #TdeleteConditions

  ok $a->stringWithConditions         eq '<a><b><c/><d/></b></a>';              #TstringWithConditions
  ok $a->stringWithConditions(qw(bb)) eq '<a><b><d/></b></a>';                  #TstringWithConditions
  ok $a->stringWithConditions(qw(cc)) eq '<a/>';                                #TstringWithConditions
 }

if (1) {
  my $x = Data::Edit::Xml::new(my $s = <<END);                                  #Tattr
<a number="1"/>
END
  ok $x->attr(qq(number)) == 1;                                                 #Tattr
     $x->attr(qq(number))  = 2;                                                 #Tattr
  ok $x->attr(qq(number)) == 2;                                                 #Tattr
  ok -s $x eq '<a number="2"/>';                                                #Tattr

  $x->attr(qq(delete))  = "me";
  ok -s $x eq '<a delete="me" number="2"/>';                                    #TdeleteAttr
  $x->deleteAttr(qq(delete));                                                   #TdeleteAttr
  ok -s $x eq '<a number="2"/>';                                                #TdeleteAttr #TsetAttr

  $x->setAttr(first=>1, second=>2, last=>undef);                                #TsetAttr

  ok -s $x eq '<a first="1" number="2" second="2"/>';                           #TdeleteAttrs #Tattrs #TsetAttr #TgetAttrs #TattrCount
  ok $x->attrCount == 3;                                                        #TattrCount
  ok $x->attrCount(qw(first second third)) == 1;                                #TattrCount
  is_deeply [$x->attrs(qw(third second first ))], [undef, 2, 1];                #Tattrs
  is_deeply [$x->getAttrs], [qw(first number second)];                          #TgetAttrs

  $x->deleteAttrs(qw(first second third number));                               #TdeleteAttrs
  ok -s $x eq '<a/>';                                                           #TdeleteAttrs
 }

if (1)
 {my $a = newTag(undef, q(a), id=>"aa", a=>"1", b=>"1");
  ok q(<a a="1" b="1" id="aa"/>) eq -s $a;                                      #Tset
  $a->set(a=>11, b=>undef, c=>3, d=>4, e=>5);                                   #Tset
  ok q(<a a="11" c="3" d="4" e="5" id="aa"/>) eq -s $a;
 }                                                                              #Tset

if (1) {
  my $a = Data::Edit::Xml::new('<a number="1"/>');                              #TopAttr
  ok $a %  qq(number) == 1;                                                     #TopAttr
 }

#if (1)
# {my $a = Data::Edit::Xml::new("<a/>");                                        TopSetTag
#  $a += qq(b);                                                                 TopSetTag
#  ok -s $a eq "<b/>";                                                          TopSetTag
# }

if (1) {
  my $c = Data::Edit::Xml::new("<c/>");                                         #TopWrapWith
  my $b = $c / qq(b);                                                           #TopWrapWith
  ok -s $b eq "<b><c/></b>";                                                    #TopWrapWith
  my $a = $b / qq(a);                                                           #TopWrapWith
  ok -s $a eq "<a><b><c/></b></a>";                                             #TopWrapWith
 }

if (1) {
  my $a = Data::Edit::Xml::new("<a><b><c/></b></a>");                           #TopUnWrap
  my $b = $a >= 'b';                                                            #TopUnWrap
   ++$b;                                                                        #TopUnWrap
  ok -s $a eq "<a><c/></a>";                                                    #TopUnWrap
 }

if (1) {
  my $x = Data::Edit::Xml::new(my $s = <<END);                                  #TprintAttributes
<a no="1" word="first"/>
END
  ok $x->printAttributes eq qq( no="1" word="first");                           #TrenameAttr #TprintAttributes
  $x->renameAttr(qw(no number));                                                #TrenameAttr
  ok $x->printAttributes eq qq( number="1" word="first");                       #TrenameAttr #TchangeAttr
  $x->changeAttr(qw(number word));                                              #TchangeAttr
  ok $x->printAttributes eq qq( number="1" word="first");                       #TchangeAttr #TrenameAttrValue

  $x->renameAttrValue(qw(number 1 numeral I));                                  #TrenameAttrValue
  ok $x->printAttributes eq qq( numeral="I" word="first");                      #TrenameAttrValue #TchangeAttrValue

  $x->changeAttrValue(qw(word second greek mono));                              #TchangeAttrValue
  ok $x->printAttributes eq qq( numeral="I" word="first");                      #TchangeAttrValue

  $x->changeAttrValue(qw(word first greek mono));                               #TchangeAttrValue
  ok $x->printAttributes eq qq( greek="mono" numeral="I");                      #TchangeAttrValue
 }

if (1) {
  my $x = Data::Edit::Xml::new(my $s = <<END);                                  #TwrapTo #Tgo
<aa>
  <a>
    <b/>
      <c id="1"/><c id="2"/><c id="3"/><c id="4"/>
    <d/>
  </a>
</aa>
END
  ok $x->go(qw(a c))   ->id == 1;                                               #Tgo
  ok $x->go(qw(a c -2))->id == 3;                                               #Tgo
  ok $x->go(qw(a c *)) == 4;                                                    #Tgo
  ok 1234 == join '', map {$_->id} $x->go(qw(a c *));                           #Tgo
  $x->go(qw(a c))->wrapTo($x->go(qw(a c -1)), qq(C), id=>1234);                 #TwrapTo
  ok -p $x eq <<END;                                                            #TwrapTo
<aa>
  <a>
    <b/>
    <C id="1234">
      <c id="1"/>
      <c id="2"/>
      <c id="3"/>
      <c id="4"/>
    </C>
    <d/>
  </a>
</aa>
END
  my $C = $x->go(qw(a C));                                                      #TwrapTo
  $C->wrapTo($C, qq(D));                                                        #TwrapTo
  ok -p $x eq <<END;                                                            #TwrapTo
<aa>
  <a>
    <b/>
    <D>
      <C id="1234">
        <c id="1"/>
        <c id="2"/>
        <c id="3"/>
        <c id="4"/>
      </C>
    </D>
    <d/>
  </a>
</aa>
END
 }

if (1)
 {my $a = Data::Edit::Xml::new(my $s = <<END);                                  #TwrapFrom
<a>
  <b>
    <c id="0"/><c id="1"/><c id="2"/><c id="3"/>
  </b>
</a>
END
  my $b = $a->first;                                                            #TwrapFrom
  my @c = $b->contents;                                                         #TwrapFrom
  $c[1]->wrapFrom($c[0], qw(D id DD));                                          #TwrapFrom
  ok -p $a eq <<END;                                                            #TwrapFrom
<a>
  <b>
    <D id="DD">
      <c id="0"/>
      <c id="1"/>
    </D>
    <c id="2"/>
    <c id="3"/>
  </b>
</a>
END
  $c[2]->wrapTo  ($c[2], qw(E id EE));
  $c[3]->wrapTo  ($c[3], qw(F id FF));
  ok -p $a eq <<END;                                                            #TwrapTo
<a>
  <b>
    <D id="DD">
      <c id="0"/>
      <c id="1"/>
    </D>
    <E id="EE">
      <c id="2"/>
    </E>
    <F id="FF">
      <c id="3"/>
    </F>
  </b>
</a>
END
 }

if (1)                                                                          # Blank text
 {my $f = temporaryFile;
  overWriteFile($f, "<a> <b/>   <c/> <d/> </a>");
  my $x = Data::Edit::Xml::new($f);
  unlink $f;
  $x->putFirstAsText(' ');
  $x->go(q(b))->putNextAsText(' ');
  $x->go(q(d))->putPrevAsText(' ');
  $x->putLastAsText(' ');

  ok $x->countTags == 8;
  ok $x->contentAsTags eq "CDATA b CDATA c CDATA d CDATA";
  my $c = $x->go(qw(c));
  $c->replaceWithBlank;

  ok $x->countTags == 6;
  ok $x->contentAsTags eq "CDATA b CDATA d CDATA";
 }

if (1)                                                                          # Blank text
 {my $f = temporaryFile;
  overWriteFile($f, "<a>  </a>");
  my $x = Data::Edit::Xml::new();
     $x->inputFile = $f;
     $x->parse;
  unlink $f;
  $x->putFirstAsText(' ') for 1..10;
  $x->putLastAsText(' ')  for 1..10;
  ok $x->countTags == 2;
  ok -s $x eq "<a>                    </a>";
 }

if (1) {
  my @files =                                                                   #TexpandIncludes
   (owf("in1/a.xml", q(<a id="a"><include href="../in2/b.xml"/></a>)),          #TexpandIncludes
    owf("in2/b.xml", q(<b id="b"><include href="c.xml"/></b>)),                 #TexpandIncludes
    owf("in2/c.xml", q(<c id="c"/>)));                                          #TexpandIncludes

  my $x = Data::Edit::Xml::new(fpf(currentDirectory, $files[0]));               #TexpandIncludes
     $x->expandIncludes;                                                        #TexpandIncludes
  ok <<END eq -p $x;                                                            #TexpandIncludes
<a id="a">
  <b id="b">
    <c id="c"/>
  </b>
</a>
END
  map{unlink $_} @files;
  map{rmdir  $_} map {my ($p) = parseFileName $_; $p} @files;
 }

if (1) {
  my $a = Data::Edit::Xml::new("<a><b>A</b></a>");                              #TprettyStringCDATA
  my $b = $a->first;                                                            #TprettyStringCDATA
     $b->first->replaceWithBlank;                                               #TprettyStringCDATA
  ok $a->prettyStringCDATA eq <<END;                                            #TprettyStringCDATA #TisText #TisBlankText
<a>
    <b><CDATA> </CDATA></b>
</a>
END
  ok $b->first->isText;                                                         #TisText
  ok $b->first->isText(qw(b a));                                                #TisText
  ok $b->first->isBlankText;                                                    #TisBlankText
 }

if (1) {                                                                        # Text
  my $x = Data::Edit::Xml::new(<<END);                                          #Tcount #TisEmpty
<a>

</a>
END
  ok $x->count == 0;                                                            #Tcount
  ok $x->isEmpty;                                                               #TisEmpty
  ok -s $x eq "<a/>";
  $x->putFirstAsText(' ');
  ok $x->count == 1;
  $x->putFirstAsText("\n");
  ok $x->countTags == 2;

  $x->putFirstAsText('3');
  ok nn($x->string) eq "<a>3N </a>";
  ok $x->countTags == 2;
  ok !$x->isEmpty;
  $x->putFirstAsText(' ');
  ok $x->countTags == 2;
  $x->putFirstAsText(' ');
  ok $x->countTags == 2;
  $x->putFirstAsText(' 2 ');
  ok $x->countTags == 2;
  $x->putFirstAsText("\n");
  ok $x->countTags == 2;
  $x->putFirstAsText(' ');
  ok $x->countTags == 2;
  $x->putFirstAsText(' 1 ');
  ok $x->countTags == 2;
  $x->putFirstAsText(' ');
  ok $x->countTags == 2;
  $x->putFirstAsText(' ');
  ok $x->first->tag eq qq(CDATA);
  ok $x->first->isText;
  ok $x->countTags == 2;
  ok nn($x->string) eq "<a>   1  N 2   3N </a>";
 }

if (1)                                                                          # Text and tags
 {my $x = Data::Edit::Xml::new(<<END);
<a>

  <b/>

  <c/>
</a>
END
  $x->by(sub
   {my ($o) = @_;
    $o->putFirstAsText($_) for ('  ', 'F', '', 'F', ' ', '');
    $o->putLastAsText ($_) for ('  ', 'L', '', 'L', '',  ' ');
    unless($o == $x)
     {$o->putNextAsText ($_) for (' N  ', ' N ', '', ' N ', ' ', ' N ');        # N will always be preceded and succeeded by spaces
      $o->putPrevAsText ($_) for (' P', '' ,   '', ' P',  ' ',   ' P')          # P will always be preceded               by spaces
     }
   });
  ok -s $x eq "<a> FF   P P  P<b> FF    LL </b> N   N  N  N   P P  P<c> FF    LL </c> N   N  N  N    LL </a>";
 }

if (1)
 {my $x = Data::Edit::Xml::new(<<END);                                          #TcontentAsTags #TcontentAfterAsTags #TcontentBeforeAsTags #TcontentAfter #Tover #Tover2 #TmatchAfter #TmatchAfter2  #TcontentAfterAsTags2 #TmatchBefore #TmatchBefore2 #TcontentBefore #TcontentBeforeAsTags2
<a>
  <b>
    <c/><d/><e/><f/><g/>
  </b>
</a>
END

  ok $x->go(q(b))->contentAsTags eq 'c d e f g';                                #TcontentAsTags

  ok 'f g' eq join ' ', map {$_->tag} $x->go(qw(b e))->contentAfter;            #TcontentAfterAsTags
  ok 'c d' eq join ' ', map {$_->tag} $x->go(qw(b e))->contentBefore;           #TcontentBeforeAsTags

  ok $x->go(qw(b e))->contentAfterAsTags eq 'f g';                              #TcontentAfterAsTags
  ok $x->go(qw(b e))->contentBeforeAsTags eq 'c d';                             #TcontentBeforeAsTags

  ok 'f g' eq join ' ', map {$_->tag} $x->go(qw(b e))->contentAfter;            #TcontentAfter
  ok 'c d' eq join ' ', map {$_->tag} $x->go(qw(b e))->contentBefore;           #TcontentBefore

  ok $x->go(q(b))->over(qr(d.+e));                                              #Tover
  ok $x->go(q(b))->over2(qr(\A c  d  e  f  g \Z));                              #Tover2
  ok $x->go(q(b))->contentAsTags  eq q(c d e f g) ;                             #Tover2
  ok $x->go(q(b))->contentAsTags2 eq q( c  d  e  f  g );                        #TcontentAsTags2
  ok $x->go(qw(b e))->matchAfter  (qr(\Af g\Z));                                #TmatchAfter
  ok $x->go(qw(b e))->matchAfter2 (qr(\A f  g \Z));                             #TmatchAfter2
  ok $x->go(qw(b e))->contentAfterAsTags2 eq q( f  g );                         #TcontentAfterAsTags2
  ok $x->go(qw(b e))->matchBefore (qr(\Ac d\Z));                                #TmatchBefore
  ok $x->go(qw(b e))->matchBefore2(qr(\A c  d \Z));                             #TmatchBefore2
  ok $x->go(qw(b e))->contentBeforeAsTags2 eq q( c  d );                        #TcontentBeforeAsTags2
 }

if (1) {
  my $a = Data::Edit::Xml::new(<<END);                                          #TopString #TopContents
<a>
  <b><c>ccc</c></b>
  <d><e>eee</e></d>
</a>
END
  my ($b, $d) =  @$a;                                                           #TopContents
  ok -c $b eq q(b a);                                                           #TopContents
  my ($c)     =  @$b;                                                           #TopContents
  my ($e)     =  @$d;
  ok -c $c eq q(c b a);                                                         #TopContents
  -x $c;
  ok -p $a eq <<END;
<a>
  <b/>
  <d>
    <e>eee</e>
  </d>
</a>
END
  -u $e;
  ok -p $a eq <<END;
<a>
  <b/>
  <d>eee</d>
</a>
END
  -R $d;
  ok -p $a eq <<END;
<a>
  <b/>
  <required-cleanup>&lt;d&gt;eee&lt;/d&gt;
</required-cleanup>
</a>
END
 }

if (1) {
  my $a = Data::Edit::Xml::new(<<END);                                          #Tnew #TopString #TbyList #TbyReverseList  #TprintNode
<a>
  <b>
    <c id="42" match="mm"/>
  </b>
  <d>
    <e/>
  </d>
</a>
END

  my ($c, $b, $e, $d) = $a->byList;                                             #TopString
  ok $c->printNode eq q(c id="42" match="mm");                                  #TopString #TprintNode
  ok -A $c eq q(c id="42" match="mm");                                          #TopString

    my ($E, $D, $C, $B) = $a->byReverseList;                                    #TbyReverseList
    ok -A $C eq q(c id="42" match="mm");                                        #TbyReverseList

  ok -b $e;                                                                     #TopString
  ok -c $e eq q(e d a);                                                         #TopString #TbyList

  ok -f $b eq $c;                                                               #TopString
  ok -l $a eq $d;                                                               #TopString

  ok -O $a, q( b  d );                                                          #TopString
  ok -o $a, q(b d);                                                             #TopString

  ok -w $a eq q('<a><b><c id="42" match="mm"/></b><d><e/></d></a>');            #TopString

  ok -p $a eq <<END;                                                            #TdownReverse #TdownReverseX #Tby #TopBy #TbyX #TbyReverse #TbyReverseX #Tnew #Tstring #TopString #Tcontext #TisFirst #TisLast #TopGo #TopAt
<a>
  <b>
    <c id="42" match="mm"/>
  </b>
  <d>
    <e/>
  </d>
</a>
END

  ok -s $a eq '<a><b><c id="42" match="mm"/></b><d><e/></d></a>';               #TopString #Tstring
  ok -t $a eq 'a';                                                              #TopString

  $a->numberTree;                                                               #TopString #TnumberTree

  ok -z $a eq <<END;                                                            #TopString #TnumberTree
<a id="1">
  <b id="2">
    <c id="42" match="mm"/>
  </b>
  <d id="4">
    <e id="5"/>
  </d>
</a>
END

  ok 'bd' eq join '', map {$_->tag} @$a ;
  ok (($a >= [qw(d e)]) <= [qw(e d a)]);                                        #TopGo #TopAt

  ok $a->go(qw(d e))->context eq 'e d a';                                       #Tcontext
  ok $a->go(q(b))->isFirst;                                                     #TisFirst
  ok $a->go(q(d))->isLast;                                                      #TisLast

  if (1)
   {my $s; $a->down(sub{$s .= $_->tag}); ok $s eq "abcde"                       #Tdown #TdownX
   }
  if (1)
   {my $s; $a->downReverse(sub{$s .= $_->tag}); ok $s eq "adebc"                #TdownReverse #TdownReverseX
   }
  if (1)
   {my $s; $a->by(sub{$s .= $_->tag}); ok $s eq "cbeda"                         #Tby
   }
  if (1)
   {my $s; $a->byX(sub{$s .= $_->tag}); ok $s eq "cbeda"                        #TbyX
   }
  if (1)
   {my $s; $a x= sub{$s .= -t $_}; ok $s eq "cbeda"                             #TopBy
   }
  if (1)
   {my $s; $a->byReverse(sub{$s .= $_->tag}); ok $s eq "edcba"                  #TbyReverse #TbyReverseX
   }

  if (1)
   {my $s; my $n = sub{$s .= $_->tag}; $a->through($n, $n);                     #Tthrough #TthroughX
    ok $s eq "abccbdeeda"                                                       #Tthrough #TthroughX
   }
 }

if (1) {
  my $x = Data::Edit::Xml::new(<<END);                                          #TopCut
<a>
  <b><c/></b>
</a>
END

  my $b = $x >= qq(b);                                                          #TopCut
   --$b;                                                                        #TopCut
  ok -s $x eq "<a/>";                                                           #TopCut
  ok -s $b eq "<b><c/></b>";                                                    #TopCut
 }

if (1)
 {my $a = Data::Edit::Xml::new('<a/>');
  ok -p $a eq <<END;                                                            #TopPutFirst
<a/>
END
  my $f = $a >> qq(first);                                                      #TopPutFirst
  ok -p $a eq <<END;                                                            #TopPutFirst #TopPutLast
<a>
  <first/>
</a>
END
  my $l = $a << qq(last);                                                       #TopPutLast
  ok -p $a eq <<END;                                                            #TopPutLast #TopPutNext
<a>
  <first/>
  <last/>
</a>
END

  $f += qq(next);                                                               #TopPutNext
  ok -p $a eq <<END;                                                            #TopPutNext  #TopPutPrev
<a>
  <first/>
  <next/>
  <last/>
</a>
END
  $l -= qq(prev);                                                               #TopPutPrev
  ok -p $a eq <<END;                                                            #TopPutPrev
<a>
  <first/>
  <next/>
  <prev/>
  <last/>
</a>
END
 }

if (1)
 {my $a = Data::Edit::Xml::new('<a/>');
  ok -p $a eq <<END;                                                            #TopPutFirstAssign
<a/>
END
  my $n = $a ** qq(z);
  ok $n->tag eq q(z);

  $a >>= qq(first);                                                             #TopPutFirstAssign
  ok -p $a eq <<END;                                                            #TopPutFirstAssign #TopPutLastAssign
<a>
  <first/>
</a>
END
  $a <<= qq(last);                                                              #TopPutLastAssign
  ok -p $a eq <<END;                                                            #TopPutLastAssign #TopPutNextAssign
<a>
  <first/>
  <last/>
</a>
END

  my $f = -f $a;                                                                #TopPutNextAssign
  $f += qq(next);                                                               #TopPutNextAssign
  ok -p $a eq <<END;                                                            #TopPutNextAssign  #TopPutPrevAssign
<a>
  <first/>
  <next/>
  <last/>
</a>
END
  my $l = -l $a;                                                                #TopPutPrevAssign
  $l -= qq(prev);                                                               #TopPutPrevAssign
  ok -p $a eq <<END;                                                            #TopPutPrevAssign
<a>
  <first/>
  <next/>
  <prev/>
  <last/>
</a>
END
 }

if (1) {
  my $a = Data::Edit::Xml::new("<a/>");                                         #TopNew
  my $b = $a ** q(b);                                                           #TopNew
  ok -s $b eq "<b/>";                                                           #TopNew
 }

if (1)                                                                          # NextOn
 {my $a = Data::Edit::Xml::new("<a><b><c id='1'/><d id='2'/><c id='3'/><d id='4'/><e id='5'/></b></a>");
  ok -p $a eq <<END;                                                            #TnextOn #TprevOn
<a>
  <b>
    <c id="1"/>
    <d id="2"/>
    <c id="3"/>
    <d id="4"/>
    <e id="5"/>
  </b>
</a>
END
  my $c = $a->firstContextOf(qw(c));
  my $e = $a->lastContextOf(qw(e));
  ok $c->id == 1;                                                               #TnextOn #TprevOn
  ok $e->id == 5;                                                               #TnextOn #TprevOn
  ok $c->nextOn(qw(d))  ->id == 2;                                              #TnextOn
  ok $c->nextOn(qw(c d))->id == 4;                                              #TnextOn
  ok $e->nextOn(qw(c d))     == $e;                                             #TnextOn
  ok $e->prevOn(qw(d))  ->id == 4;                                              #TprevOn
  ok $e->prevOn(qw(c d))     == $c;                                             #TprevOn

  my $x = $a >= [qw(b c 1)];
  my $w = $x->prev;
  my $y = $x->next;
  ok -s $w eq '<d id="2"/>';
  ok -s $y eq '<d id="4"/>';

  is_deeply {$a->first->present}, {c=>2, d=>2, e=>1};                           #Tpresent
 }

if (1)                                                                          # Put as text
 {my $x = Data::Edit::Xml::new(<<END);
<a id="aa"><b id="bb"><c id="cc"/></b></a>
END

  ok -p $x eq <<END;                                                            #TputFirstAsText
<a id="aa">
  <b id="bb">
    <c id="cc"/>
  </b>
</a>
END

  $x->go(qw(b c))->putFirstAsText("<d id=\"dd\">DDDD</d>");                     #TputFirstAsText
  ok -p $x eq <<END;                                                            #TputFirstAsText #TputLastAsText
<a id="aa">
  <b id="bb">
    <c id="cc"><d id="dd">DDDD</d></c>
  </b>
</a>
END

  $x->go(qw(b c))->putLastAsText("<e id=\"ee\">EEEE</e>");                      #TputLastAsText
  ok -p $x eq <<END;                                                            #TputLastAsText #TputNextAsText
<a id="aa">
  <b id="bb">
    <c id="cc"><d id="dd">DDDD</d><e id="ee">EEEE</e></c>
  </b>
</a>
END

  $x->go(qw(b c))->putNextAsText("<n id=\"nn\">NNNN</n>");                      #TputNextAsText
  ok -p $x eq <<END;                                                            #TputNextAsText  #TputPrevAsText
<a id="aa">
  <b id="bb">
    <c id="cc"><d id="dd">DDDD</d><e id="ee">EEEE</e></c>
<n id="nn">NNNN</n>
  </b>
</a>
END

  $x->go(qw(b c))->putPrevAsText("<p id=\"pp\">PPPP</p>");                      #TputPrevAsText

  ok -p $x eq <<END;                                                            #TputPrevAsText
<a id="aa">
  <b id="bb"><p id="pp">PPPP</p>
    <c id="cc"><d id="dd">DDDD</d><e id="ee">EEEE</e></c>
<n id="nn">NNNN</n>
  </b>
</a>
END
}

if (1) {                                                                        #TfirstText #TlastText #TnextText #TprevText
  my $a = Data::Edit::Xml::new("<a>AA<b/>BB<c/>CC<d/><e/><f/>DD<g/>HH</a>");
  ok -p $a eq <<END;
<a>AA
  <b/>
BB
  <c/>
CC
  <d/>
  <e/>
  <f/>
DD
  <g/>
HH
</a>
END
  ok  $a->firstText_a__text eq q(AA);
  ok !$a->go_c__firstText_c_a;
  ok !$a->go_c__firstText_c_b;
  ok  $a->lastText__text eq q(HH);
  ok  $a->lastText_a__text eq q(HH);
  ok !$a->go_c__lastText;
  ok  $a->go_c__nextText_c_a__text eq q(CC);
  ok !$a->go_e__nextText;
  ok  $a->go_c__prevText_c__text eq q(BB);
  ok !$a->go_e__prevText;
 }

if (1)
 {my $a = Data::Edit::Xml::new(<<END);
<a>
  <b>
     <c>
       <d>ddd</d>
    </c>
  </b>
</a>
END

  $a->first->replaceContentWithMovedContent($a->go(qw(b c d)));
  ok -p $a eq <<END;
<a>
  <b>ddd</b>
</a>
END
 }

if (1) {
  my $a = Data::Edit::Xml::new(<<END);                                          #TreplaceContentWithMovedContent
<a>
  <b>
     <b1/>
     <b2/>
  </b>
  <c>
     <c1/>
     <c2/>
  </c>
  <d>
     <d1/>
     <d2/>
  </d>
</a>
END

  my ($b, $c, $d) = $a->contents;                                               #TreplaceContentWithMovedContent
  $d->replaceContentWithMovedContent($c, $b);                                   #TreplaceContentWithMovedContent
  ok -p $a eq <<END;                                                            #TreplaceContentWithMovedContent
<a>
  <b/>
  <c/>
  <d>
    <c1/>
    <c2/>
    <b1/>
    <b2/>
  </d>
</a>
END
 }

if (1) {
  my $a = Data::Edit::Xml::new(<<END);                                          #TreplaceContentWithMovedContent
<a>
  <d>
     <b>
       <b1/>
       <b2/>
    </b>
    <c>
       <c1/>
       <c2/>
    </c>
  </d>
</a>
END

  my ($d)     = $a->contents;                                                   #TreplaceContentWithMovedContent
  my ($b, $c) = $d->contents;                                                   #TreplaceContentWithMovedContent
  $d->replaceContentWithMovedContent($c, $b);                                   #TreplaceContentWithMovedContent
  ok -p $a eq <<END;                                                            #TreplaceContentWithMovedContent
<a>
  <d>
    <c1/>
    <c2/>
    <b1/>
    <b2/>
  </d>
</a>
END
 }

if (1) {
  my $a = Data::Edit::Xml::new(<<END);                                          #Tfirst #Tlast #Tnext #Tprev #TfirstBy #TlastBy #Tindex #Tposition #TfirstSibling #TlastSibling
<a         id="11">
  <b       id="12">
     <c    id="13"/>
     <d    id="14"/>
     <b    id="15">
        <c id="16"/>
        <d id="17"/>
        <e id="18"/>
        <f id="19"/>
        <g id="20"/>
     </b>
     <f    id="21"/>
     <g    id="22"/>
  </b>
  <b       id="23">
     <c    id="24"/>
     <d    id="25"/>
     <b    id="26">
        <c id="27"/>
        <d id="28"/>
        <e id="29"/>
        <f id="30"/>
        <g id="31"/>
     </b>
     <f    id="32"/>
     <g    id="33"/>
  </b>
</a>
END

  ok $a->go(qw(b 1))->id == 23;                                                 #Tindex
  ok $a->go(qw(b 1))->index == 1;                                               #Tindex

  ok $a->go(qw(b 1 b))->id == 26;                                               #Tposition
  ok $a->go(qw(b 1 b))->position == 2;                                          #Tposition

  ok  $a->go(q(b))->first->id == 13;                                            #Tfirst
  ok  $a->go(q(b))->first(qw(c b a));                                           #Tfirst
  ok !$a->go(q(b))->first(qw(b a));                                             #Tfirst

  ok  $a->go(qw(b b))->firstSibling->id == 13;                                   #TfirstSibling
  ok  $a->go(qw(b b))->lastSibling ->id == 22;                                   #TlastSibling

  ok  $a->go(q(b))->last ->id == 22;                                            #Tlast
  ok  $a->go(q(b))->last(qw(g b a));                                            #Tlast
  ok !$a->go(q(b))->last(qw(b a));                                              #Tlast
  ok !$a->go(q(b))->last(qw(b a));                                              #Tlast

  ok  $a->go(qw(b b e))->next ->id == 19;                                       #Tnext
  ok  $a->go(qw(b b e))->next(qw(f b b a));                                     #Tnext
  ok !$a->go(qw(b b e))->next(qw(f b a));                                       #Tnext

  ok  $a->go(qw(b b e))->prev ->id == 17;                                       #Tprev
  ok  $a->go(qw(b b e))->prev(qw(d b b a));                                     #Tprev
  ok !$a->go(qw(b b e))->prev(qw(d b a));                                       #Tprev

  if (1)
   {my %f = $a->firstBy;                                                        #TfirstBy
    ok $f{b}->id == 12;                                                         #TfirstBy
   }

  if (1)
   {my %f = $a->firstDown;                                                      #TfirstDown
    ok $f{b}->id == 15;                                                         #TfirstDown
   }

  if (1)
   {my %l = $a->lastBy;                                                         #TlastBy
    ok $l{b}->id == 23;                                                         #TlastBy
   }

  if (1)
   {my %l = $a->lastDown;                                                       #TlastDown
    ok $l{b}->id == 26;                                                         #TlastDown
   }
 }

if (1) {
  my $x = Data::Edit::Xml::new(<<END);                                          #TfirstContextOf #TlastContextOf
<a        id="a1">
  <b1     id="b1">
     <c   id="c1">
       <d id="d1">DD11</d>
       <e id="e1">EE11</e>
    </c>
  </b1>
  <b2     id="b2">
     <c   id="c2">
       <d id="d2">DD22</d>
       <e id="e2">EE22</e>
    </c>
  </b2>
  <b3     id="b3">
     <c   id="c3">
       <d id="d3">DD33</d>
       <e id="e3">EE33</e>
    </c>
  </b3>
</a>
END

  ok $x->firstContextOf(qw(d c))         ->id     eq qq(d1);                    #TfirstContextOf
  ok $x->firstContextOf(qw(e c b2))      ->id     eq qq(e2);                    #TfirstContextOf
  ok $x->firstContextOf(qw(CDATA d c b2))->string eq qq(DD22);                  #TfirstContextOf

  ok $x-> lastContextOf(qw(d c))         ->id     eq qq(d3);                    #TlastContextOf
  ok $x-> lastContextOf(qw(e c b2     )) ->id     eq qq(e2);                    #TlastContextOf
  ok $x-> lastContextOf(qw(CDATA e c b2))->string eq qq(EE22);                  #TlastContextOf
 }

if (1) {                                                                        # New
  my $x = Data::Edit::Xml::newTree("a", id=>1, class=>"aa");                    #TnewTree #TnewTag
  ok -s $x eq '<a class="aa" id="1"/>';                                         #TnewTree
  ok $x->attrCount == 2;
  $x->putLast($x->newTag("b", id=>2, class=>"bb"));                             #TnewTag
  ok $x->go(q(b))->attrCount == 2;
  ok -p $x eq <<END;                                                            #TnewText #TnewTag
<a class="aa" id="1">
  <b class="bb" id="2"/>
</a>
END
  $x->putLast($x->newText("t"));                                                #TnewText

  ok -p $x eq <<END;                                                            #TnewText
<a class="aa" id="1">
  <b class="bb" id="2"/>
t
</a>
END
 }

if (1) {                                                                        # Well known attributes
  my $a = Data::Edit::Xml::newTree("a", id=>1, class=>2, href=>3, outputclass=>4); #TcountOutputClasses

  ok $a->id          == 1;
  ok $a->class       == 2;
  ok $a->href        == 3;
  ok $a->outputclass == 4;

  is_deeply { 4 => 1 }, $a->countOutputClasses;                                 #TcountOutputClasses
 }

if (1) {                                                                          # Spares
  my $c = Data::Edit::Xml::newTree("c", id=>33);                                #TwrapUp
  my ($b, $a) = $c->wrapUp(qw(b a));                                            #TwrapUp
  ok -p $a eq <<'END';                                                          #TwrapUp
<a>
  <b>
    <c id="33"/>
  </b>
</a>
END
 }

if (1) {
  my $a = Data::Edit::Xml::newTree("a", id=>33);                                #TwrapDown
  my ($b, $c) = $a->wrapDown(qw(b c));                                          #TwrapDown
  ok $a->tag eq qq(a);
  ok $b->tag eq qq(b);
  ok $c->tag eq qq(c);
  ok $a->id == 33;
  ok -p $a eq <<END;                                                            #TwrapDown
<a id="33">
  <b>
    <c/>
  </b>
</a>
END
 }

if (1) {                                                                        # Matches text
  my $x = Data::Edit::Xml::new(<<END);                                          #TmatchesText
<a>
  <b>
    <c>CDECD</c>
  </b>
</a>
END
  my $c = $x->go(qw(b c))->first;                                               #TmatchesText
  ok !$c->matchesText(qr(\AD));                                                 #TmatchesText
  ok  $c->matchesText(qr(\AC), qw(c b a));                                      #TmatchesText
  ok !$c->matchesText(qr(\AD), qw(c b a));                                      #TmatchesText
  is_deeply [qw(E)], [$c->matchesText(qr(CD(.)CD))];                            #TmatchesText
 }

if (1)                                                                          # Create
 {my $x = Data::Edit::Xml::new(<<END);
<a>
  <b>
    <C/>
  </b>
  <c>
    <D/>
     E
    </c>
  <d>
    <F/>
    <G/>
    <H/>
  </d>
  <e/>
</a>
END

  $x->go(qw(d G))->replaceWithBlank;
  ok $x->prettyStringCDATA eq <<END;                                            #TbitsNodeTextBlank
<a>
    <b>
        <C/>
    </b>
    <c>
        <D/>
<CDATA>
     E
    </CDATA>
    </c>
    <d>
        <F/>
<CDATA> </CDATA>
        <H/>
    </d>
    <e/>
</a>
END

  ok '100' eq -B $x;                                                            #TbitsNodeTextBlank
  ok '100' eq -B $x->go(q(b));                                                  #TbitsNodeTextBlank
  ok '110' eq -B $x->go(q(c));                                                  #TbitsNodeTextBlank
  ok '111' eq -B $x->go(q(d));                                                  #TbitsNodeTextBlank
  ok !-B $x->go(qw(e));                                                         #TbitsNodeTextBlank
 }

if (1)                                                                          # Default error file
 {my $a = eval {Data::Edit::Xml::new("</a>")};
  my ($m, $f) = split /\n/, $@;
  ok $m =~ m(Xml parse error, see file:)s;
  ok -e $f;
  unlink $f;
  $f =~ s'out.data'';
  rmdir $f;
 }

if (1)
 {my $a = Data::Edit::Xml::new(<<END);                                          #TisAllBlankText
<a>
  <b>
    <c>
      <z/>
    </c>
  </b>
  <d/>
</a>
END
  $a->by(sub{$_->replaceWithBlank(qw(z))});                                     #TisAllBlankText
  my ($b, $c, $d) = $a->firstBy(qw(b c d));                                     #TisAllBlankText
  ok !$b->isEmpty;
  ok !$b->isAllBlankText;
  ok !$c->isEmpty;
  ok  $c->isAllBlankText;                                                       #TisAllBlankText
  ok  $c->isAllBlankText(qw(c b a));                                            #TisAllBlankText
  ok !$c->isAllBlankText(qw(c a));                                              #TisAllBlankText
  ok  $d->isEmpty;
  ok  $d->isEmpty(qw(d a));
  ok !$d->isEmpty(qw(d b));
  ok  $d->isAllBlankText;
  ok  $d->isAllBlankText(qw(d a));
  ok !$d->isAllBlankText(qw(d b));
  ok  $a->first(qw(b a)) == $b;
  ok !$a->first(qw(a));
  ok  $a->last(qw(d a))  == $d;
  ok !$a->last(qw(a));
  ok  $b->next(qw(d a))  == $d;
  ok !$b->next(qw(a));
  ok  $d->prev(qw(b a))  == $b;
  ok !$d->prev(qw(a));

  ok  $b->isFirst;
  ok  $b->isFirst(qw(b a));
  ok !$b->isFirst(qw(a));

  ok  $d->isLast;
  ok  $d->isLast(qw(d a));
  ok !$d->isLast(qw(a));

  $d->props = q(Props);
  $d->otherprops = q(OtherProps);
  $d->style = q(Style);
  ok $d->printAttributes eq
    q( otherprops="OtherProps" props="Props" style="Style");
  my $D = $d->countAttrNames;
  is_deeply $D, { otherprops => 1, props => 1, style => 1 };

  ok  $c->at(q(c), undef, q(a));
  ok !$c->cut(qw(b a));
}

if (1)                                                                          # Unwrap
 {my $x = Data::Edit::Xml::new("<a><b><c/></b></a>");
  $x->go(qw(b c))->unwrap;
  $x->checkParentage;
  ok -s $x eq "<a><b/></a>";
  $x->go(q(b))->unwrap;
  ok -s $x eq "<a/>";
  ok !$x->unwrap;                                                               # 2019.09.27 23:35:45
#  eval {$x->unwrap};
#  ok $@ =~ /\ACannot unwrap the outer most node/gs;
 }

if (1)
 {my $a = Data::Edit::Xml::new("<a> </a>");                                     #Tequals #Tclone
  my $A = $a->clone;                                                            #Tequals #Tclone
  ok -s $A eq q(<a/>);                                                          #Tequals #Tclone
  ok $a->equals($A);                                                            #Tequals #Tclone
 }

if (1) {                                                                        #Trenew #Troot
  my $a = Data::Edit::Xml::new("<a/>", inputFile=>q(aaa.xml));
  $a->putFirstAsText(qq(<b/>));
  ok !$a->go(q(b));
  my $A = $a->renew;
  ok -t $A->go(q(b)) eq q(b);
  ok $A->root->inputFile eq $a->root->inputFile;
 }

if (1)                                                                          #TnumberTree #TfindByNumber #Tnumber
 {my $a = Data::Edit::Xml::new(<<END);
<a><b><c/></b><d><e/></d></a>
END

  $a->numberTree;
  ok -z $a eq <<END;
<a id="1">
  <b id="2">
    <c id="3"/>
  </b>
  <d id="4">
    <e id="5"/>
  </d>
</a>
END

  ok -t $a->findByNumber_4 eq q(d);
  ok    $a->findByNumber_3__up__number == 2;
 }

if (1)                                                                          #Tup #Tupn #TupUntil #TupWhile
 {my $a = Data::Edit::Xml::new(<<END);
<a><b><c><b><b><b><b><c/></b></b></b></b></c></b></a>
END

  $a->numberTree;
  ok -z $a eq <<END;
<a id="1">
  <b id="2">
    <c id="3">
      <b id="4">
        <b id="5">
          <b id="6">
            <b id="7">
              <c id="8"/>
            </b>
          </b>
        </b>
      </b>
    </c>
  </b>
</a>
END

  my $c = $a->findByNumber(8);
  ok -t $c eq q(c);
  ok  $c->up_b__number == 7;
  ok  $c->upn_2__number == 6;
  ok  $c->upWhile_b__number == 4;
  ok  $c->upWhile_a_b__number == 4;
  ok  $c->upWhile_b_c__number == 2;

  ok  $c->upUntil__number == 8;
  ok  $c->upUntil_b_c__number == 4;
 }

if (1) {
  ok Data::Edit::Xml::cdata eq q(CDATA);                                        #Tcdata
  ok Data::Edit::Xml::replaceSpecialChars(q(<">)) eq q(&lt;&quot;&gt;);         #TreplaceSpecialChars
  ok Data::Edit::Xml::undoSpecialChars(q(&lt;&quot;&gt;)) eq q(<">);            #TundoSpecialChars
 }

if (1) {                                                                        # Break in and out
  my $A = Data::Edit::Xml::new("<a><b><d/><c/><c/><e/><c/><c/><d/></b></a>");   #TbreakOut
  ok -p $A eq <<END;
<a>
  <b>
    <d/>
    <c/>
    <c/>
    <e/>
    <c/>
    <c/>
    <d/>
  </b>
</a>
END

  if (1)
   {my $a = $A->clone;
    $a->go(q(b))->breakOut($a, qw(d e));                                        #TbreakOut
    ok -p $a eq <<END;                                                          #TbreakOut #TbreakIn
<a>
  <d/>
  <b>
    <c/>
    <c/>
  </b>
  <e/>
  <b>
    <c/>
    <c/>
  </b>
  <d/>
</a>
END

    $a->go(qw(b 1))->breakIn;                                                   #TbreakIn
    ok -p $a eq <<END;                                                          #TbreakIn
<a>
  <b>
    <d/>
    <c/>
    <c/>
    <e/>
    <c/>
    <c/>
    <d/>
  </b>
</a>
END

    $a->go(q(b))->breakOut($a, qw(d e));                                        # Break backwards

    ok -p $a eq <<END;                                                          #TbreakInBackwards
<a>
  <d/>
  <b>
    <c/>
    <c/>
  </b>
  <e/>
  <b>
    <c/>
    <c/>
  </b>
  <d/>
</a>
END

    $a->go(qw(b 1))->breakInBackwards;                                          #TbreakInBackwards
    ok -p $a eq <<END;                                                          #TbreakInBackwards
<a>
  <b>
    <d/>
    <c/>
    <c/>
    <e/>
    <c/>
    <c/>
  </b>
  <d/>
</a>
END

    my $d = $a->go(q(d))->cut;
    eval {$d->putLast($d)};
    ok $@ =~ m/\ARecursive insertion attempted/s;
    $a->go(q(b))->putLast($d);
    ok $A->equals($a);

    $a->go(q(b))->breakOut($a, qw(d e));

    ok -p $a eq <<END;                                                          #TbreakInForwards
<a>
  <d/>
  <b>
    <c/>
    <c/>
  </b>
  <e/>
  <b>
    <c/>
    <c/>
  </b>
  <d/>
</a>
END

    $a->go(q(b))->breakInForwards;                                              #TbreakInForwards
    ok -p $a eq <<END;                                                          #TbreakInForwards
<a>
  <d/>
  <b>
    <c/>
    <c/>
    <e/>
    <c/>
    <c/>
    <d/>
  </b>
</a>
END

    my $D = $a->go(q(d))->cut;
    eval {$D->putFirst($D)};
    ok $@ =~ m/\ARecursive insertion attempted/s;
    $a->go(q(b))->putFirst($D);
    ok $A->equals($a);
   }
 }

if (1)
 {my @tags = qw(a b c d e);
  my $x = Data::Edit::Xml::new(<<END);                                          #Tordered #Tpath #Tdisordered #Tabove #Tbelow #Tbefore #Tafter #TabovePath #TbelowPath
<a       id='a1'>
  <b     id='b1'>
    <c   id='c1'/>
    <c   id='c2'/>
    <d   id='d1'>
      <e id='e1'/>
    </d>
    <c   id='c3'/>
    <c   id='c4'/>
    <d   id='d2'>
      <e id='e2'/>
    </d>
    <c   id='c5'/>
    <c   id='c6'/>
  </b>
</a>
END
  my ($a, $b, $c, $d, $e) = $x->firstDown(@tags);                               #TabovePath #TbelowPath
  my ($A, $B, $C, $D, $E) = $x->lastDown(@tags);

  is_deeply [$b, $d, $e], [$b->abovePath($e)];                                  #TabovePath
  is_deeply [$e, $d, $b], [$e->belowPath($b)];                                  #TbelowPath
  is_deeply [],   [$c->abovePath($d)];                                          #TabovePath
  is_deeply [$c], [$c->belowPath($c)];                                          #TbelowPath

  ok eval ' $'.$_    .'->tag eq "'.$_.'"' for @tags;                            # Tags equal their variable names
  ok eval ' $'.uc($_).'->tag eq "'.$_.'"' for @tags;                            # Tags equal their lowercased uppercase variable names
  ok eval ' $'.$_     .'->ordered($'.uc($_).')->tag eq $'.$_.'->tag'  for @tags;# Lowercase nodes precede uppercase nodes
  ok eval '!$'.uc($_).'->ordered($'.$_    .') or $'.$_.' == $'.uc($_) for @tags;# Uppercase nodes equal lowercase nodes or do not precede them

  ok $A == $a;
  ok $B == $b;
  ok $C == $b->go(qw(c 5));
  ok $D == $b->go(qw(d -1));
  ok $E == $D->go(qw(e));

  is_deeply [$x->go(qw(b d 1 e))->path], [qw(b d 1 e)];                         #Tpath
  $x->by(sub {ok $x->go($_->path) == $_});                                      #Tpath

  ok $a->id eq 'a1';
  ok $b->id eq 'b1';                                                            #Tdisordered #Tabove
  ok $c->id eq 'c1';                                                            #Tdisordered #Tafter
  ok $d->id eq 'd1';                                                            #Tdisordered #Tbelow
  ok $e->id eq "e1";                                                            #Tordered    #Tabove #Tbelow #Tdisordered #Tbefore #Tafter
  ok $E->id eq "e2";                                                            #Tordered    #Tabove #Tbefore

  ok  $b->above($e);                                                            #Tabove
  ok !$E->above($e);                                                            #Tabove
  ok !$d->below($e);                                                            #Tbelow

  ok  $e->disordered($c        )->id eq "c1";                                   #Tdisordered
  ok  $b->disordered($c, $e, $d)->id eq "d1";                                   #Tdisordered
  ok !$c->disordered($e);                                                       #Tdisordered

  ok  $e->ordered($E);                                                          #Tordered
  ok !$E->ordered($e);                                                          #Tordered
  ok  $e->ordered($e);                                                          #Tordered
  ok  $e->ordered;                                                              #Tordered
  ok  $a->ordered($b,$c,$d,$e);
  ok  $A->ordered($B,$D,$E,$C);

  is_deeply[map{$_->tag}$a->firstDown(@tags)],[map{$_->tag}$a-> lastBy(@tags)];
  is_deeply[map{$_->tag}$a-> lastDown(@tags)],[map{$_->tag}$a->firstBy(@tags)];

  ok $e->before($E);                                                            #Tbefore
  ok $e->after($c);                                                             #Tafter

 }

if (1)
 {my $a = Data::Edit::Xml::new(<<END);                                          #Tat
<a>
  <b>
    <c> <d/> </c>
    <c> <e/> </c>
    <c> <f/> </c>
  </b>
</a>
END
  ok  $a->go(qw(b c -1 f))->at(qw(f c b a));                                    #Tat
  ok  $a->go(qw(b c  1 e))->at(undef, qr(c|d), undef, qq(a));                   #Tat

  my $d = $a->go(qw(b c d));

  ok $d->context eq q(d c b a);                                                 #Tat #TatOrBelow
  ok  $d->at(qw(d c b), undef);                                                 #Tat
  ok !$d->at(qw(d c b), undef, undef);                                          #Tat
  ok !$d->at(qw(d e b));                                                        #Tat

  ok  $d->atOrBelow(qw(d c b a));                                               #TatOrBelow
  ok  $d->atOrBelow(qw(  c b a));                                               #TatOrBelow
  ok  $d->atOrBelow(qw(    b a));                                               #TatOrBelow
  ok !$d->atOrBelow(qw(  c   a));                                               #TatOrBelow
 }

if (1)
 {my $a = Data::Edit::Xml::new(qq(<a> </a>));
  ok !$a->bitsNodeTextBlank &&  $a->isEmpty;
 }

if (1)
 {my $a = Data::Edit::Xml::new(qq(<a><b>B</b><c/> </a>));
  ok  $a->bitsNodeTextBlank && !$a->isEmpty;
 }

if (1)                                                                          # Numbered
 {my $a = Data::Edit::Xml::new(<<END);
<a>
  <b>
    <c>
      <e/>
    </c>
    <d>
      <e/>
    </d>
    <c>
      <d>
        <e/>
      </d>
    </c>
    <d>
      <e/>
    </d>
    <c>
      <d>
        <e/>
      </d>
    </c>
  </b>
</a>
END
  $a->numberTree;
  ok -z $a eq <<END;                                                            #TpathString #TfirstInIndex #TlastInIndex #TcommonAncestor #Tdepth #Tto #Tfrom #TfromTo
<a id="1">
  <b id="2">
    <c id="3">
      <e id="4"/>
    </c>
    <d id="5">
      <e id="6"/>
    </d>
    <c id="7">
      <d id="8">
        <e id="9"/>
      </d>
    </c>
    <d id="10">
      <e id="11"/>
    </d>
    <c id="12">
      <d id="13">
        <e id="14"/>
      </d>
    </c>
  </b>
</a>
END

  is_deeply [$a->findByNumber(11)->path], [(qw(b d 1 e))];                      # FindByNumber
  ok $a->findByNumber(9)->pathString eq 'b c 1 d e';                            #TpathString

  ok !$a->above($a);                                                            # Above
  ok  $a->findByNumber(12)->above($a->findByNumber(14));
  ok !$a->findByNumber( 7)->above($a->findByNumber(12));

  ok !$a->below($a);                                                            # Below
  ok  $a->findByNumber( 9)->below($a->findByNumber(7));
  ok !$a->findByNumber( 8)->below($a->findByNumber(10));

  ok  $a->findByNumber(13)->after($a->findByNumber(10));

  if (1)
   {my ($m, $n) = $a->findByNumbers(5, 10);
    ok  $m->before($n);
   }

  ok  $a->findByNumber (5)->firstInIndex;                                       #TfirstInIndex
  ok  $a->findByNumber(10)->lastInIndex;                                        #TlastInIndex

  ok !$a->findByNumber(7) ->firstInIndex;                                       #TfirstInIndex
  ok !$a->findByNumber(7) ->lastInIndex;                                        #TlastInIndex

  if (1)
   {my ($b, $e, @n) = $a->findByNumbers(2, 4, 6, 9);                            #TcommonAncestor
    ok -t $b eq 'b';
    ok -t $e eq 'e';
    ok $e == $e->commonAncestor;                                                #TcommonAncestor
    ok $e == $e->commonAncestor($e);                                            #TcommonAncestor
    ok $b == $e->commonAncestor($b);                                            #TcommonAncestor
    ok $e == $e->commonAncestor($e, $e);
    ok $b == $e->commonAncestor($e, $b);
    ok $b == $e->commonAncestor(@n);                                            #TcommonAncestor
   }

  if (1)
   {my ($d, $c, $D) = $a->findByNumbers(5, 7, 10);                              #TfromTo #Tto #Tfrom
    ok -t $d eq 'd';
    ok -t $c eq 'c';
    ok -t $D eq 'd';
    my @r = $d->fromTo($D);                                                     #TfromTo
    ok @r == 3;                                                                 #TfromTo
    my @R = $d->fromTo($D, qw(c));                                              #TfromTo
    ok @R == 1;                                                                 #TfromTo
    ok $R[0]->number == 7;                                                      #TfromTo
    ok !$D->fromTo($d);                                                         #TfromTo
    ok 1 == $d->fromTo($d);                                                     #TfromTo

    my @f = $d->from;                                                           #Tfrom
    ok @f == 4;                                                                 #Tfrom
    ok $d == $f[0];                                                             #Tfrom
    my @F = $d->from(qw(c));                                                    #Tfrom
    ok @F == 2;                                                                 #Tfrom
    ok $F[1]->number == 12;                                                     #Tfrom

    my @t = $D->to;                                                             #Tto
    ok $D == $t[-1];                                                            #Tfrom
    ok @t == 4;                                                                 #Tto
    my @T = $D->to(qw(c));                                                      #Tto
    ok @T == 2;                                                                 #Tto
    ok $T[1]->number == 7;                                                      #Tto
   }

  ok 0 == $a->depth;                                                            #Tdepth
  ok 4 == $a->findByNumber(14)->depth;                                          #Tdepth
 }

if (1)
 {my $a = Data::Edit::Xml::new(<<END);                                          #TfirstOf #TlastOf
<a><b><c/><d/><d/><e/><d/><d/><c/></b></a>
END
  is_deeply [qw(c d d)], [map {-t $_} $a->go(q(b))->firstOf(qw(c d))];          #TfirstOf
  is_deeply [qw(d d c)], [map {-t $_} $a->go(q(b))->lastOf (qw(c d))];          #TlastOf
 }

if (1)                                                                          # IsOnlyChild
 {my $a = Data::Edit::Xml::new("<a><b><c><d/></c></b></a>");
  ok $a->go(qw(b c d))->isOnlyChild;
  ok $a->go(qw(b c d))->isOnlyChild(qw(d));
  ok $a->go(qw(b c d))->isOnlyChild(qw(d c));
  ok $a->go(qw(b c d))->isOnlyChild(qw(d c b));
 }

if (1)
 {my $a = Data::Edit::Xml::new("<a><b>bb</b><c>cc<d/>ee</c></a>");              #TcontainsSingleText
  ok  $a->go(q(b))->containsSingleText->text eq q(bb);                          #TcontainsSingleText
  ok !$a->go(q(c))->containsSingleText;                                         #TcontainsSingleText
 }

if (1)                                                                          # Cut
 {my $x = Data::Edit::Xml::new("<a><b><c/></b></a>");
  $x->go(qw(b c))->cut;
  $x->checkParentage;
  ok -s $x eq "<a><b/></a>";
  $x->go(q(b))->cut;
  ok -s $x eq "<a/>";
  eval {$x->cut};
  ok !$@;                                                                       # Permit multiple cut outs of the same node
 }

if (1)                                                                          #Tunwrap
 {my $x = Data::Edit::Xml::new(q(<a>A<b/>B</a>));
  my $b = $x->go_b;
  $b->putFirst($x->newText(' c '));
  ok -s $x eq q(<a>A<b> c </b>B</a>);
  $b->unwrap;
  ok -s $x eq q(<a>A c B</a>);
 }

if (1)                                                                          # Errors
 {my $f = temporaryFile;
  my $e = temporaryFile;
  overWriteFile($f, <<END);
<a>
END
  my $x = Data::Edit::Xml::new();
     $x->input      = $f;
     $x->errorsFile = $e;
  eval {$x->parse};
  ok $@ =~ /\AXml parse error, see file:/;
  ok -e $e;
  my $s = readFile($e);
  ok CORE::index($s, $f) > 0;
  ok $s =~ /no element found at line 2, column 0, byte 4/s;
  unlink $e, $f;
 }

if (1)                                                                          # Unwrap/Cut in by
 {my $f = temporaryFile;
  overWriteFile($f, <<END);
<a>
  <b><c/><C/><c/></b>
  <b><c/><c/><C/></b>
  <B><c/><c/><C/></B>
  <b><c/><c/><C/></b>
  <b><C/><C/><c/></b>
  <B><C/><C/><C/></B>
  <B><c/><c/><C/></B>
</a>
END
  my $x = Data::Edit::Xml::new();
     $x->input = $f;
     $x->parse;
  unlink $f;

  my $A = 0;
  $x->checkParser;
  $x->by(sub                                                                    # Add stuff and move things around
   {my ($o) = @_;
    for my $t('', ' ', 'aa', 11)
     {$o->putFirst($x->newTag(q(a)))           if ++$A %  3 == 0;
      $o->putLast ($x->newTag(q(b)))           if ++$A %  5 == 0;
      if ($o->parent)
       {$o->putNext ($x->newTag(q(c)))         if ++$A %  7 == 0;
        $o->putPrev ($x->newTag(q(d)))         if ++$A %  2 == 0;
        $o->putFirstAsText($t)                 if ++$A %  3 == 0;
        $o->putLastAsText ($t)                 if ++$A %  2 == 0;
        $o->putNextAsText ($t)                 if ++$A %  3 == 0;
        $o->putPrevAsText ($t)                 if ++$A %  2 == 0;
       }
      $o->wrapContentWith(qw(ww))              if ++$A %  5 == 0;
      $o->wrapWith(qw(xx))                     if ++$A %  3 == 0;
      $o->wrapUp  (qw(aa bb))                  if ++$A %  5 == 0;
      $o->wrapDown(qw(cc dd))                  if ++$A %  7 == 0;
      if (my $p = $o->parent)
       {if(!$p->above($o))
         {$p->putFirst     ($o->cut) if ++$A % 2 == 0;
          $p->putLast      ($o->cut) if ++$A % 5 == 0;
          $p->replaceWith  ($o->cut) if ++$A % 2 == 0;
          if (my $q = $p->parent)
           {$p->putNext    ($o->cut) if ++$A % 2 == 0;
            $p->putPrev    ($o->cut) if ++$A % 2 == 0;
            $q->putLast    ($o->cut) if ++$A % 3 == 0;
            $q->putNext    ($o->cut) if ++$A % 5 == 0;
            $q->putPrev    ($o->cut) if ++$A % 3 == 0;
            $q->putFirst   ($o->cut) if ++$A % 3 == 0;
            $q->replaceWith($o->cut) if ++$A % 3 == 0;
           }
         }
       }
     }
   });

  is_deeply $x->countTagNames,
   {a     => 1,
    aa    => 22,
    b     => 27,
    B     => 3,
    bb    => 22,
    C     => 10,
    c     => 27,
    cc    => 16,
    CDATA => 168,
    d     => 112,
    dd    => 16,
    ww    => 23,
  };

  ok $x->countTags == 447;
  $x->checkParentage;

  ok  $x->equals($x);                                                           # Equals and clone
  my  $y = $x->clone;
  my  $z = $y->clone;
  ok  $y->equals($x);
  ok  $y->equals($y);
  ok  $y->equals($z);
  ok  $x->equals($z);
  ok  $y->by(sub
   {if ($_->at(q(C)))
     {$_->change(q(D));
     }
   });

  ok !$y->equals($z);

  if (1)                                                                        # Save restore
   {my $f = temporaryFile;
    unlink $f;
    my $y1 = eval {Data::Edit::Xml::restore($f)};
    ok $@ =~ /Cannot restore from a non existent file/gs;

    $y->save($f);                                                               #Tsave #Trestore
    my $Y = Data::Edit::Xml::restore($f);                                       #Tsave #Trestore
    unlink $f;
    ok $Y->equals($y);                                                          #Tsave #Trestore
   }

  my $a = 0;                                                                    # Cut and unwrap
  $x->by(sub
   {my ($o) = @_;
    my $t = $o->tag;
    eval {$_->cut}    if ++$a % 2 && $o != $x;
    eval {$_->unwrap} if ++$a % 2 && $o != $x;
   });

  $x->checkParentage;
  ok -s $x eq "<a/>";
 }

if (1)                                                                          # First of
 {my $x = Data::Edit::Xml::new(<<END);                                          #Tc #Tcontents
<a>
  <b id="b1"><c id="1"/></b>
  <d id="d1"><c id="2"/></d>
  <e id="e1"><c id="3"/></e>
  <b id="b2"><c id="4"/></b>
  <d id="d2"><c id="5"/></d>
  <e id="e2"><c id="6"/></e>
</a>
END

  ok !$x->firstContextOf(qw(c a));
  ok !$x-> lastContextOf(qw(c a));

  ok $x->firstContextOf(qw(c b))->id == 1;
  ok $x->firstContextOf(qw(c d))->id == 2;
  ok $x->firstContextOf(qw(c e))->id == 3;
  ok $x-> lastContextOf(qw(c b))->id == 4;
  ok $x-> lastContextOf(qw(c d))->id == 5;
  ok $x-> lastContextOf(qw(c e))->id == 6;

  is_deeply [map{$_->id} $x->c(q(d))],  [qw(d1 d2)];                             #Tc
  is_deeply [map{$_->id} $x->contents], [qw(b1 d1 e1 b2 d2 e2)];                 #Tcontents
 }

if (1)                                                                          # Special characters
 {my $t =
   '<a id="&quot;&lt;&gt;&quot;&lt;&gt;">&quot;&lt;&gt;&quot;&lt;&gt;</a>';
  my $x = Data::Edit::Xml::new($t);
  ok -s $x eq $t;
 }

if (1)
 {my $s = <<END;                                                                #TprettyString #Tconcatenate #TprettyStringContent #TprettyStringNumbered #TstringContent #TstringQuoted
<a>
  <b>
    <A/>
    <B/>
  </b>
  <c>
    <C/>
    <D/>
  </c>
</a>
END
  my $a = Data::Edit::Xml::new($s);                                             #TprettyString #Tconcatenate
  ok $s eq $a->prettyString;                                                    #TprettyString
  ok $s eq -p $a;                                                               #TprettyString

  ok $a->stringContent eq "<b><A/><B/></b><c><C/><D/></c>";                     #TstringContent
  ok $a->stringQuoted eq q('<a><b><A/><B/></b><c><C/><D/></c></a>');            #TstringQuoted

  ok $a->prettyStringContent eq <<END;                                          #TprettyStringContent
<b>
  <A/>
  <B/>
</b>
<c>
  <C/>
  <D/>
</c>
END

  $a->numberTree;                                                               #TprettyStringNumbered #TfindByNumber #TfindByNumbers #Tancestry
  ok $a->prettyStringNumbered eq <<END;                                         #TprettyStringNumbered #TfindByNumber #TfindByNumbers #Tancestry #TtocNumbers
<a id="1">
  <b id="2">
    <A id="3"/>
    <B id="4"/>
  </b>
  <c id="5">
    <C id="6"/>
    <D id="7"/>
  </c>
</a>
END

  if (1)
   {my $t = $a->tocNumbers(qw(b A B));
    is_deeply {map {$_=>$t->{$_}->tag} keys %$t}, {"1"=>"b", "1 1"=>"A", "1 2"=>"B"}
   }
  if (1)
   {my $t = $a->tocNumbers(qw(b c A B C));
    is_deeply {map {$_=>$t->{$_}->tag} keys %$t}, { "1" => "b", "1 1" => "A", "1 2" => "B", "2" => "c", "2 1" => "C" }
   }
  if (1) {
    my $t = $a->tocNumbers();                                                   #TtocNumbers
    is_deeply {map {$_=>$t->{$_}->tag} keys %$t},                               #TtocNumbers
     {"1"  =>"b",                                                               #TtocNumbers
      "1 1"=>"A",                                                               #TtocNumbers
      "1 2"=>"B",                                                               #TtocNumbers
      "2"  =>"c",                                                               #TtocNumbers
      "2 1"=> "C",                                                              #TtocNumbers
      "2 2"=>"D"                                                                #TtocNumbers
     }                                                                          #TtocNumbers
   }

  ok q(D) eq -t $a->findByNumber(7);                                            #TfindByNumber
  is_deeply [map {-t $_} $a->findByNumbers(1..3)], [qw(a b A)];                 #TfindByNumbers
  is_deeply [map {-t $_} $a->findByNumber(7)->ancestry], [qw(D c a)];           #Tancestry

  $a->go(q(b))->concatenate($a->go(q(c)));                                      #Tconcatenate

  my $t = <<END;                                                                #Tconcatenate
<a>
  <b>
    <A/>
    <B/>
    <C/>
    <D/>
  </b>
</a>
END
  ok $t eq -p $a;                                                               #Tconcatenate
 }

if (1)
 {my $s = <<END;                                                                #TprettyStringContentNumbered
<a>
  <b>
    <c/>
  </b>
</a>
END
  my $a = Data::Edit::Xml::new($s);                                             #TprettyStringContentNumbered
  $a->numberTree;                                                               #TprettyStringContentNumbered
  ok $a->prettyStringContentNumbered eq <<END;                                  #TprettyStringContentNumbered
<b id="2">
  <c id="3"/>
</b>
END

  ok $a->go(qw(b))->prettyStringContentNumbered eq <<END;                                  #TprettyStringContentNumbered
<c id="3"/>
END
 }

if (1)                                                                          # concatenateSiblings
 {my $a = Data::Edit::Xml::new('<a><b><c id="1"/></b><b><c id="2"/></b><b><c id="3"/></b><b><c id="4"/></b></a>');
  ok -p $a eq <<END;                                                            #TconcatenateSiblings
<a>
  <b>
    <c id="1"/>
  </b>
  <b>
    <c id="2"/>
  </b>
  <b>
    <c id="3"/>
  </b>
  <b>
    <c id="4"/>
  </b>
</a>
END

  $a->go(qw(b 3))->concatenateSiblings;                                        #TconcatenateSiblings

  ok -p $a eq <<END;                                                            #TconcatenateSiblings
<a>
  <b>
    <c id="1"/>
    <c id="2"/>
    <c id="3"/>
    <c id="4"/>
  </b>
</a>
END

  $a->concatenateSiblings;
  ok -s $a eq '<a><b><c id="1"/><c id="2"/><c id="3"/><c id="4"/></b></a>';
 }

if (1)
 {my $a = Data::Edit::Xml::new('<a/>');                                         #Tchange
  $a->change(qq(b));                                                            #Tchange
  ok -s $a eq '<b/>';                                                           #Tchange
 }

if (1) {                                                                        #TcountTagNames  #TcountAttrNames #TcountAttrValues #TcountAttrNamesAndValues
  my $x = Data::Edit::Xml::new(<<END);
<a A="A1" B="B1" C="C1">
  <b  B="B1" C="C1">
    <c  C="C1">
    </c>
    <c/>
  </b>
  <b  B="B2">
    <c C="C2"/>
  </b>
</a>
END

  is_deeply $x->countTagNames,
   {a => 1, b => 2, c => 3};

  is_deeply $x->countAttrNames,
   {A => 1, B => 3, C => 4};

  is_deeply $x->countAttrValues,
   {A1 => 1, B1 => 2, B2 => 1, C1 => 3, C2 => 1};

  is_deeply $x->countAttrNamesAndValues,
   {A => {A1 => 1}, B => {B1 => 2, B2 => 1}, C => {C1 => 3, C2 => 1}};
 }

if (1)                                                                          # *NonBlank
 {my $a = Data::Edit::Xml::new
   ("<a>1<A/>2<B/>3<C/>4<D/>5<E/>6<F/>7<G/>8<H/>9</a>");
  map {$_->replaceWithBlank} grep {$_->isText}               $a->contents;
  map {$_->cut}              grep {$_->tag =~ m/\A[BDFH]\Z/} $a->contents;

  ok $a->prettyStringCDATA eq <<'END';                                          #TfirstNonBlank #TnextNonBlank #TfirstIn #TnextIn  #TlastNonBlank #TprevNonBlank #TlastIn #TprevIn
<a><CDATA> </CDATA>
    <A/>
<CDATA>  </CDATA>
    <C/>
<CDATA>  </CDATA>
    <E/>
<CDATA>  </CDATA>
    <G/>
<CDATA>  </CDATA>
</a>
END


  ok $a->firstNonBlank->tag eq qq(A);                                           #TfirstNonBlank
  ok $a->firstNonBlank(qw(A a));                                                #TfirstNonBlank

  ok $a->firstNonBlank->nextNonBlank->tag eq qq(C);                             #TnextNonBlank
  ok $a->firstNonBlank->nextNonBlank(qw(C a));                                  #TnextNonBlank

  ok $a->firstIn(qw(b B c C))->tag eq qq(C);                                    #TfirstIn
  ok $a->firstIn(qw(b B c C))->nextIn(qw(A G))->tag eq qq(G);                   #TnextIn

  ok $a->lastNonBlank->tag eq qq(G);                                            #TlastNonBlank
  ok $a->lastNonBlank(qw(G a));                                                 #TlastNonBlank

  ok $a->lastNonBlank->prevNonBlank->tag eq qq(E);                              #TprevNonBlank
  ok $a->lastNonBlank->prevNonBlank(qw(E a));                                   #TprevNonBlank

  ok $a->lastIn(qw(e E f F))->tag eq qq(E);                                     #TlastIn
  ok $a->lastIn(qw(e E f F))->prevIn(qw(A G))->tag eq qq(A);                    #TprevIn
 }

if (1)
 {my $a = Data::Edit::Xml::new(<<END);                                          #TisOnlyChild #TisOnlyChildToDepth #TisEmpty #TisFirstToDepth #TisLastToDepth
<a>
  <b>
    <c>
      <d/>
    </c>
  </b>
  <e>
    <f/>
  </e>
</a>
END

  my ($d, $c, $b, $f, $e) = $a->byList;                                         #TisOnlyChild #TisOnlyChildToDepth #TisEmpty #TisFirstToDepth #TisLastToDepth

  ok  $d->parent->isOnlyChild;
  ok  $d->isOnlyChild;                                                          #TisOnlyChild
  ok  $d->isOnlyChild(qw(d c));
  ok  $d->isOnlyChild(qw(d c b));
  ok  $d->isOnlyChild(qw(d c b a));
  ok !$d->isOnlyChild(qw(b));                                                   #TisOnlyChild
  ok  $d->isOnlyChildToDepth(1, qw(d c b a));                                   #TisOnlyChildToDepth
  ok  $d->isOnlyChildToDepth(2, qw(d c b a));                                   #TisOnlyChildToDepth
  ok !$d->isOnlyChildToDepth(3, qw(d c b a));                                   #TisOnlyChildToDepth
  ok  $d->isEmpty;                                                              #TisEmpty

  ok  $d->isFirstToDepth(4);                                                    #TisFirstToDepth
  ok !$f->isFirstToDepth(2);                                                    #TisFirstToDepth
  ok  $f->isFirstToDepth(1);                                                    #TisFirstToDepth
  ok !$f->isFirstToDepth(3);                                                    #TisFirstToDepth

  ok  $c->isLastToDepth(1);                                                     #TisLastToDepth
  ok !$c->isLastToDepth(3);                                                     #TisLastToDepth
  ok  $d->isLastToDepth(2);                                                     #TisLastToDepth
  ok !$d->isLastToDepth(4);                                                     #TisLastToDepth
 }

#if (1)                                                                         # Operators
# {my $a = Data::Edit::Xml::new("<a id='1'><b id='2'><c id='3'/></b></a>");
#  my $b = $a >= [qw(b)]; ok $b->id == 2;
#  my $c = $b >= [qw(c)]; ok $c->id == 3;
#
#  ok $c <= [qw(c b a)];
#  $a x= sub {ok $_->id == 3 if $_ <= [qw(c b a)]};
#
#  my $A = $a >> '<b id="4"/>';
#  ok -s $A eq '<b id="4"/>';
#  ok -s $a eq '<a id="1"><b id="4"/><b id="2"><c id="3"/></b></a>';
#
#  my $B = $b > '<b id="5"/>';
#  ok -s $B eq  '<b id="5"/>';
#  ok -s $a eq '<a id="1"><b id="4"/><b id="2"><c id="3"/></b><b id="5"/></a>';
#
#  my $C = $b < '<b id="6"/>';
#  ok -s $C eq  '<b id="6"/>';
#  ok -s $a eq '<a id="1"><b id="4"/><b id="6"/><b id="2"><c id="3"/></b><b id="5"/></a>';
#
#  my $D = $b << qq(<d id="7"/>);
#  ok -s $D eq  '<d id="7"/>';
#  ok -s $a eq '<a id="1"><b id="4"/><b id="6"/><b id="2"><c id="3"/><d id="7"/></b><b id="5"/></a>'; Trenew
#  my $x = $a->renew;                                                                                 Trenew
#  ok -s $a eq '<a id="1"><b id="4"/><b id="6"/><b id="2"><c id="3"/><d id="7"/></b><b id="5"/></a>'; Trenew

#  ok 4 == grep{$_ <= [qw(b a)] } @$x;

#  ok $a % 'id' == 1;
#  ok $b % 'id' == 2;
#  ok $c % 'id' == 3;

#  $a += qq(aa);
#  ok -t $a eq 'aa';

#  my $e = $a / qq(ee);
#  ok -s $e eq '<ee><aa id="1"><b id="4"/><b id="6"/><b id="2"><c id="3"/><d id="7"/></b><b id="5"/></aa></ee>';
#
#  my $f = $a * qq(f);
#  ok -s $e eq '<ee><aa id="1"><f><b id="4"/><b id="6"/><b id="2"><c id="3"/><d id="7"/></b><b id="5"/></f></aa></ee>';
#
#  --$c;
#  ok -s $e eq '<ee><aa id="1"><f><b id="4"/><b id="6"/><b id="2"><d id="7"/></b><b id="5"/></f></aa></ee>';
#
#  ++$a;
#  ok -s $e eq '<ee><f><b id="4"/><b id="6"/><b id="2"><d id="7"/></b><b id="5"/></f></ee>';
# }

if (1)                                                                          # Labels
 {my $x = Data::Edit::Xml::new("<a><b><c/></b></a>");
  ok $x->stringReplacingIdsWithLabels eq '<a><b><c/></b></a>';                                             #TaddLabels #TcountLabels #TgetLabels #TstringNode
  my $b = $x->go(q(b));                                                         #TaddLabels #TcountLabels #TgetLabels #TstringNode
  my $c = $b->go(q(c));
  ok $b->countLabels == 0;                                                      #TaddLabels #TcountLabels #TgetLabels
  ok $c->countLabels == 0;
  $b->addLabels(1..2);                                                          #TaddLabels #TcountLabels #TgetLabels #TstringNode
  $b->addLabels(3..4);                                                          #TaddLabels #TcountLabels #TgetLabels #TstringNode
  ok $x->stringReplacingIdsWithLabels eq '<a><b id="1, 2, 3, 4"><c/></b></a>';                             #TaddLabels #TgetLabels #TcopyLabels #TcountLabels #TstringNode

  $b->numberTree;                                                               #TstringNode
  ok $b->stringNode eq "b(2) 0:1 1:2 2:3 3:4";                                  #TstringNode
  ok $b->countLabels == 4;                                                      #TcountLabels
  is_deeply [1..4], [$b->getLabels];                                            #TgetLabels

  $b->copyLabels($c) for 1..2;                                                  #TcopyLabels
  ok $x->stringReplacingIdsWithLabels eq '<a><b id="1, 2, 3, 4"><c id="1, 2, 3, 4"/></b></a>';             #TcopyLabels #TdeleteLabels
  ok $b->countLabels == 4;
  ok $c->countLabels == 4;
  is_deeply [1..4], [$b->getLabels];
  is_deeply [1..4], [$c->getLabels];

  $b->deleteLabels(1,4) for 1..2;                                               #TdeleteLabels
  ok $x->stringReplacingIdsWithLabels eq '<a><b id="2, 3"><c id="1, 2, 3, 4"/></b></a>';                   #TdeleteLabels #TmoveLabels
  ok $b->countLabels == 2;
  ok $c->countLabels == 4;
  is_deeply [2..3], [$b->getLabels];
  is_deeply [1..4], [$c->getLabels];

  $b->moveLabels($c) for 1..2;                                                  #TmoveLabels
  ok $x->stringReplacingIdsWithLabels eq '<a><b><c id="1, 2, 3, 4"/></b></a>';                             #TmoveLabels
  ok $b->countLabels == 0;
  ok $c->countLabels == 4;
  is_deeply [], [$b->getLabels];
  is_deeply [1..4], [$c->getLabels];

  ok -s $x eq '<a><b><c/></b></a>';
  $c->id = 11;
  ok -s $x eq '<a><b><c id="11"/></b></a>';
  ok $x->stringReplacingIdsWithLabels eq '<a><b><c id="1, 2, 3, 4"/></b></a>';
  ok -p $x eq <<END;                                                            #TwrapWith
<a>
  <b>
    <c id="11"/>
  </b>
</a>
END

  $x->go(qw(b c))->wrapWith(qw(C));                                             #TwrapWith
  ok -p $x eq <<END;                                                            #TwrapWith
<a>
  <b>
    <C>
      <c id="11"/>
    </C>
  </b>
</a>
END

  $c->wrapContentWith(qw(D id 2));                                              # WrapContentWIth
  ok -s $x eq '<a><b><C><c id="11"><D id="2"/></c></C></b></a>';
  $c->wrapContentWith(qw(E id 3));
  ok -s $x eq '<a><b><C><c id="11"><E id="3"><D id="2"/></E></c></C></b></a>';

  ok $x->stringReplacingIdsWithLabels eq '<a><b><C><c id="1, 2, 3, 4"><E><D/></E></c></C></b></a>';

  $c->wrapUp(qw(A B));                                                          # WrapUp
  ok -s $x eq '<a><b><C><B><A><c id="11"><E id="3"><D id="2"/></E></c></A></B></C></b></a>';
  $c->wrapDown(qw(G F));                                                        # WrapDown
  ok -s $x eq '<a><b><C><B><A><c id="11"><G><F><E id="3"><D id="2"/></E></F></G></c></A></B></C></b></a>';
}

if (1)
 {my $x = Data::Edit::Xml::new("<a><b><c/></b></a>");
  my $b = $x->go(q(b));
  my $c = $x->go(qw(b c));

  ok $x->stringReplacingIdsWithLabels eq '<a><b><c/></b></a>';                                             #TstringReplacingIdsWithLabels
  $b->addLabels(1..4);                                                          #TstringReplacingIdsWithLabels
  $c->addLabels(5..8);                                                          #TstringReplacingIdsWithLabels

  ok $x->stringReplacingIdsWithLabels eq '<a><b id="1, 2, 3, 4"><c id="5, 6, 7, 8"/></b></a>';             #TstringReplacingIdsWithLabels
  my $s = $x->stringReplacingIdsWithLabels;                                     #TstringReplacingIdsWithLabels
  ok $s eq '<a><b id="1, 2, 3, 4"><c id="5, 6, 7, 8"/></b></a>';                #TstringReplacingIdsWithLabels

  $b->deleteLabels;
  $c->deleteLabels;
  ok $x->stringReplacingIdsWithLabels eq '<a><b><c/></b></a>';
 }

if (1)                                                                          # X versions
 {my $x = Data::Edit::Xml::new("<a><b><c/><c/><c/></b></a>");
  ok -p $x eq <<END;                                                            #TwrapContentWith
<a>
  <b>
    <c/>
    <c/>
    <c/>
  </b>
</a>
END
  $x->go(q(b))->wrapContentWith(qw(D id DD));                                   #TwrapContentWith
  ok -p $x eq <<END;                                                            #TwrapContentWith
<a>
  <b>
    <D id="DD">
      <c/>
      <c/>
      <c/>
    </D>
  </b>
</a>
END
 }

if (1)                                                                          # X versions
 {my $a = Data::Edit::Xml::new(<<END);
<a>
  <b id="1"/><c id="2"/><d id="3"/><c id="4"/><d id="5"/>
  <e id="6"/>
  <b id="7"/><c id="8"/><d id="9"/>
  <f id="10"/>
</a>
END
  ok -p $a eq <<END;                                                            #TwrapContentWith
<a>
  <b id="1"/>
  <c id="2"/>
  <d id="3"/>
  <c id="4"/>
  <d id="5"/>
  <e id="6"/>
  <b id="7"/>
  <c id="8"/>
  <d id="9"/>
  <f id="10"/>
</a>
END

  my ($b, $e, $f) = $a->firstBy(qw(b e f));
  ok $b->id ==  1;
  ok $e->id ==  6;
  ok $f->id == 10;

  ok $b->nextOn(qw(c))  ->id == 2;
  ok $b->nextOn(qw(c d))->id == 5;
  ok '1 2 3 4 5' eq join ' ', map {$_->id}    $b->nextOn(qw(b c d));

  ok $e->prevOn(qw(b c d))->id == 1;
  ok '6 5 4 3 2 1' eq join ' ', map {$_->id}  $e->prevOn(qw(b c d));
  ok '6 5 4 3 2'   eq join ' ', map {$_->id}  $e->prevOn(qw(  c d));
 }

if (0)                                                                          # X versions
 {my $a = Data::Edit::Xml::new("<a><b><c/></b></a>");
  eval
   {my $c = $a->go(qw(b c));
    my $d = $a->goX(qw(b c d));
    ok 0;
   };
 }

if (1)
 {my $a = Data::Edit::Xml::new(<<END);
<sli>
  <li>
    <p>Diagnose the problem</p>
    <p>This can be quite difficult</p>
    <p>Sometimes impossible</p>
  </li>
  <li>
  <p><pre>ls -la</pre></p>
  <p><pre>
drwxr-xr-x  2 phil phil   4096 Jun 15  2016 Desktop
drwxr-xr-x  2 phil phil   4096 Nov  9 20:26 Downloads
</pre></p>
  </li>
</sli>
END

  # Transform to Dita step 1

  $a->by(sub
   {my ($o, $p) = @_;
    if ($o->at(qw(pre p li sli)) and $o->isOnlyChild)
     {$o->change($p->isFirst ? qw(cmd) : qw(stepresult));
      $p->unwrap;
     }
    elsif ($o->at(qw(li sli))    and $o->over(qr(\Ap( p)+\Z)))
     {$_->change($_->isFirst ? qw(cmd) : qw(info)) for $o->contents;
     }
   });

  # Transform to Dita step 2

  $a->by(sub
  {my ($o) = @_;
   $o->change(qw(step))          if $o->at(qw(li sli));
   $o->change(qw(steps))         if $o->at(qw(sli));
   $o->id = 's'.($o->position+1) if $o->at(qw(step));
   $o->id = 'i'.($o->index+1)    if $o->at(qw(info));
   $o->wrapWith(qw(screen))      if $o->at(qw(CDATA stepresult));
  });

  # Print the results

  is_deeply [split //,  (-p $a) =~ s/\s+//gsr], [split //, <<END =~ s/\s+//gsr];# Dita
<steps>
  <step id="s1">
    <cmd>Diagnose the problem
    </cmd>
    <info id="i1">This can be quite difficult
    </info>
    <info id="i2">Sometimes impossible
    </info>
  </step>
  <step id="s2">
    <cmd>ls -la
    </cmd>
    <stepresult>
      <screen>
drwxr-xr-x  2 phil phil   4096 Jun 15  2016 Desktop
drwxr-xr-x  2 phil phil   4096 Nov  9 20:26 Downloads
      </screen>
    </stepresult>
  </step>
</steps>
END
}

if (1)                                                                          # Delete in context - methods
 {my $a = Data::Edit::Xml::new q(<a><b><c/></b><d><c/></d></a>);
  ok -s $a -> by(sub {$_ -> cut(qw(c b a))}) eq
    '<a><b/><d><c/></d></a>';
 }

if (1)                                                                          # Delete in context - methods
 {my $a = Data::Edit::Xml::new q(<a><b><c/></b><d><c/></d></a>);
  ok -s $a -> by(sub {$_ -> cut_c_b_a}) eq
    '<a><b/><d><c/></d></a>';
 }

if (1)                                                                          # Delete in context - chaining
 {my $a = Data::Edit::Xml::new("<a><b><c/></b><d><c/></d></a>");
  $a->go_b_c__cut;
  ok -s $a eq
    '<a><b/><d><c/></d></a>';
 }

if (1)                                                                          # Delete in context - operators
 {my $a = Data::Edit::Xml::new("<a><b><c/></b><d><c/></d></a>");
  ok -s ($a x sub {--$_ if $_ <= [qw(c b a)]}) eq
    '<a><b/><d><c/></d></a>';
 }

if (1)                                                                          # Delete in context - operators
 {my $a = Data::Edit::Xml::new("<a><b><c/></b><d><c/></d></a>");
  ok -s ($a x sub{$_->cut_c_b_a}) eq
    '<a><b/><d><c/></d></a>';
 }

if (1)                                                                          # Delete in context - exit chaining
 {my $a = Data::Edit::Xml::new("<a><b><c/></b><d><c/></d></a>");
  ok -s $a->byX(sub {$_->at(qw(c b a))->cut}) eq
    '<a><b/><d><c/></d></a>';
 }

if (1)                                                                          # Delete in context - exit chaining
 {my $a = Data::Edit::Xml::new("<a><b><c/></b></a>");
  $a->byX(sub {die "found: c\n" if $_->at(qw(c b a))});
  ok $@ =~ m(\Afound: c)s
 }

if (1)                                                                          # Delete in context - exit chaining
 {my $a = Data::Edit::Xml::new(<<END);
<a>
<p>• Minimum 1 number</p>
<p>•   No leading, trailing, or embedded spaces</p>
<p>• Not case-sensitive</p>
</a>
END

  $a->by(sub                                                                    # Bulleted list to <ul>
   {if ($_->at(qw(p)))                                                          # <p>
     {if (my $t = $_->containsSingleText)                                       # <p> with single text
       {if ($t->text =~ s(\A\x{2022}\s*) ()s)                                   # Starting with a bullet
         {$_->change(qw(li));                                                   # <p> to <li>
          if (my $p = $_->prev(qw(ul)))                                         # Previous element is ul?
           {$p->putLast($_->cut);                                               # Put in preceding list or create a new list
           }
          else
           {$_->wrapWith(qw(ul))
           }
         }
       }
     }
   });

  ok -p $a eq <<END;
<a>
  <ul>
    <li>Minimum 1 number</li>
    <li>No leading, trailing, or embedded spaces</li>
    <li>Not case-sensitive</li>
  </ul>
</a>
END
 }

if (1)                                                                          # Bulleted list to <ul>
 {my $a = Data::Edit::Xml::new(<<END);
<a>
<p>• Minimum 1 number</p>
<p>•   No leading, trailing, or embedded spaces</p>
<p>• Not case-sensitive</p>
</a>
END

$a->change_ul->by(sub
 {$_->up__change_li if $_->text_p and $_->text =~ s/\A•\s*//s
 });

  ok -p $a eq <<END;                                                            # Results
<ul>
  <li>Minimum 1 number</li>
  <li>No leading, trailing, or embedded spaces</li>
  <li>Not case-sensitive</li>
</ul>
END
 }

if (1)
 {my $x = Data::Edit::Xml::new(<<END);                                          #TcopyAttrs #TmoveAttrs
<x>
  <a a="1" b="2"/>
  <b b="3" c="4"/>
  <c/>
</x>
END
  my ($a, $b, $c) = $x->contents;                                               #TcopyAttrs #TmoveAttrs
  $a->copyAttrs($b, qw(aa bb));                                                 #TcopyAttrs
  ok <<END eq -p $x;                                                            #TcopyAttrs
<x>
  <a a="1" b="2"/>
  <b b="3" c="4"/>
  <c/>
</x>
END
  $a->copyAttrs($b);                                                            #TcopyAttrs
  ok <<END eq -p $x;                                                            #TcopyAttrs
<x>
  <a a="1" b="2"/>
  <b a="1" b="2" c="4"/>
  <c/>
</x>
END
  $a->moveAttrs($c, qw(aa bb));                                                 #TmoveAttrs
  ok <<END eq -p $x;                                                            #TmoveAttrs
<x>
  <a a="1" b="2"/>
  <b a="1" b="2" c="4"/>
  <c/>
</x>
END
  $b->moveAttrs($c);                                                            #TmoveAttrs
  ok <<END eq -p $x;                                                            #TmoveAttrs
<x>
  <a a="1" b="2"/>
  <b/>
  <c a="1" b="2" c="4"/>
</x>
END
 }

if (1)
 {my $x = Data::Edit::Xml::new(<<END);                                          #TcopyNewAttrs #TmoveNewAttrs
<x>
  <a a="1" b="2"/>
  <b b="3" c="4"/>
  <c/>
</x>
END
  my ($a, $b, $c) = $x->contents;                                               #TcopyNewAttrs #TmoveNewAttrs
  $a->copyNewAttrs($b, qw(aa bb));                                              #TcopyNewAttrs
  ok <<END eq -p $x;                                                            #TcopyNewAttrs
<x>
  <a a="1" b="2"/>
  <b b="3" c="4"/>
  <c/>
</x>
END
  $a->copyNewAttrs($b);                                                         #TcopyNewAttrs
  ok <<END eq -p $x;                                                            #TcopyNewAttrs
<x>
  <a a="1" b="2"/>
  <b a="1" b="3" c="4"/>
  <c/>
</x>
END
  $b->moveNewAttrs($c, qw(aa bb));                                              #TmoveNewAttrs
  ok <<END eq -p $x;                                                            #TmoveNewAttrs
<x>
  <a a="1" b="2"/>
  <b a="1" b="3" c="4"/>
  <c/>
</x>
END
  $b->moveNewAttrs($c);                                                         #TmoveNewAttrs
  ok <<END eq -p $x;                                                            #TmoveNewAttrs #Tswap
<x>
  <a a="1" b="2"/>
  <b/>
  <c a="1" b="3" c="4"/>
</x>
END

  $a->swap($c);                                                                 #Tswap
  ok <<END eq -p $x;                                                            #TmoveNewAttrs #Tswap
<x>
  <c a="1" b="3" c="4"/>
  <b/>
  <a a="1" b="2"/>
</x>
END
 }

if (1)
 {my $x = Data::Edit::Xml::new(<<END);                                          #TisFirstText #TisLastText
<x>
  <a>aaa
    <b>bbb</b>
    ccc
    <d>ddd</d>
    eee
  </a>
</x>
END
  my $a = $x->first;                                                            #TisFirstText
  my ($ta, $b, $tc, $d, $te) = $a->contents;                                    #TisFirstText
  ok $ta      ->isFirstText(qw(a x));                                           #TisFirstText
  ok $b->first->isFirstText(qw(b a x));                                         #TisFirstText
  ok $b->prev ->isFirstText(qw(a x));                                           #TisFirstText
  ok $d->last ->isFirstText(qw(d a x));                                         #TisFirstText
  ok $d->next ->isLastText (qw(a x));                                           #TisLastText
  ok $d->last ->isLastText (qw(d a x));                                         #TisLastText
  ok $te      ->isLastText (qw(a x));                                           #TisLastText
 }

if (1)
 {my $x = Data::Edit::Xml::new(<<END);                                          #Tdiff #Tclone
<x>
  <a>aaa
    <b>bbb</b>
    ccc
    <d>ddd</d>
    eee
  </a>
</x>
END

  ok !$x->diff($x);                                                             #Tdiff
  my $y = $x->clone;                                                            #Tdiff #Tclone
  ok !$x->diff($y);                                                             #Tdiff #Tclone
  $y->first->putLast($x->newTag(q(f)));                                         #Tdiff

  ok nws(<<END) eq nws(-p $y);                                                  #Tdiff
<x>
  <a>aaa
    <b>bbb</b>
    ccc
    <d>ddd</d>
    eee
    <f/>
  </a>
</x>
END

  is_deeply [$x->diff($y)],    ["<d>ddd</d> eee <", "/a></x>", "f/></a></x>"];  #Tdiff
  is_deeply [diff(-p $x, $y)], ["<d>ddd</d> eee <", "/a></x>", "f/></a></x>"];  #Tdiff
  is_deeply [$x->diff(-p $y)], ["<d>ddd</d> eee <", "/a></x>", "f/></a></x>"];  #Tdiff

  my $X = writeFile(undef, -p $x);                                              #Tdiff
  my $Y = writeFile(undef, -p $y);                                              #Tdiff
  is_deeply [diff($X, $Y)],    ["<d>ddd</d> eee <", "/a></x>", "f/></a></x>"];  #Tdiff

  unlink $X, $Y;
 }

if (1)
 {my $a = Data::Edit::Xml::new(<<END);                                          #Tdata
<a   id="1">
  <b id="2"/>
  <c id="3"/>
  <d id="4"/>
</a>
END

  my ($b, $c, $d) = $a->contents;                                               #Tdata
  $c->data->{transform} = 1;                                                    #Tdata

  ok <<END eq -p $a;                                                            #Tdata
<a id="1">
  <b id="2"/>
  <c id="3"/>
  <d id="4"/>
</a>
END

  $a x= sub                                                                     #Tdata
   {$_->cut if $_->data->{transform};
   };

  ok <<END eq -p $a;                                                            #Tdata
<a id="1">
  <b id="2"/>
  <d id="4"/>
</a>
END
 }

ok xmlHeader("<a/>") eq <<END;                                                  #TxmlHeader
<?xml version="1.0" encoding="UTF-8"?>
<a/>
END

if (1)
 {my $a = Data::Edit::Xml::new("<a><b/></a>");                                  #Tcrc

  my ($b) = $a->contents;                                                       #Tcrc

  changeReasonCommentSelectionSpecification = {ccc=>1, ddd=>1};                 #Tcrc #TchangeReasonCommentSelectionSpecification
  $b->putFirst(my $c = $b->newTag(q(c)));                                       #Tcrc
  $c->crc($_) for qw(aaa ccc);                                                  #Tcrc

  ok <<END eq -p $a;                                                            #Tcrc
<a>
  <b><!--ccc-->
    <c/>
  </b>
</a>
END
  changeReasonCommentSelectionSpecification = undef;                            #Tcrc #TchangeReasonCommentSelectionSpecification
  $c->putFirst(my $d = $c->newTag(q(d)));                                       #Tcrc
  $d->crc($_) for qw(aaa ccc);                                                  #Tcrc

  ok <<END eq -p $a;                                                            #Tcrc
<a>
  <b><!--ccc-->
    <c>
      <d/>
    </c>
  </b>
</a>
END
 }

if (1)
 {my $a = Data::Edit::Xml::new(<<END);                                          #TrequiredCleanUp
<a>
  <b>
    <c>
      ccc
    </c>
  </b>
</a>
END
  my ($b) = $a->contents;                                                       #TrequiredCleanUp
  $b->requiredCleanUp(q(33));                                                   #TrequiredCleanUp
  ok -p $a eq <<END;                                                            #TrequiredCleanUp
<a>
  <required-cleanup outputclass="33">&lt;b&gt;
  &lt;c&gt;
      ccc
    &lt;/c&gt;
&lt;/b&gt;
</required-cleanup>
</a>
END
 }

if (1)
 {my $a = Data::Edit::Xml::new(<<END);                                          #TreplaceWithRequiredCleanUp
<a>
  <b/>
</a>
END
  my ($b) = $a->contents;                                                       #TreplaceWithRequiredCleanUp
  $b->replaceWithRequiredCleanUp(q(bb));                                        #TreplaceWithRequiredCleanUp
  ok -p $a eq <<END;                                                            #TreplaceWithRequiredCleanUp
<a>
  <required-cleanup>bb</required-cleanup>
</a>
END
 }

if (1)
 {my $a = Data::Edit::Xml::new(<<END);                                          #TunwrapParentsWithSingleChild
<a>
  <b>
    <c>
      <d/>
    </c>
  </b>
  <e/>
</a>
END

  $a->go(qw(b c d))->unwrapParentsWithSingleChild;                              #TunwrapParentsWithSingleChild
  ok -p $a eq <<END;                                                            #TunwrapParentsWithSingleChild
<a>
  <d/>
  <e/>
</a>
END
 }

if (1)
 {my $a = Data::Edit::Xml::new(<<END);                                          #TputFirstRequiredCleanUp
<a>
  <b/>
</a>
END
  $a->putFirstRequiredCleanUp(qq(1111\n));                                      #TputFirstRequiredCleanUp
  ok -p $a eq <<END;                                                            #TputFirstRequiredCleanUp #TputLastRequiredCleanUp
<a>
  <required-cleanup>1111
</required-cleanup>
  <b/>
</a>
END
  $a->putLastRequiredCleanUp(qq(4444\n));                                       #TputLastRequiredCleanUp

  ok -p $a eq <<END;                                                            #TputLastRequiredCleanUp #TputNextRequiredCleanUp
<a>
  <required-cleanup>1111
</required-cleanup>
  <b/>
  <required-cleanup>4444
</required-cleanup>
</a>
END

  $a->go(q(b))->putNextRequiredCleanUp(qq(3333\n));                             #TputNextRequiredCleanUp
  ok -p $a eq <<END;                                                            #TputNextRequiredCleanUp  #TputPrevRequiredCleanUp
<a>
  <required-cleanup>1111
</required-cleanup>
  <b/>
  <required-cleanup>3333
</required-cleanup>
  <required-cleanup>4444
</required-cleanup>
</a>
END

  $a->go(q(b))->putPrevRequiredCleanUp(qq(2222\n));                             #TputPrevRequiredCleanUp
  ok -p $a eq <<END;                                                            #TputPrevRequiredCleanUp
<a>
  <required-cleanup>1111
</required-cleanup>
  <required-cleanup>2222
</required-cleanup>
  <b/>
  <required-cleanup>3333
</required-cleanup>
  <required-cleanup>4444
</required-cleanup>
</a>
END
 }


if (1)
 {my $a = Data::Edit::Xml::new(<<END);                                          #Tinvert
<a>
  <b id="b">
    <c id="c">
      <d/>
      <e/>
    </c>
  </b>
</a>
END

  $a->first->invert;                                                            #Tinvert
  ok -p $a eq <<END;                                                            #Tinvert
<a>
  <c id="c">
    <b id="b">
      <d/>
      <e/>
    </b>
  </c>
</a>
END

  $a->first->invert;                                                            #Tinvert
  ok -p $a eq <<END;                                                            #Tinvert
<a>
  <b id="b">
    <c id="c">
      <d/>
      <e/>
    </c>
  </b>
</a>
END
 }

if (1)
 {my $a = Data::Edit::Xml::new(<<END);                                          #TinvertFirst #TinvertLast
<a>
  <b>
    <c>
      <d/>
      <e/>
    </c>
    <f/>
    <g/>
  </b>
</a>
END

  $a->first->invertFirst;
  ok -p $a eq <<END;                                                            #TinvertFirst #TinvertLast
<a>
  <c>
    <d/>
    <e/>
    <b>
      <f/>
      <g/>
    </b>
  </c>
</a>
END

  $a->first->invertLast;
  ok -p $a eq <<END;                                                            #TinvertLast
<a>
  <b>
    <c>
      <d/>
      <e/>
    </c>
    <f/>
    <g/>
  </b>
</a>
END
 }

if (1)
 {my $a = Data::Edit::Xml::new(<<END);                                          #TmergeDuplicateChildWithParent #ThasSingleChild
<a>
  <b   id="b" b="bb">
    <b id="c" c="cc"/>
  </b>
</a>
END

  my ($c, $b) = $a->byList;                                                     #TmergeDuplicateChildWithParent #ThasSingleChild
  is_deeply [$b->id, $c->id], [qw(b c)];                                        #TmergeDuplicateChildWithParent #ThasSingleChild
  ok $c == $b->hasSingleChild;                                                  #TmergeDuplicateChildWithParent #ThasSingleChild
  $b->mergeDuplicateChildWithParent;                                            #TmergeDuplicateChildWithParent
  ok -p $a eq <<END;                                                            #TmergeDuplicateChildWithParent
<a>
  <b b="bb" c="cc" id="b"/>
</a>
END

  ok $b == $a->hasSingleChild;                                                  #TmergeDuplicateChildWithParent #ThasSingleChild
 }

# Dita tests

if (1)
 {my $a = Data::Edit::Xml::new(<<END);                                          #TditaObviousChanges
<dita>
  <ol>
    <li><para>aaa</para></li>
    <li><para>bbb</para></li>
  </ol>
</dita>
END

  $a->ditaObviousChanges;                                                       #TditaObviousChanges
  ok -p $a eq <<END;                                                            #TditaListToSteps #TditaStepsToList #TditaObviousChanges
<dita>
  <ol>
    <li>
      <p>aaa</p>
    </li>
    <li>
      <p>bbb</p>
    </li>
  </ol>
</dita>
END

  $a->first->ditaListToSteps;                                                   #TditaListToSteps
  ok -p $a eq <<END;                                                            #TditaListToSteps
<dita>
  <steps>
    <step>
      <cmd>aaa</cmd>
    </step>
    <step>
      <cmd>bbb</cmd>
    </step>
  </steps>
</dita>
END

  $a->first->ditaStepsToList;                                                   #TditaStepsToList
  ok -p $a eq <<END;                                                            #TditaStepsToList #TditaListToStepsUnordered
<dita>
  <ol>
    <li>aaa</li>
    <li>bbb</li>
  </ol>
</dita>
END

  $a->first->ditaListToStepsUnordered;                                          #TditaListToStepsUnordered
  ok -p $a eq <<END;                                                            #TditaListToStepsUnordered
<dita>
  <steps-unordered>
    <step>
      <cmd>aaa</cmd>
    </step>
    <step>
      <cmd>bbb</cmd>
    </step>
  </steps-unordered>
</dita>
END

  ok Data::Edit::Xml::new(q(<concept/>))->ditaTopicHeaders eq <<END;            #TditaTopicHeaders
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE concept PUBLIC "-//OASIS//DTD DITA Concept//EN" "concept.dtd" []>
END
 }

if (1)
 {my $a = Data::Edit::Xml::new(<<END);                                          #TditaListToSubSteps
<dita>
  <ol>
    <li>aaa</li>
    <li>bbb</li>
  </ol>
</dita>
END

  $a->first->ditaListToSubSteps;                                                #TditaListToSubSteps
  ok -p $a eq <<END;                                                            #TditaListToSubSteps
<dita>
  <substeps>
    <substep>
      <cmd>aaa</cmd>
    </substep>
    <substep>
      <cmd>bbb</cmd>
    </substep>
  </substeps>
</dita>
END
}

if (1)
 {my $x = Data::Edit::Xml::new(<<END);                                          #ThtmlHeadersToSections #TdivideDocumentIntoSections
<x>
<h1>h1</h1>
  H1
<h2>h2</h2>
  H2
<h3>h3</h3>
  H3
<h3>h3</h3>
  H3
<h2>h2</h2>
  H2
<h4>h4</h4>
  H4
</x>
END

my %file;

$x->htmlHeadersToSections;                                                      #ThtmlHeadersToSections #TdivideDocumentIntoSections

  $x->divideDocumentIntoSections(sub                                            #ThtmlHeadersToSections #TdivideDocumentIntoSections
   {my ($topicref, $section) = @_;                                              #ThtmlHeadersToSections #TdivideDocumentIntoSections
    my $file = keys %file;                                                      #ThtmlHeadersToSections #TdivideDocumentIntoSections
    $topicref->href = $file;                                                    #ThtmlHeadersToSections #TdivideDocumentIntoSections
    $file{$file} = -p $section;                                                 #ThtmlHeadersToSections #TdivideDocumentIntoSections
    $section->cut;                                                              #ThtmlHeadersToSections #TdivideDocumentIntoSections
   });                                                                          #ThtmlHeadersToSections #TdivideDocumentIntoSections

  ok -p $x eq <<END;                                                            #ThtmlHeadersToSections #TdivideDocumentIntoSections
<x>
  <topicref href="0">
    <topicref href="1">
      <topicref href="2"/>
      <topicref href="3"/>
    </topicref>
    <topicref href="4">
      <topicref href="5"/>
    </topicref>
  </topicref>
</x>
END
 }

if (1)
 {my $a = Data::Edit::Xml::new(<<END);                                          #TnumberTreesJustIds
<a>A
  <b id="bb">B
    <c/>
    <d>D
      <e id="ee"/>
        E
      <f/>
        F
    </d>
    G
  </b>
  H
</a>
END
  $a->numberTreesJustIds(q(T));                                                 #TnumberTreesJustIds
  my $A = Data::Edit::Xml::new(<<END);                                          #TnumberTreesJustIds
<a id="T1">A
  <b id="bb">B
    <c id="T2"/>
    <d id="T3">D
      <e id="ee"/>
        E
      <f id="T4"/>
        F
    </d>
    G
  </b>
  H
</a>
END
  ok -p $a eq -p $A;                                                            #TnumberTreesJustIds
 }

if (1)
 {my $a = Data::Edit::Xml::new(<<END);                                          #TindexIds
<a id="A">
  <b id="B">
    <c id="C"/>
    <d id="D">
      <e id="E"/>
      <f id="F"/>
    </d>
  </b>
</a>
END

  my $i = $a->indexIds;                                                         #TindexIds
  ok $i->{C}->tag eq q(c);                                                      #TindexIds
  ok $i->{E}->tag eq q(e);                                                      #TindexIds
 }

if (1)
 {my $a = Data::Edit::Xml::new(<<END);                                          #TcreatePatch #TData::Edit::Xml::Patch::install
<a>Aaaaa
  <b b1="b1" b2="b2">Bbbbb
    <c c1="c1" />Ccccc
    <d d1="d1" >Ddddd
      <e  e1="e1" />
        Eeeee
      <f  f1="f1" />
        Fffff
    </d>
    Ggggg
  </b>
  Hhhhhh
</a>
END

  my $A = Data::Edit::Xml::new(<<END);                                          #TcreatePatch #TData::Edit::Xml::Patch::install
<a>AaaAaaA
  <b b1="b1" b3="B3">BbbBbbB
    <c c1="C1" />Ccccc
    <d d2="D2" >DddDddD
      <e  e3="E3" />
        EeeEeeE
      <f  f1="F1" />
        FffFffF
    </d>
    GggGggG
  </b>
  Hhhhhh
</a>
END

  $a->numberTreesJustIds(q(a));                                                 #TcreatePatch #TData::Edit::Xml::Patch::install
  $A->numberTreesJustIds(q(a));                                                 #TcreatePatch #TData::Edit::Xml::Patch::install

  my $patches = $a->createPatch($A);                                            #TcreatePatch #TData::Edit::Xml::Patch::install
  $patches->install($a);                                                        #TcreatePatch #TData::Edit::Xml::Patch::install

  ok !$a->diff  ($A);                                                           #TcreatePatch #TData::Edit::Xml::Patch::install
  ok  $a->equals($A);                                                           #TcreatePatch #TData::Edit::Xml::Patch::install
 }

if (1)
 {my $a = Data::Edit::Xml::new(<<END);                                          #TupThru
<a>
  <b>
    <c/>
    <d>
      <e/>
      <f/>
    </d>
  </b>
</a>
END

  my ($c, $e, $f, $d, $b) = $a->byList;                                         #TupThru
  ok -t $f                eq q(f);                                              #TupThru
  ok -t $f->upThru        eq q(f);                                              #TupThru
  ok -t $f->upThru(qw(d)) eq q(d);                                              #TupThru
  ok -t eval{$f->upThru(qw(d))->last->prev} eq q(e);                            #TupThru
  ok !  eval{$f->upThru(qw(d b))->next};                                        #TupThru
 }

if (1)
 {my $a = Data::Edit::Xml::newTree(q(a));                                       #TaddFirst
  $a->addFirst_b for 1..2;                                                      #TaddFirst
  ok -p $a eq <<END;                                                            #TaddFirst #TaddLast
<a>
  <b/>
</a>
END
  $a->addLast(qw(e)) for 1..2;                                                  #TaddLast
  ok -p $a eq <<END;                                                            #TaddLast #TaddNext
<a>
  <b/>
  <e/>
</a>
END
  $a->addFirst_b__addNext_c;                                                    #TaddNext
  ok -p $a eq <<END;                                                            #TaddNext #TaddPrev
<a>
  <b/>
  <c/>
  <e/>
</a>
END
  $a->addLast_e__addPrev_d;                                                     #TaddPrev
  ok -p $a eq <<END;                                                            #TaddPrev
<a>
  <b/>
  <c/>
  <d/>
  <e/>
</a>
END
 }

if (1)
 {my $a = Data::Edit::Xml::new(q(<a><b/></a>));                                 #TaddWrapWith
  my $b = $a->first;                                                            #TaddWrapWith
  $b->addWrapWith_c for 1..2;                                                   #TaddWrapWith
  ok -p $a eq <<END;                                                            #TaddWrapWith #TaddSingleChild
<a>
  <c>
    <b/>
  </c>
</a>
END
  $a->addSingleChild_d for 1..2;                                                #TaddSingleChild
  ok -p $a eq <<END;                                                            #TaddSingleChild
<a>
  <d>
    <c>
      <b/>
    </c>
  </d>
</a>
END
 }

if (1)
 {my $a = Data::Edit::Xml::newTree(q(a));                                       #TaddFirstAsText
  $a->addFirstAsText(q(aaaa)) for 1..2;                                         #TaddFirstAsText
  ok -s $a eq q(<a>aaaa</a>);                                                   #TaddFirstAsText #TaddLastAsText
  $a->addLastAsText(q(dddd)) for 1..2;                                          #TaddLastAsText
  ok -s $a eq q(<a>aaaadddd</a>);                                               #TaddLastAsText
 }

if (1)
 {my $a = Data::Edit::Xml::new(q(<a><b/></a>));                                 #TaddNextAsText
  $a->go(q(b))->addNextAsText(q(bbbb)) for 1..2;                                #TaddNextAsText
  ok -p $a eq <<END;                                                            #TaddNextAsText #TaddPrevAsText
<a>
  <b/>
bbbb
</a>
END
  $a->go(q(b))->addPrevAsText(q(aaaa)) for 1..2;                                #TaddPrevAsText
  ok -p $a eq <<END;                                                            #TaddPrevAsText
<a>aaaa
  <b/>
bbbb
</a>
END
 }

if (1)
 {my $a = Data::Edit::Xml::new(<<END);                                          #TditaParagraphToNote
<a>
  <p> Note: see over for details.</p>
</a>
END

  $a->ditaParagraphToNote(1);                                                   #TditaParagraphToNote
  ok -p $a eq <<END;                                                            #TditaParagraphToNote
<a>
  <note>
    <p>See over for details.</p>
  </note>
</a>
END
 }

if (1)
 {my $a = Data::Edit::Xml::new(<<END);                                          #Tpropagate
<a>
  <b b="B">
    <b c="C">
      <c/>
      <b d="D">
        <d/>
        <b e="E">
          <e/>
        </b>
      </b>
    </b>
  </b>
</a>
END

  $a->propagate(q(b));                                                          #Tpropagate
  ok -p $a eq <<END;                                                            #Tpropagate
<a>
  <c b="B" c="C"/>
  <d b="B" c="C" d="D"/>
  <e b="B" c="C" d="D" e="E"/>
</a>
END
 }

if (1)
 {my $a = Data::Edit::Xml::new(q(<a><b/><c/><d/></a>));                         #TwrapSiblingsBefore

  my ($b, $c, $d) = $a->byList;                                                 #TwrapSiblingsBefore

  $c->wrapSiblingsBefore(q(X));                                                 #TwrapSiblingsBefore
  ok -p $a eq <<END;                                                            #TwrapSiblingsBefore
<a>
  <X>
    <b/>
  </X>
  <c/>
  <d/>
</a>
END
 }

if (1)
 {my $a = Data::Edit::Xml::new(q(<a><b/><c/><d/></a>));                         #TwrapSiblingsAfter

  my ($b, $c, $d) = $a->byList;                                                 #TwrapSiblingsAfter

  $c->wrapSiblingsAfter(q(Y));                                                  #TwrapSiblingsAfter
  ok -p $a eq <<END;                                                            #TwrapSiblingsAfter
<a>
  <b/>
  <c/>
  <Y>
    <d/>
  </Y>
</a>
END
 }

if (1)
 {my $a = Data::Edit::Xml::new(q(<a><b/><c/><d/></a>));                         #TwrapSiblingsBetween

  my ($b, $c, $d) = $a->byList;                                                 #TwrapSiblingsBetween

  $b->wrapSiblingsBetween($d, q(Y));                                            #TwrapSiblingsBetween
  ok -p $a eq <<END;                                                            #TwrapSiblingsBetween
<a>
  <b/>
  <Y>
    <c/>
  </Y>
  <d/>
</a>
END
 }

if (1)
 {my $a = Data::Edit::Xml::new(<<END);                                          #TwordStyles
<a>
 <text:list-style style:name="aa">
   <text:list-level-style-bullet text:level="2"/>
 </text:list-style>
</a>
END

  my $styles = $a->wordStyles;                                                  #TwordStyles
  is_deeply $styles, {bulletedList=>{aa=>{2=>1}}};                              #TwordStyles
 }

if (1)
 {my $a = Data::Edit::Xml::new(<<END);                                          #ThtmlTableToDita
 <table>
   <thead>
    <tr>
       <th>Month</th>
       <th>Savings</th>
       <th>Phone</th>
       <th>Comment</th>
    </tr>
   </thead>
   <tbody>
    <tr>
       <td>January</td>
       <td>100</td>
       <td>555-1212</td>
    </tr>
    <tr>
       <td>February</td>
       <td>80</td>
    </tr>
   </tbody>
</table>
END
  $a->htmlTableToDita;                                                          #ThtmlTableToDita
  ok -p $a eq <<END;                                                            #ThtmlTableToDita
<table>
  <tgroup cols="4">
    <colspec colname="c1" colnum="1" colwidth="1*"/>
    <colspec colname="c2" colnum="2" colwidth="1*"/>
    <colspec colname="c3" colnum="3" colwidth="1*"/>
    <colspec colname="c4" colnum="4" colwidth="1*"/>
    <thead>
      <row>
        <entry>Month</entry>
        <entry>Savings</entry>
        <entry>Phone</entry>
        <entry>Comment</entry>
      </row>
    </thead>
    <tbody>
      <row>
        <entry>January</entry>
        <entry>100</entry>
        <entry nameend="c4" namest="c3">555-1212</entry>
      </row>
      <row>
        <entry>February</entry>
        <entry nameend="c4" namest="c2">80</entry>
      </row>
    </tbody>
  </tgroup>
</table>
END
 }

if (1)
 {my $a = Data::Edit::Xml::new(<<END);                                          #TforestNumberTrees
<a>
  <b id="b">
    <c/>
  </b>
  <b id="B">
    <d/>
    <e/>
  </b>
</a>
END

  my $e = $a->go(qw(b -1 e));                                                   #TforestNumberTrees
  $e->forestNumberTrees(1);                                                     #TforestNumberTrees
  ok -p $a eq <<END;                                                            #TforestNumberTrees #TfindByForestNumber
<a id="1_1">
  <b id="1_2">
    <c id="1_3"/>
  </b>
  <b id="1_4">
    <d id="1_5"/>
    <e id="1_6"/>
  </b>
</a>
END
  my $b = $e->findByForestNumber(1, 2);
  is_deeply [$b->getLabels], ["b"];
  my $B = $e->findByForestNumber(1, 4);                                         #TfindByForestNumber
  is_deeply [$B->getLabels], ["B"];                                             #TfindByForestNumber
 }

if (1)
 {my $a = Data::Edit::Xml::new(<<END);                                          #TgoFish
<a>
  <b>
    <c>
      <d/>
    </c>
    <c/>
  </b>
  <b/>
</a>
END
  if (1) {
    my ($good, $fail, $possible) = $a->goFish(qw(b c D));                       #TgoFish
    ok  $fail eq q(D);                                                          #TgoFish
    is_deeply $good,     [qw(b c)];                                             #TgoFish
    is_deeply $possible, [q(d)];                                                #TgoFish
   }

  if (1)
   {my ($good, $fail, $possible) = $a->goFish(qw(b 3));
    ok  $fail eq q(3);
    is_deeply $good,     [q(b)];
    is_deeply $possible, [0..2];
   }

  if (1)
   {my ($good, $fail, $possible) = $a->goFish(qw(b 0 c D));
    ok  $fail eq q(D);
    is_deeply $good,     [qw(b 0 c)];
    is_deeply $possible, [qw(d)];
   }

  if (1)
   {my ($g, $f, $p) = $a->goFish(qw(b c d));
    is_deeply $g, [qw(b c d)];
    ok !$f;
    ok !$p;
   }
 }

if (1)
 {my $a = Data::Edit::Xml::new(<<END);                                          #TisFirst  #TisLast  #TisOnlyChild #ThowFirst #ThowLast #ThowOnlyChild #ThowFarAbove  #ThowFarBelow #ThowFar #Tadjacent #TcommonAdjacentAncestors
<a>
  <b>
    <c>
      <d/>
    </c>
  </b>
  <b>
    <c/>
  </b>
  <e>
    <f/>
  </e>
</a>
END
  my ($d, $c, $b, $C, $B, $f, $e) = $a->byList;                                 #TisFirst  #TisLast  #TisOnlyChild #ThowFirst #ThowLast #ThowOnlyChild #ThowFarAbove  #ThowFarBelow #ThowFar #Tadjacent #TcommonAdjacentAncestors

  is_deeply [$d->commonAdjacentAncestors($C)], [$b, $B];                        #TcommonAdjacentAncestors                               #ThowFar

  ok $d->howFar($d) == 0;                                                       #ThowFar
  ok $d->howFar($a) == 3;                                                       #ThowFar
  ok $b->howFar($B) == 1;                                                       #ThowFar
  ok $d->howFar($f) == 5;                                                       #ThowFar
  ok $d->howFar($C) == 4;                                                       #ThowFar

  ok  $a->isFirst;                                                              #TisFirst
  ok  $a->isLast;                                                               #TisLast
  ok  $a->isOnlyChild;                                                          #TisOnlyChild
  ok !$a->adjacent($B);                                                         #Tadjacent
  ok  $b->adjacent($B);                                                         #Tadjacent

  ok $d->howFirst     == 4;                                                     #ThowFirst
  ok $f->howLast      == 3;                                                     #ThowLast
  ok $d->howOnlyChild == 2;                                                     #ThowOnlyChild

  ok  $a->howFarAbove($d) == 3;                                                 #ThowFarAbove
  ok !$d->howFarAbove($c);                                                      #ThowFarAbove

  ok  $d->howFarBelow($a) == 3;                                                 #ThowFarBelow
  ok !$c->howFarBelow($d);                                                      #ThowFarBelow
 }


if (1)
 {my $a = Data::Edit::Xml::new(<<END);                                          #Tnot
<a>
  <b/>
</a>
END
  ok $a->first->not_a_c;                                                        #Tnot
 }

if (1)
 {my $a = Data::Edit::Xml::new(<<END);                                          #TAUTOLOAD
<a>
  <b>
    <c/>
  </b>
</a>
END
  my ($c, $b) = $a->byList;                                                     #TAUTOLOAD
  ok  $c->at_c_b_a;                                                             #TAUTOLOAD
  ok !$c->at_b;                                                                 #TAUTOLOAD
  ok  -t $c->change_d_c_b eq q(d);                                              #TAUTOLOAD
  ok !   $c->change_d_b;                                                        #TAUTOLOAD
 }

if (1)
 {my $a = Data::Edit::Xml::new(<<END);                                          #TditaMergeLists
<a>
  <li id="1"/>
  <ol/>
  <ol>
    <li id="2"/>
    <li id="3"/>
  </ol>
</a>
END
  $a x= sub{$_->ditaMergeLists};                                                #TditaMergeLists
  ok -p $a eq <<END;                                                            #TditaMergeLists
<a>
  <ol>
    <li id="1"/>
    <li id="2"/>
    <li id="3"/>
  </ol>
</a>
END
 }

if (1)
 {my $a = Data::Edit::Xml::new(q(<a a="1" b="2" c="3" d="4" e="5"/>));          #TcountAttrNamesOnTagExcluding
  ok $a->countAttrNamesOnTagExcluding_a_b == 3;
END
 }


if (1)
 {my $a = Data::Edit::Xml::new(<<END);                                          #Tapn #Tap #Tan #TupWhileFirst #TupUntilFirst #TupWhileLast #TupUntilLast #TupWhileIsOnlyChild #TupUntilIsOnlyChild #TdownWhileFirst #TdownWhileLast #TdownWhileIsOnlyChild #ThasSingleChildToDepth #TparentOf #TchildOf
<a>
  <b>
    <c/>
    <d/>
    <e>
      <j/>
    </e>
    <f/>
  </b>
  <g>
    <h>
      <i>
        <k/>
        <l/>
      </i>
    </h>
  </g>
</a>
END
  my ($c, $d, $j, $e, $f, $b, $k, $l, $i, $h, $g) = $a->byList;                 #Tapn #Tap #Tan #TupWhileFirst #TupUntilFirst #TupWhileLast #TupUntilLast #TupWhileIsOnlyChild #TupUntilIsOnlyChild #TdownWhileFirst #TdownWhileLast #TdownWhileSingleChild #ThasSingleChildToDepth

  ok $h == $g->hasSingleChildToDepth(1);                                        #ThasSingleChildToDepth
  ok $i == $g->hasSingleChildToDepth_2_i_h_g;                                   #ThasSingleChildToDepth
  ok      !$g->hasSingleChildToDepth_2_i_h_G;                                   #ThasSingleChildToDepth
  ok      !$g->hasSingleChildToDepth(0);                                        #ThasSingleChildToDepth
  ok      !$g->hasSingleChildToDepth(3);                                        #ThasSingleChildToDepth
  ok $i == $i->hasSingleChildToDepth(0);                                        #ThasSingleChildToDepth

  ok  $h == $g->downWhileHasSingleChild;                                        #TdownWhileHasSingleChild
  ok  $h == $h->downWhileHasSingleChild;                                        #TdownWhileHasSingleChild
  ok       !$i->downWhileHasSingleChild;                                        #TdownWhileHasSingleChild

  ok  $k == $g->downWhileFirst;                                                 #TdownWhileFirst
  ok  $c == $a->downWhileFirst;                                                 #TdownWhileFirst
  ok  $c == $c->downWhileFirst;                                                 #TdownWhileFirst
  ok       !$d->downWhileFirst;                                                 #TdownWhileFirst
  ok  $l == $a->downWhileLast;                                                  #TdownWhileLast
  ok  $l == $g->downWhileLast;                                                  #TdownWhileLast
  ok       !$d->downWhileLast;                                                  #TdownWhileLast

  ok  $h == $i->upWhileIsOnlyChild;                                             #TupWhileIsOnlyChild
  ok  $j == $j->upWhileIsOnlyChild;                                             #TupWhileIsOnlyChild
  ok !$d->upWhileIsOnlyChild;                                                   #TupWhileIsOnlyChild

  ok  $i == $k->upUntilIsOnlyChild;                                             #TupUntilIsOnlyChild

  is_deeply[$c, $e], [$d->apn_c_d_e_b_a];                                       #Tapn

  ok  $c == $d->ap_d_c_b_a;                                                     #Tap
  ok  $c == $d->ap_d;                                                           #Tap
  ok !$c->ap_c;                                                                 #Tap

  ok  $e == $d->an_d_e_b_a;                                                     #Tan
  ok  $f == $e->an_e;                                                           #Tan
  ok !$f->an_f;                                                                 #Tan

  ok  $h == $i->upWhileFirst;                                                   #TupWhileFirst
  ok  $a == $c->upWhileFirst;                                                   #TupWhileFirst
  ok !$d->upWhileFirst;                                                         #TupWhileFirst

  ok  $b == $d->upUntilFirst;                                                   #TupUntilFirst

  ok  $j == $j->upWhileLast;                                                    #TupWhileLast
  ok  $a == $l->upWhileLast;                                                    #TupWhileLast
  ok !$d->upWhileLast;                                                          #TupWhileLast

  ok  $i == $k->upUntilLast;                                                    #TupWhileLast

  ok $j->childOf($e);                                                           #TchildOf
  ok $e->parentOf($j);                                                          #TparentOf
 }

if (1)
 {my $a = Data::Edit::Xml::new(<<END);                                          #TputFirstCut
<a>
  <b>
    <c/>
    <d/>
  </b>
</a>
END
  my ($c, $d, $b) = $a->byList;                                                 #TputFirstCut

  $c->putFirstCut($d, qw(c b a));                                               #TputFirstCut
  ok -p $a eq <<END;                                                            #TputFirstCut
<a>
  <b>
    <c>
      <d/>
    </c>
  </b>
</a>
END
 }

if (1)
 {my $a = Data::Edit::Xml::new(<<END);                                          #TputLastCut
<a>
  <b>
    <c/>
    <d/>
  </b>
</a>
END
  my ($c, $d, $b) = $a->byList;                                                 #TputLastCut

  $a->putLastCut($d, qw(a));                                                    #TputLastCut

  ok -p $a eq <<END;                                                            #TputLastCut
<a>
  <b>
    <c/>
  </b>
  <d/>
</a>
END
 }

if (1)
 {my $a = Data::Edit::Xml::new(<<END);                                          #TputNextCut
<a>
  <b>
    <c/>
    <d/>
  </b>
</a>
END
  my ($c, $d, $b) = $a->byList;                                                 #TputNextCut

  $d->putNextCut($c, qw(d b a));                                                #TputNextCut
  ok -p $a eq <<END;                                                            #TputNextCut
<a>
  <b>
    <d/>
    <c/>
  </b>
</a>
END
 }

if (1)
 {my $a = Data::Edit::Xml::new(<<END);                                          #TputPrevCut
<a>
  <b>
    <c/>
    <d/>
  </b>
</a>
END
  my ($c, $d, $b) = $a->byList;                                                 #TputPrevCut

  $c->putPrevCut($d, qw(c b a));                                                #TputPrevCut
  ok -p $a eq <<END;                                                            #TputPrevCut
<a>
  <b>
    <d/>
    <c/>
  </b>
</a>
END
 }

if (1)
 {my $a = Data::Edit::Xml::new(<<END);                                          #TopWrapContentWith
<a>
  <b>
    <c/>
    <d/>
  </b>
</a>
END
  my ($c, $d, $b) = $a->byList;                                                 #TopWrapContentWith

  $b *= q(B);                                                                   #TopWrapContentWith
  ok -p $a eq <<END;                                                            #TopWrapContentWith
<a>
  <b>
    <B>
      <c/>
      <d/>
    </B>
  </b>
</a>
END
 }

if (1)
 {my $a = Data::Edit::Xml::new(<<END);                                          #TopUnwrap
<a>
  <b>
    <c/>
    <d/>
  </b>
</a>
END
  my ($c, $d, $b) = $a->byList;                                                 #TopUnwrap

  $b++;                                                                         #TopUnwrap

  ok -p $a eq <<END;                                                            #TopUnwrap
<a>
  <c/>
  <d/>
</a>
END
 }

if (1)
 {my $a = Data::Edit::Xml::new(<<END);                                          #TequalsIgnoringAttributes
<a>
  <b   id="1" outputclass="1" name="b">
    <c id="2" outputclass="2" name="c"/>
  </b>
</a>
END
  my $A = Data::Edit::Xml::new(<<END);                                          #TequalsIgnoringAttributes
<a>
  <b   id="11" outputclass="11" name="b">
    <c id="22" outputclass="22" name="c"/>
  </b>
</a>
END

  ok !$a->equals($A);                                                           #TequalsIgnoringAttributes
  ok !$a->equalsIgnoringAttributes($A, qw(id));                                 #TequalsIgnoringAttributes
  ok  $a->equalsIgnoringAttributes($A, qw(id outputclass));                     #TequalsIgnoringAttributes
 }

if (1)
 {my $a = Data::Edit::Xml::new(<<END);                                          #TstringExtendingIdsWithLabels
<a id="a">
  <b id="b">
    <c id="c"/>
  </b>
  <b id="B">
    <c id="C"/>
  </b>
</a>
END

  my $A =  Data::Edit::Xml::new(<<END);
<a id="aa">
  <b id="bb">
    <c id="cc"/>
  </b>
  <b>
    <c/>
  </b>
</a>
END

  my $N = 0; $a->by(sub{$_->addLabels((-t $_).++$N)});                          #TstringExtendingIdsWithLabels

  ok -p (new $a->stringExtendingIdsWithLabels) eq <<END;                        #TcopyLabelsAndIdsInTree #TstringExtendingIdsWithLabels
<a id="a, a5">
  <b id="b, b2">
    <c id="c, c1"/>
  </b>
  <b id="B, b4">
    <c id="C, c3"/>
  </b>
</a>
END

  ok -p (new $A->stringExtendingIdsWithLabels) eq <<END;                        #TcopyLabelsAndIdsInTree
<a id="aa">
  <b id="bb">
    <c id="cc"/>
  </b>
  <b>
    <c/>
  </b>
</a>
END

  ok $a->copyLabelsAndIdsInTree($A) == 10;                                      #TcopyLabelsAndIdsInTree

  ok -p (new $A->stringExtendingIdsWithLabels) eq <<END;                        #TcopyLabelsAndIdsInTree #TlabelsInTree
<a id="aa, a, a5">
  <b id="bb, b, b2">
    <c id="cc, c, c1"/>
  </b>
  <b id="B, b4">
    <c id="C, c3"/>
  </b>
</a>
END

  is_deeply [sort keys %{$A->labelsInTree}],                                    #TlabelsInTree
    ["B", "C", "a", "a5", "b", "b2", "b4", "c", "c1", "c3"];                    #TlabelsInTree

 }

if (1)                                                                          #TmatchTree
 {my $a = Data::Edit::Xml::new(<<END);
<a>
  <b>
    <c/>
    <d/>
  </b>
  <e>
    <f>
      <g/>
    </f>
  </e>
</a>
END
  my ($c, $d, $b, $g, $f, $e) = $a->byList;

  is_deeply [$b, $c, $d], [$b->matchTree(qw(b c d))];
  is_deeply [$e, $f, $g], [$e->matchTree(qr(\Ae\Z), [qw(f g)])];
  is_deeply [$c],         [$c->matchTree(qw(c))];
  is_deeply [$a, $b, $c, $d, $e, $f, $g],
            [$a->matchTree({a=>1}, [qw(b c d)], [qw(e), [qw(f g)]])];
 }

if (1)                                                                          #Toat #Toft #Tolt
 {my $a = Data::Edit::Xml::new(<<END);
<a>
  <b>
    <c/>
    <d/>
  </b>
  <e>
    <f>
      <g/>
    </f>
  </e>
</a>
END
  my ($c, $d, $b, $g, $f, $e) = $a->byList;

  ok  $b->oat_c_d;
  ok  $a->oat_b_e;
  ok  $g->oat;
  ok !$b->oat_d;

  ok $a->oft_b;
  ok $b->oft_c;
  ok $f->oft_g;
  ok $g->oft;
  ok !$b->oft_d;

  ok $a->olt_e;
  ok $b->olt_d;
  ok $f->olt_g;
  ok $g->olt;
  ok !$b->olt_c;
 }

if (1)                                                                          #ToverAllTags #ToverFirstTags #ToverLastTags
 {my $a = Data::Edit::Xml::new(<<END);
<a>
  <b/>
  <c/>
  <d/>
</a>
END

  ok  $a->overAllTags_b_c_d;
  ok !$a->overAllTags_b_c;
  ok !$a->overAllTags_b_c_d_e;
  ok  $a->oat_b_c_d;
  ok !$a->oat_B_c_d;

  ok  $a->overFirstTags_b_c_d;
  ok  $a->overFirstTags_b_c;
  ok !$a->overFirstTags_b_c_d_e;
  ok  $a->oft_b_c;
  ok !$a->oft_B_c;

  ok  $a->overLastTags_b_c_d;
  ok  $a->overLastTags_c_d;
  ok !$a->overLastTags_b_c_d_e;
  ok  $a->olt_c_d;
  ok !$a->olt_C_d;
 }

if (1)
 {my $a = Data::Edit::Xml::new(<<END);                                          #TfirstTextMatches #TlastTextMatches #TnextTextMatches #TprevTextMatches #TdeleteContent
<a>
  <b>bb<c>cc</c>BB
  </b>
</a>
END
  my ($bb, $cc, $c, $BB, $b) = $a->byList;                                      #TfirstTextMatches #TlastTextMatches

  ok $bb->matchesText(qr(bb));                                                  #TfirstTextMatches
  ok $b->at_b_a &&  $b->firstTextMatches(qr(bb));                               #TfirstTextMatches
  ok                $b->firstTextMatches(qr(bb), qw(b a));                      #TfirstTextMatches
  ok $c->at_c_b &&  $c->firstTextMatches(qr(cc));                               #TfirstTextMatches
  ok $c->at_c_b && !$c->firstTextMatches(qr(bb));                               #TfirstTextMatches

  ok $BB->matchesText(qr(BB));                                                  #TlastTextMatches
  ok $b->at_b_a &&  $b->lastTextMatches(qr(BB));                                #TlastTextMatches
  ok                $b->lastTextMatches(qr(BB), qw(b a));                       #TlastTextMatches
  ok $c->at_c_b &&  $c->lastTextMatches(qr(cc));                                #TlastTextMatches
  ok $c->at_c_b && !$c->lastTextMatches(qr(bb));                                #TlastTextMatches

  ok $cc->matchesText(qr(cc));                                                  #TnextTextMatches #TprevTextMatches
  ok $c->at_c_b &&  $c->prevTextMatches(qr(bb));                                #TprevTextMatches
  ok $c->at_c_b &&  $c->nextTextMatches(qr(BB));                                #TnextTextMatches
  ok $b->at_b   && !$b->prevTextMatches(qr(bb));                                #TprevTextMatches
  ok $b->at_b   && !$b->nextTextMatches(qr(BB));                                #TnextTextMatches

  $b->deleteContent;                                                            #TdeleteContent
  ok -p $a eq <<END;                                                            #TdeleteContent
<a>
  <b/>
</a>
END
 }

if (1)
 {my $a = Data::Edit::Xml::new(<<END);                                          #TprettyString
<a>
  <b>bbb</b>.
  <c>ccc</c>.
</a>
END
 }

if (1)
 {my $a = Data::Edit::Xml::new(<<END);                                          #TchangeAttributeValue
<a aa="abc"/>
END
  $a->changeAttributeValue(q(aa), sub{s(b) (B)});                               #TchangeAttributeValue
  ok -p $a eq <<END;                                                            #TchangeAttributeValue
<a aa="aBc"/>
END
 }

if (1)                                                                          #TprettyStringDitaHeaders #TopString
 {my $a = Data::Edit::Xml::new(<<END);
<concept/>
END

  ok $a->ditaPrettyPrintWithHeaders eq <<END;
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE concept PUBLIC "-//OASIS//DTD DITA Concept//EN" "concept.dtd" []>
<concept/>
END

 }

if (1)                                                                          #Tupn
 {my $a = Data::Edit::Xml::new(<<END);                                          #Tupn
<a><b><c><d><e/></d></c></b></a>
END

  my ($e, $d, $c, $b) = $a->byList;                                             #Tupn

  ok $e = $e->upn_0_e_d_c_b_a;                                                  #Tupn
  ok $d = $e->upn_1_d_c_b_a;                                                    #Tupn
  ok $c = $e->upn_2_c_b_a;                                                      #Tupn
  ok $b = $e->upn_3_b_a;                                                        #Tupn
  ok $a = $e->upn_4_a;                                                          #Tupn
  ok     !$e->upn_5;                                                            #Tupn

  is_deeply [$e, $d, $c, $b, $a], [$e->ancestry];
 }

if (1)                                                                          #Tattributes
 {my $a = Data::Edit::Xml::new(q(<a/>));

  $a->id  = q(id);
  ok $a->id eq q(id);
  ok !defined($a->href);
  ok $a->hrefX eq q();
  ok -s $a eq q(<a id="id"/>);
  $a->href = q(href);
  ok $a->href  eq q(href);
  ok $a->hrefX eq q(href);
  ok -s $a eq q(<a href="href" id="id"/>);
  $a->href = undef;
  ok !defined($a->href);
  ok $a->hrefX eq q();
  ok -s $a eq q(<a id="id"/>);
 }

if (1)                                                                          #TditaMaximumNumberOfEntriesInARow #TditaAddColSpecToTGroup
 {my $a = Data::Edit::Xml::new(<<END);
<table>
  <tgroup>
    <tbody>
      <row><entry/></row>
      <row><entry/><entry/></row>
      <row><entry/><entry/><entry/></row>
      <row><entry/><entry/></row>
      <row/>
    </tbody>
  </tgroup>
</table>
END

  ok 3 == $a->go_tgroup->ditaMaximumNumberOfEntriesInATGroupRow;
  $a->first->ditaAddColSpecToTGroup(3);

  ok -p $a eq <<END
<table>
  <tgroup cols="3">
    <colspec colname="c1" colnum="1" colwidth="1*"/>
    <colspec colname="c2" colnum="2" colwidth="1*"/>
    <colspec colname="c3" colnum="3" colwidth="1*"/>
    <tbody>
      <row>
        <entry/>
      </row>
      <row>
        <entry/>
        <entry/>
      </row>
      <row>
        <entry/>
        <entry/>
        <entry/>
      </row>
      <row>
        <entry/>
        <entry/>
      </row>
      <row/>
    </tbody>
  </tgroup>
</table>
END
 }

if (1)                                                                          #TditaFixTGroupColSpec
 {my $a = Data::Edit::Xml::new(<<END);
<table>
  <tgroup>
    <tbody>
      <row><entry/></row>
      <row><entry/><entry/></row>
      <row><entry/><entry/><entry/></row>
      <row><entry/><entry/></row>
      <row>
        <entry>
          <table>
            <tbody>
              <row><entry/><entry/><entry/><entry/><entry/><entry/><entry/></row>
            </tbody>
          </table>
        </entry>
      </row>
   </tbody>
 </tgroup>
</table>
END

  $a->go_tgroup->ditaFixTGroupColSpec;

  ok -p $a eq <<END
<table>
  <tgroup cols="3">
    <colspec colname="c1" colnum="1" colwidth="1*"/>
    <colspec colname="c2" colnum="2" colwidth="1*"/>
    <colspec colname="c3" colnum="3" colwidth="1*"/>
    <tbody>
      <row>
        <entry/>
      </row>
      <row>
        <entry/>
        <entry/>
      </row>
      <row>
        <entry/>
        <entry/>
        <entry/>
      </row>
      <row>
        <entry/>
        <entry/>
      </row>
      <row>
        <entry>
          <table>
            <tbody>
              <row>
                <entry/>
                <entry/>
                <entry/>
                <entry/>
                <entry/>
                <entry/>
                <entry/>
              </row>
            </tbody>
          </table>
        </entry>
      </row>
    </tbody>
  </tgroup>
</table>
END
 }

if (1)
 {my $a = Data::Edit::Xml::new(<<END);                                          #TfirstNot #TlastNot #TnextNot #TprevNot #TnextWhile #TprevWhile #TnextUntil #TprevUntil
<a>
  <b/>
  <c/>
  <d/>
  <e/>
  <f/>
</a>
END
  my ($b, $c, $d, $e, $f) = $a->byList;                                         #TfirstNot #TlastNot #TnextNot #TprevNot #TnextWhile #TprevWhile #TnextUntil #TprevUntil
  ok $c == $a->firstNot_a_b;                                                    #TfirstNot
  ok $d == $a->lastNot_e_f;                                                     #TlastNot

  ok $e == $b->nextWhile_c_d;                                                   #TnextWhile
  ok $c == $f->prevWhile_e_d;                                                   #TprevWhile
  ok $c == $b->nextWhile;                                                       #TnextWhile
  ok $b == $c->prevWhile;                                                       #TprevWhile

  ok $e == $b->nextUntil_e_f;                                                   #TnextUntil
  ok $b == $f->prevUntil_a_b;                                                   #TprevUntil
  ok      !$b->nextUntil;                                                       #TnextUntil
  ok      !$c->prevUntil;                                                       #TprevUntil
 }

if (1) {                                                                        #TfirstWhile #TfirstUntil #TlastWhile #TlastUntil
  my $a = Data::Edit::Xml::new(<<END);
<a>
  <b>
    <c>
      <d>
        <e>
          <f/>
        </e>
      </d>
    </c>
  </b>
  <B>
    <C>
      <D>
        <E>
          <F/>
        </E>
      </D>
    </C>
  </B>
</a>
END
  my ($f, $e, $d, $c, $b, $F, $E, $D, $C, $B) = $a->byList;

  ok eval qq(-t \$$_ eq q($_)), $_ for qw(a b c d e f B C D E F);

  ok  $d == $a->firstWhile_a_d_c_b;
  ok  $f == $a->firstWhile_a_d_c_b_e_f_g_h;
  ok !$b->firstWhile_a;

  ok  $e == $a->firstUntil_e_d;
  ok       !$c->firstUntil_c;
  ok       !$b->firstUntil_a;

  ok  $D == $a->lastWhile_a_D_C_B;
  ok  $F == $a->lastWhile_a_D_C_B_E_F_G_H;
  ok !$B->lastWhile_a;

  ok  $E == $a->lastUntil_E_D;
  ok       !$C->lastUntil_C;
  ok !$B->lastUntil_a;
 }

if (1)                                                                          #TAUTOLOAD
 {my $a = Data::Edit::Xml::new(<<END);
<a><b><c/><d/><e/><f/></b></a>
END

  ok -t $a->first_b__first_c__next__next_e__next eq q(f);
  ok   !$a->first_b__first_c__next__next_f;
 }

if (1)                                                                          #TmatchesFirst #TmatchesLast #TmatchesNext #TmatchesPrev
 {my $a = Data::Edit::Xml::new(<<END);
<a>
  <b/>
  <c/>
  <d/>
  <e/>
  <f/>
</a>
END
  my ($b, $c, $d, $e, $f) = $a->byList;

  ok   $a->matchesFirst_b_c_d_e_f;
  ok  !$a->matchesFirst_c;
  ok   $a->matchesLast_f_e_d_c_b;
  ok  !$a->matchesLast_f_d;
  ok   $c->matchesNext_d_e_f;
  ok  !$d->matchesNext_e_f_a;
  ok   $e->matchesPrev_d_c_b;
  ok  !$e->matchesPrev_d_c_b_a;
 }

if (1)                                                                          #Tfirstn #Tnextn
 {my $a = Data::Edit::Xml::new(<<END);
<a><b><c><d/><e/><f/></c></b></a>
END
  ok -p $a eq <<END;
<a>
  <b>
    <c>
      <d/>
      <e/>
      <f/>
    </c>
  </b>
</a>
END
  ok  -t $a->firstn_0 eq q(a);
  ok  -t $a->firstn_1 eq q(b);
  ok  -t $a->firstn_2 eq q(c);
  ok  -t $a->firstn_3 eq q(d);

  ok  -t $a->firstn_3__nextn_0 eq q(d);
  ok  -t $a->firstn_3__nextn_1 eq q(e);
  ok  -t $a->firstn_3__nextn_2 eq q(f);
 }

if (1)                                                                          #Tlastn #Tprevn
 {my $a = Data::Edit::Xml::new(<<END);
<a><b><c><d/><e/><f/></c></b>
   <B><C><D/><E/><F/></C></B></a>
END
  ok -p $a eq <<END;
<a>
  <b>
    <c>
      <d/>
      <e/>
      <f/>
    </c>
  </b>
  <B>
    <C>
      <D/>
      <E/>
      <F/>
    </C>
  </B>
</a>
END

  ok  -t $a->lastn_0 eq q(a);
  ok  -t $a->lastn_1 eq q(B);
  ok  -t $a->lastn_2 eq q(C);
  ok  -t $a->lastn_3 eq q(F);

  ok  -t $a->lastn_3__prevn_0 eq q(F);
  ok  -t $a->lastn_3__prevn_1 eq q(E);
  ok  -t $a->lastn_3__prevn_2 eq q(D);
 }

if (1) {
  my $a = Data::Edit::Xml::new(q(<a>).(q(<b>𝝱</b>)x1e3).q(</a>));               #TwriteCompressedFile #TreadCompressedFile
  my $file = $a->writeCompressedFile(q(zzz.xml.zip));                           #TwriteCompressedFile #TreadCompressedFile
  ok length(-s $a) eq 8007;
  ok -e $file;
  my $A = readCompressedFile($file);                                            #TwriteCompressedFile #TreadCompressedFile
  ok $a->equals($A);                                                            #TwriteCompressedFile #TreadCompressedFile
  ok length(-s $a) == length(-s $A);
  ok -t $a->firstn_0    eq q(a);
  ok -t $a->firstn_1    eq q(b);
  ok $a->firstn_2__text eq q(𝝱);
  unlink $file;
 }

if (1)                                                                          #TditaPrettyPrintWithHeaders
 {my $a = Data::Edit::Xml::new(q(<concept/>));

  ok $a->ditaPrettyPrintWithHeaders eq <<END;
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE concept PUBLIC "-//OASIS//DTD DITA Concept//EN" "concept.dtd" []>
<concept/>
END
 }

if (1)                                                                          # Command and attributes
 {my $a = Data::Edit::Xml::new(<<END);
<a><b><c><d/><e/><e/><f/><f/></c></b></a>
END
  my $n = 0;
  $a->by(sub
   {my ($o) = @_;
    $o->id    = ++$n % 2;
    $o->class =   $n % 3;
   });

  ok -p $a eq <<END;                                                           #TdeleteAttrsInTree
<a class="2" id="0">
  <b class="1" id="1">
    <c class="0" id="0">
      <d class="1" id="1"/>
      <e class="2" id="0"/>
      <e class="0" id="1"/>
      <f class="1" id="0"/>
      <f class="2" id="1"/>
    </c>
  </b>
</a>
END

  $a->by(sub
   {my ($o) = @_;
    ok  $o->class == 2 if $o->atId_0_e;
    ok  $o->class == 0 if $o->atId_1_e_c_b;
    ok !$o->atId_1_e_b;
   });

  $a->deleteAttrsInTree_class;                                                  #TdeleteAttrsInTree
  ok -p $a eq <<END                                                             #TdeleteAttrsInTree
<a id="0">
  <b id="1">
    <c id="0">
      <d id="1"/>
      <e id="0"/>
      <e id="1"/>
      <f id="0"/>
      <f id="1"/>
    </c>
  </b>
</a>
END
 }

if (1)                                                                          #TattrX
 {my $a = Data::Edit::Xml::new(q(<a><b name="bb"/></a>));

  my  $b = $a->first;
  ok  $b->attrX_name eq q(bb);
  ok !$b->attrX_bbb;
 }

if (1)
 {my $a = Data::Edit::Xml::new(q(<a>aaa</a>));

  my  $t = $a->first;
  ok !$t->unwrap;
 }

if (1)
 {my $a = Data::Edit::Xml::new(<<END);
<?xml version="1.0" encoding="UTF-8"?>
<body>
<p>
±
</p>
</body>
END
 }

if (1)
 {my $a = Data::Edit::Xml::new(<<END);
<a><b/><c/><B><c/></B><c/><b/></a>
END

  $a->numberTreesJustIds(q(i));

  ok -p $a eq <<END;                                                            #TwrapRuns #TfindById
<a id="i1">
  <b id="i2"/>
  <c id="i3"/>
  <B id="i4">
    <c id="i5"/>
  </B>
  <c id="i6"/>
  <b id="i7"/>
</a>
END

  ok -t $a->findById_i4 eq q(B);                                                #TfindById
  ok -t $a->findById_i5 eq q(c);                                                #TfindById

  $a->wrapRuns(q(B));                                                           #TwrapRuns

  ok -p $a eq <<END;                                                            #TwrapRuns
<a id="i1">
  <B>
    <b id="i2"/>
    <c id="i3"/>
  </B>
  <B id="i4">
    <c id="i5"/>
  </B>
  <B>
    <c id="i6"/>
    <b id="i7"/>
  </B>
</a>
END
 }

if (1)                                                                          #TmatchesNode #TmatchesSubTree #TfindMatchingSubTrees
 {my $a = Data::Edit::Xml::new(<<END);
<a       id="1">
  <b     id="2"   name="b">
    <c   id="3"   name="c"/>
  </b>
  <c     id="4">
    <b   id="5"   name="b">
      <c id="6"   name="c"/>
    </b>
  </c>
</a>
END

  my ($c, $b, $C, $B) = $a->byList;
  ok  $b->id == 2;
  ok  $c->id == 3;
  ok  $B->id == 5;
  ok  $C->id == 6;
  ok  $c->matchesNode($C, qw(name));
  ok !$c->matchesNode($C, qw(id name));
  ok  $c->matchesSubTree($C, qw(name));
  ok  $b->matchesSubTree($B, qw(name));
  ok !$c->matchesSubTree($C, qw(id name));
  ok !$b->matchesSubTree($C, qw(name));

  is_deeply [$a->findMatchingSubTrees($b, qw(name))], [$b, $B];
  is_deeply [$a->findMatchingSubTrees($c, qw(name))], [$c, $C];
  is_deeply [$a->findMatchingSubTrees(new(q(<c/>)))], [$c, $C];
  is_deeply [$a->findMatchingSubTrees(new(q(<b><c/></b>)))], [$b, $B];
  is_deeply [$a->findMatchingSubTrees(new(q(<b id="2"><c id="3"/></b>)), q(id))], [$b];
 }

if (1)                                                                          #TputFirstAsTree #TputLastAsTree #TputNextAsTree #TputPrevAsTree
 {my $a = Data::Edit::Xml::new(q(<a/>));

  ok -p $a eq <<END;
<a/>
END

  my $b = $a->putFirstAsTree(q(<b/>));
  ok -p $a eq <<END;
<a>
  <b/>
</a>
END

  $b->putNextAsTree(q(<c/>));
  ok -p $a eq <<END;
<a>
  <b/>
  <c/>
</a>
END

  my $e = $a->putLastAsTree(q(<e/>));
  ok -p $a eq <<END;
<a>
  <b/>
  <c/>
  <e/>
</a>
END

  $e->putPrevAsTree(q(<d/>));
  ok -p $a eq <<END;
<a>
  <b/>
  <c/>
  <d/>
  <e/>
</a>
END
 }

if (1)                                                                          #TwrapFromFirst
 {my $a = Data::Edit::Xml::new(q(<a><b/><c/><d/></a>));

  ok -p $a eq <<END;
<a>
  <b/>
  <c/>
  <d/>
</a>
END

  $a->go_c->wrapFromFirst_B;
  ok -p $a eq <<END;
<a>
  <B>
    <b/>
    <c/>
  </B>
  <d/>
</a>
END
 }

if (1)                                                                          #TwrapToLast
 {my $a = Data::Edit::Xml::new(q(<a><b/><c/><d/></a>));

  ok -p $a eq <<END;
<a>
  <b/>
  <c/>
  <d/>
</a>
END

  $a->go_c->wrapToLast_D;
  ok -p $a eq <<END;
<a>
  <b/>
  <D>
    <c/>
    <d/>
  </D>
</a>
END
 }

if (1)                                                                          #Theight #Tdepth #TdepthProfile
 {my $a = Data::Edit::Xml::new(q(<a><b><c><d/></c><e/></b></a>));

  ok -p $a eq <<END;
<a>
  <b>
    <c>
      <d/>
    </c>
    <e/>
  </b>
</a>
END

 my ($d, $c, $e, $b) = $a->byList;
 ok $a->height == 4;
 ok $a->depth  == 0;
 ok $c->depth  == 2;
 ok $c->height == 2;
 ok $e->depth  == 2;
 ok $e->height == 1;

 is_deeply [$a->depthProfile], [qw(4 3 3 2 1)];
}

if (1)                                                                          #TsetDepthProfile #TsetRepresentationAsTagsAndText #TsetRepresentationAsText #TdepthProfileLast #TrepresentationLast #TmatchNodesByRepresentation #TstringTagsAndText #TstringText
 {my $a = Data::Edit::Xml::new(<<END);
<a>
  <b>
    <c>cc
      <d/>
dd
    </c>
  </b>
  <B>
    <c>cc
      <d/>
dd
    </c>
  </B>
</a>
END

 my $b = $a->first_b; my $B = $a->last_B;
 my $c = $b->first_c; my $C = $B->first_c;
 my $d = $c->first_d; my $D = $C->first_d;

 $a->setDepthProfile;

 ok $b->depthProfileLast eq q(3 3 3 2 1);
 ok $b->depthProfileLast eq $B->depthProfileLast;

# Represent using tags and text
 $a->setRepresentationAsTagsAndText;
 is_deeply [$b->stringTagsAndText],   [qw(cc d dd c b)];
 is_deeply [$B->stringTagsAndText],   [qw(cc d dd c B)];
 ok         $b->representationLast  eq qq(cc d dd c b);
 ok         $B->representationLast  eq qq(cc d dd c B);
 ok         $c->representationLast  eq qq(cc d dd c);
 ok         $C->representationLast  eq qq(cc d dd c);
 ok dump($b->representationLast) ne dump($B->representationLast);
 is_deeply  $c->representationLast,
            $C->representationLast;

 my $m  = $a->matchNodesByRepresentation;

 my $bb = $b->representationLast;
 is_deeply $m->{$bb}, [$b];

 my $cc = $c->representationLast;
 is_deeply $m->{$cc}, [$c, $C];

# Represent using just text
 $a->setRepresentationAsText;
 is_deeply [$b->stringText],          [qw(cc dd)];
 is_deeply [$B->stringText],          [qw(cc dd)];
 ok         $b->representationLast  eq qq(cc dd);
 ok         $B->representationLast  eq qq(cc dd);
 is_deeply  $b->representationLast,
            $B->representationLast;
 is_deeply  $c->representationLast,
            $C->representationLast;

 my $M  = $a->matchNodesByRepresentation;
 my $BB = $b->representationLast;
 is_deeply $M->{$BB}, [$c, $b, $C, $B];

 my $CC = $c->representationLast;
 is_deeply $M->{$BB}, [$c, $b, $C, $B];

 ok $b->representationLast eq $c->representationLast;
}

if (1)                                                                          #TisOnlyChildText
 {my $a = Data::Edit::Xml::new(q(<a>aaaa</a>));
  ok $a->first->isOnlyChildText;
 }

if (1)                                                                          #TisOnlyChildBlankText
 {my $a = Data::Edit::Xml::new(q(<a>aaaa</a>));
  $a->first->text = q( );
  ok  $a->prettyStringCDATA eq qq(<a><CDATA> </CDATA></a>\n);
  ok  $a->first->isOnlyChildBlankText;
  ok !$a->isOnlyChildBlankText;
 }

if (1)                                                                          #TisOnlyChildBlankText
 {my $a = Data::Edit::Xml::new(q(<a/>));
  my $b = $a->new(q(<b/>));
  ok -p $a eq qq(<a/>\n);
  ok -p $b eq qq(<b/>\n);
 }

if (1)                                                                          #TattrValueAt #TattrAt
 {my $a = Data::Edit::Xml::new(q(<a><b c="C"/></a>));
  my $b = $a->first;
  ok !$a->attrAt_id;
  ok  $b->attrAt_c_b_a;
  ok !$b->attrAt_b_b_a;
  ok !$b->attrValueAt_c_C_c_a;
  ok  $b->attrValueAt_c_C_b_a;
 }

if (1)                                                                          #TditaListToTable
 {my $a = Data::Edit::Xml::new(<<END);
<ul id="Table3" outputclass="fcRowList">
    <li>
        <p id="1">1111</p>
        <p id="2">2222</p>
        <p id="3">3333</p>
        <p id="4">4444</p>
    </li>
    <li>
        <p id="tableToListA1" outputclass="1">aaaa1111</p>
        <p id="tableToListA2" outputclass="1">aaaa2222</p>
        <p id="tableToListA3" outputclass="1">aaaa3333</p>
        <p id="tableToListA4" outputclass="1">aaaa4444</p>
    </li>
    <li>
        <p id="tableToListB1" outputclass="1">bbbb1111</p>
        <p id="tableToListB2" outputclass="1">bbbb2222</p>
        <p id="tableToListB3" outputclass="1">bbbb3333</p>
    </li>
    <li>
        <p id="tableToListC1" outputclass="1">cccc1111</p>
        <p id="tableToListC2" outputclass="1">cccc2222</p>
        <p id="tableToListC3" outputclass="1">cccc3333</p>
        <p id="tableToListC4" outputclass="1">cccc4444</p>
        <p id="tableToListC5" outputclass="1">cccc5555</p>
    </li>
</ul>
END
  $a->ditaListToTable;
  ok nws(-p $a) eq nws(<<END)
<table id="Table3" outputclass="fcRowList">
  <tgroup cols="5">
    <colspec colname="c1" colnum="1" colwidth="1*"/>
    <colspec colname="c2" colnum="2" colwidth="1*"/>
    <colspec colname="c3" colnum="3" colwidth="1*"/>
    <colspec colname="c4" colnum="4" colwidth="1*"/>
    <colspec colname="c5" colnum="5" colwidth="1*"/>
    <thead>
      <row>
        <entry/>
        <entry/>
        <entry/>
        <entry/>
        <entry/>
      </row>
    </thead>
    <tbody>
      <row>
        <entry id="1">1111</entry>
        <entry id="2">2222</entry>
        <entry id="3">3333</entry>
        <entry id="4">4444</entry>
      </row>
      <row>
        <entry id="tableToListA1" outputclass="1">aaaa1111</entry>
        <entry id="tableToListA2" outputclass="1">aaaa2222</entry>
        <entry id="tableToListA3" outputclass="1">aaaa3333</entry>
        <entry id="tableToListA4" outputclass="1">aaaa4444</entry>
      </row>
      <row>
        <entry id="tableToListB1" outputclass="1">bbbb1111</entry>
        <entry id="tableToListB2" outputclass="1">bbbb2222</entry>
        <entry id="tableToListB3" outputclass="1">bbbb3333</entry>
      </row>
      <row>
        <entry id="tableToListC1" outputclass="1">cccc1111</entry>
        <entry id="tableToListC2" outputclass="1">cccc2222</entry>
        <entry id="tableToListC3" outputclass="1">cccc3333</entry>
        <entry id="tableToListC4" outputclass="1">cccc4444</entry>
        <entry id="tableToListC5" outputclass="1">cccc5555</entry>
      </row>
    </tbody>
  </tgroup>
</table>
END
 }

if (1)                                                                          #TgiveEveryIdAGuid
 {my $a = Data::Edit::Xml::new(<<END);
<a id="a">
  <b id="b">
    <c id="c"/>
  </b>
  <d/>
</a>
END
  my $n  =0;
  $a->giveEveryIdAGuid(sub
   {my ($n, $o) = @_;
    qq(GUID-$n)
   });

  ok -p $a eq <<END;
<a id="GUID-3">
  <b id="GUID-2">
    <c id="GUID-1"/>
  </b>
  <d/>
</a>
END

 ok $a->stringReplacingIdsWithLabels eq q(<a id="a"><b id="b"><c id="c"/></b><d/></a>);
 ok $a->stringExtendingIdsWithLabels eq q(<a id="GUID-3, a"><b id="GUID-2, b"><c id="GUID-1, c"/></b><d/></a>);

 }

if (1) {                                                                        #Tlocation #TlineLocation #TapproxLocation #TclosestLocation #TformatOxygenMessage
  my $a = Data::Edit::Xml::new(<<END, lineNumbers=>1, inputFile=>q(aaa.xml));
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE concept PUBLIC "-//OASIS//DTD DITA Concept//EN" "concept.dtd">
<test id="t1">
 <title>Test_</title>
  <testbody>
    <setup>
      <p>Place the boiling water and fresh tea in the pot.</p>
    </setup>
    <checks>
      <p>Make sure the pot is on an insulated surface.</p>
    </checks>
    <run>
      <p>Stir with a spoon then let brew for 5 minutes.</p>
    </run>
    <results>
      <p>Pour the tea into a cup.</p>
    </results>
    <outcome>
      <p>An enjoyable cup of tea.</p>
    </outcome>
  </testbody>
</test>
END

  ok -p $a eq <<END;
<test id="t1" xtrf="3.1:14">
  <title xtrf="4.2:8">Test_</title>
  <testbody xtrf="5.3:12">
    <setup xtrf="6.5:11">
      <p xtrf="7.7:9">Place the boiling water and fresh tea in the pot.</p>
    </setup>
    <checks xtrf="9.5:12">
      <p xtrf="10.7:9">Make sure the pot is on an insulated surface.</p>
    </checks>
    <run xtrf="12.5:9">
      <p xtrf="13.7:9">Stir with a spoon then let brew for 5 minutes.</p>
    </run>
    <results xtrf="15.5:13">
      <p xtrf="16.7:9">Pour the tea into a cup.</p>
    </results>
    <outcome xtrf="18.5:13">
      <p xtrf="19.7:9">An enjoyable cup of tea.</p>
    </outcome>
  </testbody>
</test>
END

  ok $a->go_testbody_run_p__location eq q( on line 13 from 7 to 9 in file: aaa.xml);

  my $p = $a->go_testbody_run_p;
  $p->putNext(my $q = $p->newTag_hello);
  ok $p->lineLocation eq q(on line 13 from 7 to 9);

  ok $q->location eq q( in file: aaa.xml);
  ok $q->closestLocation == $p;
  ok $q->approxLocation eq q( on line 13 from 7 to 9 in file: aaa.xml);

  ok $q->formatOxygenMessage(q(E), q(), q(Hello detected)) eq <<END;
Type: E
Line: 13
Column: 7
EndLine: 13
EndColumn: 9
AdditionalInfoURL:
Description: Hello detected
END
 }

if (1) {                                                                        #TbyList #TbyReverseList #TdownList #TdownReverseList
  my $a = Data::Edit::Xml::new(<<END);
<a>
 <b>
  <c/>
  <d/>
 </b>
 <e/>
 <f>
  <g/>
  <h/>
 </f>
</a>
END

  ok q(c d b e g h f a) eq join ' ', map{-t $_} $a->byList;
  ok q(h g f e d c b a) eq join ' ', map{-t $_} $a->byReverseList;
  ok q(a b c d e f g h) eq join ' ', map{-t $_} $a->downList;
  ok q(a f h g e b d c) eq join ' ', map{-t $_} $a->downReverseList;
 }

if (1) {
  my $a = Data::Edit::Xml::new(<<END, lineNumbers=>1);
<a>
 <b>
  <c/>
  <c>
    <d/>
  </c>
 </b>
</a>
END

  ok $a->go_b_c_1_d__location eq q( on line 5 from 5 to 8);
 }

if (1) {                                                                        #TprecedingSiblingOf #TsucceedingSiblingOf
  my $a = Data::Edit::Xml::new(<<END);
<a>
 <b>
  <c/>
  <d/>
  <e/>
 </b>
 <B>
  <C/>
  <D/>
  <E/>
 </B>
</a>
END

  my ($c, $d, $e, $b, $C, $D, $B) = $a->byList;
  ok !$e->succeedingSiblingOf($e);
  ok  $e->succeedingSiblingOf($d);
  ok  $e->succeedingSiblingOf($c);
  ok !$e->succeedingSiblingOf($b);
  ok !$e->succeedingSiblingOf($B);
  ok !$e->succeedingSiblingOf($C);

  ok !$c->precedingSiblingOf($c);
  ok  $c->precedingSiblingOf($d);
  ok  $c->precedingSiblingOf($e);
  ok !$c->precedingSiblingOf($b);
  ok !$c->precedingSiblingOf($B);
  ok !$c->precedingSiblingOf($C);
 }

if (1) {                                                                        #TmoveStartFirst
  my $a = Data::Edit::Xml::new(<<END);
<a>
 <b/>
 <c/>
 <d>
  <e/>
 </d>
</a>
END

  my ($b, $c, $e, $d) = $a->byList;

  $d->moveStartFirst;
  ok -p $a eq <<END;
<a>
  <d>
    <b/>
    <c/>
    <e/>
  </d>
</a>
END
 }

if (1) {                                                                        #TmoveStartAfter
  my $a = Data::Edit::Xml::new(<<END);
<a>
 <b/>
 <c/>
 <d>
  <e/>
 </d>
</a>
END

  my ($b, $c, $e, $d) = $a->byList;

  $d->moveStartAfter($b);
  ok -p $a eq <<END;
<a>
  <b/>
  <d>
    <c/>
    <e/>
  </d>
</a>
END

  $d->moveStartAfter($c);
  ok -t $d eq q(d);
  ok -t $c eq q(c);
  ok -p $a eq <<END;
<a>
  <b/>
  <c/>
  <d>
    <e/>
  </d>
</a>
END

  $d->moveStartAfter($e);
  ok -p $a eq <<END;
<a>
  <b/>
  <c/>
  <e/>
  <d/>
</a>
END
 }

if (1) {                                                                        #TmoveStartBefore
  my $a = Data::Edit::Xml::new(<<END);
<a>
 <b/>
 <c/>
 <d>
  <e/>
 </d>
</a>
END

  my ($b, $c, $e, $d) = $a->byList;

  $d->moveStartBefore($c);
  ok -p $a eq <<END;
<a>
  <b/>
  <d>
    <c/>
    <e/>
  </d>
</a>
END

  $d->moveStartBefore($e);
  ok -t $d eq q(d);
  ok -t $e eq q(e);
  ok -p $a eq <<END;
<a>
  <b/>
  <c/>
  <d>
    <e/>
  </d>
</a>
END

  $d->moveEndBefore($e);
  ok -t $d eq q(d);
  ok -t $e eq q(e);
  ok -p $a eq <<END;
<a>
  <b/>
  <c/>
  <d/>
  <e/>
</a>
END
 }

if (1) {                                                                        #TmoveEndLast
  my $a = Data::Edit::Xml::new(<<END);
<a>
 <b>
  <c/>
 </b>
 <d/>
 <e/>
</a>
END

  my ($c, $b, $d, $e) = $a->byList;

  $b->moveEndLast;
  ok -p $a eq <<END;
<a>
  <b>
    <c/>
    <d/>
    <e/>
  </b>
</a>
END
 }

if (1) {                                                                        #TmoveEndAfter
  my $a = Data::Edit::Xml::new(<<END);
<a>
 <b>
  <c/>
 </b>
 <d/>
 <e/>
</a>
END

  my ($c,  $b, $d, $e) = $a->byList;

  $b->moveEndAfter($d);
  ok -p $a eq <<END;
<a>
  <b>
    <c/>
    <d/>
  </b>
  <e/>
</a>
END

  $b->moveEndAfter($c);
  ok -p $a eq <<END;
<a>
  <b>
    <c/>
  </b>
  <d/>
  <e/>
</a>
END
 }

if (1) {                                                                        #TmoveEndBefore
  my $a = Data::Edit::Xml::new(<<END);
<a>
 <b>
  <c/>
 </b>
 <d/>
 <e/>
</a>
END

  my ($c,  $b, $d, $e) = $a->byList;

  $b->moveEndBefore($e);
  ok -p $a eq <<END;
<a>
  <b>
    <c/>
    <d/>
  </b>
  <e/>
</a>
END

  $b->moveEndBefore($d);
  ok -p $a eq <<END;
<a>
  <b>
    <c/>
  </b>
  <d/>
  <e/>
</a>
END
 }

if (1) {                                                                        #TditaXrefs
  my $a = Data::Edit::Xml::new(<<END);
<body>
 <xref href="http://www.org">a</xref>
</body>
END

  $a->ditaXrefs;

  ok -p $a eq <<END;
<body>
  <p>
    <xref format="html" href="http://www.org" scope="external">a</xref>
  </p>
</body>
END
 }

if (1) {                                                                        #TdeleteAttrValueAtInTree
  my $a = Data::Edit::Xml::new(<<END);
<a>
 <b>
   <c id="c"/>
   <c id="d"/>
 </b>
 <d>
   <c id="c"/>
   <c id="d"/>
 </d>
</a>
END

  $a->deleteAttrValueAtInTree_id_c_c_b;
  ok -p $a eq <<END;
<a>
  <b>
    <c/>
    <c id="d"/>
  </b>
  <d>
    <c id="c"/>
    <c id="d"/>
  </d>
</a>
END
 }

if (1) {                                                                        #TditaStepsToChoices
  my $a = Data::Edit::Xml::new(<<END);
<steps>
  <step>
    <cmd>Command</cmd>
    <stepresult>
Step result
</stepresult>
    <stepxmp>
Step example
</stepxmp>
  </step>
  <step>
    <cmd>Command</cmd>
    <stepresult>
Step result
</stepresult>
    <stepxmp>
Step example
</stepxmp>
  </step>
</steps>
END

  $a->ditaStepsToChoices;

  ok -p $a eq <<END;
<choices>
  <choice>
    <p>Command</p>
Step result
Step example
  </choice>
  <choice>
    <p>Command</p>
Step result
Step example
  </choice>
</choices>
END
 }

if (1) {                                                                        #TditaConvertSubStepsToSteps
  my $a = Data::Edit::Xml::new(<<END);
<substeps>
  <substep>
    <cmd>C1</cmd>
  </substep>
  <substep>
    <cmd>C2</cmd>
  </substep>
</substeps>
END

  $a->ditaConvertSubStepsToSteps;

  ok -p $a eq <<END;
<steps>
  <step>
    <cmd>C1</cmd>
  </step>
  <step>
    <cmd>C2</cmd>
  </step>
</steps>
END
 }

if (1) {                                                                        #TditaConvertOlToSubSteps
  my $a = Data::Edit::Xml::new(<<END);
<ol>
  <li>A</li>
  <li>B</li>
  <li>C</li>
</ol>
END

  $a->ditaConvertOlToSubSteps;

  ok -p $a eq <<END;
<substeps>
  <substep>
    <cmd>A</cmd>
  </substep>
  <substep>
    <cmd>B</cmd>
  </substep>
  <substep>
    <cmd>C</cmd>
  </substep>
</substeps>
END
 }

if (1) {                                                                        #TditaConvertUlToSubSteps
  my $a = Data::Edit::Xml::new(<<END);
<ul>
  <li>A</li>
  <li>B</li>
  <li>C</li>
</ul>
END

  $a->ditaConvertUlToSubSteps;

  ok -p $a eq <<END;
<substeps>
  <substep>
    <cmd>A</cmd>
  </substep>
  <substep>
    <cmd>B</cmd>
  </substep>
  <substep>
    <cmd>C</cmd>
  </substep>
</substeps>
END
 }

if (1) {                                                                        #TditaTGroupStatistics #TditaRemoveTGroupTrailingEmptyEntries #TditaMaximumNumberOfEntriesInATGroupRow
  my $a = Data::Edit::Xml::new(<<END);
<table>
  <tgroup cols="1">
    <colspec/>
    <thead>
      <row>
        <entry>aaaa</entry>
        <entry/>
      </row>
      <row>
        <entry>bbbb</entry>
      </row>
    </thead>
    <tbody>
      <row>
        <entry>cccc</entry>
        <entry/>
        <entry/>
      </row>
      <row>
        <entry>dddd</entry>
      </row>
    </tbody>
  </tgroup>
</table>
END

  my $g = $a->go_tgroup;

  ok $g->ditaMaximumNumberOfEntriesInATGroupRow == 3;

  is_deeply $g->ditaTGroupStatistics,
bless({
  colsAttribute => 1,
  colSpec => 1,
  maxBody => 3,
  maxBodyMinusPadding => 3,
  maxHead => 2,
  maxHeadMinusPadding => 2,
  minBody => 1,
  minHead => 1,
  rows    => 4,
}, "Data::Edit::Xml::Table::Statistics");

  $g->ditaRemoveTGroupTrailingEmptyEntries;

  ok -p $a eq <<END;
<table>
  <tgroup cols="1">
    <colspec/>
    <thead>
      <row>
        <entry>aaaa</entry>
      </row>
      <row>
        <entry>bbbb</entry>
      </row>
    </thead>
    <tbody>
      <row>
        <entry>cccc</entry>
      </row>
      <row>
        <entry>dddd</entry>
      </row>
    </tbody>
  </tgroup>
</table>
END
 }

if (1) {                                                                        #TfixEntryColSpan #TfixEntryRowSpan
  my $a = Data::Edit::Xml::new(<<END);
<table>
  <tgroup cols="7">
    <colspec colname="a"/>
    <colspec colname="b"/>
    <colspec colname="c"/>
    <colspec colname="d"/>
    <colspec colname="e"/>
    <colspec colname="f"/>
    <colspec colname="g"/>
    <tbody>
      <row>
        <entry colspan="7"/>
      </row>
      <row>
        <entry colspan="2"/>
        <entry colspan="3"/>
        <entry colspan="2"/>
      </row>
      <row>
        <entry/>
        <entry rowspan="2"/>
        <entry colspan="2"/>
        <entry colspan="2"/>
        <entry/>
      </row>
    </tbody>
  </tgroup>
</table>
END

  $a->by(sub
   {my ($e) = @_;
    if ($e->at_entry)
     {$e->fixEntryColSpan;
      $e->fixEntryRowSpan;
     }
   });

  ok -p $a eq <<END;
<table>
  <tgroup cols="7">
    <colspec colname="a"/>
    <colspec colname="b"/>
    <colspec colname="c"/>
    <colspec colname="d"/>
    <colspec colname="e"/>
    <colspec colname="f"/>
    <colspec colname="g"/>
    <tbody>
      <row>
        <entry nameend="g" namest="a"/>
      </row>
      <row>
        <entry nameend="b" namest="a"/>
        <entry nameend="e" namest="c"/>
        <entry nameend="g" namest="f"/>
      </row>
      <row>
        <entry/>
        <entry morerows="1"/>
        <entry nameend="d" namest="c"/>
        <entry nameend="f" namest="e"/>
        <entry/>
      </row>
    </tbody>
  </tgroup>
</table>
END
 }

if (1) {
  my $a = Data::Edit::Xml::new(<<END);
<table>
  <tgroup cols="8">
    <tbody>
      <row>
        <entry namest="c1" nameend="c2">12</entry>
        <entry namest="c3" nameend="c5">35</entry>
      </row>
      <row>
        <entry namest="c1" nameend="c9">19</entry>
      </row>
    </tbody>
  </tgroup>
</table>
END

  ok $a->go_tgroup_tbody_row  ->ditaNumberOfColumnsInRow == 5;
  ok $a->go_tgroup_tbody_row_1->ditaNumberOfColumnsInRow == 9;

  ok $a->fixTable ne <<END;
<table>
  <tgroup cols="9">
    <colspec colname="c1" colnum="1" colwidth="1*"/>
    <colspec colname="c2" colnum="2" colwidth="1*"/>
    <colspec colname="c3" colnum="3" colwidth="1*"/>
    <colspec colname="c4" colnum="4" colwidth="1*"/>
    <colspec colname="c5" colnum="5" colwidth="1*"/>
    <colspec colname="c6" colnum="6" colwidth="1*"/>
    <colspec colname="c7" colnum="7" colwidth="1*"/>
    <colspec colname="c8" colnum="8" colwidth="1*"/>
    <colspec colname="c9" colnum="9" colwidth="1*"/>
    <tbody>
      <row>
        <entry nameend="c2" namest="c1">12</entry>
        <entry nameend="c5" namest="c3">35</entry>
        <entry/>
        <entry/>
        <entry/>
        <entry/>
      </row>
      <row>
        <entry nameend="c9" namest="c1">19</entry>
      </row>
    </tbody>
  </tgroup>
</table>
END
}

if (1) {                                                                        #TditaAddPadEntriesToTGroupRows  #TditaRemoveTrailingEmptyEntries #TfixTGroup #TfixTable
  my $a = Data::Edit::Xml::new(<<END);
<table>
  <tgroup cols="1">
    <colspec/>
    <thead>
      <row>
        <entry>aaaa</entry>
      </row>
      <row>
        <entry>bbbb</entry>
      </row>
    </thead>
    <tbody>
      <row>
        <entry>cccc</entry>
      </row>
      <row>
        <entry>dddd</entry>
      </row>
    </tbody>
  </tgroup>
</table>
END

  my $g = $a->go_tgroup;

  $g->ditaAddPadEntriesToTGroupRows(2);

  is_deeply $g->ditaTGroupStatistics,
bless({
  colsAttribute => 1,
  colSpec => 1,
  maxBody => 2,
  maxBodyMinusPadding => 2,
  maxHead => 2,
  maxHeadMinusPadding => 2,
  minBody => 2,
  minHead => 2,
  rows    => 4,
}, "Data::Edit::Xml::Table::Statistics");

  $g->fixTGroup;

  ok -p $a eq <<END;
<table>
  <tgroup cols="2">
    <colspec colname="c1" colnum="1" colwidth="1*"/>
    <colspec colname="c2" colnum="2" colwidth="1*"/>
    <thead>
      <row>
        <entry>aaaa</entry>
        <entry/>
      </row>
      <row>
        <entry>bbbb</entry>
        <entry/>
      </row>
    </thead>
    <tbody>
      <row>
        <entry>cccc</entry>
        <entry/>
      </row>
      <row>
        <entry>dddd</entry>
        <entry/>
      </row>
    </tbody>
  </tgroup>
</table>
END

  is_deeply $g->ditaTGroupStatistics,
bless({
  colsAttribute => 2,
  colSpec => 2,
  maxBody => 2,
  maxBodyMinusPadding => 2,
  maxHead => 2,
  maxHeadMinusPadding => 2,
  minBody => 2,
  minHead => 2,
  rows    => 4,

}, "Data::Edit::Xml::Table::Statistics");

  $a->fixTable;
  is_deeply $g->ditaTGroupStatistics,
bless({
  colsAttribute => 2,
  colSpec => 2,
  maxBody => 2,
  maxBodyMinusPadding => 2,
  maxHead => 2,
  maxHeadMinusPadding => 2,
  minBody => 2,
  minHead => 2,
  rows    => 4,
}, "Data::Edit::Xml::Table::Statistics");

 }

if (1) {                                                                        #TmergeLikeElements
  my $a = Data::Edit::Xml::new(<<END);
<x><a>aa</a><b>bb</b><b>cc</b><b>dd</b><b>ee</b></x>
END

  ok -p $a eq <<END;
<x>
  <a>aa</a>
  <b>bb</b>
  <b>cc</b>
  <b>dd</b>
  <b>ee</b>
</x>
END

  $a->by(sub{$_->mergeLikeElements});
  ok -p $a eq <<END;
<x>
  <a>aa</a>
  <b>bb cc dd ee</b>
</x>
END

 }

if (1) {                                                                        #TmergeLikeNext
  my $a = Data::Edit::Xml::new(<<END);
<a>
  <b>bb</b>
  <b>dd</b>
</a>
END

  $a->first->mergeLikeNext;

  ok -p $a eq <<END;
<a>
  <b>bb dd</b>
</a>
END
 }

if (1) {                                                                        #TmergeLikePrev
  my $a = Data::Edit::Xml::new(<<END);
<a>
  <b>bb</b>
  <b>dd</b>
</a>
END

  $a->last->mergeLikePrev;

  ok -p $a eq <<END;
<a>
  <b>bb dd</b>
</a>
END
 }

if (1) {                                                                        #TditaListToChoices
  my $a = Data::Edit::Xml::new(<<END);
<ol>
  <li>
    <p/>
  </li>
  <li>
    <p/>
  </li>
</ol>
END

  $a->ditaListToChoices;
  ok -p $a eq <<END;
<choices>
  <choice>
    <p/>
  </choice>
  <choice>
    <p/>
  </choice>
</choices>
END
 }

if (1) {                                                                        #TfirstUntilText #TlastUntilText
  my $a = Data::Edit::Xml::new(<<END);
<a>
  <b>
    cccc
    <d/>
    <e/>
  </b>
  <f>
    <g/>
    hhhh
    <i/>
  </f>
  <j>
    <k/>
    <l/>
    mmmm
  </j>
</a>
END

  ok $a->firstUntilText->text =~ m(cccc)s;
  ok $a->lastUntilText ->text =~ m(mmmm)s;

 }

if (1) {                                                                        #TstringAsMd5Sum
  my $a = Data::Edit::Xml::new(<<END);
<a>
  <b>
    c
  </b>
</a>
END

  ok $a->stringAsMd5Sum eq q(390bf05d8f5671cc6a4771834840d695);
  ok ((-k $a)->id       eq q(GUID-390bf05d-8f56-71cc-6a47-71834840d695));
  ok $a->id             ne (-k $a->first)->id;
 }


if (1) {                                                                        #TcreateGuidId
  my $a = Data::Edit::Xml::new(<<END);
<a>
  <b>
    c
  </b>
</a>
END

  ok $a->createGuidId->id eq q(GUID-390bf05d-8f56-71cc-6a47-71834840d695);
 }

if (1) {                                                                        #TswapTags #TswapTagWithParent
  my $a = Data::Edit::Xml::new(<<END);
<a>
  <b>
    <c/>
  </b>
</a>
END

  $a->swapTags($a->first);
  ok -p $a eq <<END;
<b>
  <a>
    <c/>
  </a>
</b>
END

  my ($C, $A) = $a->downWhileFirst->swapTagWithParent;
  ok -t $A eq q(a);
  ok -t $C eq q(c);
  ok -p $a eq <<END;
<b>
  <c>
    <a/>
  </c>
</b>
END
 }

if (1) {                                                                        #TditaSampleBookMap #TisADitaMap

  my $a = Data::Edit::Xml::ditaSampleBookMap
   (author=>q(mim),
    chapters=>Data::Edit::Xml::new(<<END));
<a>
  <b>
    <c/>
  </b>
</a>
END

  ok $a->isADitaMap;
  ok !$a->first->isADitaMap;

  ok -p $a eq <<END;
<bookmap id="GUID-3b04e859-bc04-ac2d-e4bb-f0f99b2a8f90">
  <booktitle>
    <mainbooktitle/>
  </booktitle>
  <bookmeta>
    <shortdesc/>
    <author>mim</author>
    <source/>
    <category/>
    <keywords>
      <keyword/>
    </keywords>
    <prodinfo>
      <prodname product=""/>
      <vrmlist>
        <vrm version=""/>
      </vrmlist>
      <prognum/>
      <brand/>
    </prodinfo>
    <bookchangehistory>
      <approved>
        <revisionid/>
      </approved>
    </bookchangehistory>
    <bookrights>
      <copyrfirst>
        <year/>
      </copyrfirst>
      <bookowner/>
    </bookrights>
  </bookmeta>
  <frontmatter>
    <notices/>
    <booklists>
      <toc/>
    </booklists>
    <preface/>
  </frontmatter>
  <a>
    <b>
      <c/>
    </b>
  </a>
  <appendices/>
  <reltable>
    <relheader>
      <relcolspec/>
      <relcolspec/>
    </relheader>
    <relrow>
      <relcell/>
      <relcell/>
    </relrow>
    <relrow>
      <relcell/>
      <relcell/>
    </relrow>
  </reltable>
</bookmap>
END
 }

if (1) {                                                                        #TditaSampleConcept
  ok Data::Edit::Xml::ditaSampleConcept
   (title=>q(New Concept),
   )->prettyString eq <<END;
<concept id="GUID-a5405560-67e7-0cd1-9188-9a2546d13a37">
  <title id="title">New Concept</title>
  <conbody>
    <p>Please provide the body of this concept using the body keyword</p>
  </conbody>
</concept>
END
 }

if (1) {                                                                        #TditaSampleTask
  ok Data::Edit::Xml::ditaSampleTask
   (title=>q(New Task),
   )->prettyString eq <<END;
<task id="GUID-2ac5c2a6-2475-d5d4-4ce6-d68fdf2102e3">
  <title id="title">New Task</title>
  <taskbody>
    <context/>
    <steps/>
    <result/>
  </taskbody>
</task>
END
 }

if (1) {
  my $a = Data::Edit::Xml::new(<<END);
<a>
  <b>
    c
  </b>
</a>
END

 my ($c, $b) = $a->byList;
 my $B = $b->clone;
 $b->replaceContentWith($B);
 ok nws(-p $a) eq nws(<<END);
<a>
  <b>
    <b>
      c
    </b>
  </b>
</a>
END
 }

if (1) {                                                                        #TputContentAfter
  my $a = Data::Edit::Xml::new(<<END);
<a>
  <b>
    <c/><d/>
  </b>
</a>
END

 my ($c, $d, $b) = $a->byList;

 $b->putContentAfter;
 ok -p $a eq <<END;
<a>
  <b/>
  <c/>
  <d/>
</a>
END
 }

if (1) {                                                                        #TputContentBefore
  my $a = Data::Edit::Xml::new(<<END);
<a>
  <b>
    <c/><d/>
  </b>
</a>
END

 my ($c, $d, $b) = $a->byList;

 $b->putContentBefore;
 ok -p $a eq <<END;
<a>
  <c/>
  <d/>
  <b/>
</a>
END
 }

if (1) {                                                                        #TputSiblingsFirst
  my $a = Data::Edit::Xml::new(<<END);
<a>
  <b/><c/><d/><e/><f/>
</a>
END

 my ($b, $c, $d, $e, $f) = $a->byList;

 $d->putSiblingsFirst;
 ok -p $a eq <<END;
<a>
  <d>
    <b/>
    <c/>
  </d>
  <e/>
  <f/>
</a>
END
 }

if (1) {                                                                        #TputSiblingsLast
  my $a = Data::Edit::Xml::new(<<END);
<a>
  <b/><c/><d/><e/><f/>
</a>
END

 my ($b, $c, $d, $e, $f) = $a->byList;

 $d->putSiblingsLast;
 ok -p $a eq <<END;
<a>
  <b/>
  <c/>
  <d>
    <e/>
    <f/>
  </d>
</a>
END
 }

if (1) {                                                                        #TputSiblingsAfterParent
  my $a = Data::Edit::Xml::new(<<END);
<a>
  <b>
    <c/>d<e/>
  </b>
</a>
END

 my ($c, $d, $e, $b) = $a->byList;

 $d->putSiblingsAfterParent;

 ok -p $a eq <<END;
<a>
  <b>
    <c/>
  </b>
d
  <e/>
</a>
END
 }

if (1) {                                                                        #TputSiblingsBeforeParent
  my $a = Data::Edit::Xml::new(<<END);
<a>
  <b>
    <c/>d<e/>
  </b>
</a>
END

 my ($c, $d, $e, $b) = $a->byList;

 $d->putSiblingsBeforeParent;

 ok -p $a eq <<END;
<a>
  <c/>
d
  <b>
    <e/>
  </b>
</a>
END
 }

if (1) {                                                                        #TditaConvertConceptToTask
  my $a = Data::Edit::Xml::new(<<END);
<concept id="x1">
  <title/>
  <conbody>
    <p>context</p>
    <ol>
      <li> <note>note</note><image/><fig/> <p>cmd</p>  <p>info</p>  </li>
      <li>cmd text<p>info</p>  </li>
    </ol>
    <p>between</p>
    <ol>
      <li>
        <p>cmd <note>first in info</note><note>second in info</note></p>
        <p>info</p>
      </li>
      <li> cmd text <p>info</p> </li>
      <li>
        <ol> <li>substep 1</li> <li>substep 2</li> </ol>
      </li>
    </ol>
    <p>results</p>
  </conbody>
</concept>
END

 $a->ditaConvertConceptToTask;

 ok nws(-p $a) eq nws(<<END);
<task id="x1">
  <title/>
  <taskbody>
    <context>
      <p>context</p>
    </context>
    <steps>
      <step>
        <note>note</note>
        <note>
          <image/>
        </note>
        <note>
          <fig/>
        </note>
        <cmd>cmd</cmd>
        <info>
          <p>info</p>
        </info>
      </step>
      <step>
        <cmd>cmd text</cmd>
        <info>
          <p>info</p>
        </info>
      </step>
      <stepsection>
        <p>between</p>
      </stepsection>
      <step>
        <cmd>cmd </cmd>
        <info>
          <note>first in info</note>
          <note>second in info</note>
          <p>info</p>
        </info>
      </step>
      <step>
        <cmd> cmd text </cmd>
        <info>
          <p>info</p>
        </info>
      </step>
      <step>
        <cmd>Choose one of the following:</cmd>
        <choices>
          <choice>substep 1</choice>
          <choice>substep 2</choice>
        </choices>
      </step>
    </steps>
    <result>
      <p>results</p>
    </result>

  </taskbody>
</task>
END
 }


if (1) {                                                                        #TditaConvertTopicToTask
  my $a = Data::Edit::Xml::new(<<END);
<concept id="c1">
  <title/>
  <conbody>
    <p>results</p>
    <ol><li/>Click</ol>
  </conbody>
</concept>
END

 $a->ditaConvertTopicToTask;

 ok nws(-p $a) eq nws(<<END);
<task id="c1">
  <title/>
  <taskbody>
    <context>
      <p>results</p>
    </context>
    <steps>
      <step>
        <cmd/>
      </step>
Click
    </steps>
    <result/>
  </taskbody>
</task>
END
 }

if (1) {                                                                        #TchangeOrDeleteAttr
  my $a = Data::Edit::Xml::new(<<END);
<a a="1"/>
END

  $a->changeOrDeleteAttr_a_b;

  ok -p $a eq <<END;
<a b="1"/>
END
 }

if (1) {                                                                        #TchangeOrDeleteAttr
  my $a = Data::Edit::Xml::new(<<END);
<a a="1" b="2"/>
END

  $a->changeOrDeleteAttr_a_b;

  ok -p $a eq <<END;
<a b="2"/>
END
 }

if (1) {                                                                        #TchangeOrDeleteAttrValue
  my $a = Data::Edit::Xml::new(<<END);
<a a="1"/>
END

  $a->changeOrDeleteAttrValue_a_0_b_1;

  ok -p $a eq <<END;
<a a="1"/>
END

  $a->changeOrDeleteAttrValue_a_1_b_3;

  ok -p $a eq <<END;
<a b="3"/>
END
 }

if (1) {                                                                        #TchangeOrDeleteAttrValue
  my $a = Data::Edit::Xml::new(<<END);
<a a="1" b="2"/>
END

  $a->changeOrDeleteAttrValue_a_0_b_2;
  ok -p $a eq <<END;
<a b="2"/>
END
 }

if (1) {                                                                        #TchangeOrDeleteAttrValue
  my $a = Data::Edit::Xml::new(<<END);
<a a="1"/>
END

  $a->changeOrDeleteAttrValue_a_1_b_3;

  ok -p $a eq <<END;
<a b="3"/>
END
 }

if (1) {                                                                        #TsplitBefore
  my $a = Data::Edit::Xml::new(<<END);
<a>
  <b>
    <c/>
    <d/>
    <e/>
  </b>
</a>
END

  $a->go_b_d__splitBefore;

  ok -p $a eq <<END;
<a>
  <b>
    <c/>
  </b>
  <b>
    <d/>
    <e/>
  </b>
</a>
END
 }

if (1) {                                                                        #TsplitAfter
  my $a = Data::Edit::Xml::new(<<END);
<a>
  <b>
    <c/>
    <d/>
    <e/>
  </b>
</a>
END

  $a->go_b_d__splitAfter;

  ok -p $a eq <<END;
<a>
  <b>
    <c/>
    <d/>
  </b>
  <b>
    <e/>
  </b>
</a>
END
 }

if (1) {                                                                        #TditaConvertSimpleTableToTable
  my $a = Data::Edit::Xml::new(<<END);
    <simpletable>
      <sthead>
        <stentry>Symbol</stentry>
        <stentry>Meaning</stentry>
      </sthead>
      <strow>
        <stentry>aaaa</stentry>
        <stentry>bbbb</stentry>
      </strow>
    </simpletable>
END

  $a->ditaConvertSimpleTableToTable;

  ok -p $a eq <<END;
<table>
  <tgroup cols="2">
    <colspec colname="c1" colnum="1" colwidth="1*"/>
    <colspec colname="c2" colnum="2" colwidth="1*"/>
    <thead>
      <row>
        <entry>Symbol</entry>
        <entry>Meaning</entry>
      </row>
    </thead>
    <tbody>
      <row>
        <entry>aaaa</entry>
        <entry>bbbb</entry>
      </row>
    </tbody>
  </tgroup>
</table>
END
 }

if (1) {                                                                        #TprintStack
  my $a = Data::Edit::Xml::new(<<END);
<a id="a">
  <b id="b">
    <c>
      <d id="d"/>
    </c>
  </b>
</a>
END

  my $d = $a->firstUntil_d;
  ok $d->printStack eq <<END;
a id="a"
  b id="b"
    c
      d id="d"
END
  ok -S $d eq <<END;
a id="a"
  b id="b"
    c
      d id="d"
END
 }

if (1) {                                                                        #ThtmlTables #TjsonString
  my $a = Data::Edit::Xml::new(<<END);
<task id="t1">
  <title>Title Text</title>
  <taskbody>
    <context>To add a new configuration:</context>
    <steps>
      <step><cmd>Click<b>Next</b>to go to the next page</cmd></step>
      <step><cmd>On the Ports page, complete the following fields</cmd></step>
      <step><cmd>Click to exit without saving</cmd></step>
    </steps>
    <result>
      <p>good!</p>
    </result>
  </taskbody>
</task>
END

  ok stringMd5Sum($a->htmlTables =~ s(#\w+) ()gsr) eq q(1e8555c2ea191a54361cbd8cc5baa94b);
  ok stringMd5Sum($a->jsonString)                  eq q(3afa81dc2b2a249834fbac53a1ebd5f1);
 }

if (1) {                                                                        #TxmlToJson
  my $x = <<END;
<a a="1">t1
  <b b="2" c="3"/>
t2
</a>
END

  ok xmlToJson($x) eq <<'END';
{
   "attributes" : {
      "a" : "1"
   },

   "contents" : [
      {
         "tag" : "CDATA",
         "text" : "t1\n  "
      },

      {
         "attributes" : {
            "b" : "2",
            "c" : "3"
         },

         "tag" : "b"
      },

      {
         "tag" : "CDATA",
         "text" : "\nt2\n"
      }
   ],

   "tag" : "a"
}
END

  ok xmlToJson($x, 1) eq <<'END';
{
   "attributes" : {
      "a" : "1"
   },

   "contents" : [
      {
         "tag" : "CDATA",
         "text" : "t1\\n  "
      },

      {
         "attributes" : {
            "b" : "2",
            "c" : "3"
         },

         "tag" : "b"
      },

      {
         "tag" : "CDATA",
         "text" : "\\nt2\\n"
      }
   ],

   "tag" : "a"
}
END
 }

if (1) {                                                                        # Create a sample json string representing a concept
  my $a = <<END;
<concept id="c1">
  <title>Title text</title>
  <conbody>
    <p>Description of the idea</p>
    <q>An invalid tag</q>
  </conbody>
</concept>
END

  ok xmlToJson($a) eq <<'END';
{
   "attributes" : {
      "id" : "c1"
   },

   "contents" : [
      {
         "contents" : [
            {
               "tag" : "CDATA",
               "text" : "Title text"
            }
         ],

         "tag" : "title"
      },

      {
         "contents" : [
            {
               "contents" : [
                  {
                     "tag" : "CDATA",
                     "text" : "Description of the idea"
                  }
               ],

               "tag" : "p"
            },

            {
               "contents" : [
                  {
                     "tag" : "CDATA",
                     "text" : "An invalid tag"
                  }
               ],

               "tag" : "q"
            }
         ],

         "tag" : "conbody"
      }
   ],

   "tag" : "concept"
}
END

 }

if (1) {
  my $a = Data::Edit::Xml::new(<<END);
<a>
  <b>
    <c/>
    <d/>
  </b>
  <e/>
</a>
END

  my ($c, $d, $b, $e) = $a->byList;
  ok -t $b eq q(b);
  ok -t $d eq q(d);
  $b->moveStartAfter($c);
  ok -p $a eq <<END;
<a>
  <c/>
  <b>
    <d/>
  </b>
  <e/>
</a>
END
 }

if (1) {
  my $a = Data::Edit::Xml::new(<<END);
<a>
  <b>
    <c/>
    <d/>
  </b>
  <e/>
</a>
END

  my ($c, $d, $b, $e) = $a->byList;
  ok -t $b eq q(b);
  ok -t $d eq q(d);
  $b->moveStartAfter($d);
  ok -p $a eq <<END;
<a>
  <c/>
  <d/>
  <b/>
  <e/>
</a>
END
 }

if (1) {
  my $a = Data::Edit::Xml::new(<<END);
<a>
  <b/>
  <c>
    <d/>
  </c>
  <e/>
</a>
END

  my ($b, $d, $c, $e) = $a->byList;
  ok -t $b eq q(b);
  ok -t $c eq q(c);
  ok -t $d eq q(d);
  $c->moveStartBefore($b);
  ok -p $a eq <<END;
<a>
  <c>
    <b/>
    <d/>
  </c>
  <e/>
</a>
END
  $c->moveEndAfter($d);
  ok -p $a eq <<END;
<a>
  <c>
    <b/>
    <d/>
  </c>
  <e/>
</a>
END
  $c->moveEndBefore($e);
  ok -p $a eq <<END;
<a>
  <c>
    <b/>
    <d/>
  </c>
  <e/>
</a>
END
  $c->moveEndAfter($e);
  ok -p $a eq <<END;
<a>
  <c>
    <b/>
    <d/>
    <e/>
  </c>
</a>
END

  $c->moveEndBefore($e);
  ok -p $a eq <<END;
<a>
  <c>
    <b/>
    <d/>
  </c>
  <e/>
</a>
END

  $c->moveEndBefore($b);
  ok -p $a eq <<END;
<a>
  <c/>
  <b/>
  <d/>
  <e/>
</a>
END

  $b->moveEndBefore($d);
  ok -p $a eq <<END;
<a>
  <c/>
  <b/>
  <d/>
  <e/>
</a>
END

  $b->moveEndAfter($d);
  ok -p $a eq <<END;
<a>
  <c/>
  <b>
    <d/>
  </b>
  <e/>
</a>
END

  $b->moveStartAfter($d);
  ok -p $a eq <<END;
<a>
  <c/>
  <d/>
  <b/>
  <e/>
</a>
END

  $b->moveStartAfter($c);
  ok -p $a eq <<END;
<a>
  <c/>
  <b>
    <d/>
  </b>
  <e/>
</a>
END

  $e->moveStartBefore($c);
  ok -p $a eq <<END;
<a>
  <e>
    <c/>
    <b>
      <d/>
    </b>
  </e>
</a>
END
 }

if (1) {
  my $a = Data::Edit::Xml::new(<<END);
<a>
  <b>
    <c/>
  </b>
  <d/>
  <e/>
</a>
END

  my ($c, $b, $d, $e) = $a->byList;
  $b->moveEndAfter($d);
  ok -p $a eq <<END;
<a>
  <b>
    <c/>
    <d/>
  </b>
  <e/>
</a>
END

  $d->moveStartFirst;
  ok -p $a eq <<END;
<a>
  <b>
    <d>
      <c/>
    </d>
  </b>
  <e/>
</a>
END
 }

if (1) {
  my $a = Data::Edit::Xml::new(<<END);
<a>
  <b>
    <c/>
  </b>
  <d/>
  <e/>
</a>
END

  my ($c, $b, $d, $e) = $a->byList;
  $b->moveEndBefore($e);
  ok -p $a eq <<END;
<a>
  <b>
    <c/>
    <d/>
  </b>
  <e/>
</a>
END

  $b->moveStartBefore($d);
  ok -p $a eq <<END;
<a>
  <c/>
  <b>
    <d/>
  </b>
  <e/>
</a>
END

  $b->moveStartFirst;
  ok -p $a eq <<END;
<a>
  <b>
    <c/>
    <d/>
  </b>
  <e/>
</a>
END

  $b->moveEndLast;
  ok -p $a eq <<END;
<a>
  <b>
    <c/>
    <d/>
    <e/>
  </b>
</a>
END

  $c->moveStartFirst;
  ok -p $a eq <<END;
<a>
  <b>
    <c/>
    <d/>
    <e/>
  </b>
</a>
END

  $e->moveEndLast;
  ok -p $a eq <<END;
<a>
  <b>
    <c/>
    <d/>
    <e/>
  </b>
</a>
END

  $b->moveEndBefore($c);
  ok -p $a eq <<END;
<a>
  <b/>
  <c/>
  <d/>
  <e/>
</a>
END
 }

if (1) {                                                                        #TditaConvertConceptToReference #TditaCouldConvertConceptToTask
  my $c = Data::Edit::Xml::new(<<END);
<concept id="c1">
  <title>Title Text</title>
  <conbody>
    <p>Concept 11</p>
    <section>
      <p>A section 11</p>
    </section>
    <section>
      <p>A section 22</p>
    </section>
    <p>Concept 222</p>
  </conbody>
</concept>
END

  my $r = $c->ditaConvertConceptToReference;

  ok -p $c eq <<END;
<reference id="c1">
  <title>Title Text</title>
  <refbody>
    <section>
      <p>Concept 11</p>
    </section>
    <section>
      <p>A section 11</p>
    </section>
    <section>
      <p>A section 22</p>
    </section>
    <section>
      <p>Concept 222</p>
    </section>
  </refbody>
</reference>
END

  my $C = $c->ditaConvertReferenceToConcept;
  ok -p $C eq <<END;
<concept id="c1">
  <title>Title Text</title>
  <conbody>
    <p>Concept 11</p>
    <p>A section 11</p>
    <p>A section 22</p>
    <p>Concept 222</p>
  </conbody>
</concept>
END

  ok !$C->ditaCouldConvertConceptToTask;
 }

if (1) {                                                                        #TditaConvertReferenceToConcept #TditaConvertConceptToTask #TditaCouldConvertConceptToTask
  my $ref = Data::Edit::Xml::new(<<END);
<reference id="GUID-0cab6472-6f2a-e46a-9b6d-16649416ab53">
    <title>Testing the S3 Feature for PM 4 1</title>
    <refbody>
        <section>
            <p>To test the S# feature:</p>
            <ol>
                <li>
                    <p>Set the PCIe link state to L1.2.</p>
                </li>
                <li>
                    <p>Issue the command: PM 4 1 command.</p>
                </li>
            </ol>
        </section>
    </refbody>
</reference>
END

  my $concept = $ref->ditaConvertReferenceToConcept;

  ok -p $concept eq <<END;
<concept id="GUID-0cab6472-6f2a-e46a-9b6d-16649416ab53">
  <title>Testing the S3 Feature for PM 4 1</title>
  <conbody>
    <p>To test the S# feature:</p>
    <ol>
      <li>
        <p>Set the PCIe link state to L1.2.</p>
      </li>
      <li>
        <p>Issue the command: PM 4 1 command.</p>
      </li>
    </ol>
  </conbody>
</concept>
END

  ok $concept->ditaCouldConvertConceptToTask;
  my $task = $concept->ditaConvertConceptToTask;

  ok -p $task eq <<END
<task id="GUID-0cab6472-6f2a-e46a-9b6d-16649416ab53">
  <title>Testing the S3 Feature for PM 4 1</title>
  <taskbody>
    <context>
      <p>To test the S# feature:</p>
    </context>
    <steps>
      <step>
        <cmd>Set the PCIe link state to L1.2.</cmd>
      </step>
      <step>
        <cmd>Issue the command: PM 4 1 command.</cmd>
      </step>
    </steps>
    <result/>
  </taskbody>
</task>
END
 }

if (1) {                                                                        #TditaConvertReferenceToTask
  my $ref = Data::Edit::Xml::new(<<END);
<reference id="GUID-0cab6472-6f2a-e46a-9b6d-16649416ab53">
    <title>Testing the S3 Feature for PM 4 1</title>
    <refbody>
        <section>
            <p>To test the S# feature:</p>
            <ol>
                <li>
                    <p>Set the PCIe link state to L1.2.</p>
                </li>
                <li>
                    <p>Issue the command: PM 4 1 command.</p>
                </li>
            </ol>
        </section>
    </refbody>
</reference>
END

  my $task = $ref->ditaConvertReferenceToTask;

  ok -p $task eq <<END
<task id="GUID-0cab6472-6f2a-e46a-9b6d-16649416ab53">
  <title>Testing the S3 Feature for PM 4 1</title>
  <taskbody>
    <context>
      <p>To test the S# feature:</p>
    </context>
    <steps>
      <step>
        <cmd>Set the PCIe link state to L1.2.</cmd>
      </step>
      <step>
        <cmd>Issue the command: PM 4 1 command.</cmd>
      </step>
    </steps>
    <result/>
  </taskbody>
</task>
END
 }

if (1) {                                                                        #ThasSingleChildText
  my $a = Data::Edit::Xml::new(<<END);
<a>aa
  <b>bb</b>
</a>
END
  ok !$a->hasSingleChildText;
  ok  $a->last->hasSingleChildText;
 }

if (1) {                                                                        #TcutIfEmpty
  my $a = Data::Edit::Xml::new(<<END);
<a>
  <b>bb</b>
  <c/>
  <d></d>
  <e>ee</e>
</a>
END

  my (undef, $b, $c, $d, undef, $e) = $a->byList;
  is_deeply [q(b)..q(e)], [map {-t $_} ($b, $c, $d, $e)];

  $c->cutIfEmpty;
  ok -p $a eq <<END;
<a>
  <b>bb</b>
  <d/>
  <e>ee</e>
</a>
END

  $d->cutIfEmpty;
  ok -p $a eq <<END;
<a>
  <b>bb</b>
  <e>ee</e>
</a>
END

  ok !$_->cutIfEmpty for $a, $b, $e;
 }

if (1) {                                                                        #TbreakOutChild
  my $a = Data::Edit::Xml::new(<<END);
<a>
  <b>
    <c/>
    <d/>
    <e/>
  </b>
</a>
END

  my ($c, $d, $e, $b) = $a->byList;
  $d->breakOutChild;

  ok -p $a eq <<END;
<a>
  <b>
    <c/>
  </b>
  <d/>
  <b>
    <e/>
  </b>
</a>
END
 }

if (1) {                                                                        #TcText
  my $a = Data::Edit::Xml::new(<<END);
<a>
  <b>b1
    <c/>
    b2
    <d/>
    b3
  </b>
  <e>
    <f/>
  </e>
</a>
END

  my (undef, $c, undef, $d, undef, $b, $f, $e) = $a->byList;
  is_deeply ["b".."f"], [map {-t $_} ($b, $c, $d, $e, $f)];

  ok  $b->cText == 3;

  my @b = $b->cText;
  ok "b1 b2 b3" eq join " ", map {trim $_->text} @b;
  ok !$e->cText;
  ok !$a->cText;
 }

if (1) {
  my $a = Data::Edit::Xml::new(<<END);
<a>
  <b>b
    <c/>
    c
    <d/>
    d
  </b>
</a>
END

  my ($B, $c, $C, $d, $D, $b) = $a->byList;

  ok  $B->text =~ m(b)s;
  ok !defined($c->text);

  $_->text = '' for $B, $C, $D;

  ok $a->prettyStringCDATA eq <<END;
<a>
    <b><CDATA></CDATA>
        <c/>
<CDATA></CDATA>
        <d/>
<CDATA></CDATA>
    </b>
</a>
END

  my $A = $a->renew;
  ok $A->prettyStringCDATA eq <<END;
<a>
    <b>
        <c/>
        <d/>
    </b>
</a>
END
 }

if (1) {                                                                        #TswapFirstSibling #TswapLastSibling
  my $a = Data::Edit::Xml::new(<<END);
<a>
  <b/>
  <c/>
  <d/>
  <e/>
  <f/>
  <g/>
</a>
END

  $a->go_f__swapFirstSibling;

  ok -p $a eq <<END;
<a>
  <f/>
  <c/>
  <d/>
  <e/>
  <b/>
  <g/>
</a>
END

  $a->go_c__swapLastSibling;

  ok -p $a eq <<END;
<a>
  <f/>
  <g/>
  <d/>
  <e/>
  <b/>
  <c/>
</a>
END
 }

if (1) {                                                                        #TswapNext #TswapPrev
  my $a = Data::Edit::Xml::new(<<END);
<a>
  <b>
    <c>
      <d/>
      <e/>
    </c>
    <f>
      <g/>
    </f>
  </b>
</a>
END

  my ($d, $e, $c, $g, $f, $b) = $a->byList;

  ok -t $b eq q(b);

  $d->swapNext; $g->swapPrev;
  ok $d->after($e);
  ok -p $a eq <<END;
<a>
  <b>
    <c>
      <e/>
      <d/>
    </c>
    <f>
      <g/>
    </f>
  </b>
</a>
END

  $d->swapPrev; $c->swapNext;
  ok $f->before($c);
  ok -p $a eq <<END;
<a>
  <b>
    <f>
      <g/>
    </f>
    <c>
      <d/>
      <e/>
    </c>
  </b>
</a>
END
 }


if (1) {                                                                        #TsplitTo
  my $a = Data::Edit::Xml::new(<<END);
<a>
  <b>
    <c>
      <d>
        <e/>
      </d>
    </c>
  </b>
</a>
END

  my ($e, $d, $c, $b) = $a->byList;

  ok !$a->splitTo($a);
  ok !$b->splitTo($a);
  ok  $e->splitTo($b);

  ok -p $a eq <<END;
<a>
  <b>
    <c>
      <d/>
    </c>
  </b>
  <e/>
  <b>
    <c>
      <d/>
    </c>
  </b>
</a>
END
 }

if (1) {                                                                        #TadoptChild #TzipDown
  my $a = Data::Edit::Xml::new(<<END);
<a>
  <b>
    <c>
      <d>
        <e/>
      </d>
    </c>
  </b>
</a>
END

  my ($e, $d, $c, $b) = $a->byList;

  $a->adoptChild($e);
  ok -p $a eq <<END;
<a>
  <b>
    <c>
      <d/>
    </c>
  </b>
  <e/>
  <b>
    <c>
      <d/>
    </c>
  </b>
</a>
END

  $e->zipDown;
  ok -p $a eq <<END;
<a>
  <b>
    <c>
      <d>
        <e/>
      </d>
    </c>
  </b>
</a>
END

 }

if (1) {                                                                        #TsplitTo #TzipDownOnce #TzipDown
  my $a = Data::Edit::Xml::new(<<END);
<a>
  <b>
    <c>
      <d>
        <e/>
      </d>
    </c>
  </b>
</a>
END

  my ($e, $d, $c, $b) = $a->byList;

  ok !$a->splitTo($a);
  ok !$b->splitTo($a);
  ok  $e->splitTo($b);

  ok -p $a eq <<END;
<a>
  <b>
    <c>
      <d/>
    </c>
  </b>
  <e/>
  <b>
    <c>
      <d/>
    </c>
  </b>
</a>
END

  ok $e->zipDownOnce;                                                           # Invalidates b
  ok -p $a eq <<END;
<a>
  <b>
    <c>
      <d/>
    </c>
    <e/>
    <c>
      <d/>
    </c>
  </b>
</a>
END

 ok  $e->splitTo($a->first);                                                    # b invalidated by zipDownOnce
 $e->zipDown;

 ok -p $a eq <<END;
<a>
  <b>
    <c>
      <d>
        <e/>
      </d>
    </c>
  </b>
</a>
END
 }

if (1) {                                                                        #TjsonToXml
  my $a = Data::Edit::Xml::new(<<END);
<a id="a">
  <b id="b" out="out">b1
    <c id="c"/>
    b2
    <d id="d"/>
    b3
  </b>
</a>
END

  my                $json = $a->jsonString;
  ok   stringMd5Sum($json) eq q(5bb9140c7076978dfd5f600d68a82164);
  ok -p jsonToXml  ($json) eq <<END;
<a id="a">
  <b id="b" out="out">b1
    <c id="c"/>
b2
    <d id="d"/>
b3
  </b>
</a>
END
 }

if (1) {                                                                        #TditaConvertSectionToConcept #TditaConvertConceptToSection
  my $a = Data::Edit::Xml::new(<<END);
<section id="a">
  <title>title</title>
  <p>text</p>
</section>
END

  ok -p $a->ditaConvertSectionToConcept eq <<END;
<concept id="a">
  <title>title</title>
  <conbody>
    <p>text</p>
  </conbody>
</concept>
END

  ok -p $a->ditaConvertConceptToSection eq <<END;
<section id="a">
  <title>title</title>
  <p>text</p>
</section>
END
 }

if (1) {                                                                        #TditaConvertSectionToReference
  my $a = Data::Edit::Xml::new(<<END);
<section id="a">
  <title>title</title>
  <p>text</p>
</section>
END

  ok -p $a->ditaConvertSectionToReference eq <<END
<reference id="a">
  <title>title</title>
  <refbody>
    <section>
      <p>text</p>
    </section>
  </refbody>
</reference>
END
 }

if (1) {                                                                        #TditaConvertSectionToTask
  my $a = Data::Edit::Xml::new(<<END);
<section id="a">
  <title>title</title>
  <ol>
    <li>step1</li>
    <li>step2</li>
  </ol>
</section>
END

  ok -p $a->ditaConvertSectionToTask eq <<END
<task id="a">
  <title>title</title>
  <taskbody>
    <context/>
    <steps>
      <step>
        <cmd>step1</cmd>
      </step>
      <step>
        <cmd>step2</cmd>
      </step>
    </steps>
    <result/>
  </taskbody>
</task>
END
 }

if (1) {                                                                        #TditaConvertDlToUl

  my $a = Data::Edit::Xml::new(<<END);
<dl>
  <dlentry><dt>A</dt><dt>B</dt><dt>C</dt></dlentry>
  <dlentry><dd>a</dd><dd>b</dd><dd>b</dd></dlentry>
</dl>
END

  $a->ditaConvertDlToUl;

  ok -p $a eq <<END;
<ul>
  <li>A</li>
  <li>B</li>
  <li>C</li>
  <li>a</li>
  <li>b</li>
  <li>b</li>
</ul>
END

  my $b = Data::Edit::Xml::new(<<END);
<dl>
  <dlentry><dt>A</dt><dd>a</dd></dlentry>
</dl>
END

  ok !$a->ditaConvertDlToUl;
 }

if (1) {                                                                        #TdownToDie

  my $a = Data::Edit::Xml::new(<<END);
<a>
  <b>
    <c/>
  </b>
  <d>
    <e/>
  </d>
</a>
END

  my @a;
  $a->downToDie(sub
   {confess if -t $_ eq q(b);
    push @a, -t $_;
   });

  is_deeply [@a], [qw(a d e)];

  my @b;
  $a->downToDie(sub
   {confess if -t $_ eq q(c);
    push @b, -t $_;
   });

  is_deeply [@b], [qw(a b d e)];
 }

if (1) {                                                                        #TsplitAndWrapToEnd #TsplitAndWrapFromStart

  my $A = Data::Edit::Xml::new(<<END);
<a>
  <PP/>
  <b>
    <c/>
  </b>
  <QQ/>
  <b>
    <d/>
  </b>
  <RR/>
  <b>
    <e/>
  </b>
  <SS/>
</a>
END

  my $a = $A->clone;
  ok 3 == $a->splitAndWrapFromStart(qw(b B));

  ok -p $a eq <<END;
<a>
  <B>
    <PP/>
    <b>
      <c/>
    </b>
  </B>
  <B>
    <QQ/>
    <b>
      <d/>
    </b>
  </B>
  <B>
    <RR/>
    <b>
      <e/>
    </b>
  </B>
  <SS/>
</a>
END


  my $b = $A->clone;
  ok 3 == $b->splitAndWrapToEnd(qw(b B));

  ok -p $b eq <<END;
<a>
  <PP/>
  <B>
    <b>
      <c/>
    </b>
    <QQ/>
  </B>
  <B>
    <b>
      <d/>
    </b>
    <RR/>
  </B>
  <B>
    <b>
      <e/>
    </b>
    <SS/>
  </B>
</a>
END
 }

if (1) {                                                                        #TdivideHtmlDocumentIntoSections

  my $a = Data::Edit::Xml::new(<<END);
<a>
  <h1>HH11</h1>    text1
    <h2>HH22</h2>  text2
    <h2>HH22</h2>  text3
      <h4>HH44</h4>text4
    <h1>HH11</h1>  text5
</a>
END

  $a->divideHtmlDocumentIntoSections;

  ok nws(-p $a) eq nws(<<END);
<a>
  <section outputclass="1">
    <title>HH11</title>
text1
    <section outputclass="2">
      <title>HH22</title>
text2
    </section>
    <section outputclass="2">
      <title>HH22</title>
text3
      <section outputclass="4">
        <title>HH44</title>
text4
      </section>
    </section>
  </section>
  <section outputclass="1">
    <title>HH11</title>
text5
  </section>
</a>
END
 }

if (1) {                                                                        #TditaConvertFromHtmlDl
  my $a = Data::Edit::Xml::new(<<END);
<dl>
  <dt>t11</dt>
  <dd>d11</dd>
  <dd>d12</dd>
  <dt>t21</dt>
  <dd>d21</dd>
  <dd>d22</dd>
  <dt>t31</dt>
  <dd>d31</dd>
  <dd>d32</dd>
</dl>
END

  $a->ditaConvertFromHtmlDl;

  ok -p $a eq <<END;
<dl>
  <dlentry>
    <dt>t11</dt>
    <dd>d11</dd>
    <dd>d12</dd>
  </dlentry>
  <dlentry>
    <dt>t21</dt>
    <dd>d21</dd>
    <dd>d22</dd>
  </dlentry>
  <dlentry>
    <dt>t31</dt>
    <dd>d31</dd>
    <dd>d32</dd>
  </dlentry>
</dl>
END
 }

if (1) {                                                                        #TmoveBlockFirst
  my $x = Data::Edit::Xml::new(<<END);
<x>
  <a/>
  <b/>
  <c/>
  <d/>
  <e/>
  <f>
    <g/>
  </f>
  <h/>
</x>
END
  my ($a, $b, $c, $d, $e, $g, $f, $h) = $x->byList;

  $c->moveBlockFirst($d, $f, qw(c x));

  ok -p $x eq <<END;
<x>
  <a/>
  <b/>
  <e/>
  <f>
    <c/>
    <d/>
    <g/>
  </f>
  <h/>
</x>
END
 }

if (1) {                                                                        #TmoveBlockLast
  my $x = Data::Edit::Xml::new(<<END);
<x>
  <a/>
  <b/>
  <c/>
  <d/>
  <e/>
  <f>
    <g/>
  </f>
  <h/>
</x>
END
  my ($a, $b, $c, $d, $e, $g, $f, $h) = $x->byList;

  $c->moveBlockLast($d, $f, qw(c x));

  ok -p $x eq <<END;
<x>
  <a/>
  <b/>
  <e/>
  <f>
    <g/>
    <c/>
    <d/>
  </f>
  <h/>
</x>
END
 }

if (1) {                                                                        #TmoveBlockAfter
  my $x = Data::Edit::Xml::new(q(<x><a/><b/><c/><d/><e/><f/><g/><h/></x>));

  my ($a, $b, $c, $d, $e, $f, $g, $h) = $x->byList;

  $c->moveBlockAfter($d, $f, qw(c x));

  ok -C $x eq q(<a/><b/><e/><f/><c/><d/><g/><h/>);
 }

if (1) {                                                                        #TmoveBlockBefore
  my $x = Data::Edit::Xml::new(q(<x><a/><b/><c/><d/><e/><f/><g/><h/></x>));

  my ($a, $b, $c, $d, $e, $f, $g, $h) = $x->byList;

  $f->moveBlockBefore($g, $b, qw(f x));

  ok -C $x eq q(<a/><f/><g/><b/><c/><d/><e/><h/>);
 }

if (1) {                                                                        #TmoveBlockToLastFirst
  my $x = Data::Edit::Xml::new(<<END);
<x>
  <a/>
  <b/>
  <c/>
</x>
END
  my ($a, $b, $c) = $x->byList;

  $b->moveBlockToLastFirst($a);

  ok -p $x eq <<END;
<x>
  <a>
    <b/>
    <c/>
  </a>
</x>
END
 }

if (1) {                                                                        #TmoveBlockToLastLast
  my $x = Data::Edit::Xml::new(<<END);
<x>
  <a>
    <b/>
    <c/>
    <d/>
  </a>
  <e>
    <f/>
    <g/>
    <h/>
  </e>
</x>
END
  my ($b, $c, $d, $a, $f, $g, $h, $e) = $x->byList;

  $c->moveBlockToLastLast($e);

  ok -p $x eq <<END;
<x>
  <a>
    <b/>
  </a>
  <e>
    <f/>
    <g/>
    <h/>
    <c/>
    <d/>
  </e>
</x>
END
 }

if (1) {                                                                        #TmoveBlockToLastAfter
  my $x = Data::Edit::Xml::new(<<END);
<x>
  <a>
    <b/>
    <c/>
    <d/>
  </a>
  <e/>
</x>
END
  my ($b, $c, $d, $a, $e) = $x->byList;

  $c->moveBlockToLastAfter($e);

  ok -p $x eq <<END;
<x>
  <a>
    <b/>
  </a>
  <e/>
  <c/>
  <d/>
</x>
END
 }

if (1) {                                                                        #TmoveBlockToLastBefore
  my $x = Data::Edit::Xml::new(<<END);
<x>
  <a/>
  <b>
    <c/>
    <d/>
    <e/>
  </b>
</x>
END
  my ($a, $c, $d, $e, $b) = $x->byList;

  $d->moveBlockToLastBefore($b);

  ok -p $x eq <<END;
<x>
  <a/>
  <d/>
  <e/>
  <b>
    <c/>
  </b>
</x>
END
 }

if (1) {                                                                        #TmoveBlockFromFirstFirst
  my $x = Data::Edit::Xml::new(<<END);
<x>
  <a>
    <b/>
    <c/>
    <d/>
  </a>
  <e>
    <f/>
    <g/>
    <h/>
  </e>
</x>
END
  my ($b, $c, $d, $a, $f, $g, $h, $e) = $x->byList;

  $g->moveBlockFromFirstFirst($a);

  ok -p $x eq <<END;
<x>
  <a>
    <f/>
    <g/>
    <b/>
    <c/>
    <d/>
  </a>
  <e>
    <h/>
  </e>
</x>
END
 }

if (1) {                                                                        #TmoveBlockFromFirstLast
  my $x = Data::Edit::Xml::new(<<END);
<x>
  <a/>
  <b/>
  <c/>
</x>
END
  my ($a, $b, $c) = $x->byList;

  $b->moveBlockFromFirstLast($c);

  ok -p $x eq <<END;
<x>
  <c>
    <a/>
    <b/>
  </c>
</x>
END
 }

if (1) {                                                                        #TmoveBlockFromFirstAfter
  my $x = Data::Edit::Xml::new(<<END);
<x>
  <a>
    <b/>
    <c/>
    <d/>
  </a>
  <e/>
</x>
END
  my ($b, $c, $d, $a, $e) = $x->byList;

  $c->moveBlockFromFirstAfter($e);

  ok -p $x eq <<END;
<x>
  <a>
    <d/>
  </a>
  <e/>
  <b/>
  <c/>
</x>
END
 }

if (1) {                                                                        #TmoveBlockFromFirstBefore
  my $x = Data::Edit::Xml::new(<<END);
<x>
  <a/>
  <b>
    <c/>
    <d/>
    <e/>
  </b>
</x>
END
  my ($a, $c, $d, $e, $b) = $x->byList;

  $d->moveBlockFromFirstBefore($b);

  ok -p $x eq <<END;
<x>
  <a/>
  <c/>
  <d/>
  <b>
    <e/>
  </b>
</x>
END
 }

if (1) {                                                                        #TrenameAttrXtr
  my $a = Data::Edit::Xml::new(q(<a a="1" b="2" c="3" d="4"/>));
  my @a = $a->renameAttrXtr_a_b_c;
  ok      -A $a eq q(a c="3" d="4" xtrc="1" xtrf="2");
  is_deeply [@a], [qw(c)];

  my $b = Data::Edit::Xml::new(q(<b a="1" b="2" c="3"  d="4" xtrf="5"/>));
  my @b = $b->renameAttrXtr_a_b_c;
  ok      -A $b eq q(b b="2" c="3" d="4" xtrc="1" xtrf="5");
  is_deeply [@b], [qw(b c)];
 }

if (1) {                                                                        #TunwrapParentOfOnlyChild
  my $a = Data::Edit::Xml::new(<<END);
<a>
  <b>
    <c/>
  </b>
</a>
END

  $a->go_b_c->unwrapParentOfOnlyChild;

  ok -p $a eq <<END;
<a>
  <c/>
</a>
END
 }

if (1) {                                                                        #TunwrapOnlyChild
  my $a = Data::Edit::Xml::new(<<END);
<a>
  <b>
    <c/>
  </b>
  <d/>
</a>
END

  $a->go_b->unwrapOnlyChild;

  ok -p $a eq <<END;
<a>
  <b>
    <c/>
  </b>
  <d/>
</a>
END

  $a->go_b_c->unwrapOnlyChild;

  ok -p $a eq <<END;
<a>
  <b/>
  <d/>
</a>
END
 }

if (1) {                                                                        #TditaSyntaxDiagramFromDocBookCmdSynopsis #TditaSyntaxDiagramToBasicRepresentation
  my $a = Data::Edit::Xml::new(<<END);
<cmdsynopsis>
  <command>fs</command>
  <arg choice="plain">list</arg>
  <group choice="opt">
    <arg choice="plain">--pending</arg>
    <arg choice="plain">--running</arg>
  </group>
  <group choice="opt">
    <arg choice="plain">--online</arg>
    <arg choice="plain">
      --offline
      <group choice="req">
        <arg choice="plain">http</arg>
        <arg choice="plain">https</arg>
        <arg choice="plain">ftp</arg>
        <arg choice="plain">ssh</arg>
      </group>
    </arg>
    <arg choice="plain">--special</arg>
    <arg choice="plain">--snap</arg>
  </group>
  <group choice="opt">
    <arg choice="plain">--real</arg>
    <arg choice="plain">--virtual</arg>
    <arg choice="plain">--absolute</arg>
  </group>
  <arg choice="opt">--titled</arg>
  <group choice="opt">
    <arg choice="plain">--total</arg>
    <arg choice="plain">--space</arg>
  </group>
  <arg choice="opt">
    --rename
    <replaceable>new-name</replaceable>
  </arg>
  <arg choice="opt">
    --sort
    <replaceable>sort procedure</replaceable>
  </arg>
  <arg choice="opt">--raw</arg>
  <arg choice="opt">
    --limit
    <replaceable>limits</replaceable>
  </arg>
  <arg choice="opt">--page</arg>
  <arg choice="opt" rep="repeat">
    <replaceable>files</replaceable>
  </arg>
</cmdsynopsis>
END

  $a->ditaSyntaxDiagramFromDocBookCmdSynopsis;

  ok -p $a eq <<END;
<syntaxdiagram>
  <groupseq>
    <kwd outputclass="command">fs</kwd>
    <kwd>list</kwd>
    <groupchoice importance="optional">
      <kwd>--pending</kwd>
      <kwd>--running</kwd>
    </groupchoice>
    <groupchoice importance="optional">
      <kwd>--online</kwd>
      <groupseq>
        <kwd>
      --offline
      </kwd>
        <groupchoice>
          <kwd>http</kwd>
          <kwd>https</kwd>
          <kwd>ftp</kwd>
          <kwd>ssh</kwd>
        </groupchoice>
      </groupseq>
      <kwd>--special</kwd>
      <kwd>--snap</kwd>
    </groupchoice>
    <groupchoice importance="optional">
      <kwd>--real</kwd>
      <kwd>--virtual</kwd>
      <kwd>--absolute</kwd>
    </groupchoice>
    <kwd importance="optional">--titled</kwd>
    <groupchoice importance="optional">
      <kwd>--total</kwd>
      <kwd>--space</kwd>
    </groupchoice>
    <groupseq importance="optional">
      <kwd>
    --rename
    </kwd>
      <var>new-name</var>
    </groupseq>
    <groupseq importance="optional">
      <kwd>
    --sort
    </kwd>
      <var>sort procedure</var>
    </groupseq>
    <kwd importance="optional">--raw</kwd>
    <groupseq importance="optional">
      <kwd>
    --limit
    </kwd>
      <var>limits</var>
    </groupseq>
    <kwd importance="optional">--page</kwd>
    <groupseq importance="optional">
      <var>files</var>
      <sep>…</sep>
    </groupseq>
  </groupseq>
</syntaxdiagram>
END

  $a->ditaSyntaxDiagramToBasicRepresentation;

  ok nws(-p $a) eq nws(<<END);
<p>
  <cmdname>fs</cmdname>
  <codeph>list</codeph>

 [ <codeph>--pending</codeph> | <codeph>--running</codeph> ]

 [ <codeph>--online</codeph> |
   <codeph> --offline </codeph> { <codeph>http</codeph> | <codeph>https</codeph>  | <codeph>ftp</codeph>  | <codeph>ssh</codeph> }  |
   <codeph>--special</codeph> |
   <codeph>--snap</codeph>
 ]

 [ <codeph>--real</codeph> |
   <codeph>--virtual</codeph> |
   <codeph>--absolute</codeph>
 ]

 [ <codeph>--titled</codeph> ]
 [ <codeph>--total</codeph>  |  <codeph>--space</codeph> ]
 [ <codeph> --rename </codeph>  <userinput>new-name</userinput> ]
 [ <codeph> --sort </codeph>    <userinput>sort procedure</userinput> ]
 [ <codeph>--raw</codeph> ]
 [ <codeph> --limit </codeph>     <userinput>limits</userinput> ]
 [ <codeph>--page</codeph>  ]
 [ <userinput>files</userinput> <codeph>…</codeph> ]
</p>
END
 }

if (1) {
  my $a = Data::Edit::Xml::new(<<END);
<cmdsynopsis>
  <command>pureadmin</command>
  <arg choice="plain">refresh</arg>
  <arg choice="req">--clear</arg>
  <arg choice="req" rep="repeat"><replaceable>USER</replaceable></arg>
</cmdsynopsis>
END

  $a->ditaSyntaxDiagramFromDocBookCmdSynopsis;

  ok -p $a eq <<END;
<syntaxdiagram>
  <groupseq>
    <kwd outputclass="command">pureadmin</kwd>
    <kwd>refresh</kwd>
    <kwd>--clear</kwd>
    <groupchoice>
      <groupseq>
        <var>USER</var>
        <sep>…</sep>
      </groupseq>
    </groupchoice>
  </groupseq>
</syntaxdiagram>
END

  $a->ditaSyntaxDiagramToBasicRepresentation;

  ok nws(-p $a) eq nws(<<END);
<p>
  <cmdname>pureadmin</cmdname>
  <codeph>refresh</codeph>
  <codeph>--clear</codeph>
 {
  <userinput>USER</userinput>
  <codeph>…</codeph>
}
</p>
END
 }

if (1) {                                                                        #TditaGetConRef #TditaReplaceConref #TditaExpandAllConRefs #TditaReplaceAnyConref
  my $dir = temporaryFolder;
  my $a = Data::Edit::Xml::new(<<END);
<a>
  <b id="b">
    <c/>
  </b>
</a>
END

  my $target = fpe($dir, qw(target xml));
  owf($target, -p $a);

  my $b = ditaGetConRef("target.xml#a/b", $dir);
  ok -p $b eq <<END;
<b id="b">
  <c/>
</b>
END

  my $A = Data::Edit::Xml::new(<<END);
<a>
  <b id="B" conref="target.xml#a/b"/>
</a>
END
  my $target2 = fpe($dir, qw(target2 xml));
  owf($target2, -p $A);

  my $B = Data::Edit::Xml::new(<<END);
<a>
  <b id="C" conref="target2.xml#a/B"/>
</a>
END

  $B->ditaExpandAllConRefs($dir);
  ok -p $B eq <<END;
<a>
  <b id="C">
    <c/>
  </b>
</a>
END

  my $CF = owf(fpe($dir, qw(source xml)), <<END);
<a>
  <b id="D" conref="target2.xml#a/B"/>
</a>
END

  my $C = Data::Edit::Xml::new($CF);

  $C->first__ditaReplaceAnyConref;
  ok -p $C eq <<END;
<a>
  <b id="D">
    <c/>
  </b>
</a>
END

  my $DF = owf(fpe($dir, qw(source xml)), <<END);
<a id="E" conref="target2.xml#a/B"/>
END

  my $D = Data::Edit::Xml::new($DF);

  $D->ditaReplaceAnyConref;
  ok -p $D eq <<END;
<b id="E">
  <c/>
</b>
END

  my $EF = owf(fpe($dir, qw(source xml)), <<END);
<a id="F" conref="target2.xml"/>
END

  my $E = Data::Edit::Xml::new($EF);

  $E->ditaReplaceAnyConref;

  ok -p $E eq <<END;
<a id="F">
  <b id="B">
    <c/>
  </b>
</a>
END

  clearFolder($dir, 3);
 }

if (1) {                                                                        #TditaReplaceAnyConrefIdeallyWithMatchingTag #TditaReplaceAnyConrefInContext
  my $dir = temporaryFolder;
  my $target = Data::Edit::Xml::new(<<END);
<a>
  <c id="b"/>
  <b id="b"/>
</a>
END

  owf(fpe($dir, qw(target xml)), -p $target);

  my $s = Data::Edit::Xml::new(q(<a conref="target.xml#a/b"/>));
  $s->inputFile = fpe($dir, qw(source xml));

  my $s1 = $s->clone->change_c;
     $s1->ditaReplaceAnyConrefIdeallyWithMatchingTag;
  ok -p $s1 eq <<END;
<c id="b"/>
END

  my  $s2 = $s->clone->change_b;
  ok !$s2->ditaReplaceAnyConrefInContext(qr(\Ac A\Z));
      $s2->ditaReplaceAnyConrefInContext(qr(\Ac a\Z));
  ok -p $s2 eq <<END;
<c id="b"/>
END

  my $b = ditaGetConRef("target.xml#a/b", $dir, qr(\Ab\Z));
  ok -p $b eq <<END;
<b id="b"/>
END

  my $c = ditaGetConRef("target.xml#a/b", $dir, qr(\Ac a\Z));
  ok -p $c eq <<END;
<c id="b"/>
END

  my $d = ditaGetConRef("target.xml#a/b", $dir, qr(\Ad\Z));
  ok -p $c eq <<END;
<c id="b"/>
END

  ok !ditaGetConRef("target.xml#a/b", $dir, qr(\A(d|e)\Z), 1);

  clearFolder($dir, 3);
 }

if (1)
 {my $a = Data::Edit::Xml::new(<<END);
<table>
<tgroup cols="0">
  <tbody>
    <row>
      <entry nameend="c3" namest="c1">
        <p>Connect a
          <uicontrol>PL</uicontrol>
 (6G) or
          <uicontrol>HL</uicontrol>
 (12G) cable pair from
          <uicontrol>CT0</uicontrol>
 to
          <uicontrol>SH0</uicontrol>.
Following are the port numbers:
        </p>
        <p>
          <table>
            <tgroup cols="4">
              <colspec colname="c1" colnum="1" colwidth="1*"/>
              <colspec colname="c2" colnum="2" colwidth="1*"/>
              <colspec colname="c3" colnum="3" colwidth="1*"/>
              <colspec colname="c4" colnum="4" colwidth="1*"/>
              <thead>
                <row>
                  <entry>
                    <p>Cable Type</p>
                  </entry>
                  <entry>
                    <p>SH0 Port</p>
                  </entry>
                  <entry>
                    <p>CT0 Port</p>
                  </entry>
                  <entry>
                    <p>Symbol</p>
                  </entry>
                </row>
              </thead>
              <tbody>
                <row>
                  <entry>
                    <p>PL/HL</p>
                  </entry>
                  <entry>
                    <p> 0 </p>
                  </entry>
                  <entry>
                    <p>SAS2</p>
                  </entry>
                  <entry>
                    <image href="png_2d66ccb0bdc06f386ff4680eb650dde3.png"/>
                  </entry>
                </row>
                <row>
                  <entry>
                    <p>PL/HL</p>
                  </entry>
                  <entry>
                    <p>3</p>
                  </entry>
                  <entry>
                    <p>SAS0</p>
                  </entry>
                  <entry>
                    <fig>
                      <image href="png_241ea299eacf7994812863bfe556863d.png"/>
                    </fig>
                  </entry>
                </row>
              </tbody>
            </tgroup>
          </table>
        </p>
        <p>If the array has two or more shelves, connect a
          <uicontrol>QR</uicontrol>
 (6G) or
          <uicontrol>IR</uicontrol>
 (12G) cable pair from
          <uicontrol>CT0</uicontrol>
 to
          <uicontrol>SH1</uicontrol>.
Following are the port numbers:
        </p>
        <p>
          <table>
            <tgroup cols="4">
              <colspec colname="c1" colnum="1" colwidth="1*"/>
              <colspec colname="c2" colnum="2" colwidth="1*"/>
              <colspec colname="c3" colnum="3" colwidth="1*"/>
              <colspec colname="c4" colnum="4" colwidth="1*"/>
              <thead>
                <row>
                  <entry>
                    <p>Cable Type</p>
                  </entry>
                  <entry>
                    <p>SH1 Port</p>
                  </entry>
                  <entry>
                    <p>CT0 Port</p>
                  </entry>
                  <entry>
                    <p>Symbol</p>
                  </entry>
                </row>
              </thead>
              <tbody>
                <row>
                  <entry>
                    <p>QR/IR</p>
                  </entry>
                  <entry>
                    <p>2</p>
                  </entry>
                  <entry>
                    <p>SAS1</p>
                  </entry>
                  <entry>
                    <fig>
                      <image href="png_0a0ac3f3832e5f2b9939d30cd97eb2c1.png"/>
                    </fig>
                  </entry>
                </row>
                <row>
                  <entry>
                    <p>QR/IR</p>
                  </entry>
                  <entry>
                    <p>5</p>
                  </entry>
                  <entry>
                    <p>SAS3</p>
                  </entry>
                  <entry>
                    <fig>
                      <image href="png_dd09680b7b776f9090b593014fe2b801.png"/>
                    </fig>
                  </entry>
                </row>
              </tbody>
            </tgroup>
          </table>
        </p>
        <p>Pull each connector gently to make sure the latch is engaged.</p>
      </entry>
      <entry>
        <fig>
          <image href="png_a9d7eda941c6ff1a5c6101ef19b42abe.png"/>
        </fig>
        <p>Connecting SAS Cables to
          <ph conref="../mcvars.dita#mcvars/variableId4"/>
 CT0
        </p>
      </entry>
    </row>
  </tbody>
</tgroup>
</table>
END

 $a->fixTable;
 }

if (1) {                                                                        #TisFirstN #TisLastN #TnextN #TprevN
  my $x = Data::Edit::Xml::new(<<END);
<x>
  <a/>
  <b/>
  <c/>
  <d/>
  <e/>
</x>
END

  my ($a, $b, $c, $d, $e) = $x->byList;

  is_deeply [$b->isFirstN_2_a_b_x], [$a, $b];
  ok !$b->isFirstN_2_b_x;

  is_deeply [$d->isLastN_2_d_e_x], [$d, $e];
  ok !$d->isLastN_2_d_x;

  is_deeply [$b->nextN_3_b_c_d], [$b, $c, $d];
  is_deeply [$d->prevN_3_b_c_d], [$b, $c, $d];
 }

if (1) {                                                                        #TputNextFirstCut
  my $a = Data::Edit::Xml::new(<<END);
<a>
  <b/>
  <c>
    <d/>
  </c>
</a>
END

  my ($b, $d, $c) = $a->byList;

  ok !$b->putNextFirstCut_c_x;

  $b->putNextFirstCut_c_a;
  ok  -p $a eq <<END;
<a>
  <c>
    <b/>
    <d/>
  </c>
</a>
END
 }

if (1) {                                                                        #TputNextFirstCut2
  my $a = Data::Edit::Xml::new(<<END);
<a>
  <b/>
  <c>
    <d/>
  </c>
</a>
END

  my ($b, $d, $c) = $a->byList;

  ok !$b->putNextFirstCut2_c_a;

  $b->putNextFirstCut2_d_c_a;

  ok  -p $a eq <<END;
<a>
  <c>
    <d>
      <b/>
    </d>
  </c>
</a>
END
 }

if (1) {                                                                        #TputPrevLastCut
  my $a = Data::Edit::Xml::new(<<END);
<a>
  <b>
    <c/>
  </b>
   <d/>
</a>
END

  my ($c, $b, $d) = $a->byList;

  ok !$d->putPrevLastCut_b_c;

  $d->putPrevLastCut_b_a;
  ok  -p $a eq <<END;
<a>
  <b>
    <c/>
    <d/>
  </b>
</a>
END
 }

if (1) {                                                                        #TputPrevLastCut2
  my $a = Data::Edit::Xml::new(<<END);
<a>
  <b>
    <c/>
  </b>
   <d/>
</a>
END

  my ($c, $b, $d) = $a->byList;

  ok !$d->putPrevLastCut2_c_a;

  $d->putPrevLastCut2_c_b_a;
  ok  -p $a eq <<END;
<a>
  <b>
    <c>
      <d/>
    </c>
  </b>
</a>
END
 }

if (1) {                                                                        #TwrapNext
  my $a = Data::Edit::Xml::new(<<END);
<a>
  <b/>
  <c/>
  <d/>
</a>
END

  my ($b, $c, $d) = $a->byList;

  $b->wrapNext_e;
  ok  -p $a eq <<END;
<a>
  <e>
    <b/>
    <c/>
  </e>
  <d/>
</a>
END
 }

if (1) {                                                                        #TwrapPrev
  my $a = Data::Edit::Xml::new(<<END);
<a>
  <b/>
  <c/>
  <d/>
</a>
END

  my ($b, $c, $d) = $a->byList;

  $c->wrapPrev_e;
  ok  -p $a eq <<END;
<a>
  <e>
    <b/>
    <c/>
  </e>
  <d/>
</a>
END
 }

if (1) {                                                                        #TwrapFirstN
  my $a = Data::Edit::Xml::new(<<END);
<a>
  <b/>
  <c/>
  <d/>
</a>
END

  $a->wrapFirstN_2_Z;

  ok -p $a eq <<END;
<a>
  <Z>
    <b/>
    <c/>
  </Z>
  <d/>
</a>
END
 }

if (1) {                                                                        #TwrapLastN
  my $a = Data::Edit::Xml::new(<<END);
<a>
  <b/>
  <c/>
  <d/>
</a>
END

  $a->wrapLastN_2_Z;

  ok -p $a eq <<END;
<a>
  <b/>
  <Z>
    <c/>
    <d/>
  </Z>
</a>
END
 }

if (1) {                                                                        #TwrapNextN
  my $a = Data::Edit::Xml::new(<<END);
<a>
  <b/>
  <c/>
  <d/>
  <e/>
</a>
END

  $a->go_c__wrapNextN_2_Z;

  ok -p $a eq <<END;
<a>
  <b/>
  <Z>
    <c/>
    <d/>
  </Z>
  <e/>
</a>
END
 }

if (1) {                                                                        #TwrapPrevN
  my $a = Data::Edit::Xml::new(<<END);
<a>
  <b/>
  <c/>
  <d/>
  <e/>
</a>
END

  $a->go_d__wrapPrevN_2_Z;

  ok -p $a eq <<END;
<a>
  <b/>
  <Z>
    <c/>
    <d/>
  </Z>
  <e/>
</a>
END
 }

if (1) {                                                                        #TwrapWithN
  my $a = Data::Edit::Xml::new(<<END);
<a>
  <d/>
</a>
END

  ok !$a->go_d__wrapWithN_2_c_b_a;
  $a->by(sub{$_->wrapWithN_2_c_b_d_a});

  ok -p $a eq <<END;
<a>
  <b>
    <c>
      <d/>
    </c>
  </b>
</a>
END
 }

if (1) {                                                                        #TwrapWithAll
  my $a = Data::Edit::Xml::new(<<END);
<a>
  <d/>
</a>
END

  $a->go_d__wrapWithAll_c_b;

  ok -p $a eq <<END;
<a>
  <b>
    <c>
      <d/>
    </c>
  </b>
</a>
END
 }

if (1) {                                                                        #TwrapWithAll
  my $a = Data::Edit::Xml::new(q(<a id="c4"/>));
  my $b = Data::Edit::Xml::new(q(<a id="c5"/>));

  ok -M $a eq -M $b;
 }

if (1) {                                                                        #TputUpNextCut
  my $a = Data::Edit::Xml::new(<<END);
<a>
  <b>
    <c>
      <d/>
    </c>
  </b>
</a>
END

  $a->by(sub{$_->putUpNextCut_d_c_b_a});

  ok -p $a eq <<END
<a>
  <b>
    <c/>
    <d/>
  </b>
</a>
END
 }

if (1) {                                                                        #TputUpNextCut2
  my $a = Data::Edit::Xml::new(<<END);
<a>
  <b>
    <c>
      <d/>
    </c>
  </b>
</a>
END

  $a->by(sub{$_->putUpNextCut2_d_c_b_a});

  ok -p $a eq <<END
<a>
  <b>
    <c/>
  </b>
  <d/>
</a>
END
 }

if (1) {                                                                        #TputUpPrevCut
  my $a = Data::Edit::Xml::new(<<END);
<a>
  <b>
    <c>
      <d/>
    </c>
  </b>
</a>
END

  $a->by(sub{$_->putUpPrevCut_d_c_b_a});

  ok -p $a eq <<END
<a>
  <b>
    <d/>
    <c/>
  </b>
</a>
END
 }

if (1) {                                                                        #TputUpPrevCut2
  my $a = Data::Edit::Xml::new(<<END);
<a>
  <b>
    <c>
      <d/>
    </c>
  </b>
</a>
END

  $a->by(sub{$_->putUpPrevCut2_d_c_b_a});

  ok -p $a eq <<END
<a>
  <d/>
  <b>
    <c/>
  </b>
</a>
END
 }

if (1) {                                                                        #TcountNonEmptyTags
  my $a = Data::Edit::Xml::new(<<END);
<a>
  <b>
    <c>
      <d/>
    </c>
  </b>
</a>
END

  is_deeply $a->countNonEmptyTags(qw(c)), {a=>1, b=>1};
  is_deeply $a->go_b->countNonEmptyTags,  {b=>1, c=>1};
 }

ok -p Data::Edit::Xml::new(<<END)->by(sub
<persons>
  <person username="JS1"><name>John</name><surname>Smith</surname></person>
  <person username="MI1"><name>Morka</name><surname>Ismincius</surname></person>
</persons>
END
 {$_->unwrap_name;
  $_->cut_surname;
  $_->change_name_person__change_root_persons;
 })
eq <<END;
<persons>
  <name username="JS1">John</name>
  <name username="MI1">Morka</name>
</persons>
END

if (1) {                                                                        #TeditText
  my $a = Data::Edit::Xml::new(<<END);
<a><b>bbbb</b><c/></a>
END

  $a->by(sub{$_->editText =~ s(bbbb) (BBBB)gs});

  ok -p $a eq <<END;
<a>
  <b>BBBB</b>
  <c/>
</a>
END
 }

if (1) {                                                                        #TchangeKids
  my $a = Data::Edit::Xml::new(<<END);
<a>
  <b>
    <c/>
    <d/>
    <e/>
  </b>
  <f>
    <g/>
    <h>
      <i/>
    </h>
  </f>
  <j>
    <k/>
    <l>
      <m/>
    </l>
  </j>
</a>
END

  $a->changeKids(q(B), qr(\A(b|j|k|l|m)\Z), q(a));

  ok -p $a eq <<END;
<a>
  <B>
    <c/>
    <d/>
    <e/>
  </B>
  <f>
    <g/>
    <h>
      <i/>
    </h>
  </f>
  <B>
    <k/>
    <l>
      <m/>
    </l>
  </B>
</a>
END

  $a->go_f__changeKids_F;

  ok -p $a eq <<END;
<a>
  <B>
    <c/>
    <d/>
    <e/>
  </B>
  <f>
    <F/>
    <F>
      <i/>
    </F>
  </f>
  <B>
    <k/>
    <l>
      <m/>
    </l>
  </B>
</a>
END
 }

if (1) {                                                                        #TditaWrapWithPUnderConbody
  my $a = Data::Edit::Xml::new(<<END);
<conbody>text<q>quote</q><p>new paragraph</p>
more text</conbody>
END

  $a->ditaWrapWithPUnderConbody;

  my $b = new(<<END);
<conbody><p>text<q>quote</q></p><p>new paragraph
more text</p></conbody>
END

  ok $a->equals($b);
 }

if (1) {                                                                        #TcountTexts
  my $a = Data::Edit::Xml::new(<<END);
<concept>
  <title/>
  <conbody/>
</concept>
END

  is_deeply $a->countTexts, {};
 }

if (1) {                                                                        #TdupPutNext #TdupPutPrev
  my $a = Data::Edit::Xml::new(<<END);
<a>
  <b>
    <c/>
  </b>
  <d>
    <e/>
  </d>
</a>
END

  $a->first__dupPutNext_b;

  ok -p $a eq <<END;
<a>
  <b>
    <c/>
  </b>
  <b>
    <c/>
  </b>
  <d>
    <e/>
  </d>
</a>
END

  $a->last__dupPutPrev_d;

  ok -p $a eq <<END;
<a>
  <b>
    <c/>
  </b>
  <b>
    <c/>
  </b>
  <d>
    <e/>
  </d>
  <d>
    <e/>
  </d>
</a>
END
 }

if (1) {                                                                        #TdupPutNextN
  my $a = Data::Edit::Xml::new(<<END);
<a>
  <b>
    <c/>
  </b>
  <d>
    <e/>
    <f/>
    <g/>
  </d>
</a>
END

  $a->go_b__dupPutNextN_2;

  ok -p $a eq <<END;
<a>
  <b>
    <c/>
  </b>
  <b>
    <c/>
  </b>
  <b>
    <c/>
  </b>
  <d>
    <e/>
    <f/>
    <g/>
  </d>
</a>
END
 }

if (1) {                                                                        #TsetSelectionStart  #TsetSelectionEnd  #TmoveSelectionBefore #TmoveSelectionAfter #TmoveSelectionFirst #TmoveSelectionLast
  my $a = Data::Edit::Xml::new(<<END);
<a>
  <b>
    <c/>
  </b>
  <b>
    <c/>
  </b>
  <d>
    <e/>
    <f/>
    <g/>
  </d>
</a>
END
  $a->go_b_1__setSelectionStart;
  $a->go_d__setSelectionEnd;
  $a->first__moveSelectionBefore;

  ok -p $a eq <<END;
<a>
  <b>
    <c/>
  </b>
  <d>
    <e/>
    <f/>
    <g/>
  </d>
  <b>
    <c/>
  </b>
</a>
END

  $a->go_d_e__setSelectionStart;
  $a->go_d_g__setSelectionEnd;
  $a->go_b_c__moveSelectionAfter;
  ok -p $a eq <<END;
<a>
  <b>
    <c/>
    <e/>
    <f/>
    <g/>
  </b>
  <d/>
  <b>
    <c/>
  </b>
</a>
END

  $a->go_b_e__setSelectionStart;
  $a->go_b_f__setSelectionEnd;
  $a->last__moveSelectionFirst;

  ok -p $a eq <<END;
<a>
  <b>
    <c/>
    <g/>
  </b>
  <d/>
  <b>
    <e><!-- start --></e>
    <f><!-- end --></f>
    <c/>
  </b>
</a>
END

  $a->go_d__setSelectionStart;
  $a->last__setSelectionEnd;
  $a->go_b__moveSelectionLast;

  ok -p $a eq <<END;
<a>
  <b>
    <c/>
    <g/>
    <d/>
    <b>
      <e><!-- start --></e>
      <f><!-- end --></f>
      <c/>
    </b>
  </b>
</a>
END
 }

if (1) {                                                                        #TputFirstAsComment #TputLastAsComment #TputNextAsComment #TputPrevAsComment
  my $a = Data::Edit::Xml::new(<<END);
<a>
  <b>
    <c/>
  </b>
</a>
END

  $a->putFirstAsComment(q(First));
  $a->putLastAsComment (q(Last));
  $a->go_b_c->putPrevAsComment(q(Before C));
  $a->go_b_c->putNextAsComment(q(After C));

  ok -p $a eq <<END;
<a><!-- First -->
  <b><!-- Before C -->
    <c/>
<!-- After C -->
  </b>
<!-- Last -->
</a>
END
 }

if (1) {                                                                        #TsplitParentAfter
  my $a = Data::Edit::Xml::new(<<END);
<a>
  <b>
    <c/>
    <d/>
    <e/>
  </b>
</a>
END

  $a->go_b_d__splitParentAfter;

  ok -p $a eq <<END;
<a>
  <b>
    <c/>
    <d/>
  </b>
  <b>
    <e/>
  </b>
</a>
END
 }

if (1) {                                                                        #TsplitParentBefore
  my $a = Data::Edit::Xml::new(<<END);
<a>
  <b>
    <c/>
    <d/>
    <e/>
  </b>
</a>
END

  $a->go_b_d__splitParentBefore;

  ok -p $a ne <<END;
<a>
  <b>
    <c/>
  </b>
  <b>
    <d/>
    <e/>
  </b>
</a>
END
 }

if (1) {                                                                        #TcountWords
  my $a = Data::Edit::Xml::new(<<END);
<a>
  <b>aaa bbb ccc</b>
  <c>AAA BBB CCC</c>
  <c>aaa AAA</c>
</a>
END

  my $t = $a->countWords;

  is_deeply $t, { AAA => 2, aaa => 2, bbb => 1, BBB => 1, CCC => 1, ccc => 1 };
 }

if (1) {                                                                        #TcountReport
  my $a = Data::Edit::Xml::new(<<END);
<a>
  <b>aaa bbb ccc</b>
  <c>AAA BBB CCC</c>
  <c>aaa AAA</c>
</a>
END

  my $t = $a->countReport;

  ok $t eq <<END;
   Tag  Count
1    3  CDATA
2    1  a
3    1  b
4    2  c
END
 }

if (1) {                                                                        #TdupTag
  my $a = Data::Edit::Xml::new(<<END);
<a>
  <b><c/></b>
</a>
END

  my $d = $a->go_b__dupTag;
  ok -p $d eq <<END;
<b/>
END
 }

if (1) {                                                                        #Tp
  my $a = Data::Edit::Xml::new(<<END);
<a>
  <b>
    <c>
      <d/>
    </c>
  </b>
</a>
END

  my ($d, $c, $b) = $a->byList;
  is_deeply $d->p, [$a, $b, $c, $d];
 }

if (1) {                                                                        #TwrapToLastOrFirstIn
  my $a = Data::Edit::Xml::new(<<END);
<a><b/><c/><d/><e/><f/></a>
END

  $a->go_e__wrapToLastOrFirstIn_g_h;
  ok -p $a eq <<END;
<a>
  <b/>
  <c/>
  <d/>
  <g>
    <e/>
    <f/>
  </g>
</a>
END

  $a->go_d__wrapToLastOrFirstIn_h_g_c;
  ok -p $a eq <<END;
<a>
  <b/>
  <c/>
  <h>
    <d/>
    <g>
      <e/>
      <f/>
    </g>
  </h>
</a>
END

  $a->go_c__wrapToLastOrFirstIn_i_c;
  ok -p $a eq <<END;
<a>
  <b/>
  <i>
    <c/>
    <h>
      <d/>
      <g>
        <e/>
        <f/>
      </g>
    </h>
  </i>
</a>
END
 }

if (1) {                                                                        #TwrapFromFirstOrLastIn
  my $a = Data::Edit::Xml::new(<<END);
<a><b/><c/><d/><e/><f/></a>
END

  $a->go_d__wrapFromFirstOrLastIn_g_a_b_c;
  ok -p $a eq <<END;
<a>
  <b/>
  <g>
    <c/>
    <d/>
  </g>
  <e/>
  <f/>
</a>
END

  $a->go_e__wrapFromFirstOrLastIn_i_A_B_C;
  ok -p $a eq <<END;
<a>
  <i>
    <b/>
    <g>
      <c/>
      <d/>
    </g>
    <e/>
  </i>
  <f/>
</a>
END
 }

if (1) {                                                                        #TmergeOnlyChildLikeNext
  my $a = Data::Edit::Xml::new(<<END);
<a>
  <b id="1">
     <c/>
  </b>
  <b id="2">
     <c/>
  </b>
</a>
END

  $a->go_b_c__mergeOnlyChildLikeNext;
  ok -p $a eq <<END;
<a>
  <b id="1">
    <c/>
  </b>
</a>
END
 }

if (1) {                                                                        #TmergeOnlyChildLikePrev
  my $a = Data::Edit::Xml::new(<<END);
<a>
  <b id="1">
     <c/>
  </b>
  <b id="2">
     <c/>
  </b>
</a>
END

  $a->go_b_1_c__mergeOnlyChildLikePrev;
  ok -p $a eq <<END;
<a>
  <b id="2">
    <c/>
  </b>
</a>
END
 }

if (1) {                                                                        #TditaAddTopicReport
  my $a = Data::Edit::Xml::new(<<END);
<task id="c1">
  <title/>
  <taskbody>
  </taskbody>
</task>
END

  $a->ditaAddTopicReport(q(Please note the following discrepancies));
  ok -p $a eq <<END;
<task id="c1">
  <title/>
  <taskbody>
    <context>
      <required-cleanup>Please note the following discrepancies</required-cleanup>
    </context>
  </taskbody>
</task>
END
 }

if (1) {                                                                        #Thelp
  my $a = Data::Edit::Xml::new(<<END);
<a>
  <steps/>
  <dd/>
</a>
END

  my $u = $a->go_dd->help;
  ok $u =~ m(\Ahttp://docs.oasis-open.org/dita/dita/v1.3/errata02/os/complete/part3-all-inclusive/contentmodels/cmltd.html#cmltd__dd\Z);

  my $U = $a->go_steps->help;
  ok $U =~ m(\Ahttp://docs.oasis-open.org/dita/dita/v1.3/errata02/os/complete/part3-all-inclusive/contentmodels/cmlts.html#cmlts__steps\Z);
 }

if (1) {                                                                        #TmergeOnlyChildLikePrevLast
  my $a = Data::Edit::Xml::new(<<END);
<a>
  <b><c/><d>1</d></b>
  <b>    <d>2</d></b>
</a>
END

  my $c = $a->last__last__mergeOnlyChildLikePrevLast;
  ok -p $a eq <<END;
<a>
  <b>
    <c/>
    <d>1 2</d>
  </b>
</a>
END

  ok -p $c eq <<END
<d>1 2</d>
END
 }

if (1) {                                                                        #TditaAbsoluteHref
  my $a = Data::Edit::Xml::new(<<END);
<a href="bbb.txt">
</a>
END

  ok $a->ditaAbsoluteHref(q(/home/aaa/)) eq q(/home/aaa/bbb.txt);
 }

if (1) {                                                                        #Tparentage
  my $a = Data::Edit::Xml::new(<<END);
<a>
  <b>
    <c>
      <d/>
    </c>
  </b>
</a>
END

  my ($d, $c, $b) = $a->byList;

  is_deeply $a->go_b_c_d__parentage, [$a, $b, $c, $d];
 }

if (1) {                                                                        #TditaRoot
  my $a = Data::Edit::Xml::new(q(<concept><a/></concept>));
  ok  $a->ditaRoot;
  ok !$a->first->ditaRoot;
 }

if (1) {                                                                        #TgetNodeAs #TputNodeAs #TreportNode #TreportNodeAttributes #TreportNodeContext
  my $a = Data::Edit::Xml::new(<<END);
<a>
  <b>
    <c>
      <d id="d"/>
    </c>
  </b>
</a>
END

  my ($d, $c, $b) = $a->byList;

  $d->putNodeAs(q(D));
  ok $a->getNodeAs(q(D)) == $d;
  %Data::Edit::Xml::savedNodes = ();
  ok $a->getNodeAs(q(D)) == $a;

  ok $a->reportNode_AAAA eq <<END;
AAAA
<a>
  <b>
    <c>
      <d id="d"/>
    </c>
  </b>
</a>
END
  ok $d->reportNodeAttributes_BBBB eq q(BBBB  id="d");
  ok $d->reportNodeAttributes_CCCC eq q(CCCC  id="d");
  ok $d->reportNodeContext_DDDD    eq q(DDDD d c b a);
 }

if (1) {                                                                        #TprintNodeAsSingleton
  my $a = Data::Edit::Xml::new(<<END);
<a id="a" outputclass="aa">
  <b/>
</a>
END

  ok $a->printNodeAsSingleton eq q(<a id="a" outputclass="aa"/>);
 }

if (1) {                                                                        #TstringContentOrText
  my $a = Data::Edit::Xml::new(<<END);
<a>AAAA</a>
END

  ok $a->stringContent              eq q(AAAA);
  ok $a->stringContentOrText        eq q(AAAA);
  ok $a->first__stringContent       eq q();
  ok $a->first__stringContentOrText eq q(AAAA);
 }

if (0) {                                                                        # Performance of by
  use Time::HiRes qw(time);
  my $M = 10;
  my $N = 4;
  my $a = sub
   {my $a = qq(<a/>)       x$M;
    my $b = qq(<b>$a</b>\n)x$M;
    my $c = qq(<c>$b</c>\n)x$M;
    my $d = qq(<d>$c</d>\n)x$M;
    my $e = qq(<e>$d</e>\n)x$M;
    my $f = qq(<f>$e</f>);
    Data::Edit::Xml::new($f);
   }->();

  for(1..$N)
   {my @d;
    for my $sub(qw(by By))
     {my $start = time;
      for (1..$N)
       {$a->$sub(sub
         {my ($o) = @_;
         });
       }
      push @d, time - $start;
     }
    say STDERR "AAAA ", $d[1]/$d[0];                                            # By / by
   }
  exit;
 }

if (1) {                                                                        #TcopyAttrsFromParent
  my $a = Data::Edit::Xml::new(<<END);
<a id="a" a="a">
  <b id="b" b="b"/>
</a>
END
  my $b = $a->first;
  $b->copyAttrsFromParent_id;
  ok <<END eq -p $a;
<a a="a" id="a">
  <b b="b" id="a"/>
</a>
END

  $b->copyAttrsFromParent;

  ok <<END eq -p $a;
<a a="a" id="a">
  <b a="a" b="b" id="a"/>
</a>
END
 }

if (1) {                                                                        #TcopyAttrsToParent
  my $a = Data::Edit::Xml::new(<<END);
<a a="a">
  <b id="b" b="b"/>
</a>
END
  my $b = $a->first;
  $b->copyAttrsToParent_id;
  ok <<END eq -p $a;
<a a="a" id="b">
  <b b="b" id="b"/>
</a>
END

  $b->copyAttrsToParent;

  ok <<END eq -p $a;
<a a="a" b="b" id="b">
  <b b="b" id="b"/>
</a>
END
 }

if (1) {                                                                        #Troot #Ttop
  my $a = Data::Edit::Xml::new(<<END);
<a><b><c><d/></c></b></a>
END

  $a->by(sub
   {ok $a == $_->root;
    ok $a == $_->top;
   });
 }

if (1) {                                                                        #TaddAttr
  my $a = Data::Edit::Xml::new(q(<a id="a"/>));

  $a->addAttr(id=>"b", class=>"c");

  ok -p $a eq qq(<a class="c" id="a"/>\n);
 }

if (1) {                                                                        #Treorder
  my $a = Data::Edit::Xml::new(<<END);
<a>
  <d>
    <c/>
  </d>
  <b>
    <c/>
  </b>
</a>
END

  $a->by(sub {$_->reorder_b_d_a});

  ok -p $a eq <<END;
<a>
  <b>
    <c/>
  </b>
  <d>
    <c/>
  </d>
</a>
END

  $a->by(sub {$_->reorder_b_d_a});

  ok -p $a eq <<END;
<a>
  <b>
    <c/>
  </b>
  <d>
    <c/>
  </d>
</a>
END
 }

if (1) {                                                                        #TjoinWithText
  my $a = Data::Edit::Xml::new(<<END);
<a>
  <b/>
  <b/>
  <b/>
</a>
END

  $a->joinWithText_CC_b_a;

  ok -p $a eq <<END;
<a>
  <b/>
CC
  <b/>
CC
  <b/>
</a>
END
 }

if (1) {                                                                        #TprettyStringHtml #TprintAttributesHtml
  my $a = Data::Edit::Xml::new(<<END);
<a id="1">
  <b id="2" b="B">
    <c/>
  </b>
  <d id="3" d="D">
    Some text
  </d>
</a>
END

  ok $a->prettyStringHtml eq <<END;
<div class="xmlLine"><span class="xmlLineStartTag"></span><span class="xmlLt">&lt;</span><span class="xmlTag">a</span> <span class="xmlAttr">id</span><span class="xmlEquals">=</span><span class="xmlValue">"1"</span><span class="xmlGt">&gt;</span></div>
<div class="xmlLine"><span class="xmlLineStartTag">&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="xmlLt">&lt;</span><span class="xmlTag">b</span> <span class="xmlAttr">b</span><span class="xmlEquals">=</span><span class="xmlValue">"B"</span> <span class="xmlAttr">id</span><span class="xmlEquals">=</span><span class="xmlValue">"2"</span><span class="xmlGt">&gt;</span></div>
<div class="xmlLine"><span class="xmlLineStartTag">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="xmlLt">&lt;</span><span class="xmlTag">c</span><span class="xmlSlashGt">/&gt;</span></div>
<div class="xmlLine"><span class="xmlLineStartTag">&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="xmlLtSlash">&lt;/</span><span class="xmlTag">b</span><span class="xmlGt">&gt;</span></div>
<div class="xmlLine"><span class="xmlLineStartTag">&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="xmlLt">&lt;</span><span class="xmlTag">d</span> <span class="xmlAttr">d</span><span class="xmlEquals">=</span><span class="xmlValue">"D"</span> <span class="xmlAttr">id</span><span class="xmlEquals">=</span><span class="xmlValue">"3"</span><span class="xmlGt">&gt;</span><span class="xmlText">Some text</span><span class="xmlLtSlash">&lt;/</span><span class="xmlTag">d</span><span class="xmlGt">&gt;</span></div>
<div class="xmlLine"><span class="xmlLineStartTag"></span><span class="xmlLtSlash">&lt;/</span><span class="xmlTag">a</span><span class="xmlGt">&gt;</span></div>
END
 }

if (1) {                                                                        #TattrsNone
  my $a = Data::Edit::Xml::new(q(<a><b/><c id="cc"/></a>));
  ok $a->first__attrsNone;
  ok !$a->last__attrsNone;
 }

if (1) {                                                                        #Tsss
  my $a = Data::Edit::Xml::new(q(<a><b><c/></b></a>));

  $a->go_b_c__sss_aa_bb_cc;

  ok -p $a eq <<END;
<a>
  <b>
    <c/>
aa bb cc
  </b>
</a>
END
 }

if (1) {                                                                        #TputTextFirst #TputTextLast #TputTextNext #TputTextPrev
  my $a = Data::Edit::Xml::new(q(<a><b><c/></b></a>));

  my $b = $a->first;

  $b->putTextPrev(qw(Before the B node));
  $b->putTextNext(qw(After the B node));
  $b->putTextFirst(qw(First under the B node));
  $b->putTextLast(qw(Last under the B node));

  ok -p $a eq <<END;
<a>Before the B node
  <b>First under the B node
    <c/>
Last under the B node
  </b>
After the B node
</a>
END
 }

if (1) {                                                                        #TisEmpty #ThasContent
  my $a = Data::Edit::Xml::new(q(<a><b><c/></b></a>));

  my ($c, $b) = $a->byList;

  ok $c->isEmpty;
  ok $b->hasContent;
 }

if (1) {                                                                        #TcutFirst #TcutLast #TcutNext #TcutPrev #TputCutOutFirst #TputCutOutLast #TputCutOutNext #TputCutOutPrev
  my $a = Data::Edit::Xml::new(<<END);
<a>
  <b/>
  <c/>
  <d/>
  <e/>
  <f/>
</a>
END

  my ($b, $c, $d, $e, $f) = $a->byList;
  $b->cutNext_c_a;
  ok -p $a eq <<END;
<a>
  <b/>
  <d/>
  <e/>
  <f/>
</a>
END

  $f->cutPrev_e_a;
  ok -p $a eq <<END;
<a>
  <b/>
  <d/>
  <f/>
</a>
END
  $a->cutFirst_b;
  ok -p $a eq <<END;
<a>
  <d/>
  <f/>
</a>
END
  $a->cutLast_f;
  ok -p $a eq <<END;
<a>
  <d/>
</a>
END

  ok @saveLastCutOut == 4;

  $a->putCutOutFirst;
  ok -p $a eq <<END;
<a>
  <f/>
  <d/>
</a>
END

  $a->putCutOutLast;
  ok -p $a eq <<END;
<a>
  <f/>
  <d/>
  <b/>
</a>
END

  $d->putCutOutPrev;
  ok -p $a eq <<END;
<a>
  <f/>
  <e/>
  <d/>
  <b/>
</a>
END

  $d->putCutOutNext;
  ok -p $a eq <<END;
<a>
  <f/>
  <e/>
  <d/>
  <c/>
  <b/>
</a>
END

 }

if (0) {                                                                        #TditaCutIfEmptyAndFirstFailingChild
  my $a = Data::Edit::Xml::new(<<END);
<concept>
  <title/>
  <p/>
</concept>
END

  $a->by(sub
   {$_->ditaCutIfEmptyAndFirstFailingChild_p;
   });

  ok -p $a eq <<END
<concept>
  <title/>
</concept>
END
 }

if (1) {                                                                        #TatTop
  my $a = Data::Edit::Xml::new(<<END);
<a><b/></a>
END

  ok  $a->atTop;
  ok !$a->first__atTop;
 }

if (0) {                                                                        #tditaFindFirstFailure
  my $a = Data::Edit::Xml::new(<<END);
<concept><title/><conbody><p/><q/></conbody></concept>
END

  ok -t $a->ditaFindFirstFailure eq 'q';
 }

if (1) {                                                                        #TmoveFirst #TmoveLast
  my $a = Data::Edit::Xml::new(<<END);
<a><b/><c/><d/></a>
END

  my ($b, $c, $d) = $a->byList;
  $d->moveFirst;
  $b->moveLast;

  ok -p $a eq <<END;
<a>
  <d/>
  <c/>
  <b/>
</a>
END
 }

if (1) {
  my $a = Data::Edit::Xml::new(<<END);                                          #TisOnlyChild #TisOnlyChildN
<a>
  <b>
    <c>
      <d>
        <e/>
      </d>
    </c>
  </b>
  <f>
    <g/>
  </f>
</a>
END

  my ($e, $d, $c, $b, $g, $f) = $a->byList;
  ok  $e->isOnlyChild_e_d_c_b_a    == $e;
  ok  $e->isOnlyChildN_3_e_d_c_b_a == $e;
  ok !$g->isOnlyChildN_2;
 }

if (1) {
  my $a = Data::Edit::Xml::new(<<END);                                          #TchangeText #TisText #TchangeTextToSpace #TatText
<a>abcdcba</a>
END

  my  $t = $a->first;
  ok  $t->isText;
  ok  $t->changeText(q/b(.)d(.)b/, q/B$1D$2B/, q/s/);
  ok  $t->text eq q(aBcDcBa);
  ok  $t->atText(qr(aBcDc.a));

  ok !$t->changeText(q(d),         q(e),       q(s));
  ok  $t->changeText(q(d),         q(e),       q(i));
  ok  $t->text eq q(aBcecBa);
  ok  $t->atText(qr(\A(abc))i);

  ok  $t->changeTextToSpace(qr([abc]));
  ok  $t->text eq q( B e B );
 }

if (1) {                                                                        #TisNotFirst #TisNotLast
  my $a = Data::Edit::Xml::new(<<END);
<a><b/><c/><d/></a>
END

  my ($b, $c, $d) = $a->byList;
  ok $c->isNotFirst;
  ok $b->isNotLast;
 }

if (1) {                                                                        #TwrapWithDup #TwrapContentWithDup
  my $a = Data::Edit::Xml::new(<<END);
<a><b><c/></b></a>
END

  my ($c, $b) = $a->byList;

  $c->wrapWithDup->id = 'c';
  ok -p $a eq <<END;
<a>
  <b>
    <c id="c">
      <c/>
    </c>
  </b>
</a>
END

  $b->wrapContentWithDup->id = 'b';
  ok -p $a eq <<END;
<a>
  <b>
    <b id="b">
      <c id="c">
        <c/>
      </c>
    </b>
  </b>
</a>
END
 }

if (1) {                                                                        #TatStringContentMatches
  my $a = Data::Edit::Xml::new(<<END);
<a>
  <b>
    <c>cc</c>
    <d>dd</d>
  </b>
</a>
END

  $a->by(sub
   {my ($o) = @_;
    if (!$o->atStringContentMatches_dd)
     {$o->id = "no";
     }
   });

  ok -p $a eq <<END;
<a>
  <b>
    <c id="no">cc</c>
    <d>dd</d>
  </b>
</a>
END
 }

#if (1)
# {use Time::HiRes qw(time);
#  my $N = 14;
#
#  my @alpha = reverse @{['a'..'z']}[0..$N-1];
#  my $x = '';
#  for my $a(@alpha)
#   {$x = qq(<$a>$x$x</$a>\n);
#   }
#
#  my $a = new($x);
#
#  my $start = time;
#  my $n = 0;
#  $a->by(sub
#   {++$n if $_->at(qw(e d c b a));
#   });
##N=18 by = 0.154; by1 = 0.118;
##N=10 by = 5.39 ; by1 = 5.1   with _ notation
##N=10 by = 2.9  ; by1 = 2.7   with qw() notation
##N=14 by = 84   ; by1 = 80    with _
##N=14 by = 44   ; by1 = 42    with qw()
#  my $delta = (time - $start) * 1e3;
#  lll "AAAA $delta  count=$n";
# }

if (1) {
  my $a = Data::Edit::Xml::new(<<END);
<a>
  <b>
    111
    <c/>
    222
  </b>
</a>
END

  $a->by(sub
   {my ($o) = @_;
    if ($o->at_c)
     {$o->splitBefore;
     }
   });

  ok -p $a eq <<END;
<a>
  <b>
    111
    </b>
  <b>
    <c/>
    222
  </b>
</a>
END
 }

# 2019.12.02 19:23:28 splitBefore was inadvertantly merging text segments resulting in text being copied rather than moved
if (1) {
  my $a = Data::Edit::Xml::new(<<END);
<a>
  <b>
    111
    <c/>
    222
  </b>
</a>
END

  $a->by(sub
   {my ($o) = @_;
    if ($o->at_c)
     {$o->splitAfter;
     }
   });

  ok -p $a eq <<END;
<a>
  <b>
    111
    <c/>
  </b>
  <b>
    222
  </b>
</a>
END
 }

if (1) {                                                                        #TnextIs #TprevIs #TfirstIs #TlastIs
  my $a = Data::Edit::Xml::new(<<END);
<a>
  <b/><c/><d/><e/>
</a>
END

  ok  $a->go_c__nextIs_d;
  ok !$a->go_c__nextIs_c;
  ok  $a->go_c__prevIs_b;
  ok !$a->go_c__prevIs_e;

  ok  $a->firstIs_b;
  ok !$a->firstIs_c;

  ok  $a->lastIs_e;
  ok !$a->lastIs_d;
 }

if (1) {                                                                        #TsubMd5 #TsubMd5Tree
  my $a = Data::Edit::Xml::new(<<END);
<a>
 <b>bb</b>
 <b><c>bb</c></b>
 <d>dd</d>
 <d>dd</d>
</a>
END

  is_deeply [$a->first->subMd5Tree], [q(21ad0bd836b90d08f4cf640b4c298e7c), q(bb)];

  is_deeply subMd5($a, map {$_=>1} qw(b c)),
   {b => {"21ad0bd836b90d08f4cf640b4c298e7c" => {    bb      => 1},
          "95c1bde7f88ced04c1b1fbcab120ed96" => {"<c>bb</c>" => 1},
          },
    c => {"21ad0bd836b90d08f4cf640b4c298e7c" => {    bb      => 1}},
   };
# owf(q(/home/phil/z/z/z/out.xml), dump(subMd5($a, map{$_=>1} qw(b c))));
 }

latestTest:;

say STDERR "Took ", sprintf("%.3f",  time - $startTime), " seconds";

done_testing;

1

# owf(q(/home/phil/z/z/z/out.xml), -p $a);
