Commit 21f2c6a7 authored by Wandenberg Peixoto's avatar Wandenberg Peixoto

refactoring PushStream javascript client

parent 48c039f1
......@@ -7,6 +7,10 @@
</head>
<body>
<form action="/pub" method="POST">
<p>
<span style="display: block; float: left; width: 55px;">mode:</span>
<span id="mode" style="display:none;"></span>
</p>
<p>
<span style="display: block; float: left; width: 55px;">satus:</span>
<span class="online" style="display:none; color:green">online</span>
......@@ -30,13 +34,22 @@
</p>
<p><input type="submit" value="Send" id="sendButton"/></p>
</form>
<p><input type="button" value="Show Log" id="showLog"/></p>
<div id="Log4jsLogOutput" style="width:800px;height:200px;overflow:scroll;display:none;"></div>
<script src="js/jquery-1.4.2.min.js" type="text/javascript" language="javascript" charset="utf-8"></script>
<script src="js/log4js.js" type="text/javascript" language="javascript" charset="utf-8"></script>
<script src="js/pushstream.js" type="text/javascript" language="javascript" charset="utf-8"></script>
<script src="/js/jquery.min.js" type="text/javascript" language="javascript" charset="utf-8"></script>
<script src="/js/pushstream.js" type="text/javascript" language="javascript" charset="utf-8"></script>
<script type="text/javascript" language="javascript" charset="utf-8">
// <![CDATA[
$(function() {
PushStream.LOG_LEVEL = 'debug';
var pushstream = new PushStream({
host: window.location.hostname,
port: window.location.port,
modes: "eventsource|stream"
});
pushstream.onmessage = _manageEvent;
pushstream.onstatuschange = _statuschanged;
function onSendText() {
$("#message").val('');
};
......@@ -55,24 +68,27 @@
chat.scrollTop(chat[0].scrollHeight - chat.height());
};
function _statuschanged() {
if ((PushStream.status == 4) || (PushStream.status == 5)) {
function _statuschanged(state) {
if (state == PushStream.OPEN) {
$(".offline").hide();
$(".online").show();
$("#mode").html(pushstream.wrapper.type).show();
} else {
$(".online").hide();
$(".offline").show();
$(".online").hide();
$("#mode").html("").hide();
}
};
$("#room").change(function(){
if (PushStream.status == 5) {
PushStream.disconnect();
}
function _connect(channel) {
pushstream.removeAllChannels();
pushstream.addChannel(channel);
try {
pushstream.connect();
} catch(e) {alert(e)};
$("#chat").val('');
PushStream.joinChannel($("#room").val(), 0 );
PushStream.connect();
});
}
$("#sendButton").click(function(){
if (($("#nick").val() != "") && ($("#message").val() != "") && ($("#room").val() != "")) {
......@@ -84,17 +100,15 @@
return false;
});
var now = new Date();
var _hostId = (now.getTime() + "" + (Math.random() * 10000)).replace('.','');
$("#room").change(function(){
_connect($("#room").val());
});
PushStream.host = window.location.hostname;
PushStream.port = window.location.port;
PushStream.hostid = _hostId;
PushStream.registerEventCallback("process", _manageEvent);
PushStream.registerEventCallback("statuschanged", _statuschanged);
PushStream.joinChannel($("#room").val(), 0 );
PushStream.connect();
$("#showLog").click(function(){
$("#Log4jsLogOutput").show();
});
_connect($("#room").val());
// ]]>
</script>
</body>
......
Log4js = {
logLevel: 'error', /* debug, info, error */
logOutputId: 'Log4jsLogOutput',
debug : function(logstr) {
Log4js._log(logstr, 'debug');
},
info : function(logstr) {
Log4js._log(logstr, 'info');
},
error : function(logstr) {
Log4js._log(logstr, 'error');
},
_log: function(logstr, level) {
if ((Log4js.logLevel === level) || ('error' === level) || (Log4js.logLevel === 'debug') ) {
if (window.console) {
window.console.log(logstr);
} else if (document.getElementById(Log4js.logOutputId)) {
document.getElementById(Log4js.logOutputId).innerHTML += logstr+"<br/>";
}
}
}
};
PushStream = {
callbacks: {
process: function() {},
reset: function() {},
eof: function() {},
statuschanged: function() {}
},
host: null,
port: 80,
hostid: null,
status: 0,
channelcount: 0,
channels: {},
lastrequest: 0,
frameref: null,
frameloadtimer: null,
frameloadingtimeout: 15000,
pingtimer: null,
pingingtimeout: 30000,
reconnecttimer: null,
reconnecttimeout: 3000,
checkChannelAvailabilityInterval: 60000,
backtrackDefault: 10,
mode: 'iframe',
connect: function() {
Log4js.debug('entering connect');
if (!PushStream.host) throw "PushStream host not specified";
if (isNaN(PushStream.port)) throw "PushStream port not specified";
if (!PushStream.channelcount) throw "No channels specified";
if (!PushStream.hostid) PushStream.hostid = t+""+Math.floor(Math.random()*1000000);
document.domain = PushStream.extract_xss_domain(document.domain);
if (PushStream.status) PushStream.disconnect();
PushStream.setStatus(1);
var now = new Date();
var t = now.getTime();
PushStream.loadFrame(PushStream.getSubsUrl());
PushStream.lastrequest = t;
Log4js.debug('leaving connect');
},
reconnect: function(interval) {
if (PushStream.status != 6) {
PushStream.reconnecttimer = setTimeout(PushStream.connect, interval || PushStream.reconnecttimeout);
}
},
disconnect: function() {
Log4js.debug('entering disconnect');
if (PushStream.status) {
PushStream.clearPingtimer();
PushStream.clearFrameloadtimer();
PushStream.clearReconnecttimer();
if (typeof CollectGarbage == 'function') CollectGarbage();
if (PushStream.status != 6) PushStream.setStatus(0);
Log4js.info("Disconnected");
}
Log4js.debug('leaving disconnect');
},
joinChannel: function(channelname, backtrack) {
Log4js.debug('entering joinChannel');
if (typeof(PushStream.channels[channelname]) != "undefined") throw "Cannot join channel "+channelname+": already subscribed";
PushStream.channels[channelname] = {backtrack:backtrack, lastmsgreceived:-1};
Log4js.info("Joined channel " + channelname);
PushStream.channelcount++;
if (PushStream.status != 0) PushStream.connect();
Log4js.debug('leaving joinChannel');
},
loadFrame: function(url) {
try {
var transferDoc = (!PushStream.frameref) ? new ActiveXObject("htmlfile") : PushStream.frameref;
transferDoc.open();
transferDoc.write("<html><script>document.domain=\""+(document.domain)+"\";</script></html>");
transferDoc.parentWindow.PushStream = PushStream;
transferDoc.close();
var ifrDiv = transferDoc.createElement("div");
transferDoc.appendChild(ifrDiv);
ifrDiv.innerHTML = "<iframe src=\""+url+"\" onload=\"PushStream.frameload();\"></iframe>";
PushStream.frameref = transferDoc;
} catch (e) {
if (!PushStream.frameref) {
var ifr = document.createElement("IFRAME");
ifr.style.width = "10px";
ifr.style.height = "10px";
ifr.style.border = "none";
ifr.style.position = "absolute";
ifr.style.top = "-10px";
ifr.style.marginTop = "-10px";
ifr.style.zIndex = "-20";
ifr.PushStream = PushStream;
ifr.onload = PushStream.frameload;
document.body.appendChild(ifr);
PushStream.frameref = ifr;
}
PushStream.frameref.setAttribute("src", url);
}
Log4js.info("Loading URL '" + url + "' into frame...");
PushStream.frameloadtimer = setTimeout(PushStream.frameloadtimeout, PushStream.frameloadingtimeout);
},
frameload: function() {
Log4js.info("Frame loaded whitout streaming");
PushStream.clearFrameloadtimer();
PushStream.setStatus(8);
PushStream.reconnect(PushStream.checkChannelAvailabilityInterval);
},
frameloadtimeout: function() {
Log4js.info("Frame load timeout");
PushStream.clearFrameloadtimer();
PushStream.setStatus(3);
PushStream.reconnect(PushStream.frameloadingtimeout);
},
register: function(ifr) {
PushStream.clearFrameloadtimer();
ifr.p = PushStream.process;
ifr.r = PushStream.reset;
ifr.eof = PushStream.eof;
PushStream.setStatus(4);
PushStream.setPingtimer();
Log4js.info("Frame registered");
},
pingtimeout: function() {
Log4js.info("Ping timeout");
PushStream.setStatus(7);
PushStream.clearPingtimer();
PushStream.reconnect();
},
process: function(id, channel, data) {
Log4js.info("Message received");
PushStream.setStatus(5);
PushStream.clearPingtimer();
if (id == -1) {
Log4js.debug("Ping");
} else if (typeof(PushStream.channels[channel]) != "undefined") {
Log4js.debug("Message " + id + " received on channel " + channel + " (last id on channel: " + PushStream.channels[channel].lastmsgreceived + ")\n" + data);
PushStream.callbacks["process"](data);
PushStream.channels[channel].lastmsgreceived = id;
}
PushStream.setPingtimer();
},
reset: function() {
if (PushStream.status != 6) {
Log4js.info("Stream reset");
PushStream.callbacks["reset"]();
PushStream.reconnect();
}
},
eof: function() {
Log4js.info("Received end of stream, will not reconnect");
PushStream.callbacks["eof"]();
PushStream.setStatus(6);
PushStream.disconnect();
},
setStatus: function(newstatus) {
// Statuses: 0 = Uninitialised,
// 1 = Loading stream,
// 2 = Loading controller frame,
// 3 = Controller frame timeout, retrying.
// 4 = Controller frame loaded and ready
// 5 = Receiving data
// 6 = End of stream, will not reconnect
// 7 = Ping Timeout
// 8 = Frame loaded whitout streaming, channel problably empty or not exists
if (PushStream.status != newstatus) {
Log4js.info('PushStream.status ' + newstatus);
PushStream.status = newstatus;
PushStream.callbacks["statuschanged"](newstatus);
}
},
registerEventCallback: function(evt, funcRef) {
Function.prototype.andThen=function(g) {
var f=this;
var a=PushStream.arguments
return function(args) {
f(a);g(args);
}
};
if (typeof PushStream.callbacks[evt] == "function") {
PushStream.callbacks[evt] = (PushStream.callbacks[evt]).andThen(funcRef);
} else {
PushStream.callbacks[evt] = funcRef;
}
},
extract_xss_domain: function(old_domain) {
if (old_domain.match(/^(\d{1,3}\.){3}\d{1,3}$/)) return old_domain;
domain_pieces = old_domain.split('.');
return domain_pieces.slice(-2, domain_pieces.length).join(".");
},
getSubsUrl: function() {
var surl = "http://" + PushStream.host + ((PushStream.port==80)?"":":"+PushStream.port) + "/sub";
for (var c in PushStream.channels) {
var channelinfo = "/" + c + PushStream.getBacktrack(c);
surl += channelinfo;
}
var now = new Date();
surl += "?nc="+now.getTime();
return surl;
},
getBacktrack: function(channelName) {
var channel = PushStream.channels[channelName];
if (channel.backtrack != 0) {
var backtrack = ".b"
if (channel.backtrack > 0) {
backtrack += channel.backtrack
} else {
backtrack += PushStream.backtrackDefault;
}
return backtrack;
} else return "";
},
clearPingtimer: function() {
if (PushStream.pingtimer) {
clearTimeout(PushStream.pingtimer);
PushStream.pingtimer = null;
}
},
setPingtimer: function() {
PushStream.clearPingtimer();
PushStream.pingtimer = setTimeout(PushStream.pingtimeout, PushStream.pingingtimeout);
},
clearFrameloadtimer: function() {
if (PushStream.frameloadtimer) {
if (PushStream.frameloadtimer) clearTimeout(PushStream.frameloadtimer);
PushStream.frameloadtimer = null;
}
},
clearReconnecttimer: function() {
if (PushStream.reconnecttimer) {
if (PushStream.reconnecttimer) clearTimeout(PushStream.reconnecttimer);
PushStream.reconnecttimer = null;
}
}
};
This diff is collapsed.
......@@ -3,8 +3,145 @@ describe("PushStream", function() {
beforeEach(function() {
});
it("should use default port", function() {
expect(PushStream.port).toEqual(80);
describe("when defining library external interface", function() {
it("should has a class named PushStream", function() {
expect(new PushStream()).toBeDefined();
});
it("should has a log level constant", function() {
expect(PushStream.LOG_LEVEL).toBeDefined();
});
it("should has a log output element id constant", function() {
expect(PushStream.LOG_OUTPUT_ELEMENT_ID).toBeDefined();
});
it("should define status code constants", function() {
expect(PushStream.CLOSED).toBeDefined();
expect(PushStream.CONNECTING).toBeDefined();
});
});
describe("when using default values", function() {
var pushstream = null;
beforeEach(function() {
pushstream = new PushStream();
});
it("should use current hostname", function() {
expect(pushstream.host).toBe(window.location.hostname);
});
it("should use port 80", function() {
expect(pushstream.port).toBe(80);
});
it("should not use ssl", function() {
expect(pushstream.useSSL).toBeFalsy();
});
it("should set state as uninitialised", function() {
expect(pushstream.readyState).toBe(PushStream.CLOSED);
});
it("should use '/sub' as url prefix for stream", function() {
expect(pushstream.urlPrefixStream).toBe('/sub');
});
it("should use '/ev' as url prefix for event source", function() {
expect(pushstream.urlPrefixEventsource).toBe('/ev');
});
it("should use '/lp' as url prefix for long-polling", function() {
expect(pushstream.urlPrefixLongpolling).toBe('/lp');
});
it("should has all modes availables", function() {
expect(pushstream.modes).toEqual(['eventsource', 'stream', 'longpolling']);
});
it("should define callbacks attributes", function() {
expect(pushstream.onopen).toBeDefined();
expect(pushstream.onmessage).toBeDefined();
expect(pushstream.onerror).toBeDefined();
expect(pushstream.onstatuschange).toBeDefined();
});
it("should has an empty channels list", function() {
expect(pushstream.channels).toEqual({});
expect(pushstream.channelsCount).toBe(0);
});
});
describe("when manipulating channels", function() {
var pushstream = null;
beforeEach(function() {
pushstream = new PushStream();
});
describe("and is not connected", function() {
describe("and is adding a channel", function() {
it("should keep channel name", function() {
pushstream.addChannel("ch1");
expect(pushstream.channels.ch1).toBeDefined();
});
it("should keep channel options", function() {
var options = {key:"value"};
pushstream.addChannel("ch2", options);
expect(pushstream.channels.ch2).toBe(options);
});
it("should increment channels counter", function() {
var count = pushstream.channelsCount;
pushstream.addChannel("ch3");
expect(pushstream.channelsCount).toBe(count + 1);
});
});
describe("and is removing a channel", function() {
beforeEach(function() {
pushstream.addChannel("ch1", {key:"value1"});
pushstream.addChannel("ch2", {key:"value2"});
pushstream.addChannel("ch3");
});
it("should remove channel name and options", function() {
pushstream.removeChannel("ch2");
expect(pushstream.channels.ch1).toEqual({key:"value1"});
expect(pushstream.channels.ch2).not.toBeDefined();
expect(pushstream.channels.ch3).toBeDefined();
});
it("should decrement channels counter", function() {
var count = pushstream.channelsCount;
pushstream.removeChannel("ch2");
expect(pushstream.channelsCount).toBe(count - 1);
});
});
describe("and is removing all channels", function() {
beforeEach(function() {
pushstream.addChannel("ch1", {key:"value1"});
pushstream.addChannel("ch2", {key:"value2"});
pushstream.addChannel("ch3");
});
it("should remove channels names and options", function() {
pushstream.removeAllChannels();
expect(pushstream.channels.ch1).not.toBeDefined();
expect(pushstream.channels.ch2).not.toBeDefined();
expect(pushstream.channels.ch3).not.toBeDefined();
});
it("should reset channels counter", function() {
pushstream.removeAllChannels();
expect(pushstream.channelsCount).toBe(0);
});
});
});
});
});
......@@ -11,9 +11,7 @@
# - dist/**/*.js
#
src_files:
- misc/examples/js/jquery-1.4.2.min.js
- misc/examples/js/log4js.js
- misc/examples/js/pushstream.js
- misc/js/pushstream.js
# stylesheets
#
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment