diff --git a/.nuget/packages.config b/.nuget/packages.config
index 715540c0..9437ec58 100644
--- a/.nuget/packages.config
+++ b/.nuget/packages.config
@@ -1,4 +1,4 @@
-
+
\ No newline at end of file
diff --git a/Disco.Services/Disco.Services.csproj b/Disco.Services/Disco.Services.csproj
index f0453aee..f1c6745d 100644
--- a/Disco.Services/Disco.Services.csproj
+++ b/Disco.Services/Disco.Services.csproj
@@ -42,8 +42,9 @@
..\packages\LumenWorks.Framework.IO.3.8.0\lib\net20\LumenWorks.Framework.IO.dll
-
- ..\packages\Microsoft.AspNet.SignalR.Core.2.0.3\lib\net45\Microsoft.AspNet.SignalR.Core.dll
+
+ False
+ ..\packages\Microsoft.AspNet.SignalR.Core.2.1.0\lib\net45\Microsoft.AspNet.SignalR.Core.dll
..\packages\Microsoft.Owin.2.0.1\lib\net45\Microsoft.Owin.dll
@@ -361,7 +362,7 @@
-
+
diff --git a/Disco.Services/packages.config b/Disco.Services/packages.config
index 00be84eb..3d0ed850 100644
--- a/Disco.Services/packages.config
+++ b/Disco.Services/packages.config
@@ -4,7 +4,7 @@
-
+
diff --git a/Disco.Web/ClientSource/Scripts/Modules/jQuery-SignalR.js b/Disco.Web/ClientSource/Scripts/Modules/jQuery-SignalR.js
index 360cacef..7bf0c20f 100644
--- a/Disco.Web/ClientSource/Scripts/Modules/jQuery-SignalR.js
+++ b/Disco.Web/ClientSource/Scripts/Modules/jQuery-SignalR.js
@@ -1,8 +1,8 @@
-///#source 1 1 /ClientSource/Scripts/Modules/jQuery-SignalR/jquery.signalR-2.0.3.js
+///#source 1 1 /ClientSource/Scripts/Modules/jQuery-SignalR/jquery.signalR-2.1.0.js
/* jquery.signalR.core.js */
/*global window:false */
/*!
- * ASP.NET SignalR JavaScript Library v2.0.3
+ * ASP.NET SignalR JavaScript Library v2.1.0
* http://signalr.net/
*
* Copyright (C) Microsoft Corporation. All rights reserved.
@@ -12,7 +12,6 @@
///
///
(function ($, window, undefined) {
- "use strict";
var resources = {
nojQuery: "jQuery was not found. Please ensure jQuery is referenced before the SignalR client JavaScript file.",
@@ -21,6 +20,10 @@
stoppedWhileLoading: "The connection was stopped during page load.",
stoppedWhileNegotiating: "The connection was stopped during the negotiate request.",
errorParsingNegotiateResponse: "Error parsing negotiate response.",
+ errorDuringStartRequest: "Error during start request. Stopping the connection.",
+ stoppedDuringStartRequest: "The connection was stopped during the start request.",
+ errorParsingStartResponse: "Error parsing start response: '{0}'. Stopping the connection.",
+ invalidStartResponse: "Invalid start response: '{0}'. Stopping the connection.",
protocolIncompatible: "You are using a version of the client that isn't compatible with the server. Client version {0}, server version {1}.",
sendFailed: "Send failed.",
parseFailed: "Failed at parsing response: {0}",
@@ -33,7 +36,9 @@
pingServerFailedStatusCode: "Failed to ping server. Server responded with status code {0}, stopping the connection.",
pingServerFailedParse: "Failed to parse ping server response, stopping the connection.",
noConnectionTransport: "Connection is in an invalid state, there is no transport active.",
- webSocketsInvalidState: "The Web Socket transport is in an invalid state, transitioning into reconnecting."
+ webSocketsInvalidState: "The Web Socket transport is in an invalid state, transitioning into reconnecting.",
+ reconnectTimeout: "Couldn't reconnect within the configured timeout of {0} ms, disconnecting.",
+ reconnectWindowTimeout: "The client has been inactive since {0} and it has exceeded the inactivity timeout of {1} ms. Stopping the connection."
};
if (typeof ($) !== "function") {
@@ -94,6 +99,11 @@
isDisconnecting = function (connection) {
return connection.state === signalR.connectionState.disconnected;
},
+
+ supportsKeepAlive = function (connection) {
+ return connection._.keepAliveData.activated &&
+ connection.transport.supportsKeepAlive(connection);
+ },
configureStopReconnectingTimeout = function (connection) {
var stopReconnectingTimeout,
@@ -103,7 +113,9 @@
// Without this check if a connection is stopped then started events will be bound multiple times.
if (!connection._.configuredStopReconnectingTimeout) {
onReconnectTimeout = function (connection) {
- connection.log("Couldn't reconnect within the configured timeout (" + connection.disconnectTimeout + "ms), disconnecting.");
+ var message = signalR._.format(signalR.resources.reconnectTimeout, connection.disconnectTimeout);
+ connection.log(message);
+ $(connection).triggerHandler(events.onError, [signalR._.error(message, /* source */ "TimeoutException")]);
connection.stop(/* async */ false, /* notifyServer */ false);
};
@@ -323,6 +335,7 @@
this.url = url;
this.qs = qs;
+ this.lastError = null;
this._ = {
keepAliveData: {},
connectingMessageBuffer: new ConnectingMessageBuffer(this, function (message) {
@@ -352,6 +365,8 @@
}
},
+ _originalJson: window.JSON,
+
json: window.JSON,
isCrossDomain: function (url, against) {
@@ -387,7 +402,7 @@
state: signalR.connectionState.disconnected,
- clientProtocol: "1.3",
+ clientProtocol: "1.4",
reconnectDelay: 2000,
@@ -414,6 +429,8 @@
deferred = connection._deferral || $.Deferred(), // Check to see if there is a pre-existing deferral that's being built on, if so we want to keep using it
parser = window.document.createElement("a");
+ connection.lastError = null;
+
// Persist the deferral so that if start is called multiple times the same deferral is used.
connection._deferral = deferred;
@@ -471,14 +488,14 @@
parser.href = connection.url;
if (!parser.protocol || parser.protocol === ":") {
connection.protocol = window.document.location.protocol;
- connection.host = window.document.location.host;
- connection.baseUrl = connection.protocol + "//" + connection.host;
+ connection.host = parser.host || window.document.location.host;
} else {
connection.protocol = parser.protocol;
connection.host = parser.host;
- connection.baseUrl = parser.protocol + "//" + parser.host;
}
+ connection.baseUrl = connection.protocol + "//" + connection.host;
+
// Set the websocket protocol
connection.wsProtocol = connection.protocol === "https:" ? "wss://" : "ws://";
@@ -587,7 +604,7 @@
window.clearTimeout(connection._.onFailedTimeoutHandle);
- if (transport.supportsKeepAlive && connection._.keepAliveData.activated) {
+ if (supportsKeepAlive(connection)) {
signalR.transports._logic.monitorKeepAlive(connection);
}
@@ -647,109 +664,97 @@
url = signalR.transports._logic.prepareQueryString(connection, url);
- // Add the client version to the negotiate request. We utilize the same addQs method here
- // so that it can append the clientVersion appropriately to the URL
- url = signalR.transports._logic.addQs(url, {
- clientProtocol: connection.clientProtocol
- });
-
connection.log("Negotiating with '" + url + "'.");
// Save the ajax negotiate request object so we can abort it if stop is called while the request is in flight.
- connection._.negotiateRequest = $.ajax(
- $.extend({}, $.signalR.ajaxDefaults, {
- xhrFields: { withCredentials: connection.withCredentials },
- url: url,
- type: "GET",
- contentType: connection.contentType,
- data: {},
- dataType: connection.ajaxDataType,
- error: function (error, statusText) {
- // We don't want to cause any errors if we're aborting our own negotiate request.
- if (statusText !== _negotiateAbortText) {
- onFailed(error, connection);
- } else {
- // This rejection will noop if the deferred has already been resolved or rejected.
- deferred.reject(signalR._.error(resources.stoppedWhileNegotiating, null /* error */, connection._.negotiateRequest));
- }
- },
- success: function (result) {
- var res,
- keepAliveData,
- protocolError,
- transports = [],
- supportedTransports = [];
-
- try {
- res = connection._parseResponse(result);
- } catch (error) {
- onFailed(signalR._.error(resources.errorParsingNegotiateResponse, error), connection);
- return;
- }
-
- keepAliveData = connection._.keepAliveData;
- connection.appRelativeUrl = res.Url;
- connection.id = res.ConnectionId;
- connection.token = res.ConnectionToken;
- connection.webSocketServerUrl = res.WebSocketServerUrl;
-
- // Once the server has labeled the PersistentConnection as Disconnected, we should stop attempting to reconnect
- // after res.DisconnectTimeout seconds.
- connection.disconnectTimeout = res.DisconnectTimeout * 1000; // in ms
-
- // Add the TransportConnectTimeout from the response to the transportConnectTimeout from the client to calculate the total timeout
- connection._.totalTransportConnectTimeout = connection.transportConnectTimeout + res.TransportConnectTimeout * 1000;
-
- // If we have a keep alive
- if (res.KeepAliveTimeout) {
- // Register the keep alive data as activated
- keepAliveData.activated = true;
-
- // Timeout to designate when to force the connection into reconnecting converted to milliseconds
- keepAliveData.timeout = res.KeepAliveTimeout * 1000;
-
- // Timeout to designate when to warn the developer that the connection may be dead or is not responding.
- keepAliveData.timeoutWarning = keepAliveData.timeout * connection.keepAliveWarnAt;
-
- // Instantiate the frequency in which we check the keep alive. It must be short in order to not miss/pick up any changes
- connection._.beatInterval = (keepAliveData.timeout - keepAliveData.timeoutWarning) / 3;
- } else {
- keepAliveData.activated = false;
- }
-
- connection.reconnectWindow = connection.disconnectTimeout + (keepAliveData.timeout || 0);
-
- if (!res.ProtocolVersion || res.ProtocolVersion !== connection.clientProtocol) {
- protocolError = signalR._.error(signalR._.format(resources.protocolIncompatible, connection.clientProtocol, res.ProtocolVersion));
- $(connection).triggerHandler(events.onError, [protocolError]);
- deferred.reject(protocolError);
-
- return;
- }
-
- $.each(signalR.transports, function (key) {
- if ((key.indexOf("_") === 0) || (key === "webSockets" && !res.TryWebSockets)) {
- return true;
- }
- supportedTransports.push(key);
- });
-
- if ($.isArray(config.transport)) {
- $.each(config.transport, function (_, transport) {
- if ($.inArray(transport, supportedTransports) >= 0) {
- transports.push(transport);
- }
- });
- } else if (config.transport === "auto") {
- transports = supportedTransports;
- } else if ($.inArray(config.transport, supportedTransports) >= 0) {
- transports.push(config.transport);
- }
-
- initialize(transports);
+ connection._.negotiateRequest = signalR.transports._logic.ajax(connection, {
+ url: url,
+ error: function (error, statusText) {
+ // We don't want to cause any errors if we're aborting our own negotiate request.
+ if (statusText !== _negotiateAbortText) {
+ onFailed(error, connection);
+ } else {
+ // This rejection will noop if the deferred has already been resolved or rejected.
+ deferred.reject(signalR._.error(resources.stoppedWhileNegotiating, null /* error */, connection._.negotiateRequest));
}
+ },
+ success: function (result) {
+ var res,
+ keepAliveData,
+ protocolError,
+ transports = [],
+ supportedTransports = [];
+
+ try {
+ res = connection._parseResponse(result);
+ } catch (error) {
+ onFailed(signalR._.error(resources.errorParsingNegotiateResponse, error), connection);
+ return;
+ }
+
+ keepAliveData = connection._.keepAliveData;
+ connection.appRelativeUrl = res.Url;
+ connection.id = res.ConnectionId;
+ connection.token = res.ConnectionToken;
+ connection.webSocketServerUrl = res.WebSocketServerUrl;
+ connection._.longPollDelay = res.LongPollDelay * 1000; // in ms
+
+ // Once the server has labeled the PersistentConnection as Disconnected, we should stop attempting to reconnect
+ // after res.DisconnectTimeout seconds.
+ connection.disconnectTimeout = res.DisconnectTimeout * 1000; // in ms
+
+ // Add the TransportConnectTimeout from the response to the transportConnectTimeout from the client to calculate the total timeout
+ connection._.totalTransportConnectTimeout = connection.transportConnectTimeout + res.TransportConnectTimeout * 1000;
+
+ // If we have a keep alive
+ if (res.KeepAliveTimeout) {
+ // Register the keep alive data as activated
+ keepAliveData.activated = true;
+
+ // Timeout to designate when to force the connection into reconnecting converted to milliseconds
+ keepAliveData.timeout = res.KeepAliveTimeout * 1000;
+
+ // Timeout to designate when to warn the developer that the connection may be dead or is not responding.
+ keepAliveData.timeoutWarning = keepAliveData.timeout * connection.keepAliveWarnAt;
+
+ // Instantiate the frequency in which we check the keep alive. It must be short in order to not miss/pick up any changes
+ connection._.beatInterval = (keepAliveData.timeout - keepAliveData.timeoutWarning) / 3;
+ } else {
+ keepAliveData.activated = false;
+ }
+
+ connection.reconnectWindow = connection.disconnectTimeout + (keepAliveData.timeout || 0);
+
+ if (!res.ProtocolVersion || res.ProtocolVersion !== connection.clientProtocol) {
+ protocolError = signalR._.error(signalR._.format(resources.protocolIncompatible, connection.clientProtocol, res.ProtocolVersion));
+ $(connection).triggerHandler(events.onError, [protocolError]);
+ deferred.reject(protocolError);
+
+ return;
+ }
+
+ $.each(signalR.transports, function (key) {
+ if ((key.indexOf("_") === 0) || (key === "webSockets" && !res.TryWebSockets)) {
+ return true;
+ }
+ supportedTransports.push(key);
+ });
+
+ if ($.isArray(config.transport)) {
+ $.each(config.transport, function (_, transport) {
+ if ($.inArray(transport, supportedTransports) >= 0) {
+ transports.push(transport);
+ }
+ });
+ } else if (config.transport === "auto") {
+ transports = supportedTransports;
+ } else if ($.inArray(config.transport, supportedTransports) >= 0) {
+ transports.push(config.transport);
+ }
+
+ initialize(transports);
}
- ));
+ });
return deferred.promise();
},
@@ -814,6 +819,7 @@
///
var connection = this;
$(connection).bind(events.onError, function (e, errorData, sendData) {
+ connection.lastError = errorData;
// In practice 'errorData' is the SignalR built error object.
// In practice 'sendData' is undefined for all error events except those triggered by
// 'ajaxSend' and 'webSockets.send'.'sendData' is the original send payload.
@@ -883,7 +889,6 @@
}
// Always clean up private non-timeout based state.
- delete connection._deferral;
delete connection._.config;
delete connection._.deferredStartHandler;
@@ -921,7 +926,7 @@
connection.transport.abort(connection, async);
}
- if (connection.transport.supportsKeepAlive && connection._.keepAliveData.activated) {
+ if (supportsKeepAlive(connection)) {
signalR.transports._logic.stopMonitoringKeepAlive(connection);
}
@@ -934,15 +939,20 @@
delete connection._.negotiateRequest;
}
+ // Ensure that tryAbortStartRequest is called before connection._deferral is deleted
+ signalR.transports._logic.tryAbortStartRequest(connection);
+
// Trigger the disconnect event
$(connection).triggerHandler(events.onDisconnect);
+ delete connection._deferral;
delete connection.messageId;
delete connection.groupsToken;
delete connection.id;
delete connection._.pingIntervalId;
delete connection._.lastMessageAt;
delete connection._.lastActiveAt;
+ delete connection._.longPollDelay;
// Clear out our message buffer
connection._.connectingMessageBuffer.clear();
@@ -980,11 +990,11 @@
///
(function ($, window, undefined) {
- "use strict";
var signalR = $.signalR,
events = $.signalR.events,
changeState = $.signalR.changeState,
+ startAbortText = "__Start Aborted__",
transportLogic;
signalR.transports = {};
@@ -1029,95 +1039,100 @@
}
}
- function addConnectionData(url, connectionData) {
- var appender = url.indexOf("?") !== -1 ? "&" : "?";
+ function getAjaxUrl(connection, path) {
+ var url = connection.url + path;
- if (connectionData) {
- url += appender + "connectionData=" + window.encodeURIComponent(connectionData);
+ if (connection.transport) {
+ url += "?transport=" + connection.transport.name;
}
- return url;
+ return transportLogic.prepareQueryString(connection, url);
}
transportLogic = signalR.transports._logic = {
+ ajax: function (connection, options) {
+ return $.ajax(
+ $.extend(/*deep copy*/ true, {}, $.signalR.ajaxDefaults, {
+ type: "GET",
+ data: {},
+ xhrFields: { withCredentials: connection.withCredentials },
+ contentType: connection.contentType,
+ dataType: connection.ajaxDataType
+ }, options));
+ },
+
pingServer: function (connection) {
/// Pings the server
/// Connection associated with the server ping
///
- var url, deferral = $.Deferred(), xhr;
+ var url,
+ xhr,
+ deferral = $.Deferred();
if (connection.transport) {
url = connection.url + "/ping";
url = transportLogic.addQs(url, connection.qs);
- xhr = $.ajax(
- $.extend({}, $.signalR.ajaxDefaults, {
- xhrFields: { withCredentials: connection.withCredentials },
- url: url,
- type: "GET",
- contentType: connection.contentType,
- data: {},
- dataType: connection.ajaxDataType,
- success: function (result) {
- var data;
+ xhr = transportLogic.ajax(connection, {
+ url: url,
+ success: function (result) {
+ var data;
- try {
- data = connection._parseResponse(result);
- }
- catch (error) {
- deferral.reject(
- signalR._.transportError(
- signalR.resources.pingServerFailedParse,
- connection.transport,
- error,
- xhr
- )
- );
- connection.stop();
- return;
- }
+ try {
+ data = connection._parseResponse(result);
+ }
+ catch (error) {
+ deferral.reject(
+ signalR._.transportError(
+ signalR.resources.pingServerFailedParse,
+ connection.transport,
+ error,
+ xhr
+ )
+ );
+ connection.stop();
+ return;
+ }
- if (data.Response === "pong") {
- deferral.resolve();
- }
- else {
- deferral.reject(
- signalR._.transportError(
- signalR._.format(signalR.resources.pingServerFailedInvalidResponse, result.responseText),
- connection.transport,
- null /* error */,
- xhr
- )
- );
- }
- },
- error: function (error) {
- if (error.status === 401 || error.status === 403) {
- deferral.reject(
- signalR._.transportError(
- signalR._.format(signalR.resources.pingServerFailedStatusCode, error.status),
- connection.transport,
- error,
- xhr
- )
- );
- connection.stop();
- }
- else {
- deferral.reject(
- signalR._.transportError(
- signalR.resources.pingServerFailed,
- connection.transport,
- error,
- xhr
- )
- );
- }
+ if (data.Response === "pong") {
+ deferral.resolve();
+ }
+ else {
+ deferral.reject(
+ signalR._.transportError(
+ signalR._.format(signalR.resources.pingServerFailedInvalidResponse, result),
+ connection.transport,
+ null /* error */,
+ xhr
+ )
+ );
+ }
+ },
+ error: function (error) {
+ if (error.status === 401 || error.status === 403) {
+ deferral.reject(
+ signalR._.transportError(
+ signalR._.format(signalR.resources.pingServerFailedStatusCode, error.status),
+ connection.transport,
+ error,
+ xhr
+ )
+ );
+ connection.stop();
+ }
+ else {
+ deferral.reject(
+ signalR._.transportError(
+ signalR.resources.pingServerFailed,
+ connection.transport,
+ error,
+ xhr
+ )
+ );
}
}
- ));
-
+ });
}
else {
deferral.reject(
@@ -1132,9 +1147,23 @@
},
prepareQueryString: function (connection, url) {
- url = transportLogic.addQs(url, connection.qs);
+ var preparedUrl;
- return addConnectionData(url, connection.data);
+ // Use addQs to start since it handles the ?/& prefix for us
+ preparedUrl = transportLogic.addQs(url, "clientProtocol=" + connection.clientProtocol);
+
+ // Add the user-specified query string params if any
+ preparedUrl = transportLogic.addQs(preparedUrl, connection.qs);
+
+ if (connection.token) {
+ preparedUrl += "&connectionToken=" + window.encodeURIComponent(connection.token);
+ }
+
+ if (connection.data) {
+ preparedUrl += "&connectionData=" + window.encodeURIComponent(connection.data);
+ }
+
+ return preparedUrl;
},
addQs: function (url, qs) {
@@ -1166,7 +1195,7 @@
/// Gets the url for making a GET based connect request
var baseUrl = transport === "webSockets" ? "" : connection.baseUrl,
url = baseUrl + connection.appRelativeUrl,
- qs = "transport=" + transport + "&connectionToken=" + window.encodeURIComponent(connection.token);
+ qs = "transport=" + transport;
if (connection.groupsToken) {
qs += "&groupsToken=" + window.encodeURIComponent(connection.groupsToken);
@@ -1219,52 +1248,47 @@
ajaxSend: function (connection, data) {
var payload = transportLogic.stringifySend(connection, data),
- url = connection.url + "/send" + "?transport=" + connection.transport.name + "&connectionToken=" + window.encodeURIComponent(connection.token),
+ url = getAjaxUrl(connection, "/send"),
xhr,
onFail = function (error, connection) {
$(connection).triggerHandler(events.onError, [signalR._.transportError(signalR.resources.sendFailed, connection.transport, error, xhr), data]);
};
- url = transportLogic.prepareQueryString(connection, url);
- xhr = $.ajax(
- $.extend({}, $.signalR.ajaxDefaults, {
- xhrFields: { withCredentials: connection.withCredentials },
- url: url,
- type: connection.ajaxDataType === "jsonp" ? "GET" : "POST",
- contentType: signalR._.defaultContentType,
- dataType: connection.ajaxDataType,
- data: {
- data: payload
- },
- success: function (result) {
- var res;
+ xhr = transportLogic.ajax(connection, {
+ url: url,
+ type: connection.ajaxDataType === "jsonp" ? "GET" : "POST",
+ contentType: signalR._.defaultContentType,
+ data: {
+ data: payload
+ },
+ success: function (result) {
+ var res;
- if (result) {
- try {
- res = connection._parseResponse(result);
- }
- catch (error) {
- onFail(error, connection);
- connection.stop();
- return;
- }
-
- transportLogic.triggerReceived(connection, res);
+ if (result) {
+ try {
+ res = connection._parseResponse(result);
}
- },
- error: function (error, textStatus) {
- if (textStatus === "abort" || textStatus === "parsererror") {
- // The parsererror happens for sends that don't return any data, and hence
- // don't write the jsonp callback to the response. This is harder to fix on the server
- // so just hack around it on the client for now.
+ catch (error) {
+ onFail(error, connection);
+ connection.stop();
return;
}
- onFail(error, connection);
+ transportLogic.triggerReceived(connection, res);
}
+ },
+ error: function (error, textStatus) {
+ if (textStatus === "abort" || textStatus === "parsererror") {
+ // The parsererror happens for sends that don't return any data, and hence
+ // don't write the jsonp callback to the response. This is harder to fix on the server
+ // so just hack around it on the client for now.
+ return;
+ }
+
+ onFail(error, connection);
}
- ));
+ });
return xhr;
},
@@ -1277,28 +1301,81 @@
// Async by default unless explicitly overidden
async = typeof async === "undefined" ? true : async;
- var url = connection.url + "/abort" + "?transport=" + connection.transport.name + "&connectionToken=" + window.encodeURIComponent(connection.token);
- url = transportLogic.prepareQueryString(connection, url);
+ var url = getAjaxUrl(connection, "/abort");
- $.ajax(
- $.extend({}, $.signalR.ajaxDefaults, {
- xhrFields: { withCredentials: connection.withCredentials },
- url: url,
- async: async,
- timeout: 1000,
- type: "POST",
- contentType: connection.contentType,
- dataType: connection.ajaxDataType,
- data: {}
- }
- ));
+ transportLogic.ajax(connection, {
+ url: url,
+ async: async,
+ timeout: 1000,
+ type: "POST"
+ });
connection.log("Fired ajax abort async = " + async + ".");
},
- tryInitialize: function (persistentResponse, onInitialized) {
+ tryInitialize: function (connection, persistentResponse, onInitialized) {
+ var startUrl,
+ xhr,
+ rejectDeferred = function (error) {
+ var deferred = connection._deferral;
+ if (deferred) {
+ deferred.reject(error);
+ }
+ },
+ triggerStartError = function (error) {
+ $(connection).triggerHandler(events.onError, [error]);
+ rejectDeferred(error);
+ connection.stop();
+ };
+
if (persistentResponse.Initialized) {
- onInitialized();
+ startUrl = getAjaxUrl(connection, "/start");
+
+ xhr = transportLogic.ajax(connection, {
+ url: startUrl,
+ success: function (result) {
+ var data;
+
+ try {
+ data = connection._parseResponse(result);
+ } catch (error) {
+ triggerStartError(signalR._.error(
+ signalR._.format(signalR.resources.errorParsingStartResponse, result),
+ error, xhr));
+ return;
+ }
+
+ if (data.Response === "started") {
+ onInitialized();
+ } else {
+ triggerStartError(signalR._.error(
+ signalR._.format(signalR.resources.invalidStartResponse, result),
+ null /* error */, xhr));
+ }
+ },
+ error: function (error, statusText) {
+ if (statusText !== startAbortText) {
+ triggerStartError(signalR._.error(
+ signalR.resources.errorDuringStartRequest,
+ error, xhr));
+ } else {
+ // Stop has been called
+ rejectDeferred(signalR._.error(
+ signalR.resources.stoppedDuringStartRequest,
+ null /* error */, xhr));
+ }
+ }
+ });
+
+ connection._.startRequest = xhr;
+ }
+ },
+
+ tryAbortStartRequest: function (connection) {
+ if (connection._.startRequest) {
+ // If the start request has already completed this will noop.
+ connection._.startRequest.abort(startAbortText);
+ delete connection._.startRequest;
}
},
@@ -1317,14 +1394,6 @@
if (minData) {
data = transportLogic.maximizePersistentResponse(minData);
- if (data.Disconnect) {
- connection.log("Disconnect command received from server.");
-
- // Disconnected by the server
- connection.stop(false, false);
- return;
- }
-
transportLogic.updateGroups(connection, data.GroupsToken);
if (data.MessageId) {
@@ -1336,7 +1405,7 @@
transportLogic.triggerReceived(connection, message);
});
- transportLogic.tryInitialize(data, onInitialized);
+ transportLogic.tryInitialize(connection, data, onInitialized);
}
}
},
@@ -1423,8 +1492,10 @@
verifyLastActive: function (connection) {
if (new Date().getTime() - connection._.lastActiveAt >= connection.reconnectWindow) {
- connection.log("There has not been an active server connection for an extended period of time. Stopping connection.");
- connection.stop();
+ var message = signalR._.format(signalR.resources.reconnectWindowTimeout, new Date(connection._.lastActiveAt), connection.reconnectWindow);
+ connection.log(message);
+ $(connection).triggerHandler(events.onError, [signalR._.error(message, /* source */ "TimeoutException")]);
+ connection.stop(/* async */ false, /* notifyServer */ false);
return false;
}
@@ -1487,7 +1558,6 @@
///
(function ($, window, undefined) {
- "use strict";
var signalR = $.signalR,
events = $.signalR.events,
@@ -1497,7 +1567,9 @@
signalR.transports.webSockets = {
name: "webSockets",
- supportsKeepAlive: true,
+ supportsKeepAlive: function () {
+ return true;
+ },
send: function (connection, data) {
var payload = transportLogic.stringifySend(connection, data);
@@ -1639,17 +1711,22 @@
///
(function ($, window, undefined) {
- "use strict";
var signalR = $.signalR,
events = $.signalR.events,
changeState = $.signalR.changeState,
- transportLogic = signalR.transports._logic;
+ transportLogic = signalR.transports._logic,
+ clearReconnectAttemptTimeout = function (connection) {
+ window.clearTimeout(connection._.reconnectAttemptTimeoutHandle);
+ delete connection._.reconnectAttemptTimeoutHandle;
+ };
signalR.transports.serverSentEvents = {
name: "serverSentEvents",
- supportsKeepAlive: true,
+ supportsKeepAlive: function () {
+ return true;
+ },
timeOut: 3000,
@@ -1658,8 +1735,7 @@
opened = false,
$connection = $(connection),
reconnecting = !onSuccess,
- url,
- reconnectTimeout;
+ url;
if (connection.eventSource) {
connection.log("The connection already has an event source. Stopping it.");
@@ -1696,7 +1772,7 @@
}
if (reconnecting) {
- reconnectTimeout = window.setTimeout(function () {
+ connection._.reconnectAttemptTimeoutHandle = window.setTimeout(function () {
if (opened === false) {
// If we're reconnecting and the event source is attempting to connect,
// don't keep retrying. This causes duplicate connections to spawn.
@@ -1712,10 +1788,7 @@
connection.eventSource.addEventListener("open", function (e) {
connection.log("EventSource connected.");
- if (reconnectTimeout) {
- window.clearTimeout(reconnectTimeout);
- }
-
+ clearReconnectAttemptTimeout(connection);
transportLogic.clearReconnectTimeout(connection);
if (opened === false) {
@@ -1795,6 +1868,7 @@
stop: function (connection) {
// Don't trigger a reconnect after stopping
+ clearReconnectAttemptTimeout(connection);
transportLogic.clearReconnectTimeout(connection);
if (connection && connection.eventSource) {
@@ -1818,7 +1892,6 @@
///
(function ($, window, undefined) {
- "use strict";
var signalR = $.signalR,
events = $.signalR.events,
@@ -1873,7 +1946,9 @@
signalR.transports.foreverFrame = {
name: "foreverFrame",
- supportsKeepAlive: true,
+ supportsKeepAlive: function () {
+ return true;
+ },
// Added as a value here so we can create tests to verify functionality
iframeClearThreshold: 50,
@@ -1963,9 +2038,21 @@
receive: function (connection, data) {
var cw,
- body;
+ body,
+ response;
- transportLogic.processMessages(connection, data, connection.onSuccess);
+ if (connection.json !== connection._originalJson) {
+ // If there's a custom JSON parser configured then serialize the object
+ // using the original (browser) JSON parser and then deserialize it using
+ // the custom parser (connection._parseResponse does that). This is so we
+ // can easily send the response from the server as "raw" JSON but still
+ // support custom JSON deserialization in the browser.
+ data = connection._originalJson.stringify(data);
+ }
+
+ response = connection._parseResponse(data);
+
+ transportLogic.processMessages(connection, response, connection.onSuccess);
// Protect against connection stopping from a callback trigger within the processMessages above.
if (connection.state === $.signalR.connectionState.connected) {
@@ -2049,18 +2136,33 @@
///
(function ($, window, undefined) {
- "use strict";
var signalR = $.signalR,
events = $.signalR.events,
changeState = $.signalR.changeState,
isDisconnecting = $.signalR.isDisconnecting,
- transportLogic = signalR.transports._logic;
+ transportLogic = signalR.transports._logic,
+ browserSupportsXHRProgress = (function () {
+ try {
+ return "onprogress" in new window.XMLHttpRequest();
+ } catch (e) {
+ // No XHR means no XHR progress event
+ return false;
+ }
+ })();
signalR.transports.longPolling = {
name: "longPolling",
- supportsKeepAlive: false,
+ supportsKeepAlive: function (connection) {
+ return browserSupportsXHRProgress &&
+ connection.ajaxDataType !== "jsonp" &&
+ // Don't check for keep alives if there is a delay configured between poll requests.
+ // Don't check for keep alives if the server didn't send back the "LongPollDelay" as
+ // part of the response to /negotiate. That indicates the server is running an older
+ // version of SignalR that doesn't send long polling keep alives.
+ connection._.longPollDelay === 0;
+ },
reconnectDelay: 3000,
@@ -2071,10 +2173,11 @@
fireConnect = function () {
fireConnect = $.noop;
- connection.log("LongPolling connected.");
- onSuccess();
// Reset onFailed to null because it shouldn't be called again
onFailed = null;
+
+ connection.log("LongPolling connected.");
+ onSuccess();
},
tryFailConnect = function () {
if (onFailed) {
@@ -2126,123 +2229,122 @@
}
connection.log("Opening long polling request to '" + url + "'.");
- instance.pollXhr = $.ajax(
- $.extend({}, $.signalR.ajaxDefaults, {
- xhrFields: { withCredentials: connection.withCredentials },
- url: url,
- type: "GET",
- dataType: connection.ajaxDataType,
- contentType: connection.contentType,
- success: function (result) {
- var minData,
- delay = 0,
- data,
- shouldReconnect;
+ instance.pollXhr = transportLogic.ajax(connection, {
+ xhrFields: {
+ onprogress: function () {
+ transportLogic.markLastMessage(connection);
+ }
+ },
+ url: url,
+ success: function (result) {
+ var minData,
+ delay = 0,
+ data,
+ shouldReconnect;
- connection.log("Long poll complete.");
+ connection.log("Long poll complete.");
- // Reset our reconnect errors so if we transition into a reconnecting state again we trigger
- // reconnected quickly
- reconnectErrors = 0;
+ // Reset our reconnect errors so if we transition into a reconnecting state again we trigger
+ // reconnected quickly
+ reconnectErrors = 0;
- try {
- minData = connection._parseResponse(result);
- }
- catch (error) {
- transportLogic.handleParseFailure(instance, result, error, tryFailConnect, instance.pollXhr);
+ try {
+ // Remove any keep-alives from the beginning of the result
+ minData = connection._parseResponse(result);
+ }
+ catch (error) {
+ transportLogic.handleParseFailure(instance, result, error, tryFailConnect, instance.pollXhr);
+ return;
+ }
+
+ // If there's currently a timeout to trigger reconnect, fire it now before processing messages
+ if (privateData.reconnectTimeoutId !== null) {
+ fireReconnected(instance);
+ }
+
+ if (minData) {
+ data = transportLogic.maximizePersistentResponse(minData);
+ }
+
+ transportLogic.processMessages(instance, minData, fireConnect);
+
+ if (data &&
+ $.type(data.LongPollDelay) === "number") {
+ delay = data.LongPollDelay;
+ }
+
+ if (data && data.Disconnect) {
+ return;
+ }
+
+ if (isDisconnecting(instance) === true) {
+ return;
+ }
+
+ shouldReconnect = data && data.ShouldReconnect;
+ if (shouldReconnect) {
+ // Transition into the reconnecting state
+ // If this fails then that means that the user transitioned the connection into a invalid state in processMessages.
+ if (!transportLogic.ensureReconnectingState(instance)) {
return;
}
-
- // If there's currently a timeout to trigger reconnect, fire it now before processing messages
- if (privateData.reconnectTimeoutId !== null) {
- fireReconnected(instance);
- }
-
- if (minData) {
- data = transportLogic.maximizePersistentResponse(minData);
- }
-
- transportLogic.processMessages(instance, minData, fireConnect);
-
- if (data &&
- $.type(data.LongPollDelay) === "number") {
- delay = data.LongPollDelay;
- }
-
- if (data && data.Disconnect) {
- return;
- }
-
- if (isDisconnecting(instance) === true) {
- return;
- }
-
- shouldReconnect = data && data.ShouldReconnect;
- if (shouldReconnect) {
- // Transition into the reconnecting state
- // If this fails then that means that the user transitioned the connection into a invalid state in processMessages.
- if (!transportLogic.ensureReconnectingState(instance)) {
- return;
- }
- }
-
- // We never want to pass a raiseReconnect flag after a successful poll. This is handled via the error function
- if (delay > 0) {
- privateData.pollTimeoutId = window.setTimeout(function () {
- poll(instance, shouldReconnect);
- }, delay);
- } else {
- poll(instance, shouldReconnect);
- }
- },
-
- error: function (data, textStatus) {
- // Stop trying to trigger reconnect, connection is in an error state
- // If we're not in the reconnect state this will noop
- window.clearTimeout(privateData.reconnectTimeoutId);
- privateData.reconnectTimeoutId = null;
-
- if (textStatus === "abort") {
- connection.log("Aborted xhr request.");
- return;
- }
-
- if (!tryFailConnect()) {
-
- // Increment our reconnect errors, we assume all errors to be reconnect errors
- // In the case that it's our first error this will cause Reconnect to be fired
- // after 1 second due to reconnectErrors being = 1.
- reconnectErrors++;
-
- if (connection.state !== signalR.connectionState.reconnecting) {
- connection.log("An error occurred using longPolling. Status = " + textStatus + ". Response = " + data.responseText + ".");
- $(instance).triggerHandler(events.onError, [signalR._.transportError(signalR.resources.longPollFailed, connection.transport, data, instance.pollXhr)]);
- }
-
- // We check the state here to verify that we're not in an invalid state prior to verifying Reconnect.
- // If we're not in connected or reconnecting then the next ensureReconnectingState check will fail and will return.
- // Therefore we don't want to change that failure code path.
- if ((connection.state === signalR.connectionState.connected ||
- connection.state === signalR.connectionState.reconnecting) &&
- !transportLogic.verifyLastActive(connection)) {
- return;
- }
-
- // Transition into the reconnecting state
- // If this fails then that means that the user transitioned the connection into the disconnected or connecting state within the above error handler trigger.
- if (!transportLogic.ensureReconnectingState(instance)) {
- return;
- }
-
- // Call poll with the raiseReconnect flag as true after the reconnect delay
- privateData.pollTimeoutId = window.setTimeout(function () {
- poll(instance, true);
- }, that.reconnectDelay);
- }
}
- }
- ));
+ // We never want to pass a raiseReconnect flag after a successful poll. This is handled via the error function
+ if (delay > 0) {
+ privateData.pollTimeoutId = window.setTimeout(function () {
+ poll(instance, shouldReconnect);
+ }, delay);
+ } else {
+ poll(instance, shouldReconnect);
+ }
+ },
+
+ error: function (data, textStatus) {
+ // Stop trying to trigger reconnect, connection is in an error state
+ // If we're not in the reconnect state this will noop
+ window.clearTimeout(privateData.reconnectTimeoutId);
+ privateData.reconnectTimeoutId = null;
+
+ if (textStatus === "abort") {
+ connection.log("Aborted xhr request.");
+ return;
+ }
+
+ if (!tryFailConnect()) {
+
+ // Increment our reconnect errors, we assume all errors to be reconnect errors
+ // In the case that it's our first error this will cause Reconnect to be fired
+ // after 1 second due to reconnectErrors being = 1.
+ reconnectErrors++;
+
+ if (connection.state !== signalR.connectionState.reconnecting) {
+ connection.log("An error occurred using longPolling. Status = " + textStatus + ". Response = " + data.responseText + ".");
+ $(instance).triggerHandler(events.onError, [signalR._.transportError(signalR.resources.longPollFailed, connection.transport, data, instance.pollXhr)]);
+ }
+
+ // We check the state here to verify that we're not in an invalid state prior to verifying Reconnect.
+ // If we're not in connected or reconnecting then the next ensureReconnectingState check will fail and will return.
+ // Therefore we don't want to change that failure code path.
+ if ((connection.state === signalR.connectionState.connected ||
+ connection.state === signalR.connectionState.reconnecting) &&
+ !transportLogic.verifyLastActive(connection)) {
+ return;
+ }
+
+ // Transition into the reconnecting state
+ // If this fails then that means that the user transitioned the connection into the disconnected or connecting state within the above error handler trigger.
+ if (!transportLogic.ensureReconnectingState(instance)) {
+ return;
+ }
+
+ // Call poll with the raiseReconnect flag as true after the reconnect delay
+ privateData.pollTimeoutId = window.setTimeout(function () {
+ poll(instance, true);
+ }, that.reconnectDelay);
+ }
+ }
+ });
// This will only ever pass after an error has occured via the poll ajax procedure.
if (reconnecting && raiseReconnect === true) {
@@ -2258,7 +2360,9 @@
},
lostConnection: function (connection) {
- throw new Error("Lost Connection not handled for LongPolling");
+ if (connection.pollXhr) {
+ connection.pollXhr.abort("lostConnection");
+ }
},
send: function (connection, data) {
@@ -2295,7 +2399,6 @@
///
(function ($, window, undefined) {
- "use strict";
var eventNamespace = ".hubProxy",
signalR = $.signalR;
@@ -2336,11 +2439,11 @@
///
var callbacks = connection._.invocationCallbacks,
callback;
-
+
if (hasMembers(callbacks)) {
connection.log("Clearing hub invocation callbacks with error: " + error + ".");
}
-
+
// Reset the callback cache now as we have a local var referencing it
connection._.invocationCallbackId = 0;
delete connection._.invocationCallbacks;
@@ -2458,7 +2561,15 @@
// Update the hub state
$.extend(that.state, result.State);
- if (result.Error) {
+ if (result.Progress) {
+ if (d.notifyWith) {
+ // Progress is only supported in jQuery 1.7+
+ d.notifyWith(that, [result.Progress.Data]);
+ } else if(!connection._.progressjQueryVersionLogged) {
+ connection.log("A hub method invocation progress update was received but the version of jQuery in use (" + $.prototype.jquery + ") does not support progress updates. Upgrade to jQuery 1.7+ to receive progress notifications.");
+ connection._.progressjQueryVersionLogged = true;
+ }
+ } else if (result.Error) {
// Server hub method threw an exception, log it & reject the deferred
if (result.StackTrace) {
connection.log(result.Error + "\n" + result.StackTrace + ".");
@@ -2484,7 +2595,7 @@
if (!$.isEmptyObject(that.state)) {
data.S = that.state;
}
-
+
connection.log("Invoking " + that.hubName + "." + methodName);
connection.send(data);
@@ -2495,6 +2606,10 @@
return {
State: minHubResponse.S,
Result: minHubResponse.R,
+ Progress: minHubResponse.P ? {
+ Id: minHubResponse.P.I,
+ Data: minHubResponse.P.D
+ } : null,
Id: minHubResponse.I,
IsHubException: minHubResponse.H,
Error: minHubResponse.E,
@@ -2553,7 +2668,17 @@
return;
}
- if (typeof (minData.I) !== "undefined") {
+ // We have to handle progress updates first in order to ensure old clients that receive
+ // progress updates enter the return value branch and then no-op when they can't find
+ // the callback in the map (because the minData.I value will not be a valid callback ID)
+ if (typeof (minData.P) !== "undefined") {
+ // Process progress notification
+ dataCallbackId = minData.P.I.toString();
+ callback = connection._.invocationCallbacks[dataCallbackId];
+ if (callback) {
+ callback.method.call(callback.scope, minData);
+ }
+ } else if (typeof (minData.I) !== "undefined") {
// We received the return value from a server method invocation, look up callback by id and call it
dataCallbackId = minData.I.toString();
callback = connection._.invocationCallbacks[dataCallbackId];
@@ -2690,12 +2815,12 @@
/*global window:false */
///
(function ($, undefined) {
- $.signalR.version = "2.0.3";
+ $.signalR.version = "2.1.0";
}(window.jQuery));
///#source 1 1 /ClientSource/Scripts/Modules/jQuery-SignalR/disco-hubs.js
/*!
- * ASP.NET SignalR JavaScript Library v2.0.3
+ * ASP.NET SignalR JavaScript Library v2.1.0
* http://signalr.net/
*
* Copyright Microsoft Open Technologies, Inc. All rights reserved.
@@ -2774,37 +2899,38 @@
registerHubProxies(proxies, false);
});
- proxies.deviceUpdates = this.createHubProxy('deviceUpdates');
- proxies.deviceUpdates.client = { };
- proxies.deviceUpdates.server = {
+ proxies['deviceUpdates'] = this.createHubProxy('deviceUpdates');
+ proxies['deviceUpdates'].client = { };
+ proxies['deviceUpdates'].server = {
};
- proxies.jobUpdates = this.createHubProxy('jobUpdates');
- proxies.jobUpdates.client = { };
- proxies.jobUpdates.server = {
+ proxies['jobUpdates'] = this.createHubProxy('jobUpdates');
+ proxies['jobUpdates'].client = { };
+ proxies['jobUpdates'].server = {
};
- proxies.logNotifications = this.createHubProxy('logNotifications');
- proxies.logNotifications.client = { };
- proxies.logNotifications.server = {
+ proxies['logNotifications'] = this.createHubProxy('logNotifications');
+ proxies['logNotifications'].client = { };
+ proxies['logNotifications'].server = {
};
- proxies.noticeboardUpdates = this.createHubProxy('noticeboardUpdates');
- proxies.noticeboardUpdates.client = { };
- proxies.noticeboardUpdates.server = {
+ proxies['noticeboardUpdates'] = this.createHubProxy('noticeboardUpdates');
+ proxies['noticeboardUpdates'].client = { };
+ proxies['noticeboardUpdates'].server = {
};
- proxies.scheduledTaskNotifications = this.createHubProxy('scheduledTaskNotifications');
- proxies.scheduledTaskNotifications.client = { };
- proxies.scheduledTaskNotifications.server = {
+ proxies['scheduledTaskNotifications'] = this.createHubProxy('scheduledTaskNotifications');
+ proxies['scheduledTaskNotifications'].client = { };
+ proxies['scheduledTaskNotifications'].server = {
getStatus: function () {
- return proxies.scheduledTaskNotifications.invoke.apply(proxies.scheduledTaskNotifications, $.merge(["GetStatus"], $.makeArray(arguments)));
+ /// Calls the GetStatus method on the server-side scheduledTaskNotifications hub.
Returns a jQuery.Deferred() promise.
+ return proxies['scheduledTaskNotifications'].invoke.apply(proxies['scheduledTaskNotifications'], $.merge(["GetStatus"], $.makeArray(arguments)));
}
};
- proxies.userUpdates = this.createHubProxy('userUpdates');
- proxies.userUpdates.client = { };
- proxies.userUpdates.server = {
+ proxies['userUpdates'] = this.createHubProxy('userUpdates');
+ proxies['userUpdates'].client = { };
+ proxies['userUpdates'].server = {
};
return proxies;
diff --git a/Disco.Web/ClientSource/Scripts/Modules/jQuery-SignalR.js.bundle b/Disco.Web/ClientSource/Scripts/Modules/jQuery-SignalR.js.bundle
index 19bdb425..dac8fd68 100644
--- a/Disco.Web/ClientSource/Scripts/Modules/jQuery-SignalR.js.bundle
+++ b/Disco.Web/ClientSource/Scripts/Modules/jQuery-SignalR.js.bundle
@@ -1,5 +1,5 @@
- /ClientSource/Scripts/Modules/jQuery-SignalR/jquery.signalR-2.0.3.js
+ /ClientSource/Scripts/Modules/jQuery-SignalR/jquery.signalR-2.1.0.js
/ClientSource/Scripts/Modules/jQuery-SignalR/disco-hubs.js
\ No newline at end of file
diff --git a/Disco.Web/ClientSource/Scripts/Modules/jQuery-SignalR.min.js b/Disco.Web/ClientSource/Scripts/Modules/jQuery-SignalR.min.js
index a1a221af..9282beb9 100644
--- a/Disco.Web/ClientSource/Scripts/Modules/jQuery-SignalR.min.js
+++ b/Disco.Web/ClientSource/Scripts/Modules/jQuery-SignalR.min.js
@@ -1,4 +1,4 @@
-(function(n,t,i){"use strict";function p(t,i){var u,f;if(n.isArray(t)){for(u=t.length-1;u>=0;u--)f=t[u],n.type(f)==="string"&&r.transports[f]||(i.log("Invalid transport: "+f+", removing it from the transports list."),t.splice(u,1));t.length===0&&(i.log("No transports remain within the specified transport array."),t=null)}else if(r.transports[t]||t==="auto"){if(t==="auto"&&r._.ieVersion<=8)return["longPolling"]}else i.log("Invalid transport: "+t.toString()+"."),t=null;return t}function w(n){return n==="http:"?80:n==="https:"?443:void 0}function l(n,t){return t.match(/:\d+$/)?t:t+":"+w(n)}function b(t,i){var u=this,r=[];u.tryBuffer=function(i){return t.state===n.signalR.connectionState.connecting?(r.push(i),!0):!1};u.drain=function(){if(t.state===n.signalR.connectionState.connected)while(r.length>0)i(r.shift())};u.clear=function(){r=[]}}var f={nojQuery:"jQuery was not found. Please ensure jQuery is referenced before the SignalR client JavaScript file.",noTransportOnInit:"No transport could be initialized successfully. Try specifying a different transport or none at all for auto initialization.",errorOnNegotiate:"Error during negotiation request.",stoppedWhileLoading:"The connection was stopped during page load.",stoppedWhileNegotiating:"The connection was stopped during the negotiate request.",errorParsingNegotiateResponse:"Error parsing negotiate response.",protocolIncompatible:"You are using a version of the client that isn't compatible with the server. Client version {0}, server version {1}.",sendFailed:"Send failed.",parseFailed:"Failed at parsing response: {0}",longPollFailed:"Long polling request failed.",eventSourceFailedToConnect:"EventSource failed to connect.",eventSourceError:"Error raised by EventSource",webSocketClosed:"WebSocket closed.",pingServerFailedInvalidResponse:"Invalid ping response when pinging server: '{0}'.",pingServerFailed:"Failed to ping server.",pingServerFailedStatusCode:"Failed to ping server. Server responded with status code {0}, stopping the connection.",pingServerFailedParse:"Failed to parse ping server response, stopping the connection.",noConnectionTransport:"Connection is in an invalid state, there is no transport active.",webSocketsInvalidState:"The Web Socket transport is in an invalid state, transitioning into reconnecting."};if(typeof n!="function")throw new Error(f.nojQuery);var r,h,s=t.document.readyState==="complete",e=n(t),c="__Negotiate Aborted__",u={onStart:"onStart",onStarting:"onStarting",onReceived:"onReceived",onError:"onError",onConnectionSlow:"onConnectionSlow",onReconnecting:"onReconnecting",onReconnect:"onReconnect",onStateChanged:"onStateChanged",onDisconnect:"onDisconnect"},a=function(n,i){if(i!==!1){var r;typeof t.console!="undefined"&&(r="["+(new Date).toTimeString()+"] SignalR: "+n,t.console.debug?t.console.debug(r):t.console.log&&t.console.log(r))}},o=function(t,i,r){return i===t.state?(t.state=r,n(t).triggerHandler(u.onStateChanged,[{oldState:i,newState:r}]),!0):!1},v=function(n){return n.state===r.connectionState.disconnected},y=function(n){var i,u;n._.configuredStopReconnectingTimeout||(u=function(n){n.log("Couldn't reconnect within the configured timeout ("+n.disconnectTimeout+"ms), disconnecting.");n.stop(!1,!1)},n.reconnecting(function(){var n=this;n.state===r.connectionState.reconnecting&&(i=t.setTimeout(function(){u(n)},n.disconnectTimeout))}),n.stateChanged(function(n){n.oldState===r.connectionState.reconnecting&&t.clearTimeout(i)}),n._.configuredStopReconnectingTimeout=!0)};r=function(n,t,i){return new r.fn.init(n,t,i)};r._={defaultContentType:"application/x-www-form-urlencoded; charset=UTF-8",ieVersion:function(){var i,n;return t.navigator.appName==="Microsoft Internet Explorer"&&(n=/MSIE ([0-9]+\.[0-9]+)/.exec(t.navigator.userAgent),n&&(i=t.parseFloat(n[1]))),i}(),error:function(n,t,i){var r=new Error(n);return r.source=t,typeof i!="undefined"&&(r.context=i),r},transportError:function(n,t,r,u){var f=this.error(n,r,u);return f.transport=t?t.name:i,f},format:function(){for(var t=arguments[0],n=0;n<\/script>.");}};e.load(function(){s=!0});r.fn=r.prototype={init:function(t,i,r){var f=n(this);this.url=t;this.qs=i;this._={keepAliveData:{},connectingMessageBuffer:new b(this,function(n){f.triggerHandler(u.onReceived,[n])}),onFailedTimeoutHandle:null,lastMessageAt:(new Date).getTime(),lastActiveAt:(new Date).getTime(),beatInterval:5e3,beatHandle:null,totalTransportConnectTimeout:0};typeof r=="boolean"&&(this.logging=r)},_parseResponse:function(n){var t=this;return n?typeof n=="string"?t.json.parse(n):n:n},json:t.JSON,isCrossDomain:function(i,r){var u;return(i=n.trim(i),r=r||t.location,i.indexOf("http")!==0)?!1:(u=t.document.createElement("a"),u.href=i,u.protocol+l(u.protocol,u.host)!==r.protocol+l(r.protocol,r.host))},ajaxDataType:"text",contentType:"application/json; charset=UTF-8",logging:!1,state:r.connectionState.disconnected,clientProtocol:"1.3",reconnectDelay:2e3,transportConnectTimeout:0,disconnectTimeout:3e4,reconnectWindow:3e4,keepAliveWarnAt:2/3,start:function(i,h){var l=this,a={pingInterval:3e5,waitForPageLoad:!0,transport:"auto",jsonp:!1},k,v=l._deferral||n.Deferred(),w=t.document.createElement("a"),b,d;if(l._deferral=v,!l.json)throw new Error("SignalR: No JSON parser found. Please ensure json2.js is referenced before the SignalR.js file if you need to support clients without native JSON parsing support, e.g. IE<8.");if(n.type(i)==="function"?h=i:n.type(i)==="object"&&(n.extend(a,i),n.type(a.callback)==="function"&&(h=a.callback)),a.transport=p(a.transport,l),!a.transport)throw new Error("SignalR: Invalid transport(s) specified, aborting start.");return(l._.config=a,!s&&a.waitForPageLoad===!0)?(l._.deferredStartHandler=function(){l.start(i,h)},e.bind("load",l._.deferredStartHandler),v.promise()):l.state===r.connectionState.connecting?v.promise():o(l,r.connectionState.disconnected,r.connectionState.connecting)===!1?(v.resolve(l),v.promise()):(y(l),w.href=l.url,w.protocol&&w.protocol!==":"?(l.protocol=w.protocol,l.host=w.host,l.baseUrl=w.protocol+"//"+w.host):(l.protocol=t.document.location.protocol,l.host=t.document.location.host,l.baseUrl=l.protocol+"//"+l.host),l.wsProtocol=l.protocol==="https:"?"wss://":"ws://",a.transport==="auto"&&a.jsonp===!0&&(a.transport="longPolling"),l.url.indexOf("//")===0&&(l.url=t.location.protocol+l.url,l.log("Protocol relative URL detected, normalizing it to '"+l.url+"'.")),this.isCrossDomain(l.url)&&(l.log("Auto detected cross domain url."),a.transport==="auto"&&(a.transport=["webSockets","serverSentEvents","longPolling"]),typeof a.withCredentials=="undefined"&&(a.withCredentials=!0),a.jsonp||(a.jsonp=!n.support.cors,a.jsonp&&l.log("Using jsonp because this browser doesn't support CORS.")),l.contentType=r._.defaultContentType),l.withCredentials=a.withCredentials,l.ajaxDataType=a.jsonp?"jsonp":"text",n(l).bind(u.onStart,function(){n.type(h)==="function"&&h.call(l);v.resolve(l)}),k=function(i,s){var y=r._.error(f.noTransportOnInit);if(s=s||0,s>=i.length){n(l).triggerHandler(u.onError,[y]);v.reject(y);l.stop();return}if(l.state!==r.connectionState.disconnected){var p=i[s],h=r.transports[p],c=!1,a=function(){c||(c=!0,t.clearTimeout(l._.onFailedTimeoutHandle),h.stop(l),k(i,s+1))};l.transport=h;try{l._.onFailedTimeoutHandle=t.setTimeout(function(){l.log(h.name+" timed out when trying to connect.");a()},l._.totalTransportConnectTimeout);h.start(l,function(){var i=r._.firefoxMajorVersion(t.navigator.userAgent)>=11,f=!!l.withCredentials&&i;l.state!==r.connectionState.disconnected&&(c||(c=!0,t.clearTimeout(l._.onFailedTimeoutHandle),h.supportsKeepAlive&&l._.keepAliveData.activated&&r.transports._logic.monitorKeepAlive(l),r.transports._logic.startHeartbeat(l),r._.configurePingInterval(l),o(l,r.connectionState.connecting,r.connectionState.connected),l._.connectingMessageBuffer.drain(),n(l).triggerHandler(u.onStart),e.bind("unload",function(){l.log("Window unloading, stopping the connection.");l.stop(f)}),i&&e.bind("beforeunload",function(){t.setTimeout(function(){l.stop(f)},0)})))},a)}catch(w){l.log(h.name+" transport threw '"+w.message+"' when attempting to start.");a()}}},b=l.url+"/negotiate",d=function(t,i){var e=r._.error(f.errorOnNegotiate,t,i._.negotiateRequest);n(i).triggerHandler(u.onError,e);v.reject(e);i.stop()},n(l).triggerHandler(u.onStarting),b=r.transports._logic.prepareQueryString(l,b),b=r.transports._logic.addQs(b,{clientProtocol:l.clientProtocol}),l.log("Negotiating with '"+b+"'."),l._.negotiateRequest=n.ajax(n.extend({},n.signalR.ajaxDefaults,{xhrFields:{withCredentials:l.withCredentials},url:b,type:"GET",contentType:l.contentType,data:{},dataType:l.ajaxDataType,error:function(n,t){t!==c?d(n,l):v.reject(r._.error(f.stoppedWhileNegotiating,null,l._.negotiateRequest))},success:function(t){var i,e,h,o=[],s=[];try{i=l._parseResponse(t)}catch(c){d(r._.error(f.errorParsingNegotiateResponse,c),l);return}if(e=l._.keepAliveData,l.appRelativeUrl=i.Url,l.id=i.ConnectionId,l.token=i.ConnectionToken,l.webSocketServerUrl=i.WebSocketServerUrl,l.disconnectTimeout=i.DisconnectTimeout*1e3,l._.totalTransportConnectTimeout=l.transportConnectTimeout+i.TransportConnectTimeout*1e3,i.KeepAliveTimeout?(e.activated=!0,e.timeout=i.KeepAliveTimeout*1e3,e.timeoutWarning=e.timeout*l.keepAliveWarnAt,l._.beatInterval=(e.timeout-e.timeoutWarning)/3):e.activated=!1,l.reconnectWindow=l.disconnectTimeout+(e.timeout||0),!i.ProtocolVersion||i.ProtocolVersion!==l.clientProtocol){h=r._.error(r._.format(f.protocolIncompatible,l.clientProtocol,i.ProtocolVersion));n(l).triggerHandler(u.onError,[h]);v.reject(h);return}n.each(r.transports,function(n){if(n.indexOf("_")===0||n==="webSockets"&&!i.TryWebSockets)return!0;s.push(n)});n.isArray(a.transport)?n.each(a.transport,function(t,i){n.inArray(i,s)>=0&&o.push(i)}):a.transport==="auto"?o=s:n.inArray(a.transport,s)>=0&&o.push(a.transport);k(o)}})),v.promise())},starting:function(t){var i=this;return n(i).bind(u.onStarting,function(){t.call(i)}),i},send:function(n){var t=this;if(t.state===r.connectionState.disconnected)throw new Error("SignalR: Connection must be started before data can be sent. Call .start() before .send()");if(t.state===r.connectionState.connecting)throw new Error("SignalR: Connection has not been fully initialized. Use .start().done() or .start().fail() to run logic after the connection has started.");return t.transport.send(t,n),t},received:function(t){var i=this;return n(i).bind(u.onReceived,function(n,r){t.call(i,r)}),i},stateChanged:function(t){var i=this;return n(i).bind(u.onStateChanged,function(n,r){t.call(i,r)}),i},error:function(t){var i=this;return n(i).bind(u.onError,function(n,r,u){t.call(i,r,u)}),i},disconnected:function(t){var i=this;return n(i).bind(u.onDisconnect,function(){t.call(i)}),i},connectionSlow:function(t){var i=this;return n(i).bind(u.onConnectionSlow,function(){t.call(i)}),i},reconnecting:function(t){var i=this;return n(i).bind(u.onReconnecting,function(){t.call(i)}),i},reconnected:function(t){var i=this;return n(i).bind(u.onReconnect,function(){t.call(i)}),i},stop:function(i,h){var l=this,a=l._deferral;if(l._.deferredStartHandler&&e.unbind("load",l._.deferredStartHandler),delete l._deferral,delete l._.config,delete l._.deferredStartHandler,!s&&(!l._.config||l._.config.waitForPageLoad===!0)){l.log("Stopping connection prior to negotiate.");a&&a.reject(r._.error(f.stoppedWhileLoading));return}if(l.state!==r.connectionState.disconnected)return l.log("Stopping connection."),o(l,l.state,r.connectionState.disconnected),t.clearTimeout(l._.beatHandle),t.clearTimeout(l._.onFailedTimeoutHandle),t.clearInterval(l._.pingIntervalId),l.transport&&(l.transport.stop(l),h!==!1&&l.transport.abort(l,i),l.transport.supportsKeepAlive&&l._.keepAliveData.activated&&r.transports._logic.stopMonitoringKeepAlive(l),l.transport=null),l._.negotiateRequest&&(l._.negotiateRequest.abort(c),delete l._.negotiateRequest),n(l).triggerHandler(u.onDisconnect),delete l.messageId,delete l.groupsToken,delete l.id,delete l._.pingIntervalId,delete l._.lastMessageAt,delete l._.lastActiveAt,l._.connectingMessageBuffer.clear(),l},log:function(n){a(n,this.logging)}};r.fn.init.prototype=r.fn;r.noConflict=function(){return n.connection===r&&(n.connection=h),r};n.connection&&(h=n.connection);n.connection=n.signalR=r})(window.jQuery,window),function(n,t){"use strict";function f(n){n._.keepAliveData.monitoring&&o(n);r.markActive(n)&&(n._.beatHandle=t.setTimeout(function(){f(n)},n._.beatInterval))}function o(t){var r=t._.keepAliveData,f;t.state===i.connectionState.connected&&(f=(new Date).getTime()-t._.lastMessageAt,f>=r.timeout?(t.log("Keep alive timed out. Notifying transport that connection has been lost."),t.transport.lostConnection(t)):f>=r.timeoutWarning?r.userNotified||(t.log("Keep alive has been missed, connection may be dead/slow."),n(t).triggerHandler(u.onConnectionSlow),r.userNotified=!0):r.userNotified=!1)}function s(n,i){var r=n.indexOf("?")!==-1?"&":"?";return i&&(n+=r+"connectionData="+t.encodeURIComponent(i)),n}var i=n.signalR,u=n.signalR.events,e=n.signalR.changeState,r;i.transports={};r=i.transports._logic={pingServer:function(t){var e,u=n.Deferred(),f;return t.transport?(e=t.url+"/ping",e=r.addQs(e,t.qs),f=n.ajax(n.extend({},n.signalR.ajaxDefaults,{xhrFields:{withCredentials:t.withCredentials},url:e,type:"GET",contentType:t.contentType,data:{},dataType:t.ajaxDataType,success:function(n){var r;try{r=t._parseResponse(n)}catch(e){u.reject(i._.transportError(i.resources.pingServerFailedParse,t.transport,e,f));t.stop();return}r.Response==="pong"?u.resolve():u.reject(i._.transportError(i._.format(i.resources.pingServerFailedInvalidResponse,n.responseText),t.transport,null,f))},error:function(n){n.status===401||n.status===403?(u.reject(i._.transportError(i._.format(i.resources.pingServerFailedStatusCode,n.status),t.transport,n,f)),t.stop()):u.reject(i._.transportError(i.resources.pingServerFailed,t.transport,n,f))}}))):u.reject(i._.transportError(i.resources.noConnectionTransport,t.transport)),u.promise()},prepareQueryString:function(n,t){return t=r.addQs(t,n.qs),s(t,n.data)},addQs:function(t,i){var r=t.indexOf("?")!==-1?"&":"?",u;if(!i)return t;if(typeof i=="object")return t+r+n.param(i);if(typeof i=="string")return u=i.charAt(0),(u==="?"||u==="&")&&(r=""),t+r+i;throw new Error("Query string property must be either a string or object.");},getUrl:function(n,i,u,f){var s=i==="webSockets"?"":n.baseUrl,e=s+n.appRelativeUrl,o="transport="+i+"&connectionToken="+t.encodeURIComponent(n.token);return n.groupsToken&&(o+="&groupsToken="+t.encodeURIComponent(n.groupsToken)),u?(e+=f?"/poll":"/reconnect",n.messageId&&(o+="&messageId="+t.encodeURIComponent(n.messageId))):e+="/connect",e+="?"+o,e=r.prepareQueryString(n,e),e+("&tid="+Math.floor(Math.random()*11))},maximizePersistentResponse:function(n){return{MessageId:n.C,Messages:n.M,Initialized:typeof n.S!="undefined"?!0:!1,Disconnect:typeof n.D!="undefined"?!0:!1,ShouldReconnect:typeof n.T!="undefined"?!0:!1,LongPollDelay:n.L,GroupsToken:n.G}},updateGroups:function(n,t){t&&(n.groupsToken=t)},stringifySend:function(n,t){return typeof t=="string"||typeof t=="undefined"||t===null?t:n.json.stringify(t)},ajaxSend:function(f,e){var c=r.stringifySend(f,e),o=f.url+"/send?transport="+f.transport.name+"&connectionToken="+t.encodeURIComponent(f.token),s,h=function(t,r){n(r).triggerHandler(u.onError,[i._.transportError(i.resources.sendFailed,r.transport,t,s),e])};return o=r.prepareQueryString(f,o),s=n.ajax(n.extend({},n.signalR.ajaxDefaults,{xhrFields:{withCredentials:f.withCredentials},url:o,type:f.ajaxDataType==="jsonp"?"GET":"POST",contentType:i._.defaultContentType,dataType:f.ajaxDataType,data:{data:c},success:function(n){var t;if(n){try{t=f._parseResponse(n)}catch(i){h(i,f);f.stop();return}r.triggerReceived(f,t)}},error:function(n,t){t!=="abort"&&t!=="parsererror"&&h(n,f)}}))},ajaxAbort:function(i,u){if(typeof i.transport!="undefined"){u=typeof u=="undefined"?!0:u;var f=i.url+"/abort?transport="+i.transport.name+"&connectionToken="+t.encodeURIComponent(i.token);f=r.prepareQueryString(i,f);n.ajax(n.extend({},n.signalR.ajaxDefaults,{xhrFields:{withCredentials:i.withCredentials},url:f,async:u,timeout:1e3,type:"POST",contentType:i.contentType,dataType:i.ajaxDataType,data:{}}));i.log("Fired ajax abort async = "+u+".")}},tryInitialize:function(n,t){n.Initialized&&t()},triggerReceived:function(t,i){t._.connectingMessageBuffer.tryBuffer(i)||n(t).triggerHandler(u.onReceived,[i])},processMessages:function(t,i,u){var f;if(r.markLastMessage(t),i){if(f=r.maximizePersistentResponse(i),f.Disconnect){t.log("Disconnect command received from server.");t.stop(!1,!1);return}r.updateGroups(t,f.GroupsToken);f.MessageId&&(t.messageId=f.MessageId);f.Messages&&(n.each(f.Messages,function(n,i){r.triggerReceived(t,i)}),r.tryInitialize(f,u))}},monitorKeepAlive:function(t){var i=t._.keepAliveData;i.monitoring?t.log("Tried to monitor keep alive but it's already being monitored."):(i.monitoring=!0,r.markLastMessage(t),t._.keepAliveData.reconnectKeepAliveUpdate=function(){r.markLastMessage(t)},n(t).bind(u.onReconnect,t._.keepAliveData.reconnectKeepAliveUpdate),t.log("Now monitoring keep alive with a warning timeout of "+i.timeoutWarning+" and a connection lost timeout of "+i.timeout+"."))},stopMonitoringKeepAlive:function(t){var i=t._.keepAliveData;i.monitoring&&(i.monitoring=!1,n(t).unbind(u.onReconnect,t._.keepAliveData.reconnectKeepAliveUpdate),t._.keepAliveData={},t.log("Stopping the monitoring of the keep alive."))},startHeartbeat:function(n){n._.lastActiveAt=(new Date).getTime();f(n)},markLastMessage:function(n){n._.lastMessageAt=(new Date).getTime()},markActive:function(n){return r.verifyLastActive(n)?(n._.lastActiveAt=(new Date).getTime(),!0):!1},isConnectedOrReconnecting:function(n){return n.state===i.connectionState.connected||n.state===i.connectionState.reconnecting},ensureReconnectingState:function(t){return e(t,i.connectionState.connected,i.connectionState.reconnecting)===!0&&n(t).triggerHandler(u.onReconnecting),t.state===i.connectionState.reconnecting},clearReconnectTimeout:function(n){n&&n._.reconnectTimeout&&(t.clearTimeout(n._.reconnectTimeout),delete n._.reconnectTimeout)},verifyLastActive:function(n){return(new Date).getTime()-n._.lastActiveAt>=n.reconnectWindow?(n.log("There has not been an active server connection for an extended period of time. Stopping connection."),n.stop(),!1):!0},reconnect:function(n,u){var f=i.transports[u];if(r.isConnectedOrReconnecting(n)&&!n._.reconnectTimeout){if(!r.verifyLastActive(n))return;n._.reconnectTimeout=t.setTimeout(function(){r.verifyLastActive(n)&&(f.stop(n),r.ensureReconnectingState(n)&&(n.log(u+" reconnecting."),f.start(n)))},n.reconnectDelay)}},handleParseFailure:function(t,r,f,e,o){t.state===i.connectionState.connecting?(t.log("Failed to parse server response while attempting to connect."),e()):(n(t).triggerHandler(u.onError,[i._.transportError(i._.format(i.resources.parseFailed,r),t.transport,f,o)]),t.stop())},foreverFrame:{count:0,connections:{}}}}(window.jQuery,window),function(n,t){"use strict";var r=n.signalR,u=n.signalR.events,f=n.signalR.changeState,i=r.transports._logic;r.transports.webSockets={name:"webSockets",supportsKeepAlive:!0,send:function(t,f){var e=i.stringifySend(t,f);try{t.socket.send(e)}catch(o){n(t).triggerHandler(u.onError,[r._.transportError(r.resources.webSocketsInvalidState,t.transport,o,t.socket),f])}},start:function(e,o,s){var h,c=!1,l=this,a=!o,v=n(e);if(!t.WebSocket){s();return}e.socket||(h=e.webSocketServerUrl?e.webSocketServerUrl:e.wsProtocol+e.host,h+=i.getUrl(e,this.name,a),e.log("Connecting to websocket endpoint '"+h+"'."),e.socket=new t.WebSocket(h),e.socket.onopen=function(){c=!0;e.log("Websocket opened.");i.clearReconnectTimeout(e);f(e,r.connectionState.reconnecting,r.connectionState.connected)===!0&&v.triggerHandler(u.onReconnect)},e.socket.onclose=function(t){if(this===e.socket){if(c)typeof t.wasClean!="undefined"&&t.wasClean===!1?(n(e).triggerHandler(u.onError,[r._.transportError(r.resources.webSocketClosed,e.transport,t)]),e.log("Unclean disconnect from websocket: "+t.reason||"[no reason given].")):e.log("Websocket closed.");else{s?s():a&&l.reconnect(e);return}l.reconnect(e)}},e.socket.onmessage=function(t){var r;try{r=e._parseResponse(t.data)}catch(u){i.handleParseFailure(e,t.data,u,s,t);return}r&&(n.isEmptyObject(r)||r.M?i.processMessages(e,r,o):i.triggerReceived(e,r))})},reconnect:function(n){i.reconnect(n,this.name)},lostConnection:function(n){this.reconnect(n)},stop:function(n){i.clearReconnectTimeout(n);n.socket&&(n.log("Closing the Websocket."),n.socket.close(),n.socket=null)},abort:function(n,t){i.ajaxAbort(n,t)}}}(window.jQuery,window),function(n,t){"use strict";var i=n.signalR,u=n.signalR.events,f=n.signalR.changeState,r=i.transports._logic;i.transports.serverSentEvents={name:"serverSentEvents",supportsKeepAlive:!0,timeOut:3e3,start:function(e,o,s){var h=this,c=!1,l=n(e),a=!o,v,y;if(e.eventSource&&(e.log("The connection already has an event source. Stopping it."),e.stop()),!t.EventSource){s&&(e.log("This browser doesn't support SSE."),s());return}v=r.getUrl(e,this.name,a);try{e.log("Attempting to connect to SSE endpoint '"+v+"'.");e.eventSource=new t.EventSource(v,{withCredentials:e.withCredentials})}catch(p){e.log("EventSource failed trying to connect with error "+p.Message+".");s?s():(l.triggerHandler(u.onError,[i._.transportError(i.resources.eventSourceFailedToConnect,e.transport,p)]),a&&h.reconnect(e));return}a&&(y=t.setTimeout(function(){c===!1&&e.eventSource.readyState!==t.EventSource.OPEN&&h.reconnect(e)},h.timeOut));e.eventSource.addEventListener("open",function(){e.log("EventSource connected.");y&&t.clearTimeout(y);r.clearReconnectTimeout(e);c===!1&&(c=!0,f(e,i.connectionState.reconnecting,i.connectionState.connected)===!0&&l.triggerHandler(u.onReconnect))},!1);e.eventSource.addEventListener("message",function(n){var t;if(n.data!=="initialized"){try{t=e._parseResponse(n.data)}catch(i){r.handleParseFailure(e,n.data,i,s,n);return}r.processMessages(e,t,o)}},!1);e.eventSource.addEventListener("error",function(n){if(this===e.eventSource){if(!c){s&&s();return}e.log("EventSource readyState: "+e.eventSource.readyState+".");n.eventPhase===t.EventSource.CLOSED?(e.log("EventSource reconnecting due to the server connection ending."),h.reconnect(e)):(e.log("EventSource error."),l.triggerHandler(u.onError,[i._.transportError(i.resources.eventSourceError,e.transport,n)]))}},!1)},reconnect:function(n){r.reconnect(n,this.name)},lostConnection:function(n){this.reconnect(n)},send:function(n,t){r.ajaxSend(n,t)},stop:function(n){r.clearReconnectTimeout(n);n&&n.eventSource&&(n.log("EventSource calling close()."),n.eventSource.close(),n.eventSource=null,delete n.eventSource)},abort:function(n,t){r.ajaxAbort(n,t)}}}(window.jQuery,window),function(n,t){"use strict";var r=n.signalR,e=n.signalR.events,o=n.signalR.changeState,i=r.transports._logic,u=function(){var n=t.document.createElement("iframe");return n.setAttribute("style","position:absolute;top:0;left:0;width:0;height:0;visibility:hidden;"),n},f=function(){var i=null,f=1e3,n=0;return{prevent:function(){r._.ieVersion<=8&&(n===0&&(i=t.setInterval(function(){var n=u();t.document.body.appendChild(n);t.document.body.removeChild(n);n=null},f)),n++)},cancel:function(){n===1&&t.clearInterval(i);n>0&&n--}}}();r.transports.foreverFrame={name:"foreverFrame",supportsKeepAlive:!0,iframeClearThreshold:50,start:function(n,r,e){var l=this,s=i.foreverFrame.count+=1,h,o=u(),c=function(){n.log("Forever frame iframe finished loading and is no longer receiving messages.");l.reconnect(n)};if(t.EventSource){e&&(n.log("This browser supports SSE, skipping Forever Frame."),e());return}o.setAttribute("data-signalr-connection-id",n.id);f.prevent();h=i.getUrl(n,this.name);h+="&frameId="+s;t.document.body.appendChild(o);n.log("Binding to iframe's load event.");o.addEventListener?o.addEventListener("load",c,!1):o.attachEvent&&o.attachEvent("onload",c);o.src=h;i.foreverFrame.connections[s]=n;n.frame=o;n.frameId=s;r&&(n.onSuccess=function(){n.log("Iframe transport started.");r()})},reconnect:function(n){var r=this;i.isConnectedOrReconnecting(n)&&i.verifyLastActive(n)&&t.setTimeout(function(){if(i.verifyLastActive(n)&&n.frame&&i.ensureReconnectingState(n)){var u=n.frame,t=i.getUrl(n,r.name,!0)+"&frameId="+n.frameId;n.log("Updating iframe src to '"+t+"'.");u.src=t}},n.reconnectDelay)},lostConnection:function(n){this.reconnect(n)},send:function(n,t){i.ajaxSend(n,t)},receive:function(t,u){var f,e;if(i.processMessages(t,u,t.onSuccess),t.state===n.signalR.connectionState.connected&&(t.frameMessageCount=(t.frameMessageCount||0)+1,t.frameMessageCount>r.transports.foreverFrame.iframeClearThreshold&&(t.frameMessageCount=0,f=t.frame.contentWindow||t.frame.contentDocument,f&&f.document&&f.document.body)))for(e=f.document.body;e.firstChild;)e.removeChild(e.firstChild)},stop:function(n){var r=null;if(f.cancel(),n.frame){if(n.frame.stop)n.frame.stop();else try{r=n.frame.contentWindow||n.frame.contentDocument;r.document&&r.document.execCommand&&r.document.execCommand("Stop")}catch(u){n.log("Error occured when stopping foreverFrame transport. Message = "+u.message+".")}n.frame.parentNode===t.document.body&&t.document.body.removeChild(n.frame);delete i.foreverFrame.connections[n.frameId];n.frame=null;n.frameId=null;delete n.frame;delete n.frameId;delete n.onSuccess;delete n.frameMessageCount;n.log("Stopping forever frame.")}},abort:function(n,t){i.ajaxAbort(n,t)},getConnection:function(n){return i.foreverFrame.connections[n]},started:function(t){o(t,r.connectionState.reconnecting,r.connectionState.connected)===!0&&n(t).triggerHandler(e.onReconnect)}}}(window.jQuery,window),function(n,t){"use strict";var i=n.signalR,u=n.signalR.events,e=n.signalR.changeState,f=n.signalR.isDisconnecting,r=i.transports._logic;i.transports.longPolling={name:"longPolling",supportsKeepAlive:!1,reconnectDelay:3e3,start:function(o,s,h){var a=this,v=function(){v=n.noop;o.log("LongPolling connected.");s();h=null},y=function(){return h?(h(),h=null,o.log("LongPolling failed to connect."),!0):!1},c=o._,l=0,p=function(r){t.clearTimeout(c.reconnectTimeoutId);c.reconnectTimeoutId=null;e(r,i.connectionState.reconnecting,i.connectionState.connected)===!0&&(r.log("Raising the reconnect event"),n(r).triggerHandler(u.onReconnect))},w=36e5;o.pollXhr&&(o.log("Polling xhr requests already exists, aborting."),o.stop());o.messageId=null;c.reconnectTimeoutId=null;c.pollTimeoutId=t.setTimeout(function(){(function e(s,h){var d=s.messageId,g=d===null,b=!g,nt=!h,k=r.getUrl(s,a.name,b,nt);f(s)!==!0&&(o.log("Opening long polling request to '"+k+"'."),s.pollXhr=n.ajax(n.extend({},n.signalR.ajaxDefaults,{xhrFields:{withCredentials:o.withCredentials},url:k,type:"GET",dataType:o.ajaxDataType,contentType:o.contentType,success:function(i){var h,w=0,u,a;o.log("Long poll complete.");l=0;try{h=o._parseResponse(i)}catch(b){r.handleParseFailure(s,i,b,y,s.pollXhr);return}(c.reconnectTimeoutId!==null&&p(s),h&&(u=r.maximizePersistentResponse(h)),r.processMessages(s,h,v),u&&n.type(u.LongPollDelay)==="number"&&(w=u.LongPollDelay),u&&u.Disconnect)||f(s)!==!0&&(a=u&&u.ShouldReconnect,!a||r.ensureReconnectingState(s))&&(w>0?c.pollTimeoutId=t.setTimeout(function(){e(s,a)},w):e(s,a))},error:function(f,h){if(t.clearTimeout(c.reconnectTimeoutId),c.reconnectTimeoutId=null,h==="abort"){o.log("Aborted xhr request.");return}if(!y()){if(l++,o.state!==i.connectionState.reconnecting&&(o.log("An error occurred using longPolling. Status = "+h+". Response = "+f.responseText+"."),n(s).triggerHandler(u.onError,[i._.transportError(i.resources.longPollFailed,o.transport,f,s.pollXhr)])),(o.state===i.connectionState.connected||o.state===i.connectionState.reconnecting)&&!r.verifyLastActive(o))return;if(!r.ensureReconnectingState(s))return;c.pollTimeoutId=t.setTimeout(function(){e(s,!0)},a.reconnectDelay)}}})),b&&h===!0&&(c.reconnectTimeoutId=t.setTimeout(function(){p(s)},Math.min(1e3*(Math.pow(2,l)-1),w))))})(o)},250)},lostConnection:function(){throw new Error("Lost Connection not handled for LongPolling");},send:function(n,t){r.ajaxSend(n,t)},stop:function(n){t.clearTimeout(n._.pollTimeoutId);t.clearTimeout(n._.reconnectTimeoutId);delete n._.pollTimeoutId;delete n._.reconnectTimeoutId;n.pollXhr&&(n.pollXhr.abort(),n.pollXhr=null,delete n.pollXhr)},abort:function(n,t){r.ajaxAbort(n,t)}}}(window.jQuery,window),function(n){"use strict";function r(n){return n+e}function s(n,t,i){for(var f=n.length,u=[],r=0;r=0;u--)f=t[u],n.type(f)==="string"&&r.transports[f]||(i.log("Invalid transport: "+f+", removing it from the transports list."),t.splice(u,1));t.length===0&&(i.log("No transports remain within the specified transport array."),t=null)}else if(r.transports[t]||t==="auto"){if(t==="auto"&&r._.ieVersion<=8)return["longPolling"]}else i.log("Invalid transport: "+t.toString()+"."),t=null;return t}function b(n){return n==="http:"?80:n==="https:"?443:void 0}function a(n,t){return t.match(/:\d+$/)?t:t+":"+b(n)}function k(t,i){var u=this,r=[];u.tryBuffer=function(i){return t.state===n.signalR.connectionState.connecting?(r.push(i),!0):!1};u.drain=function(){if(t.state===n.signalR.connectionState.connected)while(r.length>0)i(r.shift())};u.clear=function(){r=[]}}var f={nojQuery:"jQuery was not found. Please ensure jQuery is referenced before the SignalR client JavaScript file.",noTransportOnInit:"No transport could be initialized successfully. Try specifying a different transport or none at all for auto initialization.",errorOnNegotiate:"Error during negotiation request.",stoppedWhileLoading:"The connection was stopped during page load.",stoppedWhileNegotiating:"The connection was stopped during the negotiate request.",errorParsingNegotiateResponse:"Error parsing negotiate response.",errorDuringStartRequest:"Error during start request. Stopping the connection.",stoppedDuringStartRequest:"The connection was stopped during the start request.",errorParsingStartResponse:"Error parsing start response: '{0}'. Stopping the connection.",invalidStartResponse:"Invalid start response: '{0}'. Stopping the connection.",protocolIncompatible:"You are using a version of the client that isn't compatible with the server. Client version {0}, server version {1}.",sendFailed:"Send failed.",parseFailed:"Failed at parsing response: {0}",longPollFailed:"Long polling request failed.",eventSourceFailedToConnect:"EventSource failed to connect.",eventSourceError:"Error raised by EventSource",webSocketClosed:"WebSocket closed.",pingServerFailedInvalidResponse:"Invalid ping response when pinging server: '{0}'.",pingServerFailed:"Failed to ping server.",pingServerFailedStatusCode:"Failed to ping server. Server responded with status code {0}, stopping the connection.",pingServerFailedParse:"Failed to parse ping server response, stopping the connection.",noConnectionTransport:"Connection is in an invalid state, there is no transport active.",webSocketsInvalidState:"The Web Socket transport is in an invalid state, transitioning into reconnecting.",reconnectTimeout:"Couldn't reconnect within the configured timeout of {0} ms, disconnecting.",reconnectWindowTimeout:"The client has been inactive since {0} and it has exceeded the inactivity timeout of {1} ms. Stopping the connection."};if(typeof n!="function")throw new Error(f.nojQuery);var r,h,s=t.document.readyState==="complete",e=n(t),c="__Negotiate Aborted__",u={onStart:"onStart",onStarting:"onStarting",onReceived:"onReceived",onError:"onError",onConnectionSlow:"onConnectionSlow",onReconnecting:"onReconnecting",onReconnect:"onReconnect",onStateChanged:"onStateChanged",onDisconnect:"onDisconnect"},v=function(n,i){if(i!==!1){var r;typeof t.console!="undefined"&&(r="["+(new Date).toTimeString()+"] SignalR: "+n,t.console.debug?t.console.debug(r):t.console.log&&t.console.log(r))}},o=function(t,i,r){return i===t.state?(t.state=r,n(t).triggerHandler(u.onStateChanged,[{oldState:i,newState:r}]),!0):!1},y=function(n){return n.state===r.connectionState.disconnected},l=function(n){return n._.keepAliveData.activated&&n.transport.supportsKeepAlive(n)},p=function(i){var f,e;i._.configuredStopReconnectingTimeout||(e=function(t){var i=r._.format(r.resources.reconnectTimeout,t.disconnectTimeout);t.log(i);n(t).triggerHandler(u.onError,[r._.error(i,"TimeoutException")]);t.stop(!1,!1)},i.reconnecting(function(){var n=this;n.state===r.connectionState.reconnecting&&(f=t.setTimeout(function(){e(n)},n.disconnectTimeout))}),i.stateChanged(function(n){n.oldState===r.connectionState.reconnecting&&t.clearTimeout(f)}),i._.configuredStopReconnectingTimeout=!0)};r=function(n,t,i){return new r.fn.init(n,t,i)};r._={defaultContentType:"application/x-www-form-urlencoded; charset=UTF-8",ieVersion:function(){var i,n;return t.navigator.appName==="Microsoft Internet Explorer"&&(n=/MSIE ([0-9]+\.[0-9]+)/.exec(t.navigator.userAgent),n&&(i=t.parseFloat(n[1]))),i}(),error:function(n,t,i){var r=new Error(n);return r.source=t,typeof i!="undefined"&&(r.context=i),r},transportError:function(n,t,r,u){var f=this.error(n,r,u);return f.transport=t?t.name:i,f},format:function(){for(var t=arguments[0],n=0;n<\/script>.");}};e.load(function(){s=!0});r.fn=r.prototype={init:function(t,i,r){var f=n(this);this.url=t;this.qs=i;this.lastError=null;this._={keepAliveData:{},connectingMessageBuffer:new k(this,function(n){f.triggerHandler(u.onReceived,[n])}),onFailedTimeoutHandle:null,lastMessageAt:(new Date).getTime(),lastActiveAt:(new Date).getTime(),beatInterval:5e3,beatHandle:null,totalTransportConnectTimeout:0};typeof r=="boolean"&&(this.logging=r)},_parseResponse:function(n){var t=this;return n?typeof n=="string"?t.json.parse(n):n:n},_originalJson:t.JSON,json:t.JSON,isCrossDomain:function(i,r){var u;return(i=n.trim(i),r=r||t.location,i.indexOf("http")!==0)?!1:(u=t.document.createElement("a"),u.href=i,u.protocol+a(u.protocol,u.host)!==r.protocol+a(r.protocol,r.host))},ajaxDataType:"text",contentType:"application/json; charset=UTF-8",logging:!1,state:r.connectionState.disconnected,clientProtocol:"1.4",reconnectDelay:2e3,transportConnectTimeout:0,disconnectTimeout:3e4,reconnectWindow:3e4,keepAliveWarnAt:2/3,start:function(i,h){var a=this,v={pingInterval:3e5,waitForPageLoad:!0,transport:"auto",jsonp:!1},d,y=a._deferral||n.Deferred(),b=t.document.createElement("a"),k,g;if(a.lastError=null,a._deferral=y,!a.json)throw new Error("SignalR: No JSON parser found. Please ensure json2.js is referenced before the SignalR.js file if you need to support clients without native JSON parsing support, e.g. IE<8.");if(n.type(i)==="function"?h=i:n.type(i)==="object"&&(n.extend(v,i),n.type(v.callback)==="function"&&(h=v.callback)),v.transport=w(v.transport,a),!v.transport)throw new Error("SignalR: Invalid transport(s) specified, aborting start.");return(a._.config=v,!s&&v.waitForPageLoad===!0)?(a._.deferredStartHandler=function(){a.start(i,h)},e.bind("load",a._.deferredStartHandler),y.promise()):a.state===r.connectionState.connecting?y.promise():o(a,r.connectionState.disconnected,r.connectionState.connecting)===!1?(y.resolve(a),y.promise()):(p(a),b.href=a.url,b.protocol&&b.protocol!==":"?(a.protocol=b.protocol,a.host=b.host):(a.protocol=t.document.location.protocol,a.host=b.host||t.document.location.host),a.baseUrl=a.protocol+"//"+a.host,a.wsProtocol=a.protocol==="https:"?"wss://":"ws://",v.transport==="auto"&&v.jsonp===!0&&(v.transport="longPolling"),a.url.indexOf("//")===0&&(a.url=t.location.protocol+a.url,a.log("Protocol relative URL detected, normalizing it to '"+a.url+"'.")),this.isCrossDomain(a.url)&&(a.log("Auto detected cross domain url."),v.transport==="auto"&&(v.transport=["webSockets","serverSentEvents","longPolling"]),typeof v.withCredentials=="undefined"&&(v.withCredentials=!0),v.jsonp||(v.jsonp=!n.support.cors,v.jsonp&&a.log("Using jsonp because this browser doesn't support CORS.")),a.contentType=r._.defaultContentType),a.withCredentials=v.withCredentials,a.ajaxDataType=v.jsonp?"jsonp":"text",n(a).bind(u.onStart,function(){n.type(h)==="function"&&h.call(a);y.resolve(a)}),d=function(i,s){var p=r._.error(f.noTransportOnInit);if(s=s||0,s>=i.length){n(a).triggerHandler(u.onError,[p]);y.reject(p);a.stop();return}if(a.state!==r.connectionState.disconnected){var w=i[s],h=r.transports[w],c=!1,v=function(){c||(c=!0,t.clearTimeout(a._.onFailedTimeoutHandle),h.stop(a),d(i,s+1))};a.transport=h;try{a._.onFailedTimeoutHandle=t.setTimeout(function(){a.log(h.name+" timed out when trying to connect.");v()},a._.totalTransportConnectTimeout);h.start(a,function(){var i=r._.firefoxMajorVersion(t.navigator.userAgent)>=11,f=!!a.withCredentials&&i;a.state!==r.connectionState.disconnected&&(c||(c=!0,t.clearTimeout(a._.onFailedTimeoutHandle),l(a)&&r.transports._logic.monitorKeepAlive(a),r.transports._logic.startHeartbeat(a),r._.configurePingInterval(a),o(a,r.connectionState.connecting,r.connectionState.connected),a._.connectingMessageBuffer.drain(),n(a).triggerHandler(u.onStart),e.bind("unload",function(){a.log("Window unloading, stopping the connection.");a.stop(f)}),i&&e.bind("beforeunload",function(){t.setTimeout(function(){a.stop(f)},0)})))},v)}catch(b){a.log(h.name+" transport threw '"+b.message+"' when attempting to start.");v()}}},k=a.url+"/negotiate",g=function(t,i){var e=r._.error(f.errorOnNegotiate,t,i._.negotiateRequest);n(i).triggerHandler(u.onError,e);y.reject(e);i.stop()},n(a).triggerHandler(u.onStarting),k=r.transports._logic.prepareQueryString(a,k),a.log("Negotiating with '"+k+"'."),a._.negotiateRequest=r.transports._logic.ajax(a,{url:k,error:function(n,t){t!==c?g(n,a):y.reject(r._.error(f.stoppedWhileNegotiating,null,a._.negotiateRequest))},success:function(t){var i,e,h,o=[],s=[];try{i=a._parseResponse(t)}catch(c){g(r._.error(f.errorParsingNegotiateResponse,c),a);return}if(e=a._.keepAliveData,a.appRelativeUrl=i.Url,a.id=i.ConnectionId,a.token=i.ConnectionToken,a.webSocketServerUrl=i.WebSocketServerUrl,a._.longPollDelay=i.LongPollDelay*1e3,a.disconnectTimeout=i.DisconnectTimeout*1e3,a._.totalTransportConnectTimeout=a.transportConnectTimeout+i.TransportConnectTimeout*1e3,i.KeepAliveTimeout?(e.activated=!0,e.timeout=i.KeepAliveTimeout*1e3,e.timeoutWarning=e.timeout*a.keepAliveWarnAt,a._.beatInterval=(e.timeout-e.timeoutWarning)/3):e.activated=!1,a.reconnectWindow=a.disconnectTimeout+(e.timeout||0),!i.ProtocolVersion||i.ProtocolVersion!==a.clientProtocol){h=r._.error(r._.format(f.protocolIncompatible,a.clientProtocol,i.ProtocolVersion));n(a).triggerHandler(u.onError,[h]);y.reject(h);return}n.each(r.transports,function(n){if(n.indexOf("_")===0||n==="webSockets"&&!i.TryWebSockets)return!0;s.push(n)});n.isArray(v.transport)?n.each(v.transport,function(t,i){n.inArray(i,s)>=0&&o.push(i)}):v.transport==="auto"?o=s:n.inArray(v.transport,s)>=0&&o.push(v.transport);d(o)}}),y.promise())},starting:function(t){var i=this;return n(i).bind(u.onStarting,function(){t.call(i)}),i},send:function(n){var t=this;if(t.state===r.connectionState.disconnected)throw new Error("SignalR: Connection must be started before data can be sent. Call .start() before .send()");if(t.state===r.connectionState.connecting)throw new Error("SignalR: Connection has not been fully initialized. Use .start().done() or .start().fail() to run logic after the connection has started.");return t.transport.send(t,n),t},received:function(t){var i=this;return n(i).bind(u.onReceived,function(n,r){t.call(i,r)}),i},stateChanged:function(t){var i=this;return n(i).bind(u.onStateChanged,function(n,r){t.call(i,r)}),i},error:function(t){var i=this;return n(i).bind(u.onError,function(n,r,u){i.lastError=r;t.call(i,r,u)}),i},disconnected:function(t){var i=this;return n(i).bind(u.onDisconnect,function(){t.call(i)}),i},connectionSlow:function(t){var i=this;return n(i).bind(u.onConnectionSlow,function(){t.call(i)}),i},reconnecting:function(t){var i=this;return n(i).bind(u.onReconnecting,function(){t.call(i)}),i},reconnected:function(t){var i=this;return n(i).bind(u.onReconnect,function(){t.call(i)}),i},stop:function(i,h){var a=this,v=a._deferral;if(a._.deferredStartHandler&&e.unbind("load",a._.deferredStartHandler),delete a._.config,delete a._.deferredStartHandler,!s&&(!a._.config||a._.config.waitForPageLoad===!0)){a.log("Stopping connection prior to negotiate.");v&&v.reject(r._.error(f.stoppedWhileLoading));return}if(a.state!==r.connectionState.disconnected)return a.log("Stopping connection."),o(a,a.state,r.connectionState.disconnected),t.clearTimeout(a._.beatHandle),t.clearTimeout(a._.onFailedTimeoutHandle),t.clearInterval(a._.pingIntervalId),a.transport&&(a.transport.stop(a),h!==!1&&a.transport.abort(a,i),l(a)&&r.transports._logic.stopMonitoringKeepAlive(a),a.transport=null),a._.negotiateRequest&&(a._.negotiateRequest.abort(c),delete a._.negotiateRequest),r.transports._logic.tryAbortStartRequest(a),n(a).triggerHandler(u.onDisconnect),delete a._deferral,delete a.messageId,delete a.groupsToken,delete a.id,delete a._.pingIntervalId,delete a._.lastMessageAt,delete a._.lastActiveAt,delete a._.longPollDelay,a._.connectingMessageBuffer.clear(),a},log:function(n){v(n,this.logging)}};r.fn.init.prototype=r.fn;r.noConflict=function(){return n.connection===r&&(n.connection=h),r};n.connection&&(h=n.connection);n.connection=n.signalR=r})(window.jQuery,window),function(n,t){function o(n){n._.keepAliveData.monitoring&&h(n);r.markActive(n)&&(n._.beatHandle=t.setTimeout(function(){o(n)},n._.beatInterval))}function h(t){var r=t._.keepAliveData,f;t.state===i.connectionState.connected&&(f=(new Date).getTime()-t._.lastMessageAt,f>=r.timeout?(t.log("Keep alive timed out. Notifying transport that connection has been lost."),t.transport.lostConnection(t)):f>=r.timeoutWarning?r.userNotified||(t.log("Keep alive has been missed, connection may be dead/slow."),n(t).triggerHandler(u.onConnectionSlow),r.userNotified=!0):r.userNotified=!1)}function f(n,t){var i=n.url+t;return n.transport&&(i+="?transport="+n.transport.name),r.prepareQueryString(n,i)}var i=n.signalR,u=n.signalR.events,s=n.signalR.changeState,e="__Start Aborted__",r;i.transports={};r=i.transports._logic={ajax:function(t,i){return n.ajax(n.extend(!0,{},n.signalR.ajaxDefaults,{type:"GET",data:{},xhrFields:{withCredentials:t.withCredentials},contentType:t.contentType,dataType:t.ajaxDataType},i))},pingServer:function(t){var e,f,u=n.Deferred();return t.transport?(e=t.url+"/ping",e=r.addQs(e,t.qs),f=r.ajax(t,{url:e,success:function(n){var r;try{r=t._parseResponse(n)}catch(e){u.reject(i._.transportError(i.resources.pingServerFailedParse,t.transport,e,f));t.stop();return}r.Response==="pong"?u.resolve():u.reject(i._.transportError(i._.format(i.resources.pingServerFailedInvalidResponse,n),t.transport,null,f))},error:function(n){n.status===401||n.status===403?(u.reject(i._.transportError(i._.format(i.resources.pingServerFailedStatusCode,n.status),t.transport,n,f)),t.stop()):u.reject(i._.transportError(i.resources.pingServerFailed,t.transport,n,f))}})):u.reject(i._.transportError(i.resources.noConnectionTransport,t.transport)),u.promise()},prepareQueryString:function(n,i){var u;return u=r.addQs(i,"clientProtocol="+n.clientProtocol),u=r.addQs(u,n.qs),n.token&&(u+="&connectionToken="+t.encodeURIComponent(n.token)),n.data&&(u+="&connectionData="+t.encodeURIComponent(n.data)),u},addQs:function(t,i){var r=t.indexOf("?")!==-1?"&":"?",u;if(!i)return t;if(typeof i=="object")return t+r+n.param(i);if(typeof i=="string")return u=i.charAt(0),(u==="?"||u==="&")&&(r=""),t+r+i;throw new Error("Query string property must be either a string or object.");},getUrl:function(n,i,u,f){var s=i==="webSockets"?"":n.baseUrl,e=s+n.appRelativeUrl,o="transport="+i;return n.groupsToken&&(o+="&groupsToken="+t.encodeURIComponent(n.groupsToken)),u?(e+=f?"/poll":"/reconnect",n.messageId&&(o+="&messageId="+t.encodeURIComponent(n.messageId))):e+="/connect",e+="?"+o,e=r.prepareQueryString(n,e),e+("&tid="+Math.floor(Math.random()*11))},maximizePersistentResponse:function(n){return{MessageId:n.C,Messages:n.M,Initialized:typeof n.S!="undefined"?!0:!1,Disconnect:typeof n.D!="undefined"?!0:!1,ShouldReconnect:typeof n.T!="undefined"?!0:!1,LongPollDelay:n.L,GroupsToken:n.G}},updateGroups:function(n,t){t&&(n.groupsToken=t)},stringifySend:function(n,t){return typeof t=="string"||typeof t=="undefined"||t===null?t:n.json.stringify(t)},ajaxSend:function(t,e){var h=r.stringifySend(t,e),c=f(t,"/send"),o,s=function(t,r){n(r).triggerHandler(u.onError,[i._.transportError(i.resources.sendFailed,r.transport,t,o),e])};return o=r.ajax(t,{url:c,type:t.ajaxDataType==="jsonp"?"GET":"POST",contentType:i._.defaultContentType,data:{data:h},success:function(n){var i;if(n){try{i=t._parseResponse(n)}catch(u){s(u,t);t.stop();return}r.triggerReceived(t,i)}},error:function(n,i){i!=="abort"&&i!=="parsererror"&&s(n,t)}})},ajaxAbort:function(n,t){if(typeof n.transport!="undefined"){t=typeof t=="undefined"?!0:t;var i=f(n,"/abort");r.ajax(n,{url:i,async:t,timeout:1e3,type:"POST"});n.log("Fired ajax abort async = "+t+".")}},tryInitialize:function(t,o,s){var l,h,a=function(n){var i=t._deferral;i&&i.reject(n)},c=function(i){n(t).triggerHandler(u.onError,[i]);a(i);t.stop()};o.Initialized&&(l=f(t,"/start"),h=r.ajax(t,{url:l,success:function(n){var r;try{r=t._parseResponse(n)}catch(u){c(i._.error(i._.format(i.resources.errorParsingStartResponse,n),u,h));return}r.Response==="started"?s():c(i._.error(i._.format(i.resources.invalidStartResponse,n),null,h))},error:function(n,t){t!==e?c(i._.error(i.resources.errorDuringStartRequest,n,h)):a(i._.error(i.resources.stoppedDuringStartRequest,null,h))}}),t._.startRequest=h)},tryAbortStartRequest:function(n){n._.startRequest&&(n._.startRequest.abort(e),delete n._.startRequest)},triggerReceived:function(t,i){t._.connectingMessageBuffer.tryBuffer(i)||n(t).triggerHandler(u.onReceived,[i])},processMessages:function(t,i,u){var f;r.markLastMessage(t);i&&(f=r.maximizePersistentResponse(i),r.updateGroups(t,f.GroupsToken),f.MessageId&&(t.messageId=f.MessageId),f.Messages&&(n.each(f.Messages,function(n,i){r.triggerReceived(t,i)}),r.tryInitialize(t,f,u)))},monitorKeepAlive:function(t){var i=t._.keepAliveData;i.monitoring?t.log("Tried to monitor keep alive but it's already being monitored."):(i.monitoring=!0,r.markLastMessage(t),t._.keepAliveData.reconnectKeepAliveUpdate=function(){r.markLastMessage(t)},n(t).bind(u.onReconnect,t._.keepAliveData.reconnectKeepAliveUpdate),t.log("Now monitoring keep alive with a warning timeout of "+i.timeoutWarning+" and a connection lost timeout of "+i.timeout+"."))},stopMonitoringKeepAlive:function(t){var i=t._.keepAliveData;i.monitoring&&(i.monitoring=!1,n(t).unbind(u.onReconnect,t._.keepAliveData.reconnectKeepAliveUpdate),t._.keepAliveData={},t.log("Stopping the monitoring of the keep alive."))},startHeartbeat:function(n){n._.lastActiveAt=(new Date).getTime();o(n)},markLastMessage:function(n){n._.lastMessageAt=(new Date).getTime()},markActive:function(n){return r.verifyLastActive(n)?(n._.lastActiveAt=(new Date).getTime(),!0):!1},isConnectedOrReconnecting:function(n){return n.state===i.connectionState.connected||n.state===i.connectionState.reconnecting},ensureReconnectingState:function(t){return s(t,i.connectionState.connected,i.connectionState.reconnecting)===!0&&n(t).triggerHandler(u.onReconnecting),t.state===i.connectionState.reconnecting},clearReconnectTimeout:function(n){n&&n._.reconnectTimeout&&(t.clearTimeout(n._.reconnectTimeout),delete n._.reconnectTimeout)},verifyLastActive:function(t){if((new Date).getTime()-t._.lastActiveAt>=t.reconnectWindow){var r=i._.format(i.resources.reconnectWindowTimeout,new Date(t._.lastActiveAt),t.reconnectWindow);return t.log(r),n(t).triggerHandler(u.onError,[i._.error(r,"TimeoutException")]),t.stop(!1,!1),!1}return!0},reconnect:function(n,u){var f=i.transports[u];if(r.isConnectedOrReconnecting(n)&&!n._.reconnectTimeout){if(!r.verifyLastActive(n))return;n._.reconnectTimeout=t.setTimeout(function(){r.verifyLastActive(n)&&(f.stop(n),r.ensureReconnectingState(n)&&(n.log(u+" reconnecting."),f.start(n)))},n.reconnectDelay)}},handleParseFailure:function(t,r,f,e,o){t.state===i.connectionState.connecting?(t.log("Failed to parse server response while attempting to connect."),e()):(n(t).triggerHandler(u.onError,[i._.transportError(i._.format(i.resources.parseFailed,r),t.transport,f,o)]),t.stop())},foreverFrame:{count:0,connections:{}}}}(window.jQuery,window),function(n,t){var r=n.signalR,u=n.signalR.events,f=n.signalR.changeState,i=r.transports._logic;r.transports.webSockets={name:"webSockets",supportsKeepAlive:function(){return!0},send:function(t,f){var e=i.stringifySend(t,f);try{t.socket.send(e)}catch(o){n(t).triggerHandler(u.onError,[r._.transportError(r.resources.webSocketsInvalidState,t.transport,o,t.socket),f])}},start:function(e,o,s){var h,c=!1,l=this,a=!o,v=n(e);if(!t.WebSocket){s();return}e.socket||(h=e.webSocketServerUrl?e.webSocketServerUrl:e.wsProtocol+e.host,h+=i.getUrl(e,this.name,a),e.log("Connecting to websocket endpoint '"+h+"'."),e.socket=new t.WebSocket(h),e.socket.onopen=function(){c=!0;e.log("Websocket opened.");i.clearReconnectTimeout(e);f(e,r.connectionState.reconnecting,r.connectionState.connected)===!0&&v.triggerHandler(u.onReconnect)},e.socket.onclose=function(t){if(this===e.socket){if(c)typeof t.wasClean!="undefined"&&t.wasClean===!1?(n(e).triggerHandler(u.onError,[r._.transportError(r.resources.webSocketClosed,e.transport,t)]),e.log("Unclean disconnect from websocket: "+t.reason||"[no reason given].")):e.log("Websocket closed.");else{s?s():a&&l.reconnect(e);return}l.reconnect(e)}},e.socket.onmessage=function(t){var r;try{r=e._parseResponse(t.data)}catch(u){i.handleParseFailure(e,t.data,u,s,t);return}r&&(n.isEmptyObject(r)||r.M?i.processMessages(e,r,o):i.triggerReceived(e,r))})},reconnect:function(n){i.reconnect(n,this.name)},lostConnection:function(n){this.reconnect(n)},stop:function(n){i.clearReconnectTimeout(n);n.socket&&(n.log("Closing the Websocket."),n.socket.close(),n.socket=null)},abort:function(n,t){i.ajaxAbort(n,t)}}}(window.jQuery,window),function(n,t){var i=n.signalR,u=n.signalR.events,e=n.signalR.changeState,r=i.transports._logic,f=function(n){t.clearTimeout(n._.reconnectAttemptTimeoutHandle);delete n._.reconnectAttemptTimeoutHandle};i.transports.serverSentEvents={name:"serverSentEvents",supportsKeepAlive:function(){return!0},timeOut:3e3,start:function(o,s,h){var c=this,l=!1,a=n(o),v=!s,y;if(o.eventSource&&(o.log("The connection already has an event source. Stopping it."),o.stop()),!t.EventSource){h&&(o.log("This browser doesn't support SSE."),h());return}y=r.getUrl(o,this.name,v);try{o.log("Attempting to connect to SSE endpoint '"+y+"'.");o.eventSource=new t.EventSource(y,{withCredentials:o.withCredentials})}catch(p){o.log("EventSource failed trying to connect with error "+p.Message+".");h?h():(a.triggerHandler(u.onError,[i._.transportError(i.resources.eventSourceFailedToConnect,o.transport,p)]),v&&c.reconnect(o));return}v&&(o._.reconnectAttemptTimeoutHandle=t.setTimeout(function(){l===!1&&o.eventSource.readyState!==t.EventSource.OPEN&&c.reconnect(o)},c.timeOut));o.eventSource.addEventListener("open",function(){o.log("EventSource connected.");f(o);r.clearReconnectTimeout(o);l===!1&&(l=!0,e(o,i.connectionState.reconnecting,i.connectionState.connected)===!0&&a.triggerHandler(u.onReconnect))},!1);o.eventSource.addEventListener("message",function(n){var t;if(n.data!=="initialized"){try{t=o._parseResponse(n.data)}catch(i){r.handleParseFailure(o,n.data,i,h,n);return}r.processMessages(o,t,s)}},!1);o.eventSource.addEventListener("error",function(n){if(this===o.eventSource){if(!l){h&&h();return}o.log("EventSource readyState: "+o.eventSource.readyState+".");n.eventPhase===t.EventSource.CLOSED?(o.log("EventSource reconnecting due to the server connection ending."),c.reconnect(o)):(o.log("EventSource error."),a.triggerHandler(u.onError,[i._.transportError(i.resources.eventSourceError,o.transport,n)]))}},!1)},reconnect:function(n){r.reconnect(n,this.name)},lostConnection:function(n){this.reconnect(n)},send:function(n,t){r.ajaxSend(n,t)},stop:function(n){f(n);r.clearReconnectTimeout(n);n&&n.eventSource&&(n.log("EventSource calling close()."),n.eventSource.close(),n.eventSource=null,delete n.eventSource)},abort:function(n,t){r.ajaxAbort(n,t)}}}(window.jQuery,window),function(n,t){var r=n.signalR,e=n.signalR.events,o=n.signalR.changeState,i=r.transports._logic,u=function(){var n=t.document.createElement("iframe");return n.setAttribute("style","position:absolute;top:0;left:0;width:0;height:0;visibility:hidden;"),n},f=function(){var i=null,f=1e3,n=0;return{prevent:function(){r._.ieVersion<=8&&(n===0&&(i=t.setInterval(function(){var n=u();t.document.body.appendChild(n);t.document.body.removeChild(n);n=null},f)),n++)},cancel:function(){n===1&&t.clearInterval(i);n>0&&n--}}}();r.transports.foreverFrame={name:"foreverFrame",supportsKeepAlive:function(){return!0},iframeClearThreshold:50,start:function(n,r,e){var l=this,s=i.foreverFrame.count+=1,h,o=u(),c=function(){n.log("Forever frame iframe finished loading and is no longer receiving messages.");l.reconnect(n)};if(t.EventSource){e&&(n.log("This browser supports SSE, skipping Forever Frame."),e());return}o.setAttribute("data-signalr-connection-id",n.id);f.prevent();h=i.getUrl(n,this.name);h+="&frameId="+s;t.document.body.appendChild(o);n.log("Binding to iframe's load event.");o.addEventListener?o.addEventListener("load",c,!1):o.attachEvent&&o.attachEvent("onload",c);o.src=h;i.foreverFrame.connections[s]=n;n.frame=o;n.frameId=s;r&&(n.onSuccess=function(){n.log("Iframe transport started.");r()})},reconnect:function(n){var r=this;i.isConnectedOrReconnecting(n)&&i.verifyLastActive(n)&&t.setTimeout(function(){if(i.verifyLastActive(n)&&n.frame&&i.ensureReconnectingState(n)){var u=n.frame,t=i.getUrl(n,r.name,!0)+"&frameId="+n.frameId;n.log("Updating iframe src to '"+t+"'.");u.src=t}},n.reconnectDelay)},lostConnection:function(n){this.reconnect(n)},send:function(n,t){i.ajaxSend(n,t)},receive:function(t,u){var f,e,o;if(t.json!==t._originalJson&&(u=t._originalJson.stringify(u)),o=t._parseResponse(u),i.processMessages(t,o,t.onSuccess),t.state===n.signalR.connectionState.connected&&(t.frameMessageCount=(t.frameMessageCount||0)+1,t.frameMessageCount>r.transports.foreverFrame.iframeClearThreshold&&(t.frameMessageCount=0,f=t.frame.contentWindow||t.frame.contentDocument,f&&f.document&&f.document.body)))for(e=f.document.body;e.firstChild;)e.removeChild(e.firstChild)},stop:function(n){var r=null;if(f.cancel(),n.frame){if(n.frame.stop)n.frame.stop();else try{r=n.frame.contentWindow||n.frame.contentDocument;r.document&&r.document.execCommand&&r.document.execCommand("Stop")}catch(u){n.log("Error occured when stopping foreverFrame transport. Message = "+u.message+".")}n.frame.parentNode===t.document.body&&t.document.body.removeChild(n.frame);delete i.foreverFrame.connections[n.frameId];n.frame=null;n.frameId=null;delete n.frame;delete n.frameId;delete n.onSuccess;delete n.frameMessageCount;n.log("Stopping forever frame.")}},abort:function(n,t){i.ajaxAbort(n,t)},getConnection:function(n){return i.foreverFrame.connections[n]},started:function(t){o(t,r.connectionState.reconnecting,r.connectionState.connected)===!0&&n(t).triggerHandler(e.onReconnect)}}}(window.jQuery,window),function(n,t){var r=n.signalR,u=n.signalR.events,e=n.signalR.changeState,f=n.signalR.isDisconnecting,i=r.transports._logic,o=function(){try{return"onprogress"in new t.XMLHttpRequest}catch(n){return!1}}();r.transports.longPolling={name:"longPolling",supportsKeepAlive:function(n){return o&&n.ajaxDataType!=="jsonp"&&n._.longPollDelay===0},reconnectDelay:3e3,start:function(o,s,h){var a=this,v=function(){v=n.noop;h=null;o.log("LongPolling connected.");s()},y=function(){return h?(h(),h=null,o.log("LongPolling failed to connect."),!0):!1},c=o._,l=0,p=function(i){t.clearTimeout(c.reconnectTimeoutId);c.reconnectTimeoutId=null;e(i,r.connectionState.reconnecting,r.connectionState.connected)===!0&&(i.log("Raising the reconnect event"),n(i).triggerHandler(u.onReconnect))},w=36e5;o.pollXhr&&(o.log("Polling xhr requests already exists, aborting."),o.stop());o.messageId=null;c.reconnectTimeoutId=null;c.pollTimeoutId=t.setTimeout(function(){(function e(s,h){var d=s.messageId,g=d===null,b=!g,nt=!h,k=i.getUrl(s,a.name,b,nt);f(s)!==!0&&(o.log("Opening long polling request to '"+k+"'."),s.pollXhr=i.ajax(o,{xhrFields:{onprogress:function(){i.markLastMessage(o)}},url:k,success:function(r){var h,w=0,u,a;o.log("Long poll complete.");l=0;try{h=o._parseResponse(r)}catch(b){i.handleParseFailure(s,r,b,y,s.pollXhr);return}(c.reconnectTimeoutId!==null&&p(s),h&&(u=i.maximizePersistentResponse(h)),i.processMessages(s,h,v),u&&n.type(u.LongPollDelay)==="number"&&(w=u.LongPollDelay),u&&u.Disconnect)||f(s)!==!0&&(a=u&&u.ShouldReconnect,!a||i.ensureReconnectingState(s))&&(w>0?c.pollTimeoutId=t.setTimeout(function(){e(s,a)},w):e(s,a))},error:function(f,h){if(t.clearTimeout(c.reconnectTimeoutId),c.reconnectTimeoutId=null,h==="abort"){o.log("Aborted xhr request.");return}if(!y()){if(l++,o.state!==r.connectionState.reconnecting&&(o.log("An error occurred using longPolling. Status = "+h+". Response = "+f.responseText+"."),n(s).triggerHandler(u.onError,[r._.transportError(r.resources.longPollFailed,o.transport,f,s.pollXhr)])),(o.state===r.connectionState.connected||o.state===r.connectionState.reconnecting)&&!i.verifyLastActive(o))return;if(!i.ensureReconnectingState(s))return;c.pollTimeoutId=t.setTimeout(function(){e(s,!0)},a.reconnectDelay)}}}),b&&h===!0&&(c.reconnectTimeoutId=t.setTimeout(function(){p(s)},Math.min(1e3*(Math.pow(2,l)-1),w))))})(o)},250)},lostConnection:function(n){n.pollXhr&&n.pollXhr.abort("lostConnection")},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){t.clearTimeout(n._.pollTimeoutId);t.clearTimeout(n._.reconnectTimeoutId);delete n._.pollTimeoutId;delete n._.reconnectTimeoutId;n.pollXhr&&(n.pollXhr.abort(),n.pollXhr=null,delete n.pollXhr)},abort:function(n,t){i.ajaxAbort(n,t)}}}(window.jQuery,window),function(n){function r(n){return n+e}function s(n,t,i){for(var f=n.length,u=[],r=0;rCalls the GetStatus method on the server-side scheduledTaskNotifications hub.
Returns a jQuery.Deferred() promise.
+ return proxies['scheduledTaskNotifications'].invoke.apply(proxies['scheduledTaskNotifications'], $.merge(["GetStatus"], $.makeArray(arguments)));
}
};
- proxies.userUpdates = this.createHubProxy('userUpdates');
- proxies.userUpdates.client = { };
- proxies.userUpdates.server = {
+ proxies['userUpdates'] = this.createHubProxy('userUpdates');
+ proxies['userUpdates'].client = { };
+ proxies['userUpdates'].server = {
};
return proxies;
diff --git a/Disco.Web/ClientSource/Scripts/Modules/jQuery-SignalR/jquery.signalR-2.0.3.js b/Disco.Web/ClientSource/Scripts/Modules/jQuery-SignalR/jquery.signalR-2.1.0.js
similarity index 79%
rename from Disco.Web/ClientSource/Scripts/Modules/jQuery-SignalR/jquery.signalR-2.0.3.js
rename to Disco.Web/ClientSource/Scripts/Modules/jQuery-SignalR/jquery.signalR-2.1.0.js
index 6bbfc149..8a71c4ab 100644
--- a/Disco.Web/ClientSource/Scripts/Modules/jQuery-SignalR/jquery.signalR-2.0.3.js
+++ b/Disco.Web/ClientSource/Scripts/Modules/jQuery-SignalR/jquery.signalR-2.1.0.js
@@ -1,7 +1,7 @@
/* jquery.signalR.core.js */
/*global window:false */
/*!
- * ASP.NET SignalR JavaScript Library v2.0.3
+ * ASP.NET SignalR JavaScript Library v2.1.0
* http://signalr.net/
*
* Copyright (C) Microsoft Corporation. All rights reserved.
@@ -11,7 +11,6 @@
///
///
(function ($, window, undefined) {
- "use strict";
var resources = {
nojQuery: "jQuery was not found. Please ensure jQuery is referenced before the SignalR client JavaScript file.",
@@ -20,6 +19,10 @@
stoppedWhileLoading: "The connection was stopped during page load.",
stoppedWhileNegotiating: "The connection was stopped during the negotiate request.",
errorParsingNegotiateResponse: "Error parsing negotiate response.",
+ errorDuringStartRequest: "Error during start request. Stopping the connection.",
+ stoppedDuringStartRequest: "The connection was stopped during the start request.",
+ errorParsingStartResponse: "Error parsing start response: '{0}'. Stopping the connection.",
+ invalidStartResponse: "Invalid start response: '{0}'. Stopping the connection.",
protocolIncompatible: "You are using a version of the client that isn't compatible with the server. Client version {0}, server version {1}.",
sendFailed: "Send failed.",
parseFailed: "Failed at parsing response: {0}",
@@ -32,7 +35,9 @@
pingServerFailedStatusCode: "Failed to ping server. Server responded with status code {0}, stopping the connection.",
pingServerFailedParse: "Failed to parse ping server response, stopping the connection.",
noConnectionTransport: "Connection is in an invalid state, there is no transport active.",
- webSocketsInvalidState: "The Web Socket transport is in an invalid state, transitioning into reconnecting."
+ webSocketsInvalidState: "The Web Socket transport is in an invalid state, transitioning into reconnecting.",
+ reconnectTimeout: "Couldn't reconnect within the configured timeout of {0} ms, disconnecting.",
+ reconnectWindowTimeout: "The client has been inactive since {0} and it has exceeded the inactivity timeout of {1} ms. Stopping the connection."
};
if (typeof ($) !== "function") {
@@ -93,6 +98,11 @@
isDisconnecting = function (connection) {
return connection.state === signalR.connectionState.disconnected;
},
+
+ supportsKeepAlive = function (connection) {
+ return connection._.keepAliveData.activated &&
+ connection.transport.supportsKeepAlive(connection);
+ },
configureStopReconnectingTimeout = function (connection) {
var stopReconnectingTimeout,
@@ -102,7 +112,9 @@
// Without this check if a connection is stopped then started events will be bound multiple times.
if (!connection._.configuredStopReconnectingTimeout) {
onReconnectTimeout = function (connection) {
- connection.log("Couldn't reconnect within the configured timeout (" + connection.disconnectTimeout + "ms), disconnecting.");
+ var message = signalR._.format(signalR.resources.reconnectTimeout, connection.disconnectTimeout);
+ connection.log(message);
+ $(connection).triggerHandler(events.onError, [signalR._.error(message, /* source */ "TimeoutException")]);
connection.stop(/* async */ false, /* notifyServer */ false);
};
@@ -322,6 +334,7 @@
this.url = url;
this.qs = qs;
+ this.lastError = null;
this._ = {
keepAliveData: {},
connectingMessageBuffer: new ConnectingMessageBuffer(this, function (message) {
@@ -351,6 +364,8 @@
}
},
+ _originalJson: window.JSON,
+
json: window.JSON,
isCrossDomain: function (url, against) {
@@ -386,7 +401,7 @@
state: signalR.connectionState.disconnected,
- clientProtocol: "1.3",
+ clientProtocol: "1.4",
reconnectDelay: 2000,
@@ -413,6 +428,8 @@
deferred = connection._deferral || $.Deferred(), // Check to see if there is a pre-existing deferral that's being built on, if so we want to keep using it
parser = window.document.createElement("a");
+ connection.lastError = null;
+
// Persist the deferral so that if start is called multiple times the same deferral is used.
connection._deferral = deferred;
@@ -470,14 +487,14 @@
parser.href = connection.url;
if (!parser.protocol || parser.protocol === ":") {
connection.protocol = window.document.location.protocol;
- connection.host = window.document.location.host;
- connection.baseUrl = connection.protocol + "//" + connection.host;
+ connection.host = parser.host || window.document.location.host;
} else {
connection.protocol = parser.protocol;
connection.host = parser.host;
- connection.baseUrl = parser.protocol + "//" + parser.host;
}
+ connection.baseUrl = connection.protocol + "//" + connection.host;
+
// Set the websocket protocol
connection.wsProtocol = connection.protocol === "https:" ? "wss://" : "ws://";
@@ -586,7 +603,7 @@
window.clearTimeout(connection._.onFailedTimeoutHandle);
- if (transport.supportsKeepAlive && connection._.keepAliveData.activated) {
+ if (supportsKeepAlive(connection)) {
signalR.transports._logic.monitorKeepAlive(connection);
}
@@ -646,109 +663,97 @@
url = signalR.transports._logic.prepareQueryString(connection, url);
- // Add the client version to the negotiate request. We utilize the same addQs method here
- // so that it can append the clientVersion appropriately to the URL
- url = signalR.transports._logic.addQs(url, {
- clientProtocol: connection.clientProtocol
- });
-
connection.log("Negotiating with '" + url + "'.");
// Save the ajax negotiate request object so we can abort it if stop is called while the request is in flight.
- connection._.negotiateRequest = $.ajax(
- $.extend({}, $.signalR.ajaxDefaults, {
- xhrFields: { withCredentials: connection.withCredentials },
- url: url,
- type: "GET",
- contentType: connection.contentType,
- data: {},
- dataType: connection.ajaxDataType,
- error: function (error, statusText) {
- // We don't want to cause any errors if we're aborting our own negotiate request.
- if (statusText !== _negotiateAbortText) {
- onFailed(error, connection);
- } else {
- // This rejection will noop if the deferred has already been resolved or rejected.
- deferred.reject(signalR._.error(resources.stoppedWhileNegotiating, null /* error */, connection._.negotiateRequest));
- }
- },
- success: function (result) {
- var res,
- keepAliveData,
- protocolError,
- transports = [],
- supportedTransports = [];
-
- try {
- res = connection._parseResponse(result);
- } catch (error) {
- onFailed(signalR._.error(resources.errorParsingNegotiateResponse, error), connection);
- return;
- }
-
- keepAliveData = connection._.keepAliveData;
- connection.appRelativeUrl = res.Url;
- connection.id = res.ConnectionId;
- connection.token = res.ConnectionToken;
- connection.webSocketServerUrl = res.WebSocketServerUrl;
-
- // Once the server has labeled the PersistentConnection as Disconnected, we should stop attempting to reconnect
- // after res.DisconnectTimeout seconds.
- connection.disconnectTimeout = res.DisconnectTimeout * 1000; // in ms
-
- // Add the TransportConnectTimeout from the response to the transportConnectTimeout from the client to calculate the total timeout
- connection._.totalTransportConnectTimeout = connection.transportConnectTimeout + res.TransportConnectTimeout * 1000;
-
- // If we have a keep alive
- if (res.KeepAliveTimeout) {
- // Register the keep alive data as activated
- keepAliveData.activated = true;
-
- // Timeout to designate when to force the connection into reconnecting converted to milliseconds
- keepAliveData.timeout = res.KeepAliveTimeout * 1000;
-
- // Timeout to designate when to warn the developer that the connection may be dead or is not responding.
- keepAliveData.timeoutWarning = keepAliveData.timeout * connection.keepAliveWarnAt;
-
- // Instantiate the frequency in which we check the keep alive. It must be short in order to not miss/pick up any changes
- connection._.beatInterval = (keepAliveData.timeout - keepAliveData.timeoutWarning) / 3;
- } else {
- keepAliveData.activated = false;
- }
-
- connection.reconnectWindow = connection.disconnectTimeout + (keepAliveData.timeout || 0);
-
- if (!res.ProtocolVersion || res.ProtocolVersion !== connection.clientProtocol) {
- protocolError = signalR._.error(signalR._.format(resources.protocolIncompatible, connection.clientProtocol, res.ProtocolVersion));
- $(connection).triggerHandler(events.onError, [protocolError]);
- deferred.reject(protocolError);
-
- return;
- }
-
- $.each(signalR.transports, function (key) {
- if ((key.indexOf("_") === 0) || (key === "webSockets" && !res.TryWebSockets)) {
- return true;
- }
- supportedTransports.push(key);
- });
-
- if ($.isArray(config.transport)) {
- $.each(config.transport, function (_, transport) {
- if ($.inArray(transport, supportedTransports) >= 0) {
- transports.push(transport);
- }
- });
- } else if (config.transport === "auto") {
- transports = supportedTransports;
- } else if ($.inArray(config.transport, supportedTransports) >= 0) {
- transports.push(config.transport);
- }
-
- initialize(transports);
+ connection._.negotiateRequest = signalR.transports._logic.ajax(connection, {
+ url: url,
+ error: function (error, statusText) {
+ // We don't want to cause any errors if we're aborting our own negotiate request.
+ if (statusText !== _negotiateAbortText) {
+ onFailed(error, connection);
+ } else {
+ // This rejection will noop if the deferred has already been resolved or rejected.
+ deferred.reject(signalR._.error(resources.stoppedWhileNegotiating, null /* error */, connection._.negotiateRequest));
}
+ },
+ success: function (result) {
+ var res,
+ keepAliveData,
+ protocolError,
+ transports = [],
+ supportedTransports = [];
+
+ try {
+ res = connection._parseResponse(result);
+ } catch (error) {
+ onFailed(signalR._.error(resources.errorParsingNegotiateResponse, error), connection);
+ return;
+ }
+
+ keepAliveData = connection._.keepAliveData;
+ connection.appRelativeUrl = res.Url;
+ connection.id = res.ConnectionId;
+ connection.token = res.ConnectionToken;
+ connection.webSocketServerUrl = res.WebSocketServerUrl;
+ connection._.longPollDelay = res.LongPollDelay * 1000; // in ms
+
+ // Once the server has labeled the PersistentConnection as Disconnected, we should stop attempting to reconnect
+ // after res.DisconnectTimeout seconds.
+ connection.disconnectTimeout = res.DisconnectTimeout * 1000; // in ms
+
+ // Add the TransportConnectTimeout from the response to the transportConnectTimeout from the client to calculate the total timeout
+ connection._.totalTransportConnectTimeout = connection.transportConnectTimeout + res.TransportConnectTimeout * 1000;
+
+ // If we have a keep alive
+ if (res.KeepAliveTimeout) {
+ // Register the keep alive data as activated
+ keepAliveData.activated = true;
+
+ // Timeout to designate when to force the connection into reconnecting converted to milliseconds
+ keepAliveData.timeout = res.KeepAliveTimeout * 1000;
+
+ // Timeout to designate when to warn the developer that the connection may be dead or is not responding.
+ keepAliveData.timeoutWarning = keepAliveData.timeout * connection.keepAliveWarnAt;
+
+ // Instantiate the frequency in which we check the keep alive. It must be short in order to not miss/pick up any changes
+ connection._.beatInterval = (keepAliveData.timeout - keepAliveData.timeoutWarning) / 3;
+ } else {
+ keepAliveData.activated = false;
+ }
+
+ connection.reconnectWindow = connection.disconnectTimeout + (keepAliveData.timeout || 0);
+
+ if (!res.ProtocolVersion || res.ProtocolVersion !== connection.clientProtocol) {
+ protocolError = signalR._.error(signalR._.format(resources.protocolIncompatible, connection.clientProtocol, res.ProtocolVersion));
+ $(connection).triggerHandler(events.onError, [protocolError]);
+ deferred.reject(protocolError);
+
+ return;
+ }
+
+ $.each(signalR.transports, function (key) {
+ if ((key.indexOf("_") === 0) || (key === "webSockets" && !res.TryWebSockets)) {
+ return true;
+ }
+ supportedTransports.push(key);
+ });
+
+ if ($.isArray(config.transport)) {
+ $.each(config.transport, function (_, transport) {
+ if ($.inArray(transport, supportedTransports) >= 0) {
+ transports.push(transport);
+ }
+ });
+ } else if (config.transport === "auto") {
+ transports = supportedTransports;
+ } else if ($.inArray(config.transport, supportedTransports) >= 0) {
+ transports.push(config.transport);
+ }
+
+ initialize(transports);
}
- ));
+ });
return deferred.promise();
},
@@ -813,6 +818,7 @@
///
var connection = this;
$(connection).bind(events.onError, function (e, errorData, sendData) {
+ connection.lastError = errorData;
// In practice 'errorData' is the SignalR built error object.
// In practice 'sendData' is undefined for all error events except those triggered by
// 'ajaxSend' and 'webSockets.send'.'sendData' is the original send payload.
@@ -882,7 +888,6 @@
}
// Always clean up private non-timeout based state.
- delete connection._deferral;
delete connection._.config;
delete connection._.deferredStartHandler;
@@ -920,7 +925,7 @@
connection.transport.abort(connection, async);
}
- if (connection.transport.supportsKeepAlive && connection._.keepAliveData.activated) {
+ if (supportsKeepAlive(connection)) {
signalR.transports._logic.stopMonitoringKeepAlive(connection);
}
@@ -933,15 +938,20 @@
delete connection._.negotiateRequest;
}
+ // Ensure that tryAbortStartRequest is called before connection._deferral is deleted
+ signalR.transports._logic.tryAbortStartRequest(connection);
+
// Trigger the disconnect event
$(connection).triggerHandler(events.onDisconnect);
+ delete connection._deferral;
delete connection.messageId;
delete connection.groupsToken;
delete connection.id;
delete connection._.pingIntervalId;
delete connection._.lastMessageAt;
delete connection._.lastActiveAt;
+ delete connection._.longPollDelay;
// Clear out our message buffer
connection._.connectingMessageBuffer.clear();
@@ -979,11 +989,11 @@
///
(function ($, window, undefined) {
- "use strict";
var signalR = $.signalR,
events = $.signalR.events,
changeState = $.signalR.changeState,
+ startAbortText = "__Start Aborted__",
transportLogic;
signalR.transports = {};
@@ -1028,95 +1038,100 @@
}
}
- function addConnectionData(url, connectionData) {
- var appender = url.indexOf("?") !== -1 ? "&" : "?";
+ function getAjaxUrl(connection, path) {
+ var url = connection.url + path;
- if (connectionData) {
- url += appender + "connectionData=" + window.encodeURIComponent(connectionData);
+ if (connection.transport) {
+ url += "?transport=" + connection.transport.name;
}
- return url;
+ return transportLogic.prepareQueryString(connection, url);
}
transportLogic = signalR.transports._logic = {
+ ajax: function (connection, options) {
+ return $.ajax(
+ $.extend(/*deep copy*/ true, {}, $.signalR.ajaxDefaults, {
+ type: "GET",
+ data: {},
+ xhrFields: { withCredentials: connection.withCredentials },
+ contentType: connection.contentType,
+ dataType: connection.ajaxDataType
+ }, options));
+ },
+
pingServer: function (connection) {
/// Pings the server
/// Connection associated with the server ping
///
- var url, deferral = $.Deferred(), xhr;
+ var url,
+ xhr,
+ deferral = $.Deferred();
if (connection.transport) {
url = connection.url + "/ping";
url = transportLogic.addQs(url, connection.qs);
- xhr = $.ajax(
- $.extend({}, $.signalR.ajaxDefaults, {
- xhrFields: { withCredentials: connection.withCredentials },
- url: url,
- type: "GET",
- contentType: connection.contentType,
- data: {},
- dataType: connection.ajaxDataType,
- success: function (result) {
- var data;
+ xhr = transportLogic.ajax(connection, {
+ url: url,
+ success: function (result) {
+ var data;
- try {
- data = connection._parseResponse(result);
- }
- catch (error) {
- deferral.reject(
- signalR._.transportError(
- signalR.resources.pingServerFailedParse,
- connection.transport,
- error,
- xhr
- )
- );
- connection.stop();
- return;
- }
+ try {
+ data = connection._parseResponse(result);
+ }
+ catch (error) {
+ deferral.reject(
+ signalR._.transportError(
+ signalR.resources.pingServerFailedParse,
+ connection.transport,
+ error,
+ xhr
+ )
+ );
+ connection.stop();
+ return;
+ }
- if (data.Response === "pong") {
- deferral.resolve();
- }
- else {
- deferral.reject(
- signalR._.transportError(
- signalR._.format(signalR.resources.pingServerFailedInvalidResponse, result.responseText),
- connection.transport,
- null /* error */,
- xhr
- )
- );
- }
- },
- error: function (error) {
- if (error.status === 401 || error.status === 403) {
- deferral.reject(
- signalR._.transportError(
- signalR._.format(signalR.resources.pingServerFailedStatusCode, error.status),
- connection.transport,
- error,
- xhr
- )
- );
- connection.stop();
- }
- else {
- deferral.reject(
- signalR._.transportError(
- signalR.resources.pingServerFailed,
- connection.transport,
- error,
- xhr
- )
- );
- }
+ if (data.Response === "pong") {
+ deferral.resolve();
+ }
+ else {
+ deferral.reject(
+ signalR._.transportError(
+ signalR._.format(signalR.resources.pingServerFailedInvalidResponse, result),
+ connection.transport,
+ null /* error */,
+ xhr
+ )
+ );
+ }
+ },
+ error: function (error) {
+ if (error.status === 401 || error.status === 403) {
+ deferral.reject(
+ signalR._.transportError(
+ signalR._.format(signalR.resources.pingServerFailedStatusCode, error.status),
+ connection.transport,
+ error,
+ xhr
+ )
+ );
+ connection.stop();
+ }
+ else {
+ deferral.reject(
+ signalR._.transportError(
+ signalR.resources.pingServerFailed,
+ connection.transport,
+ error,
+ xhr
+ )
+ );
}
}
- ));
-
+ });
}
else {
deferral.reject(
@@ -1131,9 +1146,23 @@
},
prepareQueryString: function (connection, url) {
- url = transportLogic.addQs(url, connection.qs);
+ var preparedUrl;
- return addConnectionData(url, connection.data);
+ // Use addQs to start since it handles the ?/& prefix for us
+ preparedUrl = transportLogic.addQs(url, "clientProtocol=" + connection.clientProtocol);
+
+ // Add the user-specified query string params if any
+ preparedUrl = transportLogic.addQs(preparedUrl, connection.qs);
+
+ if (connection.token) {
+ preparedUrl += "&connectionToken=" + window.encodeURIComponent(connection.token);
+ }
+
+ if (connection.data) {
+ preparedUrl += "&connectionData=" + window.encodeURIComponent(connection.data);
+ }
+
+ return preparedUrl;
},
addQs: function (url, qs) {
@@ -1165,7 +1194,7 @@
/// Gets the url for making a GET based connect request
var baseUrl = transport === "webSockets" ? "" : connection.baseUrl,
url = baseUrl + connection.appRelativeUrl,
- qs = "transport=" + transport + "&connectionToken=" + window.encodeURIComponent(connection.token);
+ qs = "transport=" + transport;
if (connection.groupsToken) {
qs += "&groupsToken=" + window.encodeURIComponent(connection.groupsToken);
@@ -1218,52 +1247,47 @@
ajaxSend: function (connection, data) {
var payload = transportLogic.stringifySend(connection, data),
- url = connection.url + "/send" + "?transport=" + connection.transport.name + "&connectionToken=" + window.encodeURIComponent(connection.token),
+ url = getAjaxUrl(connection, "/send"),
xhr,
onFail = function (error, connection) {
$(connection).triggerHandler(events.onError, [signalR._.transportError(signalR.resources.sendFailed, connection.transport, error, xhr), data]);
};
- url = transportLogic.prepareQueryString(connection, url);
- xhr = $.ajax(
- $.extend({}, $.signalR.ajaxDefaults, {
- xhrFields: { withCredentials: connection.withCredentials },
- url: url,
- type: connection.ajaxDataType === "jsonp" ? "GET" : "POST",
- contentType: signalR._.defaultContentType,
- dataType: connection.ajaxDataType,
- data: {
- data: payload
- },
- success: function (result) {
- var res;
+ xhr = transportLogic.ajax(connection, {
+ url: url,
+ type: connection.ajaxDataType === "jsonp" ? "GET" : "POST",
+ contentType: signalR._.defaultContentType,
+ data: {
+ data: payload
+ },
+ success: function (result) {
+ var res;
- if (result) {
- try {
- res = connection._parseResponse(result);
- }
- catch (error) {
- onFail(error, connection);
- connection.stop();
- return;
- }
-
- transportLogic.triggerReceived(connection, res);
+ if (result) {
+ try {
+ res = connection._parseResponse(result);
}
- },
- error: function (error, textStatus) {
- if (textStatus === "abort" || textStatus === "parsererror") {
- // The parsererror happens for sends that don't return any data, and hence
- // don't write the jsonp callback to the response. This is harder to fix on the server
- // so just hack around it on the client for now.
+ catch (error) {
+ onFail(error, connection);
+ connection.stop();
return;
}
- onFail(error, connection);
+ transportLogic.triggerReceived(connection, res);
}
+ },
+ error: function (error, textStatus) {
+ if (textStatus === "abort" || textStatus === "parsererror") {
+ // The parsererror happens for sends that don't return any data, and hence
+ // don't write the jsonp callback to the response. This is harder to fix on the server
+ // so just hack around it on the client for now.
+ return;
+ }
+
+ onFail(error, connection);
}
- ));
+ });
return xhr;
},
@@ -1276,28 +1300,81 @@
// Async by default unless explicitly overidden
async = typeof async === "undefined" ? true : async;
- var url = connection.url + "/abort" + "?transport=" + connection.transport.name + "&connectionToken=" + window.encodeURIComponent(connection.token);
- url = transportLogic.prepareQueryString(connection, url);
+ var url = getAjaxUrl(connection, "/abort");
- $.ajax(
- $.extend({}, $.signalR.ajaxDefaults, {
- xhrFields: { withCredentials: connection.withCredentials },
- url: url,
- async: async,
- timeout: 1000,
- type: "POST",
- contentType: connection.contentType,
- dataType: connection.ajaxDataType,
- data: {}
- }
- ));
+ transportLogic.ajax(connection, {
+ url: url,
+ async: async,
+ timeout: 1000,
+ type: "POST"
+ });
connection.log("Fired ajax abort async = " + async + ".");
},
- tryInitialize: function (persistentResponse, onInitialized) {
+ tryInitialize: function (connection, persistentResponse, onInitialized) {
+ var startUrl,
+ xhr,
+ rejectDeferred = function (error) {
+ var deferred = connection._deferral;
+ if (deferred) {
+ deferred.reject(error);
+ }
+ },
+ triggerStartError = function (error) {
+ $(connection).triggerHandler(events.onError, [error]);
+ rejectDeferred(error);
+ connection.stop();
+ };
+
if (persistentResponse.Initialized) {
- onInitialized();
+ startUrl = getAjaxUrl(connection, "/start");
+
+ xhr = transportLogic.ajax(connection, {
+ url: startUrl,
+ success: function (result) {
+ var data;
+
+ try {
+ data = connection._parseResponse(result);
+ } catch (error) {
+ triggerStartError(signalR._.error(
+ signalR._.format(signalR.resources.errorParsingStartResponse, result),
+ error, xhr));
+ return;
+ }
+
+ if (data.Response === "started") {
+ onInitialized();
+ } else {
+ triggerStartError(signalR._.error(
+ signalR._.format(signalR.resources.invalidStartResponse, result),
+ null /* error */, xhr));
+ }
+ },
+ error: function (error, statusText) {
+ if (statusText !== startAbortText) {
+ triggerStartError(signalR._.error(
+ signalR.resources.errorDuringStartRequest,
+ error, xhr));
+ } else {
+ // Stop has been called
+ rejectDeferred(signalR._.error(
+ signalR.resources.stoppedDuringStartRequest,
+ null /* error */, xhr));
+ }
+ }
+ });
+
+ connection._.startRequest = xhr;
+ }
+ },
+
+ tryAbortStartRequest: function (connection) {
+ if (connection._.startRequest) {
+ // If the start request has already completed this will noop.
+ connection._.startRequest.abort(startAbortText);
+ delete connection._.startRequest;
}
},
@@ -1316,14 +1393,6 @@
if (minData) {
data = transportLogic.maximizePersistentResponse(minData);
- if (data.Disconnect) {
- connection.log("Disconnect command received from server.");
-
- // Disconnected by the server
- connection.stop(false, false);
- return;
- }
-
transportLogic.updateGroups(connection, data.GroupsToken);
if (data.MessageId) {
@@ -1335,7 +1404,7 @@
transportLogic.triggerReceived(connection, message);
});
- transportLogic.tryInitialize(data, onInitialized);
+ transportLogic.tryInitialize(connection, data, onInitialized);
}
}
},
@@ -1422,8 +1491,10 @@
verifyLastActive: function (connection) {
if (new Date().getTime() - connection._.lastActiveAt >= connection.reconnectWindow) {
- connection.log("There has not been an active server connection for an extended period of time. Stopping connection.");
- connection.stop();
+ var message = signalR._.format(signalR.resources.reconnectWindowTimeout, new Date(connection._.lastActiveAt), connection.reconnectWindow);
+ connection.log(message);
+ $(connection).triggerHandler(events.onError, [signalR._.error(message, /* source */ "TimeoutException")]);
+ connection.stop(/* async */ false, /* notifyServer */ false);
return false;
}
@@ -1486,7 +1557,6 @@
///
(function ($, window, undefined) {
- "use strict";
var signalR = $.signalR,
events = $.signalR.events,
@@ -1496,7 +1566,9 @@
signalR.transports.webSockets = {
name: "webSockets",
- supportsKeepAlive: true,
+ supportsKeepAlive: function () {
+ return true;
+ },
send: function (connection, data) {
var payload = transportLogic.stringifySend(connection, data);
@@ -1638,17 +1710,22 @@
///
(function ($, window, undefined) {
- "use strict";
var signalR = $.signalR,
events = $.signalR.events,
changeState = $.signalR.changeState,
- transportLogic = signalR.transports._logic;
+ transportLogic = signalR.transports._logic,
+ clearReconnectAttemptTimeout = function (connection) {
+ window.clearTimeout(connection._.reconnectAttemptTimeoutHandle);
+ delete connection._.reconnectAttemptTimeoutHandle;
+ };
signalR.transports.serverSentEvents = {
name: "serverSentEvents",
- supportsKeepAlive: true,
+ supportsKeepAlive: function () {
+ return true;
+ },
timeOut: 3000,
@@ -1657,8 +1734,7 @@
opened = false,
$connection = $(connection),
reconnecting = !onSuccess,
- url,
- reconnectTimeout;
+ url;
if (connection.eventSource) {
connection.log("The connection already has an event source. Stopping it.");
@@ -1695,7 +1771,7 @@
}
if (reconnecting) {
- reconnectTimeout = window.setTimeout(function () {
+ connection._.reconnectAttemptTimeoutHandle = window.setTimeout(function () {
if (opened === false) {
// If we're reconnecting and the event source is attempting to connect,
// don't keep retrying. This causes duplicate connections to spawn.
@@ -1711,10 +1787,7 @@
connection.eventSource.addEventListener("open", function (e) {
connection.log("EventSource connected.");
- if (reconnectTimeout) {
- window.clearTimeout(reconnectTimeout);
- }
-
+ clearReconnectAttemptTimeout(connection);
transportLogic.clearReconnectTimeout(connection);
if (opened === false) {
@@ -1794,6 +1867,7 @@
stop: function (connection) {
// Don't trigger a reconnect after stopping
+ clearReconnectAttemptTimeout(connection);
transportLogic.clearReconnectTimeout(connection);
if (connection && connection.eventSource) {
@@ -1817,7 +1891,6 @@
///
(function ($, window, undefined) {
- "use strict";
var signalR = $.signalR,
events = $.signalR.events,
@@ -1872,7 +1945,9 @@
signalR.transports.foreverFrame = {
name: "foreverFrame",
- supportsKeepAlive: true,
+ supportsKeepAlive: function () {
+ return true;
+ },
// Added as a value here so we can create tests to verify functionality
iframeClearThreshold: 50,
@@ -1962,9 +2037,21 @@
receive: function (connection, data) {
var cw,
- body;
+ body,
+ response;
- transportLogic.processMessages(connection, data, connection.onSuccess);
+ if (connection.json !== connection._originalJson) {
+ // If there's a custom JSON parser configured then serialize the object
+ // using the original (browser) JSON parser and then deserialize it using
+ // the custom parser (connection._parseResponse does that). This is so we
+ // can easily send the response from the server as "raw" JSON but still
+ // support custom JSON deserialization in the browser.
+ data = connection._originalJson.stringify(data);
+ }
+
+ response = connection._parseResponse(data);
+
+ transportLogic.processMessages(connection, response, connection.onSuccess);
// Protect against connection stopping from a callback trigger within the processMessages above.
if (connection.state === $.signalR.connectionState.connected) {
@@ -2048,18 +2135,33 @@
///
(function ($, window, undefined) {
- "use strict";
var signalR = $.signalR,
events = $.signalR.events,
changeState = $.signalR.changeState,
isDisconnecting = $.signalR.isDisconnecting,
- transportLogic = signalR.transports._logic;
+ transportLogic = signalR.transports._logic,
+ browserSupportsXHRProgress = (function () {
+ try {
+ return "onprogress" in new window.XMLHttpRequest();
+ } catch (e) {
+ // No XHR means no XHR progress event
+ return false;
+ }
+ })();
signalR.transports.longPolling = {
name: "longPolling",
- supportsKeepAlive: false,
+ supportsKeepAlive: function (connection) {
+ return browserSupportsXHRProgress &&
+ connection.ajaxDataType !== "jsonp" &&
+ // Don't check for keep alives if there is a delay configured between poll requests.
+ // Don't check for keep alives if the server didn't send back the "LongPollDelay" as
+ // part of the response to /negotiate. That indicates the server is running an older
+ // version of SignalR that doesn't send long polling keep alives.
+ connection._.longPollDelay === 0;
+ },
reconnectDelay: 3000,
@@ -2070,10 +2172,11 @@
fireConnect = function () {
fireConnect = $.noop;
- connection.log("LongPolling connected.");
- onSuccess();
// Reset onFailed to null because it shouldn't be called again
onFailed = null;
+
+ connection.log("LongPolling connected.");
+ onSuccess();
},
tryFailConnect = function () {
if (onFailed) {
@@ -2125,123 +2228,122 @@
}
connection.log("Opening long polling request to '" + url + "'.");
- instance.pollXhr = $.ajax(
- $.extend({}, $.signalR.ajaxDefaults, {
- xhrFields: { withCredentials: connection.withCredentials },
- url: url,
- type: "GET",
- dataType: connection.ajaxDataType,
- contentType: connection.contentType,
- success: function (result) {
- var minData,
- delay = 0,
- data,
- shouldReconnect;
+ instance.pollXhr = transportLogic.ajax(connection, {
+ xhrFields: {
+ onprogress: function () {
+ transportLogic.markLastMessage(connection);
+ }
+ },
+ url: url,
+ success: function (result) {
+ var minData,
+ delay = 0,
+ data,
+ shouldReconnect;
- connection.log("Long poll complete.");
+ connection.log("Long poll complete.");
- // Reset our reconnect errors so if we transition into a reconnecting state again we trigger
- // reconnected quickly
- reconnectErrors = 0;
+ // Reset our reconnect errors so if we transition into a reconnecting state again we trigger
+ // reconnected quickly
+ reconnectErrors = 0;
- try {
- minData = connection._parseResponse(result);
- }
- catch (error) {
- transportLogic.handleParseFailure(instance, result, error, tryFailConnect, instance.pollXhr);
+ try {
+ // Remove any keep-alives from the beginning of the result
+ minData = connection._parseResponse(result);
+ }
+ catch (error) {
+ transportLogic.handleParseFailure(instance, result, error, tryFailConnect, instance.pollXhr);
+ return;
+ }
+
+ // If there's currently a timeout to trigger reconnect, fire it now before processing messages
+ if (privateData.reconnectTimeoutId !== null) {
+ fireReconnected(instance);
+ }
+
+ if (minData) {
+ data = transportLogic.maximizePersistentResponse(minData);
+ }
+
+ transportLogic.processMessages(instance, minData, fireConnect);
+
+ if (data &&
+ $.type(data.LongPollDelay) === "number") {
+ delay = data.LongPollDelay;
+ }
+
+ if (data && data.Disconnect) {
+ return;
+ }
+
+ if (isDisconnecting(instance) === true) {
+ return;
+ }
+
+ shouldReconnect = data && data.ShouldReconnect;
+ if (shouldReconnect) {
+ // Transition into the reconnecting state
+ // If this fails then that means that the user transitioned the connection into a invalid state in processMessages.
+ if (!transportLogic.ensureReconnectingState(instance)) {
return;
}
-
- // If there's currently a timeout to trigger reconnect, fire it now before processing messages
- if (privateData.reconnectTimeoutId !== null) {
- fireReconnected(instance);
- }
-
- if (minData) {
- data = transportLogic.maximizePersistentResponse(minData);
- }
-
- transportLogic.processMessages(instance, minData, fireConnect);
-
- if (data &&
- $.type(data.LongPollDelay) === "number") {
- delay = data.LongPollDelay;
- }
-
- if (data && data.Disconnect) {
- return;
- }
-
- if (isDisconnecting(instance) === true) {
- return;
- }
-
- shouldReconnect = data && data.ShouldReconnect;
- if (shouldReconnect) {
- // Transition into the reconnecting state
- // If this fails then that means that the user transitioned the connection into a invalid state in processMessages.
- if (!transportLogic.ensureReconnectingState(instance)) {
- return;
- }
- }
-
- // We never want to pass a raiseReconnect flag after a successful poll. This is handled via the error function
- if (delay > 0) {
- privateData.pollTimeoutId = window.setTimeout(function () {
- poll(instance, shouldReconnect);
- }, delay);
- } else {
- poll(instance, shouldReconnect);
- }
- },
-
- error: function (data, textStatus) {
- // Stop trying to trigger reconnect, connection is in an error state
- // If we're not in the reconnect state this will noop
- window.clearTimeout(privateData.reconnectTimeoutId);
- privateData.reconnectTimeoutId = null;
-
- if (textStatus === "abort") {
- connection.log("Aborted xhr request.");
- return;
- }
-
- if (!tryFailConnect()) {
-
- // Increment our reconnect errors, we assume all errors to be reconnect errors
- // In the case that it's our first error this will cause Reconnect to be fired
- // after 1 second due to reconnectErrors being = 1.
- reconnectErrors++;
-
- if (connection.state !== signalR.connectionState.reconnecting) {
- connection.log("An error occurred using longPolling. Status = " + textStatus + ". Response = " + data.responseText + ".");
- $(instance).triggerHandler(events.onError, [signalR._.transportError(signalR.resources.longPollFailed, connection.transport, data, instance.pollXhr)]);
- }
-
- // We check the state here to verify that we're not in an invalid state prior to verifying Reconnect.
- // If we're not in connected or reconnecting then the next ensureReconnectingState check will fail and will return.
- // Therefore we don't want to change that failure code path.
- if ((connection.state === signalR.connectionState.connected ||
- connection.state === signalR.connectionState.reconnecting) &&
- !transportLogic.verifyLastActive(connection)) {
- return;
- }
-
- // Transition into the reconnecting state
- // If this fails then that means that the user transitioned the connection into the disconnected or connecting state within the above error handler trigger.
- if (!transportLogic.ensureReconnectingState(instance)) {
- return;
- }
-
- // Call poll with the raiseReconnect flag as true after the reconnect delay
- privateData.pollTimeoutId = window.setTimeout(function () {
- poll(instance, true);
- }, that.reconnectDelay);
- }
}
- }
- ));
+ // We never want to pass a raiseReconnect flag after a successful poll. This is handled via the error function
+ if (delay > 0) {
+ privateData.pollTimeoutId = window.setTimeout(function () {
+ poll(instance, shouldReconnect);
+ }, delay);
+ } else {
+ poll(instance, shouldReconnect);
+ }
+ },
+
+ error: function (data, textStatus) {
+ // Stop trying to trigger reconnect, connection is in an error state
+ // If we're not in the reconnect state this will noop
+ window.clearTimeout(privateData.reconnectTimeoutId);
+ privateData.reconnectTimeoutId = null;
+
+ if (textStatus === "abort") {
+ connection.log("Aborted xhr request.");
+ return;
+ }
+
+ if (!tryFailConnect()) {
+
+ // Increment our reconnect errors, we assume all errors to be reconnect errors
+ // In the case that it's our first error this will cause Reconnect to be fired
+ // after 1 second due to reconnectErrors being = 1.
+ reconnectErrors++;
+
+ if (connection.state !== signalR.connectionState.reconnecting) {
+ connection.log("An error occurred using longPolling. Status = " + textStatus + ". Response = " + data.responseText + ".");
+ $(instance).triggerHandler(events.onError, [signalR._.transportError(signalR.resources.longPollFailed, connection.transport, data, instance.pollXhr)]);
+ }
+
+ // We check the state here to verify that we're not in an invalid state prior to verifying Reconnect.
+ // If we're not in connected or reconnecting then the next ensureReconnectingState check will fail and will return.
+ // Therefore we don't want to change that failure code path.
+ if ((connection.state === signalR.connectionState.connected ||
+ connection.state === signalR.connectionState.reconnecting) &&
+ !transportLogic.verifyLastActive(connection)) {
+ return;
+ }
+
+ // Transition into the reconnecting state
+ // If this fails then that means that the user transitioned the connection into the disconnected or connecting state within the above error handler trigger.
+ if (!transportLogic.ensureReconnectingState(instance)) {
+ return;
+ }
+
+ // Call poll with the raiseReconnect flag as true after the reconnect delay
+ privateData.pollTimeoutId = window.setTimeout(function () {
+ poll(instance, true);
+ }, that.reconnectDelay);
+ }
+ }
+ });
// This will only ever pass after an error has occured via the poll ajax procedure.
if (reconnecting && raiseReconnect === true) {
@@ -2257,7 +2359,9 @@
},
lostConnection: function (connection) {
- throw new Error("Lost Connection not handled for LongPolling");
+ if (connection.pollXhr) {
+ connection.pollXhr.abort("lostConnection");
+ }
},
send: function (connection, data) {
@@ -2294,7 +2398,6 @@
///
(function ($, window, undefined) {
- "use strict";
var eventNamespace = ".hubProxy",
signalR = $.signalR;
@@ -2335,11 +2438,11 @@
///
var callbacks = connection._.invocationCallbacks,
callback;
-
+
if (hasMembers(callbacks)) {
connection.log("Clearing hub invocation callbacks with error: " + error + ".");
}
-
+
// Reset the callback cache now as we have a local var referencing it
connection._.invocationCallbackId = 0;
delete connection._.invocationCallbacks;
@@ -2457,7 +2560,15 @@
// Update the hub state
$.extend(that.state, result.State);
- if (result.Error) {
+ if (result.Progress) {
+ if (d.notifyWith) {
+ // Progress is only supported in jQuery 1.7+
+ d.notifyWith(that, [result.Progress.Data]);
+ } else if(!connection._.progressjQueryVersionLogged) {
+ connection.log("A hub method invocation progress update was received but the version of jQuery in use (" + $.prototype.jquery + ") does not support progress updates. Upgrade to jQuery 1.7+ to receive progress notifications.");
+ connection._.progressjQueryVersionLogged = true;
+ }
+ } else if (result.Error) {
// Server hub method threw an exception, log it & reject the deferred
if (result.StackTrace) {
connection.log(result.Error + "\n" + result.StackTrace + ".");
@@ -2483,7 +2594,7 @@
if (!$.isEmptyObject(that.state)) {
data.S = that.state;
}
-
+
connection.log("Invoking " + that.hubName + "." + methodName);
connection.send(data);
@@ -2494,6 +2605,10 @@
return {
State: minHubResponse.S,
Result: minHubResponse.R,
+ Progress: minHubResponse.P ? {
+ Id: minHubResponse.P.I,
+ Data: minHubResponse.P.D
+ } : null,
Id: minHubResponse.I,
IsHubException: minHubResponse.H,
Error: minHubResponse.E,
@@ -2552,7 +2667,17 @@
return;
}
- if (typeof (minData.I) !== "undefined") {
+ // We have to handle progress updates first in order to ensure old clients that receive
+ // progress updates enter the return value branch and then no-op when they can't find
+ // the callback in the map (because the minData.I value will not be a valid callback ID)
+ if (typeof (minData.P) !== "undefined") {
+ // Process progress notification
+ dataCallbackId = minData.P.I.toString();
+ callback = connection._.invocationCallbacks[dataCallbackId];
+ if (callback) {
+ callback.method.call(callback.scope, minData);
+ }
+ } else if (typeof (minData.I) !== "undefined") {
// We received the return value from a server method invocation, look up callback by id and call it
dataCallbackId = minData.I.toString();
callback = connection._.invocationCallbacks[dataCallbackId];
@@ -2689,5 +2814,5 @@
/*global window:false */
///
(function ($, undefined) {
- $.signalR.version = "2.0.3";
+ $.signalR.version = "2.1.0";
}(window.jQuery));
diff --git a/Disco.Web/Disco.Web.csproj b/Disco.Web/Disco.Web.csproj
index e767baa7..ea1277c3 100644
--- a/Disco.Web/Disco.Web.csproj
+++ b/Disco.Web/Disco.Web.csproj
@@ -46,12 +46,13 @@
..\packages\MarkdownSharp.1.13.0.0\lib\35\MarkdownSharp.dll
-
+
False
- ..\packages\Microsoft.AspNet.SignalR.Core.2.0.3\lib\net45\Microsoft.AspNet.SignalR.Core.dll
+ ..\packages\Microsoft.AspNet.SignalR.Core.2.1.0\lib\net45\Microsoft.AspNet.SignalR.Core.dll
-
- ..\packages\Microsoft.AspNet.SignalR.SystemWeb.2.0.3\lib\net45\Microsoft.AspNet.SignalR.SystemWeb.dll
+
+ False
+ ..\packages\Microsoft.AspNet.SignalR.SystemWeb.2.1.0\lib\net45\Microsoft.AspNet.SignalR.SystemWeb.dll
@@ -2073,7 +2074,6 @@
-
@@ -2087,6 +2087,7 @@
+
Designer
@@ -2176,7 +2177,7 @@
False
-
+
diff --git a/Disco.Web/packages.config b/Disco.Web/packages.config
index 8c4889d3..2ad72783 100644
--- a/Disco.Web/packages.config
+++ b/Disco.Web/packages.config
@@ -12,10 +12,10 @@
-
-
-
-
+
+
+
+
diff --git a/Disco.sln b/Disco.sln
index 4af9d51e..0879dc19 100644
--- a/Disco.sln
+++ b/Disco.sln
@@ -21,7 +21,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{FE0E88
ProjectSection(SolutionItems) = preProject
.nuget\NuGet.Config = .nuget\NuGet.Config
.nuget\NuGet.exe = .nuget\NuGet.exe
- .nuget\NuGet.targets = .nuget\NuGet.targets
EndProjectSection
EndProject
Global
@@ -121,12 +120,12 @@ Global
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
- BuildVersion_BuildAction = Both
- BuildVersion_StartDate = 2001/1/1
- BuildVersion_BuildVersioningStyle = None.None.None.None
- BuildVersion_UpdateAssemblyVersion = False
- BuildVersion_UpdateFileVersion = False
- BuildVersion_DetectChanges = True
BuildVersion_UseGlobalSettings = False
+ BuildVersion_DetectChanges = True
+ BuildVersion_UpdateFileVersion = False
+ BuildVersion_UpdateAssemblyVersion = False
+ BuildVersion_BuildVersioningStyle = None.None.None.None
+ BuildVersion_StartDate = 2001/1/1
+ BuildVersion_BuildAction = Both
EndGlobalSection
EndGlobal