vigigraph / vigigraph / public / js / graph.js @ b02623ff
History | View | Annotate | Download (15.6 KB)
1 |
var refresh_delay = 30; |
---|---|
2 |
var graphs = [];
|
3 |
|
4 |
var old_fragment = ''; |
5 |
var skip_detection = 0; |
6 |
|
7 |
|
8 |
var Graph = new Class({ |
9 |
Implements: [Events, Options],
|
10 |
|
11 |
options: {
|
12 |
// Les valeurs par défaut sont assignées dans reset().
|
13 |
duration: 86400, |
14 |
start: null, |
15 |
autoRefresh: 0, |
16 |
refreshDelay: null, |
17 |
left: null, |
18 |
top: null |
19 |
}, |
20 |
|
21 |
initialize: function (options, host, graph) { |
22 |
this.setOptions(options);
|
23 |
this.host = host;
|
24 |
this.graph = graph;
|
25 |
this.refreshTimer = null; |
26 |
this.destroyed = false; |
27 |
|
28 |
new Request.JSON({
|
29 |
url: app_path + 'rpc/startTime', |
30 |
onSuccess: function (data) { |
31 |
this.startTime = data.starttime;
|
32 |
}.bind(this)
|
33 |
}).get({'host': this.host}); |
34 |
|
35 |
var toolbar = new Jx.Toolbar({position:'top'}); |
36 |
var periods = [
|
37 |
[_('Last 12 hours'), 12], |
38 |
[_('Last 24 hours'), 24], |
39 |
[_('Last 48 hours'), 48], |
40 |
[_('Last 7 days'), 7*24], |
41 |
[_('Last 14 days'), 14*24], |
42 |
[_('Last 3 months'), 3*30*24], |
43 |
[_('Last 6 months'), 6*30*24], |
44 |
[_('Last year'), 365*24] |
45 |
]; |
46 |
|
47 |
var timeframe = new Jx.Menu({ |
48 |
label: _("Timeframe"), |
49 |
image: app_path + 'images/history.png', |
50 |
tooltip: _("Timeframe menu") |
51 |
}); |
52 |
|
53 |
periods.each(function (period) {
|
54 |
var menuItem = new Jx.Menu.Item({ |
55 |
label: period[0] |
56 |
}); |
57 |
menuItem.options.period = period[1] * 60 * 60; |
58 |
menuItem.addEvent('click', function () { |
59 |
this[0].options.start = null; |
60 |
this[0].options.duration = this[1].options.period; |
61 |
this[0].updateGraph(); |
62 |
}.bind([this, menuItem]));
|
63 |
timeframe.add(menuItem); |
64 |
}, this);
|
65 |
|
66 |
// Indicateur d'alerte en cas d'erreur
|
67 |
var alert_msg = _(
|
68 |
'Could not load the graph for "%(graph)s" on "%(host)s". ' +
|
69 |
'Make sure VigiRRD is running and receives performance data.'
|
70 |
); |
71 |
// Le pattern donné à substitute permet de garder une syntaxe
|
72 |
// cohérente avec Python (facilite le travail des traducteurs).
|
73 |
alert_msg = alert_msg.substitute({ |
74 |
'graph': this.graph, |
75 |
'host': this.host |
76 |
}, (/\\?%\(([^()]+)\)s/g));
|
77 |
this.alert_indicator = new Element("img"); |
78 |
this.alert_indicator.addClass("alert-indicator"); |
79 |
this.alert_indicator.setProperty("src", app_path + 'images/messagebox_warning.png'); |
80 |
this.alert_indicator.setProperty("title", alert_msg); |
81 |
|
82 |
this.indicators = new Jx.Menu({ |
83 |
label: _("Export to CSV"), |
84 |
image: app_path + 'images/document-export.png', |
85 |
tooltip: _("Export the content of this graph to CSV") |
86 |
}); |
87 |
|
88 |
new Request.JSON({
|
89 |
url: app_path + 'rpc/getIndicators', |
90 |
onSuccess: function (data) { |
91 |
data.items.each(function (item) {
|
92 |
this.indicators.add(new Jx.Menu.Item({ |
93 |
name: item[0], |
94 |
label: item[1], |
95 |
onClick: this.exportCSV.bind(this), |
96 |
indicator: true |
97 |
})); |
98 |
}, this);
|
99 |
|
100 |
this.indicators.add(new Jx.Menu.Item({ |
101 |
label: _('All'), |
102 |
onClick: this.exportCSV.bind(this), |
103 |
indicator: false |
104 |
})); |
105 |
}.bind(this)
|
106 |
}).get({ |
107 |
'host': this.host, |
108 |
'graph': this.graph |
109 |
}); |
110 |
|
111 |
this.refresh_button = new Jx.Button({ |
112 |
image: app_path + 'images/refresh.png', |
113 |
tooltip: _("Automatically refresh the graph"), |
114 |
toggle: true, |
115 |
onDown: function() { |
116 |
// On s'assure qu'il n'y a pas déjà un timer lancé.
|
117 |
if ($chk(this.refreshTimer)) |
118 |
return;
|
119 |
var delay =
|
120 |
this.options.refreshDelay ||
|
121 |
window.refresh_delay; |
122 |
this.refreshTimer =
|
123 |
this.updateGraph.periodical(delay * 1000, this); |
124 |
this.options.autoRefresh = 1; |
125 |
window.updateURI(); |
126 |
}.bind(this),
|
127 |
onUp: function() { |
128 |
clearInterval(this.refreshTimer);
|
129 |
this.options.autoRefresh = 0; |
130 |
window.updateURI(); |
131 |
}.bind(this)
|
132 |
}); |
133 |
|
134 |
this.zoom_in = new Jx.Button({ |
135 |
image: app_path + 'images/zoom-in.png', |
136 |
tooltip: _("Zoom in"), |
137 |
onClick: function() { |
138 |
this.updateZoom(0.5); |
139 |
}.bind(this)
|
140 |
}); |
141 |
|
142 |
this.zoom_out = new Jx.Button({ |
143 |
image: app_path + 'images/zoom-out.png', |
144 |
tooltip: _("Zoom out"), |
145 |
onClick: function() { |
146 |
this.updateZoom(2); |
147 |
}.bind(this)
|
148 |
}); |
149 |
|
150 |
toolbar.add( |
151 |
this.refresh_button,
|
152 |
timeframe, |
153 |
new Jx.Button({
|
154 |
image: app_path + 'images/start.png', |
155 |
tooltip: _("Graph start"), |
156 |
onClick: function() { |
157 |
this.options.start = this.startTime; |
158 |
this.updateGraph();
|
159 |
}.bind(this)
|
160 |
}), |
161 |
new Jx.Button({
|
162 |
image: app_path + 'images/previous.png', |
163 |
tooltip: _("Previous section"), |
164 |
onClick: function() { |
165 |
this.options.start =
|
166 |
this.getStartTime() -
|
167 |
this.options.duration;
|
168 |
this.updateGraph();
|
169 |
}.bind(this)
|
170 |
}), |
171 |
new Jx.Button({
|
172 |
image: app_path + 'images/next.png', |
173 |
tooltip: _("Next section"), |
174 |
onClick: function() { |
175 |
this.options.start =
|
176 |
this.getStartTime() +
|
177 |
this.options.duration;
|
178 |
this.updateGraph();
|
179 |
}.bind(this)
|
180 |
}), |
181 |
new Jx.Button({
|
182 |
image: app_path + 'images/end.png', |
183 |
tooltip: _("Graph end"), |
184 |
onClick: function() { |
185 |
this.options.start = null; |
186 |
this.updateGraph();
|
187 |
}.bind(this)
|
188 |
}), |
189 |
this.zoom_in,
|
190 |
this.zoom_out,
|
191 |
this.indicators,
|
192 |
new Jx.Button({
|
193 |
image: app_path + 'images/document-print-small.png', |
194 |
tooltip: _("Print graph"), |
195 |
onClick: this.print.bind(this) |
196 |
}) |
197 |
); |
198 |
|
199 |
var label = _("Graph for \"%(graph)s\" on \"%(host)s\""); |
200 |
// Le pattern donné à substitute permet de garder une syntaxe
|
201 |
// cohérente avec Python (facilite le travail des traducteurs).
|
202 |
label = label.substitute({ |
203 |
'graph': this.graph, |
204 |
'host': this.host |
205 |
}, (/\\?%\(([^()]+)\)s/g));
|
206 |
|
207 |
this.graph_window = new Jx.Dialog({ |
208 |
label: label,
|
209 |
modal: false, |
210 |
move: true, |
211 |
close: true, |
212 |
horizontal: this.options.left + ' left', |
213 |
vertical: this.options.top + ' top', |
214 |
width: 575, |
215 |
height: 75, |
216 |
toolbars: [toolbar]
|
217 |
}); |
218 |
|
219 |
this.alert_indicator.inject(this.graph_window.content.parentNode); |
220 |
|
221 |
// mise à jour
|
222 |
this.updateGraph();
|
223 |
this.graph_window.open();
|
224 |
|
225 |
this.refresh_button.setActive(parseInt(this.options.autoRefresh, 10)); |
226 |
|
227 |
var onClose = function () { |
228 |
if (this.destroyed) return; |
229 |
this.destroyed = true; |
230 |
this.graph_window.domObj.dispose();
|
231 |
window.graphs.erase(this);
|
232 |
window.updateURI(); |
233 |
}; |
234 |
|
235 |
this.graph_window.addEvent('close', onClose.bind(this)); |
236 |
// sizeChange est déclenché à la fois après un redimensionnement
|
237 |
// et après un déplacement. Ce cas est mal documenté dans JxLib.
|
238 |
this.graph_window.addEvent('sizeChange', this.dialogMoved.bind(this)); |
239 |
|
240 |
// Simule un déplacement de la fenêtre,
|
241 |
// pour mettre à jour les coordonnées.
|
242 |
this.dialogMoved();
|
243 |
window.graphs.push(this);
|
244 |
return this; |
245 |
}, |
246 |
|
247 |
destroy: function () { |
248 |
this.graph_window.close();
|
249 |
}, |
250 |
|
251 |
dialogMoved: function () { |
252 |
// Repris de l'API interne de JxLib (création du Drag).
|
253 |
this.options.left = parseInt(this.graph_window.domObj.style.left, 10); |
254 |
this.options.top = parseInt(this.graph_window.domObj.style.top, 10); |
255 |
window.updateURI(); |
256 |
}, |
257 |
|
258 |
updateZoom: function (factor) { |
259 |
this.options.duration = parseInt(this.options.duration, 10) * factor; |
260 |
// Période minimale d'affichage : 1 minute.
|
261 |
if (this.options.duration < 60) |
262 |
this.options.duration = 60; |
263 |
// On (dés)active le bouton de zoom en avant au besoin.
|
264 |
this.zoom_in.setEnabled(this.options.duration != 60); |
265 |
this.updateGraph();
|
266 |
}, |
267 |
|
268 |
getStartTime: function () { |
269 |
var start = this.options.start; |
270 |
if (start == null) |
271 |
// @TODO: cette heure est en localtime a priori.
|
272 |
start = (new Date() / 1000).toInt() - this.options.duration; |
273 |
if (start < 0) |
274 |
return 0; |
275 |
return start;
|
276 |
}, |
277 |
|
278 |
exportCSV: function (menuItem) { |
279 |
var uri = new URI(app_path + 'vigirrd/' + |
280 |
encodeURIComponent(this.host) + '/export'); |
281 |
|
282 |
var start = this.getStartTime(); |
283 |
|
284 |
uri.setData({ |
285 |
host: this.host, |
286 |
graphtemplate: this.graph, |
287 |
start: start,
|
288 |
end: start + this.options.duration, |
289 |
nocache: (new Date() / 1) |
290 |
}); |
291 |
|
292 |
if (menuItem.options.indicator)
|
293 |
uri.setData({ds: menuItem.options.name}, true); |
294 |
|
295 |
window.open(uri.toString()); |
296 |
}, |
297 |
|
298 |
// Cette fonction est aussi utilisée dans print.js
|
299 |
// pour gérer l'impression globale.
|
300 |
getPrintParams: function () { |
301 |
var img = this.graph_window.content.getElement('img'); |
302 |
var img_uri = new URI(img.src); |
303 |
var params = img_uri.getData();
|
304 |
return {
|
305 |
host: params.host,
|
306 |
start: params.start,
|
307 |
duration: params.duration,
|
308 |
graph: params.graphtemplate,
|
309 |
nocache: params.nocache
|
310 |
}; |
311 |
}, |
312 |
|
313 |
print: function () { |
314 |
var uri = new URI(app_path + 'rpc/graphsList'); |
315 |
uri.setData({graphs: [this.getPrintParams()]}); |
316 |
var print_window = window.open(uri.toString());
|
317 |
print_window.onload = function () { |
318 |
this.print();
|
319 |
}.bind(print_window); |
320 |
}, |
321 |
|
322 |
updateGraph: function () { |
323 |
var uri = new URI(app_path + 'vigirrd/' + |
324 |
encodeURIComponent(this.host) + '/graph.png'); |
325 |
|
326 |
var start = this.getStartTime(); |
327 |
|
328 |
uri.setData({ |
329 |
host: this.host, |
330 |
start: start,
|
331 |
duration: this.options.duration, |
332 |
graphtemplate: this.graph, |
333 |
// Permet d'empêcher la mise en cache du graphe.
|
334 |
// Nécessaire car le graphe évolue dynamiquement au fil de l'eau.
|
335 |
nocache: (new Date() / 1) |
336 |
}); |
337 |
// On génère dynamiquement une balise "img" pour charger le graphe.
|
338 |
this.graph_window.setContent(
|
339 |
'<img src="' + uri.toString() + '"/' + '>'); |
340 |
var img = this.graph_window.content.getElement('img'); |
341 |
img.addEvent('load', function () { |
342 |
this[0].graph_window.resize( |
343 |
this[1].width + 25, |
344 |
this[1].height + 88 |
345 |
); |
346 |
this[0].hideAlert(); |
347 |
}.bind([this, img]));
|
348 |
img.addEvent('error', function () { |
349 |
// if (this.refreshTimer) clearInterval(this.refreshTimer);
|
350 |
this.showAlert();
|
351 |
//this.graph_window.close();
|
352 |
}.bind(this));
|
353 |
}, |
354 |
|
355 |
showAlert: function() { |
356 |
this.alert_indicator.setStyle("display", "block"); |
357 |
var zindex = parseInt(this.graph_window.domObj.getStyle("z-index"), 10) + 1; |
358 |
this.alert_indicator.setStyle("z-index", zindex); |
359 |
return;
|
360 |
}, |
361 |
|
362 |
hideAlert: function() { |
363 |
this.alert_indicator.setStyle("display", "none"); |
364 |
} |
365 |
}); |
366 |
|
367 |
var updateURI = function () { |
368 |
var graphs_uri = [];
|
369 |
var uri = new URI(); |
370 |
|
371 |
// Section critique.
|
372 |
window.skip_detection++; |
373 |
uri.set('fragment', ''); |
374 |
|
375 |
window.graphs.each(function (graph) {
|
376 |
var props = new Hash(graph.options); |
377 |
props.extend({host: graph.host, graph: graph.graph}); |
378 |
this.push(props.toQueryString());
|
379 |
}, graphs_uri); |
380 |
|
381 |
uri.setData({'graphs': graphs_uri, safety: 1}, false, 'fragment'); |
382 |
uri.go(); |
383 |
window.old_fragment = uri.toString(); |
384 |
|
385 |
// Fin de section critique.
|
386 |
window.skip_detection--; |
387 |
}; |
388 |
|
389 |
var update_visible_graphs = function (new_fragment) { |
390 |
// On réouvre les graphes précédemment chargés.
|
391 |
var new_graphs = [];
|
392 |
var qs = new Hash(new_fragment.get('fragment').parseQueryString()); |
393 |
if (qs.has('graphs')) { |
394 |
new_graphs = (new Hash(qs.get('graphs'))).getValues(); |
395 |
} |
396 |
|
397 |
// Section critique.
|
398 |
window.skip_detection++; |
399 |
|
400 |
var prev_graphs = window.graphs;
|
401 |
window.graphs = []; |
402 |
prev_graphs.each(function (graph) { graph.destroy(); });
|
403 |
|
404 |
new_graphs.each(function (graph) {
|
405 |
var uri = new URI('?' + graph); |
406 |
var qs = new Hash(uri.getData()); |
407 |
if (qs.has('host') && qs.has('graph')) { |
408 |
var options = new Hash(); |
409 |
var params = [
|
410 |
'start',
|
411 |
'duration',
|
412 |
'left',
|
413 |
'top',
|
414 |
'autoRefresh',
|
415 |
'refreshDelay'
|
416 |
]; |
417 |
|
418 |
params.each(function (param) {
|
419 |
if (this[0].has(param)) |
420 |
this[1].set(param, this[0].get(param)); |
421 |
}, [qs, options]); |
422 |
|
423 |
new Graph(
|
424 |
options.getClean(), |
425 |
qs.get('host'),
|
426 |
qs.get('graph')
|
427 |
); |
428 |
} |
429 |
}); |
430 |
|
431 |
window.updateURI(); |
432 |
|
433 |
// Fin de section critique.
|
434 |
window.skip_detection--; |
435 |
|
436 |
if (window.graphs.length == 1) { |
437 |
new Request.JSON({
|
438 |
url: app_path + 'rpc/selectHostAndGraph', |
439 |
onSuccess: function (results) { |
440 |
window.toolbar.host_picker.setItem(results.idhost, this[0]); |
441 |
window.toolbar.graph_picker.idselection = results.idgraph; |
442 |
window.toolbar.graph_picker.setLabel(this[1]); |
443 |
}.bind([ |
444 |
window.graphs[0].host,
|
445 |
window.graphs[0].graph
|
446 |
]) |
447 |
}).get({ |
448 |
host: window.graphs[0].host, |
449 |
graph: window.graphs[0].graph |
450 |
}); |
451 |
} |
452 |
}; |
453 |
|
454 |
var hash_change_detector = function() { |
455 |
var new_fragment;
|
456 |
|
457 |
// Pour les moments où on a besoin de mettre à jour
|
458 |
// volontairement l'URI (à l'ouverture d'un graphe).
|
459 |
if (window.skip_detection) return; |
460 |
|
461 |
// Force mootools à analyser l'URL courante de nouveau,
|
462 |
// ce qui mettra à jour la partie "fragment" de l'URI.
|
463 |
URI.base = new URI(
|
464 |
document.getElements('base[href]', true).getLast(), |
465 |
{base: document.location}
|
466 |
); |
467 |
|
468 |
new_fragment = new URI();
|
469 |
if (old_fragment.toString() != new_fragment.toString()) {
|
470 |
update_visible_graphs(new_fragment, old_fragment); |
471 |
} |
472 |
}; |
473 |
|
474 |
if ('onhashchange' in window) { |
475 |
window.onhashchange = hash_change_detector; |
476 |
} else {
|
477 |
hash_change_detector.periodical(100);
|
478 |
} |
479 |
|
480 |
window.addEvent('load', function () { |
481 |
new Request.JSON({
|
482 |
url: app_path + 'rpc/tempoDelayRefresh', |
483 |
onSuccess: function (data) { |
484 |
window.refresh_delay = data.delay; |
485 |
} |
486 |
}).get(); |
487 |
|
488 |
hash_change_detector(); |
489 |
}); |