@openui5/sap.ui.core
Version:
OpenUI5 Core Library sap.ui.core
387 lines (347 loc) • 12.5 kB
JavaScript
/*!
* OpenUI5
* (c) Copyright 2009-2021 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
// Provides class sap.ui.core.FocusHandler
sap.ui.define([
'../Device',
'../base/Object',
"sap/ui/dom/containsOrEquals",
"sap/base/Log",
"sap/ui/thirdparty/jquery",
// jQuery Plugin "control"
"sap/ui/dom/jquery/control"
],
function(Device, BaseObject, containsOrEquals, Log, jQuery) {
"use strict";
/**
* Constructs an instance of an sap.ui.core.FocusHandler.
* Keeps track of the focused element.
*
* @class Keeps track of the focused element.
* @param {Element} oRootRef e.g. document.body
* @param {sap.ui.core.Core} oCore Reference to the Core implementation
* @alias sap.ui.core.FocusHandler
* @extends sap.ui.base.Object
* @private
*/
var FocusHandler = BaseObject.extend("sap.ui.core.FocusHandler", /** @lends sap.ui.core.FocusHandler.prototype */ {
constructor : function(oRootRef, oCore) {
BaseObject.apply(this);
this.oCore = oCore;
// keep track of element currently in focus
this.oCurrent = null;
// keep track of the element previously had the focus
this.oLast = null;
// buffer the focus/blur events for correct order
this.aEventQueue = [];
// keep track of last focused element
this.oLastFocusedControlInfo = null;
// keep track of focused element which is using Renderer.apiVersion=2
this.oPatchingControlFocusInfo = null;
this.fEventHandler = jQuery.proxy(this.onEvent, this);
// initialize event handling
if (oRootRef.addEventListener && !Device.browser.msie) { //FF, Safari
oRootRef.addEventListener("focus", this.fEventHandler, true);
oRootRef.addEventListener("blur", this.fEventHandler, true);
} else { //IE
jQuery(oRootRef).on("activate", this.fEventHandler);
jQuery(oRootRef).on("deactivate", this.fEventHandler);
}
Log.debug("FocusHandler setup on Root " + oRootRef.type + (oRootRef.id ? ": " + oRootRef.id : ""), null, "sap.ui.core.FocusHandler");
}
});
/**
* Returns the Id of the control/element currently in focus.
* @return {string} the Id of the control/element currently in focus.
* @public
*/
FocusHandler.prototype.getCurrentFocusedControlId = function(){
var aCtrls = null;
try {
var $Act = jQuery(document.activeElement);
if ($Act.is(":focus")) {
aCtrls = $Act.control();
}
} catch (err) {
//escape eslint check for empty block
}
return aCtrls && aCtrls.length > 0 ? aCtrls[0].getId() : null;
};
/**
* Returns the focus info of the current focused control or the control with the given id, if exists.
*
* @see sap.ui.core.FocusHandler#restoreFocus
* @see sap.ui.core.FocusHandler#getCurrentFocusedControlId
* @param {string} [sControlId] the id of the control. If not given the id of the current focused control (if exists) is used
* @return {object} the focus info of the current focused control or the control with the given id, if exists.
* @private
*/
FocusHandler.prototype.getControlFocusInfo = function(sControlId){
sControlId = sControlId || this.getCurrentFocusedControlId();
if (!sControlId) {
return null;
}
var oControl = this.oCore && this.oCore.byId(sControlId);
if (oControl) {
return {
id : sControlId,
control : oControl,
info : oControl.getFocusInfo(),
type : oControl.getMetadata().getName(),
focusref : oControl.getFocusDomRef()
};
}
return null;
};
/**
* Stores the focus info of the current focused control which is using Renderer.apiVersion=2
*
* @see sap.ui.core.FocusHandler#restoreFocus
* @see sap.ui.core.FocusHandler#getControlFocusInfo
* @param {HTMLElement} oDomRef The DOM reference of the control where the rendering is happening
* @private
*/
FocusHandler.prototype.storePatchingControlFocusInfo = function(oDomRef) {
var oActiveElement = document.activeElement;
if (!oActiveElement || !oDomRef.contains(oActiveElement)) {
this.oPatchingControlFocusInfo = null;
} else {
this.oPatchingControlFocusInfo = this.getControlFocusInfo();
if (this.oPatchingControlFocusInfo) {
this.oPatchingControlFocusInfo.patching = true;
}
}
};
/**
* Returns the focus info of the last focused control which is using Renderer.apiVersion=2
*
* @see sap.ui.core.FocusHandler#storePatchingControlFocusInfo
* @private
*/
FocusHandler.prototype.getPatchingControlFocusInfo = function() {
return this.oPatchingControlFocusInfo;
};
/**
* If the given control is the last known focused control, the stored focusInfo is updated.
*
* @see sap.ui.core.FocusHandler#restoreFocus
* @see sap.ui.core.FocusHandler#getControlFocusInfo
* @param {string} oControl the control
* @private
*/
FocusHandler.prototype.updateControlFocusInfo = function(oControl){
if (oControl && this.oLastFocusedControlInfo && this.oLastFocusedControlInfo.control === oControl) {
var sControlId = oControl.getId();
this.oLastFocusedControlInfo = this.getControlFocusInfo(sControlId);
Log.debug("Update focus info of control " + sControlId, null, "sap.ui.core.FocusHandler");
}
};
/**
* Restores the focus to the last known focused control or to the given focusInfo, if possible.
*
* @see sap.ui.core.FocusHandler#getControlFocusInfo
* @param {object} [oControlFocusInfo] the focus info previously received from getControlFocusInfo
* @private
*/
FocusHandler.prototype.restoreFocus = function(oControlFocusInfo){
var oInfo = oControlFocusInfo || this.oLastFocusedControlInfo;
if (!oInfo) {
return;
}
var oControl = this.oCore && this.oCore.byId(oInfo.id);
var oFocusRef = oInfo.focusref;
if (oControl
&& oInfo.info
&& oControl.getMetadata().getName() == oInfo.type
&& (oInfo.patching
|| (oControl.getFocusDomRef() != oFocusRef
&& (oControlFocusInfo || /*!oControlFocusInfo &&*/ oControl !== oInfo.control)))) {
Log.debug("Apply focus info of control " + oInfo.id, null, "sap.ui.core.FocusHandler");
oInfo.control = oControl;
this.oLastFocusedControlInfo = oInfo;
// Do not store dom patch info in the last focused control info
delete this.oLastFocusedControlInfo.patching;
oControl.applyFocusInfo(oInfo.info);
} else {
Log.debug("Apply focus info of control " + oInfo.id + " not possible", null, "sap.ui.core.FocusHandler");
}
};
/**
* Destroy method of the Focus Handler.
* It unregisters the event handlers.
*
* @param {jQuery.Event} event the event that initiated the destruction of the FocusHandler
* @private
*/
FocusHandler.prototype.destroy = function(event) {
var oRootRef = event.data.oRootRef;
if (oRootRef) {
if (oRootRef.removeEventListener && !Device.browser.msie) { //FF, Safari
oRootRef.removeEventListener("focus", this.fEventHandler, true);
oRootRef.removeEventListener("blur", this.fEventHandler, true);
} else { //IE
jQuery(oRootRef).off("activate", this.fEventHandler);
jQuery(oRootRef).off("deactivate", this.fEventHandler);
}
}
this.oCore = null;
};
/**
* Handles the focus/blur events.
*
* @param oRootRef e.g. document.body
* @private
*/
FocusHandler.prototype.onEvent = function(oBrowserEvent){
var oEvent = jQuery.event.fix(oBrowserEvent);
Log.debug("Event " + oEvent.type + " reached Focus Handler (target: " + oEvent.target + (oEvent.target ? oEvent.target.id : "") + ")", null, "sap.ui.core.FocusHandler");
var type = (oEvent.type == "focus" || oEvent.type == "focusin" || oEvent.type == "activate") ? "focus" : "blur";
this.aEventQueue.push({type:type, controlId: getControlIdForDOM(oEvent.target)});
if (this.aEventQueue.length == 1) {
this.processEvent();
}
};
/**
* Processes the focus/blur events in the event queue.
*
* @private
*/
FocusHandler.prototype.processEvent = function(){
var oEvent = this.aEventQueue[0];
if (!oEvent) {
return;
}
try {
if (oEvent.type == "focus") {
this.onfocusEvent(oEvent.controlId);
} else if (oEvent.type == "blur") {
this.onblurEvent(oEvent.controlId);
}
} finally { //Ensure that queue is processed until it is empty!
this.aEventQueue.shift();
if (this.aEventQueue.length > 0) {
this.processEvent();
}
}
};
/**
* Processes the focus event taken from the event queue.
*
* @param {string} sControlId Id of the event related control
* @private
*/
FocusHandler.prototype.onfocusEvent = function(sControlId){
var oControl = this.oCore && this.oCore.byId(sControlId);
if (oControl) {
this.oLastFocusedControlInfo = this.getControlFocusInfo(sControlId);
Log.debug("Store focus info of control " + sControlId, null, "sap.ui.core.FocusHandler");
}
this.oCurrent = sControlId;
if (!this.oLast) {
// No last active element to be left...
return;
}
if (this.oLast != this.oCurrent) {
// if same control is focused again (e.g. while re-rendering) no focusleave is needed
triggerFocusleave(this.oLast, sControlId, this.oCore);
}
this.oLast = null;
};
/**
* Processes the blur event taken from the event queue.
*
* @param {string} sControlId Id of the event related control
* @private
*/
FocusHandler.prototype.onblurEvent = function(sControlId){
if (!this.oCurrent) {
// No current Item, so nothing to lose focus...
return;
}
this.oLast = sControlId;
this.oCurrent = null;
setTimeout(this["checkForLostFocus"].bind(this), 0);
};
/**
* Checks for lost focus and provides events in case of losing the focus.
* Called in delayed manner from {@link sap.ui.core.FocusHandler#onblurEvent}.
*
* @private
*/
FocusHandler.prototype.checkForLostFocus = function(){
if (this.oCurrent == null && this.oLast != null) {
triggerFocusleave(this.oLast, null, this.oCore);
}
this.oLast = null;
};
//***********************************************************
// Utility / convenience
//***********************************************************
/**
* Returns the id of the control/element to which the given DOM
* reference belongs to or <code>null</code> if no such
* control/element exists.
*
* @param {Element} oDOM the DOM reference
* @returns {string} Id of the control or null
* @private
*/
var getControlIdForDOM = function(oDOM){
var sId = jQuery(oDOM).closest("[data-sap-ui]").attr("id");
if (sId) {
return sId;
}
return null;
};
/**
* Calls the onsapfocusleave function on the control with id sControlId
* with the information about the given related control.
*
* @param {string} sControlId
* @param {string} sRelatedControlId
* @private
*/
var triggerFocusleave = function(sControlId, sRelatedControlId, oCore){
var oControl = sControlId ? oCore && oCore.byId(sControlId) : null;
if (oControl) {
var oRelatedControl = sRelatedControlId ? oCore.byId(sRelatedControlId) : null;
var oEvent = jQuery.Event("sapfocusleave");
oEvent.target = oControl.getDomRef();
oEvent.relatedControlId = oRelatedControl ? oRelatedControl.getId() : null;
oEvent.relatedControlFocusInfo = oRelatedControl ? oRelatedControl.getFocusInfo() : null;
//TODO: Cleanup the popup! The following is shit
var oControlUIArea = oControl.getUIArea();
var oUiArea = null;
if (oControlUIArea) {
oUiArea = oCore.getUIArea(oControlUIArea.getId());
} else {
var oPopupUIAreaDomRef = oCore.getStaticAreaRef();
if (containsOrEquals(oPopupUIAreaDomRef, oEvent.target)) {
oUiArea = oCore.getUIArea(oPopupUIAreaDomRef.id);
}
}
if (oUiArea) {
oUiArea._handleEvent(oEvent);
}
}
};
/*
* Checks if the passed DOM reference is nested in the active DOM of the document
* @param {Element} oDomRef The new active element
* @private
* @type boolean
* @returns {boolean} whether the passed DOM reference is nested in the active DOM of the document
*/
/*function isInActiveDom(oDomRef) {
assert(oDomRef != null);
var oCurrDomRef = oDomRef;
while(oCurrDomRef) {
if(oCurrDomRef === document) return true;
oCurrDomRef = oCurrDomRef.parentNode;
}
return false;
};*/
return FocusHandler;
});