From 897ad4e8d2b36ae98f8f2f61846b1ab5145922f4 Mon Sep 17 00:00:00 2001 From: Stef Walter Date: Wed, 30 Aug 2006 21:25:18 +0000 Subject: Move html directory to www --- www/Makefile.am | 13 + www/ajax/action-date.gif | Bin 0 -> 1024 bytes www/ajax/action-next.gif | Bin 0 -> 877 bytes www/ajax/action-prev.gif | Bin 0 -> 874 bytes www/ajax/action-zoom.gif | Bin 0 -> 965 bytes www/ajax/frame.html | 43 +++ www/ajax/index.html | 50 ++++ www/ajax/rrdui.js | 685 +++++++++++++++++++++++++++++++++++++++++++++++ www/ajax/style.css | 164 ++++++++++++ 9 files changed, 955 insertions(+) create mode 100644 www/Makefile.am create mode 100644 www/ajax/action-date.gif create mode 100644 www/ajax/action-next.gif create mode 100644 www/ajax/action-prev.gif create mode 100644 www/ajax/action-zoom.gif create mode 100644 www/ajax/frame.html create mode 100644 www/ajax/index.html create mode 100644 www/ajax/rrdui.js create mode 100644 www/ajax/style.css (limited to 'www') diff --git a/www/Makefile.am b/www/Makefile.am new file mode 100644 index 0000000..5bb713c --- /dev/null +++ b/www/Makefile.am @@ -0,0 +1,13 @@ + +EXTRA_DIST = ajax static + +htmldir = $(datadir)/rrdui/ +nobase_html_DATA = \ + ajax/action-date.gif \ + ajax/action-next.gif \ + ajax/action-prev.gif \ + ajax/action-zoom.gif \ + ajax/frame.html \ + ajax/index.html \ + ajax/rrdui.js \ + ajax/style.css diff --git a/www/ajax/action-date.gif b/www/ajax/action-date.gif new file mode 100644 index 0000000..a249e30 Binary files /dev/null and b/www/ajax/action-date.gif differ diff --git a/www/ajax/action-next.gif b/www/ajax/action-next.gif new file mode 100644 index 0000000..c01940a Binary files /dev/null and b/www/ajax/action-next.gif differ diff --git a/www/ajax/action-prev.gif b/www/ajax/action-prev.gif new file mode 100644 index 0000000..f7eba32 Binary files /dev/null and b/www/ajax/action-prev.gif differ diff --git a/www/ajax/action-zoom.gif b/www/ajax/action-zoom.gif new file mode 100644 index 0000000..c1e9800 Binary files /dev/null and b/www/ajax/action-zoom.gif differ diff --git a/www/ajax/frame.html b/www/ajax/frame.html new file mode 100644 index 0000000..3c9aae8 --- /dev/null +++ b/www/ajax/frame.html @@ -0,0 +1,43 @@ + + + + + + +
+
+ + +
+ Move Backwards
+ Move Forwards
+ Zoom Out
+ Select a Time Span
+
+ + +
+ +
+ + diff --git a/www/ajax/index.html b/www/ajax/index.html new file mode 100644 index 0000000..3ed3264 --- /dev/null +++ b/www/ajax/index.html @@ -0,0 +1,50 @@ + + + + + + Statistics Monitoring + + + + + + +
+ + + + +
+ Text +
+ +
+ +
+ + + 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; +} diff --git a/www/ajax/style.css b/www/ajax/style.css new file mode 100644 index 0000000..b72e4ca --- /dev/null +++ b/www/ajax/style.css @@ -0,0 +1,164 @@ + +/* ----------------------------------------------------------------------------- + * MAIN LAYOUT + */ + +* +{ + color: #E7E1CC; + font-family: sans-serif; +} + +body +{ + background-color: #1A1A1A +} + +#main-table +{ + width: 100%; + height: 100%; + padding: 10px; +} + +#top-row +{ + height: 30pt; + padding: 0px; +} + +#main-row + { height: 100%; } + +#main-cell + { background-color: #252424; padding: 5pt; } + +#main-frame +{ + border: none; + height: 100%; + width: 100%; +} + +#logo +{ + font-size: 16pt; + font-weight: bold; +} + +/* ----------------------------------------------------------------------------- + * GROUP HEADERS + */ + +/* A group button at the top */ +.header +{ + background-color: #3D2C2C; + padding: 4pt 6pt; + font-size: 8pt; + font-weight: bold; + -moz-border-radius: 3pt; + cursor: pointer; + margin-right: 5pt; +} + +/* Our sample header (which gets copied for each group) is hidden */ +#header-template + { visibility: hidden; } + +/* ----------------------------------------------------------------------------- + * GRAPHS/FRAME + */ + +body.in-frame +{ + background-color: #252424; +} + +img.graph +{ + margin: 7px; +} + +#zoom +{ + cursor: crosshair; + position: absolute; + background-color: red; + display: none; + top: 0px; + left: 0px; + width: 10px; + height: 10px; + filter: alpha(opacity=40); + -moz-opacity: 0.5; + -khtml-opacity: 0.5; + opacity: 0.5; +} + +/* ----------------------------------------------------------------------------- + * ACTION BAR + */ + +#actions +{ + background-color: #252424; + position: absolute; + left: 50px; + top: 50px; + height: 120px; + display: none; +} + +.action +{ + margin: 3px; + cursor: pointer; + + /* COMPAT: IE doesn't support :hover on anything but links + so transparency has no use whatsover */ + + -moz-opacity: 0.75; + -khtml-opacity: 0.75; + opacity: 0.75; +} + +img.action:hover +{ + -moz-opacity: 1; + -khtml-opacity: 1; + opacity: 1; +} + +#goto +{ + position: absolute; + padding: 10px; + top: 0px; + left: 0px; + background-color: #252424; + display: none; +} + +#goto-select +{ + border: 1px solid black; + font-size: 8pt; + font-weight: normal; + width: 120px; + background-color: #252424; +} + +/* ----------------------------------------------------------------------------- + * MISC + */ + +.error +{ + background-color: #F3BDBD; + border: 1px solid red; + padding: 3pt; + font-size: 8pt; + color: black; +} + -- cgit v1.2.3