diff options
author | Stef Walter <stef@memberwebs.com> | 2006-08-30 21:18:01 +0000 |
---|---|---|
committer | Stef Walter <stef@memberwebs.com> | 2006-08-30 21:18:01 +0000 |
commit | 96e3074f9de2b0c7ba8b0b11edfa42ffd2803037 (patch) | |
tree | c7b5addc5b6f0b14fe028b54fae6384e1cd1f2a6 /html/ajax/rrdui.js | |
parent | e228b6ac441b5a9ceff398d874ba7b4a118c819b (diff) |
Reorganize things, and build and install them properly
Diffstat (limited to 'html/ajax/rrdui.js')
-rw-r--r-- | html/ajax/rrdui.js | 685 |
1 files changed, 685 insertions, 0 deletions
diff --git a/html/ajax/rrdui.js b/html/ajax/rrdui.js new file mode 100644 index 0000000..5ed315a --- /dev/null +++ b/html/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; +} |