프로젝트

일반

사용자정보

통계
| 브랜치(Branch): | 개정판:

markus / MarkusLogview / SignalREngineServiceWindowsService / Scripts / jquery.signalR-2.2.2.js @ 84578b97

이력 | 보기 | 이력해설 | 다운로드 (121 KB)

1
/* jquery.signalR.core.js */
2
/*global window:false */
3
/*!
4
 * ASP.NET SignalR JavaScript Library v2.2.2
5
 * http://signalr.net/
6
 *
7
 * Copyright (c) .NET Foundation. All rights reserved.
8
 * Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
9
 *
10
 */
11

    
12
/// <reference path="Scripts/jquery-1.6.4.js" />
13
/// <reference path="jquery.signalR.version.js" />
14
(function ($, window, undefined) {
15

    
16
    var resources = {
17
        nojQuery: "jQuery was not found. Please ensure jQuery is referenced before the SignalR client JavaScript file.",
18
        noTransportOnInit: "No transport could be initialized successfully. Try specifying a different transport or none at all for auto initialization.",
19
        errorOnNegotiate: "Error during negotiation request.",
20
        stoppedWhileLoading: "The connection was stopped during page load.",
21
        stoppedWhileNegotiating: "The connection was stopped during the negotiate request.",
22
        errorParsingNegotiateResponse: "Error parsing negotiate response.",
23
        errorDuringStartRequest: "Error during start request. Stopping the connection.",
24
        stoppedDuringStartRequest: "The connection was stopped during the start request.",
25
        errorParsingStartResponse: "Error parsing start response: '{0}'. Stopping the connection.",
26
        invalidStartResponse: "Invalid start response: '{0}'. Stopping the connection.",
27
        protocolIncompatible: "You are using a version of the client that isn't compatible with the server. Client version {0}, server version {1}.",
28
        sendFailed: "Send failed.",
29
        parseFailed: "Failed at parsing response: {0}",
30
        longPollFailed: "Long polling request failed.",
31
        eventSourceFailedToConnect: "EventSource failed to connect.",
32
        eventSourceError: "Error raised by EventSource",
33
        webSocketClosed: "WebSocket closed.",
34
        pingServerFailedInvalidResponse: "Invalid ping response when pinging server: '{0}'.",
35
        pingServerFailed: "Failed to ping server.",
36
        pingServerFailedStatusCode: "Failed to ping server.  Server responded with status code {0}, stopping the connection.",
37
        pingServerFailedParse: "Failed to parse ping server response, stopping the connection.",
38
        noConnectionTransport: "Connection is in an invalid state, there is no transport active.",
39
        webSocketsInvalidState: "The Web Socket transport is in an invalid state, transitioning into reconnecting.",
40
        reconnectTimeout: "Couldn't reconnect within the configured timeout of {0} ms, disconnecting.",
41
        reconnectWindowTimeout: "The client has been inactive since {0} and it has exceeded the inactivity timeout of {1} ms. Stopping the connection."
42
    };
43

    
44
    if (typeof ($) !== "function") {
45
        // no jQuery!
46
        throw new Error(resources.nojQuery);
47
    }
48

    
49
    var signalR,
50
        _connection,
51
        _pageLoaded = (window.document.readyState === "complete"),
52
        _pageWindow = $(window),
53
        _negotiateAbortText = "__Negotiate Aborted__",
54
        events = {
55
            onStart: "onStart",
56
            onStarting: "onStarting",
57
            onReceived: "onReceived",
58
            onError: "onError",
59
            onConnectionSlow: "onConnectionSlow",
60
            onReconnecting: "onReconnecting",
61
            onReconnect: "onReconnect",
62
            onStateChanged: "onStateChanged",
63
            onDisconnect: "onDisconnect"
64
        },
65
        ajaxDefaults = {
66
            processData: true,
67
            timeout: null,
68
            async: true,
69
            global: false,
70
            cache: false
71
        },
72
        log = function (msg, logging) {
73
            if (logging === false) {
74
                return;
75
            }
76
            var m;
77
            if (typeof (window.console) === "undefined") {
78
                return;
79
            }
80
            m = "[" + new Date().toTimeString() + "] SignalR: " + msg;
81
            if (window.console.debug) {
82
                window.console.debug(m);
83
            } else if (window.console.log) {
84
                window.console.log(m);
85
            }
86
        },
87

    
88
        changeState = function (connection, expectedState, newState) {
89
            if (expectedState === connection.state) {
90
                connection.state = newState;
91

    
92
                $(connection).triggerHandler(events.onStateChanged, [{ oldState: expectedState, newState: newState }]);
93
                return true;
94
            }
95

    
96
            return false;
97
        },
98

    
99
        isDisconnecting = function (connection) {
100
            return connection.state === signalR.connectionState.disconnected;
101
        },
102

    
103
        supportsKeepAlive = function (connection) {
104
            return connection._.keepAliveData.activated &&
105
                   connection.transport.supportsKeepAlive(connection);
106
        },
107

    
108
        configureStopReconnectingTimeout = function (connection) {
109
            var stopReconnectingTimeout,
110
                onReconnectTimeout;
111

    
112
            // Check if this connection has already been configured to stop reconnecting after a specified timeout.
113
            // Without this check if a connection is stopped then started events will be bound multiple times.
114
            if (!connection._.configuredStopReconnectingTimeout) {
115
                onReconnectTimeout = function (connection) {
116
                    var message = signalR._.format(signalR.resources.reconnectTimeout, connection.disconnectTimeout);
117
                    connection.log(message);
118
                    $(connection).triggerHandler(events.onError, [signalR._.error(message, /* source */ "TimeoutException")]);
119
                    connection.stop(/* async */ false, /* notifyServer */ false);
120
                };
121

    
122
                connection.reconnecting(function () {
123
                    var connection = this;
124

    
125
                    // Guard against state changing in a previous user defined even handler
126
                    if (connection.state === signalR.connectionState.reconnecting) {
127
                        stopReconnectingTimeout = window.setTimeout(function () { onReconnectTimeout(connection); }, connection.disconnectTimeout);
128
                    }
129
                });
130

    
131
                connection.stateChanged(function (data) {
132
                    if (data.oldState === signalR.connectionState.reconnecting) {
133
                        // Clear the pending reconnect timeout check
134
                        window.clearTimeout(stopReconnectingTimeout);
135
                    }
136
                });
137

    
138
                connection._.configuredStopReconnectingTimeout = true;
139
            }
140
        };
141

    
142
    signalR = function (url, qs, logging) {
143
        /// <summary>Creates a new SignalR connection for the given url</summary>
144
        /// <param name="url" type="String">The URL of the long polling endpoint</param>
145
        /// <param name="qs" type="Object">
146
        ///     [Optional] Custom querystring parameters to add to the connection URL.
147
        ///     If an object, every non-function member will be added to the querystring.
148
        ///     If a string, it's added to the QS as specified.
149
        /// </param>
150
        /// <param name="logging" type="Boolean">
151
        ///     [Optional] A flag indicating whether connection logging is enabled to the browser
152
        ///     console/log. Defaults to false.
153
        /// </param>
154

    
155
        return new signalR.fn.init(url, qs, logging);
156
    };
157

    
158
    signalR._ = {
159
        defaultContentType: "application/x-www-form-urlencoded; charset=UTF-8",
160

    
161
        ieVersion: (function () {
162
            var version,
163
                matches;
164

    
165
            if (window.navigator.appName === 'Microsoft Internet Explorer') {
166
                // Check if the user agent has the pattern "MSIE (one or more numbers).(one or more numbers)";
167
                matches = /MSIE ([0-9]+\.[0-9]+)/.exec(window.navigator.userAgent);
168

    
169
                if (matches) {
170
                    version = window.parseFloat(matches[1]);
171
                }
172
            }
173

    
174
            // undefined value means not IE
175
            return version;
176
        })(),
177

    
178
        error: function (message, source, context) {
179
            var e = new Error(message);
180
            e.source = source;
181

    
182
            if (typeof context !== "undefined") {
183
                e.context = context;
184
            }
185

    
186
            return e;
187
        },
188

    
189
        transportError: function (message, transport, source, context) {
190
            var e = this.error(message, source, context);
191
            e.transport = transport ? transport.name : undefined;
192
            return e;
193
        },
194

    
195
        format: function () {
196
            /// <summary>Usage: format("Hi {0}, you are {1}!", "Foo", 100) </summary>
197
            var s = arguments[0];
198
            for (var i = 0; i < arguments.length - 1; i++) {
199
                s = s.replace("{" + i + "}", arguments[i + 1]);
200
            }
201
            return s;
202
        },
203

    
204
        firefoxMajorVersion: function (userAgent) {
205
            // Firefox user agents: http://useragentstring.com/pages/Firefox/
206
            var matches = userAgent.match(/Firefox\/(\d+)/);
207
            if (!matches || !matches.length || matches.length < 2) {
208
                return 0;
209
            }
210
            return parseInt(matches[1], 10 /* radix */);
211
        },
212

    
213
        configurePingInterval: function (connection) {
214
            var config = connection._.config,
215
                onFail = function (error) {
216
                    $(connection).triggerHandler(events.onError, [error]);
217
                };
218

    
219
            if (config && !connection._.pingIntervalId && config.pingInterval) {
220
                connection._.pingIntervalId = window.setInterval(function () {
221
                    signalR.transports._logic.pingServer(connection).fail(onFail);
222
                }, config.pingInterval);
223
            }
224
        }
225
    };
226

    
227
    signalR.events = events;
228

    
229
    signalR.resources = resources;
230

    
231
    signalR.ajaxDefaults = ajaxDefaults;
232

    
233
    signalR.changeState = changeState;
234

    
235
    signalR.isDisconnecting = isDisconnecting;
236

    
237
    signalR.connectionState = {
238
        connecting: 0,
239
        connected: 1,
240
        reconnecting: 2,
241
        disconnected: 4
242
    };
243

    
244
    signalR.hub = {
245
        start: function () {
246
            // This will get replaced with the real hub connection start method when hubs is referenced correctly
247
            throw new Error("SignalR: Error loading hubs. Ensure your hubs reference is correct, e.g. <script src='/signalr/js'></script>.");
248
        }
249
    };
250

    
251
    // .on() was added in version 1.7.0, .load() was removed in version 3.0.0 so we fallback to .load() if .on() does
252
    // not exist to not break existing applications
253
    if (typeof _pageWindow.on == "function") {
254
        _pageWindow.on("load", function () { _pageLoaded = true; });
255
    }
256
    else {
257
        _pageWindow.load(function () { _pageLoaded = true; });
258
    }
259

    
260
    function validateTransport(requestedTransport, connection) {
261
        /// <summary>Validates the requested transport by cross checking it with the pre-defined signalR.transports</summary>
262
        /// <param name="requestedTransport" type="Object">The designated transports that the user has specified.</param>
263
        /// <param name="connection" type="signalR">The connection that will be using the requested transports.  Used for logging purposes.</param>
264
        /// <returns type="Object" />
265

    
266
        if ($.isArray(requestedTransport)) {
267
            // Go through transport array and remove an "invalid" tranports
268
            for (var i = requestedTransport.length - 1; i >= 0; i--) {
269
                var transport = requestedTransport[i];
270
                if ($.type(transport) !== "string" || !signalR.transports[transport]) {
271
                    connection.log("Invalid transport: " + transport + ", removing it from the transports list.");
272
                    requestedTransport.splice(i, 1);
273
                }
274
            }
275

    
276
            // Verify we still have transports left, if we dont then we have invalid transports
277
            if (requestedTransport.length === 0) {
278
                connection.log("No transports remain within the specified transport array.");
279
                requestedTransport = null;
280
            }
281
        } else if (!signalR.transports[requestedTransport] && requestedTransport !== "auto") {
282
            connection.log("Invalid transport: " + requestedTransport.toString() + ".");
283
            requestedTransport = null;
284
        } else if (requestedTransport === "auto" && signalR._.ieVersion <= 8) {
285
            // If we're doing an auto transport and we're IE8 then force longPolling, #1764
286
            return ["longPolling"];
287

    
288
        }
289

    
290
        return requestedTransport;
291
    }
292

    
293
    function getDefaultPort(protocol) {
294
        if (protocol === "http:") {
295
            return 80;
296
        } else if (protocol === "https:") {
297
            return 443;
298
        }
299
    }
300

    
301
    function addDefaultPort(protocol, url) {
302
        // Remove ports  from url.  We have to check if there's a / or end of line
303
        // following the port in order to avoid removing ports such as 8080.
304
        if (url.match(/:\d+$/)) {
305
            return url;
306
        } else {
307
            return url + ":" + getDefaultPort(protocol);
308
        }
309
    }
310

    
311
    function ConnectingMessageBuffer(connection, drainCallback) {
312
        var that = this,
313
            buffer = [];
314

    
315
        that.tryBuffer = function (message) {
316
            if (connection.state === $.signalR.connectionState.connecting) {
317
                buffer.push(message);
318

    
319
                return true;
320
            }
321

    
322
            return false;
323
        };
324

    
325
        that.drain = function () {
326
            // Ensure that the connection is connected when we drain (do not want to drain while a connection is not active)
327
            if (connection.state === $.signalR.connectionState.connected) {
328
                while (buffer.length > 0) {
329
                    drainCallback(buffer.shift());
330
                }
331
            }
332
        };
333

    
334
        that.clear = function () {
335
            buffer = [];
336
        };
337
    }
338

    
339
    signalR.fn = signalR.prototype = {
340
        init: function (url, qs, logging) {
341
            var $connection = $(this);
342

    
343
            this.url = url;
344
            this.qs = qs;
345
            this.lastError = null;
346
            this._ = {
347
                keepAliveData: {},
348
                connectingMessageBuffer: new ConnectingMessageBuffer(this, function (message) {
349
                    $connection.triggerHandler(events.onReceived, [message]);
350
                }),
351
                lastMessageAt: new Date().getTime(),
352
                lastActiveAt: new Date().getTime(),
353
                beatInterval: 5000, // Default value, will only be overridden if keep alive is enabled,
354
                beatHandle: null,
355
                totalTransportConnectTimeout: 0 // This will be the sum of the TransportConnectTimeout sent in response to negotiate and connection.transportConnectTimeout
356
            };
357
            if (typeof (logging) === "boolean") {
358
                this.logging = logging;
359
            }
360
        },
361

    
362
        _parseResponse: function (response) {
363
            var that = this;
364

    
365
            if (!response) {
366
                return response;
367
            } else if (typeof response === "string") {
368
                return that.json.parse(response);
369
            } else {
370
                return response;
371
            }
372
        },
373

    
374
        _originalJson: window.JSON,
375

    
376
        json: window.JSON,
377

    
378
        isCrossDomain: function (url, against) {
379
            /// <summary>Checks if url is cross domain</summary>
380
            /// <param name="url" type="String">The base URL</param>
381
            /// <param name="against" type="Object">
382
            ///     An optional argument to compare the URL against, if not specified it will be set to window.location.
383
            ///     If specified it must contain a protocol and a host property.
384
            /// </param>
385
            var link;
386

    
387
            url = $.trim(url);
388

    
389
            against = against || window.location;
390

    
391
            if (url.indexOf("http") !== 0) {
392
                return false;
393
            }
394

    
395
            // Create an anchor tag.
396
            link = window.document.createElement("a");
397
            link.href = url;
398

    
399
            // When checking for cross domain we have to special case port 80 because the window.location will remove the
400
            return link.protocol + addDefaultPort(link.protocol, link.host) !== against.protocol + addDefaultPort(against.protocol, against.host);
401
        },
402

    
403
        ajaxDataType: "text",
404

    
405
        contentType: "application/json; charset=UTF-8",
406

    
407
        logging: false,
408

    
409
        state: signalR.connectionState.disconnected,
410

    
411
        clientProtocol: "1.5",
412

    
413
        reconnectDelay: 2000,
414

    
415
        transportConnectTimeout: 0,
416

    
417
        disconnectTimeout: 30000, // This should be set by the server in response to the negotiate request (30s default)
418

    
419
        reconnectWindow: 30000, // This should be set by the server in response to the negotiate request
420

    
421
        keepAliveWarnAt: 2 / 3, // Warn user of slow connection if we breach the X% mark of the keep alive timeout
422

    
423
        start: function (options, callback) {
424
            /// <summary>Starts the connection</summary>
425
            /// <param name="options" type="Object">Options map</param>
426
            /// <param name="callback" type="Function">A callback function to execute when the connection has started</param>
427
            var connection = this,
428
                config = {
429
                    pingInterval: 300000,
430
                    waitForPageLoad: true,
431
                    transport: "auto",
432
                    jsonp: false
433
                },
434
                initialize,
435
                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
436
                parser = window.document.createElement("a");
437

    
438
            connection.lastError = null;
439

    
440
            // Persist the deferral so that if start is called multiple times the same deferral is used.
441
            connection._deferral = deferred;
442

    
443
            if (!connection.json) {
444
                // no JSON!
445
                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.");
446
            }
447

    
448
            if ($.type(options) === "function") {
449
                // Support calling with single callback parameter
450
                callback = options;
451
            } else if ($.type(options) === "object") {
452
                $.extend(config, options);
453
                if ($.type(config.callback) === "function") {
454
                    callback = config.callback;
455
                }
456
            }
457

    
458
            config.transport = validateTransport(config.transport, connection);
459

    
460
            // If the transport is invalid throw an error and abort start
461
            if (!config.transport) {
462
                throw new Error("SignalR: Invalid transport(s) specified, aborting start.");
463
            }
464

    
465
            connection._.config = config;
466

    
467
            // Check to see if start is being called prior to page load
468
            // If waitForPageLoad is true we then want to re-direct function call to the window load event
469
            if (!_pageLoaded && config.waitForPageLoad === true) {
470
                connection._.deferredStartHandler = function () {
471
                    connection.start(options, callback);
472
                };
473
                _pageWindow.bind("load", connection._.deferredStartHandler);
474

    
475
                return deferred.promise();
476
            }
477

    
478
            // If we're already connecting just return the same deferral as the original connection start
479
            if (connection.state === signalR.connectionState.connecting) {
480
                return deferred.promise();
481
            } else if (changeState(connection,
482
                            signalR.connectionState.disconnected,
483
                            signalR.connectionState.connecting) === false) {
484
                // We're not connecting so try and transition into connecting.
485
                // If we fail to transition then we're either in connected or reconnecting.
486

    
487
                deferred.resolve(connection);
488
                return deferred.promise();
489
            }
490

    
491
            configureStopReconnectingTimeout(connection);
492

    
493
            // Resolve the full url
494
            parser.href = connection.url;
495
            if (!parser.protocol || parser.protocol === ":") {
496
                connection.protocol = window.document.location.protocol;
497
                connection.host = parser.host || window.document.location.host;
498
            } else {
499
                connection.protocol = parser.protocol;
500
                connection.host = parser.host;
501
            }
502

    
503
            connection.baseUrl = connection.protocol + "//" + connection.host;
504

    
505
            // Set the websocket protocol
506
            connection.wsProtocol = connection.protocol === "https:" ? "wss://" : "ws://";
507

    
508
            // If jsonp with no/auto transport is specified, then set the transport to long polling
509
            // since that is the only transport for which jsonp really makes sense.
510
            // Some developers might actually choose to specify jsonp for same origin requests
511
            // as demonstrated by Issue #623.
512
            if (config.transport === "auto" && config.jsonp === true) {
513
                config.transport = "longPolling";
514
            }
515

    
516
            // If the url is protocol relative, prepend the current windows protocol to the url.
517
            if (connection.url.indexOf("//") === 0) {
518
                connection.url = window.location.protocol + connection.url;
519
                connection.log("Protocol relative URL detected, normalizing it to '" + connection.url + "'.");
520
            }
521

    
522
            if (this.isCrossDomain(connection.url)) {
523
                connection.log("Auto detected cross domain url.");
524

    
525
                if (config.transport === "auto") {
526
                    // TODO: Support XDM with foreverFrame
527
                    config.transport = ["webSockets", "serverSentEvents", "longPolling"];
528
                }
529

    
530
                if (typeof (config.withCredentials) === "undefined") {
531
                    config.withCredentials = true;
532
                }
533

    
534
                // Determine if jsonp is the only choice for negotiation, ajaxSend and ajaxAbort.
535
                // i.e. if the browser doesn't supports CORS
536
                // If it is, ignore any preference to the contrary, and switch to jsonp.
537
                if (!config.jsonp) {
538
                    config.jsonp = !$.support.cors;
539

    
540
                    if (config.jsonp) {
541
                        connection.log("Using jsonp because this browser doesn't support CORS.");
542
                    }
543
                }
544

    
545
                connection.contentType = signalR._.defaultContentType;
546
            }
547

    
548
            connection.withCredentials = config.withCredentials;
549

    
550
            connection.ajaxDataType = config.jsonp ? "jsonp" : "text";
551

    
552
            $(connection).bind(events.onStart, function (e, data) {
553
                if ($.type(callback) === "function") {
554
                    callback.call(connection);
555
                }
556
                deferred.resolve(connection);
557
            });
558

    
559
            connection._.initHandler = signalR.transports._logic.initHandler(connection);
560

    
561
            initialize = function (transports, index) {
562
                var noTransportError = signalR._.error(resources.noTransportOnInit);
563

    
564
                index = index || 0;
565
                if (index >= transports.length) {
566
                    if (index === 0) {
567
                        connection.log("No transports supported by the server were selected.");
568
                    } else if (index === 1) {
569
                        connection.log("No fallback transports were selected.");
570
                    } else {
571
                        connection.log("Fallback transports exhausted.");
572
                    }
573

    
574
                    // No transport initialized successfully
575
                    $(connection).triggerHandler(events.onError, [noTransportError]);
576
                    deferred.reject(noTransportError);
577
                    // Stop the connection if it has connected and move it into the disconnected state
578
                    connection.stop();
579
                    return;
580
                }
581

    
582
                // The connection was aborted
583
                if (connection.state === signalR.connectionState.disconnected) {
584
                    return;
585
                }
586

    
587
                var transportName = transports[index],
588
                    transport = signalR.transports[transportName],
589
                    onFallback = function () {
590
                        initialize(transports, index + 1);
591
                    };
592

    
593
                connection.transport = transport;
594

    
595
                try {
596
                    connection._.initHandler.start(transport, function () { // success
597
                        // Firefox 11+ doesn't allow sync XHR withCredentials: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest#withCredentials
598
                        var isFirefox11OrGreater = signalR._.firefoxMajorVersion(window.navigator.userAgent) >= 11,
599
                            asyncAbort = !!connection.withCredentials && isFirefox11OrGreater;
600

    
601
                        connection.log("The start request succeeded. Transitioning to the connected state.");
602

    
603
                        if (supportsKeepAlive(connection)) {
604
                            signalR.transports._logic.monitorKeepAlive(connection);
605
                        }
606

    
607
                        signalR.transports._logic.startHeartbeat(connection);
608

    
609
                        // Used to ensure low activity clients maintain their authentication.
610
                        // Must be configured once a transport has been decided to perform valid ping requests.
611
                        signalR._.configurePingInterval(connection);
612

    
613
                        if (!changeState(connection,
614
                                            signalR.connectionState.connecting,
615
                                            signalR.connectionState.connected)) {
616
                            connection.log("WARNING! The connection was not in the connecting state.");
617
                        }
618

    
619
                        // Drain any incoming buffered messages (messages that came in prior to connect)
620
                        connection._.connectingMessageBuffer.drain();
621

    
622
                        $(connection).triggerHandler(events.onStart);
623

    
624
                        // wire the stop handler for when the user leaves the page
625
                        _pageWindow.bind("unload", function () {
626
                            connection.log("Window unloading, stopping the connection.");
627

    
628
                            connection.stop(asyncAbort);
629
                        });
630

    
631
                        if (isFirefox11OrGreater) {
632
                            // Firefox does not fire cross-domain XHRs in the normal unload handler on tab close.
633
                            // #2400
634
                            _pageWindow.bind("beforeunload", function () {
635
                                // If connection.stop() runs runs in beforeunload and fails, it will also fail
636
                                // in unload unless connection.stop() runs after a timeout.
637
                                window.setTimeout(function () {
638
                                    connection.stop(asyncAbort);
639
                                }, 0);
640
                            });
641
                        }
642
                    }, onFallback);
643
                }
644
                catch (error) {
645
                    connection.log(transport.name + " transport threw '" + error.message + "' when attempting to start.");
646
                    onFallback();
647
                }
648
            };
649

    
650
            var url = connection.url + "/negotiate",
651
                onFailed = function (error, connection) {
652
                    var err = signalR._.error(resources.errorOnNegotiate, error, connection._.negotiateRequest);
653

    
654
                    $(connection).triggerHandler(events.onError, err);
655
                    deferred.reject(err);
656
                    // Stop the connection if negotiate failed
657
                    connection.stop();
658
                };
659

    
660
            $(connection).triggerHandler(events.onStarting);
661

    
662
            url = signalR.transports._logic.prepareQueryString(connection, url);
663

    
664
            connection.log("Negotiating with '" + url + "'.");
665

    
666
            // Save the ajax negotiate request object so we can abort it if stop is called while the request is in flight.
667
            connection._.negotiateRequest = signalR.transports._logic.ajax(connection, {
668
                url: url,
669
                error: function (error, statusText) {
670
                    // We don't want to cause any errors if we're aborting our own negotiate request.
671
                    if (statusText !== _negotiateAbortText) {
672
                        onFailed(error, connection);
673
                    } else {
674
                        // This rejection will noop if the deferred has already been resolved or rejected.
675
                        deferred.reject(signalR._.error(resources.stoppedWhileNegotiating, null /* error */, connection._.negotiateRequest));
676
                    }
677
                },
678
                success: function (result) {
679
                    var res,
680
                        keepAliveData,
681
                        protocolError,
682
                        transports = [],
683
                        supportedTransports = [];
684

    
685
                    try {
686
                        res = connection._parseResponse(result);
687
                    } catch (error) {
688
                        onFailed(signalR._.error(resources.errorParsingNegotiateResponse, error), connection);
689
                        return;
690
                    }
691

    
692
                    keepAliveData = connection._.keepAliveData;
693
                    connection.appRelativeUrl = res.Url;
694
                    connection.id = res.ConnectionId;
695
                    connection.token = res.ConnectionToken;
696
                    connection.webSocketServerUrl = res.WebSocketServerUrl;
697

    
698
                    // The long poll timeout is the ConnectionTimeout plus 10 seconds
699
                    connection._.pollTimeout = res.ConnectionTimeout * 1000 + 10000; // in ms
700

    
701
                    // Once the server has labeled the PersistentConnection as Disconnected, we should stop attempting to reconnect
702
                    // after res.DisconnectTimeout seconds.
703
                    connection.disconnectTimeout = res.DisconnectTimeout * 1000; // in ms
704

    
705
                    // Add the TransportConnectTimeout from the response to the transportConnectTimeout from the client to calculate the total timeout
706
                    connection._.totalTransportConnectTimeout = connection.transportConnectTimeout + res.TransportConnectTimeout * 1000;
707

    
708
                    // If we have a keep alive
709
                    if (res.KeepAliveTimeout) {
710
                        // Register the keep alive data as activated
711
                        keepAliveData.activated = true;
712

    
713
                        // Timeout to designate when to force the connection into reconnecting converted to milliseconds
714
                        keepAliveData.timeout = res.KeepAliveTimeout * 1000;
715

    
716
                        // Timeout to designate when to warn the developer that the connection may be dead or is not responding.
717
                        keepAliveData.timeoutWarning = keepAliveData.timeout * connection.keepAliveWarnAt;
718

    
719
                        // Instantiate the frequency in which we check the keep alive.  It must be short in order to not miss/pick up any changes
720
                        connection._.beatInterval = (keepAliveData.timeout - keepAliveData.timeoutWarning) / 3;
721
                    } else {
722
                        keepAliveData.activated = false;
723
                    }
724

    
725
                    connection.reconnectWindow = connection.disconnectTimeout + (keepAliveData.timeout || 0);
726

    
727
                    if (!res.ProtocolVersion || res.ProtocolVersion !== connection.clientProtocol) {
728
                        protocolError = signalR._.error(signalR._.format(resources.protocolIncompatible, connection.clientProtocol, res.ProtocolVersion));
729
                        $(connection).triggerHandler(events.onError, [protocolError]);
730
                        deferred.reject(protocolError);
731

    
732
                        return;
733
                    }
734

    
735
                    $.each(signalR.transports, function (key) {
736
                        if ((key.indexOf("_") === 0) || (key === "webSockets" && !res.TryWebSockets)) {
737
                            return true;
738
                        }
739
                        supportedTransports.push(key);
740
                    });
741

    
742
                    if ($.isArray(config.transport)) {
743
                        $.each(config.transport, function (_, transport) {
744
                            if ($.inArray(transport, supportedTransports) >= 0) {
745
                                transports.push(transport);
746
                            }
747
                        });
748
                    } else if (config.transport === "auto") {
749
                        transports = supportedTransports;
750
                    } else if ($.inArray(config.transport, supportedTransports) >= 0) {
751
                        transports.push(config.transport);
752
                    }
753

    
754
                    initialize(transports);
755
                }
756
            });
757

    
758
            return deferred.promise();
759
        },
760

    
761
        starting: function (callback) {
762
            /// <summary>Adds a callback that will be invoked before anything is sent over the connection</summary>
763
            /// <param name="callback" type="Function">A callback function to execute before the connection is fully instantiated.</param>
764
            /// <returns type="signalR" />
765
            var connection = this;
766
            $(connection).bind(events.onStarting, function (e, data) {
767
                callback.call(connection);
768
            });
769
            return connection;
770
        },
771

    
772
        send: function (data) {
773
            /// <summary>Sends data over the connection</summary>
774
            /// <param name="data" type="String">The data to send over the connection</param>
775
            /// <returns type="signalR" />
776
            var connection = this;
777

    
778
            if (connection.state === signalR.connectionState.disconnected) {
779
                // Connection hasn't been started yet
780
                throw new Error("SignalR: Connection must be started before data can be sent. Call .start() before .send()");
781
            }
782

    
783
            if (connection.state === signalR.connectionState.connecting) {
784
                // Connection hasn't been started yet
785
                throw new Error("SignalR: Connection has not been fully initialized. Use .start().done() or .start().fail() to run logic after the connection has started.");
786
            }
787

    
788
            connection.transport.send(connection, data);
789
            // REVIEW: Should we return deferred here?
790
            return connection;
791
        },
792

    
793
        received: function (callback) {
794
            /// <summary>Adds a callback that will be invoked after anything is received over the connection</summary>
795
            /// <param name="callback" type="Function">A callback function to execute when any data is received on the connection</param>
796
            /// <returns type="signalR" />
797
            var connection = this;
798
            $(connection).bind(events.onReceived, function (e, data) {
799
                callback.call(connection, data);
800
            });
801
            return connection;
802
        },
803

    
804
        stateChanged: function (callback) {
805
            /// <summary>Adds a callback that will be invoked when the connection state changes</summary>
806
            /// <param name="callback" type="Function">A callback function to execute when the connection state changes</param>
807
            /// <returns type="signalR" />
808
            var connection = this;
809
            $(connection).bind(events.onStateChanged, function (e, data) {
810
                callback.call(connection, data);
811
            });
812
            return connection;
813
        },
814

    
815
        error: function (callback) {
816
            /// <summary>Adds a callback that will be invoked after an error occurs with the connection</summary>
817
            /// <param name="callback" type="Function">A callback function to execute when an error occurs on the connection</param>
818
            /// <returns type="signalR" />
819
            var connection = this;
820
            $(connection).bind(events.onError, function (e, errorData, sendData) {
821
                connection.lastError = errorData;
822
                // In practice 'errorData' is the SignalR built error object.
823
                // In practice 'sendData' is undefined for all error events except those triggered by
824
                // 'ajaxSend' and 'webSockets.send'.'sendData' is the original send payload.
825
                callback.call(connection, errorData, sendData);
826
            });
827
            return connection;
828
        },
829

    
830
        disconnected: function (callback) {
831
            /// <summary>Adds a callback that will be invoked when the client disconnects</summary>
832
            /// <param name="callback" type="Function">A callback function to execute when the connection is broken</param>
833
            /// <returns type="signalR" />
834
            var connection = this;
835
            $(connection).bind(events.onDisconnect, function (e, data) {
836
                callback.call(connection);
837
            });
838
            return connection;
839
        },
840

    
841
        connectionSlow: function (callback) {
842
            /// <summary>Adds a callback that will be invoked when the client detects a slow connection</summary>
843
            /// <param name="callback" type="Function">A callback function to execute when the connection is slow</param>
844
            /// <returns type="signalR" />
845
            var connection = this;
846
            $(connection).bind(events.onConnectionSlow, function (e, data) {
847
                callback.call(connection);
848
            });
849

    
850
            return connection;
851
        },
852

    
853
        reconnecting: function (callback) {
854
            /// <summary>Adds a callback that will be invoked when the underlying transport begins reconnecting</summary>
855
            /// <param name="callback" type="Function">A callback function to execute when the connection enters a reconnecting state</param>
856
            /// <returns type="signalR" />
857
            var connection = this;
858
            $(connection).bind(events.onReconnecting, function (e, data) {
859
                callback.call(connection);
860
            });
861
            return connection;
862
        },
863

    
864
        reconnected: function (callback) {
865
            /// <summary>Adds a callback that will be invoked when the underlying transport reconnects</summary>
866
            /// <param name="callback" type="Function">A callback function to execute when the connection is restored</param>
867
            /// <returns type="signalR" />
868
            var connection = this;
869
            $(connection).bind(events.onReconnect, function (e, data) {
870
                callback.call(connection);
871
            });
872
            return connection;
873
        },
874

    
875
        stop: function (async, notifyServer) {
876
            /// <summary>Stops listening</summary>
877
            /// <param name="async" type="Boolean">Whether or not to asynchronously abort the connection</param>
878
            /// <param name="notifyServer" type="Boolean">Whether we want to notify the server that we are aborting the connection</param>
879
            /// <returns type="signalR" />
880
            var connection = this,
881
                // Save deferral because this is always cleaned up
882
                deferral = connection._deferral;
883

    
884
            // Verify that we've bound a load event.
885
            if (connection._.deferredStartHandler) {
886
                // Unbind the event.
887
                _pageWindow.unbind("load", connection._.deferredStartHandler);
888
            }
889

    
890
            // Always clean up private non-timeout based state.
891
            delete connection._.config;
892
            delete connection._.deferredStartHandler;
893

    
894
            // This needs to be checked despite the connection state because a connection start can be deferred until page load.
895
            // If we've deferred the start due to a page load we need to unbind the "onLoad" -> start event.
896
            if (!_pageLoaded && (!connection._.config || connection._.config.waitForPageLoad === true)) {
897
                connection.log("Stopping connection prior to negotiate.");
898

    
899
                // If we have a deferral we should reject it
900
                if (deferral) {
901
                    deferral.reject(signalR._.error(resources.stoppedWhileLoading));
902
                }
903

    
904
                // Short-circuit because the start has not been fully started.
905
                return;
906
            }
907

    
908
            if (connection.state === signalR.connectionState.disconnected) {
909
                return;
910
            }
911

    
912
            connection.log("Stopping connection.");
913

    
914
            // Clear this no matter what
915
            window.clearTimeout(connection._.beatHandle);
916
            window.clearInterval(connection._.pingIntervalId);
917

    
918
            if (connection.transport) {
919
                connection.transport.stop(connection);
920

    
921
                if (notifyServer !== false) {
922
                    connection.transport.abort(connection, async);
923
                }
924

    
925
                if (supportsKeepAlive(connection)) {
926
                    signalR.transports._logic.stopMonitoringKeepAlive(connection);
927
                }
928

    
929
                connection.transport = null;
930
            }
931

    
932
            if (connection._.negotiateRequest) {
933
                // If the negotiation request has already completed this will noop.
934
                connection._.negotiateRequest.abort(_negotiateAbortText);
935
                delete connection._.negotiateRequest;
936
            }
937

    
938
            // Ensure that initHandler.stop() is called before connection._deferral is deleted
939
            if (connection._.initHandler) {
940
                connection._.initHandler.stop();
941
            }
942

    
943
            delete connection._deferral;
944
            delete connection.messageId;
945
            delete connection.groupsToken;
946
            delete connection.id;
947
            delete connection._.pingIntervalId;
948
            delete connection._.lastMessageAt;
949
            delete connection._.lastActiveAt;
950

    
951
            // Clear out our message buffer
952
            connection._.connectingMessageBuffer.clear();
953
            
954
            // Clean up this event
955
            $(connection).unbind(events.onStart);
956

    
957
            // Trigger the disconnect event
958
            changeState(connection, connection.state, signalR.connectionState.disconnected);
959
            $(connection).triggerHandler(events.onDisconnect);
960

    
961
            return connection;
962
        },
963

    
964
        log: function (msg) {
965
            log(msg, this.logging);
966
        }
967
    };
968

    
969
    signalR.fn.init.prototype = signalR.fn;
970

    
971
    signalR.noConflict = function () {
972
        /// <summary>Reinstates the original value of $.connection and returns the signalR object for manual assignment</summary>
973
        /// <returns type="signalR" />
974
        if ($.connection === signalR) {
975
            $.connection = _connection;
976
        }
977
        return signalR;
978
    };
979

    
980
    if ($.connection) {
981
        _connection = $.connection;
982
    }
983

    
984
    $.connection = $.signalR = signalR;
985

    
986
}(window.jQuery, window));
987
/* jquery.signalR.transports.common.js */
988
// Copyright (c) .NET Foundation. All rights reserved.
989
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
990

    
991
/*global window:false */
992
/// <reference path="jquery.signalR.core.js" />
993

    
994
(function ($, window, undefined) {
995

    
996
    var signalR = $.signalR,
997
        events = $.signalR.events,
998
        changeState = $.signalR.changeState,
999
        startAbortText = "__Start Aborted__",
1000
        transportLogic;
1001

    
1002
    signalR.transports = {};
1003

    
1004
    function beat(connection) {
1005
        if (connection._.keepAliveData.monitoring) {
1006
            checkIfAlive(connection);
1007
        }
1008

    
1009
        // Ensure that we successfully marked active before continuing the heartbeat.
1010
        if (transportLogic.markActive(connection)) {
1011
            connection._.beatHandle = window.setTimeout(function () {
1012
                beat(connection);
1013
            }, connection._.beatInterval);
1014
        }
1015
    }
1016

    
1017
    function checkIfAlive(connection) {
1018
        var keepAliveData = connection._.keepAliveData,
1019
            timeElapsed;
1020

    
1021
        // Only check if we're connected
1022
        if (connection.state === signalR.connectionState.connected) {
1023
            timeElapsed = new Date().getTime() - connection._.lastMessageAt;
1024

    
1025
            // Check if the keep alive has completely timed out
1026
            if (timeElapsed >= keepAliveData.timeout) {
1027
                connection.log("Keep alive timed out.  Notifying transport that connection has been lost.");
1028

    
1029
                // Notify transport that the connection has been lost
1030
                connection.transport.lostConnection(connection);
1031
            } else if (timeElapsed >= keepAliveData.timeoutWarning) {
1032
                // This is to assure that the user only gets a single warning
1033
                if (!keepAliveData.userNotified) {
1034
                    connection.log("Keep alive has been missed, connection may be dead/slow.");
1035
                    $(connection).triggerHandler(events.onConnectionSlow);
1036
                    keepAliveData.userNotified = true;
1037
                }
1038
            } else {
1039
                keepAliveData.userNotified = false;
1040
            }
1041
        }
1042
    }
1043

    
1044
    function getAjaxUrl(connection, path) {
1045
        var url = connection.url + path;
1046

    
1047
        if (connection.transport) {
1048
            url += "?transport=" + connection.transport.name;
1049
        }
1050

    
1051
        return transportLogic.prepareQueryString(connection, url);
1052
    }
1053

    
1054
    function InitHandler(connection) {
1055
        this.connection = connection;
1056

    
1057
        this.startRequested = false;
1058
        this.startCompleted = false;
1059
        this.connectionStopped = false;
1060
    }
1061

    
1062
    InitHandler.prototype = {
1063
        start: function (transport, onSuccess, onFallback) {
1064
            var that = this,
1065
                connection = that.connection,
1066
                failCalled = false;
1067

    
1068
            if (that.startRequested || that.connectionStopped) {
1069
                connection.log("WARNING! " + transport.name + " transport cannot be started. Initialization ongoing or completed.");
1070
                return;
1071
            }
1072

    
1073
            connection.log(transport.name + " transport starting.");
1074

    
1075
            transport.start(connection, function () {
1076
                if (!failCalled) {
1077
                    that.initReceived(transport, onSuccess);
1078
                }
1079
            }, function (error) {
1080
                // Don't allow the same transport to cause onFallback to be called twice
1081
                if (!failCalled) {
1082
                    failCalled = true;
1083
                    that.transportFailed(transport, error, onFallback);
1084
                }
1085

    
1086
                // Returns true if the transport should stop;
1087
                // false if it should attempt to reconnect
1088
                return !that.startCompleted || that.connectionStopped;
1089
            });
1090

    
1091
            that.transportTimeoutHandle = window.setTimeout(function () {
1092
                if (!failCalled) {
1093
                    failCalled = true;
1094
                    connection.log(transport.name + " transport timed out when trying to connect.");
1095
                    that.transportFailed(transport, undefined, onFallback);
1096
                }
1097
            }, connection._.totalTransportConnectTimeout);
1098
        },
1099

    
1100
        stop: function () {
1101
            this.connectionStopped = true;
1102
            window.clearTimeout(this.transportTimeoutHandle);
1103
            signalR.transports._logic.tryAbortStartRequest(this.connection);
1104
        },
1105

    
1106
        initReceived: function (transport, onSuccess) {
1107
            var that = this,
1108
                connection = that.connection;
1109

    
1110
            if (that.startRequested) {
1111
                connection.log("WARNING! The client received multiple init messages.");
1112
                return;
1113
            }
1114

    
1115
            if (that.connectionStopped) {
1116
                return;
1117
            }
1118

    
1119
            that.startRequested = true;
1120
            window.clearTimeout(that.transportTimeoutHandle);
1121

    
1122
            connection.log(transport.name + " transport connected. Initiating start request.");
1123
            signalR.transports._logic.ajaxStart(connection, function () {
1124
                that.startCompleted = true;
1125
                onSuccess();
1126
            });
1127
        },
1128

    
1129
        transportFailed: function (transport, error, onFallback) {
1130
            var connection = this.connection,
1131
                deferred = connection._deferral,
1132
                wrappedError;
1133

    
1134
            if (this.connectionStopped) {
1135
                return;
1136
            }
1137

    
1138
            window.clearTimeout(this.transportTimeoutHandle);
1139

    
1140
            if (!this.startRequested) {
1141
                transport.stop(connection);
1142

    
1143
                connection.log(transport.name + " transport failed to connect. Attempting to fall back.");
1144
                onFallback();
1145
            } else if (!this.startCompleted) {
1146
                // Do not attempt to fall back if a start request is ongoing during a transport failure.
1147
                // Instead, trigger an error and stop the connection.
1148
                wrappedError = signalR._.error(signalR.resources.errorDuringStartRequest, error);
1149

    
1150
                connection.log(transport.name + " transport failed during the start request. Stopping the connection.");
1151
                $(connection).triggerHandler(events.onError, [wrappedError]);
1152
                if (deferred) {
1153
                    deferred.reject(wrappedError);
1154
                }
1155

    
1156
                connection.stop();
1157
            } else {
1158
                // The start request has completed, but the connection has not stopped.
1159
                // No need to do anything here. The transport should attempt its normal reconnect logic.
1160
            }
1161
        }
1162
    };
1163

    
1164
    transportLogic = signalR.transports._logic = {
1165
        ajax: function (connection, options) {
1166
            return $.ajax(
1167
                $.extend(/*deep copy*/ true, {}, $.signalR.ajaxDefaults, {
1168
                    type: "GET",
1169
                    data: {},
1170
                    xhrFields: { withCredentials: connection.withCredentials },
1171
                    contentType: connection.contentType,
1172
                    dataType: connection.ajaxDataType
1173
                }, options));
1174
        },
1175

    
1176
        pingServer: function (connection) {
1177
            /// <summary>Pings the server</summary>
1178
            /// <param name="connection" type="signalr">Connection associated with the server ping</param>
1179
            /// <returns type="signalR" />
1180
            var url,
1181
                xhr,
1182
                deferral = $.Deferred();
1183

    
1184
            if (connection.transport) {
1185
                url = connection.url + "/ping";
1186

    
1187
                url = transportLogic.addQs(url, connection.qs);
1188

    
1189
                xhr = transportLogic.ajax(connection, {
1190
                    url: url,
1191
                    success: function (result) {
1192
                        var data;
1193

    
1194
                        try {
1195
                            data = connection._parseResponse(result);
1196
                        }
1197
                        catch (error) {
1198
                            deferral.reject(
1199
                                signalR._.transportError(
1200
                                    signalR.resources.pingServerFailedParse,
1201
                                    connection.transport,
1202
                                    error,
1203
                                    xhr
1204
                                )
1205
                            );
1206
                            connection.stop();
1207
                            return;
1208
                        }
1209

    
1210
                        if (data.Response === "pong") {
1211
                            deferral.resolve();
1212
                        }
1213
                        else {
1214
                            deferral.reject(
1215
                                signalR._.transportError(
1216
                                    signalR._.format(signalR.resources.pingServerFailedInvalidResponse, result),
1217
                                    connection.transport,
1218
                                    null /* error */,
1219
                                    xhr
1220
                                )
1221
                            );
1222
                        }
1223
                    },
1224
                    error: function (error) {
1225
                        if (error.status === 401 || error.status === 403) {
1226
                            deferral.reject(
1227
                                signalR._.transportError(
1228
                                    signalR._.format(signalR.resources.pingServerFailedStatusCode, error.status),
1229
                                    connection.transport,
1230
                                    error,
1231
                                    xhr
1232
                                )
1233
                            );
1234
                            connection.stop();
1235
                        }
1236
                        else {
1237
                            deferral.reject(
1238
                                signalR._.transportError(
1239
                                    signalR.resources.pingServerFailed,
1240
                                    connection.transport,
1241
                                    error,
1242
                                    xhr
1243
                                )
1244
                            );
1245
                        }
1246
                    }
1247
                });
1248
            }
1249
            else {
1250
                deferral.reject(
1251
                    signalR._.transportError(
1252
                        signalR.resources.noConnectionTransport,
1253
                        connection.transport
1254
                    )
1255
                );
1256
            }
1257

    
1258
            return deferral.promise();
1259
        },
1260

    
1261
        prepareQueryString: function (connection, url) {
1262
            var preparedUrl;
1263

    
1264
            // Use addQs to start since it handles the ?/& prefix for us
1265
            preparedUrl = transportLogic.addQs(url, "clientProtocol=" + connection.clientProtocol);
1266

    
1267
            // Add the user-specified query string params if any
1268
            preparedUrl = transportLogic.addQs(preparedUrl, connection.qs);
1269

    
1270
            if (connection.token) {
1271
                preparedUrl += "&connectionToken=" + window.encodeURIComponent(connection.token);
1272
            }
1273

    
1274
            if (connection.data) {
1275
                preparedUrl += "&connectionData=" + window.encodeURIComponent(connection.data);
1276
            }
1277

    
1278
            return preparedUrl;
1279
        },
1280

    
1281
        addQs: function (url, qs) {
1282
            var appender = url.indexOf("?") !== -1 ? "&" : "?",
1283
                firstChar;
1284

    
1285
            if (!qs) {
1286
                return url;
1287
            }
1288

    
1289
            if (typeof (qs) === "object") {
1290
                return url + appender + $.param(qs);
1291
            }
1292

    
1293
            if (typeof (qs) === "string") {
1294
                firstChar = qs.charAt(0);
1295

    
1296
                if (firstChar === "?" || firstChar === "&") {
1297
                    appender = "";
1298
                }
1299

    
1300
                return url + appender + qs;
1301
            }
1302

    
1303
            throw new Error("Query string property must be either a string or object.");
1304
        },
1305

    
1306
        // BUG #2953: The url needs to be same otherwise it will cause a memory leak
1307
        getUrl: function (connection, transport, reconnecting, poll, ajaxPost) {
1308
            /// <summary>Gets the url for making a GET based connect request</summary>
1309
            var baseUrl = transport === "webSockets" ? "" : connection.baseUrl,
1310
                url = baseUrl + connection.appRelativeUrl,
1311
                qs = "transport=" + transport;
1312

    
1313
            if (!ajaxPost && connection.groupsToken) {
1314
                qs += "&groupsToken=" + window.encodeURIComponent(connection.groupsToken);
1315
            }
1316

    
1317
            if (!reconnecting) {
1318
                url += "/connect";
1319
            } else {
1320
                if (poll) {
1321
                    // longPolling transport specific
1322
                    url += "/poll";
1323
                } else {
1324
                    url += "/reconnect";
1325
                }
1326

    
1327
                if (!ajaxPost && connection.messageId) {
1328
                    qs += "&messageId=" + window.encodeURIComponent(connection.messageId);
1329
                }
1330
            }
1331
            url += "?" + qs;
1332
            url = transportLogic.prepareQueryString(connection, url);
1333

    
1334
            if (!ajaxPost) {
1335
                url += "&tid=" + Math.floor(Math.random() * 11);
1336
            }
1337

    
1338
            return url;
1339
        },
1340

    
1341
        maximizePersistentResponse: function (minPersistentResponse) {
1342
            return {
1343
                MessageId: minPersistentResponse.C,
1344
                Messages: minPersistentResponse.M,
1345
                Initialized: typeof (minPersistentResponse.S) !== "undefined" ? true : false,
1346
                ShouldReconnect: typeof (minPersistentResponse.T) !== "undefined" ? true : false,
1347
                LongPollDelay: minPersistentResponse.L,
1348
                GroupsToken: minPersistentResponse.G
1349
            };
1350
        },
1351

    
1352
        updateGroups: function (connection, groupsToken) {
1353
            if (groupsToken) {
1354
                connection.groupsToken = groupsToken;
1355
            }
1356
        },
1357

    
1358
        stringifySend: function (connection, message) {
1359
            if (typeof (message) === "string" || typeof (message) === "undefined" || message === null) {
1360
                return message;
1361
            }
1362
            return connection.json.stringify(message);
1363
        },
1364

    
1365
        ajaxSend: function (connection, data) {
1366
            var payload = transportLogic.stringifySend(connection, data),
1367
                url = getAjaxUrl(connection, "/send"),
1368
                xhr,
1369
                onFail = function (error, connection) {
1370
                    $(connection).triggerHandler(events.onError, [signalR._.transportError(signalR.resources.sendFailed, connection.transport, error, xhr), data]);
1371
                };
1372

    
1373

    
1374
            xhr = transportLogic.ajax(connection, {
1375
                url: url,
1376
                type: connection.ajaxDataType === "jsonp" ? "GET" : "POST",
1377
                contentType: signalR._.defaultContentType,
1378
                data: {
1379
                    data: payload
1380
                },
1381
                success: function (result) {
1382
                    var res;
1383

    
1384
                    if (result) {
1385
                        try {
1386
                            res = connection._parseResponse(result);
1387
                        }
1388
                        catch (error) {
1389
                            onFail(error, connection);
1390
                            connection.stop();
1391
                            return;
1392
                        }
1393

    
1394
                        transportLogic.triggerReceived(connection, res);
1395
                    }
1396
                },
1397
                error: function (error, textStatus) {
1398
                    if (textStatus === "abort" || textStatus === "parsererror") {
1399
                        // The parsererror happens for sends that don't return any data, and hence
1400
                        // don't write the jsonp callback to the response. This is harder to fix on the server
1401
                        // so just hack around it on the client for now.
1402
                        return;
1403
                    }
1404

    
1405
                    onFail(error, connection);
1406
                }
1407
            });
1408

    
1409
            return xhr;
1410
        },
1411

    
1412
        ajaxAbort: function (connection, async) {
1413
            if (typeof (connection.transport) === "undefined") {
1414
                return;
1415
            }
1416

    
1417
            // Async by default unless explicitly overidden
1418
            async = typeof async === "undefined" ? true : async;
1419

    
1420
            var url = getAjaxUrl(connection, "/abort");
1421

    
1422
            transportLogic.ajax(connection, {
1423
                url: url,
1424
                async: async,
1425
                timeout: 1000,
1426
                type: "POST"
1427
            });
1428

    
1429
            connection.log("Fired ajax abort async = " + async + ".");
1430
        },
1431

    
1432
        ajaxStart: function (connection, onSuccess) {
1433
            var rejectDeferred = function (error) {
1434
                    var deferred = connection._deferral;
1435
                    if (deferred) {
1436
                        deferred.reject(error);
1437
                    }
1438
                },
1439
                triggerStartError = function (error) {
1440
                    connection.log("The start request failed. Stopping the connection.");
1441
                    $(connection).triggerHandler(events.onError, [error]);
1442
                    rejectDeferred(error);
1443
                    connection.stop();
1444
                };
1445

    
1446
            connection._.startRequest = transportLogic.ajax(connection, {
1447
                url: getAjaxUrl(connection, "/start"),
1448
                success: function (result, statusText, xhr) {
1449
                    var data;
1450

    
1451
                    try {
1452
                        data = connection._parseResponse(result);
1453
                    } catch (error) {
1454
                        triggerStartError(signalR._.error(
1455
                            signalR._.format(signalR.resources.errorParsingStartResponse, result),
1456
                            error, xhr));
1457
                        return;
1458
                    }
1459

    
1460
                    if (data.Response === "started") {
1461
                        onSuccess();
1462
                    } else {
1463
                        triggerStartError(signalR._.error(
1464
                            signalR._.format(signalR.resources.invalidStartResponse, result),
1465
                            null /* error */, xhr));
1466
                    }
1467
                },
1468
                error: function (xhr, statusText, error) {
1469
                    if (statusText !== startAbortText) {
1470
                        triggerStartError(signalR._.error(
1471
                            signalR.resources.errorDuringStartRequest,
1472
                            error, xhr));
1473
                    } else {
1474
                        // Stop has been called, no need to trigger the error handler
1475
                        // or stop the connection again with onStartError
1476
                        connection.log("The start request aborted because connection.stop() was called.");
1477
                        rejectDeferred(signalR._.error(
1478
                            signalR.resources.stoppedDuringStartRequest,
1479
                            null /* error */, xhr));
1480
                    }
1481
                }
1482
            });
1483
        },
1484

    
1485
        tryAbortStartRequest: function (connection) {
1486
            if (connection._.startRequest) {
1487
                // If the start request has already completed this will noop.
1488
                connection._.startRequest.abort(startAbortText);
1489
                delete connection._.startRequest;
1490
            }
1491
        },
1492

    
1493
        tryInitialize: function (connection, persistentResponse, onInitialized) {
1494
            if (persistentResponse.Initialized && onInitialized) {
1495
                onInitialized();
1496
            } else if (persistentResponse.Initialized) {
1497
                connection.log("WARNING! The client received an init message after reconnecting.");
1498
            }
1499

    
1500
        },
1501

    
1502
        triggerReceived: function (connection, data) {
1503
            if (!connection._.connectingMessageBuffer.tryBuffer(data)) {
1504
                $(connection).triggerHandler(events.onReceived, [data]);
1505
            }
1506
        },
1507

    
1508
        processMessages: function (connection, minData, onInitialized) {
1509
            var data;
1510

    
1511
            // Update the last message time stamp
1512
            transportLogic.markLastMessage(connection);
1513

    
1514
            if (minData) {
1515
                data = transportLogic.maximizePersistentResponse(minData);
1516

    
1517
                transportLogic.updateGroups(connection, data.GroupsToken);
1518

    
1519
                if (data.MessageId) {
1520
                    connection.messageId = data.MessageId;
1521
                }
1522

    
1523
                if (data.Messages) {
1524
                    $.each(data.Messages, function (index, message) {
1525
                        transportLogic.triggerReceived(connection, message);
1526
                    });
1527

    
1528
                    transportLogic.tryInitialize(connection, data, onInitialized);
1529
                }
1530
            }
1531
        },
1532

    
1533
        monitorKeepAlive: function (connection) {
1534
            var keepAliveData = connection._.keepAliveData;
1535

    
1536
            // If we haven't initiated the keep alive timeouts then we need to
1537
            if (!keepAliveData.monitoring) {
1538
                keepAliveData.monitoring = true;
1539

    
1540
                transportLogic.markLastMessage(connection);
1541

    
1542
                // Save the function so we can unbind it on stop
1543
                connection._.keepAliveData.reconnectKeepAliveUpdate = function () {
1544
                    // Mark a new message so that keep alive doesn't time out connections
1545
                    transportLogic.markLastMessage(connection);
1546
                };
1547

    
1548
                // Update Keep alive on reconnect
1549
                $(connection).bind(events.onReconnect, connection._.keepAliveData.reconnectKeepAliveUpdate);
1550

    
1551
                connection.log("Now monitoring keep alive with a warning timeout of " + keepAliveData.timeoutWarning + ", keep alive timeout of " + keepAliveData.timeout + " and disconnecting timeout of " + connection.disconnectTimeout);
1552
            } else {
1553
                connection.log("Tried to monitor keep alive but it's already being monitored.");
1554
            }
1555
        },
1556

    
1557
        stopMonitoringKeepAlive: function (connection) {
1558
            var keepAliveData = connection._.keepAliveData;
1559

    
1560
            // Only attempt to stop the keep alive monitoring if its being monitored
1561
            if (keepAliveData.monitoring) {
1562
                // Stop monitoring
1563
                keepAliveData.monitoring = false;
1564

    
1565
                // Remove the updateKeepAlive function from the reconnect event
1566
                $(connection).unbind(events.onReconnect, connection._.keepAliveData.reconnectKeepAliveUpdate);
1567

    
1568
                // Clear all the keep alive data
1569
                connection._.keepAliveData = {};
1570
                connection.log("Stopping the monitoring of the keep alive.");
1571
            }
1572
        },
1573

    
1574
        startHeartbeat: function (connection) {
1575
            connection._.lastActiveAt = new Date().getTime();
1576
            beat(connection);
1577
        },
1578

    
1579
        markLastMessage: function (connection) {
1580
            connection._.lastMessageAt = new Date().getTime();
1581
        },
1582

    
1583
        markActive: function (connection) {
1584
            if (transportLogic.verifyLastActive(connection)) {
1585
                connection._.lastActiveAt = new Date().getTime();
1586
                return true;
1587
            }
1588

    
1589
            return false;
1590
        },
1591

    
1592
        isConnectedOrReconnecting: function (connection) {
1593
            return connection.state === signalR.connectionState.connected ||
1594
                   connection.state === signalR.connectionState.reconnecting;
1595
        },
1596

    
1597
        ensureReconnectingState: function (connection) {
1598
            if (changeState(connection,
1599
                        signalR.connectionState.connected,
1600
                        signalR.connectionState.reconnecting) === true) {
1601
                $(connection).triggerHandler(events.onReconnecting);
1602
            }
1603
            return connection.state === signalR.connectionState.reconnecting;
1604
        },
1605

    
1606
        clearReconnectTimeout: function (connection) {
1607
            if (connection && connection._.reconnectTimeout) {
1608
                window.clearTimeout(connection._.reconnectTimeout);
1609
                delete connection._.reconnectTimeout;
1610
            }
1611
        },
1612

    
1613
        verifyLastActive: function (connection) {
1614
            if (new Date().getTime() - connection._.lastActiveAt >= connection.reconnectWindow) {
1615
                var message = signalR._.format(signalR.resources.reconnectWindowTimeout, new Date(connection._.lastActiveAt), connection.reconnectWindow);
1616
                connection.log(message);
1617
                $(connection).triggerHandler(events.onError, [signalR._.error(message, /* source */ "TimeoutException")]);
1618
                connection.stop(/* async */ false, /* notifyServer */ false);
1619
                return false;
1620
            }
1621

    
1622
            return true;
1623
        },
1624

    
1625
        reconnect: function (connection, transportName) {
1626
            var transport = signalR.transports[transportName];
1627

    
1628
            // We should only set a reconnectTimeout if we are currently connected
1629
            // and a reconnectTimeout isn't already set.
1630
            if (transportLogic.isConnectedOrReconnecting(connection) && !connection._.reconnectTimeout) {
1631
                // Need to verify before the setTimeout occurs because an application sleep could occur during the setTimeout duration.
1632
                if (!transportLogic.verifyLastActive(connection)) {
1633
                    return;
1634
                }
1635

    
1636
                connection._.reconnectTimeout = window.setTimeout(function () {
1637
                    if (!transportLogic.verifyLastActive(connection)) {
1638
                        return;
1639
                    }
1640

    
1641
                    transport.stop(connection);
1642

    
1643
                    if (transportLogic.ensureReconnectingState(connection)) {
1644
                        connection.log(transportName + " reconnecting.");
1645
                        transport.start(connection);
1646
                    }
1647
                }, connection.reconnectDelay);
1648
            }
1649
        },
1650

    
1651
        handleParseFailure: function (connection, result, error, onFailed, context) {
1652
            var wrappedError = signalR._.transportError(
1653
                signalR._.format(signalR.resources.parseFailed, result),
1654
                connection.transport,
1655
                error,
1656
                context);
1657

    
1658
            // If we're in the initialization phase trigger onFailed, otherwise stop the connection.
1659
            if (onFailed && onFailed(wrappedError)) {
1660
                connection.log("Failed to parse server response while attempting to connect.");
1661
            } else {
1662
                $(connection).triggerHandler(events.onError, [wrappedError]);
1663
                connection.stop();
1664
            }
1665
        },
1666

    
1667
        initHandler: function (connection) {
1668
            return new InitHandler(connection);
1669
        },
1670

    
1671
        foreverFrame: {
1672
            count: 0,
1673
            connections: {}
1674
        }
1675
    };
1676

    
1677
}(window.jQuery, window));
1678
/* jquery.signalR.transports.webSockets.js */
1679
// Copyright (c) .NET Foundation. All rights reserved.
1680
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
1681

    
1682

    
1683
/*global window:false */
1684
/// <reference path="jquery.signalR.transports.common.js" />
1685

    
1686
(function ($, window, undefined) {
1687

    
1688
    var signalR = $.signalR,
1689
        events = $.signalR.events,
1690
        changeState = $.signalR.changeState,
1691
        transportLogic = signalR.transports._logic;
1692

    
1693
    signalR.transports.webSockets = {
1694
        name: "webSockets",
1695

    
1696
        supportsKeepAlive: function () {
1697
            return true;
1698
        },
1699

    
1700
        send: function (connection, data) {
1701
            var payload = transportLogic.stringifySend(connection, data);
1702

    
1703
            try {
1704
                connection.socket.send(payload);
1705
            } catch (ex) {
1706
                $(connection).triggerHandler(events.onError,
1707
                    [signalR._.transportError(
1708
                        signalR.resources.webSocketsInvalidState,
1709
                        connection.transport,
1710
                        ex,
1711
                        connection.socket
1712
                    ),
1713
                    data]);
1714
            }
1715
        },
1716

    
1717
        start: function (connection, onSuccess, onFailed) {
1718
            var url,
1719
                opened = false,
1720
                that = this,
1721
                reconnecting = !onSuccess,
1722
                $connection = $(connection);
1723

    
1724
            if (!window.WebSocket) {
1725
                onFailed();
1726
                return;
1727
            }
1728

    
1729
            if (!connection.socket) {
1730
                if (connection.webSocketServerUrl) {
1731
                    url = connection.webSocketServerUrl;
1732
                } else {
1733
                    url = connection.wsProtocol + connection.host;
1734
                }
1735

    
1736
                url += transportLogic.getUrl(connection, this.name, reconnecting);
1737

    
1738
                connection.log("Connecting to websocket endpoint '" + url + "'.");
1739
                connection.socket = new window.WebSocket(url);
1740

    
1741
                connection.socket.onopen = function () {
1742
                    opened = true;
1743
                    connection.log("Websocket opened.");
1744

    
1745
                    transportLogic.clearReconnectTimeout(connection);
1746

    
1747
                    if (changeState(connection,
1748
                                    signalR.connectionState.reconnecting,
1749
                                    signalR.connectionState.connected) === true) {
1750
                        $connection.triggerHandler(events.onReconnect);
1751
                    }
1752
                };
1753

    
1754
                connection.socket.onclose = function (event) {
1755
                    var error;
1756

    
1757
                    // Only handle a socket close if the close is from the current socket.
1758
                    // Sometimes on disconnect the server will push down an onclose event
1759
                    // to an expired socket.
1760

    
1761
                    if (this === connection.socket) {
1762
                        if (opened && typeof event.wasClean !== "undefined" && event.wasClean === false) {
1763
                            // Ideally this would use the websocket.onerror handler (rather than checking wasClean in onclose) but
1764
                            // I found in some circumstances Chrome won't call onerror. This implementation seems to work on all browsers.
1765
                            error = signalR._.transportError(
1766
                                signalR.resources.webSocketClosed,
1767
                                connection.transport,
1768
                                event);
1769

    
1770
                            connection.log("Unclean disconnect from websocket: " + (event.reason || "[no reason given]."));
1771
                        } else {
1772
                            connection.log("Websocket closed.");
1773
                        }
1774

    
1775
                        if (!onFailed || !onFailed(error)) {
1776
                            if (error) {
1777
                                $(connection).triggerHandler(events.onError, [error]);
1778
                            }
1779

    
1780
                            that.reconnect(connection);
1781
                        }
1782
                    }
1783
                };
1784

    
1785
                connection.socket.onmessage = function (event) {
1786
                    var data;
1787

    
1788
                    try {
1789
                        data = connection._parseResponse(event.data);
1790
                    }
1791
                    catch (error) {
1792
                        transportLogic.handleParseFailure(connection, event.data, error, onFailed, event);
1793
                        return;
1794
                    }
1795

    
1796
                    if (data) {
1797
                        // data.M is PersistentResponse.Messages
1798
                        if ($.isEmptyObject(data) || data.M) {
1799
                            transportLogic.processMessages(connection, data, onSuccess);
1800
                        } else {
1801
                            // For websockets we need to trigger onReceived
1802
                            // for callbacks to outgoing hub calls.
1803
                            transportLogic.triggerReceived(connection, data);
1804
                        }
1805
                    }
1806
                };
1807
            }
1808
        },
1809

    
1810
        reconnect: function (connection) {
1811
            transportLogic.reconnect(connection, this.name);
1812
        },
1813

    
1814
        lostConnection: function (connection) {
1815
            this.reconnect(connection);
1816
        },
1817

    
1818
        stop: function (connection) {
1819
            // Don't trigger a reconnect after stopping
1820
            transportLogic.clearReconnectTimeout(connection);
1821

    
1822
            if (connection.socket) {
1823
                connection.log("Closing the Websocket.");
1824
                connection.socket.close();
1825
                connection.socket = null;
1826
            }
1827
        },
1828

    
1829
        abort: function (connection, async) {
1830
            transportLogic.ajaxAbort(connection, async);
1831
        }
1832
    };
1833

    
1834
}(window.jQuery, window));
1835
/* jquery.signalR.transports.serverSentEvents.js */
1836
// Copyright (c) .NET Foundation. All rights reserved.
1837
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
1838

    
1839

    
1840
/*global window:false */
1841
/// <reference path="jquery.signalR.transports.common.js" />
1842

    
1843
(function ($, window, undefined) {
1844

    
1845
    var signalR = $.signalR,
1846
        events = $.signalR.events,
1847
        changeState = $.signalR.changeState,
1848
        transportLogic = signalR.transports._logic,
1849
        clearReconnectAttemptTimeout = function (connection) {
1850
            window.clearTimeout(connection._.reconnectAttemptTimeoutHandle);
1851
            delete connection._.reconnectAttemptTimeoutHandle;
1852
        };
1853

    
1854
    signalR.transports.serverSentEvents = {
1855
        name: "serverSentEvents",
1856

    
1857
        supportsKeepAlive: function () {
1858
            return true;
1859
        },
1860

    
1861
        timeOut: 3000,
1862

    
1863
        start: function (connection, onSuccess, onFailed) {
1864
            var that = this,
1865
                opened = false,
1866
                $connection = $(connection),
1867
                reconnecting = !onSuccess,
1868
                url;
1869

    
1870
            if (connection.eventSource) {
1871
                connection.log("The connection already has an event source. Stopping it.");
1872
                connection.stop();
1873
            }
1874

    
1875
            if (!window.EventSource) {
1876
                if (onFailed) {
1877
                    connection.log("This browser doesn't support SSE.");
1878
                    onFailed();
1879
                }
1880
                return;
1881
            }
1882

    
1883
            url = transportLogic.getUrl(connection, this.name, reconnecting);
1884

    
1885
            try {
1886
                connection.log("Attempting to connect to SSE endpoint '" + url + "'.");
1887
                connection.eventSource = new window.EventSource(url, { withCredentials: connection.withCredentials });
1888
            }
1889
            catch (e) {
1890
                connection.log("EventSource failed trying to connect with error " + e.Message + ".");
1891
                if (onFailed) {
1892
                    // The connection failed, call the failed callback
1893
                    onFailed();
1894
                } else {
1895
                    $connection.triggerHandler(events.onError, [signalR._.transportError(signalR.resources.eventSourceFailedToConnect, connection.transport, e)]);
1896
                    if (reconnecting) {
1897
                        // If we were reconnecting, rather than doing initial connect, then try reconnect again
1898
                        that.reconnect(connection);
1899
                    }
1900
                }
1901
                return;
1902
            }
1903

    
1904
            if (reconnecting) {
1905
                connection._.reconnectAttemptTimeoutHandle = window.setTimeout(function () {
1906
                    if (opened === false) {
1907
                        // If we're reconnecting and the event source is attempting to connect,
1908
                        // don't keep retrying. This causes duplicate connections to spawn.
1909
                        if (connection.eventSource.readyState !== window.EventSource.OPEN) {
1910
                            // If we were reconnecting, rather than doing initial connect, then try reconnect again
1911
                            that.reconnect(connection);
1912
                        }
1913
                    }
1914
                },
1915
                that.timeOut);
1916
            }
1917

    
1918
            connection.eventSource.addEventListener("open", function (e) {
1919
                connection.log("EventSource connected.");
1920

    
1921
                clearReconnectAttemptTimeout(connection);
1922
                transportLogic.clearReconnectTimeout(connection);
1923

    
1924
                if (opened === false) {
1925
                    opened = true;
1926

    
1927
                    if (changeState(connection,
1928
                                         signalR.connectionState.reconnecting,
1929
                                         signalR.connectionState.connected) === true) {
1930
                        $connection.triggerHandler(events.onReconnect);
1931
                    }
1932
                }
1933
            }, false);
1934

    
1935
            connection.eventSource.addEventListener("message", function (e) {
1936
                var res;
1937

    
1938
                // process messages
1939
                if (e.data === "initialized") {
1940
                    return;
1941
                }
1942

    
1943
                try {
1944
                    res = connection._parseResponse(e.data);
1945
                }
1946
                catch (error) {
1947
                    transportLogic.handleParseFailure(connection, e.data, error, onFailed, e);
1948
                    return;
1949
                }
1950

    
1951
                transportLogic.processMessages(connection, res, onSuccess);
1952
            }, false);
1953

    
1954
            connection.eventSource.addEventListener("error", function (e) {
1955
                var error = signalR._.transportError(
1956
                    signalR.resources.eventSourceError,
1957
                    connection.transport,
1958
                    e);
1959

    
1960
                // Only handle an error if the error is from the current Event Source.
1961
                // Sometimes on disconnect the server will push down an error event
1962
                // to an expired Event Source.
1963
                if (this !== connection.eventSource) {
1964
                    return;
1965
                }
1966

    
1967
                if (onFailed && onFailed(error)) {
1968
                    return;
1969
                }
1970

    
1971
                connection.log("EventSource readyState: " + connection.eventSource.readyState + ".");
1972

    
1973
                if (e.eventPhase === window.EventSource.CLOSED) {
1974
                    // We don't use the EventSource's native reconnect function as it
1975
                    // doesn't allow us to change the URL when reconnecting. We need
1976
                    // to change the URL to not include the /connect suffix, and pass
1977
                    // the last message id we received.
1978
                    connection.log("EventSource reconnecting due to the server connection ending.");
1979
                    that.reconnect(connection);
1980
                } else {
1981
                    // connection error
1982
                    connection.log("EventSource error.");
1983
                    $connection.triggerHandler(events.onError, [error]);
1984
                }
1985
            }, false);
1986
        },
1987

    
1988
        reconnect: function (connection) {
1989
            transportLogic.reconnect(connection, this.name);
1990
        },
1991

    
1992
        lostConnection: function (connection) {
1993
            this.reconnect(connection);
1994
        },
1995

    
1996
        send: function (connection, data) {
1997
            transportLogic.ajaxSend(connection, data);
1998
        },
1999

    
2000
        stop: function (connection) {
2001
            // Don't trigger a reconnect after stopping
2002
            clearReconnectAttemptTimeout(connection);
2003
            transportLogic.clearReconnectTimeout(connection);
2004

    
2005
            if (connection && connection.eventSource) {
2006
                connection.log("EventSource calling close().");
2007
                connection.eventSource.close();
2008
                connection.eventSource = null;
2009
                delete connection.eventSource;
2010
            }
2011
        },
2012

    
2013
        abort: function (connection, async) {
2014
            transportLogic.ajaxAbort(connection, async);
2015
        }
2016
    };
2017

    
2018
}(window.jQuery, window));
2019
/* jquery.signalR.transports.foreverFrame.js */
2020
// Copyright (c) .NET Foundation. All rights reserved.
2021
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
2022

    
2023

    
2024
/*global window:false */
2025
/// <reference path="jquery.signalR.transports.common.js" />
2026

    
2027
(function ($, window, undefined) {
2028

    
2029
    var signalR = $.signalR,
2030
        events = $.signalR.events,
2031
        changeState = $.signalR.changeState,
2032
        transportLogic = signalR.transports._logic,
2033
        createFrame = function () {
2034
            var frame = window.document.createElement("iframe");
2035
            frame.setAttribute("style", "position:absolute;top:0;left:0;width:0;height:0;visibility:hidden;");
2036
            return frame;
2037
        },
2038
        // Used to prevent infinite loading icon spins in older versions of ie
2039
        // We build this object inside a closure so we don't pollute the rest of
2040
        // the foreverFrame transport with unnecessary functions/utilities.
2041
        loadPreventer = (function () {
2042
            var loadingFixIntervalId = null,
2043
                loadingFixInterval = 1000,
2044
                attachedTo = 0;
2045

    
2046
            return {
2047
                prevent: function () {
2048
                    // Prevent additional iframe removal procedures from newer browsers
2049
                    if (signalR._.ieVersion <= 8) {
2050
                        // We only ever want to set the interval one time, so on the first attachedTo
2051
                        if (attachedTo === 0) {
2052
                            // Create and destroy iframe every 3 seconds to prevent loading icon, super hacky
2053
                            loadingFixIntervalId = window.setInterval(function () {
2054
                                var tempFrame = createFrame();
2055

    
2056
                                window.document.body.appendChild(tempFrame);
2057
                                window.document.body.removeChild(tempFrame);
2058

    
2059
                                tempFrame = null;
2060
                            }, loadingFixInterval);
2061
                        }
2062

    
2063
                        attachedTo++;
2064
                    }
2065
                },
2066
                cancel: function () {
2067
                    // Only clear the interval if there's only one more object that the loadPreventer is attachedTo
2068
                    if (attachedTo === 1) {
2069
                        window.clearInterval(loadingFixIntervalId);
2070
                    }
2071

    
2072
                    if (attachedTo > 0) {
2073
                        attachedTo--;
2074
                    }
2075
                }
2076
            };
2077
        })();
2078

    
2079
    signalR.transports.foreverFrame = {
2080
        name: "foreverFrame",
2081

    
2082
        supportsKeepAlive: function () {
2083
            return true;
2084
        },
2085

    
2086
        // Added as a value here so we can create tests to verify functionality
2087
        iframeClearThreshold: 50,
2088

    
2089
        start: function (connection, onSuccess, onFailed) {
2090
            var that = this,
2091
                frameId = (transportLogic.foreverFrame.count += 1),
2092
                url,
2093
                frame = createFrame(),
2094
                frameLoadHandler = function () {
2095
                    connection.log("Forever frame iframe finished loading and is no longer receiving messages.");
2096
                    if (!onFailed || !onFailed()) {
2097
                        that.reconnect(connection);
2098
                    }
2099
                };
2100

    
2101
            if (window.EventSource) {
2102
                // If the browser supports SSE, don't use Forever Frame
2103
                if (onFailed) {
2104
                    connection.log("Forever Frame is not supported by SignalR on browsers with SSE support.");
2105
                    onFailed();
2106
                }
2107
                return;
2108
            }
2109

    
2110
            frame.setAttribute("data-signalr-connection-id", connection.id);
2111

    
2112
            // Start preventing loading icon
2113
            // This will only perform work if the loadPreventer is not attached to another connection.
2114
            loadPreventer.prevent();
2115

    
2116
            // Build the url
2117
            url = transportLogic.getUrl(connection, this.name);
2118
            url += "&frameId=" + frameId;
2119

    
2120
            // add frame to the document prior to setting URL to avoid caching issues.
2121
            window.document.documentElement.appendChild(frame);
2122

    
2123
            connection.log("Binding to iframe's load event.");
2124

    
2125
            if (frame.addEventListener) {
2126
                frame.addEventListener("load", frameLoadHandler, false);
2127
            } else if (frame.attachEvent) {
2128
                frame.attachEvent("onload", frameLoadHandler);
2129
            }
2130

    
2131
            frame.src = url;
2132
            transportLogic.foreverFrame.connections[frameId] = connection;
2133

    
2134
            connection.frame = frame;
2135
            connection.frameId = frameId;
2136

    
2137
            if (onSuccess) {
2138
                connection.onSuccess = function () {
2139
                    connection.log("Iframe transport started.");
2140
                    onSuccess();
2141
                };
2142
            }
2143
        },
2144

    
2145
        reconnect: function (connection) {
2146
            var that = this;
2147

    
2148
            // Need to verify connection state and verify before the setTimeout occurs because an application sleep could occur during the setTimeout duration.
2149
            if (transportLogic.isConnectedOrReconnecting(connection) && transportLogic.verifyLastActive(connection)) {
2150
                window.setTimeout(function () {
2151
                    // Verify that we're ok to reconnect.
2152
                    if (!transportLogic.verifyLastActive(connection)) {
2153
                        return;
2154
                    }
2155

    
2156
                    if (connection.frame && transportLogic.ensureReconnectingState(connection)) {
2157
                        var frame = connection.frame,
2158
                            src = transportLogic.getUrl(connection, that.name, true) + "&frameId=" + connection.frameId;
2159
                        connection.log("Updating iframe src to '" + src + "'.");
2160
                        frame.src = src;
2161
                    }
2162
                }, connection.reconnectDelay);
2163
            }
2164
        },
2165

    
2166
        lostConnection: function (connection) {
2167
            this.reconnect(connection);
2168
        },
2169

    
2170
        send: function (connection, data) {
2171
            transportLogic.ajaxSend(connection, data);
2172
        },
2173

    
2174
        receive: function (connection, data) {
2175
            var cw,
2176
                body,
2177
                response;
2178

    
2179
            if (connection.json !== connection._originalJson) {
2180
                // If there's a custom JSON parser configured then serialize the object
2181
                // using the original (browser) JSON parser and then deserialize it using
2182
                // the custom parser (connection._parseResponse does that). This is so we
2183
                // can easily send the response from the server as "raw" JSON but still
2184
                // support custom JSON deserialization in the browser.
2185
                data = connection._originalJson.stringify(data);
2186
            }
2187

    
2188
            response = connection._parseResponse(data);
2189

    
2190
            transportLogic.processMessages(connection, response, connection.onSuccess);
2191

    
2192
            // Protect against connection stopping from a callback trigger within the processMessages above.
2193
            if (connection.state === $.signalR.connectionState.connected) {
2194
                // Delete the script & div elements
2195
                connection.frameMessageCount = (connection.frameMessageCount || 0) + 1;
2196
                if (connection.frameMessageCount > signalR.transports.foreverFrame.iframeClearThreshold) {
2197
                    connection.frameMessageCount = 0;
2198
                    cw = connection.frame.contentWindow || connection.frame.contentDocument;
2199
                    if (cw && cw.document && cw.document.body) {
2200
                        body = cw.document.body;
2201

    
2202
                        // Remove all the child elements from the iframe's body to conserver memory
2203
                        while (body.firstChild) {
2204
                            body.removeChild(body.firstChild);
2205
                        }
2206
                    }
2207
                }
2208
            }
2209
        },
2210

    
2211
        stop: function (connection) {
2212
            var cw = null;
2213

    
2214
            // Stop attempting to prevent loading icon
2215
            loadPreventer.cancel();
2216

    
2217
            if (connection.frame) {
2218
                if (connection.frame.stop) {
2219
                    connection.frame.stop();
2220
                } else {
2221
                    try {
2222
                        cw = connection.frame.contentWindow || connection.frame.contentDocument;
2223
                        if (cw.document && cw.document.execCommand) {
2224
                            cw.document.execCommand("Stop");
2225
                        }
2226
                    }
2227
                    catch (e) {
2228
                        connection.log("Error occurred when stopping foreverFrame transport. Message = " + e.message + ".");
2229
                    }
2230
                }
2231

    
2232
                // Ensure the iframe is where we left it
2233
                if (connection.frame.parentNode === window.document.documentElement) {
2234
                    window.document.documentElement.removeChild(connection.frame);
2235
                }
2236

    
2237
                delete transportLogic.foreverFrame.connections[connection.frameId];
2238
                connection.frame = null;
2239
                connection.frameId = null;
2240
                delete connection.frame;
2241
                delete connection.frameId;
2242
                delete connection.onSuccess;
2243
                delete connection.frameMessageCount;
2244
                connection.log("Stopping forever frame.");
2245
            }
2246
        },
2247

    
2248
        abort: function (connection, async) {
2249
            transportLogic.ajaxAbort(connection, async);
2250
        },
2251

    
2252
        getConnection: function (id) {
2253
            return transportLogic.foreverFrame.connections[id];
2254
        },
2255

    
2256
        started: function (connection) {
2257
            if (changeState(connection,
2258
                signalR.connectionState.reconnecting,
2259
                signalR.connectionState.connected) === true) {
2260

    
2261
                $(connection).triggerHandler(events.onReconnect);
2262
            }
2263
        }
2264
    };
2265

    
2266
}(window.jQuery, window));
2267
/* jquery.signalR.transports.longPolling.js */
2268
// Copyright (c) .NET Foundation. All rights reserved.
2269
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
2270

    
2271

    
2272
/*global window:false */
2273
/// <reference path="jquery.signalR.transports.common.js" />
2274

    
2275
(function ($, window, undefined) {
2276

    
2277
    var signalR = $.signalR,
2278
        events = $.signalR.events,
2279
        changeState = $.signalR.changeState,
2280
        isDisconnecting = $.signalR.isDisconnecting,
2281
        transportLogic = signalR.transports._logic;
2282

    
2283
    signalR.transports.longPolling = {
2284
        name: "longPolling",
2285

    
2286
        supportsKeepAlive: function () {
2287
            return false;
2288
        },
2289

    
2290
        reconnectDelay: 3000,
2291

    
2292
        start: function (connection, onSuccess, onFailed) {
2293
            /// <summary>Starts the long polling connection</summary>
2294
            /// <param name="connection" type="signalR">The SignalR connection to start</param>
2295
            var that = this,
2296
                fireConnect = function () {
2297
                    fireConnect = $.noop;
2298

    
2299
                    connection.log("LongPolling connected.");
2300

    
2301
                    if (onSuccess) {
2302
                        onSuccess();
2303
                    } else {
2304
                        connection.log("WARNING! The client received an init message after reconnecting.");
2305
                    }
2306
                },
2307
                tryFailConnect = function (error) {
2308
                    if (onFailed(error)) {
2309
                        connection.log("LongPolling failed to connect.");
2310
                        return true;
2311
                    }
2312

    
2313
                    return false;
2314
                },
2315
                privateData = connection._,
2316
                reconnectErrors = 0,
2317
                fireReconnected = function (instance) {
2318
                    window.clearTimeout(privateData.reconnectTimeoutId);
2319
                    privateData.reconnectTimeoutId = null;
2320

    
2321
                    if (changeState(instance,
2322
                                    signalR.connectionState.reconnecting,
2323
                                    signalR.connectionState.connected) === true) {
2324
                        // Successfully reconnected!
2325
                        instance.log("Raising the reconnect event");
2326
                        $(instance).triggerHandler(events.onReconnect);
2327
                    }
2328
                },
2329
                // 1 hour
2330
                maxFireReconnectedTimeout = 3600000;
2331

    
2332
            if (connection.pollXhr) {
2333
                connection.log("Polling xhr requests already exists, aborting.");
2334
                connection.stop();
2335
            }
2336

    
2337
            connection.messageId = null;
2338

    
2339
            privateData.reconnectTimeoutId = null;
2340

    
2341
            privateData.pollTimeoutId = window.setTimeout(function () {
2342
                (function poll(instance, raiseReconnect) {
2343
                    var messageId = instance.messageId,
2344
                        connect = (messageId === null),
2345
                        reconnecting = !connect,
2346
                        polling = !raiseReconnect,
2347
                        url = transportLogic.getUrl(instance, that.name, reconnecting, polling, true /* use Post for longPolling */),
2348
                        postData = {};
2349

    
2350
                    if (instance.messageId) {
2351
                        postData.messageId = instance.messageId;
2352
                    }
2353

    
2354
                    if (instance.groupsToken) {
2355
                        postData.groupsToken = instance.groupsToken;
2356
                    }
2357

    
2358
                    // If we've disconnected during the time we've tried to re-instantiate the poll then stop.
2359
                    if (isDisconnecting(instance) === true) {
2360
                        return;
2361
                    }
2362

    
2363
                    connection.log("Opening long polling request to '" + url + "'.");
2364
                    instance.pollXhr = transportLogic.ajax(connection, {
2365
                        xhrFields: {
2366
                            onprogress: function () {
2367
                                transportLogic.markLastMessage(connection);
2368
                            }
2369
                        },
2370
                        url: url,
2371
                        type: "POST",
2372
                        contentType: signalR._.defaultContentType,
2373
                        data: postData,
2374
                        timeout: connection._.pollTimeout,
2375
                        success: function (result) {
2376
                            var minData,
2377
                                delay = 0,
2378
                                data,
2379
                                shouldReconnect;
2380

    
2381
                            connection.log("Long poll complete.");
2382

    
2383
                            // Reset our reconnect errors so if we transition into a reconnecting state again we trigger
2384
                            // reconnected quickly
2385
                            reconnectErrors = 0;
2386

    
2387
                            try {
2388
                                // Remove any keep-alives from the beginning of the result
2389
                                minData = connection._parseResponse(result);
2390
                            }
2391
                            catch (error) {
2392
                                transportLogic.handleParseFailure(instance, result, error, tryFailConnect, instance.pollXhr);
2393
                                return;
2394
                            }
2395

    
2396
                            // If there's currently a timeout to trigger reconnect, fire it now before processing messages
2397
                            if (privateData.reconnectTimeoutId !== null) {
2398
                                fireReconnected(instance);
2399
                            }
2400

    
2401
                            if (minData) {
2402
                                data = transportLogic.maximizePersistentResponse(minData);
2403
                            }
2404

    
2405
                            transportLogic.processMessages(instance, minData, fireConnect);
2406

    
2407
                            if (data &&
2408
                                $.type(data.LongPollDelay) === "number") {
2409
                                delay = data.LongPollDelay;
2410
                            }
2411

    
2412
                            if (isDisconnecting(instance) === true) {
2413
                                return;
2414
                            }
2415

    
2416
                            shouldReconnect = data && data.ShouldReconnect;
2417
                            if (shouldReconnect) {
2418
                                // Transition into the reconnecting state
2419
                                // If this fails then that means that the user transitioned the connection into a invalid state in processMessages.
2420
                                if (!transportLogic.ensureReconnectingState(instance)) {
2421
                                    return;
2422
                                }
2423
                            }
2424

    
2425
                            // We never want to pass a raiseReconnect flag after a successful poll.  This is handled via the error function
2426
                            if (delay > 0) {
2427
                                privateData.pollTimeoutId = window.setTimeout(function () {
2428
                                    poll(instance, shouldReconnect);
2429
                                }, delay);
2430
                            } else {
2431
                                poll(instance, shouldReconnect);
2432
                            }
2433
                        },
2434

    
2435
                        error: function (data, textStatus) {
2436
                            var error = signalR._.transportError(signalR.resources.longPollFailed, connection.transport, data, instance.pollXhr);
2437

    
2438
                            // Stop trying to trigger reconnect, connection is in an error state
2439
                            // If we're not in the reconnect state this will noop
2440
                            window.clearTimeout(privateData.reconnectTimeoutId);
2441
                            privateData.reconnectTimeoutId = null;
2442

    
2443
                            if (textStatus === "abort") {
2444
                                connection.log("Aborted xhr request.");
2445
                                return;
2446
                            }
2447

    
2448
                            if (!tryFailConnect(error)) {
2449

    
2450
                                // Increment our reconnect errors, we assume all errors to be reconnect errors
2451
                                // In the case that it's our first error this will cause Reconnect to be fired
2452
                                // after 1 second due to reconnectErrors being = 1.
2453
                                reconnectErrors++;
2454

    
2455
                                if (connection.state !== signalR.connectionState.reconnecting) {
2456
                                    connection.log("An error occurred using longPolling. Status = " + textStatus + ".  Response = " + data.responseText + ".");
2457
                                    $(instance).triggerHandler(events.onError, [error]);
2458
                                }
2459

    
2460
                                // We check the state here to verify that we're not in an invalid state prior to verifying Reconnect.
2461
                                // If we're not in connected or reconnecting then the next ensureReconnectingState check will fail and will return.
2462
                                // Therefore we don't want to change that failure code path.
2463
                                if ((connection.state === signalR.connectionState.connected ||
2464
                                    connection.state === signalR.connectionState.reconnecting) &&
2465
                                    !transportLogic.verifyLastActive(connection)) {
2466
                                    return;
2467
                                }
2468

    
2469
                                // Transition into the reconnecting state
2470
                                // If this fails then that means that the user transitioned the connection into the disconnected or connecting state within the above error handler trigger.
2471
                                if (!transportLogic.ensureReconnectingState(instance)) {
2472
                                    return;
2473
                                }
2474

    
2475
                                // Call poll with the raiseReconnect flag as true after the reconnect delay
2476
                                privateData.pollTimeoutId = window.setTimeout(function () {
2477
                                    poll(instance, true);
2478
                                }, that.reconnectDelay);
2479
                            }
2480
                        }
2481
                    });
2482

    
2483
                    // This will only ever pass after an error has occurred via the poll ajax procedure.
2484
                    if (reconnecting && raiseReconnect === true) {
2485
                        // We wait to reconnect depending on how many times we've failed to reconnect.
2486
                        // This is essentially a heuristic that will exponentially increase in wait time before
2487
                        // triggering reconnected.  This depends on the "error" handler of Poll to cancel this
2488
                        // timeout if it triggers before the Reconnected event fires.
2489
                        // The Math.min at the end is to ensure that the reconnect timeout does not overflow.
2490
                        privateData.reconnectTimeoutId = window.setTimeout(function () { fireReconnected(instance); }, Math.min(1000 * (Math.pow(2, reconnectErrors) - 1), maxFireReconnectedTimeout));
2491
                    }
2492
                }(connection));
2493
            }, 250); // Have to delay initial poll so Chrome doesn't show loader spinner in tab
2494
        },
2495

    
2496
        lostConnection: function (connection) {
2497
            if (connection.pollXhr) {
2498
                connection.pollXhr.abort("lostConnection");
2499
            }
2500
        },
2501

    
2502
        send: function (connection, data) {
2503
            transportLogic.ajaxSend(connection, data);
2504
        },
2505

    
2506
        stop: function (connection) {
2507
            /// <summary>Stops the long polling connection</summary>
2508
            /// <param name="connection" type="signalR">The SignalR connection to stop</param>
2509

    
2510
            window.clearTimeout(connection._.pollTimeoutId);
2511
            window.clearTimeout(connection._.reconnectTimeoutId);
2512

    
2513
            delete connection._.pollTimeoutId;
2514
            delete connection._.reconnectTimeoutId;
2515

    
2516
            if (connection.pollXhr) {
2517
                connection.pollXhr.abort();
2518
                connection.pollXhr = null;
2519
                delete connection.pollXhr;
2520
            }
2521
        },
2522

    
2523
        abort: function (connection, async) {
2524
            transportLogic.ajaxAbort(connection, async);
2525
        }
2526
    };
2527

    
2528
}(window.jQuery, window));
2529
/* jquery.signalR.hubs.js */
2530
// Copyright (c) .NET Foundation. All rights reserved.
2531
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
2532

    
2533
/*global window:false */
2534
/// <reference path="jquery.signalR.core.js" />
2535

    
2536
(function ($, window, undefined) {
2537

    
2538
    var eventNamespace = ".hubProxy",
2539
        signalR = $.signalR;
2540

    
2541
    function makeEventName(event) {
2542
        return event + eventNamespace;
2543
    }
2544

    
2545
    // Equivalent to Array.prototype.map
2546
    function map(arr, fun, thisp) {
2547
        var i,
2548
            length = arr.length,
2549
            result = [];
2550
        for (i = 0; i < length; i += 1) {
2551
            if (arr.hasOwnProperty(i)) {
2552
                result[i] = fun.call(thisp, arr[i], i, arr);
2553
            }
2554
        }
2555
        return result;
2556
    }
2557

    
2558
    function getArgValue(a) {
2559
        return $.isFunction(a) ? null : ($.type(a) === "undefined" ? null : a);
2560
    }
2561

    
2562
    function hasMembers(obj) {
2563
        for (var key in obj) {
2564
            // If we have any properties in our callback map then we have callbacks and can exit the loop via return
2565
            if (obj.hasOwnProperty(key)) {
2566
                return true;
2567
            }
2568
        }
2569

    
2570
        return false;
2571
    }
2572

    
2573
    function clearInvocationCallbacks(connection, error) {
2574
        /// <param name="connection" type="hubConnection" />
2575
        var callbacks = connection._.invocationCallbacks,
2576
            callback;
2577

    
2578
        if (hasMembers(callbacks)) {
2579
            connection.log("Clearing hub invocation callbacks with error: " + error + ".");
2580
        }
2581

    
2582
        // Reset the callback cache now as we have a local var referencing it
2583
        connection._.invocationCallbackId = 0;
2584
        delete connection._.invocationCallbacks;
2585
        connection._.invocationCallbacks = {};
2586

    
2587
        // Loop over the callbacks and invoke them.
2588
        // We do this using a local var reference and *after* we've cleared the cache
2589
        // so that if a fail callback itself tries to invoke another method we don't
2590
        // end up with its callback in the list we're looping over.
2591
        for (var callbackId in callbacks) {
2592
            callback = callbacks[callbackId];
2593
            callback.method.call(callback.scope, { E: error });
2594
        }
2595
    }
2596

    
2597
    // hubProxy
2598
    function hubProxy(hubConnection, hubName) {
2599
        /// <summary>
2600
        ///     Creates a new proxy object for the given hub connection that can be used to invoke
2601
        ///     methods on server hubs and handle client method invocation requests from the server.
2602
        /// </summary>
2603
        return new hubProxy.fn.init(hubConnection, hubName);
2604
    }
2605

    
2606
    hubProxy.fn = hubProxy.prototype = {
2607
        init: function (connection, hubName) {
2608
            this.state = {};
2609
            this.connection = connection;
2610
            this.hubName = hubName;
2611
            this._ = {
2612
                callbackMap: {}
2613
            };
2614
        },
2615

    
2616
        constructor: hubProxy,
2617

    
2618
        hasSubscriptions: function () {
2619
            return hasMembers(this._.callbackMap);
2620
        },
2621

    
2622
        on: function (eventName, callback) {
2623
            /// <summary>Wires up a callback to be invoked when a invocation request is received from the server hub.</summary>
2624
            /// <param name="eventName" type="String">The name of the hub event to register the callback for.</param>
2625
            /// <param name="callback" type="Function">The callback to be invoked.</param>
2626
            var that = this,
2627
                callbackMap = that._.callbackMap;
2628

    
2629
            // Normalize the event name to lowercase
2630
            eventName = eventName.toLowerCase();
2631

    
2632
            // If there is not an event registered for this callback yet we want to create its event space in the callback map.
2633
            if (!callbackMap[eventName]) {
2634
                callbackMap[eventName] = {};
2635
            }
2636

    
2637
            // Map the callback to our encompassed function
2638
            callbackMap[eventName][callback] = function (e, data) {
2639
                callback.apply(that, data);
2640
            };
2641

    
2642
            $(that).bind(makeEventName(eventName), callbackMap[eventName][callback]);
2643

    
2644
            return that;
2645
        },
2646

    
2647
        off: function (eventName, callback) {
2648
            /// <summary>Removes the callback invocation request from the server hub for the given event name.</summary>
2649
            /// <param name="eventName" type="String">The name of the hub event to unregister the callback for.</param>
2650
            /// <param name="callback" type="Function">The callback to be invoked.</param>
2651
            var that = this,
2652
                callbackMap = that._.callbackMap,
2653
                callbackSpace;
2654

    
2655
            // Normalize the event name to lowercase
2656
            eventName = eventName.toLowerCase();
2657

    
2658
            callbackSpace = callbackMap[eventName];
2659

    
2660
            // Verify that there is an event space to unbind
2661
            if (callbackSpace) {
2662
                // Only unbind if there's an event bound with eventName and a callback with the specified callback
2663
                if (callbackSpace[callback]) {
2664
                    $(that).unbind(makeEventName(eventName), callbackSpace[callback]);
2665

    
2666
                    // Remove the callback from the callback map
2667
                    delete callbackSpace[callback];
2668

    
2669
                    // Check if there are any members left on the event, if not we need to destroy it.
2670
                    if (!hasMembers(callbackSpace)) {
2671
                        delete callbackMap[eventName];
2672
                    }
2673
                } else if (!callback) { // Check if we're removing the whole event and we didn't error because of an invalid callback
2674
                    $(that).unbind(makeEventName(eventName));
2675

    
2676
                    delete callbackMap[eventName];
2677
                }
2678
            }
2679

    
2680
            return that;
2681
        },
2682

    
2683
        invoke: function (methodName) {
2684
            /// <summary>Invokes a server hub method with the given arguments.</summary>
2685
            /// <param name="methodName" type="String">The name of the server hub method.</param>
2686

    
2687
            var that = this,
2688
                connection = that.connection,
2689
                args = $.makeArray(arguments).slice(1),
2690
                argValues = map(args, getArgValue),
2691
                data = { H: that.hubName, M: methodName, A: argValues, I: connection._.invocationCallbackId },
2692
                d = $.Deferred(),
2693
                callback = function (minResult) {
2694
                    var result = that._maximizeHubResponse(minResult),
2695
                        source,
2696
                        error;
2697

    
2698
                    // Update the hub state
2699
                    $.extend(that.state, result.State);
2700

    
2701
                    if (result.Progress) {
2702
                        if (d.notifyWith) {
2703
                            // Progress is only supported in jQuery 1.7+
2704
                            d.notifyWith(that, [result.Progress.Data]);
2705
                        } else if(!connection._.progressjQueryVersionLogged) {
2706
                            connection.log("A hub method invocation progress update was received but the version of jQuery in use (" + $.prototype.jquery + ") does not support progress updates. Upgrade to jQuery 1.7+ to receive progress notifications.");
2707
                            connection._.progressjQueryVersionLogged = true;
2708
                        }
2709
                    } else if (result.Error) {
2710
                        // Server hub method threw an exception, log it & reject the deferred
2711
                        if (result.StackTrace) {
2712
                            connection.log(result.Error + "\n" + result.StackTrace + ".");
2713
                        }
2714

    
2715
                        // result.ErrorData is only set if a HubException was thrown
2716
                        source = result.IsHubException ? "HubException" : "Exception";
2717
                        error = signalR._.error(result.Error, source);
2718
                        error.data = result.ErrorData;
2719

    
2720
                        connection.log(that.hubName + "." + methodName + " failed to execute. Error: " + error.message);
2721
                        d.rejectWith(that, [error]);
2722
                    } else {
2723
                        // Server invocation succeeded, resolve the deferred
2724
                        connection.log("Invoked " + that.hubName + "." + methodName);
2725
                        d.resolveWith(that, [result.Result]);
2726
                    }
2727
                };
2728

    
2729
            connection._.invocationCallbacks[connection._.invocationCallbackId.toString()] = { scope: that, method: callback };
2730
            connection._.invocationCallbackId += 1;
2731

    
2732
            if (!$.isEmptyObject(that.state)) {
2733
                data.S = that.state;
2734
            }
2735

    
2736
            connection.log("Invoking " + that.hubName + "." + methodName);
2737
            connection.send(data);
2738

    
2739
            return d.promise();
2740
        },
2741

    
2742
        _maximizeHubResponse: function (minHubResponse) {
2743
            return {
2744
                State: minHubResponse.S,
2745
                Result: minHubResponse.R,
2746
                Progress: minHubResponse.P ? {
2747
                    Id: minHubResponse.P.I,
2748
                    Data: minHubResponse.P.D
2749
                } : null,
2750
                Id: minHubResponse.I,
2751
                IsHubException: minHubResponse.H,
2752
                Error: minHubResponse.E,
2753
                StackTrace: minHubResponse.T,
2754
                ErrorData: minHubResponse.D
2755
            };
2756
        }
2757
    };
2758

    
2759
    hubProxy.fn.init.prototype = hubProxy.fn;
2760

    
2761
    // hubConnection
2762
    function hubConnection(url, options) {
2763
        /// <summary>Creates a new hub connection.</summary>
2764
        /// <param name="url" type="String">[Optional] The hub route url, defaults to "/signalr".</param>
2765
        /// <param name="options" type="Object">[Optional] Settings to use when creating the hubConnection.</param>
2766
        var settings = {
2767
            qs: null,
2768
            logging: false,
2769
            useDefaultPath: true
2770
        };
2771

    
2772
        $.extend(settings, options);
2773

    
2774
        if (!url || settings.useDefaultPath) {
2775
            url = (url || "") + "/signalr";
2776
        }
2777
        return new hubConnection.fn.init(url, settings);
2778
    }
2779

    
2780
    hubConnection.fn = hubConnection.prototype = $.connection();
2781

    
2782
    hubConnection.fn.init = function (url, options) {
2783
        var settings = {
2784
                qs: null,
2785
                logging: false,
2786
                useDefaultPath: true
2787
            },
2788
            connection = this;
2789

    
2790
        $.extend(settings, options);
2791

    
2792
        // Call the base constructor
2793
        $.signalR.fn.init.call(connection, url, settings.qs, settings.logging);
2794

    
2795
        // Object to store hub proxies for this connection
2796
        connection.proxies = {};
2797

    
2798
        connection._.invocationCallbackId = 0;
2799
        connection._.invocationCallbacks = {};
2800

    
2801
        // Wire up the received handler
2802
        connection.received(function (minData) {
2803
            var data, proxy, dataCallbackId, callback, hubName, eventName;
2804
            if (!minData) {
2805
                return;
2806
            }
2807

    
2808
            // We have to handle progress updates first in order to ensure old clients that receive
2809
            // progress updates enter the return value branch and then no-op when they can't find
2810
            // the callback in the map (because the minData.I value will not be a valid callback ID)
2811
            if (typeof (minData.P) !== "undefined") {
2812
                // Process progress notification
2813
                dataCallbackId = minData.P.I.toString();
2814
                callback = connection._.invocationCallbacks[dataCallbackId];
2815
                if (callback) {
2816
                    callback.method.call(callback.scope, minData);
2817
                }
2818
            } else if (typeof (minData.I) !== "undefined") {
2819
                // We received the return value from a server method invocation, look up callback by id and call it
2820
                dataCallbackId = minData.I.toString();
2821
                callback = connection._.invocationCallbacks[dataCallbackId];
2822
                if (callback) {
2823
                    // Delete the callback from the proxy
2824
                    connection._.invocationCallbacks[dataCallbackId] = null;
2825
                    delete connection._.invocationCallbacks[dataCallbackId];
2826

    
2827
                    // Invoke the callback
2828
                    callback.method.call(callback.scope, minData);
2829
                }
2830
            } else {
2831
                data = this._maximizeClientHubInvocation(minData);
2832

    
2833
                // We received a client invocation request, i.e. broadcast from server hub
2834
                connection.log("Triggering client hub event '" + data.Method + "' on hub '" + data.Hub + "'.");
2835

    
2836
                // Normalize the names to lowercase
2837
                hubName = data.Hub.toLowerCase();
2838
                eventName = data.Method.toLowerCase();
2839

    
2840
                // Trigger the local invocation event
2841
                proxy = this.proxies[hubName];
2842

    
2843
                // Update the hub state
2844
                $.extend(proxy.state, data.State);
2845
                $(proxy).triggerHandler(makeEventName(eventName), [data.Args]);
2846
            }
2847
        });
2848

    
2849
        connection.error(function (errData, origData) {
2850
            var callbackId, callback;
2851

    
2852
            if (!origData) {
2853
                // No original data passed so this is not a send error
2854
                return;
2855
            }
2856

    
2857
            callbackId = origData.I;
2858
            callback = connection._.invocationCallbacks[callbackId];
2859

    
2860
            // Verify that there is a callback bound (could have been cleared)
2861
            if (callback) {
2862
                // Delete the callback
2863
                connection._.invocationCallbacks[callbackId] = null;
2864
                delete connection._.invocationCallbacks[callbackId];
2865

    
2866
                // Invoke the callback with an error to reject the promise
2867
                callback.method.call(callback.scope, { E: errData });
2868
            }
2869
        });
2870

    
2871
        connection.reconnecting(function () {
2872
            if (connection.transport && connection.transport.name === "webSockets") {
2873
                clearInvocationCallbacks(connection, "Connection started reconnecting before invocation result was received.");
2874
            }
2875
        });
2876

    
2877
        connection.disconnected(function () {
2878
            clearInvocationCallbacks(connection, "Connection was disconnected before invocation result was received.");
2879
        });
2880
    };
2881

    
2882
    hubConnection.fn._maximizeClientHubInvocation = function (minClientHubInvocation) {
2883
        return {
2884
            Hub: minClientHubInvocation.H,
2885
            Method: minClientHubInvocation.M,
2886
            Args: minClientHubInvocation.A,
2887
            State: minClientHubInvocation.S
2888
        };
2889
    };
2890

    
2891
    hubConnection.fn._registerSubscribedHubs = function () {
2892
        /// <summary>
2893
        ///     Sets the starting event to loop through the known hubs and register any new hubs
2894
        ///     that have been added to the proxy.
2895
        /// </summary>
2896
        var connection = this;
2897

    
2898
        if (!connection._subscribedToHubs) {
2899
            connection._subscribedToHubs = true;
2900
            connection.starting(function () {
2901
                // Set the connection's data object with all the hub proxies with active subscriptions.
2902
                // These proxies will receive notifications from the server.
2903
                var subscribedHubs = [];
2904

    
2905
                $.each(connection.proxies, function (key) {
2906
                    if (this.hasSubscriptions()) {
2907
                        subscribedHubs.push({ name: key });
2908
                        connection.log("Client subscribed to hub '" + key + "'.");
2909
                    }
2910
                });
2911

    
2912
                if (subscribedHubs.length === 0) {
2913
                    connection.log("No hubs have been subscribed to.  The client will not receive data from hubs.  To fix, declare at least one client side function prior to connection start for each hub you wish to subscribe to.");
2914
                }
2915

    
2916
                connection.data = connection.json.stringify(subscribedHubs);
2917
            });
2918
        }
2919
    };
2920

    
2921
    hubConnection.fn.createHubProxy = function (hubName) {
2922
        /// <summary>
2923
        ///     Creates a new proxy object for the given hub connection that can be used to invoke
2924
        ///     methods on server hubs and handle client method invocation requests from the server.
2925
        /// </summary>
2926
        /// <param name="hubName" type="String">
2927
        ///     The name of the hub on the server to create the proxy for.
2928
        /// </param>
2929

    
2930
        // Normalize the name to lowercase
2931
        hubName = hubName.toLowerCase();
2932

    
2933
        var proxy = this.proxies[hubName];
2934
        if (!proxy) {
2935
            proxy = hubProxy(this, hubName);
2936
            this.proxies[hubName] = proxy;
2937
        }
2938

    
2939
        this._registerSubscribedHubs();
2940

    
2941
        return proxy;
2942
    };
2943

    
2944
    hubConnection.fn.init.prototype = hubConnection.fn;
2945

    
2946
    $.hubConnection = hubConnection;
2947

    
2948
}(window.jQuery, window));
2949
/* jquery.signalR.version.js */
2950
// Copyright (c) .NET Foundation. All rights reserved.
2951
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
2952

    
2953

    
2954
/*global window:false */
2955
/// <reference path="jquery.signalR.core.js" />
2956
(function ($, undefined) {
2957
    $.signalR.version = "2.2.2";
2958
}(window.jQuery));
클립보드 이미지 추가 (최대 크기: 500 MB)