vigigraph / vigigraph / public / js / graph.js @ c1b9f1da
History | View | Annotate | Download (12.3 KB)
1 | 7c9baa43 | Francois POIROTTE | var Graph = new Class({ |
---|---|---|---|
2 | Implements: [Events, Options],
|
||
3 | |||
4 | options: {
|
||
5 | // Les valeurs par défaut sont assignées dans reset().
|
||
6 | duration: 86400, |
||
7 | start: null, |
||
8 | autoRefresh: 0, |
||
9 | refreshDelay: null, |
||
10 | left: null, |
||
11 | top: null |
||
12 | }, |
||
13 | |||
14 | initialize: function (options, host, graph) { |
||
15 | this.setOptions(options);
|
||
16 | this.host = host;
|
||
17 | this.graph = graph;
|
||
18 | this.refreshTimer = null; |
||
19 | |||
20 | new Request.JSON({
|
||
21 | c1b9f1da | Francois POIROTTE | url: app_path + 'rpc/startTime', |
22 | 7c9baa43 | Francois POIROTTE | onSuccess: function (data) { |
23 | this.startTime = data.starttime;
|
||
24 | }.bind(this)
|
||
25 | }).get({'host': this.host}); |
||
26 | |||
27 | var toolbar = new Jx.Toolbar({position:'top'}); |
||
28 | var periods = [
|
||
29 | [_('Last 12 hours'), 12], |
||
30 | [_('Last 24 hours'), 24], |
||
31 | [_('Last 48 hours'), 48], |
||
32 | [_('Last 7 days'), 7*24], |
||
33 | [_('Last 14 days'), 14*24], |
||
34 | [_('Last 3 months'), 3*30*24], |
||
35 | [_('Last 6 months'), 6*30*24], |
||
36 | [_('Last year'), 365*24] |
||
37 | ]; |
||
38 | |||
39 | var timeframe = new Jx.Menu({ |
||
40 | label: _("Timeframe"), |
||
41 | image: app_path + 'images/history.png', |
||
42 | tooltip: _("Timeframe menu") |
||
43 | }); |
||
44 | |||
45 | periods.each(function (period) {
|
||
46 | var menuItem = new Jx.Menu.Item({ |
||
47 | label: period[0] |
||
48 | }); |
||
49 | menuItem.options.period = period[1] * 60 * 60; |
||
50 | menuItem.addEvent('click', function () { |
||
51 | this[0].options.duration = this[1].options.period; |
||
52 | this[0].updateGraph(); |
||
53 | }.bind([this, menuItem]));
|
||
54 | timeframe.add(menuItem); |
||
55 | }, this);
|
||
56 | |||
57 | this.indicators = new Jx.Menu({ |
||
58 | label: _("Export to CSV"), |
||
59 | image: app_path + 'images/document-export.png', |
||
60 | tooltip: _("Export the content of this graph to CSV"), |
||
61 | }) |
||
62 | |||
63 | new Request.JSON({
|
||
64 | url: app_path + 'rpc/getIndicators', |
||
65 | onSuccess: function (data) { |
||
66 | data.items.each(function (item) {
|
||
67 | this.indicators.add(new Jx.Menu.Item({ |
||
68 | label: item,
|
||
69 | onClick: this.exportCSV.bind(this), |
||
70 | indicator: true |
||
71 | })); |
||
72 | }, this);
|
||
73 | |||
74 | this.indicators.add(new Jx.Menu.Item({ |
||
75 | label: _('All'), |
||
76 | onClick: this.exportCSV.bind(this), |
||
77 | indicator: false |
||
78 | })); |
||
79 | }.bind(this)
|
||
80 | }).get({ |
||
81 | 'host': this.host, |
||
82 | 'graph': this.graph |
||
83 | }); |
||
84 | |||
85 | this.refresh_button = new Jx.Button({ |
||
86 | image: app_path + 'images/refresh.png', |
||
87 | 89829224 | Francois POIROTTE | tooltip: _("Automatically refresh the graph"), |
88 | 7c9baa43 | Francois POIROTTE | toggle: true, |
89 | onDown: function() { |
||
90 | // On s'assure qu'il n'y a pas déjà un timer lancé.
|
||
91 | if ($chk(this.refreshTimer)) |
||
92 | return;
|
||
93 | var delay =
|
||
94 | this.options.refreshDelay ||
|
||
95 | window.refresh_delay; |
||
96 | this.refreshTimer =
|
||
97 | this.updateGraph.periodical(delay * 1000, this); |
||
98 | this.options.autoRefresh = 1; |
||
99 | this.updateURI();
|
||
100 | }.bind(this),
|
||
101 | onUp: function() { |
||
102 | clearInterval(this.refreshTimer);
|
||
103 | this.options.autoRefresh = 0; |
||
104 | this.updateURI();
|
||
105 | }.bind(this)
|
||
106 | }); |
||
107 | |||
108 | toolbar.add( |
||
109 | this.refresh_button,
|
||
110 | timeframe, |
||
111 | new Jx.Button({
|
||
112 | image: app_path + 'images/start.png', |
||
113 | tooltip: _("Graph start"), |
||
114 | onClick: function() { |
||
115 | this.options.start = this.startTime; |
||
116 | this.updateGraph();
|
||
117 | }.bind(this)
|
||
118 | }), |
||
119 | new Jx.Button({
|
||
120 | image: app_path + 'images/previous.png', |
||
121 | tooltip: _("Previous section"), |
||
122 | onClick: function() { |
||
123 | this.options.start -= this.options.duration; |
||
124 | this.updateGraph();
|
||
125 | }.bind(this)
|
||
126 | }), |
||
127 | new Jx.Button({
|
||
128 | image: app_path + 'images/next.png', |
||
129 | tooltip: _("Next section"), |
||
130 | onClick: function() { |
||
131 | this.options.start += this.options.duration; |
||
132 | this.updateGraph();
|
||
133 | }.bind(this)
|
||
134 | }), |
||
135 | new Jx.Button({
|
||
136 | image: app_path + 'images/end.png', |
||
137 | tooltip: _("Graph end"), |
||
138 | onClick: function() { |
||
139 | this.options.start = null; |
||
140 | this.updateGraph();
|
||
141 | }.bind(this)
|
||
142 | }), |
||
143 | new Jx.Button({
|
||
144 | image: app_path + 'images/zoom-in.png', |
||
145 | tooltip: _("Zoom in"), |
||
146 | onClick: function() { |
||
147 | if (this.options.duration > 1) { |
||
148 | this.options.duration /= 2; |
||
149 | this.updateGraph();
|
||
150 | } |
||
151 | }.bind(this)
|
||
152 | }), |
||
153 | new Jx.Button({
|
||
154 | image: app_path + 'images/zoom-out.png', |
||
155 | tooltip: _("Zoom out"), |
||
156 | onClick: function() { |
||
157 | this.options.duration *= 2; |
||
158 | this.updateGraph();
|
||
159 | }.bind(this)
|
||
160 | }), |
||
161 | this.indicators,
|
||
162 | new Jx.Button({
|
||
163 | image: app_path + 'images/document-print-small.png', |
||
164 | tooltip: _("Print graph"), |
||
165 | onClick: this.print.bind(this) |
||
166 | }) |
||
167 | ); |
||
168 | |||
169 | var label = _("Graph for \"%(graph)s\" on \"%(host)s\""); |
||
170 | // Le pattern donné à substitute permet de garder une syntaxe
|
||
171 | // cohérente avec Python (facilite le travail des traducteurs).
|
||
172 | label = label.substitute({ |
||
173 | 'graph': this.graph, |
||
174 | 'host': this.host |
||
175 | }, (/\\?%\(([^()]+)\)s/g));
|
||
176 | |||
177 | this.graph_window = new Jx.Dialog({ |
||
178 | label: label,
|
||
179 | modal: false, |
||
180 | move: true, |
||
181 | close: true, |
||
182 | horizontal: this.options.left + ' left', |
||
183 | vertical: this.options.top + ' top', |
||
184 | width: 575, |
||
185 | height: 75, |
||
186 | toolbars: [toolbar]
|
||
187 | }); |
||
188 | |||
189 | function removeDialog() { |
||
190 | window.graphs.erase(this);
|
||
191 | this.updateURI();
|
||
192 | } |
||
193 | |||
194 | this.updateGraph();
|
||
195 | this.graph_window.open();
|
||
196 | window.graphs.push(this);
|
||
197 | |||
198 | this.refresh_button.setActive(parseInt(this.options.autoRefresh)); |
||
199 | |||
200 | this.graph_window.addEvent('close', removeDialog.bind(this)); |
||
201 | // sizeChange est déclenché à la fois après un redimensionnement
|
||
202 | // et après un déplacement. Ce cas est mal documenté dans JxLib.
|
||
203 | this.graph_window.addEvent('sizeChange', this.dialogMoved.bind(this)); |
||
204 | |||
205 | // Simule un déplacement de la fenêtre,
|
||
206 | // pour mettre à jour les coordonnées.
|
||
207 | this.dialogMoved();
|
||
208 | }, |
||
209 | |||
210 | dialogMoved: function () { |
||
211 | // Repris de l'API interne de JxLib (création du Drag).
|
||
212 | this.options.left = parseInt(this.graph_window.domObj.style.left, 10); |
||
213 | this.options.top = parseInt(this.graph_window.domObj.style.top, 10); |
||
214 | this.updateURI();
|
||
215 | }, |
||
216 | |||
217 | updateURI: function () { |
||
218 | var graphs = [];
|
||
219 | var uri = new URI(); |
||
220 | uri.set('fragment', ''); |
||
221 | |||
222 | window.graphs.each(function (graph) {
|
||
223 | var props = new Hash(graph.options); |
||
224 | props.extend({host: graph.host, graph: graph.graph}); |
||
225 | this.push(props.toQueryString());
|
||
226 | }, graphs); |
||
227 | |||
228 | uri.setData({'graphs': graphs, safety: 1}, false, 'fragment'); |
||
229 | uri.go(); |
||
230 | }, |
||
231 | |||
232 | 89829224 | Francois POIROTTE | getStartTime: function () { |
233 | if (this.options.start == null) |
||
234 | // @TODO: cette heure est en localtime a priori.
|
||
235 | return (new Date() / 1000).toInt() - this.options.duration; |
||
236 | return this.options.start; |
||
237 | }, |
||
238 | |||
239 | 7c9baa43 | Francois POIROTTE | exportCSV: function (menuItem) { |
240 | var uri = new URI(app_path + 'vigirrd/' + |
||
241 | encodeURIComponent(this.host) + '/export'); |
||
242 | |||
243 | 89829224 | Francois POIROTTE | var start = this.getStartTime(); |
244 | 7c9baa43 | Francois POIROTTE | |
245 | uri.setData({ |
||
246 | host: this.host, |
||
247 | graphtemplate: this.graph, |
||
248 | start: start,
|
||
249 | end: start + this.options.duration, |
||
250 | nocache: (new Date() / 1) |
||
251 | }) |
||
252 | |||
253 | if (menuItem.options.indicator)
|
||
254 | uri.setData({ds: menuItem.options.label}, true); |
||
255 | |||
256 | window.open(uri.toString()); |
||
257 | }, |
||
258 | |||
259 | 89829224 | Francois POIROTTE | // Cette fonction est aussi utilisée dans print.js
|
260 | // pour gérer l'impression globale.
|
||
261 | getPrintParams: function () { |
||
262 | var img = this.graph_window.content.getElement('img'); |
||
263 | var img_uri = new URI(img.src); |
||
264 | var params = img_uri.getData();
|
||
265 | return {
|
||
266 | host: params.host,
|
||
267 | start: params.start,
|
||
268 | duration: params.duration,
|
||
269 | graph: params.graphtemplate,
|
||
270 | nocache: params.nocache
|
||
271 | } |
||
272 | }, |
||
273 | |||
274 | 7c9baa43 | Francois POIROTTE | print: function () { |
275 | var uri = new URI(app_path + 'rpc/graphsList'); |
||
276 | 89829224 | Francois POIROTTE | uri.setData({graphs: [this.getPrintParams()]}); |
277 | 7c9baa43 | Francois POIROTTE | var print_window = window.open(uri.toString());
|
278 | print_window.print(); |
||
279 | }, |
||
280 | |||
281 | updateGraph: function () { |
||
282 | var uri = new URI(app_path + 'vigirrd/' + |
||
283 | encodeURIComponent(this.host) + '/graph.png'); |
||
284 | |||
285 | 89829224 | Francois POIROTTE | var start = this.getStartTime(); |
286 | 7c9baa43 | Francois POIROTTE | |
287 | uri.setData({ |
||
288 | host: this.host, |
||
289 | start: start,
|
||
290 | duration: this.options.duration, |
||
291 | graphtemplate: this.graph, |
||
292 | // Permet d'empêcher la mise en cache du graphe.
|
||
293 | // Nécessaire car le graphe évolue dynamiquement au fil de l'eau.
|
||
294 | nocache: (new Date() / 1) |
||
295 | }); |
||
296 | // On génère dynamiquement une balise "img" pour charger le graphe.
|
||
297 | this.graph_window.setContent(
|
||
298 | '<img src="' + uri.toString() + '"/' + '>'); |
||
299 | var img = this.graph_window.content.getElement('img'); |
||
300 | img.addEvent('load', function () { |
||
301 | this[0].graph_window.resize( |
||
302 | this[1].width + 25, |
||
303 | this[1].height + 88 |
||
304 | ); |
||
305 | }.bind([this, img]));
|
||
306 | img.addEvent('error', function () { |
||
307 | var msg = _(
|
||
308 | 'Could not load the graph for "%(graph)s" on "%(host)s".\n' +
|
||
309 | 'Make sure VigiRRD is running and receives performance data.'
|
||
310 | ); |
||
311 | // Le pattern donné à substitute permet de garder une syntaxe
|
||
312 | // cohérente avec Python (facilite le travail des traducteurs).
|
||
313 | msg = msg.substitute({ |
||
314 | 'graph': this.graph, |
||
315 | 'host': this.host |
||
316 | }, (/\\?%\(([^()]+)\)s/g));
|
||
317 | alert(msg); |
||
318 | this.graph_window.close();
|
||
319 | }.bind(this));
|
||
320 | } |
||
321 | }); |
||
322 | |||
323 | var refresh_delay = 30; |
||
324 | var graphs = [];
|
||
325 | window.addEvent('load', function () { |
||
326 | new Request.JSON({
|
||
327 | url: app_path + 'rpc/tempoDelayRefresh', |
||
328 | onSuccess: function (data) { |
||
329 | window.refresh_delay = data.delay; |
||
330 | } |
||
331 | }).get(); |
||
332 | |||
333 | // On réouvre les graphes précédemment chargés.
|
||
334 | var graphs = [];
|
||
335 | var uri = new URI(); |
||
336 | var qs = new Hash(uri.get('fragment').parseQueryString()); |
||
337 | if (qs.has('graphs')) { |
||
338 | graphs = (new Hash(qs.get('graphs'))).getValues(); |
||
339 | } |
||
340 | |||
341 | graphs.each(function (graph) {
|
||
342 | var uri = new URI('?' + graph); |
||
343 | var qs = new Hash(uri.getData()); |
||
344 | if (qs.has('host') && qs.has('graph')) { |
||
345 | var options = new Hash(); |
||
346 | var params = [
|
||
347 | 'start',
|
||
348 | 'duration',
|
||
349 | 'left',
|
||
350 | 'top',
|
||
351 | 'autoRefresh',
|
||
352 | 'refreshDelay'
|
||
353 | ]; |
||
354 | |||
355 | params.each(function (param) {
|
||
356 | if (this[0].has(param)) |
||
357 | this[1].set(param, this[0].get(param)); |
||
358 | }, [qs, options]); |
||
359 | |||
360 | new Graph(
|
||
361 | options.getClean(), |
||
362 | qs.get('host'),
|
||
363 | qs.get('graph')
|
||
364 | ); |
||
365 | } |
||
366 | }); |
||
367 | |||
368 | // Nécessaire car le constructeur de Graph ajoute les graphes à l'URI.
|
||
369 | // On restaure donc l'ancienne URI ici, pour éviter les doublons.
|
||
370 | if (graphs.length)
|
||
371 | uri.go(); |
||
372 | }); |