This commit is contained in:
Tom Moor 2020-10-09 17:22:06 -07:00
parent fd211731cc
commit 9e279064d6

View File

@ -2,6 +2,7 @@
* @module YXml * @module YXml
*/ */
import { XmlElement } from 'yjs'
import { import {
YXmlEvent, YXmlEvent,
YXmlElement, YXmlElement,
@ -14,7 +15,7 @@ import {
YXmlFragmentRefID, YXmlFragmentRefID,
callTypeObservers, callTypeObservers,
transact, transact,
AbstractUpdateDecoder, AbstractUpdateEncoder, Doc, ContentType, Transaction, Item, YXmlText, YXmlHook, Snapshot // eslint-disable-line AbstractUpdateDecoder, AbstractUpdateEncoder, Doc, ID, ContentType, Transaction, Item, YXmlText, YXmlHook, Snapshot // eslint-disable-line
} from '../internals.js' } from '../internals.js'
/** /**
@ -29,6 +30,13 @@ import {
* @typedef {string} CSS_Selector * @typedef {string} CSS_Selector
*/ */
/**
* @typedef {Object} Filters
* @property {CSS_Selector|undefined} Filters.tagname
* @property {ID|undefined} Filters.id
* @property {Record<string, any>|undefined} Filters.attributes
*/
/** /**
* Dom filter function. * Dom filter function.
* *
@ -98,7 +106,7 @@ export class YXmlTreeWalker {
} }
} }
} }
} while (n !== null && (n.deleted || !this._filter(/** @type {ContentType} */ (n.content).type))) } while (n !== null && (n.deleted || !this._filter(/** @type {ContentType} */(n.content).type)))
} }
this._firstCall = false this._firstCall = false
if (n === null) { if (n === null) {
@ -140,7 +148,7 @@ export class YXmlFragment extends AbstractType {
*/ */
_integrate (y, item) { _integrate (y, item) {
super._integrate(y, item) super._integrate(y, item)
this.insert(0, /** @type {Array<any>} */ (this._prelimContent)) this.insert(0, /** @type {Array<any>} */(this._prelimContent))
this._prelimContent = null this._prelimContent = null
} }
@ -179,19 +187,57 @@ export class YXmlFragment extends AbstractType {
* *
* Query support: * Query support:
* - tagname * - tagname
* TODO:
* - id * - id
* TODO:
* - attribute * - attribute
* *
* @param {CSS_Selector} query The query on the children. * @param {CSS_Selector|Filters} query The query on the children.
* @return {YXmlElement|YXmlText|YXmlHook|null} The first element that matches the query or null. * @return {YXmlElement|YXmlText|YXmlHook|null} The first element that matches the query or null.
* *
* @public * @public
*/ */
querySelector (query) { querySelector (query) {
query = query.toUpperCase() /**
// @ts-ignore * @type {Filters}
const iterator = new YXmlTreeWalker(this, element => element.nodeName && element.nodeName.toUpperCase() === query) */
let filters = {}
// Allow passing a string to query the tagname for backwards compatability
if (typeof query === 'string') {
filters.tagname = query.toUpperCase()
} else {
filters = query
}
const iterator = new YXmlTreeWalker(this, element => {
// @ts-ignore
if (filters.tagname && element.nodeName && element.nodeName.toUpperCase() === filters.tagname) {
return true
}
if (filters.id && element._item && element._item.id === filters.id) {
return true
}
if (filters.attributes && element instanceof XmlElement) {
const attributes = element.getAttributes()
const keys = Object.keys(filters.attributes)
// All passed attributes must match to count as a match
for (const key of keys) {
if (filters.attributes[key] !== attributes[key]) {
return false
}
}
// accounts for passing an empty object as a filter
if (keys.length > 1) {
return true
}
}
return false
})
const next = iterator.next() const next = iterator.next()
if (next.done) { if (next.done) {
return null return null