php-collection: New graph toolbox
[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         GraphPositionToolbox(null);
39 }
40
41 var req = null;
42
43 // DHTML helper code to asynchronous loading of content
44 function loadXMLDoc(url, query) {
45         if (window.XMLHttpRequest) {
46                 req = new XMLHttpRequest();
47                 req.onreadystatechange = processReqChange;
48                 req.open('POST', url, true);
49                 req.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8'); 
50                 req.send(query);
51         } else if (window.ActiveXObject) {
52                 req = new ActiveXObject("Microsoft.XMLHTTP");
53                 if (req) {
54                         req.onreadystatechange = processReqChange;
55                         req.open('POST', url, true);
56                         req.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8'); 
57                         req.send(query);
58                 }
59         }
60 }
61
62 // DHTML new-content dispatcher
63 function processReqChange(evt) {
64         if (req.readyState == 4) {
65                 if (req.status == 200) {
66                         var response = req.responseXML.documentElement;
67                         var method = response.getElementsByTagName('method')[0].firstChild.data;
68                         var result = response.getElementsByTagName('result')[0];
69                         req = null;
70                         eval(method + '(result)');
71                 }
72         }
73 }
74
75 // Update contents of a <select> drop-down list
76 function refillSelect(options, select) {
77         if (!select)
78                 return -1;
79
80         var childCnt = select.childNodes.length;
81         var oldValue = select.selectedIndex > 0 ? select.options[select.selectedIndex].value : '/';
82         while (childCnt > 0)
83                 select.removeChild(select.childNodes[--childCnt]);
84
85         var optCnt = options ? options.length : 0;
86         if (optCnt == 0) {
87                 select.setAttribute('disabled', 'disabled');
88                 return -1;
89         } else {
90                 select.removeAttribute('disabled');
91                 var keepSelection = false;
92                 if (optCnt == 1) {
93                         keepSelection = true;
94                         oldValue = options[0].firstChild ? options[0].firstChild.data : '';
95                 } else if (oldValue != '/') {
96                         for (i = 0; i < optCnt && !keepSelection; i++)
97                                 if (oldValue == (options[i].firstChild ? options[i].firstChild.data : ''))
98                                         keepSelection = true;
99                 }
100                 newOption = document.createElement("option");
101                 newOption.value = '/';
102                 if (keepSelection)
103                         newOption.setAttribute('disabled', 'disabled');
104                 else
105                         newOption.setAttribute('selected', 'selected');
106                 newOption.setAttribute('style', 'font-style: italic');
107                 newOption.appendChild(document.createTextNode('- please select -'));
108                 select.appendChild(newOption);
109                 for (i = 0; i < optCnt; i++) {
110                         newOption = document.createElement("option");
111                         newOption.value = options[i].firstChild ? options[i].firstChild.data : '';
112                         if (keepSelection && newOption.value == oldValue)
113                                 newOption.setAttribute('selected', 'selected');
114                         if (keepSelection && optCnt == 1 && newOption.value == '@') {
115                                 newOption.setAttribute('style', 'font-style: italic');
116                                 newOption.appendChild(document.createTextNode('Meta graph'));
117                         } else
118                                 newOption.appendChild(document.createTextNode(newOption.value));
119                         select.appendChild(newOption);
120                 }
121                 return keepSelection ? select.selectedIndex : -1;
122         }
123 }
124
125 // Request refresh of host list
126 function ListRefreshHost() {
127         var query = 'action=list_hosts';
128         loadXMLDoc(dhtml_url, query);
129 }
130
131 // Handle update to host list
132 function ListOfHost(response) {
133         var select = document.getElementById('host_list');
134         var idx = refillSelect(response ? response.getElementsByTagName('option') : null, select);
135         if (idx > 0) {
136                 ListRefreshPlugin();
137         } else
138                 ListOfPlugin(null);
139 }
140
141 // Request refresh of plugin list
142 function ListRefreshPlugin() {
143         var host_list = document.getElementById('host_list');
144         var host      = host_list.selectedIndex >= 0 ? host_list.options[host_list.selectedIndex].value : '/';
145         if (host != '/') {
146                 var query = 'action=list_plugins&host='+encodeURIComponent(host);
147                 loadXMLDoc(dhtml_url, query);
148         } else {
149                 ListOfPlugin(null);
150         }
151 }
152
153 // Handle update to plugin list
154 function ListOfPlugin(response) {
155         var select = document.getElementById('plugin_list');
156         var idx = refillSelect(response ? response.getElementsByTagName('option') : null, select);
157         if (idx > 0) {
158                 ListRefreshPluginInstance();
159         } else
160                 ListOfPluginInstance(null);
161 }
162
163 // Request refresh of plugin instance list
164 function ListRefreshPluginInstance() {
165         var host_list   = document.getElementById('host_list');
166         var host        = host_list.selectedIndex >= 0 ? host_list.options[host_list.selectedIndex].value : '/';
167         var plugin_list = document.getElementById('plugin_list');
168         var plugin      = plugin_list.selectedIndex >= 0 ? plugin_list.options[plugin_list.selectedIndex].value : '/';
169         if (host != '/' && plugin != '/') {
170                 var query = 'action=list_pinsts&host='+encodeURIComponent(host)+'&plugin='+encodeURIComponent(plugin);
171                 loadXMLDoc(dhtml_url, query);
172         } else {
173                 ListOfPluginInstance(null);
174         }
175 }
176
177 // Handle update of plugin instance list
178 function ListOfPluginInstance(response) {
179         var select = document.getElementById('pinst_list');
180         var idx = refillSelect(response ? response.getElementsByTagName('option') : null, select);
181         if (idx > 0) {
182                 ListRefreshType();
183         } else
184                 ListOfType(null);
185 }
186
187 // Request refresh of type list
188 function ListRefreshType() {
189         var host_list   = document.getElementById('host_list');
190         var host        = host_list.selectedIndex >= 0 ? host_list.options[host_list.selectedIndex].value : '/';
191         var plugin_list = document.getElementById('plugin_list');
192         var plugin      = plugin_list.selectedIndex >= 0 ? plugin_list.options[plugin_list.selectedIndex].value : '/';
193         var pinst_list  = document.getElementById('pinst_list');
194         var pinst       = pinst_list.selectedIndex >= 0 ? pinst_list.options[pinst_list.selectedIndex].value : '/';
195         if (host != '/' && plugin != '/' && pinst != '/') {
196                 var query = 'action=list_types&host='+encodeURIComponent(host)+'&plugin='+encodeURIComponent(plugin)+'&plugin_instance='+encodeURIComponent(pinst);
197                 loadXMLDoc(dhtml_url, query);
198         } else {
199                 ListOfType(null);
200         }
201 }
202
203 // Handle update of type list
204 function ListOfType(response) {
205         var select = document.getElementById('type_list');
206         var idx = refillSelect(response ? response.getElementsByTagName('option') : null, select);
207         if (idx > 0) {
208                 ListRefreshTypeInstance();
209         } else
210                 ListOfTypeInstance(null);
211 }
212
213 // Request refresh of type instance list
214 function ListRefreshTypeInstance() {
215         var host_list   = document.getElementById('host_list');
216         var host        = host_list.selectedIndex >= 0 ? host_list.options[host_list.selectedIndex].value : '/';
217         var plugin_list = document.getElementById('plugin_list');
218         var plugin      = plugin_list.selectedIndex >= 0 ? plugin_list.options[plugin_list.selectedIndex].value : '/';
219         var pinst_list  = document.getElementById('pinst_list');
220         var pinst       = pinst_list.selectedIndex >= 0 ? pinst_list.options[pinst_list.selectedIndex].value : '/';
221         var type_list   = document.getElementById('type_list');
222         var type        = type_list.selectedIndex >= 0 ? type_list.options[type_list.selectedIndex].value : '/';
223         if (host != '/' && plugin != '/' && pinst != '/' && type != '/') {
224                 var query = 'action=list_tinsts&host='+encodeURIComponent(host)+'&plugin='+encodeURIComponent(plugin)+'&plugin_instance='+encodeURIComponent(pinst)+'&type='+encodeURIComponent(type);
225                 loadXMLDoc(dhtml_url, query);
226         } else {
227                 ListOfTypeInstance(null);
228         }
229 }
230
231 // Handle update of type instance list
232 function ListOfTypeInstance(response) {
233         var select = document.getElementById('tinst_list');
234         var idx = refillSelect(response ? response.getElementsByTagName('option') : null, select);
235         if (idx > 0) {
236                 // Enable add button
237                 RefreshButtons();
238         } else {
239                 // Disable add button
240                 RefreshButtons();
241         }
242 }
243
244 function RefreshButtons() {
245         var host_list   = document.getElementById('host_list');
246         var host        = host_list.selectedIndex >= 0 ? host_list.options[host_list.selectedIndex].value : '/';
247         var plugin_list = document.getElementById('plugin_list');
248         var plugin      = plugin_list.selectedIndex >= 0 ? plugin_list.options[plugin_list.selectedIndex].value : '/';
249         var pinst_list  = document.getElementById('pinst_list');
250         var pinst       = pinst_list.selectedIndex >= 0 ? pinst_list.options[pinst_list.selectedIndex].value : '/';
251         var type_list   = document.getElementById('type_list');
252         var type        = type_list.selectedIndex >= 0 ? type_list.options[type_list.selectedIndex].value : '/';
253         var tinst_list  = document.getElementById('tinst_list');
254         var tinst       = tinst_list.selectedIndex >= 0 ? tinst_list.options[tinst_list.selectedIndex].value : '/';
255         if (host != '/' && plugin != '/' && pinst != '/' && type != '/' && tinst != '/') {
256                 document.getElementById('btnAdd').removeAttribute('disabled');
257         } else {
258                 document.getElementById('btnAdd').setAttribute('disabled', 'disabled');
259         }
260
261         var graphs = document.getElementById('graphs');
262         if (graphs.getElementsByTagName('div').length > 1) {
263                 document.getElementById('btnClear').removeAttribute('disabled');
264                 document.getElementById('btnRefresh').removeAttribute('disabled');
265         } else {
266                 document.getElementById('btnClear').setAttribute('disabled', 'disabled');
267                 document.getElementById('btnRefresh').setAttribute('disabled', 'disabled');
268         }
269 }
270
271 var nextGraphId = 1;
272 var graphList = new Array();
273
274 function GraphAppend() {
275         var host_list   = document.getElementById('host_list');
276         var host        = host_list.selectedIndex >= 0 ? host_list.options[host_list.selectedIndex].value : '/';
277         var plugin_list = document.getElementById('plugin_list');
278         var plugin      = plugin_list.selectedIndex >= 0 ? plugin_list.options[plugin_list.selectedIndex].value : '/';
279         var pinst_list  = document.getElementById('pinst_list');
280         var pinst       = pinst_list.selectedIndex >= 0 ? pinst_list.options[pinst_list.selectedIndex].value : '/';
281         var type_list   = document.getElementById('type_list');
282         var type        = type_list.selectedIndex >= 0 ? type_list.options[type_list.selectedIndex].value : '/';
283         var tinst_list  = document.getElementById('tinst_list');
284         var tinst       = tinst_list.selectedIndex >= 0 ? tinst_list.options[tinst_list.selectedIndex].value : '/';
285         var time_list   = document.getElementById('timespan');
286         var timespan    = time_list.selectedIndex >= 0 ? time_list.options[time_list.selectedIndex].value : '';
287         var tinyLegend  = document.getElementById('tinylegend').checked;
288         var logarithmic = document.getElementById('logarithmic').checked
289         GraphDoAppend(host, plugin, pinst, type, tinst, timespan, tinyLegend, logarithmic);
290 }
291
292 function GraphDoAppend(host, plugin, pinst, type, tinst, timespan, tinyLegend, logarithmic) {
293         var graphs      = document.getElementById('graphs');
294
295         if (host != '/' && plugin != '/' && pinst != '/' && type != '/') {
296                 var graph_id   = 'graph_'+nextGraphId++;
297                 var graph_src  = graph_url+'?host='+encodeURIComponent(host)+'&plugin='+encodeURIComponent(plugin)+'&plugin_instance='+encodeURIComponent(pinst)+'&type='+encodeURIComponent(type);
298                 var graph_alt  = '';
299                 var grap_title = '';
300                 if (tinst == '@') {
301                         graph_alt   = host+'/'+plugin+(pinst.length > 0 ? '-'+pinst : '')+'/'+type;
302                         graph_title = type+' of '+plugin+(pinst.length > 0 ? '-'+pinst : '')+' plugin for '+host;
303                 } else {
304                         graph_alt   = host+'/'+plugin+(pinst.length > 0 ? '-'+pinst : '')+'/'+type+(tinst.length > 0 ? '-'+tinst : '');
305                         graph_title = type+(tinst.length > 0 ? '-'+tinst : '')+' of '+plugin+(pinst.length > 0 ? '-'+pinst : '')+' plugin for '+host;
306                         graph_src  += '&type_instance='+encodeURIComponent(tinst);
307                 }
308                 if (logarithmic)
309                         graph_src += '&logarithmic=1';
310                 if (tinyLegend)
311                         graph_src += '&tinylegend=1';
312                 if (timespan)
313                         graph_src += '&timespan='+encodeURIComponent(timespan);
314                 var now    = new Date();
315                 graph_src += '&ts='+now.getTime();
316                 graphList.push(graph_id+' '+encodeURIComponent(graph_alt)+(logarithmic ? '&logarithmic=1' : '')+(tinyLegend ? '&tinylegend=1' : '')+'&timespan='+encodeURIComponent(timespan));
317
318                 // Graph container
319                 newGraph = document.createElement('div');
320                 newGraph.setAttribute('class', 'graph');
321                 newGraph.setAttribute('id', graph_id);
322                 // Graph cell + graph
323                 newImg = document.createElement('img');
324                 newImg.setAttribute('src', graph_src);
325                 newImg.setAttribute('alt', graph_alt);
326                 newImg.setAttribute('title', graph_title);
327                 newImg.setAttribute('onclick', 'GraphToggleTools("'+graph_id+'")');
328                 newGraph.appendChild(newImg);
329                 graphs.appendChild(newGraph);
330         }
331         document.getElementById('nograph').style.display = 'none';
332         RefreshButtons();
333 }
334
335 function GraphDropAll() {
336         var graphs = document.getElementById('graphs');
337         var childCnt = graphs.childNodes.length;
338         while (childCnt > 0)
339                 if (graphs.childNodes[--childCnt].id != 'nograph' && (graphs.childNodes[childCnt].nodeName == 'div' || graphs.childNodes[childCnt].nodeName == 'DIV'))
340                         graphs.removeChild(graphs.childNodes[childCnt]);
341                 else if (graphs.childNodes[childCnt].id == 'nograph')
342                         graphs.childNodes[childCnt].style.display = 'block';
343         graphList = new Array();
344         RefreshButtons();
345 }
346
347 function GraphToggleTools(graph) {
348         var graphId = document.getElementById('ge_graphid').value;
349         var ref_img = null;
350         if (graphId == graph || graph == '') {
351                 ref_img = null;
352         } else {
353                 var graphDiv = document.getElementById(graph);
354                 var imgs     = graphDiv ? graphDiv.getElementsByTagName('img') : null;
355                 var imgCnt   = imgs ? imgs.length : 0;
356                 while (imgCnt > 0)
357                         if (imgs[--imgCnt].parentNode.getAttribute('class') == 'graph')
358                                 ref_img = imgs[imgCnt];
359         }
360         if (ref_img) {
361                 var ts_sel  =  document.getElementById('ge_timespan');
362                 var src_url = ref_img.src;
363                 var ge      = document.getElementById('ge');
364                 // Fix field values
365                 var ts = src_url.match(/&timespan=[^&]*/);
366                 ts = ts ? ts[0].substr(10) : '';
367                 document.getElementById('ge_graphid').value = graph;
368                 document.getElementById('ge_tinylegend').checked = src_url.match(/&tinylegend=1/);
369                 document.getElementById('ge_logarithmic').checked = src_url.match(/&logarithmic=1/);
370                 for (i = 0; i < ts_sel.options.length; i++)
371                         if (ts_sel.options[i].value == ts) {
372                                 ts_sel.selectedIndex = i;
373                                 break;
374                         }
375                 // show tools box and position it properly
376                 ge.style.display = 'table';
377                 GraphPositionToolbox(ref_img);
378         } else {
379                 // hide tools box
380                 document.getElementById('ge').style.display = 'none';
381                 document.getElementById('ge_graphid').value = '';
382         }
383 }
384
385 function GraphPositionToolbox(ref_img) {
386         var ge      = document.getElementById('ge');
387         if (ge.style.display != 'none') {
388                 var wl = 0; var wt = 0;
389                 var x = ref_img;
390                 if (ref_img == null) {
391                         var graphDiv = document.getElementById(document.getElementById('ge_graphid').value);
392                         var imgs     = graphDiv ? graphDiv.getElementsByTagName('img') : null;
393                         var imgCnt   = imgs ? imgs.length : 0;
394                         while (imgCnt > 0)
395                                 if (imgs[--imgCnt].parentNode.getAttribute('class') == 'graph')
396                                         ref_img = imgs[imgCnt];
397
398                         if (ref_img == null) {
399                                 document.getElementById('ge_graphid').value = '';
400                                 ge.style.display = 'none';
401                                 return;
402                         } else
403                                 x = ref_img;
404                 }
405                 while (x != null) {
406                         wl += x.offsetLeft;
407                         wt += x.offsetTop;
408                         x = x.offsetParent;
409                 }
410                 ge.style.left    = (wl + (ref_img.offsetWidth - ge.offsetWidth) / 2)+'px';
411                 ge.style.top     = (wt + (ref_img.offsetHeight - ge.offsetHeight) / 2)+'px';
412         }
413 }
414
415 function GraphRefreshAll() {
416         var imgs   = document.getElementById('graphs').getElementsByTagName('img');
417         var imgCnt = imgs.length;
418         var now    = new Date();
419         var newTS  = '&ts='+now.getTime();
420         while (imgCnt > 0)
421                 if (imgs[--imgCnt].parentNode.getAttribute('class') == 'graph') {
422                         var oldSrc = imgs[imgCnt].src;
423                         var newSrc = oldSrc.replace(/&ts=[0-9]+/, newTS);
424                         if (newSrc == oldSrc)
425                                 newSrc = newSrc + newTS;
426                         imgs[imgCnt].setAttribute('src', newSrc);
427                 }
428 }
429
430 function GraphRefresh(graph) {
431         var graphElement = null;
432         if (graph == null) {
433                 var graphId = document.getElementById('ge_graphid').value;
434                 if (graphId != '')
435                         graphElement = document.getElementById(graphId);
436         } else 
437                 graphElement = document.getElementById(graph);
438         if (graphElement != null) {
439                 var imgs = graphElement.getElementsByTagName('img');
440                 var imgCnt = imgs.length;
441                 while (imgCnt > 0)
442                         if (imgs[--imgCnt].parentNode.getAttribute('class') == 'graph') {
443                                 var now    = new Date();
444                                 var newTS  = '&ts='+now.getTime();
445                                 var oldSrc = imgs[imgCnt].src;
446                                 var newSrc = oldSrc.replace(/&ts=[0-9]+/, newTS);
447                                 if (newSrc == oldSrc)
448                                         newSrc = newSrc+newTS;
449                                 imgs[imgCnt].setAttribute('src', newSrc);
450                                 break;
451                         }
452         }
453 }
454
455 function GraphAdjust(graph) {
456         var graphId = graph == null ? document.getElementById('ge_graphid').value : graph;
457         var graphElement = document.getElementById(graphId);
458         if (graphElement != null) {
459                 var time_list   = document.getElementById('ge_timespan');
460                 var timespan    = time_list.selectedIndex >= 0 ? time_list.options[time_list.selectedIndex].value : '';
461                 var tinyLegend  = document.getElementById('ge_tinylegend').checked;
462                 var logarithmic = document.getElementById('ge_logarithmic').checked
463                 var imgs = graphElement.getElementsByTagName('img');
464                 var imgCnt = imgs.length;
465                 var ref_img     = null;
466                 while (imgCnt > 0)
467                         if (imgs[--imgCnt].parentNode.getAttribute('class') == 'graph') {
468                                 var now    = new Date();
469                                 var newTS  = '&ts='+now.getTime();
470                                 var oldSrc = imgs[imgCnt].src;
471                                 var newSrc = oldSrc.replace(/&ts=[^&]*/, newTS);
472                                 if (newSrc == oldSrc)
473                                         newSrc = newSrc+newTS;
474                                 newSrc     = newSrc.replace(/&logarithmic=[^&]*/, '');
475                                 if (logarithmic)
476                                         newSrc += '&logarithmic=1';
477                                 newSrc     = newSrc.replace(/&tinylegend=[^&]*/, '');
478                                 if (tinyLegend)
479                                         newSrc += '&tinylegend=1';
480                                 newSrc     = newSrc.replace(/&timespan=[^&]*/, '');
481                                 if (timespan)
482                                         newSrc += '&timespan='+encodeURIComponent(timespan);
483                                 imgs[imgCnt].setAttribute('src', newSrc);
484
485                                 var myList = Array();
486                                 for (i = 0; i < graphList.length; i++)
487                                         if (graphList[i].substring(0, graphId.length) == graphId && graphList[i].charAt(graphId.length) == ' ') {
488                                                 newSrc = graphList[i];
489                                                 newSrc = newSrc.replace(/&logarithmic=[^&]*/, '');
490                                                 newSrc = newSrc.replace(/&tinylegend=[^&]*/, '');
491                                                 newSrc = newSrc.replace(/&timespan=[^&]*/, '');
492                                                 newSrc = newSrc+(logarithmic ? '&logarithmic=1' : '')+(tinyLegend ? '&tinylegend=1' : '')+'&timespan='+encodeURIComponent(timespan);
493                                                 myList.push(newSrc);
494                                                 continue;
495                                         } else
496                                                 myList.push(graphList[i]);
497                                 graphList = myList;
498                                 window.setTimeout("GraphPositionToolbox(null)", 10);
499                                 // GraphPositionToolbox(imgs[imgCnt]);
500                                 break;
501                         }
502         }
503 }
504
505 function GraphRemove(graph) {
506         var graphs = document.getElementById('graphs');
507         var graphId = graph == null ? document.getElementById('ge_graphid').value : graph;
508         var graphElement = document.getElementById(graphId);
509         if (graphElement) {
510                 GraphToggleTools('');
511                 graphs.removeChild(graphElement);
512                 RefreshButtons();
513                 if (graphs.getElementsByTagName('div').length == 1)
514                         document.getElementById('nograph').style.display = 'block';
515
516                 var myList = Array();
517                 for (i = 0; i < graphList.length; i++)
518                         if (graphList[i].substring(0, graphId.length) == graphId && graphList[i].charAt(graphId.length) == ' ')
519                                 continue;
520                         else
521                                 myList.push(graphList[i]);
522                 graphList = myList;
523         }
524 }
525
526 function GraphMoveUp(graph) {
527         var graphs    = document.getElementById('graphs');
528         var graphId   = graph == null ? document.getElementById('ge_graphid').value : graph;
529         var childCnt  = graphs.childNodes.length;
530         var prevGraph = null;
531         for (i = 0; i < childCnt; i++)
532                 if (graphs.childNodes[i].nodeName == 'div' || graphs.childNodes[i].nodeName == 'DIV') {
533                         if (graphs.childNodes[i].id == 'nograph') {
534                                 // Skip
535                         } else if (graphs.childNodes[i].id == graphId) {
536                                 var myGraph = graphs.childNodes[i];
537                                 if (prevGraph) {
538                                         graphs.removeChild(myGraph);
539                                         graphs.insertBefore(myGraph, prevGraph);
540                                 }
541                                 break;
542                         } else
543                                 prevGraph = graphs.childNodes[i];
544                 }
545         for (i = 0; i < graphList.length; i++)
546                 if (graphList[i].substring(0, graphId.length) == graphId && graphList[i].charAt(graphId.length) == ' ') {
547                         if (i > 0) {
548                                 var tmp = graphList[i-1];
549                                 graphList[i-1] = graphList[i];
550                                 graphList[i]   = tmp;
551                         }
552                         break;
553                 }
554         GraphPositionToolbox(null);
555 }
556
557 function GraphMoveDown(graph) {
558         var graphs    = document.getElementById('graphs');
559         var graphId   = graph == null ? document.getElementById('ge_graphid').value : graph;
560         var childCnt  = graphs.childNodes.length;
561         var nextGraph = null;
562         var myGraph   = null;
563         for (i = 0; i < childCnt; i++)
564                 if (graphs.childNodes[i].nodeName == 'div' || graphs.childNodes[i].nodeName == 'DIV') {
565                         if (graphs.childNodes[i].id == 'nograph') {
566                                 // Skip
567                         } else if (graphs.childNodes[i].id == graphId) {
568                                 myGraph = graphs.childNodes[i];
569                         } else if (myGraph) {
570                                 nextGraph = graphs.childNodes[i];
571                                 graphs.removeChild(nextGraph);
572                                 graphs.insertBefore(nextGraph, myGraph);
573                                 break;
574                         }
575                 }
576         for (i = 0; i < graphList.length; i++)
577                 if (graphList[i].substring(0, graphId.length) == graphId && graphList[i].charAt(graphId.length) == ' ') {
578                         if (i+1 < graphList.length) {
579                                 var tmp = graphList[i+1];
580                                 graphList[i+1] = graphList[i];
581                                 graphList[i]   = tmp;
582                         }
583                         break;
584                 }
585         GraphPositionToolbox(null);
586 }
587
588 function GraphListFromCookie(lname) {
589         if (document.cookie.length > 0) {
590                 var cname= 'graphLst'+lname+'=';
591                 var cookies = document.cookie.split('; ');
592                 for (i = 0; i < cookies.length; i++)
593                         if (cookies[i].substring(0, cname.length) == cname)
594                                 return cookies[i].substring(cname.length).split('/');
595         }
596         return new Array();
597 }
598
599 function GraphListNameSort(a, b) {
600         if (a[0] > b[0])
601                 return 1
602         else if (a[0] < b[0])
603                 return -1;
604         else
605                 return 0;
606 }
607
608 function GraphListRefresh() {
609         var select   = document.getElementById('GraphList');
610         var childCnt = select.childNodes.length;
611         var oldValue = select.selectedIndex > 0 ? select.options[select.selectedIndex].value : '/';
612         while (childCnt > 0)
613                 select.removeChild(select.childNodes[--childCnt]);
614
615         // Determine available names
616         var options = new Array();
617         if (document.cookie.length > 0) {
618                 var cookies = document.cookie.split('; ');
619                 for (i = 0; i < cookies.length; i++)
620                         if (cookies[i].substring(0, 8) == 'graphLst') {
621                                 var p = cookies[i].indexOf('=');
622                                 if (p < 0)
623                                         continue;
624                                 options.push(new Array(cookies[i].substring(8, p), cookies[i].substring(p+1).split('/').length));
625                         }
626         }
627         options.sort(GraphListNameSort);
628
629         var optCnt  = options ? options.length : 0;
630         if (optCnt == 0) {
631                 select.setAttribute('disabled', 'disabled');
632                 return -1;
633         } else {
634                 select.removeAttribute('disabled');
635                 for (i = 0; i < optCnt; i++) {
636                         newOption = document.createElement("option");
637                         newOption.value = options[i][0];
638                         if (newOption.value == oldValue)
639                                 newOption.setAttribute('selected', 'selected');
640                         if (options[i][1] == 1)
641                                 newOption.appendChild(document.createTextNode(newOption.value+' (1 graph)'));
642                         else
643                                 newOption.appendChild(document.createTextNode(newOption.value+' ('+options[i][1]+' graphs)'));
644                         select.appendChild(newOption);
645                 }
646                 return select.selectedIndex;
647         }
648 }
649
650 function GraphListCheckName(doalert) {
651         var lname = document.getElementById('GraphListName');
652         if (lname) {
653                 if (lname.value.match(/^[a-zA-Z0-9_-]+$/)) {
654                         lname.style.backgroundColor = '';
655                         return lname.value;
656                 } else {
657                         lname.style.backgroundColor = '#ffdddd';
658                         if (doalert && lname.value.length == 0)
659                                 alert('Graph list name is empty.\n\n'+
660                                       'Please fill in a name and try again.');
661                         else if (doalert)
662                                 alert('Graph list name contains non-permitted character.\n\n'+
663                                       'Only anlphanumerical characters (a-z, A-Z, 0-9), hyphen (-) and underscore (_) are permitted.\n'+
664                                       'Please correct and try again.');
665                         lname.focus();
666                 }
667         }
668         return '';
669 }
670
671 function GraphSave() {
672         var lstName = GraphListCheckName(true);
673         if (lstName.length == 0)
674                 return;
675         if (graphList.length > 0) {
676                 // Save graph list to cookie
677                 var str = '';
678                 for (i = 0; i < graphList.length; i++) {
679                         var g = graphList[i].indexOf(' ');
680                         if (i > 0)
681                                 str += '/';
682                         str += graphList[i].substring(g+1);
683                 }
684
685                 document.cookie = 'graphLst'+lstName+'='+str;
686                 if (GraphListFromCookie(lstName).length == 0)
687                         alert("Failed to save graph list '"+lstName+"' to cookie.");
688                 else
689                         alert("Successfully saved current graph list.");
690         } else {
691                 document.cookie = 'graphLst'+lstName+'=; expires='+new Date().toGMTString();
692                 alert("Cleared saved graph list.");
693         }
694         GraphListRefresh();
695 }
696
697 function GraphDrop() {
698         var cname = document.getElementById('GraphList');
699         if (cname && cname.selectedIndex >= 0) {
700                 cname = cname.options[cname.selectedIndex].value;
701                 document.cookie = 'graphLst'+cname+'=; expires='+new Date().toGMTString();
702                 GraphListRefresh();
703         } else
704                 return;
705 }
706
707 function GraphLoad() {
708         var cname = document.getElementById('GraphList');
709         if (cname && cname.selectedIndex >= 0)
710                 cname = cname.options[cname.selectedIndex].value;
711         else
712                 return;
713         // Load graph list from cookie
714         var grLst = GraphListFromCookie(cname);
715         var oldLength = graphList.length;
716         for (i = 0; i < grLst.length; i++) {
717                 var host        = '';
718                 var plugin      = '';
719                 var pinst       = '';
720                 var type        = '';
721                 var tinst       = '';
722                 var timespan    = '';
723                 var logarithmic = false;
724                 var tinyLegend  = false;
725                 var graph = grLst[i].split('&');
726                 for (j = 0; j < graph.length; j++)
727                         if (graph[j] == 'logarithmic=1')
728                                 logarithmic = true;
729                         else if (graph[j] == 'tinylegend=1')
730                                 tinyLegend = true;
731                         else if (graph[j].substring(0, 9) == 'timespan=')
732                                 timespan = decodeURIComponent(graph[j].substring(9));
733                 graph = decodeURIComponent(graph[0]).split('/');
734                 host = graph[0];
735                 if (graph.length > 1) {
736                         var g = graph[1].indexOf('-');
737                         if (g >= 0) {
738                                 plugin = graph[1].substring(0, g);
739                                 pinst  = graph[1].substring(g+1);
740                         } else
741                                 plugin = graph[1];
742                 }
743                 if (graph.length > 2) {
744                         var g = graph[2].indexOf('-');
745                         if (g >= 0) {
746                                 type  = graph[2].substring(0, g);
747                                 tinst = graph[2].substring(g+1);
748                         } else
749                                 type  = graph[2];
750                 }
751
752                 if (host && plugin && type)
753                         GraphDoAppend(host, plugin, pinst, type, tinst, timespan, tinyLegend, logarithmic);
754         }
755         if (grLst.length == 0)
756                 alert("No list '"+cname+"' found for loading.");
757         else if (grLst.length + oldLength != graphList.length)
758                 alert("Could not load all graphs, probably damaged cookie.");
759 }
760