Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

First pass

  • Loading branch information...
commit 47b1011cfaeb085ec4d52ec9cbf314a22f270558 0 parents
@jtraband jtraband authored
Showing with 60,835 additions and 0 deletions.
  1. +22 −0 .gitignore
  2. +80 −0 Breeze.Client/Scripts/IBlade/Old/pubsub.old.js
  3. +302 −0 Breeze.Client/Scripts/IBlade/assertParam.js
  4. +9 −0 Breeze.Client/Scripts/IBlade/build.ps1
  5. +4 −0 Breeze.Client/Scripts/IBlade/buildend.frag
  6. +12 −0 Breeze.Client/Scripts/IBlade/buildstart.frag
  7. +12 −0 Breeze.Client/Scripts/IBlade/buildx.js
  8. +37 −0 Breeze.Client/Scripts/IBlade/core.js
  9. +323 −0 Breeze.Client/Scripts/IBlade/coreFns.js
  10. +113 −0 Breeze.Client/Scripts/IBlade/coreFns.notyetneeded.js
  11. +178 −0 Breeze.Client/Scripts/IBlade/dataType.js
  12. +183 −0 Breeze.Client/Scripts/IBlade/defaultPropertyInterceptor.js
  13. +912 −0 Breeze.Client/Scripts/IBlade/entityAspect.js
  14. +2,143 −0 Breeze.Client/Scripts/IBlade/entityManager.js
  15. +1,475 −0 Breeze.Client/Scripts/IBlade/entityMetadata.js
  16. +35 −0 Breeze.Client/Scripts/IBlade/entityModel.js
  17. +1,852 −0 Breeze.Client/Scripts/IBlade/entityQuery.js
  18. +38 −0 Breeze.Client/Scripts/IBlade/entityTrackingInterface.js
  19. +170 −0 Breeze.Client/Scripts/IBlade/entityTracking_backingStore.js
  20. +92 −0 Breeze.Client/Scripts/IBlade/entityTracking_ko.js
  21. +267 −0 Breeze.Client/Scripts/IBlade/enum.js
  22. +177 −0 Breeze.Client/Scripts/IBlade/event.js
  23. +135 −0 Breeze.Client/Scripts/IBlade/keyGenerator.js
  24. +40 −0 Breeze.Client/Scripts/IBlade/keyGeneratorInterface.js
  25. +135 −0 Breeze.Client/Scripts/IBlade/relationArray.js
  26. +40 −0 Breeze.Client/Scripts/IBlade/remoteAccessInterface.js
  27. +112 −0 Breeze.Client/Scripts/IBlade/remoteAccess_odata.js
  28. +137 −0 Breeze.Client/Scripts/IBlade/remoteAccess_webApi.js
  29. +10 −0 Breeze.Client/Scripts/IBlade/root.js
  30. +120 −0 Breeze.Client/Scripts/IBlade/testFns.js
  31. +650 −0 Breeze.Client/Scripts/IBlade/validate.js
  32. +10 −0 Breeze.Client/Scripts/IBlade/yuidoc.json
  33. +487 −0 Breeze.Client/Scripts/Tests/attachTests.js
  34. +138 −0 Breeze.Client/Scripts/Tests/classRewriteTests.js
  35. +293 −0 Breeze.Client/Scripts/Tests/entityManagerTests.js
  36. +179 −0 Breeze.Client/Scripts/Tests/entityQueryCtorTests.js
  37. +384 −0 Breeze.Client/Scripts/Tests/entityTests.js
  38. +69 −0 Breeze.Client/Scripts/Tests/example.pavlovtest.js
  39. +84 −0 Breeze.Client/Scripts/Tests/metadataTests.js
  40. +475 −0 Breeze.Client/Scripts/Tests/miscTests.js
  41. +139 −0 Breeze.Client/Scripts/Tests/paramTests.js
  42. +1,089 −0 Breeze.Client/Scripts/Tests/queryTests.js
  43. +705 −0 Breeze.Client/Scripts/Tests/saveTests.js
  44. +24 −0 Breeze.Client/Scripts/Tests/testRunner.js
  45. +238 −0 Breeze.Client/Scripts/Tests/validateEntityTests.js
  46. +101 −0 Breeze.Client/Scripts/Tests/validateTests.js
  47. +277 −0 Breeze.Client/Scripts/ThirdParty/almond.js
  48. +7,225 −0 Breeze.Client/Scripts/ThirdParty/datajs-1.0.2.js
  49. +6,987 −0 Breeze.Client/Scripts/ThirdParty/jquery-1.6.2-vsdoc.js
  50. +8,981 −0 Breeze.Client/Scripts/ThirdParty/jquery-1.6.2.js
  51. +18 −0 Breeze.Client/Scripts/ThirdParty/jquery-1.6.2.min.js
  52. +3,223 −0 Breeze.Client/Scripts/ThirdParty/knockout-2.0.0.debug.js
  53. +97 −0 Breeze.Client/Scripts/ThirdParty/knockout-2.0.0.js
  54. +1,116 −0 Breeze.Client/Scripts/ThirdParty/modernizr-2.0.6-development-only.js
  55. +557 −0 Breeze.Client/Scripts/ThirdParty/q.README.md
  56. +1,465 −0 Breeze.Client/Scripts/ThirdParty/q.js
  57. +19 −0 Breeze.Client/Scripts/ThirdParty/q.min.js
  58. +225 −0 Breeze.Client/Scripts/ThirdParty/qunit.css
  59. +1,448 −0 Breeze.Client/Scripts/ThirdParty/qunit.js
  60. +324 −0 Breeze.Client/Scripts/ThirdParty/qunit.test.js
  61. +12,557 −0 Breeze.Client/Scripts/ThirdParty/r.js
  62. +2,053 −0 Breeze.Client/Scripts/ThirdParty/require.js
  63. +33 −0 Breeze.Client/Scripts/ThirdParty/require.min.js
