2abe78dac74cd091c83406543d0aba16dbf15842
[project/luci.git] / applications / luci-app-statistics / htdocs / luci-static / resources / view / statistics / graphs.js
1 'use strict';
2 'require ui';
3 'require uci';
4 'require statistics.rrdtool as rrdtool';
5
6 var pollFn = null,
7 activePlugin = null,
8 activeInstance = null;
9
10 return L.view.extend({
11 load: function() {
12 return rrdtool.load();
13 },
14
15 updatePluginTab: function(host, span, time, ev) {
16 var container = ev.target,
17 plugin = ev.detail.tab,
18 plugin_instances = rrdtool.pluginInstances(host.value, plugin);
19
20 activePlugin = plugin;
21
22 L.dom.content(container, [
23 E('p', {}, [
24 E('em', { 'class': 'spinning' }, [ _('Loading data…') ])
25 ])
26 ]);
27
28 Promise.all(plugin_instances.map(function(instance) {
29 return rrdtool.render(plugin, instance, false, host.value, span.value, Math.max(200, container.offsetWidth - 100));
30 })).then(function(blobs) {
31 var multiple = blobs.length > 1;
32
33 L.dom.content(container, E('div', {}, blobs.map(function(blobs, i) {
34 var plugin_instance = plugin_instances[i];
35
36 return E('div', {
37 'class': 'center',
38 'data-tab': multiple ? i : null,
39 'data-tab-title': multiple ? '%s: %s'.format(rrdtool.pluginTitle(plugin), plugin_instances[i]) : null,
40 'data-plugin': plugin,
41 'data-plugin-instance': plugin_instances[i],
42 'cbi-tab-active': function(ev) { activeInstance = ev.target.getAttribute('data-plugin-instance') }
43 }, blobs.map(function(blob) {
44 return E('img', {
45 'src': URL.createObjectURL(new Blob([blob], { type: 'image/png' }))
46 });
47 }));
48 })));
49
50 if (multiple)
51 ui.tabs.initTabGroup(container.lastElementChild.childNodes);
52 else
53 activeInstance = plugin_instances[0];
54 });
55 },
56
57 updateGraphs: function(host, span, time, container, ev) {
58 var plugin_names = rrdtool.pluginNames(host.value);
59
60 container.querySelectorAll('img').forEach(function(img) {
61 URL.revokeObjectURL(img.src);
62 });
63
64 L.dom.content(container, null);
65
66 if (container.hasAttribute('data-initialized')) {
67 container.removeAttribute('data-initialized');
68 container.parentNode.removeChild(container.previousElementSibling);
69 }
70
71 for (var i = 0; i < plugin_names.length; i++) {
72 if (!rrdtool.hasDefinition(plugin_names[i]))
73 continue;
74
75 container.appendChild(E('div', {
76 'data-tab': plugin_names[i],
77 'data-tab-title': rrdtool.pluginTitle(plugin_names[i]),
78 'cbi-tab-active': L.bind(this.updatePluginTab, this, host, span, time)
79 }, [
80 E('p', {}, [
81 E('em', { 'class': 'spinning' }, [ _('Loading data…') ])
82 ])
83 ]));
84 }
85
86 ui.tabs.initTabGroup(container.childNodes);
87 },
88
89 refreshGraphs: function(host, span, time, container) {
90 var div = document.querySelector('[data-plugin="%s"][data-plugin-instance="%s"]'.format(activePlugin, activeInstance || ''));
91
92 return rrdtool.render(activePlugin, activeInstance || '', false, host.value, span.value, Math.max(200, container.offsetWidth - 100)).then(function(blobs) {
93 return Promise.all(blobs.map(function(blob) {
94 return new Promise(function(resolveFn, rejectFn) {
95 var img = E('img', { 'src': URL.createObjectURL(new Blob([blob], { type: 'image/png' })) });
96 img.onload = function(ev) { resolveFn(img) };
97 img.onerror = function(ev) { resolveFn(img) };
98 });
99 })).then(function(imgs) {
100 while (div.childNodes.length > imgs.length)
101 div.removeChild(div.lastElementChild);
102
103 for (var i = 0; i < imgs.length; i++) {
104 if (i < div.childNodes.length) {
105 URL.revokeObjectURL(div.childNodes[i].src);
106 div.childNodes[i].src = imgs[i].src;
107 }
108 else {
109 div.appendChild(E('img', { 'src': imgs[i].src }));
110 }
111 }
112 });
113 });
114 },
115
116 togglePolling: function(host, span, time, container, ev) {
117 var btn = ev.currentTarget;
118
119 if (pollFn) {
120 L.Poll.remove(pollFn);
121 pollFn = null;
122 }
123
124 if (time.value != '0') {
125 pollFn = L.bind(this.refreshGraphs, this, host, span, time, container);
126 L.Poll.add(pollFn, +time.value);
127 }
128 },
129
130 render: function() {
131 var hosts = rrdtool.hostInstances();
132 return hosts.length ? this.renderGraphs() : this.renderNoData();
133 },
134
135 renderNoData: function() {
136 ui.showModal(_('No RRD data found'), [
137 E('p', {}, _('There is no RRD data available yet to render graphs.')),
138 E('p', {}, _('You need to configure <em>collectd</em> to gather data into <em>.rrd</em> files.')),
139 E('div', { 'class': 'right' }, [
140 E('button', {
141 'class': 'cbi-button',
142 'click': function(ev) { location.href = 'collectd' }
143 }, [ _('Setup collectd') ])
144 ])
145 ]);
146 },
147
148 renderGraphs: function() {
149 var hostSel = E('select', { 'style': 'max-width:170px', 'data-name': 'host' }, rrdtool.hostInstances().map(function(host) {
150 return E('option', {
151 'selected': (rrdtool.opts.host == host) ? 'selected' : null
152 }, [ host ])
153 }));
154
155 var spanSel = E('select', { 'style': 'max-width:170px', 'data-name': 'timespan' }, L.toArray(uci.get('luci_statistics', 'collectd_rrdtool', 'RRATimespans')).map(function(span) {
156 return E('option', {
157 'selected': (rrdtool.opts.timespan == span) ? 'selected' : null
158 }, [ span ])
159 }));
160
161 var timeSel = E('select', { 'style': 'max-width:170px', 'data-name': 'refresh' }, [
162 E('option', { 'value': 0 }, [ _('Do not refresh') ]),
163 E('option', { 'value': 5 }, [ _('Every 5 seconds') ]),
164 E('option', { 'value': 30 }, [ _('Every 30 seconds') ]),
165 E('option', { 'value': 60 }, [ _('Every minute') ])
166 ]);
167
168 var graphDiv = E('div', { 'data-name': 'graphs' });
169
170 var view = E([], [
171 E('h2', {}, [ _('Statistics') ]),
172 E('div', {}, [
173 E('div', {}, [
174 hostSel,
175 E('button', {
176 'class': 'cbi-button cbi-button-apply',
177 'click': ui.createHandlerFn(this, 'updateGraphs', hostSel, spanSel, timeSel, graphDiv, )
178 }, [ _('Display Host »') ]),
179 ' ',
180 spanSel,
181 E('button', {
182 'class': 'cbi-button cbi-button-apply',
183 'click': ui.createHandlerFn(this, 'updateGraphs', hostSel, spanSel, timeSel, graphDiv)
184 }, [ _('Display timespan »') ]),
185 ' ',
186 timeSel,
187 E('button', {
188 'class': 'cbi-button cbi-button-apply',
189 'click': ui.createHandlerFn(this, 'togglePolling', hostSel, spanSel, timeSel, graphDiv)
190 }, [ _('Apply interval »') ])
191 ]),
192 E('hr'),
193 graphDiv
194 ])
195 ]);
196
197 requestAnimationFrame(L.bind(this.updateGraphs, this, hostSel, spanSel, timeSel, graphDiv));
198
199 return view;
200 },
201
202 handleSave: null,
203 handleSaveApply: null,
204 handleReset: null
205 });