CLOSE
CLOSE
https://www.sikich.com

Table Browser Extension for D365: Part 2 Convert Bookmarklet to Browser Extension

In part one of this series, we talked about how Microsoft Dynamics 365 Finance and Operations (D365FO) can leverage the browser and web-based technologies to augment and extend D365FO functionality. We started by making a JavaScript bookmark, which watermarks our D365FO “Gold” environment and changes the “NavBar” elements background color. In part 2 we are going to convert that bookmarklet to a Chrome browser extension. This can also be done for other browsers such as Opera and Firefox.

Our Chrome browser extension will consist of two files. Both files can be created with your text editor or IDE of choice:

  1. A manifest file (manifest.json)
  2. A content script file (content.js)

Create a manifest file (manifest.json)

To create a Chrome browser extension, we will first create a manifest file that defines the name and permissions needed for our extension. The manifest is a JSON file, the format of which is defined in the Chrome and Mozilla developer documentation.

Example 2A manifest.json:

{

  "manifest_version": 2,

  "name": "d3f2goldflag",

  "version": "0.1",

  "permissions": [ "tabs",

    "activeTab",

    "<all_urls>"

  ], 

  "content_scripts": [

    {

   "run_at": "document_idle",

      "matches": ["https://[gold].sandbox.operations.dynamics.com/*"],

      "js": ["content.js"]   

    }

  ]

}

The manifest in example 2A says that we will call our extension “d3f2goldflag,” that the extension needs permissions for tabs and all URLS, and for the browser to run a Content JavaScript file when the page reaches “document_idle” state on a URL that matches. Placeholder [gold] in the code can be replaced by the address to your gold environment.

Create a content script (content.js)

In order for Visual Studio IntelliSense to know about the Chrome API, first download an IntelliSense file and add a comment to the top of your new JavaScript content.js file:

/// <reference path="chrome.intellisense.js" />

We can then start creating our content script by more or less migrating the function from our bookmarklet:

var d = document, e = d.getElementById("NavBar");     

e.style.backgroundColor = "red";     

document.getElementById("NavBarDashboard_label").innerHTML = 

"D3FO GOLD NO TRANSACTIONS";

Enable developer mode in the Chrome browser extension settings, and then load the new unpacked extension. Some additional details on getting started can be found in the Chrome browser extension documentation.

Once you have loaded the extension, refreshing with F5 on a D365FO page will cause the content script to execute. We can see how our bookmarklet ported and it’s not well. The background color changed on the “NavBar,” but our code to notify the user with Text appears to have had no impact. If we open the DevTools (F12 in Chrome) and reload the page, we should see the issue right away. Our script is not finding the node with id: “NavBarDashboard_label” because the page has not finished loading or creating it yet.

There are a few different ways one can address this problem. We used the MutationObserver interface to check for existence of the “NavBarDashboard_label” node and a timeout in case the observer can’t find the parent node. With these updates we can now listen for when the “NavBar” element has updates to its children, and then check to see if “NavBarDashboard_label” is one of them allowing the page to finish loading before attempting to change the innerHtml.

Example 2B content.js

var e = document.getElementById("NavBar");

if (typeof (e) != 'undefined' && e != null) {

    var d = document, e = d.getElementById("NavBar");

    e.style.backgroundColor = "red";

}

else {

    console.log("failed to find nav bar");

}

//https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver

// Callback function to execute when mutations are observed

var callback = function(mutationsList, observer) {

    for(var mutation of mutationsList) {

        if (mutation.type == 'childList') {

            if(typeof (document.getElementById("NavBarDashboard_label")) != 'undefined') {

                document.getElementById("NavBarDashboard_label").innerHTML = 

                    "D3FO GOLD NO TRANSACTIONS";

                observer.disconnect();

                console.log('nav bar label updated');

            }

        }

    }

};

// Create an observer instance

var observer = new MutationObserver(callback);

//check for node and timeout

//https://stackoverflow.com/questions/40398054/observe-on-mutationobserver-parameter-1-is-not-of-type-node

function addObserverIfDesiredNodeAvailable() {

    var target = document.getElementById( "NavBar" );

    if(!target) {

        //The node we need does not exist yet.

        //Wait 500ms and try again

        window.setTimeout(addObserverIfDesiredNodeAvailable,500);

        return;

    }

    var config = { attributes: true, childList: true, subtree: true };

    if(typeof(target != 'undefined')){

        observer.observe(target,config);

    }

}

addObserverIfDesiredNodeAvailable();

Now that we have given the DOM a chance to finish loading, our extension should have the intended effect:

convert bookmarklet to browser extension

In part three of the series, we will expand on the capabilities of Chrome browser extensions and use the D365FO metadata API. By combining our metadata information with our browser extension, we can simplify opening the D365FO table browser with an extension.

Have any questions? Feel free to contact us at any time!

This publication contains general information only and Sikich is not, by means of this publication, rendering accounting, business, financial, investment, legal, tax, or any other professional advice or services. This publication is not a substitute for such professional advice or services, nor should you use it as a basis for any decision, action or omission that may affect you or your business. Before making any decision, taking any action or omitting an action that may affect you or your business, you should consult a qualified professional advisor. In addition, this publication may contain certain content generated by an artificial intelligence (AI) language model. You acknowledge that Sikich shall not be responsible for any loss sustained by you or any person who relies on this publication.

About the Author