diff --git a/panel/LogView.js b/panel/LogView.js index 05a90a3..fe8d274 100644 --- a/panel/LogView.js +++ b/panel/LogView.js @@ -5,270 +5,256 @@ */ Ext.define('Proxmox.panel.LogView', { extend: 'Ext.panel.Panel', - - alias: ['widget.proxmoxLogView'], + xtype: 'proxmoxLogView', pageSize: 500, - + viewBuffer: 50, lineHeight: 16, - viewInfo: undefined, - scrollToEnd: true, - autoScroll: true, + // callback for load failure, used for ceph + failCallback: undefined, - layout: 'auto', + controller: { + xclass: 'Ext.app.ViewController', - bodyPadding: 5, - - getMaxDown: function(scrollToEnd) { - var me = this; - - var target = me.getTargetEl(); - var dom = target.dom; - if (scrollToEnd) { - 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; - } - - var el = me.dataCmp.el; - - if (me.viewInfo && me.viewInfo.start === start && - me.viewInfo.end === end && me.viewInfo.total === total && - me.viewInfo.textLength === text.length) { - return; // same content - } - - var maxDown = me.getMaxDown(); - var scrollToEnd = (maxDown <= 0) && me.scrollToEnd; - - 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) { - var me = this; - - var req_params = { - start: start, - 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'); - } - - Proxmox.Utils.API2Request({ - url: me.url, - params: req_params, - method: 'GET', - success: function(response) { - Proxmox.Utils.setErrorMask(me, false); - var list = response.result.data; - var total = response.result.total; - var first = 0, last = 0; - var text = ''; - Ext.Array.each(list, function(item) { - if (!first|| item.n < first) { - first = item.n; - } - if (!last || item.n > last) { - last = item.n; - } - text = text + Ext.htmlEncode(item.t) + "
"; - }); - - if (first && last && total) { - me.updateView(first -1 , last -1, total, text); - } else { - me.updateView(0, 0, 0, ''); - } - }, - failure: function(response) { - var msg = response.htmlStatus; - Proxmox.Utils.setErrorMask(me, msg); - } - }); - }, - - attemptLoad: function(start) { - var me = this; - if (!me.loadTask) { - me.loadTask = Ext.create('Ext.util.DelayedTask', me.doAttemptLoad, me, []); - } - me.loadTask.delay(200, me.doAttemptLoad, me, [start]); - }, - - requestUpdate: function(top, force) { - var me = this; - - if (top === undefined) { - 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) { + updateParams: function() { + var me = this; + var viewModel = me.getViewModel(); + var since = viewModel.get('since'); + var until = viewModel.get('until'); + if (viewModel.get('hide_timespan')) { 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"; - } - - // show logs from today back to 3 days ago per default - me.until_date = new Date(); - me.since_date = new Date(); - me.since_date.setDate(me.until_date.getDate() - 3); - - me.dataCmp = Ext.create('Ext.Component', { - style: 'font:normal 11px tahoma, arial, verdana, sans-serif;' + - 'line-height: ' + me.lineHeight.toString() + 'px; white-space: pre;' - }); - - me.task = Ext.TaskManager.start({ - run: function() { - if (!me.isVisible() || !me.scrollToEnd || !me.viewInfo) { - return; - } - - var maxDown = me.getMaxDown(); - if (maxDown > 0) { - return; - } - - me.requestUpdate(undefined, true); - }, - interval: 1000 - }); - - Ext.apply(me, { - items: me.dataCmp, - listeners: { - destroy: function() { - Ext.TaskManager.stop(me.task); - } + if (since > until) { + Ext.Msg.alert('Error', 'Since date must be less equal than Until date.'); + return; } - }); - if (me.log_select_timespan) { - me.tbar = ['->','Since: ', - { - xtype: 'datefield', - maxValue: me.until_date, - value: me.since_date, - name: 'since_date', - format: 'Y-m-d', - listeners: { - select: function(field, date) { - me.since_date_selected = date; - var until_field = field.up().down('field[name=until_date]'); - if (date > until_field.getValue()) { - until_field.setValue(date); - } - } - } - }, - 'Until: ', - { - xtype: 'datefield', - maxValue: me.until_date, - value: me.until_date, - name: 'until_date', - format: 'Y-m-d', - listeners: { - select: function(field, date) { - var since_field = field.up().down('field[name=since_date]'); - if (date < since_field.getValue()) { - since_field.setValue(date); - } - } - } - }, - { - xtype: 'button', - text: 'Update', - handler: function() { - 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(); - } - } - } - ]; + viewModel.set('params.since', Ext.Date.format(since, 'Y-m-d')); + viewModel.set('params.until', Ext.Date.format(until, 'Y-m-d') + ' 23:59:59'); + me.getView().loadTask.delay(200); + }, + + 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 view = me.getView(); + var viewModel = me.getViewModel(); + var content = me.lookup('content'); + var data = viewModel.get('data'); + + if (first === data.first && total === data.total && text.length === data.textlen) { + return; // same content, skip setting and scrolling + } + 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({ + url: me.getView().url, + params: viewModel.get('params'), + method: 'GET', + success: function(response) { + Proxmox.Utils.setErrorMask(me, false); + var total = response.result.total; + var lines = new Array(); + var first = Infinity; + + Ext.Array.each(response.result.data, function(line) { + if (first > line.n) { + first = line.n; + } + lines[line.n - 1] = Ext.htmlEncode(line.t); + }); + + lines.length = total; + me.updateView(lines.join('
'), first - 1, total); + }, + failure: function(response) { + if (view.failCallback) { + view.failCallback(response); + } else { + var msg = response.htmlStatus; + Proxmox.Utils.setErrorMask(me, msg); + } + } + }); + }, + + onScroll: function(x, y) { + var me = this; + var view = me.getView(); + 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); + } + }, + + init: function(view) { + var me = this; + + if (!view.url) { + throw "no url specified"; + } + + var viewModel = this.getViewModel(); + var since = new Date(); + since.setDate(since.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'); + + view.loadTask = new Ext.util.DelayedTask(me.doLoad, me); + + me.updateParams(); + view.task = Ext.TaskManager.start({ + run: function() { + if (!view.isVisible() || !view.scrollToEnd) { + return; + } + + if (me.scrollPosBottom() <= 1) { + view.loadTask.delay(200); + } + }, + interval: 1000 + }); } + }, + onDestroy: function() { + var me = this; + me.loadTask.cancel(); + Ext.TaskManager.stop(me.task); + }, - me.callParent(); - } + // for user to initiate a load from outside + 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', + name: 'since_date', + reference: 'since', + format: 'Y-m-d', + bind: { + value: '{since}', + maxValue: '{until}' + } + }, + 'Until: ', + { + xtype: 'datefield', + name: 'until_date', + reference: 'until', + format: 'Y-m-d', + bind: { + value: '{until}', + minValue: '{since}' + } + }, + { + xtype: 'button', + text: 'Update', + handler: 'updateParams' + } + ], + }, + + items: [ + { + xtype: 'box', + reference: 'content', + style: { + font: 'normal 11px tahoma, arial, verdana, sans-serif', + 'white-space': 'pre' + }, + } + ] }); diff --git a/window/TaskViewer.js b/window/TaskViewer.js index 39f42ad..b508882 100644 --- a/window/TaskViewer.js +++ b/window/TaskViewer.js @@ -198,8 +198,8 @@ Ext.define('Proxmox.window.TaskViewer', { var status = statgrid.getObjectValue('status'); if (status === 'stopped') { - logView.requestUpdate(undefined, true); logView.scrollToEnd = false; + logView.requestUpdate(); statstore.stopUpdate(); me.taskDone(statgrid.getObjectValue('exitstatus') == 'OK'); }