Showing posts with label javascript. Show all posts
Showing posts with label javascript. Show all posts

Saturday, January 19, 2013

Dynamics CRM 2011 Open dialog from Javascript

There are many nice things you could do withe dialogs, but making the user to go and choose a dialog from the ribbon isn't really good UI and it doesn't make lot of sense.

Example of when I used the code.
Let say you want to help the user go throw the process of closing case, wouldn't be goo idea to replace the resolve case ribbon button with new ribbon that will open a dialog?

In this example I'm using CrmRestKit, It is required for the code to work, Please make sure you add it to the form.

Javascript:


var DIALOG_NAME = "Test Dialog";

CrmRestKit.ByQuery('Workflow', ['WorkflowId'], "Category/Value eq 1 and StateCode/Value eq 1 and Type/Value eq 1 and Name eq '" + DIALOG_NAME + "'", false)
            .fail(function (t) {
            alert(t)
        })
            .done(function (data, status, xhr) {
            // Assert
            //equals(data.d.results.length, 2, 'Expected two');          
            if (data.d.results.length < 1) {
                alert("Missing dialog named: '"+DIALOG_NAME+"'");
                return;
            }
            var dialogId = data.d.results[0].WorkflowId;
            var objectId = Xrm.Page.data.entity.getId().replace("{", "%7b").replace("}", "%7d");
            var url = Xrm.Page.context.prependOrgName("/cs/dialog/rundialog.aspx?DialogId=%7b" + dialogId + "%7d&EntityName="+ Xrm.Page.data.entity.getEntityName() +"&ObjectId=" + objectId);
            var returnValue = showModalDialog(url);
        });

Sunday, January 6, 2013

Dynamics CRM 2011 - Javascript - Getting selected records from subgrid

Getting the selected values from subgrid in form is not supported, which makes it little bit more tricky.
Lets say you need to get selected records from subgrid after the user got out of the subgrid, like after onchange event of some form attribute. In this case the focus moves out of the subgrid and all you'll get would be an empty list.

The trick to get the selected records is: Get the focus back on the subgrid before trying to get the values.
Anyway it is not supported as well.

My Sample code:

var subgridName = "testSubGrid"

function GetSelectedRecords() {
    var stringOfSelectedIds = "No records were selected";
    if (Xrm.Page.getControl(subgridName) != null) {
        document.getElementById(subgridName).getElementsByTagName('a')[0].focus();
    }
    //document.getElementById(subgridName).control.get_selectedIds()
    if (document.getElementById(subgridName) != null && document.getElementById(subgridName).control != null) {
        selectedRecods = document.getElementById(subgridName).control.get_selectedIds();
        if (selectedRecods != null && selectedRecods.length > 0) {
            stringOfSelectedIds = "Selceted records: " + selectedRecods.join();
        }
        alert(stringOfSelectedIds);
    }
}

GetSelectedRecords()

Sunday, December 16, 2012

Dynamics CRM 2011 - Preventing close alert

Dynamics CRM 2011 - Preventing close alert


In Dynamics CRM 4.0 it was easy to disable close alert by using this one line of code before you close the window:
crmForm.detachCloseAlert();

In Dynamics CRM 2011, this method is depracated. The way i workaround it, is setting all dirty fields submit mode to never.
function IsAttributeDirty(attribute, index) {    return attribute.getIsDirty();}
function SetAllDirtyFieldsToNeverSubmit(){ var attributes = Xrm.Page.data.entity.attributes.get(IsAttributeDirty); for (var i in attributes) { attributes[i].setSubmitMode("never"); }}//ends function RemoveAllDirtyFields(){
Close window with out fields change message:
SetAllDirtyFieldsToNeverSubmit();
Xrm.Page.ui.close();

Saturday, October 20, 2012

Dynamics CRM 2011 Form Script Analyzer

Dynamics CRM 2011 Form Script Analyzer

Few weeks ago i needed to get all the Javascript that is running in one of our customers Dynamics CRM 2011 Organization, I needed to get out all the Web Resource that are used, where are they used, what are the function that are running and on what are they running on (Onload, OnChange, OnSave, Ribbon buttons).
It was sisyphean work but after going over each Web Resource and their dependencies, I got the list out. This is what made me think, maybe I should build such a tool that takes it all out to an CSV file.

Yesterday, while surfing the web I've found the tool that does excetly that.

In TechEd New Zealand 2012 Gayan Perera presented a talk named:
Advanced Bag of Tips & Tricks for Microsoft Dynamics CRM 2011 Developers

I've enjoyed the presentation very much.
It was about:
T4 Code generation
MSCRM JS
Script#
How to convert managed solution to unmanaged solution
ILMerge
and Form Script Analyzer.

