Newbie: Javascript, Jquery Classes and Objects - javascript

I am rather confused with mechanics and hierarchy of things in javascript, reading few links and pages are adding to them.
There are various ways to create objects in Javascript. What I am getting confused is in following:
I have 3 websocket say WSServiceA, WSServiceB, WSServiceC : These are 3 different sockets and provide different data but data structure is same. Using a simple function I am updating 3 tables in web-page.
Each of these have onopen, onclose and onmessage function so I have simply copied same code 3 times, like this:
WSServiceA.onmessage(){}
WSServiceB.onmessage(){}
WSServiceC.onmessage(){}
If I create a something like this:
function CreateService(Service, WSAddress){
this.service=Service;
this.WSAddress = WSAddress;
this.websocket = new WebSocket(this.WSAddres);
this.onopen = function(event){ Send some message }
this.onmessage = function(event){ Update Table A}
}
After that I will create
ServiceA = CreateService("A", "ws://192.168.100.1.1:10001");
ServiceB = CreateService("B", "ws://192.168.100.1.2:10001");
ServiceC = CreateService("C", "ws://192.168.100.1.3:10001");
Then how and who will call ServiceA.onopen, Service.onmessage and other functions - when data comes to those sockets? Will it be called at all? Something created inside function should be visible only in that function - I am not able to understand flow and link. Probably I am missing some key concept or this should not work.

Background
There is ECMAScript, which is the language, and javascript, which is a generic name for an implementation of ECMAScript for a particular host (noting that JavaScript™ is a trademark of Oracle Corporation).
ECMAScript by itself does very little, it doesn't even have a mechanism for input or output, it must be provided by the host. In regard to WebSockets, if the host supports them then it will implement the WebSockets API.
When you create a WebSocket through new WebSocket(...), then the host returns a host object that implements the interface. The object will have a state that changes in response to its environment (connecting, open, closing, closed) and it will dispatch events when certain things happen.
When the socket successfully makes a connection to the server, it will dispatch an open event on the socket, so that if there is an onopen listener, it will respond to the event by being called by the open handler. There are also events for error and close that have associated on* handlers.
Answer
Then how and who will call ServiceA.onopen, Service.onmessage and
other functions - when data comes to those sockets?
The host environment implementing the interface does all that.

Related

Blazor server side problem share javascript code

I'm developing my project with Blazor Server-side.
While I develop, I used javascript code to implement things that hard to implement by C#.
However, I'm facing something weird situation. (I guess it is problem for javascript)
Suppose there are 2 users(A, B). When 'A' user do some action that call javascript code, if 'B' user into same page, 'A' users action affects to 'B' user.
I implemented web page that have 3d scene with threejs. As I explained above, when User 'A' move some object with mouse event(mousemove, mousedown..), if User 'B' accesses the same page, 3d objects of B are moved to the location where User 'A' moved.
Originally, when user access to web page I developed, 3d objects's position should be 0,0,0.
My Guess
I don't use prototype or class(use variable and functions globally. I'm new to javascript.. )
Javascript runs on server-side(share resources??, If then, how can I solve it)
I'm guessing the javascript would be problem, but if you have any other opinions, would you please share?
Edited
I've solved this problem using DotNetObjectReference.Create(this);
C#
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
//send created instance to javascript
var dotNetObjRef = DotNetObjectReference.Create(this);
await JSRuntime.InvokeVoidAsync("SetObjectRef", dotNetObjRef);
}
await base.OnAfterRenderAsync(firstRender);
}
[JSInvokable]
public async Task enableSomething(bool bEnable)
{
var something = bEnable;
}
//== before edit
//[JSInvokable]
//public static async Task enableSomethingStatic(bool bEnable)
//{
// var something = bEnable;
//}
Javascript
var objectRef;
function SetObjectRef(ref) {
objectRef = ref;
}
//call c# function
objectRef.invokeMethodAsync("enableSomething", true);
It was problem of 'static' method as I guessed.
If you declare C# method called from javascript as 'static' and this method changes something of UI variable, this method can affect another users.
So I create instance of current page and send it javascript and when I need to call C# methods from javascript, I call methods using created instance.
Is there any problem or issue, please share it.
Sorry for my bad English.
JavaScript runs client side only. I don't see how two windows, let alone two users, would share data.
Almost for sure, the problem is that you are injecting a singleton service-- which means the server will use one instance for all users.
If so, you have two choices:
(1) add logic to your singleton service to incorporate users. (For example, a dictionary with UserID/Property name for key, and a column for Value)
(2) go to Startup.cs and change the suspect singleton service to .AddScoped(), which will create a new instance for each user.
For right now, I think the latter solution will solve your problem immediately. However, don't underestimate the value of Singletons-- they'll be very useful for other things.

