]>
Commit | Line | Data |
---|---|---|
de2e10b5 DM |
1 | /* |
2 | * Display log entries in a panel with scrollbar | |
3 | * The log entries are automatically refreshed via a background task, | |
017a6376 | 4 | * with newest entries coming at the bottom |
de2e10b5 DM |
5 | */ |
6 | Ext.define('Proxmox.panel.LogView', { | |
7 | extend: 'Ext.panel.Panel', | |
65bb3b67 | 8 | xtype: 'proxmoxLogView', |
de2e10b5 | 9 | |
4e95d1e9 | 10 | pageSize: 510, |
65bb3b67 | 11 | viewBuffer: 50, |
de2e10b5 DM |
12 | lineHeight: 16, |
13 | ||
de2e10b5 DM |
14 | scrollToEnd: true, |
15 | ||
65bb3b67 DC |
16 | // callback for load failure, used for ceph |
17 | failCallback: undefined, | |
de2e10b5 | 18 | |
65bb3b67 DC |
19 | controller: { |
20 | xclass: 'Ext.app.ViewController', | |
de2e10b5 | 21 | |
65bb3b67 | 22 | updateParams: function() { |
05a977a2 TL |
23 | let me = this; |
24 | let viewModel = me.getViewModel(); | |
25 | let since = viewModel.get('since'); | |
26 | let until = viewModel.get('until'); | |
65bb3b67 DC |
27 | if (viewModel.get('hide_timespan')) { |
28 | return; | |
29 | } | |
de2e10b5 | 30 | |
65bb3b67 DC |
31 | if (since > until) { |
32 | Ext.Msg.alert('Error', 'Since date must be less equal than Until date.'); | |
33 | return; | |
34 | } | |
de2e10b5 | 35 | |
65bb3b67 DC |
36 | viewModel.set('params.since', Ext.Date.format(since, 'Y-m-d')); |
37 | viewModel.set('params.until', Ext.Date.format(until, 'Y-m-d') + ' 23:59:59'); | |
38 | me.getView().loadTask.delay(200); | |
39 | }, | |
40 | ||
41 | scrollPosBottom: function() { | |
05a977a2 TL |
42 | let view = this.getView(); |
43 | let pos = view.getScrollY(); | |
44 | let maxPos = view.getScrollable().getMaxPosition().y; | |
65bb3b67 DC |
45 | return maxPos - pos; |
46 | }, | |
47 | ||
b5ff20a6 | 48 | updateView: function(lines, first, total) { |
05a977a2 TL |
49 | let me = this; |
50 | let view = me.getView(); | |
51 | let viewModel = me.getViewModel(); | |
52 | let content = me.lookup('content'); | |
53 | let data = viewModel.get('data'); | |
65bb3b67 | 54 | |
b5ff20a6 | 55 | if (first === data.first && total === data.total && lines.length === data.lines) { |
65bb3b67 DC |
56 | return; // same content, skip setting and scrolling |
57 | } | |
58 | viewModel.set('data', { | |
59 | first: first, | |
60 | total: total, | |
b5ff20a6 | 61 | lines: lines.length, |
65bb3b67 | 62 | }); |
de2e10b5 | 63 | |
05a977a2 | 64 | let scrollPos = me.scrollPosBottom(); |
b5ff20a6 | 65 | let scrollToBottom = view.scrollToEnd && scrollPos <= 5; |
de2e10b5 | 66 | |
b5ff20a6 DC |
67 | if (!scrollToBottom) { |
68 | // so that we have the 'correct' height for the text | |
69 | lines.length = total; | |
70 | } | |
71 | ||
72 | content.update(lines.join('<br>')); | |
de2e10b5 | 73 | |
b5ff20a6 | 74 | if (scrollToBottom) { |
2f474118 DC |
75 | let scroller = view.getScrollable(); |
76 | scroller.suspendEvent('scroll'); | |
77 | view.scrollTo(0, Infinity); | |
78 | me.updateStart(true); | |
79 | scroller.resumeEvent('scroll'); | |
65bb3b67 DC |
80 | } |
81 | }, | |
82 | ||
83 | doLoad: function() { | |
05a977a2 | 84 | let me = this; |
72be386c TL |
85 | if (me.running) { |
86 | me.requested = true; | |
87 | return; | |
88 | } | |
7f9a6567 | 89 | me.running = true; |
05a977a2 TL |
90 | let view = me.getView(); |
91 | let viewModel = me.getViewModel(); | |
65bb3b67 DC |
92 | Proxmox.Utils.API2Request({ |
93 | url: me.getView().url, | |
94 | params: viewModel.get('params'), | |
95 | method: 'GET', | |
96 | success: function(response) { | |
97 | Proxmox.Utils.setErrorMask(me, false); | |
05a977a2 TL |
98 | let total = response.result.total; |
99 | let lines = []; | |
100 | let first = Infinity; | |
65bb3b67 DC |
101 | |
102 | Ext.Array.each(response.result.data, function(line) { | |
103 | if (first > line.n) { | |
104 | first = line.n; | |
105 | } | |
106 | lines[line.n - 1] = Ext.htmlEncode(line.t); | |
107 | }); | |
108 | ||
b5ff20a6 | 109 | me.updateView(lines, first - 1, total); |
7f9a6567 | 110 | me.running = false; |
72be386c TL |
111 | if (me.requested) { |
112 | me.requested = false; | |
113 | view.loadTask.delay(200); | |
114 | } | |
65bb3b67 DC |
115 | }, |
116 | failure: function(response) { | |
117 | if (view.failCallback) { | |
118 | view.failCallback(response); | |
119 | } else { | |
05a977a2 | 120 | let msg = response.htmlStatus; |
65bb3b67 | 121 | Proxmox.Utils.setErrorMask(me, msg); |
de2e10b5 | 122 | } |
7f9a6567 | 123 | me.running = false; |
72be386c TL |
124 | if (me.requested) { |
125 | me.requested = false; | |
126 | view.loadTask.delay(200); | |
127 | } | |
01031528 | 128 | }, |
65bb3b67 DC |
129 | }); |
130 | }, | |
131 | ||
2f474118 DC |
132 | updateStart: function(scrolledToBottom, targetLine) { |
133 | let me = this; | |
a16b036b | 134 | let view = me.getView(), viewModel = me.getViewModel(); |
2f474118 DC |
135 | |
136 | let limit = viewModel.get('params.limit'); | |
a16b036b | 137 | let total = viewModel.get('data.total'); |
2f474118 | 138 | |
4e95d1e9 TL |
139 | // heuristic: scroll up? -> load more in front; scroll down? -> load more at end |
140 | let startRatio = view.lastTargetLine && view.lastTargetLine > targetLine ? 2/3 : 1/3; | |
141 | view.lastTargetLine = targetLine; | |
142 | ||
a16b036b TL |
143 | let newStart = scrolledToBottom |
144 | ? Math.trunc(total - limit, 10) | |
4e95d1e9 | 145 | : Math.trunc(targetLine - (startRatio * limit) + 10); |
a16b036b TL |
146 | |
147 | viewModel.set('params.start', Math.max(newStart, 0)); | |
2f474118 DC |
148 | |
149 | view.loadTask.delay(200); | |
150 | }, | |
151 | ||
65bb3b67 | 152 | onScroll: function(x, y) { |
05a977a2 | 153 | let me = this; |
a16b036b | 154 | let view = me.getView(), viewModel = me.getViewModel(); |
65bb3b67 | 155 | |
a16b036b TL |
156 | let line = view.getScrollY() / view.lineHeight; |
157 | let viewLines = view.getHeight() / view.lineHeight; | |
65bb3b67 | 158 | |
a16b036b TL |
159 | let viewStart = Math.max(Math.trunc(line - 1 - view.viewBuffer), 0); |
160 | let viewEnd = Math.trunc(line + viewLines + 1 + view.viewBuffer); | |
65bb3b67 | 161 | |
a16b036b TL |
162 | let { start, limit } = viewModel.get('params'); |
163 | ||
1eb5c21d TL |
164 | let margin = start < 20 ? 0 : 20; |
165 | ||
166 | if (viewStart < start + margin || viewEnd > start + limit - margin) { | |
2f474118 | 167 | me.updateStart(false, line); |
de2e10b5 | 168 | } |
65bb3b67 | 169 | }, |
de2e10b5 | 170 | |
65bb3b67 | 171 | init: function(view) { |
05a977a2 | 172 | let me = this; |
de2e10b5 | 173 | |
65bb3b67 DC |
174 | if (!view.url) { |
175 | throw "no url specified"; | |
de2e10b5 | 176 | } |
de2e10b5 | 177 | |
05a977a2 TL |
178 | let viewModel = this.getViewModel(); |
179 | let since = new Date(); | |
65bb3b67 DC |
180 | since.setDate(since.getDate() - 3); |
181 | viewModel.set('until', new Date()); | |
182 | viewModel.set('since', since); | |
183 | viewModel.set('params.limit', view.pageSize); | |
184 | viewModel.set('hide_timespan', !view.log_select_timespan); | |
a16b036b | 185 | me.lookup('content').setStyle('line-height', `${view.lineHeight}px`); |
65bb3b67 DC |
186 | |
187 | view.loadTask = new Ext.util.DelayedTask(me.doLoad, me); | |
188 | ||
189 | me.updateParams(); | |
190 | view.task = Ext.TaskManager.start({ | |
a16b036b | 191 | run: () => { |
65bb3b67 DC |
192 | if (!view.isVisible() || !view.scrollToEnd) { |
193 | return; | |
194 | } | |
aa9607f1 | 195 | if (me.scrollPosBottom() <= 5) { |
72be386c | 196 | view.loadTask.delay(200); |
65bb3b67 DC |
197 | } |
198 | }, | |
01031528 | 199 | interval: 1000, |
65bb3b67 | 200 | }); |
01031528 | 201 | }, |
de2e10b5 DM |
202 | }, |
203 | ||
65bb3b67 | 204 | onDestroy: function() { |
05a977a2 | 205 | let me = this; |
65bb3b67 DC |
206 | me.loadTask.cancel(); |
207 | Ext.TaskManager.stop(me.task); | |
de2e10b5 DM |
208 | }, |
209 | ||
65bb3b67 DC |
210 | // for user to initiate a load from outside |
211 | requestUpdate: function() { | |
05a977a2 | 212 | let me = this; |
65bb3b67 DC |
213 | me.loadTask.delay(200); |
214 | }, | |
de2e10b5 | 215 | |
65bb3b67 DC |
216 | viewModel: { |
217 | data: { | |
218 | until: null, | |
219 | since: null, | |
220 | hide_timespan: false, | |
221 | data: { | |
222 | start: 0, | |
223 | total: 0, | |
01031528 | 224 | textlen: 0, |
65bb3b67 DC |
225 | }, |
226 | params: { | |
227 | start: 0, | |
4e95d1e9 | 228 | limit: 510, |
01031528 TL |
229 | }, |
230 | }, | |
65bb3b67 | 231 | }, |
de2e10b5 | 232 | |
65bb3b67 DC |
233 | layout: 'auto', |
234 | bodyPadding: 5, | |
235 | scrollable: { | |
236 | x: 'auto', | |
237 | y: 'auto', | |
238 | listeners: { | |
a16b036b TL |
239 | // we have to have this here, since we cannot listen to events of the scroller in |
240 | // the viewcontroller (extjs bug?), nor does the panel have a 'scroll' event' | |
65bb3b67 DC |
241 | scroll: { |
242 | fn: function(scroller, x, y) { | |
05a977a2 | 243 | let controller = this.component.getController(); |
65bb3b67 | 244 | if (controller) { // on destroy, controller can be gone |
01031528 | 245 | controller.onScroll(x, y); |
65bb3b67 DC |
246 | } |
247 | }, | |
01031528 | 248 | buffer: 200, |
65bb3b67 | 249 | }, |
01031528 | 250 | }, |
65bb3b67 | 251 | }, |
de2e10b5 | 252 | |
65bb3b67 DC |
253 | tbar: { |
254 | bind: { | |
01031528 | 255 | hidden: '{hide_timespan}', |
65bb3b67 DC |
256 | }, |
257 | items: [ | |
258 | '->', | |
259 | 'Since: ', | |
260 | { | |
261 | xtype: 'datefield', | |
262 | name: 'since_date', | |
263 | reference: 'since', | |
264 | format: 'Y-m-d', | |
265 | bind: { | |
266 | value: '{since}', | |
01031528 TL |
267 | maxValue: '{until}', |
268 | }, | |
de2e10b5 | 269 | }, |
65bb3b67 DC |
270 | 'Until: ', |
271 | { | |
272 | xtype: 'datefield', | |
273 | name: 'until_date', | |
274 | reference: 'until', | |
275 | format: 'Y-m-d', | |
276 | bind: { | |
277 | value: '{until}', | |
01031528 TL |
278 | minValue: '{since}', |
279 | }, | |
65bb3b67 DC |
280 | }, |
281 | { | |
282 | xtype: 'button', | |
283 | text: 'Update', | |
01031528 TL |
284 | handler: 'updateParams', |
285 | }, | |
65bb3b67 DC |
286 | ], |
287 | }, | |
de2e10b5 | 288 | |
65bb3b67 DC |
289 | items: [ |
290 | { | |
291 | xtype: 'box', | |
292 | reference: 'content', | |
293 | style: { | |
294 | font: 'normal 11px tahoma, arial, verdana, sans-serif', | |
01031528 | 295 | 'white-space': 'pre', |
65bb3b67 | 296 | }, |
01031528 TL |
297 | }, |
298 | ], | |
de2e10b5 | 299 | }); |