Back to the tool:
The Form Script Analyzer is Console Application tool that exports the Web resources and where their functions are called.

You can download it here : Download
For detailed description go to the TechEd Video and skip to 39 minutes (My recommention is to see the whole thing)

I haven't tried the tool yet, but it seems promising.

Sunday, September 2, 2012

Dynamics CRM 2011 setDefaultView and View Selector


Dynamics CRM 2011 filter lookup using javascript


I'm not going to write about how to filter look up using JavaScript, it is quite straight forward and can be found in the SDK http://msdn.microsoft.com/en-us/library/gg334266.aspx#BKMK_addCustomView
, Or in many more other blogs.
This post is about setDefaultView and when it is not working.

Apparently when Lookup field parameter "View Selector" is set to off setDefaultView is not working.



There is a trick (Unsupported) to
Set the attribute control "disableViewPicker" to false and back to true after the default view is set.

For example and more details read the form thread.

Thank you yogi 4.0 for asking the right question and finding the answer you've saved me time here.

Tuesday, July 31, 2012

MSCRM 4.0 Remove new record

Microsoft Dynamics CRM 4.0 Removing new record button from the standard Tool Bar.
It is unsupported!
1) Go to the web site server
2) Open {MSCRM folder}/_root/bar_Top.aspx with any text editor
3) Find first script tag
It should look like this:

<script type="text/javascript">

function DownloadClient()
{
var sUrl = "/_root/ClientInstaller.exe";
var oFrame = window.document.frames.frmDownloadOlk;
if(IsNull(oFrame))
{
oFrame = window.document.createElement("<iframe style='display:none' id='frmDownloadOlk' src='" + prependOrgName("/_root/Blank.aspx") + "'></iframe>");
oFrame = window.document.body.insertBefore(oFrame);

window.setTimeout("window.document.frames.frmDownloadOlk.location = \"" + sUrl + "\"", 10);
}
else
{
oFrame.location = sUrl;
}
}
function DisableAddinDownload() {
if (!IsNull(document.getElementById("btn_download_olk")))
{
document.getElementById("btn_download_olk").style.visibility = "hidden";
}
}

</script>
<!--[if MSCRMClient]>
<script  type="text/javascript">
window.attachEvent( "onload", DisableAddinDownload);
</script>

4) Add RemoveNewRecord to the script
function RemoveNewRecord(){
if (!IsNull(document.getElementById("mnu_new_record")))
{
document.getElementById("mnu_new_record").style.display = "none";
}
}

5) Add window.attachEvent( "onload", RemoveNewRecord); to the script

The script should look like this:
<script type="text/javascript">
function DownloadClient()
{
var sUrl = "/_root/ClientInstaller.exe";
var oFrame = window.document.frames.frmDownloadOlk;
if(IsNull(oFrame))
{
oFrame = window.document.createElement("<iframe style='display:none' id='frmDownloadOlk' src='" + prependOrgName("/_root/Blank.aspx") + "'></iframe>");
oFrame = window.document.body.insertBefore(oFrame);

window.setTimeout("window.document.frames.frmDownloadOlk.location = \"" + sUrl + "\"", 10);
}
else
{
oFrame.location = sUrl;
}
}
function DisableAddinDownload() {
if (!IsNull(document.getElementById("btn_download_olk")))
{
document.getElementById("btn_download_olk").style.visibility = "hidden";
}
}

function RemoveNewRecord(){
if (!IsNull(document.getElementById("mnu_new_record")))
{
document.getElementById("mnu_new_record").style.display = "none";
}
}
</script>
<!--[if MSCRMClient]>
<script  type="text/javascript">
window.attachEvent( "onload", DisableAddinDownload);
window.attachEvent( "onload", RemoveNewRecord);
</script>

Save and open your MSCRM.

The result should look like:

Thanks for
http://blogs.msdn.com/b/rextang/archive/2008/08/28/8900674.aspx

Wednesday, July 4, 2012

Microsoft Dynamics CRM 2011 Validate required form javascript

Microsoft Dynamics CRM 2011 Validate required form javascript.

This code checks if form is valid for saving, by going over all required attributes and checking if it contains value.


function IsFormValidForSaving(){
var valid = true;
Xrm.Page.data.entity.attributes.forEach(function (attribute, index) {
   if (attribute.getRequiredLevel() == "required") {
      if(attribute.getValue() == null){
          if(valid){
              var control = attribute.controls.get(0);
              alert(control.getLabel() + " is missing a value");
              control.setFocus();
          }
          valid = false; 
      }
   }
});
return valid;
}
Another option is using crm system function but then it won't be supported.

Monday, June 25, 2012

Microsoft Dynamics CRM 2011 Running javascript on IE F12 developer tools

Microsoft Dynamics CRM 2011 Running javascript on IE F12 developer tool

This way it is possible to check javascript in run time on the form.

1) Open some form in the crm.
2) Press F12 -> Open the IE F12 developer tools
3) Open script tab
3) type
Xrm = document.frames["contentIFrame"].Xrm; 
Or
Xrm = frames[0].Xrm
4) Run your code

Friday, June 15, 2012

Microsoft Dynamics CRM 2011 Create new case from account's homepage ribbon


Microsoft Dynamics CRM 2011 Create new case from account's homepage ribbon Or cut the clicks

This is a how to create new case from account grid using ribbon button, just like if you would create the case from case sub grid in account form. It gets all the attributes mapping from related account.
The idea behind it was to cut the time for creating new case, Instead of going to account homepage, opening the account form, click on case sub grid and then create the case, It will be just go to account homepage choose the account and create new case.
This is a real time saver.


1) Create new solution.
2) Add new web resource type JavaScript

var CASE_URL = "/main.aspx?etc=112&extraqs=%3f_CreateFromId%3d%257b{ACCOUNTID}%257d%26_CreateFromType%3d1&pagetype=entityrecord";
CreateNewCaseFromAccount = function(id)
{
                        //get accountid
                        var nakedId = id.replace("{", "").replace("}", "");
                        var caseUrl = CASE_URL.replace("{ACCOUNTID}", nakedId);
                        var caseUrl = Xrm.Page.context.prependOrgName(caseUrl);
                        window.open(caseUrl);
}
3) Add button to account's homepage ribbon to call CreateNewCaseFromAccount function. Two ways for adding the button to account.
1) Use Ribbon workbench.
2) The old fashion way, do it yourself.
a. Add account to solution.
b. Export solution.
c. Extract the solution file.
d. Open customizations.xml using your favorite notepad
e.Add the button to the RibbonDiffXml:

  <RibbonDiffXml>
        <CustomActions>
          <CustomAction Id="HomepageGrid.account.MainTab.Actions.CreateNewCase.CustomAction" Location="Mscrm.HomepageGrid.account.MainTab.Actions.Controls._children" Sequence="41">
            <CommandUIDefinition>
              <Button Id="HomepageGrid.account.MainTab.Actions.CreateNewCase" Command="HomepageGrid.account.MainTab.Actions.CreateNewCase.Command" Sequence="20" ToolTipTitle="Create New Case" LabelText="Create New Case" ToolTipDescription="Create New Case" TemplateAlias="o1" />
            </CommandUIDefinition>
          </CustomAction>
        </CustomActions>
        <Templates>
          <RibbonTemplates Id="Mscrm.Templates"></RibbonTemplates>
        </Templates>
        <CommandDefinitions>
          <CommandDefinition Id="HomepageGrid.account.MainTab.Actions.CreateNewCase.Command">
            <EnableRules>
              <EnableRule Id="HomepageGrid.account.MainTab.Actions.CreateNewCase.Command.EnableRule.SelectionCountRule" />
            </EnableRules>
            <DisplayRules />
            <Actions>
              <JavaScriptFunction FunctionName="CreateNewCaseFromAccount" Library="$Webresource:new_/CreateNewCase/CreateNewCase.js">
                <CrmParameter Value="FirstSelectedItemId" />
              </JavaScriptFunction>
            </Actions>
          </CommandDefinition>
        </CommandDefinitions>
        <RuleDefinitions>
          <TabDisplayRules />
          <DisplayRules />
          <EnableRules>
            <EnableRule Id="HomepageGrid.account.MainTab.Actions.CreateNewCase.Command.EnableRule.SelectionCountRule">
              <SelectionCountRule AppliesTo="SelectedEntity" Maximum="1" Minimum="1" Default="true" />
            </EnableRule>
          </EnableRules>
        </RuleDefinitions>
        <LocLabels />
      </RibbonDiffXml>

f. Import the solution back and publish.


Good luck.

This post was written before Dynamics CRM 2011 RU 8 and the new Xrm.utility, It changes only the way of opening new form the basic idea of this post stays the same.
For more information about Xrm.utility go to
http://jonasrapp.cinteros.se/2012/08/xrmutility-methods-in-ms-dynamics-crm.html
or many other posts that explains the new functions available.
 

Thursday, June 14, 2012

Dynamics CRM 2011 readonly form javascript


Dynamics CRM 2011 read-only form JavaScript.

With it does:
1)     Hides the content of a form with transparent div.
Pros:
1)     Easier way to disable all the controls of the form and enabling them while keeping all the logic of the form (pre disabled controls will keep be disabled).
2)     It's faster than going over all the controls and disabling them.
Cons:
1)     Partially Unsupported

How to guide:
1)     Create new JavaScript web resource
2)     Add this code:
            //Disable all form controls
            DisableForm = function() {
                        var iframeDoc = document;
                        var iframeBody = document.getElementById("crmFormTabContainer");
                        hideContent(iframeDoc, iframeBody);
                        Xrm.Page.ui.tabs.forEach(
                                    function(tab, index)
                                    {
                                                Xrm.Page.ui.tabs.get(index).add_tabStateChange(function()
                                                {
                                                            var iframeDoc = document;
                                                            var iframeBody = document.getElementById("crmFormTabContainer");
                                                            var roSpan = document.getElementById("readonlySpan");
                                                            if (roSpan != null) {
                                                                        iframeBody.removeChild(roSpan);
                                                            }
                                                            hideContent(iframeDoc, iframeBody);
                                                });
                                    }
                        );
            }

            hideContent = function(htmlDocument, htmlElement) {
                        var roSpan = htmlDocument.createElement("div");
                        roSpan.id = "readonlySpan";
                       
                        var roStyle = "position:absolute;";
                        roStyle += "z-index:1;";
                        roStyle += "left:0px;";
                        roStyle += "top:0px;";
                        roStyle += "height:"+htmlElement.scrollHeight+"px;";
                        roStyle += "text-align:center;";
                        roStyle += "font:72px Tahoma;";
                        roStyle += "opacity:0.20;";
                        roStyle += "-moz-opacity:0.20;";
                        roStyle += '-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=20)";';
                        roStyle += 'filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=20)"';
                        roStyle += "filter: alpha(opacity=20);";
                        roStyle += "background-color:white;";

                        roSpan.style.cssText = roStyle;
                        htmlElement.appendChild(roSpan);
                        roSpan.innerHTML = "<div style='margin-top:30%;height:100px;'>READ ONLY</div>";
                        roSpan.style.zoom = 1;
            }
           
            //Enable all form control keep readonly / disable logic.
            EnableForm = function() {
                        var iframeBody = document.getElementById("crmFormTabContainer");
                        var roSpan = document.getElementById("readonlySpan");
                        if (roSpan != null) {
                                    iframeBody.removeChild(roSpan);
                        }
            }
3)     Add DisableForm function to onload event of the form.

**Another way to disable all the controls

Good luck.


Saturday, February 11, 2012

MSCRM 2011 javascript validate lead qulify javascript

Sometimes validation of fields is needed before qualifying a lead.
Setting the fields to be required isn't good enough, because there is no need to require the fields for every save.
It can be done using javascript on form save event.

Check if the save mode equals 16 (lead qualify) by using

ExecutionObj.getEventArgs().getSaveMode()

Use prevent default to prevent the page from saving and qualifying.
ExecutionObj.getEventArgs().preventDefault()


Microsoft link about preventDefault
http://msdn.microsoft.com/en-us/library/gg509060.aspx#BKMK_preventDefault

Microsoft link about getsavemode
http://msdn.microsoft.com/en-us/library/gg509060.aspx#BKMK_GetSaveMode

Good wiki that extends more about getsavemode and preventDefault
http://social.technet.microsoft.com/wiki/contents/articles/4122.aspx


Monday, January 9, 2012

MSCRM change record owner (Assign) Javascript


MSCRM change record owner (Assign).

How to add JavaScript to owner change:
On create form - It's not an assign event yet, just had OnChange event to the owner's control on the form and the code will run.
On update form - This is trickier, because every time the owner changes the OnChange event won't fire and instead thee save event is fired on the form. In order to catch it with JavaScript add code to the OnSave event.
var SAVED_MODE_ASSIGN = 47;
function onSave(ExecutionObj)
{
     if(ExecutionObj.getEventArgs().getSaveMode() == SAVED_MODE_ASSIGN)
    {
           //Do something after record assigned
    }
}



The good and the bad of how to tackle the problem:
JavaScript:
The same way I've just presented.
The Good: Easy to add code (JS), The users will see the results straight a way.
The Bad: it won't run when user assigning owner from the grid, youy needs to know JavaScript.

Proccess - Workflow
Create new Process and set it to fire on "Record is assigned".
It's async and the user will see the results of the process only after few seconds.
The Good: Easy to implement, the process will work after all assign event no matter where the user perform them.
The Bad: The user will see the result only after few seconds and it's limited to process capabilities.

