message_bus 3.3.5 → 3.3.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of message_bus might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/.eslintrc.js +13 -0
- data/.gitignore +2 -0
- data/CHANGELOG +8 -0
- data/Gemfile +2 -0
- data/README.md +25 -0
- data/Rakefile +1 -1
- data/assets/message-bus.js +69 -54
- data/bench/codecs/all_codecs.rb +39 -0
- data/bench/codecs/marshal.rb +11 -0
- data/bench/codecs/packed_string.rb +67 -0
- data/bench/codecs/string_hack.rb +47 -0
- data/bench/codecs_large_user_list.rb +29 -0
- data/bench/codecs_standard_message.rb +29 -0
- data/lib/message_bus.rb +32 -17
- data/lib/message_bus/backends/base.rb +0 -2
- data/lib/message_bus/backends/memory.rb +0 -2
- data/lib/message_bus/backends/postgres.rb +0 -2
- data/lib/message_bus/backends/redis.rb +2 -4
- data/lib/message_bus/codec/base.rb +18 -0
- data/lib/message_bus/codec/json.rb +15 -0
- data/lib/message_bus/codec/oj.rb +21 -0
- data/lib/message_bus/version.rb +1 -1
- data/package-lock.json +2192 -0
- data/package.json +7 -2
- data/spec/lib/message_bus_spec.rb +1 -1
- metadata +14 -4
- data/lib/message_bus/em_ext.rb +0 -6
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 52ac95c63b3775df984e4900aff333c2ea9f9bab7e79d16b85909048247fb709
         | 
| 4 | 
            +
              data.tar.gz: 879fd243f8947150fe8a31307d023b6ca719192568e0def45255dfdddaf1e34f
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: fe7252d6965628dd75027af80f775126394e6b3a81be01fd03f538f17fe5e7874e997cea552ee2e92d91b7ee47d17059755e7df995c5b8bab24049ce2746e0a8
         | 
| 7 | 
            +
              data.tar.gz: 2c4ef234bab5ee1701b5bb2d7362929128855dda113a2531dd35f025b9d2be11a2c18a287aa25731e73a9e8f82af898e2f7725a7c9ea4200a390eaff633b4098
         | 
    
        data/.eslintrc.js
    ADDED
    
    
    
        data/.gitignore
    CHANGED
    
    
    
        data/CHANGELOG
    CHANGED
    
    | @@ -1,3 +1,11 @@ | |
| 1 | 
            +
            31-05-2021
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            - Version 3.3.6
         | 
| 4 | 
            +
             | 
| 5 | 
            +
              - FEATURE: Introduce support for transport codecs
         | 
| 6 | 
            +
              - FIX: event subscription leak in JS after start/stop/start sequence
         | 
| 7 | 
            +
              - FEATURE: MessageBus.onVisibilityChange() can be used to trigger a visiblity change check by hand
         | 
| 8 | 
            +
             | 
| 1 9 | 
             
            28-04-2021
         | 
| 2 10 |  | 
| 3 11 | 
             
            - Version 3.3.5
         | 
    
        data/Gemfile
    CHANGED
    
    
    
        data/README.md
    CHANGED
    
    | @@ -435,6 +435,31 @@ MessageBus.configure(backend: :memory) | |
| 435 435 |  | 
| 436 436 | 
             
            The `:clear_every` option supported by the PostgreSQL backend is also supported by the in-memory backend.
         | 
| 437 437 |  | 
| 438 | 
            +
             | 
| 439 | 
            +
            ### Transport codecs
         | 
| 440 | 
            +
             | 
| 441 | 
            +
            By default MessageBus serializes messages to the backend using JSON. Under most situation this performs extremely well.
         | 
| 442 | 
            +
             | 
| 443 | 
            +
            In some exceptional cases you may consider a different transport codec. To configure a custom codec use:
         | 
| 444 | 
            +
             | 
| 445 | 
            +
            ```ruby
         | 
| 446 | 
            +
            MessageBus.configure(transport_codec: codec)
         | 
| 447 | 
            +
            ```
         | 
