/*!
 * OpenUI5
 * (c) Copyright 2026 SAP SE or an SAP affiliate company.
 * Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
 */

// Provides base class sap.ui.core.Control for all controls
sap.ui.define([
	'./CustomStyleClassSupport',
	'./Core',
	'./Element',
	'./ElementRegistry',
	'./UIArea',
	'./StaticArea',
	'./RenderManager',
	'./BusyIndicatorUtils',
	'./BlockLayerUtils',
	"sap/base/future",
	"sap/base/Log",
	"sap/ui/performance/trace/Interaction",
	"sap/ui/thirdparty/jquery"
], function(
	CustomStyleClassSupport,
	Core,
	Element,
	ElementRegistry,
	UIArea,
	StaticArea,
	RenderManager,
	BusyIndicatorUtils,
	BlockLayerUtils,
	future,
	Log,
	Interaction,
	jQuery
) {
	"use strict";

	// soft dependency
	var ResizeHandler;

	/**
	 * Creates and initializes a new control with the given <code>sId</code> and settings.
	 *
	 * @param {string} [sId] Optional ID for the new control; generated automatically if no non-empty ID is given
	 *      Note: this can be omitted, no matter whether <code>mSettings</code> will be given or not!
	 * @param {object} [mSettings] Object with initial settings for the new control
	 * @public
	 *
	 * @class Base Class for Controls.
	 *
	 * Controls provide the following features:
	 * <ul>
	 * <li><b>Rendering</b>: the <code>RenderManager</code> only expects instances of class <code>Control</code>
	 *     in its {@link sap.ui.core.RenderManager#renderControl renderControl} method.
	 *     By convention, each control class has an associated static class that takes care of rendering
	 *     the control (its 'Renderer').</li>
	 * <li><b>show / hide</b>: a control can be hidden, although it is still part of the control tree,
	 *     see property {@link #getVisible visible}</li>
	 * <li><b>local busy indicator</b>: marks a control visually as 'busy', see properties {@link #getBusy busy}
	 *     and {@link #getBusyIndicatorDelay busyIndicatorDelay}</li>
	 * <li><b>field groups</b>: by assigning the same group ID to a set of editable controls, they form a
	 *     group which can be validated together. See property {@link #getFieldGroupIds fieldGroupIds}
	 *     and event {@link #event:validateFieldGroup validateFieldGroup}.
	 *     The term <i>field</i> was chosen as most often this feature will be used to group editable
	 *     fields in a form.</li>
	 *     See the documentation for {@link topic:5b0775397e394b1fb973fa207554003e Field Groups} for more details.
	 * <li><b>custom style classes</b>: all controls allow to add custom CSS classes to their rendered DOM
	 *     without modifying their renderer code. See methods {@link #addStyleClass addStyleClass},
	 *     {@link #removeStyleClass removeStyleClass}, {@link #toggleStyleClass toggleStyleClass}
	 *     and {@link #hasStyleClass hasStyleClass}.</br>
	 *     The necessary implementation is encapsulated in {@link sap.ui.core.CustomStyleClassSupport
	 *     CustomStyleClassSupport} and can be applied to selected element classes as well.</li>
	 * <li><b>browser events</b>: by calling the methods {@link #attachBrowserEvent attachBrowserEvent} and
	 *     {@link #detachBrowserEvent detachBrowserEvent}, consumers can let the control class take care of
	 *     registering / de-registering a given set of event listeners to the control's root DOM node.
	 *     The framework will adapt the registration whenever the DOM node changes (e.g. before or after
	 *     rendering or when the control is destroyed).</li>
	 * </ul>
	 *
	 * See section "{@link topic:8dcab0011d274051808f959800cabf9f Developing Controls}"
	 * in the documentation for an introduction to control development.
	 *
	 * @extends sap.ui.core.Element
	 * @abstract
	 * @author SAP SE
	 * @version 1.136.17
	 * @alias sap.ui.core.Control
	 */
	var Control = Element.extend("sap.ui.core.Control", /** @lends sap.ui.core.Control.prototype */ {

		metadata : {
			stereotype : "control",
			"abstract" : true,
			publicMethods: ["placeAt", "attachBrowserEvent", "detachBrowserEvent", "getControlsByFieldGroup", "triggerValidateFieldGroup", "checkFieldGroupIds"],
			library: "sap.ui.core",
			properties : {
				/**
				 * Whether the control is currently in blocked state.
				 *
				 * @deprecated since version 1.69 The blocked property is deprecated.
				 * There is no accessibility support for this property.
				 * Blocked controls should not be used inside Controls, which rely on keyboard navigation, e.g. List controls.
				 */
				"blocked" : {type: "boolean", defaultValue: false},
				/**
				 * Whether the control is currently in busy state.
				 */
				"busy" : {type: "boolean", defaultValue: false},
				/**
				 * The delay in milliseconds, after which the busy indicator will show up for this control.
				 */
				"busyIndicatorDelay" : {type: "int", defaultValue: 1000},

				/**
				 * The size of the BusyIndicator. For controls with a width smaller 3rem a
				 * <code>sap.ui.core.BusyIndicatorSize.Small</code> should be used.
				 * If the size could vary in width and the width could get smaller than 3rem, the
				 * <code>sap.ui.core.BusyIndicatorSize.Auto</code> option could be used.
				 * The default is set to <code>sap.ui.core.BusyIndicatorSize.Medium</code>
				 * For a full screen BusyIndicator use <code>sap.ui.core.BusyIndicatorSize.Large</code>.
				 * @since 1.54
				 */
				"busyIndicatorSize" : {type: "sap.ui.core.BusyIndicatorSize", defaultValue: 'Medium'},

				/**
				 * Whether the control should be visible on the screen.
				 *
				 * If set to false, a placeholder will be rendered to mark the location of the invisible
				 * control in the DOM of the current page. The placeholder will be hidden and have
				 * zero dimensions (<code>display: none</code>).
				 *
				 * Also see {@link module:sap/ui/core/InvisibleRenderer InvisibleRenderer}.
				 */
				"visible" : { type: "boolean", group : "Appearance", defaultValue: true },

				/**
				 * The IDs of a logical field group that this control belongs to.
				 *
				 * All fields in a logical field group should share the same <code>fieldGroupId</code>.
				 * Once a logical field group is left, the <code>validateFieldGroup</code> event is fired.
				 *
				 * For backward compatibility with older releases, field group IDs are syntactically not
				 * limited, but it is suggested to use only valid {@link sap.ui.core.ID}s.
				 *
				 * See {@link #attachValidateFieldGroup} or consult the
				 * {@link topic:5b0775397e394b1fb973fa207554003e Field Group} documentation.
				 *
				 * @since 1.31
				 */
				"fieldGroupIds" : { type: "string[]", defaultValue: [] }

			},
			events : {
				/**
				 * Event is fired if a logical field group defined by <code>fieldGroupIds</code> of a control was left
				 * or when the user explicitly pressed the key combination that triggers validation.
				 *
				 * By default, the <code>RETURN</code> key without any modifier keys triggers validation,
				 * see {@link #triggerValidateFieldGroup}.
				 *
				 * Listen to this event to validate data of the controls belonging to a field group.
				 * See {@link #setFieldGroupIds}, or consult the
				 * {@link topic:5b0775397e394b1fb973fa207554003e Field Group} documentation.
				 */
				validateFieldGroup : {
					enableEventBubbling:true,
					parameters : {

						/**
						 * field group IDs of the logical field groups to validate
						 */
						fieldGroupIds : {type : "string[]"}
					}
				}
			}
		},

		constructor : function(sId, mSettings) {

			// TODO initialization should happen in init
			// but many of the existing controls don't call super.init()
			// As a workaround I moved the initialization of bAllowTextSelection here
			// so that it doesn't overwrite settings in init() (e.g. ListBox)
			this.bAllowTextSelection = true;

			Element.apply(this,arguments);
			this.bOutput = this.getDomRef() != null; // whether this control has already produced output
			this._bOnBeforeRenderingPhase = false; // whether the control is in the onBeforeRendering phase
		},

		renderer : null // Control has no renderer

	});


	/**
	 * Defines a new subclass of Control with the name <code>sClassName</code> and enriches it with
	 * the information contained in <code>oClassInfo</code>.
	 *
	 * <code>oClassInfo</code> can contain the same information that {@link sap.ui.core.Element.extend} already accepts,
	 * plus the following <code>renderer</code> property:
	 *
	 * Example:
	 * <pre>
	 * Control.extend("sap.mylib.MyControl", {
	 *   metadata : {
	 *     library : "sap.mylib",
	 *     properties : {
	 *       text : "string",
	 *       width : "sap.ui.core.CSSSize"
	 *     }
	 *   },
	 *   renderer: {
	 *     apiVersion: 2,
	 *     render: function(oRM, oControl) {
	 *       oRM.openStart("div", oControl);
	 *       oRM.style("width", oControl.getWidth());
	 *       oRM.openEnd();
	 *       oRM.text(oControl.getText());
	 *       oRM.close("div");
	 *     }
	 *   }
	 * });
	 * </pre>
	 *
	 * There are multiple ways how a renderer can be specified:
	 * <ul>
	 * <li>As a <b>plain object</b>: The object will be used to create a new renderer by using {@link
	 *     sap.ui.core.Renderer.extend} to extend the renderer of the base class of this control. The new renderer
	 *     will have the same global name as this control class with the additional suffix 'Renderer'.<br>
	 *     <b>Note:</b> The <code>Renderer.extend</code> method expects a plain object (no prototype chain).</li>
	 * <li>As a <b>function</b>: The given function will be used as <code>render</code> function of a new renderer;
	 *     the renderer will be created in the same way as described for the <i>plain object</i> case.</li>
	 * <li>As a <b>ready-made renderer</b>, e.g. imported from the corresponding renderer module. As renderers
	 *     are simple objects (not instances of a specific class), some heuristic is used to distinguish
	 *     renderers from the <i>plain object</i> case above: An object is assumed to be a ready-made renderer
	 *     when it has a <code>render</code> function and either is already exposed under the expected global
	 *     name or has an <code>extend</code> method.</li>
	 * <li>As a <b>fully qualified name</b>: The name will be looked up as a global property. If not defined, a
	 *     module name will be derived from the global name (dots replaced by slashes), the module will be required
	 *     and provides the renderer, either as AMD export or via the named global property.</li>
	 * <li><b>Omitting the <code>renderer</code> property</b> or setting it to <code>undefined</code>:
	 *     The fully qualified name of the renderer will be derived from the fully qualified name of the control
	 *     by adding the suffix "Renderer". The renderer then is retrieved in the same way as described for the
	 *     <i>fully qualified name</i> case.</li>
	 * <li><b><code>null</code> or empty string</b>: The control will have no renderer, a call to
	 *     <code>oControl.getMetadata().getRenderer()</code> will return <code>undefined</code>.</li>
	 * </ul>
	 *
	 * If the resulting renderer is incomplete (has no <code>render</code> function) or if it cannot be found at all,
	 * rendering of the control will be skipped.
	 *
	 * <b>Note:</b> The <code>apiVersion: 2</code> flag is required to enable in-place rendering technology.
	 * Before setting this property, please ensure that the constraints documented in section "Contract for
	 * Renderer.apiVersion 2" of the {@link sap.ui.core.RenderManager RenderManager} API documentation are
	 * fulfilled.
	 *
	 * @param {string} sClassName Name of the class to be created
	 * @param {object} [oClassInfo] Object literal with information about the class
	 * @param {function} [FNMetaImpl] Constructor function for the metadata object. If not given, it defaults to <code>sap.ui.core.ElementMetadata</code>.
	 * @returns {function} Constructor of the newly created class
	 *
	 * @public
	 * @static
	 * @name sap.ui.core.Control.extend
	 * @function
	 */

	/**
	 * Overrides {@link sap.ui.core.Element#clone Element.clone} to clone additional
	 * internal state.
	 *
	 * The additionally cloned information contains:
	 * <ul>
	 * <li>browser event handlers attached with {@link #attachBrowserEvent}</li>
	 * <li>text selection behavior</li>
	 * <li>style classes added with {@link #addStyleClass}</li>
	 * </ul>
	 *
	 * @param {string} [sIdSuffix] a suffix to be appended to the cloned element id
	 * @param {string[]} [aLocalIds] an array of local IDs within the cloned hierarchy (internally used)
	 * @returns {this} reference to the newly created clone
	 * @override
	 * @public
	 */
	Control.prototype.clone = function() {
		var oClone = Element.prototype.clone.apply(this, arguments);

		if ( this.aBindParameters ) {
			for (var i = 0, l = this.aBindParameters.length; i < l; i++) {
				var aParams = this.aBindParameters[i];
				oClone.attachBrowserEvent(aParams.sEventType, aParams.fnHandler, aParams.oListener !== this ? aParams.oListener : undefined);
			}
		}
		oClone.bAllowTextSelection = this.bAllowTextSelection;
		return oClone;
	};

	// must appear after clone() method and metamodel definition
	CustomStyleClassSupport.apply(Control.prototype);


	/**
	 * Checks whether the control is still active (part of the active DOM)
	 *
	 * @return {boolean} whether the control is still in the active DOM
	 * @private
	 */
	Control.prototype.isActive = function() {
		return document.getElementById(this.sId) != null;
	};

	/**
	 * Determines whether the control is in rendering phase.
	 *
	 * @returns {boolean}
	 * @private
	 */
	function isInRenderingPhase(oControl) {
		if (!oControl || !oControl.isA) {
			return false;
		}
		if (oControl.isA("sap.ui.core.Control")) {
			return oControl._bRenderingPhase;
		}
		return isInRenderingPhase(oControl.getParent());
	}

	/**
	 * Marks this control and its children for a re-rendering, usually because its state has changed and now differs
	 * from the rendered DOM.
	 *
	 * Managed settings (properties, aggregations, associations) automatically invalidate the corresponding object.
	 * Changing the state via the standard mutators, therefore, does not require an explicit call to <code>invalidate</code>.
	 *
	 * By default, all invalidations are buffered and processed together (asynchronously) in a new browser task.
	 *
	 * The <code>oOrigin</code> parameter was introduced to allow parent controls to limit their re-rendering to
	 * certain areas that have been invalidated by their children. As there is no strong guideline for control
	 * developers whether or not to provide the parameter, it is not a reliable source of information. It is,
	 * therefore, not recommended in general to use it, only in scenarios where a control and its descendants
	 * know each other very well (e.g. complex controls where parent and children have the same code owner).
	 *
	 * @param {sap.ui.base.ManagedObject} [oOrigin] Child control for which the method was called
	 * @protected
	 */
	Control.prototype.invalidate = function(oOrigin) {
		var oUIArea;

		// Invalidations that happen in the onBeforeRendering hook of controls can be ignored
		// since the rendering of the control is about to start.
		if ( this._bOnBeforeRenderingPhase ) {
			return;
		}

		// Mark the control that it is in a dirty state and requires rendering.
		// This flag will be used by the RenderManager to determine whether the rendering
		// is necessary for the child controls while they are getting rendered with their parents.
		// This will be cleared by the RenderManager when the control is rendered completely.
		this._bNeedsRendering = true;

		var oParent = this.getParent();
		if ( (this.bOutput || isInRenderingPhase(oParent)) && (oUIArea = this.getUIArea()) ) {
			// if this control has been rendered before (bOutput)
			// or if the invalidation happens while parent rendering (isInRenderingPhase(oParent))
			// and if it is contained in a UIArea (!!oUIArea)
			// then control re-rendering can be used (see UIArea.rerender() for details)
			//
			// The check for bOutput is necessary as the control
			// re-rendering needs to identify the previous rendering results.
			// Otherwise it wouldn't be able to replace them.
			//
			// Note about destroy(): when this control is currently in the process of being
			// destroyed, registering it for an autonomous re-rendering doesn't make sense.
			// In most cases, invalidation of the parent also doesn't make sense,
			// but there might be composite controls that rely on being invalidated when
			// a child is destroyed, so we keep the invalidation propagation untouched.
			if ( !this._bIsBeingDestroyed ) {
				oUIArea.addInvalidatedControl(this);
			}
		} else {
			// else we bubble up the hierarchy
			if (oParent && !oParent.isInvalidateSuppressed() && (
					this.bOutput /* && !this.getUIArea() */ ||
					/* !this.bOutput && */ !(this.getVisible && this.getVisible() === false))) {

				// Note: the two comments in the condition above show additional conditions
				//       that help to understand the logic. As they are always fulfilled,
				//       they have been omitted for better performance.
				//
				// If this control has a parent but either
				//  - has produced output before ('this.bOutput') but is not part of a UIArea (!this.getUIArea())
				//  - or if it didn't produce output (!this.bOutput') before and is/became visible
				// then invalidate the parent to request re-rendering
				//
				// The first commented condition is always true, otherwise the initial if condition
				// in this method would have been met. The second one must be true as well because of the
				// short evaluation logic of JavaScript. When bOutput is true the second half of the Or won't be processed.

				oParent.invalidate(this);
			}
		}
	};

	/**
	 * Synchronously updates the DOM of this control to reflect the current object state.
	 *
	 * Note that this method can only be called when the control already has a DOM representation (it has
	 * been rendered before) and when the control still is assigned to a UIArea.
	 *
	 * @deprecated As of 1.70, using this method is no longer recommended, but calling it still
	 * causes a re-rendering of the control. Synchronous DOM updates via this method have several
	 * drawbacks: they only work when the control has been rendered before (no initial rendering
	 * possible), multiple state changes won't be combined automatically into a single re-rendering,
	 * they might cause additional layout thrashing, standard invalidation might cause another
	 * async re-rendering.
	 *
	 * The recommended alternative is to rely on invalidation and standard re-rendering.
	 *
	 * @protected
	 */
	Control.prototype.rerender = function() {
		this._bNeedsRendering = true;
		UIArea.rerenderControl(this);
	};

	// @see sap.ui.core.Element#getDomRef
	Control.prototype.getDomRef = function(sSuffix) {
		// while cloning we know that control DOM does not exist
		if (this.bOutput === false && !this.oParent) {
			return null;
		}

		return Element.prototype.getDomRef.call(this, sSuffix);
	};

	/**
	 * Defines whether the user can select text inside this control.
	 * Defaults to <code>true</code> as long as this method has not been called.
	 *
	 * <b>Note:</b>This only works in Safari; for Firefox the element's style must
	 * be set to:
	 * <pre>
	 *   -moz-user-select: none;
	 * </pre>
	 * in order to prevent text selection.
	 *
	 * @param {boolean} bAllow whether to allow text selection or not
	 * @returns {this} Returns <code>this</code> to allow method chaining
	 * @public
	 */
	Control.prototype.allowTextSelection = function(bAllow) {
		this.bAllowTextSelection = bAllow;
		return this;
	};

	/**
	 * The string given as "sStyleClass" will be added to the "class" attribute of this control's root HTML element.
	 *
	 * This method is intended to be used to mark controls as being of a special type for which
	 * special styling can be provided using CSS selectors that reference this style class name.
	 *
	 * <pre>
	 * Example:
	 *    myButton.addStyleClass("myRedTextButton"); // add a CSS class to one button instance
	 *
	 * ...and in CSS:
	 *    .myRedTextButton {
	 *       color: red;
	 *    }
	 * </pre>
	 *
	 * This will add the CSS class "myRedTextButton" to the Button HTML and the CSS code above will then
	 * make the text in this particular button red.
	 *
	 * Only characters allowed inside HTML attributes are allowed.
	 * Quotes are not allowed and this method will ignore any strings containing quotes.
	 * Strings containing spaces are interpreted as multiple custom style classes which are split by space and can be removed
	 * individually later by calling removeStyleClass.
	 * Multiple calls with the same sStyleClass will have no different effect than calling once.
	 * If sStyleClass is null, empty string or it contains quotes, the call is ignored.
	 *
	 * @name sap.ui.core.Control.prototype.addStyleClass
	 * @function
	 *
	 * @param {string} sStyleClass the CSS class name to be added
	 * @returns {this} Returns <code>this</code> to allow method chaining
	 * @public
	 */

	/**
	 * Removes the given string from the list of custom style classes that have been set previously.
	 * Regular style classes like "sapUiBtn" cannot be removed.
	 *
	 * @name sap.ui.core.Control.prototype.removeStyleClass
	 * @function
	 *
	 * @param {string} sStyleClass the style to be removed
	 * @returns {this} Returns <code>this</code> to allow method chaining
	 * @public
	 */

	/**
	 * The string given as "sStyleClass" will be be either added to or removed from the "class" attribute of this control's root HTML element,
	 * depending on the value of "bAdd": if bAdd is true, sStyleClass will be added.
	 * If bAdd is not given, sStyleClass will be removed if it is currently present and will be added if not present.
	 * If sStyleClass is null or empty string, the call is ignored.
	 *
	 * See addStyleClass and removeStyleClass for further documentation.
	 *
	 * @name sap.ui.core.Control.prototype.toggleStyleClass
	 * @function
	 *
	 * @param {string} sStyleClass the CSS class name to be added or removed
	 * @param {boolean} [bAdd] whether sStyleClass should be added (or removed); when this parameter is not given, sStyleClass will be toggled (removed, if present, and added if not present)
	 * @return {this} Returns <code>this</code> to allow method chaining
	 * @public
	 */

	/**
	 * Returns true if the given style class or all multiple style classes which are generated by splitting the given string with space are already set on the control
	 * via previous call(s) to addStyleClass().
	 *
	 * @name sap.ui.core.Control.prototype.hasStyleClass
	 * @function
	 *
	 * @param {string} sStyleClass the style to check for
	 * @returns {boolean} Whether the given style(s) has been set before
	 * @public
	 */


	/**
	 * Allows binding handlers for any native browser event to the root HTML element of this Control. This internally handles
	 * DOM element replacements caused by re-rendering.
	 *
	 * <b>IMPORTANT:</b></br>
	 * This should be only used as FALLBACK when the Control events do not cover a specific use-case! Always try using
	 * SAPUI5 control events, as e.g. accessibility-related functionality is then provided automatically.
	 * E.g. when working with a <code>sap.m.Button</code>, always use the Button's "press" event, not the native "click" event, because
	 * "press" is also guaranteed to be fired when certain keyboard activity is supposed to trigger the Button.
	 *
	 * In the event handler, <code>this</code> refers to the Control - not to the root DOM element like in jQuery. While the DOM element can
	 * be used and modified, the general caveats for working with SAPUI5 control DOM elements apply. In particular the DOM element
	 * may be destroyed and replaced by a new one at any time, so modifications that are required to have permanent effect may not
	 * be done. E.g. use {@link #addStyleClass} instead if the modification is of visual nature.
	 *
	 * Use {@link #detachBrowserEvent} to remove the event handler(s) again.
	 *
	 * @param {string} sEventType A string containing one or more JavaScript event types, such as "click" or "blur".
	 * @param {function} fnHandler A function to execute each time the event is triggered.
	 * @param {object} [oListener] The object, that wants to be notified, when the event occurs
	 * @returns {this} Returns <code>this</code> to allow method chaining
	 * @public
	 */
	Control.prototype.attachBrowserEvent = function(sEventType, fnHandler, oListener) {
		if (sEventType && (typeof (sEventType) === "string")) { // do nothing if the first parameter is empty or not a string
			if (typeof fnHandler === "function") {   // also do nothing if the second parameter is not a function
				// store the parameters for bind()
				if (!this.aBindParameters) {
					this.aBindParameters = [];
				}
				oListener = oListener || this;

				var fnProxy = fnHandler.bind(oListener);

				this.aBindParameters.push({
					sEventType: sEventType,
					fnHandler: fnHandler,
					oListener: oListener,
					fnProxy : fnProxy
				});

				if (!this._sapui_bInAfterRenderingPhase) {
					// if control is rendered, directly call bind()
					this.$().on(sEventType, fnProxy);
				}
			}
		}

		return this;
	};


	/**
	 * Removes event handlers which have been previously attached using {@link #attachBrowserEvent}.
	 *
	 * Note: listeners are only removed, if the same combination of event type, callback function
	 * and context object is given as in the call to <code>attachBrowserEvent</code>.
	 *
	 * @param {string} sEventType A string containing one or more JavaScript event types, such as "click" or "blur".
	 * @param {function} fnHandler The function that is to be no longer executed.
	 * @param {object} [oListener] The context object that was given in the call to <code>attachBrowserEvent</code>.
	 * @returns {this} Returns <code>this</code> to allow method chaining
	 * @public
	 */
	Control.prototype.detachBrowserEvent = function(sEventType, fnHandler, oListener) {
		if (sEventType && (typeof (sEventType) === "string")) { // do nothing if the first parameter is empty or not a string
			if (typeof (fnHandler) === "function") {   // also do nothing if the second parameter is not a function
				var $ = this.$(),i,oParamSet;
				oListener = oListener || this;

				// remove the bind parameters from the stored array
				if (this.aBindParameters) {
					for (i = this.aBindParameters.length - 1; i >= 0; i--) {
						oParamSet = this.aBindParameters[i];
						if ( oParamSet.sEventType === sEventType  && oParamSet.fnHandler === fnHandler  &&  oParamSet.oListener === oListener ) {
							this.aBindParameters.splice(i, 1);
							// if control is rendered, directly call unbind()
							$.off(sEventType, oParamSet.fnProxy);
						}
					}
				}

			}
		}

		return this;
	};



	/**
	 * Returns a renderer for this control instance.
	 *
	 * It is retrieved using the RenderManager as done during rendering.
	 *
	 * @return {sap.ui.core.ControlRenderer} a Renderer suitable for this Control instance.
	 * @protected
	 */
	Control.prototype.getRenderer = function () {
		return RenderManager.getRenderer(this);
	};

	/**
	 * Puts <code>this</code> control into the specified container (<code>oRef</code>) at the given
	 * position (<code>oPosition</code>).
	 *
	 * First it is checked whether <code>oRef</code> is a container element / control (has a
	 * multiple aggregation with type <code>sap.ui.core.Control</code> and name 'content') or is an Id String
	 * of such a container.
	 * If this is not the case <code>oRef</code> can either be a Dom Reference or Id String of the UIArea
	 * (if it does not yet exist implicitly a new UIArea is created),
	 *
	 * The <code>oPosition</code> can be one of the following:
	 *
	 * <ul>
	 *  <li>"first": The control is added as the first element to the container.</li>
	 *  <li>"last": The control is added as the last element to the container (default).</li>
	 *  <li>"only": All existing children of the container are removed (not destroyed!) and the control is added as new child.</li>
	 *  <li><i>index</i>: The control is added at the specified <i>index</i> to the container.</li>
	 * </ul>
	 *
	 * @param {string|Element|sap.ui.core.Control} oRef container into which the control should be put
	 * @param {string|int} [vPosition="last"] Describes the position where the control should be put into the container
	 * @returns {this} Returns <code>this</code> to allow method chaining
	 * @public
	 */
	Control.prototype.placeAt = function(oRef, vPosition) {
		Core.ready(function() {
			// 1st try to resolve the oRef as a container control
			var oContainer = oRef;
			if (typeof oRef === "string") {
				oContainer = Element.getElementById(oRef);
			}

			if (oContainer instanceof Element) {
				if (!isSuitableAsContainer(oContainer)) {
					future.warningThrows("placeAt cannot be processed because container " + oContainer + " does not have an aggregation 'content'.");
					return this;
				}
			} else {
				// if no container control is found, use the corresponding UIArea
				if (oRef === StaticArea.STATIC_UIAREA_ID
					|| oRef && oRef.id === StaticArea.STATIC_UIAREA_ID) {
					oContainer = StaticArea.getUIArea();
				} else {
					oContainer = UIArea.create(oRef);
				}
			}

			if (typeof vPosition === "number") {
				oContainer.insertContent(this, vPosition);
			} else {
				vPosition = vPosition || "last"; //"last" is default
				switch (vPosition) {
					case "last":
						oContainer.addContent(this);
						break;
					case "first":
						oContainer.insertContent(this, 0);
						break;
					case "only":
						oContainer.removeAllContent();
						oContainer.addContent(this);
						break;
					default:
						future.warningThrows("Position " + vPosition + " is not supported for function placeAt.");
				}
			}
		}.bind(this));
		return this;
	};

	/**
	 * Checks whether the given UI5 element is suitable as a container for placeAt
	 * @param {sap.ui.core.Element} oContainer
	 * @returns {boolean} Whether the given UI5 element is suitable as a container
	 * @private
	 */
	function isSuitableAsContainer(oContainer) {
		var oContentAggInfo = oContainer.getMetadata().getAggregation("content");
		if (oContentAggInfo) {
			return oContentAggInfo.multiple && oContentAggInfo.type === "sap.ui.core.Control";
		}
		// use duck typing to allow placeAt even when there's no 'content' aggregation available.
		// TODO This is a workaround for sap.ui.commons.AbsoluteLayout, abandon when it's removed
		return (
			typeof oContainer.addContent === "function"
			&& typeof oContainer.insertContent === "function"
			&& typeof oContainer.removeAllContent === "function"
		);
	}

	/*
	 * Event handling
	 */

	/**
	 * Cancels user text selection if text selection is disabled for this control.
	 * See the {@link #allowTextSelection} method.
	 * @private
	 */
	Control.prototype.onselectstart = function (oBrowserEvent) {
		if (!this.bAllowTextSelection) {
			oBrowserEvent.preventDefault();
			oBrowserEvent.stopPropagation();
		}
	};

	/*
	 * Rendering
	 */

	/**
	 * Function is called before the rendering of the control is started.
	 *
	 * Applications must not call this hook method directly, it is called by the framework.
	 *
	 * Subclasses of Control should override this hook to implement any necessary actions before the rendering.
	 *
	 * @param {jQuery.Event} oEvent onBeforeRendering event object
	 * @returns {void|undefined} This hook method must not have a return value. Return value <code>void</code> is deprecated since 1.120, as it does not force functions to <b>not</b> return something.
	 * 	This implies that, for instance, no async function returning a Promise should be used.
	 *
	 * 	<b>Note:</b> While the return type is currently <code>void|undefined</code>, any
	 *	implementation of this hook must not return anything but undefined. Any other
	 * 	return value will cause an error log in this version of UI5 and will fail in future
	 * 	major versions of UI5.
	 * @protected
	 */
	Control.prototype.onBeforeRendering = function() {
		// Before adding any implementation, please remember that this method was first implemented in release 1.54.
		// Therefore, many subclasses will not call this method at all.
		return undefined;
	};

	/**
	 * Function is called when the rendering of the control is completed.
	 *
	 * Applications must not call this hook method directly, it is called by the framework.
	 *
	 * Subclasses of Control should override this hook to implement any necessary actions after the rendering.
	 *
	 * @param {jQuery.Event} oEvent onAfterRendering event object
	 * @returns {void|undefined} This hook method must not have a return value. Return value <code>void</code> is deprecated since 1.120, as it does not force functions to <b>not</b> return something.
	 * 	This implies that, for instance, no async function returning a Promise should be used.
	 *
	 * 	<b>Note:</b> While the return type is currently <code>void|undefined</code>, any
	 *	implementation of this hook must not return anything but undefined. Any other
	 * 	return value will cause an error log in this version of UI5 and will fail in future
	 * 	major versions of UI5.
	 * @protected
	 */
	Control.prototype.onAfterRendering = function() {
		// Before adding any implementation, please remember that this method was first implemented in release 1.54.
		// Therefore, many subclasses will not call this method at all.
		return undefined;
	};

	/**
	 * Returns the DOMNode Id to be used for the "labelFor" attribute of the label.
	 *
	 * By default, this is the Id of the control itself.
	 *
	 * @return {string} Id to be used for the <code>labelFor</code>
	 * @public
	 */
	Control.prototype.getIdForLabel = function () {
		return this.getId();
	};

	Control.prototype.destroy = function(bSuppressInvalidate) {
		// ignore repeated calls
		if (this.bIsDestroyed) {
			return;
		}

		// avoid rerendering
		this._bIsBeingDestroyed = true;

		//Cleanup Busy Indicator
		this._cleanupBusyIndicator();

		// do not load ResizeHandler - if it isn't there, there should be no resize handler registrations
		ResizeHandler = ResizeHandler || sap.ui.require("sap/ui/core/ResizeHandler");
		if ( ResizeHandler ) {
			ResizeHandler.deregisterAllForControl(this.getId());
		}

		// Controls can have their visible-property set to "false" in which case the Element's destroy method will
		// fail to remove the placeholder content from the DOM. We have to remove it here in that case
		if (!this.getVisible()) {
			var oPlaceholder = document.getElementById(RenderManager.createInvisiblePlaceholderId(this));
			if (oPlaceholder && oPlaceholder.parentNode) {
				oPlaceholder.parentNode.removeChild(oPlaceholder);
			}
		}

		Element.prototype.destroy.call(this, bSuppressInvalidate);
	};


	// ---- local busy indicator handling ---------------------------------------------------------------------------------------
	var oRenderingDelegate = {
		/**
		 * @this {sap.ui.core.Control}
		 * @private
		 */
		onBeforeRendering: function() {
			if (this.getBusy()) {
				fnRemoveBusyIndicator.call(this);
			}
			// remove all block-layers to prevent leftover DOM elements and eventhandlers
			fnRemoveAllBlockLayers.call(this);
		},
		/**
		 * @this {sap.ui.core.Control}
		 * @private
		 */
		onAfterRendering: function () {
			if (this.getBlocked() && this.getDomRef() && !this.getDomRef("blockedLayer")) {
				this._oBlockState = BlockLayerUtils.block(this, this.getId() + "-blockedLayer", this._sBlockSection);
				jQuery(this._oBlockState.$blockLayer.get(0)).addClass("sapUiBlockLayerOnly");
			}
			if (this.getBusy() && this.getDomRef() && !this._busyIndicatorDelayedCallId && !this.getDomRef("busyIndicator")) {
				// Also use the BusyIndicatorDelay when a control is initialized
				// with "busy = true". If the delayed call was already initialized
				// skip any further call if the control was re-rendered while
				// the delay is running.
				var iDelay = this.getBusyIndicatorDelay();

				// Only do it via timeout if there is a delay. Otherwise append the
				// BusyIndicator immediately
				if (iDelay) {
					this._busyIndicatorDelayedCallId = setTimeout(fnAppendBusyIndicator.bind(this), iDelay);
				} else {
					// To win against the focus restoration from RenderManager, we have to set focus asynchronously.
					fnAppendBusyIndicator.call(this, /* bAsyncFocus */ true);
				}
			}
		}
	};

	function checkAndFocusBlockLayer() {
		if (this._oBusyBlockState && this.getDomRef(this._sBusySection)?.contains(document.activeElement)) {
			// Move focus to the busy indicator if the focus is currently within the busy control's DOM.
			this._oBusyBlockState.lastFocusPosition = document.activeElement;
			this._oBusyBlockState.$blockLayer.get(0).focus({ preventScroll: true });
		}
	}

	/**
	 * Add busy indicator to DOM
	 *
	 * @param {boolean} [asyncFocus=false] whether focus should be set asynchronously.
	 * This is need to set the focus after the restoration from RenderManager
	 * @private
	 */
	function fnAppendBusyIndicator(bAsyncFocus) {

		// Only append if busy state is still set
		if (!this.getBusy()) {
			return;
		}

		var $this = this.$(this._sBusySection);

		//If there is a pending delayed call to append the busy indicator, we can clear it now
		if (this._busyIndicatorDelayedCallId) {
			clearTimeout(this._busyIndicatorDelayedCallId);
			delete this._busyIndicatorDelayedCallId;
		}

		// if no busy section/control jquery instance could be retrieved -> the control is not part of the dom anymore
		// this might happen in certain scenarios when e.g. a dialog is closed faster than the busyIndicatorDelay
		if (!$this || $this.length === 0) {
			Log.warning("BusyIndicator could not be rendered. The outer control instance is not valid anymore.");
			return;
		}

		if (this._sBlockSection === this._sBusySection) {
			if (this._oBlockState) {
				BusyIndicatorUtils.addHTML(this._oBlockState, this.getBusyIndicatorSize());

				BlockLayerUtils.toggleAnimationStyle(this._oBlockState, true);

				this._oBusyBlockState = this._oBlockState;

			} else {
				// BusyIndicator is the first blocking element created
				fnAddStandaloneBusyIndicator.call(this);
			}
		} else {
			// Standalone busy indicator
			fnAddStandaloneBusyIndicator.call(this);
		}

		if (bAsyncFocus) {
			setTimeout(checkAndFocusBlockLayer.bind(this), 0);
		} else {
			checkAndFocusBlockLayer.call(this);
		}
	}

	/**
	 * Adds a standalone block-layer. Might be shared with a BusyIndicator later on.
	 */
	function fnAddStandaloneBlockLayer () {
		this._oBlockState = BlockLayerUtils.block(this, this.getId() + "-blockedLayer", this._sBlockSection);
		jQuery(this._oBlockState.$blockLayer.get(0)).addClass("sapUiBlockLayerOnly");
	}

	/**
	 * Adds a standalone BusyIndicator.
	 * The block-layer code is able to recognize that a new block-layer is not needed.
	 */
	function fnAddStandaloneBusyIndicator () {
		// if there's already a busy block state, remove it first before creating a new one
		// the existing busy block state can't be reused, because the block layer DOM is removed by the renderer. A new
		// block layer needs to be created
		if (this._oBusyBlockState) {
			BlockLayerUtils.unblock(this._oBusyBlockState);
		}

		this._oBusyBlockState = BlockLayerUtils.block(this, this.getId() + "-busyIndicator", this._sBusySection);
		BusyIndicatorUtils.addHTML(this._oBusyBlockState, this.getBusyIndicatorSize());
	}

	/**
	 * Cleanup all block-layers.
	 * Doesn't matter if it's a busy- or regular block-layer.
	 * Used to remove all unnecessary DOM elements and event-handlers during destroy and before rerendering.
	 */
	function fnRemoveAllBlockLayers() {
		BlockLayerUtils.unblock(this._oBlockState);
		BlockLayerUtils.unblock(this._oBusyBlockState);

		delete this._oBlockState;
		delete this._oBusyBlockState;
	}

	/**
	 * Remove busy indicator from DOM
	 * @param {boolean} bForceRemoval Forces the removal of all Block layers
	 * @private
	 */
	function fnRemoveBusyIndicator(bForceRemoval) {
		// removing all block layers is done upon rerendering and destroy of the control
		if (bForceRemoval) {
			fnRemoveAllBlockLayers.call(this);
			return;
		}

		// Restore focus on last focus position, if possible
		let oLastFocusedElement;
		if (this._oBusyBlockState) {
			const oBlockLayerDOM = this._oBusyBlockState.$blockLayer.get(0);

			// Focus might be moved from the busy indicator
			// If it is still on the busy indicator, we restore the focus. Otherwise do nothing.
			if (oBlockLayerDOM === document.activeElement) {
				// Check if last focused DOM element is still available, restore focus on the DOM element
				// Otherwise, move focus to the control's DOM Ref
				if (jQuery(this._oBusyBlockState.lastFocusPosition).is(":sapFocusable")) {
					oLastFocusedElement = this._oBusyBlockState.lastFocusPosition;
				} else {
					oLastFocusedElement = Element.closestTo(this._oBusyBlockState.lastFocusPosition) || this;
				}
				oLastFocusedElement.focus();
			}
		}

		const $this = this.$(this._sBusySection);
		$this.removeClass('sapUiLocalBusy');
		$this.removeAttr("aria-busy");

		if (this._sBlockSection === this._sBusySection) {
			if (!this.getBlocked() && !this.getBusy()) {
				// Remove shared block state & busy block state reference
				fnRemoveAllBlockLayers.call(this);

			} else if (this.getBlocked()) {
				if (this._oBlockState || this._oBusyBlockState) {
					// Hide animation in shared block layer
					BlockLayerUtils.toggleAnimationStyle(this._oBlockState || this._oBusyBlockState, false);
					this._oBlockState = this._oBusyBlockState;
				}
			} else if (this._oBusyBlockState) {
				BlockLayerUtils.unblock(this._oBusyBlockState);

				delete this._oBusyBlockState;
			}
		} else if (this._oBusyBlockState) {
			// standalone busy block state
			BlockLayerUtils.unblock(this._oBusyBlockState);

			delete this._oBusyBlockState;
		}
	}

	/**
	 * Set the controls block state.
	 *
	 * @param {boolean} bBlocked The new blocked state to be set
	 * @returns {this} <code>this</code> to allow method chaining
	 *
	 * @private
	 * @ui5-restricted sap.ui.core, sap.m, sap.viz, sap.ui.rta, sap.ui.table
	 * @deprecated since version 1.69, the blocked property is deprecated.
	 * There is no accessibility support for this property.
	 * Blocked controls should not be used inside Controls, which rely on keyboard navigation, e.g. List controls.
	 */
	Control.prototype.setBlocked = function(bBlocked, sBlockedSection /* this is an internal parameter to apply partial blocking for a specific section of the control */) {
		//If the new state is already set, we don't need to do anything
		if (!!bBlocked == this.getProperty("blocked")) {
			return this;
		}
		this._sBlockSection = sBlockedSection || this._sBlockSection;

		//No rerendering - should be modeled as a non-invalidating property once we have that
		this.setProperty("blocked", bBlocked, /*bSuppressInvalidate*/ true);

		if (bBlocked) {
			this.addDelegate(oRenderingDelegate, false, this);
		} else if (!this.getBusy()) {
			// only remove delegate if control is not still in "busy" state
			this.removeDelegate(oRenderingDelegate);
		}

		//If no domref exists stop here.
		if (!this.getDomRef()) {
			return this;
		}

		if (bBlocked) {
			// blocking
			if (this._sBlockSection === this._sBusySection) {
				// only create a new block state if neither busy nor blocking blockState is present
				if (!this._oBusyBlockState && !this._oBlockState) {
					fnAddStandaloneBlockLayer.call(this);
				} else {
					Log.info("The control is already busy. Hence, no new block-layer was created for the shared section.");
				}
			} else {
				fnAddStandaloneBlockLayer.call(this);
			}
		} else {
			// unblocking
			if (this._sBlockSection === this._sBusySection) {
				if (!this.getBlocked() && !this.getBusy()) {
					// Remove shared block state & busy block state reference
					fnRemoveAllBlockLayers.call(this);

				} else if (this.getBusy()) {
					// Control or section is still busy, hence no removal required
					Log.info("The control is already busy. Hence, no new block-layer was created for the shared section.");

				}
			} else if (this._oBlockState) {
				// standalone block state
				BlockLayerUtils.unblock(this._oBlockState);

				delete this._oBlockState;
			}
		}

		return this;
	};

	/**
	 * Gets current value of property blocked.
	 * @returns {boolean} Whether the control is currently in blocked state. Default is 'false'.
	 * @public
	 *
	 * @deprecated since version 1.69, the blocked property is deprecated.
	 * There is no accessibility support for this property.
	 * Blocked controls should not be used inside Controls, which rely on keyboard navigation, e.g. List controls.
	 */
	Control.prototype.getBlocked = function() {
		return this.getProperty("blocked");
	};

	/**
	 * Set the controls busy state.
	 *
	 * <b>Note:</b> The busy state can't be set on controls (e.g. sap.m.ColumnListItem)
	 * which renderings have the following tags as DOM root element:
	 * area|base|br|col|embed|hr|img|input|keygen|link|menuitem|meta|param|source|track|wbr|tr
	 *
	 * @param {boolean} bBusy The new busy state to be set
	 * @returns {this} <code>this</code> to allow method chaining
	 * @public
	 */
	Control.prototype.setBusy = function (bBusy, sBusySection /* this is an internal parameter to apply partial local busy indicator for a specific section of the control */) {
		//If the new state is already set, we don't need to do anything
		if (!!bBusy == this.getProperty("busy")) {
			return this;
		}

		this._sBusySection = sBusySection || this._sBusySection;

		//No rerendering - should be modeled as a non-invalidating property once we have that
		this.setProperty("busy", bBusy, /*bSuppressInvalidate*/ true);

		if (bBusy) {
			Interaction.notifyShowBusyIndicator(this);
			this.addDelegate(oRenderingDelegate, false, this);
		} else {
			// only remove the delegate if the control is not still in "blocked" state
			if (!this.getProperty("blocked")) {
				this.removeDelegate(oRenderingDelegate);
			}

			//If there is a pending delayed call we clear it
			if (this._busyIndicatorDelayedCallId) {
				clearTimeout(this._busyIndicatorDelayedCallId);
				delete this._busyIndicatorDelayedCallId;
			}
		}

		//If no domref exists stop here.
		if (!this.getDomRef()) {
			return this;
		}

		if (bBusy) {
			if (this.getBusyIndicatorDelay() <= 0) {
				fnAppendBusyIndicator.call(this);
			} else {
				this._busyIndicatorDelayedCallId = setTimeout(fnAppendBusyIndicator.bind(this), this.getBusyIndicatorDelay());
			}
		} else {
			fnRemoveBusyIndicator.call(this);
			Interaction.notifyHideBusyIndicator(this);
		}
		return this;
	};

	/**
	 * Check if the control is currently in busy state.
	 *
	 * @public
	 * @deprecated As of 1.15, use {@link #getBusy} instead
	 * @returns {boolean}
	 * @function
	 */
	Control.prototype.isBusy = Control.prototype.getBusy;

	/**
	 * Define the delay, after which the busy indicator will show up.
	 *
	 * @public
	 * @param {int} iDelay The delay in ms
	 * @returns {this} <code>this</code> to allow method chaining
	 */
	Control.prototype.setBusyIndicatorDelay = function(iDelay) {
		// should be modeled as a non-invalidating property once we have that
		this.setProperty("busyIndicatorDelay", iDelay, /*bSuppressInvalidate*/ true);
		return this;
	};

	/**
	 * Cleanup all timers which might have been created by the busy indicator.
	 *
	 * @private
	 */
	Control.prototype._cleanupBusyIndicator = function() {
		//If there is a pending delayed call we clear it
		if (this._busyIndicatorDelayedCallId) {
			clearTimeout(this._busyIndicatorDelayedCallId);
			delete this._busyIndicatorDelayedCallId;
		}
		fnRemoveBusyIndicator.call(this, true);
	};


	// ---- field groups --------------------------------------------------------------------------------------

	/**
	 * Returns a copy of the field group IDs array. Modification of the field group IDs
	 * need to call {@link #setFieldGroupIds setFieldGroupIds} to apply the changes.
	 *
	 * @name sap.ui.core.Control.prototype.getFieldGroupIds
	 * @function
	 *
	 * @return {string[]} copy of the field group IDs
	 * @public
	 */

	/**
	 * Returns a list of all child controls with a field group ID.
	 * See {@link #checkFieldGroupIds checkFieldGroupIds} for a description of the
	 * <code>vFieldGroupIds</code> parameter.
	 * Associated controls are not taken into account.
	 *
	 * @param {string|string[]} [vFieldGroupIds] ID of the field group or an array of field group IDs to match
	 * @return {sap.ui.core.Control[]} The list of controls with a field group ID
	 * @public
	 */
	Control.prototype.getControlsByFieldGroupId = function(vFieldGroupIds) {
		return this.findAggregatedObjects(true, function(oElement) {
			if (oElement instanceof Control) {
				return oElement.checkFieldGroupIds(vFieldGroupIds);
			}
			return false;
		});
	};

	/**
	 * Returns whether this control belongs to a given combination of field groups.
	 *
	 * If the <code>vFieldGroupIds</code> parameter is not specified, the method checks whether this control belongs
	 * to <strong>any</strong> field group, that is, whether any field group ID is defined for it.
	 *
	 * If a list of field group IDs is specified, either as an array of strings or as a single string (interpreted as
	 * a comma separated list of IDs), then the method will check whether this control belongs to <strong>all</strong>
	 * given field groups. Accordingly, an empty list of IDs (empty array or empty string) will always return true.
	 *
	 * Note that a string value for <code>vFieldGroupIds</code> (comma separated list) will not be trimmed.
	 * All whitespace characters are significant, but in general not recommended in field group IDs.
	 *
	 * @param {string|string[]} [vFieldGroupIds] An array of field group IDs or a single string with a comma separated list of IDs to match
	 * @return {boolean} Whether the field group IDs defined for the control match the given ones
	 * @public
	 * @see {@link #setFieldGroupIds}
	 */
	Control.prototype.checkFieldGroupIds = function(vFieldGroupIds) {
		if (typeof vFieldGroupIds === "string") {
			// normalize single field group ID or a comma separated list of field group IDs to an array
			vFieldGroupIds = vFieldGroupIds ? vFieldGroupIds.split(",") : [];
		}
		var aFieldGroups = this._getFieldGroupIds();
		if (Array.isArray(vFieldGroupIds)) {
			// all given field group IDs must be defined for the control
			return vFieldGroupIds.every(function(sFieldGroupId) {
				return aFieldGroups.indexOf(sFieldGroupId) >= 0;
			});
		} else if ( !vFieldGroupIds ) {
			// no field group ID(s) given: check if any field group is defined for the control
			return aFieldGroups.length > 0;
		}
		return false;
	};

	/**
	 * Triggers the <code>validateFieldGroup</code> event for this control.
	 *
	 * Called by <code>sap.ui.core.UIArea</code> if a field group should be validated after it lost
	 * the focus or when the key combination was pressed that was configured to trigger validation.
	 *
	 * By default, the <code>RETURN</code> key without any modifier keys triggers validation.
	 * There's no public API to change that key combination.
	 *
	 * See {@link #attachValidateFieldGroup}.
	 *
	 * @param {string[]} aFieldGroupIds IDs of the field groups that should be validated
	 * @public
	 */
	Control.prototype.triggerValidateFieldGroup = function(aFieldGroupIds) {
		this.fireValidateFieldGroup({
			fieldGroupIds : aFieldGroupIds
		});
	};

	/**
	 * This function (if available on the concrete control) provides
	 * the current accessibility state of the control.
	 *
	 * Applications must not call this hook method directly, it is called by the framework.
	 *
	 * Subclasses of Control should implement this hook to provide any necessary accessibility information:
	 *
	 * <pre>
	 * MyControl.prototype.getAccessibilityInfo = function() {
	 *    return {
	 *      role: "textbox",
	 *      type: "date input",
	 *      description: "value",
	 *      focusable: true,
	 *      enabled: true,
	 *      editable: true,
	 *      required: true,
	 *      children: []
	 *    };
	 * };
	 * </pre>
	 *
	 * Note: The returned object provides the accessibility state of the control at the point in time when this function is called.
	 *
	 * @return {sap.ui.core.AccessibilityInfo} Current accessibility state of the control.
	 * @since 1.37.0
	 * @function
	 * @name sap.ui.core.Control.prototype.getAccessibilityInfo?
	 * @protected
	 */
	//Control.prototype.getAccessibilityInfo = function() { return null; };

	/**
	 * Returns a list of all controls with a field group ID.
	 * See {@link sap.ui.core.Control#checkFieldGroupIds Control.prototype.checkFieldGroupIds} for a description of the
	 * <code>vFieldGroupIds</code> parameter.
	 *
	 * If possible please use the respective method on a Control instance (see {@link sap.ui.core.Control#getControlsByFieldGroupId}).
	 * The control method only respects aggregated child controls, which is more effective and should be sufficient for most use-cases.
	 *
	 * @param {string|string[]} [vFieldGroupIds] ID of the field group or an array of field group IDs to match
	 * @return {sap.ui.core.Control[]} The list of controls with matching field group IDs
	 * @static
	 * @since 1.118
	 * @public
	 */
	Control.getControlsByFieldGroupId = function(vFieldGroupIds) {
		return ElementRegistry.filter((oElement) => {
			return oElement.isA("sap.ui.core.Control") && oElement.checkFieldGroupIds(vFieldGroupIds);
		});
	};

	return Control;

});
