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!