(function (context, $) { "use strict"; context.JK = context.JK || {}; context.JK.NetworkTest = function (app) { var NETWORK_TEST_TYPES = { RestPhase: 0, PktTest100NormalLatency: 1, PktTest200MediumLatency: 2, PktTest400LowLatency: 3, PktTestRateSweep: 4, RcvOnly: 5 } var STARTING_NUM_CLIENTS_AUDIO = 4; var STARTING_NUM_CLIENTS_VIDEO = 2; var AUDIO_PAYLOAD_SIZE = gon.global.ftue_network_test_packet_size; var VIDEO_PAYLOAD_SIZE = gon.global.ftue_network_test_packet_size_video; var MINIMUM_ACCEPTABLE_SESSION_SIZE = 2; var RETRY_THRESHOLD = 2; var gearUtils = context.JK.GearUtils; var rest = context.JK.Rest(); var logger = context.JK.logger; var $step = null; var TEST_SUCCESS_CALLBACK = 'JK.HandleNetworkTestSuccess'; var TEST_TIMEOUT_CALLBACK = 'JK.HandleNetworkTestTimeout'; var $startNetworkTestBtn = null; var $foreverNetworkTestBtn = null; var $testResults = null; var $testScoreAudio = null; var $testScoreVideo= null; var $testText = null; var $inProgressText = null; var $audioResultText = null; var $videoResultText = null; var testedSuccessfully = false; var $scoringBar = null; var $goodMarker = null; var $goodLine = null; var $currentScore = null; var $scoredClientsAudio = null; var $scoredClientsVideo = null; var $subscore = null; var $watchVideo = null; var $container = null; var backendGuardTimeout = null; var primeGuardTimeout = null; var primeDeferred = null; var serverClientId = ''; var audioScoring = false; var videoScoring = false; var numClientToTestAudio = STARTING_NUM_CLIENTS_AUDIO; var numClientToTestVideo = STARTING_NUM_CLIENTS_VIDEO; var testSummary = {audioAttempts: [], videoAttempts: [], final: null} var $self = $(this); var scoringZoneWidth = 100;//px var inGearWizard = false; var operatingSystem = null; var PRIME_PUMP_TIME = 1; var forever = false; // these try to make it such that we only pass a NetworkTest Pass/Failed one time in a new user flow var trackedPass = false; var lastNetworkFailure = null; var bandwidthSamples = []; var NETWORK_TEST_START = 'network_test.start'; var NETWORK_TEST_DONE = 'network_test.done'; var NETWORK_TEST_FAIL = 'network_test.fail'; var NETWORK_TEST_CANCEL = 'network_test.cancel'; function createSuccessCallbackName(priming) { if (priming) { if (inGearWizard) { return TEST_SUCCESS_CALLBACK + 'ForPumpPrimingGW'; } else { return TEST_SUCCESS_CALLBACK + 'ForPumpPrimingDialog'; } } else { if (inGearWizard) { return TEST_SUCCESS_CALLBACK + 'ForGearWizard'; } else { return TEST_SUCCESS_CALLBACK + 'ForDialog'; } } } function createTimeoutCallbackName(priming) { if (priming) { if (inGearWizard) { return TEST_TIMEOUT_CALLBACK + 'ForPumpPrimingGW'; } else { return TEST_TIMEOUT_CALLBACK + 'ForPumpPrimingDialog'; } } else { if (inGearWizard) { return TEST_TIMEOUT_CALLBACK + 'ForGearWizard'; } else { return TEST_TIMEOUT_CALLBACK + 'ForDialog'; } } } // this averages bandwidthSamples; this method is meant just for GA data function avgBandwidth(num_others) { if (bandwidthSamples.length == 0) { return 0; } else { var total = 0; context._.each(bandwidthSamples, function (sample) { total += (sample * num_others * 400 * (100 + 28)); // sample is a percentage of 400. So sample * 400 gives us how many packets/sec. 100 is payload; 28 is UDP+ETHERNET overhead, to give us bandwidth }) return total / bandwidthSamples.length; } } function reset() { trackedPass = false; lastNetworkFailure = null; resetTestState(); } function resetTestState() { serverClientId = ''; audioScoring = false; videoScoring = false; numClientToTestAudio = STARTING_NUM_CLIENTS_AUDIO; numClientToTestVideo = STARTING_NUM_CLIENTS_VIDEO; testSummary = {audioAttempts: [], videoAttempts:[]}; configureStartButton(); $scoredClientsAudio.empty(); $testResults.removeClass('good acceptable bad testing'); $testScoreAudio.removeClass('good acceptable bad testing'); $testScoreVideo.removeClass('good acceptable bad testing'); $scoredClientsAudio.text('-') $scoredClientsVideo.text('-') $inProgressText.empty(); $audioResultText.empty(); $audioResultText.hide(); $videoResultText.empty(); $videoResultText.hide(); $subscore.empty(); $currentScore.width(0); bandwidthSamples = []; } function renderStartTestAudio() { configureStartButton(); $testResults.addClass('testing'); $testScoreAudio.addClass('testing'); $goodLine.css('left', (gon.ftue_packet_rate_treshold * 100) + '%'); $goodMarker.css('left', (gon.ftue_packet_rate_treshold * 100) + '%'); } function renderStartTestVideo() { configureStartButton(); $testResults.addClass('testing'); $testScoreVideo.addClass('testing'); $goodLine.css('left', (gon.ftue_packet_rate_treshold * 100) + '%'); $goodMarker.css('left', (gon.ftue_packet_rate_treshold * 100) + '%'); } function renderStopTestAudio(score, text) { if(!score || score.length == 0) { $scoredClientsAudio.html('0'); $inProgressText.html('The audio test did not pass. Video is not tested in this case.

Please click HERE for help information.'); } else { $scoredClientsAudio.html(score); } if(text && text.length > 0) { $audioResultText.text(text); } $testResults.removeClass('testing'); $testScoreAudio.removeClass('testing'); } function renderStopTestVideo(score, text) { logger.debug("renderStopTestVideo", score, text) // don't show the audio result text until the test is over (it looks confusing otherwise). if($audioResultText.text() && $audioResultText.text().length > 0) { $audioResultText.show(); } $inProgressText.text('Your router and Internet service will support:') if(!score || score.length == 0) { $scoredClientsVideo.html('-'); } else { if(score < 2) { $scoredClientsVideo.html('0') $videoResultText.html('No other players when in a video + audio session.').show(); $testScoreVideo.addClass('acceptable'); } else { $scoredClientsVideo.html(score); var summary = "Video + audio sessions with up to " + score + " players"; if (text && text.length > 0) { // presence of text means there was an error on the last test pass. summary += '. Note that there was an error when testing for ' + (score + 1) + 'players. Support code=' + text } $videoResultText.html(summary).show(); } } $testResults.removeClass('testing'); $testScoreVideo.removeClass('testing'); } function postDiagnostic() { rest.createDiagnostic({ type: 'NETWORK_TEST_RESULT', data: {client_type: context.JK.clientType(), client_id: context.JK.JamServer.clientID, summary: testSummary} }); } function appendContextualStatement() { if (inGearWizard) { return " You can skip this step for now." } else { return ''; } } function getLastNetworkFailure() { return lastNetworkFailure; } function haltScoring() { context.jamClient.SetLatencyTestBlocked(true) rest.updateNetworkTesting({client_id: app.clientId, is_network_testing: true}) .fail(function(jqXHR) { if(jqXHR.status == 404) { // assume connection is missing app.notifyAlert("Not Connected", "You must be connected to the server to run the network test.") } else { app.notifyServerError(jqXHR, "Unable to tell server that we are beginning the network test") } }) } function resumeScoring() { context.jamClient.SetLatencyTestBlocked(false) rest.updateNetworkTesting({client_id: app.clientId, is_network_testing: false}) .fail(function(jqXHR) { if(jqXHR.status == 404) { // assume connection is missing // do nothing in this case } else { app.notifyServerError(jqXHR, "Unable to tell server that we are ending the network test") } }) } function storeLastNetworkFailure(reason, data) { if (!trackedPass) { lastNetworkFailure = {reason: reason, data: data}; } } function testFinishedAudio() { var attempt = getCurrentAttemptAudio(); if (!testSummary.final) { testSummary.final = {reason: attempt.reason}; } var reason = testSummary.final.reason; var success = false; if (reason == "success") { renderStopTestAudio(attempt.num_clients, "Audio-only sessions with up to " + attempt.num_clients + " players") testedSuccessfully = true; if (!testSummary.final.num_clients) { testSummary.final.num_clients = attempt.num_clients; } // context.jamClient.GetNetworkTestScore() == 0 is a rough approximation if the user has passed the FTUE before if (inGearWizard || context.jamClient.GetNetworkTestScore() == 0) { trackedPass = true; lastNetworkFailure = null; context.JK.GA.trackNetworkTest(context.JK.detectOS(), testSummary.final.num_clients); } context.jamClient.SetNetworkTestScore(attempt.num_clients); if (testSummary.final.num_clients == 2) { $testScoreAudio.addClass('acceptable'); } else { $testScoreAudio.addClass('good'); } success = true; } else if (reason == "minimum_client_threshold") { context.jamClient.SetNetworkTestScore(0); renderStopTestAudio('', "We're sorry, but your router and Internet service will not effectively support JamKazam sessions. Please click the HELP button for more information.") storeLastNetworkFailure(context.JK.GA.NetworkTestFailReasons.bandwidth, avgBandwidth(attempt.num_clients - 1)); } else if (reason == "unreachable" || reason == "no-transmit") { context.jamClient.SetNetworkTestScore(0); // https://jamkazam.atlassian.net/browse/VRFS-2323 renderStopTestAudio('', "We're sorry, but your router will not support JamKazam in its current configuration. Please click HERE for more information."); storeLastNetworkFailure(context.JK.GA.NetworkTestFailReasons.stun, attempt.num_clients); } else if (reason == "internal_error") { context.JK.alertSupportedNeeded("The JamKazam client software had an unexpected problem while scoring your Internet connection."); renderStopTestAudio('', ''); storeLastNetworkFailure(context.JK.GA.NetworkTestFailReasons.jamerror); } else if (reason == "remote_peer_cant_test") { context.JK.alertSupportedNeeded("The JamKazam service is experiencing technical difficulties."); renderStopTestAudio('', ''); storeLastNetworkFailure(context.JK.GA.NetworkTestFailReasons.jamerror); } else if (reason == "server_comm_timeout") { gearUtils.skipNetworkTest(); context.JK.alertSupportedNeeded("Communication with the JamKazam network service has timed out." + appendContextualStatement()); renderStopTestAudio('', ''); storeLastNetworkFailure(context.JK.GA.NetworkTestFailReasons.jamerror); } else if (reason == 'backend_gone') { context.JK.alertSupportedNeeded("The JamKazam client is experiencing technical difficulties."); renderStopTestAudio('', ''); storeLastNetworkFailure(context.JK.GA.NetworkTestFailReasons.jamerror); } else if (reason == "invalid_response") { gearUtils.skipNetworkTest(); context.JK.alertSupportedNeeded("The JamKazam client software had an unexpected problem while scoring your Internet connection.

Reason: " + attempt.backend_data.reason + '.'); renderStopTestAudio('', ''); storeLastNetworkFailure(context.JK.GA.NetworkTestFailReasons.jamerror); } else if (reason == 'no_servers') { gearUtils.skipNetworkTest(); context.JK.Banner.showAlert("No network test servers are available." + appendContextualStatement()); renderStopTestAudio('', ''); testedSuccessfully = true; storeLastNetworkFailure(context.JK.GA.NetworkTestFailReasons.jamerror); } else if (reason == 'no_network') { context.JK.Banner.showAlert("Please try again later. Your network appears down."); renderStopTestAudio('', ''); storeLastNetworkFailure(context.JK.GA.NetworkTestFailReasons.noNetwork); } else if (reason == "rest_api_error") { gearUtils.skipNetworkTest(); context.JK.alertSupportedNeeded("Unable to acquire a network test server." + appendContextualStatement()); testedSuccessfully = true; renderStopTestAudio('', ''); storeLastNetworkFailure(context.JK.GA.NetworkTestFailReasons.jamerror); } else if (reason == "timeout") { gearUtils.skipNetworkTest(); context.JK.alertSupportedNeeded("Communication with the JamKazam network service timed out." + appendContextualStatement()); testedSuccessfully = true; renderStopTestAudio('', ''); storeLastNetworkFailure(context.JK.GA.NetworkTestFailReasons.jamerror); } else { gearUtils.skipNetworkTest(); context.JK.alertSupportedNeeded("The JamKazam client software had a logic error while scoring your Internet connection."); renderStopTestAudio('', ''); storeLastNetworkFailure(context.JK.GA.NetworkTestFailReasons.jamerror); } numClientToTestAudio = STARTING_NUM_CLIENTS_AUDIO; audioScoring = false; postDiagnostic(); if (success) { if(window.VideoStore.videoEnabled) { startVideoTest(); } else { // don't test video if it's disabled renderStopTestVideo(null, null) numClientToTestVideo = STARTING_NUM_CLIENTS_VIDEO; videoScoring = false; configureStartButton(); $self.triggerHandler(NETWORK_TEST_DONE) } } else { configureStartButton(); $self.triggerHandler(NETWORK_TEST_FAIL) } } function testFinishedVideo() { var attempt = getCurrentAttemptVideo(); if (!testSummary.video_final) { testSummary.video_final = {reason: attempt.reason, num_clients: attempt.num_clients}; } if (!testSummary.video_final.num_clients) { testSummary.video_final.num_clients = attempt.num_clients; } var reason = testSummary.video_final.reason; var success = false; logger.debug("testFinishedVideo", testSummary) if (reason == "success") { renderStopTestVideo(attempt.num_clients, null) //testedSuccessfully = true; if (!testSummary.video_final.num_clients) { testSummary.video_final.num_clients = attempt.num_clients; } // context.jamClient.GetNetworkTestScore() == 0 is a rough approximation if the user has passed the FTUE before if (inGearWizard || context.jamClient.GetVideoNetworkTestScore() == 0) { //trackedPass = true; //lastNetworkFailure = null; //context.JK.GA.trackNetworkTest(context.JK.detectOS(), testSummary.final.num_clients); } context.jamClient.SetVideoNetworkTestScore(attempt.num_clients); if (!testSummary.video_final.num_clients) { $testScoreVideo.addClass('acceptable'); } else if (testSummary.video_final.num_clients >= 2) { $testScoreVideo.addClass('good'); } else { $testScoreVideo.addClass('acceptable'); } success = true; } else if (reason == "minimum_client_threshold") { context.jamClient.SetVideoNetworkTestScore(testSummary.video_final.num_clients - 1); renderStopTestVideo(testSummary.video_final.num_clients - 1, reason) //storeLastNetworkFailure(context.JK.GA.NetworkTestFailReasons.bandwidth, avgBandwidth(attempt.num_clients - 1)); } else if (reason == "unreachable" || reason == "no-transmit") { context.jamClient.SetVideoNetworkTestScore(testSummary.video_final.num_clients - 1); // https://jamkazam.atlassian.net/browse/VRFS-2323 renderStopTestVideo(testSummary.video_final.num_clients - 1, reason); // storeLastNetworkFailure(context.JK.GA.NetworkTestFailReasons.stun, attempt.num_clients); } else if (reason == "internal_error") { context.JK.alertSupportedNeeded("The JamKazam client software had an unexpected problem while scoring your Internet connection."); renderStopTestVideo(testSummary.video_final.num_clients - 1, reason); //storeLastNetworkFailure(context.JK.GA.NetworkTestFailReasons.jamerror); } else if (reason == "remote_peer_cant_test") { context.JK.alertSupportedNeeded("The JamKazam service is experiencing technical difficulties."); renderStopTestVideo(testSummary.video_final.num_clients - 1, reason); //storeLastNetworkFailure(context.JK.GA.NetworkTestFailReasons.jamerror); } else if (reason == "server_comm_timeout") { //gearUtils.skipNetworkTest(); context.JK.alertSupportedNeeded("Communication with the JamKazam network service has timed out."); renderStopTestVideo(testSummary.video_final.num_clients - 1, reason); //storeLastNetworkFailure(context.JK.GA.NetworkTestFailReasons.jamerror); } else if (reason == 'backend_gone') { context.JK.alertSupportedNeeded("The JamKazam client is experiencing technical difficulties."); renderStopTestVideo(testSummary.video_final.num_clients - 1, reason); //storeLastNetworkFailure(context.JK.GA.NetworkTestFailReasons.jamerror); } else if (reason == "invalid_response") { //gearUtils.skipNetworkTest(); context.JK.alertSupportedNeeded("The JamKazam client software had an unexpected problem while scoring your Internet connection.

Reason: " + attempt.backend_data.reason + '.'); renderStopTestVideo(testSummary.video_final.num_clients - 1, reason); //storeLastNetworkFailure(context.JK.GA.NetworkTestFailReasons.jamerror); } else if (reason == 'no_servers') { // gearUtils.skipNetworkTest(); context.JK.Banner.showAlert("No network test servers are available."); renderStopTestVideo(null, reason); //testedSuccessfully = true; //storeLastNetworkFailure(context.JK.GA.NetworkTestFailReasons.jamerror); } else if (reason == 'no_network') { context.JK.Banner.showAlert("Please try again later. Your network appears down."); renderStopTestVideo(testSummary.video_final.num_clients - 1, reason); //storeLastNetworkFailure(context.JK.GA.NetworkTestFailReasons.noNetwork); } else if (reason == "rest_api_error") { //gearUtils.skipNetworkTest(); context.JK.alertSupportedNeeded("Unable to acquire a network test server."); //testedSuccessfully = true; renderStopTestVideo(null, reason); //storeLastNetworkFailure(context.JK.GA.NetworkTestFailReasons.jamerror); } else if (reason == "timeout") { //gearUtils.skipNetworkTest(); context.JK.alertSupportedNeeded("Communication with the JamKazam network service timed out."); //testedSuccessfully = true; renderStopTestVideo(testSummary.video_final.num_clients - 1, reason); //storeLastNetworkFailure(context.JK.GA.NetworkTestFailReasons.jamerror); } else { //gearUtils.skipNetworkTest(); context.JK.alertSupportedNeeded("The JamKazam client software had a logic error while scoring your Internet connection."); renderStopTestVideo(testSummary.video_final.num_clients - 1, reason); //storeLastNetworkFailure(context.JK.GA.NetworkTestFailReasons.jamerror); } numClientToTestVideo = STARTING_NUM_CLIENTS_VIDEO; videoScoring = false; configureStartButton(); postDiagnostic(); $self.triggerHandler(NETWORK_TEST_DONE) } function startVideoTest() { videoScoring = true; renderStartTestVideo(); setTimeout(attemptTestPassVideo, 500); } function getCurrentAttemptAudio() { return testSummary.audioAttempts[testSummary.audioAttempts.length - 1]; } function getCurrentAttemptVideo() { return testSummary.videoAttempts[testSummary.videoAttempts.length - 1]; } function isFirstAttemptAudio() { return testSummary.audioAttempts.length == 0 || testSummary.audioAttempts.length == 1; } function isFirstAttemptVideo() { return testSummary.videoAttempts.length == 0 || testSummary.videoAttempts.length == 1; } function hasGoneDown() { var goneDown = false; context._.each(testSummary.audioAttempts, function(attempt) { if(attempt.num_clients == STARTING_NUM_CLIENTS_AUDIO - 1) { goneDown = true return false; } }); return goneDown; } // is this a retry attempt? If so, how many times now has it been. // 0 = this is the 1st attempt // > 0 indicates the number of retries. function numRetryAttempts() { // starting at the end of the attempts array, see how many have the same session count, which is implicitely // indicative of a retry var i = 0; var testSessionSize = null; var numSameSizeTests = 0; for(i = testSummary.audioAttempts.length - 1; i >= 0; i--) { var attempt = testSummary.audioAttempts[i]; if(testSessionSize === null) { // this is the 1st loop through. just recording the testSessionSize testSessionSize = attempt.num_clients; } else { if(testSessionSize == attempt.num_clients) { numSameSizeTests++; } else { break; // different size session found, so we are digging back into non-retry territory. bail out } } } return numSameSizeTests; } function hasTooManyRetries() { return numRetryAttempts() >= RETRY_THRESHOLD; return false; } function primeTimedOut() { logger.warn("backend never completed priming pump phase"); audioScoring = false; primeDeferred.reject(); } function backendTimedOut() { if (audioScoring) { testSummary.final = {reason: 'backend_gone'} testFinishedAudio(); } else { testSummary.video_final = {reason: 'backend_gone'} testFinishedVideo(); } } function cancel() { } function clearBackendGuard() { if (backendGuardTimeout) { clearTimeout(backendGuardTimeout); backendGuardTimeout = null; } } // we know we are attempting a pass if the backend or prime guard timeout is not null function isAttemptingPass() { return backendGuardTimeout != null || primeGuardTimeout != null; } function clearPrimeGuard() { if (primeGuardTimeout) { clearTimeout(primeGuardTimeout); primeGuardTimeout = null; } } function setPrimeGuard() { clearPrimeGuard(); primeGuardTimeout = setTimeout(function () { clearPrimeGuard(); primeTimedOut() }, (PRIME_PUMP_TIME + 10) * 1000); } function setBackendGuard() { clearBackendGuard(); backendGuardTimeout = setTimeout(function () { clearBackendGuard(); backendTimedOut() }, (gon.ftue_network_test_duration + 5) * 1000); } function attemptTestPassAudio() { var attempt = {}; attempt.payload_size = AUDIO_PAYLOAD_SIZE; attempt.duration = gon.ftue_network_test_duration; attempt.test_type = 'PktTest400LowLatency'; attempt.num_clients = numClientToTestAudio; attempt.server_client_id = serverClientId; attempt.received_progress = false; testSummary.audioAttempts.push(attempt); $scoredClientsAudio.text(numClientToTestAudio); //context.jamClient.StopNetworkTest(''); $inProgressText.html("Simulating the network traffic of a " + numClientToTestAudio + "-person audio-only session. Video will be tested next."); updateProgress(0, false); setBackendGuard(); audioScoring = true; logger.debug("network test attempt: " + numClientToTestAudio + "-person audio session, 400 packets/s, " + AUDIO_PAYLOAD_SIZE + " byte payload") context.jamClient.TestNetworkPktBwRate(serverClientId, createSuccessCallbackName(false), createTimeoutCallbackName(false), NETWORK_TEST_TYPES.PktTest400LowLatency, gon.ftue_network_test_duration, numClientToTestAudio - 1, AUDIO_PAYLOAD_SIZE, gon.global.ftue_network_test_backend_retries); } function attemptTestPassVideo() { var attempt = {}; attempt.payload_size = VIDEO_PAYLOAD_SIZE; attempt.duration = gon.ftue_network_test_duration; attempt.test_type = 'PktTest400LowLatency'; attempt.num_clients = numClientToTestVideo; attempt.server_client_id = serverClientId; attempt.received_progress = false; testSummary.videoAttempts.push(attempt); $scoredClientsVideo.text(numClientToTestVideo); //context.jamClient.StopNetworkTest(''); $inProgressText.html("Simulating the network traffic of a " + numClientToTestVideo + "-person video-enabled session."); updateProgress(0, false); setBackendGuard(); logger.debug("network test attempt: " + numClientToTestVideo + "-person video session, 400 packets/s, " + VIDEO_PAYLOAD_SIZE + " byte payload") context.jamClient.TestNetworkPktBwRate(serverClientId, createSuccessCallbackName(false), createTimeoutCallbackName(false), NETWORK_TEST_TYPES.PktTest400LowLatency, gon.ftue_network_test_duration, numClientToTestVideo - 1, VIDEO_PAYLOAD_SIZE, gon.global.ftue_network_test_backend_retries); } // you have to score a little to 'prime' the logic to know whether it's on wireless or not function primePump() { audioScoring = true; primeDeferred = new $.Deferred(); setPrimeGuard(); context.jamClient.TestNetworkPktBwRate(serverClientId, createSuccessCallbackName(true), createTimeoutCallbackName(true), NETWORK_TEST_TYPES.PktTest400LowLatency, PRIME_PUMP_TIME, 2, AUDIO_PAYLOAD_SIZE, gon.global.ftue_network_test_backend_retries); return primeDeferred; } function cancelTest() { audioScoring = false; configureStartButton(); renderStopTestAudio(); $self.triggerHandler(NETWORK_TEST_CANCEL) } function postPumpRun() { // check if on Wifi 1st var isWireless = context.jamClient.IsMyNetworkWireless(); if (isWireless == -1) { logger.warn("unable to determine if the user is on wireless or not for network test. skipping prompt.") } if (isWireless == 1) { context.JK.Banner.showAlert({buttons: [ {name: 'CANCEL', click: function () { cancelTest(); }}, {name: 'RUN NETWORK TEST ANYWAY', click: function () { attemptTestPassAudio(); ; }} ], html: "

It appears that your computer is connected to your network using WiFi.

" + "

We strongly advise against running the JamKazam application on a WiFi connection. " + "We recommend using a wired Ethernet connection from your computer to your router. " + "A WiFi connection is likely to cause significant issues in both latency and audio quality.

"}) } else { attemptTestPassAudio(); } } function pauseForRecentScoresTime() { var lastScoreTimes = context.jamClient.GetLastLatencyTestTimes() console.log(lastScoreTimes) return 0; var noPause = 0; var longAgo = 1000000; var initiated = lastScoreTimes.initiatied; var requested = lastScoreTimes.requested; if(initiated === null || initiated === undefined) { logger.warn("lastScoreTimes.initiated is not set"); initiated = longAgo; } if(requested === null || requested === undefined) { logger.warn("lastScoreTimes.requested is not set"); requested = longAgo; } if(initiated == 0) { logger.debug("lastScoreTimes.initiated is zero"); initiated = longAgo; } if(requested == 0) { logger.debug("lastScoreTimes.requested is zero"); requested = longAgo; } if(initiated < 0) { logger.debug("lastScoreTimes.initiated is less than zero"); initiated = longAgo; } if(requested < 0) { logger.debug("lastScoreTimes.requested is less than zero"); requested = longAgo; } var mostRecentValue = initiated < requested ? initiated : requested; if(mostRecentValue > gon.globalftue_network_test_min_wait_since_last_score * 1000) { return noPause; // our last score was past our min wait; so no delay necessary } else { // pause for the remainder of the min wait threshold var remainder = gon.globalftue_network_test_min_wait_since_last_score * 1000 - mostRecentValue; if(remainder > 1500) { // we need to update the UI because this is a long time for a mystery pause $startNetworkTestBtn.text('SHORT QUIET PERIOD...') } return remainder; } } function prepareNetworkTest() { if (audioScoring || videoScoring) return false; setTimeout(function() { logger.info("starting network test"); resetTestState(); audioScoring = true; $self.triggerHandler(NETWORK_TEST_START); renderStartTestAudio(); rest.getLatencyTester() .done(function (response) { // ensure there are no tests ongoing serverClientId = response.client_id; testSummary.serverClientId = serverClientId; logger.info("beginning network test against client_id: " + serverClientId); primePump() .done(function () { postPumpRun(); }) .fail(function () { logger.debug("unable to determine user's network type. primePump failed.") context.JK.Banner.showAlert({ title: 'Unable to Determine Network Type', buttons: [ {name: 'CANCEL', click: function () { cancelTest(); }}, {name: 'RUN NETWORK TEST ANYWAY', click: function () { attemptTestPassAudio(); ; }} ], html: "

We are unable to determine if your computer is connected to your network using WiFi.

" + "

We strongly advise against running the JamKazam application on a WiFi connection. " + "We recommend using a wired Ethernet connection from your computer to your router. " + "A WiFi connection is likely to cause significant issues in both latency and audio quality.

"}) }); }) .fail(function (jqXHR) { if (jqXHR.status == 404) { // means there are no network testers available. // we have to skip this part of the UI testSummary.final = {reason: 'no_servers'} } else { if (context.JK.isNetworkError(arguments)) { testSummary.final = {reason: 'no_network'} } else { testSummary.final = {reason: 'rest_api_error'} } } testFinishedAudio(); }) }, pauseForRecentScoresTime()) return false; } function updateProgress(throughput, showSubscore) { var width = throughput * 100; $currentScore.stop().data('showSubscore', showSubscore); if (!showSubscore) { $subscore.text(''); } $currentScore.animate({ duration: 1000, width: width + '%' }, { step: function (now, fx) { if (showSubscore) { var newWidth = ( 100 * parseFloat($currentScore.css('width')) / parseFloat($currentScore.parent().css('width')) ); $subscore.text((Math.round(newWidth * 10) / 10) + '%'); } } }).css('overflow', 'visible'); ; } function primePumpTimeout(data) { if(!isAttemptingPass()) { logger.error("primePumpTimeout: already completed the test pass. indicates backend sent > 1 final event"); return; } clearPrimeGuard(); audioScoring = false; logger.debug("the prime pump routine timed out") primeDeferred.reject(); } function primePumpComplete(data) { if(!isAttemptingPass()) { logger.error("primePumpComplete: already completed the test pass. indicates backend sent > 1 final event"); return; } if (data.progress === true) { // waiting... logger.debug("pump prime progress report"); setPrimeGuard(); } else { clearPrimeGuard(); // we could check for errors, but it's confusing to do so. we just want to let the backend figure out if // the interface is wireless, or not setTimeout(function () { logger.debug("pump primed"); audioScoring = false; primeDeferred.resolve(); }, 500); // give backend room to breath for timing/race issues } } function networkTestCompleteAudio(data) { if(!isAttemptingPass()) { logger.error("networkTestComplete: already completed the test pass. indicates backend sent > 1 final event"); return; } var attempt = getCurrentAttemptAudio(); function refineTest(up) { if (up === null) { logger.debug("retrying test at size: " + numClientToTestAudio); setTimeout(attemptTestPassAudio, 500); // wait a second to avoid race conditions with client/server comm } else if (up) { if (numClientToTestAudio == gon.ftue_network_test_max_clients) { attempt.reason = "success"; testFinishedAudio(); } else if(hasGoneDown()) { // this means we've gone up before... so don't go back down (i.e., creating a loop) attempt.reason = "success"; testSummary.final = { reason: 'success', num_clients: numClientToTestAudio } testFinishedAudio(); } else { numClientToTestAudio++; logger.debug("increasing number of clients to " + numClientToTestAudio); setTimeout(attemptTestPassAudio, 500); // wait a second to avoid race conditions with client/server comm } } else { // reduce numclients if we can if (numClientToTestAudio == MINIMUM_ACCEPTABLE_SESSION_SIZE) { // we are too low already. fail the user attempt.reason = "minimum_client_threshold"; testFinishedAudio(); } else if (numClientToTestAudio > STARTING_NUM_CLIENTS_AUDIO) { // this means we've gone up before... so don't go back down (i.e., creating a loop) attempt.reason = "success"; testSummary.final = { reason: 'success', num_clients: numClientToTestAudio - 1 } testFinishedAudio(); } else { numClientToTestAudio--; logger.debug("reducing number of clients to " + numClientToTestAudio); setTimeout(attemptTestPassAudio, 500); // wait a second to avoid race conditions with client/server comm } } } attempt.backend_data = data; if (data.progress === true) { setBackendGuard(); var animate = true; if (data.downthroughput && data.upthroughput) { if (data.downthroughput > 0 || data.upthroughput > 0) { attempt.received_progress = true; animate = true; } if (attempt.received_progress) { // take the lower var throughput = data.downthroughput < data.upthroughput ? data.downthroughput : data.upthroughput; bandwidthSamples.push(data.upthroughput); updateProgress(throughput, true); } } } else { clearBackendGuard(); logger.debug("network test pass completed. data: ", data); if (data.reason == "unreachable") { logger.debug("network test: unreachable (STUN issue or similar)") attempt.reason = data.reason; testFinishedAudio(); } else if (data.reason == "no-transmit") { logger.debug("network test: no-transmit (STUN issue or similar)"); attempt.reason = data.reason; testFinishedAudio(); } else if (data.reason == "internal_error") { // oops logger.debug("network test: internal_error (client had a unexpected problem)"); attempt.reason = data.reason; testFinishedAudio(); } else if (data.reason == "remote_peer_cant_test") { // old client logger.debug("network test: remote_peer_cant_test (old client)") attempt.reason = data.reason; testFinishedAudio(); } else if (data.reason == "server_comm_timeout") { logger.debug("network test: server_comm_timeout (communication with server problem)") attempt.reason = data.reason; testFinishedAudio(); } else { if (!data.downthroughput || !data.upthroughput) { // we have to assume this is bad. just not a reason we know about in code logger.debug("network test: no test data (unknown issue? " + data.reason + ")") attempt.reason = "invalid_response"; testFinishedAudio(); } else { // success... but we still have to verify if this data is within threshold if (data.downthroughput < gon.ftue_packet_rate_treshold) { logger.debug("network test: downthroughput too low. downthroughput: " + data.downthroughput + ", threshold: " + gon.ftue_packet_rate_treshold); refineTest(false); } else if (data.upthroughput < gon.ftue_packet_rate_treshold) { logger.debug("network test: upthroughput too low. upthroughput: " + data.upthroughput + ", threshold: " + gon.ftue_packet_rate_treshold); refineTest(false); } else { // true success. we can accept this score logger.debug("network test: success") refineTest(true); } } } // VRFS-1742 // context.jamClient.StopNetworkTest(serverClientId); } } function networkTestCompleteVideo(data) { if(!isAttemptingPass()) { logger.error("networkTestCompleteVideo: already completed the test pass. indicates backend sent > 1 final event"); return; } var attempt = getCurrentAttemptVideo(); function refineTest(up) { if (up === null) { logger.debug("retrying video test at size: " + numClientToTestVideo); setTimeout(attemptTestPassVideo, 500); // wait a second to avoid race conditions with client/server comm } else if (up) { if (numClientToTestVideo == gon.ftue_network_test_max_clients) { attempt.reason = "success"; testFinishedVideo(); } else { numClientToTestVideo++; logger.debug("increasing number of clients to " + numClientToTestVideo); setTimeout(attemptTestPassVideo, 500); // wait a second to avoid race conditions with client/server comm } } else { attempt.reason = "success"; testSummary.video_final = { reason: 'success', num_clients: numClientToTestVideo - 1 } testFinishedVideo(); } } attempt.backend_data = data; if (data.progress === true) { setBackendGuard(); var animate = true; if (data.downthroughput && data.upthroughput) { if (data.downthroughput > 0 || data.upthroughput > 0) { attempt.received_progress = true; animate = true; } if (attempt.received_progress) { // take the lower var throughput = data.downthroughput < data.upthroughput ? data.downthroughput : data.upthroughput; bandwidthSamples.push(data.upthroughput); updateProgress(throughput, true); } } } else { clearBackendGuard(); logger.debug("network video test pass completed. data: ", data); if (data.reason == "unreachable") { logger.debug("video network test: unreachable (STUN issue or similar)") attempt.reason = data.reason; testFinishedVideo(); } else if (data.reason == "no-transmit") { logger.debug("video network test: no-transmit (STUN issue or similar)"); attempt.reason = data.reason; testFinishedVideo(); } else if (data.reason == "internal_error") { // oops logger.debug("video network test: internal_error (client had a unexpected problem)"); attempt.reason = data.reason; testFinishedVideo(); } else if (data.reason == "remote_peer_cant_test") { // old client logger.debug("video network test: remote_peer_cant_test (old client)") attempt.reason = data.reason; testFinishedVideo(); } else if (data.reason == "server_comm_timeout") { logger.debug("video network test: server_comm_timeout (communication with server problem)") attempt.reason = data.reason; testFinishedVideo(); } else { if (!data.downthroughput || !data.upthroughput) { // we have to assume this is bad. just not a reason we know about in code logger.debug("video network test: no test data (unknown issue? " + data.reason + ")") attempt.reason = "invalid_response"; testFinishedVideo(); } else { // success... but we still have to verify if this data is within threshold if (data.downthroughput < gon.ftue_packet_rate_treshold) { logger.debug("video network test: downthroughput too low. downthroughput: " + data.downthroughput + ", threshold: " + gon.ftue_packet_rate_treshold); refineTest(false); } else if (data.upthroughput < gon.ftue_packet_rate_treshold) { logger.debug("video network test: upthroughput too low. upthroughput: " + data.upthroughput + ", threshold: " + gon.ftue_packet_rate_treshold); refineTest(false); } else { // true success. we can accept this score logger.debug("vido network test: success") refineTest(true); } } } } } function networkTestTimeoutAudio(data) { if(!isAttemptingPass()) { logger.error("networkTestTimeout: already completed the audio test pass. indicates backend sent > 1 final event"); return; } clearBackendGuard(); logger.warn("network timeout when testing latency test: " + data); var attempt = getCurrentAttemptAudio(); attempt.reason = 'timeout'; attempt.backend_data = data; testFinishedAudio(); } function networkTestTimeoutVideo(data) { if(!isAttemptingPass()) { logger.error("networkTestTimeout: already completed the video test pass. indicates backend sent > 1 final event"); return; } clearBackendGuard(); logger.warn("network timeout when testing latency test: " + data); var attempt = getCurrentAttemptVideo(); attempt.reason = 'timeout'; attempt.backend_data = data; testFinishedVideo(); } function hasScoredNetworkSuccessfully() { return testedSuccessfully; } function configureStartButton() { if (audioScoring || videoScoring) { $startNetworkTestBtn.text('NETWORK TEST RUNNING...').removeClass('button-orange').addClass('button-grey') } else { $startNetworkTestBtn.text('START NETWORK TEST').removeClass('button-grey').addClass('button-orange'); } } function initializeNextButtonState() { $dialog.setNextState(hasScoredNetworkSuccessfully() && !audioScoring && !videoScoring); } function initializeBackButtonState() { $dialog.setBackState(!audioScoring && !videoScoring); } function beforeHide() { } function initializeVideoWatchButton() { if (operatingSystem == "Win32") { $watchVideo.attr('href', 'https://www.youtube.com/watch?v=rhAdCVuwhBc'); } else { $watchVideo.attr('href', 'https://www.youtube.com/watch?v=0r1py0AYJ4Y'); } } function initialize(_$step, _inGearWizard) { $step = _$step; inGearWizard = _inGearWizard; $startNetworkTestBtn = $step.find('.start-network-test'); $foreverNetworkTestBtn = $step.find('.forever-network-test') if ($startNetworkTestBtn.length == 0) throw 'no start network test button found in network-test' $testResults = $step.find('.network-test-results'); $testScoreAudio = $step.find('.network-test-score-audio'); $scoredClientsAudio = $testScoreAudio.find('.scored-clients'); $testScoreVideo = $step.find('.network-test-score-video'); $scoredClientsVideo = $testScoreVideo.find('.scored-clients'); $testText = $step.find('.network-test-text'); $inProgressText = $step.find('.in-progress') $audioResultText = $step.find('.audio-result') $videoResultText = $step.find('.video-result') $scoringBar = $step.find('.scoring-bar'); $goodMarker = $step.find('.good-marker'); $goodLine = $step.find('.good-line'); $currentScore = $step.find('.current-score'); $subscore = $step.find('.subscore'); $watchVideo = $step.find('.watch-video'); $container = $step.find('.network-test'); if(inGearWizard) { $container.attr('data-mode', 'gear-wizard') } else { $container.attr('data-mode', 'standalone') } $startNetworkTestBtn.on('click', function () { forever = false; prepareNetworkTest(); return false; }); if(context.JK.currentUserAdmin) { $foreverNetworkTestBtn.on('click', function() { forever = true; prepareNetworkTest(); return false; }).show(); } operatingSystem = context.JK.GetOSAsString(); initializeVideoWatchButton(); // if this network test is instantiated anywhere else than the gearWizard, or a dialog, then this will have to be expanded if (inGearWizard) { context.JK.HandleNetworkTestSuccessForPumpPrimingGW = function (data) { primePumpComplete(data) }; context.JK.HandleNetworkTestTimeoutForPumpPrimingGW = function (data) { primePumpTimeout(data) }; context.JK.HandleNetworkTestSuccessForGearWizard = function (data) { if(audioScoring) { networkTestCompleteAudio(data); } else if(videoScoring) { networkTestCompleteVideo(data); } else { logger.warn("unknown state in HandleNetworkTestSuccessForGearWizard"); } }; // pin to global for bridge callback context.JK.HandleNetworkTestTimeoutForGearWizard = function (data) { if(audioScoring) { networkTestTimeoutAudio(data); } else if(videoScoring) { networkTestTimeoutVideo(data); } }; // pin to global for bridge callback } else { context.JK.HandleNetworkTestSuccessForPumpPrimingDialog = function (data) { primePumpComplete(data) }; context.JK.HandleNetworkTestTimeoutForPumpPrimingDialog = function (data) { primePumpTimeout(data) }; context.JK.HandleNetworkTestSuccessForDialog = function (data) { if(audioScoring) { networkTestCompleteAudio(data); } else if(videoScoring) { networkTestCompleteVideo(data); } else { logger.warn("unknown state in HandleNetworkTestSuccessForDialog"); } }; // pin to global for bridge callback context.JK.HandleNetworkTestTimeoutForDialog = function (data) { if(audioScoring) { networkTestTimeoutAudio(data); } else if(videoScoring) { networkTestTimeoutVideo(data); } }; // pin to global for bridge callback } } this.isScoring = function () { return audioScoring || videoScoring; }; this.hasScoredNetworkSuccessfully = hasScoredNetworkSuccessfully; this.initialize = initialize; this.reset = reset; this.cancel = cancel; this.getLastNetworkFailure = getLastNetworkFailure; this.haltScoring = haltScoring; this.resumeScoring = resumeScoring; this.NETWORK_TEST_START = NETWORK_TEST_START; this.NETWORK_TEST_DONE = NETWORK_TEST_DONE; this.NETWORK_TEST_FAIL = NETWORK_TEST_FAIL; this.NETWORK_TEST_CANCEL = NETWORK_TEST_CANCEL; return this; } })(window, jQuery);