upgrade atmosphere to 1.0.4

Project: http://git-wip-us.apache.org/repos/asf/wicket/repo
Commit: http://git-wip-us.apache.org/repos/asf/wicket/commit/4b99a793
Tree: http://git-wip-us.apache.org/repos/asf/wicket/tree/4b99a793
Diff: http://git-wip-us.apache.org/repos/asf/wicket/diff/4b99a793

Branch: refs/heads/master
Commit: 4b99a79358cb396d067c6dd8a63468934476672f
Parents: 94bc49d
Author: Emond Papegaaij <[email protected]>
Authored: Wed Nov 21 11:53:39 2012 +0100
Committer: Emond Papegaaij <[email protected]>
Committed: Wed Nov 21 11:53:39 2012 +0100

----------------------------------------------------------------------
 wicket-experimental/wicket-atmosphere/pom.xml      |    2 +-
 .../apache/wicket/atmosphere/jquery.atmosphere.js  |  718 +++++++++++++--
 2 files changed, 620 insertions(+), 100 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/wicket/blob/4b99a793/wicket-experimental/wicket-atmosphere/pom.xml
----------------------------------------------------------------------
diff --git a/wicket-experimental/wicket-atmosphere/pom.xml 
b/wicket-experimental/wicket-atmosphere/pom.xml
index 7196408..b1d5c32 100644
--- a/wicket-experimental/wicket-atmosphere/pom.xml
+++ b/wicket-experimental/wicket-atmosphere/pom.xml
@@ -27,7 +27,7 @@
        <version>0.6-SNAPSHOT</version>
        <packaging>jar</packaging>
        <properties>
-               <atmosphere.version>1.0.0</atmosphere.version>
+               <atmosphere.version>1.0.4</atmosphere.version>
        </properties>
        <name>Wicket-Atmosphere</name>
        <description>Wicket-Atmosphere provides integration of the Atmosphere 
Framework in Wicket.</description>

http://git-wip-us.apache.org/repos/asf/wicket/blob/4b99a793/wicket-experimental/wicket-atmosphere/src/main/java/org/apache/wicket/atmosphere/jquery.atmosphere.js
----------------------------------------------------------------------
diff --git 
a/wicket-experimental/wicket-atmosphere/src/main/java/org/apache/wicket/atmosphere/jquery.atmosphere.js
 
