d3js chart update triggered on websocket message - javascript

Running an autobahn websocket server in Python and picking up json updates in the html frontend. The value (in this case "temp") is being displayed as a number with innerHTML when message arrive and that part works fine.
We also got a dial chart (d3js) but the update is initiated by the setInterval function at the end of the code which is far from ideal. How can I make the gauge update 'onmessage' through the websocket?
<script type="text/javascript">
//---the websocket part--
var sock = null;
var displaynumber = null;
var tempdata = null;
window.onload = function () {
var wsuri;
displaynumber = document.getElementById('disp');
if (window.location.protocol === "file:") {
wsuri = "ws://...:9000";
} else {
wsuri = "ws://" + window.location.hostname + ":9000";
}
//set up a new websocket
if ("WebSocket" in window) {
sock = new WebSocket(wsuri);
} else if ("MozWebSocket" in window) {
sock = new MozWebSocket(wsuri);
} else {
disp("Browser does not support WebSocket!");
window.location = "http://autobahn.ws/unsupportedbrowser";
}
if (sock) {
//sock.onopen = function() {};
sock.onclose = function(e) {
sock = null;
};
sock.onmessage = function(e) {
// e.data will be sent as a string and need to be converted to object
var jsondata = JSON.parse(e.data);
tempdata = jsondata.temp;
disp(tempdata + " C");
};
}
function disp(m) {
displaynumber.innerHTML = m;
}
//----This is the dial chart part
dialChart();
function dialChart() {
var powerGauge = gauge('#power-gauge', {
size: 300,
clipWidth: 300,
clipHeight: 300,
ringWidth: 60,
minValue: 0,
maxValue: 100,
transitionMs: 1000,
});
powerGauge.render();
function updateReadings() {
powerGauge.update(tempdata);
}
updateReadings();
setInterval(function() {
updateReadings();
}, 2 * 1000);
}
}
</script>

At the end of the definition of dialChart, add return powerGauge. Then when you call dialChart();, save the result to a variable. Then, instead of writing to tempdata in the socket callback, call powerGaugeVariable.update(jsondata.temp).

Related

How do I test my WebSocket which is developed in JavaScript

I have created WebSocket.js but unfortunately I am unable to test it due to unavailability of data. The requirement is the data comes from various sources so I have multiple sockets for that. Note: I want to particularly test my socket.onMessage behaves for different sockets. Please find the code snippet below:
var webSocket;
var txQueue = [];
var defaultReconnectTimeout = 1000; //will be multiplied by 2 and saved into reconnectTimeout on each try
var reconnectTimeout = defaultReconnectTimeout;
var registerWebSocketHandlers = function(webSocket) {
webSocket.onclose = function(){
setTimeout(service.reopen, reconnectTimeout *= 2);
};
webSocket.onopen = function(e) {
reconnectTimeout = defaultReconnectTimeout; //reset this
deferredSend();
};
webSocket.onerror = function(e) {
throw new Error("[WebSocket] An error occured " + e);
};
}
var uniqid = function() {
return (new Date().getTime()).toString(16);
}
var deferredSend = function() {
if(!service.isOpen()) {
$timeout(deferredSend, 100);
return;
}
while(txQueue.length && service.isOpen()) {
var payload = txQueue.shift();
webSocket.send(typeof payload === 'string' ? payload : JSON.stringify(payload));
}
};
var createNewWebSocketInstance = function(apiUrl){
var websocket = new $window.WebSocket(apiUrl);
websocket.id = uniqid();
return websocket;
}
// TODO: this is a bit hacky since we directly bind it to the raw window event
$window.onbeforeunload = function(e) {
service.close();
};
var service = {};
service.setMessageEventHandler = function(name,cb) {
instances[name].onmessage = function(msg) {
if(msg.data.indexOf('Status: connected') === 0)
{
return;
}
var jsonObj = JSON.parse(msg.data);
cb(jsonObj);
};
};
service.isOpen = function() {
return webSocket.readyState === 1;
};
service.send = function(msg) {
txQueue.push(msg);
deferredSend();
};
service.close = function() {
return webSocket.close();
};
service.reopen = function() {
// get old message handler
var msgHandler = webSocket.onmessage;
// try closing the previous WebSocket
service.close();
// open new WebSocket
openConnection();
// re-attach old handler to new WebSocket
webSocket.onmessage = msgHandler;
};
service.getId = function() {
return webSocket.id;
}
// Returns an already existing instance of the socket, if unavailable then creates a new one.
service.getInstance = function(name, config) {
if(!(name in instances)) {
instances[name] = createNewWebSocketInstance(config);
}
registerWebSocketHandlers(instances[name]);
return instances[name];
};
return service;
You can test WebSocket using websocket.html at websocket.org Echo Test Creating your own test
Using a text editor, copy the following code and save it as
websocket.html somewhere on your hard drive. Then simply open it in a browser. The page will automatically connect, send a message,
display the response, and close the connection.
See Linux - WebSocket test error.

