[pve-devel] [RFC widget-toolkit 1/1] add panel/JournalView
Dominik Csapak
d.csapak at proxmox.com
Mon May 13 16:29:58 CEST 2019
there is an error in this patch(comment inline)
On 5/13/19 2:49 PM, Dominik Csapak wrote:
> similar to LogView, but expects the result from the /node/journal api call,
> which is an array of strings, with start/endcursor instead of
> an array of objects with line numbers
>
> the new api call also does not accept start line numbers and limit,
> so it is necessary to handle it differently
>
> Signed-off-by: Dominik Csapak <d.csapak at proxmox.com>
> ---
> Makefile | 1 +
> panel/JournalView.js | 335 +++++++++++++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 336 insertions(+)
> create mode 100644 panel/JournalView.js
>
> diff --git a/Makefile b/Makefile
> index d2b905c..049a1fb 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -40,6 +40,7 @@ JSSRC= \
> grid/PendingObjectGrid.js \
> panel/InputPanel.js \
> panel/LogView.js \
> + panel/JournalView.js \
> panel/RRDChart.js \
> panel/GaugeWidget.js \
> window/Edit.js \
> diff --git a/panel/JournalView.js b/panel/JournalView.js
> new file mode 100644
> index 0000000..21c1598
> --- /dev/null
> +++ b/panel/JournalView.js
> @@ -0,0 +1,335 @@
> +/*
> + * Display log entries in a panel with scrollbar
> + * The log entries are automatically refreshed via a background task,
> + * with newest entries comming at the bottom
> + */
> +Ext.define('Proxmox.panel.JournalView', {
> + extend: 'Ext.panel.Panel',
> + xtype: 'proxmoxJournalView',
> +
> + numEntries: 500,
> + lineHeight: 16,
> +
> + scrollToEnd: true,
> +
> + controller: {
> + xclass: 'Ext.app.ViewController',
> +
> + updateParams: function() {
> + var me = this;
> + var viewModel = me.getViewModel();
> + var since = viewModel.get('since');
> + var until = viewModel.get('until');
> +
> + since.setHours(0, 0, 0, 0);
> + until.setHours(0, 0, 0, 0);
> + until.setDate(until.getDate()+1);
> +
> + me.getView().loadTask.delay(200, undefined, undefined, [
> + false,
> + false,
> + Ext.Date.format(since, "U"),
> + Ext.Date.format(until, "U")
> + ]);
> + },
> +
> + scrollPosBottom: function() {
> + var view = this.getView();
> + var pos = view.getScrollY();
> + var maxPos = view.getScrollable().getMaxPosition().y;
> + return maxPos - pos;
> + },
> +
> + scrollPosTop: function() {
> + var view = this.getView();
> + return view.getScrollY();
> + },
> +
> + updateScroll: function(livemode, num, scrollPos, scrollPosTop) {
> + var me = this;
> + var view = me.getView();
> +
> + if (!livemode) {
> + setTimeout(function() { view.scrollTo(0, 0); }, 10);
> + } else if (view.scrollToEnd && scrollPos <= 0) {
> + setTimeout(function() { view.scrollTo(0, Infinity); }, 10);
> + } else if (!view.scrollToEnd && scrollPosTop < 20*view.lineHeight) {
> + setTimeout(function() { view.scrollTo(0, num*view.lineHeight + scrollPosTop); }, 10);
> + }
> + },
> +
> + updateView: function(lines, livemode, top) {
> + var me = this;
> + var view = me.getView();
> + var viewmodel = me.getViewModel();
> + if (viewmodel.get('livemode') !== livemode) {
> + return; // we switched mode, do not update the content
> + }
> + var contentEl = me.lookup('content');
> +
> + // save old scrollpositions
> + var scrollPos = me.scrollPosBottom();
> + var scrollPosTop = me.scrollPosTop();
> +
> + var newend = lines.shift();
> + var newstart = lines.pop();
> +
> + var num = lines.length;
> + var text = lines.map(Ext.htmlEncode).join('<br>');
> +
> + if (!livemode) {
> + view.content = num ? text : 'no content';
> + } else {
> + // update content
> + if (top && num) {
> + view.content = view.content ? text + '<br>' + view.content : text;
> + } else if (!top && num) {
> + view.content = view.content ? view.content + '<br>' + text : text;
> + }
> +
> + // update cursors
> + if (!top || !view.startcursor) {
> + view.startcursor = newstart;
> + }
> +
> + if (top || !view.endcursor) {
> + view.endcursor = newend;
> + }
> + }
> +
> + contentEl.update(view.content);
> +
> + me.updateScroll(livemode, num, scrollPos, scrollPosTop);
> + },
> +
> + doLoad: function(livemode, top, since, until) {
> + var me = this;
> + if (me.running) {
> + me.requested = true;
> + return;
> + }
> + me.running = true;
> + var view = me.getView();
> + var params = {
> + last: view.numEntries || 500,
this should be lastentries
> + };
> + if (livemode) {
> + if (!top && view.startcursor) {
> + params = {
> + startcursor: view.startcursor
> + };
> + } else if (view.endcursor) {
> + params.endcursor = view.endcursor;
> + }
> + } else {
> + params = {
> + since: since,
> + until: until
> + };
> + }
> + Proxmox.Utils.API2Request({
> + url: view.url,
> + params: params,
> + waitMsgTarget: (!livemode) ? view : undefined,
> + method: 'GET',
> + success: function(response) {
> + Proxmox.Utils.setErrorMask(me, false);
> + var lines = response.result.data;
> + me.updateView(lines, livemode, top);
> + me.running = false;
> + if (me.requested) {
> + me.requested = false;
> + view.loadTask.delay(200);
> + }
> + },
> + failure: function(response) {
> + var msg = response.htmlStatus;
> + Proxmox.Utils.setErrorMask(me, msg);
> + me.running = false;
> + if (me.requested) {
> + me.requested = false;
> + view.loadTask.delay(200);
> + }
> + }
> + });
> + },
> +
> + onScroll: function(x, y) {
> + var me = this;
> + var view = me.getView();
> + var viewmodel = me.getViewModel();
> + var livemode = viewmodel.get('livemode');
> + if (!livemode) {
> + return;
> + }
> +
> + if (me.scrollPosTop() < 20*view.lineHeight) {
> + view.scrollToEnd = false;
> + view.loadTask.delay(200, undefined, undefined, [true, true]);
> + } else if (me.scrollPosBottom() <= 1) {
> + view.scrollToEnd = true;
> + }
> + },
> +
> + init: function(view) {
> + var me = this;
> +
> + if (!view.url) {
> + throw "no url specified";
> + }
> +
> + var viewmodel = me.getViewModel();
> + var viewModel = this.getViewModel();
> + var since = new Date();
> + since.setDate(since.getDate() - 3);
> + viewModel.set('until', new Date());
> + viewModel.set('since', since);
> + me.lookup('content').setStyle('line-height', view.lineHeight + 'px');
> +
> + view.loadTask = new Ext.util.DelayedTask(me.doLoad, me, [true, false]);
> +
> + me.updateParams();
> + view.task = Ext.TaskManager.start({
> + run: function() {
> + if (!view.isVisible() || !view.scrollToEnd || !viewmodel.get('livemode')) {
> + return;
> + }
> +
> + if (me.scrollPosBottom() <= 1) {
> + view.loadTask.delay(200, undefined, undefined, [true, false]);
> + }
> + },
> + interval: 1000
> + });
> + },
> +
> + onLiveMode: function() {
> + var me = this;
> + var view = me.getView();
> + delete view.startcursor;
> + delete view.endcursor;
> + delete view.content;
> + me.getViewModel().set('livemode', true);
> + view.scrollToEnd = true;
> + me.updateView([], true, false);
> + },
> +
> + onTimespan: function() {
> + var me = this;
> + me.getViewModel().set('livemode', false);
> + me.updateView([], false);
> + }
> + },
> +
> + onDestroy: function() {
> + var me = this;
> + me.loadTask.cancel();
> + Ext.TaskManager.stop(me.task);
> + delete me.content;
> + },
> +
> + // for user to initiate a load from outside
> + requestUpdate: function() {
> + var me = this;
> + me.loadTask.delay(200);
> + },
> +
> + viewModel: {
> + data: {
> + livemode: true,
> + until: null,
> + since: null
> + }
> + },
> +
> + 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: {
> +
> + items: [
> + '->',
> + {
> + xtype: 'segmentedbutton',
> + items: [
> + {
> + text: gettext('Live Mode'),
> + bind: {
> + pressed: '{livemode}'
> + },
> + handler: 'onLiveMode',
> + },
> + {
> + text: gettext('Select Timespan'),
> + bind: {
> + pressed: '{!livemode}'
> + },
> + handler: 'onTimespan',
> + }
> + ]
> + },
> + {
> + xtype: 'datefield',
> + fieldLabel: gettext('Since'),
> + name: 'since_date',
> + reference: 'since',
> + format: 'Y-m-d',
> + bind: {
> + disabled: '{livemode}',
> + value: '{since}',
> + maxValue: '{until}'
> + }
> + },
> + {
> + xtype: 'datefield',
> + fieldLabel: gettext('Until'),
> + name: 'until_date',
> + reference: 'until',
> + format: 'Y-m-d',
> + bind: {
> + disabled: '{livemode}',
> + value: '{until}',
> + minValue: '{since}'
> + }
> + },
> + {
> + xtype: 'button',
> + text: 'Update',
> + reference: 'updateBtn',
> + handler: 'updateParams',
> + bind: {
> + disabled: '{livemode}'
> + }
> + }
> + ]
> + },
> +
> + items: [
> + {
> + xtype: 'box',
> + reference: 'content',
> + style: {
> + font: 'normal 11px tahoma, arial, verdana, sans-serif',
> + 'white-space': 'pre'
> + },
> + }
> + ]
> +});
>
More information about the pve-devel
mailing list