mirror of
https://github.com/Sonarr/Sonarr.git
synced 2024-12-14 11:23:42 +02:00
SignalR added to provide realtime episode status updates. (Series/Details and Downloading only currently)
This commit is contained in:
parent
ac84d76ef9
commit
f19721912b
@ -171,6 +171,12 @@
|
||||
<Reference Include="Prowlin">
|
||||
<HintPath>..\packages\Prowlin 0.9.4163.39219\Prowlin.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="SignalR">
|
||||
<HintPath>..\packages\SignalR.Server.0.4.0.0\lib\net40\SignalR.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="SignalR.Hosting.AspNet">
|
||||
<HintPath>..\packages\SignalR.Hosting.AspNet.0.4.0.0\lib\net40\SignalR.Hosting.AspNet.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.ComponentModel.DataAnnotations" />
|
||||
<Reference Include="System.configuration" />
|
||||
@ -299,6 +305,7 @@
|
||||
<Compile Include="Providers\QualityTypeProvider.cs" />
|
||||
<Compile Include="Providers\ReferenceDataProvider.cs" />
|
||||
<Compile Include="Providers\SearchProvider.cs" />
|
||||
<Compile Include="Providers\SignalRProvider.cs" />
|
||||
<Compile Include="Providers\SmtpProvider.cs" />
|
||||
<Compile Include="Providers\TwitterProvider.cs" />
|
||||
<Compile Include="Providers\UpdateProvider.cs" />
|
||||
|
@ -17,13 +17,15 @@ public class DownloadProvider
|
||||
private readonly ExternalNotificationProvider _externalNotificationProvider;
|
||||
private readonly ConfigProvider _configProvider;
|
||||
private readonly BlackholeProvider _blackholeProvider;
|
||||
private readonly SignalRProvider _signalRProvider;
|
||||
|
||||
private static readonly Logger logger = LogManager.GetCurrentClassLogger();
|
||||
|
||||
[Inject]
|
||||
public DownloadProvider(SabProvider sabProvider, HistoryProvider historyProvider,
|
||||
EpisodeProvider episodeProvider, ExternalNotificationProvider externalNotificationProvider,
|
||||
ConfigProvider configProvider, BlackholeProvider blackholeProvider)
|
||||
ConfigProvider configProvider, BlackholeProvider blackholeProvider,
|
||||
SignalRProvider signalRProvider)
|
||||
{
|
||||
_sabProvider = sabProvider;
|
||||
_historyProvider = historyProvider;
|
||||
@ -31,6 +33,7 @@ public DownloadProvider(SabProvider sabProvider, HistoryProvider historyProvider
|
||||
_externalNotificationProvider = externalNotificationProvider;
|
||||
_configProvider = configProvider;
|
||||
_blackholeProvider = blackholeProvider;
|
||||
_signalRProvider = signalRProvider;
|
||||
}
|
||||
|
||||
public DownloadProvider()
|
||||
@ -63,6 +66,8 @@ public virtual bool DownloadReport(EpisodeParseResult parseResult)
|
||||
|
||||
_historyProvider.Add(history);
|
||||
_episodeProvider.MarkEpisodeAsFetched(episode.EpisodeId);
|
||||
|
||||
_signalRProvider.UpdateEpisodeStatus(episode.EpisodeId, EpisodeStatusType.Downloading);
|
||||
}
|
||||
|
||||
_externalNotificationProvider.OnGrab(downloadTitle);
|
||||
|
31
NzbDrone.Core/Providers/SignalRProvider.cs
Normal file
31
NzbDrone.Core/Providers/SignalRProvider.cs
Normal file
@ -0,0 +1,31 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using NLog;
|
||||
using NzbDrone.Core.Model;
|
||||
using SignalR;
|
||||
using SignalR.Hosting.AspNet;
|
||||
using SignalR.Hubs;
|
||||
using SignalR.Infrastructure;
|
||||
|
||||
namespace NzbDrone.Core.Providers
|
||||
{
|
||||
public class SignalRProvider : Hub
|
||||
{
|
||||
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
|
||||
|
||||
public virtual void UpdateEpisodeStatus(int episodeId, EpisodeStatusType episodeStatus)
|
||||
{
|
||||
Logger.Trace("Sending Status update to client. EpisodeId: {0}, Status: {1}", episodeId, episodeStatus);
|
||||
|
||||
GetClients().updatedStatus(episodeId, episodeStatus.ToString());
|
||||
}
|
||||
|
||||
private static dynamic GetClients()
|
||||
{
|
||||
IConnectionManager connectionManager = AspNetHost.DependencyResolver.Resolve<IConnectionManager>();
|
||||
return connectionManager.GetClients<SignalRProvider>();
|
||||
}
|
||||
}
|
||||
}
|
@ -9,6 +9,8 @@
|
||||
<package id="Newtonsoft.Json" version="4.0.8" />
|
||||
<package id="Ninject" version="2.2.1.4" />
|
||||
<package id="NLog" version="2.0.0.2000" />
|
||||
<package id="SignalR.Hosting.AspNet" version="0.4.0.0" />
|
||||
<package id="SignalR.Server" version="0.4.0.0" />
|
||||
<package id="SqlServerCompact" version="4.0.8482.1" />
|
||||
<package id="twitterizer" version="2.4.0.26532" />
|
||||
<package id="WebActivator" version="1.5" />
|
||||
|
@ -59,6 +59,9 @@
|
||||
<Reference Include="MvcMiniProfiler, Version=1.9.0.0, Culture=neutral, PublicKeyToken=b44f9351044011a3, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\MiniProfiler.1.9\lib\net40\MvcMiniProfiler.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Newtonsoft.Json, Version=4.0.8.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Newtonsoft.Json.4.0.8\lib\net40\Newtonsoft.Json.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Ninject, Version=2.2.0.0, Culture=neutral, PublicKeyToken=c7192dc5380945e7, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Ninject.2.2.1.4\lib\net40-Full\Ninject.dll</HintPath>
|
||||
</Reference>
|
||||
@ -68,6 +71,12 @@
|
||||
<Reference Include="NLog, Version=2.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NLog.2.0.0.2000\lib\net40\NLog.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="SignalR, Version=0.4.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\SignalR.Server.0.4.0.0\lib\net40\SignalR.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="SignalR.Hosting.AspNet, Version=0.4.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\SignalR.Hosting.AspNet.0.4.0.0\lib\net40\SignalR.Hosting.AspNet.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.ComponentModel.DataAnnotations">
|
||||
@ -325,13 +334,14 @@
|
||||
<Content Include="Scripts\DataTables-1.9.0\media\js\jquery.dataTables.js" />
|
||||
<Content Include="Scripts\DataTables-1.9.0\media\js\jquery.dataTables.min.js" />
|
||||
<Content Include="Scripts\DataTables-1.9.0\media\js\jquery.jeditable.js" />
|
||||
<Content Include="Scripts\DataTables-1.9.0\media\js\jquery.validate.js" />
|
||||
<Content Include="Scripts\jquery-1.7.1-vsdoc.js" />
|
||||
<Content Include="Scripts\jquery-1.7.1.min.js" />
|
||||
<Content Include="Scripts\jquery-ui-1.8.17.js" />
|
||||
<Content Include="Scripts\jquery-ui-1.8.17.min.js" />
|
||||
<Content Include="Scripts\jquery.hotkeys.js" />
|
||||
<Content Include="Scripts\jquery.livequery.js" />
|
||||
<Content Include="Scripts\jquery.signalR.js" />
|
||||
<Content Include="Scripts\jquery.signalR.min.js" />
|
||||
<Content Include="Scripts\jquery.validate-vsdoc.js" />
|
||||
<Content Include="Scripts\jquery.validate.js" />
|
||||
<Content Include="Scripts\jquery.validate.min.js" />
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -63,3 +63,35 @@ function redrawGrid() {
|
||||
function reloadGrid() {
|
||||
oTable.fnReloadAjax();
|
||||
}
|
||||
|
||||
|
||||
//SignalR
|
||||
$(function () {
|
||||
// Proxy created on the fly
|
||||
var signalRProvider = $.connection.signalRProvider;
|
||||
|
||||
// Declare a function on the chat hub so the server can invoke it
|
||||
signalRProvider.updatedStatus = function (episodeId, episodeStatus) {
|
||||
var imageSrc = '../../Content/Images/' + episodeStatus + '.png';
|
||||
var row = $('tr.episodeId_' + episodeId);
|
||||
|
||||
if (row.length == 0)
|
||||
return;
|
||||
|
||||
var statusImage = $(row).find('img.statusImage');
|
||||
|
||||
if (statusImage.length == 0)
|
||||
return;
|
||||
|
||||
statusImage.attr('alt', episodeStatus);
|
||||
statusImage.attr('title', episodeStatus);
|
||||
statusImage.attr('src', imageSrc);
|
||||
|
||||
if (episodeStatus != "Missing") {
|
||||
statusImage.parent('td').removeClass('episodeMissing');
|
||||
}
|
||||
};
|
||||
|
||||
// Start the connection
|
||||
$.connection.hub.start();
|
||||
});
|
885
NzbDrone.Web/Scripts/jquery.signalR.js
Normal file
885
NzbDrone.Web/Scripts/jquery.signalR.js
Normal file
@ -0,0 +1,885 @@
|
||||
/// <reference path="jquery-1.6.2.js" />
|
||||
(function ($, window) {
|
||||
/// <param name="$" type="jQuery" />
|
||||
"use strict";
|
||||
|
||||
if (typeof ($) !== "function") {
|
||||
// no jQuery!
|
||||
throw "SignalR: jQuery not found. Please ensure jQuery is referenced before the SignalR.js file.";
|
||||
}
|
||||
|
||||
if (!window.JSON) {
|
||||
// no JSON!
|
||||
throw "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.";
|
||||
}
|
||||
|
||||
var signalR,
|
||||
_connection,
|
||||
events = {
|
||||
onStart: "onStart",
|
||||
onStarting: "onStarting",
|
||||
onSending: "onSending",
|
||||
onReceived: "onReceived",
|
||||
onError: "onError",
|
||||
onReconnect: "onReconnect",
|
||||
onDisconnect: "onDisconnect"
|
||||
},
|
||||
log = function (msg, logging) {
|
||||
if (logging === false) {
|
||||
return;
|
||||
}
|
||||
var m;
|
||||
if (typeof (window.console) === "undefined") {
|
||||
return;
|
||||
}
|
||||
m = "[" + new Date().toTimeString() + "] SignalR: " + msg;
|
||||
if (window.console.debug) {
|
||||
window.console.debug(m);
|
||||
} else if (window.console.log) {
|
||||
window.console.log(m);
|
||||
}
|
||||
};
|
||||
|
||||
signalR = function (url, qs, logging) {
|
||||
/// <summary>Creates a new SignalR connection for the given url</summary>
|
||||
/// <param name="url" type="String">The URL of the long polling endpoint</param>
|
||||
/// <param name="qs" type="Object">
|
||||
/// [Optional] Custom querystring parameters to add to the connection URL.
|
||||
/// If an object, every non-function member will be added to the querystring.
|
||||
/// If a string, it's added to the QS as specified.
|
||||
/// </param>
|
||||
/// <param name="logging" type="Boolean">
|
||||
/// [Optional] A flag indicating whether connection logging is enabled to the browser
|
||||
/// console/log. Defaults to false.
|
||||
/// </param>
|
||||
/// <returns type="signalR" />
|
||||
|
||||
return new signalR.fn.init(url, qs, logging);
|
||||
};
|
||||
|
||||
signalR.fn = signalR.prototype = {
|
||||
init: function (url, qs, logging) {
|
||||
this.url = url;
|
||||
this.qs = qs;
|
||||
if (typeof (logging) === "boolean") {
|
||||
this.logging = logging;
|
||||
}
|
||||
},
|
||||
|
||||
logging: false,
|
||||
|
||||
reconnectDelay: 2000,
|
||||
|
||||
start: function (options, callback) {
|
||||
/// <summary>Starts the connection</summary>
|
||||
/// <param name="options" type="Object">Options map</param>
|
||||
/// <param name="callback" type="Function">A callback function to execute when the connection has started</param>
|
||||
var connection = this,
|
||||
config = {
|
||||
transport: "auto"
|
||||
},
|
||||
initialize,
|
||||
promise = $.Deferred();
|
||||
|
||||
if (connection.transport) {
|
||||
// Already started, just return
|
||||
promise.resolve(connection);
|
||||
return promise;
|
||||
}
|
||||
|
||||
if ($.type(options) === "function") {
|
||||
// Support calling with single callback parameter
|
||||
callback = options;
|
||||
} else if ($.type(options) === "object") {
|
||||
$.extend(config, options);
|
||||
if ($.type(config.callback) === "function") {
|
||||
callback = config.callback;
|
||||
}
|
||||
}
|
||||
|
||||
$(connection).bind(events.onStart, function (e, data) {
|
||||
if ($.type(callback) === "function") {
|
||||
callback.call(connection);
|
||||
}
|
||||
promise.resolve(connection);
|
||||
});
|
||||
|
||||
initialize = function (transports, index) {
|
||||
index = index || 0;
|
||||
if (index >= transports.length) {
|
||||
if (!connection.transport) {
|
||||
// No transport initialized successfully
|
||||
promise.reject("SignalR: No transport could be initialized successfully. Try specifying a different transport or none at all for auto initialization.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
var transportName = transports[index],
|
||||
transport = $.type(transportName) === "object" ? transportName : signalR.transports[transportName];
|
||||
|
||||
transport.start(connection, function () {
|
||||
connection.transport = transport;
|
||||
$(connection).trigger(events.onStart);
|
||||
}, function () {
|
||||
initialize(transports, index + 1);
|
||||
});
|
||||
};
|
||||
|
||||
window.setTimeout(function () {
|
||||
$.ajax(connection.url + "/negotiate", {
|
||||
global: false,
|
||||
type: "POST",
|
||||
data: {},
|
||||
error: function (error) {
|
||||
$(connection).trigger(events.onError, [error]);
|
||||
promise.reject("SignalR: Error during negotiation request: " + error);
|
||||
},
|
||||
success: function (res) {
|
||||
connection.appRelativeUrl = res.Url;
|
||||
connection.id = res.ConnectionId;
|
||||
connection.webSocketServerUrl = res.WebSocketServerUrl;
|
||||
|
||||
if (!res.ProtocolVersion || res.ProtocolVersion !== "1.0") {
|
||||
$(connection).trigger(events.onError, "SignalR: Incompatible protocol version.");
|
||||
promise.reject("SignalR: Incompatible protocol version.");
|
||||
return;
|
||||
}
|
||||
|
||||
$(connection).trigger(events.onStarting);
|
||||
|
||||
var transports = [],
|
||||
supportedTransports = [];
|
||||
|
||||
$.each(signalR.transports, function (key) {
|
||||
if (key === "webSockets" && !res.TryWebSockets) {
|
||||
// Server said don't even try WebSockets, but keep processing the loop
|
||||
return true;
|
||||
}
|
||||
supportedTransports.push(key);
|
||||
});
|
||||
|
||||
if ($.isArray(config.transport)) {
|
||||
// ordered list provided
|
||||
$.each(config.transport, function () {
|
||||
var transport = this;
|
||||
if ($.type(transport) === "object" || ($.type(transport) === "string" && $.inArray("" + transport, supportedTransports) >= 0)) {
|
||||
transports.push($.type(transport) === "string" ? "" + transport : transport);
|
||||
}
|
||||
});
|
||||
} else if ($.type(config.transport) === "object" ||
|
||||
$.inArray(config.transport, supportedTransports) >= 0) {
|
||||
// specific transport provided, as object or a named transport, e.g. "longPolling"
|
||||
transports.push(config.transport);
|
||||
} else { // default "auto"
|
||||
transports = supportedTransports;
|
||||
}
|
||||
initialize(transports);
|
||||
}
|
||||
});
|
||||
}, 0);
|
||||
|
||||
return promise;
|
||||
},
|
||||
|
||||
starting: function (callback) {
|
||||
/// <summary>Adds a callback that will be invoked before the connection is started</summary>
|
||||
/// <param name="callback" type="Function">A callback function to execute when the connection is starting</param>
|
||||
/// <returns type="signalR" />
|
||||
var connection = this,
|
||||
$connection = $(connection);
|
||||
|
||||
$connection.bind(events.onStarting, function (e, data) {
|
||||
callback.call(connection);
|
||||
// Unbind immediately, we don't want to call this callback again
|
||||
$connection.unbind(events.onStarting);
|
||||
});
|
||||
|
||||
return connection;
|
||||
},
|
||||
|
||||
send: function (data) {
|
||||
/// <summary>Sends data over the connection</summary>
|
||||
/// <param name="data" type="String">The data to send over the connection</param>
|
||||
/// <returns type="signalR" />
|
||||
var connection = this;
|
||||
|
||||
if (!connection.transport) {
|
||||
// Connection hasn't been started yet
|
||||
throw "SignalR: Connection must be started before data can be sent. Call .start() before .send()";
|
||||
}
|
||||
|
||||
connection.transport.send(connection, data);
|
||||
|
||||
return connection;
|
||||
},
|
||||
|
||||
sending: function (callback) {
|
||||
/// <summary>Adds a callback that will be invoked before anything is sent over the connection</summary>
|
||||
/// <param name="callback" type="Function">A callback function to execute before each time data is sent on the connection</param>
|
||||
/// <returns type="signalR" />
|
||||
var connection = this;
|
||||
$(connection).bind(events.onSending, function (e, data) {
|
||||
callback.call(connection);
|
||||
});
|
||||
return connection;
|
||||
},
|
||||
|
||||
received: function (callback) {
|
||||
/// <summary>Adds a callback that will be invoked after anything is received over the connection</summary>
|
||||
/// <param name="callback" type="Function">A callback function to execute when any data is received on the connection</param>
|
||||
/// <returns type="signalR" />
|
||||
var connection = this;
|
||||
$(connection).bind(events.onReceived, function (e, data) {
|
||||
callback.call(connection, data);
|
||||
});
|
||||
return connection;
|
||||
},
|
||||
|
||||
error: function (callback) {
|
||||
/// <summary>Adds a callback that will be invoked after an error occurs with the connection</summary>
|
||||
/// <param name="callback" type="Function">A callback function to execute when an error occurs on the connection</param>
|
||||
/// <returns type="signalR" />
|
||||
var connection = this;
|
||||
$(connection).bind(events.onError, function (e, data) {
|
||||
callback.call(connection, data);
|
||||
});
|
||||
return connection;
|
||||
},
|
||||
|
||||
disconnected: function (callback) {
|
||||
/// <summary>Adds a callback that will be invoked when the client disconnects</summary>
|
||||
/// <param name="callback" type="Function">A callback function to execute when the connection is broken</param>
|
||||
/// <returns type="signalR" />
|
||||
var connection = this;
|
||||
$(connection).bind(events.onDisconnect, function (e, data) {
|
||||
callback.call(connection);
|
||||
});
|
||||
return connection;
|
||||
},
|
||||
|
||||
reconnected: function (callback) {
|
||||
/// <summary>Adds a callback that will be invoked when the underlying transport reconnects</summary>
|
||||
/// <param name="callback" type="Function">A callback function to execute when the connection is restored</param>
|
||||
/// <returns type="signalR" />
|
||||
var connection = this;
|
||||
$(connection).bind(events.onReconnect, function (e, data) {
|
||||
callback.call(connection);
|
||||
});
|
||||
return connection;
|
||||
},
|
||||
|
||||
stop: function () {
|
||||
/// <summary>Stops listening</summary>
|
||||
/// <returns type="signalR" />
|
||||
var connection = this;
|
||||
|
||||
if (connection.transport) {
|
||||
connection.transport.stop(connection);
|
||||
connection.transport = null;
|
||||
}
|
||||
|
||||
delete connection.messageId;
|
||||
delete connection.groups;
|
||||
|
||||
// Trigger the disconnect event
|
||||
$connection.trigger(events.onDisconnect);
|
||||
|
||||
return connection;
|
||||
},
|
||||
|
||||
log: log
|
||||
};
|
||||
|
||||
signalR.fn.init.prototype = signalR.fn;
|
||||
|
||||
|
||||
// Transports
|
||||
var transportLogic = {
|
||||
|
||||
addQs: function (url, connection) {
|
||||
if (!connection.qs) {
|
||||
return url;
|
||||
}
|
||||
|
||||
if (typeof (connection.qs) === "object") {
|
||||
return url + "&" + $.param(connection.qs);
|
||||
}
|
||||
|
||||
if (typeof (connection.qs) === "string") {
|
||||
return url + "&" + connection.qs;
|
||||
}
|
||||
|
||||
return url + "&" + escape(connection.qs.toString());
|
||||
},
|
||||
|
||||
getUrl: function (connection, transport, reconnecting) {
|
||||
/// <summary>Gets the url for making a GET based connect request</summary>
|
||||
var url = connection.url,
|
||||
qs = "transport=" + transport + "&connectionId=" + window.escape(connection.id);
|
||||
|
||||
if (connection.data) {
|
||||
qs += "&connectionData=" + window.escape(connection.data);
|
||||
}
|
||||
|
||||
if (!reconnecting) {
|
||||
url = url + "/connect";
|
||||
} else {
|
||||
if (connection.messageId) {
|
||||
qs += "&messageId=" + connection.messageId;
|
||||
}
|
||||
if (connection.groups) {
|
||||
qs += "&groups=" + window.escape(JSON.stringify(connection.groups));
|
||||
}
|
||||
}
|
||||
url += "?" + qs;
|
||||
url = this.addQs(url, connection);
|
||||
return url;
|
||||
},
|
||||
|
||||
ajaxSend: function (connection, data) {
|
||||
var url = connection.url + "/send" + "?transport=" + connection.transport.name + "&connectionId=" + window.escape(connection.id);
|
||||
url = this.addQs(url, connection);
|
||||
$.ajax(url, {
|
||||
global: false,
|
||||
type: "POST",
|
||||
dataType: "json",
|
||||
data: {
|
||||
data: data
|
||||
},
|
||||
success: function (result) {
|
||||
if (result) {
|
||||
$(connection).trigger(events.onReceived, [result]);
|
||||
}
|
||||
},
|
||||
error: function (errData, textStatus) {
|
||||
if (textStatus === "abort") {
|
||||
return;
|
||||
}
|
||||
$(connection).trigger(events.onError, [errData]);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
processMessages: function (connection, data) {
|
||||
var $connection = $(connection);
|
||||
|
||||
if (data) {
|
||||
if (data.Disconnect) {
|
||||
log("Disconnect command received from server", connection.logging);
|
||||
|
||||
// Disconnected by the server
|
||||
connection.stop();
|
||||
|
||||
// Trigger the disconnect event
|
||||
$connection.trigger(events.onDisconnect);
|
||||
return;
|
||||
}
|
||||
|
||||
if (data.Messages) {
|
||||
$.each(data.Messages, function () {
|
||||
try {
|
||||
$connection.trigger(events.onReceived, [this]);
|
||||
}
|
||||
catch (e) {
|
||||
log("Error raising received " + e, connection.logging);
|
||||
$(connection).trigger(events.onError, [e]);
|
||||
}
|
||||
});
|
||||
}
|
||||
connection.messageId = data.MessageId;
|
||||
connection.groups = data.TransportData.Groups;
|
||||
}
|
||||
},
|
||||
|
||||
foreverFrame: {
|
||||
count: 0,
|
||||
connections: {}
|
||||
}
|
||||
};
|
||||
|
||||
signalR.transports = {
|
||||
|
||||
webSockets: {
|
||||
name: "webSockets",
|
||||
|
||||
send: function (connection, data) {
|
||||
connection.socket.send(data);
|
||||
},
|
||||
|
||||
start: function (connection, onSuccess, onFailed) {
|
||||
var url,
|
||||
opened = false,
|
||||
protocol;
|
||||
|
||||
if (window.MozWebSocket) {
|
||||
window.WebSocket = window.MozWebSocket;
|
||||
}
|
||||
|
||||
if (!window.WebSocket) {
|
||||
onFailed();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!connection.socket) {
|
||||
if (connection.webSocketServerUrl) {
|
||||
url = connection.webSocketServerUrl;
|
||||
}
|
||||
else {
|
||||
// Determine the protocol
|
||||
protocol = document.location.protocol === "https:" ? "wss://" : "ws://";
|
||||
|
||||
url = protocol + document.location.host + connection.appRelativeUrl;
|
||||
}
|
||||
|
||||
// Build the url
|
||||
$(connection).trigger(events.onSending);
|
||||
if (connection.data) {
|
||||
url += "?connectionData=" + connection.data + "&transport=webSockets&connectionId=" + connection.id;
|
||||
} else {
|
||||
url += "?transport=webSockets&connectionId=" + connection.id;
|
||||
}
|
||||
|
||||
connection.socket = new window.WebSocket(url);
|
||||
connection.socket.onopen = function () {
|
||||
opened = true;
|
||||
if (onSuccess) {
|
||||
onSuccess();
|
||||
}
|
||||
};
|
||||
|
||||
connection.socket.onclose = function (event) {
|
||||
if (!opened) {
|
||||
if (onFailed) {
|
||||
onFailed();
|
||||
}
|
||||
} else if (typeof event.wasClean != "undefined" && event.wasClean === false) {
|
||||
// Ideally this would use the websocket.onerror handler (rather than checking wasClean in onclose) but
|
||||
// I found in some circumstances Chrome won't call onerror. This implementation seems to work on all browsers.
|
||||
$(connection).trigger(events.onError);
|
||||
// TODO: Support reconnect attempt here, need to ensure last message id, groups, and connection data go up on reconnect
|
||||
}
|
||||
connection.socket = null;
|
||||
};
|
||||
|
||||
connection.socket.onmessage = function (event) {
|
||||
var data = window.JSON.parse(event.data),
|
||||
$connection;
|
||||
if (data) {
|
||||
$connection = $(connection);
|
||||
|
||||
if (data.Messages) {
|
||||
$.each(data.Messages, function () {
|
||||
try {
|
||||
$connection.trigger(events.onReceived, [this]);
|
||||
}
|
||||
catch (e) {
|
||||
log("Error raising received " + e, connection.logging);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
$connection.trigger(events.onReceived, [data]);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
stop: function (connection) {
|
||||
if (connection.socket !== null) {
|
||||
connection.socket.close();
|
||||
connection.socket = null;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
serverSentEvents: {
|
||||
name: "serverSentEvents",
|
||||
|
||||
timeOut: 3000,
|
||||
|
||||
start: function (connection, onSuccess, onFailed) {
|
||||
var that = this,
|
||||
opened = false,
|
||||
$connection = $(connection),
|
||||
reconnecting = !onSuccess,
|
||||
url,
|
||||
connectTimeOut;
|
||||
|
||||
if (connection.eventSource) {
|
||||
connection.stop();
|
||||
}
|
||||
|
||||
if (!window.EventSource) {
|
||||
if (onFailed) {
|
||||
onFailed();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
$connection.trigger(events.onSending);
|
||||
|
||||
url = transportLogic.getUrl(connection, this.name, reconnecting);
|
||||
|
||||
try {
|
||||
connection.eventSource = new window.EventSource(url);
|
||||
}
|
||||
catch (e) {
|
||||
log("EventSource failed trying to connect with error " + e.Message, connection.logging);
|
||||
if (onFailed) {
|
||||
// The connection failed, call the failed callback
|
||||
onFailed();
|
||||
}
|
||||
else {
|
||||
$connection.trigger(events.onError, [e]);
|
||||
if (reconnecting) {
|
||||
// If we were reconnecting, rather than doing initial connect, then try reconnect again
|
||||
log("EventSource reconnecting", connection.logging);
|
||||
that.reconnect(connection);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// After connecting, if after the specified timeout there's no response stop the connection
|
||||
// and raise on failed
|
||||
connectTimeOut = window.setTimeout(function () {
|
||||
if (opened === false) {
|
||||
log("EventSource timed out trying to connect", connection.logging);
|
||||
|
||||
if (onFailed) {
|
||||
onFailed();
|
||||
}
|
||||
|
||||
if (reconnecting) {
|
||||
// If we were reconnecting, rather than doing initial connect, then try reconnect again
|
||||
log("EventSource reconnecting", connection.logging);
|
||||
that.reconnect(connection);
|
||||
} else {
|
||||
that.stop(connection);
|
||||
}
|
||||
}
|
||||
},
|
||||
that.timeOut);
|
||||
|
||||
connection.eventSource.addEventListener("open", function (e) {
|
||||
log("EventSource connected", connection.logging);
|
||||
|
||||
if (connectTimeOut) {
|
||||
window.clearTimeout(connectTimeOut);
|
||||
}
|
||||
|
||||
if (opened === false) {
|
||||
opened = true;
|
||||
|
||||
if (onSuccess) {
|
||||
onSuccess();
|
||||
}
|
||||
|
||||
if (reconnecting) {
|
||||
$connection.trigger(events.onReconnect);
|
||||
}
|
||||
}
|
||||
}, false);
|
||||
|
||||
connection.eventSource.addEventListener("message", function (e) {
|
||||
// process messages
|
||||
if (e.data === "initialized") {
|
||||
return;
|
||||
}
|
||||
transportLogic.processMessages(connection, window.JSON.parse(e.data));
|
||||
}, false);
|
||||
|
||||
connection.eventSource.addEventListener("error", function (e) {
|
||||
if (!opened) {
|
||||
if (onFailed) {
|
||||
onFailed();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
log("EventSource readyState: " + connection.eventSource.readyState, connection.logging);
|
||||
|
||||
if (e.eventPhase === window.EventSource.CLOSED) {
|
||||
// connection closed
|
||||
if (connection.eventSource.readyState === window.EventSource.CONNECTING) {
|
||||
// We don't use the EventSource's native reconnect function as it
|
||||
// doesn't allow us to change the URL when reconnecting. We need
|
||||
// to change the URL to not include the /connect suffix, and pass
|
||||
// the last message id we received.
|
||||
log("EventSource reconnecting due to the server connection ending", connection.logging);
|
||||
that.reconnect(connection);
|
||||
}
|
||||
else {
|
||||
// The EventSource has closed, either because its close() method was called,
|
||||
// or the server sent down a "don't reconnect" frame.
|
||||
log("EventSource closed", connection.logging);
|
||||
that.stop(connection);
|
||||
}
|
||||
} else {
|
||||
// connection error
|
||||
log("EventSource error", connection.logging);
|
||||
$connection.trigger(events.onError);
|
||||
}
|
||||
}, false);
|
||||
},
|
||||
|
||||
reconnect: function (connection) {
|
||||
var that = this;
|
||||
window.setTimeout(function () {
|
||||
that.stop(connection);
|
||||
that.start(connection);
|
||||
}, connection.reconnectDelay);
|
||||
},
|
||||
|
||||
send: function (connection, data) {
|
||||
transportLogic.ajaxSend(connection, data);
|
||||
},
|
||||
|
||||
stop: function (connection) {
|
||||
if (connection && connection.eventSource) {
|
||||
connection.eventSource.close();
|
||||
connection.eventSource = null;
|
||||
delete connection.eventSource;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
foreverFrame: {
|
||||
name: "foreverFrame",
|
||||
|
||||
timeOut: 3000,
|
||||
|
||||
start: function (connection, onSuccess, onFailed) {
|
||||
var that = this,
|
||||
frameId = (transportLogic.foreverFrame.count += 1),
|
||||
url,
|
||||
connectTimeOut,
|
||||
frame = $("<iframe data-signalr-connection-id='" + connection.id + "' style='position:absolute;width:0;height:0;visibility:hidden;'></iframe>");
|
||||
|
||||
if (window.EventSource) {
|
||||
// If the browser supports SSE, don't use Forever Frame
|
||||
if (onFailed) {
|
||||
onFailed();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
$(connection).trigger(events.onSending);
|
||||
|
||||
// Build the url
|
||||
url = transportLogic.getUrl(connection, this.name);
|
||||
url += "&frameId=" + frameId;
|
||||
|
||||
frame.prop("src", url);
|
||||
transportLogic.foreverFrame.connections[frameId] = connection;
|
||||
|
||||
frame.bind("readystatechange", function () {
|
||||
if ($.inArray(this.readyState, ["loaded", "complete"]) >= 0) {
|
||||
log("Forever frame iframe readyState changed to " + this.readyState + ", reconnecting", connection.logging);
|
||||
that.reconnect(connection);
|
||||
}
|
||||
});
|
||||
|
||||
connection.frame = frame[0];
|
||||
connection.frameId = frameId;
|
||||
|
||||
if (onSuccess) {
|
||||
connection.onSuccess = onSuccess;
|
||||
}
|
||||
|
||||
$("body").append(frame);
|
||||
|
||||
// After connecting, if after the specified timeout there's no response stop the connection
|
||||
// and raise on failed
|
||||
connectTimeOut = window.setTimeout(function () {
|
||||
if (connection.onSuccess) {
|
||||
that.stop(connection);
|
||||
|
||||
if (onFailed) {
|
||||
onFailed();
|
||||
}
|
||||
}
|
||||
}, that.timeOut);
|
||||
},
|
||||
|
||||
reconnect: function (connection) {
|
||||
var that = this;
|
||||
window.setTimeout(function () {
|
||||
var frame = connection.frame,
|
||||
src = transportLogic.getUrl(connection, that.name, true) + "&frameId=" + connection.frameId;
|
||||
frame.src = src;
|
||||
}, connection.reconnectDelay);
|
||||
},
|
||||
|
||||
send: function (connection, data) {
|
||||
transportLogic.ajaxSend(connection, data);
|
||||
},
|
||||
|
||||
receive: transportLogic.processMessages,
|
||||
|
||||
stop: function (connection) {
|
||||
if (connection.frame) {
|
||||
if (connection.frame.stop) {
|
||||
connection.frame.stop();
|
||||
} else if (connection.frame.document && connection.frame.document.execCommand) {
|
||||
connection.frame.document.execCommand("Stop");
|
||||
}
|
||||
$(connection.frame).remove();
|
||||
delete transportLogic.foreverFrame.connections[connection.frameId];
|
||||
connection.frame = null;
|
||||
connection.frameId = null;
|
||||
delete connection.frame;
|
||||
delete connection.frameId;
|
||||
}
|
||||
},
|
||||
|
||||
getConnection: function (id) {
|
||||
return transportLogic.foreverFrame.connections[id];
|
||||
},
|
||||
|
||||
started: function (connection) {
|
||||
if (connection.onSuccess) {
|
||||
connection.onSuccess();
|
||||
connection.onSuccess = null;
|
||||
delete connection.onSuccess;
|
||||
}
|
||||
else {
|
||||
// If there's no onSuccess handler we assume this is a reconnect
|
||||
$(connection).trigger(events.onReconnect);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
longPolling: {
|
||||
name: "longPolling",
|
||||
|
||||
reconnectDelay: 3000,
|
||||
|
||||
start: function (connection, onSuccess, onFailed) {
|
||||
/// <summary>Starts the long polling connection</summary>
|
||||
/// <param name="connection" type="signalR">The SignalR connection to start</param>
|
||||
var that = this;
|
||||
if (connection.pollXhr) {
|
||||
connection.stop();
|
||||
}
|
||||
|
||||
connection.messageId = null;
|
||||
|
||||
window.setTimeout(function () {
|
||||
(function poll(instance, raiseReconnect) {
|
||||
$(instance).trigger(events.onSending);
|
||||
|
||||
var messageId = instance.messageId,
|
||||
connect = (messageId === null),
|
||||
url = transportLogic.getUrl(instance, that.name, !connect),
|
||||
reconnectTimeOut = null,
|
||||
reconnectFired = false;
|
||||
|
||||
instance.pollXhr = $.ajax(url, {
|
||||
global: false,
|
||||
|
||||
type: "GET",
|
||||
|
||||
dataType: "json",
|
||||
|
||||
success: function (data) {
|
||||
var delay = 0,
|
||||
timedOutReceived = false;
|
||||
|
||||
if (raiseReconnect === true) {
|
||||
// Fire the reconnect event if it hasn't been fired as yet
|
||||
if (reconnectFired === false) {
|
||||
$(instance).trigger(events.onReconnect);
|
||||
reconnectFired = true;
|
||||
}
|
||||
}
|
||||
|
||||
transportLogic.processMessages(instance, data);
|
||||
if (data && $.type(data.TransportData.LongPollDelay) === "number") {
|
||||
delay = data.TransportData.LongPollDelay;
|
||||
}
|
||||
|
||||
if (data && data.TimedOut) {
|
||||
timedOutReceived = data.TimedOut;
|
||||
}
|
||||
|
||||
if (delay > 0) {
|
||||
window.setTimeout(function () {
|
||||
poll(instance, timedOutReceived);
|
||||
}, delay);
|
||||
} else {
|
||||
poll(instance, timedOutReceived);
|
||||
}
|
||||
},
|
||||
|
||||
error: function (data, textStatus) {
|
||||
if (textStatus === "abort") {
|
||||
return;
|
||||
}
|
||||
|
||||
if (reconnectTimeOut) {
|
||||
// If the request failed then we clear the timeout so that the
|
||||
// reconnect event doesn't get fired
|
||||
clearTimeout(reconnectTimeOut);
|
||||
}
|
||||
|
||||
$(instance).trigger(events.onError, [data]);
|
||||
|
||||
window.setTimeout(function () {
|
||||
poll(instance, true);
|
||||
}, connection.reconnectDelay);
|
||||
}
|
||||
});
|
||||
|
||||
if (raiseReconnect === true) {
|
||||
reconnectTimeOut = window.setTimeout(function () {
|
||||
if (reconnectFired === false) {
|
||||
$(instance).trigger(events.onReconnect);
|
||||
reconnectFired = true;
|
||||
}
|
||||
},
|
||||
that.reconnectDelay);
|
||||
}
|
||||
|
||||
} (connection));
|
||||
|
||||
// Now connected
|
||||
// There's no good way know when the long poll has actually started so
|
||||
// we assume it only takes around 150ms (max) to start the connection
|
||||
window.setTimeout(onSuccess, 150);
|
||||
|
||||
}, 250); // Have to delay initial poll so Chrome doesn't show loader spinner in tab
|
||||
},
|
||||
|
||||
send: function (connection, data) {
|
||||
transportLogic.ajaxSend(connection, data);
|
||||
},
|
||||
|
||||
stop: function (connection) {
|
||||
/// <summary>Stops the long polling connection</summary>
|
||||
/// <param name="connection" type="signalR">The SignalR connection to stop</param>
|
||||
if (connection.pollXhr) {
|
||||
connection.pollXhr.abort();
|
||||
connection.pollXhr = null;
|
||||
delete connection.pollXhr;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
signalR.noConflict = function () {
|
||||
/// <summary>Reinstates the original value of $.connection and returns the signalR object for manual assignment</summary>
|
||||
/// <returns type="signalR" />
|
||||
if ($.connection === signalR) {
|
||||
$.connection = _connection;
|
||||
}
|
||||
return signalR;
|
||||
};
|
||||
|
||||
if ($.connection) {
|
||||
_connection = $.connection;
|
||||
}
|
||||
|
||||
$.connection = $.signalR = signalR;
|
||||
|
||||
} (window.jQuery, window));
|
1
NzbDrone.Web/Scripts/jquery.signalR.min.js
vendored
Normal file
1
NzbDrone.Web/Scripts/jquery.signalR.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@ -2,7 +2,7 @@
|
||||
@using NzbDrone.Web.Helpers
|
||||
@model NzbDrone.Web.Models.EpisodeModel
|
||||
|
||||
<tr class="@Model.EpisodeId data-row@(ViewData["AltRow"] == null || !(bool)ViewData["AltRow"] ? "" : " alt-row")">
|
||||
<tr class="episodeId_@(Model.EpisodeId) data-row@(ViewData["AltRow"] == null || !(bool)ViewData["AltRow"] ? "" : " alt-row")">
|
||||
<td>@Model.EpisodeNumber</td>
|
||||
<td>@Model.Title</td>
|
||||
<td>@Model.AirDate</td>
|
||||
@ -25,7 +25,7 @@
|
||||
@*Commands Column*@
|
||||
<td class="@cellColourClass">
|
||||
<img src='../../Content/Images/@(Model.Ignored ? "ignored" : "notIgnored").png' class='ignoreEpisode ignoreEpisode_@(Model.SeasonNumber)@(Model.Ignored ? " ignored" : " ") gridImage' id='@Model.EpisodeId' title='Click to toggle episode ignore status' />
|
||||
<img src='../../Content/Images/@(Model.Status).png' alt='@Model.Status' title='@Model.Status' class='gridImage status-@Model.Status' />
|
||||
<img src='../../Content/Images/@(Model.Status).png' alt='@Model.Status' title='@Model.Status' class='gridImage status-@Model.Status statusImage' />
|
||||
@Ajax.ImageActionLink("../../Content/Images/Search.png", new { Alt = "Search", Title = "Search for episode", @class = "gridImage" }, "Search", "Episode", new { episodeId = Model.EpisodeId }, null, null)
|
||||
@Ajax.ImageActionLink("../../Content/Images/Rename.png", new { Alt = "Rename", Title = "Rename episode", @class = "gridImage" }, "Rename", "Episode", new { episodeFileId = Model.EpisodeFileId }, null, null)
|
||||
</td>
|
||||
|
@ -28,7 +28,10 @@
|
||||
@Html.IncludeScript("jquery-tgc-countdown-1.0.js")
|
||||
@Html.IncludeScript("jquery.watermark.min.js")
|
||||
@Html.IncludeScript("jquery.hotkeys.js")
|
||||
@Html.IncludeScript("jquery.signalR.min.js")
|
||||
@Html.IncludeScript("jquery.validate.min.js")
|
||||
@Html.IncludeScript("doTimeout.js")
|
||||
<script src="@Url.Content("~/signalr/hubs")" type="text/javascript"></script>
|
||||
@Html.IncludeScript("NzbDrone/localSearch.js")
|
||||
@Html.IncludeScript("NzbDrone/AutoComplete.js")
|
||||
@Html.IncludeScript("NzbDrone/Notification.js")
|
||||
@ -38,7 +41,6 @@
|
||||
@Html.IncludeScript("DataTables-1.9.0/media/js/jquery.dataTables.reloadAjax.js")
|
||||
@Html.IncludeScript("DataTables-1.9.0/media/js/jquery.dataTables.editable.js")
|
||||
@Html.IncludeScript("DataTables-1.9.0/media/js/jquery.jeditable.js")
|
||||
@Html.IncludeScript("DataTables-1.9.0/media/js/jquery.validate.js")
|
||||
@Html.IncludeScript("jquery.dataTables.4button.pagination.js")
|
||||
@RenderSection("Scripts", required: false)
|
||||
</body>
|
||||
|
@ -14,10 +14,14 @@
|
||||
<package id="Microsoft.Web.Infrastructure" version="1.0.0.0" />
|
||||
<package id="MiniProfiler" version="1.9" />
|
||||
<package id="MiniProfiler.MVC3" version="1.9.1" />
|
||||
<package id="Newtonsoft.Json" version="4.0.8" />
|
||||
<package id="Ninject" version="2.2.1.4" />
|
||||
<package id="Ninject.MVC3" version="2.2.2.0" />
|
||||
<package id="Ninject.Web.Mvc2" version="2.2.0.1" />
|
||||
<package id="NLog" version="2.0.0.2000" />
|
||||
<package id="SignalR.Hosting.AspNet" version="0.4.0.0" />
|
||||
<package id="SignalR.Js" version="0.4.0" />
|
||||
<package id="SignalR.Server" version="0.4.0.0" />
|
||||
<package id="SqlServerCompact" version="4.0.8482.1" />
|
||||
<package id="WebActivator" version="1.5" />
|
||||
</packages>
|
BIN
packages/SignalR.Hosting.AspNet.0.4.0.0/SignalR.Hosting.AspNet.0.4.0.0.nupkg
vendored
Normal file
BIN
packages/SignalR.Hosting.AspNet.0.4.0.0/SignalR.Hosting.AspNet.0.4.0.0.nupkg
vendored
Normal file
Binary file not shown.
BIN
packages/SignalR.Hosting.AspNet.0.4.0.0/lib/net40/SignalR.Hosting.AspNet.dll
vendored
Normal file
BIN
packages/SignalR.Hosting.AspNet.0.4.0.0/lib/net40/SignalR.Hosting.AspNet.dll
vendored
Normal file
Binary file not shown.
BIN
packages/SignalR.Js.0.4.0/SignalR.Js.0.4.0.nupkg
vendored
Normal file
BIN
packages/SignalR.Js.0.4.0/SignalR.Js.0.4.0.nupkg
vendored
Normal file
Binary file not shown.
885
packages/SignalR.Js.0.4.0/content/Scripts/jquery.signalR.js
vendored
Normal file
885
packages/SignalR.Js.0.4.0/content/Scripts/jquery.signalR.js
vendored
Normal file
@ -0,0 +1,885 @@
|
||||
/// <reference path="jquery-1.6.2.js" />
|
||||
(function ($, window) {
|
||||
/// <param name="$" type="jQuery" />
|
||||
"use strict";
|
||||
|
||||
if (typeof ($) !== "function") {
|
||||
// no jQuery!
|
||||
throw "SignalR: jQuery not found. Please ensure jQuery is referenced before the SignalR.js file.";
|
||||
}
|
||||
|
||||
if (!window.JSON) {
|
||||
// no JSON!
|
||||
throw "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.";
|
||||
}
|
||||
|
||||
var signalR,
|
||||
_connection,
|
||||
events = {
|
||||
onStart: "onStart",
|
||||
onStarting: "onStarting",
|
||||
onSending: "onSending",
|
||||
onReceived: "onReceived",
|
||||
onError: "onError",
|
||||
onReconnect: "onReconnect",
|
||||
onDisconnect: "onDisconnect"
|
||||
},
|
||||
log = function (msg, logging) {
|
||||
if (logging === false) {
|
||||
return;
|
||||
}
|
||||
var m;
|
||||
if (typeof (window.console) === "undefined") {
|
||||
return;
|
||||
}
|
||||
m = "[" + new Date().toTimeString() + "] SignalR: " + msg;
|
||||
if (window.console.debug) {
|
||||
window.console.debug(m);
|
||||
} else if (window.console.log) {
|
||||
window.console.log(m);
|
||||
}
|
||||
};
|
||||
|
||||
signalR = function (url, qs, logging) {
|
||||
/// <summary>Creates a new SignalR connection for the given url</summary>
|
||||
/// <param name="url" type="String">The URL of the long polling endpoint</param>
|
||||
/// <param name="qs" type="Object">
|
||||
/// [Optional] Custom querystring parameters to add to the connection URL.
|
||||
/// If an object, every non-function member will be added to the querystring.
|
||||
/// If a string, it's added to the QS as specified.
|
||||
/// </param>
|
||||
/// <param name="logging" type="Boolean">
|
||||
/// [Optional] A flag indicating whether connection logging is enabled to the browser
|
||||
/// console/log. Defaults to false.
|
||||
/// </param>
|
||||
/// <returns type="signalR" />
|
||||
|
||||
return new signalR.fn.init(url, qs, logging);
|
||||
};
|
||||
|
||||
signalR.fn = signalR.prototype = {
|
||||
init: function (url, qs, logging) {
|
||||
this.url = url;
|
||||
this.qs = qs;
|
||||
if (typeof (logging) === "boolean") {
|
||||
this.logging = logging;
|
||||
}
|
||||
},
|
||||
|
||||
logging: false,
|
||||
|
||||
reconnectDelay: 2000,
|
||||
|
||||
start: function (options, callback) {
|
||||
/// <summary>Starts the connection</summary>
|
||||
/// <param name="options" type="Object">Options map</param>
|
||||
/// <param name="callback" type="Function">A callback function to execute when the connection has started</param>
|
||||
var connection = this,
|
||||
config = {
|
||||
transport: "auto"
|
||||
},
|
||||
initialize,
|
||||
promise = $.Deferred();
|
||||
|
||||
if (connection.transport) {
|
||||
// Already started, just return
|
||||
promise.resolve(connection);
|
||||
return promise;
|
||||
}
|
||||
|
||||
if ($.type(options) === "function") {
|
||||
// Support calling with single callback parameter
|
||||
callback = options;
|
||||
} else if ($.type(options) === "object") {
|
||||
$.extend(config, options);
|
||||
if ($.type(config.callback) === "function") {
|
||||
callback = config.callback;
|
||||
}
|
||||
}
|
||||
|
||||
$(connection).bind(events.onStart, function (e, data) {
|
||||
if ($.type(callback) === "function") {
|
||||
callback.call(connection);
|
||||
}
|
||||
promise.resolve(connection);
|
||||
});
|
||||
|
||||
initialize = function (transports, index) {
|
||||
index = index || 0;
|
||||
if (index >= transports.length) {
|
||||
if (!connection.transport) {
|
||||
// No transport initialized successfully
|
||||
promise.reject("SignalR: No transport could be initialized successfully. Try specifying a different transport or none at all for auto initialization.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
var transportName = transports[index],
|
||||
transport = $.type(transportName) === "object" ? transportName : signalR.transports[transportName];
|
||||
|
||||
transport.start(connection, function () {
|
||||
connection.transport = transport;
|
||||
$(connection).trigger(events.onStart);
|
||||
}, function () {
|
||||
initialize(transports, index + 1);
|
||||
});
|
||||
};
|
||||
|
||||
window.setTimeout(function () {
|
||||
$.ajax(connection.url + "/negotiate", {
|
||||
global: false,
|
||||
type: "POST",
|
||||
data: {},
|
||||
error: function (error) {
|
||||
$(connection).trigger(events.onError, [error]);
|
||||
promise.reject("SignalR: Error during negotiation request: " + error);
|
||||
},
|
||||
success: function (res) {
|
||||
connection.appRelativeUrl = res.Url;
|
||||
connection.id = res.ConnectionId;
|
||||
connection.webSocketServerUrl = res.WebSocketServerUrl;
|
||||
|
||||
if (!res.ProtocolVersion || res.ProtocolVersion !== "1.0") {
|
||||
$(connection).trigger(events.onError, "SignalR: Incompatible protocol version.");
|
||||
promise.reject("SignalR: Incompatible protocol version.");
|
||||
return;
|
||||
}
|
||||
|
||||
$(connection).trigger(events.onStarting);
|
||||
|
||||
var transports = [],
|
||||
supportedTransports = [];
|
||||
|
||||
$.each(signalR.transports, function (key) {
|
||||
if (key === "webSockets" && !res.TryWebSockets) {
|
||||
// Server said don't even try WebSockets, but keep processing the loop
|
||||
return true;
|
||||
}
|
||||
supportedTransports.push(key);
|
||||
});
|
||||
|
||||
if ($.isArray(config.transport)) {
|
||||
// ordered list provided
|
||||
$.each(config.transport, function () {
|
||||
var transport = this;
|
||||
if ($.type(transport) === "object" || ($.type(transport) === "string" && $.inArray("" + transport, supportedTransports) >= 0)) {
|
||||
transports.push($.type(transport) === "string" ? "" + transport : transport);
|
||||
}
|
||||
});
|
||||
} else if ($.type(config.transport) === "object" ||
|
||||
$.inArray(config.transport, supportedTransports) >= 0) {
|
||||
// specific transport provided, as object or a named transport, e.g. "longPolling"
|
||||
transports.push(config.transport);
|
||||
} else { // default "auto"
|
||||
transports = supportedTransports;
|
||||
}
|
||||
initialize(transports);
|
||||
}
|
||||
});
|
||||
}, 0);
|
||||
|
||||
return promise;
|
||||
},
|
||||
|
||||
starting: function (callback) {
|
||||
/// <summary>Adds a callback that will be invoked before the connection is started</summary>
|
||||
/// <param name="callback" type="Function">A callback function to execute when the connection is starting</param>
|
||||
/// <returns type="signalR" />
|
||||
var connection = this,
|
||||
$connection = $(connection);
|
||||
|
||||
$connection.bind(events.onStarting, function (e, data) {
|
||||
callback.call(connection);
|
||||
// Unbind immediately, we don't want to call this callback again
|
||||
$connection.unbind(events.onStarting);
|
||||
});
|
||||
|
||||
return connection;
|
||||
},
|
||||
|
||||
send: function (data) {
|
||||
/// <summary>Sends data over the connection</summary>
|
||||
/// <param name="data" type="String">The data to send over the connection</param>
|
||||
/// <returns type="signalR" />
|
||||
var connection = this;
|
||||
|
||||
if (!connection.transport) {
|
||||
// Connection hasn't been started yet
|
||||
throw "SignalR: Connection must be started before data can be sent. Call .start() before .send()";
|
||||
}
|
||||
|
||||
connection.transport.send(connection, data);
|
||||
|
||||
return connection;
|
||||
},
|
||||
|
||||
sending: function (callback) {
|
||||
/// <summary>Adds a callback that will be invoked before anything is sent over the connection</summary>
|
||||
/// <param name="callback" type="Function">A callback function to execute before each time data is sent on the connection</param>
|
||||
/// <returns type="signalR" />
|
||||
var connection = this;
|
||||
$(connection).bind(events.onSending, function (e, data) {
|
||||
callback.call(connection);
|
||||
});
|
||||
return connection;
|
||||
},
|
||||
|
||||
received: function (callback) {
|
||||
/// <summary>Adds a callback that will be invoked after anything is received over the connection</summary>
|
||||
/// <param name="callback" type="Function">A callback function to execute when any data is received on the connection</param>
|
||||
/// <returns type="signalR" />
|
||||
var connection = this;
|
||||
$(connection).bind(events.onReceived, function (e, data) {
|
||||
callback.call(connection, data);
|
||||
});
|
||||
return connection;
|
||||
},
|
||||
|
||||
error: function (callback) {
|
||||
/// <summary>Adds a callback that will be invoked after an error occurs with the connection</summary>
|
||||
/// <param name="callback" type="Function">A callback function to execute when an error occurs on the connection</param>
|
||||
/// <returns type="signalR" />
|
||||
var connection = this;
|
||||
$(connection).bind(events.onError, function (e, data) {
|
||||
callback.call(connection, data);
|
||||
});
|
||||
return connection;
|
||||
},
|
||||
|
||||
disconnected: function (callback) {
|
||||
/// <summary>Adds a callback that will be invoked when the client disconnects</summary>
|
||||
/// <param name="callback" type="Function">A callback function to execute when the connection is broken</param>
|
||||
/// <returns type="signalR" />
|
||||
var connection = this;
|
||||
$(connection).bind(events.onDisconnect, function (e, data) {
|
||||
callback.call(connection);
|
||||
});
|
||||
return connection;
|
||||
},
|
||||
|
||||
reconnected: function (callback) {
|
||||
/// <summary>Adds a callback that will be invoked when the underlying transport reconnects</summary>
|
||||
/// <param name="callback" type="Function">A callback function to execute when the connection is restored</param>
|
||||
/// <returns type="signalR" />
|
||||
var connection = this;
|
||||
$(connection).bind(events.onReconnect, function (e, data) {
|
||||
callback.call(connection);
|
||||
});
|
||||
return connection;
|
||||
},
|
||||
|
||||
stop: function () {
|
||||
/// <summary>Stops listening</summary>
|
||||
/// <returns type="signalR" />
|
||||
var connection = this;
|
||||
|
||||
if (connection.transport) {
|
||||
connection.transport.stop(connection);
|
||||
connection.transport = null;
|
||||
}
|
||||
|
||||
delete connection.messageId;
|
||||
delete connection.groups;
|
||||
|
||||
// Trigger the disconnect event
|
||||
$connection.trigger(events.onDisconnect);
|
||||
|
||||
return connection;
|
||||
},
|
||||
|
||||
log: log
|
||||
};
|
||||
|
||||
signalR.fn.init.prototype = signalR.fn;
|
||||
|
||||
|
||||
// Transports
|
||||
var transportLogic = {
|
||||
|
||||
addQs: function (url, connection) {
|
||||
if (!connection.qs) {
|
||||
return url;
|
||||
}
|
||||
|
||||
if (typeof (connection.qs) === "object") {
|
||||
return url + "&" + $.param(connection.qs);
|
||||
}
|
||||
|
||||
if (typeof (connection.qs) === "string") {
|
||||
return url + "&" + connection.qs;
|
||||
}
|
||||
|
||||
return url + "&" + escape(connection.qs.toString());
|
||||
},
|
||||
|
||||
getUrl: function (connection, transport, reconnecting) {
|
||||
/// <summary>Gets the url for making a GET based connect request</summary>
|
||||
var url = connection.url,
|
||||
qs = "transport=" + transport + "&connectionId=" + window.escape(connection.id);
|
||||
|
||||
if (connection.data) {
|
||||
qs += "&connectionData=" + window.escape(connection.data);
|
||||
}
|
||||
|
||||
if (!reconnecting) {
|
||||
url = url + "/connect";
|
||||
} else {
|
||||
if (connection.messageId) {
|
||||
qs += "&messageId=" + connection.messageId;
|
||||
}
|
||||
if (connection.groups) {
|
||||
qs += "&groups=" + window.escape(JSON.stringify(connection.groups));
|
||||
}
|
||||
}
|
||||
url += "?" + qs;
|
||||
url = this.addQs(url, connection);
|
||||
return url;
|
||||
},
|
||||
|
||||
ajaxSend: function (connection, data) {
|
||||
var url = connection.url + "/send" + "?transport=" + connection.transport.name + "&connectionId=" + window.escape(connection.id);
|
||||
url = this.addQs(url, connection);
|
||||
$.ajax(url, {
|
||||
global: false,
|
||||
type: "POST",
|
||||
dataType: "json",
|
||||
data: {
|
||||
data: data
|
||||
},
|
||||
success: function (result) {
|
||||
if (result) {
|
||||
$(connection).trigger(events.onReceived, [result]);
|
||||
}
|
||||
},
|
||||
error: function (errData, textStatus) {
|
||||
if (textStatus === "abort") {
|
||||
return;
|
||||
}
|
||||
$(connection).trigger(events.onError, [errData]);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
processMessages: function (connection, data) {
|
||||
var $connection = $(connection);
|
||||
|
||||
if (data) {
|
||||
if (data.Disconnect) {
|
||||
log("Disconnect command received from server", connection.logging);
|
||||
|
||||
// Disconnected by the server
|
||||
connection.stop();
|
||||
|
||||
// Trigger the disconnect event
|
||||
$connection.trigger(events.onDisconnect);
|
||||
return;
|
||||
}
|
||||
|
||||
if (data.Messages) {
|
||||
$.each(data.Messages, function () {
|
||||
try {
|
||||
$connection.trigger(events.onReceived, [this]);
|
||||
}
|
||||
catch (e) {
|
||||
log("Error raising received " + e, connection.logging);
|
||||
$(connection).trigger(events.onError, [e]);
|
||||
}
|
||||
});
|
||||
}
|
||||
connection.messageId = data.MessageId;
|
||||
connection.groups = data.TransportData.Groups;
|
||||
}
|
||||
},
|
||||
|
||||
foreverFrame: {
|
||||
count: 0,
|
||||
connections: {}
|
||||
}
|
||||
};
|
||||
|
||||
signalR.transports = {
|
||||
|
||||
webSockets: {
|
||||
name: "webSockets",
|
||||
|
||||
send: function (connection, data) {
|
||||
connection.socket.send(data);
|
||||
},
|
||||
|
||||
start: function (connection, onSuccess, onFailed) {
|
||||
var url,
|
||||
opened = false,
|
||||
protocol;
|
||||
|
||||
if (window.MozWebSocket) {
|
||||
window.WebSocket = window.MozWebSocket;
|
||||
}
|
||||
|
||||
if (!window.WebSocket) {
|
||||
onFailed();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!connection.socket) {
|
||||
if (connection.webSocketServerUrl) {
|
||||
url = connection.webSocketServerUrl;
|
||||
}
|
||||
else {
|
||||
// Determine the protocol
|
||||
protocol = document.location.protocol === "https:" ? "wss://" : "ws://";
|
||||
|
||||
url = protocol + document.location.host + connection.appRelativeUrl;
|
||||
}
|
||||
|
||||
// Build the url
|
||||
$(connection).trigger(events.onSending);
|
||||
if (connection.data) {
|
||||
url += "?connectionData=" + connection.data + "&transport=webSockets&connectionId=" + connection.id;
|
||||
} else {
|
||||
url += "?transport=webSockets&connectionId=" + connection.id;
|
||||
}
|
||||
|
||||
connection.socket = new window.WebSocket(url);
|
||||
connection.socket.onopen = function () {
|
||||
opened = true;
|
||||
if (onSuccess) {
|
||||
onSuccess();
|
||||
}
|
||||
};
|
||||
|
||||
connection.socket.onclose = function (event) {
|
||||
if (!opened) {
|
||||
if (onFailed) {
|
||||
onFailed();
|
||||
}
|
||||
} else if (typeof event.wasClean != "undefined" && event.wasClean === false) {
|
||||
// Ideally this would use the websocket.onerror handler (rather than checking wasClean in onclose) but
|
||||
// I found in some circumstances Chrome won't call onerror. This implementation seems to work on all browsers.
|
||||
$(connection).trigger(events.onError);
|
||||
// TODO: Support reconnect attempt here, need to ensure last message id, groups, and connection data go up on reconnect
|
||||
}
|
||||
connection.socket = null;
|
||||
};
|
||||
|
||||
connection.socket.onmessage = function (event) {
|
||||
var data = window.JSON.parse(event.data),
|
||||
$connection;
|
||||
if (data) {
|
||||
$connection = $(connection);
|
||||
|
||||
if (data.Messages) {
|
||||
$.each(data.Messages, function () {
|
||||
try {
|
||||
$connection.trigger(events.onReceived, [this]);
|
||||
}
|
||||
catch (e) {
|
||||
log("Error raising received " + e, connection.logging);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
$connection.trigger(events.onReceived, [data]);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
stop: function (connection) {
|
||||
if (connection.socket !== null) {
|
||||
connection.socket.close();
|
||||
connection.socket = null;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
serverSentEvents: {
|
||||
name: "serverSentEvents",
|
||||
|
||||
timeOut: 3000,
|
||||
|
||||
start: function (connection, onSuccess, onFailed) {
|
||||
var that = this,
|
||||
opened = false,
|
||||
$connection = $(connection),
|
||||
reconnecting = !onSuccess,
|
||||
url,
|
||||
connectTimeOut;
|
||||
|
||||
if (connection.eventSource) {
|
||||
connection.stop();
|
||||
}
|
||||
|
||||
if (!window.EventSource) {
|
||||
if (onFailed) {
|
||||
onFailed();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
$connection.trigger(events.onSending);
|
||||
|
||||
url = transportLogic.getUrl(connection, this.name, reconnecting);
|
||||
|
||||
try {
|
||||
connection.eventSource = new window.EventSource(url);
|
||||
}
|
||||
catch (e) {
|
||||
log("EventSource failed trying to connect with error " + e.Message, connection.logging);
|
||||
if (onFailed) {
|
||||
// The connection failed, call the failed callback
|
||||
onFailed();
|
||||
}
|
||||
else {
|
||||
$connection.trigger(events.onError, [e]);
|
||||
if (reconnecting) {
|
||||
// If we were reconnecting, rather than doing initial connect, then try reconnect again
|
||||
log("EventSource reconnecting", connection.logging);
|
||||
that.reconnect(connection);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// After connecting, if after the specified timeout there's no response stop the connection
|
||||
// and raise on failed
|
||||
connectTimeOut = window.setTimeout(function () {
|
||||
if (opened === false) {
|
||||
log("EventSource timed out trying to connect", connection.logging);
|
||||
|
||||
if (onFailed) {
|
||||
onFailed();
|
||||
}
|
||||
|
||||
if (reconnecting) {
|
||||
// If we were reconnecting, rather than doing initial connect, then try reconnect again
|
||||
log("EventSource reconnecting", connection.logging);
|
||||
that.reconnect(connection);
|
||||
} else {
|
||||
that.stop(connection);
|
||||
}
|
||||
}
|
||||
},
|
||||
that.timeOut);
|
||||
|
||||
connection.eventSource.addEventListener("open", function (e) {
|
||||
log("EventSource connected", connection.logging);
|
||||
|
||||
if (connectTimeOut) {
|
||||
window.clearTimeout(connectTimeOut);
|
||||
}
|
||||
|
||||
if (opened === false) {
|
||||
opened = true;
|
||||
|
||||
if (onSuccess) {
|
||||
onSuccess();
|
||||
}
|
||||
|
||||
if (reconnecting) {
|
||||
$connection.trigger(events.onReconnect);
|
||||
}
|
||||
}
|
||||
}, false);
|
||||
|
||||
connection.eventSource.addEventListener("message", function (e) {
|
||||
// process messages
|
||||
if (e.data === "initialized") {
|
||||
return;
|
||||
}
|
||||
transportLogic.processMessages(connection, window.JSON.parse(e.data));
|
||||
}, false);
|
||||
|
||||
connection.eventSource.addEventListener("error", function (e) {
|
||||
if (!opened) {
|
||||
if (onFailed) {
|
||||
onFailed();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
log("EventSource readyState: " + connection.eventSource.readyState, connection.logging);
|
||||
|
||||
if (e.eventPhase === window.EventSource.CLOSED) {
|
||||
// connection closed
|
||||
if (connection.eventSource.readyState === window.EventSource.CONNECTING) {
|
||||
// We don't use the EventSource's native reconnect function as it
|
||||
// doesn't allow us to change the URL when reconnecting. We need
|
||||
// to change the URL to not include the /connect suffix, and pass
|
||||
// the last message id we received.
|
||||
log("EventSource reconnecting due to the server connection ending", connection.logging);
|
||||
that.reconnect(connection);
|
||||
}
|
||||
else {
|
||||
// The EventSource has closed, either because its close() method was called,
|
||||
// or the server sent down a "don't reconnect" frame.
|
||||
log("EventSource closed", connection.logging);
|
||||
that.stop(connection);
|
||||
}
|
||||
} else {
|
||||
// connection error
|
||||
log("EventSource error", connection.logging);
|
||||
$connection.trigger(events.onError);
|
||||
}
|
||||
}, false);
|
||||
},
|
||||
|
||||
reconnect: function (connection) {
|
||||
var that = this;
|
||||
window.setTimeout(function () {
|
||||
that.stop(connection);
|
||||
that.start(connection);
|
||||
}, connection.reconnectDelay);
|
||||
},
|
||||
|
||||
send: function (connection, data) {
|
||||
transportLogic.ajaxSend(connection, data);
|
||||
},
|
||||
|
||||
stop: function (connection) {
|
||||
if (connection && connection.eventSource) {
|
||||
connection.eventSource.close();
|
||||
connection.eventSource = null;
|
||||
delete connection.eventSource;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
foreverFrame: {
|
||||
name: "foreverFrame",
|
||||
|
||||
timeOut: 3000,
|
||||
|
||||
start: function (connection, onSuccess, onFailed) {
|
||||
var that = this,
|
||||
frameId = (transportLogic.foreverFrame.count += 1),
|
||||
url,
|
||||
connectTimeOut,
|
||||
frame = $("<iframe data-signalr-connection-id='" + connection.id + "' style='position:absolute;width:0;height:0;visibility:hidden;'></iframe>");
|
||||
|
||||
if (window.EventSource) {
|
||||
// If the browser supports SSE, don't use Forever Frame
|
||||
if (onFailed) {
|
||||
onFailed();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
$(connection).trigger(events.onSending);
|
||||
|
||||
// Build the url
|
||||
url = transportLogic.getUrl(connection, this.name);
|
||||
url += "&frameId=" + frameId;
|
||||
|
||||
frame.prop("src", url);
|
||||
transportLogic.foreverFrame.connections[frameId] = connection;
|
||||
|
||||
frame.bind("readystatechange", function () {
|
||||
if ($.inArray(this.readyState, ["loaded", "complete"]) >= 0) {
|
||||
log("Forever frame iframe readyState changed to " + this.readyState + ", reconnecting", connection.logging);
|
||||
that.reconnect(connection);
|
||||
}
|
||||
});
|
||||
|
||||
connection.frame = frame[0];
|
||||
connection.frameId = frameId;
|
||||
|
||||
if (onSuccess) {
|
||||
connection.onSuccess = onSuccess;
|
||||
}
|
||||
|
||||
$("body").append(frame);
|
||||
|
||||
// After connecting, if after the specified timeout there's no response stop the connection
|
||||
// and raise on failed
|
||||
connectTimeOut = window.setTimeout(function () {
|
||||
if (connection.onSuccess) {
|
||||
that.stop(connection);
|
||||
|
||||
if (onFailed) {
|
||||
onFailed();
|
||||
}
|
||||
}
|
||||
}, that.timeOut);
|
||||
},
|
||||
|
||||
reconnect: function (connection) {
|
||||
var that = this;
|
||||
window.setTimeout(function () {
|
||||
var frame = connection.frame,
|
||||
src = transportLogic.getUrl(connection, that.name, true) + "&frameId=" + connection.frameId;
|
||||
frame.src = src;
|
||||
}, connection.reconnectDelay);
|
||||
},
|
||||
|
||||
send: function (connection, data) {
|
||||
transportLogic.ajaxSend(connection, data);
|
||||
},
|
||||
|
||||
receive: transportLogic.processMessages,
|
||||
|
||||
stop: function (connection) {
|
||||
if (connection.frame) {
|
||||
if (connection.frame.stop) {
|
||||
connection.frame.stop();
|
||||
} else if (connection.frame.document && connection.frame.document.execCommand) {
|
||||
connection.frame.document.execCommand("Stop");
|
||||
}
|
||||
$(connection.frame).remove();
|
||||
delete transportLogic.foreverFrame.connections[connection.frameId];
|
||||
connection.frame = null;
|
||||
connection.frameId = null;
|
||||
delete connection.frame;
|
||||
delete connection.frameId;
|
||||
}
|
||||
},
|
||||
|
||||
getConnection: function (id) {
|
||||
return transportLogic.foreverFrame.connections[id];
|
||||
},
|
||||
|
||||
started: function (connection) {
|
||||
if (connection.onSuccess) {
|
||||
connection.onSuccess();
|
||||
connection.onSuccess = null;
|
||||
delete connection.onSuccess;
|
||||
}
|
||||
else {
|
||||
// If there's no onSuccess handler we assume this is a reconnect
|
||||
$(connection).trigger(events.onReconnect);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
longPolling: {
|
||||
name: "longPolling",
|
||||
|
||||
reconnectDelay: 3000,
|
||||
|
||||
start: function (connection, onSuccess, onFailed) {
|
||||
/// <summary>Starts the long polling connection</summary>
|
||||
/// <param name="connection" type="signalR">The SignalR connection to start</param>
|
||||
var that = this;
|
||||
if (connection.pollXhr) {
|
||||
connection.stop();
|
||||
}
|
||||
|
||||
connection.messageId = null;
|
||||
|
||||
window.setTimeout(function () {
|
||||
(function poll(instance, raiseReconnect) {
|
||||
$(instance).trigger(events.onSending);
|
||||
|
||||
var messageId = instance.messageId,
|
||||
connect = (messageId === null),
|
||||
url = transportLogic.getUrl(instance, that.name, !connect),
|
||||
reconnectTimeOut = null,
|
||||
reconnectFired = false;
|
||||
|
||||
instance.pollXhr = $.ajax(url, {
|
||||
global: false,
|
||||
|
||||
type: "GET",
|
||||
|
||||
dataType: "json",
|
||||
|
||||
success: function (data) {
|
||||
var delay = 0,
|
||||
timedOutReceived = false;
|
||||
|
||||
if (raiseReconnect === true) {
|
||||
// Fire the reconnect event if it hasn't been fired as yet
|
||||
if (reconnectFired === false) {
|
||||
$(instance).trigger(events.onReconnect);
|
||||
reconnectFired = true;
|
||||
}
|
||||
}
|
||||
|
||||
transportLogic.processMessages(instance, data);
|
||||
if (data && $.type(data.TransportData.LongPollDelay) === "number") {
|
||||
delay = data.TransportData.LongPollDelay;
|
||||
}
|
||||
|
||||
if (data && data.TimedOut) {
|
||||
timedOutReceived = data.TimedOut;
|
||||
}
|
||||
|
||||
if (delay > 0) {
|
||||
window.setTimeout(function () {
|
||||
poll(instance, timedOutReceived);
|
||||
}, delay);
|
||||
} else {
|
||||
poll(instance, timedOutReceived);
|
||||
}
|
||||
},
|
||||
|
||||
error: function (data, textStatus) {
|
||||
if (textStatus === "abort") {
|
||||
return;
|
||||
}
|
||||
|
||||
if (reconnectTimeOut) {
|
||||
// If the request failed then we clear the timeout so that the
|
||||
// reconnect event doesn't get fired
|
||||
clearTimeout(reconnectTimeOut);
|
||||
}
|
||||
|
||||
$(instance).trigger(events.onError, [data]);
|
||||
|
||||
window.setTimeout(function () {
|
||||
poll(instance, true);
|
||||
}, connection.reconnectDelay);
|
||||
}
|
||||
});
|
||||
|
||||
if (raiseReconnect === true) {
|
||||
reconnectTimeOut = window.setTimeout(function () {
|
||||
if (reconnectFired === false) {
|
||||
$(instance).trigger(events.onReconnect);
|
||||
reconnectFired = true;
|
||||
}
|
||||
},
|
||||
that.reconnectDelay);
|
||||
}
|
||||
|
||||
} (connection));
|
||||
|
||||
// Now connected
|
||||
// There's no good way know when the long poll has actually started so
|
||||
// we assume it only takes around 150ms (max) to start the connection
|
||||
window.setTimeout(onSuccess, 150);
|
||||
|
||||
}, 250); // Have to delay initial poll so Chrome doesn't show loader spinner in tab
|
||||
},
|
||||
|
||||
send: function (connection, data) {
|
||||
transportLogic.ajaxSend(connection, data);
|
||||
},
|
||||
|
||||
stop: function (connection) {
|
||||
/// <summary>Stops the long polling connection</summary>
|
||||
/// <param name="connection" type="signalR">The SignalR connection to stop</param>
|
||||
if (connection.pollXhr) {
|
||||
connection.pollXhr.abort();
|
||||
connection.pollXhr = null;
|
||||
delete connection.pollXhr;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
signalR.noConflict = function () {
|
||||
/// <summary>Reinstates the original value of $.connection and returns the signalR object for manual assignment</summary>
|
||||
/// <returns type="signalR" />
|
||||
if ($.connection === signalR) {
|
||||
$.connection = _connection;
|
||||
}
|
||||
return signalR;
|
||||
};
|
||||
|
||||
if ($.connection) {
|
||||
_connection = $.connection;
|
||||
}
|
||||
|
||||
$.connection = $.signalR = signalR;
|
||||
|
||||
} (window.jQuery, window));
|
1
packages/SignalR.Js.0.4.0/content/Scripts/jquery.signalR.min.js
vendored
Normal file
1
packages/SignalR.Js.0.4.0/content/Scripts/jquery.signalR.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
BIN
packages/SignalR.Server.0.4.0.0/SignalR.Server.0.4.0.0.nupkg
vendored
Normal file
BIN
packages/SignalR.Server.0.4.0.0/SignalR.Server.0.4.0.0.nupkg
vendored
Normal file
Binary file not shown.
BIN
packages/SignalR.Server.0.4.0.0/lib/net40/SignalR.dll
vendored
Normal file
BIN
packages/SignalR.Server.0.4.0.0/lib/net40/SignalR.dll
vendored
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user