-3

I need to execute a function after a certain script was loaded so I load the script using an ajax call and on the success I invoke the function that uses an object that was suppose to be loaded already from that script and I get an error that this object is undefined. Here is my code:

function LoadInlineManualScript() {
  return $.ajax({
            url: "../Scripts/inline-manual-player.js",
            dataType: "script",
            beforeSend: function () {
                loader.hide();
                loader.unbind();
            },
            success: SetInlineManualCallbacks
        });
    }


function SetInlineManualCallbacks() {
            debugger;
            //Here I get the error!
            window.inline_manual_player.setCallbacks({
                onTopicDone: function (player, topic_id, step_id) {
                    console.log('Step was displayed.');
                }
            });
        }

And I get Uncaught TypeError: Cannot read property 'setCallbacks' of undefined.

I tried to change the call to use async:false but it didn't help.

Offir
  • 3,252
  • 3
  • 41
  • 73
  • What is `window.inline_manual_player.setCallbacks`? Because that seems to be the problem. – Bwaxxlo Jul 28 '16 at 16:33
  • There is no code in your script that actually assigns a value to `window.inline_manual_player`. You ajax code is not doing that anyway. – trincot Jul 31 '16 at 08:40
  • @trincot window.inline_manual_player is loaded from the script file. – Offir Jul 31 '16 at 08:46
  • Change `success: SetInlineManualCallbacks(inlineUid)` to `success: SetInlineManualCallbacks.bind(null, inlineUid)` – trincot Jul 31 '16 at 09:10
  • Please show the code in `../Scripts/inline-manual-player.js` which defines `window.inline_manual_player`. If it fails you will not have that object of course. What messages do you get in `console`? – trincot Jul 31 '16 at 09:21
  • It's not my js , it's minified. – Offir Jul 31 '16 at 09:23
  • @trincot, no I have other problem, I tried Leo solution which load the script, but it wasn't excuted. I tried this: http://stackoverflow.com/questions/16230886/trying-to-fire-onload-event-on-script-tag with no success. – Offir Jul 31 '16 at 09:25
  • Don't do that. jQuery does all that for you, so stick with jQuery. What about your script file, can you share its code, even if minified, or give a link to it? Did you try the `bind` solution above? – trincot Jul 31 '16 at 09:32
  • @trincot , I stuck on this thing for so long... I tried getScript , I tried loading the script like in my question, nothing worked, only the way he showed me work. – Offir Jul 31 '16 at 09:36
  • I copied your code with my own referred javascript (which just creates the necessary variables you refer to), and with the change to `bind` it works, so you must have a problem that is not included in your question. Please provide the information of the script. – trincot Jul 31 '16 at 09:37
  • Also, which version of jQuery are you using? – trincot Jul 31 '16 at 09:38
  • @trincot jquery-2.0.0 – Offir Jul 31 '16 at 09:39
  • What do you mean with the change to "bind"? – Offir Jul 31 '16 at 09:39
  • See my comment above about `bind`... 30 minutes ago. Did you read it? – trincot Jul 31 '16 at 09:41
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/118733/discussion-between-offir-peer-and-trincot). – Offir Jul 31 '16 at 09:44
  • Does the `inline-manual-player.js` script really run before the `success` executes ? ... If not, there is no `window.inline_manual_player` – Asons Jul 31 '16 at 10:06
  • The JS does not set that `inline_manual_player` variable. It tests for its presence, but it does not set it itself. Do you have documentation somewhere that states it SHOULD set that variable? Or any documentation that explains what will create that variable? – trincot Jul 31 '16 at 10:33
  • It's weird, as I said , the way Leo suggested below does work. – Offir Jul 31 '16 at 11:17
  • It seems that because the script I am trying to load is with "use strict" I can't load it via Jquery. Take a look here: http://stackoverflow.com/questions/12776405/use-strict-jquery-getscript-script-cant-export-to-global-namespace?noredirect=1&lq=1 – Offir Jul 31 '16 at 11:21
  • So, your question is solved? – trincot Jul 31 '16 at 11:26
  • I sent to the developer of inline-manual this link, he is on it. I will let you know soon. – Offir Jul 31 '16 at 11:29
  • @trincot , Marek is the developer of this tool. Thank you for your help, take a look at my final answer. – Offir Jul 31 '16 at 12:02

9 Answers9

1

