Using socket.io inside my namespace - javascript

I'm shipping socket.io as part of my code for 3rd party sites to use. I don't want it to pollute the global namespace (e.g. different versions of io will collide) but rather make it work only as part of my library, So it will be namespaced and called only from
MyLibrary.io
How do I go about this?

Since you have total control over the file, then the easiest thing to do would be to just modify the file to load socket.io at a new location.
The easiest way I would say to do that would be to wrap the contents of dist/socket.io.js with
(function() {
// All Standard SocketIO code
}).call(MyLibrary);
That will make the file load io onto MyLibrary instead. SocketIO, like many libraries, uses this to decide how to load things. In a browser circumstance, this is the object window, but by wrapping a function around everything, you can control this and change it to your own value by using the method call.
Many other libraries, though not socketIO, have a helper to avoid exactly this problem. Often they have a method called noConflict(). For example, if you wanted to avoid a problem like this for jQuery, you could do:
MyLibrary.$ = MyLibrary.jQuery = jQuery.noConflict(true);
That works because when jQuery loads, it saves a reference to the previously loaded version, so that if it needs to, it can set the global objects back to how they were.

It seems that the suggested way to do this (Issue #85) is to load it as per normal then run these two lines:
MyLibrary.io = window.io; // Assign it to your desired namespace
delete window.io; // Remove it from the window namespace
The code in socket.io.js only adds the io variable to the window.
You could modify the code in socket.io.js but that might make it hard to maintain in the future since you would have to track changes you make to that file and propagate them into newer versions of socket.io.js.
At first, I thought you could do something with module.exports since it had the CommonJS format, however, after reading the code, I realized it's just the way things were organized before building the socket.io.js file. See the file directory here for what I'm referring to.

Related

How to use javascript without appending it to DOM

I am developing an Single Page Application (SPA) from scratch. I am doing it from scratch using only HTML, CSS and vanilla JavaScript and not using any external frameworks.
My application will initially load Web page but upon navigating to some other page say page2, it will only load required data and functions about other page2 from page2.js and not reload the entire Web page.
To use the JavaScript I will append it to body. But the problem is that when I navigate same page again it will append the same JavaScript again. The more pages I visit the more scripts are attached.
I have tried removing existing script tag in favour or upcoming script and it works good, but is there a way that I don't have to append script to DOM in the first place?
So my question is, is there a way we can parse (not just plain read) or execute JavaScript file without using any physical medium (DOM)
Although I am expecting pure JavaScript, libraries would also work, just need a logical explaination
So my question is, is there a way we can parse (not just plain read) or execute JavaScript file without using any physical medium (DOM)
Yes, you can. How you do it depends on how cutting-edge the environment you're going to support is (either natively, or via tools that can emulate some things in older environments).
In a modern environment...
...you could solve this with dynamic import, which is new in ES2020 (but already supported by up-to-date browsers, and emulated by tools like Webpack and Rollup.js). With dynamic import, you'd do something like this:
async function loadPage(moduleUrl) {
const mod = await import(moduleUrl);
mod.main();
}
No matter how many times it's requested, within a realm a module is only loaded once. (Your SPA will be within a realm, so that works.) So the code above will dynamically load the module's code the first time, but just give you back a reference to the already-loaded module the second, third, etc. times. main would be a function you export from the module that tells it you've come (back) to the "page". Your modules might look like this:
// ...code here that only runs once...
// ...perhaps it loads the markup via ajax...
export function main() {
// ...this function gets called very time the user go (back) to our "page"
}
Live example on CodeSandbox.
In older environments...
...two answers for you:
You could use eval...
You can read your code from your server as text using ajax, then evaluate it with eval. You will hear that "eval is evil" and that's not a bad high-level understanding for it. :-) The arguments against it are:
It requires parsing code; some people claim firing up a code parser is "slow" (for some definition of "slow).
It parses and evaluates arbitrary code from strings.
You can see why #2 in particular could be problematic: You have to trust the string you're evaluating. So never use eval on user-supplied content, for instance, in another user's session (User A could be trying to do something malicious with code you run in User B's session).
But in your case, you want and need both of those things, and you trust the source of the string (your server), so it's fine.
But you probably don't need to
I don't think you need that, though, even in older environments. Your code already knows what JavaScript file it needs to load for "page" X, right? So just see whether that code has already been loaded and don't load it again if it is. For instance:
function loadPage(scriptUrl, markupUrl) {
// ...
if (!document.querySelector(`script[src="${scriptUrl}"]`)) {
// ...not found, add a `script` tag for it...
} else {
// ...perhaps call a well-known function to run code that should run
// when you return to the "page"
}
// ...
}
Or if you don't want to use the DOM for it, have an object or Map or Set that you use to keep track of what you've already loaded.
Go back to old-school -- web 1.0, DOM level 1.0, has your back. Something like this would do the trick:
<html><head>
<script>
if (!document.getElementById('myScriptId')) {
document.write('<script id="myScriptId" src="/path/to/myscript"></scri' + 'pt>');
}
</script>
This technique gets everybody upset, but it works great to avoid the problems associated with doing dynamic loading via DOM script tag injection. The key is that this causes the document parser to block until the script has loaded, so you don't need to worry about onload/onready events, etc, etc.
One caveat, pull this trick near the start of your document, because you're going to cause the engine to do a partial DOM reparse and mess up speculative loading.

requirejs returns a module with attach and notNeeded methods

The main question here is, in what cases does calling require(arrayOfPaths, onSuccess, onError) cause the onSuccess callback be called with an object, that instead of the module, return an object that has two methods: attach and notNeeded?
The secondary question is, what do these attach() and notNeeded() methods do?
I cannot find any documentation on these.
Reproduction of this scenario is pretty straightforward. I have an AMD module (TypeScript'ed) on some web server that I load using the require method. This call works the first time and works for multiple require calls for multiple, other AMD modules. However, if I refresh the page, which, with my logic causes the require calls to be made again, it no longer returns my AMD module but instead the object that I described above.
My assumption is that the object returned is a cached version of the object that maybe if I call attach() it will give me that cached object back. I haven't tested that though.
Here are a couple of things that throws the "caching" idea out of the window:
1. This is a browser Refresh (not Ctrl+R style) but still a refresh which means the page is loaded and any script tags added by requirejs should be gone
2. The path that I'm supplying to the require call has a ?ts= where = new Date().getTime()
So, the path and call on the first call may be:
require(["http://someurl/provider.js?ts=123"], onSuccess, onError);
and on the second call, it may be:
require(["http://someurl/provider.js?ts=124"], onSuccess, onError);
Ok, I created a a small web project that attempted to do the same thing I was doing and was not able to reproduce it. I don't think it's worthwhile for me to post the code that does not reproduce it, but I can if needed be.
But I did manage to make the behavior go away. First, a little background on my web project: my web project is an ASP.NET MVC 5 web app that uses the Angle theme from WrapBootstrap. If you're not familiar with this theme, it's basically bringing a bunch of third party libraries together such as angular, bootstrap, fontawesome, whirl, just to name a few.
Now, secondly, I searched my entire source code root folder and sub folders for the word 'notNeeded' and found that fastclick references it, but not necessarily create any objects with a notNeeded property. fastclick is one of the libraries that the Angle theme pulls in by default. fastclick was the only one that had any references to notNeeded; and thus excluding requirejs.
Finally, I tried removing fastclick from the list of modules that the ui.router tries to resolve and by doing that, it removed this behavior. I don't want to say that this is the answer since I would like to know exactly what prompted this behavior/artifact. Did the browser (IE) do this? As I play around with this some more, maybe I can get something verifiable out there.

Make an external Script available to all members of qooxdoo Object

So I have been making a web app in the qooxdoo framework that utilised the d3 library. At the moment, every function which needs to use the d3 library works like this:
myFunction : function() {
var req = new qx.bom.request.Script();
req.onload = this.myActualFunction(); //calls function when script loads
req.open("GET","http://d3js.org/d3.v3.js" );
req.send();
}
It seems verbose to have to call the script loader for lots of different functions*. We could, and eventually probably will, switch to using d3 from a local directory. Nevertheless, it seems like there are lots of times when you would like to use a script loader to make a script available to, say, every member function of an object. Is there someway that I can achieve that? If I passed the script loader around like a variable, does that mean that every function which has it in scope gets access to the library?
The Manual did not appear very helpful on this topic.
*I presume that qooxdoo arranges to cache the script - it seems to be pretty good at those type of optimisations, though I have no specific knowledge of how the script loaders are treated in the compiled version.
The best approach of handling an external library is to create a Qooxdoo wrapper which will be a first-class citizen in the dependency management process. Qooxdoo internally makes use of this approach, e.g. Sizzle, Mustache, etc. Luckily for you there's already a contribution for D3. Though you can make your own wrapper without a hassle.
You can also, use add-script to "link" the library to the document but I see this way mostly disadvantageous.

when to define a module and when to only require files using requireJS

I'm struggling to get requireJS to work properly. Page is running fine, but I think I'm doing things in an oh-so wrong way.
For example, on page xzy I'm adding the following JavaScript at the end of the page (the JS must stay on the page for now, so no external js-files possible)
<script type="text/javascript" language="javascript">
//<![CDATA[
(function () {
require([
'async!http://maps.google.com/maps/api/js?v=3&sensor=false',
'maps/jquery.ui.map.full.min.js',
'maps/jquery.ui.map.extensions.min'
], function() {
// ... do stuff with Google Maps
}
);
}());
//]]>
</script>
Doing this makes google.map and the $.().gmap method globally available, which probably shouldn't be available globally.
Questions:
Should I convert this into a requireJS module? Why?
If so, will the module be available on other pages as well or do I just "re-define" on page 123 and the dependency files will already have been cached?
And finally - will I have to convert the code inside my require call into module.methods, which I then call via module_name.method_name(pass_some_parameters)?
Just looking at the JS:
http://maps.google.com/maps/api/js?v=3&sensor=false
You can see that window.google is a global. There's not much you can do about that without Google releasing an AMD version.
Your decision regarding should you create a module should firstly be a question of readability/maintainability of the JS code. Modules are (should be), readable, reusable chunks of code/reusable abstractions that the rest of your code can consume. You should also derive testing benefits from this - each module should be easier to test in isolation.
You can end up with many more JS files if you choose a modular approach, and you might think this leads to performance issues - i.e. multiple HTTP requests. But this is mitigated by using the RequireJS Optimiser to optimise your modules to a single file.
If you convert to a module, yes you can require it from other pages, and if your HTTP caching headers are set up, then the browser may choose to use a cached version, thus saving you a HTTP request (same caching heuristics apply if you've optimised every module into a single file).
If you re-define (I assume you mean copy and paste the code block), then those dependencies listed in the call to require should all be cached by the browser, and therefore instantly available (depending on your web server and its HTTP caching headers).
Finally, yes you may have to refactor the code a bit to expose the new module's API. If that means exposing a single object with methods, then that's what you should do. This process almost inevitably leads to better code though in my experience. As you've had to think more about what the module's purpose is, and this often leads to your breaking the coupling between pieces of code.

Avoid duplicate Javascript inside iframes

Here is a wireframe to ilustrate the structure of a legacy project that shows some performance issue:
For all dialogs (From jQuery UI) open a new iframe are created and all js from Home are re-downloaded and all objects are re-instanced. Can I create a reference from jQuery from Home to all new iframes and work in each iframe isolated scope?
For example:
[Home scope]
$("#some-el").data('foo', 'bar');
console.log($("#some-el").data('foo')); // results bar
[App1 scope]
//after defined in Home first run
console.log($("#some-el").data('foo')); // results undefined
PS: Remember this is a legacy architeture and all solutions must be consider this scenario.
I've encountered this situation before. One approach is to define some javascript that gets loaded into the iframes that just reroutes any function calls to top.functionCall() instead of containing their actual definition. It becomes very simple if all of your functions are under one namespace, like so:
Parent window js:
var namespace = (function () {
// all of your functions are in here as properties of namespace
})();
iframe window js:
var namespace = top.namespace;
One issue with this is any context sensitive functions (functions that rely or operate on the window object) will most likely break.
Actually, if all of these are hosted in the same place, the browser will NOT be downloading the files multiple times. Rather, it will be caching the first result, and then pulling from cache in the second. Iframes are treated as a separate context, so you won't need to worry about variable or form conflicts.
Assuming that downloading the same file twice is your primary concern, then you should be ok there.
An alternative design would be to use AJAX instead of iframed content - but having been where you are in working with legacy apps, I realize how hard that can be to do without real JSON / REST calls available. One thing I've done is changed the views inside the iframes to be "partials," returning only the necessary HTML contents without the HTML head etc., and loading them using $.load(). This gets complex as you will need to execute bindings post-load and carefully track form ID's etc., but it can be done.

Categories