| 448 | 
            +
             | 
| 449 | 
            +
            A codec class must implement MessageBus::Codec::Base. Specifically an `encode` and `decode` method.
         | 
| 450 | 
            +
             | 
| 451 | 
            +
            See the `bench` directory for examples where the default JSON codec can perform poorly. A specific examples may be
         | 
| 452 | 
            +
            attempting to distribute a message to a restricted list of thousands of users. In cases like this you may consider
         | 
| 453 | 
            +
            using a packed string encoder.
         | 
| 454 | 
            +
             | 
| 455 | 
            +
            Keep in mind, much of MessageBus internals and supporting tools expect data to be converted to JSON and back, if you use a naive (and fast) `Marshal` based codec you may need to limit the features you use. Specifically the Postgresql backend expects the codec never to return a string with `\u0000`, additionally some classes like DistributedCache expect keys to be converted to Strings.
         | 
| 456 | 
            +
             | 
| 457 | 
            +
            Another example may be very large and complicated messages where Oj in compatability mode outperforms JSON. To opt for the Oj codec use:
         | 
| 458 | 
            +
             | 
| 459 | 
            +
            ```
         | 
| 460 | 
            +
            MessageBus.configure(transport_codec: MessageBus::Codec::Oj.new)
         | 
| 461 | 
            +
            ```
         | 
| 462 | 
            +
             | 
| 438 463 | 
             
            ### Forking/threading app servers
         | 
| 439 464 |  | 
| 440 465 | 
             
            If you're using a forking or threading app server and you're not getting immediate delivery of published messages, you might need to configure your web server to re-connect to the message_bus backend
         | 
    
        data/Rakefile
    CHANGED
    
    
    
        data/assets/message-bus.js
    CHANGED
    
    | @@ -1,9 +1,9 @@ | |
| 1 | 
            -
            /* | 
| 1 | 
            +
            /*global define, jQuery*/
         | 
