Showing posts with label JavaScript. Show all posts
Showing posts with label JavaScript. Show all posts

Get UnCached version form Ajax file request

While loading external javascript there is problem of caching the general solutionfor this is appending a url parameter like "?rnd=Date.Now()" at the end of the url, so that the url is always unique while making the call. The other way around is to set the IIS ouput caching with FIle change notifications. But the same could be achieve without much effort by setting the response header. We can set the response header to get the modified version from the server instead of the cached one using the following script:
 

/* Ajax with Un Cached output in MSCRM 4 */
function _LoadIsvScript(execute) {
var JsScriptUrl = "/ISV/customscript.js";
var strServerURL = SERVER_URL.split(ORG_UNIQUE_NAME);
var script_uri = strServerURL[0] + JsScriptUrl;
var ajax = new ActiveXObject("Msxml2.XMLHTTP");
ajax.open('GET', script_uri, false);
//GetUnCachedVersion
ajax.setRequestHeader("If-Modified-Since", "Sat, 01 Jan 2000 00:00:00 GMT");
ajax.setRequestHeader('Cache-Control', 'no-cache');
ajax.send('');
return ajax.responseText;
}


Converting a JavaScript Date object into the proper XML string


function SwConvertDateTimeToXml(dateTime) {

var offset = (ORG_TIMEZONE_OFFSET < 0) ? -ORG_TIMEZONE_OFFSET : ORG_TIMEZONE_OFFSET;
var timezoneOffsetHours = Math.floor(offset / 60);
var timezoneOffsetMinutes = offset - timezoneOffsetHours * 60;

var s =
dateTime.getYear().toString() + "-" +
SwGetFormattedDatePart(dateTime.getMonth() + 1) + "-" +
SwGetFormattedDatePart(dateTime.getDate()) + "T" +
SwGetFormattedDatePart(dateTime.getHours()) + ":" +
SwGetFormattedDatePart(dateTime.getMinutes()) + ":" +
SwGetFormattedDatePart(dateTime.getSeconds()) +
((ORG_TIMEZONE_OFFSET < 0) ? "-" : "+") +
SwGetFormattedDatePart(timezoneOffsetHours) + ":" +
SwGetFormattedDatePart(timezoneOffsetMinutes);

return s;
}

function SwGetFormattedDatePart(value) {
return (value < 10) ? ("0" + value.toString()) : value.toString();
}

Converting an XML date string to a JavaScript Date object


function SwConvertXmlToDateTime(crmDateTimeString) {
var dateTimeParts = crmDateTimeString.split("T");
var dateString = dateTimeParts[0];
var timeString = dateTimeParts[1];
var dateParts = dateString.split("-");
var timeZoneSeparator = (timeString.indexOf("-") != -1) ? "-" : "+";
var timeZoneParts = timeString.split(timeZoneSeparator);
var timeParts = timeZoneParts[0].split(":");

var date = new Date(SwParseInt(dateParts[0]), SwParseInt(dateParts[1]) - 1, SwParseInt(dateParts[2]), SwParseInt(timeParts[0]), SwParseInt(timeParts[1]), SwParseInt(timeParts[2]));
return date;
}

function SwParseInt(value) {
if ((value.length == 2) && (value.substr(0, 1) == "0")) {
value = value.substr(1, 1);
}

return parseInt(value);
}

Reusing code in OnLoad and OnChange event handlers

Somtimes we need to execute the same set of twice once in OnLoad and in OnChange which calls for maintaining two set of codes or copying the same code for other. Again if there is some change in the first one we need to change the second one also.

Here is a simple implementation to avoid it. We will make use of the HTML DOM and include a function it, this function lives until the window is open accessable form all over the form.



var Hello = Function(){
alert("Say hello");
};


// now you can use somthng like this
window.Greet = Hello;

//or

window.Greet = function(){ alert("say hello"); };


To use it you can call this function from any location, like

// calling the function
window.Greet();

Logging JScript Errors to windows event log

Writing Jscript for Dynamics CRM is a tough task, there were no errors is the data is in proper format, but somtimes i wish to log all those information & exception in some place to review/analyse. But since Jscript is not meant for this. Still there is something that you can do with Windows Event Logger. Here is a small script that uses activeXObject to write the log to eventviewer. Later you can view the log by running "eventvwer" in the cmd prompt.

Here is the script



// Creates a log object
Log.prototype = {
// adds functions to it
Err:function(Error){
var WshShell = new ActiveXObject("WScript.shell");
WshShell.LogEvent(1,Error);
},
Success:function(Message){
var WshShell = new ActiveXObject("WScript.shell");
WshShell.LogEvent(0,Message);
},
Warn:function(Warning){
var WshShell = new ActiveXObject("WScript.shell");
WshShell.LogEvent(2,Message);
},
Info:function(Information){
var WshShell = new ActiveXObject("WScript.shell");
WshShell.LogEvent(4,Information);
},
AuditSuccess:function(Message){
var WshShell = new ActiveXObject("WScript.shell");
WshShell.LogEvent(8,Message);
},
AuditFailure:function(Message){
var WshShell = new ActiveXObject("WScript.shell");
WshShell.LogEvent(16,Message);
}
}

// Calling the log to write error
Log.Err("error in script");

Formatting international phone numbers

The CRM SDK contains a sample to format US phone numbers and it works pretty well. However, there are customers outside the US and the sample doesn't work with international phone numbers. An easy formatting rule is replacing any occurrence of '(', ')' or a space with a dash. People can then enter the phone number in their preferred way, but get the same output.


var originalPhoneNumber = "+49 (89) 12345678";
var formattedPhoneNumber = originalPhoneNumber.replace(/[^0-9,+]/g, "-");
formattedPhoneNumber = formattedPhoneNumber.replace(/-+/g, "-");
alert(formattedPhoneNumber);


The first call to the replace method changes every character in the input string that is not a digit and not the plus sign (which is used for international
numbers) to the dash symbol. However, the output is +49--89--12345678, so the second call replaces all occurrences of multiple dashes with a single
one, giving a final result of +49-89-12345678.

