[OLINGO-147] Initial OData V3 JsvaScript library contribution
diff --git a/BuildProcessTemplates/DefaultTemplate.11.1.xaml b/BuildProcessTemplates/DefaultTemplate.11.1.xaml
new file mode 100644
index 0000000..bf54edf
--- /dev/null
+++ b/BuildProcessTemplates/DefaultTemplate.11.1.xaml
@@ -0,0 +1,543 @@
+<Activity mc:Ignorable="sad" x:Class="TfsBuild.Process" xmlns="http://schemas.microsoft.com/netfx/2009/xaml/activities" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mt="clr-namespace:Microsoft.TeamFoundation;assembly=Microsoft.TeamFoundation.Common" xmlns:mtbc="clr-namespace:Microsoft.TeamFoundation.Build.Client;assembly=Microsoft.TeamFoundation.Build.Client" xmlns:mtbw="clr-namespace:Microsoft.TeamFoundation.Build.Workflow;assembly=Microsoft.TeamFoundation.Build.Workflow" xmlns:mtbwa="clr-namespace:Microsoft.TeamFoundation.Build.Workflow.Activities;assembly=Microsoft.TeamFoundation.Build.Workflow" xmlns:mtbwt="clr-namespace:Microsoft.TeamFoundation.Build.Workflow.Tracking;assembly=Microsoft.TeamFoundation.Build.Workflow" xmlns:mttbb="clr-namespace:Microsoft.TeamFoundation.TestImpact.BuildIntegration.BuildActivities;assembly=Microsoft.TeamFoundation.TestImpact.BuildIntegration" xmlns:mtvc="clr-namespace:Microsoft.TeamFoundation.VersionControl.Client;assembly=Microsoft.TeamFoundation.VersionControl.Client" xmlns:mtvco="clr-namespace:Microsoft.TeamFoundation.VersionControl.Common;assembly=Microsoft.TeamFoundation.VersionControl.Common" xmlns:mva="clr-namespace:Microsoft.VisualBasic.Activities;assembly=System.Activities" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:sad="http://schemas.microsoft.com/netfx/2009/xaml/activities/presentation" xmlns:sad1="clr-namespace:System.Activities.Debugger;assembly=System.Activities" xmlns:scg="clr-namespace:System.Collections.Generic;assembly=mscorlib" xmlns:sl="clr-namespace:System.Linq;assembly=System.Core" xmlns:this="clr-namespace:TfsBuild;" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
+  <x:Members>
+    <x:Property Name="BuildSettings" Type="InArgument(mtbwa:BuildSettings)" />
+    <x:Property Name="TestSpecs" Type="InArgument(mtbwa:TestSpecList)" />
+    <x:Property Name="BuildNumberFormat" Type="InArgument(x:String)" />
+    <x:Property Name="SolutionSpecificBuildOutputs" Type="InArgument(x:Boolean)" />
+    <x:Property Name="CleanWorkspace" Type="InArgument(mtbwa:CleanWorkspaceOption)" />
+    <x:Property Name="RunCodeAnalysis" Type="InArgument(mtbwa:CodeAnalysisOption)" />
+    <x:Property Name="SourceAndSymbolServerSettings" Type="InArgument(mtbwa:SourceAndSymbolServerSettings)" />
+    <x:Property Name="AgentSettings" Type="InArgument(mtbwa:AgentSettings)" />
+    <x:Property Name="AssociateChangesetsAndWorkItems" Type="InArgument(x:Boolean)" />
+    <x:Property Name="CreateWorkItem" Type="InArgument(x:Boolean)" />
+    <x:Property Name="MSBuildArguments" Type="InArgument(x:String)" />
+    <x:Property Name="MSBuildPlatform" Type="InArgument(mtbwa:ToolPlatform)" />
+    <x:Property Name="MSBuildMultiProc" Type="InArgument(x:Boolean)" />
+    <x:Property Name="PerformTestImpactAnalysis" Type="InArgument(x:Boolean)" />
+    <x:Property Name="CreateLabel" Type="InArgument(x:Boolean)" />
+    <x:Property Name="DisableTests" Type="InArgument(x:Boolean)" />
+    <x:Property Name="GetVersion" Type="InArgument(x:String)" />
+    <x:Property Name="PrivateDropLocation" Type="InArgument(x:String)" />
+    <x:Property Name="Verbosity" Type="InArgument(mtbw:BuildVerbosity)" />
+    <x:Property Name="Metadata" Type="mtbw:ProcessParameterMetadataCollection" />
+    <x:Property Name="SupportedReasons" Type="mtbc:BuildReason" />
+    <x:Property Name="BuildProcessVersion" Type="x:String" />
+  </x:Members>
+  <this:Process.BuildSettings>[New Microsoft.TeamFoundation.Build.Workflow.Activities.BuildSettings()]</this:Process.BuildSettings>
+  <this:Process.DisableTests>[False]</this:Process.DisableTests>
+  <this:Process.TestSpecs>[New Microsoft.TeamFoundation.Build.Workflow.Activities.TestSpecList(New Microsoft.TeamFoundation.Build.Workflow.Activities.AgileTestPlatformSpec("**\*test*.dll"))]</this:Process.TestSpecs>
+  <this:Process.BuildNumberFormat>["$(BuildDefinitionName)_$(Date:yyyyMMdd)$(Rev:.r)"]</this:Process.BuildNumberFormat>
+  <this:Process.SolutionSpecificBuildOutputs>[False]</this:Process.SolutionSpecificBuildOutputs>
+  <this:Process.AssociateChangesetsAndWorkItems>[True]</this:Process.AssociateChangesetsAndWorkItems>
+  <this:Process.CreateWorkItem>[True]</this:Process.CreateWorkItem>
+  <this:Process.CleanWorkspace>[Microsoft.TeamFoundation.Build.Workflow.Activities.CleanWorkspaceOption.All]</this:Process.CleanWorkspace>
+  <this:Process.MSBuildArguments>
+    <InArgument x:TypeArguments="x:String" />
+  </this:Process.MSBuildArguments>
+  <this:Process.RunCodeAnalysis>[Microsoft.TeamFoundation.Build.Workflow.Activities.CodeAnalysisOption.AsConfigured]</this:Process.RunCodeAnalysis>
+  <this:Process.MSBuildMultiProc>[True]</this:Process.MSBuildMultiProc>
+  <this:Process.MSBuildPlatform>[Microsoft.TeamFoundation.Build.Workflow.Activities.ToolPlatform.Auto]</this:Process.MSBuildPlatform>
+  <this:Process.PerformTestImpactAnalysis>[True]</this:Process.PerformTestImpactAnalysis>
+  <this:Process.SourceAndSymbolServerSettings>[New Microsoft.TeamFoundation.Build.Workflow.Activities.SourceAndSymbolServerSettings(True, Nothing)]</this:Process.SourceAndSymbolServerSettings>
+  <this:Process.CreateLabel>[True]</this:Process.CreateLabel>
+  <this:Process.GetVersion>
+    <InArgument x:TypeArguments="x:String" />
+  </this:Process.GetVersion>
+  <this:Process.AgentSettings>[New Microsoft.TeamFoundation.Build.Workflow.Activities.AgentSettings() With {.MaxWaitTime = New System.TimeSpan(4, 0, 0), .MaxExecutionTime = New System.TimeSpan(0, 0, 0), .TagComparison = Microsoft.TeamFoundation.Build.Workflow.Activities.TagComparison.MatchExactly }]</this:Process.AgentSettings>
+  <this:Process.Verbosity>[Microsoft.TeamFoundation.Build.Workflow.BuildVerbosity.Normal]</this:Process.Verbosity>
+  <this:Process.Metadata>
+    <mtbw:ProcessParameterMetadataCollection>
+      <mtbw:ProcessParameterMetadata BrowsableWhen="EditingDefinition" Category="#300 Advanced" DisplayName="MSBuild Multi-Proc" Description="Enable MSBuid Multi-proc to build your solutions' projects in parallel, when possible, using all available processors on the build server." ParameterName="MSBuildMultiProc" />
+      <mtbw:ProcessParameterMetadata BrowsableWhen="EditingDefinition" Category="#300 Advanced" DisplayName="Solution Specific Build Outputs" Description="True will put build outputs into folders based on the solution name. False will put all build outputs into the same folder." ParameterName="SolutionSpecificBuildOutputs" />
+    </mtbw:ProcessParameterMetadataCollection>
+  </this:Process.Metadata>
+  <this:Process.SupportedReasons>All</this:Process.SupportedReasons>
+  <this:Process.BuildProcessVersion>11.0</this:Process.BuildProcessVersion>  
+  <mva:VisualBasic.Settings>Assembly references and imported namespaces serialized as XML namespaces</mva:VisualBasic.Settings>
+  <Sequence mtbwt:BuildTrackingParticipant.Importance="None">
+    <Sequence.Variables>
+      <Variable x:TypeArguments="mtbc:IBuildDetail" Name="BuildDetail" />
+      <Variable x:TypeArguments="x:String"  Name="DropLocation" />
+    </Sequence.Variables>
+    <mtbwa:GetBuildDetail DisplayName="Get the Build" Result="[BuildDetail]" mtbwt:BuildTrackingParticipant.Importance="Low" />
+    <Sequence DisplayName="Update Drop Location" mtbwt:BuildTrackingParticipant.Importance="Low">
+      <mtbwa:InvokeForReason DisplayName="Update Build Number for Triggered Builds" Reason="Triggered">
+        <mtbwa:UpdateBuildNumber BuildNumberFormat="[BuildNumberFormat]" DisplayName="Update Build Number" />
+      </mtbwa:InvokeForReason>
+      <If Condition="[(Not String.IsNullOrEmpty(BuildDetail.DropLocationRoot)) AndAlso (BuildDetail.Reason And Microsoft.TeamFoundation.Build.Client.BuildReason.Triggered) = BuildDetail.Reason]" DisplayName="If Build Reason is Triggered" mtbwt:BuildTrackingParticipant.Importance="Low">
+        <If.Then>
+          <Sequence mtbwt:BuildTrackingParticipant.Importance="None">
+            <Assign x:TypeArguments="x:String" mtbwt:BuildTrackingParticipant.Importance="None" Value="[BuildDropProvider.CombinePaths(BuildDetail.DropLocationRoot, BuildDetail.BuildDefinition.Name, BuildDetail.BuildNumber)]" To="[DropLocation]" />
+            <mtbwa:SetBuildProperties DisplayName="Set Drop Location" DropLocation="[DropLocation]" PropertiesToSet="DropLocation" mtbwt:BuildTrackingParticipant.Importance="Low" />
+          </Sequence>
+        </If.Then>
+      </If>
+      <If Condition="[(Not String.IsNullOrEmpty(PrivateDropLocation)) AndAlso BuildDetail.Reason = Microsoft.TeamFoundation.Build.Client.BuildReason.ValidateShelveset]" DisplayName="If Build Reason is ValidateShelveset" mtbwt:BuildTrackingParticipant.Importance="Low">
+        <If.Then>
+          <Sequence mtbwt:BuildTrackingParticipant.Importance="None">
+            <Assign x:TypeArguments="x:String" Value="[BuildDropProvider.CombinePaths(PrivateDropLocation, BuildDetail.BuildDefinition.Name, BuildDetail.BuildNumber)]" To="[DropLocation]" mtbwt:BuildTrackingParticipant.Importance="None" />
+            <mtbwa:SetBuildProperties DisplayName="Set Drop Location for Private Build" DropLocation="[DropLocation]" PropertiesToSet="DropLocation" mtbwt:BuildTrackingParticipant.Importance="Low" />
+          </Sequence>
+        </If.Then>
+      </If>
+    </Sequence>
+    <mtbwa:AgentScope DisplayName="Run On Agent" MaxExecutionTime="[AgentSettings.MaxExecutionTime]" MaxWaitTime="[AgentSettings.MaxWaitTime]" ReservationSpec="[AgentSettings.GetAgentReservationSpec()]">
+      <mtbwa:AgentScope.Variables>
+        <Variable x:TypeArguments="mtbc:IBuildAgent" Name="BuildAgent" />
+        <Variable x:TypeArguments="mtvc:Workspace" Name="Workspace" />
+        <Variable x:TypeArguments="x:String" Name="BuildDirectory" />
+        <Variable x:TypeArguments="x:String" Default="[BuildDetail.BuildNumber]" Name="LabelName" />
+        <Variable x:TypeArguments="x:String" Name="WorkspaceName" />
+        <Variable x:TypeArguments="x:String" Name="SourcesDirectory" />
+        <Variable x:TypeArguments="x:String" Name="BinariesDirectory" />
+        <Variable x:TypeArguments="x:String" Name="TestResultsDirectory" />
+      </mtbwa:AgentScope.Variables>
+      <Sequence DisplayName="Initialize Variables" mtbwt:BuildTrackingParticipant.Importance="Low">
+        <mtbwa:GetBuildAgent DisplayName="Get the Agent" Result="[BuildAgent]" mtbwt:BuildTrackingParticipant.Importance="Low" />
+        <mtbwa:GetBuildDirectory DisplayName="Get the Build Directory" Result="[BuildDirectory]" mtbwt:BuildTrackingParticipant.Importance="Low" />
+        <Assign x:TypeArguments="x:String" DisplayName="Initialize Workspace Name" To="[WorkspaceName]" Value="[String.Format(&quot;{0}_{1}_{2}&quot;, BuildDetail.BuildDefinition.Id, Microsoft.TeamFoundation.LinkingUtilities.DecodeUri(BuildAgent.Uri.AbsoluteUri).ToolSpecificId, BuildAgent.ServiceHost.Name)]" mtbwt:BuildTrackingParticipant.Importance="Low" />
+        <Assign x:TypeArguments="x:String" DisplayName="Initialize Sources Directory" To="[SourcesDirectory]" Value="[String.Format(&quot;{0}\Sources&quot;, BuildDirectory)]" mtbwt:BuildTrackingParticipant.Importance="Low" />
+        <Assign x:TypeArguments="x:String" DisplayName="Initialize Binaries Directory" To="[BinariesDirectory]" Value="[String.Format(&quot;{0}\Binaries&quot;, BuildDirectory)]" mtbwt:BuildTrackingParticipant.Importance="Low" />
+        <Assign x:TypeArguments="x:String" DisplayName="Initialize TestResults Directory" To="[TestResultsDirectory]" Value="[String.Format(&quot;{0}\TestResults&quot;, BuildDirectory)]" mtbwt:BuildTrackingParticipant.Importance="Low" />
+        <If Condition="[Not BuildSettings.HasPlatformConfigurations]" DisplayName="If Not BuildSettings.HasPlatformConfigurations" mtbwt:BuildTrackingParticipant.Importance="Low">
+          <If.Then>
+            <AddToCollection x:TypeArguments="mtbwa:PlatformConfiguration" DisplayName="Use Default Platform Configuration" Collection="[BuildSettings.PlatformConfigurations]" Item="[Microsoft.TeamFoundation.Build.Workflow.Activities.PlatformConfiguration.Default]" mtbwt:BuildTrackingParticipant.Importance="Low" />
+          </If.Then>
+        </If>
+        <If Condition="[WorkspaceName.Length &gt; Microsoft.TeamFoundation.VersionControl.Common.RepositoryConstants.MaxWorkspaceNameSize]" DisplayName="If WorkspaceName &gt; MaxSize" mtbwt:BuildTrackingParticipant.Importance="Low">
+          <If.Then>
+            <Sequence mtbwt:BuildTrackingParticipant.Importance="None">
+              <mtbwa:WriteBuildWarning DisplayName="Write Workspace Size Warning" Message="[String.Format(&quot;The workspace name '{0}' exceeds the maximum allowed limit of '{1}' characters. Truncating it to match the maximum limit.&quot;, WorkspaceName, Microsoft.TeamFoundation.VersionControl.Common.RepositoryConstants.MaxWorkspaceNameSize)]" />
+              <Assign x:TypeArguments="x:String" DisplayName="Truncate WorkspaceName to MaxSize" To="[WorkspaceName]" Value="[WorkspaceName.Substring(0, Microsoft.TeamFoundation.VersionControl.Common.RepositoryConstants.MaxWorkspaceNameSize).TrimEnd()]" mtbwt:BuildTrackingParticipant.Importance="Low" />
+            </Sequence>
+          </If.Then>
+        </If>
+      </Sequence>
+      <Sequence DisplayName="Initialize Workspace" mtbwt:BuildTrackingParticipant.Importance="Low">
+        <mtbwa:DeleteDirectory Directory="[TestResultsDirectory]" DisplayName="Delete Test Results Directory" Recursive="[True]" mtbwt:BuildTrackingParticipant.Importance="Low" />
+        <If Condition="[Not CleanWorkspace = Microsoft.TeamFoundation.Build.Workflow.Activities.CleanWorkspaceOption.None]" DisplayName="If Not CleanWorkspace = CleanWorkspaceOption.None" mtbwt:BuildTrackingParticipant.Importance="Low">
+          <If.Then>
+            <mtbwa:DeleteDirectory Directory="[BinariesDirectory]" DisplayName="Delete Binaries Directory" mtbwt:BuildTrackingParticipant.Importance="Low" />
+          </If.Then>
+        </If>
+        <If Condition="[CleanWorkspace = Microsoft.TeamFoundation.Build.Workflow.Activities.CleanWorkspaceOption.All]" DisplayName="If CleanWorkspace = CleanWorkspaceOption.All" mtbwt:BuildTrackingParticipant.Importance="Low">
+          <If.Then>
+            <Sequence DisplayName="Delete Workspace and Sources Directory" mtbwt:BuildTrackingParticipant.Importance="Low">
+              <mtbwa:DeleteWorkspace DeleteLocalItems="[True]" DisplayName="Delete Workspace" Name="[WorkspaceName]" mtbwt:BuildTrackingParticipant.Importance="Low" />
+              <mtbwa:DeleteDirectory Directory="[SourcesDirectory]" DisplayName="Delete Sources Directory" mtbwt:BuildTrackingParticipant.Importance="Low" />
+            </Sequence>
+          </If.Then>
+        </If>
+        <mtbwa:CreateWorkspace BuildDirectory="[BuildDirectory]" Comment="[&quot;Workspace Created by Team Build&quot;]" DisplayName="Create Workspace" Name="[WorkspaceName]" Result="[Workspace]" SourcesDirectory="[SourcesDirectory]" />
+        <If Condition="[CleanWorkspace = Microsoft.TeamFoundation.Build.Workflow.Activities.CleanWorkspaceOption.Outputs]" DisplayName="If CleanWorkspace = CleanWorkspaceOption.Outputs" mtbwt:BuildTrackingParticipant.Importance="Low">
+          <If.Then>
+            <ForEach x:TypeArguments="mtbwa:PlatformConfiguration" DisplayName="For Each Configuration in BuildSettings.PlatformConfigurations" Values="[BuildSettings.PlatformConfigurations]" mtbwt:BuildTrackingParticipant.Importance="Low">
+              <ActivityAction x:TypeArguments="mtbwa:PlatformConfiguration">
+                <ActivityAction.Argument>
+                  <DelegateInArgument x:TypeArguments="mtbwa:PlatformConfiguration" Name="platformConfiguration" />
+                </ActivityAction.Argument>
+                <Sequence DisplayName="Clean Configuration">
+                  <If Condition="[BuildSettings.HasProjectsToBuild]" DisplayName="If BuildSettings.HasProjectsToBuild" mtbwt:BuildTrackingParticipant.Importance="Low">
+                    <If.Then>
+                      <ForEach x:TypeArguments="x:String" DisplayName="For Each Project in BuildSettings.ProjectsToBuild" Values="[BuildSettings.ProjectsToBuild]" mtbwt:BuildTrackingParticipant.Importance="Low">
+                        <ActivityAction x:TypeArguments="x:String">
+                          <ActivityAction.Argument>
+                            <DelegateInArgument x:TypeArguments="x:String" Name="serverBuildProjectItem" />
+                          </ActivityAction.Argument>
+                          <Sequence DisplayName="Clean Project" mtbwt:BuildTrackingParticipant.Importance="Low">
+                            <Sequence.Variables>
+                              <Variable x:TypeArguments="x:String" Name="localBuildProjectItem" />
+                            </Sequence.Variables>
+                            <mtbwa:ConvertWorkspaceItem DisplayName="Convert Server Paths to Local Paths" Input="[serverBuildProjectItem]" Result="[localBuildProjectItem]" Workspace="[Workspace]" mtbwt:BuildTrackingParticipant.Importance="Low" />
+                            <If Condition="[System.IO.File.Exists(localBuildProjectItem)]" DisplayName="If File.Exists(Project)" mtbwt:BuildTrackingParticipant.Importance="Low">
+                              <If.Then>
+                                <mtbwa:MSBuild CommandLineArguments="[String.Format(&quot;/p:SkipInvalidConfigurations=true {0}&quot;, MSBuildArguments)]" Configuration="[platformConfiguration.Configuration]" DisplayName="Run MSBuild for Project" GenerateVSPropsFile="[True]" MaxProcesses="[If (MSBuildMultiProc, 0, 1)]" OutDir="[BinariesDirectory]" Platform="[platformConfiguration.Platform]" Project="[localBuildProjectItem]" Targets="[New String() { &quot;Clean&quot; }]" TargetsNotLogged="[New String() {&quot;GetNativeManifest&quot;, &quot;GetCopyToOutputDirectoryItems&quot;, &quot;GetTargetPath&quot;}]" ToolPlatform="[MSBuildPlatform]" Verbosity="[Verbosity]" />
+                              </If.Then>
+                            </If>
+                          </Sequence>
+                        </ActivityAction>
+                      </ForEach>
+                    </If.Then>
+                  </If>
+                </Sequence>
+              </ActivityAction>
+            </ForEach>
+          </If.Then>
+        </If>
+        <mtbwa:SyncWorkspace DisplayName="Get Workspace" VersionOverride="[GetVersion]" Workspace="[Workspace]">
+          <mtbwa:SyncWorkspace.RequestsFailed>
+            <ActivityAction x:TypeArguments="scg:ICollection(mtbc:IQueuedBuild)">
+              <ActivityAction.Argument>
+                <DelegateInArgument x:TypeArguments="scg:ICollection(mtbc:IQueuedBuild)" Name="failedRequests" />
+              </ActivityAction.Argument>
+              <mtbwa:RetryRequests Behavior="[Microsoft.TeamFoundation.Build.Workflow.Activities.RetryBehavior.DoNotBatch]" DisplayName="Mark Requests for Retry" Requests="[failedRequests]" mtbwt:BuildTrackingParticipant.Importance="Low" />
+            </ActivityAction>
+          </mtbwa:SyncWorkspace.RequestsFailed>
+        </mtbwa:SyncWorkspace>
+      </Sequence>
+      <If Condition="[CreateLabel]" DisplayName="If CreateLabel" mtbwt:BuildTrackingParticipant.Importance="Low">
+        <If.Then>
+          <mtbwa:InvokeForReason DisplayName="Create and Set Label for non-Shelveset Builds" Reason="Manual, IndividualCI, BatchedCI, Schedule, ScheduleForced, UserCreated">
+            <mtbwa:LabelWorkspace Comment="[&quot;Label Created by Team Build&quot;]" DisplayName="Create Label" Name="[LabelName]" Scope="[String.Format(&quot;$/{0}&quot;, BuildDetail.BuildDefinition.TeamProject)]" Workspace="[Workspace]" />
+            <mtbwa:SetBuildProperties DisplayName="Set Label on BuildDetail" LabelName="[String.Format(&quot;{0}@$/{1}&quot;, LabelName, BuildDetail.BuildDefinition.TeamProject)]" PropertiesToSet="LabelName" mtbwt:BuildTrackingParticipant.Importance="Low" />
+          </mtbwa:InvokeForReason>
+        </If.Then>
+        <If.Else>
+          <mtbwa:WriteBuildMessage DisplayName="Write Message" Message="Not Labeling sources" Importance="[Microsoft.TeamFoundation.Build.Client.BuildMessageImportance.High]" />
+        </If.Else>
+      </If>
+      <TryCatch DisplayName="Try Compile, Test, and Associate Changesets and Work Items" mtbwt:BuildTrackingParticipant.Importance="Low">
+        <TryCatch.Finally>
+          <Sequence DisplayName="Revert Workspace and Copy Files to Drop Location" mtbwt:BuildTrackingParticipant.Importance="Low">
+            <mtbwa:InvokeForReason DisplayName="Revert Workspace for Shelveset Builds" Reason="CheckInShelveset, ValidateShelveset">
+              <mtbwa:RevertWorkspace DisplayName="Revert Workspace" Workspace="[Workspace]" />
+            </mtbwa:InvokeForReason>
+            <If  Condition="[Not String.IsNullOrEmpty(DropLocation)]" DisplayName="If DropLocation is Set" mtbwt:BuildTrackingParticipant.Importance="Low">
+              <If.Then>
+                <mtbwa:CopyDirectory DisplayName="Drop Files to Drop Location" Source="[BinariesDirectory]" Destination="[DropLocation]" />
+              </If.Then>
+            </If>
+          </Sequence>
+        </TryCatch.Finally>
+        <TryCatch.Try>
+          <Sequence mtbwt:BuildTrackingParticipant.Importance="None">
+            <Sequence.Variables>
+              <Variable x:TypeArguments="s:Exception" Name="compilationException" />
+              <Variable x:TypeArguments="scg:IList(mtvc:Changeset)" Name="associatedChangesets" />
+              <Variable x:TypeArguments="s:Boolean" Name="treatTestFailureAsBuildFailure" />
+            </Sequence.Variables>
+            <Parallel DisplayName="Compile, Test, and Associate Changesets and Work Items">
+              <TryCatch DisplayName="Try Compile and Test" mtbwt:BuildTrackingParticipant.Importance="Low">
+                <TryCatch.Try>
+                  <Sequence DisplayName="Compile and Test">
+                    <ForEach x:TypeArguments="mtbwa:PlatformConfiguration" DisplayName="For Each Configuration in BuildSettings.PlatformConfigurations" Values="[BuildSettings.PlatformConfigurations]" mtbwt:BuildTrackingParticipant.Importance="Low">
+                      <ActivityAction x:TypeArguments="mtbwa:PlatformConfiguration">
+                        <ActivityAction.Argument>
+                          <DelegateInArgument x:TypeArguments="mtbwa:PlatformConfiguration" Name="platformConfiguration" />
+                        </ActivityAction.Argument>
+                        <Sequence DisplayName="Compile and Test for Configuration" mtbwt:BuildTrackingParticipant.Importance="Low">
+                          <Sequence.Variables>
+                            <Variable x:TypeArguments="x:String" Name="outputDirectory" />
+                            <Variable x:TypeArguments="x:String" Name="logFileDropLocation" />
+                          </Sequence.Variables>
+                          <Sequence DisplayName="Initialize Variables" mtbwt:BuildTrackingParticipant.Importance="Low">
+                            <Assign x:TypeArguments="x:String" DisplayName="Create OutputDirectory Per Platform and Configuration" To="[outputDirectory]" Value="[If (platformConfiguration.IsEmpty Or BuildSettings.PlatformConfigurations.Count = 1, BinariesDirectory, If (platformConfiguration.IsPlatformEmptyOrAnyCpu, BinariesDirectory + &quot;\&quot; + platformConfiguration.Configuration, BinariesDirectory + &quot;\&quot; + platformConfiguration.Platform + &quot;\&quot; + platformConfiguration.Configuration))]" mtbwt:BuildTrackingParticipant.Importance="Low" />
+                            <If Condition="[Not String.IsNullOrEmpty(DropLocation)]" DisplayName="If DropLocation is Set" mtbwt:BuildTrackingParticipant.Importance="Low">
+                              <If.Then>
+                                <Assign x:TypeArguments="x:String" DisplayName="Initialize LogFile Drop Location" To="[logFileDropLocation]" Value="[If (platformConfiguration.IsEmpty Or BuildSettings.PlatformConfigurations.Count = 1, BuildDropProvider.CombinePaths(DropLocation, &quot;logs&quot;), If (platformConfiguration.IsPlatformEmptyOrAnyCpu, BuildDropProvider.CombinePaths(DropLocation, &quot;logs&quot;, platformConfiguration.Configuration), BuildDropProvider.CombinePaths(DropLocation, &quot;logs&quot;, platformConfiguration.Platform, platformConfiguration.Configuration)))]" mtbwt:BuildTrackingParticipant.Importance="Low" />
+                              </If.Then>
+                            </If>
+                          </Sequence>
+                          <If Condition="[BuildSettings.HasProjectsToBuild]" DisplayName="If BuildSettings.HasProjectsToBuild" mtbwt:BuildTrackingParticipant.Importance="Low">
+                            <If.Then>
+                              <ForEach x:TypeArguments="x:String" DisplayName="For Each Project in BuildSettings.ProjectsToBuild" Values="[BuildSettings.ProjectsToBuild]" mtbwt:BuildTrackingParticipant.Importance="Low">
+                                <ActivityAction x:TypeArguments="x:String">
+                                  <ActivityAction.Argument>
+                                    <DelegateInArgument x:TypeArguments="x:String" Name="serverBuildProjectItem" />
+                                  </ActivityAction.Argument>
+                                  <TryCatch DisplayName="Try to Compile the Project" mtbwt:BuildTrackingParticipant.Importance="Low">
+                                    <TryCatch.Try>
+                                      <Sequence DisplayName="Compile the Project" mtbwt:BuildTrackingParticipant.Importance="Low">
+                                        <Sequence.Variables>
+                                          <Variable x:TypeArguments="x:String" Name="localProject" />
+                                          <Variable x:TypeArguments="x:String" Name="outputDirectoryPerProject" Default="[outputDirectory]" />
+                                        </Sequence.Variables>
+                                        <mtbwa:ConvertWorkspaceItem DisplayName="Convert Server Path to Local Path" Input="[serverBuildProjectItem]" Result="[localProject]" Workspace="[Workspace]" mtbwt:BuildTrackingParticipant.Importance="Low" />
+                                        <If Condition="[SolutionSpecificBuildOutputs]" DisplayName="If Build Outputs are Solution-Specific" mtbwt:BuildTrackingParticipant.Importance="Low">
+                                          <If.Then>
+                                            <Sequence DisplayName="Update Output Directory" mtbwt:BuildTrackingParticipant.Importance="Low">
+                                              <Assign x:TypeArguments="x:String" DisplayName="Set Solution-Specific Output Directory" To="[outputDirectoryPerProject]" Value="[System.IO.Path.Combine(outputDirectory, System.IO.Path.GetFileNameWithoutExtension(localProject))]" mtbwt:BuildTrackingParticipant.Importance="Low" />
+                                              <If DisplayName="If Output Directory Exists" Condition="[System.IO.Directory.Exists(outputDirectoryPerProject)]" mtbwt:BuildTrackingParticipant.Importance="Low">
+                                                <If.Then>
+                                                  <mtbwa:WriteBuildWarning DisplayName="Write Duplicate Project Names Warning" Message="[String.Format(&quot;{0} conflicts with another solution/project. Build outputs for solutions/projects with the same name will be copied to the same directory. To separate the build outputs, change the name of one of the solutions/projects.&quot;, System.IO.Path.GetFileNameWithoutExtension(localProject))]" />
+                                                </If.Then>
+                                              </If>
+                                            </Sequence>
+                                          </If.Then>
+                                        </If>
+                                        <mtbwa:MSBuild CommandLineArguments="[String.Format(&quot;/p:SkipInvalidConfigurations=true {0}&quot;, MSBuildArguments)]" Configuration="[platformConfiguration.Configuration]" DisplayName="Run MSBuild for Project" GenerateVSPropsFile="[True]" LogFileDropLocation="[logFileDropLocation]" MaxProcesses="[If (MSBuildMultiProc, 0, 1)]" OutDir="[outputDirectoryPerProject]" Platform="[platformConfiguration.Platform]" Project="[localProject]" RunCodeAnalysis="[RunCodeAnalysis]" TargetsNotLogged="[New String() {&quot;GetNativeManifest&quot;, &quot;GetCopyToOutputDirectoryItems&quot;, &quot;GetTargetPath&quot;}]" ToolPlatform="[MSBuildPlatform]" Verbosity="[Verbosity]" />
+                                      </Sequence>
+                                    </TryCatch.Try>
+                                    <TryCatch.Catches>
+                                      <Catch x:TypeArguments="s:Exception">
+                                        <ActivityAction x:TypeArguments="s:Exception">
+                                          <ActivityAction.Argument>
+                                            <DelegateInArgument x:TypeArguments="s:Exception" Name="ex" />
+                                          </ActivityAction.Argument>
+                                          <Sequence DisplayName="Handle Exception">
+                                            <Sequence.Variables>
+                                              <Variable x:TypeArguments="scg:ICollection(mtbc:IQueuedBuild)" Name="failedRequests" />
+                                            </Sequence.Variables>
+                                            <mtbwa:SetBuildProperties CompilationStatus="[Microsoft.TeamFoundation.Build.Client.BuildPhaseStatus.Failed]" DisplayName="Set CompilationStatus to Failed" PropertiesToSet="CompilationStatus" mtbwt:BuildTrackingParticipant.Importance="Low" />
+                                            <If Condition="[CreateWorkItem]" DisplayName="If CreateWorkItem" mtbwt:BuildTrackingParticipant.Importance="Low">
+                                              <If.Then>
+                                                <mtbwa:InvokeForReason DisplayName="Create Work Item for non-Shelveset Builds" Reason="Manual, IndividualCI, BatchedCI, Schedule, ScheduleForced, UserCreated">
+                                                  <mtbwa:OpenWorkItem AssignedTo="[BuildDetail.RequestedFor]" Comment="[&quot;This work item was created by TFS Build on a build failure.&quot;]" CustomFields="[New Dictionary(Of String, String) From { {&quot;System.Reason&quot;, &quot;Build Failure&quot;}, {&quot;Microsoft.VSTS.TCM.ReproSteps&quot;, &quot;Start the build using TFS Build&quot;}, {&quot;Priority&quot;, &quot;1&quot;}, {&quot;Severity&quot;, &quot;1 - Critical&quot;} }]" DisplayName="Create Work Item" Title="[String.Format(&quot;Build Failure in Build: {0}&quot;, BuildDetail.BuildNumber)]" Type="[&quot;Bug&quot;]" />
+                                                </mtbwa:InvokeForReason>
+                                              </If.Then>
+                                            </If>
+                                            <mtbwa:GetApprovedRequests DisplayName="Get Requests Approved for Check In" Result="[failedRequests]" mtbwt:BuildTrackingParticipant.Importance="None" />
+                                            <mtbwa:RetryRequests Behavior="[Microsoft.TeamFoundation.Build.Workflow.Activities.RetryBehavior.DoNotBatch]" DisplayName="Mark Requests for Retry" Requests="[failedRequests]" mtbwt:BuildTrackingParticipant.Importance="Low" />
+                                            <Rethrow DisplayName="Rethrow the exception so the build will stop" mtbwt:BuildTrackingParticipant.Importance="Low" />
+                                          </Sequence>
+                                        </ActivityAction>
+                                      </Catch>
+                                    </TryCatch.Catches>
+                                  </TryCatch>
+                                </ActivityAction>
+                              </ForEach>
+                            </If.Then>
+                          </If>
+                          <If Condition="[Not DisableTests]" DisplayName="If Not DisableTests" mtbwt:BuildTrackingParticipant.Importance="Low">
+                            <If.Then>
+                              <Sequence DisplayName="Run Tests" mtbwt:BuildTrackingParticipant.Importance="Low">
+                                <If Condition="[Not TestSpecs Is Nothing]" DisplayName="If Not TestSpecs Is Nothing" mtbwt:BuildTrackingParticipant.Importance="Low">
+                                  <If.Then>
+                                    <ForEach x:TypeArguments="mtbwa:TestSpec" DisplayName="For Each TestSpec in TestSpecs" Values="[TestSpecs]" mtbwt:BuildTrackingParticipant.Importance="Low">
+                                      <ActivityAction x:TypeArguments="mtbwa:TestSpec">
+                                        <ActivityAction.Argument>
+                                          <DelegateInArgument x:TypeArguments="mtbwa:TestSpec" Name="spec" />
+                                        </ActivityAction.Argument>
+                                        <TryCatch DisplayName="Try Run Tests" mtbwt:BuildTrackingParticipant.Importance="Low">
+                                          <TryCatch.Try>
+                                            <If Condition="[TypeOf spec Is Microsoft.TeamFoundation.Build.Workflow.Activities.AgileTestPlatformSpec]" DisplayName="If spec Is AgileTestPlatformSpec" mtbwt:BuildTrackingParticipant.Importance="None">
+                                              <If.Then>
+                                                <Sequence DisplayName="Run Visual Studio Test Runner for Test Sources" mtbwt:BuildTrackingParticipant.Importance="Low">
+                                                  <Sequence.Variables>
+                                                    <Variable x:TypeArguments="mtbwa:AgileTestPlatformSpec" Name="agileTestPlatformAssembly" />
+                                                    <Variable x:TypeArguments="scg:IEnumerable(x:String)" Name="agileTestPlatformAssemblies" />
+                                                  </Sequence.Variables>
+                                                  <Assign x:TypeArguments="mtbwa:AgileTestPlatformSpec" DisplayName="Assign spec to agileTestPlatformAssembly" To="[agileTestPlatformAssembly]" Value="[DirectCast(spec, Microsoft.TeamFoundation.Build.Workflow.Activities.AgileTestPlatformSpec)]" mtbwt:BuildTrackingParticipant.Importance="Low" />
+                                                  <mtbwa:FindMatchingFiles DisplayName="Find Visual Studio Test Platform Test Assemblies" MatchPattern="[String.Format(&quot;{0}\{1}&quot;, outputDirectory, agileTestPlatformAssembly.AssemblyFileSpec)]" Result="[agileTestPlatformAssemblies]" mtbwt:BuildTrackingParticipant.Importance="Low" />
+                                                  <If Condition="[agileTestPlatformAssemblies.Count() &gt; 0]" DisplayName="If Visual Studio Test Platform Test Assemblies Found" mtbwt:BuildTrackingParticipant.Importance="Low">
+                                                    <If.Then>
+                                                      <If Condition="[agileTestPlatformAssembly.HasRunSettingsFile]" DisplayName="If agileTestPlatformAssembly.HasRunSettingsFile" mtbwt:BuildTrackingParticipant.Importance="Low">
+                                                        <If.Then>
+                                                          <Sequence DisplayName="Find Run Settings File And Run Visual Studio Test Runner" mtbwt:BuildTrackingParticipant.Importance="Low">
+                                                            <Sequence.Variables>
+                                                              <Variable x:TypeArguments="x:String" Name="localRunSettings" />
+                                                            </Sequence.Variables>
+                                                            <mtbwa:GenerateRunSettings DisplayName="Generate Run Settings File" RunSettingsForTestRun="[agileTestPlatformAssembly.RunSettingsForTestRun]" Result="[localRunSettings]" Workspace="[Workspace]" mtbwt:BuildTrackingParticipant.Importance="Low" />
+                                                            <mtbwa:RunTests DisplayName="Run Visual Studio Test Runner for Test Sources" RunName="[agileTestPlatformAssembly.RunName]" Flavor="[platformConfiguration.Configuration]" Platform="[platformConfiguration.Platform]" TestSources="[agileTestPlatformAssemblies]" RunSettings="[localRunSettings]" TestCaseFilter="[agileTestPlatformAssembly.TestCaseFilter]" ExecutionPlatform="[agileTestPlatformAssembly.ExecutionPlatform]" />
+                                                          </Sequence>
+                                                        </If.Then>
+                                                        <If.Else>
+                                                          <mtbwa:RunTests DisplayName="Run Visual Studio Test Runner for Test Sources" RunName="[agileTestPlatformAssembly.RunName]" Flavor="[platformConfiguration.Configuration]" Platform="[platformConfiguration.Platform]" TestSources="[agileTestPlatformAssemblies]" TestCaseFilter="[agileTestPlatformAssembly.TestCaseFilter]" ExecutionPlatform="[agileTestPlatformAssembly.ExecutionPlatform]" />
+                                                        </If.Else>
+                                                      </If>
+                                                    </If.Then>
+                                                  </If>
+                                                </Sequence>
+                                              </If.Then>
+                                              <If.Else>
+                                                <If Condition="[TypeOf spec Is Microsoft.TeamFoundation.Build.Workflow.Activities.TestMetadataFileSpec]" DisplayName="If spec Is TestMetadataFileSpec" mtbwt:BuildTrackingParticipant.Importance="None">
+                                                  <If.Then>
+                                                    <Sequence DisplayName="Run MSTest for Metadata File">
+                                                      <Sequence.Variables>
+                                                        <Variable x:TypeArguments="mtbwa:TestMetadataFileSpec" Name="testMetadataFile" />
+                                                        <Variable x:TypeArguments="x:String" Name="localTestMetadata" />
+                                                      </Sequence.Variables>
+                                                      <Assign x:TypeArguments="mtbwa:TestMetadataFileSpec" DisplayName="Assign spec to testMetadataFile" To="[testMetadataFile]" Value="[DirectCast(spec, Microsoft.TeamFoundation.Build.Workflow.Activities.TestMetadataFileSpec)]" mtbwt:BuildTrackingParticipant.Importance="Low" />
+                                                      <mtbwa:ConvertWorkspaceItem DisplayName="Convert Server Path to Local Path" Input="[testMetadataFile.MetadataFileName]" Result="[localTestMetadata]" Workspace="[Workspace]" mtbwt:BuildTrackingParticipant.Importance="Low" />
+                                                      <mtbwa:MSTest RunTitle="[testMetadataFile.RunName]" Category="[testMetadataFile.CategoryFilter]" DisplayName="Run MSTest for Metadata File" Flavor="[platformConfiguration.Configuration]" MaxPriority="[testMetadataFile.MaximumPriority]" MinPriority="[testMetadataFile.MinimumPriority]" PathToResultsFilesRoot="[TestResultsDirectory]" Platform="[platformConfiguration.Platform]" SearchPathRoot="[outputDirectory]" TestLists="[testMetadataFile.TestLists]" TestMetadata="[localTestMetadata]" TestSettings="[String.Empty]" CommandLineArguments="[testMetadataFile.MSTestCommandLineArgs]" />
+                                                    </Sequence>
+                                                  </If.Then>
+                                                  <If.Else>
+                                                    <Sequence DisplayName="Run MSTest for Test Assemblies" mtbwt:BuildTrackingParticipant.Importance="Low">
+                                                      <Sequence.Variables>
+                                                        <Variable x:TypeArguments="mtbwa:TestAssemblySpec" Name="testAssembly" />
+                                                        <Variable x:TypeArguments="scg:IEnumerable(x:String)" Name="testAssemblies" />
+                                                        <Variable x:TypeArguments="x:String" Default="[String.Empty]" Name="testFlavor" />
+                                                        <Variable x:TypeArguments="x:String" Default="[String.Empty]" Name="testPlatform" />
+                                                      </Sequence.Variables>
+                                                      <Assign x:TypeArguments="mtbwa:TestAssemblySpec" DisplayName="Assign spec to testAssembly" To="[testAssembly]" Value="[DirectCast(spec, Microsoft.TeamFoundation.Build.Workflow.Activities.TestAssemblySpec)]" mtbwt:BuildTrackingParticipant.Importance="Low" />
+                                                      <mtbwa:FindMatchingFiles DisplayName="Find Test Assemblies" MatchPattern="[String.Format(&quot;{0}\{1}&quot;, outputDirectory, testAssembly.AssemblyFileSpec)]" Result="[testAssemblies]" mtbwt:BuildTrackingParticipant.Importance="Low" />
+                                                      <If Condition="[testAssemblies.Count() &gt; 0]" DisplayName="If Test Assemblies Found" mtbwt:BuildTrackingParticipant.Importance="Low">
+                                                        <If.Then>
+                                                          <If Condition="[testAssembly.HasTestSettingsFile]" DisplayName="If testAssembly.HasTestSettingsFile" mtbwt:BuildTrackingParticipant.Importance="Low">
+                                                            <If.Then>
+                                                              <Sequence DisplayName="Find Test Settings File And Run MSTest" mtbwt:BuildTrackingParticipant.Importance="Low">
+                                                                <Sequence.Variables>
+                                                                  <Variable x:TypeArguments="x:String" Name="localTestSettings" />
+                                                                </Sequence.Variables>
+                                                                <mtbwa:ConvertWorkspaceItem DisplayName="Convert Server Path to Local Path" Input="[testAssembly.TestSettingsFileName]" Result="[localTestSettings]" Workspace="[Workspace]" mtbwt:BuildTrackingParticipant.Importance="Low" />
+                                                                <mtbwa:MSTest RunTitle="[testAssembly.RunName]" Category="[testAssembly.CategoryFilter]" DisplayName="Run MSTest for Test Assemblies" Flavor="[platformConfiguration.Configuration]" MaxPriority="[testAssembly.MaximumPriority]" MinPriority="[testAssembly.MinimumPriority]" PathToResultsFilesRoot="[TestResultsDirectory]" Platform="[platformConfiguration.Platform]" SearchPathRoot="[outputDirectory]" TestContainers="[testAssemblies]" TestSettings="[localTestSettings]" CommandLineArguments="[testAssembly.MSTestCommandLineArgs]" />
+                                                              </Sequence>
+                                                            </If.Then>
+                                                            <If.Else>
+                                                              <mtbwa:MSTest RunTitle="[testAssembly.RunName]" Category="[testAssembly.CategoryFilter]" DisplayName="Run MSTest for Test Assemblies" Flavor="[platformConfiguration.Configuration]" MaxPriority="[testAssembly.MaximumPriority]" MinPriority="[testAssembly.MinimumPriority]" PathToResultsFilesRoot="[TestResultsDirectory]" Platform="[platformConfiguration.Platform]" SearchPathRoot="[outputDirectory]" TestContainers="[testAssemblies]" CommandLineArguments="[testAssembly.MSTestCommandLineArgs]" />
+                                                            </If.Else>
+                                                          </If>
+                                                        </If.Then>
+                                                      </If>
+                                                    </Sequence>
+                                                  </If.Else>
+                                                </If>
+                                              </If.Else>
+                                            </If>
+                                          </TryCatch.Try>
+                                          <TryCatch.Catches>
+                                            <Catch x:TypeArguments="s:Exception">
+                                              <ActivityAction x:TypeArguments="s:Exception">
+                                                <ActivityAction.Argument>
+                                                  <DelegateInArgument x:TypeArguments="s:Exception" Name="testException" />
+                                                </ActivityAction.Argument>
+                                                <Sequence DisplayName="Handle Test Run Exception">
+                                                  <Sequence.Variables>
+                                                    <Variable x:TypeArguments="scg:ICollection(mtbc:IQueuedBuild)" Name="failedRequests" />
+                                                  </Sequence.Variables>
+                                                  <If Condition="[Not (TypeOf testException Is Microsoft.TeamFoundation.Build.Workflow.Activities.TestFailureException)]" DisplayName="If testException is NOT TestFailureException" mtbwt:BuildTrackingParticipant.Importance="Low">
+                                                    <If.Then>
+                                                      <mtbwa:WriteBuildError DisplayName="Write Test Failure Message" Message="[testException.Message]" />
+                                                    </If.Then>
+                                                  </If>
+                                                  <mtbwa:SetBuildProperties DisplayName="Set TestStatus to Failed" PropertiesToSet="TestStatus" TestStatus="[Microsoft.TeamFoundation.Build.Client.BuildPhaseStatus.Failed]" mtbwt:BuildTrackingParticipant.Importance="Low" />
+                                                  <If Condition="[spec.FailBuildOnFailure]" DisplayName="If spec.FailBuildOnFailure" mtbwt:BuildTrackingParticipant.Importance="Low">
+                                                    <If.Then>
+                                                      <Assign x:TypeArguments="s:Boolean" DisplayName="Set treatTestFailureAsBuildFailure to True" To="[treatTestFailureAsBuildFailure]" Value="[True]" mtbwt:BuildTrackingParticipant.Importance="Low" />
+                                                    </If.Then>
+                                                  </If>
+                                                  <mtbwa:GetApprovedRequests DisplayName="Get Requests Approved for Check In" Result="[failedRequests]" mtbwt:BuildTrackingParticipant.Importance="None" />
+                                                  <mtbwa:RetryRequests Behavior="[Microsoft.TeamFoundation.Build.Workflow.Activities.RetryBehavior.DoNotBatch]" DisplayName="Mark Requests for Retry" Requests="[failedRequests]" mtbwt:BuildTrackingParticipant.Importance="Low" />
+                                                </Sequence>
+                                              </ActivityAction>
+                                            </Catch>
+                                          </TryCatch.Catches>
+                                        </TryCatch>
+                                      </ActivityAction>
+                                    </ForEach>
+                                  </If.Then>
+                                </If>
+                              </Sequence>
+                            </If.Then>
+                            <If.Else>
+                              <If Condition="[(Not TestSpecs Is Nothing) And (TestSpecs.Count > 0)]" DisplayName="If TestSpecs Is Not Nothing or Empty" mtbwt:BuildTrackingParticipant.Importance="Low">
+                                <If.Then>
+                                  <mtbwa:WriteBuildWarning DisplayName="Write Warning" Message="No automated tests will be run for this build because tests have been disabled for this build definition. To enable these tests, edit this build definition and set the Disable Tests process parameter to false." />
+                                </If.Then>
+                              </If>
+                            </If.Else>
+                          </If>
+                        </Sequence>
+                      </ActivityAction>
+                    </ForEach>
+                    <If Condition="[BuildDetail.CompilationStatus = Microsoft.TeamFoundation.Build.Client.BuildPhaseStatus.Unknown]" DisplayName="If CompilationStatus = Unknown" mtbwt:BuildTrackingParticipant.Importance="Low">
+                      <If.Then>
+                        <mtbwa:SetBuildProperties CompilationStatus="[Microsoft.TeamFoundation.Build.Client.BuildPhaseStatus.Succeeded]" DisplayName="Set CompilationStatus to Succeeded" PropertiesToSet="CompilationStatus" mtbwt:BuildTrackingParticipant.Importance="Low" />
+                      </If.Then>
+                    </If>
+                    <If Condition="[BuildDetail.TestStatus = Microsoft.TeamFoundation.Build.Client.BuildPhaseStatus.Unknown]" DisplayName="If TestStatus = Unknown" mtbwt:BuildTrackingParticipant.Importance="Low">
+                      <If.Then>
+                        <mtbwa:SetBuildProperties DisplayName="Set TestStatus to Succeeded" PropertiesToSet="TestStatus" TestStatus="[Microsoft.TeamFoundation.Build.Client.BuildPhaseStatus.Succeeded]" mtbwt:BuildTrackingParticipant.Importance="Low" />
+                      </If.Then>
+                    </If>
+                    <If Condition="[treatTestFailureAsBuildFailure And (BuildDetail.TestStatus = Microsoft.TeamFoundation.Build.Client.BuildPhaseStatus.Failed)]" DisplayName="If TreatTestFailureAsBuildFailure And (TestStatus = Failed)" mtbwt:BuildTrackingParticipant.Importance="Low">
+                      <If.Then>
+                        <mtbwa:SetBuildProperties DisplayName="Set Status to Failed" PropertiesToSet="Status" Status="[Microsoft.TeamFoundation.Build.Client.BuildStatus.Failed]" mtbwt:BuildTrackingParticipant.Importance="Low" />
+                      </If.Then>
+                    </If>
+                  </Sequence>
+                </TryCatch.Try>
+                <TryCatch.Catches>
+                  <Catch x:TypeArguments="s:Exception">
+                    <ActivityAction x:TypeArguments="s:Exception">
+                      <ActivityAction.Argument>
+                        <DelegateInArgument x:TypeArguments="s:Exception" Name="compilationExceptionArgument" />
+                      </ActivityAction.Argument>
+                      <Assign x:TypeArguments="s:Exception" DisplayName="Save the Compilation Exception" To="[compilationException]" Value="[compilationExceptionArgument]" mtbwt:BuildTrackingParticipant.Importance="None" />
+                    </ActivityAction>
+                  </Catch>
+                </TryCatch.Catches>
+              </TryCatch>
+              <If Condition="[AssociateChangesetsAndWorkItems]" DisplayName="If AssociateChangesetsAndWorkItems" mtbwt:BuildTrackingParticipant.Importance="Low">
+                <If.Then>
+                  <If Condition="[CreateLabel]" DisplayName="If CreateLabel and AssociateChangesetsAndWorkItems" mtbwt:BuildTrackingParticipant.Importance="Low">
+                    <If.Then>
+                      <mtbwa:InvokeForReason DisplayName="Associate Changesets and Work Items for non-Shelveset Builds" Reason="Manual, IndividualCI, BatchedCI, Schedule, ScheduleForced, UserCreated">
+                        <mtbwa:AssociateChangesetsAndWorkItems DisplayName="Associate Changesets and Work Items" Result="[associatedChangesets]" />
+                      </mtbwa:InvokeForReason>
+                    </If.Then>
+                    <If.Else>
+                      <mtbwa:WriteBuildWarning DisplayName="Write Associate Changesets and Work Items Warning" Message="Cannot Associate Changesets and Work Items because the Label Sources option is set to False." />
+                    </If.Else>
+                  </If>
+                </If.Then>
+              </If>
+            </Parallel>
+            <If Condition="[Not compilationException Is Nothing]" DisplayName="If a Compilation Exception Occurred" mtbwt:BuildTrackingParticipant.Importance="Low">
+              <If.Then>
+                <Throw DisplayName="Rethrow Compilation Exception" Exception="[compilationException]" mtbwt:BuildTrackingParticipant.Importance="Low" />
+              </If.Then>
+            </If>
+            <Parallel DisplayName="Get Impacted Tests, Index Sources and Publish Symbols">
+              <If Condition="[PerformTestImpactAnalysis]" DisplayName="If PerformTestImpactAnalysis" mtbwt:BuildTrackingParticipant.Importance="Low">
+                <If.Then>
+                  <Sequence DisplayName="Get Impacted Tests" mtbwt:BuildTrackingParticipant.Importance="Low">
+                    <Sequence.Variables>
+                      <Variable x:TypeArguments="scg:IEnumerable(x:String)" Name="assemblies" />
+                    </Sequence.Variables>
+                    <mtbwa:FindMatchingFiles DisplayName="Find Build Outputs" MatchPattern="[String.Format(&quot;{0}\**\*.dll;{0}\**\*.exe&quot;, BinariesDirectory)]" Result="[assemblies]" mtbwt:BuildTrackingParticipant.Importance="Low" />
+                    <mttbb:GetImpactedTests Assemblies="[assemblies]" AssociatedChangesets="[associatedChangesets]" BinariesRoot="[BinariesDirectory]" Build="[BuildDetail]" CodeChanges="{x:Null}" DisplayName="Get Impacted Tests" ImpactedTests="{x:Null}" Workspace="[Workspace]" />
+                  </Sequence>
+                </If.Then>
+              </If>
+              <If Condition="[SourceAndSymbolServerSettings.IndexSources Or SourceAndSymbolServerSettings.HasSymbolStorePath]" DisplayName="If SourceAndSymbolServerSettings.IndexSources Or SourceAndSymbolServerSettings.HasSymbolStorePath" mtbwt:BuildTrackingParticipant.Importance="Low">
+                <If.Then>
+                  <mtbwa:InvokeForReason DisplayName="Index Sources and Publish Symbols for Triggered Builds" Reason="Triggered">
+                    <mtbwa:InvokeForReason.Variables>
+                      <Variable x:TypeArguments="scg:IEnumerable(x:String)" Name="symbolFiles" />
+                    </mtbwa:InvokeForReason.Variables>
+                    <mtbwa:FindMatchingFiles DisplayName="Find Symbol Files" MatchPattern="[String.Format(&quot;{0}\**\*.pdb&quot;, BinariesDirectory)]" Result="[symbolFiles]" mtbwt:BuildTrackingParticipant.Importance="Low" />
+                    <If Condition="[SourceAndSymbolServerSettings.IndexSources]" DisplayName="If SourceAndSymbolServerSettings.IndexSources" mtbwt:BuildTrackingParticipant.Importance="Low">
+                      <If.Then>
+                        <TryCatch DisplayName="Try Index Sources" mtbwt:BuildTrackingParticipant.Importance="Low">
+                          <TryCatch.Try>
+                            <mtbwa:IndexSources DisplayName="Index Sources" FileList="[symbolFiles]" />
+                          </TryCatch.Try>
+                          <TryCatch.Catches>
+                            <Catch x:TypeArguments="s:Exception">
+                              <ActivityAction x:TypeArguments="s:Exception">
+                                <ActivityAction.Argument>
+                                  <DelegateInArgument x:TypeArguments="s:Exception" Name="exception" />
+                                </ActivityAction.Argument>
+                                <mtbwa:WriteBuildError DisplayName="Write Indexing Sources Error" Message="[exception.Message]" />
+                              </ActivityAction>
+                            </Catch>
+                          </TryCatch.Catches>
+                        </TryCatch>
+                      </If.Then>
+                    </If>
+                    <If Condition="[SourceAndSymbolServerSettings.HasSymbolStorePath]" DisplayName="If SourceAndSymbolServerSettings.HasSymbolStorePath" mtbwt:BuildTrackingParticipant.Importance="Low">
+                      <If.Then>
+                        <TryCatch DisplayName="Try Publish Symbols" mtbwt:BuildTrackingParticipant.Importance="Low">
+                          <TryCatch.Try>
+                            <mtbwa:SharedResourceScope DisplayName="Synchronize Access to Symbol Store" MaxExecutionTime="[TimeSpan.Zero]" MaxWaitTime="[New TimeSpan(1, 0, 0)]" ResourceName="[SourceAndSymbolServerSettings.SymbolStorePath]" mtbwt:BuildTrackingParticipant.Importance="Low">
+                              <mtbwa:PublishSymbols DisplayName="Publish Symbols" FileList="[symbolFiles]" ProductName="[BuildDetail.BuildDefinition.Name]" StorePath="[SourceAndSymbolServerSettings.SymbolStorePath]" Version="[BuildDetail.BuildNumber]" />
+                            </mtbwa:SharedResourceScope>
+                          </TryCatch.Try>
+                          <TryCatch.Catches>
+                            <Catch x:TypeArguments="s:Exception">
+                              <ActivityAction x:TypeArguments="s:Exception">
+                                <ActivityAction.Argument>
+                                  <DelegateInArgument x:TypeArguments="s:Exception" Name="exception" />
+                                </ActivityAction.Argument>
+                                <mtbwa:WriteBuildError DisplayName="Write Publishing Symbols Error" Message="[exception.Message]" />
+                              </ActivityAction>
+                            </Catch>
+                          </TryCatch.Catches>
+                        </TryCatch>
+                      </If.Then>
+                    </If>
+                  </mtbwa:InvokeForReason>
+                </If.Then>
+              </If>
+            </Parallel>
+          </Sequence>
+        </TryCatch.Try>
+      </TryCatch>
+    </mtbwa:AgentScope>
+    <mtbwa:InvokeForReason DisplayName="Check In Gated Changes for CheckInShelveset Builds" Reason="CheckInShelveset">
+      <mtbwa:CheckInGatedChanges DisplayName="Check In Gated Changes" />
+    </mtbwa:InvokeForReason>
+  </Sequence>
+</Activity>
diff --git a/BuildProcessTemplates/LabDefaultTemplate.11.xaml b/BuildProcessTemplates/LabDefaultTemplate.11.xaml
new file mode 100644
index 0000000..9e1fb0b
--- /dev/null
+++ b/BuildProcessTemplates/LabDefaultTemplate.11.xaml
@@ -0,0 +1,208 @@
+<Activity mc:Ignorable="sads sap" x:Class="TfsBuild.Process" this:Process.LabWorkflowParameters="[New Microsoft.TeamFoundation.Lab.Workflow.Activities.LabWorkflowDetails()]" this:Process.Verbosity="[Microsoft.TeamFoundation.Build.Workflow.BuildVerbosity.Normal]" this:Process.BuildNumberFormat="[&quot;$(BuildDefinitionName)_$(Date:yyyyMMdd)$(Rev:.r)&quot;]" this:Process.SupportedReasons="Manual, BatchedCI, Schedule, ScheduleForced" this:Process.TimeoutForDeploymentScriptInMinutes="30" xmlns="http://schemas.microsoft.com/netfx/2009/xaml/activities" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mt="clr-namespace:Microsoft.TeamFoundation;assembly=Microsoft.TeamFoundation.Common" xmlns:mtbc="clr-namespace:Microsoft.TeamFoundation.Build.Client;assembly=Microsoft.TeamFoundation.Build.Client" xmlns:mtbc1="clr-namespace:Microsoft.TeamFoundation.Build.Common;assembly=Microsoft.TeamFoundation.Build.Common" xmlns:mtbp="clr-namespace:Microsoft.TeamFoundation.Build.ProcessComponents;assembly=Microsoft.TeamFoundation.Build.ProcessComponents" xmlns:mtbw="clr-namespace:Microsoft.TeamFoundation.Build.Workflow;assembly=Microsoft.TeamFoundation.Build.Workflow" xmlns:mtbwa="clr-namespace:Microsoft.TeamFoundation.Build.Workflow.Activities;assembly=Microsoft.TeamFoundation.Build.Workflow" xmlns:mtbws="clr-namespace:Microsoft.TeamFoundation.Build.Workflow.Services;assembly=Microsoft.TeamFoundation.Build.Workflow" xmlns:mtlc="clr-namespace:Microsoft.TeamFoundation.Lab.Client;assembly=Microsoft.TeamFoundation.Lab.Client" xmlns:mtltc="clr-namespace:Microsoft.TeamFoundation.Lab.TestIntegration.Client;assembly=Microsoft.TeamFoundation.Lab.TestIntegration.Client" xmlns:mtlwa="clr-namespace:Microsoft.TeamFoundation.Lab.Workflow.Activities;assembly=Microsoft.TeamFoundation.Lab.Workflow.Activities" xmlns:mtlwc="clr-namespace:Microsoft.TeamFoundation.Lab.WorkflowIntegration.Client;assembly=Microsoft.TeamFoundation.Lab.WorkflowIntegration.Client" xmlns:mttbb="clr-namespace:Microsoft.TeamFoundation.TestImpact.BuildIntegration.BuildActivities;assembly=Microsoft.TeamFoundation.TestImpact.BuildIntegration" xmlns:mttc="clr-namespace:Microsoft.TeamFoundation.TestManagement.Client;assembly=Microsoft.TeamFoundation.TestManagement.Client" xmlns:mtvc="clr-namespace:Microsoft.TeamFoundation.VersionControl.Client;assembly=Microsoft.TeamFoundation.VersionControl.Client" xmlns:mva="clr-namespace:Microsoft.VisualBasic.Activities;assembly=System.Activities" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:s1="clr-namespace:System;assembly=System" xmlns:s2="clr-namespace:System;assembly=System.Core" xmlns:s3="clr-namespace:System;assembly=System.ServiceModel" xmlns:s4="clr-namespace:System;assembly=System.ComponentModel.Composition" xmlns:sa="clr-namespace:System.Activities;assembly=System.Activities" xmlns:sad="clr-namespace:System.Activities.Debugger;assembly=System.Activities" xmlns:sads="http://schemas.microsoft.com/netfx/2010/xaml/activities/debugger" xmlns:sap="http://schemas.microsoft.com/netfx/2009/xaml/activities/presentation" xmlns:scg="clr-namespace:System.Collections.Generic;assembly=mscorlib" xmlns:sl="clr-namespace:System.Linq;assembly=System.Core" xmlns:this="clr-namespace:TfsBuild" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
+  <x:Members>
+    <x:Property Name="BuildProcessVersion" Type="x:String" />
+    <x:Property Name="Metadata" Type="mtbw:ProcessParameterMetadataCollection" />
+    <x:Property Name="LabWorkflowParameters" Type="InArgument(mtlwa:LabWorkflowDetails)" />
+    <x:Property Name="Verbosity" Type="InArgument(mtbw:BuildVerbosity)" />
+    <x:Property Name="BuildNumberFormat" Type="InArgument(x:String)" />
+    <x:Property Name="SupportedReasons" Type="mtbc:BuildReason" />
+    <x:Property Name="TimeoutForDeploymentScriptInMinutes" Type="InArgument(x:Int32)">
+      <x:Property.Attributes>
+        <RequiredArgumentAttribute />
+      </x:Property.Attributes>
+    </x:Property>
+  </x:Members>
+  <this:Process.BuildProcessVersion>11.0</this:Process.BuildProcessVersion>
+  <this:Process.Metadata>
+    <mtbw:ProcessParameterMetadataCollection>
+      <mtbw:ProcessParameterMetadata BrowsableWhen="Always" Category="Misc" DisplayName="Timeout For Each Deployment Script (in Minutes)" ParameterName="TimeoutForDeploymentScriptInMinutes" />
+    </mtbw:ProcessParameterMetadataCollection>
+  </this:Process.Metadata>
+  <sap:VirtualizedContainerService.HintSize>920,3702</sap:VirtualizedContainerService.HintSize>
+  <mva:VisualBasic.Settings>Assembly references and imported namespaces serialized as XML namespaces</mva:VisualBasic.Settings>
+  <Sequence DisplayName="Application Deployment Workflow" sad:XamlDebuggerXmlReader.FileName="D:\VSTLM\src\vset\LabManager\LabCustomActivities\Templates\LabDefaultTemplate.xaml" sap:VirtualizedContainerService.HintSize="880,3662" mva:VisualBasic.Settings="Assembly references and imported namespaces serialized as XML namespaces">
+    <Sequence.Variables>
+      <Variable x:TypeArguments="mtbc:IBuildDetail" Name="BuildDetail" />
+      <Variable x:TypeArguments="x:String" Name="LabEnvironmentUri" />
+      <Variable x:TypeArguments="x:String" Name="BuildLocation" />
+      <Variable x:TypeArguments="x:Int64" Name="SnapshotId" />
+      <Variable x:TypeArguments="x:Int32" Name="QueueBuildId" />
+      <Variable x:TypeArguments="mtbc:BuildStatus" Default="[Microsoft.TeamFoundation.Build.Client.BuildStatus.Succeeded]" Name="BuildStatus" />
+      <Variable x:TypeArguments="mtbc:IBuildDetail" Name="ChildBuildDetail" />
+      <Variable x:TypeArguments="mtbc:IBuildDetail" Name="SelectedBuildDetail" />
+      <Variable x:TypeArguments="x:String" Name="BuildNumber" />
+      <Variable x:TypeArguments="mtlc:LabEnvironment" Name="LabEnvironment" />
+    </Sequence.Variables>   
+    <mtbwa:UpdateBuildNumber BuildNumberFormat="[BuildNumberFormat]" DisplayName="Update Build Number" sap:VirtualizedContainerService.HintSize="858,22" />
+    <mtbwa:GetBuildDetail DisplayName="Get Build Details" sap:VirtualizedContainerService.HintSize="858,22" Result="[BuildDetail]" />
+    <If Condition="[LabWorkflowParameters.BuildDetails.IsTeamSystemBuild = True AndAlso LabWorkflowParameters.BuildDetails.QueueNewBuild = True]" DisplayName="If Build is needed" sap:VirtualizedContainerService.HintSize="858,416">
+      <If.Then>
+        <Sequence DisplayName="Do Build" sap:VirtualizedContainerService.HintSize="301,308">
+          <sap:WorkflowViewStateService.ViewState>
+            <scg:Dictionary x:TypeArguments="x:String, x:Object">
+              <x:Boolean x:Key="IsExpanded">True</x:Boolean>
+            </scg:Dictionary>
+          </sap:WorkflowViewStateService.ViewState>
+          <mtlwa:RunWorkflow BuildController="{x:Null}" LabEnvironmentUri="{x:Null}" BuildDefinition="[LabWorkflowParameters.BuildDetails.BuildDefinitionName]" DisplayName="Start Build Workflow" sap:VirtualizedContainerService.HintSize="242,22" ProjectName="[BuildDetail.TeamProject]" Result="[QueueBuildId]" />
+          <mtlwa:WaitForWorkflow AllowPartiallySucceededBuild="True" BuildDetails="[ChildBuildDetail]" DisplayName="Wait For Build To Complete" sap:VirtualizedContainerService.HintSize="242,22" LabWorkflowType="[Microsoft.TeamFoundation.Lab.Workflow.Activities.WorkflowType.Build]" MaxConsecutiveFailuresToIgnoreDuringWaitForCompletion="[3]" MaxWaitTime="[TimeSpan.Zero]" QueueBuildId="[QueueBuildId]" RefreshInterval="[System.TimeSpan.FromMinutes(1)]" Result="[BuildStatus]" ThrowOnError="True" />
+          <Assign DisplayName="Set Build Location" sap:VirtualizedContainerService.HintSize="242,60">
+            <Assign.To>
+              <OutArgument x:TypeArguments="x:Uri">[LabWorkflowParameters.BuildDetails.BuildUri]</OutArgument>
+            </Assign.To>
+            <Assign.Value>
+              <InArgument x:TypeArguments="x:Uri">[ChildBuildDetail.Uri]</InArgument>
+            </Assign.Value>
+          </Assign>
+        </Sequence>
+      </If.Then>
+    </If>
+    <mtlwa:WriteDeploymentInformation Url="{x:Null}" DeploymentInformationType="[Microsoft.TeamFoundation.Build.Common.DeploymentInformationTypes.Deploy]" DisplayName="Update Deployment Summary" sap:VirtualizedContainerService.HintSize="858,22" Message="[String.Format(&quot;Lab environment: {0}&quot;, LabWorkflowParameters.EnvironmentDetails.LabEnvironmentName)]" />
+    <mtlwa:GetBuildLocationAndBuildNumber BuildDetails="[LabWorkflowParameters.BuildDetails]" BuildNumber="[BuildNumber]" DisplayName="Get Build Location And Build Number" sap:VirtualizedContainerService.HintSize="858,22" Result="[BuildLocation]" SelectedBuild="[SelectedBuildDetail]" />
+    <If Condition="[LabWorkflowParameters.BuildDetails.IsTeamSystemBuild = True]" DisplayName="Compute build location needed" sap:VirtualizedContainerService.HintSize="858,208">
+      <If.Then>
+        <Assign DisplayName="Compute build path" sap:VirtualizedContainerService.HintSize="291,100">
+          <Assign.To>
+            <OutArgument x:TypeArguments="x:String">[BuildLocation]</OutArgument>
+          </Assign.To>
+          <Assign.Value>
+            <InArgument x:TypeArguments="x:String">[If(LabWorkflowParameters.BuildDetails.Configuration Is Nothing, BuildLocation, If(LabWorkflowParameters.BuildDetails.Configuration.IsEmpty Or (SelectedBuildDetail.Information.GetNodesByType(Microsoft.TeamFoundation.Build.Common.InformationTypes.ConfigurationSummary, True)).Count = 1, BuildLocation, If(LabWorkflowParameters.BuildDetails.Configuration.IsPlatformEmptyOrAnyCpu, BuildLocation + "\" + LabWorkflowParameters.BuildDetails.Configuration.Configuration, BuildLocation + "\" + LabWorkflowParameters.BuildDetails.Configuration.Platform + "\" + LabWorkflowParameters.BuildDetails.Configuration.Configuration)))]</InArgument>
+          </Assign.Value>
+        </Assign>
+      </If.Then>
+    </If>
+    <If Condition="[LabWorkflowParameters.EnvironmentDetails.Disposition = Microsoft.TeamFoundation.Lab.Client.LabEnvironmentDisposition.Stored]" DisplayName="If user selected stored environment" sap:VirtualizedContainerService.HintSize="858,208">
+      <If.Then>
+        <Throw DisplayName="Indicate error" Exception="[New System.Exception(&quot;You have selected an environment that is stored in the library. Select an environment that is deployed on a team project host group.&quot;)]" sap:VirtualizedContainerService.HintSize="269,100" />
+      </If.Then>
+    </If>
+    <Assign DisplayName="Get Lab Environment Uri" sap:VirtualizedContainerService.HintSize="858,22" >
+      <Assign.To>
+        <OutArgument x:TypeArguments="x:String">[LabEnvironmentUri]</OutArgument>
+      </Assign.To>
+      <Assign.Value>
+        <InArgument x:TypeArguments="x:String">[LabWorkflowParameters.EnvironmentDetails.LabEnvironmentUri.ToString()]</InArgument>
+      </Assign.Value>
+    </Assign>
+    <mtlwa:GetLabEnvironment DisplayName="Get Lab Environment" LabEnvironmentUri="[LabEnvironmentUri]" Result="[LabEnvironment]" />
+    <If Condition="[LabWorkflowParameters.EnvironmentDetails.RevertToSnapshot = True]" DisplayName=" If Restore Snapshot" sap:VirtualizedContainerService.HintSize="858,316">
+      <If.Then>
+        <Sequence DisplayName="Restore Snapshot" sap:VirtualizedContainerService.HintSize="231,208">
+          <mtlwa:GetLabEnvironmentSnapshotId DisplayName="Get Snapshot Details" sap:VirtualizedContainerService.HintSize="200,22" LabEnvironmentUri="[LabEnvironmentUri]" Result="[SnapshotId]" SnapshotName="[LabWorkflowParameters.EnvironmentDetails.SnapshotName]" />
+          <mtlwa:RestoreLabEnvironment DisplayName="Restore Lab Environment to Snapshot" sap:VirtualizedContainerService.HintSize="200,22" LabEnvironmentUri="[LabEnvironmentUri]" SnapshotId="[SnapshotId]" />
+        </Sequence>
+      </If.Then>
+      <If.Else>
+        <Sequence DisplayName="No Clean Snapshot" >
+          <If Condition="[Not String.Equals(Microsoft.TeamFoundation.Lab.Client.LabEnvironment.UnmanagedProvider, labEnvironment.LabProvider)]" DisplayName= "If Virtual Environment">
+            <If.Then>
+              <mtlwa:WriteDeploymentInformation Url="{x:Null}" DeploymentInformationType="[Microsoft.TeamFoundation.Build.Common.DeploymentInformationTypes.Deploy]" DisplayName="Clean snapshot not specified " sap:VirtualizedContainerService.HintSize="208,208" Message="Build definition did not specify a clean snapshot. It is a best practice to use clean snapshot when running the lab workflow." />
+            </If.Then>
+          </If>
+        </Sequence>        
+      </If.Else>
+    </If>
+    <If Condition="[LabWorkflowParameters.DeploymentDetails.DeploymentNeeded = True or LabWorkflowParameters.TestParameters.RunTest = True]" DisplayName="If deployment or test needed" sap:VirtualizedContainerService.HintSize="858,1214">
+      <If.Then>
+	<mtlwa:WaitForEnvironmentReady DisplayName="Wait For Environment To Be Ready" sap:VirtualizedContainerService.HintSize="711,22" LabEnvironmentUri="[LabEnvironmentUri]" MaxWaitTime="[System.TimeSpan.FromMinutes(10)]" />
+      </If.Then>
+    </If>
+    <If Condition="[LabWorkflowParameters.DeploymentDetails.DeploymentNeeded = True]" DisplayName="If deployment needed" sap:VirtualizedContainerService.HintSize="858,1214">
+      <If.Then>
+        <Sequence DisplayName="Do deployment" sap:VirtualizedContainerService.HintSize="733,1106">                    
+          <mtlwa:ReserveEnvironmentForDeployment DisplayName="Reserve Environment For Deployment" sap:VirtualizedContainerService.HintSize="711,22" LabEnvironmentUri="[LabEnvironmentUri]" />
+            <TryCatch DisplayName="Deploy Build on Environment">
+              <TryCatch.Try>
+                <Sequence DisplayName="Deploying Build">
+                  <ForEach x:TypeArguments="x:String" DisplayName="Run Deployment scripts" sap:VirtualizedContainerService.HintSize="711,254" Values="[LabWorkflowParameters.DeploymentDetails.Scripts]">
+                    <ActivityAction x:TypeArguments="x:String">
+                      <ActivityAction.Argument>
+                        <DelegateInArgument x:TypeArguments="x:String" Name="deploymentConfigurationPair" />
+                      </ActivityAction.Argument>
+                      <mtlwa:RunDeploymentTask BuildLocation="[BuildLocation]" DeploymentScriptDetails="[deploymentConfigurationPair]" DisplayName="Run Deployment Task" sap:VirtualizedContainerService.HintSize="200,22" LabEnvironmentUri="[LabEnvironmentUri]" MaxWaitTime="[TimeSpan.FromMinutes(TimeoutForDeploymentScriptInMinutes)]" ThrowOnError="True" UseRoleForDeployment="[LabWorkflowParameters.DeploymentDetails.UseRoleForDeployment]" />
+                    </ActivityAction>
+                  </ForEach>
+                </Sequence>
+              </TryCatch.Try>
+              <TryCatch.Finally>
+                <mtlwa:ReleaseEnvironmentFromDeployment DisplayName="Release Environment From Deployment" sap:VirtualizedContainerService.HintSize="711,22" LabEnvironmentUri="[LabEnvironmentUri]" />
+              </TryCatch.Finally>
+            </TryCatch>
+          <mtlwa:WriteDeploymentInformation DeploymentInformationType="[Microsoft.TeamFoundation.Build.Common.DeploymentInformationTypes.Deploy]" DisplayName="Application Deployment Succeeded" sap:VirtualizedContainerService.HintSize="711,22" Message="[String.Format(&quot;The application was deployed successfully from the following build location:&quot;)]" Url="[BuildLocation]" />
+          <If Condition="[LabWorkflowParameters.DeploymentDetails.TakePostDeploymentSnapshot = True]" DisplayName="Post Deployment Snapshot" sap:VirtualizedContainerService.HintSize="711,626">
+            <If.Then>
+              <Sequence DisplayName="Take Post deployment Snapshot " sap:VirtualizedContainerService.HintSize="486,518">
+                <Sequence.Variables>
+                  <Variable x:TypeArguments="x:Int64" Name="PostDeploymentSnapshotChainId" />
+                  <Variable x:TypeArguments="x:String" Default="[String.Format(&quot;{0}_{1}&quot;, BuildDetail.BuildDefinition.Name, BuildDetail.BuildNumber)]" Name="PostDeploymentSnapshotName" />
+                </Sequence.Variables>
+                <If Condition="[String.IsNullOrEmpty(LabWorkflowParameters.DeploymentDetails.PostDeploymentSnapshotName) = False]" DisplayName="Check snapshot name" sap:VirtualizedContainerService.HintSize="464,208">
+                  <If.Then>
+                    <Assign sap:VirtualizedContainerService.HintSize="291,100">
+                      <Assign.To>
+                        <OutArgument x:TypeArguments="x:String">[PostDeploymentSnapshotName]</OutArgument>
+                      </Assign.To>
+                      <Assign.Value>
+                        <InArgument x:TypeArguments="x:String">[If(LabWorkflowParameters.BuildDetails.IsTeamSystemBuild = True,String.Format("{0}_{1}_{2}", LabWorkflowParameters.DeploymentDetails.PostDeploymentSnapshotName, BuildNumber,BuildDetail.BuildNumber),String.Format("{0}_{1}", LabWorkflowParameters.DeploymentDetails.PostDeploymentSnapshotName, BuildDetail.BuildNumber))]</InArgument>
+                      </Assign.Value>
+                    </Assign>
+                  </If.Then>
+                </If>
+                <mtlwa:SnapshotLabEnvironment DisplayName="Taking Post Deployment snapshot" sap:VirtualizedContainerService.HintSize="464,22" LabEnvironmentUri="[LabEnvironmentUri]" SnapshotChainId="[PostDeploymentSnapshotChainId]" SnapshotName="[PostDeploymentSnapshotName]" />
+                <mtlwa:WriteDeploymentInformation Url="{x:Null}" DeploymentInformationType="[Microsoft.TeamFoundation.Build.Common.DeploymentInformationTypes.Deploy]" DisplayName="Taking Snapshot succeeded" sap:VirtualizedContainerService.HintSize="464,22" Message="[String.Format(&quot;The following snapshot was taken after the deployment was finished: {0}&quot;, PostDeploymentSnapshotName)]" />
+                <mtlwa:WriteDeploymentInformation DeploymentInformationType="[Microsoft.TeamFoundation.Build.Common.DeploymentInformationTypes.ConnectToSnapshot]" DisplayName="Added connection link to the Snapshot" sap:VirtualizedContainerService.HintSize="464,22" Message="[PostDeploymentSnapshotName]" Url="[PostDeploymentSnapshotChainId.ToString()]" />
+              </Sequence>
+            </If.Then>
+            <If.Else>
+              <Sequence DisplayName = "No Post Deployment Snapshot"> 
+                <If Condition="[Not String.Equals(Microsoft.TeamFoundation.Lab.Client.LabEnvironment.UnmanagedProvider, labEnvironment.LabProvider)]" DisplayName= "If Virtual Environment">
+                  <If.Then>
+                    <mtlwa:WriteDeploymentInformation Url="{x:Null}" DeploymentInformationType="[Microsoft.TeamFoundation.Build.Common.DeploymentInformationTypes.Deploy]" DisplayName="Post deployment snapshot not specified" sap:VirtualizedContainerService.HintSize="200,518" Message="Build definition did not specify a post deployment snapshot. It is a best practice to take post deployment snapshot when running the lab workflow." />
+                  </If.Then>
+                </If>
+              </Sequence>                
+            </If.Else>
+          </If>
+        </Sequence>
+      </If.Then>
+    </If>
+    <If Condition="[LabWorkflowParameters.TestParameters.RunTest = True]" DisplayName="Run Tests on Environment" sap:VirtualizedContainerService.HintSize="858,604">
+      <If.Then>
+        <Sequence DisplayName="Run Tests" sap:VirtualizedContainerService.HintSize="656,498">
+          <Sequence.Variables>
+            <Variable x:TypeArguments="mtltc:TestingCapabilityInformation" Name="TestCapabilityInfo" />
+            <Variable x:TypeArguments="x:String" Name="variable1" />
+            <Variable x:TypeArguments="mtlwa:TestRunStatistics" Default="[New Microsoft.TeamFoundation.Lab.Workflow.Activities.TestRunStatistics()]" Name="TestResults" />
+          </Sequence.Variables>
+          <mtlwa:ExecuteRemoteTestRun2 MaxWaitTime="{x:Null}" TestEnvironment="{x:Null}" BuildNumber="[BuildNumber]" BuildDefinitionName="[LabWorkflowParameters.BuildDetails.BuildDefinitionName]" DisplayName="Running Tests" sap:VirtualizedContainerService.HintSize="634,22" LabEnvironmentUri="[LabEnvironmentUri]" MaxConsecutiveFailuresToIgnoreDuringWaitForCompletion="[3]" RefreshInterval="[System.TimeSpan.FromMinutes(1)]" Result="[TestResults]" TestDirectory="[BuildLocation]" TestParameters="[LabWorkflowParameters.TestParameters]" Title="[String.Format(&quot;{0}&quot;, BuildDetail.BuildNumber)]" />
+          <If Condition="[TestResults.PassedTests &lt;&gt; TestResults.TotalTests Or TestResults.TestRunStatus &lt;&gt; TestManagement.Client.TestRunState.Completed]" DisplayName="If all tests have not passed" sap:VirtualizedContainerService.HintSize="634,312">
+            <If.Then>
+              <If Condition="[(LabWorkflowParameters.BuildDetails.IsTeamSystemBuild = True AndAlso LabWorkflowParameters.BuildDetails.QueueNewBuild = True) Or (LabWorkflowParameters.DeploymentDetails.DeploymentNeeded = True)]" DisplayName="Set build status" sap:VirtualizedContainerService.HintSize="509,206">
+                <If.Then>
+                  <Assign DisplayName="Partially succeeded" sap:VirtualizedContainerService.HintSize="242,100">
+                    <Assign.To>
+                      <OutArgument x:TypeArguments="mtbc:BuildStatus">[BuildStatus]</OutArgument>
+                    </Assign.To>
+                    <Assign.Value>
+                      <InArgument x:TypeArguments="mtbc:BuildStatus">[Microsoft.TeamFoundation.Build.Client.BuildStatus.PartiallySucceeded]</InArgument>
+                    </Assign.Value>
+                  </Assign>
+                </If.Then>
+                <If.Else>
+                  <Assign DisplayName="Failed" sap:VirtualizedContainerService.HintSize="242,100">
+                    <Assign.To>
+                      <OutArgument x:TypeArguments="mtbc:BuildStatus">[BuildStatus]</OutArgument>
+                    </Assign.To>
+                    <Assign.Value>
+                      <InArgument x:TypeArguments="mtbc:BuildStatus">[Microsoft.TeamFoundation.Build.Client.BuildStatus.Failed]</InArgument>
+                    </Assign.Value>
+                  </Assign>
+                </If.Else>
+              </If>
+            </If.Then>
+          </If>
+        </Sequence>
+      </If.Then>
+    </If>
+    <mtbwa:SetBuildProperties DisplayName="Set build status" sap:VirtualizedContainerService.HintSize="858,22" PropertiesToSet="Status" Status="[BuildStatus]" />
+  </Sequence>
+</Activity>
\ No newline at end of file
diff --git a/BuildProcessTemplates/UpgradeTemplate.xaml b/BuildProcessTemplates/UpgradeTemplate.xaml
new file mode 100644
index 0000000..b3ee07f
--- /dev/null
+++ b/BuildProcessTemplates/UpgradeTemplate.xaml
@@ -0,0 +1,76 @@
+<Activity mc:Ignorable="sad" x:Class="TfsBuild.Process" xmlns="http://schemas.microsoft.com/netfx/2009/xaml/activities" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mtbc="clr-namespace:Microsoft.TeamFoundation.Build.Client;assembly=Microsoft.TeamFoundation.Build.Client" xmlns:mtbw="clr-namespace:Microsoft.TeamFoundation.Build.Workflow;assembly=Microsoft.TeamFoundation.Build.Workflow" xmlns:mtbwa="clr-namespace:Microsoft.TeamFoundation.Build.Workflow.Activities;assembly=Microsoft.TeamFoundation.Build.Workflow" xmlns:mtbwt="clr-namespace:Microsoft.TeamFoundation.Build.Workflow.Tracking;assembly=Microsoft.TeamFoundation.Build.Workflow" xmlns:mtvc="clr-namespace:Microsoft.TeamFoundation.VersionControl.Client;assembly=Microsoft.TeamFoundation.VersionControl.Client" xmlns:mva="clr-namespace:Microsoft.VisualBasic.Activities;assembly=System.Activities" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:sad="http://schemas.microsoft.com/netfx/2009/xaml/activities/presentation" xmlns:sad1="clr-namespace:System.Activities.Debugger;assembly=System.Activities" xmlns:scg="clr-namespace:System.Collections.Generic;assembly=mscorlib" xmlns:this="clr-namespace:TfsBuild;" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
+  <x:Members>
+    <x:Property Name="ConfigurationFolderPath" Type="InArgument(x:String)" />
+    <x:Property Name="AgentSettings" Type="InArgument(mtbwa:AgentSettings)" />
+    <x:Property Name="MSBuildArguments" Type="InArgument(x:String)" />
+    <x:Property Name="MSBuildPlatform" Type="InArgument(mtbwa:ToolPlatform)" />
+    <x:Property Name="DoNotDownloadBuildType" Type="InArgument(x:Boolean)" />
+    <x:Property Name="LogFilePerProject" Type="InArgument(x:Boolean)" />
+    <x:Property Name="SourcesSubdirectory" Type="InArgument(x:String)" />
+    <x:Property Name="BinariesSubdirectory" Type="InArgument(x:String)" />
+    <x:Property Name="TestResultsSubdirectory" Type="InArgument(x:String)" />
+    <x:Property Name="RecursionType" Type="InArgument(mtvc:RecursionType)" />
+    <x:Property Name="Verbosity" Type="InArgument(mtbw:BuildVerbosity)" />
+    <x:Property Name="Metadata" Type="mtbw:ProcessParameterMetadataCollection" />
+    <x:Property Name="SupportedReasons" Type="mtbc:BuildReason" />
+  </x:Members>
+  <this:Process.ConfigurationFolderPath>
+    <InArgument x:TypeArguments="x:String" />
+  </this:Process.ConfigurationFolderPath>
+  <this:Process.AgentSettings>[New Microsoft.TeamFoundation.Build.Workflow.Activities.AgentSettings() With {.MaxWaitTime = New System.TimeSpan(4, 0, 0), .MaxExecutionTime = New System.TimeSpan(0, 0, 0), .TagComparison = Microsoft.TeamFoundation.Build.Workflow.Activities.TagComparison.MatchExactly }]</this:Process.AgentSettings>
+  <this:Process.MSBuildArguments>
+    <InArgument x:TypeArguments="x:String" />
+  </this:Process.MSBuildArguments>
+  <this:Process.MSBuildPlatform>[Microsoft.TeamFoundation.Build.Workflow.Activities.ToolPlatform.Auto]</this:Process.MSBuildPlatform>
+  <this:Process.DoNotDownloadBuildType>[False]</this:Process.DoNotDownloadBuildType>
+  <this:Process.LogFilePerProject>[False]</this:Process.LogFilePerProject>
+  <this:Process.SourcesSubdirectory>
+    <InArgument x:TypeArguments="x:String" />
+  </this:Process.SourcesSubdirectory>
+  <this:Process.BinariesSubdirectory>
+    <InArgument x:TypeArguments="x:String" />
+  </this:Process.BinariesSubdirectory>
+  <this:Process.TestResultsSubdirectory>
+    <InArgument x:TypeArguments="x:String" />
+  </this:Process.TestResultsSubdirectory>
+  <this:Process.RecursionType>[Microsoft.TeamFoundation.VersionControl.Client.RecursionType.OneLevel]</this:Process.RecursionType>
+  <this:Process.Verbosity>[Microsoft.TeamFoundation.Build.Workflow.BuildVerbosity.Normal]</this:Process.Verbosity>
+  <this:Process.Metadata>
+    <mtbw:ProcessParameterMetadataCollection />
+  </this:Process.Metadata>
+  <this:Process.SupportedReasons>All</this:Process.SupportedReasons>
+  <mva:VisualBasic.Settings>Assembly references and imported namespaces serialized as XML namespaces</mva:VisualBasic.Settings>
+  <Sequence mtbwt:BuildTrackingParticipant.Importance="None">
+    <Sequence.Variables>
+      <Variable x:TypeArguments="mtbc:IBuildDetail" Name="BuildDetail" />
+    </Sequence.Variables>
+    <mtbwa:GetBuildDetail DisplayName="Get the Build" Result="[BuildDetail]" />
+    <mtbwa:InvokeForReason DisplayName="Update Build Number for Triggered Builds" Reason="Triggered">
+      <mtbwa:UpdateBuildNumber BuildNumberFormat="[&quot;$(BuildDefinitionName)_$(Date:yyyyMMdd)$(Rev:.r)&quot;]" DisplayName="Update Build Number" />
+    </mtbwa:InvokeForReason>
+    <mtbwa:AgentScope DisplayName="Run On Agent" MaxExecutionTime="[AgentSettings.MaxExecutionTime]" MaxWaitTime="[AgentSettings.MaxWaitTime]" ReservationSpec="[AgentSettings.GetAgentReservationSpec()]">
+      <mtbwa:AgentScope.Variables>
+        <Variable x:TypeArguments="x:String" Name="buildDirectory" />
+      </mtbwa:AgentScope.Variables>
+      <mtbwa:GetBuildDirectory DisplayName="Get the Build Directory" Result="[buildDirectory]" />
+      <If Condition="[Not String.IsNullOrEmpty(ConfigurationFolderPath)]" DisplayName="If Not String.IsNullOrEmpty(ConfigurationFolderPath)">
+        <If.Then>
+          <mtbwa:TfsBuild BinariesSubdirectory="[BinariesSubdirectory]" BuildDirectory="[buildDirectory]" CommandLineArguments="[MSBuildArguments]" ConfigurationFolderPath="[ConfigurationFolderPath]" DisplayName="Run TfsBuild for Configuration Folder" DoNotDownloadBuildType="[DoNotDownloadBuildType]" LogFilePerProject="[LogFilePerProject]" RecursionType="[RecursionType]" SourcesSubdirectory="[SourcesSubdirectory]" TargetsNotLogged="[New String() {&quot;GetNativeManifest&quot;, &quot;GetCopyToOutputDirectoryItems&quot;, &quot;GetTargetPath&quot;}]" TestResultsSubdirectory="[TestResultsSubdirectory]" ToolPlatform="[MSBuildPlatform]" Verbosity="[Verbosity]" />
+        </If.Then>
+      </If>
+      <If Condition="[BuildDetail.CompilationStatus = Microsoft.TeamFoundation.Build.Client.BuildPhaseStatus.Unknown]" DisplayName="If CompilationStatus = Unknown">
+        <If.Then>
+           <mtbwa:SetBuildProperties CompilationStatus="[Microsoft.TeamFoundation.Build.Client.BuildPhaseStatus.Succeeded]" DisplayName="Set CompilationStatus to Succeeded" PropertiesToSet="CompilationStatus" />
+        </If.Then>
+      </If>
+      <If Condition="[BuildDetail.TestStatus = Microsoft.TeamFoundation.Build.Client.BuildPhaseStatus.Unknown]" DisplayName="If TestStatus = Unknown">
+        <If.Then>
+          <mtbwa:SetBuildProperties DisplayName="Set TestStatus to Succeeded" PropertiesToSet="TestStatus" TestStatus="[Microsoft.TeamFoundation.Build.Client.BuildPhaseStatus.Succeeded]" />
+        </If.Then>
+      </If>
+    </mtbwa:AgentScope>
+    <mtbwa:InvokeForReason Reason="CheckInShelveset">
+      <mtbwa:CheckInGatedChanges DisplayName="Check In Gated Changes" />
+    </mtbwa:InvokeForReason>
+  </Sequence>
+</Activity>
\ No newline at end of file
diff --git a/BuildTools/ajaxmin/readme.txt b/BuildTools/ajaxmin/readme.txt
new file mode 100644
index 0000000..c74506c
--- /dev/null
+++ b/BuildTools/ajaxmin/readme.txt
@@ -0,0 +1,2 @@
+To enable the DataJS library to be minified, download the Ajax Minifier from
+http://ajaxmin.codeplex.com/ and copy the binaries into %DJSROOT%\BuildTools\ajaxmin
diff --git a/BuildTools/djsbuild.cmd b/BuildTools/djsbuild.cmd
new file mode 100644
index 0000000..0e17f63
--- /dev/null
+++ b/BuildTools/djsbuild.cmd
@@ -0,0 +1,136 @@
+@echo off
+
+rem Copyright (c) Microsoft Open Technologies, Inc.  All rights reserved.
+rem Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation 
+rem files (the "Software"), to deal  in the Software without restriction, including without limitation the rights  to use, copy,
+rem modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the 
+rem Software is furnished to do so, subject to the following conditions:
+rem 
+rem The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+rem 
+rem THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR  IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+rem WARRANTIES OF MERCHANTABILITY,  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+rem COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 
+rem ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+setlocal    
+
+
+if "%DJSROOT%" == "" (
+  echo Please set the DJSROOT environment variable for the build process.
+  exit /b 1
+)
+
+if "%DJSOUT%" == "" (
+  echo Please set the DJSOUT environment variable for the build process.
+  exit /b 1
+)
+
+rem Build overview:
+rem 1. Call msbuild to generate the webapplication bin directory.
+rem 2. All solution files are copied or merged into the output directory.
+rem 3. Internal references are removed from the merged file (eg, ODATA_INTERNAL).
+rem    This produces datajs.js.
+rem 4. The minifier is run. This produces datajs.min.js.
+rem 5. The files are renamed to the version-specific filenames.
+
+echo Invoking msbuild...
+where /Q msbuild.exe
+if errorlevel 1 (
+    echo Please add the path to msbuild.exe to the Path environment variable for the build process.
+    exit /b 1
+)
+msbuild /nologo /clp:DisableConsoleColor /target:rebuild "%DJSROOT%\JSLib\JSLib.sln"
+
+echo Building internal datajs version...
+cscript "%DJSROOT%\BuildTools\djsbuildfile.wsf" //Nologo /build-solution:true /out:"%DJSOUT%" "%DJSROOT%\JSLib\JSLib.sln"
+if errorlevel 1 (
+  exit /b %errorlevel%
+)
+
+if exist "%DJSOUT%\jslib.sln\src\datajs.merged.js" (
+  del "%DJSOUT%\jslib.sln\src\datajs.merged.js"
+  if errorlevel 1 (
+    exit /b %errorlevel%
+  )
+)
+
+ren "%DJSOUT%\jslib.sln\src\datajs.js" datajs.merged.js
+if errorlevel 1 (
+  exit /b %errorlevel%
+)
+
+echo.
+echo Removing internal references...
+cscript "%DJSROOT%\BuildTools\djsbuildfile.wsf" //Nologo /remove-internals:true /out:"%DJSOUT%\jslib.sln\src\datajs.js" "%DJSOUT%\jslib.sln\src\datajs.merged.js"
+if errorlevel 1 (
+  exit /b %errorlevel%
+)
+
+echo.
+echo Minifying output file...
+if exist "%DJSROOT%\BuildTools\ajaxmin\AjaxMin.exe" (
+  "%DJSROOT%\BuildTools\ajaxmin\AjaxMin.exe" -JS -debug:false -analyze -clobber:true "%DJSOUT%\jslib.sln\src\datajs.js" -out "%DJSOUT%\jslib.sln\src\datajs.min.body.js" > "%DJSOUT%\jslib.sln\src\datajs.js.log" 2>&1
+  if errorlevel 1 (
+    echo Error when running AjaxMin.exe on "%DJSOUT%\jslib.sln\src\datajs.js"
+    echo See log at "%DJSOUT%\jslib.sln\src\datajs.js.log"
+    exit /b 1
+  )
+
+  copy "%DJSROOT%\BuildTools\djslicense.js" /B +"%DJSOUT%\jslib.sln\src\datajs.min.body.js" /B "%DJSOUT%\jslib.sln\src\datajs.min.js" /B
+  del /Q "%DJSOUT%\jslib.sln\src\datajs.min.body.js"
+  
+) else (
+  type "%DJSROOT%\BuildTools\ajaxmin\readme.txt"
+  echo.
+  echo Using non-minified file instead.
+  copy /y "%DJSOUT%\jslib.sln\src\datajs.js" "%DJSOUT%\jslib.sln\src\datajs.min.js"
+)
+
+
+call "%DJSROOT%\BuildTools\djsbuildver.cmd"
+
+if exist "%DJSOUT%\jslib.sln\src\datajs-%DJSVER%.js" (
+  del /q "%DJSOUT%\jslib.sln\src\datajs-%DJSVER%.js"
+  if errorlevel 1 (
+    exit /b %errorlevel%
+  )
+)
+
+if exist "%DJSOUT%\jslib.sln\src\datajs-%DJSVER%.min.js" (
+  del /q "%DJSOUT%\jslib.sln\src\datajs-%DJSVER%.min.js"
+  if errorlevel 1 (
+    exit /b %errorlevel%
+  )
+)
+
+ren "%DJSOUT%\jslib.sln\src\datajs.js" datajs-%DJSVER%.js
+ren "%DJSOUT%\jslib.sln\src\datajs.min.js" datajs-%DJSVER%.min.js
+
+echo.
+echo Redirecting unit tests to internal file...
+cscript "%DJSROOT%\BuildTools\djspatchtests.wsf" //Nologo /in:"%DJSOUT%\jslib.sln\tests" /replace-script:"../src/datajs.merged.js"
+if errorlevel 1 (
+   exit /b %errorlevel%
+)
+
+echo.
+echo Copying bin folder...
+robocopy "%DJSROOT%\JSLib\bin" "%DJSOUT%\jslib.sln\bin" /E /R:10 1>NUL
+
+if errorlevel 8 (
+    exit /b %errorlevel%
+)
+
+if errorlevel 16 (
+    exit /b %errorlevel%
+)
+
+echo.
+echo The built files are:
+echo %DJSOUT%\jslib.sln\src\datajs-%DJSVER%.js     - development version
+echo %DJSOUT%\jslib.sln\src\datajs-%DJSVER%.min.js - minified version
+
+endlocal
+
+exit /b 0
\ No newline at end of file
diff --git a/BuildTools/djsbuildfile.js b/BuildTools/djsbuildfile.js
new file mode 100644
index 0000000..d31ae85
--- /dev/null
+++ b/BuildTools/djsbuildfile.js
@@ -0,0 +1,186 @@
+/// <reference path="djscommon.js" />
+
+// Copyright (c) Microsoft Open Technologies, Inc.  All rights reserved.
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal  in the Software without restriction, including without limitation the rights  to use, copy,
+// modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR  IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// WARRANTIES OF MERCHANTABILITY,  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+var markerFormats = {};
+
+markerFormats[".js"] = "// MARKER";
+markerFormats[".html"] = "<!-- MARKER -->";
+markerFormats[".htm"] = markerFormats[".html"];
+
+function MakeMarker(ext, marker) {
+    /// <summary>Builds the appropriate marker string based on the file extension.</summary>
+    /// <param name="ext" type="String">Extension of the file for which the marker string is going to be generated.</param>
+    /// <param name="marker" type="String">Text to be contained in the marker.</param>
+    /// <returns type="String">Specific marker string.</returns>
+
+    var format = markerFormats[ext.toLowerCase()];
+    if (format) {
+        return format.replace("MARKER", marker);
+    }
+    return null;
+}
+
+function BuildFromProject(path, outBasePath) {
+    // Read the csproj.
+    WScript.Echo("Reading project " + path);
+    var xml = new ActiveXObject("Msxml2.DOMDocument.6.0");
+    xml.load(path);
+    xml.setProperty("SelectionNamespaces", "xmlns:msb='http://schemas.microsoft.com/developer/msbuild/2003'");
+    xml.setProperty("SelectionLanguage", "XPath");
+
+    var files = xml.selectNodes("//msb:ItemGroup/*[not(self::msb:Reference)]/@Include");
+    var fileIncludes = {};
+    var i, len, filePath;
+
+    // Get file dependencies.
+    for (i = 0, len = files.length; i < len; i++) {
+        filePath = PathCombine(PathGetDirectory(path), files[i].value);
+        if (FileExists(filePath)) {
+            var includes = GetFileIncludes(filePath);
+            var j, includeLen;
+            for (j = 0, includeLen = includes.length; j < includeLen; j++) {
+                fileIncludes[includes[j]] = {};
+            }
+        } else if (!FolderExists(filePath)) {
+            throw { message: "path doesn't exist " + filePath };
+        }
+    }
+
+    // Build the files that are not in the dependency list.
+    for (i = 0, len = files.length; i < len; i++) {
+        filePath = PathCombine(PathGetDirectory(path), files[i].value);
+        var outputPath = PathCombine(outBasePath, files[i].value);
+        if (!fileIncludes[filePath] && !FolderExists(filePath)) {
+            BuildFile(filePath, outputPath);
+        }
+    }
+}
+
+function GetFileIncludes(path) {
+    var content = ReadAllTextFile(path);
+    var lines = StringSplit(content, "\r\n");
+    var result = [];
+    var i, len;
+    for (i = 0, len = lines.length; i < len; i++) {
+        var line = lines[i];
+        var includeIndex = line.indexOf("// INCLUDE:");
+        if (includeIndex !== -1) {
+            var anotherPath = line.substr(includeIndex + 11);
+            anotherPath = anotherPath.replace(/^\s+|\s+$/g, "");
+            if (!FileExists(anotherPath)) {
+                anotherPath = PathGetDirectory(path) + "\\" + anotherPath;
+            }
+            result.push(GetFileIncludes(anotherPath));
+            result.push(anotherPath);
+        }
+    }
+
+    return result;
+}
+
+// TODO: provide support for composing relative paths.
+function BuildFromSln(path, outBasePath) {
+    WScript.Echo("Reading solution " + path);
+    var outPath = PathCombine(outBasePath, PathGetFileName(path));
+    var regEx = /(Project\("[^"]+"\)\s*=\s*)"([^"]+)"\s*,\s*"([^"]+)"/;
+    var content = ReadAllTextFile(path);
+    var lines = StringSplit(content, "\r\n");
+    var i, len;
+    for (i = 0, len = lines.length; i < len; i++) {
+        var matches = regEx.exec(lines[i]);
+        if (matches) {
+            var projectPath = PathCombine(PathGetDirectory(path), matches[3]);
+            BuildFromProject(projectPath, outPath);
+        }
+    }
+}
+
+function BuildFile(path, outPath) {
+    /// <summary>Builds a JavaScript file. </summary>
+    /// <param name="path" type="String">Path to the file whose content is going to be built.</param>
+    /// <param name="outPath" type="String">Path of the built file.</param>
+
+    if (!FileExists(path)) {
+        throw { message: "File does not exist: " + inName };
+    }
+
+    CreateFolderIfMissing(PathGetDirectory(outPath));
+    switch (PathGetExtension(path).toLowerCase()) {
+        case ".js":
+        case ".htm":
+        case ".html":
+            WScript.Echo("building file: " + path);
+            var built = BuildFileForContent(path, false);
+            SaveTextToFile(built, outPath);
+            break;
+        default:
+            WScript.Echo("copying file: " + path);
+            CopyFile(path, outPath, true);
+            break;
+    }
+}
+
+function BuildFileForContent(path, contentOnly) {
+    var ext = PathGetExtension(path);
+    var includeCallback = function (line, inContent) {    
+        var includeIndex = line.indexOf(MakeMarker(ext, "INCLUDE: "));
+        if (includeIndex !== -1) {
+            var anotherPath = line.substr(includeIndex + 11);
+            anotherPath = anotherPath.replace(/^\s+|\s+$/g, "");
+            if (!FileExists(anotherPath)) {
+                anotherPath = PathGetDirectory(path) + "\\" + anotherPath;
+            }
+
+            return BuildFileForContent(anotherPath, true);
+        } else {
+            return (inContent) ? line : null;
+        }
+    };
+
+    return ExtractContentsBetweenMarkers(path, contentOnly, /*isExclusion*/ false, 
+        MakeMarker(ext, "CONTENT START"), MakeMarker(ext, "CONTENT END"), includeCallback);
+}
+
+function RemoveInternals(inName, outName) {
+    var ext = PathGetExtension(inName);
+    var includeCallback = function (line, inContent) {
+        return (line.indexOf("djsassert") === -1) ? line : null;
+    };
+    var content = ExtractContentsBetweenMarkers(inName, true, /*isExclusion*/ true,
+        MakeMarker(ext, "DATAJS INTERNAL START"), MakeMarker(ext, "DATAJS INTERNAL END"), includeCallback);
+    
+    SaveTextToFile(content, outName);
+}
+
+RunAndQuit(function () {
+    var args = WScript.Arguments;
+    var inName = WScript.Arguments.Unnamed(0);
+    if (!inName) {
+        throw { message: "no input specified" };
+    }
+
+    var outName = WScript.Arguments.Named("out");
+    if (!outName) {
+        throw { message: "no output specified" };
+    }
+
+    if (CheckScriptFlag("build-solution")) {
+        BuildFromSln(inName, outName);
+    }
+    if (CheckScriptFlag("remove-internals")) {
+        RemoveInternals(inName, outName);
+    }
+});
\ No newline at end of file
diff --git a/BuildTools/djsbuildfile.wsf b/BuildTools/djsbuildfile.wsf
new file mode 100644
index 0000000..e8006f5
--- /dev/null
+++ b/BuildTools/djsbuildfile.wsf
@@ -0,0 +1,17 @@
+<package>
+  <job>
+    <runtime>
+      <description>
+      This script is used to process files from the datajs project
+      into a variety of output script files.
+      </description>
+      <unnamed name="in" helpstring="Input to process" type="string" required="true" />
+      <named name="out" helpstring="Path to directory for output files." type="string" required="true" />
+      <named name="build-solution" helpstring="Builds the solution input by coalescing scripts." type="string" required="false" />
+      <named name="remove-internals" helpstring="Removes internal references from the input." type="string" required="false" />
+      <example>cscript djsbuildfile.wsf //Nologo /out:%djsroot%\JSLib\JSLib.sln /in:%djsroot%\out</example>
+    </runtime>
+    <script language="JScript" src="djscommon.js" />
+    <script language="JScript" src="djsbuildfile.js" />
+  </job>
+</package>
\ No newline at end of file
diff --git a/BuildTools/djsbuildver.cmd b/BuildTools/djsbuildver.cmd
new file mode 100644
index 0000000..279e499
--- /dev/null
+++ b/BuildTools/djsbuildver.cmd
@@ -0,0 +1,16 @@
+@echo off
+
+rem Copyright (c) Microsoft Open Technologies, Inc.  All rights reserved.
+rem Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation 
+rem files (the "Software"), to deal  in the Software without restriction, including without limitation the rights  to use, copy,
+rem modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the 
+rem Software is furnished to do so, subject to the following conditions:
+rem 
+rem The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+rem 
+rem THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR  IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+rem WARRANTIES OF MERCHANTABILITY,  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+rem COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 
+rem ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+set DJSVER=1.1.2
\ No newline at end of file
diff --git a/BuildTools/djscommon.js b/BuildTools/djscommon.js
new file mode 100644
index 0000000..ca779bb
--- /dev/null
+++ b/BuildTools/djscommon.js
@@ -0,0 +1,522 @@
+// Copyright (c) Microsoft Open Technologies, Inc.  All rights reserved.
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal  in the Software without restriction, including without limitation the rights  to use, copy,
+// modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR  IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// WARRANTIES OF MERCHANTABILITY,  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+//
+// References:
+//
+// JScript Language Reference
+// http://msdn2.microsoft.com/en-us/library/yek4tbz0
+//
+// Windows Script Host Object Model
+// http://msdn2.microsoft.com/en-us/library/a74hyyw0
+//
+// Script Runtime
+// http://msdn2.microsoft.com/en-us/library/hww8txat.aspx
+//
+
+function ArrayAny(arr, callback) {
+    /// <summary>Checks whether any element in an array satisfies a predicate.</summary>
+    /// <param name="arr" type="Array">Array to operate on.</param>
+    /// <param name="callback" type="Function">Function to test with element and index, returning true or false.</param>
+    /// <returns type="Boolean">true if 'callback' returns true for any element; false otherwise.</returns>
+    for (var i = 0; i < arr.length; i++) {
+        if (callback(arr[i], i)) {
+            return true;
+        }
+    }
+
+    return false;
+}
+
+function ArrayWhere(arr, callback) {
+    /// <summary>Returns the elements in an array that satisfy a predicate.</summary>
+    /// <param name="arr" type="Array">Array to operate on.</param>
+    /// <param name="callback" type="Function">Function to test with element and index, returning true or false.</param>
+    /// <returns type="Array">Array of elements from arr that satisfy the predicate.</returns>
+
+    var result = [];
+
+    for (var i = 0; i < arr.length; i++) {
+        if (callback(arr[i], i)) {
+            result.push(arr[i]);
+        }
+    }
+    return result;
+}
+
+function ArrayForEach(arr, callback) {
+    /// <summary>Invokes a callback for each element in the array.</summary>
+    /// <param name="arr" type="Array">Array to operate on.</param>
+    /// <param name="callback" type="Function">Function to invoke with element and index.</param>
+    for (var i = 0; i < arr.length; i++) {
+        callback(arr[i], i);
+    }
+}
+
+function CheckScriptFlag(name) {
+    /// <summary>Checks whether a script argument was given with true or false.</summary>
+    /// <param name="name" type="String">Argument name to check.</param>
+    /// <returns type="Boolean">
+    /// true if the argument was given witha value of 'true' or 'True'; false otherwise.
+    /// </returns>
+    var flag = WScript.Arguments.Named(name);
+    if (!flag) {
+        return false;
+    }
+
+    return flag === "true" || flag === "True";
+}
+
+function CreateFolderIfMissing(path) {
+    /// <summary>Creates a folder if it doesn't exist.</summary>
+    /// <param name="path" type="String">Path to folder to create.</param>
+    /// <remarks>This function will write out to the console on creation.</remarks>
+    if (!path) return;
+    var parent = PathGetDirectory(path);
+    var fso = new ActiveXObject("Scripting.FileSystemObject");
+    if (!fso.FolderExists(parent)) {
+        CreateFolderIfMissing(parent);
+    }
+    if (!fso.FolderExists(path)) {
+        WScript.Echo("Creating " + path + "...");
+        fso.CreateFolder(path);
+    }
+}
+
+function DeleteFile(path, force) {
+    /// <summary>Deletes a file.</summary>
+    /// <param name="path" type="String">Path to the file.</param>
+    /// <param name="force" type="Boolean">Whether to delete the file even if it has the read-only attribute set.</param>
+
+    var fso = new ActiveXObject("Scripting.FileSystemObject");
+    fso.DeleteFile(path, force);
+}
+
+function DeleteFolder(path, force) {
+    /// <summary>Deletes a folder.</summary>
+    /// <param name="path" type="String">Path to the folder.</param>
+    /// <param name="force" type="Boolean">Whether to delete the folder even if it has the read-only attribute set.</param>
+
+    var fso = new ActiveXObject("Scripting.FileSystemObject");
+    fso.DeleteFolder(path, force);
+}
+
+function CopyFolder(source, dest, overwrite) {
+    /// <summary>Recursively copies a folder and its contents from source to dest.</summary>
+    /// <param name="source" type="String">Path to the source folder location.</param>
+    /// <param name="dest" type="String">Path to the destination folder location.</param>
+    /// <param name="overwrite" type="Boolean">Whether to overwrite a folder in the destination location.</param>
+
+    var fso = new ActiveXObject("Scripting.FileSystemObject");
+    fso.CopyFolder(source, dest, overwrite);
+}
+
+
+function CopyFile(source, dest, overwrite) {
+    /// <summary>Copies a file from source to dest.</summary>
+    /// <param name="source" type="String">Path to the source file location.</param>
+    /// <param name="dest" type="String">Path to the destination file location.</param>
+    /// <param name="overwrite" type="Boolean">Whether to overwrite a file in the destination location.</param>
+
+    var fso = new ActiveXObject("Scripting.FileSystemObject");
+
+    if (overwrite && fso.FileExists(dest)) {
+        var f = fso.getFile(dest);
+        f.attributes = 0;
+    }
+
+    fso.CopyFile(source, dest, overwrite);
+}
+
+function ExtractContentsBetweenMarkers(path, contentOnly, isExclusion, startMarker, endMarker, callback) {
+    /// <summary>
+    /// Extracts the lines from the 'path' text file between the start and end markers.
+    /// </summary>
+    /// <param name="path" type="String">Path to file.</param>
+    /// <param name="contentOnly" type="Boolean">
+    /// true to skip everything until it's found between markers, false to start including everything from the start.
+    /// </param>
+    /// <param name="isExclusion" type="Boolean">
+    /// false if the 'extraction' means keeping the content; true if it means not excluding it from the result.
+    /// </param>
+    /// <param name="startMarker" type="String">Line content to match for content start.</param>
+    /// <param name="endMarker" type="String">Line content to match for content end.</param>
+    /// <param name="callback" type="Function" mayBeNull="true">
+    /// If true, then this function is called for every line along with the inContent flag
+    /// before the line is added; the called function may return a line
+    /// to be added in its place, null to skip processing.
+    /// </param>
+    /// <returns type="String">The string content of the file.</returns>
+
+    var content = ReadAllTextFile(path);
+    return ExtractContentsBetweenMarkersForText(content, contentOnly, isExclusion, startMarker, endMarker, callback);
+}
+
+function ExtractContentsBetweenMarkersForText(content, contentOnly, isExclusion, startMarker, endMarker, callback) {
+    /// <summary>
+    /// Extracts the lines from the specified text between the start and end markers.
+    /// </summary>
+    /// <param name="content" type="String">Text to process.</param>
+    /// <param name="contentOnly" type="Boolean">
+    /// true to skip everything until it's found between markers, false to start including everything from the start.
+    /// </param>
+    /// <param name="isExclusion" type="Boolean">
+    /// false if the 'extraction' means keeping the content; true if it means not excluding it from the result.
+    /// </param>
+    /// <param name="startMarker" type="String">Line content to match for content start.</param>
+    /// <param name="endMarker" type="String">Line content to match for content end.</param>
+    /// <param name="callback" type="Function" mayBeNull="true">
+    /// If true, then this function is called for every line along with the inContent flag
+    /// before the line is added; the called function may return a line
+    /// to be added in its place, null to skip processing.
+    /// </param>
+    /// <returns type="String">The extracted content.</returns>
+
+    var inContent = contentOnly === false;
+    var lines = StringSplit(content, "\r\n");
+    var result = [];
+    var i, len;
+    for (i = 0, len = lines.length; i < len; i++) {
+        var line = lines[i];
+        var contentStartIndex = line.indexOf(startMarker);
+        if (inContent === false && contentStartIndex !== -1) {
+            inContent = true;
+            continue;
+        }
+
+        var contentEndIndex = line.indexOf(endMarker);
+        if (inContent === true && contentEndIndex !== -1) {
+            inContent = false;
+            continue;
+        }
+
+        if (inContent !== isExclusion) {
+            if (callback) {
+                var callbackResult = callback(line, inContent);
+                if (callbackResult !== null && callbackResult !== undefined) {
+                    result.push(callbackResult);
+                }
+            } else {
+                result.push(line);
+            }
+        }
+    }
+
+    return result.join("\r\n");
+}
+
+function FolderExists(path) {
+    /// <summary>Checks whether the specified directory exists.</summary>
+    var fso = new ActiveXObject("Scripting.FileSystemObject");
+    if (fso.FolderExists(path)) {
+        return true;
+    }
+    else {
+        return false;
+    }
+}
+
+function FileExists(path) {
+    /// <summary>Checks whether the specified file exists.</summary>
+    var fso = new ActiveXObject("Scripting.FileSystemObject");
+    if (fso.FileExists(path)) {
+        return true;
+    }
+    else {
+        return false;
+    }
+}
+
+function GetEnvironmentVariable(name) {
+    /// <summary>Gets the value of the specified environment variable.</summary>
+    /// <param name="name" type="String">Name of the variable value to get.</param>
+    /// <returns type="String">Value for the given environment variable; null if undefined.</returns>
+    var shell = new ActiveXObject("WScript.Shell");
+    var result = shell.ExpandEnvironmentStrings("%" + name + "%");
+    if (result == "%" + name + "%") {
+        result = null;
+    }
+
+    return result;
+}
+
+function GetFilesRecursive(path) {
+    /// <summary>Gets all file names under the specified directory path.</summary>
+    /// <param name="path" type="String">Path to directory.</param>
+    /// <returns type="Array">Array of all file names under path.</returns>
+
+    var result = [];
+    var fso = new ActiveXObject("Scripting.FileSystemObject");
+    var pending = [path];
+
+    while (pending.length) {
+        var item = pending.pop();
+        var folder = fso.GetFolder(item);
+        for (var files = new Enumerator(folder.Files); !files.atEnd(); files.moveNext()) {
+            result.push(files.item().Path);
+        }
+
+        for (var subFolders = new Enumerator(folder.SubFolders); !subFolders.atEnd(); subFolders.moveNext()) {
+            pending.push(subFolders.item().Path);
+        }
+    }
+
+    return result;
+}
+
+function GetRelativePathFrom(startPath, endPath) {
+    if (startPath[startPath.length - 1] !== "\\") {
+        startPath += "\\";
+    }
+
+    if (startPath.length > endPath.length) {
+        throw { message: "traversing up NYI" };
+    }
+
+    return endPath.substr(startPath.length);
+}
+
+function MatchesMask(file, mask) {
+    if (!mask) {
+        return false;
+    }
+
+    if (file === mask) {
+        return true;
+    }
+
+    if (mask.substr(0, 1) === "*") {
+        var rest = mask.substr(1);
+        return file.substr(file.length - rest.length) === rest;
+    } else if (mask.substr(mask.length - 1) === "*") {
+        var end = mask.substr(0, mask.length - 1);
+        return file.substr(0, end.length) === end;
+    }
+
+    return false;
+}
+
+function PathGetDirectory(path) {
+    /// <summary>
+    /// Returns the directory of the specified path string (excluding the trailing "\\");
+    /// empty if there is no path.
+    /// </summary>
+
+    var l = path.length;
+    var startIndex = l;
+    while (--startIndex >= 0) {
+        var ch = path.substr(startIndex, 1);
+        if (ch == "\\") {
+            if (startIndex === 0) {
+                return "";
+            } else {
+                return path.substr(0, startIndex);
+            }
+        }
+    }
+
+    return "";
+}
+
+function PathGetFileName(path) {
+    /// <summary>
+    /// Returns the file name for the specified path string; empty if there is no
+    /// directory information.
+    /// </summary>
+    var l = path.length;
+    var startIndex = l;
+    while (--startIndex >= 0) {
+        var ch = path.substr(startIndex, 1);
+        if (ch == "\\" || ch == "/" || ch == ":") {
+            return path.substr(startIndex, l - startIndex);
+        }
+    }
+    return "";
+}
+
+function PathGetExtension(path) {
+    /// <summary>
+    /// Returns the extension of the specified path string (including the ".");
+    /// empty if there is no extension.
+    /// </summary>
+    var l = path.length;
+    var startIndex = l;
+    while (--startIndex >= 0) {
+        var ch = path.substr(startIndex, 1);
+        if (ch == ".") {
+            if (startIndex != (l - 1)) {
+                return path.substr(startIndex, l - startIndex);
+            }
+            return "";
+        }
+        else if (ch == "\\" || ch == ":") {
+            break;
+        }
+    }
+    return "";
+}
+
+function ReadAllTextFile(path) {
+    /// <summary>Reads all the content of the file into a string.</summary>
+    /// <param name="path" type="String">File name to read from.</param>
+    /// <returns type="String">File contents.</returns>
+    var ForReading = 1, ForWriting = 2;
+    var fso = new ActiveXObject("Scripting.FileSystemObject");
+    var file = fso.OpenTextFile(path, ForReading);
+    try {
+        var result;
+        if (file.AtEndOfStream) {
+            result = "";
+        } else {
+            result = file.ReadAll();
+        }
+    } finally {
+        file.Close();
+    }
+
+    return result;
+}
+
+function ReadXmlFile(path) {
+    /// <summary>Reads an XML document from the specified path.</summary>
+    /// <param name="path" type="String">Path to file on disk.</param>
+    /// <returns>A DOMDocument with the contents of the given file.</returns>
+    var result = new ActiveXObject("Msxml2.DOMDocument.6.0");
+    result.async = false;
+    result.load(path);
+    if (result.parseError.errorCode !== 0) {
+        throw { message: "Error reading '" + path + "': " + result.parseError.reason };
+    }
+
+    return result;
+}
+
+// Runs the specified function catching exceptions and quits the current script.
+function RunAndQuit(f) {
+    try {
+        f();
+    }
+    catch (e) {
+        // An error with 'statusCode' defined will avoid the usual error dump handling.
+        if (e.statusCode !== undefined) {
+            if (e.message) {
+                WScript.Echo(e.message);
+            }
+
+            WScript.Quit(e.statusCode);
+        }
+
+        WScript.Echo("Error caught while running this function:");
+        WScript.Echo(f.toString());
+        WScript.Echo("Error details:");
+        if (typeof (e) == "object" && e.toString() == "[object Object]" || e.toString() === "[object Error]") {
+            for (var p in e) WScript.Echo(" " + p + ": " + e[p]);
+        }
+        else {
+            WScript.Echo(e);
+        }
+
+        WScript.Quit(1);
+    }
+    WScript.Quit(0);
+}
+
+function RunConsoleCommand(strCommand, timeout, retry) {
+    /// <summary>Runs a command and waits for it to exit.</summary>
+    /// <param name="strCommand" type="String">Command to run.</param>
+    /// <param name="timeout" type="int">Timeout in seconds.</param>
+    /// <param name="timeout" type="bool">Boolean specifying whether to retry on timeout or not.</param>
+    /// <returns type="Array">An array with stdout in 0, stderr in 1 and exit code in 2. Forced
+    /// termination sets 2 to 1.</returns>
+    var WshShell = new ActiveXObject("WScript.Shell");
+    var result = new Array(3);
+    var oExec = WshShell.Exec(strCommand);
+    var counter = 0;
+
+    if (timeout) {
+        // Status of 0 means the process is still running
+        while (oExec.Status === 0 && counter < timeout) {
+            WScript.Sleep(1000);
+            counter++;
+        }
+
+        if (timeout === counter && oExec.Status === 0) {
+            WScript.Echo("Forcefully terminating " + strCommand + " after " + timeout + " seconds.");
+            oExec.Terminate();
+            result[2] = 1;
+            if (retry) {
+                return RunConsoleCommand(strCommand, timeout, false);
+            }
+        }
+    }
+
+    result[0] = oExec.StdOut.ReadAll();
+    result[1] = oExec.StdErr.ReadAll();
+
+    if (!result[2]) {
+        result[2] = oExec.ExitCode;
+    }
+
+    return result;
+}
+
+function SaveTextToFile(content, path) {
+    /// <summary>Saves text content into a file.</summary>
+    /// <param name="content" type="String">Content to save.</param>
+    /// <param name="path" type="String">Path of file to save into.</param>
+    var ForReading = 1, ForWriting = 2;
+    var fso = new ActiveXObject("Scripting.FileSystemObject");
+    var file = fso.OpenTextFile(path, ForWriting, true);
+    file.Write(content);
+    file.Close();
+}
+
+function StringSplit(strLine, strSeparator) {
+    /// <summary>Splits a string into a string array.</summary>
+    var result = new Array();
+    var startIndex = 0;
+    var resultIndex = 0;
+    while (startIndex < strLine.length) {
+        var endIndex = strLine.indexOf(strSeparator, startIndex);
+        if (endIndex == -1) {
+            endIndex = strLine.length;
+        }
+        result[resultIndex] = strLine.substring(startIndex, endIndex);
+        startIndex = endIndex + strSeparator.length;
+        resultIndex++;
+    }
+    return result;
+}
+
+function PathCombine(path1, path2) {
+    if (path1.charAt(path1.length - 1) !== "\\") {
+        return path1 + "\\" + path2;
+    }
+    return path1 + path2;
+}
+
+function RemoveReadOnlyAttribute(path) {
+    /// <summary>Removes the read-only attribute on the specified file.</summary>
+    /// <param name="path" type="String">Path to the file.</param>
+    var fso = new ActiveXObject("Scripting.FileSystemObject");
+    var f = fso.getFile(path);
+    if (1 === (f.attributes & 1)) {
+        f.attributes = (f.attributes & ~1);
+    }
+}
+
+function WriteXmlFile(document, path) {
+    /// <summary>Write an XML document to the specified path.</summary>
+    /// <param name="path" type="String">Path to file on disk.</param>
+    document.save(path);
+}
\ No newline at end of file
diff --git a/BuildTools/djslicense.js b/BuildTools/djslicense.js
new file mode 100644
index 0000000..a6bfe15
--- /dev/null
+++ b/BuildTools/djslicense.js
@@ -0,0 +1,12 @@
+// Copyright (c) Microsoft Open Technologies, Inc.  All rights reserved.
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal  in the Software without restriction, including without limitation the rights  to use, copy,
+// modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR  IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// WARRANTIES OF MERCHANTABILITY,  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/BuildTools/djspatchtests.js b/BuildTools/djspatchtests.js
new file mode 100644
index 0000000..ef977f6
--- /dev/null
+++ b/BuildTools/djspatchtests.js
@@ -0,0 +1,55 @@
+/// <reference path="djscommon.js" />
+
+// Copyright (c) Microsoft Open Technologies, Inc.  All rights reserved.
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal  in the Software without restriction, including without limitation the rights  to use, copy,
+// modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR  IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// WARRANTIES OF MERCHANTABILITY,  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+// This script will search for a particular text pattern in an html file and replace its first occurrence with the value defined in 
+// the replace-script parameter. All other lines with subsequent occurrences of the pattern will be removed from the file. 
+
+function Main() {
+    var shell = new ActiveXObject("WScript.Shell");
+    var inputPath = shell.ExpandEnvironmentStrings(WScript.Arguments.Named("in"));
+    var replaceScript = WScript.Arguments.Named("replace-script");
+    var removePattern = /\.\.\/src\/.*\.js/;
+
+    var files = ArrayWhere(GetFilesRecursive(inputPath), function (file) {
+        return MatchesMask(file, "*.htm") || MatchesMask(file, "*.html");
+    });
+
+    ArrayForEach(files, function (file) {
+        var content = ReadAllTextFile(file);
+        var lines = StringSplit(content, "\r\n");
+        var result = [];
+        var included = false;
+        ArrayForEach(lines, function (line) {
+            if (line.match(removePattern)) {
+                if (!included) {
+                    included = true;
+                    line = line.replace(removePattern, replaceScript);
+                    result.push(line);
+                }
+            } else {
+                result.push(line);
+            }
+        });
+
+        if (included) {
+            content = result.join("\r\n");
+            WScript.Echo("Patched " + file);
+            RemoveReadOnlyAttribute(file);
+            SaveTextToFile(content, file);
+        }
+    });
+}
+
+RunAndQuit(Main);
\ No newline at end of file
diff --git a/BuildTools/djspatchtests.wsf b/BuildTools/djspatchtests.wsf
new file mode 100644
index 0000000..7df918a
--- /dev/null
+++ b/BuildTools/djspatchtests.wsf
@@ -0,0 +1,14 @@
+<package>
+  <job>
+    <runtime>
+      <description>
+      This script is used to patch the html files so they can use the build output.
+      </description>
+      <named name="in" helpstring="Path to directory with the html test files to patch." type="string" required="true" />
+      <named name="replace-script" helpstring="Target script reference." type="string" required="true" />
+      <example>cscript djspatchtests.wsf //Nologo /in:%DJSOUT%\jslib.sln\tests /replace-script:"../src/datajs-%DJSVER%.js"</example>
+    </runtime>
+    <script language="JScript" src="djscommon.js" />
+    <script language="JScript" src="djspatchtests.js" />
+  </job>
+</package>
\ No newline at end of file
diff --git a/JSLib/JSLib.csproj b/JSLib/JSLib.csproj
new file mode 100644
index 0000000..bff96ae
--- /dev/null
+++ b/JSLib/JSLib.csproj
@@ -0,0 +1,131 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+	<PropertyGroup>
+		<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+		<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+		<ProductVersion>
+		</ProductVersion>
+		<SchemaVersion>2.0</SchemaVersion>
+		<ProjectGuid>{73ADF1A7-613B-4679-885B-2AE4AFAA9A6A}</ProjectGuid>
+		<ProjectTypeGuids>{349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>
+		<OutputType>Library</OutputType>
+		<AppDesignerFolder>Properties</AppDesignerFolder>
+		<RootNamespace>JSLib</RootNamespace>
+		<AssemblyName>JSLib</AssemblyName>
+		<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
+		<UseIISExpress>false</UseIISExpress>
+	</PropertyGroup>
+	<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+		<DebugSymbols>true</DebugSymbols>
+		<DebugType>full</DebugType>
+		<Optimize>false</Optimize>
+		<OutputPath>bin\</OutputPath>
+		<DefineConstants>DEBUG;TRACE</DefineConstants>
+		<ErrorReport>prompt</ErrorReport>
+		<WarningLevel>4</WarningLevel>
+	</PropertyGroup>
+	<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+		<DebugType>pdbonly</DebugType>
+		<Optimize>true</Optimize>
+		<OutputPath>bin\</OutputPath>
+		<DefineConstants>TRACE</DefineConstants>
+		<ErrorReport>prompt</ErrorReport>
+		<WarningLevel>4</WarningLevel>
+	</PropertyGroup>
+	<ItemGroup>
+		<!-- Product code. -->
+		<Content Include="src\cache-source.js"/>
+		<Content Include="src\odata-gml.js"/>
+		<Content Include="src\utils.js"/>
+		<Content Include="src\odata-json-light.js"/>
+		<Content Include="src\datajs.js"/>
+		<Content Include="src\cache.js"/>
+		<Content Include="src\deferred.js"/>
+		<Content Include="src\odata.js"/>
+		<Content Include="src\odata-batch.js"/>
+		<Content Include="src\odata-handler.js"/>
+		<Content Include="src\odata-utils.js"/>
+		<Content Include="src\odata-xml.js"/>
+		<Content Include="src\odata-metadata.js"/>
+		<Content Include="src\odata-json.js"/>
+		<Content Include="src\odata-atom.js"/>
+		<Content Include="src\odata-net.js"/>
+		<Content Include="src\store-dom.js"/>
+		<Content Include="src\store-indexeddb.js"/>
+		<Content Include="src\store-memory.js"/>
+		<Content Include="src\store.js"/>
+		<Content Include="src\xml.js"/>
+		<Content Include="tests\common\djstest.js"/>
+		<Content Include="tests\common\ODataReadOracle.js"/>
+		<Content Include="tests\common\ODataReadOracle.svc"/>
+		<Content Include="tests\common\TestLogger.svc"/>
+		<Content Include="tests\common\TestSynchronizerClient.js"/>
+		<Content Include="tests\endpoints\FoodStoreDataServiceV3.svc"/>
+		<Content Include="tests\endpoints\FoodStoreDataService.svc"/>
+		<Content Include="tests\endpoints\FoodStoreDataServiceV2.svc"/>
+		<Content Include="tests\endpoints\web.config"/>
+		<Content Include="tests\odata-atom-tests.js"/>
+		<Content Include="tests\odata-qunit-tests.htm"/>
+		<Content Include="tests\run-tests.wsf"/>
+		<Content Include="tests\test-list.js"/>
+		<!-- Configuration file for the web project. -->
+		<Content Include="Web.config"/>
+	</ItemGroup>
+	<ItemGroup>
+		<Compile Include="tests\code\AtomReader.cs"/>
+		<Compile Include="tests\code\CsdlReader.cs"/>
+		<Compile Include="tests\code\JsDate.cs"/>
+		<Compile Include="tests\code\JsonObject.cs"/>
+		<Compile Include="tests\code\ReaderUtils.cs"/>
+		<Compile Include="tests\code\ReflectionDataContext.cs"/>
+	</ItemGroup>
+	<ItemGroup>
+	</ItemGroup>
+	<ItemGroup>
+		<Reference Include="Microsoft.Data.Edm, Version=5.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
+			<HintPath>packages\Microsoft.Data.Edm.5.1.0\lib\net40\Microsoft.Data.Edm.dll</HintPath>
+		</Reference>
+		<Reference Include="Microsoft.Data.OData, Version=5.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
+			<HintPath>packages\Microsoft.Data.OData.5.1.0\lib\net40\Microsoft.Data.OData.dll</HintPath>
+		</Reference>
+		<Reference Include="Microsoft.Data.Services, Version=5.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
+			<HintPath>packages\Microsoft.Data.Services.5.1.0\lib\net40\Microsoft.Data.Services.dll</HintPath>
+		</Reference>
+		<Reference Include="Microsoft.Data.Services.Client, Version=5.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
+			<HintPath>packages\Microsoft.Data.Services.Client.5.1.0\lib\net40\Microsoft.Data.Services.Client.dll</HintPath>
+		</Reference>
+		<Reference Include="System"/>
+		<Reference Include="System.Net"/>
+		<Reference Include="System.Runtime.Serialization"/>
+		<Reference Include="System.ServiceModel"/>
+		<Reference Include="System.ServiceModel.Activation"/>
+		<Reference Include="System.ServiceModel.Web"/>
+		<Reference Include="System.Spatial, Version=5.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
+			<HintPath>packages\System.Spatial.5.1.0\lib\net40\System.Spatial.dll</HintPath>
+		</Reference>
+		<Reference Include="System.Web.Extensions"/>
+		<Reference Include="System.Xml"/>
+		<Reference Include="System.Xml.Linq"/>
+	</ItemGroup>
+	<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets"/>
+	<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\WebApplications\Microsoft.WebApplication.targets"/>
+	<ProjectExtensions>
+		<VisualStudio>
+			<FlavorProperties GUID="{349c5851-65df-11da-9384-00065b846f21}">
+				<WebProjectProperties>
+					<UseIIS>False</UseIIS>
+					<AutoAssignPort>True</AutoAssignPort>
+					<DevelopmentServerPort>10092</DevelopmentServerPort>
+					<DevelopmentServerVPath>/</DevelopmentServerVPath>
+					<IISUrl>
+					</IISUrl>
+					<NTLMAuthentication>False</NTLMAuthentication>
+					<UseCustomServer>False</UseCustomServer>
+					<CustomServerUrl>
+					</CustomServerUrl>
+					<SaveServerSettingsInUserFile>False</SaveServerSettingsInUserFile>
+				</WebProjectProperties>
+			</FlavorProperties>
+		</VisualStudio>
+	</ProjectExtensions>
+</Project>
diff --git a/JSLib/JSLib.sln b/JSLib/JSLib.sln
new file mode 100644
index 0000000..b87a0df
--- /dev/null
+++ b/JSLib/JSLib.sln
@@ -0,0 +1,20 @@
+
+Microsoft Visual Studio Solution File, Format Version 11.00
+# Visual Studio 2010
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JSLib", "JSLib.csproj", "{73ADF1A7-613B-4679-885B-2AE4AFAA9A6A}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Any CPU = Debug|Any CPU
+		Release|Any CPU = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{73ADF1A7-613B-4679-885B-2AE4AFAA9A6A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{73ADF1A7-613B-4679-885B-2AE4AFAA9A6A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{73ADF1A7-613B-4679-885B-2AE4AFAA9A6A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{73ADF1A7-613B-4679-885B-2AE4AFAA9A6A}.Release|Any CPU.Build.0 = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+EndGlobal
\ No newline at end of file
diff --git a/JSLib/Web.config b/JSLib/Web.config
new file mode 100644
index 0000000..142a457
--- /dev/null
+++ b/JSLib/Web.config
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<configuration>
+  <system.web>
+    <compilation debug="true" targetFramework="4.0">
+      <assemblies>
+        <add assembly="System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089" />
+        <add assembly="System.Data.DataSetExtensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089" />
+        <add assembly="System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
+        <add assembly="System.Xml.Linq, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089" />
+        <add assembly="System.Data.Entity, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089" />
+        <add assembly="System.ServiceModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089" />
+        <add assembly="System.ServiceModel.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
+        <add assembly="Microsoft.Data.OData, Version=5.1, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
+        <add assembly="Microsoft.Data.Services, Version=5.1, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
+        <add assembly="Microsoft.Data.Services.Client, Version=5.1, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
+      </assemblies>
+    </compilation>
+  </system.web>
+  <system.webServer>
+    <directoryBrowse enabled="true" />
+  </system.webServer>
+  <system.codedom>
+    <compilers>
+      <compiler language="c#;cs;csharp" extension=".cs" type="Microsoft.CSharp.CSharpCodeProvider,System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+        <providerOption name="CompilerVersion" value="v4.0" />
+      </compiler>
+    </compilers>
+  </system.codedom>
+  <system.net>
+    <defaultProxy>
+      <proxy autoDetect="True" />
+    </defaultProxy>
+  </system.net>
+</configuration>
\ No newline at end of file
diff --git a/JSLib/src/cache-source.js b/JSLib/src/cache-source.js
new file mode 100644
index 0000000..3616b84
--- /dev/null
+++ b/JSLib/src/cache-source.js
@@ -0,0 +1,183 @@
+/// <reference path="odata-utils.js" />
+
+// Copyright (c) Microsoft Open Technologies, Inc.  All rights reserved.
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal  in the Software without restriction, including without limitation the rights  to use, copy,
+// modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR  IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// WARRANTIES OF MERCHANTABILITY,  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+// cache-source.js
+
+(function (window, undefined) {
+
+    var datajs = window.datajs || {};
+    var odata = window.OData || {};
+
+    var parseInt10 = datajs.parseInt10;
+    var normalizeURICase = datajs.normalizeURICase;
+
+    // CONTENT START
+
+    var appendQueryOption = function (uri, queryOption) {
+        /// <summary>Appends the specified escaped query option to the specified URI.</summary>
+        /// <param name="uri" type="String">URI to append option to.</param>
+        /// <param name="queryOption" type="String">Escaped query option to append.</param>
+        var separator = (uri.indexOf("?") >= 0) ? "&" : "?";
+        return uri + separator + queryOption;
+    };
+
+    var appendSegment = function (uri, segment) {
+        /// <summary>Appends the specified segment to the given URI.</summary>
+        /// <param name="uri" type="String">URI to append a segment to.</param>
+        /// <param name="segment" type="String">Segment to append.</param>
+        /// <returns type="String">The original URI with a new segment appended.</returns>
+
+        var index = uri.indexOf("?");
+        var queryPortion = "";
+        if (index >= 0) {
+            queryPortion = uri.substr(index);
+            uri = uri.substr(0, index);
+        }
+
+        if (uri[uri.length - 1] !== "/") {
+            uri += "/";
+        }
+        return uri + segment + queryPortion;
+    };
+
+    var buildODataRequest = function (uri, options) {
+        /// <summary>Builds a request object to GET the specified URI.</summary>
+        /// <param name="uri" type="String">URI for request.</param>
+        /// <param name="options" type="Object">Additional options.</param>
+
+        return {
+            method: "GET",
+            requestUri: uri,
+            user: options.user,
+            password: options.password,
+            enableJsonpCallback: options.enableJsonpCallback,
+            callbackParameterName: options.callbackParameterName,
+            formatQueryString: options.formatQueryString
+        };
+    };
+
+    var findQueryOptionStart = function (uri, name) {
+        /// <summary>Finds the index where the value of a query option starts.</summary>
+        /// <param name="uri" type="String">URI to search in.</param>
+        /// <param name="name" type="String">Name to look for.</param>
+        /// <returns type="Number">The index where the query option starts.</returns>
+
+        var result = -1;
+        var queryIndex = uri.indexOf("?");
+        if (queryIndex !== -1) {
+            var start = uri.indexOf("?" + name + "=", queryIndex);
+            if (start === -1) {
+                start = uri.indexOf("&" + name + "=", queryIndex);
+            }
+            if (start !== -1) {
+                result = start + name.length + 2;
+            }
+        }
+        return result;
+    };
+
+    var queryForData = function (uri, options, success, error) {
+        /// <summary>Gets data from an OData service.</summary>
+        /// <param name="uri" type="String">URI to the OData service.</param>
+        /// <param name="options" type="Object">Object with additional well-known request options.</param>
+        /// <param name="success" type="Function">Success callback.</param>
+        /// <param name="error" type="Function">Error callback.</param>
+        /// <returns type="Object">Object with an abort method.</returns>
+
+        var request = queryForDataInternal(uri, options, [], success, error);
+        return request;
+    };
+
+    var queryForDataInternal = function (uri, options, data, success, error) {
+        /// <summary>Gets data from an OData service taking into consideration server side paging.</summary>
+        /// <param name="uri" type="String">URI to the OData service.</param>
+        /// <param name="options" type="Object">Object with additional well-known request options.</param>
+        /// <param name="data" type="Array">Array that stores the data provided by the OData service.</param>
+        /// <param name="success" type="Function">Success callback.</param>
+        /// <param name="error" type="Function">Error callback.</param>
+        /// <returns type="Object">Object with an abort method.</returns>
+
+        var request = buildODataRequest(uri, options);
+        var currentRequest = odata.request(request, function (newData) {
+            var next = newData.__next;
+            var results = newData.results;
+
+            data = data.concat(results);
+
+            if (next) {
+                currentRequest = queryForDataInternal(next, options, data, success, error);
+            } else {
+                success(data);
+            }
+        }, error, undefined, options.httpClient, options.metadata);
+
+        return {
+            abort: function () {
+                currentRequest.abort();
+            }
+        };
+    };
+
+    var ODataCacheSource = function (options) {
+        /// <summary>Creates a data cache source object for requesting data from an OData service.</summary>
+        /// <param name="options">Options for the cache data source.</param>
+        /// <returns type="ODataCacheSource">A new data cache source instance.</returns>
+
+        var that = this;
+        var uri = options.source;
+        
+        that.identifier = normalizeURICase(encodeURI(decodeURI(uri)));
+        that.options = options;
+
+        that.count = function (success, error) {
+            /// <summary>Gets the number of items in the collection.</summary>
+            /// <param name="success" type="Function">Success callback with the item count.</param>
+            /// <param name="error" type="Function">Error callback.</param>
+            /// <returns type="Object">Request object with an abort method./<param>
+
+            var options = that.options;
+            return odata.request(
+                buildODataRequest(appendSegment(uri, "$count"), options),
+                function (data) {
+                    var count = parseInt10(data.toString());
+                    if (isNaN(count)) {
+                        error({ message: "Count is NaN", count: count });
+                    } else {
+                        success(count);
+                    }
+                }, error, undefined, options.httpClient, options.metadata);
+        };
+
+        that.read = function (index, count, success, error) {
+            /// <summary>Gets a number of consecutive items from the collection.</summary>
+            /// <param name="index" type="Number">Zero-based index of the items to retrieve.</param>
+            /// <param name="count" type="Number">Number of items to retrieve.</param>
+            /// <param name="success" type="Function">Success callback with the requested items.</param>
+            /// <param name="error" type="Function">Error callback.</param>
+            /// <returns type="Object">Request object with an abort method./<param>
+
+            var queryOptions = "$skip=" + index + "&$top=" + count;
+            return queryForData(appendQueryOption(uri, queryOptions), that.options, success, error);
+        };
+
+        return that;
+    };
+
+    // DATAJS INTERNAL START
+    window.datajs.ODataCacheSource = ODataCacheSource;
+    // DATAJS INTERNAL END
+
+    // CONTENT END
+})(this);
\ No newline at end of file
diff --git a/JSLib/src/cache.js b/JSLib/src/cache.js
new file mode 100644
index 0000000..b3a3a01
--- /dev/null
+++ b/JSLib/src/cache.js
@@ -0,0 +1,1349 @@
+/// <reference path="odata-utils.js" />
+
+// Copyright (c) Microsoft Open Technologies, Inc.  All rights reserved.
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal  in the Software without restriction, including without limitation the rights  to use, copy,
+// modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR  IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// WARRANTIES OF MERCHANTABILITY,  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+// cache.js
+
+(function (window, undefined) {
+
+    var datajs = window.datajs || {};
+
+    var assigned = datajs.assigned;
+    var delay = datajs.delay;
+    var extend = datajs.extend;
+    var djsassert = datajs.djsassert;
+    var isArray = datajs.isArray;
+    var normalizeURI = datajs.normalizeURI;
+    var parseInt10 = datajs.parseInt10;
+    var undefinedDefault = datajs.undefinedDefault;
+
+    var createDeferred = datajs.createDeferred;
+    var DjsDeferred = datajs.DjsDeferred;
+    var ODataCacheSource = datajs.ODataCacheSource;
+
+    // CONTENT START
+
+    var appendPage = function (operation, page) {
+        /// <summary>Appends a page's data to the operation data.</summary>
+        /// <param name="operation" type="Object">Operation with (i)ndex, (c)ount and (d)ata.</param>
+        /// <param name="page" type="Object">Page with (i)ndex, (c)ount and (d)ata.</param>
+
+        var intersection = intersectRanges(operation, page);
+        if (intersection) {
+            var start = intersection.i - page.i;
+            var end = start + (operation.c - operation.d.length);
+            operation.d = operation.d.concat(page.d.slice(start, end));
+        }
+    };
+
+    var intersectRanges = function (x, y) {
+        /// <summary>Returns the {(i)ndex, (c)ount} range for the intersection of x and y.</summary>
+        /// <param name="x" type="Object">Range with (i)ndex and (c)ount members.</param>
+        /// <param name="y" type="Object">Range with (i)ndex and (c)ount members.</param>
+        /// <returns type="Object">The intersection (i)ndex and (c)ount; undefined if there is no intersection.</returns>
+
+        var xLast = x.i + x.c;
+        var yLast = y.i + y.c;
+        var resultIndex = (x.i > y.i) ? x.i : y.i;
+        var resultLast = (xLast < yLast) ? xLast : yLast;
+        var result;
+        if (resultLast >= resultIndex) {
+            result = { i: resultIndex, c: resultLast - resultIndex };
+        }
+
+        return result;
+    };
+
+    var checkZeroGreater = function (val, name) {
+        /// <summary>Checks whether val is a defined number with value zero or greater.</summary>
+        /// <param name="val" type="Number">Value to check.</param>
+        /// <param name="name" type="String">Parameter name to use in exception.</param>
+
+        if (val === undefined || typeof val !== "number") {
+            throw { message: "'" + name + "' must be a number." };
+        }
+
+        if (isNaN(val) || val < 0 || !isFinite(val)) {
+            throw { message: "'" + name + "' must be greater than or equal to zero." };
+        }
+    };
+
+    var checkUndefinedGreaterThanZero = function (val, name) {
+        /// <summary>Checks whether val is undefined or a number with value greater than zero.</summary>
+        /// <param name="val" type="Number">Value to check.</param>
+        /// <param name="name" type="String">Parameter name to use in exception.</param>
+
+        if (val !== undefined) {
+            if (typeof val !== "number") {
+                throw { message: "'" + name + "' must be a number." };
+            }
+
+            if (isNaN(val) || val <= 0 || !isFinite(val)) {
+                throw { message: "'" + name + "' must be greater than zero." };
+            }
+        }
+    };
+
+    var checkUndefinedOrNumber = function (val, name) {
+        /// <summary>Checks whether val is undefined or a number</summary>
+        /// <param name="val" type="Number">Value to check.</param>
+        /// <param name="name" type="String">Parameter name to use in exception.</param>
+        if (val !== undefined && (typeof val !== "number" || isNaN(val) || !isFinite(val))) {
+            throw { message: "'" + name + "' must be a number." };
+        }
+    };
+
+    var removeFromArray = function (arr, item) {
+        /// <summary>Performs a linear search on the specified array and removes the first instance of 'item'.</summary>
+        /// <param name="arr" type="Array">Array to search.</param>
+        /// <param name="item">Item being sought.</param>
+        /// <returns type="Boolean">Whether the item was removed.</returns>
+
+        var i, len;
+        for (i = 0, len = arr.length; i < len; i++) {
+            if (arr[i] === item) {
+                arr.splice(i, 1);
+                return true;
+            }
+        }
+
+        return false;
+    };
+
+    var estimateSize = function (obj) {
+        /// <summary>Estimates the size of an object in bytes.</summary>
+        /// <param name="obj" type="Object">Object to determine the size of.</param>
+        /// <returns type="Integer">Estimated size of the object in bytes.</returns>
+        var size = 0;
+        var type = typeof obj;
+
+        if (type === "object" && obj) {
+            for (var name in obj) {
+                size += name.length * 2 + estimateSize(obj[name]);
+            }
+        } else if (type === "string") {
+            size = obj.length * 2;
+        } else {
+            size = 8;
+        }
+        return size;
+    };
+
+    var snapToPageBoundaries = function (lowIndex, highIndex, pageSize) {
+        /// <summary>Snaps low and high indices into page sizes and returns a range.</summary>
+        /// <param name="lowIndex" type="Number">Low index to snap to a lower value.</param>
+        /// <param name="highIndex" type="Number">High index to snap to a higher value.</param>
+        /// <param name="pageSize" type="Number">Page size to snap to.</param>
+        /// <returns type="Object">A range with (i)ndex and (c)ount of elements.</returns>
+
+        lowIndex = Math.floor(lowIndex / pageSize) * pageSize;
+        highIndex = Math.ceil((highIndex + 1) / pageSize) * pageSize;
+        return { i: lowIndex, c: highIndex - lowIndex };
+    };
+
+    // The DataCache is implemented using state machines.  The following constants are used to properly
+    // identify and label the states that these machines transition to.
+
+    // DataCache state constants
+
+    var CACHE_STATE_DESTROY = "destroy";
+    var CACHE_STATE_IDLE = "idle";
+    var CACHE_STATE_INIT = "init";
+    var CACHE_STATE_READ = "read";
+    var CACHE_STATE_PREFETCH = "prefetch";
+    var CACHE_STATE_WRITE = "write";
+
+    // DataCacheOperation state machine states.
+    // Transitions on operations also depend on the cache current of the cache.
+
+    var OPERATION_STATE_CANCEL = "cancel";
+    var OPERATION_STATE_END = "end";
+    var OPERATION_STATE_ERROR = "error";
+    var OPERATION_STATE_START = "start";
+    var OPERATION_STATE_WAIT = "wait";
+
+    // Destroy state machine states
+
+    var DESTROY_STATE_CLEAR = "clear";
+
+    // Read / Prefetch state machine states
+
+    var READ_STATE_DONE = "done";
+    var READ_STATE_LOCAL = "local";
+    var READ_STATE_SAVE = "save";
+    var READ_STATE_SOURCE = "source";
+
+    var DataCacheOperation = function (stateMachine, promise, isCancelable, index, count, data, pending) {
+        /// <summary>Creates a new operation object.</summary>
+        /// <param name="stateMachine" type="Function">State machine that describes the specific behavior of the operation.</param>
+        /// <param name="promise" type ="DjsDeferred">Promise for requested values.</param>
+        /// <param name="isCancelable" type ="Boolean">Whether this operation can be canceled or not.</param>
+        /// <param name="index" type="Number">Index of first item requested.</param>
+        /// <param name="count" type="Number">Count of items requested.</param>
+        /// <param name="data" type="Array">Array with the items requested by the operation.</param>
+        /// <param name="pending" type="Number">Total number of pending prefetch records.</param>
+        /// <returns type="DataCacheOperation">A new data cache operation instance.</returns>
+
+        /// <field name="p" type="DjsDeferred">Promise for requested values.</field>
+        /// <field name="i" type="Number">Index of first item requested.</field>
+        /// <field name="c" type="Number">Count of items requested.</field>
+        /// <field name="d" type="Array">Array with the items requested by the operation.</field>
+        /// <field name="s" type="Array">Current state of the operation.</field>
+        /// <field name="canceled" type="Boolean">Whether the operation has been canceled.</field>
+        /// <field name="pending" type="Number">Total number of pending prefetch records.</field>
+        /// <field name="oncomplete" type="Function">Callback executed when the operation reaches the end state.</field>
+
+        var stateData;
+        var cacheState;
+        var that = this;
+
+        that.p = promise;
+        that.i = index;
+        that.c = count;
+        that.d = data;
+        that.s = OPERATION_STATE_START;
+
+        that.canceled = false;
+        that.pending = pending;
+        that.oncomplete = null;
+
+        that.cancel = function () {
+            /// <summary>Transitions this operation to the cancel state and sets the canceled flag to true.</summary>
+            /// <remarks>The function is a no-op if the operation is non-cancelable.</summary>
+
+            if (!isCancelable) {
+                return;
+            }
+
+            var state = that.s;
+            if (state !== OPERATION_STATE_ERROR && state !== OPERATION_STATE_END && state !== OPERATION_STATE_CANCEL) {
+                that.canceled = true;
+                transition(OPERATION_STATE_CANCEL, stateData);
+            }
+        };
+
+        that.complete = function () {
+            /// <summary>Transitions this operation to the end state.</summary>
+
+            djsassert(that.s !== OPERATION_STATE_END, "DataCacheOperation.complete() - operation is in the end state", that);
+            transition(OPERATION_STATE_END, stateData);
+        };
+
+        that.error = function (err) {
+            /// <summary>Transitions this operation to the error state.</summary>
+            if (!that.canceled) {
+                djsassert(that.s !== OPERATION_STATE_END, "DataCacheOperation.error() - operation is in the end state", that);
+                djsassert(that.s !== OPERATION_STATE_ERROR, "DataCacheOperation.error() - operation is in the error state", that);
+                transition(OPERATION_STATE_ERROR, err);
+            }
+        };
+
+        that.run = function (state) {
+            /// <summary>Executes the operation's current state in the context of a new cache state.</summary>
+            /// <param name="state" type="Object">New cache state.</param>
+
+            cacheState = state;
+            that.transition(that.s, stateData);
+        };
+
+        that.wait = function (data) {
+            /// <summary>Transitions this operation to the wait state.</summary>
+
+            djsassert(that.s !== OPERATION_STATE_END, "DataCacheOperation.wait() - operation is in the end state", that);
+            transition(OPERATION_STATE_WAIT, data);
+        };
+
+        var operationStateMachine = function (opTargetState, cacheState, data) {
+            /// <summary>State machine that describes all operations common behavior.</summary>
+            /// <param name="opTargetState" type="Object">Operation state to transition to.</param>
+            /// <param name="cacheState" type="Object">Current cache state.</param>
+            /// <param name="data" type="Object" optional="true">Additional data passed to the state.</param>
+
+            switch (opTargetState) {
+                case OPERATION_STATE_START:
+                    // Initial state of the operation. The operation will remain in this state until the cache has been fully initialized.
+                    if (cacheState !== CACHE_STATE_INIT) {
+                        stateMachine(that, opTargetState, cacheState, data);
+                    }
+                    break;
+
+                case OPERATION_STATE_WAIT:
+                    // Wait state indicating that the operation is active but waiting for an asynchronous operation to complete.
+                    stateMachine(that, opTargetState, cacheState, data);
+                    break;
+
+                case OPERATION_STATE_CANCEL:
+                    // Cancel state.
+                    stateMachine(that, opTargetState, cacheState, data);
+                    that.fireCanceled();
+                    transition(OPERATION_STATE_END);
+                    break;
+
+                case OPERATION_STATE_ERROR:
+                    // Error state. Data is expected to be an object detailing the error condition.
+                    stateMachine(that, opTargetState, cacheState, data);
+                    that.canceled = true;
+                    that.fireRejected(data);
+                    transition(OPERATION_STATE_END);
+                    break;
+
+                case OPERATION_STATE_END:
+                    // Final state of the operation.
+                    if (that.oncomplete) {
+                        that.oncomplete(that);
+                    }
+                    if (!that.canceled) {
+                        that.fireResolved();
+                    }
+                    stateMachine(that, opTargetState, cacheState, data);
+                    break;
+
+                default:
+                    // Any other state is passed down to the state machine describing the operation's specific behavior.
+                    // DATAJS INTERNAL START 
+                    if (true) {
+                        // Check that the state machine actually handled the sate.
+                        var handled = stateMachine(that, opTargetState, cacheState, data);
+                        djsassert(handled, "Bad operation state: " + opTargetState + " cacheState: " + cacheState, this);
+                    } else {
+                        // DATAJS INTERNAL END 
+                        stateMachine(that, opTargetState, cacheState, data);
+                        // DATAJS INTERNAL START
+                    }
+                    // DATAJS INTERNAL END
+                    break;
+            }
+        };
+
+        var transition = function (state, data) {
+            /// <summary>Transitions this operation to a new state.</summary>
+            /// <param name="state" type="Object">State to transition the operation to.</param>
+            /// <param name="data" type="Object" optional="true">Additional data passed to the state.</param>
+
+            that.s = state;
+            stateData = data;
+            operationStateMachine(state, cacheState, data);
+        };
+
+        that.transition = transition;
+
+        return that;
+    };
+
+    DataCacheOperation.prototype.fireResolved = function () {
+        /// <summary>Fires a resolved notification as necessary.</summary>
+
+        // Fire the resolve just once.
+        var p = this.p;
+        if (p) {
+            this.p = null;
+            p.resolve(this.d);
+        }
+    };
+
+    DataCacheOperation.prototype.fireRejected = function (reason) {
+        /// <summary>Fires a rejected notification as necessary.</summary>
+
+        // Fire the rejection just once.
+        var p = this.p;
+        if (p) {
+            this.p = null;
+            p.reject(reason);
+        }
+    };
+
+    DataCacheOperation.prototype.fireCanceled = function () {
+        /// <summary>Fires a canceled notification as necessary.</summary>
+
+        this.fireRejected({ canceled: true, message: "Operation canceled" });
+    };
+
+
+    var DataCache = function (options) {
+        /// <summary>Creates a data cache for a collection that is efficiently loaded on-demand.</summary>
+        /// <param name="options">
+        /// Options for the data cache, including name, source, pageSize,
+        /// prefetchSize, cacheSize, storage mechanism, and initial prefetch and local-data handler.
+        /// </param>
+        /// <returns type="DataCache">A new data cache instance.</returns>
+
+        var state = CACHE_STATE_INIT;
+        var stats = { counts: 0, netReads: 0, prefetches: 0, cacheReads: 0 };
+
+        var clearOperations = [];
+        var readOperations = [];
+        var prefetchOperations = [];
+
+        var actualCacheSize = 0;                                             // Actual cache size in bytes.
+        var allDataLocal = false;                                            // Whether all data is local.
+        var cacheSize = undefinedDefault(options.cacheSize, 1048576);        // Requested cache size in bytes, default 1 MB.
+        var collectionCount = 0;                                             // Number of elements in the server collection.
+        var highestSavedPage = 0;                                            // Highest index of all the saved pages.
+        var highestSavedPageSize = 0;                                        // Item count of the saved page with the highest index.
+        var overflowed = cacheSize === 0;                                    // If the cache has overflowed (actualCacheSize > cacheSize or cacheSize == 0);
+        var pageSize = undefinedDefault(options.pageSize, 50);               // Number of elements to store per page.
+        var prefetchSize = undefinedDefault(options.prefetchSize, pageSize); // Number of elements to prefetch from the source when the cache is idling.
+        var version = "1.0";
+        var cacheFailure;
+
+        var pendingOperations = 0;
+
+        var source = options.source;
+        if (typeof source === "string") {
+            // Create a new cache source.
+            source = new ODataCacheSource(options);
+        }
+        source.options = options;
+
+        // Create a cache local store.
+        var store = datajs.createStore(options.name, options.mechanism);
+
+        var that = this;
+
+        that.onidle = options.idle;
+        that.stats = stats;
+
+        that.count = function () {
+            /// <summary>Counts the number of items in the collection.</summary>
+            /// <returns type="Object">A promise with the number of items.</returns>
+
+            if (cacheFailure) {
+                throw cacheFailure;
+            }
+
+            var deferred = createDeferred();
+            var canceled = false;
+
+            if (allDataLocal) {
+                delay(function () {
+                    deferred.resolve(collectionCount);
+                });
+
+                return deferred.promise();
+            }
+
+            // TODO: Consider returning the local data count instead once allDataLocal flag is set to true.
+            var request = source.count(function (count) {
+                request = null;
+                stats.counts++;
+                deferred.resolve(count);
+            }, function (err) {
+                request = null;
+                deferred.reject(extend(err, { canceled: canceled }));
+            });
+
+            return extend(deferred.promise(), {
+                cancel: function () {
+                    /// <summary>Aborts the count operation.</summary>
+                    if (request) {
+                        canceled = true;
+                        request.abort();
+                        request = null;
+                    }
+                }
+            });
+        };
+
+        that.clear = function () {
+            /// <summary>Cancels all running operations and clears all local data associated with this cache.</summary>
+            /// <remarks>
+            /// New read requests made while a clear operation is in progress will not be canceled.
+            /// Instead they will be queued for execution once the operation is completed.
+            /// </remarks>
+            /// <returns type="Object">A promise that has no value and can't be canceled.</returns>
+
+            if (cacheFailure) {
+                throw cacheFailure;
+            }
+
+            if (clearOperations.length === 0) {
+                var deferred = createDeferred();
+                var op = new DataCacheOperation(destroyStateMachine, deferred, false);
+                queueAndStart(op, clearOperations);
+                return deferred.promise();
+            }
+            return clearOperations[0].p;
+        };
+
+        that.filterForward = function (index, count, predicate) {
+            /// <summary>Filters the cache data based a predicate.</summary>
+            /// <param name="index" type="Number">The index of the item to start filtering forward from.</param>
+            /// <param name="count" type="Number">Maximum number of items to include in the result.</param>
+            /// <param name="predicate" type="Function">Callback function returning a boolean that determines whether an item should be included in the result or not.</param>
+            /// <remarks>
+            /// Specifying a negative count value will yield all the items in the cache that satisfy the predicate.
+            /// </remarks>
+            /// <returns type="DjsDeferred">A promise for an array of results.</returns>
+            return filter(index, count, predicate, false);
+        };
+
+        that.filterBack = function (index, count, predicate) {
+            /// <summary>Filters the cache data based a predicate.</summary>
+            /// <param name="index" type="Number">The index of the item to start filtering backward from.</param>
+            /// <param name="count" type="Number">Maximum number of items to include in the result.</param>
+            /// <param name="predicate" type="Function">Callback function returning a boolean that determines whether an item should be included in the result or not.</param>
+            /// <remarks>
+            /// Specifying a negative count value will yield all the items in the cache that satisfy the predicate.
+            /// </remarks>
+            /// <returns type="DjsDeferred">A promise for an array of results.</returns>
+            return filter(index, count, predicate, true);
+        };
+
+        that.readRange = function (index, count) {
+            /// <summary>Reads a range of adjacent records.</summary>
+            /// <param name="index" type="Number">Zero-based index of record range to read.</param>
+            /// <param name="count" type="Number">Number of records in the range.</param>
+            /// <remarks>
+            /// New read requests made while a clear operation is in progress will not be canceled.
+            /// Instead they will be queued for execution once the operation is completed.
+            /// </remarks>
+            /// <returns type="DjsDeferred">
+            /// A promise for an array of records; less records may be returned if the
+            /// end of the collection is found.
+            /// </returns>
+
+            checkZeroGreater(index, "index");
+            checkZeroGreater(count, "count");
+
+            if (cacheFailure) {
+                throw cacheFailure;
+            }
+
+            var deferred = createDeferred();
+
+            // Merging read operations would be a nice optimization here.
+            var op = new DataCacheOperation(readStateMachine, deferred, true, index, count, [], 0);
+            queueAndStart(op, readOperations);
+
+            return extend(deferred.promise(), {
+                cancel: function () {
+                    /// <summary>Aborts the readRange operation.</summary>
+                    op.cancel();
+                }
+            });
+        };
+
+        that.ToObservable = that.toObservable = function () {
+            /// <summary>Creates an Observable object that enumerates all the cache contents.</summary>
+            /// <returns>A new Observable object that enumerates all the cache contents.</returns>
+            if (!window.Rx || !window.Rx.Observable) {
+                throw { message: "Rx library not available - include rx.js" };
+            }
+
+            if (cacheFailure) {
+                throw cacheFailure;
+            }
+
+            return window.Rx.Observable.CreateWithDisposable(function (obs) {
+                var disposed = false;
+                var index = 0;
+
+                var errorCallback = function (error) {
+                    if (!disposed) {
+                        obs.OnError(error);
+                    }
+                };
+
+                var successCallback = function (data) {
+                    if (!disposed) {
+                        var i, len;
+                        for (i = 0, len = data.length; i < len; i++) {
+                            // The wrapper automatically checks for Dispose
+                            // on the observer, so we don't need to check it here.
+                            obs.OnNext(data[i]);
+                        }
+
+                        if (data.length < pageSize) {
+                            obs.OnCompleted();
+                        } else {
+                            index += pageSize;
+                            that.readRange(index, pageSize).then(successCallback, errorCallback);
+                        }
+                    }
+                };
+
+                that.readRange(index, pageSize).then(successCallback, errorCallback);
+
+                return { Dispose: function () { disposed = true; } };
+            });
+        };
+
+        var cacheFailureCallback = function (message) {
+            /// <summary>Creates a function that handles a callback by setting the cache into failure mode.</summary>
+            /// <param name="message" type="String">Message text.</param>
+            /// <returns type="Function">Function to use as error callback.</returns>
+            /// <remarks>
+            /// This function will specifically handle problems with critical store resources
+            /// during cache initialization.
+            /// </remarks>
+
+            return function (error) {
+                cacheFailure = { message: message, error: error };
+
+                // Destroy any pending clear or read operations.
+                // At this point there should be no prefetch operations.
+                // Count operations will go through but are benign because they
+                // won't interact with the store.
+                djsassert(prefetchOperations.length === 0, "prefetchOperations.length === 0");
+                var i, len;
+                for (i = 0, len = readOperations.length; i < len; i++) {
+                    readOperations[i].fireRejected(cacheFailure);
+                }
+                for (i = 0, len = clearOperations.length; i < len; i++) {
+                    clearOperations[i].fireRejected(cacheFailure);
+                }
+
+                // Null out the operation arrays.
+                readOperations = clearOperations = null;
+            };
+        };
+
+        var changeState = function (newState) {
+            /// <summary>Updates the cache's state and signals all pending operations of the change.</summary>
+            /// <param name="newState" type="Object">New cache state.</param>
+            /// <remarks>This method is a no-op if the cache's current state and the new state are the same.</remarks>
+
+            if (newState !== state) {
+                state = newState;
+                var operations = clearOperations.concat(readOperations, prefetchOperations);
+                var i, len;
+                for (i = 0, len = operations.length; i < len; i++) {
+                    operations[i].run(state);
+                }
+            }
+        };
+
+        var clearStore = function () {
+            /// <summary>Removes all the data stored in the cache.</summary>
+            /// <returns type="DjsDeferred">A promise with no value.</returns>
+            djsassert(state === CACHE_STATE_DESTROY || state === CACHE_STATE_INIT, "DataCache.clearStore() - cache is not on the destroy or initialize state, current sate = " + state);
+
+            var deferred = new DjsDeferred();
+            store.clear(function () {
+
+                // Reset the cache settings.
+                actualCacheSize = 0;
+                allDataLocal = false;
+                collectionCount = 0;
+                highestSavedPage = 0;
+                highestSavedPageSize = 0;
+                overflowed = cacheSize === 0;
+
+                // version is not reset, in case there is other state in eg V1.1 that is still around.
+
+                // Reset the cache stats.
+                stats = { counts: 0, netReads: 0, prefetches: 0, cacheReads: 0 };
+                that.stats = stats;
+
+                store.close();
+                deferred.resolve();
+            }, function (err) {
+                deferred.reject(err);
+            });
+            return deferred;
+        };
+
+        var dequeueOperation = function (operation) {
+            /// <summary>Removes an operation from the caches queues and changes the cache state to idle.</summary>
+            /// <param name="operation" type="DataCacheOperation">Operation to dequeue.</param>
+            /// <remarks>This method is used as a handler for the operation's oncomplete event.</remarks>
+
+            var removed = removeFromArray(clearOperations, operation);
+            if (!removed) {
+                removed = removeFromArray(readOperations, operation);
+                if (!removed) {
+                    removeFromArray(prefetchOperations, operation);
+                }
+            }
+
+            pendingOperations--;
+            changeState(CACHE_STATE_IDLE);
+        };
+
+        var fetchPage = function (start) {
+            /// <summary>Requests data from the cache source.</summary>
+            /// <param name="start" type="Number">Zero-based index of items to request.</param>
+            /// <returns type="DjsDeferred">A promise for a page object with (i)ndex, (c)ount, (d)ata.</returns>
+
+            djsassert(state !== CACHE_STATE_DESTROY, "DataCache.fetchPage() - cache is on the destroy state");
+            djsassert(state !== CACHE_STATE_IDLE, "DataCache.fetchPage() - cache is on the idle state");
+
+            var deferred = new DjsDeferred();
+            var canceled = false;
+
+            var request = source.read(start, pageSize, function (data) {
+                var page = { i: start, c: data.length, d: data };
+                deferred.resolve(page);
+            }, function (err) {
+                deferred.reject(err);
+            });
+
+            return extend(deferred, {
+                cancel: function () {
+                    if (request) {
+                        request.abort();
+                        canceled = true;
+                        request = null;
+                    }
+                }
+            });
+        };
+
+        var filter = function (index, count, predicate, backwards) {
+            /// <summary>Filters the cache data based a predicate.</summary>
+            /// <param name="index" type="Number">The index of the item to start filtering from.</param>
+            /// <param name="count" type="Number">Maximum number of items to include in the result.</param>
+            /// <param name="predicate" type="Function">Callback function returning a boolean that determines whether an item should be included in the result or not.</param>
+            /// <param name="backwards" type="Boolean">True if the filtering should move backward from the specified index, falsey otherwise.</param>
+            /// <remarks>
+            /// Specifying a negative count value will yield all the items in the cache that satisfy the predicate.
+            /// </remarks>
+            /// <returns type="DjsDeferred">A promise for an array of results.</returns>
+            index = parseInt10(index);
+            count = parseInt10(count);
+
+            if (isNaN(index)) {
+                throw { message: "'index' must be a valid number.", index: index };
+            }
+            if (isNaN(count)) {
+                throw { message: "'count' must be a valid number.", count: count };
+            }
+
+            if (cacheFailure) {
+                throw cacheFailure;
+            }
+
+            index = Math.max(index, 0);
+
+            var deferred = createDeferred();
+            var arr = [];
+            var canceled = false;
+            var pendingReadRange = null;
+
+            var readMore = function (readIndex, readCount) {
+                if (!canceled) {
+                    if (count >= 0 && arr.length >= count) {
+                        deferred.resolve(arr);
+                    } else {
+                        pendingReadRange = that.readRange(readIndex, readCount).then(function (data) {
+                            for (var i = 0, length = data.length; i < length && (count < 0 || arr.length < count); i++) {
+                                var dataIndex = backwards ? length - i - 1 : i;
+                                var item = data[dataIndex];
+                                if (predicate(item)) {
+                                    var element = {
+                                        index: readIndex + dataIndex,
+                                        item: item
+                                    };
+
+                                    backwards ? arr.unshift(element) : arr.push(element);
+                                }
+                            }
+
+                            // Have we reached the end of the collection?
+                            if ((!backwards && data.length < readCount) || (backwards && readIndex <= 0)) {
+                                deferred.resolve(arr);
+                            } else {
+                                var nextIndex = backwards ? Math.max(readIndex - pageSize, 0) : readIndex + readCount;
+                                readMore(nextIndex, pageSize);
+                            }
+                        }, function (err) {
+                            deferred.reject(err);
+                        });
+                    }
+                }
+            };
+
+            // Initially, we read from the given starting index to the next/previous page boundary
+            var initialPage = snapToPageBoundaries(index, index, pageSize);
+            var initialIndex = backwards ? initialPage.i : index;
+            var initialCount = backwards ? index - initialPage.i + 1 : initialPage.i + initialPage.c - index;
+            readMore(initialIndex, initialCount);
+
+            return extend(deferred.promise(), {
+                cancel: function () {
+                    /// <summary>Aborts the filter operation</summary>
+                    if (pendingReadRange) {
+                        pendingReadRange.cancel();
+                    }
+                    canceled = true;
+                }
+            });
+        };
+
+        var fireOnIdle = function () {
+            /// <summary>Fires an onidle event if any functions are assigned.</summary>
+
+            if (that.onidle && pendingOperations === 0) {
+                that.onidle();
+            }
+        };
+
+        var prefetch = function (start) {
+            /// <summary>Creates and starts a new prefetch operation.</summary>
+            /// <param name="start" type="Number">Zero-based index of the items to prefetch.</param>
+            /// <remarks>
+            /// This method is a no-op if any of the following conditions is true:
+            ///     1.- prefetchSize is 0
+            ///     2.- All data has been read and stored locally in the cache.
+            ///     3.- There is already an all data prefetch operation queued.
+            ///     4.- The cache has run out of available space (overflowed).
+            /// <remarks>
+
+            if (allDataLocal || prefetchSize === 0 || overflowed) {
+                return;
+            }
+
+            djsassert(state === CACHE_STATE_READ, "DataCache.prefetch() - cache is not on the read state, current state: " + state);
+
+            if (prefetchOperations.length === 0 || (prefetchOperations[0] && prefetchOperations[0].c !== -1)) {
+                // Merging prefetch operations would be a nice optimization here.
+                var op = new DataCacheOperation(prefetchStateMachine, null, true, start, prefetchSize, null, prefetchSize);
+                queueAndStart(op, prefetchOperations);
+            }
+        };
+
+        var queueAndStart = function (op, queue) {
+            /// <summary>Queues an operation and runs it.</summary>
+            /// <param name="op" type="DataCacheOperation">Operation to queue.</param>
+            /// <param name="queue" type="Array">Array that will store the operation.</param>
+
+            op.oncomplete = dequeueOperation;
+            queue.push(op);
+            pendingOperations++;
+            op.run(state);
+        };
+
+        var readPage = function (key) {
+            /// <summary>Requests a page from the cache local store.</summary>
+            /// <param name="key" type="Number">Zero-based index of the reuqested page.</param>
+            /// <returns type="DjsDeferred">A promise for a found flag and page object with (i)ndex, (c)ount, (d)ata, and (t)icks.</returns>
+
+            djsassert(state !== CACHE_STATE_DESTROY, "DataCache.readPage() - cache is on the destroy state");
+
+            var canceled = false;
+            var deferred = extend(new DjsDeferred(), {
+                cancel: function () {
+                    /// <summary>Aborts the readPage operation.</summary>
+                    canceled = true;
+                }
+            });
+
+            var error = storeFailureCallback(deferred, "Read page from store failure");
+
+            store.contains(key, function (contained) {
+                if (canceled) {
+                    return;
+                }
+                if (contained) {
+                    store.read(key, function (_, data) {
+                        if (!canceled) {
+                            deferred.resolve(data !== undefined, data);
+                        }
+                    }, error);
+                    return;
+                }
+                deferred.resolve(false);
+            }, error);
+            return deferred;
+        };
+
+        var savePage = function (key, page) {
+            /// <summary>Saves a page to the cache local store.</summary>
+            /// <param name="key" type="Number">Zero-based index of the requested page.</param>
+            /// <param name="page" type="Object">Object with (i)ndex, (c)ount, (d)ata, and (t)icks.</param>
+            /// <returns type="DjsDeferred">A promise with no value.</returns>
+
+            djsassert(state !== CACHE_STATE_DESTROY, "DataCache.savePage() - cache is on the destroy state");
+            djsassert(state !== CACHE_STATE_IDLE, "DataCache.savePage() - cache is on the idle state");
+
+            var canceled = false;
+
+            var deferred = extend(new DjsDeferred(), {
+                cancel: function () {
+                    /// <summary>Aborts the readPage operation.</summary>
+                    canceled = true;
+                }
+            });
+
+            var error = storeFailureCallback(deferred, "Save page to store failure");
+
+            var resolve = function () {
+                deferred.resolve(true);
+            };
+
+            if (page.c > 0) {
+                var pageBytes = estimateSize(page);
+                overflowed = cacheSize >= 0 && cacheSize < actualCacheSize + pageBytes;
+
+                if (!overflowed) {
+                    store.addOrUpdate(key, page, function () {
+                        updateSettings(page, pageBytes);
+                        saveSettings(resolve, error);
+                    }, error);
+                } else {
+                    resolve();
+                }
+            } else {
+                updateSettings(page, 0);
+                saveSettings(resolve, error);
+            }
+            return deferred;
+        };
+
+        var saveSettings = function (success, error) {
+            /// <summary>Saves the cache's current settings to the local store.</summary>
+            /// <param name="success" type="Function">Success callback.</param>
+            /// <param name="error" type="Function">Errror callback.</param>
+
+            var settings = {
+                actualCacheSize: actualCacheSize,
+                allDataLocal: allDataLocal,
+                cacheSize: cacheSize,
+                collectionCount: collectionCount,
+                highestSavedPage: highestSavedPage,
+                highestSavedPageSize: highestSavedPageSize,
+                pageSize: pageSize,
+                sourceId: source.identifier,
+                version: version
+            };
+
+            store.addOrUpdate("__settings", settings, success, error);
+        };
+
+        var storeFailureCallback = function (deferred/*, message*/) {
+            /// <summary>Creates a function that handles a store error.</summary>
+            /// <param name="deferred" type="DjsDeferred">Deferred object to resolve.</param>
+            /// <param name="message" type="String">Message text.</param>
+            /// <returns type="Function">Function to use as error callback.</returns>
+            /// <remarks>
+            /// This function will specifically handle problems when interacting with the store.
+            /// </remarks>
+
+            return function (/*error*/) {
+                // var console = window.console;
+                // if (console && console.log) {
+                //    console.log(message);
+                //    console.dir(error);
+                // }
+                deferred.resolve(false);
+            };
+        };
+
+        var updateSettings = function (page, pageBytes) {
+            /// <summary>Updates the cache's settings based on a page object.</summary>
+            /// <param name="page" type="Object">Object with (i)ndex, (c)ount, (d)ata.</param>
+            /// <param name="pageBytes" type="Number">Size of the page in bytes.</param>
+
+            var pageCount = page.c;
+            var pageIndex = page.i;
+
+            // Detect the collection size.
+            if (pageCount === 0) {
+                if (highestSavedPage === pageIndex - pageSize) {
+                    collectionCount = highestSavedPage + highestSavedPageSize;
+                }
+            } else {
+                highestSavedPage = Math.max(highestSavedPage, pageIndex);
+                if (highestSavedPage === pageIndex) {
+                    highestSavedPageSize = pageCount;
+                }
+                actualCacheSize += pageBytes;
+                if (pageCount < pageSize && !collectionCount) {
+                    collectionCount = pageIndex + pageCount;
+                }
+            }
+
+            // Detect the end of the collection.
+            if (!allDataLocal && collectionCount === highestSavedPage + highestSavedPageSize) {
+                allDataLocal = true;
+            }
+        };
+
+        var cancelStateMachine = function (operation, opTargetState, cacheState, data) {
+            /// <summary>State machine describing the behavior for cancelling a read or prefetch operation.</summary>
+            /// <param name="operation" type="DataCacheOperation">Operation being run.</param>
+            /// <param name="opTargetState" type="Object">Operation state to transition to.</param>
+            /// <param name="cacheState" type="Object">Current cache state.</param>
+            /// <param name="data" type="Object" optional="true">Additional data passed to the state.</param>
+            /// <remarks>
+            /// This state machine contains behavior common to read and prefetch operations.
+            /// </remarks>
+
+            var canceled = operation.canceled && opTargetState !== OPERATION_STATE_END;
+            if (canceled) {
+                if (opTargetState === OPERATION_STATE_CANCEL) {
+                    // Cancel state.
+                    // Data is expected to be any pending request made to the cache.
+                    if (data && data.cancel) {
+                        data.cancel();
+                    }
+                }
+            }
+            return canceled;
+        };
+
+        var destroyStateMachine = function (operation, opTargetState, cacheState) {
+            /// <summary>State machine describing the behavior of a clear operation.</summary>
+            /// <param name="operation" type="DataCacheOperation">Operation being run.</param>
+            /// <param name="opTargetState" type="Object">Operation state to transition to.</param>
+            /// <param name="cacheState" type="Object">Current cache state.</param>
+            /// <remarks>
+            /// Clear operations have the highest priority and can't be interrupted by other operations; however,
+            /// they will preempt any other operation currently executing.
+            /// </remarks>
+
+            var transition = operation.transition;
+
+            // Signal the cache that a clear operation is running.
+            if (cacheState !== CACHE_STATE_DESTROY) {
+                changeState(CACHE_STATE_DESTROY);
+                return true;
+            }
+
+            switch (opTargetState) {
+                case OPERATION_STATE_START:
+                    // Initial state of the operation.
+                    transition(DESTROY_STATE_CLEAR);
+                    break;
+
+                case OPERATION_STATE_END:
+                    // State that signals the operation is done.
+                    fireOnIdle();
+                    break;
+
+                case DESTROY_STATE_CLEAR:
+                    // State that clears all the local data of the cache.
+                    clearStore().then(function () {
+                        // Terminate the operation once the local store has been cleared.
+                        operation.complete();
+                    });
+                    // Wait until the clear request completes.
+                    operation.wait();
+                    break;
+
+                default:
+                    return false;
+            }
+            return true;
+        };
+
+        var prefetchStateMachine = function (operation, opTargetState, cacheState, data) {
+            /// <summary>State machine describing the behavior of a prefetch operation.</summary>
+            /// <param name="operation" type="DataCacheOperation">Operation being run.</param>
+            /// <param name="opTargetState" type="Object">Operation state to transition to.</param>
+            /// <param name="cacheState" type="Object">Current cache state.</param>
+            /// <param name="data" type="Object" optional="true">Additional data passed to the state.</param>
+            /// <remarks>
+            /// Prefetch operations have the lowest priority and will be interrupted by operations of
+            /// other kinds. A preempted prefetch operation will resume its execution only when the state
+            /// of the cache returns to idle.
+            ///
+            /// If a clear operation starts executing then all the prefetch operations are canceled,
+            /// even if they haven't started executing yet.
+            /// </remarks>
+
+            // Handle cancelation
+            if (!cancelStateMachine(operation, opTargetState, cacheState, data)) {
+
+                var transition = operation.transition;
+
+                // Handle preemption
+                if (cacheState !== CACHE_STATE_PREFETCH) {
+                    if (cacheState === CACHE_STATE_DESTROY) {
+                        if (opTargetState !== OPERATION_STATE_CANCEL) {
+                            operation.cancel();
+                        }
+                    } else if (cacheState === CACHE_STATE_IDLE) {
+                        // Signal the cache that a prefetch operation is running.
+                        changeState(CACHE_STATE_PREFETCH);
+                    }
+                    return true;
+                }
+
+                switch (opTargetState) {
+                    case OPERATION_STATE_START:
+                        // Initial state of the operation.
+                        if (prefetchOperations[0] === operation) {
+                            transition(READ_STATE_LOCAL, operation.i);
+                        }
+                        break;
+
+                    case READ_STATE_DONE:
+                        // State that determines if the operation can be resolved or has to
+                        // continue processing.
+                        // Data is expected to be the read page.
+                        var pending = operation.pending;
+
+                        if (pending > 0) {
+                            pending -= Math.min(pending, data.c);
+                        }
+
+                        // Are we done, or has all the data been stored?
+                        if (allDataLocal || pending === 0 || data.c < pageSize || overflowed) {
+                            operation.complete();
+                        } else {
+                            // Continue processing the operation.
+                            operation.pending = pending;
+                            transition(READ_STATE_LOCAL, data.i + pageSize);
+                        }
+                        break;
+
+                    default:
+                        return readSaveStateMachine(operation, opTargetState, cacheState, data, true);
+                }
+            }
+            return true;
+        };
+
+        var readStateMachine = function (operation, opTargetState, cacheState, data) {
+            /// <summary>State machine describing the behavior of a read operation.</summary>
+            /// <param name="operation" type="DataCacheOperation">Operation being run.</param>
+            /// <param name="opTargetState" type="Object">Operation state to transition to.</param>
+            /// <param name="cacheState" type="Object">Current cache state.</param>
+            /// <param name="data" type="Object" optional="true">Additional data passed to the state.</param>
+            /// <remarks>
+            /// Read operations have a higher priority than prefetch operations, but lower than
+            /// clear operations. They will preempt any prefetch operation currently running
+            /// but will be interrupted by a clear operation.
+            ///
+            /// If a clear operation starts executing then all the currently running
+            /// read operations are canceled. Read operations that haven't started yet will
+            /// wait in the start state until the destory operation finishes.
+            /// </remarks>
+
+            // Handle cancelation
+            if (!cancelStateMachine(operation, opTargetState, cacheState, data)) {
+
+                var transition = operation.transition;
+
+                // Handle preemption
+                if (cacheState !== CACHE_STATE_READ && opTargetState !== OPERATION_STATE_START) {
+                    if (cacheState === CACHE_STATE_DESTROY) {
+                        if (opTargetState !== OPERATION_STATE_START) {
+                            operation.cancel();
+                        }
+                    } else if (cacheState !== CACHE_STATE_WRITE) {
+                        // Signal the cache that a read operation is running.
+                        djsassert(state == CACHE_STATE_IDLE || state === CACHE_STATE_PREFETCH, "DataCache.readStateMachine() - cache is not on the read or idle state.");
+                        changeState(CACHE_STATE_READ);
+                    }
+
+                    return true;
+                }
+
+                switch (opTargetState) {
+                    case OPERATION_STATE_START:
+                        // Initial state of the operation.
+                        // Wait until the cache is idle or prefetching.
+                        if (cacheState === CACHE_STATE_IDLE || cacheState === CACHE_STATE_PREFETCH) {
+                            // Signal the cache that a read operation is running.
+                            changeState(CACHE_STATE_READ);
+                            if (operation.c > 0) {
+                                // Snap the requested range to a page boundary.
+                                var range = snapToPageBoundaries(operation.i, operation.c, pageSize);
+                                transition(READ_STATE_LOCAL, range.i);
+                            } else {
+                                transition(READ_STATE_DONE, operation);
+                            }
+                        }
+                        break;
+
+                    case READ_STATE_DONE:
+                        // State that determines if the operation can be resolved or has to
+                        // continue processing.
+                        // Data is expected to be the read page.
+                        appendPage(operation, data);
+                        var len = operation.d.length;
+                        // Are we done?
+                        if (operation.c === len || data.c < pageSize) {
+                            // Update the stats, request for a prefetch operation.
+                            stats.cacheReads++;
+                            prefetch(data.i + data.c);
+                            // Terminate the operation.
+                            operation.complete();
+                        } else {
+                            // Continue processing the operation.
+                            transition(READ_STATE_LOCAL, data.i + pageSize);
+                        }
+                        break;
+
+                    default:
+                        return readSaveStateMachine(operation, opTargetState, cacheState, data, false);
+                }
+            }
+
+            return true;
+        };
+
+        var readSaveStateMachine = function (operation, opTargetState, cacheState, data, isPrefetch) {
+            /// <summary>State machine describing the behavior for reading and saving data into the cache.</summary>
+            /// <param name="operation" type="DataCacheOperation">Operation being run.</param>
+            /// <param name="opTargetState" type="Object">Operation state to transition to.</param>
+            /// <param name="cacheState" type="Object">Current cache state.</param>
+            /// <param name="data" type="Object" optional="true">Additional data passed to the state.</param>
+            /// <param name="isPrefetch" type="Boolean">Flag indicating whether a read (false) or prefetch (true) operation is running.
+            /// <remarks>
+            /// This state machine contains behavior common to read and prefetch operations.
+            /// </remarks>
+
+            var error = operation.error;
+            var transition = operation.transition;
+            var wait = operation.wait;
+            var request;
+
+            switch (opTargetState) {
+                case OPERATION_STATE_END:
+                    // State that signals the operation is done.
+                    fireOnIdle();
+                    break;
+
+                case READ_STATE_LOCAL:
+                    // State that requests for a page from the local store.
+                    // Data is expected to be the index of the page to request.
+                    request = readPage(data).then(function (found, page) {
+                        // Signal the cache that a read operation is running.
+                        if (!operation.canceled) {
+                            if (found) {
+                                // The page is in the local store, check if the operation can be resolved.
+                                transition(READ_STATE_DONE, page);
+                            } else {
+                                // The page is not in the local store, request it from the source.
+                                transition(READ_STATE_SOURCE, data);
+                            }
+                        }
+                    });
+                    break;
+
+                case READ_STATE_SOURCE:
+                    // State that requests for a page from the cache source.
+                    // Data is expected to be the index of the page to request.
+                    request = fetchPage(data).then(function (page) {
+                        // Signal the cache that a read operation is running.
+                        if (!operation.canceled) {
+                            // Update the stats and save the page to the local store.
+                            if (isPrefetch) {
+                                stats.prefetches++;
+                            } else {
+                                stats.netReads++;
+                            }
+                            transition(READ_STATE_SAVE, page);
+                        }
+                    }, error);
+                    break;
+
+                case READ_STATE_SAVE:
+                    // State that saves a  page to the local store.
+                    // Data is expected to be the page to save.
+                    // Write access to the store is exclusive.
+                    if (cacheState !== CACHE_STATE_WRITE) {
+                        changeState(CACHE_STATE_WRITE);
+                        request = savePage(data.i, data).then(function (saved) {
+                            if (!operation.canceled) {
+                                if (!saved && isPrefetch) {
+                                    operation.pending = 0;
+                                }
+                                // Check if the operation can be resolved.
+                                transition(READ_STATE_DONE, data);
+                            }
+                            changeState(CACHE_STATE_IDLE);
+                        });
+                    }
+                    break;
+
+                default:
+                    // Unknown state that can't be handled by this state machine.
+                    return false;
+            }
+
+            if (request) {
+                // The operation might have been canceled between stack frames do to the async calls.
+                if (operation.canceled) {
+                    request.cancel();
+                } else if (operation.s === opTargetState) {
+                    // Wait for the request to complete.
+                    wait(request);
+                }
+            }
+
+            return true;
+        };
+
+        // Initialize the cache.
+        store.read("__settings", function (_, settings) {
+            if (assigned(settings)) {
+                var settingsVersion = settings.version;
+                if (!settingsVersion || settingsVersion.indexOf("1.") !== 0) {
+                    cacheFailureCallback("Unsupported cache store version " + settingsVersion)();
+                    return;
+                }
+
+                if (pageSize !== settings.pageSize || source.identifier !== settings.sourceId) {
+                    // The shape or the source of the data was changed so invalidate the store.
+                    clearStore().then(function () {
+                        // Signal the cache is fully initialized.
+                        changeState(CACHE_STATE_IDLE);
+                    }, cacheFailureCallback("Unable to clear store during initialization"));
+                } else {
+                    // Restore the saved settings.
+                    actualCacheSize = settings.actualCacheSize;
+                    allDataLocal = settings.allDataLocal;
+                    cacheSize = settings.cacheSize;
+                    collectionCount = settings.collectionCount;
+                    highestSavedPage = settings.highestSavedPage;
+                    highestSavedPageSize = settings.highestSavedPageSize;
+                    version = settingsVersion;
+
+                    // Signal the cache is fully initialized.
+                    changeState(CACHE_STATE_IDLE);
+                }
+            } else {
+                // This is a brand new cache.
+                saveSettings(function () {
+                    // Signal the cache is fully initialized.
+                    changeState(CACHE_STATE_IDLE);
+                }, cacheFailureCallback("Unable to write settings during initialization."));
+            }
+        }, cacheFailureCallback("Unable to read settings from store."));
+
+        return that;
+    };
+
+    datajs.createDataCache = function (options) {
+        /// <summary>Creates a data cache for a collection that is efficiently loaded on-demand.</summary>
+        /// <param name="options">
+        /// Options for the data cache, including name, source, pageSize,
+        /// prefetchSize, cacheSize, storage mechanism, and initial prefetch and local-data handler.
+        /// </param>
+        /// <returns type="DataCache">A new data cache instance.</returns>
+        checkUndefinedGreaterThanZero(options.pageSize, "pageSize");
+        checkUndefinedOrNumber(options.cacheSize, "cacheSize");
+        checkUndefinedOrNumber(options.prefetchSize, "prefetchSize");
+
+        if (!assigned(options.name)) {
+            throw { message: "Undefined or null name", options: options };
+        }
+
+        if (!assigned(options.source)) {
+            throw { message: "Undefined source", options: options };
+        }
+
+        return new DataCache(options);
+    };
+
+    // DATAJS INTERNAL START
+    window.datajs.estimateSize = estimateSize;
+    // DATAJS INTERNAL END
+
+    // CONTENT END
+})(this);
\ No newline at end of file
diff --git a/JSLib/src/datajs.js b/JSLib/src/datajs.js
new file mode 100644
index 0000000..fc5bb62
--- /dev/null
+++ b/JSLib/src/datajs.js
@@ -0,0 +1,61 @@
+// Copyright (c) Microsoft Open Technologies, Inc.  All rights reserved.
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal  in the Software without restriction, including without limitation the rights  to use, copy,
+// modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR  IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// WARRANTIES OF MERCHANTABILITY,  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+// datajs.js
+
+(function (window, undefined) {
+
+    var datajs = window.datajs || {};
+    var odata = window.OData || {};
+
+    // AMD support
+    if (typeof define === 'function' && define.amd) {
+        define('datajs', datajs);
+        define('OData', odata);
+    } else {
+        window.datajs = datajs;
+        window.OData = odata;
+    }
+
+    datajs.version = {
+        major: 1,
+        minor: 1,
+        build: 1
+    };
+
+    // INCLUDE: utils.js
+    // INCLUDE: xml.js
+
+    // INCLUDE: deferred.js
+
+    // INCLUDE: odata-utils.js
+    // INCLUDE: odata-net.js
+    // INCLUDE: odata-handler.js
+    // INCLUDE: odata-gml.js
+    // INCLUDE: odata-xml.js
+    // INCLUDE: odata-atom.js
+    // INCLUDE: odata-metadata.js
+    // INCLUDE: odata-json-light.js
+    // INCLUDE: odata-json.js
+    // INCLUDE: odata-batch.js
+    // INCLUDE: odata.js
+
+    // INCLUDE: store-dom.js
+    // INCLUDE: store-indexeddb.js
+    // INCLUDE: store-memory.js
+    // INCLUDE: store.js
+
+    // INCLUDE: cache-source.js
+    // INCLUDE: cache.js
+
+})(this);
\ No newline at end of file
diff --git a/JSLib/src/deferred.js b/JSLib/src/deferred.js
new file mode 100644
index 0000000..7f6f6a8
--- /dev/null
+++ b/JSLib/src/deferred.js
@@ -0,0 +1,179 @@
+/// <reference path="odata-utils.js" />
+
+// Copyright (c) Microsoft Open Technologies, Inc.  All rights reserved.
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal  in the Software without restriction, including without limitation the rights  to use, copy,
+// modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR  IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// WARRANTIES OF MERCHANTABILITY,  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+// deferred.js
+
+(function (window, undefined) {
+
+    // CONTENT START
+
+    var forwardCall = function (thisValue, name, returnValue) {
+        /// <summary>Creates a new function to forward a call.</summary>
+        /// <param name="thisValue" type="Object">Value to use as the 'this' object.</param>
+        /// <param name="name" type="String">Name of function to forward to.</param>
+        /// <param name="returnValue" type="Object">Return value for the forward call (helps keep identity when chaining calls).</param>
+        /// <returns type="Function">A new function that will forward a call.</returns>
+
+        return function () {
+            thisValue[name].apply(thisValue, arguments);
+            return returnValue;
+        };
+    };
+
+    var DjsDeferred = function () {
+        /// <summary>Initializes a new DjsDeferred object.</summary>
+        /// <remarks>
+        /// Compability Note A - Ordering of callbacks through chained 'then' invocations
+        ///
+        /// The Wiki entry at http://wiki.commonjs.org/wiki/Promises/A
+        /// implies that .then() returns a distinct object.
+        ////
+        /// For compatibility with http://api.jquery.com/category/deferred-object/
+        /// we return this same object. This affects ordering, as
+        /// the jQuery version will fire callbacks in registration
+        /// order regardless of whether they occur on the result
+        /// or the original object.
+        ///
+        /// Compability Note B - Fulfillment value
+        ///
+        /// The Wiki entry at http://wiki.commonjs.org/wiki/Promises/A
+        /// implies that the result of a success callback is the
+        /// fulfillment value of the object and is received by
+        /// other success callbacks that are chained.
+        ///
+        /// For compatibility with http://api.jquery.com/category/deferred-object/
+        /// we disregard this value instead.
+        /// </remarks>
+
+        this._arguments = undefined;
+        this._done = undefined;
+        this._fail = undefined;
+        this._resolved = false;
+        this._rejected = false;
+    };
+
+    DjsDeferred.prototype = {
+        then: function (fulfilledHandler, errorHandler /*, progressHandler */) {
+            /// <summary>Adds success and error callbacks for this deferred object.</summary>
+            /// <param name="fulfilledHandler" type="Function" mayBeNull="true" optional="true">Success callback.</param>
+            /// <param name="errorHandler" type="Function" mayBeNull="true" optional="true">Error callback.</param>
+            /// <remarks>See Compatibility Note A.</remarks>
+
+            if (fulfilledHandler) {
+                if (!this._done) {
+                    this._done = [fulfilledHandler];
+                } else {
+                    this._done.push(fulfilledHandler);
+                }
+            }
+
+            if (errorHandler) {
+                if (!this._fail) {
+                    this._fail = [errorHandler];
+                } else {
+                    this._fail.push(errorHandler);
+                }
+            }
+
+            //// See Compatibility Note A in the DjsDeferred constructor.
+            //// if (!this._next) {
+            ////    this._next = createDeferred();
+            //// }
+            //// return this._next.promise();
+
+            if (this._resolved) {
+                this.resolve.apply(this, this._arguments);
+            } else if (this._rejected) {
+                this.reject.apply(this, this._arguments);
+            }
+
+            return this;
+        },
+
+        resolve: function (/* args */) {
+            /// <summary>Invokes success callbacks for this deferred object.</summary>
+            /// <remarks>All arguments are forwarded to success callbacks.</remarks>
+
+
+            if (this._done) {
+                var i, len;
+                for (i = 0, len = this._done.length; i < len; i++) {
+                    //// See Compability Note B - Fulfillment value.
+                    //// var nextValue =
+                    this._done[i].apply(null, arguments);
+                }
+
+                //// See Compatibility Note A in the DjsDeferred constructor.
+                //// this._next.resolve(nextValue);
+                //// delete this._next;
+
+                this._done = undefined;
+                this._resolved = false;
+                this._arguments = undefined;
+            } else {
+                this._resolved = true;
+                this._arguments = arguments;
+            }
+        },
+
+        reject: function (/* args */) {
+            /// <summary>Invokes error callbacks for this deferred object.</summary>
+            /// <remarks>All arguments are forwarded to error callbacks.</remarks>
+            if (this._fail) {
+                var i, len;
+                for (i = 0, len = this._fail.length; i < len; i++) {
+                    this._fail[i].apply(null, arguments);
+                }
+
+                this._fail = undefined;
+                this._rejected = false;
+                this._arguments = undefined;
+            } else {
+                this._rejected = true;
+                this._arguments = arguments;
+            }
+        },
+
+        promise: function () {
+            /// <summary>Returns a version of this object that has only the read-only methods available.</summary>
+            /// <returns>An object with only the promise object.</returns>
+
+            var result = {};
+            result.then = forwardCall(this, "then", result);
+            return result;
+        }
+    };
+
+    var createDeferred = function () {
+        /// <summary>Creates a deferred object.</summary>
+        /// <returns type="DjsDeferred">
+        /// A new deferred object. If jQuery is installed, then a jQuery
+        /// Deferred object is returned, which provides a superset of features.
+        /// </returns>
+
+        if (window.jQuery && window.jQuery.Deferred) {
+            return new window.jQuery.Deferred();
+        } else {
+            return new DjsDeferred();
+        }
+    };
+
+    // DATAJS INTERNAL START
+    window.datajs.createDeferred = createDeferred;
+    window.datajs.DjsDeferred = DjsDeferred;
+    // DATAJS INTERNAL END
+
+    // CONTENT END
+})(this);
\ No newline at end of file
diff --git a/JSLib/src/odata-atom.js b/JSLib/src/odata-atom.js
new file mode 100644
index 0000000..d92c2a6
--- /dev/null
+++ b/JSLib/src/odata-atom.js
@@ -0,0 +1,1411 @@
+/// <reference path="odata-utils.js" />
+/// <reference path="odata-handler.js" />
+/// <reference path="odata-xml.js" />
+
+// Copyright (c) Microsoft Open Technologies, Inc.  All rights reserved.
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation 
+// files (the "Software"), to deal  in the Software without restriction, including without limitation the rights  to use, copy,
+// modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the 
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR  IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// WARRANTIES OF MERCHANTABILITY,  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+// odata-atom.js
+
+(function (window, undefined) {
+
+    var datajs = window.datajs || {};
+    var odata = window.OData || {};
+
+    // imports
+    var contains = datajs.contains;
+    var djsassert = datajs.djsassert;
+    var isArray = datajs.isArray;
+    var isObject = datajs.isObject;
+    var isXmlNSDeclaration = datajs.isXmlNSDeclaration;
+    var normalizeURI = datajs.normalizeURI;
+    var parseInt10 = datajs.parseInt10;
+    var xmlAppendChild = datajs.xmlAppendChild;
+    var xmlAppendChildren = datajs.xmlAppendChildren
+    var xmlAttributes = datajs.xmlAttributes;
+    var xmlAttributeNode = datajs.xmlAttributeNode;
+    var xmlAttributeValue = datajs.xmlAttributeValue;
+    var xmlBaseURI = datajs.xmlBaseURI;
+    var xmlChildElements = datajs.xmlChildElements;
+    var xmlDom = datajs.xmlDom;
+    var xmlFirstChildElement = datajs.xmlFirstChildElement;
+    var xmlFindElementByPath = datajs.xmlFindElementByPath;
+    var xmlFindNodeByPath = datajs.xmlFindNodeByPath;
+    var xmlInnerText = datajs.xmlInnerText;
+    var xmlLocalName = datajs.xmlLocalName;
+    var xmlNamespaceURI = datajs.xmlNamespaceURI;
+    var xmlNewAttribute = datajs.xmlNewAttribute;
+    var xmlNewElement = datajs.xmlNewElement;
+    var xmlNewFragment = datajs.xmlNewFragment;
+    var xmlNewNodeByPath = datajs.xmlNewNodeByPath;
+    var xmlNewNSDeclaration = datajs.xmlNewNSDeclaration;
+    var xmlNewText = datajs.xmlNewText;
+    var xmlNodeValue = datajs.xmlNodeValue;
+    var xmlNS = datajs.xmlNS;
+    var xmlnsNS = datajs.xmlnsNS;
+    var xmlQualifiedName = datajs.xmlQualifiedName;
+    var xmlParse = datajs.xmlParse;
+    var xmlSerialize = datajs.xmlSerialize;
+    var xmlSerializeDescendants = datajs.xmlSerializeDescendants;
+    var xmlSibling = datajs.xmlSibling;
+    var w3org = datajs.w3org;
+
+    var adoDs = odata.adoDs;
+    var contentType = odata.contentType;
+    var createAttributeExtension = odata.createAttributeExtension;
+    var createElementExtension = odata.createElementExtension;
+    var handler = odata.handler;
+    var isPrimitiveEdmType = odata.isPrimitiveEdmType;
+    var isFeed = odata.isFeed;
+    var isNamedStream = odata.isNamedStream;
+    var lookupEntityType = odata.lookupEntityType;
+    var lookupComplexType = odata.lookupComplexType;
+    var lookupProperty = odata.lookupProperty;
+    var navigationPropertyKind = odata.navigationPropertyKind;
+    var MAX_DATA_SERVICE_VERSION = odata.MAX_DATA_SERVICE_VERSION;
+    var maxVersion = odata.maxVersion;
+    var odataXmlNs = odata.odataXmlNs;
+    var odataMetaXmlNs = odata.odataMetaXmlNs;
+    var odataMetaPrefix = odata.odataMetaPrefix;
+    var odataPrefix = odata.odataPrefix;
+    var odataRelatedPrefix = odata.odataRelatedPrefix;
+    var odataScheme = odata.odataScheme;
+    var parseBool = odata.parseBool;
+    var parseDateTime = odata.parseDateTime;
+    var parseDateTimeOffset = odata.parseDateTimeOffset;
+    var parseDuration = odata.parseDuration;
+    var parseTimezone = odata.parseTimezone;
+    var xmlNewODataElement = odata.xmlNewODataElement;
+    var xmlNewODataElementInfo = odata.xmlNewODataElementInfo;
+    var xmlNewODataMetaAttribute = odata.xmlNewODataMetaAttribute;
+    var xmlNewODataMetaElement = odata.xmlNewODataMetaElement;
+    var xmlNewODataDataElement = odata.xmlNewODataDataElement;
+    var xmlReadODataEdmPropertyValue = odata.xmlReadODataEdmPropertyValue;
+    var xmlReadODataProperty = odata.xmlReadODataProperty;
+
+    // CONTENT START
+
+    var atomPrefix = "a";
+
+    var atomXmlNs = w3org + "2005/Atom";                    // http://www.w3.org/2005/Atom
+    var appXmlNs = w3org + "2007/app";                      // http://www.w3.org/2007/app
+
+    var odataEditMediaPrefix = adoDs + "/edit-media/";        // http://schemas.microsoft.com/ado/2007/08/dataservices/edit-media
+    var odataMediaResourcePrefix = adoDs + "/mediaresource/"; // http://schemas.microsoft.com/ado/2007/08/dataservices/mediaresource
+    var odataRelatedLinksPrefix = adoDs + "/relatedlinks/";   // http://schemas.microsoft.com/ado/2007/08/dataservices/relatedlinks
+
+    var atomAcceptTypes = ["application/atom+xml", "application/atomsvc+xml", "application/xml"];
+    var atomMediaType = atomAcceptTypes[0];
+
+    // These are the namespaces that are not considered ATOM extension namespaces.
+    var nonExtensionNamepaces = [atomXmlNs, appXmlNs, xmlNS, xmlnsNS];
+
+    // These are entity property mapping paths that have well-known paths.
+    var knownCustomizationPaths = {
+        SyndicationAuthorEmail: "author/email",
+        SyndicationAuthorName: "author/name",
+        SyndicationAuthorUri: "author/uri",
+        SyndicationContributorEmail: "contributor/email",
+        SyndicationContributorName: "contributor/name",
+        SyndicationContributorUri: "contributor/uri",
+        SyndicationPublished: "published",
+        SyndicationRights: "rights",
+        SyndicationSummary: "summary",
+        SyndicationTitle: "title",
+        SyndicationUpdated: "updated"
+    };
+
+    var expandedFeedCustomizationPath = function (path) {
+        /// <summary>Returns an expanded customization path if it's well-known.</summary>
+        /// <param name="path" type="String">Path to expand.</param>
+        /// <returns type="String">Expanded path or just 'path' otherwise.</returns>
+
+        return knownCustomizationPaths[path] || path;
+    };
+
+    var isExtensionNs = function (nsURI) {
+        /// <summary>Checks whether the specified namespace is an extension namespace to ATOM.</summary>
+        /// <param type="String" name="nsURI">Namespace to check.</param>
+        /// <returns type="Boolean">true if nsURI is an extension namespace to ATOM; false otherwise.</returns>
+
+        return !(contains(nonExtensionNamepaces, nsURI));
+    };
+
+    var atomFeedCustomization = function (customizationModel, entityType, model, propertyName, suffix) {
+        /// <summary>Creates an object describing a feed customization that was delcared in an OData conceptual schema.</summary>
+        /// <param name="customizationModel" type="Object">Object describing the customization delcared in the conceptual schema.</param>
+        /// <param name="entityType" type="Object">Object describing the entity type that owns the customization in an OData conceputal schema.</param>
+        /// <param name="model" type="Object">Object describing an OData conceptual schema.</param>
+        /// <param name="propertyName" type="String" optional="true">Name of the property to which this customization applies.</param>
+        /// <param name="suffix" type="String" optional="true">Suffix to feed customization properties in the conceptual schema.</param>
+        /// <returns type="Object">Object that describes an applicable feed customization.</returns>
+
+        suffix = suffix || "";
+        var targetPath = customizationModel["FC_TargetPath" + suffix];
+        if (!targetPath) {
+            return null;
+        }
+
+        var sourcePath = customizationModel["FC_SourcePath" + suffix];
+        var targetXmlPath = expandedFeedCustomizationPath(targetPath);
+
+        var propertyPath = propertyName ? propertyName + (sourcePath ? "/" + sourcePath : "") : sourcePath;
+        var propertyType = propertyPath && lookupPropertyType(model, entityType, propertyPath);
+        var nsURI = customizationModel["FC_NsUri" + suffix] || null;
+        var nsPrefix = customizationModel["FC_NsPrefix" + suffix] || null;
+        var keepinContent = customizationModel["FC_KeepInContent" + suffix] || "";
+
+        if (targetPath !== targetXmlPath) {
+            nsURI = atomXmlNs;
+            nsPrefix = atomPrefix;
+        }
+
+        return {
+            contentKind: customizationModel["FC_ContentKind" + suffix],
+            keepInContent: keepinContent.toLowerCase() === "true",
+            nsPrefix: nsPrefix,
+            nsURI: nsURI,
+            propertyPath: propertyPath,
+            propertyType: propertyType,
+            entryPath: targetXmlPath
+        };
+    };
+
+    var atomApplyAllFeedCustomizations = function (entityType, model, callback) {
+        /// <summary>Gets all the feed customizations that have to be applied to an entry as per the enity type declared in an OData conceptual schema.</summary>
+        /// <param name="entityType" type="Object">Object describing an entity type in a conceptual schema.</param>
+        /// <param name="model" type="Object">Object describing an OData conceptual schema.</param>
+        /// <param name="callback" type="Function">Callback function to be invoked for each feed customization that needs to be applied.</param>
+
+        var customizations = [];
+        while (entityType) {
+            var sourcePath = entityType.FC_SourcePath;
+            var customization = atomFeedCustomization(entityType, entityType, model);
+            if (customization) {
+                callback(customization);
+            }
+
+            var properties = entityType.property || [];
+            var i, len;
+            for (i = 0, len = properties.length; i < len; i++) {
+                var property = properties[i];
+                var suffixCounter = 0;
+                var suffix = "";
+
+                while (customization = atomFeedCustomization(property, entityType, model, property.name, suffix)) {
+                    callback(customization);
+                    suffixCounter++;
+                    suffix = "_" + suffixCounter;
+                }
+            }
+            entityType = lookupEntityType(entityType.baseType, model);
+        }
+        return customizations;
+    };
+
+    var atomReadExtensionAttributes = function (domElement) {
+        /// <summary>Reads ATOM extension attributes (any attribute not in the Atom namespace) from a DOM element.</summary>
+        /// <param name="domElement">DOM element with zero or more extension attributes.</param>
+        /// <returns type="Array">An array of extension attribute representations.</returns>
+
+        var extensions = [];
+        xmlAttributes(domElement, function (attribute) {
+            var nsURI = xmlNamespaceURI(attribute);
+            if (isExtensionNs(nsURI)) {
+                extensions.push(createAttributeExtension(attribute, true));
+            }
+        });
+        return extensions;
+    };
+
+    var atomReadExtensionElement = function (domElement) {
+        /// <summary>Reads an ATOM extension element (an element not in the ATOM namespaces).</summary>
+        /// <param name="domElement">DOM element not part of the atom namespace.</param>
+        /// <returns type="Object">Object representing the extension element.</returns>
+
+        return createElementExtension(domElement, /*addNamespaceURI*/true);
+    };
+
+    var atomReadDocument = function (domElement, baseURI, model) {
+        /// <summary>Reads an ATOM entry, feed or service document, producing an object model in return.</summary>
+        /// <param name="domElement">Top-level ATOM DOM element to read.</param>
+        /// <param name="baseURI" type="String">Base URI for normalizing relative URIs found in the ATOM document.</param>
+        /// <param name="model" type="Object">Object that describes the conceptual schema.</param>
+        /// <returns type="Object">The object model representing the specified element, undefined if the top-level element is not part of the ATOM specification.</returns>
+
+        var nsURI = xmlNamespaceURI(domElement);
+        var localName = xmlLocalName(domElement);
+
+        // Handle service documents.
+        if (nsURI === appXmlNs && localName === "service") {
+            return atomReadServiceDocument(domElement, baseURI);
+        }
+
+        // Handle feed and entry elements.
+        if (nsURI === atomXmlNs) {
+            if (localName === "feed") {
+                return atomReadFeed(domElement, baseURI, model);
+            }
+            if (localName === "entry") {
+                return atomReadEntry(domElement, baseURI, model);
+            }
+        }
+
+        // Allow undefined to be returned.
+    };
+
+    var atomReadAdvertisedActionOrFunction = function (domElement, baseURI) {
+        /// <summary>Reads the DOM element for an action or a function in an OData Atom document.</summary>
+        /// <param name="domElement">DOM element to read.</param>
+        /// <param name="baseURI" type="String">Base URI for normalizing the action or function target url.</param>
+        /// <returns type="Object">Object with title, target, and metadata fields.</returns>
+
+        var extensions = [];
+        var result = { extensions: extensions };
+        xmlAttributes(domElement, function (attribute) {
+            var localName = xmlLocalName(attribute);
+            var nsURI = xmlNamespaceURI(attribute);
+            var value = xmlNodeValue(attribute);
+
+            if (nsURI === null) {
+                if (localName === "title" || localName === "metadata") {
+                    result[localName] = value;
+                    return;
+                }
+                if (localName === "target") {
+                    result.target = normalizeURI(value, xmlBaseURI(domElement, baseURI));
+                    return;
+                }
+            }
+
+            if (isExtensionNs(nsURI)) {
+                extensions.push(createAttributeExtension(attribute, true));
+            }
+        });
+        return result;
+    };
+
+    var atomReadAdvertisedAction = function (domElement, baseURI, parentMetadata) {
+        /// <summary>Reads the DOM element for an action in an OData Atom document.</summary>
+        /// <param name="domElement">DOM element to read.</param>
+        /// <param name="baseURI" type="String">Base URI for normalizing the action or target url.</param>
+        /// <param name="parentMetadata" type="Object">Object to update with the action metadata.</param>
+
+        var actions = parentMetadata.actions = parentMetadata.actions || [];
+        actions.push(atomReadAdvertisedActionOrFunction(domElement, baseURI));
+    };
+
+    var atomReadAdvertisedFunction = function (domElement, baseURI, parentMetadata) {
+        /// <summary>Reads the DOM element for an action in an OData Atom document.</summary>
+        /// <param name="domElement">DOM element to read.</param>
+        /// <param name="baseURI" type="String">Base URI for normalizing the action or target url.</param>
+        /// <param name="parentMetadata" type="Object">Object to update with the action metadata.</param>
+
+        var functions = parentMetadata.functions = parentMetadata.functions || [];
+        functions.push(atomReadAdvertisedActionOrFunction(domElement, baseURI));
+    };
+
+    var atomReadFeed = function (domElement, baseURI, model) {
+        /// <summary>Reads a DOM element for an ATOM feed, producing an object model in return.</summary>
+        /// <param name="domElement">ATOM feed DOM element.</param>
+        /// <param name="baseURI" type="String">Base URI for normalizing relative URIs found in the ATOM feed.</param>
+        /// <param name="model">Metadata that describes the conceptual schema.</param>
+        /// <returns type="Object">A new object representing the feed.</returns>
+
+        var extensions = atomReadExtensionAttributes(domElement);
+        var feedMetadata = { feed_extensions: extensions };
+        var results = [];
+
+        var feed = { __metadata: feedMetadata, results: results };
+
+        baseURI = xmlBaseURI(domElement, baseURI);
+
+        xmlChildElements(domElement, function (child) {
+            var nsURI = xmlNamespaceURI(child);
+            var localName = xmlLocalName(child);
+
+            if (nsURI === odataMetaXmlNs) {
+                if (localName === "count") {
+                    feed.__count = parseInt(xmlInnerText(child), 10);
+                    return;
+                }
+                if (localName === "action") {
+                    atomReadAdvertisedAction(child, baseURI, feedMetadata);
+                    return;
+                }
+                if (localName === "function") {
+                    atomReadAdvertisedFunction(child, baseURI, feedMetadata);
+                    return;
+                }
+            }
+
+            if (isExtensionNs(nsURI)) {
+                extensions.push(createElementExtension(child));
+                return;
+            }
+
+            // The element should belong to the ATOM namespace.
+            djsassert(nsURI === atomXmlNs, "atomReadFeed - child feed element is not in the atom namespace!!");
+
+            if (localName === "entry") {
+                results.push(atomReadEntry(child, baseURI, model));
+                return;
+            }
+            if (localName === "link") {
+                atomReadFeedLink(child, feed, baseURI);
+                return;
+            }
+            if (localName === "id") {
+                feedMetadata.uri = normalizeURI(xmlInnerText(child), baseURI);
+                feedMetadata.uri_extensions = atomReadExtensionAttributes(child);
+                return;
+            }
+            if (localName === "title") {
+                feedMetadata.title = xmlInnerText(child) || "";
+                feedMetadata.title_extensions = atomReadExtensionAttributes(child);
+                return;
+            }
+        });
+
+        return feed;
+    };
+
+    var atomReadFeedLink = function (domElement, feed, baseURI) {
+        /// <summary>Reads an ATOM link DOM element for a feed.</summary>
+        /// <param name="domElement">ATOM link DOM element.</param>
+        /// <param name="feed">Feed object to be annotated with the link data.</param>
+        /// <param name="baseURI" type="String">Base URI for normalizing relative URIs found in the payload.</param>
+
+        var link = atomReadLink(domElement, baseURI);
+        var href = link.href;
+        var rel = link.rel;
+        var extensions = link.extensions;
+        var metadata = feed.__metadata;
+
+        if (rel === "next") {
+            feed.__next = href;
+            metadata.next_extensions = extensions;
+            return;
+        }
+        if (rel === "self") {
+            metadata.self = href;
+            metadata.self_extensions = extensions;
+            return;
+        }
+    };
+
+    var atomReadLink = function (domElement, baseURI) {
+        /// <summary>Reads an ATOM link DOM element.</summary>
+        /// <param name="linkElement">DOM element to read.</param>
+        /// <param name="baseURI" type="String">Base URI for normalizing the link href.</param>
+        /// <returns type="Object">A link element representation.</returns>
+
+        baseURI = xmlBaseURI(domElement, baseURI);
+
+        var extensions = [];
+        var link = { extensions: extensions, baseURI: baseURI };
+
+        xmlAttributes(domElement, function (attribute) {
+            var nsURI = xmlNamespaceURI(attribute);
+            var localName = xmlLocalName(attribute);
+            var value = attribute.value;
+
+            if (localName === "href") {
+                link.href = normalizeURI(value, baseURI);
+                return;
+            }
+            if (localName === "type" || localName === "rel") {
+                link[localName] = value;
+                return;
+            }
+
+            if (isExtensionNs(nsURI)) {
+                extensions.push(createAttributeExtension(attribute, true));
+            }
+        });
+
+        if (!link.href) {
+            throw { error: "href attribute missing on link element", element: domElement };
+        }
+
+        return link;
+    };
+
+    var atomGetObjectValueByPath = function (path, item) {
+        /// <summary>Gets a slashed path value from the specified item.</summary>
+        /// <param name="path" type="String">Property path to read ('/'-separated).</param>
+        /// <param name="item" type="Object">Object to get value from.</param>
+        /// <returns>The property value, possibly undefined if any path segment is missing.</returns>
+
+        // Fast path.
+        if (path.indexOf('/') === -1) {
+            return item[path];
+        } else {
+            var parts = path.split('/');
+            var i, len;
+            for (i = 0, len = parts.length; i < len; i++) {
+                // Avoid traversing a null object.
+                if (item === null) {
+                    return undefined;
+                }
+
+                item = item[parts[i]];
+                if (item === undefined) {
+                    return item;
+                }
+            }
+
+            return item;
+        }
+    };
+
+    var atomSetEntryValueByPath = function (path, target, value, propertyType) {
+        /// <summary>Sets a slashed path value on the specified target.</summary>
+        /// <param name="path" type="String">Property path to set ('/'-separated).</param>
+        /// <param name="target" type="Object">Object to set value on.</param>
+        /// <param name="value">Value to set.</param>
+        /// <param name="propertyType" type="String" optional="true">Property type to set in metadata.</param>
+
+        var propertyName;
+        if (path.indexOf('/') === -1) {
+            target[path] = value;
+            propertyName = path;
+        } else {
+            var parts = path.split('/');
+            var i, len;
+            for (i = 0, len = (parts.length - 1); i < len; i++) {
+                // We construct each step of the way if the property is missing;
+                // if it's already initialized to null, we stop further processing.
+                var next = target[parts[i]];
+                if (next === undefined) {
+                    next = {};
+                    target[parts[i]] = next;
+                } else if (next === null) {
+                    return;
+                }
+                target = next;
+            }
+            propertyName = parts[i];
+            target[propertyName] = value;
+        }
+
+        if (propertyType) {
+            var metadata = target.__metadata = target.__metadata || {};
+            var properties = metadata.properties = metadata.properties || {};
+            var property = properties[propertyName] = properties[propertyName] || {};
+            property.type = propertyType;
+        }
+    };
+
+    var atomApplyCustomizationToEntryObject = function (customization, domElement, entry) {
+        /// <summary>Applies a specific feed customization item to an object.</summary>
+        /// <param name="customization">Object with customization description.</param>
+        /// <param name="sourcePath">Property path to set ('source' in the description).</param>
+        /// <param name="entryElement">XML element for the entry that corresponds to the object being read.</param>
+        /// <param name="entryObject">Object being read.</param>
+        /// <param name="propertyType" type="String">Name of property type to set.</param>
+        /// <param name="suffix" type="String">Suffix to feed customization properties.</param>
+
+        var propertyPath = customization.propertyPath;
+        // If keepInConent equals true or the property value is null we do nothing as this overrides any other customization.
+        if (customization.keepInContent || atomGetObjectValueByPath(propertyPath, entry) === null) {
+            return;
+        }
+
+        var xmlNode = xmlFindNodeByPath(domElement, customization.nsURI, customization.entryPath);
+
+        // If the XML tree does not contain the necessary elements to read the value,
+        // then it shouldn't be considered null, but rather ignored at all. This prevents
+        // the customization from generating the object path down to the property.
+        if (!xmlNode) {
+            return;
+        }
+
+        var propertyType = customization.propertyType;
+        var propertyValue;
+
+        if (customization.contentKind === "xhtml") {
+            // Treat per XHTML in http://tools.ietf.org/html/rfc4287#section-3.1.1, including the DIV
+            // in the content.
+            propertyValue = xmlSerializeDescendants(xmlNode);
+        } else {
+            propertyValue = xmlReadODataEdmPropertyValue(xmlNode, propertyType || "Edm.String");
+        }
+        // Set the value on the entry.
+        atomSetEntryValueByPath(propertyPath, entry, propertyValue, propertyType);
+    };
+
+    var lookupPropertyType = function (metadata, owningType, path) {
+        /// <summary>Looks up the type of a property given its path in an entity type.</summary>
+        /// <param name="metadata">Metadata in which to search for base and complex types.</param>
+        /// <param name="owningType">Type to which property belongs.</param>
+        /// <param name="path" type="String" mayBeNull="false">Property path to look at.</param>
+        /// <returns type="String">The name of the property type; possibly null.</returns>
+
+        var parts = path.split("/");
+        var i, len;
+        while (owningType) {
+            // Keep track of the type being traversed, necessary for complex types.
+            var traversedType = owningType;
+
+            for (i = 0, len = parts.length; i < len; i++) {
+                // Traverse down the structure as necessary.
+                var properties = traversedType.property;
+                if (!properties) {
+                    break;
+                }
+
+                // Find the property by scanning the property list (might be worth pre-processing).
+                var propertyFound = lookupProperty(properties, parts[i]);
+                if (!propertyFound) {
+                    break;
+                }
+
+                var propertyType = propertyFound.type;
+
+                // We could in theory still be missing types, but that would
+                // be caused by a malformed path.
+                if (!propertyType || isPrimitiveEdmType(propertyType)) {
+                    return propertyType || null;
+                }
+
+                traversedType = lookupComplexType(propertyType, metadata);
+                if (!traversedType) {
+                    return null;
+                }
+            }
+
+            // Traverse up the inheritance chain.
+            owningType = lookupEntityType(owningType.baseType, metadata);
+        }
+
+        return null;
+    };
+
+    var atomReadEntry = function (domElement, baseURI, model) {
+        /// <summary>Reads a DOM element for an ATOM entry, producing an object model in return.</summary>
+        /// <param name="domElement">ATOM entry DOM element.</param>
+        /// <param name="baseURI" type="String">Base URI for normalizing relative URIs found in the ATOM entry.</param>
+        /// <param name="model">Metadata that describes the conceptual schema.</param>
+        /// <returns type="Object">A new object representing the entry.</returns>
+
+        var entryMetadata = {};
+        var entry = { __metadata: entryMetadata };
+
+        var etag = xmlAttributeValue(domElement, "etag", odataMetaXmlNs);
+        if (etag) {
+            entryMetadata.etag = etag;
+        }
+
+        baseURI = xmlBaseURI(domElement, baseURI);
+
+        xmlChildElements(domElement, function (child) {
+            var nsURI = xmlNamespaceURI(child);
+            var localName = xmlLocalName(child);
+
+            if (nsURI === atomXmlNs) {
+                if (localName === "id") {
+                    atomReadEntryId(child, entryMetadata, baseURI);
+                    return;
+                }
+                if (localName === "category") {
+                    atomReadEntryType(child, entryMetadata);
+                    return;
+                }
+                if (localName === "content") {
+                    atomReadEntryContent(child, entry, entryMetadata, baseURI);
+                    return;
+                }
+                if (localName === "link") {
+                    atomReadEntryLink(child, entry, entryMetadata, baseURI, model);
+                    return;
+                }
+                return;
+            }
+
+            if (nsURI === odataMetaXmlNs) {
+                if (localName === "properties") {
+                    atomReadEntryStructuralObject(child, entry, entryMetadata);
+                    return;
+                }
+                if (localName === "action") {
+                    atomReadAdvertisedAction(child, baseURI, entryMetadata);
+                    return;
+                }
+                if (localName === "function") {
+                    atomReadAdvertisedFunction(child, baseURI, entryMetadata);
+                    return;
+                }
+            }
+        });
+
+        // Apply feed customizations if applicable
+        var entityType = lookupEntityType(entryMetadata.type, model);
+        atomApplyAllFeedCustomizations(entityType, model, function (customization) {
+            atomApplyCustomizationToEntryObject(customization, domElement, entry);
+        });
+
+        return entry;
+    };
+
+    var atomReadEntryId = function (domElement, entryMetadata, baseURI) {
+        /// <summary>Reads an ATOM entry id DOM element.</summary>
+        /// <param name="domElement">ATOM id DOM element.</param>
+        /// <param name="entryMetadata">Entry metadata object to update with the id information.</param>
+
+        entryMetadata.uri = normalizeURI(xmlInnerText(domElement), xmlBaseURI(domElement, baseURI));
+        entryMetadata.uri_extensions = atomReadExtensionAttributes(domElement);
+    };
+
+    var atomReadEntryType = function (domElement, entryMetadata) {
+        /// <summary>Reads type information from an ATOM category DOM element.</summary>
+        /// <param name="domElement">ATOM category DOM element.</param>
+        /// <param name="entryMetadata">Entry metadata object to update with the type information.</param>
+
+        if (xmlAttributeValue(domElement, "scheme") === odataScheme) {
+            if (entryMetadata.type) {
+                throw { message: "Invalid AtomPub document: multiple category elements defining the entry type were encounterd withing an entry", element: domElement };
+            }
+
+            var typeExtensions = [];
+            xmlAttributes(domElement, function (attribute) {
+                var nsURI = xmlNamespaceURI(attribute);
+                var localName = xmlLocalName(attribute);
+
+                if (!nsURI) {
+                    if (localName !== "scheme" && localName !== "term") {
+                        typeExtensions.push(createAttributeExtension(attribute, true));
+                    }
+                    return;
+                }
+
+                if (isExtensionNs(nsURI)) {
+                    typeExtensions.push(createAttributeExtension(attribute, true));
+                }
+            });
+
+            entryMetadata.type = xmlAttributeValue(domElement, "term");
+            entryMetadata.type_extensions = typeExtensions;
+        }
+    };
+
+    var atomReadEntryContent = function (domElement, entry, entryMetadata, baseURI) {
+        /// <summary>Reads an ATOM content DOM element.</summary>
+        /// <param name="domElement">ATOM content DOM element.</param>
+        /// <param name="entry">Entry object to update with information.</param>
+        /// <param name="entryMetadata">Entry metadata object to update with the content information.</param>
+        /// <param name="baseURI" type="String">Base URI for normalizing relative URIs found in the Atom entry content.</param>
+
+        var src = xmlAttributeValue(domElement, "src");
+        var type = xmlAttributeValue(domElement, "type");
+
+        if (src) {
+            if (!type) {
+                throw {
+                    message: "Invalid AtomPub document: content element must specify the type attribute if the src attribute is also specified",
+                    element: domElement
+                };
+            }
+
+            entryMetadata.media_src = normalizeURI(src, xmlBaseURI(domElement, baseURI));
+            entryMetadata.content_type = type;
+        }
+
+        xmlChildElements(domElement, function (child) {
+            if (src) {
+                throw { message: "Invalid AtomPub document: content element must not have child elements if the src attribute is specified", element: domElement };
+            }
+
+            if (xmlNamespaceURI(child) === odataMetaXmlNs && xmlLocalName(child) === "properties") {
+                atomReadEntryStructuralObject(child, entry, entryMetadata);
+            }
+        });
+    };
+
+    var atomReadEntryLink = function (domElement, entry, entryMetadata, baseURI, model) {
+        /// <summary>Reads a link element on an entry.</summary>
+        /// <param name="atomEntryLink">'link' element on the entry.</param>
+        /// <param name="entry" type="Object">Entry object to update with the link data.</param>
+        /// <param name="entryMetadata">Entry metadata object to update with the link metadata.</param>
+        /// <param name="baseURI" type="String">Base URI for normalizing the link href.</param>
+        /// <param name="model" type="Object">Metadata that describes the conceptual schema.</param>
+
+        var link = atomReadLink(domElement, baseURI);
+
+        var rel = link.rel;
+        var href = link.href;
+        var extensions = link.extensions;
+
+        if (rel === "self") {
+            entryMetadata.self = href;
+            entryMetadata.self_link_extensions = extensions;
+            return;
+        }
+
+        if (rel === "edit") {
+            entryMetadata.edit = href;
+            entryMetadata.edit_link_extensions = extensions;
+            return;
+        }
+
+        if (rel === "edit-media") {
+            entryMetadata.edit_media = link.href;
+            entryMetadata.edit_media_extensions = extensions;
+            atomReadLinkMediaEtag(link, entryMetadata);
+            return;
+        }
+
+        // This might be a named stream edit link
+        if (rel.indexOf(odataEditMediaPrefix) === 0) {
+            atomReadNamedStreamEditLink(link, entry, entryMetadata);
+            return;
+        }
+
+        // This might be a named stram media resource (read) link
+        if (rel.indexOf(odataMediaResourcePrefix) === 0) {
+            atomReadNamedStreamSelfLink(link, entry, entryMetadata);
+            return;
+        }
+
+        // This might be a navigation property
+        if (rel.indexOf(odataRelatedPrefix) === 0) {
+            atomReadNavPropLink(domElement, link, entry, entryMetadata, model);
+            return;
+        }
+
+        if (rel.indexOf(odataRelatedLinksPrefix) === 0) {
+            atomReadNavPropRelatedLink(link, entryMetadata);
+            return;
+        }
+    };
+
+    var atomReadNavPropRelatedLink = function (link, entryMetadata) {
+        /// <summary>Reads a link represnting the links related to a navigation property in an OData Atom document.</summary>
+        /// <param name="link" type="Object">Object representing the parsed link DOM element.</param>
+        /// <param name="entryMetadata" type="Object">Entry metadata object to update with the related links information.</param>
+
+        var propertyName = link.rel.substring(odataRelatedLinksPrefix.length);
+        djsassert(propertyName, "atomReadNavPropRelatedLink - property name is null, empty or undefined!");
+
+        // Set the extra property information on the entry object metadata.
+        entryMetadata.properties = entryMetadata.properties || {};
+        var propertyMetadata = entryMetadata.properties[propertyName] = entryMetadata.properties[propertyName] || {};
+
+        propertyMetadata.associationuri = link.href;
+        propertyMetadata.associationuri_extensions = link.extensions;
+    };
+
+    var atomReadNavPropLink = function (domElement, link, entry, entryMetadata, model) {
+        /// <summary>Reads a link representing a navigation property in an OData Atom document.</summary>
+        /// <param name="domElement">DOM element for a navigation property in an OData Atom document.</summary>
+        /// <param name="link" type="Object">Object representing the parsed link DOM element.</param>
+        /// <param name="entry" type="Object">Entry object to update with the navigation property.</param>
+        /// <param name="entryMetadata">Entry metadata object to update with the navigation property metadata.</param>
+        /// <param name="model" type="Object">Metadata that describes the conceptual schema.</param>
+
+        // Get any inline data.
+        var inlineData;
+        var inlineElement = xmlFirstChildElement(domElement, odataMetaXmlNs, "inline");
+        if (inlineElement) {
+            var inlineDocRoot = xmlFirstChildElement(inlineElement);
+            var inlineBaseURI = xmlBaseURI(inlineElement, link.baseURI);
+            inlineData = inlineDocRoot ? atomReadDocument(inlineDocRoot, inlineBaseURI, model) : null;
+        } else {
+            // If the link has no inline content, we consider it deferred.
+            inlineData = { __deferred: { uri: link.href} };
+        }
+
+        var propertyName = link.rel.substring(odataRelatedPrefix.length);
+
+        // Set the property value on the entry object.
+        entry[propertyName] = inlineData;
+
+        // Set the extra property information on the entry object metadata.
+        entryMetadata.properties = entryMetadata.properties || {};
+        var propertyMetadata = entryMetadata.properties[propertyName] = entryMetadata.properties[propertyName] || {};
+
+        propertyMetadata.extensions = link.extensions;
+    };
+
+    var atomReadNamedStreamEditLink = function (link, entry, entryMetadata) {
+        /// <summary>Reads a link representing the edit-media url of a named stream in an OData Atom document.</summary>
+        /// <param name="link" type="Object">Object representing the parsed link DOM element.</param>
+        /// <param name="entry" type="Object">Entry object to update with the named stream data.</param>
+        /// <param name="entryMetadata">Entry metadata object to update with the named stream metadata.</param>
+
+        var propertyName = link.rel.substring(odataEditMediaPrefix.length);
+        djsassert(propertyName, "atomReadNamedStreamEditLink - property name is null, empty or undefined!");
+
+        var namedStreamMediaResource = atomGetEntryNamedStreamMediaResource(propertyName, entry, entryMetadata);
+        var mediaResource = namedStreamMediaResource.value;
+        var mediaResourceMetadata = namedStreamMediaResource.metadata;
+
+        var editMedia = link.href;
+
+        mediaResource.edit_media = editMedia;
+        mediaResource.content_type = link.type;
+        mediaResourceMetadata.edit_media_extensions = link.extensions;
+
+        // If there is only the edit link, make it the media self link as well.
+        mediaResource.media_src = mediaResource.media_src || editMedia;
+        mediaResourceMetadata.media_src_extensions = mediaResourceMetadata.media_src_extensions || [];
+
+        atomReadLinkMediaEtag(link, mediaResource);
+    };
+
+    var atomReadNamedStreamSelfLink = function (link, entry, entryMetadata) {
+        /// <summary>Reads a link representing the self url of a named stream in an OData Atom document.</summary>
+        /// <param name="link" type="Object">Object representing the parsed link DOM element.</param>
+        /// <param name="entry" type="Object">Entry object to update with the named stream data.</param>
+        /// <param name="entryMetadata">Entry metadata object to update with the named stream metadata.</param>
+
+        var propertyName = link.rel.substring(odataMediaResourcePrefix.length);
+        djsassert(propertyName, "atomReadNamedStreamEditLink - property name is null, empty or undefined!");
+
+        var namedStreamMediaResource = atomGetEntryNamedStreamMediaResource(propertyName, entry, entryMetadata);
+        var mediaResource = namedStreamMediaResource.value;
+        var mediaResourceMetadata = namedStreamMediaResource.metadata;
+
+        mediaResource.media_src = link.href;
+        mediaResourceMetadata.media_src_extensions = link.extensions;
+        mediaResource.content_type = link.type;
+    };
+
+    var atomGetEntryNamedStreamMediaResource = function (name, entry, entryMetadata) {
+        /// <summary>Gets the media resource object and metadata object for a named stream in an entry object.</summary>
+        /// <param name="link" type="Object">Object representing the parsed link DOM element.</param>
+        /// <param name="entry" type="Object">Entry object from which the media resource object will be obtained.</param>
+        /// <param name="entryMetadata" type="Object">Entry metadata object from which the media resource metadata object will be obtained.</param>
+        /// <remarks>
+        ///    If the entry doest' have a media resource for the named stream indicated by the name argument, then this function will create a new
+        ///    one along with its metadata object.
+        /// <remarks>
+        /// <returns type="Object"> Object containing the value and metadata of the named stream's media resource. <returns>
+
+        entryMetadata.properties = entryMetadata.properties || {};
+
+        var mediaResourceMetadata = entryMetadata.properties[name];
+        var mediaResource = entry[name] && entry[name].__mediaresource;
+
+        if (!mediaResource) {
+            mediaResource = {};
+            entry[name] = { __mediaresource: mediaResource };
+            entryMetadata.properties[name] = mediaResourceMetadata = {};
+        }
+        return { value: mediaResource, metadata: mediaResourceMetadata };
+    };
+
+    var atomReadLinkMediaEtag = function (link, mediaResource) {
+        /// <summary>Gets the media etag from the link extensions and updates the media resource object with it.</summary>
+        /// <param name="link" type="Object">Object representing the parsed link DOM element.</param>
+        /// <param name="mediaResource" type="Object">Object containing media information for an OData Atom entry.</param>
+        /// <remarks>
+        ///    The function will remove the extension object for the etag if it finds it in the link extensions and will set
+        ///    its value under the media_etag property of the mediaResource object.
+        /// <remarks>
+        /// <returns type="Object"> Object containing the value and metadata of the named stream's media resource. <returns>
+
+        var extensions = link.extensions;
+        var i, len;
+        for (i = 0, len = extensions.length; i < len; i++) {
+            if (extensions[i].namespaceURI === odataMetaXmlNs && extensions[i].name === "etag") {
+                mediaResource.media_etag = extensions[i].value;
+                extensions.splice(i, 1);
+                return;
+            }
+        }
+    };
+
+    var atomReadEntryStructuralObject = function (domElement, parent, parentMetadata) {
+        /// <summary>Reads an atom entry's property as a structural object and sets its value in the parent and the metadata in the parentMetadata objects.</summary>
+        /// <param name="propertiesElement">XML element for the 'properties' node.</param>
+        /// <param name="parent">
+        ///     Object that will contain the property value. It can be either an antom entry or
+        ///     an atom complex property object.
+        /// </param>
+        /// <param name="parentMetadata">Object that will contain the property metadata. It can be either an atom entry metadata or a complex property metadata object</param>
+
+        xmlChildElements(domElement, function (child) {
+            var property = xmlReadODataProperty(child);
+            if (property) {
+                var propertyName = property.name;
+                var propertiesMetadata = parentMetadata.properties = parentMetadata.properties || {};
+                propertiesMetadata[propertyName] = property.metadata;
+                parent[propertyName] = property.value;
+            }
+        });
+    };
+
+    var atomReadServiceDocument = function (domElement, baseURI) {
+        /// <summary>Reads an AtomPub service document</summary>
+        /// <param name="atomServiceDoc">DOM element for the root of an AtomPub service document</param>
+        /// <param name="baseURI" type="String">Base URI for normalizing relative URIs found in the AtomPub service document.</param>
+        /// <returns type="Object">An object that contains the properties of the service document</returns>
+
+        var workspaces = [];
+        var extensions = [];
+
+        baseURI = xmlBaseURI(domElement, baseURI);
+        // Find all the workspace elements.
+        xmlChildElements(domElement, function (child) {
+            if (xmlNamespaceURI(child) === appXmlNs && xmlLocalName(child) === "workspace") {
+                workspaces.push(atomReadServiceDocumentWorkspace(child, baseURI));
+                return;
+            }
+            extensions.push(createElementExtension(child));
+        });
+
+        // AtomPub (RFC 5023 Section 8.3.1) says a service document MUST contain one or
+        // more workspaces. Throw if we don't find any.
+        if (workspaces.length === 0) {
+            throw { message: "Invalid AtomPub service document: No workspace element found.", element: domElement };
+        }
+
+        return { workspaces: workspaces, extensions: extensions };
+    };
+
+    var atomReadServiceDocumentWorkspace = function (domElement, baseURI) {
+        /// <summary>Reads a single workspace element from an AtomPub service document</summary>
+        /// <param name="domElement">DOM element that represents a workspace of an AtomPub service document</param>
+        /// <param name="baseURI" type="String">Base URI for normalizing relative URIs found in the AtomPub service document workspace.</param>
+        /// <returns type="Object">An object that contains the properties of the workspace</returns>
+
+        var collections = [];
+        var extensions = [];
+        var title; // = undefined;
+
+        baseURI = xmlBaseURI(domElement, baseURI);
+
+        xmlChildElements(domElement, function (child) {
+            var nsURI = xmlNamespaceURI(child);
+            var localName = xmlLocalName(child);
+
+            if (nsURI === atomXmlNs) {
+                if (localName === "title") {
+                    if (title !== undefined) {
+                        throw { message: "Invalid AtomPub service document: workspace has more than one child title element", element: child };
+                    }
+
+                    title = xmlInnerText(child);
+                    return;
+                }
+            }
+
+            if (nsURI === appXmlNs) {
+                if (localName === "collection") {
+                    collections.push(atomReadServiceDocumentCollection(child, baseURI));
+                }
+                return;
+            }
+            extensions.push(atomReadExtensionElement(child));
+        });
+
+        return { title: title || "", collections: collections, extensions: extensions };
+    };
+
+    var atomReadServiceDocumentCollection = function (domElement, baseURI) {
+        /// <summary>Reads a service document collection element into an object.</summary>
+        /// <param name="domElement">DOM element that represents a collection of an AtomPub service document.</param>
+        /// <param name="baseURI" type="String">Base URI for normalizing relative URIs found in the AtomPub service document collection.</param>
+        /// <returns type="Object">An object that contains the properties of the collection.</returns>
+
+
+        var href = xmlAttributeValue(domElement, "href");
+
+        if (!href) {
+            throw { message: "Invalid AtomPub service document: collection has no href attribute", element: domElement };
+        }
+
+        baseURI = xmlBaseURI(domElement, baseURI);
+        href = normalizeURI(href, xmlBaseURI(domElement, baseURI));
+        var extensions = [];
+        var title; // = undefined;
+
+        xmlChildElements(domElement, function (child) {
+            var nsURI = xmlNamespaceURI(child);
+            var localName = xmlLocalName(child);
+
+            if (nsURI === atomXmlNs) {
+                if (localName === "title") {
+                    if (title !== undefined) {
+                        throw { message: "Invalid AtomPub service document: collection has more than one child title element", element: child };
+                    }
+                    title = xmlInnerText(child);
+                }
+                return;
+            }
+
+            if (nsURI !== appXmlNs) {
+                extensions.push(atomReadExtensionElement(domElement));
+            }
+        });
+
+        // AtomPub (RFC 5023 Section 8.3.3) says the collection element MUST contain
+        // a title element. It's likely to be problematic if the service doc doesn't
+        // have one so here we throw.
+        if (!title) {
+            throw { message: "Invalid AtomPub service document: collection has no title element", element: domElement };
+        }
+
+        return { title: title, href: href, extensions: extensions };
+    };
+
+    var atomNewElement = function (dom, name, children) {
+        /// <summary>Creates a new DOM element in the Atom namespace.</summary>
+        /// <param name="dom">DOM document used for creating the new DOM Element.</param>
+        /// <param name="name" type="String">Local name of the Atom element to create.</param>
+        /// <param name="children" type="Array">Array containing DOM nodes or string values that will be added as children of the new DOM element.</param>
+        /// <returns>New DOM element in the Atom namespace.</returns>
+        /// <remarks>
+        ///    If a value in the children collection is a string, then a new DOM text node is going to be created
+        ///    for it and then appended as a child of the new DOM Element.
+        /// </remarks>
+
+        return xmlNewElement(dom, atomXmlNs, xmlQualifiedName(atomPrefix, name), children);
+    };
+
+    var atomNewAttribute = function (dom, name, value) {
+        /// <summary>Creates a new DOM attribute for an Atom element in the default namespace.</summary>
+        /// <param name="dom">DOM document used for creating the new DOM Element.</param>
+        /// <param name="name" type="String">Local name of the OData attribute to create.</param>
+        /// <param name="value">Attribute value.</param>
+        /// <returns>New DOM attribute in the default namespace.</returns>
+
+        return xmlNewAttribute(dom, null, name, value);
+    };
+
+    var atomCanRemoveProperty = function (propertyElement) {
+        /// <summary>Checks whether the property represented by domElement can be removed from the atom document DOM tree.</summary>
+        /// <param name="propertyElement">DOM element for the property to test.</param>
+        /// <remarks>
+        ///     The property can only be removed if it doens't have any children and only has namespace or type declaration attributes.
+        /// </remarks>
+        /// <returns type="Boolean">True is the property can be removed; false otherwise.</returns>
+
+        if (propertyElement.childNodes.length > 0) {
+            return false;
+        }
+
+        var isEmpty = true;
+        var attributes = propertyElement.attributes;
+        var i, len;
+        for (i = 0, len = attributes.length; i < len && isEmpty; i++) {
+            var attribute = attributes[i];
+
+            isEmpty = isEmpty && isXmlNSDeclaration(attribute) ||
+                 (xmlNamespaceURI(attribute) == odataMetaXmlNs && xmlLocalName(attribute) === "type");
+        }
+        return isEmpty;
+    };
+
+    var atomNewODataNavigationProperty = function (dom, name, kind, value, model) {
+        /// <summary>Creates a new Atom link DOM element for a navigation property in an OData Atom document.</summary>
+        /// <param name="dom">DOM document used for creating the new DOM Element.</param>
+        /// <param name="name" type="String">Property name.</param>
+        /// <param name="kind" type="String">Navigation property kind. Expected values are "deferred", "entry", or "feed".</param>
+        /// <param name="value" optional="true" mayBeNull="true">Value of the navigation property, if any.</param>
+        /// <param name="model" type="Object" optional="true">Object describing an OData conceptual schema.</param>
+        /// <returns type="Object">
+        ///     Object containing the new Atom link DOM element for the navigation property and the
+        ///     required data service version for this property.
+        /// </returns>
+
+        var linkType = null;
+        var linkContent = null;
+        var linkContentBodyData = null;
+        var href = "";
+
+        if (kind !== "deferred") {
+            linkType = atomNewAttribute(dom, "type", "application/atom+xml;type=" + kind);
+            linkContent = xmlNewODataMetaElement(dom, "inline");
+
+            if (value) {
+                href = value.__metadata && value.__metadata.uri || "";
+                linkContentBodyData =
+                    atomNewODataFeed(dom, value, model) ||
+                    atomNewODataEntry(dom, value, model);
+                xmlAppendChild(linkContent, linkContentBodyData.element);
+            }
+        } else {
+            href = value.__deferred.uri;
+        }
+
+        var navProp = atomNewElement(dom, "link", [
+            atomNewAttribute(dom, "href", href),
+            atomNewAttribute(dom, "rel", normalizeURI(name, odataRelatedPrefix)),
+            linkType,
+            linkContent
+        ]);
+
+        return xmlNewODataElementInfo(navProp, linkContentBodyData ? linkContentBodyData.dsv : "1.0");
+    };
+
+    var atomNewODataEntryDataItem = function (dom, name, value, dataItemMetadata, dataItemModel, model) {
+        /// <summary>Creates a new DOM element for a data item in an entry, complex property, or collection property.</summary>
+        /// <param name="dom">DOM document used for creating the new DOM Element.</param>
+        /// <param name="name" type="String">Data item name.</param>
+        /// <param name="value" optional="true" mayBeNull="true">Value of the data item, if any.</param>
+        /// <param name="dataItemMetadata" type="Object" optional="true">Object containing metadata about the data item.</param>
+        /// <param name="dataItemModel" type="Object" optional="true">Object describing the data item in an OData conceptual schema.</param>
+        /// <param name="model" type="Object" optional="true">Object describing an OData conceptual schema.</param>
+        /// <returns type="Object">
+        ///     Object containing the new DOM element in the appropriate namespace for the data item and the
+        ///     required data service version for it.
+        /// </returns>
+
+        if (isNamedStream(value)) {
+            return null;
+        }
+
+        var dataElement = xmlNewODataDataElement(dom, name, value, dataItemMetadata, dataItemModel, model);
+        if (!dataElement) {
+            // This may be a navigation property.
+            var navPropKind = navigationPropertyKind(value, dataItemModel);
+            djsassert(navPropKind !== null, "atomNewODataEntryDataItem - navigation property kind is null for property " + name);
+
+            dataElement = atomNewODataNavigationProperty(dom, name, navPropKind, value, model);
+        }
+        return dataElement;
+    };
+
+    var atomEntryCustomization = function (dom, entry, entryProperties, customization) {
+        /// <summary>Applies a feed customization by transforming an Atom entry DOM element as needed.</summary>
+        /// <param name="dom">DOM document used for creating any new DOM nodes required by the customization.</param>
+        /// <param name="entry">DOM element for the Atom entry to which the customization is going to be applied.</param>
+        /// <param name="entryProperties">DOM element containing the properties of the Atom entry.</param>
+        /// <param name="customization" type="Object">Object describing an applicable feed customization.</param>
+        /// <remarks>
+        ///     Look into the atomfeedCustomization function for a description of the customization object.
+        /// </remarks>
+        /// <returns type="String">Data service version required by the applied customization</returns>
+
+        var atomProperty = xmlFindElementByPath(entryProperties, odataXmlNs, customization.propertyPath);
+        var atomPropertyNullAttribute = atomProperty && xmlAttributeNode(atomProperty, "null", odataMetaXmlNs);
+        var atomPropertyValue;
+        var dataServiceVersion = "1.0";
+
+        if (atomPropertyNullAttribute && atomPropertyNullAttribute.value === "true") {
+            return dataServiceVersion;
+        }
+
+        if (atomProperty) {
+            atomPropertyValue = xmlInnerText(atomProperty) || "";
+            if (!customization.keepInContent) {
+                dataServiceVersion = "2.0";
+                var parent = atomProperty.parentNode;
+                var candidate = parent;
+
+                parent.removeChild(atomProperty);
+                while (candidate !== entryProperties && atomCanRemoveProperty(candidate)) {
+                    parent = candidate.parentNode;
+                    parent.removeChild(candidate);
+                    candidate = parent;
+                }
+            }
+        }
+
+        var targetNode = xmlNewNodeByPath(dom, entry,
+            customization.nsURI, customization.nsPrefix, customization.entryPath);
+
+        if (targetNode.nodeType === 2) {
+            targetNode.value = atomPropertyValue;
+            return dataServiceVersion;
+        }
+
+        var contentKind = customization.contentKind;
+        xmlAppendChildren(targetNode, [
+                contentKind && xmlNewAttribute(dom, null, "type", contentKind),
+                contentKind === "xhtml" ? xmlNewFragment(dom, atomPropertyValue) : atomPropertyValue
+        ]);
+
+        return dataServiceVersion;
+    };
+
+    var atomNewODataEntry = function (dom, data, model) {
+        /// <summary>Creates a new DOM element for an Atom entry.</summary>
+        /// <param name="dom">DOM document used for creating the new DOM Element.</param>
+        /// <param name="data" type="Object">Entry object in the library's internal representation.</param>
+        /// <param name="model" type="Object" optional="true">Object describing an OData conceptual schema.</param>
+        /// <returns type="Object">
+        ///     Object containing the new DOM element for the Atom entry and the required data service version for it.
+        /// </returns>
+
+        var payloadMetadata = data.__metadata || {};
+        var propertiesMetadata = payloadMetadata.properties || {};
+
+        var etag = payloadMetadata.etag;
+        var uri = payloadMetadata.uri;
+        var typeName = payloadMetadata.type;
+        var entityType = lookupEntityType(typeName, model);
+
+        var properties = xmlNewODataMetaElement(dom, "properties");
+        var entry = atomNewElement(dom, "entry", [
+            atomNewElement(dom, "author",
+                atomNewElement(dom, "name")
+            ),
+            etag && xmlNewODataMetaAttribute(dom, "etag", etag),
+            uri && atomNewElement(dom, "id", uri),
+            typeName && atomNewElement(dom, "category", [
+                atomNewAttribute(dom, "term", typeName),
+                atomNewAttribute(dom, "scheme", odataScheme)
+            ]),
+        // TODO: MLE support goes here.
+            atomNewElement(dom, "content", [
+                atomNewAttribute(dom, "type", "application/xml"),
+                properties
+            ])
+        ]);
+
+        var dataServiceVersion = "1.0";
+        for (var name in data) {
+            if (name !== "__metadata") {
+                var entryDataItemMetadata = propertiesMetadata[name] || {};
+                var entryDataItemModel = entityType && (
+                    lookupProperty(entityType.property, name) ||
+                    lookupProperty(entityType.navigationProperty, name));
+
+                var entryDataItem = atomNewODataEntryDataItem(dom, name, data[name], entryDataItemMetadata, entryDataItemModel, model);
+                if (entryDataItem) {
+                    var entryElement = entryDataItem.element;
+                    var entryElementParent = (xmlNamespaceURI(entryElement) === atomXmlNs) ? entry : properties;
+
+                    xmlAppendChild(entryElementParent, entryElement);
+                    dataServiceVersion = maxVersion(dataServiceVersion, entryDataItem.dsv);
+                }
+            }
+        }
+
+        atomApplyAllFeedCustomizations(entityType, model, function (customization) {
+            var customizationDsv = atomEntryCustomization(dom, entry, properties, customization);
+            dataServiceVersion = maxVersion(dataServiceVersion, customizationDsv);
+        });
+
+        return xmlNewODataElementInfo(entry, dataServiceVersion);
+    };
+
+    var atomNewODataFeed = function (dom, data, model) {
+        /// <summary>Creates a new DOM element for an Atom feed.</summary>
+        /// <param name="dom">DOM document used for creating the new DOM Element.</param>
+        /// <param name="data" type="Object">Feed object in the library's internal representation.</param>
+        /// <param name="model" type="Object" optional="true">Object describing an OData conceptual schema.</param>
+        /// <returns type="Object">
+        ///     Object containing the new DOM element for the Atom feed and the required data service version for it.
+        /// </returns>
+
+        var entries = isArray(data) ? data : data.results;
+
+        if (!entries) {
+            return null;
+        }
+
+        var dataServiceVersion = "1.0";
+        var atomFeed = atomNewElement(dom, "feed");
+
+        var i, len;
+        for (i = 0, len = entries.length; i < len; i++) {
+            var atomEntryData = atomNewODataEntry(dom, entries[i], model);
+            xmlAppendChild(atomFeed, atomEntryData.element);
+            dataServiceVersion = maxVersion(dataServiceVersion, atomEntryData.dsv);
+        }
+        return xmlNewODataElementInfo(atomFeed, dataServiceVersion);
+    };
+
+    var atomNewODataDocument = function (data, model) {
+        /// <summary>Creates a new OData Atom document.</summary>
+        /// <param name="data" type="Object">Feed or entry object in the libary's internal representaion.</param>
+        /// <param name="model" type="Object" optional="true">Object describing an OData conceptual schema.</param>
+        /// <returns type="Object">
+        ///     Object containing the new DOM document for the Atom document and the required data service version for it.
+        /// </returns>
+
+        if (data) {
+            var atomRootWriter = isFeed(data) && atomNewODataFeed ||
+                isObject(data) && atomNewODataEntry;
+
+            if (atomRootWriter) {
+                var dom = xmlDom();
+                var atomRootData = atomRootWriter(dom, data, model);
+
+                if (atomRootData) {
+                    var atomRootElement = atomRootData.element;
+                    xmlAppendChildren(atomRootElement, [
+                        xmlNewNSDeclaration(dom, odataMetaXmlNs, odataMetaPrefix),
+                        xmlNewNSDeclaration(dom, odataXmlNs, odataPrefix)
+                    ]);
+                    return xmlNewODataElementInfo(xmlAppendChild(dom, atomRootElement), atomRootData.dsv);
+                }
+            }
+        }
+        return null;
+    };
+
+    var atomParser = function (handler, text, context) {
+        /// <summary>Parses an ATOM document (feed, entry or service document).</summary>
+        /// <param name="handler">This handler.</param>
+        /// <param name="text" type="String">Document text.</param>
+        /// <param name="context" type="Object">Object with parsing context.</param>
+        /// <returns>An object representation of the document; undefined if not applicable.</returns>
+
+        if (text) {
+            var atomDoc = xmlParse(text);
+            var atomRoot = xmlFirstChildElement(atomDoc);
+            if (atomRoot) {
+                return atomReadDocument(atomRoot, null, context.metadata);
+            }
+        }
+    };
+
+    var atomSerializer = function (handler, data, context) {
+        /// <summary>Serializes an ATOM object into a document (feed or entry).</summary>
+        /// <param name="handler">This handler.</param>
+        /// <param name="data" type="Object">Representation of feed or entry.</param>
+        /// <param name="context" type="Object">Object with parsing context.</param>
+        /// <returns>An text representation of the data object; undefined if not applicable.</returns>
+
+        var cType = context.contentType = context.contentType || contentType(atomMediaType);
+        if (cType && cType.mediaType === atomMediaType) {
+            var atomDoc = atomNewODataDocument(data, context.metadata);
+            if (atomDoc) {
+                context.dataServiceVersion = maxVersion(context.dataServiceVersion || "1.0", atomDoc.dsv);
+                return xmlSerialize(atomDoc.element);
+            }
+        }
+        // Allow undefined to be returned.
+    };
+
+    odata.atomHandler = handler(atomParser, atomSerializer, atomAcceptTypes.join(","), MAX_DATA_SERVICE_VERSION);
+
+    // DATAJS INTERNAL START
+    odata.atomParser = atomParser;
+    odata.atomSerializer = atomSerializer;
+    odata.atomReadDocument = atomReadDocument;
+    odata.atomReadFeed = atomReadFeed;
+    odata.atomReadFeedLink = atomReadFeedLink;
+    odata.atomReadLink = atomReadLink;
+    odata.atomReadExtensionElement = atomReadExtensionElement;
+    odata.atomReadExtensionAttributes = atomReadExtensionAttributes;
+    odata.atomReadEntry = atomReadEntry;
+    odata.atomReadEntryType = atomReadEntryType;
+    odata.atomReadEntryContent = atomReadEntryContent;
+    odata.atomReadEntryLink = atomReadEntryLink;
+    odata.atomReadEntryStructuralObject = atomReadEntryStructuralObject;
+    odata.atomReadServiceDocument = atomReadServiceDocument;
+    odata.atomReadServiceDocumentWorkspace = atomReadServiceDocumentWorkspace;
+    odata.atomReadServiceDocumentCollection = atomReadServiceDocumentCollection;
+    odata.expandedFeedCustomizationPath = expandedFeedCustomizationPath;
+    odata.lookupPropertyType = lookupPropertyType;
+    odata.atomSetEntryValueByPath = atomSetEntryValueByPath;
+    // DATAJS INTERNAL END
+
+    // CONTENT END
+})(this);
\ No newline at end of file
diff --git a/JSLib/src/odata-batch.js b/JSLib/src/odata-batch.js
new file mode 100644
index 0000000..770d875
--- /dev/null
+++ b/JSLib/src/odata-batch.js
@@ -0,0 +1,393 @@
+/// <reference path="odata-utils.js" />
+
+// Copyright (c) Microsoft Open Technologies, Inc.  All rights reserved.
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation 
+// files (the "Software"), to deal  in the Software without restriction, including without limitation the rights  to use, copy,
+// modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the 
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR  IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// WARRANTIES OF MERCHANTABILITY,  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+// odata-batch.js
+
+(function (window, undefined) {
+
+    var datajs = window.datajs || {};
+    var odata = window.OData || {};
+
+    // Imports
+
+    var extend = datajs.extend;
+    var isArray = datajs.isArray;
+    var trimString = datajs.trimString;
+
+    var contentType = odata.contentType;
+    var handler = odata.handler;
+    var isBatch = odata.isBatch;
+    var MAX_DATA_SERVICE_VERSION = odata.MAX_DATA_SERVICE_VERSION;
+    var normalizeHeaders = odata.normalizeHeaders;
+    var payloadTypeOf = odata.payloadTypeOf;
+    var prepareRequest = odata.prepareRequest;
+
+    // CONTENT START
+    var batchMediaType = "multipart/mixed";
+    var responseStatusRegex = /^HTTP\/1\.\d (\d{3}) (.*)$/i;
+    var responseHeaderRegex = /^([^()<>@,;:\\"\/[\]?={} \t]+)\s?:\s?(.*)/;
+
+    var hex16 = function () {
+        /// <summary>
+        /// Calculates a random 16 bit number and returns it in hexadecimal format.
+        /// </summary>
+        /// <returns type="String">A 16-bit number in hex format.</returns>
+
+        return Math.floor((1 + Math.random()) * 0x10000).toString(16).substr(1);
+    };
+
+    var createBoundary = function (prefix) {
+        /// <summary>
+        /// Creates a string that can be used as a multipart request boundary.
+        /// </summary>
+        /// <param name="prefix" type="String" optional="true">String to use as the start of the boundary string</param>
+        /// <returns type="String">Boundary string of the format: <prefix><hex16>-<hex16>-<hex16></returns>
+
+        return prefix + hex16() + "-" + hex16() + "-" + hex16();
+    };
+
+    var partHandler = function (context) {
+        /// <summary>
+        /// Gets the handler for data serialization of individual requests / responses in a batch.
+        /// </summary>
+        /// <param name="context">Context used for data serialization.</param>
+        /// <returns>Handler object.</returns>
+
+        return context.handler.partHandler;
+    };
+
+    var currentBoundary = function (context) {
+        /// <summary>
+        /// Gets the current boundary used for parsing the body of a multipart response.
+        /// </summary>
+        /// <param name="context">Context used for parsing a multipart response.</param>
+        /// <returns type="String">Boundary string.</returns>
+
+        var boundaries = context.boundaries;
+        return boundaries[boundaries.length - 1];
+    };
+
+    var batchParser = function (handler, text, context) {
+        /// <summary>Parses a batch response.</summary>
+        /// <param name="handler">This handler.</param>
+        /// <param name="text" type="String">Batch text.</param>
+        /// <param name="context" type="Object">Object with parsing context.</param>
+        /// <returns>An object representation of the batch.</returns>
+
+        var boundary = context.contentType.properties["boundary"];
+        return { __batchResponses: readBatch(text, { boundaries: [boundary], handlerContext: context }) };
+    };
+
+    var batchSerializer = function (handler, data, context) {
+        /// <summary>Serializes a batch object representation into text.</summary>
+        /// <param name="handler">This handler.</param>
+        /// <param name="data" type="Object">Representation of a batch.</param>
+        /// <param name="context" type="Object">Object with parsing context.</param>
+        /// <returns>An text representation of the batch object; undefined if not applicable.</returns>
+
+        var cType = context.contentType = context.contentType || contentType(batchMediaType);
+        if (cType.mediaType === batchMediaType) {
+            return writeBatch(data, context);
+        }
+    };
+
+    var readBatch = function (text, context) {
+        /// <summary>
+        /// Parses a multipart/mixed response body from from the position defined by the context.
+        /// </summary>
+        /// <param name="text" type="String" optional="false">Body of the multipart/mixed response.</param>
+        /// <param name="context">Context used for parsing.</param>
+        /// <returns>Array of objects representing the individual responses.</returns>
+
+        var delimiter = "--" + currentBoundary(context);
+
+        // Move beyond the delimiter and read the complete batch
+        readTo(text, context, delimiter);
+
+        // Ignore the incoming line
+        readLine(text, context);
+
+        // Read the batch parts
+        var responses = [];
+        var partEnd;
+
+        while (partEnd !== "--" && context.position < text.length) {
+            var partHeaders = readHeaders(text, context);
+            var partContentType = contentType(partHeaders["Content-Type"]);
+
+            var changeResponses;
+            if (partContentType && partContentType.mediaType === batchMediaType) {
+                context.boundaries.push(partContentType.properties["boundary"]);
+                try {
+                    changeResponses = readBatch(text, context);
+                } catch (e) {
+                    e.response = readResponse(text, context, delimiter);
+                    changeResponses = [e];
+                }
+                responses.push({ __changeResponses: changeResponses });
+                context.boundaries.pop();
+                readTo(text, context, "--" + currentBoundary(context));
+            } else {
+                if (!partContentType || partContentType.mediaType !== "application/http") {
+                    throw { message: "invalid MIME part type " };
+                }
+                // Skip empty line
+                readLine(text, context);
+                // Read the response
+                var response = readResponse(text, context, delimiter);
+                try {
+                    if (response.statusCode >= 200 && response.statusCode <= 299) {
+                        partHandler(context.handlerContext).read(response, context.handlerContext);
+                    } else {
+                        // Keep track of failed responses and continue processing the batch.
+                        response = { message: "HTTP request failed", response: response };
+                    }
+                } catch (e) {
+                    response = e;
+                }
+
+                responses.push(response);
+            }
+
+            partEnd = text.substr(context.position, 2);
+
+            // Ignore the incoming line.
+            readLine(text, context);
+        }
+        return responses;
+    };
+
+    var readHeaders = function (text, context) {
+        /// <summary>
+        /// Parses the http headers in the text from the position defined by the context.
+        /// </summary>
+        /// <param name="text" type="String" optional="false">Text containing an http response's headers</param>
+        /// <param name="context">Context used for parsing.</param>
+        /// <returns>Object containing the headers as key value pairs.</returns>
+        /// <remarks>
+        /// This function doesn't support split headers and it will stop reading when it hits two consecutive line breaks.
+        /// </remarks>
+
+        var headers = {};
+        var parts;
+        var line;
+        var pos;
+
+        do {
+            pos = context.position;
+            line = readLine(text, context);
+            parts = responseHeaderRegex.exec(line);
+            if (parts !== null) {
+                headers[parts[1]] = parts[2];
+            } else {
+                // Whatever was found is not a header, so reset the context position.
+                context.position = pos;
+            }
+        } while (line && parts);
+
+        normalizeHeaders(headers);
+
+        return headers;
+    };
+
+    var readResponse = function (text, context, delimiter) {
+        /// <summary>
+        /// Parses an HTTP response.
+        /// </summary>
+        /// <param name="text" type="String" optional="false">Text representing the http response.</param>
+        /// <param name="context" optional="false">Context used for parsing.</param>
+        /// <param name="delimiter" type="String" optional="false">String used as delimiter of the multipart response parts.</param>
+        /// <returns>Object representing the http response.</returns>
+
+        // Read the status line.
+        var pos = context.position;
+        var match = responseStatusRegex.exec(readLine(text, context));
+
+        var statusCode;
+        var statusText;
+        var headers;
+
+        if (match) {
+            statusCode = match[1];
+            statusText = match[2];
+            headers = readHeaders(text, context);
+            readLine(text, context);
+        } else {
+            context.position = pos;
+        }
+
+        return {
+            statusCode: statusCode,
+            statusText: statusText,
+            headers: headers,
+            body: readTo(text, context, "\r\n" + delimiter)
+        };
+    };
+
+    var readLine = function (text, context) {
+        /// <summary>
+        /// Returns a substring from the position defined by the context up to the next line break (CRLF).
+        /// </summary>
+        /// <param name="text" type="String" optional="false">Input string.</param>
+        /// <param name="context" optional="false">Context used for reading the input string.</param>
+        /// <returns type="String">Substring to the first ocurrence of a line break or null if none can be found. </returns>
+
+        return readTo(text, context, "\r\n");
+    };
+
+    var readTo = function (text, context, str) {
+        /// <summary>
+        /// Returns a substring from the position given by the context up to value defined by the str parameter and increments the position in the context.
+        /// </summary>
+        /// <param name="text" type="String" optional="false">Input string.</param>
+        /// <param name="context" type="Object" optional="false">Context used for reading the input string.</param>
+        /// <param name="str" type="String" optional="true">Substring to read up to.</param>
+        /// <returns type="String">Substring to the first ocurrence of str or the end of the input string if str is not specified. Null if the marker is not found.</returns>
+
+        var start = context.position || 0;
+        var end = text.length;
+        if (str) {
+            end = text.indexOf(str, start);
+            if (end === -1) {
+                return null;
+            }
+            context.position = end + str.length;
+        } else {
+            context.position = end;
+        }
+
+        return text.substring(start, end);
+    };
+
+    var writeBatch = function (data, context) {
+        /// <summary>
+        /// Serializes a batch request object to a string.
+        /// </summary>
+        /// <param name="data" optional="false">Batch request object in payload representation format</param>
+        /// <param name="context" optional="false">Context used for the serialization</param>
+        /// <returns type="String">String representing the batch request</returns>
+
+        if (!isBatch(data)) {
+            throw { message: "Data is not a batch object." };
+        }
+
+        var batchBoundary = createBoundary("batch_");
+        var batchParts = data.__batchRequests;
+        var batch = "";
+        var i, len;
+        for (i = 0, len = batchParts.length; i < len; i++) {
+            batch += writeBatchPartDelimiter(batchBoundary, false) +
+                     writeBatchPart(batchParts[i], context);
+        }
+        batch += writeBatchPartDelimiter(batchBoundary, true);
+
+        // Register the boundary with the request content type.
+        var contentTypeProperties = context.contentType.properties;
+        contentTypeProperties.boundary = batchBoundary;
+
+        return batch;
+    };
+
+    var writeBatchPartDelimiter = function (boundary, close) {
+        /// <summary>
+        /// Creates the delimiter that indicates that start or end of an individual request.
+        /// </summary>
+        /// <param name="boundary" type="String" optional="false">Boundary string used to indicate the start of the request</param>
+        /// <param name="close" type="Boolean">Flag indicating that a close delimiter string should be generated</param>
+        /// <returns type="String">Delimiter string</returns>
+
+        var result = "\r\n--" + boundary;
+        if (close) {
+            result += "--";
+        }
+
+        return result + "\r\n";
+    };
+
+    var writeBatchPart = function (part, context, nested) {
+        /// <summary>
+        /// Serializes a part of a batch request to a string. A part can be either a GET request or
+        /// a change set grouping several CUD (create, update, delete) requests.
+        /// </summary>
+        /// <param name="part" optional="false">Request or change set object in payload representation format</param>
+        /// <param name="context" optional="false">Object containing context information used for the serialization</param>
+        /// <param name="nested" type="boolean" optional="true">Flag indicating that the part is nested inside a change set</param>
+        /// <returns type="String">String representing the serialized part</returns>
+        /// <remarks>
+        /// A change set is an array of request objects and they cannot be nested inside other change sets.
+        /// </remarks>
+
+        var changeSet = part.__changeRequests;
+        var result;
+        if (isArray(changeSet)) {
+            if (nested) {
+                throw { message: "Not Supported: change set nested in other change set" };
+            }
+
+            var changeSetBoundary = createBoundary("changeset_");
+            result = "Content-Type: " + batchMediaType + "; boundary=" + changeSetBoundary + "\r\n";
+            var i, len;
+            for (i = 0, len = changeSet.length; i < len; i++) {
+                result += writeBatchPartDelimiter(changeSetBoundary, false) +
+                     writeBatchPart(changeSet[i], context, true);
+            }
+
+            result += writeBatchPartDelimiter(changeSetBoundary, true);
+        } else {
+            result = "Content-Type: application/http\r\nContent-Transfer-Encoding: binary\r\n\r\n";
+            var partContext = extend({}, context);
+            partContext.handler = handler;
+            partContext.request = part;
+            partContext.contentType = null;
+
+            prepareRequest(part, partHandler(context), partContext);
+            result += writeRequest(part);
+        }
+
+        return result;
+    };
+
+    var writeRequest = function (request) {
+        /// <summary>
+        /// Serializes a request object to a string.
+        /// </summary>
+        /// <param name="request" optional="false">Request object to serialize</param>
+        /// <returns type="String">String representing the serialized request</returns>
+
+        var result = (request.method ? request.method : "GET") + " " + request.requestUri + " HTTP/1.1\r\n";
+        for (var name in request.headers) {
+            if (request.headers[name]) {
+                result = result + name + ": " + request.headers[name] + "\r\n";
+            }
+        }
+
+        result += "\r\n";
+
+        if (request.body) {
+            result += request.body;
+        }
+
+        return result;
+    };
+
+    odata.batchHandler = handler(batchParser, batchSerializer, batchMediaType, MAX_DATA_SERVICE_VERSION);
+
+    // DATAJS INTERNAL START
+    odata.batchSerializer = batchSerializer;
+    odata.writeRequest = writeRequest;
+    // DATAJS INTERNAL END
+
+    // CONTENT END
+})(this);
\ No newline at end of file
diff --git a/JSLib/src/odata-gml.js b/JSLib/src/odata-gml.js
new file mode 100644
index 0000000..2bcf5f5
--- /dev/null
+++ b/JSLib/src/odata-gml.js
@@ -0,0 +1,831 @@
+// Copyright (c) Microsoft Open Technologies, Inc.  All rights reserved.
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation 
+// files (the "Software"), to deal  in the Software without restriction, including without limitation the rights  to use, copy,
+// modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the 
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR  IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// WARRANTIES OF MERCHANTABILITY,  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+// odata-gml.js
+
+(function (window, undefined) {
+
+    var datajs = window.datajs || {};
+    var odata = window.OData || {};
+
+    // Imports.
+
+    var contains = datajs.contains;
+    var djsassert = datajs.djsassert;
+    var http = datajs.http;
+    var isArray = datajs.isArray;
+    var xmlAppendChild = datajs.xmlAppendChild;
+    var xmlAttributeValue = datajs.xmlAttributeValue;
+    var xmlChildElements = datajs.xmlChildElements;
+    var xmlFirstChildElement = datajs.xmlFirstChildElement;
+    var xmlInnerText = datajs.xmlInnerText;
+    var xmlLocalName = datajs.xmlLocalName;
+    var xmlNamespaceURI = datajs.xmlNamespaceURI;
+    var xmlNewElement = datajs.xmlNewElement;
+    var xmlQualifiedName = datajs.xmlQualifiedName;
+    var GEOJSON_POINT = odata.GEOJSON_POINT;
+    var GEOJSON_LINESTRING = odata.GEOJSON_LINESTRING;
+    var GEOJSON_POLYGON = odata.GEOJSON_POLYGON;
+    var GEOJSON_MULTIPOINT = odata.GEOJSON_MULTIPOINT;
+    var GEOJSON_MULTILINESTRING = odata.GEOJSON_MULTILINESTRING;
+    var GEOJSON_MULTIPOLYGON = odata.GEOJSON_MULTIPOLYGON;
+    var GEOJSON_GEOMETRYCOLLECTION = odata.GEOJSON_GEOMETRYCOLLECTION;
+
+    // CONTENT START
+    var gmlOpenGis = http + "www.opengis.net";           // http://www.opengis.net
+    var gmlXmlNs = gmlOpenGis + "/gml";                 // http://www.opengis.net/gml
+    var gmlSrsPrefix = gmlOpenGis + "/def/crs/EPSG/0/"; // http://www.opengis.net/def/crs/EPSG/0/
+
+    var gmlPrefix = "gml";
+
+    var gmlCreateGeoJSONOBject = function (type, member, data) {
+        /// <summary>Creates a GeoJSON object with the specified type, member and value.</summary>
+        /// <param name="type" type="String">GeoJSON object type.</param>
+        /// <param name="member" type="String">Name for the data member in the GeoJSON object.</param>
+        /// <param name="data">Data to be contained by the GeoJSON object.</param>
+        /// <returns type="Object">GeoJSON object.</returns>
+
+        var result = { type: type };
+        result[member] = data;
+        return result;
+    };
+
+    var gmlSwapLatLong = function (coordinates) {
+        /// <summary>Swaps the longitude and latitude in the coordinates array.</summary>
+        /// <param name="coordinates" type="Array">Array of doubles descrbing a set of coordinates.</param>
+        /// <returns type="Array">Array of doubles with the latitude and longitude components swapped.</returns>
+
+        if (isArray(coordinates) && coordinates.length >= 2) {
+            var tmp = coordinates[0];
+            coordinates[0] = coordinates[1];
+            coordinates[1] = tmp;
+        }
+        return coordinates;
+    };
+
+    var gmlReadODataMultiItem = function (domElement, type, member, members, valueReader, isGeography) {
+        /// <summary>
+        ///    Reads a GML DOM element that represents a composite structure like a multi-point or a
+        ///    multi-geometry returnig its GeoJSON representation.
+        /// </summary>
+        /// <param name="domElement">GML DOM element.</param>
+        /// <param name="type" type="String">GeoJSON object type.</param>
+        /// <param name="member" type="String">Name for the child element representing a single item in the composite structure.</param>
+        /// <param name="members" type="String">Name for the child element representing a collection of items in the composite structure.</param>
+        /// <param name="valueReader" type="Function">Callback function invoked to get the coordinates of each item in the comoposite structure.</param>
+        /// <param name="isGeography" type="Boolean" Optional="True">Flag indicating if the value uses a geographic reference system or not.<param>
+        /// <remarks>
+        ///    When using a geographic reference system, the first component of all the coordinates in each <pos> element in the GML DOM tree is the Latitude and
+        ///    will be deserialized as the second component of each <pos> element in the GML DOM tree.
+        /// </remarks>
+        /// <returns type="Object">GeoJSON object.</returns>
+
+        var coordinates = gmlReadODataMultiItemValue(domElement, member, members, valueReader, isGeography);
+        return gmlCreateGeoJSONOBject(type, "coordinates", coordinates);
+    };
+
+    var gmlReadODataMultiItemValue = function (domElement, member, members, valueReader, isGeography) {
+        /// <summary>
+        ///    Reads the value of a GML DOM element that represents a composite structure like a multi-point or a
+        ///    multi-geometry returnig its items.
+        /// </summary>
+        /// <param name="domElement">GML DOM element.</param>
+        /// <param name="type" type="String">GeoJSON object type.</param>
+        /// <param name="member" type="String">Name for the child element representing a single item in the composite structure.</param>
+        /// <param name="members" type="String">Name for the child element representing a collection of items in the composite structure.</param>
+        /// <param name="valueReader" type="Function">Callback function invoked to get the transformed value of each item in the comoposite structure.</param>
+        /// <param name="isGeography" type="Boolean" Optional="True">Flag indicating if the value uses a geographic reference system or not.<param>
+        /// <remarks>
+        ///    When using a geographic reference system, the first component of all the coordinates in each <pos> element in the GML DOM tree is the Latitude and
+        ///    will be deserialized as the second component of each <pos> element in the GML DOM tree.
+        /// </remarks>
+        /// <returns type="Array">Array containing the transformed value of each item in the multi-item.</returns>
+
+        var items = [];
+
+        xmlChildElements(domElement, function (child) {
+            if (xmlNamespaceURI(child) !== gmlXmlNs) {
+                return;
+            }
+
+            var localName = xmlLocalName(child);
+
+            if (localName === member) {
+                var valueElement = xmlFirstChildElement(child, gmlXmlNs);
+                if (valueElement) {
+                    var value = valueReader(valueElement, isGeography);
+                    if (value) {
+                        items.push(value);
+                    }
+                }
+                return;
+            }
+
+            if (localName === members) {
+                xmlChildElements(child, function (valueElement) {
+                    if (xmlNamespaceURI(valueElement) !== gmlXmlNs) {
+                        return;
+                    }
+
+                    var value = valueReader(valueElement, isGeography);
+                    if (value) {
+                        items.push(value);
+                    }
+                });
+            }
+        });
+        return items;
+    };
+
+    var gmlReadODataCollection = function (domElement, isGeography) {
+        /// <summary>Reads a GML DOM element representing a multi-geometry returning its GeoJSON representation.</summary>
+        /// <param name="domElement">DOM element.</param>
+        /// <param name="isGeography" type="Boolean" Optional="True">Flag indicating if the value uses a geographic reference system or not.<param>
+        /// <remarks>
+        ///    When using a geographic reference system, the first component of all the coordinates in each <pos> element in the GML DOM tree is the Latitude and
+        ///    will be deserialized as the second component of each <pos> element in the GML DOM tree.
+        /// </remarks>
+        /// <returns type="Object">MultiGeometry object in GeoJSON format.</returns>
+
+        var geometries = gmlReadODataMultiItemValue(domElement, "geometryMember", "geometryMembers", gmlReadODataSpatialValue, isGeography);
+        return gmlCreateGeoJSONOBject(GEOJSON_GEOMETRYCOLLECTION, "geometries", geometries);
+    };
+
+    var gmlReadODataLineString = function (domElement, isGeography) {
+        /// <summary>Reads a GML DOM element representing a line string returning its GeoJSON representation.</summary>
+        /// <param name="domElement">DOM element.</param>
+        /// <param name="isGeography" type="Boolean" Optional="True">Flag indicating if the value uses a geographic reference system or not.<param>
+        /// <remarks>
+        ///    When using a geographic reference system, the first component of all the coordinates in each <pos> element in the GML DOM tree is the Latitude and
+        ///    will be deserialized as the second component of each <pos> element in the GML DOM tree.
+        /// </remarks>
+        /// <returns type="Object">LineString object in GeoJSON format.</returns>
+
+        return gmlCreateGeoJSONOBject(GEOJSON_LINESTRING, "coordinates", gmlReadODataLineValue(domElement, isGeography));
+    };
+
+    var gmlReadODataMultiLineString = function (domElement, isGeography) {
+        /// <summary>Reads a GML DOM element representing a multi-line string returning its GeoJSON representation.</summary>
+        /// <param name="domElement">DOM element.</param>
+        /// <param name="isGeography" type="Boolean" Optional="True">Flag indicating if the value uses a geographic reference system or not.<param>
+        /// <remarks>
+        ///    When using a geographic reference system, the first component of all the coordinates in each <pos> element in the GML DOM tree is the Latitude and
+        ///    will be deserialized as the second component of each <pos> element in the GML DOM tree.
+        /// </remarks>
+        /// <returns type="Object">MultiLineString object in GeoJSON format.</returns>
+
+        return gmlReadODataMultiItem(domElement, GEOJSON_MULTILINESTRING, "curveMember", "curveMembers", gmlReadODataLineValue, isGeography);
+    };
+
+    var gmlReadODataMultiPoint = function (domElement, isGeography) {
+        /// <summary>Reads a GML DOM element representing a multi-point returning its GeoJSON representation.</summary>
+        /// <param name="domElement">DOM element.</param>
+        /// <param name="isGeography" type="Boolean" Optional="True">Flag indicating if the value uses a geographic reference system or not.<param>
+        /// <remarks>
+        ///    When using a geographic reference system, the first component of all the coordinates in each <pos> element in the GML DOM tree is the Latitude and
+        ///    will be deserialized as the second component of each <pos> element in the GML DOM tree.
+        /// </remarks>
+        /// <returns type="Object">MultiPoint object in GeoJSON format.</returns>
+
+        return gmlReadODataMultiItem(domElement, GEOJSON_MULTIPOINT, "pointMember", "pointMembers", gmlReadODataPointValue, isGeography);
+    };
+
+    var gmlReadODataMultiPolygon = function (domElement, isGeography) {
+        /// <summary>Reads a GML DOM element representing a multi-polygon returning its GeoJSON representation.</summary>
+        /// <param name="domElement">DOM element.</param>
+        /// <param name="isGeography" type="Boolean" Optional="True">Flag indicating if the value uses a geographic reference system or not.<param>
+        /// <remarks>
+        ///    When using a geographic reference system, the first component of all the coordinates in each <pos> element in the GML DOM tree is the Latitude and
+        ///    will be deserialized as the second component of each <pos> element in the GML DOM tree.
+        /// </remarks>
+        /// <returns type="Object">MultiPolygon object in GeoJSON format.</returns>
+
+        return gmlReadODataMultiItem(domElement, GEOJSON_MULTIPOLYGON, "surfaceMember", "surfaceMembers", gmlReadODataPolygonValue, isGeography);
+    };
+
+    var gmlReadODataPoint = function (domElement, isGeography) {
+        /// <summary>Reads a GML DOM element representing a point returning its GeoJSON representation.</summary>
+        /// <param name="domElement">DOM element.</param>
+        /// <param name="isGeography" type="Boolean" Optional="True">Flag indicating if the value uses a geographic reference system or not.<param>
+        /// <remarks>
+        ///    When using a geographic reference system, the first component of all the coordinates in each <pos> element in the GML DOM tree is the Latitude and
+        ///    will be deserialized as the second component of each <pos> element in the GML DOM tree.
+        /// </remarks>
+        /// <returns type="Object">Point object in GeoJSON format.</returns>
+
+        return gmlCreateGeoJSONOBject(GEOJSON_POINT, "coordinates", gmlReadODataPointValue(domElement, isGeography));
+    };
+
+    var gmlReadODataPolygon = function (domElement, isGeography) {
+        /// <summary>Reads a GML DOM element representing a polygon returning its GeoJSON representation.</summary>
+        /// <param name="domElement">DOM element.</param>
+        /// <param name="isGeography" type="Boolean" Optional="True">Flag indicating if the value uses a geographic reference system or not.<param>
+        /// <remarks>
+        ///    When using a geographic reference system, the first component of all the coordinates in each <pos> element in the GML DOM tree is the Latitude and
+        ///    will be deserialized as the second component of each <pos> element in the GML DOM tree.
+        /// </remarks>
+        /// <returns type="Object">Polygon object in GeoJSON format.</returns>
+
+        return gmlCreateGeoJSONOBject(GEOJSON_POLYGON, "coordinates", gmlReadODataPolygonValue(domElement, isGeography));
+    };
+
+    var gmlReadODataLineValue = function (domElement, isGeography) {
+        /// <summary>Reads the value of a GML DOM element representing a line returning its set of coordinates.</summary>
+        /// <param name="domElement">DOM element.</param>
+        /// <param name="isGeography" type="Boolean" Optional="True">Flag indicating if the value uses a geographic reference system or not.<param>
+        /// <remarks>
+        ///    When using a geographic reference system, the first component of all the coordinates in each <pos> element in the GML DOM tree is the Latitude and
+        ///    will be deserialized as the second component of each <pos> element in the GML DOM tree.
+        /// </remarks>
+        /// <returns type="Array">Array containing an array of doubles for each coordinate of the line.</returns>
+
+        var coordinates = [];
+
+        xmlChildElements(domElement, function (child) {
+            var nsURI = xmlNamespaceURI(child);
+
+            if (nsURI !== gmlXmlNs) {
+                return;
+            }
+
+            var localName = xmlLocalName(child);
+
+            if (localName === "posList") {
+                coordinates = gmlReadODataPosListValue(child, isGeography);
+                return;
+            }
+            if (localName === "pointProperty") {
+                coordinates.push(gmlReadODataPointWrapperValue(child, isGeography));
+                return;
+            }
+            if (localName === "pos") {
+                coordinates.push(gmlReadODataPosValue(child, isGeography));
+                return;
+            }
+        });
+
+        return coordinates;
+    };
+
+    var gmlReadODataPointValue = function (domElement, isGeography) {
+        /// <summary>Reads the value of a GML DOM element representing a point returning its coordinates.</summary>
+        /// <param name="domElement">DOM element.</param>
+        /// <param name="isGeography" type="Boolean" Optional="True">Flag indicating if the value uses a geographic reference system or not.<param>
+        /// <remarks>
+        ///    When using a geographic reference system, the first component of all the coordinates in each <pos> element in the GML DOM tree is the Latitude and
+        ///    will be deserialized as the second component of each <pos> element in the GML DOM tree.
+        /// </remarks>
+        /// <returns type="Array">Array of doubles containing the point coordinates.</returns>
+
+        var pos = xmlFirstChildElement(domElement, gmlXmlNs, "pos");
+        return pos ? gmlReadODataPosValue(pos, isGeography) : [];
+    };
+
+    var gmlReadODataPointWrapperValue = function (domElement, isGeography) {
+        /// <summary>Reads the value of a GML DOM element wrapping an element representing a point returning its coordinates.</summary>
+        /// <param name="domElement">DOM element.</param>
+        /// <param name="isGeography" type="Boolean" Optional="True">Flag indicating if the value uses a geographic reference system or not.<param>
+        /// <remarks>
+        ///    When using a geographic reference system, the first component of all the coordinates in each <pos> element in the GML DOM tree is the Latitude and
+        ///    will be deserialized as the second component of each <pos> element in the GML DOM tree.
+        /// </remarks>
+        /// <returns type="Array">Array of doubles containing the point coordinates.</returns>
+
+        var point = xmlFirstChildElement(domElement, gmlXmlNs, "Point");
+        return point ? gmlReadODataPointValue(point, isGeography) : [];
+    };
+
+    var gmlReadODataPolygonValue = function (domElement, isGeography) {
+        /// <summary>Reads the value of a GML DOM element representing a polygon returning its set of coordinates.</summary>
+        /// <param name="domElement">DOM element.</param>
+        /// <param name="isGeography" type="Boolean" Optional="True">Flag indicating if the value uses a geographic reference system or not.<param>
+        /// <remarks>
+        ///    When using a geographic reference system, the first component of all the coordinates in each <pos> element in the GML DOM tree is the Latitude and
+        ///    will be deserialized as the second component of each <pos> element in the GML DOM tree.
+        /// </remarks>
+        /// <returns type="Array">Array containing an array of array of doubles for each ring of the polygon.</returns>
+
+        var coordinates = [];
+        var exteriorFound = false;
+        xmlChildElements(domElement, function (child) {
+            if (xmlNamespaceURI(child) !== gmlXmlNs) {
+                return;
+            }
+
+            // Only the exterior and the interior rings are interesting
+            var localName = xmlLocalName(child);
+            if (localName === "exterior") {
+                exteriorFound = true;
+                coordinates.unshift(gmlReadODataPolygonRingValue(child, isGeography));
+                return;
+            }
+            if (localName === "interior") {
+                coordinates.push(gmlReadODataPolygonRingValue(child, isGeography));
+                return;
+            }
+        });
+
+        if (!exteriorFound && coordinates.length > 0) {
+            // Push an empty exterior ring.
+            coordinates.unshift([[]]);
+        }
+
+        return coordinates;
+    };
+
+    var gmlReadODataPolygonRingValue = function (domElement, isGeography) {
+        /// <summary>Reads the value of a GML DOM element representing a linear ring in a GML Polygon element.</summary>
+        /// <param name="domElement">DOM element.</param>
+        /// <param name="isGeography" type="Boolean" Optional="True">Flag indicating if the value uses a geographic reference system or not.<param>
+        /// <remarks>
+        ///    When using a geographic reference system, the first component of all the coordinates in each <pos> element in the GML DOM tree is the Latitude and
+        ///    will be deserialized as the second component of each <pos> element in the GML DOM tree.
+        /// </remarks>
+        /// <returns type="Array">Array containing an array of doubles for each coordinate of the linear ring.</returns>
+
+        var value = [];
+        xmlChildElements(domElement, function (child) {
+            if (xmlNamespaceURI(child) !== gmlXmlNs || xmlLocalName(child) !== "LinearRing") {
+                return;
+            }
+            value = gmlReadODataLineValue(child, isGeography);
+        });
+        return value;
+    };
+
+    var gmlReadODataPosListValue = function (domElement, isGeography) {
+        /// <summary>Reads the value of a GML DOM element representing a list of positions retruning its set of coordinates.</summary>
+        /// <param name="domElement">DOM element.</param>
+        /// <param name="isGeography" type="Boolean" Optional="True">Flag indicating if the value uses a geographic reference system or not.<param>
+        /// <remarks>
+        ///    When using a geographic reference system, the first component of all the coordinates in each <pos> element in the GML DOM tree is the Latitude and
+        ///    will be deserialized as the second component of each <pos> element in the GML DOM tree.
+        ///
+        ///    The positions described by the list are assumed to be 2D, so 
+        ///    an exception will be thrown if the list has an odd number elements.
+        /// </remarks>
+        /// <returns type="Array">Array containing an array of doubles for each coordinate in the list.</returns>
+
+        var coordinates = gmlReadODataPosValue(domElement, false);
+        var len = coordinates.length;
+
+        if (len % 2 !== 0) {
+            throw { message: "GML posList element has an uneven number of numeric values" };
+        }
+
+        var value = [];
+        for (var i = 0; i < len; i += 2) {
+            var pos = coordinates.slice(i, i + 2);
+            value.push(isGeography ? gmlSwapLatLong(pos) : pos);
+        }
+        return value;
+    };
+
+    var gmlReadODataPosValue = function (domElement, isGeography) {
+        /// <summary>Reads the value of a GML element describing a position or a set of coordinates in an OData spatial property value.</summary>
+        /// <param name="property">DOM element for the GML element.</param>
+        /// <param name="isGeography" type="Boolean" Optional="True">Flag indicating if the value uses a geographic reference system or not.<param>
+        /// <remarks>
+        ///    When using a geographic reference system, the first component of all the coordinates in each <pos> element in the GML DOM tree is the Latitude and
+        ///    will be deserialized as the second component of each <pos> element in the GML DOM tree.
+        /// </remarks>
+        /// <returns type="Array">Array of doubles containing the coordinates.</returns>
+
+        var value = [];
+        var delims = " \t\r\n";
+        var text = xmlInnerText(domElement);
+
+        if (text) {
+            var len = text.length;
+            var start = 0;
+            var end = 0;
+
+            while (end <= len) {
+                if (delims.indexOf(text.charAt(end)) !== -1) {
+                    var coord = text.substring(start, end);
+                    if (coord) {
+                        value.push(parseFloat(coord));
+                    }
+                    start = end + 1;
+                }
+                end++;
+            }
+        }
+
+        return isGeography ? gmlSwapLatLong(value) : value;
+    };
+
+    var gmlReadODataSpatialValue = function (domElement, isGeography) {
+        /// <summary>Reads the value of a GML DOM element a spatial value in an OData XML document.</summary>
+        /// <param name="domElement">DOM element.</param>
+        /// <param name="isGeography" type="Boolean" Optional="True">Flag indicating if the value uses a geographic reference system or not.<param>
+        /// <remarks>
+        ///    When using a geographic reference system, the first component of all the coordinates in each <pos> element in the GML DOM tree is the Latitude and
+        ///    will be deserialized as the second component of each position coordinates in the resulting GeoJSON object.
+        /// </remarks>
+        /// <returns type="Array">Array containing an array of doubles for each coordinate of the polygon.</returns>
+
+        var localName = xmlLocalName(domElement);
+        var reader;
+
+        switch (localName) {
+            case "Point":
+                reader = gmlReadODataPoint;
+                break;
+            case "Polygon":
+                reader = gmlReadODataPolygon;
+                break;
+            case "LineString":
+                reader = gmlReadODataLineString;
+                break;
+            case "MultiPoint":
+                reader = gmlReadODataMultiPoint;
+                break;
+            case "MultiCurve":
+                reader = gmlReadODataMultiLineString;
+                break;
+            case "MultiSurface":
+                reader = gmlReadODataMultiPolygon;
+                break;
+            case "MultiGeometry":
+                reader = gmlReadODataCollection;
+                break;
+            default:
+                throw { message: "Unsupported element: " + localName, element: domElement };
+        }
+
+        var value = reader(domElement, isGeography);
+        // Read the CRS
+        // WCF Data Services qualifies the srsName attribute withing the GML namespace; however
+        // other end points might no do this as per the standard.
+
+        var srsName = xmlAttributeValue(domElement, "srsName", gmlXmlNs) ||
+                      xmlAttributeValue(domElement, "srsName");
+
+        if (srsName) {
+            if (srsName.indexOf(gmlSrsPrefix) !== 0) {
+                throw { message: "Unsupported srs name: " + srsName, element: domElement };
+            }
+
+            var crsId = srsName.substring(gmlSrsPrefix.length);
+            if (crsId) {
+                value.crs = {
+                    type: "name",
+                    properties: {
+                        name: "EPSG:" + crsId
+                    }
+                };
+            }
+        }
+        return value;
+    };
+
+    var gmlNewODataSpatialValue = function (dom, value, type, isGeography) {
+        /// <summary>Creates a new GML DOM element for the value of an OData spatial property or GeoJSON object.</summary>
+        /// <param name="dom">DOM document used for creating the new DOM Element.</param>
+        /// <param name="value" type="Object">Spatial property value in GeoJSON format.</param>
+        /// <param name="type" type="String">String indicating the GeoJSON type of the value to serialize.</param>
+        /// <param name="isGeography" type="Boolean" Optional="True">Flag indicating if the value uses a geographic reference system or not.<param>
+        /// <remarks>
+        ///    When using a geographic reference system, the first component of all the coordinates in the GeoJSON value is the Longitude and
+        ///    will be serialized as the second component of each <pos> element in the GML DOM tree.
+        /// </remarks>
+        /// <returns>New DOM element in the GML namespace for the spatial value. </returns>
+
+        var gmlWriter;
+
+        switch (type) {
+            case GEOJSON_POINT:
+                gmlWriter = gmlNewODataPoint;
+                break;
+            case GEOJSON_LINESTRING:
+                gmlWriter = gmlNewODataLineString;
+                break;
+            case GEOJSON_POLYGON:
+                gmlWriter = gmlNewODataPolygon;
+                break;
+            case GEOJSON_MULTIPOINT:
+                gmlWriter = gmlNewODataMultiPoint;
+                break;
+            case GEOJSON_MULTILINESTRING:
+                gmlWriter = gmlNewODataMultiLineString;
+                break;
+            case GEOJSON_MULTIPOLYGON:
+                gmlWriter = gmlNewODataMultiPolygon;
+                break;
+            case GEOJSON_GEOMETRYCOLLECTION:
+                gmlWriter = gmlNewODataGeometryCollection;
+                break;
+            default:
+                djsassert(false, "gmlNewODataSpatialValue - Unknown GeoJSON type <" + type + ">!!");
+                return null;
+        }
+
+        var gml = gmlWriter(dom, value, isGeography);
+
+        // Set the srsName attribute if applicable.
+        var crs = value.crs;
+        if (crs) {
+            if (crs.type === "name") {
+                var properties = crs.properties;
+                var name = properties && properties.name;
+                if (name && name.indexOf("ESPG:") === 0 && name.length > 5) {
+                    var crsId = name.substring(5);
+                    var srsName = xmlNewAttribute(dom, null, "srsName", gmlPrefix + crsId);
+                    xmlAppendChild(gml, srsName);
+                }
+            }
+        }
+
+        return gml;
+    };
+
+    var gmlNewODataElement = function (dom, name, children) {
+        /// <summary>Creates a new DOM element in the GML namespace.</summary>
+        /// <param name="dom">DOM document used for creating the new DOM Element.</param>
+        /// <param name="name" type="String">Local name of the GML element to create.</param>
+        /// <param name="children" type="Array">Array containing DOM nodes or string values that will be added as children of the new DOM element.</param>
+        /// <returns>New DOM element in the GML namespace.</returns>
+        /// <remarks>
+        ///    If a value in the children collection is a string, then a new DOM text node is going to be created
+        ///    for it and then appended as a child of the new DOM Element.
+        /// </remarks>
+
+        return xmlNewElement(dom, gmlXmlNs, xmlQualifiedName(gmlPrefix, name), children);
+    };
+
+    var gmlNewODataPosElement = function (dom, coordinates, isGeography) {
+        /// <summary>Creates a new GML pos DOM element.</summary>
+        /// <param name="dom">DOM document used for creating the new DOM Element.</param>
+        /// <param name="coordinates" type="Array">Array of doubles describing the coordinates of the pos element.</param>
+        /// <param name="isGeography" type="Boolean">Flag indicating if the coordinates use a geographic reference system or not.<param>
+        /// <remarks>
+        ///    When using a geographic reference system, the first coordinate is the Longitude and
+        ///    will be serialized as the second component of the <pos> element in the GML DOM tree.
+        /// </remarks>
+        /// <returns>New pos DOM element in the GML namespace.</returns>
+
+        var posValue = isArray(coordinates) ? coordinates : [];
+
+        // If using a geographic reference system, then the first coordinate is the longitude and it has to
+        // swapped with the latitude.
+        posValue = isGeography ? gmlSwapLatLong(posValue) : posValue;
+
+        return gmlNewODataElement(dom, "pos", posValue.join(" "));
+    };
+
+    var gmlNewODataLineElement = function (dom, name, coordinates, isGeography) {
+        /// <summary>Creates a new GML DOM element representing a line.</summary>
+        /// <param name="dom">DOM document used for creating the new DOM Element.</param>
+        /// <param name="name" type="String">Name of the element to create.</param>
+        /// <param name="coordinates" type="Array">Array of array of doubles describing the coordinates of the line element.</param>
+        /// <param name="isGeography" type="Boolean">Flag indicating if the coordinates use a geographic reference system or not.<param>
+        /// <remarks>
+        ///    When using a geographic reference system, the first component of all the coordinates is the Longitude and
+        ///    will be serialized as the second component of each <pos> element in the GML DOM tree.
+        /// </remarks>
+        /// <returns>New DOM element in the GML namespace.</returns>
+
+        var element = gmlNewODataElement(dom, name);
+        if (isArray(coordinates)) {
+            var i, len;
+            for (i = 0, len = coordinates.length; i < len; i++) {
+                xmlAppendChild(element, gmlNewODataPosElement(dom, coordinates[i], isGeography));
+            }
+
+            if (len === 0) {
+                xmlAppendChild(element, gmlNewODataElement(dom, "posList"));
+            }
+        }
+        return element;
+    };
+
+    var gmlNewODataPointElement = function (dom, coordinates, isGeography) {
+        /// <summary>Creates a new GML Point DOM element.</summary>
+        /// <param name="dom">DOM document used for creating the new DOM Element.</param>
+        /// <param name="value" type="Object">GeoJSON Point object.</param>
+        /// <param name="isGeography" type="Boolean">Flag indicating if the value uses a geographic reference system or not.<param>
+        /// <remarks>
+        ///    When using a geographic reference system, the first component of all the coordinates in the GeoJSON value is the Longitude and
+        ///    will be serialized as the second component of each <pos> element in the GML DOM tree.
+        /// </remarks>
+        /// <returns>New DOM element in the GML namespace for the GeoJSON Point.</returns>
+
+        return gmlNewODataElement(dom, "Point", gmlNewODataPosElement(dom, coordinates, isGeography));
+    };
+
+    var gmlNewODataLineStringElement = function (dom, coordinates, isGeography) {
+        /// <summary>Creates a new GML LineString DOM element.</summary>
+        /// <param name="dom">DOM document used for creating the new DOM Element.</param>
+        /// <param name="coordinates" type="Array">Array of array of doubles describing the coordinates of the line element.</param>
+        /// <param name="isGeography" type="Boolean">Flag indicating if the value uses a geographic reference system or not.<param>
+        /// <remarks>
+        ///    When using a geographic reference system, the first component of all the coordinates in the GeoJSON value is the Longitude and
+        ///    will be serialized as the second component of each <pos> element in the GML DOM tree.
+        /// </remarks>
+        /// <returns>New DOM element in the GML namespace for the GeoJSON LineString.</returns>
+
+        return gmlNewODataLineElement(dom, "LineString", coordinates, isGeography);
+    };
+
+    var gmlNewODataPolygonRingElement = function (dom, name, coordinates, isGeography) {
+        /// <summary>Creates a new GML DOM element representing a polygon ring.</summary>
+        /// <param name="dom">DOM document used for creating the new DOM Element.</param>
+        /// <param name="name" type="String">Name of the element to create.</param>
+        /// <param name="coordinates" type="Array">Array of array of doubles describing the coordinates of the polygon ring.</param>
+        /// <param name="isGeography" type="Boolean">Flag indicating if the coordinates use a geographic reference system or not.<param>
+        /// <remarks>
+        ///    When using a geographic reference system, the first component of all the coordinates is the Longitude and
+        ///    will be serialized as the second component of each <pos> element in the GML DOM tree.
+        /// </remarks>
+        /// <returns>New DOM element in the GML namespace.</returns>
+
+        var ringElement = gmlNewODataElement(dom, name);
+        if (isArray(coordinates) && coordinates.length > 0) {
+            var linearRing = gmlNewODataLineElement(dom, "LinearRing", coordinates, isGeography);
+            xmlAppendChild(ringElement, linearRing);
+        }
+        return ringElement;
+    };
+
+    var gmlNewODataPolygonElement = function (dom, coordinates, isGeography) {
+        /// <summary>Creates a new GML Polygon DOM element for a GeoJSON Polygon object.</summary>
+        /// <param name="dom">DOM document used for creating the new DOM Element.</param>
+        /// <param name="coordinates" type="Array">Array of array of array of doubles describing the coordinates of the polygon.</param>
+        /// <param name="isGeography" type="Boolean">Flag indicating if the value uses a geographic reference system or not.<param>
+        /// <remarks>
+        ///    When using a geographic reference system, the first component of all the coordinates in the GeoJSON value is the Longitude and
+        ///    will be serialized as the second component of each <pos> element in the GML DOM tree.
+        /// </remarks>
+        /// <returns>New DOM element in the GML namespace.</returns>
+
+        var len = coordinates && coordinates.length;
+        var element = gmlNewODataElement(dom, "Polygon");
+
+        if (isArray(coordinates) && len > 0) {
+            xmlAppendChild(element, gmlNewODataPolygonRingElement(dom, "exterior", coordinates[0], isGeography));
+
+            var i;
+            for (i = 1; i < len; i++) {
+                xmlAppendChild(element, gmlNewODataPolygonRingElement(dom, "interior", coordinates[i], isGeography));
+            }
+        }
+        return element;
+    };
+
+    var gmlNewODataPoint = function (dom, value, isGeography) {
+        /// <summary>Creates a new GML Point DOM element for a GeoJSON Point object.</summary>
+        /// <param name="dom">DOM document used for creating the new DOM Element.</param>
+        /// <param name="value" type="Object">GeoJSON Point object.</param>
+        /// <param name="isGeography" type="Boolean">Flag indicating if the value uses a geographic reference system or not.<param>
+        /// <remarks>
+        ///    When using a geographic reference system, the first component of all the coordinates in the GeoJSON value is the Longitude and
+        ///    will be serialized as the second component of each <pos> element in the GML DOM tree.
+        /// </remarks>
+        /// <returns>New DOM element in the GML namespace for the GeoJSON Point.</returns>
+
+        return gmlNewODataPointElement(dom, value.coordinates, isGeography);
+    };
+
+    var gmlNewODataLineString = function (dom, value, isGeography) {
+        /// <summary>Creates a new GML LineString DOM element for a GeoJSON LineString object.</summary>
+        /// <param name="dom">DOM document used for creating the new DOM Element.</param>
+        /// <param name="value" type="Object">GeoJSON LineString object.</param>
+        /// <param name="isGeography" type="Boolean">Flag indicating if the value uses a geographic reference system or not.<param>
+        /// <remarks>
+        ///    When using a geographic reference system, the first component of all the coordinates in the GeoJSON value is the Longitude and
+        ///    will be serialized as the second component of each <pos> element in the GML DOM tree.
+        /// </remarks>
+        /// <returns>New DOM element in the GML namespace for the GeoJSON LineString.</returns>
+
+        return gmlNewODataLineStringElement(dom, value.coordinates, isGeography);
+    };
+
+    var gmlNewODataPolygon = function (dom, value, isGeography) {
+        /// <summary>Creates a new GML Polygon DOM element for a GeoJSON Polygon object.</summary>
+        /// <param name="dom">DOM document used for creating the new DOM Element.</param>
+        /// <param name="value" type="Object">GeoJSON Polygon object.</param>
+        /// <param name="isGeography" type="Boolean">Flag indicating if the value uses a geographic reference system or not.<param>
+        /// <remarks>
+        ///    When using a geographic reference system, the first component of all the coordinates in the GeoJSON value is the Longitude and
+        ///    will be serialized as the second component of each <pos> element in the GML DOM tree.
+        /// </remarks>
+        /// <returns>New DOM element in the GML namespace for the GeoJSON Polygon.</returns>
+
+        return gmlNewODataPolygonElement(dom, value.coordinates, isGeography);
+    };
+
+    var gmlNewODataMultiItem = function (dom, name, members, items, itemWriter, isGeography) {
+        /// <summary>Creates a new GML DOM element for a composite structure like a multi-point or a multi-geometry.</summary>
+        /// <param name="dom">DOM document used for creating the new DOM Element.</param>
+        /// <param name="name" type="String">Name of the element to create.</param>
+        /// <param name="items" type="Array">Array of items in the composite structure.</param>
+        /// <param name="isGeography" type="Boolean">Flag indicating if the multi-item uses a geographic reference system or not.<param>
+        /// <remarks>
+        ///    When using a geographic reference system, the first component of all the coordinates in each of the items is the Longitude and
+        ///    will be serialized as the second component of each <pos> element in the GML DOM tree.
+        /// </remarks>
+        /// <returns>New DOM element in the GML namespace.</returns>
+
+        var len = items && items.length;
+        var element = gmlNewODataElement(dom, name);
+
+        if (isArray(items) && len > 0) {
+            var membersElement = gmlNewODataElement(dom, members);
+            var i;
+            for (i = 0; i < len; i++) {
+                xmlAppendChild(membersElement, itemWriter(dom, items[i], isGeography));
+            }
+            xmlAppendChild(element, membersElement);
+        }
+        return element;
+    };
+
+    var gmlNewODataMultiPoint = function (dom, value, isGeography) {
+        /// <summary>Creates a new GML MultiPoint DOM element for a GeoJSON MultiPoint object.</summary>
+        /// <param name="dom">DOM document used for creating the new DOM Element.</param>
+        /// <param name="value" type="Object">GeoJSON MultiPoint object.</param>
+        /// <param name="isGeography" type="Boolean">Flag indicating if the value uses a geographic reference system or not.<param>
+        /// <remarks>
+        ///    When using a geographic reference system, the first component of all the coordinates in the GeoJSON value is the Longitude and
+        ///    will be serialized as the second component of each <pos> element in the GML DOM tree.
+        /// </remarks>
+        /// <returns>New DOM element in the GML namespace for the GeoJSON MultiPoint.</returns>
+
+        return gmlNewODataMultiItem(dom, "MultiPoint", "pointMembers", value.coordinates, gmlNewODataPointElement, isGeography);
+    };
+
+    var gmlNewODataMultiLineString = function (dom, value, isGeography) {
+        /// <summary>Creates a new GML MultiCurve DOM element for a GeoJSON MultiLineString object.</summary>
+        /// <param name="dom">DOM document used for creating the new DOM Element.</param>
+        /// <param name="value" type="Object">GeoJSON MultiLineString object.</param>
+        /// <param name="isGeography" type="Boolean">Flag indicating if the value uses a geographic reference system or not.<param>
+        /// <remarks>
+        ///    When using a geographic reference system, the first component of all the coordinates in the GeoJSON value is the Longitude and
+        ///    will be serialized as the second component of each <pos> element in the GML DOM tree.
+        /// </remarks>
+        /// <returns>New DOM element in the GML namespace for the GeoJSON MultiLineString.</returns>
+
+        return gmlNewODataMultiItem(dom, "MultiCurve", "curveMembers", value.coordinates, gmlNewODataLineStringElement, isGeography);
+    };
+
+    var gmlNewODataMultiPolygon = function (dom, value, isGeography) {
+        /// <summary>Creates a new GML MultiSurface DOM element for a GeoJSON MultiPolygon object.</summary>
+        /// <param name="dom">DOM document used for creating the new DOM Element.</param>
+        /// <param name="value" type="Object">GeoJSON MultiPolygon object.</param>
+        /// <param name="isGeography" type="Boolean">Flag indicating if the value uses a geographic reference system or not.<param>
+        /// <remarks>
+        ///    When using a geographic reference system, the first component of all the coordinates in the GeoJSON value is the Longitude and
+        ///    will be serialized as the second component of each <pos> element in the GML DOM tree.
+        /// </remarks>
+        /// <returns>New DOM element in the GML namespace for the GeoJSON MultiPolygon.</returns>
+
+        return gmlNewODataMultiItem(dom, "MultiSurface", "surfaceMembers", value.coordinates, gmlNewODataPolygonElement, isGeography);
+    };
+
+    var gmlNewODataGeometryCollectionItem = function (dom, value, isGeography) {
+        /// <summary>Creates a new GML element for an item in a geometry collection object.</summary>
+        /// <param name="dom">DOM document used for creating the new DOM Element.</param>
+        /// <param name="item" type="Object">GeoJSON object in the geometry collection.</param>
+        /// <param name="isGeography" type="Boolean">Flag indicating if the value uses a geographic reference system or not.<param>
+        /// <remarks>
+        ///    When using a geographic reference system, the first component of all the coordinates in the GeoJSON value is the Longitude and
+        ///    will be serialized as the second component of each <pos> element in the GML DOM tree.
+        /// </remarks>
+        /// <returns>New DOM element in the GML namespace.</returns>
+
+        return gmlNewODataSpatialValue(dom, value, value.type, isGeography);
+    };
+
+    var gmlNewODataGeometryCollection = function (dom, value, isGeography) {
+        /// <summary>Creates a new GML MultiGeometry DOM element for a GeoJSON GeometryCollection object.</summary>
+        /// <param name="dom">DOM document used for creating the new DOM Element.</param>
+        /// <param name="value" type="Object">GeoJSON GeometryCollection object.</param>
+        /// <param name="isGeography" type="Boolean">Flag indicating if the value uses a geographic reference system or not.<param>
+        /// <remarks>
+        ///    When using a geographic reference system, the first component of all the coordinates in the GeoJSON value is the Longitude and
+        ///    will be serialized as the second component of each <pos> element in the GML DOM tree.
+        /// </remarks>
+        /// <returns>New DOM element in the GML namespace for the GeoJSON GeometryCollection.</returns>
+
+        return gmlNewODataMultiItem(dom, "MultiGeometry", "geometryMembers", value.geometries, gmlNewODataGeometryCollectionItem, isGeography);
+    };
+
+    // DATAJS INTERNAL START
+    odata.gmlNewODataSpatialValue = gmlNewODataSpatialValue;
+    odata.gmlReadODataSpatialValue = gmlReadODataSpatialValue;
+    odata.gmlXmlNs = gmlXmlNs;
+    // DATAJS INTERNAL END
+
+    // CONTENT END
+})(this);
\ No newline at end of file
diff --git a/JSLib/src/odata-handler.js b/JSLib/src/odata-handler.js
new file mode 100644
index 0000000..5f83429
--- /dev/null
+++ b/JSLib/src/odata-handler.js
@@ -0,0 +1,276 @@
+// Copyright (c) Microsoft Open Technologies, Inc.  All rights reserved.
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal  in the Software without restriction, including without limitation the rights  to use, copy,
+// modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR  IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// WARRANTIES OF MERCHANTABILITY,  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+// odata-handler.js
+
+(function (window, undefined) {
+
+    var datajs = window.datajs || {};
+    var odata = window.OData || {};
+
+    // Imports.
+    var assigned = datajs.assigned;
+    var extend = datajs.extend;
+    var trimString = datajs.trimString;
+
+    var maxVersion = odata.maxVersion;
+
+    // CONTENT START
+
+    var MAX_DATA_SERVICE_VERSION = "3.0";
+
+    var contentType = function (str) {
+        /// <summary>Parses a string into an object with media type and properties.</summary>
+        /// <param name="str" type="String">String with media type to parse.</param>
+        /// <returns>null if the string is empty; an object with 'mediaType' and a 'properties' dictionary otherwise.</returns>
+
+        if (!str) {
+            return null;
+        }
+
+        var contentTypeParts = str.split(";");
+        var properties = {};
+
+        var i, len;
+        for (i = 1, len = contentTypeParts.length; i < len; i++) {
+            var contentTypeParams = contentTypeParts[i].split("=");
+            properties[trimString(contentTypeParams[0])] = contentTypeParams[1];
+        }
+
+        return { mediaType: trimString(contentTypeParts[0]), properties: properties };
+    };
+
+    var contentTypeToString = function (contentType) {
+        /// <summary>Serializes an object with media type and properties dictionary into a string.</summary>
+        /// <param name="contentType">Object with media type and properties dictionary to serialize.</param>
+        /// <returns>String representation of the media type object; undefined if contentType is null or undefined.</returns>
+
+        if (!contentType) {
+            return undefined;
+        }
+
+        var result = contentType.mediaType;
+        var property;
+        for (property in contentType.properties) {
+            result += ";" + property + "=" + contentType.properties[property];
+        }
+        return result;
+    };
+
+    var createReadWriteContext = function (contentType, dataServiceVersion, context, handler) {
+        /// <summary>Creates an object that is going to be used as the context for the handler's parser and serializer.</summary>
+        /// <param name="contentType">Object with media type and properties dictionary.</param>
+        /// <param name="dataServiceVersion" type="String">String indicating the version of the protocol to use.</param>
+        /// <param name="context">Operation context.</param>
+        /// <param name="handler">Handler object that is processing a resquest or response.</param>
+        /// <returns>Context object.</returns>
+
+        var rwContext = {};
+        extend(rwContext, context);
+        extend(rwContext, {
+            contentType: contentType,
+            dataServiceVersion: dataServiceVersion,
+            handler: handler
+        });
+
+        return rwContext;
+    };
+
+    var fixRequestHeader = function (request, name, value) {
+        /// <summary>Sets a request header's value. If the header has already a value other than undefined, null or empty string, then this method does nothing.</summary>
+        /// <param name="request">Request object on which the header will be set.</param>
+        /// <param name="name" type="String">Header name.</param>
+        /// <param name="value" type="String">Header value.</param>
+        if (!request) {
+            return;
+        }
+
+        var headers = request.headers;
+        if (!headers[name]) {
+            headers[name] = value;
+        }
+    };
+
+    var fixDataServiceVersionHeader = function (request, version) {
+        /// <summary>Sets the DataServiceVersion header of the request if its value is not yet defined or of a lower version.</summary>
+        /// <param name="request">Request object on which the header will be set.</param>
+        /// <param name="version" type="String">Version value.</param>
+        /// <remarks>
+        /// If the request has already a version value higher than the one supplied the this function does nothing.
+        /// </remarks>
+
+        if (request) {
+            var headers = request.headers;
+            var dsv = headers["DataServiceVersion"];
+            headers["DataServiceVersion"] = dsv ? maxVersion(dsv, version) : version;
+        }
+    };
+
+    var getRequestOrResponseHeader = function (requestOrResponse, name) {
+        /// <summary>Gets the value of a request or response header.</summary>
+        /// <param name="requestOrResponse">Object representing a request or a response.</param>
+        /// <param name="name" type="String">Name of the header to retrieve.</param>
+        /// <returns type="String">String value of the header; undefined if the header cannot be found.</returns>
+
+        var headers = requestOrResponse.headers;
+        return (headers && headers[name]) || undefined;
+    };
+
+    var getContentType = function (requestOrResponse) {
+        /// <summary>Gets the value of the Content-Type header from a request or response.</summary>
+        /// <param name="requestOrResponse">Object representing a request or a response.</param>
+        /// <returns type="Object">Object with 'mediaType' and a 'properties' dictionary; null in case that the header is not found or doesn't have a value.</returns>
+
+        return contentType(getRequestOrResponseHeader(requestOrResponse, "Content-Type"));
+    };
+
+    var versionRE = /^\s?(\d+\.\d+);?.*$/;
+    var getDataServiceVersion = function (requestOrResponse) {
+        /// <summary>Gets the value of the DataServiceVersion header from a request or response.</summary>
+        /// <param name="requestOrResponse">Object representing a request or a response.</param>
+        /// <returns type="String">Data service version; undefined if the header cannot be found.</returns>
+
+        var value = getRequestOrResponseHeader(requestOrResponse, "DataServiceVersion");
+        if (value) {
+            var matches = versionRE.exec(value);
+            if (matches && matches.length) {
+                return matches[1];
+            }
+        }
+
+        // Fall through and return undefined.
+    };
+
+    var handlerAccepts = function (handler, cType) {
+        /// <summary>Checks that a handler can process a particular mime type.</summary>
+        /// <param name="handler">Handler object that is processing a resquest or response.</param>
+        /// <param name="cType">Object with 'mediaType' and a 'properties' dictionary.</param>
+        /// <returns type="Boolean">True if the handler can process the mime type; false otherwise.</returns>
+
+        // The following check isn't as strict because if cType.mediaType = application/; it will match an accept value of "application/xml";
+        // however in practice we don't not expect to see such "suffixed" mimeTypes for the handlers.
+        return handler.accept.indexOf(cType.mediaType) >= 0;
+    };
+
+    var handlerRead = function (handler, parseCallback, response, context) {
+        /// <summary>Invokes the parser associated with a handler for reading the payload of a HTTP response.</summary>
+        /// <param name="handler">Handler object that is processing the response.</param>
+        /// <param name="parseCallback" type="Function">Parser function that will process the response payload.</param>
+        /// <param name="response">HTTP response whose payload is going to be processed.</param>
+        /// <param name="context">Object used as the context for processing the response.</param>
+        /// <returns type="Boolean">True if the handler processed the response payload and the response.data property was set; false otherwise.</returns>
+
+        if (!response || !response.headers) {
+            return false;
+        }
+
+        var cType = getContentType(response);
+        var version = getDataServiceVersion(response) || "";
+        var body = response.body;
+
+        if (!assigned(body)) {
+            return false;
+        }
+
+        if (handlerAccepts(handler, cType)) {
+            var readContext = createReadWriteContext(cType, version, context, handler);
+            readContext.response = response;
+            response.data = parseCallback(handler, body, readContext);
+            return response.data !== undefined;
+        }
+
+        return false;
+    };
+
+    var handlerWrite = function (handler, serializeCallback, request, context) {
+        /// <summary>Invokes the serializer associated with a handler for generating the payload of a HTTP request.</summary>
+        /// <param name="handler">Handler object that is processing the request.</param>
+        /// <param name="serializeCallback" type="Function">Serializer function that will generate the request payload.</param>
+        /// <param name="response">HTTP request whose payload is going to be generated.</param>
+        /// <param name="context">Object used as the context for serializing the request.</param>
+        /// <returns type="Boolean">True if the handler serialized the request payload and the request.body property was set; false otherwise.</returns>
+        if (!request || !request.headers) {
+            return false;
+        }
+
+        var cType = getContentType(request);
+        var version = getDataServiceVersion(request);
+
+        if (!cType || handlerAccepts(handler, cType)) {
+            var writeContext = createReadWriteContext(cType, version, context, handler);
+            writeContext.request = request;
+
+            request.body = serializeCallback(handler, request.data, writeContext);
+
+            if (request.body !== undefined) {
+                fixDataServiceVersionHeader(request, writeContext.dataServiceVersion || "1.0");
+
+                fixRequestHeader(request, "Content-Type", contentTypeToString(writeContext.contentType));
+                fixRequestHeader(request, "MaxDataServiceVersion", handler.maxDataServiceVersion);
+                return true;
+            }
+        }
+
+        return false;
+    };
+
+    var handler = function (parseCallback, serializeCallback, accept, maxDataServiceVersion) {
+        /// <summary>Creates a handler object for processing HTTP requests and responses.</summary>
+        /// <param name="parseCallback" type="Function">Parser function that will process the response payload.</param>
+        /// <param name="serializeCallback" type="Function">Serializer function that will generate the request payload.</param>
+        /// <param name="accept" type="String">String containing a comma separated list of the mime types that this handler can work with.</param>
+        /// <param name="maxDataServiceVersion" type="String">String indicating the highest version of the protocol that this handler can work with.</param>
+        /// <returns type="Object">Handler object.</returns>
+
+        return {
+            accept: accept,
+            maxDataServiceVersion: maxDataServiceVersion,
+
+            read: function (response, context) {
+                return handlerRead(this, parseCallback, response, context);
+            },
+
+            write: function (request, context) {
+                return handlerWrite(this, serializeCallback, request, context);
+            }
+        };
+    };
+
+    var textParse = function (handler, body /*, context */) {
+        return body;
+    };
+
+    var textSerialize = function (handler, data /*, context */) {
+        if (assigned(data)) {
+            return data.toString();
+        } else {
+            return undefined;
+        }
+    };
+
+    odata.textHandler = handler(textParse, textSerialize, "text/plain", MAX_DATA_SERVICE_VERSION);
+
+    // DATAJS INTERNAL START
+    odata.contentType = contentType;
+    odata.contentTypeToString = contentTypeToString;
+    odata.handler = handler;
+    odata.createReadWriteContext = createReadWriteContext;
+    odata.fixRequestHeader = fixRequestHeader;
+    odata.getRequestOrResponseHeader = getRequestOrResponseHeader;
+    odata.getContentType = getContentType;
+    odata.getDataServiceVersion = getDataServiceVersion;
+    odata.MAX_DATA_SERVICE_VERSION = MAX_DATA_SERVICE_VERSION;
+    // DATAJS INTERNAL END
+
+    // CONTENT END
+})(this);
\ No newline at end of file
diff --git a/JSLib/src/odata-json-light.js b/JSLib/src/odata-json-light.js
new file mode 100644
index 0000000..9a10601
--- /dev/null
+++ b/JSLib/src/odata-json-light.js
@@ -0,0 +1,1391 @@
+// Copyright (c) Microsoft Open Technologies, Inc.  All rights reserved.
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation 
+// files (the "Software"), to deal  in the Software without restriction, including without limitation the rights  to use, copy,
+// modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the 
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR  IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// WARRANTIES OF MERCHANTABILITY,  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+// odata-json-light.js
+
+(function (window, undefined) {
+
+    var datajs = window.datajs || {};
+    var odata = window.OData || {};
+
+    // Imports
+
+    var assigned = datajs.assigned;
+    var djsassert = datajs.djsassert;
+    var extend = datajs.extend;
+    var getURIInfo = datajs.getURIInfo;
+    var isArray = datajs.isArray;
+    var isDate = datajs.isDate;
+    var normalizeURI = datajs.normalizeURI;
+    var renameProperty = datajs.renameProperty;
+    var undefinedDefault = datajs.undefinedDefault;
+    var convertByteArrayToHexString = datajs.convertByteArrayToHexString;
+
+    var dataItemTypeName = odata.dataItemTypeName;
+    var EDM_DATETIME = odata.EDM_DATETIME;
+    var EDM_DATETIMEOFFSET = odata.EDM_DATETIMEOFFSET;
+    var EDM_TIME = odata.EDM_TIME;
+    var getCollectionType = odata.getCollectionType;
+    var isCollection = odata.isCollection;
+    var isCollectionType = odata.isCollectionType;
+    var isComplex = odata.isComplex;
+    var isDeferred = odata.isDeferred;
+    var isFeed = odata.isFeed;
+    var isEntry = odata.isEntry;
+    var isGeographyEdmType = odata.isGeographyEdmType;
+    var isGeometryEdmType = odata.isGeometryEdmType;
+    var isPrimitiveEdmType = odata.isPrimitiveEdmType;
+    var isPrimitive = odata.isPrimitive;
+    var lookupComplexType = odata.lookupComplexType;
+    var lookupDefaultEntityContainer = odata.lookupDefaultEntityContainer;
+    var lookupEntityContainer = odata.lookupEntityContainer;
+    var lookupEntitySet = odata.lookupEntitySet;
+    var lookupEntityType = odata.lookupEntityType;
+    var lookupFunctionImport = odata.lookupFunctionImport;
+    var lookupNavigationPropertyType = odata.lookupNavigationPropertyType;
+    var getEntitySetInfo = odata.getEntitySetInfo;
+    var lookupNavigationPropertyEntitySet = odata.lookupNavigationPropertyEntitySet;
+    var lookupProperty = odata.lookupProperty;
+    var parseBool = odata.parseBool;
+    var parseDateTime = odata.parseDateTime;
+    var parseDateTimeOffset = odata.parseDateTimeOffset;
+    var parseDuration = odata.parseDuration;
+
+    // CONTENT START
+
+    var PAYLOADTYPE_OBJECT = "o";
+    var PAYLOADTYPE_FEED = "f";
+    var PAYLOADTYPE_PRIMITIVE = "p";
+    var PAYLOADTYPE_COLLECTION = "c";
+    var PAYLOADTYPE_SVCDOC = "s";
+    var PAYLOADTYPE_LINKS = "l";
+
+    var odataNs = "odata";
+    var odataAnnotationPrefix = odataNs + ".";
+
+    var bindAnnotation = "@" + odataAnnotationPrefix + "bind";
+    var metadataAnnotation = odataAnnotationPrefix + "metadata";
+    var navUrlAnnotation = odataAnnotationPrefix + "navigationLinkUrl";
+    var typeAnnotation = odataAnnotationPrefix + "type";
+
+    var jsonLightNameMap = {
+        readLink: "self",
+        editLink: "edit",
+        nextLink: "__next",
+        mediaReadLink: "media_src",
+        mediaEditLink: "edit_media",
+        mediaContentType: "content_type",
+        mediaETag: "media_etag",
+        count: "__count",
+        media_src: "mediaReadLink",
+        edit_media: "mediaEditLink",
+        content_type: "mediaContentType",
+        media_etag: "mediaETag",
+        url: "uri"
+    };
+
+    var jsonLightAnnotations = {
+        metadata: "odata.metadata",
+        count: "odata.count",
+        next: "odata.nextLink",
+        id: "odata.id",
+        etag: "odata.etag",
+        read: "odata.readLink",
+        edit: "odata.editLink",
+        mediaRead: "odata.mediaReadLink",
+        mediaEdit: "odata.mediaEditLink",
+        mediaEtag: "odata.mediaETag",
+        mediaContentType: "odata.mediaContentType",
+        actions: "odata.actions",
+        functions: "odata.functions",
+        navigationUrl: "odata.navigationLinkUrl",
+        associationUrl: "odata.associationLinkUrl",
+        type: "odata.type"
+    };
+
+    var jsonLightAnnotationInfo = function (annotation) {
+        /// <summary>Gets the name and target of an annotation in a JSON light payload.</summary>
+        /// <param name="annotation" type="String">JSON light payload annotation.</param>
+        /// <returns type="Object">Object containing the annotation name and the target property name.</param>
+
+        if (annotation.indexOf(".") > 0) {
+            var targetEnd = annotation.indexOf("@");
+            var target = targetEnd > -1 ? annotation.substring(0, targetEnd) : null;
+            var name = annotation.substring(targetEnd + 1);
+
+            return {
+                target: target,
+                name: name,
+                isOData: name.indexOf(odataAnnotationPrefix) === 0
+            };
+        }
+        return null;
+    };
+
+    var jsonLightDataItemType = function (name, value, container, dataItemModel, model) {
+        /// <summary>Gets the type name of a JSON light data item that belongs to a feed, an entry, a complex type property, or a collection property.</summary>
+        /// <param name="name" type="String">Name of the data item for which the type name is going to be retrieved.</param>
+        /// <param name="value">Value of the data item.</param>
+        /// <param name="container" type="Object">JSON light object that owns the data item.</param>
+        /// <param name="dataItemModel" type="Object" optional="true">Object describing the data item in an OData conceptual schema.</param>
+        /// <param name="model" type="Object" optional="true">Object describing an OData conceptual schema.</param>
+        /// <remarks>
+        ///    This function will first try to get the type name from the data item's value itself if it is a JSON light object; otherwise
+        ///    it will try to get it from the odata.type annotation applied to the data item in the container. Then, it will fallback to the data item model.
+        ///    If all attempts fail, it will return null.
+        /// </remarks>
+        /// <returns type="String">Data item type name; null if the type name cannot be found.</returns>
+
+        return (isComplex(value) && value[typeAnnotation]) ||
+            (container && container[name + "@" + typeAnnotation]) ||
+            (dataItemModel && dataItemModel.type) ||
+            (lookupNavigationPropertyType(dataItemModel, model)) ||
+            null;
+    };
+
+    var jsonLightDataItemModel = function (name, containerModel) {
+        /// <summary>Gets an object describing a data item in an OData conceptual schema.</summary>
+        /// <param name="name" type="String">Name of the data item for which the model is going to be retrieved.</param>
+        /// <param name="containerModel" type="Object">Object describing the owner of the data item in an OData conceptual schema.</param>
+        /// <returns type="Object">Object describing the data item; null if it cannot be found.</returns>
+
+        if (containerModel) {
+            return lookupProperty(containerModel.property, name) ||
+                lookupProperty(containerModel.navigationProperty, name);
+        }
+        return null;
+    };
+
+    var jsonLightIsEntry = function (data) {
+        /// <summary>Determines whether data represents a JSON light entry object.</summary>
+        /// <param name="data" type="Object">JSON light object to test.</param>
+        /// <returns type="Boolean">True if the data is JSON light entry object; false otherwise.</returns>
+
+        return isComplex(data) && ((odataAnnotationPrefix + "id") in data);
+    };
+
+    var jsonLightIsNavigationProperty = function (name, data, dataItemModel) {
+        /// <summary>Determines whether a data item in a JSON light object is a navigation property.</summary>
+        /// <param name="name" type="String">Name of the data item to test.</param>
+        /// <param name="data" type="Object">JSON light object that owns the data item.</param>
+        /// <param name="dataItemModel" type="Object">Object describing the data item in an OData conceptual schema.</param>
+        /// <returns type="Boolean">True if the data item is a navigation property; false otherwise.</returns>
+
+        djsassert(isComplex(data), "jsonLightIsNavProp - data is not an object!!");
+        if (!!data[name + "@" + navUrlAnnotation] || (dataItemModel && dataItemModel.relationship)) {
+            return true;
+        }
+
+        // Sniff the property value.
+        var value = isArray(data[name]) ? data[name][0] : data[name];
+        return jsonLightIsEntry(value);
+    };
+
+    var jsonLightIsPrimitiveType = function (typeName) {
+        /// <summary>Determines whether a type name is a primitive type in a JSON light payload.</summary>
+        /// <param name="typeName" type="String">Type name to test.</param>
+        /// <returns type="Boolean">True if the type name an EDM primitive type or an OData spatial type; false otherwise.</returns>
+
+        return isPrimitiveEdmType(typeName) || isGeographyEdmType(typeName) || isGeometryEdmType(typeName);
+    };
+
+    var jsonLightReadDataAnnotations = function (data, obj, baseURI, dataModel, model) {
+        /// <summary>Converts annotations found in a JSON light payload object to either properties or metadata.</summary>
+        /// <param name="data" type="Object">JSON light payload object containing the annotations to convert.</param>
+        /// <param name="obj" type="Object">Object that will store the converted annotations.</param>
+        /// <param name="baseURI" type="String">Base URI for normalizing relative URIs found in the payload.</param>
+        /// <param name="dataModel" type="Object">Object describing the JSON light payload in an OData conceptual schema.</param>
+        /// <param name="model" type="Object" optional="true">Object describing an OData conceptual schema.</param>
+        /// <returns>JSON light payload object with its annotations converted to either properties or metadata.</param>
+
+        for (var name in data) {
+            if (name.indexOf(".") > 0 && name.charAt(0) !== "#") {
+                var annotationInfo = jsonLightAnnotationInfo(name);
+                if (annotationInfo) {
+                    var annotationName = annotationInfo.name;
+                    var target = annotationInfo.target;
+                    var targetModel = null;
+                    var targetType = null;
+
+                    if (target) {
+                        targetModel = jsonLightDataItemModel(target, dataModel);
+                        targetType = jsonLightDataItemType(target, data[target], data, targetModel, model);
+                    }
+
+                    if (annotationInfo.isOData) {
+                        jsonLightApplyPayloadODataAnnotation(annotationName, target, targetType, data[name], data, obj, baseURI);
+                    } else {
+                        obj[name] = data[name];
+                    }
+                }
+            }
+        }
+        return obj;
+    };
+
+    var jsonLightApplyPayloadODataAnnotation = function (name, target, targetType, value, data, obj, baseURI) {
+        /// <summary>
+        ///   Processes a JSON Light payload OData annotation producing either a property, payload metadata, or property metadata on its owner object.
+        /// </summary>
+        /// <param name="name" type="String">Annotation name.</param>
+        /// <param name="target" type="String">Name of the property that is being targeted by the annotation.</param>
+        /// <param name="targetType" type="String">Type name of the target property.</param>
+        /// <param name="data" type="Object">JSON light object containing the annotation.</param>
+        /// <param name="obj" type="Object">Object that will hold properties produced by the annotation.</param>
+        /// <param name="baseURI" type="String">Base URI for normalizing relative URIs found in the payload.</param>
+
+        var annotation = name.substring(odataAnnotationPrefix.length);
+
+        switch (annotation) {
+            case "navigationLinkUrl":
+                jsonLightApplyNavigationUrlAnnotation(annotation, target, targetType, value, data, obj, baseURI);
+                return;
+            case "nextLink":
+            case "count":
+                jsonLightApplyFeedAnnotation(annotation, target, value, obj, baseURI);
+                return;
+            case "mediaReadLink":
+            case "mediaEditLink":
+            case "mediaContentType":
+            case "mediaETag":
+                jsonLightApplyMediaAnnotation(annotation, target, targetType, value, obj, baseURI);
+                return;
+            default:
+                jsonLightApplyMetadataAnnotation(annotation, target, value, obj, baseURI);
+                return;
+        }
+    };
+
+    var jsonLightApplyMetadataAnnotation = function (name, target, value, obj, baseURI) {
+        /// <summary>
+        ///    Converts a JSON light annotation that applies to entry metadata only (i.e. odata.editLink or odata.readLink) and its value
+        ///    into their library's internal representation and saves it back to data.
+        /// </summary>
+        /// <param name="name" type="String">Annotation name.</param>
+        /// <param name="target" type="String">Name of the property on which the annotation should be applied.</param>
+        /// <param name="value" type="Object">Annotation value.</param>
+        /// <param name="obj" type="Object">Object that will hold properties produced by the annotation.</param>
+        /// <param name="baseURI" type="String">Base URI for normalizing relative URIs found in the payload.</param>
+
+        var metadata = obj.__metadata = obj.__metadata || {};
+        var mappedName = jsonLightNameMap[name] || name;
+
+        if (name === "editLink") {
+            metadata.uri = normalizeURI(value, baseURI);
+            metadata[mappedName] = metadata.uri;
+            return;
+        }
+
+        if (name === "readLink" || name === "associationLinkUrl") {
+            value = normalizeURI(value, baseURI);
+        }
+
+        if (target) {
+            var propertiesMetadata = metadata.properties = metadata.properties || {};
+            var propertyMetadata = propertiesMetadata[target] = propertiesMetadata[target] || {};
+
+            if (name === "type") {
+                propertyMetadata[mappedName] = propertyMetadata[mappedName] || value;
+                return;
+            }
+            propertyMetadata[mappedName] = value;
+            return;
+        }
+        metadata[mappedName] = value;
+    };
+
+    var jsonLightApplyFeedAnnotation = function (name, target, value, obj, baseURI) {
+        /// <summary>
+        ///    Converts a JSON light annotation that applies to feeds only (i.e. odata.count or odata.nextlink) and its value
+        ///    into their library's internal representation and saves it back to data.
+        /// </summary>
+        /// <param name="name" type="String">Annotation name.</param>
+        /// <param name="target" type="String">Name of the property on which the annotation should be applied.</param>
+        /// <param name="value" type="Object">Annotation value.</param>
+        /// <param name="obj" type="Object">Object that will hold properties produced by the annotation.</param>
+        /// <param name="baseURI" type="String">Base URI for normalizing relative URIs found in the payload.</param>
+
+        var mappedName = jsonLightNameMap[name];
+        var feed = target ? obj[target] : obj;
+        feed[mappedName] = (name === "nextLink") ? normalizeURI(value, baseURI) : value;
+    };
+
+    var jsonLightApplyMediaAnnotation = function (name, target, targetType, value, obj, baseURI) {
+        /// <summary>
+        ///    Converts a JSON light media annotation in and its value into their library's internal representation
+        ///    and saves it back to data or metadata.
+        /// </summary>
+        /// <param name="name" type="String">Annotation name.</param>
+        /// <param name="target" type="String">Name of the property on which the annotation should be applied.</param>
+        /// <param name="targetType" type="String">Type name of the target property.</param>
+        /// <param name="value" type="Object">Annotation value.</param>
+        /// <param name="obj" type="Object">Object that will hold properties produced by the annotation.</param>
+        /// <param name="baseURI" type="String">Base URI for normalizing relative URIs found in the payload.</param>
+
+        var metadata = obj.__metadata = obj.__metadata || {};
+        var mappedName = jsonLightNameMap[name];
+
+        if (name === "mediaReadLink" || name === "mediaEditLink") {
+            value = normalizeURI(value, baseURI);
+        }
+
+        if (target) {
+            var propertiesMetadata = metadata.properties = metadata.properties || {};
+            var propertyMetadata = propertiesMetadata[target] = propertiesMetadata[target] || {};
+            propertyMetadata.type = propertyMetadata.type || targetType;
+
+            obj.__metadata = metadata;
+            obj[target] = obj[target] || { __mediaresource: {} };
+            obj[target].__mediaresource[mappedName] = value;
+            return;
+        }
+
+        metadata[mappedName] = value;
+    };
+
+    var jsonLightApplyNavigationUrlAnnotation = function (name, target, targetType, value, data, obj, baseURI) {
+        /// <summary>
+        ///    Converts a JSON light navigation property annotation and its value into their library's internal representation
+        ///    and saves it back to data o metadata.
+        /// </summary>
+        /// <param name="name" type="String">Annotation name.</param>
+        /// <param name="target" type="String">Name of the property on which the annotation should be applied.</param>
+        /// <param name="targetType" type="String">Type name of the target property.</param>
+        /// <param name="value" type="Object">Annotation value.</param>
+        /// <param name="data" type="Object">JSON light object containing the annotation.</param>
+        /// <param name="obj" type="Object">Object that will hold properties produced by the annotation.</param>
+        /// <param name="baseURI" type="String">Base URI for normalizing relative URIs found in the payload.</param>
+
+        var metadata = obj.__metadata = obj.__metadata || {};
+        var propertiesMetadata = metadata.properties = metadata.properties || {};
+        var propertyMetadata = propertiesMetadata[target] = propertiesMetadata[target] || {};
+        var uri = normalizeURI(value, baseURI);
+
+        if (data.hasOwnProperty(target)) {
+            // The navigation property is inlined in the payload,
+            // so the navigation link url should be pushed to the object's
+            // property metadata instead.
+            propertyMetadata.navigationLinkUrl = uri;
+            return;
+        }
+        obj[target] = { __deferred: { uri: uri} };
+        propertyMetadata.type = propertyMetadata.type || targetType;
+    };
+
+
+    var jsonLightReadDataItemValue = function (value, typeName, dataItemMetadata, baseURI, dataItemModel, model, recognizeDates) {
+        /// <summary>Converts the value of a data item in a JSON light object to its library representation.</summary>
+        /// <param name="value">Data item value to convert.</param>
+        /// <param name="typeName" type="String">Type name of the data item.</param>
+        /// <param name="dataItemMetadata" type="Object">Object containing metadata about the data item.</param>
+        /// <param name="baseURI" type="String">Base URI for normalizing relative URIs found in the payload.</param>
+        /// <param name="dataItemModel" type="Object" optional="true">Object describing the data item in an OData conceptual schema.</param>
+        /// <param name="model" type="Object" optional="true">Object describing an OData conceptual schema.</param>
+        /// <param name="recognizeDates" type="Boolean" optional="true">Flag indicating whether datetime literal strings should be converted to JavaScript Date objects.</param>
+        /// <returns>Data item value in its library representation.</param>
+
+        if (typeof value === "string") {
+            return jsonLightReadStringPropertyValue(value, typeName, recognizeDates);
+        }
+
+        if (!jsonLightIsPrimitiveType(typeName)) {
+            if (isArray(value)) {
+                return jsonLightReadCollectionPropertyValue(value, typeName, dataItemMetadata, baseURI, model, recognizeDates);
+            }
+
+            if (isComplex(value)) {
+                return jsonLightReadComplexPropertyValue(value, typeName, dataItemMetadata, baseURI, model, recognizeDates);
+            }
+        }
+        return value;
+    };
+
+    var jsonLightReadStringPropertyValue = function (value, propertyType, recognizeDates) {
+        /// <summary>Convertes the value of a string property in a JSON light object to its library representation.</summary>
+        /// <param name="value" type="String">String value to convert.</param>
+        /// <param name="propertyType" type="String">Type name of the property.</param>
+        /// <param name="recognizeDates" type="Boolean" optional="true">Flag indicating whether datetime literal strings should be converted to JavaScript Date objects.</param>
+        /// <returns>String property value in its library representation.</returns>
+
+        switch (propertyType) {
+            case EDM_TIME:
+                return parseDuration(value);
+            case EDM_DATETIME:
+                return parseDateTime(value, /*nullOnError*/false);
+            case EDM_DATETIMEOFFSET:
+                return parseDateTimeOffset(value, /*nullOnError*/false);
+        }
+
+        if (recognizeDates) {
+            return parseDateTime(value, /*nullOnError*/true) ||
+                   parseDateTimeOffset(value, /*nullOnError*/true) ||
+                   value;
+        }
+        return value;
+    };
+
+    var jsonLightReadCollectionPropertyValue = function (value, propertyType, propertyMetadata, baseURI, model, recognizeDates) {
+        /// <summary>Converts the value of a collection property in a JSON light object into its library representation.</summary>
+        /// <param name="value" type="Array">Collection property value to convert.</param>
+        /// <param name="propertyType" type="String">Property type name.</param>
+        /// <param name="propertyMetadata" type="Object">Object containing metadata about the collection property.</param>
+        /// <param name="baseURI" type="String">Base URI for normalizing relative URIs found in the payload.</param>
+        /// <param name="model" type="Object" optional="true">Object describing an OData conceptual schema.</param>
+        /// <param name="recognizeDates" type="Boolean" optional="true">Flag indicating whether datetime literal strings should be converted to JavaScript Date objects.</param>
+        /// <returns type="Object">Collection property value in its library representation.</returns>
+
+        var collectionType = getCollectionType(propertyType);
+        var itemsMetadata = [];
+        var items = [];
+
+        var i, len;
+        for (i = 0, len = value.length; i < len; i++) {
+            var itemType = jsonLightDataItemType(null, value[i]) || collectionType;
+            var itemMetadata = { type: itemType };
+            var item = jsonLightReadDataItemValue(value[i], itemType, itemMetadata, baseURI, null, model, recognizeDates);
+
+            if (!jsonLightIsPrimitiveType(itemType) && !isPrimitive(value[i])) {
+                itemsMetadata.push(itemMetadata);
+            }
+            items.push(item);
+        }
+
+        if (itemsMetadata.length > 0) {
+            propertyMetadata.elements = itemsMetadata;
+        }
+
+        return { __metadata: { type: propertyType }, results: items };
+    };
+
+    var jsonLightReadComplexPropertyValue = function (value, propertyType, propertyMetadata, baseURI, model, recognizeDates) {
+        /// <summary>Converts the value of a comples property in a JSON light object into its library representation.</summary>
+        /// <param name="value" type="Object">Complex property value to convert.</param>
+        /// <param name="propertyType" type="String">Property type name.</param>
+        /// <param name="propertyMetadata" type="Object">Object containing metadata about the complx type property.</param>
+        /// <param name="baseURI" type="String">Base URI for normalizing relative URIs found in the payload.</param>
+        /// <param name="model" type="Object" optional="true">Object describing an OData conceptual schema.</param>
+        /// <param name="recognizeDates" type="Boolean" optional="true">Flag indicating whether datetime literal strings should be converted to JavaScript Date objects.</param>
+        /// <returns type="Object">Complex property value in its library representation.</returns>
+
+        var complexValue = jsonLightReadObject(value, { type: propertyType }, baseURI, model, recognizeDates);
+        var complexMetadata = complexValue.__metadata;
+        var complexPropertiesMetadata = complexMetadata.properties;
+
+        if (complexPropertiesMetadata) {
+            propertyMetadata.properties = complexPropertiesMetadata;
+            delete complexMetadata.properties;
+        }
+        return complexValue;
+    };
+
+    var jsonLightReadNavigationPropertyValue = function (value, propertyInfo, baseURI, model, recognizeDates) {
+        /// <summary>Converts the value of a navigation property in a JSON light object into its library representation.</summary>
+        /// <param name="value">Navigation property property value to convert.</param>
+        /// <param name="propertyInfo" type="String">Information about the property whether it's an entry, feed or complex type.</param>
+        /// <param name="baseURI" type="String">Base URI for normalizing relative URIs found in the payload.</param>
+        /// <param name="model" type="Object" optional="true">Object describing an OData conceptual schema.</param>
+        /// <param name="recognizeDates" type="Boolean" optional="true">Flag indicating whether datetime literal strings should be converted to JavaScript Date objects.</param>
+        /// <returns type="Object">Collection property value in its library representation.</returns>
+
+        if (isArray(value)) {
+            return jsonLightReadFeed(value, propertyInfo, baseURI, model, recognizeDates);
+        }
+
+        if (isComplex(value)) {
+            return jsonLightReadObject(value, propertyInfo, baseURI, model, recognizeDates);
+        }
+        return null;
+    };
+
+    var jsonLightReadObject = function (data, objectInfo, baseURI, model, recognizeDates) {
+        /// <summary>Converts a JSON light entry or complex type object into its library representation.</summary>
+        /// <param name="data" type="Object">JSON light entry or complex type object to convert.</param>
+        /// <param name="objectInfo" type="Object">Information about the entry or complex.</param>
+        /// <param name="baseURI" type="String">Base URI for normalizing relative URIs found in the payload.</param>
+        /// <param name="model" type="Object" optional="true">Object describing an OData conceptual schema.</param>
+        /// <param name="recognizeDates" type="Boolean" optional="true">Flag indicating whether datetime literal strings should be converted to JavaScript Date objects.</param>
+        /// <returns type="Object">Entry or complex type object.</param>
+
+        objectInfo = objectInfo || {};
+        var actualType = data[typeAnnotation] || objectInfo.type || null;
+        var dataModel = lookupEntityType(actualType, model);
+        var isEntry = true;
+        if (!dataModel) {
+            isEntry = false;
+            dataModel = lookupComplexType(actualType, model);
+        }
+
+        var metadata = { type: actualType };
+        var obj = { __metadata: metadata };
+        var propertiesMetadata = {};
+        var baseTypeModel;
+        if (isEntry && dataModel && objectInfo.entitySet && objectInfo.contentTypeOdata == "minimalmetadata") {
+            var serviceURI = baseURI.substring(0, baseURI.lastIndexOf("$metadata"));
+            baseTypeModel = null; // check if the key model is in a parent type.
+            if (!dataModel.key) {
+                baseTypeModel = dataModel;
+            }
+            while (!!baseTypeModel && !baseTypeModel.key && baseTypeModel.baseType) {
+                baseTypeModel = lookupEntityType(baseTypeModel.baseType, model);
+            }
+
+            if (dataModel.key || (!!baseTypeModel && baseTypeModel.key)) {
+                var entryKey;
+                if (dataModel.key) {
+                    entryKey = jsonLightGetEntryKey(data, dataModel);
+                } else {
+                    entryKey = jsonLightGetEntryKey(data, baseTypeModel);
+                }
+                if (entryKey) {
+                    var entryInfo = {
+                        key: entryKey,
+                        entitySet: objectInfo.entitySet,
+                        functionImport: objectInfo.functionImport,
+                        containerName: objectInfo.containerName
+                    };
+                    jsonLightComputeUrisIfMissing(data, entryInfo, actualType, serviceURI, dataModel, baseTypeModel);
+                }
+            }
+        }
+
+        for (var name in data) {
+            if (name.indexOf("#") === 0) {
+                // This is an advertised function or action.
+                jsonLightReadAdvertisedFunctionOrAction(name.substring(1), data[name], obj, baseURI, model);
+            } else {
+                // Is name NOT an annotation?
+                if (name.indexOf(".") === -1) {
+                    if (!metadata.properties) {
+                        metadata.properties = propertiesMetadata;
+                    }
+
+                    var propertyValue = data[name];
+                    var propertyModel = propertyModel = jsonLightDataItemModel(name, dataModel);
+                    baseTypeModel = dataModel;
+                    while (!!dataModel && propertyModel === null && baseTypeModel.baseType) {
+                        baseTypeModel = lookupEntityType(baseTypeModel.baseType, model);
+                        propertyModel = propertyModel = jsonLightDataItemModel(name, baseTypeModel);
+                    }
+                    var isNavigationProperty = jsonLightIsNavigationProperty(name, data, propertyModel);
+                    var propertyType = jsonLightDataItemType(name, propertyValue, data, propertyModel, model);
+                    var propertyMetadata = propertiesMetadata[name] = propertiesMetadata[name] || { type: propertyType };
+                    if (isNavigationProperty) {
+                        var propertyInfo = {};
+                        if (objectInfo.entitySet !== undefined) {
+                            var navigationPropertyEntitySetName = lookupNavigationPropertyEntitySet(propertyModel, objectInfo.entitySet.name, model);
+                            propertyInfo = getEntitySetInfo(navigationPropertyEntitySetName, model);
+                        }
+                        propertyInfo.contentTypeOdata = objectInfo.contentTypeOdata;
+                        propertyInfo.kind = objectInfo.kind;
+                        propertyInfo.type = propertyType;
+                        obj[name] = jsonLightReadNavigationPropertyValue(propertyValue, propertyInfo, baseURI, model, recognizeDates);
+                    } else {
+                        obj[name] = jsonLightReadDataItemValue(propertyValue, propertyType, propertyMetadata, baseURI, propertyModel, model, recognizeDates);
+                    }
+                }
+            }
+        }
+
+        return jsonLightReadDataAnnotations(data, obj, baseURI, dataModel, model);
+    };
+
+    var jsonLightReadAdvertisedFunctionOrAction = function (name, value, obj, baseURI, model) {
+        /// <summary>Converts a JSON light advertised action or function object into its library representation.</summary>
+        /// <param name="name" type="String">Advertised action or function name.</param>
+        /// <param name="value">Advertised action or function value.</param>
+        /// <param name="obj" type="Object">Object that will the converted value of the advertised action or function.</param>
+        /// <param name="baseURI" type="String">Base URI for normalizing the action's or function's relative URIs.</param>
+        /// <param name="model" type="Object" optional="true">Object describing an OData conceptual schema.</param>
+        /// <remarks>
+        ///     Actions and functions have the same representation in json light, so to disambiguate them the function uses
+        ///     the model object.  If available, the function will look for the functionImport object that describes the
+        ///     the action or the function.  If for whatever reason the functionImport can't be retrieved from the model (like
+        ///     there is no model available or there is no functionImport within the model), then the value is going to be treated
+        ///     as an advertised action and stored under obj.__metadata.actions.
+        /// </remarks>
+
+        if (!name || !isArray(value) && !isComplex(value)) {
+            return;
+        }
+
+        var isFunction = false;
+        var nsEnd = name.lastIndexOf(".");
+        var simpleName = name.substring(nsEnd + 1);
+        var containerName = (nsEnd > -1) ? name.substring(0, nsEnd) : "";
+
+        var container = (simpleName === name || containerName.indexOf(".") === -1) ?
+            lookupDefaultEntityContainer(model) :
+            lookupEntityContainer(containerName, model);
+
+        if (container) {
+            var functionImport = lookupFunctionImport(container.functionImport, simpleName);
+            if (functionImport && !!functionImport.isSideEffecting) {
+                isFunction = !parseBool(functionImport.isSideEffecting);
+            }
+        }
+
+        var metadata = obj.__metadata;
+        var targetName = isFunction ? "functions" : "actions";
+        var metadataURI = normalizeURI(name, baseURI);
+        var items = (isArray(value)) ? value : [value];
+
+        var i, len;
+        for (i = 0, len = items.length; i < len; i++) {
+            var item = items[i];
+            if (item) {
+                var targetCollection = metadata[targetName] = metadata[targetName] || [];
+                var actionOrFunction = { metadata: metadataURI, title: item.title, target: normalizeURI(item.target, baseURI) };
+                targetCollection.push(actionOrFunction);
+            }
+        }
+    };
+
+    var jsonLightReadFeed = function (data, feedInfo, baseURI, model, recognizeDates) {
+        /// <summary>Converts a JSON light feed or top level collection property object into its library representation.</summary>
+        /// <param name="data" type="Object">JSON light feed object to convert.</param>
+        /// <param name="typeName" type="String">Type name of the feed or collection items.</param>
+        /// <param name="baseURI" type="String">Base URI for normalizing relative URIs found in the payload.</param>
+        /// <param name="model" type="Object" optional="true">Object describing an OData conceptual schema.</param>
+        /// <param name="recognizeDates" type="Boolean" optional="true">Flag indicating whether datetime literal strings should be converted to JavaScript Date objects.</param>
+        /// <returns type="Object">Feed or top level collection object.</param>
+
+        var items = isArray(data) ? data : data.value;
+        var entries = [];
+        var i, len, entry;
+        for (i = 0, len = items.length; i < len; i++) {
+            entry = jsonLightReadObject(items[i], feedInfo, baseURI, model, recognizeDates);
+            entries.push(entry);
+        }
+
+        var feed = { results: entries };
+
+        if (isComplex(data)) {
+            for (var name in data) {
+                if (name.indexOf("#") === 0) {
+                    // This is an advertised function or action.
+                    feed.__metadata = feed.__metadata || {};
+                    jsonLightReadAdvertisedFunctionOrAction(name.substring(1), data[name], feed, baseURI, model);
+                }
+            }
+            feed = jsonLightReadDataAnnotations(data, feed, baseURI);
+        }
+        return feed;
+    };
+
+    var jsonLightGetEntryKey = function (data, entityModel) {
+        /// <summary>Gets the key of an entry.</summary>
+        /// <param name="data" type="Object">JSON light entry.</param>
+        /// <param name="entityModel" type="String">Object describing the entry Model</param>
+        /// <returns type="string">Entry instance key.</returns>
+
+        var entityInstanceKey;
+        var entityKeys = entityModel.key.propertyRef;
+        var type;
+        entityInstanceKey = "(";
+        if (entityKeys.length == 1) {
+            type = lookupProperty(entityModel.property, entityKeys[0].name).type;
+            entityInstanceKey += formatLiteral(data[entityKeys[0].name], type);
+        } else {
+            var first = true;
+            for (var i = 0; i < entityKeys.length; i++) {
+                if (!first) {
+                    entityInstanceKey += ",";
+                } else {
+                    first = false;
+                }
+                type = lookupProperty(entityModel.property, entityKeys[i].name).type;
+                entityInstanceKey += entityKeys[i].name + "=" + formatLiteral(data[entityKeys[i].name], type);
+            }
+        }
+        entityInstanceKey += ")";
+        return entityInstanceKey;
+    };
+
+
+    var jsonLightComputeUrisIfMissing = function (data, entryInfo, actualType, serviceURI, entityModel, baseTypeModel) {
+        /// <summary>Compute the URI according to OData conventions if it doesn't exist</summary>
+        /// <param name="data" type="Object">JSON light entry.</param>
+        /// <param name="entryInfo" type="Object">Information about the entry includes type, key, entitySet and entityContainerName.</param>
+        /// <param name="actualType" type="String">Type of the entry</param>
+        /// <param name="serviceURI" type="String">Base URI the service.</param>
+        /// <param name="entityModel" type="Object">Object describing an OData conceptual schema of the entry.</param>
+        /// <param name="baseTypeModel" type="Object" optional="true">Object escribing an OData conceptual schema of the baseType if it exists.</param>
+
+        var lastIdSegment = data[jsonLightAnnotations.id] || data[jsonLightAnnotations.read] || data[jsonLightAnnotations.edit] || entryInfo.entitySet.name + entryInfo.key;
+        data[jsonLightAnnotations.id] = serviceURI + lastIdSegment;
+        if (!data[jsonLightAnnotations.edit]) {
+            data[jsonLightAnnotations.edit] = entryInfo.entitySet.name + entryInfo.key;
+            if (entryInfo.entitySet.entityType != actualType) {
+                data[jsonLightAnnotations.edit] += "/" + actualType;
+            }
+        }
+        data[jsonLightAnnotations.read] = data[jsonLightAnnotations.read] || data[jsonLightAnnotations.edit];
+        if (!data[jsonLightAnnotations.etag]) {
+            var etag = jsonLightComputeETag(data, entityModel, baseTypeModel);
+            if (!!etag) {
+                data[jsonLightAnnotations.etag] = etag;
+            }
+        }
+
+        jsonLightComputeStreamLinks(data, entityModel, baseTypeModel);
+        jsonLightComputeNavigationAndAssociationProperties(data, entityModel, baseTypeModel);
+        jsonLightComputeFunctionImports(data, entryInfo);
+    };
+
+    var jsonLightComputeETag = function (data, entityModel, baseTypeModel) {
+        /// <summary>Computes the etag of an entry</summary>
+        /// <param name="data" type="Object">JSON light entry.</param>
+        /// <param name="entryInfo" type="Object">Object describing the entry model.</param>
+        /// <param name="baseTypeModel" type="Object"  optional="true">Object describing an OData conceptual schema of the baseType if it exists.</param>
+        /// <returns type="string">Etag value</returns>
+        var etag = "";
+        var propertyModel;
+        for (var i = 0; entityModel.property && i < entityModel.property.length; i++) {
+            propertyModel = entityModel.property[i];
+            etag = jsonLightAppendValueToEtag(data, etag, propertyModel);
+
+        }
+        if (baseTypeModel) {
+            for (i = 0; baseTypeModel.property && i < baseTypeModel.property.length; i++) {
+                propertyModel = baseTypeModel.property[i];
+                etag = jsonLightAppendValueToEtag(data, etag, propertyModel);
+            }
+        }
+        if (etag.length > 0) {
+            return etag + "\"";
+        }
+        return null;
+    };
+
+    var jsonLightAppendValueToEtag = function (data, etag, propertyModel) {
+        /// <summary>Adds a propery value to the etag after formatting.</summary>
+        /// <param name="data" type="Object">JSON light entry.</param>
+        /// <param name="etag" type="Object">value of the etag.</param>
+        /// <param name="propertyModel" type="Object">Object describing an OData conceptual schema of the property.</param>
+        /// <returns type="string">Etag value</returns>
+
+        if (propertyModel.concurrencyMode == "Fixed") {
+            if (etag.length > 0) {
+                etag += ",";
+            } else {
+                etag += "W/\"";
+            }
+            if (data[propertyModel.name] !== null) {
+                etag += formatLiteral(data[propertyModel.name], propertyModel.type);
+            } else {
+                etag += "null";
+            }
+        }
+        return etag;
+    };
+
+    var jsonLightComputeNavigationAndAssociationProperties = function (data, entityModel, baseTypeModel) {
+        /// <summary>Adds navigation links to the entry metadata</summary>
+        /// <param name="data" type="Object">JSON light entry.</param>
+        /// <param name="entityModel" type="Object">Object describing the entry model.</param>
+        /// <param name="baseTypeModel" type="Object"  optional="true">Object describing an OData conceptual schema of the baseType if it exists.</param>
+
+        var navigationLinkAnnotation = "@odata.navigationLinkUrl";
+        var associationLinkAnnotation = "@odata.associationLinkUrl";
+        var navigationPropertyName, navigationPropertyAnnotation, associationPropertyAnnotation;
+        for (var i = 0; entityModel.navigationProperty && i < entityModel.navigationProperty.length; i++) {
+            navigationPropertyName = entityModel.navigationProperty[i].name;
+            navigationPropertyAnnotation = navigationPropertyName + navigationLinkAnnotation;
+            if (data[navigationPropertyAnnotation] === undefined) {
+                data[navigationPropertyAnnotation] = data[jsonLightAnnotations.edit] + "/" + encodeURIComponent(navigationPropertyName);
+            }
+            associationPropertyAnnotation = navigationPropertyName + associationLinkAnnotation;
+            if (data[associationPropertyAnnotation] === undefined) {
+                data[associationPropertyAnnotation] = data[jsonLightAnnotations.edit] + "/$links/" + encodeURIComponent(navigationPropertyName);
+            }
+        }
+
+        if (baseTypeModel && baseTypeModel.navigationProperty) {
+            for (i = 0; i < baseTypeModel.navigationProperty.length; i++) {
+                navigationPropertyName = baseTypeModel.navigationProperty[i].name;
+                navigationPropertyAnnotation = navigationPropertyName + navigationLinkAnnotation;
+                if (data[navigationPropertyAnnotation] === undefined) {
+                    data[navigationPropertyAnnotation] = data[jsonLightAnnotations.edit] + "/" + encodeURIComponent(navigationPropertyName);
+                }
+                associationPropertyAnnotation = navigationPropertyName + associationLinkAnnotation;
+                if (data[associationPropertyAnnotation] === undefined) {
+                    data[associationPropertyAnnotation] = data[jsonLightAnnotations.edit] + "/$links/" + encodeURIComponent(navigationPropertyName);
+                }
+            }
+        }
+    };
+
+    var formatLiteral = function (value, type) {
+        /// <summary>Formats a value according to Uri literal format</summary>
+        /// <param name="value">Value to be formatted.</param>
+        /// <param name="type">Edm type of the value</param>
+        /// <returns type="string">Value after formatting</returns>
+
+        value = "" + formatRowLiteral(value, type);
+        value = encodeURIComponent(value.replace("'", "''"));
+        switch ((type)) {
+            case "Edm.Binary":
+                return "X'" + value + "'";
+            case "Edm.DateTime":
+                return "datetime" + "'" + value + "'";
+            case "Edm.DateTimeOffset":
+                return "datetimeoffset" + "'" + value + "'";
+            case "Edm.Decimal":
+                return value + "M";
+            case "Edm.Guid":
+                return "guid" + "'" + value + "'";
+            case "Edm.Int64":
+                return value + "L";
+            case "Edm.Float":
+                return value + "f";
+            case "Edm.Double":
+                return value + "D";
+            case "Edm.Geography":
+                return "geography" + "'" + value + "'";
+            case "Edm.Geometry":
+                return "geometry" + "'" + value + "'";
+            case "Edm.Time":
+                return "time" + "'" + value + "'";
+            case "Edm.String":
+                return "'" + value + "'";
+            default:
+                return value;
+        }
+    };
+
+
+    var formatRowLiteral = function (value, type) {
+        switch (type) {
+            case "Edm.Binary":
+                return convertByteArrayToHexString(value);
+            default:
+                return value;
+        }
+    };
+
+    var jsonLightComputeFunctionImports = function (data, entryInfo) {
+        /// <summary>Adds functions and actions links to the entry metadata</summary>
+        /// <param name="entry" type="Object">JSON light entry.</param>
+        /// <param name="entityInfo" type="Object">Object describing the entry</param>
+
+        var functionImport = entryInfo.functionImport || [];
+        for (var i = 0; i < functionImport.length; i++) {
+            if (functionImport[i].isBindable && functionImport[i].parameter[0] && functionImport[i].parameter[0].type == entryInfo.entitySet.entityType) {
+                var functionImportAnnotation = "#" + entryInfo.containerName + "." + functionImport[i].name;
+                if (data[functionImportAnnotation] == undefined) {
+                    data[functionImportAnnotation] = {
+                        title: functionImport[i].name,
+                        target: data[jsonLightAnnotations.edit] + "/" + functionImport[i].name
+                    };
+                }
+            }
+        }
+    };
+
+    var jsonLightComputeStreamLinks = function (data, entityModel, baseTypeModel) {
+        /// <summary>Adds stream links to the entry metadata</summary>
+        /// <param name="data" type="Object">JSON light entry.</param>
+        /// <param name="entityModel" type="Object">Object describing the entry model.</param>
+        /// <param name="baseTypeModel" type="Object"  optional="true">Object describing an OData conceptual schema of the baseType if it exists.</param>
+
+        if (entityModel.hasStream || (baseTypeModel && baseTypeModel.hasStream)) {
+            data[jsonLightAnnotations.mediaEdit] = data[jsonLightAnnotations.mediaEdit] || data[jsonLightAnnotations.mediaEdit] + "/$value";
+            data[jsonLightAnnotations.mediaRead] = data[jsonLightAnnotations.mediaRead] || data[jsonLightAnnotations.mediaEdit];
+        }
+    };
+
+    var jsonLightReadTopPrimitiveProperty = function (data, typeName, baseURI, recognizeDates) {
+        /// <summary>Converts a JSON light top level primitive property object into its library representation.</summary>
+        /// <param name="data" type="Object">JSON light feed object to convert.</param>
+        /// <param name="typeName" type="String">Type name of the primitive property.</param>
+        /// <param name="baseURI" type="String">Base URI for normalizing relative URIs found in the payload.</param>
+        /// <param name="recognizeDates" type="Boolean" optional="true">Flag indicating whether datetime literal strings should be converted to JavaScript Date objects.</param>
+        /// <returns type="Object">Top level primitive property object.</param>
+
+        var metadata = { type: typeName };
+        var value = jsonLightReadDataItemValue(data.value, typeName, metadata, baseURI, null, null, recognizeDates);
+        return jsonLightReadDataAnnotations(data, { __metadata: metadata, value: value }, baseURI);
+    };
+
+    var jsonLightReadTopCollectionProperty = function (data, typeName, baseURI, model, recognizeDates) {
+        /// <summary>Converts a JSON light top level collection property object into its library representation.</summary>
+        /// <param name="data" type="Object">JSON light feed object to convert.</param>
+        /// <param name="typeName" type="String">Type name of the collection property.</param>
+        /// <param name="baseURI" type="String">Base URI for normalizing relative URIs found in the payload.</param>
+        /// <param name="model" type="Object" optional="true">Object describing an OData conceptual schema.</param>
+        /// <param name="recognizeDates" type="Boolean" optional="true">Flag indicating whether datetime literal strings should be converted to JavaScript Date objects.</param>
+        /// <returns type="Object">Top level collection property object.</param>
+
+        var propertyMetadata = {};
+        var value = jsonLightReadCollectionPropertyValue(data.value, typeName, propertyMetadata, baseURI, model, recognizeDates);
+        extend(value.__metadata, propertyMetadata);
+        return jsonLightReadDataAnnotations(data, value, baseURI);
+    };
+
+    var jsonLightReadLinksDocument = function (data, baseURI) {
+        /// <summary>Converts a JSON light links collection object to its library representation.</summary>
+        /// <param name="data" type="Object">JSON light link object to convert.</param>
+        /// <param name="baseURI" type="String">Base URI for normalizing relative URIs found in the payload.</param>
+        /// <returns type="Object">Links collection object.</param>
+
+        var items = data.value;
+        if (!isArray(items)) {
+            return jsonLightReadLink(data, baseURI);
+        }
+
+        var results = [];
+        var i, len;
+        for (i = 0, len = items.length; i < len; i++) {
+            results.push(jsonLightReadLink(items[i], baseURI));
+        }
+
+        var links = { results: results };
+        return jsonLightReadDataAnnotations(data, links, baseURI);
+    };
+
+    var jsonLightReadLink = function (data, baseURI) {
+        /// <summary>Converts a JSON light link object to its library representation.</summary>
+        /// <param name="data" type="Object">JSON light link object to convert.</param>
+        /// <param name="baseURI" type="String">Base URI for normalizing relative URIs found in the payload.</param>
+        /// <returns type="Object">Link object.</param>
+
+        var link = { uri: normalizeURI(data.url, baseURI) };
+
+        link = jsonLightReadDataAnnotations(data, link, baseURI);
+        var metadata = link.__metadata || {};
+        var metadataProperties = metadata.properties || {};
+
+        jsonLightRemoveTypePropertyMetadata(metadataProperties.url);
+        renameProperty(metadataProperties, "url", "uri");
+
+        return link;
+    };
+
+    var jsonLightRemoveTypePropertyMetadata = function (propertyMetadata) {
+        /// <summary>Removes the type property from a property metadata object.</summary>
+        /// <param name="propertyMetadata" type="Object">Property metadata object.</param>
+
+        if (propertyMetadata) {
+            delete propertyMetadata.type;
+        }
+    };
+
+    var jsonLightReadSvcDocument = function (data, baseURI) {
+        /// <summary>Converts a JSON light service document object to its library representation.</summary>
+        /// <param name="data" type="Object">JSON light service document object to convert.</param>
+        /// <param name="baseURI" type="String">Base URI for normalizing relative URIs found in the payload.</param>
+        /// <returns type="Object">Link object.</param>
+
+        var items = data.value;
+        var collections = [];
+        var workspace = jsonLightReadDataAnnotations(data, { collections: collections }, baseURI);
+
+        var metadata = workspace.__metadata || {};
+        var metadataProperties = metadata.properties || {};
+
+        jsonLightRemoveTypePropertyMetadata(metadataProperties.value);
+        renameProperty(metadataProperties, "value", "collections");
+
+        var i, len;
+        for (i = 0, len = items.length; i < len; i++) {
+            var item = items[i];
+            var collection = { title: item.name, href: normalizeURI(item.url, baseURI) };
+
+            collection = jsonLightReadDataAnnotations(item, collection, baseURI);
+            metadata = collection.__metadata || {};
+            metadataProperties = metadata.properties || {};
+
+            jsonLightRemoveTypePropertyMetadata(metadataProperties.name);
+            jsonLightRemoveTypePropertyMetadata(metadataProperties.url);
+
+            renameProperty(metadataProperties, "name", "title");
+            renameProperty(metadataProperties, "url", "href");
+
+            collections.push(collection);
+        }
+
+        return { workspaces: [workspace] };
+    };
+
+    var jsonLightMakePayloadInfo = function (kind, type) {
+        /// <summary>Creates an object containing information for the json light payload.</summary>
+        /// <param name="kind" type="String">JSON light payload kind, one of the PAYLOADTYPE_XXX constant values.</param>
+        /// <param name="typeName" type="String">Type name of the JSON light payload.</param>
+        /// <returns type="Object">Object with kind and type fields.</returns>
+
+        /// <field name="kind" type="String">Kind of the JSON light payload. One of the PAYLOADTYPE_XXX constant values.</field>
+        /// <field name="type" type="String">Data type of the JSON light payload.</field>
+
+        return { kind: kind, type: type || null };
+    };
+
+    var jsonLightPayloadInfo = function (data, model, inferFeedAsComplexType) {
+        /// <summary>Infers the information describing the JSON light payload from its metadata annotation, structure, and data model.</summary>
+        /// <param name="data" type="Object">Json light response payload object.</param>
+        /// <param name="model" type="Object">Object describing an OData conceptual schema.</param>
+        /// <param name="inferFeedAsComplexType" type="Boolean">True if a JSON light payload that looks like a feed should be treated as a complex type property instead.</param>
+        /// <remarks>
+        ///     If the arguments passed to the function don't convey enough information about the payload to determine without doubt that the payload is a feed then it
+        ///     will try to use the payload object structure instead.  If the payload looks like a feed (has value property that is an array or non-primitive values) then
+        ///     the function will report its kind as PAYLOADTYPE_FEED unless the inferFeedAsComplexType flag is set to true. This flag comes from the user request
+        ///     and allows the user to control how the library behaves with an ambigous JSON light payload.
+        /// </remarks>
+        /// <returns type="Object">
+        ///     Object with kind and type fields. Null if there is no metadata annotation or the payload info cannot be obtained..
+        /// </returns>
+
+        var metadataUri = data[metadataAnnotation];
+        if (!metadataUri || typeof metadataUri !== "string") {
+            return null;
+        }
+
+        var fragmentStart = metadataUri.lastIndexOf("#");
+        if (fragmentStart === -1) {
+            return jsonLightMakePayloadInfo(PAYLOADTYPE_SVCDOC);
+        }
+
+        var elementStart = metadataUri.indexOf("@Element", fragmentStart);
+        var fragmentEnd = elementStart - 1;
+
+        if (fragmentEnd < 0) {
+            fragmentEnd = metadataUri.indexOf("?", fragmentStart);
+            if (fragmentEnd === -1) {
+                fragmentEnd = metadataUri.length;
+            }
+        }
+
+        var fragment = metadataUri.substring(fragmentStart + 1, fragmentEnd);
+        if (fragment.indexOf("/$links/") > 0) {
+            return jsonLightMakePayloadInfo(PAYLOADTYPE_LINKS);
+        }
+
+        var fragmentParts = fragment.split("/");
+        if (fragmentParts.length >= 0) {
+            var qualifiedName = fragmentParts[0];
+            var typeCast = fragmentParts[1];
+
+            if (jsonLightIsPrimitiveType(qualifiedName)) {
+                return jsonLightMakePayloadInfo(PAYLOADTYPE_PRIMITIVE, qualifiedName);
+            }
+
+            if (isCollectionType(qualifiedName)) {
+                return jsonLightMakePayloadInfo(PAYLOADTYPE_COLLECTION, qualifiedName);
+            }
+
+            var entityType = typeCast;
+            var entitySet, functionImport, containerName;
+            if (!typeCast) {
+                var nsEnd = qualifiedName.lastIndexOf(".");
+                var simpleName = qualifiedName.substring(nsEnd + 1);
+                var container = (simpleName === qualifiedName) ?
+                    lookupDefaultEntityContainer(model) :
+                    lookupEntityContainer(qualifiedName.substring(0, nsEnd), model);
+
+                if (container) {
+                    entitySet = lookupEntitySet(container.entitySet, simpleName);
+                    functionImport = container.functionImport;
+                    containerName = container.name;
+                    entityType = !!entitySet ? entitySet.entityType : null;
+                }
+            }
+
+            var info;
+            if (elementStart > 0) {
+                info = jsonLightMakePayloadInfo(PAYLOADTYPE_OBJECT, entityType);
+                info.entitySet = entitySet;
+                info.functionImport = functionImport;
+                info.containerName = containerName;
+                return info;
+            }
+
+            if (entityType) {
+                info = jsonLightMakePayloadInfo(PAYLOADTYPE_FEED, entityType);
+                info.entitySet = entitySet;
+                info.functionImport = functionImport;
+                info.containerName = containerName;
+                return info;
+            }
+
+            if (isArray(data.value) && !lookupComplexType(qualifiedName, model)) {
+                var item = data.value[0];
+                if (!isPrimitive(item)) {
+                    if (jsonLightIsEntry(item) || !inferFeedAsComplexType) {
+                        return jsonLightMakePayloadInfo(PAYLOADTYPE_FEED, null);
+                    }
+                }
+            }
+
+            return jsonLightMakePayloadInfo(PAYLOADTYPE_OBJECT, qualifiedName);
+        }
+
+        return null;
+    };
+
+    var jsonLightReadPayload = function (data, model, recognizeDates, inferFeedAsComplexType, contentTypeOdata) {
+        /// <summary>Converts a JSON light response payload object into its library's internal representation.</summary>
+        /// <param name="data" type="Object">Json light response payload object.</param>
+        /// <param name="model" type="Object">Object describing an OData conceptual schema.</param>
+        /// <param name="recognizeDates" type="Boolean" optional="true">Flag indicating whether datetime literal strings should be converted to JavaScript Date objects.</param>
+        /// <param name="inferFeedAsComplexType" type="Boolean">True if a JSON light payload that looks like a feed should be reported as a complex type property instead.</param>
+        /// <param name="contentTypeOdata" type="string">Includes the type of json ( minimalmetadata, fullmetadata .. etc )</param>
+        /// <returns type="Object">Object in the library's representation.</returns>
+
+        if (!isComplex(data)) {
+            return data;
+        }
+
+        contentTypeOdata = contentTypeOdata || "minimalmetadata";
+        var baseURI = data[metadataAnnotation];
+        var payloadInfo = jsonLightPayloadInfo(data, model, inferFeedAsComplexType);
+        if (assigned(payloadInfo)) {
+            payloadInfo.contentTypeOdata = contentTypeOdata;
+        }
+        var typeName = null;
+        if (payloadInfo) {
+            delete data[metadataAnnotation];
+
+            typeName = payloadInfo.type;
+            switch (payloadInfo.kind) {
+                case PAYLOADTYPE_FEED:
+                    return jsonLightReadFeed(data, payloadInfo, baseURI, model, recognizeDates);
+                case PAYLOADTYPE_COLLECTION:
+                    return jsonLightReadTopCollectionProperty(data, typeName, baseURI, model, recognizeDates);
+                case PAYLOADTYPE_PRIMITIVE:
+                    return jsonLightReadTopPrimitiveProperty(data, typeName, baseURI, recognizeDates);
+                case PAYLOADTYPE_SVCDOC:
+                    return jsonLightReadSvcDocument(data, baseURI);
+                case PAYLOADTYPE_LINKS:
+                    return jsonLightReadLinksDocument(data, baseURI);
+            }
+        }
+        return jsonLightReadObject(data, payloadInfo, baseURI, model, recognizeDates);
+    };
+
+    var jsonLightSerializableMetadata = ["type", "etag", "media_src", "edit_media", "content_type", "media_etag"];
+
+    var formatJsonLight = function (obj, context) {
+        /// <summary>Converts an object in the library's internal representation to its json light representation.</summary>
+        /// <param name="obj" type="Object">Object the library's internal representation.</param>
+        /// <param name="context" type="Object">Object with the serialization context.</param>
+        /// <returns type="Object">Object in its json light representation.</returns>
+
+        // Regular expression used to test that the uri is for a $links document.
+        var linksUriRE = /\/\$links\//;
+        var data = {};
+        var metadata = obj.__metadata;
+
+        var islinks = context && linksUriRE.test(context.request.requestUri);
+        formatJsonLightData(obj, (metadata && metadata.properties), data, islinks);
+        return data;
+    };
+
+    var formatJsonLightMetadata = function (metadata, data) {
+        /// <summary>Formats an object's metadata into the appropriate json light annotations and saves them to data.</summary>
+        /// <param name="obj" type="Object">Object whose metadata is going to be formatted as annotations.</param>
+        /// <param name="data" type="Object">Object on which the annotations are going to be stored.</param>
+
+        if (metadata) {
+            var i, len;
+            for (i = 0, len = jsonLightSerializableMetadata.length; i < len; i++) {
+                // There is only a subset of metadata values that are interesting during update requests.
+                var name = jsonLightSerializableMetadata[i];
+                var qName = odataAnnotationPrefix + (jsonLightNameMap[name] || name);
+                formatJsonLightAnnotation(qName, null, metadata[name], data);
+            }
+        }
+    };
+
+    var formatJsonLightData = function (obj, pMetadata, data, isLinks) {
+        /// <summary>Formats an object's data into the appropriate json light values and saves them to data.</summary>
+        /// <param name="obj" type="Object">Object whose data is going to be formatted.</param>
+        /// <param name="pMetadata" type="Object">Object that contains metadata for the properties that are being formatted.</param>
+        /// <param name="data" type="Object">Object on which the formatted values are going to be stored.</param>
+        /// <param name="isLinks" type="Boolean">True if a links document is being formatted.  False otherwise.</param>
+
+        for (var key in obj) {
+            var value = obj[key];
+            if (key === "__metadata") {
+                // key is the object metadata.
+                formatJsonLightMetadata(value, data);
+            } else if (key.indexOf(".") === -1) {
+                // key is an regular property or array element.
+                if (isLinks && key === "uri") {
+                    formatJsonLightEntityLink(value, data);
+                } else {
+                    formatJsonLightProperty(key, value, pMetadata, data, isLinks);
+                }
+            } else {
+                data[key] = value;
+            }
+        }
+    };
+
+    var formatJsonLightProperty = function (name, value, pMetadata, data) {
+        /// <summary>Formats an object's value identified by name to its json light representation and saves it to data.</summary>
+        /// <param name="name" type="String">Property name.</param>
+        /// <param name="value">Property value.</param>
+        /// <param name="pMetadata" type="Object">Object that contains metadata for the property that is being formatted.</param>
+        /// <param name="data" type="Object">Object on which the formatted value is going to be stored.</param>
+
+        // Get property type from property metadata
+        var propertyMetadata = pMetadata && pMetadata[name] || { properties: undefined, type: undefined };
+        var typeName = dataItemTypeName(value, propertyMetadata);
+
+        if (isPrimitive(value) || !value) {
+            // It is a primitive value then.
+            formatJsonLightAnnotation(typeAnnotation, name, typeName, data);
+            data[name] = value;
+            return;
+        }
+
+        if (isFeed(value, typeName) || isEntry(value)) {
+            formatJsonLightInlineProperty(name, value, data);
+            return;
+        }
+
+        if (!typeName && isDeferred(value)) {
+            // It is really a deferred property.
+            formatJsonLightDeferredProperty(name, value, data);
+            return;
+        }
+
+        if (isCollection(value, typeName)) {
+            // The thing is a collection, format it as one.
+            if (getCollectionType(typeName)) {
+                formatJsonLightAnnotation(typeAnnotation, name, typeName, data);
+            }
+            formatJsonLightCollectionProperty(name, value, data);
+            return;
+        }
+
+        djsassert(isComplex(value), "formatJsonLightProperty - Value is not a complex type value");
+
+        // Format the complex property value in a new object in data[name].
+        data[name] = {};
+        formatJsonLightAnnotation(typeAnnotation, null, typeName, data[name]);
+        formatJsonLightData(value, propertyMetadata.properties, data[name]);
+    };
+
+    var formatJsonLightEntityLink = function (value, data) {
+        /// <summary>Formats an entity link in a $links document and saves it into data.</summary>
+        /// <param name="value" type="String">Entity link value.</summary>
+        /// <param name="data" type="Object">Object on which the formatted value is going to be stored.</param>
+        data.url = value;
+    };
+
+    var formatJsonLightDeferredProperty = function (name, value, data) {
+        /// <summary>Formats the object value's identified by name as an odata.navigalinkurl annotation and saves it to data.</summary>
+        /// <param name="name" type="String">Name of the deferred property to be formatted.</param>
+        /// <param name="value" type="Object">Deferred property value to be formatted.</param>
+        /// <param name="data" type="Object">Object on which the formatted value is going to be stored.</param>
+
+        formatJsonLightAnnotation(navUrlAnnotation, name, value.__deferred.uri, data);
+    };
+
+    var formatJsonLightCollectionProperty = function (name, value, data) {
+        /// <summary>Formats a collection property in obj identified by name as a json light collection property and saves it to data.</summary>
+        /// <param name="name" type="String">Name of the collection property to be formatted.</param>
+        /// <param name="value" type="Object">Collection property value to be formatted.</param>
+        /// <param name="data" type="Object">Object on which the formatted value is going to be stored.</param>
+
+        data[name] = [];
+        var items = isArray(value) ? value : value.results;
+        formatJsonLightData(items, null, data[name]);
+    };
+
+    var formatJsonLightInlineProperty = function (name, value, data) {
+        /// <summary>Formats an inline feed or entry property in obj identified by name as a json light value and saves it to data.</summary>
+        /// <param name="name" type="String">Name of the inline feed or entry property to be formatted.</param>
+        /// <param name="value" type="Object or Array">Value of the inline feed or entry property.</param>
+        /// <param name="data" type="Object">Object on which the formatted value is going to be stored.</param>
+
+        if (isFeed(value)) {
+            data[name] = [];
+            // Format each of the inline feed entries
+            var entries = isArray(value) ? value : value.results;
+            var i, len;
+            for (i = 0, len = entries.length; i < len; i++) {
+                formatJsonLightInlineEntry(name, entries[i], true, data);
+            }
+            return;
+        }
+        formatJsonLightInlineEntry(name, value, false, data);
+    };
+
+    var formatJsonLightInlineEntry = function (name, value, inFeed, data) {
+        /// <summary>Formats an inline entry value in the property identified by name as a json light value and saves it to data.</summary>
+        /// <param name="name" type="String">Name of the inline feed or entry property that owns the entry formatted.</param>
+        /// <param name="value" type="Object">Inline entry value to be formatted.</param>
+        /// <param name="inFeed" type="Boolean">True if the entry is in an inline feed; false otherwise.
+        /// <param name="data" type="Object">Object on which the formatted value is going to be stored.</param>
+
+        // This might be a bind instead of a deep insert.
+        var uri = value.__metadata && value.__metadata.uri;
+        if (uri) {
+            formatJsonLightBinding(name, uri, inFeed, data);
+            return;
+        }
+
+        var entry = formatJsonLight(value);
+        if (inFeed) {
+            data[name].push(entry);
+            return;
+        }
+        data[name] = entry;
+    };
+
+    var formatJsonLightBinding = function (name, uri, inFeed, data) {
+        /// <summary>Formats an entry binding in the inline property in obj identified by name as an odata.bind annotation and saves it to data.</summary>
+        /// <param name="name" type="String">Name of the inline property that has the binding to be formated.</param>
+        /// <param name="uri" type="String">Uri to the bound entry.</param>
+        /// <param name="inFeed" type="Boolean">True if the binding is in an inline feed; false otherwise.
+        /// <param name="data" type="Object">Object on which the formatted value is going to be stored.</param>
+
+        var bindingName = name + bindAnnotation;
+        if (inFeed) {
+            // The binding is inside an inline feed, so merge it with whatever other bindings already exist in data.
+            data[bindingName] = data[bindingName] || [];
+            data[bindingName].push(uri);
+            return;
+        }
+        // The binding is on an inline entry; it can be safely overwritten.
+        data[bindingName] = uri;
+    };
+
+    var formatJsonLightAnnotation = function (qName, target, value, data) {
+        /// <summary>Formats a value as a json light annotation and stores it in data</summary>
+        /// <param name="qName" type="String">Qualified name of the annotation.</param>
+        /// <param name="target" type="String">Name of the property that the metadata value targets.</param>
+        /// <param name="value">Annotation value.</param>
+        /// <param name="data" type="Object">Object on which the annotation is going to be stored.</param>
+
+        if (value !== undefined) {
+            if(target) {
+                data[target + "@" + qName] = value;
+            }
+            else {
+                data[qName] = value;
+            }
+        }
+    };
+
+    // DATAJS INTERNAL START
+    odata.jsonLightReadPayload = jsonLightReadPayload;
+    odata.formatJsonLight = formatJsonLight;
+    // DATAJS INTERNAL END
+
+    // CONTENT END
+})(this);
+
+
+
diff --git a/JSLib/src/odata-json.js b/JSLib/src/odata-json.js
new file mode 100644
index 0000000..8f11857
--- /dev/null
+++ b/JSLib/src/odata-json.js
@@ -0,0 +1,379 @@
+// Copyright (c) Microsoft Open Technologies, Inc.  All rights reserved.
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation 
+// files (the "Software"), to deal  in the Software without restriction, including without limitation the rights  to use, copy,
+// modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the 
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR  IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// WARRANTIES OF MERCHANTABILITY,  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+// odata-json.js
+
+(function (window, undefined) {
+
+    var datajs = window.datajs || {};
+    var odata = window.OData || {};
+
+    // Imports 
+
+    var defined = datajs.defined;
+    var extend = datajs.extend;
+    var isArray = datajs.isArray;
+    var isDate = datajs.isDate;
+    var normalizeURI = datajs.normalizeURI;
+    var parseInt10 = datajs.parseInt10;
+
+    var contentType = odata.contentType;
+    var jsonLightReadPayload = odata.jsonLightReadPayload;
+    var formatDateTimeOffset = odata.formatDateTimeOffset;
+    var formatDuration = odata.formatDuration;
+    var formatJsonLight = odata.formatJsonLight;
+    var formatNumberWidth = odata.formatNumberWidth;
+    var getCanonicalTimezone = odata.getCanonicalTimezone;
+    var handler = odata.handler;
+    var isComplex = odata.isComplex;
+    var lookupComplexType = odata.lookupComplexType;
+    var lookupEntityType = odata.lookupEntityType;
+    var MAX_DATA_SERVICE_VERSION = odata.MAX_DATA_SERVICE_VERSION;
+    var maxVersion = odata.maxVersion;
+    var parseDateTime = odata.parseDateTime;
+    var parseDuration = odata.parseDuration;
+    var parseTimezone = odata.parseTimezone;
+    var payloadTypeOf = odata.payloadTypeOf;
+    var traverse = odata.traverse;
+
+    // CONTENT START
+
+    var jsonMediaType = "application/json";
+    var jsonContentType = contentType(jsonMediaType);
+
+    var jsonReadAdvertisedActionsOrFunctions = function (value) {
+        /// <summary>Reads and object containing action or function metadata and maps them into a single array of objects.</summary>
+        /// <param name="value" type="Object">Object containing action or function metadata.</param>
+        /// <returns type="Array">Array of objects containing metadata for the actions or functions specified in value.</returns>
+
+        var result = [];
+        for (var name in value) {
+            var i, len;
+            for (i = 0, len = value[name].length; i < len; i++) {
+                result.push(extend({ metadata: name }, value[name][i]));
+            }
+        }
+        return result;
+    };
+
+    var jsonApplyMetadata = function (value, metadata, dateParser, recognizeDates) {
+        /// <summary>Applies metadata coming from both the payload and the metadata object to the value.</summary>
+        /// <param name="value" type="Object">Data on which the metada is going to be applied.</param>
+        /// <param name="metadata">Metadata store; one of edmx, schema, or an array of any of them.</param>
+        /// <param name="dateParser" type="function">Function used for parsing datetime values.</param>
+        /// <param name="recognizeDates" type="Boolean">
+        ///     True if strings formatted as datetime values should be treated as datetime values. False otherwise.
+        /// </param>
+        /// <returns type="Object">Transformed data.</returns>
+
+        if (value && typeof value === "object") {
+            var dataTypeName;
+            var valueMetadata = value.__metadata;
+
+            if (valueMetadata) {
+                if (valueMetadata.actions) {
+                    valueMetadata.actions = jsonReadAdvertisedActionsOrFunctions(valueMetadata.actions);
+                }
+                if (valueMetadata.functions) {
+                    valueMetadata.functions = jsonReadAdvertisedActionsOrFunctions(valueMetadata.functions);
+                }
+                dataTypeName = valueMetadata && valueMetadata.type;
+            }
+
+            var dataType = lookupEntityType(dataTypeName, metadata) || lookupComplexType(dataTypeName, metadata);
+            var propertyValue;
+            if (dataType) {
+                var properties = dataType.property;
+                if (properties) {
+                    var i, len;
+                    for (i = 0, len = properties.length; i < len; i++) {
+                        var property = properties[i];
+                        var propertyName = property.name;
+                        propertyValue = value[propertyName];
+
+                        if (property.type === "Edm.DateTime" || property.type === "Edm.DateTimeOffset") {
+                            if (propertyValue) {
+                                propertyValue = dateParser(propertyValue);
+                                if (!propertyValue) {
+                                    throw { message: "Invalid date/time value" };
+                                }
+                                value[propertyName] = propertyValue;
+                            }
+                        } else if (property.type === "Edm.Time") {
+                            value[propertyName] = parseDuration(propertyValue);
+                        }
+                    }
+                }
+            } else if (recognizeDates) {
+                for (var name in value) {
+                    propertyValue = value[name];
+                    if (typeof propertyValue === "string") {
+                        value[name] = dateParser(propertyValue) || propertyValue;
+                    }
+                }
+            }
+        }
+        return value;
+    };
+
+    var isJsonLight = function (contentType) {
+        /// <summary>Tests where the content type indicates a json light payload.</summary>
+        /// <param name="contentType">Object with media type and properties dictionary.</param>
+        /// <returns type="Boolean">True is the content type indicates a json light payload. False otherwise.</returns>
+
+        if (contentType) {
+            var odata = contentType.properties.odata;
+            return odata === "nometadata" || odata === "minimalmetadata" || odata === "fullmetadata";
+        }
+        return false;
+    };
+
+    var normalizeServiceDocument = function (data, baseURI) {
+        /// <summary>Normalizes a JSON service document to look like an ATOM service document.</summary>
+        /// <param name="data" type="Object">Object representation of service documents as deserialized.</param>
+        /// <param name="baseURI" type="String">Base URI to resolve relative URIs.</param>
+        /// <returns type="Object">An object representation of the service document.</returns>
+        var workspace = { collections: [] };
+
+        var i, len;
+        for (i = 0, len = data.EntitySets.length; i < len; i++) {
+            var title = data.EntitySets[i];
+            var collection = {
+                title: title,
+                href: normalizeURI(title, baseURI)
+            };
+
+            workspace.collections.push(collection);
+        }
+
+        return { workspaces: [workspace] };
+    };
+
+    // The regular expression corresponds to something like this:
+    // /Date(123+60)/
+    //
+    // This first number is date ticks, the + may be a - and is optional,
+    // with the second number indicating a timezone offset in minutes.
+    //
+    // On the wire, the leading and trailing forward slashes are
+    // escaped without being required to so the chance of collisions is reduced;
+    // however, by the time we see the objects, the characters already
+    // look like regular forward slashes.
+    var jsonDateRE = /^\/Date\((-?\d+)(\+|-)?(\d+)?\)\/$/;
+
+    var minutesToOffset = function (minutes) {
+        /// <summary>Formats the given minutes into (+/-)hh:mm format.</summary>
+        /// <param name="minutes" type="Number">Number of minutes to format.</param>
+        /// <returns type="String">The minutes in (+/-)hh:mm format.</returns>
+
+        var sign;
+        if (minutes < 0) {
+            sign = "-";
+            minutes = -minutes;
+        } else {
+            sign = "+";
+        }
+
+        var hours = Math.floor(minutes / 60);
+        minutes = minutes - (60 * hours);
+
+        return sign + formatNumberWidth(hours, 2) + ":" + formatNumberWidth(minutes, 2);
+    };
+
+    var parseJsonDateString = function (value) {
+        /// <summary>Parses the JSON Date representation into a Date object.</summary>
+        /// <param name="value" type="String">String value.</param>
+        /// <returns type="Date">A Date object if the value matches one; falsy otherwise.</returns>
+
+        var arr = value && jsonDateRE.exec(value);
+        if (arr) {
+            // 0 - complete results; 1 - ticks; 2 - sign; 3 - minutes
+            var result = new Date(parseInt10(arr[1]));
+            if (arr[2]) {
+                var mins = parseInt10(arr[3]);
+                if (arr[2] === "-") {
+                    mins = -mins;
+                }
+
+                // The offset is reversed to get back the UTC date, which is
+                // what the API will eventually have.
+                var current = result.getUTCMinutes();
+                result.setUTCMinutes(current - mins);
+                result.__edmType = "Edm.DateTimeOffset";
+                result.__offset = minutesToOffset(mins);
+            }
+            if (!isNaN(result.valueOf())) {
+                return result;
+            }
+        }
+
+        // Allow undefined to be returned.
+    };
+
+    // Some JSON implementations cannot produce the character sequence \/
+    // which is needed to format DateTime and DateTimeOffset into the
+    // JSON string representation defined by the OData protocol.
+    // See the history of this file for a candidate implementation of
+    // a 'formatJsonDateString' function.
+
+    var jsonParser = function (handler, text, context) {
+        /// <summary>Parses a JSON OData payload.</summary>
+        /// <param name="handler">This handler.</param>
+        /// <param name="text">Payload text (this parser also handles pre-parsed objects).</param>
+        /// <param name="context" type="Object">Object with parsing context.</param>
+        /// <returns>An object representation of the OData payload.</returns>
+
+        var recognizeDates = defined(context.recognizeDates, handler.recognizeDates);
+        var inferJsonLightFeedAsObject = defined(context.inferJsonLightFeedAsObject, handler.inferJsonLightFeedAsObject);
+        var model = context.metadata;
+        var dataServiceVersion = context.dataServiceVersion;
+        var dateParser = parseJsonDateString;
+        var json = (typeof text === "string") ? window.JSON.parse(text) : text;
+
+        if ((maxVersion("3.0", dataServiceVersion) === dataServiceVersion)) {
+            if (isJsonLight(context.contentType)) {
+                return jsonLightReadPayload(json, model, recognizeDates, inferJsonLightFeedAsObject, context.contentType.properties.odata);
+            }
+            dateParser = parseDateTime;
+        }
+
+        json = traverse(json.d, function (key, value) {
+            return jsonApplyMetadata(value, model, dateParser, recognizeDates);
+        });
+
+        json = jsonUpdateDataFromVersion(json, context.dataServiceVersion);
+        return jsonNormalizeData(json, context.response.requestUri);
+    };
+
+    var jsonToString = function (data) {
+        /// <summary>Converts the data into a JSON string.</summary>
+        /// <param name="data">Data to serialize.</param>
+        /// <returns type="String">The JSON string representation of data.</returns>
+
+        var result; // = undefined;
+        // Save the current date.toJSON function
+        var dateToJSON = Date.prototype.toJSON;
+        try {
+            // Set our own date.toJSON function
+            Date.prototype.toJSON = function () {
+                return formatDateTimeOffset(this);
+            };
+            result = window.JSON.stringify(data, jsonReplacer);
+        } finally {
+            // Restore the original toJSON function
+            Date.prototype.toJSON = dateToJSON;
+        }
+        return result;
+    };
+
+    var jsonSerializer = function (handler, data, context) {
+        /// <summary>Serializes the data by returning its string representation.</summary>
+        /// <param name="handler">This handler.</param>
+        /// <param name="data">Data to serialize.</param>
+        /// <param name="context" type="Object">Object with serialization context.</param>
+        /// <returns type="String">The string representation of data.</returns>
+
+        var dataServiceVersion = context.dataServiceVersion || "1.0";
+        var useJsonLight = defined(context.useJsonLight, handler.useJsonLight);
+        var cType = context.contentType = context.contentType || jsonContentType;
+
+        if (cType && cType.mediaType === jsonContentType.mediaType) {
+            var json = data;
+            if (useJsonLight || isJsonLight(cType)) {
+                context.dataServiceVersion = maxVersion(dataServiceVersion, "3.0");
+                json = formatJsonLight(data, context);
+                return jsonToString(json);
+            }
+            if (maxVersion("3.0", dataServiceVersion) === dataServiceVersion) {
+                cType.properties.odata = "verbose";
+                context.contentType = cType;
+            }
+            return jsonToString(json);
+        }
+        return undefined;
+    };
+
+    var jsonReplacer = function (_, value) {
+        /// <summary>JSON replacer function for converting a value to its JSON representation.</summary>
+        /// <param value type="Object">Value to convert.</param>
+        /// <returns type="String">JSON representation of the input value.</returns>
+        /// <remarks>
+        ///   This method is used during JSON serialization and invoked only by the JSON.stringify function.
+        ///   It should never be called directly.
+        /// </remarks>
+
+        if (value && value.__edmType === "Edm.Time") {
+            return formatDuration(value);
+        } else {
+            return value;
+        }
+    };
+
+    var jsonNormalizeData = function (data, baseURI) {
+        /// <summary>
+        /// Normalizes the specified data into an intermediate representation.
+        /// like the latest supported version.
+        /// </summary>
+        /// <param name="data" optional="false">Data to update.</param>
+        /// <param name="baseURI" optional="false">URI to use as the base for normalizing references.</param>
+
+        var isSvcDoc = isComplex(data) && !data.__metadata && isArray(data.EntitySets);
+        return isSvcDoc ? normalizeServiceDocument(data, baseURI) : data;
+    };
+
+    var jsonUpdateDataFromVersion = function (data, dataVersion) {
+        /// <summary>
+        /// Updates the specified data in the specified version to look
+        /// like the latest supported version.
+        /// </summary>
+        /// <param name="data" optional="false">Data to update.</param>
+        /// <param name="dataVersion" optional="true" type="String">Version the data is in (possibly unknown).</param>
+
+        // Strip the trailing comma if there.
+        if (dataVersion && dataVersion.lastIndexOf(";") === dataVersion.length - 1) {
+            dataVersion = dataVersion.substr(0, dataVersion.length - 1);
+        }
+
+        if (!dataVersion || dataVersion === "1.0") {
+            if (isArray(data)) {
+                data = { results: data };
+            }
+        }
+
+        return data;
+    };
+
+    var jsonHandler = handler(jsonParser, jsonSerializer, jsonMediaType, MAX_DATA_SERVICE_VERSION);
+    jsonHandler.recognizeDates = false;
+    jsonHandler.useJsonLight = false;
+    jsonHandler.inferJsonLightFeedAsObject = false;
+
+    odata.jsonHandler = jsonHandler;
+
+
+
+    // DATAJS INTERNAL START
+    odata.jsonParser = jsonParser;
+    odata.jsonSerializer = jsonSerializer;
+    odata.jsonNormalizeData = jsonNormalizeData;
+    odata.jsonUpdateDataFromVersion = jsonUpdateDataFromVersion;
+    odata.normalizeServiceDocument = normalizeServiceDocument;
+    odata.parseJsonDateString = parseJsonDateString;
+    // DATAJS INTERNAL END
+
+    // CONTENT END
+})(this);
+
+
+
+
diff --git a/JSLib/src/odata-metadata.js b/JSLib/src/odata-metadata.js
new file mode 100644
index 0000000..a7ca00a
--- /dev/null
+++ b/JSLib/src/odata-metadata.js
@@ -0,0 +1,500 @@
+// Copyright (c) Microsoft Open Technologies, Inc.  All rights reserved.
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation 
+// files (the "Software"), to deal  in the Software without restriction, including without limitation the rights  to use, copy,
+// modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the 
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR  IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// WARRANTIES OF MERCHANTABILITY,  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+// odata-metadata.js
+
+(function (window, undefined) {
+
+    var datajs = window.datajs || {};
+    var odata = window.OData || {};
+
+    // imports 
+
+    var contains = datajs.contains;
+    var normalizeURI = datajs.normalizeURI;
+    var xmlAttributes = datajs.xmlAttributes;
+    var xmlChildElements = datajs.xmlChildElements;
+    var xmlFirstChildElement = datajs.xmlFirstChildElement;
+    var xmlInnerText = datajs.xmlInnerText;
+    var xmlLocalName = datajs.xmlLocalName;
+    var xmlNamespaceURI = datajs.xmlNamespaceURI;
+    var xmlNS = datajs.xmlNS;
+    var xmlnsNS = datajs.xmlnsNS;
+    var xmlParse = datajs.xmlParse;
+
+    var createAttributeExtension = odata.createAttributeExtension;
+    var createElementExtension = odata.createElementExtension;
+    var edmxNs = odata.edmxNs;
+    var edmNs1 = odata.edmNs1;
+    var edmNs1_1 = odata.edmNs1_1;
+    var edmNs1_2 = odata.edmNs1_2;
+    var edmNs2a = odata.edmNs2a;
+    var edmNs2b = odata.edmNs2b;
+    var edmNs3 = odata.edmNs3;
+    var handler = odata.handler;
+    var MAX_DATA_SERVICE_VERSION = odata.MAX_DATA_SERVICE_VERSION;
+    var odataMetaXmlNs = odata.odataMetaXmlNs;
+
+
+    var xmlMediaType = "application/xml";
+
+    // CONTENT START
+
+    var schemaElement = function (attributes, elements, text, ns) {
+        /// <summary>Creates an object that describes an element in an schema.</summary>
+        /// <param name="attributes" type="Array">List containing the names of the attributes allowed for this element.</param>
+        /// <param name="elements" type="Array">List containing the names of the child elements allowed for this element.</param>
+        /// <param name="text" type="Boolean">Flag indicating if the element's text value is of interest or not.</param>
+        /// <param name="ns" type="String">Namespace to which the element belongs to.</param>
+        /// <remarks>
+        ///    If a child element name ends with * then it is understood by the schema that that child element can appear 0 or more times.
+        /// </remarks>
+        /// <returns type="Object">Object with attributes, elements, text, and ns fields.</returns>
+
+        return {
+            attributes: attributes,
+            elements: elements,
+            text: text || false,
+            ns: ns
+        };
+    };
+
+    // It's assumed that all elements may have Documentation children and Annotation elements.
+    // See http://msdn.microsoft.com/en-us/library/bb399292.aspx for a CSDL reference.
+    var schema = {
+        elements: {
+            Annotations: schemaElement(
+            /*attributes*/["Target", "Qualifier"],
+            /*elements*/["TypeAnnotation*", "ValueAnnotation*"]
+            ),
+            Association: schemaElement(
+            /*attributes*/["Name"],
+            /*elements*/["End*", "ReferentialConstraint", "TypeAnnotation*", "ValueAnnotation*"]
+            ),
+            AssociationSet: schemaElement(
+            /*attributes*/["Name", "Association"],
+            /*elements*/["End*", "TypeAnnotation*", "ValueAnnotation*"]
+            ),
+            Binary: schemaElement(
+            /*attributes*/null,
+            /*elements*/null,
+            /*text*/true
+            ),
+            Bool: schemaElement(
+            /*attributes*/null,
+            /*elements*/null,
+            /*text*/true
+            ),
+            Collection: schemaElement(
+            /*attributes*/null,
+            /*elements*/["String*", "Int*", "Float*", "Decimal*", "Bool*", "DateTime*", "DateTimeOffset*", "Guid*", "Binary*", "Time*", "Collection*", "Record*"]
+            ),
+            CollectionType: schemaElement(
+            /*attributes*/["ElementType", "Nullable", "DefaultValue", "MaxLength", "FixedLength", "Precision", "Scale", "Unicode", "Collation", "SRID"],
+            /*elements*/["CollectionType", "ReferenceType", "RowType", "TypeRef"]
+            ),
+            ComplexType: schemaElement(
+            /*attributes*/["Name", "BaseType", "Abstract"],
+            /*elements*/["Property*", "TypeAnnotation*", "ValueAnnotation*"]
+            ),
+            DateTime: schemaElement(
+            /*attributes*/null,
+            /*elements*/null,
+            /*text*/true
+            ),
+            DateTimeOffset: schemaElement(
+            /*attributes*/null,
+            /*elements*/null,
+            /*text*/true
+            ),
+            Decimal: schemaElement(
+            /*attributes*/null,
+            /*elements*/null,
+            /*text*/true
+            ),
+            DefiningExpression: schemaElement(
+            /*attributes*/null,
+            /*elements*/null,
+            /*text*/true
+            ),
+            Dependent: schemaElement(
+            /*attributes*/["Role"],
+            /*elements*/["PropertyRef*"]
+            ),
+            Documentation: schemaElement(
+            /*attributes*/null,
+            /*elements*/null,
+            /*text*/true
+            ),
+            End: schemaElement(
+            /*attributes*/["Type", "Role", "Multiplicity", "EntitySet"],
+            /*elements*/["OnDelete"]
+            ),
+            EntityContainer: schemaElement(
+            /*attributes*/["Name", "Extends"],
+            /*elements*/["EntitySet*", "AssociationSet*", "FunctionImport*", "TypeAnnotation*", "ValueAnnotation*"]
+            ),
+            EntitySet: schemaElement(
+            /*attributes*/["Name", "EntityType"],
+            /*elements*/["TypeAnnotation*", "ValueAnnotation*"]
+            ),
+            EntityType: schemaElement(
+            /*attributes*/["Name", "BaseType", "Abstract", "OpenType"],
+            /*elements*/["Key", "Property*", "NavigationProperty*", "TypeAnnotation*", "ValueAnnotation*"]
+            ),
+            EnumType: schemaElement(
+            /*attributes*/["Name", "UnderlyingType", "IsFlags"],
+            /*elements*/["Member*"]
+            ),
+            Float: schemaElement(
+            /*attributes*/null,
+            /*elements*/null,
+            /*text*/true
+            ),
+            Function: schemaElement(
+            /*attributes*/["Name", "ReturnType"],
+            /*elements*/["Parameter*", "DefiningExpression", "ReturnType", "TypeAnnotation*", "ValueAnnotation*"]
+            ),
+            FunctionImport: schemaElement(
+            /*attributes*/["Name", "ReturnType", "EntitySet", "IsSideEffecting", "IsComposable", "IsBindable", "EntitySetPath"],
+            /*elements*/["Parameter*", "ReturnType", "TypeAnnotation*", "ValueAnnotation*"]
+            ),
+            Guid: schemaElement(
+            /*attributes*/null,
+            /*elements*/null,
+            /*text*/true
+            ),
+            Int: schemaElement(
+            /*attributes*/null,
+            /*elements*/null,
+            /*text*/true
+            ),
+            Key: schemaElement(
+            /*attributes*/null,
+            /*elements*/["PropertyRef*"]
+            ),
+            LabeledElement: schemaElement(
+            /*attributes*/["Name"],
+            /*elements*/["Path", "String", "Int", "Float", "Decimal", "Bool", "DateTime", "DateTimeOffset", "Guid", "Binary", "Time", "Collection", "Record", "LabeledElement", "Null"]
+            ),
+            Member: schemaElement(
+            /*attributes*/["Name", "Value"]
+            ),
+            NavigationProperty: schemaElement(
+            /*attributes*/["Name", "Relationship", "ToRole", "FromRole", "ContainsTarget"],
+            /*elements*/["TypeAnnotation*", "ValueAnnotation*"]
+            ),
+            Null: schemaElement(
+            /*attributes*/null,
+            /*elements*/null
+            ),
+            OnDelete: schemaElement(
+            /*attributes*/["Action"]
+            ),
+            Path: schemaElement(
+            /*attributes*/null,
+            /*elements*/null,
+            /*text*/true
+            ),
+            Parameter: schemaElement(
+            /*attributes*/["Name", "Type", "Mode", "Nullable", "DefaultValue", "MaxLength", "FixedLength", "Precision", "Scale", "Unicode", "Collation", "ConcurrencyMode", "SRID"],
+            /*elements*/["CollectionType", "ReferenceType", "RowType", "TypeRef", "TypeAnnotation*", "ValueAnnotation*"]
+            ),
+            Principal: schemaElement(
+            /*attributes*/["Role"],
+            /*elements*/["PropertyRef*"]
+            ),
+            Property: schemaElement(
+            /*attributes*/["Name", "Type", "Nullable", "DefaultValue", "MaxLength", "FixedLength", "Precision", "Scale", "Unicode", "Collation", "ConcurrencyMode", "CollectionKind", "SRID"],
+            /*elements*/["CollectionType", "ReferenceType", "RowType", "TypeAnnotation*", "ValueAnnotation*"]
+            ),
+            PropertyRef: schemaElement(
+            /*attributes*/["Name"]
+            ),
+            PropertyValue: schemaElement(
+            /*attributes*/["Property", "Path", "String", "Int", "Float", "Decimal", "Bool", "DateTime", "DateTimeOffset", "Guid", "Binary", "Time"],
+            /*Elements*/["Path", "String", "Int", "Float", "Decimal", "Bool", "DateTime", "DateTimeOffset", "Guid", "Binary", "Time", "Collection", "Record", "LabeledElement", "Null"]
+            ),
+            ReferenceType: schemaElement(
+            /*attributes*/["Type"]
+            ),
+            ReferentialConstraint: schemaElement(
+            /*attributes*/null,
+            /*elements*/["Principal", "Dependent"]
+            ),
+            ReturnType: schemaElement(
+            /*attributes*/["ReturnType", "Type", "EntitySet"],
+            /*elements*/["CollectionType", "ReferenceType", "RowType"]
+            ),
+            RowType: schemaElement(
+            /*elements*/["Property*"]
+            ),
+            String: schemaElement(
+            /*attributes*/null,
+            /*elements*/null,
+            /*text*/true
+            ),
+            Schema: schemaElement(
+            /*attributes*/["Namespace", "Alias"],
+            /*elements*/["Using*", "EntityContainer*", "EntityType*", "Association*", "ComplexType*", "Function*", "ValueTerm*", "Annotations*"]
+            ),
+            Time: schemaElement(
+            /*attributes*/null,
+            /*elements*/null,
+            /*text*/true
+            ),
+            TypeAnnotation: schemaElement(
+            /*attributes*/["Term", "Qualifier"],
+            /*elements*/["PropertyValue*"]
+            ),
+            TypeRef: schemaElement(
+            /*attributes*/["Type", "Nullable", "DefaultValue", "MaxLength", "FixedLength", "Precision", "Scale", "Unicode", "Collation", "SRID"]
+            ),
+            Using: schemaElement(
+            /*attributes*/["Namespace", "Alias"]
+            ),
+            ValueAnnotation: schemaElement(
+            /*attributes*/["Term", "Qualifier", "Path", "String", "Int", "Float", "Decimal", "Bool", "DateTime", "DateTimeOffset", "Guid", "Binary", "Time"],
+            /*Elements*/["Path", "String", "Int", "Float", "Decimal", "Bool", "DateTime", "DateTimeOffset", "Guid", "Binary", "Time", "Collection", "Record", "LabeledElement", "Null"]
+            ),
+            ValueTerm: schemaElement(
+            /*attributes*/["Name", "Type"],
+            /*elements*/["TypeAnnotation*", "ValueAnnotation*"]
+            ),
+
+            // See http://msdn.microsoft.com/en-us/library/dd541238(v=prot.10) for an EDMX reference.
+            Edmx: schemaElement(
+            /*attributes*/["Version"],
+            /*elements*/["DataServices", "Reference*", "AnnotationsReference*"],
+            /*text*/false,
+            /*ns*/edmxNs
+            ),
+            DataServices: schemaElement(
+            /*attributes*/null,
+            /*elements*/["Schema*"],
+            /*text*/false,
+            /*ns*/edmxNs
+            )
+        }
+    };
+
+    // See http://msdn.microsoft.com/en-us/library/ee373839.aspx for a feed customization reference.
+    var customizationAttributes = ["m:FC_ContentKind", "m:FC_KeepInContent", "m:FC_NsPrefix", "m:FC_NsUri", "m:FC_SourcePath", "m:FC_TargetPath"];
+    schema.elements.Property.attributes = schema.elements.Property.attributes.concat(customizationAttributes);
+    schema.elements.EntityType.attributes = schema.elements.EntityType.attributes.concat(customizationAttributes);
+
+    // See http://msdn.microsoft.com/en-us/library/dd541284(PROT.10).aspx for an EDMX reference.
+    schema.elements.Edmx = { attributes: ["Version"], elements: ["DataServices"], ns: edmxNs };
+    schema.elements.DataServices = { elements: ["Schema*"], ns: edmxNs };
+
+    // See http://msdn.microsoft.com/en-us/library/dd541233(v=PROT.10) for Conceptual Schema Definition Language Document for Data Services.
+    schema.elements.EntityContainer.attributes.push("m:IsDefaultEntityContainer");
+    schema.elements.Property.attributes.push("m:MimeType");
+    schema.elements.FunctionImport.attributes.push("m:HttpMethod");
+    schema.elements.FunctionImport.attributes.push("m:IsAlwaysBindable");
+    schema.elements.EntityType.attributes.push("m:HasStream");
+    schema.elements.DataServices.attributes = ["m:DataServiceVersion", "m:MaxDataServiceVersion"];
+
+    var scriptCase = function (text) {
+        /// <summary>Converts a Pascal-case identifier into a camel-case identifier.</summary>
+        /// <param name="text" type="String">Text to convert.</param>
+        /// <returns type="String">Converted text.</returns>
+        /// <remarks>If the text starts with multiple uppercase characters, it is left as-is.</remarks>
+
+        if (!text) {
+            return text;
+        }
+
+        if (text.length > 1) {
+            var firstTwo = text.substr(0, 2);
+            if (firstTwo === firstTwo.toUpperCase()) {
+                return text;
+            }
+
+            return text.charAt(0).toLowerCase() + text.substr(1);
+        }
+
+        return text.charAt(0).toLowerCase();
+    };
+
+    var getChildSchema = function (parentSchema, candidateName) {
+        /// <summary>Gets the schema node for the specified element.</summary>
+        /// <param name="parentSchema" type="Object">Schema of the parent XML node of 'element'.</param>
+        /// <param name="candidateName">XML element name to consider.</param>
+        /// <returns type="Object">The schema that describes the specified element; null if not found.</returns>
+
+        if (candidateName === "Documentation") {
+            return { isArray: true, propertyName: "documentation" };
+        }
+
+        var elements = parentSchema.elements;
+        if (!elements) {
+            return null;
+        }
+
+        var i, len;
+        for (i = 0, len = elements.length; i < len; i++) {
+            var elementName = elements[i];
+            var multipleElements = false;
+            if (elementName.charAt(elementName.length - 1) === "*") {
+                multipleElements = true;
+                elementName = elementName.substr(0, elementName.length - 1);
+            }
+
+            if (candidateName === elementName) {
+                var propertyName = scriptCase(elementName);
+                return { isArray: multipleElements, propertyName: propertyName };
+            }
+        }
+
+        return null;
+    };
+
+    // This regular expression is used to detect a feed customization element
+    // after we've normalized it into the 'm' prefix. It starts with m:FC_,
+    // followed by other characters, and ends with _ and a number.
+    // The captures are 0 - whole string, 1 - name as it appears in internal table.
+    var isFeedCustomizationNameRE = /^(m:FC_.*)_[0-9]+$/;
+
+    var isEdmNamespace = function (nsURI) {
+        /// <summary>Checks whether the specifies namespace URI is one of the known CSDL namespace URIs.</summary>
+        /// <param name="nsURI" type="String">Namespace URI to check.</param>
+        /// <returns type="Boolean">true if nsURI is a known CSDL namespace; false otherwise.</returns>
+
+        return nsURI === edmNs1 ||
+               nsURI === edmNs1_1 ||
+               nsURI === edmNs1_2 ||
+               nsURI === edmNs2a ||
+               nsURI === edmNs2b ||
+               nsURI === edmNs3;
+    };
+
+    var parseConceptualModelElement = function (element) {
+        /// <summary>Parses a CSDL document.</summary>
+        /// <param name="element">DOM element to parse.</param>
+        /// <returns type="Object">An object describing the parsed element.</returns>
+
+        var localName = xmlLocalName(element);
+        var nsURI = xmlNamespaceURI(element);
+        var elementSchema = schema.elements[localName];
+        if (!elementSchema) {
+            return null;
+        }
+
+        if (elementSchema.ns) {
+            if (nsURI !== elementSchema.ns) {
+                return null;
+            }
+        } else if (!isEdmNamespace(nsURI)) {
+            return null;
+        }
+
+        var item = {};
+        var extensions = [];
+        var attributes = elementSchema.attributes || [];
+        xmlAttributes(element, function (attribute) {
+
+            var localName = xmlLocalName(attribute);
+            var nsURI = xmlNamespaceURI(attribute);
+            var value = attribute.value;
+
+            // Don't do anything with xmlns attributes.
+            if (nsURI === xmlnsNS) {
+                return;
+            }
+
+            // Currently, only m: for metadata is supported as a prefix in the internal schema table,
+            // un-prefixed element names imply one a CSDL element.
+            var schemaName = null;
+            var handled = false;
+            if (isEdmNamespace(nsURI) || nsURI === null) {
+                schemaName = "";
+            } else if (nsURI === odataMetaXmlNs) {
+                schemaName = "m:";
+            }
+
+            if (schemaName !== null) {
+                schemaName += localName;
+
+                // Feed customizations for complex types have additional
+                // attributes with a suffixed counter starting at '1', so
+                // take that into account when doing the lookup.
+                var match = isFeedCustomizationNameRE.exec(schemaName);
+                if (match) {
+                    schemaName = match[1];
+                }
+
+                if (contains(attributes, schemaName)) {
+                    handled = true;
+                    item[scriptCase(localName)] = value;
+                }
+            }
+
+            if (!handled) {
+                extensions.push(createAttributeExtension(attribute));
+            }
+        });
+
+        xmlChildElements(element, function (child) {
+            var localName = xmlLocalName(child);
+            var childSchema = getChildSchema(elementSchema, localName);
+            if (childSchema) {
+                if (childSchema.isArray) {
+                    var arr = item[childSchema.propertyName];
+                    if (!arr) {
+                        arr = [];
+                        item[childSchema.propertyName] = arr;
+                    }
+                    arr.push(parseConceptualModelElement(child));
+                } else {
+                    item[childSchema.propertyName] = parseConceptualModelElement(child);
+                }
+            } else {
+                extensions.push(createElementExtension(child));
+            }
+        });
+
+        if (elementSchema.text) {
+            item.text = xmlInnerText(element);
+        }
+
+        if (extensions.length) {
+            item.extensions = extensions;
+        }
+
+        return item;
+    };
+
+    var metadataParser = function (handler, text) {
+        /// <summary>Parses a metadata document.</summary>
+        /// <param name="handler">This handler.</param>
+        /// <param name="text" type="String">Metadata text.</param>
+        /// <returns>An object representation of the conceptual model.</returns>
+
+        var doc = xmlParse(text);
+        var root = xmlFirstChildElement(doc);
+        return parseConceptualModelElement(root) || undefined;
+    };
+
+    odata.metadataHandler = handler(metadataParser, null, xmlMediaType, MAX_DATA_SERVICE_VERSION);
+
+    // DATAJS INTERNAL START
+    odata.schema = schema;
+    odata.scriptCase = scriptCase;
+    odata.getChildSchema = getChildSchema;
+    odata.parseConceptualModelElement = parseConceptualModelElement;
+    odata.metadataParser = metadataParser;
+    // DATAJS INTERNAL END
+
+    // CONTENT END
+})(this);
\ No newline at end of file
diff --git a/JSLib/src/odata-net.js b/JSLib/src/odata-net.js
new file mode 100644
index 0000000..ad9b8dd
--- /dev/null
+++ b/JSLib/src/odata-net.js
@@ -0,0 +1,330 @@
+// Copyright (c) Microsoft Open Technologies, Inc.  All rights reserved.
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation 
+// files (the "Software"), to deal  in the Software without restriction, including without limitation the rights  to use, copy,
+// modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the 
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR  IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// WARRANTIES OF MERCHANTABILITY,  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+// odata-net.js
+
+(function (window, undefined) {
+
+    var datajs = window.datajs || {};
+    var odata = window.OData || {};
+
+    // Imports.
+
+    var defined = datajs.defined;
+    var delay = datajs.delay;
+
+    // CONTENT START
+    var ticks = 0;
+
+    var canUseJSONP = function (request) {
+        /// <summary>
+        /// Checks whether the specified request can be satisfied with a JSONP request.
+        /// </summary>
+        /// <param name="request">Request object to check.</param>
+        /// <returns type="Boolean">true if the request can be satisfied; false otherwise.</returns>
+
+        // Requests that 'degrade' without changing their meaning by going through JSONP
+        // are considered usable.
+        //
+        // We allow data to come in a different format, as the servers SHOULD honor the Accept
+        // request but may in practice return content with a different MIME type.
+        if (request.method && request.method !== "GET") {
+            return false;
+        }
+
+        return true;
+    };
+
+    var createIFrame = function (url) {
+        /// <summary>Creates an IFRAME tag for loading the JSONP script</summary>
+        /// <param name="url" type="String">The source URL of the script</param>
+        /// <returns type="HTMLElement">The IFRAME tag</returns>
+        var iframe = window.document.createElement("IFRAME");
+        iframe.style.display = "none";
+
+        var attributeEncodedUrl = url.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/\</g, "&lt;");
+        var html = "<html><head><script type=\"text/javascript\" src=\"" + attributeEncodedUrl + "\"><\/script><\/head><body><\/body><\/html>";
+
+        var body = window.document.getElementsByTagName("BODY")[0];
+        body.appendChild(iframe);
+
+        writeHtmlToIFrame(iframe, html);
+        return iframe;
+    };
+
+    var createXmlHttpRequest = function () {
+        /// <summary>Creates a XmlHttpRequest object.</summary>
+        /// <returns type="XmlHttpRequest">XmlHttpRequest object.</returns>
+        if (window.XMLHttpRequest) {
+            return new window.XMLHttpRequest();
+        }
+        var exception;
+        if (window.ActiveXObject) {
+            try {
+                return new window.ActiveXObject("Msxml2.XMLHTTP.6.0");
+            } catch (_) {
+                try {
+                    return new window.ActiveXObject("Msxml2.XMLHTTP.3.0");
+                } catch (e) {
+                    exception = e;
+                }
+            }
+        } else {
+            exception = { message: "XMLHttpRequest not supported" };
+        }
+        throw exception;
+    };
+
+    var isAbsoluteUrl = function (url) {
+        /// <summary>Checks whether the specified URL is an absolute URL.</summary>
+        /// <param name="url" type="String">URL to check.</param>
+        /// <returns type="Boolean">true if the url is an absolute URL; false otherwise.</returns>
+
+        return url.indexOf("http://") === 0 ||
+            url.indexOf("https://") === 0 ||
+            url.indexOf("file://") === 0;
+    };
+
+    var isLocalUrl = function (url) {
+        /// <summary>Checks whether the specified URL is local to the current context.</summary>
+        /// <param name="url" type="String">URL to check.</param>
+        /// <returns type="Boolean">true if the url is a local URL; false otherwise.</returns>
+
+        if (!isAbsoluteUrl(url)) {
+            return true;
+        }
+
+        // URL-embedded username and password will not be recognized as same-origin URLs.
+        var location = window.location;
+        var locationDomain = location.protocol + "//" + location.host + "/";
+        return (url.indexOf(locationDomain) === 0);
+    };
+
+    var removeCallback = function (name, tick) {
+        /// <summary>Removes a callback used for a JSONP request.</summary>
+        /// <param name="name" type="String">Function name to remove.</param>
+        /// <param name="tick" type="Number">Tick count used on the callback.</param>
+        try {
+            delete window[name];
+        } catch (err) {
+            window[name] = undefined;
+            if (tick === ticks - 1) {
+                ticks -= 1;
+            }
+        }
+    };
+
+    var removeIFrame = function (iframe) {
+        /// <summary>Removes an iframe.</summary>
+        /// <param name="iframe" type="Object">The iframe to remove.</param>
+        /// <returns type="Object">Null value to be assigned to iframe reference.</returns>
+        if (iframe) {
+            writeHtmlToIFrame(iframe, "");
+            iframe.parentNode.removeChild(iframe);
+        }
+
+        return null;
+    };
+
+    var readResponseHeaders = function (xhr, headers) {
+        /// <summary>Reads response headers into array.</summary>
+        /// <param name="xhr" type="XMLHttpRequest">HTTP request with response available.</param>
+        /// <param name="headers" type="Array">Target array to fill with name/value pairs.</param>
+
+        var responseHeaders = xhr.getAllResponseHeaders().split(/\r?\n/);
+        var i, len;
+        for (i = 0, len = responseHeaders.length; i < len; i++) {
+            if (responseHeaders[i]) {
+                var header = responseHeaders[i].split(": ");
+                headers[header[0]] = header[1];
+            }
+        }
+    };
+
+    var writeHtmlToIFrame = function (iframe, html) {
+        /// <summary>Writes HTML to an IFRAME document.</summary>
+        /// <param name="iframe" type="HTMLElement">The IFRAME element to write to.</param>
+        /// <param name="html" type="String">The HTML to write.</param>
+        var frameDocument = (iframe.contentWindow) ? iframe.contentWindow.document : iframe.contentDocument.document;
+        frameDocument.open();
+        frameDocument.write(html);
+        frameDocument.close();
+    };
+
+    odata.defaultHttpClient = {
+        callbackParameterName: "$callback",
+
+        formatQueryString: "$format=json",
+
+        enableJsonpCallback: false,
+
+        request: function (request, success, error) {
+            /// <summary>Performs a network request.</summary>
+            /// <param name="request" type="Object">Request description.</request>
+            /// <param name="success" type="Function">Success callback with the response object.</param>
+            /// <param name="error" type="Function">Error callback with an error object.</param>
+            /// <returns type="Object">Object with an 'abort' method for the operation.</returns>
+
+            var result = {};
+            var xhr = null;
+            var done = false;
+            var iframe;
+
+            result.abort = function () {
+                iframe = removeIFrame(iframe);
+                if (done) {
+                    return;
+                }
+
+                done = true;
+                if (xhr) {
+                    xhr.abort();
+                    xhr = null;
+                }
+
+                error({ message: "Request aborted" });
+            };
+
+            var handleTimeout = function () {
+                iframe = removeIFrame(iframe);
+                if (!done) {
+                    done = true;
+                    xhr = null;
+                    error({ message: "Request timed out" });
+                }
+            };
+
+            var name;
+            var url = request.requestUri;
+            var enableJsonpCallback = defined(request.enableJsonpCallback, this.enableJsonpCallback);
+            var callbackParameterName = defined(request.callbackParameterName, this.callbackParameterName);
+            var formatQueryString = defined(request.formatQueryString, this.formatQueryString);
+            if (!enableJsonpCallback || isLocalUrl(url)) {
+
+                xhr = createXmlHttpRequest();
+                xhr.onreadystatechange = function () {
+                    if (done || xhr === null || xhr.readyState !== 4) {
+                        return;
+                    }
+
+                    // Workaround for XHR behavior on IE.
+                    var statusText = xhr.statusText;
+                    var statusCode = xhr.status;
+                    if (statusCode === 1223) {
+                        statusCode = 204;
+                        statusText = "No Content";
+                    }
+
+                    var headers = [];
+                    readResponseHeaders(xhr, headers);
+
+                    var response = { requestUri: url, statusCode: statusCode, statusText: statusText, headers: headers, body: xhr.responseText };
+
+                    done = true;
+                    xhr = null;
+                    if (statusCode >= 200 && statusCode <= 299) {
+                        success(response);
+                    } else {
+                        error({ message: "HTTP request failed", request: request, response: response });
+                    }
+                };
+
+                xhr.open(request.method || "GET", url, true, request.user, request.password);
+
+                // Set the name/value pairs.
+                if (request.headers) {
+                    for (name in request.headers) {
+                        xhr.setRequestHeader(name, request.headers[name]);
+                    }
+                }
+
+                // Set the timeout if available.
+                if (request.timeoutMS) {
+                    xhr.timeout = request.timeoutMS;
+                    xhr.ontimeout = handleTimeout;
+                }
+
+                xhr.send(request.body);
+            } else {
+                if (!canUseJSONP(request)) {
+                    throw { message: "Request is not local and cannot be done through JSONP." };
+                }
+
+                var tick = ticks;
+                ticks += 1;
+                var tickText = tick.toString();
+                var succeeded = false;
+                var timeoutId;
+                name = "handleJSONP_" + tickText;
+                window[name] = function (data) {
+                    iframe = removeIFrame(iframe);
+                    if (!done) {
+                        succeeded = true;
+                        window.clearTimeout(timeoutId);
+                        removeCallback(name, tick);
+
+                        // Workaround for IE8 and IE10 below where trying to access data.constructor after the IFRAME has been removed
+                        // throws an "unknown exception"
+                        if (window.ActiveXObject) {
+                            data = window.JSON.parse(window.JSON.stringify(data));
+                        }
+
+
+                        var headers;
+                        // Adding dataServiceVersion in case of json light ( data.d doesn't exist )
+                        if (data.d === undefined) {
+                            headers = { "Content-Type": "application/json;odata=minimalmetadata", dataServiceVersion: "3.0" };
+                        } else {
+                            headers = { "Content-Type": "application/json" };
+                        }
+                        // Call the success callback in the context of the parent window, instead of the IFRAME
+                        delay(function () {
+                            removeIFrame(iframe);
+                            success({ body: data, statusCode: 200, headers: headers });
+                        });
+                    }
+                };
+
+                // Default to two minutes before timing out, 1000 ms * 60 * 2 = 120000.
+                var timeoutMS = (request.timeoutMS) ? request.timeoutMS : 120000;
+                timeoutId = window.setTimeout(handleTimeout, timeoutMS);
+
+                var queryStringParams = callbackParameterName + "=parent." + name;
+                if (this.formatQueryString) {
+                    queryStringParams += "&" + formatQueryString;
+                }
+
+                var qIndex = url.indexOf("?");
+                if (qIndex === -1) {
+                    url = url + "?" + queryStringParams;
+                } else if (qIndex === url.length - 1) {
+                    url = url + queryStringParams;
+                } else {
+                    url = url + "&" + queryStringParams;
+                }
+
+                iframe = createIFrame(url);
+            }
+
+            return result;
+        }
+    };
+
+    // DATAJS INTERNAL START
+    odata.canUseJSONP = canUseJSONP;
+    odata.isAbsoluteUrl = isAbsoluteUrl;
+    odata.isLocalUrl = isLocalUrl;
+    // DATAJS INTERNAL END
+
+    // CONTENT END
+})(this);
\ No newline at end of file
diff --git a/JSLib/src/odata-utils.js b/JSLib/src/odata-utils.js
new file mode 100644
index 0000000..ad2cf00
--- /dev/null
+++ b/JSLib/src/odata-utils.js
@@ -0,0 +1,1117 @@
+// Copyright (c) Microsoft Open Technologies, Inc.  All rights reserved.
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation 
+// files (the "Software"), to deal  in the Software without restriction, including without limitation the rights  to use, copy,
+// modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the 
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR  IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// WARRANTIES OF MERCHANTABILITY,  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+// odata-utils.js
+
+(function (window, undefined) {
+
+    var datajs = window.datajs || {};
+    var odata = window.OData || {};
+
+    // Imports
+    var assigned = datajs.assigned;
+    var contains = datajs.contains;
+    var find = datajs.find;
+    var isArray = datajs.isArray;
+    var isDate = datajs.isDate;
+    var isObject = datajs.isObject;
+    var parseInt10 = datajs.parseInt10;
+
+    // CONTENT START
+
+    var dataItemTypeName = function (value, metadata) {
+        /// <summary>Gets the type name of a data item value that belongs to a feed, an entry, a complex type property, or a collection property.</summary>
+        /// <param name="value">Value of the data item from which the type name is going to be retrieved.</param>
+        /// <param name="metadata" type="object" optional="true">Object containing metadata about the data tiem.</param>
+        /// <remarks>
+        ///    This function will first try to get the type name from the data item's value itself if it is an object with a __metadata property; otherwise
+        ///    it will try to recover it from the metadata.  If both attempts fail, it will return null.
+        /// </remarks>
+        /// <returns type="String">Data item type name; null if the type name cannot be found within the value or the metadata</returns>
+
+        var valueTypeName = ((value && value.__metadata) || {}).type;
+        return valueTypeName || (metadata ? metadata.type : null);
+    };
+
+    var EDM = "Edm.";
+    var EDM_BINARY = EDM + "Binary";
+    var EDM_BOOLEAN = EDM + "Boolean";
+    var EDM_BYTE = EDM + "Byte";
+    var EDM_DATETIME = EDM + "DateTime";
+    var EDM_DATETIMEOFFSET = EDM + "DateTimeOffset";
+    var EDM_DECIMAL = EDM + "Decimal";
+    var EDM_DOUBLE = EDM + "Double";
+    var EDM_GUID = EDM + "Guid";
+    var EDM_INT16 = EDM + "Int16";
+    var EDM_INT32 = EDM + "Int32";
+    var EDM_INT64 = EDM + "Int64";
+    var EDM_SBYTE = EDM + "SByte";
+    var EDM_SINGLE = EDM + "Single";
+    var EDM_STRING = EDM + "String";
+    var EDM_TIME = EDM + "Time";
+
+    var EDM_GEOGRAPHY = EDM + "Geography";
+    var EDM_GEOGRAPHY_POINT = EDM_GEOGRAPHY + "Point";
+    var EDM_GEOGRAPHY_LINESTRING = EDM_GEOGRAPHY + "LineString";
+    var EDM_GEOGRAPHY_POLYGON = EDM_GEOGRAPHY + "Polygon";
+    var EDM_GEOGRAPHY_COLLECTION = EDM_GEOGRAPHY + "Collection";
+    var EDM_GEOGRAPHY_MULTIPOLYGON = EDM_GEOGRAPHY + "MultiPolygon";
+    var EDM_GEOGRAPHY_MULTILINESTRING = EDM_GEOGRAPHY + "MultiLineString";
+    var EDM_GEOGRAPHY_MULTIPOINT = EDM_GEOGRAPHY + "MultiPoint";
+
+    var EDM_GEOMETRY = EDM + "Geometry";
+    var EDM_GEOMETRY_POINT = EDM_GEOMETRY + "Point";
+    var EDM_GEOMETRY_LINESTRING = EDM_GEOMETRY + "LineString";
+    var EDM_GEOMETRY_POLYGON = EDM_GEOMETRY + "Polygon";
+    var EDM_GEOMETRY_COLLECTION = EDM_GEOMETRY + "Collection";
+    var EDM_GEOMETRY_MULTIPOLYGON = EDM_GEOMETRY + "MultiPolygon";
+    var EDM_GEOMETRY_MULTILINESTRING = EDM_GEOMETRY + "MultiLineString";
+    var EDM_GEOMETRY_MULTIPOINT = EDM_GEOMETRY + "MultiPoint";
+
+    var GEOJSON_POINT = "Point";
+    var GEOJSON_LINESTRING = "LineString";
+    var GEOJSON_POLYGON = "Polygon";
+    var GEOJSON_MULTIPOINT = "MultiPoint";
+    var GEOJSON_MULTILINESTRING = "MultiLineString";
+    var GEOJSON_MULTIPOLYGON = "MultiPolygon";
+    var GEOJSON_GEOMETRYCOLLECTION = "GeometryCollection";
+
+    var primitiveEdmTypes = [
+        EDM_STRING,
+        EDM_INT32,
+        EDM_INT64,
+        EDM_BOOLEAN,
+        EDM_DOUBLE,
+        EDM_SINGLE,
+        EDM_DATETIME,
+        EDM_DATETIMEOFFSET,
+        EDM_TIME,
+        EDM_DECIMAL,
+        EDM_GUID,
+        EDM_BYTE,
+        EDM_INT16,
+        EDM_SBYTE,
+        EDM_BINARY
+    ];
+
+    var geometryEdmTypes = [
+        EDM_GEOMETRY,
+        EDM_GEOMETRY_POINT,
+        EDM_GEOMETRY_LINESTRING,
+        EDM_GEOMETRY_POLYGON,
+        EDM_GEOMETRY_COLLECTION,
+        EDM_GEOMETRY_MULTIPOLYGON,
+        EDM_GEOMETRY_MULTILINESTRING,
+        EDM_GEOMETRY_MULTIPOINT
+    ];
+
+    var geographyEdmTypes = [
+        EDM_GEOGRAPHY,
+        EDM_GEOGRAPHY_POINT,
+        EDM_GEOGRAPHY_LINESTRING,
+        EDM_GEOGRAPHY_POLYGON,
+        EDM_GEOGRAPHY_COLLECTION,
+        EDM_GEOGRAPHY_MULTIPOLYGON,
+        EDM_GEOGRAPHY_MULTILINESTRING,
+        EDM_GEOGRAPHY_MULTIPOINT
+    ];
+
+    var forEachSchema = function (metadata, callback) {
+        /// <summary>Invokes a function once per schema in metadata.</summary>
+        /// <param name="metadata">Metadata store; one of edmx, schema, or an array of any of them.</param>
+        /// <param name="callback" type="Function">Callback function to invoke once per schema.</param>
+        /// <returns>
+        /// The first truthy value to be returned from the callback; null or the last falsy value otherwise.
+        /// </returns>
+
+        if (!metadata) {
+            return null;
+        }
+
+        if (isArray(metadata)) {
+            var i, len, result;
+            for (i = 0, len = metadata.length; i < len; i++) {
+                result = forEachSchema(metadata[i], callback);
+                if (result) {
+                    return result;
+                }
+            }
+
+            return null;
+        } else {
+            if (metadata.dataServices) {
+                return forEachSchema(metadata.dataServices.schema, callback);
+            }
+
+            return callback(metadata);
+        }
+    };
+
+    var formatMilliseconds = function (ms, ns) {
+        /// <summary>Formats a millisecond and a nanosecond value into a single string.</summary>
+        /// <param name="ms" type="Number" mayBeNull="false">Number of milliseconds to format.</param>
+        /// <param name="ns" type="Number" mayBeNull="false">Number of nanoseconds to format.</param>
+        /// <returns type="String">Formatted text.</returns>
+        /// <remarks>If the value is already as string it's returned as-is.</remarks>
+
+        // Avoid generating milliseconds if not necessary.
+        if (ms === 0) {
+            ms = "";
+        } else {
+            ms = "." + formatNumberWidth(ms.toString(), 3);
+        }
+        if (ns > 0) {
+            if (ms === "") {
+                ms = ".000";
+            }
+            ms += formatNumberWidth(ns.toString(), 4);
+        }
+        return ms;
+    };
+
+    var formatDateTimeOffset = function (value) {
+        /// <summary>Formats a DateTime or DateTimeOffset value a string.</summary>
+        /// <param name="value" type="Date" mayBeNull="false">Value to format.</param>
+        /// <returns type="String">Formatted text.</returns>
+        /// <remarks>If the value is already as string it's returned as-is.</remarks>
+
+        if (typeof value === "string") {
+            return value;
+        }
+
+        var hasOffset = isDateTimeOffset(value);
+        var offset = getCanonicalTimezone(value.__offset);
+        if (hasOffset && offset !== "Z") {
+            // We're about to change the value, so make a copy.
+            value = new Date(value.valueOf());
+
+            var timezone = parseTimezone(offset);
+            var hours = value.getUTCHours() + (timezone.d * timezone.h);
+            var minutes = value.getUTCMinutes() + (timezone.d * timezone.m);
+
+            value.setUTCHours(hours, minutes);
+        } else if (!hasOffset) {
+            // Don't suffix a 'Z' for Edm.DateTime values.
+            offset = "";
+        }
+
+        var year = value.getUTCFullYear();
+        var month = value.getUTCMonth() + 1;
+        var sign = "";
+        if (year <= 0) {
+            year = -(year - 1);
+            sign = "-";
+        }
+
+        var ms = formatMilliseconds(value.getUTCMilliseconds(), value.__ns);
+
+        return sign +
+            formatNumberWidth(year, 4) + "-" +
+            formatNumberWidth(month, 2) + "-" +
+            formatNumberWidth(value.getUTCDate(), 2) + "T" +
+            formatNumberWidth(value.getUTCHours(), 2) + ":" +
+            formatNumberWidth(value.getUTCMinutes(), 2) + ":" +
+            formatNumberWidth(value.getUTCSeconds(), 2) +
+            ms + offset;
+    };
+
+    var formatDuration = function (value) {
+        /// <summary>Converts a duration to a string in xsd:duration format.</summary>
+        /// <param name="value" type="Object">Object with ms and __edmType properties.</param>
+        /// <returns type="String">String representation of the time object in xsd:duration format.</returns>
+
+        var ms = value.ms;
+
+        var sign = "";
+        if (ms < 0) {
+            sign = "-";
+            ms = -ms;
+        }
+
+        var days = Math.floor(ms / 86400000);
+        ms -= 86400000 * days;
+        var hours = Math.floor(ms / 3600000);
+        ms -= 3600000 * hours;
+        var minutes = Math.floor(ms / 60000);
+        ms -= 60000 * minutes;
+        var seconds = Math.floor(ms / 1000);
+        ms -= seconds * 1000;
+
+        return sign + "P" +
+               formatNumberWidth(days, 2) + "DT" +
+               formatNumberWidth(hours, 2) + "H" +
+               formatNumberWidth(minutes, 2) + "M" +
+               formatNumberWidth(seconds, 2) +
+               formatMilliseconds(ms, value.ns) + "S";
+    };
+
+    var formatNumberWidth = function (value, width, append) {
+        /// <summary>Formats the specified value to the given width.</summary>
+        /// <param name="value" type="Number">Number to format (non-negative).</param>
+        /// <param name="width" type="Number">Minimum width for number.</param>
+        /// <param name="append" type="Boolean">Flag indicating if the value is padded at the beginning (false) or at the end (true).</param>
+        /// <returns type="String">Text representation.</returns>
+        var result = value.toString(10);
+        while (result.length < width) {
+            if (append) {
+                result += "0";
+            } else {
+                result = "0" + result;
+            }
+        }
+
+        return result;
+    };
+
+    var getCanonicalTimezone = function (timezone) {
+        /// <summary>Gets the canonical timezone representation.</summary>
+        /// <param name="timezone" type="String">Timezone representation.</param>
+        /// <returns type="String">An 'Z' string if the timezone is absent or 0; the timezone otherwise.</returns>
+
+        return (!timezone || timezone === "Z" || timezone === "+00:00" || timezone === "-00:00") ? "Z" : timezone;
+    };
+
+    var getCollectionType = function (typeName) {
+        /// <summary>Gets the type of a collection type name.</summary>
+        /// <param name="typeName" type="String">Type name of the collection.</param>
+        /// <returns type="String">Type of the collection; null if the type name is not a collection type.</returns>
+
+        if (typeof typeName === "string") {
+            var end = typeName.indexOf(")", 10);
+            if (typeName.indexOf("Collection(") === 0 && end > 0) {
+                return typeName.substring(11, end);
+            }
+        }
+        return null;
+    };
+
+    var invokeRequest = function (request, success, error, handler, httpClient, context) {
+        /// <summary>Sends a request containing OData payload to a server.</summary>
+        /// <param name="request">Object that represents the request to be sent..</param>
+        /// <param name="success">Callback for a successful read operation.</param>
+        /// <param name="error">Callback for handling errors.</param>
+        /// <param name="handler">Handler for data serialization.</param>
+        /// <param name="httpClient">HTTP client layer.</param>
+        /// <param name="context">Context used for processing the request</param>
+
+        return httpClient.request(request, function (response) {
+            try {
+                if (response.headers) {
+                    normalizeHeaders(response.headers);
+                }
+
+                if (response.data === undefined && response.statusCode !== 204) {
+                    handler.read(response, context);
+                }
+            } catch (err) {
+                if (err.request === undefined) {
+                    err.request = request;
+                }
+                if (err.response === undefined) {
+                    err.response = response;
+                }
+                error(err);
+                return;
+            }
+
+            success(response.data, response);
+        }, error);
+    };
+
+    var isBatch = function (value) {
+        /// <summary>Tests whether a value is a batch object in the library's internal representation.</summary>
+        /// <param name="value">Value to test.</param>
+        /// <returns type="Boolean">True is the value is a batch object; false otherwise.</returns>
+
+        return isComplex(value) && isArray(value.__batchRequests);
+    };
+
+    // Regular expression used for testing and parsing for a collection type.
+    var collectionTypeRE = /Collection\((.*)\)/;
+
+    var isCollection = function (value, typeName) {
+        /// <summary>Tests whether a value is a collection value in the library's internal representation.</summary>
+        /// <param name="value">Value to test.</param>
+        /// <param name="typeName" type="Sting">Type name of the value. This is used to disambiguate from a collection property value.</param>
+        /// <returns type="Boolean">True is the value is a feed value; false otherwise.</returns>
+
+        var colData = value && value.results || value;
+        return !!colData &&
+            (isCollectionType(typeName)) ||
+            (!typeName && isArray(colData) && !isComplex(colData[0]));
+    };
+
+    var isCollectionType = function (typeName) {
+        /// <summary>Checks whether the specified type name is a collection type.</summary>
+        /// <param name="typeName" type="String">Name of type to check.</param>
+        /// <returns type="Boolean">True if the type is the name of a collection type; false otherwise.</returns>
+        return collectionTypeRE.test(typeName);
+    };
+
+    var isComplex = function (value) {
+        /// <summary>Tests whether a value is a complex type value in the library's internal representation.</summary>
+        /// <param name="value">Value to test.</param>
+        /// <returns type="Boolean">True is the value is a complex type value; false otherwise.</returns>
+
+        return !!value &&
+            isObject(value) &&
+            !isArray(value) &&
+            !isDate(value);
+    };
+
+    var isDateTimeOffset = function (value) {
+        /// <summary>Checks whether a Date object is DateTimeOffset value</summary>
+        /// <param name="value" type="Date" mayBeNull="false">Value to check.</param>
+        /// <returns type="Boolean">true if the value is a DateTimeOffset, false otherwise.</returns>
+        return (value.__edmType === "Edm.DateTimeOffset" || (!value.__edmType && value.__offset));
+    };
+
+    var isDeferred = function (value) {
+        /// <summary>Tests whether a value is a deferred navigation property in the library's internal representation.</summary>
+        /// <param name="value">Value to test.</param>
+        /// <returns type="Boolean">True is the value is a deferred navigation property; false otherwise.</returns>
+
+        if (!value && !isComplex(value)) {
+            return false;
+        }
+        var metadata = value.__metadata || {};
+        var deferred = value.__deferred || {};
+        return !metadata.type && !!deferred.uri;
+    };
+
+    var isEntry = function (value) {
+        /// <summary>Tests whether a value is an entry object in the library's internal representation.</summary>
+        /// <param name="value">Value to test.</param>
+        /// <returns type="Boolean">True is the value is an entry object; false otherwise.</returns>
+
+        return isComplex(value) && value.__metadata && "uri" in value.__metadata;
+    };
+
+    var isFeed = function (value, typeName) {
+        /// <summary>Tests whether a value is a feed value in the library's internal representation.</summary>
+        /// <param name="value">Value to test.</param>
+        /// <param name="typeName" type="Sting">Type name of the value. This is used to disambiguate from a collection property value.</param>
+        /// <returns type="Boolean">True is the value is a feed value; false otherwise.</returns>
+
+        var feedData = value && value.results || value;
+        return isArray(feedData) && (
+            (!isCollectionType(typeName)) &&
+            (isComplex(feedData[0]))
+        );
+    };
+
+    var isGeographyEdmType = function (typeName) {
+        /// <summary>Checks whether the specified type name is a geography EDM type.</summary>
+        /// <param name="typeName" type="String">Name of type to check.</param>
+        /// <returns type="Boolean">True if the type is a geography EDM type; false otherwise.</returns>
+
+        return contains(geographyEdmTypes, typeName);
+    };
+
+    var isGeometryEdmType = function (typeName) {
+        /// <summary>Checks whether the specified type name is a geometry EDM type.</summary>
+        /// <param name="typeName" type="String">Name of type to check.</param>
+        /// <returns type="Boolean">True if the type is a geometry EDM type; false otherwise.</returns>
+
+        return contains(geometryEdmTypes, typeName);
+    };
+
+    var isNamedStream = function (value) {
+        /// <summary>Tests whether a value is a named stream value in the library's internal representation.</summary>
+        /// <param name="value">Value to test.</param>
+        /// <returns type="Boolean">True is the value is a named stream; false otherwise.</returns>
+
+        if (!value && !isComplex(value)) {
+            return false;
+        }
+        var metadata = value.__metadata;
+        var mediaResource = value.__mediaresource;
+        return !metadata && !!mediaResource && !!mediaResource.media_src;
+    };
+
+    var isPrimitive = function (value) {
+        /// <summary>Tests whether a value is a primitive type value in the library's internal representation.</summary>
+        /// <param name="value">Value to test.</param>
+        /// <remarks>
+        ///    Date objects are considered primitive types by the library.
+        /// </remarks>
+        /// <returns type="Boolean">True is the value is a primitive type value.</returns>
+
+        return isDate(value) ||
+            typeof value === "string" ||
+            typeof value === "number" ||
+            typeof value === "boolean";
+    };
+
+    var isPrimitiveEdmType = function (typeName) {
+        /// <summary>Checks whether the specified type name is a primitive EDM type.</summary>
+        /// <param name="typeName" type="String">Name of type to check.</param>
+        /// <returns type="Boolean">True if the type is a primitive EDM type; false otherwise.</returns>
+
+        return contains(primitiveEdmTypes, typeName);
+    };
+
+    var navigationPropertyKind = function (value, propertyModel) {
+        /// <summary>Gets the kind of a navigation property value.</summary>
+        /// <param name="value">Value of the navigation property.</param>
+        /// <param name="propertyModel" type="Object" optional="true">
+        ///     Object that describes the navigation property in an OData conceptual schema.
+        /// </param>
+        /// <remarks>
+        ///     The returned string is as follows
+        /// </remarks>
+        /// <returns type="String">String value describing the kind of the navigation property; null if the kind cannot be determined.</returns>
+
+        if (isDeferred(value)) {
+            return "deferred";
+        }
+        if (isEntry(value)) {
+            return "entry";
+        }
+        if (isFeed(value)) {
+            return "feed";
+        }
+        if (propertyModel && propertyModel.relationship) {
+            if (value === null || value === undefined || !isFeed(value)) {
+                return "entry";
+            }
+            return "feed";
+        }
+        return null;
+    };
+
+    var lookupProperty = function (properties, name) {
+        /// <summary>Looks up a property by name.</summary>
+        /// <param name="properties" type="Array" mayBeNull="true">Array of property objects as per EDM metadata.</param>
+        /// <param name="name" type="String">Name to look for.</param>
+        /// <returns type="Object">The property object; null if not found.</returns>
+
+        return find(properties, function (property) {
+            return property.name === name;
+        });
+    };
+
+    var lookupInMetadata = function (name, metadata, kind) {
+        /// <summary>Looks up a type object by name.</summary>
+        /// <param name="name" type="String">Name, possibly null or empty.</param>
+        /// <param name="metadata">Metadata store; one of edmx, schema, or an array of any of them.</param>
+        /// <param name="kind" type="String">Kind of object to look for as per EDM metadata.</param>
+        /// <returns>An type description if the name is found; null otherwise.</returns>
+
+        return (name) ? forEachSchema(metadata, function (schema) {
+            return lookupInSchema(name, schema, kind);
+        }) : null;
+    };
+
+    var lookupEntitySet = function (entitySets, name) {
+        /// <summary>Looks up a entity set by name.</summary>
+        /// <param name="properties" type="Array" mayBeNull="true">Array of entity set objects as per EDM metadata.</param>
+        /// <param name="name" type="String">Name to look for.</param>
+        /// <returns type="Object">The entity set object; null if not found.</returns>
+
+        return find(entitySets, function (entitySet) {
+            return entitySet.name === name;
+        });
+    };
+
+    var lookupComplexType = function (name, metadata) {
+        /// <summary>Looks up a complex type object by name.</summary>
+        /// <param name="name" type="String">Name, possibly null or empty.</param>
+        /// <param name="metadata">Metadata store; one of edmx, schema, or an array of any of them.</param>
+        /// <returns>A complex type description if the name is found; null otherwise.</returns>
+
+        return lookupInMetadata(name, metadata, "complexType");
+    };
+
+    var lookupEntityType = function (name, metadata) {
+        /// <summary>Looks up an entity type object by name.</summary>
+        /// <param name="name" type="String">Name, possibly null or empty.</param>
+        /// <param name="metadata">Metadata store; one of edmx, schema, or an array of any of them.</param>
+        /// <returns>An entity type description if the name is found; null otherwise.</returns>
+
+        return lookupInMetadata(name, metadata, "entityType");
+    };
+
+    var lookupDefaultEntityContainer = function (metadata) {
+        /// <summary>Looks up an</summary>
+        /// <param name="name" type="String">Name, possibly null or empty.</param>
+        /// <param name="metadata">Metadata store; one of edmx, schema, or an array of any of them.</param>
+        /// <returns>An entity container description if the name is found; null otherwise.</returns>
+
+        return forEachSchema(metadata, function (schema) {
+            return find(schema.entityContainer, function (container) {
+                return parseBool(container.isDefaultEntityContainer);
+            });
+        });
+    };
+
+    var lookupEntityContainer = function (name, metadata) {
+        /// <summary>Looks up an entity container object by name.</summary>
+        /// <param name="name" type="String">Name, possibly null or empty.</param>
+        /// <param name="metadata">Metadata store; one of edmx, schema, or an array of any of them.</param>
+        /// <returns>An entity container description if the name is found; null otherwise.</returns>
+
+        return lookupInMetadata(name, metadata, "entityContainer");
+    };
+
+    var lookupFunctionImport = function (functionImports, name) {
+        /// <summary>Looks up a function import by name.</summary>
+        /// <param name="properties" type="Array" mayBeNull="true">Array of function import objects as per EDM metadata.</param>
+        /// <param name="name" type="String">Name to look for.</param>
+        /// <returns type="Object">The entity set object; null if not found.</returns>
+
+        return find(functionImports, function (functionImport) {
+            return functionImport.name === name;
+        });
+    };
+
+    var lookupNavigationPropertyType = function (navigationProperty, metadata) {
+        /// <summary>Looks up the target entity type for a navigation property.</summary>
+        /// <param name="navigationProperty" type="Object"></param>
+        /// <param name="metadata" type="Object"></param>
+        /// <returns type="String">The entity type name for the specified property, null if not found.</returns>
+
+        var result = null;
+        if (navigationProperty) {
+            var rel = navigationProperty.relationship;
+            var association = forEachSchema(metadata, function (schema) {
+                // The name should be the namespace qualified name in 'ns'.'type' format.
+                var nameOnly = removeNamespace(schema["namespace"], rel);
+                var associations = schema.association;
+                if (nameOnly && associations) {
+                    var i, len;
+                    for (i = 0, len = associations.length; i < len; i++) {
+                        if (associations[i].name === nameOnly) {
+                            return associations[i];
+                        }
+                    }
+                }
+                return null;
+            });
+
+            if (association) {
+                var end = association.end[0];
+                if (end.role !== navigationProperty.toRole) {
+                    end = association.end[1];
+                    // For metadata to be valid, end.role === navigationProperty.toRole now.
+                }
+                result = end.type;
+            }
+        }
+        return result;
+    };
+
+    var lookupNavigationPropertyEntitySet = function (navigationProperty, sourceEntitySetName, metadata) {
+        /// <summary>Looks up the target entityset name for a navigation property.</summary>
+        /// <param name="navigationProperty" type="Object"></param>
+        /// <param name="metadata" type="Object"></param>
+        /// <returns type="String">The entityset name for the specified property, null if not found.</returns>
+
+        if (navigationProperty) {
+            var rel = navigationProperty.relationship;
+            var associationSet = forEachSchema(metadata, function (schema) {
+                var containers = schema.entityContainer;
+                for (var i = 0; i < containers.length; i++) {
+                    var associationSets = containers[i].associationSet;
+                    if (associationSets) {
+                        for (var j = 0; j < associationSets.length; j++) {
+                            if (associationSets[j].association == rel) {
+                                return associationSets[j];
+                            }
+                        }
+                    }
+                }
+                return null;
+            });
+            if (associationSet && associationSet.end[0] && associationSet.end[1]) {
+                return (associationSet.end[0].entitySet == sourceEntitySetName) ? associationSet.end[1].entitySet : associationSet.end[0].entitySet;
+            }
+        }
+        return null;
+    };
+
+    var getEntitySetInfo = function (entitySetName, metadata) {
+        /// <summary>Gets the entitySet info, container name and functionImports for an entitySet</summary>
+        /// <param name="navigationProperty" type="Object"></param>
+        /// <param name="metadata" type="Object"></param>
+        /// <returns type="Object">The info about the entitySet.</returns>
+
+        var info = forEachSchema(metadata, function (schema) {
+            var containers = schema.entityContainer;
+            for (var i = 0; i < containers.length; i++) {
+                var entitySets = containers[i].entitySet;
+                if (entitySets) {
+                    for (var j = 0; j < entitySets.length; j++) {
+                        if (entitySets[j].name == entitySetName) {
+                            return { entitySet: entitySets[j], containerName: containers[i].name, functionImport: containers[i].functionImport };
+                        }
+                    }
+                }
+            }
+            return null;
+        });
+
+        return info;
+    };
+
+    var removeNamespace = function (ns, fullName) {
+        /// <summary>Given an expected namespace prefix, removes it from a full name.</summary>
+        /// <param name="ns" type="String">Expected namespace.</param>
+        /// <param name="fullName" type="String">Full name in 'ns'.'name' form.</param>
+        /// <returns type="String">The local name, null if it isn't found in the expected namespace.</returns>
+
+        if (fullName.indexOf(ns) === 0 && fullName.charAt(ns.length) === ".") {
+            return fullName.substr(ns.length + 1);
+        }
+
+        return null;
+    };
+
+    var lookupInSchema = function (name, schema, kind) {
+        /// <summary>Looks up a schema object by name.</summary>
+        /// <param name="name" type="String">Name (assigned).</param>
+        /// <param name="schema">Schema object as per EDM metadata.</param>
+        /// <param name="kind" type="String">Kind of object to look for as per EDM metadata.</param>
+        /// <returns>An entity type description if the name is found; null otherwise.</returns>
+
+        if (name && schema) {
+            // The name should be the namespace qualified name in 'ns'.'type' format.
+            var nameOnly = removeNamespace(schema["namespace"], name);
+            if (nameOnly) {
+                return find(schema[kind], function (item) {
+                    return item.name === nameOnly;
+                });
+            }
+        }
+        return null;
+    };
+
+    var maxVersion = function (left, right) {
+        /// <summary>Compares to version strings and returns the higher one.</summary>
+        /// <param name="left" type="String">Version string in the form "major.minor.rev"</param>
+        /// <param name="right" type="String">Version string in the form "major.minor.rev"</param>
+        /// <returns type="String">The higher version string.</returns>
+
+        if (left === right) {
+            return left;
+        }
+
+        var leftParts = left.split(".");
+        var rightParts = right.split(".");
+
+        var len = (leftParts.length >= rightParts.length) ?
+            leftParts.length :
+            rightParts.length;
+
+        for (var i = 0; i < len; i++) {
+            var leftVersion = leftParts[i] && parseInt10(leftParts[i]);
+            var rightVersion = rightParts[i] && parseInt10(rightParts[i]);
+            if (leftVersion > rightVersion) {
+                return left;
+            }
+            if (leftVersion < rightVersion) {
+                return right;
+            }
+        }
+    };
+
+    var normalHeaders = {
+        "accept": "Accept",
+        "content-type": "Content-Type",
+        "dataserviceversion": "DataServiceVersion",
+        "maxdataserviceversion": "MaxDataServiceVersion"
+    };
+
+    var normalizeHeaders = function (headers) {
+        /// <summary>Normalizes headers so they can be found with consistent casing.</summary>
+        /// <param name="headers" type="Object">Dictionary of name/value pairs.</param>
+
+        for (var name in headers) {
+            var lowerName = name.toLowerCase();
+            var normalName = normalHeaders[lowerName];
+            if (normalName && name !== normalName) {
+                var val = headers[name];
+                delete headers[name];
+                headers[normalName] = val;
+            }
+        }
+    };
+
+    var parseBool = function (propertyValue) {
+        /// <summary>Parses a string into a boolean value.</summary>
+        /// <param name="propertyValue">Value to parse.</param>
+        /// <returns type="Boolean">true if the property value is 'true'; false otherwise.</returns>
+
+        if (typeof propertyValue === "boolean") {
+            return propertyValue;
+        }
+
+        return typeof propertyValue === "string" && propertyValue.toLowerCase() === "true";
+    };
+
+
+    // The captured indices for this expression are:
+    // 0     - complete input
+    // 1,2,3 - year with optional minus sign, month, day
+    // 4,5,6 - hours, minutes, seconds
+    // 7     - optional milliseconds
+    // 8     - everything else (presumably offset information)
+    var parseDateTimeRE = /^(-?\d{4,})-(\d{2})-(\d{2})T(\d{2}):(\d{2})(?::(\d{2}))?(?:\.(\d+))?(.*)$/;
+
+    var parseDateTimeMaybeOffset = function (value, withOffset, nullOnError) {
+        /// <summary>Parses a string into a DateTime value.</summary>
+        /// <param name="value" type="String">Value to parse.</param>
+        /// <param name="withOffset" type="Boolean">Whether offset is expected.</param>
+        /// <returns type="Date">The parsed value.</returns>
+
+        // We cannot parse this in cases of failure to match or if offset information is specified.
+        var parts = parseDateTimeRE.exec(value);
+        var offset = (parts) ? getCanonicalTimezone(parts[8]) : null;
+
+        if (!parts || (!withOffset && offset !== "Z")) {
+            if (nullOnError) {
+                return null;
+            }
+            throw { message: "Invalid date/time value" };
+        }
+
+        // Pre-parse years, account for year '0' being invalid in dateTime.
+        var year = parseInt10(parts[1]);
+        if (year <= 0) {
+            year++;
+        }
+
+        // Pre-parse optional milliseconds, fill in default. Fail if value is too precise.
+        var ms = parts[7];
+        var ns = 0;
+        if (!ms) {
+            ms = 0;
+        } else {
+            if (ms.length > 7) {
+                if (nullOnError) {
+                    return null;
+                }
+                throw { message: "Cannot parse date/time value to given precision." };
+            }
+
+            ns = formatNumberWidth(ms.substring(3), 4, true);
+            ms = formatNumberWidth(ms.substring(0, 3), 3, true);
+
+            ms = parseInt10(ms);
+            ns = parseInt10(ns);
+        }
+
+        // Pre-parse other time components and offset them if necessary.
+        var hours = parseInt10(parts[4]);
+        var minutes = parseInt10(parts[5]);
+        var seconds = parseInt10(parts[6]) || 0;
+        if (offset !== "Z") {
+            // The offset is reversed to get back the UTC date, which is
+            // what the API will eventually have.
+            var timezone = parseTimezone(offset);
+            var direction = -(timezone.d);
+            hours += timezone.h * direction;
+            minutes += timezone.m * direction;
+        }
+
+        // Set the date and time separately with setFullYear, so years 0-99 aren't biased like in Date.UTC.
+        var result = new Date();
+        result.setUTCFullYear(
+            year,                       // Year.
+            parseInt10(parts[2]) - 1,   // Month (zero-based for Date.UTC and setFullYear).
+            parseInt10(parts[3])        // Date.
+            );
+        result.setUTCHours(hours, minutes, seconds, ms);
+
+        if (isNaN(result.valueOf())) {
+            if (nullOnError) {
+                return null;
+            }
+            throw { message: "Invalid date/time value" };
+        }
+
+        if (withOffset) {
+            result.__edmType = "Edm.DateTimeOffset";
+            result.__offset = offset;
+        }
+
+        if (ns) {
+            result.__ns = ns;
+        }
+
+        return result;
+    };
+
+    var parseDateTime = function (propertyValue, nullOnError) {
+        /// <summary>Parses a string into a DateTime value.</summary>
+        /// <param name="propertyValue" type="String">Value to parse.</param>
+        /// <returns type="Date">The parsed value.</returns>
+
+        return parseDateTimeMaybeOffset(propertyValue, false, nullOnError);
+    };
+
+    var parseDateTimeOffset = function (propertyValue, nullOnError) {
+        /// <summary>Parses a string into a DateTimeOffset value.</summary>
+        /// <param name="propertyValue" type="String">Value to parse.</param>
+        /// <returns type="Date">The parsed value.</returns>
+        /// <remarks>
+        /// The resulting object is annotated with an __edmType property and
+        /// an __offset property reflecting the original intended offset of
+        /// the value. The time is adjusted for UTC time, as the current
+        /// timezone-aware Date APIs will only work with the local timezone.
+        /// </remarks>
+
+        return parseDateTimeMaybeOffset(propertyValue, true, nullOnError);
+    };
+
+    // The captured indices for this expression are:
+    // 0       - complete input
+    // 1       - direction
+    // 2,3,4   - years, months, days
+    // 5,6,7,8 - hours, minutes, seconds, miliseconds
+
+    var parseTimeRE = /^([+-])?P(?:(\d+)Y)?(?:(\d+)M)?(?:(\d+)D)?(?:T(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)(?:\.(\d+))?S)?)?/;
+
+    var isEdmDurationValue = function(value) {
+        parseTimeRE.test(value);
+    };
+
+    var parseDuration = function (duration) {
+        /// <summary>Parses a string in xsd:duration format.</summary>
+        /// <param name="duration" type="String">Duration value.</param>
+        /// <remarks>
+        /// This method will throw an exception if the input string has a year or a month component.
+        /// </remarks>
+        /// <returns type="Object">Object representing the time</returns>
+
+        var parts = parseTimeRE.exec(duration);
+
+        if (parts === null) {
+            throw { message: "Invalid duration value." };
+        }
+
+        var years = parts[2] || "0";
+        var months = parts[3] || "0";
+        var days = parseInt10(parts[4] || 0);
+        var hours = parseInt10(parts[5] || 0);
+        var minutes = parseInt10(parts[6] || 0);
+        var seconds = parseFloat(parts[7] || 0);
+
+        if (years !== "0" || months !== "0") {
+            throw { message: "Unsupported duration value." };
+        }
+
+        var ms = parts[8];
+        var ns = 0;
+        if (!ms) {
+            ms = 0;
+        } else {
+            if (ms.length > 7) {
+                throw { message: "Cannot parse duration value to given precision." };
+            }
+
+            ns = formatNumberWidth(ms.substring(3), 4, true);
+            ms = formatNumberWidth(ms.substring(0, 3), 3, true);
+
+            ms = parseInt10(ms);
+            ns = parseInt10(ns);
+        }
+
+        ms += seconds * 1000 + minutes * 60000 + hours * 3600000 + days * 86400000;
+
+        if (parts[1] === "-") {
+            ms = -ms;
+        }
+
+        var result = { ms: ms, __edmType: "Edm.Time" };
+
+        if (ns) {
+            result.ns = ns;
+        }
+        return result;
+    };
+
+    var parseTimezone = function (timezone) {
+        /// <summary>Parses a timezone description in (+|-)nn:nn format.</summary>
+        /// <param name="timezone" type="String">Timezone offset.</param>
+        /// <returns type="Object">
+        /// An object with a (d)irection property of 1 for + and -1 for -,
+        /// offset (h)ours and offset (m)inutes.
+        /// </returns>
+
+        var direction = timezone.substring(0, 1);
+        direction = (direction === "+") ? 1 : -1;
+
+        var offsetHours = parseInt10(timezone.substring(1));
+        var offsetMinutes = parseInt10(timezone.substring(timezone.indexOf(":") + 1));
+        return { d: direction, h: offsetHours, m: offsetMinutes };
+    };
+
+    var prepareRequest = function (request, handler, context) {
+        /// <summary>Prepares a request object so that it can be sent through the network.</summary>
+        /// <param name="request">Object that represents the request to be sent.</param>
+        /// <param name="handler">Handler for data serialization</param>
+        /// <param name="context">Context used for preparing the request</param>
+
+        // Default to GET if no method has been specified.
+        if (!request.method) {
+            request.method = "GET";
+        }
+
+        if (!request.headers) {
+            request.headers = {};
+        } else {
+            normalizeHeaders(request.headers);
+        }
+
+        if (request.headers.Accept === undefined) {
+            request.headers.Accept = handler.accept;
+        }
+
+        if (assigned(request.data) && request.body === undefined) {
+            handler.write(request, context);
+        }
+
+        if (!assigned(request.headers.MaxDataServiceVersion)) {
+            request.headers.MaxDataServiceVersion = handler.maxDataServiceVersion || "1.0";
+        }
+    };
+
+    var traverseInternal = function (item, owner, callback) {
+        /// <summary>Traverses a tree of objects invoking callback for every value.</summary>
+        /// <param name="item" type="Object">Object or array to traverse.</param>
+        /// <param name="callback" type="Function">
+        /// Callback function with key and value, similar to JSON.parse reviver.
+        /// </param>
+        /// <returns type="Object">The object with traversed properties.</returns>
+        /// <remarks>Unlike the JSON reviver, this won't delete null members.</remarks>
+
+        if (item && typeof item === "object") {
+            for (var name in item) {
+                var value = item[name];
+                var result = traverseInternal(value, name, callback);
+                result = callback(name, result, owner);
+                if (result !== value) {
+                    if (value === undefined) {
+                        delete item[name];
+                    } else {
+                        item[name] = result;
+                    }
+                }
+            }
+        }
+
+        return item;
+    };
+
+    var traverse = function (item, callback) {
+        /// <summary>Traverses a tree of objects invoking callback for every value.</summary>
+        /// <param name="item" type="Object">Object or array to traverse.</param>
+        /// <param name="callback" type="Function">
+        /// Callback function with key and value, similar to JSON.parse reviver.
+        /// </param>
+        /// <returns type="Object">The traversed object.</returns>
+        /// <remarks>Unlike the JSON reviver, this won't delete null members.</remarks>
+
+        return callback("", traverseInternal(item, "", callback));
+    };
+
+    // DATAJS INTERNAL START
+    odata.dataItemTypeName = dataItemTypeName;
+    odata.EDM_BINARY = EDM_BINARY;
+    odata.EDM_BOOLEAN = EDM_BOOLEAN;
+    odata.EDM_BYTE = EDM_BYTE;
+    odata.EDM_DATETIME = EDM_DATETIME;
+    odata.EDM_DATETIMEOFFSET = EDM_DATETIMEOFFSET;
+    odata.EDM_DECIMAL = EDM_DECIMAL;
+    odata.EDM_DOUBLE = EDM_DOUBLE;
+    odata.EDM_GEOGRAPHY = EDM_GEOGRAPHY;
+    odata.EDM_GEOGRAPHY_POINT = EDM_GEOGRAPHY_POINT;
+    odata.EDM_GEOGRAPHY_LINESTRING = EDM_GEOGRAPHY_LINESTRING;
+    odata.EDM_GEOGRAPHY_POLYGON = EDM_GEOGRAPHY_POLYGON;
+    odata.EDM_GEOGRAPHY_COLLECTION = EDM_GEOGRAPHY_COLLECTION;
+    odata.EDM_GEOGRAPHY_MULTIPOLYGON = EDM_GEOGRAPHY_MULTIPOLYGON;
+    odata.EDM_GEOGRAPHY_MULTILINESTRING = EDM_GEOGRAPHY_MULTILINESTRING;
+    odata.EDM_GEOGRAPHY_MULTIPOINT = EDM_GEOGRAPHY_MULTIPOINT;
+    odata.EDM_GEOMETRY = EDM_GEOMETRY;
+    odata.EDM_GEOMETRY_POINT = EDM_GEOMETRY_POINT;
+    odata.EDM_GEOMETRY_LINESTRING = EDM_GEOMETRY_LINESTRING;
+    odata.EDM_GEOMETRY_POLYGON = EDM_GEOMETRY_POLYGON;
+    odata.EDM_GEOMETRY_COLLECTION = EDM_GEOMETRY_COLLECTION;
+    odata.EDM_GEOMETRY_MULTIPOLYGON = EDM_GEOMETRY_MULTIPOLYGON;
+    odata.EDM_GEOMETRY_MULTILINESTRING = EDM_GEOMETRY_MULTILINESTRING;
+    odata.EDM_GEOMETRY_MULTIPOINT = EDM_GEOMETRY_MULTIPOINT;
+    odata.EDM_GUID = EDM_GUID;
+    odata.EDM_INT16 = EDM_INT16;
+    odata.EDM_INT32 = EDM_INT32;
+    odata.EDM_INT64 = EDM_INT64;
+    odata.EDM_SBYTE = EDM_SBYTE;
+    odata.EDM_SINGLE = EDM_SINGLE;
+    odata.EDM_STRING = EDM_STRING;
+    odata.EDM_TIME = EDM_TIME;
+    odata.GEOJSON_POINT = GEOJSON_POINT;
+    odata.GEOJSON_LINESTRING = GEOJSON_LINESTRING;
+    odata.GEOJSON_POLYGON = GEOJSON_POLYGON;
+    odata.GEOJSON_MULTIPOINT = GEOJSON_MULTIPOINT;
+    odata.GEOJSON_MULTILINESTRING = GEOJSON_MULTILINESTRING;
+    odata.GEOJSON_MULTIPOLYGON = GEOJSON_MULTIPOLYGON;
+    odata.GEOJSON_GEOMETRYCOLLECTION = GEOJSON_GEOMETRYCOLLECTION;
+    odata.forEachSchema = forEachSchema;
+    odata.formatDateTimeOffset = formatDateTimeOffset;
+    odata.formatDuration = formatDuration;
+    odata.formatNumberWidth = formatNumberWidth;
+    odata.getCanonicalTimezone = getCanonicalTimezone;
+    odata.getCollectionType = getCollectionType;
+    odata.invokeRequest = invokeRequest;
+    odata.isBatch = isBatch;
+    odata.isCollection = isCollection;
+    odata.isCollectionType = isCollectionType;
+    odata.isComplex = isComplex;
+    odata.isDateTimeOffset = isDateTimeOffset;
+    odata.isDeferred = isDeferred;
+    odata.isEntry = isEntry;
+    odata.isFeed = isFeed;
+    odata.isGeographyEdmType = isGeographyEdmType;
+    odata.isGeometryEdmType = isGeometryEdmType;
+    odata.isNamedStream = isNamedStream;
+    odata.isPrimitive = isPrimitive;
+    odata.isPrimitiveEdmType = isPrimitiveEdmType;
+    odata.lookupComplexType = lookupComplexType;
+    odata.lookupDefaultEntityContainer = lookupDefaultEntityContainer;
+    odata.lookupEntityContainer = lookupEntityContainer;
+    odata.lookupEntitySet = lookupEntitySet;
+    odata.lookupEntityType = lookupEntityType;
+    odata.lookupFunctionImport = lookupFunctionImport;
+    odata.lookupNavigationPropertyType = lookupNavigationPropertyType;
+    odata.lookupNavigationPropertyEntitySet = lookupNavigationPropertyEntitySet;
+    odata.lookupInSchema = lookupInSchema;
+    odata.lookupProperty = lookupProperty;
+    odata.lookupInMetadata = lookupInMetadata;
+    odata.getEntitySetInfo = getEntitySetInfo;
+    odata.maxVersion = maxVersion;
+    odata.navigationPropertyKind = navigationPropertyKind;
+    odata.normalizeHeaders = normalizeHeaders;
+    odata.parseBool = parseBool;
+    odata.parseDateTime = parseDateTime;
+    odata.parseDateTimeOffset = parseDateTimeOffset;
+    odata.parseDuration = parseDuration;
+    odata.parseTimezone = parseTimezone;
+    odata.parseInt10 = parseInt10;
+    odata.prepareRequest = prepareRequest;
+    odata.removeNamespace = removeNamespace;
+    odata.traverse = traverse;
+
+    // DATAJS INTERNAL END
+
+    // CONTENT END
+})(this);
\ No newline at end of file
diff --git a/JSLib/src/odata-xml.js b/JSLib/src/odata-xml.js
new file mode 100644
index 0000000..b2d00f6
--- /dev/null
+++ b/JSLib/src/odata-xml.js
@@ -0,0 +1,850 @@
+// Copyright (c) Microsoft Open Technologies, Inc.  All rights reserved.
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation 
+// files (the "Software"), to deal  in the Software without restriction, including without limitation the rights  to use, copy,
+// modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the 
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR  IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// WARRANTIES OF MERCHANTABILITY,  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+// odata-xml.js
+
+(function (window, undefined) {
+
+    var datajs = window.datajs || {};
+    var odata = window.OData || {};
+
+    // Imports.
+
+    var djsassert = datajs.djsassert;
+    var http = datajs.http;
+    var isArray = datajs.isArray;
+    var isDate = datajs.isDate;
+    var isObject = datajs.isObject;
+    var normalizeURI = datajs.normalizeURI;
+    var parseInt10 = datajs.parseInt10;
+    var xmlAppendChild = datajs.xmlAppendChild;
+    var xmlAppendChildren = datajs.xmlAppendChildren;
+    var xmlAttributes = datajs.xmlAttributes;
+    var xmlBaseURI = datajs.xmlBaseURI;
+    var xmlChildElements = datajs.xmlChildElements;
+    var xmlDom = datajs.xmlDom;
+    var xmlFirstChildElement = datajs.xmlFirstChildElement;
+    var xmlInnerText = datajs.xmlInnerText;
+    var xmlLocalName = datajs.xmlLocalName;
+    var xmlNamespaceURI = datajs.xmlNamespaceURI;
+    var xmlNewAttribute = datajs.xmlNewAttribute;
+    var xmlNewElement = datajs.xmlNewElement;
+    var xmlNodeValue = datajs.xmlNodeValue;
+    var xmlNS = datajs.xmlNS;
+    var xmlnsNS = datajs.xmlnsNS;
+    var xmlParse = datajs.xmlParse;
+    var xmlQualifiedName = datajs.xmlQualifiedName;
+    var xmlSerialize = datajs.xmlSerialize;
+    var xmlSiblingElement = datajs.xmlSiblingElement;
+    var w3org = datajs.w3org;
+
+    var dataItemTypeName = odata.dataItemTypeName;
+    var EDM_BINARY = odata.EDM_BINARY;
+    var EDM_BOOLEAN = odata.EDM_BOOLEAN;
+    var EDM_BYTE = odata.EDM_BYTE;
+    var EDM_DATETIME = odata.EDM_DATETIME;
+    var EDM_DATETIMEOFFSET = odata.EDM_DATETIMEOFFSET;
+    var EDM_DECIMAL = odata.EDM_DECIMAL;
+    var EDM_DOUBLE = odata.EDM_DOUBLE;
+    var EDM_GEOGRAPHY = odata.EDM_GEOGRAPHY;
+    var EDM_GEOGRAPHY_POINT = odata.EDM_GEOGRAPHY_POINT;
+    var EDM_GEOGRAPHY_LINESTRING = odata.EDM_GEOGRAPHY_LINESTRING;
+    var EDM_GEOGRAPHY_POLYGON = odata.EDM_GEOGRAPHY_POLYGON;
+    var EDM_GEOGRAPHY_COLLECTION = odata.EDM_GEOGRAPHY_COLLECTION;
+    var EDM_GEOGRAPHY_MULTIPOLYGON = odata.EDM_GEOGRAPHY_MULTIPOLYGON;
+    var EDM_GEOGRAPHY_MULTILINESTRING = odata.EDM_GEOGRAPHY_MULTILINESTRING;
+    var EDM_GEOGRAPHY_MULTIPOINT = odata.EDM_GEOGRAPHY_MULTIPOINT;
+    var EDM_GEOMETRY = odata.EDM_GEOMETRY;
+    var EDM_GEOMETRY_POINT = odata.EDM_GEOMETRY_POINT;
+    var EDM_GEOMETRY_LINESTRING = odata.EDM_GEOMETRY_LINESTRING;
+    var EDM_GEOMETRY_POLYGON = odata.EDM_GEOMETRY_POLYGON;
+    var EDM_GEOMETRY_COLLECTION = odata.EDM_GEOMETRY_COLLECTION;
+    var EDM_GEOMETRY_MULTIPOLYGON = odata.EDM_GEOMETRY_MULTIPOLYGON;
+    var EDM_GEOMETRY_MULTILINESTRING = odata.EDM_GEOMETRY_MULTILINESTRING;
+    var EDM_GEOMETRY_MULTIPOINT = odata.EDM_GEOMETRY_MULTIPOINT;
+    var EDM_GUID = odata.EDM_GUID;
+    var EDM_INT16 = odata.EDM_INT16;
+    var EDM_INT32 = odata.EDM_INT32;
+    var EDM_INT64 = odata.EDM_INT64;
+    var EDM_SBYTE = odata.EDM_SBYTE;
+    var EDM_SINGLE = odata.EDM_SINGLE;
+    var EDM_STRING = odata.EDM_STRING;
+    var EDM_TIME = odata.EDM_TIME;
+    var GEOJSON_POINT = odata.GEOJSON_POINT;
+    var GEOJSON_LINESTRING = odata.GEOJSON_LINESTRING;
+    var GEOJSON_POLYGON = odata.GEOJSON_POLYGON;
+    var GEOJSON_MULTIPOINT = odata.GEOJSON_MULTIPOINT;
+    var GEOJSON_MULTILINESTRING = odata.GEOJSON_MULTILINESTRING;
+    var GEOJSON_MULTIPOLYGON = odata.GEOJSON_MULTIPOLYGON;
+    var GEOJSON_GEOMETRYCOLLECTION = odata.GEOJSON_GEOMETRYCOLLECTION;
+    var formatDateTimeOffset = odata.formatDateTimeOffset;
+    var formatDuration = odata.formatDuration;
+    var getCollectionType = odata.getCollectionType;
+    var gmlNewODataSpatialValue = odata.gmlNewODataSpatialValue;
+    var gmlReadODataSpatialValue = odata.gmlReadODataSpatialValue;
+    var gmlXmlNs = odata.gmlXmlNs;
+    var handler = odata.handler;
+    var isCollection = odata.isCollection;
+    var isCollectionType = odata.isCollectionType;
+    var isDeferred = odata.isDeferred;
+    var isNamedStream = odata.isNamedStream;
+    var isGeographyEdmType = odata.isGeographyEdmType;
+    var isGeometryEdmType = odata.isGeometryEdmType;
+    var isPrimitive = odata.isPrimitive;
+    var isPrimitiveEdmType = odata.isPrimitiveEdmType;
+    var lookupComplexType = odata.lookupComplexType;
+    var lookupProperty = odata.lookupProperty;
+    var maxVersion = odata.maxVersion;
+    var navigationPropertyKind = odata.navigationPropertyKind;
+    var MAX_DATA_SERVICE_VERSION = odata.MAX_DATA_SERVICE_VERSION;
+    var parseBool = odata.parseBool;
+    var parseDateTime = odata.parseDateTime;
+    var parseDateTimeOffset = odata.parseDateTimeOffset;
+    var parseDuration = odata.parseDuration;
+    var parseTimezone = odata.parseTimezone;
+
+    // CONTENT START
+
+    var xmlMediaType = "application/xml";
+
+    var ado = http + "schemas.microsoft.com/ado/";      // http://schemas.microsoft.com/ado/
+    var adoDs = ado + "2007/08/dataservices";           // http://schemas.microsoft.com/ado/2007/08/dataservices
+
+    var edmxNs = ado + "2007/06/edmx";                  // http://schemas.microsoft.com/ado/2007/06/edmx
+    var edmNs1 = ado + "2006/04/edm";                   // http://schemas.microsoft.com/ado/2006/04/edm
+    var edmNs1_1 = ado + "2007/05/edm";                 // http://schemas.microsoft.com/ado/2007/05/edm
+    var edmNs1_2 = ado + "2008/01/edm";                 // http://schemas.microsoft.com/ado/2008/01/edm
+
+    // There are two valid namespaces for Edm 2.0
+    var edmNs2a = ado + "2008/09/edm";                  // http://schemas.microsoft.com/ado/2008/09/edm
+    var edmNs2b = ado + "2009/08/edm";                  // http://schemas.microsoft.com/ado/2009/08/edm
+
+    var edmNs3 = ado + "2009/11/edm";                   // http://schemas.microsoft.com/ado/2009/11/edm
+
+    var odataXmlNs = adoDs;                             // http://schemas.microsoft.com/ado/2007/08/dataservices
+    var odataMetaXmlNs = adoDs + "/metadata";           // http://schemas.microsoft.com/ado/2007/08/dataservices/metadata
+    var odataRelatedPrefix = adoDs + "/related/";       // http://schemas.microsoft.com/ado/2007/08/dataservices/related
+    var odataScheme = adoDs + "/scheme";                // http://schemas.microsoft.com/ado/2007/08/dataservices/scheme
+
+    var odataPrefix = "d";
+    var odataMetaPrefix = "m";
+
+    var createAttributeExtension = function (domNode, useNamespaceURI) {
+        /// <summary>Creates an extension object for the specified attribute.</summary>
+        /// <param name="domNode">DOM node for the attribute.</param>
+        /// <param name="useNamespaceURI" type="Boolean">Flag indicating if the namespaceURI property should be added to the extension object instead of the namespace property.</param>
+        /// <remarks>
+        ///    The useNamespaceURI flag is used to prevent a breaking change from older versions of datajs in which extension
+        ///    objects created for Atom extension attributes have the namespaceURI property instead of the namespace one.
+        ///    
+        ///    This flag and the namespaceURI property should be deprecated in future major versions of the library.
+        /// </remarks>
+        /// <returns type="Object">The new extension object.</returns>
+
+        djsassert(domNode.nodeType === 2, "createAttributeExtension - domNode is not an attribute node!!");
+        var extension = { name: xmlLocalName(domNode), value: domNode.value };
+        extension[useNamespaceURI ? "namespaceURI" : "namespace"] = xmlNamespaceURI(domNode);
+
+        return extension;
+    };
+
+    var createElementExtension = function (domNode, useNamespaceURI) {
+        /// <summary>Creates an extension object for the specified element.</summary>
+        /// <param name="domNode">DOM node for the element.</param>
+        /// <param name="useNamespaceURI" type="Boolean">Flag indicating if the namespaceURI property should be added to the extension object instead of the namespace property.</param>
+        /// <remarks>
+        ///    The useNamespaceURI flag is used to prevent a breaking change from older versions of datajs in which extension
+        ///    objects created for Atom extension attributes have the namespaceURI property instead of the namespace one.
+        ///    
+        ///    This flag and the namespaceURI property should be deprecated in future major versions of the library.
+        /// </remarks>
+        /// <returns type="Object">The new extension object.</returns>
+
+        djsassert(domNode.nodeType === 1, "createAttributeExtension - domNode is not an element node!!");
+
+        var attributeExtensions = [];
+        var childrenExtensions = [];
+
+        var i, len;
+        var attributes = domNode.attributes;
+        for (i = 0, len = attributes.length; i < len; i++) {
+            var attr = attributes[i];
+            if (xmlNamespaceURI(attr) !== xmlnsNS) {
+                attributeExtensions.push(createAttributeExtension(attr, useNamespaceURI));
+            }
+        }
+
+        var child = domNode.firstChild;
+        while (child != null) {
+            if (child.nodeType === 1) {
+                childrenExtensions.push(createElementExtension(child, useNamespaceURI));
+            }
+            child = child.nextSibling;
+        }
+
+        var extension = {
+            name: xmlLocalName(domNode),
+            value: xmlInnerText(domNode),
+            attributes: attributeExtensions,
+            children: childrenExtensions
+        };
+
+        extension[useNamespaceURI ? "namespaceURI" : "namespace"] = xmlNamespaceURI(domNode);
+        return extension;
+    };
+
+    var isCollectionItemElement = function (domElement) {
+        /// <summary>Checks whether the domElement is a collection item.</summary>
+        /// <param name="domElement">DOM element possibliy represnting a collection item.</param>
+        /// <returns type="Boolean">True if the domeElement belongs to the OData metadata namespace and its local name is "element"; false otherwise.</returns>
+
+        return xmlNamespaceURI(domElement) === odataXmlNs && xmlLocalName(domElement) === "element";
+    };
+
+    var makePropertyMetadata = function (type, extensions) {
+        /// <summary>Creates an object containing property metadata.</summary>
+        /// <param type="String" name="type">Property type name.</param>
+        /// <param type="Array" name="extensions">Array of attribute extension objects.</param>
+        /// <returns type="Object">Property metadata object cotaining type and extensions fields.</returns>
+
+        return { type: type, extensions: extensions };
+    };
+
+    var odataInferTypeFromPropertyXmlDom = function (domElement) {
+        /// <summary>Infers type of a property based on its xml DOM tree.</summary>
+        /// <param name="domElement">DOM element for the property.</param>
+        /// <returns type="String">Inferred type name; null if the type cannot be determined.</returns>
+
+        if (xmlFirstChildElement(domElement, gmlXmlNs)) {
+            return EDM_GEOMETRY;
+        }
+
+        var firstChild = xmlFirstChildElement(domElement, odataXmlNs);
+        if (!firstChild) {
+            return EDM_STRING;
+        }
+
+        if (isCollectionItemElement(firstChild)) {
+            var sibling = xmlSiblingElement(firstChild, odataXmlNs);
+            if (sibling && isCollectionItemElement(sibling)) {
+                // More than one <element> tag have been found, it can be safely assumed that this is a collection property.
+                return "Collection()";
+            }
+        }
+
+        return null;
+    };
+
+    var xmlReadODataPropertyAttributes = function (domElement) {
+        /// <summary>Reads the attributes of a property DOM element in an OData XML document.</summary>
+        /// <param name="domElement">DOM element for the property.</param>
+        /// <returns type="Object">Object containing the property type, if it is null, and its attribute extensions.</returns>
+
+        var type = null;
+        var isNull = false;
+        var extensions = [];
+
+        xmlAttributes(domElement, function (attribute) {
+            var nsURI = xmlNamespaceURI(attribute);
+            var localName = xmlLocalName(attribute);
+            var value = xmlNodeValue(attribute);
+
+            if (nsURI === odataMetaXmlNs) {
+                if (localName === "null") {
+                    isNull = (value.toLowerCase() === "true");
+                    return;
+                }
+
+                if (localName === "type") {
+                    type = value;
+                    return;
+                }
+            }
+
+            if (nsURI !== xmlNS && nsURI !== xmlnsNS) {
+                extensions.push(createAttributeExtension(attribute, true));
+                return;
+            }
+        });
+
+        return { type: (!type && isNull ? EDM_STRING : type), isNull: isNull, extensions: extensions };
+    };
+
+    var xmlReadODataProperty = function (domElement) {
+        /// <summary>Reads a property DOM element in an OData XML document.</summary>
+        /// <param name="domElement">DOM element for the property.</param>
+        /// <returns type="Object">Object with name, value, and metadata for the property.</returns>
+
+        if (xmlNamespaceURI(domElement) !== odataXmlNs) {
+            // domElement is not a proprety element because it is not in the odata xml namespace.
+            return null;
+        }
+
+        var propertyName = xmlLocalName(domElement);
+        var propertyAttributes = xmlReadODataPropertyAttributes(domElement);
+
+        var propertyIsNull = propertyAttributes.isNull;
+        var propertyType = propertyAttributes.type;
+
+        var propertyMetadata = makePropertyMetadata(propertyType, propertyAttributes.extensions);
+        var propertyValue = propertyIsNull ? null : xmlReadODataPropertyValue(domElement, propertyType, propertyMetadata);
+
+        return { name: propertyName, value: propertyValue, metadata: propertyMetadata };
+    };
+
+    var xmlReadODataPropertyValue = function (domElement, propertyType, propertyMetadata) {
+        /// <summary>Reads the value of a property in an OData XML document.</summary>
+        /// <param name="domElement">DOM element for the property.</param>
+        /// <param name="propertyType" type="String">Property type name.</param>
+        /// <param name="propertyMetadata" type="Object">Object that will store metadata about the property.</param>
+        /// <returns>Property value.</returns>
+
+        if (!propertyType) {
+            propertyType = odataInferTypeFromPropertyXmlDom(domElement);
+            propertyMetadata.type = propertyType;
+        }
+
+        var isGeograhpyType = isGeographyEdmType(propertyType);
+        if (isGeograhpyType || isGeometryEdmType(propertyType)) {
+            return xmlReadODataSpatialPropertyValue(domElement, propertyType, isGeograhpyType);
+        }
+
+        if (isPrimitiveEdmType(propertyType)) {
+            return xmlReadODataEdmPropertyValue(domElement, propertyType);
+        }
+
+        if (isCollectionType(propertyType)) {
+            return xmlReadODataCollectionPropertyValue(domElement, propertyType, propertyMetadata);
+        }
+
+        return xmlReadODataComplexPropertyValue(domElement, propertyType, propertyMetadata);
+    };
+
+    var xmlReadODataSpatialPropertyValue = function (domElement, propertyType, isGeography) {
+        /// <summary>Reads the value of an spatial property in an OData XML document.</summary>
+        /// <param name="property">DOM element for the spatial property.</param>
+        /// <param name="propertyType" type="String">Property type name.</param>
+        /// <param name="isGeography" type="Boolean" Optional="True">Flag indicating if the value uses a geographic reference system or not.<param>
+        /// <remarks>
+        ///    When using a geographic reference system, the first component of all the coordinates in each <pos> element in the GML DOM tree is the Latitude and
+        ///    will be deserialized as the second component of each <pos> element in the GML DOM tree.
+        /// </remarks>
+        /// <returns>Spatial property value in GeoJSON format.</returns>
+
+        var gmlRoot = xmlFirstChildElement(domElement, gmlXmlNs);
+        djsassert(gmlRoot, "xmlReadODataSpatialPropertyValue - domElement doesn't have a child element that belongs to the gml namespace!!");
+
+        var value = gmlReadODataSpatialValue(gmlRoot, isGeography);
+        value.__metadata = { type: propertyType };
+        return value;
+    };
+
+    var xmlReadODataEdmPropertyValue = function (domNode, propertyType) {
+        /// <summary>Reads the value of an EDM property in an OData XML document.</summary>
+        /// <param name="donNode">DOM node for the EDM property.</param>
+        /// <param name="propertyType" type="String">Property type name.</param>
+        /// <returns>EDM property value.</returns>
+
+        var propertyValue = xmlNodeValue(domNode) || "";
+
+        switch (propertyType) {
+            case EDM_BOOLEAN:
+                return parseBool(propertyValue);
+            case EDM_BINARY:
+            case EDM_DECIMAL:
+            case EDM_GUID:
+            case EDM_INT64:
+            case EDM_STRING:
+                return propertyValue;
+            case EDM_BYTE:
+            case EDM_INT16:
+            case EDM_INT32:
+            case EDM_SBYTE:
+                return parseInt10(propertyValue);
+            case EDM_DOUBLE:
+            case EDM_SINGLE:
+                return parseFloat(propertyValue);
+            case EDM_TIME:
+                return parseDuration(propertyValue);
+            case EDM_DATETIME:
+                return parseDateTime(propertyValue);
+            case EDM_DATETIMEOFFSET:
+                return parseDateTimeOffset(propertyValue);
+        }
+
+        return propertyValue;
+    };
+
+    var xmlReadODataComplexPropertyValue = function(domElement, propertyType, propertyMetadata) {
+        /// <summary>Reads the value of a complex type property in an OData XML document.</summary>
+        /// <param name="property">DOM element for the complex type property.</param>
+        /// <param name="propertyType" type="String">Property type name.</param>
+        /// <param name="propertyMetadata" type="Object">Object that will store metadata about the property.</param>
+        /// <returns type="Object">Complex type property value.</returns>
+
+        var propertyValue = { __metadata: { type: propertyType } };
+        xmlChildElements(domElement, function(child) {
+            var childProperty = xmlReadODataProperty(child);
+            var childPropertyName = childProperty.name;
+
+            propertyMetadata.properties = propertyMetadata.properties || {};
+            propertyMetadata.properties[childPropertyName] = childProperty.metadata;
+            propertyValue[childPropertyName] = childProperty.value;
+        });
+
+        return propertyValue;
+    };
+
+    var xmlReadODataCollectionPropertyValue = function (domElement, propertyType, propertyMetadata) {
+        /// <summary>Reads the value of a collection property in an OData XML document.</summary>
+        /// <param name="property">DOM element for the collection property.</param>
+        /// <param name="propertyType" type="String">Property type name.</param>
+        /// <param name="propertyMetadata" type="Object">Object that will store metadata about the property.</param>
+        /// <returns type="Object">Collection property value.</returns>
+
+        var items = [];
+        var itemsMetadata = propertyMetadata.elements = [];
+        var collectionType = getCollectionType(propertyType);
+
+        xmlChildElements(domElement, function (child) {
+            if (isCollectionItemElement(child)) {
+                var itemAttributes = xmlReadODataPropertyAttributes(child);
+                var itemExtensions = itemAttributes.extensions;
+                var itemType = itemAttributes.type || collectionType;
+                var itemMetadata = makePropertyMetadata(itemType, itemExtensions);
+
+                var item = xmlReadODataPropertyValue(child, itemType, itemMetadata);
+
+                items.push(item);
+                itemsMetadata.push(itemMetadata);
+            }
+        });
+
+        return { __metadata: { type: propertyType === "Collection()" ? null : propertyType }, results: items };
+    };
+
+    var readODataXmlDocument = function (xmlRoot, baseURI) {
+        /// <summary>Reads an OData link(s) producing an object model in return.</summary>
+        /// <param name="xmlRoot">Top-level element to read.</param>
+        /// <param name="baseURI" type="String">Base URI for normalizing relative URIs found in the XML payload.</param>
+        /// <returns type="Object">The object model representing the specified element.</returns>
+
+        if (xmlNamespaceURI(xmlRoot) === odataXmlNs) {
+            baseURI = xmlBaseURI(xmlRoot, baseURI);
+            var localName = xmlLocalName(xmlRoot);
+
+            if (localName === "links") {
+                return readLinks(xmlRoot, baseURI);
+            }
+            if (localName === "uri") {
+                return readUri(xmlRoot, baseURI);
+            }
+        }
+        return undefined;
+    };
+
+    var readLinks = function (linksElement, baseURI) {
+        /// <summary>Deserializes an OData XML links element.</summary>
+        /// <param name="linksElement">XML links element.</param>
+        /// <param name="baseURI" type="String">Base URI for normalizing relative URIs found in the XML payload.</param>
+        /// <returns type="Object">A new object representing the links collection.</returns>
+
+        var uris = [];
+
+        xmlChildElements(linksElement, function (child) {
+            if (xmlLocalName(child) === "uri" && xmlNamespaceURI(child) === odataXmlNs) {
+                uris.push(readUri(child, baseURI));
+            }
+        });
+
+        return { results: uris };
+    };
+
+    var readUri = function (uriElement, baseURI) {
+        /// <summary>Deserializes an OData XML uri element.</summary>
+        /// <param name="uriElement">XML uri element.</param>
+        /// <param name="baseURI" type="String">Base URI for normalizing relative URIs found in the XML payload.</param>
+        /// <returns type="Object">A new object representing the uri.</returns>
+
+        var uri = xmlInnerText(uriElement) || "";
+        return { uri: normalizeURI(uri, baseURI) };
+    };
+
+    var xmlODataInferSpatialValueGeoJsonType = function (value, edmType) {
+        /// <summary>Infers the GeoJSON type from the spatial property value and the edm type name.</summary>
+        /// <param name="value" type="Object">Spatial property value in GeoJSON format.</param>
+        /// <param name="edmType" type="String" mayBeNull="true" optional="true">Spatial property edm type.<param>
+        /// <remarks>
+        ///   If the edmType parameter is null, undefined, "Edm.Geometry" or "Edm.Geography", then the function returns
+        ///   the GeoJSON type indicated by the value's type property.
+        ///
+        ///   If the edmType parameter is specified or is not one of the base spatial types, then it is used to
+        ///   determine the GeoJSON type and the value's type property is ignored.
+        /// </remarks>
+        /// <returns>New DOM element in the GML namespace for the spatial value. </returns>
+
+        if (edmType === EDM_GEOMETRY || edmType === EDM_GEOGRAPHY) {
+            return value && value.type;
+        }
+
+        if (edmType === EDM_GEOMETRY_POINT || edmType === EDM_GEOGRAPHY_POINT) {
+            return GEOJSON_POINT;
+        }
+
+        if (edmType === EDM_GEOMETRY_LINESTRING || edmType === EDM_GEOGRAPHY_LINESTRING) {
+            return GEOJSON_LINESTRING;
+        }
+
+        if (edmType === EDM_GEOMETRY_POLYGON || edmType === EDM_GEOGRAPHY_POLYGON) {
+            return GEOJSON_POLYGON;
+        }
+
+        if (edmType === EDM_GEOMETRY_COLLECTION || edmType === EDM_GEOGRAPHY_COLLECTION) {
+            return GEOJSON_GEOMETRYCOLLECTION;
+        }
+
+        if (edmType === EDM_GEOMETRY_MULTIPOLYGON || edmType === EDM_GEOGRAPHY_MULTIPOLYGON) {
+            return GEOJSON_MULTIPOLYGON;
+        }
+
+        if (edmType === EDM_GEOMETRY_MULTILINESTRING || edmType === EDM_GEOGRAPHY_MULTILINESTRING) {
+            return GEOJSON_MULTILINESTRING;
+        }
+
+        if (edmType === EDM_GEOMETRY_MULTIPOINT || edmType === EDM_GEOGRAPHY_MULTIPOINT) {
+            return GEOJSON_MULTIPOINT;
+        }
+
+        djsassert(false, "gmlInferGeoJsonType - edm type <" + edmType + "> was unexpected!!");
+        return null;
+    };
+
+    var xmlNewODataMetaElement = function (dom, name, children) {
+        /// <summary>Creates a new DOM element in the OData metadata namespace.</summary>
+        /// <param name="dom">DOM document used for creating the new DOM Element.</param>
+        /// <param name="name" type="String">Local name of the OData metadata element to create.</param>
+        /// <param name="children" type="Array">Array containing DOM nodes or string values that will be added as children of the new DOM element.</param>
+        /// <returns>New DOM element in the OData metadata namespace.</returns>
+        /// <remarks>
+        ///    If a value in the children collection is a string, then a new DOM text node is going to be created
+        ///    for it and then appended as a child of the new DOM Element.
+        /// </remarks>
+
+        return xmlNewElement(dom, odataMetaXmlNs, xmlQualifiedName(odataMetaPrefix, name), children);
+    };
+
+    var xmlNewODataMetaAttribute = function (dom, name, value) {
+        /// <summary>Creates a new DOM attribute in the odata namespace.</summary>
+        /// <param name="dom">DOM document used for creating the new DOM Element.</param>
+        /// <param name="name" type="String">Local name of the OData attribute to create.</param>
+        /// <param name="value">Attribute value.</param>
+        /// <returns>New DOM attribute in the odata namespace.</returns>
+
+        return xmlNewAttribute(dom, odataMetaXmlNs, xmlQualifiedName(odataMetaPrefix, name), value);
+    };
+
+    var xmlNewODataElement = function (dom, name, children) {
+        /// <summary>Creates a new DOM element in the OData namespace.</summary>
+        /// <param name="dom">DOM document used for creating the new DOM Element.</param>
+        /// <param name="name" type="String">Local name of the OData element to create.</param>
+        /// <param name="children" type="Array">Array containing DOM nodes or string values that will be added as children of the new DOM element.</param>
+        /// <returns>New DOM element in the OData namespace.</returns>
+        /// <remarks>
+        ///    If a value in the children collection is a string, then a new DOM text node is going to be created
+        ///    for it and then appended as a child of the new DOM Element.
+        /// </remarks>
+
+        return xmlNewElement(dom, odataXmlNs, xmlQualifiedName(odataPrefix, name), children);
+    };
+
+    var xmlNewODataPrimitiveValue = function (value, typeName) {
+        /// <summary>Returns the string representation of primitive value for an OData XML document.</summary>
+        /// <param name="value">Primivite value to format.</param>
+        /// <param name="typeName" type="String" optional="true">Type name of the primitive value.</param>
+        /// <returns type="String">Formatted primitive value.</returns>
+
+        if (typeName === EDM_DATETIME || typeName === EDM_DATETIMEOFFSET || isDate(value)) {
+            return formatDateTimeOffset(value);
+        }
+        if (typeName === EDM_TIME) {
+            return formatDuration(value);
+        }
+        return value.toString();
+    };
+
+    var xmlNewODataElementInfo = function (domElement, dataServiceVersion) {
+        /// <summary>Creates an object that represents a new DOM element for an OData XML document and the data service version it requires.</summary>
+        /// <param name="domElement">New DOM element for an OData XML document.</param>
+        /// <param name="dataServiceVersion" type="String">Required data service version by the new DOM element.</param>
+        /// <returns type="Object">Object containing new DOM element and its required data service version.</returns>
+
+        return { element: domElement, dsv: dataServiceVersion };
+    };
+
+    var xmlNewODataProperty = function (dom, name, typeName, children) {
+        /// <summary>Creates a new DOM element for an entry property in an OData XML document.</summary>
+        /// <param name="dom">DOM document used for creating the new DOM Element.</param>
+        /// <param name="name" type="String">Property name.</param>
+        /// <param name="typeName" type="String" optional="true">Property type name.</param>
+        /// <param name="children" type="Array">Array containing DOM nodes or string values that will be added as children of the new DOM element.</param>
+        /// <remarks>
+        ///    If a value in the children collection is a string, then a new DOM text node is going to be created
+        ///    for it and then appended as a child of the new DOM Element.
+        /// </remarks>
+        /// <returns>New DOM element in the OData namespace for the entry property.</returns>
+
+        var typeAttribute = typeName ? xmlNewODataMetaAttribute(dom, "type", typeName) : null;
+        var property = xmlNewODataElement(dom, name, typeAttribute);
+        return xmlAppendChildren(property, children);
+    };
+
+    var xmlNewODataEdmProperty = function (dom, name, value, typeName) {
+        /// <summary>Creates a new DOM element for an EDM property in an OData XML document.</summary>
+        /// <param name="dom">DOM document used for creating the new DOM Element.</param>
+        /// <param name="name" type="String">Property name.</param>
+        /// <param name="value">Property value.</param>
+        /// <param name="typeName" type="String" optional="true">Property type name.</param>
+        /// <returns type="Object">
+        ///     Object containing the new DOM element in the OData namespace for the EDM property and the
+        ///     required data service version for this property.
+        /// </returns>
+
+        var propertyValue = xmlNewODataPrimitiveValue(value, typeName);
+        var property = xmlNewODataProperty(dom, name, typeName, propertyValue);
+        return xmlNewODataElementInfo(property, /*dataServiceVersion*/"1.0");
+    };
+
+    var xmlNewODataNullProperty = function (dom, name, typeName, model) {
+        /// <summary>Creates a new DOM element for a null property in an OData XML document.</summary>
+        /// <param name="dom">DOM document used for creating the new DOM Element.</param>
+        /// <param name="name" type="String">Property name.</param>
+        /// <param name="typeName" type="String" optional="true">Property type name.</param>
+        /// <param name="model" type="Object" optional="true">Object describing an OData conceptual schema.</param>
+        /// <remarks>
+        ///     If no typeName is specified, then it will be assumed that this is a primitive type property.
+        /// </remarks>
+        /// <returns type="Object">
+        ///     Object containing the new DOM element in the OData namespace for the null property and the 
+        ///     required data service version for this property.
+        /// </returns>
+
+        var nullAttribute = xmlNewODataMetaAttribute(dom, "null", "true");
+        var property = xmlNewODataProperty(dom, name, typeName, nullAttribute);
+        var dataServiceVersion = lookupComplexType(typeName, model) ? "2.0" : "1.0";
+
+        return xmlNewODataElementInfo(property, dataServiceVersion);
+    };
+
+    var xmlNewODataCollectionProperty = function (dom, name, value, typeName, collectionMetadata, collectionModel, model) {
+        /// <summary>Creates a new DOM element for a collection property in an OData XML document.</summary>
+        /// <param name="dom">DOM document used for creating the new DOM Element.</param>
+        /// <param name="name" type="String">Property name.</param>
+        /// <param name="value">Property value either as an array or an object representing a collection in the library's internal representation.</param>
+        /// <param name="typeName" type="String" optional="true">Property type name.</param>
+        /// <param name="collectionMetadata" type="Object" optional="true">Object containing metadata about the collection property.</param>
+        /// <param name="collectionModel" type="Object" optional="true">Object describing the collection property in an OData conceptual schema.</param>
+        /// <param name="model" type="Object" optional="true">Object describing an OData conceptual schema.</param>
+        /// <returns type="Object">
+        ///     Object containing the new DOM element in the OData namespace for the collection property and the
+        ///     required data service version for this property.
+        /// </returns>
+
+        var itemTypeName = getCollectionType(typeName);
+        var items = isArray(value) ? value : value.results;
+        var itemMetadata = typeName ? { type: itemTypeName} : {};
+        itemMetadata.properties = collectionMetadata.properties;
+
+        var xmlProperty = xmlNewODataProperty(dom, name, itemTypeName ? typeName : null);
+
+        var i, len;
+        for (i = 0, len = items.length; i < len; i++) {
+            var itemValue = items[i];
+            var item = xmlNewODataDataElement(dom, "element", itemValue, itemMetadata, collectionModel, model);
+
+            xmlAppendChild(xmlProperty, item.element);
+        }
+        return xmlNewODataElementInfo(xmlProperty, /*dataServiceVersion*/"3.0");
+    };
+
+    var xmlNewODataComplexProperty = function (dom, name, value, typeName, propertyMetadata, propertyModel, model) {
+        /// <summary>Creates a new DOM element for a complex type property in an OData XML document.</summary>
+        /// <param name="dom">DOM document used for creating the new DOM Element.</param>
+        /// <param name="name" type="String">Property name.</param>
+        /// <param name="value">Property value as an object in the library's internal representation.</param>
+        /// <param name="typeName" type="String" optional="true">Property type name.</param>
+        /// <param name="propertyMetadata" type="Object" optional="true">Object containing metadata about the complex type property.</param>
+        /// <param name="propertyModel" type="Object" optional="true">Object describing the complex type property in an OData conceptual schema.</param>
+        /// <param name="model" type="Object" optional="true">Object describing an OData conceptual schema.</param>
+        /// <returns type="Object">
+        ///     Object containing the new DOM element in the OData namespace for the complex type property and the
+        ///     required data service version for this property.
+        /// </returns>
+
+        var xmlProperty = xmlNewODataProperty(dom, name, typeName);
+        var complexTypePropertiesMetadata = propertyMetadata.properties || {};
+        var complexTypeModel = lookupComplexType(typeName, model) || {};
+
+        var dataServiceVersion = "1.0";
+
+        for (var key in value) {
+            if (key !== "__metadata") {
+                var memberValue = value[key];
+                var memberModel = lookupProperty(complexTypeModel.property, key);
+                var memberMetadata = complexTypePropertiesMetadata[key] || {};
+                var member = xmlNewODataDataElement(dom, key, memberValue, memberMetadata, memberModel, model);
+
+                dataServiceVersion = maxVersion(dataServiceVersion, member.dsv);
+                xmlAppendChild(xmlProperty, member.element);
+            }
+        }
+        return xmlNewODataElementInfo(xmlProperty, dataServiceVersion);
+    };
+
+    var xmlNewODataSpatialProperty = function (dom, name, value, typeName, isGeography) {
+        /// <summary>Creates a new DOM element for an EDM spatial property in an OData XML document.</summary>
+        /// <param name="dom">DOM document used for creating the new DOM Element.</param>
+        /// <param name="name" type="String">Property name.</param>
+        /// <param name="value" type="Object">GeoJSON object containing the property value.</param>
+        /// <param name="typeName" type="String" optional="true">Property type name.</param>
+        /// <returns type="Object">
+        ///     Object containing the new DOM element in the OData namespace for the EDM property and the
+        ///     required data service version for this property.
+        /// </returns>
+
+        var geoJsonType = xmlODataInferSpatialValueGeoJsonType(value, typeName);
+
+        var gmlRoot = gmlNewODataSpatialValue(dom, value, geoJsonType, isGeography);
+        var xmlProperty = xmlNewODataProperty(dom, name, typeName, gmlRoot);
+
+        return xmlNewODataElementInfo(xmlProperty, "3.0");
+    };
+
+    var xmlNewODataDataElement = function (dom, name, value, dataItemMetadata, dataItemModel, model) {
+        /// <summary>Creates a new DOM element for a data item in an entry, complex property, or collection property.</summary>
+        /// <param name="dom">DOM document used for creating the new DOM Element.</param>
+        /// <param name="name" type="String">Data item name.</param>
+        /// <param name="value" optional="true" mayBeNull="true">Value of the data item, if any.</param>
+        /// <param name="dataItemMetadata" type="Object" optional="true">Object containing metadata about the data item.</param>
+        /// <param name="dataItemModel" type="Object" optional="true">Object describing the data item in an OData conceptual schema.</param>
+        /// <param name="model" type="Object" optional="true">Object describing an OData conceptual schema.</param>
+        /// <returns type="Object">
+        ///     Object containing the new DOM element in the appropriate namespace for the data item and the
+        ///     required data service version for it.
+        /// </returns>
+
+        var typeName = dataItemTypeName(value, dataItemMetadata, dataItemModel);
+        if (isPrimitive(value)) {
+            return xmlNewODataEdmProperty(dom, name, value, typeName || EDM_STRING);
+        }
+
+        var isGeography = isGeographyEdmType(typeName);
+        if (isGeography || isGeometryEdmType(typeName)) {
+            return xmlNewODataSpatialProperty(dom, name, value, typeName, isGeography);
+        }
+
+        if (isCollection(value, typeName)) {
+            return xmlNewODataCollectionProperty(dom, name, value, typeName, dataItemMetadata, dataItemModel, model);
+        }
+
+        if (isNamedStream(value)) {
+            return null;
+        }
+
+        // This may be a navigation property.
+        var navPropKind = navigationPropertyKind(value, dataItemModel);
+        if (navPropKind !== null) {
+            return null;
+        }
+
+        if (value === null) {
+            return xmlNewODataNullProperty(dom, name, typeName);
+        }
+
+        djsassert(isObject(value), "xmlNewODataEntryProperty - property '" + name + "' is not an object");
+        return xmlNewODataComplexProperty(dom, name, value, typeName, dataItemMetadata, dataItemModel, model);
+    };
+
+    var odataNewLinkDocument = function (data) {
+        /// <summary>Writes the specified data into an OData XML document.</summary>
+        /// <param name="data">Data to write.</param>
+        /// <returns>The root of the DOM tree built.</returns>
+
+        if (data && isObject(data)) {
+            var dom = xmlDom();
+            return xmlAppendChild(dom, xmlNewODataElement(dom, "uri", data.uri));
+        }
+        // Allow for undefined to be returned.
+    };
+
+    var xmlParser = function (handler, text) {
+        /// <summary>Parses an OData XML document.</summary>
+        /// <param name="handler">This handler.</param>
+        /// <param name="text" type="String">Document text.</param>
+        /// <returns>An object representation of the document; undefined if not applicable.</returns>
+
+        if (text) {
+            var doc = xmlParse(text);
+            var root = xmlFirstChildElement(doc);
+            if (root) {
+                return readODataXmlDocument(root);
+            }
+        }
+
+        // Allow for undefined to be returned.
+    };
+
+    var xmlSerializer = function (handler, data, context) {
+        /// <summary>Serializes an OData XML object into a document.</summary>
+        /// <param name="handler">This handler.</param>
+        /// <param name="data" type="Object">Representation of feed or entry.</param>
+        /// <param name="context" type="Object">Object with parsing context.</param>
+        /// <returns>A text representation of the data object; undefined if not applicable.</returns>
+
+        var cType = context.contentType = context.contentType || contentType(xmlMediaType);
+        if (cType && cType.mediaType === xmlMediaType) {
+            return xmlSerialize(odataNewLinkDocument(data));
+        }
+        return undefined;
+    };
+
+    odata.xmlHandler = handler(xmlParser, xmlSerializer, xmlMediaType, MAX_DATA_SERVICE_VERSION);
+
+    // DATAJS INTERNAL START
+    odata.adoDs = adoDs;
+    odata.createAttributeExtension = createAttributeExtension;
+    odata.createElementExtension = createElementExtension;
+    odata.edmxNs = edmxNs;
+    odata.edmNs1 = edmNs1;
+    odata.edmNs1_1 = edmNs1_1;
+    odata.edmNs1_2 = edmNs1_2
+    odata.edmNs2a = edmNs2a;
+    odata.edmNs2b = edmNs2b;
+    odata.edmNs3 = edmNs3;
+    odata.odataMetaXmlNs = odataMetaXmlNs;
+    odata.odataMetaPrefix = odataMetaPrefix;
+    odata.odataXmlNs = odataXmlNs;
+    odata.odataPrefix = odataPrefix;
+    odata.odataScheme = odataScheme;
+    odata.odataRelatedPrefix = odataRelatedPrefix;
+    odata.xmlNewODataElement = xmlNewODataElement;
+    odata.xmlNewODataElementInfo = xmlNewODataElementInfo;
+    odata.xmlNewODataMetaAttribute = xmlNewODataMetaAttribute;
+    odata.xmlNewODataMetaElement = xmlNewODataMetaElement;
+    odata.xmlNewODataDataElement = xmlNewODataDataElement;
+    odata.xmlReadODataEdmPropertyValue = xmlReadODataEdmPropertyValue;
+    odata.xmlReadODataProperty = xmlReadODataProperty;
+
+    // DATAJS INTERNAL END
+
+    // CONTENT END
+})(this);
\ No newline at end of file
diff --git a/JSLib/src/odata.js b/JSLib/src/odata.js
new file mode 100644
index 0000000..98b80e2
--- /dev/null
+++ b/JSLib/src/odata.js
@@ -0,0 +1,158 @@
+// Copyright (c) Microsoft Open Technologies, Inc.  All rights reserved.
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal  in the Software without restriction, including without limitation the rights  to use, copy,
+// modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR  IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// WARRANTIES OF MERCHANTABILITY,  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+// odata.js
+
+(function (window, undefined) {
+
+    var datajs = window.datajs || {};
+    var odata = window.OData || {};
+
+    // Imports
+
+    var assigned = datajs.assigned;
+    var defined = datajs.defined;
+    var throwErrorCallback = datajs.throwErrorCallback;
+
+    var invokeRequest = odata.invokeRequest;
+    var MAX_DATA_SERVICE_VERSION = odata.MAX_DATA_SERVICE_VERSION;
+    var prepareRequest = odata.prepareRequest;
+    var metadataParser = odata.metadataParser;
+
+    // CONTENT START
+
+    var handlers = [odata.jsonHandler, odata.atomHandler, odata.xmlHandler, odata.textHandler];
+
+    var dispatchHandler = function (handlerMethod, requestOrResponse, context) {
+        /// <summary>Dispatches an operation to handlers.</summary>
+        /// <param name="handlerMethod" type="String">Name of handler method to invoke.</param>
+        /// <param name="requestOrResponse" type="Object">request/response argument for delegated call.</param>
+        /// <param name="context" type="Object">context argument for delegated call.</param>
+
+        var i, len;
+        for (i = 0, len = handlers.length; i < len && !handlers[i][handlerMethod](requestOrResponse, context); i++) {
+        }
+
+        if (i === len) {
+            throw { message: "no handler for data" };
+        }
+    };
+
+    odata.defaultSuccess = function (data) {
+        /// <summary>Default success handler for OData.</summary>
+        /// <param name="data">Data to process.</param>
+
+        window.alert(window.JSON.stringify(data));
+    };
+
+    odata.defaultError = throwErrorCallback;
+
+    odata.defaultHandler = {
+        read: function (response, context) {
+            /// <summary>Reads the body of the specified response by delegating to JSON and ATOM handlers.</summary>
+            /// <param name="response">Response object.</param>
+            /// <param name="context">Operation context.</param>
+
+            if (response && assigned(response.body) && response.headers["Content-Type"]) {
+                dispatchHandler("read", response, context);
+            }
+        },
+
+        write: function (request, context) {
+            /// <summary>Write the body of the specified request by delegating to JSON and ATOM handlers.</summary>
+            /// <param name="request">Reques tobject.</param>
+            /// <param name="context">Operation context.</param>
+
+            dispatchHandler("write", request, context);
+        },
+
+        maxDataServiceVersion: MAX_DATA_SERVICE_VERSION,
+        accept: "application/atomsvc+xml;q=0.8, application/json;odata=fullmetadata;q=0.7, application/json;q=0.5, */*;q=0.1"
+    };
+
+    odata.defaultMetadata = [];
+
+    odata.read = function (urlOrRequest, success, error, handler, httpClient, metadata) {
+        /// <summary>Reads data from the specified URL.</summary>
+        /// <param name="urlOrRequest">URL to read data from.</param>
+        /// <param name="success" type="Function" optional="true">Callback for a successful read operation.</param>
+        /// <param name="error" type="Function" optional="true">Callback for handling errors.</param>
+        /// <param name="handler" type="Object" optional="true">Handler for data serialization.</param>
+        /// <param name="httpClient" type="Object" optional="true">HTTP client layer.</param>
+        /// <param name="metadata" type="Object" optional="true">Conceptual metadata for this request.</param>
+
+        var request;
+        if (urlOrRequest instanceof String || typeof urlOrRequest === "string") {
+            request = { requestUri: urlOrRequest };
+        } else {
+            request = urlOrRequest;
+        }
+
+        return odata.request(request, success, error, handler, httpClient, metadata);
+    };
+
+    odata.request = function (request, s