locked
Rollup 12 - problem with web resources and jquery

    Question

  • Hi all,

    Before  Rollup 12, the main form iframe of an entity added js script references in the <head> section. After RU12 the scripts are added in the body and scripts aren't always executed in the order in which they referenced - causing random 'jquery' is undefined errors.

    Is there any way of forcing CRM to reference scripts in the <head> section, or a way of adding 'DEFER' to the script tags ?

    Paul

    Thursday, February 28, 2013 3:43 PM

Answers

  • I've done a blog post about this topic that describes the async loading behaviour and possible solutions - http://www.develop1.net/public/post/Asynchronous-loading-of-JavaScript-Web-Resources-after-U12POLARIS.aspx

    Scott Durow
    Read my blog: www.develop1.net/public     Follow Me on Twitter
    If this post answers your question, please click "Mark As Answer" on the post and "Mark as Helpful"

    Friday, March 29, 2013 9:12 PM
    Answerer
  • Thanks Scott for posting this.  Just a suggestion to make it a little more reusable, this is what I have come up with.  It makes it so that you don't need to ever modify the actual 'waitForScripts' method.  Just pass in the name of objects or functions that you expect to be defined and it will dynamically check to make sure that they are defined.  So it looks like this:

    function WaitForScripts(ObjectNames, callback) {

        var hasLoaded = false;

        function checkScripts() {

            var allLoaded = true;

            for (var i = 0; i < ObjectNames.length; i++) {

                var hasLoaded = true;

                var ObjectName = ObjectNames[i];

                var Script = "typeof " + ObjectName + " == 'undefined'";

                hasLoaded = !eval(Script);

                allLoaded = allLoaded && hasLoaded;

                if (!allLoaded) { setTimeout(checkScripts, 10); break; }

            }

            if (allLoaded) { callback(); }

        }

        setTimeout(checkScripts, 0);

    }


    Steve R

    Tuesday, April 23, 2013 7:43 PM

