I'm trying to delay the construction of every 'page' (i.e. a Wt::WWidget inside my global Wt::WStackedWidget), until it is needed. Therefore I'm using a method similar to the DeferredWidget of the Widget Gallery example of Wt.
However, when I load a library using require, the execution of javascript code is not delayed until the library is loaded, when the content is not loaded with the first request (f.ex. inside WWidget::load()), i.e. running the following code
wApp->require("myLibrary.js"); // defines function MyFunction ();
doJavaScript ("MyFunction ();");
runs without error when it is requested on the first loaded page, but when the content is loaded after a user event, the following javascript error occurs:
MyFunction is not defined
Question: How should I overcome this error or how should I correctly delay the loading of my (large) javascript library until needed?
Further research
Inspecting the source code of WebRenderer::collectJS:
Javascript updates seems to be performed before loading new libraries:
// Executing javascript updates, including doJavaScript calls.
for (unsigned i = 0; i < changes.size(); ++i) {
changes[i]->asJavaScript(sout, DomElement::Priority::Update);
delete changes[i];
}
...
// Loading new libraries.
int librariesLoaded = loadScriptLibraries(*js, app);
Shouldn't the javascript update being delayed until the new libraries are loaded?
Further research - Part 2
Executing javascript code (which may depend on required libraries) is delayed at two different places, i.e. inside
WebRenderer::collectJavaScript: delays execution of all javascript code (including invisible changes) until all old required libraries (excluding newly required libraries f.ex. inside WWidget::load) are loaded.
WebRenderer::collectJS: delays execution of some javascript code until all required libraries (including newly required libraries f.ex. inside WWidget::load) are loaded.
I am not sure with the javascript scriploader behavior. But in my wt experience i make it append in this way.
1) My javascript library is load in my main page at start with require.
2) If i need later some new function, i write it in my script string like this :
string javacode = "function MyTest ( ) { "
"alert('test') ; }"
"MyTest();"
doJavaScript ( javacode ) ;
If you want load some javascript file and run some function after it is load you schould make the require in the contructor of your container class.
Then you derived the function bool Wt::WCompositeWidget::loaded()
and put in this function your dojavaScript...
Related
I am trying to write a unit test where I want to verify that a ajax call has been made.
The code is simple :
it('test spycall',()=>{
spyOn($,"ajax");
//my method call which in turns use ajax
MyFunc();
expect($.ajax.calls.mostRecent().args[0]["url"].toEqual("myurl");
});
The error that I get :
Property 'calls' doesn't exist on type '{settings:jqueryAjaxSettings):jQueryXHR;(url:string, settings?:JQueryAjaxSettings}
$.ajax.calls, among others, is part of the Jasmine testing framework, not JQuery itself (As you know, Jasmine (or rather, Jasmine-Jquery, the plugin you're using) is adding certain debugging functions to JQuery's prototype in order to, well, be able to test ajax calls).
The bad part is that your .d.ts typescript definition file, the file that acts as an interface between typescript and pure JS libraries isn't aware of Jasmine's functions.
There are several ways you could approach fixing this, like
looking if someone has adjusted the JQuery .d.ts file for Jasmine's functions or
creating the new .d.ts file yourself by modifying the original one or, (what I would be doing)
overwriting the typescript definition by declaring $.ajax as any, or not including the typescript definition at all in your testing codebase and declaring $ as any.
There are 2 ways to get rid of the error:
// 1
expect(($.ajax as any).calls.mostRecent().args[0].url).toEqual("myurl");
// 2
let ajaxSpy = spyOn($,"ajax");
expect(ajaxSpy.calls.mostRecent().args[0].url).toEqual("myurl");
You can also use partial matching:
expect(($.ajax as any).calls.mostRecent().args).toEqual([
jasmine.objectContaining({url: "myurl"})
]);
I'm trying to get QUnit to work with my production environment, and noticed that QUnit exports a log function with this definition:
function ( callback ) {
config[key].push( callback );
}
Why does it do this? There seem to be a hole bunch of functions that are exported globally, like begin, deepEqual, done, etc etc. Isn't it better if all these functions are within a namespace instead?
Shouldn't this behaviour be noted in the documentation? I had another log function defined in my codebase (shorthand for console.log), and this rendered strange bugs from QUnits side because log("a log message") is not correct usage of QUnit's log function.
Is there a way to contain QUnit within its namespace? Giving the code an object instead of window does not work.
Using JQuery.load(), I change the content of my website's mainWindow to allow the user to switch between tabs. For each tab, there is one or multiple scipts that contain functions that are executed once the tab content is loaded.
Obviously when switching to the tab for the first time, the script has to be fetched from the server and interpreted, but this shouldn't happen if the user switches back to the tab later on. So, to put it short:
Load() html
make sure javascript functions exist, otherwise load script and interpret it.
call a a function on the javascript after the DOM is rebuilt.
Step one and two have to be complete before step 3 is performed.
At the moment, I am using nested callbacks to realize this:
function openFirstTab(){
$("#mainWindow").load("firstTab.php", function(){
if(typeof(onloadfFirstTab) != "function"){
jQuery.getScript("assets/js/FirstTab.js", function(){
onloadFirstTab();
});
}
else{
onloadFirstTab();
}
} );
}
but I feel that there should be a better way.
You can't write the code entirely synchronously since you can't load script synchronously after page load ( unless you do a synchronous XHR request and eval the results - not recommended ).
You've got a couple of choices. There are pre-existing dependency management libs like RequireJS which may fit the bill here, or if you just need to load a single file you can do something like this to clean up your code a bit rather than using if/else:
function loadDependencies() {
// For the sake of example, the script adds "superplugin" to the jQuery prototype
return $.getScript( "http://mysite.com/jquery.superplugin.js" );
}
function action() {
// If superplugin hasn't been loaded yet, then load it
$.when( jQuery.fn.superplugin || loadDependencies() ).done(function() {
// Your dependencies are loaded now
});
}
This makes use of jQuery Deferreds/Promises to make the code much nicer.
If you don't want to load the JS more than once and you are going to dynamically load it, then the only way to know whether it's already loaded is to test for some condition that indicates it has already been loaded. The choices I'm aware of are:
The simplest I know of is what you are already doing (check for the existence of a function that is defined in the javascript).
You could also use a property on each tab (using jQuery's .data() that you set to true after you load the script.
You could write the dynamically loaded code so that it knows how to avoid re-initializing itself if it has already been loaded. In that case, you just load it each time, but the successive times don't do anything. Hint, you can't have any statically defined globals and you have to test if it's already been loaded before it runs its own initialization code.
(Haven't tested it yet, so I am not sure if it works, especially since I didn't yet really understand scope in javascript:)
function require(scripts, callback){
var loadCount = 0;
function done(){
loadCount -=1;
if (loadCount==0){
callback();
}
}
for ( var script in scripts){
if (!script.exitsts()){
loadCount +=1;
jQuery.getScript(script.url, done);
}
}
}
This function takes an array of scripts that are required and makes sure all of them are interpreted before it calls the callback().
The "script" class:
function script(url, testFunc){
this.url =url;
this.testFunction = testFunc;
this.exists = function(){
if(typeof(testFunction)=="function"){
return true;
}
else{
return false;
}
}
}
Where the test-function is a function that is defined (only) in the concerned script.
PS:
To enable caching in JQuery and thus prevent the browser from doing a GET request every time getScript() is called, you can use one of the methods that are presented here.
Even though unnecessary GET - requests are avoided, the script is still getting interpreted every time getScript() is called. This might sometimes be the desired behavior. But in many cases, there is no need to re-interpret library functions. In these cases it makes sense to avoid calling getScript() if the required library functions are already available. (As it is done in this example with script.exists().
BACKGROUND:
I am using JRuby in an eclipse plugin for my product. I have a bunch of scripts that define a DSL and perform operations for me. I want to be able to dynamically reload these scripts whenever required. The scripts could change themselves on file system and moreover the location of the scripts could also change. I could even have multiple copies on file system of slightly modified/changed scripts. Each time I want scripts from a specific location to be utilized.
As I have understood so far, using "load" instead of "require" should do the job. So now if before calling any Ruby methods/functions I use "load 'XXX.rb'", it will reload the XXX.rb utilizing the new changes.
PROBLEM:
In my code I am using ScriptingContainer to run scriplets to access ruby functions. I set load paths on this scripting container to indicate from which locations the scripts should be loaded. However, the problem is that on subsequent calls and even with different instances of ScriptingContainer, I have noticed that the scripts that were loaded the first time are utilized every time. "load" reloads them, but after loading those scripts once, the next time I might need to load similar scripts from a different location but its not happening.
My assumption was that utilizing a different scripting container instance should have done the job but it seems that the load paths are globally set somewhere and calling "setLoadPath" on new ScriptingContainer instances either does not modify existing paths or only appends. If the latter case is true then probably when searching for scripts they are always found on oldest paths set and newer load paths get ignored.
Any ideas???
The solution is to specify scope for a ScriptingContainer instance when creating it. One of the ScriptingContainer constructors takes in a parameter of type LocalContextScope, use one of the constants to define the scope. See LocalContextScope.java
To test this defect and solution I have written a small snippet. You may try it out:
public class LoadPathProblem {
public static void main(String[] args) {
// Create the first container
ScriptingContainer c1 = new ScriptingContainer();
// FIX ScriptingContainer c1 = new ScriptingContainer(LocalContextScope.SINGLETHREAD);
// Setting a load path for scripts
String path1[] = new String[] { ".\\scripts\\one" };
c1.getProvider().setLoadPaths(Arrays.asList(path1));
// Run a script that requires loading scripts in the load path above
EvalUnit unit1 = c1.parse("load 'test.rb'\n" + "testCall");
IRubyObject ret1 = unit1.run();
System.out.println(JavaEmbedUtils.rubyToJava(ret1));
// Create the second container, completely independent of the first one
ScriptingContainer c2 = new ScriptingContainer();
// FIX ScriptingContainer c2 = new ScriptingContainer(LocalContextScope.SINGLETHREAD);
// Setting a different load path for this container as compared to the first container
String path2[] = new String[] { ".\\Scripts\\two" };
c2.getProvider().setLoadPaths(Arrays.asList(path2));
// Run a script that requires loading scripts in the different path
EvalUnit unit2 = c2.parse("load 'test.rb'\n" + "testCall");
IRubyObject ret2 = unit2.run();
/*
* PROBLEM: Expected here that the function testCall will be called from
* the .\scripts\two\test.rb, but as you can see the output says that
* the function was still called from .\scripts\one\test.rb.
*/
System.out.println(JavaEmbedUtils.rubyToJava(ret2));
}
}
Test scripts to try out the above code can be in different folders but with the same filename ("test.rb" for the above example:
./scripts/one/test.rb
def testCall
"Called one"
end
./scripts/two/test.rb
def testCall
"Called two"
end
When leaving Firefox running for some time the strange thing begin to happen with my extension. Here's some code, that I need to describe the problem:
extension.js
var My = {};
overlay.js
Components.utils.import("resource://myextension/extension.js");
My.extension = (function() {
var someFunc = function() {
// more code
My.module.otherFunc();
};
// more code
})();
At some point we start getting the strange error: 'My' is undefined in overlay.js:6
My guess is that Firefox unloads extension.js module silently, otherwise I couldn't find any hint why this may happen. Do you?
Firefox version: 3.x
Thanks!
While you can pass functions to modules as temporary callbacks, you should take steps to ensure that they are not used after the window is closed, because then all its global variables, including My, are deleted. If the module subsequently tries to call the function then you will get the error as described.