b/wicket-experimental/wicket-atmosphere/src/main/java/org/apache/wicket/atmosphere/jquery.atmosphere.js
index cfb9289..286c728 100644
--- 
a/wicket-experimental/wicket-atmosphere/src/main/java/org/apache/wicket/atmosphere/jquery.atmosphere.js
+++ 
b/wicket-experimental/wicket-atmosphere/src/main/java/org/apache/wicket/atmosphere/jquery.atmosphere.js
@@ -1,4 +1,6 @@
 /**
+ * Copyright 2012 Jeanfrancois Arcand
+ *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
@@ -12,26 +14,32 @@
  * limitations under the License.
  */
 /*
- * Part of this code has been taked from
- *
- * jQuery Stream @VERSION
- * Comet Streaming JavaScript Library
- * http://code.google.com/p/jquery-stream/
+ * IE streaming/XDR supports is copied/highly inspired by 
http://code.google.com/p/jquery-stream/
  *
  * Copyright 2011, Donghwan Kim
  * Licensed under the Apache License, Version 2.0
  * http://www.apache.org/licenses/LICENSE-2.0
  *
- * Compatible with jQuery 1.5+
- */
+ * LocalStorage supports is copied/highly inspired by 
https://github.com/flowersinthesand/jquery-socket
+ * Copyright 2011, Donghwan Kim
+ * Licensed under the Apache License, Version 2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * */
 /**
  * Official documentation of this library: 
https://github.com/Atmosphere/atmosphere/wiki/jQuery.atmosphere.js-API
  */
 jQuery.atmosphere = function() {
-    jQuery(window).unload(function() {
+    jQuery(window).bind("unload.atmosphere", function() {
         jQuery.atmosphere.unsubscribe();
     });
 
+    // Prevent ESC to kill the connection from Firefox.
+    jQuery(window).keypress(function(e){
+        if(e.keyCode == 27){
+            e.preventDefault();
+        }
+    });
+
     var parseHeaders = function(headerString) {
         var match, rheaders = /^(.*?):[ \t]*([^\r\n]*)\r?$/mg, headers = {};
         while (match = rheaders.exec(headerString)) {
@@ -41,7 +49,7 @@ jQuery.atmosphere = function() {
     };
 
     return {
-        version : "1.0",
+        version : "1.0.3",
         requests : [],
         callbacks : [],
 
@@ -59,6 +67,8 @@ jQuery.atmosphere = function() {
         },
         onTransportFailure : function(response) {
         },
+        onLocalMessage : function (response) {
+        },
 
         AtmosphereRequest : function(options) {
 
@@ -100,6 +110,8 @@ jQuery.atmosphere = function() {
                 reconnectInterval : 0,
                 dropAtmosphereHeaders : true,
                 uuid : 0,
+                shared : false,
+                readResponsesHeaders : true,
                 onError : function(response) {
                 },
                 onClose : function(response) {
@@ -113,6 +125,8 @@ jQuery.atmosphere = function() {
                 onMessagePublished : function(response) {
                 },
                 onTransportFailure : function (reason, request) {
+                },
+                onLocalMessage : function (request) {
                 }
             };
 
@@ -123,12 +137,12 @@ jQuery.atmosphere = function() {
             var _response = {
                 status: 200,
                 responseBody : '',
-                expectedBodySize : -1,
                 headers : [],
                 state : "messageReceived",
                 transport : "polling",
                 error: null,
                 request : null,
+                partialMessage : "",
                 id : 0
             };
 
@@ -189,6 +203,33 @@ jQuery.atmosphere = function() {
              */
             var _abordingConnection = false;
 
+            /**
+             * A local "channel' of communication.
+             * @private
+             */
+            var _localSocketF = null;
+
+            /**
+             * The storage used.
+             * @private
+             */
+            var _storageService;
+
+            /**
+             * Local communication
+             * @private
+             */
+            var _localStorageService = null;
+
+            /**
+             * A Unique ID
+             * @private
+             */
+            var guid = jQuery.now();
+
+            /** Trace time */
+            var _traceTimer;
+
             // Automatic call to subscribe
             _subscribe(options);
 
@@ -264,6 +305,39 @@ jQuery.atmosphere = function() {
              * @private
              */
             function _execute() {
+                // Shared across multiple tabs/windows.
+                if (_request.shared) {
+
+                    var version  = 0;
+                    if (navigator.appVersion.indexOf("MSIE") != -1) {
+                        version = 
parseFloat(navigator.appVersion.split("MSIE")[1]);
+                    }
+
+                    // Multi Tab aren't working on IE 8. Tested with 
atmosphere.js and jquery-socket.js
+                    // both pops up a blank page.
+                    if (version != 8) {
+                        _localStorageService = _local(_request);
+                        if (_localStorageService != null) {
+                            if (_request.logLevel == 'debug') {
+                                jQuery.atmosphere.debug("Storage service 
available. All communication will be local");
+                            }
+
+                            if (_localStorageService.open(_request)) {
+                                // Local connection.
+                                return;
+                            }
+                        }
+
+                        if (_request.logLevel == 'debug') {
+                            jQuery.atmosphere.debug("No Storage service 
available.");
+                        }
+                    } else {
+                        jQuery.atmosphere.info("Multi tab not supported on IE 
8.");
+                    }
+                    // Wasn't local or an error occurred
+                    _localStorageService = null;
+                }
+
                 if (_request.transport != 'websocket' && _request.transport != 
'sse') {
                     _open('opening', _request.transport, _request);
                     _executeRequest();
@@ -283,10 +357,324 @@ jQuery.atmosphere = function() {
                 }
             }
 
+            function _local(request) {
+                var trace, connector, orphan, name = "atmosphere-" + 
request.url, connectors = {
+                    storage: function() {
+                        if (!jQuery.atmosphere.supportStorage()) {
+                            return;
+                        }
+
+                        var storage = window.localStorage,
+                            get = function(key) {
+                                return jQuery.parseJSON(storage.getItem(name + 
"-" + key));
+                            },
+                            set = function(key, value) {
+                                storage.setItem(name + "-" + key, 
jQuery.stringifyJSON(value));
+                            };
+
+                        return {
+                            init: function() {
+                                set("children", 
get("children").concat([guid]));
+                                jQuery(window).on("storage.socket", 
function(event) {
+                                    event = event.originalEvent;
+                                    if (event.key === name && event.newValue) {
+                                        listener(event.newValue);
+                                    }
+                                });
+                                return get("opened");
+                            },
+                            signal: function(type, data) {
+                                storage.setItem(name, 
jQuery.stringifyJSON({target: "p", type: type, data: data}));
+                            },
+                            close: function() {
+                                var index, children = get("children");
+
+                                jQuery(window).off("storage.socket");
+                                if (children) {
+                                    index = jQuery.inArray(request.id, 
children);
+                                    if (index > -1) {
+                                        children.splice(index, 1);
+                                        set("children", children);
+                                    }
+                                }
+                            }
+                        };
+                    },
+                    windowref: function() {
+                        var win = window.open("", name.replace(/\W/g, ""));
+
+                        if (!win || win.closed || !win.callbacks) {
+                            return;
+                        }
+
+                        return {
+                            init: function() {
+                                win.callbacks.push(listener);
+                                win.children.push(guid);
+                                return win.opened;
+                            },
+                            signal: function(type, data) {
+                                if (!win.closed && win.fire) {
+                                    win.fire(jQuery.stringifyJSON({target: 
"p", type: type, data: data}));
+                                }
+                            },
+                            close : function() {
+                                function remove(array, e) {
+                                    var index = jQuery.inArray(e, array);
+                                    if (index > -1) {
+                                        array.splice(index, 1);
+                                    }
+                                }
+
+                                // Removes traces only if the parent is alive
+                                if (!orphan) {
+                                    remove(win.callbacks, listener);
+                                    remove(win.children, guid);
+                                }
+                            }
+
+                        };
+                    }
+                };
+
+                // Receives open, close and message command from the parent
+                function listener(string) {
+                    var command = jQuery.parseJSON(string), data = 
command.data;
+
+                    if (command.target === "c") {
+                        switch (command.type) {
+                            case "open":
+                                _open("opening", 'local', _request)
+                                break;
+                            case "close":
+                                if (!orphan) {
+                                    orphan = true;
+                                    if (data.reason === "aborted") {
+                                        _close();
+                                    } else {
+                                        // Gives the heir some time to 
reconnect
+                                        if (data.heir === guid) {
+                                            _execute();
+                                        } else {
+                                            setTimeout(function() {
+                                                _execute();
+                                            }, 100);
+                                        }
+                                    }
+                                }
+                                break;
+                            case "message":
+                                _prepareCallback(data, "messageReceived", 200, 
request.transport);
+                                break;
+                            case "localMessage":
+                                _localMessage(data);
+                                break;
+                        }
+                    }
+                }
+
+                function findTrace() {
+                    var matcher = new RegExp("(?:^|; )(" + 
encodeURIComponent(name) + ")=([^;]*)").exec(document.cookie);
+                    if (matcher) {
+                        return 
jQuery.parseJSON(decodeURIComponent(matcher[2]));
+                    }
+                }
+
+                // Finds and validates the parent socket's trace from the 
cookie
+                trace = findTrace();
+                if (!trace || jQuery.now() - trace.ts > 1000) {
+                    return;
+                }
+
+                // Chooses a connector
+                connector = connectors.storage() || connectors.windowref();
+                if (!connector) {
+                    return;
+                }
+
+                return {
+                    open: function() {
+                        var parentOpened;
+
+                        // Checks the shared one is alive
+                        _traceTimer = setInterval(function() {
+                            var oldTrace = trace;
+                            trace = findTrace();
+                            if (!trace || oldTrace.ts === trace.ts) {
+                                // Simulates a close signal
+                                listener(jQuery.stringifyJSON({target: "c", 
type: "close", data: {reason: "error", heir: oldTrace.heir}}));
+                            }
+                        }, 1000);
+
+                        parentOpened = connector.init();
+                        if (parentOpened) {
+                            // Firing the open event without delay robs the 
user of the opportunity to bind connecting event handlers
+                            setTimeout(function() {
+                                _open("opening", 'local', request)
+                            }, 50);
+                        }
+                        return parentOpened;
+                    },
+                    send: function(event) {
+                        connector.signal("send", event);
+                    },
+                    localSend: function(event) {
+                        connector.signal("localSend", 
jQuery.stringifyJSON({id: guid , event: event}));
+                    },
+                    close: function() {
+                        // Do not signal the parent if this method is executed 
by the unload event handler
+                        if (!_abordingConnection) {
+                            clearInterval(_traceTimer);
+                            connector.signal("close");
+                            connector.close();
+                        }
+                    }
+                };
+            };
+
+            function share() {
+                var storageService, name = "atmosphere-" + _request.url, 
servers = {
+                    // Powered by the storage event and the localStorage
+                    // http://www.w3.org/TR/webstorage/#event-storage
+                    storage: function() {
+                        if (!jQuery.atmosphere.supportStorage()) {
+                            return;
+                        }
+
+                        var storage = window.localStorage;
+
+                        return {
+                            init: function() {
+                                // Handles the storage event
+                                jQuery(window).on("storage.socket", 
function(event) {
+                                    event = event.originalEvent;
+                                    // When a deletion, newValue initialized 
to null
+                                    if (event.key === name && event.newValue) {
+                                        listener(event.newValue);
+                                    }
+                                });
+                            },
+                            signal: function(type, data) {
+                                storage.setItem(name, 
jQuery.stringifyJSON({target: "c", type: type, data: data}));
+                            },
+                            get: function(key) {
+                                return jQuery.parseJSON(storage.getItem(name + 
"-" + key));
+                            },
+                            set: function(key, value) {
+                                storage.setItem(name + "-" + key, 
jQuery.stringifyJSON(value));
+                            },
+                            close : function() {
+                                jQuery(window).off("storage.socket");
+                                storage.removeItem(name);
+                                storage.removeItem(name + "-opened");
+                                storage.removeItem(name + "-children");
+                            }
+
+                        };
+                    },
+                    // Powered by the window.open method
+                    // https://developer.mozilla.org/en/DOM/window.open
+                    windowref: function() {
+                        // Internet Explorer raises an invalid argument error
+                        // when calling the window.open method with the name 
containing non-word characters
+                        var neim = name.replace(/\W/g, ""), win = 
(jQuery('iframe[name="' + neim + '"]')[0]
+                            || jQuery('<iframe name="' + neim + '" 
/>').hide().appendTo("body")[0]).contentWindow;
+
+                        return {
+                            init: function() {
+                                // Callbacks from different windows
+                                win.callbacks = [listener];
+                                // In IE 8 and less, only string argument can 
be safely passed to the function in other window
+                                win.fire = function(string) {
+                                    var i;
+
+                                    for (i = 0; i < win.callbacks.length; i++) 
{
+                                        win.callbacks[i](string);
+                                    }
+                                };
+                            },
+                            signal: function(type, data) {
+                                if (!win.closed && win.fire) {
+                                    win.fire(jQuery.stringifyJSON({target: 
"c", type: type, data: data}));
+                                }
+                            },
+                            get: function(key) {
+                                return !win.closed ? win[key] : null;
+                            },
+                            set: function(key, value) {
+                                if (!win.closed) {
+                                    win[key] = value;
+                                }
+                            },
+                            close : function() {}
+                        };
+                    }
+                };
+
+
+                // Receives send and close command from the children
+                function listener(string) {
+                    var command = jQuery.parseJSON(string), data = 
command.data;
+
+                    if (command.target === "p") {
+                        switch (command.type) {
+                            case "send":
+                                _push(data);
+                                break;
+                            case "localSend":
+                                _localMessage(data);
+                                break;
+                            case "close":
+                                _close();
+                                break;
+                        }
+                    }
+                }
+
+                _localSocketF = function propagateMessageEvent(context) {
+                    storageService.signal("message", context);
+                }
+
+                function leaveTrace() {
+                    document.cookie = encodeURIComponent(name) + "=" +
+                        // Opera's JSON implementation ignores a number whose 
a last digit of 0 strangely
+                        // but has no problem with a number whose a last digit 
of 9 + 1
+                        encodeURIComponent(jQuery.stringifyJSON({ts: 
jQuery.now() + 1, heir: (storageService.get("children") || [])[0]}));
+                }
+
+                // Chooses a storageService
+                storageService = servers.storage() || servers.windowref();
+                storageService.init();
+
+                if (_request.logLevel == 'debug') {
+                    jQuery.atmosphere.debug("Installed StorageService " + 
storageService);
+                }
+
+                // List of children sockets
+                storageService.set("children", []);
+
+                if (storageService.get("opened") != null && 
!storageService.get("opened")) {
+                    // Flag indicating the parent socket is opened
+                    storageService.set("opened", false);
+                }
+                // Leaves traces
+                leaveTrace();
+                _traceTimer = setInterval(leaveTrace, 1000);
+
+                _storageService = storageService;
+            }
+
             /**
              * @private
              */
             function _open(state, transport, request) {
+                if (_request.shared && transport != 'local') {
+                    share();
+                }
+
+                if (_storageService != null) {
+                    _storageService.set("opened", true);
+                }
 
                 request.close = function() {
                     _close();
@@ -299,7 +687,11 @@ jQuery.atmosphere = function() {
                 _response.status = 200;
                 var prevTransport = _response.transport;
                 _response.transport = transport;
+
+                var _body = _response.responseBody;
                 _invokeCallback();
+                _response.responseBody = _body;
+
                 _response.state = prevState;
                 _response.transport = prevTransport;
             }
@@ -313,6 +705,9 @@ jQuery.atmosphere = function() {
              * @private
              */
             function _jsonp(request) {
+                // When CORS is enabled, make sure we force the proper 
transport.
+                request.transport="jsonp";
+
                 var rq = _request;
                 if ((request != null) && (typeof(request) != 'undefined')) {
                     rq = request;
@@ -323,7 +718,7 @@ jQuery.atmosphere = function() {
                 if (rq.attachHeadersAsQueryString) {
                     url = _attachHeaders(rq);
                     if (data != '') {
-                        url += "&X-Atmosphere-Post-Body=" + data;
+                        url += "&X-Atmosphere-Post-Body=" + 
encodeURIComponent(data);
                     }
                     data = '';
                 }
@@ -343,7 +738,9 @@ jQuery.atmosphere = function() {
                     success: function(json) {
 
                         if (rq.requestCount++ < rq.maxRequest) {
-                            if (rq.executeCallbackBeforeReconnect) {
+                            _readHeaders(_jqxhr, rq);
+
+                            if (!rq.executeCallbackBeforeReconnect) {
                                 _reconnect(_jqxhr, rq);
                             }
 
@@ -358,7 +755,7 @@ jQuery.atmosphere = function() {
 
                             _prepareCallback(msg, "messageReceived", 200, 
rq.transport);
 
-                            if (!rq.executeCallbackBeforeReconnect) {
+                            if (rq.executeCallbackBeforeReconnect) {
                                 _reconnect(_jqxhr, rq);
                             }
                         } else {
@@ -392,7 +789,7 @@ jQuery.atmosphere = function() {
                 if (rq.attachHeadersAsQueryString) {
                     url = _attachHeaders(rq);
                     if (data != '') {
-                        url += "&X-Atmosphere-Post-Body=" + data;
+                        url += "&X-Atmosphere-Post-Body=" + 
encodeURIComponent(data);
                     }
                     data = '';
                 }
@@ -410,13 +807,13 @@ jQuery.atmosphere = function() {
                     success: function(data, textStatus, jqXHR) {
 
                         if (rq.requestCount++ < rq.maxRequest) {
-                            if (rq.executeCallbackBeforeReconnect) {
+                            if (!rq.executeCallbackBeforeReconnect) {
                                 _reconnect(_jqxhr, rq);
                             }
 
                             _prepareCallback(data, "messageReceived", 200, 
rq.transport);
 
-                            if (!rq.executeCallbackBeforeReconnect) {
+                            if (rq.executeCallbackBeforeReconnect) {
                                 _reconnect(_jqxhr, rq);
                             }
                         } else {
@@ -557,18 +954,20 @@ jQuery.atmosphere = function() {
                     _response.responseBody = "";
                     _response.status = !sseOpened ? 501 : 200;
                     _invokeCallback();
+                    _sse.close();
 
                     if (_abordingConnection) {
                         jQuery.atmosphere.log(_request.logLevel, ["SSE closed 
normally"]);
                     } else if (!sseOpened) {
                         _reconnectWithFallbackTransport("SSE failed. 
Downgrading to fallback transport and resending");
                     } else if (_request.reconnect && (_response.transport == 
'sse')) {
+                        _request.requestCount = _requestCount;
                         if (_requestCount++ < _request.maxRequest) {
-                            _request.requestCount = _requestCount;
+                            _request.id = setTimeout(function() {
+                                _executeSSE(true);
+                            }, _request.reconnectInterval);
                             _response.responseBody = "";
-                            _executeSSE(true);
                         } else {
-                            _sse.close();
                             jQuery.atmosphere.log(_request.logLevel, ["SSE 
reconnect maximum try reached " + _request.requestCount]);
                             _onError();
                         }
@@ -745,23 +1144,35 @@ jQuery.atmosphere = function() {
              */
             function _trackMessageSize(message, request, response) {
                 if (request.trackMessageLength) {
-                    // The message length is the included within the message
+
+                    // If we have found partial message, prepend them.
+                    if (response.partialMessage.length != 0) {
+                        message = response.partialMessage + message;
+                    }
+
+                    var messages = [];
+                    var messageLength = 0;
                     var messageStart = 
message.indexOf(request.messageDelimiter);
+                    while (messageStart != -1) {
+                        messageLength = message.substring(messageLength, 
messageStart);
+                        message = message.substring(messageStart + 
request.messageDelimiter.length, message.length);
 
-                    var length = response.expectedBodySize;
-                    if (messageStart != -1) {
-                        length = message.substring(0, messageStart);
-                        message = message.substring(messageStart + 1);
-                        response.expectedBodySize = length;
+                        if (message.length == 0 || message.length < 
messageLength) break;
+
+                        messageStart = 
message.indexOf(request.messageDelimiter);
+                        messages.push(message.substring(0, messageLength));
                     }
 
-                    if (messageStart != -1) {
-                        response.responseBody = message;
+                    if (messages.length == 0 || (messageStart != -1 && 
message.length != 0 && messageLength != message.length)){
+                        response.partialMessage = messageLength + 
request.messageDelimiter + message ;
                     } else {
-                        response.responseBody += message;
+                        response.partialMessage = "";
                     }
 
-                    if (response.responseBody.length != length) {
+                    if (messages.length != 0) {
+                        response.responseBody = 
messages.join(request.messageDelimiter);
+                        return false;
+                    } else {
                         return true;
                     }
                 } else {
@@ -862,18 +1273,19 @@ jQuery.atmosphere = function() {
             function _buildAjaxRequest() {
                 var ajaxRequest;
                 if (jQuery.browser.msie) {
-                    var activexmodes = ["Msxml2.XMLHTTP", "Microsoft.XMLHTTP"];
-                    for (var i = 0; i < activexmodes.length; i++) {
-                        try {
-                            ajaxRequest = new ActiveXObject(activexmodes[i]);
-                        } catch(e) {
-                        }
-                    }
-
-                } else if (window.XMLHttpRequest) {
-                    ajaxRequest = new XMLHttpRequest();
+                    if (typeof XMLHttpRequest == "undefined")
+                      XMLHttpRequest = function () {
+                        try { return new ActiveXObject("Msxml2.XMLHTTP.6.0"); }
+                          catch (e) {}
+                        try { return new ActiveXObject("Msxml2.XMLHTTP.3.0"); }
+                          catch (e) {}
+                        try { return new ActiveXObject("Microsoft.XMLHTTP"); }
+                          catch (e) {}
+                        //Microsoft.XMLHTTP points to Msxml2.XMLHTTP and is 
redundant
+                        throw new Error("This browser does not support 
XMLHttpRequest.");
+                      };
                 }
-                return ajaxRequest;
+                return new XMLHttpRequest();
             }
 
             /**
@@ -923,17 +1335,20 @@ jQuery.atmosphere = function() {
                         _response.transport = rq.transport;
                     }
 
-                    var error = false;
                     if (!jQuery.browser.msie) {
                         ajaxRequest.onerror = function() {
-                            error = true;
                             try {
                                 _response.status = XMLHttpRequest.status;
                             } catch(e) {
-                                _response.status = 404;
+                                _response.status = 500;
+                            }
+
+                            if (!_response.status) {
+                                _response.status = 500;
                             }
 
                             _response.state = "error";
+                            _invokeCallback();
                             _reconnect(ajaxRequest, rq, true);
                         };
                     }
@@ -975,33 +1390,30 @@ jQuery.atmosphere = function() {
                             clearTimeout(rq.id);
                         }
 
-                        try {
-                            var tempUUID = 
ajaxRequest.getResponseHeader('X-Atmosphere-tracking-id');
-                            if (tempUUID != null || tempUUID != undefined) {
-                                _request.uuid = tempUUID.split(" ").pop();
-                            }
-                        } catch (e) {
-                        }
-
                         if (update) {
                             var responseText = ajaxRequest.responseText;
 
-                            // Do not fail on trying to retrieve headers. 
Chrome migth fail with
-                            // Refused to get unsafe header
-                            // Let the failure happens later with a better 
error message
-                            try {
-                                var tempDate = 
ajaxRequest.getResponseHeader('X-Cache-Date');
-                                if (tempDate != null || tempDate != undefined) 
{
-                                    _request.lastTimestamp = tempDate.split(" 
").pop();
-                                }
-                            } catch (e) {
+                            // MSIE status can be higher than 1000, Chrome can 
be 0
+                            if (ajaxRequest.status >= 500 || 
ajaxRequest.status == 0) {
+                                _onError();
+                                return;
                             }
 
+                            _readHeaders(ajaxRequest, _request);
+
                             if (rq.transport == 'streaming') {
                                 var text = 
responseText.substring(rq.lastIndex, responseText.length);
                                 _response.isJunkEnded = true;
 
-                                if (rq.lastIndex == 0 && text.indexOf("<!-- 
Welcome to the Atmosphere Framework.") != -1) {
+                                //fix junk is comming in parts
+                                if (!_response.junkFull && (text.indexOf("<!-- 
Welcome to the Atmosphere Framework.") == -1 || text.indexOf("<!-- EOD -->") == 
-1)) {
+                                    return;
+                                }
+                                _response.junkFull = true;
+
+                                //if it's the start and we see the junk start
+                                //fix for reconnecting on chrome - junk is 
comming in parts
+                                if (rq.lastIndex == 0 && text.indexOf("<!-- 
Welcome to the Atmosphere Framework.") != -1 && text.indexOf("<!-- EOD -->") != 
-1) {
                                     _response.isJunkEnded = false;
                                 }
 
@@ -1012,6 +1424,8 @@ jQuery.atmosphere = function() {
 
                                     if (junkEnd > endOfJunkLength && junkEnd 
!= text.length) {
                                         _response.responseBody = 
text.substring(junkEnd);
+                                        //fix cached messages
+                                        skipCallbackInvocation = 
_trackMessageSize(_response.responseBody, rq, _response);
                                     } else {
                                         skipCallbackInvocation = true;
                                     }
@@ -1028,15 +1442,7 @@ jQuery.atmosphere = function() {
                                                 _response.status = 
ajaxRequest.status;
                                                 _response.headers = 
parseHeaders(ajaxRequest.getAllResponseHeaders());
 
-                                                // HOTFIX for firefox bug: 
https://bugzilla.mozilla.org/show_bug.cgi?id=608735
-                                                if (_request.headers) {
-                                                    
jQuery.each(_request.headers, function(name) {
-                                                        var v = 
ajaxRequest.getResponseHeader(name);
-                                                        if (v) {
-                                                            
_response.headers[name] = v;
-                                                        }
-                                                    });
-                                                }
+                                                _readHeaders(ajaxRequest, 
_request);
                                             }
                                             catch(e) {
                                                 _response.status = 404;
@@ -1067,15 +1473,7 @@ jQuery.atmosphere = function() {
                                 _response.status = ajaxRequest.status;
                                 _response.headers = 
parseHeaders(ajaxRequest.getAllResponseHeaders());
 
-                                // HOTFIX for firefox bug: 
https://bugzilla.mozilla.org/show_bug.cgi?id=608735
-                                if (_request.headers) {
-                                    jQuery.each(_request.headers, 
function(name) {
-                                        var v = 
ajaxRequest.getResponseHeader(name);
-                                        if (v) {
-                                            _response.headers[name] = v;
-                                        }
-                                    });
-                                }
+                                _readHeaders(ajaxRequest, rq);
                             } catch(e) {
                                 _response.status = 404;
                             }
@@ -1121,7 +1519,9 @@ jQuery.atmosphere = function() {
                     _subscribed = true;
 
                 } else {
-                    jQuery.atmosphere.log(rq.logLevel, ["Max re-connection 
reached."]);
+                    if (rq.logLevel == 'debug') {
+                        jQuery.atmosphere.log(rq.logLevel, ["Max re-connection 
reached."]);
+                    }
                     _onError();
                 }
             }
@@ -1206,10 +1606,8 @@ jQuery.atmosphere = function() {
                     rq = request;
                 }
 
-                var lastMessage = "";
                 var transport = rq.transport;
                 var lastIndex = 0;
-
                 var xdrCallback = function (xdr) {
                     var responseBody = xdr.responseText;
                     var isJunkEnded = false;
@@ -1227,7 +1625,6 @@ jQuery.atmosphere = function() {
                             lastIndex += responseBody.length;
                         }
                     }
-
                     _prepareCallback(responseBody, "messageReceived", 200, 
transport);
                 };
 
@@ -1249,19 +1646,38 @@ jQuery.atmosphere = function() {
                 // Handles open and message event
                 xdr.onprogress = function() {
                     xdrCallback(xdr);
+                    rq.lastMessage = xdr.responseText;
                 };
                 // Handles error event
                 xdr.onerror = function() {
-                    _prepareCallback(xdr.responseText, "error", 500, 
transport);
+                    // If the server doesn't send anything back to XDR will 
fail with polling
+                    if (rq.transport != 'polling') {
+                        _prepareCallback(xdr.responseText, "error", 500, 
transport);
+                    }
                 };
                 // Handles close event
-                xdr.onload = function() {
-                    if (lastMessage != xdr.responseText) {
+                xdr.onload = function () {
+                    // XDomain loop forever on itself without this.
+                    // TODO: Clearly I need to come with something better than 
that solution
+                    if (rq.lastMessage == xdr.responseText) return;
+
+                    if (rq.executeCallbackBeforeReconnect) {
                         xdrCallback(xdr);
                     }
-                    if (rq.transport == "long-polling") {
-                        _executeRequest();
+
+                    // window.XDomainRequest() cannot read response headers, 
hence X-Atmosphere-Tracking-ID
+                    // and X-Cache-Date won't work.
+                    // _readHeaders()
+
+                    if (rq.transport == "long-polling" && rq.requestCount++ < 
rq.maxRequest) {
+                        xdr.status = 200;
+                        _reconnect(xdr, rq, false);
+                    }
+
+                    if (!rq.executeCallbackBeforeReconnect) {
+                        xdrCallback(xdr);
                     }
+                    rq.lastMessage = xdr.responseText;
                 };
 
                 return {
@@ -1271,7 +1687,7 @@ jQuery.atmosphere = function() {
                         }
                         var url = _attachHeaders(rq);
                         if (rq.method == 'POST') {
-                            url += "&X-Atmosphere-Post-Body=" + rq.data;
+                            url += "&X-Atmosphere-Post-Body=" + 
encodeURIComponent(rq.data);
                         }
                         xdr.open(rq.method, rewriteURL(url));
                         xdr.send();
@@ -1321,7 +1737,7 @@ jQuery.atmosphere = function() {
 
                         url = _attachHeaders(rq);
                         if (rq.data != '') {
-                            url += "&X-Atmosphere-Post-Body=" + rq.data;
+                            url += "&X-Atmosphere-Post-Body=" + 
encodeURIComponent(rq.data);
                         }
 
                         // Finally attach a timestamp to prevent Android and 
IE caching.
@@ -1399,10 +1815,11 @@ jQuery.atmosphere = function() {
                                     var text = readResponse();
                                     if (text.length > rq.lastIndex) {
                                         _response.status = 200;
-                                        _prepareCallback(text, 
"messageReceived", 200, rq.transport);
 
                                         // Empties response every time that it 
is handled
                                         res.innerText = "";
+                                        _prepareCallback(text, 
"messageReceived", 200, rq.transport);
+
                                         rq.lastIndex = 0;
                                     }
 
@@ -1440,7 +1857,10 @@ jQuery.atmosphere = function() {
              * @private
              */
             function _push(message) {
-                if (_activeRequest != null || _sse != null) {
+
+                if (_localStorageService != null) {
+                    _pushLocal(message);
+                } else if (_activeRequest != null || _sse != null) {
                     _pushAjaxMessage(message);
                 } else if (_ieStream != null) {
                     _pushIE(message);
@@ -1451,6 +1871,25 @@ jQuery.atmosphere = function() {
                 }
             }
 
+            function _pushLocal(message) {
+                _localStorageService.send(message);
+            }
+
+            function _intraPush(message) {
+                // IE 9 will crash if not.
+                if (message.length == 0) return;
+
+                try {
+                    if (_localStorageService) {
+                        _localStorageService.localSend(message);
+                    } else {
+                        _storageService.signal("localMessage",  
jQuery.stringifyJSON({id: guid , event: message}));
+                    }
+                } catch (err) {
+                    jQuery.atmosphere.error(err);
+                }
+            }
+
             /**
              * Send a message using currently opened ajax request (using
              * http-streaming or long-polling). <br>
@@ -1526,6 +1965,7 @@ jQuery.atmosphere = function() {
                     maxRequest : 60,
                     logLevel : 'info',
                     requestCount : 0,
+                    withCredentials : _request.withCredentials,
                     transport: 'polling',
                     attachHeadersAsQueryString: true,
                     enableXDR: _request.enableXDR,
@@ -1571,6 +2011,17 @@ jQuery.atmosphere = function() {
                 }
             }
 
+            function _localMessage(message) {
+                var m = jQuery.parseJSON(message);
+                if (m.id != guid) {
+                    if (typeof(_request.onLocalMessage) != 'undefined') {
+                        _request.onLocalMessage(m.event);
+                    } else if (typeof(jQuery.atmosphere.onLocalMessage) != 
'undefined') {
+                        jQuery.atmosphere.onLocalMessage(m.event);
+                    }
+                }
+            }
+
             function _prepareCallback(messageBody, state, errorCode, 
transport) {
 
                 if (state == "messageReceived") {
@@ -1579,16 +2030,38 @@ jQuery.atmosphere = function() {
 
                 _response.transport = transport;
                 _response.status = errorCode;
-
-                // If not -1, we have buffered the message.
-                if (_response.expectedBodySize == -1) {
-                    _response.responseBody = messageBody;
-                }
                 _response.state = state;
 
                 _invokeCallback();
             }
 
+            function _readHeaders(xdr, request) {
+                if (!request.readResponsesHeaders) return;
+
+                try {
+                    var tempDate = xdr.getResponseHeader('X-Cache-Date');
+                    if (tempDate && tempDate != null && tempDate.length > 0 ) {
+                        request.lastTimestamp = tempDate.split(" ").pop();
+                    }
+
+                    var tempUUID = 
xdr.getResponseHeader('X-Atmosphere-tracking-id');
+                    if (tempUUID && tempUUID != null) {
+                        request.uuid = tempUUID.split(" ").pop();
+                    }
+
+                    // HOTFIX for firefox bug: 
https://bugzilla.mozilla.org/show_bug.cgi?id=608735
+                    if (request.headers) {
+                        jQuery.each(_request.headers, function (name) {
+                            var v = xdr.getResponseHeader(name);
+                            if (v) {
+                                _response.headers[name] = v;
+                            }
+                        });
+                    }
+                } catch (e) {
+                }
+            }
+
             function _invokeFunction(response) {
                 _f(response, _request);
                 // Global
@@ -1629,6 +2102,10 @@ jQuery.atmosphere = function() {
                     func(_response);
                 };
 
+                if (_localStorageService == null && _localSocketF != null) {
+                    _localSocketF(_response.responseBody);
+                }
+
                 var messages = typeof(_response.responseBody) == 'string' ? 
_response.responseBody.split(_request.messageDelimiter) : new 
Array(_response.responseBody);
                 for (var i = 0; i < messages.length; i++) {
 
@@ -1636,6 +2113,16 @@ jQuery.atmosphere = function() {
                         continue;
                     }
                     _response.responseBody = jQuery.trim(messages[i]);
+
+                    // Ugly see issue 400.
+                    if (_response.responseBody.length == 0 && 
_response.transport == 'streaming' && _response.state == "messageReceived") {
+                        var ua = navigator.userAgent.toLowerCase();
+                        var isAndroid = ua.indexOf("android") > -1;
+                        if (isAndroid) {
+                            continue;
+                        }
+                    }
+
                     _invokeFunction(_response);
 
                     // Invoke global callbacks
@@ -1681,6 +2168,20 @@ jQuery.atmosphere = function() {
                 _invokeCallback();
 
                 _clearState();
+                
+                // Stop sharing a connection
+                if (_storageService != null) {
+                    // Clears trace timer
+                    clearInterval(_traceTimer);
+                    // Removes the trace
+                    document.cookie = encodeURIComponent("atmosphere-" + 
_request.url) + "=; expires=Thu, 01 Jan 1970 00:00:00 GMT";
+                    // The heir is the parent unless unloading
+                    _storageService.signal("close", {reason: "", heir: 
!_abordingConnection ? guid : (_storageService.get("children") || [])[0]});
+                    _storageService.close();
+                }
+                if (_localStorageService != null) {
+                    _localStorageService.close();
+                }
             }
 
             function _clearState() {
@@ -1729,7 +2230,11 @@ jQuery.atmosphere = function() {
 
             this.push = function(message) {
                 _push(message);
-            }
+            };
+
+            this.pushLocal = function(message) {
+                _intraPush(message);
+            };
 
             this.response = _response;
         },
@@ -1810,7 +2315,7 @@ jQuery.atmosphere = function() {
         checkCORSSupport : function() {
             if (jQuery.browser.msie && !window.XDomainRequest) {
                 return true;
-            } else if (jQuery.browser.opera) {
+            } else if (jQuery.browser.opera && jQuery.browser.version < 12.0) {
                 return true;
             }
 
@@ -1845,6 +2350,21 @@ jQuery.atmosphere = function() {
             return jQuery.param(data, jQuery.ajaxSettings.traditional);
         },
 
+        supportStorage : function() {
+            var storage = window.localStorage;
+            if (storage) {
+                try {
+                    storage.setItem("t", "t");
+                    storage.removeItem("t");
+                    // The storage event of Internet Explorer and Firefox 3 
works strangely
+                    return window.StorageEvent && !jQuery.browser.msie && 
!(jQuery.browser.mozilla && jQuery.browser.version.split(".")[0] === "1");
+                } catch (e) {
+                }
+            }
+
+            return false;
+        },
+
         iterate : function (fn, interval) {
             var timeoutId;
 

Reply via email to