Merge pull request #3661 from Icinga/feature/preserve-scroll-position-upon-form-submits
Preserve scroll position upon form submits
(cherry picked from commit d8c4fcc517
)
Signed-off-by: Johannes Meyer <johannes.meyer@icinga.com>
This commit is contained in:
parent
4cdd83dcf4
commit
60e3201c66
|
@ -139,10 +139,15 @@
|
||||||
req.fail(this.onFailure);
|
req.fail(this.onFailure);
|
||||||
req.complete(this.onComplete);
|
req.complete(this.onComplete);
|
||||||
req.autorefresh = autorefresh;
|
req.autorefresh = autorefresh;
|
||||||
|
req.method = method;
|
||||||
req.action = action;
|
req.action = action;
|
||||||
req.addToHistory = true;
|
req.addToHistory = true;
|
||||||
req.progressTimer = progressTimer;
|
req.progressTimer = progressTimer;
|
||||||
|
|
||||||
|
if (url.match(/#/)) {
|
||||||
|
req.forceFocus = url.split(/#/)[1];
|
||||||
|
}
|
||||||
|
|
||||||
if (id) {
|
if (id) {
|
||||||
this.requests[id] = req;
|
this.requests[id] = req;
|
||||||
}
|
}
|
||||||
|
@ -349,10 +354,7 @@
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.redirectToUrl(
|
this.redirectToUrl(redirect, req.$target, req);
|
||||||
redirect, req.$target, req.url, req.getResponseHeader('X-Icinga-Rerender-Layout'), req.forceFocus,
|
|
||||||
req.getResponseHeader('X-Icinga-Refresh')
|
|
||||||
);
|
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -361,14 +363,20 @@
|
||||||
*
|
*
|
||||||
* @param {string} url
|
* @param {string} url
|
||||||
* @param {object} $target
|
* @param {object} $target
|
||||||
* @param {string} origin
|
* @param {XMLHttpRequest} referrer
|
||||||
* @param {boolean} rerenderLayout
|
|
||||||
*/
|
*/
|
||||||
redirectToUrl: function (url, $target, origin, rerenderLayout, forceFocus, autoRefreshInterval) {
|
redirectToUrl: function (url, $target, referrer) {
|
||||||
var icinga = this.icinga;
|
var icinga = this.icinga,
|
||||||
|
rerenderLayout,
|
||||||
|
autoRefreshInterval,
|
||||||
|
forceFocus,
|
||||||
|
origin;
|
||||||
|
|
||||||
if (typeof rerenderLayout === 'undefined') {
|
if (typeof referrer !== 'undefined') {
|
||||||
rerenderLayout = false;
|
rerenderLayout = referrer.getResponseHeader('X-Icinga-Rerender-Layout');
|
||||||
|
autoRefreshInterval = referrer.autoRefreshInterval;
|
||||||
|
forceFocus = referrer.forceFocus;
|
||||||
|
origin = referrer.url;
|
||||||
}
|
}
|
||||||
|
|
||||||
icinga.logger.debug(
|
icinga.logger.debug(
|
||||||
|
@ -416,6 +424,7 @@
|
||||||
var req = this.loadUrl(url, $target);
|
var req = this.loadUrl(url, $target);
|
||||||
req.forceFocus = url === origin ? forceFocus : null;
|
req.forceFocus = url === origin ? forceFocus : null;
|
||||||
req.autoRefreshInterval = autoRefreshInterval;
|
req.autoRefreshInterval = autoRefreshInterval;
|
||||||
|
req.referrer = referrer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -448,9 +457,8 @@
|
||||||
this.failureNotice = null;
|
this.failureNotice = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var url = req.url;
|
|
||||||
this.icinga.logger.debug(
|
this.icinga.logger.debug(
|
||||||
'Got response for ', req.$target, ', URL was ' + url
|
'Got response for ', req.$target, ', URL was ' + req.url
|
||||||
);
|
);
|
||||||
this.processNotificationHeader(req);
|
this.processNotificationHeader(req);
|
||||||
|
|
||||||
|
@ -570,6 +578,20 @@
|
||||||
rendered = true;
|
rendered = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var referrer = req.referrer;
|
||||||
|
if (typeof referrer === 'undefined') {
|
||||||
|
referrer = req;
|
||||||
|
}
|
||||||
|
|
||||||
|
var autoSubmit = false;
|
||||||
|
if (referrer.method === 'POST') {
|
||||||
|
var newUrl = this.icinga.utils.parseUrl(req.url);
|
||||||
|
var currentUrl = this.icinga.utils.parseUrl(req.$target.data('icingaUrl'));
|
||||||
|
if (newUrl.path === currentUrl.path && this.icinga.utils.objectsEqual(newUrl.params, currentUrl.params)) {
|
||||||
|
autoSubmit = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
req.$target.data('icingaUrl', req.url);
|
req.$target.data('icingaUrl', req.url);
|
||||||
|
|
||||||
this.icinga.ui.initializeTriStates($resp);
|
this.icinga.ui.initializeTriStates($resp);
|
||||||
|
@ -583,13 +605,10 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
// .html() removes outer div we added above
|
// .html() removes outer div we added above
|
||||||
this.renderContentToContainer($resp.html(), req.$target, req.action, req.autorefresh, req.forceFocus);
|
this.renderContentToContainer($resp.html(), req.$target, req.action, req.autorefresh, req.forceFocus, autoSubmit);
|
||||||
if (oldNotifications) {
|
if (oldNotifications) {
|
||||||
oldNotifications.appendTo($('#notifications'));
|
oldNotifications.appendTo($('#notifications'));
|
||||||
}
|
}
|
||||||
if (url.match(/#/)) {
|
|
||||||
setTimeout(this.icinga.ui.focusElement, 0, url.split(/#/)[1], req.$target);
|
|
||||||
}
|
|
||||||
if (newBody) {
|
if (newBody) {
|
||||||
this.icinga.ui.fixDebugVisibility().triggerWindowResize();
|
this.icinga.ui.fixDebugVisibility().triggerWindowResize();
|
||||||
}
|
}
|
||||||
|
@ -756,17 +775,22 @@
|
||||||
/**
|
/**
|
||||||
* Smoothly render given HTML to given container
|
* Smoothly render given HTML to given container
|
||||||
*/
|
*/
|
||||||
renderContentToContainer: function (content, $container, action, autorefresh, forceFocus) {
|
renderContentToContainer: function (content, $container, action, autorefresh, forceFocus, autoSubmit) {
|
||||||
// Container update happens here
|
// Container update happens here
|
||||||
var scrollPos = false;
|
var scrollPos = false;
|
||||||
var _this = this;
|
var _this = this;
|
||||||
var containerId = $container.attr('id');
|
var containerId = $container.attr('id');
|
||||||
|
|
||||||
var activeElementPath = false;
|
var activeElementPath = false;
|
||||||
|
var navigationAnchor = false;
|
||||||
var focusFallback = false;
|
var focusFallback = false;
|
||||||
|
|
||||||
if (forceFocus && forceFocus.length) {
|
if (forceFocus && forceFocus.length) {
|
||||||
activeElementPath = this.icinga.utils.getCSSPath($(forceFocus));
|
if (typeof forceFocus === 'string') {
|
||||||
|
navigationAnchor = forceFocus;
|
||||||
|
} else {
|
||||||
|
activeElementPath = this.icinga.utils.getCSSPath($(forceFocus));
|
||||||
|
}
|
||||||
} else if (document.activeElement && document.activeElement.id === 'search') {
|
} else if (document.activeElement && document.activeElement.id === 'search') {
|
||||||
activeElementPath = '#search';
|
activeElementPath = '#search';
|
||||||
} else if (document.activeElement
|
} else if (document.activeElement
|
||||||
|
@ -785,8 +809,8 @@
|
||||||
activeElementPath = this.icinga.utils.getCSSPath($activeElement);
|
activeElementPath = this.icinga.utils.getCSSPath($activeElement);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof containerId !== 'undefined') {
|
if (! forceFocus && typeof containerId !== 'undefined') {
|
||||||
if (autorefresh) {
|
if (autorefresh || autoSubmit) {
|
||||||
scrollPos = $container.scrollTop();
|
scrollPos = $container.scrollTop();
|
||||||
} else {
|
} else {
|
||||||
scrollPos = 0;
|
scrollPos = 0;
|
||||||
|
@ -844,7 +868,9 @@
|
||||||
}
|
}
|
||||||
this.icinga.ui.assignUniqueContainerIds();
|
this.icinga.ui.assignUniqueContainerIds();
|
||||||
|
|
||||||
if (! activeElementPath) {
|
if (navigationAnchor) {
|
||||||
|
setTimeout(this.icinga.ui.focusElement, 0, navigationAnchor, $container);
|
||||||
|
} else if (! activeElementPath) {
|
||||||
// Active element was not in this container
|
// Active element was not in this container
|
||||||
if (! autorefresh) {
|
if (! autorefresh) {
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
|
@ -862,7 +888,7 @@
|
||||||
var $activeElement = $(activeElementPath);
|
var $activeElement = $(activeElementPath);
|
||||||
|
|
||||||
if ($activeElement.length && $activeElement.is(':visible')) {
|
if ($activeElement.length && $activeElement.is(':visible')) {
|
||||||
$activeElement.focus();
|
$activeElement[0].focus({preventScroll: autorefresh});
|
||||||
} else if (! autorefresh) {
|
} else if (! autorefresh) {
|
||||||
if (focusFallback) {
|
if (focusFallback) {
|
||||||
$(focusFallback.parent).find(focusFallback.child).focus();
|
$(focusFallback.parent).find(focusFallback.child).focus();
|
||||||
|
@ -874,14 +900,22 @@
|
||||||
}, 0);
|
}, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (scrollPos !== false) {
|
|
||||||
setTimeout($container.scrollTop.bind($container), 0, scrollPos);
|
|
||||||
}
|
|
||||||
var icinga = this.icinga;
|
var icinga = this.icinga;
|
||||||
//icinga.events.applyHandlers($container);
|
//icinga.events.applyHandlers($container);
|
||||||
icinga.ui.initializeControls($container);
|
icinga.ui.initializeControls($container);
|
||||||
icinga.ui.fixControls();
|
icinga.ui.fixControls();
|
||||||
|
|
||||||
|
if (scrollPos !== false) {
|
||||||
|
$container.scrollTop(scrollPos);
|
||||||
|
|
||||||
|
// Fallback for browsers without support for focus({preventScroll: true})
|
||||||
|
setTimeout(function () {
|
||||||
|
if ($container.scrollTop() !== scrollPos) {
|
||||||
|
$container.scrollTop(scrollPos);
|
||||||
|
}
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
|
|
||||||
// Re-enable all click events (disabled as of performance reasons)
|
// Re-enable all click events (disabled as of performance reasons)
|
||||||
// $('*').off('click');
|
// $('*').off('click');
|
||||||
},
|
},
|
||||||
|
|
|
@ -567,18 +567,22 @@
|
||||||
|
|
||||||
$container.find('.controls').each(function() {
|
$container.find('.controls').each(function() {
|
||||||
var $controls = $(this);
|
var $controls = $(this);
|
||||||
if (! $controls.next('.fake-controls').length) {
|
if (! $controls.prev('.fake-controls').length) {
|
||||||
var $tabs = $controls.find('.tabs', $controls);
|
var $tabs = $controls.find('.tabs', $controls);
|
||||||
if ($tabs.length && $controls.children().length > 1 && ! $tabs.next('.tabs-spacer').length) {
|
if ($tabs.length && $controls.children().length > 1 && ! $tabs.next('.tabs-spacer').length) {
|
||||||
$tabs.after($('<div class="tabs-spacer"></div>'));
|
$tabs.after($('<div class="tabs-spacer"></div>'));
|
||||||
}
|
}
|
||||||
var $fakeControls = $('<div class="fake-controls"></div>');
|
var $fakeControls = $('<div class="fake-controls"></div>');
|
||||||
$fakeControls.height($controls.height()).css({
|
$fakeControls.height($controls.height()).css({
|
||||||
display: 'block'
|
// That's only temporary. It's reset in fixControls, which is called at the end of this
|
||||||
|
// function. Its purpose is to prevent the content from jumping up upon auto-refreshes.
|
||||||
|
// It works by making the fake-controls appear at the same vertical level as the controls
|
||||||
|
// and the height of the content then doesn't change when taking the controls out of the flow.
|
||||||
|
float: 'right'
|
||||||
});
|
});
|
||||||
$controls.css({
|
$controls.before($fakeControls).css({
|
||||||
position: 'fixed'
|
position: 'fixed'
|
||||||
}).after($fakeControls);
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -669,12 +673,15 @@
|
||||||
|
|
||||||
$container.find('.controls').each(function() {
|
$container.find('.controls').each(function() {
|
||||||
var $controls = $(this);
|
var $controls = $(this);
|
||||||
var $fakeControls = $controls.next('.fake-controls');
|
var $fakeControls = $controls.prev('.fake-controls');
|
||||||
|
$fakeControls.css({
|
||||||
|
float: '', // Set by initializeControls
|
||||||
|
height: $controls.height()
|
||||||
|
});
|
||||||
$controls.css({
|
$controls.css({
|
||||||
top: $container.offsetParent().position().top,
|
top: $container.offsetParent().position().top,
|
||||||
width: $fakeControls.outerWidth()
|
width: $fakeControls.outerWidth()
|
||||||
});
|
});
|
||||||
$fakeControls.height($controls.height());
|
|
||||||
});
|
});
|
||||||
|
|
||||||
var $statusBar = $container.children('.monitoring-statusbar');
|
var $statusBar = $container.children('.monitoring-statusbar');
|
||||||
|
|
|
@ -352,6 +352,14 @@
|
||||||
return keys;
|
return keys;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
objectsEqual: function equals(obj1, obj2) {
|
||||||
|
return Object.keys(obj1)
|
||||||
|
.concat(Object.keys(obj2))
|
||||||
|
.every(function (key) {
|
||||||
|
return obj1[key] === obj2[key];
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cleanup
|
* Cleanup
|
||||||
*/
|
*/
|
||||||
|
|
Loading…
Reference in New Issue