191 lines
10 KiB
JavaScript
191 lines
10 KiB
JavaScript
/*!
|
|
* jQuery.EventSource (jQuery.eventsource)
|
|
*
|
|
* Copyright (c) 2011 Rick Waldron
|
|
* Dual licensed under the MIT and GPL licenses.
|
|
*/
|
|
!function(jQuery, global) {
|
|
jQuery.extend(jQuery.ajaxSettings.accepts, {
|
|
stream: "text/event-stream"
|
|
});
|
|
var stream = {
|
|
defaults: {
|
|
label: null,
|
|
url: null,
|
|
open: jQuery.noop,
|
|
error: jQuery.noop,
|
|
message: jQuery.noop
|
|
},
|
|
setup: {
|
|
stream: {},
|
|
lastEventId: 0,
|
|
isHostApi: !1,
|
|
retry: 500,
|
|
options: {}
|
|
},
|
|
cache: {}
|
|
}, pluginFns = {
|
|
"public": {
|
|
close: function(label) {
|
|
var tmp = {};
|
|
if (!label || "*" === label) {
|
|
for (var prop in stream.cache) stream.cache[prop].isHostApi && stream.cache[prop].stream.close();
|
|
return stream.cache = {}, stream.cache;
|
|
}
|
|
for (var prop in stream.cache) label !== prop ? tmp[prop] = stream.cache[prop] : stream.cache[prop].isHostApi ? stream.cache[prop].stream.close() : stream.cache[prop].isClosed = !0;
|
|
return stream.cache = tmp, stream.cache;
|
|
},
|
|
removeEventListener: function(label, evtType) {
|
|
if (!label || "*" === label) {
|
|
for (var prop in stream.cache) stream.cache[prop].isHostApi ? stream.cache[prop].stream.removeEventListener(evtType, stream.cache[prop][evtType + "Callback"]) : stream.cache[prop].options[evtType] = $.noop;
|
|
return !0;
|
|
}
|
|
for (var prop in stream.cache) label == prop && (stream.cache[prop].isHostApi ? (stream.cache[prop].stream.removeEventListener(evtType, stream.cache[prop][evtType + "Callback"]),
|
|
stream.cache[prop].options[evtType + "Callback"] = jQuery.noop) : stream.cache[prop].options[evtType] = $.noop);
|
|
return !0;
|
|
},
|
|
addEventListener: function(label, evtType, callback) {
|
|
if (stream.cache[label][evtType + "Callback"] = function(event) {
|
|
var streamData = [];
|
|
stream.cache[label] && (this.label = label, "message" === evtType ? (streamData[streamData.length] = JSON.parse(event.data),
|
|
stream.cache[label].lastEventId = +event.lastEventId, callback.call(this, streamData[0] ? streamData[0] : null, {
|
|
data: streamData,
|
|
lastEventId: stream.cache[label].lastEventId
|
|
}, event)) : callback.call(this, event));
|
|
}, stream.cache[label].options[evtType + "Callback"] = callback, !label || "*" === label) {
|
|
for (var prop in stream.cache) stream.cache[prop].isHostApi ? stream.cache[prop].stream.addEventListener(evtType, stream.cache[prop][evtType + "Callback"], !1) : (stream.cache[prop].options[evtType] = $.noop,
|
|
stream.cache[prop].options[evtType] = callback);
|
|
return !0;
|
|
}
|
|
for (var prop in stream.cache) label == prop && (stream.cache[prop].isHostApi ? stream.cache[prop].stream.addEventListener(evtType, stream.cache[label][evtType + "Callback"], !1) : (stream.cache[prop].options[evtType] = $.noop,
|
|
stream.cache[prop].options[evtType] = callback));
|
|
return !0;
|
|
},
|
|
streams: function(label) {
|
|
return label && "*" !== label ? stream.cache[label] || {} : stream.cache;
|
|
}
|
|
},
|
|
_private: {
|
|
openEventSource: function(options) {
|
|
var label = options.label;
|
|
return stream.cache[label].openCallback = function(event) {
|
|
stream.cache[label] && (this.label = label, stream.cache[label].options.open.call(this, event));
|
|
}, stream.cache[label].errorCallback = function(event) {
|
|
stream.cache[label] && (this.label = label, stream.cache[label].options.error.call(this, event));
|
|
}, stream.cache[label].messageCallback = function(event) {
|
|
var streamData = [];
|
|
stream.cache[label] && (streamData[streamData.length] = JSON.parse(event.data),
|
|
this.label = label, stream.cache[label].lastEventId = +event.lastEventId, stream.cache[label].options.message.call(this, streamData[0] ? streamData[0] : null, {
|
|
data: streamData,
|
|
lastEventId: stream.cache[label].lastEventId
|
|
}, event));
|
|
}, stream.cache[label].stream.addEventListener("open", stream.cache[label].openCallback, !1),
|
|
stream.cache[label].stream.addEventListener("error", stream.cache[label].errorCallback, !1),
|
|
stream.cache[label].stream.addEventListener("message", stream.cache[label].messageCallback, !1),
|
|
stream.cache[label].stream;
|
|
},
|
|
openPollingSource: function(options) {
|
|
var source, label = options.label;
|
|
if (stream.cache[label]) {
|
|
if (stream.cache[label].isClosed) return void stream.cache[label].options.error.call(this, {
|
|
eventPhase: 2,
|
|
type: "error",
|
|
data: "readyState:closed"
|
|
});
|
|
source = jQuery.ajax({
|
|
type: "GET",
|
|
url: options.url,
|
|
data: options.data,
|
|
beforeSend: function(xhr) {
|
|
stream.cache[label] && (this.label = label, stream.cache[label].options.open.call(this)),
|
|
xhr.setRequestHeader("Last-Event-ID", stream.cache[label].lastEventId);
|
|
},
|
|
success: function(data) {
|
|
var tempdata, retries, label = options.label, streamData = jQuery.map(data.split("\n\n"), function(sdata, i) {
|
|
return !!sdata && sdata;
|
|
}), idx = 0, length = streamData.length, reTrim = /^(\s|\u00A0)+|(\s|\u00A0)+$/g;
|
|
if (Array.isArray(streamData)) for (;length > idx; idx++) if (streamData[idx]) {
|
|
for (var parts = streamData[idx].split("\n"), eventType = "message", data = [], i = 0, line = ""; i < parts.length; i++) if (line = parts[i].replace(reTrim, ""),
|
|
0 == line.indexOf("event")) eventType = line.replace(/event:?\s*/, ""); else if (0 == line.indexOf("retry")) retries = parseInt(line.replace(/retry:?\s*/, "")),
|
|
!isNaN(retries) && stream.cache[label] && (stream.cache[label].retry = stream.cache[label].options.retry = retries); else if (0 == line.indexOf("data")) data.push(line.replace(/data:?\s*/, "")); else if (0 == line.indexOf("id:")) {
|
|
var lastEventId = line.replace(/id:?\s*/, "");
|
|
!isNaN(lastEventId) && stream.cache[label] && (stream.cache[label].lastEventId = +lastEventId);
|
|
} else 0 == line.indexOf("id") && stream.cache[label] && (stream.cache[label].lastEventId = null);
|
|
if (tempdata = data.join(""), tempdata.length > 0 && stream.cache[label]) if ("json" === options.dataType) try {
|
|
tempdata = JSON.parse(tempdata), stream.cache[label].options.message.call(this, tempdata, {
|
|
data: tempdata,
|
|
lastEventId: stream.cache[label].lastEventId
|
|
});
|
|
} catch (e) {
|
|
stream.cache[label].options.error.call(this, {
|
|
eventPhase: 0,
|
|
type: "error",
|
|
data: {
|
|
errorThrown: e.message
|
|
}
|
|
});
|
|
} else stream.cache[label].options.message.call(this, tempdata, {
|
|
data: tempdata,
|
|
lastEventId: stream.cache[label].lastEventId
|
|
});
|
|
}
|
|
stream.cache[label] && (this.label = label, setTimeout(function() {
|
|
pluginFns._private.openPollingSource.call(this, options);
|
|
}, stream.cache[label] && stream.cache[label].retry || 4e3));
|
|
},
|
|
error: function(jqXHR, textStatus, errorThrown) {
|
|
if (stream.cache[label] && stream.cache[label].options.error.call(this, {
|
|
eventPhase: 2,
|
|
type: "error",
|
|
data: {
|
|
readyState: jqXHR.readyState,
|
|
textStatus: textStatus,
|
|
errorThrown: errorThrown
|
|
}
|
|
}), 403 === jqXHR.status && jqXHR.responseText.indexOf("JS_ERR_LOST_SESSION") > -1) try {
|
|
iLOGlobal.topPage.doLogout({
|
|
langKey: "login.sessionExp",
|
|
text: "Session expired."
|
|
}, 0);
|
|
} catch (e) {} else setTimeout(function() {
|
|
pluginFns._private.openPollingSource.call(this, options);
|
|
}, stream.cache[label] && stream.cache[label].retry || 4e3);
|
|
},
|
|
complete: function(jqXHR, textStatus) {
|
|
stream.cache[label] && stream.cache[label].options.error.call(this, {
|
|
eventPhase: 0,
|
|
type: "error",
|
|
data: {
|
|
readyState: jqXHR.readyState,
|
|
textStatus: textStatus
|
|
}
|
|
});
|
|
},
|
|
cache: !1,
|
|
timeout: 5e4
|
|
});
|
|
}
|
|
return source;
|
|
}
|
|
}
|
|
}, isHostApi = global.EventSource ? !0 : !1;
|
|
jQuery.eventsource = function(options) {
|
|
var streamType, opts;
|
|
if (options && !jQuery.isPlainObject(options) && pluginFns["public"][options]) return pluginFns["public"][options](arguments[1] ? arguments[1] : "*", arguments[2], arguments[3]);
|
|
if (options.data = options.data && jQuery.isPlainObject(options.data) ? jQuery.param(options.data) : options.data,
|
|
!options.url || "string" != typeof options.url) throw new SyntaxError("Not enough arguments: Must provide a url");
|
|
return options.label = options.label ? options.label : options.url + "?" + options.data,
|
|
opts = jQuery.extend({}, stream.defaults, options), stream.cache[opts.label] = {
|
|
options: opts
|
|
}, streamType = isHostApi ? new EventSource(opts.url + (opts.data ? "?" + opts.data : "")) : pluginFns._private.openPollingSource(opts),
|
|
stream.cache[opts.label] = jQuery.extend({}, stream.setup, {
|
|
stream: streamType,
|
|
isHostApi: isHostApi,
|
|
options: opts
|
|
}), isHostApi && pluginFns._private.openEventSource(opts), stream.cache;
|
|
}, jQuery.each([ "close", "streams", "addEventListener", "removeEventListener" ], function(idx, name) {
|
|
jQuery.eventsource[name] = function(arg) {
|
|
return jQuery.eventsource(name, arg || "*");
|
|
};
|
|
});
|
|
}(jQuery, window); |