Two things:

  1. Function SetInlineManualCallbacks doesn't have a return statement. So despite issue 2, SetInlineManualCallbacks(inlineUid) should return undefined. However, success needs a function:

    success: SetInlineManualCallbacks.bind(null, inlineUid)
    

    Or old-school without bind:

    success: function() {
        SetInlineManualCallbacks(inlineUid)
    }
    

    But, looks like SetInlineManualCallbacks doesn't need any arguments, so this would be enough in your case:

    success: SetInlineManualCallbacks
    
  2. It says window.inline_manual_player is undefined. Judging from the variable name, I think it's because you just loaded the JS file content, as plain text, but you didn't execute the content as JavaScript.

    You could use eval to execute it in success callback, but that's really a bad idea. I would probably insert a <script> tag into DOM, instead of using AJAX.

    function LoadInlineManualScript() {
        var s = document.createElement('script')
        s.src = "../Scripts/inline-manual-player.js"
        // your callback will fire when the script is loaded and executed
        s.onload = SetInlineManualCallbacks
        // it will start to load right after appended, and execute of course
        document.body.appendChild(s)
    }
    
Leo
  • 13,428
  • 5
  • 43
  • 61
1

Why you are loading by ajax? Let the browser do it.

The function loadScript insert the script on DOM and attach a callback to the event onload of script element.

function loadScript(href_script, callback) {
    var script = document.createElement('script');
    script.src = href_script;
    script.onload = callback;

    // The same code used by Google Analytics
    var x = document.getElementsByTagName('script')[0];
    x.parentNode.insertBefore(script, x);
};

function SetInlineManualCallbacks() {
  debugger;
  //Here I get the error!
  console.log("Code Running");
}

Here an example:

loadScript("//www.google-analytics.com/analytics.js", SetInlineManualCallbacks);
nsantana
  • 1,992
  • 1
  • 16
  • 17
0

When you do this

success: SetInlineManualCallbacks(inlineUid)

You are executing the method and setting whatever it returns to the success callback. You are not saying call this when success is triggered.

It should just be

success: SetInlineManualCallbacks

But I think there is an issue with the Ajax call itself, you are loading a script and expecting it to do something, I do not think the Ajax call is what you want. If the script is just returning inlineUid why are you using a script and not a JSON or text call. If inlineUid is global, than just read it in the callback method. Without knowing what is happening in the script file, it is hard to give a full answer.

epascarello
  • 204,599
  • 20
  • 195
  • 236
0

As someone suggested, Inline Manual is initialising itself in that script. You have your Site in Test mode, therefore there is an additional ajax request within the script itself, to check if the user can access it. So the script might load, but the inline_manual_player object won't be available yet when it is loaded because there are other things happening before it creates the object.

If you want to set the callbacks before the object is available, you can use queuing actions before calling the script to load.

var inline_manual_player = inline_manual_player || []; inline_manual_player.push(['setCallbacks', my_callbacks]);

http://help.inlinemanual.com/docs/queuing-actions

Marek Sotak
  • 136
  • 2
0

This answer is a combination of some answers I got here:

The function I use to load the script instead of using Jquery's $.getScript is:

   //Must load the script after inlineManualTracking object is ready.
    function LoadInlineManualScript() {
        loader.hide();
        loader.unbind();
        var script = document.createElement('script');
        script.src = "../Scripts/inline-manual-player.js";
        // your callback will fire when the script is loaded and executed
        script.onload = WaitForInlineManualScriptToLoad;
        // it will start to load right after appended, and execute of course
        document.body.appendChild(script);
    }

The script I load has use strict and it cannot be loaded with jquery because of the following reason: "use strict"; + jQuery.getScript() = script can't export to global namespace like epascarello mentioned earlier.

Even after using Leo's answer I still had a problem with that object and that's why I use the following function after the onload event of the script.

function WaitForInlineManualScriptToLoad() {
    if (typeof window.inline_manual_player == 'undefined') {
        setTimeout(WaitForInlineManualScriptToLoad, 100);
        return;
    }
    InitInlineManualPlayerCallBacks();
}

function InitInlineManualPlayerCallBacks() {
    window.inline_manual_player.setCallbacks({
        onTopicDone: function (player, topic_id, step_id) {
            console.log('Step was displayed.');
        }
    });
}

This works! it's not the prettiest, but it works.
The developer of inline-manual tool had responded here(Marek) and mentioned the fact that my site in under "Test" mode. There was no way we could know this can affect.

Community
  • 1
  • 1
Offir
  • 3,252
  • 3
  • 41
  • 73
  • 1
    I see, inline-manual-player.js is still going to load other scripts, that makes the variable undefined even after itself loaded. Marek's approach to predefine options is a bit like Google Analytics. – Leo Aug 07 '16 at 00:25
0

According to your question you want to execute a skript, after another script was loaded correctly.

In your own answer using the jQuery "getScript"-function won´t work for you, so why not trying the old-fashioned way without using jQuery after all?

Create a new script-element, add it to your document head and set a callback-function on readystate.

function loadScript(url, callback) {
    var newScript = document.createElement( "script" )
    newScript.type = "text/javascript";
    if(newScript.readyState) {  //IE
        newScript.onreadystatechange = function() {
            if (newScript.readyState === "loaded" || newScript.readyState === "complete") {
                newScript.onreadystatechange = null;
                callback();
            }
        };
    } else {  //Others
        newScript.onload = function() {
            callback();
        };
    }
    newScript.src = url;
    document.getElementsByTagName("head")[0].appendChild( newScript );
}

Now you can call the defined method and offer a callback-function, in your case this would be:

loadScript("../Scripts/inline-manual-player.js", function() {
    debugger;
    // should work now
    window.inline_manual_player.setCallbacks({
        onTopicDone: function (player, topic_id, step_id) {
            console.log('Step was displayed.');
        }
    });
});
NeronNF
  • 98
  • 1
  • 7
0

It seems the script wasn't executed at that time. Please note the callback is fired once the script has been loaded but not necessarily executed, based on JQuery documentation.

If you want to make sure the script was executed, you can load the script synchronically on the HTML before you use it. If you do this, you won't need ajax, and you will keep your code simple. KISS principle

If you need to do it the way you have it here, then you can apply the observer pattern creating a custom event.

Here is an example.

At the end of the file "inline-manual-player.js". You can do something like this.

$.event.trigger({
type: "fileExecuted");

Then instead of the success callback, you would subscribe to the "fileExecuted" event.

$(document).on("fileExecuted", SetInlineManualCallbacks);

Hope it helps.

jlulloav
  • 152
  • 3
-1

success takes a function and not a value. Unless SetInlineManualCallbacks returns another function, use bind and partially apply arguments.

function LoadInlineManualScript() {
  return $.ajax({
            url: "../Scripts/inline-manual-player.js",
            dataType: "script",
            beforeSend: function () {
                loader.hide();
                loader.unbind();
            },
            success: SetInlineManualCallbacks.bind(null, inlineUid)
        });
    }

It would help if you explained where inlineUid comes from.

Bwaxxlo
  • 1,860
  • 13
  • 18
-1

It would appear that inline-manual-player.js loads window.inline_manual_player asynchronously. This means the thread is allowed to move on after that file's onload event fires but before window.inline_manual_player is loaded. Thus, you're finding that window.inline_manual_player is undefined for a period. This is probably due to inline-manual-player.js making ajax calls of its own (or setTimeouts, etc. - JavaScript is an asynchronous beast).

Rather than setting an ugly (no offense) setTimeout, there are two potentially better possibilities.

1

If you're willing and able to update inline-manual-player.js, you could put something like this at the top of that file:

window.inline-manual-player-callback-queue = [];

... and later in that file, after window.inline_manual_player is asynchronously defined, add:

// I've made this an array, in case you'd like to add multiple functions to be called upon window.inline-manual-player being ready.
var queue = window.inline-manual-player-callback-queue;
var length = queue.length;
var i = 0;
for(i; i<length; i++) {
    queue[i]();
}

Finally, in your initial file, input the following:

function addCallback() {
    window.inline_manual_player.setCallbacks({
        onTopicDone: function (player, topic_id, step_id) {
            console.log('Step was displayed.');
        }
    });
}

$.ajax({
    url: "../Scripts/inline-manual-player.js",
    dataType: "script",
    beforeSend: function () {
        loader.hide();
        loader.unbind();
    },
    success: function() {
        // Because you've defined window.inline-manual-player-callback-queue at the start of inline-manual-player.js, the following will be available upon onload (aka success) of that file:
        window.inline-manual-player-callback-queue.push(addCallback);
    }
});

2

If editing inline-manual-player.js isn't an option, the initial author of that file may have already included something akin to window.inline-manual-player-callback-queue. Check the documentation of the file, and see if there's not a global method or array you can add your callback function to, to have inline-manual-player.js call it automatically, once all the async magic has taken place.

Best of luck to you!

jmealy
  • 583
  • 1
  • 5
  • 14
  • You could have saved some time if you would have read my answer and then Marek's. – Offir Aug 01 '16 at 06:57
  • You're answer isn't good, @OffirPe'er. It uses a setTimeout, and doesn't take advantage of the pattern @MarekSotak and the docs recommend: `var inline_manual_player = inline_manual_player || [];`. – jmealy Aug 10 '16 at 21:22
  • Marek sent me the answer I posted in private email. – Offir Aug 11 '16 at 06:16