import jk js scripts in to reactjs app for interacting with websocket messages
This commit is contained in:
parent
13175f64f1
commit
4c69f76e42
|
|
@ -5,11 +5,12 @@
|
|||
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#2c7be5" />
|
||||
|
||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://fonts.googleapis.com/css?family=Open+Sans:300,400,600,700|Poppins:100,200,300,400,500,600,700,800,900&display=swap"
|
||||
/>
|
||||
/>
|
||||
<title>JamKazam</title>
|
||||
</head>
|
||||
|
||||
|
|
|
|||
|
|
@ -6,13 +6,48 @@ import 'react-toastify/dist/ReactToastify.min.css';
|
|||
import 'react-datetime/css/react-datetime.css';
|
||||
import 'react-image-lightbox/style.css';
|
||||
|
||||
import useImportScript from "./hooks/useImportScript";
|
||||
import useScript from './hooks/useScript';
|
||||
|
||||
const App = () => {
|
||||
|
||||
useImportScript(`${process.env.REACT_APP_LEGACY_BASE_URL}/client_scripts`)
|
||||
function initJKScripts() {
|
||||
const app = window.JK.JamKazam();
|
||||
const jamServer = new window.JK.JamServer(app, function(event_type) {
|
||||
console.log('---EVENT_TYPE---', event_type);
|
||||
//return app.activeElementEvent(event_type)
|
||||
});
|
||||
jamServer.initialize();
|
||||
window.JK.initJamClient(app);
|
||||
|
||||
return (
|
||||
const clientInit = new window.JK.ClientInit();
|
||||
clientInit.init();
|
||||
|
||||
window.JK.JamServer.connect() // singleton here defined in JamServer.js
|
||||
.done(function() {
|
||||
console.log('Jamserver connected');
|
||||
//_initAfterConnect(true);
|
||||
})
|
||||
.fail(function() {
|
||||
console.log('Jamserver connection error');
|
||||
//_initAfterConnect(false);
|
||||
});
|
||||
|
||||
registerTextMessageCallback();
|
||||
|
||||
}
|
||||
|
||||
function registerTextMessageCallback(){
|
||||
window.JK.JamServer.registerMessageCallback(window.JK.MessageType.TEXT_MESSAGE, function(header, payload) {
|
||||
console.log('Handling CHAT_MESSAGE msg ' + JSON.stringify(payload));
|
||||
// chatMessageReceived(payload);
|
||||
// context.ChatActions.msgReceived(payload);
|
||||
// handledNotification(payload);
|
||||
});
|
||||
}
|
||||
|
||||
useScript(`${process.env.REACT_APP_LEGACY_BASE_URL}/client_scripts`, initJKScripts);
|
||||
|
||||
return (
|
||||
<Router basename={process.env.PUBLIC_URL}>
|
||||
<Layout />
|
||||
</Router>
|
||||
|
|
|
|||
|
|
@ -47,7 +47,6 @@ const JKMessageModal = props => {
|
|||
}, [show]);
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
<Modal isOpen={show} toggle={toggle}>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,106 @@
|
|||
// Inspiration from: https://www.fullstackreact.com/articles/Declaratively_loading_JS_libraries/index.html
|
||||
// ================================================================================
|
||||
// Summary: a handy class for dynamically loading and using async JS libs in a ReactJS app
|
||||
// ================================================================================
|
||||
//
|
||||
// Usage:
|
||||
// 1. create a `ScriptCache` instance:
|
||||
// const scriptCache = new ScriptCache(["http://remote.cdn.com/myLibrary.min.js", "http://..."]);
|
||||
// 2. pass any functions that depend on window globals (from your script) into `scriptCache.onLoad`
|
||||
// ================================================================================
|
||||
|
||||
export default class ScriptCache {
|
||||
|
||||
static SCRIPT_STATUS = {
|
||||
COMPLETE: "complete",
|
||||
ERROR: "error"
|
||||
}
|
||||
|
||||
constructor(scripts) {
|
||||
this.loaded = [];
|
||||
this.failed = [];
|
||||
this.pending = [];
|
||||
this.load(scripts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this block to run any additional setup
|
||||
* when the scripts have loaded (or failed)
|
||||
*/
|
||||
onLoad(onSuccess, onReject) {
|
||||
if (onReject) onReject(this.failed);
|
||||
if (onSuccess) onSuccess(this.loaded);
|
||||
}
|
||||
|
||||
/**
|
||||
* This will loop through and load any scripts
|
||||
* passed into the class constructor
|
||||
*/
|
||||
load(scripts = []) {
|
||||
if (!scripts.length) return;
|
||||
const scriptPromises = [];
|
||||
for (let script of scripts) {
|
||||
scriptPromises.push(this.loadScript(script))
|
||||
}
|
||||
|
||||
return Promise.all(scriptPromises);
|
||||
}
|
||||
|
||||
/**
|
||||
* This loads a single script from its source.
|
||||
* The 'loading' action is wrapped in a promise,
|
||||
* which should fail if the script cannot be fetched
|
||||
*/
|
||||
loadScript(script) {
|
||||
if (this.loaded.indexOf(script) > -1) return Promise.resolve(script);
|
||||
this.pending.push(script);
|
||||
return this.createScriptTag(script)
|
||||
.then((script) => {
|
||||
this.loaded.push(script);
|
||||
this.pending.splice(this.pending.indexOf(script), 1);
|
||||
return script;
|
||||
})
|
||||
.catch((e) => {
|
||||
this.failed.push(script);
|
||||
this.pending.splice(this.pending.indexOf(script), 1);
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* This creates a <script> tag and appends it to the document body */
|
||||
createScriptTag = (scriptSrc, onComplete) => new Promise((resolve, reject) => {
|
||||
let resolved = false,
|
||||
errored = false,
|
||||
body = document.body,
|
||||
tag = document.createElement("script");
|
||||
|
||||
const handleLoad = (event) => { resolved = true; resolve(scriptSrc); };
|
||||
const handleReject = (event) => { errored = true; reject(scriptSrc); };
|
||||
const handleComplete = () => {
|
||||
if (resolved) return handleLoad();
|
||||
if (errored) return handleReject();
|
||||
|
||||
const status = ScriptCache.SCRIPT_STATUS;
|
||||
const state = tag.readyState;
|
||||
if (state === status.COMPLETE) handleLoad();
|
||||
else if (state === status.ERROR) handleReject();
|
||||
}
|
||||
|
||||
tag.type = "text/javascript";
|
||||
tag.async = false;
|
||||
// Replace 'onComplete' callback reference in some script tag urls (e.g. Google Maps V3)
|
||||
if (scriptSrc.match(/callback=CALLBACK_NAME/)) {
|
||||
const onCompleteName = "onScriptSrcLoaded";
|
||||
scriptSrc = scriptSrc.replace(/(callback=)[^&]+/, `$1${onCompleteName}`)
|
||||
window[onCompleteName] = handleLoad;
|
||||
|
||||
} else tag.addEventListener("load", handleLoad);
|
||||
|
||||
tag.addEventListener("error", handleReject);
|
||||
tag.onreadystatechange = handleComplete;
|
||||
tag.src = scriptSrc;
|
||||
body.appendChild(tag);
|
||||
|
||||
return tag;
|
||||
})
|
||||
}
|
||||
|
|
@ -1,13 +1,14 @@
|
|||
import { useEffect } from 'react';
|
||||
const useImportScript = resourceUrl=> {
|
||||
const useScript = (src, onloadHandler) => {
|
||||
useEffect(() => {
|
||||
const script = document.createElement('script');
|
||||
script.src = resourceUrl;
|
||||
script.defer = true;
|
||||
script.src = src;
|
||||
script.defer = false;
|
||||
document.body.appendChild(script);
|
||||
script.onload = onloadHandler;
|
||||
return () => {
|
||||
document.body.removeChild(script);
|
||||
}
|
||||
}, [resourceUrl]);
|
||||
}, [src]);
|
||||
};
|
||||
export default useImportScript;
|
||||
export default useScript;
|
||||
|
|
@ -53,11 +53,8 @@ const DashboardLayout = ({ location }) => {
|
|||
});
|
||||
};
|
||||
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
fetchCurrentUser();
|
||||
//registerForChatMessages()
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
|
|
@ -113,19 +110,7 @@ const DashboardSection = () => {
|
|||
const { isFluid, isVertical, navbarStyle } = useContext(AppContext);
|
||||
const isKanban = getPageName('kanban');
|
||||
|
||||
const registerForChatMessages = () => {
|
||||
console.log('registering for chat messages');
|
||||
window.JK.JamServer.registerMessageCallback(window.JK.MessageType.CHAT_MESSAGE, function (header, payload) {
|
||||
console.debug("Handling CHAT_MESSAGE msg " + JSON.stringify(payload));
|
||||
})
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
//registerForChatMessages()
|
||||
}, []);
|
||||
|
||||
return (
|
||||
|
||||
|
||||
<div className={isFluid || isKanban ? 'container-fluid' : 'container'}>
|
||||
{isVertical && <NavbarVertical isKanban={isKanban} navbarStyle={navbarStyle} />}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,975 @@
|
|||
// The wrapper around the web-socket connection to the server
|
||||
// manages the connection, heartbeats, and reconnect logic.
|
||||
// presents itself as a dialog, or in-situ banner (_jamServer.html.haml)
|
||||
(function (context, $) {
|
||||
|
||||
"use strict";
|
||||
|
||||
context.JK = context.JK || {};
|
||||
|
||||
var logger = context.JK.logger;
|
||||
var msg_factory = context.JK.MessageFactory;
|
||||
var rest = context.JK.Rest();
|
||||
var EVENTS = context.JK.EVENTS;
|
||||
|
||||
// Let socket.io know where WebSocketMain.swf is
|
||||
context.WEB_SOCKET_SWF_LOCATION = "assets/flash/WebSocketMain.swf";
|
||||
|
||||
context.JK.JamServer = function (app, activeElementEvent) {
|
||||
|
||||
// uniquely identify the websocket connection
|
||||
var channelId = null;
|
||||
var clientType = null;
|
||||
var mode = null;
|
||||
var isLatencyTesterMode = false;
|
||||
|
||||
// heartbeat
|
||||
var startHeartbeatTimeout = null;
|
||||
var heartbeatInterval = null;
|
||||
var heartbeatMS = null;
|
||||
var connection_expire_time = null;
|
||||
var lastHeartbeatSentTime = null;
|
||||
var lastHeartbeatAckTime = null;
|
||||
var lastHeartbeatFound = false;
|
||||
var lastDisconnectedReason = null;
|
||||
var heartbeatAckCheckInterval = null;
|
||||
var notificationLastSeenAt = undefined;
|
||||
var notificationLastSeen = undefined;
|
||||
var clientClosedConnection = false;
|
||||
var initialConnectAttempt = true;
|
||||
var active = true;
|
||||
|
||||
// reconnection logic
|
||||
var connectDeferred = null;
|
||||
var freezeInteraction = false;
|
||||
var countdownInterval = null;
|
||||
var reconnectAttemptLookup = [2, 2, 2, 4, 8, 15, 30];
|
||||
var reconnectAttempt = 0;
|
||||
var reconnectingWaitPeriodStart = null;
|
||||
var reconnectDueTime = null;
|
||||
var connectTimeout = null;
|
||||
var activityTimeout;
|
||||
|
||||
// elements
|
||||
var $inSituBanner = null;
|
||||
var $inSituBannerHolder = null;
|
||||
var $messageContents = null;
|
||||
var $dialog = null;
|
||||
var $templateServerConnection = null;
|
||||
var $templateNoLogin = null;
|
||||
var $templateDisconnected = null;
|
||||
var $currentDisplay = null;
|
||||
|
||||
var $self = $(this);
|
||||
|
||||
var server = {};
|
||||
server.socket = {};
|
||||
server.signedIn = false;
|
||||
server.clientID = "";
|
||||
server.publicIP = "";
|
||||
server.dispatchTable = {};
|
||||
server.socketClosedListeners = [];
|
||||
server.connecting = false; // is the websocket connection being opened?
|
||||
server.connected = false; // is the websocket connection opened AND logged in?
|
||||
server.reconnecting = false; // are we beginning the reconnect sequence (which includes an internet health check)
|
||||
|
||||
function heartbeatStateReset() {
|
||||
lastHeartbeatSentTime = null;
|
||||
lastHeartbeatAckTime = null;
|
||||
lastHeartbeatFound = false;
|
||||
}
|
||||
|
||||
// if activeElementVotes is null, then we are assuming this is the initial connect sequence
|
||||
function initiateReconnect(activeElementVotes, in_error) {
|
||||
var initialConnect = !!activeElementVotes;
|
||||
|
||||
freezeInteraction = activeElementVotes && ((activeElementVotes.dialog && activeElementVotes.dialog.freezeInteraction === true) || (activeElementVotes.screen && activeElementVotes.screen.freezeInteraction === true));
|
||||
|
||||
if (in_error) {
|
||||
reconnectAttempt = 0;
|
||||
//$currentDisplay = renderDisconnected();
|
||||
//beginReconnectPeriod();
|
||||
}
|
||||
}
|
||||
|
||||
// handles logic if the websocket connection closes, and if it was in error then also prompt for reconnect
|
||||
function closedCleanup(in_error) {
|
||||
|
||||
if(isLatencyTester()) {
|
||||
logger.info("latency-tester: websocket connection lost")
|
||||
}
|
||||
|
||||
if(server.connected) {
|
||||
$self.triggerHandler(EVENTS.CONNECTION_DOWN);
|
||||
}
|
||||
|
||||
server.connected = false;
|
||||
server.connecting = false;
|
||||
|
||||
// stop future heartbeats
|
||||
if (heartbeatInterval != null) {
|
||||
clearInterval(heartbeatInterval);
|
||||
heartbeatInterval = null;
|
||||
}
|
||||
|
||||
// stop the heartbeat start delay from happening
|
||||
if (startHeartbeatTimeout != null) {
|
||||
clearTimeout(startHeartbeatTimeout);
|
||||
startHeartbeatTimeout = null;
|
||||
}
|
||||
|
||||
// stop checking for heartbeat acks
|
||||
if (heartbeatAckCheckInterval != null) {
|
||||
clearTimeout(heartbeatAckCheckInterval);
|
||||
heartbeatAckCheckInterval = null;
|
||||
}
|
||||
|
||||
clearConnectTimeout();
|
||||
|
||||
// noReconnect is a global to suppress reconnect behavior, so check it first
|
||||
|
||||
// we don't show any reconnect dialog on the initial connect; so we have this one-time flag
|
||||
// to cause reconnects in the case that the websocket is down on the initially connect
|
||||
if(server.noReconnect) {
|
||||
//renderLoginRequired();
|
||||
}
|
||||
else if ((initialConnectAttempt || !server.reconnecting)) {
|
||||
server.reconnecting = true;
|
||||
initialConnectAttempt = false;
|
||||
|
||||
var result = activeElementEvent('beforeDisconnect');
|
||||
|
||||
initiateReconnect(result, in_error);
|
||||
|
||||
activeElementEvent('afterDisconnect');
|
||||
|
||||
// notify anyone listening that the socket closed
|
||||
var len = server.socketClosedListeners.length;
|
||||
for (var i = 0; i < len; i++) {
|
||||
try {
|
||||
server.socketClosedListeners[i](in_error);
|
||||
} catch (ex) {
|
||||
logger.warn('exception in callback for websocket closed event:' + ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////
|
||||
//// HEARTBEAT /////
|
||||
////////////////////
|
||||
function _heartbeatAckCheck() {
|
||||
|
||||
// if we've seen an ack to the latest heartbeat, don't bother with checking again
|
||||
// this makes us resilient to front-end hangs
|
||||
if (lastHeartbeatFound) {
|
||||
return;
|
||||
}
|
||||
|
||||
// check if the server is still sending heartbeat acks back down
|
||||
// this logic equates to 'if we have not received a heartbeat within heartbeatMissedMS, then get upset
|
||||
if (new Date().getTime() - lastHeartbeatAckTime.getTime() > connection_expire_time) {
|
||||
logger.error("no heartbeat ack received from server after ", connection_expire_time, " seconds . giving up on socket connection");
|
||||
lastDisconnectedReason = 'NO_HEARTBEAT_ACK';
|
||||
context.JK.JamServer.close(true);
|
||||
}
|
||||
else {
|
||||
lastHeartbeatFound = true;
|
||||
}
|
||||
}
|
||||
|
||||
function _heartbeat() {
|
||||
|
||||
if(isLatencyTester()) {
|
||||
logger.info("latency-tester debug: heartbeat" + app.heartbeatActive)
|
||||
}
|
||||
if (app.heartbeatActive) {
|
||||
//console.log("heartbeat active?: " + active)
|
||||
var message = context.JK.MessageFactory.heartbeat(notificationLastSeen, notificationLastSeenAt, active);
|
||||
notificationLastSeenAt = undefined;
|
||||
notificationLastSeen = undefined;
|
||||
// for debugging purposes, see if the last time we've sent a heartbeat is way off (500ms) of the target interval
|
||||
var now = new Date();
|
||||
|
||||
|
||||
if(lastHeartbeatSentTime) {
|
||||
var drift = new Date().getTime() - lastHeartbeatSentTime.getTime() - heartbeatMS;
|
||||
if (drift > 500) {
|
||||
logger.warn("significant drift between heartbeats: " + drift + 'ms beyond target interval')
|
||||
}
|
||||
}
|
||||
lastHeartbeatSentTime = now;
|
||||
context.JK.JamServer.send(message);
|
||||
lastHeartbeatFound = false;
|
||||
}
|
||||
}
|
||||
|
||||
function isClientMode() {
|
||||
return mode == "client";
|
||||
}
|
||||
|
||||
function isLatencyTester() {
|
||||
return isLatencyTesterMode;
|
||||
}
|
||||
|
||||
function clearConnectTimeout() {
|
||||
if (connectTimeout) {
|
||||
clearTimeout(connectTimeout);
|
||||
connectTimeout = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
function loggedIn(header, payload) {
|
||||
|
||||
// reason for setTimeout:
|
||||
// loggedIn causes an absolute ton of initialization to happen, and errors sometimes happen
|
||||
// but because loggedIn(header,payload) is a callback from a websocket, the browser doesn't show a stack trace...
|
||||
|
||||
setTimeout(function() {
|
||||
server.signedIn = true;
|
||||
server.clientID = payload.client_id;
|
||||
server.publicIP = payload.public_ip;
|
||||
|
||||
if (context.jamClient !== undefined) {
|
||||
context.jamClient.connected = true;
|
||||
context.jamClient.clientID = server.clientID;
|
||||
}
|
||||
|
||||
clearConnectTimeout();
|
||||
|
||||
heartbeatStateReset();
|
||||
|
||||
app.clientId = payload.client_id;
|
||||
|
||||
if (isClientMode() && context.jamClient) {
|
||||
// tell the backend that we have logged in
|
||||
try {
|
||||
var msg = {user_id: payload.user_id, token: payload.token, username: payload.username, arses: payload.arses, client_id_int: payload.client_id_int, subscription: payload.subscription}
|
||||
if(payload.connection_policy) {
|
||||
try {
|
||||
msg.policy= JSON.parse(payload.connection_policy)
|
||||
}
|
||||
catch(e) {
|
||||
msg.policy = null
|
||||
console.log("unable to parse connection policy", e)
|
||||
}
|
||||
}
|
||||
console.log("logged with new msg", msg)
|
||||
context.jamClient.OnLoggedIn(msg); // ACTS AS CONTINUATION
|
||||
}
|
||||
catch(e) {
|
||||
console.log("fallback to old callback", e)
|
||||
context.jamClient.OnLoggedIn(payload.user_id, payload.token, payload.username); // ACTS AS CONTINUATION
|
||||
}
|
||||
|
||||
|
||||
$.cookie('client_id', payload.client_id);
|
||||
}
|
||||
|
||||
// this has to be after context.jamclient.OnLoggedIn, because it hangs in scenarios
|
||||
// where there is no device on startup for the current profile.
|
||||
// So, in that case, it's possible that a reconnect loop will attempt, but we *do not want*
|
||||
// it to go through unless we've passed through .OnLoggedIn
|
||||
server.connected = true;
|
||||
server.reconnecting = false;
|
||||
server.connecting = false;
|
||||
initialConnectAttempt = false;
|
||||
|
||||
heartbeatMS = payload.heartbeat_interval * 1000;
|
||||
connection_expire_time = payload.connection_expire_time * 1000;
|
||||
logger.info("loggedIn(): clientId=" + app.clientId + " heartbeat=" + payload.heartbeat_interval + "s expire_time=" + payload.connection_expire_time + 's');
|
||||
|
||||
// add some randomness to help move heartbeats apart from each other
|
||||
|
||||
// send 1st heartbeat somewhere between 0 - 0.5 of the connection expire time
|
||||
var randomStartTime = connection_expire_time * (Math.random() / 2)
|
||||
|
||||
if (startHeartbeatTimeout) {
|
||||
logger.warn("start heartbeat timeout is active; should be null")
|
||||
clearTimeout(startHeartbeatTimeout)
|
||||
}
|
||||
|
||||
if (heartbeatInterval != null) {
|
||||
logger.warn("heartbeatInterval is active; should be null")
|
||||
clearInterval(heartbeatInterval);
|
||||
heartbeatInterval = null;
|
||||
}
|
||||
|
||||
if (heartbeatAckCheckInterval != null) {
|
||||
logger.warn("heartbeatAckCheckInterval is active; should be null")
|
||||
clearInterval(heartbeatAckCheckInterval);
|
||||
heartbeatAckCheckInterval = null;
|
||||
}
|
||||
|
||||
startHeartbeatTimeout = setTimeout(function() {
|
||||
if(server.connected) {
|
||||
heartbeatInterval = context.setInterval(_heartbeat, heartbeatMS);
|
||||
heartbeatAckCheckInterval = context.setInterval(_heartbeatAckCheck, 1000);
|
||||
lastHeartbeatAckTime = new Date(new Date().getTime() + heartbeatMS); // add a little forgiveness to server for initial heartbeat
|
||||
}
|
||||
}, randomStartTime)
|
||||
|
||||
logger.info("starting heartbeat timer in " + randomStartTime/1000 + 's')
|
||||
|
||||
|
||||
|
||||
connectDeferred.resolve();
|
||||
$self.triggerHandler(EVENTS.CONNECTION_UP)
|
||||
|
||||
activeElementEvent('afterConnect', payload);
|
||||
|
||||
if (payload.client_update && context.JK.ClientUpdateInstance) {
|
||||
context.JK.ClientUpdateInstance.runCheck(payload.client_update.product, payload.client_update.version, payload.client_update.uri, payload.client_update.size)
|
||||
}
|
||||
}, 0)
|
||||
}
|
||||
|
||||
function setActive(active) {
|
||||
if(context.UserActivityActions) {
|
||||
context.UserActivityActions.setActive(active)
|
||||
}
|
||||
}
|
||||
function markAway() {
|
||||
logger.debug("sleep again!")
|
||||
active = false;
|
||||
setActive(active)
|
||||
var userStatus = msg_factory.userStatus(false, null);
|
||||
server.send(userStatus);
|
||||
}
|
||||
|
||||
function activityCheck() {
|
||||
var timeoutTime = 300000; // 5 * 1000 * 60 , 5 minutes
|
||||
active = true;
|
||||
setActive(active)
|
||||
activityTimeout = setTimeout(markAway, timeoutTime);
|
||||
$(document).ready(function() {
|
||||
$('body').bind('mousedown keydown touchstart focus', function(event) {
|
||||
if (activityTimeout) {
|
||||
clearTimeout(activityTimeout);
|
||||
activityTimeout = null;
|
||||
}
|
||||
|
||||
if (!active) {
|
||||
if(server && server.connected) {
|
||||
logger.debug("awake again!")
|
||||
var userStatus = msg_factory.userStatus(true, null);
|
||||
server.send(userStatus);
|
||||
}
|
||||
}
|
||||
active = true;
|
||||
setActive(active)
|
||||
activityTimeout = setTimeout(markAway, timeoutTime);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function heartbeatAck(header, payload) {
|
||||
lastHeartbeatAckTime = new Date();
|
||||
}
|
||||
|
||||
function registerLoginAck() {
|
||||
logger.debug("register for loggedIn to set clientId");
|
||||
context.JK.JamServer.registerMessageCallback(context.JK.MessageType.LOGIN_ACK, loggedIn);
|
||||
}
|
||||
|
||||
function registerHeartbeatAck() {
|
||||
logger.debug("register for heartbeatAck");
|
||||
context.JK.JamServer.registerMessageCallback(context.JK.MessageType.HEARTBEAT_ACK, heartbeatAck);
|
||||
}
|
||||
|
||||
function registerSocketClosed() {
|
||||
logger.debug("register for socket closed");
|
||||
context.JK.JamServer.registerOnSocketClosed(socketClosed);
|
||||
}
|
||||
|
||||
function registerServerRejection() {
|
||||
logger.debug("register for server rejection");
|
||||
context.JK.JamServer.registerMessageCallback(context.JK.MessageType.SERVER_REJECTION_ERROR, serverRejection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called whenever the websocket closes; this gives us a chance to cleanup things that should be stopped/cleared
|
||||
* @param in_error did the socket close abnormally?
|
||||
*/
|
||||
function socketClosed(in_error) {
|
||||
|
||||
// tell the backend that we have logged out
|
||||
if (context.jamClient) context.jamClient.OnLoggedOut();
|
||||
|
||||
}
|
||||
|
||||
function serverRejection(header, payload) {
|
||||
logger.warn("server rejected our websocket connection. reason=" + payload.error_msg)
|
||||
|
||||
if(payload.error_code == 'max_user_connections') {
|
||||
context.JK.Banner.showAlert("Too Many Connections", "You have too many connections to the server. If you believe this is in error, please contact support.")
|
||||
}
|
||||
else if(payload.error_code == 'invalid_login' || payload.error_code == 'empty_login') {
|
||||
logger.debug(payload.error_code + ": no longer reconnecting")
|
||||
server.noReconnect = true; // stop trying to log in!!
|
||||
}
|
||||
else if (payload.error_code == 'no_reconnect') {
|
||||
logger.debug(payload.error_code + ": no longer reconnecting")
|
||||
server.noReconnect = true; // stop trying to log in!!
|
||||
context.JK.Banner.showAlert("Misbehaved Client", "Please restart your application in order to continue using JamKazam.")
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////
|
||||
/// RECONNECT /////
|
||||
///////////////////
|
||||
function internetUp() {
|
||||
var start = new Date().getTime();
|
||||
server.connect()
|
||||
.done(function () {
|
||||
guardAgainstRapidTransition(start, finishReconnect);
|
||||
})
|
||||
.fail(function () {
|
||||
guardAgainstRapidTransition(start, closedOnReconnectAttempt);
|
||||
});
|
||||
}
|
||||
|
||||
// websocket couldn't connect. let's try again soon
|
||||
function closedOnReconnectAttempt() {
|
||||
failedReconnect();
|
||||
}
|
||||
|
||||
function finishReconnect() {
|
||||
|
||||
if(!clientClosedConnection) {
|
||||
lastDisconnectedReason = 'WEBSOCKET_CLOSED_REMOTELY'
|
||||
clientClosedConnection = false;
|
||||
}
|
||||
else if(!lastDisconnectedReason) {
|
||||
// let's have at least some sort of type, however generci
|
||||
lastDisconnectedReason = 'WEBSOCKET_CLOSED_LOCALLY'
|
||||
}
|
||||
|
||||
// if ($currentDisplay.is('.no-websocket-connection')) {
|
||||
// // this path is the 'not in session path'; so there is nothing else to do
|
||||
// $currentDisplay.removeClass('active');
|
||||
|
||||
// // TODO: tell certain elements that we've reconnected
|
||||
// }
|
||||
// else {
|
||||
// window.location.reload();
|
||||
// }
|
||||
}
|
||||
|
||||
function buildOptions() {
|
||||
return {};
|
||||
}
|
||||
|
||||
function renderLoginRequired() {
|
||||
var $inSituContent = $(context._.template($templateNoLogin.html(), buildOptions(), { variable: 'data' }));
|
||||
$inSituContent.find('a.disconnected-login').click(function() {
|
||||
var redirectPath= '?redirect-to=' + encodeURIComponent(context.JK.locationPath());
|
||||
if(gon.isNativeClient) {
|
||||
window.location.href = '/signin' + redirectPath;
|
||||
}
|
||||
else {
|
||||
window.location.href = '/' + redirectPath;
|
||||
}
|
||||
return false;
|
||||
})
|
||||
$messageContents.empty();
|
||||
$messageContents.append($inSituContent);
|
||||
$inSituBannerHolder.addClass('active');
|
||||
return $inSituBannerHolder;
|
||||
}
|
||||
function renderDisconnected() {
|
||||
|
||||
var content = null;
|
||||
if (freezeInteraction) {
|
||||
var template = $templateDisconnected.html();
|
||||
var templateHtml = $(context.JK.fillTemplate(template, buildOptions()));
|
||||
templateHtml.find('.reconnect-countdown').html(formatDelaySecs(reconnectDelaySecs()));
|
||||
content = context.JK.Banner.show({
|
||||
html: templateHtml,
|
||||
type: 'reconnect'
|
||||
});
|
||||
}
|
||||
else {
|
||||
var $inSituContent = $(context._.template($templateServerConnection.html(), buildOptions(), { variable: 'data' }));
|
||||
$inSituContent.find('.reconnect-countdown').html(formatDelaySecs(reconnectDelaySecs()));
|
||||
$messageContents.empty();
|
||||
$messageContents.append($inSituContent);
|
||||
$inSituBannerHolder.addClass('active');
|
||||
content = $inSituBannerHolder;
|
||||
}
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
function formatDelaySecs(secs) {
|
||||
return $('<span class="countdown-seconds"><span class="countdown">' + secs + '</span> ' + (secs == 1 ? ' second.<span style="visibility:hidden">s</span>' : 'seconds.') + '</span>');
|
||||
}
|
||||
|
||||
function setCountdown($parent) {
|
||||
$parent.find('.reconnect-countdown').html(formatDelaySecs(reconnectDelaySecs()));
|
||||
}
|
||||
|
||||
function renderCouldNotReconnect() {
|
||||
//return renderDisconnected();
|
||||
}
|
||||
|
||||
function renderReconnecting() {
|
||||
$currentDisplay.find('.reconnect-progress-msg').text('Attempting to reconnect...')
|
||||
|
||||
if ($currentDisplay.is('.no-websocket-connection')) {
|
||||
$currentDisplay.find('.disconnected-reconnect').removeClass('reconnect-enabled').addClass('reconnect-disabled');
|
||||
}
|
||||
else {
|
||||
$currentDisplay.find('.disconnected-reconnect').removeClass('button-orange').addClass('button-grey');
|
||||
}
|
||||
}
|
||||
|
||||
function failedReconnect() {
|
||||
reconnectAttempt += 1;
|
||||
//$currentDisplay = renderCouldNotReconnect();
|
||||
//beginReconnectPeriod();
|
||||
}
|
||||
|
||||
function guardAgainstRapidTransition(start, nextStep) {
|
||||
var now = new Date().getTime();
|
||||
|
||||
if ((now - start) < 1500) {
|
||||
setTimeout(function () {
|
||||
nextStep();
|
||||
}, 1500 - (now - start))
|
||||
}
|
||||
else {
|
||||
nextStep();
|
||||
}
|
||||
}
|
||||
|
||||
function attemptReconnect() {
|
||||
|
||||
if(server.connecting) {
|
||||
logger.warn("attemptReconnect called when already connecting");
|
||||
return;
|
||||
}
|
||||
|
||||
if(server.connected) {
|
||||
logger.warn("attemptReconnect called when already connected");
|
||||
return;
|
||||
}
|
||||
|
||||
var start = new Date().getTime();
|
||||
|
||||
//renderReconnecting();
|
||||
|
||||
rest.serverHealthCheck()
|
||||
.done(function () {
|
||||
guardAgainstRapidTransition(start, internetUp);
|
||||
})
|
||||
.fail(function (xhr, textStatus, errorThrown) {
|
||||
|
||||
if (xhr && xhr.status >= 100) {
|
||||
// we could connect to the server, and it's alive
|
||||
guardAgainstRapidTransition(start, internetUp);
|
||||
}
|
||||
else {
|
||||
guardAgainstRapidTransition(start, failedReconnect);
|
||||
}
|
||||
});
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function clearReconnectTimers() {
|
||||
if (countdownInterval) {
|
||||
clearInterval(countdownInterval);
|
||||
countdownInterval = null;
|
||||
}
|
||||
}
|
||||
|
||||
function beginReconnectPeriod() {
|
||||
// allow user to force reconnect
|
||||
$currentDisplay.find('a.disconnected-reconnect').unbind('click').click(function () {
|
||||
if ($(this).is('.button-orange') || $(this).is('.reconnect-enabled')) {
|
||||
logger.debug("user initiated reconnect")
|
||||
clearReconnectTimers();
|
||||
attemptReconnect();
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
reconnectingWaitPeriodStart = new Date().getTime();
|
||||
reconnectDueTime = reconnectingWaitPeriodStart + reconnectDelaySecs() * 1000;
|
||||
|
||||
// update count down timer periodically
|
||||
countdownInterval = setInterval(function () {
|
||||
var now = new Date().getTime();
|
||||
if (now > reconnectDueTime) {
|
||||
clearReconnectTimers();
|
||||
attemptReconnect();
|
||||
}
|
||||
else {
|
||||
var secondsUntilReconnect = Math.ceil((reconnectDueTime - now) / 1000);
|
||||
$currentDisplay.find('.reconnect-countdown').html(formatDelaySecs(secondsUntilReconnect));
|
||||
}
|
||||
}, 333);
|
||||
}
|
||||
|
||||
function reconnectDelaySecs() {
|
||||
if (reconnectAttempt > reconnectAttemptLookup.length - 1) {
|
||||
return reconnectAttemptLookup[reconnectAttemptLookup.length - 1];
|
||||
}
|
||||
else {
|
||||
return reconnectAttemptLookup[reconnectAttempt];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
server.registerOnSocketClosed = function (callback) {
|
||||
server.socketClosedListeners.push(callback);
|
||||
}
|
||||
|
||||
server.registerMessageCallback = function (messageType, callback) {
|
||||
if (server.dispatchTable[messageType] === undefined) {
|
||||
server.dispatchTable[messageType] = [];
|
||||
}
|
||||
|
||||
server.dispatchTable[messageType].push(callback);
|
||||
};
|
||||
|
||||
server.unregisterMessageCallback = function (messageType, callback) {
|
||||
if (server.dispatchTable[messageType] !== undefined) {
|
||||
for (var i = server.dispatchTable[messageType].length; i--;) {
|
||||
if (server.dispatchTable[messageType][i] === callback) {
|
||||
server.dispatchTable[messageType].splice(i, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (server.dispatchTable[messageType].length === 0) {
|
||||
delete server.dispatchTable[messageType];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
server.connect = function () {
|
||||
|
||||
if(server.connecting) {
|
||||
logger.error("server.connect should never be called if we are already connecting. cancelling.")
|
||||
// XXX should return connectPromise, but needs to be tested/vetted
|
||||
return;
|
||||
}
|
||||
if(server.connected) {
|
||||
logger.error("server.connect should never be called if we are already connected. cancelling.")
|
||||
// XXX should return connectPromise, but needs to be tested/vetted
|
||||
return;
|
||||
}
|
||||
|
||||
if(!clientType) {
|
||||
clientType = context.JK.clientType();
|
||||
}
|
||||
if(!mode) {
|
||||
mode = 'client'
|
||||
if (context.jamClient && context.jamClient.getOperatingMode) {
|
||||
mode = context.jamClient.getOperatingMode()
|
||||
}
|
||||
isLatencyTesterMode = mode == 'server';
|
||||
}
|
||||
|
||||
connectDeferred = new $.Deferred();
|
||||
channelId = context.JK.generateUUID(); // create a new channel ID for every websocket connection
|
||||
|
||||
var rememberToken = $.cookie("remember_token");
|
||||
|
||||
if(isClientMode() && !rememberToken) {
|
||||
server.noReconnect = true;
|
||||
logger.debug("no login info; shutting down websocket");
|
||||
//renderLoginRequired();
|
||||
connectDeferred.reject();
|
||||
return connectDeferred;
|
||||
}
|
||||
// we will log in one of 3 ways:
|
||||
// browser: use session cookie, and auth with token
|
||||
// native: use session cookie, and use the token
|
||||
// latency_tester: ask for client ID from backend; no token (trusted)
|
||||
|
||||
|
||||
if (isClientMode()) {
|
||||
var client_type = context.JK.clientType()
|
||||
var client_id = (gon.global.env == "development" ? $.cookie('client_id') : null)
|
||||
var machine = context.jamClient.SessionGetMacHash()
|
||||
if (machine) {
|
||||
machine = machine.all
|
||||
}
|
||||
}
|
||||
else {
|
||||
var client_type = 'latency_tester'
|
||||
var client_id = context.jamClient.clientID
|
||||
var machine = null
|
||||
}
|
||||
var params = {
|
||||
channel_id: channelId,
|
||||
token: rememberToken,
|
||||
client_type: client_type, // isClientMode() ? context.JK.clientType() : 'latency_tester',
|
||||
client_id: client_id,
|
||||
machine: machine,
|
||||
os: context.JK.GetOSAsString(),
|
||||
//jamblaster_serial_no: context.PlatformStore.jamblasterSerialNo(),
|
||||
udp_reachable: context.JK.StunInstance ? !context.JK.StunInstance.sync() : null // latency tester doesn't have the stun class loaded
|
||||
}
|
||||
var uri = context.gon.websocket_gateway_uri + '?' + $.param(params); // Set in index.html.erb.
|
||||
|
||||
logger.debug("connecting websocket: " + uri);
|
||||
|
||||
server.connecting = true;
|
||||
server.socket = new context.WebSocket(uri);
|
||||
server.socket.channelId = channelId;
|
||||
server.socket.onopen = server.onOpen;
|
||||
server.socket.onmessage = server.onMessage;
|
||||
server.socket.onclose = server.onClose;
|
||||
|
||||
connectTimeout = setTimeout(function () {
|
||||
logger.debug("connection timeout fired")
|
||||
connectTimeout = null;
|
||||
|
||||
if(connectDeferred.state() === 'pending') {
|
||||
server.close(true);
|
||||
connectDeferred.reject();
|
||||
}
|
||||
}, 4000);
|
||||
|
||||
return connectDeferred;
|
||||
};
|
||||
|
||||
server.close = function (in_error) {
|
||||
logger.info("closing websocket");
|
||||
|
||||
clientClosedConnection = true;
|
||||
server.socket.close();
|
||||
|
||||
closedCleanup(in_error);
|
||||
}
|
||||
|
||||
server.rememberLogin = function () {
|
||||
var token, loginMessage;
|
||||
token = $.cookie("remember_token");
|
||||
|
||||
loginMessage = msg_factory.login_with_token(token, null, clientType);
|
||||
server.send(loginMessage);
|
||||
};
|
||||
|
||||
server.latencyTesterLogin = function() {
|
||||
var loginMessage = msg_factory.login_with_client_id(context.jamClient.clientID, 'latency_tester');
|
||||
server.send(loginMessage);
|
||||
}
|
||||
|
||||
server.onOpen = function () {
|
||||
logger.debug("server.onOpen");
|
||||
|
||||
// we should receive LOGIN_ACK very soon. we already set a timer elsewhere to give 4 seconds to receive it
|
||||
};
|
||||
|
||||
server.onMessage = function (e) {
|
||||
var message = JSON.parse(e.data),
|
||||
messageType = message.type.toLowerCase(),
|
||||
payload = message[messageType],
|
||||
callbacks = server.dispatchTable[message.type];
|
||||
|
||||
if (message.type == context.JK.MessageType.PEER_MESSAGE) {
|
||||
//logger.info("server.onMessage:" + messageType);
|
||||
}
|
||||
else if (message.type != context.JK.MessageType.HEARTBEAT_ACK && message.type != context.JK.MessageType.PEER_MESSAGE) {
|
||||
logger.info("server.onMessage:" + messageType + " payload:" + JSON.stringify(payload));
|
||||
}
|
||||
|
||||
if (callbacks !== undefined) {
|
||||
var len = callbacks.length;
|
||||
for (var i = 0; i < len; i++) {
|
||||
try {
|
||||
callbacks[i](message, payload);
|
||||
} catch (ex) {
|
||||
logger.warn('exception in callback for websocket message:' + ex);
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
logger.info("Unexpected message type %s.", message.type);
|
||||
}
|
||||
};
|
||||
|
||||
// onClose is called if either client or server closes connection
|
||||
server.onClose = function () {
|
||||
logger.info("Socket to server closed.");
|
||||
|
||||
var disconnectedSocket = this;
|
||||
|
||||
if(disconnectedSocket.channelId != server.socket.channelId) {
|
||||
logger.debug(" ignoring disconnect for non-current socket. current=" + server.socket.channelId + ", disc=" + disconnectedSocket.channelId)
|
||||
return;
|
||||
}
|
||||
|
||||
if (connectDeferred.state() === "pending") {
|
||||
connectDeferred.reject();
|
||||
}
|
||||
|
||||
closedCleanup(true);
|
||||
};
|
||||
|
||||
server.send = function (message) {
|
||||
|
||||
var jsMessage = JSON.stringify(message);
|
||||
|
||||
if( isLatencyTester() && (message.type == context.JK.MessageType.HEARTBEAT || message.type == context.JK.MessageType.PEER_MESSAGE)) {
|
||||
logger.info("latency-tester: server.send(" + jsMessage + ")")
|
||||
}
|
||||
else if (message.type != context.JK.MessageType.HEARTBEAT && message.type != context.JK.MessageType.PEER_MESSAGE) {
|
||||
logger.info("server.send(" + jsMessage + ")");
|
||||
}
|
||||
if (server !== undefined && server.socket !== undefined && server.socket.send !== undefined) {
|
||||
try {
|
||||
server.socket.send(jsMessage);
|
||||
}
|
||||
catch(err) {
|
||||
logger.warn("error when sending on websocket: " + err)
|
||||
}
|
||||
} else {
|
||||
logger.warn("Dropped message because server connection is closed.");
|
||||
}
|
||||
};
|
||||
|
||||
server.loginSession = function (sessionId) {
|
||||
var loginMessage;
|
||||
|
||||
if (!server.signedIn) {
|
||||
logger.warn("Not signed in!");
|
||||
// TODO: surface the error
|
||||
return;
|
||||
}
|
||||
|
||||
loginMessage = msg_factory.login_jam_session(sessionId);
|
||||
server.send(loginMessage);
|
||||
};
|
||||
|
||||
/** with the advent of the reliable UDP channel, this is no longer how messages are sent from client-to-clent
|
||||
* however, the mechanism still exists and is useful in test contexts; and maybe in the future
|
||||
* @param receiver_id client ID of message to send
|
||||
* @param message the actual message
|
||||
*/
|
||||
server.sendP2PMessage = function (receiver_id, message) {
|
||||
//logger.log("P2P message from [" + server.clientID + "] to [" + receiver_id + "]: " + message);
|
||||
//console.time('sendP2PMessage');
|
||||
var outgoing_msg = msg_factory.client_p2p_message(server.clientID, receiver_id, message);
|
||||
server.send(outgoing_msg);
|
||||
//console.timeEnd('sendP2PMessage');
|
||||
};
|
||||
|
||||
server.sendLogin = function (token) {
|
||||
logger.debug("sending login message on behalf of client")
|
||||
var outgoing_msg = msg_factory.login_with_token(token, null, null);
|
||||
server.send(outgoing_msg);
|
||||
};
|
||||
|
||||
server.sendLogout = function () {
|
||||
logger.debug("sending logout message on behalf of client")
|
||||
var outgoing_msg = msg_factory.logout();
|
||||
server.send(outgoing_msg);
|
||||
};
|
||||
|
||||
server.sendChatMessage = function(channel, message) {
|
||||
|
||||
if (server.connected) {
|
||||
|
||||
var chatMsg = msg_factory.chatMessage(channel, message)
|
||||
server.send(chatMsg)
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
server.updateNotificationSeen = function (notificationId, notificationCreatedAt) {
|
||||
var time = new Date(notificationCreatedAt);
|
||||
|
||||
if (!notificationCreatedAt) {
|
||||
throw 'invalid value passed to updateNotificationSeen'
|
||||
}
|
||||
|
||||
if (!notificationLastSeenAt) {
|
||||
notificationLastSeenAt = notificationCreatedAt;
|
||||
notificationLastSeen = notificationId;
|
||||
logger.debug("updated notificationLastSeenAt with: " + notificationCreatedAt);
|
||||
}
|
||||
else if (time.getTime() > new Date(notificationLastSeenAt).getTime()) {
|
||||
notificationLastSeenAt = notificationCreatedAt;
|
||||
notificationLastSeen = notificationId;
|
||||
logger.debug("updated notificationLastSeenAt with: " + notificationCreatedAt);
|
||||
}
|
||||
else {
|
||||
logger.debug("ignored notificationLastSeenAt for: " + notificationCreatedAt);
|
||||
}
|
||||
}
|
||||
|
||||
server.registerMessageCallback(context.JK.MessageType.PEER_MESSAGE, function (header, payload) {
|
||||
if (context.jamClient !== undefined) {
|
||||
context.jamClient.P2PMessageReceived(header.from, payload.message);
|
||||
}
|
||||
});
|
||||
|
||||
server.get$Server = function() {
|
||||
return $self;
|
||||
}
|
||||
|
||||
context.JK.JamServer = server;
|
||||
|
||||
// Callbacks from jamClient
|
||||
if (context.jamClient !== undefined && context.jamClient.IsNativeClient()) {
|
||||
context.jamClient.SendP2PMessage.connect(server.sendP2PMessage);
|
||||
|
||||
if (context.jamClient.SendLogin) {
|
||||
context.jamClient.SendLogin.connect(server.sendLogin);
|
||||
context.jamClient.SendLogin.connect(server.sendLogout);
|
||||
}
|
||||
}
|
||||
|
||||
function initialize() {
|
||||
|
||||
registerLoginAck();
|
||||
registerHeartbeatAck();
|
||||
registerServerRejection();
|
||||
registerSocketClosed();
|
||||
activityCheck();
|
||||
|
||||
// $inSituBanner = $('.server-connection');
|
||||
// $inSituBannerHolder = $('.no-websocket-connection');
|
||||
// $messageContents = $inSituBannerHolder.find('.message-contents');
|
||||
// $dialog = $('#banner');
|
||||
// $templateServerConnection = $('#template-server-connection');
|
||||
// $templateNoLogin = $('#template-no-login');
|
||||
// $templateDisconnected = $('#template-disconnected');
|
||||
|
||||
// if ($inSituBanner.length != 1) {
|
||||
// throw "found wrong number of .server-connection: " + $inSituBanner.length;
|
||||
// }
|
||||
// if ($inSituBannerHolder.length != 1) {
|
||||
// throw "found wrong number of .no-websocket-connection: " + $inSituBannerHolder.length;
|
||||
// }
|
||||
// if ($messageContents.length != 1) {
|
||||
// throw "found wrong number of .message-contents: " + $messageContents.length;
|
||||
// }
|
||||
// if ($dialog.length != 1) {
|
||||
// throw "found wrong number of #banner: " + $dialog.length;
|
||||
// }
|
||||
// if ($templateServerConnection.length != 1) {
|
||||
// throw "found wrong number of #template-server-connection: " + $templateServerConnection.length;
|
||||
// }
|
||||
// if ($templateDisconnected.length != 1) {
|
||||
// throw "found wrong number of #template-disconnected: " + $templateDisconnected.length;
|
||||
// }
|
||||
}
|
||||
|
||||
this.initialize = initialize;
|
||||
|
||||
return this;
|
||||
}
|
||||
})(window, jQuery);
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
//= require jquery
|
||||
//= require jquery.monkeypatch
|
||||
//= require jquery_ujs
|
||||
//= require jquery.cookie
|
||||
//= require jstz
|
||||
//= require AAC_underscore
|
||||
//= require jam_rest
|
||||
//= require AAA_Log
|
||||
//= require globals
|
||||
//= require AAB_message_factory
|
||||
//= require layout
|
||||
//= require client_init
|
||||
//= require utils
|
||||
//= require jamkazam
|
||||
//= require modern/JamServer_copy
|
||||
|
||||
//= require fakeJamClient
|
||||
//= require fakeJamClientMessages
|
||||
//= require fakeJamClientRecordings
|
||||
|
||||
|
||||
// (function (context, $) {
|
||||
// "use strict";
|
||||
// context.JK = context.JK || {};
|
||||
|
||||
// $(document).on('JAMKAZAM_CONSTRUCTED', function(e, data) {
|
||||
// console.log("JAMKAZAM_CONSTRUCTED");
|
||||
// var app = data.app;
|
||||
// if(!app) throw "app not found";
|
||||
|
||||
// // makes window.jamClient / context.jamClient set to something non-null very early on
|
||||
// context.JK.initJamClient(app);
|
||||
|
||||
// })
|
||||
|
||||
// })(window, jQuery);
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
<%= include_gon :need_tag => false %>
|
||||
<%= Rails.application.assets.find_asset('modern/scripts.js').to_s.html_safe %>
|
||||
|
||||
$(function() {
|
||||
|
||||
<%# JK = JK || {};
|
||||
|
||||
JK.app = JK.JamKazam();
|
||||
var jamServer = new JK.JamServer(JK.app, function(event_type) {return JK.app.activeElementEvent(event_type)});
|
||||
jamServer.initialize();
|
||||
|
||||
var clientInit = new JK.ClientInit();
|
||||
clientInit.init();
|
||||
|
||||
JK.JamServer.connect() // singleton here defined in JamServer.js
|
||||
.done(function() {
|
||||
//_initAfterConnect(true);
|
||||
})
|
||||
.fail(function() {
|
||||
//_initAfterConnect(false);
|
||||
}); %>
|
||||
|
||||
})
|
||||
|
||||
|
||||
Loading…
Reference in New Issue