Imported Upstream version 3.6.0

Former-commit-id: da6be194a6b1221998fc28233f2503bd61dd9d14
This commit is contained in:
Jo Shields
2014-08-13 10:39:27 +01:00
commit a575963da9
50588 changed files with 8155799 additions and 0 deletions

View File

@@ -0,0 +1,12 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Reflection;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("SPA")]
[assembly: AssemblyDescription("This is a dummy assembly to satisfy the build process")]
[assembly: Guid("6df72360-ebfc-4097-96fa-2ee418c04f7b")]

View File

@@ -0,0 +1,165 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory),Runtime.sln))\tools\WebStack.settings.targets" />
<PropertyGroup>
<ProductVersion>1.0.0</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{1ACEF677-B6A0-4680-A076-7893DE176D6B}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<ScriptOutputPath>$(OutputPath)</ScriptOutputPath>
<OutputPath>bin\$(Configuration)</OutputPath>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DefineConstants>DEBUG;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DefineConstants>TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'CodeCov|AnyCPU' ">
<DefineConstants>DEBUG;TRACE;CODECOV</DefineConstants>
</PropertyGroup>
<ItemGroup>
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<Content Include="upshot\AssociatedEntitiesView.js" />
<Content Include="upshot\Core.js" />
<Content Include="upshot\DataContext.js" />
<Content Include="upshot\DataProvider.js" />
<Content Include="upshot\DataProvider.OData.js" />
<Content Include="upshot\DataProvider.ria.js" />
<Content Include="upshot\DataSource.js" />
<Content Include="upshot\EntitySet.js" />
<Content Include="upshot\EntitySource.js" />
<Content Include="upshot\EntityView.js" />
<Content Include="upshot\LocalDataSource.js" />
<Content Include="upshot\Metadata.js" />
<Content Include="upshot\Observability.js" />
<Content Include="upshot\RemoteDataSource.js" />
<Content Include="upshot\Upshot.Compat.jQueryUI.js" />
<Content Include="upshot\Upshot.Compat.JsViews.js" />
<Content Include="upshot\Upshot.Compat.Knockout.js" />
<Content Include="upshot\Upshot.Compat.WinJS.js" />
<Content Include="upshot\upshot.dataview.js" />
<Content Include="nav\nav.js">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>nav.coffee</DependentUpon>
</Content>
<Content Include="nav\nav.transitions.js">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>nav.transitions.coffee</DependentUpon>
</Content>
</ItemGroup>
<ItemGroup>
<IntelliSense Include="upshot\IntelliSense\Dependencies.js" />
<IntelliSense Include="upshot\IntelliSense\jquery-1.5.2-vsdoc.js" />
<IntelliSense Include="upshot\IntelliSense\knockout-2.0.0.debug.js" />
<IntelliSense Include="upshot\IntelliSense\References.js" />
</ItemGroup>
<ItemGroup>
<None Include="nav\nav.coffee">
<Generator>CoffeeScriptGenerator</Generator>
<LastGenOutput>nav.js</LastGenOutput>
</None>
<None Include="nav\nav.transitions.coffee">
<Generator>CoffeeScriptGenerator</Generator>
<LastGenOutput>nav.transitions.js</LastGenOutput>
</None>
</ItemGroup>
<PropertyGroup>
<JSKnownGlobalNames>jQuery,$,upshot,WinJS,OData,JSON,ko</JSKnownGlobalNames>
</PropertyGroup>
<!-- We don't use these targets, but VS will try to upgrade the project when they aren't present -->
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<ProjectExtensions />
<!-- SPA build targets -->
<Import Project="SPA.targets" />
<Target Name="CompileJs">
<!-- purpose of compile step here is to do some basic syntax checking on the files prior to packaging -->
<ItemGroup>
<!-- for validation, compile all js files in the project; this doesn't mean they will be included in the package -->
<_CompileJavascript Include="@(Content->'%(FullPath)')" Condition="%(Extension) == '.js'" />
</ItemGroup>
</Target>
<Target Name="BuildUpshot" BeforeTargets="Build">
<PropertyGroup>
<UpshotPkgOutputFile>$(ScriptOutputPath)\upshot.js</UpshotPkgOutputFile>
</PropertyGroup>
<ItemGroup>
<PkgOutputFile Include="$(UpshotPkgOutputFile)" />
<!-- order matters here -->
<UpshotPkg Include="upshot\Core.js" />
<UpshotPkg Include="upshot\Observability.js" />
<UpshotPkg Include="upshot\Metadata.js" />
<UpshotPkg Include="upshot\EntitySource.js" />
<UpshotPkg Include="upshot\EntityView.js" />
<UpshotPkg Include="upshot\DataSource.js" />
<UpshotPkg Include="upshot\EntitySet.js" />
<UpshotPkg Include="upshot\DataContext.js" />
<UpshotPkg Include="upshot\DataProvider.js" />
<UpshotPkg Include="upshot\RemoteDataSource.js" />
<UpshotPkg Include="upshot\AssociatedEntitiesView.js" />
<UpshotPkg Include="upshot\LocalDataSource.js" />
<UpshotPkg Include="upshot\DataProvider.OData.js" />
<UpshotPkg Include="upshot\DataProvider.ria.js" />
</ItemGroup>
<PropertyGroup>
<UpshotJQueryPkgOutputFile>$(ScriptOutputPath)\Upshot.Compat.jQueryUI.js</UpshotJQueryPkgOutputFile>
</PropertyGroup>
<ItemGroup>
<PkgOutputFile Include="$(UpshotJQueryPkgOutputFile)" />
<UpshotJQueryPkg Include="upshot\Upshot.Compat.jQueryUI.js" />
</ItemGroup>
<PropertyGroup>
<UpshotJsViewsPkgOutputFile>$(ScriptOutputPath)\Upshot.Compat.JsViews.js</UpshotJsViewsPkgOutputFile>
</PropertyGroup>
<ItemGroup>
<PkgOutputFile Include="$(UpshotJsViewsPkgOutputFile)" />
<UpshotJsViewsPkg Include="upshot\Upshot.Compat.JsViews.js" />
</ItemGroup>
<PropertyGroup>
<UpshotKnockoutPkgOutputFile>$(ScriptOutputPath)\Upshot.Compat.Knockout.js</UpshotKnockoutPkgOutputFile>
</PropertyGroup>
<ItemGroup>
<PkgOutputFile Include="$(UpshotKnockoutPkgOutputFile)" />
<UpshotKnockoutPkg Include="upshot\Upshot.Compat.Knockout.js" />
</ItemGroup>
<PropertyGroup>
<UpshotDataViewPkgOutputFile>$(ScriptOutputPath)\upshot.dataview.js</UpshotDataViewPkgOutputFile>
</PropertyGroup>
<ItemGroup>
<PkgOutputFile Include="$(UpshotDataViewPkgOutputFile)" />
<UpshotDataViewPkg Include="upshot\upshot.dataview.js" />
</ItemGroup>
<ProcessScriptFiles ScriptFiles="@(UpshotPkg)" OutputFile="$(UpshotPkgOutputFile)" />
<ProcessScriptFiles ScriptFiles="@(UpshotJQueryPkg)" OutputFile="$(UpshotJQueryPkgOutputFile)" />
<ProcessScriptFiles ScriptFiles="@(UpshotJsViewsPkg)" OutputFile="$(UpshotJsViewsPkgOutputFile)" />
<ProcessScriptFiles ScriptFiles="@(UpshotKnockoutPkg)" OutputFile="$(UpshotKnockoutPkgOutputFile)" />
<ProcessScriptFiles ScriptFiles="@(UpshotDataViewPkg)" OutputFile="$(UpshotDataViewPkgOutputFile)" />
</Target>
<Target Name="BuildNav" BeforeTargets="Build">
<ItemGroup>
<NavJsFiles Include="nav\nav.js;nav\nav.transitions.js" />
</ItemGroup>
<Copy SourceFiles="@(NavJsFiles)" DestinationFolder="$(ScriptOutputPath)" />
<ItemGroup>
<NavPkgOutputFile Include="$(ScriptOutputPath)\nav.js" />
<NavPkgOutputFile Include="$(ScriptOutputPath)\nav.transitions.js" />
</ItemGroup>
</Target>
<Target Name="CleanJs" BeforeTargets="Clean">
<ItemGroup>
<CleanPkgOutputFile Include="$(ScriptOutputPath)\upshot.js" />
<CleanPkgOutputFile Include="$(ScriptOutputPath)\Upshot.Compat.jQueryUI.js" />
<CleanPkgOutputFile Include="$(ScriptOutputPath)\Upshot.Compat.JsViews.js" />
<CleanPkgOutputFile Include="$(ScriptOutputPath)\Upshot.Compat.Knockout.js" />
<CleanPkgOutputFile Include="$(ScriptOutputPath)\upshot.dataview.js" />
<CleanPkgOutputFile Include="$(ScriptOutputPath)\nav.js" />
<CleanPkgOutputFile Include="$(ScriptOutputPath)\nav.transitions.js" />
</ItemGroup>
<Delete Files="@(CleanPkgOutputFile->'%(RootDir)%(Directory)%(Filename)%(Extension)')" />
</Target>
</Project>

View File

@@ -0,0 +1,68 @@
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<UsingTask TaskName="ProcessScriptFiles" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll">
<ParameterGroup>
<ScriptFiles ParameterType="Microsoft.Build.Framework.ITaskItem[]" Required="true" />
<OutputFile ParameterType="Microsoft.Build.Framework.ITaskItem" Required="true" />
</ParameterGroup>
<Task>
<Using Namespace="System"/>
<Using Namespace="System.IO"/>
<Using Namespace="System.Text"/>
<Using Namespace="System.Text.RegularExpressions"/>
<Using Namespace="Microsoft.Build.Framework"/>
<Using Namespace="Microsoft.Build.Utilities"/>
<Code Type="Fragment" Language="cs">
<![CDATA[
try
{
string output = string.Empty;
// Adding copyright
output +=
"// Copyright (c) Microsoft. All rights reserved." + Environment.NewLine +
"// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation" + Environment.NewLine +
"// files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy," + Environment.NewLine +
"// modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the" + Environment.NewLine +
"// Software is furnished to do so, subject to the following conditions:" + Environment.NewLine +
"//" + Environment.NewLine +
"// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software." + Environment.NewLine +
"//" + Environment.NewLine +
"// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE" + Environment.NewLine +
"// WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR" + Environment.NewLine +
"// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE," + Environment.NewLine +
"// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE." + Environment.NewLine;
// Removing /// <reference path="script.js" /> lines
// Removing ///#RESTORE from lines
Regex regex = new Regex("/// <reference path=\"[^\"]*\" />\r+\n|///#RESTORE ", RegexOptions.Multiline | RegexOptions.Compiled);
foreach (ITaskItem file in ScriptFiles)
{
string fullPath = Path.GetFullPath(file.ItemSpec);
// Adding the original file name
output +=
Environment.NewLine +
"///" + Environment.NewLine +
"/// " + Path.GetFileName(fullPath) + Environment.NewLine +
"///" + Environment.NewLine +
Environment.NewLine;
output += regex.Replace(File.ReadAllText(fullPath), "");
}
string outputPath = Path.GetFullPath(OutputFile.ItemSpec);
File.WriteAllText(outputPath, output, Encoding.UTF8);
return true;
}
catch (Exception ex)
{
Log.LogErrorFromException(ex);
return false;
}
]]>
</Code>
</Task>
</UsingTask>
</Project>

View File

