UNPKG

jquery-sse

Version:

jQuery Plugin for Server-Sent Events (SSE) EventSource Polyfill

240 lines (203 loc) 7.59 kB
/* * jQuery Plugin for Server-Sent Events (SSE) EventSource Polyfill v0.1.3 * https://github.com/byjg/jquery-sse * * This document is licensed as free software under the terms of the * MIT License: http://www.opensource.org/licenses/mit-license.php * * Copyright (c) 2015 by JG (João Gilberto Magalhães). */ (function ($) { $.extend({ SSE: function (url, customSettings) { var sse = {instance: null, type: null}; var settings = { onOpen: function (e) { }, onEnd: function (e) { }, onError: function (e) { }, onMessage: function (e) { }, options: {}, headers: {}, events: {} }; $.extend(settings, customSettings); sse._url = url; sse._remoteHost = null; sse._settings = settings; // Start the proper EventSource object or Ajax fallback sse._start = sse.start; sse.start = function () { if (this.instance) { return false; } if (!window.EventSource || this._settings.options.forceAjax || (Object.keys(this._settings.headers).length > 0)) { createAjax(this); } else { createEventSource(this); } return true; }; // Stop the proper object sse.stop = function () { if (!this.instance) { return false; } if (!window.EventSource || this._settings.options.forceAjax || (Object.keys(this._settings.headers).length > 0)) { // Nothing to do; } else { this.instance.close(); } this._settings.onEnd(); this.instance = null; this.type = null; return true; }; return sse; } }); // Private Method for Handle EventSource object function createEventSource(me) { me.type = 'event'; me.instance = new EventSource(me._url); me.instance.successCount = 0; me.instance.onmessage = me._settings.onMessage; me.instance.onopen = function (e) { if (me.instance.successCount++ === 0) { me._settings.onOpen(e); } }; me.instance.onerror = function (e) { if (e.target.readyState === EventSource.CLOSED) { me._settings.onError(e); } }; for (var key in me._settings.events) { me.instance.addEventListener(key, me._settings.events[key], false); } } // Handle the Ajax instance (fallback) function createAjax(me) { me.type = 'ajax'; me.instance = {successCount: 0, id: null, retry: 3000, data: "", event: ""}; runAjax(me); } function handleAjax(me, receivedData) { if (!me.instance) { return; } if (me.instance.successCount++ === 0) { me._settings.onOpen(); } var lines = receivedData.split("\n"); // Process the return to generate a compatible SSE response me.instance.data = ""; var countBreakLine = 0; for (var key in lines) { var separatorPos = lines[key].indexOf(":"); var item = [ lines[key].substr(0, separatorPos), lines[key].substr(separatorPos + 1) ]; switch (item[0]) { // If the first part is empty, needed to check another sequence case "": if (!item[1] && countBreakLine++ === 1) { // Avoid comments! eventMessage = { data: me.instance.data, lastEventId: me.instance.id, origin: 'http://' + me._remoteHost, returnValue: true }; // If there are a custom event then call it if (me.instance.event && me._settings.events[me.instance.event]) { me._settings.events[me.instance.event](eventMessage); } else { me._settings.onMessage(eventMessage); } me.instance.data = ""; me.instance.event = ""; countBreakLine = 0; } break; // Define the new retry object; case "retry": countBreakLine = 0; me.instance.retry = parseInt(item[1].trim()); break; // Define the new ID case "id": countBreakLine = 0; me.instance.id = item[1].trim(); break; // Define a custom event case "event": countBreakLine = 0; me.instance.event = item[1].trim(); break; // Define the data to be processed. case "data": countBreakLine = 0; me.instance.data += (me.instance.data !== "" ? "\n" : "") + item[1].trim(); break; default: countBreakLine = 0; } } } function getRemoteHost(me) { $.ajax({ type: 'HEAD', headers: me._settings.headers, url: me._url, complete: function(xhr) { me._remoteHost = xhr.getResponseHeader('Host'); } }); } // Handle the continous Ajax request (fallback) function runAjax(me) { if (!me.instance) { return; } if (!me._remoteHost) { getRemoteHost(me); } var headers = {'Last-Event-ID': me.instance.id}; $.extend(headers, me._settings.headers); // https://stackoverflow.com/questions/7740646/jquery-read-ajax-stream-incrementally var lastResponseLen = false; var thisResponse = ""; $.ajax({ url: me._url, method: 'GET', headers: headers, xhrFields: { onprogress: function(e) { var response = e.currentTarget.response; if(lastResponseLen === false) { thisResponse += response; } else { thisResponse += response.substring(lastResponseLen); } lastResponseLen = response.length; var hasFullMessage = thisResponse.lastIndexOf("\n\n"); if (hasFullMessage >= 0) { var chunk = thisResponse.substring(0, hasFullMessage + 2); thisResponse = thisResponse.substring(hasFullMessage + 2); handleAjax(me, chunk) } } }, success: function () { setTimeout(function () { runAjax(me); }, me.instance.retry); }, error: me._settings.onError }); } })(jQuery);