Plugins
Create new plugin on event - Assign.
Write done the logic or get someone do to it for you.
The Good: Works any way you want it to and in any condition you want.
The Bad: You'll needs to know how to write plugins. Takes a bit more time (It's done outside the CRM)

Sunday, January 1, 2012

MSCRM 2011 depended optionset

There is no need to create something that is already there.
There is a very good (generic) solution for depended optionset (picklist) in the SDK.

The problem is it's technical solution, That's mean you'll probably need developer next to you when you do it the first time. 

Maybe I'll create GUI for it one day. :)

Tuesday, December 13, 2011

Using tinyMCE in Dynamics CRM 2011

Using tinyMCE in Dynamics CRM 2011

Based on htmlbox - dynamics crm 2011 integretion described in here:

https://community.dynamics.com/product/crm/crmnontechnical/b/crmsoftwareblog/archive/2011/06/27/adding-wysiwyg-editor-to-textarea-in-microsoft-dynamics-crm-part-2-crm-2011.aspx

There are other tinyMCE - Dynamics crm 2011 integrations on the web for exmaple:
http://procentrix.com/Community/BrendenS/post.aspx?ID=13

IMPORTANT: This solution is unsupported.

Dynamics crm TinyMCE

Walkthrough:
  1. Get the minified Jquery from
    http://jquery.com/
  2. Get the tinyMCE
    http://www.tinymce.com/download/download.php
  3. Use the web resource util as described here
    http://blog.customereffective.com/blog/2011/06/using-crm-2011-web-resource-utility.html
  4. Add the jquery and tinymce to the entity form
  5. Create new web resource with the following code and add it to the form
    
    var textAreaName = "new_description";
    
    function onLoad(textarea)
    {
     textAreaName = textarea;
     Xrm.Page.getAttribute(textAreaName).setSubmitMode("always");
     //Xrm.Page.getControl("new_description").setVisible(false);
     //$('#new_description').css("width", "90%").css("height", "10%");
     $('#' + textAreaName + '_d').append('<textarea id="wysiwyg" name="wysiwyg" style="width:90%;height:90%;">' + $("#" + textAreaName).val() + '</textarea>');
     $('#' + textAreaName).hide();
    
     tinyMCE.init({
      mode : "exact",
      elements : "wysiwyg",
      theme : "advanced",
      plugins : "autolink,lists,pagebreak,style,layer,table,save,advhr,advimage,advlink,emotions,iespell,insertdatetime,preview,media,searchreplace,print,contextmenu,paste,directionality,fullscreen,noneditable,visualchars,nonbreaking,xhtmlxtras,template,inlinepopups,autosave",
      theme_advanced_buttons1 : "newdocument,|,bold,italic,underline,|,justifyleft,justifycenter,justifyright,fontselect,fontsizeselect,formatselect",
      theme_advanced_buttons2 : "cut,copy,paste,|,bullist,numlist,|,outdent,indent,|,undo,redo,|,link,unlink,anchor,image,|,code,preview,|,forecolor,backcolor",
      theme_advanced_buttons3 : "insertdate,inserttime,|,spellchecker,advhr,,removeformat,|,sub,sup,|,charmap,emotions", 
      theme_advanced_toolbar_location : "top",
      theme_advanced_toolbar_align : "left",
      theme_advanced_statusbar_location : "bottom",
      theme_advanced_resizing : true,
      onchange_callback : "myCustomOnChangeHandler"
     });
    }
    
    function myCustomOnChangeHandler(inst) {
      if(inst.isDirty()) {
       inst.save();
      }
      Xrm.Page.getAttribute(textAreaName).setValue($('#wysiwyg').val());
      return true;
    }
    
    function onSave()
    {
     for (var i=0; i<tinymce.editors.length; i++) {
      tinyMCE.execCommand('mceRemoveControl',false, tinymce.editors[i].id); 
     };
     $('#wysiwyg').remove();
    }
    
  6. Add new onload event: function name: onLoad Parameter: "the attribute schema name"
  7. Add new onsave event: function name: onSave 
  8. Save and publish...


You can download an example here (Replaces the entity task!):
unmanaged solution:
managed solution:


Monday, November 28, 2011

Dynamics crm 2011 n to n lookup

N:N lookup in Dynamics CRM 2011.

For to 4.0 version: http://dynamicslollipops.blogspot.com/2011/07/mscrm-40-nn-lookup-field.html

Prerequest customization:
1) 1:N relation, and lookup placed on the form.
2) N:N relationship for holding the values.

Walkthrough:
1) Create new javascript webresource with the following code:

var CRM_FORM_TYPE_CREATE = 1;

RetreiveAssociatedEntities = function(relationshipSchemaName, entity1SchemaName, entity1KeyValue, retreiveAttribute) {
    var fetchXml = "<fetch mapping='logical'>"
    + "  <entity name='" + relationshipSchemaName + "'>"
    + "    <all-attributes />"
    + "    <filter>"
    + "      <condition attribute='" + entity1SchemaName + "id' operator='eq' value ='" + entity1KeyValue + "' />"
    + "    </filter>"
    + "  </entity>"
    + "</fetch>";

    var fetchResults = Fetch(fetchXml);

    var nodeList = fetchResults.selectNodes("resultset/result");

    var returnList = new Array();
    if (nodeList == null || nodeList.length == 0) {
        return returnList;
    } else {
        for (i = 0; i < nodeList.length; i++) {
            var idValue = nodeList[i].selectSingleNode('./' + retreiveAttribute).nodeTypedValue;
            returnList[i] = idValue;
        }
        return returnList;
    }
}
MischiefMayhemSOAP = function(serviceUrl, xmlSoapBody, soapActionHeader, suppressError) {
    var xmlReq = "<?xml version='1.0' encoding='utf-8'?>"
    + "<soap:Envelope xmlns:soap='http://schemas.xmlsoap.org/soap/envelope/'"
    + "  xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'"
    + "  xmlns:xsd='http://www.w3.org/2001/XMLSchema'>"
    + GenerateAuthenticationHeader()
    + "  <soap:Body>"
    + xmlSoapBody
    + "  </soap:Body>"
    + "</soap:Envelope>";

    var httpObj = new ActiveXObject("Msxml2.XMLHTTP");

    httpObj.open("POST", serviceUrl, false);

    httpObj.setRequestHeader("SOAPAction", soapActionHeader);
    httpObj.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
    httpObj.setRequestHeader("Content-Length", xmlReq.length);

    httpObj.send(xmlReq);

    var resultXml = httpObj.responseXML;

    var errorCount = resultXml.selectNodes("//error").length;
    if (errorCount != 0) {
        var msg = resultXml.selectSingleNode("//description").nodeTypedValue;

        if (typeof (suppressError) == "undefined" || suppressError == null) {
            alert("The following error was encountered: " + msg);
        }

        return null;
    } else {
        return resultXml;
    }
}

Fetch = function(fetchXml) {
    var xmlSoapBody = "<Fetch xmlns='http://schemas.microsoft.com/crm/2007/WebServices'>"
    + "  <fetchXml>"
    + FetchEncode(fetchXml)
    + "  </fetchXml>"
    + "</Fetch>";

    var fetchResponse = MischiefMayhemSOAP("/MSCRMServices/2007/CrmService.asmx", xmlSoapBody, "http://schemas.microsoft.com/crm/2007/WebServices/Fetch");

    if (fetchResponse != null) {
        var fetchResults = new ActiveXObject("Msxml2.DOMDocument");

        fetchResults.async = false;
        fetchResults.resolveExternals = false;
        fetchResults.loadXML(fetchResponse.text);

        return fetchResults;
    } else {
        return null;
    }
}
FetchEncode = function(strInput) //_HtmlEncode
{
    var c;
    var HtmlEncode = '';

    if (strInput == null) {
        return null;
    }
    if (strInput == '') {
        return '';
    }

    for (var cnt = 0; cnt < strInput.length; cnt++) {
        c = strInput.charCodeAt(cnt);

        if (((c > 96) && (c < 123)) ||
  ((c > 64) && (c < 91)) ||
  (c == 32) ||
  ((c > 47) && (c < 58)) ||
  (c == 46) ||
  (c == 44) ||
  (c == 45) ||
  (c == 95)) {
            HtmlEncode = HtmlEncode + String.fromCharCode(c);
        }
        else {
            HtmlEncode = HtmlEncode + '&#' + c + ';';
        }
    }

    return HtmlEncode;
}

AssociateEntities = function(moniker1name, moniker1id, moniker2name, moniker2id, RelationshipName) {
    var authenticationHeader = GenerateAuthenticationHeader();
    // Prepare the SOAP message.
    var xml = "<?xml version='1.0' encoding='utf-8'?>";
    xml += "<soap:Envelope xmlns:soap='http://schemas.xmlsoap.org/soap/envelope/' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance\' xmlns:xsd='http://www.w3.org/2001/XMLSchema\'>";
    xml += authenticationHeader;
    xml += "<soap:Body><Execute xmlns='http://schemas.microsoft.com/crm/2007/WebServices'><Request xsi:type='AssociateEntitiesRequest'>";
    xml += "<Moniker1><Id xmlns='http://schemas.microsoft.com/crm/2006/CoreTypes'>" + moniker1id + "</Id>";
    xml += "<Name xmlns='http://schemas.microsoft.com/crm/2006/CoreTypes'>" + moniker1name + "</Name></Moniker1>";
    xml += "<Moniker2><Id xmlns='http://schemas.microsoft.com/crm/2006/CoreTypes'>" + moniker2id + "</Id>";
    xml += "<Name xmlns='http://schemas.microsoft.com/crm/2006/CoreTypes'>" + moniker2name + "</Name></Moniker2>";
    xml += "<RelationshipName>" + RelationshipName + "</RelationshipName>";
    xml += "</Request></Execute></soap:Body></soap:Envelope>";

    // Prepare the xmlHttpObject and send the request.
    var xHReq = new ActiveXObject("Msxml2.XMLHTTP");
    xHReq.Open("POST", "/mscrmservices/2007/CrmService.asmx", false);
    xHReq.setRequestHeader("SOAPAction", "http://schemas.microsoft.com/crm/2007/WebServices/Execute");
    xHReq.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
    xHReq.setRequestHeader("Content-Length", xml.length);
    xHReq.send(xml);

    // Capture the result.
    var resultXml = xHReq.responseXML;

    // Check for errors.
    var errorCount = resultXml.selectNodes('//error').length;

    if (errorCount != 0) {
        var msg = resultXml.selectSingleNode('//description').nodeTypedValue;
        alert(msg);
    }
}

DisassociateEntities = function(moniker1name, moniker1id, moniker2name, moniker2id, RelationshipName) {
    var authenticationHeader = GenerateAuthenticationHeader();
    // Prepare the SOAP message.
    var xml = "<?xml version='1.0' encoding='utf-8'?>";
    xml += "<soap:Envelope xmlns:soap='http://schemas.xmlsoap.org/soap/envelope/' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance\' xmlns:xsd='http://www.w3.org/2001/XMLSchema\'>";
    xml += authenticationHeader;
    xml += "<soap:Body><Execute xmlns='http://schemas.microsoft.com/crm/2007/WebServices'><Request xsi:type='DisassociateEntitiesRequest'>";
    xml += "<Moniker1><Id xmlns='http://schemas.microsoft.com/crm/2006/CoreTypes'>" + moniker1id + "</Id>";
    xml += "<Name xmlns='http://schemas.microsoft.com/crm/2006/CoreTypes'>" + moniker1name + "</Name></Moniker1>";
    xml += "<Moniker2><Id xmlns='http://schemas.microsoft.com/crm/2006/CoreTypes'>" + moniker2id + "</Id>";
    xml += "<Name xmlns='http://schemas.microsoft.com/crm/2006/CoreTypes'>" + moniker2name + "</Name></Moniker2>";
    xml += "<RelationshipName>" + RelationshipName + "</RelationshipName>";
    xml += "</Request></Execute></soap:Body></soap:Envelope>";

    // Prepare the xmlHttpObject and send the request.
    var xHReq = new ActiveXObject("Msxml2.XMLHTTP");
    xHReq.Open("POST", "/mscrmservices/2007/CrmService.asmx", false);
    xHReq.setRequestHeader("SOAPAction", "http://schemas.microsoft.com/crm/2007/WebServices/Execute");
    xHReq.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
    xHReq.setRequestHeader("Content-Length", xml.length);
    xHReq.send(xml);

    // Capture the result.
    var resultXml = xHReq.responseXML;

    // Check for errors.
    var errorCount = resultXml.selectNodes('//error').length;

    if (errorCount != 0) {
        var msg = resultXml.selectSingleNode('//description').nodeTypedValue;
        alert(msg);
    }
}

//returns the item location in array if not found -1
GetIndexFromArray = function(item, recordArr) {
    for (var i = 0; i < recordArr.length; i++) {
        if (recordArr[i] != null && recordArr[i].id == item) {
            return i;
        }
    }
    return -1;
}

/*"new_new_entity1_new_entity2new","new_multynewid", "new_entity2", "new_name"*/
FillMultiLookup = function(relationshipSchemaName, lookupSchemaName, relatedEntitySchemaName, relatedEntityPrimaryAttributeSchemaName) {
    var relatedValues = RetreiveAssociatedEntities(relationshipSchemaName, Xrm.Page.data.entity.getEntityName(), Xrm.Page.data.entity.getId(), relatedEntitySchemaName + "id");
    var value = new Array();
    for (var i = 0; i < relatedValues.length; i++) {
        value[i] = new Object();
        value[i].id = relatedValues[i];
        value[i].name = RetreiveAssociatedEntities(relatedEntitySchemaName, relatedEntitySchemaName, relatedValues[i], relatedEntityPrimaryAttributeSchemaName)[0];
        value[i].typename = relatedEntitySchemaName;
    }
 crmForm.all[lookupSchemaName].DataValue = value;
}

UpdateN2N = function(nnId, relatedEntitySchemaName, relatedEntitySchemaId, lookupSchemaName) {
    var oldValues = RetreiveAssociatedEntities(nnId, Xrm.Page.data.entity.getEntityName(), Xrm.Page.data.entity.getId(), relatedEntitySchemaId);
 var value = crmForm.all[lookupSchemaName].DataValue;
    //if there's records in lookup
    if (value != null) {
        //go over all the related records and remove them if not in the new list (lookup)
        var temp = value;
        for (var i = 0; i < oldValues.length; i++) {
            //if not in the new list disassociate them
            var index = GetIndexFromArray(oldValues[i], temp);
            if (index == -1) {
                DisassociateEntities(Xrm.Page.data.entity.getEntityName(), Xrm.Page.data.entity.getId(), relatedEntitySchemaName, oldValues[i], nnId);
            }
            else { // if in the list remove them from the list
                temp[index] = null;
            }
        } //ends for
        //go over all the remaining records and associate them
        for (var i = 0; i < temp.length; i++) {
            if (temp[i] != null) {
                AssociateEntities(Xrm.Page.data.entity.getEntityName(), Xrm.Page.data.entity.getId(), relatedEntitySchemaName, temp[i].id, nnId);
            }
        }
    }
    else if (oldValues != null) {
        for (var i = 0; i < oldValues.length; i++) {
            DisassociateEntities(Xrm.Page.data.entity.getEntityName(), Xrm.Page.data.entity.getId(), relatedEntitySchemaName, oldValues[i], nnId);
        }
    }
}

FilterN2NLookup = function(filterByLookup, filterLookup, entityName, nnName, nnFrom, linkedEntityName, linkedAttributeName) {
    var filter = '';
    var values = filterByLookup.DataValue;
    if (values != null) {
        //filter = '<filters><filter entity="' + entityName + '"><condition attribute="' + attributeName + '" operator="in">';
        //for (var i = 0; i < values.length; i++) {
        //    filter += '<value uiname="' + values[i].name + '">' + values[i].id + '</value>';
        // }
        //filter += '</condition></filter></filters>';

        filter = '<link-entity entity="' + entityName + '" name="' + nnName + '" from="' + nnFrom + '" to="' + nnFrom + '" visible="false" intersect="true"><link-entity name="' + linkedEntityName + '" from="' + linkedAttributeName + '" to="' + linkedAttributeName + '"><filter type="and"><condition attribute="' + linkedAttributeName + '" operator="in">';
        for (var i = 0; i < values.length; i++) {
            filter += '<value uiname="' + values[i].name + '">' + values[i].id + '</value>';
        }
        filter += '</condition></filter></link-entity></link-entity>';

    }
    filterLookup.AddParam('filters', filter);
}

/*"new_new_entity1_new_entity2new","new_multynewid", "new_entity2", "new_name"*/
function ConvertN2N(relationshipSchemaName, lookupSchemaName, relatedEntitySchemaName, relatedEntityPrimaryAttributeSchemaName)
{
 document.getElementById(lookupSchemaName).setAttribute("lookupstyle", "multi");
 document.getElementById(lookupSchemaName).setAttribute("_lookupstyle", "multi");

 document.getElementById(lookupSchemaName).onchange = function(){UpdateN2N(relationshipSchemaName, relatedEntitySchemaName, relatedEntitySchemaName + "id", lookupSchemaName)};

 Xrm.Page.getAttribute(lookupSchemaName).setSubmitMode("never");

 if (crmForm.FormType != CRM_FORM_TYPE_CREATE) {
  FillMultiLookup(relationshipSchemaName, lookupSchemaName, relatedEntitySchemaName, relatedEntityPrimaryAttributeSchemaName);
 }
 else {
  Xrm.Page.getControl(lookupSchemaName).setDisabled(true);
 }
}

2) Add it to the form
3) Add to the onload 
   3.1) Function: ConvertN2N
   3.2) Parameters: N:N Relationship name, lookup attribute name, Related Entity Schema Name, Related Entity Primary Attribute Schema Name
(Example: "new_incident_new_problem", "new_problemid", "new_problem", "new_name")



That's it, everything else is inside the code. 
Plug and Play.