Project

General

Profile

Statistics
| Branch: | Tag: | Revision:

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
});