// *********************************************************************************
// File: fsAtomMerge.js
// Notes: You must run this file with cscript.exe (i.e. not wscript.exe)
//
// Copyright (c) Microsoft Corporation. All Rights Reserved.
// *********************************************************************************
// -------------------------- MAIN (BEGIN) --------------------------
// Validate arguments
var g_Arguments = WScript.Arguments;
if (g_Arguments.length < 2)
{
DisplayUsage();
WScript.Quit();
}
// Get required parameters
var g_LocalPath = g_Arguments(0);
var g_IncomingPath = g_Arguments(1);
var g_pILocalAtomXmlDOMDocument = null;
try
{
// Create instance of XML DOM
g_pILocalAtomXmlDOMDocument = new ActiveXObject("Microsoft.XMLDOM");
g_pILocalAtomXmlDOMDocument.async = false;
// Load local document
var Success = g_pILocalAtomXmlDOMDocument.load(g_LocalPath);
if (!Success)
throw new Error(0, "IXmlDocument::load failed");
}
catch (e)
{
WScript.Echo("Exception while loading '" + g_LocalPath +"': " + e.message);
WScript.Quit();
}
// Get local "feed" element
var g_pILocalFeedXmlDOMElement = g_pILocalAtomXmlDOMDocument.documentElement;
// Check if FeedSync namespace exists, if not then display error
if (g_pILocalFeedXmlDOMElement.getAttribute("xmlns:sx") == null)
{
WScript.Echo("Can't process local Atom file - it does not contain a 'sx' namespace!");
WScript.Quit();
}
var g_pIIncomingAtomXmlDOMDocument = null;
try
{
// Create instance of XML DOM
g_pIIncomingAtomXmlDOMDocument = new ActiveXObject("Microsoft.XMLDOM");
g_pIIncomingAtomXmlDOMDocument.async = false;
// Load incoming document
var Success = g_pIIncomingAtomXmlDOMDocument.load(g_IncomingPath);
if (!Success)
throw new Error(0, "IXmlDocument::load failed");
}
catch (e)
{
WScript.Echo("Exception while loading '" + g_IncomingPath +"': " + e.message);
WScript.Quit();
}
// Get incoming "feed" element
var g_pIIncomingFeedXmlDOMElement = g_pIIncomingAtomXmlDOMDocument.documentElement;
// Check if FeedSync namespace exists, if not then display error
if (g_pIIncomingFeedXmlDOMElement.getAttribute("xmlns:sx") == null)
{
WScript.Echo("Can't process incoming Atom file - it does not contain a 'sx' namespace!");
WScript.Quit();
}
// *********************************************************************************
// BIG HONKING NOTE: We only deal with "entry" elements when merging, so any other
// changes made in the incoming document are ignored. Remember that
// the goal of FeedSync isn't to replicate Atom files, it is to
// replicate items via Atom.
// *********************************************************************************
// Create hashtable for local FSNodes
var g_LocalFSNodeHashtable = new Object();
// Populate local FSNode hashtable
PopulateFSNodesFromXmlDOMElement(g_LocalFSNodeHashtable, g_pILocalFeedXmlDOMElement);
// Create hashtable for incoming FSNodes
var g_IncomingFSNodeHashtable = new Object();
// Populate incoming FSNode hashtable
PopulateFSNodesFromXmlDOMElement(g_IncomingFSNodeHashtable, g_pIIncomingFeedXmlDOMElement);
var g_pIOutputAtomXmlDOMDocument = null;
try
{
// Create instance of XML DOM
g_pIOutputAtomXmlDOMDocument = new ActiveXObject("Microsoft.XMLDOM");
g_pIOutputAtomXmlDOMDocument.async = false;
// Create output feed document based on local feed document
g_pILocalAtomXmlDOMDocument.save(g_pIOutputAtomXmlDOMDocument);
if (!Success)
throw new Error(0, "IXmlDocument::load failed");
}
catch (e)
{
WScript.Echo("Exception while saving local document: " + e.message);
WScript.Quit();
}
// Create output array
var g_OutputFSNodeArray = new Array();
// Get output "feed" element
var g_pIOutputFeedXmlDOMElement = g_pIOutputAtomXmlDOMDocument.documentElement;
// Get "entry" elements of "feed" element
var g_pIEntryXmlDOMElements = g_pIOutputFeedXmlDOMElement.selectNodes("entry");
// *********************************************************************************
// BIG HONKING NOTE: Iterate all "entry" elements of the "feed" element (start
// with last and progress to first) in order to remove them. We
// remove them because there is further processing below that
// will take the appropriate "entry" elements from the local and
// incoming documents and add them. Note that we don't just remove
// the "feed" element and add a new one in it's place because
// a) there could be attributes on the "feed" element and b)
// there could be non-"entry" elements of the "feed" element.
for (var Index = g_pIEntryXmlDOMElements.length - 1; Index >= 0; --Index)
{
// Get next "entry" element
var pIEntryXmlDOMElement = g_pIEntryXmlDOMElements(Index);
// Remove "entry" element from output "feed" element
g_pIOutputFeedXmlDOMElement.removeChild(pIEntryXmlDOMElement);
}
// *********************************************************************************
var g_HashtableKey = null;
// Iterate items in local FSNode hashtable
for (g_HashtableKey in g_LocalFSNodeHashtable)
{
// Get FSNode from local hashtable
var LocalFSNode = g_LocalFSNodeHashtable[g_HashtableKey];
// Get reference to local FSSyncNode
var LocalFSSyncNode = LocalFSNode.m_FSSyncNode;
// Get FSNode from incoming hashtable
var IncomingFSNode = g_IncomingFSNodeHashtable[g_HashtableKey];
// Validate incoming FSNode exists, if not then node was added to local
// document
if (IncomingFSNode == null)
{
// Create clone of LocalFSNode
var ClonedFSNode = CloneFSNode(LocalFSNode);
// Add cloned FSNode to output array
g_OutputFSNodeArray[g_OutputFSNodeArray.length] = ClonedFSNode;
// Continue loop
continue;
}
// Get merged FSNode
var MergedFSNode = MergeFSNodes(LocalFSNode, IncomingFSNode);
// Add merged FSNode to output array
g_OutputFSNodeArray[g_OutputFSNodeArray.length] = MergedFSNode;
// Set incoming hashtable entry to null so we don't process it during
// second pass below
g_IncomingFSNodeHashtable[g_HashtableKey] = null;
}
// Iterate items in incoming FSNode hashtable - all remaining items are
// guaranteed to be additions to incoming document
for (g_HashtableKey in g_IncomingFSNodeHashtable)
{
// Get FSNode from incoming hashtable
var IncomingFSNode = g_IncomingFSNodeHashtable[g_HashtableKey];
// Validate FSNode exists, if not then just continue loop because
// entry was set to null when processing local hashtable
if (IncomingFSNode == null)
continue;
// Create clone of IncomingFSNode
var ClonedFSNode = CloneFSNode(IncomingFSNode);
// Add cloned FSNode to output array
g_OutputFSNodeArray[g_OutputFSNodeArray.length] = ClonedFSNode;
}
// Iterate output FSNodes
for (var Index = 0; Index < g_OutputFSNodeArray.length; ++Index)
{
// Get next output FSNode
var OutputFSNode = g_OutputFSNodeArray[Index];
// Append output FSNode's element to "feed" element
g_pIOutputFeedXmlDOMElement.appendChild(OutputFSNode.m_pIXmlDOMElement);
}
// Save modified contents to standand output stream
WScript.StdOut.Write(g_pIOutputAtomXmlDOMDocument.xml);
// -------------------------- MAIN (END) --------------------------
// -------------------------- FSNodeClass (BEGIN) --------------------------
function FSNodeClass(i_pIXmlDOMElement)
{
// Assign m_pIOXmlDOMElement member variable
this.m_pIXmlDOMElement = i_pIXmlDOMElement;
// Assign m_FSSyncNode member variable by creating a new instance of
// FSSyncNode and passing a reference to the current FSNode
this.m_FSSyncNode = new FSSyncNodeClass(this);
}
// -------------------------- FSNodeClass (END) --------------------------
// -------------------------- FSSyncNodeClass (BEGIN) --------------------------
function FSSyncNodeClass(i_FSNode)
{
// Assign m_FSNode member variable
this.m_FSNode = i_FSNode;
// Get reference to FSNode's XmlDOMElement
var pIXmlDOMElement = this.m_FSNode.m_pIXmlDOMElement;
// Assign m_pIXmlDOMElement member variable
this.m_pIXmlDOMElement = pIXmlDOMElement.selectSingleNode("sx:sync");
// Validate m_pISyncXmlDOMElement member variable
if (this.m_pIXmlDOMElement == null)
{
WScript.Echo("Unable to find 'sx:sync' element where parent id='" + this.m_FSNode.m_ParentID + "'");
WScript.Quit(0);
}
// Assign m_ID member variable
this.m_ID = this.m_pIXmlDOMElement.getAttribute("id");
// Validate m_ID member variable
if (this.m_ID == null)
{
WScript.Echo("Unable to find 'id' attribute for 'sx:sync' element where parent id='" + this.m_FSNode.m_ParentID + "'");
WScript.Quit(0);
}
// Assign m_Updates member variable
this.m_Updates = this.m_pIXmlDOMElement.getAttribute("updates");
// Validate m_Updates member variable
if (this.m_Updates == null)
{
WScript.Echo("Unable to find 'updates' attribute for 'sx:sync' element where id='" + this.m_ID + "'");
WScript.Quit(0);
}
// Assign m_NoConflicts member variable
var NoConflicts = this.m_pIXmlDOMElement.getAttribute("noconflicts");
// Validate m_Conflict member variable
if (NoConflicts == "true")
this.m_NoConflicts = true;
else
this.m_NoConflicts = false;
// Assign m_FSConflictNodes member variable by creating a new array
this.m_FSConflictNodes = new Array();
if (!this.m_NoConflicts)
{
// Get reference to "sx:conflicts" element
this.m_pIConflictsXmlDOMElement = this.m_pIXmlDOMElement.selectSingleNode("sx:conflicts");
// Validate that "sx:conflicts" element exists
if (this.m_pIConflictsXmlDOMElement != null)
{
// Get conflict "entry" elements
var pIConflictItemXmlDOMElements = this.m_pIConflictsXmlDOMElement.selectNodes("entry");
// Iterate conflict "entry" elements
for (var Index = 0; Index < pIConflictItemXmlDOMElements.length; ++Index)
{
// Get reference to next conflict "entry" element
var pIConflictItemXmlDOMElement = pIConflictItemXmlDOMElements(Index);
// Assign array entry by creating a new instance of FSNode and passing
// a reference to the current conflict "entry" element
this.m_FSConflictNodes[Index] = new FSNodeClass(pIConflictItemXmlDOMElement);
}
}
}
// Assign m_FSHistoryNodes member variable by creating a new array
this.m_FSHistoryNodes = new Array();
// Get "sx:history" elements
var pIHistoryXmlDOMElements = this.m_pIXmlDOMElement.selectNodes("sx:history");
// Iterate "sx:history" elements
for (var Index = 0; Index < pIHistoryXmlDOMElements.length; ++Index)
{
// Get reference to next "sx:history" element
var pIHistoryXmlDOMElement = pIHistoryXmlDOMElements(Index);
// Assign array entry by creating a new instance of FSHistoryNode and passing
// a reference to the current "sx:history" element
this.m_FSHistoryNodes[Index] = new FSHistoryNodeClass(this, pIHistoryXmlDOMElement);
}
}
// -------------------------- FSSyncNodeClass (END) --------------------------
// -------------------------- FSHistoryNodeClass (BEGIN) --------------------------
function FSHistoryNodeClass(i_FSSyncNode, i_pIHistoryXmlDOMElement)
{
// Assign m_FSSyncNode member variable
this.m_FSSyncNode = i_FSSyncNode;
// Assign m_pIXmlDOMElement member variable
this.m_pIXmlDOMElement = i_pIHistoryXmlDOMElement;
// Validate m_pIXmlDOMElement member variable
if (this.m_pIXmlDOMElement == null)
{
WScript.Echo("Unable to find 'sx:history' element for 'sx:sync' element where id='" + this.m_FSSyncNode.m_ID + "'");
return;
}
// Assign m_Sequence member variable
this.m_Sequence = this.m_pIXmlDOMElement.getAttribute("sequence");
// Assign m_When member variable
this.m_When = this.m_pIXmlDOMElement.getAttribute("when");
// Assign m_By member variable
this.m_By = this.m_pIXmlDOMElement.getAttribute("by");
// Validate that either m_When or m_By member variable have been assigned
if ((this.m_When == null) && (this.m_By == null))
{
WScript.Echo("Unable to find 'when' or 'by' attribute in 'sx:history' element for 'sx:sync' element where id='" + this.m_FSSyncNode.m_ID + "'");
WScript.Quit(0);
}
}
// -------------------------- FSHistoryNodeClass (BEGIN) --------------------------
function PopulateFSNodesFromXmlDOMElement(i_Hashtable, i_pIXmlDOMElement)
{
// Get "entry" elements
var pIItemXmlDOMElements = i_pIXmlDOMElement.selectNodes("entry");
// Iterate "entry" elements
for (var Index = 0; Index < pIItemXmlDOMElements.length; ++Index)
{
// Get reference to next "entry" element
var pIItemXmlDOMElement = pIItemXmlDOMElements(Index);
// Create new instance of FSNodeClass
var FSNode = new FSNodeClass(pIItemXmlDOMElement);
// Get reference to FSSyncNode
var FSSyncNode = FSNode.m_FSSyncNode;
// Add FSNode to hashtable using FSSyncNode's id as key
i_Hashtable[FSSyncNode.m_ID] = FSNode;
}
}
function MergeFSNodes(i_LocalFSNode, i_IncomingFSNode)
{
// Create collection for local item and local item's conflicts
var LocalItemCollection = new Array();
// Created clone of local FSNode
var ClonedLocalFSNode = CloneFSNode(i_LocalFSNode);
// Get reference to local FSSyncNode
var ClonedLocalFSSyncNode = ClonedLocalFSNode.m_FSSyncNode;
// Populate collection with clone of local item's conflicts
for (var Index = 0; Index < ClonedLocalFSSyncNode.m_FSConflictNodes.length; ++Index)
LocalItemCollection[LocalItemCollection.length] = CloneFSNode(ClonedLocalFSSyncNode.m_FSConflictNodes[Index]);
// See if "sx:conflicts" element exists, if so remove it
if (ClonedLocalFSSyncNode.m_pIConflictsXmlDOMElement != null)
ClonedLocalFSSyncNode.m_pIConflictsXmlDOMElement.parentNode.removeChild(ClonedLocalFSSyncNode.m_pIConflictsXmlDOMElement);
// Populate collection with clone of local item
LocalItemCollection[LocalItemCollection.length] = ClonedLocalFSNode;
// Create collection for incoming item and incoming item's conflicts
var IncomingItemCollection = new Array();
// Created clone of incoming FSNode
var ClonedIncomingFSNode = CloneFSNode(i_IncomingFSNode);
// Get reference to incoming FSSyncNode
var ClonedIncomingFSSyncNode = ClonedIncomingFSNode.m_FSSyncNode;
// Populate collection with clone of incoming item's conflicts
for (var Index = 0; Index < ClonedIncomingFSSyncNode.m_FSConflictNodes.length; ++Index)
IncomingItemCollection[IncomingItemCollection.length] = CloneFSNode(ClonedIncomingFSSyncNode.m_FSConflictNodes[Index]);
// See if "sx:conflicts" element exists, if so remove it
if (ClonedIncomingFSSyncNode.m_pIConflictsXmlDOMElement != null)
ClonedIncomingFSSyncNode.m_pIConflictsXmlDOMElement.parentNode.removeChild(ClonedIncomingFSSyncNode.m_pIConflictsXmlDOMElement);
// Populate collection with clone of incoming item
IncomingItemCollection[IncomingItemCollection.length] = ClonedIncomingFSNode;
// Create collection for merge result
var MergeResultItemCollection = new Array();
var WinnerFSNode = null;
// Process collections using local item collection as outer collection
// and incoming item collection as inner collection
WinnerFSNode = ProcessCollections(LocalItemCollection, IncomingItemCollection, MergeResultItemCollection, WinnerFSNode);
// Process collections using incoming item collection as outer collection
// and local item collection as inner collection
WinnerFSNode = ProcessCollections(IncomingItemCollection, LocalItemCollection, MergeResultItemCollection, WinnerFSNode);
// Get reference to winner's FSSyncNode
var WinnerFSSyncNode = WinnerFSNode.m_FSSyncNode;
// If the "noconflicts" attribute is true, or if there is only one
// item in the merge result collection (i.e. the winner), then we are
// done processing
if (WinnerFSSyncNode.m_NoConflicts || (MergeResultItemCollection.length == 1))
return WinnerFSNode;
// Create "sx:conflicts" element for winner
var pIWinnerConflictsXmlDOMElement = g_pIOutputAtomXmlDOMDocument.createElement("sx:conflicts");
// Append "sx:conflicts" element to winner's "sx:sync" element
WinnerFSSyncNode.m_pIXmlDOMElement.appendChild(pIWinnerConflictsXmlDOMElement);
// Get reference to winner's conflict nodes
var WinnerFSConflictNodes = WinnerFSSyncNode.m_FSConflictNodes;
// Create empty array to hold winner's conflict nodes
WinnerFSConflictNodes = new Array();
// Iterate items in merge result collection
for (var Index = 0; Index < MergeResultItemCollection.length; ++Index)
{
// Get next item in merge result collection
var MergeResultItem = MergeResultItemCollection[Index];
// If the merge result item matches the winner item, just
// continue the loop
if (0 == CompareFSNodes(WinnerFSNode, MergeResultItem))
continue;
// Get reference to merge result item's element
var pIMergeResultItemXmlDOMElement = MergeResultItemCollection[Index].m_pIXmlDOMElement;
// Append merge result's element to winner's "sx:conflicts" element
pIWinnerConflictsXmlDOMElement.appendChild(pIMergeResultItemXmlDOMElement);
// Add new item to winner's conflict nodes
WinnerFSConflictNodes[WinnerFSConflictNodes.length] = new FSNodeClass(pIMergeResultItemXmlDOMElement);
}
return WinnerFSNode;
}
function ProcessCollections(i_OuterFSNodeCollection, i_InnerFSNodeCollection, io_MergeFSNodeCollection, i_WinnerFSNode)
{
// Iterate outer FSNode collection
for (var OuterFSNodeCollectionIndex = 0; OuterFSNodeCollectionIndex < i_OuterFSNodeCollection.length; ++OuterFSNodeCollectionIndex)
{
// Get next FSNode in outer collection
var OuterFSNode = i_OuterFSNodeCollection[OuterFSNodeCollectionIndex];
// Get reference to outer FSSyncNode
var OuterFSSyncNode = OuterFSNode.m_FSSyncNode;
var OuterFSNodeSubsumed = false;
// Iterate inner FSNode collection
for (var InnerFSNodeCollectionIndex = 0; InnerFSNodeCollectionIndex < i_InnerFSNodeCollection.length; ++InnerFSNodeCollectionIndex)
{
// Get next FSNode in inner collection
var InnerFSNode = i_InnerFSNodeCollection[InnerFSNodeCollectionIndex];
// Check value of inner FSNode exists - if not then
// just continue loop
if (InnerFSNode == null)
continue;
// Get reference to inner FSSyncNode
var InnerFSSyncNode = InnerFSNode.m_FSSyncNode;
// Get the topmost "sx:history" element for the outer FSSyncNode
var OuterFSHistoryNode = OuterFSNode.m_FSSyncNode.m_FSHistoryNodes[0];
// Iterate FSHistoryNodes for inner FSSyncNode
for (var HistoryIndex = 0; HistoryIndex < InnerFSSyncNode.m_FSHistoryNodes.length; ++HistoryIndex)
{
// Get next FSHistoryNode
var InnerFSHistoryNode = InnerFSSyncNode.m_FSHistoryNodes[HistoryIndex];
// See if "by" attribute exists for outer FSHistoryNode and if
// it does, see if it's value matches "by" attribute value for
// inner FSHistoryNode
if ((OuterFSHistoryNode.m_By != null) && (OuterFSHistoryNode.m_By == InnerFSHistoryNode.m_By))
{
// See if "sequence" attribute for the inner FSHistoryNode
// is greater than or equal to the "sequence" attribute for
// the outer FSHistoryNode
if (InnerFSHistoryNode.m_Sequence >= OuterFSHistoryNode.m_Sequence)
{
// Indicate subsumption
OuterFSNodeSubsumed = true;
}
// Stop iterating FSHistoryNodes
break;
}
// See if "by" attribute does not exist for both outer FSHistoryNode
// and inner FSHistoryNode
else if ((OuterFSHistoryNode.m_By == null) && (InnerFSHistoryNode.m_By == null))
{
// See if "when" attribute exists for both outer FSHistoryNode
// and inner FSHistoryNode
if ((InnerFSHistoryNode.m_When != null) && (OuterFSHistoryNode.m_When != null))
{
// See if normalized dates match - if so then the outer FSNode
// is subsumed
if (InnerFSHistoryNode.m_When == OuterFSHistoryNode.m_When)
{
// Indicate subsumption
OuterFSNodeSubsumed = true;
// Stop iterating FSHistoryNodes
break;
}
}
}
}
// Check for subsumption
if (OuterFSNodeSubsumed)
{
// Stop iterating inner FSNodes
break;
}
}
// Check for subsumption
if (OuterFSNodeSubsumed)
{
// Remove outer FSNode from outer FSNode collection
i_OuterFSNodeCollection[OuterFSNodeCollectionIndex] = null;
// Continue iterating outer FSNodes
continue;
}
// See if outer FSSyncNode has any FSConflictNodes
if (OuterFSSyncNode.m_FSConflictNodes.length > 0)
{
// Remove the "sx:conflicts" sub-element for outer
// FSSyncNode
OuterFSSyncNode.m_pIConflictsXmlDOMElement.parentNode.removeChild(OuterFSSyncNode.m_pIConflictsXmlDOMElement);
}
// Add the outer FSNode to the merge result collection
io_MergeFSNodeCollection[io_MergeFSNodeCollection.length] = OuterFSNode;
// See if winner FSNode has not been assigned yet or
// if the outer FSNode represents a more recent update
// than that of the current winner FSNode
if ((i_WinnerFSNode == null) || (-1 == CompareFSNodes(i_WinnerFSNode, OuterFSNode)))
{
// Assign the outer FSNode as the winner FSNode
i_WinnerFSNode = OuterFSNode;
}
}
return i_WinnerFSNode;
}
function CompareFSNodes(i_FSNode1, i_FSNode2)
{
// This function compares the two FSNodes and returns:
// 1 if i_FSNode1 is newer than i_FSNode2
// -1 if i_FSNode2 is newer than i_FSNode1
// 0 if FSNodes are equal
// null if FSNodes are equal but conflict data is different
//
// Get reference to FSSyncNode for i_FSNode1
var FSSyncNode1 = i_FSNode1.m_FSSyncNode;
// Get reference to FSSyncNode for i_FSNode2
var FSSyncNode2 = i_FSNode2.m_FSSyncNode;
// Compare "updates" attributes - if they are equal then do subsequent checks
if (FSSyncNode1.m_Updates == FSSyncNode2.m_Updates)
{
// Get reference to topmost FSHistoryNode for FSSyncNode1
var FSHistoryNode1 = FSSyncNode1.m_FSHistoryNodes[0];
// Get reference to topmost FSHistoryNode for FSSyncNode2
var FSHistoryNode2 = FSSyncNode2.m_FSHistoryNodes[0];
// See if "when" attribute exist for either FSHistoryNode
if ((FSHistoryNode1.m_When != null) || (FSHistoryNode2.m_When != null))
{
// See if "when" attribute exist for both FSHistoryNodes
if ((FSHistoryNode1.m_When != null) && (FSHistoryNode2.m_When != null))
{
// Compare date values - since we use RFC3339 values, we can use
// string comparison when comparing datetimes
if (FSHistoryNode1.m_When > FSHistoryNode2.m_When)
{
// FSHistoryNode1 node has a later "when" attribute, so i_FSNode1
// is newer
return 1;
}
else if (FSHistoryNode2.m_When > FSHistoryNode1.m_When)
{
// FSHistoryNode2 node has a later "when" attribute, so i_FSNode2
// is newer
return -1;
}
else
{
// Same "when" attribute value for both FSHistoryNodes - try further
// checking below
}
}
else if (FSHistoryNode1.m_When != null)
{
// FSHistoryNode1 has a "when" attribute but FSHistoryNode2 does not, so
// i_FSNode1 is newer
return 1;
}
else
{
// FSHistoryNode2 has a "when" attribute but FSHistoryNode1 does not, so
// i_FSNode2 is newer
return -1;
}
}
else
{
// Neither FSHistoryNode has "when" attribute - try further checking below
}
// See if "by" attribute exist for either FSHistoryNode
if ((FSHistoryNode1.m_By != null) || (FSHistoryNode2.m_By != null))
{
// See if "by" attribute exist for both FSHistoryNodes
if ((FSHistoryNode1.m_By != null) && (FSHistoryNode2.m_By != null))
{
// Compare "by" values
if (FSHistoryNode1.m_By > FSHistoryNode2.m_By)
{
// FSHistoryNode1 node has a later "by" attribute, so i_FSNode1
// is newer
return 1;
}
else if (FSHistoryNode1.m_By < FSHistoryNode2.m_By)
{
// FSHistoryNode2 node has a later "by" attribute, so i_FSNode2
// is newer
return -1;
}
else
{
// Same "by" attribute value for both FSHistoryNodes - so we must
// compare conflict items
// If number of conflict item nodes is different, items are equal
// but conflict item data is different
if (FSSyncNode1.m_FSConflictNodes.length != FSSyncNode2.m_FSConflictNodes.length)
return null;
// Check if conflict item nodes exist
if (FSSyncNode1.m_FSConflictNodes.length > 0)
{
// Iterate conflict nodes for item 1
for (var Index1 = 0; Index1 < FSSyncNode1.m_FSConflictNodes.length; ++Index)
{
var MatchingConflictItem = false;
// Get reference to next conflict node for item 1
var ConflictNode1 = FSSyncNode1.m_FSConflictNodes[Index1];
// Iterate conflict nodes for item 2
for (var Index2 = 0; Index2 < FSSyncNode2.m_FSConflictNodes.length; ++Index)
{
// Get reference to next conflict node for item 2
var ConflictNode2 = FSSyncNode2.m_FSConflictNodes[Index2];
// Compare conflict nodes
if (0 == CompareFSNodes(ConflictNode1, ConflictNode2))
{
MatchingConflictItem = true;
break;
}
}
}
if (!MatchingConflictItem)
{
// No matching conflict item - so items are equal but conflict
// item data is different
return null;
}
}
// Items are equal
return 0;
}
}
}
else if (FSHistoryNode1.m_By != null)
{
// FSHistoryNode1 has a "by" attribute but FSHistoryNode2 does not, so
// i_FSNode1 is newer
return 1;
}
else if (FSHistoryNode2.m_By != null)
{
// FSHistoryNode2 has a "by" attribute but FSHistoryNode1 does not, so
// i_FSNode2 is newer
return -1;
}
else
{
// Neither FSHistoryNode has "by" attribute - so we can't tell which
// FSNode is newer
return 0;
}
}
else if (FSSyncNode1.m_Updates> FSSyncNode2.m_Updates)
{
// FSSyncNode1 has a later "updates" attribute, so i_FSNode1 is newer
return 1;
}
else
{
// FSSyncNode2 has a later "updates" attribute, so i_FSNode2 is newer
return -1;
}
}
function CloneFSNode(i_FSNode)
{
// Get reference to original FSNode's XmlDOMElement
var pIXmlDOMElement = i_FSNode.m_pIXmlDOMElement;
// Create (deep copy) clone of XmlDOMElement
var pIClonedXmlDOMElement = pIXmlDOMElement.cloneNode(true);
// Create new instance of FSNode
var ClonedFSNode = new FSNodeClass(pIClonedXmlDOMElement);
// Return new instance of FSNode
return ClonedFSNode;
}
function DisplayUsage(i_Text)
{
var Text = "Usage:\r\nfsAtomMerge.js [LocalPath] [IncomingPath]\r\n\r\nParameters:\r\n LocalPath=fully qualified filename of local Atom document (required)\r\n IncomingPath=fully qualified filename for incoming Atom document (required)\r\n";
// If text was provided, prepend it to default usage text
if ((i_Text != null) && (i_Text != ""))
Text = i_Text + "\r\n\r\n" + Text;
WScript.Echo(Text);
}