While doing a project where MS Dynamics CRM is used a lot of customizations are performed by JavaScript.
Usually the way to it is to perform some JavaScript actions in the OnLoad of the Page.
MS Dynamics CRM has a extention point, where you can control the OnLoad of Detail Forms by entering JavaScript.
Now when you need to deploy your CRM configuration to more than one system (like we do at my project, it is sold as part of a product), you want to use a centralized Javascript file so you can change your url's etc. all in one place.
To do this (unsupported by Microsoft!) I learnt the following technique from CRM Specialists:
First technique
1: var script = document.createElement('script');
2: script.language = 'javascript';
3: script.src = '/_customscript/customscript.js';
4: script.onreadystatechange = OnScriptReadyState;
5: document.getElementsByTagName('head')[0].appendChild(script);
6:
7: function OnScriptReadyState()
8: {
9: if (event.srcElement.readyState == 'complete')
10: {
11: // Perform onload script
12: //Doit();
13: }
14: }
Listing 1
The drawback with this technique is that the first time CRM loads (and every time the cache is empty) the script is not executed. Leaving the user to think the application does not work. After some time it really annoyed me, so I started to ask uncle Google again for a solution. I found the following on http://blog.odynia.org/archives/1-Javascript-Includes.html.
What this guy does is doing an AJAX call, to get the js file.
Next he loads the javascript as a string, eval() it, and imports all functions found into the current namespace, so you can access them.
It needs functionnames a-z, it cannot handle numeric values in the name of the function, but i will fix this before I will use it.
Otherwise I think it rocks! Async technique (no first time drawback)
1: function load_script (url)
2: {
3: var x = new ActiveXObject("Msxml2.XMLHTTP");
4: x.open('GET', url, false);
5: x.send('');
6: eval(x.responseText);
7: var s = x.responseText.split(/\n/);
8: var r = /^function\s*([a-z_]+)/i;
9: for (var i = 0; i < s.length; i++)
10: {
11: var m = r.exec(s[i]);
12: if (m != null)
13: {
14: window[m[1]] = eval(m[1]);
15: }
16: }
17: }
18:
19: load_script("/_customscript/customscript.js");
20:
21: //perform onload scripts
22: //DoIt();
Listing 2
Addition:
As I mentioned numbers in the functionname caused the code to fail. So I changed the regex pattern in line 8 from listing 2 into:
1: var r = /^function\s*([a-zA-Z_0-9]+)/i;
Listing 3
With this regex pattern functions with numbers in the name also are added to the namespace. I added the uppercase A-Z not because functions with uppercase characters in the name where not added, but as a best practice. also you can never be sure browsers keep on using IgnoreCase as default setting.
As you can read in the comments, Marc-Andre uses the following pattern:
1: var r = /^(?:function|var)\s*([a-zA-Z_]+)/i;
He wants some vars (which he uses as constants) to be added to the namespace also, maybe I would add the 0-9 here also. anyway, I think it is a good suggestion to mention here.
Addition 2:
Steve Le Mon made a very good suggestion and tried out a few things, he found a way around the parsing of the functions and/or vars and adding them to the current namespace.
I tweaked his code a little bit and ended up with the following:
1: function InjectScript(scriptFile)
2: {
3: var netRequest = new ActiveXObject("Msxml2.XMLHTTP");
4: netRequest.open("GET", scriptFile, false);
5: netRequest.send(null);
6: eval(netRequest.responseText);
7: }
8:
9: InjectScript('/_customscript/customscript.js');
10:
11: //CallFunctionInExternalFile();
Listing 4
This technique removes the overhead of the parsing of the functions and vars so will perform faster.
Henry Cordes
My thoughts exactly...