Skip to content

Instantly share code, notes, and snippets.

@gistlyn
Last active April 26, 2024 10:27
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gistlyn/5ccd25dc44fcd8999dc560b2f7e4d707 to your computer and use it in GitHub Desktop.
Save gistlyn/5ccd25dc44fcd8999dc560b2f7e4d707 to your computer and use it in GitHub Desktop.
rewrite ss-utils.js to minimize allocations in IE
;(function (root, f) {
if (typeof exports === 'object' && typeof module === 'object')
module.exports = f(require("jquery"));
if (typeof define === "function" && define.amd)
define(["jquery"], f);
else if (typeof exports === "object")
f(require("jquery"));
else
f(root.jQuery);
})(this, function ($) {
if (!$.ss) $.ss = {};
$.ss.handlers = {};
$.ss.onSubmitDisable = "[type=submit]";
$.ss.validation = {
overrideMessages: false,
messages: {
NotEmpty: "Required",
NotNull: "Required",
Email: "Invalid email",
AlreadyExists: "Already exists"
},
errorFilter: function (errorMsg, errorCode, type) {
return this.overrideMessages
? this.messages[errorCode] || errorMsg || splitCase(errorCode)
: errorMsg || splitCase(errorCode);
}
};
$.ss.clearAdjacentError = function () {
$(this).removeClass("error");
$(this).prev(".help-inline,.help-block").removeClass("error").html("");
$(this).next(".help-inline,.help-block").removeClass("error").html("");
};
$.ss.todate = function (s) { return new Date(parseFloat(/Date\(([^)]+)\)/.exec(s)[1])); };
$.ss.todfmt = function (s) { return $.ss.dfmt($.ss.todate(s)); };
function pad(d) { return d < 10 ? '0' + d : d; };
$.ss.dfmt = function (d) { return d.getFullYear() + '/' + pad(d.getMonth() + 1) + '/' + pad(d.getDate()); };
$.ss.dfmthm = function (d) { return d.getFullYear() + '/' + pad(d.getMonth() + 1) + '/' + pad(d.getDate()) + ' ' + pad(d.getHours()) + ":" + pad(d.getMinutes()); };
$.ss.tfmt12 = function (d) { return pad((d.getHours() + 24) % 12 || 12) + ":" + pad(d.getMinutes()) + ":" + pad(d.getSeconds()) + " " + (d.getHours() > 12 ? "PM" : "AM"); };
$.ss.splitOnFirst = function (s, c, A) {
var pos = s && s.indexOf(c);
if (A) {
A[1] = null;
if (!s) {
A[0] = s;
}
else if (pos >= 0) {
A[0] = s.substring(0, pos);
A[1] = s.substring(pos + 1);
return A;
} else {
A[0] = s;
}
return A;
} else {
if (!s) return [s];
return pos >= 0 ? [s.substring(0, pos), s.substring(pos + 1)] : [s];
}
};
$.ss.splitOnLast = function (s, c) { if (!s) return [s]; var pos = s.lastIndexOf(c); return pos >= 0 ? [s.substring(0, pos), s.substring(pos + 1)] : [s]; };
$.ss.getSelection = function () {
return window.getSelection
? window.getSelection().toString()
: document.selection && document.selection.type !== "Control"
? document.selection.createRange().text : "";
};
$.ss.hasOwn = Object.prototype.hasOwnProperty;
$.ss.keysIn = function(o) {
var ret = [];
if (o !== null) {
var obj = Object(o);
for (var key in obj) {
if ($.ss.hasOwn.call(obj, key)) {
ret.push(key);
}
}
}
return ret;
};
$.ss.copyObject = function(to, props, o) {
var i = -1, len = props.length;
while (++i < len) {
var key = props[i];
o[key] = to[key];
}
return o;
};
$.ss.extend = function () {
var i = 0, len = arguments.length;
var to = arguments[0];
if (!to) to = {};
while (++i < len) {
var from = arguments[i];
var props = $.ss.keysIn(from);
var j = -1, pLen = props.length;
while (++j < pLen) {
var key = props[j];
to[key] = from[key];
}
}
return to;
};
$.ss.combinePaths = function() {
var parts = [], i, l;
for (i = 0, l = arguments.length; i < l; i++) {
var arg = arguments[i];
parts = arg.indexOf("://") === -1
? parts.concat(arg.split("/"))
: parts.concat(arg.lastIndexOf("/") === arg.length-1 ? arg.substring(0, arg.length-1) : arg);
}
var paths = [];
for (i = 0, l = parts.length; i < l; i++) {
var part = parts[i];
if (!part || part === ".") continue;
if (part === "..") paths.pop();
else paths.push(part);
}
if (parts[0] === "") paths.unshift("");
return paths.join("/") || (paths.length ? "/" : ".");
};
$.ss.queryString = function (url) {
if (!url || url.indexOf('?') === -1) return {};
var pairs = $.ss.splitOnFirst(url, '?')[1].split('&');
var map = {};
for (var i = 0; i < pairs.length; ++i) {
var p = pairs[i].split('=');
map[p[0]] = p.length > 1
? decodeURIComponent(p[1].replace(/\+/g, ' '))
: null;
}
return map;
};
$.ss.toUrl = function (url, args) {
for (var k in args) {
url += url.indexOf('?') >= 0 ? '&' : '?';
url += k + "=" + $.ss.encodeValue(args[k]);
}
return url;
};
$.ss.encodeValue = function (o) {
if (o == null) return "";
if ($.isArray(o)) {
var s = "";
for (var i = 0; i < o.length; i++) {
if (s.length > 0)
s += ',';
s += $.ss.encodeValue(o[i]);
}
return s;
}
return encodeURIComponent(o);
};
$.ss.bindAll = function (o) {
for (var k in o) {
if (typeof o[k] == 'function')
o[k] = o[k].bind(o);
}
return o;
};
$.ss.createPath = function (route, args) {
var argKeys = {};
for (var k in args) {
argKeys[k.toLowerCase()] = k;
}
var parts = route.split('/');
var url = '';
for (var i = 0; i < parts.length; i++) {
var p = parts[i];
if (p == null) p = '';
if (p[0] === '{' && p[p.length - 1] === '}') {
var key = argKeys[p.substring(1, p.length - 1).toLowerCase()];
if (key) {
p = args[key];
delete args[key];
}
}
if (url.length > 0) url += '/';
url += p;
}
return route[0] === '/' ? '/' + url : url;
};
$.ss.createUrl = function(route, args) {
var url = $.ss.createPath(route, args);
return $.ss.toUrl(url, args);
};
function splitCase(t) {
return typeof t != 'string' ? t : t.replace(/([A-Z]|[0-9]+)/g, ' $1').replace(/_/g, ' ');
}
$.ss.humanize = function (s) { return !s || s.indexOf(' ') >= 0 ? s : splitCase(s); };
function toCamelCase(key) {
return !key ? key : key.charAt(0).toLowerCase() + key.substring(1);
}
$.ss.toCamelCase = toCamelCase;
$.ss.toPascalCase = function (s) { return !s ? s : s.charAt(0).toUpperCase() + s.substring(1); };
$.ss.normalizeKey = function (key) {
return typeof key == "string" ? key.toLowerCase().replace(/_/g, '') : key;
};
$.ss.normalize = function (dto, deep) {
if ($.isArray(dto)) {
if (!deep) return dto;
var to = [];
for (var i = 0; i < dto.length; i++) {
to[i] = $.ss.normalize(dto[i], deep);
}
return to;
}
if (typeof dto != "object") return dto;
var o = {};
for (var k in dto) {
o[$.ss.normalizeKey(k)] = deep ? $.ss.normalize(dto[k], deep) : dto[k];
}
return o;
};
function sanitize(status) {
if (status["errors"])
return status;
var to = {};
for (var k in status)
to[toCamelCase(k)] = status[k];
to.errors = [];
$.each(status.Errors || [], function (i, o) {
var err = {};
for (var k in o)
err[toCamelCase(k)] = o[k];
to.errors.push(err);
});
return to;
}
$.ss.parseResponseStatus = function (json, defaultMsg) {
try {
var err = JSON.parse(json);
return sanitize(err.ResponseStatus || err.responseStatus);
} catch (e) {
return {
message: defaultMsg,
__error: { error: e, json: json }
};
}
};
$.ss.postJSON = function (url, data, success, error) {
return $.ajax({
type: "POST", url: url, dataType: "json", contentType: "application/json",
data: typeof data == "string" ? data : JSON.stringify(data),
success: success, error: error
});
};
$.ss.createElement = createElement;
var keyAliases = {className:'class',htmlFor:'for'};
function createElement(tagName, options, attrs) {
var el = document.createElement(tagName);
if (attrs) {
for (var key in attrs) {
if (!attrs.hasOwnProperty(key)) continue;
el.setAttribute(keyAliases[key] || key, attrs[key]);
}
}
if (options && options.insertAfter) {
options.insertAfter.parentNode.insertBefore(el, options.insertAfter.nextSibling);
}
return el;
}
$.ss.showInvalidInputs = function() {
var errorMsg = this.getAttribute('data-invalid');
if (errorMsg) {
var isCheck = this.type === "checkbox" || this.type === "radio" || hasClass(this, 'form-check');
var elFormCheck = isCheck ? parent(this,'form-check') : null;
var elInputGroup = parent(this,'input-group');
if (!isCheck)
addClass(this, 'is-invalid');
else
addClass(elFormCheck || this.parentElement, 'is-invalid form-control');
var elNext = this.nextElementSibling;
var elLast = elInputGroup ? elInputGroup.lastElementChild :
(elNext && (elNext.getAttribute('for') === this.id || elNext.tagName === "SMALL")
? (isCheck ? elFormCheck || elNext.parentElement : elNext)
: this);
var elError = elLast.nextElementSibling && hasClass(elLast.nextElementSibling, 'invalid-feedback')
? elLast.nextElementSibling
: (elInputGroup && elInputGroup.querySelector('.invalid-feedback'))
|| $.ss.createElement("div", { insertAfter:elLast }, { className: 'invalid-feedback' });
elError.innerHTML = errorMsg;
}
};
function parent(el,cls) {
while (el && !hasClass(el,cls))
el = el.parentElement;
return el;
}
function hasClass(el, cls) {
return el && (" " + el.className + " ").replace(/[\n\t\r]/g, " ").indexOf(" " + cls + " ") > -1;
}
function addClass(el, cls) {
if (!hasClass(el, cls)) el.className = (el.className + " " + cls).trim();
}
function errorResponseExcept(status, fieldNames) {
if (typeof fieldNames == 'string')
fieldNames = arguments.length == 1 ? [fieldNames] : Array.prototype.slice.call(arguments);
if (fieldNames && !(status.errors == null || status.errors.length == 0)) {
var lowerFieldsNames = fieldNames.map(function (x) { return (x || '').toLowerCase(); });
for (var i = 0, _a = status.errors; i < _a.length; i++) {
var field = _a[i];
if (lowerFieldsNames.indexOf((field.fieldName || '').toLowerCase()) !== -1) {
return undefined;
}
}
for (var _b = 0, _c = status.errors; _b < _c.length; _b++) {
var field = _c[_b];
if (lowerFieldsNames.indexOf((field.fieldName || '').toLowerCase()) === -1) {
return field.message || field.errorCode;
}
}
}
return status.message || status.errorCode || undefined;
}
$.ss.errorResponseExcept = errorResponseExcept;
$.fn.bootstrap = function(){
$(this).find('[data-invalid]').each($.ss.showInvalidInputs);
return this;
};
$.fn.setFieldError = function (name, msg) {
$(this).applyErrors({
errors: [{
fieldName: name,
message: msg
}]
});
};
$.fn.serializeMap = function () {
var o = {};
$.each($(this).serializeArray(), function (i, e) {
o[e.name] = e.value;
});
return o;
};
$.fn.applyErrors = function (status, opt) {
this.clearErrors();
if (!status) return this;
status = sanitize(status);
this.addClass("has-errors");
var bs4 = opt && opt.type === "bootstrap-v4";
var o = $.ss.extend({}, $.ss.validation, opt);
if (opt && opt.messages) {
o.overrideMessages = true;
$.ss.extend(o.messages, $.ss.validation.messages);
}
var filter = $.proxy(o.errorFilter, o),
errors = status.errors;
if (errors && errors.length) {
var fieldMap = {}, fieldLabelMap = {};
this.find("input,textarea,select,button").each(function () {
var $el = $(this);
var $prev = $el.prev(), $next = $el.next();
var isCheck = this.type === "radio" || this.type === "checkbox";
var fieldId = $el.attr("name") || this.id;
if (!fieldId) return;
var key = (fieldId).toLowerCase();
fieldMap[key] = $el;
if (!bs4) {
if ($prev.hasClass("help-inline") || $prev.hasClass("help-block")) {
fieldLabelMap[key] = $prev;
} else if ($next.hasClass("help-inline") || $next.hasClass("help-block")) {
fieldLabelMap[key] = $next;
}
}
});
this.find(".help-inline[data-for],.help-block[data-for]").each(function () {
var $el = $(this);
var key = $el.data("for").toLowerCase();
fieldLabelMap[key] = $el;
});
$.each(errors, function (i, error) {
var key = (error.fieldName || "").toLowerCase();
var $field = fieldMap[key];
if ($field) {
if (!bs4) {
$field.addClass("error");
$field.parent().addClass("has-error");
} else {
var type = $field.attr('type'), isCheck = type === "radio" || type === "checkbox";
if (!isCheck) $field.addClass("is-invalid");
$field.attr("data-invalid", filter(error.message, error.errorCode, "field"));
}
}
var $lblErr = fieldLabelMap[key];
if (!$lblErr) return;
$lblErr.addClass("error");
$lblErr.html(filter(error.message, error.errorCode, "field"));
$lblErr.show();
});
this.find("[data-validation-summary]").each(function () {
var fields = this.getAttribute('data-validation-summary').split(',');
var summaryMsg = errorResponseExcept(status, fields);
if (summaryMsg)
this.innerHTML = bsAlert(summaryMsg);
});
} else {
var htmlSummary = filter(status.message || splitCase(status.errorCode), status.errorCode, "summary");
if (!bs4) {
this.find(".error-summary").html(htmlSummary).show();
} else {
this.find("[data-validation-summary]").html(htmlSummary[0] === "<" ? htmlSummary : bsAlert(htmlSummary));
}
}
return this;
};
function bsAlert(msg) { return '<div class="alert alert-danger">' + msg + '</div>'; }
$.fn.clearErrors = function () {
this.removeClass("has-errors");
this.find(".error-summary").html("").hide();
this.find(".help-inline.error, .help-block.error").each(function () {
$(this).html("");
});
this.find(".error").each(function () {
$(this).removeClass("error");
});
this.find(".has-error").each(function () {
$(this).removeClass("has-error");
});
this.find("[data-validation-summary]").html("");
this.find('.form-check.is-invalid [data-invalid]').removeAttr('data-invalid');
this.find('.form-check.is-invalid').removeClass('form-control');
this.find('.is-invalid').removeClass('is-invalid').removeAttr('data-invalid');
this.find('.is-valid').removeClass('is-valid');
};
$.fn.bindForm = function (orig) { //bootstrap v3
return this.each(function () {
var f = $(this);
if (orig && orig.model)
$.ss.populateForm(this,orig.model);
f.submit(function (e) {
e.preventDefault();
return $(f).ajaxSubmit(orig);
});
});
};
$.fn.bootstrapForm = function (orig) { //bootstrap v4
return this.each(function () {
var f = $(this);
if (orig.model)
$.ss.populateForm(this,orig.model);
f.submit(function (e) {
e.preventDefault();
orig.type = "bootstrap-v4";
return $(f).ajaxSubmit(orig);
});
});
};
$.fn.ajaxSubmit = function (orig) {
orig = orig || {};
var bs4 = orig.type === "bootstrap-v4";
if (orig.validation) {
$.ss.extend($.ss.validation, orig.validation);
}
return this.each(function () {
var f = $(this);
f.clearErrors();
try {
if (orig.validate && orig.validate.call(f) === false)
return false;
} catch (e) {
return false;
}
f.addClass("loading");
var $disable = $(orig.onSubmitDisable || $.ss.onSubmitDisable, f);
$disable.attr("disabled", "disabled");
var opt = $.ss.extend({}, orig, {
type: f.attr('method') || "POST",
url: f.attr('action'),
data: f.serialize(),
accept: "application/json",
error: function (jq, jqStatus, statusText) {
var err, errMsg = "The request failed with " + (statusText || jq.statusText);
try {
err = JSON.parse(jq.responseText);
} catch (e) {
console.log("JSON.parse", e)
}
if (!err) {
f.addClass("has-errors");
f.find(".error-summary").html(errMsg);
if (bs4) {
var elSummary = f.find('[data-validation-summary]');
elSummary.html('<div class="alert alert-danger">' + errMsg + '</div>');
}
} else {
f.applyErrors(err.ResponseStatus || err.responseStatus, {type:orig.type});
}
if (orig.error) {
orig.error.apply(this, arguments);
}
if (bs4) {
f.find('[data-invalid]').each($.ss.showInvalidInputs);
}
},
complete: function (jq) {
f.removeClass("loading");
$disable.removeAttr("disabled");
if (orig.complete) {
orig.complete.apply(this, arguments);
}
var loc = jq.getResponseHeader("X-Location");
if (loc) {
location.href = loc;
}
var evt = jq.getResponseHeader("X-Trigger");
if (evt) {
var pos = evt.indexOf(':');
var cmd = pos >= 0 ? evt.substring(0, pos) : evt;
var data = pos >= 0 ? evt.substring(pos + 1) : null;
f.trigger(cmd, data ? [data] : []);
}
},
dataType: "json"
});
$.ajax(opt);
return false;
});
};
$.fn.applyValues = function (map) {
return this.each(function () {
var $el = $(this);
$.each(map, function (k, v) {
$el.find("#" + k + ",[name=" + k + "]").val(v);
});
$el.find("[data-html]").each(function () {
$(this).html(map[$(this).data("html")] || "");
});
$el.find("[data-val]").each(function () {
$(this).val(map[$(this).data("val")] || "");
});
$el.find("[data-src]").each(function () {
$(this).attr("src", map[$(this).data("src")] || "");
});
$el.find("[data-href]").each(function () {
$(this).attr("href", map[$(this).data("href")] || "");
});
});
};
$.ss.__call = $.ss.__call || function (e) {
var $el = $(e.target);
var attr = $el.data(e.type) || $el.closest("[data-" + e.type + "]").data(e.type);
if (!attr) return;
var pos = attr.indexOf(':'), fn;
if (pos >= 0) {
var cmd = attr.substring(0, pos);
var data = attr.substring(pos + 1);
if (cmd === 'trigger') {
$el.trigger(data, [e.target]);
} else {
fn = $.ss.handlers[cmd];
if (fn) {
fn.apply(e.target, data.split(','));
}
}
} else {
fn = $.ss.handlers[attr];
if (fn) {
fn.apply(e.target, [].slice.call(arguments));
}
}
};
$.ss.listenOn = 'click dblclick change focus blur focusin focusout select keydown keypress keyup hover toggle input';
$.fn.bindHandlers = function (handlers) {
$.ss.extend($.ss.handlers, handlers || {});
return this.each(function () {
var $el = $(this);
$el.off($.ss.listenOn, $.ss.__call);
$el.on($.ss.listenOn, $.ss.__call);
});
};
$.ss.populateForm = function(form, model) {
if (!model)
return;
var toggleCase = function (s) { return !s ? s :
s[0] === s[0].toUpperCase() ? exports.toCamelCase(s) : s[0] === s[0].toLowerCase() ? exports.toPascalCase(s) : s; };
for (var key in model) {
var val = model[key];
if (typeof val == 'undefined' || val === null)
val = '';
var el = form.elements.namedItem(key) || form.elements.namedItem(toggleCase(key));
var input = el;
if (!el)
continue;
var type = input.type || el[0].type;
switch (type) {
case 'radio':
case 'checkbox':
var len = el.length;
for (var i = 0; i < len; i++) {
el[i].checked = (val.indexOf(el[i].value) > -1);
}
break;
case 'select-multiple':
var values = $.isArray(val) ? val : [val];
var select = el;
for (var i = 0; i < select.options.length; i++) {
select.options[i].selected = (values.indexOf(select.options[i].value) > -1);
}
break;
case 'select':
case 'select-one':
input.value = val.toString() || val;
break;
case 'date':
var d = exports.toDate(val);
if (d)
input.value = d.toISOString().split('T')[0];
break;
default:
input.value = val;
break;
}
}
};
$.fn.setActiveLinks = function () {
var url = window.location.href;
return this.each(function () {
$(this).filter(function () {
return this.href === url;
})
.addClass('active')
.closest("li").addClass('active');
});
};
$.ss.eventSourceStop = false;
$.ss.eventOptions = {};
$.ss.eventReceivers = {};
$.ss.eventChannels = [];
$.ss.eventSourceUrl = null;
$.ss.updateSubscriberUrl = null;
$.ss.updateChannels = function(channels) {
$.ss.eventChannels = channels;
if (!$.ss.eventSource) return;
var url = $.ss.eventSource.url;
var qs = $.ss.queryString(url);
qs['channels'] = channels;
delete qs['channel'];
var pos = url.indexOf('?');
$.ss.eventSourceUrl = $.ss.toUrl(pos >= 0 ? url.substring(0, pos) : url, qs);
};
$.ss.updateSubscriberInfo = function (subscribe, unsubscribe) {
var sub = typeof subscribe == "string" ? subscribe.split(',') : subscribe;
var unsub = typeof unsubscribe == "string" ? unsubscribe.split(',') : unsubscribe;
var channels = [];
for (var i in $.ss.eventChannels) {
var c = $.ss.eventChannels[i];
if (unsub == null || $.inArray(c, unsub) === -1) {
channels.push(c);
}
}
if (sub) {
for (var i in sub) {
var c = sub[i];
if ($.inArray(c, channels) === -1) {
channels.push(c);
}
}
}
$.ss.updateChannels(channels);
};
$.ss.subscribeToChannels = function (channels, cb, cbError) {
return $.ss.updateSubscriber({ SubscribeChannels: channels.join(',') }, cb, cbError);
};
$.ss.unsubscribeFromChannels = function (channels, cb, cbError) {
return $.ss.updateSubscriber({ UnsubscribeChannels: channels.join(',') }, cb, cbError);
};
$.ss.updateSubscriber = function (data, cb, cbError) {
if (!$.ss.updateSubscriberUrl)
throw new Error("updateSubscriberUrl was not populated");
return $.ajax({
type: "POST",
url: $.ss.updateSubscriberUrl,
data: data,
dataType: "json",
success: function (r) {
$.ss.updateSubscriberInfo(data.SubscribeChannels, data.UnsubscribeChannels);
r.channels = $.ss.eventChannels;
if (cb != null)
cb(r);
},
error: function (e) {
$.ss.reconnectServerEvents({ errorArgs: arguments });
if (cbError != null)
cbError(e);
}
});
};
$.ss.reconnectServerEvents = function (opt) {
if ($.ss.eventSourceStop) return;
opt = opt || {};
var hold = $.ss.eventSource;
var es = new EventSource(opt.url || $.ss.eventSourceUrl || hold.url, { withCredentials:hold.withCredentials });
es.onerror = opt.onerror || hold.onerror;
es.onmessage = opt.onmessage || hold.onmessage;
var fn = $.ss.handlers["onReconnect"];
if (fn != null)
fn.apply(es, opt.errorArgs);
hold.close();
return $.ss.eventSource = es;
};
$.ss.invokeReceiver = function (r, cmd, el, msg, e, name) {
if (r) {
if (typeof (r[cmd]) == "function") {
r[cmd].call(el || r[cmd], msg, e);
} else {
r[cmd] = msg;
}
}
};
$.ss.onUnload = function () {
if ($.ss.unRegisterUrl) {
$.ajax({ type: 'POST', url: $.ss.unRegisterUrl, async: false });
}
};
if (!Function.prototype.bind) (function () {
var slice = Array.prototype.slice;
Function.prototype.bind = function () {
var thatFunc = this, thatArg = arguments[0];
var args = slice.call(arguments, 1);
if (typeof thatFunc !== 'function') {
throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
}
return function () {
var funcArgs = args.concat(slice.call(arguments));
return thatFunc.apply(thatArg, funcArgs);
};
};
})();
function sendHeartbeat() {
var _ = this._, connectId = this.connectId, opt = _.eventOptions;
if (connectId !== _.CONNECT_ID) // Only allow latest connections heartbeat callback through
return;
if (_.eventSource.readyState === 2) //CLOSED
{
var stopFn = _.handlers["onStop"];
if (stopFn !== null)
stopFn.apply(_.eventSource);
_.reconnectServerEvents({ errorArgs: { error: 'CLOSED' } });
return;
}
$.ajax({
type: "POST",
url: opt.heartbeatUrl,
data: null,
dataType: "text",
success: function (r) {
setTimeout(sendHeartbeat.bind({ _: _, connectId: _.CONNECT_ID }), parseInt(opt.heartbeatIntervalMs) || 10000);
},
error: function () {
_.reconnectServerEvents({ errorArgs: arguments });
}
});
}
function handleCommand($, _, opt, cmd, el, msg, e) {
if (cmd === "onConnect") {
_.extend(opt, msg);
if (opt.heartbeatUrl) {
_.CONNECT_ID = _.CONNECT_ID ? _.CONNECT_ID + 1 : 1;
setTimeout(sendHeartbeat.bind({ _: _, connectId: _.CONNECT_ID }), parseInt(opt.heartbeatIntervalMs) || 10000);
}
_.unRegisterUrl = opt.unRegisterUrl;
_.updateSubscriberUrl = opt.updateSubscriberUrl;
_.updateChannels((opt.channels || "").split(','));
}
var fn = _.handlers[cmd];
if (fn) {
fn.call(el || _.document.body, msg, e);
}
}
function onMessage(e) {
var $ = this, _ = $.ss, opt = _.eventOptions, A=_.ARRAY_CACHE, selector, channel, json, op, target, cmd, cssSel, $els, el;
A = _.splitOnFirst(e.data, ' ', A);
selector = A[0];
json = A[1];
A = _.splitOnFirst(selector, '@', A);
if (A[1]) {
channel = A[0];
selector = A[1];
}
var msg = json ? _.parseJson(json) : null;
A = _.splitOnFirst(selector, '.', A);
op = A[0];
if (!A[1])
throw "invalid selector format: " + selector;
target = A[1].indexOf('%20') >= 0 ? A[1].replace(new RegExp("%20", 'g'), " ") : A[1];
if (opt.validate && opt.validate(op, target, msg, json) === false)
return;
A = _.splitOnFirst(target, '$', A);
cmd = A[0];
cssSel = A[1];
$els = cssSel && $(cssSel);
el = $els && $els[0];
e = _.extend({ cmd:cmd, op:op, selector:selector, "$target":target, cssSelector:cssSel, json:json, channel:channel }, e);
if (op === "cmd") {
handleCommand($, _, opt, cmd, el, msg, e);
}
else if (op === "trigger") {
$(el || _.document).trigger(cmd, [msg, e]);
}
else if (op === "css") {
$($els || _.document.body).css(cmd, msg, e);
}
else {
var r = opt.receivers && opt.receivers[op] || _.eventReceivers[op];
_.invokeReceiver(r, cmd, el, msg, e, op);
}
var fn = _.handlers["onMessage"];
if (fn) fn.call(el || _.document.body, msg, e);
if (opt.success) opt.success(selector, msg, e); //deprecated
}
$.fn.handleServerEvents = function (opt) {
$.ss.eventSource = this[0];
$.ss.eventOptions = opt = opt || {};
$.ss.document = document;
$.ss.parseJson = JSON.parse;
$.ss.ARRAY_CACHE = [];
if (opt.handlers) {
$.ss.extend($.ss.handlers, opt.handlers || {});
}
$(window).on("unload", $.ss.onUnload);
$.ss.eventSource.onmessage = onMessage.bind($);
var hold = $.ss.eventSource.onerror;
$.ss.eventSource.onerror = function () {
var args = arguments;
window.setTimeout(function () {
$.ss.reconnectServerEvents({ errorArgs: args });
if (hold)
hold.apply(args);
}, 10000);
};
};
});
function reset() {
$.ss.eventSourceStop = false;
$.ss.eventOptions = {};
$.ss.eventReceivers = {};
$.ss.eventChannels = [];
$.ss.eventSourceUrl = $.ss.updateSubscriberUrl = $.ss.eventOptions = $.ss.eventSource = null;
}
$.ss.disposeServerEvents = function (cb) {
var unRegisterUrl = $.ss.eventOptions && $.ss.eventOptions.unRegisterUrl;
if ($.ss.eventSource) $.ss.eventSource.close();
reset();
if (unRegisterUrl) {
$.ajax({ type: 'POST', url: unRegisterUrl, complete: function() { if (cb) cb(); } });
} else {
if (cb) cb();
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment