backbone.js sync using websocket - javascript

I'm trying to use WebSocket for replacing Backbone.sync method.
Then I created three instance of "Sample" model named sample1, sample2 and sample3 and call fetch() method. Each request was successfully sent to the server.
However, when response data coming from the server, every response is set to the sample1. In other word, backbone.js triggered change event and only sample1 instance catch it.
I know there's good library named backbone.WS. However i would like to know why below code didn't work properly. Please give me any comments.
$(document).ready(function() {
var ws;
var url = ws://localhost:8080/application/sample;
Backbone.sync = function(method, model, options) {
var data = JSON.stringify({
'method' : method,
'data' : model.attributes}
);
if(ws == null) {
console.log("Create a new connection to the server.");
ws = new WebSocket(url);
console.log("Binding callback methods.");
ws.onopen = onOpen;
ws.onmessage = onMessage;
ws.onclose = onClose;
ws.onerror = onError;
}
function send(message) {
// Wait until the state of the socket is not ready and send the message when it is...
waitForSocketConnection(ws, function(){
console.log("message sent!!!");
ws.send(message);
});
}
// Make the function wait until the connection is made...
function waitForSocketConnection(socket, callback){
setTimeout(
function () {
if (socket.readyState === 1) {
console.log("Connection is made")
if(callback != null){
callback();
}
return;
} else {
console.log("Waiting for connecting...")
waitForSocketConnection(socket, callback);
}
}, 10); // wait 5 milisecond for the connection...
}
function onOpen(event) {
console.info("Connection established to " + url);
};
function onMessage(event) {
console.info("Message Received!");
var message = JSON.parse(event.data);
console.log("model: " + JSON.stringify(model.attributes));
model.set(message);
};
function onClose(event) {
console.log(event.code);
};
function onError() {
console.warn("There was an error with your websocket.");
}
send(data);
console.log("Request is sent to the server.");
options.success(message);
};
var Sample = Backbone.Model.extend({
defaults : {
id : 0,
name : "default"
}
});
var sample1 = new Sample({id:1});
var sample2 = new Sample({id:2});
var sample3 = new Sample({id:3});
sample1.fetch();
sample2.fetch();
sample3.fetch();
sample1.on("change", function(model) {
console.log("--- onChange method in sample 1");
});
sample2.on("change", function(model) {
console.log("+++ onChange method in sample 2");
});
sample3.on("change", function(model) {
console.log("=== onChange method in sample 3");
});
}

Related

Detect request url of an ajax request

So I have used the below function to detect an ajax call.
var oldXHR = window.XMLHttpRequest;
function newXHR() {
var realXHR = new oldXHR();
realXHR.addEventListener("readystatechange", function() {
if(realXHR.readyState==1){
alert('server connection established');
}
if(realXHR.readyState==2){
alert('request received');
}
if(realXHR.readyState==3){
alert('processing request');
}
if(realXHR.readyState==4){
alert('request finished and response is ready');
}
}, false);
return realXHR;
}
window.XMLHttpRequest = newXHR;
It is working but now I need the url of that particular ajax request. I have functions like below:-
function loadFundSimulation(num_days = ''){
var url = "<?php echo site_url('investment_plan/simulation/FUND'); ?>";
$.post(url, data).done(function (response,status,xhr) {
#....code....#
}).fail(function (data) {
#....code....#
});
}
When the ajax is being called at that time I want url of this functions. I have many functions like this. When I get the url I want to append ?debug = 1 at the end of the url. I have tried alert(this.url); but it was returning undefined. Any help will appreciated. Thanks in advance.
Edit
var open = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function(method, uri, async, user, pass) {
this.addEventListener("readystatechange", function(event) {
if(this.readyState == 4){
var self = this;
var response = {
method: method,
uri: uri,
responseText: self.responseText
};
response.uri = uri + '?debug=1';
console.log(response);
} else {
console.log(this.readyState);
}
}, false);
open.call(this, method, uri, async, user, pass);
};
I have got the url of that ajax request and I appended ?debug=1 as well. When I console.log(response); I see the url is being changed but I still don't see any error. Please let me know I have to do anything else for that.
After searching a lot this is the best way to do this. Though only tested on chrome.
(function() {
var proxied = window.XMLHttpRequest.prototype.open;
window.XMLHttpRequest.prototype.open = function() {
arguments[1] = arguments[1] + '&debug=1';
console.log( arguments[1] );
return proxied.apply(this, [].slice.call(arguments));
};
})();

How To Reuse Token From FireBase

I am implementing Firebase Web Notification using Firebase.
The whole process is:
There are two files: a) generate-token.js b) firebase-messaging.sw.js
Permission to get access token( By calling requestPermission).
After clicking Allow, token is generated.
An Ajax call is made to backend service to save the token correspond to that browser.
We have separate module for Notification-sender and Notification-Manager to schedule and send notification.
firebase-messaging.sw.js is at root. It is called when notification is called.
There are 2 main methods for this:
initMessaging.setBackgroundMessageHandler(function (payload) {
)}; ( For background handling of message)
and
messaging.onMessage(function(payload) {}); : This method is in generate-token.js for receiving foreground message.
When notification is received, an ajax call is made to api to track notification with actionId=1 and for clicking notification actionId=3.
My questions:
In step 1, once I make an ajax call to save the token, I have to reuse that token in my methods of firebase-messaging.sw.js to hit track api (Track api require token and deviceId)
Currently, I am making device_id using userAgent, which may clash with some other machine. Is there a better way?
How can I reuse my token, as service-worker dont have access to local storage, DOM, cookies,etc.?
My code:
generate-token.js:
firebase.initializeApp(config);
const messaging=firebase.messaging();
var postURL=null;
var gcmId=null;
var id=getUniqueId();
var id1=id.split('.').join("");
if ('serviceWorker' in navigator) {
// Register service worker
navigator.serviceWorker.register('/firebase-messaging-sw.js').then(function (reg) {
console.log("SW registration succeeded. Scope is " + reg.scope);
console.log("Updating service worker" +reg.update());
}).catch(function (err) {
console.error("SW registration failed with error " + err);
});
}
messaging.onMessage(function(payload) {
console.log("OnMessage called app is in foreground");
//tracking notification with actionid=1;
trackNotification(payload.data,1,postURL,gcmId);
});
function requestPermission(url) {
console.log('Requesting permission...');
postURL=url;
var firebaseRefUrl=firebase.database().ref().child(location.host.split('.').join("")+"/" + "url");
firebaseRefUrl.set(url);
messaging.requestPermission()
.then(function () {
console.log('Notification permission granted.');
return messaging.getToken();
})
.then(function(token){
userAction(token,url);
var firebaseRefToken=firebase.database().ref().child(location.host.split('.').join("") + "/" + id1);
firebaseRefToken.set(token);
gcmId=token;
})
.catch(function (err) {
console.log('Unable to get permission for notification.', err);
});
}
function getUniqueId(){
var Sys = {};
var ua = navigator.userAgent.toLowerCase();
return ua;
}
function userAction(token,url) {
try{
console.log("Calling wrapper interface to save gcm_id");
var obj = new Object();
obj.deviceId =getUniqueId();
obj.token=token;
var jsonString= JSON.stringify(obj);
var xhttp = new XMLHttpRequest();
xhttp.open("POST", url+ "/registerGCM", true);
xhttp.setRequestHeader("Content-type", "application/json");
xhttp.send(jsonString);
xhttp.onreadystatechange = function() {
if(xhttp.readyState == XMLHttpRequest.DONE && xhttp.status == 200) {
var jsonDeviceId=JSON.parse(xhttp.responseText);
localStorage.setItem("deviceId",jsonDeviceId.deviceId);
}
}
}catch(error) {
console.log('Error while calling apis.', error);
}}
firebase-messaging.sw.js:
firebase.initializeApp(config);
var click=null;
var url;
var token;
var obj=new Object();
//Initialise firebase messaging
const initMessaging = firebase.messaging();
var id=getUniqueId();
var id1=id.split('.').join("");
var firebaseRefToken=firebase.database().ref().child(location.host.split('.').join("") + "/" + id1);
firebaseRefToken.on('value',function(data){
token=data.val();
});
var firebaseRefUrl=firebase.database().ref().child(location.host.split('.').join("") +"/" + "url");
firebaseRefUrl.on('value',function(data){
url=data.val();
});
//Background push notification handler. It fires up when the browser or web page in which push notification is activated are closed.
initMessaging.setBackgroundMessageHandler(function (payload) {
//console.log("In background");
console.log("Tracking notification when the app is in background");
var gcmId;
var tokenRefCheck=firebase.database().ref().child(location.host.split('.').join("") + "/" + id1);
tokenRefCheck.on('value',function(data){
gcmId=data.val();
});
trackNotification(obj.data,1,url,null);
});
//Displays incoming push notification
self.addEventListener('push', function (event) {
console.log('Push Notification Received.');
var eventData = event.data.text();
obj = JSON.parse(eventData); //Parse the received JSON object.
//printing payload
console.log("PAyload is " + JSON.stringify(obj,2,null));
const title = obj.data.title;
click=obj.data.targetActionData;
const options = {
body: obj.data.body,
icon: obj.data.icon,
click_action:obj.data.targetActionData
};
event.preventDefault();
event.waitUntil(self.registration.showNotification(title, options));
});
//Take action when a user clicks on the received notification.
self.addEventListener('notificationclick', function (event) {
console.log("Notification clicked");
event.notification.close();
event.preventDefault(); // prevent the browser from focusing the Notification's tab
trackNotification(obj.data,3,url,null);
event.waitUntil(
clients.openWindow(click)
);
});
self.addEventListener('notificationclose', function (event) {
console.log("Notification closed");
event.notification.close();
event.preventDefault(); // prevent the browser from focusing the Notification's tab
trackNotification(obj.data,2,url,null);
});
function trackNotification(data,actionId,url,gcmId) {
try{
var obj=new Object();
console.log("Calling track notification api to save the data");
if(actionId===1){
console.log("Tracking for receving notification");
}
if(actionId===2){
console.log("Tracking for closing notification");
}
if(actionId===3){
console.log("Tracking for clicking notification");
}
obj.deviceId =getUniqueId();
obj.campaignId=data.campaignId;
obj.actionTime=new Date();
if(gcmId!=null)
obj.token=gcmId;
else
obj.token=token;
obj.actionId=actionId;
obj.scheduleId=data.scheduleId;
var jsonString= JSON.stringify(obj,2,null);
/*xhttp.open("POST", postURL+ "/trackNotification", true);
xhttp.setRequestHeader("Content-type", "application/json");
xhttp.send(jsonString);*/
console.log("Payload" + jsonString);
fetch(url + "/trackNotification", {
method: 'post',
headers: {
"Content-type": "application/json; charset=UTF-8"
},
body: jsonString
})
.then(function (data) {
})
.catch(function (error) {
console.log('Request failed', error);
});
}catch(error) {
console.log('Error while calling apis.', error);
}}
function getUniqueId(){
var Sys = {};
var ua = navigator.userAgent.toLowerCase();
return ua;
}
I am using Firebase Database, and save the token with useragent as a key, but it will sure collide with some other device.
You can save the data in the service worker cache.
The Service Worker API comes with a Cache interface, that lets you
create stores of responses keyed by request. While this interface was
intended for service workers it is actually exposed on the window, and
can be accessed from anywhere in your scripts. The entry point is
caches.
https://developers.google.com/web/ilt/pwa/caching-files-with-service-worker

Javascript call method in another method

Now I'm trying to implement Unity Webgl with jslib. I'm so confused about how to call method in another method's function. I want to call method Recv when message was coming (ws.onmessage). But, it show "TypeError: this.Recv is undefined". Could you please help me figure out this source? Thank you !!!!!
Here's my source code
var ws = null;
var init_url = "";
var received_msg = "";
var error_msg = "";
var WebsocketLib = {
Hello: function(){
window.alert("Hello,world!");
},
InitSocket: function(url){
init_url = Pointer_stringify(url);
console.log("InitWebSocket: "+init_url);
ws = new WebSocket(init_url);
ws.onopen = function(evt){
console.log("Connect");
isConnected = false;
ws.send("hello");
};
ws.onclose = function(evt) {
console.log("Close");
isConnected = false;
};
ws.onmessage = function(evt) {
received_msg = evt.data;
console.log("[recv] "+received_msg);
this.Recv.call(this);
};
ws.onerror = function(evt) {
error_msg = evt.data;
console.log("[error] "+error_msg);
this.Error.call(this);
};
},
Recv: function(){
console.log("[recv] "+received_msg);
var buffer = _malloc(received_msg.length + 1);
writeStringToMemory(returnStr, buffer);
return buffer;
},
Error: function(){
console.log("[error] "+error_msg);
var buffer = _malloc(error_msg.length + 1);
writeStringToMemory(error_msg, buffer);
return buffer;
}
}
Inside of ws.onmessage this will refer to ws (as we're inside a method of ws) and not WebsocketLib.
However, inside Initsocket, where you define the handlers, this would correctly (in the sense that this is what you want) refer to the WebsocketLib object, so you can create a bound function to bind the outer this value to be used as this inside the event handler, like this:
ws.onmessage = function(evt) {
received_msg = evt.data;
console.log("[recv] "+received_msg);
this.Recv.call(this);
}.bind(this);
in JavaScript the value of this behaves differently than in other languages. Its value depends on how the function is called. You can read more about it in the Mozilla MDN page.
To solve your specific problem you can:
InitSocket: function(url){
var that = this; // [1]
init_url = Pointer_stringify(url);
console.log("InitWebSocket: "+init_url);
ws = new WebSocket(init_url);
ws.onopen = function(evt){
console.log("Connect");
isConnected = false;
ws.send("hello");
};
ws.onclose = function(evt) {
console.log("Close");
isConnected = false;
};
ws.onmessage = function(evt) {
received_msg = evt.data;
console.log("[recv] "+received_msg);
that.Recv.call(that); // [2]
};
ws.onerror = function(evt) {
error_msg = evt.data;
console.log("[error] "+error_msg);
that.Error.call(that); // [2]
};
},
In line 1 I bind the this variable to a custom variable that I decided to call that (but you can call it as you want). Then in line 2 I used that instead of this.
Inside the ws.onmessage function the value of this is not referring to the instance of WebsocketLib, so you need to use this "trick" and access the right this value using the one saved in the closure, inside the value of that.

Firefox SDK Error "Image contains errors and cannot be displayed"

I am developing an extension for Mozilla Firefox, where I override the native listener with my own and monitor all HTTP requests, as shown in the post here:
http://www.softwareishard.com/blog/firebug/nsitraceablechannel-intercept-http-traffic/
I monitor those requests that reside under a specific domain and change their corresponding response body, with the response body I receive from my own XMLHTTPRequest. For text files, everything is working fine.
However, I face a problem while downloading images. For some reason, when I write the incoming data to the stream and then, try to open the image, I get the error that the image contains errors and cannot be displayed.
What am I possibly doing wrong?
Update: I provide some code, in order to clarify my approach.
var xmlRequest = Cc['#mozilla.org/xmlextras/xmlhttprequest;1'].createInstance(Ci.nsIXMLHttpRequest);
...
xmlRequest.open('GET', xmlRequestURL, false);
xmlRequest.send(null);
function TracingListener() {}
TracingListener.prototype = {
originalListener: null,
onStartRequest: function (request, context) {
httpChannel = request.QueryInterface(Ci.nsIHttpChannel);
requestURL = httpChannel.URI.spec;
try {
this.originalListener.onStartRequest(request, context);
}
catch (ex) {
request.cancel(ex);
}
},
onDataAvailable: function (request, context, inputStream, offset, count) {
httpChannel = request.QueryInterface(Ci.nsIHttpChannel);
requestURL = httpChannel.URI.spec;
//Read the contents from the stream, but ignore them.
var binaryInputStream = CCIN('#mozilla.org/binaryinputstream;1', 'nsIBinaryInputStream');
binaryInputStream.setInputStream(inputStream);
var binaryOutputStream = CCIN('#mozilla.org/binaryoutputstream;1', 'nsIBinaryOutputStream');
var data = binaryInputStream.readBytes(count);
//Delay the call to the original listener.
},
onStopRequest: function (request, context, statusCode) {
httpChannel = request.QueryInterface(Ci.nsIHttpChannel);
requestURL = httpChannel.URI.spec;
//Check if the response is successful.
if(xmlRequest.status == 200) {
try {
var responseLen = xmlRequest.getResponseHeader("Content-Length");
var response = xmlRequest.response;
var storageStream = CCIN('#mozilla.org/storagestream;1', 'nsIStorageStream');
storageStream.init(8192, responseLen, null);
var binaryOutputStream = CCIN('#mozilla.org/binaryoutputstream;1', 'nsIBinaryOutputStream');
binaryOutputStream.setOutputStream(storageStream.getOutputStream(0));
binaryOutputStream.writeBytes(response, responseLen);
//Make the call to the original listener.
this.originalListener.onDataAvailable(request, context, storageStream.newInputStream(0), 0, responseLen);
}
catch (e) {
dumpError(e);
}
//Pass it to the original listener
this.originalListener.onStopRequest(request, context, statusCode);
}
else {
console.log('[INFO] onStopRequest not processed, status is ' + xmlRequest.status + ', for URL: ' + requestURL);
}
}
}
var observer = {
httpRequestObserver: {
observe: function (request, aTopic, aData) {
httpChannel = request.QueryInterface(Ci.nsIHttpChannel);
requestURL = httpChannel.URI.spec;
if(mustBeMonitored(requestURL)) {
console.log('[INFO] Observing URL: ' + requestURL);
if (aTopic == 'http-on-modify-request') {
console.log('[INFO] ' + aTopic + ' for URL: ' + requestURL);
var newListener = new TracingListener();
request.QueryInterface(Ci.nsITraceableChannel);
newListener.originalListener = request.setNewListener(newListener);
}
}
},
register: function () {
observerService.addObserver(observer.httpRequestObserver, 'http-on-modify-request', false);
},
unregister: function () {
observerService.removeObserver(observer.httpRequestObserver, 'http-on-modify-request');
},
QueryInterface: function (aIID) {
/*if (typeof Cc == "undefined") {
var Cc = components.classes;
}
if (typeof Ci == "undefined") {
var Ci = components.interfaces;
}*/
if (aIID.equals(Ci.nsIObserver) || aIID.equals(Ci.nsISupports))
return this;
throw components.results.NS_NOINTERFACE;
}
}
};
Finally, I was able to detect the problem. For the XMLHttpRequest, I had to specify its response type as follows:
xmlRequest.responseType = 'arraybuffer';
Then, the response was stored in a JavaScript ArrayBuffer, which I had to transform into a Uint8Array and then, store it into the stream.
This solution applies for both binary and text files.

When I call terminate on my web worker what state is it in?

I have a web worker that I'm using to poll for information.
Here is the code for starting and stopping the web worker:
var eftWorker = undefined;
that.StartWorker = function () {
if (eftWorker === undefined) {
eftWorker = new Worker('../scripts/ETEL.EftWorker.js');
eftWorker.addEventListener('message', function(e) {
EftWorkerResponseHandler(e.data);
}, false);
eftWorker.addEventListener('error', function(e) {
EftWorkerErrorResponseHandler(e);
}, false);
}
eftWorker.postMessage({ cmd: eftCmdStart });
};
that.StopWorker = function () {
if (eftWorker !== undefined) {
eftWorker.postMessage({ cmd: eftCmdStop });
eftWorker.terminate();
}
eftWorker = undefined;
};
When I call terminate on the worker, because the worker is polling there seems to be a backlog of unprocessed postmessage events.
I'm setting the web worker to "undefined" on initialisation of the containing view and on termination of the web worker. I believe because of the latter, those unprocessed events are shown as ABORT_ERRORs.
Is there an intermediate state that I can use to test the existence of the web worker so that I can allow it to process the outstanding events and therefore avoid the errors?
Or is there a different approach that I might use to avoid the accumulation of errors after terminate is called?
Here is my solution to the problem.
I'm recording the state of the worker in a separate variable in order that I can keep it alive to handle the outstanding messages that are causing the errors.
Also I'm trapping and discarding any errors generated inside the worker itself.
var eftWorker = undefined;
var eftWorkerState = undefined;
var workerStateStarted = 'started';
var workerStateStopped = 'stopped';
var StartWorker = function () {
if (eftWorker === undefined | eftWorkerState !== workerStateStarted) {
eftWorker = new Worker('/scripts/ETEL.EftWorker.js');
eftWorker.addEventListener('message', function(e) {
EftWorkerResponseHandler(e.data);
}, false);
eftWorker.addEventListener('error', function (e) {
EftWorkerErrorResponseHandler(e);
}, false);
}
eftWorker.postMessage({ cmd: eftCmdStart });
eftWorkerState = workerStateStarted;
};
that.StopWorker = function () {
if (eftWorker !== undefined) {
eftWorker.postMessage({ cmd: eftCmdStop });
eftWorker.terminate();
}
eftWorkerState = workerStateStopped;
//eftWorker = undefined;
};
var EftWorkerResponseHandler = function (msg) {
try {
if (eftWorkerState === workerStateStarted) {
if (msg != '' && msg !== undefined) {
var parser = new DOMParser();
var xmlDoc = parser.parseFromString(msg, 'text/xml');
var json = $.xmlToJSON(xmlDoc);
AlterPaymentUIAccordingToEftWorkerStatus(json);
}
}
} catch (exception) { }
};
And here's the code from the worker responsible for sending back the status messages.
EftSendGetRequest = function(passedUrl) {
if (xmlHttpReq === undefined) {
xmlHttpReq = new XMLHttpRequest();
}
try {
xmlHttpReq.open("GET", passedUrl, false);
xmlHttpReq.send();
} catch (e) { }
return xmlHttpReq.responseText;
};
This is old but I was looking at it while searching for a different answer..
Why not let the worker handle its own state and termination? The original question asks how to let the worker finish outstanding requests. So let it finish its requests and indicate when it is done.
If the javascript was something like this:
var eftWorker = undefined;
var StartWorker = function () {
if (eftWorker === undefined) {
eftWorker = new Worker('/scripts/ETEL.EftWorker.js');
eftWorker.addEventListener('message', function(e) {
EftWorkerResponseHandler(e.data);
}, false);
eftWorker.addEventListener('error', function (e) {
EftWorkerErrorResponseHandler(e);
}, false);
// i'm assuming we don't want to trigger start multiple times,
// so this is moved inside the IF.
eftWorker.postMessage({ cmd: eftCmdStart });
}
};
that.StopWorker = function () {
if (eftWorker !== undefined) {
eftWorker.postMessage({ cmd: eftCmdStop });
}
};
var EftWorkerResponseHandler = function (msg) {
try {
if (msg && msg === 'readyToTerminate') {
eftWorker.terminate();
eftWorker = undefined;
} else {
// handle other situations.
}
} catch (exception) { }
};
and the worker was like this:
;(function(self, undefined) {
var receivedStopCmd = false;
self.addEventListener('message', function(e){
if (e.data.cmd === 'eftCmdStart') {
// kick off processing here
EftSendGetRequest('...');
}
if (e.data.cmd === 'eftCmdStop') {
// xhr might be in process, this just updates what
// the onload function does.
receivedStopCmd = true;
}
}, false);
var EftSendGetRequest = function(passedUrl) {
if (xmlHttpReq === undefined) {
xmlHttpReq = new XMLHttpRequest();
}
try {
xmlHttpReq.open("GET", passedUrl, false);
xmlHttpReq.onload = function(){
if (!receivedStopCmd) {
// post response and/or keep polling
self.postMessage('whatever the message is');
} else {
// (1) stop polling so no more
// requests are sent..if this
// requires doing anyhting
// (2) Send a message that this worker can be terminated.
self.postMessage('readyToTerminate');
}
};
xmlHttpReq.send();
} catch (e) { }
return xmlHttpReq.responseText;
};
})(self);
This would allow the XHR to manage itself. I didn't run this of course.. its just an example of the approach I would take on the question.

Categories