Removing a navigation bar entry at runtime

To remove a navigation bar entry dynamically, you can use the following code:

var navigationBarEntry = document.getElementById("navProds");

if (navigationBarEntry != null) {
var lbArea = navigationBarEntry.parentNode;
if (lbArea != null) {
lbArea.removeChild(navigationBarEntry);
}
}

If you haven't already done it, download and install the Internet Explorer Developer Toolbar to find the name of the navigation bar entry.

Retrieving the current user information

The new solution uses the Microsoft CRM web services to retrieve the user id, business unit id, organization id and the first, last and full name of the currently logged on user. I have attached a simple test form with the following OnLoad event:


var xml = "" +
"" +
"" +
GenerateAuthenticationHeader() +
" " +
" " +
" " +
" systemuser" +
" " +
" " +
" businessunitid" +
" firstname" +
" fullname" +
" lastname" +
" organizationid" +
" systemuserid" +
"
" +
"
" +
" false" +
" " +
" And" +
" " +
" " +
" systemuserid" +
" EqualUserId" +
"
" +
"
" +
"
" +
"
" +
"
" +
"
" +
"
" +
"";

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

xmlHttpRequest.Open("POST", "/mscrmservices/2007/CrmService.asmx", false);
xmlHttpRequest.setRequestHeader("SOAPAction", "http://schemas.microsoft.com/crm/2007/WebServices/RetrieveMultiple");
xmlHttpRequest.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
xmlHttpRequest.setRequestHeader("Content-Length", xml.length);
xmlHttpRequest.send(xml);

var resultXml = xmlHttpRequest.responseXML;
var entityNode = resultXml.selectSingleNode("//RetrieveMultipleResult/BusinessEntities/BusinessEntity");

var firstNameNode = entityNode.selectSingleNode("q1:firstname");
var lastNameNode = entityNode.selectSingleNode("q1:lastname");
var fullNameNode = entityNode.selectSingleNode("q1:fullname");
var systemUserIdNode = entityNode.selectSingleNode("q1:systemuserid");
var businessUnitIdNode = entityNode.selectSingleNode("q1:businessunitid");
var organizationIdNode = entityNode.selectSingleNode("q1:organizationid");

crmForm.all.sw_firstname.DataValue = (firstNameNode == null) ? null : firstNameNode.text;
crmForm.all.sw_lastname.DataValue = (lastNameNode == null) ? null : lastNameNode.text;
crmForm.all.sw_name.DataValue = (fullNameNode == null) ? null : fullNameNode.text;
crmForm.all.sw_systemuserid.DataValue = (systemUserIdNode == null) ? null : systemUserIdNode.text;
crmForm.all.sw_businessunitid.DataValue = (businessUnitIdNode == null) ? null : businessUnitIdNode.text;
crmForm.all.sw_organizationid.DataValue = (organizationIdNode == null) ? null : organizationIdNode.text;

parsing Xml Response


// Save all entity nodes in an array. Each result is returned in a BusinessEntity node.
// All BusinessEntity nodes are contained in a single BusinessEntities node.
// The BusinessEntities node in contained in a RetrieveMultipleResult node
// You could also use the XPath //BusinessEntities/BusinessEntity or //BusinessEntity
// "//" tells the XML parser to find all occurrences in the document starting with the
// supplied path, so it would find a/b/c/BusinessEntity as well as x/BusinessEntity.
var entityNodes = resultXml.selectNodes("//RetrieveMultipleResult/BusinessEntities/BusinessEntity");

// Loop through the collection of returned entities.
// Note that the query above limits the result to a single entity, so you will only find one
// node. To be more specific, it could be 0 nodes as well, if you don't have access to accounts
// or your system is empty.
for (var i = 0; i < entityNodes.length; i++) {

// Access the current array element
var entityNode = entityNodes[i];

// Attributes are child nodes of the BusinessEntity node. Use selectSingleNode to return
// the first occurrence of a named node. The selectNodes method we used before returns all
// matching nodes, but as an attribute name only occurs once, selectSingleNode is easier to use.
// Use the same namespace alias (q1) you have specified in the query.
var accountidNode = entityNode.selectSingleNode("q1:accountid");
var nameNode = entityNode.selectSingleNode("q1:name");

// Always check for null values. If an attribute is set to null, it is not contained in the
// resulting XML. And accessing accountidNode.text when accountidNode is null leads to
// a runtime error.
var accountid = (accountidNode == null) ? null : accountidNode.text;
var name = (nameNode == null) ? null : nameNode.text;

// finally display the values.
alert(name + ", " + accountid);
}

Singleton Pattern (Javascript)

The singleton pattern is what you use when you want to ensure that only one instance of an object is ever created. In classical object-oriented programming languages, the concepts behind creating a singleton was a bit tricky to wrap your mind around because it involved a class that has both static and non-static properties and methods. I’m talking about JavaScript here though, so with JavaScript being a dynamic language without true classes, the JavaScript version of a singleton is excessively simple. Why do you need the singleton?

Before I get into implementation details, I should discuss why the singleton pattern is useful for your applications. The ability to ensure you have only one instance of an object can actually come in quite handy. In server-side languages, you might use a singleton for handling a connection to a database because it's just a waste of resources to create more than one database connection for each request. Similarly, in front-end JavaScript, you might want to have an object that handles all AJAX requests be a singleton. A simple rule could be: if it has the exact same functionality every time you create a new instance, then make it a singleton. This isn't the only reason to make a singleton, though. At least in JavaScript, the singleton allows you to namespace objects and functions to keep them organized and keep them from cluttering the global namespace, which as you probably know is a horrible idea, especially if you use third party code. Using the singleton for name-spacing is also referred to as the module design pattern.

Show me the singleton

To create a singleton, all you really need to do is create an object literal.

var Singleton = {
prop: 1,
another_prop: 'value',
method: function() {…},
another_method: function() {…}
};


