changeset 45:27785e0ac4d8

Added error detection on the client-side.
author Atul Varma <varmaa@toolness.com>
date Tue, 28 Apr 2009 10:03:51 -0700
parents d6467f3845ad
children 346573b610cd
files openwebchat.css openwebchat.html openwebchat.js openwebchat.py
diffstat 4 files changed, 76 insertions(+), 31 deletions(-) [+]
line wrap: on
line diff
--- a/openwebchat.css	Tue Apr 28 08:32:25 2009 -0700
+++ b/openwebchat.css	Tue Apr 28 10:03:51 2009 -0700
@@ -53,3 +53,10 @@
 #templates {
     display: none;
 }
+
+.error {
+    background: red;
+    padding: 1em;
+    margin-top: 1em;
+    margin-bottom: 1em;
+}
--- a/openwebchat.html	Tue Apr 28 08:32:25 2009 -0700
+++ b/openwebchat.html	Tue Apr 28 10:03:51 2009 -0700
@@ -15,6 +15,11 @@
 <p>Your name is <input type="text" id="name"/>.</p>
 </div>
 <div id="templates">
+  <div class="error">
+    It appears that your connection to the server has been
+    severed. Please reload this page to reconnect. (Sorry, this will
+    be more user-friendly in the future!)
+  </div>
   <div class="message">
     <div class="author"></div>
     <div class="timestamp"></div>
--- a/openwebchat.js	Tue Apr 28 08:32:25 2009 -0700
+++ b/openwebchat.js	Tue Apr 28 10:03:51 2009 -0700
@@ -1,13 +1,29 @@
 var OpenWebChat = {
-  startMessageListener: function startMessageListener(onMessage) {
+  startMessageListener: function startMessageListener(options) {
     var req = new XMLHttpRequest();
     req.multipart = true;
     req.open('GET', 'listen/multipart', true);
     req.overrideMimeType('application/json');
     req.addEventListener(
+      "error",
+      function() {
+        options.onError(null);
+      },
+      false
+    );
+    req.addEventListener(
       "load",
       function onload(evt) {
-        onMessage(JSON.parse(req.responseText));
+        var msg;
+        var errorOccurred = false;
+        try {
+          msg = JSON.parse(req.responseText);
+        } catch (e) {
+          options.onError(e);
+          errorOccurred = true;
+        }
+        if (!errorOccurred)
+          options.onMessage(msg);
       },
       false
     );
@@ -46,30 +62,39 @@
       });
 
     OpenWebChat.startMessageListener(
-      function(msg) {
-        var block = $('#templates .message').clone();
+      {onMessage: function onMessage(msg) {
+         var block = $('#templates .message').clone();
 
-        // Try to dynamically determine if the message is HTML or not.
-        var parser = new DOMParser();
-        var dom = parser.parseFromString('<xml>' + msg.content + '</xml>',
-                                         'text/xml');
-        if (dom.firstChild.nodeName == 'parsererror')
-          $('.content', block).text(msg.content);
-        else
-          $('.content', block).html(msg.content);
+         // Try to dynamically determine if the message is HTML or not.
+         var parser = new DOMParser();
+         var dom = parser.parseFromString('<xml>' + msg.content + '</xml>',
+                                          'text/xml');
+         if (dom.firstChild.nodeName == 'parsererror')
+           $('.content', block).text(msg.content);
+         else
+           $('.content', block).html(msg.content);
 
-        var author = msg.author ? msg.author : 'Anonymous';
-        if (author != $('#content .author:last').text())
-          $('.author', block).text(author);
-        else
-          $('.author', block).remove();
+         var author = msg.author ? msg.author : 'Anonymous';
+         if (author != $('#content .author:last').text())
+           $('.author', block).text(author);
+         else
+           $('.author', block).remove();
+
+         $('.timestamp', block).text(msg.time);
 
-        $('.timestamp', block).text(msg.time);
-
-        block.hide();
-        $('#incoming-messages').append(block);
-        block.slideDown(function() {
-                          window.scrollTo(0, $(window).height());
-                        });
+         block.hide();
+         $('#incoming-messages').append(block);
+         block.slideDown(function() {
+                           window.scrollTo(0, $(window).height());
+                         });
+       },
+       onError: function onError(exception) {
+         if (window.console)
+           window.console.log('The error', exception, 'occurred.');
+         var error = $('#templates .error').clone();
+         error.hide();
+         $('#incoming-messages').append(error);
+         error.slideDown();
+       }
       });
   });
--- a/openwebchat.py	Tue Apr 28 08:32:25 2009 -0700
+++ b/openwebchat.py	Tue Apr 28 10:03:51 2009 -0700
@@ -86,21 +86,29 @@
         while 1:
             yield self._until_one_request_processed(addr)
 
+    def __multipart_boundary(self, boundary):
+        # Here we actually declare the content type and start the
+        # transfer of the document itself; this is needed to
+        # trigger an error on the browser-side, because a closed
+        # connection during any other phase is unlikely to
+        # cause any event to trigger on the client webpage.
+        return '\r\n'.join(('--%s' % boundary,
+                            'Content-Type: text/plain',
+                            '',
+                            ''))
+
     def _until_multipart_header_sent(self, boundary):
         yield self._until_http_response_sent(
-            '--%s\r\n' % boundary,
+            self.__multipart_boundary(boundary),
             mimetype = ('multipart/x-mixed-replace; '
                         'boundary="%s"' % boundary))
 
     def _until_multipart_part_sent(self, boundary, msg):
         yield until_sent('\r\n'.join(
-                ('Content-Length: %d' % len(msg),
-                 'Content-Type: text/plain',
+                (msg,
                  '',
-                 msg,
-                 '',
-                 '--%s' % boundary,
-                 '')))
+                 self.__multipart_boundary(boundary))
+                ))
 
     def _until_one_request_processed(self, addr):
         request = yield until_received(terminator = '\r\n\r\n')