@@ -0,0 +1,222 @@
###!
nav.js v0.1 - (c) Microsoft Corporation
###
# Small helper function for creating jQuery-style read/write function wrappers around an underlying value
readWriteValue = (initialValue) ->
currentValue = initialValue
return () ->
if arguments.length > 0 then currentValue = arguments[0]
currentValue
class window.NavHistory
constructor: (opts) ->
@options = opts || {}
@options.params = @_extend({}, @options.params, @_asString) # Ensure all defaults are strings
@isLinkedToUrl = false
# If KO is referenced, @position and @entries will be observable. Otherwise, use plain JS objects.
# You can override this via @options.ko if you want.
useKo = if 'ko' of @options then @options.ko else ko?.observable?
@position = if useKo then ko.observable(-1) else readWriteValue(-1)
@entries = if useKo then ko.observableArray([]) else []
length: => @_entriesArray().length
relative: (offset) => @_entriesArray()[@position() + offset] || {}
current: => @relative(0)
params: => @current().params || {}
loadedData: => @current().loadedData
back: => if @position() > 0 then @navigateAll(@relative(-1).params)
forward: => if @position() < @length() - 1 then @navigateAll(@relative(1).params)
_entriesArray: => if typeof @entries == 'function' then @entries() else @entries
initialize: (opts) ->
if opts?.linkToUrl
@_linkToUrl()
else
@navigateAll(opts?.params || {})
return this
navigate: (newParams, opts) =>
# Retain any params not specified
newParamsPlusCurrent = @_extend(@_extend({}, @params()), newParams)
@navigateAll(newParamsPlusCurrent, opts)
navigateAll: (newParams, opts) =>
newParams = @_normalizeParams(newParams)
isBack = false
isForward = false
isNoChange = false
transition = opts?.transition
navEntry = null
if @length() && @_propsAreEqual(newParams, @params())
# We're already there. No need to create a new nav entry.
if opts?.force
isNoChange = true
navEntry = @current()
else
# In the absence of a "force" directive, we don't even need to navigate at all
return
else if @_propsAreEqual(newParams, @relative(-1)?.params)
# It's a "back" - reuse existing transition if no new one was given
isBack = true
transition = transition || @current().savedTransition
navEntry = @relative(-1)
else if @_propsAreEqual(newParams, @relative(1)?.params)
# It's a "forward" - reuse existing transition if no new one was given
isForward = true
navEntry = @relative(1)
transition = transition || @current().savedTransition
else
# Entirely new navigation - create new entry
navEntry = { params: newParams, navEntryId: "navEntry_" + @_getUniqueSequenceValue() }
# Extra param for beforeNavigate/onNavigate callbacks
# Note that navInfo.transition is what will be used during the current navigation (regardless of direction)
# whereas navEntry.forwardTransition is what we're storing in case we need to reuse transitions in the future
navInfo = { isFirst: @length() == 0, isBack: isBack, isForward: isForward, transition: transition }
beforeNavigateCallback = () =>
if isBack
# Consider also doing a History.back() here. This feels more natural if there's only a single NavHistory instance,
# because then programmatic back/forwards is the same as using the browser controls. But if you have multiple NavHistory
# instances, you can't be sure that History.back() will take you to the same place that this instance has logged.
@position(@position() - 1)
else if isForward
# As above comment, except with History.forwards
@position(@position() + 1)
else if !isNoChange
# Clear "forward" items, and add new entry
deleteCount = @length() - @position() - 1
@entries().splice(@position() + 1, deleteCount, navEntry)
# Move to it, possibly by removing the first entry if we've exceeded capacity
if @options.maxEntries && @length() > @options.maxEntries
@entries.shift() # This will notify subscribers to @entries, if it's observable
else
@position(@position() + 1)
# Notify subscribers to @entries, if it's observable
if typeof @entries.valueHasMutated == 'function'
@entries.valueHasMutated()
if !isBack && navInfo.transition
@current().savedTransition = navInfo.transition
# Consider only using pushState for totally new navigations (not isBack and not isForwards), and using History.back()
# and History.forwards() as in above comments.
if @isLinkedToUrl && (opts?.updateUrl != false) && !isNoChange
updatedQueryString = @_getUpdatedQueryString(@params())
window.NavHistory.historyProvider.pushState({ url: updatedQueryString })
if @options.onNavigate
@options.onNavigate.call(this, @current(), navInfo)
if !@options.beforeNavigate
beforeNavigateCallback()
else
threadLoadToken = @objectLoadToken = {}
@options.beforeNavigate.call(this, navEntry, navInfo, ((loadedData) =>
# Ignore the callback unless threadLoadToken still matches. Avoids race conditions.
if threadLoadToken == @objectLoadToken
if loadedData != undefined
navEntry.loadedData = loadedData
beforeNavigateCallback()
))
this
_asString: (val) -> if val == null || val == undefined then "" else val.toString()
_extend: (target, source, mapFunction) ->
for own key, value of source
target[key] = if mapFunction then mapFunction(value) else value
target
_normalizeParams: (params) ->
# Normalized params are purely strings, and contain a value for every default
defaults = @options.params || {}
@_extend(@_extend({}, defaults), params || {}, @_asString)
_propsAreEqual: (obj1, obj2) ->
if !(obj1 && obj2)
return obj1 == obj2
for own obj1key, obj1value of obj1
if obj2[obj1key] != obj1value then return false
for own obj2key, obj2value of obj2
if obj1[obj2key] != obj2value then return false
true
_parseQueryString: (url) ->
if url.indexOf('?') < 0 then return {}
query = url.substring(url.lastIndexOf('?') + 1)
result = {}
for pair in query.split("&")
tokens = pair.split("=")
if (tokens.length == 2)
result[tokens[0]] = decodeURIComponent(tokens[1])
result
_formatQueryString: (params) ->
formattedUrl = '?'
for own key, value of params
if formattedUrl != '?'
formattedUrl += '&'
formattedUrl += key + '=' + encodeURIComponent(value)
formattedUrl
_getUpdatedQueryString: (params) ->
# Take the existing query string...
allUrlParams = @_parseQueryString(window.NavHistory.historyProvider.getState().url)
# ... update the params based on the supplied arg (removing any that correspond to our default)
for own key, defaultValue of @options.params
suppliedValue = params[key]
if suppliedValue == defaultValue
delete allUrlParams[key]
else
allUrlParams[key] = suppliedValue
# ... and return the resulting querystring
@_formatQueryString(allUrlParams)
_getUniqueSequenceValue: () ->
NavHistory._sequence = NavHistory._sequence || 0
(NavHistory._sequence++).toString()
_linkToUrl: ->
@isLinkedToUrl = true
onStateChange = =>
# Get the subset of URL params that applies to this NavHistory instance
applicableParams = {}
allUrlParams = @_parseQueryString(window.NavHistory.historyProvider.getState().url)
defaults = @options.params || {}
for own key, value of allUrlParams when defaults.hasOwnProperty(key)
applicableParams[key] = value
# ... and navigate to the new params
@navigateAll(applicableParams, { updateUrl: false })
# Perform initial navigation (loads state from the requested URL)
onStateChange()
# Respond to future URL changes too
window.NavHistory.historyProvider.onStateChange(onStateChange)
# Default history provider is history.js
window.NavHistory.historyProvider =
onStateChange: (handler) -> History.Adapter.bind(window, 'statechange', handler)
pushState: (data) -> History.pushState(null, null, data.url)
getState: -> History.getState()
back: -> History.back()
# Helper to display a specific element, simultaneously hiding its siblings. Useful for toggling panes, tabs, etc.
# Does not require any DOM library.
window.NavHistory.showPane = (elementId, navInfo) ->
elemToShow = document.getElementById(elementId)
if elemToShow
for sibling in elemToShow.parentNode.childNodes when sibling.nodeType == 1
sibling.style.display = 'none'
elemToShow.style.display = 'block'

View File

@@ -0,0 +1,317 @@
(function() {
/*!
nav.js v0.1 - (c) Microsoft Corporation
*/
var readWriteValue;
var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, __hasProp = Object.prototype.hasOwnProperty;
readWriteValue = function(initialValue) {
var currentValue;
currentValue = initialValue;
return function() {
if (arguments.length > 0) currentValue = arguments[0];
return currentValue;
};
};
window.NavHistory = (function() {
function NavHistory(opts) {
this.navigateAll = __bind(this.navigateAll, this);
this.navigate = __bind(this.navigate, this);
this._entriesArray = __bind(this._entriesArray, this);
this.forward = __bind(this.forward, this);
this.back = __bind(this.back, this);
this.loadedData = __bind(this.loadedData, this);
this.params = __bind(this.params, this);
this.current = __bind(this.current, this);
this.relative = __bind(this.relative, this);
this.length = __bind(this.length, this);
var useKo;
this.options = opts || {};
this.options.params = this._extend({}, this.options.params, this._asString);
this.isLinkedToUrl = false;
useKo = 'ko' in this.options ? this.options.ko : (typeof ko !== "undefined" && ko !== null ? ko.observable : void 0) != null;
this.position = useKo ? ko.observable(-1) : readWriteValue(-1);
this.entries = useKo ? ko.observableArray([]) : [];
}
NavHistory.prototype.length = function() {
return this._entriesArray().length;
};
NavHistory.prototype.relative = function(offset) {
return this._entriesArray()[this.position() + offset] || {};
};
NavHistory.prototype.current = function() {
return this.relative(0);
};
NavHistory.prototype.params = function() {
return this.current().params || {};
};
NavHistory.prototype.loadedData = function() {
return this.current().loadedData;
};
NavHistory.prototype.back = function() {
if (this.position() > 0) return this.navigateAll(this.relative(-1).params);
};
NavHistory.prototype.forward = function() {
if (this.position() < this.length() - 1) {
return this.navigateAll(this.relative(1).params);
}
};
NavHistory.prototype._entriesArray = function() {
if (typeof this.entries === 'function') {
return this.entries();
} else {
return this.entries;
}
};
NavHistory.prototype.initialize = function(opts) {
if (opts != null ? opts.linkToUrl : void 0) {
this._linkToUrl();
} else {
this.navigateAll((opts != null ? opts.params : void 0) || {});
}
return this;
};
NavHistory.prototype.navigate = function(newParams, opts) {
var newParamsPlusCurrent;
newParamsPlusCurrent = this._extend(this._extend({}, this.params()), newParams);
return this.navigateAll(newParamsPlusCurrent, opts);
};
NavHistory.prototype.navigateAll = function(newParams, opts) {
var beforeNavigateCallback, isBack, isForward, isNoChange, navEntry, navInfo, threadLoadToken, transition, _ref, _ref2;
var _this = this;
newParams = this._normalizeParams(newParams);
isBack = false;
isForward = false;
isNoChange = false;
transition = opts != null ? opts.transition : void 0;
navEntry = null;
if (this.length() && this._propsAreEqual(newParams, this.params())) {
if (opts != null ? opts.force : void 0) {
isNoChange = true;
navEntry = this.current();
} else {
return;
}
} else if (this._propsAreEqual(newParams, (_ref = this.relative(-1)) != null ? _ref.params : void 0)) {
isBack = true;
transition = transition || this.current().savedTransition;
navEntry = this.relative(-1);
} else if (this._propsAreEqual(newParams, (_ref2 = this.relative(1)) != null ? _ref2.params : void 0)) {
isForward = true;
navEntry = this.relative(1);
transition = transition || this.current().savedTransition;
} else {
navEntry = {
params: newParams,
navEntryId: "navEntry_" + this._getUniqueSequenceValue()
};
}
navInfo = {
isFirst: this.length() === 0,
isBack: isBack,
isForward: isForward,
transition: transition
};
beforeNavigateCallback = function() {
var deleteCount, updatedQueryString;
if (isBack) {
_this.position(_this.position() - 1);
} else if (isForward) {
_this.position(_this.position() + 1);
} else if (!isNoChange) {
deleteCount = _this.length() - _this.position() - 1;
_this.entries().splice(_this.position() + 1, deleteCount, navEntry);
if (_this.options.maxEntries && _this.length() > _this.options.maxEntries) {
_this.entries.shift();
} else {
_this.position(_this.position() + 1);
if (typeof _this.entries.valueHasMutated === 'function') {
_this.entries.valueHasMutated();
}
}
}
if (!isBack && navInfo.transition) {
_this.current().savedTransition = navInfo.transition;
}
if (_this.isLinkedToUrl && ((opts != null ? opts.updateUrl : void 0) !== false) && !isNoChange) {
updatedQueryString = _this._getUpdatedQueryString(_this.params());
window.NavHistory.historyProvider.pushState({
url: updatedQueryString
});
}
if (_this.options.onNavigate) {
return _this.options.onNavigate.call(_this, _this.current(), navInfo);
}
};
if (!this.options.beforeNavigate) {
beforeNavigateCallback();
} else {
threadLoadToken = this.objectLoadToken = {};
this.options.beforeNavigate.call(this, navEntry, navInfo, (function(loadedData) {
if (threadLoadToken === _this.objectLoadToken) {
if (loadedData !== void 0) navEntry.loadedData = loadedData;
return beforeNavigateCallback();
}
}));
}
return this;
};
NavHistory.prototype._asString = function(val) {
if (val === null || val === void 0) {
return "";
} else {
return val.toString();
}
};
NavHistory.prototype._extend = function(target, source, mapFunction) {
var key, value;
for (key in source) {
if (!__hasProp.call(source, key)) continue;
value = source[key];
target[key] = mapFunction ? mapFunction(value) : value;
}
return target;
};
NavHistory.prototype._normalizeParams = function(params) {
var defaults;
defaults = this.options.params || {};
return this._extend(this._extend({}, defaults), params || {}, this._asString);
};
NavHistory.prototype._propsAreEqual = function(obj1, obj2) {
var obj1key, obj1value, obj2key, obj2value;
if (!(obj1 && obj2)) return obj1 === obj2;
for (obj1key in obj1) {
if (!__hasProp.call(obj1, obj1key)) continue;
obj1value = obj1[obj1key];
if (obj2[obj1key] !== obj1value) return false;
}
for (obj2key in obj2) {
if (!__hasProp.call(obj2, obj2key)) continue;
obj2value = obj2[obj2key];
if (obj1[obj2key] !== obj2value) return false;
}
return true;
};
NavHistory.prototype._parseQueryString = function(url) {
var pair, query, result, tokens, _i, _len, _ref;
if (url.indexOf('?') < 0) return {};
query = url.substring(url.lastIndexOf('?') + 1);
result = {};
_ref = query.split("&");
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
pair = _ref[_i];
tokens = pair.split("=");
if (tokens.length === 2) result[tokens[0]] = decodeURIComponent(tokens[1]);
}
return result;
};
NavHistory.prototype._formatQueryString = function(params) {
var formattedUrl, key, value;
formattedUrl = '?';
for (key in params) {
if (!__hasProp.call(params, key)) continue;
value = params[key];
if (formattedUrl !== '?') formattedUrl += '&';
formattedUrl += key + '=' + encodeURIComponent(value);
}
return formattedUrl;
};
NavHistory.prototype._getUpdatedQueryString = function(params) {
var allUrlParams, defaultValue, key, suppliedValue, _ref;
allUrlParams = this._parseQueryString(window.NavHistory.historyProvider.getState().url);
_ref = this.options.params;
for (key in _ref) {
if (!__hasProp.call(_ref, key)) continue;
defaultValue = _ref[key];
suppliedValue = params[key];
if (suppliedValue === defaultValue) {
delete allUrlParams[key];
} else {
allUrlParams[key] = suppliedValue;
}
}
return this._formatQueryString(allUrlParams);
};
NavHistory.prototype._getUniqueSequenceValue = function() {
NavHistory._sequence = NavHistory._sequence || 0;
return (NavHistory._sequence++).toString();
};
NavHistory.prototype._linkToUrl = function() {
var onStateChange;
var _this = this;
this.isLinkedToUrl = true;
onStateChange = function() {
var allUrlParams, applicableParams, defaults, key, value;
applicableParams = {};
allUrlParams = _this._parseQueryString(window.NavHistory.historyProvider.getState().url);
defaults = _this.options.params || {};
for (key in allUrlParams) {
if (!__hasProp.call(allUrlParams, key)) continue;
value = allUrlParams[key];
if (defaults.hasOwnProperty(key)) applicableParams[key] = value;
}
return _this.navigateAll(applicableParams, {
updateUrl: false
});
};
onStateChange();
return window.NavHistory.historyProvider.onStateChange(onStateChange);
};
return NavHistory;
})();
window.NavHistory.historyProvider = {
onStateChange: function(handler) {
return History.Adapter.bind(window, 'statechange', handler);
},
pushState: function(data) {
return History.pushState(null, null, data.url);
},
getState: function() {
return History.getState();
},
back: function() {
return History.back();
}
};
window.NavHistory.showPane = function(elementId, navInfo) {
var elemToShow, sibling, _i, _len, _ref;
elemToShow = document.getElementById(elementId);
if (elemToShow) {
_ref = elemToShow.parentNode.childNodes;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
sibling = _ref[_i];
if (sibling.nodeType === 1) sibling.style.display = 'none';
}
return elemToShow.style.display = 'block';
}
};
}).call(this);