You can also create singletons that have private properties and methods, but it's a little bit trickier as it involves using a closure and a self-invoking anonymous function. Inside a function, some local functions and/or variables are declared. You then create and return an object literal, which has some methods that refererence the variables and functions that you declared within the larger function's scope. That outer function is immediatly executed by placing () immediately after the function declaration and the resulting object literal is assigned to a variable. If this is confusing, then take a look over the following code and then I'll explain it some more afterward.

var Singleton = (function() {
var private_property = 0,
private_method = function () {
console.log('This is private');
}

return {
prop: 1,
another_prop: 'value',
method: function() {
private_method();
return private_property;
},
another_method: function() {…}
}
}());


The key is that when a variable is declared with var in front of it inside a function, that variable is only accessible inside the function and by functions that were declared within that function (the functions in the object literal for example). The return statement gives us back the object literal, which gets assigned to Singleton after the outer function executes itself.


Namespacing with the singleton


In JavaScript, namespacing is done by adding objects as properties of another object, so that it is one or more layers deep. This is useful for organizing code into logical sections. While the YUI JavaScript library does this to a degree that I feel is nearly excessive with numerous levels of namespacing, in general it is considered best practice to limit nesting namespaces to only a few lavels or less. The code below is an example of namespacing.

var Namespace = {
Util: {
util_method1: function() {…},
util_method2: function() {…}
},
Ajax: {
ajax_method: function() {…}
},
some_method: function() {…}
};

// Here's what it looks like when it's used
Namespace.Util.util_method1();
Namespace.Ajax.ajax_method();
Namespace.some_method();

The use of namespacing, as I said earlier, keeps global variables to a minumum. Heck, you can even have entire applications attached to a single object namespace named app if that's your perogative.

CRM 4.0 Form JS Library

Here are a set of functions to work with CRM 4.0 forms.
 
/* Jscript */

document = new Object();

// Field Object
document.getFieldObj = function(fname)
{
var ret = document.getElementById(fname);
return ret;
};
// FieldText
document.getDataValue = function(fname)
{
var str = document.getFieldObj(fname);
str = str.DataValue;
return tmp;
};
document.getDefaultValue = function(fname)
{
var obj = document.getFieldObj(fname);
obj = obj.DefaultValue;
return tmp;
};
document.lookupItem = function(fname)
{
var obj = document.getFieldObj(fname);
return (obj[0].name);
};
document.lookupGuid = function(fname)
{
var obj = document.getFieldObj(fname);
return (obj[0].id);
};
document.lookupTypename = function(fname)
{
var obj = document.getFieldObj(fname);
return (obj[0].typename);
};
document.setFocus = function(fname)
{
var obj = document.getFieldObj(fname);
obj.SetFocus();
};
document.onChange = function(fname)
{
var obj = document.getFieldObj(fname);
obj.FireOnChange();
};
document.getRequiredLevel = function(fname)
{
var tmp = document.getFieldObj(fname);
return tmp.RequiredLevel;
};
document.idDirty = function(fname)
{
var tmp = document.getFieldObj(fname);
if(tmp.IsDirty)
return true;
else
return false;
};
document.disableField = function(fname)
{
var str = document.getFieldObj(fname);
str.disabled = true;
return true;
}
document.forceSubmit = function(fname)
{
var obj = document.getFieldObj(fname);
obj.ForceSubmit;
}
document.getSelectedText = function(fname)
{
var obj = document.getFieldObj(fname);
return(obj.SelectedText);
}
document.getSelectedOption = function(fname)
{
var obj = document.getFieldObj(fname);
return(obj.GetSelectedOption);
}
document.getOptions = function(fname)
{
var obj = document.getFieldObj(fname);
return(obj.Options);
}
document.addOption = function(fname,text,datavalue)
{
var obj = document.getFieldObj(fname);
obj.AddOption(text,datavalue);
return true;
}
document.delOption = function(fname,value)
{
var obj = document.getFieldObj(fname);
obj.DeleteOption(value);
return true;
}
document.genSoap = function(fxml)
{
var soap2 = "";
soap2 += GenerateAuthenticationHeader();
soap2 += "";
soap2 += fxml;
soap2 += "
";
return soap2;
}
document.ajaxRequest = function(genUrl)
{
var xhr = new ActiveXObject("Msxml2.XMLHTTP");
xhr.open("GET", genUrl, false);
xhr.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
xhr.send(null);
var resultSet = xhr.responsetext;
return resultSet;
}

document.ajaxSoap = function (soap_msg) {
// COUNTRY ISO CODE
var XmlHttp = new ActiveXObject("Msxml2.XMLHTTP");
XmlHttp.open("POST", "/mscrmservices/2007/CrmService.asmx", false);
XmlHttp.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
XmlHttp.setRequestHeader("SOAPAction", "http://schemas.microsoft.com/crm/2007/WebServices/Fetch");
XmlHttp.setRequestHeader("Content-Length", soap_msg.length);
XmlHttp.send(soap_msg);
var resultSet = XmlHttp.responseXML.text;
}

Get Record Guid by name

//Change the fetch query as per use
 
/* Jscript */


document.getBuGuid = function(product_nm){
//var _d_fxml = "";
var _d_fxml = "<fetch mapping='logical'><entity name='new_businessinterest'><no-attrs/><filter type='and'><condition attribute='new_name' operator='eq' value='"+product_nm+"'/></filter></entity></fetch>";
//var _e_fxml = CrmEncodeDecode.CrmHtmlEncode(_d_fxml);
var soap_header= document.genSoap(_d_fxml);
var ajResponse = document.ajaxSoap(soap_header);

var oXmlDoc = new ActiveXObject("Microsoft.XMLDOM"); //Create an XML document that you can parse.o
oXmlDoc.async = false;

oXmlDoc.loadXML(ajResponse); //Load the XML document that has the UnEncoded results.
var results = oXmlDoc.getElementsByTagName('resultset');
var bu_guid = oXmlDoc.selectNodes("//new_businessinterestid")[0].text;
var re1='.*?'; // Non-greedy match on filler
var re2='([A-Z0-9]{8}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{12})'; // SQL GUID 1
var p = new RegExp(re1+re2,["i"]);
var m = p.exec(bu_guid);
if (m != null)
{
var guid1=m[1];
return(guid1.replace(/ }
};

AJAX wrapper

JavaScript AJAX wrapper for Ajax calls
 
/* Jscript */


//// AJAX wrap
function AjaxWrapper()
{
var object = this;

object.Request = NewRequest();
object.Request.onreadystatechange = CompleteRequest;

this.Sync = true;
this.Method = "GET";
this.URL = "";
this.WebServiceMethod = "";
this.Parameters = new ParameterCollection();

this.Execute = ExecuteRequest;
this.AsyncCallbackMethod = null;

this.ResultXML = null;
this.ResultText = null;

function NewRequest()
{
if (window.XMLHttpRequest)
return new XMLHttpRequest();
else
return new ActiveXObject("Microsoft.XMLHTTP");
}

function ExecuteRequest()
{
var parameters = object.Parameters.toString();

ResetRequest();

if (this.Method.toUpperCase() == "POST")
{
if (object.WebServiceMethod.length > 0)
object.Request.open(object.Method, object.URL + "/" + object.WebServiceMethod, !object.Sync);
else
object.Request.open(object.Method, object.URL, !object.Sync);

object.Request.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
object.Request.send(object.Parameters);
}
else if (this.Method.toUpperCase() == "GET")
{
if (object.WebServiceMethod.length > 0 && parameters.length > 0)
object.Request.open(object.Method, object.URL + "/" + object.WebServiceMethod + "?" + parameters, !object.Sync);
else if (object.WebServiceMethod.length > 0)
object.Request.open(object.Method, object.URL + "/" + object.WebServiceMethod, !object.Sync);
else if (parametres.length > 0)
object.Request.open(object.Method, object.URL + "?" + parameters, !object.Sync);
else
object.Request.open(object.Method, object.URL, !object.Sync);

object.Request.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
object.Request.send();
}
else
{
throw "The method '" + this.Method.toUpperCase() + "' is not supported !";
}

if (object.Sync)
FinishRequest(object.Request.responseText);
}

function CompleteRequest()
{
if (object.Request.readyState == 4)
{
if (object.Request.status == 200)
{
FinishRequest(object.Request.responseText);

if (object.AsyncCallbackMethod != null)
object.AsyncCallbackMethod();
}
}
}

function ResetRequest()
{
object.Request = NewRequest();
object.Request.onreadystatechange = CompleteRequest;
}

function FinishRequest(retourTexte)
{
var xmlDoc = new ActiveXObject("MSXML2.DOMDocument");

object.ResultText = object.Request.responseText;

try
{
xmlDoc.loadXML(object.Request.responseText);

if (xmlDoc.parsed && xmlDoc.xml.length > 0)
object.ResultXML = xmlDoc;
else
object.ResultXML = null;
}
catch (ex)
{
object.ResultXML = null;
}
}
}

Parsing XML Content

 
/* Jscript: Parse XML Document */


var oXmlDoc = new ActiveXObject("Microsoft.XMLDOM"); //Create an XML document that you can parse.
oXmlDoc.async = false;
oXmlDoc.loadXML(resultSet); //Load the XML document that has the UnEncoded results.
var results = oXmlDoc.getElementsByTagName('result'); //get the specified node by DOM function
var currency = oXmlDoc.selectNodes("//businessinterest")[1].text; //gets the no of nodes with function



Show Picklist item in CRM 4.0

 
/* Jscript */

function ShowPickListItem(listID, value)
{
var objList = document.getElementById(listID);

if (objList.SavedList != null)
{
var selValue = null;
var indexInsertion = 0;

for (var i=0; i if (objList.SavedList[i].value == value)
objList.SavedList[i].Visible = true;

// Keep the selected value so we can reselect it after
if (objList.selectedIndex > -1)
selValue = objList.options[objList.selectedIndex].value;
// Remove all the items in the list
for (var i=objList.options.length - 1; i>=0; i--)
objList.options.remove(i);

// Add the items that must be visible
for (var i=0; i {
if (objList.SavedList[i].Visible)
{
var oOption = document.createElement('option');

oOption.text = objList.SavedList[i].Libelle;
oOption.value = objList.SavedList[i].value;

objList.options.add(oOption);
}
}

// Reselect the item that was selected
for (var i=0; i if (objList.options[i].value == selValue)
objList.selectedIndex = i;
}
}

Complete DateDiff() Function

Perhaps the above function looks like the Visual Basic function of the same name. In fact it is loosely based on it. I was planning on recreating it in its complete form for your enjoyment, but, thankfully someone has already beat me to it. That someone is Rob Eberhardt of Slingshot Solutions. It's part of his excellent jsDate script. It's free to use as long as you give credit where credit is due. His function offers a lot of advantages over the simple one presented above. For starters, his can calculate the month interval, which cannot be done by dividing into the number of milliseconds since month lengths differ. It also supports setting the first day of the week to something other than Sunday. Finally, it adjusts for Daylight Savings Time, which affects intervals of a day ("d") and larger:
Date.DateDiff = function(p_Interval, p_Date1, p_Date2, p_FirstDayOfWeek){
 p_FirstDayOfWeek = (isNaN(p_FirstDayOfWeek) || p_FirstDayOfWeek==0) ? vbSunday : parseInt(p_FirstDayOfWeek);

 var dt1 = Date.CDate(p_Date1);
 var dt2 = Date.CDate(p_Date2);

 //correct Daylight Savings Ttime (DST)-affected intervals ("d" & bigger)
 if("h,n,s,ms".indexOf(p_Interval.toLowerCase())==-1){
  if(p_Date1.toString().indexOf(":") ==-1){ dt1.setUTCHours(0,0,0,0) }; // no time, assume 12am
  if(p_Date2.toString().indexOf(":") ==-1){ dt2.setUTCHours(0,0,0,0) }; // no time, assume 12am
 }


 // get ms between UTC dates and make into "difference" date
 var iDiffMS = dt2.valueOf() - dt1.valueOf();
 var dtDiff = new Date(iDiffMS);

 // calc various diffs
 var nYears  = dt2.getUTCFullYear() - dt1.getUTCFullYear();
 var nMonths = dt2.getUTCMonth() - dt1.getUTCMonth() + (nYears!=0 ? nYears*12 : 0);
 var nQuarters = parseInt(nMonths / 3);
 
 var nMilliseconds = iDiffMS;
 var nSeconds = parseInt(iDiffMS / 1000);
 var nMinutes = parseInt(nSeconds / 60);
 var nHours = parseInt(nMinutes / 60);
 var nDays  = parseInt(nHours / 24); //now fixed for DST switch days
 var nWeeks = parseInt(nDays / 7);

 if(p_Interval.toLowerCase()=='ww'){
   // set dates to 1st & last FirstDayOfWeek
   var offset = Date.DatePart("w", dt1, p_FirstDayOfWeek)-1;
   if(offset){ dt1.setDate(dt1.getDate() +7 -offset); }
   var offset = Date.DatePart("w", dt2, p_FirstDayOfWeek)-1;
   if(offset){ dt2.setDate(dt2.getDate() -offset); }
   // recurse to "w" with adjusted dates
   var nCalWeeks = Date.DateDiff("w", dt1, dt2) + 1;
 }
 
 // return difference
 switch(p_Interval.toLowerCase()){
  case "yyyy": return nYears;
  case "q": return nQuarters;
  case "m": return nMonths;
  case "y": // day of year
  case "d": return nDays;
  case "w": return nWeeks;
  case "ww":return nCalWeeks; // week of year 
  case "h": return nHours;
  case "n": return nMinutes;
  case "s": return nSeconds;
  case "ms":return nMilliseconds;
  default : return "invalid interval: '" + p_Interval + "'";
 }
}
var y2k  = new Date(2000, 0, 1) //Month is 0-11 in JavaScript!
var today= new Date();
console.log('Months since the new millenium: ' + Date.DateDiff('m', y2k, today)); //displays 143

A Simple dateDiff() Function

There is no reason to write a function for each date/time interval; one function can contain all of the required intervals and return the correct value for the one we want. In the following function, the datepart argument tells it what interval we are after, where 'w' is a week, 'd' a day, 'h' hours, 'n' for minutes, and 's' for seconds:

// datepart: 'y', 'm', 'w', 'd', 'h', 'n', 's'
Date.dateDiff = function(datepart, fromdate, todate) { 
  datepart = datepart.toLowerCase(); 
  var diff = todate - fromdate; 
  var divideBy = { w:604800000, 
                   d:86400000, 
                   h:3600000, 
                   n:60000, 
                   s:1000 }; 
  
  return Math.floor( diff/divideBy[datepart]);
}
//Set the two dates
var y2k  = new Date(2000, 0, 1);
var today= new Date();
console.log('Weeks since the new millenium: ' + Date.dateDiff('w', y2k, today)); //displays 625

Converting Milliseconds to other Intervals

As long as you can calculate the number of milliseconds in an interval, you can come up with a number by dividing the total number of milliseconds by the number of milliseconds in the desired interval. What's more, we can apply the modulus (%) operator to strip out that value to determine the next larger interval. The key is to always go from the smallest interval - milliseconds - to the largest - days:

Date.daysBetween = function( date1, date2 ) {
  //Get 1 day in milliseconds
  var one_day=1000*60*60*24;

  // Convert both dates to milliseconds
  var date1_ms = date1.getTime();
  var date2_ms = date2.getTime();

  // Calculate the difference in milliseconds
  var difference_ms = date2_ms - date1_ms;
  //take out milliseconds
  difference_ms = difference_ms/1000;
  var seconds = Math.floor(difference_ms % 60);
  difference_ms = difference_ms/60; 
  var minutes = Math.floor(difference_ms % 60);
  difference_ms = difference_ms/60; 
  var hours = Math.floor(difference_ms % 24);  
  var days = Math.floor(difference_ms/24);
  
  return days + ' days, ' + hours + ' hours, ' + minutes + ' minutes, and ' + seconds + ' seconds';
}

//Set the two dates
var y2k  = new Date(2000, 0, 1);
var Jan1st2010 = new Date(y2k.getYear() + 10, y2k.getMonth(), y2k.getDate());
var today= new Date();
//displays "Days from Wed Jan 01 0110 00:00:00 GMT-0500 (Eastern Standard Time) to Tue Dec 27 2011 12:14:02 GMT-0500 (Eastern Standard Time): 694686 days, 12 hours, 14 minutes, and 2 seconds"
console.log('Days from ' + Jan1st2010 + ' to ' + today + ': ' + Date.daysBetween(Jan1st2010, today));

Calculating the Difference between Two Known Dates

Unfortunately, calculating a date interval such as days, weeks, or months between two known dates is not as easy because you can't just add Date objects together. In order to use a Date object in any sort of calculation, we must first retrieve the Date's internal millisecond value, which is stored as a large integer. The function to do that is Date.getTime(). Once both Dates have been converted, subtracting the later one from the earlier one returns the difference in milliseconds. The desired interval can then be determined by dividing that number by the corresponding number of milliseconds. For instance, to obtain the number of days for a given number of milliseconds, we would divide by 86,400,000, the number of milliseconds in a day (1000 x 60 seconds x 60 minutes x 24 hours):
 
Date.daysBetween = function( date1, date2 ) {
  //Get 1 day in milliseconds
  var one_day=1000*60*60*24;

  // Convert both dates to milliseconds
  var date1_ms = date1.getTime();
  var date2_ms = date2.getTime();

  // Calculate the difference in milliseconds
  var difference_ms = date2_ms - date1_ms;
    
  // Convert back to days and return
  return Math.round(difference_ms/one_day); 
}

//Set the two dates
var y2k  = new Date(2000, 0, 1); 
var Jan1st2010 = new Date(y2k.getFullYear() + 10, y2k.getMonth(), y2k.getDate());
var today= new Date();
//displays 726
console.log( 'Days since ' 
           + Jan1st2010.toLocaleDateString() + ': ' 
           + Date.daysBetween(Jan1st2010, today));
The rounding is optional, depending on whether you want partial days or not.

jsDate: VBScript native Date functions emulated for Javascript

/*
Name: jsDate
Desc: VBScript native Date functions emulated for Javascript
Author: Rob Eberhardt, Slingshot Solutions - http://slingfive.com/
Note: see jsDate.txt for more info
*/

// constants
vbGeneralDate=0; vbLongDate=1; vbShortDate=2; vbLongTime=3; vbShortTime=4;  // NamedFormat
vbUseSystemDayOfWeek=0; vbSunday=1; vbMonday=2; vbTuesday=3; vbWednesday=4; vbThursday=5; vbFriday=6; vbSaturday=7; // FirstDayOfWeek
vbUseSystem=0; vbFirstJan1=1; vbFirstFourDays=2; vbFirstFullWeek=3; // FirstWeekOfYear

// arrays (1-based)
Date.MonthNames = [null,'January','February','March','April','May','June','July','August','September','October','November','December'];
Date.WeekdayNames = [null,'Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'];




Date.IsDate = function(p_Expression){
 return !isNaN(new Date(p_Expression));  // <-- review further
}

Date.CDate = function(p_Date){
 if(Date.IsDate(p_Date)){ return new Date(p_Date); }

 var strTry = p_Date.replace(/\-/g, '/').replace(/\./g, '/').replace(/ /g, '/'); // fix separators
 strTry = strTry.replace(/pm$/i, " pm").replace(/am$/i, " am"); // and meridian spacing
 if(Date.IsDate(strTry)){ return new Date(strTry); }

 var strTryYear = strTry + '/' + new Date().getFullYear(); // append year
 if(Date.IsDate(strTryYear)){ return new Date(strTryYear); }
 

 if(strTry.indexOf(":")){ // if appears to have time
  var strTryYear2 = strTry.replace(/ /, '/' + new Date().getFullYear() + ' '); // insert year
  if(Date.IsDate(strTryYear2)){ return new Date(strTryYear2); }

  var strTryDate = new Date().toDateString() + ' ' + p_Date; // pre-pend current date
  if(Date.IsDate(strTryDate)){ return new Date(strTryDate); }
 }
 
 return false; // double as looser IsDate
 //throw("Error #13 - Type mismatch"); // or is this better? 
}
 


Date.DateAdd = function(p_Interval, p_Number, p_Date){
 if(!Date.CDate(p_Date)){ return "invalid date: '" + p_Date + "'"; }
 if(isNaN(p_Number)){ return "invalid number: '" + p_Number + "'"; } 

 p_Number = new Number(p_Number);
 var dt = Date.CDate(p_Date);
 
 switch(p_Interval.toLowerCase()){
  case "yyyy": {
   dt.setFullYear(dt.getFullYear() + p_Number);
   break;
  }
  case "q": {
   dt.setMonth(dt.getMonth() + (p_Number*3));
   break;
  }
  case "m": {
   dt.setMonth(dt.getMonth() + p_Number);
   break;
  }
  case "y":   // day of year
  case "d":   // day
  case "w": {  // weekday
   dt.setDate(dt.getDate() + p_Number);
   break;
  }
  case "ww": { // week of year
   dt.setDate(dt.getDate() + (p_Number*7));
   break;
  }
  case "h": {
   dt.setHours(dt.getHours() + p_Number);
   break;
  }
  case "n": {  // minute
   dt.setMinutes(dt.getMinutes() + p_Number);
   break;
  }
  case "s": {
   dt.setSeconds(dt.getSeconds() + p_Number);
   break;
  }
  case "ms": { // JS extension
   dt.setMilliseconds(dt.getMilliseconds() + p_Number);
   break;
  }
  default: {
   return "invalid interval: '" + p_Interval + "'";
  }
 }
 return dt;
}



Date.DateDiff = function(p_Interval, p_Date1, p_Date2, p_FirstDayOfWeek){
 if(!Date.CDate(p_Date1)){ return "invalid date: '" + p_Date1 + "'"; }
 if(!Date.CDate(p_Date2)){ return "invalid date: '" + p_Date2 + "'"; }
 p_FirstDayOfWeek = (isNaN(p_FirstDayOfWeek) || p_FirstDayOfWeek==0) ? vbSunday : parseInt(p_FirstDayOfWeek); // set default & cast

 var dt1 = Date.CDate(p_Date1);
 var dt2 = Date.CDate(p_Date2);

 // correct DST-affected intervals ("d" & bigger)
 if("h,n,s,ms".indexOf(p_Interval.toLowerCase())==-1){
  if(p_Date1.toString().indexOf(":") ==-1){ dt1.setUTCHours(0,0,0,0) }; // no time, assume 12am
  if(p_Date2.toString().indexOf(":") ==-1){ dt2.setUTCHours(0,0,0,0) }; // no time, assume 12am
 }


 // get ms between UTC dates and make into "difference" date
 var iDiffMS = dt2.valueOf() - dt1.valueOf();
 var dtDiff = new Date(iDiffMS);

 // calc various diffs
 var nYears  = dt2.getUTCFullYear() - dt1.getUTCFullYear();
 var nMonths = dt2.getUTCMonth() - dt1.getUTCMonth() + (nYears!=0 ? nYears*12 : 0);
 var nQuarters = parseInt(nMonths / 3); //<<-- different than VBScript, which watches rollover not completion
 
 var nMilliseconds = iDiffMS;
 var nSeconds = parseInt(iDiffMS / 1000);
 var nMinutes = parseInt(nSeconds / 60);
 var nHours = parseInt(nMinutes / 60);
 var nDays  = parseInt(nHours / 24); // <-- now fixed for DST switch days
 var nWeeks = parseInt(nDays / 7);


 if(p_Interval.toLowerCase()=='ww'){
   // set dates to 1st & last FirstDayOfWeek
   var offset = Date.DatePart("w", dt1, p_FirstDayOfWeek)-1;
   if(offset){ dt1.setDate(dt1.getDate() +7 -offset); }
   var offset = Date.DatePart("w", dt2, p_FirstDayOfWeek)-1;
   if(offset){ dt2.setDate(dt2.getDate() -offset); }
   // recurse to "w" with adjusted dates
   var nCalWeeks = Date.DateDiff("w", dt1, dt2) + 1;
 }
 // TODO: similar for 'w'?
 
 
 // return difference
 switch(p_Interval.toLowerCase()){
  case "yyyy": return nYears;
  case "q": return nQuarters;
  case "m": return nMonths;
  case "y":   // day of year
  case "d": return nDays;
  case "w": return nWeeks;
  case "ww":return nCalWeeks; // week of year 
  case "h": return nHours;
  case "n": return nMinutes;
  case "s": return nSeconds;
  case "ms":return nMilliseconds; // not in VBScript
  default : return "invalid interval: '" + p_Interval + "'";
 }
}




Date.DatePart = function(p_Interval, p_Date, p_FirstDayOfWeek){
 if(!Date.CDate(p_Date)){ return "invalid date: '" + p_Date + "'"; }

 var dtPart = Date.CDate(p_Date);
 
 switch(p_Interval.toLowerCase()){
  case "yyyy": return dtPart.getFullYear();
  case "q": return parseInt(dtPart.getMonth() / 3) + 1;
  case "m": return dtPart.getMonth() + 1;
  case "y": return Date.DateDiff("y", "1/1/" + dtPart.getFullYear(), dtPart) + 1; // day of year
  case "d": return dtPart.getDate();
  case "w": return Date.Weekday(dtPart.getDay()+1, p_FirstDayOfWeek);  // weekday
  case "ww":return Date.DateDiff("ww", "1/1/" + dtPart.getFullYear(), dtPart, p_FirstDayOfWeek) + 1; // week of year
  case "h": return dtPart.getHours();
  case "n": return dtPart.getMinutes();
  case "s": return dtPart.getSeconds();
  case "ms":return dtPart.getMilliseconds(); // <-- JS extension, NOT in VBScript
  default : return "invalid interval: '" + p_Interval + "'";
 }
}



Date.MonthName = function(p_Month, p_Abbreviate){
 if(isNaN(p_Month)){ // v0.94- compat: extract real param from passed date
  if(!Date.CDate(p_Month)){ return "invalid month: '" + p_Month + "'"; }
  p_Month = DatePart("m", Date.CDate(p_Month));
 }

 var retVal = Date.MonthNames[p_Month];
 if(p_Abbreviate==true){ retVal = retVal.substring(0, 3) } // abbr to 3 chars
 return retVal;
}


Date.WeekdayName = function(p_Weekday, p_Abbreviate, p_FirstDayOfWeek){
 if(isNaN(p_Weekday)){ // v0.94- compat: extract real param from passed date
  if(!Date.CDate(p_Weekday)){ return "invalid weekday: '" + p_Weekday + "'"; }
  p_Weekday = DatePart("w", Date.CDate(p_Weekday));
 }
 p_FirstDayOfWeek = (isNaN(p_FirstDayOfWeek) || p_FirstDayOfWeek==0) ? vbSunday : parseInt(p_FirstDayOfWeek); // set default & cast

 var nWeekdayNameIdx = ((p_FirstDayOfWeek-1 + parseInt(p_Weekday)-1 +7) % 7) + 1; // compensate nWeekdayNameIdx for p_FirstDayOfWeek
 var retVal = Date.WeekdayNames[nWeekdayNameIdx];
 if(p_Abbreviate==true){ retVal = retVal.substring(0, 3) } // abbr to 3 chars
 return retVal;
}


// adjusts weekday for week starting on p_FirstDayOfWeek
Date.Weekday=function(p_Weekday, p_FirstDayOfWeek){ 
 p_FirstDayOfWeek = (isNaN(p_FirstDayOfWeek) || p_FirstDayOfWeek==0) ? vbSunday : parseInt(p_FirstDayOfWeek); // set default & cast

 return ((parseInt(p_Weekday) - p_FirstDayOfWeek +7) % 7) + 1;
}





Date.FormatDateTime = function(p_Date, p_NamedFormat){
 if(p_Date.toUpperCase().substring(0,3) == "NOW"){ p_Date = new Date() };
 if(!Date.CDate(p_Date)){ return "invalid date: '" + p_Date + "'"; }
 if(isNaN(p_NamedFormat)){ p_NamedFormat = vbGeneralDate };

 var dt = Date.CDate(p_Date);

 switch(parseInt(p_NamedFormat)){
  case vbGeneralDate: return dt.toString();
  case vbLongDate:  return Format(p_Date, 'DDDD, MMMM D, YYYY');
  case vbShortDate:  return Format(p_Date, 'MM/DD/YYYY');
  case vbLongTime:  return dt.toLocaleTimeString();
  case vbShortTime:  return Format(p_Date, 'HH:MM:SS');
  default: return "invalid NamedFormat: '" + p_NamedFormat + "'";
 }
}


Date.Format = function(p_Date, p_Format, p_FirstDayOfWeek, p_firstweekofyear) {
 if(!Date.CDate(p_Date)){ return "invalid date: '" + p_Date + "'"; }
 if(!p_Format || p_Format==''){ return dt.toString() };

 var dt = Date.CDate(p_Date);

 // Zero-padding formatter
 this.pad = function(p_str){
  if(p_str.toString().length==1){p_str = '0' + p_str}
  return p_str;
 }

 var ampm = dt.getHours()>=12 ? 'PM' : 'AM'
 var hr = dt.getHours();
 if (hr == 0){hr = 12};
 if (hr > 12) {hr -= 12};
 var strShortTime = hr +':'+ this.pad(dt.getMinutes()) +':'+ this.pad(dt.getSeconds()) +' '+ ampm;
 var strShortDate = (dt.getMonth()+1) +'/'+ dt.getDate() +'/'+ new String( dt.getFullYear() ).substring(2,4);
 var strLongDate = Date.MonthName(dt.getMonth()+1) +' '+ dt.getDate() +', '+ dt.getFullYear();  //

 var retVal = p_Format;
 
 // switch tokens whose alpha replacements could be accidentally captured
 retVal = retVal.replace( new RegExp('C', 'gi'), 'CCCC' ); 
 retVal = retVal.replace( new RegExp('mmmm', 'gi'), 'XXXX' );
 retVal = retVal.replace( new RegExp('mmm', 'gi'), 'XXX' );
 retVal = retVal.replace( new RegExp('dddddd', 'gi'), 'AAAAAA' ); 
 retVal = retVal.replace( new RegExp('ddddd', 'gi'), 'AAAAA' ); 
 retVal = retVal.replace( new RegExp('dddd', 'gi'), 'AAAA' );
 retVal = retVal.replace( new RegExp('ddd', 'gi'), 'AAA' );
 retVal = retVal.replace( new RegExp('timezone', 'gi'), 'ZZZZ' );
 retVal = retVal.replace( new RegExp('time24', 'gi'), 'TTTT' );
 retVal = retVal.replace( new RegExp('time', 'gi'), 'TTT' );

 // now do simple token replacements
 retVal = retVal.replace( new RegExp('yyyy', 'gi'), dt.getFullYear() );
 retVal = retVal.replace( new RegExp('yy', 'gi'), new String( dt.getFullYear() ).substring(2,4) );
 retVal = retVal.replace( new RegExp('y', 'gi'), Date.DatePart("y", dt) );
 retVal = retVal.replace( new RegExp('q', 'gi'), Date.DatePart("q", dt) );
 retVal = retVal.replace( new RegExp('mm', 'gi'), (dt.getMonth() + 1) ); 
 retVal = retVal.replace( new RegExp('m', 'gi'), (dt.getMonth() + 1) ); 
 retVal = retVal.replace( new RegExp('dd', 'gi'), this.pad(dt.getDate()) );
 retVal = retVal.replace( new RegExp('d', 'gi'), dt.getDate() );
 retVal = retVal.replace( new RegExp('hh', 'gi'), this.pad(dt.getHours()) );
 retVal = retVal.replace( new RegExp('h', 'gi'), dt.getHours() );
 retVal = retVal.replace( new RegExp('nn', 'gi'), this.pad(dt.getMinutes()) );
 retVal = retVal.replace( new RegExp('n', 'gi'), dt.getMinutes() );
 retVal = retVal.replace( new RegExp('ss', 'gi'), this.pad(dt.getSeconds()) ); 
 retVal = retVal.replace( new RegExp('s', 'gi'), dt.getSeconds() ); 
 retVal = retVal.replace( new RegExp('t t t t t', 'gi'), strShortTime ); 
 retVal = retVal.replace( new RegExp('am/pm', 'g'), dt.getHours()>=12 ? 'pm' : 'am');
 retVal = retVal.replace( new RegExp('AM/PM', 'g'), dt.getHours()>=12 ? 'PM' : 'AM');
 retVal = retVal.replace( new RegExp('a/p', 'g'), dt.getHours()>=12 ? 'p' : 'a');
 retVal = retVal.replace( new RegExp('A/P', 'g'), dt.getHours()>=12 ? 'P' : 'A');
 retVal = retVal.replace( new RegExp('AMPM', 'g'), dt.getHours()>=12 ? 'pm' : 'am');
 // (always proceed largest same-lettered token to smallest)

 // now finish the previously set-aside tokens 
 retVal = retVal.replace( new RegExp('XXXX', 'gi'), Date.MonthName(dt.getMonth()+1, false) ); //
 retVal = retVal.replace( new RegExp('XXX',  'gi'), Date.MonthName(dt.getMonth()+1, true ) ); //
 retVal = retVal.replace( new RegExp('AAAAAA', 'gi'), strLongDate ); 
 retVal = retVal.replace( new RegExp('AAAAA', 'gi'), strShortDate ); 
 retVal = retVal.replace( new RegExp('AAAA', 'gi'), Date.WeekdayName(dt.getDay()+1, false, p_FirstDayOfWeek) ); // 
 retVal = retVal.replace( new RegExp('AAA',  'gi'), Date.WeekdayName(dt.getDay()+1, true,  p_FirstDayOfWeek) ); // 
 retVal = retVal.replace( new RegExp('TTTT', 'gi'), dt.getHours() + ':' + this.pad(dt.getMinutes()) );
 retVal = retVal.replace( new RegExp('TTT',  'gi'), hr +':'+ this.pad(dt.getMinutes()) +' '+ ampm );
 retVal = retVal.replace( new RegExp('CCCC', 'gi'), strShortDate +' '+ strShortTime ); 

 // finally timezone
 tz = dt.getTimezoneOffset();
 timezone = (tz<0) ? ('GMT-' + tz/60) : (tz==0) ? ('GMT') : ('GMT+' + tz/60);
 retVal = retVal.replace( new RegExp('ZZZZ', 'gi'), timezone );

 return retVal;
}



// ====================================

/* if desired, map new methods to direct functions
*/
IsDate = Date.IsDate;
CDate = Date.CDate;
DateAdd = Date.DateAdd;
DateDiff = Date.DateDiff;
DatePart = Date.DatePart;
MonthName = Date.MonthName;
WeekdayName = Date.WeekdayName;
Weekday = Date.Weekday;
FormatDateTime = Date.FormatDateTime;
Format = Date.Format;



/* and other capitalizations for easier porting
isDate = IsDate;
dateAdd = DateAdd;
dateDiff = DateDiff;
datePart = DatePart;
monthName = MonthName;
weekdayName = WeekdayName;
formatDateTime = FormatDateTime;
format = Format;

isdate = IsDate;
dateadd = DateAdd;
datediff = DateDiff;
datepart = DatePart;
monthname = MonthName;
weekdayname = WeekdayName;
formatdatetime = FormatDateTime;

ISDATE = IsDate;
DATEADD = DateAdd;
DATEDIFF = DateDiff;
DATEPART = DatePart;
MONTHNAME = MonthName;
WEEKDAYNAME = WeekdayName;
FORMATDATETIME = FormatDateTime;
FORMAT = Format;
*/