jam-cloud/web/app/assets/javascripts/webJamClient.js

897 lines
29 KiB
JavaScript

(function (context) {
"use strict";
context.JK = context.JK || {};
// Incremental web-client bridge shim.
//
// Phase 1: delegate to FakeJamClient so the install seam can be exercised in
// forced-native browser mode without changing behavior.
// Phase 2+: replace selected methods with real WebRTC / websocket-gateway /
// REST-backed implementations while preserving the jamClient API surface.
context.JK.WebJamClient = function (app, p2pMessageFactory) {
var logger = context.JK.logger || console;
var inner = new context.JK.FakeJamClient(app, p2pMessageFactory);
var self = this;
var state = {
app: app,
inSessionPage: false,
inSession: false,
currentSessionId: null,
sessionEventCallbackName: "",
sessionJoinLeaveCallbackName: "",
recordingCallbacks: null,
connectionStatusRefreshRateMs: 0,
localMedia: {
status: "idle", // idle | requesting | active | failed
stream: null,
error: null,
autoStartEnabled: false
},
controlState: {
master: {},
personal: {},
initialized: {
master: false,
personal: false
}
},
participants: {},
webrtc: {
enabled: false,
peerConnections: {},
localClientId: null
}
};
// TEMP/diagnostic recorder for manual capture runs. Disabled by default.
// Enable with either:
// localStorage['jk.webClient.recording'] = '1'
// or window.JK_WEB_CLIENT_RECORDING_ENABLED = true
var recorder = {
enabled: false,
events: [],
maxEvents: 5000
};
logger.info("*** Web JamClient shim initialized (delegating to FakeJamClient). ***");
logger.info("*** Web JamClient shim mode: delegated fake + incremental overrides ***");
// Copy all enumerable members from the fake client instance so existing code
// can call methods/properties directly on this object.
Object.keys(inner).forEach(function(key) {
var value = inner[key];
if (typeof value === "function") {
this[key] = value.bind(inner);
} else {
this[key] = value;
}
}, this);
function nowIso() {
return (new Date()).toISOString();
}
function readLocalStorageFlag(key) {
try {
if (!context.localStorage) { return false; }
return context.localStorage.getItem(key) === "1";
} catch (e) {
return false;
}
}
function recorderEnabledByDefault() {
return !!context.JK_WEB_CLIENT_RECORDING_ENABLED ||
readLocalStorageFlag("jk.webClient.recording") ||
!!(context.gon && context.gon.web_client_recording_enabled);
}
function safeSerialize(value) {
try {
return JSON.parse(JSON.stringify(value));
} catch (e) {
return "[unserializable]";
}
}
function pushRecord(event) {
if (!recorder.enabled) { return; }
event = event || {};
event.ts = event.ts || nowIso();
recorder.events.push(event);
if (recorder.events.length > recorder.maxEvents) {
recorder.events.shift();
}
}
function instrumentThenable(methodName, result) {
if (!result || typeof result.then !== "function") {
return result;
}
try {
result.then(function(resolved) {
pushRecord({
kind: "jamClientCallResolved",
method: methodName,
result: safeSerialize(resolved)
});
return resolved;
}, function(rejected) {
pushRecord({
kind: "jamClientCallRejected",
method: methodName,
error: safeSerialize(rejected)
});
return rejected;
});
} catch (e) {
pushRecord({
kind: "jamClientInstrumentationError",
method: methodName,
error: (e && e.message) ? e.message : String(e)
});
}
return result;
}
function invokeDelegate(methodName, argsLike) {
var fn = inner[methodName];
if (typeof fn !== "function") {
return undefined;
}
var args = Array.prototype.slice.call(argsLike || []);
pushRecord({
kind: "jamClientCall",
method: methodName,
args: safeSerialize(args)
});
try {
var result = fn.apply(inner, args);
if (!result || typeof result.then !== "function") {
pushRecord({
kind: "jamClientCallReturn",
method: methodName,
result: safeSerialize(result)
});
}
return instrumentThenable(methodName, result);
} catch (e) {
pushRecord({
kind: "jamClientCallThrow",
method: methodName,
error: (e && e.message) ? e.message : String(e)
});
throw e;
}
}
function shouldAutoStartLocalMedia() {
return !!(context.gon && context.gon.web_client_media_autostart) ||
!!context.JK_WEB_CLIENT_MEDIA_AUTOSTART ||
readLocalStorageFlag("jk.webClient.mediaAutoStart");
}
function startLocalMediaIfEnabled() {
if (!context.navigator || !context.navigator.mediaDevices || !context.navigator.mediaDevices.getUserMedia) {
state.localMedia.status = "failed";
state.localMedia.error = "getUserMedia_unavailable";
pushRecord({kind: "webClientMediaStatus", status: state.localMedia.status, error: state.localMedia.error});
return;
}
if (!shouldAutoStartLocalMedia()) {
state.localMedia.autoStartEnabled = false;
return;
}
if (state.localMedia.status === "requesting" || state.localMedia.status === "active") {
return;
}
state.localMedia.autoStartEnabled = true;
state.localMedia.status = "requesting";
state.localMedia.error = null;
pushRecord({kind: "webClientMediaStatus", status: state.localMedia.status});
context.navigator.mediaDevices.getUserMedia({audio: true, video: false})
.then(function(stream) {
state.localMedia.stream = stream;
state.localMedia.status = "active";
state.localMedia.error = null;
pushRecord({
kind: "webClientMediaStatus",
status: state.localMedia.status,
tracks: stream && stream.getAudioTracks ? stream.getAudioTracks().length : 0
});
})
.catch(function(err) {
state.localMedia.stream = null;
state.localMedia.status = "failed";
state.localMedia.error = (err && err.name) ? err.name : String(err);
pushRecord({
kind: "webClientMediaStatus",
status: state.localMedia.status,
error: state.localMedia.error
});
});
}
function stopLocalMedia() {
var stream = state.localMedia.stream;
if (stream && stream.getTracks) {
stream.getTracks().forEach(function(track) {
try {
track.stop();
} catch (e) {}
});
}
state.localMedia.stream = null;
if (state.localMedia.status === "active" || state.localMedia.status === "requesting") {
state.localMedia.status = "idle";
}
pushRecord({kind: "webClientMediaStatus", status: state.localMedia.status});
}
function ensureLocalMediaForWebRtc() {
if (!state.webrtc.enabled) { return; }
if (!context.navigator || !context.navigator.mediaDevices || !context.navigator.mediaDevices.getUserMedia) {
return;
}
if (state.localMedia.status === "requesting" || state.localMedia.status === "active") {
return;
}
state.localMedia.status = "requesting";
state.localMedia.error = null;
pushRecord({kind: "webClientMediaStatus", status: state.localMedia.status, reason: "webrtc"});
context.navigator.mediaDevices.getUserMedia({audio: true, video: false})
.then(function(stream) {
state.localMedia.stream = stream;
state.localMedia.status = "active";
state.localMedia.error = null;
pushRecord({
kind: "webClientMediaStatus",
status: state.localMedia.status,
reason: "webrtc",
tracks: stream && stream.getAudioTracks ? stream.getAudioTracks().length : 0
});
Object.keys(state.webrtc.peerConnections).forEach(function(clientId) {
var entry = state.webrtc.peerConnections[clientId];
if (entry && entry.pc) {
addLocalTracksToPeerConnection(entry.pc);
}
});
})
.catch(function(err) {
state.localMedia.stream = null;
state.localMedia.status = "failed";
state.localMedia.error = (err && err.name) ? err.name : String(err);
pushRecord({
kind: "webClientMediaStatus",
status: state.localMedia.status,
reason: "webrtc",
error: state.localMedia.error
});
});
}
function modeKey(isMasterOrPersonal) {
return isMasterOrPersonal ? "master" : "personal";
}
function deepClone(value) {
return safeSerialize(value);
}
function getJamServer() {
return context.JK && context.JK.JamServer ? context.JK.JamServer : null;
}
function getSelfClientId() {
if (state.webrtc.localClientId) { return state.webrtc.localClientId; }
if (state.app && state.app.clientId) { return state.app.clientId; }
var server = getJamServer();
if (server && server.clientID) { return server.clientID; }
return null;
}
function readWebRtcEnabledFlag() {
return !!context.JK_WEB_CLIENT_WEBRTC_ENABLED ||
readLocalStorageFlag("jk.webClient.webrtc") ||
!!(context.gon && context.gon.web_client_webrtc_enabled);
}
function ensureControlStateCache(isMasterOrPersonal) {
var key = modeKey(isMasterOrPersonal);
if (state.controlState.initialized[key]) {
return;
}
var delegateState = invokeDelegate("SessionGetAllControlState", [isMasterOrPersonal]);
var byId = {};
if (delegateState && delegateState.length) {
delegateState.forEach(function(mixer) {
if (!mixer || !mixer.id) { return; }
byId[mixer.id] = deepClone(mixer);
});
}
state.controlState[key] = byId;
state.controlState.initialized[key] = true;
}
var syncTracksTimer = null;
function scheduleTrackSync() {
if (!context.MixerActions || !context.MixerActions.syncTracks) { return; }
if (syncTracksTimer) {
context.clearTimeout(syncTracksTimer);
}
syncTracksTimer = context.setTimeout(function() {
syncTracksTimer = null;
try {
context.MixerActions.syncTracks();
pushRecord({kind: "webClientTrackSync", source: "SessionSetControlState"});
} catch (e) {
pushRecord({
kind: "webClientTrackSyncError",
error: (e && e.message) ? e.message : String(e)
});
}
}, 50);
}
function applyTrackVolumeToCache(mixerId, isMasterOrPersonal) {
ensureControlStateCache(isMasterOrPersonal);
var key = modeKey(isMasterOrPersonal);
var cache = state.controlState[key];
var existing = cache[mixerId] || {};
var trackVolume = context.trackVolumeObject || {};
var updated = deepClone(existing) || {};
updated.id = mixerId;
updated.master = !!isMasterOrPersonal;
updated.monitor = !isMasterOrPersonal;
if (trackVolume.clientID !== undefined) { updated.client_id = trackVolume.clientID; }
if (trackVolume.mute !== undefined) { updated.mute = !!trackVolume.mute; }
if (trackVolume.volL !== undefined) { updated.volume_left = trackVolume.volL; }
if (trackVolume.volR !== undefined) { updated.volume_right = trackVolume.volR; }
if (trackVolume.pan !== undefined) { updated.pan = trackVolume.pan; }
if (trackVolume.loop !== undefined) { updated.loop = !!trackVolume.loop; }
if (trackVolume.name !== undefined) { updated.name = trackVolume.name; }
if (trackVolume.record !== undefined) { updated.record = !!trackVolume.record; }
if (updated.range_low === undefined) { updated.range_low = -80; }
if (updated.range_high === undefined) { updated.range_high = 20; }
cache[mixerId] = updated;
return updated;
}
function updateLocalTrackMuteFromControlState(controlState) {
if (!controlState) { return; }
if (!state.localMedia.stream || !state.localMedia.stream.getAudioTracks) { return; }
var trackClientId = controlState.client_id || "";
var selfClientId = getSelfClientId();
var isSelfControl = !trackClientId || (selfClientId && trackClientId === selfClientId);
if (!isSelfControl) { return; }
var shouldMute = !!controlState.mute;
state.localMedia.stream.getAudioTracks().forEach(function(track) {
try {
track.enabled = !shouldMute;
} catch (e) {}
});
}
function getPeerConnectionCtor() {
return context.RTCPeerConnection || context.webkitRTCPeerConnection || context.mozRTCPeerConnection;
}
function sendWebRtcSignal(receiverClientId, payload) {
var server = getJamServer();
if (!server || typeof server.sendP2PMessage !== "function") {
pushRecord({kind: "webrtc.signalDropped", reason: "jamServerUnavailable", to: receiverClientId});
return;
}
var envelope = {
jk_webclient_webrtc: true,
session_id: state.currentSessionId,
from_client_id: getSelfClientId(),
payload: payload
};
try {
server.sendP2PMessage(receiverClientId, JSON.stringify(envelope));
pushRecord({
kind: "webrtc.signalSent",
to: receiverClientId,
signal_type: payload && payload.type ? payload.type : "candidate"
});
} catch (e) {
pushRecord({
kind: "webrtc.signalSendError",
to: receiverClientId,
error: (e && e.message) ? e.message : String(e)
});
}
}
function addLocalTracksToPeerConnection(peerConnection) {
if (!peerConnection || !state.localMedia.stream || !state.localMedia.stream.getTracks) {
return;
}
var stream = state.localMedia.stream;
stream.getTracks().forEach(function(track) {
try {
peerConnection.addTrack(track, stream);
} catch (e) {}
});
}
function closePeerConnection(clientId) {
var entry = state.webrtc.peerConnections[clientId];
if (!entry) { return; }
try {
if (entry.pc && typeof entry.pc.close === "function") {
entry.pc.close();
}
} catch (e) {}
delete state.webrtc.peerConnections[clientId];
pushRecord({kind: "webrtc.peerClosed", client_id: clientId});
}
function ensurePeerConnection(clientId) {
if (!state.webrtc.enabled || !clientId) { return null; }
if (state.webrtc.peerConnections[clientId]) {
return state.webrtc.peerConnections[clientId];
}
var RTCPeerConnectionCtor = getPeerConnectionCtor();
if (!RTCPeerConnectionCtor) {
pushRecord({kind: "webrtc.unavailable", reason: "RTCPeerConnection"});
return null;
}
var iceServers = (context.gon && context.gon.web_client_ice_servers) || [];
var peerConnection = null;
try {
peerConnection = new RTCPeerConnectionCtor({iceServers: iceServers});
} catch (e) {
pushRecord({
kind: "webrtc.peerCreateError",
client_id: clientId,
error: (e && e.message) ? e.message : String(e)
});
return null;
}
var entry = {
client_id: clientId,
pc: peerConnection
};
state.webrtc.peerConnections[clientId] = entry;
addLocalTracksToPeerConnection(peerConnection);
peerConnection.onicecandidate = function(evt) {
if (!evt || !evt.candidate) { return; }
sendWebRtcSignal(clientId, {
type: "candidate",
candidate: evt.candidate
});
};
peerConnection.onconnectionstatechange = function() {
pushRecord({
kind: "webrtc.connectionState",
client_id: clientId,
state: peerConnection.connectionState
});
};
peerConnection.ontrack = function(evt) {
entry.remoteStream = evt && evt.streams && evt.streams.length ? evt.streams[0] : null;
pushRecord({
kind: "webrtc.remoteTrack",
client_id: clientId,
has_stream: !!entry.remoteStream
});
};
pushRecord({kind: "webrtc.peerCreated", client_id: clientId});
return entry;
}
function maybeCreateOffer(clientId) {
var entry = ensurePeerConnection(clientId);
if (!entry || !entry.pc || typeof entry.pc.createOffer !== "function") { return; }
entry.pc.createOffer()
.then(function(offer) {
return entry.pc.setLocalDescription(offer).then(function() {
sendWebRtcSignal(clientId, {type: "offer", sdp: offer});
});
})
.catch(function(err) {
pushRecord({
kind: "webrtc.offerError",
client_id: clientId,
error: (err && err.message) ? err.message : String(err)
});
});
}
function onWebRtcSignal(fromClientId, signalPayload) {
if (!state.webrtc.enabled || !signalPayload || !fromClientId) { return false; }
var entry = ensurePeerConnection(fromClientId);
if (!entry || !entry.pc) { return true; }
var pc = entry.pc;
if (signalPayload.type === "offer") {
pc.setRemoteDescription(signalPayload.sdp)
.then(function() {
return pc.createAnswer();
})
.then(function(answer) {
return pc.setLocalDescription(answer).then(function() {
sendWebRtcSignal(fromClientId, {type: "answer", sdp: answer});
});
})
.catch(function(err) {
pushRecord({
kind: "webrtc.answerError",
client_id: fromClientId,
error: (err && err.message) ? err.message : String(err)
});
});
return true;
}
if (signalPayload.type === "answer") {
pc.setRemoteDescription(signalPayload.sdp).catch(function(err) {
pushRecord({
kind: "webrtc.setAnswerError",
client_id: fromClientId,
error: (err && err.message) ? err.message : String(err)
});
});
return true;
}
if (signalPayload.type === "candidate") {
if (signalPayload.candidate && typeof pc.addIceCandidate === "function") {
pc.addIceCandidate(signalPayload.candidate).catch(function(err) {
pushRecord({
kind: "webrtc.addCandidateError",
client_id: fromClientId,
error: (err && err.message) ? err.message : String(err)
});
});
}
return true;
}
return false;
}
function installInstrumentationWrappers(target) {
Object.keys(target).forEach(function(key) {
var original = target[key];
if (typeof original !== "function") { return; }
if (key.indexOf("__") === 0) { return; }
if (key === "WebClientRecorderEnable" || key === "WebClientRecorderDisable" ||
key === "WebClientRecorderClear" || key === "WebClientRecorderGetLog" ||
key === "GetWebClientDebugState" || key === "IsWebClient") {
return;
}
if (key === "RegisterSessionJoinLeaveRequestCallBack" ||
key === "RegisterRecordingCallbacks" ||
key === "SessionRegisterCallback" ||
key === "SessionSetConnectionStatusRefreshRate" ||
key === "SessionPageEnter" ||
key === "SessionPageLeave" ||
key === "JoinSession" ||
key === "LeaveSession") {
return;
}
if (original.__jkWebClientInstrumented) { return; }
target[key] = function() {
var args = Array.prototype.slice.call(arguments);
pushRecord({
kind: "jamClientCall",
method: key,
args: safeSerialize(args)
});
try {
var result = original.apply(target, args);
if (!result || typeof result.then !== "function") {
pushRecord({
kind: "jamClientCallReturn",
method: key,
result: safeSerialize(result)
});
}
return instrumentThenable(key, result);
} catch (e) {
pushRecord({
kind: "jamClientCallThrow",
method: key,
error: (e && e.message) ? e.message : String(e)
});
throw e;
}
};
target[key].__jkWebClientInstrumented = true;
});
}
// Explicit markers for new mode detection/instrumentation.
this.IsWebClient = function() {
return true;
};
// Conservative behavior for now:
// keep reporting "not native" until the web shim is ready to satisfy more
// native-only expectations. `gon.isNativeClient` remains the feature-mode flag.
this.IsNativeClient = function() {
return false;
};
this.GetWebClientDebugState = function() {
return {
mode: "delegated-fake",
state: {
inSessionPage: state.inSessionPage,
inSession: state.inSession,
currentSessionId: state.currentSessionId,
sessionEventCallbackName: state.sessionEventCallbackName,
sessionJoinLeaveCallbackName: state.sessionJoinLeaveCallbackName,
connectionStatusRefreshRateMs: state.connectionStatusRefreshRateMs,
localMedia: {
status: state.localMedia.status,
error: state.localMedia.error,
hasStream: !!state.localMedia.stream,
autoStartEnabled: !!state.localMedia.autoStartEnabled
},
webrtc: {
enabled: !!state.webrtc.enabled,
localClientId: getSelfClientId(),
peerCount: Object.keys(state.webrtc.peerConnections).length
}
},
recorder: {
enabled: recorder.enabled,
eventCount: recorder.events.length
}
};
};
// ---- Recorder controls (TEMP / manual validation support) ----
this.WebClientRecorderEnable = function() {
recorder.enabled = true;
pushRecord({kind: "webClientRecorder", action: "enable"});
return true;
};
this.WebClientRecorderDisable = function() {
pushRecord({kind: "webClientRecorder", action: "disable"});
recorder.enabled = false;
return true;
};
this.WebClientRecorderClear = function() {
recorder.events = [];
return true;
};
this.WebClientRecorderGetLog = function() {
return recorder.events.slice();
};
// ---- Incremental method overrides (session lifecycle / callback bookkeeping) ----
this.RegisterSessionJoinLeaveRequestCallBack = function(callbackName) {
state.sessionJoinLeaveCallbackName = callbackName || "";
return invokeDelegate("RegisterSessionJoinLeaveRequestCallBack", arguments);
};
this.RegisterRecordingCallbacks = function() {
state.recordingCallbacks = Array.prototype.slice.call(arguments);
return invokeDelegate("RegisterRecordingCallbacks", arguments);
};
this.SessionRegisterCallback = function(callbackName) {
state.sessionEventCallbackName = callbackName || "";
return invokeDelegate("SessionRegisterCallback", arguments);
};
this.SessionSetConnectionStatusRefreshRate = function(milliseconds) {
state.connectionStatusRefreshRateMs = milliseconds || 0;
return invokeDelegate("SessionSetConnectionStatusRefreshRate", arguments);
};
this.SessionPageEnter = function() {
state.inSessionPage = true;
pushRecord({kind: "webClientSessionState", event: "SessionPageEnter", state: safeSerialize(this.GetWebClientDebugState().state)});
startLocalMediaIfEnabled();
return invokeDelegate("SessionPageEnter", arguments);
};
this.SessionPageLeave = function() {
state.inSessionPage = false;
pushRecord({kind: "webClientSessionState", event: "SessionPageLeave", state: safeSerialize(this.GetWebClientDebugState().state)});
if (!state.inSession) {
stopLocalMedia();
}
return invokeDelegate("SessionPageLeave", arguments);
};
this.JoinSession = function(sessionDescriptor) {
state.inSession = true;
if (sessionDescriptor && typeof sessionDescriptor === "object") {
state.currentSessionId = sessionDescriptor.sessionID || state.currentSessionId;
}
pushRecord({kind: "webClientSessionState", event: "JoinSession", session: safeSerialize(sessionDescriptor)});
startLocalMediaIfEnabled();
if (state.webrtc.enabled) {
ensureLocalMediaForWebRtc();
}
return invokeDelegate("JoinSession", arguments);
};
this.LeaveSession = function(sessionDescriptor) {
state.inSession = false;
state.currentSessionId = null;
pushRecord({kind: "webClientSessionState", event: "LeaveSession", session: safeSerialize(sessionDescriptor)});
Object.keys(state.webrtc.peerConnections).forEach(function(clientId) {
closePeerConnection(clientId);
});
if (!state.inSessionPage) {
stopLocalMedia();
}
return invokeDelegate("LeaveSession", arguments);
};
this.SessionGetAllControlState = function(isMasterOrPersonal) {
ensureControlStateCache(isMasterOrPersonal);
var key = modeKey(isMasterOrPersonal);
var values = Object.keys(state.controlState[key]).map(function(mixerId) {
return deepClone(state.controlState[key][mixerId]);
});
pushRecord({
kind: "webClientControlStateRead",
mode: key,
count: values.length
});
return values;
};
this.SessionSetControlState = function(mixerId, isMasterOrPersonal) {
var updated = applyTrackVolumeToCache(mixerId, isMasterOrPersonal);
updateLocalTrackMuteFromControlState(updated);
if (context.trackVolumeObject && context.trackVolumeObject.broadcast) {
scheduleTrackSync();
}
pushRecord({
kind: "webClientControlStateSet",
mixer_id: mixerId,
mode: modeKey(isMasterOrPersonal),
state: deepClone(updated),
broadcast: !!(context.trackVolumeObject && context.trackVolumeObject.broadcast)
});
return invokeDelegate("SessionSetControlState", arguments);
};
this.UpdateSessionInfo = function(sessionInfo) {
if (sessionInfo && sessionInfo.id) {
state.currentSessionId = sessionInfo.id;
}
if (sessionInfo && sessionInfo.participants && sessionInfo.participants.length) {
var nextParticipants = {};
sessionInfo.participants.forEach(function(participant) {
if (!participant || !participant.client_id) { return; }
nextParticipants[participant.client_id] = deepClone(participant);
});
state.participants = nextParticipants;
}
return invokeDelegate("UpdateSessionInfo", arguments);
};
this.ParticipantJoined = function(session, participant) {
if (participant && participant.client_id) {
state.participants[participant.client_id] = deepClone(participant);
if (state.webrtc.enabled) {
var selfClientId = getSelfClientId();
if (participant.client_id !== selfClientId) {
maybeCreateOffer(participant.client_id);
}
}
}
return invokeDelegate("ParticipantJoined", arguments);
};
this.ParticipantLeft = function(session, participant) {
if (participant && participant.client_id) {
delete state.participants[participant.client_id];
closePeerConnection(participant.client_id);
}
return invokeDelegate("ParticipantLeft", arguments);
};
this.ClientJoinedSession = function(sourceUserId, clientId, sessionId) {
if (clientId) {
state.participants[clientId] = state.participants[clientId] || {client_id: clientId};
if (state.webrtc.enabled) {
var selfClientId = getSelfClientId();
if (clientId !== selfClientId) {
maybeCreateOffer(clientId);
}
}
}
return invokeDelegate("ClientJoinedSession", arguments);
};
this.ClientLeftSession = function(sourceUserId, clientId, sessionId) {
if (clientId) {
delete state.participants[clientId];
closePeerConnection(clientId);
}
return invokeDelegate("ClientLeftSession", arguments);
};
this.P2PMessageReceived = function(from, payload) {
var parsedPayload = payload;
if (typeof parsedPayload === "string") {
try {
parsedPayload = JSON.parse(parsedPayload);
} catch (e) {}
}
if (parsedPayload && parsedPayload.jk_webclient_webrtc && parsedPayload.payload) {
onWebRtcSignal(from, parsedPayload.payload);
}
return invokeDelegate("P2PMessageReceived", [from, payload]);
};
// Keep a handle to the delegate for incremental replacement/testing.
this.__delegate = inner;
this.__state = state;
this.__recorder = recorder;
state.webrtc.enabled = readWebRtcEnabledFlag();
if (state.webrtc.enabled) {
pushRecord({kind: "webrtc.enabled"});
}
recorder.enabled = recorderEnabledByDefault();
if (recorder.enabled) {
pushRecord({kind: "webClientRecorder", action: "auto-enable"});
}
installInstrumentationWrappers(this);
};
})(window);