123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193 |
- /* jquery.signalR.core.js */
- /*global window:false */
- /*!
- * ASP.NET SignalR JavaScript Library v1.1.3
- * http://signalr.net/
- *
- * Copyright Microsoft Open Technologies, Inc. All rights reserved.
- * Licensed under the Apache 2.0
- * https://github.com/SignalR/SignalR/blob/master/LICENSE.md
- *
- */
- /// <reference path="Scripts/jquery-1.6.4.js" />
- (function ($, window) {
- "use strict";
- if (typeof ($) !== "function") {
- // no jQuery!
- throw new Error("SignalR: jQuery not found. Please ensure jQuery is referenced before the SignalR.js file.");
- }
- if (!window.JSON) {
- // no JSON!
- throw new Error("SignalR: No JSON parser found. Please ensure json2.js is referenced before the SignalR.js file if you need to support clients without native JSON parsing support, e.g. IE<8.");
- }
- var signalR,
- _connection,
- _pageLoaded = (window.document.readyState === "complete"),
- _pageWindow = $(window),
- events = {
- onStart: "onStart",
- onStarting: "onStarting",
- onReceived: "onReceived",
- onError: "onError",
- onConnectionSlow: "onConnectionSlow",
- onReconnecting: "onReconnecting",
- onReconnect: "onReconnect",
- onStateChanged: "onStateChanged",
- 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);
- }
- },
- changeState = function (connection, expectedState, newState) {
- if (expectedState === connection.state) {
- connection.state = newState;
- $(connection).triggerHandler(events.onStateChanged, [{ oldState: expectedState, newState: newState }]);
- return true;
- }
- return false;
- },
- isDisconnecting = function (connection) {
- return connection.state === signalR.connectionState.disconnected;
- },
- configureStopReconnectingTimeout = function (connection) {
- var stopReconnectingTimeout,
- onReconnectTimeout;
- // Check if this connection has already been configured to stop reconnecting after a specified timeout.
- // Without this check if a connection is stopped then started events will be bound multiple times.
- if (!connection._.configuredStopReconnectingTimeout) {
- onReconnectTimeout = function (connection) {
- connection.log("Couldn't reconnect within the configured timeout (" + connection.disconnectTimeout + "ms), disconnecting.");
- connection.stop(/* async */ false, /* notifyServer */ false);
- };
- connection.reconnecting(function () {
- var connection = this;
- // Guard against state changing in a previous user defined even handler
- if (connection.state === signalR.connectionState.reconnecting) {
- stopReconnectingTimeout = window.setTimeout(function () { onReconnectTimeout(connection); }, connection.disconnectTimeout);
- }
- });
- connection.stateChanged(function (data) {
- if (data.oldState === signalR.connectionState.reconnecting) {
- // Clear the pending reconnect timeout check
- window.clearTimeout(stopReconnectingTimeout);
- }
- });
- connection._.configuredStopReconnectingTimeout = true;
- }
- };
- 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>
- return new signalR.fn.init(url, qs, logging);
- };
- signalR._ = {
- defaultContentType: "application/x-www-form-urlencoded; charset=UTF-8",
- ieVersion: (function () {
- var version,
- matches;
- if (window.navigator.appName === 'Microsoft Internet Explorer') {
- // Check if the user agent has the pattern "MSIE (one or more numbers).(one or more numbers)";
- matches = /MSIE ([0-9]+\.[0-9]+)/.exec(window.navigator.userAgent);
- if (matches) {
- version = window.parseFloat(matches[1]);
- }
- }
- // undefined value means not IE
- return version;
- })()
- };
- signalR.events = events;
- signalR.changeState = changeState;
- signalR.isDisconnecting = isDisconnecting;
- signalR.connectionState = {
- connecting: 0,
- connected: 1,
- reconnecting: 2,
- disconnected: 4
- };
- signalR.hub = {
- start: function () {
- // This will get replaced with the real hub connection start method when hubs is referenced correctly
- throw new Error("SignalR: Error loading hubs. Ensure your hubs reference is correct, e.g. <script src='/signalr/hubs'></script>.");
- }
- };
- _pageWindow.load(function () { _pageLoaded = true; });
- function validateTransport(requestedTransport, connection) {
- /// <summary>Validates the requested transport by cross checking it with the pre-defined signalR.transports</summary>
- /// <param name="requestedTransport" type="Object">The designated transports that the user has specified.</param>
- /// <param name="connection" type="signalR">The connection that will be using the requested transports. Used for logging purposes.</param>
- /// <returns type="Object" />
- if ($.isArray(requestedTransport)) {
- // Go through transport array and remove an "invalid" tranports
- for (var i = requestedTransport.length - 1; i >= 0; i--) {
- var transport = requestedTransport[i];
- if ($.type(requestedTransport) !== "object" && ($.type(transport) !== "string" || !signalR.transports[transport])) {
- connection.log("Invalid transport: " + transport + ", removing it from the transports list.");
- requestedTransport.splice(i, 1);
- }
- }
- // Verify we still have transports left, if we dont then we have invalid transports
- if (requestedTransport.length === 0) {
- connection.log("No transports remain within the specified transport array.");
- requestedTransport = null;
- }
- } else if ($.type(requestedTransport) !== "object" && !signalR.transports[requestedTransport] && requestedTransport !== "auto") {
- connection.log("Invalid transport: " + requestedTransport.toString());
- requestedTransport = null;
- }
- else if (requestedTransport === "auto" && signalR._.ieVersion <= 8)
- {
- // If we're doing an auto transport and we're IE8 then force longPolling, #1764
- return ["longPolling"];
- }
- return requestedTransport;
- }
- function getDefaultPort(protocol) {
- if(protocol === "http:") {
- return 80;
- }
- else if (protocol === "https:") {
- return 443;
- }
- }
- function addDefaultPort(protocol, url) {
- // Remove ports from url. We have to check if there's a / or end of line
- // following the port in order to avoid removing ports such as 8080.
- if(url.match(/:\d+$/)) {
- return url;
- } else {
- return url + ":" + getDefaultPort(protocol);
- }
- }
- signalR.fn = signalR.prototype = {
- init: function (url, qs, logging) {
- this.url = url;
- this.qs = qs;
- this._ = {};
- if (typeof (logging) === "boolean") {
- this.logging = logging;
- }
- },
- isCrossDomain: function (url, against) {
- /// <summary>Checks if url is cross domain</summary>
- /// <param name="url" type="String">The base URL</param>
- /// <param name="against" type="Object">
- /// An optional argument to compare the URL against, if not specified it will be set to window.location.
- /// If specified it must contain a protocol and a host property.
- /// </param>
- var link;
- url = $.trim(url);
- if (url.indexOf("http") !== 0) {
- return false;
- }
- against = against || window.location;
- // Create an anchor tag.
- link = window.document.createElement("a");
- link.href = url;
- // When checking for cross domain we have to special case port 80 because the window.location will remove the
- return link.protocol + addDefaultPort(link.protocol, link.host) !== against.protocol + addDefaultPort(against.protocol, against.host);
- },
- ajaxDataType: "json",
- contentType: "application/json; charset=UTF-8",
- logging: false,
- state: signalR.connectionState.disconnected,
- keepAliveData: {},
- reconnectDelay: 2000,
- disconnectTimeout: 30000, // This should be set by the server in response to the negotiate request (30s default)
- keepAliveWarnAt: 2 / 3, // Warn user of slow connection if we breach the X% mark of the keep alive timeout
- 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 = {
- waitForPageLoad: true,
- transport: "auto",
- jsonp: false
- },
- initialize,
- deferred = connection._deferral || $.Deferred(), // Check to see if there is a pre-existing deferral that's being built on, if so we want to keep using it
- parser = window.document.createElement("a");
- 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;
- }
- }
- config.transport = validateTransport(config.transport, connection);
- // If the transport is invalid throw an error and abort start
- if (!config.transport) {
- throw new Error("SignalR: Invalid transport(s) specified, aborting start.");
- }
- // Check to see if start is being called prior to page load
- // If waitForPageLoad is true we then want to re-direct function call to the window load event
- if (!_pageLoaded && config.waitForPageLoad === true) {
- _pageWindow.load(function () {
- connection._deferral = deferred;
- connection.start(options, callback);
- });
- return deferred.promise();
- }
- configureStopReconnectingTimeout(connection);
- if (changeState(connection,
- signalR.connectionState.disconnected,
- signalR.connectionState.connecting) === false) {
- // Already started, just return
- deferred.resolve(connection);
- return deferred.promise();
- }
- // Resolve the full url
- parser.href = connection.url;
- if (!parser.protocol || parser.protocol === ":") {
- connection.protocol = window.document.location.protocol;
- connection.host = window.document.location.host;
- connection.baseUrl = connection.protocol + "//" + connection.host;
- }
- else {
- connection.protocol = parser.protocol;
- connection.host = parser.host;
- connection.baseUrl = parser.protocol + "//" + parser.host;
- }
- // Set the websocket protocol
- connection.wsProtocol = connection.protocol === "https:" ? "wss://" : "ws://";
- // If jsonp with no/auto transport is specified, then set the transport to long polling
- // since that is the only transport for which jsonp really makes sense.
- // Some developers might actually choose to specify jsonp for same origin requests
- // as demonstrated by Issue #623.
- if (config.transport === "auto" && config.jsonp === true) {
- config.transport = "longPolling";
- }
- if (this.isCrossDomain(connection.url)) {
- connection.log("Auto detected cross domain url.");
- if (config.transport === "auto") {
- // Try webSockets and longPolling since SSE doesn't support CORS
- // TODO: Support XDM with foreverFrame
- config.transport = ["webSockets", "longPolling"];
- }
- // Determine if jsonp is the only choice for negotiation, ajaxSend and ajaxAbort.
- // i.e. if the browser doesn't supports CORS
- // If it is, ignore any preference to the contrary, and switch to jsonp.
- if (!config.jsonp) {
- config.jsonp = !$.support.cors;
- if (config.jsonp) {
- connection.log("Using jsonp because this browser doesn't support CORS");
- }
- }
- connection.contentType = signalR._.defaultContentType;
- }
- connection.ajaxDataType = config.jsonp ? "jsonp" : "json";
- $(connection).bind(events.onStart, function (e, data) {
- if ($.type(callback) === "function") {
- callback.call(connection);
- }
- deferred.resolve(connection);
- });
- initialize = function (transports, index) {
- index = index || 0;
- if (index >= transports.length) {
- if (!connection.transport) {
- // No transport initialized successfully
- $(connection).triggerHandler(events.onError, ["SignalR: No transport could be initialized successfully. Try specifying a different transport or none at all for auto initialization."]);
- deferred.reject("SignalR: No transport could be initialized successfully. Try specifying a different transport or none at all for auto initialization.");
- // Stop the connection if it has connected and move it into the disconnected state
- connection.stop();
- }
- return;
- }
- var transportName = transports[index],
- transport = $.type(transportName) === "object" ? transportName : signalR.transports[transportName];
- if (transportName.indexOf("_") === 0) {
- // Private member
- initialize(transports, index + 1);
- return;
- }
- transport.start(connection, function () { // success
- if (transport.supportsKeepAlive && connection.keepAliveData.activated) {
- signalR.transports._logic.monitorKeepAlive(connection);
- }
- connection.transport = transport;
- changeState(connection,
- signalR.connectionState.connecting,
- signalR.connectionState.connected);
- $(connection).triggerHandler(events.onStart);
- _pageWindow.unload(function () { // failure
- connection.stop(false /* async */);
- });
- }, function () {
- initialize(transports, index + 1);
- });
- };
- var url = connection.url + "/negotiate";
- url = signalR.transports._logic.addQs(url, connection);
- connection.log("Negotiating with '" + url + "'.");
- $.ajax({
- url: url,
- global: false,
- cache: false,
- type: "GET",
- contentType: connection.contentType,
- data: {},
- dataType: connection.ajaxDataType,
- error: function (error) {
- $(connection).triggerHandler(events.onError, [error.responseText]);
- deferred.reject("SignalR: Error during negotiation request: " + error.responseText);
- // Stop the connection if negotiate failed
- connection.stop();
- },
- success: function (res) {
- var keepAliveData = connection.keepAliveData;
- connection.appRelativeUrl = res.Url;
- connection.id = res.ConnectionId;
- connection.token = res.ConnectionToken;
- connection.webSocketServerUrl = res.WebSocketServerUrl;
- // Once the server has labeled the PersistentConnection as Disconnected, we should stop attempting to reconnect
- // after res.DisconnectTimeout seconds.
- connection.disconnectTimeout = res.DisconnectTimeout * 1000; // in ms
-
- // If we have a keep alive
- if (res.KeepAliveTimeout) {
- // Register the keep alive data as activated
- keepAliveData.activated = true;
- // Timeout to designate when to force the connection into reconnecting converted to milliseconds
- keepAliveData.timeout = res.KeepAliveTimeout * 1000;
- // Timeout to designate when to warn the developer that the connection may be dead or is not responding.
- keepAliveData.timeoutWarning = keepAliveData.timeout * connection.keepAliveWarnAt;
- // Instantiate the frequency in which we check the keep alive. It must be short in order to not miss/pick up any changes
- keepAliveData.checkInterval = (keepAliveData.timeout - keepAliveData.timeoutWarning) / 3;
- }
- else {
- keepAliveData.activated = false;
- }
- if (!res.ProtocolVersion || res.ProtocolVersion !== "1.2") {
- $(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 + "."]);
- 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 + ".");
- return;
- }
- $(connection).triggerHandler(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);
- }
- });
- return deferred.promise();
- },
- starting: 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.onStarting, function (e, data) {
- callback.call(connection);
- });
- 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.state === signalR.connectionState.disconnected) {
- // Connection hasn't been started yet
- throw new Error("SignalR: Connection must be started before data can be sent. Call .start() before .send()");
- }
- if (connection.state === signalR.connectionState.connecting) {
- // Connection hasn't been started yet
- throw new Error("SignalR: Connection has not been fully initialized. Use .start().done() or .start().fail() to run logic after the connection has started.");
- }
- connection.transport.send(connection, data);
- // REVIEW: Should we return deferred here?
- 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;
- },
- stateChanged: function (callback) {
- /// <summary>Adds a callback that will be invoked when the connection state changes</summary>
- /// <param name="callback" type="Function">A callback function to execute when the connection state changes</param>
- /// <returns type="signalR" />
- var connection = this;
- $(connection).bind(events.onStateChanged, 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;
- },
- connectionSlow: function (callback) {
- /// <summary>Adds a callback that will be invoked when the client detects a slow connection</summary>
- /// <param name="callback" type="Function">A callback function to execute when the connection is slow</param>
- /// <returns type="signalR" />
- var connection = this;
- $(connection).bind(events.onConnectionSlow, function(e, data) {
- callback.call(connection);
- });
- return connection;
- },
- reconnecting: function (callback) {
- /// <summary>Adds a callback that will be invoked when the underlying transport begins reconnecting</summary>
- /// <param name="callback" type="Function">A callback function to execute when the connection enters a reconnecting state</param>
- /// <returns type="signalR" />
- var connection = this;
- $(connection).bind(events.onReconnecting, 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 (async, notifyServer) {
- /// <summary>Stops listening</summary>
- /// <param name="async" type="Boolean">Whether or not to asynchronously abort the connection</param>
- /// <param name="notifyServer" type="Boolean">Whether we want to notify the server that we are aborting the connection</param>
- /// <returns type="signalR" />
- var connection = this;
- if (connection.state === signalR.connectionState.disconnected) {
- return;
- }
- try {
- if (connection.transport) {
- if (notifyServer !== false) {
- connection.transport.abort(connection, async);
- }
- if (connection.transport.supportsKeepAlive && connection.keepAliveData.activated) {
- signalR.transports._logic.stopMonitoringKeepAlive(connection);
- }
- connection.transport.stop(connection);
- connection.transport = null;
- }
- // Trigger the disconnect event
- $(connection).triggerHandler(events.onDisconnect);
- delete connection.messageId;
- delete connection.groupsToken;
- // 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.
- delete connection.id;
- delete connection._deferral;
- }
- finally {
- changeState(connection, connection.state, signalR.connectionState.disconnected);
- }
- return connection;
- },
- log: function (msg) {
- log(msg, this.logging);
- }
- };
- signalR.fn.init.prototype = signalR.fn;
- 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));
- /* jquery.signalR.transports.common.js */
- // Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
- /*global window:false */
- /// <reference path="jquery.signalR.core.js" />
- (function ($, window) {
- "use strict";
- var signalR = $.signalR,
- events = $.signalR.events,
- changeState = $.signalR.changeState;
- signalR.transports = {};
- function checkIfAlive(connection) {
- var keepAliveData = connection.keepAliveData,
- diff,
- timeElapsed;
- // Only check if we're connected
- if (connection.state === signalR.connectionState.connected) {
- diff = new Date();
- diff.setTime(diff - keepAliveData.lastKeepAlive);
- timeElapsed = diff.getTime();
- // Check if the keep alive has completely timed out
- if (timeElapsed >= keepAliveData.timeout) {
- connection.log("Keep alive timed out. Notifying transport that connection has been lost.");
- // Notify transport that the connection has been lost
- connection.transport.lostConnection(connection);
- }
- else if (timeElapsed >= keepAliveData.timeoutWarning) {
- // This is to assure that the user only gets a single warning
- if (!keepAliveData.userNotified) {
- connection.log("Keep alive has been missed, connection may be dead/slow.");
- $(connection).triggerHandler(events.onConnectionSlow);
- keepAliveData.userNotified = true;
- }
- }
- else {
- keepAliveData.userNotified = false;
- }
- }
- // Verify we're monitoring the keep alive
- // We don't want this as a part of the inner if statement above because we want keep alives to continue to be checked
- // in the event that the server comes back online (if it goes offline).
- if (keepAliveData.monitoring) {
- window.setTimeout(function () {
- checkIfAlive(connection);
- }, keepAliveData.checkInterval);
- }
- }
- function isConnectedOrReconnecting(connection) {
- return connection.state === signalR.connectionState.connected ||
- connection.state === signalR.connectionState.reconnecting;
- }
- signalR.transports._logic = {
- pingServer: function (connection, transport) {
- /// <summary>Pings the server</summary>
- /// <param name="connection" type="signalr">Connection associated with the server ping</param>
- /// <returns type="signalR" />
- var baseUrl = transport === "webSockets" ? "" : connection.baseUrl,
- url = baseUrl + connection.appRelativeUrl + "/ping",
- deferral = $.Deferred();
- url = this.addQs(url, connection);
- $.ajax({
- url: url,
- global: false,
- cache: false,
- type: "GET",
- contentType: connection.contentType,
- data: {},
- dataType: connection.ajaxDataType,
- success: function (data) {
- if (data.Response === "pong") {
- deferral.resolve();
- }
- else {
- deferral.reject("SignalR: Invalid ping response when pinging server: " + (data.responseText || data.statusText));
- }
- },
- error: function (data) {
- deferral.reject("SignalR: Error pinging server: " + (data.responseText || data.statusText));
- }
- });
- return deferral.promise();
- },
- addQs: function (url, connection) {
- var appender = url.indexOf("?") !== -1 ? "&" : "?",
- firstChar;
- if (!connection.qs) {
- return url;
- }
- if (typeof (connection.qs) === "object") {
- return url + appender + $.param(connection.qs);
- }
- if (typeof (connection.qs) === "string") {
- firstChar = connection.qs.charAt(0);
- if (firstChar === "?" || firstChar === "&") {
- appender = "";
- }
- return url + appender + connection.qs;
- }
- throw new Error("Connections query string property must be either a string or object.");
- },
- getUrl: function (connection, transport, reconnecting, poll) {
- /// <summary>Gets the url for making a GET based connect request</summary>
- var baseUrl = transport === "webSockets" ? "" : connection.baseUrl,
- url = baseUrl + connection.appRelativeUrl,
- qs = "transport=" + transport + "&connectionToken=" + window.encodeURIComponent(connection.token);
- if (connection.data) {
- qs += "&connectionData=" + window.encodeURIComponent(connection.data);
- }
- if (connection.groupsToken) {
- qs += "&groupsToken=" + window.encodeURIComponent(connection.groupsToken);
- }
- if (!reconnecting) {
- url += "/connect";
- } else {
- if (poll) {
- // longPolling transport specific
- url += "/poll";
- } else {
- url += "/reconnect";
- }
- if (connection.messageId) {
- qs += "&messageId=" + window.encodeURIComponent(connection.messageId);
- }
- }
- url += "?" + qs;
- url = this.addQs(url, connection);
- url += "&tid=" + Math.floor(Math.random() * 11);
- return url;
- },
- maximizePersistentResponse: function (minPersistentResponse) {
- return {
- MessageId: minPersistentResponse.C,
- Messages: minPersistentResponse.M,
- Disconnect: typeof (minPersistentResponse.D) !== "undefined" ? true : false,
- TimedOut: typeof (minPersistentResponse.T) !== "undefined" ? true : false,
- LongPollDelay: minPersistentResponse.L,
- GroupsToken: minPersistentResponse.G
- };
- },
- updateGroups: function (connection, groupsToken) {
- if (groupsToken) {
- connection.groupsToken = groupsToken;
- }
- },
- ajaxSend: function (connection, data) {
- var url = connection.url + "/send" + "?transport=" + connection.transport.name + "&connectionToken=" + window.encodeURIComponent(connection.token);
- url = this.addQs(url, connection);
- return $.ajax({
- url: url,
- global: false,
- type: connection.ajaxDataType === "jsonp" ? "GET" : "POST",
- contentType: signalR._.defaultContentType,
- dataType: connection.ajaxDataType,
- data: {
- data: data
- },
- success: function (result) {
- if (result) {
- $(connection).triggerHandler(events.onReceived, [result]);
- }
- },
- error: function (errData, textStatus) {
- if (textStatus === "abort" || textStatus === "parsererror") {
- // The parsererror happens for sends that don't return any data, and hence
- // don't write the jsonp callback to the response. This is harder to fix on the server
- // so just hack around it on the client for now.
- return;
- }
- $(connection).triggerHandler(events.onError, [errData, data]);
- }
- });
- },
- ajaxAbort: function (connection, async) {
- if (typeof (connection.transport) === "undefined") {
- return;
- }
- // Async by default unless explicitly overidden
- async = typeof async === "undefined" ? true : async;
- var url = connection.url + "/abort" + "?transport=" + connection.transport.name + "&connectionToken=" + window.encodeURIComponent(connection.token);
- url = this.addQs(url, connection);
- $.ajax({
- url: url,
- async: async,
- timeout: 1000,
- global: false,
- type: "POST",
- contentType: connection.contentType,
- dataType: connection.ajaxDataType,
- data: {}
- });
- connection.log("Fired ajax abort async = " + async);
- },
- processMessages: function (connection, minData) {
- var data;
- // Transport can be null if we've just closed the connection
- if (connection.transport) {
- var $connection = $(connection);
- // If our transport supports keep alive then we need to update the last keep alive time stamp.
- // Very rarely the transport can be null.
- if (connection.transport.supportsKeepAlive && connection.keepAliveData.activated) {
- this.updateKeepAlive(connection);
- }
- if (!minData) {
- return;
- }
- data = this.maximizePersistentResponse(minData);
- if (data.Disconnect) {
- connection.log("Disconnect command received from server");
- // Disconnected by the server
- connection.stop(false, false);
- return;
- }
- this.updateGroups(connection, data.GroupsToken);
- if (data.Messages) {
- $.each(data.Messages, function (index, message) {
- $connection.triggerHandler(events.onReceived, [message]);
- });
- }
- if (data.MessageId) {
- connection.messageId = data.MessageId;
- }
- }
- },
- monitorKeepAlive: function (connection) {
- var keepAliveData = connection.keepAliveData,
- that = this;
- // If we haven't initiated the keep alive timeouts then we need to
- if (!keepAliveData.monitoring) {
- keepAliveData.monitoring = true;
- // Initialize the keep alive time stamp ping
- that.updateKeepAlive(connection);
- // Save the function so we can unbind it on stop
- connection.keepAliveData.reconnectKeepAliveUpdate = function () {
- that.updateKeepAlive(connection);
- };
- // Update Keep alive on reconnect
- $(connection).bind(events.onReconnect, connection.keepAliveData.reconnectKeepAliveUpdate);
- connection.log("Now monitoring keep alive with a warning timeout of " + keepAliveData.timeoutWarning + " and a connection lost timeout of " + keepAliveData.timeout);
- // Start the monitoring of the keep alive
- checkIfAlive(connection);
- }
- else {
- connection.log("Tried to monitor keep alive but it's already being monitored");
- }
- },
- stopMonitoringKeepAlive: function (connection) {
- var keepAliveData = connection.keepAliveData;
- // Only attempt to stop the keep alive monitoring if its being monitored
- if (keepAliveData.monitoring) {
- // Stop monitoring
- keepAliveData.monitoring = false;
- // Remove the updateKeepAlive function from the reconnect event
- $(connection).unbind(events.onReconnect, connection.keepAliveData.reconnectKeepAliveUpdate);
- // Clear all the keep alive data
- connection.keepAliveData = {};
- connection.log("Stopping the monitoring of the keep alive");
- }
- },
- updateKeepAlive: function (connection) {
- connection.keepAliveData.lastKeepAlive = new Date();
- },
- ensureReconnectingState: function (connection) {
- if (changeState(connection,
- signalR.connectionState.connected,
- signalR.connectionState.reconnecting) === true) {
- $(connection).triggerHandler(events.onReconnecting);
- }
- return connection.state === signalR.connectionState.reconnecting;
- },
- clearReconnectTimeout: function (connection) {
- if (connection && connection._.reconnectTimeout) {
- window.clearTimeout(connection._.reconnectTimeout);
- delete connection._.reconnectTimeout;
- }
- },
- reconnect: function (connection, transportName) {
- var transport = signalR.transports[transportName],
- that = this;
- // We should only set a reconnectTimeout if we are currently connected
- // and a reconnectTimeout isn't already set.
- if (isConnectedOrReconnecting(connection) && !connection._.reconnectTimeout) {
- connection._.reconnectTimeout = window.setTimeout(function () {
- transport.stop(connection);
- if (that.ensureReconnectingState(connection)) {
- connection.log(transportName + " reconnecting");
- transport.start(connection);
- }
- }, connection.reconnectDelay);
- }
- },
- foreverFrame: {
- count: 0,
- connections: {}
- }
- };
- }(window.jQuery, window));
- /* jquery.signalR.transports.webSockets.js */
- // Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
- /*global window:false */
- /// <reference path="jquery.signalR.transports.common.js" />
- (function ($, window) {
- "use strict";
- var signalR = $.signalR,
- events = $.signalR.events,
- changeState = $.signalR.changeState,
- transportLogic = signalR.transports._logic;
- signalR.transports.webSockets = {
- name: "webSockets",
- supportsKeepAlive: true,
- send: function (connection, data) {
- connection.socket.send(data);
- },
- start: function (connection, onSuccess, onFailed) {
- var url,
- opened = false,
- that = this,
- reconnecting = !onSuccess,
- $connection = $(connection);
- if (!window.WebSocket) {
- onFailed();
- return;
- }
- if (!connection.socket) {
- if (connection.webSocketServerUrl) {
- url = connection.webSocketServerUrl;
- }
- else {
- url = connection.wsProtocol + connection.host;
- }
- url += transportLogic.getUrl(connection, this.name, reconnecting);
- connection.log("Connecting to websocket endpoint '" + url + "'");
- connection.socket = new window.WebSocket(url);
- connection.socket.onopen = function () {
- opened = true;
- connection.log("Websocket opened");
- transportLogic.clearReconnectTimeout(connection);
- if (onSuccess) {
- onSuccess();
- } else if (changeState(connection,
- signalR.connectionState.reconnecting,
- signalR.connectionState.connected) === true) {
- $connection.triggerHandler(events.onReconnect);
- }
- };
- connection.socket.onclose = function (event) {
- // Only handle a socket close if the close is from the current socket.
- // Sometimes on disconnect the server will push down an onclose event
- // to an expired socket.
- if (this === connection.socket) {
- if (!opened) {
- if (onFailed) {
- onFailed();
- }
- else if (reconnecting) {
- that.reconnect(connection);
- }
- return;
- }
- 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).triggerHandler(events.onError, [event.reason]);
- connection.log("Unclean disconnect from websocket." + event.reason);
- }
- else {
- connection.log("Websocket closed");
- }
- that.reconnect(connection);
- }
- };
- connection.socket.onmessage = function (event) {
- var data = window.JSON.parse(event.data),
- $connection = $(connection);
- if (data) {
- // data.M is PersistentResponse.Messages
- if ($.isEmptyObject(data) || data.M) {
- transportLogic.processMessages(connection, data);
- } else {
- // For websockets we need to trigger onReceived
- // for callbacks to outgoing hub calls.
- $connection.triggerHandler(events.onReceived, [data]);
- }
- }
- };
- }
- },
- reconnect: function (connection) {
- transportLogic.reconnect(connection, this.name);
- },
- lostConnection: function (connection) {
- this.reconnect(connection);
- },
- stop: function (connection) {
- // Don't trigger a reconnect after stopping
- transportLogic.clearReconnectTimeout(connection);
- if (connection.socket !== null) {
- connection.log("Closing the Websocket");
- connection.socket.close();
- connection.socket = null;
- }
- },
- abort: function (connection) {
- }
- };
- }(window.jQuery, window));
- /* jquery.signalR.transports.serverSentEvents.js */
- // Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
- /*global window:false */
- /// <reference path="jquery.signalR.transports.common.js" />
- (function ($, window) {
- "use strict";
- var signalR = $.signalR,
- events = $.signalR.events,
- changeState = $.signalR.changeState,
- transportLogic = signalR.transports._logic;
- signalR.transports.serverSentEvents = {
- name: "serverSentEvents",
- supportsKeepAlive: true,
- timeOut: 3000,
- start: function (connection, onSuccess, onFailed) {
- var that = this,
- opened = false,
- $connection = $(connection),
- reconnecting = !onSuccess,
- url,
- connectTimeOut;
- if (connection.eventSource) {
- connection.log("The connection already has an event source. Stopping it.");
- connection.stop();
- }
- if (!window.EventSource) {
- if (onFailed) {
- connection.log("This browser doesn't support SSE.");
- onFailed();
- }
- return;
- }
- url = transportLogic.getUrl(connection, this.name, reconnecting);
- try {
- connection.log("Attempting to connect to SSE endpoint '" + url + "'");
- connection.eventSource = new window.EventSource(url);
- }
- catch (e) {
- connection.log("EventSource failed trying to connect with error " + e.Message);
- if (onFailed) {
- // The connection failed, call the failed callback
- onFailed();
- }
- else {
- $connection.triggerHandler(events.onError, [e]);
- if (reconnecting) {
- // If we were reconnecting, rather than doing initial connect, then try reconnect again
- 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) {
- connection.log("EventSource timed out trying to connect");
- connection.log("EventSource readyState: " + connection.eventSource.readyState);
- if (!reconnecting) {
- that.stop(connection);
- }
- if (reconnecting) {
- // If we're reconnecting and the event source is attempting to connect,
- // don't keep retrying. This causes duplicate connections to spawn.
- if (connection.eventSource.readyState !== window.EventSource.CONNECTING &&
- connection.eventSource.readyState !== window.EventSource.OPEN) {
- // If we were reconnecting, rather than doing initial connect, then try reconnect again
- that.reconnect(connection);
- }
- } else if (onFailed) {
- onFailed();
- }
- }
- },
- that.timeOut);
- connection.eventSource.addEventListener("open", function (e) {
- connection.log("EventSource connected");
- if (connectTimeOut) {
- window.clearTimeout(connectTimeOut);
- }
- transportLogic.clearReconnectTimeout(connection);
- if (opened === false) {
- opened = true;
- if (onSuccess) {
- onSuccess();
- } else if (changeState(connection,
- signalR.connectionState.reconnecting,
- signalR.connectionState.connected) === true) {
- // If there's no onSuccess handler we assume this is a reconnect
- $connection.triggerHandler(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) {
- // Only handle an error if the error is from the current Event Source.
- // Sometimes on disconnect the server will push down an error event
- // to an expired Event Source.
- if (this === connection.eventSource) {
- if (!opened) {
- if (onFailed) {
- onFailed();
- }
- return;
- }
- connection.log("EventSource readyState: " + connection.eventSource.readyState);
- if (e.eventPhase === window.EventSource.CLOSED) {
- // 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.
- connection.log("EventSource reconnecting due to the server connection ending");
- that.reconnect(connection);
- } else {
- // connection error
- connection.log("EventSource error");
- $connection.triggerHandler(events.onError);
- }
- }
- }, false);
- },
- reconnect: function (connection) {
- transportLogic.reconnect(connection, this.name);
- },
- lostConnection: function (connection) {
- this.reconnect(connection);
- },
- send: function (connection, data) {
- transportLogic.ajaxSend(connection, data);
- },
- stop: function (connection) {
- // Don't trigger a reconnect after stopping
- transportLogic.clearReconnectTimeout(connection);
- if (connection && connection.eventSource) {
- connection.log("EventSource calling close()");
- connection.eventSource.close();
- connection.eventSource = null;
- delete connection.eventSource;
- }
- },
- abort: function (connection, async) {
- transportLogic.ajaxAbort(connection, async);
- }
- };
- }(window.jQuery, window));
- /* jquery.signalR.transports.foreverFrame.js */
- // Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
- /*global window:false */
- /// <reference path="jquery.signalR.transports.common.js" />
- (function ($, window) {
- "use strict";
- var signalR = $.signalR,
- events = $.signalR.events,
- changeState = $.signalR.changeState,
- transportLogic = signalR.transports._logic,
- // Used to prevent infinite loading icon spins in older versions of ie
- // We build this object inside a closure so we don't pollute the rest of
- // the foreverFrame transport with unnecessary functions/utilities.
- loadPreventer = (function () {
- var loadingFixIntervalId = null,
- loadingFixInterval = 1000,
- attachedTo = 0;
- return {
- prevent: function () {
- // Prevent additional iframe removal procedures from newer browsers
- if (signalR._.ieVersion <= 8) {
- // We only ever want to set the interval one time, so on the first attachedTo
- if (attachedTo === 0) {
- // Create and destroy iframe every 3 seconds to prevent loading icon, super hacky
- loadingFixIntervalId = window.setInterval(function () {
- var tempFrame = $("<iframe style='position:absolute;top:0;left:0;width:0;height:0;visibility:hidden;' src=''></iframe>");
- $("body").append(tempFrame);
- tempFrame.remove();
- tempFrame = null;
- }, loadingFixInterval);
- }
- attachedTo++;
- }
- },
- cancel: function () {
- // Only clear the interval if there's only one more object that the loadPreventer is attachedTo
- if (attachedTo === 1) {
- window.clearInterval(loadingFixIntervalId);
- }
- if (attachedTo > 0) {
- attachedTo--;
- }
- }
- };
- })();
- signalR.transports.foreverFrame = {
- name: "foreverFrame",
- supportsKeepAlive: true,
- timeOut: 3000,
- start: function (connection, onSuccess, onFailed) {
- var that = this,
- frameId = (transportLogic.foreverFrame.count += 1),
- url,
- frame = $("<iframe data-signalr-connection-id='" + connection.id + "' style='position:absolute;top:0;left:0;width:0;height:0;visibility:hidden;' src=''></iframe>");
- if (window.EventSource) {
- // If the browser supports SSE, don't use Forever Frame
- if (onFailed) {
- connection.log("This browser supports SSE, skipping Forever Frame.");
- onFailed();
- }
- return;
- }
- // Start preventing loading icon
- // This will only perform work if the loadPreventer is not attached to another connection.
- loadPreventer.prevent();
- // Build the url
- url = transportLogic.getUrl(connection, this.name);
- url += "&frameId=" + frameId;
- // Set body prior to setting URL to avoid caching issues.
- $("body").append(frame);
- frame.prop("src", url);
- transportLogic.foreverFrame.connections[frameId] = connection;
- connection.log("Binding to iframe's readystatechange event.");
- frame.bind("readystatechange", function () {
- if ($.inArray(this.readyState, ["loaded", "complete"]) >= 0) {
- connection.log("Forever frame iframe readyState changed to " + this.readyState + ", reconnecting");
- that.reconnect(connection);
- }
- });
- connection.frame = frame[0];
- connection.frameId = frameId;
- if (onSuccess) {
- connection.onSuccess = onSuccess;
- }
- // After connecting, if after the specified timeout there's no response stop the connection
- // and raise on failed
- window.setTimeout(function () {
- if (connection.onSuccess) {
- connection.log("Failed to connect using forever frame source, it timed out after " + that.timeOut + "ms.");
- that.stop(connection);
- if (onFailed) {
- onFailed();
- }
- }
- }, that.timeOut);
- },
- reconnect: function (connection) {
- var that = this;
- window.setTimeout(function () {
- if (connection.frame && transportLogic.ensureReconnectingState(connection)) {
- var frame = connection.frame,
- src = transportLogic.getUrl(connection, that.name, true) + "&frameId=" + connection.frameId;
- connection.log("Updating iframe src to '" + src + "'.");
- frame.src = src;
- }
- }, connection.reconnectDelay);
- },
- lostConnection: function (connection) {
- this.reconnect(connection);
- },
- send: function (connection, data) {
- transportLogic.ajaxSend(connection, data);
- },
- receive: function (connection, data) {
- var cw;
- transportLogic.processMessages(connection, data);
- // Delete the script & div elements
- connection.frameMessageCount = (connection.frameMessageCount || 0) + 1;
- if (connection.frameMessageCount > 50) {
- connection.frameMessageCount = 0;
- cw = connection.frame.contentWindow || connection.frame.contentDocument;
- if (cw && cw.document) {
- $("body", cw.document).empty();
- }
- }
- },
- stop: function (connection) {
- var cw = null;
- // Stop attempting to prevent loading icon
- loadPreventer.cancel();
- if (connection.frame) {
- if (connection.frame.stop) {
- connection.frame.stop();
- } else {
- try {
- cw = connection.frame.contentWindow || connection.frame.contentDocument;
- if (cw.document && cw.document.execCommand) {
- cw.document.execCommand("Stop");
- }
- }
- catch (e) {
- connection.log("SignalR: Error occured when stopping foreverFrame transport. Message = " + e.message);
- }
- }
- $(connection.frame).remove();
- delete transportLogic.foreverFrame.connections[connection.frameId];
- connection.frame = null;
- connection.frameId = null;
- delete connection.frame;
- delete connection.frameId;
- connection.log("Stopping forever frame");
- }
- },
- abort: function (connection, async) {
- transportLogic.ajaxAbort(connection, async);
- },
- getConnection: function (id) {
- return transportLogic.foreverFrame.connections[id];
- },
- started: function (connection) {
- if (connection.onSuccess) {
- connection.onSuccess();
- connection.onSuccess = null;
- delete connection.onSuccess;
- } else if (changeState(connection,
- signalR.connectionState.reconnecting,
- signalR.connectionState.connected) === true) {
- // If there's no onSuccess handler we assume this is a reconnect
- $(connection).triggerHandler(events.onReconnect);
- }
- }
- };
- }(window.jQuery, window));
- /* jquery.signalR.transports.longPolling.js */
- // Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
- /*global window:false */
- /// <reference path="jquery.signalR.transports.common.js" />
- (function ($, window) {
- "use strict";
- var signalR = $.signalR,
- events = $.signalR.events,
- changeState = $.signalR.changeState,
- isDisconnecting = $.signalR.isDisconnecting,
- transportLogic = signalR.transports._logic;
- signalR.transports.longPolling = {
- name: "longPolling",
- supportsKeepAlive: false,
- reconnectDelay: 3000,
- init: function (connection, onComplete) {
- /// <summary>Pings the server to ensure availability</summary>
- /// <param name="connection" type="signalr">Connection associated with the server ping</param>
- /// <param name="onComplete" type="Function">Callback to call once initialization has completed</param>
- var that = this,
- pingLoop,
- // pingFail is used to loop the re-ping behavior. When we fail we want to re-try.
- pingFail = function (reason) {
- if (isDisconnecting(connection) === false) {
- connection.log("SignalR: Server ping failed because '" + reason + "', re-trying ping.");
- window.setTimeout(pingLoop, that.reconnectDelay);
- }
- };
- connection.log("SignalR: Initializing long polling connection with server.");
- pingLoop = function () {
- // Ping the server, on successful ping call the onComplete method, otherwise if we fail call the pingFail
- transportLogic.pingServer(connection, that.name).done(onComplete).fail(pingFail);
- };
- pingLoop();
- },
- 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,
- initialConnectedFired = false,
- fireConnect = function () {
- if (initialConnectedFired) {
- return;
- }
- initialConnectedFired = true;
- onSuccess();
- connection.log("Longpolling connected");
- },
- reconnectErrors = 0,
- reconnectTimeoutId = null,
- fireReconnected = function (instance) {
- window.clearTimeout(reconnectTimeoutId);
- reconnectTimeoutId = null;
- if (changeState(connection,
- signalR.connectionState.reconnecting,
- signalR.connectionState.connected) === true) {
- // Successfully reconnected!
- connection.log("Raising the reconnect event");
- $(instance).triggerHandler(events.onReconnect);
- }
- },
- // 1 hour
- maxFireReconnectedTimeout = 3600000;
- if (connection.pollXhr) {
- connection.log("Polling xhr requests already exists, aborting.");
- connection.stop();
- }
- // We start with an initialization procedure which pings the server to verify that it is there.
- // On scucessful initialization we'll then proceed with starting the transport.
- that.init(connection, function () {
- connection.messageId = null;
- window.setTimeout(function () {
- (function poll(instance, raiseReconnect) {
- var messageId = instance.messageId,
- connect = (messageId === null),
- reconnecting = !connect,
- polling = !raiseReconnect,
- url = transportLogic.getUrl(instance, that.name, reconnecting, polling);
- // If we've disconnected during the time we've tried to re-instantiate the poll then stop.
- if (isDisconnecting(instance) === true) {
- return;
- }
- connection.log("Attempting to connect to '" + url + "' using longPolling.");
- instance.pollXhr = $.ajax({
- url: url,
- global: false,
- cache: false,
- type: "GET",
- dataType: connection.ajaxDataType,
- contentType: connection.contentType,
- success: function (minData) {
- var delay = 0,
- data;
- // Reset our reconnect errors so if we transition into a reconnecting state again we trigger
- // reconnected quickly
- reconnectErrors = 0;
- // If there's currently a timeout to trigger reconnect, fire it now before processing messages
- if (reconnectTimeoutId !== null) {
- fireReconnected();
- }
- fireConnect();
- if (minData) {
- data = transportLogic.maximizePersistentResponse(minData);
- }
- transportLogic.processMessages(instance, minData);
- if (data &&
- $.type(data.LongPollDelay) === "number") {
- delay = data.LongPollDelay;
- }
- if (data && data.Disconnect) {
- return;
- }
- if (isDisconnecting(instance) === true) {
- return;
- }
- // We never want to pass a raiseReconnect flag after a successful poll. This is handled via the error function
- if (delay > 0) {
- window.setTimeout(function () {
- poll(instance, false);
- }, delay);
- } else {
- poll(instance, false);
- }
- },
- error: function (data, textStatus) {
- // Stop trying to trigger reconnect, connection is in an error state
- // If we're not in the reconnect state this will noop
- window.clearTimeout(reconnectTimeoutId);
- reconnectTimeoutId = null;
- if (textStatus === "abort") {
- connection.log("Aborted xhr requst.");
- return;
- }
- // Increment our reconnect errors, we assume all errors to be reconnect errors
- // In the case that it's our first error this will cause Reconnect to be fired
- // after 1 second due to reconnectErrors being = 1.
- reconnectErrors++;
- if (connection.state !== signalR.connectionState.reconnecting) {
- connection.log("An error occurred using longPolling. Status = " + textStatus + ". " + data.responseText);
- $(instance).triggerHandler(events.onError, [data.responseText]);
- }
- // Transition into the reconnecting state
- transportLogic.ensureReconnectingState(instance);
- // If we've errored out we need to verify that the server is still there, so re-start initialization process
- // This will ping the server until it successfully gets a response.
- that.init(instance, function () {
- // Call poll with the raiseReconnect flag as true
- poll(instance, true);
- });
- }
- });
- // This will only ever pass after an error has occured via the poll ajax procedure.
- if (reconnecting && raiseReconnect === true) {
- // We wait to reconnect depending on how many times we've failed to reconnect.
- // This is essentially a heuristic that will exponentially increase in wait time before
- // triggering reconnected. This depends on the "error" handler of Poll to cancel this
- // timeout if it triggers before the Reconnected event fires.
- // The Math.min at the end is to ensure that the reconnect timeout does not overflow.
- reconnectTimeoutId = window.setTimeout(function () { fireReconnected(instance); }, Math.min(1000 * (Math.pow(2, reconnectErrors) - 1), maxFireReconnectedTimeout));
- }
- }(connection));
- // Set an arbitrary timeout to trigger onSuccess, this will alot for enough time on the server to wire up the connection.
- // Will be fixed by #1189 and this code can be modified to not be a timeout
- window.setTimeout(function () {
- // Trigger the onSuccess() method because we've now instantiated a connection
- fireConnect();
- }, 250);
- }, 250); // Have to delay initial poll so Chrome doesn't show loader spinner in tab
- });
- },
- lostConnection: function (connection) {
- throw new Error("Lost Connection not handled for LongPolling");
- },
- 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;
- }
- },
- abort: function (connection, async) {
- transportLogic.ajaxAbort(connection, async);
- }
- };
- }(window.jQuery, window));
- /* jquery.signalR.hubs.js */
- // Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
- /*global window:false */
- /// <reference path="jquery.signalR.core.js" />
- (function ($, window) {
- "use strict";
- // we use a global id for tracking callbacks so the server doesn't have to send extra info like hub name
- var eventNamespace = ".hubProxy";
- function makeEventName(event) {
- return event + eventNamespace;
- }
- // Equivalent to Array.prototype.map
- function map(arr, fun, thisp) {
- var i,
- length = arr.length,
- result = [];
- for (i = 0; i < length; i += 1) {
- if (arr.hasOwnProperty(i)) {
- result[i] = fun.call(thisp, arr[i], i, arr);
- }
- }
- return result;
- }
- function getArgValue(a) {
- return $.isFunction(a) ? null : ($.type(a) === "undefined" ? null : a);
- }
- function hasMembers(obj) {
- for (var key in obj) {
- // If we have any properties in our callback map then we have callbacks and can exit the loop via return
- if (obj.hasOwnProperty(key)) {
- return true;
- }
- }
- return false;
- }
- function clearInvocationCallbacks(connection, error) {
- /// <param name="connection" type="hubConnection" />
- var callbacks = connection._.invocationCallbacks,
- callback;
-
- connection.log("Clearing hub invocation callbacks with error: " + error);
-
- // Reset the callback cache now as we have a local var referencing it
- connection._.invocationCallbackId = 0;
- delete connection._.invocationCallbacks;
- connection._.invocationCallbacks = {};
- // Loop over the callbacks and invoke them.
- // We do this using a local var reference and *after* we've cleared the cache
- // so that if a fail callback itself tries to invoke another method we don't
- // end up with its callback in the list we're looping over.
- for (var callbackId in callbacks) {
- callback = callbacks[callbackId];
- callback.method.call(callback.scope, { E: error });
- }
- }
- // hubProxy
- function hubProxy(hubConnection, hubName) {
- /// <summary>
- /// Creates a new proxy object for the given hub connection that can be used to invoke
- /// methods on server hubs and handle client method invocation requests from the server.
- /// </summary>
- return new hubProxy.fn.init(hubConnection, hubName);
- }
- hubProxy.fn = hubProxy.prototype = {
- init: function (connection, hubName) {
- this.state = {};
- this.connection = connection;
- this.hubName = hubName;
- this._ = {
- callbackMap: {}
- };
- },
- hasSubscriptions: function () {
- return hasMembers(this._.callbackMap);
- },
- on: function (eventName, callback) {
- /// <summary>Wires up a callback to be invoked when a invocation request is received from the server hub.</summary>
- /// <param name="eventName" type="String">The name of the hub event to register the callback for.</param>
- /// <param name="callback" type="Function">The callback to be invoked.</param>
- var self = this,
- callbackMap = self._.callbackMap;
- // Normalize the event name to lowercase
- eventName = eventName.toLowerCase();
- // If there is not an event registered for this callback yet we want to create its event space in the callback map.
- if (!callbackMap[eventName]) {
- callbackMap[eventName] = {};
- }
- // Map the callback to our encompassed function
- callbackMap[eventName][callback] = function (e, data) {
- callback.apply(self, data);
- };
- $(self).bind(makeEventName(eventName), callbackMap[eventName][callback]);
- return self;
- },
- off: function (eventName, callback) {
- /// <summary>Removes the callback invocation request from the server hub for the given event name.</summary>
- /// <param name="eventName" type="String">The name of the hub event to unregister the callback for.</param>
- /// <param name="callback" type="Function">The callback to be invoked.</param>
- var self = this,
- callbackMap = self._.callbackMap,
- callbackSpace;
- // Normalize the event name to lowercase
- eventName = eventName.toLowerCase();
- callbackSpace = callbackMap[eventName];
- // Verify that there is an event space to unbind
- if (callbackSpace) {
- // Only unbind if there's an event bound with eventName and a callback with the specified callback
- if (callbackSpace[callback]) {
- $(self).unbind(makeEventName(eventName), callbackSpace[callback]);
- // Remove the callback from the callback map
- delete callbackSpace[callback];
- // Check if there are any members left on the event, if not we need to destroy it.
- if (!hasMembers(callbackSpace)) {
- delete callbackMap[eventName];
- }
- }
- else if (!callback) { // Check if we're removing the whole event and we didn't error because of an invalid callback
- $(self).unbind(makeEventName(eventName));
- delete callbackMap[eventName];
- }
- }
- return self;
- },
- invoke: function (methodName) {
- /// <summary>Invokes a server hub method with the given arguments.</summary>
- /// <param name="methodName" type="String">The name of the server hub method.</param>
- var self = this,
- connection = self.connection,
- args = $.makeArray(arguments).slice(1),
- argValues = map(args, getArgValue),
- data = { H: self.hubName, M: methodName, A: argValues, I: connection._.invocationCallbackId },
- d = $.Deferred(),
- callback = function (minResult) {
- var result = self._maximizeHubResponse(minResult);
- // Update the hub state
- $.extend(self.state, result.State);
- if (result.Error) {
- // Server hub method threw an exception, log it & reject the deferred
- if (result.StackTrace) {
- connection.log(result.Error + "\n" + result.StackTrace);
- }
- d.rejectWith(self, [result.Error]);
- } else {
- // Server invocation succeeded, resolve the deferred
- d.resolveWith(self, [result.Result]);
- }
- };
- connection._.invocationCallbacks[connection._.invocationCallbackId.toString()] = { scope: self, method: callback };
- connection._.invocationCallbackId += 1;
- if (!$.isEmptyObject(self.state)) {
- data.S = self.state;
- }
-
- connection.send(window.JSON.stringify(data));
- return d.promise();
- },
- _maximizeHubResponse: function (minHubResponse) {
- return {
- State: minHubResponse.S,
- Result: minHubResponse.R,
- Id: minHubResponse.I,
- Error: minHubResponse.E,
- StackTrace: minHubResponse.T
- };
- }
- };
- hubProxy.fn.init.prototype = hubProxy.fn;
- // hubConnection
- function hubConnection(url, options) {
- /// <summary>Creates a new hub connection.</summary>
- /// <param name="url" type="String">[Optional] The hub route url, defaults to "/signalr".</param>
- /// <param name="options" type="Object">[Optional] Settings to use when creating the hubConnection.</param>
- var settings = {
- qs: null,
- logging: false,
- useDefaultPath: true
- };
- $.extend(settings, options);
- if (!url || settings.useDefaultPath) {
- url = (url || "") + "/signalr";
- }
- return new hubConnection.fn.init(url, settings);
- }
- hubConnection.fn = hubConnection.prototype = $.connection();
- hubConnection.fn.init = function (url, options) {
- var settings = {
- qs: null,
- logging: false,
- useDefaultPath: true
- },
- connection = this;
- $.extend(settings, options);
- // Call the base constructor
- $.signalR.fn.init.call(connection, url, settings.qs, settings.logging);
- // Object to store hub proxies for this connection
- connection.proxies = {};
- connection._.invocationCallbackId = 0;
- connection._.invocationCallbacks = {};
- // Wire up the received handler
- connection.received(function (minData) {
- var data, proxy, dataCallbackId, callback, hubName, eventName;
- if (!minData) {
- return;
- }
- if (typeof (minData.I) !== "undefined") {
- // We received the return value from a server method invocation, look up callback by id and call it
- dataCallbackId = minData.I.toString();
- callback = connection._.invocationCallbacks[dataCallbackId];
- if (callback) {
- // Delete the callback from the proxy
- connection._.invocationCallbacks[dataCallbackId] = null;
- delete connection._.invocationCallbacks[dataCallbackId];
- // Invoke the callback
- callback.method.call(callback.scope, minData);
- }
- } else {
- data = this._maximizeClientHubInvocation(minData);
- // We received a client invocation request, i.e. broadcast from server hub
- connection.log("Triggering client hub event '" + data.Method + "' on hub '" + data.Hub + "'.");
- // Normalize the names to lowercase
- hubName = data.Hub.toLowerCase();
- eventName = data.Method.toLowerCase();
- // Trigger the local invocation event
- proxy = this.proxies[hubName];
- // Update the hub state
- $.extend(proxy.state, data.State);
- $(proxy).triggerHandler(makeEventName(eventName), [data.Args]);
- }
- });
- connection.error(function (errData, origData) {
- var data, callbackId, callback;
- if (connection.transport && connection.transport.name === "webSockets") {
- // WebSockets connections have all callbacks removed on reconnect instead
- // as WebSockets sends are fire & forget
- return;
- }
- if (!origData) {
- // No original data passed so this is not a send error
- return;
- }
- try {
- data = window.JSON.parse(origData);
- if (!data.I) {
- // The original data doesn't have a callback ID so not a send error
- return;
- }
- } catch (e) {
- // The original data is not a JSON payload so this is not a send error
- return;
- }
-
- callbackId = data.I;
- callback = connection._.invocationCallbacks[callbackId];
- // Invoke the callback with an error to reject the promise
- callback.method.call(callback.scope, { E: errData });
- // Delete the callback
- connection._.invocationCallbacks[callbackId] = null;
- delete connection._.invocationCallbacks[callbackId];
- });
- connection.reconnecting(function () {
- if (connection.transport && connection.transport.name === "webSockets") {
- clearInvocationCallbacks(connection, "Connection started reconnecting before invocation result was received.");
- }
- });
- connection.disconnected(function () {
- clearInvocationCallbacks(connection, "Connection was disconnected before invocation result was received.");
- });
- };
- hubConnection.fn._maximizeClientHubInvocation = function (minClientHubInvocation) {
- return {
- Hub: minClientHubInvocation.H,
- Method: minClientHubInvocation.M,
- Args: minClientHubInvocation.A,
- State: minClientHubInvocation.S
- };
- };
- hubConnection.fn._registerSubscribedHubs = function () {
- /// <summary>
- /// Sets the starting event to loop through the known hubs and register any new hubs
- /// that have been added to the proxy.
- /// </summary>
- if (!this._subscribedToHubs) {
- this._subscribedToHubs = true;
- this.starting(function () {
- // Set the connection's data object with all the hub proxies with active subscriptions.
- // These proxies will receive notifications from the server.
- var subscribedHubs = [];
- $.each(this.proxies, function (key) {
- if (this.hasSubscriptions()) {
- subscribedHubs.push({ name: key });
- }
- });
- this.data = window.JSON.stringify(subscribedHubs);
- });
- }
- };
- hubConnection.fn.createHubProxy = function (hubName) {
- /// <summary>
- /// Creates a new proxy object for the given hub connection that can be used to invoke
- /// methods on server hubs and handle client method invocation requests from the server.
- /// </summary>
- /// <param name="hubName" type="String">
- /// The name of the hub on the server to create the proxy for.
- /// </param>
- // Normalize the name to lowercase
- hubName = hubName.toLowerCase();
- var proxy = this.proxies[hubName];
- if (!proxy) {
- proxy = hubProxy(this, hubName);
- this.proxies[hubName] = proxy;
- }
- this._registerSubscribedHubs();
- return proxy;
- };
- hubConnection.fn.init.prototype = hubConnection.fn;
- $.hubConnection = hubConnection;
- }(window.jQuery, window));
- /* jquery.signalR.version.js */
- // Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
- /*global window:false */
- /// <reference path="jquery.signalR.core.js" />
- (function ($) {
- $.signalR.version = "1.1.3";
- }(window.jQuery));
|