All replies

  • Deference is already applied, at the form, by way of events.  So long as every bit of code you deploy is wrapped in a function, and associated with events through the form editor, then you can be sure that none of them will fire until all are loaded.  That's the purpose of the form's events.  If you have unencapsulated code, then yes, it will run whenever it is loaded, and that is unpredictable.  You'd probably have to get into some serious asynchronous JS frameworking to get around that.


    Dave Berry - MVP Dynamics CRM - http:\\crmentropy.blogspot.com Please follow the forum guidelines when inquiring of the dedicated CRM community for assistance.

    Thursday, February 28, 2013 3:50 PM
    Moderator

  • Hi David,

    The first web resource that is included in our form is the jquery library, which defines 'jquery' and $. After that, we have other libraries that expect those variables to be defined. This was all functional pre-RU12 because the scripts executed sequentially. These jquery undefined errors happen even when no events are attached to the form (only referencing the scripts).

    If I include alerts in each of the libraries, pre RU12 the alerts are displayed in the same order as the scripts are referenced - post RU12 the order is random.

    How can I make sure the libraries are loaded in the order in which they are referenced  ?

    Paul

    Thursday, February 28, 2013 4:05 PM
  • You're not the only one that has run into this.  Some have gotten around it by using require.js.  Again, the form shouldn't fire its events or method calls until all registered scripts have been loaded--meaning that no script should be executed until that time.  The only scripts that should, are those that have been intentionally designed to do so.  This should only be a problem for code within script files that are not fully encapsulated.  Any unencapsulated code will run at the moment it's imported to the DOM.

    If a library you're using is being a "bad actor" in this space, then I recommend wrapping the whole thing in a "function()" declaration and using namespacing principles as a work around.  I honestly believe that it's the responsibility of a library writer to expect their code to be loaded in an unfavorable order, but also to trust the device/implementer to have all scripts loaded before calling out any methods.  It's hard to say that this is Microsoft's fault, but it's obvious that their change has forced these things to the surface.


    Dave Berry - MVP Dynamics CRM - http:\\crmentropy.blogspot.com Please follow the forum guidelines when inquiring of the dedicated CRM community for assistance.

    Thursday, February 28, 2013 5:41 PM
    Moderator
  • Nevertheless Microsoft should have warned us about this change. They deployed it in CRM online without informing us.

    There are enough valid examples where you want control over the order of your files.

    “responsibility of a library writer to expect their code to be loaded in an unfavorable order”

    I think the makers of JQuery and JQuery UI are responsible library makers but you will get a javascript error when JQuery is not loaded before JQuery UI. (Same problem with JQuery Plugins)

     

    Some partners work with namespaces and defined the ‘first level’ namespaces in a global.js webresource. In a file called account.js (containing only code needed on account) you expected to have the base namespaces available. This is of course easy solvable.

     

    Just think if a browser would suddenly change the order of the javascript, a lot of (big) sites would be in trouble. (Even this forum J)


    Thursday, February 28, 2013 6:57 PM
  • I won't dispute Microsoft's responsibility to identifying these changes, when they're intentional.  I can't speak to whether this was intentional, or not.  I suspect they've implemented a more dynamic and asynchronous loading mechanism to streamline the form's load, and that this is a byproduct of that optimization.  All the same, I think that it forces us to consider, as developers, the dynamics of script function as wholly independent units with interdependent bindings that resolve themselves at the platform's discretion.  I would recommend that you issue a patch to jQuery UI and submit it back to the project for consideration.  The beauty of open-source, eh?  ;)

    Dave Berry - MVP Dynamics CRM - http:\\crmentropy.blogspot.com Please follow the forum guidelines when inquiring of the dedicated CRM community for assistance.

    Thursday, February 28, 2013 7:37 PM
    Moderator
  • I won't dispute Microsoft's responsibility to identifying these changes, when they're intentional.  I can't speak to whether this was intentional, or not.  I suspect they've implemented a more dynamic and asynchronous loading mechanism to streamline the form's load, and that this is a byproduct of that optimization.  All the same, I think that it forces us to consider, as developers, the dynamics of script function as wholly independent units with interdependent bindings that resolve themselves at the platform's discretion.  I would recommend that you issue a patch to jQuery UI and submit it back to the project for consideration.  The beauty of open-source, eh?  ;)

    Dave Berry - MVP Dynamics CRM - http:\\crmentropy.blogspot.com Please follow the forum guidelines when inquiring of the dedicated CRM community for assistance.


    Considering Microsoft 'broke' the functionality (intentionally or not) a better alternative would be for microsoft to FIX it (or at least give the option to load scripts in the Header tag). Scripts have always executed in the order they are referenced and Microsoft even lets you change the order of web resources in CRM (which has now become completely pointless).
    Thursday, February 28, 2013 8:30 PM
  • "I won't dispute Microsoft's responsibility to identifying these changes, when they're intentional."

    Is there such a thing as an intentional bug?

    We do the same thing in our forms, have the jquery libraries listed first, and then the main form .js listed at the end of any pre-requisites..

    Forgive my confusion but has this change broken that? Or are we ONLY talking about unencapsulated code?


    Thanks! Josh Ash




    • Edited by Josh Ashwood Thursday, February 28, 2013 10:05 PM
    Thursday, February 28, 2013 10:01 PM
  • "I won't dispute Microsoft's responsibility to identifying these changes, when they're intentional."

    Is there such a thing as an intentional bug?

    We do the same thing in our forms, have the jquery libraries listed first, and then the main form .js listed at the end of any pre-requisites..

    Forgive my confusion but has this change broken that? Or are we ONLY talking about unencapsulated code?


    Thanks! Josh Ash





    Probably, clear your cache and check the console in Developer tools. If you have any libraries that reference $ or jQuery, you'll see random undefined error messages.
    Friday, March 01, 2013 3:28 PM
  • I don't dispute that this is a bug, based on the implied functionality of library ordering in the form customization.  My statement, "when they're intentional" means that I agree Microsoft has a responsibility to list these behavioral changes ahead of or conjointly with the update release.  For bugs, contact Microsoft Support.  This is demonstrable and reproducible; there should not be a fee to submit this trouble report--and, you'll be among the first to receive a hotfix when they're made available.

    When I spoke to dynamic JavaScript inclusion, especially when made in a cross-threaded, asynchronous fashion, I think that modern script libraries should resolve their own dependencies.  If you're familiar with the programming concept, "Dependency Injection", then this will make more sense.  Scripts should be more tolerant to unordered loads.  As a principle, I believe the benefits are strong.


    Dave Berry - MVP Dynamics CRM - http:\\crmentropy.blogspot.com Please follow the forum guidelines when inquiring of the dedicated CRM community for assistance.

    Saturday, March 02, 2013 3:24 AM
    Moderator
  • Hi Paul

    I just blogged about this, http://www.magnetismsolutions.com.au/blog/gayanperera/2013/03/03/crm-2011-polaris-asynchronous-javascript-loading
    The workaround isn't pretty but it works.


    Kids don't try this at home!

    Sunday, March 03, 2013 10:00 PM
  • Thanks for the workaround code. I'm surprised we are not hearing a lot more noise about this issue? There must be a lot of deployments out there referencing JSON and JQuery etc that have installed the UR12 update, and you'd think they would all be having script issues.  

    As it stands though, this is a pretty bad bug.

    I don't have UR12 installed and based on all the issues I'm seeing on these forums, I don't have any plans to install it.. 

    Does anyone have it installed that can raise a support ticket so we can get an ETA on a fix?


    Thanks! Josh Ash

    Sunday, March 03, 2013 10:37 PM
  • Hi Josh, there is a bit of noise but as Dave mentioned above, the benefits are strong. Check this blog post (http://www.stevesouders.com/blog/2010/12/15/controljs-part-1/) for an example of page load/rendering times based on async/sync loading.


    Kids don't try this at home!

    Sunday, March 03, 2013 11:04 PM
  • I'm not sure the issue of rendering and loading times on async/sync loading is very relevant here. 

    The relevant issue is that there is a breaking change in this update that causes failures. 

    And we need a fix for it. 


    Thanks! Josh Ash

    Sunday, March 03, 2013 11:27 PM
  • Hi Paul

    I just blogged about this, http://www.magnetismsolutions.com.au/blog/gayanperera/2013/03/03/crm-2011-polaris-asynchronous-javascript-loading
    The workaround isn't pretty but it works.


    Kids don't try this at home!

    Not very pretty but it works :)

    But we still have a few more problems with script errors in global.ashx and PageLoader.js that appear related...

    Wednesday, March 06, 2013 8:31 PM
  • Can you expand on what the errors are that you're seeing and what you are doing to resolve them? I would not even understand what the nature of the problem was without Gayan and Davids posts and am trying to understand how you could still be seeing issues with the workaround script in place,as conceptually it would seem that solves the issues - could there be some internal javascript that is having the same issue? And, do you have a ticket open about this?


    Thanks! Josh Ash


    Thursday, March 07, 2013 8:40 AM
  • Hahaha yeah, it's a cheap and dirty hack. You might be able to make it a bit prettier/nicer by doing a few smart things like looping through existing script tags that get rendered by the PageLoader, check for "loaded=true" flag PageLoader sets when it's done loading js libs asynchronously.

    PageLoader does a pretty good job loading asynchronously and waits for the correct lib to load before calling the function, but because each function can only be linked to 1 lib without specifying a dependancy chain we get these issues. eg: as soon as PageLoader sees LibX got loaded it'll call FunctionX on that lib but doesn't realize that LibX relies on LibY which hasn't loaded yet.


    Kids don't try this at home!

    Thursday, March 07, 2013 9:25 AM
  • Hi Gayan,

    I've had to adopt a similar approach to the one described in your blog post because of this issue.

    One thing I'd add is that you need to make sure you use the current 'window.WEB_RESOURCE_ORG_VERSION_NUMBER' in the webresource url's otherwise the browser will download a new version with every request and not use the local cached version (poor performance, increased bandwidth required etc.)

    The url would then be something like:

    http://server/orgname/%7B634982751850003236%7D/WebResources/....js

    I use a lot of jQuery and scriptsharp dependant scripts and the issue is that these scripts are assumed to be present before other scripts load - it's a shame we can't elect some scripts to load synchronously to maintain backward compatibility. Maybe this option will be added in the future.


    Scott Durow
    Read my blog: www.develop1.net/public     Follow Me on Twitter
    If this post answers your question, please click "Mark As Answer" on the post and "Mark as Helpful"

    Thursday, March 07, 2013 6:32 PM
    Answerer
  • Hi,

    My friend Maarten Doctor solved this issue even before UR12 was release! 

    http://www.maartendocter.nl/requirejs-typescript-msdyncrm2011/

    Nice one Maarten!




    Scott Durow
    Read my blog: www.develop1.net/public     Follow Me on Twitter
    If this post answers your question, please click "Mark As Answer" on the post and "Mark as Helpful"

    Thursday, March 07, 2013 6:43 PM
    Answerer
  • Off topic sorry, Scott do you use scriptsharp 0.8 or 0.7.5?

    Kids don't try this at home!

    Thursday, March 07, 2013 8:15 PM
  • HI Gayan,

    I use 0.7.5, but I've had to edit the mscorlib.js so that it doesn't interfere with Dynamics CRM version. 


    Scott Durow
    Read my blog: www.develop1.net/public     Follow Me on Twitter
    If this post answers your question, please click "Mark As Answer" on the post and "Mark as Helpful"

    Thursday, March 07, 2013 8:31 PM
    Answerer
  • I've been looking at this some more. I wasn't happy with avoiding using the CRM2011 script loader and replacing it with another - so instead, I've written a function that simply waits until the required scripts are loaded and then executes the depending code:

    To use it, you must wrap any code that is dependant on other code as follows:

    waitForScripts("somescript",["mscorlib", "jquery", "jquery_ui"],
    function () {
    
    ... Your original code goes here...
    
    });

    The first parameter is the name of the script that's loading (so other scripts can wait for it) and the second parameter is the list of scripts to wait for.

    To ensure that jQuery UI is loaded only after jQuery, you could wrap the whole of jQuery UI in :

    waitForScripts("jquery_ui",["jquery"],
    function () {
    
    ...jQuery UI goes here
    
    
    });


    The function needs to be accessible in the same script file as:

    function waitForScripts(name, scriptNames, callback) {
        var hasLoaded = false;
        window._loadedScripts = window._loadedScripts || [];
        var checkScripts = function () {
            var allLoaded = true;
            for (var i = 0; i < scriptNames.length; i++) {
                var hasLoaded = true;
                var script = scriptNames[i];
                switch (script) {
                    case "mscorlib":
                        hasLoaded = typeof (window.ss) != "undefined";
                        break;
                    case "jquery":
                        hasLoaded = typeof (window.jQuery) != "undefined";
                        break;
                    default:
                        hasLoaded = window._loadedScripts[script];
                        break;
                }
              
                allLoaded = allLoaded && hasLoaded;
                if (!allLoaded) {
                    setTimeout(checkScripts, 10);
                    break;
                }
            }
    
            if (allLoaded) {
                callback();
                window._loadedScripts[name] = true;
            }
        }
        setTimeout(checkScripts, 0);
    }

    UPDATE: I adopted a simpler approach (amended above), so that rather waiting for the actual script to load, I'm waiting for a semaphore to be added to an array on the window. This way, we can wait for the actual script to loaded AND run.

    Microsoft support are aware of the issue, and I'm awaiting more information on the way forwards...

    UPDATE (27-March-13) : Still nothing back from support on this - they are still researching it...

    UPDATE (24-April-13) : I've been told that this issue will be addressed in UR15. The update will revert script loading behaviour back to pre-UR12.

    UPDATE (6-Sept-13) : This is confirmed as being fixed in UR15 http://support.microsoft.com/kb/2843571


    Scott Durow
    Read my blog: www.develop1.net/public     Follow Me on Twitter
    If this post answers your question, please click "Mark As Answer" on the post and "Mark as Helpful"






    Friday, March 08, 2013 12:33 AM
    Answerer
  • I've done a blog post about this topic that describes the async loading behaviour and possible solutions - http://www.develop1.net/public/post/Asynchronous-loading-of-JavaScript-Web-Resources-after-U12POLARIS.aspx

    Scott Durow
    Read my blog: www.develop1.net/public     Follow Me on Twitter
    If this post answers your question, please click "Mark As Answer" on the post and "Mark as Helpful"

    Friday, March 29, 2013 9:12 PM
    Answerer
  • Thanks Scott for posting this.  Just a suggestion to make it a little more reusable, this is what I have come up with.  It makes it so that you don't need to ever modify the actual 'waitForScripts' method.  Just pass in the name of objects or functions that you expect to be defined and it will dynamically check to make sure that they are defined.  So it looks like this:

    function WaitForScripts(ObjectNames, callback) {

        var hasLoaded = false;

        function checkScripts() {

            var allLoaded = true;

            for (var i = 0; i < ObjectNames.length; i++) {

                var hasLoaded = true;

                var ObjectName = ObjectNames[i];

                var Script = "typeof " + ObjectName + " == 'undefined'";

                hasLoaded = !eval(Script);

                allLoaded = allLoaded && hasLoaded;

                if (!allLoaded) { setTimeout(checkScripts, 10); break; }

            }

            if (allLoaded) { callback(); }

        }

        setTimeout(checkScripts, 0);

    }


    Steve R

    Tuesday, April 23, 2013 7:43 PM
  • Hi Steve,

    Great work - thanks for sharing.


    Scott Durow
    Read my blog: www.develop1.net/public     Follow Me on Twitter
    If this post answers your question, please click "Mark As Answer" on the post and "Mark as Helpful"

    Tuesday, April 23, 2013 8:24 PM
    Answerer
  • Scott,

    I would like to confirm how to call your function:

    waitForScripts(name, scriptNames, callback)

    I am using jquery_block_ui and jqueryUI in my solution.

    If I understand your post

    http://www.develop1.net/public/post/Asynchronous-loading-of-JavaScript-Web-Resources-after-U12POLARIS.aspx

    1. Define the function waitForScripts in my version of jqueryUI. QN: Do I need to define a waitForScripts inside of jQueryUI? I think I do, since it's possible jQueryUI will try to load before my account form js and before jQuery js. If this is true, what code from jQueryUI do I implement inside the waitforScripts?
    2. Define the function waitForScripts in my account form js file when calling jQueryUI autocomplete

    waitForScripts("account", ["jQueryUI", "jQuery"], function() { // my code inside account which includes call to autocmomplete });

    Cheers.

    Mark

    Sunday, May 12, 2013 9:43 PM
  • Hi Scott, 

    Can you clarify something for me?  In your example, I assume the 'OnLoad' function exisits in client.js?  If so, a quick question - I have a form that is loading jQuery.js, JSON2.js, RestOperations.js, and new_entity_OnLoad.js (they should load in that order, and each is dependent on the previous).   The function I am calling in the form onLoad event exists in the new_entity_OnLoad.js.  For now, it's simply : function OnLoadTest () { alert('Hi Mom!'); } , but it will make calls to functions in RestOperations, so I need RestOperations to be loaded prior.

    I've wrapped all four files as you've described, and the watForScripts code is firing in order for each.  However, when the form tries to fire the OnLoadTest it's throwing:
    There was an error with this field's customized event. 

    Field: window

    Event:onload

    Error: 'OnLoadTest' is undefined.

    If I throw an alert('running?); in before the  function OnLoadTest () { alert('Hi Mom!'); },  "running?" is shown, so I know the callback code is executing.   If I remove your code, the OnLoadTest fires fine (even if I include calls to functions in the RestOperations).  Do I need your solution in the new_entity_OnLoad.js ?

    Thanks!



    Monday, June 17, 2013 4:22 PM
  • Hi,

    Sometimes you'll see everything working fine if you remove the wait because you're actually loading cached versions of the scripts - if you clear the cache, then you'll then see the issue.

    You will need to use the waitForScript in the new_entity_OnLoad if it depends on another script for it to load (code that runs immediately rather than being called from an event) - but if the dependencies are purely in functions that you are calling from OnLoad then you won't need to use it.

    The technique I describe here is to deal with library dependencies such as jQuery UI that can't actually load without jQuery being loaded first.



    Scott Durow
    Blog: www.develop1.net    Follow Me
    Rockstar365  Profile
    If this post answers your question, please click "Mark As Answer" on the post and "Mark as Helpful"

    • Proposed as answer by John Voorhis Monday, June 17, 2013 8:22 PM
    Monday, June 17, 2013 6:27 PM
    Answerer
  • Ahh I got ya, the new_entity_onload just has the function that is executed by the form onLoad event.  I references functions in the RestOperations (the SDK Rest helper). Noting in it is executed until the form tries to actually raise the event.   I still can't digure our why it doesn't see the OnLoadTest function, though.

    I know about the cache thing, and had been clearing it. That's makes it all the more confusing.  Thanks for your help and the great workaround (and Ribbon Workbench!). 

    Monday, June 17, 2013 8:22 PM
  • i have found few solutions as i am migrating my old CRM to to rollup 12.It is pain and these are the few thing i have done.

    http://chamarairesh.blogspot.com/2013/07/rich-text-box-with-crm-2011-rollup-12.html

    and 

    http://chamarairesh.blogspot.com/2013/06/how-to-fix-crm-2011-update-rollup-12.html

    Thursday, July 04, 2013 4:05 AM