diff --git a/jquery.eventsource.js b/jquery.eventsource.js index 51281a9..848d51f 100644 --- a/jquery.eventsource.js +++ b/jquery.eventsource.js @@ -1,6 +1,6 @@ /*! * jQuery.EventSource (jQuery.eventsource) - * + * * Copyright (c) 2011 Rick Waldron * Dual licensed under the MIT and GPL licenses. */ @@ -12,7 +12,7 @@ }); var stream = { - + defaults: { // Stream identity label: null, @@ -23,9 +23,10 @@ message: jQuery.noop }, setup: { - stream: {}, + stream: {}, lastEventId: 0, isHostApi: false, + retry: 500, history: {}, options: {} }, @@ -35,26 +36,39 @@ pluginFns = { public: { - close: function ( label ) { + close: function( label ) { - var cache = {}; - - if ( label !== "*" ) { + var tmp = {}; + if ( !label || label === "*" ) { for ( var prop in stream.cache ) { - if ( label !== prop ) { - cache[ prop ] = stream.cache[ prop ]; + if ( stream.cache[ prop ].isHostApi ) { + stream.cache[ prop ].stream.close(); } } + + stream.cache = {}; + + return stream.cache; } - stream.cache = cache; + for ( var prop in stream.cache ) { + if ( label !== prop ) { + tmp[ prop ] = stream.cache[ prop ]; + } else { + if ( stream.cache[ prop ].isHostApi ) { + stream.cache[ prop ].stream.close(); + } + } + } + + stream.cache = tmp; return stream.cache; - }, - streams: function ( label ) { + }, + streams: function( label ) { - if ( label === "*" ) { + if ( !label || label === "*" ) { return stream.cache; } @@ -63,104 +77,116 @@ }, _private: { - // Open a host api event source - openEventSource: function ( options ) { + // Open a host api event source + openEventSource: function( options ) { var label = options.label; - stream.cache[ label ].stream.addEventListener("open", function (event) { - if ( stream.cache[label] ) { + stream.cache[ label ].stream.addEventListener("open", function(event) { + if ( stream.cache[ label ] ) { this.label = label; - stream.cache[label].options.open.call(this, event); + stream.cache[ label ].options.open.call(this, event); } }, false); - stream.cache[label].stream.addEventListener("message", function (event) { - + stream.cache[label].stream.addEventListener("message", function(event) { + var streamData = []; - if ( stream.cache[label] ) { + if ( stream.cache[ label ] ) { streamData[ streamData.length ] = jQuery.parseJSON( event.data ); this.label = label; - stream.cache[label].lastEventId = +event.lastEventId; - stream.cache[label].history[stream.cache[label].lastEventId] = streamData; - stream.cache[label].options.message.call(this, streamData[0] ? streamData[0] : null, { + stream.cache[ label ].lastEventId = +event.lastEventId; + stream.cache[ label ].history[stream.cache[ label ].lastEventId] = streamData; + stream.cache[ label ].options.message.call(this, streamData[0] ? streamData[0] : null, { data: streamData, - lastEventId: stream.cache[label].lastEventId + lastEventId: stream.cache[ label ].lastEventId }, event); - // TODO: Add custom event triggering + // TODO: Add custom event triggering } }, false); - return stream.cache[label].stream; - }, + return stream.cache[ label ].stream; + }, // open fallback event source - openPollingSource: function ( options ) { - var label = options.label, + openPollingSource: function( options ) { + var label = options.label, source; - if ( stream.cache[label] ) { + if ( stream.cache[ label ] ) { source = jQuery.ajax({ type: "GET", url: options.url, data: options.data, - beforeSend: function () { - if ( stream.cache[label] ) { + beforeSend: function() { + if ( stream.cache[ label ] ) { this.label = label; - stream.cache[label].options.open.call( this ); + stream.cache[ label ].options.open.call( this ); } }, - success: function ( data ) { + success: function( data ) { var tempdata, label = options.label, parsedData = [], - streamData = jQuery.map( data.split("\n"), function(sdata, i) { + streamData = jQuery.map( data.split("\n\n"), function(sdata, i) { return !!sdata && sdata; - }), - idx = 0, length = streamData.length; + }), + idx = 0, length = streamData.length, + rretryprefix = /retry/, + retries; - if ( jQuery.isArray(streamData) ) { + if ( jQuery.isArray( streamData ) ) { for ( ; idx < length; idx++ ) { - if ( streamData[idx] ) { - tempdata = streamData[idx].split("data: ")[ 1 ]; + if ( streamData[ idx ] ) { - // Convert `dataType` here - if ( options.dataType === "json" ) { - tempdata = jQuery.parseJSON( tempdata ); - } + if ( rretryprefix.test( streamData[ idx ] ) && + (retries = streamData[ idx ].split("retry: ")).length ) { + + if ( retries.length === 2 && !retries[ 0 ] ) { + + stream.cache[ label ].retry = stream.cache[ label ].options.retry = +retries[ 1 ]; + } + + } else { + tempdata = streamData[ idx ].split("data: ")[ 1 ]; + + // Convert `dataType` here + if ( options.dataType === "json" ) { + tempdata = jQuery.parseJSON( tempdata ); + } - parsedData[ parsedData.length ] = tempdata; + parsedData[ parsedData.length ] = tempdata; + } } } } - if ( stream.cache[label] ) { + if ( stream.cache[ label ] ) { this.label = label; - stream.cache[label].lastEventId++; - stream.cache[label].history[stream.cache[label].lastEventId] = parsedData; - stream.cache[label].options.message.call(this, parsedData[0] ? parsedData[0] : null, { + stream.cache[ label ].lastEventId++; + stream.cache[ label ].history[ stream.cache[ label ].lastEventId ] = parsedData; + stream.cache[ label ].options.message.call(this, parsedData[0] ? parsedData[0] : null, { data: parsedData, - lastEventId: stream.cache[label].lastEventId + lastEventId: stream.cache[ label ].lastEventId }); - setTimeout( - function () { + function() { pluginFns._private.openPollingSource.call( this, options ); }, - // matches speed of host api EventSource - 500 + // Use server sent retry time if exists or default retry time if not + ( stream.cache[ label ] && stream.cache[ label ].retry ) || 500 ); } }, @@ -181,7 +207,7 @@ // Plugin sub function if ( options && !jQuery.isPlainObject( options ) && pluginFns.public[ options ] ) { // If no label was passed, send message to all streams - return pluginFns.public[ options ]( + return pluginFns.public[ options ]( arguments[1] ? arguments[1] : "*" @@ -189,7 +215,7 @@ } // If params were passed in as an object, normalize to a query string - options.data = options.data && jQuery.isPlainObject( options.data ) ? + options.data = options.data && jQuery.isPlainObject( options.data ) ? jQuery.param( options.data ) : options.data; @@ -214,7 +240,7 @@ }; - // Determine and declare `event stream` source, + // Determine and declare `event stream` source, // whether will be host api or XHR fallback streamType = !isHostApi ? // If not host api, open a polling fallback @@ -236,5 +262,10 @@ return stream.cache; }; + jQuery.each( [ "close", "streams" ], function( idx, name ) { + jQuery.eventsource[ name ] = function( arg ) { + return jQuery.eventsource( name, arg || "*" ); + }; + }); })(jQuery, window); diff --git a/test-event-sources/event-source-2.php b/test-event-sources/event-source-2.php index 66e7121..0d71cb1 100644 --- a/test-event-sources/event-source-2.php +++ b/test-event-sources/event-source-2.php @@ -1,19 +1,19 @@ - array( - 'time' => time(), - 'message' => 'Some kind of foo' - ), - 1 => array( - 'time' => time(), - 'message' => 'Some kind of quux' - ) - ) - ) . "\n"; - -?> \ No newline at end of file + array( + 'time' => time(), + 'message' => 'Some kind of foo' + ), + 1 => array( + 'time' => time(), + 'message' => 'Some kind of quux' + ) + ) + ) . "\n"; + +?> diff --git a/test-event-sources/event-source-retry.php b/test-event-sources/event-source-retry.php new file mode 100644 index 0000000..5a44f36 --- /dev/null +++ b/test-event-sources/event-source-retry.php @@ -0,0 +1,20 @@ + array( + 'time' => time(), + 'message' => 'Some kind of foo' + ), + 1 => array( + 'time' => time(), + 'message' => 'Some kind of quux' + ) + ) + ) . "\n"; + + +?> diff --git a/test/index.html b/test/index.html index 41b7f08..b5ff890 100644 --- a/test/index.html +++ b/test/index.html @@ -3,12 +3,12 @@