Skip to content

Instantly share code, notes, and snippets.

@rpgmaker
Created May 14, 2012 04:31
Show Gist options
  • Save rpgmaker/2691804 to your computer and use it in GitHub Desktop.
Save rpgmaker/2691804 to your computer and use it in GitHub Desktop.
PSB JS API (Second Draft)
var JSON;
if (!JSON) {
JSON = {};
}
(function () {
'use strict';
function f(n) {
// Format integers to have at least two digits.
return n < 10 ? '0' + n : n;
}
if (typeof Date.prototype.toJSON !== 'function') {
Date.prototype.toJSON = function (key) {
return isFinite(this.valueOf())
? this.getUTCFullYear() + '-' +
f(this.getUTCMonth() + 1) + '-' +
f(this.getUTCDate()) + 'T' +
f(this.getUTCHours()) + ':' +
f(this.getUTCMinutes()) + ':' +
f(this.getUTCSeconds()) + 'Z'
: null;
};
String.prototype.toJSON =
Number.prototype.toJSON =
Boolean.prototype.toJSON = function (key) {
return this.valueOf();
};
}
var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
gap,
indent,
meta = { // table of character substitutions
'\b': '\\b',
'\t': '\\t',
'\n': '\\n',
'\f': '\\f',
'\r': '\\r',
'"' : '\\"',
'\\': '\\\\'
},
rep;
function quote(string) {
escapable.lastIndex = 0;
return escapable.test(string) ? '"' + string.replace(escapable, function (a) {
var c = meta[a];
return typeof c === 'string'
? c
: '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
}) + '"' : '"' + string + '"';
}
function str(key, holder) {
var i, // The loop counter.
k, // The member key.
v, // The member value.
length,
mind = gap,
partial,
value = holder[key];
if (value && typeof value === 'object' &&
typeof value.toJSON === 'function') {
value = value.toJSON(key);
}
if (typeof rep === 'function') {
value = rep.call(holder, key, value);
}
switch (typeof value) {
case 'string':
return quote(value);
case 'number':
return isFinite(value) ? String(value) : 'null';
case 'boolean':
case 'null':
return String(value);
case 'object':
if (!value) {
return 'null';
}
gap += indent;
partial = [];
if (Object.prototype.toString.apply(value) === '[object Array]') {
length = value.length;
for (i = 0; i < length; i += 1) {
partial[i] = str(i, value) || 'null';
}
v = partial.length === 0
? '[]'
: gap
? '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']'
: '[' + partial.join(',') + ']';
gap = mind;
return v;
}
if (rep && typeof rep === 'object') {
length = rep.length;
for (i = 0; i < length; i += 1) {
if (typeof rep[i] === 'string') {
k = rep[i];
v = str(k, value);
if (v) {
partial.push(quote(k) + (gap ? ': ' : ':') + v);
}
}
}
} else {
for (k in value) {
if (Object.prototype.hasOwnProperty.call(value, k)) {
v = str(k, value);
if (v) {
partial.push(quote(k) + (gap ? ': ' : ':') + v);
}
}
}
}
v = partial.length === 0
? '{}'
: gap
? '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}'
: '{' + partial.join(',') + '}';
gap = mind;
return v;
}
}
if (typeof JSON.stringify !== 'function') {
JSON.stringify = function (value, replacer, space) {
var i;
gap = '';
indent = '';
if (typeof space === 'number') {
for (i = 0; i < space; i += 1) {
indent += ' ';
}
} else if (typeof space === 'string') {
indent = space;
}
rep = replacer;
if (replacer && typeof replacer !== 'function' &&
(typeof replacer !== 'object' ||
typeof replacer.length !== 'number')) {
throw new Error('JSON.stringify');
}
return str('', {'': value});
};
}
if (typeof JSON.parse !== 'function') {
JSON.parse = function (text, reviver) {
var j;
function walk(holder, key) {
var k, v, value = holder[key];
if (value && typeof value === 'object') {
for (k in value) {
if (Object.prototype.hasOwnProperty.call(value, k)) {
v = walk(value, k);
if (v !== undefined) {
value[k] = v;
} else {
delete value[k];
}
}
}
}
return reviver.call(holder, key, value);
}
text = String(text);
cx.lastIndex = 0;
if (cx.test(text)) {
text = text.replace(cx, function (a) {
return '\\u' +
('0000' + a.charCodeAt(0).toString(16)).slice(-4);
});
}
if (/^[\],:{}\s]*$/
.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@')
.replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']')
.replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
j = eval('(' + text + ')');
return typeof reviver === 'function'
? walk({'': j}, '')
: j;
}
throw new SyntaxError('JSON.parse');
};
}
}());
String.format = function (text) {
'use strict';
if (arguments.length <= 1) return text;
var length = arguments.length - 2, token;
for (token = 0; token <= length; token++) {
text = text.replace(new RegExp("\\{" + token + "\\}", "gi"), arguments[token + 1]);
}
return text;
};
jQuery.support.cors = true;
(function (jQuery) {
'use strict';
if (window.XDomainRequest) {
jQuery.ajaxTransport(function (s) {
if (s.crossDomain && s.async) {
if (s.timeout) {
s.xdrTimeout = s.timeout;
delete s.timeout;
}
var xdr;
return {
send: function (_, complete) {
function callback(status, statusText, responses, responseHeaders) {
xdr.onload = xdr.onerror = xdr.ontimeout = jQuery.noop;
xdr = undefined;
complete(status, statusText, responses, responseHeaders);
}
xdr = new XDomainRequest();
xdr.open(s.type, s.url);
xdr.onload = function () {
callback(200, "OK", { text: xdr.responseText }, "Content-Type: " + xdr.contentType);
};
xdr.onerror = function () {
callback(404, "Not Found");
};
if (s.xdrTimeout) {
xdr.ontimeout = function () {
callback(0, "timeout");
};
xdr.timeout = s.xdrTimeout;
}
xdr.send((s.hasContent && s.data) || null);
},
abort: function () {
if (xdr) {
xdr.onerror = jQuery.noop();
xdr.abort();
}
}
};
}
});
}
})(jQuery);
var PSERVICEBUS;
if (!PSERVICEBUS) PSERVICEBUS = {};
(function (fn) {
'use strict';
var hubcounter = 0,
hubconnections = {};
fn.hub = function () {
var self = this,
interval = 500,
transport = null,
active = false,
msgID = 0,
hubID = ++hubcounter,
name = "hub" + hubID,
url = null,
protocols = {
httpstreaming: 1,
serversent: 4,
ieforeverframe: 2,
foreverframe: 3
},
protocol =
($.browser.msie ? protocols.ieforeverframe :
$.browser.opera ? protocols.serversent :
$.browser.safari || $.browser.webkit || $.browser.mozilla ?
protocols.httpstreaming : protocols.foreverframe),
transportType = (
protocol === protocols.ieforeverframe || protocol === protocols.foreverframe ? "foreverframe" :
protocol === protocols.httpstreaming ? "httpstreaming" :
protocol === protocols.serversent ? "serversent" : ""),
scope = function (f, context) {
return function () { return f.apply(context); };
},
xhrcallback = function () {
if (transport.readyState === 4) { self.close(); return; }
var messages = transport.responseText.match(/<comet>(.+?)<\/comet>/g);
while (messages && messages[msgID]) {
var message = messages[msgID].substring(7, messages[msgID].length - 8),
msg = JSON.parse(message);
self.onmessage(msg);
msgID++;
}
},
checkframe = function () {
var iframe = transport.getElementById(name);
if (!iframe) return;
if (iframe.readyState === 'complete') { self.close(); return; }
setTimeout(scope(checkframe, self), interval);
};
hubconnections[hubID] = self;
var _open = function () {
if (active) return;
active = true;
switch (protocol) {
case protocols.httpstreaming:
transport = ("XDomainRequest" in window) ? new XDomainRequest() :
new XMLHttpRequest();
transport.open("GET", url, true);
transport.onreadystatechange = scope(xhrcallback, self);
transport.send(null);
break;
case protocols.ieforeverframe:
var p = document.location.protocol;
transport = new ActiveXObject("htmlfile");
transport.open();
transport.write(String.format("<html><script>{0}</script></html>",
p.indexOf("file") >= 0 || document.domain.indexOf("localhost") >= 0 ? "" :
String.format("document.domain='{0}//{1}';", p, document.domain)));
transport.close();
transport.parentWindow.push = self.onmessage;
var div = transport.createElement("div");
transport.body.appendChild(div);
div.innerHTML = String.format("<iframe id='{0}' src='{1}'></iframe>", name, url);
setTimeout(scope(checkframe, self), interval);
break;
case protocols.foreverframe:
transport = document.createElement("iframe");
transport.style.display = "none";
transport.src = url;
transport.push = self.onmessage;
if(transport.contentWindow)
transport.contentWindow.push = self.onmessage;
transport.onload = scope(self.close, self);
document.body.appendChild(transport);
break;
case protocols.serversent:
transport = new EventSource(url);
transport.onmessage = function (response) {
var msg = JSON.parse(response.data);
self.onmessage(msg);
};
break;
}
};
this.onclose = function () { };
this.onmessage = function (msg) { };
this.open = function (uri) {
url = uri + "&transport=" + transportType;
setTimeout(function () { _open(); }, 10);
};
this.close = function () {
if (!active) return;
active = false;
switch (protocol) {
case protocols.httpstreaming:
if (transport.readyState !== 4) transport.abort();
break;
case protocols.ieforeverframe:
var transportframe = transport.getElementById(name);
transportframe.src = 'about:blank';
transport.body.removeChild(transportframe);
transport.parentWindow.push = null;
CollectGarbage();
break;
case protocols.foreverframe:
transport.src = 'about:blank';
document.body.removeChild(transport);
break;
case protocols.serversent:
transport.close();
break;
}
transport = null;
self.onclose();
};
};
fn.hubcallback = function(){
for (var key in hubconnections)
hubconnections[key].close();
hubconnections = {};
};
} (PSERVICEBUS));
(function (fn) {
'use strict';
var endpointAddress = "http://localhost:8087/ESBRestService/",
websocketAddress = "",
websocketPort = "8081",
rethrow = false,
apikey = "demo",
passcode = "demo",
includeIdentity = false,
usernamecookie = "pservicebus_username",
apikeytoken = "pservicebus_apikey_key",
passcodetoken = "pservicebus_passcode_key",
username = "",
durable = false,
handlers = {},
longpolling = false,
invalidstate = 1,
getcookie = function (c_name) {
var i, x, y, ARRcookies = document.cookie.split(";");
for (i = 0; i < ARRcookies.length; i++) {
x = ARRcookies[i].substr(0, ARRcookies[i].indexOf("="));
y = ARRcookies[i].substr(ARRcookies[i].indexOf("=") + 1);
x = x.replace(/^\s+|\s+$/g, "");
if (x === c_name) {
return unescape(y);
}
}
},
setcookie = function (c_name, value, exdays) {
var exdate = new Date();
exdate.setDate(exdate.getDate() + exdays);
var c_value = escape(value) + ((!exdays) ? "" : "; expires=" + exdate.toUTCString());
document.cookie = c_name + "=" + c_value;
},
getusername = function () {
if (username) return username;
username = getcookie(usernamecookie);
if (!username){
var date = new Date();
username = String.format("js{0}{1}{2}", date.getTime(), date.getMonth(), date.getFullYear());
}
if (durable) setcookie(usernamecookie, username, 30);
return username;
};
fn.ondisconnect = function () { };
fn.onconnect = function () { };
fn.connected = true;
fn.transport = {
msmq: 0,
rabbitmq: 1,
ravendb: 2,
tcp: 3,
redis: 7
};
fn.format = {
json: 1
};
fn.timespan = function (day, hour, min, sec, ms) {
sec = sec || 0;
ms = ms || 0;
min = min || 0;
hour = hour || 0;
day = day || 0;
var date = new Date();
date.setDate(date.getDate() + day);
date.setMinutes(date.getMinutes() + min);
date.setSeconds(date.getSeconds() + sec);
date.setMilliseconds(date.getMilliseconds() + ms);
date.setHours(date.getHours() + hour);
return date.getTime() - (new Date().getTime());
};
var ajax = (function () {
return {
helper: (function () {
var usejsonp = false,
fail = function (e) {
if (typeof (e) !== "string") return;
throw e;
},
jsonp = function (method, data, success) {
fn.connected = method !== "Disconnect";
data.ReThrowException = rethrow.toString();
data.ESBUserName = apikey;
data.ESBPassword = passcode;
var url = String.format("{0}{1}?callback=?", endpointAddress, method);
$.ajax({
url: url,
data: data,
dataType: 'jsonp',
crossDomain: true,
contentType: 'application/json; charset=utf-8',
success: function (result) {
if (success) success(JSON.parse(result));
},
error: function (e) {
if (fail) fail(e);
}
});
},
json = function (method, data, success, async) {
fn.connected = method !== "Disconnect";
async = async || false;
var url = String.format("{0}{1}?ReThrowException={2}&ESBUserName={3}&ESBPassword={4}",
endpointAddress, method, rethrow, apikey, passcode);
$.ajax({
type: 'POST',
url: url,
data: data,
traditional: true,
dataType: 'json',
async: async,
success: function (result, status, xhr) {
if (result) {
var d = result.d || result;
if (d) {
var evald = JSON.parse(d);
if (success) success(evald);
}
}
},
error: function (xhr, e, msg) {
if (!usejsonp) {
usejsonp = true;
jsonp(method, data, success);
return;
}
if (fail) fail(xhr.responseText);
}
});
};
return {
invoke: function(method, data, success, async){
if(usejsonp) jsonp(method, data, success);
else json(method, data, success, async);
}
};
})()
};
})();
var messagehandler = function (username, _transport, _dto, _interval, _batchsize, _action) {
var subscriber = username,
transport = _transport,
dto = _dto,
interval = _interval,
batchsize = _batchsize,
action = _action,
handle = null,
hub = null,
websocket = null,
running = false,
self = this,
xhr = null,
huburl = (endpointAddress + String.format("StreamSubscriberMessages/?subscriberName={0}&transportName={1}&messageTypeInfo={2}&batchSize={3}&interval={4}",
subscriber, transport, JSON.stringify(dto), batchsize, interval)),
handlemessage = function () {
if (!running) return;
handle = clearInterval(handle);
xhr = ajax.helper.invoke("GetSubscriberMessages",
{ subscriberName: subscriber, transportName: transport,
messageTypeInfo: JSON.stringify(dto),
batchSize: batchsize
}, function (msgs) {
for (var i = 0; i < msgs.length; i++) action(msgs[i]);
handle = setInterval(handlemessage, interval);
});
};
//longpolling = $.browser.opera && document.location.protocol.indexOf("file://") >= 0 ? true : longpolling;
//longpolling = $.browser.opera && huburl.indexOf(String.format("{0}.", document.domain)) < 0 ? true : longpolling;
this.start = function () {
if (running) return;
running = true;
if ("WebSocket" in window) {
websocket = new WebSocket(fn.setting.websocket());
websocket.onopen = function () {
websocket.send(JSON.stringify({ SubscriberName: subscriber, TransportName: transport,
BatchSize: batchsize, MessageTypeInfo: JSON.stringify(dto), Interval: interval
}));
};
websocket.onmessage = function (e) {
var msg = JSON.parse(e.data);
action(msg);
};
}
else if (!longpolling) {
hub = new fn.hub();
hub.onmessage = action;
hub.onclose = function () { /*stream closing*/ };
hub.open(huburl);
}
else {
handle = setInterval(handlemessage, interval);
}
};
this.stop = function () {
running = false;
if (longpolling) handle = clearInterval(handle);
try {
if (websocket) websocket.close();
else if (hub) hub.close();
else if (xhr) xhr.abort();
} catch (e) { }
xhr = null;
hub = null;
websocket = null;
};
};
fn.setting = (function () {
return {
apikey: function (value) { apikey = value; },
passcode: function (value) { passcode = value; },
rethrow: function (value) { rethrow = value; },
websocketport: function (port) { websocketPort = port; },
endpoint: function (value) { endpointAddress = value; },
includeIdentity: function (value) { includeIdentity = value; },
durable: function (value) { durable = value; },
longpolling: function (value) { longpolling = value; },
logging: false,
websocket: function () {
if (websocketAddress) return websocketAddress;
var url = endpointAddress.replace("//", "\\").split("/")[0],
tokens = url.split(":");
tokens[tokens.length - 1] = websocketPort;
tokens[0] = "ws";
url = tokens.join(":");
return url.replace("\\", "//");
},
transport: (function () {
var address = "localhost:5672;userID=guest;password=guest",
parseEndpoint = function (topicName, subscriberName) {
return address + ";queue=" + topicName + subscriberName;
};
return {
address: function (value) { address = value; },
type: fn.transport.rabbitmq,
info: function (topicName, subscriberName) {
switch (this.type) {
case fn.transport.msmq:
case fn.transport.rabbitmq:
case fn.transport.redis:
return {
Format: fn.format.json,
Path: parseEndpoint(topicName, subscriberName)
};
case fn.transport.ravendb:
return {
Format: fn.format.json,
ConnectionString: parseEndpoint(topicName, subscriberName)
};
case fn.transport.tcp:
var tokens = address.split(';'),
ipTokens = tokens[0].split(':'),
useSSL = tokens.length > 1 ?
("true" === tokens[1].toLowerCase) : false,
ipAddress = ipTokens[0],
port = parseInt(ipTokens[1]);
return {
Format: fn.format.json,
IPAddress: ipAddress, Port: port,
UseSSL: useSSL
};
}
}
};
})()
};
})();
fn.register = function (data) {
if (!data.topic)
throw "topic need to be set for register method";
ajax.helper.invoke("RegisterTopic", {
topicData:
JSON.stringify({
ContractDict: (data.info || {}),
TopicName: data.topic,
TopicDescription: data.description || data.topic})
});
};
fn.unregister = function (data) {
};
fn.publish = function (data) {
if (!data.topic || !data.message)
throw "topic and message property need to be set for publish method";
var message = data.message,
headers = data.headers || {},
expiration = data.expiration || fn.timespan(30)/*30 days*/;
if (includeIdentity) {
message[apikeytoken] = apikey;
message[passcodetoken] = passcode;
}
ajax.helper.invoke("PublishTopic",
{ topicName: data.topic,
topicData: JSON.stringify({ Headers: headers, ExpiresIn: expiration }),
publishData: JSON.stringify([message])
});
};
fn.unsubscribe = function (data) {
if (!data.topic)
throw "topic property need to be set for unsubscribe method";
var topicname = data.topic,
name = getusername();
ajax.helper.invoke("UnSubscribeFrom",
{ subscriber: name, topicName: topicname });
ajax.helper.invoke("DeleteTransport",
{ subscriber: name, transportName: topicname });
if (handlers[topicname])
handlers[topicname].stop();
};
fn.subscribe = function (data) {
if (!data.topic || !data.callback)
throw "topic and callback property need to be set for subscribe method";
if (typeof (data.callback) !== "function")
throw "callback must be a function";
var topicname = data.topic,
callback = data.callback,
username = getusername(),
filter = data.filter || "",
batchsize = data.batchsize || 1,
contract = {},
interval = data.interval || fn.timespan(0, 0, 0, 0, 500) /*500 millisecond*/,
handler;
ajax.helper.invoke("SelectTopic", { name: topicname }, function (t) {
contract = t.ContractDict;
ajax.helper.invoke("SelectSubscriber", { name: username }, function (s) {
var filters = filter.split(','),
topics = s.Topics,
transports = s.Transports,
state = s.State,
actions = [], i;
if (includeIdentity) {
filters.push(String.format(" {0} = {1}", apikeytoken, apikey));
filters.push(String.format(" {0} = {1}", passcodetoken, passcode));
}
filter = filters.join(",");
if (state === invalidstate)
actions.push(function () {
ajax.helper.invoke("CreateSubscriber", { subscriber: username });
});
if (!topics[topicname]) {
actions.push(function () {
ajax.helper.invoke("SubscribeTo",
{ subscriber: username, topicName: topicname, filter: filter });
});
actions.push(function () {
ajax.helper.invoke("AddTransport",
{ subscriber: username, transportName: topicname,
transportType: fn.setting.transport.type,
transportData:
JSON.stringify(fn.setting.transport.info(topicname, username)),
topicName: topicname
});
});
}
for (i = 0; i < actions.length; i++) actions[i]();
if (includeIdentity) {
contract[apikeytoken] = "";
contract[passcodetoken] = "";
}
handler = new messagehandler(username, topicname, contract, interval, 1, callback);
handler.start();
handlers[topicname] = handler;
});
});
};
$(window).bind("beforeunload", function () {
if (!durable)
ajax.helper.invoke("DeleteSubscriber", { name: getusername() });
fn.hubcallback();
});
} (PSERVICEBUS));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment