Blog - Rencore

Loading a JavaScript library from a CDN

Written by Rencore | Jan 22, 2015 1:42:17 PM

Why this is required

I can see people still using script tags, get script etc to load external or other libraries into their SharePoint pages.

Scot Hillier recently did something like this for the remote provisioning model (Blog post can be found here) However even in this awesome idea SOD/MDS and ADM have been ignored. In my blog series SharePoint JavaScript Development Part 4 I detailed how to use SOD, MDS and ADM together with your scripts. But I don’t say how to use this with a CDN, or how to activate SOD on scripts that don’t have the required components.

Basically you have 2 options:

Option 1) Edit the libraries and host them yourselves

Pros: Strong governance over the scripts, and right testing controls

Cons: Bugs won’t get fixed automatically in updates

Option 2) Load the files from the CDN, shimming in the code when it is loaded for SOD/MDS and ADM

Pros: Bugs in the libraries will automatically be patched

Cons: No control over updates and testing is hard

Deciding which is up to you, however option 1 we can use the samples I gave before for loading. Option 2 doesn’t work straight away.

So to get around this I have written this little example of what would be required to correctly load CDN libraries with SOD and register them for ADM and MDS.

The code:

Note no namespaces are used, please use them in your code!

// callback method
function foo() {
    console.log("JQuery loaded from CDN with SOD");
    console.log(jQuery.fn.jquery);
}
// looping method to check if script is loaded without SOD
function checkLoadedLoop(key, namespace){
    if(!objectExists(namespace))
    {
        // Recursively call this method until script is loaded
        setTimeout(function() {
            checkLoadedLoop(key, namespace);
        }, 10);
    }
    else
    {
        // Script is loaded, register with ADM/MDS and notify SOD
        Type.registerNamespace(namespace);
        NotifySOD(key);
    }
};
// Method to check for our objects being ready
function objectExists(name) {
    var index = 0,
    parts = name.split('.'),
    result;
    result = window;
    index = 0;
    try {
        // Loop through each part of the namespace and check to see if the objects exist
        while (typeof result !== "undefined" && result !== null && index < parts.length) {
            result = result[parts[index++]];
        }
    }
    catch (e) {
    }
    if (index < parts.length) {
        return false;
    }
    return true;
}
// Notify SOD wrapper
function NotifySOD(key) {
    // Ensure that the bootstrap has loaded, if this is wrong then script load order is incorrect
    if (typeof (NotifyScriptLoadedAndExecuteWaitingJobs) == "function") {
            NotifyScriptLoadedAndExecuteWaitingJobs(key);            
        } else {
            throw "SP Context not found.";
        }
}
// Execute func wrapper
function getScript(key, namespace, callback, external, bSync) {
    // Apply sealing pattern to callback
    if (typeof key == "string") {
        // Ensure script is called from the server, and attach a loaded event
        SP.SOD.executeFunc(key, namespace, function() {
            // this only executes on loading of a namespace
            }, bSync);
            // This executes when NotifyScriptLoadedAndExecuteWaitingJobs is called
        SP.SOD.executeOrDelayUntilScriptLoaded(callback, key);
    } else if (typeof key.length != "undefined") {
       // Sometimes we want to load multiple scripts in parallel
       // We can do this by accepting an array of keys and using loadMultiple
       SP.SOD.loadMultiple(key, callback, bSync);
    } else {
       return false;
    }
    if(external) {
        checkLoadedLoop(key, namespace);
    }
}
// Register our CDN library
SP.SOD.registerSod("jQuery","//code.jquery.com/jquery-1.11.2.min.js");
// Load our CDN library calling foo when loaded
getScript("jQuery", "jQuery.fn.jquery", foo, true);

As you can see for production this script has all the methods in place. They just need integrating into your frameworks and testing in your scenarios.

The namespace you supply to the getScript method should be the expected object chain you wish to use, in jQuery’s case I have used the version number object to check to see if the script has finished loading.

All code comments are in place so feel free to ask any questions you may have.

Taking this further

You can easily take this a step further, and I will be doing this myself combining my efforts with that of Scot Hillier creating a 365 app that provides global custom governable CDN support to your 365 tenants.

If you have any ideas for using this sort of process, let me know in the comments below. I am happy to have a discussion or many discussion on the topic!