src/lib/Parser.js
/**
  Consumes an element if possible.

  @returns {boolean}
    Whether an element was consumed.

  @see https://www.w3.org/TR/2008/REC-xml-20081126/#NT-element
  */
Consumes an element if possible.
Consumes an element if possible .
consumeElement() {
    let { scanner } = this;
    let mark = scanner.charIndex;
    if (scanner.peek() !== '<') {
      return false;
    }
    scanner.advance();
    let name = this.consumeName();
    if (!name) {
      scanner.reset(mark);
      return false;
    }
    let attributes = Object.create(null);
    while (this.consumeWhitespace()) {
      let attrName = this.consumeName();
      if (!attrName) {
        continue;
      }
      let attrValue = this.consumeEqual()
        && this.consumeAttributeValue();
      if (attrValue === false) {
        this.error('Attribute value expected');
      }
      if (attrName in attributes) {
        this.error(`Duplicate attribute: ${attrName}`);
      }
      if (attrName === 'xml:space'
          && attrValue !== 'default'
          && attrValue !== 'preserve') {
        this.error('Value of the `xml:space` attribute must be "default" or "preserve"');
      }
      attributes[attrName] = attrValue;
    }
    if (this.options.sortAttributes) {
      let attrNames = Object.keys(attributes).sort();
      let sortedAttributes = Object.create(null);
      for (let i = 0; i < attrNames.length; ++i) {
        let attrName = attrNames[i];
        sortedAttributes[attrName] = attributes[attrName];
      }
      attributes = sortedAttributes;
    }
    let isEmpty = Boolean(scanner.consumeStringFast('/>'));
    let element = new XmlElement(name, attributes);
    element.parent = this.currentNode;
    if (!isEmpty) {
      if (!scanner.consumeStringFast('>')) {
        this.error(`Unclosed start tag for element \`${name}\``);
      }
      this.currentNode = element;
      this.consumeCharData();
      while (
        this.consumeElement()
          || this.consumeContentReference()
          || this.consumeCdataSection()
          || this.consumeProcessingInstruction()
          || this.consumeComment()
      ) {
        this.consumeCharData();
      }
      let endTagMark = scanner.charIndex;
      let endTagName;
      if (!scanner.consumeStringFast('</')
          || !(endTagName = this.consumeName())
          || endTagName !== name) {
        scanner.reset(endTagMark);
        this.error(`Missing end tag for element ${name}`);
      }
      this.consumeWhitespace();
      if (!scanner.consumeStringFast('>')) {
        this.error(`Unclosed end tag for element ${name}`);
      }
      this.currentNode = element.parent;
    }
    this.addNode(element);
    return true;
  }
consumeElement ( ) { let { scanner } = this ; let mark = scanner . charIndex ; if ( scanner . peek ( ) !== '<' ) { return false ; } scanner . advance ( ) ; let name = this . consumeName ( ) ; if ( ! name ) { scanner . reset ( mark ) ; return false ; } let attributes = Object . create ( null ) ; while ( this . consumeWhitespace ( ) ) { let attrName = this . consumeName ( ) ; if ( ! attrName ) { continue ; } let attrValue = this . consumeEqual ( ) && this . consumeAttributeValue ( ) ; if ( attrValue === false ) { this . error ( 'Attribute value expected' ) ; } if ( attrName in attributes ) { this . error ( ` ${ attrName } ` ) ; } if ( attrName === 'xml:space' && attrValue !== 'default' && attrValue !== 'preserve' ) { this . error ( 'Value of the `xml:space` attribute must be "default" or "preserve"' ) ; } attributes [ attrName ] = attrValue ; } if ( this . options . sortAttributes ) { let attrNames = Object . keys ( attributes ) . sort ( ) ; let sortedAttributes = Object . create ( null ) ; for ( let i = 0 ; i < attrNames . length ; ++ i ) { let attrName = attrNames [ i ] ; sortedAttributes [ attrName ] = attributes [ attrName ] ; } attributes = sortedAttributes ; } let isEmpty = Boolean ( scanner . consumeStringFast ( '/>' ) ) ; let element = new XmlElement ( name , attributes ) ; element . parent = this . currentNode ; if ( ! isEmpty ) { if ( ! scanner . consumeStringFast ( '>' ) ) { this . error ( ` \` ${ name } \` ` ) ; } this . currentNode = element ; this . consumeCharData ( ) ; while ( this . consumeElement ( ) || this . consumeContentReference ( ) || this . consumeCdataSection ( ) || this . consumeProcessingInstruction ( ) || this . consumeComment ( ) ) { this . consumeCharData ( ) ; } let endTagMark = scanner . charIndex ; let endTagName ; if ( ! scanner . consumeStringFast ( '</' ) || ! ( endTagName = this . consumeName ( ) ) || endTagName !== name ) { scanner . reset ( endTagMark ) ; this . error ( ` ${ name } ` ) ; } this . consumeWhitespace ( ) ; if ( ! scanner . consumeStringFast ( '>' ) ) { this . error ( ` ${ name } ` ) ; } this . currentNode = element . parent ; } this . addNode ( element ) ; return true ; }
Consumes an element if possible.
Consumes an element if possible .