// CURRENT ISSUE: host is not being saved. or it may have not been saved, and my state is screwed up.
// if (given host not in hosts) hosts.append(given host);
var _host = "";
var hosts = [];
var pairingCert;
var myUniqueid;
var api;
var relaunchSourceEvent;
// Called by the common.js module.
function attachListeners() {
changeUiModeForNaClLoad();
$('#selectResolution').on('change', saveResolution);
$('#selectFramerate').on('change', saveFramerate);
$('#bitrateSlider').on('input', updateBitrateField); // input occurs every notch you slide
$('#bitrateSlider').on('change', saveBitrate); // change occurs once the mouse lets go.
$("#remoteAudioEnabledSwitch").on('click', saveRemoteAudio);
$('#hostChosen').on('click', hostChosen);
$('#addHostCell').on('click', addHost);
$('#cancelAddHost').on('click', cancelAddHost);
$('#continueAddHost').on('click', continueAddHost);
$('#forgetHost').on('click', forgetHost);
$('#cancelPairingDialog').on('click', pairingPopupCanceled);
$('#cancelQuitApp').on('click', cancelQuitApp);
$('#backIcon').on('click', showHostsAndSettingsMode);
$('#continueQuitApp').on('click', continueQuitApp);
$('#quitGameButton').on('click', stopGame);
$(window).resize(fullscreenNaclModule);
chrome.app.window.current().onMaximized.addListener(fullscreenChromeWindow);
}
function fullscreenChromeWindow() {
// when the user clicks the maximize button on the window,
// FIRST restore it to the previous size, then fullscreen it to the whole screen
// this prevents the previous window size from being 'maximized',
// and allows us to functionally retain two window sizes
// so that when the user hits `esc`, they go back to the "restored" size,
// instead of "maximized", which would immediately go to fullscreen
chrome.app.window.current().restore();
chrome.app.window.current().fullscreen();
}
function changeUiModeForNaClLoad() {
$("#main-content").children().not("#listener, #naclSpinner").hide();
$('#naclSpinnerMessage').text('Loading Moonlight plugin...');
$('#naclSpinner').css('display', 'inline-block');
}
function restoreUiAfterNaClLoad() {
$("#main-content").children().not("#listener, #naclSpinner, #gameSelection").show();
$('#naclSpinner').hide();
$('#loadingSpinner').css('display', 'none');
showHostsAndSettingsMode();
}
function snackbarLog(givenMessage) {
console.log(givenMessage);
var data = {
message: givenMessage,
timeout: 2000
};
document.querySelector('#snackbar').MaterialSnackbar.showSnackbar(data);
}
function updateBitrateField() {
$('#bitrateField').html($('#bitrateSlider').val() + " Mbps");
}
function moduleDidLoad() {
if(!myUniqueid) {
console.log("Failed to get uniqueId. Generating new one");
myUniqueid = uniqueid();
storeData('uniqueid', myUniqueid, null);
}
if(!pairingCert) { // we couldn't load a cert. Make one.
console.log("Failed to load local cert. Generating new one");
sendMessage('makeCert', []).then(function (cert) {
storeData('cert', cert, null);
pairingCert = cert;
console.log("Generated new cert.");
}, function (failedCert) {
console.log('ERROR: failed to generate new cert!');
console.log('Returned error was: ' + failedCert);
}).then(function (ret) {
sendMessage('httpInit', [pairingCert.cert, pairingCert.privateKey, myUniqueid]).then(function (ret) {
restoreUiAfterNaClLoad();
}, function (failedInit) {
console.log('ERROR: failed httpInit!');
console.log('Returned error was: ' + failedInit);
});
});
}
else {
sendMessage('httpInit', [pairingCert.cert, pairingCert.privateKey, myUniqueid]).then(function (ret) {
restoreUiAfterNaClLoad();
}, function (failedInit) {
console.log('ERROR: failed httpInit!');
console.log('Returned error was: ' + failedInit);
});
}
}
// pair to the given hostname or IP. Returns whether pairing was successful.
function pairTo(host, onSuccess, onFailure) {
if(!pairingCert) {
snackbarLog('ERROR: cert has not been generated yet. Is NaCl initialized?');
console.log("User wants to pair, and we still have no cert. Problem = very yes.");
onFailure();
return;
}
api = new NvHTTP(host, myUniqueid);
api.refreshServerInfo().then(function (ret) {
if (api.paired) {
onSuccess();
return;
}
var randomNumber = String("0000" + (Math.random()*10000|0)).slice(-4);
var pairingDialog = document.querySelector('#pairingDialog');
$('#pairingDialogText').html('Please enter the number ' + randomNumber + ' on the GFE dialog on the computer. This dialog will be dismissed once complete');
pairingDialog.showModal();
console.log('sending pairing request to ' + host + ' with random number ' + randomNumber);
api.pair(randomNumber).then(function (paired) {
if (!paired) {
if (api.currentGame != 0) {
$('#pairingDialogText').html('Error: ' + host + ' is in app. Cannot pair until the app is stopped.');
} else {
$('#pairingDialogText').html('Error: failed to pair with ' + host + '. failure reason unknown.');
}
onFailure();
return;
}
snackbarLog('Pairing successful');
pairingDialog.close();
onSuccess();
}, function (failedPairing) {
snackbarLog('Failed pairing to: ' + host);
console.log('pairing failed, and returned ' + failedPairing);
onFailure();
});
}, function (failedRefreshInfo) {
snackbarLog('Failed to connect to ' + host + '! Are you sure the host is on?');
console.log('Returned error was: ' + failedRefreshInfo);
});
}
function hostChosen(sourceEvent) {
if(sourceEvent && sourceEvent.srcElement) {
if (sourceEvent.srcElement.innerText == "") {
console.log('user clicked image. we gotta hack to parse out the host.');
host = sourceEvent.currentTarget.childNodes[1].textContent;
} else {
console.log('parsing host from grid element.');
host = sourceEvent.srcElement.innerText;
}
}
api = new NvHTTP(host, myUniqueid);
api.refreshServerInfo().then(function (ret) {
if(!api.paired) {
pairTo(host, function(){ showApps(); saveHosts(); }, function(){});
} else {
showApps();
}
}, function (failedRefreshInfo) {
snackbarLog('Failed to connect to ' + host + '! Are you sure the host is on?');
console.log('Returned error was: ' + failedRefreshInfo);
});
}
// the `+` was selected on the host grid.
// give the user a dialog to input connection details for the PC
function addHost() {
document.querySelector('#addHostDialog').showModal();
}
// user canceled the dialog for adding a new PC
function cancelAddHost() {
document.querySelector('#addHostDialog').close();
}
function addHostToGrid(host) {
var cell = document.createElement('div');
cell.className += 'mdl-cell mdl-cell--3-col host-cell mdl-button mdl-js-button mdl-js-ripple-effect';
cell.id = 'hostgrid-' + host;
cell.innerHTML = host;
$(cell).prepend($("", {src: "static/res/ic_desktop_windows_white_24px.svg"}));
$('#host-grid').append(cell);
cell.onclick = hostChosen;
if(hosts.indexOf(host) < 0) {
hosts.push(host);
}
}
function continueAddHost() {
var inputHost = $('#dialogInputHost').val();
pairTo(inputHost,
function() {
addHostToGrid(inputHost);
saveHosts();
document.querySelector('#addHostDialog').close();
},
function() {
snackbarLog('pairing to ' + inputHost + ' failed!');
});
}
// locally remove the hostname/ip from the saved `hosts` array.
// note: this does not make the host forget the pairing to us.
// this means we can re-add the host, and will still be paired.
// TODO: use the chrome context menu to add right-click support to remove the host in grid-ui
// https://github.com/GoogleChrome/chrome-app-samples/blob/master/samples/context-menu/main.js
function forgetHost() {
snackbarLog('Feature not yet ported to grid-ui');
hosts.splice(hosts.indexOf(host), 1); // remove the host from the array;
saveHosts();
}
function pairingPopupCanceled() {
document.querySelector('#pairingDialog').close();
}
// puts the CSS style for current app on the app that's currently running
// and puts the CSS style for non-current app apps that aren't running
// this requires a hot-off-the-host `api`, and the appId we're going to stylize
// the function was made like this so that we can remove duplicated code, but
// not do N*N stylizations of the box art, or make the code not flow very well
function stylizeBoxArt(freshApi, appIdToStylize) {
if (freshApi.currentGame === appIdToStylize){ // stylize the currently running game
// destylize it, if it has the not-current-game style
if ($('#game-'+ appIdToStylize).hasClass("not-current-game")) $('#game-'+ appIdToStylize).removeClass("not-current-game");
// add the current-game style
$('#game-'+ appIdToStylize).addClass("current-game");
} else {
// destylize it, if it has the current-game style
if ($('#game-'+ appIdToStylize).hasClass("current-game")) $('#game-'+ appIdToStylize).removeClass("current-game");
// add the not-current-game style
$('#game-'+ appIdToStylize).addClass('not-current-game');
}
}
// show the app list
function showApps() {
if(!api || !api.paired) { // safety checking. shouldn't happen.
console.log('Moved into showApps, but `api` did not initialize properly! Failing.');
return;
}
// if game grid is populated, empty it
$("#game-grid").empty();
api.getAppList().then(function (appList) {
appList.forEach(function (app) {
api.getBoxArt(app.id).then(function (resolvedPromise) {
// put the box art into the image holder
var imageBlob = new Blob([resolvedPromise], {type: "image/png"});
$("#game-grid").append($("