# src/Types/YXml/YXmlFragment.js

    import { createAssociation } from '../../Bindings/DomBinding/util.js'
    import YXmlTreeWalker from './YXmlTreeWalker.js'

    import YArray from '../YArray/YArray.js'
    import YXmlEvent from './YXmlEvent.js'
    import { logItemHelper } from '../../MessageHandler/messageToString.js'

    /**
     * Dom filter function.
     *
     * @callback domFilter
     * @param {string} nodeName The nodeName of the element
     * @param {Map} attributes The map of attributes.
     * @return {boolean} Whether to include the Dom node in the YXmlElement.
     */

    /**
     * Define the elements to which a set of CSS queries apply.
     * {@link https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors|CSS_Selectors}
     *
     * @example
     *   query = '.classSelector'
     *   query = 'nodeSelector'
     *   query = '#idSelector'
     *
     * @typedef {string} CSS_Selector
     */

    /**
     * Represents a list of {@link YXmlElement}.and {@link YXmlText} types.
     * A YxmlFragment is similar to a {@link YXmlElement}, but it does not have a
     * nodeName and it does not have attributes. Though it can be bound to a DOM
     * element - in this case the attributes and the nodeName are not shared.
     *
     * @public
     */
    export default class YXmlFragment extends YArray {
      /**
       * Create a subtree of childNodes.
       *
       * @example
       * const walker = elem.createTreeWalker(dom => dom.nodeName === 'div')
       * for (let node in walker) {
       *   // `node` is a div node
       *   nop(node)
       * }
       *
       * @param {Function} filter Function that is called on each child element and
       *                          returns a Boolean indicating whether the child
       *                          is to be included in the subtree.
       * @return {TreeWalker} A subtree and a position within it.
       *
       * @public
       */
      createTreeWalker (filter) {
        return new YXmlTreeWalker(this, filter)
      }

      /**
       * Returns the first YXmlElement that matches the query.
       * Similar to DOM's {@link querySelector}.
       *
       * Query support:
       *   - tagname
       * TODO:
       *   - id
       *   - attribute
       *
       * @param {CSS_Selector} query The query on the children.
       * @return {?YXmlElement} The first element that matches the query or null.
       *
       * @public
       */
      querySelector (query) {
        query = query.toUpperCase()
        const iterator = new YXmlTreeWalker(this, element => element.nodeName === query)
        const next = iterator.next()
        if (next.done) {
          return null
        } else {
          return next.value
        }
      }

      /**
       * Returns all YXmlElements that match the query.
       * Similar to Dom's {@link querySelectorAll}.
       *
       * TODO: Does not yet support all queries. Currently only query by tagName.
       *
       * @param {CSS_Selector} query The query on the children
       * @return {Array<YXmlElement>} The elements that match this query.
       *
       * @public
       */
      querySelectorAll (query) {
        query = query.toUpperCase()
        return Array.from(new YXmlTreeWalker(this, element => element.nodeName === query))
      }

      /**
       * Creates YArray Event and calls observers.
       *
       * @private
       */
      _callObserver (transaction, parentSubs, remote) {
        this._callEventHandler(transaction, new YXmlEvent(this, parentSubs, remote, transaction))
      }

      /**
       * Get the string representation of all the children of this YXmlFragment.
       *
       * @return {string} The string representation of all children.
       */
      toString () {
        return this.map(xml => xml.toString()).join('')
      }

      /**
       * @private
       * Unbind from Dom and mark this Item as deleted.
       *
       * @param {Y} y The Yjs instance
       * @param {boolean} createDelete Whether to propagate a message that this
       *                               Type was deleted.
       * @param {boolean} [gcChildren=y._hasUndoManager===false] Whether to garbage
       *                                         collect the children of this type.
       *
       * @private
       */
      _delete (y, createDelete, gcChildren) {
        super._delete(y, createDelete, gcChildren)
      }

      /**
       * Creates a Dom Element that mirrors this YXmlElement.
       *
       * @param {Document} [_document=document] The document object (you must define
       *                                        this when calling this method in
       *                                        nodejs)
       * @param {Object<key:hookDefinition>} [hooks={}] Optional property to customize how hooks
       *                                             are presented in the DOM
       * @param {DomBinding} [binding] You should not set this property. This is
       *                               used if DomBinding wants to create a
       *                               association to the created DOM type
       * @return {Element} The {@link https://developer.mozilla.org/en-US/docs/Web/API/Element|Dom Element}
       *
       * @public
       */
      toDom (_document = document, hooks = {}, binding) {
        const fragment = _document.createDocumentFragment()
        createAssociation(binding, fragment, this)
        this.forEach(xmlType => {
          fragment.insertBefore(xmlType.toDom(_document, hooks, binding), null)
        })
        return fragment
      }
      /**
       * Transform this YXml Type to a readable format.
       * Useful for logging as all Items and Delete implement this method.
       *
       * @private
       */
      _logString () {
        return logItemHelper('YXml', this)
      }
    }