View File

@@ -0,0 +1,231 @@
###!
nav.transitions.js v0.1 - (c) Microsoft Corporation
###
# Pane transitions. Currently assumes availability of XUI. Need to generalise for jQuery.
$ = x$
# Feature detection
features =
vendor:
if (/webkit/i).test(navigator.appVersion) then 'webkit'
else if (/firefox/i).test(navigator.userAgent) then 'Moz'
else if 'opera' of window then 'O'
else ''
isAndroid: (/android/gi).test(navigator.appVersion)
features.useCssTransform = (!features.isAndroid) && (features.vendor + 'Transform' of document.documentElement.style)
features.cssTransformPrefix = "-" + features.vendor.toLowerCase() + "-"
features.transitionEndEvent =
if features.vendor == 'webkit' then 'webkitTransitionEnd'
else if features.vendor == 'O' then 'oTransitionEnd'
else 'transitionend'
$.isTouch = 'ontouchstart' of document.documentElement
$.clickOrTouch = if $.isTouch then 'touchstart' else 'click'
features.supportsCssTouchScroll = (typeof document.body.style.webkitOverflowScrolling != "undefined") # Currently only iOS 5 can do native touch scrolling
features.supportsIScroll = (features.vendor == 'webkit' || features.vendor == "Moz")
# Utilities
findFirstChildWithClass = (elem, className) ->
child = elem.firstChild
while child
if $(child).hasClass(className) then return child
child = child.nextSibling
return null
oppositeDirection = { left: 'right', right: 'left', top: 'bottom', bottom: 'top' }
oppositeTransition = (transition) ->
if transition
for own key of transition
if $.paneTransitionInverters.hasOwnProperty(key)
return $.paneTransitionInverters[key](transition[key])
null
# Implement $.getJSON and $.map like jQuery does
$.getJSON = (url, options) ->
callback = if typeof options == "function" then options else options.callback
$(null).xhr(url, {
method: options.method,
async: true,
data: JSON.stringify(options.data),
headers: options.headers,
callback: ->
callback(JSON.parse(this.responseText))
})
$.map = (items, map) ->
results = []
for item in items
mapped = map(item)
if mapped != undefined
results.push(mapped)
results
# XUI extensions
$.fn.togglePane = (show) ->
@each ->
# Can't just toggle display:block/none, as that resets any scroll positions within the pane
# Can't just toggle visibility:visible/hidden either, as Safari iOS still sends events (e.g., taps) into the element
# So, just move it very far away
if show
@style.display = 'block'
if @isOffScreen
@isOffScreen = false
@style.top = ''
@style.bottom = ''
else
@isOffScreen = true
@style.top = '-10000px'
@style.bottom = '10000px'
this
$.fn.afterNextTransition = (callback) ->
@each ->
elem = this
handlerWrapper = () ->
callback.apply(this, arguments)
elem.removeEventListener(features.transitionEndEvent, handlerWrapper)
elem.addEventListener(features.transitionEndEvent, handlerWrapper);
$.fn.animateTranslation = (finalPos, transition, callback) ->
callback = callback || () -> { }
@each ->
$this = $(this)
if features.useCssTransform
transform = {}
transform[features.cssTransformPrefix + "transform"] = "translate(" + finalPos.left + ", " + finalPos.top + ")"
transform[features.cssTransformPrefix + "transition"] = if transition then features.cssTransformPrefix + "transform 250ms ease-out" else null
if transition
$this.afterNextTransition(callback)
$this.css(transform)
if !transition
callback()
else
if transition
$this.tween(finalPos, callback)
else
$this.css(finalPos)
callback()
$.fn.setPanePosition = (position, transition, callback) ->
callback = callback || () -> { }
@each ->
$this = $(this).togglePane(true)
x = 0
y = 0
width = @parentNode.offsetWidth
height = @parentNode.offsetHeight
switch position
when 'right' then x = width
when 'left' then x = -1 * width
when 'top' then y = -1 * height
when 'bottom' then y = height
finalPos = { left: x + 'px', right: (-1 * x) + 'px', top: y + 'px', bottom: (-1 * y) + 'px' }
$this.animateTranslation(finalPos, transition, callback)
$.fn.slidePane = (options) ->
@each ->
$this = $(this)
afterSlide = ->
if options.to then $this.togglePane(false)
if options.callback then options.callback()
$this.setPanePosition(options.from, null)
.setPanePosition(options.to, true, afterSlide)
$.fn.showPane = (options) ->
options = options || {}
@each ->
activePane = findFirstChildWithClass(this.parentNode, "active")
if activePane != this # Not already shown
$(this).has(".scroll-y.autoscroll").touchScroll({ hScroll: false })
$(this).has(".scroll-x.autoscroll").touchScroll({ yScroll: false })
# Find and invoke the requested transition
transitionToUse = 'default'
for own transitionKey of $.paneTransitions
if options.hasOwnProperty(transitionKey)
transitionToUse = transitionKey
break
$.paneTransitions[transitionKey](this, activePane, options[transitionKey])
# Keep track of which pane is active
$(this).addClass("active")
if activePane
$(activePane).removeClass("active")
$.fn.showBySlidingParent = (options) ->
@each ->
targetPaneOffsetLeft = parseInt(@style.left) || 0
targetPaneOffsetTop = parseInt(@style.top) || 0
finalPos =
left: (-1*targetPaneOffsetLeft) + '%'
right: targetPaneOffsetLeft + '%'
top: (-1*targetPaneOffsetTop) + '%'
bottom: targetPaneOffsetTop + '%'
$(this).css({ display: 'block' })
$(@parentNode).css({ 'overflow': 'visible' }).animateTranslation(finalPos, options.animate != false)
this
$.fn.touchScroll = (options) ->
if (!features.supportsCssTouchScroll) && features.supportsIScroll
@each ->
if !@hasIScroll
@hasIScroll = new iScroll(this, options)
doRefresh = => @hasIScroll.refresh()
setTimeout(doRefresh, 0)
this
this
$.fn.clickOrTouch = (handler) ->
@on($.clickOrTouch, handler)
# Create missing event shortcuts for IE version of XUI
for eventName in ['click']
if (!$.fn[eventName])
$.fn[eventName] = (handler) -> @on(eventName, handler)
# Transitions
$.paneTransitions =
slideFrom: (incomingPane, outgoingPane, options) ->
$(incomingPane).slidePane({ from: options })
if outgoingPane
$(outgoingPane).slidePane({ to: oppositeDirection[options] })
coverFrom: (incomingPane, outgoingPane, options) ->
outgoingZIndex = outgoingPane?.style.zIndex || 0
$(incomingPane).css({ zIndex: outgoingZIndex + 1 })
.slidePane({
from: options,
callback: () -> $(outgoingPane).togglePane(false)
})
uncoverTo: (incomingPane, outgoingPane, options) ->
incomingZIndex = incomingPane.style.zIndex || 0;
$(incomingPane).togglePane(true).setPanePosition()
$(outgoingPane).css({ zIndex: incomingZIndex + 1 }).slidePane({ to: options })
default: (incomingPane, outgoingPane, options) ->
# No transition - just show instantly, and hide the previously active pane
$(incomingPane).togglePane(true).setPanePosition()
$(outgoingPane).togglePane(false)
$.paneTransitionInverters =
slideFrom: (direction) -> { slideFrom: oppositeDirection[direction] }
coverFrom: (direction) -> { uncoverTo: direction }
uncoverTo: (direction) -> { coverFrom: direction }
# Hook into nav.js so you can easily animate transitions on navigation
window.NavHistory.animatePane = (elementId, navInfo) ->
transition = navInfo.transition || (if !navInfo.isFirst then { slideFrom: 'right' } else null)
if navInfo.isBack
transition = oppositeTransition(transition)
x$('#' + elementId).showPane(transition)
window.NavHistory.slideParent = (elementId, navInfo) ->
x$('#' + elementId).showBySlidingParent({ animate: !navInfo.isFirst })

View File

@@ -0,0 +1,338 @@
(function() {
/*!
nav.transitions.js v0.1 - (c) Microsoft Corporation
*/
var $, eventName, features, findFirstChildWithClass, oppositeDirection, oppositeTransition, _i, _len, _ref;
var __hasProp = Object.prototype.hasOwnProperty;
$ = x$;
features = {
vendor: /webkit/i.test(navigator.appVersion) ? 'webkit' : /firefox/i.test(navigator.userAgent) ? 'Moz' : 'opera' in window ? 'O' : '',
isAndroid: /android/gi.test(navigator.appVersion)
};
features.useCssTransform = (!features.isAndroid) && (features.vendor + 'Transform' in document.documentElement.style);
features.cssTransformPrefix = "-" + features.vendor.toLowerCase() + "-";
features.transitionEndEvent = features.vendor === 'webkit' ? 'webkitTransitionEnd' : features.vendor === 'O' ? 'oTransitionEnd' : 'transitionend';
$.isTouch = 'ontouchstart' in document.documentElement;
$.clickOrTouch = $.isTouch ? 'touchstart' : 'click';
features.supportsCssTouchScroll = typeof document.body.style.webkitOverflowScrolling !== "undefined";
features.supportsIScroll = features.vendor === 'webkit' || features.vendor === "Moz";
findFirstChildWithClass = function(elem, className) {
var child;
child = elem.firstChild;
while (child) {
if ($(child).hasClass(className)) return child;
child = child.nextSibling;
}
return null;
};
oppositeDirection = {
left: 'right',
right: 'left',
top: 'bottom',
bottom: 'top'
};
oppositeTransition = function(transition) {
var key;
if (transition) {
for (key in transition) {
if (!__hasProp.call(transition, key)) continue;
if ($.paneTransitionInverters.hasOwnProperty(key)) {
return $.paneTransitionInverters[key](transition[key]);
}
}
}
return null;
};
$.getJSON = function(url, options) {
var callback;
callback = typeof options === "function" ? options : options.callback;
return $(null).xhr(url, {
method: options.method,
async: true,
data: JSON.stringify(options.data),
headers: options.headers,
callback: function() {
return callback(JSON.parse(this.responseText));
}
});
};
$.map = function(items, map) {
var item, mapped, results, _i, _len;
results = [];
for (_i = 0, _len = items.length; _i < _len; _i++) {
item = items[_i];
mapped = map(item);
if (mapped !== void 0) results.push(mapped);
}
return results;
};
$.fn.togglePane = function(show) {
return this.each(function() {
if (show) {
this.style.display = 'block';
if (this.isOffScreen) {
this.isOffScreen = false;
this.style.top = '';
this.style.bottom = '';
}
} else {
this.isOffScreen = true;
this.style.top = '-10000px';
this.style.bottom = '10000px';
}
return this;
});
};
$.fn.afterNextTransition = function(callback) {
return this.each(function() {
var elem, handlerWrapper;
elem = this;
handlerWrapper = function() {
callback.apply(this, arguments);
return elem.removeEventListener(features.transitionEndEvent, handlerWrapper);
};
return elem.addEventListener(features.transitionEndEvent, handlerWrapper);
});
};
$.fn.animateTranslation = function(finalPos, transition, callback) {
callback = callback || function() {
return {};
};
return this.each(function() {
var $this, transform;
$this = $(this);
if (features.useCssTransform) {
transform = {};
transform[features.cssTransformPrefix + "transform"] = "translate(" + finalPos.left + ", " + finalPos.top + ")";
transform[features.cssTransformPrefix + "transition"] = transition ? features.cssTransformPrefix + "transform 250ms ease-out" : null;
if (transition) $this.afterNextTransition(callback);
$this.css(transform);
if (!transition) return callback();
} else {
if (transition) {
return $this.tween(finalPos, callback);
} else {
$this.css(finalPos);
return callback();
}
}
});
};
$.fn.setPanePosition = function(position, transition, callback) {
callback = callback || function() {
return {};
};
return this.each(function() {
var $this, finalPos, height, width, x, y;
$this = $(this).togglePane(true);
x = 0;
y = 0;
width = this.parentNode.offsetWidth;
height = this.parentNode.offsetHeight;
switch (position) {
case 'right':
x = width;
break;
case 'left':
x = -1 * width;
break;
case 'top':
y = -1 * height;
break;
case 'bottom':
y = height;
}
finalPos = {
left: x + 'px',
right: (-1 * x) + 'px',
top: y + 'px',
bottom: (-1 * y) + 'px'
};
return $this.animateTranslation(finalPos, transition, callback);
});
};
$.fn.slidePane = function(options) {
return this.each(function() {
var $this, afterSlide;
$this = $(this);
afterSlide = function() {
if (options.to) $this.togglePane(false);
if (options.callback) return options.callback();
};
return $this.setPanePosition(options.from, null).setPanePosition(options.to, true, afterSlide);
});
};
$.fn.showPane = function(options) {
options = options || {};
return this.each(function() {
var activePane, transitionKey, transitionToUse, _ref;
activePane = findFirstChildWithClass(this.parentNode, "active");
if (activePane !== this) {
$(this).has(".scroll-y.autoscroll").touchScroll({
hScroll: false
});
$(this).has(".scroll-x.autoscroll").touchScroll({
yScroll: false
});
transitionToUse = 'default';
_ref = $.paneTransitions;
for (transitionKey in _ref) {
if (!__hasProp.call(_ref, transitionKey)) continue;
if (options.hasOwnProperty(transitionKey)) {
transitionToUse = transitionKey;
break;
}
}
$.paneTransitions[transitionKey](this, activePane, options[transitionKey]);
$(this).addClass("active");
if (activePane) return $(activePane).removeClass("active");
}
});
};
$.fn.showBySlidingParent = function(options) {
return this.each(function() {
var finalPos, targetPaneOffsetLeft, targetPaneOffsetTop;
targetPaneOffsetLeft = parseInt(this.style.left) || 0;
targetPaneOffsetTop = parseInt(this.style.top) || 0;
finalPos = {
left: (-1 * targetPaneOffsetLeft) + '%',
right: targetPaneOffsetLeft + '%',
top: (-1 * targetPaneOffsetTop) + '%',
bottom: targetPaneOffsetTop + '%'
};
$(this).css({
display: 'block'
});
$(this.parentNode).css({
'overflow': 'visible'
}).animateTranslation(finalPos, options.animate !== false);
return this;
});
};
$.fn.touchScroll = function(options) {
if ((!features.supportsCssTouchScroll) && features.supportsIScroll) {
this.each(function() {
var doRefresh;
var _this = this;
if (!this.hasIScroll) this.hasIScroll = new iScroll(this, options);
doRefresh = function() {
return _this.hasIScroll.refresh();
};
setTimeout(doRefresh, 0);
return this;
});
}
return this;
};
$.fn.clickOrTouch = function(handler) {
return this.on($.clickOrTouch, handler);
};
_ref = ['click'];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
eventName = _ref[_i];
if (!$.fn[eventName]) {
$.fn[eventName] = function(handler) {
return this.on(eventName, handler);
};
}
}
$.paneTransitions = {
slideFrom: function(incomingPane, outgoingPane, options) {
$(incomingPane).slidePane({
from: options
});
if (outgoingPane) {
return $(outgoingPane).slidePane({
to: oppositeDirection[options]
});
}
},
coverFrom: function(incomingPane, outgoingPane, options) {
var outgoingZIndex;
outgoingZIndex = (outgoingPane != null ? outgoingPane.style.zIndex : void 0) || 0;
return $(incomingPane).css({
zIndex: outgoingZIndex + 1
}).slidePane({
from: options,
callback: function() {
return $(outgoingPane).togglePane(false);
}
});
},
uncoverTo: function(incomingPane, outgoingPane, options) {
var incomingZIndex;
incomingZIndex = incomingPane.style.zIndex || 0;
$(incomingPane).togglePane(true).setPanePosition();
return $(outgoingPane).css({
zIndex: incomingZIndex + 1
}).slidePane({
to: options
});
},
"default": function(incomingPane, outgoingPane, options) {
$(incomingPane).togglePane(true).setPanePosition();
return $(outgoingPane).togglePane(false);
}
};
$.paneTransitionInverters = {
slideFrom: function(direction) {
return {
slideFrom: oppositeDirection[direction]
};
},
coverFrom: function(direction) {
return {
uncoverTo: direction
};
},
uncoverTo: function(direction) {
return {
coverFrom: direction
};
}
};
window.NavHistory.animatePane = function(elementId, navInfo) {
var transition;
transition = navInfo.transition || (!navInfo.isFirst ? {
slideFrom: 'right'
} : null);
if (navInfo.isBack) transition = oppositeTransition(transition);
return x$('#' + elementId).showPane(transition);
};
window.NavHistory.slideParent = function(elementId, navInfo) {
return x$('#' + elementId).showBySlidingParent({
animate: !navInfo.isFirst
});
};
}).call(this);

View File

@@ -0,0 +1,221 @@
/// <reference path="IntelliSense\References.js" />
///#RESTORE (function (global, $, upshot, undefined)
{
var base = upshot.EntityView.prototype;
var obs = upshot.observability;
var ctor = function (entity, parentEntitySet, childEntitySet, associationMetadata, parentPropertySetter, result) {
this._entity = entity;
this._parentEntitySet = parentEntitySet;
this._childEntitySet = childEntitySet;
this._associationMetadata = associationMetadata;
this._parentPropertySetter = parentPropertySetter;
// The EntityView base class observes its "source" option (which is the target entity set) for
// array- and property-changes.
// Additionally, we need to observe property changes on the source entity set to catch:
// - FK property changes that would affect a parent association property
// - PK (non-FK) property changes that would affect a child association property
var self = this;
this._sourceEntitySetObserver = function (entity, property, newValue) {
if (!self._needRecompute &&
$.inArray(property, associationMetadata.thisKey) >= 0) {
self._setNeedRecompute();
}
};
var sourceEntitySet = associationMetadata.isForeignKey ? childEntitySet : parentEntitySet;
sourceEntitySet.bind("propertyChanged", this._sourceEntitySetObserver);
var entitySource = associationMetadata.isForeignKey ? parentEntitySet : childEntitySet;
base.constructor.call(this, { source: entitySource, result: result });
// We only ever instantiate AssociatedEntitiesViews when adding entities to
// an EntitySet, which always ends with a recompute.
this._initialized = false;
this._setNeedRecompute();
};
var instanceMembers = {
// Internal methods
// This is called from EntitySet.js as it treats tracked changes to parent association
// properties on child entities.
__handleParentPropertySet: function (parentEntity) {
this._handleRelationshipEdit(this._entity, parentEntity);
},
// Private methods
_dispose: function () {
var sourceEntitySet = this._associationMetadata.isForeignKey ? this._childEntitySet : this._parentEntitySet;
sourceEntitySet.unbind("propertyChanged", this._sourceEntitySetObserver);
base._dispose.apply(this, arguments);
},
_handleEntityAdd: function (entity) {
this._handleRelationshipEdit(entity, this._entity);
},
// Do the appropriate EntitySet adds and FK property changes to reflect an editied relationship
// between childEntity and parentEntity.
_handleRelationshipEdit: function (childEntity, parentEntity) {
var associationMetadata = this._associationMetadata,
isForeignKey = associationMetadata.isForeignKey,
parentKeyValue;
if (!parentEntity) {
parentKeyValue = null;
} else {
if ($.inArray(parentEntity, obs.asArray(this._parentEntitySet.getEntities())) < 0) {
// TODO -- Should this implicitly add the parent entity? I doubt it.
throw "Parent entity is not in the parent entity set for this association.";
} else if ((this._parentEntitySet.getEntityState(parentEntity) || "").indexOf("Add") > 0) {
// TODO -- Add support for added parent entities without an established key value, fix-up after commit.
throw "NYI -- Cannot set foreign keys to key values computed from added entities. Commit the parent entity first.";
}
var parentKey = isForeignKey ? associationMetadata.otherKey : associationMetadata.thisKey;
parentKeyValue = obs.getProperty(parentEntity, parentKey[0]); // TODO -- Generalize to N fields.
if (parentKeyValue === undefined) {
throw "Parent entity has no value for its '" + parentKey[0] + "' key property.";
}
}
var childKey = isForeignKey ? associationMetadata.thisKey : associationMetadata.otherKey,
childKeyValue = obs.getProperty(childEntity, childKey[0]), // TODO -- Generalize to N fields.
setForeignKeyValue;
if (!parentEntity) {
if (childKeyValue !== null) {
setForeignKeyValue = true;
}
} else if (childKeyValue === undefined || childKeyValue !== parentKeyValue) {
setForeignKeyValue = true;
}
var isAddToChildEntities = !isForeignKey;
if (isAddToChildEntities && $.inArray(childEntity, obs.asArray(this._entitySource.getEntities())) < 0) {
// Base class will translate add to child entities into an add on our input EntitySet.
base._handleEntityAdd.call(this, childEntity);
}
if (setForeignKeyValue) {
// Do this after the entitySet add above. That way, the property change will be observable by clients
// interested in childEntitiesCollection or the EntitySet.
// Likewise, above, we will have done obs.track (as part of adding to the EntitySet) before
// obs.setProperty, in case establishing observable proxies is done implicitly w/in setProperty
// (as WinJS support does).
this._childEntitySet.__setProperty(childEntity, childKey[0], parentKeyValue); // TODO -- Generalize to N fields.
}
},
_onPropertyChanged: function (entity, property, newValue) {
if (!this._needRecompute &&
$.inArray(property, this._associationMetadata.otherKey) >= 0) {
this._setNeedRecompute();
}
base._onPropertyChanged.apply(this, arguments);
},
_onArrayChanged: function (type, eventArgs) {
if (this._needRecompute) {
return;
}
var needRecompute;
switch (type) {
case "insert":
case "remove":
var self = this;
$.each(eventArgs.items, function (index, entity) {
if (self._haveEntity(entity) ^ type === "insert") {
needRecompute = true;
return false;
}
});
break;
case "replaceAll":
needRecompute = true;
break;
default:
throw "NYI -- Array operation '" + type + "' is not supported.";
}
if (needRecompute) {
this._setNeedRecompute();
}
},
_recompute: function () {
var clientEntities = this._clientEntities,
newEntities = this._computeAssociatedEntities();
if (!this._initialized) {
this._initialized = true;
if (newEntities.length > 0) { // Don't event a replaceAll if we're not actually modifying the entities array.
var oldEntities = obs.asArray(clientEntities).slice(); // Here, assume a live array. It will be for jQuery compat.
obs.refresh(clientEntities, newEntities);
this._trigger("arrayChanged", "replaceAll", { oldItems: oldEntities, newItems: obs.asArray(clientEntities) });
}
} else {
// Perform adds/removes on clientEntities to have it reflect the same membership
// as newEntities. Issue change events for the adds/removes.
// Don't try to preserve ordering between clientEntities and newEntities.
// Assume that obs.asArray returns a non-live array instance. It will be for Knockout compat.
// Don't cache obs.asArray(clientEntities) below.
var self = this;
var addedEntities = $.grep(newEntities, function (entity) {
return $.inArray(entity, obs.asArray(clientEntities)) < 0;
});
$.each(addedEntities, function (unused, entity) {
var index = obs.asArray(clientEntities).length,
items = [entity];
obs.insert(clientEntities, index, items);
self._trigger("arrayChanged", "insert", { index: index, items: items });
});
var removedEntities = $.grep(obs.asArray(clientEntities), function (entity) {
return $.inArray(entity, newEntities) < 0;
});
$.each(removedEntities, function (unused, entity) {
var indexRemove = $.inArray(entity, obs.asArray(clientEntities));
obs.remove(clientEntities, indexRemove, 1);
self._trigger("arrayChanged", "remove", { index: indexRemove, items: [entity] });
});
}
if (this._parentPropertySetter) {
// EntitySet.js has supplied a handler with which to make observable changes
// to a parent association property on a child entity.
this._parentPropertySetter.apply(this);
}
},
_computeAssociatedEntities: function () {
var entity = this._entity,
associationMetadata = this._associationMetadata,
sourceKeyValue = obs.getProperty(entity, associationMetadata.thisKey[0]), // TODO -- Generalize to N fields.
targetEntitySet = associationMetadata.isForeignKey ? this._parentEntitySet : this._childEntitySet,
targetEntities = obs.asArray(targetEntitySet.getEntities()),
targetKey = associationMetadata.otherKey,
associatedEntities = [];
for (var i = 0; i < targetEntities.length; i++) {
var targetEntity = targetEntities[i],
targetKeyValue = obs.getProperty(targetEntity, targetKey[0]); // TODO -- Generalize to N fields.
if (targetKeyValue !== undefined && targetKeyValue === sourceKeyValue) {
associatedEntities.push(targetEntity);
}
}
return associatedEntities;
}
// TODO -- Make array removals from "_clientEntities" null out foreign key values.
};
upshot.AssociatedEntitiesView = upshot.deriveClass(base, ctor, instanceMembers);
}
///#RESTORE )(this, jQuery, upshot);

View File

@@ -0,0 +1,270 @@
/// <reference path="IntelliSense\References.js" />
///#RESTORE (function (global, undefined)
{
function extend(target, members) {
for (var member in members) {
target[member] = members[member];
}
return target;
}
function defineNamespace(name) {
var names = name.split(".");
var current = global;
for (var i = 0; i < names.length; i++) {
var ns = current[names[i]];
if (!ns || typeof ns !== "object") {
current[names[i]] = ns = {};
}
current = ns;
}
return current;
}
function defineClass(ctor, instanceMembers, classMembers) {
ctor = ctor || function () { };
if (instanceMembers) {
extend(ctor.prototype, instanceMembers);
}
if (classMembers) {
extend(ctor, classMembers);
}
return ctor;
}
function deriveClass(basePrototype, ctor, instanceMembers) {
var prototype = {};
extend(prototype, basePrototype);
extend(prototype, instanceMembers); // Will override like-named members on basePrototype.
ctor = ctor || function () { };
ctor.prototype = prototype;
ctor.prototype.constructor = ctor;
return ctor;
}
function classof(o) {
if (o === null) {
return "null";
}
if (o === undefined) {
return "undefined";
}
return Object.prototype.toString.call(o).slice(8, -1).toLowerCase();
}
function isArray(o) {
return classof(o) === "array";
}
function isObject(o) {
return classof(o) === "object";
}
function isValueArray(o) {
return isArray(o) && (o.length === 0 || !(isArray(o[0]) || isObject(o[0])));
}
function isDate(o) {
return classof(o) === "date";
}
function isFunction(o) {
return classof(o) === "function";
}
function isGuid(value) {
return (typeof value === "string") && /[a-fA-F\d]{8}-(?:[a-fA-F\d]{4}-){3}[a-fA-F\d]{12}/.test(value);
}
var hasOwnProperty = Object.prototype.hasOwnProperty;
function isEmpty(obj) {
if (obj === null || obj === undefined) {
return true;
}
for (var key in obj) {
if (hasOwnProperty.call(obj, key)) {
return false;
}
}
return true;
}
var idCounter = 0;
function uniqueId(prefix) {
/// <summary>Generates a unique id (unique within the entire client session)</summary>
/// <param name="prefix" type="String">Optional prefix to the id</param>
/// <returns type="String" />
prefix || (prefix = "");
return prefix + idCounter++;
}
function cache(object, key, value) {
if (!object) {
return;
}
if (arguments.length === 2) {
// read
var cacheName = upshot.cacheName;
if (cacheName && object[cacheName]) {
return object[cacheName][key];
}
return null;
} else {
// write
if (object.nodeType !== undefined) {
throw "upshot.cache cannot be used with DOM elements";
}
var cacheName = upshot.cacheName || (upshot.cacheName = uniqueId("__upshot__"));
object[cacheName] || (object[cacheName] = function () { });
return object[cacheName][key] = value;
}
}
function deleteCache(object, key) {
var cacheName = upshot.cacheName;
if (cacheName && object && object[cacheName]) {
if (key) {
delete object[cacheName][key];
}
if (!key || isEmpty(object[cacheName])) {
delete object[cacheName];
}
}
}
function sameArrayContents(array1, array2) {
if (array1.length !== array2.length) {
return false;
} else {
for (var i = 0; i < array1.length; i++) {
if (array1[i] !== array2[i]) {
return false;
}
}
}
return true;
}
// This routine provides an equivalent of array.push(item) missing from JavaScript array.
function arrayRemove(array, item) {
var callback = upshot.isFunction(item) ? item : undefined;
for (var index = 0; index < array.length; index++) {
if (callback ? callback(array[index]) : (array[index] === item)) {
array.splice(index, 1);
return index;
}
}
return -1;
}
// pre-defined ns
///#RESTORE var upshot = defineNamespace("upshot");
// pre-defined routines
upshot.extend = extend;
upshot.defineNamespace = defineNamespace;
upshot.defineClass = defineClass;
upshot.deriveClass = deriveClass;
upshot.classof = classof;
upshot.isArray = isArray;
upshot.isObject = isObject;
upshot.isValueArray = isValueArray;
upshot.isDate = isDate;
upshot.isFunction = isFunction;
upshot.isGuid = isGuid;
upshot.isEmpty = isEmpty;
upshot.uniqueId = uniqueId;
upshot.cacheName = null;
upshot.cache = cache;
upshot.deleteCache = deleteCache;
upshot.sameArrayContents = sameArrayContents;
upshot.arrayRemove = arrayRemove;
upshot.EntityState = {
Unmodified: "Unmodified",
ClientUpdated: "ClientUpdated",
ClientAdded: "ClientAdded",
ClientDeleted: "ClientDeleted",
ServerUpdating: "ServerUpdating",
ServerAdding: "ServerAdding",
ServerDeleting: "ServerDeleting",
Deleted: "Deleted",
isClientModified: function (entityState) {
return entityState && entityState.indexOf("Client") === 0;
},
isServerSyncing: function (entityState) {
return entityState && entityState.indexOf("Server") === 0;
},
isUpdated: function (entityState) {
return entityState && entityState.indexOf("Updat") > 0;
},
isDeleted: function (entityState) {
return entityState && entityState.indexOf("Delet") > 0;
},
isAdded: function (entityState) {
return entityState && entityState.indexOf("Add") > 0;
}
};
///#DEBUG
upshot.assert = function (cond, msg) {
if (!cond) {
alert(msg || "assert is encountered!");
}
}
///#ENDDEBUG
var entitySources = [];
function registerRootEntitySource (entitySource) {
entitySources.push(entitySource);
}
function deregisterRootEntitySource (entitySource) {
entitySources.splice($.inArray(entitySource, entitySources), 1);
}
var recomputeInProgress;
function triggerRecompute () {
if (recomputeInProgress) {
throw "Cannot make observable edits from within an event callback.";
}
try {
recomputeInProgress = true;
var sources = entitySources.slice();
$.each(sources, function (index, source) {
source.__recomputeDependentViews();
});
$.each(sources, function (index, source) {
if (source.__flushEntityStateChangedEvents) {
source.__flushEntityStateChangedEvents();
}
});
}
finally {
recomputeInProgress = false;
}
}
function beginChange () {
if (recomputeInProgress) {
throw "Cannot make observable edits from within an event callback.";
}
}
function endChange () {
triggerRecompute();
}
upshot.__registerRootEntitySource = registerRootEntitySource;
upshot.__deregisterRootEntitySource = deregisterRootEntitySource;
upshot.__triggerRecompute = triggerRecompute;
upshot.__beginChange = beginChange;
upshot.__endChange = endChange;
}
///#RESTORE )(this);

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,172 @@
/// <reference path="IntelliSense\References.js" />
///#RESTORE (function (global, $, upshot, undefined)
{
function pad(count, value) {
var str = "0000" + value;
return str.slice(str.length - count);
}
function formatDateTime(date) {
return "datetime" +
"'" + pad(4, date.getUTCFullYear()) +
"-" + pad(2, date.getUTCMonth() + 1) +
"-" + pad(2, date.getUTCDate()) +
"T" + pad(2, date.getUTCHours()) +
":" + pad(2, date.getUTCMinutes()) +
":" + pad(2, date.getUTCSeconds()) + "'";
}
function getQueryResult(getResult) {
var entities = getResult.results,
resultType = entities.length && entities[0].__metadata.type;
var metadata;
if (resultType) {
metadata = {};
metadata[resultType] = {
key: ["__metadata.uri"]
};
}
var count = getResult.__count,
totalCount = count === undefined ? null : +count;
return {
type: resultType,
metadata: metadata,
entities: entities,
totalCount: totalCount
};
}
var instanceMembers = {
// Public methods
get: function (parameters, queryParameters, success, error) {
/// <summary>
/// Asynchronously gets data from the server using the specified parameters
/// </summary>
/// <param name="parameters" type="String">The get parameters</param>
/// <param name="queryParameters" type="Object">An object where each property is a query to pass to the operation. This parameter is optional.</param>
/// <param name="success" type="Function">Optional success callback</param>
/// <param name="error" type="Function">Optional error callback</param>
/// <returns type="Promise">A Promise representing the result of the load operation</returns>
var operation, operationParameters;
if (parameters) {
operation = parameters.operationName;
operationParameters = parameters.operationParameters;
}
if ($.isFunction(operationParameters)) {
success = operationParameters;
error = queryParameters;
}
var self = this;
// $.map applied to objects is supported in jQuery >= 1.6. Our current baseline is jQuery 1.5
var parameterStrings = [];
$.each($.extend({}, operationParameters, upshot.ODataDataProvider.getODataQueryParameters(queryParameters)), function (key, value) {
parameterStrings.push(key.toString() + "=" + value.toString());
});
var queryString = parameterStrings.length ? ("?" + parameterStrings.join("&")) : "";
// Invoke the query
OData.read(upshot.DataProvider.normalizeUrl(parameters.url) + operation + queryString,
function (result) {
if (success) {
arguments[0] = getQueryResult(arguments[0]);
success.apply(self, arguments);
}
},
function (reason) {
if (error) {
error.call(self, -1, reason.message, reason);
}
}
);
},
submit: function () {
throw "Saving edits through the OData data provider is not supported.";
}
};
var classMembers = {
getODataQueryParameters: function (query) {
query = query || {};
var queryParameters = {};
// filters -> $filter
if (query.filters && query.filters.length) {
var filterParameter = "",
applyOperator = function (property, operator, value) {
if (typeof value === "string") {
if (upshot.isGuid(value)) {
value = "guid'" + value + "'";
} else {
value = "'" + value + "'";
}
} else if (upshot.isDate(value)) {
value = formatDateTime(value);
}
switch (operator) {
case "<": return property + " lt " + value;
case "<=": return property + " le " + value;
case "==": return property + " eq " + value;
case "!=": return property + " ne " + value;
case ">=": return property + " ge " + value;
case ">": return property + " gt " + value;
case "StartsWith": return "startswith(" + property + "," + value + ") eq true";
case "EndsWith": return "endswith(" + property + "," + value + ") eq true";
case "Contains": return "substringof(" + value + "," + property + ") eq true";
default: throw "The operator '" + operator + "' is not supported.";
}
};
$.each(query.filters, function (index, filter) {
if (filterParameter) {
filterParameter += " and ";
}
filterParameter += applyOperator(filter.property, filter.operator, filter.value);
});
queryParameters.$filter = filterParameter;
}
// sort -> $orderby
if (query.sort && query.sort.length) {
var formatSort = function (sort) {
return !!sort.descending ? (sort.property + " desc") : sort.property;
};
queryParameters.$orderby = $.map(query.sort, function (sort, index) {
return formatSort(sort);
}).join();
}
// skip -> $skip
if (query.skip) {
queryParameters.$skip = query.skip;
}
// take -> $top
if (query.take) {
queryParameters.$top = query.take;
}
// includeTotalCount -> $inlinecount
if (query.includeTotalCount) {
queryParameters.$inlinecount = "allpages";
}
return queryParameters;
}
}
upshot.ODataDataProvider = upshot.defineClass(null, instanceMembers, classMembers);
}
///#RESTORE )(this, jQuery, upshot);

View File

@@ -0,0 +1,173 @@
/// <reference path="IntelliSense\References.js" />
///#RESTORE (function (global, $, upshot, undefined)
{
function getQueryResult(getResult, wrappedResult) {
var entities, totalCount;
if (wrappedResult) {
entities = getResult.Results;
totalCount = getResult.TotalCount;
}
else {
entities = getResult;
}
return {
entities: upshot.isArray(entities) ? entities : [entities],
totalCount: totalCount
};
}
var instanceMembers = {
// Public methods
get: function (parameters, queryParameters, success, error) {
/// <summary>
/// Asynchronously gets data from the server using the specified parameters
/// </summary>
/// <param name="parameters" type="String">The get parameters</param>
/// <param name="queryParameters" type="Object">An object where each property is a query to pass to the operation. This parameter is optional.</param>
/// <param name="success" type="Function">Optional success callback</param>
/// <param name="error" type="Function">Optional error callback</param>
/// <returns type="Promise">A Promise representing the result of the load operation</returns>
var operation, operationParameters;
if (parameters) {
operation = parameters.operationName;
operationParameters = parameters.operationParameters;
}
if ($.isFunction(operationParameters)) {
success = operationParameters;
error = queryParameters;
}
var self = this;
// set up the request parameters
var url = upshot.DataProvider.normalizeUrl(parameters.url) + operation;
var oDataQueryParams = upshot.ODataDataProvider.getODataQueryParameters(queryParameters);
var data = $.extend({}, operationParameters, oDataQueryParams);
var wrappedResult = oDataQueryParams.$inlinecount == "allpages";
// invoke the query
$.ajax({
url: url,
data: data,
success: success && function () {
arguments[0] = getQueryResult(arguments[0], wrappedResult);
success.apply(self, arguments);
},
error: error && function (jqXHR, statusText, errorText) {
error.call(self, jqXHR.status, self._parseErrorText(jqXHR.responseText) || errorText, jqXHR);
},
dataType: "json"
});
},
submit: function (parameters, changeSet, success, error) {
/// <summary>
/// Asynchronously submits the specified changeset
/// </summary>
/// <param name="parameters" type="String">The submit parameters</param>
/// <param name="changeSet" type="Object">The changeset to submit</param>
/// <param name="success" type="Function">Optional success callback</param>
/// <param name="error" type="Function">Optional error callback</param>
/// <returns type="Promise">A Promise representing the result of the post operation</returns>
$.each(changeSet, function (index, changeSetEntry) {
switch (changeSetEntry.Operation) {
case 2: // insert
changeSetEntry.Operation = 1;
break;
case 3: // update
changeSetEntry.Operation = 2;
break;
case 4: // delete
changeSetEntry.Operation = 3;
break;
};
});
var self = this,
encodedChangeSet = JSON.stringify(changeSet);
$.ajax({
url: upshot.DataProvider.normalizeUrl(parameters.url) + "Submit",
contentType: "application/json",
data: encodedChangeSet,
dataType: "json",
type: "POST",
success: (success || error) && function (data, statusText, jqXHR) {
var result = data;
var hasErrors = false;
if (result) {
// transform to Error property
$.each(result, function (index, changeSetEntry) {
// even though upshot currently doesn't support reporting of concurrency conflicts,
// we must still identify such failures
$.each(["ConflictMembers", "ValidationErrors", "IsDeleteConflict"], function (index, property) {
if (changeSetEntry.hasOwnProperty(property)) {
changeSetEntry.Error = changeSetEntry.Error || {};
changeSetEntry.Error[property] = changeSetEntry[property];
hasErrors = true;
}
});
});
}
if (!hasErrors) {
if (success) {
success.call(self, result);
}
} else if (error) {
var errorText = "Submit failed.";
if (result) {
for (var i = 0; i < result.length; ++i) {
var validationError = (result[i].ValidationErrors && result[i].ValidationErrors[0] && result[i].ValidationErrors[0].Message);
if (validationError) {
errorText = validationError;
break;
}
}
}
error.call(self, jqXHR.status, errorText, jqXHR, result);
}
},
error: error && function (jqXHR, statusText, errorText) {
error.call(self, jqXHR.status, self._parseErrorText(jqXHR.responseText) || errorText, jqXHR);
}
});
},
_parseErrorText: function (responseText) {
var match = /Exception]: (.+)\r/g.exec(responseText);
if (match && match[1]) {
return match[1];
}
if (/^{.*}$/g.test(responseText)) {
var error = JSON.parse(responseText);
// TODO: error.Message returned by DataController
// Does ErrorMessage check still necessary?
if (error.ErrorMessage) {
return error.ErrorMessage;
} else if (error.Message) {
return error.Message;
}
}
}
}
var classMembers = {
normalizeUrl: function (url) {
if (url && url.substring(url.length - 1) !== "/") {
return url + "/";
}
return url;
}
}
upshot.DataProvider = upshot.defineClass(null, instanceMembers, classMembers);
}
///#RESTORE )(this, jQuery, upshot);

View File

@@ -0,0 +1,251 @@
/// <reference path="IntelliSense\References.js" />
///#RESTORE (function (global, $, upshot, undefined)
{
function transformQuery(query) {
var queryParameters = {};
// filters -> $where
if (query.filters && query.filters.length) {
var whereParameter = "",
applyOperator = function (property, operator, value) {
if (typeof value === "string") {
if (upshot.isGuid(value)) {
value = "Guid(" + value + ")";
} else {
value = '"' + value + '"';
}
} else if (upshot.isDate(value)) {
// DomainService expects ticks; js Date.getTime() gives ms since epoch
value = "DateTime(" + (value.getTime() * 10000 + 621355968000000000) + ")";
}
switch (operator) {
case "<":
case "<=":
case "==":
case "!=":
case ">=":
case ">": return property + operator + value;
case "StartsWith":
case "EndsWith":
case "Contains": return property + "." + operator + "(" + value + ")";
default: throw "The operator '" + operator + "' is not supported.";
}
};
$.each(query.filters, function (index, filter) {
if (whereParameter) {
whereParameter += " AND ";
}
whereParameter += applyOperator(filter.property, filter.operator, filter.value);
});
queryParameters.$where = whereParameter;
}
// sort -> $orderby
if (query.sort && query.sort.length) {
var formatSort = function (sort) {
return !!sort.descending ? (sort.property + " desc") : sort.property;
};
queryParameters.$orderby = $.map(query.sort, function (sort, index) {
return formatSort(sort);
}).join();
}
// skip -> $skip
if (query.skip) {
queryParameters.$skip = query.skip;
}
// take -> $take
if (query.take) {
queryParameters.$take = query.take;
}
// includeTotalCount -> $includeTotalCount
if (query.includeTotalCount) {
queryParameters.$includeTotalCount = query.includeTotalCount;
}
return queryParameters;
}
function transformParameters(parameters) {
// perform any required transformations on the specified parameters
// before invoking the service, for example json serializing arrays
// and other complex parameters.
if (parameters) {
$.each(parameters || {}, function (key, value) {
if ($.isArray(value)) {
// json serialize arrays since this is the format the json
// endpoint expects.
parameters[key] = JSON.stringify(value);
}
});
}
return parameters;
}
function getQueryResult(getResult) {
var resultKey;
$.each(getResult, function (key) {
if (/Result$/.test(key)) {
resultKey = key;
return false;
}
});
var result = getResult[resultKey];
// process the metadata
var metadata = {};
$.each(result.Metadata, function (unused, metadataForType) {
metadata[metadataForType.type] = {
key: metadataForType.key,
fields: metadataForType.fields,
rules: metadataForType.rules,
messages: metadataForType.messages
};
});
var includedEntities;
if (result.IncludedResults) {
// group included entities by type
includedEntities = {};
$.each(result.IncludedResults, function (unused, entity) {
var entityType = entity.__type;
var entities = includedEntities[entityType] || (includedEntities[entityType] = []);
entities.push(entity);
});
}
return {
type: result.Metadata[0].type,
metadata: metadata,
entities: result.RootResults,
includedEntities: includedEntities,
totalCount: result.TotalCount || 0
};
}
var instanceMembers = {
// Public methods
get: function (parameters, queryParameters, success, error) {
/// <summary>
/// Asynchronously gets data from the server using the specified parameters
/// </summary>
/// <param name="parameters" type="String">The get parameters</param>
/// <param name="queryParameters" type="Object">An object where each property is a query to pass to the operation. This parameter is optional.</param>
/// <param name="success" type="Function">Optional success callback</param>
/// <param name="error" type="Function">Optional error callback</param>
/// <returns type="Promise">A Promise representing the result of the load operation</returns>
var operation, operationParameters;
if (parameters) {
operation = parameters.operationName;
operationParameters = parameters.operationParameters;
}
if ($.isFunction(operationParameters)) {
success = operationParameters;
error = queryParameters;
}
var self = this;
// Invoke the query
$.ajax({
url: upshot.DataProvider.normalizeUrl(parameters.url) + "json/" + operation,
data: $.extend({}, transformParameters(operationParameters), transformQuery(queryParameters || {})),
success: success && function () {
arguments[0] = getQueryResult(arguments[0]);
success.apply(self, arguments);
},
error: error && function (jqXHR, statusText, errorText) {
error.call(self, jqXHR.status, self._parseErrorText(jqXHR.responseText) || errorText, jqXHR);
},
dataType: "json"
});
},
submit: function (parameters, changeSet, success, error) {
/// <summary>
/// Asynchronously submits the specified changeset
/// </summary>
/// <param name="parameters" type="String">The submit parameters</param>
/// <param name="changeSet" type="Object">The changeset to submit</param>
/// <param name="success" type="Function">Optional success callback</param>
/// <param name="error" type="Function">Optional error callback</param>
/// <returns type="Promise">A Promise representing the result of the post operation</returns>
var self = this,
encodedChangeSet = JSON.stringify({ changeSet: changeSet });
$.ajax({
url: upshot.DataProvider.normalizeUrl(parameters.url) + "json/SubmitChanges",
contentType: "application/json",
data: encodedChangeSet,
dataType: "json",
type: "POST",
success: (success || error) && function (data, statusText, jqXHR) {
var result = data["SubmitChangesResult"];
var hasErrors = false;
if (result) {
// transform to Error property
$.each(result, function (index, changeSetEntry) {
// even though upshot currently doesn't support reporting of concurrency conflicts,
// we must still identify such failures
$.each(["ConflictMembers", "ValidationErrors", "IsDeleteConflict"], function (index, property) {
if (changeSetEntry.hasOwnProperty(property)) {
changeSetEntry.Error = changeSetEntry.Error || {};
changeSetEntry.Error[property] = changeSetEntry[property];
hasErrors = true;
}
});
});
}
if (!hasErrors) {
if (success) {
success.call(self, result);
}
} else if (error) {
var errorText = "Submit failed.";
if (result) {
for (var i = 0; i < result.length; ++i) {
var validationError = (result[i].ValidationErrors && result[i].ValidationErrors[0] && result[i].ValidationErrors[0].Message);
if (validationError) {
errorText = validationError;
break;
}
}
}
error.call(self, jqXHR.status, errorText, jqXHR, result);
}
},
error: error && function (jqXHR, statusText, errorText) {
error.call(self, jqXHR.status, self._parseErrorText(jqXHR.responseText) || errorText, jqXHR);
}
});
},
_parseErrorText: function (responseText) {
var match = /Exception]: (.+)\r/g.exec(responseText);
if (match && match[1]) {
return match[1];
}
if (/^{.*}$/g.test(responseText)) {
var error = JSON.parse(responseText);
if (error.ErrorMessage) {
return error.ErrorMessage;
}
}
}
}
upshot.riaDataProvider = upshot.defineClass(null, instanceMembers);
}
///#RESTORE )(this, jQuery, upshot);

View File

@@ -0,0 +1,168 @@
/// <reference path="IntelliSense\References.js" />
///#RESTORE (function (global, $, upshot, undefined)
{
var base = upshot.EntityView.prototype;
var obs = upshot.observability;
var queryOptions = { paging: "setPaging", sort: "setSort", filter: "setFilter" };
var ctor = function (options) {
if (options && options.result && options.result.length !== 0) {
throw "NYI -- Currently, \"result\" array must be empty to bind to a data source.";
}
this._skip = null;
this._take = null;
this._includeTotalCount = false;
this._lastRefreshTotalEntityCount = 0;
this._allowRefreshWithEdits = options && !!options.allowRefreshWithEdits;
if (options) {
var self = this;
$.each(options, function (key, value) {
if (queryOptions[key]) {
self[queryOptions[key]](value);
}
});
}
base.constructor.apply(this, arguments);
// Events specific to DataSource
this._bindFromOptions(options, [ "refreshStart", "refreshSuccess", "refreshError" ]);
};
var instanceMembers = {
// Public methods
// TODO -- These query set-* methods should be consolidated, passing a "settings" parameter.
// That way, we can issue a single "needs refresh" event when the client updates the query settings.
// TODO -- Changing query options should trigger "results stale".
setSort: function (sort) {
throw "Unreachable"; // Abstract/pure virtual method.
},
setFilter: function (filter) {
throw "Unreachable"; // Abstract/pure virtual method.
},
setPaging: function (paging) {
/// <summary>
/// Establishes the paging specification that is to be applied when loading model data.
/// </summary>
/// <param name="paging">
/// &#10;The paging specification to be applied when loading model data.
/// &#10;Should be supplied as an object of the form &#123; skip: &#60;number&#62;, take: &#60;number&#62;, includeTotalCount: &#60;bool&#62; &#125;. All properties on this object are optional.
/// &#10;When supplied as null or undefined, the paging specification for this DataSource is cleared.
/// </param>
/// <returns type="upshot.DataSource"/>
paging = paging || {};
this._skip = paging.skip;
this._take = paging.take;
this._includeTotalCount = !!paging.includeTotalCount;
return this;
},
getTotalEntityCount: function () {
/// <summary>
/// Returns the total entity count from the last refresh operation on this DataSource. This count will differ from DataSource.getEntities().length when a filter or paging specification is applied to this DataSource.
/// </summary>
/// <returns type="Number"/>
// NOTE: We had been updating this to reflect internal, client-only adds, but this doesn't
// generalize nicely. For instance, a RemoteDataSource might have server-only logic that
// determines whether an added entity should be included in a filtered query result.
// TODO: Revisit this conclusion.
return this._lastRefreshTotalEntityCount;
},
refresh: function (options) {
throw "Unreachable"; // Abstract/pure virtual method.
},
reset: function () {
/// <summary>
/// Empties the result array for this DataSource (that is, the array returned by DataSource.getEntities()). The result array can be repopulated using DataSource.refresh().
/// </summary>
/// <returns type="Number"/>
this._applyNewQueryResult([]);
return this;
},
// Private methods
// acceptable filter parameter
// { property: "Id", operator: "==", value: 1 } // default operator is "=="
// and an array of such
_normalizeFilters: function (filter) {
filter = upshot.isArray(filter) ? filter : [filter];
var filters = [];
for (var i = 0; i < filter.length; i++) {
var filterPart = filter[i];
if (filterPart) {
if (!$.isFunction(filterPart)) {
filterPart.operator = filterPart.operator || "==";
}
filters.push(filterPart);
}
}
return filters;
},
_verifyOkToRefresh: function () {
if (!this._allowRefreshWithEdits) {
var self = this;
$.each(obs.asArray(this._clientEntities), function (unused, entity) {
if (self.getEntityState(entity) !== upshot.EntityState.Unmodified) {
throw "Refreshing this DataSource will potentially remove unsaved entities. Such entities might encounter errors during save, and your app should have UI to view such errors. Either disallow DataSource.refresh() with edits or build error UI and suppress this exception with the 'allowRefreshWithEdits' DataSource option.";
}
});
}
},
_completeRefresh: function (entities, totalCount, success) {
if (this._applyNewQueryResult(entities, totalCount)) {
upshot.__triggerRecompute();
}
var newClientEntities = obs.asArray(this._clientEntities),
newTotalCount = this._lastRefreshTotalEntityCount;
this._trigger("refreshSuccess", newClientEntities, newTotalCount);
if ($.isFunction(success)) {
success.call(this, newClientEntities, newTotalCount);
}
},
_failRefresh: function (httpStatus, errorText, context, fail) {
this._trigger("refreshError", httpStatus, errorText, context);
if ($.isFunction(fail)) {
fail.call(this, httpStatus, errorText, context);
}
},
_applyNewQueryResult: function (entities, totalCount) {
this._lastRefreshTotalEntityCount = totalCount;
var sameEntities = upshot.sameArrayContents(obs.asArray(this._clientEntities), entities);
if (!sameEntities) {
// Update our client entities.
var oldEntities = obs.asArray(this._clientEntities).slice();
obs.refresh(this._clientEntities, entities);
this._trigger("arrayChanged", "replaceAll", { oldItems: oldEntities, newItems: obs.asArray(this._clientEntities) });
return true;
} else {
return false;
}
}
};
upshot.DataSource = upshot.deriveClass(base, ctor, instanceMembers);
}
///#RESTORE )(this, jQuery, upshot);

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,232 @@
/// <reference path="IntelliSense\References.js" />
///#RESTORE (function (global, $, upshot, undefined)
{
var obs = upshot.observability;
var ctor = function (options) {
var result = options && options.result;
if (result) {
if (upshot.EntitySource.as(result)) {
throw "Array is already bound to an EntitySource.";
}
}
this._viewsToRecompute = [];
this._eventCallbacks = {};
this._clientEntities = result || obs.createCollection();
// Events shared by subclasses
this._bindFromOptions(options, [ "arrayChanged", "propertyChanged", "entityStateChanged" ]);
var self = this;
obs.track(this._clientEntities, {
afterChange: function (array, type, eventArguments) {
upshot.__beginChange();
self._handleArrayChange(type, eventArguments);
},
afterEvent: function () {
upshot.__endChange();
}
});
upshot.cache(this._clientEntities, "entitySource", this);
};
var instanceMembers = {
// Public methods
dispose: function () {
/// <summary>
/// Disposes the EntitySource instance.
/// </summary>
if (this._eventCallbacks) { // Use _eventCallbacks as an indicator as to whether we've been disposed.
obs.track(this._clientEntities, null);
upshot.deleteCache(this._clientEntities, "entitySource");
this._dispose(); // Give subclass code an opportunity to clean up.
this._eventCallbacks = null;
}
},
// TODO: bind/unbind/_trigger are duplicated in EntitySource and DataContext, consider common routine.
bind: function (event, callback) {
/// <summary>
/// Registers the supplied callback to be called when an event is raised.
/// </summary>
/// <param name="event" type="String">
/// &#10;The event name.
/// </param>
/// <param name="callback" type="Function">
/// &#10;The callback function.
/// </param>
/// <returns type="upshot.EntitySource"/>
if (typeof event === "string") {
var list = this._eventCallbacks[event] || (this._eventCallbacks[event] = []);
list.push(callback);
} else {
for (var key in event) {
this.bind(key, event[key]);
}
}
return this;
},
unbind: function (event, callback) {
/// <summary>
/// Deregisters the supplied callback for the supplied event.
/// </summary>
/// <param name="event" type="String">
/// &#10;The event name.
/// </param>
/// <param name="callback" type="Function">
/// &#10;The callback function to be deregistered.
/// </param>
/// <returns type="upshot.EntitySource"/>
if (typeof event === "string") {
var list = this._eventCallbacks && this._eventCallbacks[event];
if (list) {
for (var i = 0, l = list.length; i < l; i++) {
if (list[i] === callback) {
list.splice(i, 1);
break;
}
}
}
} else {
for (var key in event) {
this.unbind(key, event[key]);
}
}
return this;
},
getEntities: function () {
/// <summary>
/// Returns the stable, observable array of model data.
/// </summary>
/// <returns type="Array"/>
return this._clientEntities;
},
// Internal methods
__registerForRecompute: function (entityView) {
if ($.inArray(entityView, this._viewsToRecompute) <= 0) {
this._viewsToRecompute.push(entityView);
}
},
__recomputeDependentViews: function () {
while (this._viewsToRecompute.length > 0) { // Downstream entity views might be dirtied due to recompute.
var viewsToRecompute = this._viewsToRecompute.slice();
this._viewsToRecompute.splice(0, this._viewsToRecompute.length);
$.each(viewsToRecompute, function (index, entityView) {
entityView.__recompute();
});
}
},
// Used to translate entity inserts through EntityViews (and onto their input EntitySource).
__addEntity: function (entity) {
var index = obs.asArray(this._clientEntities).length;
obs.insert(this._clientEntities, index, [entity]);
this._handleArrayChange("insert", { index: index, items: [entity] });
},
// Used to translate entity removes through EntityViews (and onto their input EntitySource).
__deleteEntity: function (entity, index) {
var index = $.inArray(entity, obs.asArray(this._clientEntities));
///#DEBUG
upshot.assert(index >= 0, "entity must exist!");
///#ENDDEBUG
obs.remove(this._clientEntities, index, 1);
this._handleArrayChange("remove", { index: index, items: [entity] });
},
// Private methods
_bindFromOptions: function (options, events) {
if (options) {
var self = this;
$.each(events, function (unused, event) {
var callback = options && options[event];
if (callback) {
self.bind(event, callback);
}
});
}
},
_dispose: function () {
// Will be overridden by derived classes.
},
_handleArrayChange: function (type, eventArguments) {
switch (type) {
case "insert":
var entitiesToAdd = eventArguments.items;
if (entitiesToAdd.length > 1) {
throw "NYI -- Can only add a single entity to/from an array in one operation.";
}
var entityToAdd = entitiesToAdd[0];
this._handleEntityAdd(entityToAdd);
break;
case "remove":
throw "Use 'deleteEntity' to delete entities from your array. Destructive delete is not yet implemented.";
case "replaceAll":
if (!upshot.sameArrayContents(eventArguments.newItems, obs.asArray(this._clientEntities))) {
throw "NYI -- Can only replaceAll with own entities.";
}
break;
default:
throw "NYI -- Array operation '" + type + "' is not supported.";
}
this._trigger("arrayChanged", type, eventArguments);
},
_handleEntityAdd: function (entity) {
// Will be overridden by derived classes to do specific handling for an entity add.
},
_purgeEntity: function (entity) {
// TODO -- Should we try to handle duplicates here?
var index = $.inArray(entity, obs.asArray(this._clientEntities));
obs.remove(this._clientEntities, index, 1);
this._trigger("arrayChanged", "remove", { index: index, items: [entity] });
},
_trigger: function (eventType) {
var list = this._eventCallbacks[eventType];
if (list) {
var args = Array.prototype.slice.call(arguments, 1);
// clone the list to be robust against bind/unbind during callback
list = list.slice(0);
for (var i = 0, l = list.length; i < l; i++) {
list[i].apply(this, args);
}
}
return this;
}
};
var classMembers = {
as: function (array) {
return upshot.cache(array, "entitySource");
}
};
upshot.EntitySource = upshot.defineClass(ctor, instanceMembers, classMembers);
}
///#RESTORE )(this, jQuery, upshot);

View File

@@ -0,0 +1,327 @@
/// <reference path="IntelliSense\References.js" />
///#RESTORE (function (global, $, upshot, undefined)
{
var base = upshot.EntitySource.prototype;
var obs = upshot.observability;
var ctor = function (options) {
this._needRecompute = false;
var self = this;
this._observer = {
propertyChanged: function (entity, property, newValue) { self._onPropertyChanged(entity, property, newValue); },
arrayChanged: function (type, eventArgs) { self._onArrayChanged(type, eventArgs); },
entityStateChanged: function (entity, state, error) { self._onEntityStateChanged(entity, state, error); },
entityUpdated: function (entity, path, eventArgs) { self._onEntityUpdated(entity, path, eventArgs); }
};
// RemoteDataSource may dynamically bind to its EntitySet as it refreshes.
this._entitySource = null; // Make JS runtime type inference happy?
var entitySource = options && options.source;
if (entitySource) {
this._bindToEntitySource(entitySource);
}
base.constructor.call(this, options);
};
var dataContextMethodNames = [
"getDataContext",
"getEntityState",
"getEntityValidationRules",
"getEntityId",
"revertChanges",
"deleteEntity",
"getEntityErrors",
"getEntityError",
"isUpdated",
"revertUpdates"
];
var instanceMembers = {
///#DEBUG
getDataContext: function () {
/// <summary>
/// Returns the DataContext used as a cache for model data.
/// </summary>
/// <returns type="upshot.DataContext"/>
throw "Not reached"; // For Intellisense only.
},
getEntityState: function (entity) {
/// <summary>
/// Returns the EntityState for the supplied entity.
/// </summary>
/// <param name="entity" type="Object">
/// &#10;The entity for which EntityState will be returned.
/// </param>
/// <returns type="upshot.EntityState"/>
throw "Not reached"; // For Intellisense only.
},
getEntityValidationRules: function () {
/// <summary>
/// Returns entity validation rules for the type of entity returned by this EntityView.
/// </summary>
/// <returns type="Object"/>
throw "Not reached"; // For Intellisense only.
},
getEntityId: function (entity) {
/// <summary>
/// Returns an identifier for the supplied entity.
/// </summary>
/// <param name="entity" type="Object"/>
/// <returns type="String"/>
throw "Not reached"; // For Intellisense only.
},
revertChanges: function (all) {
/// <summary>
/// Reverts any edits to model data (to entities) back to original entity values.
/// </summary>
/// <param name="all" type="Boolean" optional="true">
/// &#10;Revert all model edits in the underlying DataContext (to all types of entities). Otherwise, revert changes only to those entities of the type loaded by this EntityView.
/// </param>
/// <returns type="upshot.EntityView"/>
throw "Not reached"; // For Intellisense only.
},
deleteEntity: function (entity) {
/// <summary>
/// Marks the supplied entity for deletion. This is a non-destructive operation, meaning that the entity will remain in the EntityView.getEntities() array until the server commits the delete.
/// </summary>
/// <param name="entity" type="Object">
/// &#10;The entity to be marked for deletion.
/// </param>
/// <returns type="upshot.EntityView"/>
throw "Not reached"; // For Intellisense only.
},
getEntityErrors: function () {
/// <summary>
/// Returns an array of server errors by entity, of the form [ &#123; entity: &#60;entity&#62;, error: &#60;object&#62; &#125;, ... ].
/// </summary>
/// <returns type="Array"/>
throw "Not reached"; // For Intellisense only.
},
getEntityError: function (entity) {
/// <summary>
/// Returns server errors for the supplied entity.
/// </summary>
/// <param name="entity" type="Object">
/// &#10;The entity for which server errors are to be returned.
/// </param>
/// <returns type="Object"/>
throw "Not reached"; // For Intellisense only.
},
isUpdated: function (entity, path, ignoreChildren) {
/// <summary>
/// Returns whether the entity of any of the objects or arrays it contains are updated. When a path is specified,
/// it returns whether the specified property of any of its children are updated. This function will never return
/// 'true' for entities not in the 'ClientUpdated' state.
/// </summary>
/// <param name="entity" type="Object">
/// &#10;The entity to check for updates
/// </param>
/// <param name="path" type="String" optional="true">
/// &#10;The path to the property to check for updates. The path should be valid javascript; for example "Addresses[3].Street".
/// </param>
/// <param name="ignoreChildren" type="Boolean" optional="true">
/// &#10;Whether or not updates to the children of the specified property should be considered in the result
/// </param>
/// <returns type="Boolean"/>
throw "Not reached"; // For Intellisense only.
},
revertUpdates: function (entity, path, skipChildren) {
/// <summary>
/// Reverts updates to the entity and all the objects or arrays it contains. When a path is specified, it will
/// revert only updates to the specified property and all of its children. This function is a no-op for entities
/// not in the 'ClientUpdated' state.
/// </summary>
/// <param name="entity" type="Object">
/// &#10;The entity to revert updates for
/// </param>
/// <param name="path" type="String" optional="true">
/// &#10;The path to the property to revert updates for. The path should be valid javascript; for example "Addresses[3].Street".
/// </param>
/// <param name="skipChildren" type="Boolean" optional="true">
/// &#10;Whether or not to revert updates to the children of the specified property
/// </param>
/// <returns type="upshot.EntityView"/>
throw "Not reached"; // For Intellisense only.
},
///#ENDDEBUG
// Internal methods
__registerForRecompute: function (entityView) {
// Some EntityView that depends on us wants to recompute.
base.__registerForRecompute.apply(this, arguments);
// Register on our input EntitySource transitively back to the root EntitySources, from which
// our recompute wave originates.
this._entitySource.__registerForRecompute(this);
},
__recompute: function () {
// Our input EntitySource is giving us an opportunity to recompute. Do so, if we've so-marked.
if (this._needRecompute) {
this._needRecompute = false;
this._recompute();
}
// Tell EntityViews that depend on us to recompute.
this.__recomputeDependentViews();
},
// Private methods
_dispose: function () {
if (this._entitySource) { // RemoteDataSource dynamically binds to its input EntitySource.
this._entitySource.unbind(this._observer);
}
base._dispose.apply(this, arguments);
},
_bindToEntitySource: function (entitySource) {
if (this._entitySource === entitySource) {
return;
}
var self = this;
// Remove proxied DataContext-derived methods.
if (this._entitySource) {
$.each(dataContextMethodNames, function (index, name) {
if (self[name]) {
delete self[name];
}
});
this._entitySource.unbind(this._observer);
}
this._entitySource = entitySource;
// Proxy these DataContext-derived methods, if they're available.
if (entitySource.getDataContext) {
$.each(dataContextMethodNames, function (index, name) {
if (name !== "getEntityErrors") {
// Don't use $.proxy here, as that will statically bind to entitySource[name] and
// RemoteDataSource will dynamically change entitySource[name].
self[name] = function () {
var ret = entitySource[name].apply(entitySource, arguments);
return (name === "deleteEntity") ? self : ret;
};
}
});
}
this.getEntityErrors = function () {
return $.grep(entitySource.getEntityErrors(), function (error) {
return self._haveEntity(error.entity);
});
};
entitySource.bind(this._observer);
},
_setNeedRecompute: function () {
// Sub-classes will call this method to mark themselves as being dirty, requiring recompute.
this._needRecompute = true;
this._entitySource.__registerForRecompute(this);
},
_recompute: function () {
// In response to a call to _setNeedRecompute, we're getting called to recompute as
// part of the next recompute wave.
throw "Unreachable"; // Abstract/pure virtual method.
},
_handleEntityAdd: function (entity) {
// Translate adds onto our input EntitySource.
this._entitySource.__addEntity(entity);
base._handleEntityAdd.apply(this, arguments);
},
_haveEntity: function (entity) {
return $.inArray(entity, obs.asArray(this._clientEntities)) >= 0;
},
_purgeEntity: function (entity) {
base._purgeEntity.apply(this, arguments);
},
_onPropertyChanged: function (entity, property, newValue) {
// Translate property changes from our input EntitySource onto our result, if appropriate.
// NOTE: _haveEntity will be with respect to our current, stable set of result entities.
// Ignoring direct, observable inserts and removes, this result set will only change
// as part of our separate recompute wave, which happens _after_ such data change events.
if (this._haveEntity(entity)) {
this._trigger("propertyChanged", entity, property, newValue);
}
},
_onArrayChanged: function (type, eventArgs) {
// NOTE: These are not translated directly in the same way that property and entity state
// change events are. Rather, subclasses have specific logic as to how changes to the
// membership of their input EntitySource impacts their result entity membership.
// Will be overridden by derived classes.
},
_onEntityStateChanged: function (entity, state, error) {
if (this._haveEntity(entity)) {
if (state === upshot.EntityState.Deleted) {
// Entities deleted from our cache (due to an accepted server delete or due to a
// reverted internal add) should disappear from all dependent EntityViews.
this._purgeEntity(entity);
}
// Translate entity state changes from our input EntitySource onto our result, if appropriate.
// NOTE: _haveEntity will be with respect to our current, stable set of result entities.
// Ignoring direct, observable inserts and removes, this result set will only change
// as part of our separate recompute wave, which happens _after_ such change events.
this._trigger("entityStateChanged", entity, state, error);
}
},
_onEntityUpdated: function (entity, path, eventArgs) {
// Translate property changes from our input EntitySource onto our result, if appropriate.
// NOTE: _haveEntity will be with respect to our current, stable set of result entities.
// Ignoring direct, observable inserts and removes, this result set will only change
// as part of our separate recompute wave, which happens _after_ such data change events.
if (this._haveEntity(entity)) {
this._trigger("entityUpdated", entity, path, eventArgs);
}
}
};
upshot.EntityView = upshot.deriveClass(base, ctor, instanceMembers);
upshot.EntityView.__dataContextMethodNames = dataContextMethodNames;
}
///#RESTORE )(this, jQuery, upshot);

View File

@@ -0,0 +1,5 @@
/// <reference path="jquery-1.5.2-vsdoc.js" />
/// <reference path="knockout-2.0.0.debug.js" />
// top-level objects - these could possibly be transitioned to script dependencies
var upshot = {}, WinJS = {};
WinJS.Namespace = {};

View File

@@ -0,0 +1,21 @@
/// <reference path="Dependencies.js" />
/// <reference path="..\Core.js" />
/// <reference path="..\AssociatedEntitiesView.js" />
/// <reference path="..\Upshot.Compat.jQueryUI.js" />
/// <reference path="..\Upshot.Compat.JsViews.js" />
/// <reference path="..\Upshot.Compat.Knockout.js" />
/// <reference path="..\Upshot.Compat.WinJS.js" />
/// <reference path="..\DataContext.js" />
/// <reference path="..\DataProvider.js" />
/// <reference path="..\DataProvider.OData.js" />
/// <reference path="..\DataProvider.datacontroller.js" />
/// <reference path="..\DataProvider.json.js" />
/// <reference path="..\DataSource.js" />
/// <reference path="..\EntitySet.js" />
/// <reference path="..\EntitySource.js" />
/// <reference path="..\EntityView.js" />
/// <reference path="..\LocalDataSource.js" />
/// <reference path="..\Observability.js" />
/// <reference path="..\RemoteDataSource.js" />
// This file enables VS IntelliSense in JavaScript, otherwise it can be removed

View File

@@ -0,0 +1 @@
7774a0adb694ea5996f31763d900a2abba04a57d

Some files were not shown because too many files have changed in this diff Show More