Managing JavaScript References in Office 365

Scot Hillier

by Scot Hillier on 1/21/2015

Share this:

Article Details

Date Revised:

Applies to:
CDN Manager, GitHub, Scot Hillier, SharePoint development, SharePoint Online, third-party JavaScript libraries

Update: This article provides the background for CDN Manager, but read the article about version 2 and install that code instead. The code changed significantly.

Recently, David Mann started an interesting discussion about managing JavaScript references in SharePoint 2013 when he posted Loading JavaScript Libraries – It Shouldn’t be this Hard. The article was inspired by his experiences creating solutions that use JavaScript and are reliant upon third-party libraries. Simply put, there is no control in SharePoint 2013 over the loading and management of third-party JavaScript libraries. Therefore, many organizations suffer from a situation in which the same library is loaded multiple times, or different versions of the same library are loaded, or the order of loading does not support inter-library dependencies.

The situation is complicated by the fact that third-party JavaScript libraries may be introduced by apps, customized master pages, or script injection through web parts. These solutions may be developed by power users, professional developers, or ISVs who all have different standards and requirements. The solutions may be targeted at on-premises farms, SharePoint online, or both. Marc Anderson, who also does a significant amount of JavaScript work, proposed a “magic quadrant” to show the scope of the problem.

Figure 1, JavaScript library management problem scope

While Figure 1 shows that the scope of the problem is large, it occurred to me that one of the primary pain points is the management of JavaScript references in Office 365 for solutions that are based on Script web parts. As client-side development in SharePoint has become more prominent, more and more solutions are being created by simply injecting JavaScript into a SharePoint page. Furthermore, Microsoft is advising organizations against creating custom master pages in SharePoint online, which eliminates the most-common approach for getting custom JavaScript code into a page. Chris O’Brien wrote a great blog post you can read that describes the logic behind Microsoft’s new guidance. Even though the logic is understandable, the guidance sets up a situation in which JavaScript library loading is unmanageable.

For this article, I will take a shot at solving the specific case of managing third-party JavaScript libraries for SharePoint Online. Much of what I do in the article could certainly be used in an on-premises farm, but you also have more options on-premises where you are still free to create custom master pages without too much guilt.

Introducing the CDN Manager App

In order to manage the injection of third-party JavaScript library references, I created a SharePoint-hosted app named the CDN Manager. I have made this app available in a public GitHub repository so that the community may work on it together to make improvements. The app maintains a list of JavaScript library references that can be selected and then injected into the host web. This gives a single location where library references can be managed.

Getting started with the app

You can fork the repository or download the code as a zip and rebuild the app. Then you can upload the app to your app catalog in SharePoint Online where it will be available for installation in a given site. Once installed, you will see a super-fancy app icon (Figure 2) appear on the Site Contents page.

CDN Manager Icon

Figure 2, The CDN Manager app icon

When you launch the app, you will go to the main page, which shows a dashboard giving you the current status of loaded libraries.

CDN Manager Dashboard

Figure 3, The CDN Manager dashboard

The app comes pre-loaded with three commonly-used libraries. The dashboard shows the status of the libraries. The Validated column shows that the app has tested the URL associated with the library and was able to successfully reach the JavaScript file. The Active column shows that the given library is selected for installation into the host web. The Installed column indicates whether or not the given library is currently available in the host web. So, according to Figure 3, all of the library references are valid, only jQuery is selected for installation, and none of the libraries has yet been installed. Clicking the button labeled “Install Active CDNs” will inject a script reference for that library into the host web. Clicking “Remove All CDNs” will uninstall every listed library reference, whether it is marked as active or not.

It’s important to note that while the app is named “CDN Manager” and it comes pre-loaded with three CDN references, it will actually work with any valid URL. When the reference library is installed, the app simply creates a script tag with a src attribute pointing to the given URL. It’s also important to note that the URL entries use the protocol-relative URL syntax, which will ensure the URL is adjusted properly for http or https environments. At this point, you can go to any page in the site, drop a Script web part and write JavaScript code that utilizes jQuery. As a simple example, I put a Script web part on the page that injected the code in Listing 1. When the page runs, a notification appears as shown in Figure 4.

Listing 1, Testing for the presence of the jQuery library

<script type='text/javascript'>
 SP.SOD.executeOrDelayUntilScriptLoaded(isLoaded, 'sp.core.js');
   function isLoaded() {
       if (typeof jQuery === 'undefined') {
           SP.UI.Notify.addNotification("jQuery not loaded", false);
       } else {
           SP.UI.Notify.addNotification("jQuery loaded", false);

Figure 4, Notification that jQuery is installed

Once you understand the basics of the app, you can customize the list of library references to suit your needs. From the main page of the app, click “Manage CDN List.” This will take you to a SharePoint list containing the library references. You can simply add, edit, or delete references from this list. The Title field must contain a unique name for the library reference. The Url field should point to the location of the library. Remember that this field should use a protocol-relative URL. The Active field specifies that the library should be injected into the host web when you click the install button. The Sequence field determines the order of injection, which is important if libraries have dependencies on each other.

CDN Manager shows a SharePoint list containing the library references

Figure 5, The CDN list

Understanding the code

The critical function of the CDN Manager app is the installation of the library references in the host web. This is done by using the Microsoft Patterns and Practices script injection pattern. The referenced video describes the pattern from an MVC provider-hosted app using CSOM. The CDN Manager app, however, is written entirely in JavaScript using jQuery and the SharePoint REST API. Both approaches create a custom action extension programmatically where the Location property is set to ScriptLink and the ScriptBlock contains simple JavaScript that uses document.write to create a script tag referencing the specified library. The Sequence property of the custom action is used to manage the load order. Listing 2 shows a REST API call to create the custom action in the host web using the cross-domain library.

Listing 2, Creating the custom action to inject the script reference

var script = {
    "sequence": 0,
    "description": "jQuery",
    "scriptBlock": "document.write(unescape(\"%3Cscript src='" + 
                   "//" + 
                   "' type='text/javascript'%3E%3C/script%3E\"));"

var executor = new SP.RequestExecutor(appWebUrl);

    url: "../_api/SP.AppContextSite(@target)/web/usercustomactions" +
         "[email protected]='" + hostWebUrl + "'",
    method: "POST",
    body: JSON.stringify({
        'Sequence': script.sequence,
        'Description': script.description,
        'Location': 'ScriptLink',
        'ScriptBlock': script.scriptBlock
    headers: {
        "content-type": "application/json",
        "accept": "application/json",
        "X-RequestDigest": jQuery("#__REQUESTDIGEST").val()
    success: function (data) {
    error: function (err) {

Engaging the community

The CDN Manager is intended as a starting point for managing library references in SharePoint Online when you are not editing master pages and using Script Editor web parts to inject JavaScript. I expect the community to have some good ideas for improving the app, so you can certainly start a discussion here with your good ideas. Additionally, you should feel free to fork the GitHub repo and make changes. Submit pull requests to me, and we can work together to improve the functionality.

Topic: Development

Sign in with

Or register

  • Bah! Submitted too soon. :-(

    I've got some ideas to try out. I send a pull request if I come up with anything interesting.

    --Dave Mann
  • Scot, thanks for jumpstarting this. The conversation needs to be had and options discussed. Are we as a community going to come up with the perfect answer for every situation? Not without some help from Microsoft. That doesn't mean we shouldn't have the discussion. I think that *most* situations can be solved but it will take some work. At the very least, it will be an enlightening discussion.

    Devs: please fork the repo and find a problem to fix. Let's see where this goes...
  • Please use #CDNManager on Twitter
  • Thanks for all the feedback. The discussion is exactly what we want to start. The idea is for community members to fork the repo and make these types of improvement. Then debate them and send me a "pull request". I'm not looking to be the one who combs through comments and then produces a new version. I want the community to do that!

    A couple of thoughts for the debate:
    1. Remember, the app requires full control over the web, so it can't be in the store and there has to be a high level of trust before installing it
    2. It's a good idea to put in some tests on the items in the list to make sure they are really CDNs. The app is already checking the CDN validity, but will still install one that isn't valid. That should probably change.
    3. Yes, we know that this does not prevent other apps or end users from goofing things up by deploying their own references anyway. There will never be a replacement for some level of governance, but this can work well as a starting point. I know David Mann wants to add the capability to catalog what's already there -that a great idea.

    Thanks again, everyone
  • Code doesn't stay formatted after submission so here is the pastebin from my last post.

    I didn't register btw sorry - Hugh Wood
  • Hi Scott,
    At the moment the app is open to xss, which isn't what we want at all (Implied eval with codeblock).
    Also the app has to be used and configured on every site collection, a good step would be to allow it to take from a list on a central portal of allowed CDNs, and have defaults.
    Lastly use SOD and register the loaded frameworks:

    Example (Please use namespaces!!!! I didn't because it is an example)

    var i = 0;
    function foo() {
    console.log("JQuery loaded from CDN with SOD");
    function checkLoadedLoop(key, namespace){
    setTimeout(function() {
    checkLoadedLoop(key, namespace);
    }, 10);
    function objectExists(name) {
    var index = 0,
    parts = name.split('.'),
    result = window;
    index = 0;
    try {
    while (typeof result !== "undefined" && result !== null && index < parts.length) {
    result = result[parts[index++]];
    catch (e) {
    console.log(index + "," + parts.length);
    if (index < parts.length) {
    return false;
    return true;
    function NotifySOD(key) {
    // Ensure that the bootstrap has loaded, if this is wrong then script load order is incorrect
    if (typeof (NotifyScriptLoadedAndExecuteWaitingJobs) == "function") {
    } else {
    throw "SP Context not found.";
    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);
    getScript("jQuery", "jQuery.fn.jquery", foo, true);
  • How should this solve the problem of a 3rd party script beeing loaded several times, as long as not all involved parties are using the CDN manager (which is an unlikely scenario)? A different version of jQuery for example still can be included using a different approach (through masterpage, script editor, custom action in another solution).

    Also since this approach is using the ScriptLink action, won't this inject the script in the host web and all subwebs, including any app webs? Which is not what I would expect if I enable a script injection in the CDN Manager app, that the script is also loaded in any other app installed in the same hostweb (at least using ScriptLink from sandboxed solutions has this effect).
  • As Scot said, "The CDN Manager is intended as a starting point". If you have concerns, then by all means grab the repo and make improvements in the code base!

  • Be careful with this. Unlike a sandbox solution or provider-hosted app there is no way to automatically retract the injected script when the app is removed. You must manually remove the references by calling a function that removes your custom action from the UserActions collection directly. Otherwise, the script references will get orphaned in the host web with no way to pull them out without writing JSOM/CSOM (or reinstalling the app and running your removal function). Worse, there is no way to warn the administrator of this on the app tile itself. You can put language into the landing page of the SP app but that doesn't guarantee the site admin will remember to perform the script removal before just removing the app.