Foreword
This is the third and final part of the article, and for better understanding of this part, I recommend reading the first and second parts first.
Part III
From Previous Parts
The Challenge
To create an engine that will implement JavaScript’s missing functionality for including JavaScript files in other JavaScript files.
The Solutions
A solution that works only if the external files contain functions, or declarations that don't depend on other files, and another solution that works in every situation but has one small disadvantage: having to create header files for the source files.
The second isn’t far from meeting the challenge, but not there yet. Can we push further?
Solution 3
The Logic
One of the problems identified back then was that when the execution of a script starts, it goes all the way and from that we have the restriction: all files should contain only function declarations and object declarations (but only if they're not related to objects from other files).
Ideally, only the part with the $include
function of the file should be executed – and we will name this part header, and then, when the time is right, the rest – body. But as I stated above, it cannot be done.
Let’s think again; how do we stop the execution of a script? For a function, you use return
, but for the entire script?
Error is the answer!!! When an error occurs, the script’s execution is halted! So we throw an error on purpose! The next question will be: will the browser display the error? It will, unless we override window.onerror
so that nothing will be shown to the user.
Our code should look like this:
$include = function ()
{
throw new Error("Special Error");
}
Window.onerror = function ()
{
}
But two problems arise:
- Can the execution of the script be resumed when needed? No! (But we can execute it all again.)
- Also, what happens when a legitimate error occurs? Can it be shown? (Yes, because we can differentiate between an error from the parser and a personalized error thrown by us.)
However, there is a more elegant approach: using an iFrame.
But how? A solution would be a text file. Next, we read the header (that now does not necessarily have to be a function) and then use eval
to execute the body. As stated in the first part, eval
handles resources badly and this makes it totally inappropriate for executing large portions of code as in our case.
The other problem is that IE doesn't load the JavaScript file like text, but asks you to download it.
So we will have an HTML file that will load the script normally and will execute only the $include
function that will pass to the parent document the filenames that are dependencies for the file currently loaded in the iFrame, and after that, we will throw the error.
Also, when a file is loaded, it is also cached, and when needed for the main document, it isn’t loaded from the server again.
But what happens if the script has no dependencies, therefore no $include
function?
Can we force the user to use $include()
with no parameters? Yes we can, but also we can wait till the script.onload
event is fired and then tell the parent document that this file has no dependencies.
Of course, there is another problem: the script will be executed twice: first in the iFrame and then in the main document; and that’s the least of our problems; the code can contain alert, confirm, open etc., that will screw up our work badly by executing in the iFrame.
And yet another artifice: overriding window.alert
, window.open
, window.confirm
, etc., and not just setting them to do nothing but to stop the execution of the script also.
And that seems to be it!
Let’s recap a bit:
The main page will contain a tree of files that will be created by recursively passing a filename (filepath
) to the iFrame’s document that will return the dependencies for that file. Here we’ll need an algorithm to check for circular dependencies. And of course, the $include
function will do nothing in the main page.
After that, all the files can be added to the main document’s head in the correct order.
Of course, we will keep from the first solution the code that lets us specify path relative to the IncludingEngine.js file and not to the current HTML document, and from the second solution, the way the application starts with the Main
function.
The Implementation
Unlike the implementations from the previous two parts, this one is much larger, therefore not suited for pasting here. But the most important thing was already presented above: the general idea together with the little ideas on how to overcome the different barriers. ,However you can download this implementation and then examine it. (Don’t forget, there are two files: IncludingEngine.js and LoadFile.htm that together do the job).
The diagram below expresses the power of the product that resulted from this solution:
Things that this diagram doesn't show about the resultant product:
- it works online and offline.
- doesn't use
eval
. - detects and shows the files that form circular dependencies.
Now we can say that the challenges established in the first part were finally met.
Notes:
The latest version of this implementation can be found at IncludingEngine.jsFramework.com.
Here are all the parts of the "JavaScript Including Engine" article series: Part I, Part II, Part III. Also, you may want to visit the IncludingEngine website for more info.