(function (context, $) { "use strict"; context.JK = context.JK || {}; context.JK.VoiceChatHelper = function (app) { var logger = context.JK.logger; var ALERT_NAMES = context.JK.ALERT_NAMES; var ASSIGNMENT = context.JK.ASSIGNMENT; var VOICE_CHAT = context.JK.VOICE_CHAT; var MAX_TRACKS = context.JK.MAX_TRACKS; var MAX_OUTPUTS = context.JK.MAX_OUTPUTS; var gearUtils = context.JK.GearUtils; var $parent = null; var $reuseAudioInputRadio = null; var $useChatInputRadio = null; var $chatInputs = null; var $templateChatInput = null; var $selectedChatInput = null;// should only be used if isChatEnabled = true var $voiceChatVuLeft = null; var $voiceChatVuRight = null; var $voiceChatFader = null; var saveImmediate = null; // if true, then every action by the user results in a save to the backend immediately, false means you have to call trySave to persist var uniqueCallbackName = null; // needed because iCheck fires iChecked event even when you programmatically change it, unlike when using .val(x) var ignoreICheckEvent = false; var lastSavedTime = new Date(); var vuOptions = null; var faderHeight = null; var startingState = null; var resettedOnInvalidDevice = false; // this is set to true when we clear chat, and set to false when the user interacts in anyway function defaultReuse() { suppressChange(function(){ $reuseAudioInputRadio.iCheck('check').attr('checked', 'checked'); $useChatInputRadio.removeAttr('checked'); }) } function isChatEnabled() { return $useChatInputRadio.is(':checked'); } function onInvalidAudioDevice(e, data) { logger.debug("voice_chat_helper: onInvalidAudioDevice") if(resettedOnInvalidDevice) { // we've already tried to clear the audio device, and the user hasn't interacted, but still we are getting this event // we can't keep taking action, so stop logger.error("voice_chat_helper: onInvalidAudioDevice: ignoring event because we have already tried to handle it"); return; } resettedOnInvalidDevice = true; $selectedChatInput = null; // you can't do this in the event callback; it hangs the app indefinitely, and somehow 'sticks' the mic input into bad state until reboot setTimeout(function() { context.jamClient.FTUEClearChatInput(); context.jamClient.TrackSetChatEnable(true); var result = context.jamClient.TrackSaveAssignments(); if(!result || result.length == 0) { context.JK.Banner.showAlert('It appears the selected chat input is not functioning. Please try another chat input.'); } else { context.JK.alertSupportedNeeded("Unable to unwind invalid chat input selection.") } }, 1); } function beforeShow() { userInteracted(); renderNoVolume(); context.JK.onBackendEvent(ALERT_NAMES.AUDIO_DEVICE_NOT_PRESENT, 'voice_chat_helper', onInvalidAudioDevice); registerVuCallbacks(); } function beforeHide() { context.JK.offBackendEvent(ALERT_NAMES.AUDIO_DEVICE_NOT_PRESENT, 'voice_chat_helper', onInvalidAudioDevice); jamClient.FTUERegisterVUCallbacks('', '', ''); } function userInteracted() { resettedOnInvalidDevice = false; } function reset(forceDisabledChat) { $selectedChatInput = null; if(!forceDisabledChat && context.jamClient.TrackGetChatEnable()) { enableChat(false); } else { disableChat(false); } $chatInputs.empty(); var chatInputs = gearUtils.getChatInputs(); context._.each(chatInputs, function(chatInput) { if(chatInput.assignment > 0) { return; } var chatChannelName = chatInput.name; var chatChannelId = chatInput.id; var isCurrentlyChat = chatInput.assignment == ASSIGNMENT.CHAT; var $chat = $(context._.template($templateChatInput.html(), {id: chatChannelId, name: chatChannelName}, { variable: 'data' })); var $chatInput = $chat.find('input'); if(isCurrentlyChat) { $selectedChatInput = $chatInput; $selectedChatInput.attr('checked', 'checked'); } //$chat.hide(); // we'll show it once it's styled with iCheck $chatInputs.append($chat); }); var $radioButtons = $chatInputs.find('input[name="chat-device"]'); context.JK.checkbox($radioButtons).on('ifChecked', function(e) { userInteracted(); var $input = $(e.currentTarget); $selectedChatInput = $input; // for use in handleNext if(saveImmediate) { var channelId = $input.attr('data-channel-id'); lastSavedTime = new Date(); context.jamClient.TrackSetChatInput(channelId); lastSavedTime = new Date(); //context.jamClient.TrackSetAssignment(channelId, true, ASSIGNMENT.CHAT); var result = context.jamClient.TrackSaveAssignments(); if(!result || result.length == 0) { // success } else { context.jamClient.FTUEClearChatInput(); context.jamClient.TrackSetChatEnable(true); var result = context.jamClient.TrackSaveAssignments(); if(!result || result.length == 0) { context.JK.Banner.showAlert('Unable to save chat selection. ' + result); } else { context.JK.alertSupportedNeeded("Unable to unwind invalid chat selection.") } return false; } } }); if(!isChatEnabled()) { disableChatButtonsUI(); } renderVolumes(); startingState = getCurrentState(); } function disableChatButtonsUI() { var $radioButtons = $chatInputs.find('input[name="chat-device"]'); $radioButtons.iCheck('disable') $chatInputs.addClass('disabled'); $radioButtons.iCheck('uncheck'); $selectedChatInput = null; } function enableChatButtonsUI() { var $radioButtons = $chatInputs.find('input[name="chat-device"]'); $radioButtons.iCheck('enable') $chatInputs.removeClass('disabled'); } function suppressChange(proc) { ignoreICheckEvent = true; try { proc(); } finally { ignoreICheckEvent = false; } } function disableChat(applyToBackend) { if(saveImmediate && applyToBackend) { logger.debug("voiceChatHelper: disabling chat to backend"); var state = getCurrentState(); //context.jamClient.TrackSetChatEnable(false); //if(state.chat_channel) { // context.jamClient.TrackSetAssignment(state.chat_channel, true, ASSIGNMENT.UNASSIGNED); //} context.jamClient.FTUEClearChatInput(); var result = context.jamClient.TrackSaveAssignments(); if(!result || result.length == 0) { renderNoVolume(); // success suppressChange(function() { $reuseAudioInputRadio.iCheck('check').attr('checked', 'checked'); $useChatInputRadio.removeAttr('checked'); }) } else { context.JK.Banner.showAlert('Unable to disable chat. ' + result); return false; } } else { logger.debug("voiceChatHelper: disabling chat UI only"); suppressChange(function() { $reuseAudioInputRadio.iCheck('check').attr('checked', 'checked'); $useChatInputRadio.removeAttr('checked'); }) } disableChatButtonsUI(); } function enableChat(applyToBackend) { if(saveImmediate && applyToBackend) { logger.debug("voiceChatHelper: enabling chat to backend"); context.jamClient.TrackSetChatEnable(true); var result = context.jamClient.TrackSaveAssignments(); if(!result || result.length == 0) { // success suppressChange(function() { $useChatInputRadio.iCheck('check').attr('checked', 'checked'); $reuseAudioInputRadio.removeAttr('checked'); }) } else { context.JK.Banner.showAlert('Unable to enable chat. ' + result); return false; } } else { logger.debug("voiceChatHelper: enabling chat UI only"); suppressChange(function() { $useChatInputRadio.iCheck('check').attr('checked', 'checked'); $reuseAudioInputRadio.removeAttr('checked'); }) } enableChatButtonsUI(); renderVolumes(); } function handleChatEnabledToggle() { context.JK.checkbox($reuseAudioInputRadio); context.JK.checkbox($useChatInputRadio); // plugin sets to relative on the element; have to do this as an override $reuseAudioInputRadio.closest('.iradio_minimal').css('position', 'absolute'); $useChatInputRadio.closest('.iradio_minimal').css('position', 'absolute'); $reuseAudioInputRadio.on('ifChecked', function() { if(!ignoreICheckEvent) { userInteracted(); disableChat(true); } }); $useChatInputRadio.on('ifChecked', function() { if(!ignoreICheckEvent) { userInteracted(); enableChat(true) } }); } // gets the state of the UI function getCurrentState() { var state = { enabled:null, chat_channel:null }; state.enabled = $useChatInputRadio.is(':checked'); state.chat_channel = $selectedChatInput && $selectedChatInput.attr('data-channel-id'); logger.debug("desired chat state: enabled=" + state.enabled + ", chat_channel=" + state.chat_channel) return state; } function cancel() { logger.debug("canceling voice chat state"); return trySave(startingState); } function trySave(state) { if(!state) { state = getCurrentState(); } if(state.enabled && state.chat_channel) { logger.debug("enabling chat. chat_channel=" + state.chat_channel); context.jamClient.TrackSetChatEnable(true); context.jamClient.FTUESetChatInput(state.chat_channel); //context.jamClient.TrackSetAssignment(state.chat_channel, true, ASSIGNMENT.CHAT); } else { logger.debug("disabling chat."); context.jamClient.FTUEClearChatInput(); //context.jamClient.TrackSetChatEnable(false); //if(state.chat_channel) { //context.jamClient.TrackSetAssignment(state.chat_channel, true, ASSIGNMENT.UNASSIGNED); //} } var result = context.jamClient.TrackSaveAssignments(); if(!result || result.length == 0) { // success if(!state.enabled) { renderNoVolume(); } return true; } else { context.JK.Banner.showAlert('Unable to save chat assignments. ' + result); return false; } } function initializeVUMeters() { context.JK.VuHelpers.renderVU($voiceChatVuLeft, vuOptions); context.JK.VuHelpers.renderVU($voiceChatVuRight, vuOptions); context.JK.FaderHelpers.renderFader($voiceChatFader, {faderId: '', faderType: "vertical", height: faderHeight}); $voiceChatFader.on('fader_change', faderChange); } // renders volumes based on what the backend says function renderVolumes() { var $fader = $voiceChatFader.find('[data-control="fader"]'); var db = context.jamClient.FTUEGetChatInputVolume(); var faderPct = db + 80; context.JK.FaderHelpers.setHandlePosition($fader, faderPct); } function renderNoVolume() { var $fader = $voiceChatFader.find('[data-control="fader"]'); context.JK.FaderHelpers.setHandlePosition($fader, 50); context.JK.VuHelpers.updateVU($voiceChatVuLeft, 0); context.JK.VuHelpers.updateVU($voiceChatVuRight, 0); } function faderChange(e, data) { // TODO - using hardcoded range of -80 to 20 for output levels. var mixerLevel = data.percentage - 80; // Convert our [0-100] to [-80 - +20] range context.jamClient.FTUESetChatInputVolume(mixerLevel); } function registerVuCallbacks() { logger.debug("voice-chat-helper: registering vu callbacks"); jamClient.FTUERegisterVUCallbacks( "JK.voiceChatHelperAudioOutputVUCallback", "JK.voiceChatHelperAudioInputVUCallback", "JK." + uniqueCallbackName ); jamClient.SetVURefreshRate(200); } function initialize(_$step, caller, _saveImmediate, _vuOptions, _faderHeight) { $parent = _$step; saveImmediate = _saveImmediate; vuOptions = _vuOptions; faderHeight = _faderHeight; $reuseAudioInputRadio = $parent.find('.reuse-audio-input input'); $useChatInputRadio = $parent.find('.use-chat-input input'); $chatInputs = $parent.find('.chat-inputs'); $templateChatInput = $('#template-chat-input'); $voiceChatVuLeft = $parent.find('.voice-chat-vu-left'); $voiceChatVuRight = $parent.find('.voice-chat-vu-right'); $voiceChatFader = $parent.find('.chat-fader') handleChatEnabledToggle(); initializeVUMeters(); renderVolumes(); uniqueCallbackName = 'voiceChatHelperChatInputVUCallback' + caller; context.JK[uniqueCallbackName] = function(dbValue, leftClip, rightClip) { context.JK.ftueVUCallback(dbValue, $voiceChatVuLeft); context.JK.ftueVUCallback(dbValue, $voiceChatVuRight); } } context.JK.voiceChatHelperAudioInputVUCallback = function (dbValue) {}; context.JK.voiceChatHelperAudioOutputVUCallback = function (dbValue) {}; this.reset = reset; this.trySave = trySave; this.cancel = cancel; this.initialize = initialize; this.beforeShow = beforeShow; this.beforeHide = beforeHide; return this; }; })(window, jQuery);