if (typeof simulate === 'undefined') {
var simulate = false;
}
$(function(){
jQuery.fx.interval = 50;
if (simulate) {
setTimeout(initializeTest, 1000);
return;
}
checkInstalledPlugins();
initializeTest();
});
// CONSTANTS
// Testing phases
var PHASE_LOADING = 0;
var PHASE_WELCOME = 1;
var PHASE_PREPARING = 2;
var PHASE_UPLOAD = 3;
var PHASE_DOWNLOAD = 4;
var PHASE_RESULTS = 5;
// STATUS VARIABLES
var use_websocket_client = false;
var websocket_client = null;
var currentPhase = PHASE_LOADING;
var currentPage = 'welcome';
var transitionSpeed = 400;
// A front-end implementation could define some specific server. If not, then
// just use the current server's hostname.
if (typeof window.ndtServer === 'undefined') {
var ndtServer = location.hostname;
}
// Gauges used for showing download/upload speed
var downloadGauge, uploadGauge;
var gaugeUpdateInterval;
var gaugeMaxValue = 1000;
// PRIMARY METHODS
function initializeTest() {
// Initialize gauges
initializeGauges();
// Initialize start buttons
$('.start.button').click(startTest);
// Results view selector
$('#results .view-selector .summary').click(showResultsSummary);
$('#results .view-selector .details').click(showResultsDetails);
$('#results .view-selector .advanced').click(showResultsAdvanced);
$('body').removeClass('initializing');
$('body').addClass('ready');
//return showPage('results');
setPhase(PHASE_WELCOME);
}
function startTest(evt) {
evt.stopPropagation();
evt.preventDefault();
createBackend();
if (!isPluginLoaded()) {
$('#warning-plugin').show();
return;
}
$('#warning-plugin').hide();
$('#javaButton').attr('disabled', true);
$('#websocketButton').attr('disabled', true);
showPage('test', resetGauges);
$('#rttValue').html('');
if (simulate) return simulateTest();
currentPhase = PHASE_WELCOME;
testNDT().run_test(ndtServer);
monitorTest();
}
function simulateTest() {
setPhase(PHASE_RESULTS);
return;
setPhase(PHASE_PREPARING);
setTimeout(function(){ setPhase(PHASE_UPLOAD) }, 2000);
setTimeout(function(){ setPhase(PHASE_DOWNLOAD) }, 4000);
setTimeout(function(){ setPhase(PHASE_RESULTS) }, 6000);
}
function monitorTest() {
var message = testError();
var currentStatus = testStatus();
/*
var currentStatus = testStatus();
debug(currentStatus);
var diagnosis = testDiagnosis();
debug(diagnosis);
*/
if (message.match(/not run/) && currentPhase != PHASE_LOADING) {
setPhase(PHASE_WELCOME);
return false;
}
if (message.match(/completed/) && currentPhase < PHASE_RESULTS) {
setPhase(PHASE_RESULTS);
return true;
}
if (message.match(/failed/) && currentPhase < PHASE_RESULTS) {
setPhase(PHASE_RESULTS);
return false;
}
if (currentStatus.match(/Outbound/) && currentPhase < PHASE_UPLOAD) {
setPhase(PHASE_UPLOAD);
}
if (currentStatus.match(/Inbound/) && currentPhase < PHASE_DOWNLOAD) {
setPhase(PHASE_DOWNLOAD);
}
if (!currentStatus.match(/Middleboxes/) && !currentStatus.match(/notStarted/)
&& !remoteServer().match(/ndt/) && currentPhase == PHASE_PREPARING) {
debug('Remote server is ' + remoteServer());
setPhase(PHASE_UPLOAD);
}
if (remoteServer() !== 'unknown' && currentPhase < PHASE_PREPARING) {
setPhase(PHASE_PREPARING);
}
setTimeout(monitorTest, 1000);
}
// PHASES
function setPhase(phase) {
switch (phase) {
case PHASE_WELCOME:
debug('WELCOME');
showPage('welcome');
break;
case PHASE_PREPARING:
uploadGauge.setValue(0);
downloadGauge.setValue(0);
debug('PREPARING TEST');
$('#loading').show();
$('#upload').hide();
$('#download').hide();
showPage('test', resetGauges);
break;
case PHASE_UPLOAD:
var pcBuffSpdLimit, rtt, gaugeConfig = [];
debug('UPLOAD TEST');
pcBuffSpdLimit = speedLimit();
rtt = averageRoundTrip();
if (isNaN(rtt)) {
$('#rttValue').html('n/a');
} else {
$('rttValue').html(printNumberValue(Math.round(rtt)) + ' ms');
}
if (!isNaN(pcBuffSpdLimit)) {
if (pcBuffSpdLimit > gaugeMaxValue) {
pcBuffSpdLimit = gaugeMaxValue;
}
gaugeConfig.push({
from: 0, to: pcBuffSpdLimit, color: 'rgb(0, 255, 0)'
});
gaugeConfig.push({
from: pcBuffSpdLimit, to: gaugeMaxValue, color: 'rgb(255, 0, 0)'
});
uploadGauge.updateConfig({
highlights: gaugeConfig
});
downloadGauge.updateConfig({
highlights: gaugeConfig
});
}
$('#loading').hide();
$('#upload').show();
gaugeUpdateInterval = setInterval(function(){
updateGaugeValue();
},1000);
$('#test .remote.location .address').get(0).innerHTML = remoteServer();
break;
case PHASE_DOWNLOAD:
debug('DOWNLOAD TEST');
$('#upload').hide();
$('#download').show();
break;
case PHASE_RESULTS:
debug('SHOW RESULTS');
debug('Testing complete');
printDownloadSpeed();
printUploadSpeed();
$('#latency').html(printNumberValue(Math.round(averageRoundTrip())));
$('#jitter').html(printJitter(false));
$('#test-details').html(testDetails());
$('#test-advanced').append(testDiagnosis());
$('#javaButton').attr('disabled', false);
showPage('results');
break;
default:
return false;
}
currentPhase = phase;
}
// PAGES
function showPage(page, callback) {
debug('Show page: ' + page);
if (page == currentPage) {
if (callback !== undefined) callback();
return true;
}
if (currentPage !== undefined) {
$('#' + currentPage).fadeOut(transitionSpeed, function(){
$('#' + page).fadeIn(transitionSpeed, callback);
});
}
else {
debug('No current page');
$('#' + page).fadeIn(transitionSpeed, callback);
}
currentPage = page;
}
// RESULTS
function showResultsSummary() {
showResultsPage('summary');
}
function showResultsDetails() {
showResultsPage('details');
}
function showResultsAdvanced() {
showResultsPage('advanced');
}
function showResultsPage(page) {
debug('Results: show ' + page);
var pages = ['summary', 'details', 'advanced'];
for (var i=0, len=pages.length; i < len; i++) {
$('#results')[(page == pages[i]) ? 'addClass' : 'removeClass'](pages[i]);
}
}
// GAUGE
function initializeGauges() {
var gaugeValues = [];
for (var i=0; i<=10; i++) {
gaugeValues.push(0.1 * gaugeMaxValue * i);
}
uploadGauge = new Gauge({
renderTo : 'uploadGauge',
width : 270,
height : 270,
units : 'Mb/s',
title : 'Upload',
minValue : 0,
maxValue : gaugeMaxValue,
majorTicks : gaugeValues,
highlights : [{ from: 0, to: gaugeMaxValue, color: 'rgb(0, 255, 0)' }]
});;
gaugeValues = [];
for (var i=0; i<=10; i++) {
gaugeValues.push(0.1 * gaugeMaxValue * i);
}
downloadGauge = new Gauge({
renderTo : 'downloadGauge',
width : 270,
height : 270,
units : 'Mb/s',
title : 'Download',
minValue : 0,
maxValue : gaugeMaxValue,
majorTicks : gaugeValues,
highlights : [{ from: 0, to: gaugeMaxValue, color: 'rgb(0, 255, 0)' }]
});;
}
function resetGauges() {
var gaugeConfig = [];
gaugeConfig.push({
from: 0, to: gaugeMaxValue, color: 'rgb(0, 255, 0)'
});
uploadGauge.updateConfig({
highlights: gaugeConfig
});
uploadGauge.setValue(0);
downloadGauge.updateConfig({
highlights: gaugeConfig
});
downloadGauge.setValue(0);
}
function updateGaugeValue() {
var downloadSpeedVal = downloadSpeed();
var uploadSpeedVal = uploadSpeed(false);
if (currentPhase == PHASE_UPLOAD) {
uploadGauge.updateConfig({
units: getSpeedUnit(uploadSpeedVal)
});
uploadGauge.setValue(getJustfiedSpeed(uploadSpeedVal));
} else if (currentPhase == PHASE_DOWNLOAD) {
downloadGauge.updateConfig({
units: getSpeedUnit(downloadSpeedVal)
});
downloadGauge.setValue(getJustfiedSpeed(downloadSpeedVal));
} else {
clearInterval(gaugeUpdateInterval);
}
}
// TESTING JAVA/WEBSOCKET CLIENT
function testNDT() {
if (websocket_client) {
return websocket_client;
}
return $('#NDT');
}
function testStatus() {
return testNDT().get_status();
}
function testDiagnosis() {
var div = document.createElement('div');
if (simulate) {
div.innerHTML = 'Test diagnosis';
return div;
}
var diagnosisArray = testNDT().get_diagnosis().split('\n');
var txt = '';
var table;
var isTable = false;
diagnosisArray.forEach(
function addRow(value) {
if (isTable) {
rowArray = value.split(':');
if (rowArray.length>1) {
var row = table.insertRow(-1);
rowArray.forEach(
function addCell(cellValue, idx) {
var cell =row.insertCell(idx);
cell.innerHTML = cellValue;
}
);
} else {
isTable = false;
txt = txt + value;
}
} else {
if (value.indexOf('=== Results sent by the server ===') != -1) {
table = document.createElement('table');
isTable = true;
} else {
txt = txt + value + '\n';
}
}
}
);
txt = txt + '=== Results sent by the server ===';
div.innerHTML = txt;
if (isTable) {
div.appendChild(table);
}
return div;
}
function testError() {
return testNDT().get_errmsg();
}
function remoteServer() {
if (simulate) return '0.0.0.0';
return testNDT().get_host();
}
function uploadSpeed(raw) {
if (simulate) return 0;
var speed = testNDT().getNDTvar('ClientToServerSpeed');
return raw ? speed : parseFloat(speed);
}
function downloadSpeed() {
if (simulate) return 0;
return parseFloat(testNDT().getNDTvar('ServerToClientSpeed'));
}
function averageRoundTrip() {
if (simulate) return 0;
return parseFloat(testNDT().getNDTvar('avgrtt'));
}
function jitter() {
if (simulate) return 0;
return parseFloat(testNDT().getNDTvar('Jitter'));
}
function speedLimit() {
if (simulate) return 0;
return parseFloat(testNDT().get_PcBuffSpdLimit());
}
function printPacketLoss() {
var packetLoss = parseFloat(testNDT().getNDTvar('loss'));
packetLoss = (packetLoss*100).toFixed(2);
return packetLoss;
}
function printJitter(boldValue) {
var retStr = '';
var jitterValue = jitter();
if (jitterValue >= 1000) {
retStr += (boldValue ? '' : '') + printNumberValue(jitterValue/1000) + (boldValue ? '' : '') + ' sec';
} else {
retStr += (boldValue ? '' : '') + printNumberValue(jitterValue) + (boldValue ? '' : '') + ' msec';
}
return retStr;
}
function getSpeedUnit(speedInKB) {
var unit = ['kb/s', 'Mb/s', 'Gb/s', 'Tb/s', 'Pb/s', 'Eb/s'];
var e = Math.floor(Math.log(speedInKB*1000) / Math.log(1000));
return unit[e];
}
function getJustfiedSpeed(speedInKB) {
var e = Math.floor(Math.log(speedInKB) / Math.log(1000));
return (speedInKB / Math.pow(1000, e)).toFixed(2);
}
function printDownloadSpeed() {
var downloadSpeedVal = downloadSpeed();
$('#download-speed').html(getJustfiedSpeed(downloadSpeedVal));
$('#download-speed-units').html(getSpeedUnit(downloadSpeedVal));
}
function printUploadSpeed() {
var uploadSpeedVal = uploadSpeed(false);
$('#upload-speed').html(getJustfiedSpeed(uploadSpeedVal));
$('#upload-speed-units').html(getSpeedUnit(uploadSpeedVal));
}
function readNDTvar(variable) {
var ret = testNDT().getNDTvar(variable);
return !ret ? '-' : ret;
}
function printNumberValue(value) {
return isNaN(value) ? '-' : value;
}
function testDetails() {
if (simulate) return 'Test details';
var d = '';
var errorMsg = testError();
if (errorMsg.match(/failed/)) {
d += 'Error occured while performing test:
'.bold();
if (errorMsg.match(/#2048/)) {
d += 'Security error. This error may be caused by firewall issues, make sure that port 843 is available on the NDT server, and that you can access it.'.bold().fontcolor('red') + '
';
} else {
d += errorMsg.bold().fontcolor('red') + '
';
}
}
d += 'Your system: ' + readNDTvar('OperatingSystem').bold() + '
';
d += 'Plugin version: ' + (readNDTvar('PluginVersion') + ' (' + readNDTvar('OsArchitecture') + ')
').bold();
d += '
';
d += 'TCP receive window: ' + readNDTvar('CurRwinRcvd').bold() + ' current, ' + readNDTvar('MaxRwinRcvd').bold() + ' maximum
';
d += '' + printNumberValue(printPacketLoss()) + ' % of packets lost during test
';
d += 'Round trip time: ' + readNDTvar('MinRTT').bold() + ' msec (minimum), ' + readNDTvar('MaxRTT').bold() + ' msec (maximum), ' + printNumberValue(Math.round(averageRoundTrip())) + ' msec (average)
';
d += 'Jitter: ' + printNumberValue(printJitter(true)) + '
';
d += readNDTvar('waitsec').bold() + ' seconds spend waiting following a timeout
';
d += 'TCP time-out counter: ' + readNDTvar('CurRTO').bold() + '
';
d += readNDTvar('SACKsRcvd').bold() + ' selective acknowledgement packets received
';
d += '
';
if (readNDTvar('mismatch') == 'yes') {
d += 'A duplex mismatch condition was detected.
'.fontcolor('red').bold();
}
else {
d += 'No duplex mismatch condition was detected.
'.fontcolor('green');
}
if (readNDTvar('bad_cable') == 'yes') {
d += 'The test detected a cable fault.
'.fontcolor('red').bold();
}
else {
d += 'The test did not detect a cable fault.
'.fontcolor('green');
}
if (readNDTvar('congestion') == 'yes') {
d += 'Network congestion may be limiting the connection.
'.fontcolor('red').bold();
}
else {
d += 'No network congestion was detected.
'.fontcolor('green');
}
d += '
';
d += printNumberValue(readNDTvar('cwndtime')).bold() + ' % of the time was not spent in a receiver limited or sender limited state.
';
d += printNumberValue(readNDTvar('rwintime')).bold() + ' % of the time the connection is limited by the client machine\'s receive buffer.
';
d += 'Optimal receive buffer: ' + printNumberValue(readNDTvar('optimalRcvrBuffer')).bold() + ' bytes
';
d += 'Bottleneck link: ' + readNDTvar('accessTech').bold() + '
';
d += readNDTvar('DupAcksIn').bold() + ' duplicate ACKs set
';
return d;
}
// BACKEND METHODS
function useJavaAsBackend() {
$('#warning-websocket').hide();
$('#rtt').show();
$('#rttValue').show();
$('.warning-environment').innerHTML = '';
use_websocket_client = false;
$('#websocketButton').removeClass('active');
$('#javaButton').addClass('active');
}
function useWebsocketAsBackend() {
$('#rtt').hide();
$('#rttValue').hide();
$('#warning-websocket').show();
use_websocket_client = true;
$('#javaButton').removeClass('active');
$('#websocketButton').addClass('active');
}
function createBackend() {
$('#backendContainer').empty();
if (use_websocket_client) {
websocket_client = new NDTWrapper(window.ndtServer);
}
else {
var app = document.createElement('applet');
app.id = 'NDT';
app.name = 'NDT';
app.archive = 'Tcpbw100.jar';
app.code = 'edu.internet2.ndt.Tcpbw100.class';
app.width = '600';
app.height = '10';
$('#backendContainer').append(app);
}
}
// UTILITIES
function debug(message) {
if (typeof allowDebug !== 'undefined') {
if (allowDebug && window.console) console.debug(message);
}
}
function isPluginLoaded() {
try {
testStatus();
return true;
} catch(e) {
return false;
}
}
function checkInstalledPlugins() {
var hasJava = false;
var hasWebsockets = false;
$('#warning-plugin').hide();
$('#warning-websocket').hide();
hasJava = true;
if (typeof deployJava !== 'undefined') {
if (deployJava.getJREs() == '') {
hasJava = false;
}
}
hasWebsockets = false;
try {
var ndt_js = new NDTjs();
if (ndt_js.checkBrowserSupport()) {
hasWebsockets = true;
}
} catch(e) {
hasWebsockets = false;
}
if (!hasWebsockets) {
$('#websocketButton').attr('disabled', true);
}
if (!hasJava) {
$('#javaButton').attr('disabled', true);
}
if (hasWebsockets) {
useWebsocketAsBackend();
}
else if (hasJava) {
useJavaAsBackend();
}
}
// Attempts to determine the absolute path of a script, minus the name of the
// script itself.
function getScriptPath() {
var scripts = document.getElementsByTagName('SCRIPT');
var fileRegex = new RegExp('\/ndt-wrapper\.js$');
var path = '';
if (scripts && scripts.length > 0) {
for(var i in scripts) {
if(scripts[i].src && scripts[i].src.match(fileRegex)) {
path = scripts[i].src.replace(fileRegex, '');
break;
}
}
}
return path.substring(location.origin.length);
};