]> git.proxmox.com Git - proxmox-widget-toolkit.git/blob - src/panel/JournalView.js
language selector: increase only picker list view
[proxmox-widget-toolkit.git] / src / panel / JournalView.js
1 /*
2 * Display log entries in a panel with scrollbar
3 * The log entries are automatically refreshed via a background task,
4 * with newest entries coming at the bottom
5 */
6 Ext.define('Proxmox.panel.JournalView', {
7 extend: 'Ext.panel.Panel',
8 xtype: 'proxmoxJournalView',
9
10 numEntries: 500,
11 lineHeight: 16,
12
13 scrollToEnd: true,
14
15 controller: {
16 xclass: 'Ext.app.ViewController',
17
18 updateParams: function() {
19 let me = this;
20 let viewModel = me.getViewModel();
21 let since = viewModel.get('since');
22 let until = viewModel.get('until');
23
24 since.setHours(0, 0, 0, 0);
25 until.setHours(0, 0, 0, 0);
26 until.setDate(until.getDate()+1);
27
28 me.getView().loadTask.delay(200, undefined, undefined, [
29 false,
30 false,
31 Ext.Date.format(since, "U"),
32 Ext.Date.format(until, "U"),
33 ]);
34 },
35
36 scrollPosBottom: function() {
37 let view = this.getView();
38 let pos = view.getScrollY();
39 let maxPos = view.getScrollable().getMaxPosition().y;
40 return maxPos - pos;
41 },
42
43 scrollPosTop: function() {
44 let view = this.getView();
45 return view.getScrollY();
46 },
47
48 updateScroll: function(livemode, num, scrollPos, scrollPosTop) {
49 let me = this;
50 let view = me.getView();
51
52 if (!livemode) {
53 setTimeout(function() { view.scrollTo(0, 0); }, 10);
54 } else if (view.scrollToEnd && scrollPos <= 5) {
55 setTimeout(function() { view.scrollTo(0, Infinity); }, 10);
56 } else if (!view.scrollToEnd && scrollPosTop < 20 * view.lineHeight) {
57 setTimeout(function() { view.scrollTo(0, (num * view.lineHeight) + scrollPosTop); }, 10);
58 }
59 },
60
61 updateView: function(lines, livemode, top) {
62 let me = this;
63 let view = me.getView();
64 let viewmodel = me.getViewModel();
65 if (!viewmodel || viewmodel.get('livemode') !== livemode) {
66 return; // we switched mode, do not update the content
67 }
68 let contentEl = me.lookup('content');
69
70 // save old scrollpositions
71 let scrollPos = me.scrollPosBottom();
72 let scrollPosTop = me.scrollPosTop();
73
74 let newend = lines.shift();
75 let newstart = lines.pop();
76
77 let num = lines.length;
78 let text = lines.map(Ext.htmlEncode).join('<br>');
79
80 let contentChanged = true;
81
82 if (!livemode) {
83 if (num) {
84 view.content = text;
85 } else {
86 view.content = 'nothing logged or no timespan selected';
87 }
88 } else {
89 // update content
90 if (top && num) {
91 view.content = view.content ? text + '<br>' + view.content : text;
92 } else if (!top && num) {
93 view.content = view.content ? view.content + '<br>' + text : text;
94 } else {
95 contentChanged = false;
96 }
97
98 // update cursors
99 if (!top || !view.startcursor) {
100 view.startcursor = newstart;
101 }
102
103 if (top || !view.endcursor) {
104 view.endcursor = newend;
105 }
106 }
107
108 if (contentChanged) {
109 contentEl.update(view.content);
110 }
111
112 me.updateScroll(livemode, num, scrollPos, scrollPosTop);
113 },
114
115 doLoad: function(livemode, top, since, until) {
116 let me = this;
117 if (me.running) {
118 me.requested = true;
119 return;
120 }
121 me.running = true;
122 let view = me.getView();
123 let params = {
124 lastentries: view.numEntries || 500,
125 };
126 if (livemode) {
127 if (!top && view.startcursor) {
128 params = {
129 startcursor: view.startcursor,
130 };
131 } else if (view.endcursor) {
132 params.endcursor = view.endcursor;
133 }
134 } else {
135 params = {
136 since: since,
137 until: until,
138 };
139 }
140 Proxmox.Utils.API2Request({
141 url: view.url,
142 params: params,
143 waitMsgTarget: !livemode ? view : undefined,
144 method: 'GET',
145 success: function(response) {
146 if (me.isDestroyed) {
147 return;
148 }
149 Proxmox.Utils.setErrorMask(me, false);
150 let lines = response.result.data;
151 me.updateView(lines, livemode, top);
152 me.running = false;
153 if (me.requested) {
154 me.requested = false;
155 view.loadTask.delay(200);
156 }
157 },
158 failure: function(response) {
159 let msg = response.htmlStatus;
160 Proxmox.Utils.setErrorMask(me, msg);
161 me.running = false;
162 if (me.requested) {
163 me.requested = false;
164 view.loadTask.delay(200);
165 }
166 },
167 });
168 },
169
170 onScroll: function(x, y) {
171 let me = this;
172 let view = me.getView();
173 let viewmodel = me.getViewModel();
174 let livemode = viewmodel.get('livemode');
175 if (!livemode) {
176 return;
177 }
178
179 if (me.scrollPosTop() < 20*view.lineHeight) {
180 view.scrollToEnd = false;
181 view.loadTask.delay(200, undefined, undefined, [true, true]);
182 } else if (me.scrollPosBottom() <= 5) {
183 view.scrollToEnd = true;
184 }
185 },
186
187 init: function(view) {
188 let me = this;
189
190 if (!view.url) {
191 throw "no url specified";
192 }
193
194 let viewmodel = me.getViewModel();
195 let viewModel = this.getViewModel();
196 let since = new Date();
197 since.setDate(since.getDate() - 3);
198 viewModel.set('until', new Date());
199 viewModel.set('since', since);
200 me.lookup('content').setStyle('line-height', view.lineHeight + 'px');
201
202 view.loadTask = new Ext.util.DelayedTask(me.doLoad, me, [true, false]);
203
204 view.task = Ext.TaskManager.start({
205 run: function() {
206 if (!view.isVisible() || !view.scrollToEnd || !viewmodel.get('livemode')) {
207 return;
208 }
209
210 if (me.scrollPosBottom() <= 5) {
211 view.loadTask.delay(200, undefined, undefined, [true, false]);
212 }
213 },
214 interval: 1000,
215 });
216 },
217
218 onLiveMode: function() {
219 let me = this;
220 let view = me.getView();
221 delete view.startcursor;
222 delete view.endcursor;
223 delete view.content;
224 me.getViewModel().set('livemode', true);
225 view.scrollToEnd = true;
226 me.updateView([], true, false);
227 },
228
229 onTimespan: function() {
230 let me = this;
231 me.getViewModel().set('livemode', false);
232 me.updateView([], false);
233 },
234 },
235
236 onDestroy: function() {
237 let me = this;
238 me.loadTask.cancel();
239 Ext.TaskManager.stop(me.task);
240 delete me.content;
241 },
242
243 // for user to initiate a load from outside
244 requestUpdate: function() {
245 let me = this;
246 me.loadTask.delay(200);
247 },
248
249 viewModel: {
250 data: {
251 livemode: true,
252 until: null,
253 since: null,
254 },
255 },
256
257 layout: 'auto',
258 bodyPadding: 5,
259 scrollable: {
260 x: 'auto',
261 y: 'auto',
262 listeners: {
263 // we have to have this here, since we cannot listen to events
264 // of the scroller in the viewcontroller (extjs bug?), nor does
265 // the panel have a 'scroll' event'
266 scroll: {
267 fn: function(scroller, x, y) {
268 let controller = this.component.getController();
269 if (controller) { // on destroy, controller can be gone
270 controller.onScroll(x, y);
271 }
272 },
273 buffer: 200,
274 },
275 },
276 },
277
278 tbar: {
279
280 items: [
281 '->',
282 {
283 xtype: 'segmentedbutton',
284 items: [
285 {
286 text: gettext('Live Mode'),
287 bind: {
288 pressed: '{livemode}',
289 },
290 handler: 'onLiveMode',
291 },
292 {
293 text: gettext('Select Timespan'),
294 bind: {
295 pressed: '{!livemode}',
296 },
297 handler: 'onTimespan',
298 },
299 ],
300 },
301 {
302 xtype: 'box',
303 bind: { disabled: '{livemode}' },
304 autoEl: { cn: gettext('Since') + ':' },
305 },
306 {
307 xtype: 'datefield',
308 name: 'since_date',
309 reference: 'since',
310 format: 'Y-m-d',
311 bind: {
312 disabled: '{livemode}',
313 value: '{since}',
314 maxValue: '{until}',
315 },
316 },
317 {
318 xtype: 'box',
319 bind: { disabled: '{livemode}' },
320 autoEl: { cn: gettext('Until') + ':' },
321 },
322 {
323 xtype: 'datefield',
324 name: 'until_date',
325 reference: 'until',
326 format: 'Y-m-d',
327 bind: {
328 disabled: '{livemode}',
329 value: '{until}',
330 minValue: '{since}',
331 },
332 },
333 {
334 xtype: 'button',
335 text: 'Update',
336 reference: 'updateBtn',
337 handler: 'updateParams',
338 bind: {
339 disabled: '{livemode}',
340 },
341 },
342 ],
343 },
344
345 items: [
346 {
347 xtype: 'box',
348 reference: 'content',
349 style: {
350 font: 'normal 11px tahoma, arial, verdana, sans-serif',
351 'white-space': 'pre',
352 },
353 },
354 ],
355 });