diff --git a/datamodels/2.x/itop-portal-base/portal/public/js/portal_form_handler.js b/datamodels/2.x/itop-portal-base/portal/public/js/portal_form_handler.js
index bda0aeca28..bd88971eee 100644
--- a/datamodels/2.x/itop-portal-base/portal/public/js/portal_form_handler.js
+++ b/datamodels/2.x/itop-portal-base/portal/public/js/portal_form_handler.js
@@ -95,8 +95,7 @@ $(function()
_onFieldsTouched: function(oEvent)
{
this._super(oEvent);
- $('body').trigger('register_blocker.portal.itop', {'sBlockerId': this.element.attr('id'), 'sTargetElemSelector': '#' + this.element.closest('.modal').attr('id'), 'oTargetElemSelector': '#' + this.element.closest('.modal').attr('id'), 'sEventName': 'hide.bs.modal'});
- $('body').trigger('register_blocker.portal.itop', {'sBlockerId': this.element.attr('id'), 'sTargetElemSelector': 'document', 'oTargetElemSelector': document, 'sEventName': 'beforeunload'});
+ this._registerBlockers();
},
// Overload from parent class
_onSubmitClick: function(oEvent)
@@ -255,7 +254,7 @@ $(function()
// If everything is okay, we close the form and apply the submit rule.
if(oValidation.valid)
{
- $('body').trigger('unregister_blocker.portal.itop', {'sBlockerId': me.element.attr('id')});
+ me._unregisterBlockers();
// Checking if we have to redirect to another page
if(sRuleType === 'redirect')
@@ -301,7 +300,7 @@ $(function()
if(me.options.field_set.field_set('option', 'touched_fields').length > 0)
{
me._disableFormBeforeLoading();
- $('body').trigger('unregister_blocker.portal.itop', {'sBlockerId': me.element.attr('id')});
+ me._unregisterBlockers();
$.post(
me.options.endpoint,
{
@@ -434,6 +433,27 @@ $(function()
window.location.href = sHomepageUrl;
}
},
+ _registerBlockers: function()
+ {
+ $('body').trigger('register_blocker.itop', {
+ 'sBlockerId': this.element.attr('id'),
+ 'sTargetElemSelector': '#' + this.element.closest('.modal').attr('id'),
+ 'oTargetElemSelector': '#' + this.element.closest('.modal').attr('id'),
+ 'sEventName': 'hide.bs.modal'
+ });
+ $('body').trigger('register_blocker.itop', {
+ 'sBlockerId': this.element.attr('id'),
+ 'sTargetElemSelector': 'document',
+ 'oTargetElemSelector': document,
+ 'sEventName': 'beforeunload'
+ });
+ },
+ _unregisterBlockers: function()
+ {
+ $('body').trigger('unregister_blocker.itop', {
+ 'sBlockerId': this.element.attr('id')
+ });
+ },
submit: function(oEvent)
{
this._onSubmitClick(oEvent);
diff --git a/datamodels/2.x/itop-portal-base/portal/public/js/portal_leave_handler.js b/datamodels/2.x/itop-portal-base/portal/public/js/portal_leave_handler.js
deleted file mode 100644
index c04d6d237e..0000000000
--- a/datamodels/2.x/itop-portal-base/portal/public/js/portal_leave_handler.js
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * Copyright (C) 2013-2023 Combodo SARL
- *
- * This file is part of iTop.
- *
- * iTop is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * iTop is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- */
-
-;
-$(function()
-{
- // the widget definition, where 'itop' is the namespace,
- // 'portal_leave_handler' the widget name
- $.widget( 'itop.portal_leave_handler',
- {
- // default options
- options:
- {
- 'message': 'allo la',
- },
- events: ['hide.bs.modal', 'beforeunload'],
- //[event]
- registered_blockers: {},
- // {id : {target : 'event1', target : 'event2'}}
-
- // the constructor
- _create: function()
- {
- var me =this;
- this.element
- .addClass('portal_leave_handler');
-
- this.element.on('register_blocker.portal.itop', function(oEvent, oData){
- me._onRegisterBlocker(oData.sBlockerId, oData.sTargetElemSelector, oData.oTargetElemSelector, oData.sEventName);
- });
- this.element.on('unregister_blocker.portal.itop', function(oEvent, oData){
- me._onUnregisterBlocker(oData.sBlockerId);
- });
-
- this.element.on('hide.bs.modal', function(oEvent) {return me._onLeaveHandler(oEvent);});
- window.addEventListener('beforeunload', function(oEvent) {return me._onLeaveHandler(oEvent);});
-
- this._super();
- },
- _onRegisterBlocker: function(sBlockerId, sTargetElemSelector, oTargetElemSelector, sEventName)
- {
- var aRegisteredBlock = {};
- aRegisteredBlock[sTargetElemSelector] = {'eventName': sEventName, 'selector': oTargetElemSelector};
- $.extend(
- aRegisteredBlock,
- this.registered_blockers[sBlockerId]
- );
- this.registered_blockers[sBlockerId] = aRegisteredBlock;
- },
- _onUnregisterBlocker: function(sBlockerId)
- {
- delete this.registered_blockers[sBlockerId];
- },
- _onLeaveHandler: function(oEvent)
- {
- var me = this;
- for(var aRegisteredBlocker in me.registered_blockers)
- {
- for(var sBlockerTarget in me.registered_blockers[aRegisteredBlocker])
- {
- if($(me.registered_blockers[aRegisteredBlocker][sBlockerTarget]['selector'])[0] === oEvent.target && me.registered_blockers[aRegisteredBlocker][sBlockerTarget]['eventName'].split('.')[0] === oEvent.type)
- {
- if(oEvent.type === 'beforeunload')
- {
- oEvent.returnValue = me.options.message;
- return;
- }
- else
- {
- var $bReturnValue = confirm(me.options.message);
- if ($bReturnValue)
- {
- $('body').trigger('unregister_blocker.portal.itop', {'sBlockerId': aRegisteredBlocker});
- }
- return $bReturnValue;
- }
- }
- }
- }
- },
- // events bound via _bind are removed automatically
- // revert other modifications here
- _destroy: function()
- {
- this.element
- .removeClass('portal_leave_handler');
-
- this._super();
- },
- // _setOptions is called with a hash of all options that are changing
- // always refresh when changing options
- _setOptions: function()
- {
- this._superApply(arguments);
- },
- // _setOption is called for each individual option that is changing
- _setOption: function( key, value )
- {
- this._super( key, value );
- },
- showOptions: function()
- {
- return this.options;
- }
- });
-});
-
diff --git a/datamodels/2.x/itop-portal-base/portal/templates/layout.html.twig b/datamodels/2.x/itop-portal-base/portal/templates/layout.html.twig
index 474e71e90c..d5959219a1 100644
--- a/datamodels/2.x/itop-portal-base/portal/templates/layout.html.twig
+++ b/datamodels/2.x/itop-portal-base/portal/templates/layout.html.twig
@@ -135,7 +135,7 @@
-
+
{# Selectize for sets #}
@@ -539,7 +539,12 @@
});
// Initialize confirmation message handler when a form with touched fields is closed
- oBodyElem.portal_leave_handler({'message': '{{ 'Portal:Form:Close:Warning'|dict_s }}'});
+ oBodyElem.leave_handler({
+ 'message': '{{ 'Portal:Form:Close:Warning'|dict_s }}',
+ 'extra_events': {
+ 'body': ['hide.bs.modal']
+ }
+ });
{% endblock %}
});
diff --git a/js/leave_handler.js b/js/leave_handler.js
new file mode 100644
index 0000000000..dd1d9fdff3
--- /dev/null
+++ b/js/leave_handler.js
@@ -0,0 +1,155 @@
+/*
+ * @copyright Copyright (C) 2010-2023 Combodo SARL
+ * @license http://opensource.org/licenses/AGPL-3.0
+ */
+
+/**
+ * Leave handler
+ *
+ * Prevent unexcepted loose of data when leaving a modal / page by prompting a confirmation to the user
+ *
+ * @since 3.1.0
+ * @internal
+ */
+;
+$(function()
+{
+ // the widget definition, where 'itop' is the namespace,
+ // 'leave_handler' the widget name
+ $.widget( 'itop.leave_handler',
+ {
+ // default options
+ options:
+ {
+ 'message': 'Do you really want to loose your changes?',
+ 'extra_events': {},
+ },
+ //[event]
+ events: {
+ 'window': ['beforeunload'],
+ 'body': [],
+ 'element': []
+ },
+ // {id : {target : 'event1', target : 'event2'}}
+ registered_blockers: {},
+
+ // the constructor
+ _create: function()
+ {
+ const me =this;
+ this.element
+ .addClass('leave_handler');
+
+ this.element.on('register_blocker.itop', function(oEvent, oData){
+ me._onRegisterBlocker(oData.sBlockerId, oData.sTargetElemSelector, oData.oTargetElemSelector, oData.sEventName);
+ });
+ this.element.on('unregister_blocker.itop', function(oEvent, oData){
+ me._onUnregisterBlocker(oData.sBlockerId);
+ });
+
+ // Merge default events with extra events from the consumer
+ // Note: There is no native way yet to recursively merge objects with arrays (and no duplicate)
+ for (const sTarget in this.options.extra_events) {
+ for (const sEvent of this.options.extra_events[sTarget]) {
+ // Ignore event already present
+ if (this.events[sTarget] === undefined || this.events[sTarget].indexOf(sEvent) !== -1) {
+ continue;
+ }
+
+ this.events[sTarget].push(sEvent);
+ }
+ }
+
+ // Register listeners for all events
+ for (const sTarget in this.events) {
+ if (sTarget === 'window') {
+ for (const sEvent of this.events[sTarget]) {
+ window.addEventListener(sEvent, function(oEvent) {
+ return me._onLeaveHandler(oEvent);
+ });
+ }
+ } else if (sTarget === 'body') {
+ for (const sEvent of this.events[sTarget]) {
+ $('body').on(sEvent, function(oEvent) {
+ return me._onLeaveHandler(oEvent);
+ });
+ }
+ } else if (sTarget === 'element') {
+ for (const sEvent of this.events[sTarget]) {
+ this.element.on(sEvent, function(oEvent) {
+ return me._onLeaveHandler(oEvent);
+ });
+ }
+ }
+ }
+
+ this._super();
+ },
+ _onRegisterBlocker: function(sBlockerId, sTargetElemSelector, oTargetElemSelector, sEventName)
+ {
+ let aRegisteredBlock = {};
+ aRegisteredBlock[sTargetElemSelector] = {'eventName': sEventName, 'selector': oTargetElemSelector};
+ $.extend(
+ aRegisteredBlock,
+ this.registered_blockers[sBlockerId]
+ );
+ this.registered_blockers[sBlockerId] = aRegisteredBlock;
+ },
+ _onUnregisterBlocker: function(sBlockerId)
+ {
+ delete this.registered_blockers[sBlockerId];
+ },
+ _onLeaveHandler: function(oEvent)
+ {
+ const me = this;
+ for(const aRegisteredBlocker in me.registered_blockers)
+ {
+ for(const sBlockerTarget in me.registered_blockers[aRegisteredBlocker])
+ {
+ if($(me.registered_blockers[aRegisteredBlocker][sBlockerTarget]['selector'])[0] === oEvent.target && me.registered_blockers[aRegisteredBlocker][sBlockerTarget]['eventName'].split('.')[0] === oEvent.type)
+ {
+ if(oEvent.type === 'beforeunload')
+ {
+ oEvent.returnValue = me.options.message;
+ return;
+ }
+ else
+ {
+ const $bReturnValue = confirm(me.options.message);
+ if ($bReturnValue)
+ {
+ $('body').trigger('unregister_blocker.itop', {'sBlockerId': aRegisteredBlocker});
+ }
+ return $bReturnValue;
+ }
+ }
+ }
+ }
+ },
+ // events bound via _bind are removed automatically
+ // revert other modifications here
+ _destroy: function()
+ {
+ this.element
+ .removeClass('leave_handler');
+
+ this._super();
+ },
+ // _setOptions is called with a hash of all options that are changing
+ // always refresh when changing options
+ _setOptions: function()
+ {
+ this._superApply(arguments);
+ },
+ // _setOption is called for each individual option that is changing
+ _setOption: function( key, value )
+ {
+ this._super( key, value );
+ },
+ showOptions: function()
+ {
+ return this.options;
+ }
+ });
+});
+