Skip to content

Instantly share code, notes, and snippets.

@juranki
Created July 23, 2009 22:22
Show Gist options
  • Save juranki/153684 to your computer and use it in GitHub Desktop.
Save juranki/153684 to your computer and use it in GitHub Desktop.
Ubiquity UI to RabbitMQ chat demo
(function($) {
var factoryServiceDefaults = {
factoryServiceUrl: "/rpc/rabbitmq",
timeout: 30000
};
var channelDefaults = {
rpcServiceUrlBase: "/rpc/",
username: "guest",
password: "guest",
virtualHost: null,
realm: "/data",
channelTimeout: 10 /* seconds; zero means "do not specify" */
};
var accessRequestDefaults = {
realm:"/data",
exclusive:false,
passive:true,
active:true,
write:true,
read:true
};
var exchangeDefaults = {
type:"direct",
passive:false,
durable:false,
auto_delete:false,
internal:false,
nowait:false,
args:{}
};
var queueDefaults = {
passive:false,
durable:false,
exclusive:false,
auto_delete:false,
nowait:false,
args:{}
};
var consumerDefaults = {
consumer_tag: "",
no_local: false,
no_ack: false,
exclusive: false
};
var rpcDefaults = {
timeout:0,
type: "POST",
contentType: "application/json",
processData: false,
dataType: "json",
error: function (xhr,status,error) {
try {
CmdUtils.log({rpc_error:error,
status:status,
xhr:xhr});
console.log({rpc_error:error,
status:status,
xhr:xhr}); }
catch (err) {}
}
};
var rpcId = 1;
var rpc = function(url, method, params, options) {
var req = {version: "1.1", id: rpcId++, method: method, params: params};
// var ajaxOpts = $.extend({url: url,data: JSON.stringify(req)},
var ajaxOpts = $.extend({url: url,data: Utils.encodeJson(req)},
rpcDefaults,options);
ajaxOpts["beforeSend"] = function(xhr){
xhr.setRequestHeader("Accept","application/json");
if(ajaxOpts.timeout) {
xhr.setRequestHeader("X-JSON-RPC-Timeout","" + ajaxOpts.timeout);
}
};
return $.ajax(ajaxOpts);
};
var args2list = function(args) {
var l = [];
var i;
for(i = 0; i < args.length; i++) {
l.push(args[i]);
}
return l;
};
var rpcService = function(url,readyCB,options) {
return rpc(url,"system.describe",[],{
success: function(sd) {
var res = sd.result;
var svc = {};
$.each(res.procs, function(i,desc) {
svc[desc.name] = function() {
var args = args2list(arguments);
var userCB = args.shift();
var cb = function(reply) {
if (reply.error) {
try {console.log(reply.error);} catch(err){}
} else {
userCB(reply.result);
}
};
var opts = $.extend({},options,{success:cb});
while(args.length < desc.params.length) {
args.push(null);
}
//CmdUtils.log("rpc: " + desc.name);
return rpc(url,desc.name,args,opts);
};
});
readyCB(svc);
}
});
};
$.openRabbitChannel = function(fnReady,options) {
var opts = $.extend({}, factoryServiceDefaults, options||{});
rpcService(opts.factoryServiceUrl,
function(factorySvc) {
new RabbitChannel(factorySvc,fnReady,opts);
},
opts);
};
var RabbitChannel = function(factorySvc,fnReady,opts) {
this.options = $.extend({},channelDefaults,opts);
this.consumers = {};
this.alive = true;
this.ticket = null;
this.svc = null;
this.async_handlers = {
"basic.deliver": function(args, content, props) {
var consumer = this.consumers[args[0]];
if (consumer) {
try {
consumer({
content: content,
delivery_tag: args[1],
redelivered: args[2],
exchange: args[3],
routing_key: args[4],
props: this._js_props(props)});
} catch (err) {}
}
}
};
var that = this;
factorySvc.open(chanCreatedCB,
this.options.username,
this.options.password,
this.options.channelTimeout,
this.options.virtualHost);
function chanCreatedCB(result) {
rpcService(that.options.rpcServiceUrlBase + result.service,
readyCB, {timeout: that.options.channelTimeout * 1000});
}
function readyCB(svc) {
that.svc = svc;
that.poll_tophalf();
that.accessRequest(fnReady,{realm:that.options.realm});
};
};
RabbitChannel.prototype.accessRequest = function(cb,opts) {
var that = this;
var o = $.extend({},accessRequestDefaults,opts||{});
this._call(function(result) {
that.ticket = result.args[0];
cb(that);
},
"access.request",
[o.realm,o.exclusive,o.passive,o.active,o.write,o.read]);
};
RabbitChannel.prototype.exchangeDeclare = function(cb,name,opts) {
var that = this;
var o = $.extend({},exchangeDefaults,opts||{});
this._call(cb,"exchange.declare",[this.ticket,
name,
o.type,
o.passive,
o.durable,
o.auto_delete,
o.internal,
o.nowait,
o.args])
};
RabbitChannel.prototype.queueDeclare = function(cb,name,opts) {
var that = this;
var o = $.extend({},queueDefaults,opts||{});
var cbwrap = function(result) {cb(result.args[0]);};
this._call(cbwrap,"queue.declare",[this.ticket,
name||"",
o.passive,
o.durable,
o.exclusive,
o.auto_delete,
o.nowait,
o.args]);
};
RabbitChannel.prototype.queueDelete = function(cb,name,if_unused,if_empty) {
var that = this;
var cbwrap = function(result) {cb(result.args[0]);};
this._call(cbwrap,"queue.delete",[this.ticket,
name||"",
if_unused||false,
if_empty||false,
false //nowait
]);
};
RabbitChannel.prototype.queueBind = function(cb,q,e,routing_key,args) {
var that = this;
this._call(cb,"queue.bind", [this.ticket,
q,
e,
routing_key||"",
false,//nowait
args||{}]);
};
RabbitChannel.prototype.basicConsume = function(cb,q,consumeCB,options) {
var that = this;
var o = $.extend({},consumerDefaults,options||{});
var cbwrap = function(result) {
var tag = result.args[0];
that.consumers[tag] = consumeCB;
cb(tag);
};
this._call(cbwrap, "basic.consume", [this.ticket,
q,
o.consumer_tag,
o.no_local,
o.no_ack,
o.exclusive,
false // nowait
]);
};
RabbitChannel.prototype.basicPublish = function(x, routing_key, message,
props, mandatory, immediate) {
this._cast("basic.publish", [this.ticket,
x,
routing_key,
mandatory||false,
immediate||false],
message, this._amqp_props(props || {}));
};
RabbitChannel.prototype.basicAck = function(delivery_tag, multiple) {
this._cast("basic.ack", [delivery_tag, multiple||false]);
};
RabbitChannel.prototype.basicCancel = function(cb,consumer_tag) {
var that = this;
var cbwrap = function(result) {
var tag = result.args[0];
delete that.consumers[tag];
cb(tag);
};
this._call(cbwrap, "basic.cancel", [consumer_tag, false /*nowait*/]);
};
RabbitChannel.prototype.poll_tophalf = function() {
var that = this;
if (this.alive) {
this.svc.poll(function(result) {
that.handlePollResult(result);
that.poll_tophalf();
});
}
};
RabbitChannel.prototype.close = function() {
var that = this;
if (this.alive) {
this.alive = false;
this.svc.close(function(result) {
that.handlePollResult(result);
});
}
};
RabbitChannel.prototype.handlePollResult = function(result) {
var that = this;
if (result === "stop") {
this.alive = false;
} else {
$.each(result,function(i,msg) {
that.handleAsyncMessage(msg);
});
}
};
RabbitChannel.prototype.handleAsyncMessage = function(msg) {
var handler = this.async_handlers[msg.method];
if(handler) {
handler.apply(this,[msg.args,msg.content,msg.props]);
} else {
try {console.log({async: msg});} catch(err) {}
}
};
RabbitChannel.prototype._call = function(cb,method,args) {
if (this.alive) {
this.svc.call(cb,method,args);
}
};
RabbitChannel.prototype._cast = function(method,args,content,props) {
var that = this;
if (this.alive) {
this.svc.cast(function(r) {that.handlePollResult(r);},
method,args,content,props);
}
};
RabbitChannel.prototype._js_props = function(props) {
return { content_type: props[0],
content_encoding: props[1],
headers: props[2],
delivery_mode: props[3],
priority: props[4],
correlation_id: props[5],
reply_to: props[6],
expiration: props[7],
message_id: props[8],
timestamp: props[9],
type: props[10],
user_id: props[11],
app_id: props[12],
cluster_id: props[13] };
};
RabbitChannel.prototype._amqp_props = function(props) {
return [props.content_type,
props.content_encoding,
props.headers,
props.delivery_mode,
props.priority,
props.correlation_id,
props.reply_to,
props.expiration,
props.message_id,
props.timestamp,
props.type,
props.user_id,
props.app_id,
props.cluster_id];
};
})(jQuery);
(function() {
var icon_url = "http://www.downloadatoz.com/_imgbank/ra/rabbitmq/RabbitMQ.icon.gif";
var channel = null;
var qName = null;
var xName = null;
var messages = [];
var nic = "user" + Math.floor(Math.random() * 1e10);
var prepareArgs = function(args) {
var txt = (args.object ? args.object.text : '');
var nick = (args.alias ? args.alias.text : null);
var goal = (args.goal ? args.goal.text : null);
if (!nick) {nick = nic;}
return {txt:txt,nick:nick,goal:goal};
};
var update = function(elem,args) {
$(elem).html("");
$(elem).append("<h3>RabbitMQ Chat</h3>");
if(!channel) {
$(elem).append("Not connected. <b>chat-toggle</b> to connect");
} else {
$(elem).append("<p>You're in <b>" + xName + "</b> as <b>" + nic + "</b></p>");
$.each(messages, function(i,m) {
var d = $("<div></div>");
d.append("<span style='margin-right:0.5em;font-weight:bold'>" + m.nic +
"</span><span>" + m.msg + "</span>");
$(elem).append(d);
});
if(args) {
var {txt,nick,goal} = prepareArgs(args);
if(txt) {
var d = $("<div style='border:1px groove #555;padding:2px;background:#888;color:black;text-shadow:0 0.1em 0.05em #aaa'></div>");
d.append("<span style='margin-right:0.5em;font-weight:bold'>" + nick +
"</span><span>" + txt + "</span>");
if(goal) {
d.append("<span style='margin-left:0.5em;font-weight:bold;font-size:90%'>(to " + goal + ")</span>");
}
$(elem).append(d);
}
}
}
};
var disconnect = function() {
if(channel) {
channel.close();
channel = null;
displayMessage("Disconnected");
}
};
var changeChannel = function(newXName, cb) {
if(!channel) { return; }
var onQBound = function() {
$("#chatoutput").html();
displayMessage("In room '" + xName + "'");
channel.basicConsume(function() {},
qName,
function(delivery) {
messages.push({nic:delivery.routing_key,
msg:delivery.content});
if (messages.length > 8) {
messages.shift();
}
displayMessage(delivery.routing_key + ": " +
delivery.content);
},
{ no_ack: true });
if(cb) {cb();}
};
var declareFreshQ = function() {
channel.queueDeclare(function(name) {
displayMessage("Q declared");
qName = name;
channel.queueBind(onQBound,qName,xName);
});
};
var onXDeclared = function() {
displayMessage("X declared");
if(qName) {
channel.queueDelete(declareFreshQ,qName);
} else {
declareFreshQ();
}
};
xName = newXName;
if (channel.exchangeDeclare) {
//CmdUtils.log("declaring");
displayMessage("declaring " + xName);
channel.exchangeDeclare(onXDeclared,xName,{type:"fanout"});
}
};
var send = function(text) {
if (!channel) {return;}
channel.basicPublish(xName, nic, text, { content_type: "text/plain" });
};
var connect = function() {
displayMessage("Connecting...");
$.openRabbitChannel(function(chan) {
channel = chan;
displayMessage("Joining room 'rabbit'...");
Utils.setTimeout(function() {changeChannel("rabbit");},100);
},
{
factoryServiceUrl: "http://chat.juranki.com/rpc/rabbitmq",
rpcServiceUrlBase: "http://chat.juranki.com/rpc/",
virtualHost: "/chat"
});
};
var reopen = function(ubi) {
Utils.setTimeout(function() {
var t = ubi.textBox;
ubi.openWindow();
$(t).val("chat-send ");
Utils.setTimeout(function() {t.setSelectionRange(10,10);},100);
},100);
};
var toggle_cmd = {
names:["chat-toggle", "chat-on/off"],
icon: icon_url,
description: "Toggle chat on and off",
preview: update,
execute: function execute() {
if(channel) {
disconnect();
} else {
connect();
}
}
};
var send_cmd = {
names:["chat-send"],
icon: icon_url,
description: "Send chat message",
arguments: [{role: 'object', nountype: noun_arb_text, label:'text'},
{role: 'alias', nountype: noun_arb_text, label:'nick'},
{role: 'goal', nountype: noun_arb_text, label:'room'}],
preview: update,
execute: function execute(args) {
var {txt,nick,goal} = prepareArgs(args);
var {gUbiquity} = context.chromeWindow;
if (nick) { nic = nick; }
if (goal) {
changeChannel(goal, function() {send(txt);});
} else {
send(txt);
}
reopen(gUbiquity);
}
};
CmdUtils.CreateCommand(toggle_cmd);
// CmdUtils.CreateCommand(disconnect_cmd);
//CmdUtils.CreateCommand(room_cmd);
CmdUtils.CreateCommand(send_cmd);
// CmdUtils.CreateCommand(nick_cmd);
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment