summaryrefslogtreecommitdiff
path: root/www/ajax/rrdui.js
diff options
context:
space:
mode:
authorStef Walter <stef@memberwebs.com>2006-08-30 21:25:18 +0000
committerStef Walter <stef@memberwebs.com>2006-08-30 21:25:18 +0000
commit897ad4e8d2b36ae98f8f2f61846b1ab5145922f4 (patch)
treedfbaaf84460df45229c1bd0569b3e9628a58c699 /www/ajax/rrdui.js
parent3b0babad49a78bed803108faf132ac229a9d8ff2 (diff)
Move html directory to www
Diffstat (limited to 'www/ajax/rrdui.js')
-rw-r--r--www/ajax/rrdui.js685
1 files changed, 685 insertions, 0 deletions
diff --git a/www/ajax/rrdui.js b/www/ajax/rrdui.js
new file mode 100644
index 0000000..5ed315a
--- /dev/null
+++ b/www/ajax/rrdui.js
@@ -0,0 +1,685 @@
+
+
+/* -----------------------------------------------------------------------------
+ * STARTUP
+ */
+
+// Fix up the ENDPOINT config setting
+if(ENDPOINT.charAt(0) != '/')
+ ENDPOINT = '/' + ENDPOINT;
+
+/* TODO: Check setup variables */
+/* TODO: Loading indicator */
+
+var categoryCurrent = null;
+var categoryArea = document.getElementById("headers");
+var xmlData = null;
+var gdoc = null;
+var gwindow = null;
+
+function init()
+{
+ /* Set by frame.html */
+ if(!window._frameLoaded)
+ {
+ window.setTimeout("init();", 100);
+ return;
+ }
+
+ setupFrame();
+ loadXml(makeEndpointURI(), loadedGraphData);
+}
+
+init();
+
+function loadedGraphData(doc)
+{
+ if(!doc)
+ {
+ displayError("Couldn't load graph data from server", categoryArea);
+ return;
+ }
+
+ xmlData = doc;
+
+ displayCategories();
+ displayCurrentPage();
+}
+
+
+/* -----------------------------------------------------------------------------
+ * GRAPHS
+ */
+
+function getFrameDocument()
+{
+ var frame = document.getElementById("main-frame");
+ /* COMPAT: IE and Mozilla access the content of frames differently */
+ return frame.contentDocument ? frame.contentDocument : frame.contentWindow.document;
+}
+
+function setupFrame()
+{
+ gdoc = getFrameDocument();
+ var frame = document.getElementById("main-frame");
+ /* COMPAT: IE and Mozilla access the content of frames differently */
+ gwindow = frame.contentDocument ? frame.contentDocument.defaultView : frame.contentWindow;
+}
+
+function displayCurrentPage()
+{
+ var area = gdoc.getElementById("graphs");
+ var img;
+
+ /* Hide all images not from this category */
+ var i, images = area.getElementsByTagName("img");
+ for(i = 0; i < images.length; i++)
+ {
+ img = images.item(i);
+ var vis = (img._category == categoryCurrent);
+
+ img.style.display = vis ? "inline" : "none";
+
+ if(vis != img._visible)
+ {
+ /* The graph needs reloading if now turning visible */
+ img._visible = vis;
+ if(vis)
+ reloadGraph(img, false);
+ else
+ img._loading = false;
+ }
+ }
+
+ var graphs = findCategoryGraphs(categoryCurrent);
+ if(!graphs)
+ {
+ displayError("Graph data isn't loaded from server", area);
+ return;
+ }
+
+ var tend = nowTime();
+ var tbeg = tend - 86400; /* One day by default */
+
+ for(i = 0; i < graphs.length; i++)
+ {
+ var child = graphs[i];
+
+ var name = child.getAttribute("name");
+ if(!name || !name.length)
+ continue;
+
+ /* Make sure we don't already have this graph */
+ var id = "graph-" + categoryCurrent + "-" + name;
+ img = gdoc.getElementById(id);
+ if(img != null)
+ continue;
+
+ var interval = child.getAttribute("interval");
+ if(!interval || isNaN(interval) || interval <= 0)
+ interval = 0;
+ interval = Number(interval);
+
+ img = gdoc.createElement("img");
+
+ var title = child.getAttribute("title");
+ if(title && title.length)
+ {
+ img.setAttribute("title", child.getAttribute("title"));
+ img.setAttribute("alt", child.getAttribute("title"));
+ }
+ else
+ img.setAttribute("alt", "Graph");
+
+ img.className = "graph";
+ img.id = id;
+ img.style.cursor = "crosshair";
+
+ area.appendChild(img);
+
+ img._category = categoryCurrent;
+ img._name = name;
+ img._tbeg = tbeg;
+ img._tend = tend;
+ img._tinterval = interval;
+ img._visible = true;
+ img._loading = false;
+
+ reloadGraph(img, false);
+
+ img.onmousedown = function(evt)
+ { actionGotoCancel(); return zoomGraphStart(evt || gwindow.event); }
+ img.onmouseover = function(evt)
+ { return actionsDisplay(gwindow.event ? gwindow.event : evt); }
+
+ /* Setup for an auto scroll */
+ if(interval)
+ {
+ /* Bump up auto scroll interval to 5 seconds */
+ var interval = img._tinterval < 5 ? 5 : img._tinterval;
+ window.setInterval(autoScroll, interval * 1000, img);
+ }
+ }
+}
+
+function reloadGraph(img, force)
+{
+ img._last = nowTime();
+
+ if(!img._visible)
+ return;
+
+ /* TODO: We should encode colons properly */
+
+ var uri = makeEndpointURI() + "/graph?category=" + img._category + "&name=" + img._name;
+
+ /* Add date options */
+ uri += "&start=" + img._tbeg + "&end=" + img._tend;
+
+ /* And colors */
+ uri += "&color=BACK" + GRAPH_COLOR_BACK + "&color=FONT" + GRAPH_COLOR_FONT +
+ "&color=SHADEA" + GRAPH_COLOR_BACK + "&color=SHADEB" + GRAPH_COLOR_BACK;
+
+ /* Size */
+ uri += "&height=" + GRAPH_HEIGHT + "&width=" + GRAPH_WIDTH;
+
+ if(force || img.getAttribute("src") != uri)
+ {
+ img._loading = true;
+ updateCursors(img);
+
+ /* HACK: The onload event for IMG is called with a strange event target */
+ img.onload = new Function("reloadedGraph('" + img.id + "');");
+ img.setAttribute("src", uri);
+ }
+}
+
+function reloadedGraph(id)
+{
+ var img = getFrameDocument().getElementById(id);
+ img._loading = false;
+ updateCursors(img);
+}
+
+function updateCursors(img)
+{
+ if(img._actions)
+ {
+ var i, acts = img._actions.getElementsByTagName("img");
+ for(i = 0; i < acts.length; i++)
+ acts.item(i).style.cursor = img._loading ? "wait" : "pointer";
+ }
+
+ img.style.cursor = img._loading ? "wait" : "crosshair";
+}
+
+function autoScroll(img)
+{
+ /* Don't auto scroll if we have loading issues */
+ if(img._loading)
+ return;
+
+ /* If we're displaying 'now' somewhere in the graph... */
+ var now = nowTime();
+ if((img._tend + (img._tinterval * 2)) >= now)
+ {
+ var diff = now - img._last;
+
+ if(diff > 0)
+ {
+ /* ... then scroll by X seconds to scroll the graph */
+ img._tbeg += diff;
+ img._tend += diff;
+ reloadGraph(img, false);
+ }
+ }
+}
+
+
+
+/* -----------------------------------------------------------------------------
+ * ZOOM
+ */
+
+function zoomGraphStart(evt)
+{
+ /* COMPAT: In IE the left button is 1, Mozilla is 0 */
+ var lbutton = gwindow.event ? 1 : 0;
+
+ if(evt.button != lbutton)
+ return;
+
+ /* COMPAT: Target is handled differently in Internet nastiness */
+ var img = evt.target || evt.srcElement;
+
+ if(img.className != "graph")
+ return;
+
+ var zoom = gdoc.getElementById("zoom");
+
+ zoom._img = img;
+ zoom._zooming = true;
+ zoom._from = evt.clientX + gdoc.body.scrollLeft;
+
+ zoom._start = zoom._from - img.offsetLeft;
+ zoom._end = zoom._start;
+
+ zoom.style.width = 1;
+ zoom.style.left = zoom._from;
+ zoom.style.height = img.height;
+ zoom.style.top = img.offsetTop;
+ zoom.style.display = "block";
+
+ zoom.onmousemove = zoom._img.onmousemove = function(evt)
+ { return zoomGraphUpdate(gwindow.event ? gwindow.event : evt); }
+ zoom.onmouseup = zoom._img.onmouseup = function(evt)
+ { return zoomGraphDone(gwindow.event ? gwindow.event : evt); }
+ document.onmouseup = gdoc.onmouseup = function(evt)
+ { return zoomGraphDone(gwindow.event ? gwindow.event : evt); }
+
+ cancelEvent(evt);
+ return false;
+}
+
+function zoomGraphUpdate(evt)
+{
+ var zoom = gdoc.getElementById("zoom");
+ if(!zoom._zooming || !zoom._img)
+ return;
+
+ var to = evt.clientX + gdoc.body.scrollLeft;
+ var width = to - zoom._from;
+ zoom._end = to - zoom._img.offsetLeft;
+
+ if(width == 0)
+ width = 1;
+
+ if(width < 0)
+ {
+ zoom.style.left = to;
+ zoom.style.width = -width;
+ }
+ else
+ {
+ zoom.style.left = zoom._from;
+ zoom.style.width = width;
+ }
+
+ zoom.style.height = zoom._img.height;
+ zoom.style.top = zoom._img.offsetTop;
+ zoom.style.display = "block";
+
+ cancelEvent(evt);
+ return false;
+}
+
+function zoomGraphDone(evt)
+{
+ var zoom = gdoc.getElementById("zoom");
+ if(!zoom._zooming)
+ return;
+
+ /* COMPAT: Target is handled differently in Internet nastiness */
+ var targ = evt.target || evt.srcElement;
+ if(targ == zoom || targ == zoom._img)
+ zoomGraphSelect(zoom);
+
+ zoomGraphCancel(evt);
+}
+
+function zoomGraphSelect(zoom)
+{
+ var left = GRAPH_MARGIN_LEFT;
+ var right = zoom._img.width - GRAPH_MARGIN_RIGHT;
+
+ var start = zoom._start > zoom._end ? zoom._end : zoom._start;
+ var end = zoom._start > zoom._end ? zoom._start : zoom._end;
+
+ /* If too small selected, then punt */
+ if((end - start) <= 1)
+ return;
+
+ /* If entire thing selected, then punt */
+ if(start < left && end > right)
+ return;
+
+ /* If only margins selected, then punt */
+ if(!(start < right && end > left))
+ return;
+
+ /* Limit it to the right range */
+ var range = zoom._img.width - GRAPH_MARGIN_RIGHT - GRAPH_MARGIN_LEFT;
+ start = start < left ? 0 : start - left;
+ end = end - left > range ? range : end - left;
+
+ /* Compute the ratio pixels:time */
+ var ratio = (zoom._img._tend - zoom._img._tbeg) / range;
+
+ var obeg = zoom._img._tbeg;
+ var oend = zoom._img._tend;
+
+ zoom._img._tend = Math.floor((end * ratio) + zoom._img._tbeg);
+ zoom._img._tbeg = Math.floor((start * ratio) + zoom._img._tbeg);
+
+ reloadGraph(zoom._img, true);
+}
+
+function zoomGraphCancel(evt)
+{
+ var zoom = gdoc.getElementById("zoom");
+
+ if(zoom._zooming) {
+ if(zoom._img)
+ zoom._img.onmousemove = zoom._img.onmouseup = zoom._img.onmouseleave = null;
+ zoom._img = null;
+ zoom.onmousemove = zoom.onmouseup = null;
+ zoom._zooming = false;
+ }
+
+ zoom.style.display = "none";
+}
+
+function zoomGraphOut(img)
+{
+ var factor = Math.floor((img._tend - img._tbeg) / 2.5);
+ var now = nowTime();
+
+ /* If near the end we have special behavior */
+ if(img._tend >= now - 300 || img._tend <= now)
+ {
+ img._tbeg -= factor;
+ img._tend = now;
+ }
+ else
+ {
+ factor = Math.floor(factor / 2);
+ img._tbeg -= factor
+ img._tend += factor;
+ }
+
+ reloadGraph(img, true);
+}
+
+function zoomGraphMove(img, dir)
+{
+ var factor = Math.floor((img._tend - img._tbeg) / 3);
+ if(!dir)
+ factor = -factor;
+ img._tbeg += factor;
+ img._tend += factor;
+ reloadGraph(img, true);
+}
+
+function actionsDisplay(evt)
+{
+ /* COMPAT: Target is handled differently in Internet nastiness */
+ var img = evt.target || evt.srcElement;
+
+ if(img.className != "graph")
+ return;
+
+ var actions = gdoc.getElementById("actions");
+
+ /* Disconnect from previous image */
+ if(actions._img)
+ actions._img._actions = null;
+
+ /* Attach to this image */
+ actions._img = img;
+ img._actions = actions;
+ updateCursors(img);
+
+ actions._display = true;
+
+ actions.style.top = img.offsetTop;
+ actions.style.left = img.offsetLeft + (img.width - 16);
+ actions.style.display = "block";
+
+ actions.onmouseout = img.onmouseout = function(evt)
+ { return actionsHide(gwindow.event ? gwindow.event : evt); }
+
+ /* TODO: We only actually have to do this once */
+ var i, acts = actions.getElementsByTagName("img");
+ for(i = 0; i < acts.length; i++)
+ {
+ acts.item(i).onclick = function(evt)
+ { return actionRun(gwindow.event ? gwindow.event : evt); }
+ }
+}
+
+function actionsHide(evt)
+{
+ var actions = gdoc.getElementById("actions");
+ if(!actions._display)
+ return;
+
+ var x = evt.clientX + gdoc.body.scrollLeft;
+ var y = evt.clientY + gdoc.body.scrollTop;
+ var img = actions._img;
+
+ /* See if we're actually still within the image */
+ if(x >= img.offsetLeft && x <= img.offsetLeft + img.width &&
+ y >= img.offsetTop && y <= img.offsetTop + img.height)
+ return;
+
+ actions.style.display = "none";
+ img.onmouseout = actions.onmouseout = null;
+ img._actions = null;
+ actions._img = null;
+ actions._display = false;
+}
+
+function actionRun(evt)
+{
+ /* COMPAT: Target is handled differently in Internet nastiness */
+ var act = evt.target || evt.srcElement;
+
+ if(act.className != "action")
+ return;
+
+ var actions = gdoc.getElementById("actions");
+ if(!actions._display)
+ return;
+
+ switch(act.getAttribute("id"))
+ {
+ case "action-zoom":
+ zoomGraphOut(actions._img);
+ break;
+ case "action-next":
+ zoomGraphMove(actions._img, true);
+ break;
+ case "action-prev":
+ zoomGraphMove(actions._img, false);
+ break;
+ case "action-goto":
+ actionGoto(actions._img, act);
+ break;
+ }
+}
+
+function actionGoto(img, act)
+{
+ /* Display the selection thingy */
+ var got = gdoc.getElementById("goto");
+
+ /* TODO: Make numbers constants */
+ got.style.top = img.offsetTop + 40;
+ got.style.left = (img.offsetLeft + img.width) - 160;
+ got.style.width = 120;
+ got.style.display = "block";
+
+ var sel = gdoc.getElementById("goto-select");
+ sel.onchange = actionGotoChange;
+ sel._img = img;
+ sel.value = null;
+ sel.selectedIndex = -1;
+}
+
+function actionGotoCancel()
+{
+ var got = gdoc.getElementById("goto");
+ got.style.display = "none";
+}
+
+function actionGotoChange()
+{
+ var sel = gdoc.getElementById("goto-select");
+ var img = sel._img;
+ sel.onchange = null;
+ sel._img = null;
+
+ times = sel.value.split(":");
+ img._tbeg = nowTime() - times[0];
+ img._tend = nowTime() - times[1];
+ reloadGraph(img, true);
+
+ actionGotoCancel();
+}
+
+/* -----------------------------------------------------------------------------
+ * GROUP CATEGORIES
+ */
+
+function findCategoryGraphs(cat)
+{
+ var i;
+
+ if(!xmlData)
+ return null;
+
+ var ret = new Array();
+ var graphs = xmlData.getElementsByTagName("graph");
+ for(i = 0; i < graphs.length; i++)
+ {
+ if(graphs.item(i).getAttribute("category") == cat)
+ ret.push(graphs.item(i));
+ }
+ return ret;
+}
+
+function changeCategory(evt)
+{
+ var header = evt.target || evt.srcElement;
+ categoryCurrent = header.getAttribute("category");
+ displayCurrentPage();
+}
+
+function displayCategories()
+{
+ var cats = new Object();
+ var cat, i, name;
+
+ /* Get the template and clean it up a bit */
+ var template = document.getElementById("header-template");
+
+ var graphs = xmlData.getElementsByTagName("graph");
+ for(i = 0; i < graphs.length; i++)
+ {
+ cat = graphs.item(i).getAttribute("category");
+ if(!cat)
+ {
+ cat = "Other";
+ graphs.item(i).setAttribute("category", "Other");
+ }
+ cats[cat] = true;
+ }
+
+ for(name in cats)
+ {
+ if(!categoryCurrent)
+ categoryCurrent = name;
+
+ var el = template.cloneNode(false);
+ el.removeAttribute("id");
+ el.appendChild(document.createTextNode(name));
+ el.setAttribute("category", name);
+
+ el.onclick = function(evt)
+ { return changeCategory(evt || window.event); }
+
+ categoryArea.insertBefore(el, template);
+ }
+}
+
+/* -----------------------------------------------------------------------------
+ * HELPERS
+ */
+
+function loadXml(uri, callback)
+{
+ var xmlhttp;
+ var xmlHttpChange = function() {
+ if(xmlhttp.readyState == 4)
+ {
+ if(xmlhttp.status == 200)
+ callback(xmlhttp.responseXML);
+ else
+ callback(null);
+ }
+ }
+
+ /* Mozilla code */
+ if(window.XMLHttpRequest)
+ {
+ xmlhttp = new XMLHttpRequest();
+ xmlhttp.onreadystatechange = xmlHttpChange;
+ xmlhttp.open("GET", uri, true);
+ xmlhttp.send(null);
+ }
+
+ /* And now for the Nasty */
+ else if (window.ActiveXObject)
+ {
+ xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
+ /* TODO: need to do something when this fails (can it fail?) */
+ if(xmlhttp)
+ {
+ xmlhttp.onreadystatechange = xmlHttpChange;
+ xmlhttp.open("GET", uri, true);
+ xmlhttp.send()
+ }
+ }
+}
+
+function displayError(message, place)
+{
+ var el = document.createElement("span");
+ el.className = "error";
+ var text = document.createTextNode(message);
+ el.appendChild(text);
+
+ if(place)
+ place.appendChild(el);
+
+ return el;
+}
+
+function nowTime()
+{
+ return Math.floor((new Date()).getTime() / 1000);
+}
+
+function debug(msg)
+{
+ setTimeout(function() { throw new Error("[debug] " + msg); }, 0);
+}
+
+function makeEndpointURI()
+{
+ /* TODO: This should use the scheme of the current URI */
+ return "http://" + document.location.host + ENDPOINT;
+}
+
+function cancelEvent(evt)
+{
+ /* COMPAT: Totally different for IE and mozilla */
+ if(evt.preventDefault)
+ evt.preventDefault();
+ if(evt.stopPropagation)
+ evt.stopPropagation();
+ if("cancelBubble" in evt)
+ evt.cancelBubble = true;
+ if("returnValue" in evt)
+ evt.returnValue = false;
+}