Is there a way to reply to only the sender, after receiving a BroadcastChannel message?

Suppose I have a bunch of same-origin windows or tabs A, B, C, D, and E, that don't hold references to each other. (e.g. a user opened them independently). Suppose A sends a BroadcastChannel message to the others, and as a result, D needs to send some data back to A, ideally without involving B, C, or E.
Is this possible, using any of the message-passing APIs?
There's an event.source property on the broadcast message event, which looked as if it should maybe contain a WindowProxy or MessagePort object in this context, but (in my tests with Firefox 78 at least) it was simply null. There's also a ports array, but that was empty.
...I'm aware that you could start up a SharedWorker to assign each window a unique ID and act as a waystation for passing messages between them, but (a) that seems very complicated for the functionality desired, and (b) every message sent that way is going to need 2 hops, from window to sharedWorker and back to a window, crossing thread boundaries both times, and (usually) getting serialized & unserialized both times as well - even when the two windows share the same javascript thread! So it's not very efficient.
This seems like such an obvious thing to want to do, I'm finding it hard to believe there isn't something obvious I'm missing... but I don't see it, if so!
Looks like the standards require source to be null for a BroadcastChannel. But it shares the MessageEvent interface with several other APIs that do use source, hence why it exists, but is null.
The postMessage(message) method steps are:
...
5. Remove source from destinations.
Looks like they intentionally kept BroadcastChannel very lightweight. Just a guess, but the functionality you're looking for might have required additional resources that they didn't want to allocate. This guess is based on a general note they have in the spec:
For elaborate cases, e.g. to manage locking of shared state, to manage synchronization of resources between a server and multiple local clients, to share a WebSocket connection with a remote host, and so forth, shared workers are the most appropriate solution.
For simple cases, though, where a shared worker would be an unreasonable overhead, authors can use the simple channel-based broadcast mechanism described in this section.
SharedWorkers are definitely more appropriate for complicated cases, think of the BroadcastChannel really just as a one-to-many simple notification sender.
It isn't able to transfer data — Which of the receivers should become the owner then? — so except in the case of Blobs (which are just small wrappers with no data of their own), passing data through a BroadcastChannel means it has to be fully deserialized by all receivers, not the most performant way of doing.
So I'm not sure what kind of data you need to send, but if it's big data that should normally be transferable, then probably prefer a SharedWorker.
One workaround though if your data is not to be transfered, is to create a new BroadcastChannel that only your two contexts will listen at.
Live demo
In page A:
const common_channel = new BroadcastChannel( "main" );
const uuid = "private-" + Math.random();
common_channel.postMessage( {
type: "gimme the data",
from: "pageB",
respondAt: uuid
} );
const private_channel = new BroadcastChannel( uuid );
private_channel.onmessage = ({data}) => {
handleDataFromPageB(data);
private_channel.close();
};
In page B:
const common_channel = new BroadcastChannel( "main" );
common_channel.onmessage = ({ data }) => {
if( data.from === "pageB" && data.type === "gimme the data" ) {
const private_channel = new BroadcastChannel( data.respondAt );
private_channel.postMessage( the_data );
private_channel.close();
}
};
Regarding why you can't have a ports value on MessageEvent firing on BroadcastChannels it's because MessagePorts must be transfered, but as we already said, BroadcastChannels can't do transfers.
For why there is no source, it's probably because as you expected that should have been a WindowProxy object, but WorkerContexts can also post messages to BroadcastChannels, and they don't implement that interface (e.g their postMessage method wouldn't do the same thing at all than for a WindowContext).

How to store/stash JavaScript event and reuse it later?

From https://developers.google.com/web/fundamentals/app-install-banners/#trigger-m68
let deferredPrompt;
window.addEventListener('beforeinstallprompt', (e) => {
e.preventDefault();
// Stash the event so it can be triggered later.
deferredPrompt = e;
});
This code is fine, but I want to trigger the stashed event later, in a different place. To perform that, I need to store an event not just in a variable, but somewhere else.
The question: how can an event be stored with its methods?
I tried Local Storage with serialization/deserialization of an object:
> localStorage.setItem('stashed-event', JSON.stringify(e))
>
> JSON.parse(localStorage.getItem('stashed-event'))
But this approach doesn't work as expected, because it's storing only key-values and losing all event methods.
You cannot store an event in this manner. You want to store an object. Only serializable properties are storable for such an object. Functions are not serializable in JavaScript. Functions are not serializable in many languages.
Fundamentally this is basically because when you deserialize an object, its signature can change. If you have ever programmed in java, this is similar to a deserialization error when reading in a serialized object and attempting to reconstruct an object. Because the body of a method function of an object can change in between the time the object is written to some storage and then later read, methods are not serializable. This is because when you serialize an object, it does not serialize its interface definition where methods are defined. It just stores data.
Same reason when you serialize to a json string, it drops the functions.
Instead of storing an event, store the useful information from the event in an object (or let things be implicitly dropped by stringify and use the event directly).
Which method of storage you use just depends on things not mentioned in your question. Such as how long it should be stored, whether it should be available outside of your site's origin, how much data will typically be stored, whether there is more than one object to store, etc. Based on the limited information provided in your question, you are probably fine just using either localStorage or an in memory array.
If you find the need to store hundreds of objects then indexedDB would begin to be more appropriate. But just choosing a different storage medium will have no effect whatsoever on whether you can store functions. You cannot store functions.
There have been loads of talk around this as soon as I/O 2018 mentioned about handling of A2HS event being developer driven from now onwards. This is also captured in the official doc and inspired from it, there is a beautiful article explaining thoroughly how to achieve exactly this scenario. While I'd suggest to go through the complete article for proper understanding of the updated dynamics around the A2HS flow, feel free to jump onto the "The New Add To Homescreen Flow" section for your requirement.
In a nutshell, follow the following steps:
Create a variable outside the scope of the beforeinstallprompt event handler.
Save a reference to the beforeinstallprompt event object in the above handler.
Use this later to trigger the add to homescreen prompt on demand.
The article have the complete code snippets which you can refer/reuse.
Edit: I read your question once again and realized one important aspect you might be specifically looking for, viz., using it "somewhere else". If this means you are referring to using it on a different page, then my suggestion would be to go for storing the event object in:
IndexedDB which is a collection of "object stores" which you can just drop objects into. Disadvantage - Can have browser compatibility restrictions. Also, can result in large amount of nested callbacks.
Or you can choose to use the "in process cache" (heap memory of your application) which doesn't require serializing either. Disadvantage - This cannot be shared across multiple servers though.
Other than this, I cannot foresee a con free solution at the moment. But will try to figure it out and possibly update the thread.
After reading your question a few times, and the answers another few,
The question: how can any javascript Object be stored with its methods?
The answer: there is no how.
However,
Josh properly explained you can extract and store all the serializable properties, say data, from your event.
I will just add you can create an event with somehow that same data later anywhere, this new event will have all the methods any Event has, but by now probably none of use.
Obviously, even serialized in your data, properties like timeStamp, isTrusted, etc... will be overriden at creating the new event.
What you just miss / need is an EventTarget, the value of the event.target property,
the reference which is lost forever when document.body unloads forever, or when serializing the event Object.
But if it is still alive, or if you know what event.target should be, like a DOM Element or any Object you can reference, from wherever you recreate the event (where?), just dispatch your event to that object, if it listens to that event.type,
your brand new event should be at least heard.
Simple example from MDN EventTarget, or see EventTarget.dispatchEvent
As a comment over the extensive answer by cegfault: eval, and text source code... could be <script> text source code </script>... should your script produces a (String) script. If not you ´d probably better go further backwards to where did your script creates the unserializable things that appear in your event, and think about recreating those things, not the event.
TL;DR to accomplish what you are doing, you have three options:
Store a reference to the event in a global value (which is what most tutorials - like your referenced youtube video - will recommend you do). This requires the event to run in the same context (ie web page) as when you store the reference
When you store the reference to the event in localStorage (such as by name or a key/value look up), on the page/context where you want to execute the event, make sure the appropriate functions and libraries are loaded before executing the event
[strongly NOT recommended] Store the javascript source code in your storate and eval() it later [again, please don't do this]
As mentioned by #Josh and #SaurabhRajpal, what you are asking for, strictly speaking, is not possible in JavaScript. What you are doing with JSON.stringify(e) will probably return undefined or null, as the MDN documentation for JSON.stringify says:
If undefined, a Function, or a Symbol is encountered during conversion it is either omitted (when it is found in an object) or censored to null (when it is found in an array). JSON.stringify can also just return undefined when passing in "pure" values like JSON.stringify(function(){}) or JSON.stringify(undefined).
In short, there is no way to store a single function into localStorage (or any other offline storage). To explain why this is not possible, see this example:
function foo() {
console.log("a")
}
function bar() {
return foo()
}
How can you store bar() for later usage? In order to store bar, you would also have to store foo(). This becomes much more complicated when you consider referencing a function which is in, or uses, a large library (like jQuery, underscore, D3, charting libraries, etc). Keep in mind your computer has already parsed the source code down into binary, and as such won't easily know how to read the function for every possible if, for, and switch statements to ensure all possible correlated functions and libraries are saved.
If you really wanted to do this, you would have to write your own javascript parser, and you really don't want to do that!
So what are your options? First, do everything on the same page, and store the reference to the event in a global value (the youtube video you link to in a comment is using this method).
Your second option is to use a reference to the event (not the event itself), and make sure the source code for that reference is use later. For (html) example:
// on page #1
<script src="path/to/my/js/library.js"></script>
...
<script>
window.addEventListener('beforeinstallprompt', (e) => {
e.preventDefault()
localStorage.setItem('stashed-event', 'before-install')
})
</script>
// later, on page #2:
<script src="path/to/my/js/library.js"></script>
...
<script>
var evt = localStorage.setItem('stashed-event', 'before-install')
if(evt == 'before-install') {
dosomething() // which would be a function in path/to/my/js/library.js
}
// another option here would be to define window listeners for all possible events
// in your library.js file, and then simply build and trigger the event here. for
// more about this, see: this link:
// https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Creating_and_triggering_events
</script>
Finally, you can store javascript source code and then eval() it later. Please, please, please do NOT do this. It's bad practice and can lead to very evil things. But, if you insist:
// on page #1
window.addEventListener('beforeinstallprompt', (e) => {
e.preventDefault()
SomeAjaxFunction("path/to/my/js/library.js", function(responseText) {
localStorage.setItem('stashed-event', {
name: 'before-install',
call: 'beforeInstallFunction()',
src: responseText
})
})
})
// later, on page #2:
var evt = localStorage.setItem('stashed-event', 'before-install')
if(evt) {
console.log("triggering event " + evt.name)
eval(evt.src)
eval(evt.call)
}
Like I said, this is a really bad idea, but it's an option.
IMHO, I think you're trying to avoid including a library or source code in a later page/app/whatever, and javascript just does not work this way. It's best to pass around references in-memory, and only use key/value storage for names. Everything else is a type of coding gymnastics to avoid simply including your source code in the places it needs ot be included.
You can create a global constant and update it when ever event changes rather than serializing it and de-serializing which is a costly processes. SO this is how you can do it - You can create a window instance and clone the event in the window object so that it wont mutate.(Note this wont won't work across tabs)
window.addEventListener('click', (e) => {
e.preventDefault();
window.deferredPrompt = Object.assign(e);//Don't mutate
});
let someOtherMethod = ()=>{
console.log(window.deferredPrompt)
}
window.setInterval(someOtherMethod, 5000);
Try clicking after 5 seconds in the last window and check after 5 seconds
Here is a simple but successful solution.
The idea is to capture the event in a variable and only fire it when signaled by another window of the same origin (domain etc).
The solution uses localStorage methods as the signaling semaphore.
Here is the code I used. I have tested it successfully in Chrome, both mobile & desktop.
//In event handling window
//Register the ServiceWorker
if('serviceWorker' in navigator) {
navigator.serviceWorker.register('sw.js');
};
//Capture beforeInstall event
window.addEventListener('beforeinstallprompt', function(event){
event.preventDefault();
window.deferredPrompt = event;
return false;
})
//Wait for signal
window.onstorage = event => {
if (event.key === 'installprompt') {
//Fire the event when signaled.
window.deferredPrompt.prompt();
// Discard event
window.deferredPrompt = null;
//Discard storage item
localStorage.removeItem('installprompt');
}
}
//In a different window or tab from the same origin fire the event when ready.
localStorage.setItem('installprompt', 'whatever');
Please see if this helps.
Defining an event listener for 'beforeinstallprompt' event
window.addEventListener('beforeinstallprompt', (e) => {
e.preventDefault();
//do all the stufff
console.log('Event triggered');
});
When you want to dispatch the event manually.
Create a new event variable.
var myevent = new Event('beforeinstallprompt');
window.dispatchEvent(myevent);
Outputs 'Event triggered' in the console.

OnContextCreated() in Cef not being called

I have a similar problem to the person in this post; I'm trying to extend the cefsimple.exe app included with the chromium embedded framework binaries to include a V8 handler. I implemented the OnContextCreated() method and made sure to extend RenderProcessHandler in the SimpleHandler class. I'm trying to implement a simple window bound variable called test_string; here's what my code looks like;
void SimpleHandler::OnContextCreated(
CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefV8Context> context)
{
CefRefPtr<CefV8Value> object = context->GetGlobal();
object->SetValue("test_string", CefV8Value::CreateString("this is a test"), V8_PROPERTY_ATTRIBUTE_NONE);
}
But the program never arrives at any breakpoints I add within the method, and the variable is undefined on any webpages I load within the app. I saw that one of the solutions in the other thread is to enable the settings.single_process flag, which i've done, but my code still doesn't reach the breakpoint.
To be clear, I'm accessing the variable on pages with window.test_string.
Make sure that you are sending that CefApp to CefExecuteProcess.
CefRefPtr<SimpleApp> app(new SimpleApp);
// CEF applications have multiple sub-processes (render, plugin, GPU, etc)
// that share the same executable. This function checks the command-line and,
// if this is a sub-process, executes the appropriate logic.
int exit_code = CefExecuteProcess(main_args, app, sandbox_info);
if (exit_code >= 0) {
// The sub-process has completed so return here.
return exit_code;
}
Found this solution here
Have you read through the General Usage guide? Some key points below
https://bitbucket.org/chromiumembedded/cef/wiki/GeneralUsage#markdown-header-cefapp
https://bitbucket.org/chromiumembedded/cef/wiki/GeneralUsage#markdown-header-processes
The single_process mode is not supported so I've never used it. In general I'd avoid it. The multi process architecture means you need to attach the debugger to the process. The Chromium guide is relevant to CEF in this instance.
https://www.chromium.org/developers/how-tos/debugging-on-windows#TOC-Attaching-to-the-renderer
you need to ensure your App is derived from CefRenderProcessHandler
not SimpleHandler!!!
class SimpleApp : public CefApp
, public CefRenderProcessHandler
{
virtual void OnContextCreated(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefV8Context> context) OVERRIDE;
valdemar-rudolfovich says you need to pass instance of SimpleApp in
CefExecuteProcess

How to discover and load js files at runtime?

I'm writing a node.js based server that manages a range of devices. The node.js based server tells connected clients about its abilities. The abilities are defined by separate js files that define objects that are using inheritance via util.inherits().
The problem I have is that right now, I have to define a new js for a new ability and then update the main js program to require the new js, change the code to publish that the ability is available, and then utilise the new ability if requested to by the client.
I would like to make the main code more generic whereby it can
detect the new abilities,
automatically include them,
notify the clients, and
utilise the code.
The detection I can do via the various npm modules out there that support tree browsing, I can just nominate a subdirectory for all capabilities and discover what files are there. I presume that I can use require for step 2 (though not 100% certain), however I don't know how to do step 3 and 4 or use the results from step 2 with step 3 and 4.
I would value any feedback on how to solve this problem.
To clarify my problem. Right now my logic is as per the following:
var logicA = requires('./capabilities/a.js');
var logicB = requires('./capabilities/b.js');
var logicC = requires('./capabilities/c.js');
var Comms.CAPABILITY_A = 'a';
var Comms.CAPABILITY_B = 'b';
var Comms.CAPABILITY_C = 'c';
var Comms.MSG_CAPABILITY = 0;
var Comms.MSG_DO_LOGIC = 1;
function onMessageReceived(comms, msgId, body) {
switch (msgId) {
case(MSG_DO_LOGIC):
doLogic(body);
break;
...
}
}
function doLogic(flag) {
switch(flag) {
case(Comms.CAPABILITY_A):
logicA.doLogic();
break;
case(Comms.CAPABILITY_B):
logicB.doLogic();
break;
case(Comms.CAPABILITY_C):
logicC.doLogic();
break;
}
}
At the client side I have hard coded logic that presumes what is available. I can remove this by having the server send an array of the capabilities to the client, and then the client can choose one of the elements of the array and pass it back as the request to execute the logic. This is not my problem.
My problem is understanding how to cause the host program load all the logic dynamically and then evaluate which logic to execute on the dynamically loaded logic.
I should state that when I say dynamic, I mean that the code available is determined at runtime. However the evaluation is only ever performed when the server is first started.
I solved the problem by creating a register.js where all the protocols are kept. Each time I create a new protocol, I add it to the register.
Via the register I can get an array of all registered protocols. I can pass them back to the client, the client can choose a protocol and I can request an instance of the protocol via the register class.
While there is some hardcoding, it's restricted to the register class which is in the same directory as the protocols.
So in the register I have the following functions:
getList()
getText()
validateProtocolId()
getProtocol()
I use getList() to return an array of the protocol id's that are registered. I use getText() to provide a human readable list of supported protocols. I use validateProtocolId() to validate an id returned from the client to confirm that the id represents a registered protocol and then I use getProtocol() to generate an instance of the registered protocol.
In essence the getProtocol() just does a require('./<protocol file>.js') as appropriate.
It's not as elegant as auto discovery, but it allows tighter controls on what is registered without forcing custom file, etc.

Categories