var height = 16;
var radius = height / 2;
var margin = 10;
var processArray = new Array();
var rowHeight = 20;
var graphCanvas;
var alertSeconds = 120;
var alertColor = 'rgb(255, 137, 51)';
var barColor = 'rgb(0,171,235)';
var alertTransparentColor = 'rgba(255, 137, 51, 0.9)';
var barTransparentColor = 'rgba(0,171,235,0.9)';
var IGNOREDCOMMAND = 'monkey';
var CURRENTMAX = 600;
var ZEROTIME = "00:00:00"
var CURRENTMAXTIME = "00:00:00"
var context;
var chartWidth = 0;
var chartHeight = 600;
var POLLSERVER = 5000;
var PollServerID = 0;
var FRAMERATE = 30;
var BLANK = " ";
$(document).ready(function()
{
//The following enables the webkit debugger, comment out when done debugging...if ever.
P4JsApi.setWebKitDeveloperExtrasEnabled(true);
init();
$("body").click(graphClick);
$("#prefLink").click(prefClick);
$("#closeButton").click(closePreferences);
$("#terminateButton").click(terminateProcess);
});
function init()
{
chartWidth = $("body")[0].parentElement.clientWidth - margin;
var top = document.getElementById("graphCanvas").offsetTop;
chartHeight = $("body").scrollHeight;
//hide the dialog boxes
$("#infoBox").hide();
$("#preferencesDiv").hide();
//init offscreen canvas and context
graphCanvas = document.getElementById("graphCanvas");
context = graphCanvas.getContext("2d");
graphCanvas.width = chartWidth;
graphCanvas.height = chartHeight;
chartWidth = graphCanvas.width - margin * 2;
LoadProcesses();
PollServerID = setInterval("LoadProcesses()", POLLSERVER);
setInterval("draw()", FRAMERATE);
}
function draw()
{
graphCanvas.width = $("body")[0].parentElement.clientWidth - margin;
var top = document.getElementById("graphCanvas").offsetTop;
var visibleChartHeight = $("body")[0].parentElement.clientHeight - top;
var proposedHeight = (processArray.length + 2) * rowHeight;
//shrink or expand the canvas to fit the number of processes or the window, whichever is larger
if(proposedHeight >= visibleChartHeight )
chartHeight = proposedHeight;
else
chartHeight = visibleChartHeight;
graphCanvas.height = chartHeight;
chartWidth = graphCanvas.width - margin * 2;
DrawBars();
//draw the horizontal axis numbers
var seconds = " (hh:mm:ss)";
context.fillStyle = "white";
context.fillText("0" + seconds, margin, margin);
context.fillText(CURRENTMAXTIME + seconds, chartWidth - GetTextWidth(CURRENTMAXTIME + seconds, context), margin);
//draw vertical line where process length goes into alert
context.fillStyle = "rgba(0, 255, 0, 0.5)";
var alertX = 300;
alertX = chartWidth * (alertSeconds / CURRENTMAX) + margin;
context.fillRect(alertX, rowHeight, 1, graphCanvas.height);
}
function SetEmailAddress(user)
{
P4JsApi.p4("user -o " + user, EmailCallback);
}
function EmailCallback()
{
document.getElementById("email").innerHTML = P4JsApi.encodeForHTML( arguments[0].data[0].Email );
}
function LoadProcesses()
{
P4JsApi.p4("monitor show -l", ProcessCallback);
}
function ProcessCallback()
{
var processes = arguments[0];
var bufArray = new Array();
var longestProcess = 0;
var hideInfoBox = true;
var selectedId = parseInt(document.getElementById("id").innerHTML);
//figure out the max number for the horizontal axis
//and create the Process object
for (var i=0; i<processes.size; i++)
{
var process = processes.data[i];
var seconds = GetSecondsFromTime(process.time);
if( seconds > longestProcess && process.command != IGNOREDCOMMAND )
longestProcess = seconds;
if( seconds > 0 && process.command != IGNOREDCOMMAND )
{
var newProcess = new Process( process.id, process.status, process.user, process.time, seconds, process.command, process.args);
bufArray.push(newProcess);
if( selectedId == process.id )
{
hideInfoBox = false;
UpdateInfoBox(newProcess, seconds > alertSeconds);
}
}
}
if( hideInfoBox )
$(document.getElementById("infoBox")).hide("slow");
//make sure the bars fit within the chart
while( longestProcess > CURRENTMAX )
CURRENTMAX = CURRENTMAX * 2;
bufArray.sort(TimeSorter);
if( longestProcess < 600 )
CURRENTMAX = 600;
CURRENTMAXTIME = GetTimeFromSeconds(CURRENTMAX);
var j = 0;
//now that the processes have been sorted, initialize the bars and set the animation values
for (processIndex in bufArray)
{
var process = bufArray[processIndex];
var width = chartWidth * (process.seconds / CURRENTMAX);
process.roundedrectangle = new RoundedRectangle( margin, rowHeight * j + margin * 2, width, height, radius );
SetPreviousProcessValues(process);
SetAnimationIncrement(process);
j++;
}
processArray = bufArray;
}
function TimeSorter(processA, processB)
{
return (processB.seconds - processA.seconds);
}
function SetPreviousProcessValues(process)
{
var i;
for(i in processArray)
{
var testProcess = processArray[i];
if(testProcess.id == process.id)
{
process.selected = processArray[i].selected;
process.roundedrectangle.previouswidth = processArray[i].roundedrectangle.previouswidth;
break;
}
}
}
function GetSecondsFromTime(time)
{
var timeArray = time.split(":");
var hours = parseInt(timeArray[0]) * 60 * 60;
var minutes = parseInt(timeArray[1]) * 60;
var seconds = parseInt(timeArray[2]);
return hours + minutes + seconds;
}
function GetTimeFromSeconds(totalSec)
{
var hours = parseInt( totalSec / 3600 );
var minutes = parseInt( totalSec / 60 ) % 60;
var seconds = totalSec % 60;
return (hours < 10 ? "0" + hours : hours) + ":" + (minutes < 10 ? "0" + minutes : minutes) + ":" + (seconds < 10 ? "0" + seconds : seconds);
}
function DrawBars()
{
if (graphCanvas.getContext)
{
var i;
for(i in processArray)
{
var process = processArray[i];
process.roundedrectangle.previouswidth = process.roundedrectangle.previouswidth + process.roundedrectangle.animationincrement;
DrawBar(process, process.seconds > alertSeconds);
}
}
}
function graphClick(e)
{
//intercept the click if they clicked on the info box
if( !OpenInfoBoxClicked(e.pageX, e.pageY) )
{
var x = e.offsetX;
var y = e.offsetY;
var barClicked = false;
var i;
var processColor = barTransparentColor;
for(i in processArray)
{
if( !barClicked && BarContains( x, y, processArray[i]) )
{
processDetails(processArray[i]);
SetEmailAddress(processArray[i].user);
processArray[i].selected = true;
if( processArray[i].seconds > alertSeconds )
processColor = alertTransparentColor;
barClicked = true;
}
else
{
processArray[i].selected = false;
}
}
if( barClicked )
{
ShowInfoBox(e.pageX, e.pageY, processColor);
}
else
{
var infoBox = document.getElementById("infoBox");
$(infoBox).hide("slow");
clearDetails();
}
}
}
function OpenInfoBoxClicked(x, y)
{
if( $("#infoBox").is(':hidden') )
return false;
var rectangle = $("#infoBox")[0];
var contains = ( x >= rectangle.offsetLeft && x <= rectangle.offsetLeft + rectangle.offsetWidth && y >= rectangle.offsetTop && y <= rectangle.offsetTop + rectangle.offsetHeight );
return contains;
}
function ShowInfoBox(x, y, color, highlight)
{
var infoBox = document.getElementById("infoBox");
//if the info box wants to show up offscreen to the right, move it on screen
if( x + infoBox.clientWidth > chartWidth )
x = chartWidth - infoBox.clientWidth;
infoBox.style.background = color;
infoBox.style.left = parseInt(x) + "px";
infoBox.style.top = parseInt(y) + "px";
$(infoBox).show("slow");
}
function UpdateInfoBox(process, alert)
{
processDetails(process);
var infoBox = document.getElementById("infoBox");
if( alert )
infoBox.style.background = alertTransparentColor;
else
infoBox.style.background = barTransparentColor;
}
function Process(id, status, user, time, seconds, command, argument)
{
this.id = id;
this.status = status;
this.user = user;
this.time = time;
this.seconds = seconds;
this.command = command;
this.argument = argument;
this.selected = false;
this.UserNameWidth = GetTextWidth(BLANK + user, context);
}
function GetTextWidth(text, element)
{
var textTester = document.getElementById("TextTester");
textTester.style.font = element.font;
textTester.innerHTML = P4JsApi.encodeForHTML(text);
return textTester.clientWidth + 1;
}
function RoundedRectangle(x,y,width,height, radius)
{
this.x = x;
this.y = y;
this.width = width;
this.previouswidth = 0;
this.height = height;
this.radius = radius;
this.animationincrement = 0;
}
function SetAnimationIncrement(process)
{
process.roundedrectangle.animationincrement = (process.roundedrectangle.width - process.roundedrectangle.previouswidth) / (POLLSERVER/FRAMERATE);
}
function BarContains(x, y, process)
{
var roundedrectangle = process.roundedrectangle;
var rectX = roundedrectangle.x;
var rectY = roundedrectangle.y;
var rectWidth = rectX + roundedrectangle.width + process.UserNameWidth;
var contains = ( x >= rectX && x <= rectWidth && y >= rectY && y <= roundedrectangle.height + 2 + rectY );//that '2' in there is cheating, not sure why I need it, but I do...
return contains;
}
function processDetails(process)
{
document.getElementById("id").innerHTML = P4JsApi.encodeForHTML(process.id);
document.getElementById("status").innerHTML = P4JsApi.encodeForHTML(process.status);
document.getElementById("user").innerHTML = P4JsApi.encodeForHTML(process.user);
//email is done separately...
document.getElementById("time").innerHTML = P4JsApi.encodeForHTML(process.time) + " hh:mm:ss";
document.getElementById("command").innerHTML = P4JsApi.encodeForHTML(process.command);
document.getElementById("argument").innerHTML = P4JsApi.encodeForHTML(process.argument);
}
function clearDetails()
{
document.getElementById("id").value = "";
document.getElementById("status").value = "";
document.getElementById("user").value = "";
document.getElementById("email").value = "";
document.getElementById("time").value = "";
document.getElementById("command").value = "";
document.getElementById("argument").value = "";
}
function DrawBar(process, alert)
{
var roundedrectangle = process.roundedrectangle;
var x = roundedrectangle.x;
var y = roundedrectangle.y;
var width = roundedrectangle.previouswidth;
if( width > roundedrectangle.width )
width = roundedrectangle.width;
var height= roundedrectangle.height;
var radius = roundedrectangle.radius;
if( radius * 2 > width )
radius = width / 2;
context.beginPath();
context.lineTo(x,y+height-radius);//start at lower left corner - the radius
context.quadraticCurveTo(x,y+height,x+radius,y+height);//curve around to bottom
context.lineTo(x+width-radius,y+height);//draw bottom line
context.quadraticCurveTo(x+width,y+height,x+width,y+height-radius);//curve around to right side
context.lineTo(x+width,y+radius);//draw right side
context.quadraticCurveTo(x+width,y,x+width-radius,y);//curve around to top
context.lineTo(x+radius,y);//draw top
context.quadraticCurveTo(x,y,x,y+radius);//curve around to left
context.closePath();//draws the left side
var lingrad = context.createLinearGradient(x,y,x,y+height);
var color = barColor;
var highlight = '#a6d8ea';
if( alert )
{
color = alertColor;
highlight = '#fad2b4';
}
lingrad.addColorStop(0, color);
lingrad.addColorStop(0.4, highlight);
lingrad.addColorStop(0.6, highlight);
lingrad.addColorStop(1, color);
context.fillStyle = lingrad;
context.fill();
if( process.selected )
{
context.strokeStyle = "rgb(255, 255, 255)";
context.lineWidth = 2;
context.stroke();
}
context.fillText(P4JsApi.encodeForHTML(BLANK + process.user), width + roundedrectangle.x, roundedrectangle.y + roundedrectangle.height/2);
//for some reason, in order to turn the fill color back to white I have to do this rather than fillStyle = "rgb(255,255,255)";
lingrad.addColorStop(0, '#ffffff');
lingrad.addColorStop(0.4, '#ffffff');
lingrad.addColorStop(0.6, '#ffffff');
lingrad.addColorStop(1, '#ffffff');
context.fillStyle = lingrad;
}
function prefClick()
{
if( $("#preferencesDiv").is(':hidden') )
{
//populate the prefs
$("#alert-seconds-input").val(alertSeconds);
$("#poll-seconds-input").val(POLLSERVER/1000);
$("#Show-idle-input").attr('checked', IGNOREDCOMMAND != 'IDLE');
//then show the dialog
$("#preferencesDiv").show("slow");
}
else
$("#preferencesDiv").hide("slow");
}
function closePreferences()
{
var checkInt = parseInt($("#alert-seconds-input").val());
if( isNaN(checkInt) )
{
alert("Enter a valid number of seconds for showing alert processes.");
$("#alert-seconds-input").val(alertSeconds);
return;
}
alertSeconds = checkInt;
checkInt = parseInt($("#poll-seconds-input").val());
if( isNaN(checkInt) )
{
alert("Enter a valid number of seconds for polling the server.");
$("#poll-seconds-input").val(POLLSERVER / 1000);
return;
}
//restart the server-polling interval if it's been changed
var pollInterval = checkInt * 1000;
$("#preferencesDiv").hide("slow");
if( POLLSERVER != pollInterval )
{
POLLSERVER = pollInterval;
clearInterval(PollServerID);
PollServerID = setInterval("LoadProcesses()", POLLSERVER);
}
if( $("#Show-idle-input").is(':checked' ) )
IGNOREDCOMMAND = 'MONKEY';//I use Monkey because I'm pretty sure we'll never have a command called Monkey...
else
IGNOREDCOMMAND = 'IDLE';
LoadProcesses();
}
function terminateProcess()
{
var i;
for(i in processArray)
{
if( processArray[i].selected )
{
var terminate = confirm("You are about to terminate process " + parseInt(processArray[i].id) + "." + "\nAre you sure you want to continue?");
if (terminate)
{
P4JsApi.p4("monitor terminate " + parseInt(processArray[i].id));
$("#infoBox").hide("slow");
}
break;
}
}
}