How do I define and receive data from multiple WebSockets in JavaScript?

I have the following code snippet which I need to extend to define multiple WebSockets and I am clueless as to how do I go about it:
var registerWebSocketHandlers = function(webSocket) {
webSocket.onclose = function(){
setTimeout(service.reopen, reconnectTimeout *= 2);
};
webSocket.onopen = function(e) {
icc.publish('webSocket.reconnect');
reconnectTimeout = defaultReconnectTimeout; //reset this
deferredSend();
};
webSocket.onerror = function(e) {
throw new Error("[WebSocket] An error occured " + e);
};
}
var openConnection = function() {
connectionWasOpenBefore = true;
webSocket = new $window.WebSocket(xyz);
webSocket.id = uniqid();
registerWebSocketHandlers(webSocket);
};
var uniqid = function() {
return (new Date().getTime()).toString(16);
}
service.setMessageEventHandler = function(cb) {
webSocket.onmessage = function(msg) {
if(msg.data.indexOf('Status: connected') === 0)
{
return;
}
var jsonObj = JSON.parse(msg.data);
cb(jsonObj);
};
};
How do I twist the code to suit the needs of multiple WebSockets and attaching the appropriate callback to it?
Use the multiton pattern.
var socketFactory = module.factory('SocketFactory', function($rootScope){
var factory = {};
var instances = {};
factory.getInstance = function(name, config){
if(!(name in instances)){
instances[name] = createNewWebSocketInstance(name, config);
}
return instances[name];
};
var createNewWebSocketInstance = function(name, config){
var webSocket = new $window.WebSocket(config.address);
webSocket.id = uniqid();
registerWebSocketHandlers(webSocket, name, config.handlers); //etc.
return webSocket;
};
var registerWebSocketHandlers = function(webSocket, name, handlers){
webSocket.onmessage = function(event){
$rootScope.$emit('SocketMessageReceived_' + name, event.data);
};
//etc...
};
return factory;
});
This will separate your different websockets by name. Use getInstance('whatever') to get a websocket labelled as 'whatever'.
var firstConfig = {url: '*****', username: '****', password: '****', etc: '****'};
// You only need to pass in the config the first time.
var firstWebSocket = SocketFactory.getInstance('firstSocket', firstConfig);
var secondConfig = {url: '####', username: '####', password: '####', etc: '####'};
var secondWebSocket = SocketFactory.getInstance('secondSocket', secondConfig);
Next, from any other area you can access the configured websockets by their instance names.
var firstWebSocket = SocketFactory.getInstance('firstSocket');
// It would probably be a good idea to add this listener in the SocketFactory instead and broadcast an event when there's a message so multiple listeners can respond to it.
firstWebSocket.onmessage = function(){...};
var secondWebSocket = SocketFactory.getInstance('secondSocket');
secondWebSocket.onmessage = function(){...};

Calling data from JSON file using AJAX