Sorry, we could not display the entire diff because it was too big.
22 .gitignore
@@ -0,0 +1,22 @@
+.nuget
+.svn
+_ReSharper*
+Sample_Odata
+Sample_WebApi_RC
+Samples
+TestResults
+*.user
+*.docx
+__Notes.txt
+*.vsmdi
+*.cmd
+*.testsettings
+bin
+obj
+*.old
+Unused
+*.suo
+Breeze.Client/Breeze.Client.csproj
+Breeze.Client/packages.config
+Breeze.sln
+packages
80 Breeze.Client/Scripts/IBlade/Old/pubsub.old.js
@@ -0,0 +1,80 @@
+
+define(["core"], function (core) {
+ var PubSub = function () {
+ // key is topic, value is { unsubKey, callback }
+ // unsubKey = { topic, token }
+ this._topicMap = {};
+ this.__nextToken = 1;
+ };
+
+ function handleExceptionDefault(e) {
+ // TODO: maybe log this
+ // for now do nothing;
+ }
+
+ PubSub.prototype.defaultExceptionHandler = handleExceptionDefault;
+
+ PubSub.prototype.publish = function (topic, data, publishAsync, handleException) {
+
+ function publishCore() {
+ subscribers.forEach(function (s) {
+ try {
+ s.callback(data);
+ } catch (e) {
+ e.context = "unable to publish on topic: " + topic;
+ if (handleException) {
+ handleException(e);
+ } else if (this.defaultExceptionHandler) {
+ defaultExceptionHandler(e);
+ } else {
+ handleExceptionDefault(e);
+ }
+ }
+ });
+ }
+
+ var subscribers = this._topicMap[topic];
+ if (!subscribers) return false;
+
+ if (publishAsync === true) {
+ setTimeout(publishCore, 0);
+ } else {
+ publishCore();
+ }
+ return true;
+ };
+
+ PubSub.prototype.subscribe = function (topic, callback) {
+ var subscribers = this._topicMap[topic];
+ if (!subscribers) {
+ subscribers = [];
+ this._topicMap[topic] = subscribers;
+ }
+
+ var unsubKey = { topic: topic, token: this._nextToken };
+ subscribers.push({ unsubKey: unsubKey, callback: callback });
+ ++this._nextToken;
+ return unsubKey;
+ };
+
+
+ PubSub.prototype.unsubscribe = function (unsubKey) {
+ var subscribers = this._topicMap[unsubKey.topic];
+ if (!subscribers) return false;
+ var ix = core.arrayIndexOf(subscribers, function (s) {
+ return s.token === unsubKey.token;
+ });
+ if (ix !== -1) {
+ subscribers.splice(ix, 1);
+ if (subscribers.length === 0) {
+ delete this._topicMap[unsubKey.topic];
+ }
+ return true;
+ } else {
+ return false;
+ }
+
+ };
+
+ return PubSub;
+});
302 Breeze.Client/Scripts/IBlade/assertParam.js
@@ -0,0 +1,302 @@
+
+define(["coreFns"], function (core) {
+
+ // The %1 parameter
+ // is required
+ // must be a %2
+ // must be an instance of %2
+ // must be an instance of the %2 enumeration
+ // must have a %2 property
+ // must be an array where each element
+ // is optional or
+
+ var Param = function (v, name) {
+ this.v = v;
+ this.name = name;
+ this._fns = [null];
+ this._pending = [];
+ };
+
+ Param.prototype.isBoolean = function () {
+ return this.isTypeOf('boolean');
+ };
+
+ Param.prototype.isString = function () {
+ return this.isTypeOf('string');
+ };
+
+ Param.prototype.isNumber = function () {
+ return this.isTypeOf('number');
+ };
+
+ Param.prototype.isFunction = function () {
+ return this.isTypeOf('function');
+ };
+
+ Param.prototype.isTypeOf = function (typeName) {
+ var result = function (that, v) {
+ if (v == null) return false;
+ if (typeof (v) === typeName) return true;
+ return false;
+ };
+ result.getMessage = function () {
+ return core.formatString(" must be a '%1'", typeName);
+ };
+ return this.compose(result);
+ };
+
+ Param.prototype.isInstanceOf = function (type, typeName) {
+ var result = function (that, v) {
+ if (v == null) return false;
+ return (v instanceof type);
+ };
+ typeName = typeName || type.prototype._$typeName;
+ result.getMessage = function () {
+ return core.formatString(" must be an instance of '%1'", typeName);
+ };
+ return this.compose(result);
+ };
+
+ Param.prototype.hasProperty = function (propertyName) {
+ var result = function (that, v) {
+ if (v == null) return false;
+ return (v[propertyName] !== undefined);
+ };
+ result.getMessage = function () {
+ return core.formatString(" must have a '%1' property ", propertyName);
+ };
+ return this.compose(result);
+ };
+
+ Param.prototype.isEnumOf = function (enumType) {
+ var result = function (that, v) {
+ if (v == null) false;
+ return enumType.contains(v);
+ };
+ result.getMessage = function () {
+ return core.formatString(" must be an instance of the '%1' enumeration", enumType.name);
+ };
+ return this.compose(result);
+ };
+
+ Param.prototype.isRequired = function () {
+ if (this.fn && !this._or) {
+ return this;
+ } else {
+ var result = function (that, v) {
+ return v != null;
+ };
+ result.getMessage = function () {
+ return " is required";
+ };
+ return this;
+ }
+ };
+
+ Param.prototype.isOptional = function () {
+ if (this._fn) {
+ setFn(this, makeOptional(this._fn));
+ } else {
+ this._pending.push(function (that, fn) {
+ return makeOptional(fn);
+ });
+ }
+ return this;
+ };
+
+ Param.prototype.isNonEmptyArray = function () {
+ return this.isArray(true);
+ };
+
+ Param.prototype.isArray = function (mustBeNonEmpty) {
+ if (this._fn) {
+ setFn(this, makeArray(this._fn, mustBeNonEmpty));
+ } else {
+ setFn(this, makeArray(null, mustBeNonEmpty));
+ this._pending.push(function (that, fn) {
+ return makeArray(fn, mustBeNonEmpty)
+ });
+ }
+ return this;
+ };
+
+ Param.prototype.or = function () {
+ this._fns.push(null);
+ this._fn = null;
+ return this;
+ };
+
+ Param.prototype.getMessage = function () {
+ var msg = this._fns.map(function (fn) {
+ return fn.getMessage();
+ }).join(", or it");
+ return core.formatString(this.MESSAGE_PREFIX, this.name) + " " + msg;
+ };
+
+ Param.prototype.check = function (defaultValue) {
+ var fn = compile(this);
+ if (!fn) return;
+ if (!fn(this, this.v)) {
+ throw new Error(this.getMessage());
+ }
+ if (this.v !== undefined) {
+ return this.v;
+ } else {
+ return defaultValue;
+ }
+ };
+
+ Param.prototype.checkMsg = function () {
+ var fn = compile(this);
+ if (!fn) return;
+ if (!fn(this, this.v)) {
+ return this.getMessage();
+ }
+ };
+
+ Param.prototype.withDefault = function(defaultValue) {
+ this.defaultValue = defaultValue;
+ return this;
+ };
+
+ Param.prototype.whereParam = function(propName) {
+ return this.parent.whereParam(propName);
+ };
+
+ Param.prototype.applyAll = function(instance, throwIfUnknownProperty) {
+ throwIfUnknownProperty = throwIfUnknownProperty == null ? true : throwIfUnknownProperty;
+ var clone = core.extend({ }, this.parent.config);
+ this.parent.params.forEach(function(p) {
+ if (throwIfUnknownProperty) delete clone[p.name];
+ p._applyOne(instance, p.defaultValue);
+ });
+ // should be no properties left in the clone
+ if (throwIfUnknownProperty) {
+ for (var key in clone) {
+ throw new Error("Invalid property in config: " + key);
+ }
+ }
+ };
+
+ Param.prototype._applyOne = function (instance, defaultValue) {
+ this.check();
+ if (this.v !== undefined) {
+ instance[this.name] = this.v;
+ } else {
+ if (defaultValue !== undefined) {
+ instance[this.name] = defaultValue;
+ }
+ }
+ };
+
+ Param.prototype.compose = function (fn) {
+
+ if (this._pending.length > 0) {
+ while (this._pending.length > 0) {
+ var pending = this._pending.pop();
+ fn = pending(this, fn);
+ }
+ setFn(this, fn);
+ } else {
+ if (this._fn) {
+ throw new Error("Illegal construction - use 'or' to combine checks");
+ }
+ setFn(this, fn);
+ }
+ return this;
+ };
+
+ Param.prototype.MESSAGE_PREFIX = "The '%1' parameter ";
+
+ var assertParam = function (v, name) {
+ return new Param(v, name);
+ };
+
+ var CompositeParam = function(config) {
+ if (typeof (config) !== "object") {
+ throw new Error("Configuration parameter should be an object, instead it is a: " + typeof (config) );
+ }
+ this.config = config;
+ this.params = [];
+ };
+
+ CompositeParam.prototype.whereParam = function(propName) {
+ var param = new Param(this.config[propName], propName);
+ param.parent = this;
+ this.params.push(param);
+ return param;
+ };
+
+ var assertConfig = function(config) {
+ return new CompositeParam(config);
+ };
+
+ // private functions
+
+ function makeOptional(fn) {
+ var result = function (that, v) {
+ if (v == null) return true;
+ return fn(that, v);
+ };
+
+ result.getMessage = function () {
+ return " is optional, or it" + fn.getMessage();
+ };
+ return result;
+ }
+
+ function makeArray(fn, mustNotBeEmpty) {
+ var result = function (that, v) {
+ if (!Array.isArray(v)) {
+ return false;
+ }
+ if (mustNotBeEmpty) {
+ if (v.length === 0) return false;
+ }
+ // allow standalone is array call.
+ if (!fn) return true;
+
+ return v.every(function (v1) {
+ return fn(that, v1);
+ });
+ };
+ result.getMessage = function () {
+ var arrayDescr = mustNotBeEmpty ? "a nonEmpty array" : "an array";
+ var element = fn ? " where each element" + fn.getMessage() : "";
+ return " must be " + arrayDescr + element;
+ };
+ return result;
+ }
+
+
+
+ function setFn(that, fn) {
+ that._fns[that._fns.length - 1] = fn;
+ that._fn = fn;
+ }
+
+ function compile(self) {
+ if (!self._compiledFn) {
+ // clear off last one if null
+ if (self._fns[self._fns.length - 1] == null) {
+ self._fns.pop();
+ }
+ if (self._fns.length === 0) {
+ return undefined;
+ }
+ self._compiledFn = function (that, v) {
+ return that._fns.some(function (fn) {
+ return fn(that, v);
+ });
+ };
+ };
+ return self._compiledFn;
+ }
+
+
+ // Param is exposed so that additional 'is' methods can be added to the prototype.
+ return { Param: Param, assertParam: assertParam, assertConfig: assertConfig };
+
+
+
+})
9 Breeze.Client/Scripts/IBlade/build.ps1
@@ -0,0 +1,9 @@
+$env:path += ";c:\program files (x86)\nodejs"
+
+node ../ThirdParty/r.js -o buildx.js
+
+node ../ThirdParty/r.js -o buildx.js out=../breeze.debug.js optimize=none
+
+
+# $a = new-object -comobject wscript.shell
+# $b = $a.popup("This is a test",0,"Test Message Box",1)
4 Breeze.Client/Scripts/IBlade/buildend.frag
@@ -0,0 +1,4 @@
+
+ var breeze = requirejs('root');
+ return breeze;
+}));
12 Breeze.Client/Scripts/IBlade/buildstart.frag
@@ -0,0 +1,12 @@
+/*
+ * Breeze.js - v0.0.1 - Copyright (c) 2012, IdeaBlade under the terms of the XXX
+ * license found at http://ideablade/...
+ * Author: Jay Traband
+ */
+(function (definitionFn) {
+ if (typeof define === "function") {
+ define([], definitionFn);
+ } else {
+ definitionFn();
+ }
+}( function() {
12 Breeze.Client/Scripts/IBlade/buildx.js
@@ -0,0 +1,12 @@
+({
+ name: "../ThirdParty/almond.js",
+ include: "root",
+ baseUrl: ".",
+ out: "../breeze.js",
+ wrap: {
+ startFile: "buildstart.frag",
+ endFile: "buildend.frag"
+ }
+})
+
+
37 Breeze.Client/Scripts/IBlade/core.js
@@ -0,0 +1,37 @@
+define(["coreFns", "enum", "event", "assertParam"],
+function (core, Enum, Event, m_assertParam) {
+
+ /**
+ Utility types and functions of generally global applicability.
+ @module core
+ @main core
+ **/
+ core.Enum = Enum;
+ core.Event = Event;
+ core.extend(core, m_assertParam);
+ core.config = {};
+ core.config.functionRegistry = {};
+ core.config.typeRegistry = { };
+
+ // this is needed for reflection purposes when deserializing an object that needs a ctor.
+ core.config.registerFunction = function (fn, fnName) {
+ core.assertParam(fn, "fn").isFunction().check();
+ core.assertParam(fnName, "fnName").isString().check();
+ fn.prototype._$fnName = fnName;
+ core.config.functionRegistry[fnName] = fn;
+ };
+
+
+ core.config.registerType = function (ctor, typeName) {
+ core.assertParam(ctor, "ctor").isFunction().check();
+ core.assertParam(typeName, "typeName").isString().check();
+ ctor.prototype._$typeName = typeName;
+ core.config.typeRegistry[typeName] = ctor;
+ };
+
+ core.config.stringifyPad = " ";
+
+ // core.config.remoteAccessImplementation
+ // core.config.trackingImplementation
+ return core;
+});
323 Breeze.Client/Scripts/IBlade/coreFns.js
@@ -0,0 +1,323 @@
+
+define(function () {
+ "use strict";
+
+ var hasOwnProperty = Object.prototype.hasOwnProperty;
+
+ // transform an object's values
+ function objectMapValue(obj, kvProjection) {
+ var value, newMap = {};
+ for (var key in obj) {
+ if (hasOwnProperty.call(obj, key)) {
+ value = kvProjection(key, obj[key]);
+ if (value !== undefined) {
+ newMap[key] = value;
+ }
+ }
+ }
+ return newMap;
+ }
+
+ // shink an object's surface
+ function objectFilter(obj, kvPredicate) {
+ var result = {};
+ for (var key in obj) {
+ if (hasOwnProperty.call(obj, key)) {
+ var value = obj[key];
+ if (kvPredicate(key, value)) {
+ result[key] = value;
+ }
+ }
+ }
+ return result;
+ };
+
+ // iterate over object
+ function objectForEach(obj, kvFn) {
+ for (var key in obj) {
+ if (hasOwnProperty.call(obj, key)) {
+ kvFn(key, obj[key]);
+ }
+ }
+ }
+
+ // Functional extensions
+
+ // can be used like: persons.filter(propEq("firstName", "John"))
+ function propEq(propertyName, value) {
+ return function (obj) {
+ return obj[propertyName] === value;
+ };
+ }
+
+ // can be used like persons.map(pluck("firstName"))
+ function pluck(propertyName) {
+ return function (obj) { return obj[propertyName]; };
+ }
+
+ // end functional extensions
+
+
+ function getOwnPropertyValues(source) {
+ var result = [];
+ for (var name in source) {
+ if (hasOwnProperty.call(source, name)) {
+ result.push(source[name]);
+ }
+ }
+ return result;
+ }
+
+ function extend(target, source) {
+ if (!source) return target;
+ for (var name in source) {
+ if (hasOwnProperty.call(source, name)) {
+ target[name] = source[name];
+ }
+ }
+ return target;
+ }
+
+
+ // array functions
+
+ function arrayFirst(array, predicate) {
+ for (var i = 0, j = array.length; i < j; i++) {
+ if (predicate(array[i])) {
+ return array[i];
+ }
+ }
+ return null;
+ }
+
+ function arrayIndexOf(array, predicate) {
+ for (var i = 0, j = array.length; i < j; i++) {
+ if (predicate(array[i])) return i;
+ }
+ return -1;
+ }
+
+ function arrayRemoveItem(array, callbackOrItem) {
+ var callback = isFunction(callbackOrItem) ? callbackOrItem : undefined;
+ var l = array.length;
+ for (var index = 0; index < l; index++) {
+ if (callback ? callback(array[index]) : (array[index] === callbackOrItem)) {
+ array.splice(index, 1);
+ return index;
+ }
+ }
+ return -1;
+ }
+
+ function arrayZip(a1, a2, callback) {
+
+ var result = [];
+ var n = Math.min(a1.length, a2.length);
+ for (var i = 0; i < n; ++i) {
+ result.push(callback(a1[i], a2[i]));
+ }
+
+ return result;
+ }
+
+ function arrayDistinct(array) {
+ array = array || [];
+ var result = [];
+ for (var i = 0, j = array.length; i < j; i++) {
+ if (result.indexOf(array[i]) < 0)
+ result.push(array[i]);
+ }
+ return result;
+ }
+
+ // much faster but only works on array items with a toString method that
+ // returns distinct string for distinct objects. So this is safe for arrays with primitive
+ // types but not for arrays with object types, unless toString() has been implemented.
+ function arrayDistinctUnsafe(array) {
+ var o = {}, i, l = array.length, r = [];
+ for (i = 0; i < l; i += 1) {
+ var v = array[i];
+ o[v] = v;
+ }
+ for (i in o) r.push(o[i]);
+ return r;
+ }
+
+ function arrayEquals(a1, a2, equalsFn) {
+ //Check if the arrays are undefined/null
+ if (!a1 || !a2) return false;
+
+ if (a1.length != a2.length) return false;
+
+ //go thru all the vars
+ for (var i = 0; i < a1.length; i++) {
+ //if the var is an array, we need to make a recursive check
+ //otherwise we'll just compare the values
+ if (typeof a1[i] == 'object') {
+ if (!arrayEquals(a1[i], a2[i])) return false;
+ } else {
+ if (equalsFn) {
+ if (!equalsFn(a1, a2)) return false;
+ } else {
+ if (a1[i] != a2[i]) return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ // end of array functions
+
+ function using(obj, property, tempValue, fn) {
+ var originalValue = obj[property];
+ if (tempValue === originalValue) {
+ return fn();
+ }
+ obj[property] = tempValue;
+ try {
+ return fn();
+ } finally {
+ obj[property] = originalValue;
+ }
+ }
+
+ function memoize(fn) {
+ return function () {
+ var args = Array.prototype.slice.call(arguments),
+ hash = "",
+ i = args.length,
+ currentArg = null;
+ while (i--) {
+ currentArg = args[i];
+ hash += (currentArg === Object(currentArg)) ?
+ JSON.stringify(currentArg) : currentArg;
+ fn.memoize || (fn.memoize = {});
+ }
+ return (hash in fn.memoize) ?
+ fn.memoize[hash] :
+ fn.memoize[hash] = fn.apply(this, args);
+ };
+ }
+
+ function getUuid() {
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
+ var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
+ return v.toString(16);
+ });
+ }
+
+ // is functions
+
+ function classof(o) {
+ if (o === null) {
+ return "null";
+ }
+ if (o === undefined) {
+ return "undefined";
+ }
+ return Object.prototype.toString.call(o).slice(8, -1).toLowerCase();
+ }
+
+ 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);
+ }
+
+ function isEmpty(obj) {
+ if (obj === null || obj === undefined) {
+ return true;
+ }
+ for (var key in obj) {
+ if (hasOwnProperty.call(obj, key)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ function isNumeric(n) {
+ return !isNaN(parseFloat(n)) && isFinite(n);
+ }
+
+ // end of is Functions
+
+
+
+ // string functions
+
+ function stringStartsWith(str, prefix) {
+ // returns false for empty strings too
+ if ((!str) || !prefix) return false;
+ return str.indexOf(prefix, 0) === 0;
+ }
+
+ function stringEndsWith(str, suffix) {
+ // returns false for empty strings too
+ if ((!str) || !suffix) return false;
+ return str.indexOf(suffix, str.length - suffix.length) !== -1;
+ }
+
+ // Based on fragment from Dean Edwards' Base 2 library
+ // format("a %1 and a %2", "cat", "dog") -> "a cat and a dog"
+ function formatString(string) {
+ var args = arguments;
+ var pattern = RegExp("%([1-" + (arguments.length - 1) + "])", "g");
+ return string.replace(pattern, function (match, index) {
+ return args[index];
+ });
+ };
+
+ // end of string functions
+
+ // shims
+
+ if (!Object.create) {
+ Object.create = function (parent) {
+ var F = function () { };
+ F.prototype = parent;
+ return new F();
+ };
+ }
+
+ return {
+ getOwnPropertyValues: getOwnPropertyValues,
+ objectForEach: objectForEach,
+ objectMapValue: objectMapValue,
+ objectFilter: objectFilter,
+
+ extend: extend,
+ propEq: propEq,
+ pluck: pluck,
+
+ arrayDistinct: arrayDistinct,
+ arrayDistinctUnsafe: arrayDistinctUnsafe,
+ arrayEquals: arrayEquals,
+ arrayFirst: arrayFirst,
+ arrayIndexOf: arrayIndexOf,
+ arrayRemoveItem: arrayRemoveItem,
+ arrayZip: arrayZip,
+
+ using: using,
+ memoize: memoize,
+ getUuid: getUuid,
+
+ isDate: isDate,
+ isGuid: isGuid,
+ isFunction: isFunction,
+ isEmpty: isEmpty,
+ isNumeric: isNumeric,
+
+ stringStartsWith: stringStartsWith,
+ stringEndsWith: stringEndsWith,
+ formatString: formatString
+ };
+
+});
+
113 Breeze.Client/Scripts/IBlade/coreFns.notyetneeded.js
@@ -0,0 +1,113 @@
+
+define(function () {
+ "use strict";
+
+ function stringTrimLeft(str) {
+ return str.replace(/^\s+/, '');
+ }
+
+ function stringTrimRight(str) {
+ return str.replace(/\s+$/, '');
+ }
+
+
+ // not sure if we have a good use case yet for these
+ function applyMethod(method, args) {
+ return function (obj) {
+ return method.apply(obj, args);
+ };
+ }
+
+ function applyFunction(fn) {
+ return function(obj) {
+ return fn(obj);
+ };
+ }
+
+ /* Returns the class name of the argument or undefined if
+ it's not a valid JavaScript object.
+ */
+ function getObjectClass(obj) {
+ if (obj && obj.constructor && obj.constructor.toString) {
+ var arr = obj.constructor.toString().match(
+ /function\s*(\w+)/);
+
+ if (arr && arr.length == 2) {
+ return arr[1];
+ }
+ }
+ return undefined;
+ }
+
+
+ function toISODateString(d) {
+ function pad(n) {
+ return n < 10 ? '0' + n : n;
+ }
+
+ return d.getUTCFullYear() + '-'
+ + pad(d.getUTCMonth() + 1) + '-'
+ + pad(d.getUTCDate()) + 'T'
+ + pad(d.getUTCHours()) + ':'
+ + pad(d.getUTCMinutes()) + ':'
+ + pad(d.getUTCSeconds()) + 'Z';
+ }
+
+ function getOwnPropertyNames(object) {
+ var result = [];
+ for (var name in object) {
+ if (object.hasOwnProperty(name)) {
+ result.push(name);
+ }
+ }
+ return result;
+ }
+
+ function isArray(o) {
+ return classof(o) === "array";
+ }
+
+ function isObject(o) {
+ return classof(o) === "object";
+ }
+
+
+ // object.watch polyfill
+ function watch(obj, prop, interceptor) {
+
+ var val = obj[prop],
+ getter = function () {
+ return val;
+ },
+ setter = function (newval) {
+ val = interceptor.call(obj, prop, val, newval);
+ };
+
+ if (delete this[prop]) { // can't watch constants
+ if (Object.defineProperty) { // ECMAScript 5
+ Object.defineProperty(this, prop, {
+ get: getter,
+ set: setter,
+ enumerable: false,
+ configurable: true
+ });
+ } else if (Object.prototype.__defineGetter__ && Object.prototype.__defineSetter__) { // legacy
+ Object.prototype.__defineGetter__.call(this, prop, getter);
+ Object.prototype.__defineSetter__.call(this, prop, setter);
+ }
+ }
+ }
+
+
+ function unwatch(obj, prop) {
+ var val = obj[prop];
+ delete obj[prop];
+ obj.prop = val;
+ }
+
+ // shims and shim like functions
+
+ if (!String.prototype.trim) {
+ String.prototype.trim = function () { return this.replace(/^\s\s*/, '').replace(/\s\s*$/, ''); };
+ }
+
178 Breeze.Client/Scripts/IBlade/dataType.js
@@ -0,0 +1,178 @@
+
+define(["core", "validate"],
+function (core, m_validate) {
+ /**
+ @module entityModel
+ **/
+
+ var Enum = core.Enum;
+ var Validator = m_validate.Validator;
+
+ /**
+ DataType is an 'Enum' containing all of the supported data types.
+
+ @class DataType
+ @static
+ **/
+
+ /**
+ The default value of this DataType.
+ @property defaultValue {any}
+ **/
+
+ /**
+ Whether this is a 'numeric' DataType.
+ @property isNumeric {Boolean}
+ **/
+
+ var dataTypeMethods = {
+
+ };
+
+ var DataType = new Enum("DataType", dataTypeMethods);
+ /**
+ @property String {symbol}
+ @final
+ @static
+ **/
+ DataType.String = DataType.addSymbol({ defaultValue: "" });
+ /**
+ @property Int64 {symbol}
+ @final
+ @static
+ **/
+ DataType.Int64 = DataType.addSymbol({ defaultValue: 0, isNumeric: true });
+ /**
+ @property Int32 {symbol}
+ @final
+ @static
+ **/
+ DataType.Int32 = DataType.addSymbol({ defaultValue: 0, isNumeric: true });
+ /**
+ @property Int16 {symbol}
+ @final
+ @static
+ **/
+ DataType.Int16 = DataType.addSymbol({ defaultValue: 0, isNumeric: true });
+ /**
+ @property Decimal {symbol}
+ @final
+ @static
+ **/
+ DataType.Decimal = DataType.addSymbol({ defaultValue: 0, isNumeric: true });
+ /**
+ @property Double {symbol}
+ @final
+ @static
+ **/
+ DataType.Double = DataType.addSymbol({ defaultValue: 0, isNumeric: true });
+ /**
+ @property Single {symbol}
+ @final
+ @static
+ **/
+ DataType.Single = DataType.addSymbol({ defaultValue: 0, isNumeric: true });
+ /**
+ @property DateTime {symbol}
+ @final
+ @static
+ **/
+ DataType.DateTime = DataType.addSymbol({ defaultValue: Date.now() });
+ /**
+ @property Boolean {symbol}
+ @final
+ @static
+ **/
+ DataType.Boolean = DataType.addSymbol({ defaultValue: false });
+ /**
+ @property Guid {symbol}
+ @final
+ @static
+ **/
+ DataType.Guid = DataType.addSymbol({ defaultValue: "00000000-0000-0000-0000-000000000000" });
+ /**
+ @property Byte {symbol}
+ @final
+ @static
+ **/
+ DataType.Byte = DataType.addSymbol({ defaultValue: 0 });
+ /**
+ @property Binary {symbol}
+ @final
+ @static
+ **/
+ DataType.Binary = DataType.addSymbol({ defaultValue: null });
+ /**
+ @property Undefined {symbol}
+ @final
+ @static
+ **/
+ DataType.Undefined = DataType.addSymbol({ defaultValue: undefined });
+ DataType.seal();
+ DataType.getSymbols().forEach(function (sym) {
+ sym.validatorCtor = getValidatorCtor(sym);
+ });
+
+ /**
+ Returns the DataType for a specified type name.
+ @method toDataType
+ @static
+ @param typeName {String}
+ @return A DataType enum value.
+ **/
+ DataType.toDataType = function (typeName) {
+ // if OData style
+ var dt;
+ var parts = typeName.split("Edm.");
+ if (parts.length > 1) {
+ if (parts[1] === "image") {
+ // hack
+ dt = DataType.Byte;
+ } else {
+ dt = DataType.fromName(parts[1]);
+ }
+ }
+
+ if (!dt) {
+ throw new Error("Unable to recognize DataType for: " + typeName);
+ }
+ return dt;
+ };
+
+ function getValidatorCtor(symbol) {
+ switch (symbol) {
+ case DataType.String:
+ return Validator.string;
+ case DataType.Int64:
+ return Validator.int64;
+ case DataType.Int32:
+ return Validator.int32;
+ case DataType.Int16:
+ return Validator.int16;
+ case DataType.Decimal:
+ return Validator.number;
+ case DataType.Double:
+ return Validator.number;
+ case DataType.Single:
+ return Validator.number;
+ case DataType.DateTime:
+ return Validator.date;
+ case DataType.Boolean:
+ return Validator.bool;
+ case DataType.Guid:
+ return Validator.guid;
+ case DataType.Byte:
+ return Validator.byte;
+ case DataType.Binary:
+ // TODO: don't quite know how to validate this yet.
+ return Validator.none;
+ case DataType.Undefined:
+ return Validator.none;
+ }
+ };
+
+
+ return DataType;
+
+});
+
183 Breeze.Client/Scripts/IBlade/defaultPropertyInterceptor.js
@@ -0,0 +1,183 @@
+
+define(["core", "entityAspect"],
+function (core, m_entityAspect) {
+
+ var EntityKey = m_entityAspect.EntityKey;
+ var EntityState = m_entityAspect.EntityState;
+ var EntityAction = m_entityAspect.EntityAction;
+
+ function defaultPropertyInterceptor(property, newValue, rawAccessorFn) {
+ // 'this' is the entity itself in this context.
+
+ var oldValue = rawAccessorFn();
+ // exit if no change
+ if (newValue === oldValue) {
+ return;
+ }
+ var propName = property.name;
+
+ // CANNOT DO NEXT LINE because it has the possibility of creating a new property
+ // 'entityAspect' on 'this'. - Not permitted by IE inside of a defined property on a prototype.
+ // var aspect = new EntityAspect(this);
+
+ var aspect = this.entityAspect;
+ if (aspect._inProcess && aspect._inProcess === property) {
+ // recursion avoided.
+ return;
+ }
+
+ // TODO: we actually need to handle multiple properties in process. not just one
+ // NOTE: this may not be needed because of the newValue === oldValue test above.
+ // to avoid recursion.
+ // We could use core.using here but decided not to for perf reasons - this method runs a lot.
+ // i.e core.using(aspect, "_inProcess", property, function() {...
+ aspect._inProcess = property;
+ try {
+
+ var entityManager = aspect.entityManager;
+ // store an original value for this property if not already set
+ if (aspect.entityState.isUnchangedOrModified()) {
+ if (!aspect.originalValues[propName] && property.isDataProperty) {
+ // the || property.defaultValue is to insure that undefined -> null;
+ // otherwise this entry will be skipped during serialization
+ aspect.originalValues[propName] = oldValue || property.defaultValue;
+ }
+ }
+
+ // set the value
+ if (property.isNavigationProperty) {
+ if (!property.isScalar) {
+ throw new Error("Nonscalar navigation properties are readonly - entities can be added or removed but the collection may not be changed.");
+ }
+
+ var inverseProp = property.inverse;
+ if (newValue) {
+ if (entityManager && newValue.entityAspect.entityState.isDetached()) {
+ entityManager.attachEntity(newValue, EntityState.Added);
+ }
+ // process related updates ( the inverse relationship) first so that collection dups check works properly.
+ // update inverse relationship
+
+ if (inverseProp) {
+ if (inverseProp.isScalar) {
+ // navigation property change - undo old relation
+ if (oldValue) {
+ // TODO: null -> NullEntity later
+ oldValue.setProperty(inverseProp.name, null);
+ }
+ newValue.setProperty(inverseProp.name, this);
+ } else {
+ // navigation property change - undo old relation
+ if (oldValue) {
+ var oldSiblings = oldValue.getProperty(inverseProp.name);
+ var ix = oldSiblings.indexOf(this);
+ oldSiblings.splice(ix, 1);
+ }
+ var siblings = newValue.getProperty(inverseProp.name);
+ // recursion check if already in the collection is performed by the relationArray
+ siblings.push(this);
+ }
+ }
+ }
+
+
+ rawAccessorFn(newValue);
+ if (entityManager && !entityManager.isLoading) {
+ if (aspect.entityState.isUnchanged()) {
+ aspect.setModified();
+ }
+ if (entityManager.validationOptions.validateOnPropertyChange) {
+ aspect._validateProperty(property, newValue, { entity: this, oldValue: oldValue });
+ }
+ }
+ // update fk data property
+ if (property.relatedDataProperties) {
+ if (!aspect.entityState.isDeleted()) {
+ var inverseKeyProps = inverseProp.parentEntityType.keyProperties;
+ if (inverseKeyProps.length !== 1 && !newValue) {
+ throw new Error("Only single property foreign keys are currently supported.");
+ }
+ var keyProp = inverseKeyProps[0];
+ var relatedValue = newValue ? newValue.getProperty(keyProp.name) : keyProp.defaultValue;
+
+ this.setProperty(property.relatedDataProperties[0].name, relatedValue);
+ }
+ }
+
+ } else {
+
+ // updating a dataProperty
+ rawAccessorFn(newValue);
+ // NOTE: next few lines are the same as above but not refactored for perf reasons.
+ if (entityManager && !entityManager.isLoading) {
+ if (aspect.entityState.isUnchanged()) {
+ aspect.setModified();
+ }
+ if (entityManager.validationOptions.validateOnPropertyChange) {
+ aspect._validateProperty(property, newValue, { entity: this, oldValue: oldValue });
+ }
+ }
+ // update corresponding nav property if attached.
+ if (property.relatedNavigationProperty && entityManager) {
+ var relatedNavProp = property.relatedNavigationProperty;
+ var key = new EntityKey(relatedNavProp.entityType, [newValue]);
+ var relatedEntity = entityManager.findEntityByKey(key);
+
+ if (relatedEntity) {
+ this.setProperty(relatedNavProp.name, relatedEntity);
+ } else {
+ // it may not have been fetched yet in which case we want to add it as an unattachedChild.
+ entityManager._unattachedChildrenMap.addChild(key, relatedNavProp, this);
+ }
+ }
+
+ if (property.isKeyProperty) {
+ // propogate pk change to all related entities;
+ if (oldValue && !aspect.entityState.isDetached()) {
+ aspect.primaryKeyWasChanged = true;
+ }
+
+ var that = this;
+ this.entityType.navigationProperties.forEach(function(np) {
+ var inverseNp = np.inverse;
+ if (!inverseNp) return;
+ if (!inverseNp.foreignKeyNames) return;
+ var npValue = that.getProperty(np.name);
+ var propertyIx = that.entityType.keyProperties.indexOf(property);
+ var fkName = inverseNp.foreignKeyNames[propertyIx];
+ if (np.isScalar) {
+ if (!npValue) return;
+ npValue.setProperty(fkName, newValue);
+
+ } else {
+ npValue.forEach(function(iv) {
+ iv.setProperty(fkName, newValue);
+ });
+ }
+ });
+ // insure that cached key is updated.
+ aspect.getKey(true);
+ }
+ }
+
+ var propChangedArgs = { propertyName: propName, oldValue: oldValue, newValue: newValue };
+ if (entityManager) {
+ // propertyChanged will be fired during loading but we only want to fire it once per entity, not once per property.
+ // so propertyChanged is also fired in the entityManager mergeEntity method.
+ if (entityManager.propertyChangeNotificationEnabled && !entityManager.isLoading) {
+ aspect.propertyChanged.publish(propChangedArgs);
+ }
+ if (entityManager.entityChangeNotificationEnabled) {
+ entityManager.entityChanged.publish({ entityAction: EntityAction.PropertyChange, entity: this, args: propChangedArgs });
+ }
+ } else {
+ aspect.propertyChanged.publish(propChangedArgs);
+ }
+ } finally {
+ aspect._inProcess = null;
+ }
+ }
+
+ return defaultPropertyInterceptor;
+
+});
912 Breeze.Client/Scripts/IBlade/entityAspect.js
@@ -0,0 +1,912 @@
+
+define(["core", "event", "validate"],
+function (core, Event, m_validate) {
+ /**
+ @module entityModel
+ **/
+
+ var Enum = core.Enum;
+ var assertParam = core.assertParam;
+
+ var Validator = m_validate.Validator;
+ var ValidationError = m_validate.ValidationError;
+
+ var EntityState = (function () {
+ /**
+ EntityState is an 'Enum' containing all of the valid states for an 'Entity'.
+
+ @class EntityState
+ @static
+ **/
+ var entityStateMethods = {
+ /**
+ @example
+ var es = anEntity.entityAspect.entityState;
+ return es.IsUnchanged();
+ is the same as
+ @example
+ return es === EntityState.Unchanged;
+ @method IsUnchanged
+ @return Whether an entityState instance is EntityState.Unchanged.
+ **/
+ isUnchanged: function () { return this === EntityState.Unchanged; },
+ /**
+ @example
+ var es = anEntity.entityAspect.entityState;
+ return es.IsAdded();
+ is the same as
+ @example
+ return es === EntityState.Added;
+ @method IsAdded
+ @return Whether an entityState instance is EntityState.Added.
+ **/
+ isAdded: function () { return this === EntityState.Added; },
+ /**
+ @example
+ var es = anEntity.entityAspect.entityState;
+ return es.IsModified();
+ is the same as
+ @example
+ return es === EntityState.Modified;
+ @method IsModified
+ @return Whether an entityState instance is EntityState.Modified.
+ **/
+ isModified: function () { return this === EntityState.Modified; },
+ /**
+ @example
+ var es = anEntity.entityAspect.entityState;
+ return es.isDeleted();
+ is the same as
+ @example
+ return es === EntityState.Deleted;
+ @method isDeleted
+ @return Whether an entityState instance is EntityState.Deleted.
+ **/
+ isDeleted: function () { return this === EntityState.Deleted; },
+ /**
+ @example
+ var es = anEntity.entityAspect.entityState;
+ return es.isDetached();
+ is the same as
+ @example
+ return es === EntityState.Detached;
+ @method isDetached
+ @return Whether an entityState instance is EntityState.Detached.
+ **/
+ isDetached: function () { return this === EntityState.Detached; },
+ /**
+ @example
+ var es = anEntity.entityAspect.entityState;
+ return es.isUnchangedOrModified();
+ is the same as
+ @example
+ return es === EntityState.Unchanged || es === EntityState.Modified
+ @method isUnchangedOrModified
+ @return Whether an entityState instance is EntityState.Unchanged or EntityState.Modified.
+ **/
+ isUnchangedOrModified: function () {
+ return this === EntityState.Unchanged || this === EntityState.Modified;
+ },
+ /**
+ @example
+ var es = anEntity.entityAspect.entityState;
+ return es.isAddedModifiedOrDeleted();
+ is the same as
+ @example
+ return es === EntityState.Added || es === EntityState.Modified || es === EntityState.Deleted
+ @method isAddedModifiedOrDeleted
+ @return Whether an entityState instance is EntityState.Unchanged or EntityState.Modified or EntityState.Deleted.
+ **/
+ isAddedModifiedOrDeleted: function () {
+ return this === EntityState.Added ||
+ this === EntityState.Modified ||
+ this === EntityState.Deleted;
+ }
+ };
+
+ var EntityState = new Enum("EntityState", entityStateMethods);
+ /**
+ The 'Unchanged' state.
+
+ @property Unchanged {symbol}
+ @final
+ @static
+ **/
+ EntityState.Unchanged = EntityState.addSymbol();
+ /**
+ The 'Added' state.
+
+ @property Added {symbol}
+ @final
+ @static
+ **/
+ EntityState.Added = EntityState.addSymbol();
+ /**
+ The 'Modified' state.
+
+ @property Modified {symbol}
+ @final
+ @static
+ **/
+ EntityState.Modified = EntityState.addSymbol();
+ /**
+ The 'Deleted' state.
+
+ @property Deleted {symbol}
+ @final
+ @static
+ **/
+ EntityState.Deleted = EntityState.addSymbol();
+ /**
+ The 'Detached' state.
+
+ @property Detached {symbol}
+ @final
+ @static
+ **/
+ EntityState.Detached = EntityState.addSymbol();
+ EntityState.seal();
+ return EntityState;
+ })();
+
+ var EntityAction = (function () {
+ /**
+ EntityAction is an 'Enum' containing all of the valid actions that can occur to an 'Entity'.
+
+ @class EntityAction
+ @static
+ **/
+ var entityActionMethods = {
+ isAttach: function () { return !!this.isAttach; },
+ isDetach: function () { return !!this.isDetach; },
+ isModification: function () { return !!this.isModification; }
+ };
+
+ var EntityAction = new Enum("EntityAction", entityActionMethods);
+
+ /**
+ Attach - Entity was attached via an AttachEntity call.
+
+ @property Attach {symbol}
+ @final
+ @static
+ **/
+ EntityAction.Attach = EntityAction.addSymbol({ isAttach: true});
+
+ /**
+ AttachOnQuery - Entity was attached as a result of a query.
+
+ @property AttachOnQuery {symbol}
+ @final
+ @static
+ **/
+ EntityAction.AttachOnQuery = EntityAction.addSymbol({ isAttach: true});
+
+ /**
+ AttachOnImport - Entity was attached as a result of an import.
+
+ @property AttachOnImport {symbol}
+ @final
+ @static
+ **/
+ EntityAction.AttachOnImport = EntityAction.addSymbol({ isAttach: true});
+
+
+ /**
+ AttachOnQuery - Entity was detached.
+
+ @property Detach {symbol}
+ @final
+ @static
+ **/
+ EntityAction.Detach = EntityAction.addSymbol( { isDetach: true });
+
+ /**
+ MergeOnQuery - Properties on the entity were merged as a result of a query.
+
+ @property MergeOnQuery {symbol}
+ @final
+ @static
+ **/
+ EntityAction.MergeOnQuery = EntityAction.addSymbol( { isModification: true });
+
+ /**
+ MergeOnImport - Properties on the entity were merged as a result of an import.
+
+ @property MergeOnImport {symbol}
+ @final
+ @static
+ **/
+ EntityAction.MergeOnImport = EntityAction.addSymbol( { isModification: true });
+
+ /**
+ MergeOnImport - Properties on the entity were merged as a result of a save
+
+ @property MergeOnImport {symbol}
+ @final
+ @static
+ **/
+ EntityAction.MergeOnSave = EntityAction.addSymbol( { isModification: true });
+
+ /**
+ PropertyChange - A property on the entity was changed.
+
+ @property PropertyChange {symbol}
+ @final
+ @static
+ **/
+ EntityAction.PropertyChange = EntityAction.addSymbol({ isModification: true});
+
+ /**
+ EntityStateChange - The EntityState of the entity was changed.
+
+ @property EntityStateChange {symbol}
+ @final
+ @static
+ **/
+ EntityAction.EntityStateChange = EntityAction.addSymbol();
+
+
+ /**
+ AcceptChanges - AcceptChanges was called on the entity, or its entityState was set to Unmodified.
+
+ @property AcceptChanges {symbol}
+ @final
+ @static
+ **/
+ EntityAction.AcceptChanges = EntityAction.addSymbol();
+
+ /**
+ RejectChanges - RejectChanges was called on the entity.
+
+ @property RejectChanges {symbol}
+ @final
+ @static
+ **/
+ EntityAction.RejectChanges = EntityAction.addSymbol({ isModification: true});
+
+ /**
+ Clear - The EntityManager was cleared. All entities detached.
+
+ @property Clear {symbol}
+ @final
+ @static
+ **/
+ EntityAction.Clear = EntityAction.addSymbol({ isDetach: true});
+
+ EntityAction.seal();
+ return EntityAction;
+ })();
+
+ var EntityAspect = function () {
+ /**
+ An EntityAspect instance is associated with every attached entity and is accessed via the entity's 'entityAspect' property.
+
+ The EntityAspect itself provides properties to determine and modify the EntityState of the entity and has methods
+ that provide a variety of services including validation and change tracking.
+
+ An EntityAspect will almost never need to be constructed directly. You will usually get an EntityAspect by accessing
+ an entities 'entityAspect' property. This property will be automatically attached when an entity is created via either
+ a query, import or EntityManager.createEntity call.
+
+ // assume order is an order entity attached to an EntityManager.
+ var aspect = order.entityAspect;
+ var currentState = aspect.entityState;
+ @class EntityAspect
+ **/
+ var ctor = function (entity) {
+ if (!entity) {
+ throw new Error("The EntityAspect ctor requires an entity as its only argument.");
+ }
+ if (entity.entityAspect) {
+ return entity.entityAspect;
+ }
+ // if called without new
+ if (!(this instanceof EntityAspect)) {
+ return new EntityAspect(entity);
+ }
+
+ // entityType should already be on the entity from 'watch'
+ this.entity = entity;
+ entity.entityAspect = this;
+
+ // TODO: keep public or not?
+ this.entityGroup = null;
+ this.entityManager = null;
+ this.entityState = EntityState.Detached;
+ this.isBeingSaved = false;
+ this.originalValues = {};
+ this._validationErrors = {};
+ this.validationErrorsChanged = new Event("validationErrorsChanged");
+ this.propertyChanged = new Event("propertyChanged");
+ var entityType = entity.entityType;
+ if (!entityType) {
+ throw new Error("Tracking has not yet been enabled on this entity");
+ }
+ var proto = entityType.getEntityCtor().prototype;
+ core.config.trackingImplementation.startTracking(entity, proto);
+ };
+
+ /**
+ The Entity that this aspect is associated with.
+
+ __readOnly__
+ @property entity {Entity}
+ **/
+
+ /**
+ The {{#crossLink "EntityManager"}}{{/crossLink}} that contains this entity.
+
+ __readOnly__
+ @property entityManager {EntityManager}
+ **/
+
+ /**
+ The {{#crossLink "EntityState"}}{{/crossLink}} of this entity.
+
+ __readOnly__
+ @property entityState {EntityState}
+ **/
+
+ /**
+ Whether this entity is in the process of being saved.
+
+ __readOnly__
+ @property isBeingSaved {Boolean}
+ **/
+
+ /**
+ The 'original values' of this entity where they are different from the 'current values'.
+ This is a map where the key is a property name and the value is the 'original value' of the property.
+
+ __readOnly__
+ @property originalValues {Object}
+ **/
+
+ /**
+ An {{#crossLink "Event"}}{{/crossLink}} that fires whenever a value of one of this entity's properties change.
+ @example
+ // assume order is an order entity attached to an EntityManager.
+ order.entityAspect.propertyChanged.subscribe(
+ function (changeArgs) {
+ // this code will be executed anytime a property value changes on the 'order' entity.
+ var propertyNameChanged = changeArgs.propertyName;
+ var oldValue = changeArgs.oldValue;
+ var newValue = changeArgs.newValue;
+ });
+ @event propertyChanged
+ @param propertyName {String} The property that changed. This value will be 'null' for operations that replace the entire entity. This includes
+ queries, imports and saves that require a merge. The remaining parameters will not exist in this case either.
+ @param oldValue {Object} The old value of this property before the change.
+ @param newValue {Object} The new value of this property after the change.
+ @readOnly
+ **/
+
+ /**
+ An {{#crossLink "Event"}}{{/crossLink}} that fires whenever any of the validation errors on this entity change.
+ Note that this might be the removal of an error when some data on the entity is fixed.
+ @example
+ // assume order is an order entity attached to an EntityManager.
+ order.entityAspect.validationErrorsChanged.subscribe(
+ function (changeArgs) {
+ // this code will be executed anytime a property value changes on the 'order' entity.
+ var errorsAdded = changeArgs.added;
+ var errorsCleared = changeArgs.removed;
+ });
+ @event validationErrorsChanged
+ @param added {Array of ValidationError} An array containing any newly added {{#crossLink "ValidationError"}}{{/crossLink}}s
+ @param removed {Array of ValidationError} An array containing any newly removed {{#crossLink "ValidationError"}}{{/crossLink}}s. This is those
+ errors that have been 'fixed'
+ @readOnly
+ **/
+
+ /**
+ Returns the {{#crossLink "EntityKey"}}{{/crossLink}} for this Entity.
+ @example
+ // assume order is an order entity attached to an EntityManager.
+ var entityKey = order.entityAspect.getKey();
+ @method getKey
+ @param [forceRefresh = false] {Boolean} Forces the recalculation of the key. This should normally be unnecessary.
+ @return {EntityKey} The {{#crossLink "EntityKey"}}{{/crossLink}} associated with this Entity.
+ **/
+ ctor.prototype.getKey = function (forceRefresh) {
+ forceRefresh = core.assertParam(forceRefresh, "forceRefresh").isBoolean().isOptional().check(false);
+ if (forceRefresh || !this._entityKey) {
+ var entityType = this.entity.entityType;
+ var keyProps = entityType.keyProperties;
+ var values = keyProps.map(function (p) {
+ return this.entity.getProperty(p.name);
+ }, this);
+ this._entityKey = new EntityKey(entityType, values);
+ }
+ return this._entityKey;
+ };
+
+ /**
+ Returns the entity to an {{#crossLink "EntityState"}}{{/crossLink}} of 'Unchanged' by committing all changes made since the entity was last queried
+ had 'acceptChanges' called on it.
+ @example
+ // assume order is an order entity attached to an EntityManager.
+ order.entityAspect.acceptChanges();
+ // The 'order' entity will now be in an 'Unchanged' state with any changes committed.
+ @method acceptChanges
+ **/
+ ctor.prototype.acceptChanges = function () {
+ this.setUnchanged();
+ if (this.entityManager.entityChangeNotificationEnabled) {
+ this.entityManager.entityChanged.publish({ entityAction: EntityAction.AcceptChanges, entity: this.entity });
+ }
+ };
+
+ /**
+ Returns the entity to an EntityState of 'Unchanged' by rejecting all changes made to it since the entity was last queried
+ had 'rejectChanges' called on it.
+ @example
+ // assume order is an order entity attached to an EntityManager.
+ order.entityAspect.rejectChanges();
+ // The 'order' entity will now be in an 'Unchanged' state with any changes rejected.
+ @method rejectChanges
+ **/
+ ctor.prototype.rejectChanges = function () {
+ var originalValues = this.originalValues;
+ var entity = this.entity;
+ for (var propName in originalValues) {
+ entity.setProperty(propName, originalValues[propName]);
+ }
+ this.setUnchanged();
+ if (this.entityManager.entityChangeNotificationEnabled) {
+ this.entityManager.entityChanged.publish({ entityAction: EntityAction.RejectChanges, entity: entity });
+ }
+ };
+
+ /**
+ Sets the entity to an EntityState of 'Unchanged'. This is also the equivalent of calling {{#crossLink "EntityAspect/acceptChanges"}}{{/crossLink}}
+ @example
+ // assume order is an order entity attached to an EntityManager.
+ order.entityAspect.setUnchanged();
+ // The 'order' entity will now be in an 'Unchanged' state with any changes committed.
+ @method setUnchanged
+ **/
+ ctor.prototype.setUnchanged = function () {
+ this.originalValues = {};
+ delete this.hasTempKey;
+ this.entityState = EntityState.Unchanged;
+ if (this.entityManager.entityChangeNotificationEnabled) {
+ this.entityManager.entityChanged.publish({ entityAction: EntityAction.EntityStateChange, entity: this.entity });
+ }
+ };
+
+ // Dangerous method - see notes - talk to Jay - this is not a complete impl
+ // ctor.prototype.setAdded = function () {
+ // this.originalValues = {};
+ // this.entityState = EntityState.Added;
+ // if (this.entity.entityType.autoGeneratedKeyType !== AutoGeneratedKeyType.None) {
+ // this.entityManager.generateTempKeyValue(this.entity);
+ // }
+ // };
+
+ /**
+ Sets the entity to an EntityState of 'Modified'. This can also be achieved by changing the value of any property on an 'Unchanged' entity.
+ @example
+ // assume order is an order entity attached to an EntityManager.
+ order.entityAspect.setModified();
+ // The 'order' entity will now be in a 'Modified' state.
+ @method setModified
+ **/
+ ctor.prototype.setModified = function () {
+ this.entityState = EntityState.Modified;
+ if (this.entityManager.entityChangeNotificationEnabled) {
+ this.entityManager.entityChanged.publish({ entityAction: EntityAction.EntityStateChange, entity: this.entity });
+ }
+ };
+
+ /**
+ Sets the entity to an EntityState of 'Deleted'. This both marks the entity as being scheduled for deletion during the next 'Save' call
+ but also removes the entity from all of its related entities.
+ @example
+ // assume order is an order entity attached to an EntityManager.
+ order.entityAspect.setDeleted();
+ // The 'order' entity will now be in a 'Deleted' state and it will no longer have any 'related' entities.
+ @method setDeleted
+ **/
+ ctor.prototype.setDeleted = function () {
+ this.entityState = EntityState.Deleted;
+ this._removeFromRelations();
+ if (this.entityManager.entityChangeNotificationEnabled) {
+ this.entityManager.entityChanged.publish({ entityAction: EntityAction.EntityStateChange, entity: this.entity });
+ }
+ // TODO: think about cascade deletes
+ };
+
+ /**
+ Performs validation on the entity, any errors encountered during the validation are available via the
+ {{#crossLink "EntityAspect.getValidationErrors"}}{{/crossLink}} method. Validating an entity means executing
+ all of the validators on both the entity itself as well as those on each of its properties.
+ @example
+ // assume order is an order entity attached to an EntityManager.
+ var isOk = order.entityAspect.validateEntity();
+ // isOk will be 'true' if there are no errors on the entity.
+ if (!isOk) {
+ var errors = order.entityAspect.getValidationErrors();
+ }
+ @method validateEntity
+ @return {Boolean} Whether the entity passed validation.
+ **/
+ ctor.prototype.validateEntity = function () {
+ var ok = true;
+ var entityType = this.entity.entityType;
+ this._processValidationOpAndPublish(function (that) {
+ // property level first
+ entityType.getProperties().forEach(function (p) {
+ var value = that.entity.getProperty(p.name);
+ if (p.validators.length > 0) {
+ ok = that._validateProperty(p, value) && ok;
+ }
+ });
+ // then entity level
+ entityType.validators.forEach(function (validator) {
+ ok = validate(that, validator, that.entity) && ok;
+ });
+ });
+
+ return ok;
+ };
+
+ /**
+ Performs validation on a specific property of this entity, any errors encountered during the validation are available via the
+ {{#crossLink "EntityAspect.getValidationErrors"}}{{/crossLink}} method. Validating a property means executing
+ all of the validators on the specified property. This call is also made automatically anytime a property
+ of an entity is changed.
+ @example
+ // assume order is an order entity attached to an EntityManager.
+ var isOk = order.entityAspect.validateProperty("Order");
+ or
+ @example
+ var orderDateProperty = order.entityType.getProperty("OrderDate");
+ var isOk = order.entityAspect.validateProperty(OrderDateProperty);
+ @method validateProperty
+ @param property {DataProperty|NavigationProperty} The {{#crossLink "DataProperty"}}{{/crossLink}} or
+ {{#crossLink "NavigationProperty"}}{{/crossLink}} to validate.
+ @param [context] {Object} A context object used to pass additional information to each {{#crossLink "Validator"}}{{/crossLink}}
+ @return {Boolean} Whether the entity passed validation.
+ **/
+ ctor.prototype.validateProperty = function (property, context) {
+ assertParam(property, "property").isString().or().isEntityProperty().check();
+ if (typeof (property) === 'string') {
+ property = this.entity.entityType.getProperty(property, true);
+ }
+
+ var value = this.entity.getProperty(property.name);
+ return this._validateProperty(property, value, context);
+ };
+
+ /**
+ Returns the validation errors associated with either the entire entity or any specified property.
+ @example
+ This method can return all of the errors for an Entity
+ @example
+ // assume order is an order entity attached to an EntityManager.
+ var valErrors = order.entityAspect.getValidationErrors();
+ as well as those for just a specific property.
+ @example
+ // assume order is an order entity attached to an EntityManager.
+ var orderDateErrors = order.entityAspect.getValidationErrors("OrderDate");
+ which can also be expressed as
+ @example
+ // assume order is an order entity attached to an EntityManager.
+ var orderDateProperty = order.entityType.getProperty("OrderDate");
+ var orderDateErrors = order.entityAspect.getValidationErrors(orderDateProperty);
+ @method getValidationErrors
+ @param [property] {DataProperty|NavigationProperty} The property for which validation errors should be retrieved.
+ If omitted, all of the validation errors for this entity will be returned.
+ @return {Array of ValidationError}
+ **/
+ ctor.prototype.getValidationErrors = function (property) {
+ assertParam(property, "property").isOptional().isEntityProperty().or().isString();
+ var result = core.getOwnPropertyValues(this._validationErrors);
+ if (property) {
+ var propertyName = typeof (property) === 'string' ? property : property.name;
+ result = result.filter(function (ve) {
+ return (ve.property.name === propertyName);
+ });
+ }
+ return result;
+ };
+
+ /**
+ Adds a validation error for a specified property.
+ @method addValidationError
+ @param validationError {ValidationError}
+ **/
+ ctor.prototype.addValidationError = function (validationError) {
+ assertParam(validationError, "validationError").isInstanceOf(ValidationError).check();
+ this._processValidationOpAndPublish(function (that) {
+ that._addValidationError(validationError);
+ });
+ };
+
+ /**
+ Removes a validation error for a specified property.
+ @method removeValidationError
+ @param validator {Validator}
+ @param [property] {DataProperty|NavigationProperty}
+ **/
+ ctor.prototype.removeValidationError = function (validator, property) {
+ assertParam(validator, "validator").isString().or().isInstanceOf(Validator).check();
+ assertParam(property, "property").isOptional().isEntityProperty();
+ this._processValidationOpAndPublish(function (that) {
+ that._removeValidationError(validator, property);
+ });
+ };
+
+ /**
+ Performs a query for the value of a specified {{#crossLink "NavigationProperty"}}{{/crossLink}}.
+ @example
+ emp.entityAspect.loadNavigationProperty("Orders")
+ .then(function (data) {
+ var orders = data.results;
+ }).fail(function (exception) {
+ // handle exception here;
+ });
+ @method loadNavigationProperty
+ @async
+ @param navigationProperty {NavigationProperty} The NavigationProperty to 'load'.
+ @param [callback] {Function} Function to call on success.
+ @param [errorCallback] {Function} Function to call on failure.
+ @return {Promise}
+ **/
+ // This method is provided in entityQuery.js.
+ // ctor.prototype.loadNavigationProperty = function(navigationProperty, callback, errorCallback)
+
+ // returns null for np's that do not have a parentKey
+ ctor.prototype.getParentKey = function (navigationProperty) {
+ // NavigationProperty doesn't yet exist
+ // core.assertParam(navigationProperty, "navigationProperty").isInstanceOf(NavigationProperty).check();
+ var fkNames = navigationProperty.foreignKeyNames;
+ if (!fkNames) return null;
+ var that = this;
+ var fkValues = fkNames.map(function (fkn) {
+ return that.entity.getProperty(fkn);
+ });
+ return new EntityKey(navigationProperty.entityType, fkValues);
+ };
+
+ // internal methods
+
+ ctor.prototype._removeFromRelations = function () {
+ var entity = this.entity;
+
+ // remove this entity from any collections.
+ // mark the entity deleted
+ entity.entityType.navigationProperties.forEach(function (np) {
+ var inverseNp = np.inverse;
+ if (!inverseNp) return;
+ var npValue = entity.getProperty(np.name);
+ if (np.isScalar) {
+ if (npValue) {
+ if (inverseNp.isScalar) {
+ npValue.setProperty(inverseNp.name, null);
+ } else {
+ var collection = npValue.getProperty(inverseNp.name);
+ if (collection.length) {
+ core.arrayRemoveItem(collection, entity);
+ }
+ }
+ }
+ } else {
+ npValue.forEach(function (v) {
+ if (inverseNp.isScalar) {
+ v.setProperty(inverseNp.name, null);
+ } else {
+ // TODO: many to many - not yet handled.
+ }
+ });
+ // now clear it.
+ npValue.length = 0;
+ }
+ });
+
+ };
+
+ // called from defaultInterceptor.
+ ctor.prototype._validateProperty = function (property, value, context) {
+ var ok = true;
+ this._processValidationOpAndPublish(function (that) {
+ if (context) {
+ context.property = property;
+ } else {
+ context = { property: property };
+ }
+ property.validators.forEach(function (validator) {
+ ok = ok && validate(that, validator, value, context);
+ });
+ });
+ return ok;
+ };
+
+ ctor.prototype._processValidationOpAndPublish = function (validationFn) {
+ if (this._pendingValidationResult) {
+ // only top level processValidations call publishes
+ validationFn(this);
+ } else {
+ try {
+ this._pendingValidationResult = { added: [], removed: [] };
+ validationFn(this);
+ if (this._pendingValidationResult.added.length > 0 || this._pendingValidationResult.removed.length > 0) {
+ this.validationErrorsChanged.publish(this._pendingValidationResult);
+ }
+ } finally {
+ this._pendingValidationResult = undefined;
+ }
+ }
+ };
+
+ ctor.prototype._addValidationError = function (validationError) {
+ this._validationErrors[validationError.key] = validationError;
+ this._pendingValidationResult.added.push(validationError);
+ };
+
+ ctor.prototype._removeValidationError = function (validator, property) {
+ var key = ValidationError.getKey(validator, property);
+ var valError = this._validationErrors[key];
+ if (valError) {
+ delete this._validationErrors[key];
+ this._pendingValidationResult.removed.push(valError);
+ }
+ };
+
+ function validate(aspect, validator, value, context) {
+ var ve = validator.validate(value, context);
+ if (ve) {
+ aspect._addValidationError(ve);
+ return false;
+ } else {
+ aspect._removeValidationError(validator, context ? context.property: null);
+ return true;
+ }
+ }
+
+ return ctor;
+
+ } ();
+
+ var EntityKey = (function () {
+
+ var ENTITY_KEY_DELIMITER = ":::";
+
+ /**
+ An EntityKey is an object that represents the unique identity of an entity. EntityKey's are immutable.
+
+ @class EntityKey
+ **/
+
+ /**
+ Constructs a new EntityKey. Each entity within an EntityManager will have a unique EntityKey.
+ @example
+ // assume em1 is an EntityManager containing a number of existing entities.
+ var empType = em1.metadataStore.getEntityType("Employee");
+ var entityKey = new EntityKey(empType, 1);
+ EntityKey's may also be found by calling EntityAspect.getKey()
+ @example
+ // assume employee1 is an existing Employee entity
+ var empKey = employee1.entityAspect.getKey();
+ Multipart keys are created by passing an array as the 'keyValues' parameter
+ @example
+ var empTerrType = em1.metadataStore.getEntityType("EmployeeTerritory");
+ var empTerrKey = new EntityKey(empTerrType, [ 1, 77]);
+ // The order of the properties in the 'keyValues' array must be the same as that
+ // returned by empTerrType.keyProperties
+ @method <ctor> EntityKey
+ @param entityType {EntityType} The {{#crossLink "EntityType"}}{{/crossLink}} of the entity.
+ @param keyValues {value|Array of values} A single value or an array of values.
+ **/
+ var ctor = function (entityType, keyValues) {
+ // can't ref EntityType here because of module circularity
+ // assertParam(entityType, "entityType").isInstanceOf(EntityType);
+ if (!Array.isArray(keyValues)) {
+ keyValues = Array.prototype.slice.call(arguments, 1);
+ }
+ if (!this instanceof ctor) {
+ return new ctor(entityType, keyValues);
+ }
+ this.entityType = entityType;
+ this.values = keyValues;
+ this._keyInGroup = createKeyString(keyValues);
+ };
+ ctor._$typeName = "EntityKey";
+
+ ctor.prototype.toJSON = function () {
+ return {
+ entityType: this.entityType.name,
+ values: this.values
+ };
+ };
+
+ ctor.fromJSON = function (json, metadataStore) {
+ var et = metadataStore.getEntityType(json.entityType, true);
+ return new EntityKey(et, json.values);
+ };
+
+ /**
+ Used to compare EntityKeys are determine if they refer to the same Entity.
+ There is also an static version of 'equals' with the same functionality.
+ @example
+ // assume em1 is an EntityManager containing a number of existing entities.
+ var empType = em1.metadataStore.getEntityType("Employee");
+ var empKey1 = new EntityKey(empType, 1);
+ // assume employee1 is an existing Employee entity
+ var empKey2 = employee1.entityAspect.getKey();
+ if (empKey1.equals(empKey2)) {
+ // do something ...
+ }
+ @method equals
+ @param entityKey {EntityKey}
+ **/
+ ctor.prototype.equals = function (entityKey) {
+ if (!entityKey instanceof EntityKey) return false;
+ return (this.entityType === entityKey.entityType) &&
+ core.arrayEquals(this.values, entityKey.values);
+ };
+
+ /*
+ Returns a human readable representation of this EntityKey.
+ @method toString
+ */
+ ctor.prototype.toString = function () {
+ return this.entityType.name + '-' + this._keyInGroup;
+ };
+
+ /**
+ Used to compare EntityKeys are determine if they refer to the same Entity.
+ There is also an instance version of 'equals' with the same functionality.
+ @example
+ // assume em1 is an EntityManager containing a number of existing entities.
+ var empType = em1.metadataStore.getEntityType("Employee");
+ var empKey1 = new EntityKey(empType, 1);
+ // assume employee1 is an existing Employee entity
+ var empKey2 = employee1.entityAspect.getKey();
+ if (EntityKey.equals(empKey1, empKey2)) {
+ // do something ...
+ }
+ @method equals
+ @static
+ @param k1 {EntityKey}
+ @param k2 {EntityKey}
+ **/
+ ctor.equals = function (k1, k2) {
+ if (!k1 instanceof EntityKey) return false;
+ return k1.equals(k2);
+ };
+
+ // TODO: we may want to compare to default values later.
+ ctor.prototype._isEmpty = function () {
+ return this.values.join("").length === 0;
+ };
+
+ ctor._fromRawEntity = function (rawEntity, entityType) {
+ var keyValues = entityType.keyProperties.map(function (p) {
+ return rawEntity[p.name];
+ });
+ return new EntityKey(entityType, keyValues);
+ };
+
+
+
+ function createKeyString(keyValues) {
+ return keyValues.join(ENTITY_KEY_DELIMITER);
+ }
+
+ return ctor;
+ })();
+
+ // expose
+
+ return {
+ EntityAspect: EntityAspect,
+ EntityState: EntityState,
+ EntityAction: EntityAction,
+ EntityKey: EntityKey
+ };
+
+
+});
2,143 Breeze.Client/Scripts/IBlade/entityManager.js
@@ -0,0 +1,2143 @@
+
+
+define(["core", "entityMetadata", "entityAspect", "entityQuery", "keyGenerator"],
+function (core, m_entityMetadata, m_entityAspect, m_entityQuery, KeyGenerator) {
+ "use strict";
+ /**
+ @module entityModel
+ **/
+ var Enum = core.Enum;
+ var Event = core.Event;
+ var assertConfig = core.assertConfig;
+ var assertParam = core.assertParam;
+
+ var MetadataStore = m_entityMetadata.MetadataStore;
+ var EntityType = m_entityMetadata.EntityType;
+ var AutoGeneratedKeyType = m_entityMetadata.AutoGeneratedKeyType;
+ var DataType = m_entityMetadata.DataType;
+
+ var EntityAspect = m_entityAspect.EntityAspect;
+ var EntityKey = m_entityAspect.EntityKey;
+ var EntityState = m_entityAspect.EntityState;
+ var EntityAction = m_entityAspect.EntityAction;
+
+ var EntityQuery = m_entityQuery.EntityQuery;
+
+ // TODO: think about dif between find and get.
+
+ var EntityManager = (function () {
+ /**
+ Instances of the EntityManager contain and manage collections of entities, either retrieved from a backend datastore or created on the client.
+ @class EntityManager
+ **/
+
+ /**
+ @example
+ At its most basic an EntityManager can be constructed with just a service name
+ @example
+ var entityManager = new EntityManager( "api/NorthwindIBModel");
+ This is the same as calling it with the following configuration object
+ @example
+ var entityManager = new EntityManager( {serviceName: "api/NorthwindIBModel" });
+ Usually however, configuration objects will contain more than just the 'serviceName';
+ @example
+ var metadataStore = new MetadataStore();
+ var entityManager = new EntityManager( {
+ serviceName: "api/NorthwindIBModel",
+ metadataStore: metadataStore
+ });
+ or
+ @example
+ return new QueryOptions({
+ mergeStrategy: obj,
+ fetchStrategy: this.fetchStrategy
+ });
+ var queryOptions = new QueryOptions({
+ mergeStrategy: MergeStrategy.OverwriteChanges,
+ fetchStrategy: FetchStrategy.FromServer
+ });
+ var validationOptions = new ValidationOptions({
+ validateOnAttach: true,
+ validateOnSave: true,
+ validateOnQuery: false
+ });
+ var entityManager = new EntityManager({
+ serviceName: "api/NorthwindIBModel",
+ queryOptions: queryOptions,
+ validationOptions: validationOptions
+ });
+ @method <ctor> EntityManager
+ @param [config] {Object|String} Configuration settings or a service name.
+ @param [config.serviceName] {String}
+ @param [config.metadataStore=MetadataStore.defaultInstance] {MetadataStore}
+ @param [config.queryOptions=QueryOptions.defaultInstance] {QueryOptions}
+ @param [config.validationOptions=ValidationOptions.defaultInstance] {ValidationOptions}
+ @param [config.saveOptions=SaveOptions.defaultInstance] {SaveOptions}
+ @param [config.keyGeneratorCtor] {Function}
+ @param [config.remoteAccessImplementation] {instance of RemoteAccessImplementation interface}
+ **/
+ var ctor = function (config) {
+ // // not allowed with useStrict
+ // if (!(this instanceof arguments.callee)) {
+ // throw new Error("Constructor called as a function");
+ // }
+
+ var assert = null;
+ if (arguments.length > 1) {
+ throw new Error("The EntityManager ctor has a single optional argument that is either a 'serviceName' or a configuration object.");
+ }
+ if (arguments.length === 0) {
+ this.serviceName = "";
+ config = {};
+ } else if (typeof config === 'string') {
+ this.serviceName = config;
+ config = {};
+ } else {
+ assert = assertConfig(config).whereParam("serviceName").isString();
+ }
+
+ if (!assert) {
+ assert = assertConfig(config);
+ }
+ assert
+ .whereParam("metadataStore").isInstanceOf(MetadataStore).isOptional().withDefault(MetadataStore.defaultInstance)
+ .whereParam("queryOptions").isInstanceOf(QueryOptions).isOptional().withDefault(QueryOptions.defaultInstance)
+ .whereParam("saveOptions").isInstanceOf(SaveOptions).isOptional().withDefault(SaveOptions.defaultInstance)
+ .whereParam("validationOptions").isInstanceOf(ValidationOptions).isOptional().withDefault(ValidationOptions.defaultInstance)
+ .whereParam("keyGeneratorCtor").isFunction().isOptional().withDefault(function() { return new KeyGenerator(); })
+ .whereParam("remoteAccessImplementation").withDefault(core.config.remoteAccessImplementation)
+ .applyAll(this);
+
+ if (this.serviceName.substr(-1) !== "/") {
+ this.serviceName = this.serviceName + '/';
+ }
+ this.entityChanged = new Event("entityChanged");
+ this.propertyChangeNotificationEnabled = true;
+ this.entityChangeNotificationEnabled = true;
+ this.clear();
+ };
+ ctor.prototype._$typeName = "EntityManager";
+
+ /**
+ The service name associated with this EntityManager.
+
+ __readOnly__
+ @property serviceName {String}
+ **/
+
+ /**
+ The {{#crossLink "MetadataStore"}}{{/crossLink}} associated with this EntityManager.
+
+ __readOnly__
+ @property metadataStore {MetadataStore}
+ **/
+
+ /**
+ The {{#crossLink "QueryOptions"}}{{/crossLink}} associated with this EntityManager.
+
+ __readOnly__
+ @property queryOptions {QueryOptions}
+ **/
+
+ /**
+ The {{#crossLink "SaveOptions"}}{{/crossLink}} associated with this EntityManager.
+
+ __readOnly__
+ @property saveOptions {SaveOptions}
+ **/
+
+ /**
+ The {{#crossLink "ValidationOptions"}}{{/crossLink}} associated with this EntityManager.
+
+ __readOnly__
+ @property validationOptions {ValidationOptions}
+ **/
+
+ /**
+ The {{#crossLink "KeyGenerator"}}{{/crossLink}} constructor associated with this EntityManager.
+
+ __readOnly__
+ @property keyGeneratorCtor {KeyGenerator constructor}
+ **/
+
+ /**
+ The RemoteAccess implementation instance associated with this EntityManager.
+
+ __readOnly__
+ @property remoteAccessImplementation {implementation instance of remoteAccessImplementation interface}
+ **/
+
+ /**
+ Whether the entityAspect.propertyChanged event will be fired on property change events. Default is true;
+
+ __readOnly__
+ @property propertyChangeNotificationEnabled {Boolean}
+ **/
+