| 2 2 |  | 
| 3 3 | 
             
            (function (root, factory) {
         | 
| 4 | 
            -
              if (typeof define ===  | 
| 4 | 
            +
              if (typeof define === "function" && define.amd) {
         | 
| 5 5 | 
             
                // AMD. Register as an anonymous module.
         | 
| 6 | 
            -
                define([], function ( | 
| 6 | 
            +
                define([], function () {
         | 
| 7 7 | 
             
                  // Also create a global in case some scripts
         | 
| 8 8 | 
             
                  // that are loaded still are looking for
         | 
| 9 9 | 
             
                  // a global even when an AMD loader is in use.
         | 
| @@ -13,12 +13,12 @@ | |
| 13 13 | 
             
                // Browser globals
         | 
| 14 14 | 
             
                root.MessageBus = factory();
         | 
| 15 15 | 
             
              }
         | 
| 16 | 
            -
            }(typeof self !==  | 
| 16 | 
            +
            })(typeof self !== "undefined" ? self : this, function () {
         | 
| 17 17 | 
             
              "use strict";
         | 
| 18 18 |  | 
| 19 19 | 
             
              // http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript
         | 
| 20 | 
            -
              var uniqueId = function() {
         | 
| 21 | 
            -
                return "xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx".replace(/[xy]/g, function(c) {
         | 
| 20 | 
            +
              var uniqueId = function () {
         | 
| 21 | 
            +
                return "xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx".replace(/[xy]/g, function (c) {
         | 
| 22 22 | 
             
                  var r = (Math.random() * 16) | 0;
         | 
| 23 23 | 
             
                  var v = c === "x" ? r : (r & 0x3) | 0x8;
         | 
| 24 24 | 
             
                  return v.toString(16);
         | 
| @@ -42,7 +42,7 @@ | |
| 42 42 | 
             
              var totalAjaxCalls = 0;
         | 
| 43 43 | 
             
              var lastAjax;
         | 
| 44 44 |  | 
| 45 | 
            -
              var isHidden = (function() {
         | 
| 45 | 
            +
              var isHidden = (function () {
         | 
| 46 46 | 
             
                var prefixes = ["", "webkit", "ms", "moz"];
         | 
| 47 47 | 
             
                var hiddenProperty;
         | 
| 48 48 | 
             
                for (var i = 0; i < prefixes.length; i++) {
         | 
| @@ -53,7 +53,7 @@ | |
| 53 53 | 
             
                  }
         | 
| 54 54 | 
             
                }
         | 
| 55 55 |  | 
| 56 | 
            -
                return function() {
         | 
| 56 | 
            +
                return function () {
         | 
| 57 57 | 
             
                  if (hiddenProperty !== undefined) {
         | 
| 58 58 | 
             
                    return document[hiddenProperty];
         | 
| 59 59 | 
             
                  } else {
         | 
| @@ -62,7 +62,7 @@ | |
| 62 62 | 
             
                };
         | 
| 63 63 | 
             
              })();
         | 
| 64 64 |  | 
| 65 | 
            -
              var hasLocalStorage = (function() {
         | 
| 65 | 
            +
              var hasLocalStorage = (function () {
         | 
| 66 66 | 
             
                try {
         | 
| 67 67 | 
             
                  localStorage.setItem("mbTestLocalStorage", Date.now());
         | 
| 68 68 | 
             
                  localStorage.removeItem("mbTestLocalStorage");
         | 
| @@ -72,13 +72,13 @@ | |
| 72 72 | 
             
                }
         | 
| 73 73 | 
             
              })();
         | 
| 74 74 |  | 
| 75 | 
            -
              var updateLastAjax = function() {
         | 
| 75 | 
            +
              var updateLastAjax = function () {
         | 
| 76 76 | 
             
                if (hasLocalStorage) {
         | 
| 77 77 | 
             
                  localStorage.setItem("__mbLastAjax", Date.now());
         | 
| 78 78 | 
             
                }
         | 
| 79 79 | 
             
              };
         | 
| 80 80 |  | 
| 81 | 
            -
              var hiddenTabShouldWait = function() {
         | 
| 81 | 
            +
              var hiddenTabShouldWait = function () {
         | 
| 82 82 | 
             
                if (hasLocalStorage && isHidden()) {
         | 
| 83 83 | 
             
                  var lastAjaxCall = parseInt(localStorage.getItem("__mbLastAjax"), 10);
         | 
| 84 84 | 
             
                  var deltaAjax = Date.now() - lastAjaxCall;
         | 
| @@ -89,19 +89,21 @@ | |
| 89 89 | 
             
              };
         | 
| 90 90 |  | 
| 91 91 | 
             
              var hasonprogress = new XMLHttpRequest().onprogress === null;
         | 
| 92 | 
            -
              var allowChunked = function() {
         | 
| 92 | 
            +
              var allowChunked = function () {
         | 
| 93 93 | 
             
                return me.enableChunkedEncoding && hasonprogress;
         | 
| 94 94 | 
             
              };
         | 
| 95 95 |  | 
| 96 | 
            -
              var shouldLongPoll = function() {
         | 
| 96 | 
            +
              var shouldLongPoll = function () {
         | 
| 97 97 | 
             
                return (
         | 
| 98 98 | 
             
                  me.alwaysLongPoll ||
         | 
| 99 99 | 
             
                  (me.shouldLongPollCallback ? me.shouldLongPollCallback() : !isHidden())
         | 
| 100 100 | 
             
                );
         | 
| 101 101 | 
             
              };
         | 
| 102 102 |  | 
| 103 | 
            -
              var processMessages = function(messages) {
         | 
| 104 | 
            -
                if ( | 
| 103 | 
            +
              var processMessages = function (messages) {
         | 
| 104 | 
            +
                if (!messages || messages.length === 0) {
         | 
| 105 | 
            +
                  return false;
         | 
| 106 | 
            +
                }
         | 
| 105 107 |  | 
| 106 108 | 
             
                for (var i = 0; i < messages.length; i++) {
         | 
| 107 109 | 
             
                  var message = messages[i];
         | 
| @@ -133,7 +135,7 @@ | |
| 133 135 | 
             
                return true;
         | 
| 134 136 | 
             
              };
         | 
| 135 137 |  | 
| 136 | 
            -
              var reqSuccess = function(messages) {
         | 
| 138 | 
            +
              var reqSuccess = function (messages) {
         | 
| 137 139 | 
             
                failCount = 0;
         | 
| 138 140 | 
             
                if (paused) {
         | 
| 139 141 | 
             
                  if (messages) {
         | 
| @@ -147,7 +149,7 @@ | |
| 147 149 | 
             
                return false;
         | 
| 148 150 | 
             
              };
         | 
| 149 151 |  | 
| 150 | 
            -
              var longPoller = function(poll, data) {
         | 
| 152 | 
            +
              var longPoller = function (poll, data) {
         | 
| 151 153 | 
             
                if (ajaxInProgress) {
         | 
| 152 154 | 
             
                  // never allow concurrent ajax reqs
         | 
| 153 155 | 
             
                  return;
         | 
| @@ -180,7 +182,7 @@ | |
| 180 182 |  | 
| 181 183 | 
             
                var dataType = chunked ? "text" : "json";
         | 
| 182 184 |  | 
| 183 | 
            -
                var handle_progress = function(payload, position) {
         | 
| 185 | 
            +
                var handle_progress = function (payload, position) {
         | 
| 184 186 | 
             
                  var separator = "\r\n|\r\n";
         | 
| 185 187 | 
             
                  var endChunk = payload.indexOf(separator, position);
         | 
| 186 188 |  | 
| @@ -203,7 +205,7 @@ | |
| 203 205 | 
             
                  return handle_progress(payload, endChunk + separator.length);
         | 
| 204 206 | 
             
                };
         | 
| 205 207 |  | 
| 206 | 
            -
                var disableChunked = function() {
         | 
| 208 | 
            +
                var disableChunked = function () {
         | 
| 207 209 | 
             
                  if (me.longPoll) {
         | 
| 208 210 | 
             
                    me.longPoll.abort();
         | 
| 209 211 | 
             
                    chunkedBackoff = 30;
         | 
| @@ -231,12 +233,12 @@ | |
| 231 233 | 
             
                  headers: headers,
         | 
| 232 234 | 
             
                  messageBus: {
         | 
| 233 235 | 
             
                    chunked: chunked,
         | 
| 234 | 
            -
                    onProgressListener: function(xhr) {
         | 
| 236 | 
            +
                    onProgressListener: function (xhr) {
         | 
| 235 237 | 
             
                      var position = 0;
         | 
| 236 238 | 
             
                      // if it takes longer than 3000 ms to get first chunk, we have some proxy
         | 
| 237 239 | 
             
                      // this is messing with us, so just backoff from using chunked for now
         | 
| 238 240 | 
             
                      var chunkedTimeout = setTimeout(disableChunked, 3000);
         | 
| 239 | 
            -
                      return (xhr.onprogress = function() {
         | 
| 241 | 
            +
                      return (xhr.onprogress = function () {
         | 
| 240 242 | 
             
                        clearTimeout(chunkedTimeout);
         | 
| 241 243 | 
             
                        if (
         | 
| 242 244 | 
             
                          xhr.getResponseHeader("Content-Type") ===
         | 
| @@ -247,9 +249,9 @@ | |
| 247 249 | 
             
                          position = handle_progress(xhr.responseText, position);
         | 
| 248 250 | 
             
                        }
         | 
| 249 251 | 
             
                      });
         | 
| 250 | 
            -
                    }
         | 
| 252 | 
            +
                    },
         | 
| 251 253 | 
             
                  },
         | 
| 252 | 
            -
                  xhr: function() {
         | 
| 254 | 
            +
                  xhr: function () {
         | 
| 253 255 | 
             
                    var xhr = jQuery.ajaxSettings.xhr();
         | 
| 254 256 | 
             
                    if (!chunked) {
         | 
| 255 257 | 
             
                      return xhr;
         | 
| @@ -257,7 +259,7 @@ | |
| 257 259 | 
             
                    this.messageBus.onProgressListener(xhr);
         | 
| 258 260 | 
             
                    return xhr;
         | 
| 259 261 | 
             
                  },
         | 
| 260 | 
            -
                  success: function(messages) {
         | 
| 262 | 
            +
                  success: function (messages) {
         | 
| 261 263 | 
             
                    if (!chunked) {
         | 
| 262 264 | 
             
                      // we may have requested text so jQuery will not parse
         | 
| 263 265 | 
             
                      if (typeof messages === "string") {
         | 
| @@ -266,7 +268,7 @@ | |
| 266 268 | 
             
                      gotData = reqSuccess(messages);
         | 
| 267 269 | 
             
                    }
         | 
| 268 270 | 
             
                  },
         | 
| 269 | 
            -
                  error: function(xhr, textStatus | 
| 271 | 
            +
                  error: function (xhr, textStatus) {
         | 
| 270 272 | 
             
                    if (xhr.status === 429) {
         | 
| 271 273 | 
             
                      var tryAfter =
         | 
| 272 274 | 
             
                        parseInt(
         | 
| @@ -285,7 +287,7 @@ | |
| 285 287 | 
             
                      totalAjaxFailures += 1;
         | 
| 286 288 | 
             
                    }
         | 
| 287 289 | 
             
                  },
         | 
| 288 | 
            -
                  complete: function() {
         | 
| 290 | 
            +
                  complete: function () {
         | 
| 289 291 | 
             
                    ajaxInProgress = false;
         | 
| 290 292 |  | 
| 291 293 | 
             
                    var interval;
         | 
| @@ -323,14 +325,14 @@ | |
| 323 325 | 
             
                    }
         | 
| 324 326 |  | 
| 325 327 | 
             
                    if (started) {
         | 
| 326 | 
            -
                      pollTimeout = setTimeout(function() {
         | 
| 328 | 
            +
                      pollTimeout = setTimeout(function () {
         | 
| 327 329 | 
             
                        pollTimeout = null;
         | 
| 328 330 | 
             
                        poll();
         | 
| 329 331 | 
             
                      }, interval);
         | 
| 330 332 | 
             
                    }
         | 
| 331 333 |  | 
| 332 334 | 
             
                    me.longPoll = null;
         | 
| 333 | 
            -
                  }
         | 
| 335 | 
            +
                  },
         | 
| 334 336 | 
             
                });
         | 
| 335 337 |  | 
| 336 338 | 
             
                return req;
         | 
| @@ -352,7 +354,7 @@ | |
| 352 354 | 
             
                baseUrl: baseUrl,
         | 
| 353 355 | 
             
                headers: {},
         | 
| 354 356 | 
             
                ajax: typeof jQuery !== "undefined" && jQuery.ajax,
         | 
| 355 | 
            -
                diagnostics: function() {
         | 
| 357 | 
            +
                diagnostics: function () {
         | 
| 356 358 | 
             
                  console.log("Stopped: " + stopped + " Started: " + started);
         | 
| 357 359 | 
             
                  console.log("Current callbacks");
         | 
| 358 360 | 
             
                  console.log(callbacks);
         | 
| @@ -369,42 +371,50 @@ | |
| 369 371 | 
             
                  );
         | 
| 370 372 | 
             
                },
         | 
| 371 373 |  | 
| 372 | 
            -
                pause: function() {
         | 
| 374 | 
            +
                pause: function () {
         | 
| 373 375 | 
             
                  paused = true;
         | 
| 374 376 | 
             
                },
         | 
| 375 377 |  | 
| 376 | 
            -
                resume: function() {
         | 
| 378 | 
            +
                resume: function () {
         | 
| 377 379 | 
             
                  paused = false;
         | 
| 378 380 | 
             
                  processMessages(later);
         | 
| 379 381 | 
             
                  later = [];
         | 
| 380 382 | 
             
                },
         | 
| 381 383 |  | 
| 382 | 
            -
                stop: function() {
         | 
| 384 | 
            +
                stop: function () {
         | 
| 383 385 | 
             
                  stopped = true;
         | 
| 384 386 | 
             
                  started = false;
         | 
| 385 387 | 
             
                  if (delayPollTimeout) {
         | 
| 386 388 | 
             
                    clearTimeout(delayPollTimeout);
         | 
| 387 389 | 
             
                    delayPollTimeout = null;
         | 
| 388 390 | 
             
                  }
         | 
| 391 | 
            +
                  if (pollTimeout) {
         | 
| 392 | 
            +
                    clearTimeout(pollTimeout);
         | 
| 393 | 
            +
                    pollTimeout = null;
         | 
| 394 | 
            +
                  }
         | 
| 389 395 | 
             
                  if (me.longPoll) {
         | 
| 390 396 | 
             
                    me.longPoll.abort();
         | 
| 391 397 | 
             
                  }
         | 
| 398 | 
            +
                  if (me.onVisibilityChange) {
         | 
| 399 | 
            +
                    document.removeEventListener("visibilitychange", me.onVisibilityChange);
         | 
| 400 | 
            +
                    me.onVisibilityChange = null;
         | 
| 401 | 
            +
                  }
         | 
| 392 402 | 
             
                },
         | 
| 393 403 |  | 
| 394 404 | 
             
                // Start polling
         | 
| 395 | 
            -
                start: function() {
         | 
| 405 | 
            +
                start: function () {
         | 
| 396 406 | 
             
                  if (started) return;
         | 
| 397 407 | 
             
                  started = true;
         | 
| 398 408 | 
             
                  stopped = false;
         | 
| 399 409 |  | 
| 400 | 
            -
                  var poll = function() {
         | 
| 410 | 
            +
                  var poll = function () {
         | 
| 401 411 | 
             
                    if (stopped) {
         | 
| 402 412 | 
             
                      return;
         | 
| 403 413 | 
             
                    }
         | 
| 404 414 |  | 
| 405 415 | 
             
                    if (callbacks.length === 0 || hiddenTabShouldWait()) {
         | 
| 406 416 | 
             
                      if (!delayPollTimeout) {
         | 
| 407 | 
            -
                        delayPollTimeout = setTimeout(function() {
         | 
| 417 | 
            +
                        delayPollTimeout = setTimeout(function () {
         | 
| 408 418 | 
             
                          delayPollTimeout = null;
         | 
| 409 419 | 
             
                          poll();
         | 
| 410 420 | 
             
                        }, parseInt(500 + Math.random() * 500));
         | 
| @@ -426,25 +436,28 @@ | |
| 426 436 |  | 
| 427 437 | 
             
                  // monitor visibility, issue a new long poll when the page shows
         | 
| 428 438 | 
             
                  if (document.addEventListener && "hidden" in document) {
         | 
| 429 | 
            -
                    me. | 
| 430 | 
            -
                       | 
| 431 | 
            -
             | 
| 432 | 
            -
                         | 
| 433 | 
            -
             | 
| 434 | 
            -
             | 
| 435 | 
            -
             | 
| 436 | 
            -
             | 
| 437 | 
            -
             | 
| 438 | 
            -
             | 
| 439 | 
            -
                         | 
| 439 | 
            +
                    me.onVisibilityChange = function () {
         | 
| 440 | 
            +
                      if (
         | 
| 441 | 
            +
                        !document.hidden &&
         | 
| 442 | 
            +
                        !me.longPoll &&
         | 
| 443 | 
            +
                        (pollTimeout || delayPollTimeout)
         | 
| 444 | 
            +
                      ) {
         | 
| 445 | 
            +
                        clearTimeout(pollTimeout);
         | 
| 446 | 
            +
                        clearTimeout(delayPollTimeout);
         | 
| 447 | 
            +
             | 
| 448 | 
            +
                        delayPollTimeout = null;
         | 
| 449 | 
            +
                        pollTimeout = null;
         | 
| 450 | 
            +
                        poll();
         | 
| 440 451 | 
             
                      }
         | 
| 441 | 
            -
                     | 
| 452 | 
            +
                    };
         | 
| 453 | 
            +
             | 
| 454 | 
            +
                    document.addEventListener("visibilitychange", me.onVisibilityChange);
         | 
| 442 455 | 
             
                  }
         | 
| 443 456 |  | 
| 444 457 | 
             
                  poll();
         | 
| 445 458 | 
             
                },
         | 
| 446 459 |  | 
| 447 | 
            -
                status: function() {
         | 
| 460 | 
            +
                status: function () {
         | 
| 448 461 | 
             
                  if (paused) {
         | 
| 449 462 | 
             
                    return "paused";
         | 
| 450 463 | 
             
                  } else if (started) {
         | 
| @@ -463,7 +476,7 @@ | |
| 463 476 | 
             
                // -2 will recieve last message + all new messages
         | 
| 464 477 | 
             
                // -3 will recieve last 2 messages + all new messages
         | 
| 465 478 | 
             
                // if undefined will default to -1
         | 
| 466 | 
            -
                subscribe: function(channel, func, lastId) {
         | 
| 479 | 
            +
                subscribe: function (channel, func, lastId) {
         | 
| 467 480 | 
             
                  if (!started && !stopped) {
         | 
| 468 481 | 
             
                    me.start();
         | 
| 469 482 | 
             
                  }
         | 
| @@ -471,7 +484,9 @@ | |
| 471 484 | 
             
                  if (lastId === null || typeof lastId === "undefined") {
         | 
| 472 485 | 
             
                    lastId = -1;
         | 
| 473 486 | 
             
                  } else if (typeof lastId !== "number") {
         | 
| 474 | 
            -
                    throw  | 
| 487 | 
            +
                    throw (
         | 
| 488 | 
            +
                      "lastId has type " + typeof lastId + " but a number was expected."
         | 
| 489 | 
            +
                    );
         | 
| 475 490 | 
             
                  }
         | 
| 476 491 |  | 
| 477 492 | 
             
                  if (typeof channel !== "string") {
         | 
| @@ -481,7 +496,7 @@ | |
| 481 496 | 
             
                  callbacks.push({
         | 
| 482 497 | 
             
                    channel: channel,
         | 
| 483 498 | 
             
                    func: func,
         | 
| 484 | 
            -
                    last_id: lastId
         | 
| 499 | 
            +
                    last_id: lastId,
         | 
| 485 500 | 
             
                  });
         | 
| 486 501 | 
             
                  if (me.longPoll) {
         | 
| 487 502 | 
             
                    me.longPoll.abort();
         | 
| @@ -491,7 +506,7 @@ | |
| 491 506 | 
             
                },
         | 
| 492 507 |  | 
| 493 508 | 
             
                // Unsubscribe from a channel
         | 
| 494 | 
            -
                unsubscribe: function(channel, func) {
         | 
| 509 | 
            +
                unsubscribe: function (channel, func) {
         | 
| 495 510 | 
             
                  // TODO allow for globbing in the middle of a channel name
         | 
| 496 511 | 
             
                  // like /something/*/something
         | 
| 497 512 | 
             
                  // at the moment we only support globbing /something/*
         | 
| @@ -528,7 +543,7 @@ | |
| 528 543 | 
             
                  }
         | 
| 529 544 |  | 
| 530 545 | 
             
                  return removed;
         | 
| 531 | 
            -
                }
         | 
| 546 | 
            +
                },
         | 
| 532 547 | 
             
              };
         | 
| 533 548 | 
             
              return me;
         | 
| 534 | 
            -
            }) | 
| 549 | 
            +
            });
         |