contrib/php-collection: Add a PHP frontend for graph generation.
[collectd.git] / contrib / php-collection / browser.js
1 /*
2  * Copyright (C) 2009  Bruno PrĂ©mont <bonbons AT linux-vserver.org>
3  *
4  * This program is free software; you can redistribute it and/or modify it under
5  * the terms of the GNU General Public License as published by the Free Software
6  * Foundation; only version 2 of the License is applicable.
7  *
8  * This program is distributed in the hope that it will be useful, but WITHOUT
9  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
10  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
11  * details.
12  *
13  * You should have received a copy of the GNU General Public License along with
14  * this program; if not, write to the Free Software Foundation, Inc.,
15  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
16  */
17
18 // Toggle visibility of a div
19 function toggleDiv(divID) {
20         var div   = document.getElementById(divID);
21         var label = document.getElementById(divID+'_sw');
22         var label_txt = null;
23         if (div) {
24                 if (div.style.display == 'none') {
25                         div.style.display = 'block';
26                         label_txt = 'Hide';
27                 } else {
28                         div.style.display = 'none';
29                         label_txt = 'Show';
30                 }
31         }
32         if (label_txt && label) {
33                 var childCnt = label.childNodes.length;
34                 while (childCnt > 0)
35                         label.removeChild(label.childNodes[--childCnt]);
36                 label.appendChild(document.createTextNode(label_txt));
37         }
38 }
39
40 // DHTML helper code to asynchronous loading of content
41 function loadXMLDoc(url, query) {
42         if (window.XMLHttpRequest) {
43                 req = new XMLHttpRequest();
44                 req.onreadystatechange = processReqChange;
45                 req.open('POST', url, true);
46                 req.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8'); 
47                 req.send(query);
48         } else if (window.ActiveXObject) {
49                 req = new ActiveXObject("Microsoft.XMLHTTP");
50                 if (req) {
51                         req.onreadystatechange = processReqChange;
52                         req.open('POST', url, true);
53                         req.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8'); 
54                         req.send(query);
55                 }
56         }
57 }
58
59 // DHTML new-content dispatcher
60 function processReqChange() {
61         if (req.readyState == 4) {
62                 if (req.status == 200) {
63                         response = req.responseXML.documentElement;
64                         method = response.getElementsByTagName('method')[0].firstChild.data;
65                         result = response.getElementsByTagName('result')[0];
66                         eval(method + '(result)');
67                 }
68         }
69 }
70
71 // Update contents of a <select> drop-down list
72 function refillSelect(options, select) {
73         if (!select)
74                 return -1;
75
76         var childCnt = select.childNodes.length;
77         var oldValue = select.selectedIndex > 0 ? select.options[select.selectedIndex].value : '/';
78         while (childCnt > 0)
79                 select.removeChild(select.childNodes[--childCnt]);
80
81         var optCnt = options ? options.length : 0;
82         if (optCnt == 0) {
83                 select.setAttribute('disabled', 'disabled');
84                 return -1;
85         } else {
86                 select.removeAttribute('disabled');
87                 var keepSelection = false;
88                 if (optCnt == 1) {
89                         keepSelection = true;
90                         oldValue = options[0].firstChild ? options[0].firstChild.data : '';
91                 } else if (oldValue != '/') {
92                         for (i = 0; i < optCnt && !keepSelection; i++)
93                                 if (oldValue == (options[i].firstChild ? options[i].firstChild.data : ''))
94                                         keepSelection = true;
95                 }
96                 newOption = document.createElement("option");
97                 newOption.value = '/';
98                 if (keepSelection)
99                         newOption.setAttribute('disabled', 'disabled');
100                 else
101                         newOption.setAttribute('selected', 'selected');
102                 newOption.setAttribute('style', 'font-style: italic');
103                 newOption.appendChild(document.createTextNode('- please select -'));
104                 select.appendChild(newOption);
105                 for (i = 0; i < optCnt; i++) {
106                         newOption = document.createElement("option");
107                         newOption.value = options[i].firstChild ? options[i].firstChild.data : '';
108                         if (keepSelection && newOption.value == oldValue)
109                                 newOption.setAttribute('selected', 'selected');
110                         if (keepSelection && optCnt == 1 && newOption.value == '@') {
111                                 newOption.setAttribute('style', 'font-style: italic');
112                                 newOption.appendChild(document.createTextNode('Meta graph'));
113                         } else
114                                 newOption.appendChild(document.createTextNode(newOption.value));
115                         select.appendChild(newOption);
116                 }
117                 return keepSelection ? select.selectedIndex : -1;
118         }
119 }
120
121 // Request refresh of host list
122 function ListRefreshHost() {
123         var query = 'action=list_hosts';
124         loadXMLDoc(dhtml_url, query);
125 }
126
127 // Handle update to host list
128 function ListOfHost(response) {
129         var select = document.getElementById('host_list');
130         var idx = refillSelect(response ? response.getElementsByTagName('option') : null, select);
131         if (idx > 0) {
132                 ListRefreshPlugin();
133         } else
134                 ListOfPlugin(null);
135 }
136
137 // Request refresh of plugin list
138 function ListRefreshPlugin() {
139         var host_list = document.getElementById('host_list');
140         var host      = host_list.selectedIndex >= 0 ? host_list.options[host_list.selectedIndex].value : '/';
141         if (host != '/') {
142                 var query = 'action=list_plugins&host='+encodeURIComponent(host);
143                 loadXMLDoc(dhtml_url, query);
144         } else {
145                 ListOfPlugin(null);
146         }
147 }
148
149 // Handle update to plugin list
150 function ListOfPlugin(response) {
151         var select = document.getElementById('plugin_list');
152         var idx = refillSelect(response ? response.getElementsByTagName('option') : null, select);
153         if (idx > 0) {
154                 ListRefreshPluginInstance();
155         } else
156                 ListOfPluginInstance(null);
157 }
158
159 // Request refresh of plugin instance list
160 function ListRefreshPluginInstance() {
161         var host_list   = document.getElementById('host_list');
162         var host        = host_list.selectedIndex >= 0 ? host_list.options[host_list.selectedIndex].value : '/';
163         var plugin_list = document.getElementById('plugin_list');
164         var plugin      = plugin_list.selectedIndex >= 0 ? plugin_list.options[plugin_list.selectedIndex].value : '/';
165         if (host != '/' && plugin != '/') {
166                 var query = 'action=list_pinsts&host='+encodeURIComponent(host)+'&plugin='+encodeURIComponent(plugin);
167                 loadXMLDoc(dhtml_url, query);
168         } else {
169                 ListOfPluginInstance(null);
170         }
171 }
172
173 // Handle update of plugin instance list
174 function ListOfPluginInstance(response) {
175         var select = document.getElementById('pinst_list');
176         var idx = refillSelect(response ? response.getElementsByTagName('option') : null, select);
177         if (idx > 0) {
178                 ListRefreshType();
179         } else
180                 ListOfType(null);
181 }
182
183 // Request refresh of type list
184 function ListRefreshType() {
185         var host_list   = document.getElementById('host_list');
186         var host        = host_list.selectedIndex >= 0 ? host_list.options[host_list.selectedIndex].value : '/';
187         var plugin_list = document.getElementById('plugin_list');
188         var plugin      = plugin_list.selectedIndex >= 0 ? plugin_list.options[plugin_list.selectedIndex].value : '/';
189         var pinst_list  = document.getElementById('pinst_list');
190         var pinst       = pinst_list.selectedIndex >= 0 ? pinst_list.options[pinst_list.selectedIndex].value : '/';
191         if (host != '/' && plugin != '/' && pinst != '/') {
192                 var query = 'action=list_types&host='+encodeURIComponent(host)+'&plugin='+encodeURIComponent(plugin)+'&plugin_instance='+encodeURIComponent(pinst);
193                 loadXMLDoc(dhtml_url, query);
194         } else {
195                 ListOfType(null);
196         }
197 }
198
199 // Handle update of type list
200 function ListOfType(response) {
201         var select = document.getElementById('type_list');
202         var idx = refillSelect(response ? response.getElementsByTagName('option') : null, select);
203         if (idx > 0) {
204                 ListRefreshTypeInstance();
205         } else
206                 ListOfTypeInstance(null);
207 }
208
209 // Request refresh of type instance list
210 function ListRefreshTypeInstance() {
211         var host_list   = document.getElementById('host_list');
212         var host        = host_list.selectedIndex >= 0 ? host_list.options[host_list.selectedIndex].value : '/';
213         var plugin_list = document.getElementById('plugin_list');
214         var plugin      = plugin_list.selectedIndex >= 0 ? plugin_list.options[plugin_list.selectedIndex].value : '/';
215         var pinst_list  = document.getElementById('pinst_list');
216         var pinst       = pinst_list.selectedIndex >= 0 ? pinst_list.options[pinst_list.selectedIndex].value : '/';
217         var type_list   = document.getElementById('type_list');
218         var type        = type_list.selectedIndex >= 0 ? type_list.options[type_list.selectedIndex].value : '/';
219         if (host != '/' && plugin != '/' && pinst != '/' && type != '/') {
220                 var query = 'action=list_tinsts&host='+encodeURIComponent(host)+'&plugin='+encodeURIComponent(plugin)+'&plugin_instance='+encodeURIComponent(pinst)+'&type='+encodeURIComponent(type);
221                 loadXMLDoc(dhtml_url, query);
222         } else {
223                 ListOfTypeInstance(null);
224         }
225 }
226
227 // Handle update of type instance list
228 function ListOfTypeInstance(response) {
229         var select = document.getElementById('tinst_list');
230         var idx = refillSelect(response ? response.getElementsByTagName('option') : null, select);
231         if (idx > 0) {
232                 // Enable add button
233                 RefreshButtons();
234         } else {
235                 // Disable add button
236                 RefreshButtons();
237         }
238 }
239
240 function RefreshButtons() {
241         var host_list   = document.getElementById('host_list');
242         var host        = host_list.selectedIndex >= 0 ? host_list.options[host_list.selectedIndex].value : '/';
243         var plugin_list = document.getElementById('plugin_list');
244         var plugin      = plugin_list.selectedIndex >= 0 ? plugin_list.options[plugin_list.selectedIndex].value : '/';
245         var pinst_list  = document.getElementById('pinst_list');
246         var pinst       = pinst_list.selectedIndex >= 0 ? pinst_list.options[pinst_list.selectedIndex].value : '/';
247         var type_list   = document.getElementById('type_list');
248         var type        = type_list.selectedIndex >= 0 ? type_list.options[type_list.selectedIndex].value : '/';
249         var tinst_list  = document.getElementById('tinst_list');
250         var tinst       = tinst_list.selectedIndex >= 0 ? tinst_list.options[tinst_list.selectedIndex].value : '/';
251         if (host != '/' && plugin != '/' && pinst != '/' && type != '/' && tinst != '/') {
252                 document.getElementById('btnAdd').removeAttribute('disabled');
253         } else {
254                 document.getElementById('btnAdd').setAttribute('disabled', 'disabled');
255         }
256
257         var graphs = document.getElementById('graphs');
258         if (graphs.getElementsByTagName('div').length > 1) {
259                 document.getElementById('btnClear').removeAttribute('disabled');
260                 document.getElementById('btnRefresh').removeAttribute('disabled');
261         } else {
262                 document.getElementById('btnClear').setAttribute('disabled', 'disabled');
263                 document.getElementById('btnRefresh').setAttribute('disabled', 'disabled');
264         }
265 }
266
267 var nextGraphId = 1;
268
269 function GraphAppend() {
270         var graphs      = document.getElementById('graphs');
271         var host_list   = document.getElementById('host_list');
272         var host        = host_list.selectedIndex >= 0 ? host_list.options[host_list.selectedIndex].value : '/';
273         var plugin_list = document.getElementById('plugin_list');
274         var plugin      = plugin_list.selectedIndex >= 0 ? plugin_list.options[plugin_list.selectedIndex].value : '/';
275         var pinst_list  = document.getElementById('pinst_list');
276         var pinst       = pinst_list.selectedIndex >= 0 ? pinst_list.options[pinst_list.selectedIndex].value : '/';
277         var type_list   = document.getElementById('type_list');
278         var type        = type_list.selectedIndex >= 0 ? type_list.options[type_list.selectedIndex].value : '/';
279         var tinst_list  = document.getElementById('tinst_list');
280         var tinst       = tinst_list.selectedIndex >= 0 ? tinst_list.options[tinst_list.selectedIndex].value : '/';
281
282         if (host != '/' && plugin != '/' && pinst != '/' && type != '/') {
283                 var graph_id   = 'graph_'+nextGraphId++;
284                 var graph_src  = graph_url+'?host='+encodeURIComponent(host)+'&plugin='+encodeURIComponent(plugin)+'&plugin_instance='+encodeURIComponent(pinst)+'&type='+encodeURIComponent(type);
285                 var graph_alt  = '';
286                 var grap_title = '';
287                 if (tinst == '@') {
288                         graph_alt   = host+'/'+plugin+(pinst.length > 0 ? '-'+pinst : '')+'/'+type;
289                         graph_title = type+' of '+plugin+(pinst.length > 0 ? '-'+pinst : '')+' plugin for '+host;
290                 } else {
291                         graph_alt   = host+'/'+plugin+(pinst.length > 0 ? '-'+pinst : '')+'/'+type+(tinst.length > 0 ? '-'+tinst : '');
292                         graph_title = type+(tinst.length > 0 ? '-'+tinst : '')+' of '+plugin+(pinst.length > 0 ? '-'+pinst : '')+' plugin for '+host;
293                         graph_src  += '&type_instance='+encodeURIComponent(tinst);
294                 }
295                 if (document.getElementById('logarithmic').checked)
296                         graph_src += '&logarithmic=1';
297                 if (document.getElementById('tinylegend').checked)
298                         graph_src += '&tinylegend=1';
299                 if (document.getElementById('timespan').selectedIndex >= 0)
300                         graph_src += '&timespan='+encodeURIComponent(document.getElementById('timespan').options[document.getElementById('timespan').selectedIndex].value);
301                 var now    = new Date();
302                 graph_src += '&ts='+now.getTime();
303
304                 // Graph container
305                 newGraph = document.createElement('div');
306                 newGraph.setAttribute('class', 'graph');
307                 newGraph.setAttribute('id', graph_id);
308                 // Graph cell + graph
309                 newDiv = document.createElement('div');
310                 newDiv.setAttribute('class', 'graph_img');
311                 newImg = document.createElement('img');
312                 newImg.setAttribute('src', graph_src);
313                 newImg.setAttribute('alt', graph_alt);
314                 newImg.setAttribute('title', graph_title);
315                 newDiv.appendChild(newImg);
316                 newGraph.appendChild(newDiv);
317                 // Graph tools
318                 newDiv = document.createElement('div');
319                 newDiv.setAttribute('class', 'graph_opts');
320                 // - move up
321                 newImg = document.createElement('img');
322                 newImg.setAttribute('src', 'move-up.png');
323                 newImg.setAttribute('alt', 'UP');
324                 newImg.setAttribute('title', 'Move graph up');
325                 newA = document.createElement('a');
326                 newA.setAttribute('href', 'javascript:GraphMoveUp("'+graph_id+'")');
327                 newA.appendChild(newImg);
328                 newDiv.appendChild(newA);
329                 newDiv.appendChild(document.createElement('br'));
330                 // - refresh
331                 newImg = document.createElement('img');
332                 newImg.setAttribute('src', 'refresh.png');
333                 newImg.setAttribute('alt', 'R');
334                 newImg.setAttribute('title', 'Refresh graph');
335                 newA = document.createElement('a');
336                 newA.setAttribute('href', 'javascript:GraphRefresh("'+graph_id+'")');
337                 newA.appendChild(newImg);
338                 newDiv.appendChild(newA);
339                 newDiv.appendChild(document.createElement('br'));
340                 // - remove
341                 newImg = document.createElement('img');
342                 newImg.setAttribute('src', 'delete.png');
343                 newImg.setAttribute('alt', 'RM');
344                 newImg.setAttribute('title', 'Remove graph');
345                 newA = document.createElement('a');
346                 newA.setAttribute('href', 'javascript:GraphRemove("'+graph_id+'")');
347                 newA.appendChild(newImg);
348                 newDiv.appendChild(newA);
349                 newDiv.appendChild(document.createElement('br'));
350                 // - move down
351                 newImg = document.createElement('img');
352                 newImg.setAttribute('src', 'move-down.png');
353                 newImg.setAttribute('alt', 'DN');
354                 newImg.setAttribute('title', 'Move graph down');
355                 newA = document.createElement('a');
356                 newA.setAttribute('href', 'javascript:GraphMoveDown("'+graph_id+'")');
357                 newA.appendChild(newImg);
358                 newDiv.appendChild(newA);
359                 newGraph.appendChild(newDiv);
360
361                 graphs.appendChild(newGraph);
362         }
363         document.getElementById('nograph').style.display = 'none';
364         RefreshButtons();
365 }
366
367 function GraphDropAll() {
368         var graphs = document.getElementById('graphs');
369         var childCnt = graphs.childNodes.length;
370         while (childCnt > 0)
371                 if (graphs.childNodes[--childCnt].id != 'nograph' && (graphs.childNodes[childCnt].nodeName == 'div' || graphs.childNodes[childCnt].nodeName == 'DIV'))
372                         graphs.removeChild(graphs.childNodes[childCnt]);
373                 else if (graphs.childNodes[childCnt].id == 'nograph')
374                         graphs.childNodes[childCnt].style.display = 'block';
375         RefreshButtons();
376 }
377
378 function GraphRefresh(graph) {
379         if (graph == null) {
380                 var imgs   = document.getElementById('graphs').getElementsByTagName('img');
381                 var imgCnt = imgs.length;
382                 var now    = new Date();
383                 var newTS  = '&ts='+now.getTime();
384                 while (imgCnt > 0)
385                         if (imgs[--imgCnt].parentNode.getAttribute('class') == 'graph_img') {
386                                 var oldSrc = imgs[imgCnt].src;
387                                 var newSrc = oldSrc.replace(/&ts=[0-9]+/, newTS);
388                                 if (newSrc == oldSrc)
389                                         newSrc = newSrc + newTS;
390                                 imgs[imgCnt].setAttribute('src', newSrc);
391                         }
392         } else if (document.getElementById(graph)) {
393                 var imgs = document.getElementById(graph).getElementsByTagName('img');
394                 var imgCnt = imgs.length;
395                 while (imgCnt > 0)
396                         if (imgs[--imgCnt].parentNode.getAttribute('class') == 'graph_img') {
397                                 var now    = new Date();
398                                 var newTS  = '&ts='+now.getTime();
399                                 var oldSrc = imgs[imgCnt].src;
400                                 var newSrc = oldSrc.replace(/&ts=[0-9]+/, newTS);
401                                 if (newSrc == oldSrc)
402                                         newSrc = newSrc+newTS;
403                                 imgs[imgCnt].setAttribute('src', newSrc);
404                                 break;
405                         }
406         }
407 }
408
409 function GraphRemove(graph) {
410         var graphs = document.getElementById('graphs');
411         if (document.getElementById(graph)) {
412                 graphs.removeChild(document.getElementById(graph));
413                 RefreshButtons();
414                 if (graphs.getElementsByTagName('div').length == 1)
415                         document.getElementById('nograph').style.display = 'block';
416         }
417 }
418
419 function GraphMoveUp(graph) {
420         var graphs    = document.getElementById('graphs');
421         var childCnt  = graphs.childNodes.length;
422         var prevGraph = null;
423         for (i = 0; i < childCnt; i++)
424                 if (graphs.childNodes[i].nodeName == 'div' || graphs.childNodes[i].nodeName == 'DIV') {
425                         if (graphs.childNodes[i].id == 'nograph') {
426                                 // Skip
427                         } else if (graphs.childNodes[i].id == graph) {
428                                 var myGraph = graphs.childNodes[i];
429                                 if (prevGraph) {
430                                         graphs.removeChild(myGraph);
431                                         graphs.insertBefore(myGraph, prevGraph);
432                                 }
433                                 break;
434                         } else
435                                 prevGraph = graphs.childNodes[i];
436                 }
437 }
438
439 function GraphMoveDown(graph) {
440         var graphs    = document.getElementById('graphs');
441         var childCnt  = graphs.childNodes.length;
442         var nextGraph = null;
443         var myGraph   = null;
444         for (i = 0; i < childCnt; i++)
445                 if (graphs.childNodes[i].nodeName == 'div' || graphs.childNodes[i].nodeName == 'DIV') {
446                         if (graphs.childNodes[i].id == 'nograph') {
447                                 // Skip
448                         } else if (graphs.childNodes[i].id == graph) {
449                                 myGraph = graphs.childNodes[i];
450                         } else if (myGraph) {
451                                 nextGraph = graphs.childNodes[i];
452                                 graphs.removeChild(nextGraph);
453                                 graphs.insertBefore(nextGraph, myGraph);
454                                 break;
455                         }
456                 }
457 }
458