cluedin-widget
Version:
This is the project for creating and managing widgets in CluedIn.
1,142 lines (930 loc) • 125 kB
JavaScript
/* jquery.signalR.core.js */
/*global window:false */
/*!
* ASP.NET SignalR JavaScript Library v2.2.0
* http://signalr.net/
*
* Copyright (C) Microsoft Corporation. All rights reserved.
*
*/
/// <reference path="Scripts/jquery-1.6.4.js" />
/// <reference path="jquery.signalR.version.js" />
(function( $, window, undefined ) {
var resources = {
nojQuery: "jQuery was not found. Please ensure jQuery is referenced before the SignalR client JavaScript file.",
noTransportOnInit: "No transport could be initialized successfully. Try specifying a different transport or none at all for auto initialization.",
errorOnNegotiate: "Error during negotiation request.",
stoppedWhileLoading: "The connection was stopped during page load.",
stoppedWhileNegotiating: "The connection was stopped during the negotiate request.",
errorParsingNegotiateResponse: "Error parsing negotiate response.",
errorDuringStartRequest: "Error during start request. Stopping the connection.",
stoppedDuringStartRequest: "The connection was stopped during the start request.",
errorParsingStartResponse: "Error parsing start response: '{0}'. Stopping the connection.",
invalidStartResponse: "Invalid start response: '{0}'. Stopping the connection.",
protocolIncompatible: "You are using a version of the client that isn't compatible with the server. Client version {0}, server version {1}.",
sendFailed: "Send failed.",
parseFailed: "Failed at parsing response: {0}",
longPollFailed: "Long polling request failed.",
eventSourceFailedToConnect: "EventSource failed to connect.",
eventSourceError: "Error raised by EventSource",
webSocketClosed: "WebSocket closed.",
pingServerFailedInvalidResponse: "Invalid ping response when pinging server: '{0}'.",
pingServerFailed: "Failed to ping server.",
pingServerFailedStatusCode: "Failed to ping server. Server responded with status code {0}, stopping the connection.",
pingServerFailedParse: "Failed to parse ping server response, stopping the connection.",
noConnectionTransport: "Connection is in an invalid state, there is no transport active.",
webSocketsInvalidState: "The Web Socket transport is in an invalid state, transitioning into reconnecting.",
reconnectTimeout: "Couldn't reconnect within the configured timeout of {0} ms, disconnecting.",
reconnectWindowTimeout: "The client has been inactive since {0} and it has exceeded the inactivity timeout of {1} ms. Stopping the connection."
};
if ( typeof ($) !== "function" ) {
// no jQuery!
throw new Error( resources.nojQuery );
}
var signalR,
_connection,
_pageLoaded = (window.document.readyState === "complete"),
_pageWindow = $( window ),
_negotiateAbortText = "__Negotiate Aborted__",
events = {
onStart: "onStart",
onStarting: "onStarting",
onReceived: "onReceived",
onError: "onError",
onConnectionSlow: "onConnectionSlow",
onReconnecting: "onReconnecting",
onReconnect: "onReconnect",
onStateChanged: "onStateChanged",
onDisconnect: "onDisconnect"
},
ajaxDefaults = {
processData: true,
timeout: null,
async: true,
global: false,
cache: false
},
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;
},
supportsKeepAlive = function( connection ) {
return connection._.keepAliveData.activated &&
connection.transport.supportsKeepAlive( connection );
},
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 ) {
var message = signalR._.format( signalR.resources.reconnectTimeout, connection.disconnectTimeout );
connection.log( message );
$( connection ).triggerHandler( events.onError, [ signalR._.error( message, /* source */ "TimeoutException" ) ] );
connection.stop( /* async */ false, /* notifyServer */ false );
};
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;
})(),
error: function( message, source, context ) {
var e = new Error( message );
e.source = source;
if ( typeof context !== "undefined" ) {
e.context = context;
}
return e;
},
transportError: function( message, transport, source, context ) {
var e = this.error( message, source, context );
e.transport = transport ? transport.name : undefined;
return e;
},
format: function() {
/// <summary>Usage: format("Hi {0}, you are {1}!", "Foo", 100) </summary>
var s = arguments[ 0 ];
for( var i = 0; i < arguments.length - 1; i++ ) {
s = s.replace( "{" + i + "}", arguments[ i + 1 ] );
}
return s;
},
firefoxMajorVersion: function( userAgent ) {
// Firefox user agents: http://useragentstring.com/pages/Firefox/
var matches = userAgent.match( /Firefox\/(\d+)/ );
if ( !matches || !matches.length || matches.length < 2 ) {
return 0;
}
return parseInt( matches[ 1 ], 10 /* radix */ );
},
configurePingInterval: function( connection ) {
var config = connection._.config,
onFail = function( error ) {
$( connection ).triggerHandler( events.onError, [ error ] );
};
if ( config && !connection._.pingIntervalId && config.pingInterval ) {
connection._.pingIntervalId = window.setInterval( function() {
signalR.transports._logic.pingServer( connection ).fail( onFail );
}, config.pingInterval );
}
}
};
signalR.events = events;
signalR.resources = resources;
signalR.ajaxDefaults = ajaxDefaults;
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/js'></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( 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 ( !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 );
}
}
function ConnectingMessageBuffer( connection, drainCallback ) {
var that = this,
buffer = [];
that.tryBuffer = function( message ) {
if ( connection.state === $.signalR.connectionState.connecting ) {
buffer.push( message );
return true;
}
return false;
};
that.drain = function() {
// Ensure that the connection is connected when we drain (do not want to drain while a connection is not active)
if ( connection.state === $.signalR.connectionState.connected ) {
while( buffer.length > 0 ) {
drainCallback( buffer.shift() );
}
}
};
that.clear = function() {
buffer = [];
};
}
signalR.fn = signalR.prototype = {
init: function( url, qs, logging ) {
var $connection = $( this );
this.url = url;
this.qs = qs;
this.lastError = null;
this._ = {
keepAliveData: {},
connectingMessageBuffer: new ConnectingMessageBuffer( this, function( message ) {
$connection.triggerHandler( events.onReceived, [ message ] );
} ),
lastMessageAt: new Date().getTime(),
lastActiveAt: new Date().getTime(),
beatInterval: 5000, // Default value, will only be overridden if keep alive is enabled,
beatHandle: null,
totalTransportConnectTimeout: 0 // This will be the sum of the TransportConnectTimeout sent in response to negotiate and connection.transportConnectTimeout
};
if ( typeof (logging) === "boolean" ) {
this.logging = logging;
}
},
_parseResponse: function( response ) {
var that = this;
if ( !response ) {
return response;
} else if ( typeof response === "string" ) {
return that.json.parse( response );
} else {
return response;
}
},
_originalJson: window.JSON,
json: window.JSON,
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 );
against = against || window.location;
if ( url.indexOf( "http" ) !== 0 ) {
return false;
}
// 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: "text",
contentType: "application/json; charset=UTF-8",
logging: false,
state: signalR.connectionState.disconnected,
clientProtocol: "1.5",
reconnectDelay: 2000,
transportConnectTimeout: 0,
disconnectTimeout: 30000, // This should be set by the server in response to the negotiate request (30s default)
reconnectWindow: 30000, // This should be set by the server in response to the negotiate request
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 = {
pingInterval: 300000,
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" );
connection.lastError = null;
// Persist the deferral so that if start is called multiple times the same deferral is used.
connection._deferral = deferred;
if ( !connection.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." );
}
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." );
}
connection._.config = config;
// 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 ) {
connection._.deferredStartHandler = function() {
connection.start( options, callback );
};
_pageWindow.bind( "load", connection._.deferredStartHandler );
return deferred.promise();
}
// If we're already connecting just return the same deferral as the original connection start
if ( connection.state === signalR.connectionState.connecting ) {
return deferred.promise();
} else if ( changeState( connection,
signalR.connectionState.disconnected,
signalR.connectionState.connecting ) === false ) {
// We're not connecting so try and transition into connecting.
// If we fail to transition then we're either in connected or reconnecting.
deferred.resolve( connection );
return deferred.promise();
}
configureStopReconnectingTimeout( connection );
// Resolve the full url
parser.href = connection.url;
if ( !parser.protocol || parser.protocol === ":" ) {
connection.protocol = window.document.location.protocol;
connection.host = parser.host || window.document.location.host;
} else {
connection.protocol = parser.protocol;
connection.host = parser.host;
}
connection.baseUrl = connection.protocol + "//" + connection.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 the url is protocol relative, prepend the current windows protocol to the url.
if ( connection.url.indexOf( "//" ) === 0 ) {
connection.url = window.location.protocol + connection.url;
connection.log( "Protocol relative URL detected, normalizing it to '" + connection.url + "'." );
}
if ( this.isCrossDomain( connection.url ) ) {
connection.log( "Auto detected cross domain url." );
if ( config.transport === "auto" ) {
// TODO: Support XDM with foreverFrame
config.transport = [ "webSockets", "serverSentEvents", "longPolling" ];
}
if ( typeof (config.withCredentials) === "undefined" ) {
config.withCredentials = true;
}
// 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.withCredentials = config.withCredentials;
connection.ajaxDataType = config.jsonp ? "jsonp" : "text";
$( connection ).bind( events.onStart, function( e, data ) {
if ( $.type( callback ) === "function" ) {
callback.call( connection );
}
deferred.resolve( connection );
} );
connection._.initHandler = signalR.transports._logic.initHandler( connection );
initialize = function( transports, index ) {
var noTransportError = signalR._.error( resources.noTransportOnInit );
index = index || 0;
if ( index >= transports.length ) {
if ( index === 0 ) {
connection.log( "No transports supported by the server were selected." );
} else if ( index === 1 ) {
connection.log( "No fallback transports were selected." );
} else {
connection.log( "Fallback transports exhausted." );
}
// No transport initialized successfully
$( connection ).triggerHandler( events.onError, [ noTransportError ] );
deferred.reject( noTransportError );
// Stop the connection if it has connected and move it into the disconnected state
connection.stop();
return;
}
// The connection was aborted
if ( connection.state === signalR.connectionState.disconnected ) {
return;
}
var transportName = transports[ index ],
transport = signalR.transports[ transportName ],
onFallback = function() {
initialize( transports, index + 1 );
};
connection.transport = transport;
try {
connection._.initHandler.start( transport, function() { // success
// Firefox 11+ doesn't allow sync XHR withCredentials: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest#withCredentials
var isFirefox11OrGreater = signalR._.firefoxMajorVersion( window.navigator.userAgent ) >= 11,
asyncAbort = !!connection.withCredentials && isFirefox11OrGreater;
connection.log( "The start request succeeded. Transitioning to the connected state." );
if ( supportsKeepAlive( connection ) ) {
signalR.transports._logic.monitorKeepAlive( connection );
}
signalR.transports._logic.startHeartbeat( connection );
// Used to ensure low activity clients maintain their authentication.
// Must be configured once a transport has been decided to perform valid ping requests.
signalR._.configurePingInterval( connection );
if ( !changeState( connection,
signalR.connectionState.connecting,
signalR.connectionState.connected ) ) {
connection.log( "WARNING! The connection was not in the connecting state." );
}
// Drain any incoming buffered messages (messages that came in prior to connect)
connection._.connectingMessageBuffer.drain();
$( connection ).triggerHandler( events.onStart );
// wire the stop handler for when the user leaves the page
_pageWindow.bind( "unload", function() {
connection.log( "Window unloading, stopping the connection." );
connection.stop( asyncAbort );
} );
if ( isFirefox11OrGreater ) {
// Firefox does not fire cross-domain XHRs in the normal unload handler on tab close.
// #2400
_pageWindow.bind( "beforeunload", function() {
// If connection.stop() runs runs in beforeunload and fails, it will also fail
// in unload unless connection.stop() runs after a timeout.
window.setTimeout( function() {
connection.stop( asyncAbort );
}, 0 );
} );
}
}, onFallback );
}
catch( error ) {
connection.log( transport.name + " transport threw '" + error.message + "' when attempting to start." );
onFallback();
}
};
var url = connection.url + "/negotiate",
onFailed = function( error, connection ) {
var err = signalR._.error( resources.errorOnNegotiate, error, connection._.negotiateRequest );
$( connection ).triggerHandler( events.onError, err );
deferred.reject( err );
// Stop the connection if negotiate failed
connection.stop();
};
$( connection ).triggerHandler( events.onStarting );
url = signalR.transports._logic.prepareQueryString( connection, url );
connection.log( "Negotiating with '" + url + "'." );
// Save the ajax negotiate request object so we can abort it if stop is called while the request is in flight.
connection._.negotiateRequest = signalR.transports._logic.ajax( connection, {
url: url,
error: function( error, statusText ) {
// We don't want to cause any errors if we're aborting our own negotiate request.
if ( statusText !== _negotiateAbortText ) {
onFailed( error, connection );
} else {
// This rejection will noop if the deferred has already been resolved or rejected.
deferred.reject( signalR._.error( resources.stoppedWhileNegotiating, null /* error */, connection._.negotiateRequest ) );
}
},
success: function( result ) {
var res,
keepAliveData,
protocolError,
transports = [],
supportedTransports = [];
try {
res = connection._parseResponse( result );
} catch( error ) {
onFailed( signalR._.error( resources.errorParsingNegotiateResponse, error ), connection );
return;
}
keepAliveData = connection._.keepAliveData;
connection.appRelativeUrl = res.Url;
connection.id = res.ConnectionId;
connection.token = res.ConnectionToken;
connection.webSocketServerUrl = res.WebSocketServerUrl;
// The long poll timeout is the ConnectionTimeout plus 10 seconds
connection._.pollTimeout = res.ConnectionTimeout * 1000 + 10000; // in ms
// Once the server has labeled the PersistentConnection as Disconnected, we should stop attempting to reconnect
// after res.DisconnectTimeout seconds.
connection.disconnectTimeout = res.DisconnectTimeout * 1000; // in ms
// Add the TransportConnectTimeout from the response to the transportConnectTimeout from the client to calculate the total timeout
connection._.totalTransportConnectTimeout = connection.transportConnectTimeout + res.TransportConnectTimeout * 1000;
// If we have a keep alive
if ( res.KeepAliveTimeout ) {
// Register the keep alive data as activated
keepAliveData.activated = true;
// Timeout to designate when to force the connection into reconnecting converted to milliseconds
keepAliveData.timeout = res.KeepAliveTimeout * 1000;
// Timeout to designate when to warn the developer that the connection may be dead or is not responding.
keepAliveData.timeoutWarning = keepAliveData.timeout * connection.keepAliveWarnAt;
// Instantiate the frequency in which we check the keep alive. It must be short in order to not miss/pick up any changes
connection._.beatInterval = (keepAliveData.timeout - keepAliveData.timeoutWarning) / 3;
} else {
keepAliveData.activated = false;
}
connection.reconnectWindow = connection.disconnectTimeout + (keepAliveData.timeout || 0);
if ( !res.ProtocolVersion || res.ProtocolVersion !== connection.clientProtocol ) {
protocolError = signalR._.error( signalR._.format( resources.protocolIncompatible, connection.clientProtocol, res.ProtocolVersion ) );
$( connection ).triggerHandler( events.onError, [ protocolError ] );
deferred.reject( protocolError );
return;
}
$.each( signalR.transports, function( key ) {
if ( (key.indexOf( "_" ) === 0) || (key === "webSockets" && !res.TryWebSockets) ) {
return true;
}
supportedTransports.push( key );
} );
if ( $.isArray( config.transport ) ) {
$.each( config.transport, function( _, transport ) {
if ( $.inArray( transport, supportedTransports ) >= 0 ) {
transports.push( transport );
}
} );
} else if ( config.transport === "auto" ) {
transports = supportedTransports;
} else if ( $.inArray( config.transport, supportedTransports ) >= 0 ) {
transports.push( config.transport );
}
initialize( transports );
}
} );
return deferred.promise();
},
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 the connection is fully instantiated.</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, errorData, sendData ) {
connection.lastError = errorData;
// In practice 'errorData' is the SignalR built error object.
// In practice 'sendData' is undefined for all error events except those triggered by
// 'ajaxSend' and 'webSockets.send'.'sendData' is the original send payload.
callback.call( connection, errorData, sendData );
} );
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,
// Save deferral because this is always cleaned up
deferral = connection._deferral;
// Verify that we've bound a load event.
if ( connection._.deferredStartHandler ) {
// Unbind the event.
_pageWindow.unbind( "load", connection._.deferredStartHandler );
}
// Always clean up private non-timeout based state.
delete connection._.config;
delete connection._.deferredStartHandler;
// This needs to be checked despite the connection state because a connection start can be deferred until page load.
// If we've deferred the start due to a page load we need to unbind the "onLoad" -> start event.
if ( !_pageLoaded && (!connection._.config || connection._.config.waitForPageLoad === true) ) {
connection.log( "Stopping connection prior to negotiate." );
// If we have a deferral we should reject it
if ( deferral ) {
deferral.reject( signalR._.error( resources.stoppedWhileLoading ) );
}
// Short-circuit because the start has not been fully started.
return;
}
if ( connection.state === signalR.connectionState.disconnected ) {
return;
}
connection.log( "Stopping connection." );
changeState( connection, connection.state, signalR.connectionState.disconnected );
// Clear this no matter what
window.clearTimeout( connection._.beatHandle );
window.clearInterval( connection._.pingIntervalId );
if ( connection.transport ) {
connection.transport.stop( connection );
if ( notifyServer !== false ) {
connection.transport.abort( connection, async );
}
if ( supportsKeepAlive( connection ) ) {
signalR.transports._logic.stopMonitoringKeepAlive( connection );
}
connection.transport = null;
}
if ( connection._.negotiateRequest ) {
// If the negotiation request has already completed this will noop.
connection._.negotiateRequest.abort( _negotiateAbortText );
delete connection._.negotiateRequest;
}
// Ensure that initHandler.stop() is called before connection._deferral is deleted
if ( connection._.initHandler ) {
connection._.initHandler.stop();
}
// Trigger the disconnect event
$( connection ).triggerHandler( events.onDisconnect );
delete connection._deferral;
delete connection.messageId;
delete connection.groupsToken;
delete connection.id;
delete connection._.pingIntervalId;
delete connection._.lastMessageAt;
delete connection._.lastActiveAt;
// Clear out our message buffer
connection._.connectingMessageBuffer.clear();
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, undefined ) {
var signalR = $.signalR,
events = $.signalR.events,
changeState = $.signalR.changeState,
startAbortText = "__Start Aborted__",
transportLogic;
signalR.transports = {};
function beat( connection ) {
if ( connection._.keepAliveData.monitoring ) {
checkIfAlive( connection );
}
// Ensure that we successfully marked active before continuing the heartbeat.
if ( transportLogic.markActive( connection ) ) {
connection._.beatHandle = window.setTimeout( function() {
beat( connection );
}, connection._.beatInterval );
}
}
function checkIfAlive( connection ) {
var keepAliveData = connection._.keepAliveData,
timeElapsed;
// Only check if we're connected
if ( connection.state === signalR.connectionState.connected ) {
timeElapsed = new Date().getTime() - connection._.lastMessageAt;
// 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;
}
}
}
function getAjaxUrl( connection, path ) {
var url = connection.url + path;
if ( connection.transport ) {
url += "?transport=" + connection.transport.name;
}
return transportLogic.prepareQueryString( connection, url );
}
function InitHandler( connection ) {
this.connection = connection;
this.startRequested = false;
this.startCompleted = false;
this.connectionStopped = false;
}
InitHandler.prototype = {
start: function( transport, onSuccess, onFallback ) {
var that = this,
connection = that.connection,
failCalled = false;
if ( that.startRequested || that.connectionStopped ) {
connection.log( "WARNING! " + transport.name + " transport cannot be started. Initialization ongoing or completed." );
return;
}
connection.log( transport.name + " transport starting." );
that.transportTimeoutHandle = window.setTimeout( function() {
if ( !failCalled ) {
failCalled = true;
connection.log( transport.name + " transport timed out when trying to connect." );
that.transportFailed( transport, undefined, onFallback );
}
}, connection._.totalTransportConnectTimeout );
transport.start( connection, function() {
if ( !failCalled ) {
that.initReceived( transport, onSuccess );
}
}, function( error ) {
// Don't allow the same transport to cause onFallback to be called twice
if ( !failCalled ) {
failCalled = true;
that.transportFailed( transport, error, onFallback );
}
// Returns true if the transport should stop;
// false if it should attempt to reconnect
return !that.startCompleted || that.connectionStopped;
} );
},
stop: function() {
this.connectionStopped = true;
window.clearTimeout( this.transportTimeoutHandle );
signalR.transports._logic.tryAbortStartRequest( this.connection );
},
initReceived: function( transport, onSuccess ) {
var that = this,
connection = that.connection;
if ( that.startRequested ) {
connection.log( "WARNING! The client received multiple init messages." );
return;
}
if ( that.connectionStopped ) {
return;
}
that.startRequested = true;
window.clearTimeout( that.transportTimeoutHandle );
connection.log( transport.name + " transport connected. Initiating start request." );
signalR.transports._logic.ajaxStart( connection, function() {
that.startCompleted = true;
onSuccess();
} );
},
transportFailed: function( transport, error, onFallback ) {
var connection = this.connection,
deferred = connection._deferral,
wrappedError;
if ( this.connectionStopped ) {
return;
}
window.clearTimeout( this.transportTimeoutHandle );
if ( !this.startRequested ) {
transport.stop( connection );
connection.log( transport.name + " transport failed to connect. Attempting to fall back." );
onFallback();
} else if ( !this.startCompleted ) {