mirror of
https://git.proxmox.com/git/proxmox-widget-toolkit
synced 2025-05-11 00:32:22 +00:00
rework panel/LogView
use the view, viewmodel, controller style code, which makes the logic much more understandable this adds two features to it, namely a viewBuffer range, which controls the distance to the end of the data on which the panel loads the next data and an optional failCallback, which will be used for the cephlog this also fixes #2151 Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
This commit is contained in:
parent
f2a630d4c6
commit
65bb3b678f
364
panel/LogView.js
364
panel/LogView.js
@ -5,270 +5,256 @@
|
|||||||
*/
|
*/
|
||||||
Ext.define('Proxmox.panel.LogView', {
|
Ext.define('Proxmox.panel.LogView', {
|
||||||
extend: 'Ext.panel.Panel',
|
extend: 'Ext.panel.Panel',
|
||||||
|
xtype: 'proxmoxLogView',
|
||||||
alias: ['widget.proxmoxLogView'],
|
|
||||||
|
|
||||||
pageSize: 500,
|
pageSize: 500,
|
||||||
|
viewBuffer: 50,
|
||||||
lineHeight: 16,
|
lineHeight: 16,
|
||||||
|
|
||||||
viewInfo: undefined,
|
|
||||||
|
|
||||||
scrollToEnd: true,
|
scrollToEnd: true,
|
||||||
|
|
||||||
autoScroll: true,
|
// callback for load failure, used for ceph
|
||||||
|
failCallback: undefined,
|
||||||
|
|
||||||
layout: 'auto',
|
controller: {
|
||||||
|
xclass: 'Ext.app.ViewController',
|
||||||
|
|
||||||
bodyPadding: 5,
|
updateParams: function() {
|
||||||
|
|
||||||
getMaxDown: function(scrollToEnd) {
|
|
||||||
var me = this;
|
var me = this;
|
||||||
|
var viewModel = me.getViewModel();
|
||||||
var target = me.getTargetEl();
|
var since = viewModel.get('since');
|
||||||
var dom = target.dom;
|
var until = viewModel.get('until');
|
||||||
if (scrollToEnd) {
|
if (viewModel.get('hide_timespan')) {
|
||||||
dom.scrollTop = dom.scrollHeight - dom.clientHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
var maxDown = dom.scrollHeight - dom.clientHeight -
|
|
||||||
dom.scrollTop;
|
|
||||||
|
|
||||||
return maxDown;
|
|
||||||
},
|
|
||||||
|
|
||||||
updateView: function(start, end, total, text) {
|
|
||||||
var me = this;
|
|
||||||
|
|
||||||
if (me.destroyed) { // return if element is not there anymore
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var el = me.dataCmp.el;
|
if (since > until) {
|
||||||
|
Ext.Msg.alert('Error', 'Since date must be less equal than Until date.');
|
||||||
if (me.viewInfo && me.viewInfo.start === start &&
|
return;
|
||||||
me.viewInfo.end === end && me.viewInfo.total === total &&
|
|
||||||
me.viewInfo.textLength === text.length) {
|
|
||||||
return; // same content
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var maxDown = me.getMaxDown();
|
viewModel.set('params.since', Ext.Date.format(since, 'Y-m-d'));
|
||||||
var scrollToEnd = (maxDown <= 0) && me.scrollToEnd;
|
viewModel.set('params.until', Ext.Date.format(until, 'Y-m-d') + ' 23:59:59');
|
||||||
|
me.getView().loadTask.delay(200);
|
||||||
el.setStyle('padding-top', (start*me.lineHeight).toString() + 'px');
|
|
||||||
el.update(text);
|
|
||||||
me.dataCmp.setHeight(total*me.lineHeight);
|
|
||||||
|
|
||||||
if (scrollToEnd) {
|
|
||||||
me.getMaxDown(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
me.viewInfo = {
|
|
||||||
start: start,
|
|
||||||
end: end,
|
|
||||||
total: total,
|
|
||||||
textLength: text.length
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
|
|
||||||
doAttemptLoad: function(start) {
|
scrollPosBottom: function() {
|
||||||
|
var view = this.getView();
|
||||||
|
var pos = view.getScrollY();
|
||||||
|
var maxPos = view.getScrollable().getMaxPosition().y;
|
||||||
|
return maxPos - pos;
|
||||||
|
},
|
||||||
|
|
||||||
|
updateView: function(text, first, total) {
|
||||||
var me = this;
|
var me = this;
|
||||||
|
var view = me.getView();
|
||||||
|
var viewModel = me.getViewModel();
|
||||||
|
var content = me.lookup('content');
|
||||||
|
var data = viewModel.get('data');
|
||||||
|
|
||||||
var req_params = {
|
if (first === data.first && total === data.total && text.length === data.textlen) {
|
||||||
start: start,
|
return; // same content, skip setting and scrolling
|
||||||
limit: me.pageSize
|
|
||||||
};
|
|
||||||
|
|
||||||
if (me.log_select_timespan) {
|
|
||||||
// always show log until the end of the selected day
|
|
||||||
req_params.until = Ext.Date.format(me.until_date, 'Y-m-d') + ' 23:59:59';
|
|
||||||
req_params.since = Ext.Date.format(me.since_date, 'Y-m-d');
|
|
||||||
}
|
}
|
||||||
|
viewModel.set('data', {
|
||||||
|
first: first,
|
||||||
|
total: total,
|
||||||
|
textlen: text.length
|
||||||
|
});
|
||||||
|
|
||||||
|
var scrollPos = me.scrollPosBottom();
|
||||||
|
|
||||||
|
content.update(text);
|
||||||
|
|
||||||
|
if (view.scrollToEnd && scrollPos <= 0) {
|
||||||
|
// we use setTimeout to work around scroll handling on touchscreens
|
||||||
|
setTimeout(function() { view.scrollTo(0, Infinity); }, 10);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
doLoad: function() {
|
||||||
|
var me = this;
|
||||||
|
var view = me.getView();
|
||||||
|
var viewModel = me.getViewModel();
|
||||||
Proxmox.Utils.API2Request({
|
Proxmox.Utils.API2Request({
|
||||||
url: me.url,
|
url: me.getView().url,
|
||||||
params: req_params,
|
params: viewModel.get('params'),
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
success: function(response) {
|
success: function(response) {
|
||||||
Proxmox.Utils.setErrorMask(me, false);
|
Proxmox.Utils.setErrorMask(me, false);
|
||||||
var list = response.result.data;
|
|
||||||
var total = response.result.total;
|
var total = response.result.total;
|
||||||
var first = 0, last = 0;
|
var lines = new Array();
|
||||||
var text = '';
|
var first = Infinity;
|
||||||
Ext.Array.each(list, function(item) {
|
|
||||||
if (!first|| item.n < first) {
|
Ext.Array.each(response.result.data, function(line) {
|
||||||
first = item.n;
|
if (first > line.n) {
|
||||||
|
first = line.n;
|
||||||
}
|
}
|
||||||
if (!last || item.n > last) {
|
lines[line.n - 1] = Ext.htmlEncode(line.t);
|
||||||
last = item.n;
|
|
||||||
}
|
|
||||||
text = text + Ext.htmlEncode(item.t) + "<br>";
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (first && last && total) {
|
lines.length = total;
|
||||||
me.updateView(first -1 , last -1, total, text);
|
me.updateView(lines.join('<br>'), first - 1, total);
|
||||||
} else {
|
|
||||||
me.updateView(0, 0, 0, '');
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
failure: function(response) {
|
failure: function(response) {
|
||||||
|
if (view.failCallback) {
|
||||||
|
view.failCallback(response);
|
||||||
|
} else {
|
||||||
var msg = response.htmlStatus;
|
var msg = response.htmlStatus;
|
||||||
Proxmox.Utils.setErrorMask(me, msg);
|
Proxmox.Utils.setErrorMask(me, msg);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
attemptLoad: function(start) {
|
onScroll: function(x, y) {
|
||||||
var me = this;
|
var me = this;
|
||||||
if (!me.loadTask) {
|
var view = me.getView();
|
||||||
me.loadTask = Ext.create('Ext.util.DelayedTask', me.doAttemptLoad, me, []);
|
var viewModel = me.getViewModel();
|
||||||
|
|
||||||
|
var lineHeight = view.lineHeight;
|
||||||
|
var line = view.getScrollY()/lineHeight;
|
||||||
|
var start = viewModel.get('params.start');
|
||||||
|
var limit = viewModel.get('params.limit');
|
||||||
|
var viewLines = view.getHeight()/lineHeight;
|
||||||
|
|
||||||
|
var viewStart = Math.max(parseInt(line - 1 - view.viewBuffer, 10), 0);
|
||||||
|
var viewEnd = parseInt(line + viewLines + 1 + view.viewBuffer, 10);
|
||||||
|
|
||||||
|
if (viewStart < start || viewEnd > (start+limit)) {
|
||||||
|
viewModel.set('params.start',
|
||||||
|
Math.max(parseInt(line - limit/2 + 10, 10), 0));
|
||||||
|
view.loadTask.delay(200);
|
||||||
}
|
}
|
||||||
me.loadTask.delay(200, me.doAttemptLoad, me, [start]);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
requestUpdate: function(top, force) {
|
init: function(view) {
|
||||||
var me = this;
|
var me = this;
|
||||||
|
|
||||||
if (top === undefined) {
|
if (!view.url) {
|
||||||
var target = me.getTargetEl();
|
|
||||||
top = target.dom.scrollTop;
|
|
||||||
}
|
|
||||||
|
|
||||||
var viewStart = parseInt((top / me.lineHeight) - 1, 10);
|
|
||||||
if (viewStart < 0) {
|
|
||||||
viewStart = 0;
|
|
||||||
}
|
|
||||||
var viewEnd = parseInt(((top + me.getHeight())/ me.lineHeight) + 1, 10);
|
|
||||||
var info = me.viewInfo;
|
|
||||||
|
|
||||||
if (info && !force) {
|
|
||||||
if (viewStart >= info.start && viewEnd <= info.end) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var line = parseInt((top / me.lineHeight) - (me.pageSize / 2) + 10, 10);
|
|
||||||
if (line < 0) {
|
|
||||||
line = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
me.attemptLoad(line);
|
|
||||||
},
|
|
||||||
|
|
||||||
afterRender: function() {
|
|
||||||
var me = this;
|
|
||||||
|
|
||||||
me.callParent(arguments);
|
|
||||||
|
|
||||||
Ext.Function.defer(function() {
|
|
||||||
var target = me.getTargetEl();
|
|
||||||
target.on('scroll', function(e) {
|
|
||||||
me.requestUpdate();
|
|
||||||
});
|
|
||||||
me.requestUpdate(0);
|
|
||||||
}, 20);
|
|
||||||
},
|
|
||||||
|
|
||||||
initComponent : function() {
|
|
||||||
/*jslint confusion: true */
|
|
||||||
|
|
||||||
var me = this;
|
|
||||||
|
|
||||||
if (!me.url) {
|
|
||||||
throw "no url specified";
|
throw "no url specified";
|
||||||
}
|
}
|
||||||
|
|
||||||
// show logs from today back to 3 days ago per default
|
var viewModel = this.getViewModel();
|
||||||
me.until_date = new Date();
|
var since = new Date();
|
||||||
me.since_date = new Date();
|
since.setDate(since.getDate() - 3);
|
||||||
me.since_date.setDate(me.until_date.getDate() - 3);
|
viewModel.set('until', new Date());
|
||||||
|
viewModel.set('since', since);
|
||||||
|
viewModel.set('params.limit', view.pageSize);
|
||||||
|
viewModel.set('hide_timespan', !view.log_select_timespan);
|
||||||
|
me.lookup('content').setStyle('line-height', view.lineHeight + 'px');
|
||||||
|
|
||||||
me.dataCmp = Ext.create('Ext.Component', {
|
view.loadTask = new Ext.util.DelayedTask(me.doLoad, me);
|
||||||
style: 'font:normal 11px tahoma, arial, verdana, sans-serif;' +
|
|
||||||
'line-height: ' + me.lineHeight.toString() + 'px; white-space: pre;'
|
|
||||||
});
|
|
||||||
|
|
||||||
me.task = Ext.TaskManager.start({
|
me.updateParams();
|
||||||
|
view.task = Ext.TaskManager.start({
|
||||||
run: function() {
|
run: function() {
|
||||||
if (!me.isVisible() || !me.scrollToEnd || !me.viewInfo) {
|
if (!view.isVisible() || !view.scrollToEnd) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var maxDown = me.getMaxDown();
|
if (me.scrollPosBottom() <= 1) {
|
||||||
if (maxDown > 0) {
|
view.loadTask.delay(200);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
me.requestUpdate(undefined, true);
|
|
||||||
},
|
},
|
||||||
interval: 1000
|
interval: 1000
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
Ext.apply(me, {
|
onDestroy: function() {
|
||||||
items: me.dataCmp,
|
var me = this;
|
||||||
listeners: {
|
me.loadTask.cancel();
|
||||||
destroy: function() {
|
|
||||||
Ext.TaskManager.stop(me.task);
|
Ext.TaskManager.stop(me.task);
|
||||||
}
|
},
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (me.log_select_timespan) {
|
// for user to initiate a load from outside
|
||||||
me.tbar = ['->','Since: ',
|
requestUpdate: function() {
|
||||||
|
var me = this;
|
||||||
|
me.loadTask.delay(200);
|
||||||
|
},
|
||||||
|
|
||||||
|
viewModel: {
|
||||||
|
data: {
|
||||||
|
until: null,
|
||||||
|
since: null,
|
||||||
|
hide_timespan: false,
|
||||||
|
data: {
|
||||||
|
start: 0,
|
||||||
|
total: 0,
|
||||||
|
textlen: 0
|
||||||
|
},
|
||||||
|
params: {
|
||||||
|
start: 0,
|
||||||
|
limit: 500,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
layout: 'auto',
|
||||||
|
bodyPadding: 5,
|
||||||
|
scrollable: {
|
||||||
|
x: 'auto',
|
||||||
|
y: 'auto',
|
||||||
|
listeners: {
|
||||||
|
// we have to have this here, since we cannot listen to events
|
||||||
|
// of the scroller in the viewcontroller (extjs bug?), nor does
|
||||||
|
// the panel have a 'scroll' event'
|
||||||
|
scroll: {
|
||||||
|
fn: function(scroller, x, y) {
|
||||||
|
var controller = this.component.getController();
|
||||||
|
if (controller) { // on destroy, controller can be gone
|
||||||
|
controller.onScroll(x,y);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
buffer: 200
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
tbar: {
|
||||||
|
bind: {
|
||||||
|
hidden: '{hide_timespan}'
|
||||||
|
},
|
||||||
|
items: [
|
||||||
|
'->',
|
||||||
|
'Since: ',
|
||||||
{
|
{
|
||||||
xtype: 'datefield',
|
xtype: 'datefield',
|
||||||
maxValue: me.until_date,
|
|
||||||
value: me.since_date,
|
|
||||||
name: 'since_date',
|
name: 'since_date',
|
||||||
|
reference: 'since',
|
||||||
format: 'Y-m-d',
|
format: 'Y-m-d',
|
||||||
listeners: {
|
bind: {
|
||||||
select: function(field, date) {
|
value: '{since}',
|
||||||
me.since_date_selected = date;
|
maxValue: '{until}'
|
||||||
var until_field = field.up().down('field[name=until_date]');
|
|
||||||
if (date > until_field.getValue()) {
|
|
||||||
until_field.setValue(date);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
'Until: ',
|
'Until: ',
|
||||||
{
|
{
|
||||||
xtype: 'datefield',
|
xtype: 'datefield',
|
||||||
maxValue: me.until_date,
|
|
||||||
value: me.until_date,
|
|
||||||
name: 'until_date',
|
name: 'until_date',
|
||||||
|
reference: 'until',
|
||||||
format: 'Y-m-d',
|
format: 'Y-m-d',
|
||||||
listeners: {
|
bind: {
|
||||||
select: function(field, date) {
|
value: '{until}',
|
||||||
var since_field = field.up().down('field[name=since_date]');
|
minValue: '{since}'
|
||||||
if (date < since_field.getValue()) {
|
|
||||||
since_field.setValue(date);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
xtype: 'button',
|
xtype: 'button',
|
||||||
text: 'Update',
|
text: 'Update',
|
||||||
handler: function() {
|
handler: 'updateParams'
|
||||||
var until_field = me.down('field[name=until_date]');
|
|
||||||
var since_field = me.down('field[name=since_date]');
|
|
||||||
if (until_field.getValue() < since_field.getValue()) {
|
|
||||||
Ext.Msg.alert('Error',
|
|
||||||
'Since date must be less equal than Until date.');
|
|
||||||
until_field.setValue(me.until_date);
|
|
||||||
since_field.setValue(me.since_date);
|
|
||||||
} else {
|
|
||||||
me.until_date = until_field.getValue();
|
|
||||||
me.since_date = since_field.getValue();
|
|
||||||
me.requestUpdate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
items: [
|
||||||
me.callParent();
|
{
|
||||||
|
xtype: 'box',
|
||||||
|
reference: 'content',
|
||||||
|
style: {
|
||||||
|
font: 'normal 11px tahoma, arial, verdana, sans-serif',
|
||||||
|
'white-space': 'pre'
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
]
|
||||||
});
|
});
|
||||||
|
@ -198,8 +198,8 @@ Ext.define('Proxmox.window.TaskViewer', {
|
|||||||
var status = statgrid.getObjectValue('status');
|
var status = statgrid.getObjectValue('status');
|
||||||
|
|
||||||
if (status === 'stopped') {
|
if (status === 'stopped') {
|
||||||
logView.requestUpdate(undefined, true);
|
|
||||||
logView.scrollToEnd = false;
|
logView.scrollToEnd = false;
|
||||||
|
logView.requestUpdate();
|
||||||
statstore.stopUpdate();
|
statstore.stopUpdate();
|
||||||
me.taskDone(statgrid.getObjectValue('exitstatus') == 'OK');
|
me.taskDone(statgrid.getObjectValue('exitstatus') == 'OK');
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user