I am trying to load some data from my JSON file using AJAX. The file is called external-file.json. Here is the code, it includes other parts that haven't got to do with the data loading.The part I'm not sure of begins in the getViaAjax funtion. I can't seem to find my error.
function flip(){
if(vlib_front.style.transform){
el.children[1].style.transform = "";
el.children[0].style.transform = "";
} else {
el.children[1].style.transform = "perspective(600px) rotateY(-180deg)";
el.children[0].style.transform = "perspective(600px) rotateY(0deg)";
}
}
var vlib_front = document.getElementById('front');
var el = document.getElementById('flip3D');
el.addEventListener('click', flip);
var word = null; var explanation = null;
var i=0;
function updateDiv(id, content) {
document.getElementById(id).innerHTML = content;
document.getElementById(id).innerHTML = content;
}
updateDiv('the-h',word[i]);
updateDiv('the-p',explanation[i])
function counter (index, step){
if (word[index+step] !== undefined) {
index+=step;
i=index;
updateDiv('the-h',word[index]);
updateDiv('the-p',explanation[index]);
}
}
var decos = document.getElementById('deco');
decos.addEventListener('click', function() {
counter(i,-1);
}, false);
var incos = document.getElementById('inco');
incos.addEventListener('click', function() {
counter(i,+1);
}, false);
function getViaAjax("external-file.json", callback) { // url being the url to external File holding the json
var r = new XMLHttpRequest();
r.open("GET", "external-file.json", true);
r.onload = function() {
if(this.status < 400 && this.status > 199) {
if(typeof callback === "function")
callback(JSON.parse(this.response));
} else {
console.log("err");// server reached but gave shitty status code}
};
}
r.onerror = function(err) {console.log("error Ajax.get "+url);console.log(err);}
r.send();
}
function yourLoadingFunction(jsonData) {
word = jsonData.words;
explanation = jsonData.explanation;
updateDiv('the-h',word[i]);
updateDiv('the-p',explanation[i])
// then call whatever it is to trigger the update within the page
}
getViaAjax("external-file.json", yourLoadingFunction)
As #light said, this:
function getViaAjax("external-file.json", callback) { // url being the url to external File holding the json
var r = new XMLHttpRequest();
r.open("GET", "external-file.json", true);
Should be:
function getViaAjax(url, callback) { // url being the url to external File holding the json
var r = new XMLHttpRequest();
r.open("GET", url, true);
I built up a quick sample that I can share that might help you isolate your issue. Stand this up in a local http-server of your choice and you should see JSON.parse(xhr.response) return a javascript array containing two objects.
There are two files
data.json
index.html
data.json
[{
"id":1,
"value":"foo"
},
{
"id":2,
"value":"bar"
}]
index.html
<html>
<head>
</head>
<body onload="so.getJsonStuffs()">
<h1>so.json-with-ajax</h1>
<script type="application/javascript">
var so = (function(){
function loadData(data){
var list = document.createElement("ul");
list.id = "data-list";
data.forEach(function(element){
var item = document.createElement("li");
var content = document.createTextNode(JSON.stringify(element));
item.appendChild(content);
list.appendChild(item);
});
document.body.appendChild(list);
}
var load = function()
{
console.log("Initializing xhr");
var xhr = new XMLHttpRequest();
xhr.onload = function(e){
console.log("response has returned");
if(xhr.status > 200
&& xhr.status < 400) {
var payload = JSON.parse(xhr.response);
console.log(payload);
loadData(payload);
}
}
var uri = "data.json";
console.log("opening resource request");
xhr.open("GET", uri, true);
xhr.send();
}
return {
getJsonStuffs : load
}
})();
</script>
</body>
</html>
Running will log two Javascript objects to the Dev Tools console as well as add a ul to the DOM containing a list item for every object inside the data.json array

Capture the call that analytics.js makes after it is loaded using phantomjs

I have been trying to Capture the call that analytics.js makes after it is loaded in phantomjs headless browser.
The problem with this is that, analytics.js load after the page completely loads. So, its getting difficult to track analytics.js calls.
The code which I have tried till now is:
var url = "http://www.alexandani.com/necklaces/sand-dollar-expandable-necklace.html";
var auditlinks = {"www.google-analytics.com/analytics.js": 6, "metrics.alexandani.com": 9, "www.google-analytics.com/collect": 7};
var block_request = 1;
var execution_timeout = 40000;
var resource_timeout = 50000;
var inactivity_timeout = 50000;
var user_agent = "Mozilla/5.0 (Unknown; Linux x86_64) AppleWebKit/534.34 (KHTML, like Gecko) PhantomJS/1.9.0 Safari/534.34";
var page_http_status_target_url = url;
var page_http_status = null;
var inactivity_timeout_check_period = 100;
var requests = new Array();
var auditlink_urls = Object.keys(auditlinks);
function print(obj){
console.log(JSON.stringify(obj));
}
function create_url_cleaner(){
var rx_match_protocol = /^(http|https):\/\//i;
var rx_match_query_params = /\/*\?.*/i;
function clean_url(url){
return (
(url.replace(rx_match_protocol, ''))
.replace(rx_match_query_params, '')
);
}
return clean_url;
}
function exit(exit_reason){
console.log(requests);
print({
'requests': requests,
'exit_reason': exit_reason,
// Returning http status as an integer makes little sense to me.
'http_status': (page_http_status === null)
? null : page_http_status.toString(),
});
phantom.exit(0);
}
function start_exec_time_limiter(execution_timeout){
setTimeout(
function (){
console.log('hi');
exit("EXEC_TIMEOUT");
},
execution_timeout
);
}
function start_inactivity_tracker(
inactivity_timeout,
inactivity_timeout_check_period
){
var last_activity_time = Date.now();
function register_activity(){
last_activity_time = Date.now();
}
function check_inactivity(){
var now = Date.now();
if (now - last_activity_time > inactivity_timeout){
exit("INACTIVITY_TIMEOUT")
}
}
setInterval(check_inactivity, inactivity_timeout_check_period);
return register_activity;
}
start_exec_time_limiter(execution_timeout);
var clean_url = create_url_cleaner();
var register_activity = start_inactivity_tracker(
inactivity_timeout, inactivity_timeout_check_period);
var page = require('webpage').create();
page.settings.userAgent = user_agent;
page.settings.resourceTimeout = resource_timeout;
//page.injectJs('wait.js');
page.onError = function (msg, stack){
// Ignore errors in the webpage context.
}
page.onResourceReceived = function (response){
register_activity();
if (response.url == page_http_status_target_url){
if (response.redirectURL){
page_http_status_target_url = response.redirectURL;
}
else {
page_http_status = response.status;
page_http_status_target_url = null;
}
}
}
page.onResourceRequested = function (requestData, request){
register_activity();
var timestamp = Date.now();
var url = requestData["url"];
var bare_url = clean_url(url);
for (var k in auditlink_urls){
var alurl = auditlink_urls[k];
if (bare_url.indexOf(alurl) === 0){
requests[requests.length] = [url, auditlinks[alurl], timestamp];
if (block_request === true){
request.abort();
}
break;
}
}
}
page.open(url);
//---------------------------------------------------------------------------//
Why do you need to load analytics.js at all in PhantomJS? If all you want to do is test that the calls to ga() are what you'd expect, just stub out the ga() function and assert that the calls it receives are what you'd expect.
In fact, the analytics.js snippet itself is essentially just a stub that stores the calls it receives on its q property while it waits for the full library to download.
If you don't download the analytics.js script, that ga.q will always be inspectable.

Object has no method "open" error when using indexedDB

I'm trying to build a small class-like container that will make it a little cleaner to load and store data from the HTML5 IndexedDB. To be honest this is the first time I've ever played with this feature, so my issue could be trivial.
I'm basing my code off of this tutorial:
http://www.html5rocks.com/en/tutorials/indexeddb/todo/
function DBDictionary()
{
this.Holder = {};
this.Entries = new Array();
this.Opened = false;
this.v = "1.0";
this.Holder.indexedDB = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB;
if ('webkitIndexedDB' in window)
{
window.IDBTransaction = window.webkitIDBTransaction;
window.IDBKeyRange = window.webkitIDBKeyRange;
}
this.Holder.indexedDB = {};
this.Holder.indexedDB.db = null;
this.Holder.indexedDB.onerror = function(e)
{
console.log(e);
};
this.DownloadDB = function()
{
if(this.Opened) return;
var request = this.Holder.indexedDB.open("Storage");
request.onsuccess = function(e)
{
this.Holder.indexedDB.db = e.target.result;
var db = this.Holder.indexedDB.db;
// We can only create Object stores in a setVersion transaction;
if (v!= db.version)
{
var setVrequest = db.setVersion(v);
// onsuccess is the only place we can create Object Stores
setVrequest.onerror = this.Holder.indexedDB.onerror;
setVrequest.onsuccess = function(e)
{
if(db.objectStoreNames.contains("Storage")) db.deleteObjectStore("Storage");
var store = db.createObjectStore("Storage", {keyPath: "Key"});
this.PopulateAll();
};
}
else
{
this.PopulateAll();
}
};
request.onerror = this.Holder.indexedDB.onerror;
};
this.UploadDB = function()
{
this.DeleteAll();
this.SaveAll();
};
this.DeleteAll = function()
{
var db = this.Holder.indexedDB.db;
var trans = db.transaction(["Storage"], IDBTransaction.READ_WRITE);
var store = trans.objectStore("Storage");
Entries.forEach(function(element, index, array)
{
var request = store.delete(index);
request.onerror = function(e)
{
console.log("Error Deleting: ", e);
};
});
};
this.PopulateAll = function()
{
var db = this.Holder.indexedDB.db;
var trans = db.transaction(["Storage"], IDBTransaction.READ_WRITE);
var store = trans.objectStore("Storage");
// Get everything in the store;
var keyRange = IDBKeyRange.lowerBound(0);
var cursorRequest = store.openCursor(keyRange);
cursorRequest.onsuccess = function(e)
{
var result = e.target.result;
//No more results to load
if(!!result == false)
{
if(!this.Opened) this.Opened = true;
return;
}
this.Entries[result.Key] = result.Value;
result.continue();
};
cursorRequest.onerror = this.Holder.indexedDB.onerror;
};
this.SaveAll = function()
{
var db = this.Holder.indexedDB.db;
var trans = db.transaction(["Storage"], IDBTransaction.READ_WRITE);
var store = trans.objectStore("Storage");
Entries.forEach(function(element, index, array)
{
var data = {
"Key": index,
"Value": element,
"timeStamp": new Date().getTime()
};
var request = store.put(data);
request.onerror = function(e) {
console.log("Error Adding: ", e);
};
});
};
}
function main()
{
var dictionary = new DBDictionary();
dictionary.DownloadDB();
dictionary.Entries["hello"] = "world";
alert(dictionary.Entries["hello"]);
}
$(document).ready(main);
My desired implemented state should look something like this:
function main()
{
var dictionary = new DBDictionary();
dictionary.DownloadDB();
dictionary.Entries["hello"] = "world";
alert(dictionary.Entries["hello"]);
}
$(document).ready(main);
What this should do is download the data from the browser's IndexedDB object and store them into the object-housed array Entries. When I want to store the value of Entires back into the DB, I would call dictionary.UploadDB();
However, I'm getting the single javascript error: Uncaught TypeError: Object # has no method 'open'. I'm pretty much at a loss as to what I'm doing wrong. Can anyone offer me some tips?
Do a typeof check and console.log the this.Holder.indexedDB object to inspect the prototype. Does it inherit the IDBDatabase prototype? If it does, the open method would be available to you.
If your window.indexedDB did fire the on success callback, e.target.result would be the correct way to access the newly opened database via the event object. But the fact that you're not getting that far suggests that your this.Holder.indexedDB object is not actually an instance of an IDBDatabase.
EDIT: Yes, this is exactly your issue. If you console.log the this.holder.indexedDB object you get an object that looks like {"db":null}.
Swap this.Holder.indexedDB for window.webkitIndexedDB at your open invocation and you'll see that 'world' alert pops. JSFiddle here.

Categories