jquery.signalR-1.1.3.js 88 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193
  1. /* jquery.signalR.core.js */
  2. /*global window:false */
  3. /*!
  4. * ASP.NET SignalR JavaScript Library v1.1.3
  5. * http://signalr.net/
  6. *
  7. * Copyright Microsoft Open Technologies, Inc. All rights reserved.
  8. * Licensed under the Apache 2.0
  9. * https://github.com/SignalR/SignalR/blob/master/LICENSE.md
  10. *
  11. */
  12. /// <reference path="Scripts/jquery-1.6.4.js" />
  13. (function ($, window) {
  14. "use strict";
  15. if (typeof ($) !== "function") {
  16. // no jQuery!
  17. throw new Error("SignalR: jQuery not found. Please ensure jQuery is referenced before the SignalR.js file.");
  18. }
  19. if (!window.JSON) {
  20. // no JSON!
  21. 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.");
  22. }
  23. var signalR,
  24. _connection,
  25. _pageLoaded = (window.document.readyState === "complete"),
  26. _pageWindow = $(window),
  27. events = {
  28. onStart: "onStart",
  29. onStarting: "onStarting",
  30. onReceived: "onReceived",
  31. onError: "onError",
  32. onConnectionSlow: "onConnectionSlow",
  33. onReconnecting: "onReconnecting",
  34. onReconnect: "onReconnect",
  35. onStateChanged: "onStateChanged",
  36. onDisconnect: "onDisconnect"
  37. },
  38. log = function (msg, logging) {
  39. if (logging === false) {
  40. return;
  41. }
  42. var m;
  43. if (typeof (window.console) === "undefined") {
  44. return;
  45. }
  46. m = "[" + new Date().toTimeString() + "] SignalR: " + msg;
  47. if (window.console.debug) {
  48. window.console.debug(m);
  49. } else if (window.console.log) {
  50. window.console.log(m);
  51. }
  52. },
  53. changeState = function (connection, expectedState, newState) {
  54. if (expectedState === connection.state) {
  55. connection.state = newState;
  56. $(connection).triggerHandler(events.onStateChanged, [{ oldState: expectedState, newState: newState }]);
  57. return true;
  58. }
  59. return false;
  60. },
  61. isDisconnecting = function (connection) {
  62. return connection.state === signalR.connectionState.disconnected;
  63. },
  64. configureStopReconnectingTimeout = function (connection) {
  65. var stopReconnectingTimeout,
  66. onReconnectTimeout;
  67. // Check if this connection has already been configured to stop reconnecting after a specified timeout.
  68. // Without this check if a connection is stopped then started events will be bound multiple times.
  69. if (!connection._.configuredStopReconnectingTimeout) {
  70. onReconnectTimeout = function (connection) {
  71. connection.log("Couldn't reconnect within the configured timeout (" + connection.disconnectTimeout + "ms), disconnecting.");
  72. connection.stop(/* async */ false, /* notifyServer */ false);
  73. };
  74. connection.reconnecting(function () {
  75. var connection = this;
  76. // Guard against state changing in a previous user defined even handler
  77. if (connection.state === signalR.connectionState.reconnecting) {
  78. stopReconnectingTimeout = window.setTimeout(function () { onReconnectTimeout(connection); }, connection.disconnectTimeout);
  79. }
  80. });
  81. connection.stateChanged(function (data) {
  82. if (data.oldState === signalR.connectionState.reconnecting) {
  83. // Clear the pending reconnect timeout check
  84. window.clearTimeout(stopReconnectingTimeout);
  85. }
  86. });
  87. connection._.configuredStopReconnectingTimeout = true;
  88. }
  89. };
  90. signalR = function (url, qs, logging) {
  91. /// <summary>Creates a new SignalR connection for the given url</summary>
  92. /// <param name="url" type="String">The URL of the long polling endpoint</param>
  93. /// <param name="qs" type="Object">
  94. /// [Optional] Custom querystring parameters to add to the connection URL.
  95. /// If an object, every non-function member will be added to the querystring.
  96. /// If a string, it's added to the QS as specified.
  97. /// </param>
  98. /// <param name="logging" type="Boolean">
  99. /// [Optional] A flag indicating whether connection logging is enabled to the browser
  100. /// console/log. Defaults to false.
  101. /// </param>
  102. return new signalR.fn.init(url, qs, logging);
  103. };
  104. signalR._ = {
  105. defaultContentType: "application/x-www-form-urlencoded; charset=UTF-8",
  106. ieVersion: (function () {
  107. var version,
  108. matches;
  109. if (window.navigator.appName === 'Microsoft Internet Explorer') {
  110. // Check if the user agent has the pattern "MSIE (one or more numbers).(one or more numbers)";
  111. matches = /MSIE ([0-9]+\.[0-9]+)/.exec(window.navigator.userAgent);
  112. if (matches) {
  113. version = window.parseFloat(matches[1]);
  114. }
  115. }
  116. // undefined value means not IE
  117. return version;
  118. })()
  119. };
  120. signalR.events = events;
  121. signalR.changeState = changeState;
  122. signalR.isDisconnecting = isDisconnecting;
  123. signalR.connectionState = {
  124. connecting: 0,
  125. connected: 1,
  126. reconnecting: 2,
  127. disconnected: 4
  128. };
  129. signalR.hub = {
  130. start: function () {
  131. // This will get replaced with the real hub connection start method when hubs is referenced correctly
  132. throw new Error("SignalR: Error loading hubs. Ensure your hubs reference is correct, e.g. <script src='/signalr/hubs'></script>.");
  133. }
  134. };
  135. _pageWindow.load(function () { _pageLoaded = true; });
  136. function validateTransport(requestedTransport, connection) {
  137. /// <summary>Validates the requested transport by cross checking it with the pre-defined signalR.transports</summary>
  138. /// <param name="requestedTransport" type="Object">The designated transports that the user has specified.</param>
  139. /// <param name="connection" type="signalR">The connection that will be using the requested transports. Used for logging purposes.</param>
  140. /// <returns type="Object" />
  141. if ($.isArray(requestedTransport)) {
  142. // Go through transport array and remove an "invalid" tranports
  143. for (var i = requestedTransport.length - 1; i >= 0; i--) {
  144. var transport = requestedTransport[i];
  145. if ($.type(requestedTransport) !== "object" && ($.type(transport) !== "string" || !signalR.transports[transport])) {
  146. connection.log("Invalid transport: " + transport + ", removing it from the transports list.");
  147. requestedTransport.splice(i, 1);
  148. }
  149. }
  150. // Verify we still have transports left, if we dont then we have invalid transports
  151. if (requestedTransport.length === 0) {
  152. connection.log("No transports remain within the specified transport array.");
  153. requestedTransport = null;
  154. }
  155. } else if ($.type(requestedTransport) !== "object" && !signalR.transports[requestedTransport] && requestedTransport !== "auto") {
  156. connection.log("Invalid transport: " + requestedTransport.toString());
  157. requestedTransport = null;
  158. }
  159. else if (requestedTransport === "auto" && signalR._.ieVersion <= 8)
  160. {
  161. // If we're doing an auto transport and we're IE8 then force longPolling, #1764
  162. return ["longPolling"];
  163. }
  164. return requestedTransport;
  165. }
  166. function getDefaultPort(protocol) {
  167. if(protocol === "http:") {
  168. return 80;
  169. }
  170. else if (protocol === "https:") {
  171. return 443;
  172. }
  173. }
  174. function addDefaultPort(protocol, url) {
  175. // Remove ports from url. We have to check if there's a / or end of line
  176. // following the port in order to avoid removing ports such as 8080.
  177. if(url.match(/:\d+$/)) {
  178. return url;
  179. } else {
  180. return url + ":" + getDefaultPort(protocol);
  181. }
  182. }
  183. signalR.fn = signalR.prototype = {
  184. init: function (url, qs, logging) {
  185. this.url = url;
  186. this.qs = qs;
  187. this._ = {};
  188. if (typeof (logging) === "boolean") {
  189. this.logging = logging;
  190. }
  191. },
  192. isCrossDomain: function (url, against) {
  193. /// <summary>Checks if url is cross domain</summary>
  194. /// <param name="url" type="String">The base URL</param>
  195. /// <param name="against" type="Object">
  196. /// An optional argument to compare the URL against, if not specified it will be set to window.location.
  197. /// If specified it must contain a protocol and a host property.
  198. /// </param>
  199. var link;
  200. url = $.trim(url);
  201. if (url.indexOf("http") !== 0) {
  202. return false;
  203. }
  204. against = against || window.location;
  205. // Create an anchor tag.
  206. link = window.document.createElement("a");
  207. link.href = url;
  208. // When checking for cross domain we have to special case port 80 because the window.location will remove the
  209. return link.protocol + addDefaultPort(link.protocol, link.host) !== against.protocol + addDefaultPort(against.protocol, against.host);
  210. },
  211. ajaxDataType: "json",
  212. contentType: "application/json; charset=UTF-8",
  213. logging: false,
  214. state: signalR.connectionState.disconnected,
  215. keepAliveData: {},
  216. reconnectDelay: 2000,
  217. disconnectTimeout: 30000, // This should be set by the server in response to the negotiate request (30s default)
  218. keepAliveWarnAt: 2 / 3, // Warn user of slow connection if we breach the X% mark of the keep alive timeout
  219. start: function (options, callback) {
  220. /// <summary>Starts the connection</summary>
  221. /// <param name="options" type="Object">Options map</param>
  222. /// <param name="callback" type="Function">A callback function to execute when the connection has started</param>
  223. var connection = this,
  224. config = {
  225. waitForPageLoad: true,
  226. transport: "auto",
  227. jsonp: false
  228. },
  229. initialize,
  230. 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
  231. parser = window.document.createElement("a");
  232. if ($.type(options) === "function") {
  233. // Support calling with single callback parameter
  234. callback = options;
  235. } else if ($.type(options) === "object") {
  236. $.extend(config, options);
  237. if ($.type(config.callback) === "function") {
  238. callback = config.callback;
  239. }
  240. }
  241. config.transport = validateTransport(config.transport, connection);
  242. // If the transport is invalid throw an error and abort start
  243. if (!config.transport) {
  244. throw new Error("SignalR: Invalid transport(s) specified, aborting start.");
  245. }
  246. // Check to see if start is being called prior to page load
  247. // If waitForPageLoad is true we then want to re-direct function call to the window load event
  248. if (!_pageLoaded && config.waitForPageLoad === true) {
  249. _pageWindow.load(function () {
  250. connection._deferral = deferred;
  251. connection.start(options, callback);
  252. });
  253. return deferred.promise();
  254. }
  255. configureStopReconnectingTimeout(connection);
  256. if (changeState(connection,
  257. signalR.connectionState.disconnected,
  258. signalR.connectionState.connecting) === false) {
  259. // Already started, just return
  260. deferred.resolve(connection);
  261. return deferred.promise();
  262. }
  263. // Resolve the full url
  264. parser.href = connection.url;
  265. if (!parser.protocol || parser.protocol === ":") {
  266. connection.protocol = window.document.location.protocol;
  267. connection.host = window.document.location.host;
  268. connection.baseUrl = connection.protocol + "//" + connection.host;
  269. }
  270. else {
  271. connection.protocol = parser.protocol;
  272. connection.host = parser.host;
  273. connection.baseUrl = parser.protocol + "//" + parser.host;
  274. }
  275. // Set the websocket protocol
  276. connection.wsProtocol = connection.protocol === "https:" ? "wss://" : "ws://";
  277. // If jsonp with no/auto transport is specified, then set the transport to long polling
  278. // since that is the only transport for which jsonp really makes sense.
  279. // Some developers might actually choose to specify jsonp for same origin requests
  280. // as demonstrated by Issue #623.
  281. if (config.transport === "auto" && config.jsonp === true) {
  282. config.transport = "longPolling";
  283. }
  284. if (this.isCrossDomain(connection.url)) {
  285. connection.log("Auto detected cross domain url.");
  286. if (config.transport === "auto") {
  287. // Try webSockets and longPolling since SSE doesn't support CORS
  288. // TODO: Support XDM with foreverFrame
  289. config.transport = ["webSockets", "longPolling"];
  290. }
  291. // Determine if jsonp is the only choice for negotiation, ajaxSend and ajaxAbort.
  292. // i.e. if the browser doesn't supports CORS
  293. // If it is, ignore any preference to the contrary, and switch to jsonp.
  294. if (!config.jsonp) {
  295. config.jsonp = !$.support.cors;
  296. if (config.jsonp) {
  297. connection.log("Using jsonp because this browser doesn't support CORS");
  298. }
  299. }
  300. connection.contentType = signalR._.defaultContentType;
  301. }
  302. connection.ajaxDataType = config.jsonp ? "jsonp" : "json";
  303. $(connection).bind(events.onStart, function (e, data) {
  304. if ($.type(callback) === "function") {
  305. callback.call(connection);
  306. }
  307. deferred.resolve(connection);
  308. });
  309. initialize = function (transports, index) {
  310. index = index || 0;
  311. if (index >= transports.length) {
  312. if (!connection.transport) {
  313. // No transport initialized successfully
  314. $(connection).triggerHandler(events.onError, ["SignalR: No transport could be initialized successfully. Try specifying a different transport or none at all for auto initialization."]);
  315. deferred.reject("SignalR: No transport could be initialized successfully. Try specifying a different transport or none at all for auto initialization.");
  316. // Stop the connection if it has connected and move it into the disconnected state
  317. connection.stop();
  318. }
  319. return;
  320. }
  321. var transportName = transports[index],
  322. transport = $.type(transportName) === "object" ? transportName : signalR.transports[transportName];
  323. if (transportName.indexOf("_") === 0) {
  324. // Private member
  325. initialize(transports, index + 1);
  326. return;
  327. }
  328. transport.start(connection, function () { // success
  329. if (transport.supportsKeepAlive && connection.keepAliveData.activated) {
  330. signalR.transports._logic.monitorKeepAlive(connection);
  331. }
  332. connection.transport = transport;
  333. changeState(connection,
  334. signalR.connectionState.connecting,
  335. signalR.connectionState.connected);
  336. $(connection).triggerHandler(events.onStart);
  337. _pageWindow.unload(function () { // failure
  338. connection.stop(false /* async */);
  339. });
  340. }, function () {
  341. initialize(transports, index + 1);
  342. });
  343. };
  344. var url = connection.url + "/negotiate";
  345. url = signalR.transports._logic.addQs(url, connection);
  346. connection.log("Negotiating with '" + url + "'.");
  347. $.ajax({
  348. url: url,
  349. global: false,
  350. cache: false,
  351. type: "GET",
  352. contentType: connection.contentType,
  353. data: {},
  354. dataType: connection.ajaxDataType,
  355. error: function (error) {
  356. $(connection).triggerHandler(events.onError, [error.responseText]);
  357. deferred.reject("SignalR: Error during negotiation request: " + error.responseText);
  358. // Stop the connection if negotiate failed
  359. connection.stop();
  360. },
  361. success: function (res) {
  362. var keepAliveData = connection.keepAliveData;
  363. connection.appRelativeUrl = res.Url;
  364. connection.id = res.ConnectionId;
  365. connection.token = res.ConnectionToken;
  366. connection.webSocketServerUrl = res.WebSocketServerUrl;
  367. // Once the server has labeled the PersistentConnection as Disconnected, we should stop attempting to reconnect
  368. // after res.DisconnectTimeout seconds.
  369. connection.disconnectTimeout = res.DisconnectTimeout * 1000; // in ms
  370. // If we have a keep alive
  371. if (res.KeepAliveTimeout) {
  372. // Register the keep alive data as activated
  373. keepAliveData.activated = true;
  374. // Timeout to designate when to force the connection into reconnecting converted to milliseconds
  375. keepAliveData.timeout = res.KeepAliveTimeout * 1000;
  376. // Timeout to designate when to warn the developer that the connection may be dead or is not responding.
  377. keepAliveData.timeoutWarning = keepAliveData.timeout * connection.keepAliveWarnAt;
  378. // Instantiate the frequency in which we check the keep alive. It must be short in order to not miss/pick up any changes
  379. keepAliveData.checkInterval = (keepAliveData.timeout - keepAliveData.timeoutWarning) / 3;
  380. }
  381. else {
  382. keepAliveData.activated = false;
  383. }
  384. if (!res.ProtocolVersion || res.ProtocolVersion !== "1.2") {
  385. $(connection).triggerHandler(events.onError, ["You are using a version of the client that isn't compatible with the server. Client version 1.2, server version " + res.ProtocolVersion + "."]);
  386. deferred.reject("You are using a version of the client that isn't compatible with the server. Client version 1.2, server version " + res.ProtocolVersion + ".");
  387. return;
  388. }
  389. $(connection).triggerHandler(events.onStarting);
  390. var transports = [],
  391. supportedTransports = [];
  392. $.each(signalR.transports, function (key) {
  393. if (key === "webSockets" && !res.TryWebSockets) {
  394. // Server said don't even try WebSockets, but keep processing the loop
  395. return true;
  396. }
  397. supportedTransports.push(key);
  398. });
  399. if ($.isArray(config.transport)) {
  400. // ordered list provided
  401. $.each(config.transport, function () {
  402. var transport = this;
  403. if ($.type(transport) === "object" || ($.type(transport) === "string" && $.inArray("" + transport, supportedTransports) >= 0)) {
  404. transports.push($.type(transport) === "string" ? "" + transport : transport);
  405. }
  406. });
  407. } else if ($.type(config.transport) === "object" ||
  408. $.inArray(config.transport, supportedTransports) >= 0) {
  409. // specific transport provided, as object or a named transport, e.g. "longPolling"
  410. transports.push(config.transport);
  411. } else { // default "auto"
  412. transports = supportedTransports;
  413. }
  414. initialize(transports);
  415. }
  416. });
  417. return deferred.promise();
  418. },
  419. starting: function (callback) {
  420. /// <summary>Adds a callback that will be invoked before anything is sent over the connection</summary>
  421. /// <param name="callback" type="Function">A callback function to execute before each time data is sent on the connection</param>
  422. /// <returns type="signalR" />
  423. var connection = this;
  424. $(connection).bind(events.onStarting, function (e, data) {
  425. callback.call(connection);
  426. });
  427. return connection;
  428. },
  429. send: function (data) {
  430. /// <summary>Sends data over the connection</summary>
  431. /// <param name="data" type="String">The data to send over the connection</param>
  432. /// <returns type="signalR" />
  433. var connection = this;
  434. if (connection.state === signalR.connectionState.disconnected) {
  435. // Connection hasn't been started yet
  436. throw new Error("SignalR: Connection must be started before data can be sent. Call .start() before .send()");
  437. }
  438. if (connection.state === signalR.connectionState.connecting) {
  439. // Connection hasn't been started yet
  440. throw new Error("SignalR: Connection has not been fully initialized. Use .start().done() or .start().fail() to run logic after the connection has started.");
  441. }
  442. connection.transport.send(connection, data);
  443. // REVIEW: Should we return deferred here?
  444. return connection;
  445. },
  446. received: function (callback) {
  447. /// <summary>Adds a callback that will be invoked after anything is received over the connection</summary>
  448. /// <param name="callback" type="Function">A callback function to execute when any data is received on the connection</param>
  449. /// <returns type="signalR" />
  450. var connection = this;
  451. $(connection).bind(events.onReceived, function (e, data) {
  452. callback.call(connection, data);
  453. });
  454. return connection;
  455. },
  456. stateChanged: function (callback) {
  457. /// <summary>Adds a callback that will be invoked when the connection state changes</summary>
  458. /// <param name="callback" type="Function">A callback function to execute when the connection state changes</param>
  459. /// <returns type="signalR" />
  460. var connection = this;
  461. $(connection).bind(events.onStateChanged, function (e, data) {
  462. callback.call(connection, data);
  463. });
  464. return connection;
  465. },
  466. error: function (callback) {
  467. /// <summary>Adds a callback that will be invoked after an error occurs with the connection</summary>
  468. /// <param name="callback" type="Function">A callback function to execute when an error occurs on the connection</param>
  469. /// <returns type="signalR" />
  470. var connection = this;
  471. $(connection).bind(events.onError, function (e, data) {
  472. callback.call(connection, data);
  473. });
  474. return connection;
  475. },
  476. disconnected: function (callback) {
  477. /// <summary>Adds a callback that will be invoked when the client disconnects</summary>
  478. /// <param name="callback" type="Function">A callback function to execute when the connection is broken</param>
  479. /// <returns type="signalR" />
  480. var connection = this;
  481. $(connection).bind(events.onDisconnect, function (e, data) {
  482. callback.call(connection);
  483. });
  484. return connection;
  485. },
  486. connectionSlow: function (callback) {
  487. /// <summary>Adds a callback that will be invoked when the client detects a slow connection</summary>
  488. /// <param name="callback" type="Function">A callback function to execute when the connection is slow</param>
  489. /// <returns type="signalR" />
  490. var connection = this;
  491. $(connection).bind(events.onConnectionSlow, function(e, data) {
  492. callback.call(connection);
  493. });
  494. return connection;
  495. },
  496. reconnecting: function (callback) {
  497. /// <summary>Adds a callback that will be invoked when the underlying transport begins reconnecting</summary>
  498. /// <param name="callback" type="Function">A callback function to execute when the connection enters a reconnecting state</param>
  499. /// <returns type="signalR" />
  500. var connection = this;
  501. $(connection).bind(events.onReconnecting, function (e, data) {
  502. callback.call(connection);
  503. });
  504. return connection;
  505. },
  506. reconnected: function (callback) {
  507. /// <summary>Adds a callback that will be invoked when the underlying transport reconnects</summary>
  508. /// <param name="callback" type="Function">A callback function to execute when the connection is restored</param>
  509. /// <returns type="signalR" />
  510. var connection = this;
  511. $(connection).bind(events.onReconnect, function (e, data) {
  512. callback.call(connection);
  513. });
  514. return connection;
  515. },
  516. stop: function (async, notifyServer) {
  517. /// <summary>Stops listening</summary>
  518. /// <param name="async" type="Boolean">Whether or not to asynchronously abort the connection</param>
  519. /// <param name="notifyServer" type="Boolean">Whether we want to notify the server that we are aborting the connection</param>
  520. /// <returns type="signalR" />
  521. var connection = this;
  522. if (connection.state === signalR.connectionState.disconnected) {
  523. return;
  524. }
  525. try {
  526. if (connection.transport) {
  527. if (notifyServer !== false) {
  528. connection.transport.abort(connection, async);
  529. }
  530. if (connection.transport.supportsKeepAlive && connection.keepAliveData.activated) {
  531. signalR.transports._logic.stopMonitoringKeepAlive(connection);
  532. }
  533. connection.transport.stop(connection);
  534. connection.transport = null;
  535. }
  536. // Trigger the disconnect event
  537. $(connection).triggerHandler(events.onDisconnect);
  538. delete connection.messageId;
  539. delete connection.groupsToken;
  540. // Remove the ID and the deferral on stop, this is to ensure that if a connection is restarted it takes on a new id/deferral.
  541. delete connection.id;
  542. delete connection._deferral;
  543. }
  544. finally {
  545. changeState(connection, connection.state, signalR.connectionState.disconnected);
  546. }
  547. return connection;
  548. },
  549. log: function (msg) {
  550. log(msg, this.logging);
  551. }
  552. };
  553. signalR.fn.init.prototype = signalR.fn;
  554. signalR.noConflict = function () {
  555. /// <summary>Reinstates the original value of $.connection and returns the signalR object for manual assignment</summary>
  556. /// <returns type="signalR" />
  557. if ($.connection === signalR) {
  558. $.connection = _connection;
  559. }
  560. return signalR;
  561. };
  562. if ($.connection) {
  563. _connection = $.connection;
  564. }
  565. $.connection = $.signalR = signalR;
  566. }(window.jQuery, window));
  567. /* jquery.signalR.transports.common.js */
  568. // Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
  569. /*global window:false */
  570. /// <reference path="jquery.signalR.core.js" />
  571. (function ($, window) {
  572. "use strict";
  573. var signalR = $.signalR,
  574. events = $.signalR.events,
  575. changeState = $.signalR.changeState;
  576. signalR.transports = {};
  577. function checkIfAlive(connection) {
  578. var keepAliveData = connection.keepAliveData,
  579. diff,
  580. timeElapsed;
  581. // Only check if we're connected
  582. if (connection.state === signalR.connectionState.connected) {
  583. diff = new Date();
  584. diff.setTime(diff - keepAliveData.lastKeepAlive);
  585. timeElapsed = diff.getTime();
  586. // Check if the keep alive has completely timed out
  587. if (timeElapsed >= keepAliveData.timeout) {
  588. connection.log("Keep alive timed out. Notifying transport that connection has been lost.");
  589. // Notify transport that the connection has been lost
  590. connection.transport.lostConnection(connection);
  591. }
  592. else if (timeElapsed >= keepAliveData.timeoutWarning) {
  593. // This is to assure that the user only gets a single warning
  594. if (!keepAliveData.userNotified) {
  595. connection.log("Keep alive has been missed, connection may be dead/slow.");
  596. $(connection).triggerHandler(events.onConnectionSlow);
  597. keepAliveData.userNotified = true;
  598. }
  599. }
  600. else {
  601. keepAliveData.userNotified = false;
  602. }
  603. }
  604. // Verify we're monitoring the keep alive
  605. // We don't want this as a part of the inner if statement above because we want keep alives to continue to be checked
  606. // in the event that the server comes back online (if it goes offline).
  607. if (keepAliveData.monitoring) {
  608. window.setTimeout(function () {
  609. checkIfAlive(connection);
  610. }, keepAliveData.checkInterval);
  611. }
  612. }
  613. function isConnectedOrReconnecting(connection) {
  614. return connection.state === signalR.connectionState.connected ||
  615. connection.state === signalR.connectionState.reconnecting;
  616. }
  617. signalR.transports._logic = {
  618. pingServer: function (connection, transport) {
  619. /// <summary>Pings the server</summary>
  620. /// <param name="connection" type="signalr">Connection associated with the server ping</param>
  621. /// <returns type="signalR" />
  622. var baseUrl = transport === "webSockets" ? "" : connection.baseUrl,
  623. url = baseUrl + connection.appRelativeUrl + "/ping",
  624. deferral = $.Deferred();
  625. url = this.addQs(url, connection);
  626. $.ajax({
  627. url: url,
  628. global: false,
  629. cache: false,
  630. type: "GET",
  631. contentType: connection.contentType,
  632. data: {},
  633. dataType: connection.ajaxDataType,
  634. success: function (data) {
  635. if (data.Response === "pong") {
  636. deferral.resolve();
  637. }
  638. else {
  639. deferral.reject("SignalR: Invalid ping response when pinging server: " + (data.responseText || data.statusText));
  640. }
  641. },
  642. error: function (data) {
  643. deferral.reject("SignalR: Error pinging server: " + (data.responseText || data.statusText));
  644. }
  645. });
  646. return deferral.promise();
  647. },
  648. addQs: function (url, connection) {
  649. var appender = url.indexOf("?") !== -1 ? "&" : "?",
  650. firstChar;
  651. if (!connection.qs) {
  652. return url;
  653. }
  654. if (typeof (connection.qs) === "object") {
  655. return url + appender + $.param(connection.qs);
  656. }
  657. if (typeof (connection.qs) === "string") {
  658. firstChar = connection.qs.charAt(0);
  659. if (firstChar === "?" || firstChar === "&") {
  660. appender = "";
  661. }
  662. return url + appender + connection.qs;
  663. }
  664. throw new Error("Connections query string property must be either a string or object.");
  665. },
  666. getUrl: function (connection, transport, reconnecting, poll) {
  667. /// <summary>Gets the url for making a GET based connect request</summary>
  668. var baseUrl = transport === "webSockets" ? "" : connection.baseUrl,
  669. url = baseUrl + connection.appRelativeUrl,
  670. qs = "transport=" + transport + "&connectionToken=" + window.encodeURIComponent(connection.token);
  671. if (connection.data) {
  672. qs += "&connectionData=" + window.encodeURIComponent(connection.data);
  673. }
  674. if (connection.groupsToken) {
  675. qs += "&groupsToken=" + window.encodeURIComponent(connection.groupsToken);
  676. }
  677. if (!reconnecting) {
  678. url += "/connect";
  679. } else {
  680. if (poll) {
  681. // longPolling transport specific
  682. url += "/poll";
  683. } else {
  684. url += "/reconnect";
  685. }
  686. if (connection.messageId) {
  687. qs += "&messageId=" + window.encodeURIComponent(connection.messageId);
  688. }
  689. }
  690. url += "?" + qs;
  691. url = this.addQs(url, connection);
  692. url += "&tid=" + Math.floor(Math.random() * 11);
  693. return url;
  694. },
  695. maximizePersistentResponse: function (minPersistentResponse) {
  696. return {
  697. MessageId: minPersistentResponse.C,
  698. Messages: minPersistentResponse.M,
  699. Disconnect: typeof (minPersistentResponse.D) !== "undefined" ? true : false,
  700. TimedOut: typeof (minPersistentResponse.T) !== "undefined" ? true : false,
  701. LongPollDelay: minPersistentResponse.L,
  702. GroupsToken: minPersistentResponse.G
  703. };
  704. },
  705. updateGroups: function (connection, groupsToken) {
  706. if (groupsToken) {
  707. connection.groupsToken = groupsToken;
  708. }
  709. },
  710. ajaxSend: function (connection, data) {
  711. var url = connection.url + "/send" + "?transport=" + connection.transport.name + "&connectionToken=" + window.encodeURIComponent(connection.token);
  712. url = this.addQs(url, connection);
  713. return $.ajax({
  714. url: url,
  715. global: false,
  716. type: connection.ajaxDataType === "jsonp" ? "GET" : "POST",
  717. contentType: signalR._.defaultContentType,
  718. dataType: connection.ajaxDataType,
  719. data: {
  720. data: data
  721. },
  722. success: function (result) {
  723. if (result) {
  724. $(connection).triggerHandler(events.onReceived, [result]);
  725. }
  726. },
  727. error: function (errData, textStatus) {
  728. if (textStatus === "abort" || textStatus === "parsererror") {
  729. // The parsererror happens for sends that don't return any data, and hence
  730. // don't write the jsonp callback to the response. This is harder to fix on the server
  731. // so just hack around it on the client for now.
  732. return;
  733. }
  734. $(connection).triggerHandler(events.onError, [errData, data]);
  735. }
  736. });
  737. },
  738. ajaxAbort: function (connection, async) {
  739. if (typeof (connection.transport) === "undefined") {
  740. return;
  741. }
  742. // Async by default unless explicitly overidden
  743. async = typeof async === "undefined" ? true : async;
  744. var url = connection.url + "/abort" + "?transport=" + connection.transport.name + "&connectionToken=" + window.encodeURIComponent(connection.token);
  745. url = this.addQs(url, connection);
  746. $.ajax({
  747. url: url,
  748. async: async,
  749. timeout: 1000,
  750. global: false,
  751. type: "POST",
  752. contentType: connection.contentType,
  753. dataType: connection.ajaxDataType,
  754. data: {}
  755. });
  756. connection.log("Fired ajax abort async = " + async);
  757. },
  758. processMessages: function (connection, minData) {
  759. var data;
  760. // Transport can be null if we've just closed the connection
  761. if (connection.transport) {
  762. var $connection = $(connection);
  763. // If our transport supports keep alive then we need to update the last keep alive time stamp.
  764. // Very rarely the transport can be null.
  765. if (connection.transport.supportsKeepAlive && connection.keepAliveData.activated) {
  766. this.updateKeepAlive(connection);
  767. }
  768. if (!minData) {
  769. return;
  770. }
  771. data = this.maximizePersistentResponse(minData);
  772. if (data.Disconnect) {
  773. connection.log("Disconnect command received from server");
  774. // Disconnected by the server
  775. connection.stop(false, false);
  776. return;
  777. }
  778. this.updateGroups(connection, data.GroupsToken);
  779. if (data.Messages) {
  780. $.each(data.Messages, function (index, message) {
  781. $connection.triggerHandler(events.onReceived, [message]);
  782. });
  783. }
  784. if (data.MessageId) {
  785. connection.messageId = data.MessageId;
  786. }
  787. }
  788. },
  789. monitorKeepAlive: function (connection) {
  790. var keepAliveData = connection.keepAliveData,
  791. that = this;
  792. // If we haven't initiated the keep alive timeouts then we need to
  793. if (!keepAliveData.monitoring) {
  794. keepAliveData.monitoring = true;
  795. // Initialize the keep alive time stamp ping
  796. that.updateKeepAlive(connection);
  797. // Save the function so we can unbind it on stop
  798. connection.keepAliveData.reconnectKeepAliveUpdate = function () {
  799. that.updateKeepAlive(connection);
  800. };
  801. // Update Keep alive on reconnect
  802. $(connection).bind(events.onReconnect, connection.keepAliveData.reconnectKeepAliveUpdate);
  803. connection.log("Now monitoring keep alive with a warning timeout of " + keepAliveData.timeoutWarning + " and a connection lost timeout of " + keepAliveData.timeout);
  804. // Start the monitoring of the keep alive
  805. checkIfAlive(connection);
  806. }
  807. else {
  808. connection.log("Tried to monitor keep alive but it's already being monitored");
  809. }
  810. },
  811. stopMonitoringKeepAlive: function (connection) {
  812. var keepAliveData = connection.keepAliveData;
  813. // Only attempt to stop the keep alive monitoring if its being monitored
  814. if (keepAliveData.monitoring) {
  815. // Stop monitoring
  816. keepAliveData.monitoring = false;
  817. // Remove the updateKeepAlive function from the reconnect event
  818. $(connection).unbind(events.onReconnect, connection.keepAliveData.reconnectKeepAliveUpdate);
  819. // Clear all the keep alive data
  820. connection.keepAliveData = {};
  821. connection.log("Stopping the monitoring of the keep alive");
  822. }
  823. },
  824. updateKeepAlive: function (connection) {
  825. connection.keepAliveData.lastKeepAlive = new Date();
  826. },
  827. ensureReconnectingState: function (connection) {
  828. if (changeState(connection,
  829. signalR.connectionState.connected,
  830. signalR.connectionState.reconnecting) === true) {
  831. $(connection).triggerHandler(events.onReconnecting);
  832. }
  833. return connection.state === signalR.connectionState.reconnecting;
  834. },
  835. clearReconnectTimeout: function (connection) {
  836. if (connection && connection._.reconnectTimeout) {
  837. window.clearTimeout(connection._.reconnectTimeout);
  838. delete connection._.reconnectTimeout;
  839. }
  840. },
  841. reconnect: function (connection, transportName) {
  842. var transport = signalR.transports[transportName],
  843. that = this;
  844. // We should only set a reconnectTimeout if we are currently connected
  845. // and a reconnectTimeout isn't already set.
  846. if (isConnectedOrReconnecting(connection) && !connection._.reconnectTimeout) {
  847. connection._.reconnectTimeout = window.setTimeout(function () {
  848. transport.stop(connection);
  849. if (that.ensureReconnectingState(connection)) {
  850. connection.log(transportName + " reconnecting");
  851. transport.start(connection);
  852. }
  853. }, connection.reconnectDelay);
  854. }
  855. },
  856. foreverFrame: {
  857. count: 0,
  858. connections: {}
  859. }
  860. };
  861. }(window.jQuery, window));
  862. /* jquery.signalR.transports.webSockets.js */
  863. // Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
  864. /*global window:false */
  865. /// <reference path="jquery.signalR.transports.common.js" />
  866. (function ($, window) {
  867. "use strict";
  868. var signalR = $.signalR,
  869. events = $.signalR.events,
  870. changeState = $.signalR.changeState,
  871. transportLogic = signalR.transports._logic;
  872. signalR.transports.webSockets = {
  873. name: "webSockets",
  874. supportsKeepAlive: true,
  875. send: function (connection, data) {
  876. connection.socket.send(data);
  877. },
  878. start: function (connection, onSuccess, onFailed) {
  879. var url,
  880. opened = false,
  881. that = this,
  882. reconnecting = !onSuccess,
  883. $connection = $(connection);
  884. if (!window.WebSocket) {
  885. onFailed();
  886. return;
  887. }
  888. if (!connection.socket) {
  889. if (connection.webSocketServerUrl) {
  890. url = connection.webSocketServerUrl;
  891. }
  892. else {
  893. url = connection.wsProtocol + connection.host;
  894. }
  895. url += transportLogic.getUrl(connection, this.name, reconnecting);
  896. connection.log("Connecting to websocket endpoint '" + url + "'");
  897. connection.socket = new window.WebSocket(url);
  898. connection.socket.onopen = function () {
  899. opened = true;
  900. connection.log("Websocket opened");
  901. transportLogic.clearReconnectTimeout(connection);
  902. if (onSuccess) {
  903. onSuccess();
  904. } else if (changeState(connection,
  905. signalR.connectionState.reconnecting,
  906. signalR.connectionState.connected) === true) {
  907. $connection.triggerHandler(events.onReconnect);
  908. }
  909. };
  910. connection.socket.onclose = function (event) {
  911. // Only handle a socket close if the close is from the current socket.
  912. // Sometimes on disconnect the server will push down an onclose event
  913. // to an expired socket.
  914. if (this === connection.socket) {
  915. if (!opened) {
  916. if (onFailed) {
  917. onFailed();
  918. }
  919. else if (reconnecting) {
  920. that.reconnect(connection);
  921. }
  922. return;
  923. }
  924. else if (typeof event.wasClean !== "undefined" && event.wasClean === false) {
  925. // Ideally this would use the websocket.onerror handler (rather than checking wasClean in onclose) but
  926. // I found in some circumstances Chrome won't call onerror. This implementation seems to work on all browsers.
  927. $(connection).triggerHandler(events.onError, [event.reason]);
  928. connection.log("Unclean disconnect from websocket." + event.reason);
  929. }
  930. else {
  931. connection.log("Websocket closed");
  932. }
  933. that.reconnect(connection);
  934. }
  935. };
  936. connection.socket.onmessage = function (event) {
  937. var data = window.JSON.parse(event.data),
  938. $connection = $(connection);
  939. if (data) {
  940. // data.M is PersistentResponse.Messages
  941. if ($.isEmptyObject(data) || data.M) {
  942. transportLogic.processMessages(connection, data);
  943. } else {
  944. // For websockets we need to trigger onReceived
  945. // for callbacks to outgoing hub calls.
  946. $connection.triggerHandler(events.onReceived, [data]);
  947. }
  948. }
  949. };
  950. }
  951. },
  952. reconnect: function (connection) {
  953. transportLogic.reconnect(connection, this.name);
  954. },
  955. lostConnection: function (connection) {
  956. this.reconnect(connection);
  957. },
  958. stop: function (connection) {
  959. // Don't trigger a reconnect after stopping
  960. transportLogic.clearReconnectTimeout(connection);
  961. if (connection.socket !== null) {
  962. connection.log("Closing the Websocket");
  963. connection.socket.close();
  964. connection.socket = null;
  965. }
  966. },
  967. abort: function (connection) {
  968. }
  969. };
  970. }(window.jQuery, window));
  971. /* jquery.signalR.transports.serverSentEvents.js */
  972. // Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
  973. /*global window:false */
  974. /// <reference path="jquery.signalR.transports.common.js" />
  975. (function ($, window) {
  976. "use strict";
  977. var signalR = $.signalR,
  978. events = $.signalR.events,
  979. changeState = $.signalR.changeState,
  980. transportLogic = signalR.transports._logic;
  981. signalR.transports.serverSentEvents = {
  982. name: "serverSentEvents",
  983. supportsKeepAlive: true,
  984. timeOut: 3000,
  985. start: function (connection, onSuccess, onFailed) {
  986. var that = this,
  987. opened = false,
  988. $connection = $(connection),
  989. reconnecting = !onSuccess,
  990. url,
  991. connectTimeOut;
  992. if (connection.eventSource) {
  993. connection.log("The connection already has an event source. Stopping it.");
  994. connection.stop();
  995. }
  996. if (!window.EventSource) {
  997. if (onFailed) {
  998. connection.log("This browser doesn't support SSE.");
  999. onFailed();
  1000. }
  1001. return;
  1002. }
  1003. url = transportLogic.getUrl(connection, this.name, reconnecting);
  1004. try {
  1005. connection.log("Attempting to connect to SSE endpoint '" + url + "'");
  1006. connection.eventSource = new window.EventSource(url);
  1007. }
  1008. catch (e) {
  1009. connection.log("EventSource failed trying to connect with error " + e.Message);
  1010. if (onFailed) {
  1011. // The connection failed, call the failed callback
  1012. onFailed();
  1013. }
  1014. else {
  1015. $connection.triggerHandler(events.onError, [e]);
  1016. if (reconnecting) {
  1017. // If we were reconnecting, rather than doing initial connect, then try reconnect again
  1018. that.reconnect(connection);
  1019. }
  1020. }
  1021. return;
  1022. }
  1023. // After connecting, if after the specified timeout there's no response stop the connection
  1024. // and raise on failed
  1025. connectTimeOut = window.setTimeout(function () {
  1026. if (opened === false) {
  1027. connection.log("EventSource timed out trying to connect");
  1028. connection.log("EventSource readyState: " + connection.eventSource.readyState);
  1029. if (!reconnecting) {
  1030. that.stop(connection);
  1031. }
  1032. if (reconnecting) {
  1033. // If we're reconnecting and the event source is attempting to connect,
  1034. // don't keep retrying. This causes duplicate connections to spawn.
  1035. if (connection.eventSource.readyState !== window.EventSource.CONNECTING &&
  1036. connection.eventSource.readyState !== window.EventSource.OPEN) {
  1037. // If we were reconnecting, rather than doing initial connect, then try reconnect again
  1038. that.reconnect(connection);
  1039. }
  1040. } else if (onFailed) {
  1041. onFailed();
  1042. }
  1043. }
  1044. },
  1045. that.timeOut);
  1046. connection.eventSource.addEventListener("open", function (e) {
  1047. connection.log("EventSource connected");
  1048. if (connectTimeOut) {
  1049. window.clearTimeout(connectTimeOut);
  1050. }
  1051. transportLogic.clearReconnectTimeout(connection);
  1052. if (opened === false) {
  1053. opened = true;
  1054. if (onSuccess) {
  1055. onSuccess();
  1056. } else if (changeState(connection,
  1057. signalR.connectionState.reconnecting,
  1058. signalR.connectionState.connected) === true) {
  1059. // If there's no onSuccess handler we assume this is a reconnect
  1060. $connection.triggerHandler(events.onReconnect);
  1061. }
  1062. }
  1063. }, false);
  1064. connection.eventSource.addEventListener("message", function (e) {
  1065. // process messages
  1066. if (e.data === "initialized") {
  1067. return;
  1068. }
  1069. transportLogic.processMessages(connection, window.JSON.parse(e.data));
  1070. }, false);
  1071. connection.eventSource.addEventListener("error", function (e) {
  1072. // Only handle an error if the error is from the current Event Source.
  1073. // Sometimes on disconnect the server will push down an error event
  1074. // to an expired Event Source.
  1075. if (this === connection.eventSource) {
  1076. if (!opened) {
  1077. if (onFailed) {
  1078. onFailed();
  1079. }
  1080. return;
  1081. }
  1082. connection.log("EventSource readyState: " + connection.eventSource.readyState);
  1083. if (e.eventPhase === window.EventSource.CLOSED) {
  1084. // We don't use the EventSource's native reconnect function as it
  1085. // doesn't allow us to change the URL when reconnecting. We need
  1086. // to change the URL to not include the /connect suffix, and pass
  1087. // the last message id we received.
  1088. connection.log("EventSource reconnecting due to the server connection ending");
  1089. that.reconnect(connection);
  1090. } else {
  1091. // connection error
  1092. connection.log("EventSource error");
  1093. $connection.triggerHandler(events.onError);
  1094. }
  1095. }
  1096. }, false);
  1097. },
  1098. reconnect: function (connection) {
  1099. transportLogic.reconnect(connection, this.name);
  1100. },
  1101. lostConnection: function (connection) {
  1102. this.reconnect(connection);
  1103. },
  1104. send: function (connection, data) {
  1105. transportLogic.ajaxSend(connection, data);
  1106. },
  1107. stop: function (connection) {
  1108. // Don't trigger a reconnect after stopping
  1109. transportLogic.clearReconnectTimeout(connection);
  1110. if (connection && connection.eventSource) {
  1111. connection.log("EventSource calling close()");
  1112. connection.eventSource.close();
  1113. connection.eventSource = null;
  1114. delete connection.eventSource;
  1115. }
  1116. },
  1117. abort: function (connection, async) {
  1118. transportLogic.ajaxAbort(connection, async);
  1119. }
  1120. };
  1121. }(window.jQuery, window));
  1122. /* jquery.signalR.transports.foreverFrame.js */
  1123. // Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
  1124. /*global window:false */
  1125. /// <reference path="jquery.signalR.transports.common.js" />
  1126. (function ($, window) {
  1127. "use strict";
  1128. var signalR = $.signalR,
  1129. events = $.signalR.events,
  1130. changeState = $.signalR.changeState,
  1131. transportLogic = signalR.transports._logic,
  1132. // Used to prevent infinite loading icon spins in older versions of ie
  1133. // We build this object inside a closure so we don't pollute the rest of
  1134. // the foreverFrame transport with unnecessary functions/utilities.
  1135. loadPreventer = (function () {
  1136. var loadingFixIntervalId = null,
  1137. loadingFixInterval = 1000,
  1138. attachedTo = 0;
  1139. return {
  1140. prevent: function () {
  1141. // Prevent additional iframe removal procedures from newer browsers
  1142. if (signalR._.ieVersion <= 8) {
  1143. // We only ever want to set the interval one time, so on the first attachedTo
  1144. if (attachedTo === 0) {
  1145. // Create and destroy iframe every 3 seconds to prevent loading icon, super hacky
  1146. loadingFixIntervalId = window.setInterval(function () {
  1147. var tempFrame = $("<iframe style='position:absolute;top:0;left:0;width:0;height:0;visibility:hidden;' src=''></iframe>");
  1148. $("body").append(tempFrame);
  1149. tempFrame.remove();
  1150. tempFrame = null;
  1151. }, loadingFixInterval);
  1152. }
  1153. attachedTo++;
  1154. }
  1155. },
  1156. cancel: function () {
  1157. // Only clear the interval if there's only one more object that the loadPreventer is attachedTo
  1158. if (attachedTo === 1) {
  1159. window.clearInterval(loadingFixIntervalId);
  1160. }
  1161. if (attachedTo > 0) {
  1162. attachedTo--;
  1163. }
  1164. }
  1165. };
  1166. })();
  1167. signalR.transports.foreverFrame = {
  1168. name: "foreverFrame",
  1169. supportsKeepAlive: true,
  1170. timeOut: 3000,
  1171. start: function (connection, onSuccess, onFailed) {
  1172. var that = this,
  1173. frameId = (transportLogic.foreverFrame.count += 1),
  1174. url,
  1175. frame = $("<iframe data-signalr-connection-id='" + connection.id + "' style='position:absolute;top:0;left:0;width:0;height:0;visibility:hidden;' src=''></iframe>");
  1176. if (window.EventSource) {
  1177. // If the browser supports SSE, don't use Forever Frame
  1178. if (onFailed) {
  1179. connection.log("This browser supports SSE, skipping Forever Frame.");
  1180. onFailed();
  1181. }
  1182. return;
  1183. }
  1184. // Start preventing loading icon
  1185. // This will only perform work if the loadPreventer is not attached to another connection.
  1186. loadPreventer.prevent();
  1187. // Build the url
  1188. url = transportLogic.getUrl(connection, this.name);
  1189. url += "&frameId=" + frameId;
  1190. // Set body prior to setting URL to avoid caching issues.
  1191. $("body").append(frame);
  1192. frame.prop("src", url);
  1193. transportLogic.foreverFrame.connections[frameId] = connection;
  1194. connection.log("Binding to iframe's readystatechange event.");
  1195. frame.bind("readystatechange", function () {
  1196. if ($.inArray(this.readyState, ["loaded", "complete"]) >= 0) {
  1197. connection.log("Forever frame iframe readyState changed to " + this.readyState + ", reconnecting");
  1198. that.reconnect(connection);
  1199. }
  1200. });
  1201. connection.frame = frame[0];
  1202. connection.frameId = frameId;
  1203. if (onSuccess) {
  1204. connection.onSuccess = onSuccess;
  1205. }
  1206. // After connecting, if after the specified timeout there's no response stop the connection
  1207. // and raise on failed
  1208. window.setTimeout(function () {
  1209. if (connection.onSuccess) {
  1210. connection.log("Failed to connect using forever frame source, it timed out after " + that.timeOut + "ms.");
  1211. that.stop(connection);
  1212. if (onFailed) {
  1213. onFailed();
  1214. }
  1215. }
  1216. }, that.timeOut);
  1217. },
  1218. reconnect: function (connection) {
  1219. var that = this;
  1220. window.setTimeout(function () {
  1221. if (connection.frame && transportLogic.ensureReconnectingState(connection)) {
  1222. var frame = connection.frame,
  1223. src = transportLogic.getUrl(connection, that.name, true) + "&frameId=" + connection.frameId;
  1224. connection.log("Updating iframe src to '" + src + "'.");
  1225. frame.src = src;
  1226. }
  1227. }, connection.reconnectDelay);
  1228. },
  1229. lostConnection: function (connection) {
  1230. this.reconnect(connection);
  1231. },
  1232. send: function (connection, data) {
  1233. transportLogic.ajaxSend(connection, data);
  1234. },
  1235. receive: function (connection, data) {
  1236. var cw;
  1237. transportLogic.processMessages(connection, data);
  1238. // Delete the script & div elements
  1239. connection.frameMessageCount = (connection.frameMessageCount || 0) + 1;
  1240. if (connection.frameMessageCount > 50) {
  1241. connection.frameMessageCount = 0;
  1242. cw = connection.frame.contentWindow || connection.frame.contentDocument;
  1243. if (cw && cw.document) {
  1244. $("body", cw.document).empty();
  1245. }
  1246. }
  1247. },
  1248. stop: function (connection) {
  1249. var cw = null;
  1250. // Stop attempting to prevent loading icon
  1251. loadPreventer.cancel();
  1252. if (connection.frame) {
  1253. if (connection.frame.stop) {
  1254. connection.frame.stop();
  1255. } else {
  1256. try {
  1257. cw = connection.frame.contentWindow || connection.frame.contentDocument;
  1258. if (cw.document && cw.document.execCommand) {
  1259. cw.document.execCommand("Stop");
  1260. }
  1261. }
  1262. catch (e) {
  1263. connection.log("SignalR: Error occured when stopping foreverFrame transport. Message = " + e.message);
  1264. }
  1265. }
  1266. $(connection.frame).remove();
  1267. delete transportLogic.foreverFrame.connections[connection.frameId];
  1268. connection.frame = null;
  1269. connection.frameId = null;
  1270. delete connection.frame;
  1271. delete connection.frameId;
  1272. connection.log("Stopping forever frame");
  1273. }
  1274. },
  1275. abort: function (connection, async) {
  1276. transportLogic.ajaxAbort(connection, async);
  1277. },
  1278. getConnection: function (id) {
  1279. return transportLogic.foreverFrame.connections[id];
  1280. },
  1281. started: function (connection) {
  1282. if (connection.onSuccess) {
  1283. connection.onSuccess();
  1284. connection.onSuccess = null;
  1285. delete connection.onSuccess;
  1286. } else if (changeState(connection,
  1287. signalR.connectionState.reconnecting,
  1288. signalR.connectionState.connected) === true) {
  1289. // If there's no onSuccess handler we assume this is a reconnect
  1290. $(connection).triggerHandler(events.onReconnect);
  1291. }
  1292. }
  1293. };
  1294. }(window.jQuery, window));
  1295. /* jquery.signalR.transports.longPolling.js */
  1296. // Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
  1297. /*global window:false */
  1298. /// <reference path="jquery.signalR.transports.common.js" />
  1299. (function ($, window) {
  1300. "use strict";
  1301. var signalR = $.signalR,
  1302. events = $.signalR.events,
  1303. changeState = $.signalR.changeState,
  1304. isDisconnecting = $.signalR.isDisconnecting,
  1305. transportLogic = signalR.transports._logic;
  1306. signalR.transports.longPolling = {
  1307. name: "longPolling",
  1308. supportsKeepAlive: false,
  1309. reconnectDelay: 3000,
  1310. init: function (connection, onComplete) {
  1311. /// <summary>Pings the server to ensure availability</summary>
  1312. /// <param name="connection" type="signalr">Connection associated with the server ping</param>
  1313. /// <param name="onComplete" type="Function">Callback to call once initialization has completed</param>
  1314. var that = this,
  1315. pingLoop,
  1316. // pingFail is used to loop the re-ping behavior. When we fail we want to re-try.
  1317. pingFail = function (reason) {
  1318. if (isDisconnecting(connection) === false) {
  1319. connection.log("SignalR: Server ping failed because '" + reason + "', re-trying ping.");
  1320. window.setTimeout(pingLoop, that.reconnectDelay);
  1321. }
  1322. };
  1323. connection.log("SignalR: Initializing long polling connection with server.");
  1324. pingLoop = function () {
  1325. // Ping the server, on successful ping call the onComplete method, otherwise if we fail call the pingFail
  1326. transportLogic.pingServer(connection, that.name).done(onComplete).fail(pingFail);
  1327. };
  1328. pingLoop();
  1329. },
  1330. start: function (connection, onSuccess, onFailed) {
  1331. /// <summary>Starts the long polling connection</summary>
  1332. /// <param name="connection" type="signalR">The SignalR connection to start</param>
  1333. var that = this,
  1334. initialConnectedFired = false,
  1335. fireConnect = function () {
  1336. if (initialConnectedFired) {
  1337. return;
  1338. }
  1339. initialConnectedFired = true;
  1340. onSuccess();
  1341. connection.log("Longpolling connected");
  1342. },
  1343. reconnectErrors = 0,
  1344. reconnectTimeoutId = null,
  1345. fireReconnected = function (instance) {
  1346. window.clearTimeout(reconnectTimeoutId);
  1347. reconnectTimeoutId = null;
  1348. if (changeState(connection,
  1349. signalR.connectionState.reconnecting,
  1350. signalR.connectionState.connected) === true) {
  1351. // Successfully reconnected!
  1352. connection.log("Raising the reconnect event");
  1353. $(instance).triggerHandler(events.onReconnect);
  1354. }
  1355. },
  1356. // 1 hour
  1357. maxFireReconnectedTimeout = 3600000;
  1358. if (connection.pollXhr) {
  1359. connection.log("Polling xhr requests already exists, aborting.");
  1360. connection.stop();
  1361. }
  1362. // We start with an initialization procedure which pings the server to verify that it is there.
  1363. // On scucessful initialization we'll then proceed with starting the transport.
  1364. that.init(connection, function () {
  1365. connection.messageId = null;
  1366. window.setTimeout(function () {
  1367. (function poll(instance, raiseReconnect) {
  1368. var messageId = instance.messageId,
  1369. connect = (messageId === null),
  1370. reconnecting = !connect,
  1371. polling = !raiseReconnect,
  1372. url = transportLogic.getUrl(instance, that.name, reconnecting, polling);
  1373. // If we've disconnected during the time we've tried to re-instantiate the poll then stop.
  1374. if (isDisconnecting(instance) === true) {
  1375. return;
  1376. }
  1377. connection.log("Attempting to connect to '" + url + "' using longPolling.");
  1378. instance.pollXhr = $.ajax({
  1379. url: url,
  1380. global: false,
  1381. cache: false,
  1382. type: "GET",
  1383. dataType: connection.ajaxDataType,
  1384. contentType: connection.contentType,
  1385. success: function (minData) {
  1386. var delay = 0,
  1387. data;
  1388. // Reset our reconnect errors so if we transition into a reconnecting state again we trigger
  1389. // reconnected quickly
  1390. reconnectErrors = 0;
  1391. // If there's currently a timeout to trigger reconnect, fire it now before processing messages
  1392. if (reconnectTimeoutId !== null) {
  1393. fireReconnected();
  1394. }
  1395. fireConnect();
  1396. if (minData) {
  1397. data = transportLogic.maximizePersistentResponse(minData);
  1398. }
  1399. transportLogic.processMessages(instance, minData);
  1400. if (data &&
  1401. $.type(data.LongPollDelay) === "number") {
  1402. delay = data.LongPollDelay;
  1403. }
  1404. if (data && data.Disconnect) {
  1405. return;
  1406. }
  1407. if (isDisconnecting(instance) === true) {
  1408. return;
  1409. }
  1410. // We never want to pass a raiseReconnect flag after a successful poll. This is handled via the error function
  1411. if (delay > 0) {
  1412. window.setTimeout(function () {
  1413. poll(instance, false);
  1414. }, delay);
  1415. } else {
  1416. poll(instance, false);
  1417. }
  1418. },
  1419. error: function (data, textStatus) {
  1420. // Stop trying to trigger reconnect, connection is in an error state
  1421. // If we're not in the reconnect state this will noop
  1422. window.clearTimeout(reconnectTimeoutId);
  1423. reconnectTimeoutId = null;
  1424. if (textStatus === "abort") {
  1425. connection.log("Aborted xhr requst.");
  1426. return;
  1427. }
  1428. // Increment our reconnect errors, we assume all errors to be reconnect errors
  1429. // In the case that it's our first error this will cause Reconnect to be fired
  1430. // after 1 second due to reconnectErrors being = 1.
  1431. reconnectErrors++;
  1432. if (connection.state !== signalR.connectionState.reconnecting) {
  1433. connection.log("An error occurred using longPolling. Status = " + textStatus + ". " + data.responseText);
  1434. $(instance).triggerHandler(events.onError, [data.responseText]);
  1435. }
  1436. // Transition into the reconnecting state
  1437. transportLogic.ensureReconnectingState(instance);
  1438. // If we've errored out we need to verify that the server is still there, so re-start initialization process
  1439. // This will ping the server until it successfully gets a response.
  1440. that.init(instance, function () {
  1441. // Call poll with the raiseReconnect flag as true
  1442. poll(instance, true);
  1443. });
  1444. }
  1445. });
  1446. // This will only ever pass after an error has occured via the poll ajax procedure.
  1447. if (reconnecting && raiseReconnect === true) {
  1448. // We wait to reconnect depending on how many times we've failed to reconnect.
  1449. // This is essentially a heuristic that will exponentially increase in wait time before
  1450. // triggering reconnected. This depends on the "error" handler of Poll to cancel this
  1451. // timeout if it triggers before the Reconnected event fires.
  1452. // The Math.min at the end is to ensure that the reconnect timeout does not overflow.
  1453. reconnectTimeoutId = window.setTimeout(function () { fireReconnected(instance); }, Math.min(1000 * (Math.pow(2, reconnectErrors) - 1), maxFireReconnectedTimeout));
  1454. }
  1455. }(connection));
  1456. // Set an arbitrary timeout to trigger onSuccess, this will alot for enough time on the server to wire up the connection.
  1457. // Will be fixed by #1189 and this code can be modified to not be a timeout
  1458. window.setTimeout(function () {
  1459. // Trigger the onSuccess() method because we've now instantiated a connection
  1460. fireConnect();
  1461. }, 250);
  1462. }, 250); // Have to delay initial poll so Chrome doesn't show loader spinner in tab
  1463. });
  1464. },
  1465. lostConnection: function (connection) {
  1466. throw new Error("Lost Connection not handled for LongPolling");
  1467. },
  1468. send: function (connection, data) {
  1469. transportLogic.ajaxSend(connection, data);
  1470. },
  1471. stop: function (connection) {
  1472. /// <summary>Stops the long polling connection</summary>
  1473. /// <param name="connection" type="signalR">The SignalR connection to stop</param>
  1474. if (connection.pollXhr) {
  1475. connection.pollXhr.abort();
  1476. connection.pollXhr = null;
  1477. delete connection.pollXhr;
  1478. }
  1479. },
  1480. abort: function (connection, async) {
  1481. transportLogic.ajaxAbort(connection, async);
  1482. }
  1483. };
  1484. }(window.jQuery, window));
  1485. /* jquery.signalR.hubs.js */
  1486. // Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
  1487. /*global window:false */
  1488. /// <reference path="jquery.signalR.core.js" />
  1489. (function ($, window) {
  1490. "use strict";
  1491. // we use a global id for tracking callbacks so the server doesn't have to send extra info like hub name
  1492. var eventNamespace = ".hubProxy";
  1493. function makeEventName(event) {
  1494. return event + eventNamespace;
  1495. }
  1496. // Equivalent to Array.prototype.map
  1497. function map(arr, fun, thisp) {
  1498. var i,
  1499. length = arr.length,
  1500. result = [];
  1501. for (i = 0; i < length; i += 1) {
  1502. if (arr.hasOwnProperty(i)) {
  1503. result[i] = fun.call(thisp, arr[i], i, arr);
  1504. }
  1505. }
  1506. return result;
  1507. }
  1508. function getArgValue(a) {
  1509. return $.isFunction(a) ? null : ($.type(a) === "undefined" ? null : a);
  1510. }
  1511. function hasMembers(obj) {
  1512. for (var key in obj) {
  1513. // If we have any properties in our callback map then we have callbacks and can exit the loop via return
  1514. if (obj.hasOwnProperty(key)) {
  1515. return true;
  1516. }
  1517. }
  1518. return false;
  1519. }
  1520. function clearInvocationCallbacks(connection, error) {
  1521. /// <param name="connection" type="hubConnection" />
  1522. var callbacks = connection._.invocationCallbacks,
  1523. callback;
  1524. connection.log("Clearing hub invocation callbacks with error: " + error);
  1525. // Reset the callback cache now as we have a local var referencing it
  1526. connection._.invocationCallbackId = 0;
  1527. delete connection._.invocationCallbacks;
  1528. connection._.invocationCallbacks = {};
  1529. // Loop over the callbacks and invoke them.
  1530. // We do this using a local var reference and *after* we've cleared the cache
  1531. // so that if a fail callback itself tries to invoke another method we don't
  1532. // end up with its callback in the list we're looping over.
  1533. for (var callbackId in callbacks) {
  1534. callback = callbacks[callbackId];
  1535. callback.method.call(callback.scope, { E: error });
  1536. }
  1537. }
  1538. // hubProxy
  1539. function hubProxy(hubConnection, hubName) {
  1540. /// <summary>
  1541. /// Creates a new proxy object for the given hub connection that can be used to invoke
  1542. /// methods on server hubs and handle client method invocation requests from the server.
  1543. /// </summary>
  1544. return new hubProxy.fn.init(hubConnection, hubName);
  1545. }
  1546. hubProxy.fn = hubProxy.prototype = {
  1547. init: function (connection, hubName) {
  1548. this.state = {};
  1549. this.connection = connection;
  1550. this.hubName = hubName;
  1551. this._ = {
  1552. callbackMap: {}
  1553. };
  1554. },
  1555. hasSubscriptions: function () {
  1556. return hasMembers(this._.callbackMap);
  1557. },
  1558. on: function (eventName, callback) {
  1559. /// <summary>Wires up a callback to be invoked when a invocation request is received from the server hub.</summary>
  1560. /// <param name="eventName" type="String">The name of the hub event to register the callback for.</param>
  1561. /// <param name="callback" type="Function">The callback to be invoked.</param>
  1562. var self = this,
  1563. callbackMap = self._.callbackMap;
  1564. // Normalize the event name to lowercase
  1565. eventName = eventName.toLowerCase();
  1566. // If there is not an event registered for this callback yet we want to create its event space in the callback map.
  1567. if (!callbackMap[eventName]) {
  1568. callbackMap[eventName] = {};
  1569. }
  1570. // Map the callback to our encompassed function
  1571. callbackMap[eventName][callback] = function (e, data) {
  1572. callback.apply(self, data);
  1573. };
  1574. $(self).bind(makeEventName(eventName), callbackMap[eventName][callback]);
  1575. return self;
  1576. },
  1577. off: function (eventName, callback) {
  1578. /// <summary>Removes the callback invocation request from the server hub for the given event name.</summary>
  1579. /// <param name="eventName" type="String">The name of the hub event to unregister the callback for.</param>
  1580. /// <param name="callback" type="Function">The callback to be invoked.</param>
  1581. var self = this,
  1582. callbackMap = self._.callbackMap,
  1583. callbackSpace;
  1584. // Normalize the event name to lowercase
  1585. eventName = eventName.toLowerCase();
  1586. callbackSpace = callbackMap[eventName];
  1587. // Verify that there is an event space to unbind
  1588. if (callbackSpace) {
  1589. // Only unbind if there's an event bound with eventName and a callback with the specified callback
  1590. if (callbackSpace[callback]) {
  1591. $(self).unbind(makeEventName(eventName), callbackSpace[callback]);
  1592. // Remove the callback from the callback map
  1593. delete callbackSpace[callback];
  1594. // Check if there are any members left on the event, if not we need to destroy it.
  1595. if (!hasMembers(callbackSpace)) {
  1596. delete callbackMap[eventName];
  1597. }
  1598. }
  1599. else if (!callback) { // Check if we're removing the whole event and we didn't error because of an invalid callback
  1600. $(self).unbind(makeEventName(eventName));
  1601. delete callbackMap[eventName];
  1602. }
  1603. }
  1604. return self;
  1605. },
  1606. invoke: function (methodName) {
  1607. /// <summary>Invokes a server hub method with the given arguments.</summary>
  1608. /// <param name="methodName" type="String">The name of the server hub method.</param>
  1609. var self = this,
  1610. connection = self.connection,
  1611. args = $.makeArray(arguments).slice(1),
  1612. argValues = map(args, getArgValue),
  1613. data = { H: self.hubName, M: methodName, A: argValues, I: connection._.invocationCallbackId },
  1614. d = $.Deferred(),
  1615. callback = function (minResult) {
  1616. var result = self._maximizeHubResponse(minResult);
  1617. // Update the hub state
  1618. $.extend(self.state, result.State);
  1619. if (result.Error) {
  1620. // Server hub method threw an exception, log it & reject the deferred
  1621. if (result.StackTrace) {
  1622. connection.log(result.Error + "\n" + result.StackTrace);
  1623. }
  1624. d.rejectWith(self, [result.Error]);
  1625. } else {
  1626. // Server invocation succeeded, resolve the deferred
  1627. d.resolveWith(self, [result.Result]);
  1628. }
  1629. };
  1630. connection._.invocationCallbacks[connection._.invocationCallbackId.toString()] = { scope: self, method: callback };
  1631. connection._.invocationCallbackId += 1;
  1632. if (!$.isEmptyObject(self.state)) {
  1633. data.S = self.state;
  1634. }
  1635. connection.send(window.JSON.stringify(data));
  1636. return d.promise();
  1637. },
  1638. _maximizeHubResponse: function (minHubResponse) {
  1639. return {
  1640. State: minHubResponse.S,
  1641. Result: minHubResponse.R,
  1642. Id: minHubResponse.I,
  1643. Error: minHubResponse.E,
  1644. StackTrace: minHubResponse.T
  1645. };
  1646. }
  1647. };
  1648. hubProxy.fn.init.prototype = hubProxy.fn;
  1649. // hubConnection
  1650. function hubConnection(url, options) {
  1651. /// <summary>Creates a new hub connection.</summary>
  1652. /// <param name="url" type="String">[Optional] The hub route url, defaults to "/signalr".</param>
  1653. /// <param name="options" type="Object">[Optional] Settings to use when creating the hubConnection.</param>
  1654. var settings = {
  1655. qs: null,
  1656. logging: false,
  1657. useDefaultPath: true
  1658. };
  1659. $.extend(settings, options);
  1660. if (!url || settings.useDefaultPath) {
  1661. url = (url || "") + "/signalr";
  1662. }
  1663. return new hubConnection.fn.init(url, settings);
  1664. }
  1665. hubConnection.fn = hubConnection.prototype = $.connection();
  1666. hubConnection.fn.init = function (url, options) {
  1667. var settings = {
  1668. qs: null,
  1669. logging: false,
  1670. useDefaultPath: true
  1671. },
  1672. connection = this;
  1673. $.extend(settings, options);
  1674. // Call the base constructor
  1675. $.signalR.fn.init.call(connection, url, settings.qs, settings.logging);
  1676. // Object to store hub proxies for this connection
  1677. connection.proxies = {};
  1678. connection._.invocationCallbackId = 0;
  1679. connection._.invocationCallbacks = {};
  1680. // Wire up the received handler
  1681. connection.received(function (minData) {
  1682. var data, proxy, dataCallbackId, callback, hubName, eventName;
  1683. if (!minData) {
  1684. return;
  1685. }
  1686. if (typeof (minData.I) !== "undefined") {
  1687. // We received the return value from a server method invocation, look up callback by id and call it
  1688. dataCallbackId = minData.I.toString();
  1689. callback = connection._.invocationCallbacks[dataCallbackId];
  1690. if (callback) {
  1691. // Delete the callback from the proxy
  1692. connection._.invocationCallbacks[dataCallbackId] = null;
  1693. delete connection._.invocationCallbacks[dataCallbackId];
  1694. // Invoke the callback
  1695. callback.method.call(callback.scope, minData);
  1696. }
  1697. } else {
  1698. data = this._maximizeClientHubInvocation(minData);
  1699. // We received a client invocation request, i.e. broadcast from server hub
  1700. connection.log("Triggering client hub event '" + data.Method + "' on hub '" + data.Hub + "'.");
  1701. // Normalize the names to lowercase
  1702. hubName = data.Hub.toLowerCase();
  1703. eventName = data.Method.toLowerCase();
  1704. // Trigger the local invocation event
  1705. proxy = this.proxies[hubName];
  1706. // Update the hub state
  1707. $.extend(proxy.state, data.State);
  1708. $(proxy).triggerHandler(makeEventName(eventName), [data.Args]);
  1709. }
  1710. });
  1711. connection.error(function (errData, origData) {
  1712. var data, callbackId, callback;
  1713. if (connection.transport && connection.transport.name === "webSockets") {
  1714. // WebSockets connections have all callbacks removed on reconnect instead
  1715. // as WebSockets sends are fire & forget
  1716. return;
  1717. }
  1718. if (!origData) {
  1719. // No original data passed so this is not a send error
  1720. return;
  1721. }
  1722. try {
  1723. data = window.JSON.parse(origData);
  1724. if (!data.I) {
  1725. // The original data doesn't have a callback ID so not a send error
  1726. return;
  1727. }
  1728. } catch (e) {
  1729. // The original data is not a JSON payload so this is not a send error
  1730. return;
  1731. }
  1732. callbackId = data.I;
  1733. callback = connection._.invocationCallbacks[callbackId];
  1734. // Invoke the callback with an error to reject the promise
  1735. callback.method.call(callback.scope, { E: errData });
  1736. // Delete the callback
  1737. connection._.invocationCallbacks[callbackId] = null;
  1738. delete connection._.invocationCallbacks[callbackId];
  1739. });
  1740. connection.reconnecting(function () {
  1741. if (connection.transport && connection.transport.name === "webSockets") {
  1742. clearInvocationCallbacks(connection, "Connection started reconnecting before invocation result was received.");
  1743. }
  1744. });
  1745. connection.disconnected(function () {
  1746. clearInvocationCallbacks(connection, "Connection was disconnected before invocation result was received.");
  1747. });
  1748. };
  1749. hubConnection.fn._maximizeClientHubInvocation = function (minClientHubInvocation) {
  1750. return {
  1751. Hub: minClientHubInvocation.H,
  1752. Method: minClientHubInvocation.M,
  1753. Args: minClientHubInvocation.A,
  1754. State: minClientHubInvocation.S
  1755. };
  1756. };
  1757. hubConnection.fn._registerSubscribedHubs = function () {
  1758. /// <summary>
  1759. /// Sets the starting event to loop through the known hubs and register any new hubs
  1760. /// that have been added to the proxy.
  1761. /// </summary>
  1762. if (!this._subscribedToHubs) {
  1763. this._subscribedToHubs = true;
  1764. this.starting(function () {
  1765. // Set the connection's data object with all the hub proxies with active subscriptions.
  1766. // These proxies will receive notifications from the server.
  1767. var subscribedHubs = [];
  1768. $.each(this.proxies, function (key) {
  1769. if (this.hasSubscriptions()) {
  1770. subscribedHubs.push({ name: key });
  1771. }
  1772. });
  1773. this.data = window.JSON.stringify(subscribedHubs);
  1774. });
  1775. }
  1776. };
  1777. hubConnection.fn.createHubProxy = function (hubName) {
  1778. /// <summary>
  1779. /// Creates a new proxy object for the given hub connection that can be used to invoke
  1780. /// methods on server hubs and handle client method invocation requests from the server.
  1781. /// </summary>
  1782. /// <param name="hubName" type="String">
  1783. /// The name of the hub on the server to create the proxy for.
  1784. /// </param>
  1785. // Normalize the name to lowercase
  1786. hubName = hubName.toLowerCase();
  1787. var proxy = this.proxies[hubName];
  1788. if (!proxy) {
  1789. proxy = hubProxy(this, hubName);
  1790. this.proxies[hubName] = proxy;
  1791. }
  1792. this._registerSubscribedHubs();
  1793. return proxy;
  1794. };
  1795. hubConnection.fn.init.prototype = hubConnection.fn;
  1796. $.hubConnection = hubConnection;
  1797. }(window.jQuery, window));
  1798. /* jquery.signalR.version.js */
  1799. // Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
  1800. /*global window:false */
  1801. /// <reference path="jquery.signalR.core.js" />
  1802. (function ($) {
  1803. $.signalR.version = "1.1.3";
  1804. }(window.jQuery));