UNPKG

@openui5/sap.ui.core

Version:

OpenUI5 Core Library sap.ui.core

812 lines (736 loc) 26.3 kB
// valid-jsdoc disabled because this check is validating just the params and return statement and those are all inherited from BaseTreeModifier. /* eslint-disable valid-jsdoc */ /*! * OpenUI5 * (c) Copyright 2026 SAP SE or an SAP affiliate company. * Licensed under the Apache License, Version 2.0 - see LICENSE.txt. */ sap.ui.define([ "sap/base/util/isPlainObject", "sap/base/util/merge", "sap/ui/base/BindingInfo", "sap/ui/base/DataType", "sap/ui/core/util/reflection/BaseTreeModifier", "sap/ui/core/Fragment", "sap/ui/util/XMLHelper" ], function( isPlainObject, merge, BindingInfo, DataType, BaseTreeModifier, Fragment, XMLHelper ) { "use strict"; const CUSTOM_DATA_NS = "http://schemas.sap.com/sapui5/extension/sap.ui.core.CustomData/1"; /** * Static utility class to access XMLNodes like ManagedObjects, * inside this classes oControl usually means XML node. * * @namespace sap.ui.core.util.reflection.XmlTreeModifier * @extends sap.ui.core.util.reflection.BaseTreeModifier * @private * @ui5-restricted * @since 1.56.0 */ const XmlTreeModifier = merge( {} /* target object, to avoid changing of original modifier */, BaseTreeModifier, /** @lends sap.ui.core.util.reflection.XmlTreeModifier */{ targets: "xmlTree", /** * @inheritDoc */ setVisible: function(oControl, bVisible) { if (bVisible) { oControl.removeAttribute("visible"); } else { oControl.setAttribute("visible", bVisible); } }, /** * @inheritDoc */ getVisible: function(oControl) { return XmlTreeModifier.getProperty(oControl, "visible"); }, /** * @inheritDoc */ setStashed: function(oControl, bStashed) { if (!bStashed) { oControl.removeAttribute("stashed"); } else { oControl.setAttribute("stashed", bStashed); } XmlTreeModifier.setVisible(oControl, !bStashed); return Promise.resolve(); }, /** * @inheritDoc */ getStashed: function(oControl) { return Promise.all([ XmlTreeModifier.getProperty(oControl, "stashed"), XmlTreeModifier.getProperty(oControl, "visible") ]).then(function(aProperties) { return !!aProperties[0] || !aProperties[1]; }); }, /** * @inheritDoc */ bindProperty: function(oControl, sPropertyName, vBindingInfos) { oControl.setAttribute(sPropertyName, "{" + vBindingInfos + "}"); }, /** * @inheritDoc */ unbindProperty: function(oControl, sPropertyName) { //reset the property oControl.removeAttribute(sPropertyName); }, _setProperty: function(oControl, sPropertyName, vPropertyValue, bEscapeBindingStrings) { let sValue = XmlTreeModifier._getSerializedValue(vPropertyValue); if (bEscapeBindingStrings) { sValue = XmlTreeModifier._escapeSpecialCharactersInString(sValue); } oControl.setAttribute(sPropertyName, sValue); }, /** * @inheritDoc */ setProperty: function(oControl, sPropertyName, vPropertyValue) { // binding strings in properties needs always to be escaped, triggered by the last parameter. // It is required to be compliant with setProperty functionality in JS case. There could be // properties provided as settings with existing bindings. Use the applySettings function in this case. XmlTreeModifier._setProperty(oControl, sPropertyName, vPropertyValue, true); }, /** * @inheritDoc */ getProperty: async function(oControl, sPropertyName) { let oType; let vPropertyValue = oControl.getAttribute(sPropertyName); const oMetadata = await XmlTreeModifier.getControlMetadata(oControl); const oPropertyInfo = oMetadata.getProperty(sPropertyName); if (oPropertyInfo) { //not a property like aggregation oType = oPropertyInfo.getType(); if ( sPropertyName === "value" && XmlTreeModifier.getControlType(oControl) === "sap.ui.core.CustomData" ) { const oKeyProperty = await XmlTreeModifier.getProperty(oControl, "key"); if (oKeyProperty === "sap-ui-custom-settings") { oType = DataType.getType("object"); } } if (vPropertyValue === null) { vPropertyValue = oPropertyInfo.getDefaultValue() || oType.getDefaultValue(); } else { // unescape binding like XMLTemplateProcessor const vUnescaped = BindingInfo.parse(vPropertyValue, undefined, true); // if it is a binding, return undefined as it has to be handled differently if (isPlainObject(vUnescaped)) { if (vUnescaped.path || vUnescaped.parts) { vPropertyValue = undefined; } else { vPropertyValue = vUnescaped; } } else { vPropertyValue = oType.parseValue(vUnescaped || vPropertyValue); } } } return vPropertyValue; }, /** * @inheritDoc */ isPropertyInitial: function(oControl, sPropertyName) { const vPropertyValue = oControl.getAttribute(sPropertyName); return (vPropertyValue == null); }, /** * @inheritDoc */ setPropertyBinding: function(oControl, sPropertyName, sPropertyBinding) { if (typeof sPropertyBinding !== "string") { throw new Error("For XML, only strings are supported to be set as property binding."); } oControl.setAttribute(sPropertyName, sPropertyBinding); }, /** * @inheritDoc */ getPropertyBinding: function(oControl, sPropertyName) { const vPropertyValue = oControl.getAttribute(sPropertyName); if (vPropertyValue) { const vUnescaped = BindingInfo.parse(vPropertyValue, undefined, true); if (vUnescaped && (vUnescaped.path || vUnescaped.parts)) { return vUnescaped; } } return undefined; }, /** * @inheritDoc */ createAndAddCustomData: function(oControl, sCustomDataKey, sValue) { oControl.setAttributeNS(CUSTOM_DATA_NS, "custom.data.via.modifier:" + sCustomDataKey, XmlTreeModifier._escapeSpecialCharactersInString(sValue)); return Promise.resolve(); }, /** * @inheritDoc */ getCustomDataInfo: function(oControl, sCustomDataKey) { const oCustomData = oControl.attributes["custom.data.via.modifier:" + sCustomDataKey]; if (oCustomData) { return { customData: oCustomData, customDataValue: oCustomData.value }; } else { return {}; } }, /** * @inheritDoc */ createControl: async function(sClassName, oAppComponent, oView, oSelector, mSettings, bAsync) { const sId = XmlTreeModifier.getControlIdBySelector(oSelector, oAppComponent); if (!XmlTreeModifier.bySelector(oSelector, oAppComponent, oView)) { let sLocalName; const aClassNameParts = sClassName.split('.'); let sNamespaceURI = ""; if (aClassNameParts.length > 1) { sLocalName = aClassNameParts.pop(); sNamespaceURI = aClassNameParts.join('.'); } const oNewElementNode = oView.ownerDocument.createElementNS(sNamespaceURI, sLocalName); if (sId) { oNewElementNode.setAttribute("id", sId); } if (mSettings) { await XmlTreeModifier.applySettings(oNewElementNode, mSettings); } return oNewElementNode; } else { throw new Error("Can't create a control with duplicated ID " + sId); } }, /** * @inheritDoc */ applySettings: async function(oControl, mSettings) { const oMetadata = await XmlTreeModifier.getControlMetadata(oControl); const mMetadata = oMetadata.getJSONKeys(); Object.keys(mSettings).forEach(function(sKey) { const oKeyInfo = mMetadata[sKey]; const vValue = mSettings[sKey]; switch (oKeyInfo._iKind) { case 0: // PROPERTY // Settings provided as property could have some bindings that needs to be resolved by the core // and therefore they shouldn't be escaped by setProperty function. In opposite to the common // setProperty functionality! XmlTreeModifier._setProperty(oControl, sKey, vValue, false); break; // case 1: // SINGLE_AGGREGATION // XmlTreeModifier.insertAggregation(oControl, sKey, vValue); case 3: // SINGLE_ASSOCIATION XmlTreeModifier.setAssociation(oControl, sKey, vValue); break; default: throw new Error("Unsupported in applySettings on XMLTreeModifier: " + sKey); } }); }, /** * @inheritDoc */ _byId: function(sId, oView) { // If function defined and operational use getElementById(sId) of document or view to access control // ... Note: oView.ownerDocument.getElementById(sId) may fail under IE 11 indicating "permission denied" if (oView) { if (oView.ownerDocument && oView.ownerDocument.getElementById && oView.ownerDocument.getElementById(sId)) { return oView.ownerDocument.getElementById(sId); } return oView.querySelector("[id='" + sId + "']"); } return undefined; }, /** * @inheritDoc */ getId: function(oControl) { return oControl.getAttribute("id"); }, /** * @inheritDoc */ getParent: function(oControl) { const oParent = oControl.parentNode; if (oParent && !XmlTreeModifier.getId(oParent) && !XmlTreeModifier._isExtensionPoint(oParent)) { //go to the real control, jump over aggregation node return oParent.parentNode; } return oParent; }, /** * @inheritDoc */ _getLocalName: function(xmlElement) { // localName for standard browsers, baseName for IE, nodeName in the absence of namespaces return xmlElement.localName || xmlElement.baseName || xmlElement.nodeName; }, /** * @inheritDoc */ getControlType: function(oControl) { return XmlTreeModifier._getControlTypeInXml(oControl); }, /** * @inheritDoc */ setAssociation: function(vParent, sName, sId) { if (typeof sId !== "string"){ sId = XmlTreeModifier.getId(sId); } vParent.setAttribute(sName, sId); }, /** * @inheritDoc */ getAssociation: function(vParent, sName) { return vParent.getAttribute(sName); }, /** * @inheritDoc */ getAllAggregations: async function(oControl) { const oControlMetadata = await XmlTreeModifier.getControlMetadata(oControl); return oControlMetadata.getAllAggregations(); }, /** * @inheritDoc */ getAggregation: async function(oParent, sName) { let aChildren = []; const bSingleValueAggregation = await XmlTreeModifier._isSingleValueAggregation(oParent, sName); const oAggregationNode = await XmlTreeModifier._findAggregationNode(oParent, sName); if (oAggregationNode) { const aChildrenLocal = await XmlTreeModifier._getControlsInAggregation(oParent, oAggregationNode); aChildren = aChildrenLocal; } else { const bIsAltTypeAggregation = await XmlTreeModifier._isAltTypeAggregation(oParent, sName); if (bIsAltTypeAggregation && bSingleValueAggregation) { const oChild = await XmlTreeModifier.getProperty(oParent, sName); aChildren.push(oChild); } } if (sName === "customData") { //check namespaced attributes: let mCustomSettings; const aNewCustomData = Array.prototype.slice.call(oParent.attributes).reduce(function(aNamespacedCustomData, oAttribute) { const sLocalName = XmlTreeModifier._getLocalName(oAttribute); if (oAttribute.namespaceURI === CUSTOM_DATA_NS) { const oNewCustomData = oParent.ownerDocument.createElementNS("sap.ui.core","CustomData"); oNewCustomData.setAttribute("key", sLocalName); oNewCustomData.setAttribute("value", oAttribute.value); aNamespacedCustomData.push(oNewCustomData); } else if (oAttribute.namespaceURI && oAttribute.name.indexOf("xmlns:") !== 0 ) { // other, unknown namespace and not an xml namespace alias definition if (!mCustomSettings) { mCustomSettings = {}; } if (!mCustomSettings.hasOwnProperty(oAttribute.namespaceURI)) { mCustomSettings[oAttribute.namespaceURI] = {}; } mCustomSettings[oAttribute.namespaceURI][sLocalName] = oAttribute.nodeValue; } return aNamespacedCustomData; }, []); aChildren = aChildren.concat(aNewCustomData); //add custom settings as custom data "sap-ui-custom-settings" if (mCustomSettings) { const oNewCustomData = oParent.ownerDocument.createElementNS("sap.ui.core","CustomData"); oNewCustomData.setAttribute("key", "sap-ui-custom-settings"); XmlTreeModifier.setProperty(oNewCustomData, "value", mCustomSettings); aChildren.push(oNewCustomData); } } return bSingleValueAggregation ? aChildren[0] : aChildren; }, /** * @inheritDoc */ insertAggregation: async function(oParent, sName, oObject, iIndex, oView, bSkipAdjustIndex) { const oFoundAggregationNode = await XmlTreeModifier._findAggregationNode(oParent, sName); return insertAggregation(oParent, sName, oObject, iIndex, oView, bSkipAdjustIndex, oFoundAggregationNode); }, /** * @inheritDoc */ removeAggregation: async function(oParent, sName, oObject) { const oAggregationNode = await XmlTreeModifier._findAggregationNode(oParent, sName); oAggregationNode.removeChild(oObject); }, /** * @inheritDoc */ moveAggregation: async function(oSourceParent, sSourceAggregationName, oTargetParent, sTargetAggregationName, oObject, iIndex, oView, bSkipAdjustIndex) { const oSourceAggregationNode = await XmlTreeModifier._findAggregationNode(oSourceParent, sSourceAggregationName); const oTargetAggregationNode = await XmlTreeModifier._findAggregationNode(oTargetParent, sTargetAggregationName); oSourceAggregationNode.removeChild(oObject); await insertAggregation(oTargetParent, sTargetAggregationName, oObject, iIndex, oView, bSkipAdjustIndex, oTargetAggregationNode); }, /** * @inheritDoc */ replaceAllAggregation: async function(oControl, sAggregationName, aNewControls) { const oAggregationNode = await XmlTreeModifier._findAggregationNode(oControl, sAggregationName); const aChildControls = await XmlTreeModifier._getControlsInAggregation(oControl, oAggregationNode); aChildControls.forEach(function(oChildControl) { oAggregationNode.removeChild(oChildControl); }); aNewControls.forEach((oObject) => { oAggregationNode.appendChild(oObject); }); }, /** * @inheritDoc */ removeAllAggregation: async function(oControl, sName) { const oAggregationNode = await XmlTreeModifier._findAggregationNode(oControl, sName); if (oControl === oAggregationNode) { const aChildControls = await XmlTreeModifier._getControlsInAggregation(oControl, oControl); aChildControls.forEach(function(oChildControl) { oControl.removeChild(oChildControl); }); } else { return oControl.removeChild(oAggregationNode); } }, /** * @private */ _findAggregationNode: async function(oParent, sName) { let oAggregationNode; const aChildren = XmlTreeModifier._children(oParent); for (let i = 0; i < aChildren.length; i++) { const oNode = aChildren[i]; if (oNode.localName === sName) { oAggregationNode = oNode; break; } } if (!oAggregationNode) { const bIsDefaultAggregation = await XmlTreeModifier._isDefaultAggregation(oParent, sName); if (bIsDefaultAggregation) { return oParent; } } return oAggregationNode; }, /** * @private */ _isDefaultAggregation: async function(oParent, sAggregationName) { const oControlMetadata = await XmlTreeModifier.getControlMetadata(oParent); const oDefaultAggregation = oControlMetadata.getDefaultAggregation(); return oDefaultAggregation && sAggregationName === oDefaultAggregation.name; }, /** * @private */ _isNotNamedAggregationNode: async function(oParent, oChildNode) { const mAllAggregationsMetadata = await XmlTreeModifier.getAllAggregations(oParent); const oAggregation = mAllAggregationsMetadata[oChildNode.localName]; return oParent.namespaceURI !== oChildNode.namespaceURI || !oAggregation; //same check as in XMLTemplateProcessor (handleChild) }, /** * @private */ _isSingleValueAggregation: async function(oParent, sAggregationName) { const mAllAggregationsMetadata = await XmlTreeModifier.getAllAggregations(oParent); const oAggregationMetadata = mAllAggregationsMetadata[sAggregationName]; return !oAggregationMetadata.multiple; }, /** * @private */ _isAltTypeAggregation: async function(oParent, sAggregationName) { const oControlMetadata = await XmlTreeModifier.getControlMetadata(oParent); const oAggregationMetadata = oControlMetadata.getAllAggregations()[sAggregationName]; return !!oAggregationMetadata.altTypes; }, /** * @private */ _isExtensionPoint: function(oControl) { return XmlTreeModifier._getControlTypeInXml(oControl) === "sap.ui.core.ExtensionPoint"; }, /** * @inheritDoc */ getControlMetadata: function(oControl) { return XmlTreeModifier._getControlMetadataInXml(oControl); }, /** * @private */ _getControlsInAggregation: async function(oParent, oAggregationNode) { // //convert NodeList to Array const aXmlChildren = [].slice.call(XmlTreeModifier._children(oAggregationNode)); const aChildren = await Promise.all(aXmlChildren.map(async (oChild) => { const bIsNotNamedAggregationNode = await XmlTreeModifier._isNotNamedAggregationNode(oParent, oChild); return bIsNotNamedAggregationNode ? oChild : undefined; })); return aChildren.filter((oChild) => !!oChild); }, /** * @private */ _children: function(oParent) { if (oParent.children) { return oParent.children; } else { const aChildren = []; for (let i = 0; i < oParent.childNodes.length; i++) { const oNode = oParent.childNodes[i]; if (oNode.nodeType === oNode.ELEMENT_NODE) { aChildren.push(oNode); } } return aChildren; } }, /** * @inheritDoc */ getBindingTemplate: async function(oControl, sAggregationName) { const oAggregationNode = await XmlTreeModifier._findAggregationNode(oControl, sAggregationName); if (oAggregationNode) { const aChildren = XmlTreeModifier._children(oAggregationNode); if (aChildren.length === 1){ return aChildren[0]; } } return undefined; }, /** * @inheritDoc */ updateAggregation: function(oControl, sAggregationName) { /*only needed in JS case to indicate binding (template) has changed, in XML case binding has not been created yet (see managed object)*/ }, /** * @inheritDoc */ findIndexInParentAggregation: async function(oControl) { // find the parent const oParent = XmlTreeModifier.getParent(oControl); if (!oParent) { return -1; } // we need the aggregation name in order to find all control nodes in the parent // which are relevant to this aggregation and skip all other possible nodes const sAggregationName = await XmlTreeModifier.getParentAggregationName(oControl, oParent); // get the relevant controls from the aggregation node let aControlsInAggregation = await XmlTreeModifier.getAggregation(oParent, sAggregationName); // if the result from the above is array: if (Array.isArray(aControlsInAggregation)) { // to harmonize behavior with JSControlTree, where stashed controls are not added to the parent aggregation const aPromises = aControlsInAggregation.map(async (oControl) => { if (XmlTreeModifier._isExtensionPoint(oControl)) { return oControl; } const oProperty = await XmlTreeModifier.getProperty(oControl, "stashed"); return !oProperty ? oControl : undefined; }); aControlsInAggregation = await Promise.all(aPromises); // find and return the correct index return aControlsInAggregation.filter((oControl) => { return !!oControl; }).indexOf(oControl); } else { // if aControlsInAggregation is not an array, then the aggregation is // of type 0..1 and aControlsInAggregation is the oControl provided // to the function initially, so its index is 0 return 0; } }, /** * @inheritDoc */ getParentAggregationName: async function(oControl, oParent) { // check if the control is in named aggregation node // again check just in case const bSameAsParentNode = oParent.isSameNode(oControl.parentNode); const bNotNamedAggregation = await XmlTreeModifier._isNotNamedAggregationNode(oParent, oControl); // check if the the control is in default aggregation // and get the name of the aggregation if (bNotNamedAggregation && bSameAsParentNode) { // the control is in the default aggregation of the parent const oMetadata = await XmlTreeModifier.getControlMetadata(oParent); return oMetadata.getDefaultAggregationName(); } else { // the aggregation name is provided and we can simply take it from the xml node return XmlTreeModifier._getLocalName(oControl.parentNode); } }, /** * @inheritDoc */ findAggregation: async function(oControl, sAggregationName) { const oMetadata = await XmlTreeModifier.getControlMetadata(oControl); const oAggregations = await oMetadata.getAllAggregations(); if (oAggregations) { return oAggregations[sAggregationName]; } return undefined; }, /** * @inheritDoc */ validateType: async function(oControl, mAggregationMetadata, oParent, sFragment, iIndex) { const sTypeOrInterface = mAggregationMetadata.type; const oAggregation = await XmlTreeModifier.getAggregation(oParent, mAggregationMetadata.name); // if aggregation is not multiple and already has element inside, then it is not valid for element if (mAggregationMetadata.multiple === false && oAggregation && oAggregation.length > 0) { return false; } const vControls = await Fragment.load({ definition: sFragment }); const aControls = !Array.isArray(vControls) ? [vControls] : vControls; const bReturn = aControls[iIndex].isA(sTypeOrInterface); aControls.forEach(function(oFragmentControl) { oFragmentControl.destroy(); }); return bReturn; }, /** * @inheritDoc */ instantiateFragment: async function(sFragment, sNamespace, oView) { const oInitialFragment = XMLHelper.parse(sFragment); const oFragment = await XmlTreeModifier._checkAndPrefixIdsInFragment(oInitialFragment, sNamespace); let aControls; if (oFragment.localName === "FragmentDefinition") { aControls = XmlTreeModifier._getElementNodeChildren(oFragment); } else { aControls = [oFragment]; } // check if there is already a field with the same ID and throw error if so aControls.forEach(function(oNode) { if (XmlTreeModifier._byId(oNode.getAttribute("id"), oView)) { throw Error("The following ID is already in the view: " + oNode.getAttribute("id")); } }); return aControls; }, /** * @inheritDoc */ templateControlFragment: async function(sFragmentName, mPreprocessorSettings) { const oFragment = await BaseTreeModifier._templateFragment( sFragmentName, mPreprocessorSettings ); return XmlTreeModifier._children(oFragment); }, /** * @inheritDoc */ destroy: function(oControl) { const oParent = oControl.parentNode; if (oParent) { oParent.removeChild(oControl); } }, _getFlexCustomData: function(oControl, sType) { if (!oControl){ return undefined; } return oControl.getAttributeNS("sap.ui.fl", sType); }, /** * @inheritDoc */ bindAggregation: function(oNode, sAggregationName, vBindingInfos, oView) { XmlTreeModifier.bindProperty(oNode, sAggregationName, vBindingInfos.path); return XmlTreeModifier.insertAggregation(oNode, sAggregationName, vBindingInfos.template, 0, oView); }, /** * @inheritDoc */ unbindAggregation: function(oNode, sAggregationName) { if (oNode.hasAttribute(sAggregationName)) { oNode.removeAttribute(sAggregationName); return XmlTreeModifier.removeAllAggregation(oNode, sAggregationName); } return Promise.resolve(); }, /** * @inheritDoc */ getExtensionPointInfo: async function(sExtensionPointName, oView) { if (oView && sExtensionPointName) { const aExtensionPoints = Array.prototype.slice.call(oView.getElementsByTagNameNS("sap.ui.core", "ExtensionPoint")); const aFilteredExtensionPoints = aExtensionPoints.filter(function(oExtPoint) { return oExtPoint.getAttribute("name") === sExtensionPointName; }); const oExtensionPoint = (aFilteredExtensionPoints.length === 1) ? aFilteredExtensionPoints[0] : undefined; if (oExtensionPoint) { const oParent = XmlTreeModifier.getParent(oExtensionPoint); const aProperties = await Promise.all([ XmlTreeModifier.getParentAggregationName(oExtensionPoint, oParent), XmlTreeModifier.findIndexInParentAggregation(oExtensionPoint) ]); // increase the index by 1 to get the index behind the extension point for xml-case const oExtensionPointInfo = { parent: oParent, aggregationName: aProperties[0], index: aProperties[1] + 1, defaultContent: Array.prototype.slice.call(XmlTreeModifier._children(oExtensionPoint)) }; return oExtensionPointInfo; } } return undefined; } }); async function insertAggregation(oParent, sName, oObject, iIndex, oView, bSkipAdjustIndex, oFoundAggregationNode) { let oAggregationNode; if (!oFoundAggregationNode) { // named aggregation must have the same namespace as the parent const sNamespaceURI = oParent.namespaceURI; // no ids for aggregation nodes => no need to pass id or component oAggregationNode = await XmlTreeModifier.createControl(sNamespaceURI + "." + sName, undefined, oView); oParent.appendChild(oAggregationNode); } else { oAggregationNode = oFoundAggregationNode; } if (!bSkipAdjustIndex) { const aChildren = oAggregationNode.children; let iOffset = 0; const iStopIndex = (iIndex < aChildren.length) ? iIndex : aChildren.length; for (let i = 0; i < iStopIndex; i++) { if (aChildren[i].namespaceURI === "sap.ui.core" && aChildren[i].tagName.includes("ExtensionPoint")) { iOffset = iOffset + 1 - aChildren[i].children.length; } } iIndex = iIndex + iOffset; } if (iIndex >= oAggregationNode.childElementCount) { oAggregationNode.appendChild(oObject); } else { const aReferenceNodes = await XmlTreeModifier._getControlsInAggregation(oParent, oAggregationNode); oAggregationNode.insertBefore(oObject, aReferenceNodes[iIndex]); } return undefined; } return XmlTreeModifier; }, /* bExport= */true);