diff --git a/CREDITS.txt b/CREDITS.txt index 6d7d3fa5..bc1f57e1 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1,13 +1,11 @@ -// JayData 1.0.4 +// JayData-RESO 1.0.0 // Dual licensed under MIT and GPL v2 -// Copyright JayStack Technologies (http://jaydata.org/licensing) +// Copyright JayStack Technologies (http://jaydata.org/licensing) and National Association of REALTORS // // JayData is a standards-based, cross-platform Javascript library and a set of // practices to access and manipulate data from various online and offline sources. // // Credits: -// Hajnalka Battancs, Dániel József, János Roden, László Horváth, Péter Nochta -// Péter Zentai, Róbert Bónay, Szabolcs Czinege, Viktor Borza, Viktor Lázár, -// Zoltán Gyebrovszki, Gábor Dolla +// Original Jaydata development Team, Mark Lesswing // -// More info: http://jaydata.org +// More info: http://jaydata.org and http://crt.blogs.realtor.org diff --git a/JayDataModules/angular.js b/JayDataModules/angular.js index f071be9b..2b1427e1 100644 --- a/JayDataModules/angular.js +++ b/JayDataModules/angular.js @@ -44,6 +44,40 @@ angular.module('jaydata', ['ng', ['$provide', function ($provide) { return Object.prototype.hasOwnProperty.apply(this, arguments); } + $data.Queryable.prototype.toLiveArrayEx = function (options, resultHolder) { + if (Array.isArray(options)) { + resultHolder = options; + otions = undefined; + } + resultHolder = resultHolder || []; + options = options || {}; + var self = this, scope = options.scope || $rootScope; + + function thunk(newDefer) { + self.toArray() + .then(function (items) { + resultHolder.length = 0; + items.forEach(function (item) { + resultHolder.push(item); + }) + newDefer.resolve(resultHolder); + }) + .fail(newDefer.reject) + .then(function () { + scope.$apply(); + }); + } + + function refresh() { + var defer = $.Deferred(thunk); + defer.promise(resultHolder); + return resultHolder; + } + resultHolder.refresh = refresh; + + return refresh(); + } + $data.Queryable.prototype.toLiveArray = function (cb) { var _this = this; diff --git a/JayDataModules/kendo.js b/JayDataModules/kendo.js index 4034657c..20037d1f 100644 --- a/JayDataModules/kendo.js +++ b/JayDataModules/kendo.js @@ -7,6 +7,10 @@ kendo.data.Model.fn.init.call(this, data); } }); + $data.kendo.attachMode = true; + if ($data.EntityAttachMode) { + $data.kendo.attachMode = $data.EntityAttachMode.KeepChanges; + } var kendoTypeMap = { "$data.Blob": "string", @@ -91,11 +95,9 @@ //} } - //console.dir(memberDefinitions.getPublicMappedMethods()); var modelDefinition = { fields: fields, init: function (data) { - //console.dir(arguments); var ctxType = options && options.owningContextType || undefined; var contextSetTypes = []; @@ -514,7 +516,6 @@ $data.Trace.log(promises); jQuery.when.apply(this, promises).then(function (items, total) { - console.dir(arguments); //var result = items.map(function (item) { return item instanceof $data.Entity ? new model(item.initData) : item; }); var result = items.map(function (item) { var d = (item instanceof $data.Entity) ? item.initData : item; @@ -555,7 +556,6 @@ }); } else { - console.dir(ctx.storeToken); model[0] .innerInstance() .save(ctx.storeToken) @@ -577,7 +577,7 @@ return item.innerInstance() }); items.forEach(function (item) { - ctx.attach(item, true); + ctx.attach(item, $data.kendo.attachMode); }); ctx.saveChanges().then(function () { options.success(); @@ -588,7 +588,7 @@ }); } else { - model[0].innerInstance().save().then(function (item) { + model[0].innerInstance().save(undefined, undefined, $data.kendo.attachMode).then(function (item) { options.success(); }).fail(function () { //alert("error in update") @@ -677,9 +677,9 @@ ds = ds || {}; //unless user explicitly opts out server side logic //we just force it. - ds.serverPaging = ds.serverPaging || true; - ds.serverFiltering = ds.serverFiltering || true; - ds.serverSorting = ds.serverSorting || true; + ds.serverPaging = ds.serverPaging === undefined ? true : ds.serverPaging; + ds.serverFiltering = ds.serverFiltering === undefined ? true : ds.serverFiltering; + ds.serverSorting = ds.serverSorting === undefined ? true : ds.serverSorting; ds.pageSize = ds.pageSize === undefined ? $data.kendo.defaultPageSize : ds.pageSize; var TransportClass = self.asKendoRemoteTransportClass(model); @@ -708,4 +708,4 @@ refresh: function () { } }); -})($data); \ No newline at end of file +})($data); diff --git a/JayService/JSObjectAdapter.js b/JayService/JSObjectAdapter.js index 8a39ab06..5d43ed7c 100644 --- a/JayService/JSObjectAdapter.js +++ b/JayService/JSObjectAdapter.js @@ -57,6 +57,10 @@ $data.Class.define("$data.JSObjectAdapter", null, null, { var methodArgs = self.resolveArguments(req, serviceInstance, memberInfo); if (memberInfo.method instanceof Array ? memberInfo.method.indexOf(req.method) >= 0 : memberInfo.method === req.method){ + + req.reso.memberName = memberName; + req.reso.namespace = "System"; + oDataBuilderCfg = { version: 'V2', baseUrl: req.fullRoute, @@ -78,6 +82,9 @@ $data.Class.define("$data.JSObjectAdapter", null, null, { if (member) { var esProc = new $data.JayService.OData.EntitySetProcessor(memberName, serviceInstance, { top: serviceInstance.storageProvider.providerConfiguration.responseLimit || $data.JayService.OData.Defaults.defaultResponseLimit }); + req.reso.memberName = memberName; + req.reso.namespace = member.elementType.namespace; + oDataBuilderCfg = { version: 'V2', baseUrl: req.fullRoute, @@ -344,6 +351,9 @@ $data.Class.define("$data.JSObjectAdapter", null, null, { defer.resolve(new $data.EmptyServiceResult(404)); } } else { + + request.reso.resultSize = 1; + defer.resolve(result); } diff --git a/JayService/JayService.js b/JayService/JayService.js index 1b2f075b..782c7212 100644 --- a/JayService/JayService.js +++ b/JayService/JayService.js @@ -74,7 +74,7 @@ $data.Class.define("$data.JayService", null, null, { if (req.connection.encrypted || req.headers['X-Forwarded-Protocol'] === 'https' || req.headers['x-forwarded-protocol'] === 'https') schema += 's'; - req.fullRoute = (req.baseRoute || (schema + '://' + req.headers.host)) + app.route; + req.fullRoute = (req.baseRoute || (schema + '://' + req.headers.host)) + (app.route || req.baseUrl || req.originalUrl.replace(req.url, '')); } } }, diff --git a/JayService/OData/BatchProcessor.js b/JayService/OData/BatchProcessor.js index 12b217ec..f9e4f40e 100644 --- a/JayService/OData/BatchProcessor.js +++ b/JayService/OData/BatchProcessor.js @@ -179,7 +179,11 @@ this._discoverNavigations(changeRequest, referenceData, itemType); var entity = new itemType(changeRequest.data, { converters: $data.oDataConverter.fromDb }); referenceData[refId].resultObject = entity; - setInfo.set.add(entity); + + request.reso.memberName = setInfo.set.name; + request.reso.namespace = itemType.namespace; + setInfo.set.add(entity, request); + break; case 'MERGE': case 'PATCH': @@ -188,6 +192,10 @@ var entity = new itemType(changeRequest.data, { converters: $data.oDataConverter.fromDb }); referenceData[refId].resultObject = entity; + request.reso.memberName = setInfo.set.name; + request.reso.namespace = itemType.namespace; + setInfo.set.attach(entity, false, request); + setInfo.set.attach(entity); entity.changedProperties = entity.getType().memberDefinitions.getPublicMappedProperties().filter(function(p){ if (entity[p.name] === undefined) return false; @@ -199,7 +207,11 @@ case 'DELETE': var entity = new itemType(setInfo.idObj); referenceData[refId].resultObject = entity; - setInfo.set.remove(entity); + + request.reso.memberName = setInfo.set.name; + request.reso.namespace = itemType.namespace; + setInfo.set.remove(entity, request); + break; default: break; diff --git a/JayService/OData/EntitySetProcessor.js b/JayService/OData/EntitySetProcessor.js index c462b7a5..8b8b78f9 100644 --- a/JayService/OData/EntitySetProcessor.js +++ b/JayService/OData/EntitySetProcessor.js @@ -53,13 +53,15 @@ var parsed = $data.JayService.OData.Utils.parseUrlPart(uri, this.context); var entity = new parsed.set.createNew(parsed.idObj); bodyData[i] = entity; - parsed.set.attach(entity); + parsed.set.attach(entity, false, req); } } } var entity = new this.entitySet.createNew(bodyData, { converters: $data.oDataConverter.fromDb }); - this.entitySet.add(entity); + + this.entitySet.add(entity, req); + this.context.saveChanges({ success: function () { res.statusCode = 201; @@ -98,13 +100,13 @@ var parsed = $data.JayService.OData.Utils.parseUrlPart(uri, this.context); var entity = new parsed.set.createNew(parsed.idObj); bodyData[i] = entity; - parsed.set.attach(entity); + parsed.set.attach(entity, false, req); } } } var entity = new this.entitySet.createNew(bodyData, { converters: $data.oDataConverter.fromDb }); - this.entitySet.attach(entity); + this.entitySet.attach(entity, false, req); entity.changedProperties = entity.getType().memberDefinitions.getPublicMappedProperties().filter(function(p){ if (entity[p.name] === undefined) return false; if (p.computed) return false; @@ -127,7 +129,9 @@ self.BatchDeleteFromEntitySet(req, config, cbWrapper); } else { if (this.member.idObject) { - this.entitySet.remove(this.member.idObject); + + this.entitySet.remove(this.member.idObject, req); + this.context.saveChanges({ success: function () { //return with no content @@ -191,6 +195,9 @@ config.includes = result.includes; this.context.executeQuery(new $data.Queryable(this.entitySet, result.expression), { success: function (contextResult) { + + req.reso.resultSize = contextResult.length; + if (self.member.valueRequeset) { // request pattern: /EntitySet(key)/Field/$value self.prepareSimpleResponse(contextResult, self.member.selectedField, self.entitySet, callback); diff --git a/JaySvcUtil/JaySvcUtil.js b/JaySvcUtil/JaySvcUtil.js index a441aeef..205fa99a 100644 --- a/JaySvcUtil/JaySvcUtil.js +++ b/JaySvcUtil/JaySvcUtil.js @@ -612,7 +612,8 @@ $data.Class.define('$data.MetadataLoaderClass', null, null, { "http://schemas.microsoft.com/ado/2008/09/edm": "V2", "http://schemas.microsoft.com/ado/2009/11/edm": "V3", "http://schemas.microsoft.com/ado/2007/05/edm": "V11", - "http://schemas.microsoft.com/ado/2009/08/edm": "V22" + "http://schemas.microsoft.com/ado/2009/08/edm": "V22", + "http://docs.oasis-open.org/odata/ns/edm": "V4" } }, _maxDataServiceVersions: { @@ -621,7 +622,8 @@ $data.Class.define('$data.MetadataLoaderClass', null, null, { "http://schemas.microsoft.com/ado/2008/09/edm": "2.0", "http://schemas.microsoft.com/ado/2009/11/edm": "3.0", "http://schemas.microsoft.com/ado/2007/05/edm": "2.0", - "http://schemas.microsoft.com/ado/2009/08/edm": "2.0" + "http://schemas.microsoft.com/ado/2009/08/edm": "2.0", + "http://docs.oasis-open.org/odata/ns/edm": "4.0" } }, _supportedODataVersionXSLT: { @@ -1109,6 +1111,7 @@ $data.Class.define('$data.MetadataLoaderClass', null, null, { " \r\n" + " true\r\n" + " \r\n" + + " '$$unbound'\r\n" + " '': { '$':\r\n" + " , \r\n" + " \r\n" + @@ -1121,6 +1124,10 @@ $data.Class.define('$data.MetadataLoaderClass', null, null, { " \r\n" + " \r\n" + "\r\n" + + " \r\n" + + " ''\r\n" + + " \r\n" + + "\r\n" + " \r\n" + " \r\n" + " \r\n" + diff --git a/MIT-LICENSE.txt b/MIT-LICENSE.txt index cb1501ff..04302300 100644 --- a/MIT-LICENSE.txt +++ b/MIT-LICENSE.txt @@ -1,4 +1,4 @@ -Copyright (c) 2012 JayStack Technologies LLC. http://jaydata.org/licensing +Copyright (c) 2014 National Association of REALTORS. http://crt.blogs.realtor.org Permission is hereby granted, free of charge, to any person obtaining diff --git a/Makefile b/Makefile index eed5bb31..5bfae19e 100644 --- a/Makefile +++ b/Makefile @@ -1,9 +1,7 @@ -VERSION = '1.3.6' +VERSION = '2.0.5' TARGET_DIR = ./build RELEASE_DIR = ./release TEMP_DIR = $(TARGET_DIR)/tmp -MODULE_DIR = $(TARGET_DIR)/jaydatamodules -PROVIDERS_DIR = $(TARGET_DIR)/jaydataproviders NPM_DIR = $(TARGET_DIR)/npm NPM_BASE_DIR = ./npm TYPESYSTEM_DIR = ./TypeSystem @@ -42,8 +40,6 @@ TYPE_SYSTEM = $(TYPESYSTEM_DIR)/initializeJayData.js\ $(TYPESYSTEM_DIR)/Types/Converter.js\ $(TYPESYSTEM_DIR)/Extensions.js\ -VSDOC_SOURCE = $(TYPESYSTEM_DIR)/VS2010Intellisense.js\ - JAYDATA_SOURCE = $(TYPES_DIR)/Expressions/ExpressionNode2.js\ $(TYPES_DIR)/Expressions/ArrayLiteralExpression.js\ $(TYPES_DIR)/Expressions/CallExpression.js\ @@ -117,7 +113,6 @@ JAYDATA_SOURCE = $(TYPES_DIR)/Expressions/ExpressionNode2.js\ $(TYPES_DIR)/ServiceOperation.js\ $(TYPES_DIR)/EntityWrapper.js\ $(TYPES_DIR)/Ajax/jQueryAjaxWrapper.js\ - $(TYPES_DIR)/Ajax/WinJSAjaxWrapper.js\ $(TYPES_DIR)/Ajax/ExtJSAjaxWrapper.js\ $(TYPES_DIR)/Ajax/AjaxStub.js\ $(TYPES_DIR)/StorageProviders/modelBinderConfigCompiler.js\ @@ -127,7 +122,6 @@ JAYDATA_SOURCE = $(TYPES_DIR)/Expressions/ExpressionNode2.js\ $(TYPES_DIR)/Authentication/BasicAuth.js\ $(JAYSVCUTIL_DIR)/JaySvcUtil.js\ $(BASEMODULE_DIR)/deferred.js\ - $(TYPES_DIR)/JayStorm.js\ JAYDATA_SERVER = $(BASEMODULE_DIR)/qDeferred.js\ $(JSERVICE_DIR)/Scripts/datajs-1.0.3-patched.js\ @@ -154,9 +148,6 @@ JAYDATA_SERVER = $(BASEMODULE_DIR)/qDeferred.js\ $(ODATAPARSER_DIR)/ODataEntityExpressionBuilder.js\ $(ODATAPARSER_DIR)/Visitors/ObjectLiteralBuilderVisitor.js\ -IndexedDbProvider = $(TYPES_DIR)/StorageProviders/IndexedDB/IndexedDBConverter.js\ - $(TYPES_DIR)/StorageProviders/IndexedDB/IndexedDBStorageProvider.js\ - DbCommand = $(TYPES_DIR)/DbClient/DbCommand.js\ $(TYPES_DIR)/DbClient/DbConnection.js\ $(TYPES_DIR)/DbClient/OpenDatabaseClient/OpenDbCommand.js\ @@ -177,33 +168,6 @@ SqLiteProvider = $(DbCommand)\ $(TYPES_DIR)/StorageProviders/SqLite/SqlFilterCompiler.js\ $(TYPES_DIR)/StorageProviders/SqLite/ModelBinder/sqLite_ModelBinderCompiler.js\ -oDataProvider = $(TYPES_DIR)/StorageProviders/oData/oDataConverter.js\ - $(TYPES_DIR)/StorageProviders/oData/oDataProvider.js\ - $(TYPES_DIR)/StorageProviders/oData/oDataCompiler.js\ - $(TYPES_DIR)/StorageProviders/oData/oDataWhereCompiler.js\ - $(TYPES_DIR)/StorageProviders/oData/oDataOrderCompiler.js\ - $(TYPES_DIR)/StorageProviders/oData/oDataPagingCompiler.js\ - $(TYPES_DIR)/StorageProviders/oData/oDataProjectionCompiler.js\ - -FacebookProvider = $(TYPES_DIR)/StorageProviders/Facebook/FacebookConverter.js\ - $(TYPES_DIR)/StorageProviders/Facebook/FacebookProvider.js\ - $(TYPES_DIR)/StorageProviders/Facebook/FacebookCompiler.js\ - $(TYPES_DIR)/StorageProviders/Facebook/EntitySets/FQL/user.js\ - $(TYPES_DIR)/StorageProviders/Facebook/EntitySets/FQL/friend.js\ - $(TYPES_DIR)/StorageProviders/Facebook/EntitySets/FQL/page.js\ - $(TYPES_DIR)/StorageProviders/Facebook/EntitySets/FQLContext.js\ - -YQLProvider = $(TYPES_DIR)/StorageProviders/YQL/YQLConverter.js\ - $(TYPES_DIR)/StorageProviders/YQL/YQLProvider.js\ - $(TYPES_DIR)/StorageProviders/YQL/YQLCompiler.js\ - $(TYPES_DIR)/StorageProviders/YQL/EntitySets/geo.js\ - $(TYPES_DIR)/StorageProviders/YQL/EntitySets/YQLContext.js\ - -InMemoryProvider = $(TYPES_DIR)/StorageProviders/InMemory/InMemoryConverter.js\ - $(TYPES_DIR)/StorageProviders/InMemory/InMemoryProvider.js\ - $(TYPES_DIR)/StorageProviders/InMemory/InMemoryCompiler.js\ - $(TYPES_DIR)/StorageProviders/InMemory/InMemoryFunctionCompiler.js\ - MongoDbProvider = $(TYPES_DIR)/StorageProviders/InMemory/InMemoryConverter.js\ $(TYPES_DIR)/StorageProviders/mongoDB/mongoDBConverter.js\ $(TYPES_DIR)/StorageProviders/mongoDB/mongoDBModelBinderConfigCompiler.js\ @@ -216,92 +180,23 @@ MongoDbProvider = $(TYPES_DIR)/StorageProviders/InMemory/InMemoryConverter.js\ $(TYPES_DIR)/StorageProviders/mongoDB/mongoDBStorageProvider.js\ $(TYPES_DIR)/StorageProviders/mongoDB/ClientObjectID.js\ -StormProvider = $(TYPES_DIR)/StorageProviders/Storm/StormStorageProvider.js\ - -WebApiProvider = $(TYPES_DIR)/StorageProviders/WebApi/WebApiConverter.js\ - $(TYPES_DIR)/StorageProviders/WebApi/WebApiProvider.js\ - -DeferredModule = $(BASEMODULE_DIR)/deferred.js\ - -ErrorHandlerModule = $(BASEMODULE_DIR)/errorhandler.js\ - -FormBinderModule = $(BASEMODULE_DIR)/formBinder.js\ - -HandlebarsModule = $(BASEMODULE_DIR)/handlebars.js\ - -InMemoryModule = $(BASEMODULE_DIR)/inMemory.js\ - -KendoModule = $(BASEMODULE_DIR)/kendo.js\ - -KnockoutModule = $(BASEMODULE_DIR)/knockout.js\ - -QDeferredModule = $(BASEMODULE_DIR)/qDeferred.js\ - -SenchaModule = $(BASEMODULE_DIR)/sencha.js\ - -TemplateModule = $(BASEMODULE_DIR)/template.js\ - -ValidateModule = $(BASEMODULE_DIR)/validate.js\ - -AngularModule = $(BASEMODULE_DIR)/angular.js\ - -MsCrmModule = $(BASEMODULE_DIR)/jaydata.mscrm.js\ - -MsCrmServerModule = $(BASEMODULE_DIR)/jaydata.mscrm.server.js\ - +libRETSProvider = $(TYPES_DIR)/StorageProviders/InMemory/InMemoryConverter.js\ + $(TYPES_DIR)/StorageProviders/libRETS/libRETSStorageProvider.js\ clean: @@test ! -d $(TARGET_DIR) || rm -r $(TARGET_DIR) @@test ! -d $(RELEASE_DIR) || rm -r $(RELEASE_DIR) -modules: jaydatamodules - @@rm -r $(TEMP_DIR) - release: all @@echo "Create release folder..." @@test -d $(RELEASE_DIR) || mkdir -p $(RELEASE_DIR) - @@test -d $(RELEASE_DIR)/jaydatamodules || mkdir -p $(RELEASE_DIR)/jaydatamodules - @@test -d $(RELEASE_DIR)/jaydataproviders || mkdir -p $(RELEASE_DIR)/jaydataproviders - @@cp -r $(MODULE_DIR)/* $(RELEASE_DIR)/jaydatamodules - @@cp -r $(PROVIDERS_DIR)/* $(RELEASE_DIR)/jaydataproviders @@cp $(TARGET_DIR)/jaydata.js $(RELEASE_DIR) @@cp $(TARGET_DIR)/jaydata.min.js $(RELEASE_DIR) - @@cp $(TARGET_DIR)/jaydata-vsdoc.js $(RELEASE_DIR) -all: jaydatavsdoc jaydatamin jaydata providers jaydatamodules npms +all: jaydatamin jaydata npms @@rm -r $(TEMP_DIR) -npms: npmjaydata npmjaydata-core npmjaydata-server npmindexeddb npmsqlite npmodata npminmemory npmmongodb npmstorm npmwebapi - -npmjaydata-core: $(TYPE_SYSTEM) $(JAYDATA_SOURCE) $(CREDITS) - @@echo "Building jaydata-core npm package..." - @@test -d $(NPM_DIR)/jaydata-core || mkdir -p $(NPM_DIR)/jaydata-core - @@cp -r $(NPM_BASE_DIR)/jaydata/* $(NPM_DIR)/jaydata-core - @@cp -r $(TYPESYSTEM_DIR) $(NPM_DIR)/jaydata-core/lib - @@cat $(NPM_DIR)/jaydata-core/lib/$(TYPE_SYSTEM_NPM) | \ - sed -e 's/$data.version = "JayData [0-9].[0-9].[0-9]"/$data.version = "JayData $(VERSION)"/;s/$data.versionNumber = "[0-9].[0-9].[0-9]"/$data.versionNumber = "$(VERSION)"/' > $(NPM_DIR)/jaydata-core/lib/$(TYPE_SYSTEM_NPM).bak - @@mv $(NPM_DIR)/jaydata-core/lib/$(TYPE_SYSTEM_NPM).bak $(NPM_DIR)/jaydata-core/lib/$(TYPE_SYSTEM_NPM) - @@rsync -R $(JAYDATA_SOURCE) $(NPM_DIR)/jaydata-core/lib - @@cp -r $(GPL_LIC) $(NPM_DIR)/jaydata-core - @@cp -r $(MIT_LIC) $(NPM_DIR)/jaydata-core - @@cp -r $(CREDITS) $(NPM_DIR)/jaydata-core - @$(foreach dir,$(TYPE_SYSTEM),echo "require('"$(dir)"');" >> $(NPM_DIR)/jaydata-core/lib/index.js;) - @$(foreach dir,$(JAYDATA_SOURCE),echo "require('"$(dir)"');" >> $(NPM_DIR)/jaydata-core/lib/index.js;) - @@echo 'module.exports = $$data;' >> $(NPM_DIR)/jaydata-core/lib/index.js - @@sed -e 's/"dependencies": {},/"dependencies": {"datajs": "1.0.3", "q": "0.8.5", "qs": "0.5.0", "xmldom": "0.1.11", "url": ">0.0.1", "acorn": "0.1.0"},/;s/"name": "jaydata"/"name": "jaydata-core"/;s/jaydata@[0-9].[0-9].[0-9]/jaydata@$(VERSION)/;s/"version": "[0-9].[0-9].[0-9]"/"version": "$(VERSION)"/' $(NPM_BASE_DIR)/jaydata/package.json > $(NPM_DIR)/jaydata-core/package.json - -npmjaydata-server: $(JAYDATA_SERVER) $(CREDITS) - @@echo "Building jaydata-server npm package..." - @@test -d $(NPM_DIR)/jaydata-server || mkdir -p $(NPM_DIR)/jaydata-server - @@cp -r $(NPM_BASE_DIR)/provider/* $(NPM_DIR)/jaydata-server - @@rsync -R $(JAYDATA_SERVER) $(NPM_DIR)/jaydata-server/lib - @@cp -r $(GPL_LIC) $(NPM_DIR)/jaydata-server - @@cp -r $(MIT_LIC) $(NPM_DIR)/jaydata-server - @@cp -r $(CREDITS) $(NPM_DIR)/jaydata-server - @@echo "try{ require('jaydata-core'); }catch(e){}" >> $(NPM_DIR)/jaydata-server/lib/index.js; - @$(foreach dir,$(JAYDATA_SERVER),echo "require('"$(dir)"');" >> $(NPM_DIR)/jaydata-server/lib/index.js;) - @@echo 'module.exports = $$data;' >> $(NPM_DIR)/jaydata-server/lib/index.js - @@sed -e 's/"name": "jaydata"/"name": "jaydata-server"/;s/"version": "[0-9].[0-9].[0-9]"/"version": "$(VERSION)"/;s/"jaydata-core": "[0-9].[0-9].[0-9]"/"jaydata-core":"$(VERSION)"/' $(NPM_BASE_DIR)/provider/package.json > $(NPM_DIR)/jaydata-server/package.json +npms: npmjaydata npmjaydata: $(TYPE_SYSTEM) $(JAYDATA_SOURCE) $(CREDITS) @@echo "Building jaydata npm package..." @@ -312,318 +207,25 @@ npmjaydata: $(TYPE_SYSTEM) $(JAYDATA_SOURCE) $(CREDITS) sed -e 's/$data.version = "JayData [0-9].[0-9].[0-9]"/$data.version = "JayData $(VERSION)"/;s/$data.versionNumber = "[0-9].[0-9].[0-9]"/$data.versionNumber = "$(VERSION)"/' > $(NPM_DIR)/jaydata/lib/$(TYPE_SYSTEM_NPM).bak @@mv $(NPM_DIR)/jaydata/lib/$(TYPE_SYSTEM_NPM).bak $(NPM_DIR)/jaydata/lib/$(TYPE_SYSTEM_NPM) @@rsync -R $(JAYDATA_SOURCE) $(NPM_DIR)/jaydata/lib - @@rsync -R $(IndexedDbProvider) $(NPM_DIR)/jaydata/lib @@rsync -R $(SqLiteProvider) $(NPM_DIR)/jaydata/lib - @@rsync -R $(oDataProvider) $(NPM_DIR)/jaydata/lib - @@rsync -R $(WebApiProvider) $(NPM_DIR)/jaydata/lib - @@rsync -R $(InMemoryProvider) $(NPM_DIR)/jaydata/lib @@rsync -R $(MongoDbProvider) $(NPM_DIR)/jaydata/lib - @@rsync -R $(StormProvider) $(NPM_DIR)/jaydata/lib - @@rsync -R $(FacebookProvider) $(NPM_DIR)/jaydata/lib - @@rsync -R $(YQLProvider) $(NPM_DIR)/jaydata/lib + @@rsync -R $(libRETSProvider) $(NPM_DIR)/jaydata/lib @@rsync -R $(JAYDATA_SERVER) $(NPM_DIR)/jaydata/lib @@cp -r $(GPL_LIC) $(NPM_DIR)/jaydata @@cp -r $(MIT_LIC) $(NPM_DIR)/jaydata @@cp -r $(CREDITS) $(NPM_DIR)/jaydata @$(foreach dir,$(TYPE_SYSTEM),echo "require('"$(dir)"');" >> $(NPM_DIR)/jaydata/lib/index.js;) @$(foreach dir,$(JAYDATA_SOURCE),echo "require('"$(dir)"');" >> $(NPM_DIR)/jaydata/lib/index.js;) - @@rm -f $(NPM_DIR)/jaydata/lib/indexeddb_index.js;) - @$(foreach dir,$(IndexedDbProvider),echo "require('"$(dir)"');" >> $(NPM_DIR)/jaydata/lib/indexeddb_index.js;) - @@rm -f $(NPM_DIR)/jaydata/lib/sqlite_index.js;) @$(foreach dir,$(SqLiteProvider),echo "require('"$(dir)"');" >> $(NPM_DIR)/jaydata/lib/sqlite_index.js;) - @@rm -f $(NPM_DIR)/jaydata/lib/odata_index.js;) - @$(foreach dir,$(oDataProvider),echo "require('"$(dir)"');" >> $(NPM_DIR)/jaydata/lib/odata_index.js;) - @@rm -f $(NPM_DIR)/jaydata/lib/webapi_index.js;) - @$(foreach dir,$(WebApiProvider),echo "require('"$(dir)"');" >> $(NPM_DIR)/jaydata/lib/webapi_index.js;) - @@rm -f $(NPM_DIR)/jaydata/lib/inmemory_index.js;) - @$(foreach dir,$(InMemoryProvider),echo "require('"$(dir)"');" >> $(NPM_DIR)/jaydata/lib/inmemory_index.js;) - @@rm -f $(NPM_DIR)/jaydata/lib/mongodb_index.js;) @$(foreach dir,$(MongoDbProvider),echo "require('"$(dir)"');" >> $(NPM_DIR)/jaydata/lib/mongodb_index.js;) - @@rm -f $(NPM_DIR)/jaydata/lib/storm_index.js;) - @$(foreach dir,$(StormProvider),echo "require('"$(dir)"');" >> $(NPM_DIR)/jaydata/lib/storm_index.js;) - @@rm -f $(NPM_DIR)/jaydata/lib/facebook_index.js;) - @$(foreach dir,$(FacebookProvider),echo "require('"$(dir)"');" >> $(NPM_DIR)/jaydata/lib/facebook_index.js;) - @@rm -f $(NPM_DIR)/jaydata/lib/yql_index.js;) - @$(foreach dir,$(YQLProvider),echo "require('"$(dir)"');" >> $(NPM_DIR)/jaydata/lib/yql_index.js;) - @@rm -f $(NPM_DIR)/jaydata/lib/service_index.js;) + @$(foreach dir,$(libRETSProvider),echo "require('"$(dir)"');" >> $(NPM_DIR)/jaydata/lib/librets_index.js;) @$(foreach dir,$(JAYDATA_SERVER),echo "require('"$(dir)"');" >> $(NPM_DIR)/jaydata/lib/service_index.js;) - @@echo "require('./indexeddb_index.js');" >> $(NPM_DIR)/jaydata/lib/index.js; @@echo "require('./sqlite_index.js');" >> $(NPM_DIR)/jaydata/lib/index.js; - @@echo "require('./odata_index.js');" >> $(NPM_DIR)/jaydata/lib/index.js; - @@echo "require('./webapi_index.js');" >> $(NPM_DIR)/jaydata/lib/index.js; - @@echo "require('./inmemory_index.js');" >> $(NPM_DIR)/jaydata/lib/index.js; @@echo "require('./mongodb_index.js');" >> $(NPM_DIR)/jaydata/lib/index.js; - @@echo "require('./storm_index.js');" >> $(NPM_DIR)/jaydata/lib/index.js; - @@echo "require('./facebook_index.js');" >> $(NPM_DIR)/jaydata/lib/index.js; - @@echo "require('./yql_index.js');" >> $(NPM_DIR)/jaydata/lib/index.js; + @@echo "require('./librets_index.js');" >> $(NPM_DIR)/jaydata/lib/index.js; @@echo "require('./service_index.js');" >> $(NPM_DIR)/jaydata/lib/index.js; @@echo 'module.exports = $$data;' >> $(NPM_DIR)/jaydata/lib/index.js - @@sed -e 's/"dependencies": {},/"dependencies": {"datajs": "1.0.3", "q": "0.8.5", "qs": "0.5.0", "xmldom": "0.1.11", "url": ">0.0.1", "acorn": "0.1.0"},/;s/jaydata@[0-9].[0-9].[0-9]/jaydata@$(VERSION)/;s/"version": "[0-9].[0-9].[0-9]"/"version": "$(VERSION)"/' $(NPM_BASE_DIR)/jaydata/package.json > $(NPM_DIR)/jaydata/package.json - -npmindexeddb: $(IndexedDbProvider) $(CREDITS) - @@echo "Building IndexedDb provider npm package..." - @@test -d $(NPM_DIR)/indexeddb || mkdir -p $(NPM_DIR)/indexeddb - @@cp -r $(NPM_BASE_DIR)/provider/* $(NPM_DIR)/indexeddb - @@rsync -R $(IndexedDbProvider) $(NPM_DIR)/indexeddb/lib - @@cp -r $(GPL_LIC) $(NPM_DIR)/indexeddb - @@cp -r $(MIT_LIC) $(NPM_DIR)/indexeddb - @@cp -r $(CREDITS) $(NPM_DIR)/indexeddb - @@echo "try{ require('jaydata-core'); }catch(e){}" >> $(NPM_DIR)/indexeddb/lib/index.js; - @$(foreach dir,$(IndexedDbProvider),echo "require('"$(dir)"');" >> $(NPM_DIR)/indexeddb/lib/index.js;) - @@echo 'module.exports = $$data;' >> $(NPM_DIR)/indexeddb/lib/index.js - @@sed -e 's/"name": "jaydata"/"name": "jaydata-indexeddb"/;s/"version": "[0-9].[0-9].[0-9]"/"version": "$(VERSION)"/;s/"jaydata-core": "[0-9].[0-9].[0-9]"/"jaydata-core":"$(VERSION)"/' $(NPM_BASE_DIR)/provider/package.json > $(NPM_DIR)/indexeddb/package.json - -npmsqlite: $(SqLiteProvider) $(CREDITS) - @@echo "Building SqLiteProvider provider npm package..." - @@test -d $(NPM_DIR)/sqlite || mkdir -p $(NPM_DIR)/sqlite - @@cp -r $(NPM_BASE_DIR)/provider/* $(NPM_DIR)/sqlite - @@rsync -R $(SqLiteProvider) $(NPM_DIR)/sqlite/lib - @@cp -r $(GPL_LIC) $(NPM_DIR)/sqlite - @@cp -r $(MIT_LIC) $(NPM_DIR)/sqlite - @@cp -r $(CREDITS) $(NPM_DIR)/sqlite - @@echo "try{ require('jaydata-core'); }catch(e){}" >> $(NPM_DIR)/sqlite/lib/index.js; - @$(foreach dir,$(SqLiteProvider),echo "require('"$(dir)"');" >> $(NPM_DIR)/sqlite/lib/index.js;) - @@echo 'module.exports = $$data;' >> $(NPM_DIR)/sqlite/lib/index.js - @@sed -e 's/"name": "jaydata"/"name": "jaydata-sqlite"/;s/"version": "[0-9].[0-9].[0-9]"/"version": "$(VERSION)"/;s/"jaydata-core": "[0-9].[0-9].[0-9]"/"jaydata-core":"$(VERSION)"/' $(NPM_BASE_DIR)/provider/package.json > $(NPM_DIR)/sqlite/package.json - -npmodata: $(oDataProvider) $(CREDITS) - @@echo "Building oDataProvider provider npm package..." - @@test -d $(NPM_DIR)/odata || mkdir -p $(NPM_DIR)/odata - @@cp -r $(NPM_BASE_DIR)/provider/* $(NPM_DIR)/odata - @@rsync -R $(oDataProvider) $(NPM_DIR)/odata/lib - @@cp -r $(GPL_LIC) $(NPM_DIR)/odata - @@cp -r $(MIT_LIC) $(NPM_DIR)/odata - @@cp -r $(CREDITS) $(NPM_DIR)/odata - @@echo "require('datajs');" >> $(NPM_DIR)/odata/lib/index.js; - @@echo "try{ require('jaydata-core'); }catch(e){}" >> $(NPM_DIR)/odata/lib/index.js; - @$(foreach dir,$(oDataProvider),echo "require('"$(dir)"');" >> $(NPM_DIR)/odata/lib/index.js;) - @@echo 'module.exports = $$data;' >> $(NPM_DIR)/odata/lib/index.js - @@sed -e 's/"name": "jaydata"/"name": "jaydata-odata"/;s/"version": "[0-9].[0-9].[0-9]"/"version": "$(VERSION)"/;s/"jaydata-core": "[0-9].[0-9].[0-9]"/"jaydata-core":"$(VERSION)","datajs": "1.0.3"/' $(NPM_BASE_DIR)/provider/package.json > $(NPM_DIR)/odata/package.json - -npmwebapi: $(WebApiProvider) $(CREDITS) - @@echo "Building WebApiProvider provider npm package..." - @@test -d $(NPM_DIR)/webapi || mkdir -p $(NPM_DIR)/webapi - @@cp -r $(NPM_BASE_DIR)/provider/* $(NPM_DIR)/webapi - @@rsync -R $(WebApiProvider) $(NPM_DIR)/webapi/lib - @@cp -r $(GPL_LIC) $(NPM_DIR)/webapi - @@cp -r $(MIT_LIC) $(NPM_DIR)/webapi - @@cp -r $(CREDITS) $(NPM_DIR)/webapi - @@echo "require('datajs');" >> $(NPM_DIR)/webapi/lib/index.js; - @@echo "try{ require('jaydata-core'); }catch(e){}" >> $(NPM_DIR)/webapi/lib/index.js; - @$(foreach dir,$(WebApiProvider),echo "require('"$(dir)"');" >> $(NPM_DIR)/webapi/lib/index.js;) - @@echo 'module.exports = $$data;' >> $(NPM_DIR)/webapi/lib/index.js - @@sed -e 's/"name": "jaydata"/"name": "jaydata-webapi"/;s/"version": "[0-9].[0-9].[0-9]"/"version": "$(VERSION)"/;s/"jaydata-core": "[0-9].[0-9].[0-9]"/"jaydata-core":"$(VERSION)","datajs": "1.0.3"/' $(NPM_BASE_DIR)/provider/package.json > $(NPM_DIR)/webapi/package.json - - -npminmemory: $(InMemoryProvider) $(CREDITS) - @@echo "Building InMemoryProvider provider npm package..." - @@test -d $(NPM_DIR)/inmemory || mkdir -p $(NPM_DIR)/inmemory - @@cp -r $(NPM_BASE_DIR)/provider/* $(NPM_DIR)/inmemory - @@rsync -R $(InMemoryProvider) $(NPM_DIR)/inmemory/lib - @@cp -r $(GPL_LIC) $(NPM_DIR)/inmemory - @@cp -r $(MIT_LIC) $(NPM_DIR)/inmemory - @@cp -r $(CREDITS) $(NPM_DIR)/inmemory - @@echo "try{ require('jaydata-core'); }catch(e){}" >> $(NPM_DIR)/inmemory/lib/index.js; - @$(foreach dir,$(InMemoryProvider),echo "require('"$(dir)"');" >> $(NPM_DIR)/inmemory/lib/index.js;) - @@echo 'module.exports = $$data;' >> $(NPM_DIR)/inmemory/lib/index.js - @@sed -e 's/"name": "jaydata"/"name": "jaydata-inmemory"/;s/"version": "[0-9].[0-9].[0-9]"/"version": "$(VERSION)"/;s/"jaydata-core": "[0-9].[0-9].[0-9]"/"jaydata-core":"$(VERSION)"/' $(NPM_BASE_DIR)/provider/package.json > $(NPM_DIR)/inmemory/package.json - -npmmongodb: $(MongoDbProvider) $(CREDITS) - @@echo "Building MongoDbProvider provider npm package..." - @@test -d $(NPM_DIR)/mongodb || mkdir -p $(NPM_DIR)/mongodb - @@cp -r $(NPM_BASE_DIR)/provider/* $(NPM_DIR)/mongodb - @@rsync -R $(MongoDbProvider) $(NPM_DIR)/mongodb/lib - @@cp -r $(GPL_LIC) $(NPM_DIR)/mongodb - @@cp -r $(MIT_LIC) $(NPM_DIR)/mongodb - @@cp -r $(CREDITS) $(NPM_DIR)/mongodb - @@echo "try{ require('jaydata-core'); }catch(e){}" >> $(NPM_DIR)/mongodb/lib/index.js; - @$(foreach dir,$(MongoDbProvider),echo "require('"$(dir)"');" >> $(NPM_DIR)/mongodb/lib/index.js;) - @@echo 'module.exports = $$data;' >> $(NPM_DIR)/mongodb/lib/index.js - @@sed -e 's/"name": "jaydata"/"name": "jaydata-mongodb"/;s/"version": "[0-9].[0-9].[0-9]"/"version": "$(VERSION)"/;s/"jaydata-core": "[0-9].[0-9].[0-9]"/"jaydata-core":"$(VERSION)"/' $(NPM_BASE_DIR)/provider/package.json > $(NPM_DIR)/mongodb/package.json - -npmstorm: $(StormProvider) $(CREDITS) - @@echo "Building StormProvider provider npm package..." - @@test -d $(NPM_DIR)/storm || mkdir -p $(NPM_DIR)/storm - @@cp -r $(NPM_BASE_DIR)/provider/* $(NPM_DIR)/storm - @@rsync -R $(StormProvider) $(NPM_DIR)/storm/lib - @@cp -r $(GPL_LIC) $(NPM_DIR)/storm - @@cp -r $(MIT_LIC) $(NPM_DIR)/storm - @@cp -r $(CREDITS) $(NPM_DIR)/storm - @@echo "try{ require('jaydata-core'); }catch(e){}" >> $(NPM_DIR)/storm/lib/index.js; - @$(foreach dir,$(StormProvider),echo "require('"$(dir)"');" >> $(NPM_DIR)/storm/lib/index.js;) - @@echo 'module.exports = $$data;' >> $(NPM_DIR)/storm/lib/index.js - @@sed -e 's/"name": "jaydata"/"name": "jaydata-storm"/;s/"version": "[0-9].[0-9].[0-9]"/"version": "$(VERSION)"/;s/"jaydata-core": "[0-9].[0-9].[0-9]"/"jaydata-core":"$(VERSION)","jaydata-inmemory": "$(VERSION)"/' $(NPM_BASE_DIR)/provider/package.json > $(NPM_DIR)/storm/package.json - -providers: indexeddbprovider sqliteprovider odataprovider facebookprovider yqlprovider inmemoryprovider mongodbprovider stormprovider webapiprovider - -indexeddbprovider: $(IndexedDbProvider) $(CREDITS) - @@echo "Building IndexedDbProvider provider..." - @@test -d $(PROVIDERS_DIR) || mkdir -p $(PROVIDERS_DIR) - @@cat $(CREDITS) $(IndexedDbProvider) > $(PROVIDERS_DIR)/IndexedDbProvider.js - @@java -jar $(COMPILER) --js $(PROVIDERS_DIR)/IndexedDbProvider.js --js_output_file $(TEMP_DIR)/IndexedDbProvider.min.js - @@cat $(CREDITS) $(TEMP_DIR)/IndexedDbProvider.min.js > $(PROVIDERS_DIR)/IndexedDbProvider.min.js - -sqliteprovider: $(SqLiteProvider) $(CREDITS) - @@echo "Building SqLiteProvider provider..." - @@test -d $(PROVIDERS_DIR) || mkdir -p $(PROVIDERS_DIR) - @@cat $(CREDITS) $(SqLiteProvider) > $(PROVIDERS_DIR)/SqLiteProvider.js - @@java -jar $(COMPILER) --js $(PROVIDERS_DIR)/SqLiteProvider.js --js_output_file $(TEMP_DIR)/SqLiteProvider.min.js - @@cat $(CREDITS) $(TEMP_DIR)/SqLiteProvider.min.js > $(PROVIDERS_DIR)/SqLiteProvider.min.js - -odataprovider: $(oDataProvider) $(CREDITS) - @@echo "Building oDataProvider provider..." - @@test -d $(PROVIDERS_DIR) || mkdir -p $(PROVIDERS_DIR) - @@cat $(CREDITS) $(oDataProvider) > $(PROVIDERS_DIR)/oDataProvider.js - @@java -jar $(COMPILER) --js $(PROVIDERS_DIR)/oDataProvider.js --js_output_file $(TEMP_DIR)/oDataProvider.min.js - @@cat $(CREDITS) $(TEMP_DIR)/oDataProvider.min.js > $(PROVIDERS_DIR)/oDataProvider.min.js - -webapiprovider: $(WebApiProvider) $(CREDITS) - @@echo "Building WebApiProvider provider..." - @@test -d $(PROVIDERS_DIR) || mkdir -p $(PROVIDERS_DIR) - @@cat $(CREDITS) $(WebApiProvider) > $(PROVIDERS_DIR)/WebApiProvider.js - @@java -jar $(COMPILER) --js $(PROVIDERS_DIR)/WebApiProvider.js --js_output_file $(TEMP_DIR)/WebApiProvider.min.js - @@cat $(CREDITS) $(TEMP_DIR)/WebApiProvider.min.js > $(PROVIDERS_DIR)/WebApiProvider.min.js - -facebookprovider: $(FacebookProvider) $(CREDITS) - @@echo "Building FacebookProvider provider..." - @@test -d $(PROVIDERS_DIR) || mkdir -p $(PROVIDERS_DIR) - @@cat $(CREDITS) $(FacebookProvider) > $(PROVIDERS_DIR)/FacebookProvider.js - @@java -jar $(COMPILER) --js $(PROVIDERS_DIR)/FacebookProvider.js --js_output_file $(TEMP_DIR)/FacebookProvider.min.js - @@cat $(CREDITS) $(TEMP_DIR)/FacebookProvider.min.js > $(PROVIDERS_DIR)/FacebookProvider.min.js - -yqlprovider: $(YQLProvider) $(CREDITS) - @@echo "Building YQLProvider provider..." - @@test -d $(PROVIDERS_DIR) || mkdir -p $(PROVIDERS_DIR) - @@cat $(CREDITS) $(YQLProvider) > $(PROVIDERS_DIR)/YQLProvider.js - @@java -jar $(COMPILER) --js $(PROVIDERS_DIR)/YQLProvider.js --js_output_file $(TEMP_DIR)/YQLProvider.min.js - @@cat $(CREDITS) $(TEMP_DIR)/YQLProvider.min.js > $(PROVIDERS_DIR)/YQLProvider.min.js - -inmemoryprovider: $(InMemoryProvider) $(CREDITS) - @@echo "Building InMemoryProvider provider..." - @@test -d $(PROVIDERS_DIR) || mkdir -p $(PROVIDERS_DIR) - @@cat $(CREDITS) $(InMemoryProvider) > $(PROVIDERS_DIR)/InMemoryProvider.js - @@java -jar $(COMPILER) --js $(PROVIDERS_DIR)/InMemoryProvider.js --js_output_file $(TEMP_DIR)/InMemoryProvider.min.js - @@cat $(CREDITS) $(TEMP_DIR)/InMemoryProvider.min.js > $(PROVIDERS_DIR)/InMemoryProvider.min.js - -mongodbprovider: $(MongoDbProvider) $(CREDITS) - @@echo "Building MongoDbProvider provider..." - @@test -d $(PROVIDERS_DIR) || mkdir -p $(PROVIDERS_DIR) - @@cat $(CREDITS) $(MongoDbProvider) > $(PROVIDERS_DIR)/MongoDbProvider.js - @@java -jar $(COMPILER) --js $(PROVIDERS_DIR)/MongoDbProvider.js --js_output_file $(TEMP_DIR)/MongoDbProvider.min.js - @@cat $(CREDITS) $(TEMP_DIR)/MongoDbProvider.min.js > $(PROVIDERS_DIR)/MongoDbProvider.min.js - -stormprovider: $(StormProvider) $(CREDITS) - @@echo "Building StormProvider provider..." - @@test -d $(PROVIDERS_DIR) || mkdir -p $(PROVIDERS_DIR) - @@cat $(StormProvider) > $(PROVIDERS_DIR)/StormProvider.js - @@java -jar $(COMPILER) --js $(PROVIDERS_DIR)/StormProvider.js --js_output_file $(TEMP_DIR)/StormProvider.min.js - @@cat $(CREDITS) $(TEMP_DIR)/StormProvider.min.js > $(PROVIDERS_DIR)/StormProvider.min.js - -jaydatamodules: deferredmodule errorhandlermodule formbindermodule handlebarsmodule inmemorymodule kendomodule knockoutmodule qdeferredmodule senchamodule templatemodule validatemodule mscrmmodule mscrmservermodule angularmodule - -deferredmodule: $(DeferredModule) $(CREDITS) - @@echo "Building Deferred module..." - @@test -d $(MODULE_DIR) || mkdir -p $(MODULE_DIR) - @@cat $(CREDITS) $(DeferredModule) > $(MODULE_DIR)/deferred.js - @@java -jar $(COMPILER) --js $(MODULE_DIR)/deferred.js --js_output_file $(TEMP_DIR)/deferred.min.js - @@cat $(CREDITS) $(TEMP_DIR)/deferred.min.js > $(MODULE_DIR)/deferred.min.js - -errorhandlermodule: $(ErrorHandlerModule) $(CREDITS) - @@echo "Building ErrorHandler module..." - @@test -d $(MODULE_DIR) || mkdir -p $(MODULE_DIR) - @@cat $(CREDITS) $(ErrorHandlerModule) > $(MODULE_DIR)/errorhandler.js - @@java -jar $(COMPILER) --js $(MODULE_DIR)/errorhandler.js --js_output_file $(TEMP_DIR)/errorhandler.min.js - @@cat $(CREDITS) $(TEMP_DIR)/errorhandler.min.js > $(MODULE_DIR)/errorhandler.min.js - -formbindermodule: $(FormBinderModule) $(CREDITS) - @@echo "Building FormBinder module..." - @@test -d $(MODULE_DIR) || mkdir -p $(MODULE_DIR) - @@cat $(CREDITS) $(FormBinderModule) > $(MODULE_DIR)/formBinder.js - @@java -jar $(COMPILER) --js $(MODULE_DIR)/formBinder.js --js_output_file $(TEMP_DIR)/formBinder.min.js - @@cat $(CREDITS) $(TEMP_DIR)/formBinder.min.js > $(MODULE_DIR)/formBinder.min.js - -handlebarsmodule: $(HandlebarsModule) $(CREDITS) - @@echo "Building Handlebars module..." - @@test -d $(MODULE_DIR) || mkdir -p $(MODULE_DIR) - @@cat $(CREDITS) $(HandlebarsModule) > $(MODULE_DIR)/handlebars.js - @@java -jar $(COMPILER) --js $(MODULE_DIR)/handlebars.js --js_output_file $(TEMP_DIR)/handlebars.min.js - @@cat $(CREDITS) $(TEMP_DIR)/handlebars.min.js > $(MODULE_DIR)/handlebars.min.js - -inmemorymodule: $(InMemoryModule) $(CREDITS) - @@echo "Building InMemory module..." - @@test -d $(MODULE_DIR) || mkdir -p $(MODULE_DIR) - @@cat $(CREDITS) $(InMemoryModule) > $(MODULE_DIR)/inMemory.js - @@java -jar $(COMPILER) --js $(MODULE_DIR)/inMemory.js --js_output_file $(TEMP_DIR)/inMemory.min.js - @@cat $(CREDITS) $(TEMP_DIR)/inMemory.min.js > $(MODULE_DIR)/inMemory.min.js - -kendomodule: $(KendoModule) $(CREDITS) - @@echo "Building Kendo module..." - @@test -d $(MODULE_DIR) || mkdir -p $(MODULE_DIR) - @@cat $(CREDITS) $(KendoModule) > $(MODULE_DIR)/kendo.js - @@java -jar $(COMPILER) --js $(MODULE_DIR)/kendo.js --js_output_file $(TEMP_DIR)/kendo.min.js - @@cat $(CREDITS) $(TEMP_DIR)/kendo.min.js > $(MODULE_DIR)/kendo.min.js - -knockoutmodule: $(KnockoutModule) $(CREDITS) - @@echo "Building Knockout module..." - @@test -d $(MODULE_DIR) || mkdir -p $(MODULE_DIR) - @@cat $(CREDITS) $(KnockoutModule) > $(MODULE_DIR)/knockout.js - @@java -jar $(COMPILER) --js $(MODULE_DIR)/knockout.js --js_output_file $(TEMP_DIR)/knockout.min.js - @@cat $(CREDITS) $(TEMP_DIR)/knockout.min.js > $(MODULE_DIR)/knockout.min.js - -qdeferredmodule: $(QDeferredModule) $(CREDITS) - @@echo "Building QDeferred module..." - @@test -d $(MODULE_DIR) || mkdir -p $(MODULE_DIR) - @@cat $(CREDITS) $(QDeferredModule) > $(MODULE_DIR)/qDeferred.js - @@java -jar $(COMPILER) --js $(MODULE_DIR)/qDeferred.js --js_output_file $(TEMP_DIR)/qDeferred.min.js - @@cat $(CREDITS) $(TEMP_DIR)/qDeferred.min.js > $(MODULE_DIR)/qDeferred.min.js - -senchamodule: $(SenchaModule) $(CREDITS) - @@echo "Building Sencha module..." - @@test -d $(MODULE_DIR) || mkdir -p $(MODULE_DIR) - @@cat $(CREDITS) $(SenchaModule) > $(MODULE_DIR)/sencha.js - @@java -jar $(COMPILER) --js $(MODULE_DIR)/sencha.js --js_output_file $(TEMP_DIR)/sencha.min.js - @@cat $(CREDITS) $(TEMP_DIR)/sencha.min.js > $(MODULE_DIR)/sencha.min.js - -templatemodule: $(TemplateModule) $(CREDITS) - @@echo "Building Template module..." - @@test -d $(MODULE_DIR) || mkdir -p $(MODULE_DIR) - @@cat $(CREDITS) $(TemplateModule) > $(MODULE_DIR)/template.js - @@java -jar $(COMPILER) --js $(MODULE_DIR)/template.js --js_output_file $(TEMP_DIR)/template.min.js - @@cat $(CREDITS) $(TEMP_DIR)/template.min.js > $(MODULE_DIR)/template.min.js - -validatemodule: $(ValidateModule) $(CREDITS) - @@echo "Building Validate module..." - @@test -d $(MODULE_DIR) || mkdir -p $(MODULE_DIR) - @@cat $(CREDITS) $(ValidateModule) > $(MODULE_DIR)/validate.js - @@java -jar $(COMPILER) --js $(MODULE_DIR)/validate.js --js_output_file $(TEMP_DIR)/validate.min.js - @@cat $(CREDITS) $(TEMP_DIR)/validate.min.js > $(MODULE_DIR)/validate.min.js - -angularmodule: $(AngularModule) $(CREDITS) - @@echo "Building Angular module..." - @@test -d $(MODULE_DIR) || mkdir -p $(MODULE_DIR) - @@cat $(CREDITS) $(AngularModule) > $(MODULE_DIR)/angular.js - @@java -jar $(COMPILER) --js $(MODULE_DIR)/angular.js --js_output_file $(TEMP_DIR)/angular.min.js - @@cat $(CREDITS) $(TEMP_DIR)/angular.min.js > $(MODULE_DIR)/angular.min.js - -mscrmmodule: $(MsCrmModule) $(CREDITS) - @@echo "Building MsCrm module..." - @@test -d $(MODULE_DIR) || mkdir -p $(MODULE_DIR) - @@cat $(CREDITS) $(MsCrmModule) > $(MODULE_DIR)/jaydata.mscrm.js - @@java -jar $(COMPILER) --js $(MODULE_DIR)/jaydata.mscrm.js --js_output_file $(TEMP_DIR)/jaydata.mscrm.min.js - @@cat $(CREDITS) $(TEMP_DIR)/jaydata.mscrm.min.js > $(MODULE_DIR)/jaydata.mscrm.min.js - -mscrmservermodule: $(MsCrmServerModule) $(CREDITS) - @@echo "Building MsCrm Server module..." - @@test -d $(MODULE_DIR) || mkdir -p $(MODULE_DIR) - @@cat $(CREDITS) $(MsCrmServerModule) > $(MODULE_DIR)/jaydata.mscrm.server.js - @@java -jar $(COMPILER) --js $(MODULE_DIR)/jaydata.mscrm.server.js --js_output_file $(TEMP_DIR)/jaydata.mscrm.server.min.js - @@cat $(CREDITS) $(TEMP_DIR)/jaydata.mscrm.server.min.js > $(MODULE_DIR)/jaydata.mscrm.server.min.js - -jaydatavsdoc: jaydata $(CREDITS) - @@echo "Building JayData vsdoc version..." - @@test -d $(TEMP_DIR) || mkdir -p $(TEMP_DIR) - @@cat $(VSDOC_SOURCE) $(TEMP_DIR)/jaydata.js > $(TEMP_DIR)/jaydata-vsdoc.js - @@cat $(CREDITS) $(TEMP_DIR)/jaydata-vsdoc.js > $(TARGET_DIR)/jaydata-vsdoc.js + @@sed -e 's/"dependencies": {},/"dependencies": {"datajs": "1.0.3", "q": "0.8.5", "qs": "2.2.4", "xmldom": "0.1.11", "url": ">0.0.1", "acorn": "0.1.0","jaydata-librets": "*"},/;s/jaydata@[0-9].[0-9].[0-9]/jaydata@$(VERSION)/;s/"version": "[0-9].[0-9].[0-9]"/"version": "$(VERSION)"/' $(NPM_BASE_DIR)/jaydata/package.json > $(NPM_DIR)/jaydata/package.json jaydatamin: jaydata $(CREDITS) @@echo "Minifying JayData library..." @@ -648,7 +250,5 @@ $(CREDITS): $(CREDITS_BASE) @@test -d $(TEMP_DIR) || mkdir -p $(TEMP_DIR) @@sed -e 's/JayData [0-9].[0-9].[0-9]/JayData $(VERSION)/' $(CREDITS_BASE) > $(CREDITS) --include ./Pro/Makefile - .PHONY: JayDataMin JayData JayDataStandaloneMin JayDataStandalone All diff --git a/Scripts/datajs-1.0.3-patched.js b/Scripts/datajs-1.0.3-patched.js new file mode 100644 index 00000000..a78dacdd --- /dev/null +++ b/Scripts/datajs-1.0.3-patched.js @@ -0,0 +1,7518 @@ +// Copyright (c) Microsoft. All rights reserved. +// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, +// modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +// WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +// datajs.js + +(function (window, undefined) { + if (!window.datajs) { + window.datajs = {}; + } + + if (!window.OData) { + window.OData = {}; + } + + var datajs = window.datajs; + var odata = window.OData; + + + // Provides an enumeration of possible kinds of payloads. + var PAYLOADTYPE_BATCH = "b"; + var PAYLOADTYPE_COMPLEXTYPE = "c"; + var PAYLOADTYPE_ENTRY = "entry"; // This is used when building the payload. + var PAYLOADTYPE_FEEDORLINKS = "f"; + var PAYLOADTYPE_PRIMITIVETYPE = "p"; + var PAYLOADTYPE_SVCDOC = "s"; + var PAYLOADTYPE_UNKNOWN = "u"; + var PAYLOADTYPE_NONE = "n"; + + // Provides an enumeration of possible kinds of properties. + var PROPERTYKIND_COMPLEX = "c"; + var PROPERTYKIND_DEFERRED = "d"; + var PROPERTYKIND_INLINE = "i"; + var PROPERTYKIND_PRIMITIVE = "p"; + var PROPERTYKIND_NONE = "n"; + + var assigned = function (value) { + /// Checks whether the specified value is different from null and undefined. + /// Value to check. + /// true if the value is assigned; false otherwise. + return value !== null && value !== undefined; + }; + + var contains = function (arr, item) { + /// Checks whether the specified item is in the array. + /// Array to check in. + /// Item to look for. + /// true if the item is contained, false otherwise. + + var i, len; + for (i = 0, len = arr.length; i < len; i++) { + if (arr[i] === item) { + return true; + } + } + + return false; + }; + + var defined = function (a, b) { + /// Given two values, picks the first one that is not undefined. + /// First value. + /// Second value. + /// a if it's a defined value; else b. + return (a !== undefined) ? a : b; + }; + + var delay = function (callback) { + /// Delays the invocation of the specified function until execution unwinds. + /// Callback function. + if (arguments.length === 1) { + window.setTimeout(callback, 0); + return; + } + + var args = Array.prototype.slice.call(arguments, 1); + window.setTimeout(function () { + callback.apply(this, args); + }, 0); + }; + + + var forEachSchema = function (metadata, callback) { + /// Invokes a function once per schema in metadata. + /// Metadata store; one of edmx, schema, or an array of any of them. + /// Callback function to invoke once per schema. + /// + /// The first truthy value to be returned from the callback; null or the last falsy value otherwise. + /// + + if (!metadata) { + return null; + } + + if (isArray(metadata)) { + var i, len, result; + for (i = 0, len = metadata.length; i < len; i++) { + result = forEachSchema(metadata[i], callback); + if (result) { + return result; + } + } + + return null; + } else { + if (metadata.dataServices) { + return forEachSchema(metadata.dataServices.schema, callback); + } + + return callback(metadata); + } + }; + + var isDateTimeOffset = function (value) { + /// Checks whether a Date object is DateTimeOffset value + /// Value to check. + /// true if the value is a DateTimeOffset, false otherwise. + return (value.__edmType === "Edm.DateTimeOffset" || (!value.__edmType && value.__offset)); + }; + + var formatMilliseconds = function (ms, ns) { + /// Formats a millisecond and a nanosecond value into a single string. + /// Number of milliseconds to format. + /// Number of nanoseconds to format. + /// Formatted text. + /// If the value is already as string it's returned as-is. + + // Avoid generating milliseconds if not necessary. + if (ms === 0) { + ms = ""; + } else { + ms = "." + formatNumberWidth(ms.toString(), 3); + } + if (ns > 0) { + if (ms === "") { + ms = ".000"; + } + ms += formatNumberWidth(ns.toString(), 4); + } + return ms; + }; + + var formatDateTimeOffset = function (value) { + /// Formats a DateTime or DateTimeOffset value a string. + /// Value to format. + /// Formatted text. + /// If the value is already as string it's returned as-is. + + if (typeof value === "string") { + return value; + } + + var hasOffset = isDateTimeOffset(value); + var offset = getCanonicalTimezone(value.__offset); + if (hasOffset && offset !== "Z") { + // We're about to change the value, so make a copy. + value = new Date(value.valueOf()); + + var timezone = parseTimezone(offset); + var hours = value.getUTCHours() + (timezone.d * timezone.h); + var minutes = value.getMinutes() + (timezone.d * timezone.m); + + value.setUTCHours(hours, minutes); + } else if (!hasOffset) { + // Don't suffix a 'Z' for Edm.DateTime values. + offset = ""; + } + + var year = value.getUTCFullYear(); + var month = value.getUTCMonth() + 1; + var sign = ""; + if (year <= 0) { + year = -(year - 1); + sign = "-"; + } + + var ms = formatMilliseconds(value.getUTCMilliseconds(), value.__ns); + + return sign + + formatNumberWidth(year, 4) + "-" + + formatNumberWidth(month, 2) + "-" + + formatNumberWidth(value.getUTCDate(), 2) + "T" + + formatNumberWidth(value.getUTCHours(), 2) + ":" + + formatNumberWidth(value.getUTCMinutes(), 2) + ":" + + formatNumberWidth(value.getUTCSeconds(), 2) + + ms + offset; + }; + + var formatDuration = function (value) { + /// Converts a duration to a string in xsd:duration format. + /// Object with ms and __edmType properties. + /// String representation of the time object in xsd:duration format. + + var ms = value.ms; + + var sign = ""; + if (ms < 0) { + sign = "-"; + ms = -ms; + } + + var days = Math.floor(ms / 86400000); + ms -= 86400000 * days; + var hours = Math.floor(ms / 3600000); + ms -= 3600000 * hours; + var minutes = Math.floor(ms / 60000); + ms -= 60000 * minutes; + var seconds = Math.floor(ms / 1000); + ms -= seconds * 1000; + + return sign + "P" + + formatNumberWidth(days, 2) + "DT" + + formatNumberWidth(hours, 2) + "H" + + formatNumberWidth(minutes, 2) + "M" + + formatNumberWidth(seconds, 2) + + formatMilliseconds(ms, value.ns) + "S"; + }; + + var formatNumberWidth = function (value, width, append) { + /// Formats the specified value to the given width. + /// Number to format (non-negative). + /// Minimum width for number. + /// Flag indicating if the value is padded at the beginning (false) or at the end (true). + /// Text representation. + var result = value.toString(10); + while (result.length < width) { + if (append) { + result += "0"; + } else { + result = "0" + result; + } + } + + return result; + }; + + var getCanonicalTimezone = function (timezone) { + /// Gets the canonical timezone representation. + /// Timezone representation. + /// An 'Z' string if the timezone is absent or 0; the timezone otherwise. + + return (!timezone || timezone === "Z" || timezone === "+00:00" || timezone === "-00:00") ? "Z" : timezone; + }; + + var invokeRequest = function (request, success, error, handler, httpClient, context) { + /// Sends a request containing OData payload to a server. + /// Object that represents the request to be sent.. + /// Callback for a successful read operation. + /// Callback for handling errors. + /// Handler for data serialization. + /// HTTP client layer. + /// Context used for processing the request + return httpClient.request(request, function (response) { + try { + if (response.headers) { + normalizeHeaders(response.headers); + } + + if (response.data === undefined) { + handler.read(response, context); + } + } catch (err) { + if (err.request === undefined) { + err.request = request; + } + if (err.response === undefined) { + err.response = response; + } + error(err); + return; + } + + success(response.data, response); + }, error); + }; + + var isArray = function (value) { + /// Checks whether the specified value is an array object. + /// Value to check. + /// true if the value is an array object; false otherwise. + + return Object.prototype.toString.call(value) === "[object Array]"; + }; + + var isDate = function (value) { + /// Checks whether the specified value is a Date object. + /// Value to check. + /// true if the value is a Date object; false otherwise. + + return Object.prototype.toString.call(value) === "[object Date]"; + }; + + var lookupProperty = function (properties, name) { + /// Looks up a property by name. + /// Array of property objects as per EDM metadata. + /// Name to look for. + /// The property object; null if not found. + + if (properties) { + var i, len; + for (i = 0, len = properties.length; i < len; i++) { + if (properties[i].name === name) { + return properties[i]; + } + } + } + + return null; + }; + + var lookupTypeInMetadata = function (name, metadata, kind) { + /// Looks up a type object by name. + /// Name, possibly null or empty. + /// Metadata store; one of edmx, schema, or an array of any of them. + /// Kind of type to look for; one of 'entityType' or 'complexType'. + /// An type description if the name is found; null otherwise. + + return (name) ? forEachSchema(metadata, function (schema) { + return lookupTypeInSchema(name, schema, kind); + }) : null; + }; + + var lookupComplexType = function (name, metadata) { + /// Looks up a complex type object by name. + /// Name, possibly null or empty. + /// Metadata store; one of edmx, schema, or an array of any of them. + /// A complex type description if the name is found; null otherwise. + + return lookupTypeInMetadata(name, metadata, "complexType"); + }; + + var lookupEntityType = function (name, metadata) { + /// Looks up an entity type object by name. + /// Name, possibly null or empty. + /// Metadata store; one of edmx, schema, or an array of any of them. + /// An entity type description if the name is found; null otherwise. + + return lookupTypeInMetadata(name, metadata, "entityType"); + }; + + //// Commented out - metadata is largely optional and we don't rely on it to this extent. + //// var lookupEntityTypeForNavigation = function (navigationProperty, metadata) { + //// /// Looks up the target entity type for a navigation property. + //// /// + //// /// + //// /// The entity type metadata for the specified property, null if not found. + //// var rel = navigationProperty.relationship; + //// var association = forEachSchema(metadata, function (schema) { + //// // The name should be the namespace qualified name in 'ns'.'type' format. + //// var nameOnly = removeNamespace(schema["namespace"], rel); + //// var associations = schema.association; + //// if (nameOnly && associations) { + //// var i, len; + //// for (i = 0, len = associations.length; i < len; i++) { + //// if (associations[i].name === nameOnly) { + //// return associations[i]; + //// } + //// } + //// } + //// }); + //// var result = null; + //// if (association) { + //// var end = association.end[0]; + //// if (end.role !== navigationProperty.toRole) { + //// end = association.end[1]; + //// // For metadata to be valid, end.role === navigationProperty.toRole now. + //// } + //// result = lookupEntityType(end.type, metadata); + //// } + //// return result; + //// }; + + var removeNamespace = function (ns, fullName) { + /// Given an expected namespace prefix, removes it from a full name. + /// Expected namespace. + /// Full name in 'ns'.'name' form. + /// The local name, null if it isn't found in the expected namespace. + + if (fullName.indexOf(ns) === 0 && fullName.charAt(ns.length) === ".") { + return fullName.substr(ns.length + 1); + } + + return null; + }; + + var lookupTypeInSchema = function (name, metadata, kind) { + /// Looks up an entity type object by name. + /// Name (assigned). + /// Metadata store; one of edmx, schema. + /// Kind of type to look for; one of 'entityType' or 'complexType'. + /// An entity type description if the name is found; null otherwise. + /// + /// metadata is considered an edmx object if it contains a dataServices object. + /// + + if (metadata) { + // The name should be the namespace qualified name in 'ns'.'type' format. + var nameOnly = removeNamespace(metadata["namespace"], name); + var types = metadata[kind]; + if (nameOnly && types) { + var i, len; + for (i = 0, len = types.length; i < len; i++) { + if (types[i].name === nameOnly) { + return types[i]; + } + } + } + } + + return null; + }; + + var normalHeaders = { + "accept": "Accept", + "content-type": "Content-Type", + "dataserviceversion": "DataServiceVersion", + "maxdataserviceversion": "MaxDataServiceVersion" + }; + + var normalizeHeaders = function (headers) { + /// Normalizes headers so they can be found with consistent casing. + /// Dictionary of name/value pairs. + + for (var name in headers) { + var lowerName = name.toLowerCase(); + var normalName = normalHeaders[lowerName]; + if (normalName && name !== normalName) { + var val = headers[name]; + delete headers[name]; + headers[normalName] = val; + } + } + }; + + var undefinedDefault = function (value, defaultValue) { + /// Returns a default value in place of undefined. + /// Value to check. + /// Value to return if value is undefined. + /// value if it's defined; defaultValue otherwise. + /// + /// This should only be used for cases where falsy values are valid; + /// otherwise the pattern should be 'x = (value) ? value : defaultValue;'. + /// + return (value !== undefined) ? value : defaultValue; + }; + + var parseInt10 = function (value) { + /// Parses a value in base 10. + /// String value to parse. + /// The parsed value, NaN if not a valid value. + + return parseInt(value, 10); + }; + + + // The captured indices for this expression are: + // 0 - complete input + // 1 - direction + // 2,3,4 - years, months, days + // 5,6,7,8 - hours, minutes, seconds, miliseconds + + var parseTimeRE = /^([+-])?P(?:(\d+)Y)?(?:(\d+)M)?(?:(\d+)D)?(?:T(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)(?:\.(\d+))?S)?)?/; + + var parseDuration = function (duration) { + /// Parses a string in xsd:duration format. + /// Duration value. + /// + /// This method will throw an exception if the input string has a year or a month component. + /// + /// Object representing the time + + var parts = parseTimeRE.exec(duration); + + if (parts === null) { + throw { message: "Invalid duration value." }; + } + + var years = parts[2] || "0"; + var months = parts[3] || "0"; + var days = parseInt10(parts[4] || 0); + var hours = parseInt10(parts[5] || 0); + var minutes = parseInt10(parts[6] || 0); + var seconds = parseFloat(parts[7] || 0); + + if (years !== "0" || months !== "0") { + throw { message: "Unsupported duration value." }; + } + + var ms = parts[8]; + var ns = 0; + if (!ms) { + ms = 0; + } else { + if (ms.length > 7) { + throw { message: "Cannot parse duration value to given precision." }; + } + + ns = formatNumberWidth(ms.substring(3), 4, true); + ms = formatNumberWidth(ms.substring(0, 3), 3, true); + + ms = parseInt10(ms); + ns = parseInt10(ns); + } + + ms += seconds * 1000 + minutes * 60000 + hours * 3600000 + days * 86400000; + + if (parts[1] === "-") { + ms = -ms; + } + + var result = { ms: ms, __edmType: "Edm.Time" }; + + if (ns) { + result.ns = ns; + } + return result; + }; + + var parseTimezone = function (timezone) { + /// Parses a timezone description in (+|-)nn:nn format. + /// Timezone offset. + /// + /// An object with a (d)irection property of 1 for + and -1 for -, + /// offset (h)ours and offset (m)inutes. + /// + + var direction = timezone.substring(0, 1); + direction = (direction === "+") ? 1 : -1; + + var offsetHours = parseInt10(timezone.substring(1)); + var offsetMinutes = parseInt10(timezone.substring(timezone.indexOf(":") + 1)); + return { d: direction, h: offsetHours, m: offsetMinutes }; + }; + + var payloadTypeOf = function (data) { + /// Determines the kind of payload applicable for the specified value. + /// Value to check. + /// One of the values declared on the payloadType object. + + switch (typeof (data)) { + case "object": + if (!data) { + return PAYLOADTYPE_NONE; + } + if (isArray(data) || isArray(data.results)) { + return PAYLOADTYPE_FEEDORLINKS; + } + if (data.__metadata && data.__metadata.uri !== undefined) { + return PAYLOADTYPE_ENTRY; + } + if (isArray(data.EntitySets)) { + return PAYLOADTYPE_SVCDOC; + } + if (isArray(data.__batchRequests)) { + return PAYLOADTYPE_BATCH; + } + if (isDate(data)) { + return PAYLOADTYPE_PRIMITIVETYPE; + } + + return PAYLOADTYPE_COMPLEXTYPE; + + case "string": + case "number": + case "boolean": + return PAYLOADTYPE_PRIMITIVETYPE; + } + + return PAYLOADTYPE_UNKNOWN; + }; + + var prepareRequest = function (request, handler, context) { + /// Prepares a request object so that it can be sent through the network. + /// Object that represents the request to be sent. + /// Handler for data serialization + /// Context used for preparing the request + + // Default to GET if no method has been specified. + if (!request.method) { + request.method = "GET"; + } + + if (!request.headers) { + request.headers = {}; + } else { + normalizeHeaders(request.headers); + } + + if (request.headers.Accept === undefined) { + request.headers.Accept = handler.accept; + } + + if (assigned(request.data) && request.body === undefined) { + handler.write(request, context); + } + + if (!assigned(request.headers.MaxDataServiceVersion)) { + request.headers.MaxDataServiceVersion = handler.maxDataServiceVersion || "1.0"; + } + }; + + var propertyKindOf = function (value) { + /// Determines the kind of property for the specified value. + /// Value to check. + /// One of the values declared on the propertyKind object. + + switch (payloadTypeOf(value)) { + case PAYLOADTYPE_COMPLEXTYPE: + if (value.__deferred && value.__deferred.uri) { + return PROPERTYKIND_DEFERRED; + } + + return PROPERTYKIND_COMPLEX; + + case PAYLOADTYPE_FEEDORLINKS: + case PAYLOADTYPE_ENTRY: + return PROPERTYKIND_INLINE; + + case PAYLOADTYPE_PRIMITIVETYPE: + return PROPERTYKIND_PRIMITIVE; + } + return PROPERTYKIND_NONE; + }; + + var throwErrorCallback = function (error) { + /// Default error handler. + /// Error to handle. + throw error; + }; + + var trimString = function (str) { + /// Removes leading and trailing whitespaces from a string. + /// String to trim + /// The string with no leading or trailing whitespace. + + if (str.trim) { + return str.trim(); + } + + return str.replace(/^\s+|\s+$/g, ''); + }; + + // Regular expression that splits a uri into its components: + // 0 - is the matched string. + // 1 - is the scheme. + // 2 - is the authority. + // 3 - is the path. + // 4 - is the query. + // 5 - is the fragment. + var uriRegEx = /^([^:\/?#]+:)?(\/\/[^\/?#]*)?([^?#:]+)?(\?[^#]*)?(#.*)?/; + var uriPartNames = ["scheme", "authority", "path", "query", "fragment"]; + + var getURIInfo = function (uri) { + /// Gets information about the components of the specified URI. + /// URI to get information from. + /// + /// An object with an isAbsolute flag and part names (scheme, authority, etc.) if available. + /// + + var result = { isAbsolute: false }; + + if (uri) { + var matches = uriRegEx.exec(uri); + if (matches) { + var i, len; + for (i = 0, len = uriPartNames.length; i < len; i++) { + if (matches[i + 1]) { + result[uriPartNames[i]] = matches[i + 1]; + } + } + } + if (result.scheme) { + result.isAbsolute = true; + } + } + + return result; + }; + + var getURIFromInfo = function (uriInfo) { + /// Builds a URI string from its components. + /// An object with uri parts (scheme, authority, etc.). + /// URI string. + + return "".concat( + uriInfo.scheme || "", + uriInfo.authority || "", + uriInfo.path || "", + uriInfo.query || "", + uriInfo.fragment || ""); + }; + + // Regular expression that splits a uri authority into its subcomponents: + // 0 - is the matched string. + // 1 - is the userinfo subcomponent. + // 2 - is the host subcomponent. + // 3 - is the port component. + var uriAuthorityRegEx = /^\/{0,2}(?:([^@]*)@)?([^:]+)(?::{1}(\d+))?/; + + // Regular expression that matches percentage enconded octects (i.e %20 or %3A); + var pctEncodingRegEx = /%[0-9A-F]{2}/ig; + + var normalizeURICase = function (uri) { + /// Normalizes the casing of a URI. + /// URI to normalize, absolute or relative. + /// The URI normalized to lower case. + + var uriInfo = getURIInfo(uri); + var scheme = uriInfo.scheme; + var authority = uriInfo.authority; + + if (scheme) { + uriInfo.scheme = scheme.toLowerCase(); + if (authority) { + var matches = uriAuthorityRegEx.exec(authority); + if (matches) { + uriInfo.authority = "//" + + (matches[1] ? matches[1] + "@" : "") + + (matches[2].toLowerCase()) + + (matches[3] ? ":" + matches[3] : ""); + } + } + } + + uri = getURIFromInfo(uriInfo); + + return uri.replace(pctEncodingRegEx, function (str) { + return str.toLowerCase(); + }); + }; + + var normalizeURI = function (uri, base) { + /// Normalizes a possibly relative URI with a base URI. + /// URI to normalize, absolute or relative. + /// Base URI to compose with. + /// The composed URI if relative; the original one if absolute. + + if (!base) { + return uri; + } + + var uriInfo = getURIInfo(uri); + if (uriInfo.isAbsolute) { + return uri; + } + + var baseInfo = getURIInfo(base); + var normInfo = {}; + var path; + + if (uriInfo.authority) { + normInfo.authority = uriInfo.authority; + path = uriInfo.path; + normInfo.query = uriInfo.query; + } else { + if (!uriInfo.path) { + path = baseInfo.path; + normInfo.query = uriInfo.query || baseInfo.query; + } else { + if (uriInfo.path.charAt(0) === '/') { + path = uriInfo.path; + } else { + path = mergeUriPathWithBase(uriInfo.path, baseInfo.path); + } + normInfo.query = uriInfo.query; + } + normInfo.authority = baseInfo.authority; + } + + normInfo.path = removeDotsFromPath(path); + + normInfo.scheme = baseInfo.scheme; + normInfo.fragment = uriInfo.fragment; + + return getURIFromInfo(normInfo); + }; + + var mergeUriPathWithBase = function (uriPath, basePath) { + /// Merges the path of a relative URI and a base URI. + /// Base URI path. + /// A string with the merged path. + + var path = "/"; + var end; + + if (basePath) { + end = basePath.lastIndexOf("/"); + path = basePath.substring(0, end); + + if (path.charAt(path.length - 1) !== "/") { + path = path + "/"; + } + } + + return path + uriPath; + }; + + var removeDotsFromPath = function (path) { + /// Removes the special folders . and .. from a URI's path. + /// URI path component. + /// Path without any . and .. folders. + + var result = ""; + var segment = ""; + var end; + + while (path) { + if (path.indexOf("..") === 0 || path.indexOf(".") === 0) { + path = path.replace(/^\.\.?\/?/g, ""); + } else if (path.indexOf("/..") === 0) { + path = path.replace(/^\/\..\/?/g, "/"); + end = result.lastIndexOf("/"); + if (end === -1) { + result = ""; + } else { + result = result.substring(0, end); + } + } else if (path.indexOf("/.") === 0) { + path = path.replace(/^\/\.\/?/g, "/"); + } else { + segment = path; + end = path.indexOf("/", 1); + if (end !== -1) { + segment = path.substring(0, end); + } + result = result + segment; + path = path.replace(segment, ""); + } + } + return result; + }; + + + var ticks = 0; + + var canUseJSONP = function (request) { + /// + /// Checks whether the specified request can be satisfied with a JSONP request. + /// + /// Request object to check. + /// true if the request can be satisfied; false otherwise. + + // Requests that 'degrade' without changing their meaning by going through JSONP + // are considered usable. + // + // We allow data to come in a different format, as the servers SHOULD honor the Accept + // request but may in practice return content with a different MIME type. + if (request.method && request.method !== "GET") { + return false; + } + + return true; + }; + + var createIFrame = function (url) { + /// Creates an IFRAME tag for loading the JSONP script + /// The source URL of the script + /// The IFRAME tag + var iframe = window.document.createElement("IFRAME"); + iframe.style.display = "none"; + + var attributeEncodedUrl = url.replace(/&/g, "&").replace(/"/g, """).replace(/\<\/script><\/head><\/body><\/html>"; + + var body = window.document.getElementsByTagName("BODY")[0]; + body.appendChild(iframe); + + writeHtmlToIFrame(iframe, html); + return iframe; + }; + + var createXmlHttpRequest = function () { + /// Creates a XmlHttpRequest object. + /// XmlHttpRequest object. + if (window.XMLHttpRequest) { + return new window.XMLHttpRequest(); + } + var exception; + if (window.ActiveXObject !== undefined) { + try { + return new window.ActiveXObject("Msxml2.XMLHTTP.6.0"); + } catch (_) { + try { + return new window.ActiveXObject("Msxml2.XMLHTTP.3.0"); + } catch (e) { + exception = e; + } + } + } else { + exception = { message: "XMLHttpRequest not supported" }; + } + throw exception; + }; + + var isAbsoluteUrl = function (url) { + /// Checks whether the specified URL is an absolute URL. + /// URL to check. + /// true if the url is an absolute URL; false otherwise. + + return url.indexOf("http://") === 0 || + url.indexOf("https://") === 0 || + url.indexOf("file://") === 0; + }; + + var isLocalUrl = function (url) { + /// Checks whether the specified URL is local to the current context. + /// URL to check. + /// true if the url is a local URL; false otherwise. + + if (!isAbsoluteUrl(url)) { + return true; + } + + // URL-embedded username and password will not be recognized as same-origin URLs. + var location = window.location; + var locationDomain = location.protocol + "//" + location.host + "/"; + return (url.indexOf(locationDomain) === 0); + }; + + var removeCallback = function (name, tick) { + /// Removes a callback used for a JSONP request. + /// Function name to remove. + /// Tick count used on the callback. + try { + delete window[name]; + } catch (err) { + window[name] = undefined; + if (tick === ticks - 1) { + ticks -= 1; + } + } + }; + + var removeIFrame = function (iframe) { + /// Removes an iframe. + /// The iframe to remove. + /// Null value to be assigned to iframe reference. + if (iframe) { + writeHtmlToIFrame(iframe, ""); + iframe.parentNode.removeChild(iframe); + } + + return null; + }; + + var headerKeysForRead = ['Content-Length', 'Connection', 'Content-Type', 'Accept', 'DataServiceVersion', 'MaxDataServiceVersion', 'Etag', 'Location', 'Content-Id', 'If-Match', 'Authorization']; + odata.headerKeysForRead = headerKeysForRead; + + var readResponseHeaders = function (xhr, headers) { + /// Reads response headers into array. + /// HTTP request with response available. + /// Target array to fill with name/value pairs. + + var responseHeaders = xhr.getAllResponseHeaders().split(/\r?\n/); + //FF CORS fix + var is_firefox = typeof navigator === 'object' && navigator && typeof navigator.userAgent === 'string' ? navigator.userAgent.toLowerCase().indexOf('firefox') > -1 : false; + if (responseHeaders.length === 1 && responseHeaders[0] === '' && is_firefox && xhr.status > 0) { + for (var i = 0; i < headerKeysForRead.length; i++) { + var headerKey = headerKeysForRead[i]; + var headerValue = xhr.getResponseHeader(headerKey); + if (headerValue) { + responseHeaders.push(headerKey + ': ' + headerValue); + } + } + } + var i, len; + for (i = 0, len = responseHeaders.length; i < len; i++) { + if (responseHeaders[i]) { + var header = responseHeaders[i].split(": "); + headers[header[0]] = header[1]; + } + } + }; + + var writeHtmlToIFrame = function (iframe, html) { + /// Writes HTML to an IFRAME document. + /// The IFRAME element to write to. + /// The HTML to write. + var frameDocument = (iframe.contentWindow) ? iframe.contentWindow.document : iframe.contentDocument.document; + frameDocument.open(); + frameDocument.write(html); + frameDocument.close(); + }; + + odata.defaultHttpClient = { + callbackParameterName: "$callback", + + formatQueryString: "$format=json", + + enableJsonpCallback: false, + + request: function (request, success, error) { + /// Performs a network request. + /// Request description. + /// Success callback with the response object. + /// Error callback with an error object. + /// Object with an 'abort' method for the operation. + + var result = {}; + var xhr = null; + var done = false; + var iframe; + + result.abort = function () { + iframe = removeIFrame(iframe); + if (done) { + return; + } + + done = true; + if (xhr) { + xhr.abort(); + xhr = null; + } + + error({ message: "Request aborted" }); + }; + + var handleTimeout = function () { + iframe = removeIFrame(iframe); + if (!done) { + done = true; + xhr = null; + error({ message: "Request timed out" }); + } + }; + + var name; + var url = request.requestUri; + var enableJsonpCallback = defined(request.enableJsonpCallback, this.enableJsonpCallback); + var callbackParameterName = defined(request.callbackParameterName, this.callbackParameterName); + var formatQueryString = defined(request.formatQueryString, this.formatQueryString); + if (!enableJsonpCallback || isLocalUrl(url)) { + + xhr = createXmlHttpRequest(); + xhr.onreadystatechange = function () { + if (done || xhr === null || xhr.readyState !== 4) { + return; + } + + // Workaround for XHR behavior on IE. + var statusText = xhr.statusText; + var statusCode = xhr.status; + if (statusCode === 1223) { + statusCode = 204; + statusText = "No Content"; + } + + var headers = []; + readResponseHeaders(xhr, headers); + + var response = { requestUri: url, statusCode: statusCode, statusText: statusText, headers: headers, body: xhr.responseText }; + + done = true; + xhr = null; + if (statusCode >= 200 && statusCode <= 299) { + success(response); + } else { + error({ message: "HTTP request failed", request: request, response: response }); + } + }; + + xhr.open(request.method || "GET", url, true, request.user, request.password); + + // Set the name/value pairs. + if (request.headers) { + for (name in request.headers) { + xhr.setRequestHeader(name, request.headers[name]); + } + } + + // Set withCredential + if (request.withCredentials) + xhr.withCredentials = true; + + // Set the timeout if available. + if (request.timeoutMS) { + xhr.timeout = request.timeoutMS; + xhr.ontimeout = handleTimeout; + } + + xhr.send(request.body); + } else { + if (!canUseJSONP(request)) { + throw { message: "Request is not local and cannot be done through JSONP." }; + } + + var tick = ticks; + ticks += 1; + var tickText = tick.toString(); + var succeeded = false; + var timeoutId; + name = "handleJSONP_" + tickText; + window[name] = function (data) { + iframe = removeIFrame(iframe); + if (!done) { + succeeded = true; + window.clearTimeout(timeoutId); + removeCallback(name, tick); + + // Workaround for IE8 and below where trying to access data.constructor after the IFRAME has been removed + // throws an "unknown exception" + if (window.ActiveXObject && !window.DOMParser) { + data = window.JSON.parse(window.JSON.stringify(data)); + } + + // Call the success callback in the context of the parent window, instead of the IFRAME + delay(success, { body: data, statusCode: 200, headers: { "Content-Type": "application/json" } }); + } + }; + + // Default to two minutes before timing out, 1000 ms * 60 * 2 = 120000. + var timeoutMS = (request.timeoutMS) ? request.timeoutMS : 120000; + timeoutId = window.setTimeout(handleTimeout, timeoutMS); + + var queryStringParams = callbackParameterName + "=parent." + name; + if (this.formatQueryString) { + queryStringParams += "&" + formatQueryString; + } + + var qIndex = url.indexOf("?"); + if (qIndex === -1) { + url = url + "?" + queryStringParams; + } else if (qIndex === url.length - 1) { + url = url + queryStringParams; + } else { + url = url + "&" + queryStringParams; + } + + iframe = createIFrame(url); + } + + return result; + } + }; + + + + var MAX_DATA_SERVICE_VERSION = "2.0"; + + var contentType = function (str) { + /// Parses a string into an object with media type and properties. + /// String with media type to parse. + /// null if the string is empty; an object with 'mediaType' and a 'properties' dictionary otherwise. + + if (!str) { + return null; + } + + var contentTypeParts = str.split(";"); + var properties = {}; + + var i, len; + for (i = 1, len = contentTypeParts.length; i < len; i++) { + var contentTypeParams = contentTypeParts[i].split("="); + properties[trimString(contentTypeParams[0])] = contentTypeParams[1]; + } + + return { mediaType: trimString(contentTypeParts[0]), properties: properties }; + }; + + var contentTypeToString = function (contentType) { + /// Serializes an object with media type and properties dictionary into a string. + /// Object with media type and properties dictionary to serialize. + /// String representation of the media type object; undefined if contentType is null or undefined. + + if (!contentType) { + return undefined; + } + + var result = contentType.mediaType; + var property; + for (property in contentType.properties) { + result += ";" + property + "=" + contentType.properties[property]; + } + return result; + }; + + var createReadWriteContext = function (contentType, dataServiceVersion, context, handler) { + /// Creates an object that is going to be used as the context for the handler's parser and serializer. + /// Object with media type and properties dictionary. + /// String indicating the version of the protocol to use. + /// Operation context. + /// Handler object that is processing a resquest or response. + /// Context object. + + return { + contentType: contentType, + dataServiceVersion: dataServiceVersion, + metadata: context ? context.metadata : null, + context: context, + handler: handler + }; + }; + + var fixRequestHeader = function (request, name, value) { + /// Sets a request header's value. If the header has already a value other than undefined, null or empty string, then this method does nothing. + /// Request object on which the header will be set. + /// Header name. + /// Header value. + if (!request) { + return; + } + + var headers = request.headers; + if (!headers[name]) { + headers[name] = value; + } + }; + + var fixDataServiceVersion = function (context, version) { + /// Sets the dataServiceVersion component of the context. + /// Context object used for serialization. + /// Version value. + /// + /// If the component has already a value other than undefined, null or + /// empty string, then this method does nothing. + /// + + if (!context.dataServiceVersion) { + context.dataServiceVersion = version; + } + }; + + var getRequestOrResponseHeader = function (requestOrResponse, name) { + /// Gets the value of a request or response header. + /// Object representing a request or a response. + /// Name of the header to retrieve. + /// String value of the header; undefined if the header cannot be found. + + var headers = requestOrResponse.headers; + return (headers && headers[name]) || (headers && headers[name.toLowerCase()]) || undefined; + }; + + var getContentType = function (requestOrResponse) { + /// Gets the value of the Content-Type header from a request or response. + /// Object representing a request or a response. + /// Object with 'mediaType' and a 'properties' dictionary; null in case that the header is not found or doesn't have a value. + + return contentType(getRequestOrResponseHeader(requestOrResponse, "Content-Type")); + }; + + var versionRE = /^\s?(\d+\.\d+);?.*$/; + var getDataServiceVersion = function (requestOrResponse) { + /// Gets the value of the DataServiceVersion header from a request or response. + /// Object representing a request or a response. + /// Data service version; undefined if the header cannot be found. + + var value = getRequestOrResponseHeader(requestOrResponse, "DataServiceVersion"); + if (value) { + var matches = versionRE.exec(value); + if (matches && matches.length) { + return matches[1]; + } + } + + // Fall through and return undefined. + }; + + var handlerAccepts = function (handler, cType) { + /// Checks that a handler can process a particular mime type. + /// Handler object that is processing a resquest or response. + /// Object with 'mediaType' and a 'properties' dictionary. + /// True if the handler can process the mime type; false otherwise. + + // The following check isn't as strict because if cType.mediaType = application/; it will match an accept value of "application/xml"; + // however in practice we don't not expect to see such "suffixed" mimeTypes for the handlers. + return cType && handler.accept.indexOf(cType.mediaType) >= 0; + }; + + var handlerRead = function (handler, parseCallback, response, context) { + /// Invokes the parser associated with a handler for reading the payload of a HTTP response. + /// Handler object that is processing the response. + /// Parser function that will process the response payload. + /// HTTP response whose payload is going to be processed. + /// Object used as the context for processing the response. + /// True if the handler processed the response payload and the response.data property was set; false otherwise. + + if (!response || !response.headers) { + return false; + } + + var cType = getContentType(response); + var version = getDataServiceVersion(response) || ""; + var body = response.body; + + if (!assigned(body)) { + return false; + } + + if (handlerAccepts(handler, cType)) { + var readContext = createReadWriteContext(cType, version, context, handler); + readContext.response = response; + response.data = parseCallback(handler, body, readContext); + return response.data !== undefined; + } + + return false; + }; + + var handlerWrite = function (handler, serializeCallback, request, context) { + /// Invokes the serializer associated with a handler for generating the payload of a HTTP request. + /// Handler object that is processing the request. + /// Serializer function that will generate the request payload. + /// HTTP request whose payload is going to be generated. + /// Object used as the context for serializing the request. + /// True if the handler serialized the request payload and the request.body property was set; false otherwise. + if (!request || !request.headers) { + return false; + } + + var cType = getContentType(request); + var version = getDataServiceVersion(request); + + if (!cType || handlerAccepts(handler, cType)) { + var writeContext = createReadWriteContext(cType, version, context, handler); + writeContext.request = request; + + request.body = serializeCallback(handler, request.data, writeContext); + + if (request.body !== undefined) { + fixRequestHeader(request, "DataServiceVersion", writeContext.dataServiceVersion || "1.0"); + fixRequestHeader(request, "Content-Type", contentTypeToString(writeContext.contentType)); + fixRequestHeader(request, "MaxDataServiceVersion", handler.maxDataServiceVersion); + return true; + } + } + + return false; + }; + + var handler = function (parseCallback, serializeCallback, accept, maxDataServiceVersion) { + /// Creates a handler object for processing HTTP requests and responses. + /// Parser function that will process the response payload. + /// Serializer function that will generate the request payload. + /// String containing a comma separated list of the mime types that this handler can work with. + /// String indicating the highest version of the protocol that this handler can work with. + /// Handler object. + + return { + accept: accept, + maxDataServiceVersion: maxDataServiceVersion, + + read: function (response, context) { + return handlerRead(this, parseCallback, response, context); + }, + + write: function (request, context) { + return handlerWrite(this, serializeCallback, request, context); + } + }; + }; + + var textParse = function (handler, body /*, context */) { + return body; + }; + + var textSerialize = function (handler, data /*, context */) { + if (assigned(data)) { + return data.toString(); + } else { + return undefined; + } + }; + + odata.textHandler = handler(textParse, textSerialize, "text/plain", MAX_DATA_SERVICE_VERSION); + + + + var xmlMediaType = "application/xml"; + + // URI prefixes to generate smaller code. + var http = "http://"; + var w3org = http + "www.w3.org/"; // http://www.w3.org/ + var ado = http + "schemas.microsoft.com/ado/"; // http://schemas.microsoft.com/ado/ + var adoDs = ado + "2007/08/dataservices"; // http://schemas.microsoft.com/ado/2007/08/dataservices + + var xmlnsNS = w3org + "2000/xmlns/"; // http://www.w3.org/2000/xmlns/ + var xmlNS = w3org + "XML/1998/namespace"; // http://www.w3.org/XML/1998/namespace + var edmxNs = ado + "2007/06/edmx"; // http://schemas.microsoft.com/ado/2007/06/edmx + var edmNs = ado + "2008/09/edm"; // http://schemas.microsoft.com/ado/2008/09/edm + var edmNs2 = ado + "2006/04/edm"; // http://schemas.microsoft.com/ado/2006/04/edm + var edmNs3 = ado + "2007/05/edm"; // http://schemas.microsoft.com/ado/2007/05/edm + var edmNs4 = ado + "2009/11/edm"; // http://schemas.microsoft.com/ado/2009/11/edm + var atomXmlNs = w3org + "2005/Atom"; // http://www.w3.org/2005/Atom + var appXmlNs = w3org + "2007/app"; // http://www.w3.org/2007/app + var odataXmlNs = adoDs; // http://schemas.microsoft.com/ado/2007/08/dataservices + var odataMetaXmlNs = adoDs + "/metadata"; // http://schemas.microsoft.com/ado/2007/08/dataservices/metadata + var odataRelatedPrefix = adoDs + "/related/"; // http://schemas.microsoft.com/ado/2007/08/dataservices/related + var odataScheme = adoDs + "/scheme"; // http://schemas.microsoft.com/ado/2007/08/dataservices/scheme + + var hasLeadingOrTrailingWhitespace = function (text) { + /// Checks whether the specified string has leading or trailing spaces. + /// String to check. + /// true if text has any leading or trailing whitespace; false otherwise. + + var re = /(^\s)|(\s$)/; + return re.test(text); + }; + + var appendAsXml = function (domNode, xmlAsText) { + /// Appends an XML text fragment into the specified DOM element node. + /// DOM node for the parent element. + /// XML to append as a child of element. + + var value = "" + xmlAsText + ""; + var parsed = xmlParse(value, null); + + var doc = domNode.ownerDocument; + var imported = parsed.domNode; + if ("importNode" in doc) { + imported = doc.importNode(parsed.domNode, true); + } + + var importedChild = imported.firstChild; + while (importedChild) { + domNode.appendChild(importedChild); + importedChild = importedChild.nextSibling; + } + }; + + var safeSetProperty = function (obj, name, value) { + /// Safely set as property in an object by invoking obj.setProperty. + /// Object that exposes a setProperty method. + /// Property name. + /// Property value. + try { + obj.setProperty(name, value); + } catch (_) { } + }; + + var createDomParser = function () { + /// Creates a DOM parser object. + /// DOMParser object. + var exception; + var result; + if (window.ActiveXObject !== undefined) { + try { + result = new ActiveXObject("Msxml2.DOMDocument.6.0"); + result.async = false; + + return result; + } catch (_) { + try { + result = new ActiveXObject("Msxml2.DOMDocument.3.0"); + + result.async = false; + safeSetProperty(result, "ProhibitDTD", true); + safeSetProperty(result, "MaxElementDepth", 256); + safeSetProperty(result, "AllowDocumentFunction", false); + safeSetProperty(result, "AllowXsltScript", false); + + return result; + } catch (e) { + exception = e; + } + } + } else { + if (window.DOMParser) { + return new window.DOMParser(); + } + exception = { message: "XML DOM parser not supported" }; + } + throw exception; + }; + + var createAttributeExtension = function (wrappedNode) { + /// Creates an extension object for the specified attribute. + /// Wrapped node for the attribute. + /// The new extension object. + + return { + name: wrappedNode.localName, + namespace: wrappedNode.nsURI, + value: wrappedNode.domNode.value + }; + }; + + var createElementExtension = function (wrappedNode) { + /// Creates an extension object for the specified element. + /// Wrapped node for the element. + /// The new extension object. + + var result = { + name: wrappedNode.localName, + namespace: wrappedNode.nsURI, + value: getElementText(wrappedNode.domNode), + attributes: [], + children: [] + }; + + xmlAttributes(wrappedNode, function (wrappedAttribute) { + // Namespace declarations aren't processed, because every extension + // is namespace-qualified. + if (wrappedAttribute.nsURI !== xmlnsNS) { + result.attributes.push(createAttributeExtension(wrappedAttribute)); + } + }); + + xmlChildElements(wrappedNode, function (wrappedElement) { + result.children.push(createElementExtension(wrappedElement)); + }); + + return result; + }; + + var isWhitespace = function (text) { + /// Determines whether the specified text is empty or whitespace. + /// Value to inspect. + /// true if the text value is empty or all whitespace; false otherwise. + var ws = /^\s*$/; + return text === null || ws.test(text); + }; + + var isWhitespacePreserveContext = function (domElement) { + /// Determines whether the specified element has xml:space='preserve' applied. + /// Element to inspect. + /// Whether xml:space='preserve' is in effect. + while (domElement !== null && domElement.nodeType === 1) { + var val = xml_attribute(domElement, "space", xmlNS); + if (val === "preserve") { + return true; + } else if (val === "default") { + break; + } else { + domElement = domElement.parentNode; + } + } + + return false; + }; + + var getElementText = function (domElement) { + /// + /// Gets the concatenated value of all immediate child text and CDATA nodes for the specified element. + /// + /// Element to get values for. + /// Text for all direct children. + + var result = null; + var cursor = domElement.firstChild; + var whitespaceAlreadyRemoved = domElement.ownerDocument.preserveWhiteSpace === false; + var whitespacePreserveContext; + while (cursor) { + if (cursor.nodeType === 3 || cursor.nodeType === 4) { + // isElementContentWhitespace indicates that this is 'ignorable whitespace', + // but it's not defined by all browsers, and does not honor xml:space='preserve' + // in some implementations. + // + // If we can't tell either way, we walk up the tree to figure out whether + // xml:space is set to preserve; otherwise we discard pure-whitespace. + // + // For example 1. The space between and is usually 'ignorable'. + var text = cursor.nodeValue; + var shouldInclude = whitespaceAlreadyRemoved || !isWhitespace(text); + if (!shouldInclude) { + // Walk up the tree to figure out whether we are in xml:space='preserve' context + // for the cursor (needs to happen only once). + if (whitespacePreserveContext === undefined) { + whitespacePreserveContext = isWhitespacePreserveContext(domElement); + } + + shouldInclude = whitespacePreserveContext; + } + + if (shouldInclude) { + if (!result) { + result = text; + } else { + result += text; + } + } + } + + cursor = cursor.nextSibling; + } + + return result; + }; + + var getSingleElementByTagNameNS = function (domNode, namespaceURI, localName) { + /// Gets the first element under 'domNode' with the spacified name and namespace. + /// DOM element node. + /// The namespace URI of the element to match. + /// The local name of the element to match. + /// The first element found, null if none. + /// namespaceURI should be a specific namespace, otherwise the behavior is unspecified. + + var result; + if (domNode.getElementsByTagNameNS) { + result = domNode.getElementsByTagNameNS(namespaceURI, localName); + if (result.length !== 0) { + return result[0]; + } + } else { + var child = domNode.firstChild; + while (child) { + if (child.nodeType === 1 && + xmlLocalName(child) === localName && + child.namespaceURI === namespaceURI) { + return child; + } + + child = child.nextSibling; + } + } + + return null; + }; + + var checkParserErrorElement = function (element, text) { + /// Checks whether the specified element is a parser error element. + /// Element to check. + /// Original text that was being parsed. + /// + /// DOMParser-based parsers don't throw an exception but instead return a + /// specific document; this function checks this case and normalizes to + /// an exception being thrown. + /// + + // Firefox reports errors with a specific element. + var parserErrorNS = "http://www.mozilla.org/newlayout/xml/parsererror.xml"; + var wrappedElement = xml_wrapNode(element, ""); + if (wrappedElement.localName === "parsererror" && wrappedElement.nsURI === parserErrorNS) { + var reason = getElementText(element); + var sourceTextElement = getSingleElementByTagNameNS(wrappedElement, parserErrorNS, "sourcetext"); + var srcText = (sourceTextElement) ? sourceTextElement.nodeValue : ""; + throw { message: reason, errorXmlText: text, srcText: srcText }; + } + + // Chrome reports errors by injecting a header with an error message. + // The error may be localized, so instead we simply check for a header as the + // top element or first child of the document. + var xhtmlNS = "http://www.w3.org/1999/xhtml"; + if (wrappedElement.localName === "h3" && wrappedElement.nsURI == xhtmlNS || + getSingleElementByTagNameNS(element, xhtmlNS, "h3")) { + throw { message: xmlInnerText(element), errorXmlText: text, srcText: "" }; + } + }; + + var xmlAddNamespaceAttribute = function (domNode, name, attributeNamespace) { + /// Adds a namespace declaration attribute to the specified element node. + /// DOM node for the element. + /// Attribute name, eg: xmlns, xmlns:foo, xmlns:bar. + /// Namespace to associate. + + var doc = domNode.ownerDocument; + var attribute; + if (doc.createAttributeNS) { + attribute = doc.createAttributeNS(xmlnsNS, name); + } else { + attribute = doc.createNode(2, name, xmlnsNS); + } + + attribute.nodeValue = attributeNamespace; + domNode.setAttributeNode(attribute); + }; + + var xmlAppendPreserving = function (domNode, text) { + /// Appends a text node into the specified DOM element node. + /// DOM node for the element. + /// Text to append as a child of element. + + if (hasLeadingOrTrailingWhitespace(text)) { + var attr = xmlNewDomAttribute(domNode, "space", xmlNS, "xml"); + attr.value = "preserve"; + } + + var textNode = domNode.ownerDocument.createTextNode(text); + domNode.appendChild(textNode); + }; + + var xmlAttributes = function (element, onAttributeCallback) { + /// Iterates through the XML element's attributes and invokes the callback function for each one. + /// Wrapped element to iterate over. + /// Callback function to invoke with wrapped attribute nodes. + + var attribute; + var domNode = element.domNode; + var i, len; + for (i = 0, len = domNode.attributes.length; i < len; i++) { + attribute = domNode.attributes.item(i); + onAttributeCallback(xml_wrapNode(attribute)); + } + }; + + var xmlAttribute = function (element, localName, nsURI) { + /// Returns the value of an xml element attribute. + return xml_attribute(element.domNode, localName, nsURI); + }; + + var xmlAttributeNode = function (domNode, localName, nsURI) { + /// Gets an attribute node from an element. + /// DOM node for the parent element. + /// Local name for the attribute. + /// Namespace URI for the attribute. + /// The attribute node, null if not found. + + var attributes = domNode.attributes; + if (attributes.getNamedItemNS) { + return attributes.getNamedItemNS(nsURI, localName); + } + + return attributes.getQualifiedItem(localName, nsURI); + }; + + var xmlChildElements = function (element, onElementCallback) { + /// Iterates through the XML element's child elements and invokes the callback function for each one. + /// Wrapped element to iterate over. + /// Callback function to invoke with wrapped element nodes. + + var child = element.domNode.firstChild; + var childBaseURI; + while (child !== null) { + if (child.nodeType === 1) { + childBaseURI = normalizeURI(xml_baseURI(child), element.baseURI); + onElementCallback(xml_wrapNode(child, childBaseURI)); + } + + child = child.nextSibling; + } + }; + + var xmlFirstElement = function (element) { + /// Returns the first child element (wrapped) of the specified element. + /// Element to get first child for. + /// This first child element (wrapped), null if there is none. + + var child = element.domNode.firstChild; + var childBaseURI; + while (child !== null) { + if (child.nodeType === 1) { + childBaseURI = normalizeURI(xml_baseURI(child), element.baseURI); + return xml_wrapNode(child, childBaseURI); + } + + child = child.nextSibling; + } + + return null; + }; + + var xmlInnerText = function (domNode) { + /// Returns the text value of an XML element. + /// DOM element + /// + /// The text content of the node or the concatenated text + /// representing the node and its descendants; never null. + /// + + var result = domNode.text; + if (result !== undefined) { + return result; + } + + result = ""; + + var cursor = domNode.firstChild; + if (cursor) { + do { + // Process the node. + if (cursor.nodeType === 3 || cursor.nodeType === 4) { + result += cursor.nodeValue; + } + + // Advance the node. + var next = cursor.firstChild; + if (!next) { + while (cursor !== domNode) { + next = cursor.nextSibling; + if (next) { + cursor = next; + break; + } else { + cursor = cursor.parentNode; + } + } + } else { + cursor = next; + } + } while (cursor !== domNode); + } + + return result; + }; + + var xmlLocalName = function (node) { + /// Returns the localName of a XML node. + /// DOM node to get value for. + /// localName of node. + + if (node.localName) { + return node.localName; + } + + return node.baseName; + }; + + var xmlNewDocument = function (name, nsURI) { + /// Creates a new XML document. + /// Local name of the root element. + /// Namespace of the root element. + /// The wrapped root element of the document. + + var dom; + + if (window.ActiveXObject !== undefined) { + dom = createDomParser(); + dom.documentElement = dom.createNode(1, name, nsURI); + } + else if (window.document.implementation && window.document.implementation.createDocument) { + dom = window.document.implementation.createDocument(nsURI, name, null); + } + + return xml_wrapNode(dom.documentElement); + }; + + var xmlNewDomAttribute = function (domNode, localName, nsURI, nsPrefix) { + /// Creates a new unwrapped DOM attribute. + /// Parent element to which new node should be added. + /// Local name for attribute. + /// Namespace URI for the attribute. + /// Namespace prefix for attribute to be created. + /// The created DOM attribute. + + var doc = domNode.ownerDocument; + var attribute; + localName = (nsPrefix) ? (nsPrefix + ":" + localName) : localName; + if (doc.createAttributeNS) { + attribute = doc.createAttributeNS(nsURI, localName); + domNode.setAttributeNodeNS(attribute); + } else { + attribute = doc.createNode(2, localName, nsURI || undefined); + domNode.setAttributeNode(attribute); + } + + return attribute; + }; + + var xmlNewDomElement = function (domNode, localName, nsURI, nsPrefix) { + /// Creates a new unwrapped DOM element. + /// Parent element to which new node should be added. + /// Local name for element. + /// Namespace URI for the element. + /// Namespace prefix for attribute to be created. + /// The created DOM element. + + var doc = domNode.ownerDocument; + var element; + localName = (nsPrefix) ? (nsPrefix + ":" + localName) : localName; + if (doc.createElementNS) { + element = doc.createElementNS(nsURI, localName); + } else { + element = doc.createNode(1, localName, nsURI || undefined); + } + + domNode.appendChild(element); + return element; + }; + + var xmlNewElement = function (parent, name, nsURI, value) { + /// Creates a new wrapped element. + /// Wrapped parent element to which new node should be added. + /// Local name for element. + /// Namespace URI for the element. + /// Text value to append in element, if applicable. + /// The created wrapped element. + + var dom = parent.domNode.ownerDocument; + + var element; + if (dom.createElementNS) { + element = dom.createElementNS(nsURI, name); + } else { + element = dom.createNode(1, name, nsURI || undefined); + } + + if (value) { + xmlAppendPreserving(element, value); + } + + parent.domNode.appendChild(element); + return xml_wrapNode(element); + }; + + var xmlNewAttribute = function (element, name, nsURI, value) { + /// Creates a new wrapped attribute. + /// Wrapped parent element to which new node should be added. + /// Local name for element. + /// Namespace URI for the element. + /// Node value for the attribute. + /// The created wrapped attribute. + + var dom = element.domNode.ownerDocument; + + var attribute; + if (dom.createAttributeNS) { + attribute = dom.createAttributeNS(nsURI, name); + attribute.value = value; + element.domNode.setAttributeNodeNS(attribute); + } else { + attribute = dom.createNode(2, name, nsURI || undefined); + attribute.value = value; + element.domNode.setAttributeNode(attribute); + } + + return xml_wrapNode(attribute); + }; + + var xmlQualifyXmlTagName = function (name, prefix) { + /// Qualifies an XML tag name with the specified prefix. + /// Element name + /// Prefix to use, possibly null. + /// The qualified name. + /// This operates at the lexical level - there is no awareness of in-scope prefixes. + + if (prefix) { + return prefix + ":" + name; + } + + return name; + }; + + var xmlParse = function (text, baseURI) { + /// Returns an XML document from the specified text. + /// Document text. + /// Base URI for the document. + /// The wrapped root element of the document. + + var root = xml_parse(text); + var rootBaseURI = normalizeURI(xml_baseURI(root), baseURI); + return xml_wrapNode(root, rootBaseURI); + }; + + var xmlSerialize = function (root) { + /// + /// Returns the text representation of the document to which the specified node belongs. + /// + /// Wrapped element in the document to serialize. + /// Serialized document. + + var dom = root.domNode.ownerDocument; + return xmlSerializeNode(dom); + }; + + var xmlSerializeChildren = function (domNode) { + /// Returns the XML representation of the all the descendants of the node. + /// Node to serialize. + /// The XML representation of all the descendants of the node. + + var children = domNode.childNodes; + var i, len = children.length; + if (len === 0) { + return ""; + } + + var fragment = domNode.ownerDocument.createDocumentFragment(); + for (i = 0; i < len; i++) { + fragment.appendChild(children[i].cloneNode(true)); + } + + return xmlSerializeNode(fragment); + }; + + var xmlSerializeNode = function (domNode) { + /// Returns the XML representation of the node and all its descendants. + /// Node to serialize. + /// The XML representation of the node and all its descendants. + + var xml = domNode.xml; + if (xml !== undefined) { + return xml; + } + + if (window.XMLSerializer) { + var serializer = new window.XMLSerializer(); + return serializer.serializeToString(domNode); + } + + throw { message: "XML serialization unsupported" }; + }; + + var xml_attribute = function (domNode, localName, nsURI) { + /// Gets the value of a DOM attribute. + /// DOM element to get attribute for. + /// Name of the attribute to get. + /// Namespace of the attribute to get. + /// Value of the attribute if found; null otherwise. + + if (domNode.getAttributeNS) { + return domNode.getAttributeNS(nsURI || null, localName); + } + + // The method is not supported so we work with the attributes collection. + var node = domNode.attributes.getQualifiedItem(localName, nsURI); + if (node) { + return node.value; + } + + return null; + }; + + var xml_baseURI = function (domNode) { + /// Gets the value of the xml:base attribute on the specified element. + /// Element to get attribute value from. + /// Value of the xml:base attribute if found; null otherwise. + + return xml_attribute(domNode, "base", xmlNS); + }; + + var xml_parse = function (text) { + /// Returns an XML document from the specified text. + /// Document text. + /// The root element of the document. + + var dom = createDomParser(); + if (dom.parseFromString) { + dom = dom.parseFromString(text, "text/xml"); + checkParserErrorElement(dom.documentElement, text); + } else { + dom.loadXML(text); + if (dom.parseError.errorCode !== 0) { + throw { message: dom.parseError.reason, errorXmlText: text, srcText: dom.parseError.srcText }; + } + } + + return dom.documentElement; + }; + + var xml_wrapNode = function (domNode, baseURI) { + /// Creates a wrapped DOM node. + /// DOM node to wrap. + /// Base URI in scope for the node. + /// + /// An object with the wrapped domNode, information about its in-scope URI, and simple + /// access to name and namespace. + /// + + var nsURI = domNode.namespaceURI; + var nodeName = domNode.nodeName; + + // MSXML 3.0 doesn't return a namespaceURI for xmlns attributes. + if (!nsURI) { + if (domNode.nodeType === 2 && (nodeName === "xmlns" || nodeName.indexOf("xmlns:", 0) === 0)) { + nsURI = xmlnsNS; + } else { + nsURI = null; + } + } + + return { + baseURI: baseURI, + domNode: domNode, + localName: xmlLocalName(domNode), + nsURI: nsURI + }; + }; + + var readODataXmlDocument = function (xmlRoot) { + /// Reads an OData link(s) producing an object model in return. + /// Top-level element to read. + /// The object model representing the specified element. + + if (xmlRoot.nsURI === odataXmlNs) { + switch (xmlRoot.localName) { + case "links": + return readLinks(xmlRoot); + case "uri": + return readUri(xmlRoot); + } + } + return undefined; + }; + + var readLinks = function (linksElement) { + /// Deserializes an OData XML links element. + /// XML links element. + /// A new object representing the links collection. + + var uris = []; + + xmlChildElements(linksElement, function (child) { + if (child.localName === "uri" && child.nsURI === odataXmlNs) { + var uri = readUri(child); + uris.push(uri); + } + }); + + return { results: uris }; + }; + + var readUri = function (uriElement) { + /// Deserializes an OData XML uri element. + /// XML uri element. + /// A new object representing the uri. + + return { uri: xmlInnerText(uriElement.domNode) }; + }; + + var writeODataXmlDocument = function (data) { + /// Writes the specified data into an OData XML document. + /// Data to write. + /// The root of the DOM tree built. + + if (payloadTypeOf(data) === PAYLOADTYPE_COMPLEXTYPE && + !data.__metadata && data.hasOwnProperty("uri")) { + return writeUri(data); + } + + // Allow for undefined to be returned. + }; + + var writeUri = function (data) { + /// Writes the specified uri data into an OData XML uri document. + /// Uri data to write in intermediate format. + /// The feed element of the DOM tree built. + + var uri = writeODataXmlNode(null, "uri", odataXmlNs); + if (data.uri) { + xmlAppendPreserving(uri.domNode, data.uri); + } + + return uri; + }; + + var writeODataXmlNode = function (parent, name, nsURI) { + /// Writes the root of an OData XML document, possibly under an existing element. + /// Element under which to create a new element. + /// Name for the new element. + /// Namespace URI for the element. + /// The created element. + + if (parent) { + return xmlNewElement(parent, name, nsURI); + } + return xmlNewDocument(name, nsURI); + }; + + var xmlParser = function (handler, text) { + /// Parses an OData XML document. + /// This handler. + /// Document text. + /// An object representation of the document; undefined if not applicable. + + if (text) { + var root = xmlParse(text); + if (root) { + return readODataXmlDocument(root); + } + } + + // Allow for undefined to be returned. + }; + + var xmlSerializer = function (handler, data, context) { + /// Serializes an OData XML object into a document. + /// This handler. + /// Representation of feed or entry. + /// Object with parsing context. + /// A text representation of the data object; undefined if not applicable. + + var cType = context.contentType = context.contentType || contentType(xmlMediaType); + var result = undefined; + if (cType && cType.mediaType === xmlMediaType) { + var xmlDoc = writeODataXmlDocument(data); + if (xmlDoc) { + result = xmlSerialize(xmlDoc); + } + } + return result; + }; + + odata.xmlHandler = handler(xmlParser, xmlSerializer, xmlMediaType, MAX_DATA_SERVICE_VERSION); + + + + var atomAcceptTypes = ["application/atom+xml", "application/atomsvc+xml", "application/xml"]; + var atomMediaType = atomAcceptTypes[0]; + + var inlineTag = xmlQualifyXmlTagName("inline", "m"); + var propertiesTag = xmlQualifyXmlTagName("properties", "m"); + var propertyTypeAttribute = xmlQualifyXmlTagName("type", "m"); + var propertyNullAttribute = xmlQualifyXmlTagName("null", "m"); + + // These are the namespaces that are not considered ATOM extension namespaces. + var nonExtensionNamepaces = [atomXmlNs, appXmlNs, xmlNS, xmlnsNS]; + + // These are entity property mapping paths that have well-known paths. + var knownCustomizationPaths = { + SyndicationAuthorEmail: "author/email", + SyndicationAuthorName: "author/name", + SyndicationAuthorUri: "author/uri", + SyndicationContributorEmail: "contributor/email", + SyndicationContributorName: "contributor/name", + SyndicationContributorUri: "contributor/uri", + SyndicationPublished: "published", + SyndicationRights: "rights", + SyndicationSummary: "summary", + SyndicationTitle: "title", + SyndicationUpdated: "updated" + }; + + var isExtensionNs = function (nsURI) { + /// Checks whether the specified namespace is an extension namespace to ATOM. + /// Namespace to check. + /// true if nsURI is an extension namespace to ATOM; false otherwise. + + return !(contains(nonExtensionNamepaces, nsURI)); + }; + + var propertyTypeDefaultConverter = function (propertyValue) { + /// Does a no-op conversion on the specified value. + /// Value to convert. + /// Original property value. + + return propertyValue; + }; + + var parseBool = function (propertyValue) { + /// Parses a string into a boolean value. + /// Value to parse. + /// true if the property value is 'true'; false otherwise. + + return propertyValue === "true"; + }; + + // The captured indices for this expression are: + // 0 - complete input + // 1,2,3 - year with optional minus sign, month, day + // 4,5,6 - hours, minutes, seconds + // 7 - optional milliseconds + // 8 - everything else (presumably offset information) + var parseDateTimeRE = /^(-?\d{4,})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.(\d+))?(.*)$/; + + var parseDateTimeMaybeOffset = function (value, withOffset) { + /// Parses a string into a DateTime value. + /// Value to parse. + /// Whether offset is expected. + /// The parsed value. + + // We cannot parse this in cases of failure to match or if offset information is specified. + var parts = parseDateTimeRE.exec(value); + var offset = (parts) ? getCanonicalTimezone(parts[8]) : null; + + if (!parts || (!withOffset && offset !== "Z")) { + throw { message: "Invalid date/time value" }; + } + + // Pre-parse years, account for year '0' being invalid in dateTime. + var year = parseInt10(parts[1]); + if (year <= 0) { + year++; + } + + // Pre-parse optional milliseconds, fill in default. Fail if value is too precise. + var ms = parts[7]; + var ns = 0; + if (!ms) { + ms = 0; + } else { + if (ms.length > 7) { + throw { message: "Cannot parse date/time value to given precision." }; + } + + ns = formatNumberWidth(ms.substring(3), 4, true); + ms = formatNumberWidth(ms.substring(0, 3), 3, true); + + ms = parseInt10(ms); + ns = parseInt10(ns); + } + + // Pre-parse other time components and offset them if necessary. + var hours = parseInt10(parts[4]); + var minutes = parseInt10(parts[5]); + var seconds = parseInt10(parts[6]); + if (offset !== "Z") { + // The offset is reversed to get back the UTC date, which is + // what the API will eventually have. + var timezone = parseTimezone(offset); + var direction = -(timezone.d); + hours += timezone.h * direction; + minutes += timezone.m * direction; + } + + // Set the date and time separately with setFullYear, so years 0-99 aren't biased like in Date.UTC. + var result = new Date(); + result.setUTCFullYear( + year, // Year. + parseInt10(parts[2]) - 1, // Month (zero-based for Date.UTC and setFullYear). + parseInt10(parts[3]) // Date. + ); + result.setUTCHours(hours, minutes, seconds, ms); + + if (isNaN(result.valueOf())) { + throw { message: "Invalid date/time value" }; + } + + if (withOffset) { + result.__edmType = "Edm.DateTimeOffset"; + result.__offset = offset; + } + + if (ns) { + result.__ns = ns; + } + + return result; + }; + + var parseDateTime = function (propertyValue) { + /// Parses a string into a DateTime value. + /// Value to parse. + /// The parsed value. + + return parseDateTimeMaybeOffset(propertyValue, true); //Changed for .NET compability, that send with offset + }; + + var parseDateTimeOffset = function (propertyValue) { + /// Parses a string into a DateTimeOffset value. + /// Value to parse. + /// The parsed value. + /// + /// The resulting object is annotated with an __edmType property and + /// an __offset property reflecting the original intended offset of + /// the value. The time is adjusted for UTC time, as the current + /// timezone-aware Date APIs will only work with the local timezone. + /// + + return parseDateTimeMaybeOffset(propertyValue, true); + }; + + // Property type converters are parsers that convert strings into typed values. + var propertyTypeConverters = { + "Edm.Boolean": parseBool, + "Edm.Binary": propertyTypeDefaultConverter, + "Edm.DateTime": parseDateTime, + "Edm.DateTimeOffset": parseDateTimeOffset, + "Edm.Time": parseDuration, + "Edm.Decimal": propertyTypeDefaultConverter, + "Edm.Guid": propertyTypeDefaultConverter, + "Edm.String": propertyTypeDefaultConverter, + "Edm.Byte": parseInt10, + "Edm.Double": parseFloat, + "Edm.Single": parseFloat, + "Edm.Int16": parseInt10, + "Edm.Int32": parseInt10, + "Edm.Int64": propertyTypeDefaultConverter, + "Edm.SByte": parseInt10 + }; + + var formatToString = function (value) { + /// Formats a value by invoking .toString() on it. + /// Value to format. + /// Formatted text. + + return value.toString(); + }; + + // Property type formatters are serializers that convert typed values into strings. + var propertyTypeFormatters = { + "Edm.Binary": formatToString, + "Edm.Boolean": formatToString, + "Edm.Byte": formatToString, + "Edm.DateTime": formatDateTimeOffset, + "Edm.DateTimeOffset": formatDateTimeOffset, + "Edm.Decimal": formatToString, + "Edm.Double": formatToString, + "Edm.Guid": formatToString, + "Edm.Int16": formatToString, + "Edm.Int32": formatToString, + "Edm.Int64": formatToString, + "Edm.SByte": formatToString, + "Edm.Single": formatToString, + "Edm.String": formatToString, + "Edm.Time": formatDuration + }; + + var isPrimitiveType = function (typeName) { + /// Checks whether the specified type name is a primitive type. + /// Name of type to check. + /// + /// true if the type is the name of a primitive type; false otherwise. + /// + + return typeName && (propertyTypeConverters[typeName] !== undefined); + }; + + var convertFromAtomPropertyText = function (value, targetType) { + /// Converts a text value to the specified target type. + /// Text value to convert. + /// Name of type to convert from. + /// The converted value. + + if (value !== null && targetType) { + var converter = propertyTypeConverters[targetType]; + if (converter) { + value = converter(value); + } + } + + return value; + }; + + var convertToAtomPropertyText = function (value, targetType) { + /// Converts a typed value from the specified target type into a text value. + /// Typed value to convert. + /// Name of type to convert to. + /// The converted value as text. + + if (value !== null && targetType) { + if (isDate(value)) { + targetType = (isDateTimeOffset(value)) ? "Edm.DateTimeOffset" : "Edm.DateTime"; + } + + var converter = propertyTypeFormatters[targetType]; + if (converter) { + value = converter(value); + } + } + + return value; + }; + + var readAtomDocument = function (atomElement, metadata) { + /// Reads an ATOM entry, feed or service document, producing an object model in return. + /// Top-level element to read. + /// Metadata that describes the conceptual schema. + /// The object model representing the specified element, undefined if the top-level element is not part of the ATOM specification. + + // Handle feed and entry elements. + if (atomElement.nsURI === atomXmlNs) { + switch (atomElement.localName) { + case "feed": + return readAtomFeed(atomElement, metadata); + case "entry": + return readAtomEntry(atomElement, metadata); + } + } + + // Handle service documents. + if (atomElement.nsURI === appXmlNs && atomElement.localName === "service") { + return readAtomServiceDocument(atomElement); + } + + // Allow undefined to be returned. + }; + + var readAtomFeed = function (atomFeed, metadata) { + /// Deserializes an ATOM feed element. + /// ATOM feed element. + /// Metadata that describes the conceptual schema. + /// A new object representing the feed. + var feedMetadata = {}; + var feed = { + results: [], + __metadata: feedMetadata + }; + + feedMetadata.feed_extensions = readAtomExtensionAttributes(atomFeed); + + xmlChildElements(atomFeed, function (feedChild) { + switch (feedChild.nsURI) { + case atomXmlNs: + switch (feedChild.localName) { + case "id": + feedMetadata.uri = normalizeURI(xmlInnerText(feedChild.domNode), feedChild.baseURI); + feedMetadata.uri_extensions = readAtomExtensionAttributes(feedChild); + break; + case "title": + feedMetadata.title = xmlInnerText(feedChild.domNode); + feedMetadata.title_extensions = readAtomExtensionAttributes(feedChild); + break; + case "entry": + var entry = readAtomEntry(feedChild, metadata); + feed.results.push(entry); + break; + case "link": + readAtomFeedLink(feedChild, feed); + break; + } + return; + case odataMetaXmlNs: + if (feedChild.localName === "count") { + feed.__count = parseInt10(xmlInnerText(feedChild.domNode)); + return; + } + break; + } + + var extension = readAtomExtensionElement(feedChild); + feedMetadata.feed_extensions.push(extension); + }); + + return feed; + }; + + var readAtomFeedLink = function (feedLinkElement, feed) { + /// Reads an ATOM link element for a feed. + /// Link element to read. + /// Feed object to be annotated with information. + + var link = readAtomLink(feedLinkElement); + var feedMetadata = feed.__metadata; + switch (link.rel) { + case "next": + feed.__next = link.href; + feedMetadata.next_extensions = link.extensions; + break; + case "self": + feedMetadata.self = link.href; + feedMetadata.self_extensions = link.extensions; + break; + } + }; + + var readAtomLink = function (linkElement) { + /// Reads an ATOM link element. + /// Link element to read. + /// A link element representation. + + var link = { extensions: [] }; + var linkExtension; + + xmlAttributes(linkElement, function (attribute) { + if (!attribute.nsURI) { + switch (attribute.localName) { + case "href": + link.href = normalizeURI(attribute.domNode.nodeValue, linkElement.baseURI); + return; + case "type": + case "rel": + link[attribute.localName] = attribute.domNode.nodeValue; + return; + } + } + + if (isExtensionNs(attribute.nsURI)) { + linkExtension = readAtomExtensionAttribute(attribute); + link.extensions.push(linkExtension); + } + }); + + if (!link.href) { + throw { error: "href attribute missing on link element", element: linkElement }; + } + + return link; + }; + + var readAtomExtensionElement = function (atomExtension) { + /// Reads an ATOM extension element (an element not in the ATOM namespaces). + /// ATOM extension element. + /// An extension element representation. + + var extension = { + name: atomExtension.localName, + namespaceURI: atomExtension.nsURI, + attributes: readAtomExtensionAttributes(atomExtension), + children: [] + }; + + xmlChildElements(atomExtension, function (child) { + var childExtension = readAtomExtensionElement(child); + extension.children.push(childExtension); + }); + + if (extension.children.length === 0) { + var text = xmlInnerText(atomExtension.domNode); + if (text) { + extension.value = text; + } + } + + return extension; + }; + + var readAtomExtensionAttributes = function (xmlElement) { + /// Reads ATOM extension attributes from an element. + /// ATOM element with zero or more extension attributes. + /// An array of extension attribute representations. + + var extensions = []; + xmlAttributes(xmlElement, function (attribute) { + if (isExtensionNs(attribute.nsURI)) { + var extension = readAtomExtensionAttribute(attribute); + extensions.push(extension); + } + }); + + return extensions; + }; + + var readAtomExtensionAttribute = function (attribute) { + /// Reads an ATOM extension attribute into an object. + /// ATOM extension attribute. + /// An object with the attribute information. + + return { + name: attribute.localName, + namespaceURI: attribute.nsURI, + value: attribute.domNode.nodeValue + }; + }; + + var getObjectValueByPath = function (path, item) { + /// Gets a slashed path value from the specified item. + /// Property path to read ('/'-separated). + /// Object to get value from. + /// The property value, possibly undefined if any path segment is missing. + + // Fast path. + if (path.indexOf('/') === -1) { + return item[path]; + } else { + var parts = path.split('/'); + var i, len; + for (i = 0, len = parts.length; i < len; i++) { + // Avoid traversing a null object. + if (item === null) { + return undefined; + } + + item = item[parts[i]]; + if (item === undefined) { + return item; + } + } + + return item; + } + }; + + var setObjectValueByPath = function (path, target, value, propertyType) { + /// Sets a slashed path value on the specified target. + /// Property path to set ('/'-separated). + /// Object to set value on. + /// Value to set. + /// Property type to set in metadata. + + // Fast path. + var propertyName; + if (path.indexOf('/') === -1) { + target[path] = value; + propertyName = path; + } else { + var parts = path.split('/'); + var i, len; + for (i = 0, len = (parts.length - 1) ; i < len; i++) { + // We construct each step of the way if the property is missing; + // if it's already initialized to null, we stop further processing. + var next = target[parts[i]]; + if (next === undefined) { + next = {}; + target[parts[i]] = next; + } else if (next === null) { + return; + } + + target = next; + } + + propertyName = parts[i]; + target[propertyName] = value; + } + + if (propertyType) { + var metadata = target.__metadata = target.__metadata || {}; + var properties = metadata.properties = metadata.properties || {}; + var property = properties[propertyName] = properties[propertyName] || {}; + property.type = propertyType; + } + }; + + var expandCustomizationPath = function (path) { + /// Expands a customization path if it's well-known. + /// Path to expand. + /// Expanded path or 'path' otherwise. + + return knownCustomizationPaths[path] || path; + }; + + var getXmlPathValue = function (ns, xmlPath, element, readAsXml) { + /// Returns the text value of the element or attribute at xmlPath. + /// Namespace for elements to match. + /// + /// '/'-separated list of element names, possibly ending with a '@'-prefixed attribute name. + /// + /// Root element. + /// Whether the value should be read as XHTML. + /// The text value of the node found, undefined if none. + + var parts = xmlPath.split('/'); + var i, len; + for (i = 0, len = parts.length; i < len; i++) { + if (parts[i].charAt(0) === "@") { + return xml_attribute(element, parts[i].substring(1), ns); + } else { + element = getSingleElementByTagNameNS(element, ns, parts[i]); + if (!element) { + return undefined; + } + } + } + + if (readAsXml) { + // Treat per XHTML in http://tools.ietf.org/html/rfc4287#section-3.1.1, including the DIV + // in the content. + return xmlSerializeChildren(element); + } else { + return xmlInnerText(element); + } + }; + + var setXmlPathValue = function (ns, nsPrefix, xmlPath, element, writeAsKind, value) { + /// Sets the text value of the element or attribute at xmlPath. + /// Namespace for elements to match. + /// Namespace prefix for elements to be created. + /// + /// '/'-separated list of element names, possibly ending with a '@'-prefixed attribute name. + /// + /// Root element. + /// Whether the value should be written as text, html or xhtml. + /// The text value of the node to write. + + var target = element; + var parts = xmlPath.split('/'); + var i, len; + for (i = 0, len = parts.length; i < len; i++) { + var next; + if (parts[i].charAt(0) === "@") { + next = xmlAttributeNode(target, parts[i].substring(1), ns); + if (!next) { + next = xmlNewDomAttribute(target, parts[i].substring(1), ns, nsPrefix); + } + } else { + next = getSingleElementByTagNameNS(target, ns, parts[i]); + if (!next) { + next = xmlNewDomElement(target, parts[i], ns, nsPrefix); + } + } + + target = next; + } + + // Target can be an attribute (2) or an element (1). + if (target.nodeType === 2) { + target.value = value; + } else { + // The element should be empty at this point; we won't erase its contents. + if (writeAsKind) { + target.setAttribute("type", writeAsKind); + } + + if (writeAsKind === "xhtml") { + appendAsXml(target, value); + } else { + xmlAppendPreserving(target, value); + } + } + }; + + var isElementEmpty = function (element) { + /// Checks whether the specified XML element is empty. + /// DOM element node to check. + /// true if the element is empty; false otherwise. + /// + /// The element is considered empty if it doesn't have any attributes other than + /// namespace declarations or type declarations and if it has no child nodes. + /// + + // If there are any child elements or text nodes, it's not empty. + if (element.childNodes.length) { + return false; + } + + // If there are no attributes, then we know it's already empty. + var attributes = element.attributes; + var len = attributes.length; + if (len === 0) { + return true; + } + + // Otherwise, we have to search for attributes that aren't namespace declarations. + for (var i = 0; i < len; i++) { + var attributeName = attributes[i].nodeName; + if (attributeName !== "xmlns" && attributeName.indexOf("xmlns:") !== 0 && attributeName !== "m:type") { + return false; + } + } + + return true; + }; + var removeXmlProperty = function (entryElement, propertyPath) { + /// Removes a property from an entry. + /// XML element for an ATOM OData entry. + /// Property path to an element. + + // Get the 'properties' node from 'content' or 'properties'. + var propertiesElement = getSingleElementByTagNameNS(entryElement.domNode, odataMetaXmlNs, "properties"); + if (!propertiesElement) { + var contentElement = getSingleElementByTagNameNS(entryElement.domNode, atomXmlNs, "content"); + if (contentElement) { + propertiesElement = getSingleElementByTagNameNS(contentElement, odataMetaXmlNs, "properties"); + } + } + + if (propertiesElement) { + // Traverse down to the parent of the property path. + var propertyParentElement = propertiesElement; + var parts = propertyPath.split("/"); + var i, len; + for (i = 0, len = (parts.length - 1) ; i < len; i++) { + propertyParentElement = getSingleElementByTagNameNS(propertyParentElement, odataXmlNs, parts[i]); + if (!propertyParentElement) { + return; + } + } + + // Remove the child from its parent. + var propertyElement = getSingleElementByTagNameNS(propertyParentElement, odataXmlNs, parts[i]); + if (propertyElement) { + propertyParentElement.removeChild(propertyElement); + } + + // Remove empty elements up the parent chain. + var candidate = propertyParentElement; + while (candidate !== propertiesElement && isElementEmpty(candidate)) { + var parent = candidate.parentNode; + parent.removeChild(candidate); + candidate = parent; + } + } + }; + + var applyEntryCustomizationToEntry = function (customization, sourcePath, entryElement, entryObject, propertyType, suffix, context) { + /// Applies a specific feed customization item to an entry. + /// Object with customization description. + /// Property path to map ('source' in the description). + /// XML element for the entry that corresponds to the object being written. + /// Object being written. + /// Name of property type to write. + /// Suffix to feed customization properties. + /// Context used for serialization. + + var targetPath = customization["FC_TargetPath" + suffix]; + var xmlPath = expandCustomizationPath(targetPath); + var xmlNamespace = (targetPath !== xmlPath) ? atomXmlNs : customization["FC_NsUri" + suffix]; + var keepInContent = (customization["FC_KeepInContent" + suffix] === "true") ? true : false; + var writeAsKind = customization["FC_ContentKind" + suffix]; + var prefix = customization["FC_NsPrefix" + suffix] || null; + + // Get the value to be written. + var value = getObjectValueByPath(sourcePath, entryObject); + + // Special case: for null values, the 'property' should be left as it was generated. + // undefined values will appear when metadata describe a property the object doesn't have. + if (!assigned(value)) { + return; + } + + // Remove the property if it should not be kept in content. + if (!keepInContent) { + context.dataServiceVersion = "2.0"; + removeXmlProperty(entryElement, sourcePath); + } + + // Set/create the subtree for the property path with the appropriate value. + value = convertToAtomPropertyText(value, propertyType); + setXmlPathValue(xmlNamespace, prefix, xmlPath, entryElement.domNode, writeAsKind, value); + }; + + var applyEntryCustomizationToObject = function (customization, sourcePath, entryElement, entryObject, propertyType, suffix) { + /// Applies a specific feed customization item to an object. + /// Object with customization description. + /// Property path to set ('source' in the description). + /// XML element for the entry that corresponds to the object being read. + /// Object being read. + /// Name of property type to set. + /// Suffix to feed customization properties. + + // If keepInConent equals true then we do nothing as the object has been deserialized at this point. + if (customization["FC_KeepInContent" + suffix] === "true") { + return; + } + // An existing 'null' means that the property was set to null in the properties, + // which overrides other items. + if (getObjectValueByPath(sourcePath, entryObject) === null) { + return; + } + + var targetPath = customization["FC_TargetPath" + suffix]; + var xmlPath = expandCustomizationPath(targetPath); + var xmlNamespace = (targetPath !== xmlPath) ? atomXmlNs : customization["FC_NsUri" + suffix]; + var readAsXhtml = (customization["FC_ContentKind" + suffix] === "xhtml"); + var value = getXmlPathValue(xmlNamespace, xmlPath, entryElement.domNode, readAsXhtml); + + // If the XML tree does not contain the necessary elements to read the value, + // then it shouldn't be considered null, but rather ignored at all. This prevents + // the customization from generating the object path down to the property. + if (value === undefined) { + return; + } + + value = convertFromAtomPropertyText(value, propertyType); + + // Set the value on the object. + setObjectValueByPath(sourcePath, entryObject, value, propertyType); + }; + + var lookupPropertyType = function (metadata, entityType, path) { + /// Looks up the type of a property given its path in an entity type. + /// Metadata in which to search for base and complex types. + /// Entity type to which property belongs. + /// Property path to look at. + /// The name of the property type; possibly null. + + var parts = path.split("/"); + var i, len; + while (entityType) { + // Keep track of the type being traversed, necessary for complex types. + var traversedType = entityType; + + for (i = 0, len = parts.length; i < len; i++) { + // Traverse down the structure as necessary. + var properties = traversedType.property; + if (!properties) { + break; + } + + // Find the property by scanning the property list (might be worth pre-processing). + var propertyFound = lookupProperty(properties, parts[i]); + if (!propertyFound) { + break; + } + + var propertyType = propertyFound.type; + + // We could in theory still be missing types, but that would + // be caused by a malformed path. + if (!propertyType || isPrimitiveType(propertyType)) { + return propertyType || null; + } + + traversedType = lookupComplexType(propertyType, metadata); + if (!traversedType) { + return null; + } + } + + // Traverse up the inheritance chain. + entityType = lookupEntityType(entityType.baseType, metadata); + } + + return null; + }; + + var applyMetadataToObject = function (entryElement, entryObject, metadata) { + /// Applies feed customization properties to an object being read. + /// XML element for the entry that corresponds to the object being read. + /// Object being read. + /// Metadata that describes the conceptual schema. + + if (!metadata || metadata.length === 0) { + return; + } + + var typeName = entryObject.__metadata.type; + while (typeName) { + var entityType = lookupEntityType(typeName, metadata); + if (!entityType) { + return; + } + + // Apply all feed customizations from the entity and each of its properties. + var propertyType; + var source = entityType.FC_SourcePath; + if (source) { + propertyType = lookupPropertyType(metadata, entityType, source); + applyEntryCustomizationToObject(entityType, source, entryElement, entryObject, propertyType, ""); + } + + var properties = entityType.property; + if (properties) { + var i, len; + for (i = 0, len = properties.length; i < len; i++) { + var property = properties[i]; + var suffixCounter = 0; + var suffix = ""; + while (property["FC_TargetPath" + suffix]) { + source = property.name; + propertyType = property.type; + + var sourcePath = property["FC_SourcePath" + suffix]; + if (sourcePath) { + source += "/" + sourcePath; + propertyType = lookupPropertyType(metadata, entityType, source); + } + + applyEntryCustomizationToObject(property, source, entryElement, entryObject, propertyType, suffix); + suffixCounter++; + suffix = "_" + suffixCounter; + } + } + } + + // Apply feed customizations from base types. + typeName = entityType.baseType; + } + }; + + var readAtomEntry = function (atomEntry, metadata) { + /// Reads an ATOM entry in OData format. + /// XML element for the entry. + /// Metadata that describes the conceptual schema. + /// An object in payload representation format. + + var entryMetadata = {}; + var entry = { + __metadata: entryMetadata + }; + + var etag = xmlAttribute(atomEntry, "etag", odataMetaXmlNs); + if (etag) { + entryMetadata.etag = etag; + } + + xmlChildElements(atomEntry, function (entryChild) { + if (entryChild.nsURI === atomXmlNs) { + switch (entryChild.localName) { + case "id": + entryMetadata.uri = normalizeURI(xmlInnerText(entryChild.domNode), entryChild.baseURI); + entryMetadata.uri_extensions = readAtomExtensionAttributes(entryChild); + break; + case "category": + readAtomEntryType(entryChild, entry, entryMetadata); + break; + case "content": + readAtomEntryContent(entryChild, entry); + break; + case "link": + readAtomEntryLink(entryChild, entry, metadata); + break; + } + } + + if (entryChild.nsURI === odataMetaXmlNs && entryChild.localName === "properties") { + readAtomEntryStructuralObject(entryChild, entry, entryMetadata); + } + }); + + // Apply feed customizations if applicable. + applyMetadataToObject(atomEntry, entry, metadata); + + return entry; + }; + + var readAtomEntryType = function (atomCategory, entry, entryMetadata) { + /// Reads type information from an ATOM category element. + /// XML category element. + /// Entry object to update with information. + /// entry.__metadata, passed as an argument simply to help minify the code. + + var scheme = xmlAttribute(atomCategory, "scheme"); + var term = xmlAttribute(atomCategory, "term"); + + if (scheme === odataScheme) { + if (entry.__metadata.type) { + throw { message: "Invalid AtomPub document: multiple category elements defining the entry type were encounterd withing an entry", element: atomCategory }; + } + + entryMetadata.type = term; + entryMetadata.type_extensions = []; + + var typeExtension; + xmlAttributes(atomCategory, function (attribute) { + if (!attribute.nsURI) { + if (attribute.localName !== "scheme" && attribute.localName !== "term") { + typeExtension = readAtomExtensionAttribute(attribute); + entryMetadata.type_extensions.push(typeExtension); + } + } else if (isExtensionNs(attribute.nsURI)) { + typeExtension = readAtomExtensionAttribute(attribute); + entryMetadata.type_extensions.push(typeExtension); + } + }); + } + }; + + var readAtomEntryContent = function (atomEntryContent, entry) { + /// Reads an ATOM content element. + /// XML entry element. + /// Entry object to update with information. + + var src = xmlAttribute(atomEntryContent, "src"); + var type = xmlAttribute(atomEntryContent, "type"); + var entryMetadata = entry.__metadata; + + if (src) { + if (!type) { + throw { message: "Invalid AtomPub document: content element must specify the type attribute if the src attribute is also specified", element: atomEntryContent }; + } + + entryMetadata.media_src = normalizeURI(src, atomEntryContent.baseURI); + entryMetadata.content_type = type; + } + + xmlChildElements(atomEntryContent, function (contentChild) { + if (src) { + throw { message: "Invalid AtomPub document: content element must not have child elements if the src attribute is specified", element: atomEntryContent }; + } + + if (contentChild.nsURI === odataMetaXmlNs && contentChild.localName === "properties") { + readAtomEntryStructuralObject(contentChild, entry, entryMetadata); + } + }); + }; + + var readAtomEntryEditMediaLink = function (link, entry, entryMetadata) { + /// Reads an ATOM media link element. + /// Link representation (not the XML element). + /// Entry object to update with information. + /// entry.__metadata, passed as an argument simply to help minify the code. + + entryMetadata.edit_media = link.href; + entryMetadata.edit_media_extensions = []; + + // Read the link extensions. + var i, len; + for (i = 0, len = link.extensions.length; i < len; i++) { + if (link.extensions[i].namespaceURI === odataMetaXmlNs && link.extensions[i].name === "etag") { + entryMetadata.media_etag = link.extensions[i].value; + } else { + entryMetadata.edit_media_extensions.push(link.extensions[i]); + } + } + }; + + var readAtomEntryLink = function (atomEntryLink, entry, metadata) { + /// Reads a link element on an entry. + /// 'link' element on the entry. + /// An object in payload representation format. + /// Metadata that describes the conceptual schema. + + var link = readAtomLink(atomEntryLink); + var entryMetadata = entry.__metadata; + switch (link.rel) { + case "self": + entryMetadata.self = link.href; + entryMetadata.self_link_extensions = link.extensions; + break; + case "edit": + entryMetadata.edit = link.href; + entryMetadata.edit_link_extensions = link.extensions; + break; + case "edit-media": + readAtomEntryEditMediaLink(link, entry, entryMetadata); + break; + default: + if (link.rel.indexOf(odataRelatedPrefix) === 0) { + readAtomEntryDeferredProperty(atomEntryLink, link, entry, metadata); + } + break; + } + }; + + var readAtomEntryDeferredProperty = function (atomEntryLink, link, entry, metadata) { + /// Reads a potentially-deferred property on an entry. + /// 'link' element on the entry. + /// Parsed link object. + /// An object in payload representation format. + /// Metadata that describes the conceptual schema. + + var propertyName = link.rel.substring(odataRelatedPrefix.length); + + // undefined is used as a flag that inline data was not found (as opposed to + // found with a value or with null). + var inlineData = undefined; + + // Get any inline data. + xmlChildElements(atomEntryLink, function (child) { + if (child.nsURI === odataMetaXmlNs && child.localName === "inline") { + var inlineRoot = xmlFirstElement(child); + if (inlineRoot) { + inlineData = readAtomDocument(inlineRoot, metadata); + } else { + inlineData = null; + } + } + }); + + // If the link has no inline content, we consider it deferred. + if (inlineData === undefined) { + inlineData = { __deferred: { uri: link.href } }; + } + + // Set the property value on the entry object. + entry[propertyName] = inlineData; + + // Set the extra property information on the entry object metadata. + entry.__metadata.properties = entry.__metadata.properties || {}; + entry.__metadata.properties[propertyName] = { + extensions: link.extensions + }; + }; + + var readAtomEntryStructuralObject = function (propertiesElement, parent, parentMetadata) { + /// Reads an atom entry's property as a structural object and sets its value in the parent and the metadata in the parentMetadata objects. + /// XML element for the 'properties' node. + /// + /// Object that will contain the property value. It can be either an antom entry or + /// an atom complex property object. + /// + /// Object that will contain the property metadata. It can be either an atom entry metadata or a complex property metadata object + + xmlChildElements(propertiesElement, function (property) { + if (property.nsURI === odataXmlNs) { + parentMetadata.properties = parentMetadata.properties || {}; + readAtomEntryProperty(property, parent, parentMetadata.properties); + } + }); + }; + + var readAtomEntryProperty = function (property, parent, metadata) { + /// Reads a property on an ATOM OData entry. + /// XML element for the property. + /// + /// Object that will contain the property value. It can be either an antom entry or + /// an atom complex property object. + /// + /// Metadata for the object that will contain the property value. + + var propertyNullValue = null; + var propertyTypeValue = "Edm.String"; + var propertyExtensions = []; + + xmlAttributes(property, function (attribute) { + if (attribute.nsURI === odataMetaXmlNs) { + switch (attribute.localName) { + case "null": + propertyNullValue = attribute.domNode.nodeValue; + return; + case "type": + propertyTypeValue = attribute.domNode.nodeValue; + return; + }; + } + + if (isExtensionNs(attribute.nsURI)) { + var extension = readAtomExtensionAttribute(attribute); + propertyExtensions.push(extension); + } + }); + + var propertyValue = null; + var propertyMetadata = { + type: propertyTypeValue, + extensions: propertyExtensions + }; + + if (propertyNullValue !== "true") { + propertyValue = xmlInnerText(property.domNode); + if (isPrimitiveType(propertyTypeValue)) { + propertyValue = convertFromAtomPropertyText(propertyValue, propertyTypeValue); + } else { + // Probe for a complex type and read it. + if (xmlFirstElement(property)) { + propertyValue = { __metadata: { type: propertyTypeValue } }; + readAtomEntryStructuralObject(property, propertyValue, propertyMetadata); + } + } + } + + parent[property.localName] = propertyValue; + metadata[property.localName] = propertyMetadata; + }; + + var readAtomServiceDocument = function (atomServiceDoc) { + /// Reads an atom service document + /// An element node that represents the root service element of an AtomPub service document + /// An object that contains the properties of the service document + + // Consider handling Accept and Category elements. + + var serviceDoc = { + workspaces: [], + extensions: [] + }; + + // Find all the workspace elements. + xmlChildElements(atomServiceDoc, function (child) { + if (child.nsURI === appXmlNs && child.localName === "workspace") { + var workspace = readAtomServiceDocumentWorkspace(child); + serviceDoc.workspaces.push(workspace); + } else { + var serviceExtension = readAtomExtensionElement(atomServiceDoc); + serviceDoc.extensions.push(serviceExtension); + } + }); + + // AtomPub (RFC 5023 Section 8.3.1) says a service document MUST contain one or + // more workspaces. Throw if we don't find any. + if (serviceDoc.workspaces.length === 0) { + throw { message: "Invalid AtomPub service document: No workspace element found.", element: atomServiceDoc }; + } + + return serviceDoc; + }; + + var readAtomServiceDocumentWorkspace = function (atomWorkspace) { + /// Reads a single workspace element from an atom service document + /// An element node that represents a workspace element of an AtomPub service document + /// An object that contains the properties of the workspace + + var workspace = { + collections: [], + extensions: [] + }; + + xmlChildElements(atomWorkspace, function (child) { + if (child.nsURI === atomXmlNs) { + if (child.localName === "title") { + if (atomWorkspace.title) { + throw { message: "Invalid AtomPub service document: workspace has more than one child title element", element: child }; + } + + workspace.title = xmlInnerText(child.domNode); + } + } else if (child.nsURI === appXmlNs) { + if (child.localName === "collection") { + var collection = readAtomServiceDocumentCollection(child, workspace); + workspace.collections.push(collection); + } + } else { + var extension = readAtomExtensionElement(atomWorkspace); + workspace.extensions.push(extension); + } + }); + + workspace.title = workspace.title || ""; + + return workspace; + }; + + var readAtomServiceDocumentCollection = function (atomCollection) { + /// Reads a service document collection element into an object. + /// An element node that represents a collection element of an AtomPub service document. + /// An object that contains the properties of the collection. + + var collection = { + href: xmlAttribute(atomCollection, "href"), + extensions: [] + }; + + if (!collection.href) { + throw { message: "Invalid AtomPub service document: collection has no href attribute", element: atomCollection }; + } + + collection.href = normalizeURI(collection.href, atomCollection.baseURI); + + xmlChildElements(atomCollection, function (child) { + if (child.nsURI === atomXmlNs) { + if (child.localName === "title") { + if (collection.title) { + throw { message: "Invalid AtomPub service document: collection has more than one child title element", element: child }; + } + + collection.title = xmlInnerText(child.domNode); + } + } else if (child.nsURI !== appXmlNs) { + var extension = readAtomExtensionElement(atomCollection); + collection.extensions.push(extension); + } + }); + + // AtomPub (RFC 5023 Section 8.3.3) says the collection element MUST contain + // a title element. It's likely to be problematic if the service doc doesn't + // have one so here we throw. + if (!collection.title) { + throw { message: "Invalid AtomPub service document: collection has no title element", element: atomCollection }; + } + + return collection; + }; + + var writeAtomDocument = function (data, context) { + /// Writes the specified data into an OData ATOM document. + /// Data to write. + /// Context used for serialization. + /// The root of the DOM tree built. + + var docRoot; + var type = payloadTypeOf(data); + switch (type) { + case PAYLOADTYPE_FEEDORLINKS: + docRoot = writeAtomFeed(null, data, context); + break; + case PAYLOADTYPE_ENTRY: + // FALLTHROUGH + case PAYLOADTYPE_COMPLEXTYPE: + docRoot = writeAtomEntry(null, data, context); + break; + } + + return docRoot; + }; + + var writeAtomRoot = function (parent, name) { + /// Writes the root of an ATOM document, possibly under an existing element. + /// Element under which to create a new element. + /// Name for the new element, to be created in the ATOM namespace. + /// The created element. + + if (parent) { + return xmlNewElement(parent, name, atomXmlNs); + } + + var result = xmlNewDocument(name, atomXmlNs); + + // Add commonly used namespaces. + // ATOM is implied by the just-created element. + // xmlAddNamespaceAttribute(result.domNode, "xmlns", atomXmlNs); + xmlAddNamespaceAttribute(result.domNode, "xmlns:d", odataXmlNs); + xmlAddNamespaceAttribute(result.domNode, "xmlns:m", odataMetaXmlNs); + + return result; + }; + + var writeAtomFeed = function (parent, data, context) { + /// Writes the specified feed data into an OData ATOM feed. + /// Parent to append feed tree to. + /// Feed data to write. + /// Context used for serialization. + /// The feed element of the DOM tree built. + + var feed = writeAtomRoot(parent, "feed"); + var entries = (isArray(data)) ? data : data.results; + if (entries) { + var i, len; + for (i = 0, len = entries.length; i < len; i++) { + writeAtomEntry(feed, entries[i], context); + } + } + + return feed; + }; + + var writeAtomEntry = function (parent, data, context) { + /// Appends an ATOM entry XML payload to the parent node. + /// Parent element. + /// Data object to write in intermediate format. + /// Context used for serialization. + /// The new entry. + + var entry = writeAtomRoot(parent, "entry"); + + // Set up a default empty author name as required by ATOM. + var author = xmlNewElement(entry, "author", atomXmlNs); + xmlNewElement(author, "name", atomXmlNs); + + // Set up a default empty title as required by ATOM. + xmlNewElement(entry, "title", atomXmlNs); + + var content = xmlNewElement(entry, "content", atomXmlNs); + xmlNewAttribute(content, "type", null, "application/xml"); + + var properties = xmlNewElement(content, propertiesTag, odataMetaXmlNs); + + var propertiesMetadata = (data.__metadata) ? data.__metadata.properties : null; + + writeAtomEntryMetadata(entry, data.__metadata); + writeAtomEntryProperties(entry, properties, data, propertiesMetadata, context); + applyMetadataToEntry(entry, data, context); + + return entry; + }; + + var applyMetadataToEntry = function (entry, data, context) { + /// Applies feed customizations to the specified entry element. + /// Entry to apply feed customizations to. + /// Data object associated with the entry. + /// Context used for serialization. + + if (!data.__metadata) { + return; + } + + var metadata = context.metadata; + var entityType = lookupEntityType(data.__metadata.type, metadata); + while (entityType) { + // Apply all feed customizations from the entity and each of its properties. + var propertyType; + var source = entityType.FC_SourcePath; + if (source) { + propertyType = lookupPropertyType(metadata, entityType, source); + applyEntryCustomizationToEntry(entityType, source, entry, data, propertyType, "", context); + } + + var properties = entityType.property; + if (properties) { + var i, len; + for (i = 0, len = properties.length; i < len; i++) { + var property = properties[i]; + var suffixCounter = 0; + var suffix = ""; + while (property["FC_TargetPath" + suffix]) { + source = property.name; + if (property["FC_SourcePath" + suffix]) { + source += "/" + property["FC_SourcePath" + suffix]; + } + + applyEntryCustomizationToEntry(property, source, entry, data, property.type, suffix, context); + suffixCounter++; + suffix = "_" + suffixCounter; + } + } + } + + // Apply feed customizations from base types. + entityType = lookupEntityType(entityType.baseType, metadata); + } + }; + + var writeAtomEntryMetadata = function (entry, metadata) { + /// Writes the content of metadata into the specified DOM entry element. + /// DOM entry element. + /// Object __metadata to write. + + if (metadata) { + // Write the etag if present. + if (metadata.etag) { + xmlNewAttribute(entry, "etag", odataMetaXmlNs, metadata.etag); + } + + // Write the ID if present. + if (metadata.uri) { + xmlNewElement(entry, "id", atomXmlNs, metadata.uri); + } + + // Write the type name if present. + if (metadata.type) { + var category = xmlNewElement(entry, "category", atomXmlNs); + xmlNewAttribute(category, "term", null, metadata.type); + xmlNewAttribute(category, "scheme", null, odataScheme); + } + } + }; + + var writeAtomEntryLink = function (entry, href, rel) { + /// Writes an ATOM link into an entry. + /// DOM entry element to add link to. + /// Value for href attribute in link element. + /// Value for rel attribute in link element + /// The new link element. + + var link = xmlNewElement(entry, "link", atomXmlNs); + xmlNewAttribute(link, "rel", null, rel); + xmlNewAttribute(link, "href", null, href); + return link; + }; + + var writeAtomEntryProperties = function (entry, parentElement, data, propertiesMetadata, context) { + /// Writes the properties of an entry or complex type. + /// Entry object being written out; null if this is a complex type. + /// Parent DOM element under which the property should be added. + /// Data object to write in intermediate format. + /// Instance metadata about properties of the 'data' object. + /// Context used for serialization. + + var name, value, kind, propertyMetadata; + var contextMetadata = context.metadata; + var dataMetadataType; + for (name in data) { + if (name !== "__metadata") { + value = data[name]; + kind = propertyKindOf(value); + propertyMetadata = (propertiesMetadata) ? propertiesMetadata[name] : null; + if (!propertyMetadata) { + // Look up at-most-once. + if (dataMetadataType === undefined) { + if (data.__metadata) { + dataMetadataType = lookupEntityType(data.__metadata.type, contextMetadata); + } + } + + if (dataMetadataType) { + propertyMetadata = lookupProperty(dataMetadataType.property, name); + if (!propertyMetadata) { + propertyMetadata = lookupProperty(dataMetadataType.navigationProperty, name); + } + } + } + + if (kind === PROPERTYKIND_NONE) { + // This could be a null primitive property, complex type or link; + // look it up in metadata if available, otherwise assume primitive. + kind = PROPERTYKIND_PRIMITIVE; + if (propertyMetadata && !isPrimitiveType(propertyMetadata.type)) { + if (propertyMetadata.relationship) { + kind = PROPERTYKIND_INLINE; + } else if (lookupComplexType(propertyMetadata.type, context.metadata)) { + kind = PROPERTYKIND_COMPLEX; + } + } + } + + if (kind === PROPERTYKIND_PRIMITIVE || kind === PROPERTYKIND_COMPLEX) { + writeAtomEntryProperty(parentElement, name, kind, value, propertyMetadata, context); + } else { + writeAtomEntryDeferredProperty(entry, kind, name, value, context); + } + } + } + }; + + var writeAtomEntryProperty = function (parentElement, name, kind, value, propertiesMetadata, context) { + /// Writes a single property for an entry or complex type. + /// Parent DOM element under which the property should be added. + /// Property name. + /// Property kind description (from propertyKind values). + /// Property value. + /// Instance metadata about properties of the 'data' object. + /// Serialization context. + + var propertyTagName = xmlQualifyXmlTagName(name, "d"); + var propertyType = propertiesMetadata && propertiesMetadata.type; + var property; + if (kind === PROPERTYKIND_COMPLEX) { + property = xmlNewElement(parentElement, propertyTagName, odataXmlNs); + var propertyMetadata; + if (propertiesMetadata) { + propertyMetadata = propertiesMetadata.properties; + } + + // Null complex types require a V2 payload. + if (value === null) { + context.dataServiceVersion = "2.0"; + } + + writeAtomEntryProperties(null, property, value, propertyMetadata, context); + } else { + // Default the conversion to string if no property type has been defined. + property = xmlNewElement(parentElement, propertyTagName, odataXmlNs, convertToAtomPropertyText(value, propertyType || "Edm.String")); + } + + if (value === null) { + xmlNewAttribute(property, propertyNullAttribute, odataMetaXmlNs, "true"); + } + + if (propertyType) { + xmlNewAttribute(property, propertyTypeAttribute, odataMetaXmlNs, propertyType); + } + }; + + var writeAtomEntryDeferredProperty = function (entry, kind, name, value, context) { + /// Writes a single property for an entry or complex type. + /// Entry object being written out. + /// Property name. + /// Property kind description (from propertyKind values). + /// Property value. + /// Serialization context. + /// entry cannot be null because that would indicate a complex type, which don't support links. + + var href; + var inlineWriter; + var inlineType; + var inlineContent = kind === PROPERTYKIND_INLINE; + if (inlineContent) { + href = (value && value.__metadata) ? value.__metadata.uri : ""; + inlineType = payloadTypeOf(value); + switch (inlineType) { + case PAYLOADTYPE_ENTRY: + inlineWriter = writeAtomEntry; + break; + case PAYLOADTYPE_FEEDORLINKS: + inlineType = "feed"; + inlineWriter = writeAtomFeed; + break; + case PAYLOADTYPE_NONE: + // Only for a null entry, serialized as a link with an empty m:inline value. + inlineType = PAYLOADTYPE_ENTRY; + inlineWriter = null; + break; + default: + throw { message: "Invalid payload for inline navigation property: " + inlineType }; + } + } else { + href = value.__deferred.uri; + } + + var rel = normalizeURI(name, odataRelatedPrefix); + var link = writeAtomEntryLink(entry, href, rel); + if (inlineContent) { + var inlineRoot = xmlNewElement(link, inlineTag, odataMetaXmlNs); + xmlNewAttribute(link, "type", null, "application/atom+xml;type=" + inlineType); + if (inlineWriter) { + inlineWriter(inlineRoot, value, context); + } + } + }; + + var atomParser = function (handler, text, context) { + /// Parses an ATOM document (feed, entry or service document). + /// This handler. + /// Document text. + /// Object with parsing context. + /// An object representation of the document; undefined if not applicable. + + if (text) { + var atomRoot = xmlParse(text); + if (atomRoot) { + return readAtomDocument(atomRoot, context.metadata); + } + } + }; + + var atomSerializer = function (handler, data, context) { + /// Serializes an ATOM object into a document (feed or entry). + /// This handler. + /// Representation of feed or entry. + /// Object with parsing context. + /// An text representation of the data object; undefined if not applicable. + + var cType = context.contentType = context.contentType || contentType(atomMediaType); + var result = undefined; + if (cType && cType.mediaType === atomMediaType) { + var atomDoc = writeAtomDocument(data, context); + result = xmlSerialize(atomDoc); + } + + return result; + }; + + odata.atomHandler = handler(atomParser, atomSerializer, atomAcceptTypes.join(","), MAX_DATA_SERVICE_VERSION); + + + + // It's assumed that all elements may have Documentation children and Annotation elements. + // See http://msdn.microsoft.com/en-us/library/bb399292.aspx for a CSDL reference. + var schema = { + elements: { + Association: { + attributes: ["Name"], + elements: ["End*", "ReferentialConstraint"] + }, + AssociationSet: { + attributes: ["Name", "Association"], + elements: ["End*"] + }, + CollectionType: { + attributes: ["ElementType", "Nullable", "DefaultValue", "MaxLength", "FixedLength", "Precision", "Scale", "Unicode", "Collation"] + }, + ComplexType: { + attributes: ["Name", "BaseType", "Abstract"], + elements: ["Property*"] + }, + DefiningExpression: { + text: true + }, + Dependent: { + attributes: ["Role"], + elements: ["PropertyRef*"] + }, + Documentation: { + text: true + }, + End: { + attributes: ["Type", "Role", "Multiplicity", "EntitySet"], + elements: ["OnDelete"] + }, + EntityContainer: { + attributes: ["Name", "Extends"], + elements: ["EntitySet*", "AssociationSet*", "FunctionImport*"] + }, + EntitySet: { + attributes: ["Name", "EntityType"] + }, + EntityType: { + attributes: ["Name", "BaseType", "Abstract", "OpenType"], + elements: ["Key", "Property*", "NavigationProperty*"] + }, + Function: { + attributes: ["Name", "ReturnType"], + elements: ["Parameter*", "DefiningExpression", "ReturnType"] + }, + FunctionImport: { + attributes: ["Name", "ReturnType", "EntitySet"], + elements: ["Parameter*"] + }, + Key: { + elements: ["PropertyRef*"] + }, + NavigationProperty: { + attributes: ["Name", "Relationship", "ToRole", "FromRole"] + }, + OnDelete: { + attributes: ["Action"] + }, + Parameter: { + attributes: ["Name", "Type", "Mode", "MaxLength", "Precision", "Scale"] + }, + Principal: { + attributes: ["Role"], + elements: ["PropertyRef*"] + }, + Property: { + attributes: ["Name", "Type", "Nullable", "DefaultValue", "MaxLength", "FixedLength", "Precision", "Scale", "Unicode", "Collation", "ConcurrencyMode"] + }, + PropertyRef: { + attributes: ["Name"] + }, + ReferenceType: { + attributes: ["Type"] + }, + ReferentialConstraint: { + elements: ["Principal", "Dependent"] + }, + ReturnType: { + attributes: ["ReturnType"], + elements: ["CollectionType", "ReferenceType", "RowType"] + }, + RowType: { + elements: ["Property*"] + }, + Schema: { + attributes: ["Namespace", "Alias"], + elements: ["Using*", "EntityContainer*", "EntityType*", "Association*", "ComplexType*", "Function*"] + }, + TypeRef: { + attributes: ["Type", "Nullable", "DefaultValue", "MaxLength", "FixedLength", "Precision", "Scale", "Unicode", "Collation"] + }, + Using: { + attributes: ["Namespace", "Alias"] + } + } + }; + + // See http://msdn.microsoft.com/en-us/library/ee373839.aspx for a feed customization reference. + var customizationAttributes = ["m:FC_ContentKind", "m:FC_KeepInContent", "m:FC_NsPrefix", "m:FC_NsUri", "m:FC_SourcePath", "m:FC_TargetPath"]; + schema.elements.Property.attributes = schema.elements.Property.attributes.concat(customizationAttributes); + schema.elements.EntityType.attributes = schema.elements.EntityType.attributes.concat(customizationAttributes); + + // See http://msdn.microsoft.com/en-us/library/dd541284(PROT.10).aspx for an EDMX reference. + schema.elements.Edmx = { attributes: ["Version"], elements: ["DataServices"], ns: edmxNs }; + schema.elements.DataServices = { elements: ["Schema*"], ns: edmxNs }; + + // See http://msdn.microsoft.com/en-us/library/dd541233(v=PROT.10) for Conceptual Schema Definition Language Document for Data Services. + schema.elements.EntityContainer.attributes.push("m:IsDefaultEntityContainer"); + schema.elements.Property.attributes.push("m:MimeType"); + schema.elements.FunctionImport.attributes.push("m:HttpMethod"); + schema.elements.EntityType.attributes.push("m:HasStream"); + schema.elements.DataServices.attributes = ["m:DataServiceVersion", "m:MaxDataServiceVersion"]; + + var scriptCase = function (text) { + /// Converts a Pascal-case identifier into a camel-case identifier. + /// Text to convert. + /// Converted text. + /// If the text starts with multiple uppercase characters, it is left as-is. + + if (!text) { + return text; + } + + if (text.length > 1) { + var firstTwo = text.substr(0, 2); + if (firstTwo === firstTwo.toUpperCase()) { + return text; + } + + return text.charAt(0).toLowerCase() + text.substr(1); + } + + return text.charAt(0).toLowerCase(); + }; + + var getChildSchema = function (parentSchema, candidateName) { + /// Gets the schema node for the specified element. + /// Schema of the parent XML node of 'element'. + /// XML element name to consider. + /// The schema that describes the specified element; null if not found. + + if (candidateName === "Documentation") { + return { isArray: true, propertyName: "documentation" }; + } + + var elements = parentSchema.elements; + if (!elements) { + return null; + } + + var i, len; + for (i = 0, len = elements.length; i < len; i++) { + var elementName = elements[i]; + var multipleElements = false; + if (elementName.charAt(elementName.length - 1) === "*") { + multipleElements = true; + elementName = elementName.substr(0, elementName.length - 1); + } + + if (candidateName === elementName) { + var propertyName = scriptCase(elementName); + return { isArray: multipleElements, propertyName: propertyName }; + } + } + + return null; + }; + + // This regular expression is used to detect a feed customization element + // after we've normalized it into the 'm' prefix. It starts with m:FC_, + // followed by other characters, and ends with _ and a number. + // The captures are 0 - whole string, 1 - name as it appears in internal table. + var isFeedCustomizationNameRE = /^(m:FC_.*)_[0-9]+$/; + + var isEdmNamespace = function (nsURI) { + /// Checks whether the specifies namespace URI is one of the known CSDL namespace URIs. + /// Namespace URI to check. + /// true if nsURI is a known CSDL namespace; false otherwise. + + return nsURI === edmNs || nsURI === edmNs2 || nsURI === edmNs3 || nsURI === edmNs4; + }; + + var parseConceptualModelElement = function (element) { + /// Parses a CSDL document. + /// DOM element to parse. + /// An object describing the parsed element. + + if (!element.domNode) { + element = xml_wrapNode(element, ""); + } + + var localName = element.localName; + var elementSchema = schema.elements[localName]; + if (!elementSchema) { + return null; + } + + if (elementSchema.ns) { + if (element.nsURI !== elementSchema.ns) { + return null; + } + } else if (!isEdmNamespace(element.nsURI)) { + return null; + } + + var item = {}; + var extensions = []; + var attributes = elementSchema.attributes || []; + xmlAttributes(element, function (wrappedAttribute) { + var node = wrappedAttribute.domNode; + var localName = wrappedAttribute.localName; + var namespaceURI = wrappedAttribute.nsURI; + var value = node.value; + + // Don't do anything with xmlns attributes. + if (namespaceURI === xmlnsNS) { + return; + } + + // Currently, only m: for metadata is supported as a prefix in the internal schema table, + // un-prefixed element names imply one a CSDL element. + var schemaName = null; + var handled = false; + if (isEdmNamespace(namespaceURI) || namespaceURI === null) { + schemaName = ""; + } else if (namespaceURI === odataMetaXmlNs) { + schemaName = "m:"; + } + + if (schemaName !== null) { + schemaName += localName; + + // Feed customizations for complex types have additional + // attributes with a suffixed counter starting at '1', so + // take that into account when doing the lookup. + var match = isFeedCustomizationNameRE.exec(schemaName); + if (match) { + schemaName = match[1]; + } + + if (contains(attributes, schemaName)) { + handled = true; + item[scriptCase(localName)] = value; + } + } + + if (!handled) { + extensions.push(createAttributeExtension(wrappedAttribute)); + } + }); + + xmlChildElements(element, function (child) { + var childSchema = getChildSchema(elementSchema, child.localName); + if (childSchema) { + if (childSchema.isArray) { + var arr = item[childSchema.propertyName]; + if (!arr) { + arr = []; + item[childSchema.propertyName] = arr; + } + arr.push(parseConceptualModelElement(child)); + } else { + item[childSchema.propertyName] = parseConceptualModelElement(child); + } + } else { + extensions.push(createElementExtension(child)); + } + }); + + if (elementSchema.text) { + item.text = xmlInnerText(element); + } + + if (extensions.length) { + item.extensions = extensions; + } + + return item; + }; + + var metadataParser = function (handler, text) { + /// Parses a metadata document. + /// This handler. + /// Metadata text. + /// An object representation of the conceptual model. + + var doc = xmlParse(text); + return parseConceptualModelElement(doc) || undefined; + }; + + odata.metadataHandler = handler(metadataParser, null, xmlMediaType, MAX_DATA_SERVICE_VERSION); + + + + var jsonAcceptTypes = ["application/json", "application/json;odata=verbose"]; + var jsonMediaType = contentType(jsonAcceptTypes[0]); + var jsonVerboseMediaType = contentType(jsonAcceptTypes[1]); + + + var normalizeServiceDocument = function (data, baseURI) { + /// Normalizes a JSON service document to look like an ATOM service document. + /// Object representation of service documents as deserialized. + /// Base URI to resolve relative URIs. + /// An object representation of the service document. + var workspace = { collections: [] }; + + var i, len; + for (i = 0, len = data.EntitySets.length; i < len; i++) { + var title = data.EntitySets[i]; + var collection = { + title: title, + href: normalizeURI(title, baseURI) + }; + + workspace.collections.push(collection); + } + + return { workspaces: [workspace] }; + }; + + // The regular expression corresponds to something like this: + // /Date(123+60)/ + // + // This first number is date ticks, the + may be a - and is optional, + // with the second number indicating a timezone offset in minutes. + // + // On the wire, the leading and trailing forward slashes are + // escaped without being required to so the chance of collisions is reduced; + // however, by the time we see the objects, the characters already + // look like regular forward slashes. + var jsonDateRE = /^\/Date\((-?\d+)(\+|-)?(\d+)?\)\/$/; + + var minutesToOffset = function (minutes) { + /// Formats the given minutes into (+/-)hh:mm format. + /// Number of minutes to format. + /// The minutes in (+/-)hh:mm format. + + var sign; + if (minutes < 0) { + sign = "-"; + minutes = -minutes; + } else { + sign = "+"; + } + + var hours = Math.floor(minutes / 60); + minutes = minutes - (60 * hours); + + return sign + formatNumberWidth(hours, 2) + ":" + formatNumberWidth(minutes, 2); + }; + + var parseJsonDateString = function (value) { + /// Parses the JSON Date representation into a Date object. + /// String value. + /// A Date object if the value matches one; falsy otherwise. + + var arr = value && jsonDateRE.exec(value); + if (arr) { + // 0 - complete results; 1 - ticks; 2 - sign; 3 - minutes + var result = new Date(parseInt10(arr[1])); + if (arr[2]) { + var mins = parseInt10(arr[3]); + if (arr[2] === "-") { + mins = -mins; + } + + // The offset is reversed to get back the UTC date, which is + // what the API will eventually have. + var current = result.getUTCMinutes(); + result.setUTCMinutes(current - mins); + result.__edmType = "Edm.DateTimeOffset"; + result.__offset = minutesToOffset(mins); + } + if (!isNaN(result.valueOf())) { + return result; + } + } + + // Allow undefined to be returned. + }; + + // Some JSON implementations cannot produce the character sequence \/ + // which is needed to format DateTime and DateTimeOffset into the + // JSON string representation defined by the OData protocol. + // See the history of this file for a candidate implementation of + // a 'formatJsonDateString' function. + + var traverseInternal = function (item, callback) { + /// Traverses a tree of objects invoking callback for every value. + /// Object or array to traverse. + /// + /// Callback function with key and value, similar to JSON.parse reviver. + /// + /// The object with traversed properties. + /// Unlike the JSON reviver, this won't delete null members. + + if (item && typeof item === "object") { + for (var name in item) { + var value = item[name]; + var result = traverseInternal(value, callback); + result = callback(name, result); + if (result !== value) { + if (value === undefined) { + delete item[name]; + } else { + item[name] = result; + } + } + } + } + + return item; + }; + + var traverse = function (item, callback) { + /// Traverses a tree of objects invoking callback for every value. + /// Object or array to traverse. + /// + /// Callback function with key and value, similar to JSON.parse reviver. + /// + /// The traversed object. + /// Unlike the JSON reviver, this won't delete null members. + + return callback("", traverseInternal(item, callback)); + }; + + var jsonParser = function (handler, text, context) { + /// Parses a JSON OData payload. + /// This handler. + /// Payload text (this parser also handles pre-parsed objects). + /// Object with parsing context. + /// An object representation of the OData payload. + + var metadata = context.metadata; + var recognizeDates = defined(context.context ? context.context.recognizeDates : undefined, handler.recognizeDates); + + var json = (typeof text === "string") ? window.JSON.parse(text) : text; + json = traverse(json, function (_, value) { + if (value && typeof value === "object") { + var dataTypeName = value.__metadata && value.__metadata.type; + var dataType = lookupEntityType(dataTypeName, metadata) || lookupComplexType(dataTypeName, metadata); + + if (dataType) { + var properties = dataType.property; + if (properties) { + var i, len; + for (i = 0, len = properties.length; i < len; i++) { + var property = properties[i]; + var propertyName = property.name; + var propertyValue = value[propertyName]; + + if (property.type === "Edm.DateTime" || property.type === "Edm.DateTimeOffset") { + if (propertyValue) { + propertyValue = parseJsonDateString(propertyValue); + if (!propertyValue) { + throw { message: "Invalid date/time value" }; + } + value[propertyName] = propertyValue; + } + } else if (property.type === "Edm.Time") { + value[propertyName] = parseDuration(propertyValue); + } + } + } + } else if (recognizeDates) { + for (var name in value) { + propertyValue = value[name]; + if (typeof propertyValue === "string") { + value[name] = parseJsonDateString(propertyValue) || propertyValue; + } + } + } + } + return value; + }).d; + + json = jsonUpdateDataFromVersion(json, context.dataServiceVersion); + json = jsonNormalizeData(json, context.response.requestUri); + + return json; + }; + + var jsonSerializer = function (handler, data, context) { + /// Serializes the data by returning its string representation. + /// This handler. + /// Data to serialize. + /// Object with serialization context. + /// The string representation of data. + + var result = undefined; + var cType = context.contentType = context.contentType || jsonVerboseMediaType; + if (cType && cType.mediaType === jsonMediaType.mediaType) { + var json = data; + + // Save the current date.toJSON function + var dateToJSON = Date.prototype.toJSON; + try { + // Set our own date.toJSON function + Date.prototype.toJSON = function () { + return formatDateTimeOffset(this); + }; + result = window.JSON.stringify(json, jsonReplacer); + } finally { + // Restore the original toJSON function + Date.prototype.toJSON = dateToJSON; + } + } + return result; + }; + + var jsonReplacer = function (_, value) { + /// JSON replacer function for converting a value to its JSON representation. + /// Value to convert. + /// JSON representation of the input value. + /// + /// This method is used during JSON serialization and invoked only by the JSON.stringify function. + /// It should never be called directly. + /// + + if (value && value.__edmType === "Edm.Time") { + return formatDuration(value); + } else { + return value; + } + }; + + var jsonNormalizeData = function (data, baseURI) { + /// + /// Normalizes the specified data into an intermediate representation. + /// like the latest supported version. + /// + /// Data to update. + /// URI to use as the base for normalizing references. + + if (payloadTypeOf(data) === PAYLOADTYPE_SVCDOC) { + return normalizeServiceDocument(data, baseURI); + } else { + return data; + } + }; + + var jsonUpdateDataFromVersion = function (data, dataVersion) { + /// + /// Updates the specified data in the specified version to look + /// like the latest supported version. + /// + /// Data to update. + /// Version the data is in (possibly unknown). + + // Strip the trailing comma if there. + if (dataVersion && dataVersion.lastIndexOf(";") === dataVersion.length - 1) { + dataVersion = dataVersion.substr(0, dataVersion.length - 1); + } + + if (!dataVersion) { + // Try to detect whether this is an array, in which case it + // should probably be a feed structure - indicates V1 behavior. + if (isArray(data)) { + dataVersion = "1.0"; + } + } + + if (dataVersion === "1.0") { + if (isArray(data)) { + data = { results: data }; + } + } + + return data; + }; + + odata.jsonHandler = handler(jsonParser, jsonSerializer, jsonAcceptTypes.join(","), MAX_DATA_SERVICE_VERSION); + odata.jsonHandler.recognizeDates = false; + + + var batchMediaType = "multipart/mixed"; + var responseStatusRegex = /^HTTP\/1\.\d (\d{3}) (.*)$/i; + var responseHeaderRegex = /^([^()<>@,;:\\"\/[\]?={} \t]+)\s?:\s?(.*)/; + + var hex16 = function () { + /// + /// Calculates a random 16 bit number and returns it in hexadecimal format. + /// + /// A 16-bit number in hex format. + + return Math.floor((1 + Math.random()) * 0x10000).toString(16).substr(1); + }; + + var createBoundary = function (prefix) { + /// + /// Creates a string that can be used as a multipart request boundary. + /// + /// String to use as the start of the boundary string + /// Boundary string of the format: -- + + return prefix + hex16() + "-" + hex16() + "-" + hex16(); + }; + + var partHandler = function (context) { + /// + /// Gets the handler for data serialization of individual requests / responses in a batch. + /// + /// Context used for data serialization. + /// Handler object. + + return context.handler.partHandler; + }; + + var currentBoundary = function (context) { + /// + /// Gets the current boundary used for parsing the body of a multipart response. + /// + /// Context used for parsing a multipart response. + /// Boundary string. + + var boundaries = context.boundaries; + return boundaries[boundaries.length - 1]; + }; + + var batchParser = function (handler, text, context) { + /// Parses a batch response. + /// This handler. + /// Batch text. + /// Object with parsing context. + /// An object representation of the batch. + + var boundary = context.contentType.properties["boundary"]; + return { __batchResponses: readBatch(text, { boundaries: [boundary], handlerContext: context }) }; + }; + + var batchSerializer = function (handler, data, context) { + /// Serializes a batch object representation into text. + /// This handler. + /// Representation of a batch. + /// Object with parsing context. + /// An text representation of the batch object; undefined if not applicable. + + var cType = context.contentType = context.contentType || contentType(batchMediaType); + if (cType.mediaType === batchMediaType) { + return writeBatch(data, context); + } + }; + + var readBatch = function (text, context) { + /// + /// Parses a multipart/mixed response body from from the position defined by the context. + /// + /// Body of the multipart/mixed response. + /// Context used for parsing. + /// Array of objects representing the individual responses. + + var delimiter = "--" + currentBoundary(context); + + // Move beyond the delimiter and read the complete batch + readTo(text, context, delimiter); + + // Ignore the incoming line + readLine(text, context); + + // Read the batch parts + var responses = []; + var partEnd; + + while (partEnd !== "--" && context.position < text.length) { + var partHeaders = readHeaders(text, context); + var partContentType = contentType(partHeaders["Content-Type"]); + + if (partContentType && partContentType.mediaType === batchMediaType) { + context.boundaries.push(partContentType.properties["boundary"]); + try { + var changeResponses = readBatch(text, context); + } catch (e) { + e.response = readResponse(text, context, delimiter); + changeResponses = [e]; + } + responses.push({ __changeResponses: changeResponses }); + context.boundaries.pop(); + readTo(text, context, "--" + currentBoundary(context)); + } else { + if (!partContentType || partContentType.mediaType !== "application/http") { + throw { message: "invalid MIME part type " }; + } + // Skip empty line + readLine(text, context); + // Read the response + var response = readResponse(text, context, delimiter); + try { + if (response.statusCode >= 200 && response.statusCode <= 299) { + partHandler(context.handlerContext).read(response, context.handlerContext); + } else { + // Keep track of failed responses and continue processing the batch. + response = { message: "HTTP request failed", response: response }; + } + } catch (e) { + response = e; + } + + responses.push(response); + } + + partEnd = text.substr(context.position, 2); + + // Ignore the incoming line. + readLine(text, context); + } + return responses; + }; + + var readHeaders = function (text, context) { + /// + /// Parses the http headers in the text from the position defined by the context. + /// + /// Text containing an http response's headers + /// Context used for parsing. + /// Object containing the headers as key value pairs. + /// + /// This function doesn't support split headers and it will stop reading when it hits two consecutive line breaks. + /// + + var headers = {}; + var parts; + var line; + var pos; + + do { + pos = context.position; + line = readLine(text, context); + parts = responseHeaderRegex.exec(line); + if (parts !== null) { + headers[parts[1]] = parts[2]; + } else { + // Whatever was found is not a header, so reset the context position. + context.position = pos; + } + } while (line && parts); + + normalizeHeaders(headers); + + return headers; + }; + + var readResponse = function (text, context, delimiter) { + /// + /// Parses an HTTP response. + /// + /// Text representing the http response. + /// Context used for parsing. + /// String used as delimiter of the multipart response parts. + /// Object representing the http response. + + // Read the status line. + var pos = context.position; + var match = responseStatusRegex.exec(readLine(text, context)); + + var statusCode; + var statusText; + var headers; + + if (match) { + statusCode = match[1]; + statusText = match[2]; + headers = readHeaders(text, context); + readLine(text, context); + } else { + context.position = pos; + } + + return { + statusCode: statusCode, + statusText: statusText, + headers: headers, + body: readTo(text, context, "\r\n" + delimiter) + }; + }; + + var readLine = function (text, context) { + /// + /// Returns a substring from the position defined by the context up to the next line break (CRLF). + /// + /// Input string. + /// Context used for reading the input string. + /// Substring to the first ocurrence of a line break or null if none can be found. + + return readTo(text, context, "\r\n"); + }; + + var readTo = function (text, context, str) { + /// + /// Returns a substring from the position given by the context up to value defined by the str parameter and increments the position in the context. + /// + /// Input string. + /// Context used for reading the input string. + /// Substring to read up to. + /// Substring to the first ocurrence of str or the end of the input string if str is not specified. Null if the marker is not found. + + var start = context.position || 0; + var end = text.length; + if (str) { + end = text.indexOf(str, start); + if (end === -1) { + return null; + } + context.position = end + str.length; + } else { + context.position = end; + } + + return text.substring(start, end); + }; + + var writeBatch = function (data, context) { + /// + /// Serializes a batch request object to a string. + /// + /// Batch request object in payload representation format + /// Context used for the serialization + /// String representing the batch request + + var type = payloadTypeOf(data); + if (type !== PAYLOADTYPE_BATCH) { + throw { message: "Serialization of batches of type \"" + type + "\" is not supported" }; + } + + var batchBoundary = createBoundary("batch_"); + var batchParts = data.__batchRequests; + var batch = ""; + var i, len; + for (i = 0, len = batchParts.length; i < len; i++) { + batch += writeBatchPartDelimiter(batchBoundary, false) + + writeBatchPart(batchParts[i], context); + } + batch += writeBatchPartDelimiter(batchBoundary, true); + + // Register the boundary with the request content type. + var contentTypeProperties = context.contentType.properties; + contentTypeProperties.boundary = batchBoundary; + + return batch; + }; + + var writeBatchPartDelimiter = function (boundary, close) { + /// + /// Creates the delimiter that indicates that start or end of an individual request. + /// + /// Boundary string used to indicate the start of the request + /// Flag indicating that a close delimiter string should be generated + /// Delimiter string + + var result = "\r\n--" + boundary; + if (close) { + result += "--"; + } + + return result + "\r\n"; + }; + + var writeBatchPart = function (part, context, nested) { + /// + /// Serializes a part of a batch request to a string. A part can be either a GET request or + /// a change set grouping several CUD (create, update, delete) requests. + /// + /// Request or change set object in payload representation format + /// Object containing context information used for the serialization + /// Flag indicating that the part is nested inside a change set + /// String representing the serialized part + /// + /// A change set is an array of request objects and they cannot be nested inside other change sets. + /// + + var changeSet = part.__changeRequests; + var result; + if (isArray(changeSet)) { + if (nested) { + throw { message: "Not Supported: change set nested in other change set" }; + } + + var changeSetBoundary = createBoundary("changeset_"); + result = "Content-Type: " + batchMediaType + "; boundary=" + changeSetBoundary + "\r\n"; + var i, len; + for (i = 0, len = changeSet.length; i < len; i++) { + result += writeBatchPartDelimiter(changeSetBoundary, false) + + writeBatchPart(changeSet[i], context, true); + } + + result += writeBatchPartDelimiter(changeSetBoundary, true); + } else { + result = "Content-Type: application/http\r\nContent-Transfer-Encoding: binary\r\n\r\n"; + prepareRequest(part, partHandler(context), { metadata: context.metadata }); + result += writeRequest(part); + } + + return result; + }; + + var writeRequest = function (request) { + /// + /// Serializes a request object to a string. + /// + /// Request object to serialize + /// String representing the serialized request + + var result = (request.method ? request.method : "GET") + " " + request.requestUri + " HTTP/1.1\r\n"; + for (var name in request.headers) { + if (request.headers[name]) { + result = result + name + ": " + request.headers[name] + "\r\n"; + } + } + + result += "\r\n"; + + if (request.body) { + result += request.body; + } + + return result; + }; + + odata.batchHandler = handler(batchParser, batchSerializer, batchMediaType, MAX_DATA_SERVICE_VERSION); + + + + var handlers = [odata.jsonHandler, odata.atomHandler, odata.xmlHandler, odata.textHandler]; + + var dispatchHandler = function (handlerMethod, requestOrResponse, context) { + /// Dispatches an operation to handlers. + /// Name of handler method to invoke. + /// request/response argument for delegated call. + /// context argument for delegated call. + + var i, len; + for (i = 0, len = handlers.length; i < len && !handlers[i][handlerMethod](requestOrResponse, context) ; i++) { + } + + if (i === len) { + throw { message: "no handler for data" }; + } + }; + + odata.defaultSuccess = function (data) { + /// Default success handler for OData. + /// Data to process. + + window.alert(window.JSON.stringify(data)); + }; + + odata.defaultError = throwErrorCallback; + + odata.defaultHandler = { + read: function (response, context) { + /// Reads the body of the specified response by delegating to JSON and ATOM handlers. + /// Response object. + /// Operation context. + + if (response && assigned(response.body) && response.headers["Content-Type"]) { + dispatchHandler("read", response, context); + } + }, + + write: function (request, context) { + /// Write the body of the specified request by delegating to JSON and ATOM handlers. + /// Reques tobject. + /// Operation context. + + dispatchHandler("write", request, context); + }, + + maxDataServiceVersion: MAX_DATA_SERVICE_VERSION, + accept: "application/atomsvc+xml;q=0.8, application/json;odata=verbose;q=0.5, */*;q=0.1" + }; + + odata.defaultMetadata = []; + + odata.read = function (urlOrRequest, success, error, handler, httpClient, metadata) { + /// Reads data from the specified URL. + /// URL to read data from. + /// Callback for a successful read operation. + /// Callback for handling errors. + /// Handler for data serialization. + /// HTTP client layer. + /// Conceptual metadata for this request. + + var request; + if (urlOrRequest instanceof String || typeof urlOrRequest === "string") { + request = { requestUri: urlOrRequest }; + } else { + request = urlOrRequest; + } + + return odata.request(request, success, error, handler, httpClient, metadata); + }; + + odata.request = function (request, success, error, handler, httpClient, metadata) { + /// Sends a request containing OData payload to a server. + /// Object that represents the request to be sent. + /// Callback for a successful read operation. + /// Callback for handling errors. + /// Handler for data serialization. + /// HTTP client layer. + /// Conceptual metadata for this request. + + if (!success) { + success = odata.defaultSuccess; + } + + if (!error) { + error = odata.defaultError; + } + + if (!handler) { + handler = odata.defaultHandler; + } + + if (!httpClient) { + httpClient = odata.defaultHttpClient; + } + + if (!metadata) { + metadata = odata.defaultMetadata; + } + + // Augment the request with additional defaults. + request.recognizeDates = defined(request.recognizeDates, odata.jsonHandler.recognizeDates); + request.callbackParameterName = defined(request.callbackParameterName, odata.defaultHttpClient.callbackParameterName); + request.formatQueryString = defined(request.formatQueryString, odata.defaultHttpClient.formatQueryString); + request.enableJsonpCallback = defined(request.enableJsonpCallback, odata.defaultHttpClient.enableJsonpCallback); + + // Create the base context for read/write operations, also specifying complete settings. + var context = { + metadata: metadata, + recognizeDates: request.recognizeDates, + callbackParameterName: request.callbackParameterName, + formatQueryString: request.formatQueryString, + enableJsonpCallback: request.enableJsonpCallback + }; + + try { + prepareRequest(request, handler, context); + return invokeRequest(request, success, error, handler, httpClient, context); + } catch (err) { + error(err); + } + }; + + // Configure the batch handler to use the default handler for the batch parts. + odata.batchHandler.partHandler = odata.defaultHandler; + + + + var localStorage = window.localStorage; + + var domStoreDateToJSON = function () { + /// Converts a Date object into an object representation friendly to JSON serialization. + /// Object that represents the Date. + /// + /// This method is used to override the Date.toJSON method and is called only by + /// JSON.stringify. It should never be called directly. + /// + + var newValue = { v: this.valueOf(), t: "[object Date]" }; + // Date objects might have extra properties on them so we save them. + for (var name in this) { + newValue[name] = this[name]; + } + return newValue; + }; + + var domStoreJSONToDate = function (_, value) { + /// JSON reviver function for converting an object representing a Date in a JSON stream to a Date object + /// Object to convert. + /// Date object. + /// + /// This method is used during JSON parsing and invoked only by the reviver function. + /// It should never be called directly. + /// + + if (value && value.t === "[object Date]") { + var newValue = new Date(value.v); + for (var name in value) { + if (name !== "t" && name !== "v") { + newValue[name] = value[name]; + } + } + value = newValue; + } + return value; + }; + + var qualifyDomStoreKey = function (store, key) { + /// Qualifies the key with the name of the store. + /// Store object whose name will be used for qualifying the key. + /// Key string. + /// Fully qualified key string. + + return store.name + "#!#" + key; + }; + + var unqualifyDomStoreKey = function (store, key) { + /// Gets the key part of a fully qualified key string. + /// Store object whose name will be used for qualifying the key. + /// Fully qualified key string. + /// Key part string + + return key.replace(store.name + "#!#", ""); + }; + + var DomStore = function (name) { + /// Constructor for store objects that use DOM storage as the underlying mechanism. + /// Store name. + this.name = name; + }; + + DomStore.create = function (name) { + /// Creates a store object that uses DOM Storage as its underlying mechanism. + /// Store name. + /// Store object. + + if (DomStore.isSupported()) { + return new DomStore(name); + } + + throw { message: "Web Storage not supported by the browser" }; + }; + + DomStore.isSupported = function () { + /// Checks whether the underlying mechanism for this kind of store objects is supported by the browser. + /// True if the mechanism is supported by the browser; otherwise false. + return !!localStorage; + }; + + DomStore.prototype.add = function (key, value, success, error) { + /// Adds a new value identified by a key to the store. + /// Key string. + /// Value that is going to be added to the store. + /// Callback for a successful add operation. + /// Callback for handling errors. If not specified then store.defaultError is invoked. + /// + /// This method errors out if the store already contains the specified key. + /// + + error = error || this.defaultError; + var store = this; + this.contains(key, function (contained) { + if (!contained) { + store.addOrUpdate(key, value, success, error); + } else { + delay(error, { message: "key already exists", key: key }); + } + }, error); + }; + + DomStore.prototype.addOrUpdate = function (key, value, success, error) { + /// Adds or updates a value identified by a key to the store. + /// Key string. + /// Value that is going to be added or updated to the store. + /// Callback for a successful add or update operation. + /// Callback for handling errors. If not specified then store.defaultError is invoked. + /// + /// This method will overwrite the key's current value if it already exists in the store; otherwise it simply adds the new key and value. + /// + + error = error || this.defaultError; + + if (key instanceof Array) { + error({ message: "Array of keys not supported" }); + } else { + var fullKey = qualifyDomStoreKey(this, key); + var oldDateToJSON = Date.prototype.toJSON; + try { + var storedValue = value; + if (storedValue !== undefined) { + // Dehydrate using json + Date.prototype.toJSON = domStoreDateToJSON; + storedValue = window.JSON.stringify(value); + } + // Save the json string. + localStorage.setItem(fullKey, storedValue); + delay(success, key, value); + } + catch (e) { + if (e.code === 22 || e.number === 0x8007000E) { + delay(error, { name: "QUOTA_EXCEEDED_ERR", error: e }); + } else { + delay(error, e); + } + } + finally { + Date.prototype.toJSON = oldDateToJSON; + } + } + }; + + DomStore.prototype.clear = function (success, error) { + /// Removes all the data associated with this store object. + /// Callback for a successful clear operation. + /// Callback for handling errors. If not specified then store.defaultError is invoked. + /// + /// In case of an error, this method will not restore any keys that might have been deleted at that point. + /// + + error = error || this.defaultError; + try { + var i = 0, len = localStorage.length; + while (len > 0 && i < len) { + var fullKey = localStorage.key(i); + var key = unqualifyDomStoreKey(this, fullKey); + if (fullKey !== key) { + localStorage.removeItem(fullKey); + len = localStorage.length; + } else { + i++; + } + }; + delay(success); + } + catch (e) { + delay(error, e); + } + }; + + DomStore.prototype.close = function () { + /// This function does nothing in DomStore as it does not have a connection model + }; + + DomStore.prototype.contains = function (key, success, error) { + /// Checks whether a key exists in the store. + /// Key string. + /// Callback indicating whether the store contains the key or not. + /// Callback for handling errors. If not specified then store.defaultError is invoked. + error = error || this.defaultError; + try { + var fullKey = qualifyDomStoreKey(this, key); + var value = localStorage.getItem(fullKey); + delay(success, value !== null); + } catch (e) { + delay(error, e); + } + }; + + DomStore.prototype.defaultError = throwErrorCallback; + + DomStore.prototype.getAllKeys = function (success, error) { + /// Gets all the keys that exist in the store. + /// Callback for a successful get operation. + /// Callback for handling errors. If not specified then store.defaultError is invoked. + + error = error || this.defaultError; + + var results = []; + var i, len; + + try { + for (i = 0, len = localStorage.length; i < len; i++) { + var fullKey = localStorage.key(i); + var key = unqualifyDomStoreKey(this, fullKey); + if (fullKey !== key) { + results.push(key); + } + } + delay(success, results); + } + catch (e) { + delay(error, e); + } + }; + + /// Identifies the underlying mechanism used by the store. + DomStore.prototype.mechanism = "dom"; + + DomStore.prototype.read = function (key, success, error) { + /// Reads the value associated to a key in the store. + /// Key string. + /// Callback for a successful reads operation. + /// Callback for handling errors. If not specified then store.defaultError is invoked. + error = error || this.defaultError; + + if (key instanceof Array) { + error({ message: "Array of keys not supported" }); + } else { + try { + var fullKey = qualifyDomStoreKey(this, key); + var value = localStorage.getItem(fullKey); + if (value !== null && value !== "undefined") { + // Hydrate using json + value = window.JSON.parse(value, domStoreJSONToDate); + } + else { + value = undefined; + } + delay(success, key, value); + } catch (e) { + delay(error, e); + } + } + }; + + DomStore.prototype.remove = function (key, success, error) { + /// Removes a key and its value from the store. + /// Key string. + /// Callback for a successful remove operation. + /// Callback for handling errors. If not specified then store.defaultError is invoked. + error = error || this.defaultError; + + if (key instanceof Array) { + error({ message: "Batches not supported" }); + } else { + try { + var fullKey = qualifyDomStoreKey(this, key); + localStorage.removeItem(fullKey); + delay(success); + } catch (e) { + delay(error, e); + } + } + }; + + DomStore.prototype.update = function (key, value, success, error) { + /// Updates the value associated to a key in the store. + /// Key string. + /// New value. + /// Callback for a successful update operation. + /// Callback for handling errors. If not specified then store.defaultError is invoked. + /// + /// This method errors out if the specified key is not found in the store. + /// + + error = error || this.defaultError; + var store = this; + this.contains(key, function (contained) { + if (contained) { + store.addOrUpdate(key, value, success, error); + } else { + delay(error, { message: "key not found", key: key }); + } + }, error); + }; + + + + var mozIndexedDB = window.mozIndexedDB; + + var IDBTransaction = window.IDBTransaction; + var IDBKeyRange = window.IDBKeyRange; + + var getError = function (error, defaultError) { + /// Returns either a specific error handler or the default error handler + /// The specific error handler + /// The default error handler + /// The error callback + return function (e) { + if (e.code === 11 /* IndexedDb disk quota exceeded */) { + e = { name: "QUOTA_EXCEEDED_ERR", error: e }; + } + + if (error) { + error(e); + } else if (defaultError) { + defaultError(e); + } + }; + }; + + var openTransaction = function (store, mode, success, error) { + /// Opens a new transaction to the store + /// The store object + /// The read/write mode of the transaction (constants from IDBTransaction) + /// The success callback + /// The error callback + var name = store.name; + var db = store.db; + var errorCallback = getError(error, store.defaultError); + + if (db) { + success(db.transaction(name, mode)); + } else { + var request = mozIndexedDB.open("_datajs_" + name); + request.onsuccess = function (event) { + db = store.db = event.target.result; + if (!db.objectStoreNames.contains(name)) { + var versionRequest = db.setVersion("1.0"); + versionRequest.onsuccess = function () { + db.createObjectStore(name, null, false); + success(db.transaction(name, mode)); + }; + versionRequest.onerror = errorCallback; + versionRequest.onblocked = errorCallback; + } else { + success(db.transaction(name, mode)); + } + }; + request.onerror = getError(error, this.defaultError); + } + }; + + var IndexedDBStore = function (name) { + /// Creates a new IndexedDBStore. + /// The name of the store. + /// The new IndexedDBStore. + this.name = name; + }; + + IndexedDBStore.create = function (name) { + /// Creates a new IndexedDBStore. + /// The name of the store. + /// The new IndexedDBStore. + if (IndexedDBStore.isSupported()) { + return new IndexedDBStore(name); + } + + throw { message: "IndexedDB is not supported on this browser" }; + }; + + IndexedDBStore.isSupported = function () { + /// Returns whether IndexedDB is supported. + /// True if IndexedDB is supported, false otherwise. + return !!mozIndexedDB; + }; + + IndexedDBStore.prototype.add = function (key, value, success, error) { + /// Adds a key/value pair to the store + /// The key + /// The value + /// The success callback + /// The error callback + var name = this.name; + var defaultError = this.defaultError; + var keys = []; + var values = []; + + if (key instanceof Array) { + keys = key; + values = value; + } else { + keys = [key]; + values = [value]; + } + + openTransaction(this, IDBTransaction.READ_WRITE, function (transaction) { + transaction.onabort = getError(error, defaultError); + transaction.oncomplete = function () { + if (key instanceof Array) { + success(keys, values); + } else { + success(key, value); + } + }; + + for (var i = 0; i < keys.length && i < values.length; i++) { + transaction.objectStore(name).add(values[i], keys[i]); + } + }, error); + }; + + IndexedDBStore.prototype.addOrUpdate = function (key, value, success, error) { + /// Adds or updates a key/value pair in the store + /// The key + /// The value + /// The success callback + /// The error callback + var name = this.name; + var defaultError = this.defaultError; + var keys = []; + var values = []; + + if (key instanceof Array) { + keys = key; + values = value; + } else { + keys = [key]; + values = [value]; + } + + openTransaction(this, IDBTransaction.READ_WRITE, function (transaction) { + transaction.onabort = getError(error, defaultError); + transaction.oncomplete = function () { + if (key instanceof Array) { + success(keys, values); + } else { + success(key, value); + } + }; + + for (var i = 0; i < keys.length && i < values.length; i++) { + transaction.objectStore(name).put(values[i], keys[i]); + } + }, error); + }; + + IndexedDBStore.prototype.clear = function (success, error) { + /// Clears the store + /// The success callback + /// The error callback + var name = this.name; + var defaultError = this.defaultError; + openTransaction(this, IDBTransaction.READ_WRITE, function (transaction) { + transaction.onerror = getError(error, defaultError); + transaction.oncomplete = function () { + success(); + }; + + transaction.objectStore(name).clear(); + }, error); + }; + + IndexedDBStore.prototype.close = function () { + /// Closes the connection to the database + if (this.db) { + this.db.close(); + this.db = null; + } + }; + + IndexedDBStore.prototype.contains = function (key, success, error) { + /// Returns whether the store contains a key + /// The key + /// The success callback + /// The error callback + var name = this.name; + var defaultError = this.defaultError; + openTransaction(this, IDBTransaction.READ_ONLY, function (transaction) { + var request = transaction.objectStore(name).openCursor(IDBKeyRange.only(key)); + transaction.oncomplete = function () { + success(request.result !== undefined); + }; + transaction.onerror = getError(error, defaultError); + }, error); + }; + + IndexedDBStore.prototype.defaultError = throwErrorCallback; + + IndexedDBStore.prototype.getAllKeys = function (success, error) { + /// Gets all the keys from the store + /// The success callback + /// The error callback + var name = this.name; + var defaultError = this.defaultError; + openTransaction(this, IDBTransaction.READ_ONLY, function (transaction) { + var results = []; + + transaction.oncomplete = function () { + success(results); + }; + + var request = transaction.objectStore(name).openCursor(); + + request.onerror = getError(error, defaultError); + request.onsuccess = function (event) { + var cursor = event.target.result; + if (cursor) { + results.push(cursor.key); + // Some tools have issues because continue is a javascript reserved word. + cursor["continue"].call(cursor); + } + }; + }, error); + }; + + /// Identifies the underlying mechanism used by the store. + IndexedDBStore.prototype.mechanism = "indexeddb"; + + IndexedDBStore.prototype.read = function (key, success, error) { + /// Reads the value for the specified key + /// The key + /// The success callback + /// The error callback + /// If the key does not exist, the success handler will be called with value = undefined + var name = this.name; + var defaultError = this.defaultError; + var keys = (key instanceof Array) ? key : [key]; + + openTransaction(this, IDBTransaction.READ_WRITE, function (transaction) { + var values = []; + + transaction.onerror = getError(error, defaultError); + transaction.oncomplete = function () { + if (key instanceof Array) { + success(keys, values); + } else { + success(keys[0], values[0]); + } + }; + + for (var i = 0; i < keys.length; i++) { + // Some tools have issues because get is a javascript reserved word. + var objectStore = transaction.objectStore(name); + var request = objectStore["get"].call(objectStore, keys[i]); + request.onsuccess = function (event) { + values.push(event.target.result); + }; + } + }, error); + }; + + IndexedDBStore.prototype.remove = function (key, success, error) { + /// Removes the specified from the store + /// The key + /// The success callback + /// The error callback + var name = this.name; + var defaultError = this.defaultError; + var keys = (key instanceof Array) ? key : [key]; + + openTransaction(this, IDBTransaction.READ_WRITE, function (transaction) { + transaction.onerror = getError(error, defaultError); + transaction.oncomplete = function () { + success(); + }; + + for (var i = 0; i < keys.length; i++) { + // Some tools have issues because continue is a javascript reserved word. + var objectStore = transaction.objectStore(name); + objectStore["delete"].call(objectStore, keys[i]); + } + }, error); + }; + + IndexedDBStore.prototype.update = function (key, value, success, error) { + /// Updates a key/value pair in the store + /// The key + /// The value + /// The success callback + /// The error callback + var name = this.name; + var defaultError = this.defaultError; + var keys = []; + var values = []; + + if (key instanceof Array) { + keys = key; + values = value; + } else { + keys = [key]; + values = [value]; + } + + openTransaction(this, IDBTransaction.READ_WRITE, function (transaction) { + transaction.onabort = getError(error, defaultError); + transaction.oncomplete = function () { + if (key instanceof Array) { + success(keys, values); + } else { + success(key, value); + } + }; + + for (var i = 0; i < keys.length && i < values.length; i++) { + var request = transaction.objectStore(name).openCursor(IDBKeyRange.only(keys[i])); + request.pair = { key: keys[i], value: values[i] }; + request.onsuccess = function (event) { + var cursor = event.target.result; + if (cursor) { + cursor.update(event.target.pair.value); + } else { + transaction.abort(); + } + }; + } + }, error); + }; + + + + var MemoryStore = function (name) { + /// Constructor for store objects that use a sorted array as the underlying mechanism. + /// Store name. + + var holes = []; + var items = []; + var keys = {}; + + this.name = name; + + var getErrorCallback = function (error) { + return error || this.defaultError; + }; + + var validateKeyInput = function (key, error) { + /// Validates that the specified key is not undefined, not null, and not an array + /// Key value. + /// Error callback. + /// True if the key is valid. False if the key is invalid and the error callback has been queued for execution. + + var messageString; + + if (key instanceof Array) { + messageString = "Array of keys not supported"; + } + + if (key === undefined || key === null) { + messageString = "Invalid key"; + } + + if (messageString) { + delay(error, { message: messageString }); + return false; + } + return true; + }; + + this.add = function (key, value, success, error) { + /// Adds a new value identified by a key to the store. + /// Key string. + /// Value that is going to be added to the store. + /// Callback for a successful add operation. + /// Callback for handling errors. If not specified then store.defaultError is invoked. + /// + /// This method errors out if the store already contains the specified key. + /// + + error = getErrorCallback(error); + + if (validateKeyInput(key, error)) { + if (!keys.hasOwnProperty(key)) { + this.addOrUpdate(key, value, success, error); + } else { + error({ message: "key already exists", key: key }); + } + } + }; + + this.addOrUpdate = function (key, value, success, error) { + /// Adds or updates a value identified by a key to the store. + /// Key string. + /// Value that is going to be added or updated to the store. + /// Callback for a successful add or update operation. + /// Callback for handling errors. If not specified then store.defaultError is invoked. + /// + /// This method will overwrite the key's current value if it already exists in the store; otherwise it simply adds the new key and value. + /// + + error = getErrorCallback(error); + + if (validateKeyInput(key, error)) { + var index = keys[key]; + if (index === undefined) { + if (holes.length > 0) { + index = holes.splice(0, 1); + } else { + index = items.length; + } + } + items[index] = value; + keys[key] = index; + delay(success, key, value); + } + }; + + this.clear = function (success) { + /// Removes all the data associated with this store object. + /// Callback for a successful clear operation. + + items = []; + keys = {}; + holes = []; + + delay(success); + }; + + this.contains = function (key, success) { + /// Checks whether a key exists in the store. + /// Key string. + /// Callback indicating whether the store contains the key or not. + + var contained = keys.hasOwnProperty(key); + delay(success, contained); + }; + + this.getAllKeys = function (success) { + /// Gets all the keys that exist in the store. + /// Callback for a successful get operation. + + var results = []; + for (var name in keys) { + results.push(name); + } + delay(success, results); + }; + + this.read = function (key, success, error) { + /// Reads the value associated to a key in the store. + /// Key string. + /// Callback for a successful reads operation. + /// Callback for handling errors. If not specified then store.defaultError is invoked. + error = getErrorCallback(error); + + if (validateKeyInput(key, error)) { + var index = keys[key]; + delay(success, key, items[index]); + } + }; + + this.remove = function (key, success, error) { + /// Removes a key and its value from the store. + /// Key string. + /// Callback for a successful remove operation. + /// Callback for handling errors. If not specified then store.defaultError is invoked. + error = getErrorCallback(error); + + if (validateKeyInput(key, error)) { + var index = keys[key]; + if (index !== undefined) { + if (index === items.length - 1) { + items.pop(); + } else { + items[index] = undefined; + holes.push(index); + } + delete keys[key]; + + // The last item was removed, no need to keep track of any holes in the array. + if (items.length === 0) { + holes = []; + } + } + + delay(success); + } + }; + + this.update = function (key, value, success, error) { + /// Updates the value associated to a key in the store. + /// Key string. + /// New value. + /// Callback for a successful update operation. + /// Callback for handling errors. If not specified then store.defaultError is invoked. + /// + /// This method errors out if the specified key is not found in the store. + /// + + error = getErrorCallback(error); + if (validateKeyInput(key, error)) { + if (keys.hasOwnProperty(key)) { + this.addOrUpdate(key, value, success, error); + } else { + error({ message: "key not found", key: key }); + } + } + }; + }; + + MemoryStore.create = function (name) { + /// Creates a store object that uses memory storage as its underlying mechanism. + /// Store name. + /// Store object. + return new MemoryStore(name); + }; + + MemoryStore.isSupported = function () { + /// Checks whether the underlying mechanism for this kind of store objects is supported by the browser. + /// True if the mechanism is supported by the browser; otherwise false. + return true; + }; + + MemoryStore.prototype.close = function () { + /// This function does nothing in MemoryStore as it does not have a connection model. + }; + + MemoryStore.prototype.defaultError = throwErrorCallback; + + /// Identifies the underlying mechanism used by the store. + MemoryStore.prototype.mechanism = "memory"; + + + + var mechanisms = { + indexeddb: IndexedDBStore, + dom: DomStore, + memory: MemoryStore + }; + + datajs.defaultStoreMechanism = "best"; + + datajs.createStore = function (name, mechanism) { + /// Creates a new store object. + /// Store name. + /// A specific mechanism to use (defaults to best, can be "best", "dom", "indexeddb", "webdb"). + /// Store object. + + if (!mechanism) { + mechanism = datajs.defaultStoreMechanism; + } + + if (mechanism === "best") { + mechanism = (DomStore.isSupported()) ? "dom" : "memory"; + } + + var factory = mechanisms[mechanism]; + if (factory) { + return factory.create(name); + } + + throw { message: "Failed to create store", name: name, mechanism: mechanism }; + }; + + + + + var forwardCall = function (thisValue, name, returnValue) { + /// Creates a new function to forward a call. + /// Value to use as the 'this' object. + /// Name of function to forward to. + /// Return value for the forward call (helps keep identity when chaining calls). + /// A new function that will forward a call. + + return function () { + thisValue[name].apply(thisValue, arguments); + return returnValue; + }; + }; + + var DjsDeferred = function () { + /// Initializes a new DjsDeferred object. + /// + /// Compability Note A - Ordering of callbacks through chained 'then' invocations + /// + /// The Wiki entry at http://wiki.commonjs.org/wiki/Promises/A + /// implies that .then() returns a distinct object. + //// + /// For compatibility with http://api.jquery.com/category/deferred-object/ + /// we return this same object. This affects ordering, as + /// the jQuery version will fire callbacks in registration + /// order regardless of whether they occur on the result + /// or the original object. + /// + /// Compability Note B - Fulfillment value + /// + /// The Wiki entry at http://wiki.commonjs.org/wiki/Promises/A + /// implies that the result of a success callback is the + /// fulfillment value of the object and is received by + /// other success callbacks that are chained. + /// + /// For compatibility with http://api.jquery.com/category/deferred-object/ + /// we disregard this value instead. + /// + + this._arguments = undefined; + this._done = undefined; + this._fail = undefined; + this._resolved = false; + this._rejected = false; + }; + + DjsDeferred.prototype = { + then: function (fulfilledHandler, errorHandler /*, progressHandler */) { + /// Adds success and error callbacks for this deferred object. + /// Success callback. + /// Error callback. + /// See Compatibility Note A. + + if (fulfilledHandler) { + if (!this._done) { + this._done = [fulfilledHandler]; + } else { + this._done.push(fulfilledHandler); + } + } + + if (errorHandler) { + if (!this._fail) { + this._fail = [errorHandler]; + } else { + this._fail.push(errorHandler); + } + } + + //// See Compatibility Note A in the DjsDeferred constructor. + //// if (!this._next) { + //// this._next = createDeferred(); + //// } + //// return this._next.promise(); + + if (this._resolved) { + this.resolve.apply(this, this._arguments); + } else if (this._rejected) { + this.reject.apply(this, this._arguments); + } + + return this; + }, + + resolve: function (/* args */) { + /// Invokes success callbacks for this deferred object. + /// All arguments are forwarded to success callbacks. + + + if (this._done) { + var i, len; + for (i = 0, len = this._done.length; i < len; i++) { + //// See Compability Note B - Fulfillment value. + //// var nextValue = + this._done[i].apply(null, arguments); + } + + //// See Compatibility Note A in the DjsDeferred constructor. + //// this._next.resolve(nextValue); + //// delete this._next; + + this._done = undefined; + this._resolved = false; + this._arguments = undefined; + } else { + this._resolved = true; + this._arguments = arguments; + } + }, + + reject: function (/* args */) { + /// Invokes error callbacks for this deferred object. + /// All arguments are forwarded to error callbacks. + if (this._fail) { + var i, len; + for (i = 0, len = this._fail.length; i < len; i++) { + this._fail[i].apply(null, arguments); + } + + this._fail = undefined; + this._rejected = false; + this._arguments = undefined; + } else { + this._rejected = true; + this._arguments = arguments; + } + }, + + promise: function () { + /// Returns a version of this object that has only the read-only methods available. + /// An object with only the promise object. + + var result = {}; + result.then = forwardCall(this, "then", result); + return result; + } + }; + + var createDeferred = function () { + /// Creates a deferred object. + /// + /// A new deferred object. If jQuery is installed, then a jQuery + /// Deferred object is returned, which provides a superset of features. + /// + + if (window.jQuery && window.jQuery.Deferred) { + return new window.jQuery.Deferred(); + } else { + return new DjsDeferred(); + } + }; + + + + var appendQueryOption = function (uri, queryOption) { + /// Appends the specified escaped query option to the specified URI. + /// URI to append option to. + /// Escaped query option to append. + var separator = (uri.indexOf("?") >= 0) ? "&" : "?"; + return uri + separator + queryOption; + }; + + var appendSegment = function (uri, segment) { + /// Appends the specified segment to the given URI. + /// URI to append a segment to. + /// Segment to append. + /// The original URI with a new segment appended. + + var index = uri.indexOf("?"); + var queryPortion = ""; + if (index >= 0) { + queryPortion = uri.substr(index); + uri = uri.substr(0, index); + } + + if (uri[uri.length - 1] !== "/") { + uri += "/"; + } + return uri + segment + queryPortion; + }; + + var buildODataRequest = function (uri, options) { + /// Builds a request object to GET the specified URI. + /// URI for request. + /// Additional options. + + return { + method: "GET", + requestUri: uri, + user: options.user, + password: options.password, + enableJsonpCallback: options.enableJsonpCallback, + callbackParameterName: options.callbackParameterName, + formatQueryString: options.formatQueryString + }; + }; + + var findQueryOptionStart = function (uri, name) { + /// Finds the index where the value of a query option starts. + /// URI to search in. + /// Name to look for. + /// The index where the query option starts. + + var result = -1; + var queryIndex = uri.indexOf("?"); + if (queryIndex !== -1) { + var start = uri.indexOf("?" + name + "=", queryIndex); + if (start === -1) { + start = uri.indexOf("&" + name + "=", queryIndex); + } + if (start !== -1) { + result = start + name.length + 2; + } + } + return result; + }; + + var queryForData = function (uri, options, success, error) { + /// Gets data from an OData service. + /// URI to the OData service. + /// Object with additional well-known request options. + /// Success callback. + /// Error callback. + /// Object with an abort method. + + var request = queryForDataInternal(uri, options, [], success, error); + return request; + }; + + var queryForDataInternal = function (uri, options, data, success, error) { + /// Gets data from an OData service taking into consideration server side paging. + /// URI to the OData service. + /// Object with additional well-known request options. + /// Array that stores the data provided by the OData service. + /// Success callback. + /// Error callback. + /// Object with an abort method. + + var request = buildODataRequest(uri, options); + var currentRequest = odata.request(request, function (newData) { + var next = newData.__next; + var results = newData.results; + + data = data.concat(results); + + if (next) { + currentRequest = queryForDataInternal(next, options, data, success, error); + } else { + success(data); + } + }, error, undefined, options.httpClient, options.metadata); + + return { + abort: function () { + currentRequest.abort(); + } + }; + }; + + var ODataCacheSource = function (options) { + /// Creates a data cache source object for requesting data from an OData service. + /// Options for the cache data source. + /// A new data cache source instance. + + var that = this; + var uri = options.source; + + that.identifier = normalizeURICase(encodeURI(decodeURI(uri))); + that.options = options; + + that.count = function (success, error) { + /// Gets the number of items in the collection. + /// Success callback with the item count. + /// Error callback. + /// Request object with an abort method./ + + var options = that.options; + return odata.request( + buildODataRequest(appendSegment(uri, "$count"), options), + function (data) { + var count = parseInt10(data.toString()); + if (isNaN(count)) { + error({ message: "Count is NaN", count: count }); + } else { + success(count); + } + }, error, undefined, options.httpClient, options.metadata); + }; + + that.read = function (index, count, success, error) { + /// Gets a number of consecutive items from the collection. + /// Zero-based index of the items to retrieve. + /// Number of items to retrieve. + /// Success callback with the requested items. + /// Error callback. + /// Request object with an abort method./ + + var queryOptions = "$skip=" + index + "&$top=" + count; + return queryForData(appendQueryOption(uri, queryOptions), that.options, success, error); + }; + + return that; + }; + + + + var appendPage = function (operation, page) { + /// Appends a page's data to the operation data. + /// Operation with (i)ndex, (c)ount and (d)ata. + /// Page with (i)ndex, (c)ount and (d)ata. + + var intersection = intersectRanges(operation, page); + if (intersection) { + var start = intersection.i - page.i; + var end = start + (operation.c - operation.d.length); + operation.d = operation.d.concat(page.d.slice(start, end)); + } + }; + + var intersectRanges = function (x, y) { + /// Returns the {(i)ndex, (c)ount} range for the intersection of x and y. + /// Range with (i)ndex and (c)ount members. + /// Range with (i)ndex and (c)ount members. + /// The intersection (i)ndex and (c)ount; undefined if there is no intersection. + + var xLast = x.i + x.c; + var yLast = y.i + y.c; + var resultIndex = (x.i > y.i) ? x.i : y.i; + var resultLast = (xLast < yLast) ? xLast : yLast; + var result; + if (resultLast >= resultIndex) { + result = { i: resultIndex, c: resultLast - resultIndex }; + } + + return result; + }; + + var checkZeroGreater = function (val, name) { + /// Checks whether val is a defined number with value zero or greater. + /// Value to check. + /// Parameter name to use in exception. + + if (val === undefined || typeof val !== "number") { + throw { message: "'" + name + "' must be a number." }; + } + + if (isNaN(val) || val < 0 || !isFinite(val)) { + throw { message: "'" + name + "' must be greater than or equal to zero." }; + } + }; + + var checkUndefinedGreaterThanZero = function (val, name) { + /// Checks whether val is undefined or a number with value greater than zero. + /// Value to check. + /// Parameter name to use in exception. + + if (val !== undefined) { + if (typeof val !== "number") { + throw { message: "'" + name + "' must be a number." }; + } + + if (isNaN(val) || val <= 0 || !isFinite(val)) { + throw { message: "'" + name + "' must be greater than zero." }; + } + } + }; + + var checkUndefinedOrNumber = function (val, name) { + /// Checks whether val is undefined or a number + /// Value to check. + /// Parameter name to use in exception. + if (val !== undefined && (typeof val !== "number" || isNaN(val) || !isFinite(val))) { + throw { message: "'" + name + "' must be a number." }; + } + }; + + var removeFromArray = function (arr, item) { + /// Performs a linear search on the specified array and removes the first instance of 'item'. + /// Array to search. + /// Item being sought. + /// Whether the item was removed. + + var i, len; + for (i = 0, len = arr.length; i < len; i++) { + if (arr[i] === item) { + arr.splice(i, 1); + return true; + } + } + + return false; + }; + + var extend = function (target, values) { + /// Extends the target with the specified values. + /// Object to add properties to. + /// Object with properties to add into target. + /// The target object. + + for (var name in values) { + target[name] = values[name]; + } + + return target; + }; + + var estimateSize = function (obj) { + /// Estimates the size of an object in bytes. + /// Object to determine the size of. + /// Estimated size of the object in bytes. + var size = 0; + var type = typeof obj; + + if (type === "object" && obj) { + for (var name in obj) { + size += name.length * 2 + estimateSize(obj[name]); + } + } else if (type === "string") { + size = obj.length * 2; + } else { + size = 8; + } + return size; + }; + + var snapToPageBoundaries = function (lowIndex, highIndex, pageSize) { + /// Snaps low and high indices into page sizes and returns a range. + /// Low index to snap to a lower value. + /// High index to snap to a higher value. + /// Page size to snap to. + /// A range with (i)ndex and (c)ount of elements. + + lowIndex = Math.floor(lowIndex / pageSize) * pageSize; + highIndex = Math.ceil((highIndex + 1) / pageSize) * pageSize; + return { i: lowIndex, c: highIndex - lowIndex }; + }; + + // The DataCache is implemented using state machines. The following constants are used to properly + // identify and label the states that these machines transition to. + + // DataCache state constants + + var CACHE_STATE_DESTROY = "destroy"; + var CACHE_STATE_IDLE = "idle"; + var CACHE_STATE_INIT = "init"; + var CACHE_STATE_READ = "read"; + var CACHE_STATE_PREFETCH = "prefetch"; + var CACHE_STATE_WRITE = "write"; + + // DataCacheOperation state machine states. + // Transitions on operations also depend on the cache current of the cache. + + var OPERATION_STATE_CANCEL = "cancel"; + var OPERATION_STATE_END = "end"; + var OPERATION_STATE_ERROR = "error"; + var OPERATION_STATE_START = "start"; + var OPERATION_STATE_WAIT = "wait"; + + // Destroy state machine states + + var DESTROY_STATE_CLEAR = "clear"; + + // Read / Prefetch state machine states + + var READ_STATE_DONE = "done"; + var READ_STATE_LOCAL = "local"; + var READ_STATE_SAVE = "save"; + var READ_STATE_SOURCE = "source"; + + var DataCacheOperation = function (stateMachine, promise, isCancelable, index, count, data, pending) { + /// Creates a new operation object. + /// State machine that describes the specific behavior of the operation. + /// Promise for requested values. + /// Whether this operation can be canceled or not. + /// Index of first item requested. + /// Count of items requested. + /// Array with the items requested by the operation. + /// Total number of pending prefetch records. + /// A new data cache operation instance. + + /// Promise for requested values. + /// Index of first item requested. + /// Count of items requested. + /// Array with the items requested by the operation. + /// Current state of the operation. + /// Whether the operation has been canceled. + /// Total number of pending prefetch records. + /// Callback executed when the operation reaches the end state. + + var stateData; + var cacheState; + var that = this; + + that.p = promise; + that.i = index; + that.c = count; + that.d = data; + that.s = OPERATION_STATE_START; + + that.canceled = false; + that.pending = pending; + that.oncomplete = null; + + that.cancel = function () { + /// Transitions this operation to the cancel state and sets the canceled flag to true. + /// The function is a no-op if the operation is non-cancelable. + + if (!isCancelable) { + return; + } + + var state = that.s; + if (state !== OPERATION_STATE_ERROR && state !== OPERATION_STATE_END && state !== OPERATION_STATE_CANCEL) { + that.canceled = true; + transition(OPERATION_STATE_CANCEL, stateData); + } + }; + + that.complete = function () { + /// Transitions this operation to the end state. + + transition(OPERATION_STATE_END, stateData); + }; + + that.error = function (err) { + /// Transitions this operation to the error state. + if (!that.canceled) { + transition(OPERATION_STATE_ERROR, err); + } + }; + + that.run = function (state) { + /// Executes the operation's current state in the context of a new cache state. + /// New cache state. + + cacheState = state; + that.transition(that.s, stateData); + }; + + that.wait = function (data) { + /// Transitions this operation to the wait state. + + transition(OPERATION_STATE_WAIT, data); + }; + + var operationStateMachine = function (opTargetState, cacheState, data) { + /// State machine that describes all operations common behavior. + /// Operation state to transition to. + /// Current cache state. + /// Additional data passed to the state. + + switch (opTargetState) { + case OPERATION_STATE_START: + // Initial state of the operation. The operation will remain in this state until the cache has been fully initialized. + if (cacheState !== CACHE_STATE_INIT) { + stateMachine(that, opTargetState, cacheState, data); + } + break; + + case OPERATION_STATE_WAIT: + // Wait state indicating that the operation is active but waiting for an asynchronous operation to complete. + stateMachine(that, opTargetState, cacheState, data); + break; + + case OPERATION_STATE_CANCEL: + // Cancel state. + stateMachine(that, opTargetState, cacheState, data); + that.fireCanceled(); + transition(OPERATION_STATE_END); + break; + + case OPERATION_STATE_ERROR: + // Error state. Data is expected to be an object detailing the error condition. + stateMachine(that, opTargetState, cacheState, data); + that.canceled = true; + that.fireRejected(data); + transition(OPERATION_STATE_END); + break; + + case OPERATION_STATE_END: + // Final state of the operation. + if (that.oncomplete) { + that.oncomplete(that); + } + if (!that.canceled) { + that.fireResolved(); + } + stateMachine(that, opTargetState, cacheState, data); + break; + + default: + // Any other state is passed down to the state machine describing the operation's specific behavior. + stateMachine(that, opTargetState, cacheState, data); + break; + } + }; + + var transition = function (state, data) { + /// Transitions this operation to a new state. + /// State to transition the operation to. + /// Additional data passed to the state. + + that.s = state; + stateData = data; + operationStateMachine(state, cacheState, data); + }; + + that.transition = transition; + + return that; + }; + + DataCacheOperation.prototype.fireResolved = function () { + /// Fires a resolved notification as necessary. + + // Fire the resolve just once. + var p = this.p; + if (p) { + this.p = null; + p.resolve(this.d); + } + }; + + DataCacheOperation.prototype.fireRejected = function (reason) { + /// Fires a rejected notification as necessary. + + // Fire the rejection just once. + var p = this.p; + if (p) { + this.p = null; + p.reject(reason); + } + }; + + DataCacheOperation.prototype.fireCanceled = function () { + /// Fires a canceled notification as necessary. + + this.fireRejected({ canceled: true, message: "Operation canceled" }); + }; + + + var DataCache = function (options) { + /// Creates a data cache for a collection that is efficiently loaded on-demand. + /// + /// Options for the data cache, including name, source, pageSize, + /// prefetchSize, cacheSize, storage mechanism, and initial prefetch and local-data handler. + /// + /// A new data cache instance. + + var state = CACHE_STATE_INIT; + var stats = { counts: 0, netReads: 0, prefetches: 0, cacheReads: 0 }; + + var clearOperations = []; + var readOperations = []; + var prefetchOperations = []; + + var actualCacheSize = 0; // Actual cache size in bytes. + var allDataLocal = false; // Whether all data is local. + var cacheSize = undefinedDefault(options.cacheSize, 1048576); // Requested cache size in bytes, default 1 MB. + var collectionCount = 0; // Number of elements in the server collection. + var highestSavedPage = 0; // Highest index of all the saved pages. + var highestSavedPageSize = 0; // Item count of the saved page with the highest index. + var overflowed = cacheSize === 0; // If the cache has overflowed (actualCacheSize > cacheSize or cacheSize == 0); + var pageSize = undefinedDefault(options.pageSize, 50); // Number of elements to store per page. + var prefetchSize = undefinedDefault(options.prefetchSize, pageSize); // Number of elements to prefetch from the source when the cache is idling. + var version = "1.0"; + var cacheFailure; + + var pendingOperations = 0; + + var source = options.source; + if (typeof source === "string") { + // Create a new cache source. + source = new ODataCacheSource(options); + } + source.options = options; + + // Create a cache local store. + var store = datajs.createStore(options.name, options.mechanism); + + var that = this; + + that.onidle = options.idle; + that.stats = stats; + + that.count = function () { + /// Counts the number of items in the collection. + /// A promise with the number of items. + + if (cacheFailure) { + throw cacheFailure; + } + + var deferred = createDeferred(); + var canceled = false; + + if (allDataLocal) { + delay(function () { + deferred.resolve(collectionCount); + }); + + return deferred.promise(); + } + + // TODO: Consider returning the local data count instead once allDataLocal flag is set to true. + var request = source.count(function (count) { + request = null; + stats.counts++; + deferred.resolve(count); + }, function (err) { + request = null; + deferred.reject(extend(err, { canceled: canceled })); + }); + + return extend(deferred.promise(), { + cancel: function () { + /// Aborts the count operation. + if (request) { + canceled = true; + request.abort(); + request = null; + } + } + }); + }; + + that.clear = function () { + /// Cancels all running operations and clears all local data associated with this cache. + /// + /// New read requests made while a clear operation is in progress will not be canceled. + /// Instead they will be queued for execution once the operation is completed. + /// + /// A promise that has no value and can't be canceled. + + if (cacheFailure) { + throw cacheFailure; + } + + if (clearOperations.length === 0) { + var deferred = createDeferred(); + var op = new DataCacheOperation(destroyStateMachine, deferred, false); + queueAndStart(op, clearOperations); + return deferred.promise(); + } + return clearOperations[0].p; + }; + + that.filterForward = function (index, count, predicate) { + /// Filters the cache data based a predicate. + /// The index of the item to start filtering forward from. + /// Maximum number of items to include in the result. + /// Callback function returning a boolean that determines whether an item should be included in the result or not. + /// + /// Specifying a negative count value will yield all the items in the cache that satisfy the predicate. + /// + /// A promise for an array of results. + return filter(index, count, predicate, false); + }; + + that.filterBack = function (index, count, predicate) { + /// Filters the cache data based a predicate. + /// The index of the item to start filtering backward from. + /// Maximum number of items to include in the result. + /// Callback function returning a boolean that determines whether an item should be included in the result or not. + /// + /// Specifying a negative count value will yield all the items in the cache that satisfy the predicate. + /// + /// A promise for an array of results. + return filter(index, count, predicate, true); + }; + + that.readRange = function (index, count) { + /// Reads a range of adjacent records. + /// Zero-based index of record range to read. + /// Number of records in the range. + /// + /// New read requests made while a clear operation is in progress will not be canceled. + /// Instead they will be queued for execution once the operation is completed. + /// + /// + /// A promise for an array of records; less records may be returned if the + /// end of the collection is found. + /// + + checkZeroGreater(index, "index"); + checkZeroGreater(count, "count"); + + if (cacheFailure) { + throw cacheFailure; + } + + var deferred = createDeferred(); + + // Merging read operations would be a nice optimization here. + var op = new DataCacheOperation(readStateMachine, deferred, true, index, count, [], 0); + queueAndStart(op, readOperations); + + return extend(deferred.promise(), { + cancel: function () { + /// Aborts the readRange operation. + op.cancel(); + } + }); + }; + + that.ToObservable = that.toObservable = function () { + /// Creates an Observable object that enumerates all the cache contents. + /// A new Observable object that enumerates all the cache contents. + if (!window.Rx || !window.Rx.Observable) { + throw { message: "Rx library not available - include rx.js" }; + } + + if (cacheFailure) { + throw cacheFailure; + } + + return window.Rx.Observable.CreateWithDisposable(function (obs) { + var disposed = false; + var index = 0; + + var errorCallback = function (error) { + if (!disposed) { + obs.OnError(error); + } + }; + + var successCallback = function (data) { + if (!disposed) { + var i, len; + for (i = 0, len = data.length; i < len; i++) { + // The wrapper automatically checks for Dispose + // on the observer, so we don't need to check it here. + obs.OnNext(data[i]); + } + + if (data.length < pageSize) { + obs.OnCompleted(); + } else { + index += pageSize; + that.readRange(index, pageSize).then(successCallback, errorCallback); + } + } + }; + + that.readRange(index, pageSize).then(successCallback, errorCallback); + + return { Dispose: function () { disposed = true; } }; + }); + }; + + var cacheFailureCallback = function (message) { + /// Creates a function that handles a callback by setting the cache into failure mode. + /// Message text. + /// Function to use as error callback. + /// + /// This function will specifically handle problems with critical store resources + /// during cache initialization. + /// + + return function (error) { + cacheFailure = { message: message, error: error }; + + // Destroy any pending clear or read operations. + // At this point there should be no prefetch operations. + // Count operations will go through but are benign because they + // won't interact with the store. + var i, len; + for (i = 0, len = readOperations.length; i < len; i++) { + readOperations[i].fireRejected(cacheFailure); + } + for (i = 0, len = clearOperations.length; i < len; i++) { + clearOperations[i].fireRejected(cacheFailure); + } + + // Null out the operation arrays. + readOperations = clearOperations = null; + }; + }; + + var changeState = function (newState) { + /// Updates the cache's state and signals all pending operations of the change. + /// New cache state. + /// This method is a no-op if the cache's current state and the new state are the same. + + if (newState !== state) { + state = newState; + var operations = clearOperations.concat(readOperations, prefetchOperations); + var i, len; + for (i = 0, len = operations.length; i < len; i++) { + operations[i].run(state); + } + } + }; + + var clearStore = function () { + /// Removes all the data stored in the cache. + /// A promise with no value. + + var deferred = new DjsDeferred(); + store.clear(function () { + + // Reset the cache settings. + actualCacheSize = 0; + allDataLocal = false; + collectionCount = 0; + highestSavedPage = 0; + highestSavedPageSize = 0; + overflowed = cacheSize === 0; + + // version is not reset, in case there is other state in eg V1.1 that is still around. + + // Reset the cache stats. + stats = { counts: 0, netReads: 0, prefetches: 0, cacheReads: 0 }; + that.stats = stats; + + store.close(); + deferred.resolve(); + }, function (err) { + deferred.reject(err); + }); + return deferred; + }; + + var dequeueOperation = function (operation) { + /// Removes an operation from the caches queues and changes the cache state to idle. + /// Operation to dequeue. + /// This method is used as a handler for the operation's oncomplete event. + + var removed = removeFromArray(clearOperations, operation); + if (!removed) { + removed = removeFromArray(readOperations, operation); + if (!removed) { + removeFromArray(prefetchOperations, operation); + } + } + + pendingOperations--; + changeState(CACHE_STATE_IDLE); + }; + + var fetchPage = function (start) { + /// Requests data from the cache source. + /// Zero-based index of items to request. + /// A promise for a page object with (i)ndex, (c)ount, (d)ata. + + + var deferred = new DjsDeferred(); + var canceled = false; + + var request = source.read(start, pageSize, function (data) { + var page = { i: start, c: data.length, d: data }; + deferred.resolve(page); + }, function (err) { + deferred.reject(err); + }); + + return extend(deferred, { + cancel: function () { + if (request) { + request.abort(); + canceled = true; + request = null; + } + } + }); + }; + + var filter = function (index, count, predicate, backwards) { + /// Filters the cache data based a predicate. + /// The index of the item to start filtering from. + /// Maximum number of items to include in the result. + /// Callback function returning a boolean that determines whether an item should be included in the result or not. + /// True if the filtering should move backward from the specified index, falsey otherwise. + /// + /// Specifying a negative count value will yield all the items in the cache that satisfy the predicate. + /// + /// A promise for an array of results. + index = parseInt10(index); + count = parseInt10(count); + + if (isNaN(index)) { + throw { message: "'index' must be a valid number.", index: index }; + } + if (isNaN(count)) { + throw { message: "'count' must be a valid number.", count: count }; + } + + if (cacheFailure) { + throw cacheFailure; + } + + index = Math.max(index, 0); + + var deferred = createDeferred(); + var arr = []; + var canceled = false; + var pendingReadRange = null; + + var readMore = function (readIndex, readCount) { + if (!canceled) { + if (count >= 0 && arr.length >= count) { + deferred.resolve(arr); + } else { + pendingReadRange = that.readRange(readIndex, readCount).then(function (data) { + for (var i = 0, length = data.length; i < length && (count < 0 || arr.length < count) ; i++) { + var dataIndex = backwards ? length - i - 1 : i; + var item = data[dataIndex]; + if (predicate(item)) { + var element = { + index: readIndex + dataIndex, + item: item + }; + + backwards ? arr.unshift(element) : arr.push(element); + } + } + + // Have we reached the end of the collection? + if ((!backwards && data.length < readCount) || (backwards && readIndex <= 0)) { + deferred.resolve(arr); + } else { + var nextIndex = backwards ? Math.max(readIndex - pageSize, 0) : readIndex + readCount; + readMore(nextIndex, pageSize); + } + }, function (err) { + deferred.reject(err); + }); + } + } + }; + + // Initially, we read from the given starting index to the next/previous page boundary + var initialPage = snapToPageBoundaries(index, index, pageSize); + var initialIndex = backwards ? initialPage.i : index; + var initialCount = backwards ? index - initialPage.i + 1 : initialPage.i + initialPage.c - index; + readMore(initialIndex, initialCount); + + return extend(deferred.promise(), { + cancel: function () { + /// Aborts the filter operation + if (pendingReadRange) { + pendingReadRange.cancel(); + } + canceled = true; + } + }); + }; + + var fireOnIdle = function () { + /// Fires an onidle event if any functions are assigned. + + if (that.onidle && pendingOperations === 0) { + that.onidle(); + } + }; + + var prefetch = function (start) { + /// Creates and starts a new prefetch operation. + /// Zero-based index of the items to prefetch. + /// + /// This method is a no-op if any of the following conditions is true: + /// 1.- prefetchSize is 0 + /// 2.- All data has been read and stored locally in the cache. + /// 3.- There is already an all data prefetch operation queued. + /// 4.- The cache has run out of available space (overflowed). + /// + + if (allDataLocal || prefetchSize === 0 || overflowed) { + return; + } + + + if (prefetchOperations.length === 0 || (prefetchOperations[0] && prefetchOperations[0].c !== -1)) { + // Merging prefetch operations would be a nice optimization here. + var op = new DataCacheOperation(prefetchStateMachine, null, true, start, prefetchSize, null, prefetchSize); + queueAndStart(op, prefetchOperations); + } + }; + + var queueAndStart = function (op, queue) { + /// Queues an operation and runs it. + /// Operation to queue. + /// Array that will store the operation. + + op.oncomplete = dequeueOperation; + queue.push(op); + pendingOperations++; + op.run(state); + }; + + var readPage = function (key) { + /// Requests a page from the cache local store. + /// Zero-based index of the reuqested page. + /// A promise for a found flag and page object with (i)ndex, (c)ount, (d)ata, and (t)icks. + + + var canceled = false; + var deferred = extend(new DjsDeferred(), { + cancel: function () { + /// Aborts the readPage operation. + canceled = true; + } + }); + + var error = storeFailureCallback(deferred, "Read page from store failure"); + + store.contains(key, function (contained) { + if (canceled) { + return; + } + if (contained) { + store.read(key, function (_, data) { + if (!canceled) { + deferred.resolve(data !== undefined, data); + } + }, error); + return; + } + deferred.resolve(false); + }, error); + return deferred; + }; + + var savePage = function (key, page) { + /// Saves a page to the cache local store. + /// Zero-based index of the requested page. + /// Object with (i)ndex, (c)ount, (d)ata, and (t)icks. + /// A promise with no value. + + + var canceled = false; + + var deferred = extend(new DjsDeferred(), { + cancel: function () { + /// Aborts the readPage operation. + canceled = true; + } + }); + + var error = storeFailureCallback(deferred, "Save page to store failure"); + + var resolve = function () { + deferred.resolve(true); + }; + + if (page.c > 0) { + var pageBytes = estimateSize(page); + overflowed = cacheSize >= 0 && cacheSize < actualCacheSize + pageBytes; + + if (!overflowed) { + store.addOrUpdate(key, page, function () { + updateSettings(page, pageBytes); + saveSettings(resolve, error); + }, error); + } else { + resolve(); + } + } else { + updateSettings(page, 0); + saveSettings(resolve, error); + } + return deferred; + }; + + var saveSettings = function (success, error) { + /// Saves the cache's current settings to the local store. + /// Success callback. + /// Errror callback. + + var settings = { + actualCacheSize: actualCacheSize, + allDataLocal: allDataLocal, + cacheSize: cacheSize, + collectionCount: collectionCount, + highestSavedPage: highestSavedPage, + highestSavedPageSize: highestSavedPageSize, + pageSize: pageSize, + sourceId: source.identifier, + version: version + }; + + store.addOrUpdate("__settings", settings, success, error); + }; + + var storeFailureCallback = function (deferred/*, message*/) { + /// Creates a function that handles a store error. + /// Deferred object to resolve. + /// Message text. + /// Function to use as error callback. + /// + /// This function will specifically handle problems when interacting with the store. + /// + + return function (/*error*/) { + // var console = window.console; + // if (console && console.log) { + // console.log(message); + // console.dir(error); + // } + deferred.resolve(false); + }; + }; + + var updateSettings = function (page, pageBytes) { + /// Updates the cache's settings based on a page object. + /// Object with (i)ndex, (c)ount, (d)ata. + /// Size of the page in bytes. + + var pageCount = page.c; + var pageIndex = page.i; + + // Detect the collection size. + if (pageCount === 0) { + if (highestSavedPage === pageIndex - pageSize) { + collectionCount = highestSavedPage + highestSavedPageSize; + } + } else { + highestSavedPage = Math.max(highestSavedPage, pageIndex); + if (highestSavedPage === pageIndex) { + highestSavedPageSize = pageCount; + } + actualCacheSize += pageBytes; + if (pageCount < pageSize && !collectionCount) { + collectionCount = pageIndex + pageCount; + } + } + + // Detect the end of the collection. + if (!allDataLocal && collectionCount === highestSavedPage + highestSavedPageSize) { + allDataLocal = true; + } + }; + + var cancelStateMachine = function (operation, opTargetState, cacheState, data) { + /// State machine describing the behavior for cancelling a read or prefetch operation. + /// Operation being run. + /// Operation state to transition to. + /// Current cache state. + /// Additional data passed to the state. + /// + /// This state machine contains behavior common to read and prefetch operations. + /// + + var canceled = operation.canceled && opTargetState !== OPERATION_STATE_END; + if (canceled) { + if (opTargetState === OPERATION_STATE_CANCEL) { + // Cancel state. + // Data is expected to be any pending request made to the cache. + if (data && data.cancel) { + data.cancel(); + } + } + } + return canceled; + }; + + var destroyStateMachine = function (operation, opTargetState, cacheState) { + /// State machine describing the behavior of a clear operation. + /// Operation being run. + /// Operation state to transition to. + /// Current cache state. + /// + /// Clear operations have the highest priority and can't be interrupted by other operations; however, + /// they will preempt any other operation currently executing. + /// + + var transition = operation.transition; + + // Signal the cache that a clear operation is running. + if (cacheState !== CACHE_STATE_DESTROY) { + changeState(CACHE_STATE_DESTROY); + return true; + } + + switch (opTargetState) { + case OPERATION_STATE_START: + // Initial state of the operation. + transition(DESTROY_STATE_CLEAR); + break; + + case OPERATION_STATE_END: + // State that signals the operation is done. + fireOnIdle(); + break; + + case DESTROY_STATE_CLEAR: + // State that clears all the local data of the cache. + clearStore().then(function () { + // Terminate the operation once the local store has been cleared. + operation.complete(); + }); + // Wait until the clear request completes. + operation.wait(); + break; + + default: + return false; + } + return true; + }; + + var prefetchStateMachine = function (operation, opTargetState, cacheState, data) { + /// State machine describing the behavior of a prefetch operation. + /// Operation being run. + /// Operation state to transition to. + /// Current cache state. + /// Additional data passed to the state. + /// + /// Prefetch operations have the lowest priority and will be interrupted by operations of + /// other kinds. A preempted prefetch operation will resume its execution only when the state + /// of the cache returns to idle. + /// + /// If a clear operation starts executing then all the prefetch operations are canceled, + /// even if they haven't started executing yet. + /// + + // Handle cancelation + if (!cancelStateMachine(operation, opTargetState, cacheState, data)) { + + var transition = operation.transition; + + // Handle preemption + if (cacheState !== CACHE_STATE_PREFETCH) { + if (cacheState === CACHE_STATE_DESTROY) { + if (opTargetState !== OPERATION_STATE_CANCEL) { + operation.cancel(); + } + } else if (cacheState === CACHE_STATE_IDLE) { + // Signal the cache that a prefetch operation is running. + changeState(CACHE_STATE_PREFETCH); + } + return true; + } + + switch (opTargetState) { + case OPERATION_STATE_START: + // Initial state of the operation. + if (prefetchOperations[0] === operation) { + transition(READ_STATE_LOCAL, operation.i); + } + break; + + case READ_STATE_DONE: + // State that determines if the operation can be resolved or has to + // continue processing. + // Data is expected to be the read page. + var pending = operation.pending; + + if (pending > 0) { + pending -= Math.min(pending, data.c); + } + + // Are we done, or has all the data been stored? + if (allDataLocal || pending === 0 || data.c < pageSize || overflowed) { + operation.complete(); + } else { + // Continue processing the operation. + operation.pending = pending; + transition(READ_STATE_LOCAL, data.i + pageSize); + } + break; + + default: + return readSaveStateMachine(operation, opTargetState, cacheState, data, true); + } + } + return true; + }; + + var readStateMachine = function (operation, opTargetState, cacheState, data) { + /// State machine describing the behavior of a read operation. + /// Operation being run. + /// Operation state to transition to. + /// Current cache state. + /// Additional data passed to the state. + /// + /// Read operations have a higher priority than prefetch operations, but lower than + /// clear operations. They will preempt any prefetch operation currently running + /// but will be interrupted by a clear operation. + /// + /// If a clear operation starts executing then all the currently running + /// read operations are canceled. Read operations that haven't started yet will + /// wait in the start state until the destory operation finishes. + /// + + // Handle cancelation + if (!cancelStateMachine(operation, opTargetState, cacheState, data)) { + + var transition = operation.transition; + + // Handle preemption + if (cacheState !== CACHE_STATE_READ && opTargetState !== OPERATION_STATE_START) { + if (cacheState === CACHE_STATE_DESTROY) { + if (opTargetState !== OPERATION_STATE_START) { + operation.cancel(); + } + } else if (cacheState !== CACHE_STATE_WRITE) { + // Signal the cache that a read operation is running. + changeState(CACHE_STATE_READ); + } + + return true; + } + + switch (opTargetState) { + case OPERATION_STATE_START: + // Initial state of the operation. + // Wait until the cache is idle or prefetching. + if (cacheState === CACHE_STATE_IDLE || cacheState === CACHE_STATE_PREFETCH) { + // Signal the cache that a read operation is running. + changeState(CACHE_STATE_READ); + if (operation.c > 0) { + // Snap the requested range to a page boundary. + var range = snapToPageBoundaries(operation.i, operation.c, pageSize); + transition(READ_STATE_LOCAL, range.i); + } else { + transition(READ_STATE_DONE, operation); + } + } + break; + + case READ_STATE_DONE: + // State that determines if the operation can be resolved or has to + // continue processing. + // Data is expected to be the read page. + appendPage(operation, data); + var len = operation.d.length; + // Are we done? + if (operation.c === len || data.c < pageSize) { + // Update the stats, request for a prefetch operation. + stats.cacheReads++; + prefetch(data.i + data.c); + // Terminate the operation. + operation.complete(); + } else { + // Continue processing the operation. + transition(READ_STATE_LOCAL, data.i + pageSize); + } + break; + + default: + return readSaveStateMachine(operation, opTargetState, cacheState, data, false); + } + } + + return true; + }; + + var readSaveStateMachine = function (operation, opTargetState, cacheState, data, isPrefetch) { + /// State machine describing the behavior for reading and saving data into the cache. + /// Operation being run. + /// Operation state to transition to. + /// Current cache state. + /// Additional data passed to the state. + /// Flag indicating whether a read (false) or prefetch (true) operation is running. + /// + /// This state machine contains behavior common to read and prefetch operations. + /// + + var error = operation.error; + var transition = operation.transition; + var wait = operation.wait; + var request; + + switch (opTargetState) { + case OPERATION_STATE_END: + // State that signals the operation is done. + fireOnIdle(); + break; + + case READ_STATE_LOCAL: + // State that requests for a page from the local store. + // Data is expected to be the index of the page to request. + request = readPage(data).then(function (found, page) { + // Signal the cache that a read operation is running. + if (!operation.canceled) { + if (found) { + // The page is in the local store, check if the operation can be resolved. + transition(READ_STATE_DONE, page); + } else { + // The page is not in the local store, request it from the source. + transition(READ_STATE_SOURCE, data); + } + } + }); + break; + + case READ_STATE_SOURCE: + // State that requests for a page from the cache source. + // Data is expected to be the index of the page to request. + request = fetchPage(data).then(function (page) { + // Signal the cache that a read operation is running. + if (!operation.canceled) { + // Update the stats and save the page to the local store. + if (isPrefetch) { + stats.prefetches++; + } else { + stats.netReads++; + } + transition(READ_STATE_SAVE, page); + } + }, error); + break; + + case READ_STATE_SAVE: + // State that saves a page to the local store. + // Data is expected to be the page to save. + // Write access to the store is exclusive. + if (cacheState !== CACHE_STATE_WRITE) { + changeState(CACHE_STATE_WRITE); + request = savePage(data.i, data).then(function (saved) { + if (!operation.canceled) { + if (!saved && isPrefetch) { + operation.pending = 0; + } + // Check if the operation can be resolved. + transition(READ_STATE_DONE, data); + } + changeState(CACHE_STATE_IDLE); + }); + } + break; + + default: + // Unknown state that can't be handled by this state machine. + return false; + } + + if (request) { + // The operation might have been canceled between stack frames do to the async calls. + if (operation.canceled) { + request.cancel(); + } else if (operation.s === opTargetState) { + // Wait for the request to complete. + wait(request); + } + } + + return true; + }; + + // Initialize the cache. + store.read("__settings", function (_, settings) { + if (assigned(settings)) { + var settingsVersion = settings.version; + if (!settingsVersion || settingsVersion.indexOf("1.") !== 0) { + cacheFailureCallback("Unsupported cache store version " + settingsVersion)(); + return; + } + + if (pageSize !== settings.pageSize || source.identifier !== settings.sourceId) { + // The shape or the source of the data was changed so invalidate the store. + clearStore().then(function () { + // Signal the cache is fully initialized. + changeState(CACHE_STATE_IDLE); + }, cacheFailureCallback("Unable to clear store during initialization")); + } else { + // Restore the saved settings. + actualCacheSize = settings.actualCacheSize; + allDataLocal = settings.allDataLocal; + cacheSize = settings.cacheSize; + collectionCount = settings.collectionCount; + highestSavedPage = settings.highestSavedPage; + highestSavedPageSize = settings.highestSavedPageSize; + version = settingsVersion; + + // Signal the cache is fully initialized. + changeState(CACHE_STATE_IDLE); + } + } else { + // This is a brand new cache. + saveSettings(function () { + // Signal the cache is fully initialized. + changeState(CACHE_STATE_IDLE); + }, cacheFailureCallback("Unable to write settings during initialization.")); + } + }, cacheFailureCallback("Unable to read settings from store.")); + + return that; + }; + + datajs.createDataCache = function (options) { + /// Creates a data cache for a collection that is efficiently loaded on-demand. + /// + /// Options for the data cache, including name, source, pageSize, + /// prefetchSize, cacheSize, storage mechanism, and initial prefetch and local-data handler. + /// + /// A new data cache instance. + checkUndefinedGreaterThanZero(options.pageSize, "pageSize"); + checkUndefinedOrNumber(options.cacheSize, "cacheSize"); + checkUndefinedOrNumber(options.prefetchSize, "prefetchSize"); + + if (!assigned(options.name)) { + throw { message: "Undefined or null name", options: options }; + } + + if (!assigned(options.source)) { + throw { message: "Undefined source", options: options }; + } + + return new DataCache(options); + }; + + + /*Server Extension*/ + var handlerDataValidator = function (handler, part) { + if (typeof handler.dataValidator === 'function') { + return handler.dataValidator(part); + } + return true; + }; + var partHandlerSelector = function (context, part) { + if (Array.isArray(context.handler.partHandler)) { + var pHandlers = context.handler.partHandler; + var cType = getContentType(part); + for (var i = 0; i < pHandlers.length; i++) { + if (handlerAccepts(pHandlers[i], cType) && handlerDataValidator(part)) { + return pHandlers[i]; + } + } + return pHandlers[pHandlers.length - 1]; + } else { + return context.handler.partHandler; + } + }; + + var batchServerParser = function (handler, text, context) { + /// Parses a batch response. + /// This handler. + /// Batch text. + /// Object with parsing context. + /// An object representation of the batch. + + var boundary = context.contentType.properties["boundary"]; + return { __batchRequests: readRequestBatch(text, { boundaries: [boundary], handlerContext: context }) }; + }; + var readRequestBatch = function (text, context) { + /// + /// Parses a multipart/mixed response body from from the position defined by the context. + /// + /// Body of the multipart/mixed response. + /// Context used for parsing. + /// Array of objects representing the individual responses. + + var delimiter = "--" + currentBoundary(context); + + // Move beyond the delimiter and read the complete batch + readTo(text, context, delimiter); + + // Ignore the incoming line + readLine(text, context); + + // Read the batch parts + var responses = []; + var partEnd; + + while (partEnd !== "--" && context.position < text.length) { + var partHeaders = readHeaders(text, context); + var partContentType = contentType(partHeaders["Content-Type"]); + + if (partContentType && partContentType.mediaType === batchMediaType) { + context.boundaries.push(partContentType.properties["boundary"]); + try { + var changeRequests = readRequestBatch(text, context); + } catch (e) { + e.response = readRequestBatch(text, context, delimiter); + changeResponses = [e]; + } + responses.push({ __changeRequests: changeRequests }); + context.boundaries.pop(); + readTo(text, context, "--" + currentBoundary(context)); + } else { + if (!partContentType || partContentType.mediaType !== "application/http") { + throw { message: "invalid MIME part type " }; + } + // Skip empty line + readLine(text, context); + // Read the response + var response = readRequest(text, context, delimiter); + try { + partHandlerSelector(context.handlerContext, response).read(response, context.handlerContext); + //partHandler(context.handlerContext, response).read(response, context.handlerContext); + } catch (e) { + response = e; + } + + responses.push(response); + } + + partEnd = text.substr(context.position, 2); + + // Ignore the incoming line. + readLine(text, context); + } + return responses; + }; + var requestMethodRegex = /^(\w*) (.*) HTTP\/1\.\d$/i; + var readRequest = function (text, context, delimiter) { + /// + /// Parses an HTTP response. + /// + /// Text representing the http response. + /// Context used for parsing. + /// String used as delimiter of the multipart response parts. + /// Object representing the http response. + + // Read the status line. + var pos = context.position; + var line = readLine(text, context); + var match = requestMethodRegex.exec(line); + + var method; + var urlPart; + var headers; + + if (match) { + method = match[1]; + urlPart = match[2]; + headers = readHeaders(text, context); + readLine(text, context); + } else { + context.position = pos; + } + + return { + method: method, + urlPart: urlPart, + headers: headers, + body: readTo(text, context, "\r\n" + delimiter) + }; + }; + + var batchServerSerializer = function (handler, data, context) { + /// Serializes a batch object representation into text. + /// This handler. + /// Representation of a batch. + /// Object with parsing context. + /// An text representation of the batch object; undefined if not applicable. + + var cType = context.contentType = context.contentType || contentType(batchMediaType); + if (cType.mediaType === batchMediaType) { + return writeServerBatch(data, context); + } + }; + var prepareResponse = function (response, handler, context) { + /// Prepares a request object so that it can be sent through the network. + /// Object that represents the request to be sent. + /// Handler for data serialization + /// Context used for preparing the request + + if (!response.headers) { + response.headers = {}; + } else { + normalizeHeaders(response.headers); + } + + //if (response.headers.Accept === undefined) { + // response.headers.Accept = handler.accept; + //} + + if (assigned(response.data) && response.body === undefined) { + handler.write(response, context); + } + }; + + var writeServerBatch = function (data, context) { + /// + /// Serializes a batch request object to a string. + /// + /// Batch request object in payload representation format + /// Context used for the serialization + /// String representing the batch request + + var type = payloadTypeOf(data); + if (type !== PAYLOADTYPE_BATCH) { + throw { message: "Serialization of batches of type \"" + type + "\" is not supported" }; + } + + var batchBoundary = createBoundary("batchresponse_"); + context.request.batchBoundary = batchBoundary; + var batchParts = data.__batchRequests; + var batch = ""; + var i, len; + for (i = 0, len = batchParts.length; i < len; i++) { + batch += writeBatchPartDelimiter(batchBoundary, false) + + writeServerBatchPart(batchParts[i], context); + } + batch += writeBatchPartDelimiter(batchBoundary, true); + + // Register the boundary with the request content type. + var contentTypeProperties = context.contentType.properties; + contentTypeProperties.boundary = batchBoundary; + + return batch; + }; + var writeServerBatchPart = function (part, context, nested) { + /// + /// Serializes a part of a batch request to a string. A part can be either a GET request or + /// a change set grouping several CUD (create, update, delete) requests. + /// + /// Request or change set object in payload representation format + /// Object containing context information used for the serialization + /// Flag indicating that the part is nested inside a change set + /// String representing the serialized part + /// + /// A change set is an array of request objects and they cannot be nested inside other change sets. + /// + + var changeSet = part.__changeRequests; + var result; + if (isArray(changeSet)) { + if (nested) { + throw { message: "Not Supported: change set nested in other change set" }; + } + + var changeSetBoundary = createBoundary("changesetresponse_"); + result = "Content-Type: " + batchMediaType + "; boundary=" + changeSetBoundary + "\r\n"; + var i, len; + for (i = 0, len = changeSet.length; i < len; i++) { + result += writeBatchPartDelimiter(changeSetBoundary, false) + + writeServerBatchPart(changeSet[i], context, true); + } + + result += writeBatchPartDelimiter(changeSetBoundary, true); + } else { + result = "Content-Type: application/http\r\nContent-Transfer-Encoding: binary\r\n\r\n"; + prepareResponse(part, partHandlerSelector(context, part), { metadata: context.metadata }); + //prepareResponse(part, partHandler(context, part), { metadata: context.metadata }); + result += writeServerResponse(part); + } + + return result; + }; + var writeServerResponse = function (response) { + /// + /// Serializes a request object to a string. + /// + /// Request object to serialize + /// String representing the serialized request + + //var result = (request.method ? request.method : "GET") + " " + request.requestUri + " HTTP/1.1\r\n"; + var result = "HTTP/1.1 " + response.statusCode + " " + response.statusName + "\r\n"; + for (var name in response.headers) { + if (response.headers[name]) { + result = result + name + ": " + response.headers[name] + "\r\n"; + } + } + + result += "\r\n"; + + if (response.body) { + result += response.body; + } + + return result; + }; + + + odata.batchServerHandler = handler(batchServerParser, batchServerSerializer, batchMediaType, MAX_DATA_SERVICE_VERSION); + + var defaultJsonHandler = function (handler, text, context) { + return text ? window.JSON.parse(text) : undefined; + }; + odata.serverJsonHandler = handler(defaultJsonHandler, jsonSerializer, jsonAcceptTypes.join(","), MAX_DATA_SERVICE_VERSION); + + var atomReadHandler = handler(atomParser, textSerialize, atomAcceptTypes.join(","), MAX_DATA_SERVICE_VERSION); + atomReadHandler.dataValidator = function (part) { + return typeof part.data === 'string'; + }; + + //odata.batchServerHandler.partHandler = odata.serverJsonHandler; + odata.batchServerHandler.partHandler = [odata.serverJsonHandler, atomReadHandler, odata.atomHandler, odata.defaultHandler]; + +})(window); diff --git a/Scripts/datajs-1.1.0-patched.js b/Scripts/datajs-1.1.0-patched.js new file mode 100644 index 00000000..1161807f --- /dev/null +++ b/Scripts/datajs-1.1.0-patched.js @@ -0,0 +1,10119 @@ +// Copyright (c) Microsoft. All rights reserved. +// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, +// modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +// WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +// datajs.js + +(function (window, undefined) { + + var datajs = window.datajs || {}; + var odata = window.OData || {}; + + // AMD support + if (typeof define === 'function' && define.amd) { + define('datajs', datajs); + define('OData', odata); + } else { + window.datajs = datajs; + window.OData = odata; + } + + + var activeXObject = function (progId) { + /// Creates a new ActiveXObject from the given progId. + /// + /// ProgId string of the desired ActiveXObject. + /// + /// + /// This function throws whatever exception might occur during the creation + /// of the ActiveXObject. + /// + /// + /// The ActiveXObject instance. Null if ActiveX is not supported by the + /// browser. + /// + if (window.ActiveXObject !== undefined) { + return new window.ActiveXObject(progId); + } + return null; + }; + + var assigned = function (value) { + /// Checks whether the specified value is different from null and undefined. + /// Value to check. + /// true if the value is assigned; false otherwise. + return value !== null && value !== undefined; + }; + + var contains = function (arr, item) { + /// Checks whether the specified item is in the array. + /// Array to check in. + /// Item to look for. + /// true if the item is contained, false otherwise. + + var i, len; + for (i = 0, len = arr.length; i < len; i++) { + if (arr[i] === item) { + return true; + } + } + + return false; + }; + + var defined = function (a, b) { + /// Given two values, picks the first one that is not undefined. + /// First value. + /// Second value. + /// a if it's a defined value; else b. + return (a !== undefined) ? a : b; + }; + + var delay = function (callback) { + /// Delays the invocation of the specified function until execution unwinds. + /// Callback function. + if (arguments.length === 1) { + window.setTimeout(callback, 0); + return; + } + + var args = Array.prototype.slice.call(arguments, 1); + window.setTimeout(function () { + callback.apply(this, args); + }, 0); + }; + + + var extend = function (target, values) { + /// Extends the target with the specified values. + /// Object to add properties to. + /// Object with properties to add into target. + /// The target object. + + for (var name in values) { + target[name] = values[name]; + } + + return target; + }; + + var find = function (arr, callback) { + /// Returns the first item in the array that makes the callback function true. + /// Array to check in. + /// Callback function to invoke once per item in the array. + /// The first item that makes the callback return true; null otherwise or if the array is null. + + if (arr) { + var i, len; + for (i = 0, len = arr.length; i < len; i++) { + if (callback(arr[i])) { + return arr[i]; + } + } + } + return null; + }; + + var isArray = function (value) { + /// Checks whether the specified value is an array object. + /// Value to check. + /// true if the value is an array object; false otherwise. + + return Object.prototype.toString.call(value) === "[object Array]"; + }; + + var isDate = function (value) { + /// Checks whether the specified value is a Date object. + /// Value to check. + /// true if the value is a Date object; false otherwise. + + return Object.prototype.toString.call(value) === "[object Date]"; + }; + + var isObject = function (value) { + /// Tests whether a value is an object. + /// Value to test. + /// + /// Per javascript rules, null and array values are objects and will cause this function to return true. + /// + /// True is the value is an object; false otherwise. + + return typeof value === "object"; + }; + + var parseInt10 = function (value) { + /// Parses a value in base 10. + /// String value to parse. + /// The parsed value, NaN if not a valid value. + + return parseInt(value, 10); + }; + + var renameProperty = function (obj, oldName, newName) { + /// Renames a property in an object. + /// Object in which the property will be renamed. + /// Name of the property that will be renamed. + /// New name of the property. + /// + /// This function will not do anything if the object doesn't own a property with the specified old name. + /// + + if (obj.hasOwnProperty(oldName)) { + obj[newName] = obj[oldName]; + delete obj[oldName]; + } + }; + + var throwErrorCallback = function (error) { + /// Default error handler. + /// Error to handle. + throw error; + }; + + var trimString = function (str) { + /// Removes leading and trailing whitespaces from a string. + /// String to trim + /// The string with no leading or trailing whitespace. + + if (str.trim) { + return str.trim(); + } + + return str.replace(/^\s+|\s+$/g, ''); + }; + + var undefinedDefault = function (value, defaultValue) { + /// Returns a default value in place of undefined. + /// Value to check. + /// Value to return if value is undefined. + /// value if it's defined; defaultValue otherwise. + /// + /// This should only be used for cases where falsy values are valid; + /// otherwise the pattern should be 'x = (value) ? value : defaultValue;'. + /// + return (value !== undefined) ? value : defaultValue; + }; + + // Regular expression that splits a uri into its components: + // 0 - is the matched string. + // 1 - is the scheme. + // 2 - is the authority. + // 3 - is the path. + // 4 - is the query. + // 5 - is the fragment. + var uriRegEx = /^([^:\/?#]+:)?(\/\/[^\/?#]*)?([^?#:]+)?(\?[^#]*)?(#.*)?/; + var uriPartNames = ["scheme", "authority", "path", "query", "fragment"]; + + var getURIInfo = function (uri) { + /// Gets information about the components of the specified URI. + /// URI to get information from. + /// + /// An object with an isAbsolute flag and part names (scheme, authority, etc.) if available. + /// + + var result = { isAbsolute: false }; + + if (uri) { + var matches = uriRegEx.exec(uri); + if (matches) { + var i, len; + for (i = 0, len = uriPartNames.length; i < len; i++) { + if (matches[i + 1]) { + result[uriPartNames[i]] = matches[i + 1]; + } + } + } + if (result.scheme) { + result.isAbsolute = true; + } + } + + return result; + }; + + var getURIFromInfo = function (uriInfo) { + /// Builds a URI string from its components. + /// An object with uri parts (scheme, authority, etc.). + /// URI string. + + return "".concat( + uriInfo.scheme || "", + uriInfo.authority || "", + uriInfo.path || "", + uriInfo.query || "", + uriInfo.fragment || ""); + }; + + // Regular expression that splits a uri authority into its subcomponents: + // 0 - is the matched string. + // 1 - is the userinfo subcomponent. + // 2 - is the host subcomponent. + // 3 - is the port component. + var uriAuthorityRegEx = /^\/{0,2}(?:([^@]*)@)?([^:]+)(?::{1}(\d+))?/; + + // Regular expression that matches percentage enconded octects (i.e %20 or %3A); + var pctEncodingRegEx = /%[0-9A-F]{2}/ig; + + var normalizeURICase = function (uri) { + /// Normalizes the casing of a URI. + /// URI to normalize, absolute or relative. + /// The URI normalized to lower case. + + var uriInfo = getURIInfo(uri); + var scheme = uriInfo.scheme; + var authority = uriInfo.authority; + + if (scheme) { + uriInfo.scheme = scheme.toLowerCase(); + if (authority) { + var matches = uriAuthorityRegEx.exec(authority); + if (matches) { + uriInfo.authority = "//" + + (matches[1] ? matches[1] + "@" : "") + + (matches[2].toLowerCase()) + + (matches[3] ? ":" + matches[3] : ""); + } + } + } + + uri = getURIFromInfo(uriInfo); + + return uri.replace(pctEncodingRegEx, function (str) { + return str.toLowerCase(); + }); + }; + + var normalizeURI = function (uri, base) { + /// Normalizes a possibly relative URI with a base URI. + /// URI to normalize, absolute or relative. + /// Base URI to compose with. + /// The composed URI if relative; the original one if absolute. + + if (!base) { + return uri; + } + + var uriInfo = getURIInfo(uri); + if (uriInfo.isAbsolute) { + return uri; + } + + var baseInfo = getURIInfo(base); + var normInfo = {}; + var path; + + if (uriInfo.authority) { + normInfo.authority = uriInfo.authority; + path = uriInfo.path; + normInfo.query = uriInfo.query; + } else { + if (!uriInfo.path) { + path = baseInfo.path; + normInfo.query = uriInfo.query || baseInfo.query; + } else { + if (uriInfo.path.charAt(0) === '/') { + path = uriInfo.path; + } else { + path = mergeUriPathWithBase(uriInfo.path, baseInfo.path); + } + normInfo.query = uriInfo.query; + } + normInfo.authority = baseInfo.authority; + } + + normInfo.path = removeDotsFromPath(path); + + normInfo.scheme = baseInfo.scheme; + normInfo.fragment = uriInfo.fragment; + + return getURIFromInfo(normInfo); + }; + + var mergeUriPathWithBase = function (uriPath, basePath) { + /// Merges the path of a relative URI and a base URI. + /// Base URI path. + /// A string with the merged path. + + var path = "/"; + var end; + + if (basePath) { + end = basePath.lastIndexOf("/"); + path = basePath.substring(0, end); + + if (path.charAt(path.length - 1) !== "/") { + path = path + "/"; + } + } + + return path + uriPath; + }; + + var removeDotsFromPath = function (path) { + /// Removes the special folders . and .. from a URI's path. + /// URI path component. + /// Path without any . and .. folders. + + var result = ""; + var segment = ""; + var end; + + while (path) { + if (path.indexOf("..") === 0 || path.indexOf(".") === 0) { + path = path.replace(/^\.\.?\/?/g, ""); + } else if (path.indexOf("/..") === 0) { + path = path.replace(/^\/\..\/?/g, "/"); + end = result.lastIndexOf("/"); + if (end === -1) { + result = ""; + } else { + result = result.substring(0, end); + } + } else if (path.indexOf("/.") === 0) { + path = path.replace(/^\/\.\/?/g, "/"); + } else { + segment = path; + end = path.indexOf("/", 1); + if (end !== -1) { + segment = path.substring(0, end); + } + result = result + segment; + path = path.replace(segment, ""); + } + } + return result; + }; + + + + // URI prefixes to generate smaller code. + var http = "http://"; + var w3org = http + "www.w3.org/"; // http://www.w3.org/ + + var xhtmlNS = w3org + "1999/xhtml"; // http://www.w3.org/1999/xhtml + var xmlnsNS = w3org + "2000/xmlns/"; // http://www.w3.org/2000/xmlns/ + var xmlNS = w3org + "XML/1998/namespace"; // http://www.w3.org/XML/1998/namespace + + var mozillaParserErroNS = http + "www.mozilla.org/newlayout/xml/parsererror.xml"; + + var hasLeadingOrTrailingWhitespace = function (text) { + /// Checks whether the specified string has leading or trailing spaces. + /// String to check. + /// true if text has any leading or trailing whitespace; false otherwise. + + var re = /(^\s)|(\s$)/; + return re.test(text); + }; + + var isWhitespace = function (text) { + /// Determines whether the specified text is empty or whitespace. + /// Value to inspect. + /// true if the text value is empty or all whitespace; false otherwise. + + var ws = /^\s*$/; + return text === null || ws.test(text); + }; + + var isWhitespacePreserveContext = function (domElement) { + /// Determines whether the specified element has xml:space='preserve' applied. + /// Element to inspect. + /// Whether xml:space='preserve' is in effect. + + while (domElement !== null && domElement.nodeType === 1) { + var val = xmlAttributeValue(domElement, "space", xmlNS); + if (val === "preserve") { + return true; + } else if (val === "default") { + break; + } else { + domElement = domElement.parentNode; + } + } + + return false; + }; + + var isXmlNSDeclaration = function (domAttribute) { + /// Determines whether the attribute is a XML namespace declaration. + /// Element to inspect. + /// + /// True if the attribute is a namespace declaration (its name is 'xmlns' or starts with 'xmlns:'; false otherwise. + /// + + var nodeName = domAttribute.nodeName; + return nodeName == "xmlns" || nodeName.indexOf("xmlns:") == 0; + }; + + var safeSetProperty = function (obj, name, value) { + /// Safely set as property in an object by invoking obj.setProperty. + /// Object that exposes a setProperty method. + /// Property name. + /// Property value. + + try { + obj.setProperty(name, value); + } catch (_) { } + }; + + var msXmlDom3 = function () { + /// Creates an configures new MSXML 3.0 ActiveX object. + /// + /// This function throws any exception that occurs during the creation + /// of the MSXML 3.0 ActiveX object. + /// New MSXML 3.0 ActiveX object. + + var msxml3 = activeXObject("Msxml2.DOMDocument.3.0"); + if (msxml3) { + safeSetProperty(msxml3, "ProhibitDTD", true); + safeSetProperty(msxml3, "MaxElementDepth", 256); + safeSetProperty(msxml3, "AllowDocumentFunction", false); + safeSetProperty(msxml3, "AllowXsltScript", false); + } + return msxml3; + }; + + var msXmlDom = function () { + /// Creates an configures new MSXML 6.0 or MSXML 3.0 ActiveX object. + /// + /// This function will try to create a new MSXML 6.0 ActiveX object. If it fails then + /// it will fallback to create a new MSXML 3.0 ActiveX object. Any exception that + /// happens during the creation of the MSXML 6.0 will be handled by the function while + /// the ones that happend during the creation of the MSXML 3.0 will be thrown. + /// New MSXML 3.0 ActiveX object. + + try { + var msxml = activeXObject("Msxml2.DOMDocument.6.0"); + if (msxml) { + msxml.async = true; + } + return msxml; + } catch (_) { + return msXmlDom3(); + } + }; + + var msXmlParse = function (text) { + /// Parses an XML string using the MSXML DOM. + /// + /// This function throws any exception that occurs during the creation + /// of the MSXML ActiveX object. It also will throw an exception + /// in case of a parsing error. + /// New MSXML DOMDocument node representing the parsed XML string. + + var dom = msXmlDom(); + if (!dom) { + return null; + } + + dom.loadXML(text); + var parseError = dom.parseError; + if (parseError.errorCode !== 0) { + xmlThrowParserError(parseError.reason, parseError.srcText, text); + } + return dom; + }; + + var xmlThrowParserError = function (exceptionOrReason, srcText, errorXmlText) { + /// Throws a new exception containing XML parsing error information. + /// + /// String indicatin the reason of the parsing failure or + /// Object detailing the parsing error. + /// + /// + /// String indicating the part of the XML string that caused the parsing error. + /// + /// XML string for wich the parsing failed. + + if (typeof exceptionOrReason === "string") { + exceptionOrReason = { message: exceptionOrReason }; + }; + throw extend(exceptionOrReason, { srcText: srcText || "", errorXmlText: errorXmlText || "" }); + }; + + var xmlParse = function (text) { + /// Returns an XML DOM document from the specified text. + /// Document text. + /// XML DOM document. + /// This function will throw an exception in case of a parse error. + + var domParser = window.DOMParser && new window.DOMParser(); + var dom; + + if (!domParser) { + dom = msXmlParse(text); + if (!dom) { + xmlThrowParserError("XML DOM parser not supported"); + } + return dom; + } + + try { + dom = domParser.parseFromString(text, "text/xml"); + } catch (e) { + xmlThrowParserError(e, "", text); + } + + var element = dom.documentElement; + var nsURI = element.namespaceURI; + var localName = xmlLocalName(element); + + // Firefox reports errors by returing the DOM for an xml document describing the problem. + if (localName === "parsererror" && nsURI === mozillaParserErroNS) { + var srcTextElement = xmlFirstChildElement(element, mozillaParserErroNS, "sourcetext"); + var srcText = srcTextElement ? xmlNodeValue(srcTextElement) : ""; + xmlThrowParserError(xmlInnerText(element) || "", srcText, text); + } + + // Chrome (and maybe other webkit based browsers) report errors by injecting a header with an error message. + // The error may be localized, so instead we simply check for a header as the + // top element or descendant child of the document. + if (localName === "h3" && nsURI === xhtmlNS || xmlFirstDescendantElement(element, xhtmlNS, "h3")) { + var reason = ""; + var siblings = []; + var cursor = element.firstChild; + while (cursor) { + if (cursor.nodeType === 1) { + reason += xmlInnerText(cursor) || ""; + } + siblings.push(cursor.nextSibling); + cursor = cursor.firstChild || siblings.shift(); + } + reason += xmlInnerText(element) || ""; + xmlThrowParserError(reason, "", text); + } + + return dom; + }; + + var xmlQualifiedName = function (prefix, name) { + /// Builds a XML qualified name string in the form of "prefix:name". + /// Prefix string. + /// Name string to qualify with the prefix. + /// Qualified name. + + return prefix ? prefix + ":" + name : name; + }; + + var xmlAppendText = function (domNode, textNode) { + /// Appends a text node into the specified DOM element node. + /// DOM node for the element. + /// Text to append as a child of element. + if (hasLeadingOrTrailingWhitespace(textNode.data)) { + var attr = xmlAttributeNode(domNode, xmlNS, "space"); + if (!attr) { + attr = xmlNewAttribute(domNode.ownerDocument, xmlNS, xmlQualifiedName("xml", "space")); + xmlAppendChild(domNode, attr); + } + attr.value = "preserve"; + } + domNode.appendChild(textNode); + return domNode; + }; + + var xmlAttributes = function (element, onAttributeCallback) { + /// Iterates through the XML element's attributes and invokes the callback function for each one. + /// Wrapped element to iterate over. + /// Callback function to invoke with wrapped attribute nodes. + + var attributes = element.attributes; + var i, len; + for (i = 0, len = attributes.length; i < len; i++) { + onAttributeCallback(attributes.item(i)); + } + }; + + var xmlAttributeValue = function (domNode, localName, nsURI) { + /// Returns the value of a DOM element's attribute. + /// DOM node for the owning element. + /// Local name of the attribute. + /// Namespace URI of the attribute. + /// The attribute value, null if not found. + + var attribute = xmlAttributeNode(domNode, localName, nsURI); + return attribute ? xmlNodeValue(attribute) : null; + }; + + var xmlAttributeNode = function (domNode, localName, nsURI) { + /// Gets an attribute node from a DOM element. + /// DOM node for the owning element. + /// Local name of the attribute. + /// Namespace URI of the attribute. + /// The attribute node, null if not found. + + var attributes = domNode.attributes; + if (attributes.getNamedItemNS) { + return attributes.getNamedItemNS(nsURI || null, localName); + } + + return attributes.getQualifiedItem(localName, nsURI) || null; + }; + + var xmlBaseURI = function (domNode, baseURI) { + /// Gets the value of the xml:base attribute on the specified element. + /// Element to get xml:base attribute value from. + /// Base URI used to normalize the value of the xml:base attribute. + /// Value of the xml:base attribute if found; the baseURI or null otherwise. + + var base = xmlAttributeNode(domNode, "base", xmlNS); + return (base ? normalizeURI(base.value, baseURI) : baseURI) || null; + }; + + + var xmlChildElements = function (domNode, onElementCallback) { + /// Iterates through the XML element's child DOM elements and invokes the callback function for each one. + /// DOM Node containing the DOM elements to iterate over. + /// Callback function to invoke for each child DOM element. + + xmlTraverse(domNode, /*recursive*/false, function (child) { + if (child.nodeType === 1) { + onElementCallback(child); + } + // continue traversing. + return true; + }); + }; + + var xmlFindElementByPath = function (root, namespaceURI, path) { + /// Gets the descendant element under root that corresponds to the specified path and namespace URI. + /// DOM element node from which to get the descendant element. + /// The namespace URI of the element to match. + /// Path to the desired descendant element. + /// The element specified by path and namespace URI. + /// + /// All the elements in the path are matched against namespaceURI. + /// The function will stop searching on the first element that doesn't match the namespace and the path. + /// + + var parts = path.split("/"); + var i, len; + for (i = 0, len = parts.length; i < len; i++) { + root = root && xmlFirstChildElement(root, namespaceURI, parts[i]); + } + return root || null; + }; + + var xmlFindNodeByPath = function (root, namespaceURI, path) { + /// Gets the DOM element or DOM attribute node under root that corresponds to the specified path and namespace URI. + /// DOM element node from which to get the descendant node. + /// The namespace URI of the node to match. + /// Path to the desired descendant node. + /// The node specified by path and namespace URI. + /// + /// This function will traverse the path and match each node associated to a path segement against the namespace URI. + /// The traversal stops when the whole path has been exahusted or a node that doesn't belogong the specified namespace is encountered. + /// + /// The last segment of the path may be decorated with a starting @ character to indicate that the desired node is a DOM attribute. + /// + + var lastSegmentStart = path.lastIndexOf("/"); + var nodePath = path.substring(lastSegmentStart + 1); + var parentPath = path.substring(0, lastSegmentStart); + + var node = parentPath ? xmlFindElementByPath(root, namespaceURI, parentPath) : root; + if (node) { + if (nodePath.charAt(0) === "@") { + return xmlAttributeNode(node, nodePath.substring(1), namespaceURI); + } + return xmlFirstChildElement(node, namespaceURI, nodePath); + } + return null; + }; + + var xmlFirstChildElement = function (domNode, namespaceURI, localName) { + /// Returns the first child DOM element under the specified DOM node that matches the specified namespace URI and local name. + /// DOM node from which the child DOM element is going to be retrieved. + /// The namespace URI of the element to match. + /// Name of the element to match. + /// The node's first child DOM element that matches the specified namespace URI and local name; null otherwise. + + return xmlFirstElementMaybeRecursive(domNode, namespaceURI, localName, /*recursive*/false); + }; + + var xmlFirstDescendantElement = function (domNode, namespaceURI, localName) { + /// Returns the first descendant DOM element under the specified DOM node that matches the specified namespace URI and local name. + /// DOM node from which the descendant DOM element is going to be retrieved. + /// The namespace URI of the element to match. + /// Name of the element to match. + /// The node's first descendant DOM element that matches the specified namespace URI and local name; null otherwise. + + if (domNode.getElementsByTagNameNS) { + var result = domNode.getElementsByTagNameNS(namespaceURI, localName); + return result.length > 0 ? result[0] : null; + } + return xmlFirstElementMaybeRecursive(domNode, namespaceURI, localName, /*recursive*/true); + }; + + var xmlFirstElementMaybeRecursive = function (domNode, namespaceURI, localName, recursive) { + /// Returns the first descendant DOM element under the specified DOM node that matches the specified namespace URI and local name. + /// DOM node from which the descendant DOM element is going to be retrieved. + /// The namespace URI of the element to match. + /// Name of the element to match. + /// + /// True if the search should include all the descendants of the DOM node. + /// False if the search should be scoped only to the direct children of the DOM node. + /// + /// The node's first descendant DOM element that matches the specified namespace URI and local name; null otherwise. + + var firstElement = null; + xmlTraverse(domNode, recursive, function (child) { + if (child.nodeType === 1) { + var isExpectedNamespace = !namespaceURI || xmlNamespaceURI(child) === namespaceURI; + var isExpectedNodeName = !localName || xmlLocalName(child) === localName; + + if (isExpectedNamespace && isExpectedNodeName) { + firstElement = child; + } + } + return firstElement === null; + }); + return firstElement; + }; + + var xmlInnerText = function (xmlElement) { + /// Gets the concatenated value of all immediate child text and CDATA nodes for the specified element. + /// Element to get values for. + /// Text for all direct children. + + var result = null; + var root = (xmlElement.nodeType === 9 && xmlElement.documentElement) ? xmlElement.documentElement : xmlElement; + var whitespaceAlreadyRemoved = root.ownerDocument.preserveWhiteSpace === false; + var whitespacePreserveContext; + + xmlTraverse(root, false, function (child) { + if (child.nodeType === 3 || child.nodeType === 4) { + // isElementContentWhitespace indicates that this is 'ignorable whitespace', + // but it's not defined by all browsers, and does not honor xml:space='preserve' + // in some implementations. + // + // If we can't tell either way, we walk up the tree to figure out whether + // xml:space is set to preserve; otherwise we discard pure-whitespace. + // + // For example 1. The space between and is usually 'ignorable'. + var text = xmlNodeValue(child); + var shouldInclude = whitespaceAlreadyRemoved || !isWhitespace(text); + if (!shouldInclude) { + // Walk up the tree to figure out whether we are in xml:space='preserve' context + // for the cursor (needs to happen only once). + if (whitespacePreserveContext === undefined) { + whitespacePreserveContext = isWhitespacePreserveContext(root); + } + + shouldInclude = whitespacePreserveContext; + } + + if (shouldInclude) { + if (!result) { + result = text; + } else { + result += text; + } + } + } + // Continue traversing? + return true; + }); + return result; + }; + + var xmlLocalName = function (domNode) { + /// Returns the localName of a XML node. + /// DOM node to get the value from. + /// localName of domNode. + + return domNode.localName || domNode.baseName; + }; + + var xmlNamespaceURI = function (domNode) { + /// Returns the namespace URI of a XML node. + /// DOM node to get the value from. + /// Namespace URI of domNode. + + return domNode.namespaceURI || null; + }; + + var xmlNodeValue = function (domNode) { + /// Returns the value or the inner text of a XML node. + /// DOM node to get the value from. + /// Value of the domNode or the inner text if domNode represents a DOM element node. + + if (domNode.nodeType === 1) { + return xmlInnerText(domNode); + } + return domNode.nodeValue; + }; + + var xmlTraverse = function (domNode, recursive, onChildCallback) { + /// Walks through the descendants of the domNode and invokes a callback for each node. + /// DOM node whose descendants are going to be traversed. + /// + /// True if the traversal should include all the descenants of the DOM node. + /// False if the traversal should be scoped only to the direct children of the DOM node. + /// + /// Namespace URI of node. + + var subtrees = []; + var child = domNode.firstChild; + var proceed = true; + while (child && proceed) { + proceed = onChildCallback(child); + if (proceed) { + if (recursive && child.firstChild) { + subtrees.push(child.firstChild); + } + child = child.nextSibling || subtrees.shift(); + } + } + }; + + var xmlSiblingElement = function (domNode, namespaceURI, localName) { + /// Returns the next sibling DOM element of the specified DOM node. + /// DOM node from which the next sibling is going to be retrieved. + /// The namespace URI of the element to match. + /// Name of the element to match. + /// The node's next sibling DOM element, null if there is none. + + var sibling = domNode.nextSibling; + while (sibling) { + if (sibling.nodeType === 1) { + var isExpectedNamespace = !namespaceURI || xmlNamespaceURI(sibling) === namespaceURI; + var isExpectedNodeName = !localName || xmlLocalName(sibling) === localName; + + if (isExpectedNamespace && isExpectedNodeName) { + return sibling; + } + } + sibling = sibling.nextSibling; + } + return null; + }; + + var xmlDom = function () { + /// Creates a new empty DOM document node. + /// New DOM document node. + /// + /// This function will first try to create a native DOM document using + /// the browsers createDocument function. If the browser doesn't + /// support this but supports ActiveXObject, then an attempt to create + /// an MSXML 6.0 DOM will be made. If this attempt fails too, then an attempt + /// for creating an MXSML 3.0 DOM will be made. If this last attemp fails or + /// the browser doesn't support ActiveXObject then an exception will be thrown. + /// + + var implementation = window.document.implementation; + return (implementation && implementation.createDocument) ? + implementation.createDocument(null, null, null) : + msXmlDom(); + }; + + var xmlAppendChildren = function (parent, children) { + /// Appends a collection of child nodes or string values to a parent DOM node. + /// DOM node to which the children will be appended. + /// Array containing DOM nodes or string values that will be appended to the parent. + /// The parent with the appended children or string values. + /// + /// If a value in the children collection is a string, then a new DOM text node is going to be created + /// for it and then appended to the parent. + /// + + if (!isArray(children)) { + return xmlAppendChild(parent, children); + } + + var i, len; + for (i = 0, len = children.length; i < len; i++) { + children[i] && xmlAppendChild(parent, children[i]); + } + return parent; + }; + + var xmlAppendChild = function (parent, child) { + /// Appends a child node or a string value to a parent DOM node. + /// DOM node to which the child will be appended. + /// Child DOM node or string value to append to the parent. + /// The parent with the appended child or string value. + /// + /// If child is a string value, then a new DOM text node is going to be created + /// for it and then appended to the parent. + /// + + if (child) { + if (typeof child === "string") { + return xmlAppendText(parent, xmlNewText(parent.ownerDocument, child)); + } + if (child.nodeType === 2) { + parent.setAttributeNodeNS ? parent.setAttributeNodeNS(child) : parent.setAttributeNode(child); + } else { + parent.appendChild(child); + } + } + return parent; + }; + + var xmlNewAttribute = function (dom, namespaceURI, qualifiedName, value) { + /// Creates a new DOM attribute node. + /// DOM document used to create the attribute. + /// Namespace prefix. + /// Namespace URI. + /// DOM attribute node for the namespace declaration. + + var attribute = + dom.createAttributeNS && dom.createAttributeNS(namespaceURI, qualifiedName) || + dom.createNode(2, qualifiedName, namespaceURI || undefined); + + attribute.value = value || ""; + return attribute; + }; + + var xmlNewElement = function (dom, nampespaceURI, qualifiedName, children) { + /// Creates a new DOM element node. + /// DOM document used to create the DOM element. + /// Namespace URI of the new DOM element. + /// Qualified name in the form of "prefix:name" of the new DOM element. + /// + /// Collection of child DOM nodes or string values that are going to be appended to the new DOM element. + /// + /// New DOM element. + /// + /// If a value in the children collection is a string, then a new DOM text node is going to be created + /// for it and then appended to the new DOM element. + /// + + var element = + dom.createElementNS && dom.createElementNS(nampespaceURI, qualifiedName) || + dom.createNode(1, qualifiedName, nampespaceURI || undefined); + + return xmlAppendChildren(element, children || []); + }; + + var xmlNewNSDeclaration = function (dom, namespaceURI, prefix) { + /// Creates a namespace declaration attribute. + /// DOM document used to create the attribute. + /// Namespace URI. + /// Namespace prefix. + /// DOM attribute node for the namespace declaration. + + return xmlNewAttribute(dom, xmlnsNS, xmlQualifiedName("xmlns", prefix), namespaceURI); + }; + + var xmlNewFragment = function (dom, text) { + /// Creates a new DOM document fragment node for the specified xml text. + /// DOM document from which the fragment node is going to be created. + /// XML text to be represented by the XmlFragment. + /// New DOM document fragment object. + + var value = "" + text + ""; + var tempDom = xmlParse(value); + var tempRoot = tempDom.documentElement; + var imported = ("importNode" in dom) ? dom.importNode(tempRoot, true) : tempRoot; + var fragment = dom.createDocumentFragment(); + + var importedChild = imported.firstChild; + while (importedChild) { + fragment.appendChild(importedChild); + importedChild = importedChild.nextSibling; + } + return fragment; + }; + + var xmlNewText = function (dom, text) { + /// Creates new DOM text node. + /// DOM document used to create the text node. + /// Text value for the DOM text node. + /// DOM text node. + + return dom.createTextNode(text); + }; + + var xmlNewNodeByPath = function (dom, root, namespaceURI, prefix, path) { + /// Creates a new DOM element or DOM attribute node as specified by path and appends it to the DOM tree pointed by root. + /// DOM document used to create the new node. + /// DOM element node used as root of the subtree on which the new nodes are going to be created. + /// Namespace URI of the new DOM element or attribute. + /// Prefix used to qualify the name of the new DOM element or attribute. + /// Path string describing the location of the new DOM element or attribute from the root element. + /// DOM element or attribute node for the last segment of the path. + /// + /// This function will traverse the path and will create a new DOM element with the specified namespace URI and prefix + /// for each segment that doesn't have a matching element under root. + /// + /// The last segment of the path may be decorated with a starting @ character. In this case a new DOM attribute node + /// will be created. + /// + + var name = ""; + var parts = path.split("/"); + var xmlFindNode = xmlFirstChildElement; + var xmlNewNode = xmlNewElement; + var xmlNode = root; + + var i, len; + for (i = 0, len = parts.length; i < len; i++) { + name = parts[i]; + if (name.charAt(0) === "@") { + name = name.substring(1); + xmlFindNode = xmlAttributeNode; + xmlNewNode = xmlNewAttribute; + } + + var childNode = xmlFindNode(xmlNode, namespaceURI, name); + if (!childNode) { + childNode = xmlNewNode(dom, namespaceURI, xmlQualifiedName(prefix, name)); + xmlAppendChild(xmlNode, childNode); + } + xmlNode = childNode; + } + return xmlNode; + }; + + var xmlSerialize = function (domNode) { + /// + /// Returns the text representation of the document to which the specified node belongs. + /// + /// Wrapped element in the document to serialize. + /// Serialized document. + + var xmlSerializer = window.XMLSerializer; + if (xmlSerializer) { + var serializer = new xmlSerializer(); + return serializer.serializeToString(domNode); + } + + if (domNode.xml) { + return domNode.xml; + } + + throw { message: "XML serialization unsupported" }; + }; + + var xmlSerializeDescendants = function (domNode) { + /// Returns the XML representation of the all the descendants of the node. + /// Node to serialize. + /// The XML representation of all the descendants of the node. + + var children = domNode.childNodes; + var i, len = children.length; + if (len === 0) { + return ""; + } + + // Some implementations of the XMLSerializer don't deal very well with fragments that + // don't have a DOMElement as their first child. The work around is to wrap all the + // nodes in a dummy root node named "c", serialize it and then just extract the text between + // the and the substrings. + + var dom = domNode.ownerDocument; + var fragment = dom.createDocumentFragment(); + var fragmentRoot = dom.createElement("c"); + + fragment.appendChild(fragmentRoot); + // Move the children to the fragment tree. + for (i = 0; i < len; i++) { + fragmentRoot.appendChild(children[i]); + } + + var xml = xmlSerialize(fragment); + xml = xml.substr(3, xml.length - 7); + + // Move the children back to the original dom tree. + for (i = 0; i < len; i++) { + domNode.appendChild(fragmentRoot.childNodes[i]); + } + + return xml; + }; + + var xmlSerializeNode = function (domNode) { + /// Returns the XML representation of the node and all its descendants. + /// Node to serialize. + /// The XML representation of the node and all its descendants. + + var xml = domNode.xml; + if (xml !== undefined) { + return xml; + } + + if (window.XMLSerializer) { + var serializer = new window.XMLSerializer(); + return serializer.serializeToString(domNode); + } + + throw { message: "XML serialization unsupported" }; + }; + + + + + var forwardCall = function (thisValue, name, returnValue) { + /// Creates a new function to forward a call. + /// Value to use as the 'this' object. + /// Name of function to forward to. + /// Return value for the forward call (helps keep identity when chaining calls). + /// A new function that will forward a call. + + return function () { + thisValue[name].apply(thisValue, arguments); + return returnValue; + }; + }; + + var DjsDeferred = function () { + /// Initializes a new DjsDeferred object. + /// + /// Compability Note A - Ordering of callbacks through chained 'then' invocations + /// + /// The Wiki entry at http://wiki.commonjs.org/wiki/Promises/A + /// implies that .then() returns a distinct object. + //// + /// For compatibility with http://api.jquery.com/category/deferred-object/ + /// we return this same object. This affects ordering, as + /// the jQuery version will fire callbacks in registration + /// order regardless of whether they occur on the result + /// or the original object. + /// + /// Compability Note B - Fulfillment value + /// + /// The Wiki entry at http://wiki.commonjs.org/wiki/Promises/A + /// implies that the result of a success callback is the + /// fulfillment value of the object and is received by + /// other success callbacks that are chained. + /// + /// For compatibility with http://api.jquery.com/category/deferred-object/ + /// we disregard this value instead. + /// + + this._arguments = undefined; + this._done = undefined; + this._fail = undefined; + this._resolved = false; + this._rejected = false; + }; + + DjsDeferred.prototype = { + then: function (fulfilledHandler, errorHandler /*, progressHandler */) { + /// Adds success and error callbacks for this deferred object. + /// Success callback. + /// Error callback. + /// See Compatibility Note A. + + if (fulfilledHandler) { + if (!this._done) { + this._done = [fulfilledHandler]; + } else { + this._done.push(fulfilledHandler); + } + } + + if (errorHandler) { + if (!this._fail) { + this._fail = [errorHandler]; + } else { + this._fail.push(errorHandler); + } + } + + //// See Compatibility Note A in the DjsDeferred constructor. + //// if (!this._next) { + //// this._next = createDeferred(); + //// } + //// return this._next.promise(); + + if (this._resolved) { + this.resolve.apply(this, this._arguments); + } else if (this._rejected) { + this.reject.apply(this, this._arguments); + } + + return this; + }, + + resolve: function (/* args */) { + /// Invokes success callbacks for this deferred object. + /// All arguments are forwarded to success callbacks. + + + if (this._done) { + var i, len; + for (i = 0, len = this._done.length; i < len; i++) { + //// See Compability Note B - Fulfillment value. + //// var nextValue = + this._done[i].apply(null, arguments); + } + + //// See Compatibility Note A in the DjsDeferred constructor. + //// this._next.resolve(nextValue); + //// delete this._next; + + this._done = undefined; + this._resolved = false; + this._arguments = undefined; + } else { + this._resolved = true; + this._arguments = arguments; + } + }, + + reject: function (/* args */) { + /// Invokes error callbacks for this deferred object. + /// All arguments are forwarded to error callbacks. + if (this._fail) { + var i, len; + for (i = 0, len = this._fail.length; i < len; i++) { + this._fail[i].apply(null, arguments); + } + + this._fail = undefined; + this._rejected = false; + this._arguments = undefined; + } else { + this._rejected = true; + this._arguments = arguments; + } + }, + + promise: function () { + /// Returns a version of this object that has only the read-only methods available. + /// An object with only the promise object. + + var result = {}; + result.then = forwardCall(this, "then", result); + return result; + } + }; + + var createDeferred = function () { + /// Creates a deferred object. + /// + /// A new deferred object. If jQuery is installed, then a jQuery + /// Deferred object is returned, which provides a superset of features. + /// + + if (window.jQuery && window.jQuery.Deferred) { + return new window.jQuery.Deferred(); + } else { + return new DjsDeferred(); + } + }; + + + + + var dataItemTypeName = function (value, metadata) { + /// Gets the type name of a data item value that belongs to a feed, an entry, a complex type property, or a collection property. + /// Value of the data item from which the type name is going to be retrieved. + /// Object containing metadata about the data tiem. + /// + /// This function will first try to get the type name from the data item's value itself if it is an object with a __metadata property; otherwise + /// it will try to recover it from the metadata. If both attempts fail, it will return null. + /// + /// Data item type name; null if the type name cannot be found within the value or the metadata + + var valueTypeName = ((value && value.__metadata) || {}).type; + return valueTypeName || (metadata ? metadata.type : null); + }; + + var EDM = "Edm."; + var EDM_BINARY = EDM + "Binary"; + var EDM_BOOLEAN = EDM + "Boolean"; + var EDM_BYTE = EDM + "Byte"; + var EDM_DATETIME = EDM + "DateTime"; + var EDM_DATETIMEOFFSET = EDM + "DateTimeOffset"; + var EDM_DECIMAL = EDM + "Decimal"; + var EDM_DOUBLE = EDM + "Double"; + var EDM_GUID = EDM + "Guid"; + var EDM_INT16 = EDM + "Int16"; + var EDM_INT32 = EDM + "Int32"; + var EDM_INT64 = EDM + "Int64"; + var EDM_SBYTE = EDM + "SByte"; + var EDM_SINGLE = EDM + "Single"; + var EDM_STRING = EDM + "String"; + var EDM_TIME = EDM + "Time"; + + var EDM_GEOGRAPHY = EDM + "Geography"; + var EDM_GEOGRAPHY_POINT = EDM_GEOGRAPHY + "Point"; + var EDM_GEOGRAPHY_LINESTRING = EDM_GEOGRAPHY + "LineString"; + var EDM_GEOGRAPHY_POLYGON = EDM_GEOGRAPHY + "Polygon"; + var EDM_GEOGRAPHY_COLLECTION = EDM_GEOGRAPHY + "Collection"; + var EDM_GEOGRAPHY_MULTIPOLYGON = EDM_GEOGRAPHY + "MultiPolygon"; + var EDM_GEOGRAPHY_MULTILINESTRING = EDM_GEOGRAPHY + "MultiLineString"; + var EDM_GEOGRAPHY_MULTIPOINT = EDM_GEOGRAPHY + "MultiPoint"; + + var EDM_GEOMETRY = EDM + "Geometry"; + var EDM_GEOMETRY_POINT = EDM_GEOMETRY + "Point"; + var EDM_GEOMETRY_LINESTRING = EDM_GEOMETRY + "LineString"; + var EDM_GEOMETRY_POLYGON = EDM_GEOMETRY + "Polygon"; + var EDM_GEOMETRY_COLLECTION = EDM_GEOMETRY + "Collection"; + var EDM_GEOMETRY_MULTIPOLYGON = EDM_GEOMETRY + "MultiPolygon"; + var EDM_GEOMETRY_MULTILINESTRING = EDM_GEOMETRY + "MultiLineString"; + var EDM_GEOMETRY_MULTIPOINT = EDM_GEOMETRY + "MultiPoint"; + + var GEOJSON_POINT = "Point"; + var GEOJSON_LINESTRING = "LineString"; + var GEOJSON_POLYGON = "Polygon"; + var GEOJSON_MULTIPOINT = "MultiPoint"; + var GEOJSON_MULTILINESTRING = "MultiLineString"; + var GEOJSON_MULTIPOLYGON = "MultiPolygon"; + var GEOJSON_GEOMETRYCOLLECTION = "GeometryCollection"; + + var primitiveEdmTypes = [ + EDM_STRING, + EDM_INT32, + EDM_INT64, + EDM_BOOLEAN, + EDM_DOUBLE, + EDM_SINGLE, + EDM_DATETIME, + EDM_DATETIMEOFFSET, + EDM_TIME, + EDM_DECIMAL, + EDM_GUID, + EDM_BYTE, + EDM_INT16, + EDM_SBYTE, + EDM_BINARY + ]; + + var geometryEdmTypes = [ + EDM_GEOMETRY, + EDM_GEOMETRY_POINT, + EDM_GEOMETRY_LINESTRING, + EDM_GEOMETRY_POLYGON, + EDM_GEOMETRY_COLLECTION, + EDM_GEOMETRY_MULTIPOLYGON, + EDM_GEOMETRY_MULTILINESTRING, + EDM_GEOMETRY_MULTIPOINT + ]; + + var geographyEdmTypes = [ + EDM_GEOGRAPHY, + EDM_GEOGRAPHY_POINT, + EDM_GEOGRAPHY_LINESTRING, + EDM_GEOGRAPHY_POLYGON, + EDM_GEOGRAPHY_COLLECTION, + EDM_GEOGRAPHY_MULTIPOLYGON, + EDM_GEOGRAPHY_MULTILINESTRING, + EDM_GEOGRAPHY_MULTIPOINT + ]; + + var forEachSchema = function (metadata, callback) { + /// Invokes a function once per schema in metadata. + /// Metadata store; one of edmx, schema, or an array of any of them. + /// Callback function to invoke once per schema. + /// + /// The first truthy value to be returned from the callback; null or the last falsy value otherwise. + /// + + if (!metadata) { + return null; + } + + if (isArray(metadata)) { + var i, len, result; + for (i = 0, len = metadata.length; i < len; i++) { + result = forEachSchema(metadata[i], callback); + if (result) { + return result; + } + } + + return null; + } else { + if (metadata.dataServices) { + return forEachSchema(metadata.dataServices.schema, callback); + } + + return callback(metadata); + } + }; + + var formatMilliseconds = function (ms, ns) { + /// Formats a millisecond and a nanosecond value into a single string. + /// Number of milliseconds to format. + /// Number of nanoseconds to format. + /// Formatted text. + /// If the value is already as string it's returned as-is. + + // Avoid generating milliseconds if not necessary. + if (ms === 0) { + ms = ""; + } else { + ms = "." + formatNumberWidth(ms.toString(), 3); + } + if (ns > 0) { + if (ms === "") { + ms = ".000"; + } + ms += formatNumberWidth(ns.toString(), 4); + } + return ms; + }; + + var formatDateTimeOffset = function (value) { + /// Formats a DateTime or DateTimeOffset value a string. + /// Value to format. + /// Formatted text. + /// If the value is already as string it's returned as-is. + + if (typeof value === "string") { + return value; + } + + var hasOffset = isDateTimeOffset(value); + var offset = getCanonicalTimezone(value.__offset); + if (hasOffset && offset !== "Z") { + // We're about to change the value, so make a copy. + value = new Date(value.valueOf()); + + var timezone = parseTimezone(offset); + var hours = value.getUTCHours() + (timezone.d * timezone.h); + var minutes = value.getMinutes() + (timezone.d * timezone.m); + + value.setUTCHours(hours, minutes); + } else if (!hasOffset) { + // Don't suffix a 'Z' for Edm.DateTime values. + offset = ""; + } + + var year = value.getUTCFullYear(); + var month = value.getUTCMonth() + 1; + var sign = ""; + if (year <= 0) { + year = -(year - 1); + sign = "-"; + } + + var ms = formatMilliseconds(value.getUTCMilliseconds(), value.__ns); + + return sign + + formatNumberWidth(year, 4) + "-" + + formatNumberWidth(month, 2) + "-" + + formatNumberWidth(value.getUTCDate(), 2) + "T" + + formatNumberWidth(value.getUTCHours(), 2) + ":" + + formatNumberWidth(value.getUTCMinutes(), 2) + ":" + + formatNumberWidth(value.getUTCSeconds(), 2) + + ms + offset; + }; + + var formatDuration = function (value) { + /// Converts a duration to a string in xsd:duration format. + /// Object with ms and __edmType properties. + /// String representation of the time object in xsd:duration format. + + var ms = value.ms; + + var sign = ""; + if (ms < 0) { + sign = "-"; + ms = -ms; + } + + var days = Math.floor(ms / 86400000); + ms -= 86400000 * days; + var hours = Math.floor(ms / 3600000); + ms -= 3600000 * hours; + var minutes = Math.floor(ms / 60000); + ms -= 60000 * minutes; + var seconds = Math.floor(ms / 1000); + ms -= seconds * 1000; + + return sign + "P" + + formatNumberWidth(days, 2) + "DT" + + formatNumberWidth(hours, 2) + "H" + + formatNumberWidth(minutes, 2) + "M" + + formatNumberWidth(seconds, 2) + + formatMilliseconds(ms, value.ns) + "S"; + }; + + var formatNumberWidth = function (value, width, append) { + /// Formats the specified value to the given width. + /// Number to format (non-negative). + /// Minimum width for number. + /// Flag indicating if the value is padded at the beginning (false) or at the end (true). + /// Text representation. + var result = value.toString(10); + while (result.length < width) { + if (append) { + result += "0"; + } else { + result = "0" + result; + } + } + + return result; + }; + + var getCanonicalTimezone = function (timezone) { + /// Gets the canonical timezone representation. + /// Timezone representation. + /// An 'Z' string if the timezone is absent or 0; the timezone otherwise. + + return (!timezone || timezone === "Z" || timezone === "+00:00" || timezone === "-00:00") ? "Z" : timezone; + }; + + var getCollectionType = function (typeName) { + /// Gets the type of a collection type name. + /// Type name of the collection. + /// Type of the collection; null if the type name is not a collection type. + + if (typeof typeName === "string") { + var end = typeName.indexOf(")", 10); + if (typeName.indexOf("Collection(") === 0 && end > 0) { + return typeName.substring(11, end); + } + } + return null; + }; + + var invokeRequest = function (request, success, error, handler, httpClient, context) { + /// Sends a request containing OData payload to a server. + /// Object that represents the request to be sent.. + /// Callback for a successful read operation. + /// Callback for handling errors. + /// Handler for data serialization. + /// HTTP client layer. + /// Context used for processing the request + + return httpClient.request(request, function (response) { + try { + if (response.headers) { + normalizeHeaders(response.headers); + } + + if (response.data === undefined && response.statusCode !== 204) { + handler.read(response, context); + } + } catch (err) { + if (err.request === undefined) { + err.request = request; + } + if (err.response === undefined) { + err.response = response; + } + error(err); + return; + } + + success(response.data, response); + }, error); + }; + + var isBatch = function (value) { + /// Tests whether a value is a batch object in the library's internal representation. + /// Value to test. + /// True is the value is a batch object; false otherwise. + + return isComplex(value) && isArray(value.__batchRequests); + }; + + // Regular expression used for testing and parsing for a collection type. + var collectionTypeRE = /Collection\((.*)\)/; + + var isCollection = function (value, typeName) { + /// Tests whether a value is a collection value in the library's internal representation. + /// Value to test. + /// Type name of the value. This is used to disambiguate from a collection property value. + /// True is the value is a feed value; false otherwise. + + var colData = value && value.results || value; + return !!colData && + (isCollectionType(typeName)) || + (!typeName && isArray(colData) && !isComplex(colData[0])); + }; + + var isCollectionType = function (typeName) { + /// Checks whether the specified type name is a collection type. + /// Name of type to check. + /// True if the type is the name of a collection type; false otherwise. + return collectionTypeRE.test(typeName); + }; + + var isComplex = function (value) { + /// Tests whether a value is a complex type value in the library's internal representation. + /// Value to test. + /// True is the value is a complex type value; false otherwise. + + return !!value && + isObject(value) && + !isArray(value) && + !isDate(value); + }; + + var isDateTimeOffset = function (value) { + /// Checks whether a Date object is DateTimeOffset value + /// Value to check. + /// true if the value is a DateTimeOffset, false otherwise. + return (value.__edmType === "Edm.DateTimeOffset" || (!value.__edmType && value.__offset)); + }; + + var isDeferred = function (value) { + /// Tests whether a value is a deferred navigation property in the library's internal representation. + /// Value to test. + /// True is the value is a deferred navigation property; false otherwise. + + if (!value && !isComplex(value)) { + return false; + } + var metadata = value.__metadata || {}; + var deferred = value.__deferred || {}; + return !metadata.type && !!deferred.uri; + }; + + var isEntry = function (value) { + /// Tests whether a value is an entry object in the library's internal representation. + /// Value to test. + /// True is the value is an entry object; false otherwise. + + return isComplex(value) && value.__metadata && "uri" in value.__metadata; + }; + + var isFeed = function (value, typeName) { + /// Tests whether a value is a feed value in the library's internal representation. + /// Value to test. + /// Type name of the value. This is used to disambiguate from a collection property value. + /// True is the value is a feed value; false otherwise. + + var feedData = value && value.results || value; + return isArray(feedData) && ( + (!isCollectionType(typeName)) && + (isComplex(feedData[0])) + ); + }; + + var isGeographyEdmType = function (typeName) { + /// Checks whether the specified type name is a geography EDM type. + /// Name of type to check. + /// True if the type is a geography EDM type; false otherwise. + + return contains(geographyEdmTypes, typeName); + }; + + var isGeometryEdmType = function (typeName) { + /// Checks whether the specified type name is a geometry EDM type. + /// Name of type to check. + /// True if the type is a geometry EDM type; false otherwise. + + return contains(geometryEdmTypes, typeName); + }; + + var isNamedStream = function (value) { + /// Tests whether a value is a named stream value in the library's internal representation. + /// Value to test. + /// True is the value is a named stream; false otherwise. + + if (!value && !isComplex(value)) { + return false; + } + var metadata = value.__metadata; + var mediaResource = value.__mediaresource; + return !metadata && !!mediaResource && !!mediaResource.media_src; + }; + + var isPrimitive = function (value) { + /// Tests whether a value is a primitive type value in the library's internal representation. + /// Value to test. + /// + /// Date objects are considered primitive types by the library. + /// + /// True is the value is a primitive type value. + + return isDate(value) || + typeof value === "string" || + typeof value === "number" || + typeof value === "boolean"; + }; + + var isPrimitiveEdmType = function (typeName) { + /// Checks whether the specified type name is a primitive EDM type. + /// Name of type to check. + /// True if the type is a primitive EDM type; false otherwise. + + return contains(primitiveEdmTypes, typeName); + }; + + var navigationPropertyKind = function (value, propertyModel) { + /// Gets the kind of a navigation property value. + /// Value of the navigation property. + /// + /// Object that describes the navigation property in an OData conceptual schema. + /// + /// + /// The returned string is as follows + /// + /// String value describing the kind of the navigation property; null if the kind cannot be determined. + + if (isDeferred(value)) { + return "deferred"; + } + if (isEntry(value)) { + return "entry"; + } + if (isFeed(value)) { + return "feed"; + } + if (propertyModel && propertyModel.relationship) { + if (value === null || value === undefined || !isFeed(value)) { + return "entry"; + } + return "feed"; + } + return null; + }; + + var lookupProperty = function (properties, name) { + /// Looks up a property by name. + /// Array of property objects as per EDM metadata. + /// Name to look for. + /// The property object; null if not found. + + return find(properties, function (property) { + return property.name === name; + }); + }; + + var lookupInMetadata = function (name, metadata, kind) { + /// Looks up a type object by name. + /// Name, possibly null or empty. + /// Metadata store; one of edmx, schema, or an array of any of them. + /// Kind of object to look for as per EDM metadata. + /// An type description if the name is found; null otherwise. + + return (name) ? forEachSchema(metadata, function (schema) { + return lookupInSchema(name, schema, kind); + }) : null; + }; + + var lookupEntitySet = function (entitySets, name) { + /// Looks up a entity set by name. + /// Array of entity set objects as per EDM metadata. + /// Name to look for. + /// The entity set object; null if not found. + + return find(entitySets, function (entitySet) { + return entitySet.name === name; + }); + }; + + var lookupComplexType = function (name, metadata) { + /// Looks up a complex type object by name. + /// Name, possibly null or empty. + /// Metadata store; one of edmx, schema, or an array of any of them. + /// A complex type description if the name is found; null otherwise. + + return lookupInMetadata(name, metadata, "complexType"); + }; + + var lookupEntityType = function (name, metadata) { + /// Looks up an entity type object by name. + /// Name, possibly null or empty. + /// Metadata store; one of edmx, schema, or an array of any of them. + /// An entity type description if the name is found; null otherwise. + + return lookupInMetadata(name, metadata, "entityType"); + }; + + var lookupDefaultEntityContainer = function (metadata) { + /// Looks up an + /// Name, possibly null or empty. + /// Metadata store; one of edmx, schema, or an array of any of them. + /// An entity container description if the name is found; null otherwise. + + return forEachSchema(metadata, function (schema) { + return find(schema.entityContainer, function (container) { + return parseBool(container.isDefaultEntityContainer); + }); + }); + }; + + var lookupEntityContainer = function (name, metadata) { + /// Looks up an entity container object by name. + /// Name, possibly null or empty. + /// Metadata store; one of edmx, schema, or an array of any of them. + /// An entity container description if the name is found; null otherwise. + + return lookupInMetadata(name, metadata, "entityContainer"); + }; + + var lookupFunctionImport = function (functionImports, name) { + /// Looks up a function import by name. + /// Array of function import objects as per EDM metadata. + /// Name to look for. + /// The entity set object; null if not found. + + return find(functionImports, function (functionImport) { + return functionImport.name === name; + }); + }; + + var lookupNavigationPropertyType = function (navigationProperty, metadata) { + /// Looks up the target entity type for a navigation property. + /// + /// + /// The entity type name for the specified property, null if not found. + + var result = null; + if (navigationProperty) { + var rel = navigationProperty.relationship; + var association = forEachSchema(metadata, function (schema) { + // The name should be the namespace qualified name in 'ns'.'type' format. + var nameOnly = removeNamespace(schema["namespace"], rel); + var associations = schema.association; + if (nameOnly && associations) { + var i, len; + for (i = 0, len = associations.length; i < len; i++) { + if (associations[i].name === nameOnly) { + return associations[i]; + } + } + } + }); + + if (association) { + var end = association.end[0]; + if (end.role !== navigationProperty.toRole) { + end = association.end[1]; + // For metadata to be valid, end.role === navigationProperty.toRole now. + } + result = end.type; + } + } + return result; + }; + + var removeNamespace = function (ns, fullName) { + /// Given an expected namespace prefix, removes it from a full name. + /// Expected namespace. + /// Full name in 'ns'.'name' form. + /// The local name, null if it isn't found in the expected namespace. + + if (fullName.indexOf(ns) === 0 && fullName.charAt(ns.length) === ".") { + return fullName.substr(ns.length + 1); + } + + return null; + }; + + var lookupInSchema = function (name, schema, kind) { + /// Looks up a schema object by name. + /// Name (assigned). + /// Schema object as per EDM metadata. + /// Kind of object to look for as per EDM metadata. + /// An entity type description if the name is found; null otherwise. + + if (name && schema) { + // The name should be the namespace qualified name in 'ns'.'type' format. + var nameOnly = removeNamespace(schema["namespace"], name); + if (nameOnly) { + return find(schema[kind], function (item) { + return item.name === nameOnly; + }); + } + } + return null; + }; + + var maxVersion = function (left, right) { + /// Compares to version strings and returns the higher one. + /// Version string in the form "major.minor.rev" + /// Version string in the form "major.minor.rev" + /// The higher version string. + + if (left === right) { + return left; + } + + var leftParts = left.split("."); + var rightParts = right.split("."); + + var len = (leftParts.length >= rightParts.length) + ? leftParts.length + : rightParts.length; + + for (var i = 0; i < len; i++) { + var leftVersion = leftParts[i] && parseInt10(leftParts[i]); + var rightVersion = rightParts[i] && parseInt10(rightParts[i]); + if (leftVersion > rightVersion) { + return left; + } + if (leftVersion < rightVersion) { + return right; + } + } + }; + + var normalHeaders = { + "accept": "Accept", + "content-type": "Content-Type", + "dataserviceversion": "DataServiceVersion", + "maxdataserviceversion": "MaxDataServiceVersion" + }; + + var normalizeHeaders = function (headers) { + /// Normalizes headers so they can be found with consistent casing. + /// Dictionary of name/value pairs. + + for (var name in headers) { + var lowerName = name.toLowerCase(); + var normalName = normalHeaders[lowerName]; + if (normalName && name !== normalName) { + var val = headers[name]; + delete headers[name]; + headers[normalName] = val; + } + } + }; + + var parseBool = function (propertyValue) { + /// Parses a string into a boolean value. + /// Value to parse. + /// true if the property value is 'true'; false otherwise. + + if (typeof propertyValue === "boolean") { + return propertyValue; + } + + return typeof propertyValue === "string" && propertyValue.toLowerCase() === "true"; + }; + + + // The captured indices for this expression are: + // 0 - complete input + // 1,2,3 - year with optional minus sign, month, day + // 4,5,6 - hours, minutes, seconds + // 7 - optional milliseconds + // 8 - everything else (presumably offset information) + var parseDateTimeRE = /^(-?\d{4,})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.(\d+))?(.*)$/; + + var parseDateTimeMaybeOffset = function (value, withOffset, nullOnError) { + /// Parses a string into a DateTime value. + /// Value to parse. + /// Whether offset is expected. + /// The parsed value. + + // We cannot parse this in cases of failure to match or if offset information is specified. + var parts = parseDateTimeRE.exec(value); + var offset = (parts) ? getCanonicalTimezone(parts[8]) : null; + + if (!parts || (!withOffset && offset !== "Z")) { + if (nullOnError) { + return null; + } + throw { message: "Invalid date/time value" }; + } + + // Pre-parse years, account for year '0' being invalid in dateTime. + var year = parseInt10(parts[1]); + if (year <= 0) { + year++; + } + + // Pre-parse optional milliseconds, fill in default. Fail if value is too precise. + var ms = parts[7]; + var ns = 0; + if (!ms) { + ms = 0; + } else { + if (ms.length > 7) { + if (nullOnError) { + return null; + } + throw { message: "Cannot parse date/time value to given precision." }; + } + + ns = formatNumberWidth(ms.substring(3), 4, true); + ms = formatNumberWidth(ms.substring(0, 3), 3, true); + + ms = parseInt10(ms); + ns = parseInt10(ns); + } + + // Pre-parse other time components and offset them if necessary. + var hours = parseInt10(parts[4]); + var minutes = parseInt10(parts[5]); + var seconds = parseInt10(parts[6]); + if (offset !== "Z") { + // The offset is reversed to get back the UTC date, which is + // what the API will eventually have. + var timezone = parseTimezone(offset); + var direction = -(timezone.d); + hours += timezone.h * direction; + minutes += timezone.m * direction; + } + + // Set the date and time separately with setFullYear, so years 0-99 aren't biased like in Date.UTC. + var result = new Date(); + result.setUTCFullYear( + year, // Year. + parseInt10(parts[2]) - 1, // Month (zero-based for Date.UTC and setFullYear). + parseInt10(parts[3]) // Date. + ); + result.setUTCHours(hours, minutes, seconds, ms); + + if (isNaN(result.valueOf())) { + if (nullOnError) { + return null; + } + throw { message: "Invalid date/time value" }; + } + + if (withOffset) { + result.__edmType = "Edm.DateTimeOffset"; + result.__offset = offset; + } + + if (ns) { + result.__ns = ns; + } + + return result; + }; + + var parseDateTime = function (propertyValue, nullOnError) { + /// Parses a string into a DateTime value. + /// Value to parse. + /// The parsed value. + + return parseDateTimeMaybeOffset(propertyValue, false, nullOnError); + }; + + var parseDateTimeOffset = function (propertyValue, nullOnError) { + /// Parses a string into a DateTimeOffset value. + /// Value to parse. + /// The parsed value. + /// + /// The resulting object is annotated with an __edmType property and + /// an __offset property reflecting the original intended offset of + /// the value. The time is adjusted for UTC time, as the current + /// timezone-aware Date APIs will only work with the local timezone. + /// + + return parseDateTimeMaybeOffset(propertyValue, true, nullOnError); + }; + + // The captured indices for this expression are: + // 0 - complete input + // 1 - direction + // 2,3,4 - years, months, days + // 5,6,7,8 - hours, minutes, seconds, miliseconds + + var parseTimeRE = /^([+-])?P(?:(\d+)Y)?(?:(\d+)M)?(?:(\d+)D)?(?:T(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)(?:\.(\d+))?S)?)?/; + + var isEdmDurationValue = function (value) { + parseTimeRE.test(value); + } + + var parseDuration = function (duration) { + /// Parses a string in xsd:duration format. + /// Duration value. + /// + /// This method will throw an exception if the input string has a year or a month component. + /// + /// Object representing the time + + var parts = parseTimeRE.exec(duration); + + if (parts === null) { + throw { message: "Invalid duration value." }; + } + + var years = parts[2] || "0"; + var months = parts[3] || "0"; + var days = parseInt10(parts[4] || 0); + var hours = parseInt10(parts[5] || 0); + var minutes = parseInt10(parts[6] || 0); + var seconds = parseFloat(parts[7] || 0); + + if (years !== "0" || months !== "0") { + throw { message: "Unsupported duration value." }; + } + + var ms = parts[8]; + var ns = 0; + if (!ms) { + ms = 0; + } else { + if (ms.length > 7) { + throw { message: "Cannot parse duration value to given precision." }; + } + + ns = formatNumberWidth(ms.substring(3), 4, true); + ms = formatNumberWidth(ms.substring(0, 3), 3, true); + + ms = parseInt10(ms); + ns = parseInt10(ns); + } + + ms += seconds * 1000 + minutes * 60000 + hours * 3600000 + days * 86400000; + + if (parts[1] === "-") { + ms = -ms; + } + + var result = { ms: ms, __edmType: "Edm.Time" }; + + if (ns) { + result.ns = ns; + } + return result; + }; + + var parseTimezone = function (timezone) { + /// Parses a timezone description in (+|-)nn:nn format. + /// Timezone offset. + /// + /// An object with a (d)irection property of 1 for + and -1 for -, + /// offset (h)ours and offset (m)inutes. + /// + + var direction = timezone.substring(0, 1); + direction = (direction === "+") ? 1 : -1; + + var offsetHours = parseInt10(timezone.substring(1)); + var offsetMinutes = parseInt10(timezone.substring(timezone.indexOf(":") + 1)); + return { d: direction, h: offsetHours, m: offsetMinutes }; + }; + + var prepareRequest = function (request, handler, context) { + /// Prepares a request object so that it can be sent through the network. + /// Object that represents the request to be sent. + /// Handler for data serialization + /// Context used for preparing the request + + // Default to GET if no method has been specified. + if (!request.method) { + request.method = "GET"; + } + + if (!request.headers) { + request.headers = {}; + } else { + normalizeHeaders(request.headers); + } + + if (request.headers.Accept === undefined) { + request.headers.Accept = handler.accept; + } + + if (assigned(request.data) && request.body === undefined) { + handler.write(request, context); + } + + if (!assigned(request.headers.MaxDataServiceVersion)) { + request.headers.MaxDataServiceVersion = handler.maxDataServiceVersion || "1.0"; + } + }; + + var traverseInternal = function (item, owner, callback) { + /// Traverses a tree of objects invoking callback for every value. + /// Object or array to traverse. + /// + /// Callback function with key and value, similar to JSON.parse reviver. + /// + /// The object with traversed properties. + /// Unlike the JSON reviver, this won't delete null members. + + if (item && typeof item === "object") { + for (var name in item) { + var value = item[name]; + var result = traverseInternal(value, name, callback); + result = callback(name, result, owner); + if (result !== value) { + if (value === undefined) { + delete item[name]; + } else { + item[name] = result; + } + } + } + } + + return item; + }; + + var traverse = function (item, callback) { + /// Traverses a tree of objects invoking callback for every value. + /// Object or array to traverse. + /// + /// Callback function with key and value, similar to JSON.parse reviver. + /// + /// The traversed object. + /// Unlike the JSON reviver, this won't delete null members. + + return callback("", traverseInternal(item, "", callback)); + }; + + + var ticks = 0; + + var canUseJSONP = function (request) { + /// + /// Checks whether the specified request can be satisfied with a JSONP request. + /// + /// Request object to check. + /// true if the request can be satisfied; false otherwise. + + // Requests that 'degrade' without changing their meaning by going through JSONP + // are considered usable. + // + // We allow data to come in a different format, as the servers SHOULD honor the Accept + // request but may in practice return content with a different MIME type. + if (request.method && request.method !== "GET") { + return false; + } + + return true; + }; + + var createIFrame = function (url) { + /// Creates an IFRAME tag for loading the JSONP script + /// The source URL of the script + /// The IFRAME tag + var iframe = window.document.createElement("IFRAME"); + iframe.style.display = "none"; + + var attributeEncodedUrl = url.replace(/&/g, "&").replace(/"/g, """).replace(/\<\/script><\/head><\/body><\/html>"; + + var body = window.document.getElementsByTagName("BODY")[0]; + body.appendChild(iframe); + + writeHtmlToIFrame(iframe, html); + return iframe; + }; + + var createXmlHttpRequest = function () { + /// Creates a XmlHttpRequest object. + /// XmlHttpRequest object. + if (window.XMLHttpRequest) { + return new window.XMLHttpRequest(); + } + var exception; + if (window.ActiveXObject !== undefined) { + try { + return new window.ActiveXObject("Msxml2.XMLHTTP.6.0"); + } catch (_) { + try { + return new window.ActiveXObject("Msxml2.XMLHTTP.3.0"); + } catch (e) { + exception = e; + } + } + } else { + exception = { message: "XMLHttpRequest not supported" }; + } + throw exception; + }; + + var isAbsoluteUrl = function (url) { + /// Checks whether the specified URL is an absolute URL. + /// URL to check. + /// true if the url is an absolute URL; false otherwise. + + return url.indexOf("http://") === 0 || + url.indexOf("https://") === 0 || + url.indexOf("file://") === 0; + }; + + var isLocalUrl = function (url) { + /// Checks whether the specified URL is local to the current context. + /// URL to check. + /// true if the url is a local URL; false otherwise. + + if (!isAbsoluteUrl(url)) { + return true; + } + + // URL-embedded username and password will not be recognized as same-origin URLs. + var location = window.location; + var locationDomain = location.protocol + "//" + location.host + "/"; + return (url.indexOf(locationDomain) === 0); + }; + + var removeCallback = function (name, tick) { + /// Removes a callback used for a JSONP request. + /// Function name to remove. + /// Tick count used on the callback. + try { + delete window[name]; + } catch (err) { + window[name] = undefined; + if (tick === ticks - 1) { + ticks -= 1; + } + } + }; + + var removeIFrame = function (iframe) { + /// Removes an iframe. + /// The iframe to remove. + /// Null value to be assigned to iframe reference. + if (iframe) { + writeHtmlToIFrame(iframe, ""); + iframe.parentNode.removeChild(iframe); + } + + return null; + }; + + var readResponseHeaders = function (xhr, headers) { + /// Reads response headers into array. + /// HTTP request with response available. + /// Target array to fill with name/value pairs. + + var responseHeaders = xhr.getAllResponseHeaders().split(/\r?\n/); + var i, len; + for (i = 0, len = responseHeaders.length; i < len; i++) { + if (responseHeaders[i]) { + var header = responseHeaders[i].split(": "); + headers[header[0]] = header[1]; + } + } + }; + + var writeHtmlToIFrame = function (iframe, html) { + /// Writes HTML to an IFRAME document. + /// The IFRAME element to write to. + /// The HTML to write. + var frameDocument = (iframe.contentWindow) ? iframe.contentWindow.document : iframe.contentDocument.document; + frameDocument.open(); + frameDocument.write(html); + frameDocument.close(); + }; + + odata.defaultHttpClient = { + callbackParameterName: "$callback", + + formatQueryString: "$format=json", + + enableJsonpCallback: false, + + request: function (request, success, error) { + /// Performs a network request. + /// Request description. + /// Success callback with the response object. + /// Error callback with an error object. + /// Object with an 'abort' method for the operation. + + var result = {}; + var xhr = null; + var done = false; + var iframe; + + result.abort = function () { + iframe = removeIFrame(iframe); + if (done) { + return; + } + + done = true; + if (xhr) { + xhr.abort(); + xhr = null; + } + + error({ message: "Request aborted" }); + }; + + var handleTimeout = function () { + iframe = removeIFrame(iframe); + if (!done) { + done = true; + xhr = null; + error({ message: "Request timed out" }); + } + }; + + var name; + var url = request.requestUri; + var enableJsonpCallback = defined(request.enableJsonpCallback, this.enableJsonpCallback); + var callbackParameterName = defined(request.callbackParameterName, this.callbackParameterName); + var formatQueryString = defined(request.formatQueryString, this.formatQueryString); + if (!enableJsonpCallback || isLocalUrl(url)) { + + xhr = createXmlHttpRequest(); + xhr.onreadystatechange = function () { + if (done || xhr === null || xhr.readyState !== 4) { + return; + } + + // Workaround for XHR behavior on IE. + var statusText = xhr.statusText; + var statusCode = xhr.status; + if (statusCode === 1223) { + statusCode = 204; + statusText = "No Content"; + } + + var headers = []; + readResponseHeaders(xhr, headers); + + var response = { requestUri: url, statusCode: statusCode, statusText: statusText, headers: headers, body: xhr.responseText }; + + done = true; + xhr = null; + if (statusCode >= 200 && statusCode <= 299) { + success(response); + } else { + error({ message: "HTTP request failed", request: request, response: response }); + } + }; + + xhr.open(request.method || "GET", url, true, request.user, request.password); + + // Set the name/value pairs. + if (request.headers) { + for (name in request.headers) { + xhr.setRequestHeader(name, request.headers[name]); + } + } + + // Set the timeout if available. + if (request.timeoutMS) { + xhr.timeout = request.timeoutMS; + xhr.ontimeout = handleTimeout; + } + + xhr.send(request.body); + } else { + if (!canUseJSONP(request)) { + throw { message: "Request is not local and cannot be done through JSONP." }; + } + + var tick = ticks; + ticks += 1; + var tickText = tick.toString(); + var succeeded = false; + var timeoutId; + name = "handleJSONP_" + tickText; + window[name] = function (data) { + iframe = removeIFrame(iframe); + if (!done) { + succeeded = true; + window.clearTimeout(timeoutId); + removeCallback(name, tick); + + // Workaround for IE8 and below where trying to access data.constructor after the IFRAME has been removed + // throws an "unknown exception" + if (window.ActiveXObject && !window.DOMParser) { + data = window.JSON.parse(window.JSON.stringify(data)); + } + + // Call the success callback in the context of the parent window, instead of the IFRAME + delay(success, { body: data, statusCode: 200, headers: { "Content-Type": "application/json"} }); + } + }; + + // Default to two minutes before timing out, 1000 ms * 60 * 2 = 120000. + var timeoutMS = (request.timeoutMS) ? request.timeoutMS : 120000; + timeoutId = window.setTimeout(handleTimeout, timeoutMS); + + var queryStringParams = callbackParameterName + "=parent." + name; + if (this.formatQueryString) { + queryStringParams += "&" + formatQueryString; + } + + var qIndex = url.indexOf("?"); + if (qIndex === -1) { + url = url + "?" + queryStringParams; + } else if (qIndex === url.length - 1) { + url = url + queryStringParams; + } else { + url = url + "&" + queryStringParams; + } + + iframe = createIFrame(url); + } + + return result; + } + }; + + + + var MAX_DATA_SERVICE_VERSION = "3.0"; + + var contentType = function (str) { + /// Parses a string into an object with media type and properties. + /// String with media type to parse. + /// null if the string is empty; an object with 'mediaType' and a 'properties' dictionary otherwise. + + if (!str) { + return null; + } + + var contentTypeParts = str.split(";"); + var properties = {}; + + var i, len; + for (i = 1, len = contentTypeParts.length; i < len; i++) { + var contentTypeParams = contentTypeParts[i].split("="); + properties[trimString(contentTypeParams[0])] = contentTypeParams[1]; + } + + return { mediaType: trimString(contentTypeParts[0]), properties: properties }; + }; + + var contentTypeToString = function (contentType) { + /// Serializes an object with media type and properties dictionary into a string. + /// Object with media type and properties dictionary to serialize. + /// String representation of the media type object; undefined if contentType is null or undefined. + + if (!contentType) { + return undefined; + } + + var result = contentType.mediaType; + var property; + for (property in contentType.properties) { + result += ";" + property + "=" + contentType.properties[property]; + } + return result; + }; + + var createReadWriteContext = function (contentType, dataServiceVersion, context, handler) { + /// Creates an object that is going to be used as the context for the handler's parser and serializer. + /// Object with media type and properties dictionary. + /// String indicating the version of the protocol to use. + /// Operation context. + /// Handler object that is processing a resquest or response. + /// Context object. + + var rwContext = { + contentType: contentType, + dataServiceVersion: dataServiceVersion, + handler: handler + }; + + return extend(rwContext, context); + }; + + var fixRequestHeader = function (request, name, value) { + /// Sets a request header's value. If the header has already a value other than undefined, null or empty string, then this method does nothing. + /// Request object on which the header will be set. + /// Header name. + /// Header value. + if (!request) { + return; + } + + var headers = request.headers; + if (!headers[name]) { + headers[name] = value; + } + }; + + var fixDataServiceVersionHeader = function (request, version) { + /// Sets the DataServiceVersion header of the request if its value is not yet defined or of a lower version. + /// Request object on which the header will be set. + /// Version value. + /// + /// If the request has already a version value higher than the one supplied the this function does nothing. + /// + + if (request) { + var headers = request.headers; + var dsv = headers["DataServiceVersion"]; + headers["DataServiceVersion"] = dsv ? maxVersion(dsv, version) : version; + } + }; + + var getRequestOrResponseHeader = function (requestOrResponse, name) { + /// Gets the value of a request or response header. + /// Object representing a request or a response. + /// Name of the header to retrieve. + /// String value of the header; undefined if the header cannot be found. + + var headers = requestOrResponse.headers; + return (headers && headers[name]) || undefined; + }; + + var getContentType = function (requestOrResponse) { + /// Gets the value of the Content-Type header from a request or response. + /// Object representing a request or a response. + /// Object with 'mediaType' and a 'properties' dictionary; null in case that the header is not found or doesn't have a value. + + return contentType(getRequestOrResponseHeader(requestOrResponse, "Content-Type")); + }; + + var versionRE = /^\s?(\d+\.\d+);?.*$/; + var getDataServiceVersion = function (requestOrResponse) { + /// Gets the value of the DataServiceVersion header from a request or response. + /// Object representing a request or a response. + /// Data service version; undefined if the header cannot be found. + + var value = getRequestOrResponseHeader(requestOrResponse, "DataServiceVersion"); + if (value) { + var matches = versionRE.exec(value); + if (matches && matches.length) { + return matches[1]; + } + } + + // Fall through and return undefined. + }; + + var handlerAccepts = function (handler, cType) { + /// Checks that a handler can process a particular mime type. + /// Handler object that is processing a resquest or response. + /// Object with 'mediaType' and a 'properties' dictionary. + /// True if the handler can process the mime type; false otherwise. + + // The following check isn't as strict because if cType.mediaType = application/; it will match an accept value of "application/xml"; + // however in practice we don't not expect to see such "suffixed" mimeTypes for the handlers. + return handler.accept.indexOf(cType.mediaType) >= 0; + }; + + var handlerRead = function (handler, parseCallback, response, context) { + /// Invokes the parser associated with a handler for reading the payload of a HTTP response. + /// Handler object that is processing the response. + /// Parser function that will process the response payload. + /// HTTP response whose payload is going to be processed. + /// Object used as the context for processing the response. + /// True if the handler processed the response payload and the response.data property was set; false otherwise. + + if (!response || !response.headers) { + return false; + } + + var cType = getContentType(response); + var version = getDataServiceVersion(response) || ""; + var body = response.body; + + if (!assigned(body)) { + return false; + } + + if (handlerAccepts(handler, cType)) { + var readContext = createReadWriteContext(cType, version, context, handler); + readContext.response = response; + response.data = parseCallback(handler, body, readContext); + return response.data !== undefined; + } + + return false; + }; + + var handlerWrite = function (handler, serializeCallback, request, context) { + /// Invokes the serializer associated with a handler for generating the payload of a HTTP request. + /// Handler object that is processing the request. + /// Serializer function that will generate the request payload. + /// HTTP request whose payload is going to be generated. + /// Object used as the context for serializing the request. + /// True if the handler serialized the request payload and the request.body property was set; false otherwise. + if (!request || !request.headers) { + return false; + } + + var cType = getContentType(request); + var version = getDataServiceVersion(request); + + if (!cType || handlerAccepts(handler, cType)) { + var writeContext = createReadWriteContext(cType, version, context, handler); + writeContext.request = request; + + request.body = serializeCallback(handler, request.data, writeContext); + + if (request.body !== undefined) { + fixDataServiceVersionHeader(request, writeContext.dataServiceVersion || "1.0"); + + fixRequestHeader(request, "Content-Type", contentTypeToString(writeContext.contentType)); + fixRequestHeader(request, "MaxDataServiceVersion", handler.maxDataServiceVersion); + return true; + } + } + + return false; + }; + + var handler = function (parseCallback, serializeCallback, accept, maxDataServiceVersion) { + /// Creates a handler object for processing HTTP requests and responses. + /// Parser function that will process the response payload. + /// Serializer function that will generate the request payload. + /// String containing a comma separated list of the mime types that this handler can work with. + /// String indicating the highest version of the protocol that this handler can work with. + /// Handler object. + + return { + accept: accept, + maxDataServiceVersion: maxDataServiceVersion, + + read: function (response, context) { + return handlerRead(this, parseCallback, response, context); + }, + + write: function (request, context) { + return handlerWrite(this, serializeCallback, request, context); + } + }; + }; + + var textParse = function (handler, body /*, context */) { + return body; + }; + + var textSerialize = function (handler, data /*, context */) { + if (assigned(data)) { + return data.toString(); + } else { + return undefined; + } + }; + + odata.textHandler = handler(textParse, textSerialize, "text/plain", MAX_DATA_SERVICE_VERSION); + + + var gmlOpenGis = http + "www.opengis.net"; // http://www.opengis.net + var gmlXmlNs = gmlOpenGis + "/gml"; // http://www.opengis.net/gml + var gmlSrsPrefix = gmlOpenGis + "/def/crs/EPSG/0/"; // http://www.opengis.net/def/crs/EPSG/0/ + + var gmlPrefix = "gml"; + + var gmlCreateGeoJSONOBject = function (type, member, data) { + /// Creates a GeoJSON object with the specified type, member and value. + /// GeoJSON object type. + /// Name for the data member in the GeoJSON object. + /// Data to be contained by the GeoJSON object. + /// GeoJSON object. + + var result = { type: type }; + result[member] = data; + return result; + }; + + var gmlSwapLatLong = function (coordinates) { + /// Swaps the longitude and latitude in the coordinates array. + /// Array of doubles descrbing a set of coordinates. + /// Array of doubles with the latitude and longitude components swapped. + + if (isArray(coordinates) && coordinates.length >= 2) { + var tmp = coordinates[0]; + coordinates[0] = coordinates[1]; + coordinates[1] = tmp; + } + return coordinates; + }; + + var gmlReadODataMultiItem = function (domElement, type, member, members, valueReader, isGeography) { + /// + /// Reads a GML DOM element that represents a composite structure like a multi-point or a + /// multi-geometry returnig its GeoJSON representation. + /// + /// GML DOM element. + /// GeoJSON object type. + /// Name for the child element representing a single item in the composite structure. + /// Name for the child element representing a collection of items in the composite structure. + /// Callback function invoked to get the coordinates of each item in the comoposite structure. + /// Flag indicating if the value uses a geographic reference system or not. + /// + /// When using a geographic reference system, the first component of all the coordinates in each element in the GML DOM tree is the Latitude and + /// will be deserialized as the second component of each element in the GML DOM tree. + /// + /// GeoJSON object. + + var coordinates = gmlReadODataMultiItemValue(domElement, member, members, valueReader, isGeography); + return gmlCreateGeoJSONOBject(type, "coordinates", coordinates); + }; + + var gmlReadODataMultiItemValue = function (domElement, member, members, valueReader, isGeography) { + /// + /// Reads the value of a GML DOM element that represents a composite structure like a multi-point or a + /// multi-geometry returnig its items. + /// + /// GML DOM element. + /// GeoJSON object type. + /// Name for the child element representing a single item in the composite structure. + /// Name for the child element representing a collection of items in the composite structure. + /// Callback function invoked to get the transformed value of each item in the comoposite structure. + /// Flag indicating if the value uses a geographic reference system or not. + /// + /// When using a geographic reference system, the first component of all the coordinates in each element in the GML DOM tree is the Latitude and + /// will be deserialized as the second component of each element in the GML DOM tree. + /// + /// Array containing the transformed value of each item in the multi-item. + + var items = []; + + xmlChildElements(domElement, function (child) { + if (xmlNamespaceURI(child) !== gmlXmlNs) { + return; + } + + var localName = xmlLocalName(child); + + if (localName === member) { + var valueElement = xmlFirstChildElement(child, gmlXmlNs); + if (valueElement) { + var value = valueReader(valueElement, isGeography); + if (value) { + items.push(value); + } + } + return; + } + + if (localName === members) { + xmlChildElements(child, function (valueElement) { + if (xmlNamespaceURI(valueElement) !== gmlXmlNs) { + return; + } + + var value = valueReader(valueElement, isGeography); + if (value) { + items.push(value); + } + }); + } + }); + return items; + }; + + var gmlReadODataCollection = function (domElement, isGeography) { + /// Reads a GML DOM element representing a multi-geometry returning its GeoJSON representation. + /// DOM element. + /// Flag indicating if the value uses a geographic reference system or not. + /// + /// When using a geographic reference system, the first component of all the coordinates in each element in the GML DOM tree is the Latitude and + /// will be deserialized as the second component of each element in the GML DOM tree. + /// + /// MultiGeometry object in GeoJSON format. + + var geometries = gmlReadODataMultiItemValue(domElement, "geometryMember", "geometryMembers", gmlReadODataSpatialValue, isGeography); + return gmlCreateGeoJSONOBject(GEOJSON_GEOMETRYCOLLECTION, "geometries", geometries); + }; + + var gmlReadODataLineString = function (domElement, isGeography) { + /// Reads a GML DOM element representing a line string returning its GeoJSON representation. + /// DOM element. + /// Flag indicating if the value uses a geographic reference system or not. + /// + /// When using a geographic reference system, the first component of all the coordinates in each element in the GML DOM tree is the Latitude and + /// will be deserialized as the second component of each element in the GML DOM tree. + /// + /// LineString object in GeoJSON format. + + return gmlCreateGeoJSONOBject(GEOJSON_LINESTRING, "coordinates", gmlReadODataLineValue(domElement, isGeography)); + }; + + var gmlReadODataMultiLineString = function (domElement, isGeography) { + /// Reads a GML DOM element representing a multi-line string returning its GeoJSON representation. + /// DOM element. + /// Flag indicating if the value uses a geographic reference system or not. + /// + /// When using a geographic reference system, the first component of all the coordinates in each element in the GML DOM tree is the Latitude and + /// will be deserialized as the second component of each element in the GML DOM tree. + /// + /// MultiLineString object in GeoJSON format. + + return gmlReadODataMultiItem(domElement, GEOJSON_MULTILINESTRING, "curveMember", "curveMembers", gmlReadODataLineValue, isGeography); + }; + + var gmlReadODataMultiPoint = function (domElement, isGeography) { + /// Reads a GML DOM element representing a multi-point returning its GeoJSON representation. + /// DOM element. + /// Flag indicating if the value uses a geographic reference system or not. + /// + /// When using a geographic reference system, the first component of all the coordinates in each element in the GML DOM tree is the Latitude and + /// will be deserialized as the second component of each element in the GML DOM tree. + /// + /// MultiPoint object in GeoJSON format. + + return gmlReadODataMultiItem(domElement, GEOJSON_MULTIPOINT, "pointMember", "pointMembers", gmlReadODataPointValue, isGeography); + }; + + var gmlReadODataMultiPolygon = function (domElement, isGeography) { + /// Reads a GML DOM element representing a multi-polygon returning its GeoJSON representation. + /// DOM element. + /// Flag indicating if the value uses a geographic reference system or not. + /// + /// When using a geographic reference system, the first component of all the coordinates in each element in the GML DOM tree is the Latitude and + /// will be deserialized as the second component of each element in the GML DOM tree. + /// + /// MultiPolygon object in GeoJSON format. + + return gmlReadODataMultiItem(domElement, GEOJSON_MULTIPOLYGON, "surfaceMember", "surfaceMembers", gmlReadODataPolygonValue, isGeography); + }; + + var gmlReadODataPoint = function (domElement, isGeography) { + /// Reads a GML DOM element representing a point returning its GeoJSON representation. + /// DOM element. + /// Flag indicating if the value uses a geographic reference system or not. + /// + /// When using a geographic reference system, the first component of all the coordinates in each element in the GML DOM tree is the Latitude and + /// will be deserialized as the second component of each element in the GML DOM tree. + /// + /// Point object in GeoJSON format. + + return gmlCreateGeoJSONOBject(GEOJSON_POINT, "coordinates", gmlReadODataPointValue(domElement, isGeography)); + }; + + var gmlReadODataPolygon = function (domElement, isGeography) { + /// Reads a GML DOM element representing a polygon returning its GeoJSON representation. + /// DOM element. + /// Flag indicating if the value uses a geographic reference system or not. + /// + /// When using a geographic reference system, the first component of all the coordinates in each element in the GML DOM tree is the Latitude and + /// will be deserialized as the second component of each element in the GML DOM tree. + /// + /// Polygon object in GeoJSON format. + + return gmlCreateGeoJSONOBject(GEOJSON_POLYGON, "coordinates", gmlReadODataPolygonValue(domElement, isGeography)); + }; + + var gmlReadODataLineValue = function (domElement, isGeography) { + /// Reads the value of a GML DOM element representing a line returning its set of coordinates. + /// DOM element. + /// Flag indicating if the value uses a geographic reference system or not. + /// + /// When using a geographic reference system, the first component of all the coordinates in each element in the GML DOM tree is the Latitude and + /// will be deserialized as the second component of each element in the GML DOM tree. + /// + /// Array containing an array of doubles for each coordinate of the line. + + var coordinates = []; + + xmlChildElements(domElement, function (child) { + var nsURI = xmlNamespaceURI(child); + + if (nsURI !== gmlXmlNs) { + return; + } + + var localName = xmlLocalName(child); + + if (localName === "posList") { + coordinates = gmlReadODataPosListValue(child, isGeography); + return; + } + if (localName === "pointProperty") { + coordinates.push(gmlReadODataPointWrapperValue(child, isGeography)); + return; + } + if (localName === "pos") { + coordinates.push(gmlReadODataPosValue(child, isGeography)); + return; + } + }); + + return coordinates; + }; + + var gmlReadODataPointValue = function (domElement, isGeography) { + /// Reads the value of a GML DOM element representing a point returning its coordinates. + /// DOM element. + /// Flag indicating if the value uses a geographic reference system or not. + /// + /// When using a geographic reference system, the first component of all the coordinates in each element in the GML DOM tree is the Latitude and + /// will be deserialized as the second component of each element in the GML DOM tree. + /// + /// Array of doubles containing the point coordinates. + + var pos = xmlFirstChildElement(domElement, gmlXmlNs, "pos"); + return pos ? gmlReadODataPosValue(pos, isGeography) : []; + }; + + var gmlReadODataPointWrapperValue = function (domElement, isGeography) { + /// Reads the value of a GML DOM element wrapping an element representing a point returning its coordinates. + /// DOM element. + /// Flag indicating if the value uses a geographic reference system or not. + /// + /// When using a geographic reference system, the first component of all the coordinates in each element in the GML DOM tree is the Latitude and + /// will be deserialized as the second component of each element in the GML DOM tree. + /// + /// Array of doubles containing the point coordinates. + + var point = xmlFirstChildElement(domElement, gmlXmlNs, "Point"); + return point ? gmlReadODataPointValue(point, isGeography) : []; + }; + + var gmlReadODataPolygonValue = function (domElement, isGeography) { + /// Reads the value of a GML DOM element representing a polygon returning its set of coordinates. + /// DOM element. + /// Flag indicating if the value uses a geographic reference system or not. + /// + /// When using a geographic reference system, the first component of all the coordinates in each element in the GML DOM tree is the Latitude and + /// will be deserialized as the second component of each element in the GML DOM tree. + /// + /// Array containing an array of array of doubles for each ring of the polygon. + + var coordinates = []; + var exteriorFound = false; + xmlChildElements(domElement, function (child) { + if (xmlNamespaceURI(child) !== gmlXmlNs) { + return; + } + + // Only the exterior and the interior rings are interesting + var localName = xmlLocalName(child); + if (localName === "exterior") { + exteriorFound = true; + coordinates.unshift(gmlReadODataPolygonRingValue(child, isGeography)); + return; + } + if (localName === "interior") { + coordinates.push(gmlReadODataPolygonRingValue(child, isGeography)); + return; + } + }); + + if (!exteriorFound && coordinates.length > 0) { + // Push an empty exterior ring. + coordinates.unshift([[]]); + } + + return coordinates; + }; + + var gmlReadODataPolygonRingValue = function (domElement, isGeography) { + /// Reads the value of a GML DOM element representing a linear ring in a GML Polygon element. + /// DOM element. + /// Flag indicating if the value uses a geographic reference system or not. + /// + /// When using a geographic reference system, the first component of all the coordinates in each element in the GML DOM tree is the Latitude and + /// will be deserialized as the second component of each element in the GML DOM tree. + /// + /// Array containing an array of doubles for each coordinate of the linear ring. + + var value = []; + xmlChildElements(domElement, function (child) { + if (xmlNamespaceURI(child) !== gmlXmlNs || xmlLocalName(child) !== "LinearRing") { + return; + } + value = gmlReadODataLineValue(child, isGeography); + }); + return value; + }; + + var gmlReadODataPosListValue = function (domElement, isGeography) { + /// Reads the value of a GML DOM element representing a list of positions retruning its set of coordinates. + /// DOM element. + /// Flag indicating if the value uses a geographic reference system or not. + /// + /// When using a geographic reference system, the first component of all the coordinates in each element in the GML DOM tree is the Latitude and + /// will be deserialized as the second component of each element in the GML DOM tree. + /// + /// The positions described by the list are assumed to be 2D, so + /// an exception will be thrown if the list has an odd number elements. + /// + /// Array containing an array of doubles for each coordinate in the list. + + var coordinates = gmlReadODataPosValue(domElement, false); + var len = coordinates.length; + + if (len % 2 !== 0) { + throw { message: "GML posList element has an uneven number of numeric values" }; + } + + var value = []; + for (var i = 0; i < len; i += 2) { + var pos = coordinates.slice(i, i + 2); + value.push(isGeography ? gmlSwapLatLong(pos) : pos); + } + return value; + }; + + var gmlReadODataPosValue = function (domElement, isGeography) { + /// Reads the value of a GML element describing a position or a set of coordinates in an OData spatial property value. + /// DOM element for the GML element. + /// Flag indicating if the value uses a geographic reference system or not. + /// + /// When using a geographic reference system, the first component of all the coordinates in each element in the GML DOM tree is the Latitude and + /// will be deserialized as the second component of each element in the GML DOM tree. + /// + /// Array of doubles containing the coordinates. + + var value = []; + var delims = " \t\r\n"; + var text = xmlInnerText(domElement); + + if (text) { + var len = text.length; + var start = 0; + var end = 0; + + while (end <= len) { + if (delims.indexOf(text.charAt(end)) !== -1) { + var coord = text.substring(start, end); + if (coord) { + value.push(parseFloat(coord)); + } + start = end + 1; + } + end++; + } + } + + return isGeography ? gmlSwapLatLong(value) : value; + }; + + var gmlReadODataSpatialValue = function (domElement, isGeography) { + /// Reads the value of a GML DOM element a spatial value in an OData XML document. + /// DOM element. + /// Flag indicating if the value uses a geographic reference system or not. + /// + /// When using a geographic reference system, the first component of all the coordinates in each element in the GML DOM tree is the Latitude and + /// will be deserialized as the second component of each position coordinates in the resulting GeoJSON object. + /// + /// Array containing an array of doubles for each coordinate of the polygon. + + var localName = xmlLocalName(domElement); + var reader; + + switch (localName) { + case "Point": + reader = gmlReadODataPoint; + break; + case "Polygon": + reader = gmlReadODataPolygon; + break; + case "LineString": + reader = gmlReadODataLineString; + break; + case "MultiPoint": + reader = gmlReadODataMultiPoint; + break; + case "MultiCurve": + reader = gmlReadODataMultiLineString; + break; + case "MultiSurface": + reader = gmlReadODataMultiPolygon; + break; + case "MultiGeometry": + reader = gmlReadODataCollection; + break; + default: + throw { message: "Unsupported element: " + localName, element: domElement }; + } + + var value = reader(domElement, isGeography); + // Read the CRS + // WCF Data Services qualifies the srsName attribute withing the GML namespace; however + // other end points might no do this as per the standard. + + var srsName = xmlAttributeValue(domElement, "srsName", gmlXmlNs) || + xmlAttributeValue(domElement, "srsName"); + + if (srsName) { + if (srsName.indexOf(gmlSrsPrefix) !== 0) { + throw { message: "Unsupported srs name: " + srsName, element: domElement }; + } + + var crsId = srsName.substring(gmlSrsPrefix.length); + if (crsId) { + value.crs = { + type: "name", + properties: { + name: "EPSG:" + crsId + } + }; + } + } + return value; + }; + + var gmlNewODataSpatialValue = function (dom, value, type, isGeography) { + /// Creates a new GML DOM element for the value of an OData spatial property or GeoJSON object. + /// DOM document used for creating the new DOM Element. + /// Spatial property value in GeoJSON format. + /// String indicating the GeoJSON type of the value to serialize. + /// Flag indicating if the value uses a geographic reference system or not. + /// + /// When using a geographic reference system, the first component of all the coordinates in the GeoJSON value is the Longitude and + /// will be serialized as the second component of each element in the GML DOM tree. + /// + /// New DOM element in the GML namespace for the spatial value. + + var gmlWriter; + + switch (type) { + case GEOJSON_POINT: + gmlWriter = gmlNewODataPoint; + break; + case GEOJSON_LINESTRING: + gmlWriter = gmlNewODataLineString; + break; + case GEOJSON_POLYGON: + gmlWriter = gmlNewODataPolygon; + break; + case GEOJSON_MULTIPOINT: + gmlWriter = gmlNewODataMultiPoint; + break; + case GEOJSON_MULTILINESTRING: + gmlWriter = gmlNewODataMultiLineString; + break; + case GEOJSON_MULTIPOLYGON: + gmlWriter = gmlNewODataMultiPolygon; + break; + case GEOJSON_GEOMETRYCOLLECTION: + gmlWriter = gmlNewODataGeometryCollection; + break; + default: + return null; + } + + var gml = gmlWriter(dom, value, isGeography); + + // Set the srsName attribute if applicable. + var crs = value.crs; + if (crs) { + if (crs.type === "name") { + var properties = crs.properties; + var name = properties && properties.name; + if (name && name.indexOf("ESPG:") === 0 && name.length > 5) { + var crsId = name.substring(5); + var srsName = xmlNewAttribute(dom, null, "srsName", gmlPrefix + crsId); + xmlAppendChild(gml, srsName); + } + } + } + + return gml; + }; + + var gmlNewODataElement = function (dom, name, children) { + /// Creates a new DOM element in the GML namespace. + /// DOM document used for creating the new DOM Element. + /// Local name of the GML element to create. + /// Array containing DOM nodes or string values that will be added as children of the new DOM element. + /// New DOM element in the GML namespace. + /// + /// If a value in the children collection is a string, then a new DOM text node is going to be created + /// for it and then appended as a child of the new DOM Element. + /// + + return xmlNewElement(dom, gmlXmlNs, xmlQualifiedName(gmlPrefix, name), children); + }; + + var gmlNewODataPosElement = function (dom, coordinates, isGeography) { + /// Creates a new GML pos DOM element. + /// DOM document used for creating the new DOM Element. + /// Array of doubles describing the coordinates of the pos element. + /// Flag indicating if the coordinates use a geographic reference system or not. + /// + /// When using a geographic reference system, the first coordinate is the Longitude and + /// will be serialized as the second component of the element in the GML DOM tree. + /// + /// New pos DOM element in the GML namespace. + + var posValue = isArray(coordinates) ? coordinates : []; + + // If using a geographic reference system, then the first coordinate is the longitude and it has to + // swapped with the latitude. + posValue = isGeography ? gmlSwapLatLong(posValue) : posValue; + + return gmlNewODataElement(dom, "pos", posValue.join(" ")); + }; + + var gmlNewODataLineElement = function (dom, name, coordinates, isGeography) { + /// Creates a new GML DOM element representing a line. + /// DOM document used for creating the new DOM Element. + /// Name of the element to create. + /// Array of array of doubles describing the coordinates of the line element. + /// Flag indicating if the coordinates use a geographic reference system or not. + /// + /// When using a geographic reference system, the first component of all the coordinates is the Longitude and + /// will be serialized as the second component of each element in the GML DOM tree. + /// + /// New DOM element in the GML namespace. + + var element = gmlNewODataElement(dom, name); + if (isArray(coordinates)) { + var i, len; + for (i = 0, len = coordinates.length; i < len; i++) { + xmlAppendChild(element, gmlNewODataPosElement(dom, coordinates[i], isGeography)); + } + + if (len === 0) { + xmlAppendChild(element, gmlNewODataElement(dom, "posList")); + } + } + return element; + }; + + var gmlNewODataPointElement = function (dom, coordinates, isGeography) { + /// Creates a new GML Point DOM element. + /// DOM document used for creating the new DOM Element. + /// GeoJSON Point object. + /// Flag indicating if the value uses a geographic reference system or not. + /// + /// When using a geographic reference system, the first component of all the coordinates in the GeoJSON value is the Longitude and + /// will be serialized as the second component of each element in the GML DOM tree. + /// + /// New DOM element in the GML namespace for the GeoJSON Point. + + return gmlNewODataElement(dom, "Point", gmlNewODataPosElement(dom, coordinates, isGeography)); + }; + + var gmlNewODataLineStringElement = function (dom, coordinates, isGeography) { + /// Creates a new GML LineString DOM element. + /// DOM document used for creating the new DOM Element. + /// Array of array of doubles describing the coordinates of the line element. + /// Flag indicating if the value uses a geographic reference system or not. + /// + /// When using a geographic reference system, the first component of all the coordinates in the GeoJSON value is the Longitude and + /// will be serialized as the second component of each element in the GML DOM tree. + /// + /// New DOM element in the GML namespace for the GeoJSON LineString. + + return gmlNewODataLineElement(dom, "LineString", coordinates, isGeography); + }; + + var gmlNewODataPolygonRingElement = function (dom, name, coordinates, isGeography) { + /// Creates a new GML DOM element representing a polygon ring. + /// DOM document used for creating the new DOM Element. + /// Name of the element to create. + /// Array of array of doubles describing the coordinates of the polygon ring. + /// Flag indicating if the coordinates use a geographic reference system or not. + /// + /// When using a geographic reference system, the first component of all the coordinates is the Longitude and + /// will be serialized as the second component of each element in the GML DOM tree. + /// + /// New DOM element in the GML namespace. + + var ringElement = gmlNewODataElement(dom, name); + if (isArray(coordinates) && coordinates.length > 0) { + var linearRing = gmlNewODataLineElement(dom, "LinearRing", coordinates, isGeography); + xmlAppendChild(ringElement, linearRing); + } + return ringElement; + }; + + var gmlNewODataPolygonElement = function (dom, coordinates, isGeography) { + /// Creates a new GML Polygon DOM element for a GeoJSON Polygon object. + /// DOM document used for creating the new DOM Element. + /// Array of array of array of doubles describing the coordinates of the polygon. + /// Flag indicating if the value uses a geographic reference system or not. + /// + /// When using a geographic reference system, the first component of all the coordinates in the GeoJSON value is the Longitude and + /// will be serialized as the second component of each element in the GML DOM tree. + /// + /// New DOM element in the GML namespace. + + var len = coordinates && coordinates.length; + var element = gmlNewODataElement(dom, "Polygon"); + + if (isArray(coordinates) && len > 0) { + xmlAppendChild(element, gmlNewODataPolygonRingElement(dom, "exterior", coordinates[0], isGeography)); + + var i; + for (i = 1; i < len; i++) { + xmlAppendChild(element, gmlNewODataPolygonRingElement(dom, "interior", coordinates[i], isGeography)); + } + } + return element; + }; + + var gmlNewODataPoint = function (dom, value, isGeography) { + /// Creates a new GML Point DOM element for a GeoJSON Point object. + /// DOM document used for creating the new DOM Element. + /// GeoJSON Point object. + /// Flag indicating if the value uses a geographic reference system or not. + /// + /// When using a geographic reference system, the first component of all the coordinates in the GeoJSON value is the Longitude and + /// will be serialized as the second component of each element in the GML DOM tree. + /// + /// New DOM element in the GML namespace for the GeoJSON Point. + + return gmlNewODataPointElement(dom, value.coordinates, isGeography); + }; + + var gmlNewODataLineString = function (dom, value, isGeography) { + /// Creates a new GML LineString DOM element for a GeoJSON LineString object. + /// DOM document used for creating the new DOM Element. + /// GeoJSON LineString object. + /// Flag indicating if the value uses a geographic reference system or not. + /// + /// When using a geographic reference system, the first component of all the coordinates in the GeoJSON value is the Longitude and + /// will be serialized as the second component of each element in the GML DOM tree. + /// + /// New DOM element in the GML namespace for the GeoJSON LineString. + + return gmlNewODataLineStringElement(dom, value.coordinates, isGeography); + }; + + var gmlNewODataPolygon = function (dom, value, isGeography) { + /// Creates a new GML Polygon DOM element for a GeoJSON Polygon object. + /// DOM document used for creating the new DOM Element. + /// GeoJSON Polygon object. + /// Flag indicating if the value uses a geographic reference system or not. + /// + /// When using a geographic reference system, the first component of all the coordinates in the GeoJSON value is the Longitude and + /// will be serialized as the second component of each element in the GML DOM tree. + /// + /// New DOM element in the GML namespace for the GeoJSON Polygon. + + return gmlNewODataPolygonElement(dom, value.coordinates, isGeography); + }; + + var gmlNewODataMultiItem = function (dom, name, members, items, itemWriter, isGeography) { + /// Creates a new GML DOM element for a composite structure like a multi-point or a multi-geometry. + /// DOM document used for creating the new DOM Element. + /// Name of the element to create. + /// Array of items in the composite structure. + /// Flag indicating if the multi-item uses a geographic reference system or not. + /// + /// When using a geographic reference system, the first component of all the coordinates in each of the items is the Longitude and + /// will be serialized as the second component of each element in the GML DOM tree. + /// + /// New DOM element in the GML namespace. + + var len = items && items.length; + var element = gmlNewODataElement(dom, name); + + if (isArray(items) && len > 0) { + var membersElement = gmlNewODataElement(dom, members); + var i; + for (i = 0; i < len; i++) { + xmlAppendChild(membersElement, itemWriter(dom, items[i], isGeography)); + } + xmlAppendChild(element, membersElement); + } + return element; + }; + + var gmlNewODataMultiPoint = function (dom, value, isGeography) { + /// Creates a new GML MultiPoint DOM element for a GeoJSON MultiPoint object. + /// DOM document used for creating the new DOM Element. + /// GeoJSON MultiPoint object. + /// Flag indicating if the value uses a geographic reference system or not. + /// + /// When using a geographic reference system, the first component of all the coordinates in the GeoJSON value is the Longitude and + /// will be serialized as the second component of each element in the GML DOM tree. + /// + /// New DOM element in the GML namespace for the GeoJSON MultiPoint. + + return gmlNewODataMultiItem(dom, "MultiPoint", "pointMembers", value.coordinates, gmlNewODataPointElement, isGeography); + }; + + var gmlNewODataMultiLineString = function (dom, value, isGeography) { + /// Creates a new GML MultiCurve DOM element for a GeoJSON MultiLineString object. + /// DOM document used for creating the new DOM Element. + /// GeoJSON MultiLineString object. + /// Flag indicating if the value uses a geographic reference system or not. + /// + /// When using a geographic reference system, the first component of all the coordinates in the GeoJSON value is the Longitude and + /// will be serialized as the second component of each element in the GML DOM tree. + /// + /// New DOM element in the GML namespace for the GeoJSON MultiLineString. + + return gmlNewODataMultiItem(dom, "MultiCurve", "curveMembers", value.coordinates, gmlNewODataLineStringElement, isGeography); + }; + + var gmlNewODataMultiPolygon = function (dom, value, isGeography) { + /// Creates a new GML MultiSurface DOM element for a GeoJSON MultiPolygon object. + /// DOM document used for creating the new DOM Element. + /// GeoJSON MultiPolygon object. + /// Flag indicating if the value uses a geographic reference system or not. + /// + /// When using a geographic reference system, the first component of all the coordinates in the GeoJSON value is the Longitude and + /// will be serialized as the second component of each element in the GML DOM tree. + /// + /// New DOM element in the GML namespace for the GeoJSON MultiPolygon. + + return gmlNewODataMultiItem(dom, "MultiSurface", "surfaceMembers", value.coordinates, gmlNewODataPolygonElement, isGeography); + }; + + var gmlNewODataGeometryCollectionItem = function (dom, value, isGeography) { + /// Creates a new GML element for an item in a geometry collection object. + /// DOM document used for creating the new DOM Element. + /// GeoJSON object in the geometry collection. + /// Flag indicating if the value uses a geographic reference system or not. + /// + /// When using a geographic reference system, the first component of all the coordinates in the GeoJSON value is the Longitude and + /// will be serialized as the second component of each element in the GML DOM tree. + /// + /// New DOM element in the GML namespace. + + return gmlNewODataSpatialValue(dom, value, value.type, isGeography); + }; + + var gmlNewODataGeometryCollection = function (dom, value, isGeography) { + /// Creates a new GML MultiGeometry DOM element for a GeoJSON GeometryCollection object. + /// DOM document used for creating the new DOM Element. + /// GeoJSON GeometryCollection object. + /// Flag indicating if the value uses a geographic reference system or not. + /// + /// When using a geographic reference system, the first component of all the coordinates in the GeoJSON value is the Longitude and + /// will be serialized as the second component of each element in the GML DOM tree. + /// + /// New DOM element in the GML namespace for the GeoJSON GeometryCollection. + + return gmlNewODataMultiItem(dom, "MultiGeometry", "geometryMembers", value.geometries, gmlNewODataGeometryCollectionItem, isGeography); + }; + + + + var xmlMediaType = "application/xml"; + + var ado = http + "schemas.microsoft.com/ado/"; // http://schemas.microsoft.com/ado/ + var adoDs = ado + "2007/08/dataservices"; // http://schemas.microsoft.com/ado/2007/08/dataservices + + var edmxNs = ado + "2007/06/edmx"; // http://schemas.microsoft.com/ado/2007/06/edmx + var edmNs1 = ado + "2006/04/edm"; // http://schemas.microsoft.com/ado/2006/04/edm + var edmNs1_1 = ado + "2007/05/edm"; // http://schemas.microsoft.com/ado/2007/05/edm + var edmNs1_2 = ado + "2008/01/edm"; // http://schemas.microsoft.com/ado/2008/01/edm + + // There are two valid namespaces for Edm 2.0 + var edmNs2a = ado + "2008/09/edm"; // http://schemas.microsoft.com/ado/2008/09/edm + var edmNs2b = ado + "2009/08/edm"; // http://schemas.microsoft.com/ado/2009/08/edm + + var edmNs3 = ado + "2009/11/edm"; // http://schemas.microsoft.com/ado/2009/11/edm + + var odataXmlNs = adoDs; // http://schemas.microsoft.com/ado/2007/08/dataservices + var odataMetaXmlNs = adoDs + "/metadata"; // http://schemas.microsoft.com/ado/2007/08/dataservices/metadata + var odataRelatedPrefix = adoDs + "/related/"; // http://schemas.microsoft.com/ado/2007/08/dataservices/related + var odataScheme = adoDs + "/scheme"; // http://schemas.microsoft.com/ado/2007/08/dataservices/scheme + + var odataPrefix = "d"; + var odataMetaPrefix = "m"; + + var createAttributeExtension = function (domNode, useNamespaceURI) { + /// Creates an extension object for the specified attribute. + /// DOM node for the attribute. + /// Flag indicating if the namespaceURI property should be added to the extension object instead of the namespace property. + /// + /// The useNamespaceURI flag is used to prevent a breaking change from older versions of datajs in which extension + /// objects created for Atom extension attributes have the namespaceURI property instead of the namespace one. + /// + /// This flag and the namespaceURI property should be deprecated in future major versions of the library. + /// + /// The new extension object. + + var extension = { name: xmlLocalName(domNode), value: domNode.value }; + extension[useNamespaceURI ? "namespaceURI" : "namespace"] = xmlNamespaceURI(domNode); + + return extension; + }; + + var createElementExtension = function (domNode, useNamespaceURI) { + /// Creates an extension object for the specified element. + /// DOM node for the element. + /// Flag indicating if the namespaceURI property should be added to the extension object instead of the namespace property. + /// + /// The useNamespaceURI flag is used to prevent a breaking change from older versions of datajs in which extension + /// objects created for Atom extension attributes have the namespaceURI property instead of the namespace one. + /// + /// This flag and the namespaceURI property should be deprecated in future major versions of the library. + /// + /// The new extension object. + + + var attributeExtensions = []; + var childrenExtensions = []; + + var i, len; + var attributes = domNode.attributes; + for (i = 0, len = attributes.length; i < len; i++) { + var attr = attributes[i]; + if (xmlNamespaceURI(attr) !== xmlnsNS) { + attributeExtensions.push(createAttributeExtension(attr, useNamespaceURI)); + } + } + + var child = domNode.firstChild; + while (child != null) { + if (child.nodeType === 1) { + childrenExtensions.push(createElementExtension(child, useNamespaceURI)); + } + child = child.nextSibling; + }; + + var extension = { + name: xmlLocalName(domNode), + value: xmlInnerText(domNode), + attributes: attributeExtensions, + children: childrenExtensions + }; + + extension[useNamespaceURI ? "namespaceURI" : "namespace"] = xmlNamespaceURI(domNode); + return extension; + }; + + var isCollectionItemElement = function (domElement) { + /// Checks whether the domElement is a collection item. + /// DOM element possibliy represnting a collection item. + /// True if the domeElement belongs to the OData metadata namespace and its local name is "element"; false otherwise. + + return xmlNamespaceURI(domElement) === odataXmlNs && xmlLocalName(domElement) === "element"; + }; + + var makePropertyMetadata = function (type, extensions) { + /// Creates an object containing property metadata. + /// Property type name. + /// Array of attribute extension objects. + /// Property metadata object cotaining type and extensions fields. + + return { type: type, extensions: extensions }; + }; + + var odataInferTypeFromPropertyXmlDom = function (domElement) { + /// Infers type of a property based on its xml DOM tree. + /// DOM element for the property. + /// Inferred type name; null if the type cannot be determined. + + if (xmlFirstChildElement(domElement, gmlXmlNs)) { + return EDM_GEOMETRY; + } + + var firstChild = xmlFirstChildElement(domElement, odataXmlNs); + if (!firstChild) { + return EDM_STRING; + } + + if (isCollectionItemElement(firstChild)) { + var sibling = xmlSiblingElement(firstChild, odataXmlNs); + if (sibling && isCollectionItemElement(sibling)) { + // More than one tag have been found, it can be safely assumed that this is a collection property. + return "Collection()"; + } + } + + return null; + }; + + var xmlReadODataPropertyAttributes = function (domElement) { + /// Reads the attributes of a property DOM element in an OData XML document. + /// DOM element for the property. + /// Object containing the property type, if it is null, and its attribute extensions. + + var type = null; + var isNull = false; + var extensions = []; + + xmlAttributes(domElement, function (attribute) { + var nsURI = xmlNamespaceURI(attribute); + var localName = xmlLocalName(attribute); + var value = xmlNodeValue(attribute); + + if (nsURI === odataMetaXmlNs) { + if (localName === "null") { + isNull = (value.toLowerCase() === "true"); + return; + } + + if (localName === "type") { + type = value; + return; + } + } + + if (nsURI !== xmlNS && nsURI !== xmlnsNS) { + extensions.push(createAttributeExtension(attribute, true)); + return; + } + }); + + return { type: (!type && isNull ? EDM_STRING : type), isNull: isNull, extensions: extensions }; + }; + + var xmlReadODataProperty = function (domElement) { + /// Reads a property DOM element in an OData XML document. + /// DOM element for the property. + /// Object with name, value, and metadata for the property. + + if (xmlNamespaceURI(domElement) !== odataXmlNs) { + // domElement is not a proprety element because it is not in the odata xml namespace. + return null; + }; + + var propertyName = xmlLocalName(domElement); + var propertyAttributes = xmlReadODataPropertyAttributes(domElement); + + var propertyIsNull = propertyAttributes.isNull; + var propertyType = propertyAttributes.type; + + var propertyMetadata = makePropertyMetadata(propertyType, propertyAttributes.extensions); + var propertyValue = propertyIsNull ? null : xmlReadODataPropertyValue(domElement, propertyType, propertyMetadata); + + return { name: propertyName, value: propertyValue, metadata: propertyMetadata }; + }; + + var xmlReadODataPropertyValue = function (domElement, propertyType, propertyMetadata) { + /// Reads the value of a property in an OData XML document. + /// DOM element for the property. + /// Property type name. + /// Object that will store metadata about the property. + /// Property value. + + if (!propertyType) { + propertyType = odataInferTypeFromPropertyXmlDom(domElement); + propertyMetadata.type = propertyType; + } + + var isGeograhpyType = isGeographyEdmType(propertyType); + if (isGeograhpyType || isGeometryEdmType(propertyType)) { + return xmlReadODataSpatialPropertyValue(domElement, propertyType, isGeograhpyType); + } + + if (isPrimitiveEdmType(propertyType)) { + return xmlReadODataEdmPropertyValue(domElement, propertyType); + } + + if (isCollectionType(propertyType)) { + return xmlReadODataCollectionPropertyValue(domElement, propertyType, propertyMetadata); + } + + return xmlReadODataComplexPropertyValue(domElement, propertyType, propertyMetadata); + }; + + var xmlReadODataSpatialPropertyValue = function (domElement, propertyType, isGeography) { + /// Reads the value of an spatial property in an OData XML document. + /// DOM element for the spatial property. + /// Property type name. + /// Flag indicating if the value uses a geographic reference system or not. + /// + /// When using a geographic reference system, the first component of all the coordinates in each element in the GML DOM tree is the Latitude and + /// will be deserialized as the second component of each element in the GML DOM tree. + /// + /// Spatial property value in GeoJSON format. + + var gmlRoot = xmlFirstChildElement(domElement, gmlXmlNs); + + var value = gmlReadODataSpatialValue(gmlRoot, isGeography); + value.__metadata = { type: propertyType }; + return value; + }; + + var xmlReadODataEdmPropertyValue = function (domNode, propertyType) { + /// Reads the value of an EDM property in an OData XML document. + /// DOM node for the EDM property. + /// Property type name. + /// EDM property value. + + var propertyValue = xmlNodeValue(domNode) || ""; + + switch (propertyType) { + case EDM_BOOLEAN: + return parseBool(propertyValue); + case EDM_BINARY: + case EDM_DECIMAL: + case EDM_GUID: + case EDM_INT64: + case EDM_STRING: + return propertyValue; + case EDM_BYTE: + case EDM_INT16: + case EDM_INT32: + case EDM_SBYTE: + return parseInt10(propertyValue); + case EDM_DOUBLE: + case EDM_SINGLE: + return parseFloat(propertyValue); + case EDM_TIME: + return parseDuration(propertyValue); + case EDM_DATETIME: + return parseDateTime(propertyValue); + case EDM_DATETIMEOFFSET: + return parseDateTimeOffset(propertyValue); + }; + + return propertyValue; + }; + + var xmlReadODataComplexPropertyValue = function (domElement, propertyType, propertyMetadata) { + /// Reads the value of a complex type property in an OData XML document. + /// DOM element for the complex type property. + /// Property type name. + /// Object that will store metadata about the property. + /// Complex type property value. + + var propertyValue = { __metadata: { type: propertyType} }; + xmlChildElements(domElement, function (child) { + var childProperty = xmlReadODataProperty(child); + var childPropertyName = childProperty.name; + + propertyMetadata.properties = propertyMetadata.properties || {}; + propertyMetadata.properties[childPropertyName] = childProperty.metadata; + propertyValue[childPropertyName] = childProperty.value; + }); + + return propertyValue; + } + + var xmlReadODataCollectionPropertyValue = function (domElement, propertyType, propertyMetadata) { + /// Reads the value of a collection property in an OData XML document. + /// DOM element for the collection property. + /// Property type name. + /// Object that will store metadata about the property. + /// Collection property value. + + var items = []; + var itemsMetadata = propertyMetadata.elements = []; + var collectionType = getCollectionType(propertyType); + + xmlChildElements(domElement, function (child) { + if (isCollectionItemElement(child)) { + var itemAttributes = xmlReadODataPropertyAttributes(child); + var itemExtensions = itemAttributes.extensions; + var itemType = itemAttributes.type || collectionType; + var itemMetadata = makePropertyMetadata(itemType, itemExtensions); + + var item = xmlReadODataPropertyValue(child, itemType, itemMetadata); + + items.push(item); + itemsMetadata.push(itemMetadata); + } + }); + + return { __metadata: { type: propertyType === "Collection()" ? null : propertyType }, results: items }; + }; + + var readODataXmlDocument = function (xmlRoot, baseURI) { + /// Reads an OData link(s) producing an object model in return. + /// Top-level element to read. + /// Base URI for normalizing relative URIs found in the XML payload. + /// The object model representing the specified element. + + if (xmlNamespaceURI(xmlRoot) === odataXmlNs) { + baseURI = xmlBaseURI(xmlRoot, baseURI); + var localName = xmlLocalName(xmlRoot); + + if (localName === "links") { + return readLinks(xmlRoot, baseURI); + } + if (localName === "uri") { + return readUri(xmlRoot, baseURI); + } + } + return undefined; + }; + + var readLinks = function (linksElement, baseURI) { + /// Deserializes an OData XML links element. + /// XML links element. + /// Base URI for normalizing relative URIs found in the XML payload. + /// A new object representing the links collection. + + var uris = []; + + xmlChildElements(linksElement, function (child) { + if (xmlLocalName(child) === "uri" && xmlNamespaceURI(child) === odataXmlNs) { + uris.push(readUri(child, baseURI)); + } + }); + + return { results: uris }; + }; + + var readUri = function (uriElement, baseURI) { + /// Deserializes an OData XML uri element. + /// XML uri element. + /// Base URI for normalizing relative URIs found in the XML payload. + /// A new object representing the uri. + + var uri = xmlInnerText(uriElement) || ""; + return { uri: normalizeURI(uri, baseURI) }; + }; + + var xmlODataInferSpatialValueGeoJsonType = function (value, edmType) { + /// Infers the GeoJSON type from the spatial property value and the edm type name. + /// Spatial property value in GeoJSON format. + /// Spatial property edm type. + /// + /// If the edmType parameter is null, undefined, "Edm.Geometry" or "Edm.Geography", then the function returns + /// the GeoJSON type indicated by the value's type property. + /// + /// If the edmType parameter is specified or is not one of the base spatial types, then it is used to + /// determine the GeoJSON type and the value's type property is ignored. + /// + /// New DOM element in the GML namespace for the spatial value. + + if (edmType === EDM_GEOMETRY || edmType === EDM_GEOGRAPHY) { + return value && value.type; + } + + if (edmType === EDM_GEOMETRY_POINT || edmType === EDM_GEOGRAPHY_POINT) { + return GEOJSON_POINT; + } + + if (edmType === EDM_GEOMETRY_LINESTRING || edmType === EDM_GEOGRAPHY_LINESTRING) { + return GEOJSON_LINESTRING; + } + + if (edmType === EDM_GEOMETRY_POLYGON || edmType === EDM_GEOGRAPHY_POLYGON) { + return GEOJSON_POLYGON; + } + + if (edmType === EDM_GEOMETRY_COLLECTION || edmType === EDM_GEOGRAPHY_COLLECTION) { + return GEOJSON_GEOMETRYCOLLECTION; + } + + if (edmType === EDM_GEOMETRY_MULTIPOLYGON || edmType === EDM_GEOGRAPHY_MULTIPOLYGON) { + return GEOJSON_MULTIPOLYGON; + } + + if (edmType === EDM_GEOMETRY_MULTILINESTRING || edmType === EDM_GEOGRAPHY_MULTILINESTRING) { + return GEOJSON_MULTILINESTRING; + } + + if (edmType === EDM_GEOMETRY_MULTIPOINT || edmType === EDM_GEOGRAPHY_MULTIPOINT) { + return GEOJSON_MULTIPOINT; + } + + return null; + }; + + var xmlNewODataMetaElement = function (dom, name, children) { + /// Creates a new DOM element in the OData metadata namespace. + /// DOM document used for creating the new DOM Element. + /// Local name of the OData metadata element to create. + /// Array containing DOM nodes or string values that will be added as children of the new DOM element. + /// New DOM element in the OData metadata namespace. + /// + /// If a value in the children collection is a string, then a new DOM text node is going to be created + /// for it and then appended as a child of the new DOM Element. + /// + + return xmlNewElement(dom, odataMetaXmlNs, xmlQualifiedName(odataMetaPrefix, name), children); + }; + + var xmlNewODataMetaAttribute = function (dom, name, value) { + /// Creates a new DOM attribute in the odata namespace. + /// DOM document used for creating the new DOM Element. + /// Local name of the OData attribute to create. + /// Attribute value. + /// New DOM attribute in the odata namespace. + + return xmlNewAttribute(dom, odataMetaXmlNs, xmlQualifiedName(odataMetaPrefix, name), value); + }; + + var xmlNewODataElement = function (dom, name, children) { + /// Creates a new DOM element in the OData namespace. + /// DOM document used for creating the new DOM Element. + /// Local name of the OData element to create. + /// Array containing DOM nodes or string values that will be added as children of the new DOM element. + /// New DOM element in the OData namespace. + /// + /// If a value in the children collection is a string, then a new DOM text node is going to be created + /// for it and then appended as a child of the new DOM Element. + /// + + return xmlNewElement(dom, odataXmlNs, xmlQualifiedName(odataPrefix, name), children); + }; + + var xmlNewODataPrimitiveValue = function (value, typeName) { + /// Returns the string representation of primitive value for an OData XML document. + /// Primivite value to format. + /// Type name of the primitive value. + /// Formatted primitive value. + + if (typeName === EDM_DATETIME || typeName === EDM_DATETIMEOFFSET || isDate(value)) { + return formatDateTimeOffset(value); + } + if (typeName === EDM_TIME) { + return formatDuration(value); + } + return value.toString(); + }; + + var xmlNewODataElementInfo = function (domElement, dataServiceVersion) { + /// Creates an object that represents a new DOM element for an OData XML document and the data service version it requires. + /// New DOM element for an OData XML document. + /// Required data service version by the new DOM element. + /// Object containing new DOM element and its required data service version. + + return { element: domElement, dsv: dataServiceVersion }; + }; + + var xmlNewODataProperty = function (dom, name, typeName, children) { + /// Creates a new DOM element for an entry property in an OData XML document. + /// DOM document used for creating the new DOM Element. + /// Property name. + /// Property type name. + /// Array containing DOM nodes or string values that will be added as children of the new DOM element. + /// + /// If a value in the children collection is a string, then a new DOM text node is going to be created + /// for it and then appended as a child of the new DOM Element. + /// + /// New DOM element in the OData namespace for the entry property. + + var typeAttribute = typeName ? xmlNewODataMetaAttribute(dom, "type", typeName) : null; + var property = xmlNewODataElement(dom, name, typeAttribute); + return xmlAppendChildren(property, children); + }; + + var xmlNewODataEdmProperty = function (dom, name, value, typeName) { + /// Creates a new DOM element for an EDM property in an OData XML document. + /// DOM document used for creating the new DOM Element. + /// Property name. + /// Property value. + /// Property type name. + /// + /// Object containing the new DOM element in the OData namespace for the EDM property and the + /// required data service version for this property. + /// + + var propertyValue = xmlNewODataPrimitiveValue(value, typeName); + var property = xmlNewODataProperty(dom, name, typeName, propertyValue); + return xmlNewODataElementInfo(property, /*dataServiceVersion*/"1.0"); + }; + + var xmlNewODataNullProperty = function (dom, name, typeName, model) { + /// Creates a new DOM element for a null property in an OData XML document. + /// DOM document used for creating the new DOM Element. + /// Property name. + /// Property type name. + /// Object describing an OData conceptual schema. + /// + /// If no typeName is specified, then it will be assumed that this is a primitive type property. + /// + /// + /// Object containing the new DOM element in the OData namespace for the null property and the + /// required data service version for this property. + /// + + var nullAttribute = xmlNewODataMetaAttribute(dom, "null", "true"); + var property = xmlNewODataProperty(dom, name, typeName, nullAttribute); + var dataServiceVersion = lookupComplexType(typeName, model) ? "2.0" : "1.0"; + + return xmlNewODataElementInfo(property, dataServiceVersion); + }; + + var xmlNewODataCollectionProperty = function (dom, name, value, typeName, collectionMetadata, collectionModel, model) { + /// Creates a new DOM element for a collection property in an OData XML document. + /// DOM document used for creating the new DOM Element. + /// Property name. + /// Property value either as an array or an object representing a collection in the library's internal representation. + /// Property type name. + /// Object containing metadata about the collection property. + /// Object describing the collection property in an OData conceptual schema. + /// Object describing an OData conceptual schema. + /// + /// Object containing the new DOM element in the OData namespace for the collection property and the + /// required data service version for this property. + /// + + var itemTypeName = getCollectionType(typeName); + var items = isArray(value) ? value : value.results; + var itemMetadata = typeName ? { type: itemTypeName} : {}; + itemMetadata.properties = collectionMetadata.properties; + + var xmlProperty = xmlNewODataProperty(dom, name, itemTypeName ? typeName : null); + + var i, len; + for (i = 0, len = items.length; i < len; i++) { + var itemValue = items[i]; + var item = xmlNewODataDataElement(dom, "element", itemValue, itemMetadata, collectionModel, model); + + xmlAppendChild(xmlProperty, item.element); + } + return xmlNewODataElementInfo(xmlProperty, /*dataServiceVersion*/"3.0"); + }; + + var xmlNewODataComplexProperty = function (dom, name, value, typeName, propertyMetadata, propertyModel, model) { + /// Creates a new DOM element for a complex type property in an OData XML document. + /// DOM document used for creating the new DOM Element. + /// Property name. + /// Property value as an object in the library's internal representation. + /// Property type name. + /// Object containing metadata about the complex type property. + /// Object describing the complex type property in an OData conceptual schema. + /// Object describing an OData conceptual schema. + /// + /// Object containing the new DOM element in the OData namespace for the complex type property and the + /// required data service version for this property. + /// + + var xmlProperty = xmlNewODataProperty(dom, name, typeName); + var complexTypePropertiesMetadata = propertyMetadata.properties || {}; + var complexTypeModel = lookupComplexType(typeName, model) || {}; + + var dataServiceVersion = "1.0"; + + for (var key in value) { + if (key !== "__metadata") { + var memberValue = value[key]; + var memberModel = lookupProperty(complexTypeModel.property, key); + var memberMetadata = complexTypePropertiesMetadata[key] || {}; + var member = xmlNewODataDataElement(dom, key, memberValue, memberMetadata, memberModel, model); + + dataServiceVersion = maxVersion(dataServiceVersion, member.dsv); + xmlAppendChild(xmlProperty, member.element); + } + }; + return xmlNewODataElementInfo(xmlProperty, dataServiceVersion); + }; + + var xmlNewODataSpatialProperty = function (dom, name, value, typeName, isGeography) { + /// Creates a new DOM element for an EDM spatial property in an OData XML document. + /// DOM document used for creating the new DOM Element. + /// Property name. + /// GeoJSON object containing the property value. + /// Property type name. + /// + /// Object containing the new DOM element in the OData namespace for the EDM property and the + /// required data service version for this property. + /// + + var geoJsonType = xmlODataInferSpatialValueGeoJsonType(value, typeName); + + var gmlRoot = gmlNewODataSpatialValue(dom, value, geoJsonType, isGeography); + var xmlProperty = xmlNewODataProperty(dom, name, typeName, gmlRoot); + + return xmlNewODataElementInfo(xmlProperty, "3.0"); + }; + + var xmlNewODataDataElement = function (dom, name, value, dataItemMetadata, dataItemModel, model) { + /// Creates a new DOM element for a data item in an entry, complex property, or collection property. + /// DOM document used for creating the new DOM Element. + /// Data item name. + /// Value of the data item, if any. + /// Object containing metadata about the data item. + /// Object describing the data item in an OData conceptual schema. + /// Object describing an OData conceptual schema. + /// + /// Object containing the new DOM element in the appropriate namespace for the data item and the + /// required data service version for it. + /// + + var typeName = dataItemTypeName(value, dataItemMetadata, dataItemModel); + if (isPrimitive(value)) { + return xmlNewODataEdmProperty(dom, name, value, typeName || EDM_STRING); + } + + var isGeography = isGeographyEdmType(typeName); + if (isGeography || isGeometryEdmType(typeName)) { + return xmlNewODataSpatialProperty(dom, name, value, typeName, isGeography); + } + + if (isCollection(value, typeName)) { + return xmlNewODataCollectionProperty(dom, name, value, typeName, dataItemMetadata, dataItemModel, model); + } + + if (isNamedStream(value)) { + return null; + } + + // This may be a navigation property. + var navPropKind = navigationPropertyKind(value, dataItemModel); + if (navPropKind !== null) { + return null; + } + + if (value === null) { + return xmlNewODataNullProperty(dom, name, typeName); + } + + return xmlNewODataComplexProperty(dom, name, value, typeName, dataItemMetadata, dataItemModel, model); + }; + + var odataNewLinkDocument = function (data) { + /// Writes the specified data into an OData XML document. + /// Data to write. + /// The root of the DOM tree built. + + if (data && isObject(data)) { + var dom = xmlDom(); + return xmlAppendChild(dom, xmlNewODataElement(dom, "uri", data.uri)); + } + // Allow for undefined to be returned. + }; + + var xmlParser = function (handler, text) { + /// Parses an OData XML document. + /// This handler. + /// Document text. + /// An object representation of the document; undefined if not applicable. + + if (text) { + var doc = xmlParse(text); + var root = xmlFirstChildElement(doc); + if (root) { + return readODataXmlDocument(root); + } + } + + // Allow for undefined to be returned. + }; + + var xmlSerializer = function (handler, data, context) { + /// Serializes an OData XML object into a document. + /// This handler. + /// Representation of feed or entry. + /// Object with parsing context. + /// A text representation of the data object; undefined if not applicable. + + var cType = context.contentType = context.contentType || contentType(xmlMediaType); + if (cType && cType.mediaType === xmlMediaType) { + return xmlSerialize(odataNewLinkDocument(data)); + } + return undefined; + }; + + odata.xmlHandler = handler(xmlParser, xmlSerializer, xmlMediaType, MAX_DATA_SERVICE_VERSION); + + + + var atomPrefix = "a"; + + var atomXmlNs = w3org + "2005/Atom"; // http://www.w3.org/2005/Atom + var appXmlNs = w3org + "2007/app"; // http://www.w3.org/2007/app + + var odataEditMediaPrefix = adoDs + "/edit-media/"; // http://schemas.microsoft.com/ado/2007/08/dataservices/edit-media + var odataMediaResourcePrefix = adoDs + "/mediaresource/"; // http://schemas.microsoft.com/ado/2007/08/dataservices/mediaresource + var odataRelatedLinksPrefix = adoDs + "/relatedlinks/"; // http://schemas.microsoft.com/ado/2007/08/dataservices/relatedlinks + + var atomAcceptTypes = ["application/atom+xml", "application/atomsvc+xml", "application/xml"]; + var atomMediaType = atomAcceptTypes[0]; + + // These are the namespaces that are not considered ATOM extension namespaces. + var nonExtensionNamepaces = [atomXmlNs, appXmlNs, xmlNS, xmlnsNS]; + + // These are entity property mapping paths that have well-known paths. + var knownCustomizationPaths = { + SyndicationAuthorEmail: "author/email", + SyndicationAuthorName: "author/name", + SyndicationAuthorUri: "author/uri", + SyndicationContributorEmail: "contributor/email", + SyndicationContributorName: "contributor/name", + SyndicationContributorUri: "contributor/uri", + SyndicationPublished: "published", + SyndicationRights: "rights", + SyndicationSummary: "summary", + SyndicationTitle: "title", + SyndicationUpdated: "updated" + }; + + var expandedFeedCustomizationPath = function (path) { + /// Returns an expanded customization path if it's well-known. + /// Path to expand. + /// Expanded path or just 'path' otherwise. + + return knownCustomizationPaths[path] || path; + }; + + var isExtensionNs = function (nsURI) { + /// Checks whether the specified namespace is an extension namespace to ATOM. + /// Namespace to check. + /// true if nsURI is an extension namespace to ATOM; false otherwise. + + return !(contains(nonExtensionNamepaces, nsURI)); + }; + + var atomFeedCustomization = function (customizationModel, entityType, model, propertyName, suffix) { + /// Creates an object describing a feed customization that was delcared in an OData conceptual schema. + /// Object describing the customization delcared in the conceptual schema. + /// Object describing the entity type that owns the customization in an OData conceputal schema. + /// Object describing an OData conceptual schema. + /// Name of the property to which this customization applies. + /// Suffix to feed customization properties in the conceptual schema. + /// Object that describes an applicable feed customization. + + suffix = suffix || ""; + var targetPath = customizationModel["FC_TargetPath" + suffix]; + if (!targetPath) { + return null; + } + + var sourcePath = customizationModel["FC_SourcePath" + suffix]; + var targetXmlPath = expandedFeedCustomizationPath(targetPath); + + var propertyPath = propertyName ? propertyName + (sourcePath ? "/" + sourcePath : "") : sourcePath; + var propertyType = propertyPath && lookupPropertyType(model, entityType, propertyPath); + var nsURI = customizationModel["FC_NsUri" + suffix] || null; + var nsPrefix = customizationModel["FC_NsPrefix" + suffix] || null; + var keepinContent = customizationModel["FC_KeepInContent" + suffix] || ""; + + if (targetPath !== targetXmlPath) { + nsURI = atomXmlNs; + nsPrefix = atomPrefix; + } + + return { + contentKind: customizationModel["FC_ContentKind" + suffix], + keepInContent: keepinContent.toLowerCase() === "true", + nsPrefix: nsPrefix, + nsURI: nsURI, + propertyPath: propertyPath, + propertyType: propertyType, + entryPath: targetXmlPath + }; + }; + + var atomApplyAllFeedCustomizations = function (entityType, model, callback) { + /// Gets all the feed customizations that have to be applied to an entry as per the enity type declared in an OData conceptual schema. + /// Object describing an entity type in a conceptual schema. + /// Object describing an OData conceptual schema. + /// Callback function to be invoked for each feed customization that needs to be applied. + + var customizations = []; + while (entityType) { + var sourcePath = entityType.FC_SourcePath; + var customization = atomFeedCustomization(entityType, entityType, model); + if (customization) { + callback(customization); + } + + var properties = entityType.property || []; + var i, len; + for (i = 0, len = properties.length; i < len; i++) { + var property = properties[i]; + var suffixCounter = 0; + var suffix = ""; + + while (customization = atomFeedCustomization(property, entityType, model, property.name, suffix)) { + callback(customization); + suffixCounter++; + suffix = "_" + suffixCounter; + }; + } + entityType = lookupEntityType(entityType.baseType, model); + } + return customizations; + }; + + var atomReadExtensionAttributes = function (domElement) { + /// Reads ATOM extension attributes (any attribute not in the Atom namespace) from a DOM element. + /// DOM element with zero or more extension attributes. + /// An array of extension attribute representations. + + var extensions = []; + xmlAttributes(domElement, function (attribute) { + var nsURI = xmlNamespaceURI(attribute); + if (isExtensionNs(nsURI)) { + extensions.push(createAttributeExtension(attribute, true)); + } + }); + return extensions; + }; + + var atomReadExtensionElement = function (domElement) { + /// Reads an ATOM extension element (an element not in the ATOM namespaces). + /// DOM element not part of the atom namespace. + /// Object representing the extension element. + + return createElementExtension(domElement, /*addNamespaceURI*/true); + }; + + var atomReadDocument = function (domElement, baseURI, model) { + /// Reads an ATOM entry, feed or service document, producing an object model in return. + /// Top-level ATOM DOM element to read. + /// Base URI for normalizing relative URIs found in the ATOM document. + /// Object that describes the conceptual schema. + /// The object model representing the specified element, undefined if the top-level element is not part of the ATOM specification. + + var nsURI = xmlNamespaceURI(domElement); + var localName = xmlLocalName(domElement); + + // Handle service documents. + if (nsURI === appXmlNs && localName === "service") { + return atomReadServiceDocument(domElement, baseURI); + } + + // Handle feed and entry elements. + if (nsURI === atomXmlNs) { + if (localName === "feed") { + return atomReadFeed(domElement, baseURI, model); + } + if (localName === "entry") { + return atomReadEntry(domElement, baseURI, model); + } + } + + // Allow undefined to be returned. + }; + + var atomReadAdvertisedActionOrFunction = function (domElement, baseURI) { + /// Reads the DOM element for an action or a function in an OData Atom document. + /// DOM element to read. + /// Base URI for normalizing the action or function target url. + /// Object with title, target, and metadata fields. + + var extensions = []; + var result = { extensions: extensions }; + xmlAttributes(domElement, function (attribute) { + var localName = xmlLocalName(attribute); + var nsURI = xmlNamespaceURI(attribute); + var value = xmlNodeValue(attribute); + + if (nsURI === null) { + if (localName === "title" || localName === "metadata") { + result[localName] = value; + return; + } + if (localName === "target") { + result.target = normalizeURI(value, xmlBaseURI(domElement, baseURI)); + return; + } + } + + if (isExtensionNs(nsURI)) { + extensions.push(createAttributeExtension(attribute, true)); + } + }); + return result; + }; + + var atomReadAdvertisedAction = function (domElement, baseURI, parentMetadata) { + /// Reads the DOM element for an action in an OData Atom document. + /// DOM element to read. + /// Base URI for normalizing the action or target url. + /// Object to update with the action metadata. + + var actions = parentMetadata.actions = parentMetadata.actions || []; + actions.push(atomReadAdvertisedActionOrFunction(domElement, baseURI)); + }; + + var atomReadAdvertisedFunction = function (domElement, baseURI, parentMetadata) { + /// Reads the DOM element for an action in an OData Atom document. + /// DOM element to read. + /// Base URI for normalizing the action or target url. + /// Object to update with the action metadata. + + var functions = parentMetadata.functions = parentMetadata.functions || []; + functions.push(atomReadAdvertisedActionOrFunction(domElement, baseURI)); + }; + + var atomReadFeed = function (domElement, baseURI, model) { + /// Reads a DOM element for an ATOM feed, producing an object model in return. + /// ATOM feed DOM element. + /// Base URI for normalizing relative URIs found in the ATOM feed. + /// Metadata that describes the conceptual schema. + /// A new object representing the feed. + + var extensions = atomReadExtensionAttributes(domElement); + var feedMetadata = { feed_extensions: extensions }; + var results = []; + + var feed = { __metadata: feedMetadata, results: results }; + + baseURI = xmlBaseURI(domElement, baseURI); + + xmlChildElements(domElement, function (child) { + var nsURI = xmlNamespaceURI(child); + var localName = xmlLocalName(child); + + if (nsURI === odataMetaXmlNs) { + if (localName === "count") { + feed.__count = parseInt(xmlInnerText(child)); + return; + } + if (localName === "action") { + atomReadAdvertisedAction(child, baseURI, feedMetadata); + return; + } + if (localName === "function") { + atomReadAdvertisedFunction(child, baseURI, feedMetadata); + return; + } + } + + if (isExtensionNs(nsURI)) { + extensions.push(createElementExtension(child)); + return; + }; + + // The element should belong to the ATOM namespace. + + if (localName === "entry") { + results.push(atomReadEntry(child, baseURI, model)); + return; + } + if (localName === "link") { + atomReadFeedLink(child, feed, baseURI); + return; + } + if (localName === "id") { + feedMetadata.uri = normalizeURI(xmlInnerText(child), baseURI); + feedMetadata.uri_extensions = atomReadExtensionAttributes(child); + return; + } + if (localName === "title") { + feedMetadata.title = xmlInnerText(child) || ""; + feedMetadata.title_extensions = atomReadExtensionAttributes(child); + return; + } + }); + + return feed; + }; + + var atomReadFeedLink = function (domElement, feed, baseURI) { + /// Reads an ATOM link DOM element for a feed. + /// ATOM link DOM element. + /// Feed object to be annotated with the link data. + /// Base URI for normalizing relative URIs found in the payload. + + var link = atomReadLink(domElement, baseURI); + var href = link.href; + var rel = link.rel; + var extensions = link.extensions; + var metadata = feed.__metadata; + + if (rel === "next") { + feed.__next = href; + metadata.next_extensions = extensions; + return; + } + if (rel === "self") { + metadata.self = href; + metadata.self_extensions = extensions; + return; + } + }; + + var atomReadLink = function (domElement, baseURI) { + /// Reads an ATOM link DOM element. + /// DOM element to read. + /// Base URI for normalizing the link href. + /// A link element representation. + + baseURI = xmlBaseURI(domElement, baseURI); + + var extensions = []; + var link = { extensions: extensions, baseURI: baseURI }; + + xmlAttributes(domElement, function (attribute) { + var nsURI = xmlNamespaceURI(attribute); + var localName = xmlLocalName(attribute); + var value = attribute.value; + + if (localName === "href") { + link.href = normalizeURI(value, baseURI); + return; + } + if (localName === "type" || localName === "rel") { + link[localName] = value; + return; + } + + if (isExtensionNs(nsURI)) { + extensions.push(createAttributeExtension(attribute, true)); + } + }); + + if (!link.href) { + throw { error: "href attribute missing on link element", element: domElement }; + } + + return link; + }; + + var atomGetObjectValueByPath = function (path, item) { + /// Gets a slashed path value from the specified item. + /// Property path to read ('/'-separated). + /// Object to get value from. + /// The property value, possibly undefined if any path segment is missing. + + // Fast path. + if (path.indexOf('/') === -1) { + return item[path]; + } else { + var parts = path.split('/'); + var i, len; + for (i = 0, len = parts.length; i < len; i++) { + // Avoid traversing a null object. + if (item === null) { + return undefined; + } + + item = item[parts[i]]; + if (item === undefined) { + return item; + } + } + + return item; + } + }; + + var atomSetEntryValueByPath = function (path, target, value, propertyType) { + /// Sets a slashed path value on the specified target. + /// Property path to set ('/'-separated). + /// Object to set value on. + /// Value to set. + /// Property type to set in metadata. + + var propertyName; + if (path.indexOf('/') === -1) { + target[path] = value; + propertyName = path; + } else { + var parts = path.split('/'); + var i, len; + for (i = 0, len = (parts.length - 1); i < len; i++) { + // We construct each step of the way if the property is missing; + // if it's already initialized to null, we stop further processing. + var next = target[parts[i]]; + if (next === undefined) { + next = {}; + target[parts[i]] = next; + } else if (next === null) { + return; + } + target = next; + } + propertyName = parts[i]; + target[propertyName] = value; + } + + if (propertyType) { + var metadata = target.__metadata = target.__metadata || {}; + var properties = metadata.properties = metadata.properties || {}; + var property = properties[propertyName] = properties[propertyName] || {}; + property.type = propertyType; + } + }; + + var atomApplyCustomizationToEntryObject = function (customization, domElement, entry) { + /// Applies a specific feed customization item to an object. + /// Object with customization description. + /// Property path to set ('source' in the description). + /// XML element for the entry that corresponds to the object being read. + /// Object being read. + /// Name of property type to set. + /// Suffix to feed customization properties. + + var propertyPath = customization.propertyPath; + // If keepInConent equals true or the property value is null we do nothing as this overrides any other customization. + if (customization.keepInContent || atomGetObjectValueByPath(propertyPath, entry) === null) { + return; + } + + var xmlNode = xmlFindNodeByPath(domElement, customization.nsURI, customization.entryPath); + + // If the XML tree does not contain the necessary elements to read the value, + // then it shouldn't be considered null, but rather ignored at all. This prevents + // the customization from generating the object path down to the property. + if (!xmlNode) { + return; + } + + var propertyType = customization.propertyType; + var propertyValue; + + if (customization.contentKind === "xhtml") { + // Treat per XHTML in http://tools.ietf.org/html/rfc4287#section-3.1.1, including the DIV + // in the content. + propertyValue = xmlSerializeDescendants(xmlNode); + } else { + propertyValue = xmlReadODataEdmPropertyValue(xmlNode, propertyType || "Edm.String"); + } + // Set the value on the entry. + atomSetEntryValueByPath(propertyPath, entry, propertyValue, propertyType); + }; + + var lookupPropertyType = function (metadata, owningType, path) { + /// Looks up the type of a property given its path in an entity type. + /// Metadata in which to search for base and complex types. + /// Type to which property belongs. + /// Property path to look at. + /// The name of the property type; possibly null. + + var parts = path.split("/"); + var i, len; + while (owningType) { + // Keep track of the type being traversed, necessary for complex types. + var traversedType = owningType; + + for (i = 0, len = parts.length; i < len; i++) { + // Traverse down the structure as necessary. + var properties = traversedType.property; + if (!properties) { + break; + } + + // Find the property by scanning the property list (might be worth pre-processing). + var propertyFound = lookupProperty(properties, parts[i]); + if (!propertyFound) { + break; + } + + var propertyType = propertyFound.type; + + // We could in theory still be missing types, but that would + // be caused by a malformed path. + if (!propertyType || isPrimitiveEdmType(propertyType)) { + return propertyType || null; + } + + traversedType = lookupComplexType(propertyType, metadata); + if (!traversedType) { + return null; + } + } + + // Traverse up the inheritance chain. + owningType = lookupEntityType(owningType.baseType, metadata); + } + + return null; + }; + + var atomReadEntry = function (domElement, baseURI, model) { + /// Reads a DOM element for an ATOM entry, producing an object model in return. + /// ATOM entry DOM element. + /// Base URI for normalizing relative URIs found in the ATOM entry. + /// Metadata that describes the conceptual schema. + /// A new object representing the entry. + + var entryMetadata = {}; + var entry = { __metadata: entryMetadata }; + + var etag = xmlAttributeValue(domElement, "etag", odataMetaXmlNs); + if (etag) { + entryMetadata.etag = etag; + } + + baseURI = xmlBaseURI(domElement, baseURI); + + xmlChildElements(domElement, function (child) { + var nsURI = xmlNamespaceURI(child); + var localName = xmlLocalName(child); + + if (nsURI === atomXmlNs) { + if (localName === "id") { + atomReadEntryId(child, entryMetadata, baseURI); + return; + } + if (localName === "category") { + atomReadEntryType(child, entryMetadata); + return; + } + if (localName === "content") { + atomReadEntryContent(child, entry, entryMetadata, baseURI); + return; + } + if (localName === "link") { + atomReadEntryLink(child, entry, entryMetadata, baseURI, model); + return; + } + return; + } + + if (nsURI === odataMetaXmlNs) { + if (localName === "properties") { + atomReadEntryStructuralObject(child, entry, entryMetadata); + return; + } + if (localName === "action") { + atomReadAdvertisedAction(child, baseURI, entryMetadata); + return; + } + if (localName === "function") { + atomReadAdvertisedFunction(child, baseURI, entryMetadata); + return; + } + } + }); + + // Apply feed customizations if applicable + var entityType = lookupEntityType(entryMetadata.type, model); + atomApplyAllFeedCustomizations(entityType, model, function (customization) { + atomApplyCustomizationToEntryObject(customization, domElement, entry); + }); + + return entry; + }; + + var atomReadEntryId = function (domElement, entryMetadata, baseURI) { + /// Reads an ATOM entry id DOM element. + /// ATOM id DOM element. + /// Entry metadata object to update with the id information. + + entryMetadata.uri = normalizeURI(xmlInnerText(domElement), xmlBaseURI(domElement, baseURI)); + entryMetadata.uri_extensions = atomReadExtensionAttributes(domElement); + }; + + var atomReadEntryType = function (domElement, entryMetadata) { + /// Reads type information from an ATOM category DOM element. + /// ATOM category DOM element. + /// Entry metadata object to update with the type information. + + if (xmlAttributeValue(domElement, "scheme") === odataScheme) { + if (entryMetadata.type) { + throw { message: "Invalid AtomPub document: multiple category elements defining the entry type were encounterd withing an entry", element: domElement }; + } + + var typeExtensions = []; + xmlAttributes(domElement, function (attribute) { + var nsURI = xmlNamespaceURI(attribute); + var localName = xmlLocalName(attribute); + + if (!nsURI) { + if (localName !== "scheme" && localName !== "term") { + typeExtensions.push(createAttributeExtension(attribute, true)); + } + return; + } + + if (isExtensionNs(nsURI)) { + typeExtensions.push(createAttributeExtension(attribute, true)); + } + }); + + entryMetadata.type = xmlAttributeValue(domElement, "term"); + entryMetadata.type_extensions = typeExtensions; + } + }; + + var atomReadEntryContent = function (domElement, entry, entryMetadata, baseURI) { + /// Reads an ATOM content DOM element. + /// ATOM content DOM element. + /// Entry object to update with information. + /// Entry metadata object to update with the content information. + /// Base URI for normalizing relative URIs found in the Atom entry content. + + var src = xmlAttributeValue(domElement, "src"); + var type = xmlAttributeValue(domElement, "type"); + + if (src) { + if (!type) { + throw { + message: "Invalid AtomPub document: content element must specify the type attribute if the src attribute is also specified", + element: domElement + }; + } + + entryMetadata.media_src = normalizeURI(src, xmlBaseURI(domElement, baseURI)); + entryMetadata.content_type = type; + } + + xmlChildElements(domElement, function (child) { + if (src) { + throw { message: "Invalid AtomPub document: content element must not have child elements if the src attribute is specified", element: domElement }; + } + + if (xmlNamespaceURI(child) === odataMetaXmlNs && xmlLocalName(child) === "properties") { + atomReadEntryStructuralObject(child, entry, entryMetadata); + } + }); + }; + + var atomReadEntryLink = function (domElement, entry, entryMetadata, baseURI, model) { + /// Reads a link element on an entry. + /// 'link' element on the entry. + /// Entry object to update with the link data. + /// Entry metadata object to update with the link metadata. + /// Base URI for normalizing the link href. + /// Metadata that describes the conceptual schema. + + var link = atomReadLink(domElement, baseURI); + + var rel = link.rel; + var href = link.href; + var extensions = link.extensions; + + if (rel === "self") { + entryMetadata.self = href; + entryMetadata.self_link_extensions = extensions; + return; + } + + if (rel === "edit") { + entryMetadata.edit = href; + entryMetadata.edit_link_extensions = extensions; + return; + } + + if (rel === "edit-media") { + entryMetadata.edit_media = link.href; + entryMetadata.edit_media_extensions = extensions; + atomReadLinkMediaEtag(link, entryMetadata); + return; + } + + // This might be a named stream edit link + if (rel.indexOf(odataEditMediaPrefix) === 0) { + atomReadNamedStreamEditLink(link, entry, entryMetadata); + return; + } + + // This might be a named stram media resource (read) link + if (rel.indexOf(odataMediaResourcePrefix) === 0) { + atomReadNamedStreamSelfLink(link, entry, entryMetadata); + return; + } + + // This might be a navigation property + if (rel.indexOf(odataRelatedPrefix) === 0) { + atomReadNavPropLink(domElement, link, entry, entryMetadata, model); + return; + } + + if (rel.indexOf(odataRelatedLinksPrefix) === 0) { + atomReadNavPropRelatedLink(link, entryMetadata); + return; + } + }; + + var atomReadNavPropRelatedLink = function (link, entryMetadata) { + /// Reads a link represnting the links related to a navigation property in an OData Atom document. + /// Object representing the parsed link DOM element. + /// Entry metadata object to update with the related links information. + + var propertyName = link.rel.substring(odataRelatedLinksPrefix.length); + + // Set the extra property information on the entry object metadata. + entryMetadata.properties = entryMetadata.properties || {}; + var propertyMetadata = entryMetadata.properties[propertyName] = entryMetadata.properties[propertyName] || {}; + + propertyMetadata.associationuri = link.href; + propertyMetadata.associationuri_extensions = link.extensions; + }; + + var atomReadNavPropLink = function (domElement, link, entry, entryMetadata, model) { + /// Reads a link representing a navigation property in an OData Atom document. + /// DOM element for a navigation property in an OData Atom document. + /// Object representing the parsed link DOM element. + /// Entry object to update with the navigation property. + /// Entry metadata object to update with the navigation property metadata. + /// Metadata that describes the conceptual schema. + + // Get any inline data. + var inlineData; + var inlineElement = xmlFirstChildElement(domElement, odataMetaXmlNs, "inline"); + if (inlineElement) { + var inlineDocRoot = xmlFirstChildElement(inlineElement); + var inlineBaseURI = xmlBaseURI(inlineElement, link.baseURI); + inlineData = inlineDocRoot ? atomReadDocument(inlineDocRoot, inlineBaseURI, model) : null; + } else { + // If the link has no inline content, we consider it deferred. + inlineData = { __deferred: { uri: link.href} }; + } + + var propertyName = link.rel.substring(odataRelatedPrefix.length); + + // Set the property value on the entry object. + entry[propertyName] = inlineData; + + // Set the extra property information on the entry object metadata. + entryMetadata.properties = entryMetadata.properties || {}; + var propertyMetadata = entryMetadata.properties[propertyName] = entryMetadata.properties[propertyName] || {}; + + propertyMetadata.extensions = link.extensions; + }; + + var atomReadNamedStreamEditLink = function (link, entry, entryMetadata) { + /// Reads a link representing the edit-media url of a named stream in an OData Atom document. + /// Object representing the parsed link DOM element. + /// Entry object to update with the named stream data. + /// Entry metadata object to update with the named stream metadata. + + var propertyName = link.rel.substring(odataEditMediaPrefix.length); + + var namedStreamMediaResource = atomGetEntryNamedStreamMediaResource(propertyName, entry, entryMetadata); + var mediaResource = namedStreamMediaResource.value; + var mediaResourceMetadata = namedStreamMediaResource.metadata; + + var editMedia = link.href; + + mediaResource.edit_media = editMedia; + mediaResource.content_type = link.type; + mediaResourceMetadata.edit_media_extensions = link.extensions; + + // If there is only the edit link, make it the media self link as well. + mediaResource.media_src = mediaResource.media_src || editMedia; + mediaResourceMetadata.media_src_extensions = mediaResourceMetadata.media_src_extensions || []; + + atomReadLinkMediaEtag(link, mediaResource); + }; + + var atomReadNamedStreamSelfLink = function (link, entry, entryMetadata) { + /// Reads a link representing the self url of a named stream in an OData Atom document. + /// Object representing the parsed link DOM element. + /// Entry object to update with the named stream data. + /// Entry metadata object to update with the named stream metadata. + + var propertyName = link.rel.substring(odataMediaResourcePrefix.length); + + var namedStreamMediaResource = atomGetEntryNamedStreamMediaResource(propertyName, entry, entryMetadata); + var mediaResource = namedStreamMediaResource.value; + var mediaResourceMetadata = namedStreamMediaResource.metadata; + + mediaResource.media_src = link.href; + mediaResourceMetadata.media_src_extensions = link.extensions; + mediaResource.content_type = link.type; + }; + + var atomGetEntryNamedStreamMediaResource = function (name, entry, entryMetadata) { + /// Gets the media resource object and metadata object for a named stream in an entry object. + /// Object representing the parsed link DOM element. + /// Entry object from which the media resource object will be obtained. + /// Entry metadata object from which the media resource metadata object will be obtained. + /// + /// If the entry doest' have a media resource for the named stream indicated by the name argument, then this function will create a new + /// one along with its metadata object. + /// + /// Object containing the value and metadata of the named stream's media resource. + + entryMetadata.properties = entryMetadata.properties || {}; + + var mediaResourceMetadata = entryMetadata.properties[name]; + var mediaResource = entry[name] && entry[name].__mediaresource; + + if (!mediaResource) { + mediaResource = {}; + entry[name] = { __mediaresource: mediaResource }; + entryMetadata.properties[name] = mediaResourceMetadata = {}; + } + return { value: mediaResource, metadata: mediaResourceMetadata }; + }; + + var atomReadLinkMediaEtag = function (link, mediaResource) { + /// Gets the media etag from the link extensions and updates the media resource object with it. + /// Object representing the parsed link DOM element. + /// Object containing media information for an OData Atom entry. + /// + /// The function will remove the extension object for the etag if it finds it in the link extensions and will set + /// its value under the media_etag property of the mediaResource object. + /// + /// Object containing the value and metadata of the named stream's media resource. + + var extensions = link.extensions; + var i, len; + for (i = 0, len = extensions.length; i < len; i++) { + if (extensions[i].namespaceURI === odataMetaXmlNs && extensions[i].name === "etag") { + mediaResource.media_etag = extensions[i].value; + extensions.splice(i, 1); + return; + } + } + }; + + var atomReadEntryStructuralObject = function (domElement, parent, parentMetadata) { + /// Reads an atom entry's property as a structural object and sets its value in the parent and the metadata in the parentMetadata objects. + /// XML element for the 'properties' node. + /// + /// Object that will contain the property value. It can be either an antom entry or + /// an atom complex property object. + /// + /// Object that will contain the property metadata. It can be either an atom entry metadata or a complex property metadata object + + xmlChildElements(domElement, function (child) { + var property = xmlReadODataProperty(child); + if (property) { + var propertyName = property.name; + var propertiesMetadata = parentMetadata.properties = parentMetadata.properties || {}; + propertiesMetadata[propertyName] = property.metadata; + parent[propertyName] = property.value; + } + }); + }; + + var atomReadServiceDocument = function (domElement, baseURI) { + /// Reads an AtomPub service document + /// DOM element for the root of an AtomPub service document + /// Base URI for normalizing relative URIs found in the AtomPub service document. + /// An object that contains the properties of the service document + + var workspaces = []; + var extensions = []; + + baseURI = xmlBaseURI(domElement, baseURI); + // Find all the workspace elements. + xmlChildElements(domElement, function (child) { + if (xmlNamespaceURI(child) === appXmlNs && xmlLocalName(child) === "workspace") { + workspaces.push(atomReadServiceDocumentWorkspace(child, baseURI)); + return; + } + extensions.push(createElementExtension(child)); + }); + + // AtomPub (RFC 5023 Section 8.3.1) says a service document MUST contain one or + // more workspaces. Throw if we don't find any. + if (workspaces.length === 0) { + throw { message: "Invalid AtomPub service document: No workspace element found.", element: domElement }; + } + + return { workspaces: workspaces, extensions: extensions }; + }; + + var atomReadServiceDocumentWorkspace = function (domElement, baseURI) { + /// Reads a single workspace element from an AtomPub service document + /// DOM element that represents a workspace of an AtomPub service document + /// Base URI for normalizing relative URIs found in the AtomPub service document workspace. + /// An object that contains the properties of the workspace + + var collections = []; + var extensions = []; + var title = undefined; + + baseURI = xmlBaseURI(domElement, baseURI); + + xmlChildElements(domElement, function (child) { + var nsURI = xmlNamespaceURI(child); + var localName = xmlLocalName(child); + + if (nsURI === atomXmlNs) { + if (localName === "title") { + if (title !== undefined) { + throw { message: "Invalid AtomPub service document: workspace has more than one child title element", element: child }; + } + + title = xmlInnerText(child); + return; + } + } + + if (nsURI === appXmlNs) { + if (localName === "collection") { + collections.push(atomReadServiceDocumentCollection(child, baseURI)); + } + return; + } + extensions.push(atomReadExtensionElement(child)); + }); + + return { title: title || "", collections: collections, extensions: extensions }; + }; + + var atomReadServiceDocumentCollection = function (domElement, baseURI) { + /// Reads a service document collection element into an object. + /// DOM element that represents a collection of an AtomPub service document. + /// Base URI for normalizing relative URIs found in the AtomPub service document collection. + /// An object that contains the properties of the collection. + + + var href = xmlAttributeValue(domElement, "href"); + + if (!href) { + throw { message: "Invalid AtomPub service document: collection has no href attribute", element: domElement }; + } + + baseURI = xmlBaseURI(domElement, baseURI); + href = normalizeURI(href, xmlBaseURI(domElement, baseURI)); + var extensions = []; + var title = undefined; + + xmlChildElements(domElement, function (child) { + var nsURI = xmlNamespaceURI(child); + var localName = xmlLocalName(child); + + if (nsURI === atomXmlNs) { + if (localName === "title") { + if (title !== undefined) { + throw { message: "Invalid AtomPub service document: collection has more than one child title element", element: child }; + } + title = xmlInnerText(child); + } + return; + } + + if (nsURI !== appXmlNs) { + extensions.push(atomReadExtensionElement(domElement)); + } + }); + + // AtomPub (RFC 5023 Section 8.3.3) says the collection element MUST contain + // a title element. It's likely to be problematic if the service doc doesn't + // have one so here we throw. + if (!title) { + throw { message: "Invalid AtomPub service document: collection has no title element", element: domElement }; + } + + return { title: title, href: href, extensions: extensions }; + }; + + var atomNewElement = function (dom, name, children) { + /// Creates a new DOM element in the Atom namespace. + /// DOM document used for creating the new DOM Element. + /// Local name of the Atom element to create. + /// Array containing DOM nodes or string values that will be added as children of the new DOM element. + /// New DOM element in the Atom namespace. + /// + /// If a value in the children collection is a string, then a new DOM text node is going to be created + /// for it and then appended as a child of the new DOM Element. + /// + + return xmlNewElement(dom, atomXmlNs, xmlQualifiedName(atomPrefix, name), children); + }; + + var atomNewAttribute = function (dom, name, value) { + /// Creates a new DOM attribute for an Atom element in the default namespace. + /// DOM document used for creating the new DOM Element. + /// Local name of the OData attribute to create. + /// Attribute value. + /// New DOM attribute in the default namespace. + + return xmlNewAttribute(dom, null, name, value); + }; + + var atomCanRemoveProperty = function (propertyElement) { + /// Checks whether the property represented by domElement can be removed from the atom document DOM tree. + /// DOM element for the property to test. + /// + /// The property can only be removed if it doens't have any children and only has namespace or type declaration attributes. + /// + /// True is the property can be removed; false otherwise. + + if (propertyElement.childNodes.length > 0) { + return false; + } + + var isEmpty = true; + var attributes = propertyElement.attributes; + var i, len; + for (i = 0, len = attributes.length; i < len && isEmpty; i++) { + var attribute = attributes[i]; + + isEmpty = isEmpty && isXmlNSDeclaration(attribute) || + (xmlNamespaceURI(attribute) == odataMetaXmlNs && xmlLocalName(attribute) === "type"); + } + return isEmpty; + }; + + var atomNewODataNavigationProperty = function (dom, name, kind, value, model) { + /// Creates a new Atom link DOM element for a navigation property in an OData Atom document. + /// DOM document used for creating the new DOM Element. + /// Property name. + /// Navigation property kind. Expected values are "deferred", "entry", or "feed". + /// Value of the navigation property, if any. + /// Object describing an OData conceptual schema. + /// + /// Object containing the new Atom link DOM element for the navigation property and the + /// required data service version for this property. + /// + + var linkType = null; + var linkContent = null; + var linkContentBodyData = null; + var href = ""; + + if (kind !== "deferred") { + linkType = atomNewAttribute(dom, "type", "application/atom+xml;type=" + kind); + linkContent = xmlNewODataMetaElement(dom, "inline"); + + if (value) { + href = value.__metadata && value.__metadata.uri || ""; + linkContentBodyData = + atomNewODataFeed(dom, value, model) || + atomNewODataEntry(dom, value, model); + xmlAppendChild(linkContent, linkContentBodyData.element); + } + } else { + href = value.__deferred.uri; + }; + + var navProp = atomNewElement(dom, "link", [ + atomNewAttribute(dom, "href", href), + atomNewAttribute(dom, "rel", normalizeURI(name, odataRelatedPrefix)), + linkType, + linkContent + ]); + + return xmlNewODataElementInfo(navProp, linkContentBodyData ? linkContentBodyData.dsv : "1.0"); + }; + + var atomNewODataEntryDataItem = function (dom, name, value, dataItemMetadata, dataItemModel, model) { + /// Creates a new DOM element for a data item in an entry, complex property, or collection property. + /// DOM document used for creating the new DOM Element. + /// Data item name. + /// Value of the data item, if any. + /// Object containing metadata about the data item. + /// Object describing the data item in an OData conceptual schema. + /// Object describing an OData conceptual schema. + /// + /// Object containing the new DOM element in the appropriate namespace for the data item and the + /// required data service version for it. + /// + + if (isNamedStream(value)) { + return null; + } + + var dataElement = xmlNewODataDataElement(dom, name, value, dataItemMetadata, dataItemModel, model); + if (!dataElement) { + // This may be a navigation property. + var navPropKind = navigationPropertyKind(value, dataItemModel); + + dataElement = atomNewODataNavigationProperty(dom, name, navPropKind, value, model); + } + return dataElement; + }; + + var atomEntryCustomization = function (dom, entry, entryProperties, customization) { + /// Applies a feed customization by transforming an Atom entry DOM element as needed. + /// DOM document used for creating any new DOM nodes required by the customization. + /// DOM element for the Atom entry to which the customization is going to be applied. + /// DOM element containing the properties of the Atom entry. + /// Object describing an applicable feed customization. + /// + /// Look into the atomfeedCustomization function for a description of the customization object. + /// + /// Data service version required by the applied customization + + var atomProperty = xmlFindElementByPath(entryProperties, odataXmlNs, customization.propertyPath); + var atomPropertyNullAttribute = atomProperty && xmlAttributeNode(atomProperty, "null", odataMetaXmlNs); + var atomPropertyValue; + var dataServiceVersion = "1.0"; + + if (atomPropertyNullAttribute && atomPropertyNullAttribute.value === "true") { + return dataServiceVersion; + } + + if (atomProperty) { + atomPropertyValue = xmlInnerText(atomProperty) || ""; + if (!customization.keepInContent) { + dataServiceVersion = "2.0"; + var parent = atomProperty.parentNode; + var candidate = parent; + + parent.removeChild(atomProperty); + while (candidate !== entryProperties && atomCanRemoveProperty(candidate)) { + parent = candidate.parentNode; + parent.removeChild(candidate); + candidate = parent; + } + } + } + + var targetNode = xmlNewNodeByPath(dom, entry, + customization.nsURI, customization.nsPrefix, customization.entryPath); + + if (targetNode.nodeType === 2) { + targetNode.value = atomPropertyValue; + return dataServiceVersion; + } + + var contentKind = customization.contentKind; + xmlAppendChildren(targetNode, [ + contentKind && xmlNewAttribute(dom, null, "type", contentKind), + contentKind === "xhtml" ? xmlNewFragment(dom, atomPropertyValue) : atomPropertyValue + ]); + + return dataServiceVersion; + }; + + var atomNewODataEntry = function (dom, data, model) { + /// Creates a new DOM element for an Atom entry. + /// DOM document used for creating the new DOM Element. + /// Entry object in the library's internal representation. + /// Object describing an OData conceptual schema. + /// + /// Object containing the new DOM element for the Atom entry and the required data service version for it. + /// + + var payloadMetadata = data.__metadata || {}; + var propertiesMetadata = payloadMetadata.properties || {}; + + var etag = payloadMetadata.etag; + var uri = payloadMetadata.uri; + var typeName = payloadMetadata.type; + var entityType = lookupEntityType(typeName, model); + + var properties = xmlNewODataMetaElement(dom, "properties"); + var entry = atomNewElement(dom, "entry", [ + atomNewElement(dom, "author", + atomNewElement(dom, "name") + ), + etag && xmlNewODataMetaAttribute(dom, "etag", etag), + uri && atomNewElement(dom, "id", uri), + typeName && atomNewElement(dom, "category", [ + atomNewAttribute(dom, "term", typeName), + atomNewAttribute(dom, "scheme", odataScheme) + ]), + // TODO: MLE support goes here. + atomNewElement(dom, "content", [ + atomNewAttribute(dom, "type", "application/xml"), + properties + ]) + ]); + + var dataServiceVersion = "1.0"; + for (var name in data) { + if (name !== "__metadata") { + var entryDataItemMetadata = propertiesMetadata[name] || {}; + var entryDataItemModel = entityType && ( + lookupProperty(entityType.property, name) + || lookupProperty(entityType.navigationProperty, name)); + + var entryDataItem = atomNewODataEntryDataItem(dom, name, data[name], entryDataItemMetadata, entryDataItemModel, model); + if (entryDataItem) { + var entryElement = entryDataItem.element; + var entryElementParent = (xmlNamespaceURI(entryElement) === atomXmlNs) ? entry : properties; + + xmlAppendChild(entryElementParent, entryElement); + dataServiceVersion = maxVersion(dataServiceVersion, entryDataItem.dsv); + } + } + }; + + atomApplyAllFeedCustomizations(entityType, model, function (customization) { + var customizationDsv = atomEntryCustomization(dom, entry, properties, customization); + dataServiceVersion = maxVersion(dataServiceVersion, customizationDsv); + }); + + return xmlNewODataElementInfo(entry, dataServiceVersion); + }; + + var atomNewODataFeed = function (dom, data, model) { + /// Creates a new DOM element for an Atom feed. + /// DOM document used for creating the new DOM Element. + /// Feed object in the library's internal representation. + /// Object describing an OData conceptual schema. + /// + /// Object containing the new DOM element for the Atom feed and the required data service version for it. + /// + + var entries = isArray(data) ? data : data.results; + + if (!entries) { + return null; + } + + var dataServiceVersion = "1.0"; + var atomFeed = atomNewElement(dom, "feed"); + + var i, len; + for (i = 0, len = entries.length; i < len; i++) { + var atomEntryData = atomNewODataEntry(dom, entries[i], model); + xmlAppendChild(atomFeed, atomEntryData.element); + dataServiceVersion = maxVersion(dataServiceVersion, atomEntryData.dsv); + } + return xmlNewODataElementInfo(atomFeed, dataServiceVersion); + }; + + var atomNewODataDocument = function (data, model) { + /// Creates a new OData Atom document. + /// Feed or entry object in the libary's internal representaion. + /// Object describing an OData conceptual schema. + /// + /// Object containing the new DOM document for the Atom document and the required data service version for it. + /// + + if (data) { + var atomRootWriter = isFeed(data) && atomNewODataFeed || + isObject(data) && atomNewODataEntry; + + if (atomRootWriter) { + var dom = xmlDom(); + var atomRootData = atomRootWriter(dom, data, model); + + if (atomRootData) { + var atomRootElement = atomRootData.element; + xmlAppendChildren(atomRootElement, [ + xmlNewNSDeclaration(dom, odataMetaXmlNs, odataMetaPrefix), + xmlNewNSDeclaration(dom, odataXmlNs, odataPrefix) + ]); + return xmlNewODataElementInfo(xmlAppendChild(dom, atomRootElement), atomRootData.dsv); + } + } + } + return null; + }; + + var atomParser = function (handler, text, context) { + /// Parses an ATOM document (feed, entry or service document). + /// This handler. + /// Document text. + /// Object with parsing context. + /// An object representation of the document; undefined if not applicable. + + if (text) { + var atomDoc = xmlParse(text); + var atomRoot = xmlFirstChildElement(atomDoc); + if (atomRoot) { + return atomReadDocument(atomRoot, null, context.metadata); + } + } + }; + + var atomSerializer = function (handler, data, context) { + /// Serializes an ATOM object into a document (feed or entry). + /// This handler. + /// Representation of feed or entry. + /// Object with parsing context. + /// An text representation of the data object; undefined if not applicable. + + var cType = context.contentType = context.contentType || contentType(atomMediaType); + if (cType && cType.mediaType === atomMediaType) { + var atomDoc = atomNewODataDocument(data, context.metadata); + if (atomDoc) { + context.dataServiceVersion = maxVersion(context.dataServiceVersion || "1.0", atomDoc.dsv); + return xmlSerialize(atomDoc.element); + } + } + // Allow undefined to be returned. + }; + + odata.atomHandler = handler(atomParser, atomSerializer, atomAcceptTypes.join(","), MAX_DATA_SERVICE_VERSION); + + + + var schemaElement = function (attributes, elements, text, ns) { + /// Creates an object that describes an element in an schema. + /// List containing the names of the attributes allowed for this element. + /// List containing the names of the child elements allowed for this element. + /// Flag indicating if the element's text value is of interest or not. + /// Namespace to which the element belongs to. + /// + /// If a child element name ends with * then it is understood by the schema that that child element can appear 0 or more times. + /// + /// Object with attributes, elements, text, and ns fields. + + return { + attributes: attributes, + elements: elements, + text: text || false, + ns: ns + }; + }; + + // It's assumed that all elements may have Documentation children and Annotation elements. + // See http://msdn.microsoft.com/en-us/library/bb399292.aspx for a CSDL reference. + var schema = { + elements: { + Annotations: schemaElement( + /*attributes*/["Target", "Qualifier"], + /*elements*/["TypeAnnotation*", "ValueAnnotation*"] + ), + Association: schemaElement( + /*attributes*/["Name"], + /*elements*/["End*", "ReferentialConstraint", "TypeAnnotation*", "ValueAnnotation*"] + ), + AssociationSet: schemaElement( + /*attributes*/["Name", "Association"], + /*elements*/["End*", "TypeAnnotation*", "ValueAnnotation*"] + ), + Binary: schemaElement( + /*attributes*/null, + /*elements*/null, + /*text*/true + ), + Bool: schemaElement( + /*attributes*/null, + /*elements*/null, + /*text*/true + ), + Collection: schemaElement( + /*attributes*/null, + /*elements*/["String*", "Int*", "Float*", "Decimal*", "Bool*", "DateTime*", "DateTimeOffset*", "Guid*", "Binary*", "Time*", "Collection*", "Record*"] + ), + CollectionType: schemaElement( + /*attributes*/["ElementType", "Nullable", "DefaultValue", "MaxLength", "FixedLength", "Precision", "Scale", "Unicode", "Collation", "SRID"] + /*elements*/["CollectionType", "ReferenceType", "RowType", "TypeRef"] + ), + ComplexType: schemaElement( + /*attributes*/["Name", "BaseType", "Abstract"], + /*elements*/["Property*", "TypeAnnotation*", "ValueAnnotation*"] + ), + DateTime: schemaElement( + /*attributes*/null, + /*elements*/null, + /*text*/true + ), + DateTimeOffset: schemaElement( + /*attributes*/null, + /*elements*/null, + /*text*/true + ), + Decimal: schemaElement( + /*attributes*/null, + /*elements*/null, + /*text*/true + ), + DefiningExpression: schemaElement( + /*attributes*/null, + /*elements*/null, + /*text*/true + ), + Dependent: schemaElement( + /*attributes*/["Role"], + /*elements*/["PropertyRef*"] + ), + Documentation: schemaElement( + /*attributes*/null, + /*elements*/null, + /*text*/true + ), + End: schemaElement( + /*attributes*/["Type", "Role", "Multiplicity", "EntitySet"], + /*elements*/["OnDelete"] + ), + EntityContainer: schemaElement( + /*attributes*/["Name", "Extends"], + /*elements*/["EntitySet*", "AssociationSet*", "FunctionImport*", "TypeAnnotation*", "ValueAnnotation*"] + ), + EntitySet: schemaElement( + /*attributes*/["Name", "EntityType"], + /*elements*/["TypeAnnotation*", "ValueAnnotation*"] + ), + EntityType: schemaElement( + /*attributes*/["Name", "BaseType", "Abstract", "OpenType"], + /*elements*/["Key", "Property*", "NavigationProperty*", "TypeAnnotation*", "ValueAnnotation*"] + ), + EnumType: schemaElement( + /*attributes*/["Name", "UnderlyingType", "IsFlags"], + /*elements*/["Member*"] + ), + Float: schemaElement( + /*attributes*/null, + /*elements*/null, + /*text*/true + ), + Function: schemaElement( + /*attributes*/["Name", "ReturnType"], + /*elements*/["Parameter*", "DefiningExpression", "ReturnType", "TypeAnnotation*", "ValueAnnotation*"] + ), + FunctionImport: schemaElement( + /*attributes*/["Name", "ReturnType", "EntitySet", "IsSideEffecting", "IsComposable", "IsBindable", "EntitySetPath"], + /*elements*/["Parameter*", "ReturnType", "TypeAnnotation*", "ValueAnnotation*"] + ), + Guid: schemaElement( + /*attributes*/null, + /*elements*/null, + /*text*/true + ), + Int: schemaElement( + /*attributes*/null, + /*elements*/null, + /*text*/true + ), + Key: schemaElement( + /*attributes*/null, + /*elements*/["PropertyRef*"] + ), + LabeledElement: schemaElement( + /*attributes*/["Name"], + /*elements*/["Path", "String", "Int", "Float", "Decimal", "Bool", "DateTime", "DateTimeOffset", "Guid", "Binary", "Time", "Collection", "Record", "LabeledElement", "Null"] + ), + Member: schemaElement( + /*attributes*/["Name", "Value"] + ), + NavigationProperty: schemaElement( + /*attributes*/["Name", "Relationship", "ToRole", "FromRole", "ContainsTarget"], + /*elements*/["TypeAnnotation*", "ValueAnnotation*"] + ), + Null: schemaElement( + /*attributes*/null, + /*elements*/null + ), + OnDelete: schemaElement( + /*attributes*/["Action"] + ), + Path: schemaElement( + /*attributes*/null, + /*elements*/null, + /*text*/true + ), + Parameter: schemaElement( + /*attributes*/["Name", "Type", "Mode", "Nullable", "DefaultValue", "MaxLength", "FixedLength", "Precision", "Scale", "Unicode", "Collation", "ConcurrencyMode", "SRID"], + /*elements*/["CollectionType", "ReferenceType", "RowType", "TypeRef", "TypeAnnotation*", "ValueAnnotation*"] + ), + Principal: schemaElement( + /*attributes*/["Role"], + /*elements*/["PropertyRef*"] + ), + Property: schemaElement( + /*attributes*/["Name", "Type", "Nullable", "DefaultValue", "MaxLength", "FixedLength", "Precision", "Scale", "Unicode", "Collation", "ConcurrencyMode", "CollectionKind", "SRID"], + /*elements*/["CollectionType", "ReferenceType", "RowType", "TypeAnnotation*", "ValueAnnotation*"] + ), + PropertyRef: schemaElement( + /*attributes*/["Name"] + ), + PropertyValue: schemaElement( + /*attributes*/["Property", "Path", "String", "Int", "Float", "Decimal", "Bool", "DateTime", "DateTimeOffset", "Guid", "Binary", "Time"], + /*Elements*/["Path", "String", "Int", "Float", "Decimal", "Bool", "DateTime", "DateTimeOffset", "Guid", "Binary", "Time", "Collection", "Record", "LabeledElement", "Null"] + ), + ReferenceType: schemaElement( + /*attributes*/["Type"] + ), + ReferentialConstraint: schemaElement( + /*attributes*/null, + /*elements*/["Principal", "Dependent"] + ), + ReturnType: schemaElement( + /*attributes*/["ReturnType", "Type", "EntitySet"], + /*elements*/["CollectionType", "ReferenceType", "RowType"] + ), + RowType: schemaElement( + /*elements*/["Property*"] + ), + String: schemaElement( + /*attributes*/null, + /*elements*/null, + /*text*/true + ), + Schema: schemaElement( + /*attributes*/["Namespace", "Alias"], + /*elements*/["Using*", "EntityContainer*", "EntityType*", "Association*", "ComplexType*", "Function*", "ValueTerm*", "Annotations*"] + ), + Time: schemaElement( + /*attributes*/null, + /*elements*/null, + /*text*/true + ), + TypeAnnotation: schemaElement( + /*attributes*/["Term", "Qualifier"], + /*elements*/["PropertyValue*"] + ), + TypeRef: schemaElement( + /*attributes*/["Type", "Nullable", "DefaultValue", "MaxLength", "FixedLength", "Precision", "Scale", "Unicode", "Collation", "SRID"] + ), + Using: schemaElement( + /*attributes*/["Namespace", "Alias"] + ), + ValueAnnotation: schemaElement( + /*attributes*/["Term", "Qualifier", "Path", "String", "Int", "Float", "Decimal", "Bool", "DateTime", "DateTimeOffset", "Guid", "Binary", "Time"], + /*Elements*/["Path", "String", "Int", "Float", "Decimal", "Bool", "DateTime", "DateTimeOffset", "Guid", "Binary", "Time", "Collection", "Record", "LabeledElement", "Null"] + ), + ValueTerm: schemaElement( + /*attributes*/["Name", "Type"], + /*elements*/["TypeAnnotation*", "ValueAnnotation*"] + ), + + // See http://msdn.microsoft.com/en-us/library/dd541238(v=prot.10) for an EDMX reference. + Edmx: schemaElement( + /*attributes*/["Version"], + /*elements*/["DataServices", "Reference*", "AnnotationsReference*"], + /*text*/false, + /*ns*/edmxNs + ), + DataServices: schemaElement( + /*attributes*/null, + /*elements*/["Schema*"], + /*text*/false, + /*ns*/edmxNs + ) + } + }; + + // See http://msdn.microsoft.com/en-us/library/ee373839.aspx for a feed customization reference. + var customizationAttributes = ["m:FC_ContentKind", "m:FC_KeepInContent", "m:FC_NsPrefix", "m:FC_NsUri", "m:FC_SourcePath", "m:FC_TargetPath"]; + schema.elements.Property.attributes = schema.elements.Property.attributes.concat(customizationAttributes); + schema.elements.EntityType.attributes = schema.elements.EntityType.attributes.concat(customizationAttributes); + + // See http://msdn.microsoft.com/en-us/library/dd541284(PROT.10).aspx for an EDMX reference. + schema.elements.Edmx = { attributes: ["Version"], elements: ["DataServices"], ns: edmxNs }; + schema.elements.DataServices = { elements: ["Schema*"], ns: edmxNs }; + + // See http://msdn.microsoft.com/en-us/library/dd541233(v=PROT.10) for Conceptual Schema Definition Language Document for Data Services. + schema.elements.EntityContainer.attributes.push("m:IsDefaultEntityContainer"); + schema.elements.Property.attributes.push("m:MimeType"); + schema.elements.FunctionImport.attributes.push("m:HttpMethod"); + schema.elements.FunctionImport.attributes.push("m:IsAlwaysBindable"); + schema.elements.EntityType.attributes.push("m:HasStream"); + schema.elements.DataServices.attributes = ["m:DataServiceVersion", "m:MaxDataServiceVersion"]; + + var scriptCase = function (text) { + /// Converts a Pascal-case identifier into a camel-case identifier. + /// Text to convert. + /// Converted text. + /// If the text starts with multiple uppercase characters, it is left as-is. + + if (!text) { + return text; + } + + if (text.length > 1) { + var firstTwo = text.substr(0, 2); + if (firstTwo === firstTwo.toUpperCase()) { + return text; + } + + return text.charAt(0).toLowerCase() + text.substr(1); + } + + return text.charAt(0).toLowerCase(); + }; + + var getChildSchema = function (parentSchema, candidateName) { + /// Gets the schema node for the specified element. + /// Schema of the parent XML node of 'element'. + /// XML element name to consider. + /// The schema that describes the specified element; null if not found. + + if (candidateName === "Documentation") { + return { isArray: true, propertyName: "documentation" }; + } + + var elements = parentSchema.elements; + if (!elements) { + return null; + } + + var i, len; + for (i = 0, len = elements.length; i < len; i++) { + var elementName = elements[i]; + var multipleElements = false; + if (elementName.charAt(elementName.length - 1) === "*") { + multipleElements = true; + elementName = elementName.substr(0, elementName.length - 1); + } + + if (candidateName === elementName) { + var propertyName = scriptCase(elementName); + return { isArray: multipleElements, propertyName: propertyName }; + } + } + + return null; + }; + + // This regular expression is used to detect a feed customization element + // after we've normalized it into the 'm' prefix. It starts with m:FC_, + // followed by other characters, and ends with _ and a number. + // The captures are 0 - whole string, 1 - name as it appears in internal table. + var isFeedCustomizationNameRE = /^(m:FC_.*)_[0-9]+$/; + + var isEdmNamespace = function (nsURI) { + /// Checks whether the specifies namespace URI is one of the known CSDL namespace URIs. + /// Namespace URI to check. + /// true if nsURI is a known CSDL namespace; false otherwise. + + return nsURI === edmNs1 + || nsURI === edmNs1_1 + || nsURI === edmNs1_2 + || nsURI === edmNs2a + || nsURI === edmNs2b + || nsURI === edmNs3 + }; + + var parseConceptualModelElement = function (element) { + /// Parses a CSDL document. + /// DOM element to parse. + /// An object describing the parsed element. + + var localName = xmlLocalName(element); + var nsURI = xmlNamespaceURI(element); + var elementSchema = schema.elements[localName]; + if (!elementSchema) { + return null; + } + + if (elementSchema.ns) { + if (nsURI !== elementSchema.ns) { + return null; + } + } else if (!isEdmNamespace(nsURI)) { + return null; + } + + var item = {}; + var extensions = []; + var attributes = elementSchema.attributes || []; + xmlAttributes(element, function (attribute) { + + var localName = xmlLocalName(attribute); + var nsURI = xmlNamespaceURI(attribute); + var value = attribute.value; + + // Don't do anything with xmlns attributes. + if (nsURI === xmlnsNS) { + return; + } + + // Currently, only m: for metadata is supported as a prefix in the internal schema table, + // un-prefixed element names imply one a CSDL element. + var schemaName = null; + var handled = false; + if (isEdmNamespace(nsURI) || nsURI === null) { + schemaName = ""; + } else if (nsURI === odataMetaXmlNs) { + schemaName = "m:"; + } + + if (schemaName !== null) { + schemaName += localName; + + // Feed customizations for complex types have additional + // attributes with a suffixed counter starting at '1', so + // take that into account when doing the lookup. + var match = isFeedCustomizationNameRE.exec(schemaName); + if (match) { + schemaName = match[1]; + } + + if (contains(attributes, schemaName)) { + handled = true; + item[scriptCase(localName)] = value; + } + } + + if (!handled) { + extensions.push(createAttributeExtension(attribute)); + } + }); + + xmlChildElements(element, function (child) { + var localName = xmlLocalName(child); + var childSchema = getChildSchema(elementSchema, localName); + if (childSchema) { + if (childSchema.isArray) { + var arr = item[childSchema.propertyName]; + if (!arr) { + arr = []; + item[childSchema.propertyName] = arr; + } + arr.push(parseConceptualModelElement(child)); + } else { + item[childSchema.propertyName] = parseConceptualModelElement(child); + } + } else { + extensions.push(createElementExtension(child)); + } + }); + + if (elementSchema.text) { + item.text = xmlInnerText(element); + } + + if (extensions.length) { + item.extensions = extensions; + } + + return item; + }; + + var metadataParser = function (handler, text) { + /// Parses a metadata document. + /// This handler. + /// Metadata text. + /// An object representation of the conceptual model. + + var doc = xmlParse(text); + var root = xmlFirstChildElement(doc); + return parseConceptualModelElement(root) || undefined; + }; + + odata.metadataHandler = handler(metadataParser, null, xmlMediaType, MAX_DATA_SERVICE_VERSION); + + + + var PAYLOADTYPE_OBJECT = "o"; + var PAYLOADTYPE_FEED = "f"; + var PAYLOADTYPE_PRIMITIVE = "p"; + var PAYLOADTYPE_COLLECTION = "c"; + var PAYLOADTYPE_SVCDOC = "s"; + var PAYLOADTYPE_LINKS = "l"; + + var odataNs = "odata"; + var odataAnnotationPrefix = odataNs + "."; + + var bindAnnotation = "@" + odataAnnotationPrefix + "bind"; + var metadataAnnotation = odataAnnotationPrefix + "metadata"; + var navUrlAnnotation = odataAnnotationPrefix + "navigationLinkUrl"; + var typeAnnotation = odataAnnotationPrefix + "type"; + + var jsonLightNameMap = { + readLink: "self", + editLink: "edit", + nextLink: "__next", + mediaReadLink: "media_src", + mediaEditLink: "edit_media", + mediaContentType: "content_type", + mediaETag: "media_etag", + count: "__count", + media_src: "mediaReadLink", + edit_media: "mediaEditLink", + content_type: "mediaContentType", + media_etag: "mediaETag", + url: "uri" + }; + + var jsonLightAnnotationInfo = function (annotation) { + /// Gets the name and target of an annotation in a JSON light payload. + /// JSON light payload annotation. + /// Object containing the annotation name and the target property name. + + if (annotation.indexOf(".") > 0) { + var targetEnd = annotation.indexOf("@"); + var target = targetEnd > -1 ? annotation.substring(0, targetEnd) : null; + var name = annotation.substring(targetEnd + 1); + + return { + target: target, + name: name, + isOData: name.indexOf(odataAnnotationPrefix) === 0 + }; + } + return null; + }; + + var jsonLightDataItemType = function (name, value, container, dataItemModel, model) { + /// Gets the type name of a JSON light data item that belongs to a feed, an entry, a complex type property, or a collection property. + /// Name of the data item for which the type name is going to be retrieved. + /// Value of the data item. + /// JSON light object that owns the data item. + /// Object describing the data item in an OData conceptual schema. + /// Object describing an OData conceptual schema. + /// + /// This function will first try to get the type name from the data item's value itself if it is a JSON light object; otherwise + /// it will try to get it from the odata.type annotation applied to the data item in the container. Then, it will fallback to the data item model. + /// If all attempts fail, it will return null. + /// + /// Data item type name; null if the type name cannot be found. + + return (isComplex(value) && value[typeAnnotation]) || + (container && container[name + "@" + typeAnnotation]) || + (dataItemModel && dataItemModel.type) || + (lookupNavigationPropertyType(dataItemModel, model)) || + null; + }; + + var jsonLightDataItemModel = function (name, containerModel) { + /// Gets an object describing a data item in an OData conceptual schema. + /// Name of the data item for which the model is going to be retrieved. + /// Object describing the owner of the data item in an OData conceptual schema. + /// Object describing the data item; null if it cannot be found. + + if (containerModel) { + return lookupProperty(containerModel.property, name) || + lookupProperty(containerModel.navigationProperty, name); + } + return null; + }; + + var jsonLightIsEntry = function (data) { + /// Determines whether data represents a JSON light entry object. + /// JSON light object to test. + /// True if the data is JSON light entry object; false otherwise. + + return isComplex(data) && data.hasOwnProperty(odataAnnotationPrefix + "id"); + }; + + var jsonLightIsNavigationProperty = function (name, data, dataItemModel) { + /// Determines whether a data item in a JSON light object is a navigation property. + /// Name of the data item to test. + /// JSON light object that owns the data item. + /// Object describing the data item in an OData conceptual schema. + /// True if the data item is a navigation property; false otherwise. + + if (!!data[name + "@" + navUrlAnnotation] || (dataItemModel && dataItemModel.relationship)) { + return true; + } + + // Sniff the property value. + var value = isArray(data[name]) ? data[name][0] : data[name]; + return jsonLightIsEntry(value); + }; + + var jsonLightIsPrimitiveType = function (typeName) { + /// Determines whether a type name is a primitive type in a JSON light payload. + /// Type name to test. + /// True if the type name an EDM primitive type or an OData spatial type; false otherwise. + + return isPrimitiveEdmType(typeName) || isGeographyEdmType(typeName) || isGeometryEdmType(typeName); + }; + + var jsonLightReadDataAnnotations = function (data, obj, baseURI, dataModel, model) { + /// Converts annotations found in a JSON light payload object to either properties or metadata. + /// JSON light payload object containing the annotations to convert. + /// Object that will store the converted annotations. + /// Base URI for normalizing relative URIs found in the payload. + /// Object describing the JSON light payload in an OData conceptual schema. + /// Object describing an OData conceptual schema. + /// JSON light payload object with its annotations converted to either properties or metadata. + + for (var name in data) { + if (name.indexOf(".") > 0 && name.charAt(0) !== "#") { + var annotationInfo = jsonLightAnnotationInfo(name); + if (annotationInfo) { + var annotationName = annotationInfo.name; + var target = annotationInfo.target; + var targetModel = null; + var targetType = null; + + if (target) { + targetModel = jsonLightDataItemModel(target, dataModel); + targetType = jsonLightDataItemType(target, data[target], data, targetModel, model); + } + + if (annotationInfo.isOData) { + jsonLightApplyPayloadODataAnnotation(annotationName, target, targetType, data[name], data, obj, baseURI); + } else { + obj[name] = data[name]; + } + } + } + } + return obj; + }; + 5 + var jsonLightApplyPayloadODataAnnotation = function (name, target, targetType, value, data, obj, baseURI) { + /// + /// Processes a JSON Light payload OData annotation producing either a property, payload metadata, or property metadata on its owner object. + /// + /// Annotation name. + /// Name of the property that is being targeted by the annotation. + /// Type name of the target property. + /// JSON light object containing the annotation. + /// Object that will hold properties produced by the annotation. + /// Base URI for normalizing relative URIs found in the payload. + + var annotation = name.substring(odataAnnotationPrefix.length); + + switch (annotation) { + case "navigationLinkUrl": + jsonLightApplyNavigationUrlAnnotation(annotation, target, targetType, value, data, obj, baseURI); + return; + case "nextLink": + case "count": + jsonLightApplyFeedAnnotation(annotation, target, value, obj, baseURI); + return; + case "mediaReadLink": + case "mediaEditLink": + case "mediaContentType": + case "mediaETag": + jsonLightApplyMediaAnnotation(annotation, target, targetType, value, obj, baseURI); + return; + default: + jsonLightApplyMetadataAnnotation(annotation, target, value, obj, baseURI); + return; + } + }; + + var jsonLightApplyMetadataAnnotation = function (name, target, value, obj, baseURI) { + /// + /// Converts a JSON light annotation that applies to entry metadata only (i.e. odata.editLink or odata.readLink) and its value + /// into their library's internal representation and saves it back to data. + /// + /// Annotation name. + /// Name of the property on which the annotation should be applied. + /// Annotation value. + /// Object that will hold properties produced by the annotation. + /// Base URI for normalizing relative URIs found in the payload. + + var metadata = obj.__metadata = obj.__metadata || {}; + var mappedName = jsonLightNameMap[name] || name; + + if (name === "editLink") { + metadata.uri = normalizeURI(value, baseURI); + metadata[mappedName] = metadata.uri; + return; + } + + if (name === "readLink" || name === "associationLinkUrl") { + value = normalizeURI(value, baseURI); + } + + if (target) { + var propertiesMetadata = metadata.properties = metadata.properties || {}; + var propertyMetadata = propertiesMetadata[target] = propertiesMetadata[target] || {}; + + if (name === "type") { + propertyMetadata[mappedName] = propertyMetadata[mappedName] || value; + return; + } + propertyMetadata[mappedName] = value; + return; + } + metadata[mappedName] = value; + }; + + var jsonLightApplyFeedAnnotation = function (name, target, value, obj, baseURI) { + /// + /// Converts a JSON light annotation that applies to feeds only (i.e. odata.count or odata.nextlink) and its value + /// into their library's internal representation and saves it back to data. + /// + /// Annotation name. + /// Name of the property on which the annotation should be applied. + /// Annotation value. + /// Object that will hold properties produced by the annotation. + /// Base URI for normalizing relative URIs found in the payload. + + var mappedName = jsonLightNameMap[name]; + var feed = target ? obj[target] : obj; + feed[mappedName] = (name === "nextLink") ? normalizeURI(value, baseURI) : value; + }; + + var jsonLightApplyMediaAnnotation = function (name, target, targetType, value, obj, baseURI) { + /// + /// Converts a JSON light media annotation in and its value into their library's internal representation + /// and saves it back to data or metadata. + /// + /// Annotation name. + /// Name of the property on which the annotation should be applied. + /// Type name of the target property. + /// Annotation value. + /// Object that will hold properties produced by the annotation. + /// Base URI for normalizing relative URIs found in the payload. + + var metadata = obj.__metadata = obj.__metadata || {}; + var mappedName = jsonLightNameMap[name]; + + if (name === "mediaReadLink" || name === "mediaEditLink") { + value = normalizeURI(value, baseURI); + } + + if (target) { + var propertiesMetadata = metadata.properties = metadata.properties || {}; + var propertyMetadata = propertiesMetadata[target] = propertiesMetadata[target] || {}; + propertyMetadata.type = propertyMetadata.type || targetType; + + obj.__metadata = metadata; + obj[target] = obj[target] || { __mediaresource: {} }; + obj[target].__mediaresource[mappedName] = value; + return; + } + + metadata[mappedName] = value; + }; + + var jsonLightApplyNavigationUrlAnnotation = function (name, target, targetType, value, data, obj, baseURI) { + /// + /// Converts a JSON light navigation property annotation and its value into their library's internal representation + /// and saves it back to data o metadata. + /// + /// Annotation name. + /// Name of the property on which the annotation should be applied. + /// Type name of the target property. + /// Annotation value. + /// JSON light object containing the annotation. + /// Object that will hold properties produced by the annotation. + /// Base URI for normalizing relative URIs found in the payload. + + var metadata = obj.__metadata = obj.__metadata || {}; + var propertiesMetadata = metadata.properties = metadata.properties || {}; + var propertyMetadata = propertiesMetadata[target] = propertiesMetadata[target] || {}; + var uri = normalizeURI(value, baseURI); + + if (data.hasOwnProperty(target)) { + // The navigation property is inlined in the payload, + // so the navigation link url should be pushed to the object's + // property metadata instead. + propertyMetadata.navigationLinkUrl = uri; + return; + } + obj[target] = { __deferred: { uri: uri} }; + propertyMetadata.type = propertyMetadata.type || targetType; + }; + + + var jsonLightReadDataItemValue = function (value, typeName, dataItemMetadata, baseURI, dataItemModel, model, recognizeDates) { + /// Converts the value of a data item in a JSON light object to its library representation. + /// Data item value to convert. + /// Type name of the data item. + /// Object containing metadata about the data item. + /// Base URI for normalizing relative URIs found in the payload. + /// Object describing the data item in an OData conceptual schema. + /// Object describing an OData conceptual schema. + /// Flag indicating whether datetime literal strings should be converted to JavaScript Date objects. + /// Data item value in its library representation. + + if (typeof value === "string") { + return jsonLightReadStringPropertyValue(value, typeName, recognizeDates); + } + + if (!jsonLightIsPrimitiveType(typeName)) { + if (isArray(value)) { + return jsonLightReadCollectionPropertyValue(value, typeName, dataItemMetadata, baseURI, model, recognizeDates); + } + + if (isComplex(value)) { + return jsonLightReadComplexPropertyValue(value, typeName, dataItemMetadata, baseURI, model, recognizeDates); + } + } + return value; + }; + + var jsonLightReadStringPropertyValue = function (value, propertyType, recognizeDates) { + /// Convertes the value of a string property in a JSON light object to its library representation. + /// String value to convert. + /// Type name of the property. + /// Flag indicating whether datetime literal strings should be converted to JavaScript Date objects. + /// String property value in its library representation. + + switch (propertyType) { + case EDM_TIME: + return parseDuration(value); + case EDM_DATETIME: + return parseDateTime(value, /*nullOnError*/false); + case EDM_DATETIMEOFFSET: + return parseDateTimeOffset(value, /*nullOnError*/false); + } + + if (recognizeDates) { + return parseDateTime(value, /*nullOnError*/true) + || parseDateTimeOffset(value, /*nullOnError*/true) + || value; + } + return value; + }; + + var jsonLightReadCollectionPropertyValue = function (value, propertyType, propertyMetadata, baseURI, model, recognizeDates) { + /// Converts the value of a collection property in a JSON light object into its library representation. + /// Collection property value to convert. + /// Property type name. + /// Object containing metadata about the collection property. + /// Base URI for normalizing relative URIs found in the payload. + /// Object describing an OData conceptual schema. + /// Flag indicating whether datetime literal strings should be converted to JavaScript Date objects. + /// Collection property value in its library representation. + + var collectionType = getCollectionType(propertyType); + var itemsMetadata = []; + var items = []; + + var i, len; + for (i = 0, len = value.length; i < len; i++) { + var itemType = jsonLightDataItemType(null, value[i]) || collectionType; + var itemMetadata = { type: itemType }; + var item = jsonLightReadDataItemValue(value[i], itemType, itemMetadata, baseURI, null, model, recognizeDates); + + if (!jsonLightIsPrimitiveType(itemType) && !isPrimitive(value[i])) { + itemsMetadata.push(itemMetadata); + } + items.push(item); + } + + if (itemsMetadata.length > 0) { + propertyMetadata.elements = itemsMetadata; + } + + return { __metadata: { type: propertyType }, results: items }; + }; + + var jsonLightReadComplexPropertyValue = function (value, propertyType, propertyMetadata, baseURI, model, recognizeDates) { + /// Converts the value of a comples property in a JSON light object into its library representation. + /// Complex property value to convert. + /// Property type name. + /// Object containing metadata about the complx type property. + /// Base URI for normalizing relative URIs found in the payload. + /// Object describing an OData conceptual schema. + /// Flag indicating whether datetime literal strings should be converted to JavaScript Date objects. + /// Complex property value in its library representation. + + var complexValue = jsonLightReadObject(value, propertyType, baseURI, model, recognizeDates); + var complexMetadata = complexValue.__metadata; + var complexPropertiesMetadata = complexMetadata.properties; + + if (complexPropertiesMetadata) { + propertyMetadata.properties = complexPropertiesMetadata; + delete complexMetadata.properties; + } + return complexValue; + }; + + var jsonLightReadNavigationPropertyValue = function (value, propertyType, baseURI, model, recognizeDates) { + /// Converts the value of a navigation property in a JSON light object into its library representation. + /// Navigation property property value to convert. + /// Property type name. + /// Base URI for normalizing relative URIs found in the payload. + /// Object describing an OData conceptual schema. + /// Flag indicating whether datetime literal strings should be converted to JavaScript Date objects. + /// Collection property value in its library representation. + + if (isArray(value)) { + return jsonLightReadFeed(value, propertyType, baseURI, model, recognizeDates); + } + + if (isComplex(value)) { + return jsonLightReadObject(value, propertyType, baseURI, model, recognizeDates); + } + return null; + }; + + var jsonLightReadObject = function (data, typeName, baseURI, model, recognizeDates) { + /// Converts a JSON light entry or complex type object into its library representation. + /// JSON light entry or complex type object to convert. + /// Type name of the entry or complex type. + /// Base URI for normalizing relative URIs found in the payload. + /// Object describing an OData conceptual schema. + /// Flag indicating whether datetime literal strings should be converted to JavaScript Date objects. + /// Entry or complex type object. + + var actualType = data[typeAnnotation] || typeName; + var dataModel = lookupEntityType(actualType, model) || lookupComplexType(actualType, model); + + var metadata = { type: actualType }; + var obj = { __metadata: metadata }; + var propertiesMetadata = {}; + + for (var name in data) { + if (name.indexOf("#") === 0) { + // This is an advertised function or action. + jsonLightReadAdvertisedFunctionOrAction(name.substring(1), data[name], obj, baseURI, model); + } else { + // Is name NOT an annotation? + if (name.indexOf(".") === -1) { + if (!metadata.properties) { + metadata.properties = propertiesMetadata; + } + + var propertyValue = data[name]; + var propertyModel = jsonLightDataItemModel(name, dataModel); + var isNavigationProperty = jsonLightIsNavigationProperty(name, data, propertyModel); + var propertyType = jsonLightDataItemType(name, propertyValue, data, propertyModel, model); + var propertyMetadata = propertiesMetadata[name] = propertiesMetadata[name] || { type: propertyType }; + + obj[name] = isNavigationProperty ? + jsonLightReadNavigationPropertyValue(propertyValue, propertyType, baseURI, model, recognizeDates) : + jsonLightReadDataItemValue(propertyValue, propertyType, propertyMetadata, baseURI, propertyModel, model, recognizeDates); + } + } + } + + return jsonLightReadDataAnnotations(data, obj, baseURI, dataModel, model); + }; + + var jsonLightReadAdvertisedFunctionOrAction = function (name, value, obj, baseURI, model) { + /// Converts a JSON light advertised action or function object into its library representation. + /// Advertised action or function name. + /// Advertised action or function value. + /// Object that will the converted value of the advertised action or function. + /// Base URI for normalizing the action's or function's relative URIs. + /// Object describing an OData conceptual schema. + /// + /// Actions and functions have the same representation in json light, so to disambiguate them the function uses + /// the model object. If available, the function will look for the functionImport object that describes the + /// the action or the function. If for whatever reason the functionImport can't be retrieved from the model (like + /// there is no model available or there is no functionImport within the model), then the value is going to be treated + /// as an advertised action and stored under obj.__metadata.actions. + /// + + if (!name || !isArray(value) && !isComplex(value)) { + return; + } + + var isFunction = false; + var nsEnd = name.lastIndexOf("."); + var simpleName = name.substring(nsEnd + 1); + var containerName = (nsEnd > -1) ? name.substring(0, nsEnd) : ""; + + var container = (simpleName === name || containerName.indexOf(".") === -1) ? + lookupDefaultEntityContainer(model) : + lookupEntityContainer(containerName, model); + + if (container) { + var functionImport = lookupFunctionImport(container.functionImport, simpleName); + if (functionImport && !!functionImport.isSideEffecting) { + isFunction = !parseBool(functionImport.isSideEffecting); + } + }; + + var metadata = obj.__metadata; + var targetName = isFunction ? "functions" : "actions" + var metadataURI = normalizeURI(name, baseURI); + var items = (isArray(value)) ? value : [value]; + + var i, len; + for (i = 0, len = items.length; i < len; i++) { + var item = items[i]; + if (item) { + var targetCollection = metadata[targetName] = metadata[targetName] || []; + var actionOrFunction = { metadata: metadataURI, title: item.title, target: normalizeURI(item.target, baseURI) }; + targetCollection.push(actionOrFunction); + } + } + }; + + var jsonLightReadFeed = function (data, typeName, baseURI, model, recognizeDates) { + /// Converts a JSON light feed or top level collection property object into its library representation. + /// JSON light feed object to convert. + /// Type name of the feed or collection items. + /// Base URI for normalizing relative URIs found in the payload. + /// Object describing an OData conceptual schema. + /// Flag indicating whether datetime literal strings should be converted to JavaScript Date objects. + /// Feed or top level collection object. + + var items = isArray(data) ? data : data.value; + var entries = []; + + var i, len; + for (i = 0, len = items.length; i < len; i++) { + entries.push(jsonLightReadObject(items[i], typeName, baseURI, model, recognizeDates)); + } + + var feed = { results: entries }; + + if (isComplex(data)) { + for (var name in data) { + if (name.indexOf("#") === 0) { + // This is an advertised function or action. + feed.__metadata = feed.__metadata || {}; + jsonLightReadAdvertisedFunctionOrAction(name.substring(1), data[name], feed, baseURI, model); + } + } + feed = jsonLightReadDataAnnotations(data, feed, baseURI); + } + return feed; + }; + + var jsonLightReadTopPrimitiveProperty = function (data, typeName, baseURI, recognizeDates) { + /// Converts a JSON light top level primitive property object into its library representation. + /// JSON light feed object to convert. + /// Type name of the primitive property. + /// Base URI for normalizing relative URIs found in the payload. + /// Flag indicating whether datetime literal strings should be converted to JavaScript Date objects. + /// Top level primitive property object. + + var metadata = { type: typeName }; + var value = jsonLightReadDataItemValue(data.value, typeName, metadata, baseURI, null, null, recognizeDates); + return jsonLightReadDataAnnotations(data, { __metadata: metadata, value: value }, baseURI); + }; + + var jsonLightReadTopCollectionProperty = function (data, typeName, baseURI, model, recognizeDates) { + /// Converts a JSON light top level collection property object into its library representation. + /// JSON light feed object to convert. + /// Type name of the collection property. + /// Base URI for normalizing relative URIs found in the payload. + /// Object describing an OData conceptual schema. + /// Flag indicating whether datetime literal strings should be converted to JavaScript Date objects. + /// Top level collection property object. + + var propertyMetadata = {}; + var value = jsonLightReadCollectionPropertyValue(data.value, typeName, propertyMetadata, baseURI, model, recognizeDates); + extend(value.__metadata, propertyMetadata); + return jsonLightReadDataAnnotations(data, value, baseURI); + }; + + var jsonLightReadLinksDocument = function (data, baseURI) { + /// Converts a JSON light links collection object to its library representation. + /// JSON light link object to convert. + /// Base URI for normalizing relative URIs found in the payload. + /// Links collection object. + + var items = data.value; + if (!isArray(items)) { + return jsonLightReadLink(data, baseURI); + } + + var results = []; + var i, len; + for (i = 0, len = items.length; i < len; i++) { + results.push(jsonLightReadLink(items[i], baseURI)); + } + + var links = { results: results }; + return jsonLightReadDataAnnotations(data, links, baseURI); + }; + + var jsonLightReadLink = function (data, baseURI) { + /// Converts a JSON light link object to its library representation. + /// JSON light link object to convert. + /// Base URI for normalizing relative URIs found in the payload. + /// Link object. + + var link = { uri: normalizeURI(data.url, baseURI) }; + + link = jsonLightReadDataAnnotations(data, link, baseURI); + var metadata = link.__metadata || {}; + var metadataProperties = metadata.properties || {}; + + jsonLightRemoveTypePropertyMetadata(metadataProperties.url); + renameProperty(metadataProperties, "url", "uri"); + + return link; + }; + + var jsonLightRemoveTypePropertyMetadata = function (propertyMetadata) { + /// Removes the type property from a property metadata object. + /// Property metadata object. + + if (propertyMetadata) { + delete propertyMetadata.type; + } + }; + + var jsonLightReadSvcDocument = function (data, baseURI) { + /// Converts a JSON light service document object to its library representation. + /// JSON light service document object to convert. + /// Base URI for normalizing relative URIs found in the payload. + /// Link object. + + var items = data.value; + var collections = []; + var workspace = jsonLightReadDataAnnotations(data, { collections: collections }, baseURI); + + var metadata = workspace.__metadata || {}; + var metadataProperties = metadata.properties || {}; + + jsonLightRemoveTypePropertyMetadata(metadataProperties.value); + renameProperty(metadataProperties, "value", "collections"); + + var i, len; + for (i = 0, len = items.length; i < len; i++) { + var item = items[i]; + var collection = { title: item.name, href: normalizeURI(item.url, baseURI) }; + + collection = jsonLightReadDataAnnotations(item, collection, baseURI); + metadata = collection.__metadata || {}; + metadataProperties = metadata.properties || {}; + + jsonLightRemoveTypePropertyMetadata(metadataProperties.name); + jsonLightRemoveTypePropertyMetadata(metadataProperties.url); + + renameProperty(metadataProperties, "name", "title"); + renameProperty(metadataProperties, "url", "href"); + + collections.push(collection); + } + + return { workspaces: [workspace] }; + }; + + var jsonLightMakePayloadInfo = function (kind, type) { + /// Creates an object containing information for the json light payload. + /// JSON light payload kind, one of the PAYLOADTYPE_XXX constant values. + /// Type name of the JSON light payload. + /// Object with kind and type fields. + + /// Kind of the JSON light payload. One of the PAYLOADTYPE_XXX constant values. + /// Data type of the JSON light payload. + + return { kind: kind, type: type || null }; + }; + + var jsonLightPayloadInfo = function (data, model, inferFeedAsComplexType) { + /// Infers the information describing the JSON light payload from its metadata annotation, structure, and data model. + /// Json light response payload object. + /// Object describing an OData conceptual schema. + /// True if a JSON light payload that looks like a feed should be treated as a complex type property instead. + /// + /// If the arguments passed to the function don't convey enough information about the payload to determine without doubt that the payload is a feed then it + /// will try to use the payload object structure instead. If the payload looks like a feed (has value property that is an array or non-primitive values) then + /// the function will report its kind as PAYLOADTYPE_FEED unless the inferFeedAsComplexType flag is set to true. This flag comes from the user request + /// and allows the user to control how the library behaves with an ambigous JSON light payload. + /// + /// + /// Object with kind and type fields. Null if there is no metadata annotation or the payload info cannot be obtained.. + /// + + var metadataUri = data[metadataAnnotation]; + if (!metadataUri || typeof metadataUri !== "string") { + return null; + } + + var fragmentStart = metadataUri.lastIndexOf("#"); + if (fragmentStart === -1) { + return jsonLightMakePayloadInfo(PAYLOADTYPE_SVCDOC); + } + + var elementStart = metadataUri.indexOf("@Element", fragmentStart); + var fragmentEnd = elementStart - 1; + + if (fragmentEnd < 0) { + fragmentEnd = metadataUri.indexOf("?", fragmentStart); + if (fragmentEnd === -1) { + fragmentEnd = metadataUri.length; + } + } + + var fragment = metadataUri.substring(fragmentStart + 1, fragmentEnd); + if (fragment.indexOf("/$links/") > 0) { + return jsonLightMakePayloadInfo(PAYLOADTYPE_LINKS); + } + + var fragmentParts = fragment.split("/"); + if (fragmentParts.length >= 0) { + var qualifiedName = fragmentParts[0]; + var typeCast = fragmentParts[1]; + + if (jsonLightIsPrimitiveType(qualifiedName)) { + return jsonLightMakePayloadInfo(PAYLOADTYPE_PRIMITIVE, qualifiedName); + } + + if (isCollectionType(qualifiedName)) { + return jsonLightMakePayloadInfo(PAYLOADTYPE_COLLECTION, qualifiedName); + } + + var entityType = typeCast; + if (!typeCast) { + var nsEnd = qualifiedName.lastIndexOf("."); + var simpleName = qualifiedName.substring(nsEnd + 1); + var container = (simpleName === qualifiedName) ? + lookupDefaultEntityContainer(model) : + lookupEntityContainer(qualifiedName.substring(0, nsEnd), model); + + if (container) { + var entitySet = lookupEntitySet(container.entitySet, simpleName); + entityType = !!entitySet ? entitySet.entityType : null + } + } + + if (elementStart > 0) { + return jsonLightMakePayloadInfo(PAYLOADTYPE_OBJECT, entityType); + } + + if (entityType) { + return jsonLightMakePayloadInfo(PAYLOADTYPE_FEED, entityType); + } + + if (isArray(data.value) && !lookupComplexType(qualifiedName, model)) { + var item = data.value[0]; + if (!isPrimitive(item)) { + if (jsonLightIsEntry(item) || !inferFeedAsComplexType) { + return jsonLightMakePayloadInfo(PAYLOADTYPE_FEED, null); + } + } + } + + return jsonLightMakePayloadInfo(PAYLOADTYPE_OBJECT, qualifiedName); + } + + return null; + }; + + var jsonLightReadPayload = function (data, model, recognizeDates, inferFeedAsComplexType) { + /// Converts a JSON light response payload object into its library's internal representation. + /// Json light response payload object. + /// Object describing an OData conceptual schema. + /// Flag indicating whether datetime literal strings should be converted to JavaScript Date objects. + /// True if a JSON light payload that looks like a feed should be reported as a complex type property instead. + /// Object in the library's representation. + + if (!isComplex(data)) { + return data; + } + + var baseURI = data[metadataAnnotation]; + var payloadInfo = jsonLightPayloadInfo(data, model, inferFeedAsComplexType); + var typeName = null; + if (payloadInfo) { + delete data[metadataAnnotation]; + + typeName = payloadInfo.type; + switch (payloadInfo.kind) { + case PAYLOADTYPE_FEED: + return jsonLightReadFeed(data, typeName, baseURI, model, recognizeDates); + case PAYLOADTYPE_COLLECTION: + return jsonLightReadTopCollectionProperty(data, typeName, baseURI, model, recognizeDates); + case PAYLOADTYPE_PRIMITIVE: + return jsonLightReadTopPrimitiveProperty(data, typeName, baseURI, recognizeDates); + case PAYLOADTYPE_SVCDOC: + return jsonLightReadSvcDocument(data, baseURI); + case PAYLOADTYPE_LINKS: + return jsonLightReadLinksDocument(data, baseURI); + } + } + return jsonLightReadObject(data, typeName, baseURI, model, recognizeDates); + }; + + var jsonLightSerializableMetadata = ["type", "etag", "media_src", "edit_media", "content_type", "media_etag"]; + + var formatJsonLight = function (obj, context) { + /// Converts an object in the library's internal representation to its json light representation. + /// Object the library's internal representation. + /// Object with the serialization context. + /// Object in its json light representation. + + // Regular expression used to test that the uri is for a $links document. + var linksUriRE = /\/\$links\//; + var data = {}; + var metadata = obj.__metadata; + + var islinks = context && linksUriRE.test(context.request.requestUri); + formatJsonLightData(obj, (metadata && metadata.properties), data, islinks); + return data; + }; + + var formatJsonLightMetadata = function (metadata, data) { + /// Formats an object's metadata into the appropriate json light annotations and saves them to data. + /// Object whose metadata is going to be formatted as annotations. + /// Object on which the annotations are going to be stored. + + if (metadata) { + var i, len; + for (i = 0, len = jsonLightSerializableMetadata.length; i < len; i++) { + // There is only a subset of metadata values that are interesting during update requests. + var name = jsonLightSerializableMetadata[i]; + var qName = odataAnnotationPrefix + (jsonLightNameMap[name] || name); + formatJsonLightAnnotation(qName, null, metadata[name], data); + } + } + }; + + var formatJsonLightData = function (obj, pMetadata, data, isLinks) { + /// Formats an object's data into the appropriate json light values and saves them to data. + /// Object whose data is going to be formatted. + /// Object that contains metadata for the properties that are being formatted. + /// Object on which the formatted values are going to be stored. + /// True if a links document is being formatted. False otherwise. + + for (var key in obj) { + var value = obj[key]; + if (key === "__metadata") { + // key is the object metadata. + formatJsonLightMetadata(value, data); + } else if (key.indexOf(".") === -1) { + // key is an regular property or array element. + if (isLinks && key === "uri") { + formatJsonLightEntityLink(value, data); + } else { + formatJsonLightProperty(key, value, pMetadata, data, isLinks); + } + } else { + data[key] = value; + } + } + }; + + var formatJsonLightProperty = function (name, value, pMetadata, data) { + /// Formats an object's value identified by name to its json light representation and saves it to data. + /// Property name. + /// Property value. + /// Object that contains metadata for the property that is being formatted. + /// Object on which the formatted value is going to be stored. + + // Get property type from property metadata + var propertyMetadata = pMetadata && pMetadata[name] || { properties: undefined, type: undefined }; + var typeName = dataItemTypeName(value, propertyMetadata); + + if (isPrimitive(value) || !value) { + // It is a primitive value then. + formatJsonLightAnnotation(typeAnnotation, name, typeName, data); + data[name] = value; + return; + } + + if (isFeed(value, typeName) || isEntry(value)) { + formatJsonLightInlineProperty(name, value, data); + return; + } + + if (!typeName && isDeferred(value)) { + // It is really a deferred property. + formatJsonLightDeferredProperty(name, value, data); + return; + } + + if (isCollection(value, typeName)) { + // The thing is a collection, format it as one. + if (getCollectionType(typeName)) { + formatJsonLightAnnotation(typeAnnotation, name, typeName, data); + } + formatJsonLightCollectionProperty(name, value, data); + return; + } + + + // Format the complex property value in a new object in data[name]. + data[name] = {}; + formatJsonLightAnnotation(typeAnnotation, null, typeName, data[name]); + formatJsonLightData(value, propertyMetadata.properties, data[name]); + }; + + var formatJsonLightEntityLink = function (value, data) { + /// Formats an entity link in a $links document and saves it into data. + /// Entity link value. + /// Object on which the formatted value is going to be stored. + data.url = value; + }; + + var formatJsonLightDeferredProperty = function (name, value, data) { + /// Formats the object value's identified by name as an odata.navigalinkurl annotation and saves it to data. + /// Name of the deferred property to be formatted. + /// Deferred property value to be formatted. + /// Object on which the formatted value is going to be stored. + + formatJsonLightAnnotation(navUrlAnnotation, name, value.__deferred.uri, data); + }; + + var formatJsonLightCollectionProperty = function (name, value, data) { + /// Formats a collection property in obj identified by name as a json light collection property and saves it to data. + /// Name of the collection property to be formatted. + /// Collection property value to be formatted. + /// Object on which the formatted value is going to be stored. + + data[name] = []; + var items = isArray(value) ? value : value.results; + formatJsonLightData(items, null, data[name]); + }; + + var formatJsonLightInlineProperty = function (name, value, data) { + /// Formats an inline feed or entry property in obj identified by name as a json light value and saves it to data. + /// Name of the inline feed or entry property to be formatted. + /// Value of the inline feed or entry property. + /// Object on which the formatted value is going to be stored. + + if (isFeed(value)) { + data[name] = []; + // Format each of the inline feed entries + var entries = isArray(value) ? value : value.results; + var i, len; + for (i = 0, len = entries.length; i < len; i++) { + formatJsonLightInlineEntry(name, entries[i], true, data); + } + return; + } + formatJsonLightInlineEntry(name, value, false, data); + }; + + var formatJsonLightInlineEntry = function (name, value, inFeed, data) { + /// Formats an inline entry value in the property identified by name as a json light value and saves it to data. + /// Name of the inline feed or entry property that owns the entry formatted. + /// Inline entry value to be formatted. + /// True if the entry is in an inline feed; false otherwise. + /// Object on which the formatted value is going to be stored. + + // This might be a bind instead of a deep insert. + var uri = value.__metadata && value.__metadata.uri; + if (uri) { + formatJsonLightBinding(name, uri, inFeed, data); + return; + } + + var entry = formatJsonLight(value); + if (inFeed) { + data[name].push(entry); + return; + } + data[name] = entry; + }; + + var formatJsonLightBinding = function (name, uri, inFeed, data) { + /// Formats an entry binding in the inline property in obj identified by name as an odata.bind annotation and saves it to data. + /// Name of the inline property that has the binding to be formated. + /// Uri to the bound entry. + /// True if the binding is in an inline feed; false otherwise. + /// Object on which the formatted value is going to be stored. + + var bindingName = name + bindAnnotation; + if (inFeed) { + // The binding is inside an inline feed, so merge it with whatever other bindings already exist in data. + data[bindingName] = data[bindingName] || []; + data[bindingName].push(uri); + return; + } + // The binding is on an inline entry; it can be safely overwritten. + data[bindingName] = uri; + }; + + var formatJsonLightAnnotation = function (qName, target, value, data) { + /// Formats a value as a json light annotation and stores it in data + /// Qualified name of the annotation. + /// Name of the property that the metadata value targets. + /// Annotation value. + /// Object on which the annotation is going to be stored. + + if (value !== undefined) { + target ? data[target + "@" + qName] = value : data[qName] = value; + } + }; + + + + var jsonMediaType = "application/json"; + var jsonContentType = contentType(jsonMediaType); + + var jsonReadAdvertisedActionsOrFunctions = function (value) { + /// Reads and object containing action or function metadata and maps them into a single array of objects. + /// Object containing action or function metadata. + /// Array of objects containing metadata for the actions or functions specified in value. + + var result = []; + for (name in value) { + var i, len; + for (i = 0, len = value[name].length; i < len; i++) { + result.push(extend({ metadata: name }, value[name][i])); + } + } + return result; + }; + + var jsonApplyMetadata = function (value, metadata, dateParser, recognizeDates) { + /// Applies metadata coming from both the payload and the metadata object to the value. + /// Data on which the metada is going to be applied. + /// Metadata store; one of edmx, schema, or an array of any of them. + /// Function used for parsing datetime values. + /// + /// True if strings formatted as datetime values should be treated as datetime values. False otherwise. + /// + /// Transformed data. + + if (value && typeof value === "object") { + var dataTypeName; + var valueMetadata = value.__metadata; + + if (valueMetadata) { + if (valueMetadata.actions) { + valueMetadata.actions = jsonReadAdvertisedActionsOrFunctions(valueMetadata.actions); + } + if (valueMetadata.functions) { + valueMetadata.functions = jsonReadAdvertisedActionsOrFunctions(valueMetadata.functions); + } + dataTypeName = valueMetadata && valueMetadata.type; + } + + var dataType = lookupEntityType(dataTypeName, metadata) || lookupComplexType(dataTypeName, metadata); + + if (dataType) { + var properties = dataType.property; + if (properties) { + var i, len; + for (i = 0, len = properties.length; i < len; i++) { + var property = properties[i]; + var propertyName = property.name; + var propertyValue = value[propertyName]; + + if (property.type === "Edm.DateTime" || property.type === "Edm.DateTimeOffset") { + if (propertyValue) { + propertyValue = dateParser(propertyValue); + if (!propertyValue) { + throw { message: "Invalid date/time value" }; + } + value[propertyName] = propertyValue; + } + } else if (property.type === "Edm.Time") { + value[propertyName] = parseDuration(propertyValue); + } + } + } + } else if (recognizeDates) { + for (var name in value) { + propertyValue = value[name]; + if (typeof propertyValue === "string") { + value[name] = dateParser(propertyValue) || propertyValue; + } + } + } + } + return value; + }; + + var isJsonLight = function (contentType) { + /// Tests where the content type indicates a json light payload. + /// Object with media type and properties dictionary. + /// True is the content type indicates a json light payload. False otherwise. + + if (contentType) { + var odata = contentType.properties.odata; + return odata === "nometadata" || odata === "minimalmetadata" || odata === "fullmetadata"; + } + return false; + }; + + var normalizeServiceDocument = function (data, baseURI) { + /// Normalizes a JSON service document to look like an ATOM service document. + /// Object representation of service documents as deserialized. + /// Base URI to resolve relative URIs. + /// An object representation of the service document. + var workspace = { collections: [] }; + + var i, len; + for (i = 0, len = data.EntitySets.length; i < len; i++) { + var title = data.EntitySets[i]; + var collection = { + title: title, + href: normalizeURI(title, baseURI) + }; + + workspace.collections.push(collection); + } + + return { workspaces: [workspace] }; + }; + + // The regular expression corresponds to something like this: + // /Date(123+60)/ + // + // This first number is date ticks, the + may be a - and is optional, + // with the second number indicating a timezone offset in minutes. + // + // On the wire, the leading and trailing forward slashes are + // escaped without being required to so the chance of collisions is reduced; + // however, by the time we see the objects, the characters already + // look like regular forward slashes. + var jsonDateRE = /^\/Date\((-?\d+)(\+|-)?(\d+)?\)\/$/; + + var minutesToOffset = function (minutes) { + /// Formats the given minutes into (+/-)hh:mm format. + /// Number of minutes to format. + /// The minutes in (+/-)hh:mm format. + + var sign; + if (minutes < 0) { + sign = "-"; + minutes = -minutes; + } else { + sign = "+"; + } + + var hours = Math.floor(minutes / 60); + minutes = minutes - (60 * hours); + + return sign + formatNumberWidth(hours, 2) + ":" + formatNumberWidth(minutes, 2); + }; + + var parseJsonDateString = function (value) { + /// Parses the JSON Date representation into a Date object. + /// String value. + /// A Date object if the value matches one; falsy otherwise. + + var arr = value && jsonDateRE.exec(value); + if (arr) { + // 0 - complete results; 1 - ticks; 2 - sign; 3 - minutes + var result = new Date(parseInt10(arr[1])); + if (arr[2]) { + var mins = parseInt10(arr[3]); + if (arr[2] === "-") { + mins = -mins; + } + + // The offset is reversed to get back the UTC date, which is + // what the API will eventually have. + var current = result.getUTCMinutes(); + result.setUTCMinutes(current - mins); + result.__edmType = "Edm.DateTimeOffset"; + result.__offset = minutesToOffset(mins); + } + if (!isNaN(result.valueOf())) { + return result; + } + } + + // Allow undefined to be returned. + }; + + // Some JSON implementations cannot produce the character sequence \/ + // which is needed to format DateTime and DateTimeOffset into the + // JSON string representation defined by the OData protocol. + // See the history of this file for a candidate implementation of + // a 'formatJsonDateString' function. + + var jsonParser = function (handler, text, context) { + /// Parses a JSON OData payload. + /// This handler. + /// Payload text (this parser also handles pre-parsed objects). + /// Object with parsing context. + /// An object representation of the OData payload. + + var recognizeDates = defined(context.recognizeDates, handler.recognizeDates); + var inferJsonLightFeedAsObject = defined(context.inferJsonLightFeedAsObject, handler.inferJsonLightFeedAsObject); + var model = context.metadata; + var dataServiceVersion = context.dataServiceVersion; + var dateParser = parseJsonDateString; + var json = (typeof text === "string") ? window.JSON.parse(text) : text; + + if ((maxVersion("3.0", dataServiceVersion) === dataServiceVersion)) { + if (isJsonLight(context.contentType)) { + return jsonLightReadPayload(json, model, recognizeDates, inferJsonLightFeedAsObject); + } + dateParser = parseDateTime; + } + + json = traverse(json.d, function (key, value) { + return jsonApplyMetadata(value, model, dateParser, recognizeDates); + }); + + json = jsonUpdateDataFromVersion(json, context.dataServiceVersion); + return jsonNormalizeData(json, context.response.requestUri); + }; + + var jsonToString = function (data) { + /// Converts the data into a JSON string. + /// Data to serialize. + /// The JSON string representation of data. + + var result = undefined; + // Save the current date.toJSON function + var dateToJSON = Date.prototype.toJSON; + try { + // Set our own date.toJSON function + Date.prototype.toJSON = function () { + return formatDateTimeOffset(this); + }; + result = window.JSON.stringify(data, jsonReplacer); + } finally { + // Restore the original toJSON function + Date.prototype.toJSON = dateToJSON; + } + return result; + }; + + var jsonSerializer = function (handler, data, context) { + /// Serializes the data by returning its string representation. + /// This handler. + /// Data to serialize. + /// Object with serialization context. + /// The string representation of data. + + var dataServiceVersion = context.dataServiceVersion || "1.0"; + var useJsonLight = defined(context.useJsonLight, handler.useJsonLight); + var cType = context.contentType = context.contentType || jsonContentType; + + if (cType && cType.mediaType === jsonContentType.mediaType) { + var json = data; + if (useJsonLight || isJsonLight(cType)) { + context.dataServiceVersion = maxVersion(dataServiceVersion, "3.0"); + json = formatJsonLight(data, context); + return jsonToString(json); + } + if (maxVersion("3.0", dataServiceVersion) === dataServiceVersion) { + cType.properties.odata = "verbose"; + context.contentType = cType; + } + return jsonToString(json); + } + return undefined; + }; + + var jsonReplacer = function (_, value) { + /// JSON replacer function for converting a value to its JSON representation. + /// Value to convert. + /// JSON representation of the input value. + /// + /// This method is used during JSON serialization and invoked only by the JSON.stringify function. + /// It should never be called directly. + /// + + if (value && value.__edmType === "Edm.Time") { + return formatDuration(value); + } else { + return value; + } + }; + + var jsonNormalizeData = function (data, baseURI) { + /// + /// Normalizes the specified data into an intermediate representation. + /// like the latest supported version. + /// + /// Data to update. + /// URI to use as the base for normalizing references. + + var isSvcDoc = isComplex(data) && !data.__metadata && isArray(data.EntitySets); + return isSvcDoc ? normalizeServiceDocument(data, baseURI) : data; + }; + + var jsonUpdateDataFromVersion = function (data, dataVersion) { + /// + /// Updates the specified data in the specified version to look + /// like the latest supported version. + /// + /// Data to update. + /// Version the data is in (possibly unknown). + + // Strip the trailing comma if there. + if (dataVersion && dataVersion.lastIndexOf(";") === dataVersion.length - 1) { + dataVersion = dataVersion.substr(0, dataVersion.length - 1); + } + + if (!dataVersion || dataVersion === "1.0") { + if (isArray(data)) { + data = { results: data }; + } + } + + return data; + }; + + var jsonHandler = handler(jsonParser, jsonSerializer, jsonMediaType, MAX_DATA_SERVICE_VERSION); + jsonHandler.recognizeDates = false; + jsonHandler.useJsonLight = false; + jsonHandler.inferJsonLightFeedAsObject = false; + + odata.jsonHandler = jsonHandler; + + + + + var batchMediaType = "multipart/mixed"; + var responseStatusRegex = /^HTTP\/1\.\d (\d{3}) (.*)$/i; + var responseHeaderRegex = /^([^()<>@,;:\\"\/[\]?={} \t]+)\s?:\s?(.*)/; + + var hex16 = function () { + /// + /// Calculates a random 16 bit number and returns it in hexadecimal format. + /// + /// A 16-bit number in hex format. + + return Math.floor((1 + Math.random()) * 0x10000).toString(16).substr(1); + }; + + var createBoundary = function (prefix) { + /// + /// Creates a string that can be used as a multipart request boundary. + /// + /// String to use as the start of the boundary string + /// Boundary string of the format: -- + + return prefix + hex16() + "-" + hex16() + "-" + hex16(); + }; + + var partHandler = function (context) { + /// + /// Gets the handler for data serialization of individual requests / responses in a batch. + /// + /// Context used for data serialization. + /// Handler object. + + return context.handler.partHandler; + }; + + var currentBoundary = function (context) { + /// + /// Gets the current boundary used for parsing the body of a multipart response. + /// + /// Context used for parsing a multipart response. + /// Boundary string. + + var boundaries = context.boundaries; + return boundaries[boundaries.length - 1]; + }; + + var batchParser = function (handler, text, context) { + /// Parses a batch response. + /// This handler. + /// Batch text. + /// Object with parsing context. + /// An object representation of the batch. + + var boundary = context.contentType.properties["boundary"]; + return { __batchResponses: readBatch(text, { boundaries: [boundary], handlerContext: context }) }; + }; + + var batchSerializer = function (handler, data, context) { + /// Serializes a batch object representation into text. + /// This handler. + /// Representation of a batch. + /// Object with parsing context. + /// An text representation of the batch object; undefined if not applicable. + + var cType = context.contentType = context.contentType || contentType(batchMediaType); + if (cType.mediaType === batchMediaType) { + return writeBatch(data, context); + } + }; + + var readBatch = function (text, context) { + /// + /// Parses a multipart/mixed response body from from the position defined by the context. + /// + /// Body of the multipart/mixed response. + /// Context used for parsing. + /// Array of objects representing the individual responses. + + var delimiter = "--" + currentBoundary(context); + + // Move beyond the delimiter and read the complete batch + readTo(text, context, delimiter); + + // Ignore the incoming line + readLine(text, context); + + // Read the batch parts + var responses = []; + var partEnd; + + while (partEnd !== "--" && context.position < text.length) { + var partHeaders = readHeaders(text, context); + var partContentType = contentType(partHeaders["Content-Type"]); + + if (partContentType && partContentType.mediaType === batchMediaType) { + context.boundaries.push(partContentType.properties["boundary"]); + try { + var changeResponses = readBatch(text, context); + } catch (e) { + e.response = readResponse(text, context, delimiter); + changeResponses = [e]; + } + responses.push({ __changeResponses: changeResponses }); + context.boundaries.pop(); + readTo(text, context, "--" + currentBoundary(context)); + } else { + if (!partContentType || partContentType.mediaType !== "application/http") { + throw { message: "invalid MIME part type " }; + } + // Skip empty line + readLine(text, context); + // Read the response + var response = readResponse(text, context, delimiter); + try { + if (response.statusCode >= 200 && response.statusCode <= 299) { + partHandler(context.handlerContext).read(response, context.handlerContext); + } else { + // Keep track of failed responses and continue processing the batch. + response = { message: "HTTP request failed", response: response }; + } + } catch (e) { + response = e; + } + + responses.push(response); + } + + partEnd = text.substr(context.position, 2); + + // Ignore the incoming line. + readLine(text, context); + } + return responses; + }; + + var readHeaders = function (text, context) { + /// + /// Parses the http headers in the text from the position defined by the context. + /// + /// Text containing an http response's headers + /// Context used for parsing. + /// Object containing the headers as key value pairs. + /// + /// This function doesn't support split headers and it will stop reading when it hits two consecutive line breaks. + /// + + var headers = {}; + var parts; + var line; + var pos; + + do { + pos = context.position; + line = readLine(text, context); + parts = responseHeaderRegex.exec(line); + if (parts !== null) { + headers[parts[1]] = parts[2]; + } else { + // Whatever was found is not a header, so reset the context position. + context.position = pos; + } + } while (line && parts); + + normalizeHeaders(headers); + + return headers; + }; + + var readResponse = function (text, context, delimiter) { + /// + /// Parses an HTTP response. + /// + /// Text representing the http response. + /// Context used for parsing. + /// String used as delimiter of the multipart response parts. + /// Object representing the http response. + + // Read the status line. + var pos = context.position; + var match = responseStatusRegex.exec(readLine(text, context)); + + var statusCode; + var statusText; + var headers; + + if (match) { + statusCode = match[1]; + statusText = match[2]; + headers = readHeaders(text, context); + readLine(text, context); + } else { + context.position = pos; + } + + return { + statusCode: statusCode, + statusText: statusText, + headers: headers, + body: readTo(text, context, "\r\n" + delimiter) + }; + }; + + var readLine = function (text, context) { + /// + /// Returns a substring from the position defined by the context up to the next line break (CRLF). + /// + /// Input string. + /// Context used for reading the input string. + /// Substring to the first ocurrence of a line break or null if none can be found. + + return readTo(text, context, "\r\n"); + }; + + var readTo = function (text, context, str) { + /// + /// Returns a substring from the position given by the context up to value defined by the str parameter and increments the position in the context. + /// + /// Input string. + /// Context used for reading the input string. + /// Substring to read up to. + /// Substring to the first ocurrence of str or the end of the input string if str is not specified. Null if the marker is not found. + + var start = context.position || 0; + var end = text.length; + if (str) { + end = text.indexOf(str, start); + if (end === -1) { + return null; + } + context.position = end + str.length; + } else { + context.position = end; + } + + return text.substring(start, end); + }; + + var writeBatch = function (data, context) { + /// + /// Serializes a batch request object to a string. + /// + /// Batch request object in payload representation format + /// Context used for the serialization + /// String representing the batch request + + if (!isBatch(data)) { + throw { message: "Data is not a batch object." }; + } + + var batchBoundary = createBoundary("batch_"); + var batchParts = data.__batchRequests; + var batch = ""; + var i, len; + for (i = 0, len = batchParts.length; i < len; i++) { + batch += writeBatchPartDelimiter(batchBoundary, false) + + writeBatchPart(batchParts[i], context); + } + batch += writeBatchPartDelimiter(batchBoundary, true); + + // Register the boundary with the request content type. + var contentTypeProperties = context.contentType.properties; + contentTypeProperties.boundary = batchBoundary; + + return batch; + }; + + var writeBatchPartDelimiter = function (boundary, close) { + /// + /// Creates the delimiter that indicates that start or end of an individual request. + /// + /// Boundary string used to indicate the start of the request + /// Flag indicating that a close delimiter string should be generated + /// Delimiter string + + var result = "\r\n--" + boundary; + if (close) { + result += "--"; + } + + return result + "\r\n"; + }; + + var writeBatchPart = function (part, context, nested) { + /// + /// Serializes a part of a batch request to a string. A part can be either a GET request or + /// a change set grouping several CUD (create, update, delete) requests. + /// + /// Request or change set object in payload representation format + /// Object containing context information used for the serialization + /// Flag indicating that the part is nested inside a change set + /// String representing the serialized part + /// + /// A change set is an array of request objects and they cannot be nested inside other change sets. + /// + + var changeSet = part.__changeRequests; + var result; + if (isArray(changeSet)) { + if (nested) { + throw { message: "Not Supported: change set nested in other change set" }; + } + + var changeSetBoundary = createBoundary("changeset_"); + result = "Content-Type: " + batchMediaType + "; boundary=" + changeSetBoundary + "\r\n"; + var i, len; + for (i = 0, len = changeSet.length; i < len; i++) { + result += writeBatchPartDelimiter(changeSetBoundary, false) + + writeBatchPart(changeSet[i], context, true); + } + + result += writeBatchPartDelimiter(changeSetBoundary, true); + } else { + result = "Content-Type: application/http\r\nContent-Transfer-Encoding: binary\r\n\r\n"; + var partContext = extend({}, context); + partContext.handler = handler; + partContext.request = part; + partContext.contentType = null; + + prepareRequest(part, partHandler(context), partContext); + result += writeRequest(part); + } + + return result; + }; + + var writeRequest = function (request) { + /// + /// Serializes a request object to a string. + /// + /// Request object to serialize + /// String representing the serialized request + + var result = (request.method ? request.method : "GET") + " " + request.requestUri + " HTTP/1.1\r\n"; + for (var name in request.headers) { + if (request.headers[name]) { + result = result + name + ": " + request.headers[name] + "\r\n"; + } + } + + result += "\r\n"; + + if (request.body) { + result += request.body; + } + + return result; + }; + + odata.batchHandler = handler(batchParser, batchSerializer, batchMediaType, MAX_DATA_SERVICE_VERSION); + + + + var handlers = [odata.jsonHandler, odata.atomHandler, odata.xmlHandler, odata.textHandler]; + + var dispatchHandler = function (handlerMethod, requestOrResponse, context) { + /// Dispatches an operation to handlers. + /// Name of handler method to invoke. + /// request/response argument for delegated call. + /// context argument for delegated call. + + var i, len; + for (i = 0, len = handlers.length; i < len && !handlers[i][handlerMethod](requestOrResponse, context); i++) { + } + + if (i === len) { + throw { message: "no handler for data" }; + } + }; + + odata.defaultSuccess = function (data) { + /// Default success handler for OData. + /// Data to process. + + window.alert(window.JSON.stringify(data)); + }; + + odata.defaultError = throwErrorCallback; + + odata.defaultHandler = { + read: function (response, context) { + /// Reads the body of the specified response by delegating to JSON and ATOM handlers. + /// Response object. + /// Operation context. + + if (response && assigned(response.body) && response.headers["Content-Type"]) { + dispatchHandler("read", response, context); + } + }, + + write: function (request, context) { + /// Write the body of the specified request by delegating to JSON and ATOM handlers. + /// Reques tobject. + /// Operation context. + + dispatchHandler("write", request, context); + }, + + maxDataServiceVersion: MAX_DATA_SERVICE_VERSION, + accept: "application/atomsvc+xml;q=0.8, application/json;odata=fullmetadata;q=0.7, application/json;q=0.5, */*;q=0.1" + }; + + odata.defaultMetadata = []; + + odata.read = function (urlOrRequest, success, error, handler, httpClient, metadata) { + /// Reads data from the specified URL. + /// URL to read data from. + /// Callback for a successful read operation. + /// Callback for handling errors. + /// Handler for data serialization. + /// HTTP client layer. + /// Conceptual metadata for this request. + + var request; + if (urlOrRequest instanceof String || typeof urlOrRequest === "string") { + request = { requestUri: urlOrRequest }; + } else { + request = urlOrRequest; + } + + return odata.request(request, success, error, handler, httpClient, metadata); + }; + + odata.request = function (request, success, error, handler, httpClient, metadata) { + /// Sends a request containing OData payload to a server. + /// Object that represents the request to be sent. + /// Callback for a successful read operation. + /// Callback for handling errors. + /// Handler for data serialization. + /// HTTP client layer. + /// Conceptual metadata for this request. + + success = success || odata.defaultSuccess; + error = error || odata.defaultError; + handler = handler || odata.defaultHandler; + httpClient = httpClient || odata.defaultHttpClient; + metadata = metadata || odata.defaultMetadata; + + // Augment the request with additional defaults. + request.recognizeDates = defined(request.recognizeDates, odata.jsonHandler.recognizeDates); + request.callbackParameterName = defined(request.callbackParameterName, odata.defaultHttpClient.callbackParameterName); + request.formatQueryString = defined(request.formatQueryString, odata.defaultHttpClient.formatQueryString); + request.enableJsonpCallback = defined(request.enableJsonpCallback, odata.defaultHttpClient.enableJsonpCallback); + request.useJsonLight = defined(request.useJsonLight, odata.jsonHandler.enableJsonpCallback); + request.inferJsonLightFeedAsObject = defined(request.inferJsonLightFeedAsObject, odata.jsonHandler.inferJsonLightFeedAsObject); + + // Create the base context for read/write operations, also specifying complete settings. + var context = { + metadata: metadata, + recognizeDates: request.recognizeDates, + callbackParameterName: request.callbackParameterName, + formatQueryString: request.formatQueryString, + enableJsonpCallback: request.enableJsonpCallback, + useJsonLight: request.useJsonLight, + inferJsonLightFeedAsObject: request.inferJsonLightFeedAsObject + }; + + try { + prepareRequest(request, handler, context); + return invokeRequest(request, success, error, handler, httpClient, context); + } catch (err) { + error(err); + } + }; + + // Configure the batch handler to use the default handler for the batch parts. + odata.batchHandler.partHandler = odata.defaultHandler; + + + + var localStorage = null; + + var domStoreDateToJSON = function () { + /// Converts a Date object into an object representation friendly to JSON serialization. + /// Object that represents the Date. + /// + /// This method is used to override the Date.toJSON method and is called only by + /// JSON.stringify. It should never be called directly. + /// + + var newValue = { v: this.valueOf(), t: "[object Date]" }; + // Date objects might have extra properties on them so we save them. + for (var name in this) { + newValue[name] = this[name]; + } + return newValue; + }; + + var domStoreJSONToDate = function (_, value) { + /// JSON reviver function for converting an object representing a Date in a JSON stream to a Date object + /// Object to convert. + /// Date object. + /// + /// This method is used during JSON parsing and invoked only by the reviver function. + /// It should never be called directly. + /// + + if (value && value.t === "[object Date]") { + var newValue = new Date(value.v); + for (var name in value) { + if (name !== "t" && name !== "v") { + newValue[name] = value[name]; + } + } + value = newValue; + } + return value; + }; + + var qualifyDomStoreKey = function (store, key) { + /// Qualifies the key with the name of the store. + /// Store object whose name will be used for qualifying the key. + /// Key string. + /// Fully qualified key string. + + return store.name + "#!#" + key; + }; + + var unqualifyDomStoreKey = function (store, key) { + /// Gets the key part of a fully qualified key string. + /// Store object whose name will be used for qualifying the key. + /// Fully qualified key string. + /// Key part string + + return key.replace(store.name + "#!#", ""); + }; + + var DomStore = function (name) { + /// Constructor for store objects that use DOM storage as the underlying mechanism. + /// Store name. + this.name = name; + }; + + DomStore.create = function (name) { + /// Creates a store object that uses DOM Storage as its underlying mechanism. + /// Store name. + /// Store object. + + if (DomStore.isSupported()) { + localStorage = localStorage || window.localStorage; + return new DomStore(name); + } + + throw { message: "Web Storage not supported by the browser" }; + }; + + DomStore.isSupported = function () { + /// Checks whether the underlying mechanism for this kind of store objects is supported by the browser. + /// True if the mechanism is supported by the browser; otherwise false. + return !!window.localStorage; + }; + + DomStore.prototype.add = function (key, value, success, error) { + /// Adds a new value identified by a key to the store. + /// Key string. + /// Value that is going to be added to the store. + /// Callback for a successful add operation. + /// Callback for handling errors. If not specified then store.defaultError is invoked. + /// + /// This method errors out if the store already contains the specified key. + /// + + error = error || this.defaultError; + var store = this; + this.contains(key, function (contained) { + if (!contained) { + store.addOrUpdate(key, value, success, error); + } else { + delay(error, { message: "key already exists", key: key }); + } + }, error); + }; + + DomStore.prototype.addOrUpdate = function (key, value, success, error) { + /// Adds or updates a value identified by a key to the store. + /// Key string. + /// Value that is going to be added or updated to the store. + /// Callback for a successful add or update operation. + /// Callback for handling errors. If not specified then store.defaultError is invoked. + /// + /// This method will overwrite the key's current value if it already exists in the store; otherwise it simply adds the new key and value. + /// + + error = error || this.defaultError; + + if (key instanceof Array) { + error({ message: "Array of keys not supported" }); + } else { + var fullKey = qualifyDomStoreKey(this, key); + var oldDateToJSON = Date.prototype.toJSON; + try { + var storedValue = value; + if (storedValue !== undefined) { + // Dehydrate using json + Date.prototype.toJSON = domStoreDateToJSON; + storedValue = window.JSON.stringify(value); + } + // Save the json string. + localStorage.setItem(fullKey, storedValue); + delay(success, key, value); + } + catch (e) { + if (e.code === 22 || e.number === 0x8007000E) { + delay(error, { name: "QUOTA_EXCEEDED_ERR", error: e }); + } else { + delay(error, e); + } + } + finally { + Date.prototype.toJSON = oldDateToJSON; + } + } + }; + + DomStore.prototype.clear = function (success, error) { + /// Removes all the data associated with this store object. + /// Callback for a successful clear operation. + /// Callback for handling errors. If not specified then store.defaultError is invoked. + /// + /// In case of an error, this method will not restore any keys that might have been deleted at that point. + /// + + error = error || this.defaultError; + try { + var i = 0, len = localStorage.length; + while (len > 0 && i < len) { + var fullKey = localStorage.key(i); + var key = unqualifyDomStoreKey(this, fullKey); + if (fullKey !== key) { + localStorage.removeItem(fullKey); + len = localStorage.length; + } else { + i++; + } + }; + delay(success); + } + catch (e) { + delay(error, e); + } + }; + + DomStore.prototype.close = function () { + /// This function does nothing in DomStore as it does not have a connection model + }; + + DomStore.prototype.contains = function (key, success, error) { + /// Checks whether a key exists in the store. + /// Key string. + /// Callback indicating whether the store contains the key or not. + /// Callback for handling errors. If not specified then store.defaultError is invoked. + error = error || this.defaultError; + try { + var fullKey = qualifyDomStoreKey(this, key); + var value = localStorage.getItem(fullKey); + delay(success, value !== null); + } catch (e) { + delay(error, e); + } + }; + + DomStore.prototype.defaultError = throwErrorCallback; + + DomStore.prototype.getAllKeys = function (success, error) { + /// Gets all the keys that exist in the store. + /// Callback for a successful get operation. + /// Callback for handling errors. If not specified then store.defaultError is invoked. + + error = error || this.defaultError; + + var results = []; + var i, len; + + try { + for (i = 0, len = localStorage.length; i < len; i++) { + var fullKey = localStorage.key(i); + var key = unqualifyDomStoreKey(this, fullKey); + if (fullKey !== key) { + results.push(key); + } + } + delay(success, results); + } + catch (e) { + delay(error, e); + } + }; + + /// Identifies the underlying mechanism used by the store. + DomStore.prototype.mechanism = "dom"; + + DomStore.prototype.read = function (key, success, error) { + /// Reads the value associated to a key in the store. + /// Key string. + /// Callback for a successful reads operation. + /// Callback for handling errors. If not specified then store.defaultError is invoked. + error = error || this.defaultError; + + if (key instanceof Array) { + error({ message: "Array of keys not supported" }); + } else { + try { + var fullKey = qualifyDomStoreKey(this, key); + var value = localStorage.getItem(fullKey); + if (value !== null && value !== "undefined") { + // Hydrate using json + value = window.JSON.parse(value, domStoreJSONToDate); + } + else { + value = undefined; + } + delay(success, key, value); + } catch (e) { + delay(error, e); + } + } + }; + + DomStore.prototype.remove = function (key, success, error) { + /// Removes a key and its value from the store. + /// Key string. + /// Callback for a successful remove operation. + /// Callback for handling errors. If not specified then store.defaultError is invoked. + error = error || this.defaultError; + + if (key instanceof Array) { + error({ message: "Batches not supported" }); + } else { + try { + var fullKey = qualifyDomStoreKey(this, key); + localStorage.removeItem(fullKey); + delay(success); + } catch (e) { + delay(error, e); + } + } + }; + + DomStore.prototype.update = function (key, value, success, error) { + /// Updates the value associated to a key in the store. + /// Key string. + /// New value. + /// Callback for a successful update operation. + /// Callback for handling errors. If not specified then store.defaultError is invoked. + /// + /// This method errors out if the specified key is not found in the store. + /// + + error = error || this.defaultError; + var store = this; + this.contains(key, function (contained) { + if (contained) { + store.addOrUpdate(key, value, success, error); + } else { + delay(error, { message: "key not found", key: key }); + } + }, error); + }; + + + + var indexedDB = window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB || window.indexedDB; + var IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange; + var IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction || {}; + + var IDBT_READ_ONLY = IDBTransaction.READ_ONLY || "readonly"; + var IDBT_READ_WRITE = IDBTransaction.READ_WRITE || "readwrite"; + + var getError = function (error, defaultError) { + /// Returns either a specific error handler or the default error handler + /// The specific error handler + /// The default error handler + /// The error callback + + return function (e) { + var errorFunc = error || defaultError; + if (!errorFunc) { + return; + } + + // Old api quota exceeded error support. + if (Object.prototype.toString.call(e) === "[object IDBDatabaseException]") { + if (e.code === 11 /* IndexedDb disk quota exceeded */) { + errorFunc({ name: "QuotaExceededError", error: e }); + return; + } + errorFunc(e); + return; + } + + var errName; + try { + var errObj = e.target.error || e; + errName = errObj.name; + } catch (ex) { + errName = (e.type === "blocked") ? "IndexedDBBlocked" : "UnknownError"; + } + errorFunc({ name: errName, error: e }); + }; + }; + + var openStoreDb = function (store, success, error) { + /// Opens the store object's indexed db database. + /// The store object + /// The success callback + /// The error callback + + var storeName = store.name; + var dbName = "_datajs_" + storeName; + + var request = indexedDB.open(dbName); + request.onblocked = error; + request.onerror = error; + + request.onupgradeneeded = function () { + var db = request.result; + if (!db.objectStoreNames.contains(storeName)) { + db.createObjectStore(storeName); + } + }; + + request.onsuccess = function (event) { + var db = request.result; + if (!db.objectStoreNames.contains(storeName)) { + // Should we use the old style api to define the database schema? + if ("setVersion" in db) { + var versionRequest = db.setVersion("1.0"); + versionRequest.onsuccess = function () { + var transaction = versionRequest.transaction; + transaction.oncomplete = function () { + success(db); + }; + db.createObjectStore(storeName, null, false); + }; + versionRequest.onerror = error; + versionRequest.onblocked = error; + return; + } + + // The database doesn't have the expected store. + // Fabricate an error object for the event for the schema mismatch + // and error out. + event.target.error = { name: "DBSchemaMismatch" }; + error(event); + return; + } + + db.onversionchange = function (event) { + event.target.close(); + } + success(db); + }; + }; + + var openTransaction = function (store, mode, success, error) { + /// Opens a new transaction to the store + /// The store object + /// The read/write mode of the transaction (constants from IDBTransaction) + /// The success callback + /// The error callback + + var storeName = store.name; + var storeDb = store.db; + var errorCallback = getError(error, store.defaultError); + + if (storeDb) { + success(storeDb.transaction(storeName, mode)); + return; + } + + openStoreDb(store, function (db) { + store.db = db; + success(db.transaction(storeName, mode)); + }, errorCallback); + }; + + var IndexedDBStore = function (name) { + /// Creates a new IndexedDBStore. + /// The name of the store. + /// The new IndexedDBStore. + this.name = name; + }; + + IndexedDBStore.create = function (name) { + /// Creates a new IndexedDBStore. + /// The name of the store. + /// The new IndexedDBStore. + if (IndexedDBStore.isSupported()) { + return new IndexedDBStore(name); + } + + throw { message: "IndexedDB is not supported on this browser" }; + }; + + IndexedDBStore.isSupported = function () { + /// Returns whether IndexedDB is supported. + /// True if IndexedDB is supported, false otherwise. + return !!indexedDB; + }; + + IndexedDBStore.prototype.add = function (key, value, success, error) { + /// Adds a key/value pair to the store + /// The key + /// The value + /// The success callback + /// The error callback + var name = this.name; + var defaultError = this.defaultError; + var keys = []; + var values = []; + + if (key instanceof Array) { + keys = key; + values = value; + } else { + keys = [key]; + values = [value]; + } + + openTransaction(this, IDBT_READ_WRITE, function (transaction) { + transaction.onabort = getError(error, defaultError, key, "add"); + transaction.oncomplete = function () { + if (key instanceof Array) { + success(keys, values); + } else { + success(key, value); + } + }; + + for (var i = 0; i < keys.length && i < values.length; i++) { + transaction.objectStore(name).add({ v: values[i] }, keys[i]); + } + }, error); + }; + + IndexedDBStore.prototype.addOrUpdate = function (key, value, success, error) { + /// Adds or updates a key/value pair in the store + /// The key + /// The value + /// The success callback + /// The error callback + var name = this.name; + var defaultError = this.defaultError; + var keys = []; + var values = []; + + if (key instanceof Array) { + keys = key; + values = value; + } else { + keys = [key]; + values = [value]; + } + + openTransaction(this, IDBT_READ_WRITE, function (transaction) { + transaction.onabort = getError(error, defaultError); + transaction.oncomplete = function () { + if (key instanceof Array) { + success(keys, values); + } else { + success(key, value); + } + }; + + for (var i = 0; i < keys.length && i < values.length; i++) { + var record = { v: values[i] }; + transaction.objectStore(name).put(record, keys[i]); + } + }, error); + }; + + IndexedDBStore.prototype.clear = function (success, error) { + /// Clears the store + /// The success callback + /// The error callback + var name = this.name; + var defaultError = this.defaultError; + openTransaction(this, IDBT_READ_WRITE, function (transaction) { + transaction.onerror = getError(error, defaultError); + transaction.oncomplete = function () { + success(); + }; + + transaction.objectStore(name).clear(); + }, error); + }; + + IndexedDBStore.prototype.close = function () { + /// Closes the connection to the database + if (this.db) { + this.db.close(); + this.db = null; + } + }; + + IndexedDBStore.prototype.contains = function (key, success, error) { + /// Returns whether the store contains a key + /// The key + /// The success callback + /// The error callback + var name = this.name; + var defaultError = this.defaultError; + openTransaction(this, IDBT_READ_ONLY, function (transaction) { + var objectStore = transaction.objectStore(name); + var request = objectStore["get"](key); + + transaction.oncomplete = function () { + success(!!request.result); + }; + transaction.onerror = getError(error, defaultError); + }, error); + }; + + IndexedDBStore.prototype.defaultError = throwErrorCallback; + + IndexedDBStore.prototype.getAllKeys = function (success, error) { + /// Gets all the keys from the store + /// The success callback + /// The error callback + var name = this.name; + var defaultError = this.defaultError; + openTransaction(this, IDBT_READ_WRITE, function (transaction) { + var results = []; + + transaction.oncomplete = function () { + success(results); + }; + + var request = transaction.objectStore(name).openCursor(); + + request.onerror = getError(error, defaultError); + request.onsuccess = function (event) { + var cursor = event.target.result; + if (cursor) { + results.push(cursor.key); + // Some tools have issues because continue is a javascript reserved word. + cursor["continue"].call(cursor); + } + }; + }, error); + }; + + /// Identifies the underlying mechanism used by the store. + IndexedDBStore.prototype.mechanism = "indexeddb"; + + IndexedDBStore.prototype.read = function (key, success, error) { + /// Reads the value for the specified key + /// The key + /// The success callback + /// The error callback + /// If the key does not exist, the success handler will be called with value = undefined + var name = this.name; + var defaultError = this.defaultError; + var keys = (key instanceof Array) ? key : [key]; + + openTransaction(this, IDBT_READ_ONLY, function (transaction) { + var values = []; + + transaction.onerror = getError(error, defaultError, key, "read"); + transaction.oncomplete = function () { + if (key instanceof Array) { + success(keys, values); + } else { + success(keys[0], values[0]); + } + }; + + for (var i = 0; i < keys.length; i++) { + // Some tools have issues because get is a javascript reserved word. + var objectStore = transaction.objectStore(name); + var request = objectStore["get"].call(objectStore, keys[i]); + request.onsuccess = function (event) { + var record = event.target.result; + values.push(record ? record.v : undefined); + }; + } + }, error); + }; + + IndexedDBStore.prototype.remove = function (key, success, error) { + /// Removes the specified key from the store + /// The key + /// The success callback + /// The error callback + var name = this.name; + var defaultError = this.defaultError; + var keys = (key instanceof Array) ? key : [key]; + + openTransaction(this, IDBT_READ_WRITE, function (transaction) { + transaction.onerror = getError(error, defaultError); + transaction.oncomplete = function () { + success(); + }; + + for (var i = 0; i < keys.length; i++) { + // Some tools have issues because continue is a javascript reserved word. + var objectStore = transaction.objectStore(name); + objectStore["delete"].call(objectStore, keys[i]); + } + }, error); + }; + + IndexedDBStore.prototype.update = function (key, value, success, error) { + /// Updates a key/value pair in the store + /// The key + /// The value + /// The success callback + /// The error callback + var name = this.name; + var defaultError = this.defaultError; + var keys = []; + var values = []; + + if (key instanceof Array) { + keys = key; + values = value; + } else { + keys = [key]; + values = [value]; + } + + openTransaction(this, IDBT_READ_WRITE, function (transaction) { + transaction.onabort = getError(error, defaultError); + transaction.oncomplete = function () { + if (key instanceof Array) { + success(keys, values); + } else { + success(key, value); + } + }; + + for (var i = 0; i < keys.length && i < values.length; i++) { + var request = transaction.objectStore(name).openCursor(IDBKeyRange.only(keys[i])); + var record = { v: values[i] }; + request.pair = { key: keys[i], value: record }; + request.onsuccess = function (event) { + var cursor = event.target.result; + if (cursor) { + cursor.update(event.target.pair.value); + } else { + transaction.abort(); + } + }; + } + }, error); + }; + + + + var MemoryStore = function (name) { + /// Constructor for store objects that use a sorted array as the underlying mechanism. + /// Store name. + + var holes = []; + var items = []; + var keys = {}; + + this.name = name; + + var getErrorCallback = function (error) { + return error || this.defaultError; + }; + + var validateKeyInput = function (key, error) { + /// Validates that the specified key is not undefined, not null, and not an array + /// Key value. + /// Error callback. + /// True if the key is valid. False if the key is invalid and the error callback has been queued for execution. + + var messageString; + + if (key instanceof Array) { + messageString = "Array of keys not supported"; + } + + if (key === undefined || key === null) { + messageString = "Invalid key"; + } + + if (messageString) { + delay(error, { message: messageString }); + return false; + } + return true; + }; + + this.add = function (key, value, success, error) { + /// Adds a new value identified by a key to the store. + /// Key string. + /// Value that is going to be added to the store. + /// Callback for a successful add operation. + /// Callback for handling errors. If not specified then store.defaultError is invoked. + /// + /// This method errors out if the store already contains the specified key. + /// + + error = getErrorCallback(error); + + if (validateKeyInput(key, error)) { + if (!keys.hasOwnProperty(key)) { + this.addOrUpdate(key, value, success, error); + } else { + error({ message: "key already exists", key: key }); + } + } + }; + + this.addOrUpdate = function (key, value, success, error) { + /// Adds or updates a value identified by a key to the store. + /// Key string. + /// Value that is going to be added or updated to the store. + /// Callback for a successful add or update operation. + /// Callback for handling errors. If not specified then store.defaultError is invoked. + /// + /// This method will overwrite the key's current value if it already exists in the store; otherwise it simply adds the new key and value. + /// + + error = getErrorCallback(error); + + if (validateKeyInput(key, error)) { + var index = keys[key]; + if (index === undefined) { + if (holes.length > 0) { + index = holes.splice(0, 1); + } else { + index = items.length; + } + } + items[index] = value; + keys[key] = index; + delay(success, key, value); + } + }; + + this.clear = function (success) { + /// Removes all the data associated with this store object. + /// Callback for a successful clear operation. + + items = []; + keys = {}; + holes = []; + + delay(success); + }; + + this.contains = function (key, success) { + /// Checks whether a key exists in the store. + /// Key string. + /// Callback indicating whether the store contains the key or not. + + var contained = keys.hasOwnProperty(key); + delay(success, contained); + }; + + this.getAllKeys = function (success) { + /// Gets all the keys that exist in the store. + /// Callback for a successful get operation. + + var results = []; + for (var name in keys) { + results.push(name); + } + delay(success, results); + }; + + this.read = function (key, success, error) { + /// Reads the value associated to a key in the store. + /// Key string. + /// Callback for a successful reads operation. + /// Callback for handling errors. If not specified then store.defaultError is invoked. + error = getErrorCallback(error); + + if (validateKeyInput(key, error)) { + var index = keys[key]; + delay(success, key, items[index]); + } + }; + + this.remove = function (key, success, error) { + /// Removes a key and its value from the store. + /// Key string. + /// Callback for a successful remove operation. + /// Callback for handling errors. If not specified then store.defaultError is invoked. + error = getErrorCallback(error); + + if (validateKeyInput(key, error)) { + var index = keys[key]; + if (index !== undefined) { + if (index === items.length - 1) { + items.pop(); + } else { + items[index] = undefined; + holes.push(index); + } + delete keys[key]; + + // The last item was removed, no need to keep track of any holes in the array. + if (items.length === 0) { + holes = []; + } + } + + delay(success); + } + }; + + this.update = function (key, value, success, error) { + /// Updates the value associated to a key in the store. + /// Key string. + /// New value. + /// Callback for a successful update operation. + /// Callback for handling errors. If not specified then store.defaultError is invoked. + /// + /// This method errors out if the specified key is not found in the store. + /// + + error = getErrorCallback(error); + if (validateKeyInput(key, error)) { + if (keys.hasOwnProperty(key)) { + this.addOrUpdate(key, value, success, error); + } else { + error({ message: "key not found", key: key }); + } + } + }; + }; + + MemoryStore.create = function (name) { + /// Creates a store object that uses memory storage as its underlying mechanism. + /// Store name. + /// Store object. + return new MemoryStore(name); + }; + + MemoryStore.isSupported = function () { + /// Checks whether the underlying mechanism for this kind of store objects is supported by the browser. + /// True if the mechanism is supported by the browser; otherwise false. + return true; + }; + + MemoryStore.prototype.close = function () { + /// This function does nothing in MemoryStore as it does not have a connection model. + }; + + MemoryStore.prototype.defaultError = throwErrorCallback; + + /// Identifies the underlying mechanism used by the store. + MemoryStore.prototype.mechanism = "memory"; + + + + var mechanisms = { + indexeddb: IndexedDBStore, + dom: DomStore, + memory: MemoryStore + }; + + datajs.defaultStoreMechanism = "best"; + + datajs.createStore = function (name, mechanism) { + /// Creates a new store object. + /// Store name. + /// A specific mechanism to use (defaults to best, can be "best", "dom", "indexeddb", "webdb"). + /// Store object. + + if (!mechanism) { + mechanism = datajs.defaultStoreMechanism; + } + + if (mechanism === "best") { + mechanism = (DomStore.isSupported()) ? "dom" : "memory"; + } + + var factory = mechanisms[mechanism]; + if (factory) { + return factory.create(name); + } + + throw { message: "Failed to create store", name: name, mechanism: mechanism }; + }; + + + + + var appendQueryOption = function (uri, queryOption) { + /// Appends the specified escaped query option to the specified URI. + /// URI to append option to. + /// Escaped query option to append. + var separator = (uri.indexOf("?") >= 0) ? "&" : "?"; + return uri + separator + queryOption; + }; + + var appendSegment = function (uri, segment) { + /// Appends the specified segment to the given URI. + /// URI to append a segment to. + /// Segment to append. + /// The original URI with a new segment appended. + + var index = uri.indexOf("?"); + var queryPortion = ""; + if (index >= 0) { + queryPortion = uri.substr(index); + uri = uri.substr(0, index); + } + + if (uri[uri.length - 1] !== "/") { + uri += "/"; + } + return uri + segment + queryPortion; + }; + + var buildODataRequest = function (uri, options) { + /// Builds a request object to GET the specified URI. + /// URI for request. + /// Additional options. + + return { + method: "GET", + requestUri: uri, + user: options.user, + password: options.password, + enableJsonpCallback: options.enableJsonpCallback, + callbackParameterName: options.callbackParameterName, + formatQueryString: options.formatQueryString + }; + }; + + var findQueryOptionStart = function (uri, name) { + /// Finds the index where the value of a query option starts. + /// URI to search in. + /// Name to look for. + /// The index where the query option starts. + + var result = -1; + var queryIndex = uri.indexOf("?"); + if (queryIndex !== -1) { + var start = uri.indexOf("?" + name + "=", queryIndex); + if (start === -1) { + start = uri.indexOf("&" + name + "=", queryIndex); + } + if (start !== -1) { + result = start + name.length + 2; + } + } + return result; + }; + + var queryForData = function (uri, options, success, error) { + /// Gets data from an OData service. + /// URI to the OData service. + /// Object with additional well-known request options. + /// Success callback. + /// Error callback. + /// Object with an abort method. + + var request = queryForDataInternal(uri, options, [], success, error); + return request; + }; + + var queryForDataInternal = function (uri, options, data, success, error) { + /// Gets data from an OData service taking into consideration server side paging. + /// URI to the OData service. + /// Object with additional well-known request options. + /// Array that stores the data provided by the OData service. + /// Success callback. + /// Error callback. + /// Object with an abort method. + + var request = buildODataRequest(uri, options); + var currentRequest = odata.request(request, function (newData) { + var next = newData.__next; + var results = newData.results; + + data = data.concat(results); + + if (next) { + currentRequest = queryForDataInternal(next, options, data, success, error); + } else { + success(data); + } + }, error, undefined, options.httpClient, options.metadata); + + return { + abort: function () { + currentRequest.abort(); + } + }; + }; + + var ODataCacheSource = function (options) { + /// Creates a data cache source object for requesting data from an OData service. + /// Options for the cache data source. + /// A new data cache source instance. + + var that = this; + var uri = options.source; + + that.identifier = normalizeURICase(encodeURI(decodeURI(uri))); + that.options = options; + + that.count = function (success, error) { + /// Gets the number of items in the collection. + /// Success callback with the item count. + /// Error callback. + /// Request object with an abort method./ + + var options = that.options; + return odata.request( + buildODataRequest(appendSegment(uri, "$count"), options), + function (data) { + var count = parseInt10(data.toString()); + if (isNaN(count)) { + error({ message: "Count is NaN", count: count }); + } else { + success(count); + } + }, error, undefined, options.httpClient, options.metadata); + }; + + that.read = function (index, count, success, error) { + /// Gets a number of consecutive items from the collection. + /// Zero-based index of the items to retrieve. + /// Number of items to retrieve. + /// Success callback with the requested items. + /// Error callback. + /// Request object with an abort method./ + + var queryOptions = "$skip=" + index + "&$top=" + count; + return queryForData(appendQueryOption(uri, queryOptions), that.options, success, error); + }; + + return that; + }; + + + + var appendPage = function (operation, page) { + /// Appends a page's data to the operation data. + /// Operation with (i)ndex, (c)ount and (d)ata. + /// Page with (i)ndex, (c)ount and (d)ata. + + var intersection = intersectRanges(operation, page); + if (intersection) { + var start = intersection.i - page.i; + var end = start + (operation.c - operation.d.length); + operation.d = operation.d.concat(page.d.slice(start, end)); + } + }; + + var intersectRanges = function (x, y) { + /// Returns the {(i)ndex, (c)ount} range for the intersection of x and y. + /// Range with (i)ndex and (c)ount members. + /// Range with (i)ndex and (c)ount members. + /// The intersection (i)ndex and (c)ount; undefined if there is no intersection. + + var xLast = x.i + x.c; + var yLast = y.i + y.c; + var resultIndex = (x.i > y.i) ? x.i : y.i; + var resultLast = (xLast < yLast) ? xLast : yLast; + var result; + if (resultLast >= resultIndex) { + result = { i: resultIndex, c: resultLast - resultIndex }; + } + + return result; + }; + + var checkZeroGreater = function (val, name) { + /// Checks whether val is a defined number with value zero or greater. + /// Value to check. + /// Parameter name to use in exception. + + if (val === undefined || typeof val !== "number") { + throw { message: "'" + name + "' must be a number." }; + } + + if (isNaN(val) || val < 0 || !isFinite(val)) { + throw { message: "'" + name + "' must be greater than or equal to zero." }; + } + }; + + var checkUndefinedGreaterThanZero = function (val, name) { + /// Checks whether val is undefined or a number with value greater than zero. + /// Value to check. + /// Parameter name to use in exception. + + if (val !== undefined) { + if (typeof val !== "number") { + throw { message: "'" + name + "' must be a number." }; + } + + if (isNaN(val) || val <= 0 || !isFinite(val)) { + throw { message: "'" + name + "' must be greater than zero." }; + } + } + }; + + var checkUndefinedOrNumber = function (val, name) { + /// Checks whether val is undefined or a number + /// Value to check. + /// Parameter name to use in exception. + if (val !== undefined && (typeof val !== "number" || isNaN(val) || !isFinite(val))) { + throw { message: "'" + name + "' must be a number." }; + } + }; + + var removeFromArray = function (arr, item) { + /// Performs a linear search on the specified array and removes the first instance of 'item'. + /// Array to search. + /// Item being sought. + /// Whether the item was removed. + + var i, len; + for (i = 0, len = arr.length; i < len; i++) { + if (arr[i] === item) { + arr.splice(i, 1); + return true; + } + } + + return false; + }; + + var estimateSize = function (obj) { + /// Estimates the size of an object in bytes. + /// Object to determine the size of. + /// Estimated size of the object in bytes. + var size = 0; + var type = typeof obj; + + if (type === "object" && obj) { + for (var name in obj) { + size += name.length * 2 + estimateSize(obj[name]); + } + } else if (type === "string") { + size = obj.length * 2; + } else { + size = 8; + } + return size; + }; + + var snapToPageBoundaries = function (lowIndex, highIndex, pageSize) { + /// Snaps low and high indices into page sizes and returns a range. + /// Low index to snap to a lower value. + /// High index to snap to a higher value. + /// Page size to snap to. + /// A range with (i)ndex and (c)ount of elements. + + lowIndex = Math.floor(lowIndex / pageSize) * pageSize; + highIndex = Math.ceil((highIndex + 1) / pageSize) * pageSize; + return { i: lowIndex, c: highIndex - lowIndex }; + }; + + // The DataCache is implemented using state machines. The following constants are used to properly + // identify and label the states that these machines transition to. + + // DataCache state constants + + var CACHE_STATE_DESTROY = "destroy"; + var CACHE_STATE_IDLE = "idle"; + var CACHE_STATE_INIT = "init"; + var CACHE_STATE_READ = "read"; + var CACHE_STATE_PREFETCH = "prefetch"; + var CACHE_STATE_WRITE = "write"; + + // DataCacheOperation state machine states. + // Transitions on operations also depend on the cache current of the cache. + + var OPERATION_STATE_CANCEL = "cancel"; + var OPERATION_STATE_END = "end"; + var OPERATION_STATE_ERROR = "error"; + var OPERATION_STATE_START = "start"; + var OPERATION_STATE_WAIT = "wait"; + + // Destroy state machine states + + var DESTROY_STATE_CLEAR = "clear"; + + // Read / Prefetch state machine states + + var READ_STATE_DONE = "done"; + var READ_STATE_LOCAL = "local"; + var READ_STATE_SAVE = "save"; + var READ_STATE_SOURCE = "source"; + + var DataCacheOperation = function (stateMachine, promise, isCancelable, index, count, data, pending) { + /// Creates a new operation object. + /// State machine that describes the specific behavior of the operation. + /// Promise for requested values. + /// Whether this operation can be canceled or not. + /// Index of first item requested. + /// Count of items requested. + /// Array with the items requested by the operation. + /// Total number of pending prefetch records. + /// A new data cache operation instance. + + /// Promise for requested values. + /// Index of first item requested. + /// Count of items requested. + /// Array with the items requested by the operation. + /// Current state of the operation. + /// Whether the operation has been canceled. + /// Total number of pending prefetch records. + /// Callback executed when the operation reaches the end state. + + var stateData; + var cacheState; + var that = this; + + that.p = promise; + that.i = index; + that.c = count; + that.d = data; + that.s = OPERATION_STATE_START; + + that.canceled = false; + that.pending = pending; + that.oncomplete = null; + + that.cancel = function () { + /// Transitions this operation to the cancel state and sets the canceled flag to true. + /// The function is a no-op if the operation is non-cancelable. + + if (!isCancelable) { + return; + } + + var state = that.s; + if (state !== OPERATION_STATE_ERROR && state !== OPERATION_STATE_END && state !== OPERATION_STATE_CANCEL) { + that.canceled = true; + transition(OPERATION_STATE_CANCEL, stateData); + } + }; + + that.complete = function () { + /// Transitions this operation to the end state. + + transition(OPERATION_STATE_END, stateData); + }; + + that.error = function (err) { + /// Transitions this operation to the error state. + if (!that.canceled) { + transition(OPERATION_STATE_ERROR, err); + } + }; + + that.run = function (state) { + /// Executes the operation's current state in the context of a new cache state. + /// New cache state. + + cacheState = state; + that.transition(that.s, stateData); + }; + + that.wait = function (data) { + /// Transitions this operation to the wait state. + + transition(OPERATION_STATE_WAIT, data); + }; + + var operationStateMachine = function (opTargetState, cacheState, data) { + /// State machine that describes all operations common behavior. + /// Operation state to transition to. + /// Current cache state. + /// Additional data passed to the state. + + switch (opTargetState) { + case OPERATION_STATE_START: + // Initial state of the operation. The operation will remain in this state until the cache has been fully initialized. + if (cacheState !== CACHE_STATE_INIT) { + stateMachine(that, opTargetState, cacheState, data); + } + break; + + case OPERATION_STATE_WAIT: + // Wait state indicating that the operation is active but waiting for an asynchronous operation to complete. + stateMachine(that, opTargetState, cacheState, data); + break; + + case OPERATION_STATE_CANCEL: + // Cancel state. + stateMachine(that, opTargetState, cacheState, data); + that.fireCanceled(); + transition(OPERATION_STATE_END); + break; + + case OPERATION_STATE_ERROR: + // Error state. Data is expected to be an object detailing the error condition. + stateMachine(that, opTargetState, cacheState, data); + that.canceled = true; + that.fireRejected(data); + transition(OPERATION_STATE_END); + break; + + case OPERATION_STATE_END: + // Final state of the operation. + if (that.oncomplete) { + that.oncomplete(that); + } + if (!that.canceled) { + that.fireResolved(); + } + stateMachine(that, opTargetState, cacheState, data); + break; + + default: + // Any other state is passed down to the state machine describing the operation's specific behavior. + stateMachine(that, opTargetState, cacheState, data); + break; + } + }; + + var transition = function (state, data) { + /// Transitions this operation to a new state. + /// State to transition the operation to. + /// Additional data passed to the state. + + that.s = state; + stateData = data; + operationStateMachine(state, cacheState, data); + }; + + that.transition = transition; + + return that; + }; + + DataCacheOperation.prototype.fireResolved = function () { + /// Fires a resolved notification as necessary. + + // Fire the resolve just once. + var p = this.p; + if (p) { + this.p = null; + p.resolve(this.d); + } + }; + + DataCacheOperation.prototype.fireRejected = function (reason) { + /// Fires a rejected notification as necessary. + + // Fire the rejection just once. + var p = this.p; + if (p) { + this.p = null; + p.reject(reason); + } + }; + + DataCacheOperation.prototype.fireCanceled = function () { + /// Fires a canceled notification as necessary. + + this.fireRejected({ canceled: true, message: "Operation canceled" }); + }; + + + var DataCache = function (options) { + /// Creates a data cache for a collection that is efficiently loaded on-demand. + /// + /// Options for the data cache, including name, source, pageSize, + /// prefetchSize, cacheSize, storage mechanism, and initial prefetch and local-data handler. + /// + /// A new data cache instance. + + var state = CACHE_STATE_INIT; + var stats = { counts: 0, netReads: 0, prefetches: 0, cacheReads: 0 }; + + var clearOperations = []; + var readOperations = []; + var prefetchOperations = []; + + var actualCacheSize = 0; // Actual cache size in bytes. + var allDataLocal = false; // Whether all data is local. + var cacheSize = undefinedDefault(options.cacheSize, 1048576); // Requested cache size in bytes, default 1 MB. + var collectionCount = 0; // Number of elements in the server collection. + var highestSavedPage = 0; // Highest index of all the saved pages. + var highestSavedPageSize = 0; // Item count of the saved page with the highest index. + var overflowed = cacheSize === 0; // If the cache has overflowed (actualCacheSize > cacheSize or cacheSize == 0); + var pageSize = undefinedDefault(options.pageSize, 50); // Number of elements to store per page. + var prefetchSize = undefinedDefault(options.prefetchSize, pageSize); // Number of elements to prefetch from the source when the cache is idling. + var version = "1.0"; + var cacheFailure; + + var pendingOperations = 0; + + var source = options.source; + if (typeof source === "string") { + // Create a new cache source. + source = new ODataCacheSource(options); + } + source.options = options; + + // Create a cache local store. + var store = datajs.createStore(options.name, options.mechanism); + + var that = this; + + that.onidle = options.idle; + that.stats = stats; + + that.count = function () { + /// Counts the number of items in the collection. + /// A promise with the number of items. + + if (cacheFailure) { + throw cacheFailure; + } + + var deferred = createDeferred(); + var canceled = false; + + if (allDataLocal) { + delay(function () { + deferred.resolve(collectionCount); + }); + + return deferred.promise(); + } + + // TODO: Consider returning the local data count instead once allDataLocal flag is set to true. + var request = source.count(function (count) { + request = null; + stats.counts++; + deferred.resolve(count); + }, function (err) { + request = null; + deferred.reject(extend(err, { canceled: canceled })); + }); + + return extend(deferred.promise(), { + cancel: function () { + /// Aborts the count operation. + if (request) { + canceled = true; + request.abort(); + request = null; + } + } + }); + }; + + that.clear = function () { + /// Cancels all running operations and clears all local data associated with this cache. + /// + /// New read requests made while a clear operation is in progress will not be canceled. + /// Instead they will be queued for execution once the operation is completed. + /// + /// A promise that has no value and can't be canceled. + + if (cacheFailure) { + throw cacheFailure; + } + + if (clearOperations.length === 0) { + var deferred = createDeferred(); + var op = new DataCacheOperation(destroyStateMachine, deferred, false); + queueAndStart(op, clearOperations); + return deferred.promise(); + } + return clearOperations[0].p; + }; + + that.filterForward = function (index, count, predicate) { + /// Filters the cache data based a predicate. + /// The index of the item to start filtering forward from. + /// Maximum number of items to include in the result. + /// Callback function returning a boolean that determines whether an item should be included in the result or not. + /// + /// Specifying a negative count value will yield all the items in the cache that satisfy the predicate. + /// + /// A promise for an array of results. + return filter(index, count, predicate, false); + }; + + that.filterBack = function (index, count, predicate) { + /// Filters the cache data based a predicate. + /// The index of the item to start filtering backward from. + /// Maximum number of items to include in the result. + /// Callback function returning a boolean that determines whether an item should be included in the result or not. + /// + /// Specifying a negative count value will yield all the items in the cache that satisfy the predicate. + /// + /// A promise for an array of results. + return filter(index, count, predicate, true); + }; + + that.readRange = function (index, count) { + /// Reads a range of adjacent records. + /// Zero-based index of record range to read. + /// Number of records in the range. + /// + /// New read requests made while a clear operation is in progress will not be canceled. + /// Instead they will be queued for execution once the operation is completed. + /// + /// + /// A promise for an array of records; less records may be returned if the + /// end of the collection is found. + /// + + checkZeroGreater(index, "index"); + checkZeroGreater(count, "count"); + + if (cacheFailure) { + throw cacheFailure; + } + + var deferred = createDeferred(); + + // Merging read operations would be a nice optimization here. + var op = new DataCacheOperation(readStateMachine, deferred, true, index, count, [], 0); + queueAndStart(op, readOperations); + + return extend(deferred.promise(), { + cancel: function () { + /// Aborts the readRange operation. + op.cancel(); + } + }); + }; + + that.ToObservable = that.toObservable = function () { + /// Creates an Observable object that enumerates all the cache contents. + /// A new Observable object that enumerates all the cache contents. + if (!window.Rx || !window.Rx.Observable) { + throw { message: "Rx library not available - include rx.js" }; + } + + if (cacheFailure) { + throw cacheFailure; + } + + return window.Rx.Observable.CreateWithDisposable(function (obs) { + var disposed = false; + var index = 0; + + var errorCallback = function (error) { + if (!disposed) { + obs.OnError(error); + } + }; + + var successCallback = function (data) { + if (!disposed) { + var i, len; + for (i = 0, len = data.length; i < len; i++) { + // The wrapper automatically checks for Dispose + // on the observer, so we don't need to check it here. + obs.OnNext(data[i]); + } + + if (data.length < pageSize) { + obs.OnCompleted(); + } else { + index += pageSize; + that.readRange(index, pageSize).then(successCallback, errorCallback); + } + } + }; + + that.readRange(index, pageSize).then(successCallback, errorCallback); + + return { Dispose: function () { disposed = true; } }; + }); + }; + + var cacheFailureCallback = function (message) { + /// Creates a function that handles a callback by setting the cache into failure mode. + /// Message text. + /// Function to use as error callback. + /// + /// This function will specifically handle problems with critical store resources + /// during cache initialization. + /// + + return function (error) { + cacheFailure = { message: message, error: error }; + + // Destroy any pending clear or read operations. + // At this point there should be no prefetch operations. + // Count operations will go through but are benign because they + // won't interact with the store. + var i, len; + for (i = 0, len = readOperations.length; i < len; i++) { + readOperations[i].fireRejected(cacheFailure); + } + for (i = 0, len = clearOperations.length; i < len; i++) { + clearOperations[i].fireRejected(cacheFailure); + } + + // Null out the operation arrays. + readOperations = clearOperations = null; + }; + }; + + var changeState = function (newState) { + /// Updates the cache's state and signals all pending operations of the change. + /// New cache state. + /// This method is a no-op if the cache's current state and the new state are the same. + + if (newState !== state) { + state = newState; + var operations = clearOperations.concat(readOperations, prefetchOperations); + var i, len; + for (i = 0, len = operations.length; i < len; i++) { + operations[i].run(state); + } + } + }; + + var clearStore = function () { + /// Removes all the data stored in the cache. + /// A promise with no value. + + var deferred = new DjsDeferred(); + store.clear(function () { + + // Reset the cache settings. + actualCacheSize = 0; + allDataLocal = false; + collectionCount = 0; + highestSavedPage = 0; + highestSavedPageSize = 0; + overflowed = cacheSize === 0; + + // version is not reset, in case there is other state in eg V1.1 that is still around. + + // Reset the cache stats. + stats = { counts: 0, netReads: 0, prefetches: 0, cacheReads: 0 }; + that.stats = stats; + + store.close(); + deferred.resolve(); + }, function (err) { + deferred.reject(err); + }); + return deferred; + }; + + var dequeueOperation = function (operation) { + /// Removes an operation from the caches queues and changes the cache state to idle. + /// Operation to dequeue. + /// This method is used as a handler for the operation's oncomplete event. + + var removed = removeFromArray(clearOperations, operation); + if (!removed) { + removed = removeFromArray(readOperations, operation); + if (!removed) { + removeFromArray(prefetchOperations, operation); + } + } + + pendingOperations--; + changeState(CACHE_STATE_IDLE); + }; + + var fetchPage = function (start) { + /// Requests data from the cache source. + /// Zero-based index of items to request. + /// A promise for a page object with (i)ndex, (c)ount, (d)ata. + + + var deferred = new DjsDeferred(); + var canceled = false; + + var request = source.read(start, pageSize, function (data) { + var page = { i: start, c: data.length, d: data }; + deferred.resolve(page); + }, function (err) { + deferred.reject(err); + }); + + return extend(deferred, { + cancel: function () { + if (request) { + request.abort(); + canceled = true; + request = null; + } + } + }); + }; + + var filter = function (index, count, predicate, backwards) { + /// Filters the cache data based a predicate. + /// The index of the item to start filtering from. + /// Maximum number of items to include in the result. + /// Callback function returning a boolean that determines whether an item should be included in the result or not. + /// True if the filtering should move backward from the specified index, falsey otherwise. + /// + /// Specifying a negative count value will yield all the items in the cache that satisfy the predicate. + /// + /// A promise for an array of results. + index = parseInt10(index); + count = parseInt10(count); + + if (isNaN(index)) { + throw { message: "'index' must be a valid number.", index: index }; + } + if (isNaN(count)) { + throw { message: "'count' must be a valid number.", count: count }; + } + + if (cacheFailure) { + throw cacheFailure; + } + + index = Math.max(index, 0); + + var deferred = createDeferred(); + var arr = []; + var canceled = false; + var pendingReadRange = null; + + var readMore = function (readIndex, readCount) { + if (!canceled) { + if (count >= 0 && arr.length >= count) { + deferred.resolve(arr); + } else { + pendingReadRange = that.readRange(readIndex, readCount).then(function (data) { + for (var i = 0, length = data.length; i < length && (count < 0 || arr.length < count); i++) { + var dataIndex = backwards ? length - i - 1 : i; + var item = data[dataIndex]; + if (predicate(item)) { + var element = { + index: readIndex + dataIndex, + item: item + }; + + backwards ? arr.unshift(element) : arr.push(element); + } + } + + // Have we reached the end of the collection? + if ((!backwards && data.length < readCount) || (backwards && readIndex <= 0)) { + deferred.resolve(arr); + } else { + var nextIndex = backwards ? Math.max(readIndex - pageSize, 0) : readIndex + readCount; + readMore(nextIndex, pageSize); + } + }, function (err) { + deferred.reject(err); + }); + } + } + }; + + // Initially, we read from the given starting index to the next/previous page boundary + var initialPage = snapToPageBoundaries(index, index, pageSize); + var initialIndex = backwards ? initialPage.i : index; + var initialCount = backwards ? index - initialPage.i + 1 : initialPage.i + initialPage.c - index; + readMore(initialIndex, initialCount); + + return extend(deferred.promise(), { + cancel: function () { + /// Aborts the filter operation + if (pendingReadRange) { + pendingReadRange.cancel(); + } + canceled = true; + } + }); + }; + + var fireOnIdle = function () { + /// Fires an onidle event if any functions are assigned. + + if (that.onidle && pendingOperations === 0) { + that.onidle(); + } + }; + + var prefetch = function (start) { + /// Creates and starts a new prefetch operation. + /// Zero-based index of the items to prefetch. + /// + /// This method is a no-op if any of the following conditions is true: + /// 1.- prefetchSize is 0 + /// 2.- All data has been read and stored locally in the cache. + /// 3.- There is already an all data prefetch operation queued. + /// 4.- The cache has run out of available space (overflowed). + /// + + if (allDataLocal || prefetchSize === 0 || overflowed) { + return; + } + + + if (prefetchOperations.length === 0 || (prefetchOperations[0] && prefetchOperations[0].c !== -1)) { + // Merging prefetch operations would be a nice optimization here. + var op = new DataCacheOperation(prefetchStateMachine, null, true, start, prefetchSize, null, prefetchSize); + queueAndStart(op, prefetchOperations); + } + }; + + var queueAndStart = function (op, queue) { + /// Queues an operation and runs it. + /// Operation to queue. + /// Array that will store the operation. + + op.oncomplete = dequeueOperation; + queue.push(op); + pendingOperations++; + op.run(state); + }; + + var readPage = function (key) { + /// Requests a page from the cache local store. + /// Zero-based index of the reuqested page. + /// A promise for a found flag and page object with (i)ndex, (c)ount, (d)ata, and (t)icks. + + + var canceled = false; + var deferred = extend(new DjsDeferred(), { + cancel: function () { + /// Aborts the readPage operation. + canceled = true; + } + }); + + var error = storeFailureCallback(deferred, "Read page from store failure"); + + store.contains(key, function (contained) { + if (canceled) { + return; + } + if (contained) { + store.read(key, function (_, data) { + if (!canceled) { + deferred.resolve(data !== undefined, data); + } + }, error); + return; + } + deferred.resolve(false); + }, error); + return deferred; + }; + + var savePage = function (key, page) { + /// Saves a page to the cache local store. + /// Zero-based index of the requested page. + /// Object with (i)ndex, (c)ount, (d)ata, and (t)icks. + /// A promise with no value. + + + var canceled = false; + + var deferred = extend(new DjsDeferred(), { + cancel: function () { + /// Aborts the readPage operation. + canceled = true; + } + }); + + var error = storeFailureCallback(deferred, "Save page to store failure"); + + var resolve = function () { + deferred.resolve(true); + }; + + if (page.c > 0) { + var pageBytes = estimateSize(page); + overflowed = cacheSize >= 0 && cacheSize < actualCacheSize + pageBytes; + + if (!overflowed) { + store.addOrUpdate(key, page, function () { + updateSettings(page, pageBytes); + saveSettings(resolve, error); + }, error); + } else { + resolve(); + } + } else { + updateSettings(page, 0); + saveSettings(resolve, error); + } + return deferred; + }; + + var saveSettings = function (success, error) { + /// Saves the cache's current settings to the local store. + /// Success callback. + /// Errror callback. + + var settings = { + actualCacheSize: actualCacheSize, + allDataLocal: allDataLocal, + cacheSize: cacheSize, + collectionCount: collectionCount, + highestSavedPage: highestSavedPage, + highestSavedPageSize: highestSavedPageSize, + pageSize: pageSize, + sourceId: source.identifier, + version: version + }; + + store.addOrUpdate("__settings", settings, success, error); + }; + + var storeFailureCallback = function (deferred/*, message*/) { + /// Creates a function that handles a store error. + /// Deferred object to resolve. + /// Message text. + /// Function to use as error callback. + /// + /// This function will specifically handle problems when interacting with the store. + /// + + return function (/*error*/) { + // var console = window.console; + // if (console && console.log) { + // console.log(message); + // console.dir(error); + // } + deferred.resolve(false); + }; + }; + + var updateSettings = function (page, pageBytes) { + /// Updates the cache's settings based on a page object. + /// Object with (i)ndex, (c)ount, (d)ata. + /// Size of the page in bytes. + + var pageCount = page.c; + var pageIndex = page.i; + + // Detect the collection size. + if (pageCount === 0) { + if (highestSavedPage === pageIndex - pageSize) { + collectionCount = highestSavedPage + highestSavedPageSize; + } + } else { + highestSavedPage = Math.max(highestSavedPage, pageIndex); + if (highestSavedPage === pageIndex) { + highestSavedPageSize = pageCount; + } + actualCacheSize += pageBytes; + if (pageCount < pageSize && !collectionCount) { + collectionCount = pageIndex + pageCount; + } + } + + // Detect the end of the collection. + if (!allDataLocal && collectionCount === highestSavedPage + highestSavedPageSize) { + allDataLocal = true; + } + }; + + var cancelStateMachine = function (operation, opTargetState, cacheState, data) { + /// State machine describing the behavior for cancelling a read or prefetch operation. + /// Operation being run. + /// Operation state to transition to. + /// Current cache state. + /// Additional data passed to the state. + /// + /// This state machine contains behavior common to read and prefetch operations. + /// + + var canceled = operation.canceled && opTargetState !== OPERATION_STATE_END; + if (canceled) { + if (opTargetState === OPERATION_STATE_CANCEL) { + // Cancel state. + // Data is expected to be any pending request made to the cache. + if (data && data.cancel) { + data.cancel(); + } + } + } + return canceled; + }; + + var destroyStateMachine = function (operation, opTargetState, cacheState) { + /// State machine describing the behavior of a clear operation. + /// Operation being run. + /// Operation state to transition to. + /// Current cache state. + /// + /// Clear operations have the highest priority and can't be interrupted by other operations; however, + /// they will preempt any other operation currently executing. + /// + + var transition = operation.transition; + + // Signal the cache that a clear operation is running. + if (cacheState !== CACHE_STATE_DESTROY) { + changeState(CACHE_STATE_DESTROY); + return true; + } + + switch (opTargetState) { + case OPERATION_STATE_START: + // Initial state of the operation. + transition(DESTROY_STATE_CLEAR); + break; + + case OPERATION_STATE_END: + // State that signals the operation is done. + fireOnIdle(); + break; + + case DESTROY_STATE_CLEAR: + // State that clears all the local data of the cache. + clearStore().then(function () { + // Terminate the operation once the local store has been cleared. + operation.complete(); + }); + // Wait until the clear request completes. + operation.wait(); + break; + + default: + return false; + } + return true; + }; + + var prefetchStateMachine = function (operation, opTargetState, cacheState, data) { + /// State machine describing the behavior of a prefetch operation. + /// Operation being run. + /// Operation state to transition to. + /// Current cache state. + /// Additional data passed to the state. + /// + /// Prefetch operations have the lowest priority and will be interrupted by operations of + /// other kinds. A preempted prefetch operation will resume its execution only when the state + /// of the cache returns to idle. + /// + /// If a clear operation starts executing then all the prefetch operations are canceled, + /// even if they haven't started executing yet. + /// + + // Handle cancelation + if (!cancelStateMachine(operation, opTargetState, cacheState, data)) { + + var transition = operation.transition; + + // Handle preemption + if (cacheState !== CACHE_STATE_PREFETCH) { + if (cacheState === CACHE_STATE_DESTROY) { + if (opTargetState !== OPERATION_STATE_CANCEL) { + operation.cancel(); + } + } else if (cacheState === CACHE_STATE_IDLE) { + // Signal the cache that a prefetch operation is running. + changeState(CACHE_STATE_PREFETCH); + } + return true; + } + + switch (opTargetState) { + case OPERATION_STATE_START: + // Initial state of the operation. + if (prefetchOperations[0] === operation) { + transition(READ_STATE_LOCAL, operation.i); + } + break; + + case READ_STATE_DONE: + // State that determines if the operation can be resolved or has to + // continue processing. + // Data is expected to be the read page. + var pending = operation.pending; + + if (pending > 0) { + pending -= Math.min(pending, data.c); + } + + // Are we done, or has all the data been stored? + if (allDataLocal || pending === 0 || data.c < pageSize || overflowed) { + operation.complete(); + } else { + // Continue processing the operation. + operation.pending = pending; + transition(READ_STATE_LOCAL, data.i + pageSize); + } + break; + + default: + return readSaveStateMachine(operation, opTargetState, cacheState, data, true); + } + } + return true; + }; + + var readStateMachine = function (operation, opTargetState, cacheState, data) { + /// State machine describing the behavior of a read operation. + /// Operation being run. + /// Operation state to transition to. + /// Current cache state. + /// Additional data passed to the state. + /// + /// Read operations have a higher priority than prefetch operations, but lower than + /// clear operations. They will preempt any prefetch operation currently running + /// but will be interrupted by a clear operation. + /// + /// If a clear operation starts executing then all the currently running + /// read operations are canceled. Read operations that haven't started yet will + /// wait in the start state until the destory operation finishes. + /// + + // Handle cancelation + if (!cancelStateMachine(operation, opTargetState, cacheState, data)) { + + var transition = operation.transition; + + // Handle preemption + if (cacheState !== CACHE_STATE_READ && opTargetState !== OPERATION_STATE_START) { + if (cacheState === CACHE_STATE_DESTROY) { + if (opTargetState !== OPERATION_STATE_START) { + operation.cancel(); + } + } else if (cacheState !== CACHE_STATE_WRITE) { + // Signal the cache that a read operation is running. + changeState(CACHE_STATE_READ); + } + + return true; + } + + switch (opTargetState) { + case OPERATION_STATE_START: + // Initial state of the operation. + // Wait until the cache is idle or prefetching. + if (cacheState === CACHE_STATE_IDLE || cacheState === CACHE_STATE_PREFETCH) { + // Signal the cache that a read operation is running. + changeState(CACHE_STATE_READ); + if (operation.c > 0) { + // Snap the requested range to a page boundary. + var range = snapToPageBoundaries(operation.i, operation.c, pageSize); + transition(READ_STATE_LOCAL, range.i); + } else { + transition(READ_STATE_DONE, operation); + } + } + break; + + case READ_STATE_DONE: + // State that determines if the operation can be resolved or has to + // continue processing. + // Data is expected to be the read page. + appendPage(operation, data); + var len = operation.d.length; + // Are we done? + if (operation.c === len || data.c < pageSize) { + // Update the stats, request for a prefetch operation. + stats.cacheReads++; + prefetch(data.i + data.c); + // Terminate the operation. + operation.complete(); + } else { + // Continue processing the operation. + transition(READ_STATE_LOCAL, data.i + pageSize); + } + break; + + default: + return readSaveStateMachine(operation, opTargetState, cacheState, data, false); + } + } + + return true; + }; + + var readSaveStateMachine = function (operation, opTargetState, cacheState, data, isPrefetch) { + /// State machine describing the behavior for reading and saving data into the cache. + /// Operation being run. + /// Operation state to transition to. + /// Current cache state. + /// Additional data passed to the state. + /// Flag indicating whether a read (false) or prefetch (true) operation is running. + /// + /// This state machine contains behavior common to read and prefetch operations. + /// + + var error = operation.error; + var transition = operation.transition; + var wait = operation.wait; + var request; + + switch (opTargetState) { + case OPERATION_STATE_END: + // State that signals the operation is done. + fireOnIdle(); + break; + + case READ_STATE_LOCAL: + // State that requests for a page from the local store. + // Data is expected to be the index of the page to request. + request = readPage(data).then(function (found, page) { + // Signal the cache that a read operation is running. + if (!operation.canceled) { + if (found) { + // The page is in the local store, check if the operation can be resolved. + transition(READ_STATE_DONE, page); + } else { + // The page is not in the local store, request it from the source. + transition(READ_STATE_SOURCE, data); + } + } + }); + break; + + case READ_STATE_SOURCE: + // State that requests for a page from the cache source. + // Data is expected to be the index of the page to request. + request = fetchPage(data).then(function (page) { + // Signal the cache that a read operation is running. + if (!operation.canceled) { + // Update the stats and save the page to the local store. + if (isPrefetch) { + stats.prefetches++; + } else { + stats.netReads++; + } + transition(READ_STATE_SAVE, page); + } + }, error); + break; + + case READ_STATE_SAVE: + // State that saves a page to the local store. + // Data is expected to be the page to save. + // Write access to the store is exclusive. + if (cacheState !== CACHE_STATE_WRITE) { + changeState(CACHE_STATE_WRITE); + request = savePage(data.i, data).then(function (saved) { + if (!operation.canceled) { + if (!saved && isPrefetch) { + operation.pending = 0; + } + // Check if the operation can be resolved. + transition(READ_STATE_DONE, data); + } + changeState(CACHE_STATE_IDLE); + }); + } + break; + + default: + // Unknown state that can't be handled by this state machine. + return false; + } + + if (request) { + // The operation might have been canceled between stack frames do to the async calls. + if (operation.canceled) { + request.cancel(); + } else if (operation.s === opTargetState) { + // Wait for the request to complete. + wait(request); + } + } + + return true; + }; + + // Initialize the cache. + store.read("__settings", function (_, settings) { + if (assigned(settings)) { + var settingsVersion = settings.version; + if (!settingsVersion || settingsVersion.indexOf("1.") !== 0) { + cacheFailureCallback("Unsupported cache store version " + settingsVersion)(); + return; + } + + if (pageSize !== settings.pageSize || source.identifier !== settings.sourceId) { + // The shape or the source of the data was changed so invalidate the store. + clearStore().then(function () { + // Signal the cache is fully initialized. + changeState(CACHE_STATE_IDLE); + }, cacheFailureCallback("Unable to clear store during initialization")); + } else { + // Restore the saved settings. + actualCacheSize = settings.actualCacheSize; + allDataLocal = settings.allDataLocal; + cacheSize = settings.cacheSize; + collectionCount = settings.collectionCount; + highestSavedPage = settings.highestSavedPage; + highestSavedPageSize = settings.highestSavedPageSize; + version = settingsVersion; + + // Signal the cache is fully initialized. + changeState(CACHE_STATE_IDLE); + } + } else { + // This is a brand new cache. + saveSettings(function () { + // Signal the cache is fully initialized. + changeState(CACHE_STATE_IDLE); + }, cacheFailureCallback("Unable to write settings during initialization.")); + } + }, cacheFailureCallback("Unable to read settings from store.")); + + return that; + }; + + datajs.createDataCache = function (options) { + /// Creates a data cache for a collection that is efficiently loaded on-demand. + /// + /// Options for the data cache, including name, source, pageSize, + /// prefetchSize, cacheSize, storage mechanism, and initial prefetch and local-data handler. + /// + /// A new data cache instance. + checkUndefinedGreaterThanZero(options.pageSize, "pageSize"); + checkUndefinedOrNumber(options.cacheSize, "cacheSize"); + checkUndefinedOrNumber(options.prefetchSize, "prefetchSize"); + + if (!assigned(options.name)) { + throw { message: "Undefined or null name", options: options }; + } + + if (!assigned(options.source)) { + throw { message: "Undefined source", options: options }; + } + + return new DataCache(options); + }; + + + +})(this); diff --git a/TypeSystem/Types/Blob.js b/TypeSystem/Types/Blob.js index 78712448..3be26668 100644 --- a/TypeSystem/Types/Blob.js +++ b/TypeSystem/Types/Blob.js @@ -83,7 +83,6 @@ $data.Container.registerConverter('$data.Blob',{ if (typeof Blob !== 'undefined' && value instanceof Blob){ var req = new XMLHttpRequest(); req.open('GET', URL.createObjectURL(value), false); - req.responseType = 'arraybuffer'; req.send(null); return $data.Container.convertTo(req.response, $data.Blob); } else if (typeof ArrayBuffer !== 'undefined' && value instanceof ArrayBuffer) { diff --git a/Types/Entity.js b/Types/Entity.js index f466ef34..ee4fff7e 100644 --- a/Types/Entity.js +++ b/Types/Entity.js @@ -490,7 +490,7 @@ $data.Entity = Entity = $data.Class.define("$data.Entity", null, null, { throw 'not implemented'; //todo }, refresh: function () { - if ($data.ItemStore && 'EntityInstanceSave' in $data.ItemStore) + if ($data.ItemStore && 'EntityInstanceRefresh' in $data.ItemStore) return $data.ItemStore.EntityInstanceRefresh.apply(this, arguments); else throw 'not implemented'; //todo @@ -514,6 +514,14 @@ $data.Entity = Entity = $data.Class.define("$data.Entity", null, null, { } return '#'; } +// +// EXAMPLE of how to add function calls +// +// }, +// findKeyName: function(es) { +// return es.memberDefinitions.getKeyProperties()[0].name; +// } + }, { //create get_[property] and set_[property] functions for properties diff --git a/Types/EntitySet.js b/Types/EntitySet.js index 36c31365..3cb47255 100644 --- a/Types/EntitySet.js +++ b/Types/EntitySet.js @@ -1,3 +1,4 @@ + ///EntitySet is responsible for /// -creating and holding entityType through schema /// - provide Add method @@ -66,7 +67,7 @@ $data.Class.defineEx('$data.EntitySet', } trackedEntities.push({ entitySet: this, data: entity }); }, - add: function (entity) { + add: function (entity, req) { /// /// Creates a typed entity and adds to the context. /// The init parameters whish is based on Entity @@ -101,7 +102,26 @@ $data.Class.defineEx('$data.EntitySet', data.entityState = $data.EntityState.Added; data.changedProperties = undefined; data.context = this.entityContext; - this._trackEntity(data); + + var keyName =this.elementType.memberDefinitions.getKeyProperties()[0].name; + var keyValue = data[keyName]; + if (req.reso.externalIndex) { + if (INDEX.indexOf(keyValue) == -1) { + INDEX[INDEX.length] = keyValue; + req.reso.resultSize = 1; + req.reso.keyValue = keyValue; + this._trackEntity(data); +//console.dir(data); +// } else { +//console.log("Item with a " + keyName + " of " + keyValue + " already exists. Not added"); + } + } else { + req.reso.resultSize = 1; + req.reso.keyValue = keyValue; + this._trackEntity(data); + } + + return data; }, @@ -113,7 +133,7 @@ $data.Class.defineEx('$data.EntitySet', }); return result; }, - remove: function (entity) { + remove: function (entity, req) { /// /// Creates a typed entity and marks it as Deleted. /// The init parameters whish is based on Entity @@ -145,10 +165,28 @@ $data.Class.defineEx('$data.EntitySet', data = new this.createNew(entity); } data.entityState = $data.EntityState.Deleted; + + var keyName =this.elementType.memberDefinitions.getKeyProperties()[0].name; + if (req.reso.externalIndex) { + if (entity[keyName]) { + var keyValue = entity[keyName]; + req.reso.resultSize = 1; + req.reso.keyValue = entity[keyName]; + var position = INDEX.indexOf(keyValue); + if (position != -1) { + INDEX.splice(position,1); + } + } + } else { + req.reso.resultSize = 1; + req.reso.keyValue = entity[keyName]; + } + + data.changedProperties = undefined; this._trackEntity(data); }, - attach: function (entity, mode) { + attach: function (entity, mode, req) { /// /// Creates a typed entity and adds to the Context with Unchanged state. /// The init parameters whish is based on Entity @@ -214,6 +252,11 @@ $data.Class.defineEx('$data.EntitySet', data.changedProperties = undefined; }*/ data.context = this.entityContext; + if (req) { + var keyName =this.elementType.memberDefinitions.getKeyProperties()[0].name; + req.reso.resultSize = 1; + req.reso.keyValue = entity[keyName]; + } this._trackEntity(data); }, detach: function (entity) { diff --git a/Types/ItemStore.js b/Types/ItemStore.js index 4dc37a02..35a86ac8 100644 --- a/Types/ItemStore.js +++ b/Types/ItemStore.js @@ -23,6 +23,7 @@ $data.Class.define('$data.ItemStoreClass', null, null, { }, true); }, itemStoreConfig: {}, + attachMode: $data.EntityAttachMode ? $data.EntityAttachMode.KeepChanges : true, addItemStoreAlias: function (name, contextFactoryOrToken, isDefault) { var self = this; @@ -246,7 +247,7 @@ $data.Class.define('$data.ItemStoreClass', null, null, { //Entity Instance - EntityInstanceSave: function (storeAlias, hint) { + EntityInstanceSave: function (storeAlias, hint, attachMode) { var self = $data.ItemStore; var entity = this; return self._getStoreEntitySet(storeAlias, entity) @@ -259,8 +260,7 @@ $data.Class.define('$data.ItemStoreClass', null, null, { entitySet.add(entity); break; case 'attach': - entitySet.attach(entity, true); - entity.entityState = $data.EntityState.Modified; + entitySet.attach(entity, attachMode || true); break; default: var d = new $data.PromiseHandler(); diff --git a/Types/Queryable.js b/Types/Queryable.js index bbe3a93e..7e036b6b 100644 --- a/Types/Queryable.js +++ b/Types/Queryable.js @@ -760,6 +760,9 @@ $data.Class.define('$data.Queryable', null, null, var inlineCountExp = Container.createInlineCountExpression(this.expression, constExp); return Container.createQueryable(this, inlineCountExp); }, + withCount: function (selector) { + return this.withInlineCount(selector); + }, removeAll: function (onResult, transaction) { /// Delete the query result and returns the number of deleted entities in a query as the callback parameter. diff --git a/Types/ServiceOperation.js b/Types/ServiceOperation.js index ec2b5f03..2d24c623 100644 --- a/Types/ServiceOperation.js +++ b/Types/ServiceOperation.js @@ -80,6 +80,7 @@ $data.Class.define('$data.ServiceOperation', null, null, {}, { if (cfg.params) { paramConstExpression = []; //object as parameter + //FIX: object type parameters with the same property name as the name of the first parameter if (arguments[0] && typeof arguments[0] === 'object' && arguments[0].constructor === $data.Object && cfg.params && cfg.params[0] && cfg.params[0].name in arguments[0]) { var argObj = arguments[0]; for (var i = 0; i < cfg.params.length; i++) { @@ -166,4 +167,4 @@ $data.Class.define('$data.ServiceAction', $data.ServiceOperation, null, {}, { return $data.ServiceOperation.generateServiceOperation.apply(this, arguments); } -}); \ No newline at end of file +}); diff --git a/Types/StorageProviderLoader.js b/Types/StorageProviderLoader.js index 604fdf92..587b5701 100644 --- a/Types/StorageProviderLoader.js +++ b/Types/StorageProviderLoader.js @@ -22,6 +22,9 @@ $data.Class.define('$data.StorageProviderLoaderBase', null, null, { case 'mongoDB': supported = $data.mongoDBDriver; break; + case 'libRETS': + supported = $data.libRETSDriver; + break; default: break; } diff --git a/Types/StorageProviders/libRETS/libRETSStorageProvider.js b/Types/StorageProviders/libRETS/libRETSStorageProvider.js new file mode 100644 index 00000000..f2260405 --- /dev/null +++ b/Types/StorageProviders/libRETS/libRETSStorageProvider.js @@ -0,0 +1,551 @@ +$C('$data.storageProviders.libRETS.libRETSProvider', $data.StorageProviderBase, null, +{ + constructor: function(cfg, ctx){ + this.driver = $data.libRETSDriver; + this.context = ctx; + this.providerConfiguration = $data.typeSystem.extend({ + dbCreation: $data.storageProviders.DbCreationType.DropTableIfChanged, + address: '127.0.0.1', + port: 6103, + serverOptions: {} + }, cfg); + if (this.context && this.context._buildDbType_generateConvertToFunction && this.buildDbType_generateConvertToFunction) { + this.context._buildDbType_generateConvertToFunction = this.buildDbType_generateConvertToFunction; + } + if (this.context && this.context._buildDbType_modifyInstanceDefinition && this.buildDbType_modifyInstanceDefinition) { + this.context._buildDbType_modifyInstanceDefinition = this.buildDbType_modifyInstanceDefinition; + } + }, + _getServer: function(){ + return this.driver.Server(this.providerConfiguration.address, this.providerConfiguration.port, this.providerConfiguration.serverOptions); + }, + initializeStore: function(callBack){ + var self = this; + callBack = $data.typeSystem.createCallbackSetting(callBack); + + var server = this._getServer(); + new this.driver.Db(server, { safe: false }).open(function(error, client){ + if (error){ + callBack.error(error); + return; + } + + var fn = function(error, client){ + var cnt = 0; + var collectionCount = 0; + var readyFn = function(client, entitySet){ + var countFn = function(){ + if (--cnt <= 0){ + callBack.success(self.context); + client.close(); + } + }; + + if (entitySet){ + var entitySetIndices = self.context._storageModel.getStorageModel(entitySet.createNew).indices; + if (entitySetIndices && typeof self._createIndices === 'function'){ + self._createIndices(client, entitySet, entitySetIndices, countFn); + }else countFn(); + }else countFn(); + }; + + for (var i in self.context._entitySetReferences){ + if (self.context._entitySetReferences.hasOwnProperty(i)) + cnt++; + } + + collectionCount = cnt; + var sets = Object.keys(self.context._entitySetReferences); + if (!sets.length) return readyFn(client); + sets.forEach(function(i){ + if (self.context._entitySetReferences.hasOwnProperty(i)){ + client.collectionNames({ namesOnly: true }, function(error, names){ + names = names.map(function(it){ return it.slice(it.lastIndexOf('.') + 1); }); + switch (self.providerConfiguration.dbCreation){ + case $data.storageProviders.DbCreationType.DropAllExistingTables: + if (names.indexOf(self.context._entitySetReferences[i].tableName) >= 0){ + client.dropCollection(self.context._entitySetReferences[i].tableName, function(error, result){ + if (error){ + callBack.error(error); + return; + } + if (self.context._entitySetReferences[i].tableOptions){ + client.createCollection(self.context._entitySetReferences[i].tableName, self.context._entitySetReferences[i].tableOptions, function(error, result){ + if (error){ + callBack.error(error); + return; + } + readyFn(client, self.context._entitySetReferences[i]); + }); + }else readyFn(client, self.context._entitySetReferences[i]); + }); + }else if (names.indexOf(self.context._entitySetReferences[i].tableName) < 0 && self.context._entitySetReferences[i].tableOptions){ + client.createCollection(self.context._entitySetReferences[i].tableName, self.context._entitySetReferences[i].tableOptions, function(error, result){ + if (error){ + callBack.error(error); + return; + } + readyFn(client, self.context._entitySetReferences[i]); + }); + }else readyFn(client, self.context._entitySetReferences[i]); + break; + default: + if (names.indexOf(self.context._entitySetReferences[i].tableName) < 0 && self.context._entitySetReferences[i].tableOptions){ + client.createCollection(self.context._entitySetReferences[i].tableName, self.context._entitySetReferences[i].tableOptions, function(error, result){ + if (error){ + callBack.error(error); + return; + } + readyFn(client, self.context._entitySetReferences[i]); + }); + }else readyFn(client, self.context._entitySetReferences[i]); + break; + } + }); + } + }); + }; + + if (self.providerConfiguration.username){ + client.authenticate(self.providerConfiguration.username, self.providerConfiguration.password || '', function(error, result){ + if (error){ + callBack.error(error); + return; + } + + if (result){ + fn(error, client); + return; + } + }); + }else fn(error, client); + }); + }, + _connected: function(oid, prop, prop2, it, association){ + var ret = false; + association.ReferentialConstraint.forEach(function(ref){ + if (it && ref[prop2] && oid[ref[prop2]] != undefined) ret = JSON.stringify(oid[ref[prop2]]) == JSON.stringify(it[ref[prop]] != undefined ? it[ref[prop]] : it._id); + }); + return ret; + }, + _compile: function(query){ + return new $data.storageProviders.mongoDB.mongoDBCompiler().compile(query); + }, + getTraceString: function (queryable) { + return this._compile(queryable); + }, + executeQuery: function(query, callBack){ + var self = this; + callBack = $data.typeSystem.createCallbackSetting(callBack); + + var entitySet = query.context.getEntitySetFromElementType(query.defaultType); + this._compile(query); + + var server = this._getServer(); + new this.driver.Db(server, { safe: false }).open(function(error, client){ + if (error){ + callBack.error(error); + return; + } + + var collection = new self.driver.Collection(client, entitySet.tableName); + var includes = query.includes && query.includes.length ? query.includes.map(function(it){ + //if (it.full){ + delete it.options.fields; + //} + return { + name: it.name, + type: it.type, + from: it.from, + collection: new self.driver.Collection(client, query.context.getEntitySetFromElementType(it.type).tableName), + query: it.query || {}, + options: it.options || {} + }; + }) : null; + + query.context = self.context; + var find = query.find; + + var cb = function(error, results){ + if (error){ + callBack.error(error); + return; + } + if (query.find.filter){ + results = results.filter(query.find.filter); + } + + if (query.expression.nodeType === $data.Expressions.ExpressionType.Count || query.expression.nodeType === $data.Expressions.ExpressionType.BatchDelete){ + if (results instanceof Array){ + query.rawDataList = [{ cnt: results.length }]; + }else{ + query.rawDataList = [{ cnt: results }]; + } + }else{ + query.rawDataList = results; + } + + callBack.success(query); + client.close(); + }; + + var fn = function(){ + switch (query.expression.nodeType){ + case $data.Expressions.ExpressionType.BatchDelete: + collection.remove(find.query, { safe: true }, cb); + break; + case $data.Expressions.ExpressionType.Count: + if (!includes || !includes.length){ + collection.find(find.query, find.options).count(cb); + break; + } + default: + if (find.full){ + delete find.options.fields; + } + var defaultFn = function(){ + collection.find(find.query, find.options).toArray(function(error, results){ + if (error){ + callBack.error(error); + return; + } + + var fn = function(include){ + include.collection.find({}, include.options).toArray(function(error, included){ + if (error){ + callBack.error(error); + return; + } + + var path = include.name.split('.'); + var prop = path[path.length - 1]; + var sm = self.context._storageModel.getStorageModel(include.from); + + var association = sm.Associations[prop]; + + var conn = function(res){ + if (association.FromMultiplicity == '0..1' && association.ToMultiplicity == '*'){ + var r = included.filter(function(it){ + return self._connected(it, association.ToPropertyName, association.To, res, association); + }); + res[prop] = r; + }else if (association.FromMultiplicity == '*' && association.ToMultiplicity == '0..1'){ + var r = included.filter(function(it){ + if (res[association.FromPropertyName] === null) return false; + return self._connected(res, association.FromPropertyName, association.From, it, association); + })[0]; + res[prop] = r || res[prop]; + }else if (association.FromMultiplicity == '1' && association.ToMultiplicity == '0..1'){ + var r = included.filter(function(it){ + return self._connected(it, association.ToPropertyName, association.To, res, association); + })[0]; + res[prop] = r || res[prop]; + }else if (association.FromMultiplicity == '0..1' && association.ToMultiplicity == '1'){ + var r = included.filter(function(it){ + return self._connected(res, association.FromPropertyName, association.From, it, association); + })[0]; + res[prop] = r || res[prop]; + } + }; + + var respath = function(res, path){ + var _conn = true; + for (var j = 0; j < path.length; j++){ + if (typeof res[path[j]] !== 'undefined') res = res[path[j]]; + if (Array.isArray(res) && res.length){ + _conn = false; + for (var k = 0; k < res.length; k++){ + if (j < path.length - 1) respath(res[k], path.slice(j)); + else conn(res[k]); + } + } + if (!_conn) break; + } + if (_conn){ + conn(res); + } + }; + + for (var i = 0; i < results.length; i++){ + respath(results[i], path.slice(0, -1)); + } + + if (include.options.sort) { + var order = Object.keys(include.options.sort); + var cmp = order.map(function(it){ + return new Function('it', 'return it.' + it + ';'); + }); + results.sort(function (a, b) { + var result; + for (var i = 0, l = order.length; i < l; i++) { + result = 0; + var aVal = cmp[i](a); + var bVal = cmp[i](b); + + if (include.options.sort[order[i]] == 1) + result = aVal === bVal ? 0 : (aVal > bVal || bVal === null ? 1 : -1); + else + result = aVal === bVal ? 0 : (aVal < bVal || aVal === null ? 1 : -1); + + if (result !== 0) break; + + } + return result; + }); + } + + if (includes && includes.length){ + fn(includes.shift()); + }else{ + cb(error, results); + } + }); + }; + + if (includes && includes.length){ + fn(includes.shift()); + }else{ + cb(error, results); + } + }); + }; + if (query.withInlineCount){ + collection.find(find.query, {}).count(function(err, result){ + if (error){ + callBack.error(error); + return; + } + query.__count = result; + defaultFn(); + }); + }else defaultFn(); + break; + } + }; + + if (self.providerConfiguration.username){ + client.authenticate(self.providerConfiguration.username, self.providerConfiguration.password, function(error, result){ + if (error){ + callBack.error(error); + return; + } + + if (result) fn(); + else callBack.error('Authentication failed'); + }); + }else fn(); + }); + }, + _typeFactory: function(type, value, converter){ + if ((value && value.$ref && value.$id) || value == null || value == undefined) return value; + var type = Container.resolveName(type); + var converterFn = converter ? converter[type] : undefined; + return converter && converter[type] ? converter[type](value) : new (Container.resolveType(type))(value); + }, + saveChanges: function(callBack, changedItems){ + callBack.success(0); + }, + buildDbType_generateConvertToFunction: function (storageModel) { + var self = this; + return function (logicalEntity) { + var dbInstance = new storageModel.PhysicalType(); + dbInstance.entityState = logicalEntity.entityState; + + storageModel.PhysicalType.memberDefinitions.getPublicMappedProperties().forEach(function (property) { + dbInstance.initData[property.name] = logicalEntity[property.name]; + }, this); + + if (storageModel.Associations) { + storageModel.Associations.forEach(function (association) { + if ((association.FromMultiplicity == "*" && association.ToMultiplicity == "0..1") || (association.FromMultiplicity == "0..1" && association.ToMultiplicity == "1")) { + var complexInstance = logicalEntity[association.FromPropertyName]; + if (complexInstance !== undefined) { + association.ReferentialConstraint.forEach(function (constrain) { + if (complexInstance !== null) { + dbInstance.initData[association.FromPropertyName] = { + $ref: self._entitySetReferences[association.To].tableName, + $id: self.storageProvider._typeFactory(complexInstance.getType().memberDefinitions.getMember(constrain[association.To]).type, complexInstance[constrain[association.To]], self.storageProvider.fieldConverter.toDb) + }; + dbInstance.initData[constrain[association.From]] = self.storageProvider._typeFactory(complexInstance.getType().memberDefinitions.getMember(constrain[association.To]).type, complexInstance[constrain[association.To]], self.storageProvider.fieldConverter.toDb); + dbInstance._setPropertyChanged(dbInstance.getType().memberDefinitions.getMember(constrain[association.From])); + } else { + dbInstance.initData[association.FromPropertyName] = null; + dbInstance.initData[constrain[association.From]] = null; + dbInstance._setPropertyChanged(dbInstance.getType().memberDefinitions.getMember(constrain[association.From])); + } + }, this); + } + } + }, this); + } + if (storageModel.ComplexTypes) { + storageModel.ComplexTypes.forEach(function (cmpType) { + var complexInstance = logicalEntity[cmpType.FromPropertyName]; + dbInstance.initData[cmpType.FromPropertyName] = self.storageProvider._typeFactory(cmpType.ToType, complexInstance, self.storageProvider.fieldConverter.toDb); + }, this); + } + return dbInstance; + }; + }, + buildDbType_modifyInstanceDefinition: function (instanceDefinition, storageModel) { + var buildDbType_copyPropertyDefinition = function (propertyDefinition, refProp) { + var cPropertyDef; + if (refProp) { + cPropertyDef = JSON.parse(JSON.stringify(instanceDefinition[refProp])); + cPropertyDef.kind = propertyDefinition.kind; + cPropertyDef.name = propertyDefinition.name; + cPropertyDef.notMapped = false; + } else { + cPropertyDef = JSON.parse(JSON.stringify(propertyDefinition)); + } + + cPropertyDef.dataType = Container.resolveType(propertyDefinition.dataType); + cPropertyDef.type = cPropertyDef.dataType; + cPropertyDef.key = false; + cPropertyDef.computed = false; + return cPropertyDef; + }; + var buildDbType_createConstrain = function (foreignType, dataType, propertyName, prefix) { + var constrain = new Object(); + constrain[foreignType.name] = propertyName; + constrain[dataType.name] = prefix + '__' + propertyName; + return constrain; + }; + + if (storageModel.Associations) { + storageModel.Associations.forEach(function (association) { + var addToEntityDef = false; + var foreignType = association.FromType; + var dataType = association.ToType; + var foreignPropName = association.ToPropertyName; + + association.ReferentialConstraint = association.ReferentialConstraint || []; + + if ((association.FromMultiplicity == "*" && association.ToMultiplicity == "0..1") || (association.FromMultiplicity == "0..1" && association.ToMultiplicity == "1")) { + foreignType = association.ToType; + dataType = association.FromType; + foreignPropName = association.FromPropertyName; + addToEntityDef = true; + } + + foreignType.memberDefinitions.getPublicMappedProperties().filter(function (d) { return d.key }).forEach(function (d) { + if (addToEntityDef) { + instanceDefinition[foreignPropName + '__' + d.name] = buildDbType_copyPropertyDefinition(d, foreignPropName); + } + association.ReferentialConstraint.push(buildDbType_createConstrain(foreignType, dataType, d.name, foreignPropName)); + }, this); + }, this); + } + }, + save_getInitData: function(item, convertedItems) { + var self = this; + item.physicalData = this.context._storageModel.getStorageModel(item.data.getType()).PhysicalType.convertTo(item.data, convertedItems); + var serializableObject = {}; + item.physicalData.getType().memberDefinitions.asArray().forEach(function (memdef) { + if (memdef.kind == $data.MemberTypes.navProperty || memdef.kind == $data.MemberTypes.complexProperty || (memdef.kind == $data.MemberTypes.property && !memdef.notMapped)) { + serializableObject[memdef.computed ? '_id' : memdef.name] = item.physicalData[memdef.name]; + } + }, this); + return serializableObject; + }, + + supportedDataTypes: { + value: [$data.Integer, $data.String, $data.Number, $data.Blob, $data.Boolean, $data.Date, $data.ObjectID, $data.Object, $data.GeographyPoint, $data.Guid, + $data.GeographyLineString, $data.GeographyPolygon, $data.GeographyMultiPoint, $data.GeographyMultiLineString, $data.GeographyMultiPolygon, $data.GeographyCollection, + $data.GeometryPoint, $data.GeometryLineString, $data.GeometryPolygon, $data.GeometryMultiPoint, $data.GeometryMultiLineString, $data.GeometryMultiPolygon, $data.GeometryCollection, + $data.Byte, $data.SByte, $data.Decimal, $data.Float, $data.Int16, $data.Int32, $data.Int64, $data.Time, $data.DateTimeOffset], + writable: false + }, + + supportedBinaryOperators: { + value: { + equal: { mapTo: ':', dataType: "boolean", allowedIn: [$data.Expressions.FilterExpression] }, + notEqual: { mapTo: '$ne', dataType: "boolean", allowedIn: [$data.Expressions.FilterExpression] }, + equalTyped: { mapTo: ':', dataType: "boolean", allowedIn: [$data.Expressions.FilterExpression] }, + notEqualTyped: { mapTo: '$ne', dataType: "boolean", allowedIn: [$data.Expressions.FilterExpression] }, + greaterThan: { mapTo: '$gt', dataType: "boolean", allowedIn: [$data.Expressions.FilterExpression] }, + greaterThanOrEqual: { mapTo: '$gte', dataType: "boolean", allowedIn: [$data.Expressions.FilterExpression] }, + + lessThan: { mapTo: '$lt', dataType: "boolean", allowedIn: [$data.Expressions.FilterExpression] }, + lessThenOrEqual: { mapTo: '$lte', dataType: "boolean", allowedIn: [$data.Expressions.FilterExpression] }, + or: { mapTo: '$or', dataType: "boolean", allowedIn: [$data.Expressions.FilterExpression] }, + and: { mapTo: '$and', dataType: "boolean", allowedIn: [$data.Expressions.FilterExpression] }, + + "in": { mapTo: "$in", allowedIn: [$data.Expressions.FilterExpression] } + } + }, + + supportedUnaryOperators: { + value: { + not: { mapTo: '$nor' } + } + }, + + supportedFieldOperations: { + value: { + contains: { + dataType: "boolean", allowedIn: [$data.Expressions.FilterExpression], + parameters: [{ name: "substring", dataType: "string" }] + }, + + startsWith: { + dataType: "string", allowedIn: [$data.Expressions.FilterExpression], + parameters: [{ name: "@expression", dataType: "string" }, { name: "strFragment", dataType: "string" }] + }, + + endsWith: { + dataType: "string", allowedIn: [$data.Expressions.FilterExpression], + parameters: [{ name: "@expression", dataType: "string" }, { name: "strFragment", dataType: "string" }] + } + }, + enumerable: true, + writable: true + }, + supportedSetOperations: { + value: { + filter: {}, + map: {}, + length: {}, + forEach: {}, + toArray: {}, + batchDelete: {}, + single: {}, + take: {}, + skip: {}, + orderBy: {}, + orderByDescending: {}, + first: {}, + include: {}, + withInlineCount: {}, + some: { + invokable: false, + allowedIn: [$data.Expressions.FilterExpression], + parameters: [{ name: "filter", dataType: "$data.Queryable" }], + mapTo: 'some', + frameType: $data.Expressions.SomeExpression + }, + every: { + invokable: false, + allowedIn: [$data.Expressions.FilterExpression], + parameters: [{ name: "filter", dataType: "$data.Queryable" }], + mapTo: 'every', + frameType: $data.Expressions.EveryExpression + } + }, + enumerable: true, + writable: true + }, + fieldConverter: { value: $data.mongoDBConverter } +}, { + isSupported: { + get: function(){ + if (!$data.libRETSDriver) return false; + return true; + }, + set: function(value){} + } +}); + +if ($data.storageProviders.libRETS.libRETSProvider.isSupported){ + $data.StorageProviderBase.registerProvider('libRETS', $data.storageProviders.libRETS.libRETSProvider); +} diff --git a/Types/StorageProviders/modelBinderConfigCompiler.js b/Types/StorageProviders/modelBinderConfigCompiler.js index 4f7eae6d..8f9d14f6 100644 --- a/Types/StorageProviders/modelBinderConfigCompiler.js +++ b/Types/StorageProviders/modelBinderConfigCompiler.js @@ -93,7 +93,7 @@ $C('$data.modelBinder.ModelBinderConfigCompiler', $data.Expressions.EntityExpres var builder = Container.createqueryBuilder(); builder.modelBinderConfig['$type'] = $data.Array; if (this._isoDataProvider) { - builder.modelBinderConfig['$selector'] = ['json:d.results', 'json:d', 'json:results']; + builder.modelBinderConfig['$selector'] = ['json:d.results', 'json:d', 'json:results', 'json:value']; } builder.modelBinderConfig['$item'] = {}; builder.selectModelBinderProperty('$item'); @@ -156,8 +156,10 @@ $C('$data.modelBinder.ModelBinderConfigCompiler', $data.Expressions.EntityExpres $value: function (meta, data) { return data; } } }*/ - builder._binderConfig.$item = builder._binderConfig.$item || {}; - builder.modelBinderConfig = builder._binderConfig.$item; + if (builder._binderConfig.$type === $data.Array) { + builder._binderConfig.$item = builder._binderConfig.$item || {}; + builder.modelBinderConfig = builder._binderConfig.$item; + } diff --git a/Types/StorageProviders/mongoDB/mongoDBStorageProvider.js b/Types/StorageProviders/mongoDB/mongoDBStorageProvider.js index 7dadbe47..8b8add7e 100644 --- a/Types/StorageProviders/mongoDB/mongoDBStorageProvider.js +++ b/Types/StorageProviders/mongoDB/mongoDBStorageProvider.js @@ -400,7 +400,11 @@ $C('$data.storageProviders.mongoDB.mongoDBProvider', $data.StorageProviderBase, docs.push(d.data); } - collection.insert(docs, { safe: true }, function(error, result){ + var optionsObj = { continueOnError: true }; + if (self.providerConfiguration.externalIndex) { + optionsObj = { safe: true }; + } + collection.insert(docs, optionsObj, function(error, result){ if (error){ callBack.error(error); client.close(); diff --git a/Types/StorageProviders/oData/oDataCompiler.js b/Types/StorageProviders/oData/oDataCompiler.js index fb25b400..adb3ea8c 100644 --- a/Types/StorageProviders/oData/oDataCompiler.js +++ b/Types/StorageProviders/oData/oDataCompiler.js @@ -10,7 +10,10 @@ $C('$data.storageProviders.oData.oDataCompiler', $data.Expressions.EntityExpress this.provider = query.context.storageProvider; this.context = query.context; - this.mainEntitySet = query.context.getEntitySetFromElementType(query.defaultType); + + if (query.defaultType) { + this.mainEntitySet = query.context.getEntitySetFromElementType(query.defaultType); + } var queryFragments = { urlText: "" }; @@ -24,7 +27,9 @@ $C('$data.storageProviders.oData.oDataCompiler', $data.Expressions.EntityExpress var queryText = queryFragments.urlText; var addAmp = false; for (var name in queryFragments) { - if (name != "urlText" && name != "actionPack" && name != "data" && name != "lambda" && name != "method" && name != "postData" && queryFragments[name] != "") { + if (name != "urlText" && name != "actionPack" && name != "data" && name != "lambda" && name != "method" && name != "postData" && + name != "_isBatchExecuteQuery" && name != "_subQueries" && queryFragments[name] != "") { + if (addAmp) { queryText += "&"; } else { queryText += "?"; } addAmp = true; if(name != "$urlParams"){ @@ -36,14 +41,21 @@ $C('$data.storageProviders.oData.oDataCompiler', $data.Expressions.EntityExpress } query.queryText = queryText; query.postData = queryFragments.postData; - - return { + var result = { queryText: queryText, - withInlineCount: '$inlinecount' in queryFragments, + withInlineCount: '$inlinecount' in queryFragments || '$count' in queryFragments, method: queryFragments.method || 'GET', postData: queryFragments.postData, + isBatchExecuteQuery: queryFragments._isBatchExecuteQuery, + subQueries: queryFragments._subQueries, params: [] }; + + query._getComplitedData = function () { + return result; + } + + return result; }, VisitOrderExpression: function (expression, context) { this.Visit(expression.source, context); @@ -132,7 +144,11 @@ $C('$data.storageProviders.oData.oDataCompiler', $data.Expressions.EntityExpress }, VisitInlineCountExpression: function (expression, context) { this.Visit(expression.source, context); - context["$inlinecount"] = expression.selector.value; + if (this.provider.providerConfiguration.maxDataServiceVersion === "4.0") { + context["$count"] = expression.selector.value === 'allpages'; + } else { + context["$inlinecount"] = expression.selector.value; + } }, VisitEntitySetExpression: function (expression, context) { context.urlText += "/" + expression.instance.tableName; @@ -203,5 +219,25 @@ $C('$data.storageProviders.oData.oDataCompiler', $data.Expressions.EntityExpress VisitCountExpression: function (expression, context) { this.Visit(expression.source, context); context.urlText += '/$count'; + }, + + VisitBatchExecuteQueryExpression: function (expression, context) { + context.urlText += '/$batch' + context.method = 'POST'; + context.postData = { __batchRequests: [] }; + context._isBatchExecuteQuery = true; + context._subQueries = expression.members; + + for (var i = 0; i < expression.members.length; i++) { + var queryable = expression.members[i]; + var compiler = new $data.storageProviders.oData.oDataCompiler(); + var compiled = compiler.compile(queryable); + context.postData.__batchRequests.push({ + requestUri: compiled.queryText, + method: compiled.method, + data: compiled.data, + headers: compiled.headers + }); + } } -}, {}); \ No newline at end of file +}, {}); diff --git a/Types/StorageProviders/oData/oDataProvider.js b/Types/StorageProviders/oData/oDataProvider.js index c371c53f..612bb0e3 100644 --- a/Types/StorageProviders/oData/oDataProvider.js +++ b/Types/StorageProviders/oData/oDataProvider.js @@ -1,6 +1,6 @@ var datajsPatch; -datajsPatch = function () { +datajsPatch = function (OData) { // just datajs-1.1.0 if (OData && OData.jsonHandler && 'useJsonLight' in OData.jsonHandler && typeof datajs === 'object' && !datajs.version) { $data.Trace.log('!!!!!!! - patch datajs 1.1.0'); @@ -25,11 +25,6 @@ datajsPatch = function () { $C('$data.storageProviders.oData.oDataProvider', $data.StorageProviderBase, null, { constructor: function (cfg, ctx) { - if (typeof OData === 'undefined') { - Guard.raise(new Exception('datajs is required', 'Not Found!')); - } - datajsPatch(); - this.SqlCommands = []; this.context = ctx; this.providerConfiguration = $data.typeSystem.extend({ @@ -48,6 +43,21 @@ $C('$data.storageProviders.oData.oDataProvider', $data.StorageProviderBase, null UpdateMethod: 'PATCH' }, cfg); + if (this.providerConfiguration.maxDataServiceVersion === "4.0") { + if (typeof odatajs === 'undefined' || typeof odatajs.oData === 'undefined') { + Guard.raise(new Exception('odatajs is required', 'Not Found!')); + } else { + this.oData = odatajs.oData + } + } else { + if (typeof OData === 'undefined') { + Guard.raise(new Exception('datajs is required', 'Not Found!')); + } else { + this.oData = OData; + datajsPatch(this.oData); + } + } + this.fixkDataServiceVersions(cfg); if (this.context && this.context._buildDbType_generateConvertToFunction && this.buildDbType_generateConvertToFunction) { @@ -94,7 +104,7 @@ $C('$data.storageProviders.oData.oDataProvider', $data.StorageProviderBase, null //} this.context.prepareRequest.call(this, requestData); - OData.request.apply(this, requestData); + this.oData.request.apply(this, requestData); } else { callBack.success(that.context); } @@ -164,7 +174,7 @@ $C('$data.storageProviders.oData.oDataProvider', $data.StorageProviderBase, null executeQuery: function (query, callBack) { callBack = $data.typeSystem.createCallbackSetting(callBack); - var sql; + var sql = {}; try { sql = this._compile(query); } catch (e) { @@ -174,21 +184,48 @@ $C('$data.storageProviders.oData.oDataProvider', $data.StorageProviderBase, null var schema = this.context; var that = this; + var countProperty = "__count"; + if (this.providerConfiguration.maxDataServiceVersion === "4.0") { + countProperty = "@odata.count"; + } + var requestData = [ { requestUri: this.providerConfiguration.oDataServiceHost + sql.queryText, method: sql.method, data: sql.postData, headers: { - MaxDataServiceVersion: this.providerConfiguration.maxDataServiceVersion } }, function (data, textStatus, jqXHR) { - if (!data && textStatus.body) data = JSON.parse(textStatus.body); + + if (!data && textStatus.body && !sql.isBatchExecuteQuery) data = JSON.parse(textStatus.body); if (callBack.success) { - query.rawDataList = typeof data === 'string' ? [{ cnt: Container.convertTo(data, $data.Integer) }] : data; - if (sql.withInlineCount && typeof data === 'object' && (typeof data.__count !== 'undefined' || ('d' in data && typeof data.d.__count !== 'undefined'))) { - query.__count = new Number(typeof data.__count !== 'undefined' ? data.__count : data.d.__count).valueOf(); + var processSuccess = function (query, data, sql) { + query.rawDataList = typeof data === 'string' ? [{ cnt: Container.convertTo(data, $data.Integer) }] : data; + if (sql.withInlineCount && typeof data === 'object' && (typeof data[countProperty] !== 'undefined' || ('d' in data && typeof data.d[countProperty] !== 'undefined'))) { + query.__count = new Number(typeof data[countProperty] !== 'undefined' ? data[countProperty] : data.d[countProperty]).valueOf(); + } + } + + if (sql.isBatchExecuteQuery) { + query.rawDataList = sql.subQueries; + for (var i = 0; i < data.__batchResponses.length; i++) { + var resp = data.__batchResponses[i]; + + if (!resp.data) { + if (resp.body) { + resp.data = JSON.parse(resp.body); + } else { + callBack.error(that.parseError(resp, arguments)); + return; + } + } + + processSuccess(sql.subQueries[i], resp.data, sql.subQueries[i]._getComplitedData()); + } + } else { + processSuccess(query, data, sql); } callBack.success(query); @@ -196,10 +233,15 @@ $C('$data.storageProviders.oData.oDataProvider', $data.StorageProviderBase, null }, function (error) { callBack.error(that.parseError(error, arguments)); - } + }, + sql.isBatchExecuteQuery ? this.oData.batchHandler : undefined ]; - if (this.providerConfiguration.dataServiceVersion) { + if (this.providerConfiguration.maxDataServiceVersion && this.providerConfiguration.maxDataServiceVersion !== "4.0") { + requestData[0].headers.MaxDataServiceVersion = this.providerConfiguration.maxDataServiceVersion; + } + + if (this.providerConfiguration.dataServiceVersion && this.providerConfiguration.maxDataServiceVersion !== "4.0") { requestData[0].headers.DataServiceVersion = this.providerConfiguration.dataServiceVersion; } @@ -219,7 +261,7 @@ $C('$data.storageProviders.oData.oDataProvider', $data.StorageProviderBase, null this.context.prepareRequest.call(this, requestData); //$data.ajax(requestData); //OData.request(requestData, requestData.success, requestData.error); - OData.request.apply(this, requestData); + this.oData.request.apply(this, requestData); }, _compile: function (queryable, params) { var compiler = new $data.storageProviders.oData.oDataCompiler(); @@ -258,10 +300,12 @@ $C('$data.storageProviders.oData.oDataProvider', $data.StorageProviderBase, null request = { requestUri: this.providerConfiguration.oDataServiceHost + '/', headers: { - MaxDataServiceVersion: this.providerConfiguration.maxDataServiceVersion } }; - if (this.providerConfiguration.dataServiceVersion) { + if (this.providerConfiguration.maxDataServiceVersion && this.providerConfiguration.maxDataServiceVersion !== "4.0") { + request.headers.MaxDataServiceVersion = this.providerConfiguration.maxDataServiceVersion; + } + if (this.providerConfiguration.dataServiceVersion && this.providerConfiguration.maxDataServiceVersion !== "4.0") { request.headers.DataServiceVersion = this.providerConfiguration.dataServiceVersion; } if (typeof this.providerConfiguration.useJsonLight !== 'undefined') { @@ -328,7 +372,7 @@ $C('$data.storageProviders.oData.oDataProvider', $data.StorageProviderBase, null //} this.context.prepareRequest.call(this, requestData); - OData.request.apply(this, requestData); + this.oData.request.apply(this, requestData); }, _saveBatch: function (independentBlocks, index2, callBack) { var batchRequests = []; @@ -338,9 +382,11 @@ $C('$data.storageProviders.oData.oDataProvider', $data.StorageProviderBase, null convertedItem.push(independentBlocks[index][i].data); var request = {}; request.headers = { - "Content-Id": convertedItem.length, - MaxDataServiceVersion: this.providerConfiguration.maxDataServiceVersion + "Content-Id": convertedItem.length }; + if (this.providerConfiguration.maxDataServiceVersion != "4.0") { + request.headers.MaxDataServiceVersion = this.providerConfiguration.maxDataServiceVersion; + } switch (independentBlocks[index][i].data.entityState) { case $data.EntityState.Unchanged: continue; break; case $data.EntityState.Added: @@ -364,7 +410,10 @@ $C('$data.storageProviders.oData.oDataProvider', $data.StorageProviderBase, null default: Guard.raise(new Exception("Not supported Entity state")); } - if (this.providerConfiguration.dataServiceVersion) { + if (this.providerConfiguration.maxDataServiceVersion && this.providerConfiguration.maxDataServiceVersion !== "4.0") { + request.headers.MaxDataServiceVersion = this.providerConfiguration.maxDataServiceVersion; + } + if (this.providerConfiguration.dataServiceVersion && this.providerConfiguration.maxDataServiceVersion !== "4.0") { request.headers.DataServiceVersion = this.providerConfiguration.dataServiceVersion; } batchRequests.push(request); @@ -379,7 +428,6 @@ $C('$data.storageProviders.oData.oDataProvider', $data.StorageProviderBase, null __batchRequests: [{ __changeRequests: batchRequests }] }, headers: { - MaxDataServiceVersion: this.providerConfiguration.maxDataServiceVersion } }, function (data, response) { if (response.statusCode == 202) { @@ -419,9 +467,12 @@ $C('$data.storageProviders.oData.oDataProvider', $data.StorageProviderBase, null }, function (e) { callBack.error(that.parseError(e)); - }, OData.batchHandler]; + }, this.oData.batchHandler]; - if (this.providerConfiguration.dataServiceVersion) { + if (this.providerConfiguration.maxDataServiceVersion && this.providerConfiguration.maxDataServiceVersion != "4.0") { + requestData[0].headers.MaxDataServiceVersion = this.providerConfiguration.maxDataServiceVersion; + } + if (this.providerConfiguration.dataServiceVersion && this.providerConfiguration.maxDataServiceVersion != "4.0") { requestData[0].headers.DataServiceVersion = this.providerConfiguration.dataServiceVersion; } if (typeof this.providerConfiguration.useJsonLight !== 'undefined') { @@ -435,7 +486,7 @@ $C('$data.storageProviders.oData.oDataProvider', $data.StorageProviderBase, null //} this.context.prepareRequest.call(this, requestData); - OData.request.apply(this, requestData); + this.oData.request.apply(this, requestData); }, reload_fromResponse: function (item, data, response) { var that = this; @@ -483,16 +534,43 @@ $C('$data.storageProviders.oData.oDataProvider', $data.StorageProviderBase, null }, this); }, - save_getInitData: function (item, convertedItems) { + //save_getInitData: function (item, convertedItems) { + // var self = this; + // item.physicalData = this.context._storageModel.getStorageModel(item.data.getType()).PhysicalType.convertTo(item.data, convertedItems); + // var serializableObject = {} + // item.physicalData.getType().memberDefinitions.asArray().forEach(function (memdef) { + // if (memdef.kind == $data.MemberTypes.navProperty || memdef.kind == $data.MemberTypes.complexProperty || (memdef.kind == $data.MemberTypes.property && !memdef.notMapped)) { + // if (typeof memdef.concurrencyMode === 'undefined' && (memdef.key === true || item.data.entityState === $data.EntityState.Added || item.data.changedProperties.some(function (def) { return def.name === memdef.name; }))) { + // var typeName = Container.resolveName(memdef.type); + // var converter = self.fieldConverter.toDb[typeName]; + // serializableObject[memdef.name] = converter ? converter(item.physicalData[memdef.name]) : item.physicalData[memdef.name]; + // } + // } + // }, this); + // return serializableObject; + //}, + save_getInitData: function (item, convertedItems, isComplex, isDeep) { var self = this; - item.physicalData = this.context._storageModel.getStorageModel(item.data.getType()).PhysicalType.convertTo(item.data, convertedItems); + if (!isComplex) { + item.physicalData = this.context._storageModel.getStorageModel(item.data.getType()).PhysicalType.convertTo(item.data, convertedItems); + } else { + item.physicalData = item.data; + } var serializableObject = {} item.physicalData.getType().memberDefinitions.asArray().forEach(function (memdef) { - if (memdef.kind == $data.MemberTypes.navProperty || memdef.kind == $data.MemberTypes.complexProperty || (memdef.kind == $data.MemberTypes.property && !memdef.notMapped)) { - if (typeof memdef.concurrencyMode === 'undefined' && (memdef.key === true || item.data.entityState === $data.EntityState.Added || item.data.changedProperties.some(function (def) { return def.name === memdef.name; }))) { - var typeName = Container.resolveName(memdef.type); - var converter = self.fieldConverter.toDb[typeName]; - serializableObject[memdef.name] = converter ? converter(item.physicalData[memdef.name]) : item.physicalData[memdef.name]; + if (memdef.kind == $data.MemberTypes.complexProperty && item.physicalData[memdef.name]) { + serializableObject[memdef.name] = self.save_getInitData({ data: item.physicalData[memdef.name] }, convertedItems, true, true); + } + else if (memdef.kind == $data.MemberTypes.navProperty || (memdef.kind == $data.MemberTypes.property && !memdef.notMapped)) { + if (isDeep || typeof memdef.concurrencyMode === 'undefined' && (memdef.key === true || item.data.entityState === $data.EntityState.Added || (item.data.changedProperties && item.data.changedProperties.some(function (def) { return def.name === memdef.name; })))) { + + if (memdef.kind == $data.MemberTypes.navProperty && item.physicalData[memdef.name] && this.providerConfiguration.maxDataServiceVersion === "4.0") { + serializableObject[memdef.name + "@odata.bind"] = item.physicalData[memdef.name].__metadata.uri; + } else { + var typeName = Container.resolveName(memdef.type); + var converter = self.fieldConverter.toDb[typeName]; + serializableObject[memdef.name] = converter ? converter(item.physicalData[memdef.name]) : item.physicalData[memdef.name]; + } } } }, this); @@ -747,6 +825,14 @@ $C('$data.storageProviders.oData.oDataProvider', $data.StorageProviderBase, null enumerable: true, writable: true }, + supportedContextOperation: { + value: { + batchExecuteQuery: true + }, + enumerable: true, + writable: true + }, + fieldConverter: { value: $data.oDataConverter }, resolveTypeOperations: function (operation, expression, frameType) { var memDef = expression.entityType.getMemberDefinition(operation); diff --git a/UnitTests/oDataProviderTests.js b/UnitTests/oDataProviderTests.js index a29628dd..828bf3a1 100644 --- a/UnitTests/oDataProviderTests.js +++ b/UnitTests/oDataProviderTests.js @@ -21,7 +21,7 @@ equal(q.queryText, "/Users", "Invalid query string"); var expectedObject = { $type: $data.Array, - $selector: ['json:d.results', 'json:d', 'json:results'], + $selector: ['json:d.results', 'json:d', 'json:results', 'json:value'], $item: { $type: $news.Types.User, $keys: ['Id'], @@ -42,7 +42,7 @@ equal(q.queryText, "/Users?$orderby=LoginName", "Invalid query string"); var expectedObject = { $type: $data.Array, - $selector: ['json:d.results', 'json:d', 'json:results'], + $selector: ['json:d.results', 'json:d', 'json:results', 'json:value'], $item: { $type: $news.Types.User, $keys: ['Id'], @@ -64,7 +64,7 @@ equal(q.queryText, "/Users?$orderby=Email,Id,LoginName", "Invalid query string"); var expectedObject = { $type: $data.Array, - $selector: ['json:d.results', 'json:d', 'json:results'], + $selector: ['json:d.results', 'json:d', 'json:results', 'json:value'], $item: { $type: $news.Types.User, $keys: ['Id'], @@ -86,7 +86,7 @@ equal(q.queryText, "/Users?$orderby=Email desc,Id desc,LoginName desc", "Invalid query string"); var expectedObject = { $type: $data.Array, - $selector: ['json:d.results', 'json:d', 'json:results'], + $selector: ['json:d.results', 'json:d', 'json:results', 'json:value'], $item: { $type: $news.Types.User, $keys: ['Id'], @@ -108,7 +108,7 @@ equal(q.queryText, "/Users?$orderby=Email,Id desc,LoginName", "Invalid query string"); var expectedObject = { $type: $data.Array, - $selector: ['json:d.results', 'json:d', 'json:results'], + $selector: ['json:d.results', 'json:d', 'json:results', 'json:value'], $item: { $type: $news.Types.User, $keys: ['Id'], @@ -130,7 +130,7 @@ equal(q.queryText, "/Users?$orderby=Email desc,Id,LoginName desc", "Invalid query string"); var expectedObject = { $type: $data.Array, - $selector: ['json:d.results', 'json:d', 'json:results'], + $selector: ['json:d.results', 'json:d', 'json:results', 'json:value'], $item: { $type: $news.Types.User, $keys: ['Id'], @@ -151,7 +151,7 @@ equal(q.queryText, "/Users?$top=10", "Invalid query string"); var expectedObject = { $type: $data.Array, - $selector: ['json:d.results', 'json:d', 'json:results'], + $selector: ['json:d.results', 'json:d', 'json:results', 'json:value'], $item: { $type: $news.Types.User, $keys: ['Id'], @@ -171,7 +171,7 @@ equal(q.queryText, "/Users?$skip=11", "Invalid query string"); var expectedObject = { $type: $data.Array, - $selector: ['json:d.results', 'json:d', 'json:results'], + $selector: ['json:d.results', 'json:d', 'json:results', 'json:value'], $item: { $type: $news.Types.User, $keys: ['Id'], @@ -191,7 +191,7 @@ equal(q.queryText, "/Users?$skip=11&$top=10", "Invalid query string"); var expectedObject = { $type: $data.Array, - $selector: ['json:d.results', 'json:d', 'json:results'], + $selector: ['json:d.results', 'json:d', 'json:results', 'json:value'], $item: { $type: $news.Types.User, $keys: ['Id'], @@ -212,7 +212,7 @@ equal(q.queryText, "/Users?$filter=(LoginName eq 'alma')", "Invalid query string"); var expectedObject = { $type: $data.Array, - $selector: ['json:d.results', 'json:d', 'json:results'], + $selector: ['json:d.results', 'json:d', 'json:results', 'json:value'], $item: { $type: $news.Types.User, $keys: ['Id'], @@ -232,7 +232,7 @@ equal(q.queryText, "/Users?$filter=(LoginName eq 'alma')", "Invalid query string"); var expectedObject = { $type: $data.Array, - $selector: ['json:d.results', 'json:d', 'json:results'], + $selector: ['json:d.results', 'json:d', 'json:results', 'json:value'], $item: { $type: $news.Types.User, $keys: ['Id'], @@ -252,7 +252,7 @@ equal(q.queryText, "/Users?$filter=(Id eq 15)", "Invalid query string"); var expectedObject = { $type: $data.Array, - $selector: ['json:d.results', 'json:d', 'json:results'], + $selector: ['json:d.results', 'json:d', 'json:results', 'json:value'], $item: { $type: $news.Types.User, $keys: ['Id'], @@ -272,7 +272,7 @@ equal(q.queryText, "/Users?$filter=(Id eq 15)", "Invalid query string"); var expectedObject = { $type: $data.Array, - $selector: ['json:d.results', 'json:d', 'json:results'], + $selector: ['json:d.results', 'json:d', 'json:results', 'json:value'], $item: { $type: $news.Types.User, $keys: ['Id'], @@ -302,7 +302,7 @@ equal(q.queryText, "/Articles?$filter=(CreateDate gt datetime'" + dateString + "')", "Invalid query string"); var expectedObject = { $type: $data.Array, - $selector: ['json:d.results', 'json:d', 'json:results'], + $selector: ['json:d.results', 'json:d', 'json:results', 'json:value'], $item: { $type: $news.Types.Article, $keys: ['Id'], @@ -327,7 +327,7 @@ equal(q.queryText, "/TestTable?$filter=(n0 gt 4.5)", "Invalid query string"); var expectedObject = { $type: $data.Array, - $selector: ['json:d.results', 'json:d', 'json:results'], + $selector: ['json:d.results', 'json:d', 'json:results', 'json:value'], $item: { $type: $news.Types.TestItem, $keys: ['Id'], @@ -351,7 +351,7 @@ equal(q.queryText, "/TestTable?$filter=(n0 gt 4.5)", "Invalid query string"); var expectedObject = { $type: $data.Array, - $selector: ['json:d.results', 'json:d', 'json:results'], + $selector: ['json:d.results', 'json:d', 'json:results', 'json:value'], $item: { $type: $news.Types.TestItem, $keys: ['Id'], @@ -375,7 +375,7 @@ equal(q.queryText, "/TestTable?$filter=(b0 eq true)", "Invalid query string"); var expectedObject = { $type: $data.Array, - $selector: ['json:d.results', 'json:d', 'json:results'], + $selector: ['json:d.results', 'json:d', 'json:results', 'json:value'], $item: { $type: $news.Types.TestItem, $keys: ['Id'], @@ -399,7 +399,7 @@ equal(q.queryText, "/TestTable?$filter=(b0 eq true)", "Invalid query string"); var expectedObject = { $type: $data.Array, - $selector: ['json:d.results', 'json:d', 'json:results'], + $selector: ['json:d.results', 'json:d', 'json:results', 'json:value'], $item: { $type: $news.Types.TestItem, $keys: ['Id'], @@ -423,7 +423,7 @@ equal(q.queryText, "/Users?$filter=((LoginName eq 'alma') and (Email eq 'email@company.com'))", "Invalid query string"); var expectedObject = { $type: $data.Array, - $selector: ['json:d.results', 'json:d', 'json:results'], + $selector: ['json:d.results', 'json:d', 'json:results', 'json:value'], $item: { $type: $news.Types.User, $keys: ['Id'], @@ -444,7 +444,7 @@ equal(q.queryText, "/Articles?$filter=((Title eq 'alma') and (CreateDate eq datetime'" + dateString + "'))", "Invalid query string"); var expectedObject = { $type: $data.Array, - $selector: ['json:d.results', 'json:d', 'json:results'], + $selector: ['json:d.results', 'json:d', 'json:results', 'json:value'], $item: { $type: $news.Types.Article, $keys: ['Id'], @@ -473,7 +473,7 @@ equal(q.queryText, "/Articles?$filter=((Title eq 'alma') and (CreateDate eq datetime'" + dateString + "'))&$orderby=Title", "Invalid query string"); var expectedObject = { $type: $data.Array, - $selector: ['json:d.results', 'json:d', 'json:results'], + $selector: ['json:d.results', 'json:d', 'json:results', 'json:value'], $item: { $type: $news.Types.Article, $keys: ['Id'], @@ -500,7 +500,7 @@ equal(q.queryText, "/Users?$filter=substringof('aj',LoginName)", "Invalid query string"); var expectedObject = { $type: $data.Array, - $selector: ['json:d.results', 'json:d', 'json:results'], + $selector: ['json:d.results', 'json:d', 'json:results', 'json:value'], $item: { $type: $news.Types.User, $keys: ['Id'], @@ -520,7 +520,7 @@ equal(q.queryText, "/Users?$filter=startswith(LoginName,'aj')", "Invalid query string"); var expectedObject = { $type: $data.Array, - $selector: ['json:d.results', 'json:d', 'json:results'], + $selector: ['json:d.results', 'json:d', 'json:results', 'json:value'], $item: { $type: $news.Types.User, $keys: ['Id'], @@ -540,7 +540,7 @@ equal(q.queryText, "/Users?$filter=endswith(LoginName,'aj')", "Invalid query string"); var expectedObject = { $type: $data.Array, - $selector: ['json:d.results', 'json:d', 'json:results'], + $selector: ['json:d.results', 'json:d', 'json:results', 'json:value'], $item: { $type: $news.Types.User, $keys: ['Id'], @@ -562,7 +562,7 @@ equal(q.queryText, "/Articles?$expand=Category", "Invalid query string"); var expectedObject = { $type: $data.Array, - $selector: ['json:d.results', 'json:d', 'json:results'], + $selector: ['json:d.results', 'json:d', 'json:results', 'json:value'], $item: { $type: $news.Types.Article, $keys: ['Id'], @@ -594,7 +594,7 @@ equal(q.queryText, "/Articles?$expand=Category,Author", "Invalid query string"); var expectedObject = { $type: $data.Array, - $selector: ['json:d.results', 'json:d', 'json:results'], + $selector: ['json:d.results', 'json:d', 'json:results', 'json:value'], $item: { $type: $news.Types.Article, $keys: ['Id'], @@ -634,7 +634,7 @@ equal(q.queryText, "/Articles?$expand=Category,Author/Profile", "Invalid query string"); var expectedObject = { $type: $data.Array, - $selector: ['json:d.results', 'json:d', 'json:results'], + $selector: ['json:d.results', 'json:d', 'json:results', 'json:value'], $item: { $type: $news.Types.Article, $keys: ['Id'], @@ -693,7 +693,7 @@ equal(q.queryText, "/Categories?$expand=Articles,Articles/Author", "Invalid query string"); var expectedObject = { $type: $data.Array, - $selector: ['json:d.results', 'json:d', 'json:results'], + $selector: ['json:d.results', 'json:d', 'json:results', 'json:value'], $item: { $type: $news.Types.Category, $keys: ['Id'], @@ -738,7 +738,7 @@ equal(q.queryText, "/Articles?$filter=(Category/Title eq 'Sport')", "Invalid query string"); var expectedObject = { $type: $data.Array, - $selector: ['json:d.results', 'json:d', 'json:results'], + $selector: ['json:d.results', 'json:d', 'json:results', 'json:value'], $item: { $type: $news.Types.Article, $keys: ['Id'], @@ -764,7 +764,7 @@ equal(q.queryText, "/Articles?$filter=(Author/Profile/Bio eq 'Bio1')", "Invalid query string"); var expectedObject = { $type: $data.Array, - $selector: ['json:d.results', 'json:d', 'json:results'], + $selector: ['json:d.results', 'json:d', 'json:results', 'json:value'], $item: { $type: $news.Types.Article, $keys: ['Id'], @@ -789,7 +789,7 @@ equal(q.queryText, "/Users?$filter=(Profile/Bio eq 'Bio2')", "Invalid query string"); var expectedObject = { $type: $data.Array, - $selector: ['json:d.results', 'json:d', 'json:results'], + $selector: ['json:d.results', 'json:d', 'json:results', 'json:value'], $item: { $type: $news.Types.User, $keys: ['Id'], @@ -820,7 +820,7 @@ var expectedObject = { $type: $data.Array, - $selector: ['json:d.results', 'json:d', 'json:results'], + $selector: ['json:d.results', 'json:d', 'json:results', 'json:value'], $item: { $type: $data.Integer, $source: 'Id' @@ -838,7 +838,7 @@ var expectedObject = { $type: $data.Array, - $selector: ['json:d.results', 'json:d', 'json:results'], + $selector: ['json:d.results', 'json:d', 'json:results', 'json:value'], $item: { $selector: ['json:Author.Profile.results', 'json:Author.Profile'], $type: $data.String, @@ -874,7 +874,7 @@ var expectedObject = { $type: $data.Array, - $selector: ['json:d.results', 'json:d', 'json:results'], + $selector: ['json:d.results', 'json:d', 'json:results', 'json:value'], $item: { $type: $data.Object, t: { $type: $data.String, $source: 'Title' }, @@ -893,7 +893,7 @@ var expectedObject = { $type: $data.Array, - $selector: ['json:d.results', 'json:d', 'json:results'], + $selector: ['json:d.results', 'json:d', 'json:results', 'json:value'], $item: { $type: $data.Object, t: { $type: $data.String, $selector: ['json:Author.Profile.results', 'json:Author.Profile'], $source: 'FullName' }, @@ -912,7 +912,7 @@ var expectedObject = { $type: $data.Array, - $selector: ['json:d.results', 'json:d', 'json:results'], + $selector: ['json:d.results', 'json:d', 'json:results', 'json:value'], $item: { $type: $data.Object, t: { $type: $data.String, $selector: ['json:Author.Profile.results', 'json:Author.Profile'], $source: 'FullName' }, @@ -940,7 +940,7 @@ var expectedObject = { $type: $data.Array, - $selector: ['json:d.results', 'json:d', 'json:results'], + $selector: ['json:d.results', 'json:d', 'json:results', 'json:value'], $item: { $type: $data.Object, t: { $type: $data.String, $selector: ['json:Author.Profile.results', 'json:Author.Profile'], $source: 'FullName' }, @@ -972,7 +972,7 @@ var expectedObject = { $type: $data.Array, - $selector: ['json:d.results', 'json:d', 'json:results'], + $selector: ['json:d.results', 'json:d', 'json:results', 'json:value'], $item: { $type: $data.Object, t: { $type: $data.String, $selector: ['json:Author.Profile.results', 'json:Author.Profile'], $source: 'FullName' }, @@ -1008,7 +1008,7 @@ var expectedObject = { $type: $data.Array, - $selector: ['json:d.results', 'json:d', 'json:results'], + $selector: ['json:d.results', 'json:d', 'json:results', 'json:value'], $item: { $type: $data.Object, t: { @@ -1033,7 +1033,7 @@ var expectedObject = { $type: $data.Array, - $selector: ['json:d.results', 'json:d', 'json:results'], + $selector: ['json:d.results', 'json:d', 'json:results', 'json:value'], $item: { $type: $data.Object, t: { @@ -1070,7 +1070,7 @@ var expectedObject = { $type: $data.Array, - $selector: ['json:d.results', 'json:d', 'json:results'], + $selector: ['json:d.results', 'json:d', 'json:results', 'json:value'], $item: { $type: $data.Object, t: { @@ -1125,7 +1125,7 @@ var expectedObject = { $type: $data.Array, - $selector: ['json:d.results', 'json:d', 'json:results'], + $selector: ['json:d.results', 'json:d', 'json:results', 'json:value'], $item: { $type: $news.Types.UserProfile, $selector: ['json:Reviewer.Profile.results', 'json:Reviewer.Profile'], diff --git a/Web.config b/Web.config index 0089aec8..15a7342b 100644 --- a/Web.config +++ b/Web.config @@ -83,8 +83,6 @@ - - @@ -163,4 +161,4 @@ - \ No newline at end of file + diff --git a/npm/jaydata/README.md b/npm/jaydata/README.md index 37e9ec31..5cb921ef 100644 --- a/npm/jaydata/README.md +++ b/npm/jaydata/README.md @@ -1,7 +1,6 @@ -#JayData -JayData is a unified data access library for JavaScript to CRUD + Query data from different sources like WebAPI, OData, MongoDB, WebSQL, SQLite, HTML5 localStorage, Facebook or YQL. The library can be integrated with KendoUI, Knockout.js or Sencha Touch 2 and can be used on Node.js as well. +jaydata-reso +======= + +jaydata-reso is a fork of the Jaydata package which is a unified data access library for JavaScript to CRUD + Query data from different sources. This fork works with MongoDB and libRETS. -See it in action in this [6 minutes video](http://www.youtube.com/watch?v=LlJHgj1y0CU) -KendoUI examples: [JayData example site](http://jaydata.org/examples/?tags=KendoUI) -Examples for map integration [JayData example site](http://jaydata.org/examples/?tags=Geo) diff --git a/npm/jaydata/lib/node.js b/npm/jaydata/lib/node.js index 25412e2c..a3788565 100644 --- a/npm/jaydata/lib/node.js +++ b/npm/jaydata/lib/node.js @@ -16,6 +16,10 @@ try{ $data.mongoDBDriver = require('mongodb'); }catch(e){} +try{ + $data.libRETSDriver = require('jaydata-librets'); +}catch(e){} + (function(global){ if (typeof window === "undefined") { window = this; @@ -43,4 +47,4 @@ try{ }catch(e){} if (typeof atob === 'undefined') atob = window.atob = function (buffer) { return new Buffer(buffer, 'base64').toString('binary'); }; -if (typeof btoa === 'undefined') btoa = window.btoa = function (buffer) { return new Buffer(buffer, 'binary').toString('base64'); }; \ No newline at end of file +if (typeof btoa === 'undefined') btoa = window.btoa = function (buffer) { return new Buffer(buffer, 'binary').toString('base64'); }; diff --git a/npm/jaydata/package.json b/npm/jaydata/package.json index 5a924c2a..93b9f2a7 100755 --- a/npm/jaydata/package.json +++ b/npm/jaydata/package.json @@ -1,55 +1,40 @@ { - "name": "jaydata", - "description": "Cross-platform HTML5 data-management, JavaScript Language Query (JSLQ) support for OData, SQLite, WebSQL, IndexedDB, YQL and Facebook (packaged for Node.JS)", + "name": "jaydata-reso", + "description": "Jaydata fork for RESO Web API", "keywords": [ - "HTML5 data management", - "JavaScript", - "JavaScript Language Query", - "JSLQ", "OData", - "SQLite", - "WebSQL", - "IndexedDB", - "YQL", - "Facebook", "cross-platform", - "iPhone", - "Android" + "librets", + "mongodb", + "reso" ], - "version": "1.1.0", - "homepage": "http://jaydata.org", + "version": "2.0.3", + "homepage": "http://blogs.crt.realtors.org", "author": { - "name": "JayData", - "url": "http://jaydata.org" + "name": "Center for REALTOR Technology", + "url": "http://blogs.crt.realtors.org" }, "dependencies": {}, "contributors": [ - {"name": "DĂĄniel JĂłzsef"}, - {"name": "Hajnalka Battancs"}, - {"name": "JĂĄnos Roden"}, - {"name": "LĂĄszlĂł HorvĂĄth"}, - {"name": "PĂŠter Zentai"}, - {"name": "PĂŠter Nochta"}, - {"name": "RĂłbert BĂłnay"}, - {"name": "Szabolcs Czinege"}, - {"name": "Viktor Borza"}, - {"name": "Viktor LĂĄzĂĄr"}, - {"name": "ZoltĂĄn Gyebrovszki"} + {"name": "Original JayData Development Team"}, + {"name": "Mark Lesswing"} ], "repository": { "type": "git", - "url": "git://github.com/jaydata/jaydata.git" + "url": "https://github.com/mlesswing/jaydata.git" }, "engines": { - "node": ">=0.6.0" + "node": ">=0.10.0" }, "licenses": [ { "type": "MIT", - "url": "http://jaydata.org/licensing" + "url": "http://blogs.crt.realtors.org" }, { "type": "GPLv2", - "url": "http://jaydata.org/licensing" + "url": "http://blogs.crt.realtors.org" } - ]} + ], + "readmeFilename": "README.md" +} diff --git a/npm/provider/README.md b/npm/provider/README.md deleted file mode 100644 index 37e9ec31..00000000 --- a/npm/provider/README.md +++ /dev/null @@ -1,7 +0,0 @@ -#JayData - -JayData is a unified data access library for JavaScript to CRUD + Query data from different sources like WebAPI, OData, MongoDB, WebSQL, SQLite, HTML5 localStorage, Facebook or YQL. The library can be integrated with KendoUI, Knockout.js or Sencha Touch 2 and can be used on Node.js as well. - -See it in action in this [6 minutes video](http://www.youtube.com/watch?v=LlJHgj1y0CU) -KendoUI examples: [JayData example site](http://jaydata.org/examples/?tags=KendoUI) -Examples for map integration [JayData example site](http://jaydata.org/examples/?tags=Geo) diff --git a/npm/provider/index.js b/npm/provider/index.js deleted file mode 100644 index bf4d18ac..00000000 --- a/npm/provider/index.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('./lib/index.js'); diff --git a/npm/provider/lib/index.js b/npm/provider/lib/index.js deleted file mode 100644 index e69de29b..00000000 diff --git a/npm/provider/package.json b/npm/provider/package.json deleted file mode 100755 index 82d5ed70..00000000 --- a/npm/provider/package.json +++ /dev/null @@ -1,55 +0,0 @@ -{ - "name": "jaydata", - "description": "Cross-platform HTML5 data-management, JavaScript Language Query (JSLQ) support for OData, SQLite, WebSQL, IndexedDB, YQL and Facebook (packaged for Node.JS)", - "keywords": [ - "HTML5 data management", - "JavaScript", - "JavaScript Language Query", - "JSLQ", - "OData", - "SQLite", - "WebSQL", - "IndexedDB", - "YQL", - "Facebook", - "cross-platform", - "iPhone", - "Android" - ], - "version": "1.1.0", - "homepage": "http://jaydata.org", - "author": { - "name": "JayData", - "url": "http://jaydata.org" - }, - "dependencies": { "jaydata-core": "1.1.0" }, - "contributors": [ - {"name": "DĂĄniel JĂłzsef"}, - {"name": "Hajnalka Battancs"}, - {"name": "JĂĄnos Roden"}, - {"name": "LĂĄszlĂł HorvĂĄth"}, - {"name": "PĂŠter Zentai"}, - {"name": "PĂŠter Nochta"}, - {"name": "RĂłbert BĂłnay"}, - {"name": "Szabolcs Czinege"}, - {"name": "Viktor Borza"}, - {"name": "Viktor LĂĄzĂĄr"}, - {"name": "ZoltĂĄn Gyebrovszki"} - ], - "repository": { - "type": "git", - "url": "git://github.com/jaydata/jaydata.git" - }, - "engines": { - "node": ">=0.6.0" - }, - "licenses": [ - { - "type": "MIT", - "url": "http://jaydata.org/licensing" - }, - { - "type": "GPLv2", - "url": "http://jaydata.org/licensing" - } - ]} diff --git a/oDataParser/RequestParser.js b/oDataParser/RequestParser.js index cc211450..c30984bb 100644 --- a/oDataParser/RequestParser.js +++ b/oDataParser/RequestParser.js @@ -566,7 +566,9 @@ var srcIndex = this.lexer.srcIndex; if (token.tokenType != TokenType.WORD) return; - var name = token.value; + + var name = token.value.toLowerCase(); + for (var i = 0; i < this.reservedNs.length; i++) { if (this.reservedNs[i] == name) { diff --git a/odata-server/index.js b/odata-server/index.js index c676e0f4..2a9cef98 100644 --- a/odata-server/index.js +++ b/odata-server/index.js @@ -3,6 +3,7 @@ window.DOMParser = require('xmldom').DOMParser; $data.ODataServer = function(type, db){ var connect = require('connect'); + var basicAuth = require('basic-auth-connect'); var domain = require('domain'); var config = typeof type === 'object' ? type : {}; @@ -20,11 +21,11 @@ $data.ODataServer = function(type, db){ var basicAuthFn = function(req, res, next){ if (!config.basicAuth && !config.rootAuth) return next(); - var callback = config.rootAuth ? function(){ connect.basicAuth(config.rootAuth)(req, res, next); } : next; + var callback = config.rootAuth ? function(){ basicAuth(config.rootAuth)(req, res, next); } : next; if (typeof config.basicAuth == 'function'){ - connect.basicAuth(config.basicAuth)(req, res, callback); + basicAuth(config.basicAuth)(req, res, callback); }else if (typeof config.basicAuth == 'object' && config.basicAuth.username && config.basicAuth.password){ - connect.basicAuth(config.basicAuth.username, config.basicAuth.password)(req, res, callback); + basicAuth(config.basicAuth.username, config.basicAuth.password)(req, res, callback); }else callback(); }; @@ -42,23 +43,26 @@ $data.ODataServer = function(type, db){ } }else next(); }; - + + var qs = require('qs'); var queryFn = function(req, res, next){ if (!req.query){ - connect.query()(req, res, next); + req.query = qs.parse(req._parsedUrl.query); + next(); }else next(); }; - + + var bodyParser = require('body-parser'); var bodyFn = function(req, res, next){ if (!req.body){ - connect.bodyParser()(req, res, next); + bodyParser()(req, res, next); }else next(); }; var simpleBodyFn = function(req, res, next){ $data.JayService.OData.Utils.simpleBodyReader()(req, res, next); }; - + var errorFn = function(req, res, next, callback){ var reqd = domain.create(); reqd.add(req); @@ -78,10 +82,11 @@ $data.ODataServer = function(type, db){ }); }; + var errorHandler = require('errorhandler'); var errorHandlerFn = function(err, req, res, next){ if (config.errorHandler){ - connect.errorHandler.title = typeof config.errorHandler == 'string' ? config.errorHandler : config.provider.databaseName; - connect.errorHandler()(err, req, res, next); + errorHandler.title = typeof config.errorHandler == 'string' ? config.errorHandler : config.provider.databaseName; + errorHandler()(err, req, res, next); }else next(err); }; @@ -95,7 +100,15 @@ $data.ODataServer = function(type, db){ }); config.provider.checkPermission = req.checkPermission; } - + + var schema = 'http'; + if (req && req.headers) { + if (req.connection.encrypted || req.headers['X-Forwarded-Protocol'] === 'https' || req.headers['x-forwarded-protocol'] === 'https') + schema += 's'; + + req.fullRoute = (req.baseRoute || (schema + '://' + req.headers.host)) + (req.baseUrl || (req.originalUrl.replace(req.url, ''))); + } + basicAuthFn(req, res, function(){ config.provider.user = config.user = req.user || req.remoteUser || config.user || config.provider.user || 'anonymous'; corsFn(req, res, function(){ diff --git a/odata-server/newsreader.js b/odata-server/newsreader.js index df5a738d..997da6aa 100644 --- a/odata-server/newsreader.js +++ b/odata-server/newsreader.js @@ -1,5 +1,4 @@ require('odata-server'); -//$data.createODataServer(require('./newsreader/context.js'), '/newsreader.svc', 52999, 'localhost');*/ var contextType = require('./newsreader/context.js'); var context = new $news.Types.NewsContext({ name: 'mongoDB', databaseName: 'newsreader', dbCreation: $data.storageProviders.DbCreationType.DropAllExistingTables }); @@ -8,19 +7,24 @@ context.onReady(function(db){ console.log('Test data upload successful. ', count, 'items inserted.'); console.log('Starting NewsReader OData server.'); + /*$data.createODataServer({ + type: contextType, + database: 'newsreader', + }, '/newsreader.svc', 52999, 'localhost');*/ + var connect = require('connect'); var app = connect(); - app.use(connect.basicAuth(function(username, password){ - if (username == 'admin'){ - return password == 'admin'; - }else return true; - })); app.use('/newsreader.svc', $data.ODataServer({ type: contextType, CORS: true, database: 'newsreader', responseLimit: 100, + basicAuth: function(username, password){ + if (username == 'admin'){ + return password == 'admin'; + }else return true; + }, checkPermission: function(access, user, entitySets, callback){ if (access & $data.Access.Create){ if (user == 'admin') callback.success(); diff --git a/odata-server/package.json b/odata-server/package.json index a36ab8b8..dc6f1104 100644 --- a/odata-server/package.json +++ b/odata-server/package.json @@ -12,25 +12,60 @@ "Facebook", "cross-platform" ], - "version": "1.3.1", + "version": "1.3.7", "homepage": "http://jaydata.org", "author": { "name": "JayData", "url": "http://jaydata.org" }, - "dependencies": {"datajs": "1.0.3", "q": "0.8.5", "qs": "0.5.0", "xmldom": "0.1.11", "url": ">0.0.1", "connect": ">2.0.0", "mongodb": ">1.0.0", "jaydata": ">=1.3.1" }, + "dependencies": { + "datajs": "1.0.3", + "q": "0.8.5", + "qs": "0.5.0", + "xmldom": "0.1.11", + "url": ">0.0.1", + "connect": "3.3.2", + "basic-auth-connect": "1.0.0", + "qs": "0.5.0", + "body-parser": "1.9.2", + "errorhandler": "1.2.2", + "mongodb": ">1.0.0", + "jaydata": "1.3.6" + }, "contributors": [ - {"name": "DĂĄniel JĂłzsef"}, - {"name": "Hajnalka Battancs"}, - {"name": "JĂĄnos Roden"}, - {"name": "LĂĄszlĂł HorvĂĄth"}, - {"name": "PĂŠter Zentai"}, - {"name": "PĂŠter Nochta"}, - {"name": "RĂłbert BĂłnay"}, - {"name": "Szabolcs Czinege"}, - {"name": "Viktor Borza"}, - {"name": "Viktor LĂĄzĂĄr"}, - {"name": "ZoltĂĄn Gyebrovszki"} + { + "name": "DĂĄniel JĂłzsef" + }, + { + "name": "Hajnalka Battancs" + }, + { + "name": "JĂĄnos Roden" + }, + { + "name": "LĂĄszlĂł HorvĂĄth" + }, + { + "name": "PĂŠter Zentai" + }, + { + "name": "PĂŠter Nochta" + }, + { + "name": "RĂłbert BĂłnay" + }, + { + "name": "Szabolcs Czinege" + }, + { + "name": "Viktor Borza" + }, + { + "name": "Viktor LĂĄzĂĄr" + }, + { + "name": "ZoltĂĄn Gyebrovszki" + } ], "repository": { "type": "git", @@ -48,4 +83,5 @@ "type": "GPLv2", "url": "http://jaydata.org/licensing" } - ]} + ] +} diff --git a/release/jaydata-vsdoc.js b/release/jaydata-vsdoc.js deleted file mode 100644 index 21e89922..00000000 --- a/release/jaydata-vsdoc.js +++ /dev/null @@ -1,17287 +0,0 @@ -// JayData 1.3.6 -// Dual licensed under MIT and GPL v2 -// Copyright JayStack Technologies (http://jaydata.org/licensing) -// -// JayData is a standards-based, cross-platform Javascript library and a set of -// practices to access and manipulate data from various online and offline sources. -// -// Credits: -// Hajnalka Battancs, Dániel József, János Roden, László Horváth, Péter Nochta -// Péter Zentai, Róbert Bónay, Szabolcs Czinege, Viktor Borza, Viktor Lázár, -// Zoltán Gyebrovszki, Gábor Dolla -// -// More info: http://jaydata.org -/* - -IMPORTANT: -!!! Do not reference this file to html! !!! - -*/ - -(function (window) { - if (typeof window.intellisense === 'undefined') { - window.intellisense = { - annotate: function () { }, - logMessage: function () { } - }; - } -})(window);// Acorn is a tiny, fast JavaScript parser written in JavaScript. -// -// Acorn was written by Marijn Haverbeke and released under an MIT -// license. The Unicode regexps (for identifiers and whitespace) were -// taken from [Esprima](http://esprima.org) by Ariya Hidayat. -// -// Git repositories for Acorn are available at -// -// http://marijnhaverbeke.nl/git/acorn -// https://github.com/marijnh/acorn.git -// -// Please use the [github bug tracker][ghbt] to report issues. -// -// [ghbt]: https://github.com/marijnh/acorn/issues -// -// This file defines the main parser interface. The library also comes -// with a [error-tolerant parser][dammit] and an -// [abstract syntax tree walker][walk], defined in other files. -// -// [dammit]: acorn_loose.js -// [walk]: util/walk.js - -(function(mod) { - if (typeof exports == "object" && typeof module == "object") return mod(exports); // CommonJS - if (typeof define == "function" && define.amd) return define(["exports"], mod); // AMD - mod(self.acorn || (self.acorn = {})); // Plain browser env -})(function(exports) { - "use strict"; - - exports.version = "0.1.01"; - - // The main exported interface (under `self.acorn` when in the - // browser) is a `parse` function that takes a code string and - // returns an abstract syntax tree as specified by [Mozilla parser - // API][api], with the caveat that the SpiderMonkey-specific syntax - // (`let`, `yield`, inline XML, etc) is not recognized. - // - // [api]: https://developer.mozilla.org/en-US/docs/SpiderMonkey/Parser_API - - var options, input, inputLen, sourceFile; - - exports.parse = function(inpt, opts) { - input = String(inpt); inputLen = input.length; - setOptions(opts); - initTokenState(); - return parseTopLevel(options.program); - }; - - // A second optional argument can be given to further configure - // the parser process. These options are recognized: - - var defaultOptions = exports.defaultOptions = { - // `ecmaVersion` indicates the ECMAScript version to parse. Must - // be either 3 or 5. This - // influences support for strict mode, the set of reserved words, and - // support for getters and setter. - ecmaVersion: 5, - // Turn on `strictSemicolons` to prevent the parser from doing - // automatic semicolon insertion. - strictSemicolons: false, - // When `allowTrailingCommas` is false, the parser will not allow - // trailing commas in array and object literals. - allowTrailingCommas: true, - // By default, reserved words are not enforced. Enable - // `forbidReserved` to enforce them. - forbidReserved: false, - // When `locations` is on, `loc` properties holding objects with - // `start` and `end` properties in `{line, column}` form (with - // line being 1-based and column 0-based) will be attached to the - // nodes. - locations: false, - // A function can be passed as `onComment` option, which will - // cause Acorn to call that function with `(block, text, start, - // end)` parameters whenever a comment is skipped. `block` is a - // boolean indicating whether this is a block (`/* */`) comment, - // `text` is the content of the comment, and `start` and `end` are - // character offsets that denote the start and end of the comment. - // When the `locations` option is on, two more parameters are - // passed, the full `{line, column}` locations of the start and - // end of the comments. - onComment: null, - // Nodes have their start and end characters offsets recorded in - // `start` and `end` properties (directly on the node, rather than - // the `loc` object, which holds line/column data. To also add a - // [semi-standardized][range] `range` property holding a `[start, - // end]` array with the same numbers, set the `ranges` option to - // `true`. - // - // [range]: https://bugzilla.mozilla.org/show_bug.cgi?id=745678 - ranges: false, - // It is possible to parse multiple files into a single AST by - // passing the tree produced by parsing the first file as - // `program` option in subsequent parses. This will add the - // toplevel forms of the parsed file to the `Program` (top) node - // of an existing parse tree. - program: null, - // When `location` is on, you can pass this to record the source - // file in every node's `loc` object. - sourceFile: null - }; - - function setOptions(opts) { - options = opts || {}; - for (var opt in defaultOptions) if (!options.hasOwnProperty(opt)) - options[opt] = defaultOptions[opt]; - sourceFile = options.sourceFile || null; - } - - // The `getLineInfo` function is mostly useful when the - // `locations` option is off (for performance reasons) and you - // want to find the line/column position for a given character - // offset. `input` should be the code string that the offset refers - // into. - - var getLineInfo = exports.getLineInfo = function(input, offset) { - for (var line = 1, cur = 0;;) { - lineBreak.lastIndex = cur; - var match = lineBreak.exec(input); - if (match && match.index < offset) { - ++line; - cur = match.index + match[0].length; - } else break; - } - return {line: line, column: offset - cur}; - }; - - // Acorn is organized as a tokenizer and a recursive-descent parser. - // The `tokenize` export provides an interface to the tokenizer. - // Because the tokenizer is optimized for being efficiently used by - // the Acorn parser itself, this interface is somewhat crude and not - // very modular. Performing another parse or call to `tokenize` will - // reset the internal state, and invalidate existing tokenizers. - - exports.tokenize = function(inpt, opts) { - input = String(inpt); inputLen = input.length; - setOptions(opts); - initTokenState(); - - var t = {}; - function getToken(forceRegexp) { - readToken(forceRegexp); - t.start = tokStart; t.end = tokEnd; - t.startLoc = tokStartLoc; t.endLoc = tokEndLoc; - t.type = tokType; t.value = tokVal; - return t; - } - getToken.jumpTo = function(pos, reAllowed) { - tokPos = pos; - if (options.locations) { - tokCurLine = tokLineStart = lineBreak.lastIndex = 0; - var match; - while ((match = lineBreak.exec(input)) && match.index < pos) { - ++tokCurLine; - tokLineStart = match.index + match[0].length; - } - } - var ch = input.charAt(pos - 1); - tokRegexpAllowed = reAllowed; - skipSpace(); - }; - return getToken; - }; - - // State is kept in (closure-)global variables. We already saw the - // `options`, `input`, and `inputLen` variables above. - - // The current position of the tokenizer in the input. - - var tokPos; - - // The start and end offsets of the current token. - - var tokStart, tokEnd; - - // When `options.locations` is true, these hold objects - // containing the tokens start and end line/column pairs. - - var tokStartLoc, tokEndLoc; - - // The type and value of the current token. Token types are objects, - // named by variables against which they can be compared, and - // holding properties that describe them (indicating, for example, - // the precedence of an infix operator, and the original name of a - // keyword token). The kind of value that's held in `tokVal` depends - // on the type of the token. For literals, it is the literal value, - // for operators, the operator name, and so on. - - var tokType, tokVal; - - // Interal state for the tokenizer. To distinguish between division - // operators and regular expressions, it remembers whether the last - // token was one that is allowed to be followed by an expression. - // (If it is, a slash is probably a regexp, if it isn't it's a - // division operator. See the `parseStatement` function for a - // caveat.) - - var tokRegexpAllowed; - - // When `options.locations` is true, these are used to keep - // track of the current line, and know when a new line has been - // entered. - - var tokCurLine, tokLineStart; - - // These store the position of the previous token, which is useful - // when finishing a node and assigning its `end` position. - - var lastStart, lastEnd, lastEndLoc; - - // This is the parser's state. `inFunction` is used to reject - // `return` statements outside of functions, `labels` to verify that - // `break` and `continue` have somewhere to jump to, and `strict` - // indicates whether strict mode is on. - - var inFunction, labels, strict; - - // This function is used to raise exceptions on parse errors. It - // takes an offset integer (into the current `input`) to indicate - // the location of the error, attaches the position to the end - // of the error message, and then raises a `SyntaxError` with that - // message. - - function raise(pos, message) { - var loc = getLineInfo(input, pos); - message += " (" + loc.line + ":" + loc.column + ")"; - var err = new SyntaxError(message); - err.pos = pos; err.loc = loc; err.raisedAt = tokPos; - throw err; - } - - // ## Token types - - // The assignment of fine-grained, information-carrying type objects - // allows the tokenizer to store the information it has about a - // token in a way that is very cheap for the parser to look up. - - // All token type variables start with an underscore, to make them - // easy to recognize. - - // These are the general types. The `type` property is only used to - // make them recognizeable when debugging. - - var _num = {type: "num"}, _regexp = {type: "regexp"}, _string = {type: "string"}; - var _name = {type: "name"}, _eof = {type: "eof"}; - - // Keyword tokens. The `keyword` property (also used in keyword-like - // operators) indicates that the token originated from an - // identifier-like word, which is used when parsing property names. - // - // The `beforeExpr` property is used to disambiguate between regular - // expressions and divisions. It is set on all token types that can - // be followed by an expression (thus, a slash after them would be a - // regular expression). - // - // `isLoop` marks a keyword as starting a loop, which is important - // to know when parsing a label, in order to allow or disallow - // continue jumps to that label. - - var _break = {keyword: "break"}, _case = {keyword: "case", beforeExpr: true}, _catch = {keyword: "catch"}; - var _continue = {keyword: "continue"}, _debugger = {keyword: "debugger"}, _default = {keyword: "default"}; - var _do = {keyword: "do", isLoop: true}, _else = {keyword: "else", beforeExpr: true}; - var _finally = {keyword: "finally"}, _for = {keyword: "for", isLoop: true}, _function = {keyword: "function"}; - var _if = {keyword: "if"}, _return = {keyword: "return", beforeExpr: true}, _switch = {keyword: "switch"}; - var _throw = {keyword: "throw", beforeExpr: true}, _try = {keyword: "try"}, _var = {keyword: "var"}; - var _while = {keyword: "while", isLoop: true}, _with = {keyword: "with"}, _new = {keyword: "new", beforeExpr: true}; - var _this = {keyword: "this"}; - - // The keywords that denote values. - - var _null = {keyword: "null", atomValue: null}, _true = {keyword: "true", atomValue: true}; - var _false = {keyword: "false", atomValue: false}; - - // Some keywords are treated as regular operators. `in` sometimes - // (when parsing `for`) needs to be tested against specifically, so - // we assign a variable name to it for quick comparing. - - var _in = {keyword: "in", binop: 7, beforeExpr: true}; - - // Map keyword names to token types. - - var keywordTypes = {"break": _break, "case": _case, "catch": _catch, - "continue": _continue, "debugger": _debugger, "default": _default, - "do": _do, "else": _else, "finally": _finally, "for": _for, - "function": _function, "if": _if, "return": _return, "switch": _switch, - "throw": _throw, "try": _try, "var": _var, "while": _while, "with": _with, - "null": _null, "true": _true, "false": _false, "new": _new, "in": _in, - "instanceof": {keyword: "instanceof", binop: 7, beforeExpr: true}, "this": _this, - "typeof": {keyword: "typeof", prefix: true, beforeExpr: true}, - "void": {keyword: "void", prefix: true, beforeExpr: true}, - "delete": {keyword: "delete", prefix: true, beforeExpr: true}}; - - // Punctuation token types. Again, the `type` property is purely for debugging. - - var _bracketL = {type: "[", beforeExpr: true}, _bracketR = {type: "]"}, _braceL = {type: "{", beforeExpr: true}; - var _braceR = {type: "}"}, _parenL = {type: "(", beforeExpr: true}, _parenR = {type: ")"}; - var _comma = {type: ",", beforeExpr: true}, _semi = {type: ";", beforeExpr: true}; - var _colon = {type: ":", beforeExpr: true}, _dot = {type: "."}, _question = {type: "?", beforeExpr: true}; - - // Operators. These carry several kinds of properties to help the - // parser use them properly (the presence of these properties is - // what categorizes them as operators). - // - // `binop`, when present, specifies that this operator is a binary - // operator, and will refer to its precedence. - // - // `prefix` and `postfix` mark the operator as a prefix or postfix - // unary operator. `isUpdate` specifies that the node produced by - // the operator should be of type UpdateExpression rather than - // simply UnaryExpression (`++` and `--`). - // - // `isAssign` marks all of `=`, `+=`, `-=` etcetera, which act as - // binary operators with a very low precedence, that should result - // in AssignmentExpression nodes. - - var _slash = {binop: 10, beforeExpr: true}, _eq = {isAssign: true, beforeExpr: true}; - var _assign = {isAssign: true, beforeExpr: true}, _plusmin = {binop: 9, prefix: true, beforeExpr: true}; - var _incdec = {postfix: true, prefix: true, isUpdate: true}, _prefix = {prefix: true, beforeExpr: true}; - var _bin1 = {binop: 1, beforeExpr: true}, _bin2 = {binop: 2, beforeExpr: true}; - var _bin3 = {binop: 3, beforeExpr: true}, _bin4 = {binop: 4, beforeExpr: true}; - var _bin5 = {binop: 5, beforeExpr: true}, _bin6 = {binop: 6, beforeExpr: true}; - var _bin7 = {binop: 7, beforeExpr: true}, _bin8 = {binop: 8, beforeExpr: true}; - var _bin10 = {binop: 10, beforeExpr: true}; - - // Provide access to the token types for external users of the - // tokenizer. - - exports.tokTypes = {bracketL: _bracketL, bracketR: _bracketR, braceL: _braceL, braceR: _braceR, - parenL: _parenL, parenR: _parenR, comma: _comma, semi: _semi, colon: _colon, - dot: _dot, question: _question, slash: _slash, eq: _eq, name: _name, eof: _eof, - num: _num, regexp: _regexp, string: _string}; - for (var kw in keywordTypes) exports.tokTypes[kw] = keywordTypes[kw]; - - // This is a trick taken from Esprima. It turns out that, on - // non-Chrome browsers, to check whether a string is in a set, a - // predicate containing a big ugly `switch` statement is faster than - // a regular expression, and on Chrome the two are about on par. - // This function uses `eval` (non-lexical) to produce such a - // predicate from a space-separated string of words. - // - // It starts by sorting the words by length. - - function makePredicate(words) { - words = words.split(" "); - var f = "", cats = []; - out: for (var i = 0; i < words.length; ++i) { - for (var j = 0; j < cats.length; ++j) - if (cats[j][0].length == words[i].length) { - cats[j].push(words[i]); - continue out; - } - cats.push([words[i]]); - } - function compareTo(arr) { - if (arr.length == 1) return f += "return str === " + JSON.stringify(arr[0]) + ";"; - f += "switch(str){"; - for (var i = 0; i < arr.length; ++i) f += "case " + JSON.stringify(arr[i]) + ":"; - f += "return true}return false;"; - } - - // When there are more than three length categories, an outer - // switch first dispatches on the lengths, to save on comparisons. - - if (cats.length > 3) { - cats.sort(function(a, b) {return b.length - a.length;}); - f += "switch(str.length){"; - for (var i = 0; i < cats.length; ++i) { - var cat = cats[i]; - f += "case " + cat[0].length + ":"; - compareTo(cat); - } - f += "}"; - - // Otherwise, simply generate a flat `switch` statement. - - } else { - compareTo(words); - } - return new Function("str", f); - } - - // The ECMAScript 3 reserved word list. - - var isReservedWord3 = makePredicate("abstract boolean byte char class double enum export extends final float goto implements import int interface long native package private protected public short static super synchronized throws transient volatile"); - - // ECMAScript 5 reserved words. - - var isReservedWord5 = makePredicate("class enum extends super const export import"); - - // The additional reserved words in strict mode. - - var isStrictReservedWord = makePredicate("implements interface let package private protected public static yield"); - - // The forbidden variable names in strict mode. - - var isStrictBadIdWord = makePredicate("eval arguments"); - - // And the keywords. - - var isKeyword = makePredicate("break case catch continue debugger default do else finally for function if return switch throw try var while with null true false instanceof typeof void delete new in this"); - - // ## Character categories - - // Big ugly regular expressions that match characters in the - // whitespace, identifier, and identifier-start categories. These - // are only applied when a character is found to actually have a - // code point above 128. - - var nonASCIIwhitespace = /[\u1680\u180e\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff]/; - var nonASCIIidentifierStartChars = "\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0370-\u0374\u0376\u0377\u037a-\u037d\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u048a-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05d0-\u05ea\u05f0-\u05f2\u0620-\u064a\u066e\u066f\u0671-\u06d3\u06d5\u06e5\u06e6\u06ee\u06ef\u06fa-\u06fc\u06ff\u0710\u0712-\u072f\u074d-\u07a5\u07b1\u07ca-\u07ea\u07f4\u07f5\u07fa\u0800-\u0815\u081a\u0824\u0828\u0840-\u0858\u08a0\u08a2-\u08ac\u0904-\u0939\u093d\u0950\u0958-\u0961\u0971-\u0977\u0979-\u097f\u0985-\u098c\u098f\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bd\u09ce\u09dc\u09dd\u09df-\u09e1\u09f0\u09f1\u0a05-\u0a0a\u0a0f\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a59-\u0a5c\u0a5e\u0a72-\u0a74\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2\u0ab3\u0ab5-\u0ab9\u0abd\u0ad0\u0ae0\u0ae1\u0b05-\u0b0c\u0b0f\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32\u0b33\u0b35-\u0b39\u0b3d\u0b5c\u0b5d\u0b5f-\u0b61\u0b71\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bd0\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c33\u0c35-\u0c39\u0c3d\u0c58\u0c59\u0c60\u0c61\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbd\u0cde\u0ce0\u0ce1\u0cf1\u0cf2\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d\u0d4e\u0d60\u0d61\u0d7a-\u0d7f\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0e01-\u0e30\u0e32\u0e33\u0e40-\u0e46\u0e81\u0e82\u0e84\u0e87\u0e88\u0e8a\u0e8d\u0e94-\u0e97\u0e99-\u0e9f\u0ea1-\u0ea3\u0ea5\u0ea7\u0eaa\u0eab\u0ead-\u0eb0\u0eb2\u0eb3\u0ebd\u0ec0-\u0ec4\u0ec6\u0edc-\u0edf\u0f00\u0f40-\u0f47\u0f49-\u0f6c\u0f88-\u0f8c\u1000-\u102a\u103f\u1050-\u1055\u105a-\u105d\u1061\u1065\u1066\u106e-\u1070\u1075-\u1081\u108e\u10a0-\u10c5\u10c7\u10cd\u10d0-\u10fa\u10fc-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u1380-\u138f\u13a0-\u13f4\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f0\u1700-\u170c\u170e-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176c\u176e-\u1770\u1780-\u17b3\u17d7\u17dc\u1820-\u1877\u1880-\u18a8\u18aa\u18b0-\u18f5\u1900-\u191c\u1950-\u196d\u1970-\u1974\u1980-\u19ab\u19c1-\u19c7\u1a00-\u1a16\u1a20-\u1a54\u1aa7\u1b05-\u1b33\u1b45-\u1b4b\u1b83-\u1ba0\u1bae\u1baf\u1bba-\u1be5\u1c00-\u1c23\u1c4d-\u1c4f\u1c5a-\u1c7d\u1ce9-\u1cec\u1cee-\u1cf1\u1cf5\u1cf6\u1d00-\u1dbf\u1e00-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u2071\u207f\u2090-\u209c\u2102\u2107\u210a-\u2113\u2115\u2119-\u211d\u2124\u2126\u2128\u212a-\u212d\u212f-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2ce4\u2ceb-\u2cee\u2cf2\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d80-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u2e2f\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303c\u3041-\u3096\u309d-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312d\u3131-\u318e\u31a0-\u31ba\u31f0-\u31ff\u3400-\u4db5\u4e00-\u9fcc\ua000-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua61f\ua62a\ua62b\ua640-\ua66e\ua67f-\ua697\ua6a0-\ua6ef\ua717-\ua71f\ua722-\ua788\ua78b-\ua78e\ua790-\ua793\ua7a0-\ua7aa\ua7f8-\ua801\ua803-\ua805\ua807-\ua80a\ua80c-\ua822\ua840-\ua873\ua882-\ua8b3\ua8f2-\ua8f7\ua8fb\ua90a-\ua925\ua930-\ua946\ua960-\ua97c\ua984-\ua9b2\ua9cf\uaa00-\uaa28\uaa40-\uaa42\uaa44-\uaa4b\uaa60-\uaa76\uaa7a\uaa80-\uaaaf\uaab1\uaab5\uaab6\uaab9-\uaabd\uaac0\uaac2\uaadb-\uaadd\uaae0-\uaaea\uaaf2-\uaaf4\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uabc0-\uabe2\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d\ufb1f-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40\ufb41\ufb43\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe70-\ufe74\ufe76-\ufefc\uff21-\uff3a\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc"; - var nonASCIIidentifierChars = "\u0371-\u0374\u0483-\u0487\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u0620-\u0649\u0672-\u06d3\u06e7-\u06e8\u06fb-\u06fc\u0730-\u074a\u0800-\u0814\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0840-\u0857\u08e4-\u08fe\u0900-\u0903\u093a-\u093c\u093e-\u094f\u0951-\u0957\u0962-\u0963\u0966-\u096f\u0981-\u0983\u09bc\u09be-\u09c4\u09c7\u09c8\u09d7\u09df-\u09e0\u0a01-\u0a03\u0a3c\u0a3e-\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a66-\u0a71\u0a75\u0a81-\u0a83\u0abc\u0abe-\u0ac5\u0ac7-\u0ac9\u0acb-\u0acd\u0ae2-\u0ae3\u0ae6-\u0aef\u0b01-\u0b03\u0b3c\u0b3e-\u0b44\u0b47\u0b48\u0b4b-\u0b4d\u0b56\u0b57\u0b5f-\u0b60\u0b66-\u0b6f\u0b82\u0bbe-\u0bc2\u0bc6-\u0bc8\u0bca-\u0bcd\u0bd7\u0be6-\u0bef\u0c01-\u0c03\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62-\u0c63\u0c66-\u0c6f\u0c82\u0c83\u0cbc\u0cbe-\u0cc4\u0cc6-\u0cc8\u0cca-\u0ccd\u0cd5\u0cd6\u0ce2-\u0ce3\u0ce6-\u0cef\u0d02\u0d03\u0d46-\u0d48\u0d57\u0d62-\u0d63\u0d66-\u0d6f\u0d82\u0d83\u0dca\u0dcf-\u0dd4\u0dd6\u0dd8-\u0ddf\u0df2\u0df3\u0e34-\u0e3a\u0e40-\u0e45\u0e50-\u0e59\u0eb4-\u0eb9\u0ec8-\u0ecd\u0ed0-\u0ed9\u0f18\u0f19\u0f20-\u0f29\u0f35\u0f37\u0f39\u0f41-\u0f47\u0f71-\u0f84\u0f86-\u0f87\u0f8d-\u0f97\u0f99-\u0fbc\u0fc6\u1000-\u1029\u1040-\u1049\u1067-\u106d\u1071-\u1074\u1082-\u108d\u108f-\u109d\u135d-\u135f\u170e-\u1710\u1720-\u1730\u1740-\u1750\u1772\u1773\u1780-\u17b2\u17dd\u17e0-\u17e9\u180b-\u180d\u1810-\u1819\u1920-\u192b\u1930-\u193b\u1951-\u196d\u19b0-\u19c0\u19c8-\u19c9\u19d0-\u19d9\u1a00-\u1a15\u1a20-\u1a53\u1a60-\u1a7c\u1a7f-\u1a89\u1a90-\u1a99\u1b46-\u1b4b\u1b50-\u1b59\u1b6b-\u1b73\u1bb0-\u1bb9\u1be6-\u1bf3\u1c00-\u1c22\u1c40-\u1c49\u1c5b-\u1c7d\u1cd0-\u1cd2\u1d00-\u1dbe\u1e01-\u1f15\u200c\u200d\u203f\u2040\u2054\u20d0-\u20dc\u20e1\u20e5-\u20f0\u2d81-\u2d96\u2de0-\u2dff\u3021-\u3028\u3099\u309a\ua640-\ua66d\ua674-\ua67d\ua69f\ua6f0-\ua6f1\ua7f8-\ua800\ua806\ua80b\ua823-\ua827\ua880-\ua881\ua8b4-\ua8c4\ua8d0-\ua8d9\ua8f3-\ua8f7\ua900-\ua909\ua926-\ua92d\ua930-\ua945\ua980-\ua983\ua9b3-\ua9c0\uaa00-\uaa27\uaa40-\uaa41\uaa4c-\uaa4d\uaa50-\uaa59\uaa7b\uaae0-\uaae9\uaaf2-\uaaf3\uabc0-\uabe1\uabec\uabed\uabf0-\uabf9\ufb20-\ufb28\ufe00-\ufe0f\ufe20-\ufe26\ufe33\ufe34\ufe4d-\ufe4f\uff10-\uff19\uff3f"; - var nonASCIIidentifierStart = new RegExp("[" + nonASCIIidentifierStartChars + "]"); - var nonASCIIidentifier = new RegExp("[" + nonASCIIidentifierStartChars + nonASCIIidentifierChars + "]"); - - // Whether a single character denotes a newline. - - var newline = /[\n\r\u2028\u2029]/; - - // Matches a whole line break (where CRLF is considered a single - // line break). Used to count lines. - - var lineBreak = /\r\n|[\n\r\u2028\u2029]/g; - - // Test whether a given character code starts an identifier. - - function isIdentifierStart(code) { - if (code < 65) return code === 36; - if (code < 91) return true; - if (code < 97) return code === 95; - if (code < 123)return true; - return code >= 0xaa && nonASCIIidentifierStart.test(String.fromCharCode(code)); - } - - // Test whether a given character is part of an identifier. - - function isIdentifierChar(code) { - if (code < 48) return code === 36; - if (code < 58) return true; - if (code < 65) return false; - if (code < 91) return true; - if (code < 97) return code === 95; - if (code < 123)return true; - return code >= 0xaa && nonASCIIidentifier.test(String.fromCharCode(code)); - } - - // ## Tokenizer - - // These are used when `options.locations` is on, for the - // `tokStartLoc` and `tokEndLoc` properties. - - function line_loc_t() { - this.line = tokCurLine; - this.column = tokPos - tokLineStart; - } - - // Reset the token state. Used at the start of a parse. - - function initTokenState() { - tokCurLine = 1; - tokPos = tokLineStart = 0; - tokRegexpAllowed = true; - skipSpace(); - } - - // Called at the end of every token. Sets `tokEnd`, `tokVal`, and - // `tokRegexpAllowed`, and skips the space after the token, so that - // the next one's `tokStart` will point at the right position. - - function finishToken(type, val) { - tokEnd = tokPos; - if (options.locations) tokEndLoc = new line_loc_t; - tokType = type; - skipSpace(); - tokVal = val; - tokRegexpAllowed = type.beforeExpr; - } - - function skipBlockComment() { - var startLoc = options.onComment && options.locations && new line_loc_t; - var start = tokPos, end = input.indexOf("*/", tokPos += 2); - if (end === -1) raise(tokPos - 2, "Unterminated comment"); - tokPos = end + 2; - if (options.locations) { - lineBreak.lastIndex = start; - var match; - while ((match = lineBreak.exec(input)) && match.index < tokPos) { - ++tokCurLine; - tokLineStart = match.index + match[0].length; - } - } - if (options.onComment) - options.onComment(true, input.slice(start + 2, end), start, tokPos, - startLoc, options.locations && new line_loc_t); - } - - function skipLineComment() { - var start = tokPos; - var startLoc = options.onComment && options.locations && new line_loc_t; - var ch = input.charCodeAt(tokPos+=2); - while (tokPos < inputLen && ch !== 10 && ch !== 13 && ch !== 8232 && ch !== 8329) { - ++tokPos; - ch = input.charCodeAt(tokPos); - } - if (options.onComment) - options.onComment(false, input.slice(start + 2, tokPos), start, tokPos, - startLoc, options.locations && new line_loc_t); - } - - // Called at the start of the parse and after every token. Skips - // whitespace and comments, and. - - function skipSpace() { - while (tokPos < inputLen) { - var ch = input.charCodeAt(tokPos); - if (ch === 32) { // ' ' - ++tokPos; - } else if(ch === 13) { - ++tokPos; - var next = input.charCodeAt(tokPos); - if(next === 10) { - ++tokPos; - } - if(options.locations) { - ++tokCurLine; - tokLineStart = tokPos; - } - } else if (ch === 10) { - ++tokPos; - ++tokCurLine; - tokLineStart = tokPos; - } else if(ch < 14 && ch > 8) { - ++tokPos; - } else if (ch === 47) { // '/' - var next = input.charCodeAt(tokPos+1); - if (next === 42) { // '*' - skipBlockComment(); - } else if (next === 47) { // '/' - skipLineComment(); - } else break; - } else if ((ch < 14 && ch > 8) || ch === 32 || ch === 160) { // ' ', '\xa0' - ++tokPos; - } else if (ch >= 5760 && nonASCIIwhitespace.test(String.fromCharCode(ch))) { - ++tokPos; - } else { - break; - } - } - } - - // ### Token reading - - // This is the function that is called to fetch the next token. It - // is somewhat obscure, because it works in character codes rather - // than characters, and because operator parsing has been inlined - // into it. - // - // All in the name of speed. - // - // The `forceRegexp` parameter is used in the one case where the - // `tokRegexpAllowed` trick does not work. See `parseStatement`. - - function readToken_dot() { - var next = input.charCodeAt(tokPos+1); - if (next >= 48 && next <= 57) return readNumber(true); - ++tokPos; - return finishToken(_dot); - } - - function readToken_slash() { // '/' - var next = input.charCodeAt(tokPos+1); - if (tokRegexpAllowed) {++tokPos; return readRegexp();} - if (next === 61) return finishOp(_assign, 2); - return finishOp(_slash, 1); - } - - function readToken_mult_modulo() { // '%*' - var next = input.charCodeAt(tokPos+1); - if (next === 61) return finishOp(_assign, 2); - return finishOp(_bin10, 1); - } - - function readToken_pipe_amp(code) { // '|&' - var next = input.charCodeAt(tokPos+1); - if (next === code) return finishOp(code === 124 ? _bin1 : _bin2, 2); - if (next === 61) return finishOp(_assign, 2); - return finishOp(code === 124 ? _bin3 : _bin5, 1); - } - - function readToken_caret() { // '^' - var next = input.charCodeAt(tokPos+1); - if (next === 61) return finishOp(_assign, 2); - return finishOp(_bin4, 1); - } - - function readToken_plus_min(code) { // '+-' - var next = input.charCodeAt(tokPos+1); - if (next === code) return finishOp(_incdec, 2); - if (next === 61) return finishOp(_assign, 2); - return finishOp(_plusmin, 1); - } - - function readToken_lt_gt(code) { // '<>' - var next = input.charCodeAt(tokPos+1); - var size = 1; - if (next === code) { - size = code === 62 && input.charCodeAt(tokPos+2) === 62 ? 3 : 2; - if (input.charCodeAt(tokPos + size) === 61) return finishOp(_assign, size + 1); - return finishOp(_bin8, size); - } - if (next === 61) - size = input.charCodeAt(tokPos+2) === 61 ? 3 : 2; - return finishOp(_bin7, size); - } - - function readToken_eq_excl(code) { // '=!' - var next = input.charCodeAt(tokPos+1); - if (next === 61) return finishOp(_bin6, input.charCodeAt(tokPos+2) === 61 ? 3 : 2); - return finishOp(code === 61 ? _eq : _prefix, 1); - } - - function getTokenFromCode(code) { - switch(code) { - // The interpretation of a dot depends on whether it is followed - // by a digit. - case 46: // '.' - return readToken_dot(); - - // Punctuation tokens. - case 40: ++tokPos; return finishToken(_parenL); - case 41: ++tokPos; return finishToken(_parenR); - case 59: ++tokPos; return finishToken(_semi); - case 44: ++tokPos; return finishToken(_comma); - case 91: ++tokPos; return finishToken(_bracketL); - case 93: ++tokPos; return finishToken(_bracketR); - case 123: ++tokPos; return finishToken(_braceL); - case 125: ++tokPos; return finishToken(_braceR); - case 58: ++tokPos; return finishToken(_colon); - case 63: ++tokPos; return finishToken(_question); - - // '0x' is a hexadecimal number. - case 48: // '0' - var next = input.charCodeAt(tokPos+1); - if (next === 120 || next === 88) return readHexNumber(); - // Anything else beginning with a digit is an integer, octal - // number, or float. - case 49: case 50: case 51: case 52: case 53: case 54: case 55: case 56: case 57: // 1-9 - return readNumber(false); - - // Quotes produce strings. - case 34: case 39: // '"', "'" - return readString(code); - - // Operators are parsed inline in tiny state machines. '=' (61) is - // often referred to. `finishOp` simply skips the amount of - // characters it is given as second argument, and returns a token - // of the type given by its first argument. - - case 47: // '/' - return readToken_slash(code); - - case 37: case 42: // '%*' - return readToken_mult_modulo(); - - case 124: case 38: // '|&' - return readToken_pipe_amp(code); - - case 94: // '^' - return readToken_caret(); - - case 43: case 45: // '+-' - return readToken_plus_min(code); - - case 60: case 62: // '<>' - return readToken_lt_gt(code); - - case 61: case 33: // '=!' - return readToken_eq_excl(code); - - case 126: // '~' - return finishOp(_prefix, 1); - } - - return false; - } - - function readToken(forceRegexp) { - if (!forceRegexp) tokStart = tokPos; - else tokPos = tokStart + 1; - if (options.locations) tokStartLoc = new line_loc_t; - if (forceRegexp) return readRegexp(); - if (tokPos >= inputLen) return finishToken(_eof); - - var code = input.charCodeAt(tokPos); - // Identifier or keyword. '\uXXXX' sequences are allowed in - // identifiers, so '\' also dispatches to that. - if (isIdentifierStart(code) || code === 92 /* '\' */) return readWord(); - - var tok = getTokenFromCode(code); - - if (tok === false) { - // If we are here, we either found a non-ASCII identifier - // character, or something that's entirely disallowed. - var ch = String.fromCharCode(code); - if (ch === "\\" || nonASCIIidentifierStart.test(ch)) return readWord(); - raise(tokPos, "Unexpected character '" + ch + "'"); - } - return tok; - } - - function finishOp(type, size) { - var str = input.slice(tokPos, tokPos + size); - tokPos += size; - finishToken(type, str); - } - - // Parse a regular expression. Some context-awareness is necessary, - // since a '/' inside a '[]' set does not end the expression. - - function readRegexp() { - var content = "", escaped, inClass, start = tokPos; - for (;;) { - if (tokPos >= inputLen) raise(start, "Unterminated regular expression"); - var ch = input.charAt(tokPos); - if (newline.test(ch)) raise(start, "Unterminated regular expression"); - if (!escaped) { - if (ch === "[") inClass = true; - else if (ch === "]" && inClass) inClass = false; - else if (ch === "/" && !inClass) break; - escaped = ch === "\\"; - } else escaped = false; - ++tokPos; - } - var content = input.slice(start, tokPos); - ++tokPos; - // Need to use `readWord1` because '\uXXXX' sequences are allowed - // here (don't ask). - var mods = readWord1(); - if (mods && !/^[gmsiy]*$/.test(mods)) raise(start, "Invalid regexp flag"); - return finishToken(_regexp, new RegExp(content, mods)); - } - - // Read an integer in the given radix. Return null if zero digits - // were read, the integer value otherwise. When `len` is given, this - // will return `null` unless the integer has exactly `len` digits. - - function readInt(radix, len) { - var start = tokPos, total = 0; - for (var i = 0, e = len == null ? Infinity : len; i < e; ++i) { - var code = input.charCodeAt(tokPos), val; - if (code >= 97) val = code - 97 + 10; // a - else if (code >= 65) val = code - 65 + 10; // A - else if (code >= 48 && code <= 57) val = code - 48; // 0-9 - else val = Infinity; - if (val >= radix) break; - ++tokPos; - total = total * radix + val; - } - if (tokPos === start || len != null && tokPos - start !== len) return null; - - return total; - } - - function readHexNumber() { - tokPos += 2; // 0x - var val = readInt(16); - if (val == null) raise(tokStart + 2, "Expected hexadecimal number"); - if (isIdentifierStart(input.charCodeAt(tokPos))) raise(tokPos, "Identifier directly after number"); - return finishToken(_num, val); - } - - // Read an integer, octal integer, or floating-point number. - - function readNumber(startsWithDot) { - var start = tokPos, isFloat = false, octal = input.charCodeAt(tokPos) === 48; - if (!startsWithDot && readInt(10) === null) raise(start, "Invalid number"); - if (input.charCodeAt(tokPos) === 46) { - ++tokPos; - readInt(10); - isFloat = true; - } - var next = input.charCodeAt(tokPos); - if (next === 69 || next === 101) { // 'eE' - next = input.charCodeAt(++tokPos); - if (next === 43 || next === 45) ++tokPos; // '+-' - if (readInt(10) === null) raise(start, "Invalid number") - isFloat = true; - } - if (isIdentifierStart(input.charCodeAt(tokPos))) raise(tokPos, "Identifier directly after number"); - - var str = input.slice(start, tokPos), val; - if (isFloat) val = parseFloat(str); - else if (!octal || str.length === 1) val = parseInt(str, 10); - else if (/[89]/.test(str) || strict) raise(start, "Invalid number"); - else val = parseInt(str, 8); - return finishToken(_num, val); - } - - // Read a string value, interpreting backslash-escapes. - - function readString(quote) { - tokPos++; - var out = ""; - for (;;) { - if (tokPos >= inputLen) raise(tokStart, "Unterminated string constant"); - var ch = input.charCodeAt(tokPos); - if (ch === quote) { - ++tokPos; - return finishToken(_string, out); - } - if (ch === 92) { // '\' - ch = input.charCodeAt(++tokPos); - var octal = /^[0-7]+/.exec(input.slice(tokPos, tokPos + 3)); - if (octal) octal = octal[0]; - while (octal && parseInt(octal, 8) > 255) octal = octal.slice(0, octal.length - 1); - if (octal === "0") octal = null; - ++tokPos; - if (octal) { - if (strict) raise(tokPos - 2, "Octal literal in strict mode"); - out += String.fromCharCode(parseInt(octal, 8)); - tokPos += octal.length - 1; - } else { - switch (ch) { - case 110: out += "\n"; break; // 'n' -> '\n' - case 114: out += "\r"; break; // 'r' -> '\r' - case 120: out += String.fromCharCode(readHexChar(2)); break; // 'x' - case 117: out += String.fromCharCode(readHexChar(4)); break; // 'u' - case 85: out += String.fromCharCode(readHexChar(8)); break; // 'U' - case 116: out += "\t"; break; // 't' -> '\t' - case 98: out += "\b"; break; // 'b' -> '\b' - case 118: out += "\u000b"; break; // 'v' -> '\u000b' - case 102: out += "\f"; break; // 'f' -> '\f' - case 48: out += "\0"; break; // 0 -> '\0' - case 13: if (input.charCodeAt(tokPos) === 10) ++tokPos; // '\r\n' - case 10: // ' \n' - if (options.locations) { tokLineStart = tokPos; ++tokCurLine; } - break; - default: out += String.fromCharCode(ch); break; - } - } - } else { - if (ch === 13 || ch === 10 || ch === 8232 || ch === 8329) raise(tokStart, "Unterminated string constant"); - out += String.fromCharCode(ch); // '\' - ++tokPos; - } - } - } - - // Used to read character escape sequences ('\x', '\u', '\U'). - - function readHexChar(len) { - var n = readInt(16, len); - if (n === null) raise(tokStart, "Bad character escape sequence"); - return n; - } - - // Used to signal to callers of `readWord1` whether the word - // contained any escape sequences. This is needed because words with - // escape sequences must not be interpreted as keywords. - - var containsEsc; - - // Read an identifier, and return it as a string. Sets `containsEsc` - // to whether the word contained a '\u' escape. - // - // Only builds up the word character-by-character when it actually - // containeds an escape, as a micro-optimization. - - function readWord1() { - containsEsc = false; - var word, first = true, start = tokPos; - for (;;) { - var ch = input.charCodeAt(tokPos); - if (isIdentifierChar(ch)) { - if (containsEsc) word += input.charAt(tokPos); - ++tokPos; - } else if (ch === 92) { // "\" - if (!containsEsc) word = input.slice(start, tokPos); - containsEsc = true; - if (input.charCodeAt(++tokPos) != 117) // "u" - raise(tokPos, "Expecting Unicode escape sequence \\uXXXX"); - ++tokPos; - var esc = readHexChar(4); - var escStr = String.fromCharCode(esc); - if (!escStr) raise(tokPos - 1, "Invalid Unicode escape"); - if (!(first ? isIdentifierStart(esc) : isIdentifierChar(esc))) - raise(tokPos - 4, "Invalid Unicode escape"); - word += escStr; - } else { - break; - } - first = false; - } - return containsEsc ? word : input.slice(start, tokPos); - } - - // Read an identifier or keyword token. Will check for reserved - // words when necessary. - - function readWord() { - var word = readWord1(); - var type = _name; - if (!containsEsc) { - if (isKeyword(word)) type = keywordTypes[word]; - else if (options.forbidReserved && - (options.ecmaVersion === 3 ? isReservedWord3 : isReservedWord5)(word) || - strict && isStrictReservedWord(word)) - raise(tokStart, "The keyword '" + word + "' is reserved"); - } - return finishToken(type, word); - } - - // ## Parser - - // A recursive descent parser operates by defining functions for all - // syntactic elements, and recursively calling those, each function - // advancing the input stream and returning an AST node. Precedence - // of constructs (for example, the fact that `!x[1]` means `!(x[1])` - // instead of `(!x)[1]` is handled by the fact that the parser - // function that parses unary prefix operators is called first, and - // in turn calls the function that parses `[]` subscripts — that - // way, it'll receive the node for `x[1]` already parsed, and wraps - // *that* in the unary operator node. - // - // Acorn uses an [operator precedence parser][opp] to handle binary - // operator precedence, because it is much more compact than using - // the technique outlined above, which uses different, nesting - // functions to specify precedence, for all of the ten binary - // precedence levels that JavaScript defines. - // - // [opp]: http://en.wikipedia.org/wiki/Operator-precedence_parser - - // ### Parser utilities - - // Continue to the next token. - - function next() { - lastStart = tokStart; - lastEnd = tokEnd; - lastEndLoc = tokEndLoc; - readToken(); - } - - // Enter strict mode. Re-reads the next token to please pedantic - // tests ("use strict"; 010; -- should fail). - - function setStrict(strct) { - strict = strct; - tokPos = lastEnd; - while (tokPos < tokLineStart) { - tokLineStart = input.lastIndexOf("\n", tokLineStart - 2) + 1; - --tokCurLine; - } - skipSpace(); - readToken(); - } - - // Start an AST node, attaching a start offset. - - function node_t() { - this.type = null; - this.start = tokStart; - this.end = null; - } - - function node_loc_t() { - this.start = tokStartLoc; - this.end = null; - if (sourceFile !== null) this.source = sourceFile; - } - - function startNode() { - var node = new node_t(); - if (options.locations) - node.loc = new node_loc_t(); - if (options.ranges) - node.range = [tokStart, 0]; - return node; - } - - // Start a node whose start offset information should be based on - // the start of another node. For example, a binary operator node is - // only started after its left-hand side has already been parsed. - - function startNodeFrom(other) { - var node = new node_t(); - node.start = other.start; - if (options.locations) { - node.loc = new node_loc_t(); - node.loc.start = other.loc.start; - } - if (options.ranges) - node.range = [other.range[0], 0]; - - return node; - } - - // Finish an AST node, adding `type` and `end` properties. - - function finishNode(node, type) { - node.type = type; - node.end = lastEnd; - if (options.locations) - node.loc.end = lastEndLoc; - if (options.ranges) - node.range[1] = lastEnd; - return node; - } - - // Test whether a statement node is the string literal `"use strict"`. - - function isUseStrict(stmt) { - return options.ecmaVersion >= 5 && stmt.type === "ExpressionStatement" && - stmt.expression.type === "Literal" && stmt.expression.value === "use strict"; - } - - // Predicate that tests whether the next token is of the given - // type, and if yes, consumes it as a side effect. - - function eat(type) { - if (tokType === type) { - next(); - return true; - } - } - - // Test whether a semicolon can be inserted at the current position. - - function canInsertSemicolon() { - return !options.strictSemicolons && - (tokType === _eof || tokType === _braceR || newline.test(input.slice(lastEnd, tokStart))); - } - - // Consume a semicolon, or, failing that, see if we are allowed to - // pretend that there is a semicolon at this position. - - function semicolon() { - if (!eat(_semi) && !canInsertSemicolon()) unexpected(); - } - - // Expect a token of a given type. If found, consume it, otherwise, - // raise an unexpected token error. - - function expect(type) { - if (tokType === type) next(); - else unexpected(); - } - - // Raise an unexpected token error. - - function unexpected() { - raise(tokStart, "Unexpected token"); - } - - // Verify that a node is an lval — something that can be assigned - // to. - - function checkLVal(expr) { - if (expr.type !== "Identifier" && expr.type !== "MemberExpression") - raise(expr.start, "Assigning to rvalue"); - if (strict && expr.type === "Identifier" && isStrictBadIdWord(expr.name)) - raise(expr.start, "Assigning to " + expr.name + " in strict mode"); - } - - // ### Statement parsing - - // Parse a program. Initializes the parser, reads any number of - // statements, and wraps them in a Program node. Optionally takes a - // `program` argument. If present, the statements will be appended - // to its body instead of creating a new node. - - function parseTopLevel(program) { - lastStart = lastEnd = tokPos; - if (options.locations) lastEndLoc = new line_loc_t; - inFunction = strict = null; - labels = []; - readToken(); - - var node = program || startNode(), first = true; - if (!program) node.body = []; - while (tokType !== _eof) { - var stmt = parseStatement(); - node.body.push(stmt); - if (first && isUseStrict(stmt)) setStrict(true); - first = false; - } - return finishNode(node, "Program"); - } - - var loopLabel = {kind: "loop"}, switchLabel = {kind: "switch"}; - - // Parse a single statement. - // - // If expecting a statement and finding a slash operator, parse a - // regular expression literal. This is to handle cases like - // `if (foo) /blah/.exec(foo);`, where looking at the previous token - // does not help. - - function parseStatement() { - if (tokType === _slash) - readToken(true); - - var starttype = tokType, node = startNode(); - - // Most types of statements are recognized by the keyword they - // start with. Many are trivial to parse, some require a bit of - // complexity. - - switch (starttype) { - case _break: case _continue: - next(); - var isBreak = starttype === _break; - if (eat(_semi) || canInsertSemicolon()) node.label = null; - else if (tokType !== _name) unexpected(); - else { - node.label = parseIdent(); - semicolon(); - } - - // Verify that there is an actual destination to break or - // continue to. - for (var i = 0; i < labels.length; ++i) { - var lab = labels[i]; - if (node.label == null || lab.name === node.label.name) { - if (lab.kind != null && (isBreak || lab.kind === "loop")) break; - if (node.label && isBreak) break; - } - } - if (i === labels.length) raise(node.start, "Unsyntactic " + starttype.keyword); - return finishNode(node, isBreak ? "BreakStatement" : "ContinueStatement"); - - case _debugger: - next(); - semicolon(); - return finishNode(node, "DebuggerStatement"); - - case _do: - next(); - labels.push(loopLabel); - node.body = parseStatement(); - labels.pop(); - expect(_while); - node.test = parseParenExpression(); - semicolon(); - return finishNode(node, "DoWhileStatement"); - - // Disambiguating between a `for` and a `for`/`in` loop is - // non-trivial. Basically, we have to parse the init `var` - // statement or expression, disallowing the `in` operator (see - // the second parameter to `parseExpression`), and then check - // whether the next token is `in`. When there is no init part - // (semicolon immediately after the opening parenthesis), it is - // a regular `for` loop. - - case _for: - next(); - labels.push(loopLabel); - expect(_parenL); - if (tokType === _semi) return parseFor(node, null); - if (tokType === _var) { - var init = startNode(); - next(); - parseVar(init, true); - if (init.declarations.length === 1 && eat(_in)) - return parseForIn(node, init); - return parseFor(node, init); - } - var init = parseExpression(false, true); - if (eat(_in)) {checkLVal(init); return parseForIn(node, init);} - return parseFor(node, init); - - case _function: - next(); - return parseFunction(node, true); - - case _if: - next(); - node.test = parseParenExpression(); - node.consequent = parseStatement(); - node.alternate = eat(_else) ? parseStatement() : null; - return finishNode(node, "IfStatement"); - - case _return: - if (!inFunction) raise(tokStart, "'return' outside of function"); - next(); - - // In `return` (and `break`/`continue`), the keywords with - // optional arguments, we eagerly look for a semicolon or the - // possibility to insert one. - - if (eat(_semi) || canInsertSemicolon()) node.argument = null; - else { node.argument = parseExpression(); semicolon(); } - return finishNode(node, "ReturnStatement"); - - case _switch: - next(); - node.discriminant = parseParenExpression(); - node.cases = []; - expect(_braceL); - labels.push(switchLabel); - - // Statements under must be grouped (by label) in SwitchCase - // nodes. `cur` is used to keep the node that we are currently - // adding statements to. - - for (var cur, sawDefault; tokType != _braceR;) { - if (tokType === _case || tokType === _default) { - var isCase = tokType === _case; - if (cur) finishNode(cur, "SwitchCase"); - node.cases.push(cur = startNode()); - cur.consequent = []; - next(); - if (isCase) cur.test = parseExpression(); - else { - if (sawDefault) raise(lastStart, "Multiple default clauses"); sawDefault = true; - cur.test = null; - } - expect(_colon); - } else { - if (!cur) unexpected(); - cur.consequent.push(parseStatement()); - } - } - if (cur) finishNode(cur, "SwitchCase"); - next(); // Closing brace - labels.pop(); - return finishNode(node, "SwitchStatement"); - - case _throw: - next(); - if (newline.test(input.slice(lastEnd, tokStart))) - raise(lastEnd, "Illegal newline after throw"); - node.argument = parseExpression(); - semicolon(); - return finishNode(node, "ThrowStatement"); - - case _try: - next(); - node.block = parseBlock(); - node.handler = null; - if (tokType === _catch) { - var clause = startNode(); - next(); - expect(_parenL); - clause.param = parseIdent(); - if (strict && isStrictBadIdWord(clause.param.name)) - raise(clause.param.start, "Binding " + clause.param.name + " in strict mode"); - expect(_parenR); - clause.guard = null; - clause.body = parseBlock(); - node.handler = finishNode(clause, "CatchClause"); - } - node.finalizer = eat(_finally) ? parseBlock() : null; - if (!node.handler && !node.finalizer) - raise(node.start, "Missing catch or finally clause"); - return finishNode(node, "TryStatement"); - - case _var: - next(); - node = parseVar(node); - semicolon(); - return node; - - case _while: - next(); - node.test = parseParenExpression(); - labels.push(loopLabel); - node.body = parseStatement(); - labels.pop(); - return finishNode(node, "WhileStatement"); - - case _with: - if (strict) raise(tokStart, "'with' in strict mode"); - next(); - node.object = parseParenExpression(); - node.body = parseStatement(); - return finishNode(node, "WithStatement"); - - case _braceL: - return parseBlock(); - - case _semi: - next(); - return finishNode(node, "EmptyStatement"); - - // If the statement does not start with a statement keyword or a - // brace, it's an ExpressionStatement or LabeledStatement. We - // simply start parsing an expression, and afterwards, if the - // next token is a colon and the expression was a simple - // Identifier node, we switch to interpreting it as a label. - - default: - var maybeName = tokVal, expr = parseExpression(); - if (starttype === _name && expr.type === "Identifier" && eat(_colon)) { - for (var i = 0; i < labels.length; ++i) - if (labels[i].name === maybeName) raise(expr.start, "Label '" + maybeName + "' is already declared"); - var kind = tokType.isLoop ? "loop" : tokType === _switch ? "switch" : null; - labels.push({name: maybeName, kind: kind}); - node.body = parseStatement(); - labels.pop(); - node.label = expr; - return finishNode(node, "LabeledStatement"); - } else { - node.expression = expr; - semicolon(); - return finishNode(node, "ExpressionStatement"); - } - } - } - - // Used for constructs like `switch` and `if` that insist on - // parentheses around their expression. - - function parseParenExpression() { - expect(_parenL); - var val = parseExpression(); - expect(_parenR); - return val; - } - - // Parse a semicolon-enclosed block of statements, handling `"use - // strict"` declarations when `allowStrict` is true (used for - // function bodies). - - function parseBlock(allowStrict) { - var node = startNode(), first = true, strict = false, oldStrict; - node.body = []; - expect(_braceL); - while (!eat(_braceR)) { - var stmt = parseStatement(); - node.body.push(stmt); - if (first && isUseStrict(stmt)) { - oldStrict = strict; - setStrict(strict = true); - } - first = false - } - if (strict && !oldStrict) setStrict(false); - return finishNode(node, "BlockStatement"); - } - - // Parse a regular `for` loop. The disambiguation code in - // `parseStatement` will already have parsed the init statement or - // expression. - - function parseFor(node, init) { - node.init = init; - expect(_semi); - node.test = tokType === _semi ? null : parseExpression(); - expect(_semi); - node.update = tokType === _parenR ? null : parseExpression(); - expect(_parenR); - node.body = parseStatement(); - labels.pop(); - return finishNode(node, "ForStatement"); - } - - // Parse a `for`/`in` loop. - - function parseForIn(node, init) { - node.left = init; - node.right = parseExpression(); - expect(_parenR); - node.body = parseStatement(); - labels.pop(); - return finishNode(node, "ForInStatement"); - } - - // Parse a list of variable declarations. - - function parseVar(node, noIn) { - node.declarations = []; - node.kind = "var"; - for (;;) { - var decl = startNode(); - decl.id = parseIdent(); - if (strict && isStrictBadIdWord(decl.id.name)) - raise(decl.id.start, "Binding " + decl.id.name + " in strict mode"); - decl.init = eat(_eq) ? parseExpression(true, noIn) : null; - node.declarations.push(finishNode(decl, "VariableDeclarator")); - if (!eat(_comma)) break; - } - return finishNode(node, "VariableDeclaration"); - } - - // ### Expression parsing - - // These nest, from the most general expression type at the top to - // 'atomic', nondivisible expression types at the bottom. Most of - // the functions will simply let the function(s) below them parse, - // and, *if* the syntactic construct they handle is present, wrap - // the AST node that the inner parser gave them in another node. - - // Parse a full expression. The arguments are used to forbid comma - // sequences (in argument lists, array literals, or object literals) - // or the `in` operator (in for loops initalization expressions). - - function parseExpression(noComma, noIn) { - var expr = parseMaybeAssign(noIn); - if (!noComma && tokType === _comma) { - var node = startNodeFrom(expr); - node.expressions = [expr]; - while (eat(_comma)) node.expressions.push(parseMaybeAssign(noIn)); - return finishNode(node, "SequenceExpression"); - } - return expr; - } - - // Parse an assignment expression. This includes applications of - // operators like `+=`. - - function parseMaybeAssign(noIn) { - var left = parseMaybeConditional(noIn); - if (tokType.isAssign) { - var node = startNodeFrom(left); - node.operator = tokVal; - node.left = left; - next(); - node.right = parseMaybeAssign(noIn); - checkLVal(left); - return finishNode(node, "AssignmentExpression"); - } - return left; - } - - // Parse a ternary conditional (`?:`) operator. - - function parseMaybeConditional(noIn) { - var expr = parseExprOps(noIn); - if (eat(_question)) { - var node = startNodeFrom(expr); - node.test = expr; - node.consequent = parseExpression(true); - expect(_colon); - node.alternate = parseExpression(true, noIn); - return finishNode(node, "ConditionalExpression"); - } - return expr; - } - - // Start the precedence parser. - - function parseExprOps(noIn) { - return parseExprOp(parseMaybeUnary(noIn), -1, noIn); - } - - // Parse binary operators with the operator precedence parsing - // algorithm. `left` is the left-hand side of the operator. - // `minPrec` provides context that allows the function to stop and - // defer further parser to one of its callers when it encounters an - // operator that has a lower precedence than the set it is parsing. - - function parseExprOp(left, minPrec, noIn) { - var prec = tokType.binop; - if (prec != null && (!noIn || tokType !== _in)) { - if (prec > minPrec) { - var node = startNodeFrom(left); - node.left = left; - node.operator = tokVal; - next(); - node.right = parseExprOp(parseMaybeUnary(noIn), prec, noIn); - var node = finishNode(node, /&&|\|\|/.test(node.operator) ? "LogicalExpression" : "BinaryExpression"); - return parseExprOp(node, minPrec, noIn); - } - } - return left; - } - - // Parse unary operators, both prefix and postfix. - - function parseMaybeUnary(noIn) { - if (tokType.prefix) { - var node = startNode(), update = tokType.isUpdate; - node.operator = tokVal; - node.prefix = true; - next(); - node.argument = parseMaybeUnary(noIn); - if (update) checkLVal(node.argument); - else if (strict && node.operator === "delete" && - node.argument.type === "Identifier") - raise(node.start, "Deleting local variable in strict mode"); - return finishNode(node, update ? "UpdateExpression" : "UnaryExpression"); - } - var expr = parseExprSubscripts(); - while (tokType.postfix && !canInsertSemicolon()) { - var node = startNodeFrom(expr); - node.operator = tokVal; - node.prefix = false; - node.argument = expr; - checkLVal(expr); - next(); - expr = finishNode(node, "UpdateExpression"); - } - return expr; - } - - // Parse call, dot, and `[]`-subscript expressions. - - function parseExprSubscripts() { - return parseSubscripts(parseExprAtom()); - } - - function parseSubscripts(base, noCalls) { - if (eat(_dot)) { - var node = startNodeFrom(base); - node.object = base; - node.property = parseIdent(true); - node.computed = false; - return parseSubscripts(finishNode(node, "MemberExpression"), noCalls); - } else if (eat(_bracketL)) { - var node = startNodeFrom(base); - node.object = base; - node.property = parseExpression(); - node.computed = true; - expect(_bracketR); - return parseSubscripts(finishNode(node, "MemberExpression"), noCalls); - } else if (!noCalls && eat(_parenL)) { - var node = startNodeFrom(base); - node.callee = base; - node.arguments = parseExprList(_parenR, false); - return parseSubscripts(finishNode(node, "CallExpression"), noCalls); - } else return base; - } - - // Parse an atomic expression — either a single token that is an - // expression, an expression started by a keyword like `function` or - // `new`, or an expression wrapped in punctuation like `()`, `[]`, - // or `{}`. - - function parseExprAtom() { - switch (tokType) { - case _this: - var node = startNode(); - next(); - return finishNode(node, "ThisExpression"); - case _name: - return parseIdent(); - case _num: case _string: case _regexp: - var node = startNode(); - node.value = tokVal; - node.raw = input.slice(tokStart, tokEnd); - next(); - return finishNode(node, "Literal"); - - case _null: case _true: case _false: - var node = startNode(); - node.value = tokType.atomValue; - node.raw = tokType.keyword - next(); - return finishNode(node, "Literal"); - - case _parenL: - var tokStartLoc1 = tokStartLoc, tokStart1 = tokStart; - next(); - var val = parseExpression(); - val.start = tokStart1; - val.end = tokEnd; - if (options.locations) { - val.loc.start = tokStartLoc1; - val.loc.end = tokEndLoc; - } - if (options.ranges) - val.range = [tokStart1, tokEnd]; - expect(_parenR); - return val; - - case _bracketL: - var node = startNode(); - next(); - node.elements = parseExprList(_bracketR, true, true); - return finishNode(node, "ArrayExpression"); - - case _braceL: - return parseObj(); - - case _function: - var node = startNode(); - next(); - return parseFunction(node, false); - - case _new: - return parseNew(); - - default: - unexpected(); - } - } - - // New's precedence is slightly tricky. It must allow its argument - // to be a `[]` or dot subscript expression, but not a call — at - // least, not without wrapping it in parentheses. Thus, it uses the - - function parseNew() { - var node = startNode(); - next(); - node.callee = parseSubscripts(parseExprAtom(), true); - if (eat(_parenL)) node.arguments = parseExprList(_parenR, false); - else node.arguments = []; - return finishNode(node, "NewExpression"); - } - - // Parse an object literal. - - function parseObj() { - var node = startNode(), first = true, sawGetSet = false; - node.properties = []; - next(); - while (!eat(_braceR)) { - if (!first) { - expect(_comma); - if (options.allowTrailingCommas && eat(_braceR)) break; - } else first = false; - - var prop = {key: parsePropertyName()}, isGetSet = false, kind; - if (eat(_colon)) { - prop.value = parseExpression(true); - kind = prop.kind = "init"; - } else if (options.ecmaVersion >= 5 && prop.key.type === "Identifier" && - (prop.key.name === "get" || prop.key.name === "set")) { - isGetSet = sawGetSet = true; - kind = prop.kind = prop.key.name; - prop.key = parsePropertyName(); - if (tokType !== _parenL) unexpected(); - prop.value = parseFunction(startNode(), false); - } else unexpected(); - - // getters and setters are not allowed to clash — either with - // each other or with an init property — and in strict mode, - // init properties are also not allowed to be repeated. - - if (prop.key.type === "Identifier" && (strict || sawGetSet)) { - for (var i = 0; i < node.properties.length; ++i) { - var other = node.properties[i]; - if (other.key.name === prop.key.name) { - var conflict = kind == other.kind || isGetSet && other.kind === "init" || - kind === "init" && (other.kind === "get" || other.kind === "set"); - if (conflict && !strict && kind === "init" && other.kind === "init") conflict = false; - if (conflict) raise(prop.key.start, "Redefinition of property"); - } - } - } - node.properties.push(prop); - } - return finishNode(node, "ObjectExpression"); - } - - function parsePropertyName() { - if (tokType === _num || tokType === _string) return parseExprAtom(); - return parseIdent(true); - } - - // Parse a function declaration or literal (depending on the - // `isStatement` parameter). - - function parseFunction(node, isStatement) { - if (tokType === _name) node.id = parseIdent(); - else if (isStatement) unexpected(); - else node.id = null; - node.params = []; - var first = true; - expect(_parenL); - while (!eat(_parenR)) { - if (!first) expect(_comma); else first = false; - node.params.push(parseIdent()); - } - - // Start a new scope with regard to labels and the `inFunction` - // flag (restore them to their old value afterwards). - var oldInFunc = inFunction, oldLabels = labels; - inFunction = true; labels = []; - node.body = parseBlock(true); - inFunction = oldInFunc; labels = oldLabels; - - // If this is a strict mode function, verify that argument names - // are not repeated, and it does not try to bind the words `eval` - // or `arguments`. - if (strict || node.body.body.length && isUseStrict(node.body.body[0])) { - for (var i = node.id ? -1 : 0; i < node.params.length; ++i) { - var id = i < 0 ? node.id : node.params[i]; - if (isStrictReservedWord(id.name) || isStrictBadIdWord(id.name)) - raise(id.start, "Defining '" + id.name + "' in strict mode"); - if (i >= 0) for (var j = 0; j < i; ++j) if (id.name === node.params[j].name) - raise(id.start, "Argument name clash in strict mode"); - } - } - - return finishNode(node, isStatement ? "FunctionDeclaration" : "FunctionExpression"); - } - - // Parses a comma-separated list of expressions, and returns them as - // an array. `close` is the token type that ends the list, and - // `allowEmpty` can be turned on to allow subsequent commas with - // nothing in between them to be parsed as `null` (which is needed - // for array literals). - - function parseExprList(close, allowTrailingComma, allowEmpty) { - var elts = [], first = true; - while (!eat(close)) { - if (!first) { - expect(_comma); - if (allowTrailingComma && options.allowTrailingCommas && eat(close)) break; - } else first = false; - - if (allowEmpty && tokType === _comma) elts.push(null); - else elts.push(parseExpression(true)); - } - return elts; - } - - // Parse the next token as an identifier. If `liberal` is true (used - // when parsing properties), it will also convert keywords into - // identifiers. - - function parseIdent(liberal) { - var node = startNode(); - node.name = tokType === _name ? tokVal : (liberal && !options.forbidReserved && tokType.keyword) || unexpected(); - next(); - return finishNode(node, "Identifier"); - } - -}); - -(function (global) { - if (typeof window === "undefined") { - window = this; - } - //$data = window["$data"] || (window["$data"] = {}); - $data = window["$data"] || (window["$data"] = (function _data_handler() { - //console.log("@@@@", this); - if (this instanceof _data_handler) { - //console.log( - var type = _data_handler["implementation"].apply(this, arguments); - return new type(arguments[1]); - } else { - - return _data_handler["implementation"].apply(this, arguments) - } - })); -})(this); - -if (typeof console === 'undefined') { - console = { - warn: function () { }, - error: function () { }, - log: function () { }, - dir: function () { }, - time: function () { }, - timeEnd: function () { } - }; -} - -if (!console.warn) console.warn = function () { }; -if (!console.error) console.error = function () { }; - -(function ($data) { - /// - /// Collection of JayData services - /// - $data.__namespace = true; - $data.version = "JayData 1.3.6"; - $data.versionNumber = "1.3.6"; - $data.root = {}; - $data.Acorn = $data.Acorn || (typeof acorn == 'object' ? acorn : undefined); - $data.Esprima = $data.Esprima || (typeof esprima == 'object' ? esprima : undefined); - -})($data); - -// Do not remove this block, it is used by jsdoc -/** - @name $data.Base - @class base class -*/ -Exception = function(message, name, data) { - Error.call(this); - if (Error.captureStackTrace) - Error.captureStackTrace(this, this.constructor); - - this.name = name || "Exception"; - this.message = message; - this.data = data; - - //this.toString = function() { return JSON.stringify(this); }; - -} - -Exception.prototype.__proto__ = Error.prototype; - -Exception.prototype._getStackTrace = function () { - var callstack = []; - var isCallstackPopulated = false; - // unreachable code - //return; - /*try { - i.dont.exist += 0; - } - catch (e) { - if (e.stack) { // Firefox, Chrome - var lines = e.stack.split('\n'); - for (var i = 0, len = lines.length; i < len; i++) { - //if (lines[i].match(/^\s*[A-Za-z0-9\-_\$]+\(/)) { - if (lines[i].indexOf(" at ") >= 0) - callstack.push(lines[i]); - } - //Remove call to printStackTrace() - callstack.shift(); - //TODO: Remove call to new Exception( chain - //callstack.shift(); - isCallstackPopulated = true; - } - else if (window.opera && e.message) { //Opera - var lines = e.message.split('\n'); - for (var i = 0, len = lines.length; i < len; i++) { - if (lines[i].match(/^\s*[A-Za-z0-9\-_\$]+\(/)) { - var entry = lines[i]; - //Append next line also since it has the file info - if (lines[i + 1]) { - entry += ' at ' + lines[i + 1]; - i++; - } - callstack.push(entry); - } - } - //Remove call to printStackTrace() - callstack.shift(); - //TODO: Remove call to new Exception( chain - //callstack.shift(); - isCallstackPopulated = true; - } - } - - //if (!isCallstackPopulated) { //IE and Safari - // var currentFunction = arguments.callee.caller; - // while (currentFunction) { - // var fn = currentFunction.toString(); - // var fname = fn.substring(fn.indexOf("function") + 8, fn.indexOf('(')) || 'anonymous'; - // callstack.push(fname); - // if (currentFunction == currentFunction.caller) { - // Guard.raise("Infinite loop"); - // } - // currentFunction = currentFunction.caller; - // } - //} - return callstack.join("\n\r"); */ -}; -Guard = {}; -Guard.requireValue = function (name, value) { - if (typeof value === 'undefined' || value === null) { - Guard.raise(name + " requires a value other than undefined or null"); - } -}; - -Guard.requireType = function (name, value, typeOrTypes) { - var types = typeOrTypes instanceof Array ? typeOrTypes : [typeOrTypes]; - return types.some(function (item) { - switch (typeof item) { - case "string": - return typeof value === item; - case "function": - return value instanceof item; - default: - Guard.raise("Unknown type format : " + typeof item + " for: "+ name); - } - }); -}; - -Guard.raise = function(exception){ - if (typeof intellisense === 'undefined') { - if (exception instanceof Exception){ - console.error(exception.name + ':', exception.message + '\n', exception); - }else{ - console.error(exception); - } - throw exception; - } -}; - -Object.isNullOrUndefined = function (value) { - return value === undefined || value === null; -}; -(function ObjectMethodsForPreHTML5Browsers() { - - if (!Object.getOwnPropertyNames){ - Object.getOwnPropertyNames = function(o){ - var names = []; - - for (var i in o){ - if (o.hasOwnProperty(i)) names.push(i); - } - - return names; - }; - } - - if (!Object.create) { - Object.create = function (o) { - if (arguments.length > 1) { - Guard.raise(new Error('Object.create implementation only accepts the first parameter.')); - } - function F() { } - F.prototype = o; - return new F(); - }; - } - - if (!Object.keys) { - var hasOwnProperty = Object.prototype.hasOwnProperty, - hasDontEnumBug = !({ toString: null }).propertyIsEnumerable('toString'), - dontEnums = ['toString', - 'toLocaleString', - 'valueOf', - 'hasOwnProperty', - 'isPrototypeOf', - 'propertyIsEnumerable', - 'constructor'], - dontEnumsLength = dontEnums.length; - - Object.keys = function (obj) { - - ///Refactor to Assert.IsObjectOrFunction - if (typeof obj !== 'object' && typeof obj !== 'function' || obj === null) Guard.raise(new TypeError('Object.keys called on non-object')); - - var result = []; - - for (var prop in obj) { - if (hasOwnProperty.call(obj, prop)) { - result.push(prop); - } - } - - if (hasDontEnumBug) { - for (var i = 0; i < dontEnumsLength; i++) { - if (hasOwnProperty.call(obj, dontEnums[i])) { - result.push(dontEnums[i]); - } - } - } - - return result; - }; - } - - if (!Object.defineProperty) { - Object.defineProperty = function (obj, propName, propDef) { - obj[propName] = propDef.value || {}; - }; - } - - if (!Object.defineProperties) { - Object.defineProperties = function (obj, defines) { - for (var i in defines) { - if(defines.hasOwnProperty(i)) - obj[i] = defines[i].value || {}; - } - }; - } - - if (!Array.prototype.forEach) { - Array.prototype.forEach = function (handler, thisArg) { - for (var i = 0, l = this.length; i < l; i++) { - if (thisArg) { handler.call(thisArg, this[i], i, this); } - else { handler(this[i], i, this); }; - }; - }; - }; - - if (!Array.prototype.filter) { - Array.prototype.filter = function (handler, thisArg) { - var result = []; - for (var i = 0, l = this.length; i < l; i++) { - var r = thisArg ? - handler.call(thisArg, this[i], i, this) : - handler(this[i], i, this); - if (r === true) { - result.push(this[i]); - } - } - return result; - }; - } - - if (!Array.prototype.map) { - Array.prototype.map = function (handler, thisArg) { - var result = []; - for (var i = 0, l = this.length; i < l; i++) { - var r = thisArg ? - handler.call(thisArg, this[i], i, this) : - handler(this[i], i, this); - result.push(r); - } - return result; - }; - } - - if (!Array.prototype.some) { - Array.prototype.some = function (handler, thisArg) { - for (var i = 0, l = this.length; i < l; i++) { - var r = thisArg ? - handler.call(thisArg, this[i], i, this) : - handler(this[i], i, this); - if (r) { return true; } - - } - return false; - }; - } - - if (!Array.prototype.indexOf) { - Array.prototype.indexOf = function (item, from) { - for (var i = 0, l = this.length; i < l; i++) { - if (this[i] === item) { - return i; - }; - }; - return -1; - }; - } - - if (!String.prototype.trimLeft) { - String.prototype.trimLeft = function () { - return this.replace(/^\s+/, ""); - } - } - - if (!String.prototype.trimRight) { - String.prototype.trimRight = function () { - return this.replace(/\s+$/, ""); - } - } - - if (!Function.prototype.bind) { - Function.prototype.bind = function (oThis) { - if (typeof this !== "function") { - // closest thing possible to the ECMAScript 5 internal IsCallable function - throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable"); - } - - var aArgs = Array.prototype.slice.call(arguments, 1), - fToBind = this, - fNOP = function () { }, - fBound = function () { - return fToBind.apply(this instanceof fNOP && oThis - ? this - : oThis, - aArgs.concat(Array.prototype.slice.call(arguments))); - }; - - fNOP.prototype = this.prototype; - fBound.prototype = new fNOP(); - - return fBound; - }; - } - - if (typeof Uint8Array == 'undefined'){ - Uint8Array = function(v){ - if (v instanceof Uint8Array) return v; - var self = this; - var buffer = Array.isArray(v) ? v : new Array(v); - this.length = buffer.length; - this.byteLength = this.length; - this.byteOffset = 0; - this.buffer = { byteLength: self.length }; - var getter = function(index){ - return buffer[index]; - }; - var setter = function(index, value){ - buffer[index] = (value | 0) & 0xff; - }; - var makeAccessor = function(i){ - buffer[i] = buffer[i] || 0; - Object.defineProperty(self, i, { - enumerable: true, - configurable: false, - get: function(){ - if (isNaN(+i) || ((i | 0) < 0 || (i | 0) >= self.length)){ - try{ - if (typeof document != 'undefined') document.createTextNode("").splitText(1); - return new RangeError("INDEX_SIZE_ERR"); - }catch(e){ - return e; - } - } - return getter(i); - }, - set: function(v){ - if (isNaN(+i) || ((i | 0) < 0 || (i | 0) >= self.length)){ - try{ - if (typeof document != 'undefined') document.createTextNode("").splitText(1); - return new RangeError("INDEX_SIZE_ERR"); - }catch(e){ - return e; - } - } - setter(i | 0, v); - } - }); - }; - for (var i = 0; i < self.length; i++){ - makeAccessor(i); - } - }; - } - -})(); -(function init($data, global) { - - function il(msg) { - if (typeof intellisense !== 'undefined') { - if (!intellisense.i) { - intellisense.i = 0; - } - intellisense.i = intellisense.i + 1; - intellisense.logMessage(msg + ":" + intellisense.i); - } - } - - function MemberDefinition(memberDefinitionData, definedClass) { - - ///* - ///* - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - ///[false] if false value is stored in initData, otherwise on the object - ///[true] if set to false propertyChange events are not raise and property tracking is disabled - - this.kind = MemberTypes.property; - //this.definedBy = definedClass; - Object.defineProperty(this, 'definedBy', { value: definedClass, enumerable: false, configurable: false, writable: false }); - if (memberDefinitionData) { - if (typeof memberDefinitionData === 'function' || typeof memberDefinitionData.asFunction === 'function') { - this.method = memberDefinitionData; - this.kind = MemberTypes.method; - } else { - this.enumerable = true; - this.configurable = true; - if (typeof memberDefinitionData === "number") { - this.value = memberDefinitionData; - this.type = $data.Number; - this.dataType = $data.Number; - } else if (typeof memberDefinitionData === "string") { - this.value = memberDefinitionData; - this.dataType = $data.String; - this.type = $data.String; - } else { - for (var item in memberDefinitionData) { - if (memberDefinitionData.hasOwnProperty(item)) { - this[item] = memberDefinitionData[item]; - } - } - } - } - if (this.type !== undefined) { - this.dataType = this.dataType || this.type; - } else { - this.type = this.dataType; - } - - this.originalType = this.type; - if (this.elementType !== undefined) { - this.originalElementType = this.elementType; - } - } - } - MemberDefinition.prototype.createPropertyDescriptor = function (classFunction, value) { - /// - var pd = this; - var result = { - enumerable: this.enumerable == undefined ? true : this.enumerable, - configurable: this.configurable == undefined ? true : this.configurable - }; - if (this.set && this.get) { - result.set = this.set; - result.get = this.get; - } else if ("value" in this || value) { - result.value = value || this.value; - //TODO - //result.writable = this.writable; - result.writable = true; - } - else { - result.set = function (value) { this.storeProperty(pd, value); }; - result.get = function () { return this.retrieveProperty(pd); }; - } - return result; - }; - MemberDefinition.prototype.createStorePropertyDescriptor = function (value) { - var pd = this; - return { enumerable: false, writable: true, configurable: pd.configurable, value: value }; - }; - MemberDefinition.prototype.createGetMethod = function () { - var pd = this; - return { - enumerable: false, writable: false, configurable: false, - value: function (callback, tran) { return this.getProperty(pd, callback, tran); } - }; - }; - MemberDefinition.prototype.createSetMethod = function () { - var pd = this; - return { - enumerable: false, writable: false, configurable: false, - value: function (value, callback, tran) { return this.setProperty(pd, value, callback, tran); } - }; - }; - MemberDefinition.translateDefinition = function (memDef, name, classFunction) { - var holder = classFunction; - var memberDefinition; - - if (memDef.type && Container.isTypeRegistered(memDef.type)) { - holder = Container.resolveType(memDef.type); - if (typeof holder.translateDefinition === 'function') { - memberDefinition = holder.translateDefinition.apply(holder, arguments); - memberDefinition.name = memberDefinition.name || name; - } else { - holder = classFunction; - } - } - - - if (!(memberDefinition instanceof MemberDefinition)) { - memberDefinition = new MemberDefinition(memberDefinition || memDef, holder); - memberDefinition.name = name; - } - classFunction.resolverThunks = classFunction.resolverThunks || []; - classFunction.childResolverThunks = classFunction.childResolverThunks || []; - - - var t = memberDefinition.type; - var et = memberDefinition.elementType; - - function addChildThunk(referencedType) { - if (referencedType && referencedType.isAssignableTo && $data.Entity && referencedType.isAssignableTo($data.Entity)) { - classFunction.childResolverThunks.push(function () { - if (referencedType.resolveForwardDeclarations) { - referencedType.resolveForwardDeclarations(); - } - }); - } - } - - addChildThunk(t); - addChildThunk(et); - - if ("string" === typeof t) { - if ("@" === t[0]) { - memberDefinition.type = t.substr(1); - memberDefinition.dataType = t.substr(1); - } else { - //forward declared types get this callback when type is registered - classFunction.resolverThunks.push(function () { - var rt = classFunction.container.resolveType(t); - addChildThunk(rt); - memberDefinition.type = rt; - memberDefinition.dataType = rt; - }); - } - } - - if (et) { - if ("string" === typeof et) { - if ("@" === et[0]) { - memberDefinition.elementType = et.substr(1); - } else { - //forward declared types get this callback when type is registered - classFunction.resolverThunks.push(function () { - var rt = classFunction.container.resolveType(et); - addChildThunk(rt); - memberDefinition.elementType = rt; - }); - - } - } - } - - - //if (!classFunction) - - classFunction.resolveForwardDeclarations = function () { - classFunction.resolveForwardDeclarations = function () { }; - $data.Trace.log("resolving: " + classFunction.fullName); - this.resolverThunks.forEach(function (thunk) { - thunk(); - }); - //this.resolverThunks = []; - this.childResolverThunks.forEach(function (thunk) { - thunk(); - }); - //this.childResolverThunks = []; - } - - return memberDefinition; - }; - - MemberDefinition.prototype.toJSON = function () { - var property = {}; - for (var name in this) { - if (name !== 'defineBy' && name !== 'storageModel') { - if ((name === 'type' || name === 'dataType') && (this[name] && typeof this[name] === 'function')) { - try { - property[name] = Container.resolveName(this[name]); - } catch (e) { - property[name] = this[name]; - } - } else { - property[name] = this[name]; - } - } - } - return property; - } - - //TODO global/window - $data.MemberDefinition = window["MemberDefinition"] = MemberDefinition; - - var memberDefinitionPrefix = '$'; - function MemberDefinitionCollection() { }; - MemberDefinitionCollection.prototype = { - clearCache: function () { - this.arrayCache = undefined; - this.pubMapPropsCache = undefined; - this.keyPropsCache = undefined; - this.propByTypeCache = undefined; - this.pubMapMethodsCache = undefined; - this.pubMapPropNamesCache = undefined; - }, - asArray: function () { - if (!this.arrayCache) { - this.arrayCache = []; - for (var i in this) { - if (i.indexOf(memberDefinitionPrefix) === 0) - this.arrayCache.push(this[i]); - } - } - return this.arrayCache; - }, - getPublicMappedProperties: function () { - if (!this.pubMapPropsCache) { - this.pubMapPropsCache = []; - for (var i in this) { - if (i.indexOf(memberDefinitionPrefix) === 0 && this[i].kind == 'property' && !this[i].notMapped && this[i].enumerable) - this.pubMapPropsCache.push(this[i]); - } - } - return this.pubMapPropsCache;// || (this.pubMapPropsCache = this.asArray().filter(function (m) { return m.kind == 'property' && !m.notMapped && m.enumerable; })); - }, - getPublicMappedPropertyNames: function () { - if (!this.pubMapPropNamesCache) { - this.pubMapPropNamesCache = []; - for (var i in this) { - if (i.indexOf(memberDefinitionPrefix) === 0 && this[i].kind == 'property' && !this[i].notMapped && this[i].enumerable) - this.pubMapPropNamesCache.push(this[i].name); - } - } - return this.pubMapPropNamesCache; - }, - getKeyProperties: function () { - if (!this.keyPropsCache) { - this.keyPropsCache = []; - for (var i in this) { - if (i.indexOf(memberDefinitionPrefix) === 0 && this[i].kind == 'property' && this[i].key) - this.keyPropsCache.push(this[i]); - } - } - return this.keyPropsCache; - //return this.keyPropsCache || (this.keyPropsCache = this.asArray().filter(function (m) { return m.kind == 'property' && m.key; })); - }, - getPublicMappedMethods: function () { - if (!this.pubMapMethodsCache) { - this.pubMapMethodsCache = []; - for (var i in this) { - if (i.indexOf(memberDefinitionPrefix) === 0 && this[i].kind == 'method' && this[i].method/* && this.hasOwnProperty(i)*/) - this.pubMapMethodsCache.push(this[i]); - } - } - return this.pubMapMethodsCache; - }, - getPropertyByType: function (type) { - if (!this.propByTypeCache) { - this.propByTypeCache = []; - for (var i in this) { - if (i.indexOf(memberDefinitionPrefix) === 0 && this[i].dataType == type) - this.propByTypeCache.push(this[i]); - } - } - return this.propByTypeCache; - //return this.propByTypeCache || (this.propByTypeCache = this.asArray().filter(function (m) { return m.dataType == type; })); - }, - getMember: function (name) { return this[memberDefinitionPrefix + name]; }, - setMember: function (value) { this[memberDefinitionPrefix + value.name] = value; } - }; - MemberDefinitionCollection.prototype.constructor = MemberDefinitionCollection; - $data.MemberDefinitionCollection = window["MemberDefinitionCollection"] = MemberDefinitionCollection; - - function ClassEngineBase() { - this.classNames = {}; - } - - function MemberTypes() { - /// - /// - /// - /// - } - MemberTypes.__enum = true; - - MemberTypes.method = "method"; - MemberTypes.property = "property"; - MemberTypes.navProperty = "navProperty"; - MemberTypes.complexProperty = "complexProperty"; - MemberTypes.field = "field"; - - $data.MemberTypes = MemberTypes; - - //function classToJSON() { - // var ret = {}; - // for (var i in this) { - // if (this.hasOwnProperty(i)) { - // ret[i] = this[i]; - // } - // } - // return ret; - //} - //$data.Base.toJSON = classToJSON; - - ClassEngineBase.prototype = { - - //getClass: function (classReference) { - //}, - - //getProperties: function (classFunction) { - // return classFunction.propertyDefinitions; - //}, - - define: function (className, baseClass, container, instanceDefinition, classDefinition) { - /// - /// Creates a Jaydata type - /// Name of the class - /// Basetype of the class - /// - /// Class definition (properties, methods, etc) - /// Class static definition - /// - /// - /// var t = new $data.Class.define('Types.A', $data.Base, null, { - /// constructor: function(){ }, - /// func1: function(){ }, - /// member1: { type: 'string' } - /// }, { - /// staticFunc1: function() {} - /// }) - /// - /// - /// - - return this.defineEx(className, [{ type: baseClass }], container, instanceDefinition, classDefinition); - }, - defineEx: function (className, baseClasses, container, instanceDefinition, classDefinition) { - /// - /// Creates a Jaydata type - /// Name of the class - /// Basetypes of the class. First is a real base, others are mixins - /// - /// Class definition (properties, methods, etc) - /// Class static definition - /// - /// - /// var t = new $data.Class.define('Types.A', [$data.Base, $data.Mixin1, $data.Mixin2], null, { - /// constructor: function(){ }, - /// func1: function(){ }, - /// member1: { type: 'string' } - /// }, { - /// staticFunc1: function() {} - /// }) - /// - /// - /// - /// - /// Creates a Jaydata type - /// Name of the class - /// Basetypes of the class. First is a real base, others are mixins or propagations - /// - /// Class definition (properties, methods, etc) - /// Class static definition - /// - /// - /// var t = new $data.Class.define('Types.A', [ - /// { type: $data.Base, params: [1, 'secondParameterValue', new ConstructorParameter(0)] }, - /// { type: $data.Mixin1, }, - /// { type: $data.Mixin2, }, - /// { type: $data.Propagation1, params: [new ConstructorParameter(1)], propagateTo:'Propagation1' }, - /// { type: $data.Propagation2, params: ['firstParameterValue'], propagateTo:'Propagation2' } - /// ], null, { - /// constructor: function(){ }, - /// func1: function(){ }, - /// member1: { type: 'string' } - /// }, { - /// staticFunc1: function() {} - /// }) - /// - /// - /// - - container = container || $data.Container; - - if (baseClasses.length == 0) { - baseClasses.push({ type: $data.Base }); - } else if (baseClasses.length > 0 && !baseClasses[0].type) { - baseClasses[0].type = $data.Base; - } - for (var i = 0, l = baseClasses.length; i < l; i++) { - if (typeof baseClasses[i] === 'function') - baseClasses[i] = { type: baseClasses[i] }; - } - - var providedCtor = instanceDefinition ? instanceDefinition.constructor : undefined; - - var classNameParts = className.split('.'); - var shortClassName = classNameParts.splice(classNameParts.length - 1, 1)[0]; - - var root = container === $data.Container ? window : container; - for (var i = 0; i < classNameParts.length; i++) { - var part = classNameParts[i]; - if (!root[part]) { - var ns = {}; - ns.__namespace = true; - root[part] = ns; - } - root = root[part]; - } - - - var classFunction = null; - classFunction = this.classFunctionBuilder(shortClassName, baseClasses, classDefinition, instanceDefinition); - classFunction.fullName = className; - classFunction.namespace = classNameParts.join('.'); //classname splitted - classFunction.name = shortClassName; - classFunction.container = container; - classFunction.container.registerType(className, classFunction); - - this.buildType(classFunction, baseClasses, instanceDefinition, classDefinition); - - - - if (typeof intellisense !== 'undefined') { - if (instanceDefinition && instanceDefinition.constructor) { - intellisense.annotate(classFunction, instanceDefinition.constructor); - } - } - - root[shortClassName] = this.classNames[className] = classFunction; - //classFunction.toJSON = classToJSON; - var baseCount = classFunction.baseTypes.length; - for (var i = 0; i < baseCount; i++) { - var b = classFunction.baseTypes[i]; - if ("inheritedTypeProcessor" in b) { - b.inheritedTypeProcessor(classFunction); - } - } - //classFunction.prototype.constructor = instanceDefinition.constructor; - //classFunction.constructor = instanceDefinition.constructor; - //classFunction.toJSON = function () { return classFunction.memberDefinitions.filter( function(md) { return md; }; - return classFunction; - }, - classFunctionBuilder: function (name, base, classDefinition, instanceDefinition) { - var body = this.bodyBuilder(base, classDefinition, instanceDefinition); - return new Function('base', 'classDefinition', 'instanceDefinition', 'name', 'return function ' + name + ' (){ ' + - body + ' \n}; ')(base, classDefinition, instanceDefinition, name); - }, - bodyBuilder: function (bases, classDefinition, instanceDefinition) { - var mixin = ''; - var body = ''; - var propagation = ''; - - for (var i = 0, l = bases.length; i < l; i++) { - var base = bases[i]; - var index = i; - if (index == 0) { //ctor func - if (base && base.type && base.type !== $data.Base && base.type.fullName) { - body += ' var baseArguments = $data.typeSystem.createCtorParams(arguments, base[' + index + '].params, this); \n'; - body += ' ' + base.type.fullName + '.apply(this, baseArguments); \n'; - } - } else { - if (base && base.type && base.propagateTo) { - //propagation - propagation += ' ' + (!propagation ? 'var ' : '' + '') + 'propagationArguments = $data.typeSystem.createCtorParams(arguments, base[' + index + '].params, this); \n'; - propagation += ' this["' + base.propagateTo + '"] = Object.create(' + base.type.fullName + '.prototype); \n' + - ' ' + base.type.fullName + '.apply(this["' + base.propagateTo + '"], propagationArguments); \n'; - } - else if (base && base.type && base.type.memberDefinitions && base.type.memberDefinitions.$constructor && !base.propagateTo) { - //mixin - mixin += ' ' + base.type.fullName + '.memberDefinitions.$constructor.method.apply(this); \n'; - } - } - } - if (instanceDefinition && instanceDefinition.constructor != Object) - body += " instanceDefinition.constructor.apply(this, arguments); \n"; - - return '\n //mixins \n' + mixin + '\n //construction \n' + body + '\n //propagations \n' + propagation; - }, - - buildType: function (classFunction, baseClasses, instanceDefinition, classDefinition) { - var baseClass = baseClasses[0].type; - classFunction.inheritsFrom = baseClass; - - if (baseClass) { - classFunction.prototype = Object.create(baseClass.prototype); - classFunction.memberDefinitions = Object.create(baseClass.memberDefinitions || new MemberDefinitionCollection()); - classFunction.memberDefinitions.clearCache(); - - var staticDefs = baseClass.staticDefinitions; - if (staticDefs) { - staticDefs = staticDefs.asArray(); - if (staticDefs) { - for (var i = 0; i < staticDefs.length; i++) { - this.buildMember(classFunction, staticDefs[i], undefined, 'staticDefinitions'); - } - } - } - classFunction.baseTypes = baseClass.baseTypes ? [].concat(baseClass.baseTypes) : []; - for (var i = 0; i < baseClasses.length; i++) { - classFunction.baseTypes.push(baseClasses[i].type); - } - //classFunction.baseTypes = (baseClass.baseTypes || []).concat(baseClasses.map(function (base) { return base.type; })); - if (!classFunction.isAssignableTo) { - Object.defineProperty(classFunction, "isAssignableTo", { - value: function (type) { - return this === type || this.baseTypes.indexOf(type) >= 0; - }, - writable: false, - enumerable: false, - configurable: false - }); - } - } - - if (classDefinition) { - this.buildStaticMembers(classFunction, classDefinition); - - if (classDefinition.constructor) - classFunction.classConstructor = classDefinition.constructor; - } - - if (instanceDefinition) { - this.buildInstanceMembers(classFunction, instanceDefinition); - } - - var mixins = [].concat(baseClasses); - mixins.shift(); - if (Object.keys(mixins).length > 0) - this.buildInstanceMixins(classFunction, mixins); - - classFunction.__class = true; - - classFunction.prototype.constructor = classFunction; - - Object.defineProperty(classFunction.prototype, "getType", { - value: function () { - return classFunction; - }, - writable: false, - enumerable: false, - configurable: false - }); - }, - - addMethod: function (holder, name, method, propagation) { - if (!propagation || (typeof intellisense !== 'undefined')) { - holder[name] = method; - } else { - holder[name] = function () { - return method.apply(this[propagation], arguments); - }; - } - }, - - addProperty: function (holder, name, propertyDescriptor, propagation) { - - //holder[name] = {}; - - if (propagation) { - propertyDescriptor.configurable = true; - if (propertyDescriptor.get) { - var origGet = propertyDescriptor.get; - propertyDescriptor.get = function () { - if (!this[propagation]) - Guard.raise(new Exception("not inicialized")); - return origGet.apply(this[propagation], arguments); - }; - } - if (propertyDescriptor.set) { - var origSet = propertyDescriptor.set; - propertyDescriptor.set = function () { - if (!this[propagation]) - Guard.raise(new Exception("not inicialized")); - origSet.apply(this[propagation], arguments); - }; - } - } - - Object.defineProperty(holder, name, propertyDescriptor); - }, - - addField: function (holder, name, field) { - Guard.raise("not implemented"); - }, - - buildMethod: function (classFunction, memberDefinition, propagation) { - ///The object that will receive member - ///the newly added member - var holder = memberDefinition.classMember ? classFunction : classFunction.prototype; - this.addMethod(holder, memberDefinition.name, memberDefinition.method, propagation); - }, - - buildProperty: function (classFunction, memberDefinition, propagation) { - ///The object that will receive member - ///the newly added member - var holder = memberDefinition.classMember ? classFunction : classFunction.prototype; - var pd = memberDefinition.createPropertyDescriptor(classFunction); - this.addProperty(holder, memberDefinition.name, pd, propagation); - - //if lazyload TODO - if (!memberDefinition.classMember && classFunction.__setPropertyfunctions == true && memberDefinition.withoutGetSetMethod !== true && - !('get_' + memberDefinition.name in holder || 'set_' + memberDefinition.name in holder)) { - var pdGetMethod = memberDefinition.createGetMethod(); - this.addProperty(holder, 'get_' + memberDefinition.name, pdGetMethod, propagation); - - var pdSetMethod = memberDefinition.createSetMethod(); - this.addProperty(holder, 'set_' + memberDefinition.name, pdSetMethod, propagation); - } - }, - - - buildMember: function (classFunction, memberDefinition, propagation, memberCollectionName) { - /// - memberCollectionName = memberCollectionName || 'memberDefinitions'; - classFunction[memberCollectionName] = classFunction[memberCollectionName] || new MemberDefinitionCollection(); - classFunction[memberCollectionName].setMember(memberDefinition); - - switch (memberDefinition.kind) { - case MemberTypes.method: - this.buildMethod(classFunction, memberDefinition, propagation); - break; - case MemberTypes.navProperty: - case MemberTypes.complexProperty: - case MemberTypes.property: - this.buildProperty(classFunction, memberDefinition, propagation); - break; - default: Guard.raise("Unknown member type: " + memberDefinition.kind + "," + memberDefinition.name); - } - }, - - buildStaticMembers: function (classFunction, memberListDefinition) { - ///The class constructor that will be extended - /// - var t = this; - for (var item in memberListDefinition) { - if (memberListDefinition.hasOwnProperty(item)) { - var memberDefinition = MemberDefinition.translateDefinition(memberListDefinition[item], item, classFunction); - memberDefinition.classMember = true; - t.buildMember(classFunction, memberDefinition, undefined, 'staticDefinitions'); - } - } - }, - - buildInstanceMembers: function (classFunction, memberListDefinition) { - ///The class constructor whose prototype will be extended - /// - ///pinning t outside of the closure seems actually faster then passing in the this and referencing - var t = this; - for (var item in memberListDefinition) { - if (memberListDefinition.hasOwnProperty(item)) { - var memberDefinition = MemberDefinition.translateDefinition(memberListDefinition[item], item, classFunction); - t.buildMember(classFunction, memberDefinition, undefined, 'memberDefinitions'); - } - } - }, - - copyMembers: function (sourceType, targetType) { - /// - /// - function il(msg) { - if (typeof intellisense === 'undefined') { - return; - } - intellisense.logMessage(msg); - } - - Object.keys(sourceType.prototype).forEach(function (item, i, src) { - if (item !== 'constructor' && item !== 'toString') { - il("copying item:" + item); - targetType.prototype[item] = sourceType[item]; - } - }); - }, - - buildInstanceMixins: function (classFunction, mixinList) { - ///The class constructor whose prototype will be extended - /// - - classFunction.mixins = classFunction.mixins || []; - classFunction.propagations = classFunction.propagations || []; - - for (var i = 0; i < mixinList.length; i++) { - var item = mixinList[i]; - //if (classFunction.memberDefinitions.getMember(item.type.name)) { - if (item.propagateTo) { - this.buildInstancePropagation(classFunction, item); - classFunction.propagations.push(item); - classFunction.propagations[item.type.name] = true; - } else { - this.buildInstanceMixin(classFunction, item); - classFunction.mixins.push(item); - classFunction.mixins[item.type.name] = true; - } - }; - }, - buildInstanceMixin: function (classFunction, typeObj) { - ///The class constructor whose prototype will be extended - /// - - var memberDefs = typeObj.type.memberDefinitions.asArray(); - for (var i = 0, l = memberDefs.length; i < l; i++) { - var itemName = memberDefs[i].name; - if (itemName !== 'constructor' && !classFunction.memberDefinitions.getMember(itemName)) { - this.buildMember(classFunction, memberDefs[i]); - } - } - - if (typeObj.type.staticDefinitions) { - var staticDefs = typeObj.type.staticDefinitions.asArray(); - for (var i = 0, l = staticDefs.length; i < l; i++) { - var itemName = staticDefs[i].name; - if (itemName !== 'constructor' && !classFunction.memberDefinitions.getMember(itemName)) { - this.buildMember(classFunction, staticDefs[i], undefined, 'staticDefinitions'); - } - } - } - }, - buildInstancePropagation: function (classFunction, typeObj) { - ///The class constructor whose prototype will be extended - /// - - var memberDefs = typeObj.type.memberDefinitions.asArray(); - for (var i = 0, l = memberDefs.length; i < l; i++) { - var itemName = memberDefs[i].name; - if (itemName !== 'constructor' && !classFunction.memberDefinitions.getMember(itemName)) { - this.buildMember(classFunction, memberDefs[i], typeObj.propagateTo); - } - } - } - - }; - - $data.Class = Class = new ClassEngineBase(); - - //(function (global) { - global = window; - function ContainerCtor(parentContainer) { - var parent = parentContainer; - if (parent) { - parent.addChildContainer(this); - } - - var classNames = {}; - var consolidatedClassNames = []; - var classTypes = []; - - this.classNames = classNames; - this.consolidatedClassNames = consolidatedClassNames; - this.classTypes = classTypes; - - var mappedTo = []; - this.mappedTo = mappedTo; - - var self = this; - - this["holder"] = null; - - var IoC = function (type, parameters) { - var t = self.resolveType(type); - var inst = Object.create(t.prototype); - t.apply(inst, parameters); - return inst; - }; - - var pendingResolutions = {}; - this.pendingResolutions = pendingResolutions; - - function addPendingResolution(name, onResolved) { - pendingResolutions[name] = pendingResolutions[name] || []; - pendingResolutions[name].push(onResolved); - } - - this.addChildContainer = function (container) { - //children.push(container); - } - - this.createInstance = function (type, parameters) { return IoC(type, parameters); }; - - this.mapType = function (aliasTypeOrName, realTypeOrName) { - Guard.requireValue("aliasType", aliasTypeOrName); - Guard.requireValue("realType", realTypeOrName); - var aliasT = this.getType(aliasTypeOrName); - var realT = this.getType(realTypeOrName); - var aliasPos = classTypes.indexOf(aliasT); - var realPos = classTypes.indexOf(realT); - mappedTo[aliasPos] = realPos; - }, - - //this.resolve = function (type, parameters) { - // var classFunction = this.resolveType(type, parameters); - // return new classFunction(parameters); - //}; - - - - this.isPrimitiveType = function (type) { - var t = this.resolveType(type); - - switch (true) { - case t === Number: - case t === String: - case t === Date: - case t === Boolean: - case t === Array: - case t === Object: - - case t === $data.Number: - case t === $data.Integer: - case t === $data.Date: - case t === $data.String: - case t === $data.Boolean: - case t === $data.Array: - case t === $data.Object: - case t === $data.Guid: - - case t === $data.Byte: - case t === $data.SByte: - case t === $data.Decimal: - case t === $data.Float: - case t === $data.Int16: - case t === $data.Int32: - case t === $data.Int64: - case t === $data.DateTimeOffset: - case t === $data.Time: - - case t === $data.SimpleBase: - case t === $data.Geospatial: - case t === $data.GeographyBase: - case t === $data.GeographyPoint: - case t === $data.GeographyLineString: - case t === $data.GeographyPolygon: - case t === $data.GeographyMultiPoint: - case t === $data.GeographyMultiLineString: - case t === $data.GeographyMultiPolygon: - case t === $data.GeographyCollection: - case t === $data.GeometryBase: - case t === $data.GeometryPoint: - case t === $data.GeometryLineString: - case t === $data.GeometryPolygon: - case t === $data.GeometryMultiPoint: - case t === $data.GeometryMultiLineString: - case t === $data.GeometryMultiPolygon: - case t === $data.GeometryCollection: - - return true; - default: - return false; - } - - //return t === Number || t === String || t === Date || t === String || t === Boolean || t === Array || t === Object || - // t === $data.Number || t === $data.Integer || t === $data.Date || t === $data.String || t === $data.Boolean || t === $data.Array || t === $data.Object || - // t === $data.GeographyPoint || t === $data.Guid; - }; - - - this.resolveName = function (type) { - var t = this.resolveType(type); - var tPos = classTypes.indexOf(t); - return consolidatedClassNames[tPos]; - }; - - this.resolveType = function (typeOrName, onResolved) { - //if ("string" === typeof typeOrName) { - // console.log("@@@@String type!!!", typeOrName) - //} - var t = typeOrName; - t = this.getType(t, onResolved ? true : false, onResolved); - var posT = classTypes.indexOf(t); - return typeof mappedTo[posT] === 'undefined' ? t : classTypes[mappedTo[posT]]; - }; - - - - this.getType = function (typeOrName, doNotThrow, onResolved) { - Guard.requireValue("typeOrName", typeOrName); - if (typeof typeOrName === 'function') { - return typeOrName; - }; - - if (!(typeOrName in classNames)) { - if (parent) { - var tp = parent.getType(typeOrName, true); - if (tp) return tp; - } - if (onResolved) { - addPendingResolution(typeOrName, onResolved); - return; - } - else if (doNotThrow) { - return undefined; - } else { - Guard.raise(new Exception("Unable to resolve type:" + typeOrName)); - } - }; - var result = classTypes[classNames[typeOrName]]; - if (onResolved) { - onResolved(result); - } - return result; - }; - - this.getName = function (typeOrName) { - var t = this.getType(typeOrName); - var tPos = classTypes.indexOf(t); - if (tPos == -1) - Guard.raise("unknown type to request name for: " + typeOrName); - return consolidatedClassNames[tPos]; - }; - - this.getTypes = function () { - var keys = Object.keys(classNames); - var ret = []; - for (var i = 0; i < keys.length; i++) { - var className = keys[i]; - ret.push({ name: className, type: classTypes[classNames[className]], toString: function () { return this.name; } }); - } - return ret; - }; - - //this.getTypeName( in type); - //this.resolveType() - //this.inferTypeFromValue = function (value) { - - this.getTypeName = function (value) { - //TODO refactor - switch (typeof value) { - case 'object': - if (value == null) return '$data.Object'; - if (value instanceof Array) return '$data.Array'; - if (value.getType) return value.getType().fullName; - if (value instanceof Date) return '$data.Date'; - if (value instanceof $data.Guid) return '$data.Guid'; - if (value instanceof $data.DateTimeOffset) return '$data.DateTimeOffset'; - if (value instanceof $data.GeographyPoint) return '$data.GeographyPoint'; - if (value instanceof $data.GeographyLineString) return '$data.GeographyLineString'; - if (value instanceof $data.GeographyPolygon) return '$data.GeographyPolygon'; - if (value instanceof $data.GeographyMultiPoint) return '$data.GeographyMultiPoint'; - if (value instanceof $data.GeographyMultiLineString) return '$data.GeographyMultiLineString'; - if (value instanceof $data.GeographyMultiPolygon) return '$data.GeographyMultiPolygon'; - if (value instanceof $data.GeographyCollection) return '$data.GeographyCollection'; - if (value instanceof $data.GeographyBase) return '$data.GeographyBase'; - if (value instanceof $data.GeometryPoint) return '$data.GeometryPoint'; - if (value instanceof $data.GeometryLineString) return '$data.GeometryLineString'; - if (value instanceof $data.GeometryPolygon) return '$data.GeometryPolygon'; - if (value instanceof $data.GeometryMultiPoint) return '$data.GeometryMultiPoint'; - if (value instanceof $data.GeometryMultiLineString) return '$data.GeometryMultiLineString'; - if (value instanceof $data.GeometryMultiPolygon) return '$data.GeometryMultiPolygon'; - if (value instanceof $data.GeometryCollection) return '$data.GeometryCollection'; - if (value instanceof $data.GeometryBase) return '$data.GeometryBase'; - if (value instanceof $data.Geospatial) return '$data.Geospatial'; - if (value instanceof $data.SimpleBase) return '$data.SimpleBase'; - if (typeof value.toHexString === 'function') return '$data.ObjectID'; - //if(value instanceof "number") return - default: - return typeof value; - } - }; - - this.isTypeRegistered = function (typeOrName) { - if (typeof typeOrName === 'function') { - return classTypes.indexOf(typeOrName) > -1; - } else { - return typeOrName in classNames; - } - }; - - this.unregisterType = function (type) { - Guard.raise("Unimplemented"); - }; - - - - this.getDefault = function (typeOrName) { - var t = this.resolveType(typeOrName); - switch (t) { - case $data.Number: return 0.0; - case $data.Float: return 0.0; - case $data.Decimal: return '0.0'; - case $data.Integer: return 0; - case $data.Int16: return 0; - case $data.Int32: return 0; - case $data.Int64: return '0'; - case $data.Byte: return 0; - case $data.SByte: return 0; - case $data.String: return null; - case $data.Boolean: return false; - default: return null; - } - }; - - //name array ['', '', ''] - this.getIndex = function (typeOrName) { - var t = this.resolveType(typeOrName); - return classTypes.indexOf(t); - } - - this.resolveByIndex = function (index) { - return classTypes[index]; - } - - this.registerType = function (nameOrNamesArray, type, factoryFunc) { - /// - ///Registers a type and optionally a lifetimeManager with a name - ///that can be used to later resolve the type or create new instances - ///The names of the type - ///The type to register - /// - /// - /// - ///Registers a new type that - ///The name of the type - ///The type to register - /// - - - ///TODO remove - /*if (typeof typeNameOrAlias === 'string') { - if (classNames.indexOf(typeNameOrAlias) > -1) { - Guard.raise("Type already registered. Remove first"); - } - }*/ - - if (!nameOrNamesArray) { - return; - } - - //todo add ('number', 'number') - if (typeof type === "string") { - type = self.resolveType(type); - } - - var namesArray = []; - if (typeof nameOrNamesArray === 'string') { - var tmp = []; - tmp.push(nameOrNamesArray); - namesArray = tmp; - } else { - namesArray = nameOrNamesArray; - } - - for (var i = 0; i < namesArray.length; i++) { - var parts = namesArray[i].split('.'); - var item = {}; - item.shortName = parts[parts.length - 1]; - item.fullName = namesArray[i]; - namesArray[i] = item; - } - - //if (type. - - - var creatorFnc = function () { return IoC(type, arguments); }; - - if (typeof intellisense !== 'undefined') { - intellisense.annotate(creatorFnc, type); - } - - for (var i = 0, l = namesArray.length; i < l; i++) { - var item = namesArray[i]; - if (!(("create" + item.shortName) in self)) { - if (typeof factoryFunc === 'function') { - self["create" + item.shortName] = factoryFunc; - } else { - self["create" + item.shortName] = creatorFnc; - } - } - - var typePos = classTypes.indexOf(type); - if (typePos == -1) { - //new type - typePos = classTypes.push(type) - 1; - var fn = item.fullName; - consolidatedClassNames[typePos] = item.fullName; - }; - - classNames[item.fullName] = typePos; - - var pending = pendingResolutions[item.fullName] || []; - if (pending.length > 0) { - pending.forEach(function (t) { - t(type); - }); - pendingResolutions[item.fullName] = []; - } - } - if (parent) { - parent.registerType.apply(parent, arguments); - } - if (!type.name) { - type.name = namesArray[0].shortName; - } - }; - - - var _converters = { - from: {}, - to: {} - }; - this.converters = _converters; - - this.convertTo = function (value, tType, eType /*if Array*/, options) { - Guard.requireValue("typeOrName", tType); - - if(Object.isNullOrUndefined(value)) - return value; - - var sourceTypeName = Container.getTypeName(value); - var sourceType = Container.resolveType(sourceTypeName); - var sourceTypeName = Container.resolveName(sourceType); - var targetType = Container.resolveType(tType); - var targetTypeName = Container.resolveName(targetType); - - var result; - try { - if (typeof targetType['from' + sourceTypeName] === 'function') { - // target from - result = targetType['from' + sourceTypeName].apply(targetType, arguments); - - } else if (typeof sourceType['to' + targetTypeName] === 'function') { - // source to - result = sourceType['to' + targetTypeName].apply(sourceType, arguments); - - } else if (_converters.to[targetTypeName] && _converters.to[targetTypeName][sourceTypeName]) { - // target from source - result = _converters.to[targetTypeName][sourceTypeName].apply(_converters, arguments); - - } else if (_converters.from[sourceTypeName] && _converters.from[sourceTypeName][targetTypeName]) { - // source to target - result = _converters.from[sourceTypeName][targetTypeName].apply(_converters, arguments); - - } else if (targetTypeName === sourceTypeName || value instanceof targetType) { - result = value; - - } else if (_converters.to[targetTypeName] && _converters.to[targetTypeName]['default']) { - // target from anything - result = _converters.to[targetTypeName]['default'].apply(_converters, arguments); - - } else { - throw "converter not found"; - } - } catch (e) { - Guard.raise(new Exception("Value '" + sourceTypeName + "' not convertable to '" + targetTypeName + "'", 'TypeError', value)); - } - - if (targetType === $data.Array && eType && Array.isArray(result)) { - for (var i = 0; i < result.length; i++) { - result[i] = this.convertTo.call(this, result[i], eType, undefined, options); - } - } - - return result; - }; - this.registerConverter = function (target, sourceOrToConverters, toConverterOrFromConverters, fromConverter) { - //registerConverter($data.Guid, { $data.String: fn, int: fn }, { string: fn, int:fn }) - //registerConverter($data.Guid, $data.String, fn, fn); - - var targetName = Container.resolveName(target); - if (Container.isTypeRegistered(sourceOrToConverters)) { - //isSource - _converters.to[targetName] = _converters.to[targetName] || {}; - _converters.from[targetName] = _converters.from[targetName] || {}; - - var sourceName = Container.resolveName(sourceOrToConverters); - - if (toConverterOrFromConverters) - _converters.to[targetName][sourceName] = toConverterOrFromConverters; - if (fromConverter) - _converters.from[targetName][sourceName] = fromConverter; - - } else { - // converterGroup - - //fromConverters - if (_converters.to[targetName]) { - _converters.to[targetName] = $data.typeSystem.extend(_converters.to[targetName], sourceOrToConverters); - } else { - _converters.to[targetName] = sourceOrToConverters; - } - - //toConverters - if (_converters.from[targetName]) { - _converters.from[targetName] = $data.typeSystem.extend(_converters.from[targetName], toConverterOrFromConverters); - } else { - _converters.from[targetName] = toConverterOrFromConverters; - } - } - }; - } - $data.ContainerClass = ContainerCtor; - - var c; - - global["Container"] = $data.Container = c = global["C$"] = new ContainerCtor(); - - $data.createContainer = function () { - return new ContainerCtor($data.Container); - } - - //})(window); - - global["$C"] = function () { Class.define.apply(Class, arguments); }; - - - var storeProperty = function (memberDefinition, value) { - var backingFieldName = "_" + memberDefinition.name; - if (!this[backingFieldName]) { - Object.defineProperty(this, backingFieldName, memberDefinition.createStorePropertyDescriptor(value)); - } - else { - this[backingFieldName] = value; - } - }; - var retrieveProperty = function (memberDefinition) { - var backingFieldName = "_" + memberDefinition.name; - return this[backingFieldName]; - }; - - - $data.Class.define('$data.Base', function Base() { }, null, { - storeProperty: storeProperty, - retrieveProperty: retrieveProperty, - setProperty: function (memberDefinition, value, callback) { - this[memberDefinition.name] = value; - callback(); - }, - getProperty: function (memberDefinition, callback) { - callback.apply(this, [this[memberDefinition.name]]); - } - }, { - create: function () { return Container.createInstance(this, arguments); }, - extend: function (name, container, instanceDefinition, classDefinition) { - if (container && !(container instanceof ContainerCtor)) { - classDefinition = instanceDefinition; - instanceDefinition = container; - container = undefined; - } - return $data.Class.define(name, this, container, instanceDefinition, classDefinition); - }, - getMemberDefinition: function (name) { - return this.memberDefinitions.getMember(name); - }, - addProperty: function (name, getterOrType, setterOrGetter, setter) { - var _getter = getterOrType; - var _setter = setterOrGetter; - var _type; - if (typeof _getter === 'string') { - _type = getterOrType; - _getter = setterOrGetter; - _setter = setter; - } - - var propDef = { - notMapped: true, - storeOnObject: true, - get: typeof _getter === 'function' ? _getter : function () { }, - set: typeof _setter === 'function' ? _setter : function () { }, - type: _type - }; - - var memberDefinition = MemberDefinition.translateDefinition(propDef, name, this); - $data.Class.buildMember(this, memberDefinition); - - this.memberDefinitions.clearCache(); - - return this; - }, - addMember: function (name, definition, isClassMember) { - var memberDefinition = MemberDefinition.translateDefinition(definition, name, this); - - if (isClassMember) { - memberDefinition.classMember = true; - $data.Class.buildMember(this, memberDefinition, undefined, 'staticDefinitions'); - this.staticDefinitions.clearCache(); - } else { - $data.Class.buildMember(this, memberDefinition); - this.memberDefinitions.clearCache(); - } - return this; - }, - describeField: function (name, definition) { - var memDef = this.memberDefinitions.getMember(name); - if (!memDef) { - this.addMember(name, definition); - } else { - Guard.raise(new Exception("Field '" + name + "' already defined!", "Invalid operation")); - } - return this; - }, - storeProperty: storeProperty, - retrieveProperty: retrieveProperty, - 'from$data.Object': function (value) { - return value; - } - }); - - - //override after typeSystem initialized - - - $data.Class.ConstructorParameter = ConstructorParameter = $data.Class.define('ConstructorParameter', null, null, { - constructor: function (paramIndex) { - /// - this.paramIndex = paramIndex; - }, - paramIndex: {} - }); - /*$data.Class.MixinParameter = MixinParameter = $data.Class.define('MixinParameter', null, null, { - constructor: function (typeName) { - /// - this.typeName = typeName; - }, - typeName: {} - });*/ - - //var e = new Entity(); - - - /*$data.Interface = Class.define("Interface", null, null, { - constructor: function() { Guard.raise("Can not create an interface"); } - }, - { - define: function (name, definition) { - var result = Class.define(name, $data.Interface, null, null, definition); - delete result.__class; - result.__interface = true; - return result; - } - }); - - - - $data.Observable = Observable = Class.define("Observable", null, null, { - propertyChanged: { dataType: $data.Event } - }, { - createFromInstance: function(instance) { - var propNames = instance.getClass().memberDefinitions.f - } - });*/ - - - -})($data, window); - -$data.defaultErrorCallback = function () { - //console.log('DEFAULT ERROR CALLBACK:'); - /*if (console.dir) - console.dir(arguments); - else - console.log(arguments);*/ - if (arguments.length > 0 && arguments[arguments.length - 1] && typeof arguments[arguments.length - 1].reject === 'function') { - (console.error || console.log).call(console, arguments[0]); - arguments[arguments.length - 1].reject.apply(arguments[arguments.length - 1], arguments); - } else { - if (arguments[0] instanceof Error) { - Guard.raise(arguments[0]); - } else { - Guard.raise(new Exception("DEFAULT ERROR CALLBACK!", "DefaultError", arguments)); - } - } -}; -$data.defaultSuccessCallback = function () { /*console.log('DEFAULT SUCCES CALLBACK');*/ }; -$data.defaultNotifyCallback = function () { /*console.log('DEFAULT NOTIFY CALLBACK');*/ }; - -$data.typeSystem = { - __namespace: true, - /*inherit: function (ctor, baseType) { - var proto = new baseType(); - ctor.prototype = $.extend(proto, ctor.prototype); - //console.dir(proto); - ctor.prototype.base = new baseType(); - //console.dir(ctor.prototype.base); - ctor.prototype.constructor = ctor; - return ctor; - },*/ - //mix: function (type, mixin) { - // type.prototype = $.extend(type.prototype || {}, mixin.prototype || {}); - // type.mixins = type.mixins || []; - // type.mixins.push(mixin); - // return type; - //}, - extend: function (target) { - /// - /// Extends an object with properties of additional parameters. - /// - /// - /// Object that will be extended. - /// Object to extend target with. - /// Object to extend target with. - /// - /// - if (typeof target !== 'object' && typeof target !== 'function') - Guard.raise('Target must be object or function'); - - for (var i = 1; i < arguments.length; i++) { - var obj = arguments[i]; - if (obj === null || typeof obj === 'undefined') - continue; - for (key in obj) { - target[key] = obj[key]; - } - } - return target; - }, - createCallbackSetting: function (callBack, defaultSetting) { - var setting = { - success: $data.defaultSuccessCallback, - error: $data.defaultErrorCallback, - notify: $data.defaultNotifyCallback - }; - - if (defaultSetting != undefined && defaultSetting != null) { - setting = defaultSetting; - } - - var result; - if (callBack == null || callBack == undefined) { - result = setting; - - } else if (typeof callBack == 'function') { - result = this.extend(setting, { success: callBack }); - - } else { - result = this.extend(setting, callBack); - } - - function wrapCode(fn) { var t = this; function r() { fn.apply(t, arguments); fn = function () { } } return r; } - - if (typeof result.error === 'function') - result.error = wrapCode(result.error); - - return result; - }, - createCtorParams: function (source, indexes, thisObj) { - ///Paramerter array - /// - /// - if (indexes) { - var paramArray = []; - for (var i = 0, l = indexes.length; i < l; i++) { - var item = i; - if (indexes[item] instanceof ConstructorParameter) - paramArray.push(source[indexes[item].paramIndex]); - else if (typeof indexes[item] === "function") - paramArray.push(indexes[item].apply(thisObj)); - else - paramArray.push(indexes[item]); - } - return paramArray; - } - return source; - }, - writePropertyValues: function (obj) { - if (obj && obj.getType && obj.getType().memberDefinitions) { - this.writeProperties(obj, obj.getType().memberDefinitions.asArray().filter( - function (md) { return (md.kind == "property" || md.kind == "navProperty" || md.kind == "complexProperty") && !md.prototypeProperty; } - )); - } - }, - writeProperties: function (obj, members) { - var defines = {}; - for (var i = 0, l = members.length; i < l; i++) { - var memDef = members[i]; - defines[memDef.name] = memDef.createPropertyDescriptor(null, memDef.value); - } - - Object.defineProperties(obj, defines); - - }, - writeProperty: function (obj, member, value) { - var memDef = typeof member === 'string' ? obj.getType().memberDefinitions.getMember(member) : member; - if (memDef) { - var propDef = memDef.createPropertyDescriptor(null, value); - //////OPTIMIZATION - Object.defineProperty(obj, memDef.name, propDef); - } - } -}; - -$data.debug = function () { - (console.debug || console.log).apply(console, arguments); -}; - -$data.debugWith = function () { - var cArgs = arguments; - return function (r) { - (console.debug || console.log).apply(console, cArgs); - if ((typeof Error !== 'undefined' && r instanceof Error) || - (typeof Exception !== 'undefined' && r instanceof Exception)) { - (console.error || console.log).apply(console, arguments); - } else { - (console.debug || console.log).apply(console, arguments); - } - } -}; - -$data.fdebug = { - success: $data.debugWith('success'), - error: $data.debugWith('error') -}; -$data.Number = typeof Number !== 'undefined' ? Number : function JayNumber() { }; -$data.Date = typeof Date !== 'undefined' ? Date : function JayDate() { }; -$data.String = typeof String !== 'undefined' ? String : function JayString() { }; -$data.Boolean = typeof Boolean !== 'undefined' ? Boolean : function JayBoolean() { }; -$data.Array = typeof Array !== 'undefined' ? Array : function JayArray() { }; -$data.Object = typeof Object !== 'undefined' ? Object : function JayObject() { }; -$data.Function = Function; - -$data.Byte = function JayByte() { }; -$data.SByte = function JaySByte() { }; -$data.Decimal = function JayDecimal() { }; -$data.Float = $data.Single = function JayFloat() { }; -$data.Integer = function JayInteger() { }; -$data.Int16 = function JayInt16(v) { }; -$data.Int32 = function JayInt32() { }; -$data.Int64 = function JayInt64(v) { }; -$data.ObjectID = typeof $data.mongoDBDriver !== 'undefined' && typeof $data.mongoDBDriver.ObjectID !== 'undefined' ? $data.mongoDBDriver.ObjectID : function JayObjectID() { }; -$data.Time = function JayTime() { }; -$data.DateTimeOffset = function JayDateTimeOffset(val) { - this.value = val; -}; -$data.DateTimeOffset.prototype.toJSON = function () { - return this.value instanceof Date ? this.value.toISOString() : this.value; -}; - -$data.Container.registerType(["$data.Number", "number", "JayNumber", "double"], $data.Number); -$data.Container.registerType(["$data.Integer", "int", "integer", "JayInteger"], $data.Integer); -$data.Container.registerType(["$data.Int32", "int32", "JayInt32"], $data.Int32); -$data.Container.registerType(["$data.Byte", "byte", "JayByte"], $data.Byte); -$data.Container.registerType(["$data.SByte", "sbyte", "JaySByte"], $data.SByte); -$data.Container.registerType(["$data.Decimal", "decimal", "JayDecimal"], $data.Decimal); -$data.Container.registerType(["$data.Float", "$data.Single", "float", "single", "JayFloat"], $data.Float); -$data.Container.registerType(["$data.Int16", "int16", "word", "JayInt16"], $data.Int16); -$data.Container.registerType(["$data.Int64", "int64", "long", "JayInt64"], $data.Int64); -$data.Container.registerType(["$data.String", "string", "text", "character", "JayString"], $data.String); -$data.Container.registerType(["$data.Array", "array", "Array", "[]", "JayArray"], $data.Array, function () { - return $data.Array.apply(undefined, arguments); -}); -$data.Container.registerType(["$data.Date", "datetime", "date", "JayDate"], $data.Date); -$data.Container.registerType(["$data.Time", "time", "JayTime"], $data.Time); -$data.Container.registerType(["$data.DateTimeOffset", "offset", "datetimeoffset", "JayDateTimeOffset"], $data.DateTimeOffset); -$data.Container.registerType(["$data.Boolean", "bool", "boolean", "JayBoolean"], $data.Boolean); -$data.Container.registerType(["$data.Object", "Object", "object", "{}", "JayObject"], $data.Object); -$data.Container.registerType(["$data.Function", "Function", "function"], $data.Function); -$data.Container.registerType(['$data.ObjectID', 'ObjectID', 'objectId', 'objectid', 'ID', 'Id', 'id', 'JayObjectID'], $data.ObjectID); -$data.Class.define('$data.TraceBase', null, null, { - log: function () { }, - warn: function () { }, - error: function () { } -}); - -$data.Trace = new $data.TraceBase(); -$data.Class.define('$data.Logger', $data.TraceBase, null, { - log: function () { - Array.prototype.unshift.call(arguments, this.getDateFormat()); - console.log.apply(console, arguments); - }, - warn: function () { - Array.prototype.unshift.call(arguments, this.getDateFormat()); - console.warn.apply(console, arguments); - }, - error: function () { - Array.prototype.unshift.call(arguments, this.getDateFormat()); - console.error.apply(console, arguments); - }, - - getDateFormat: function () { - var date = new Date(); - return date.getHours() + ':' + date.getMinutes() + ':' + date.getSeconds() + '.' + date.getMilliseconds(); - } -}); -$data.Number = typeof Number !== 'undefined' ? Number : function JayNumber() { }; -$data.Date = typeof Date !== 'undefined' ? Date : function JayDate() { }; -$data.String = typeof String !== 'undefined' ? String : function JayString() { }; -$data.Boolean = typeof Boolean !== 'undefined' ? Boolean : function JayBoolean() { }; -$data.Array = typeof Array !== 'undefined' ? Array : function JayArray() { }; -$data.Object = typeof Object !== 'undefined' ? Object : function JayObject() { }; -$data.Function = Function; - -$data.Byte = function JayByte() { }; -$data.SByte = function JaySByte() { }; -$data.Decimal = function JayDecimal() { }; -$data.Float = $data.Single = function JayFloat() { }; -$data.Integer = function JayInteger() { }; -$data.Int16 = function JayInt16(v) { }; -$data.Int32 = function JayInt32() { }; -$data.Int64 = function JayInt64(v) { }; -$data.ObjectID = typeof $data.mongoDBDriver !== 'undefined' && typeof $data.mongoDBDriver.ObjectID !== 'undefined' ? $data.mongoDBDriver.ObjectID : function JayObjectID() { }; -$data.Time = function JayTime() { }; -$data.DateTimeOffset = function JayDateTimeOffset(val) { - this.value = val; -}; -$data.DateTimeOffset.prototype.toJSON = function () { - return this.value instanceof Date ? this.value.toISOString() : this.value; -}; - -$data.Container.registerType(["$data.Number", "number", "JayNumber", "double"], $data.Number); -$data.Container.registerType(["$data.Integer", "int", "integer", "JayInteger"], $data.Integer); -$data.Container.registerType(["$data.Int32", "int32", "JayInt32"], $data.Int32); -$data.Container.registerType(["$data.Byte", "byte", "JayByte"], $data.Byte); -$data.Container.registerType(["$data.SByte", "sbyte", "JaySByte"], $data.SByte); -$data.Container.registerType(["$data.Decimal", "decimal", "JayDecimal"], $data.Decimal); -$data.Container.registerType(["$data.Float", "$data.Single", "float", "single", "JayFloat"], $data.Float); -$data.Container.registerType(["$data.Int16", "int16", "word", "JayInt16"], $data.Int16); -$data.Container.registerType(["$data.Int64", "int64", "long", "JayInt64"], $data.Int64); -$data.Container.registerType(["$data.String", "string", "text", "character", "JayString"], $data.String); -$data.Container.registerType(["$data.Array", "array", "Array", "[]", "JayArray"], $data.Array, function () { - return $data.Array.apply(undefined, arguments); -}); -$data.Container.registerType(["$data.Date", "datetime", "date", "JayDate"], $data.Date); -$data.Container.registerType(["$data.Time", "time", "JayTime"], $data.Time); -$data.Container.registerType(["$data.DateTimeOffset", "offset", "datetimeoffset", "JayDateTimeOffset"], $data.DateTimeOffset); -$data.Container.registerType(["$data.Boolean", "bool", "boolean", "JayBoolean"], $data.Boolean); -$data.Container.registerType(["$data.Object", "Object", "object", "{}", "JayObject"], $data.Object); -$data.Container.registerType(["$data.Function", "Function", "function"], $data.Function); -$data.Container.registerType(['$data.ObjectID', 'ObjectID', 'objectId', 'objectid', 'ID', 'Id', 'id', 'JayObjectID'], $data.ObjectID); -/* $data.SimpleBase */ -$data.SimpleBase = function SimpleBase(data) { - if (typeof data === 'object' && data) { - if (Array.isArray(this.constructor.validMembers)) { - for (var i = 0; i < this.constructor.validMembers.length; i++) { - var name = this.constructor.validMembers[i]; - - if (data[name] !== undefined) { - this[name] = data[name]; - } - } - - } else { - delete data.type; - $data.typeSystem.extend(this, data); - } - } -} -$data.SimpleBase.registerType = function (name, type, base) { - base = base || $data.SimpleBase; - - type.type = name; - type.prototype = Object.create(base.prototype); - type.prototype.constructor = type; -} -$data.Container.registerType(['$data.SimpleBase', 'SimpleBase'], $data.SimpleBase);$data.Geospatial = function Geospatial() { - this.type = this.constructor.type; - if (Array.isArray(this.constructor.validMembers)) { - for (var i = 0; i < this.constructor.validMembers.length; i++) { - var name = this.constructor.validMembers[i]; - this[name] = undefined; - } - } - - $data.SimpleBase.apply(this, arguments); - this.type = this.constructor.type || 'Unknown'; -}; -$data.SimpleBase.registerType('Geospatial', $data.Geospatial); -$data.Container.registerType(['$data.Geospatial', 'Geospatial'], $data.Geospatial); - -$data.point = function (arg) { - if (arg && arg.crs) { - if (arg.crs.properties && arg.crs.properties.name === $data.GeometryBase.defaultCrs.properties.name) { - return new $data.GeometryPoint(arg); - } else { - return new $data.GeographyPoint(arg); - } - } else if(arg) { - if ('x' in arg && 'y' in arg) { - return new $data.GeometryPoint(arg.x, arg.y); - } else if ('longitude' in arg && 'latitude' in arg) { - return new $data.GeographyPoint(arg.longitude, arg.latitude); - } else if ('lng' in arg && 'lat' in arg) { - return new $data.GeographyPoint(arg.lng, arg.lat); - } - } -}; -/* $data.GeographyBase */ -$data.GeographyBase = function GeographyBase() { - $data.Geospatial.apply(this, arguments); - - this.crs = $data.GeographyBase.defaultCrs; - $data.GeographyBase.validateGeoJSON(this); -}; - -$data.GeographyBase.defaultCrs = { - properties: { - name: 'EPSG:4326' - }, - type: 'name' -}; - -$data.GeographyBase.parseFromString = function (strData) { - var lparenIdx = strData.indexOf('('); - if(lparenIdx >= 0){ - var name = strData.substring(0, lparenIdx).toLowerCase(); - var type = $data.GeographyBase.registered[name]; - - if (type && type.parseFromString && type != $data.GeographyBase) { - return type.parseFromString(strData); - } else { - Guard.raise(new Exception('parseFromString', 'Not Implemented', strData)); - } - } -}; -$data.GeographyBase.stringifyToUrl = function (geoData) { - if (geoData instanceof $data.GeographyBase && geoData.constructor && geoData.constructor.stringifyToUrl) { - return geoData.constructor.stringifyToUrl(geoData); - } else if (geoData instanceof $data.GeographyBase && geoData.constructor && Array.isArray(geoData.constructor.validMembers) && geoData.constructor.validMembers[0] === 'coordinates') { - var data = "geography'" + geoData.type.toUpperCase() + '('; - function buildArray(d, context) { - if (Array.isArray(d[0])) { - - for (var i = 0; i < d.length; i++) { - if (i > 0) data += ','; - if (Array.isArray(d[i][0])) - data += '('; - - buildArray(d[i]); - - if (Array.isArray(d[i][0])) - data += ')'; - } - - } else { - data += d.join(' '); - } - } - buildArray(geoData.coordinates, data); - - data += ")'"; - return data; - } else { - Guard.raise(new Exception('stringifyToUrl on instance type', 'Not Implemented', geoData)); - } -}; -$data.GeographyBase.registerType = function (name, type, base) { - $data.SimpleBase.registerType(name, type, base || $data.GeographyBase); - - $data.GeographyBase.registered = $data.GeographyBase.registered || {}; - $data.GeographyBase.registered[name.toLowerCase()] = type; -}; -$data.GeographyBase.validateGeoJSON = function (geoData) { - var type = geoData.type; - if (type) { - var geoType = $data.GeographyBase.registered[type.toLowerCase()]; - if (typeof geoType.validateGeoJSON === 'function') { - var isValid = geoType.validateGeoJSON(geoData); - if (isValid) { - return isValid; - } else { - Guard.raise(new Exception("Invalid '" + type + "' format!", 'Format Exception', geoData)); - } - } - } - console.log('GeoJSON validation missing', geoData); - return; -}; -$data.SimpleBase.registerType('GeographyBase', $data.GeographyBase, $data.Geospatial); -$data.Container.registerType(['$data.GeographyBase'], $data.GeographyBase); - -/* $data.GeographyPoint */ -$data.GeographyPoint = function GeographyPoint(lon, lat) { - if (lon && typeof lon === 'object' && Array.isArray(lon)) { - $data.GeographyBase.call(this, { coordinates: lon }); - } else if (lon && typeof lon === 'object' && ('longitude' in lon || 'latitude' in lon)) { - $data.GeographyBase.call(this, { coordinates: [lon.longitude, lon.latitude] }); - } else if (lon && typeof lon === 'object' && ('lng' in lon || 'lat' in lon)) { - $data.GeographyBase.call(this, { coordinates: [lon.lng, lon.lat] }); - } else if (lon && typeof lon === 'object') { - $data.GeographyBase.call(this, lon); - } else { - $data.GeographyBase.call(this, { coordinates: [lon || 0, lat || 0] }); - } -}; -$data.GeographyPoint.validateGeoJSON = function (geoData) { - return geoData && - Array.isArray(geoData.coordinates) && - geoData.coordinates.length == 2 && - typeof geoData.coordinates[0] === 'number' && - typeof geoData.coordinates[1] === 'number'; -}; -$data.GeographyPoint.parseFromString = function (strData) { - var data = strData.substring(strData.indexOf('(') + 1, strData.lastIndexOf(')')); - var values = data.split(' '); - - return new $data.GeographyPoint(parseFloat(values[0]), parseFloat(values[1])); -}; -$data.GeographyPoint.validMembers = ['coordinates']; -$data.GeographyBase.registerType('Point', $data.GeographyPoint); -Object.defineProperty($data.GeographyPoint.prototype, 'longitude', { get: function () { return this.coordinates[0]; }, set: function (v) { this.coordinates[0] = v; } }); -Object.defineProperty($data.GeographyPoint.prototype, 'latitude', { get: function () { return this.coordinates[1]; }, set: function (v) { this.coordinates[1] = v; } }); -$data.Container.registerType(['$data.GeographyPoint', 'GeographyPoint', '$data.Geography', 'Geography', 'geography', 'geo'], $data.GeographyPoint); -$data.Geography = $data.GeographyPoint; - -/* $data.GeographyLineString */ -$data.GeographyLineString = function GeographyLineString(data) { - if (Array.isArray(data)) { - $data.GeographyBase.call(this, { coordinates: data }); - } else { - $data.GeographyBase.call(this, data); - } -}; -$data.GeographyLineString.validateGeoJSON = function (geoData) { - var isValid = geoData && - Array.isArray(geoData.coordinates); - - for (var i = 0; isValid && i < geoData.coordinates.length; i++) { - var point = geoData.coordinates[i]; - isValid = isValid && - Array.isArray(point) && - point.length == 2 && - typeof point[0] === 'number' && - typeof point[1] === 'number'; - } - - return isValid; -}; -$data.GeographyLineString.validMembers = ['coordinates']; -$data.GeographyBase.registerType('LineString', $data.GeographyLineString); -$data.Container.registerType(['$data.GeographyLineString', 'GeographyLineString'], $data.GeographyLineString); - -/* $data.GeographyPolygon */ -$data.GeographyPolygon = function GeographyPolygon(data) { - if (typeof data === 'object' && (('topLeft' in data && 'bottomRight' in data) || ('topRight' in data && 'bottomLeft' in data))) { - var tl, tr, bl, br; - - if ('topLeft' in data && 'bottomRight' in data) { - tl = data.topLeft instanceof $data.GeographyPoint ? data.topLeft : new $data.GeographyPoint(data.topLeft); - br = data.bottomRight instanceof $data.GeographyPoint ? data.bottomRight : new $data.GeographyPoint(data.bottomRight); - tr = new $data.GeographyPoint([br.coordinates[0], tl.coordinates[1]]); - bl = new $data.GeographyPoint([tl.coordinates[0], br.coordinates[1]]); - } else { - tr = data.topRight instanceof $data.GeographyPoint ? data.topRight : new $data.GeographyPoint(data.topRight); - bl = data.bottomLeft instanceof $data.GeographyPoint ? data.bottomLeft : new $data.GeographyPoint(data.bottomLeft); - tl = new $data.GeographyPoint([bl.coordinates[0], tr.coordinates[1]]); - br = new $data.GeographyPoint([tr.coordinates[0], bl.coordinates[1]]); - } - - var coordinates = []; - coordinates.push([].concat(tl.coordinates)); - coordinates.push([].concat(tr.coordinates)); - coordinates.push([].concat(br.coordinates)); - coordinates.push([].concat(bl.coordinates)); - coordinates.push([].concat(tl.coordinates)); - - $data.GeographyBase.call(this, { coordinates: [coordinates] }); - - }else if (Array.isArray(data)) { - $data.GeographyBase.call(this, { coordinates: data }); - } else { - $data.GeographyBase.call(this, data); - } -}; -$data.GeographyPolygon.validateGeoJSON = function (geoData) { - var isValid = geoData && - Array.isArray(geoData.coordinates); - - for (var i = 0; isValid && i < geoData.coordinates.length; i++) { - var polygon = geoData.coordinates[i]; - var isValid = isValid && Array.isArray(polygon); - - for (var j = 0; isValid && j < polygon.length; j++) { - var point = polygon[j]; - - isValid = isValid && - Array.isArray(point) && - point.length == 2 && - typeof point[0] === 'number' && - typeof point[1] === 'number'; - } - } - - return isValid; -}; -$data.GeographyPolygon.parseFromString = function (strData) { - var data = strData.substring(strData.indexOf('(') + 1, strData.lastIndexOf(')')); - var rings = data.substring(data.indexOf('(') + 1, data.lastIndexOf(')')).split('),('); - - var data = []; - for (var i = 0; i < rings.length; i++) { - var polyPoints = []; - var pairs = rings[i].split(','); - for (var j = 0; j < pairs.length; j++) { - var values = pairs[j].split(' '); - - polyPoints.push([parseFloat(values[0]), parseFloat(values[1])]); - } - data.push(polyPoints); - } - - return new $data.GeographyPolygon(data); -}; -$data.GeographyPolygon.validMembers = ['coordinates']; -$data.GeographyBase.registerType('Polygon', $data.GeographyPolygon); -$data.Container.registerType(['$data.GeographyPolygon', 'GeographyPolygon'], $data.GeographyPolygon); - -/* $data.GeographyMultiPoint */ -$data.GeographyMultiPoint = function GeographyMultiPoint(data) { - if (Array.isArray(data)) { - $data.GeographyBase.call(this, { coordinates: data }); - } else { - $data.GeographyBase.call(this, data); - } -}; -$data.GeographyMultiPoint.validateGeoJSON = function (geoData) { - var isValid = geoData && - Array.isArray(geoData.coordinates); - - for (var i = 0; isValid && i < geoData.coordinates.length; i++) { - var point = geoData.coordinates[i]; - isValid = isValid && - Array.isArray(point) && - point.length == 2 && - typeof point[0] === 'number' && - typeof point[1] === 'number'; - } - - return isValid; -}; -$data.GeographyMultiPoint.validMembers = ['coordinates']; -$data.GeographyBase.registerType('MultiPoint', $data.GeographyMultiPoint); -$data.Container.registerType(['$data.GeographyMultiPoint', 'GeographyMultiPoint'], $data.GeographyMultiPoint); - -/* $data.GeographyMultiLineString */ -$data.GeographyMultiLineString = function GeographyMultiLineString(data) { - if (Array.isArray(data)) { - $data.GeographyBase.call(this, { coordinates: data }); - } else { - $data.GeographyBase.call(this, data); - } -}; -$data.GeographyMultiLineString.validateGeoJSON = function (geoData) { - var isValid = geoData && - Array.isArray(geoData.coordinates); - - for (var i = 0; isValid && i < geoData.coordinates.length; i++) { - var polygon = geoData.coordinates[i]; - var isValid = isValid && Array.isArray(polygon); - - for (var j = 0; isValid && j < polygon.length; j++) { - var point = polygon[j]; - - isValid = isValid && - Array.isArray(point) && - point.length == 2 && - typeof point[0] === 'number' && - typeof point[1] === 'number'; - } - } - - return isValid; -}; -$data.GeographyMultiLineString.validMembers = ['coordinates']; -$data.GeographyBase.registerType('MultiLineString', $data.GeographyMultiLineString); -$data.Container.registerType(['$data.GeographyMultiLineString', 'GeographyMultiLineString'], $data.GeographyMultiLineString); - -/* $data.GeographyMultiPolygon */ -$data.GeographyMultiPolygon = function GeographyMultiPolygon(data) { - if (Array.isArray(data)) { - $data.GeographyBase.call(this, { coordinates: data }); - } else { - $data.GeographyBase.call(this, data); - } -}; -$data.GeographyMultiPolygon.validateGeoJSON = function (geoData) { - var isValid = geoData && - Array.isArray(geoData.coordinates); - - for (var k = 0; isValid && k < geoData.coordinates.length; k++) { - var polygons = geoData.coordinates[k]; - var isValid = isValid && Array.isArray(polygons); - - for (var i = 0; isValid && i < polygons.length; i++) { - var polygon = polygons[i]; - var isValid = isValid && Array.isArray(polygon); - - for (var j = 0; isValid && j < polygon.length; j++) { - var point = polygon[j]; - - isValid = isValid && - Array.isArray(point) && - point.length == 2 && - typeof point[0] === 'number' && - typeof point[1] === 'number'; - } - } - } - - return isValid; -}; -$data.GeographyMultiPolygon.validMembers = ['coordinates']; -$data.GeographyBase.registerType('MultiPolygon', $data.GeographyMultiPolygon); -$data.Container.registerType(['$data.GeographyMultiPolygon', 'GeographyMultiPolygon'], $data.GeographyMultiPolygon); - -/* $data.GeographyCollection */ -$data.GeographyCollection = function GeographyCollection(data) { - if (Array.isArray(data)) { - $data.GeographyBase.call(this, { geometries: data }); - } else { - $data.GeographyBase.call(this, data); - } -}; -$data.GeographyCollection.validateGeoJSON = function (geoData) { - var isValid = geoData && - Array.isArray(geoData.geometries); - - for (var i = 0; isValid && i < geoData.geometries.length; i++) { - var geometry = geoData.geometries[i]; - try { - isValid = isValid && $data.GeographyBase.validateGeoJSON(geometry); - } catch (e) { - isValid = false; - } - } - - return isValid; -}; -$data.GeographyCollection.validMembers = ['geometries']; -$data.GeographyBase.registerType('GeometryCollection', $data.GeographyCollection); -$data.Container.registerType(['$data.GeographyCollection', 'GeographyCollection'], $data.GeographyCollection); - - -/* converters */ -$data.Container.registerConverter($data.GeographyPoint, $data.Object, function (value) { - return value ? new $data.GeographyPoint(value) : value; -}); -$data.Container.registerConverter($data.GeographyLineString, $data.Object, function (value) { - return value ? new $data.GeographyLineString(value) : value; -}); -$data.Container.registerConverter($data.GeographyPolygon, $data.Object, function (value) { - return value ? new $data.GeographyPolygon(value) : value; -}); -$data.Container.registerConverter($data.GeographyMultiPoint, $data.Object, function (value) { - return value ? new $data.GeographyMultiPoint(value) : value; -}); -$data.Container.registerConverter($data.GeographyMultiLineString, $data.Object, function (value) { - return value ? new $data.GeographyMultiLineString(value) : value; -}); -$data.Container.registerConverter($data.GeographyMultiPolygon, $data.Object, function (value) { - return value ? new $data.GeographyMultiPolygon(value) : value; -}); -$data.Container.registerConverter($data.GeographyCollection, $data.Object, function (value) { - return value ? new $data.GeographyCollection(value) : value; -}); -/* $data.Geometry */ -$data.GeometryBase = function GeometryBase() { - $data.Geospatial.apply(this, arguments); - - this.crs = $data.GeometryBase.defaultCrs; - $data.GeometryBase.validateGeoJSON(this); -}; - -$data.GeometryBase.defaultCrs = { - properties: { - name: 'EPSG:0' - }, - type: 'name' -}; - -$data.GeometryBase.parseFromString = function (strData) { - var lparenIdx = strData.indexOf('('); - if (lparenIdx >= 0) { - var name = strData.substring(0, lparenIdx).toLowerCase(); - var type = $data.GeometryBase.registered[name]; - - if (type && type.parseFromString && type != $data.GeometryBase) { - return type.parseFromString(strData); - } else { - Guard.raise(new Exception('parseFromString', 'Not Implemented', strData)); - } - } -}; -$data.GeometryBase.stringifyToUrl = function (geoData) { - if (geoData instanceof $data.GeometryBase && geoData.constructor && geoData.constructor.stringifyToUrl) { - return geoData.constructor.stringifyToUrl(geoData); - } else if (geoData instanceof $data.GeometryBase && geoData.constructor && Array.isArray(geoData.constructor.validMembers) && geoData.constructor.validMembers[0] === 'coordinates') { - var data = "geometry'" + geoData.type.toUpperCase() + '('; - function buildArray(d, context) { - if (Array.isArray(d[0])) { - - for (var i = 0; i < d.length; i++) { - if (i > 0) data += ','; - if (Array.isArray(d[i][0])) - data += '('; - - buildArray(d[i]); - - if (Array.isArray(d[i][0])) - data += ')'; - } - - } else { - data += d.join(' '); - } - } - buildArray(geoData.coordinates, data); - - data += ")'"; - return data; - } else { - Guard.raise(new Exception('stringifyToUrl on instance type', 'Not Implemented', geoData)); - } -}; -$data.GeometryBase.registerType = function (name, type, base) { - $data.SimpleBase.registerType(name, type, base || $data.GeometryBase); - - $data.GeometryBase.registered = $data.GeometryBase.registered || {}; - $data.GeometryBase.registered[name.toLowerCase()] = type; -}; -$data.GeometryBase.validateGeoJSON = function (geoData) { - var type = geoData.type; - if (type) { - var geoType = $data.GeometryBase.registered[type.toLowerCase()]; - if (typeof geoType.validateGeoJSON === 'function') { - var isValid = geoType.validateGeoJSON(geoData); - if (isValid) { - return isValid; - } else { - Guard.raise(new Exception("Invalid '" + type + "' format!", 'Format Exception', geoData)); - } - } - } - console.log('GeoJSON validation missing', geoData); - return; -}; -$data.SimpleBase.registerType('GeometryBase', $data.GeometryBase, $data.Geospatial); -$data.Container.registerType(['$data.GeometryBase'], $data.GeometryBase); - -/* $data.GeometryPoint */ -$data.GeometryPoint = function GeometryPoint(x, y) { - var param = x; - if (param && typeof param === 'object' && Array.isArray(param)) { - $data.GeometryBase.call(this, { coordinates: param }); - } else if (param && typeof param === 'object' && ('x' in param || 'y' in param)) { - $data.GeometryBase.call(this, { coordinates: [param.x, param.y] }); - } else if (param && typeof param === 'object') { - $data.GeometryBase.call(this, param); - } else { - $data.GeometryBase.call(this, { coordinates: [x || 0, y || 0] }); - } -}; -$data.GeometryPoint.validateGeoJSON = function (geoData) { - return geoData && - Array.isArray(geoData.coordinates) && - geoData.coordinates.length == 2 && - typeof geoData.coordinates[0] === 'number' && - typeof geoData.coordinates[1] === 'number'; -}; -$data.GeometryPoint.parseFromString = function (strData) { - var data = strData.substring(strData.indexOf('(') + 1, strData.lastIndexOf(')')); - var values = data.split(' '); - - return new $data.GeometryPoint(parseFloat(values[0]), parseFloat(values[1])); -}; -$data.GeometryPoint.validMembers = ['coordinates']; -$data.GeometryBase.registerType('Point', $data.GeometryPoint); -Object.defineProperty($data.GeometryPoint.prototype, 'x', { get: function () { return this.coordinates[0]; }, set: function (v) { this.coordinates[0] = v; } }); -Object.defineProperty($data.GeometryPoint.prototype, 'y', { get: function () { return this.coordinates[1]; }, set: function (v) { this.coordinates[1] = v; } }); -$data.Container.registerType(['$data.GeometryPoint', 'GeometryPoint'], $data.GeometryPoint); - -/* $data.GeometryLineString */ -$data.GeometryLineString = function GeometryLineString(data) { - if (Array.isArray(data)) { - $data.GeometryBase.call(this, { coordinates: data }); - } else { - $data.GeometryBase.call(this, data); - } -}; -$data.GeometryLineString.validateGeoJSON = function (geoData) { - var isValid = geoData && - Array.isArray(geoData.coordinates); - - for (var i = 0; isValid && i < geoData.coordinates.length; i++) { - var point = geoData.coordinates[i]; - isValid = isValid && - Array.isArray(point) && - point.length == 2 && - typeof point[0] === 'number' && - typeof point[1] === 'number'; - } - - return isValid; -}; -$data.GeometryLineString.validMembers = ['coordinates']; -$data.GeometryBase.registerType('LineString', $data.GeometryLineString); -$data.Container.registerType(['$data.GeometryLineString', 'GeometryLineString'], $data.GeometryLineString); - -/* $data.GeometryPolygon */ -$data.GeometryPolygon = function GeometryPolygon(data) { - if (typeof data === 'object' && (('topLeft' in data && 'bottomRight' in data) || ('topRight' in data && 'bottomLeft' in data))) { - var tl, tr, bl, br; - - if ('topLeft' in data && 'bottomRight' in data) { - tl = data.topLeft instanceof $data.GeometryPoint ? data.topLeft : new $data.GeometryPoint(data.topLeft); - br = data.bottomRight instanceof $data.GeometryPoint ? data.bottomRight : new $data.GeometryPoint(data.bottomRight); - tr = new $data.GeometryPoint([br.coordinates[0], tl.coordinates[1]]); - bl = new $data.GeometryPoint([tl.coordinates[0], br.coordinates[1]]); - } else { - tr = data.topRight instanceof $data.GeometryPoint ? data.topRight : new $data.GeometryPoint(data.topRight); - bl = data.bottomLeft instanceof $data.GeometryPoint ? data.bottomLeft : new $data.GeometryPoint(data.bottomLeft); - tl = new $data.GeometryPoint([bl.coordinates[0], tr.coordinates[1]]); - br = new $data.GeometryPoint([tr.coordinates[0], bl.coordinates[1]]); - } - - var coordinates = []; - coordinates.push([].concat(tl.coordinates)); - coordinates.push([].concat(tr.coordinates)); - coordinates.push([].concat(br.coordinates)); - coordinates.push([].concat(bl.coordinates)); - coordinates.push([].concat(tl.coordinates)); - - $data.GeometryBase.call(this, { coordinates: [coordinates] }); - - }else if (Array.isArray(data)) { - $data.GeometryBase.call(this, { coordinates: data }); - } else { - $data.GeometryBase.call(this, data); - } -}; -$data.GeometryPolygon.validateGeoJSON = function (geoData) { - var isValid = geoData && - Array.isArray(geoData.coordinates); - - for (var i = 0; isValid && i < geoData.coordinates.length; i++) { - var polygon = geoData.coordinates[i]; - var isValid = isValid && Array.isArray(polygon); - - for (var j = 0; isValid && j < polygon.length; j++) { - var point = polygon[j]; - - isValid = isValid && - Array.isArray(point) && - point.length == 2 && - typeof point[0] === 'number' && - typeof point[1] === 'number'; - } - } - - return isValid; -}; -$data.GeometryPolygon.parseFromString = function (strData) { - var data = strData.substring(strData.indexOf('(') + 1, strData.lastIndexOf(')')); - var rings = data.substring(data.indexOf('(') + 1, data.lastIndexOf(')')).split('),('); - - var data = []; - for (var i = 0; i < rings.length; i++) { - var polyPoints = []; - var pairs = rings[i].split(','); - for (var j = 0; j < pairs.length; j++) { - var values = pairs[j].split(' '); - - polyPoints.push([parseFloat(values[0]), parseFloat(values[1])]); - } - data.push(polyPoints); - } - - return new $data.GeometryPolygon(data); -}; -$data.GeometryPolygon.validMembers = ['coordinates']; -$data.GeometryBase.registerType('Polygon', $data.GeometryPolygon); -$data.Container.registerType(['$data.GeometryPolygon', 'GeometryPolygon'], $data.GeometryPolygon); - -/* $data.GeometryMultiPoint */ -$data.GeometryMultiPoint = function GeometryMultiPoint(data) { - if (Array.isArray(data)) { - $data.GeometryBase.call(this, { coordinates: data }); - } else { - $data.GeometryBase.call(this, data); - } -}; -$data.GeometryMultiPoint.validateGeoJSON = function (geoData) { - var isValid = geoData && - Array.isArray(geoData.coordinates); - - for (var i = 0; isValid && i < geoData.coordinates.length; i++) { - var point = geoData.coordinates[i]; - isValid = isValid && - Array.isArray(point) && - point.length == 2 && - typeof point[0] === 'number' && - typeof point[1] === 'number'; - } - - return isValid; -}; -$data.GeometryMultiPoint.validMembers = ['coordinates']; -$data.GeometryBase.registerType('MultiPoint', $data.GeometryMultiPoint); -$data.Container.registerType(['$data.GeometryMultiPoint', 'GeometryMultiPoint'], $data.GeometryMultiPoint); - -/* $data.GeometryMultiLineString */ -$data.GeometryMultiLineString = function GeometryMultiLineString(data) { - if (Array.isArray(data)) { - $data.GeometryBase.call(this, { coordinates: data }); - } else { - $data.GeometryBase.call(this, data); - } -}; -$data.GeometryMultiLineString.validateGeoJSON = function (geoData) { - var isValid = geoData && - Array.isArray(geoData.coordinates); - - for (var i = 0; isValid && i < geoData.coordinates.length; i++) { - var polygon = geoData.coordinates[i]; - var isValid = isValid && Array.isArray(polygon); - - for (var j = 0; isValid && j < polygon.length; j++) { - var point = polygon[j]; - - isValid = isValid && - Array.isArray(point) && - point.length == 2 && - typeof point[0] === 'number' && - typeof point[1] === 'number'; - } - } - - return isValid; -}; -$data.GeometryMultiLineString.validMembers = ['coordinates']; -$data.GeometryBase.registerType('MultiLineString', $data.GeometryMultiLineString); -$data.Container.registerType(['$data.GeometryMultiLineString', 'GeometryMultiLineString'], $data.GeometryMultiLineString); - -/* $data.GeometryMultiPolygon */ -$data.GeometryMultiPolygon = function GeometryMultiPolygon(data) { - if (Array.isArray(data)) { - $data.GeometryBase.call(this, { coordinates: data }); - } else { - $data.GeometryBase.call(this, data); - } -}; -$data.GeometryMultiPolygon.validateGeoJSON = function (geoData) { - var isValid = geoData && - Array.isArray(geoData.coordinates); - - for (var k = 0; isValid && k < geoData.coordinates.length; k++) { - var polygons = geoData.coordinates[k]; - var isValid = isValid && Array.isArray(polygons); - - for (var i = 0; isValid && i < polygons.length; i++) { - var polygon = polygons[i]; - var isValid = isValid && Array.isArray(polygon); - - for (var j = 0; isValid && j < polygon.length; j++) { - var point = polygon[j]; - - isValid = isValid && - Array.isArray(point) && - point.length == 2 && - typeof point[0] === 'number' && - typeof point[1] === 'number'; - } - } - } - - return isValid; -}; -$data.GeometryMultiPolygon.validMembers = ['coordinates']; -$data.GeometryBase.registerType('MultiPolygon', $data.GeometryMultiPolygon); -$data.Container.registerType(['$data.GeometryMultiPolygon', 'GeometryMultiPolygon'], $data.GeometryMultiPolygon); - -/* $data.GeometryCollection */ -$data.GeometryCollection = function GeometryCollection(data) { - if (Array.isArray(data)) { - $data.GeometryBase.call(this, { geometries: data }); - } else { - $data.GeometryBase.call(this, data); - } -}; -$data.GeometryCollection.validateGeoJSON = function (geoData) { - var isValid = geoData && - Array.isArray(geoData.geometries); - - for (var i = 0; isValid && i < geoData.geometries.length; i++) { - var geometry = geoData.geometries[i]; - try { - isValid = isValid && $data.GeometryBase.validateGeoJSON(geometry); - } catch (e) { - isValid = false; - } - } - - return isValid; -}; -$data.GeometryCollection.validMembers = ['geometries']; -$data.GeometryBase.registerType('GeometryCollection', $data.GeometryCollection); -$data.Container.registerType(['$data.GeometryCollection', 'GeometryCollection'], $data.GeometryCollection); - -/* converters */ -$data.Container.registerConverter($data.GeometryPoint, $data.Object, function (value) { - return value ? new $data.GeometryPoint(value) : value; -}); -$data.Container.registerConverter($data.GeometryLineString, $data.Object, function (value) { - return value ? new $data.GeometryLineString(value) : value; -}); -$data.Container.registerConverter($data.GeometryPolygon, $data.Object, function (value) { - return value ? new $data.GeometryPolygon(value) : value; -}); -$data.Container.registerConverter($data.GeometryMultiPoint, $data.Object, function (value) { - return value ? new $data.GeometryMultiPoint(value) : value; -}); -$data.Container.registerConverter($data.GeometryMultiLineString, $data.Object, function (value) { - return value ? new $data.GeometryMultiLineString(value) : value; -}); -$data.Container.registerConverter($data.GeometryMultiPolygon, $data.Object, function (value) { - return value ? new $data.GeometryMultiPolygon(value) : value; -}); -$data.Container.registerConverter($data.GeometryCollection, $data.Object, function (value) { - return value ? new $data.GeometryCollection(value) : value; -});$data.Guid = function Guid(value) { - /// - - if (value === undefined || (typeof value === 'string' && /^[a-zA-z0-9]{8}-[a-zA-z0-9]{4}-[a-zA-z0-9]{4}-[a-zA-z0-9]{4}-[a-zA-z0-9]{12}$/.test(value))) { - this.value = value || '00000000-0000-0000-0000-000000000000'; - } else { - throw Guard.raise(new Exception('TypeError: ', 'value not convertable to $data.Guid', value)); - } -}; -$data.Container.registerType(['$data.Guid', 'Guid', 'guid'], $data.Guid); -$data.Container.registerConverter('$data.Guid', { - '$data.String': function (value) { - return value ? $data.parseGuid(value).toString() : value; - }, - '$data.Guid': function (value) { - return value ? value.toString() : value; - } -}, { - '$data.String': function (value) { - return value ? value.toString() : value; - } -}); - - -$data.Guid.prototype.toJSON = function () { - return this.value; -}; - -$data.Guid.prototype.valueOf = function () { - return this.value; -}; - -$data.Guid.prototype.toString = function () { - return this.value; -}; - -$data.Guid.NewGuid = function () { - return $data.createGuid(); -}; - -$data.parseGuid = function (guid) { - return new $data.Guid(guid); -}; - -(function () { - /*! - Math.uuid.js (v1.4) - http://www.broofa.com - mailto:robert@broofa.com - - Copyright (c) 2010 Robert Kieffer - Dual licensed under the MIT and GPL licenses. - */ - - var CHARS = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split(''); - - $data.createGuid = function (guidString) { - if (guidString) { - return new $data.Guid(guidString); - }; - - var len; - var chars = CHARS, uuid = [], i; - var radix = chars.length; - - if (len) { - // Compact form - for (i = 0; i < len; i++) uuid[i] = chars[0 | Math.random() * radix]; - } else { - // rfc4122, version 4 form - var r; - - // rfc4122 requires these characters - uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-'; - uuid[14] = '4'; - - // Fill in random data. At i==19 set the high bits of clock sequence as - // per rfc4122, sec. 4.1.5 - for (i = 0; i < 36; i++) { - if (!uuid[i]) { - r = 0 | Math.random() * 16; - uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r]; - } - } - } - - return $data.parseGuid(uuid.join('')); - }; -})();$data.Blob = function Blob(){}; - -$data.Blob.createFromHexString = function(value){ - if (value != value.match(new RegExp('[0-9a-fA-F]+'))[0]){ - Guard.raise(new Exception('TypeError: ', 'value not convertable to $data.Blob', value)); - }else{ - //if (value.length & 1) value = '0' + value; - var arr = new (typeof Buffer != 'undefined' ? Buffer : Uint8Array)(value.length >> 1); - for (var i = 0, j = 1, k = 0; i < value.length; i += 2, j += 2, k++) { - arr[k] = parseInt('0x' + value[i] + value[j], 16); - } - - return arr; - } -}; - -$data.Blob.toString = function(value){ - if (!value || !value.length) return null; - var s = ''; - for (var i = 0; i < value.length; i++){ - s += String.fromCharCode(value[i]); - } - - return s; -}; - -$data.Blob.toBase64 = function(value){ - if (!value || !value.length) return null; - return btoa($data.Blob.toString(value)); -}; - -$data.Blob.toArray = function(src){ - if (!src || !src.length) return null; - var arr = new Array(src.length); - for (var i = 0; i < src.length; i++){ - arr[i] = src[i]; - } - - return arr; -}; - -/*$data.Blob.toJSON = function(value){ - return JSON.stringify($data.Blob.toArray(value)); -};*/ - -$data.Blob.toHexString = function(value){ - if (!value || !value.length) return null; - var s = ''; - for (var i = 0; i < value.length; i++){ - s += ('00' + value[i].toString(16)).slice(-2); - } - - return s.toUpperCase(); -}; - -$data.Blob.toDataURL = function(value){ - if (!value || !value.length) return null; - return 'data:application/octet-stream;base64,' + btoa($data.Blob.toString(value)); -}; - -$data.Container.registerType(["$data.Blob", "blob", "JayBlob"], $data.Blob); -$data.Container.registerConverter('$data.Blob',{ - '$data.String': function (value){ - if (value && value.length){ - var blob = new (typeof Buffer !== 'undefined' ? Buffer : Uint8Array)(value.length); - for (var i = 0; i < value.length; i++){ - blob[i] = value.charCodeAt(i); - } - - return blob; - }else return null; - }, - '$data.Array': function(value){ - return new (typeof Buffer !== 'undefined' ? Buffer : Uint8Array)(value); - }, - '$data.Number': function(value){ - return new (typeof Buffer !== 'undefined' ? Buffer : Uint8Array)($data.packIEEE754(value, 11, 52).reverse()); - }, - '$data.Boolean': function(value){ - return new (typeof Buffer !== 'undefined' ? Buffer : Uint8Array)([value | 0]); - }, - 'default': function(value){ - if (typeof Blob !== 'undefined' && value instanceof Blob){ - var req = new XMLHttpRequest(); - req.open('GET', URL.createObjectURL(value), false); - req.responseType = 'arraybuffer'; - req.send(null); - return $data.Container.convertTo(req.response, $data.Blob); - } else if (typeof ArrayBuffer !== 'undefined' && value instanceof ArrayBuffer) { - return new (typeof Buffer !== 'undefined' ? Buffer : Uint8Array)(new Uint8Array(value)); - }else if (value instanceof Uint8Array){ - if (typeof Buffer !== 'undefined') return new Buffer(value); - else return value; - }else if (typeof Buffer !== 'undefined' ? value instanceof Buffer : false){ - return value; - }else if (value.buffer){ - return new (typeof Buffer !== 'undefined' ? Buffer : Uint8Array)(value); - }else if (typeof value == 'object' && value instanceof Object){ - var arr = []; - for (var i in value){ - arr[i] = value[i]; - } - if (!arr.length) throw 0; - return new (typeof Buffer !== 'undefined' ? Buffer : Uint8Array)(arr); - } - throw 0; - } -}, { - '$data.String': function(value){ - return $data.Blob.toString(value); - }, - '$data.Array': function(value){ - return $data.Blob.toArray(value); - } -}); -(function ($data) { - - function Edm_Boolean() { }; - $data.Container.registerType('Edm.Boolean', Edm_Boolean); - $data.Container.mapType(Edm_Boolean, $data.Boolean); - - function Edm_Binary() { }; - $data.Container.registerType('Edm.Binary', Edm_Binary); - $data.Container.mapType(Edm_Binary, $data.Blob); - - function Edm_DateTime() { }; - $data.Container.registerType('Edm.DateTime', Edm_DateTime); - $data.Container.mapType(Edm_DateTime, $data.Date); - - function Edm_DateTimeOffset() { }; - $data.Container.registerType('Edm.DateTimeOffset', Edm_DateTimeOffset); - $data.Container.mapType(Edm_DateTimeOffset, $data.DateTimeOffset); - - function Edm_Time() { }; - $data.Container.registerType('Edm.Time', Edm_Time); - $data.Container.mapType(Edm_Time, $data.Time); - - function Edm_Decimal() { }; - $data.Container.registerType('Edm.Decimal', Edm_Decimal); - $data.Container.mapType(Edm_Decimal, $data.Decimal); - - function Edm_Float() { }; - $data.Container.registerType('Edm.Float', Edm_Float); - $data.Container.mapType(Edm_Float, $data.Float); - - function Edm_Single() { }; - $data.Container.registerType('Edm.Single', Edm_Single); - $data.Container.mapType(Edm_Single, $data.Float); - - function Edm_Double() { }; - $data.Container.registerType('Edm.Double', Edm_Double); - $data.Container.mapType(Edm_Double, $data.Number); - - function Edm_Guid() { }; - $data.Container.registerType('Edm.Guid', Edm_Guid); - $data.Container.mapType(Edm_Guid, $data.Guid); - - function Edm_Int16() { }; - $data.Container.registerType('Edm.Int16', Edm_Int16); - $data.Container.mapType(Edm_Int16, $data.Int16); - - function Edm_Int32() { }; - $data.Container.registerType('Edm.Int32', Edm_Int32); - $data.Container.mapType(Edm_Int32, $data.Integer); - - function Edm_Int64() { }; - $data.Container.registerType('Edm.Int64', Edm_Int64); - $data.Container.mapType(Edm_Int64, $data.Int64); - - function Edm_Byte() { }; - $data.Container.registerType('Edm.Byte', Edm_Byte); - $data.Container.mapType(Edm_Byte, $data.Byte); - - function Edm_SByte() { }; - $data.Container.registerType('Edm.SByte', Edm_SByte); - $data.Container.mapType(Edm_SByte, $data.SByte); - - function Edm_String() { }; - $data.Container.registerType('Edm.String', Edm_String); - $data.Container.mapType(Edm_String, $data.String); - - function Edm_GeographyPoint() { }; - $data.Container.registerType('Edm.GeographyPoint', Edm_GeographyPoint); - $data.Container.mapType(Edm_GeographyPoint, $data.GeographyPoint); - - function Edm_GeographyLineString() { }; - $data.Container.registerType('Edm.GeographyLineString', Edm_GeographyLineString); - $data.Container.mapType(Edm_GeographyLineString, $data.GeographyLineString); - - function Edm_GeographyPolygon() { }; - $data.Container.registerType('Edm.GeographyPolygon', Edm_GeographyPolygon); - $data.Container.mapType(Edm_GeographyPolygon, $data.GeographyPolygon); - - function Edm_GeographyMultiPoint() { }; - $data.Container.registerType('Edm.GeographyMultiPoint', Edm_GeographyMultiPoint); - $data.Container.mapType(Edm_GeographyMultiPoint, $data.GeographyMultiPoint); - - function Edm_GeographyMultiLineString() { }; - $data.Container.registerType('Edm.GeographyMultiLineString', Edm_GeographyMultiLineString); - $data.Container.mapType(Edm_GeographyMultiLineString, $data.GeographyMultiLineString); - - function Edm_GeographyMultiPolygon() { }; - $data.Container.registerType('Edm.GeographyMultiPolygon', Edm_GeographyMultiPolygon); - $data.Container.mapType(Edm_GeographyMultiPolygon, $data.GeographyMultiPolygon); - - function Edm_GeographyCollection() { }; - $data.Container.registerType('Edm.GeographyCollection', Edm_GeographyCollection); - $data.Container.mapType(Edm_GeographyCollection, $data.GeographyCollection); - - function Edm_GeometryPoint() { }; - $data.Container.registerType('Edm.GeometryPoint', Edm_GeometryPoint); - $data.Container.mapType(Edm_GeometryPoint, $data.GeometryPoint); - - function Edm_GeometryLineString() { }; - $data.Container.registerType('Edm.GeometryLineString', Edm_GeometryLineString); - $data.Container.mapType(Edm_GeometryLineString, $data.GeometryLineString); - - function Edm_GeometryPolygon() { }; - $data.Container.registerType('Edm.GeometryPolygon', Edm_GeometryPolygon); - $data.Container.mapType(Edm_GeometryPolygon, $data.GeometryPolygon); - - function Edm_GeometryMultiPoint() { }; - $data.Container.registerType('Edm.GeometryMultiPoint', Edm_GeometryMultiPoint); - $data.Container.mapType(Edm_GeometryMultiPoint, $data.GeometryMultiPoint); - - function Edm_GeometryMultiLineString() { }; - $data.Container.registerType('Edm.GeometryMultiLineString', Edm_GeometryMultiLineString); - $data.Container.mapType(Edm_GeometryMultiLineString, $data.GeometryMultiLineString); - - function Edm_GeometryMultiPolygon() { }; - $data.Container.registerType('Edm.GeometryMultiPolygon', Edm_GeometryMultiPolygon); - $data.Container.mapType(Edm_GeometryMultiPolygon, $data.GeometryMultiPolygon); - - function Edm_GeometryCollection() { }; - $data.Container.registerType('Edm.GeometryCollection', Edm_GeometryCollection); - $data.Container.mapType(Edm_GeometryCollection, $data.GeometryCollection); - - $data.oDataEdmMapping = { - '$data.Byte': 'Edm.Byte', - '$data.SByte': 'Edm.SByte', - '$data.Decimal': 'Edm.Decimal', - '$data.Float': 'Edm.Float', - '$data.Int16': 'Edm.Int16', - '$data.Int64': 'Edm.Int64', - '$data.DateTimeOffset': 'Edm.DateTimeOffset', - '$data.Time': 'Edm.Time', - '$data.Boolean': 'Edm.Boolean', - '$data.Blob': 'Edm.Binary', - '$data.Date': 'Edm.DateTime', - '$data.Number': 'Edm.Double', - '$data.Integer': 'Edm.Int32', - '$data.Int32': 'Edm.Int32', - '$data.String': 'Edm.String', - '$data.ObjectID': 'Edm.String', - '$data.GeographyPoint': 'Edm.GeographyPoint', - '$data.GeographyLineString': 'Edm.GeographyLineString', - '$data.GeographyPolygon': 'Edm.GeographyPolygon', - '$data.GeographyMultiPoint': 'Edm.GeographyMultiPoint', - '$data.GeographyMultiLineString': 'Edm.GeographyMultiLineString', - '$data.GeographyMultiPolygon': 'Edm.GeographyMultiPolygon', - '$data.GeographyCollection': 'Edm.GeographyCollection', - '$data.GeometryPoint': 'Edm.GeometryPoint', - '$data.GeometryLineString': 'Edm.GeometryLineString', - '$data.GeometryPolygon': 'Edm.GeometryPolygon', - '$data.GeometryMultiPoint': 'Edm.GeometryMultiPoint', - '$data.GeometryMultiLineString': 'Edm.GeometryMultiLineString', - '$data.GeometryMultiPolygon': 'Edm.GeometryMultiPolygon', - '$data.GeometryCollection': 'Edm.GeometryCollection', - '$data.Guid': 'Edm.Guid' - }; - -})($data); -$data.Container.registerConverter('$data.Boolean', { - '$data.String': function(value){ - if (value.toLowerCase() == 'true') return true; - if (value.toLowerCase() == 'false') return false; - - return !!value; - }, - 'default': function(value){ - return !!value; - } -}); - -$data.Container.registerConverter('$data.Integer', { - 'default': function (value) { - if (value === Number.POSITIVE_INFINITY || - value === Number.NEGATIVE_INFINITY || - value === Number.MAX_VALUE || - value === Number.MIN_VALUE) { - return value; - } - - var r = parseInt(+value, 10); - if (isNaN(r)) throw 0; - return r; - } -}); - -$data.Container.registerConverter('$data.Int32', { - 'default': function (value) { - return value | 0; - } -}); - -$data.Container.registerConverter('$data.Number', { - 'default': function(value){ - var r = +value; - if (isNaN(r)) throw 0; - return r; - } -}); - -$data.Container.registerConverter('$data.Byte', { - 'default': function(value){ - return (value | 0) & 0xff; - } -}); - -$data.Container.registerConverter('$data.Date', { - 'default': function(value){ - var d = new Date(value); - if (isNaN(d)) throw 0; - return d; - } -}); - -$data.Container.registerConverter('$data.DateTimeOffset', { - '$data.Date': function(value){ - return value; - }, - 'default': function(value){ - var d = new Date(value); - if (isNaN(d)) throw 0; - return d; - } -}); -(function () { - function parseFromString(value) { - var regex = /^([0-9]|0[0-9]|1[0-9]|2[0-3]):([0-5][0-9]|[0-9])(:([0-5][0-9]|[0-9])(\.(\d+))?)?$/; - - var matches = regex.exec(value) - if (!matches) throw 0; - var time = ''; - time += ('00' + matches[1]).slice(-2); - time += ':' + ('00' + matches[2]).slice(-2); - if (matches[4]) { - time += ':' + ('00' + matches[4]).slice(-2); - } else { - time += ':00'; - } - if (matches[6]) - time += '.' + (matches[6] + '000').slice(0, 3); - - return time; - } - - $data.Container.registerConverter('$data.Time', { - '$data.String': parseFromString, - '$data.Number': function tt(value) { - var metrics = [1000, 60, 60]; - var result = [0, 0, 0, value | 0]; - - for (var i = 0; i < metrics.length; i++) { - result[metrics.length - (i + 1)] = (result[metrics.length - i] / metrics[i]) | 0; - result[metrics.length - i] -= result[metrics.length - (i + 1)] * metrics[i]; - } - - var time = ''; - for (var i = 0; i < result.length; i++) { - if (i < result.length - 1) { - time += ('00' + result[i]).slice(-2); - if (i < result.length - 2) time += ':'; - } else { - time += '.' + ('000' + result[i]).slice(-3); - } - } - - return parseFromString(time); - }, - '$data.Date': function (value) { - var val = value.getHours() + ':' + value.getMinutes() + ':' + value.getSeconds(); - var ms = value.getMilliseconds() - if (ms) { - val += '.' + ms; - } - - return parseFromString(val); - } - }); -})(); - -$data.Container.registerConverter('$data.Decimal', { - '$data.Boolean': function(value){ - return value ? '1' : '0'; - }, - '$data.Number': function(value){ - return value.toString(); - }, - '$data.String': function(value){ - if (!/^\-?([0-9]+(\.[0-9]+)?|Infinity)$/.test(value)) throw 0; - return value; - }, - '$data.Date': function(value){ - var r = value.valueOf(); - if (isNaN(r)) throw 0; - return r.toString(); - } -}); - -$data.packIEEE754 = function(v, ebits, fbits){ - var bias = (1 << (ebits - 1)) - 1, s, e, f, ln, i, bits, str, bytes; - - // Compute sign, exponent, fraction - if (v !== v){ - // NaN - // http://dev.w3.org/2006/webapi/WebIDL/#es-type-mapping - e = (1 << bias) - 1; f = Math.pow(2, fbits - 1); s = 0; - }else if (v === Infinity || v === -Infinity){ - e = (1 << bias) - 1; f = 0; s = (v < 0) ? 1 : 0; - }else if (v === 0){ - e = 0; f = 0; s = (1 / v === -Infinity) ? 1 : 0; - }else{ - s = v < 0; - v = Math.abs(v); - - if (v >= Math.pow(2, 1 - bias)){ - // Normalized - ln = Math.min(Math.floor(Math.log(v) / Math.LN2), bias); - e = ln + bias; - f = Math.round(v * Math.pow(2, fbits - ln) - Math.pow(2, fbits)); - }else{ - // Denormalized - e = 0; - f = Math.round(v / Math.pow(2, 1 - bias - fbits)); - } - } - - // Pack sign, exponent, fraction - bits = []; - for (i = fbits; i; i -= 1) { bits.push(f % 2 ? 1 : 0); f = Math.floor(f / 2); } - for (i = ebits; i; i -= 1) { bits.push(e % 2 ? 1 : 0); e = Math.floor(e / 2); } - bits.push(s ? 1 : 0); - bits.reverse(); - str = bits.join(''); - - // Bits to bytes - bytes = []; - while (str.length){ - bytes.push(parseInt(str.substring(0, 8), 2)); - str = str.substring(8); - } - - return bytes; -}; - -$data.unpackIEEE754 = function(bytes, ebits, fbits){ - // Bytes to bits - var bits = [], i, j, b, str, bias, s, e, f; - - for (i = bytes.length; i; i -= 1){ - b = bytes[i - 1]; - for (j = 8; j; j -= 1){ - bits.push(b % 2 ? 1 : 0); b = b >> 1; - } - } - bits.reverse(); - str = bits.join(''); - - // Unpack sign, exponent, fraction - bias = (1 << (ebits - 1)) - 1; - s = parseInt(str.substring(0, 1), 2) ? -1 : 1; - e = parseInt(str.substring(1, 1 + ebits), 2); - f = parseInt(str.substring(1 + ebits), 2); - - // Produce number - if (e === (1 << ebits) - 1){ - return f !== 0 ? NaN : s * Infinity; - }else if (e > 0){ - // Normalized - return s * Math.pow(2, e - bias) * (1 + f / Math.pow(2, fbits)); - }else if (f !== 0){ - // Denormalized - return s * Math.pow(2, -(bias - 1)) * (f / Math.pow(2, fbits)); - }else{ - return s < 0 ? -0 : 0; - } -}; - -$data.IEEE754 = function(v, e, f){ - return $data.unpackIEEE754($data.packIEEE754(v, e, f), e, f); -}; - -$data.Container.registerConverter('$data.Float', { - 'default': function(value){ - var r = +value; - if (isNaN(r)) throw 0; - return $data.IEEE754(r, 8, 23); - } -}); - -$data.Container.registerConverter('$data.Int16', { - 'default': function(value){ - var r = (value | 0) & 0xffff; - if (r >= 0x8000) return r - 0x10000; - return r; - } -}); - -$data.Container.registerConverter('$data.Int64', { - '$data.Boolean': function(value){ - return value ? '1' : '0'; - }, - '$data.Number': function(value){ - var r = value.toString(); - if (r.indexOf('.') > 0) return r.split('.')[0]; - if (r.indexOf('.') == 0) throw 0; - return r; - }, - '$data.String': function(value){ - if (!/^\-?([0-9]+(\.[0-9]+)?|Infinity)$/.test(value)) throw 0; - if (value.indexOf('.') > 0) return value.split('.')[0]; - if (value.indexOf('.') == 0) throw 0; - return value; - }, - '$data.Date': function(value){ - var r = value.valueOf(); - if (isNaN(r)) throw 0; - return r.toString(); - } -}); - -$data.Container.registerConverter('$data.SByte', { - 'default': function(value){ - var r = (value | 0) & 0xff; - if (r >= 0x80) return r - 0x100; - return r; - } -}); - -$data.Container.registerConverter('$data.String', { - '$data.Date': function(value){ - return value.toISOString(); - }, - '$data.ObjectID': function(value){ - return btoa(value.toString()); - }, - 'default': function(value){ - if (typeof value === 'object') return JSON.stringify(value); - return value.toString(); - } -}); - -$data.Container.registerConverter('$data.Object', { - '$data.String': function(value){ - return JSON.parse(value); - }, - '$data.Function': function(){ - throw 0; - } -}); - -$data.Container.registerConverter('$data.Array', { - '$data.String': function(value){ - var r = JSON.parse(value); - if (!Array.isArray(r)) throw 0; - return r; - } -}); - -$data.Container.registerConverter('$data.ObjectID', { - '$data.ObjectID': function(value){ - try{ - return btoa(value.toString()); - }catch(e){ - return value; - } - }, - '$data.String': function(id){ - return id; - } -}); - -$data.Container.proxyConverter = function(v){ return v; }; -$data.Container.defaultConverter = function(type){ return function(v){ return $data.Container.convertTo(v, type); }; }; -$data.StringFunctions = { - startsWith: function () { - var self, str; - if (arguments.length == 2) { - self = arguments[0]; - str = arguments[1]; - } else if (arguments.length == 1 && typeof this === 'string') { - self = this; - str = arguments[0]; - } else if (this instanceof String) { - self = this.valueOf(); - str = arguments[0]; - } else - return false; - - if (typeof self !== 'string') return false; - return self.indexOf(str) === 0; - }, - endsWith: function () { - var self, str; - if (arguments.length == 2) { - self = arguments[0]; - str = arguments[1]; - } else if (arguments.length == 1 && typeof this === 'string') { - self = this; - str = arguments[0]; - } else if (this instanceof String) { - self = this.valueOf(); - str = arguments[0]; - } else - return false; - - if (typeof self !== 'string') return false; - return self.slice(-str.length) === str; - }, - contains: function () { - var self, str; - if (arguments.length == 2) { - self = arguments[0]; - str = arguments[1]; - } else if (arguments.length == 1 && typeof this === 'string') { - self = this; - str = arguments[0]; - } else if (this instanceof String) { - self = this.valueOf(); - str = arguments[0]; - } else - return false; - - if (typeof self !== 'string') return false; - return self.indexOf(str) >= 0; - } -}; -//TODO: Finish refactoring ExpressionNode.js - -$data.Class.define("$data.Expressions.ExpressionType", null, null, {}, { - Constant: "constant", // { type:LITERAL, executable:true, valueType:, value: } - Variable: "variable", // { type:VARIABLE, executable:true, name: } - MemberAccess: "memberAccess", // { type:MEMBERACCESS, executable:true, expression:, member: } - Call: "call", - - /* binary operators */ - Equal: "equal", - NotEqual: "notEqual", - EqualTyped: "equalTyped", - NotEqualTyped: "notEqualTyped", - GreaterThen: "greaterThan", - LessThen: "lessThan", - GreaterThenOrEqual: "greaterThanOrEqual", - LessThenOrEqual: "lessThenOrEqual", - Or: "or", - OrBitwise: "orBitwise", - And: "and", - AndBitwise: "andBitwise", - - - In: "in", - - Add: "add", - Divide: "divide", - Multiply: "multiply", - Subtract: "subtract", - Modulo: "modulo", - ArrayIndex: "arrayIndex", - - /* unary operators */ - New: "new", - Positive: "positive", - Negative: "negative", - Increment: "increment", - Decrement: "decrement", - Not: "not", - - - This: "this", - LambdaParameterReference: "lambdaParameterReference", - LambdaParameter: "lambdaParameter", - Parameter: "parameter", - - ArrayLiteral: "arrayLiteral", - ObjectLiteral: "objectLiteral", - ObjectField: "objectField", - Function: "Function", - Unknown: "UNKNOWN", - - EntitySet: "EntitySet", - ServiceOperation: "ServiceOperation", - EntityField: "EntityField", - EntityContext: "EntityContext", - Entity: "Entity", - Filter: "Filter", - First: "First", - Count: "Count", - InlineCount: "InlineCount", - Single: "Single", - Find: "Find", - Some: "Some", - Every: "Every", - ToArray: "ToArray", - BatchDelete: "BatchDelete", - ForEach: "ForEach", - Projection: "Projection", - EntityMember: "EntityMember", - EntityFieldOperation: "EntityFieldOperation", - FrameOperation: "FrameOperation", - EntityFunctionOperation: "EntityFunctionOperation", - ContextFunctionOperation: "ContextFunctionOperation", - EntityBinary: "EntityBinary", - Code: "Code", - ParametricQuery: "ParametricQuery", - MemberInfo: "MemberInfo", - QueryParameter: "QueryParameter", - ComplexEntityField: "ComplexEntityField", - - Take: "Take", - Skip: "Skip", - OrderBy: "OrderBy", - OrderByDescending: "OrderByDescending", - Include: "Include", - - IndexedPhysicalAnd:"IndexedDBPhysicalAndFilterExpression", - IndexedLogicalAnd:"IndexedDBLogicalAndFilterExpression", - IndexedLogicalOr: "IndexedDBLogicalOrFilterExpression", - IndexedLogicalIn: "IndexedDBLogicalInFilterExpression" -}); - -$data.BinaryOperator = function () { - /// - /// - /// -} - -$data.binaryOperators = [ - { operator: "==", expressionType: $data.Expressions.ExpressionType.Equal, type: "boolean", implementation: function (a, b) { return a == b; } }, - { operator: "===", expressionType: $data.Expressions.ExpressionType.EqualTyped, type: "boolean", implementation: function (a, b) { return a === b; } }, - { operator: "!=", expressionType: $data.Expressions.ExpressionType.NotEqual, type: "boolean", implementation: function (a, b) { return a != b; } }, - { operator: "!==", expressionType: $data.Expressions.ExpressionType.NotEqualTyped, type: "boolean", implementation: function (a, b) { return a !== b; } }, - { operator: ">", expressionType: $data.Expressions.ExpressionType.GreaterThen, type: "boolean", implementation: function (a, b) { return a > b; } }, - { operator: ">=", expressionType: $data.Expressions.ExpressionType.GreaterThenOrEqual, type: "boolean", implementation: function (a, b) { return a >= b; } }, - { operator: "<=", expressionType: $data.Expressions.ExpressionType.LessThenOrEqual, type: "boolean", implementation: function (a, b) { return a <= b; } }, - { operator: "<", expressionType: $data.Expressions.ExpressionType.LessThen, type: "boolean", implementation: function (a, b) { return a < b; } }, - { operator: "&&", expressionType: $data.Expressions.ExpressionType.And, type: "boolean", implementation: function (a, b) { return a && b; } }, - { operator: "||", expressionType: $data.Expressions.ExpressionType.Or, type: "boolean", implementation: function (a, b) { return a || b; } }, - { operator: "&", expressionType: $data.Expressions.ExpressionType.AndBitwise, type: "number", implementation: function (a, b) { return a & b; } }, - { operator: "|", expressionType: $data.Expressions.ExpressionType.OrBitwise, type: "number", implementation: function (a, b) { return a | b; } }, - { operator: "+", expressionType: $data.Expressions.ExpressionType.Add, type: "number", implementation: function (a, b) { return a + b; } }, - { operator: "-", expressionType: $data.Expressions.ExpressionType.Subtract, type: "number", implementation: function (a, b) { return a - b; } }, - { operator: "/", expressionType: $data.Expressions.ExpressionType.Divide, type: "number", implementation: function (a, b) { return a / b; } }, - { operator: "%", expressionType: $data.Expressions.ExpressionType.Modulo, type: "number", implementation: function (a, b) { return a % b; } }, - { operator: "*", expressionType: $data.Expressions.ExpressionType.Multiply, type: "number", implementation: function (a, b) { return a * b; } }, - { operator: "[", expressionType: $data.Expressions.ExpressionType.ArrayIndex, type: "number", implementation: function (a, b) { return a[b]; } }, - { operator: "in", expressionType: $data.Expressions.ExpressionType.In, type: 'boolean', implementation: function (a, b) { return a in b; } } -]; - - -$data.binaryOperators.resolve = function (operator) { - var result = $data.binaryOperators.filter(function (item) { return item.operator == operator; }); - if (result.length > 0) - return operator; - //Guard.raise("Unknown operator: " + operator); -}; - -$data.binaryOperators.contains = function (operator) { - return $data.binaryOperators.some(function (item) { return item.operator == operator; }); -}; - -$data.binaryOperators.getOperator = function (operator) { - /// - var result = $data.binaryOperators.filter(function (item) { return item.operator == operator; }); - if (result.length < 1) - Guard.raise("Unknown operator: " + operator); - return result[0]; -}; - - -$data.unaryOperators = [ - { operator: "+", arity: "prefix", expressionType: $data.Expressions.ExpressionType.Positive, type: "number", implementation: function (operand) { return +operand; } }, - { operator: "-", arity: "prefix", expressionType: $data.Expressions.ExpressionType.Negative, type: "number", implementation: function (operand) { return -operand; } }, - { operator: "++", arity: "prefix", expressionType: $data.Expressions.ExpressionType.Increment, type: "number", implementation: function (operand) { return ++operand; } }, - { operator: "--", arity: "prefix", expressionType: $data.Expressions.ExpressionType.Decrement, type: "number", implementation: function (operand) { return --operand; } }, - { operator: "++", arity: "suffix", expressionType: $data.Expressions.ExpressionType.Increment, type: "number", implementation: function (operand) { return operand++; } }, - { operator: "!", arity: "prefix", expressionType: $data.Expressions.ExpressionType.Not, type: "boolean", implementation: function (operand) { return !operand; } }, - { operator: "--", arity: "suffix", expressionType: $data.Expressions.ExpressionType.Decrement, type: "number", implementation: function (operand) { return operand--; } } - - //{ operator: "new", expressionType : $data.Expressions.ExpressionType.New, type: "object", implementation: function(operand) { return new operand; } -]; - -$data.unaryOperators.resolve = function (operator) { - var result = $data.unaryOperators.filter(function (item) { return item.operator == operator; }); - if (result.length > 0) - return operator; - //Guard.raise("Unknown operator: " + operator); -}; - -$data.unaryOperators.contains = function (operator) { - return $data.unaryOperators.some(function (item) { return item.operator == operator; }); -}; - -$data.unaryOperators.getOperator = function (operator, arity) { - /// - var result = $data.unaryOperators.filter(function (item) { return item.operator == operator && (!arity || item.arity == arity); }); - if (result.length < 1) - Guard.raise("Unknown operator: " + operator); - return result[0]; -}; - - -$data.timeIt = function (fn, iterations) { - iterations = iterations || 1; - - console.time("!"); - for (var i = 0; i < iterations; i++) { - fn(); - } - console.timeEnd("!"); -} - -$data.Expressions.OperatorTypes = { - UNARY: "UNARY", // { type:UNARY, executable:true, operator:, operand: } - INCDEC: "INCDEC", // { type:INCDEC, executable:true, operator:, operand:, suffix: } - DECISION: "DECISION", // { type:DECISION, executable:true, expression:, left:, right: } - METHODCALL: "METHODCALL", // { type:METHODCALL, executable:true, object:, method:, args: } - NEW: "NEW", // { type:NEW, executable:true, values: [] }; - JSONASSIGN: "JSONASSIGN", // { type:JSONASSIGN, executable:true, left:, right: } - ARRAYACCESS: "ARRAYACCESS", // { type:ARRAYACCESS, executable:true, array:, index: } - UNKNOWN: "UNKNOWN" -}; - -$data.executable = true; - -function jsonify(obj) { return JSON.stringify(obj, null, "\t"); } - -$C('$data.Expressions.ExpressionNode', null, null, { - constructor: function () { - ///Provides a base class for all Expressions. - ///Represents the expression type of the node - ///For the list of expression node types refer to $data.Expressions.ExpressionType - /// - ///The result type of the expression - ///True if the expression can be evaluated to yield a result - ///this.nodeType = $data.Expressions.ExpressionType.Unknown; - ///this.type = type; - ///this.nodeType = $data.Expressions.ExpressionType.Unknown; - ///this.executable = (executable === undefined || executable === null) ? true : executable; - ///TODO - this.expressionType = this.constructor; - }, - - getJSON: function () { return jsonify(this); }, - - //TOBLOG maybe - /*expressionType: { - value: undefined, - ////OPTIMIZE - set: function (value) { - var _expressionType; - Object.defineProperty(this, "expressionType", { - set: function (value) { - if (typeof value === 'string') { - value = Container.resolveType(value); - } - _expressionType = value; - }, - get: function (value) { - //IE ommits listing JSON.stringify in call chain - - if (arguments.callee.caller == jsonify || arguments.callee.caller == JSON.stringify) { - return Container.resolveName(_expressionType); - } - return _expressionType; - }, - enumerable: true - }); - - this.expressionType = value; - }, - get: function () { return undefined; }, - enumerable: true - },*/ - expressionType: { - set: function (value) { - if (typeof value === 'string') { - value = Container.resolveType(value); - } - this._expressionType = value; - }, - get: function (value) { - //IE ommits listing JSON.stringify in call chain - - if (arguments.callee.caller == jsonify || arguments.callee.caller == JSON.stringify) { - return Container.resolveName(this._expressionType); - } - return this._expressionType; - }, - enumerable: true - }, - ///toString: function () { }, - nodeType: { value: $data.Expressions.ExpressionType.Unknown, writable: false }, - - type: {}, - - isTerminated: { value: false }, - - toString: function () { - return this.value; - } -}, null); - - -$C('$data.Expressions.UnaryExpression', $data.Expressions.ExpressionNode, null, { - constructor: function (operand, operator, nodeType, resolution) { - /// - /// Represents an operation with only one operand and an operator - /// - /// - /// - /// - /// - this.operand = operand; - this.operator = operator; - this.nodeType = nodeType; - this.resolution = resolution; - }, - - operator: { value: undefined, writable: true }, - operand: { value: undefined, writable: true }, - nodeType: { value: undefined, writable: true } -}); -$C('$data.Expressions.ArrayLiteralExpression', $data.Expressions.ExpressionNode, null, { - constructor: function (items) { - /// - /// - /// - this.items = items || []; - }, - nodeType: { value: $data.Expressions.ExpressionType.ArrayLiteral, writable: true }, - - items: { value: undefined, dataType: Array, elementType: $data.Expressions.ExpressionNode }, - - toString: function (debug) { - //var result; - //result = debug ? this.type + " " : ""; - //result = result + this.name; - ///Represents a call to an object or global method - ///The expression for object that has the method - ///The member descriptor - this.expression = expression; - this.member = member; - this.args = args; - }, - - nodeType: { - value: $data.Expressions.ExpressionType.Call - }, - - expression: { - value: undefined, - dataType: $data.Expressions.ExpressionNode, - writable: true - }, - - member: { - value: undefined, - dataType: $data.MemberDefinition, - writable: true - }, - - type: { - value: undefined, - writable: true - }, - - implementation: { - get: function () { - return function(thisObj, method, args) { - if (typeof method !== 'function') { - method = thisObj[method]; - } - Guard.requireType("method", method, Function); - return method.apply(thisObj, args); - }; - }, - set: function (value) { Guard.raise("Property can not be set"); } - }, - - toString: function (debug) { - return this.object.toString() + "." + this.member.toString() + "(" + ")"; - } - -}); -$C('$data.Expressions.CodeParser', null, null, { - - constructor: function (scopeContext) { - /// - /// - /// - /// - /// - this.scopeContext = scopeContext; - this.lambdaParams = []; - }, - - log: function(logInfo) { - if (this.scopeContext) - this.scopeContext.log(logInfo); - }, - - parseExpression: function (code, resolver) { - /// - ///Parses the provided code and returns a parser result with parser information - ///The JavaScript code to parse ex: "function (a,b,c) { return a + b /c }" - ///The ParameterResolver class that resolves vaiable and parameteres references - /// - /// - if (typeof code === 'object') { code = ''; } - var result = { - success: true, - errorMessage: '', - errorDetails: '' - }; - /// - - //console.log(code.toString()); - if ($data.Acorn){ - //console.log('using acorn.js'); - return { success: true, expression: this.ParserBuild($data.Acorn.parse('(' + code.toString() + ')').body[0]), errors: [] }; - }else if ($data.Esprima){ - //console.log('using esprima.js'); - return { success: true, expression: this.ParserBuild($data.Esprima.parse('(' + code.toString() + ')').body[0]), errors: [] }; - }else{ - //console.log('using JayLint'); - var AST = $data.ASTParser.parseCode(code); - this.log({ event: "AST", data: AST }); - if (!AST.success) { - return { - success: false, - error: "ASTParser error", - errorMessage: (AST.errors) ? JSON.stringify(AST.errors) : "could not get code" - }; - } - var b = this.Build2(AST.tree.first[0]); - result = { success: true, expression: b, errors: AST.errors }; - return result; - } - }, - - createExpression: function (code, resolver) { - /// - ///Parses the provided code and returns a JavaScript code expression tree - ///The JavaScript code to parse ex: "a + b /c" - ///The ParameterResolver class that resolves vaiable and parameteres references - /// - /// - /// - ///Parses the provided code and returns a JavaScript code expression tree - ///The JavaScript function to parse ex: "function (a,b,c) { return a + b /c }" - ///The ParameterResolver class that resolves vaiable and parameteres references - /// - /// - - var result = this.parseExpression(code, resolver); - if (!result.success) { - Guard.raise("ExpressionParserError: " + result.errorMessage); - } - return result.expression; - }, - - ParserBuild: function(node){ - //console.log(node); - return this['Parser' + node.type](node); - }, - - ParserExpressionStatement: function(node){ - return this.ParserBuild(node.expression); - }, - - ParserBlockStatement: function(node){ - return this.ParserBuild(node.body[0]); - }, - - ParserReturnStatement: function(node){ - return this.ParserBuild(node.argument); - }, - - ParserMemberExpression: function(node){ - return new $data.Expressions.PropertyExpression( - this.ParserBuild(node.object), - new $data.Expressions.ConstantExpression(node.property.name || node.property.value, typeof (node.property.name || node.property.value)) - ); - }, - - ParserIdentifier: function(node){ - return this.ParserParameter(node, - this.lambdaParams.indexOf(node.name) > -1 - ? $data.Expressions.ExpressionType.LambdaParameterReference - : $data.Expressions.ExpressionType.Parameter - ); - }, - - ParserObjectExpression: function(node){ - var props = new Array(node.properties.length); - for (var i = 0; i < node.properties.length; i++){ - props[i] = this.ParserProperty(node.properties[i]); - } - - return new $data.Expressions.ObjectLiteralExpression(props); - }, - - ParserArrayExpression: function(node){ - var items = new Array(node.elements.length); - for (var i = 0; i < node.elements.length; i++){ - items[i] = this.ParserBuild(node.elements[i]); - } - - return new $data.Expressions.ArrayLiteralExpression(items); - }, - - ParserProperty: function(node){ - return new $data.Expressions.ObjectFieldExpression(node.key.name, this.ParserBuild(node.value)); - }, - - ParserFunctionExpression: function(node){ - var params = new Array(node.params.length); - for (var i = 0; i < node.params.length; i++){ - this.lambdaParams.push(node.params[i].name); - params[i] = this.ParserParameter(node.params[i], $data.Expressions.ExpressionType.LambdaParameter); - params[i].owningFunction = result; - } - var result = new $data.Expressions.FunctionExpression(node.id ? node.id.name : node.id, params, this.ParserBuild(node.body)); - - return result; - }, - - ParserParameter: function(node, nodeType){ - var result = new $data.Expressions.ParameterExpression(node.name, null, nodeType); - if (nodeType == $data.Expressions.ExpressionType.LambdaParameterReference){ - result.paramIndex = this.lambdaParams.indexOf(node.name); - } - - return result; - }, - - ParserLogicalExpression: function(node){ - return this.ParserBinaryExpression(node); - }, - - ParserOperators: { - value: { - "==": { expressionType: $data.Expressions.ExpressionType.Equal, type: "boolean", implementation: function (a, b) { return a == b; } }, - "===": { expressionType: $data.Expressions.ExpressionType.EqualTyped, type: "boolean", implementation: function (a, b) { return a === b; } }, - "!=": { expressionType: $data.Expressions.ExpressionType.NotEqual, type: "boolean", implementation: function (a, b) { return a != b; } }, - "!==": { expressionType: $data.Expressions.ExpressionType.NotEqualTyped, type: "boolean", implementation: function (a, b) { return a !== b; } }, - ">": { expressionType: $data.Expressions.ExpressionType.GreaterThen, type: "boolean", implementation: function (a, b) { return a > b; } }, - ">=": { expressionType: $data.Expressions.ExpressionType.GreaterThenOrEqual, type: "boolean", implementation: function (a, b) { return a >= b; } }, - "<=": { expressionType: $data.Expressions.ExpressionType.LessThenOrEqual, type: "boolean", implementation: function (a, b) { return a <= b; } }, - "<": { expressionType: $data.Expressions.ExpressionType.LessThen, type: "boolean", implementation: function (a, b) { return a < b; } }, - "&&": { expressionType: $data.Expressions.ExpressionType.And, type: "boolean", implementation: function (a, b) { return a && b; } }, - "||": { expressionType: $data.Expressions.ExpressionType.Or, type: "boolean", implementation: function (a, b) { return a || b; } }, - "&": { expressionType: $data.Expressions.ExpressionType.AndBitwise, type: "number", implementation: function (a, b) { return a & b; } }, - "|": { expressionType: $data.Expressions.ExpressionType.OrBitwise, type: "number", implementation: function (a, b) { return a | b; } }, - "+": { expressionType: $data.Expressions.ExpressionType.Add, type: "number", implementation: function (a, b) { return a + b; } }, - "-": { expressionType: $data.Expressions.ExpressionType.Subtract, type: "number", implementation: function (a, b) { return a - b; } }, - "/": { expressionType: $data.Expressions.ExpressionType.Divide, type: "number", implementation: function (a, b) { return a / b; } }, - "%": { expressionType: $data.Expressions.ExpressionType.Modulo, type: "number", implementation: function (a, b) { return a % b; } }, - "*": { expressionType: $data.Expressions.ExpressionType.Multiply, type: "number", implementation: function (a, b) { return a * b; } }, - "[": { expressionType: $data.Expressions.ExpressionType.ArrayIndex, type: "number", implementation: function (a, b) { return a[b]; } }, - "in": { expressionType: $data.Expressions.ExpressionType.In, type: 'boolean', implementation: function (a, b) { return a in b; } } - } - }, - - ParserUnaryOperators: { - value: { - "+": { arity: "prefix", expressionType: $data.Expressions.ExpressionType.Positive, type: "number", implementation: function (operand) { return +operand; } }, - "-": { arity: "prefix", expressionType: $data.Expressions.ExpressionType.Negative, type: "number", implementation: function (operand) { return -operand; } }, - "++true": { arity: "prefix", expressionType: $data.Expressions.ExpressionType.Increment, type: "number", implementation: function (operand) { return ++operand; } }, - "--true": { arity: "prefix", expressionType: $data.Expressions.ExpressionType.Decrement, type: "number", implementation: function (operand) { return --operand; } }, - "++false": { arity: "suffix", expressionType: $data.Expressions.ExpressionType.Increment, type: "number", implementation: function (operand) { return operand++; } }, - "!": { arity: "prefix", expressionType: $data.Expressions.ExpressionType.Not, type: "boolean", implementation: function (operand) { return !operand; } }, - "--false": { arity: "suffix", expressionType: $data.Expressions.ExpressionType.Decrement, type: "number", implementation: function (operand) { return operand--; } } - } - }, - - ParserUnaryExpression: function(node){ - return new $data.Expressions.UnaryExpression(this.ParserBuild(node.argument), this.ParserUnaryOperators[node.operator], this.ParserUnaryOperators[node.operator].expressionType); - }, - - ParserUpdateExpression: function(node){ - return new $data.Expressions.UnaryExpression(this.ParserBuild(node.argument), this.ParserUnaryOperators[node.operator + node.prefix], this.ParserUnaryOperators[node.operator + node.prefix].nodeType); - }, - - ParserBinaryExpression: function(node){ - return new $data.Expressions.SimpleBinaryExpression( - this.ParserBuild(node.left), - this.ParserBuild(node.right), - this.ParserOperators[node.operator].expressionType, - node.operator, - this.ParserOperators[node.operator].type - ); - }, - - ParserThisExpression: function(node){ - return new $data.Expressions.ThisExpression(); - }, - - ParserLiteral: function(node){ - return new $data.Expressions.ConstantExpression(node.value, typeof node.value); - }, - - ParserCallExpression: function(node){ - var method = this.ParserBuild(node.callee); - var args = new Array(node.arguments.length); - for (var i = 0; i < node.arguments.length; i++){ - args[i] = this.ParserBuild(node.arguments[i]); - } - - var member; - var expression; - switch (true){ - case method instanceof $data.Expressions.PropertyExpression: - expression = method.expression; - member = method.member; - break; - case method instanceof $data.Expressions.ParameterExpression: - expression = new $data.Expressions.ConstantExpression(null, typeof null); - member = method; - break; - } - - return new $data.Expressions.CallExpression(expression, member, args); - }/*, - - Build2: function (node) { - /// - /// - var n; - switch (node.arity) { - case "number": - case "string": - n = this.BuildConstant(node); - break; - case "prefix": - switch (node.value) { - case "{": - n = this.BuildObjectLiteral(node); - break; - case "[": - n = this.BuildArrayLiteral(node); - break; - case $data.unaryOperators.resolve(node.value): - n = this.BuildUnary(node); - break; - //TODO: default case - } - break; - case "suffix": - switch (node.value) { - case $data.unaryOperators.resolve(node.value): - n = this.BuildUnary(node); - break; - default: - Guard.raise("Unknown suffix: " + node.value); - } - break; - case "infix": - switch (node.value) { - case "[": - n = this.BuildArray(node); - break; - case $data.binaryOperators.resolve(node.value): - n = this.BuildSimpleBinary(node); - break; - case "function": - Guard.raise("Unexpected function arity"); - case "(": - n = this.BuildCall(node); - break; - case ".": - n = this.BuildProperty(node); - break; - default: - debugger; - //TODO: remove debugger, throw exception or break - } - break; - case "statement": - switch (node.value) { - case "function": - n = this.BuildFunction(node); - //TODO: consider adding break - } - break; - default: - switch (node.value) { - case "function": - n = this.BuildFunction(node); - break; - case "true": - case "false": - case "null": - n = this.BuildConstant(node); - break; - case "this": - n = this.BuildThis(node); - break; - default: - n = this.BuildParameter(node); - break; - } - } - return n; - }, - - BuildThis: function (node) { - var result = Container.createThisExpression(); - return result; - }, - - BuildConstant: function (node) { - /// - var value = node.value; - var type = node.type; - if (node.reserved === true) { - switch (node.value) { - case "true": value = true; type = typeof true; break; - case "false": value = false; type = typeof false; break; - case "null": value = null; type = typeof null; break; - //TODO: missing default case - } - } - var result = new $data.Expressions.ConstantExpression(value, type); - return result; - }, - - BuildFunctionParameter: function (node) { - - }, - - BuildArray: function (node) { - switch (node.second.type) { - case "string": - return this.BuildProperty(node); - case "number": - default: - return this.BuildSimpleBinary(node); - } - }, - - BuildParameter: function (node) { - /// - /// - var paramName = node.value; - //TODO - //var paramType = this.resolver.resolveParameterType(node); - var nodeType = node.funct ? $data.Expressions.ExpressionType.LambdaParameter : - this.lambdaParams.indexOf(node.value) > -1 ? - $data.Expressions.ExpressionType.LambdaParameterReference : $data.Expressions.ExpressionType.Parameter; - var result = new $data.Expressions.ParameterExpression(node.value, null, nodeType); - - if (nodeType == $data.Expressions.ExpressionType.LambdaParameterReference) { - result.paramIndex = this.lambdaParams.indexOf(node.value); - } - - return result; - }, - - BuildArrayLiteral: function(node) { - var self = this; - var items = node.first.map(function (item) { return self.Build2(item); }); - var result = new $data.Expressions.ArrayLiteralExpression(items); - return result; - }, - - BuildObjectLiteral: function (node) { - var self = this; - var fields = node.first.map(function (item) { - var eItem = self.Build2(item.first); - var result = new $data.Expressions.ObjectFieldExpression(item.value, eItem); - return result; - }); - var result = new $data.Expressions.ObjectLiteralExpression(fields); - return result; - }, - - BuildFunction: function (node) { - /// - /// - var self = this; - var paramStack = []; - var params = node.first && node.first.map(function (paramNode) { - //paramStack.push(paramNode.value); - this.lambdaParams.push(paramNode.value); - return self.BuildParameter(paramNode); - }, this); - params = params || []; - - //skipping return for convenience - //Possible we should raise an error as predicates and selectors can - //not be code blocks just expressions - - var hasReturn = node.block.length == 0 ? false : - node.block[0].value === "return" ? true : false; - var body = (node.block.length > 0) ? this.Build2(hasReturn ? node.block[0].first : node.block[0]) : null; - - paramStack.forEach(function () { this.lambdaParams.pop(); }, this); - - var result = new $data.Expressions.FunctionExpression(node.value, params, body); - params.forEach(function (param) { - param.owningFunction = result; - }); - - //TODO place on prototyope - result.name = node.name; - return result; - }, - - BuildCall: function (node) { - var self = this; - var method = self.Build2(node.first); - var args = node.second.map(function (exp) { return self.Build2(exp); }); - var member; - var expression; - switch(true){ - case method instanceof $data.Expressions.PropertyExpression: - expression = method.expression; - member = method.member; - break; - case method instanceof $data.Expressions.ParameterExpression: - expression = Container.createConstantExpression(null, typeof null); - member = method; - break; - //TODO: missing default case - } - - var result = Container.createCallExpression(expression, member, args); - return result; - }, - - BuildProperty: function (node) { - ///Builds a PropertyExpression from the AST node - /// - /// - var expression = this.Build2(node.first); - //TODO - //var type = expression.type; - //var member = type.getMemberDefinition() - //TODO how to not if????? - var member; - if (node.second.identifier) { - member = new $data.Expressions.ConstantExpression(node.second.value, "string"); - } else { - member = this.Build2(node.second); - } - var result = new $data.Expressions.PropertyExpression(expression, member); - return result; - }, - - - BuildUnary: function(node) { - var operator = $data.unaryOperators.getOperator(node.value, node.arity); - var nodeType = operator.expressionType; - var operand = this.Build2(node.first); - var result = new $data.Expressions.UnaryExpression(operand, operator, nodeType); - return result; - }, - - BuildSimpleBinary: function (node) { - /// - - var operator = $data.binaryOperators.getOperator(node.value); - var nodeType = operator.expressionType; - - var left = this.Build2(node.first || node.left); - var right = this.Build2(node.second || node.right); - var result = new $data.Expressions.SimpleBinaryExpression(left, right, nodeType, node.value, operator.type); - return result; - } - - //Build: function (node, expNode) { - // var n; - // switch (node.arity) { - // case "ternary": - // if (node.value == "?") - // n = this.BuildDecision(node, expNode); - // else - // Guard.raise("Value of ternary node isn't implemented: " + node.value); - // break; - // case null: - // default: - // Guard.raise("Arity isn't implemented: " + node.arity); - // } - // return n; - //},*/ - -}); - -$C('$data.Expressions.ConstantExpression', $data.Expressions.ExpressionNode, null, { - constructor: function (value, type, name) { - this.value = value; - //TODO - //this.type = Container.getTypeName(value); - - this.type = type; - this.name = name; - if (!Object.isNullOrUndefined(this.value)) { - this.type = Container.resolveType(this.type) - if (Container.resolveType(Container.getTypeName(this.value)) !== this.type) - this.value = Container.convertTo(value, this.type); - } - }, - nodeType: { value: $data.Expressions.ExpressionType.Constant, enumerable: true }, - type: { value: Object, writable: true }, - value: { value: undefined, writable: true }, - toString: function (debug) { - //return "[constant: " + this.value.toString() + "]"; - return this.value.toString(); - } -}); - - -$C('$data.Expressions.FunctionExpression', $data.Expressions.ExpressionNode, null, { - constructor: function (name, parameters, body) { - /// - ///Represents a function declaration. - ///Function name - ///The list of function parameters - /// - /// - ///The list of function parameters - ///The function body - - this.parameters = parameters || []; - this.name = name; - this.body = body; - }, - - toString: function (debug) { - var paramStrings = this.parameters.map(function (p) { - return p.toString(); - }); - paramStrings = paramStrings.join(","); - var bodyString = (this.body ? this.body.toString(debug) : ''); - return "function " + this.name + "(" + paramStrings + ") { " + bodyString + "}"; - }, - nodeType: { value: $data.Expressions.ExpressionType.Function, writable: true }, - parameters: { value: undefined, dataType: Array, elementType: $data.Expressions.ParameterExpression }, - body: { value: undefined, dataType: $data.Expressions.ExpressionNode }, - type: {} -}, null); -$C('$data.Expressions.ObjectFieldExpression', $data.Expressions.ExpressionNode, null, { - constructor: function (fieldName, expression) { - /// - /// - this.fieldName = fieldName; - this.expression = expression; - }, - nodeType: { value: $data.Expressions.ExpressionType.ObjectField, writable: true }, - - toString: function (debug) { - //var result; - //result = debug ? this.type + " " : ""; - //result = result + this.name; - var result = "unimplemented"; - return result; - } -}, null); - -$C('$data.Expressions.ObjectLiteralExpression', $data.Expressions.ExpressionNode, null, { - constructor: function (members) { - ///Represent an object initializer literal expression Ex: { prop: value} - /// - this.members = members; - }, - nodeType: { value: $data.Expressions.ExpressionType.ObjectLiteral, writable: true }, - - toString: function (debug) { - //var result; - //result = debug ? this.type + " " : ""; - //result = result + this.name; - var result = "unimplemented"; - return result; - }, - - implementation: { - get: function () { - return function(namesAndValues) { - var result = { }; - namesAndValues.forEach(function(item) { - result[item.name] = item.value; - }); - return result; - }; - }, - set: function () { - } - } - -}, null); -$C('$data.Expressions.PagingExpression', $data.Expressions.ExpressionNode, null, { - constructor: function (source, expression, nType) { - /// - /// - this.source = source; - this.amount = expression; - this.nodeType = nType; - }, - nodeType: { value: $data.Expressions.ExpressionType.Unknown, writable: true }, - - toString: function (debug) { - //var result; - //result = debug ? this.type + " " : ""; - //result = result + this.name; - var result = "unimplemented"; - return result; - } -}, null); -$C('$data.Expressions.ParameterExpression', $data.Expressions.ExpressionNode, null, { - constructor: function (name, type, nodeType) { - /// - /// - //this.writePropertyValue("name", name); - //this.writePropertyValue("type", type); - this.nodeType = nodeType || $data.Expressions.ExpressionType.Parameter; - this.name = name; - this.type = type || "unknown"; - var _owningFunction; - }, - - owningFunction: { value: undefined, enumerable: false }, - nodeType: { value: $data.Expressions.ExpressionType.Parameter, writable: true }, - name: { value: undefined, dataType: String, writable: true }, - type: { value: undefined, dataType: "object", writable: true}, - toString: function (debug) { - var result; - result = debug ? this.type + " " : ""; - result = result + this.name; - return result; - } -}, null); -$C('$data.Expressions.PropertyExpression', $data.Expressions.ExpressionNode, null, { - constructor: function (expression, member) { - ///Represents accessing a property or field of an object - ///The expression for the property owner object - ///The member descriptor - ///The expression for the property owner object - ///The member descriptor - - this.expression = expression; - this.member = member; - - this.type = member.dataType; - }, - - nodeType: { - value: $data.Expressions.ExpressionType.MemberAccess - }, - - expression: { - value: undefined, - dataType: $data.Expressions.ExpressionNode, - writable: true - }, - - implementation: { - get: function () { - return function (holder, memberName) { - if (holder[memberName] === undefined) - Guard.raise(new Exception("Parameter '" + memberName + "' not found in context", 'Property not found!')); - return holder[memberName]; - }; - }, - set: function () { - } - }, - - member: { - value: undefined, - dataType: $data.MemberDefinition, - writable: true - }, - - type: { - value: undefined, - writable: true - }, - - toString: function (debug) { - return this.expression.toString() + "." + this.member.toString(); - } - -}); - -$C('$data.Expressions.SimpleBinaryExpression', $data.Expressions.ExpressionNode, null, { - constructor: function (left, right, nodeType, operator, type, resolution) { - ///Represents a bin operation with left and right operands and an operator/// - ///The left element of the binary operation - ///The right element of the binary operation - /// - this.left = left; - this.right = right; - this.nodeType = nodeType; - this.operator = operator; - this.type = type; - this.resolution = resolution; - }, - - implementation: { - get: function () { - return $data.binaryOperators.getOperator(this.operator).implementation; - }, - set: function () { } - - }, - //nodeType: { value: $data.Expressions.ExpressionType }, - type: { value: "number", writable: true } -}); -$C('$data.Expressions.ThisExpression', $data.Expressions.ExpressionNode, null, { - nodeType: { value: $data.Expressions.ExpressionType.This } -}); -$C('$data.Expressions.ExpressionVisitor', null, null, - { - constructor: function () { - this._deep = 0; - }, - - Visit: function (eNode, context) { - /// - /// - /// - // - - //this._deep = this._deep + 1; - if (!eNode) { - return eNode; - } - - var result = null; - - switch (eNode.expressionType) { - case $data.Expressions.ParameterExpression: - result = this.VisitParameter(eNode, context); - break; - case $data.Expressions.ConstantExpression: - result = this.VisitConstant(eNode, context); - break; - case $data.Expressions.FunctionExpression: - result = this.VisitFunction(eNode, context); - break; - case $data.Expressions.CallExpression: - result = this.VisitCall(eNode, context); - break; - case $data.Expressions.SimpleBinaryExpression: - result = this.VisitBinary(eNode, context); - break; - case $data.Expressions.PropertyExpression: - result = this.VisitProperty(eNode, context); - break; - //result = th - case $data.Expressions.ThisExpression: - result = this.VisitThis(eNode, context); - break; - case $data.Expressions.ObjectLiteralExpression: - result = this.VisitObjectLiteral(eNode, context); - break; - case $data.Expressions.ObjectFieldExpression: - result = this.VisitObjectField(eNode, context); - break; - case $data.Expressions.ArrayLiteralExpression: - result = this.VisitArrayLiteral(eNode, context); - break; - case $data.Expressions.UnaryExpression: - result = this.VisitUnary(eNode, context); - break; - case $data.Expressions.EntityContextExpression: - result = this.VisitEntityContext(eNode, context); - break; - default: - debugger; - break; - //case VARIABLE: - - // result = this.VisitVariable(eNode, context); - // break; - //case MEMBERACCESS: - // result = this.VisitMember(eNode, context); - // break; - //case BINARY: - // result = this.VisitBinary(eNode, context); - // break; - //case UNARY: - // result = this.VisitUnary(eNode, context); - // break; - //case INCDEC: - // result = this.VisitIncDec(eNode, context); - // break; - //case EQUALITY: result = this.VisitEquality(eNode, context); break; - //case DECISION: result = this.VisitDecision(eNode, context); break; - //case METHODCALL: result = this.VisitMethodCall(eNode, context); break; - //case NEW: result = this.VisitNew(eNode, context); break; - //case JSONASSIGN: result = this.VisitJsonAssign(eNode, context); break; - //case ARRAYACCESS: result = this.VisitArrayAccess(eNode, context); break; - //default: - // Guard.raise("Type isn't implemented: " + eNode.type); - } - - this._deep = this._deep - 1; - return result; - }, - - VisitArrayLiteral: function(eNode, context) { - /// - var self = this; - var items = eNode.items.map(function (item) { - return self.Visit(item, context); - }); - var result = Container.createArrayLiteralExpression(items); - return result; - }, - - VisitObjectLiteral: function(eNode, context) { - /// - var self = this; - var members = eNode.members.map(function (member) { - return self.Visit(member, context); - }); - var result = Container.createObjectLiteralExpression(members); - return result; - }, - - VisitObjectField: function(eNode, context) { - /// - var expression = this.Visit(eNode.expression, context); - var result = Container.createObjectFieldExpression(eNode.fieldName, expression); - return result; - }, - - VisitThis: function (eNode, context) { - return eNode; - }, - VisitCall: function (eNode, context) { - /// - var self = this; - var args = eNode.args.map(function (arg) { return this.Visit(arg, context); }, this); - var expression = this.Visit(eNode.expression, context); - var member = this.Visit(eNode.member, context); - return new $data.Expressions.CallExpression(expression, member, args); - }, - - VisitParameter: function(eNode, context) { - /// - /// - //var result = new $data.Expressions.ParameterExpression(eNode.name, eNode.type, eNode.nodeType); - return eNode; - }, - - VisitConstant: function (eNode, context) { - /// - /// - //var result = new $data.Expressions.ParameterExpression(eNode.name, eNode.type, eNode.nodeType); - return eNode; - }, - - VisitFunction: function(eNode, context) { - /// - var self = this; - - var params = eNode.parameters.map(function (p, i) { - return self.Visit(p, context); - }); - - var body = self.Visit(eNode.body, context); - var result = new $data.Expressions.FunctionExpression(eNode.name, params, body); - return result; - }, - - VisitBinary: function (eNode, context) { - /// - /// - /// - // - - var left = this.Visit(eNode.left, context); - var right = this.Visit(eNode.right, context); - return new $data.Expressions.SimpleBinaryExpression(left, right, eNode.nodeType, eNode.operator, eNode.type); - }, - - VisitProperty: function (eNode, context) { - /// - var expression = this.Visit(eNode.expression, context); - var member = this.Visit(eNode.member, context); - return new $data.Expressions.PropertyExpression(expression, member); - //var member = - }, - - VisitUnary: function (eNode, context) { - /// - /// - /// - /// - var operand = this.Visit(eNode.operand, context); - if (operand === eNode.operand) - return eNode; - return new $data.Expressions.UnaryExpression(operand, eNode.operator, eNode.nodeType); - }, - - VisitEntityContext: function (eNode, context) { - /// - /// - //var result = new $data.Expressions.ParameterExpression(eNode.name, eNode.type, eNode.nodeType); - return eNode; - }, - - VisitDecision: function (eNode, context) { - /// - /// - /// - // - - var expression = this.Visit(eNode.expression, context); - var left = this.Visit(eNode.left, context); - var right = this.Visit(eNode.right, context); - if (expression === eNode.expression && left === eNode.left && right === eNode.right) - return eNode; - return $data.Expressions.ExpressionNodeTypes.DecisionExpressionNode.create(eNode.executable, expression, left, right); - }, - - VisitNew: function (eNode, context) { - /// - /// - /// - // - - var values = this.VisitArray(eNode.values, context); - if (values === eNode.values) - return eNode; - return $data.Expressions.ExpressionNodeTypes.NewExpressionNode.create(true, values); - }, - VisitArrayAccess: function (eNode, context) { - /// - /// - /// - // - - var array = this.Visit(eNode.array, context); - var index = this.Visit(eNode.index, context); - if (array === eNode.array && index === eNode.index) - return eNode; - return $data.Expressions.ExpressionNodeTypes.ArrayAccessExpressionNode.create(true, array, index); - }, - VisitArray: function (eNodes, context) { - var args = []; - var ok = true; - for (var i = 0; i < eNodes.length; i++) { - args[i] = this.Visit(eNodes[i], context); - ok = ok && args[i] === eNodes[i]; - } - return ok ? eNodes : args; - }, - GetMemberChain: function (memberAccess, context) { - // { type:MEMBERACCESS, executable:true, expression:, member: } - if (memberAccess.expression.type == MEMBERACCESS) { - var a = this.GetMemberChain(memberAccess.expression, context); - a.push(memberAccess.member); - return a; - } - return [memberAccess.expression, memberAccess.member]; - } - }, {});$C("$data.Expressions.ParameterProcessor", $data.Expressions.ExpressionVisitor, null, { - constructor: function () { - ///Provides a base class for several ParameterProcessors like GlobalParameterProcessor or LambdaParameterProcessor - }, - - Visit: function (node, context) { - if ((node instanceof $data.Expressions.ParameterExpression || - node instanceof $data.Expressions.ThisExpression) - && this.canResolve(node)) { - var result = this.resolve(node, context); - if (result !== node) - result["resolvedBy"] = this.constructor.name; - return result; - } else { - return node; - } - }, - - canResolve: function (paramExpression) { - /// - Guard.raise("Pure method"); - }, - resolve: function (paramExpression) { - /// - Guard.raise("Pure method"); - } -}); -$C("$data.Expressions.GlobalContextProcessor", $data.Expressions.ParameterProcessor, null, { - constructor: function (global) { - /// - this.global = global; - }, - - canResolve: function (paramExpression) { - /// - return paramExpression.nodeType == $data.Expressions.ExpressionType.Parameter && this.global && typeof this.global === 'object' && - paramExpression.name in this.global; - }, - - resolve: function (paramExpression) { - /// - /// - var resultValue = this.global[paramExpression.name]; - var expression = Container.createConstantExpression(resultValue, typeof resultValue, paramExpression.name); - return expression; - } - -}); - - - -$C("$data.Expressions.ConstantValueResolver", $data.Expressions.ParameterProcessor, null, { - constructor: function (paramsObject, global, scopeContext) { - /// - this.globalResolver = Container.createGlobalContextProcessor(global); - this.paramResolver = Container.createGlobalContextProcessor(paramsObject); - this.paramsObject = paramsObject; - this.scopeContext = scopeContext; - }, - - canResolve: function (paramExpression) { - /// - return (paramExpression.name === '$context') || (paramExpression.nodeType == $data.Expressions.ExpressionType.This && this.paramsObject) - ? true : (this.paramResolver.canResolve(paramExpression) || this.globalResolver.canResolve(paramExpression)); - }, - - resolve: function (paramExpression) { - /// - /// - if (paramExpression.name === '$context') { - return Container.createEntityContextExpression(this.scopeContext); - } - if (paramExpression.nodeType == $data.Expressions.ExpressionType.This) { - return Container.createConstantExpression(this.paramsObject, typeof this.paramsObject, 'this'); - } - return this.paramResolver.canResolve(paramExpression) ? this.paramResolver.resolve(paramExpression) : this.globalResolver.resolve(paramExpression); - } - -});$C("$data.Expressions.LocalContextProcessor", $data.Expressions.GlobalContextProcessor, null, { - constructor: function (evalMethod) { - /// - this.canResolve = function (paramExpression) { - /// - return paramExpression.nodeType == $data.Expressions.ExpressionType.Parameter && - (evalMethod("typeof " + paramExpression.name) !== 'undefined'); - }; - this.resolve = function(paramExpression) { - /// - /// - var resultValue = evalMethod(paramExpression.name); - var expression = Container.createConstantExpression(resultValue, typeof resultValue); - return expression; - }; - - } - }); -$C("$data.Expressions.LambdaParameterProcessor", $data.Expressions.ParameterProcessor, null, { - constructor: function (lambdaParameterTypeInfos) { - /// - /// - var paramIndices = {}; - var $idx = "name"; - - this.canResolve = function (paramExpression, context) { - if (paramExpression.nodeType == $data.Expressions.ExpressionType.LambdaParameter) { - var fnParams = paramExpression.owningFunction.parameters; - - if (fnParams.length == 1 && paramExpression.name == fnParams[0].name) { - paramIndices[paramExpression.name] = lambdaParameterTypeInfos[0]; - return true; - } - - for (var j = 0; j < fnParams.length; j++) { - if (fnParams[j].name == paramExpression.name) { - paramIndices[paramExpression.name] = lambdaParameterTypeInfos[j]; - return true; - } - } - return false; - } - return false; - }; - - this.resolve = function(paramExpression, context) { - var lambdaParamType = paramIndices[paramExpression.name]; - var result = Container.createParameterExpression(paramExpression.name, - lambdaParamType, - $data.Expressions.ExpressionType.LambdaParameter); - result.owningFunction = paramExpression.owningFunction; - return result; - }; - } - -}); -$C('$data.Expressions.ParameterResolverVisitor', $data.Expressions.ExpressionVisitor, null, { - - constructor: function (expression, resolver) { - /// - /// ParameterResolverVisitor traverses the JavaScript Code Expression tree and converts - /// outer but otherwise execution local variable references into ConstantExpressions-t. - /// for example: context.Persons.filter(function(p) { return p.Name == document.location.href }) - /// is transformed into a constant that has the current href as its value - /// - /// - /// - this.lambdaParamCache = {}; - }, - - Visit: function (expression, resolver) { - /// - /// - //TODO base call is just ugly - return $data.Expressions.ExpressionVisitor.prototype.Visit.call(this, expression, resolver); - - }, - - - VisitArrayLiteral: function(eNode, context) { - var self = this; - var items = eNode.items.map(function (item) { return self.Visit(item, context); }); - var allLocal = items.every(function (item) { - return item instanceof $data.Expressions.ConstantExpression; - }); - - if (allLocal) { - items = items.map(function (item) { return item.value }); - return Container.createConstantExpression(items, "array"); - } else { - return Container.createArrayLiteralExpression(items); - } - }, - - VisitObjectLiteral: function(eNode, context) { - var self = this; - var members = eNode.members.map(function (item) { return self.Visit(item, context); }); - var allLocal = members.every(function (member) { - return member.expression instanceof $data.Expressions.ConstantExpression; - }); - - if (allLocal) { - var params = members.map(function (member) { return { name: member.fieldName, value: member.expression.value }; }); - var value = eNode.implementation(params); - return Container.createConstantExpression(value, typeof value); - } else { - return Container.createObjectLiteralExpression(members); - } - }, - - VisitThis: function(eNode, resolver) { - return resolver.Visit(eNode, resolver); - }, - - VisitParameter: function(eNode, resolver) { - /// - /// - /// - - var node; - ///TODO let the resolver handle lambdaReferences if it wants to deal with it - switch(eNode.nodeType){ - case $data.Expressions.ExpressionType.Parameter: - case $data.Expressions.ExpressionType.LambdaParameter: - node = resolver.Visit(eNode, resolver); - if (node.nodeType == $data.Expressions.ExpressionType.LambdaParameter) { - this.lambdaParamCache[node.name] = node; - } - return node; - case $data.Expressions.ExpressionType.LambdaParameterReference: - var lambdaParam = this.lambdaParamCache[eNode.name]; - if (lambdaParam) { - node = Container.createParameterExpression(eNode.name, - lambdaParam.type, - $data.Expressions.ExpressionType.LambdaParameterReference); - node.paramIndex = eNode.paramIndex; - //node.typeName = lambdaParam.type.name || lambdaParam.type; - return node; - } - break; - default: - return eNode; - - } - - - return eNode; - }, - - VisitConstant: function (eNode, context) { - /// - /// - return eNode; - }, - - VisitFunction: function(eNode, context) { - /// - - var self = this; - var params = eNode.parameters.map(function (p, i) { - var result = self.Visit(p, context); - return result; - }); - var body = self.Visit(eNode.body, context); - var result = new $data.Expressions.FunctionExpression(eNode.name, params, body); - - return result; - }, - - VisitBinary: function (eNode, context) { - /// - /// - /// - /// - - var left = this.Visit(eNode.left, context); - var right = this.Visit(eNode.right, context); - var expr = $data.Expressions; - - if (left instanceof expr.ConstantExpression && right instanceof expr.ConstantExpression) - { - var result = eNode.implementation(left.value, right.value); - return Container.createConstantExpression(result, typeof result); - } - return new Container.createSimpleBinaryExpression(left, right, eNode.nodeType, eNode.operator, eNode.type); - }, - - VisitUnary: function (eNode, context) { - /// - /// - /// - /// - - var operand = this.Visit(eNode.operand, context); - //var imp = $data.unaryOperators.getOperator( - var expr = $data.Expressions; - if (operand instanceof expr.ConstantExpression) - { - var result = eNode.operator.implementation(operand.value); - return Container.createConstantExpression(result, typeof result); - } - return new Container.createUnaryExpression(operand, eNode.operator, eNode.nodeType); - }, - - VisitProperty: function (eNode, context) { - /// - var expression = this.Visit(eNode.expression, context); - var member = this.Visit(eNode.member, context); - var result; - if (expression instanceof $data.Expressions.ConstantExpression && - member instanceof $data.Expressions.ConstantExpression) { - ///TODO implement checking for the member, throw on error - result = eNode.implementation(expression.value, member.value); - - //Method call processed before - //if (typeof result === 'function') { - // return new $data.Expressions.ConstantExpression( - // function () { return result.apply(expression.value, arguments); }); - //} - return Container.createConstantExpression(result, typeof result, expression.name + '$' + member.value); - } - if (expression === eNode.expression && member === eNode.member) - return eNode; - - result = Container.createPropertyExpression(expression, member); - return result; - }, - - VisitCall: function (eNode, context) { - /// - function isExecutable(args, body, obj) { - return body instanceof $data.Expressions.ConstantExpression && - //global methods will not have a this. - (!obj || obj instanceof $data.Expressions.ConstantExpression) && - args.every(function(item) { - return item instanceof $data.Expressions.ConstantExpression; - }); - } - var call = $data.Expressions.ExpressionVisitor.prototype.VisitCall.apply(this, arguments); - var obj = call.expression; - var body = call.member; - var args = call.args; - - function convertToValue(arg) { - if (arg instanceof $data.Expressions.ConstantExpression) - return arg.value; - return arg; - }; - - if (isExecutable(args, body, obj)) { - var fn = body.value; - if (typeof fn === 'string' && obj.value) { - fn = obj.value[fn]; - } - if (typeof fn !== 'function') { - //TODO dig that name out from somewhere - Guard.raise("Constant expression is not a method..."); - } - var value = eNode.implementation(obj.value, fn, args.map(convertToValue)); - return new $data.Expressions.ConstantExpression(value, typeof value); - } - return call; - } -}, {}); -$C("$data.Expressions.AggregatedVisitor", $data.Expressions.ExpressionVisitor, null, { - constructor: function (visitors) { - /// - - this.Visit = function (node, context) { - for (var i = 0; i < visitors.length; i++) { - var n = visitors[i].Visit(node, context); - if (n !== node) - return n; - } - return node; - }; - } - -}); -//"use strict"; // suspicious code - -$C('$data.Expressions.LogicalSchemaBinderVisitor', - $data.Expressions.ExpressionVisitor, null, - { - constructor: function (expression, binder) { - - }, - - VisitProperty: function (expression, context) { - /// - var exp = this.Visit(expression.expression, context); - var mem = this.Visit(expression.member, context); - - var type = exp.type; - var memberType = context.memberResolver.resolve(type, mem.value); - mem.type = memberType; - return Container.createPropertyExpression(exp, mem); - } - - }, {});$data.Class.define('$data.Expressions.ExpTreeVisitor', - null, null, - { - constructor: function () { - this._deep = 0; - }, - Visit: function (eNode, context) { - /// - /// - /// - // - this._deep = this._deep + 1; - var result = null; - switch (eNode.type) { - case LITERAL: result = this.VisitLiteral(eNode, context); break; - case VARIABLE: result = this.VisitVariable(eNode, context); break; - case MEMBERACCESS: result = this.VisitMember(eNode, context); break; - case BINARY: result = this.VisitBinary(eNode, context); break; - case UNARY: result = this.VisitUnary(eNode, context); break; - case INCDEC: result = this.VisitIncDec(eNode, context); break; - case EQUALITY: result = this.VisitEquality(eNode, context); break; - case DECISION: result = this.VisitDecision(eNode, context); break; - case METHODCALL: result = this.VisitMethodCall(eNode, context); break; - case NEW: result = this.VisitNew(eNode, context); break; - case JSONASSIGN: result = this.VisitJsonAssign(eNode, context); break; - case ARRAYACCESS: result = this.VisitArrayAccess(eNode, context); break; - default: - Guard.raise("Type isn't implemented: " + eNode.type); - } - this._deep = this._deep - 1; - return result; - }, - VisitLiteral: function (eNode, context) { - /// - /// - /// - // - - return eNode; - }, - VisitVariable: function (eNode, context) { - /// - /// - /// - // - - return eNode; - }, - VisitMember: function (eNode, context) { - /// - /// - /// - // - - var expression = this.Visit(eNode.expression, context); - var member = this.Visit(eNode.member, context); - if (expression === eNode.expression && member === eNode.member) - return eNode; - return $data.Expressions.ExpressionNodeTypes.MemberAccessExpressionNode.create(eNode.executable, expression, member); - }, - VisitBinary: function (eNode, context) { - /// - /// - /// - // - - var left = this.Visit(eNode.left, context); - var right = this.Visit(eNode.right, context); - if (left === eNode.left && right === eNode.right) - return eNode; - return $data.Expressions.ExpressionNodeTypes.BinaryExpressionNode.create(eNode.executable, eNode.operator, left, right); - }, - VisitUnary: function (eNode, context) { - /// - /// - /// - // - - var operand = this.Visit(eNode.operand, context); - if (operand === eNode.operand) - return eNode; - return $data.Expressions.ExpressionNodeTypes.UnaryExpressionNode.create(eNode.executable, eNode.operator, operand); - }, - VisitIncDec: function (eNode, context) { - /// - /// - /// - // - - var operand = this.Visit(eNode.operand, context); - if (operand === eNode.operand) - return eNode; - return $data.Expressions.ExpressionNodeTypes.IncDecExpressionNode.create(eNode.executable, eNode.operator, operand, eNode.suffix); - }, - VisitEquality: function (eNode, context) { - /// - /// - /// - // - - var left = this.Visit(eNode.left, context); - var right = this.Visit(eNode.right, context); - if (left === eNode.left && right === eNode.right) - return eNode; - return $data.Expressions.ExpressionNodeTypes.EqualityExpressionNode.create(eNode.executable, eNode.operator, left, right); - }, - VisitDecision: function (eNode, context) { - /// - /// - /// - // - - var expression = this.Visit(eNode.expression, context); - var left = this.Visit(eNode.left, context); - var right = this.Visit(eNode.right, context); - if (expression === eNode.expression && left === eNode.left && right === eNode.right) - return eNode; - return $data.Expressions.ExpressionNodeTypes.DecisionExpressionNode.create(eNode.executable, expression, left, right); - }, - VisitMethodCall: function (eNode, context) { - /// - /// - /// - // - - var object = eNode.object ? this.Visit(eNode.object, context) : null; - var args = this.VisitArray(eNode.args, context); - if (object === eNode.object && args === eNode.args) - return eNode; - return $data.Expressions.ExpressionNodeTypes.MethodcallExpressionNode.create(eNode.executable, object, eNode.method, args); - }, - VisitNew: function (eNode, context) { - /// - /// - /// - // - - var values = this.VisitArray(eNode.values, context); - if (values === eNode.values) - return eNode; - return $data.Expressions.ExpressionNodeTypes.NewExpressionNode.create(true, values); - }, - VisitJsonAssign: function (eNode, context) { - /// - /// - /// - // - - var left = this.Visit(eNode.left, context); - var right = this.Visit(eNode.right, context); - if (left === eNode.left && right === eNode.right) - return eNode; - left.JSONASSIGN = true; - right.JSONASSIGN = true; - return $data.Expressions.ExpressionNodeTypes.JsonAssignExpressionNode.create(true, left, right); - }, - VisitArrayAccess: function (eNode, context) { - /// - /// - /// - // - - var array = this.Visit(eNode.array, context); - var index = this.Visit(eNode.index, context); - if (array === eNode.array && index === eNode.index) - return eNode; - return $data.Expressions.ExpressionNodeTypes.ArrayAccessExpressionNode.create(true, array, index); - }, - VisitArray: function (eNodes, context) { - var args = []; - var ok = true; - for (var i = 0; i < eNodes.length; i++) { - args[i] = this.Visit(eNodes[i], context); - ok = ok && args[i] === eNodes[i]; - } - return ok ? eNodes : args; - }, - GetMemberChain: function (memberAccess, context) { - // { type:MEMBERACCESS, executable:true, expression:, member: } - if (memberAccess.expression.type == MEMBERACCESS) { - var a = this.GetMemberChain(memberAccess.expression, context); - a.push(memberAccess.member); - return a; - } - return [memberAccess.expression, memberAccess.member]; - } - }, {});$data.Class.define('$data.Expressions.SetExecutableVisitor', $data.Expressions.ExpTreeVisitor, null, -{ - Visit: function (eNode, context) { - switch (eNode.type) { - case LITERAL: return this.VisitLiteral(eNode, context); - case VARIABLE: return this.VisitVariable(eNode, context); - case MEMBERACCESS: return this.VisitMember(eNode, context); - case BINARY: return this.VisitBinary(eNode, context); - case UNARY: return this.VisitUnary(eNode, context); - case INCDEC: return this.VisitIncDec(eNode, context); - case EQUALITY: return this.VisitEquality(eNode, context); - case DECISION: return this.VisitDecision(eNode, context); - case METHODCALL: return this.VisitMethodCall(eNode, context); - case NEW: return this.VisitNew(eNode, context); - case JSONASSIGN: return this.VisitJsonAssign(eNode, context); - case ARRAYACCESS: return this.VisitArrayAccess(eNode, context); - default: - Guard.raise("Type isn't implemented: " + eNode.type); - } - }, - - VisitBinary: function (eNode, context) { - var left = this.Visit(eNode.left, context); - var right = this.Visit(eNode.right, context); - if (left === eNode.left && right === eNode.right && (left.executable && right.executable == eNode.executable)) - return eNode; - return $data.Expressions.ExpressionNodeTypes.BinaryExpressionNode.create(left.executable && right.executable, eNode.operator, left, right); - }, - VisitUnary: function (eNode, context) { - var operand = this.Visit(eNode.operand, context); - if (operand === eNode.operand) - return eNode; - return $data.Expressions.ExpressionNodeTypes.UnaryExpressionNode.create(operand.executable, eNode.operator, operand); - }, - VisitIncDec: function (eNode, context) { - var operand = this.Visit(eNode.operand, context); - if (operand === eNode.operand) - return eNode; - return $data.Expressions.ExpressionNodeTypes.IncDecExpressionNode.create(operand.executable, eNode.operator, operand, eNode.suffix); - }, - VisitEquality: function (eNode, context) { - var left = this.Visit(eNode.left, context); - var right = this.Visit(eNode.right, context); - if (left === eNode.left && right === eNode.right && (left.executable && right.executable == eNode.executable)) - return eNode; - return $data.Expressions.ExpressionNodeTypes.EqualityExpressionNode.create(left.executable && right.executable, eNode.operator, left, right); - }, - VisitDecision: function (eNode, context) { - var expression = this.Visit(eNode.expression, context); - var left = this.Visit(eNode.left, context); - var right = this.Visit(eNode.right, context); - if (expression === eNode.expression && left === eNode.left && right === eNode.right && (left.executable && right.executable && expression.executable == eNode.executable)) - return eNode; - return $data.Expressions.ExpressionNodeTypes.DecisionExpressionNode.create(left.executable && right.executable && expression.executable, expression, left, right); - }, - VisitMethodCall: function (eNode, context) { - var object = eNode.object ? this.Visit(eNode.object, context) : null; - var args = this.VisitArray(eNode.args, context); - if (object === eNode.object && args === eNode.args && ((object == null ? true : object.executable) == eNode.executable)) - return eNode; - return $data.Expressions.ExpressionNodeTypes.MethodcallExpressionNode.create(object == null ? true : object.executable, object, eNode.method, args); - }, - VisitNew: function (eNode, context) { - // { type:NEW, executable:true, values: [] }; - var values = this.VisitArray(eNode.values, context); - if (values === eNode.values) - return eNode; - return $data.Expressions.ExpressionNodeTypes.NewExpressionNode.create(true, values); - }, - VisitJsonAssign: function (eNode, context) { - // { type:JSONASSIGN, executable:true, left: variable, right: right } - var left = this.Visit(eNode.left, context); - var right = this.Visit(eNode.right, context); - if (left === eNode.left && right === eNode.right) - return eNode; - left.JSONASSIGN = true; - right.JSONASSIGN = true; - return $data.Expressions.ExpressionNodeTypes.JsonAssignExpressionNode.create(true, left, right); - }, - VisitArrayAccess: function (eNode, context) { - // { type:ARRAYACCESS, executable:true, array:, index: } - var array = this.Visit(eNode.array, context); - var index = this.Visit(eNode.index, context); - if (array === eNode.array && index === eNode.index) - return eNode; - return $data.Expressions.ExpressionNodeTypes.ArrayAccessExpressionNode.create(true, array, index); - }, - VisitArray: function (eNodes, context) { - var args = []; - var ok = true; - for (var i = 0; i < eNodes.length; i++) { - args[i] = this.Visit(eNodes[i], context); - ok = ok && args[i] === eNodes[i]; - } - return ok ? eNodes : args; - }, - - VisitLiteral: function (eNode, context) { - return { type: eNode.type, executable: true, value: eNode.value, valueType: eNode.valueType }; - }, - VisitVariable: function (eNode, context) { - if (typeof context.paramContext[eNode.name] == undefined) // isn't param //TODO: check ParamContext - Guard.raise("Variable is not defined in the paramContext: " + eNode.name); - //this._setExecutable(eNode, true); - return $data.Expressions.ExpressionNodeTypes.VariableExpressionNode.create(true, "Math", "GLOBALOBJECT"); - }, - VisitMember: function (eNode, context) { - var chain = this.GetMemberChain(eNode); - var firstMember = chain[0].name; - var isLambdaParam = context.lambdaParams.indexOf(firstMember) >= 0; - var isLocalParam = firstMember == context.paramsName; //TODO: check ParamContext // old: typeof context.paramContext[firstMember] != "undefined"; - if (!isLocalParam && !isLambdaParam) - Guard.raise("Variable is not defined in the paramContext or the lambda parameters: " + firstMember); - - return $data.Expressions.ExpressionNodeTypes.MemberAccessExpressionNode.create(isLocalParam, eNode.expression, eNode.member); - } -}, null);$data.Class.define('$data.Expressions.ExecutorVisitor', $data.Expressions.ExpTreeVisitor, null, -{ - //-- - VisitVariable: function (eNode, context) { - if (!eNode.executable) - return eNode; - var value = (eNode.name == context.paramsName) ? context.paramContext : window[eNode.name]; - if (typeof value == 'undefined') - Guard.raise( - new Exception("Unknown variable in '" + context.operation + "' operation. The variable isn't referenced in the parameter context and it's not a global variable: '" + eNode.name + "'.", - "InvalidOperation", { operationName: context.operation, missingParameterName: eNode.name }) - ); - return $data.Expressions.ExpressionNodeTypes.LiteralExpressionNode.create(true, typeof value, value); - }, - VisitMember: function (eNode, context) { - if (!eNode.executable) - return eNode; - var chain = this.GetMemberChain(eNode); - var value; - for (var i = 0; i < chain.length; i++) { - if (i == 0) - value = context.paramContext; - else - value = value[chain[i].name]; - } - return $data.Expressions.ExpressionNodeTypes.LiteralExpressionNode.create(true, typeof value, value); - - - }, - VisitUnary: function (eNode, context) { - var operand = this.Visit(eNode.operand, context); - if (operand !== eNode.operand) - eNode = $data.Expressions.ExpressionNodeTypes.UnaryExpressionNode.create(eNode.executable, eNode.operator, operand); - if (!eNode.executable) - return eNode; - // executing and returning with result as a literal - var value; - var src; - var operandValue = ((operand.valueType == "string") ? ("'" + operand.value + "'") : operand.value); - src = "value = " + eNode.operator + " " + operandValue; - eval(src); - - return $data.Expressions.ExpressionNodeTypes.LiteralExpressionNode.create(true, typeof value, value); - }, - VisitIncDec: function (eNode, context) { - var operand = this.Visit(eNode.operand, context); - if (operand !== eNode.operand) - eNode = $data.Expressions.ExpressionNodeTypes.IncDecExpressionNode.create(eNode.executable, eNode.operator, operand, eNode.suffix); - if (!eNode.executable) - return eNode; - // executing and returning with result as a literal - var value; - if (eNode.suffix) - value = eNode.operator == "++" ? operand.value++ : operand.value--; - else - value = eNode.operator == "++" ? ++operand.value : --operand.value; - return $data.Expressions.ExpressionNodeTypes.LiteralExpressionNode.create(true, typeof value, value); - }, - VisitBinary: function (eNode, context) { - var left = this.Visit(eNode.left, context); - var right = this.Visit(eNode.right, context); - if (left !== eNode.left || right !== eNode.right) - eNode = $data.Expressions.ExpressionNodeTypes.BinaryExpressionNode.create(eNode.executable, eNode.operator, left, right); - if (!eNode.executable) - return eNode; - // executing and returning with result as a literal - var value; - var src; - var leftValue = ((left.valueType == "string") ? ("'" + left.value + "'") : left.value); - var rightValue = ((right.valueType == "string") ? ("'" + right.value + "'") : right.value); - src = "value = " + leftValue + " " + eNode.operator + " " + rightValue; - eval(src); - - return $data.Expressions.ExpressionNodeTypes.LiteralExpressionNode.create(true, typeof value, value); - }, - VisitEquality: function (eNode, context) { - var left = this.Visit(eNode.left, context); - var right = this.Visit(eNode.right, context); - if (left !== eNode.left || right !== eNode.right) - eNode = $data.Expressions.ExpressionNodeTypes.EqualityExpressionNode.create(eNode.executable, eNode.operator, left, right); - if (!eNode.executable) - return eNode; - // executing and returning with result as a literal - var value; - var src; - var leftValue = ((left.valueType == "string") ? ("'" + left.value + "'") : left.value); - var rightValue = ((right.valueType == "string") ? ("'" + right.value + "'") : right.value); - src = "value = " + leftValue + " " + eNode.operator + " " + rightValue; - eval(src); - return $data.Expressions.ExpressionNodeTypes.LiteralExpressionNode.create(true, typeof value, value); - }, - VisitDecision: function (eNode, context) { - var expression = this.Visit(eNode.expression, context); - var left = this.Visit(eNode.left, context); - var right = this.Visit(eNode.right, context); - if (expression !== eNode.expression || left !== eNode.left || right !== eNode.right) - eNode = $data.Expressions.ExpressionNodeTypes.DecisionExpressionNode.create(eNode.executable, expression, left, right); - if (!eNode.executable) - return eNode; - // executing and returning with result as a literal - var value = expression.value ? left.value : right.value; - return $data.Expressions.ExpressionNodeTypes.LiteralExpressionNode.create(true, typeof value, value); - }, - VisitMethodCall: function (eNode, context) { - var object = eNode.object ? this.Visit(eNode.object, context) : null; - var args = this.VisitArray(eNode.args, context); - if (object !== eNode.object || args != eNode.args) - eNode = $data.Expressions.ExpressionNodeTypes.MethodcallExpressionNode.create(eNode.executable, object, eNode.method, args); - if (!eNode.executable) - return eNode; - // executing and returning with result as a literal - var a = []; - for (var i = 0; i < args.length; i++) { - var arg = args[i]; - var t = typeof arg.value; - a.push((t == "string") ? ("'" + arg.value + "'") : arg.value); - } - var value; - var src = object ? - "value = object.value[eNode.method](" + a.join(",") + ");" - : - "value = " + eNode.method + "(" + a.join(",") + ");"; - eval(src); - - return $data.Expressions.ExpressionNodeTypes.LiteralExpressionNode.create(true, typeof value, value); - }, - VisitArrayAccess: function (eNode, context) { - // { type:ARRAYACCESS, executable:true, array:, index: } - var arrayNode = this.Visit(eNode.array, context); - var indexNode = this.Visit(eNode.index, context); - var value = arrayNode.value[indexNode.value]; - return $data.Expressions.ExpressionNodeTypes.LiteralExpressionNode.create(true, typeof value, value); - } -}, null); $data.Class.define('$data.Expressions.ExpressionBuilder', null, null, -{ - constructor: function (context) { - this.context = context; - }, - _isLambdaParam: function (name) { - var p = this.context.lambdaParams; - for (var i = 0; i < p.length; i++) { - if (p[i] == name) - return true; - } - return false; - }, - _isParam: function (name) { - return this.context.paramContext[name] != undefined; - }, - _isParamRoot: function (name) { - return this.context.paramsName == name; - }, - Build: function (node, expNode) { - var n; - switch (node.arity) { - case "infix": - if ("(" == node.value) - n = this.BuildMethodCall(node, expNode); - else if ("." == node.value) - n = this.BuildMember(node, expNode); - else if (["===", "==", "!==", "!=", ">", "<", ">=", "<="].indexOf(node.value) >= 0) - n = this.BuildEquality(node, expNode); - else if (["&&", "||"].indexOf(node.value) >= 0) - n = this.BuildBinary(node, expNode); - else if (["+", "-", "*", "/", "%"].indexOf(node.value) >= 0) - n = this.BuildBinary(node, expNode); - else if ("[" == node.value) - n = this.BuildArrayAccess(node, expNode); - else - Guard.raise("Value of infix node isn't implemented: " + node.value); - break; - case "prefix": - if (["+", "-", "!"].indexOf(node.value) >= 0) - n = this.BuildUnary(node, expNode); - else if (["++", "--"].indexOf(node.value) >= 0) - n = this.BuildIncDec(node, expNode); - else if ("{" == node.value/* && "object" == node.type*/) //TODO: check the second condition necessity - n = this.BuildNewExpression(node, expNode); - else - Guard.raise("Value of prefix node isn't implemented: " + node.value); - break; - case "suffix": - if (["++", "--"].indexOf(node.value) >= 0) - n = this.BuildIncDec(node, expNode); - else - Guard.raise("Value of suffix node isn't implemented: " + node.value); - break; - case "string": - case "number": - n = this.BuildLiteral(node, expNode); //TODO: more arity to literal? - break; - case "ternary": - if (node.value == "?") - n = this.BuildDecision(node, expNode); - else - Guard.raise("Value of ternary node isn't implemented: " + node.value); - break; - case null: - case undefined: - if (node.type == "boolean" && (node.value == "true" || node.value == "false")) - n = this.BuildBoolLiteral(node, expNode); - else - n = this.BuildVariable(node, expNode); - break; - default: - Guard.raise("Arity isn't implemented: " + node.arity); - } - return n; - }, - BuildNewExpression: function (node, expNode) { - var newExpression = $data.Expressions.ExpressionNodeTypes.NewExpressionNode.create(true, []); - var n = node.first; - for (var i = 0; i < n.length; i++) - newExpression.values.push(this.Build(n[i], newExpression)); - return newExpression; - }, - BuildLiteral: function (node, expNode) { - return $data.Expressions.ExpressionNodeTypes.LiteralExpressionNode.create(true, node.arity, node.value); - }, - BuildBoolLiteral: function (node, expNode) { - return $data.Expressions.ExpressionNodeTypes.LiteralExpressionNode.create(true, node.type, node.value == "true" ? true : false); - }, - BuildVariable: function (node, expNode) { - if (!node.first) { - if (expNode.type == MEMBERACCESS) { - var subType; - if (this._isLambdaParam(node.value)) - subType = "LAMBDAPARAM"; - else if (this._isParamRoot(node.value)) - subType = "PARAMETERROOT"; - else if (this._isParam(node.value)) - subType = "PARAMETER"; - else - subType = "PROPERTY"; - } - else { - if (this._isLambdaParam(node.value)) - subType = "LAMBDAPARAM"; - else if (this._isParamRoot(node.value)) - subType = "PARAMETERROOT"; - else if (this._isParam(node.value)) - subType = "PARAMETER"; - else if (window[node.value] != undefined) - subType = "GLOBALOBJECT"; - else - Guard.raise( - new Exception("Unknown variable in '" + this.context.operation + "' operation. The variable isn't referenced in the parameter context and it's not a global variable: '" + node.value + "'.", - "InvalidOperation", { operationName: this.context.operation, missingParameterName: node.value }) - ); - } - return $data.Expressions.ExpressionNodeTypes.VariableExpressionNode.create(true, node.value, subType); - } - - var left = $data.Expressions.ExpressionNodeTypes.LiteralExpressionNode.create(true, "name", node.value); - - var jsonAssign = $data.Expressions.ExpressionNodeTypes.JsonAssignExpressionNode.create(true); - var right = this.Build(node.first, jsonAssign); - //left.parent = jsonAssign; - jsonAssign.left = left; - jsonAssign.right = right; - - left.JSONASSIGN = true; - right.JSONASSIGN = true; - - return jsonAssign; - }, - BuildMember: function (node, expNode) { - if (node.value != "." || node.arity != "infix") { - if (node.type == "string") { //TODO: more types? - return $data.Expressions.ExpressionNodeTypes.LiteralExpressionNode.create(true, node.arity, node.value); - } - return $data.Expressions.ExpressionNodeTypes.MemberAccessExpressionNode.create(true, null, node.value); - } - var result = $data.Expressions.ExpressionNodeTypes.MemberAccessExpressionNode.create(true); - var expression = this.Build(node.first, result); - var member = this.Build(node.second, result); - result.expression = expression; - result.member = member; - return result; - }, - BuildUnary: function (node, expNode) { - var result = $data.Expressions.ExpressionNodeTypes.UnaryExpressionNode.create(true, node.value); - result.operand = this.Build(node.first, result); - return result; - }, - BuildIncDec: function (node, expNode) { - var result = $data.Expressions.ExpressionNodeTypes.IncDecExpressionNode.create(true, node.value, null, node.arity == "suffix"); - result.operand = this.Build(node.first, result); - return result; - }, - BuildBinary: function (node, expNode) { - if (!node.first) Guard.raise("Cannot build binary: node.first is null"); - if (!node.second) Guard.raise("Cannot build binary: node.second is null"); - var result = $data.Expressions.ExpressionNodeTypes.BinaryExpressionNode.create(true, node.value); - result.left = this.Build(node.first, result); - result.right = this.Build(node.second, result); - return result; - }, - BuildEquality: function (node, expNode) { - var result = $data.Expressions.ExpressionNodeTypes.EqualityExpressionNode.create(true, node.value); - result.left = this.Build(node.first, result); - result.right = this.Build(node.second, result); - return result; - }, - BuildDecision: function (node, expNode) { - var result = $data.Expressions.ExpressionNodeTypes.DecisionExpressionNode.create(true); - result.expression = this.Build(node.first, result); - result.left = this.Build(node.second, result); - result.right = this.Build(node.third, result); - return result; - }, - BuildMethodCall: function (node, expNode) { - var result = $data.Expressions.ExpressionNodeTypes.MethodcallExpressionNode.create(true); - if (node.first.type == "function") { - //-- object's function - result.object = this.Build(node.first.first, result); - result.method = node.first.second.value; - } - else { - //-- global function - if (node.first.type != null) - Guard.raise("Cannot build MethodCall because type is " + type); - result.object = null; - result.method = node.first.value; - } - var argNodes = node.second; - var args = []; - for (var i = 0; i < argNodes.length; i++) { - var arg = argNodes[i]; - args[i] = this.Build(arg, result); - } - result.args = args; - return result; - }, - BuildArrayAccess: function (node, expNode) { - // { type:ARRAYACCESS, executable:true, array:, index: } - var result = $data.Expressions.ExpressionNodeTypes.ArrayAccessExpressionNode.create(true); - result.array = this.Build(node.first, result); - result.index = this.Build(node.second, result); - return result; - } -}, null);$C('$data.Expressions.AssociationInfoExpression', $data.Expressions.ExpressionNode, null, { - constructor: function (associationInfo) { - this.associationInfo = associationInfo; - }, - nodeType: { value: $data.Expressions.ExpressionType.AssociationInfo, enumerable: true } -});$C('$data.Expressions.CodeExpression', $data.Expressions.ExpressionNode, null, { - constructor: function (source, parameters) { - if (Container.resolveType(Container.getTypeName(source)) == $data.String && source.replace(/^[\s\xA0]+/, "").match("^function") != "function") { - source = "function (it) { return " + source + "; }"; - } - - this.source = source; - this.parameters = parameters; - }, - nodeType: { value: $data.Expressions.ExpressionType.Code, enumerable: true } -});$C('$data.Expressions.CodeToEntityConverter', $data.Expressions.ExpressionVisitor, null, { - constructor: function (scopeContext) { - ///This visitor converts a JS language tree into a semantical Entity Expression Tree This visitor should be invoked on a CodeExpression - ///context.thisArg contains parameters, context.lambdaParams should have an array value - this.scopeContext = scopeContext; - this.parameters = []; - - }, - - - VisitBinary: function (expression, context) { - var left = this.Visit(expression.left, context); - var right = this.Visit(expression.right, context); - - if ((!(left instanceof $data.Expressions.ConstantExpression) && right instanceof $data.Expressions.ConstantExpression) || - (!(right instanceof $data.Expressions.ConstantExpression) && left instanceof $data.Expressions.ConstantExpression)) { - - var refExpression, constExpr; - if (right instanceof $data.Expressions.ConstantExpression) { - refExpression = left; - constExpr = right; - } else { - refExpression = right; - constExpr = left; - } - - var memInfo; - if ((memInfo = refExpression.selector) instanceof $data.Expressions.MemberInfoExpression || - (memInfo = refExpression.operation) instanceof $data.Expressions.MemberInfoExpression) { - - - if (memInfo.memberDefinition && (memInfo.memberDefinition.type || memInfo.memberDefinition.dataType)) { - var fieldType = Container.resolveType(memInfo.memberDefinition.type || memInfo.memberDefinition.dataType); - var constExprType = Container.resolveType(constExpr.type); - - if (fieldType !== constExprType) { - - var value = constExpr.value; - if (expression.operator === $data.Expressions.ExpressionType.In) { - if (Array.isArray(value)) { - var resultExp = []; - for (var i = 0; i < value.length; i++) { - resultExp.push(new $data.Expressions.ConstantExpression(value[i], fieldType)); - } - value = resultExp; - fieldType = $data.Array; - } else { - fieldType = constExprType; - } - } - - if (right === constExpr) { - right = new $data.Expressions.ConstantExpression(value, fieldType, right.name); - } else { - left = new $data.Expressions.ConstantExpression(value, fieldType, left.name); - } - } - } - } - } - - var operatorResolution = this.scopeContext.resolveBinaryOperator(expression.nodeType, expression, context.frameType); - var result = Container.createSimpleBinaryExpression(left, right, expression.nodeType, expression.operator, expression.type, operatorResolution); - return result; - }, - - VisitUnary: function (expression, context) { - var operand = this.Visit(expression.operand, context); - var operatorResolution = this.scopeContext.resolveUnaryOperator(expression.nodeType, expression, context.frameType); - var result = Container.createUnaryExpression(operand, expression.operator, expression.nodeType, operatorResolution); - return result; - }, - - VisitParameter: function (expression, context) { - Guard.requireValue("context", context); - var et = $data.Expressions.ExpressionType; - switch (expression.nodeType) { - case et.LambdaParameterReference: - var result = Container.createEntityExpression(context.lambdaParameters[expression.paramIndex], { lambda: expression.name }); - return result; - case et.LambdaParameter: - //TODO: throw descriptive exception or return a value - break; - default: - Guard.raise("Global parameter " + expression.name + " not found. For query parameters use 'this.field' notation"); - break; - } - }, - - VisitThis: function (expression, context) { - ///converts the ThisExpression into a QueryParameterExpression tha't value will be evaluated and stored in this.parameters collection - var index = this.parameters.push({ name: "", value: undefined }) - 1; - var result = Container.createQueryParameterExpression("", index, context.queryParameters, undefined); - return result; - }, - - VisitFunction: function (expression, context) { - var result = $data.Expressions.ExpressionVisitor.prototype.VisitFunction.apply(this, arguments); - return result.body; - }, - - VisitCall: function (expression, context) { - //var exp = this.Visit(expression.expression); - var self = this; - var exp = this.Visit(expression.expression, context); - var member = this.Visit(expression.member, context); - var args = expression.args.map(function (arg) { - return self.Visit(arg, context); - }); - var result; - - ///filter=>function(p) { return p.Title == this.xyz.BogusFunction('asd','basd');} - switch (true) { - case exp instanceof $data.Expressions.QueryParameterExpression: - var argValues = args.map(function (a) { return a.value; }); - result = expression.implementation(exp.value, member.value, argValues); - //var args = expressions - return Container.createQueryParameterExpression(exp.name + "$" + member.value, exp.index, result, typeof result); - case exp instanceof $data.Expressions.EntityFieldExpression: - - case exp instanceof $data.Expressions.EntityFieldOperationExpression: - var operation = this.scopeContext.resolveFieldOperation(member.value, exp, context.frameType); - if (!operation) { - Guard.raise("Unknown entity field operation: " + member.getJSON()); - } - member = Container.createMemberInfoExpression(operation); - result = Container.createEntityFieldOperationExpression(exp, member, this._resolveFunctionArguments(args, operation.parameters)); - return result; - - case exp instanceof $data.Expressions.EntitySetExpression: - var operation = this.scopeContext.resolveSetOperations(member.value, exp, context.frameType); - if (!operation) { - Guard.raise("Unknown entity field operation: " + member.getJSON()); - } - member = Container.createMemberInfoExpression(operation); - result = Container.createFrameOperationExpression(exp, member, this._resolveFunctionArguments(args, operation.parameters)); - return result; - - case exp instanceof $data.Expressions.EntityExpression: - var operation = this.scopeContext.resolveTypeOperations(member.value, exp, context.frameType); - if (!operation) { - Guard.raise("Unknown entity function operation: " + member.getJSON()); - } - - member = Container.createMemberInfoExpression(operation); - result = Container.createEntityFunctionOperationExpression(exp, member, this._resolveFunctionArguments(args, operation.method.params)); - return result; - break; - case exp instanceof $data.Expressions.EntityContextExpression: - var operation = this.scopeContext.resolveContextOperations(member.value, exp, context.frameType); - if (!operation) { - Guard.raise("Unknown entity function operation: " + member.getJSON()); - } - - member = Container.createMemberInfoExpression(operation); - result = Container.createContextFunctionOperationExpression(exp, member, this._resolveFunctionArguments(args, operation.method.params)); - return result; - break; - default: - Guard.raise("VisitCall: Only fields can have operations: " + expression.getType().name); - //TODO we must not alter the visited tree - } - - }, - _resolveFunctionArguments: function (args, params) { - if (params) // remove current field poz - params = params.filter(function (p, i) { return p.name !== '@expression'; }); - - //objectArgs - if (args.length === 1 && args[0] instanceof $data.Expressions.ConstantExpression && typeof args[0].value === 'object' && args[0].value && params && params[0] && - args[0].value.constructor === $data.Object && params.some(function (param) { return param.name in args[0].value })) { - - return params.map(function (p) { - var type = p.type || p.dataType || args[0].type; - return new $data.Expressions.ConstantExpression(args[0].value[p.name], Container.resolveType(type), p.name); - }); - - } else { - return args.map(function (expr, i) { - if (expr instanceof $data.Expressions.ConstantExpression && params && params[i]) { - var type = params[i].type || params[i].dataType || expr.type; - return new $data.Expressions.ConstantExpression(expr.value, Container.resolveType(type), params[i].name); - } else { - return expr; - } - }); - } - }, - - VisitProperty: function (expression, context) { - /// - var exp = this.Visit(expression.expression, context); - var member = this.Visit(expression.member, context); - - //Guard.requireType("member", member, $data.Expressions.ConstantExpression); - Guard.requireType("member", member, $data.Expressions.ConstantExpression); - - function isPrimitiveType(memberDefinitionArg) { - - var t = memberDefinitionArg.dataType; - if (typeof t === 'function') { return false; } - - // suspicious code - /*switch (t) { - //TODO: implement this - }*/ - } - - switch (exp.expressionType) { - case $data.Expressions.EntityExpression: - var memberDefinition = exp.getMemberDefinition(member.value); - if (!memberDefinition) { - Guard.raise(new Exception("Unknown member: " + member.value, "MemberNotFound")); - } - //var storageMemberDefinition = - var storageField = memberDefinition.storageModel - .PhysicalType.memberDefinitions.getMember(memberDefinition.name); - var res; - var memberDefinitionExp; - switch (storageField.kind) { - case "property": - memberDefinitionExp = Container.createMemberInfoExpression(memberDefinition); - res = Container.createEntityFieldExpression(exp, memberDefinitionExp); - return res; - case "navProperty": - var assocInfo = memberDefinition.storageModel.Associations[memberDefinition.name]; - var setExpression = Container.createEntitySetExpression(exp, Container.createAssociationInfoExpression(assocInfo)); - if (assocInfo.ToMultiplicity !== "*") { - var ee = Container.createEntityExpression(setExpression, {}); - return ee; - }/* else { - context.lambdaParameters.push(setExpression); - }*/ - return setExpression; - case "complexProperty": - memberDefinitionExp = Container.createMemberInfoExpression(memberDefinition); - res = Container.createComplexTypeExpression(exp, memberDefinitionExp); - return res; - //TODO: missing default case - } - - //s/switch => property or navigationproperty - case $data.Expressions.ComplexTypeExpression: - var memDef = exp.getMemberDefinition(member.value); - if (!(memDef)) { - Guard.raise("Unknown member " + member.value + " on " + exp.entityType.name); - } - var memDefExp = Container.createMemberInfoExpression(memDef); - var result; - //TODO!!!! - if (Container.isPrimitiveType(Container.resolveType(memDef.dataType))) { - result = Container.createEntityFieldExpression(exp, memDefExp); - return result; - } - result = Container.createComplexTypeExpression(exp, memDefExp); - return result; - case $data.Expressions.QueryParameterExpression: - var value = expression.implementation(exp.value, member.value); - this.parameters[exp.index].name += "$" + member.value; - this.parameters[exp.index].value = value; - return Container.createQueryParameterExpression(exp.name + "$" + member.value, exp.index, value, Container.getTypeName(value)); - case $data.Expressions.EntityFieldExpression: - case $data.Expressions.EntityFieldOperationExpression: - var operation = this.scopeContext.resolveFieldOperation(member.value, exp, context.frameType); - if (!operation) { - Guard.raise("Unknown entity field operation: " + member.getJSON()); - } - member = Container.createMemberInfoExpression(operation); - result = Container.createEntityFieldOperationExpression(exp, member, []); - - return result; - default: - Guard.raise("Unknown expression type to handle: " + exp.expressionType.name); - } - } -});$C('$data.Expressions.ComplexTypeExpression', $data.Expressions.ExpressionNode, null, { - constructor: function (source, selector) { - /// - /// - /// - /// - /// - /// - /// - /// - Guard.requireType("source", source, [$data.Expressions.EntityExpression, $data.Expressions.ComplexTypeExpression]); - Guard.requireType("selector", selector, [$data.Expressions.EntityExpression, $data.Expressions.MemberInfoExpression]); - this.source = source; - this.selector = selector; - var dt = source.entityType.getMemberDefinition(selector.memberName).dataType; - var t = Container.resolveType(dt); - this.entityType = t; - }, - - getMemberDefinition: function (name) { - return this.entityType.getMemberDefinition(name); - }, - - nodeType: { value: $data.Expressions.ExpressionType.Com } -}); - -$C('$data.Expressions.EntityContextExpression', $data.Expressions.ExpressionNode, null, { - constructor: function (instance) { - /// - //Object.defineProperty(this, "instance", { value: instance, enumerable: false }); - this.instance = instance; - //this.storage_type = {}; - //this.typeName = this.type.name; - }, - instance: { enumerable: false }, - nodeType : { value: $data.Expressions.ExpressionType.EntityContext, enumerable: true } - -}); -$C('$data.Expressions.EntityExpression', $data.Expressions.ExpressionNode, null, { - constructor: function (source, selector) { - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - Guard.requireValue("source", source); - Guard.requireValue("selector", selector); - if (!(source instanceof $data.Expressions.EntitySetExpression) && !(source instanceof $data.Expressions.ServiceOperationExpression)) { - Guard.raise("Only EntitySetExpressions can be the source for an EntityExpression"); - } - - this.source = source; - this.selector = selector; - - this.entityType = this.source.elementType; - this.storageModel = this.source.storageModel; - - Guard.requireValue("entityType", this.entityType); - Guard.requireValue("storageModel", this.storageModel); - - }, - - getMemberDefinition: function (name) { - var memdef = this.entityType.getMemberDefinition(name); - if (!(memdef)) { - Guard.raise(new Exception("Unknown member " + name + " on type "+ this.entityType.name, "MemberNotFound")); - }; - memdef.storageModel = this.storageModel; - return memdef; - }, - - nodeType: { value: $data.Expressions.ExpressionType.Entity } -});$C('$data.Expressions.EntityExpressionVisitor', null, null, { - - constructor: function () { - this.lambdaTypes = []; - }, - - canVisit: function (expression) { - return expression instanceof $data.Expressions.ExpressionNode; - }, - - Visit: function (expression, context) { - if (!this.canVisit(expression)) - return expression; - - var visitorName = "Visit" + expression.getType().name; - if (visitorName in this) { - var fn = this[visitorName]; - var result = fn.call(this, expression, context); - if (typeof result === 'undefined') { - return expression; - } - return result; - } - //console.log("unhandled expression type:" + expression.getType().name); - return expression; - }, - VisitToArrayExpression: function (expression, context) { - var source = this.Visit(expression.source, context); - if (source !== expression.source) { - return Container.createToArrayExpression(source); - } - return expression; - }, - VisitForEachExpression: function (expression, context) { - var source = this.Visit(expression.source, context); - if (source !== expression.source) { - return Container.createForEachExpression(source); - } - return expression; - }, - VisitMemberInfoExpression: function (expression, context) { - return expression; - }, - - VisitSingleExpression: function (expression, context) { - var source = this.Visit(expression.source, context); - if (source !== expression.source) - return Container.createSingleExpression(source); - return expression; - }, - - VisitFirstExpression: function (expression, context) { - var source = this.Visit(expression.source, context); - if (source !== expression.source) - return Container.createFirstExpression(source); - return expression; - }, - - VisitSomeExpression: function (expression, context) { - var source = this.Visit(expression.source, context); - if (source !== expression.source) - return Container.createSomeExpression(source); - return expression; - }, - - VisitFindExpression: function (expression, context) { - var source = this.Visit(expression.source, context); - if (source !== expression.source) - return Container.createFindExpression(source); - return expression; - }, - - VisitEveryExpression: function (expression, context) { - var source = this.Visit(expression.source, context); - if (source !== expression.source) - return Container.createEveryExpression(source); - return expression; - }, - - VisitCountExpression: function (expression, context) { - var source = this.Visit(expression.source, context); - if (source !== expression.source) - return Container.createCountExpression(source); - return expression; - }, - - VisitBatchDeleteExpression: function (expression, context) { - var source = this.Visit(expression.source, context); - if (source !== expression.source) { - return Container.createBatchDeleteExpression(source); - } - return expression; - }, - - VisitObjectLiteralExpression: function (expression, context) { - var newValues = expression.members.map(function (ofe) { - return this.Visit(ofe, context); - }, this); - var equal = true; - for (var i = 0; i < expression.members.length; i++) { - equal = equal && (expression.members[i] === newValues[i]); - } - if (!equal) { - return Container.createObjectLiteralExpression(newValues); - } - return expression; - }, - VisitObjectFieldExpression: function (expression, context) { - var newExpression = this.Visit(expression.expression, context); - if (expression.expression !== newExpression) { - return Container.createObjectFieldExpression(expression.fieldName, newExpression); - } - return expression; - }, - VisitIncludeExpression: function (expression, context) { - var newExpression = this.Visit(expression.source, context); - if (newExpression !== expression.source) { - return Container.createIncludeExpression(newExpression, expression.selector); - } - return expression; - }, - - VisitUnaryExpression: function(expression, context) { - - /// - /// - var operand = this.Visit(expression.operand, context); - if (expression.operand !== operand) { - return Container.createUnaryExpression(operand, expression.operator, expression.nodeType, expression.resolution); - }; - return expression; - }, - - VisitSimpleBinaryExpression: function (expression, context) { - /// - /// - /// - // - var left = this.Visit(expression.left, context); - var right = this.Visit(expression.right, context); - if (left !== expression.left || right !== expression.right) { - return new $data.Expressions.SimpleBinaryExpression(left, right, expression.nodeType, - expression.operator, expression.type, expression.resolution); - } - return expression; - }, - - VisitEntityContextExpression: function (expression, context) { - return expression; - }, - - VisitCodeExpression: function (expression, context) { - /// - /// - /// - return expression; - }, - - VisitComplexTypeExpression: function (expression, context) { - var source = this.Visit(expression.source, context); - var selector = this.Visit(expression.selector, context); - if (source !== expression.source || selector !== expression.selector) { - var result = Container.createComplexTypeExpression(source, selector); - return result; - } - return expression; - }, - - VisitEntityExpression: function (expression, context) { - var source = this.Visit(expression.source, context); - var selector = this.Visit(expression.selector, context); - if (source !== expression.source || selector !== expression.selector) { - var result = Container.createEntityExpression(source, selector); - return result; - } - return expression; - }, - - VisitEntityFieldExpression: function (expression, context) { - var source = this.Visit(expression.source, context); - var selector = this.Visit(expression.selector, context); - if (source !== expression.source || selector !== expression.selector) { - var result = Container.createEntityFieldExpression(source, selector); - return result; - } - return expression; - }, - - VisitEntityFieldOperationExpression: function (expression, context) { - var source = this.Visit(expression.source, context); - var operation = this.Visit(expression.operation, context); - var parameters = expression.parameters.map(function (p) { - return this.Visit(p); - }, this); - var result = Container.createEntityFieldOperationExpression(source, operation, parameters); - return result; - }, - - VisitParametricQueryExpression: function (expression, context) { - var exp = this.Visit(expression.expression, context); - var args = expression.parameters.map(function (p) { - return this.Visit(p); - }, this); - var result = Container.createParametricQueryExpression(exp, args); - return result; - }, - - VisitEntitySetExpression: function (expression, context) { - var source = this.Visit(expression.source, context); - var selector = this.Visit(expression.selector, context); - if (source !== expression.source || selector !== expression.selector) { - return Container.createEntitySetExpression(source, selector, expression.params, expression.instance); - } - return expression; - }, - - VisitInlineCountExpression: function (expression, context) { - var source = this.Visit(expression.source, context); - var selector = this.Visit(expression.selector, context); - if (source !== expression.source || selector !== expression.selector) { - return Container.createInlineCountExpression(source, selector, expression.params, expression.instance); - } - return expression; - }, - - VisitFilterExpression: function (expression, context) { - var source = this.Visit(expression.source, context); - var selector = this.Visit(expression.selector, context); - if (source !== expression.source || selector !== expression.selector) { - return Container.createFilterExpression(source, selector, expression.params, expression.instance); - } - return expression; - }, - - VisitProjectionExpression: function (expression, context) { - var source = this.Visit(expression.source, context); - var selector = this.Visit(expression.selector, context); - if (source !== expression.source || selector !== expression.selector) { - var expr = Container.createProjectionExpression(source, selector, expression.params, expression.instance); - expr.projectionAs = expression.projectionAs; - return expr; - } - return expression; - }, - - VisitOrderExpression: function (expression, context) { - var source = this.Visit(expression.source, context); - var selector = this.Visit(expression.selector, context); - if (source !== expression.source || selector !== expression.selector) { - return Container.createOrderExpression(source, selector, expression.nodeType); - } - return expression; - }, - VisitPagingExpression: function (expression, context) { - var source = this.Visit(expression.source, context); - var amount = this.Visit(expression.amount, context); - if (source !== expression.source || amount !== expression.amount) { - return Container.createPagingExpression(source, amount, expression.nodeType); - } - return expression; - } -}); -$C('$data.Expressions.ExpressionMonitor', $data.Expressions.EntityExpressionVisitor, null, { - constructor: function (monitorDefinition) { - this.Visit = function (expression, context) { - - var result = expression; - var methodName; - if (this.canVisit(expression)) { - - //if (monitorDefinition.FilterExpressionNode) { - - //}; - - if (monitorDefinition.VisitExpressionNode) { - monitorDefinition.VisitExpressionNode.apply(monitorDefinition, arguments); - }; - - methodName = "Visit" + expression.getType().name; - if (methodName in monitorDefinition) { - result = monitorDefinition[methodName].apply(monitorDefinition, arguments); - } - } - - - //apply is about 3-4 times faster then call on webkit - - var args = arguments; - if (result !== expression) args = [result, context]; - result = $data.Expressions.EntityExpressionVisitor.prototype.Visit.apply(this, args); - - args = [result, context]; - - if (this.canVisit(result)) { - var expressionTypeName = result.getType().name; - if (monitorDefinition.MonitorExpressionNode) { - monitorDefinition.MonitorExpressionNode.apply(monitorDefinition, args); - } - methodName = "Monitor" + expressionTypeName; - if (methodName in monitorDefinition) { - monitorDefinition[methodName].apply(monitorDefinition, args); - } - - if (monitorDefinition.MutateExpressionNode) { - monitorDefinition.MutateExpressionNode.apply(monitorDefinition, args); - } - methodName = "Mutate" + expressionTypeName; - if (methodName in monitorDefinition) { - result = monitorDefinition[methodName].apply(monitorDefinition, args); - } - - } - return result; - }; - } -});$C('$data.Expressions.EntityFieldExpression', $data.Expressions.ExpressionNode, null, { - constructor: function (source, selector) { - /// - /// - this.selector = selector; - this.source = source; - - - if (this.selector instanceof $data.Expressions.MemberInfoExpression || this.selector.name) { - this.memberName = this.selector.name; - } - }, - - nodeType: { value: $data.Expressions.ExpressionType.EntityField } -}); - -$C('$data.Expressions.EntityFieldOperationExpression', $data.Expressions.ExpressionNode, null, { - constructor: function (source, operation, parameters) { - this.source = source; - this.operation = operation; - this.parameters = parameters; - }, - nodeType: { value: $data.Expressions.ExpressionType.EntityFieldOperation } - -});$C('$data.Expressions.EntitySetExpression', $data.Expressions.ExpressionNode, null, { - constructor: function (source, selector, params, instance) { - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - Guard.requireType("source", source, - [$data.Expressions.EntityContextExpression, $data.Expressions.EntitySetExpression]); - Guard.requireType("selector", source, - [$data.Expressions.MemberInfoExpression, $data.Expressions.CodeExpression, $data.Expressions.ParametricQueryExpression]); - - this.source = source; - this.selector = selector; - this.params = params; - //Object.defineProperty(this, "instance", { value: instance, enumerable: false, writable: true }); - this.instance = instance; - - function findContext() { - //TODO: use source from function parameter and return a value at the end of the function - var r = source; - while (r) { - if (r instanceof $data.Expressions.EntityContextExpression) { - return r; - } - r = r.source; - } - } - - ///TODO!!! - this.storage_type = {}; - var c = findContext(); - switch (true) { - case this.source instanceof $data.Expressions.EntityContextExpression: - Guard.requireType("selector", selector, $data.Expressions.MemberInfoExpression); - this.elementType = selector.memberDefinition.elementType; - this.storageModel = c.instance._storageModel.getStorageModel(this.elementType); - break; - case this.source instanceof $data.Expressions.EntityExpression: - Guard.requireType("selector", selector, $data.Expressions.AssociationInfoExpression); - this.elementType = selector.associationInfo.ToType; - this.storageModel = c.instance._storageModel.getStorageModel(this.elementType); - break; - case this.source instanceof $data.Expressions.EntitySetExpression: - this.elementType = this.source.elementType; - this.storageModel = this.source.storageModel; - break; - case this.source instanceof $data.Expressions.ServiceOperationExpression: - this.elementType = this.source.elementType;//????????? - this.storageModel = this.source.storageModel; - break; - default: - Guard.raise("take and skip must be the last expressions in the chain!"); - //Guard.raise("Unknown source type for EntitySetExpression: " + this.getType().name); - break; - } - - // suspicious code - /*if (this.source instanceof $data.Expressions.EntitySetExpression) { - //TODO: missing operation - }*/ - //EntityTypeInfo - - }, - instance: { enumerable: false }, - nodeType: { value: $data.Expressions.ExpressionType.EntitySet, enumerable: true } -}); -$C('$data.Expressions.FrameOperationExpression', $data.Expressions.ExpressionNode, null, { - constructor: function (source, operation, parameters) { - this.source = source; - this.operation = operation; - this.parameters = parameters; - }, - nodeType: { value: $data.Expressions.ExpressionType.FrameOperation } - -}); - -$C('$data.Expressions.EntityFunctionOperationExpression', $data.Expressions.FrameOperationExpression, null, { - nodeType: { value: $data.Expressions.ExpressionType.EntityFunctionOperation } -}); - -$C('$data.Expressions.ContextFunctionOperationExpression', $data.Expressions.FrameOperationExpression, null, { - nodeType: { value: $data.Expressions.ExpressionType.ContextFunctionOperation } -}); -$C('$data.Expressions.FilterExpression', $data.Expressions.EntitySetExpression, null, { - constructor: function (source, selector) { - /// - /// - /// - /// - /// - /// - /// - /// - this.resultType = $data.Array; - }, - nodeType: { value: $data.Expressions.ExpressionType.Filter, enumerable: true } -}); - -$C('$data.Expressions.InlineCountExpression', $data.Expressions.EntitySetExpression, null, { - constructor: function (source, selector) { - }, - nodeType: { value: $data.Expressions.ExpressionType.InlineCount, enumerable: true } -}); - -$C('$data.Expressions.FrameOperator', $data.Expressions.ExpressionNode, null, { - constructor: function () { - this.isTerminated = true; - } -}); - -$C('$data.Expressions.CountExpression', $data.Expressions.FrameOperator, null, { - constructor: function (source) { - /// - /// - /// - this.source = source; - this.resultType = $data.Integer; - }, - nodeType: { value: $data.Expressions.ExpressionType.Count, enumerable: true } -}); - -$C('$data.Expressions.SingleExpression', $data.Expressions.FrameOperator, null, { - constructor: function (source) { - /// - /// - /// - this.source = source; - this.resultType = $data.Object; - }, - nodeType: { value: $data.Expressions.ExpressionType.Single, enumerable: true } -}); - -$C('$data.Expressions.FindExpression', $data.Expressions.FrameOperator, null, { - constructor: function (source, params) { - /// - /// - /// - this.source = source; - this.params = params; - this.resultType = $data.Object; - }, - nodeType: { value: $data.Expressions.ExpressionType.Find, enumerable: true } -}); - -$C('$data.Expressions.FirstExpression', $data.Expressions.FrameOperator, null, { - constructor: function (source) { - /// - /// - /// - this.source = source; - this.resultType = $data.Object; - }, - nodeType: { value: $data.Expressions.ExpressionType.First, enumerable: true } -}); - -$C('$data.Expressions.ForEachExpression', $data.Expressions.FrameOperator, null, { - constructor: function (source) { - /// - /// - /// - this.source = source; - this.resultType = $data.Array; - }, - nodeType: { value: $data.Expressions.ExpressionType.ForEach, enumerable: true } -}); -$C('$data.Expressions.ToArrayExpression', $data.Expressions.FrameOperator, null, { - constructor: function (source) { - /// - /// - /// - this.source = source; - this.resultType = $data.Array; - }, - nodeType: { value: $data.Expressions.ExpressionType.ToArray, enumerable: true } -}); - -$C('$data.Expressions.SomeExpression', $data.Expressions.FrameOperator, null, { - constructor: function (source) { - /// - /// - /// - this.source = source; - this.resultType = $data.Object; - }, - nodeType: { value: $data.Expressions.ExpressionType.Some, enumerable: true } -}); - -$C('$data.Expressions.EveryExpression', $data.Expressions.FrameOperator, null, { - constructor: function (source) { - /// - /// - /// - this.source = source; - this.resultType = $data.Object; - }, - nodeType: { value: $data.Expressions.ExpressionType.Every, enumerable: true } -}); - -$C('$data.Expressions.BatchDeleteExpression', $data.Expressions.FrameOperator, null, { - constructor: function (source) { - /// - /// - /// - this.source = source; - this.resultType = $data.Integer; - }, - nodeType: { value: $data.Expressions.ExpressionType.BatchDelete, enumerable: true } -});$C('$data.Expressions.IncludeExpression', $data.Expressions.EntitySetExpression, null, { - constructor: function (source, selector) { - }, - nodeType: { value: $data.Expressions.ExpressionType.Include, writable: true }, - - toString: function (debug) { - //var result; - //result = debug ? this.type + " " : ""; - //result = result + this.name; - var result = "unimplemented"; - return result; - } -}, null); -$C('$data.Expressions.MemberInfoExpression', $data.Expressions.ExpressionNode, null, { - constructor: function (memberDefinition) { - this.memberDefinition = memberDefinition; - this.memberName = memberDefinition.name; - }, - nodeType: { value: $data.Expressions.ExpressionType.MemberInfo, enumerable: true } - -});$C('$data.Expressions.OrderExpression', $data.Expressions.EntitySetExpression, null, { - constructor: function (source, expression, nType) { - /// - /// - //this.source = source; - //this.selector = expression; - this.nodeType = nType; - }, - nodeType: { value: $data.Expressions.ExpressionType.OrderBy, writable: true }, - - toString: function (debug) { - //var result; - //result = debug ? this.type + " " : ""; - //result = result + this.name; - var result = "unimplemented"; - return result; - } -}, null); -$C('$data.Expressions.ParametricQueryExpression', $data.Expressions.ExpressionNode, null, { - constructor: function (expression, parameters) { - this.expression = expression; - this.parameters = parameters || []; - }, - nodeType: { value: $data.Expressions.ExpressionType.ParametricQuery, enumerable: true } -});$C('$data.Expressions.ProjectionExpression', $data.Expressions.EntitySetExpression, null, { - constructor: function (source, selector, params, instance) { - - }, - nodeType: { value: $data.Expressions.ExpressionType.Projection, enumerable: true } - -}); - - -$C('$data.Expressions.QueryExpressionCreator', $data.Expressions.EntityExpressionVisitor, null, { - constructor: function (scopeContext) { - /// - Guard.requireValue("scopeContext", scopeContext); - this.scopeContext = scopeContext; - }, - VisitEntitySetExpression: function (expression, context) { - if (expression.source instanceof $data.Expressions.EntityContextExpression) { - this.lambdaTypes.push(expression); - } - return expression; - }, - - VisitServiceOperationExpression: function (expression, context) { - if (expression.source instanceof $data.Expressions.EntityContextExpression) { - this.lambdaTypes.push(expression); - } - return expression; - }, - - VisitCodeExpression: function (expression, context) { - ///Converts the CodeExpression into an EntityExpression - /// - var source = expression.source.toString(); - var jsCodeTree = Container.createCodeParser(this.scopeContext).createExpression(source); - this.scopeContext.log({ event: "JSCodeExpression", data: jsCodeTree }); - - //TODO rename classes to reflex variable names - //TODO engage localValueResolver here - //var globalVariableResolver = Container.createGlobalContextProcessor(window); - var constantResolver = Container.createConstantValueResolver(expression.parameters, window, this.scopeContext); - var parameterProcessor = Container.createParameterResolverVisitor(); - - jsCodeTree = parameterProcessor.Visit(jsCodeTree, constantResolver); - - this.scopeContext.log({ event: "JSCodeExpressionResolved", data: jsCodeTree }); - var code2entity = Container.createCodeToEntityConverter(this.scopeContext); - - ///user provided query parameter object (specified as thisArg earlier) is passed in - var entityExpression = code2entity.Visit(jsCodeTree, { queryParameters: expression.parameters, lambdaParameters: this.lambdaTypes, frameType: context.frameType }); - - ///parameters are referenced, ordered and named, also collected in a flat list of name value pairs - var result = Container.createParametricQueryExpression(entityExpression, code2entity.parameters); - this.scopeContext.log({ event: "EntityExpression", data: entityExpression }); - - return result; - }, - - - VisitFilterExpression: function (expression, context) { - var source = this.Visit(expression.source, context); - context = context || {}; - context.frameType = expression.getType(); - var selector = this.Visit(expression.selector, context); - if (source !== expression.source || selector !== expression.selector) { - return Container.createFilterExpression(source, selector, expression.params, expression.instance); - } - return expression; - }, - - VisitInlineCountExpression: function (expression, context) { - var source = this.Visit(expression.source, context); - context = context || {}; - context.frameType = expression.getType(); - var selector = this.Visit(expression.selector, context); - if (source !== expression.source || selector !== expression.selector) { - return Container.createInlineCountExpression(source, selector, expression.params, expression.instance); - } - return expression; - }, - - VisitProjectionExpression: function (expression, context) { - var source = this.Visit(expression.source, context); - context = context || {}; - context.frameType = expression.getType(); - var selector = this.Visit(expression.selector, context); - if (source !== expression.source || selector !== expression.selector) { - var expr = Container.createProjectionExpression(source, selector, expression.params, expression.instance); - expr.projectionAs = expression.projectionAs; - return expr; - } - return expression; - }, - - VisitOrderExpression: function (expression, context) { - var source = this.Visit(expression.source, context); - context = context || {}; - context.frameType = expression.getType(); - var selector = this.Visit(expression.selector, context); - if (source !== expression.source || selector !== expression.selector) { - return Container.createOrderExpression(source, selector, expression.nodeType); - } - return expression; - } -});$C('$data.Expressions.QueryParameterExpression', $data.Expressions.ExpressionNode, null, { - constructor: function (name, index, value, type) { - this.name = name; - this.index = index; - this.value = value; - //TODO - this.type = Container.getTypeName(value); - }, - - nodeType: { value: $data.Expressions.ExpressionType.QueryParameter, writable: false } -});$C('$data.Expressions.RepresentationExpression', $data.Expressions.ExpressionNode, null, { - constructor: function (kind) { - }, - - getMemberDefinition: function (name) { - return this.entityType.getMemberDefinition(name); - }, - - nodeType: { value: $data.Expressions.ExpressionType.Entity } -}); - -$C('$data.Expressions.ServiceOperationExpression', $data.Expressions.ExpressionNode, null, { - constructor: function (source, selector, params, cfg, boundItem) { - /// - /// - /// - /// - /// - /// - Guard.requireType("source", source, [$data.Expressions.EntityContextExpression]); - Guard.requireType("selector", source, [$data.Expressions.MemberInfoExpression]); - - this.source = source; - this.selector = selector - this.params = params - this.cfg = cfg; - this.boundItem = boundItem; - - function findContext() { - //TODO: use source from function parameter and return a value at the end of the function - var r = source; - while (r) { - if (r instanceof $data.Expressions.EntityContextExpression) { - return r; - } - r = r.source; - } - } - - var c = findContext(); - switch (true) { - case this.source instanceof $data.Expressions.EntityContextExpression: - this.elementType = cfg.elementType ? Container.resolveType(cfg.elementType) : (this.elementType ? Container.resolveType(cfg.returnType) : null); - this.storageModel = cfg.elementType ? c.instance._storageModel.getStorageModel(Container.resolveType(cfg.elementType)) : null; - break; - default: - Guard.raise("Unknown source type for EntitySetExpression: " + this.source.getType().name); - } - - }, - nodeType: { value: $data.Expressions.ExpressionType.ServiceOperation, enumerable: true } -});$C('$data.Expressions.ContinuationExpressionBuilder', $data.Expressions.EntityExpressionVisitor, null, { - constructor: function (mode) { - this.mode = mode; - }, - compile: function (query) { - - var findContext = { mode: "find", skipExists: false }; - this.Visit(query.expression, findContext); - - var result = { - skip: findContext.skipSize, - take: findContext.pageSize, - message: '' - } - - - if ('pageSize' in findContext) { - var expression; - var context = { mode: this.mode, pageSize: findContext.pageSize }; - - if (!findContext.skipExists && (findContext.pageSize)) { - context.append = true; - expression = this.Visit(query.expression, context); - - } else if (findContext.skipExists) { - expression = this.Visit(query.expression, context) - } - - if (!context.abort) { - result.expression = expression - } - else { - result.skip = (result.skip || 0) - result.take; - result.message = 'Invalid skip value!'; - } - }else{ - result.message = 'take expression not defined in the chain!'; - } - - return result; - }, - VisitPagingExpression: function (expression, context) { - - switch (context.mode) { - case 'find': - if (expression.nodeType === $data.Expressions.ExpressionType.Take) { - context.pageSize = expression.amount.value; - } else { - context.skipSize = expression.amount.value; - context.skipExists = true; - } - break; - case 'prev': - if (expression.nodeType === $data.Expressions.ExpressionType.Skip) { - var amount = expression.amount.value - context.pageSize; - context.abort = amount < 0 && expression.amount.value >= context.pageSize; - - var constExp = Container.createConstantExpression(Math.max(amount, 0), "number"); - return Container.createPagingExpression(expression.source, constExp, expression.nodeType); - } else if (context.append) { - //no skip expression, skip: 0, no prev - context.abort = true; - } - break; - case 'next': - if (expression.nodeType === $data.Expressions.ExpressionType.Skip) { - var amount = context.pageSize + expression.amount.value; - var constExp = Container.createConstantExpression(amount, "number"); - return Container.createPagingExpression(expression.source, constExp, expression.nodeType); - } else if (context.append) { - //no skip expression, skip: 0 - var constExp = Container.createConstantExpression(context.pageSize, "number"); - return Container.createPagingExpression(expression, constExp, $data.Expressions.ExpressionType.Skip); - } - break; - default: - } - - this.Visit(expression.source, context); - } -}); -$data.Class.define('$data.Validation.ValidationError', null, null, { - constructor: function (message, propertyDefinition, type) { - /// - /// - - this.Message = message; - this.PropertyDefinition = propertyDefinition; - this.Type = type; - }, - Type: { dataType: 'string' }, - Message: { dataType: "string" }, - PropertyDefinition: { dataType: $data.MemberDefinition } -}, null); - -$data.Class.define('$data.Validation.EntityValidationBase', null, null, { - - ValidateEntity: function (entity) { - /// - return []; - }, - - ValidateEntityField: function (entity, memberDefinition) { - /// - /// - return []; - }, - - getValidationValue: function (memberDefinition, validationName) { - Guard.raise("Pure class"); - }, - getValidationMessage: function (memberDefinition, validationName, defaultMessage) { - Guard.raise("Pure class"); - } - -}, null); - -$data.Validation = $data.Validation || {}; -$data.Validation.Entity = new $data.Validation.EntityValidationBase(); -$data.Class.define('$data.Validation.Defaults', null, null, null, { - validators: { - value: { - required: function (value, definedValue) { return !Object.isNullOrUndefined(value); }, - customValidator: function (value, definedValue) { return Object.isNullOrUndefined(value) || typeof definedValue == "function" ? definedValue(value) : true; }, - - minValue: function (value, definedValue) { return Object.isNullOrUndefined(value) || value >= definedValue; }, - maxValue: function (value, definedValue) { return Object.isNullOrUndefined(value) || value <= definedValue; }, - - minLength: function (value, definedValue) { return Object.isNullOrUndefined(value) || value.length >= definedValue; }, - maxLength: function (value, definedValue) { return Object.isNullOrUndefined(value) || value.length <= definedValue; }, - length: function (value, definedValue) { return Object.isNullOrUndefined(value) || value.length == definedValue; }, - regex: function (value, definedValue) { - return Object.isNullOrUndefined(value) || - value.match(typeof definedValue === 'string' - ? new RegExp((definedValue.indexOf('/') === 0 && definedValue.lastIndexOf('/') === (definedValue.length - 1)) ? definedValue.slice(1, -1) : definedValue) - : definedValue) - } - } - }, - - _getGroupValidations: function (validations) { - var validators = {}; - if (Array.isArray(validations)) { - for (var i = 0; i < validations.length; i++) { - var validator = validations[i]; - if (typeof this.validators[validator] === 'function') { - validators[validator] = this.validators[validator]; - } - } - } - - return validators; - } -}); - -$data.Class.define('$data.Validation.EntityValidation', $data.Validation.EntityValidationBase, null, { - - ValidateEntity: function (entity) { - /// - - var errors = []; - entity.getType().memberDefinitions.getPublicMappedProperties().forEach(function (memDef) { - errors = errors.concat(this.ValidateEntityField(entity, memDef, undefined, true)); - }, this); - return errors; - }, - ValidateEntityField: function (entity, memberDefinition, newValue, valueNotSet) { - /// - /// - var errors = []; - var resolvedType = Container.resolveType(memberDefinition.dataType); - var typeName = Container.resolveName(resolvedType); - var value = !valueNotSet ? newValue : entity[memberDefinition.name]; - - if (!memberDefinition.inverseProperty && resolvedType && typeof resolvedType.isAssignableTo === 'function' && resolvedType.isAssignableTo($data.Entity)) { - typeName = $data.Entity.fullName; - } - - this.fieldValidate(entity, memberDefinition, value, errors, typeName); - return errors; - }, - - getValidationValue: function (memberDefinition, validationName) { - var value; - if (memberDefinition[validationName] && memberDefinition[validationName].value) - value = memberDefinition[validationName].value; - else - value = memberDefinition[validationName]; - - if (this.convertableValidation[validationName]) { - var typeToConvert; - if (this.convertableValidation[validationName] === true) { - typeToConvert = memberDefinition.type; - } else { - typeToConvert = this.convertableValidation[validationName]; - } - - if (typeToConvert) - value = Container.convertTo(value, typeToConvert, memberDefinition.elementType); - } - - return value; - }, - getValidationMessage: function (memberDefinition, validationName, defaultMessage) { - var eMessage = defaultMessage; - if (typeof memberDefinition[validationName] == "object" && memberDefinition[validationName].message) - eMessage = memberDefinition[validationName].message; - else if (memberDefinition.errorMessage) - eMessage = memberDefinition.errorMessage; - - return eMessage; - }, - createValidationError: function (memberDefinition, validationName, defaultMessage) { - return new $data.Validation.ValidationError(this.getValidationMessage(memberDefinition, validationName, defaultMessage), memberDefinition, validationName); - }, - - convertableValidation: { - value: { - required: '$data.Boolean', - minValue: true, - maxValue: true, - minLength: '$data.Integer', - maxLength: '$data.Integer', - length: '$data.Integer' - } - - }, - supportedValidations: { - value: { - //'$data.Entity': $data.Validation.Defaults._getGroupValidations(['required', 'customValidator']), - '$data.ObjectID': $data.Validation.Defaults._getGroupValidations(['required', 'customValidator']), - '$data.Byte': $data.Validation.Defaults._getGroupValidations(['required', 'customValidator', 'minValue', 'maxValue']), - '$data.SByte': $data.Validation.Defaults._getGroupValidations(['required', 'customValidator', 'minValue', 'maxValue']), - '$data.Decimal': $data.Validation.Defaults._getGroupValidations(['required', 'customValidator', 'minValue', 'maxValue']), - '$data.Float': $data.Validation.Defaults._getGroupValidations(['required', 'customValidator', 'minValue', 'maxValue']), - '$data.Number': $data.Validation.Defaults._getGroupValidations(['required', 'customValidator', 'minValue', 'maxValue']), - '$data.Int16': $data.Validation.Defaults._getGroupValidations(['required', 'customValidator', 'minValue', 'maxValue']), - '$data.Integer': $data.Validation.Defaults._getGroupValidations(['required', 'customValidator', 'minValue', 'maxValue']), - '$data.Int32': $data.Validation.Defaults._getGroupValidations(['required', 'customValidator', 'minValue', 'maxValue']), - '$data.Int64': $data.Validation.Defaults._getGroupValidations(['required', 'customValidator', 'minValue', 'maxValue']), - '$data.String': $data.Validation.Defaults._getGroupValidations(['required', 'customValidator', 'minLength', 'maxLength', 'length', 'regex']), - '$data.Date': $data.Validation.Defaults._getGroupValidations(['required', 'customValidator', 'minValue', 'maxValue']), - '$data.DateTimeOffset': $data.Validation.Defaults._getGroupValidations(['required', 'customValidator', 'minValue', 'maxValue']), - '$data.Time': $data.Validation.Defaults._getGroupValidations(['required', 'customValidator', 'minValue', 'maxValue']), - '$data.Boolean': $data.Validation.Defaults._getGroupValidations(['required', 'customValidator']), - '$data.Array': $data.Validation.Defaults._getGroupValidations(['required', 'customValidator', 'length']), - '$data.Object': $data.Validation.Defaults._getGroupValidations(['required', 'customValidator']), - '$data.Guid': $data.Validation.Defaults._getGroupValidations(['required', 'customValidator']), - '$data.Blob': $data.Validation.Defaults._getGroupValidations(['required', 'customValidator', 'minLength', 'maxLength', 'length']), - '$data.GeographyPoint': $data.Validation.Defaults._getGroupValidations(['required', 'customValidator']), - '$data.GeographyLineString': $data.Validation.Defaults._getGroupValidations(['required', 'customValidator']), - '$data.GeographyPolygon': $data.Validation.Defaults._getGroupValidations(['required', 'customValidator']), - '$data.GeographyMultiPoint': $data.Validation.Defaults._getGroupValidations(['required', 'customValidator']), - '$data.GeographyMultiLineString': $data.Validation.Defaults._getGroupValidations(['required', 'customValidator']), - '$data.GeographyMultiPolygon': $data.Validation.Defaults._getGroupValidations(['required', 'customValidator']), - '$data.GeographyCollection': $data.Validation.Defaults._getGroupValidations(['required', 'customValidator']), - '$data.GeometryPoint': $data.Validation.Defaults._getGroupValidations(['required', 'customValidator']), - '$data.GeometryLineString': $data.Validation.Defaults._getGroupValidations(['required', 'customValidator']), - '$data.GeometryPolygon': $data.Validation.Defaults._getGroupValidations(['required', 'customValidator']), - '$data.GeometryMultiPoint': $data.Validation.Defaults._getGroupValidations(['required', 'customValidator']), - '$data.GeometryMultiLineString': $data.Validation.Defaults._getGroupValidations(['required', 'customValidator']), - '$data.GeometryMultiPolygon': $data.Validation.Defaults._getGroupValidations(['required', 'customValidator']), - '$data.GeometryCollection': $data.Validation.Defaults._getGroupValidations(['required', 'customValidator']) - } - }, - - fieldValidate: function (entity, memberDefinition, value, errors, validationTypeName) { - /// - /// - /// - /// - if (entity.entityState == $data.EntityState.Modified && entity.changedProperties && entity.changedProperties.indexOf(memberDefinition) < 0) - return; - - var validatonGroup = this.supportedValidations[validationTypeName]; - if (validatonGroup) { - var validations = Object.keys(validatonGroup); - validations.forEach(function (validation) { - if (memberDefinition[validation] && validatonGroup[validation] && !validatonGroup[validation].call(entity, value, this.getValidationValue(memberDefinition, validation))) - errors.push(this.createValidationError(memberDefinition, validation, 'Validation error!')); - }, this); - - if (validationTypeName === $data.Entity.fullName && value instanceof $data.Entity && !value.isValid()) { - errors.push(this.createValidationError(memberDefinition, 'ComplexProperty', 'Validation error!')); - } - } - } - -}, null); - -$data.Validation.Entity = new $data.Validation.EntityValidation(); - -$data.Class.define('$data.Notifications.ChangeDistributorBase', null, null, { - distributeData: function (collectorData) { - Guard.raise("Pure class"); - } -}, null); -$data.Class.define('$data.Notifications.ChangeCollectorBase', null, null, { - buildData: function (entityContextData) { - Guard.raise("Pure class"); - }, - processChangedData: function (entityData) { - if (this.Distrbutor && this.Distrbutor.distributeData) - this.Distrbutor.distributeData(this.buildData(entityData)); - }, - Distrbutor: { enumerable: false, dataType: $data.Notifications.ChangeDistributorBase, storeOnObject: true } -}, null); -$data.Class.define('$data.Notifications.ChangeDistributor', $data.Notifications.ChangeDistributorBase, null, { - constructor: function (broadcastUrl) { - this.broadcastUrl = broadcastUrl; - }, - distributeData: function (data) { - $data.ajax({ - url: this.broadcastUrl, - type: "POST", - data: 'data=' + JSON.stringify(data), - succes: this.success, - error: this.error - }); - }, - broadcastUrl: { dataType: "string" }, - success: function () { }, - error: function () { } -}, null); -$data.Class.define('$data.Notifications.ChangeCollector', $data.Notifications.ChangeCollectorBase, null, { - buildData: function (entities) { - var result = []; - entities.forEach(function (element) { - var resObj = { entityState: element.data.entityState, typeName: element.data.getType().name }; - var enumerableMemDefCollection = []; - - switch (element.data.entityState) { - case $data.EntityState.Added: - enumerableMemDefCollection = element.data.getType().memberDefinitions.getPublicMappedProperties(); - break; - case $data.EntityState.Modified: - enumerableMemDefCollection = element.data.changedProperties; - break; - case $data.EntityState.Deleted: - enumerableMemDefCollection = element.data.getType().memberDefinitions.getKeyProperties(); - break; - default: - break; - } - - enumerableMemDefCollection.forEach(function (memDef) { - resObj[memDef.name] = element.data[memDef.name]; - }); - - result.push(resObj); - }); - - return result; - } -}, null);$data.Class.define('$data.Transaction', null, null, { - constructor: function () { - this._objectId = (new Date()).getTime(); - $data.Trace.log("create: ", this._objectId); - - this.oncomplete = new $data.Event("oncomplete", this); - this.onerror = new $data.Event("onerror", this); - }, - abort: function () { - Guard.raise(new Exception('Not Implemented', 'Not Implemented', arguments)); - }, - - _objectId: { type: $data.Integer }, - transaction: { type: $data.Object }, - - oncomplete: { type: $data.Event }, - onerror: { type: $data.Event } -}, null);$data.Class.define('$data.Access', null, null, {}, { - isAuthorized: function(access, user, sets, callback){ - var pHandler = new $data.PromiseHandler(); - var clbWrapper = pHandler.createCallback(callback); - var pHandlerResult = pHandler.getPromise(); - - //clbWrapper.error('Authorization failed', 'Access authorization'); - clbWrapper.success(true); - - return pHandlerResult; - - /*var error; - - if (!access) error = 'Access undefined'; - if (typeof access !== 'number') error = 'Invalid access type'; - if (!user) user = {}; //error = 'User undefined'; - if (!user.roles) user.roles = {}; //error = 'User has no roles'; - if (!roles) roles = {}; //error = 'Roles undefined'; - if (!(roles instanceof Array || typeof roles === 'object')) error = 'Invald roles type'; - - var pHandler = new $data.PromiseHandler(); - var clbWrapper = pHandler.createCallback(callback); - var pHandlerResult = pHandler.getPromise(); - - if (error){ - clbWrapper.error(error, 'Access authorization'); - return pHandlerResult; - } - - if (user.roles instanceof Array){ - var r = {}; - for (var i = 0; i < user.roles.length; i++){ - if (typeof user.roles[i] === 'string') r[user.roles[i]] = true; - } - user.roles = r; - } - - if (roles instanceof Array){ - var r = {}; - for (var i = 0; i < roles.length; i++){ - if (typeof roles[i] === 'string') r[roles[i]] = true; - } - roles = r; - } - - var args = arguments; - var readyFn = function(result){ - if (result) clbWrapper.success(result); - else clbWrapper.error('Authorization failed', args); - }; - - var rolesKeys = Object.getOwnPropertyNames(roles); - var i = 0; - - var callbackFn = function(result){ - if (result) readyFn(result); - - if (typeof roles[rolesKeys[i]] === 'boolean' && roles[rolesKeys[i]]){ - if (user.roles[rolesKeys[i]]) readyFn(true); - else{ - i++; - if (i < rolesKeys.length) callbackFn(); - else readyFn(false); - } - }else if (typeof roles[rolesKeys[i]] === 'function'){ - var r = roles[rolesKeys[i]].call(user); - - if (typeof r === 'function') r.call(user, (i < rolesKeys.length ? callbackFn : readyFn)); - else{ - if (r) readyFn(true); - else{ - i++; - if (i < rolesKeys.length) callbackFn(); - else readyFn(false); - } - } - }else if (typeof roles[rolesKeys[i]] === 'number'){ - if (((typeof user.roles[rolesKeys[i]] === 'number' && (user.roles[rolesKeys[i]] & access)) || - (typeof user.roles[rolesKeys[i]] !== 'number' && user.roles[rolesKeys[i]])) && - (roles[rolesKeys[i]] & access)) user.roles[rolesKeys[i]] && readyFn(true); - else{ - i++; - if (i < rolesKeys.length) callbackFn(); - else readyFn(false); - } - } - }; - - callbackFn(); - - return pHandlerResult;*/ - }, - getAccessBitmaskFromPermission: function(p){ - var access = $data.Access.None; - - if (p.Create) access |= $data.Access.Create; - if (p.Read) access |= $data.Access.Read; - if (p.Update) access |= $data.Access.Update; - if (p.Delete) access |= $data.Access.Delete; - if (p.DeleteBatch) access |= $data.Access.DeleteBatch; - if (p.Execute) access |= $data.Access.Execute; - - return access; - }, - None: { value: 0 }, - Create: { value: 1 }, - Read: { value: 2 }, - Update: { value: 4 }, - Delete: { value: 8 }, - DeleteBatch: { value: 16 }, - Execute: { value: 32 } -}); -$data.Class.define('$data.Promise', null, null, { - always: function () { Guard.raise(new Exception('$data.Promise.always', 'Not implemented!')); }, - done: function () { Guard.raise(new Exception('$data.Promise.done', 'Not implemented!')); }, - fail: function () { Guard.raise(new Exception('$data.Promise.fail', 'Not implemented!')); }, - isRejected: function () { Guard.raise(new Exception('$data.Promise.isRejected', 'Not implemented!')); }, - isResolved: function () { Guard.raise(new Exception('$data.Promise.isResolved', 'Not implemented!')); }, - //notify: function () { Guard.raise(new Exception('$data.Promise.notify', 'Not implemented!')); }, - //notifyWith: function () { Guard.raise(new Exception('$data.Promise.notifyWith', 'Not implemented!')); }, - pipe: function () { Guard.raise(new Exception('$data.Promise.pipe', 'Not implemented!')); }, - progress: function () { Guard.raise(new Exception('$data.Promise.progress', 'Not implemented!')); }, - promise: function () { Guard.raise(new Exception('$data.Promise.promise', 'Not implemented!')); }, - //reject: function () { Guard.raise(new Exception('$data.Promise.reject', 'Not implemented!')); }, - //rejectWith: function () { Guard.raise(new Exception('$data.Promise.rejectWith', 'Not implemented!')); }, - //resolve: function () { Guard.raise(new Exception('$data.Promise.resolve', 'Not implemented!')); }, - //resolveWith: function () { Guard.raise(new Exception('$data.Promise.resolveWith', 'Not implemented!')); }, - state: function () { Guard.raise(new Exception('$data.Promise.state', 'Not implemented!')); }, - then: function () { Guard.raise(new Exception('$data.Promise.then', 'Not implemented!')); } -}, null); - -$data.Class.define('$data.PromiseHandlerBase', null, null, { - constructor: function () { }, - createCallback: function (callBack) { - callBack = $data.typeSystem.createCallbackSetting(callBack); - - return cbWrapper = { - success: callBack.success, - error: callBack.error, - notify: callBack.notify - }; - }, - getPromise: function () { - return new $data.Promise(); - } -}, null); - -$data.PromiseHandler = $data.PromiseHandlerBase; -var EventSubscriber = $data.Class.define("EventSubscriber", null, null, { - constructor: function (handler, state, thisArg) { - /// - /// event handler - /// - /// - /// - /// - /// - /// - /// custom state object - /// [i]this[/i] context for handler - /// - /// event handler - /// custom state object - /// [i]this[/i] context for handler - this.handler = handler; - this.state = state; - this.thisArg = thisArg; - }, - handler: {}, - state: {}, - thisArg: {} -}); - -$data.Event = Event = $data.Class.define("$data.Event", null, null, { - constructor: function (name, sender) { - ///The name of the event - ///The originator/sender of the event. [this] in handlers will be set to this - var subscriberList = null; - var parentObject = sender; - - function detachHandler(list, handler) { - /// - /// - list.forEach(function (item, index) { - if (item.handler == handler) { - list.splice(index, 1); - } - }); - } - - this.attach = function (handler, state, thisArg) { - /// - /// - /// - /// - /// - /// - /// - /// - /// - if (!subscriberList) { - subscriberList = []; - } - subscriberList.push(new EventSubscriber(handler, state, thisArg || sender)); - }; - this.detach = function (handler) { - detachHandler(subscriberList, handler); - }; - this.fire = function (eventData, snder) { - var snd = snder || sender || this; - //eventData.eventName = name; - /// - if (subscriberList) { - subscriberList.forEach(function (subscriber) { - /// - try { - subscriber.handler.call(subscriber.thisArg, snd, eventData, subscriber.state); - } catch(ex) { - console.log("unhandled exception in event handler. exception suppressed"); - console.dir(ex); - } - }); - } - }; - this.fireCancelAble = function (eventData, snder) { - var snd = snder || sender || this; - //eventData.eventName = name; - /// - var isValid = true; - if (subscriberList) { - subscriberList.forEach(function (subscriber) { - /// - try { - isValid = isValid && (subscriber.handler.call(subscriber.thisArg, snd, eventData, subscriber.state) === false ? false : true); - } catch (ex) { - console.log("unhandled exception in event handler. exception suppressed"); - console.dir(ex); - } - }); - } - return isValid; - }; - } -}); - - -var eventData = $data.Class.define("EventData", null, null, { - eventName: {} -}); - -var PropertyChangeEventData = $data.Class.define("PropertyChangeEventData", EventData, null, { - constructor: function (propertyName, oldValue, newValue) { - this.propertyName = propertyName; - this.oldValue = oldValue; - this.newValue = newValue; - }, - propertyName: {}, - oldValue: {}, - newValue: {} -}); - -var PropertyValidationEventData = $data.Class.define("PropertyValidationEventData", EventData, null, { - constructor: function (propertyName, oldValue, newValue, errors) { - this.propertyName = propertyName; - this.oldValue = oldValue; - this.newValue = newValue; - this.errors = errors; - this.cancel = false; - }, - propertyName: {}, - oldValue: {}, - newValue: {}, - errors: {}, - cancel: {} -}); - - - -$data.Entity = Entity = $data.Class.define("$data.Entity", null, null, { - constructor: function (initData, newInstanceOptions) { - /// - /// This class provide a light weight, object-relational interface between - /// your javascript code and database. - /// - /// - /// - /// initialization data - /// - /// var category = new $news.Types.Category({ Title: 'Tech' }); - /// $news.context.Categories.add(category); - /// - /// - /// - /// initialization data - /// - /// - /// - /// - /// Determines the current $data.Entity is validated. - /// array of $data.Validation.ValidationError - /// array of MemberDefinition - /// - /// array of MemberDefinition - - this.initData = {}; - var thisType = this.getType(); - if (thisType.__copyPropertiesToInstance) { - $data.typeSystem.writePropertyValues(this); - } - - var ctx = null; - this.context = ctx; - if ("setDefaultValues" in thisType) { - if (!newInstanceOptions || newInstanceOptions.setDefaultValues !== false) { - if (!initData || Object.keys(initData).length < 1) { - initData = thisType.setDefaultValues(initData); - } - } - } - - if (typeof initData === "object") { - var typeMemDefs = thisType.memberDefinitions; - var memDefNames = typeMemDefs.getPublicMappedPropertyNames(); - - for (var i in initData) { - if (memDefNames.indexOf(i) > -1) { - var memberDef = typeMemDefs.getMember(i); - var type = Container.resolveType(memberDef.type); - var value = initData[i]; - - if (memberDef.concurrencyMode === $data.ConcurrencyMode.Fixed) { - this.initData[i] = value; - } else { - if (newInstanceOptions && newInstanceOptions.converters) { - var converter = newInstanceOptions.converters[Container.resolveName(type)]; - if (converter) - value = converter(value); - } - - this.initData[i] = Container.convertTo(value, type, memberDef.elementType, newInstanceOptions); - } - } - } - - } - - if (newInstanceOptions && newInstanceOptions.entityBuilder) { - newInstanceOptions.entityBuilder(this, thisType.memberDefinitions.asArray(), thisType); - } - - this.changedProperties = undefined; - this.entityState = undefined; - - }, - toString: function () { - /// Returns a string that represents the current $data.Entity - /// - - return this.getType().fullName + "(" + (this.Id || this.Name || '') + ")" - }, - toJSON: function () { - /// Creates pure JSON object from $data.Entity. - /// JSON representation - - var result = {}; - var self = this; - this.getType().memberDefinitions.getPublicMappedProperties().forEach(function (memDef) { - if (self[memDef.name] instanceof Date && memDef.type && Container.resolveType(memDef.type) === $data.DateTimeOffset) { - result[memDef.name] = new $data.DateTimeOffset(self[memDef.name]); - } else { - result[memDef.name] = self[memDef.name]; - } - }); - return result; - }, - equals: function (entity) { - /// Determines whether the specified $data.Entity is equal to the current $data.Entity. - /// [b]true[/b] if the specified $data.Entity is equal to the current $data.Entity; otherwise, [b]false[/b]. - - if (entity.getType() !== this.getType()) { - return false; - } - var entityPk = this.getType().memberDefinitions.getKeyProperties(); - for (var i = 0; i < entityPk.length; i++) { - if (this[entityPk[i].name] != entity[entityPk[i].name]) { - return false; - } - } - return true; - }, - - propertyChanging: { - dataType: $data.Event, storeOnObject: true, monitorChanges: false, notMapped: true, enumerable: false, prototypeProperty: true, - get: function () { - if (!this._propertyChanging) - this._propertyChanging = new Event('propertyChanging', this); - - return this._propertyChanging; - }, - set: function (value) { this._propertyChanging = value; } - }, - - propertyChanged: { - dataType: $data.Event, storeOnObject: true, monitorChanges: false, notMapped: true, enumerable: false, prototypeProperty: true, - get: function () { - if (!this._propertyChanged) - this._propertyChanged = new Event('propertyChanged', this); - - return this._propertyChanged; - }, - set: function (value) { this._propertyChanged = value; } - }, - - propertyValidationError: { - dataType: $data.Event, storeOnObject: true, monitorChanges: false, notMapped: true, enumerable: false, prototypeProperty: true, - get: function () { - if (!this._propertyValidationError) - this._propertyValidationError = new Event('propertyValidationError', this); - - return this._propertyValidationError; - }, - set: function (value) { this._propertyValidationError = value; } - }, - - // protected - storeProperty: function (memberDefinition, value) { - /// - /// - - if (memberDefinition.concurrencyMode !== $data.ConcurrencyMode.Fixed) { - value = Container.convertTo(value, memberDefinition.type, memberDefinition.elementType); - } - - var eventData = null; - if (memberDefinition.monitorChanges != false && (this._propertyChanging || this._propertyChanged || "instancePropertyChanged" in this.constructor)) { - var origValue = this[memberDefinition.name]; - eventData = new PropertyChangeEventData(memberDefinition.name, origValue, value); - if (this._propertyChanging) - this.propertyChanging.fire(eventData); - } - - if (memberDefinition.monitorChanges != false && (this._propertyValidationError || "instancePropertyValidationError" in this.constructor)) { - var errors = $data.Validation.Entity.ValidateEntityField(this, memberDefinition, value); - if (errors.length > 0) { - var origValue = this[memberDefinition.name]; - var errorEventData = new PropertyValidationEventData(memberDefinition.name, origValue, value, errors); - - if (this._propertyValidationError) - this.propertyValidationError.fire(errorEventData); - if ("instancePropertyValidationError" in this.constructor) - this.constructor["instancePropertyValidationError"].fire(errorEventData, this); - - if (errorEventData.cancel == true) - return; - } - } - - if (memberDefinition.storeOnObject == true) { - //TODO refactor to Base.getBackingFieldName - var backingFieldName = "_" + memberDefinition.name; - this[backingFieldName] = value; - } else { - this.initData[memberDefinition.name] = value; - } - this.isValidated = false; - - if (memberDefinition.monitorChanges != false && this.entityState == $data.EntityState.Unchanged) - this.entityState = $data.EntityState.Modified; - - this._setPropertyChanged(memberDefinition); - - if (memberDefinition.monitorChanges != false) { - //if (!this.changedProperties) { - // this.changedProperties = []; - //} - - //if (!this.changedProperties.some(function (memDef) { return memDef.name == memberDefinition.name })) - // this.changedProperties.push(memberDefinition); - - if (this._propertyChanged) - this.propertyChanged.fire(eventData); - - //TODO mixin framework - if ("instancePropertyChanged" in this.constructor) { - this.constructor["instancePropertyChanged"].fire(eventData, this); - } - } - }, - _setPropertyChanged: function (memberDefinition) { - if (memberDefinition.monitorChanges != false) { - if (!this.changedProperties) { - this.changedProperties = []; - } - - if (!this.changedProperties.some(function (memDef) { return memDef.name == memberDefinition.name })) - this.changedProperties.push(memberDefinition); - } - }, - - // protected - retrieveProperty: function (memberDefinition) { - /// - - if (memberDefinition.storeOnObject == true) { - //TODO refactor to Base.getBackingFieldName - var backingFieldName = "_" + memberDefinition.name; - return this[backingFieldName]; - } else { - return this.initData[memberDefinition.name]; - } - }, - - // protected - getProperty: function (memberDefinition, callback, tran) { - /// Retrieve value of member - /// - /// - /// - /// - /// - /// - /// value associated for [i]memberDefinition[/i] - - callback = $data.typeSystem.createCallbackSetting(callback); - if (this[memberDefinition.name] != undefined) { - if (tran instanceof $data.Transaction) - callback.success(this[memberDefinition.name], tran); - else - callback.success(this[memberDefinition.name]); - return; - } - - var context = this.context; - if (!this.context) { - try { - var that = this; - var storeToken = this.storeToken || this.getType().storeToken; - if (storeToken && typeof storeToken.factory === 'function') { - var ctx = storeToken.factory(); - return ctx.onReady().then(function (context) { - return context.loadItemProperty(that, memberDefinition, callback); - }); - } - } catch (e) { } - - Guard.raise(new Exception('Entity not in context', 'Invalid operation')); - } else { - return context.loadItemProperty(this, memberDefinition, callback, tran); - } - }, - // protected - setProperty: function (memberDefinition, value, callback, tran) { - /// - /// - /// done - this[memberDefinition.name] = value; - - //callback = $data.typeSystem.createCallbackSetting(callback); - var pHandler = new $data.PromiseHandler(); - callback = pHandler.createCallback(callback); - callback.success(this[memberDefinition.name]); - return pHandler.getPromise(); - }, - - isValid: function () { - /// Determines the current $data.Entity is validated and valid. - /// - - if (!this.isValidated) { - this.ValidationErrors = $data.Validation.Entity.ValidateEntity(this); - this.isValidated = true; - } - return this.ValidationErrors.length == 0; - }, - isValidated: { dataType: "bool", storeOnObject: true, monitorChanges: false, notMapped: true, enumerable: false, value: false }, - ValidationErrors: { - dataType: Array, - elementType: $data.Validation.ValidationError, - storeOnObject: true, - monitorChanges: true, - notMapped: true, - enumerable: false - }, - - resetChanges: function () { - /// reset changes - - delete this._changedProperties; - }, - - changedProperties: { - dataType: Array, - elementType: window["MemberDefinition"], - storeOnObject: true, - monitorChanges: false, - notMapped: true, - enumerable: false - }, - - entityState: { dataType: "integer", storeOnObject: true, monitorChanges: false, notMapped: true, enumerable: false }, - /* - toJSON: function () { - if (this.context) { - var itemType = this.getType(); - var storageModel = this.context._storageModel[itemType.name]; - var o = new Object(); - for (var property in this) { - if (typeof this[property] !== "function") { - var excludedFields = storageModel.Associations.every(function (association) { - return association.FromPropertyName == property && (association.FromMultiplicity == "0..1" || association.FromMultiplicity == "1"); - }, this); - if (!excludedFields) { - o[property] = this[property]; - } - } - } - return o; - } - return this; - } */ - //, - - //onReady: function (callback) { - // this.__onReadyList = this.__onReadyList || []; - // this.__onReadyList.push(callback); - //}, - - remove: function () { - if ($data.ItemStore && 'EntityInstanceRemove' in $data.ItemStore) - return $data.ItemStore.EntityInstanceRemove.apply(this, arguments); - else - throw 'not implemented'; //todo - }, - save: function () { - if ($data.ItemStore && 'EntityInstanceSave' in $data.ItemStore) - return $data.ItemStore.EntityInstanceSave.apply(this, arguments); - else - throw 'not implemented'; //todo - }, - refresh: function () { - if ($data.ItemStore && 'EntityInstanceSave' in $data.ItemStore) - return $data.ItemStore.EntityInstanceRefresh.apply(this, arguments); - else - throw 'not implemented'; //todo - }, - storeToken: { type: Object, monitorChanges: false, notMapped: true, storeOnObject: true }, - - getFieldUrl: function (field) { - if (this.context) { - return this.context.getFieldUrl(this, field); - } else if (this.getType().storeToken && typeof this.getType().storeToken.factory === 'function') { - var context = this.getType().storeToken.factory(); - return context.getFieldUrl(this, field); - } else if (this.getType().storeToken){ - try { - var ctx = $data.ItemStore._getContextPromise('default', this.getType()); - if (ctx instanceof $data.EntityContext) { - return ctx.getFieldUrl(this, field); - } - } catch (e) { - } - } - return '#'; - } -}, -{ - //create get_[property] and set_[property] functions for properties - __setPropertyfunctions: { value: true, notMapped: true, enumerable: false, storeOnObject: true }, - //copy public properties to current instance - __copyPropertiesToInstance: { value: false, notMapped: true, enumerable: false, storeOnObject: true }, - - inheritedTypeProcessor: function (type) { - if ($data.ItemStore && 'EntityInheritedTypeProcessor' in $data.ItemStore) - $data.ItemStore.EntityInheritedTypeProcessor.apply(this, arguments); - - //default value setter method factory - type.defaultValues = {}; - - type.memberDefinitions.asArray().forEach(function (pd) { - if (pd.hasOwnProperty("defaultValue")) { - type.defaultValues[pd.name] = pd.defaultValue; - } - }); - - if (Object.keys(type.defaultValues).length > 0) { - type.setDefaultValues = function (initData, instance) { - initData = initData || {}; - var dv = type.defaultValues; - for (var n in dv) { - if (!(n in initData)) { - var value = dv[n]; - if ("function" === typeof value) { - initData[n] = dv[n](n, instance); - } else { - initData[n] = dv[n]; - } - } - } - return initData; - } - } - }, - - - //Type Events - addEventListener: function(eventName, fn) { - var delegateName = "on" + eventName; - if (!(delegateName in this)) { - this[delegateName] = new $data.Event(eventName, this); - } - this[delegateName].attach(fn); - }, - removeEventListener: function(eventName, fn) { - var delegateName = "on" + eventName; - if (!(delegateName in this)) { - return; - } - this[delegateName].detach(fn); - }, - raiseEvent: function(eventName, data) { - var delegateName = "on" + eventName; - if (!(delegateName in this)) { - return; - } - this[delegateName].fire(data); - }, - - getFieldNames: function () { - return this.memberDefinitions.getPublicMappedPropertyNames(); - }, - - 'from$data.Object': function (value, type, t, options) { - if (!Object.isNullOrUndefined(value)) { - var newInstanceOptions; - if (options && options.converters) { - newInstanceOptions = { - converters: options.converters - } - } - - return new this(value, newInstanceOptions); - } else { - return value; - } - } - -}); - - -$data.define = function (name, container, definition) { - if (container && !(container instanceof $data.ContainerClass)) { - definition = container; - container = undefined; - } - if (!definition) { - throw new Error("json object type is not supported yet"); - } - var _def = {}; - var hasKey = false; - var keyFields = []; - Object.keys(definition).forEach(function (fieldName) { - var propDef = definition[fieldName]; - if (typeof propDef === 'object' && ("type" in propDef || "get" in propDef || "set" in propDef)) { - - _def[fieldName] = propDef; - if (propDef.key) { - keyFields.push(propDef); - } - - if (("get" in propDef || "set" in propDef) && (!('notMapped' in propDef) || propDef.notMapped === true)) { - propDef.notMapped = true; - propDef.storeOnObject = true; - } - if ("get" in propDef && !("set" in propDef)) { - propDef.set = function () { }; - } else if ("set" in propDef && !("get" in propDef)) { - propDef.get = function () { }; - } - - } else { - _def[fieldName] = { type: propDef }; - } - }); - - if (keyFields.length < 1) { - var keyProp; - switch (true) { - case "id" in _def: - keyProp = "id"; - break; - case "Id" in _def: - keyProp = "Id" - break; - case "ID" in _def: - keyProp = "ID" - break; - } - if (keyProp) { - _def[keyProp].key = true; - var propTypeName = $data.Container.resolveName(_def[keyProp].type); - _def[keyProp].computed = true; - //if ("$data.Number" === propTypeName || "$data.Integer" === propTypeName) { - //} - } else { - _def.Id = { type: "int", key: true, computed: true } - } - } - - - var entityType = $data.Entity.extend(name, container, _def); - return entityType; -} -$data.implementation = function (name) { - return Container.resolveType(name); -}; - - - - -(function () { - - $data.defaults = $data.defaults || {}; - $data.defaults.defaultDatabaseName = 'JayDataDefault'; - -})(); - - -$data.Class.define('$data.StorageModel', null, null, { - constructor: function () { - ///User defined type - this.ComplexTypes = []; - this.Associations = []; - }, - LogicalType: {}, - LogicalTypeName: {}, - PhysicalType: {}, - PhysicalTypeName: {}, - EventHandlers: {}, - TableName: {}, - TableOptions: { value: undefined }, - ComplexTypes: {}, - Associations: {}, - ContextType: {}, - Roles: {} -}, null); -$data.Class.define('$data.Association', null, null, { - constructor: function (initParam) { - if (initParam) { - this.From = initParam.From; - this.FromType = initParam.FromType; - this.FromMultiplicity = initParam.FromMultiplicity; - this.FromPropertyName = initParam.FromPropertyName; - this.To = initParam.To; - this.ToType = initParam.ToType; - this.ToMultiplicity = initParam.ToMultiplicity; - this.ToPropertyName = initParam.ToPropertyName; - } - }, - From: {}, - FromType: {}, - FromMultiplicity: {}, - FromPropertyName: {}, - To: {}, - ToType: {}, - ToMultiplicity: {}, - ToPropertyName: {}, - ReferentialConstraint: {} -}, null); -$data.Class.define('$data.ComplexType', $data.Association, null, {}, null); - -$data.Class.define('$data.EntityContext', null, null, -{ - constructor: function (storageProviderCfg) { - /// Provides facilities for querying and working with entity data as objects. - ///Storage provider specific configuration object. - - if ($data.ItemStore && 'ContextRegister' in $data.ItemStore) - $data.ItemStore.ContextRegister.apply(this, arguments); - - if (storageProviderCfg.queryCache) - this.queryCache = storageProviderCfg.queryCache; - - if ("string" === typeof storageProviderCfg) { - if (0 === storageProviderCfg.indexOf("http")) { - storageProviderCfg = { - name: "oData", - oDataServiceHost: storageProviderCfg - } - } else { - storageProviderCfg = { - name: "local", - databaseName: storageProviderCfg - } - } - } - - if ("provider" in storageProviderCfg) { - storageProviderCfg.name = storageProviderCfg.provider; - } - - //Initialize properties - this.lazyLoad = false; - this.trackChanges = false; - this._entitySetReferences = {}; - this._storageModel = []; - - var ctx = this; - ctx._isOK = false; - - var origSuccessInitProvider = this._successInitProvider; - this._successInitProvider = function (errorOrContext) { - if (errorOrContext instanceof $data.EntityContext) { - origSuccessInitProvider(ctx); - } else { - origSuccessInitProvider(ctx, errorOrContext); - } - } - - this._storageModel.getStorageModel = function (typeName) { - var name = Container.resolveName(typeName); - return ctx._storageModel[name]; - }; - if (typeof storageProviderCfg.name === 'string') { - var tmp = storageProviderCfg.name; - storageProviderCfg.name = [tmp]; - } - var i = 0, providerType; - var providerList = [].concat(storageProviderCfg.name); - var callBack = $data.typeSystem.createCallbackSetting({ success: this._successInitProvider, error: this._successInitProvider }); - - this._initStorageModelSync(); - ctx._initializeEntitySets(ctx.getType()); - - $data.StorageProviderLoader.load(providerList, { - success: function (providerType) { - ctx.storageProvider = new providerType(storageProviderCfg, ctx); - ctx.storageProvider.setContext(ctx); - ctx.stateManager = new $data.EntityStateManager(ctx); - - var contextType = ctx.getType(); - if (providerType.name in contextType._storageModelCache) { - ctx._storageModel = contextType._storageModelCache[providerType.name]; - } else { - ctx._initializeStorageModel(); - contextType._storageModelCache[providerType.name] = ctx._storageModel; - } - - //ctx._initializeEntitySets(contextType); - if (storageProviderCfg && storageProviderCfg.user) Object.defineProperty(ctx, 'user', { value: storageProviderCfg.user, enumerable: true }); - if (storageProviderCfg && storageProviderCfg.checkPermission) Object.defineProperty(ctx, 'checkPermission', { value: storageProviderCfg.checkPermission, enumerable: true }); - - //ctx._isOK = false; - ctx._initializeStore(callBack); - }, - error: function () { - callBack.error('Provider fallback failed!'); - } - }); - - - - this.addEventListener = function (eventName, fn) { - var delegateName = "on" + eventName; - if (!(delegateName in this)) { - this[delegateName] = new $data.Event(eventName, this); - } - this[delegateName].attach(fn); - }; - - this.removeEventListener = function (eventName, fn) { - var delegateName = "on" + eventName; - if (!(delegateName in this)) { - return; - } - this[delegateName].detach(fn); - }; - - this.raiseEvent = function (eventName, data) { - var delegateName = "on" + eventName; - if (!(delegateName in this)) { - return; - } - this[delegateName].fire(data); - }; - - - this.ready = this.onReady({ - success: $data.defaultSuccessCallback, - error: function () { - if ($data.PromiseHandler !== $data.PromiseHandlerBase) { - $data.defaultErrorCallback.apply(this, arguments); - } else { - $data.Trace.error(arguments); - } - } - }); - }, - beginTransaction: function () { - var tables = null; - var callBack = null; - var isWrite = false; - - function readParam(value) { - if (Object.isNullOrUndefined(value)) return; - - if (typeof value === 'boolean') { - isWrite = value; - } else if (Array.isArray(value)) { - tables = value; - } else { - callBack = value; - } - } - - readParam(arguments[0]); - readParam(arguments[1]); - readParam(arguments[2]); - - var pHandler = new $data.PromiseHandler(); - callBack = pHandler.createCallback(callBack); - - //callBack = $data.typeSystem.createCallbackSetting(callBack); - this.storageProvider._beginTran(tables, isWrite, callBack); - - return pHandler.getPromise(); - }, - _isReturnTransaction: function (transaction) { - return transaction instanceof $data.Base || transaction === 'returnTransaction'; - }, - _applyTransaction: function (scope, cb, args, transaction, isReturnTransaction) { - if (isReturnTransaction === true) { - if (transaction instanceof $data.Transaction) { - Array.prototype.push.call(args, transaction); - cb.apply(scope, args); - } else { - this.beginTransaction(function (tran) { - Array.prototype.push.call(args, tran); - cb.apply(scope, args); - }); - } - } - else { - cb.apply(scope, args); - } - }, - - getDataType: function (dataType) { - // Obsolate - if (typeof dataType == "string") { - var memDef_dataType = this[dataType]; - if (memDef_dataType === undefined || memDef_dataType === null) { memDef_dataType = eval(dataType); } - return memDef_dataType; - } - return dataType; - }, - _initializeEntitySets: function (ctor) { - - for (var i = 0, l = this._storageModel.length; i < l; i++){ - var storageModel = this._storageModel[i]; - this[storageModel.ItemName] = new $data.EntitySet(storageModel.LogicalType, this, storageModel.ItemName, storageModel.EventHandlers, storageModel.Roles); - var sm = this[storageModel.ItemName]; - sm.name = storageModel.ItemName; - sm.tableName = storageModel.TableName; - sm.tableOptions = storageModel.TableOptions; - sm.eventHandlers = storageModel.EventHandlers; - this._entitySetReferences[storageModel.LogicalType.name] = sm; - - this._initializeActions(sm, ctor, ctor.getMemberDefinition(storageModel.ItemName)); - - } - - }, - _initializeStore: function (callBack) { - if (this.storageProvider) { - this.storageProvider.initializeStore(callBack); - } - }, - - _initStorageModelSync: function() { - var _memDefArray = this.getType().memberDefinitions.asArray(); - - - for (var i = 0; i < _memDefArray.length; i++) { - var item = _memDefArray[i]; - if ('dataType' in item) { - var itemResolvedDataType = Container.resolveType(item.dataType); - if (itemResolvedDataType && itemResolvedDataType.isAssignableTo && itemResolvedDataType.isAssignableTo($data.EntitySet)) { - var elementType = Container.resolveType(item.elementType); - var storageModel = new $data.StorageModel(); - storageModel.TableName = item.tableName || item.name; - storageModel.TableOptions = item.tableOptions; - storageModel.ItemName = item.name; - storageModel.LogicalType = elementType; - storageModel.LogicalTypeName = elementType.name; - storageModel.PhysicalTypeName = $data.EntityContext._convertLogicalTypeNameToPhysical(storageModel.LogicalTypeName); - storageModel.ContextType = this.getType(); - storageModel.Roles = item.roles; - if (item.indices) { - storageModel.indices = item.indices; - } - if (item.beforeCreate) { - if (!storageModel.EventHandlers) storageModel.EventHandlers = {}; - storageModel.EventHandlers.beforeCreate = item.beforeCreate; - } - if (item.beforeRead) { - if (!storageModel.EventHandlers) storageModel.EventHandlers = {}; - storageModel.EventHandlers.beforeRead = item.beforeRead; - } - if (item.beforeUpdate) { - if (!storageModel.EventHandlers) storageModel.EventHandlers = {}; - storageModel.EventHandlers.beforeUpdate = item.beforeUpdate; - } - if (item.beforeDelete) { - if (!storageModel.EventHandlers) storageModel.EventHandlers = {}; - storageModel.EventHandlers.beforeDelete = item.beforeDelete; - } - if (item.afterCreate) { - if (!storageModel.EventHandlers) storageModel.EventHandlers = {}; - storageModel.EventHandlers.afterCreate = item.afterCreate; - } - if (item.afterRead) { - if (!storageModel.EventHandlers) storageModel.EventHandlers = {}; - storageModel.EventHandlers.afterRead = item.afterRead; - } - if (item.afterUpdate) { - if (!storageModel.EventHandlers) storageModel.EventHandlers = {}; - storageModel.EventHandlers.afterUpdate = item.afterUpdate; - } - if (item.afterDelete) { - if (!storageModel.EventHandlers) storageModel.EventHandlers = {}; - storageModel.EventHandlers.afterDelete = item.afterDelete; - } - this._storageModel.push(storageModel); - var name = Container.resolveName(elementType); - this._storageModel[name] = storageModel; - } - } - } - - }, - _initializeStorageModel: function () { - - - var _memDefArray = this.getType().memberDefinitions.asArray(); - - - if (typeof intellisense !== 'undefined') - return; - - - for (var i = 0; i < this._storageModel.length; i++) { - var storageModel = this._storageModel[i]; - - ///Storage model item - var dbEntityInstanceDefinition = {}; - - storageModel.Associations = storageModel.Associations || []; - storageModel.ComplexTypes = storageModel.ComplexTypes || []; - for (var j = 0; j < storageModel.LogicalType.memberDefinitions.getPublicMappedProperties().length; j++) { - var memDef = storageModel.LogicalType.memberDefinitions.getPublicMappedProperties()[j]; - ///Member definition instance - - var memDefResolvedDataType = Container.resolveType(memDef.dataType); - - if ((this.storageProvider.supportedDataTypes.indexOf(memDefResolvedDataType) > -1) && Object.isNullOrUndefined(memDef.inverseProperty)) { - //copy member definition - var t = JSON.parse(JSON.stringify(memDef)); - //change datatype to resolved type - t.dataType = memDefResolvedDataType; - dbEntityInstanceDefinition[memDef.name] = t; - continue; - } - - this._buildDbType_navigationPropertyComplite(memDef, memDefResolvedDataType, storageModel); - - //var memDef_dataType = this.getDataType(memDef.dataType); - if ((memDefResolvedDataType === $data.Array || (memDefResolvedDataType.isAssignableTo && memDefResolvedDataType.isAssignableTo($data.EntitySet))) && - (memDef.inverseProperty && memDef.inverseProperty !== '$$unbound')) { - this._buildDbType_Collection_OneManyDefinition(dbEntityInstanceDefinition, storageModel, memDefResolvedDataType, memDef); - } else { - if (memDef.inverseProperty) { - if (memDef.inverseProperty === '$$unbound') { - //member definition is navigation but not back reference - if (memDefResolvedDataType === $data.Array) { - this._buildDbType_Collection_OneManyDefinition(dbEntityInstanceDefinition, storageModel, memDefResolvedDataType, memDef); - } else { - this._buildDbType_ElementType_OneManyDefinition(dbEntityInstanceDefinition, storageModel, memDefResolvedDataType, memDef); - } - } else { - //member definition is navigation property one..one or one..many case - var fields = memDefResolvedDataType.memberDefinitions.getMember(memDef.inverseProperty); - if (fields) { - if (fields.elementType) { - //member definition is one..many connection - var referealResolvedType = Container.resolveType(fields.elementType); - if (referealResolvedType === storageModel.LogicalType) { - this._buildDbType_ElementType_OneManyDefinition(dbEntityInstanceDefinition, storageModel, memDefResolvedDataType, memDef); - } else { - if (typeof intellisense === 'undefined') { - Guard.raise(new Exception('Inverse property not valid, refereed item element type not match: ' + storageModel.LogicalTypeName, ', property: ' + memDef.name)); - } - } - } else { - //member definition is one..one connection - this._buildDbType_ElementType_OneOneDefinition(dbEntityInstanceDefinition, storageModel, memDefResolvedDataType, memDef); - } - } else { - if (typeof intellisense === 'undefined') { - Guard.raise(new Exception('Inverse property not valid')); - } - } - } - } else { - //member definition is a complex type - this._buildDbType_addComplexTypePropertyDefinition(dbEntityInstanceDefinition, storageModel, memDefResolvedDataType, memDef); - } - } - } - this._buildDbType_modifyInstanceDefinition(dbEntityInstanceDefinition, storageModel, this); - var dbEntityClassDefinition = {}; - dbEntityClassDefinition.convertTo = this._buildDbType_generateConvertToFunction(storageModel, this); - this._buildDbType_modifyClassDefinition(dbEntityClassDefinition, storageModel, this); - - //create physical type - //TODO - storageModel.PhysicalType = $data.Class.define(storageModel.PhysicalTypeName, $data.Entity, storageModel.LogicalType.container, dbEntityInstanceDefinition, dbEntityClassDefinition); - } - }, - _initializeActions: function (es, ctor, esDef) { - if (esDef && esDef.actions) { - var actionKeys = Object.keys(esDef.actions); - for (var i = 0; i < actionKeys.length; i++) { - var actionName = actionKeys[i]; - var action = esDef.actions[actionName]; - if (typeof action === 'function') { - es[actionName] = action; - } else { - var actionDef = $data.MemberDefinition.translateDefinition(action, actionName, ctor); - if (actionDef instanceof $data.MemberDefinition && actionDef.kind === $data.MemberTypes.method) { - es[actionName] = actionDef.method; - } - } - } - } - }, - _buildDbType_navigationPropertyComplite: function (memDef, memDefResolvedDataType, storageModel) { - if (!memDef.inverseProperty) { - var refMemDefs = null; - if (memDefResolvedDataType === $data.Array || (memDefResolvedDataType.isAssignableTo && memDefResolvedDataType.isAssignableTo($data.EntitySet))) { - var refStorageModel = this._storageModel.getStorageModel(Container.resolveType(memDef.elementType)); - if (refStorageModel) { - refMemDefs = []; - var pubDefs = refStorageModel.LogicalType.memberDefinitions.getPublicMappedProperties(); - for (var i = 0; i < pubDefs.length; i++) { - var m = pubDefs[i]; - if ((m.inverseProperty == memDef.name) && (Container.resolveType(m.dataType) === Container.resolveType(storageModel.LogicalType))) - refMemDefs.push(m); - } - } - } else { - var refStorageModel = this._storageModel.getStorageModel(memDefResolvedDataType); - if (refStorageModel) { - refMemDefs = []; - var pubDefs = refStorageModel.LogicalType.memberDefinitions.getPublicMappedProperties(); - for (var i = 0; i < pubDefs.length; i++) { - var m = pubDefs[i]; - if (m.elementType && ((m.inverseProperty == memDef.name) && (Container.resolveType(m.elementType) === storageModel.LogicalType))) - refMemDefs.push(m); - else if ((m.inverseProperty == memDef.name) && (Container.resolveType(m.dataType) === storageModel.LogicalType)) - refMemDefs.push(m); - } - } - } - if (refMemDefs) { - if (refMemDefs.length > 1) { - if (typeof intellisense !== 'undefined') { - Guard.raise(new Exception('More than one inverse property refer to this member definition: ' + memDef.name + ', type: ' + Container.resolveName(storageModel.LogicalType))); - } - } - var refMemDef = refMemDefs.pop(); - if (refMemDef) { - memDef.inverseProperty = refMemDef.name; - } - } - } else { - var refStorageModel = null; - if (memDefResolvedDataType === $data.Array || (memDefResolvedDataType.isAssignableTo && memDefResolvedDataType.isAssignableTo($data.EntitySet))) { - refStorageModel = this._storageModel.getStorageModel(Container.resolveType(memDef.elementType)); - - } else { - refStorageModel = this._storageModel.getStorageModel(memDefResolvedDataType); - } - - var p = refStorageModel.LogicalType.memberDefinitions.getMember(memDef.inverseProperty); - if (p) { - if (p.inverseProperty) { - if (p.inverseProperty != memDef.name) { - if (typeof intellisense === 'undefined') { - Guard.raise(new Exception('Inverse property mismatch')); - } - } - } else { - p.inverseProperty = memDef.name; - } - } - - } - }, - _buildDbType_generateConvertToFunction: function (storageModel) { return function (instance) { return instance; }; }, - _buildDbType_modifyInstanceDefinition: function (instanceDefinition, storageModel) { return; }, - _buildDbType_modifyClassDefinition: function (classDefinition, storageModel) { return; }, - _buildDbType_addComplexTypePropertyDefinition: function (dbEntityInstanceDefinition, storageModel, memDef_dataType, memDef) { - this._addNavigationPropertyDefinition(dbEntityInstanceDefinition, memDef, memDef.name, $data.MemberTypes.complexProperty); - var complexType = this._createComplexElement(storageModel.LogicalType, "", memDef.name, memDef_dataType, "", ""); - storageModel.ComplexTypes[memDef.name] = complexType; - storageModel.ComplexTypes.push(complexType); - }, - _buildDbType_Collection_OneManyDefinition: function (dbEntityInstanceDefinition, storageModel, memDef_dataType, memDef) { - var refereedType = Container.resolveType(memDef.elementType); - if (refereedType === undefined || refereedType === null) { - if (typeof intellisense === 'undefined') { - Guard.raise(new Exception("Element type definition error", "Field definition", memDef)); - } - } - var refereedStorageModel = this._storageModel.getStorageModel(refereedType); - //var refereedStorageModel = this._storageModel.filter(function (s) { return s.LogicalType === refereedType; })[0]; - if (!refereedStorageModel) { - if (typeof intellisense === 'undefined') { - Guard.raise(new Exception("No EntitySet definition for the following element type", "Field definition", memDef)); - } - } - - this._addNavigationPropertyDefinition(dbEntityInstanceDefinition, memDef, memDef.name); - var associationType = memDef.inverseProperty === '$$unbound' ? '$$unbound' : '0..1'; - var association = this._addAssociationElement(storageModel.LogicalType, associationType, memDef.name, refereedStorageModel.LogicalType, "*", memDef.inverseProperty); - storageModel.Associations[memDef.name] = association; - storageModel.Associations.push(association); - }, - _buildDbType_ElementType_OneManyDefinition: function (dbEntityInstanceDefinition, storageModel, memDef_dataType, memDef) { - var refereedType = Container.resolveType(memDef.dataType); - if (refereedType === undefined || refereedType === null) { - if (typeof intellisense === 'undefined') { - Guard.raise(new Exception("Element type definition error", "Field definition", memDef)); - } - } - var refereedStorageModel = this._storageModel.getStorageModel(refereedType); - //var refereedStorageModel = this._storageModel.filter(function (s) { return s.LogicalType === refereedType; })[0]; - if (!refereedStorageModel) { - if (typeof intellisense === 'undefined') { - Guard.raise(new Exception("No EntitySet definition for the following element type", "Field definition", memDef)); - } - } - - this._addNavigationPropertyDefinition(dbEntityInstanceDefinition, memDef, memDef.name); - var associationType = memDef.inverseProperty === '$$unbound' ? '$$unbound' : '*'; - var association = this._addAssociationElement(storageModel.LogicalType, associationType, memDef.name, refereedStorageModel.LogicalType, "0..1", memDef.inverseProperty); - storageModel.Associations[memDef.name] = association; - storageModel.Associations.push(association); - }, - _buildDbType_ElementType_OneOneDefinition: function (dbEntityInstanceDefinition, storageModel, memDef_dataType, memDef) { - var refereedType = Container.resolveType(memDef.dataType); - if (refereedType === undefined || refereedType === null) { - if (typeof intellisense === 'undefined') { - Guard.raise(new Exception("Element type definition error", "Field definition", memDef)); - } - } - var refereedStorageModel = this._storageModel.getStorageModel(refereedType);; - //var refereedStorageModel = this._storageModel.filter(function (s) { return s.LogicalType === refereedType; })[0]; - if (!refereedStorageModel) { - if (typeof intellisense === 'undefined') { - Guard.raise(new Exception("No EntitySet definition following element type", "Field definition", memDef)); - } - } - - var refereedMemberDefinition = refereedStorageModel.LogicalType.memberDefinitions.getMember(memDef.inverseProperty); - if (!refereedMemberDefinition.required && !memDef.required) { if (typeof intellisense === 'undefined') { if (typeof intellisense === 'undefined') { Guard.raise(new Exception('In one to one connection, one side must required!', 'One to One connection', memDef)); } } } - - this._addNavigationPropertyDefinition(dbEntityInstanceDefinition, memDef, memDef.name); - - var association = this._addAssociationElement(storageModel.LogicalType, - memDef.required ? "0..1" : "1", - memDef.name, - refereedStorageModel.LogicalType, - memDef.required ? "1" : "0..1", - memDef.inverseProperty); - storageModel.Associations[memDef.name] = association; - storageModel.Associations.push(association); - }, - _addNavigationPropertyDefinition: function (definition, member, associationName, kind) { - var t = JSON.parse(JSON.stringify(member)); - t.dataType = $data.EntitySet; - t.notMapped = true; - t.kind = kind ? kind : $data.MemberTypes.navProperty; - t.association = associationName; - definition[member.name] = t; - }, - _addAssociationElement: function (fromType, fromMultiplicity, fromPropName, toType, toMultiplicity, toPropName) { - return new $data.Association({ - From: fromType.name, - FromType: fromType, - FromMultiplicity: fromMultiplicity, - FromPropertyName: fromPropName, - To: toType.name, - ToType: toType, - ToMultiplicity: toMultiplicity, - ReferentialConstraint: [], - ToPropertyName: toPropName - }); - }, - _createComplexElement: function (fromType, fromMultiplicity, fromPropName, toType, toMultiplicity, toPropName) { - return new $data.ComplexType({ - From: fromType.name, - FromType: fromType, - FromMultiplicity: fromMultiplicity, - FromPropertyName: fromPropName, - To: toType.name, - ToType: toType, - ToMultiplicity: toMultiplicity, - ReferentialConstraint: [], - ToPropertyName: toPropName - }); - }, - - _successInitProvider: function (context, error) { - if (context instanceof $data.EntityContext && context._isOK !== undefined) { - if (!error) { - context._isOK = true; - if (context.onReadyFunction) { - for (var i = 0; i < context.onReadyFunction.length; i++) { - context.onReadyFunction[i].success(context); - } - context.onReadyFunction = undefined; - } - } else { - context._isOK = error; - if (context.onReadyFunction) { - for (var i = 0; i < context.onReadyFunction.length; i++) { - context.onReadyFunction[i].error(error); - } - context.onReadyFunction = undefined; - } - } - } - }, - onReady: function (fn) { - /// - /// - /// Sets the callback function to be called when the initialization of the EntityContext has successfully finished. - /// - /// - /// Success callback - /// Current entityContext object - /// - /// - /// - /// - /// - /// Sets the callback functions to be called when the initialization of the EntityContext has finished. - /// - /// - /// Success and error callbacks definition. - /// Example: [code]{ success: function(db) { .. }, error: function() { .. } }[/code] - /// - /// - /// - var pHandler = new $data.PromiseHandler(); - var callBack = pHandler.createCallback(fn); - if (this._isOK === true) { - callBack.success(this); - } else if (this._isOK !== false) { - callBack.error(this._isOK); - } else { - this.onReadyFunction = this.onReadyFunction || []; - this.onReadyFunction.push(callBack); - } - - return pHandler.getPromise(); - }, - ready: { type: $data.Promise }, - getEntitySetFromElementType: function (elementType) { - /// - /// Gets the matching EntitySet for an element type. - /// - /// - /// - /// - /// Gets the matching EntitySet for an element type. - /// - /// - /// - var result = this._entitySetReferences[elementType]; - if (!result) { - try { - result = this._entitySetReferences[eval(elementType).name]; - } catch (ex) { } - } - return result; - }, - executeQuery: function (queryable, callBack, transaction) { - var query = new $data.Query(queryable.expression, queryable.defaultType, this); - query.transaction = transaction instanceof $data.Transaction ? transaction : undefined; - var returnTransaction = this._isReturnTransaction(transaction); - - callBack = $data.typeSystem.createCallbackSetting(callBack); - var that = this; - var clbWrapper = {}; - clbWrapper.success = function (query) { - if ($data.QueryCache && $data.QueryCache.isCacheable(that, query)) { - $data.QueryCache.addToCache(that, query); - } - - query.buildResultSet(that); - - if ($data.ItemStore && 'QueryResultModifier' in $data.ItemStore) - $data.ItemStore.QueryResultModifier.call(that, query); - - var successResult; - - if (query.expression.nodeType === $data.Expressions.ExpressionType.Single || - query.expression.nodeType === $data.Expressions.ExpressionType.Find || - query.expression.nodeType === $data.Expressions.ExpressionType.Count || - query.expression.nodeType === $data.Expressions.ExpressionType.BatchDelete || - query.expression.nodeType === $data.Expressions.ExpressionType.Some || - query.expression.nodeType === $data.Expressions.ExpressionType.Every) { - if (query.result.length !== 1) { - callBack.error(new Exception('result count failed')); - return; - } - - successResult = query.result[0]; - } else if (query.expression.nodeType === $data.Expressions.ExpressionType.First) { - if (query.result.length === 0) { - callBack.error(new Exception('result count failed')); - return; - } - - successResult = query.result[0]; - } else { - if (typeof query.__count === 'number' && query.result) - query.result.totalCount = query.__count; - - that.storageProvider._buildContinuationFunction(that, query); - - successResult = query.result; - } - - var readyFn = function () { - that._applyTransaction(callBack, callBack.success, [successResult], query.transaction, returnTransaction); - - /*if (returnTransaction === true) { - if (query.transaction) - callBack.success(successResult, query.transaction); - else { - that.beginTransaction(function (tran) { - callBack.success(successResult, tran); - }); - } - } - else - callBack.success(successResult);*/ - }; - - var i = 0; - var sets = query.getEntitySets(); - - var callbackFn = function () { - var es = sets[i]; - if (es.afterRead) { - i++; - var r = es.afterRead.call(this, successResult, sets, query); - if (typeof r === 'function') { - r.call(this, i < sets.length ? callbackFn : readyFn, successResult, sets, query); - } else { - if (i < sets.length) { - callbackFn(); - } else readyFn(); - } - } else readyFn(); - } - - if (sets.length) callbackFn(); - else readyFn(); - }; - - clbWrapper.error = function () { - if(returnTransaction) - callBack.error.apply(this, arguments); - else - callBack.error.apply(this, Array.prototype.filter.call(arguments, function (p) { return !(p instanceof $data.Transaction); })); - }; - var sets = query.getEntitySets(); - - var authorizedFn = function () { - var ex = true; - var wait = false; - var ctx = that; - - var readyFn = function (cancel) { - if (cancel === false) ex = false; - - if (ex) { - if (query.transaction) { - if ($data.QueryCache && $data.QueryCache.isInCache(that, query)) { - $data.QueryCache.executeQuery(that, query, clbWrapper); - } else { - ctx.storageProvider.executeQuery(query, clbWrapper); - } - } else { - ctx.beginTransaction(function (tran) { - query.transaction = tran; - if ($data.QueryCache && $data.QueryCache.isInCache(that, query)) { - $data.QueryCache.executeQuery(that, query, clbWrapper); - } else { - ctx.storageProvider.executeQuery(query, clbWrapper); - } - }); - } - } else { - query.rawDataList = []; - query.result = []; - clbWrapper.success(query); - } - }; - - var i = 0; - var callbackFn = function (cancel) { - if (cancel === false) ex = false; - - var es = sets[i]; - if (es.beforeRead) { - i++; - var r = es.beforeRead.call(this, sets, query); - if (typeof r === 'function') { - r.call(this, (i < sets.length && ex) ? callbackFn : readyFn, sets, query); - } else { - if (r === false) ex = false; - - if (i < sets.length && ex) { - callbackFn(); - } else readyFn(); - } - } else readyFn(); - }; - - if (sets.length) callbackFn(); - else readyFn(); - }; - - if (this.user && this.checkPermission) { - this.checkPermission(query.expression.nodeType === $data.Expressions.ExpressionType.BatchDelete ? $data.Access.DeleteBatch : $data.Access.Read, this.user, sets, { - success: authorizedFn, - error: clbWrapper.error - }); - } else authorizedFn(); - }, - saveChanges: function (callback, transaction) { - /// - /// - /// Saves the changes made to the context. - /// - /// - /// Success callback - /// Current entityContext object - /// - /// - /// - /// - /// - /// Saves the changes made to the context. - /// - /// - /// Success and error callbacks definition. - /// Example: [code]{ success: function(db) { .. }, error: function() { .. } }[/code] - /// - /// - /// - - if ($data.QueryCache) { - $data.QueryCache.reset(this); - } - - var changedEntities = []; - var trackedEntities = this.stateManager.trackedEntities; - var pHandler = new $data.PromiseHandler(); - var clbWrapper = pHandler.createCallback(callback); - var pHandlerResult = pHandler.getPromise(); - var returnTransaction = this._isReturnTransaction(transaction); - - var skipItems = []; - while (trackedEntities.length > 0) { - var additionalEntities = []; - //trackedEntities.forEach(function (entityCachedItem) { - for (var i = 0; i < trackedEntities.length; i++) { - var entityCachedItem = trackedEntities[i]; - - var sModel = this._storageModel.getStorageModel(entityCachedItem.data.getType()); - if (entityCachedItem.data.entityState == $data.EntityState.Unchanged) { - entityCachedItem.skipSave = true; - skipItems.push(entityCachedItem.data); - } else { - if (entityCachedItem.data.entityState == $data.EntityState.Modified) { - if (entityCachedItem.data.changedProperties) { - var changeStoredProperty = entityCachedItem.data.changedProperties.some(function (p) { - var pMemDef = sModel.PhysicalType.memberDefinitions.getMember(p.name); - if (pMemDef.kind == $data.MemberTypes.navProperty) { - var a = sModel.Associations[pMemDef.association]; - var multiplicity = a.FromMultiplicity + a.ToMultiplicity; - return ((multiplicity == '*0..1') || (multiplicity == '0..11')) - } - return true; - }); - if (!changeStoredProperty) { - entityCachedItem.skipSave = true; - skipItems.push(entityCachedItem.data); - } - } - } - } - - //type before events with items - this.processEntityTypeBeforeEventHandler(skipItems, entityCachedItem); - - var navigationProperties = []; - var smPhyMemDefs = sModel.PhysicalType.memberDefinitions.asArray(); - for (var ism = 0; ism < smPhyMemDefs.length; ism++) { - var p = smPhyMemDefs[ism]; - if (p.kind == $data.MemberTypes.navProperty) - navigationProperties.push(p); - } - //var navigationProperties = sModel.PhysicalType.memberDefinitions.asArray().filter(function (p) { return p.kind == $data.MemberTypes.navProperty; }); - //navigationProperties.forEach(function (navProp) { - for (var j = 0; j < navigationProperties.length; j++) { - var navProp = navigationProperties[j]; - - var association = sModel.Associations[navProp.name]; //eg.:"Profile" - var name = navProp.name; //eg.: "Profile" - var navPropertyName = association.ToPropertyName; //eg.: User - - var connectedDataList = [].concat(entityCachedItem.data[name]); - //connectedDataList.forEach(function (data) { - for (var k = 0; k < connectedDataList.length; k++) { - var data = connectedDataList[k]; - - if (data) { - var value = data[navPropertyName]; - var associationType = association.FromMultiplicity + association.ToMultiplicity; - if (association.FromMultiplicity === '$$unbound') { - if (data instanceof $data.Array) { - entityCachedItem.dependentOn = entityCachedItem.dependentOn || []; - //data.forEach(function (dataItem) { - for (var l = 0; l < data.length; l++) { - var dataItem = data[l]; - - if ((entityCachedItem.dependentOn.indexOf(data) < 0) && (data.skipSave !== true)) { - entityCachedItem.dependentOn.push(data); - } - } - //}, this); - } else { - entityCachedItem.dependentOn = entityCachedItem.dependentOn || []; - if ((entityCachedItem.dependentOn.indexOf(data) < 0) && (data.skipSave !== true)) { - entityCachedItem.dependentOn.push(data); - } - } - } else { - switch (associationType) { - case "*0..1": //Array - if (value) { - if (value instanceof Array) { - if (value.indexOf(entityCachedItem.data) == -1) { - value.push(entityCachedItem.data); - data.initData[navPropertyName] = value; - data._setPropertyChanged(association.ToType.getMemberDefinition(navPropertyName)); - } - } else { - if (typeof intellisense === 'undefined') { - Guard.raise("Item must be array or subtype of array"); - } - } - } else { - data.initData[navPropertyName] = [entityCachedItem.data]; - data._setPropertyChanged(association.ToType.getMemberDefinition(navPropertyName)); - } - break; - default: //Item - if (value) { - if (value !== entityCachedItem.data) { - if (typeof intellisense === 'undefined') { - Guard.raise("Integrity check error! Item assigned to another entity!"); - } - } - } else { - data.initData[navPropertyName] = entityCachedItem.data; //set back reference for live object - data._setPropertyChanged(association.ToType.getMemberDefinition(navPropertyName)); - } - break; - } - switch (associationType) { - case "*0..1": - case "0..11": - entityCachedItem.dependentOn = entityCachedItem.dependentOn || []; - if ((entityCachedItem.dependentOn.indexOf(data) < 0) && (data.skipSave !== true)) { - entityCachedItem.dependentOn.push(data); - } - break; - } - } - if (!data.entityState) { - if (data.storeToken === this.storeToken) { - data.entityState = $data.EntityState.Modified; - } else { - data.entityState = $data.EntityState.Added; - } - } - if (additionalEntities.indexOf(data) == -1) { - additionalEntities.push(data); - } - } - } - //}, this); - } - //}, this); - } - //}, this); - - //trackedEntities.forEach(function (entity) { - for (var i = 0; i < trackedEntities.length; i++) { - var entity = trackedEntities[i]; - - if (entity.skipSave !== true) { changedEntities.push(entity); } - } - //}); - - trackedEntities = []; - //additionalEntities.forEach(function (item) { - for (var i = 0; i < additionalEntities.length; i++) { - var item = additionalEntities[i]; - - if (!skipItems.some(function (entity) { return entity == item; })) { - if (!changedEntities.some(function (entity) { return entity.data == item; })) { - trackedEntities.push({ data: item, entitySet: this.getEntitySetFromElementType(item.getType().name) }); - } - } - } - //}, this); - } - - - //changedEntities.forEach(function (d) { - for (var j = 0; j < changedEntities.length; j++) { - var d = changedEntities[j]; - - if (d.dependentOn) { - var temp = []; - for (var i = 0; i < d.dependentOn.length; i++) { - if (skipItems.indexOf(d.dependentOn[i]) < 0) { - temp.push(d.dependentOn[i]); - } - } - d.dependentOn = temp; - } - } - //}); - skipItems = null; - var ctx = this; - if (changedEntities.length == 0) { - this.stateManager.trackedEntities.length = 0; - ctx._applyTransaction(clbWrapper, clbWrapper.success, [0], transaction, returnTransaction); - - /*if (returnTransaction) { - clbWrapper.success(0, transaction); - } else { - clbWrapper.success(0); - }*/ - return pHandlerResult; - } - - //validate entities - var errors = []; - //changedEntities.forEach(function (entity) { - for (var i = 0; i < changedEntities.length; i++) { - var entity = changedEntities[i]; - - if (entity.data.entityState === $data.EntityState.Added) { - //entity.data.getType().memberDefinitions.getPublicMappedProperties().forEach(function (memDef) { - for (var j = 0; j < entity.data.getType().memberDefinitions.getPublicMappedProperties().length; j++) { - var memDef = entity.data.getType().memberDefinitions.getPublicMappedProperties()[j]; - - var memDefType = Container.resolveType(memDef.type); - if (memDef.required && !memDef.computed && !entity.data[memDef.name] && !memDef.isDependentProperty) { - switch (memDefType) { - case $data.String: - case $data.Number: - case $data.Float: - case $data.Decimal: - case $data.Integer: - case $data.Int16: - case $data.Int32: - case $data.Int64: - case $data.Byte: - case $data.SByte: - case $data.Date: - case $data.Boolean: - entity.data[memDef.name] = Container.getDefault(memDef.dataType); - break; - default: - break; - } - } - } - //}, this); - } - if ((entity.data.entityState === $data.EntityState.Added || entity.data.entityState === $data.EntityState.Modified) - && !entity.data.isValid()) { - errors.push({ item: entity.data, errors: entity.data.ValidationErrors }); - } - } - //}); - if (errors.length > 0) { - clbWrapper.error(errors); - return pHandlerResult; - } - - var access = $data.Access.None; - - var eventData = {}; - var sets = []; - for (var i = 0; i < changedEntities.length; i++) { - var it = changedEntities[i]; - var n = it.entitySet.elementType.name; - if (sets.indexOf(it.entitySet) < 0) sets.push(it.entitySet); - var es = this._entitySetReferences[n]; - if (es.beforeCreate || es.beforeUpdate || es.beforeDelete || (this.user && this.checkPermission)) { - if (!eventData[n]) eventData[n] = {}; - - switch (it.data.entityState) { - case $data.EntityState.Added: - access |= $data.Access.Create; - if (es.beforeCreate) { - if (!eventData[n].createAll) eventData[n].createAll = []; - eventData[n].createAll.push(it); - } - break; - case $data.EntityState.Modified: - access |= $data.Access.Update; - if (es.beforeUpdate) { - if (!eventData[n].modifyAll) eventData[n].modifyAll = []; - eventData[n].modifyAll.push(it); - } - break; - case $data.EntityState.Deleted: - access |= $data.Access.Delete; - if (es.beforeDelete) { - if (!eventData[n].deleteAll) eventData[n].deleteAll = []; - eventData[n].deleteAll.push(it); - } - break; - } - } - } - - var readyFn = function (cancel) { - if (cancel === false) { - cancelEvent = 'async'; - changedEntities.length = 0; - } - - if (changedEntities.length) { - //console.log('changedEntities: ', changedEntities.map(function(it){ return it.data.initData; })); - - var innerCallback = { - success: function (tran) { - ctx._postProcessSavedItems(clbWrapper, changedEntities, tran, returnTransaction); - }, - error: function () { - //TODO remove trans from args; - if (returnTransaction) - clbWrapper.error.apply(this, arguments); - else - clbWrapper.error.apply(this, Array.prototype.filter.call(arguments, function (p) { return !(p instanceof $data.Transaction); })); - } - }; - - if (transaction instanceof $data.Transaction){ - ctx.storageProvider.saveChanges(innerCallback, changedEntities, transaction); - } else { - ctx.beginTransaction(true, function (tran) { - ctx.storageProvider.saveChanges(innerCallback, changedEntities, tran); - }); - } - } else if (cancelEvent) { - clbWrapper.error(new Exception('Cancelled event in ' + cancelEvent, 'CancelEvent')); - } else { - ctx._applyTransaction(clbWrapper, clbWrapper.success, [0], transaction, returnTransaction); - - /*if(returnTransaction) - clbWrapper.success(0, transaction); - else - clbWrapper.success(0);*/ - }; - - /*else if (cancelEvent) clbWrapper.error(new $data.Exception('saveChanges cancelled from event [' + cancelEvent + ']')); - else Guard.raise('No changed entities');*/ - }; - - var cancelEvent; - var ies = Object.getOwnPropertyNames(eventData); - var i = 0; - var cmd = ['beforeUpdate', 'beforeDelete', 'beforeCreate']; - var cmdAll = { - beforeCreate: 'createAll', - beforeDelete: 'deleteAll', - beforeUpdate: 'modifyAll' - }; - - var callbackFn = function (cancel) { - if (cancel === false) { - cancelEvent = 'async'; - changedEntities.length = 0; - - readyFn(cancel); - return; - } - - var es = ctx._entitySetReferences[ies[i]]; - var c = cmd.pop(); - var ed = eventData[ies[i]]; - var all = ed[cmdAll[c]]; - - if (all) { - var m = []; - for (var im = 0; im < all.length; im++) { - m.push(all[im].data); - } - //var m = all.map(function(it){ return it.data; }); - if (!cmd.length) { - cmd = ['beforeUpdate', 'beforeDelete', 'beforeCreate']; - i++; - } - - var r = es[c].call(ctx, m); - if (typeof r === 'function') { - r.call(ctx, (i < ies.length && !cancelEvent) ? callbackFn : readyFn, m); - } else if (r === false) { - cancelEvent = (es.name + '.' + c); - //all.forEach(function (it) { - for (var index = 0; index < all.length; index++) { - var it = all[index]; - - var ix = changedEntities.indexOf(it); - changedEntities.splice(ix, 1); - } - //}); - - readyFn(); - } else { - if (i < ies.length && !cancelEvent) callbackFn(); - else readyFn(); - } - } else { - if (!cmd.length) { - cmd = ['beforeUpdate', 'beforeDelete', 'beforeCreate']; - i++; - } - - if (i < ies.length && !cancelEvent) callbackFn(); - else readyFn(); - } - }; - - if (this.user && this.checkPermission) { - this.checkPermission(access, this.user, sets, { - success: function () { - if (i < ies.length) callbackFn(); - else readyFn(); - }, - error: clbWrapper.error - }); - } else { - if (i < ies.length) callbackFn(); - else readyFn(); - } - - return pHandlerResult; - }, - - processEntityTypeBeforeEventHandler: function (skipItems, entityCachedItem) { - if (!entityCachedItem.skipSave) { - var entity = entityCachedItem.data; - var entityType = entity.getType(); - var state = entity.entityState; - - switch (true) { - case state === $data.EntityState.Added && entityType.onbeforeCreate instanceof $data.Event: - if (entityType.onbeforeCreate.fireCancelAble(entity) === false) { - entityCachedItem.skipSave = true; - skipItems.push(entity); - } - break; - case state === $data.EntityState.Modified && entityType.onbeforeUpdate instanceof $data.Event: - if (entityType.onbeforeUpdate.fireCancelAble(entity) === false) { - entityCachedItem.skipSave = true; - skipItems.push(entity); - } - break; - case state === $data.EntityState.Deleted && entityType.onbeforeDelete instanceof $data.Event: - if (entityType.onbeforeDelete.fireCancelAble(entity) === false) { - entityCachedItem.skipSave = true; - skipItems.push(entity); - } - break; - default: - break; - } - } - }, - processEntityTypeAfterEventHandler: function (entityCachedItem) { - var entity = entityCachedItem.data; - var entityType = entity.getType(); - var state = entity.entityState; - - switch (true) { - case state === $data.EntityState.Added && entityType.onafterCreate instanceof $data.Event: - entityType.onafterCreate.fire(entity); - break; - case state === $data.EntityState.Modified && entityType.onafterUpdate instanceof $data.Event: - entityType.onafterUpdate.fire(entity); - break; - case state === $data.EntityState.Deleted && entityType.onafterDelete instanceof $data.Event: - entityType.onafterDelete.fire(entity); - break; - default: - break; - } - }, - - bulkInsert: function (entitySet, fields, datas, callback) { - var pHandler = new $data.PromiseHandler(); - callback = pHandler.createCallback(callback); - if (typeof entitySet === 'string') { - var currentEntitySet; - - for (var entitySetName in this._entitySetReferences) { - var actualEntitySet = this._entitySetReferences[entitySetName]; - if (actualEntitySet.tableName === entitySet) { - currentEntitySet = actualEntitySet; - break; - } - } - - if (!currentEntitySet) - currentEntitySet = this[entitySet]; - - entitySet = currentEntitySet; - } - if (entitySet) { - this.storageProvider.bulkInsert(entitySet, fields, datas, callback); - } else { - callback.error(new Exception('EntitySet not found')); - } - return pHandler.getPromise(); - }, - - prepareRequest: function () { }, - _postProcessSavedItems: function (callBack, changedEntities, transaction, returnTransaction) { - if (this.ChangeCollector && this.ChangeCollector instanceof $data.Notifications.ChangeCollectorBase) - this.ChangeCollector.processChangedData(changedEntities); - - var eventData = {}; - var ctx = this; - //changedEntities.forEach(function (entity) { - for (var i = 0; i < changedEntities.length; i++) { - var entity = changedEntities[i]; - - if (!entity.data.storeToken) - entity.data.storeToken = ctx.storeToken; - - //type after events with items - this.processEntityTypeAfterEventHandler(entity); - - var oes = entity.data.entityState; - - entity.data.entityState = $data.EntityState.Unchanged; - entity.data.changedProperties = []; - entity.physicalData = undefined; - - var n = entity.entitySet.elementType.name; - var es = ctx._entitySetReferences[n]; - - - var eventName = undefined; - switch (oes) { - case $data.EntityState.Added: - eventName = 'added'; - break; - case $data.EntityState.Deleted: - eventName = 'deleted'; - break; - case $data.EntityState.Modified: - eventName = 'updated'; - break; - } - if (eventName) { - this.raiseEvent(eventName, entity); - } - - if (es.afterCreate || es.afterUpdate || es.afterDelete) { - if (!eventData[n]) eventData[n] = {}; - - switch (oes) { - case $data.EntityState.Added: - if (es.afterCreate) { - if (!eventData[n].createAll) eventData[n].createAll = []; - eventData[n].createAll.push(entity); - } - break; - case $data.EntityState.Modified: - if (es.afterUpdate) { - if (!eventData[n].modifyAll) eventData[n].modifyAll = []; - eventData[n].modifyAll.push(entity); - } - break; - case $data.EntityState.Deleted: - if (es.afterDelete) { - if (!eventData[n].deleteAll) eventData[n].deleteAll = []; - eventData[n].deleteAll.push(entity); - } - break; - } - } - } - //}); - - var ies = Object.getOwnPropertyNames(eventData); - var i = 0; - var ctx = this; - var cmd = ['afterUpdate', 'afterDelete', 'afterCreate']; - var cmdAll = { - afterCreate: 'createAll', - afterDelete: 'deleteAll', - afterUpdate: 'modifyAll' - }; - - var readyFn = function () { - if (!ctx.trackChanges) { - ctx.stateManager.reset(); - } - - ctx._applyTransaction(callBack, callBack.success, [changedEntities.length], transaction, returnTransaction); - - /*if (returnTransaction) - callBack.success(changedEntities.length, transaction); - else - callBack.success(changedEntities.length);*/ - }; - - var callbackFn = function () { - var es = ctx._entitySetReferences[ies[i]]; - var c = cmd.pop(); - var ed = eventData[ies[i]]; - var all = ed[cmdAll[c]]; - if (all) { - var m = []; - for (var im = 0; im < all.length; im++) { - m.push(all[im].data); - } - //var m = all.map(function(it){ return it.data; }); - if (!cmd.length) { - cmd = ['afterUpdate', 'afterDelete', 'afterCreate']; - i++; - } - - var r = es[c].call(ctx, m); - if (typeof r === 'function') { - r.call(ctx, i < ies.length ? callbackFn : readyFn, m); - } else { - if (i < ies.length) callbackFn(); - else readyFn(); - } - } else { - if (!cmd.length) { - cmd = ['afterUpdate', 'afterDelete', 'afterCreate']; - i++; - } - - if (i < ies.length) callbackFn(); - else readyFn(); - } - }; - - if (i < ies.length) callbackFn(); - else readyFn(); - }, - forEachEntitySet: function (fn, ctx) { - /// - /// Iterates over the entity sets' of current EntityContext. - /// - /// - /// - /// - /// 'this' argument for the 'fn' function. - for (var entitySetName in this._entitySetReferences) { - var actualEntitySet = this._entitySetReferences[entitySetName]; - fn.call(ctx, actualEntitySet); - } - }, - - loadItemProperty: function (entity, property, callback, transaction) { - /// - /// Loads a property of the entity through the storage provider. - /// Entity object - /// Property name - /// - /// C allback function - /// - /// - /// - /// - /// - /// Loads a property of the entity through the storage provider. - /// Entity object - /// Property name - /// - /// Success and error callbacks definition. - /// Example: [code]{ success: function(db) { .. }, error: function() { .. } }[/code] - /// - /// - /// - /// - /// Loads a property of the entity through the storage provider. - /// Entity object - /// Property definition - /// - /// Callback function - /// - /// - /// - /// - /// - /// Loads a property of the entity through the storage provider. - /// Entity object - /// Property definition - /// - /// Success and error callbacks definition. - /// Example: [code]{ success: function(db) { .. }, error: function() { .. } }[/code] - /// - /// - /// - Guard.requireType('entity', entity, $data.Entity); - - var memberDefinition = typeof property === 'string' ? entity.getType().memberDefinitions.getMember(property) : property; - var returnTransaction = this._isReturnTransaction(transaction); - - if (entity[memberDefinition.name] != undefined) { - - var pHandler = new $data.PromiseHandler(); - callBack = pHandler.createCallback(callback); - this._applyTransaction(callback, callback.success, [entity[memberDefinition.name]], transaction, returnTransaction); - /*if (returnTransaction) - callback.success(entity[memberDefinition.name], transaction); - else - callback.success(entity[memberDefinition.name]);*/ - - return pHandler.getPromise(); - } - - var isSingleSide = true; - var storageModel = this._storageModel.getStorageModel(entity.getType().fullName); - var elementType = Container.resolveType(memberDefinition.dataType); - if (elementType === $data.Array || (elementType.isAssignableTo && elementType.isAssignableTo($data.EntitySet))) { - elementType = Container.resolveType(memberDefinition.elementType); - - isSingleSide = false; - - } else { - var associations; - for (var i = 0; i < storageModel.Associations.length; i++) { - var assoc = storageModel.Associations[i]; - if (assoc.FromPropertyName == memberDefinition.name) { - associations = assoc; - break; - } - } - //var associations = storageModel.Associations.filter(function (assoc) { return assoc.FromPropertyName == memberDefinition.name; })[0]; - if (associations && associations.FromMultiplicity === "0..1" && associations.ToMultiplicity === "1") - isSingleSide = false; - } - - var keyProp = storageModel.LogicalType.memberDefinitions.getKeyProperties(); - if (isSingleSide === true) { - //singleSide - - var filterFunc = "function (e) { return"; - var filterParams = {}; - //storageModel.LogicalType.memberDefinitions.getKeyProperties().forEach(function (memDefKey, index) { - for (var index = 0; index < keyProp.length; index++) { - var memDefKey = keyProp[index]; - - if (index > 0) - filterFunc += ' &&'; - filterFunc += " e." + memDefKey.name + " == this.key" + index; - filterParams['key' + index] = entity[memDefKey.name]; - } - //}); - filterFunc += "; }" - - var entitySet = this.getEntitySetFromElementType(entity.getType()); - return entitySet - .map('function (e) { return e.' + memberDefinition.name + ' }') - .single(filterFunc, filterParams, callback, transaction); - } else { - //multipleSide - - var filterFunc = "function (e) { return" - var filterParams = {}; - //storageModel.LogicalType.memberDefinitions.getKeyProperties().forEach(function (memDefKey, index) { - for (var index = 0; index < keyProp.length; index++) { - var memDefKey = keyProp[index]; - - if (index > 0) - filterFunc += ' &&'; - filterFunc += " e." + memberDefinition.inverseProperty + "." + memDefKey.name + " == this.key" + index; - filterParams['key' + index] = entity[memDefKey.name]; - } - //}); - filterFunc += "; }" - - var entitySet = this.getEntitySetFromElementType(elementType); - return entitySet - .filter(filterFunc, filterParams) - .toArray(callback, transaction); - } - - }, - - getTraceString: function (queryable) { - /// - /// Returns a trace string. Used for debugging purposes! - /// - /// - /// Trace string - var query = new $data.Query(queryable.expression, queryable.defaultType, this); - return this.storageProvider.getTraceString(query); - }, - log: function (logInfo) { - //noop as do nothing - }, - - resolveBinaryOperator: function (operator, expression, frameType) { - return this.storageProvider.resolveBinaryOperator(operator, expression, frameType); - }, - resolveUnaryOperator: function (operator, expression, frameType) { - return this.storageProvider.resolveUnaryOperator(operator, expression, frameType); - }, - resolveFieldOperation: function (operation, expression, frameType) { - return this.storageProvider.resolveFieldOperation(operation, expression, frameType); - }, - resolveSetOperations: function (operation, expression, frameType) { - return this.storageProvider.resolveSetOperations(operation, expression, frameType); - }, - resolveTypeOperations: function (operation, expression, frameType) { - return this.storageProvider.resolveTypeOperations(operation, expression, frameType); - }, - resolveContextOperations: function (operation, expression, frameType) { - return this.storageProvider.resolveContextOperations(operation, expression, frameType); - }, - - _generateServiceOperationQueryable: function (functionName, returnEntitySet, arg, parameters) { - if (typeof console !== 'undefined' && console.log) - console.log('Obsolate: _generateServiceOperationQueryable, $data.EntityContext'); - - var params = []; - for (var i = 0; i < parameters.length; i++) { - var obj = {}; - obj[parameters[i]] = Container.resolveType(Container.getTypeName(arg[i])); - params.push(obj); - } - - var tempOperation = $data.EntityContext.generateServiceOperation({ serviceName: functionName, returnType: $data.Queryable, elementType: this[returnEntitySet].elementType, params: params }); - return tempOperation.apply(this, arg); - }, - attach: function (entity, mode) { - /// - /// Attaches an entity to its matching entity set. - /// - /// - /// Returns the attached entity. - - if (entity instanceof $data.EntityWrapper) { - entity = entity.getEntity(); - } - var entitySet = this.getEntitySetFromElementType(entity.getType()); - return entitySet.attach(entity, mode); - }, - attachOrGet: function (entity, mode) { - /// - /// Attaches an entity to its matching entity set, or returns if it's already attached. - /// - /// - /// Returns the entity. - - if (entity instanceof $data.EntityWrapper) { - entity = entity.getEntity(); - } - var entitySet = this.getEntitySetFromElementType(entity.getType()); - return entitySet.attachOrGet(entity, mode); - }, - - addMany: function (entities) { - /// - /// Adds several entities to their matching entity set. - /// - /// - /// Returns the added entities. - var self = this; - entities.forEach(function (entity) { - self.add(entity); - }); - return entities; - }, - - add: function (entity) { - /// - /// Adds a new entity to its matching entity set. - /// - /// - /// Returns the added entity. - - if (entity instanceof $data.EntityWrapper) { - entity = entity.getEntity(); - } - var entitySet = this.getEntitySetFromElementType(entity.getType()); - return entitySet.add(entity); - }, - remove: function (entity) { - /// - /// Removes an entity from its matching entity set. - /// - /// - /// Returns the removed entity. - - if (entity instanceof $data.EntityWrapper) { - entity = entity.getEntity(); - } - var entitySet = this.getEntitySetFromElementType(entity.getType()); - return entitySet.remove(entity); - }, - storeToken: { type: Object }, - - getFieldUrl: function (entity, member, collection) { - try { - var entitySet = typeof collection === 'string' ? this[collection] : collection; - var fieldName = typeof member === 'string' ? member : member.name; - if (entity instanceof $data.Entity) { - entitySet = this.getEntitySetFromElementType(entity.getType()); - } else if (!Object.isNullOrUndefined(entity) && entity.constructor !== $data.Object) { //just a single key - var keyDef = entitySet.elementType.memberDefinitions.getKeyProperties()[0]; - var key = {}; - key[keyDef.name] = entity; - entity = key; - } - - //key object - if (!(entity instanceof $data.Entity)) { - entity = new entitySet.elementType(entity); - } - - return this.storageProvider.getFieldUrl(entity, fieldName, entitySet); - } catch (e) {} - return '#'; - } -}, { - inheritedTypeProcessor: function(type) { - if (type.resolveForwardDeclarations) { - type.resolveForwardDeclarations(); - } - }, - generateServiceOperation: function (cfg) { - - var fn; - if (cfg.serviceMethod) { - var returnType = cfg.returnType ? Container.resolveType(cfg.returnType) : {}; - if (returnType.isAssignableTo && returnType.isAssignableTo($data.Queryable)) { - fn = cfg.serviceMethod; - } else { - fn = function () { - var lastParam = arguments[arguments.length - 1]; - - var pHandler = new $data.PromiseHandler(); - var cbWrapper; - - var args = arguments; - if (typeof lastParam === 'function') { - cbWrapper = pHandler.createCallback(lastParam); - arguments[arguments.length - 1] = cbWrapper; - } else { - cbWrapper = pHandler.createCallback(); - arguments.push(cbWrapper); - } - - try { - var result = cfg.serviceMethod.apply(this, arguments); - if (result !== undefined) - cbWrapper.success(result); - } catch (e) { - cbWrapper.error(e); - } - - return pHandler.getPromise(); - } - } - - } else { - fn = function () { - var context = this; - - var boundItem; - if (this instanceof $data.Entity) { - if (!cfg.method) { - cfg.method = 'POST'; - } - - if (this.context) { - context = this.context; - } else { - Guard.raise('entity not attached into context'); - return; - } - - boundItem = { - data: this, - entitySet: context.getEntitySetFromElementType(this.getType()) - }; - } - - var virtualEntitySet = cfg.elementType ? context.getEntitySetFromElementType(Container.resolveType(cfg.elementType)) : null; - - var paramConstExpression = null; - if (cfg.params) { - paramConstExpression = []; - for (var i = 0; i < cfg.params.length; i++) { - //TODO: check params type - for (var name in cfg.params[i]) { - paramConstExpression.push(Container.createConstantExpression(arguments[i], Container.resolveType(cfg.params[i][name]), name)); - } - } - } - - var ec = Container.createEntityContextExpression(context); - var memberdef = (boundItem ? boundItem.data : context).getType().getMemberDefinition(cfg.serviceName); - var es = Container.createServiceOperationExpression(ec, - Container.createMemberInfoExpression(memberdef), - paramConstExpression, - cfg, - boundItem); - - //Get callback function - var clb = arguments[arguments.length - 1]; - if (typeof clb !== 'function') { - clb = undefined; - } - - if (virtualEntitySet) { - var q = Container.createQueryable(virtualEntitySet, es); - if (clb) { - es.isTerminated = true; - return q._runQuery(clb); - } - return q; - } - else { - var returnType = cfg.returnType ? Container.resolveType(cfg.returnType) : null; - - var q = Container.createQueryable(context, es); - q.defaultType = returnType || $data.Object; - - if (returnType === $data.Queryable) { - q.defaultType = Container.resolveType(cfg.elementType); - if (clb) { - es.isTerminated = true; - return q._runQuery(clb); - } - return q; - } - es.isTerminated = true; - return q._runQuery(clb); - } - }; - }; - - var params = []; - if (cfg.params) { - for (var i = 0; i < cfg.params.length; i++) { - var param = cfg.params[i]; - for (var name in param) { - params.push({ - name: name, - type: param[name] - }); - } - } - } - $data.typeSystem.extend(fn, cfg, { params: params }); - - return fn; - }, - _convertLogicalTypeNameToPhysical: function (name) { - return name + '_$db$'; - }, - _storageModelCache: { - get: function () { - if (!this.__storageModelCache) - this.__storageModelCache = {}; - return this.__storageModelCache; - }, - set: function () { - //todo exception - } - } -}); -$data.Class.define('$data.QueryProvider', null, null, -{ - //TODO: instance member????? - constructor: function () { this.requiresExpressions= false }, - executeQuery: function (queryable, resultHandler) { - }, - getTraceString: function (queryable) { - } -}, null);$data.Class.define('$data.ModelBinder', null, null, { - - constructor: function (context) { - this.context = context; - this.providerName = null; - if (this.context.storageProvider && typeof this.context.storageProvider.getType === 'function') { - this.references = !(this.context.storageProvider.providerConfiguration.modelBinderOptimization || false); - for (var i in $data.RegisteredStorageProviders) { - if ($data.RegisteredStorageProviders[i] === this.context.storageProvider.getType()) { - this.providerName = i; - } - } - } - }, - - _deepExtend: function (o, r) { - if (o === null || o === undefined) { - return r; - } - for (var i in r) { - if (o.hasOwnProperty(i)) { - if (typeof r[i] === 'object') { - if (Array.isArray(r[i])) { - for (var j = 0; j < r[i].length; j++) { - if (o[i].indexOf(r[i][j]) < 0) { - o[i].push(r[i][j]); - } - } - } else this._deepExtend(o[i], r[i]); - } - } else { - o[i] = r[i]; - } - } - return this._finalize(o); - }, - - _finalize: function(o){ - if (o instanceof $data.Entity) { - o.changedProperties = undefined; - o.storeToken = this.context.storeToken; - } - return o; - }, - - _buildSelector: function (meta, context) { - if (meta.$selector) { - if (!(Array.isArray(meta.$selector))) { - meta.$selector = [meta.$selector]; - } - - for (var i = 0; i < meta.$selector.length; i++) { - var selector = meta.$selector[i].replace('json:', ''); - context.src += 'if('; - var path = selector.split('.'); - for (var j = 0; j < path.length; j++) { - context.src += 'di.' + path.slice(0, j + 1).join('.') + (j < path.length - 1 ? ' && ' : ' !== undefined && typeof di.' + selector + ' === "object"'); - } - context.src += '){di = di.' + selector + ';}' + (i < meta.$selector.length - 1 ? 'else ' : ''); - } - - context.src += 'if (di === null){'; - if (context.iter) context.src += context.iter + ' = null;'; - context.src += 'return null;'; - context.src += '}'; - } - }, - - _buildKey: function (name, type, keys, context, data) { - if (keys) { - var type = Container.resolveType(type); - var typeIndex = Container.getIndex(type); - type = type.fullName || type.name; - context.src += 'var ' + name + 'Fn = function(di){'; - if (!(Array.isArray(keys)) || keys.length == 1) { - if (typeof keys !== 'string') keys = keys[0]; - context.src += 'if (typeof di.' + keys + ' === "undefined") return undefined;'; - context.src += 'if (di.' + keys + ' === null) return null;'; - context.src += 'var key = ("' + type + '_' + typeIndex + '_' + keys + '#" + di.' + keys + ');'; - } else { - context.src += 'var key = "";'; - for (var i = 0; i < keys.length; i++) { - var id = typeof keys[i] !== 'object' ? keys[i] : keys[i].$source; - context.src += 'if (typeof di.' + id + ' === "undefined") return undefined;'; - context.src += 'if (di.' + id + ' === null) return null;'; - context.src += 'key += ("' + type + '_' + typeIndex + '_' + id + '#" + di.' + id + ');'; - } - } - - context.src += 'return key;};'; - } - - context.src += 'var ' + name + ' = ' + (keys ? name + 'Fn(' + (data || 'di') + ')' : 'undefined') + ';'; - }, - - build: function (meta, context) { - if (meta.$selector) { - if (!(Array.isArray(meta.$selector))) meta.$selector = [meta.$selector]; - for (var i = 0; i < meta.$selector.length; i++) { - meta.$selector[i] = meta.$selector[i].replace('json:', ''); - } - } - - if (meta.$value) { - if (typeof meta.$value === 'function') { - context.src += 'var di = di || data;'; - context.src += 'var fn = function(){ return meta' + (context.meta.length ? '.' + context.meta.join('.') : '') + '.$value.call(self, meta' + (context.meta.length ? '.' + context.meta.join('.') : '') + ', di); };'; - if (meta.$type) { - var type = Container.resolveName(Container.resolveType(meta.$type)); - var typeIndex = Container.getIndex(Container.resolveType(meta.$type)); - var converter = this.context.storageProvider.fieldConverter.fromDb[type]; - if (converter) { - context.item = 'self.context.storageProvider.fieldConverter.fromDb["' + type + '"](fn())'; - } else { - context.item = 'new (Container.resolveByIndex(' + typeIndex + '))(fn())'; - } - } else context.item = 'fn()'; - } else if (meta.$type) { - var type = Container.resolveName(Container.resolveType(meta.$type)); - var typeIndex = Container.getIndex(Container.resolveType(meta.$type)); - var converter = this.context.storageProvider.fieldConverter.fromDb[type]; - if (converter) { - context.item = 'self.context.storageProvider.fieldConverter.fromDb["' + type + '"](' + meta.$value + ')'; - } else { - context.item = 'new (Container.resolveByIndex(' + typeIndex + '))(' + meta.$value + ')'; - } - } else context.item = meta.$value; - } else if (meta.$source) { - var type = Container.resolveName(Container.resolveType(meta.$type)); - var typeIndex = Container.getIndex(Container.resolveType(meta.$type)); - var converter = this.context.storageProvider.fieldConverter.fromDb[type]; - var item = '_' + type.replace(/\./gi, '_') + '_'; - if (!context.forEach) context.src += 'var di = data;'; - context.item = item; - this._buildSelector(meta, context); - if (converter) { - context.src += 'var ' + item + ' = self.context.storageProvider.fieldConverter.fromDb["' + type + '"](di.' + meta.$source + ');'; - } else { - context.src += 'var ' + item + ' = new (Container.resolveByIndex(' + typeIndex + '))(di.' + meta.$source + ');'; - } - } else if (meta.$item) { - context.meta.push('$item'); - var iter = (context.item && context.current ? context.item + '.' + context.current : (context.item ? context.item : 'result')); - context.iter = iter; - if (iter.indexOf('.') < 0) context.src += 'var ' + iter + ';'; - context.src += 'var fn = function(di){'; - if (meta.$selector) { - context.src += 'if (typeof di !== "undefined" && !(Array.isArray(di))){'; - this._buildSelector(meta, context); - context.src += '}'; - } - if (this.references && meta.$keys) this._buildKey('forKey', meta.$type, meta.$keys, context); - //else if (this.references && meta.$item && meta.$item.$keys) this._buildKey('forKey', meta.$type, meta.$item.$keys, context); - //else context.src += 'var forKey = typeof itemKey !== "undefined" ? itemKey : undefined;'; - /*context.src += 'if (typeof forKey !== "undefined" && forKey){'; - context.src += 'if (cache[forKey]){'; - context.src += iter + ' = cache[forKey];'; - context.src += '}else{'; - context.src += iter + ' = [];'; - context.src += 'cache[forKey] = ' + iter + ';'; - context.src += '}'; - context.src += '}else{'; - context.src += iter + ' = [];'; - context.src += '}';*/ - context.src += iter + ' = typeof ' + iter + ' == "undefined" ? [] : ' + iter + ';'; - //context.src += iter + ' = [];'; - if (this.references && meta.$item.$keys) { - var keycacheName = 'keycache_' + iter.replace(/\./gi, '_'); - context.src += 'var ' + keycacheName + ';'; - context.src += 'var kci = keycacheIter.indexOf(' + iter + ');'; - context.src += 'if (kci < 0){'; - context.src += keycacheName + ' = [];'; - context.src += 'keycache.push(' + keycacheName + ');'; - context.src += 'keycacheIter.push(' + iter + ');'; - context.src += '}else{'; - context.src += keycacheName + ' = keycache[kci];'; - context.src += '}'; - //context.src += 'var ' + keycacheName + ' = ' + (meta.$item.$keys ? '[]' : 'null') + ';'; - } - context.iter = undefined; - context.forEach = true; - var itemForKey = 'itemForKey_' + iter.replace(/\./gi, '_'); - context.src += 'var forEachFn = function(di, i){'; - context.src += 'var diBackup = di;'; - if (this.providerName == "sqLite" && this.references && meta.$item.$keys) this._buildKey(itemForKey, meta.$type, meta.$item.$keys, context); - var item = context.item || 'iter'; - context.item = item; - if (!meta.$item.$source) { - this._buildSelector(meta.$item, context); - } - this.build(meta.$item, context); - if (this.references && meta.$keys) { - context.src += 'if (forKey){'; - context.src += 'if (cache[forKey]){'; - context.src += iter + ' = cache[forKey];'; - context.src += 'if (' + iter + '.indexOf(' + (context.item || item) + ') < 0){'; - context.src += iter + '.push(' + (context.item || item) + ');'; - context.src += '}}else{'; - context.src += 'cache[forKey] = ' + iter + ';'; - context.src += iter + '.push(' + (context.item || item) + ');'; - context.src += '}}else{'; - if (this.references && meta.$item.$keys) this._buildKey('cacheKey', meta.$type, meta.$item.$keys, context, 'diBackup'); - context.src += 'if (typeof cacheKey != "undefined" && cacheKey !== null){'; - context.src += 'if (keycache_' + iter.replace(/\./gi, '_') + ' && cacheKey){'; - context.src += 'if (keycache_' + iter.replace(/\./gi, '_') + '.indexOf(cacheKey) < 0){'; - context.src += iter + '.push(' + (context.item || item) + ');'; - context.src += 'keycache_' + iter.replace(/\./gi, '_') + '.push(cacheKey);'; - context.src += '}'; - context.src += '}else{'; - context.src += iter + '.push(' + (context.item || item) + ');'; - context.src += '}'; - context.src += '}'; - context.src += '}'; - } else { - if (this.references && meta.$item.$keys) { - context.src += 'if (typeof ' + itemForKey + ' !== "undefined" && ' + itemForKey + ' !== null){'; - context.src += 'if (typeof keycache_' + iter.replace(/\./gi, '_') + ' !== "undefined" && ' + itemForKey + '){'; - context.src += 'if (keycache_' + iter.replace(/\./gi, '_') + '.indexOf(' + itemForKey + ') < 0){'; - context.src += iter + '.push(' + (context.item || item) + ');'; - context.src += 'keycache_' + iter.replace(/\./gi, '_') + '.push(' + itemForKey + ');' - context.src += '}}else{'; - context.src += iter + '.push(' + (context.item || item) + ');'; - context.src += '}}else{'; - context.src += iter + '.push(' + (context.item || item) + ');'; - context.src += '}'; - /*context.src += 'if (typeof itemKey !== "undefined" && itemKey !== null){'; - context.src += 'if (typeof keycache_' + iter.replace(/\./gi, '_') + ' !== "undefined" && itemKey){'; - context.src += 'if (keycache_' + iter.replace(/\./gi, '_') + '.indexOf(itemKey) < 0){'; - context.src += iter + '.push(' + (context.item || item) + ');'; - context.src += 'keycache_' + iter.replace(/\./gi, '_') + '.push(itemKey);' - context.src += '}}else{'; - context.src += iter + '.push(' + (context.item || item) + ');'; - context.src += '}}else{'; - context.src += iter + '.push(' + (context.item || item) + ');'; - context.src += '}';*/ - } else { - context.src += iter + '.push(' + (context.item || item) + ');'; - } - } - context.src += '};'; - context.src += 'if (Array.isArray(di)) di.forEach(forEachFn);'; - context.src += 'else forEachFn(di, 0);'; - context.forEach = false; - context.item = null; - context.src += '};fn(typeof di === "undefined" ? data : di);' - context.meta.pop(); - } else if (meta.$type) { - if (!context.forEach) { - context.src += 'if (typeof di === "undefined"){'; - context.src += 'var di = data;'; - this._buildSelector(meta, context); - context.src += '}'; - } - var resolvedType = Container.resolveType(meta.$type); - var type = Container.resolveName(resolvedType); - var typeIndex = Container.getIndex(resolvedType); - var isEntityType = resolvedType.isAssignableTo && resolvedType.isAssignableTo($data.Entity); - var item = '_' + type.replace(/\./gi, '_') + '_'; - if (context.item == item) item += 'new_'; - context.item = item; - - - var isPrimitive = false; - if (!meta.$source && !meta.$value && resolvedType !== $data.Array && resolvedType !== $data.Object && !resolvedType.isAssignableTo) - isPrimitive = true; - if (resolvedType === $data.Object || resolvedType === $data.Array) { - var keys = Object.keys(meta); - if (keys.length == 1 || (keys.length == 2 && meta.$selector)) isPrimitive = true; - } - - if (isPrimitive) { - var converter = this.context.storageProvider.fieldConverter.fromDb[type]; - if (converter) { - context.src += 'var ' + item + ' = di != undefined ? self.context.storageProvider.fieldConverter.fromDb["' + type + '"](di) : di;'; - } else { - context.src += 'var ' + item + ' = di;'; - } - } else { - if (this.references && meta.$keys) { - this._buildKey('itemKey', meta.$type, meta.$keys, context); - context.src += 'if (itemKey === null) return null;'; - context.src += 'var ' + item + ';'; - context.src += 'if (itemKey && cache[itemKey]){'; - context.src += item + ' = cache[itemKey];'; - context.src += '}else{'; - if (isEntityType) { - context.src += item + ' = new (Container.resolveByIndex(' + typeIndex + '))(undefined, { setDefaultValues: false });'; - } else { - context.src += item + ' = new (Container.resolveByIndex(' + typeIndex + '))();'; - } - context.src += 'if (itemKey){'; - context.src += 'cache[itemKey] = ' + item + ';'; - context.src += '}'; - context.src += '}'; - } else { - if (isEntityType) { - context.src += 'var ' + item + ' = new (Container.resolveByIndex(' + typeIndex + '))(undefined, { setDefaultValues: false });'; - } else { - context.src += 'var ' + item + ' = new (Container.resolveByIndex(' + typeIndex + '))();'; - } - } - } - for (var i in meta) { - if (i.indexOf('$') < 0) { - context.current = i; - if (!meta[i].$item) { - if (meta[i].$value) { - context.meta.push(i); - var item = context.item; - this.build(meta[i], context); - context.src += item + '.' + i + ' = ' + context.item + ';'; - context.item = item; - context.meta.pop(); - } else if (meta[i].$source) { - context.src += 'var fn = function(di){'; - this._buildSelector(meta[i], context); - if (meta[i].$type) { - var type = Container.resolveName(Container.resolveType(meta[i].$type)); - var typeIndex = Container.getIndex(Container.resolveType(meta[i].$type)); - var converter = this.context.storageProvider.fieldConverter.fromDb[type]; - if (converter) { - context.src += 'return self.context.storageProvider.fieldConverter.fromDb["' + type + '"](di.' + meta[i].$source + ');'; - } else { - context.src += 'return new (Container.resolveByIndex(' + typeIndex + '))(di.' + meta[i].$source + ');'; - } - } else { - context.src += item + '.' + i + ' = di.' + meta[i].$source + ';'; - } - context.src += '};'; - if (meta[i].$type) context.src += item + '.' + i + ' = fn(di);'; - else context.src += 'fn(di);'; - } else if (meta[i].$type) { - context.meta.push(i); - context.src += 'var fn = function(di){'; - this._buildSelector(meta[i], context); - this.build(meta[i], context); - context.src += 'return ' + context.item + ';};'; - if (meta[i].$type === $data.Object) context.src += item + '.' + i + ' = self._deepExtend(' + item + '.' + i + ', fn(di));'; - else context.src += item + '.' + i + ' = fn(di);'; - context.item = item; - context.meta.pop(); - } else if (meta.$type) { - var memDef = Container.resolveType(meta.$type).memberDefinitions.getMember(i); - var type = Container.resolveName(memDef.type); - var entityType = Container.resolveType(meta.$type); - var entityTypeIndex = Container.getIndex(meta.$type); - var converter = this.context.storageProvider.fieldConverter.fromDb[type]; - if (this.providerName && memDef && memDef.converter && memDef.converter[this.providerName] && typeof memDef.converter[this.providerName].fromDb == 'function') { - context.src += item + '.' + i + ' = Container.resolveByIndex("' + entityTypeIndex + '").memberDefinitions.getMember("' + i + '").converter.' + this.providerName + '.fromDb(di.' + meta[i] + ', Container.resolveByIndex("' + entityTypeIndex + '").memberDefinitions.getMember("' + i + '"), self.context, Container.resolveByIndex("' + entityTypeIndex + '"));'; - } else if (converter) { - context.src += item + '.' + i + ' = self.context.storageProvider.fieldConverter.fromDb["' + type + '"](di.' + meta[i] + ');'; - } else { - //var type = Container.resolveName(Container.resolveType(type.memberDefinitions.getMember(i).type)); - var typeIndex = Container.getIndex(Container.resolveType(type.memberDefinitions.getMember(i).type)); - context.src += item + '.' + i + ' = new (Container.resolveByIndex(' + typeIndex + '))(di.' + meta[i] + ');'; - } - } - } else { - context.meta.push(i); - this.build(meta[i], context); - context.item = item; - context.meta.pop(); - } - } - } - context.src += item + ' = self._finalize(' + item + ');'; - } - }, - - call: function (data, meta) { - if (!Object.getOwnPropertyNames(meta).length) { - return data; - } - var context = { - src: '', - meta: [] - }; - context.src += 'var self = this;'; - context.src += 'var result;'; - context.src += 'var cache = {};'; - context.src += 'var keycache = [];'; - context.src += 'var keycacheIter = [];'; - this.build(meta, context); - if (context.item) context.src += 'if (typeof result === "undefined") result = ' + context.item + ';'; - context.src += 'return result;'; - - /*var beautify = require('beautifyjs'); - console.log(beautify.js_beautify(context.src));*/ - - var fn = new Function('meta', 'data', context.src).bind(this); - var ret = fn(meta, data); - return ret; - } -}); -$C('$data.queryBuilder', null, null, { - constructor: function () { - this._fragments = {}; - this.selectedFragment = null; - this._binderConfig = {}; - this.modelBinderConfig = this._binderConfig; - this._binderConfigPropertyStack = []; - }, - selectTextPart: function (name) { - if (!this._fragments[name]) { - this._fragments[name] = { text: '', params: [] }; - } - this.selectedFragment = this._fragments[name]; - }, - getTextPart: function (name) { - return this._fragments[name]; - }, - addText: function (textParticle) { - this.selectedFragment.text += textParticle; - }, - addParameter: function (param) { - this.selectedFragment.params.push(param); - }, - selectModelBinderProperty: function (name) { - this._binderConfigPropertyStack.push(this.modelBinderConfig); - if (!(name in this.modelBinderConfig)) { - this.modelBinderConfig[name] = {}; - } - this.modelBinderConfig = this.modelBinderConfig[name]; - }, - popModelBinderProperty: function () { - if (this._binderConfigPropertyStack.length === 0) { - this.modelBinderConfig = this._binderConfig(); - } else { - this.modelBinderConfig = this._binderConfigPropertyStack.pop(); - } - }, - resetModelBinderProperty: function (name) { - this._binderConfigPropertyStack = []; - this.modelBinderConfig = this._binderConfig; - }, - addKeyField: function (name) { - if(!this.modelBinderConfig['$keys']){ - this.modelBinderConfig['$keys'] = new Array(); - } - this.modelBinderConfig['$keys'].push(name); - } -}); -$C('$data.Query', null, null, -{ - constructor: function (expression, defaultType, context) { - /// - /// - /// - - this.expression = expression; - this.context = context; - - //TODO: expressions get as JSON string?! - - this.expressions = expression; - this.defaultType = defaultType; - this.result = []; - this.rawDataList = []; - this.modelBinderConfig = {}; - this.context = context; - }, - - rawDataList: { dataType: "Array" }, - result: { dataType: "Array" }, - resultType: {}, - buildResultSet: function (ctx) { - var converter = new $data.ModelBinder(this.context); - this.result = converter.call(this.rawDataList, this.modelBinderConfig); - return; - }, - getEntitySets: function(){ - var ret = []; - var ctx = this.context; - - var fn = function(expression){ - if (expression instanceof $data.Expressions.EntitySetExpression){ - if (ret.indexOf(ctx._entitySetReferences[expression.elementType.name]) < 0) - ret.push(ctx._entitySetReferences[expression.elementType.name]); - } - if (expression.source) fn(expression.source); - }; - - fn(this.expression); - - return ret; - } -}, null); -$data.Class.define('$data.Queryable', null, null, -{ - constructor: function (source, rootExpression) { - /// - /// Provides a base class for classes supporting JavaScript Language Query. - /// Provides a base class for classes supporting JavaScript Language Query. - /// - /// - /// - /// - /// Provides a base class for classes supporting JavaScript Language Query. - /// Provides a base class for classes supporting JavaScript Language Query. - /// - /// - /// - - var context = source instanceof $data.EntityContext ? source : source.entityContext; - this.defaultType = source instanceof $data.EntityContext ? null : source.defaultType; - this.entityContext = context; - this.expression = rootExpression; - }, - - filter: function (predicate, thisArg) { - ///Filters a set of entities using a boolean expression. - ///A boolean query expression - ///The query parameters - /// - /// - ///Filters a set of entities using a boolean expression formulated as string. - /// - ///The expression body of the predicate function in string. - ///To reference the lambda parameter use the 'it' context variable. - ///Example: filter("it.Title == 'Hello'") - /// - /// - /// - /// - /// - ///Filters a set of entities using a bool expression formulated as a JavaScript function. - /// - /// - /// - ///Contains the predicate parameters - /// - /// - /// - ///Filtering a set of entities with a predicate function - ///var males = Persons.filter( function( person ) { return person.Gender == 'Male' } ); - /// - /// - ///Filtering a set of entities with a predicate function and parameters - ///var draftables = Persons.filter( function( person ) { - /// return person.Gender == this.gender && person.Age > this.age - /// }, { gender: 'Male', age: 21 }); - /// - /// - ///Filtering a set of entities with a predicate as a string and parameters - ///var draftables = Persons.filter("it.Gender == this.gender && it.Age > this.age", - /// { gender: 'Male', age: 21 }); - /// - /// - if (arguments.length === 3) { - predicate = "it." + arguments[0] + - (arguments[1][0] === "." ? (arguments[1] + "(param)") : (" " + arguments[1] + " param")); - thisArg = { param : arguments[2] } - } - this._checkOperation('filter'); - var expression = Container.createCodeExpression(predicate, thisArg); - var expressionSource = this.expression; - if (this.expression instanceof $data.Expressions.FilterExpression) { - expressionSource = this.expression.source; - - var operatorResolution = this.entityContext.storageProvider.resolveBinaryOperator("and"); - expression = Container.createSimpleBinaryExpression(this.expression.selector, expression, "and", "filter", "boolean", operatorResolution); - } - var exp = Container.createFilterExpression(expressionSource, expression); - var q = Container.createQueryable(this, exp); - return q; - }, - where: function (predicate, params) { - ///Where is a convenience alias for C# developers. Use filter instead. - /// - return this.filter(predicate, params); - }, - - map: function (projection, thisArg, mappedTo) { - /// Map specifies the shape or type of each returned element. You can specify whether your results will consist of complete Person objects, just one member, a subset of members, or some completely different result type based on a computation or new object creation. When map produces something other than a copy of the source element, the operation is called a projection. The use of projections to transform data is a powerful capability of JavaScript Language Query expressions. - /// A projection expression - /// The query parameters - /// - /// - /// Map specifies the shape or type of each returned element. You can specify whether your results will consist of complete Person objects, just one member, a subset of members, or some completely different result type based on a computation or new object creation. When map produces something other than a copy of the source element, the operation is called a projection. The use of projections to transform data is a powerful capability of JavaScript Language Query expressions. - /// - /// The expression body of the projection function in string. - /// To reference the lambda parameter use the 'it' context variable. - /// Example: map("{ i: it.Id, t: it.Title }") - /// - /// - /// - /// - /// - /// Map specifies the shape or type of each returned element. You can specify whether your results will consist of complete Person objects, just one member, a subset of members, or some completely different result type based on a computation or new object creation. When map produces something other than a copy of the source element, the operation is called a projection. The use of projections to transform data is a powerful capability of JavaScript Language Query expressions. - /// - /// Projection function to specify the shape or type of each returned element. - /// - /// - /// Contains the projection parameters. - /// - /// - /// - /// Projection to get an array of the full name property of a set of Person entities - /// var personFullNames = Persons.map( function( person ) { return person.FullName; } ); - /// - /// - /// Projection to get an array of the required fields of Person entities in an anonymous type. - /// var custom = Persons.map( function( person ) { - /// return { FullName: person.FullName, Info: { Address: person.Location.Address, Phone: person.Phone } }; - /// }); - /// - /// - - this._checkOperation('map'); - var codeExpression = Container.createCodeExpression(projection, thisArg); - var exp = Container.createProjectionExpression(this.expression, codeExpression); - - if (mappedTo === 'default') - exp.projectionAs = this.defaultType; - else if (mappedTo) - exp.projectionAs = Container.resolveType(mappedTo); - else - exp.projectionAs = $data.Object; - - var q = Container.createQueryable(this, exp); - return q; - }, - select: function (projection, thisArg, mappedTo) { - ///Select is a convenience alias for C# developers. Use map instead. - /// - return this.map(projection, thisArg, mappedTo); - }, - - length: function (onResult, transaction) { - /// Returns the number of entities (or projected object) in a query as the callback parameter. - /// A callback function - /// - /// - /// Returns the number of entities (or projected object) in a query as the callback parameter. - /// - /// The callback function to handle the result. - /// - /// - /// - /// - /// Returns the number of entities (or projected object) in a query as the callback parameter. - /// - /// Object of callback functions to handle success and error. - /// Example: { success: function(cnt) { ... }, error: function() { alert("Something went wrong..."); } } - /// - /// - /// - /// Get the count of Person entities. - /// Persons.length( function( cnt ) { alert("There are " + cnt + " person(s) in the database."); } ); - /// - /// - - this._checkOperation('length'); - var pHandler = new $data.PromiseHandler(); - var cbWrapper = pHandler.createCallback(onResult); - - var countExpression = Container.createCountExpression(this.expression); - var preparator = Container.createQueryExpressionCreator(this.entityContext); - try { - var expression = preparator.Visit(countExpression); - this.entityContext.log({ event: "EntityExpression", data: expression }); - - this.entityContext.executeQuery(Container.createQueryable(this, expression), cbWrapper, transaction); - } catch (e) { - cbWrapper.error(e); - } - - return pHandler.getPromise(); - }, - count: function (onResult, transaction) { - ///Count is a convenience alias for C# developers. Use length instead. - /// - return this.length(onResult, transaction); - }, - - forEach: function (iterator, transaction) { - /// Calls the iterator function for all entity (or projected object) in the query. - /// Iterator function - /// - /// - /// Calls the iterator function for all entity (or projected object) in the query. - /// - /// Iterator function to handle the result elements. - /// - /// - /// - /// Log the full name of each Person. - /// Persons.forEach( function( person ) { console.log(person.FullName; } ); - /// - /// - - this._checkOperation('forEach'); - var pHandler = new $data.PromiseHandler(); - function iteratorFunc(items) { items.forEach(iterator); } - var cbWrapper = pHandler.createCallback(iteratorFunc); - - var forEachExpression = Container.createForEachExpression(this.expression); - var preparator = Container.createQueryExpressionCreator(this.entityContext); - try { - var expression = preparator.Visit(forEachExpression); - this.entityContext.log({ event: "EntityExpression", data: expression }); - - this.entityContext.executeQuery(Container.createQueryable(this, expression), cbWrapper, transaction); - } catch (e) { - cbWrapper.error(e); - } - - return pHandler.getPromise(); - }, - - toArray: function (onResult_items, transaction) { - /// Returns the query result as the callback parameter. - /// A callback function - /// - /// - /// Returns the query result as the callback parameter. - /// - /// The callback function to handle the result. - /// - /// - /// - /// - /// Returns the query result as the callback parameter. - /// - /// Object of callback functions to handle success and error. - /// Example: { success: function(result) { ... }, error: function() { alert("Something went wrong..."); } } - /// - /// - /// - /// Get all Person entities. - /// Persons.toArray( function( result ) { console.dir(result); } ); - /// - /// - - if (onResult_items instanceof $data.Array) - { - return this.toArray(function (results) { - onResult_items.length = 0; - results.forEach(function (item, idx) { - onResult_items.push(item); - }); - }); - } - - this._checkOperation('toArray'); - var pHandler = new $data.PromiseHandler(); - var cbWrapper = pHandler.createCallback(onResult_items); - - var toArrayExpression = Container.createToArrayExpression(this.expression); - var preparator = Container.createQueryExpressionCreator(this.entityContext); - try { - var expression = preparator.Visit(toArrayExpression); - this.entityContext.log({ event: "EntityExpression", data: expression }); - - this.entityContext.executeQuery(Container.createQueryable(this, expression), cbWrapper, transaction); - } catch (e) { - cbWrapper.error(e); - } - - return pHandler.getPromise(); - }, - toLiveArray: function (onResult, transaction) { - var self = this; - var result = []; - - var doAction = function (action) { - return function (onResult) { - var pHandler = new $data.PromiseHandler(); - var callback = pHandler.createCallback(onResult); - - var successFunc = function (res) { - result.length = 0; - - var data = res; - $data.typeSystem.extend(result, data); - - result.prev = doAction(function (cb) { - data.prev(cb); - }); - result.next = doAction(function (cb) { - data.next(cb); - }); - - callback.success.apply(this, [result].concat(Array.prototype.slice.call(arguments, 1))); - } - - action({ - success: successFunc, - error: callback.error - }, transaction); - - var promise = pHandler.getPromise(); - $data.typeSystem.extend(result, promise); - - return result; - } - } - - result.refresh = doAction(function (cb) { - self.toArray(cb); - }); - - return result.refresh.apply(result, arguments); - }, - - single: function (filterPredicate, thisArg, onResult, transaction) { - /// Filters a set of entities using a boolean expression and returns a single element or throws an error if more than one element is filtered. - /// A callback function - /// - /// - /// Filters a set of entities using a boolean expression and returns a single element or throws an error if more than one element is filtered. - /// - /// Same as in filter. - /// - /// - /// The callback function to handle the result, same as in toArray. - /// - /// - /// - /// - /// Filters a set of entities using a boolean expression and returns a single element or throws an error if more than one element is filtered. - /// - /// Same as in filter. - /// - /// - /// The callback function to handle the result, same as in toArray. - /// - /// - /// - /// Get "George" from the Person entity set. - /// Persons.single( function( person ) { return person.FirstName == this.name; }, { name: "George" }, { - /// success: function ( result ){ ... }, - /// error: function () { ... } - /// }); - /// - /// - - this._checkOperation('single'); - var q = this; - if (filterPredicate) { - q = this.filter(filterPredicate, thisArg); - } - q = q.take(2); - - var pHandler = new $data.PromiseHandler(); - var cbWrapper = pHandler.createCallback(onResult); - - var singleExpression = Container.createSingleExpression(q.expression); - var preparator = Container.createQueryExpressionCreator(q.entityContext); - try { - var expression = preparator.Visit(singleExpression); - this.entityContext.log({ event: "EntityExpression", data: expression }); - - q.entityContext.executeQuery(Container.createQueryable(q, expression), cbWrapper, transaction); - } catch (e) { - cbWrapper.error(e); - } - - return pHandler.getPromise(); - }, - - some: function (filterPredicate, thisArg, onResult, transaction) { - /// Filters a set of entities using a boolean expression and returns true if the query has any result element. - /// Filter function - /// The query parameters for filter function - /// A callback function - /// - /// - /// Filters a set of entities using a boolean expression and returns true if the query has any result element. - /// - /// Same as in filter. - /// - /// - /// The callback function to handle the result, same as in toArray. - /// - /// - /// - /// - /// Filters a set of entities using a boolean expression and returns true if the query has any result element. - /// - /// Same as in filter. - /// - /// - /// The callback function to handle the result, same as in toArray. - /// - /// - /// - /// Is there any person who's first name is "George"? - /// Persons.some( function( person ) { return person.FirstName == this.name; }, { name: "George" }, { - /// success: function ( result ){ ... }, - /// error: function () { ... } - /// }); - /// - /// - - this._checkOperation('some'); - var q = this; - if (filterPredicate) { - q = this.filter(filterPredicate, thisArg); - } - q = q.take(1); - - var pHandler = new $data.PromiseHandler(); - var cbWrapper = pHandler.createCallback(onResult); - - var someExpression = Container.createSomeExpression(q.expression); - var preparator = Container.createQueryExpressionCreator(q.entityContext); - try { - var expression = preparator.Visit(someExpression); - this.entityContext.log({ event: "EntityExpression", data: expression }); - - q.entityContext.executeQuery(Container.createQueryable(q, expression), cbWrapper, transaction); - } catch (e) { - cbWrapper.error(e); - } - - return pHandler.getPromise(); - }, - - every: function (filterPredicate, thisArg, onResult, transaction) { - /// Filters a set of entities using a boolean expression and returns true if all elements of the EntitySet is in the result set. - /// Filter function - /// The query parameters for filter function - /// A callback function - /// - /// - /// Filters a set of entities using a boolean expression and returns a - /// - /// Same as in filter. - /// - /// - /// The callback function to handle the result, same as in toArray. - /// - /// - /// - /// - /// Filters a set of entities using a boolean expression and returns a single element or throws an error if more than one element is filtered. - /// - /// Same as in filter. - /// - /// - /// The callback function to handle the result, same as in toArray. - /// - /// - /// - /// Result is true when all person are married. - /// Persons.every( function( person ) { return person.Married == true; }, null, { - /// success: function ( result ){ ... }, - /// error: function () { ... } - /// }); - /// - /// - - this._checkOperation('every'); - var q = this; - if (filterPredicate) { - q = this.filter(filterPredicate, thisArg); - } - q = q.take(1); - - var pHandler = new $data.PromiseHandler(); - var cbWrapper = pHandler.createCallback(onResult); - - var everyExpression = Container.createEveryExpression(q.expression); - var preparator = Container.createQueryExpressionCreator(q.entityContext); - try { - var expression = preparator.Visit(everyExpression); - this.entityContext.log({ event: "EntityExpression", data: expression }); - - q.entityContext.executeQuery(Container.createQueryable(q, expression), cbWrapper, transaction); - } catch (e) { - cbWrapper.error(e); - } - - return pHandler.getPromise(); - }, - - - take: function (amount) { - /// Returns only a specified number of elements from the start of the result set. - /// The number of elements to return. - /// - /// - /// Returns only a specified number of elements from the start of the result set. - /// - /// The number of elements to skip. - /// - /// - /// - /// Log the full name of each Person. - /// Persons.take(10).forEach( function( person ) { console.log(person.FullName; } ); - /// - /// - - this._checkOperation('take'); - var constExp = Container.createConstantExpression(amount, "number"); - var takeExp = Container.createPagingExpression(this.expression, constExp, $data.Expressions.ExpressionType.Take); - return Container.createQueryable(this, takeExp); - }, - skip: function (amount) { - /// Skip a specified number of elements from the start of the result set. - /// The number of elements to skip. - /// - /// - /// Skip a specified number of elements from the start of the result set. - /// - /// The number of elements to skip. - /// - /// - /// - /// Log the full name of each Person. - /// Persons.skip(1).take(5).forEach( function( person ) { console.log(person.FullName; } ); - /// - /// - - this._checkOperation('skip'); - var constExp = Container.createConstantExpression(amount, "number"); - var takeExp = Container.createPagingExpression(this.expression, constExp, $data.Expressions.ExpressionType.Skip); - return Container.createQueryable(this, takeExp); - }, - - order: function(selector) { - if (selector === '' || selector === undefined || selector === null) { - return this; - } - if(selector[0] === "-") { - var orderString = "it." + selector.replace("-",""); - return this.orderByDescending(orderString); - } else { - return this.orderBy("it." + selector); - } - - }, - - orderBy: function (selector, thisArg) { - ///Order a set of entities using an expression. - ///An order expression - ///The query parameters - /// - /// - ///Order a set of entities using an expression. - /// - ///The expression body of the order function in string. - ///To reference the lambda parameter use the 'it' context variable. - ///Example: orderBy("it.Id") - /// - /// - /// - /// - /// - ///Order a set of entities using an expression. - /// - /// - /// - ///Contains the predicate parameters - /// - /// - /// - ///Ordering a set of entities with a predicate function - ///var males = Persons.orderBy( function( person ) { return person.Id; } ); - /// - /// - - this._checkOperation('orderBy'); - var codeExpression = Container.createCodeExpression(selector, thisArg); - var exp = Container.createOrderExpression(this.expression, codeExpression, $data.Expressions.ExpressionType.OrderBy); - var q = Container.createQueryable(this, exp); - return q; - }, - orderByDescending: function (selector, thisArg) { - ///Order a set of entities descending using an expression. - ///An order expression - ///The query parameters - /// - /// - ///Order a set of entities descending using an expression. - /// - ///The expression body of the order function in string. - ///To reference the lambda parameter use the 'it' context variable. - ///Example: orderBy("it.Id") - /// - /// - /// - /// - /// - ///Order a set of entities descending using an expression. - /// - /// - /// - ///Contains the predicate parameters - /// - /// - /// - ///Ordering a set of entities with a predicate function - ///var males = Persons.orderByDescending( function( person ) { return person.Id; } ); - /// - /// - - this._checkOperation('orderByDescending'); - var codeExpression = Container.createCodeExpression(selector, thisArg); - var exp = Container.createOrderExpression(this.expression, codeExpression, $data.Expressions.ExpressionType.OrderByDescending); - var q = Container.createQueryable(this, exp); - return q; - }, - - first: function (filterPredicate, thisArg, onResult, transaction) { - /// Filters a set of entities using a boolean expression and returns the first element. - /// A callback function - /// - /// - /// Filters a set of entities using a boolean expression and returns the first element. - /// - /// Same as in filter. - /// - /// - /// The callback function to handle the result, same as in toArray. - /// - /// - /// - /// - /// Filters a set of entities using a boolean expression and returns the first element. - /// - /// Same as in filter. - /// - /// - /// The callback function to handle the result, same as in toArray. - /// - /// - /// - /// Get "George" from the Person entity set. - /// Persons.first( function( person ) { return person.FirstName == this.name; }, { name: "George" }, function ( result ){ ... }); - /// - /// - - this._checkOperation('first'); - var q = this; - if (filterPredicate) { - q = this.filter(filterPredicate, thisArg); - } - q = q.take(1); - - var pHandler = new $data.PromiseHandler(); - var cbWrapper = pHandler.createCallback(onResult); - - var firstExpression = Container.createFirstExpression(q.expression); - var preparator = Container.createQueryExpressionCreator(q.entityContext); - try { - var expression = preparator.Visit(firstExpression); - q.entityContext.log({ event: "EntityExpression", data: expression }); - - q.entityContext.executeQuery(Container.createQueryable(q, expression), cbWrapper, transaction); - } catch (e) { - cbWrapper.error(e); - } - - return pHandler.getPromise(); - }, - - find: function (keyValue, onResult, transaction) { - - var pHandler = new $data.PromiseHandler(); - var cbWrapper = pHandler.createCallback(onResult); - - var keys = this.defaultType.memberDefinitions.getKeyProperties(); - - try { - - if (keys.length === 1 && typeof keyValue !== 'object') { - var keyV = {}; - keyV[keys[0].name] = keyValue; - keyValue = keyV; - } - - if (typeof keyValue !== 'object') { - throw new Exception('Key parameter is invalid'); - } else { - - - var parameters = []; - for (var i = 0; i < keys.length; i++) { - var keyProp = keys[i]; - if (!(keyProp.name in keyValue)) { - throw new Exception('Key value missing'); - } - parameters.push(Container.createConstantExpression(keyValue[keyProp.name], keyProp.type, keyProp.name)); - } - - var operation = this.entityContext.storageProvider.supportedSetOperations['find']; - if (operation) { - - var findExpression = Container.createFindExpression(this.expression, parameters); - var preparator = Container.createQueryExpressionCreator(this.entityContext); - try { - var expression = preparator.Visit(findExpression); - this.entityContext.log({ event: "EntityExpression", data: expression }); - - this.entityContext.executeQuery(Container.createQueryable(this, expression), cbWrapper, transaction); - } catch (e) { - cbWrapper.error(e); - } - - } else { - var predicate = ''; - var params = {} - for (var i = 0; i < parameters.length; i++) { - var param = parameters[i]; - params[param.name] = param.value; - if (i > 0) predicate += ' && '; - predicate += "it." + param.name + " == this." + param.name; - } - - this.single(predicate, params, cbWrapper, transaction); - } - } - } catch (e) { - cbWrapper.error(e); - } - - return pHandler.getPromise(); - }, - - include: function (selector) { - /// Includes the given entity set in the query if it's an inverse property. - /// Entity set name - /// - /// - /// Includes the given entity set in the query if it's an inverse property. - /// - /// The name of the entity set you want to include in the query. - /// - /// - /// - /// Include the Category on every Article. - /// Articles.include("Category"); - /// - /// - - this._checkOperation('include'); - var constExp = Container.createConstantExpression(selector, "string"); - var takeExp = Container.createIncludeExpression(this.expression, constExp); - return Container.createQueryable(this, takeExp); - }, - - withInlineCount: function (selector) { - this._checkOperation('withInlineCount'); - var constExp = Container.createConstantExpression(selector || 'allpages', "string"); - var inlineCountExp = Container.createInlineCountExpression(this.expression, constExp); - return Container.createQueryable(this, inlineCountExp); - }, - - removeAll: function (onResult, transaction) { - /// Delete the query result and returns the number of deleted entities in a query as the callback parameter. - /// A callback function - /// - /// - /// Delete the query result and returns the number of deleted entities in a query as the callback parameter. - /// - /// The callback function to handle the result. - /// - /// - /// - /// - /// Delete the query result and returns the number of deleted entities in a query as the callback parameter. - /// - /// Object of callback functions to handle success and error. - /// Example: { success: function(result) { ... }, error: function() { alert("Something went wrong..."); } } - /// - /// - /// - /// Delete all People who are younger than 18 years old. - /// Persons.filter( function( p ){ return p.Age < 18; } ).removeAll( function( result ) { console.dir(result); } ); - /// - /// - - this._checkOperation('batchDelete'); - var pHandler = new $data.PromiseHandler(); - var cbWrapper = pHandler.createCallback(onResult); - - var batchDeleteExpression = Container.createBatchDeleteExpression(this.expression); - var preparator = Container.createQueryExpressionCreator(this.entityContext); - try { - var expression = preparator.Visit(batchDeleteExpression); - this.entityContext.log({ event: "EntityExpression", data: expression }); - - this.entityContext.executeQuery(Container.createQueryable(this, expression), cbWrapper, transaction); - } catch (e) { - cbWrapper.error(e); - } - - return pHandler.getPromise(); - }, - - - _runQuery: function (onResult_items, transaction) { - var pHandler = new $data.PromiseHandler(); - var cbWrapper = pHandler.createCallback(onResult_items); - - var preparator = Container.createQueryExpressionCreator(this.entityContext); - try { - var expression = preparator.Visit(this.expression); - this.entityContext.log({ event: "EntityExpression", data: expression }); - - this.entityContext.executeQuery(Container.createQueryable(this, expression), cbWrapper, transaction); - } catch (e) { - cbWrapper.error(e); - } - - return pHandler.getPromise(); - }, - - toTraceString: function (name) { - /// Returns the trace string of the query. - /// Name of the execution method (toArray, length, etc.). - /// - /// - /// Returns the trace string of the query. - /// - /// Name of the execution method (toArray, length, etc.). Optional. Default value is "toArray". - /// - /// - /// - /// Get the trace string for Articles.toArray() - /// Articles.toTraceString(); - /// - /// - - var expression = this.expression; - - if (name) { - expression = Container['create' + name + 'Expression'](expression); - } else { - expression = Container.createToArrayExpression(expression); - } - - var preparator = Container.createQueryExpressionCreator(this.entityContext); - expression = preparator.Visit(expression); - - //this.expression = expression; - var q = Container.createQueryable(this, expression) - return q.entityContext.getTraceString(q); - }, - - _checkOperation: function (name) { - var operation = this.entityContext.resolveSetOperations(name); - if (operation.invokable != undefined && !operation.invokable) - Guard.raise(new Exception("Operation '" + name + "' is not invokable with the provider")); - }, - defaultType: {} - -}, null); -///EntitySet is responsible for -/// -creating and holding entityType through schema -/// - provide Add method -/// - provide Delete method -/// - provide Update method -/// - provide queryProvider for queryable - -$data.EntitySchemaConfig = function EntitySchemaConfig() { - this.Name = ""; -}; -$data.entitySetState = { created: 0, defined: 1, active: 2 }; - -$data.Class.defineEx('$data.EntitySet', - [ - { type: $data.Queryable, params: [new ConstructorParameter(1)] } - ], null, -{ - constructor: function (elementType, context, collectionName, eventHandlers, roles) { - /// - /// Represents a typed entity set that is used to perform create, read, update, and delete operations - /// Type of entity set elements, elementType must be subclass of $data.Entity - /// Context of the EntitySet - /// Name of the EntitySet - /// - this.createNew = this[elementType.name] = this.elementType = this.defaultType = elementType; - var self = this; - context['createAdd' + elementType.name] = function (initData) { - var entity = new elementType(initData); - return self.add(entity); - } - this.stateManager = new $data.EntityStateManager(this); - - this.collectionName = collectionName; - this.roles = roles; - - for (var i in eventHandlers){ - this[i] = eventHandlers[i]; - } - }, - - addNew: function(item, cb) { - var callback = $data.typeSystem.createCallbackSetting(cb); - var _item = new this.createNew(item); - this.entityContext.saveChanges(cb); - return _item; - }, - - executeQuery: function (expression, on_ready) { - //var compiledQuery = this.entityContext - var callBack = $data.typeSystem.createCallbackSetting(on_ready); - this.entityContext.executeQuery(expression, callBack); - }, - getTraceString: function (expression) { - return this.entityContext.getTraceString(expression); - }, - setContext: function (entityContext) { - this.entitySetState = $data.entitySetState.active; - this.entityContext = entityContext; - this.entityContext[this.schema.name] = this[this.schema.name]; - }, - _trackEntity: function (entity) { - var trackedEntities = this.entityContext.stateManager.trackedEntities; - for (var i = 0; i < trackedEntities.length; i++) { - if (trackedEntities[i].data === entity) - return; - } - trackedEntities.push({ entitySet: this, data: entity }); - }, - add: function (entity) { - /// - /// Creates a typed entity and adds to the context. - /// The init parameters whish is based on Entity - /// - /// - /// Persons.add({ Name: 'John', Email: 'john@example.com', Age: 30, Gender: 'Male' }); - /// - /// - /// - /// - /// Adds the given entity to the context. - /// The entity to add - /// - /// - /// Persons.add(new $news.Types.Person({ Name: 'John', Email: 'john@example.com', Age: 30, Gender: 'Male' })); - /// - /// - /// - /// - /// var person = new $news.Types.Person({ Name: 'John', Email: 'john@example.com', Age: 30, Gender: 'Male' }); - /// Persons.add(person); - /// - /// - /// - - var data = entity; - if (entity instanceof $data.EntityWrapper) { - data = entity.getEntity(); - } else if (!(entity instanceof this.createNew)) { - data = new this.createNew(entity); - } - data.entityState = $data.EntityState.Added; - data.changedProperties = undefined; - data.context = this.entityContext; - this._trackEntity(data); - return data; - }, - - addMany: function(entities) { - var result = []; - var self = this; - entities.forEach(function (entity) { - result.push(self.add(entity)); - }); - return result; - }, - remove: function (entity) { - /// - /// Creates a typed entity and marks it as Deleted. - /// The init parameters whish is based on Entity - /// - /// Person will be marked as Deleted where an id is 5. Id is a key of entity. - /// Persons.remove({ Id: 5 }); - /// - /// - /// - /// - /// Marks the given entity as Deleted. - /// The entity to remove - /// - /// - /// Persons.remove(person); - /// - /// - /// - /// Person will be marked as Deleted where an Id is 5. Id is a key of entity. - /// Persons.add(new $news.Types.Person({ Id: 5 })); - /// - /// - /// - - var data = entity; - if (entity instanceof $data.EntityWrapper) { - data = entity.getEntity(); - } else if (!(entity instanceof this.createNew)) { - data = new this.createNew(entity); - } - data.entityState = $data.EntityState.Deleted; - data.changedProperties = undefined; - this._trackEntity(data); - }, - attach: function (entity, mode) { - /// - /// Creates a typed entity and adds to the Context with Unchanged state. - /// The init parameters whish is based on Entity - /// - /// - /// Persons.attach({ Id: 5, Email: 'newEmail@example.com' }); - /// - /// - /// - /// - /// Adds to the context and sets state Unchanged. - /// The entity to attach - /// - /// - /// Persons.attach(person); - /// - /// - /// - /// Set an entity's related entities without loading - /// - /// var categoryPromo = new $news.Types.Category({ Id: 5 }); - /// Category.attach(categoryPromo); - /// var article = new $news.Types.Article({ Title: 'New Article title', Body: 'Article body', Category: [ categoryPromo ] }); - /// Article.attach(article); - /// - /// - /// - - var data = entity; - if (entity instanceof $data.EntityWrapper) { - data = entity.getEntity(); - } else if (!(entity instanceof this.createNew)) { - data = new this.createNew(entity); - } - - for (var i = 0; i < this.entityContext.stateManager.trackedEntities.length; i++) { - var current = this.entityContext.stateManager.trackedEntities[i]; - if (current.data === data) - break; - if (current.data.equals(data)) { - Guard.raise(new Exception("Context already contains this entity!!!")); - } - } - - if (mode === true) { - if (data.changedProperties && data.changedProperties.length > 0) { - data.entityState = $data.EntityState.Modified; - } else { - data.entityState = $data.EntityState.Unchanged; - } - } else { - if (typeof mode === "string") mode = $data.EntityAttachMode[mode]; - var attachMode = mode || $data.EntityAttachMode[$data.EntityAttachMode.defaultMode]; - if (typeof attachMode === "function") { - attachMode.call($data.EntityAttachMode, data); - } else { - data.entityState = $data.EntityState.Unchanged; - data.changedProperties = undefined; - } - } - /*if (!keepChanges) { - data.entityState = $data.EntityState.Unchanged; - data.changedProperties = undefined; - }*/ - data.context = this.entityContext; - this._trackEntity(data); - }, - detach: function (entity) { - /// - /// Creates a typed entity and detach from the Context with Detached state. - /// The init parameters whish is based on Entity - /// - /// Person will be Detached where an id is 5. Id is a key of entity. - /// Persons.detach({ Id: 5 }); - /// - /// - /// - /// - /// Detach from the context and sets state Detached. - /// The entity to detach - /// - /// - /// Persons.detach(person); - /// - /// - /// - /// Person will be Detached where an Id is 5. Id is a key of entity. - /// Persons.add(new $news.Types.Person({ Id: 5 })); - /// - /// - /// - - var data = entity; - if (entity instanceof $data.EntityWrapper) { - data = entity.getEntity(); - } else if (!(entity instanceof this.createNew)) { - data = new this.createNew(entity); - } - - var existsItem; - var trackedEnt = this.entityContext.stateManager.trackedEntities; - for (var i = 0; i < trackedEnt.length; i++) { - if (trackedEnt[i].data.equals(data)) - existsItem = trackedEnt[i]; - } - - //var existsItem = this.entityContext.stateManager.trackedEntities.filter(function (i) { return i.data.equals(data); }).pop(); - if (existsItem) { - var idx = this.entityContext.stateManager.trackedEntities.indexOf(existsItem); - entity.entityState = $data.EntityState.Detached; - this.entityContext.stateManager.trackedEntities.splice(idx, 1); - return; - } - }, - attachOrGet: function (entity, mode) { - /// - /// Creates a typed entity and adds to the Context with Unchanged state. - /// The init parameters whish is based on Entity - /// - /// - /// Id is a key of entity. - /// var person = Persons.attachOrGet({ Id: 5 }); - /// - /// - /// - /// - /// If not in context then adds to it and sets state Unchanged. - /// The entity to detach - /// - /// - /// - /// var attachedPerson = Persons.attachOrGet(person); - /// - /// - /// - /// Id is a key of entity. - /// var p = new $news.Types.Person({ Id: 5 }); - /// var attachedPerson = Persons.attachOrGet(p); - /// - /// - /// - - var data = entity; - if (entity instanceof $data.EntityWrapper) { - data = entity.getEntity(); - } else if (!(entity instanceof this.createNew)) { - data = new this.createNew(entity); - } - - var existsItem; - var trackedEnt = this.entityContext.stateManager.trackedEntities; - for (var i = 0; i < trackedEnt.length; i++) { - if (trackedEnt[i].data.equals(data)) - existsItem = trackedEnt[i]; - } - //var existsItem = this.entityContext.stateManager.trackedEntities.filter(function (i) { return i.data.equals(data); }).pop(); - if (existsItem) { - return existsItem.data; - } - - if (typeof mode === "string") mode = $data.EntityAttachMode[mode]; - var attachMode = mode || $data.EntityAttachMode[$data.EntityAttachMode.defaultMode]; - if (typeof attachMode === "function") { - attachMode.call($data.EntityAttachMode, data); - } else { - data.entityState = $data.EntityState.Unchanged; - data.changedProperties = undefined; - } - //data.entityState = $data.EntityState.Unchanged; - //data.changedProperties = undefined; - data.context = this.entityContext; - this._trackEntity(data); - return data; - }, - //find: function (keys) { - // //todo global scope - // if (!this.entityKeys) { - // this.entityKeys = this.createNew.memberDefinition.filter(function (prop) { return prop.key; }, this); - // } - // this.entityContext.stateManager.trackedEntities.forEach(function (item) { - // if (item.entitySet == this) { - // var isOk = true; - // this.entityKeys.forEach(function (item, index) { isOK = isOk && (item.data[item.name] == keys[index]); }, this); - // if (isOk) { - // return item.data; - // } - // } - // }, this); - // //TODO: db call - // return null; - //}, - loadItemProperty: function (entity, memberDefinition, callback) { - /// - /// Loads a property of the entity through the storage provider. - /// Entity object - /// Property name - /// - /// Callback function - /// - /// - /// - /// - /// - /// Loads a property of the entity through the storage provider. - /// Entity object - /// Property name - /// - /// Success and error callbacks definition. - /// Example: [code]{ success: function(db) { .. }, error: function() { .. } }[/code] - /// - /// - /// - /// - /// Loads a property of the entity through the storage provider. - /// Entity object - /// Property definition - /// - /// Callback function - /// - /// - /// - /// - /// - /// Loads a property of the entity through the storage provider. - /// Entity object - /// Property definition - /// - /// Success and error callbacks definition. - /// Example: [code]{ success: function(db) { .. }, error: function() { .. } }[/code] - /// - /// - /// - - return this.entityContext.loadItemProperty(entity, memberDefinition, callback); - }, - saveChanges: function () { - return this.entityContext.saveChanges.apply(this.entityContext, arguments); - }, - addProperty: function (name, getter, setter) { - return this.elementType.addProperty.apply(this.elementType, arguments); - }, - expression: { - get: function () { - if (!this._expression) { - var ec = Container.createEntityContextExpression(this.entityContext); - //var name = entitySet.collectionName; - //var entitySet = this.entityContext[entitySetName]; - var memberdef = this.entityContext.getType().getMemberDefinition(this.collectionName); - var es = Container.createEntitySetExpression(ec, - Container.createMemberInfoExpression(memberdef), null, - this); - this._expression = es; - } - - return this._expression; - }, - set: function (value) { - this._expression = value; - } - }, - getFieldUrl: function (keys, field) { - return this.entityContext.getFieldUrl(keys, field, this); - }, - bulkInsert: function (fields, datas, callback) { - return this.entityContext.bulkInsert(this, fields, datas, callback); - } -}, null); -$data.EntityState = { - Detached:0, - Unchanged: 10, - Added: 20, - Modified: 30, - Deleted: 40 -};$data.Class.define("$data.EntityAttachMode", null, null, {}, { - defaultMode: 'Default', - AllChanged: function (data) { - var memDefs = data.getType().memberDefinitions.getPublicMappedProperties(); - for (var i = 0; i < memDefs.length; i++) { - data._setPropertyChanged(memDefs[i]); - } - data.entityState = $data.EntityState.Modified; - }, - KeepChanges: function (data) { - if (data.changedProperties && data.changedProperties.length > 0) { - data.entityState = $data.EntityState.Modified; - } else { - data.entityState = $data.EntityState.Unchanged; - } - }, - Default: function (data) { - data.entityState = $data.EntityState.Unchanged; - data.changedProperties = undefined; - } -});$data.Class.define('$data.EntityStateManager', null, null, -{ - constructor: function (entityContext) { - this.entityContext = null; - this.trackedEntities = []; - this.init(entityContext); - }, - init: function (entityContext) { - this.entityContext = entityContext; - }, - reset: function () { - this.trackedEntities = []; - } -}, null);$data.Class.define('$data.ItemStoreClass', null, null, { - constructor: function () { - var self = this; - self.itemStoreConfig = { - aliases: {}, - contextTypes: {} - } - - self.resetStoreToDefault('local', true); - $data.addStore = function () { - return self.addItemStoreAlias.apply(self, arguments); - }; - $data.implementation = self.implementation; - - $data.Entity.addMember('storeToken', { - get: function () { - if (this.storeConfigs && this.storeConfigs['default']) - return this.storeConfigs.stores[this.storeConfigs['default']]; - }, - set: function (value) { - self._setTypeStoreConfig(this, 'default', value); - } - }, true); - }, - itemStoreConfig: {}, - - addItemStoreAlias: function (name, contextFactoryOrToken, isDefault) { - var self = this; - var promise = new $data.PromiseHandler(); - var callback = promise.createCallback(); - - if ('string' === typeof name) { - //storeToken - if ('object' === typeof contextFactoryOrToken && 'factory' in contextFactoryOrToken) { - var type = Container.resolveType(contextFactoryOrToken.typeName); - - self.itemStoreConfig.aliases[name] = contextFactoryOrToken.factory; - self.itemStoreConfig.contextTypes[name] = type; - if (isDefault) { - self.itemStoreConfig['default'] = name; - } - - callback.success(); - return promise.getPromise(); - } - //contextFactory - else if ('function' === typeof contextFactoryOrToken) { - var preContext = contextFactoryOrToken(); - var contextPromise; - if (preContext && preContext instanceof $data.EntityContext) { - callback.success(preContext); - contextPromise = promise.getPromise(); - } else { - contextPromise = preContext; - } - - return contextPromise.then(function (ctx) { - if (typeof ctx === 'function') { - //factory resolve factory - return self.addItemStoreAlias(name, ctx, isDefault); - } - - if (ctx instanceof $data.EntityContext) { - return ctx.onReady() - .then(function (ctx) { - self.itemStoreConfig.aliases[name] = contextFactoryOrToken; - self.itemStoreConfig.contextTypes[name] = ctx.getType(); - if (isDefault) { - self.itemStoreConfig['default'] = name; - } - - return ctx; - }); - } else { - promise = new $data.PromiseHandler(); - callback = promise.createCallback(); - callback.error(new Exception('factory dont have context instance', 'Invalid arguments')); - return promise.getPromise(); - } - }); - } - } - - callback.error(new Exception('Name or factory missing', 'Invalid arguments')); - return promise.getPromise(); - }, - resetStoreToDefault: function (name, isDefault) { - this.itemStoreConfig.aliases[name] = this._getDefaultItemStoreFactory; - delete this.itemStoreConfig.contextTypes[name]; - if (isDefault) { - this.itemStoreConfig['default'] = name; - } - }, - _setStoreAlias: function (entity, storeToken) { - if ('object' === typeof storeToken && !entity.storeToken) - entity.storeToken = storeToken - return entity; - }, - _getStoreAlias: function (entity, storeAlias) { - var type; - if (entity instanceof $data.Entity) { - var alias = storeAlias || entity.storeToken; - if (alias) { - return alias; - } else { - type = entity.getType(); - } - } else { - type = entity; - } - - return storeAlias || (type.storeConfigs ? type.storeConfigs['default'] : undefined) || type.storeToken; - }, - _getStoreContext: function (aliasOrToken, type, nullIfInvalid) { - var contextPromise = this._getContextPromise(aliasOrToken, type); - - if (!contextPromise || contextPromise instanceof $data.EntityContext) { - var promise = new $data.PromiseHandler(); - var callback = promise.createCallback(); - callback.success(contextPromise); - contextPromise = promise.getPromise(); - } - - return contextPromise.then(function (context) { - if (context instanceof $data.EntityContext) { - return context.onReady(); - } else if (nullIfInvalid) { - return null; - } else { - var promise = new $data.PromiseHandler(); - var callback = promise.createCallback(); - callback.error(new Exception('factory return type error', 'Error')); - return promise.getPromise(); - } - }); - }, - _getContextPromise: function (aliasOrToken, type) { - /*Token*/ - if (aliasOrToken && 'object' === typeof aliasOrToken && 'function' === typeof aliasOrToken.factory) { - return aliasOrToken.factory(type); - } else if (aliasOrToken && 'object' === typeof aliasOrToken && 'object' === typeof aliasOrToken.args && 'string' === typeof aliasOrToken.typeName) { - var type = Container.resolveType(aliasOrToken.typeName); - return new type(JSON.parse(JSON.stringify(aliasOrToken.args))); - } - /*resolve alias from type (Token)*/ - else if (aliasOrToken && 'string' === typeof aliasOrToken && type.storeConfigs && type.storeConfigs.stores[aliasOrToken] && typeof type.storeConfigs.stores[aliasOrToken].factory === 'function') { - return type.storeConfigs.stores[aliasOrToken].factory(); - } - /*resolve alias from type (constructor options)*/ - else if (aliasOrToken && 'string' === typeof aliasOrToken && type.storeConfigs && type.storeConfigs.stores[aliasOrToken]) { - return this._getDefaultItemStoreFactory(type, type.storeConfigs.stores[aliasOrToken]); - } - /*resolve alias from ItemStore (factories)*/ - else if (aliasOrToken && 'string' === typeof aliasOrToken && this.itemStoreConfig.aliases[aliasOrToken]) { - return this.itemStoreConfig.aliases[aliasOrToken](type); - } - /*token is factory*/ - else if (aliasOrToken && 'function' === typeof aliasOrToken) { - return aliasOrToken(); - } - /*default no hint*/ - else { - return this.itemStoreConfig.aliases[this.itemStoreConfig['default']](type); - } - - }, - _getStoreEntitySet: function (storeAlias, instanceOrType) { - var aliasOrToken = this._getStoreAlias(instanceOrType, storeAlias); - var type = ("function" === typeof instanceOrType) ? instanceOrType : instanceOrType.getType();; - - return this._getStoreContext(aliasOrToken, type) - .then(function (ctx) { - var entitySet = ctx.getEntitySetFromElementType(type); - if (!entitySet) { - var d = new $data.PromiseHandler(); - var callback = d.createCallback(); - callback.error("EntitySet not exist for " + type.fullName); - return d.getPromise(); - } - return entitySet; - }); - }, - _getDefaultItemStoreFactory: function (instanceOrType, initStoreConfig) { - if (instanceOrType) { - var type = ("function" === typeof instanceOrType) ? instanceOrType : instanceOrType.getType(); - var typeName = $data.Container.resolveName(type) + "_items"; - var typeName = typeName.replace(/\./g, "_"); - - var storeConfig = $data.typeSystem.extend({ - collectionName: initStoreConfig && initStoreConfig.collectionName ? initStoreConfig.collectionName : 'Items', - tableName: typeName, - initParam: { provider: 'local', databaseName: typeName } - }, initStoreConfig); - - var contextDef = {}; - contextDef[storeConfig.collectionName] = { type: $data.EntitySet, elementType: type } - if (storeConfig.tableName) - contextDef[storeConfig.collectionName]['tableName'] = storeConfig.tableName; - - var inMemoryType = $data.EntityContext.extend(typeName, contextDef); - var ctx = new inMemoryType(storeConfig.initParam); - if (initStoreConfig && typeof initStoreConfig === 'object') - initStoreConfig.factory = ctx._storeToken.factory; - return ctx; - } - return undefined; - }, - implementation: function (name, contextOrAlias) { - var self = $data.ItemStore; - var result; - - if (typeof contextOrAlias === 'string') { - contextOrAlias = self.itemStoreConfig.contextTypes[contextOrAlias] - } else if (contextOrAlias instanceof $data.EntityContext) { - contextOrAlias = contextOrAlias.getType(); - } else if (!(typeof contextOrAlias === 'function' && contextOrAlias.isAssignableTo)) { - contextOrAlias = self.itemStoreConfig.contextTypes[self.itemStoreConfig['default']]; - } - - if (contextOrAlias) { - result = self._resolveFromContext(contextOrAlias, name); - } - - if (!result) { - result = Container.resolveType(name); - } - - return result; - }, - _resolveFromContext: function (contextType, name) { - var memDefs = contextType.memberDefinitions.getPublicMappedProperties(); - for (var i = 0; i < memDefs.length; i++) { - var memDef = memDefs[i]; - if (memDef.type) { - var memDefType = Container.resolveType(memDef.type); - if (memDefType.isAssignableTo && memDefType.isAssignableTo($data.EntitySet)) { - var elementType = Container.resolveType(memDef.elementType); - if (elementType.name === name) { - return elementType; - } - } - } - } - return null; - }, - - - //Entity Instance - EntityInstanceSave: function (storeAlias, hint) { - var self = $data.ItemStore; - var entity = this; - return self._getStoreEntitySet(storeAlias, entity) - .then(function (entitySet) { - return self._getSaveMode(entity, entitySet, hint, storeAlias) - .then(function (mode) { - mode = mode || 'add'; - switch (mode) { - case 'add': - entitySet.add(entity); - break; - case 'attach': - entitySet.attach(entity, true); - entity.entityState = $data.EntityState.Modified; - break; - default: - var d = new $data.PromiseHandler(); - var callback = d.createCallback(); - callback.error('save mode not supported: ' + mode); - return d.getPromise(); - } - - return entitySet.entityContext.saveChanges() - .then(function () { self._setStoreAlias(entity, entitySet.entityContext.storeToken); return entity; }); - }); - }); - }, - EntityInstanceRemove: function (storeAlias) { - var self = $data.ItemStore; - var entity = this; - return self._getStoreEntitySet(storeAlias, entity) - .then(function (entitySet) { - entitySet.remove(entity); - - return entitySet.entityContext.saveChanges() - .then(function () { return entity; }); - }); - }, - EntityInstanceRefresh: function (storeAlias, keepStore) { - var self = $data.ItemStore; - var entity = this; - var entityType = entity.getType(); - - var key = self._getKeyObjectFromEntity(entity, entityType); - - return entityType.read(key, storeAlias) - .then(function (loadedEntity) { - entityType.memberDefinitions.getPublicMappedProperties().forEach(function (memDef) { - entity[memDef.name] = loadedEntity[memDef.name]; - }); - entity.storeToken = (keepStore ? entity.storeToken : undefined) || loadedEntity.storeToken; - entity.changedProperties = undefined; - return entity; - }); - }, - - //Entity Type - EntityInheritedTypeProcessor: function (type) { - var self = $data.ItemStore; - type.readAll = self.EntityTypeReadAll(type); - type.read = self.EntityTypeRead(type); - type.removeAll = self.EntityTypeRemoveAll(type); - type.remove = self.EntityTypeRemove(type); - type.get = self.EntityTypeGet(type); //Not complete - type.save = self.EntityTypeSave(type); - type.addMany = self.EntityTypeAddMany(type); - type.itemCount = self.EntityTypeItemCount(type); - type.query = self.EntityTypeQuery(type); - type.takeFirst = self.EntityTypeTakeFirst(type); - - type.setStore = self.EntityTypeSetStore(type); - }, - EntityTypeReadAll: function (type) { - return function (storeAlias) { - var self = $data.ItemStore; - return self._getStoreEntitySet(storeAlias, type) - .then(function (entitySet) { - return entitySet.forEach(function (item) { self._setStoreAlias(item, entitySet.entityContext.storeToken); }); - }); - } - }, - EntityTypeRemoveAll: function (type) { - return function (storeAlias) { - var self = $data.ItemStore; - return self._getStoreEntitySet(storeAlias, type) - .then(function (entitySet) { - return entitySet.toArray().then(function (items) { - items.forEach(function (item) { - entitySet.remove(item); - }); - - return entitySet.entityContext.saveChanges() - .then(function () { return items; }); - }); - }); - } - }, - EntityTypeRead: function (type) { - return function (key, storeAlias) { - var self = $data.ItemStore; - return self._getStoreEntitySet(storeAlias, type) - .then(function (entitySet) { - try { - var singleParam = self._findByIdQueryable(entitySet, key); - return entitySet.single(singleParam.predicate, singleParam.thisArgs) - .then(function (item) { return self._setStoreAlias(item, entitySet.entityContext.storeToken); }); - } catch (e) { - var d = new $data.PromiseHandler(); - var callback = d.createCallback(); - callback.error(e); - return d.getPromise(); - } - }); - }; - }, - EntityTypeGet: function (type) { - return function (key, storeAlias) { - var self = $data.ItemStore; - var item = new type(self._getKeyObjectFromEntity(key)); - item.refresh(storeAlias); - return item; - }; - }, - EntityTypeSave: function (type) { - return function (initData, storeAlias, hint) { - - var self = $data.ItemStore; - var instance = new type(initData); - return instance.save(storeAlias, hint); - } - }, - EntityTypeAddMany: function (type) { - return function (initDatas, storeAlias) { - var self = $data.ItemStore; - return self._getStoreEntitySet(storeAlias, type) - .then(function (entitySet) { - var items = entitySet.addMany(initDatas); - return entitySet.entityContext.saveChanges() - .then(function () { - return items; - }); - }); - } - }, - EntityTypeRemove: function (type) { - return function (key, storeAlias) { - var self = $data.ItemStore; - var entityPk = type.memberDefinitions.getKeyProperties(); - var entity; - if (entityPk.length === 1) { - var obj = {}; - obj[entityPk[0].name] = key; - entity = new type(obj); - } else { - entity = new type(key); - } - return entity.remove(storeAlias); - } - }, - EntityTypeItemCount: function (type) { - return function (storeAlias) { - var self = $data.ItemStore; - return self._getStoreEntitySet(storeAlias, type) - .then(function (entitySet) { - return entitySet.length(); - }); - } - }, - EntityTypeQuery: function (type) { - return function (predicate, thisArg, storeAlias) { - var self = $data.ItemStore; - return self._getStoreEntitySet(storeAlias, type) - .then(function (entitySet) { - return entitySet.filter(predicate, thisArg).forEach(function (item) { self._setStoreAlias(item, entitySet.entityContext.storeToken); }); - }); - } - }, - EntityTypeTakeFirst: function (type) { - return function (predicate, thisArg, storeAlias) { - var self = $data.ItemStore; - return self._getStoreEntitySet(storeAlias, type) - .then(function (entitySet) { - return entitySet.first(predicate, thisArg) - .then(function (item) { return self._setStoreAlias(item, entitySet.entityContext.storeToken); }); - }); - } - }, - - EntityTypeSetStore: function (type) { - return function (name, config) { - if (typeof name === 'object' && typeof config === 'undefined') { - config = name; - name = 'default'; - } - - var self = $data.ItemStore; - - var defStoreConfig = {}; - if (config) { - if (config.tableName) { - defStoreConfig.tableName = config.tableName; - delete config.tableName; - } - - if (config.collectionName) { - defStoreConfig.collectionName = config.collectionName; - delete config.collectionName; - } - - if (typeof config.dataSource === 'string') { - var ds = config.dataSource; - if (ds.lastIndexOf('/') === ds.length - 1) { - ds = ds.substring(0, ds.lastIndexOf('/')); - } - var parsedApiUrl = ds.substring(0, ds.lastIndexOf('/')); - if (!defStoreConfig.tableName) - defStoreConfig.tableName = ds.substring(ds.lastIndexOf('/') + 1); - - var provider = config.provider || config.name; - switch (provider) { - case 'oData': - config.oDataServiceHost = config.oDataServiceHost || parsedApiUrl; - break; - case 'webApi': - config.apiUrl = config.apiUrl || parsedApiUrl; - break; - default: - break; - } - } - - - } else { - config = { name: 'local' }; - } - - defStoreConfig.initParam = config; - self._setTypeStoreConfig(type, name, defStoreConfig); - - return type; - } - }, - _setTypeStoreConfig: function(type, name, config){ - if (!type.storeConfigs) { - type.storeConfigs = { - stores: {} - }; - } - type.storeConfigs.stores[name] = config; - if (name === 'default') { - type.storeConfigs['default'] = name; - } - }, - - _findByIdQueryable: function (set, keys) { - var keysProps = set.defaultType.memberDefinitions.getKeyProperties(); - if (keysProps.length > 1 && keys && 'object' === typeof keys) { - var predicate = "", thisArgs = {}; - for (var i = 0; i < keysProps.length; i++) { - if (i > 0) predicate += " && "; - - var key = keysProps[i]; - predicate += "it." + key.name + " == this." + key.name; - thisArgs[key.name] = keys[key.name]; - } - - return { - predicate: predicate, - thisArgs: thisArgs - }; - } else if (keysProps.length === 1) { - return { - predicate: "it." + keysProps[0].name + " == this.value", - thisArgs: { value: keys } - }; - } else { - throw 'invalid keys'; - } - }, - _getKeyObjectFromEntity: function (obj, entityType) { - var key; - var keyDefs = entityType.memberDefinitions.getKeyProperties(); - if (keyDefs.length === 1) - key = obj && typeof obj === 'object' ? obj[keyDefs[0].name] : obj; - else { - key = {}; - - for (var i = 0; i < keyDefs.length; i++) { - key[keyDefs[0].name] = obj ? obj[keyDefs[0].name] : obj; - } - } - - return key; - }, - _getSaveMode: function (entity, entitySet, hint, storeAlias) { - var self = this; - var promise = new $data.PromiseHandler(); - var callback = promise.createCallback(); - var entityType = entity.getType(); - - switch (true) { - case hint === 'update': - callback.success('attach'); break; - case hint === 'new': - callback.success('add'); break; - case false === entityType.memberDefinitions.getKeyProperties().every(function (keyDef) { return entity[keyDef.name]; }): - callback.success('add'); break; - case !!entity.storeToken: - callback.success('attach'); break; - break; - default: - //use the current entity store informations - storeAlias = this._getStoreAlias(entity, storeAlias); - entityType.read(self._getKeyObjectFromEntity(entity, entityType), storeAlias) - .then(function () { callback.success('attach'); }) - .fail(function () { callback.success('add'); }); - break; - } - - return promise.getPromise(); - }, - - //EntityContext - ContextRegister: function (storageProviderCfg) { - //context instance - var self = this; - var args = JSON.parse(JSON.stringify(storageProviderCfg)); - this.storeToken = { - typeName: this.getType().fullName, - args: args, - factory: function () { - return new (self.getType())(args); - } - } - - //set elementType storetoken - var members = this.getType().memberDefinitions.getPublicMappedProperties(); - for (var i = 0; i < members.length; i++) { - var item = members[i]; - if (item.type) { - var itemResolvedDataType = Container.resolveType(item.type); - if (itemResolvedDataType && itemResolvedDataType.isAssignableTo && itemResolvedDataType.isAssignableTo($data.EntitySet)) { - var elementType = Container.resolveType(item.elementType); - if (!elementType.storeToken) { - elementType.storeToken = this.storeToken; - } - } - } - } - - }, - QueryResultModifier: function (query) { - var self = $data.ItemStore; - var context = query.context; - var type = query.modelBinderConfig.$type; - if ('string' === typeof type) { - type = Container.resolveType(type); - } - - if (type === $data.Array && query.modelBinderConfig.$item && query.modelBinderConfig.$item.$type) { - type = query.modelBinderConfig.$item.$type; - } - - //TODO: runs when model binding missed (inmemory) - if ((typeof type === 'undefined' && query.result && query.result[0] instanceof $data.Entity)) { - var navProps = !type ? [] : type.memberDefinitions.getPublicMappedProperties().filter(function (memDef) { - return !!memDef.inverseProperty; - }); - - for (var i = 0; i < query.result.length; i++) { - self._setStoreAlias(query.result[i], context.storeToken); - - for (var j = 0; j < navProps.length; j++) { - var navProp = navProps[j]; - if (query.result[i][navProp.name] instanceof $data.Entity) { - self._setStoreAlias(query.result[i][navProp.name], context.storeToken); - } else if (Array.isArray(query.result[i][navProp.name])) { - for (var k = 0; k < query.result[i][navProp.name].length; k++) { - if (query.result[i][navProp.name][k] instanceof $data.Entity) { - self._setStoreAlias(query.result[i][navProp.name][k], context.storeToken); - } - } - } - } - } - } - } -}); -$data.ItemStore = new $data.ItemStoreClass(); - -$data.Entity.addMember('field', function (propName) { - var def = this.memberDefinitions.getMember(propName); - if (def) { - if (def.definedBy === this) { - return new $data.MemberWrapper(def); - } else { - Guard.raise(new Exception("Member '" + propName + "' defined on '" + def.definedBy.fullName + "'!", 'Invalid Operation')); - } - } else { - Guard.raise(new Exception("Member '" + propName + "' not exists!", 'Invalid Operation')); - } - - return this; -}, true); - - -$data.Class.define('$data.MemberWrapper', null, null, { - constructor: function (memberDefinition) { - this.memberDefinition = memberDefinition; - }, - setKey: function (value) { - this.memberDefinition.key = value || value === undefined ? true : false; - return this; - }, - setComputed: function (value) { - this.memberDefinition.computed = value || value === undefined ? true : false; - return this; - }, - setRequired: function (value) { - this.memberDefinition.required = value || value === undefined ? true : false; - return this; - }, - setNullable: function (value) { - this.memberDefinition.nullable = value || value === undefined ? true : false; - return this; - }, - changeDefinition: function (attr, value) { - this.memberDefinition[attr] = value; - return this; - } -}); - -$data.Class.define('$data.StorageProviderLoaderBase', null, null, { - isSupported: function (providerName) { - $data.Trace.log('Detecting ' + providerName + ' provider support'); - var supported = true; - switch (providerName) { - case 'indexedDb': - supported = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB || (window.msIndexedDB && !(/^file:/.test(window.location.href))); - break; - case 'storm': - supported = 'XMLHttpRequest' in window; - break; - case 'webSql': - case 'sqLite': - supported = 'openDatabase' in window; - break; - case 'LocalStore': - supported = 'localStorage' in window && window.localStorage ? true : false; - break; - case 'sqLite': - supported = 'openDatabase' in window; - break; - case 'mongoDB': - supported = $data.mongoDBDriver; - break; - default: - break; - } - $data.Trace.log(providerName + ' provider is ' + (supported ? '' : 'not') + ' supported'); - return supported; - }, - scriptLoadTimeout: { type: 'int', value: 1000 }, - scriptLoadInterval: { type: 'int', value: 50 }, - npmModules: { - value: { - 'indexedDb': 'jaydata-indexeddb', - 'InMemory': 'jaydata-inmemory', - 'LocalStore': 'jaydata-inmemory', - 'mongoDB': 'jaydata-mongodb', - 'oData': 'jaydata-odata', - 'webApi': 'jaydata-webapi', - 'sqLite': 'jaydata-sqlite', - 'webSql': 'jaydata-sqlite', - 'storm': 'jaydata-storm' - } - }, - ProviderNames: { - value: { - 'indexedDb': 'IndexedDb', - 'InMemory': 'InMemory', - 'LocalStore': 'InMemory', - 'oData': 'oData', - 'webApi': 'WebApi', - 'sqLite': 'SqLite', - 'webSql': 'SqLite', - 'storm': 'Storm' - } - }, - load: function (providerList, callback) { - $data.RegisteredStorageProviders = $data.RegisteredStorageProviders || {}; - - $data.Trace.log('Loading provider(s): ' + providerList); - callback = $data.typeSystem.createCallbackSetting(callback); - - var self = this; - var cacheKey = providerList.join(','); - self._fallbackCache = self._fallbackCache || {}; - - if (self._fallbackCache[cacheKey]) { - callback.success(self._fallbackCache[cacheKey]); - } else { - this.find(providerList, { - success: function (provider, selectedProvider) { - self._fallbackCache[cacheKey] = provider; - callback.success.call(this, provider); - }, - error: callback.error - }); - } - }, - find: function (providerList, callback) { - var currentProvider = providerList.shift(); - var currentProvider = this.getVirtual(currentProvider); - if(Array.isArray(currentProvider)){ - providerList = currentProvider; - currentProvider = providerList.shift(); - } - - while (currentProvider && !this.isSupported(currentProvider)) { - currentProvider = providerList.shift(); - } - - $data.Trace.log('First supported provider is ' + currentProvider); - - if (!currentProvider){ - $data.Trace.log('Provider fallback failed'); - callback.error(); - } - - if ($data.RegisteredStorageProviders) { - $data.Trace.log('Is the ' + currentProvider + ' provider already registered?'); - var provider = $data.RegisteredStorageProviders[currentProvider]; - if (provider) { - $data.Trace.log(currentProvider + ' provider registered'); - callback.success(provider) - return; - }else{ - $data.Trace.log(currentProvider + ' provider not registered'); - } - } - - if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') { - // NodeJS - $data.Trace.log('node.js detected trying to load NPM module'); - this.loadNpmModule(currentProvider, providerList, callback); - } else { - $data.Trace.log('Browser detected trying to load provider'); - this.loadProvider(currentProvider, providerList, callback); - } - }, - loadProvider: function (currentProvider, providerList, callback) { - var self = this; - var mappedName = $data.StorageProviderLoader.ProviderNames[currentProvider] || currentProvider; - $data.Trace.log(currentProvider + ' provider is mapped to name ' + mappedName + 'Provider'); - if (mappedName) { - var url = this.getUrl(mappedName); - $data.Trace.log(currentProvider + ' provider from URL: ' + url); - - var loader = this.loadScript; - if (document && document.createElement) { - $data.Trace.log('document and document.createElement detected, using script element loader method'); - loader = this.loadScriptElement; - } - - loader.call(this, url, currentProvider, function (successful) { - var provider = $data.RegisteredStorageProviders[currentProvider]; - if (successful && provider) { - $data.Trace.log(currentProvider + ' provider successfully registered'); - callback.success(provider); - } else if (providerList.length > 0) { - $data.Trace.log(currentProvider + ' provider failed to load, trying to fallback to ' + providerList + ' provider(s)'); - self.find(providerList, callback); - } else { - $data.Trace.log(currentProvider + ' provider failed to load'); - callback.error(); - } - }); - } - }, - getUrl: function (providerName) { - var jaydataScriptMin = document.querySelector('script[src$="jaydata.min.js"]'); - var jaydataScript = document.querySelector('script[src$="jaydata.js"]'); - if (jaydataScriptMin) return jaydataScriptMin.src.substring(0, jaydataScriptMin.src.lastIndexOf('/') + 1) + 'jaydataproviders/' + providerName + 'Provider.min.js'; - else if (jaydataScript) return jaydataScript.src.substring(0, jaydataScript.src.lastIndexOf('/') + 1) + 'jaydataproviders/' + providerName + 'Provider.js'; - else return 'jaydataproviders/' + providerName + 'Provider.js'; - }, - loadScript: function (url, currentProvider, callback) { - if (!url){ - callback(false); - return; - } - - function getHttpRequest() { - if (window.XMLHttpRequest) - return new XMLHttpRequest(); - else if (window.ActiveXObject !== undefined) - return new ActiveXObject("MsXml2.XmlHttp"); - else{ - $data.Trace.log('XMLHttpRequest or MsXml2.XmlHttp ActiveXObject not found'); - callback(false); - return; - } - } - - var oXmlHttp = getHttpRequest(); - oXmlHttp.onreadystatechange = function () { - $data.Trace.log('HTTP request is in state: ' + oXmlHttp.readyState); - if (oXmlHttp.readyState == 4) { - if (oXmlHttp.status == 200 || oXmlHttp.status == 304) { - $data.Trace.log('HTTP request succeeded'); - $data.Trace.log('HTTP request response text: ' + oXmlHttp.responseText); - eval.call(window, oXmlHttp.responseText); - if (typeof callback === 'function') - callback(true); - else $data.Trace.log('Callback function is undefined'); - } else { - $data.Trace.log('HTTP request status: ', oXmlHttp.status); - if (typeof callback === 'function') - callback(false); - else $data.Trace.log('Callback function is undefined'); - } - } - }; - oXmlHttp.open('GET', url, true); - oXmlHttp.send(null); - }, - loadScriptElement: function (url, currentProvider, callback) { - var head = document.getElementsByTagName('head')[0] || document.documentElement; - - var script = document.createElement('script'); - script.type = 'text/javascript'; - script.src = url; - $data.Trace.log('Appending child ' + script + ' to ' + head); - head.appendChild(script); - - var loadInterval = this.scriptLoadInterval || 50; - var iteration = Math.ceil(this.scriptLoadTimeout / loadInterval); - $data.Trace.log('Script element watcher iterating ' + iteration + ' times'); - function watcher() { - $data.Trace.log('Script element watcher iteration ' + iteration); - var provider = $data.RegisteredStorageProviders[currentProvider]; - if (provider) { - $data.Trace.log(currentProvider + ' provider registered'); - callback(true); - } else { - iteration--; - if (iteration > 0) { - $data.Trace.log('Script element watcher next iteration'); - setTimeout(watcher, loadInterval); - } else { - $data.Trace.log('Script element loader failed'); - callback(false); - } - } - } - setTimeout(watcher, loadInterval); - }, - - loadNpmModule: function (currentProvider, providerList, callback) { - var provider = null; - try { - require(this.npmModules[currentProvider]); - provider = $data.RegisteredStorageProviders[currentProvider]; - $data.Trace.log('NPM module loader successfully registered ' + currentProvider + ' provider'); - } catch (e) { - $data.Trace.log('NPM module loader failed for ' + currentProvider + ' provider'); - } - - if (provider) { - callback.success(provider); - } else if (providerList.length > 0) { - this.find(providerList, callback); - } else { - callback.error(); - } - }, - - virtualProviders: { - type: $data.Array, - value: { - local: { - fallbacks: ['webSql', 'indexedDb', 'LocalStore'] - } - } - }, - getVirtual: function(name){ - if(this.virtualProviders[name]) - return [].concat(this.virtualProviders[name].fallbacks); - - return name; - } -}); - -$data.StorageProviderLoader = new $data.StorageProviderLoaderBase(); -$data.storageProviders = { - DbCreationType: { - Merge: 10, - DropTableIfChanged: 20, - DropTableIfChange: 20, - DropAllExistingTables: 30, - ErrorIfChange: 40, - DropDbIfChange: 50 - } -} - -$data.ConcurrencyMode = { Fixed: 'fixed', None: 'none' }; -$data.Class.define('$data.StorageProviderBase', null, null, -{ - constructor: function (schemaConfiguration, context) { - this.providerConfiguration = schemaConfiguration || {}; - - this.name = this.getType().name; - if ($data.RegisteredStorageProviders) { - var keys = Object.keys($data.RegisteredStorageProviders); - for (var i = 0; i < keys.length; i++) { - if (this instanceof $data.RegisteredStorageProviders[keys[i]]) { - this.name = keys[i]; - break; - } - } - } - }, - providers: {}, - supportedDataTypes: { value: [], writable: false }, - initializeStore: function (callBack) { - Guard.raise("Pure class"); - }, - - executeQuery: function (queryable, callBack) { - Guard.raise("Pure class"); - }, - loadRawData: function (tableName, callBack) { - callBack = $data.typeSystem.createCallbackSetting(callBack); - callBack.error(new Exception('loadRawData is not supported', 'Invalid Operation')); - }, - - buildIndependentBlocks: function (changedItems) { - /// - /// Build and processes a dependency graph from the changed items, - /// and generates blocks that can be inserted to the database sequentially. - /// - /// Array of changed items to build independent blocks from. - var edgesTo = []; - var edgesFrom = []; - - function hasOwnProperty(obj) { - /// - /// Returns true if object has own property (used for 'hashset'-like objects) - /// - /// Target object - /// True if the object has own property - for (var p in obj) { - if (obj.hasOwnProperty(p)) - return true; - } - return false; - } - - // Building edgesTo and edgesFrom arrays (containing only indeces of items in changedItems array. - for (var i = 0; i < changedItems.length; i++) { - var current = changedItems[i]; - if (!current.dependentOn || current.dependentOn.length == 0) { - // This item is independent - continue; - } - - var to = null; - // Iterating over items 'current' depends on - for (var j = 0; j < current.dependentOn.length; j++) { - var currentDependency = current.dependentOn[j]; - if (currentDependency.entityState == $data.EntityState.Unchanged) { - continue; - } - to = to || {}; - // Getting the index of current dependency - var ixDependendOn = -1; - for (var k = 0; k < changedItems.length; k++) { - if (changedItems[k].data == currentDependency) { - ixDependendOn = k; - break; - } - } - // Sanity check - if (ixDependendOn == -1) { - Guard.raise(new Exception('Dependent object not found', 'ObjectNotFound', current.dependentOn[j])); - } - // Setting edge in 'to' array - to[ixDependendOn] = true; - // Setting edge in 'from' array - from = edgesFrom[ixDependendOn] || {}; - from[i] = true; - edgesFrom[ixDependendOn] = from; - } - // Persisting found edges in edgesTo array - if (to !== null) - edgesTo[i] = to; - } - - // Array of sequentialyl independent blocks (containing objects, not just their id's) - var independentBlocks = []; - // Objects getting their dependency resolved in the current cycle. - var currentBlock = []; - // Filling currentBlock with initially independent objects. - for (var x = 0; x < changedItems.length; x++) { - if (!edgesTo.hasOwnProperty(x)) { - currentBlock.push(x); - } - } - while (currentBlock.length > 0) { - // Shifting currentBlock to cbix, - // and clearing currentBlock for next independent block - var cbix = [].concat(currentBlock); - currentBlock = []; - // Iterating over previous independent block, to generate the new one - for (var b = 0; b < cbix.length; b++) { - var dependentNodes = edgesFrom[cbix[b]]; - if (typeof dependentNodes !== 'undefined') { - for (var d in dependentNodes) { - // Removing edge from 'edgesTo' - delete edgesTo[d][cbix[b]]; - // Check if has any more dependency - if (!hasOwnProperty(edgesTo[d])) { - // It doesn't, so let's clean up a bit - delete edgesTo[d]; - // and push the item to 'currentBlock' - currentBlock.push(d); - } - } - } - // Clearing processed item from 'edgesFrom' - delete edgesFrom[cbix[b]]; - } - // Push cbix t to independentBlocks - var cb = []; - for (var c = 0; c < cbix.length; c++) { - var item = changedItems[cbix[c]]; - if (item.data.entityState != $data.EntityState.Unchanged) - cb.push(item); - } - if (cb.length > 0) - independentBlocks.push(cb); - } - return independentBlocks; - }, - getTraceString: function (queryable) { - Guard.raise("Pure class"); - }, - setContext: function (ctx) { - this.context = ctx; - }, - - _buildContinuationFunction: function (context, query) { - if (Array.isArray(query.result)) { - query.result.next = this._buildPagingMethod(context, query, 'next'); - query.result.prev = this._buildPagingMethod(context, query, 'prev'); - } - }, - _buildPagingMethod: function (context, query, mode) { - return function (onResult_items) { - var pHandler = new $data.PromiseHandler(); - var cbWrapper = pHandler.createCallback(onResult_items); - - var continuation = new $data.Expressions.ContinuationExpressionBuilder(mode); - var continuationResult = continuation.compile(query); - if (continuationResult.expression) { - var queryable = Container.createQueryable(context, continuationResult.expression); - queryable.defaultType = query.defaultType; - context.executeQuery(queryable, cbWrapper); - } else { - cbWrapper.error(new Exception(continuationResult.message, 'Invalid Operation', continuationResult)); - } - - return pHandler.getPromise(); - } - }, - - buildDbType_modifyInstanceDefinition: function (instanceDefinition, storageModel) { - var buildDbType_copyPropertyDefinition = function (propertyDefinition, refProp) { - var cPropertyDef; - if (refProp) { - cPropertyDef = JSON.parse(JSON.stringify(instanceDefinition[refProp])); - cPropertyDef.kind = propertyDefinition.kind; - cPropertyDef.name = propertyDefinition.name; - cPropertyDef.notMapped = false; - } else { - cPropertyDef = JSON.parse(JSON.stringify(propertyDefinition)); - } - - cPropertyDef.dataType = Container.resolveType(propertyDefinition.dataType); - cPropertyDef.type = cPropertyDef.dataType; - cPropertyDef.key = false; - cPropertyDef.computed = false; - return cPropertyDef; - }; - var buildDbType_createConstrain = function (foreignType, dataType, propertyName, prefix, keyPropertyName) { - var constrain = new Object(); - constrain[foreignType.name] = propertyName; - constrain[dataType.name] = keyPropertyName ? keyPropertyName : prefix + '__' + propertyName; - return constrain; - }; - - if (storageModel.Associations) { - storageModel.Associations.forEach(function (association) { - var addToEntityDef = false; - var foreignType = association.FromType; - var dataType = association.ToType; - var foreignPropName = association.ToPropertyName; - - var memDef = association.FromType.getMemberDefinition(association.FromPropertyName); - var keyProperties = []; - if (memDef && typeof memDef.keys === "string" && memDef.keys) { - keyProperties = [memDef.keys]; - } else if (memDef && Array.isArray(memDef.keys)) { - keyProperties = [].concat(memDef.keys); - } - - association.ReferentialConstraint = association.ReferentialConstraint || []; - - if ((association.FromMultiplicity == "*" && association.ToMultiplicity == "0..1") || (association.FromMultiplicity == "0..1" && association.ToMultiplicity == "1")) { - foreignType = association.ToType; - dataType = association.FromType; - foreignPropName = association.FromPropertyName; - addToEntityDef = true; - } - - foreignType.memberDefinitions.getPublicMappedProperties().filter(function (d) { return d.key }).forEach(function (d, i) { - var constraint = buildDbType_createConstrain(foreignType, dataType, d.name, foreignPropName, keyProperties[i]); - if (addToEntityDef) { - //instanceDefinition[foreignPropName + '__' + d.name] = buildDbType_copyPropertyDefinition(d, foreignPropName); - instanceDefinition[constraint[dataType.name]] = buildDbType_copyPropertyDefinition(d, foreignPropName); - - var dependentMemDef = dataType.getMemberDefinition(keyProperties[i]); - if (dependentMemDef) { - dependentMemDef.isDependentProperty = true; - dependentMemDef.navigationPropertyName = association.FromPropertyName; - } - } - association.ReferentialConstraint.push(constraint); - }, this); - }, this); - } - //Copy complex type properties - if (storageModel.ComplexTypes) { - storageModel.ComplexTypes.forEach(function (complexType) { - complexType.ReferentialConstraint = complexType.ReferentialConstraint || []; - - complexType.ToType.memberDefinitions.getPublicMappedProperties().forEach(function (d) { - instanceDefinition[complexType.FromPropertyName + '__' + d.name] = buildDbType_copyPropertyDefinition(d); - complexType.ReferentialConstraint.push(buildDbType_createConstrain(complexType.ToType, complexType.FromType, d.name, complexType.FromPropertyName)); - }, this); - }, this); - } - }, - buildDbType_generateConvertToFunction: function (storageModel) { - return function (logicalEntity) { - var dbInstance = new storageModel.PhysicalType(); - dbInstance.entityState = logicalEntity.entityState; - - //logicalEntity.changedProperties.forEach(function(memberDef){ - //}, this); - storageModel.PhysicalType.memberDefinitions.getPublicMappedProperties().forEach(function (property) { - if (logicalEntity[property.name] !== undefined) { - dbInstance[property.name] = logicalEntity[property.name]; - } - }, this); - - if (storageModel.Associations) { - storageModel.Associations.forEach(function (association) { - if ((association.FromMultiplicity == "*" && association.ToMultiplicity == "0..1") || (association.FromMultiplicity == "0..1" && association.ToMultiplicity == "1")) { - var complexInstance = logicalEntity[association.FromPropertyName]; - if (complexInstance !== undefined) { - association.ReferentialConstraint.forEach(function (constrain) { - if (complexInstance !== null) { - dbInstance[constrain[association.From]] = complexInstance[constrain[association.To]]; - } else { - dbInstance[constrain[association.From]] = null; - } - }, this); - } - } - }, this); - } - if (storageModel.ComplexTypes) { - storageModel.ComplexTypes.forEach(function (cmpType) { - var complexInstance = logicalEntity[cmpType.FromPropertyName]; - if (complexInstance !== undefined) { - cmpType.ReferentialConstraint.forEach(function (constrain) { - if (complexInstance !== null) { - dbInstance[constrain[cmpType.From]] = complexInstance[constrain[cmpType.To]]; - } else { - dbInstance[constrain[cmpType.From]] = null; - } - }, this); - } - }, this); - } - return dbInstance; - }; - }, - - bulkInsert: function (a, b, c, callback) { - callback.error(new Exception('Not Implemented')); - }, - - supportedFieldOperations: { - value: { - length: { dataType: "number", allowedIn: "filter, map" }, - substr: { dataType: "string", allowedIn: "filter", parameters: [{ name: "startFrom", dataType: "number" }, { name: "length", dataType: "number" }] }, - toLowerCase: { dataType: "string" } - }, - enumerable: true, - writable: true - }, - - resolveFieldOperation: function (operationName, expression, frameType) { - /// - var result = this.supportedFieldOperations[operationName]; - if (Array.isArray(result)) { - var i = 0; - for (; i < result.length; i++) { - if (result[i].allowedType === 'default' || Container.resolveType(result[i].allowedType) === Container.resolveType(expression.selector.memberDefinition.type) && - (frameType && result[i].allowedIn && - ( - (Array.isArray(result[i].allowedIn) && result[i].allowedIn.some(function(type){ return frameType === Container.resolveType(type); })) || - (!Array.isArray(result[i].allowedIn) && (frameType === Container.resolveType(result[i].allowedIn))) - ) - ) - ) { - result = result[i]; - break; - } - } - if (i === result.length) { - result = undefined; - } - } - - if (!result) { - Guard.raise(new Exception("Field operation '" + operationName + "' is not supported by the provider")); - }; - if (frameType && result.allowedIn) { - if ((result.allowedIn instanceof Array && !result.allowedIn.some(function (type) { return frameType === Container.resolveType(type); })) || - (!(result.allowedIn instanceof Array) && frameType !== Container.resolveType(result.allowedIn))) { - Guard.raise(new Exception(operationName + " not supported in: " + frameType.name)); - } - } - result.name = operationName; - return result; - }, - - supportedBinaryOperators: { - value: { - equal: { mapTo: 'eq', dataType: "boolean" } - }, - enumerable: true, - writable: true - }, - - resolveBinaryOperator: function (operator, expression, frameType) { - var result = this.supportedBinaryOperators[operator]; - if (!result) { - Guard.raise(new Exception("Binary operator '" + operator + "' is not supported by the provider")); - }; - if (frameType && result.allowedIn) { - if ((result.allowedIn instanceof Array && !result.allowedIn.some(function (type) { return frameType === Container.resolveType(type); })) || - (!(result.allowedIn instanceof Array) && frameType !== Container.resolveType(result.allowedIn))) { - Guard.raise(new Exception(operator + " not supported in: " + frameType.name)); - } - } - result.name = operator; - return result; - }, - - supportedUnaryOperators: { - value: { - not: { mapTo: 'not' } - }, - enumerable: true, - writable: true - }, - resolveUnaryOperator: function (operator, expression, frameType) { - var result = this.supportedUnaryOperators[operator]; - if (!result) { - Guard.raise(new Exception("Unary operator '" + operator + "' is not supported by the provider")); - }; - if (frameType && result.allowedIn) { - if ((result.allowedIn instanceof Array && !result.allowedIn.some(function (type) { return frameType === Container.resolveType(type); })) || - (!(result.allowedIn instanceof Array) && frameType !== Container.resolveType(result.allowedIn))) { - Guard.raise(new Exception(operator + " not supported in: " + frameType.name)); - } - } - result.name = operator; - return result; - }, - - supportedSetOperations: { - value: { - toArray: { invokable: true, allowedIn: [] } - }, - enumerable: true, - writable: true - }, - resolveSetOperations: function (operation, expression, frameType) { - var result = this.supportedSetOperations[operation]; - if (!result) { - Guard.raise(new Exception("Operation '" + operation + "' is not supported by the provider")); - }; - var allowedIn = result.allowedIn || []; - if (frameType && allowedIn) { - if ((allowedIn instanceof Array && !allowedIn.some(function (type) { return frameType === Container.resolveType(type); })) || - (!(allowedIn instanceof Array) && frameType !== Container.resolveType(allowedIn))) { - Guard.raise(new Exception(operation + " not supported in: " + frameType.name)); - } - } - return result; - }, - - resolveTypeOperations: function (operation, expression, frameType) { - Guard.raise(new Exception("Entity '" + expression.entityType.name + "' Operation '" + operation + "' is not supported by the provider")); - }, - - resolveContextOperations: function (operation, expression, frameType) { - Guard.raise(new Exception("Context '" + expression.instance.getType().name + "' Operation '" + operation + "' is not supported by the provider")); - }, - - makePhysicalTypeDefinition: function (entityDefinition, association) { - }, - - _beginTran: function (tables, isWrite, callBack) { - callBack.success(new $data.Transaction()); - }, - - getFieldUrl: function () { - return '#'; - }, - - supportedAutoincrementKeys: { - value: { } - } -}, -{ - onRegisterProvider: { value: new $data.Event() }, - registerProvider: function (name, provider) { - this.onRegisterProvider.fire({ name: name, provider: provider }, this); - $data.RegisteredStorageProviders = $data.RegisteredStorageProviders || []; - $data.RegisteredStorageProviders[name] = provider; - }, - getProvider: function (name) { - var provider = $data.RegisteredStorageProviders[name]; - if (!provider) - console.warn("Provider not found: '" + name + "'"); - return provider; - /*var provider = $data.RegisteredStorageProviders[name]; - if (!provider) - Guard.raise(new Exception("Provider not found: '" + name + "'", "Not Found")); - return provider;*/ - }, - isSupported: { - get: function () { return true; }, - set: function () { } - } -}); -$data.Class.define('$data.ServiceOperation', null, null, {}, { - translateDefinition: function (propertyDef, name, definedBy) { - propertyDef.serviceName = name; - var memDef = new $data.MemberDefinition(this.generateServiceOperation(propertyDef), this); - memDef.name = name; - return memDef; - }, - generateServiceOperation: function (cfg) { - - var fn; - if (cfg.serviceMethod) { - var returnType = cfg.returnType ? Container.resolveType(cfg.returnType) : {}; - if (returnType.isAssignableTo && returnType.isAssignableTo($data.Queryable)) { - fn = cfg.serviceMethod; - } else { - fn = function () { - var lastParam = arguments[arguments.length - 1]; - - var pHandler = new $data.PromiseHandler(); - var cbWrapper; - - var args = arguments; - if (typeof lastParam === 'function') { - cbWrapper = pHandler.createCallback(lastParam); - arguments[arguments.length - 1] = cbWrapper; - } else { - cbWrapper = pHandler.createCallback(); - arguments.push(cbWrapper); - } - - try { - var result = cfg.serviceMethod.apply(this, arguments); - if (result !== undefined) - cbWrapper.success(result); - } catch (e) { - cbWrapper.error(e); - } - - return pHandler.getPromise(); - } - } - - } else { - fn = function () { - var context = this; - var memberdef; - - var boundItem; - if (this instanceof $data.Entity || this instanceof $data.EntitySet) { - var entitySet; - if (this instanceof $data.Entity) { - if (this.context) { - context = this.context; - entitySet = context.getEntitySetFromElementType(this.getType()); - } else if (this.storeToken && typeof this.storeToken.factory === 'function') { - context = this.storeToken.factory(); - entitySet = context.getEntitySetFromElementType(this.getType()); - } else { - Guard.raise(new Exception("entity can't resolve context", 'Not Found!', this)); - return; - } - } else if (this instanceof $data.EntitySet) { - context = this.entityContext; - entitySet = this; - - var esDef = context.getType().getMemberDefinition(entitySet.name); - memberdef = $data.MemberDefinition.translateDefinition(esDef.actions[cfg.serviceName], cfg.serviceName, entitySet.getType()); - } - - - boundItem = { - data: this, - entitySet: entitySet - }; - } - - var virtualEntitySet = cfg.elementType ? context.getEntitySetFromElementType(Container.resolveType(cfg.elementType)) : null; - - var paramConstExpression = null; - if (cfg.params) { - paramConstExpression = []; - //object as parameter - if (arguments[0] && typeof arguments[0] === 'object' && arguments[0].constructor === $data.Object && cfg.params && cfg.params[0] && cfg.params[0].name in arguments[0]) { - var argObj = arguments[0]; - for (var i = 0; i < cfg.params.length; i++) { - var paramConfig = cfg.params[i]; - if (paramConfig.name && paramConfig.type && paramConfig.name in argObj) { - paramConstExpression.push(Container.createConstantExpression(argObj[paramConfig.name], Container.resolveType(paramConfig.type), paramConfig.name)); - } - } - } - //arg params - else { - for (var i = 0; i < cfg.params.length; i++) { - if (typeof arguments[i] == 'function') break; - - //TODO: check params type - var paramConfig = cfg.params[i]; - if (paramConfig.name && paramConfig.type && arguments[i] !== undefined) { - paramConstExpression.push(Container.createConstantExpression(arguments[i], Container.resolveType(paramConfig.type), paramConfig.name)); - } - } - } - } - - var ec = Container.createEntityContextExpression(context); - if (!memberdef) { - if (boundItem && boundItem.data) { - memberdef = boundItem.data.getType().getMemberDefinition(cfg.serviceName); - } else { - memberdef = context.getType().getMemberDefinition(cfg.serviceName); - } - } - var es = Container.createServiceOperationExpression(ec, - Container.createMemberInfoExpression(memberdef), - paramConstExpression, - cfg, - boundItem); - - //Get callback function - var clb = arguments[arguments.length - 1]; - if (!(typeof clb === 'function' || (typeof clb === 'object' /*&& clb.constructor === $data.Object*/ && (typeof clb.success === 'function' || typeof clb.error === 'function')))) { - clb = undefined; - } - - if (virtualEntitySet) { - var q = Container.createQueryable(virtualEntitySet, es); - if (clb) { - es.isTerminated = true; - return q._runQuery(clb); - } - return q; - } - else { - var returnType = cfg.returnType ? Container.resolveType(cfg.returnType) : null; - - var q = Container.createQueryable(context, es); - q.defaultType = returnType || $data.Object; - - if (returnType === $data.Queryable) { - q.defaultType = Container.resolveType(cfg.elementType); - if (clb) { - es.isTerminated = true; - return q._runQuery(clb); - } - return q; - } - es.isTerminated = true; - return q._runQuery(clb); - } - }; - }; - - var params = cfg.params || []; - $data.typeSystem.extend(fn, cfg, { params: params }); - - return fn; - } -}); - -$data.Class.define('$data.ServiceAction', $data.ServiceOperation, null, {}, { - generateServiceOperation: function (cfg) { - if (!cfg.method) { - cfg.method = 'POST'; //default Action method is POST - } - - return $data.ServiceOperation.generateServiceOperation.apply(this, arguments); - } -});$data.Base.extend('$data.EntityWrapper', { - getEntity: function () { - Guard.raise("pure object"); - } -}); -if (typeof jQuery !== 'undefined' && jQuery.ajax) { - $data.ajax = $data.ajax || jQuery.ajax; -} - -if (typeof WinJS !== 'undefined' && WinJS.xhr) { - $data.ajax = $data.ajax || function (options) { - $data.typeSystem.extend(options, { - dataType: 'json', - headers: {} - }); - var dataTypes = { - 'json': { - accept: 'application/json, text/javascript', - convert: JSON.parse - }, - 'text': { - accept: 'text/plain', - convert: function (e) { return e; } - }, - 'html': { - accept: 'text/html', - convert: function (e) { return e; } - }, - 'xml': { - accept: 'application/xml, text/xml', - convert: function (e) { - // TODO? - return e; - } - } - } - var dataTypeContext = dataTypes[options.dataType.toLowerCase()]; - - options.headers.Accept = dataTypeContext.accept; - - var successClb = options.success || $data.defaultSuccessCallback; - var errorClb = options.error || $data.defaultErrorCallback; - var progressClb = options.progress; - - var success = function (r) { - var result = dataTypeContext.convert(r.responseText); - successClb(result); - } - var error = function (r) { - var error = dataTypeContext.convert(r.responseText); - errorClb(error); - } - var progress = progressClb; - - WinJS.xhr(options) - .done(success, error, progress); - } -} - -if (typeof Ext !== 'undefined' && typeof Ext.Ajax) { - $data.ajax = $data.ajax || function (options) { - Ext.Ajax.request(options); - }; -} - -$data.ajax = $data.ajax || function () { - var cfg = arguments[arguments.length - 1]; - var clb = $data.typeSystem.createCallbackSetting(cfg); - clb.error("Not implemented"); -}; - - -$C('$data.modelBinder.FindProjectionVisitor', $data.Expressions.EntityExpressionVisitor, null, { - VisitProjectionExpression: function (expression) { - this.projectionExpression = expression; - } -}); - -$C('$data.modelBinder.ModelBinderConfigCompiler', $data.Expressions.EntityExpressionVisitor, null, { - constructor: function (query, includes, oDataProvider) { - this._query = query; - this._includes = includes; - this._isoDataProvider = oDataProvider || false; - }, - VisitSingleExpression: function (expression) { - this._defaultModelBinder(expression); - }, - VisitSomeExpression: function (expression) { - this._defaultModelBinder(expression); - }, - VisitFindExpression: function (expression) { - this._defaultModelBinder(expression); - }, - VisitEveryExpression: function (expression) { - this._defaultModelBinder(expression); - }, - VisitToArrayExpression: function (expression) { - this._defaultModelBinder(expression); - }, - VisitFirstExpression: function (expression) { - this._defaultModelBinder(expression); - }, - VisitForEachExpression: function (expression) { - this._defaultModelBinder(expression); - }, - VisitServiceOperationExpression: function (expression) { - if (expression.cfg.returnType) { - var returnType = Container.resolveType(expression.cfg.returnType); - if ((typeof returnType.isAssignableTo === 'function' && returnType.isAssignableTo($data.Queryable)) || returnType === $data.Array) { - this._defaultModelBinder(expression); - } else { - var builder = Container.createqueryBuilder(); - builder.modelBinderConfig['$type'] = returnType; - if (typeof returnType.isAssignableTo === 'function' && returnType.isAssignableTo($data.Entity)) { - builder.modelBinderConfig['$selector'] = ['json:' + expression.cfg.serviceName]; - } else { - builder.modelBinderConfig['$type'] = returnType; - builder.modelBinderConfig['$value'] = function (a, v) { - return (expression.cfg.serviceName in v) ? v[expression.cfg.serviceName] : v.value; - } - } - this.VisitExpression(expression, builder); - builder.resetModelBinderProperty(); - this._query.modelBinderConfig = builder.modelBinderConfig; - } - } - }, - VisitCountExpression: function (expression) { - var builder = Container.createqueryBuilder(); - - builder.modelBinderConfig['$type'] = $data.Array; - builder.selectModelBinderProperty('$item'); - builder.modelBinderConfig['$type'] = $data.Integer; - builder.modelBinderConfig['$source'] = 'cnt'; - builder.resetModelBinderProperty(); - this._query.modelBinderConfig = builder.modelBinderConfig; - }, - VisitBatchDeleteExpression: function (expression) { - var builder = Container.createqueryBuilder(); - - builder.modelBinderConfig['$type'] = $data.Array; - builder.selectModelBinderProperty('$item'); - builder.modelBinderConfig['$type'] = $data.Integer; - builder.modelBinderConfig['$source'] = 'cnt'; - builder.resetModelBinderProperty(); - this._query.modelBinderConfig = builder.modelBinderConfig; - }, - VisitConstantExpression: function (expression, builder) { - builder.modelBinderConfig['$type'] = expression.type; - builder.modelBinderConfig['$value'] = expression.value; - }, - - VisitExpression: function (expression, builder) { - var projVisitor = Container.createFindProjectionVisitor(); - projVisitor.Visit(expression); - - if (projVisitor.projectionExpression) { - this.Visit(projVisitor.projectionExpression, builder); - } else { - this.DefaultSelection(builder, this._query.defaultType, this._includes); - } - }, - _defaultModelBinder: function (expression) { - var builder = Container.createqueryBuilder(); - builder.modelBinderConfig['$type'] = $data.Array; - if (this._isoDataProvider) { - builder.modelBinderConfig['$selector'] = ['json:d.results', 'json:d', 'json:results']; - } - builder.modelBinderConfig['$item'] = {}; - builder.selectModelBinderProperty('$item'); - - this.VisitExpression(expression, builder); - - builder.resetModelBinderProperty(); - this._query.modelBinderConfig = builder.modelBinderConfig; - }, - _addPropertyToModelBinderConfig: function (elementType, builder) { - var storageModel = this._query.context._storageModel.getStorageModel(elementType); - if (elementType.memberDefinitions) { - elementType.memberDefinitions.getPublicMappedProperties().forEach(function (prop) { - if ((!storageModel) || (storageModel && !storageModel.Associations[prop.name] && !storageModel.ComplexTypes[prop.name])) { - - var type = Container.resolveType(prop.dataType); - if (!storageModel && this._query.context.storageProvider.supportedDataTypes.indexOf(type) < 0) { - //complex type - builder.selectModelBinderProperty(prop.name); - builder.modelBinderConfig['$type'] = type; - if (this._isoDataProvider) { - builder.modelBinderConfig['$selector'] = ['json:' + prop.name + '.results', 'json:' + prop.name]; - } else { - builder.modelBinderConfig['$selector'] = 'json:' + prop.name; - } - this._addPropertyToModelBinderConfig(type, builder); - builder.popModelBinderProperty(); - } else { - if (prop.key) { - builder.addKeyField(prop.name); - } - if (prop.concurrencyMode === $data.ConcurrencyMode.Fixed) { - builder.modelBinderConfig[prop.name] = { $selector: 'json:__metadata', $source: 'etag' } - } else if (type === $data.Array && prop.elementType) { - builder.selectModelBinderProperty(prop.name); - builder.modelBinderConfig['$type'] = type; - if (this._isoDataProvider) { - builder.modelBinderConfig['$selector'] = ['json:' + prop.name + '.results', 'json:' + prop.name]; - } else { - builder.modelBinderConfig['$selector'] = 'json:' + prop.name; - } - builder.selectModelBinderProperty('$item'); - var arrayElementType = Container.resolveType(prop.elementType); - builder.modelBinderConfig['$type'] = arrayElementType; - this._addPropertyToModelBinderConfig(arrayElementType, builder); - builder.popModelBinderProperty(); - builder.popModelBinderProperty(); - } else { - builder.modelBinderConfig[prop.name] = prop.name; - } - } - } - }, this); - } else { - /*builder._binderConfig = { - $selector: ['json:results'], - $type: $data.Array, - $item:{ - $type: elementType, - $value: function (meta, data) { return data; } - } - }*/ - builder._binderConfig.$item = builder._binderConfig.$item || {}; - builder.modelBinderConfig = builder._binderConfig.$item; - - - - } - if (storageModel) { - this._addComplexTypeProperties(storageModel.ComplexTypes, builder); - } - }, - _addComplexTypeProperties: function (complexTypes, builder) { - complexTypes.forEach(function (ct) { - if (ct.ToType !== $data.Array){ - builder.selectModelBinderProperty(ct.FromPropertyName); - builder.modelBinderConfig['$type'] = ct.ToType; - if (this._isoDataProvider) { - builder.modelBinderConfig['$selector'] = ['json:' + ct.FromPropertyName + '.results', 'json:' + ct.FromPropertyName]; - } else { - builder.modelBinderConfig['$selector'] = 'json:' + ct.FromPropertyName; - } - this._addPropertyToModelBinderConfig(ct.ToType, builder); - - builder.popModelBinderProperty(); - }else{ - var dt = ct.ToType; - var et = Container.resolveType(ct.FromType.memberDefinitions.getMember(ct.FromPropertyName).elementType); - if (dt === $data.Array && et && et.isAssignableTo && et.isAssignableTo($data.Entity)){ - config = { - $type: $data.Array, - $selector: 'json:' + ct.FromPropertyName, - $item: { - $type: et - } - }; - var md = et.memberDefinitions.getPublicMappedProperties(); - for (var i = 0; i < md.length; i++){ - config.$item[md[i].name] = { $type: md[i].type, $source: md[i].name }; - } - builder.modelBinderConfig[ct.FromPropertyName] = config; - }else{ - builder.modelBinderConfig[ct.FromPropertyName] = { - $type: ct.ToType, - $source: ct.FromPropertyName - }; - } - } - }, this); - }, - DefaultSelection: function (builder, type, allIncludes) { - //no projection, get all item from entitySet - builder.modelBinderConfig['$type'] = type; - - var storageModel = this._query.context._storageModel.getStorageModel(type); - this._addPropertyToModelBinderConfig(type, builder); - if (allIncludes) { - allIncludes.forEach(function (include) { - var includes = include.name.split('.'); - var association = null; - var tmpStorageModel = storageModel; - var itemCount = 0; - for (var i = 0; i < includes.length; i++) { - if (builder.modelBinderConfig.$item) { - builder.selectModelBinderProperty('$item'); - itemCount++; - } - builder.selectModelBinderProperty(includes[i]); - association = tmpStorageModel.Associations[includes[i]]; - tmpStorageModel = this._query.context._storageModel.getStorageModel(association.ToType); - } - if (this._isoDataProvider) { - builder.modelBinderConfig['$selector'] = ['json:' + includes[includes.length - 1] + '.results', 'json:' + includes[includes.length - 1]]; - } else { - builder.modelBinderConfig['$selector'] = 'json:' + includes[includes.length - 1]; - } - if (association.ToMultiplicity === '*') { - builder.modelBinderConfig['$type'] = $data.Array; - builder.selectModelBinderProperty('$item'); - builder.modelBinderConfig['$type'] = include.type; - this._addPropertyToModelBinderConfig(include.type, builder); - builder.popModelBinderProperty(); - } else { - builder.modelBinderConfig['$type'] = include.type; - this._addPropertyToModelBinderConfig(include.type, builder); - } - - for (var i = 0; i < includes.length + itemCount; i++) { - builder.popModelBinderProperty(); - } - }, this); - } - }, - VisitProjectionExpression: function (expression, builder) { - this.hasProjection = true; - this.Visit(expression.selector, builder); - - if (expression.selector && expression.selector.expression instanceof $data.Expressions.ObjectLiteralExpression) { - builder.modelBinderConfig['$type'] = expression.projectionAs || builder.modelBinderConfig['$type'] || $data.Object; - } - }, - VisitParametricQueryExpression: function (expression, builder) { - if (expression.expression instanceof $data.Expressions.EntityExpression || expression.expression instanceof $data.Expressions.EntitySetExpression) { - this.VisitEntityAsProjection(expression, builder); - } else { - this.Visit(expression.expression, builder); - } - - }, - VisitEntityAsProjection: function (expression, builder) { - this.mapping = ''; - this.Visit(expression.expression, builder); - var includes; - if (this.mapping && this._includes instanceof Array) { - includes = this._includes.filter(function (inc) { - return inc.name.indexOf(this.mapping + '.') === 0 - }, this); - includes = includes.map(function (inc) { - return { name: inc.name.replace(this.mapping + '.', ''), type: inc.type }; - }, this); - - if (includes.length > 0){ - this.DefaultSelection(builder, expression.expression.entityType, includes); - //console.warn('WARN: include for mapped properties is not supported!'); - } - } - - if (expression.expression instanceof $data.Expressions.EntityExpression) { - this.DefaultSelection(builder, expression.expression.entityType/*, includes*/) - } else if (expression.expression instanceof $data.Expressions.EntitySetExpression) { - builder.modelBinderConfig.$type = $data.Array; - builder.modelBinderConfig.$item = {}; - builder.selectModelBinderProperty('$item'); - this.DefaultSelection(builder, expression.expression.elementType /*, includes*/) - builder.popModelBinderProperty(); - } - - }, - - VisitEntityFieldExpression: function (expression, builder) { - this.Visit(expression.source, builder); - this.Visit(expression.selector, builder); - }, - VisitMemberInfoExpression: function (expression, builder) { - builder.modelBinderConfig['$type'] = expression.memberDefinition.type; - if (expression.memberDefinition.storageModel && expression.memberName in expression.memberDefinition.storageModel.ComplexTypes) { - this._addPropertyToModelBinderConfig(Container.resolveType(expression.memberDefinition.type), builder); - } else { - if (!(builder.modelBinderConfig.$type && Container.resolveType(builder.modelBinderConfig.$type).isAssignableTo && Container.resolveType(builder.modelBinderConfig.$type).isAssignableTo($data.Entity))) - builder.modelBinderConfig['$source'] = expression.memberName; - } - }, - VisitEntitySetExpression: function (expression, builder) { - if (expression.source instanceof $data.Expressions.EntityExpression) { - this.Visit(expression.source, builder); - this.Visit(expression.selector, builder); - } - - }, - VisitComplexTypeExpression: function (expression, builder) { - this.Visit(expression.source, builder); - this.Visit(expression.selector, builder); - - - if (('$selector' in builder.modelBinderConfig) && (builder.modelBinderConfig.$selector.length > 0)) { - if (builder.modelBinderConfig.$selector instanceof $data.Array) { - var temp = builder.modelBinderConfig.$selector[1]; - builder.modelBinderConfig.$selector[0] = temp + '.' + expression.selector.memberName + '.results'; - builder.modelBinderConfig.$selector[1] = temp + '.' + expression.selector.memberName; - } else { - builder.modelBinderConfig.$selector += '.' + expression.selector.memberName; - } - - } else { - if (this._isoDataProvider) { - builder.modelBinderConfig['$selector'] = ['json:' + expression.selector.memberName + '.results', 'json:' + expression.selector.memberName]; - } else { - builder.modelBinderConfig['$selector'] = 'json:' + expression.selector.memberName; - } - } - }, - VisitEntityExpression: function (expression, builder) { - this.Visit(expression.source, builder); - }, - VisitAssociationInfoExpression: function (expression, builder) { - if (('$selector' in builder.modelBinderConfig) && (builder.modelBinderConfig.$selector.length > 0)) { - if (builder.modelBinderConfig.$selector instanceof $data.Array) { - var temp = builder.modelBinderConfig.$selector[1]; - builder.modelBinderConfig.$selector[0] = temp + '.' + expression.associationInfo.FromPropertyName + '.results'; - builder.modelBinderConfig.$selector[1] = temp + '.' + expression.associationInfo.FromPropertyName; - } else { - builder.modelBinderConfig.$selector += '.' + expression.associationInfo.FromPropertyName; - } - - } else { - if (this._isoDataProvider) { - builder.modelBinderConfig['$selector'] = ['json:' + expression.associationInfo.FromPropertyName + '.results', 'json:' + expression.associationInfo.FromPropertyName]; - } else { - builder.modelBinderConfig['$selector'] = 'json:' + expression.associationInfo.FromPropertyName; - } - } - - if (this.mapping && this.mapping.length > 0) { this.mapping += '.'; } - this.mapping += expression.associationInfo.FromPropertyName; - }, - VisitObjectLiteralExpression: function (expression, builder) { - builder.modelBinderConfig['$type'] = $data.Object; - expression.members.forEach(function (of) { - this.Visit(of, builder); - }, this); - }, - VisitObjectFieldExpression: function (expression, builder) { - builder.selectModelBinderProperty(expression.fieldName); - if (expression.expression instanceof $data.Expressions.EntityExpression || expression.expression instanceof $data.Expressions.EntitySetExpression) { - this.VisitEntityAsProjection(expression, builder); - } else { - this.Visit(expression.expression, builder); - } - builder.popModelBinderProperty(); - } -}); -$data.Class.define("$data.Authentication.AuthenticationBase", null, null, { - constructor: function (cfg) { - this.configuration = cfg || {}; - this.Authenticated = false; - }, - /// { error:, abort:, pending:, success: } - Login: function (callbacks) { - Guard.raise("Pure class"); - }, - Logout: function () { - Guard.raise("Pure class"); - }, - CreateRequest: function (cfg) { - Guard.raise("Pure class"); - } - -}, null);$data.Class.define("$data.Authentication.Anonymous", $data.Authentication.AuthenticationBase, null, { - constructor: function (cfg) { - this.configuration = cfg || {}; - this.Authenticated = false; - }, - /// { error:, abort:, pending:, success: } - Login: function (callbacks) { - }, - Logout: function () { - }, - CreateRequest: function (cfg) { - $data.ajax(cfg); - } - -}, null);$data.Class.define("$data.Authentication.FacebookAuth", $data.Authentication.AuthenticationBase, null, { - constructor: function (cfg) { - this.configuration = $data.typeSystem.extend({ - Url_code: '', - type_code: '', - scope: '', - Url_token: '', - type_token: '', - access_token: '', - app_id: '' - }, cfg); - }, - Login: function (callbacks) { - if (this.Authenticated) { - return; - } - - var provider = this; - provider.configuration.stateCallbacks = callbacks || {}; - - $data.ajax({ - url: this.configuration.Url_code, - data: 'type=' + provider.configuration.type_code + '&client_id=' + provider.configuration.app_id + '&scope=' + provider.configuration.scope, - type: 'POST', - dataType: 'json', - success: function (data) { - if (typeof provider.configuration.stateCallbacks.pending == "function") - provider.configuration.stateCallbacks.pending(data); - provider._processRequestToken(data); - provider.Authenticated = true; - }, - error: function () { - if (typeof provider.configuration.stateCallbacks.error == "function") - provider.configuration.stateCallbacks.error(arguments); - } - }); - }, - Logout: function () { - this.Authenticated = false; - }, - CreateRequest: function (cfg) { - if (!cfg) - return; - var _this = this; - - if (cfg.url.indexOf('access_token=') === -1) { - if (cfg.url && this.Authenticated) { - var andChar = '?'; - if (cfg.url.indexOf(andChar) > 0) - andChar = '&'; - - if (this.configuration.access_token) - cfg.url = cfg.url + andChar + 'access_token=' + this.configuration.access_token; - } - } - - $data.ajax(cfg); - }, - _processRequestToken: function (verification_data) { - var provider = this; - - $data.ajax({ - url: provider.configuration.Url_token, - data: 'type=' + provider.configuration.type_token + '&client_id=' + provider.configuration.app_id + '&code=' + verification_data.code, - type: 'POST', - dataType: 'json', - success: function(result) { - provider.configuration.access_token = result.access_token; - if (typeof provider.configuration.stateCallbacks.success == "function") - provider.configuration.stateCallbacks.success(result); - }, - error: function(obj) { - var data = eval('(' + obj.responseText + ')'); - if (data.error) { - if (data.error.message == "authorization_pending") { - setTimeout(function() { - provider._processRequestToken(verification_data); - }, 2000); - } else if ("authorization_declined") { - if (typeof provider.configuration.stateCallbacks.abort == "function") - provider.configuration.stateCallbacks.abort(arguments); - } - } - } - }); - } -}, null);$data.Class.define("$data.Authentication.BasicAuth.BasicAuth", $data.Authentication.AuthenticationBase, null, { - constructor: function (cfg) { - this.configuration = $data.typeSystem.extend({ - Username: '', - Password: '' - }, cfg); - }, - Login: function (callbacks) { - if (callbacks && typeof callbacks.pending == "function") - callbacks.pending(); - }, - Logout: function () { - }, - CreateRequest: function (cfg) { - if (!cfg) - return; - var _this = this; - - var origBeforeSend = cfg.beforeSend; - cfg.beforeSend = function (xhr) { - xhr.setRequestHeader("Authorization", "Basic " + _this.__encodeBase64(_this.configuration.Username + ":" + _this.configuration.Password)); - - if(typeof origBeforeSend == "function") - origBeforeSend(xhr); - }; - - $data.ajax(cfg); - }, - __encodeBase64: function (val) { - var b64array = "ABCDEFGHIJKLMNOP" + - "QRSTUVWXYZabcdef" + - "ghijklmnopqrstuv" + - "wxyz0123456789+/" + - "="; - - input = val; - var base64 = ""; - var hex = ""; - var chr1, chr2, chr3 = ""; - var enc1, enc2, enc3, enc4 = ""; - var i = 0; - - do { - chr1 = input.charCodeAt(i++); - chr2 = input.charCodeAt(i++); - chr3 = input.charCodeAt(i++); - - enc1 = chr1 >> 2; - enc2 = ((chr1 & 3) << 4) | (chr2 >> 4); - enc3 = ((chr2 & 15) << 2) | (chr3 >> 6); - enc4 = chr3 & 63; - - if (isNaN(chr2)) { - enc3 = enc4 = 64; - } else if (isNaN(chr3)) { - enc4 = 64; - } - - base64 = base64 + - b64array.charAt(enc1) + - b64array.charAt(enc2) + - b64array.charAt(enc3) + - b64array.charAt(enc4); - chr1 = chr2 = chr3 = ""; - enc1 = enc2 = enc3 = enc4 = ""; - } while (i < input.length); - - return base64; - } -}, null); -$data.Class.define('$data.MetadataLoaderClass', null, null, { - load: function (metadataUri, callBack, config) { - - var cnf = { - EntityBaseClass: '$data.Entity', - ContextBaseClass: '$data.EntityContext', - AutoCreateContext: false, - DefaultNamespace: ('ns' + Math.random()).replace('.', '') + metadataUri.replace(/[^\w]/g, "_"), - ContextInstanceName: 'context', - EntitySetBaseClass: '$data.EntitySet', - CollectionBaseClass: 'Array', - url: metadataUri, - user: undefined, - password: undefined, - withCredentials: undefined, - httpHeaders: undefined, - - typeFilter: '', - navigation: true, - generateKeys: true, - dependentRelationsOnly: false - }; - - $data.typeSystem.extend( cnf, config || {}); - - if (cnf.DefaultNamespace && cnf.DefaultNamespace.lastIndexOf('.') !== (cnf.DefaultNamespace.length - 1)) - cnf.DefaultNamespace += '.'; - - this.factoryCache = this.factoryCache || {}; - callBack = $data.typeSystem.createCallbackSetting(callBack); - - if (metadataUri in this.factoryCache) { - - /*console.log("served from cache"); - console.dir(this.factoryCache[metadataUri]);*/ - callBack.success.apply({}, this.factoryCache[metadataUri]); - return; - } - - - - - var metadataUri; - if (cnf.url) { - cnf.SerivceUri = cnf.url.replace('/$metadata', ''); - if (cnf.url.indexOf('/$metadata') === -1) { - cnf.metadataUri = cnf.url.replace(/\/+$/, '') + '/$metadata'; - } else { - cnf.metadataUri = cnf.url; - } - } else { - callBack.error('metadata url is missing'); - } - - var self = this; - self._loadXMLDoc(cnf, function (xml, response) { - if (response.statusCode < 200 || response.statusCode > 299) { - callBack.error(response); - return; - } - - var versionInfo = self._findVersion(xml); - if (self.xsltRepoUrl) { - console.log('XSLT: ' + self.xsltRepoUrl + self._supportedODataVersionXSLT) - self._loadXMLDoc({ - metadataUri: self.xsltRepoUrl + self._supportedODataVersionXSLT, - user: cnf.user, - password: cnf.password, - httpHeaders: cnf.httpHeaders - }, function (xsl, response) { - if (response.statusCode < 200 || response.statusCode > 299) { - callBack.error(response); - return; - } - var text = response.responseText; - text = text.replace('xmlns:edm="@@VERSIONNS@@"', 'xmlns:edm="' + versionInfo.ns + '"'); - text = text.replace('@@VERSION@@', versionInfo.version); - - if (window.ActiveXObject === undefined) { - var parser = new DOMParser(); - xsl = parser.parseFromString(text, "text/xml"); - } else { - xsl = new ActiveXObject("Microsoft.XMLDOM"); - xsl.async = false; - xsl.loadXML(text); - } - - self._transform(callBack, versionInfo, xml, xsl, cnf); - }); - } else { - self._transform(callBack, versionInfo, xml, undefined, cnf); - } - - }); - }, - debugMode: { type: 'bool', value: false }, - xsltRepoUrl: { type: 'string', value: '' }, - - createFactoryFunc: function (ctxType, cnf, versionInfo) { - var self = this; - return function (config) { - if (ctxType) { - var cfg = $data.typeSystem.extend({ - name: 'oData', - oDataServiceHost: cnf.SerivceUri, - //maxDataServiceVersion: '', - user: cnf.user, - password: cnf.password, - withCredentials: cnf.withCredentials, - maxDataServiceVersion: versionInfo.maxVersion || '3.0' - }, config) - - - return new ctxType(cfg); - } else { - return null; - } - } - }, - - _transform: function (callBack, versionInfo, xml, xsl, cnf) { - var self = this; - var codeText = self._processResults(cnf.url, versionInfo, xml, xsl, cnf); - - try { - eval(codeText); - } catch (e) { - callBack.error(new Exception('SyntaxError', 'Unexpected model', [e, codeText])); - return; - } - - var ctxType; - if (!$data.generatedContexts || !(ctxType = $data.generatedContexts.pop())) { - callBack.error(new Exception('No context found in service', 'Not found', [cnf, codeText])); - return; - } - - var factoryFn = self.createFactoryFunc(ctxType, cnf, versionInfo); - this.factoryCache[cnf.url] = [factoryFn, ctxType]; - - factoryFn.type = ctxType; - - if (self.debugMode) { - factoryFn.codeText = codeText; - callBack.success(factoryFn, ctxType, codeText); - } - else { - callBack.success(factoryFn, ctxType); - } - }, - _loadXMLDoc: function (cnf, callback) { - var that = this; - if ($data.postMessageODataHandler) { - - if (cnf.user && cnf.password && (!cnf.httpHeaders || (cnf.httpHeaders && !cnf.httpHeaders['Authorization']))) { - httpHeader = httpHeader || {}; - httpHeader["Authorization"] = "Basic " + this.__encodeBase64(cnf.user + ":" + cnf.password); - } - - $data.postMessageODataHandler.requestProxy({ - url: cnf.metadataUri, - httpHeaders: cnf.httpHeaders, - success: function (response) { - var doc; - if (typeof module !== 'undefined' && typeof require !== 'undefined') { - doc = response.responseText; - } else if (window.ActiveXObject) { - doc = new ActiveXObject('Microsoft.XMLDOM'); - doc.async = 'false'; - doc.loadXML(response.responseText); - } else { - var parser = new DOMParser(); - doc = parser.parseFromString(response.responseText, 'text/xml'); - } - - callback(doc, response); - }, - error: function (e) { - that._loadXHTTP_XMLDoc(cnf, callback); - } - - }); - - } else { - this._loadXHTTP_XMLDoc(cnf, callback); - } - }, - _loadXHTTP_XMLDoc: function (cnf, callback) { - var xhttp = new XMLHttpRequest(); - xhttp.open("GET", cnf.metadataUri, true); - if (cnf.httpHeaders) { - Object.keys(cnf.httpHeaders).forEach(function (header) { - xhttp.setRequestHeader(header, cnf.httpHeaders[header]); - }); - } - xhttp.onreadystatechange = function () { - if (xhttp.readyState === 4) { - var response = { requestUri: cnf.metadataUri, statusCode: xhttp.status, statusText: xhttp.statusText, responseText: xhttp.responseText }; - callback(xhttp.responseXML || xhttp.responseText, response); - } - }; - - if (cnf.user && cnf.password && (!cnf.httpHeaders || (cnf.httpHeaders && !cnf.httpHeaders['Authorization']))) - xhttp.setRequestHeader("Authorization", "Basic " + this.__encodeBase64(cnf.user + ":" + cnf.password)); - - xhttp.send(""); - }, - _processResults: function (metadataUri, versionInfo, metadata, xsl, cnf) { - var transformXslt = this.getCurrentXSLTVersion(versionInfo, metadata); - cnf.typeFilter = this._prepareTypeFilter(metadata, versionInfo, cnf); - - if (window.ActiveXObject !== undefined) { - var xslt = new ActiveXObject("Msxml2.XSLTemplate.6.0"); - var xsldoc = new ActiveXObject("Msxml2.FreeThreadedDOMDocument.6.0"); - var xslproc; - xsldoc.async = false; - if (xsl) - xsldoc.load(xsl); - else - xsldoc.loadXML(transformXslt); - if (xsldoc.parseError.errorCode != 0) { - var myErr = xsldoc.parseError; - } else { - xslt.stylesheet = xsldoc; - var xmldoc = new ActiveXObject("Msxml2.DOMDocument.6.0"); - xmldoc.async = false; - xmldoc.load(metadata); - if (xmldoc.parseError.errorCode != 0) { - var myErr = xmldoc.parseError; - } else { - xslproc = xslt.createProcessor(); - xslproc.input = xmldoc; - - xslproc.addParameter('SerivceUri', cnf.SerivceUri); - xslproc.addParameter('EntityBaseClass', cnf.EntityBaseClass); - xslproc.addParameter('ContextBaseClass', cnf.ContextBaseClass); - xslproc.addParameter('AutoCreateContext', cnf.AutoCreateContext); - xslproc.addParameter('ContextInstanceName', cnf.ContextInstanceName); - xslproc.addParameter('EntitySetBaseClass', cnf.EntitySetBaseClass); - xslproc.addParameter('CollectionBaseClass', cnf.CollectionBaseClass); - xslproc.addParameter('DefaultNamespace', cnf.DefaultNamespace); - xslproc.addParameter('MaxDataserviceVersion', versionInfo.maxVersion || '3.0'); - xslproc.addParameter('AllowedTypesList', cnf.typeFilter); - xslproc.addParameter('GenerateNavigationProperties', cnf.navigation); - - xslproc.transform(); - return xslproc.output; - } - } - return ''; - } else if (typeof document !== 'undefined' && document.implementation && document.implementation.createDocument) { - var xsltStylesheet; - if (xsl) { - xsltStylesheet = xsl; - } else { - var parser = new DOMParser(); - xsltStylesheet = parser.parseFromString(transformXslt, "text/xml"); - } - - var xsltProcessor = new XSLTProcessor(); - xsltProcessor.importStylesheet(xsltStylesheet); - xsltProcessor.setParameter(null, 'SerivceUri', cnf.SerivceUri); - xsltProcessor.setParameter(null, 'EntityBaseClass', cnf.EntityBaseClass); - xsltProcessor.setParameter(null, 'ContextBaseClass', cnf.ContextBaseClass); - xsltProcessor.setParameter(null, 'AutoCreateContext', cnf.AutoCreateContext); - xsltProcessor.setParameter(null, 'ContextInstanceName', cnf.ContextInstanceName); - xsltProcessor.setParameter(null, 'EntitySetBaseClass', cnf.EntitySetBaseClass); - xsltProcessor.setParameter(null, 'CollectionBaseClass', cnf.CollectionBaseClass); - xsltProcessor.setParameter(null, 'DefaultNamespace', cnf.DefaultNamespace); - xsltProcessor.setParameter(null, 'MaxDataserviceVersion', versionInfo.maxVersion || '3.0'); - xsltProcessor.setParameter(null, 'AllowedTypesList', cnf.typeFilter); - xsltProcessor.setParameter(null, 'GenerateNavigationProperties', cnf.navigation); - resultDocument = xsltProcessor.transformToFragment(metadata, document); - - return resultDocument.textContent; - } else if (typeof module !== 'undefined' && typeof require !== 'undefined') { - var xslt = require('node_xslt'); - - return xslt.transform(xslt.readXsltString(transformXslt), xslt.readXmlString(metadata), [ - 'SerivceUri', "'" + cnf.SerivceUri + "'", - 'EntityBaseClass', "'" + cnf.EntityBaseClass + "'", - 'ContextBaseClass', "'" + cnf.ContextBaseClass + "'", - 'AutoCreateContext', "'" + cnf.AutoCreateContext + "'", - 'ContextInstanceName', "'" + cnf.ContextInstanceName + "'", - 'EntitySetBaseClass', "'" + cnf.EntitySetBaseClass + "'", - 'CollectionBaseClass', "'" + cnf.CollectionBaseClass + "'", - 'DefaultNamespace', "'" + cnf.DefaultNamespace + "'", - 'MaxDataserviceVersion', "'" + (versionInfo.maxVersion || '3.0') + "'", - 'AllowedTypesList', "'" + cnf.typeFilter + "'", - 'GenerateNavigationProperties', "'" + cnf.navigation + "'" - ]); - } - }, - _prepareTypeFilter: function (doc, versionInfo, cnf) { - var result = ''; - if (!(typeof doc === 'object' && "querySelector" in doc && "querySelectorAll" in doc)) - return result; - - var config = []; - if (typeof cnf.typeFilter === 'object' && cnf.typeFilter) { - var types = Object.keys(cnf.typeFilter); - for (var i = 0; i < types.length; i++) { - var cfg = cnf.typeFilter[types[i]]; - var typeData = {}; - if (typeof cfg === 'object' && cfg) { - if (Array.isArray(cfg)) { - typeData.Name = types[i]; - typeData.Fields = cfg; - } else { - typeData.Name = cfg.name || types[i]; - typeData.Fields = cfg.members || []; - } - } else if (cfg) { - typeData.Name = types[i]; - typeData.Fields = []; - } else { - continue; - } - - var typeShortName = typeData.Name; - var containerName = ""; - if (typeData.Name.lastIndexOf('.') > 0) - { - containerName = typeData.Name.substring(0, typeData.Name.lastIndexOf('.')); - typeShortName = typeData.Name.substring(typeData.Name.lastIndexOf('.') + 1); - } - - var conainers = doc.querySelectorAll("EntityContainer[Name = '" + containerName + "']"); - for (var j = 0; j < conainers.length; j++) { - var entitySetDef = conainers[j].querySelector("EntitySet[Name = '" + typeShortName + "']"); - if (entitySetDef != null) - { - typeData.Name = entitySetDef.attributes["EntityType"].value; - break; - } - - } - - config.push(typeData); - } - - var discoveredData; - if (cnf.dependentRelationsOnly) { - discoveredData = this._discoverProperyDependencies(config, doc, cnf.navigation, cnf.generateKeys); - } else { - discoveredData = this._discoverTypeDependencies(config, doc, cnf.navigation, cnf.generateKeys); - } - - var complex = doc.querySelectorAll("ComplexType"); - for (var i = 0; i < complex.length; i++) - { - var cns = complex[i].parentNode.attributes["Namespace"].value; - var data = !cns ? complex[i].attributes["Name"].value : (cns + "." + complex[i].attributes["Name"].value); - discoveredData.push({ Name: data, Fields: [] }); - } - - for (var i = 0; i < discoveredData.length; i++) - { - var row = discoveredData[i]; - if (row.Fields.length > 0) { - result += row.Name + ":" + row.Fields.join(",") + ";"; - } - else { - result += row.Name + ";"; - } - } - - } - - return result; - }, - _discoverTypeDependencies: function (types, doc, withNavPropertis, withKeys) { - var allowedTypes = []; - var allowedTypeNames = []; - var collect = []; - - for (var i = 0; i < types.length; i++) - { - var idx = collect.indexOf(types[i].Name); - if(idx >= 0){ - collect.splice(idx, 1); - } - this._discoverType(types[i], doc, allowedTypes, allowedTypeNames, withNavPropertis, withKeys, true, collect); - } - - for (var i = 0; i < collect.length; i++) - { - this._discoverType({ Name: collect[i], Fields: [] }, doc, allowedTypes, allowedTypeNames, withNavPropertis, withKeys, false, []); - } - - return allowedTypes; - }, - _discoverType: function(typeData, doc, allowedTypes, allowedTypeNames, withNavPropertis, withKeys, collectTypes, collectedTypes) { - var typeName = typeData.Name; - - if (allowedTypeNames.indexOf(typeName) >= 0) - { - return; - } - console.log("Discover: " + typeName); - - var typeShortName = typeName; - var typeNamespace = ''; - if (typeName.lastIndexOf('.') > 0) - { - typeNamespace = typeName.substring(0, typeName.lastIndexOf('.')); - typeShortName = typeName.substring(typeName.lastIndexOf('.') + 1); - } - - var schemaNode = doc.querySelector("Schema[Namespace = '" + typeNamespace + "']"); - if (schemaNode != null) - { - var typeNode = schemaNode.querySelector("EntityType[Name = '" + typeShortName + "'], ComplexType[Name = '" + typeShortName + "']"); - if (typeNode != null) - { - allowedTypes.push(typeData); - allowedTypeNames.push(typeName); - - if (withKeys && typeData.Fields.length > 0) { - var keys = typeNode.querySelectorAll("Key PropertyRef"); - if (keys != null) - { - for (var j = 0; j < keys.length; j++) - { - var keyField = keys[j].attributes["Name"].value; - if (typeData.Fields.indexOf(keyField) < 0) - typeData.Fields.splice(j, 0, keyField); - } - } - } - - if (withNavPropertis) - { - var navPropNodes = typeNode.querySelectorAll("NavigationProperty"); - for (var j = 0; j < navPropNodes.length; j++) - { - var navProp = navPropNodes[j]; - if (typeData.Fields.length == 0 || typeData.Fields.indexOf(navProp.attributes["Name"].value) >=0) - { - - var FromRole = navProp.attributes["FromRole"].value; - var ToRole = navProp.attributes["ToRole"].value; - - var association = schemaNode.querySelector("Association End[Role = '" + FromRole + "']:not([Type = '" + typeName + "'])"); - if (association == null) - { - association = schemaNode.querySelector("Association End[Role = '" + ToRole + "']:not([Type = '" + typeName + "'])"); - } - - if (association != null) - { - var nav_type = association.attributes["Type"].value; - - if (collectTypes) - { - if (collectedTypes.indexOf(nav_type) < 0 && allowedTypeNames.indexOf(nav_type) < 0) - collectedTypes.push(nav_type); - } - else - { - this._discoverType({ Name: nav_type, Fields: [] }, doc, allowedTypes, allowedTypeNames, withNavPropertis, withKeys, false, collectedTypes); - } - } - } - } - } - } - } - }, - - _discoverProperyDependencies: function (types, doc, withNavPropertis, withKeys) { - var allowedTypes = []; - var allowedTypeNames = types.map(function(t) { return t.Name; }); - - for (var i = 0; i < types.length; i++) - { - this._discoverProperties(types[i], doc, allowedTypes, allowedTypeNames, withNavPropertis, withKeys); - } - - return allowedTypes; - }, - _discoverProperties: function(typeData, doc, allowedTypes, allowedTypeNames, withNavPropertis, withKeys) { - var typeName = typeData.Name; - console.log("Discover: " + typeName); - - var hasProperty = typeData.Fields.length != 0; - var typeShortName = typeName; - var typeNamespace = ''; - if (typeName.lastIndexOf('.') > 0) - { - typeNamespace = typeName.substring(0, typeName.lastIndexOf('.')); - typeShortName = typeName.substring(typeName.lastIndexOf('.') + 1); - } - - var schemaNode = doc.querySelector("Schema[Namespace = '" + typeNamespace + "']"); - if (schemaNode != null) - { - var typeNode = schemaNode.querySelector("EntityType[Name = '" + typeShortName + "'], ComplexType[Name = '" + typeShortName + "']"); - if (typeNode != null) - { - allowedTypes.push(typeData); - - if (!hasProperty) - { - var properties = typeNode.querySelectorAll("Property"); - if (properties != null) - { - for (var j = 0; j < properties.length; j++) - { - var field = properties[j].attributes["Name"].value; - typeData.Fields.push(field); - } - } - - if (withNavPropertis) - { - var navPropNodes = typeNode.querySelectorAll("NavigationProperty"); - for (var j = 0; j < navPropNodes.length; j++) - { - var navProp = navPropNodes[j]; - var nav_name = navProp.attributes["Name"].value; - var types = [ navProp.attributes["FromRole"].value, navProp.attributes["ToRole"].value ]; - - var nav_type = ''; - for (var t = 0; t < types.length; t++) - { - var association = schemaNode.querySelector("Association End[Role = '" + types[t] + "']"); - if (association != null) - { - nav_type = association.attributes["Type"].value; - if (nav_type != typeName || t == 1) - break; - } - } - - if (allowedTypeNames.indexOf(nav_type) >= 0) - { - typeData.Fields.push(nav_name); - } - } - } - } - else if (withKeys) - { - var keys = typeNode.querySelectorAll("Key PropertyRef"); - if (keys != null) - { - for (var j = 0; j < keys.length; j++) - { - var keyField = keys[j].attributes["Name"].value; - if (typeData.Fields.indexOf(keyField) < 0) - typeData.Fields.splice(j, 0, keyField); - } - } - } - } - } - }, - - _findVersion: function (metadata) { - var maxDSVersion = ''; - - if (typeof metadata === 'object' && "getElementsByTagName" in metadata){ - var version = 'http://schemas.microsoft.com/ado/2008/09/edm'; - var item = metadata.getElementsByTagName('Schema'); - if (item) - item = item[0]; - if (item) - item = item.attributes; - if (item) - item = item.getNamedItem('xmlns'); - if (item) - version = item.value; - - var maxDSVersion = metadata.getElementsByTagName('edmx:DataServices')[0] || metadata.getElementsByTagName('DataServices')[0]; - if (maxDSVersion) - maxDSVersion = maxDSVersion.attributes.getNamedItem('m:MaxDataServiceVersion'); - if (maxDSVersion && version) - maxDSVersion = maxDSVersion.value; - - - var versionNum = this._supportedODataVersions[version]; - return { - ns: version, - version: versionNum || 'unknown', - maxVersion: maxDSVersion || this._maxDataServiceVersions[version || 'unknown'] - }; - }else if (typeof module !== 'undefined' && typeof require !== 'undefined'){ - var schemaXml = metadata; - - var schemaNamespace = 'http://schemas.microsoft.com/ado/2008/09/edm'; - var version = 'nodejs'; - for (var i in this._supportedODataVersions){ - if (schemaXml.search(new RegExp('= 0){ - schemaNamespace = i; - version = this._supportedODataVersions[i]; - break; - } - } - - return { - ns: schemaNamespace, - version: version, - maxVersion: this._maxDataServiceVersions[version || 'unknown'] - } - } - }, - _supportedODataVersions: { - value: { - "http://schemas.microsoft.com/ado/2006/04/edm": "V1", - "http://schemas.microsoft.com/ado/2008/09/edm": "V2", - "http://schemas.microsoft.com/ado/2009/11/edm": "V3", - "http://schemas.microsoft.com/ado/2007/05/edm": "V11", - "http://schemas.microsoft.com/ado/2009/08/edm": "V22" - } - }, - _maxDataServiceVersions: { - value: { - "http://schemas.microsoft.com/ado/2006/04/edm": "2.0", - "http://schemas.microsoft.com/ado/2008/09/edm": "2.0", - "http://schemas.microsoft.com/ado/2009/11/edm": "3.0", - "http://schemas.microsoft.com/ado/2007/05/edm": "2.0", - "http://schemas.microsoft.com/ado/2009/08/edm": "2.0" - } - }, - _supportedODataVersionXSLT: { - value: "JayDataContextGenerator.xslt" - }, - getCurrentXSLTVersion: function (versionInfo, metadata) { - return this._metadataConverterXSLT.replace('@@VERSIONNS@@', versionInfo.ns).replace('@@VERSION@@', versionInfo.version); - }, - __encodeBase64: function (val) { - var b64array = "ABCDEFGHIJKLMNOP" + - "QRSTUVWXYZabcdef" + - "ghijklmnopqrstuv" + - "wxyz0123456789+/" + - "="; - - var input = val; - var base64 = ""; - var hex = ""; - var chr1, chr2, chr3 = ""; - var enc1, enc2, enc3, enc4 = ""; - var i = 0; - - do { - chr1 = input.charCodeAt(i++); - chr2 = input.charCodeAt(i++); - chr3 = input.charCodeAt(i++); - - enc1 = chr1 >> 2; - enc2 = ((chr1 & 3) << 4) | (chr2 >> 4); - enc3 = ((chr2 & 15) << 2) | (chr3 >> 6); - enc4 = chr3 & 63; - - if (isNaN(chr2)) { - enc3 = enc4 = 64; - } else if (isNaN(chr3)) { - enc4 = 64; - } - - base64 = base64 + - b64array.charAt(enc1) + - b64array.charAt(enc2) + - b64array.charAt(enc3) + - b64array.charAt(enc4); - chr1 = chr2 = chr3 = ""; - enc1 = enc2 = enc3 = enc4 = ""; - } while (i < input.length); - - return base64; - }, - _metadataConverterXSLT: { - type: 'string', - value: - "\r\n" + - "\r\n" + - " \r\n" + - " \r\n" + - "\r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - "\r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - "\r\n" + - " Microsoft.Crm.Sdk.Data.Services.Product;Microsoft.Crm.Sdk.Data.Services.LeadAddress:Telephone1,City,UTCOffset;\r\n" + - "\r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " 0\">\r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - "\r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " 0\">\r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - "\r\n" + - " 0\">\r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " 0\">\r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - "\r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - "\r\n" + - "\r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " 0)\"/>\r\n" + - " 0)\"/>\r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - "\r\n" + - "/*//////////////////////////////////////////////////////////////////////////////////////\r\n" + - "////// Autogenerated by JaySvcUtil.exe http://JayData.org for more info /////////\r\n" + - "////// oData @@VERSION@@ /////////\r\n" + - "//////////////////////////////////////////////////////////////////////////////////////*/\r\n" + - "(function(global, $data, undefined) {\r\n" + - "\r\n" + - " \r\n" + - "\r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " 0)\"/>\r\n" + - " \r\n" + - " \r\n" + - " 0)\"/>\r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " 0)\"/>\r\n" + - " \r\n" + - " \r\n" + - " 0)\"/>\r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - "\r\n" + - " Info: generating type \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - "\r\n" + - "\r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " 0)\"/>\r\n" + - " \r\n" + - " \r\n" + - " 0)\"/>\r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " .extend('.', {\r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " , \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " , \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " ,\r\n" + - " \r\n" + - " ,\r\n" + - " \r\n" + - " \r\n" + - " });\r\n" + - "\r\n" + - "\r\n" + - "\r\n" + - "\r\n" + - "\r\n" + - " .extend('', {\r\n" + - " \r\n" + - "\r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - "\r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " ,\r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " ,\r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " });\r\n" + - "\r\n" + - " $data.generatedContexts = $data.generatedContexts || [];\r\n" + - " $data.generatedContexts.push();\r\n" + - " \r\n" + - " /*Context Instance*/\r\n" + - " = new ({ name:'oData', oDataServiceHost: '', maxDataServiceVersion: '' });\r\n" + - "\r\n" + - "\r\n" + - "\r\n" + - " \r\n" + - "})(window, $data);\r\n" + - " \r\n" + - " \r\n" + - "\r\n" + - " \r\n" + - "\r\n" + - " \r\n" + - " '\r\n" + - " \r\n" + - " ': { type: \r\n" + - " \r\n" + - " \r\n" + - " $data.ServiceAction\r\n" + - " \r\n" + - " \r\n" + - " $data.ServiceOperation\r\n" + - " \r\n" + - " \r\n" + - "\r\n" + - " \r\n" + - "\r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " , params: [\r\n" + - " 1) or (($IsBindable = 'false' or $IsBindable = '') and position() > 0)]\">\r\n" + - " { name: '\r\n" + - " \r\n" + - " ', type: '\r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " ' }\r\n" + - " , \r\n" + - " \r\n" + - " ]\r\n" + - "\r\n" + - " }\r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " , returnType: \r\n" + - " \r\n" + - " null\r\n" + - " $data.Queryable\r\n" + - " \r\n" + - " '\r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " '\r\n" + - " \r\n" + - " \r\n" + - "\r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " , elementType: '\r\n" + - " \r\n" + - " '\r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " , method: '\r\n" + - " \r\n" + - " '\r\n" + - " \r\n" + - " \r\n" + - " , \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " : \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " , '\r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " ': '\r\n" + - " \r\n" + - " '\r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - "\r\n" + - " '': { type: , elementType: }\r\n" + - "\r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " , actions: { \r\n" + - " \r\n" + - " \r\n" + - " ,\r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " }\r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " true\r\n" + - " \r\n" + - " '': { '$':\r\n" + - " , \r\n" + - " \r\n" + - " '$':\r\n" + - " , \r\n" + - " \r\n" + - " }\r\n" + - "\r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - "\r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " 'Array'\r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " ''\r\n" + - " \r\n" + - " \r\n" + - " ''\r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " ''\r\n" + - " \r\n" + - " \r\n" + - " ''\r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - "\r\n" + - " \r\n" + - " $data.ConcurrencyMode.\r\n" + - " \r\n" + - "\r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " true\r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - "\r\n" + - " \r\n" + - " true \r\n" + - " \r\n" + - "\r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " Number.POSITIVE_INFINITY\r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - "\r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " true''\r\n" + - " \r\n" + - "\r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " ''\r\n" + - " ''\r\n" + - " \r\n" + - " '$$unbound'\r\n" + - " \r\n" + - " ''\r\n" + - " \r\n" + - " \r\n" + - " ''\r\n" + - " \r\n" + - " \r\n" + - " ''\r\n" + - " \r\n" + - " \r\n" + - " '$$unbound'\r\n" + - " Warning: inverseProperty other side missing: \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " ''\r\n" + - " true\r\n" + - " \r\n" + - " \r\n" + - " ''\r\n" + - " \r\n" + - " \r\n" + - " '$$unbound'\r\n" + - " \r\n" + - " Warning: inverseProperty other side missing: \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - "\r\n" + - "\r\n" + - " \r\n" + - "\r\n" + - " \r\n" + - " \r\n" + - " Warning: .: is an unknown/unprocessed attribued\r\n" + - " \r\n" + - " \r\n" + - "\r\n" - } - -}); - -$data.MetadataLoader = new $data.MetadataLoaderClass(); -$data.service = function (serviceUri, config, cb) { - var _url, _config, _callback; - function getParam(paramValue) { - switch (typeof paramValue) { - case 'object': - if (typeof paramValue.success === 'function' || typeof paramValue.error === 'function') { - _callback = paramValue; - } else { - _config = paramValue; - } - break; - case 'function': - _callback = paramValue; - break; - default: - break; - } - } - getParam(config); - getParam(cb); - - if (typeof serviceUri === 'object') { - _config = $data.typeSystem.extend(serviceUri, _config); - serviceUri = serviceUri.url; - delete _config.url; - } - - var pHandler = new $data.PromiseHandler(); - _callback = pHandler.createCallback(_callback); - - $data.MetadataLoader.load(serviceUri, { - success: function (factory) { - var type = factory.type; - //register to local store - if (_config) { - var storeAlias = _config.serviceName || _config.storeAlias; - if (storeAlias && 'addStore' in $data) { - $data.addStore(storeAlias, factory, _config.isDefault === undefined || _config.isDefault) - } - } - - _callback.success(factory, type); - }, - error: _callback.error - }, _config); - - return pHandler.getPromise(); -}; -(function ($data) { - if (typeof jQuery !== 'undefined') { - $data.Class.define('$data.Deferred', $data.PromiseHandlerBase, null, { - constructor: function () { - this.deferred = new $.Deferred(); - }, - deferred: {}, - createCallback: function (callBack) { - callBack = $data.typeSystem.createCallbackSetting(callBack); - var self = this; - - return cbWrapper = { - success: function () { - callBack.success.apply(self.deferred, arguments); - self.deferred.resolve.apply(self.deferred, arguments); - }, - error: function () { - Array.prototype.push.call(arguments, self.deferred); - callBack.error.apply(self.deferred, arguments); - }, - notify: function () { - callBack.notify.apply(self.deferred, arguments); - self.deferred.notify.apply(self.deferred, arguments); - } - }; - }, - getPromise: function () { - return this.deferred.promise(); - } - }, null); - - $data.PromiseHandler = $data.Deferred; - } -})($data); -(function ($data) { - - $data.initService = function (apiKey, options) { - var d = new $data.PromiseHandler(); - var cfg; - - if (typeof apiKey === 'object') { - //appId, serviceName, ownerid, isSSL, port, license, url - cfg = apiKey; - var protocol = cfg.isSSL || cfg.isSSL === undefined ? 'https' : 'http'; - var port = cfg.port ? (':' + cfg.port) : ''; - - if (typeof cfg.license === 'string' && cfg.license.toLowerCase() === 'business') { - if (cfg.appId && cfg.serviceName) { - apiKey = protocol + '://' + cfg.appId + '.jaystack.net' + port + '/' + cfg.serviceName; - } else { - apiKey = cfg.url; - } - } else { - if (cfg.ownerId && cfg.appId && cfg.serviceName) { - apiKey = protocol + '://open.jaystack.net/' + cfg.ownerId + '/' + cfg.appId + '/api/' + cfg.serviceName; - } else { - apiKey = cfg.url; - } - } - - delete cfg.url; - cfg = $data.typeSystem.extend(cfg, options); - } else { - cfg = options; - } - - $data.service(apiKey, cfg).then(function (factory) { - var ctx = factory(); - return ctx.onReady() - .then(function (context) { - context.serviceFactory = factory; - d.deferred.resolve(context, factory, factory.type); - }).fail(function () { - d.deferred.reject.apply(d.deferred, arguments); - }); - }).fail(function(){ - d.deferred.reject.apply(d.deferred, arguments); - }); - - return d.getPromise(); - }; - -})($data); diff --git a/release/jaydata.js b/release/jaydata.js deleted file mode 100644 index d904da39..00000000 --- a/release/jaydata.js +++ /dev/null @@ -1,17273 +0,0 @@ -// JayData 1.3.6 -// Dual licensed under MIT and GPL v2 -// Copyright JayStack Technologies (http://jaydata.org/licensing) -// -// JayData is a standards-based, cross-platform Javascript library and a set of -// practices to access and manipulate data from various online and offline sources. -// -// Credits: -// Hajnalka Battancs, Dániel József, János Roden, László Horváth, Péter Nochta -// Péter Zentai, Róbert Bónay, Szabolcs Czinege, Viktor Borza, Viktor Lázár, -// Zoltán Gyebrovszki, Gábor Dolla -// -// More info: http://jaydata.org -// Acorn is a tiny, fast JavaScript parser written in JavaScript. -// -// Acorn was written by Marijn Haverbeke and released under an MIT -// license. The Unicode regexps (for identifiers and whitespace) were -// taken from [Esprima](http://esprima.org) by Ariya Hidayat. -// -// Git repositories for Acorn are available at -// -// http://marijnhaverbeke.nl/git/acorn -// https://github.com/marijnh/acorn.git -// -// Please use the [github bug tracker][ghbt] to report issues. -// -// [ghbt]: https://github.com/marijnh/acorn/issues -// -// This file defines the main parser interface. The library also comes -// with a [error-tolerant parser][dammit] and an -// [abstract syntax tree walker][walk], defined in other files. -// -// [dammit]: acorn_loose.js -// [walk]: util/walk.js - -(function(mod) { - if (typeof exports == "object" && typeof module == "object") return mod(exports); // CommonJS - if (typeof define == "function" && define.amd) return define(["exports"], mod); // AMD - mod(self.acorn || (self.acorn = {})); // Plain browser env -})(function(exports) { - "use strict"; - - exports.version = "0.1.01"; - - // The main exported interface (under `self.acorn` when in the - // browser) is a `parse` function that takes a code string and - // returns an abstract syntax tree as specified by [Mozilla parser - // API][api], with the caveat that the SpiderMonkey-specific syntax - // (`let`, `yield`, inline XML, etc) is not recognized. - // - // [api]: https://developer.mozilla.org/en-US/docs/SpiderMonkey/Parser_API - - var options, input, inputLen, sourceFile; - - exports.parse = function(inpt, opts) { - input = String(inpt); inputLen = input.length; - setOptions(opts); - initTokenState(); - return parseTopLevel(options.program); - }; - - // A second optional argument can be given to further configure - // the parser process. These options are recognized: - - var defaultOptions = exports.defaultOptions = { - // `ecmaVersion` indicates the ECMAScript version to parse. Must - // be either 3 or 5. This - // influences support for strict mode, the set of reserved words, and - // support for getters and setter. - ecmaVersion: 5, - // Turn on `strictSemicolons` to prevent the parser from doing - // automatic semicolon insertion. - strictSemicolons: false, - // When `allowTrailingCommas` is false, the parser will not allow - // trailing commas in array and object literals. - allowTrailingCommas: true, - // By default, reserved words are not enforced. Enable - // `forbidReserved` to enforce them. - forbidReserved: false, - // When `locations` is on, `loc` properties holding objects with - // `start` and `end` properties in `{line, column}` form (with - // line being 1-based and column 0-based) will be attached to the - // nodes. - locations: false, - // A function can be passed as `onComment` option, which will - // cause Acorn to call that function with `(block, text, start, - // end)` parameters whenever a comment is skipped. `block` is a - // boolean indicating whether this is a block (`/* */`) comment, - // `text` is the content of the comment, and `start` and `end` are - // character offsets that denote the start and end of the comment. - // When the `locations` option is on, two more parameters are - // passed, the full `{line, column}` locations of the start and - // end of the comments. - onComment: null, - // Nodes have their start and end characters offsets recorded in - // `start` and `end` properties (directly on the node, rather than - // the `loc` object, which holds line/column data. To also add a - // [semi-standardized][range] `range` property holding a `[start, - // end]` array with the same numbers, set the `ranges` option to - // `true`. - // - // [range]: https://bugzilla.mozilla.org/show_bug.cgi?id=745678 - ranges: false, - // It is possible to parse multiple files into a single AST by - // passing the tree produced by parsing the first file as - // `program` option in subsequent parses. This will add the - // toplevel forms of the parsed file to the `Program` (top) node - // of an existing parse tree. - program: null, - // When `location` is on, you can pass this to record the source - // file in every node's `loc` object. - sourceFile: null - }; - - function setOptions(opts) { - options = opts || {}; - for (var opt in defaultOptions) if (!options.hasOwnProperty(opt)) - options[opt] = defaultOptions[opt]; - sourceFile = options.sourceFile || null; - } - - // The `getLineInfo` function is mostly useful when the - // `locations` option is off (for performance reasons) and you - // want to find the line/column position for a given character - // offset. `input` should be the code string that the offset refers - // into. - - var getLineInfo = exports.getLineInfo = function(input, offset) { - for (var line = 1, cur = 0;;) { - lineBreak.lastIndex = cur; - var match = lineBreak.exec(input); - if (match && match.index < offset) { - ++line; - cur = match.index + match[0].length; - } else break; - } - return {line: line, column: offset - cur}; - }; - - // Acorn is organized as a tokenizer and a recursive-descent parser. - // The `tokenize` export provides an interface to the tokenizer. - // Because the tokenizer is optimized for being efficiently used by - // the Acorn parser itself, this interface is somewhat crude and not - // very modular. Performing another parse or call to `tokenize` will - // reset the internal state, and invalidate existing tokenizers. - - exports.tokenize = function(inpt, opts) { - input = String(inpt); inputLen = input.length; - setOptions(opts); - initTokenState(); - - var t = {}; - function getToken(forceRegexp) { - readToken(forceRegexp); - t.start = tokStart; t.end = tokEnd; - t.startLoc = tokStartLoc; t.endLoc = tokEndLoc; - t.type = tokType; t.value = tokVal; - return t; - } - getToken.jumpTo = function(pos, reAllowed) { - tokPos = pos; - if (options.locations) { - tokCurLine = tokLineStart = lineBreak.lastIndex = 0; - var match; - while ((match = lineBreak.exec(input)) && match.index < pos) { - ++tokCurLine; - tokLineStart = match.index + match[0].length; - } - } - var ch = input.charAt(pos - 1); - tokRegexpAllowed = reAllowed; - skipSpace(); - }; - return getToken; - }; - - // State is kept in (closure-)global variables. We already saw the - // `options`, `input`, and `inputLen` variables above. - - // The current position of the tokenizer in the input. - - var tokPos; - - // The start and end offsets of the current token. - - var tokStart, tokEnd; - - // When `options.locations` is true, these hold objects - // containing the tokens start and end line/column pairs. - - var tokStartLoc, tokEndLoc; - - // The type and value of the current token. Token types are objects, - // named by variables against which they can be compared, and - // holding properties that describe them (indicating, for example, - // the precedence of an infix operator, and the original name of a - // keyword token). The kind of value that's held in `tokVal` depends - // on the type of the token. For literals, it is the literal value, - // for operators, the operator name, and so on. - - var tokType, tokVal; - - // Interal state for the tokenizer. To distinguish between division - // operators and regular expressions, it remembers whether the last - // token was one that is allowed to be followed by an expression. - // (If it is, a slash is probably a regexp, if it isn't it's a - // division operator. See the `parseStatement` function for a - // caveat.) - - var tokRegexpAllowed; - - // When `options.locations` is true, these are used to keep - // track of the current line, and know when a new line has been - // entered. - - var tokCurLine, tokLineStart; - - // These store the position of the previous token, which is useful - // when finishing a node and assigning its `end` position. - - var lastStart, lastEnd, lastEndLoc; - - // This is the parser's state. `inFunction` is used to reject - // `return` statements outside of functions, `labels` to verify that - // `break` and `continue` have somewhere to jump to, and `strict` - // indicates whether strict mode is on. - - var inFunction, labels, strict; - - // This function is used to raise exceptions on parse errors. It - // takes an offset integer (into the current `input`) to indicate - // the location of the error, attaches the position to the end - // of the error message, and then raises a `SyntaxError` with that - // message. - - function raise(pos, message) { - var loc = getLineInfo(input, pos); - message += " (" + loc.line + ":" + loc.column + ")"; - var err = new SyntaxError(message); - err.pos = pos; err.loc = loc; err.raisedAt = tokPos; - throw err; - } - - // ## Token types - - // The assignment of fine-grained, information-carrying type objects - // allows the tokenizer to store the information it has about a - // token in a way that is very cheap for the parser to look up. - - // All token type variables start with an underscore, to make them - // easy to recognize. - - // These are the general types. The `type` property is only used to - // make them recognizeable when debugging. - - var _num = {type: "num"}, _regexp = {type: "regexp"}, _string = {type: "string"}; - var _name = {type: "name"}, _eof = {type: "eof"}; - - // Keyword tokens. The `keyword` property (also used in keyword-like - // operators) indicates that the token originated from an - // identifier-like word, which is used when parsing property names. - // - // The `beforeExpr` property is used to disambiguate between regular - // expressions and divisions. It is set on all token types that can - // be followed by an expression (thus, a slash after them would be a - // regular expression). - // - // `isLoop` marks a keyword as starting a loop, which is important - // to know when parsing a label, in order to allow or disallow - // continue jumps to that label. - - var _break = {keyword: "break"}, _case = {keyword: "case", beforeExpr: true}, _catch = {keyword: "catch"}; - var _continue = {keyword: "continue"}, _debugger = {keyword: "debugger"}, _default = {keyword: "default"}; - var _do = {keyword: "do", isLoop: true}, _else = {keyword: "else", beforeExpr: true}; - var _finally = {keyword: "finally"}, _for = {keyword: "for", isLoop: true}, _function = {keyword: "function"}; - var _if = {keyword: "if"}, _return = {keyword: "return", beforeExpr: true}, _switch = {keyword: "switch"}; - var _throw = {keyword: "throw", beforeExpr: true}, _try = {keyword: "try"}, _var = {keyword: "var"}; - var _while = {keyword: "while", isLoop: true}, _with = {keyword: "with"}, _new = {keyword: "new", beforeExpr: true}; - var _this = {keyword: "this"}; - - // The keywords that denote values. - - var _null = {keyword: "null", atomValue: null}, _true = {keyword: "true", atomValue: true}; - var _false = {keyword: "false", atomValue: false}; - - // Some keywords are treated as regular operators. `in` sometimes - // (when parsing `for`) needs to be tested against specifically, so - // we assign a variable name to it for quick comparing. - - var _in = {keyword: "in", binop: 7, beforeExpr: true}; - - // Map keyword names to token types. - - var keywordTypes = {"break": _break, "case": _case, "catch": _catch, - "continue": _continue, "debugger": _debugger, "default": _default, - "do": _do, "else": _else, "finally": _finally, "for": _for, - "function": _function, "if": _if, "return": _return, "switch": _switch, - "throw": _throw, "try": _try, "var": _var, "while": _while, "with": _with, - "null": _null, "true": _true, "false": _false, "new": _new, "in": _in, - "instanceof": {keyword: "instanceof", binop: 7, beforeExpr: true}, "this": _this, - "typeof": {keyword: "typeof", prefix: true, beforeExpr: true}, - "void": {keyword: "void", prefix: true, beforeExpr: true}, - "delete": {keyword: "delete", prefix: true, beforeExpr: true}}; - - // Punctuation token types. Again, the `type` property is purely for debugging. - - var _bracketL = {type: "[", beforeExpr: true}, _bracketR = {type: "]"}, _braceL = {type: "{", beforeExpr: true}; - var _braceR = {type: "}"}, _parenL = {type: "(", beforeExpr: true}, _parenR = {type: ")"}; - var _comma = {type: ",", beforeExpr: true}, _semi = {type: ";", beforeExpr: true}; - var _colon = {type: ":", beforeExpr: true}, _dot = {type: "."}, _question = {type: "?", beforeExpr: true}; - - // Operators. These carry several kinds of properties to help the - // parser use them properly (the presence of these properties is - // what categorizes them as operators). - // - // `binop`, when present, specifies that this operator is a binary - // operator, and will refer to its precedence. - // - // `prefix` and `postfix` mark the operator as a prefix or postfix - // unary operator. `isUpdate` specifies that the node produced by - // the operator should be of type UpdateExpression rather than - // simply UnaryExpression (`++` and `--`). - // - // `isAssign` marks all of `=`, `+=`, `-=` etcetera, which act as - // binary operators with a very low precedence, that should result - // in AssignmentExpression nodes. - - var _slash = {binop: 10, beforeExpr: true}, _eq = {isAssign: true, beforeExpr: true}; - var _assign = {isAssign: true, beforeExpr: true}, _plusmin = {binop: 9, prefix: true, beforeExpr: true}; - var _incdec = {postfix: true, prefix: true, isUpdate: true}, _prefix = {prefix: true, beforeExpr: true}; - var _bin1 = {binop: 1, beforeExpr: true}, _bin2 = {binop: 2, beforeExpr: true}; - var _bin3 = {binop: 3, beforeExpr: true}, _bin4 = {binop: 4, beforeExpr: true}; - var _bin5 = {binop: 5, beforeExpr: true}, _bin6 = {binop: 6, beforeExpr: true}; - var _bin7 = {binop: 7, beforeExpr: true}, _bin8 = {binop: 8, beforeExpr: true}; - var _bin10 = {binop: 10, beforeExpr: true}; - - // Provide access to the token types for external users of the - // tokenizer. - - exports.tokTypes = {bracketL: _bracketL, bracketR: _bracketR, braceL: _braceL, braceR: _braceR, - parenL: _parenL, parenR: _parenR, comma: _comma, semi: _semi, colon: _colon, - dot: _dot, question: _question, slash: _slash, eq: _eq, name: _name, eof: _eof, - num: _num, regexp: _regexp, string: _string}; - for (var kw in keywordTypes) exports.tokTypes[kw] = keywordTypes[kw]; - - // This is a trick taken from Esprima. It turns out that, on - // non-Chrome browsers, to check whether a string is in a set, a - // predicate containing a big ugly `switch` statement is faster than - // a regular expression, and on Chrome the two are about on par. - // This function uses `eval` (non-lexical) to produce such a - // predicate from a space-separated string of words. - // - // It starts by sorting the words by length. - - function makePredicate(words) { - words = words.split(" "); - var f = "", cats = []; - out: for (var i = 0; i < words.length; ++i) { - for (var j = 0; j < cats.length; ++j) - if (cats[j][0].length == words[i].length) { - cats[j].push(words[i]); - continue out; - } - cats.push([words[i]]); - } - function compareTo(arr) { - if (arr.length == 1) return f += "return str === " + JSON.stringify(arr[0]) + ";"; - f += "switch(str){"; - for (var i = 0; i < arr.length; ++i) f += "case " + JSON.stringify(arr[i]) + ":"; - f += "return true}return false;"; - } - - // When there are more than three length categories, an outer - // switch first dispatches on the lengths, to save on comparisons. - - if (cats.length > 3) { - cats.sort(function(a, b) {return b.length - a.length;}); - f += "switch(str.length){"; - for (var i = 0; i < cats.length; ++i) { - var cat = cats[i]; - f += "case " + cat[0].length + ":"; - compareTo(cat); - } - f += "}"; - - // Otherwise, simply generate a flat `switch` statement. - - } else { - compareTo(words); - } - return new Function("str", f); - } - - // The ECMAScript 3 reserved word list. - - var isReservedWord3 = makePredicate("abstract boolean byte char class double enum export extends final float goto implements import int interface long native package private protected public short static super synchronized throws transient volatile"); - - // ECMAScript 5 reserved words. - - var isReservedWord5 = makePredicate("class enum extends super const export import"); - - // The additional reserved words in strict mode. - - var isStrictReservedWord = makePredicate("implements interface let package private protected public static yield"); - - // The forbidden variable names in strict mode. - - var isStrictBadIdWord = makePredicate("eval arguments"); - - // And the keywords. - - var isKeyword = makePredicate("break case catch continue debugger default do else finally for function if return switch throw try var while with null true false instanceof typeof void delete new in this"); - - // ## Character categories - - // Big ugly regular expressions that match characters in the - // whitespace, identifier, and identifier-start categories. These - // are only applied when a character is found to actually have a - // code point above 128. - - var nonASCIIwhitespace = /[\u1680\u180e\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff]/; - var nonASCIIidentifierStartChars = "\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0370-\u0374\u0376\u0377\u037a-\u037d\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u048a-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05d0-\u05ea\u05f0-\u05f2\u0620-\u064a\u066e\u066f\u0671-\u06d3\u06d5\u06e5\u06e6\u06ee\u06ef\u06fa-\u06fc\u06ff\u0710\u0712-\u072f\u074d-\u07a5\u07b1\u07ca-\u07ea\u07f4\u07f5\u07fa\u0800-\u0815\u081a\u0824\u0828\u0840-\u0858\u08a0\u08a2-\u08ac\u0904-\u0939\u093d\u0950\u0958-\u0961\u0971-\u0977\u0979-\u097f\u0985-\u098c\u098f\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bd\u09ce\u09dc\u09dd\u09df-\u09e1\u09f0\u09f1\u0a05-\u0a0a\u0a0f\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a59-\u0a5c\u0a5e\u0a72-\u0a74\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2\u0ab3\u0ab5-\u0ab9\u0abd\u0ad0\u0ae0\u0ae1\u0b05-\u0b0c\u0b0f\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32\u0b33\u0b35-\u0b39\u0b3d\u0b5c\u0b5d\u0b5f-\u0b61\u0b71\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bd0\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c33\u0c35-\u0c39\u0c3d\u0c58\u0c59\u0c60\u0c61\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbd\u0cde\u0ce0\u0ce1\u0cf1\u0cf2\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d\u0d4e\u0d60\u0d61\u0d7a-\u0d7f\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0e01-\u0e30\u0e32\u0e33\u0e40-\u0e46\u0e81\u0e82\u0e84\u0e87\u0e88\u0e8a\u0e8d\u0e94-\u0e97\u0e99-\u0e9f\u0ea1-\u0ea3\u0ea5\u0ea7\u0eaa\u0eab\u0ead-\u0eb0\u0eb2\u0eb3\u0ebd\u0ec0-\u0ec4\u0ec6\u0edc-\u0edf\u0f00\u0f40-\u0f47\u0f49-\u0f6c\u0f88-\u0f8c\u1000-\u102a\u103f\u1050-\u1055\u105a-\u105d\u1061\u1065\u1066\u106e-\u1070\u1075-\u1081\u108e\u10a0-\u10c5\u10c7\u10cd\u10d0-\u10fa\u10fc-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u1380-\u138f\u13a0-\u13f4\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f0\u1700-\u170c\u170e-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176c\u176e-\u1770\u1780-\u17b3\u17d7\u17dc\u1820-\u1877\u1880-\u18a8\u18aa\u18b0-\u18f5\u1900-\u191c\u1950-\u196d\u1970-\u1974\u1980-\u19ab\u19c1-\u19c7\u1a00-\u1a16\u1a20-\u1a54\u1aa7\u1b05-\u1b33\u1b45-\u1b4b\u1b83-\u1ba0\u1bae\u1baf\u1bba-\u1be5\u1c00-\u1c23\u1c4d-\u1c4f\u1c5a-\u1c7d\u1ce9-\u1cec\u1cee-\u1cf1\u1cf5\u1cf6\u1d00-\u1dbf\u1e00-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u2071\u207f\u2090-\u209c\u2102\u2107\u210a-\u2113\u2115\u2119-\u211d\u2124\u2126\u2128\u212a-\u212d\u212f-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2ce4\u2ceb-\u2cee\u2cf2\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d80-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u2e2f\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303c\u3041-\u3096\u309d-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312d\u3131-\u318e\u31a0-\u31ba\u31f0-\u31ff\u3400-\u4db5\u4e00-\u9fcc\ua000-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua61f\ua62a\ua62b\ua640-\ua66e\ua67f-\ua697\ua6a0-\ua6ef\ua717-\ua71f\ua722-\ua788\ua78b-\ua78e\ua790-\ua793\ua7a0-\ua7aa\ua7f8-\ua801\ua803-\ua805\ua807-\ua80a\ua80c-\ua822\ua840-\ua873\ua882-\ua8b3\ua8f2-\ua8f7\ua8fb\ua90a-\ua925\ua930-\ua946\ua960-\ua97c\ua984-\ua9b2\ua9cf\uaa00-\uaa28\uaa40-\uaa42\uaa44-\uaa4b\uaa60-\uaa76\uaa7a\uaa80-\uaaaf\uaab1\uaab5\uaab6\uaab9-\uaabd\uaac0\uaac2\uaadb-\uaadd\uaae0-\uaaea\uaaf2-\uaaf4\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uabc0-\uabe2\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d\ufb1f-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40\ufb41\ufb43\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe70-\ufe74\ufe76-\ufefc\uff21-\uff3a\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc"; - var nonASCIIidentifierChars = "\u0371-\u0374\u0483-\u0487\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u0620-\u0649\u0672-\u06d3\u06e7-\u06e8\u06fb-\u06fc\u0730-\u074a\u0800-\u0814\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0840-\u0857\u08e4-\u08fe\u0900-\u0903\u093a-\u093c\u093e-\u094f\u0951-\u0957\u0962-\u0963\u0966-\u096f\u0981-\u0983\u09bc\u09be-\u09c4\u09c7\u09c8\u09d7\u09df-\u09e0\u0a01-\u0a03\u0a3c\u0a3e-\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a66-\u0a71\u0a75\u0a81-\u0a83\u0abc\u0abe-\u0ac5\u0ac7-\u0ac9\u0acb-\u0acd\u0ae2-\u0ae3\u0ae6-\u0aef\u0b01-\u0b03\u0b3c\u0b3e-\u0b44\u0b47\u0b48\u0b4b-\u0b4d\u0b56\u0b57\u0b5f-\u0b60\u0b66-\u0b6f\u0b82\u0bbe-\u0bc2\u0bc6-\u0bc8\u0bca-\u0bcd\u0bd7\u0be6-\u0bef\u0c01-\u0c03\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62-\u0c63\u0c66-\u0c6f\u0c82\u0c83\u0cbc\u0cbe-\u0cc4\u0cc6-\u0cc8\u0cca-\u0ccd\u0cd5\u0cd6\u0ce2-\u0ce3\u0ce6-\u0cef\u0d02\u0d03\u0d46-\u0d48\u0d57\u0d62-\u0d63\u0d66-\u0d6f\u0d82\u0d83\u0dca\u0dcf-\u0dd4\u0dd6\u0dd8-\u0ddf\u0df2\u0df3\u0e34-\u0e3a\u0e40-\u0e45\u0e50-\u0e59\u0eb4-\u0eb9\u0ec8-\u0ecd\u0ed0-\u0ed9\u0f18\u0f19\u0f20-\u0f29\u0f35\u0f37\u0f39\u0f41-\u0f47\u0f71-\u0f84\u0f86-\u0f87\u0f8d-\u0f97\u0f99-\u0fbc\u0fc6\u1000-\u1029\u1040-\u1049\u1067-\u106d\u1071-\u1074\u1082-\u108d\u108f-\u109d\u135d-\u135f\u170e-\u1710\u1720-\u1730\u1740-\u1750\u1772\u1773\u1780-\u17b2\u17dd\u17e0-\u17e9\u180b-\u180d\u1810-\u1819\u1920-\u192b\u1930-\u193b\u1951-\u196d\u19b0-\u19c0\u19c8-\u19c9\u19d0-\u19d9\u1a00-\u1a15\u1a20-\u1a53\u1a60-\u1a7c\u1a7f-\u1a89\u1a90-\u1a99\u1b46-\u1b4b\u1b50-\u1b59\u1b6b-\u1b73\u1bb0-\u1bb9\u1be6-\u1bf3\u1c00-\u1c22\u1c40-\u1c49\u1c5b-\u1c7d\u1cd0-\u1cd2\u1d00-\u1dbe\u1e01-\u1f15\u200c\u200d\u203f\u2040\u2054\u20d0-\u20dc\u20e1\u20e5-\u20f0\u2d81-\u2d96\u2de0-\u2dff\u3021-\u3028\u3099\u309a\ua640-\ua66d\ua674-\ua67d\ua69f\ua6f0-\ua6f1\ua7f8-\ua800\ua806\ua80b\ua823-\ua827\ua880-\ua881\ua8b4-\ua8c4\ua8d0-\ua8d9\ua8f3-\ua8f7\ua900-\ua909\ua926-\ua92d\ua930-\ua945\ua980-\ua983\ua9b3-\ua9c0\uaa00-\uaa27\uaa40-\uaa41\uaa4c-\uaa4d\uaa50-\uaa59\uaa7b\uaae0-\uaae9\uaaf2-\uaaf3\uabc0-\uabe1\uabec\uabed\uabf0-\uabf9\ufb20-\ufb28\ufe00-\ufe0f\ufe20-\ufe26\ufe33\ufe34\ufe4d-\ufe4f\uff10-\uff19\uff3f"; - var nonASCIIidentifierStart = new RegExp("[" + nonASCIIidentifierStartChars + "]"); - var nonASCIIidentifier = new RegExp("[" + nonASCIIidentifierStartChars + nonASCIIidentifierChars + "]"); - - // Whether a single character denotes a newline. - - var newline = /[\n\r\u2028\u2029]/; - - // Matches a whole line break (where CRLF is considered a single - // line break). Used to count lines. - - var lineBreak = /\r\n|[\n\r\u2028\u2029]/g; - - // Test whether a given character code starts an identifier. - - function isIdentifierStart(code) { - if (code < 65) return code === 36; - if (code < 91) return true; - if (code < 97) return code === 95; - if (code < 123)return true; - return code >= 0xaa && nonASCIIidentifierStart.test(String.fromCharCode(code)); - } - - // Test whether a given character is part of an identifier. - - function isIdentifierChar(code) { - if (code < 48) return code === 36; - if (code < 58) return true; - if (code < 65) return false; - if (code < 91) return true; - if (code < 97) return code === 95; - if (code < 123)return true; - return code >= 0xaa && nonASCIIidentifier.test(String.fromCharCode(code)); - } - - // ## Tokenizer - - // These are used when `options.locations` is on, for the - // `tokStartLoc` and `tokEndLoc` properties. - - function line_loc_t() { - this.line = tokCurLine; - this.column = tokPos - tokLineStart; - } - - // Reset the token state. Used at the start of a parse. - - function initTokenState() { - tokCurLine = 1; - tokPos = tokLineStart = 0; - tokRegexpAllowed = true; - skipSpace(); - } - - // Called at the end of every token. Sets `tokEnd`, `tokVal`, and - // `tokRegexpAllowed`, and skips the space after the token, so that - // the next one's `tokStart` will point at the right position. - - function finishToken(type, val) { - tokEnd = tokPos; - if (options.locations) tokEndLoc = new line_loc_t; - tokType = type; - skipSpace(); - tokVal = val; - tokRegexpAllowed = type.beforeExpr; - } - - function skipBlockComment() { - var startLoc = options.onComment && options.locations && new line_loc_t; - var start = tokPos, end = input.indexOf("*/", tokPos += 2); - if (end === -1) raise(tokPos - 2, "Unterminated comment"); - tokPos = end + 2; - if (options.locations) { - lineBreak.lastIndex = start; - var match; - while ((match = lineBreak.exec(input)) && match.index < tokPos) { - ++tokCurLine; - tokLineStart = match.index + match[0].length; - } - } - if (options.onComment) - options.onComment(true, input.slice(start + 2, end), start, tokPos, - startLoc, options.locations && new line_loc_t); - } - - function skipLineComment() { - var start = tokPos; - var startLoc = options.onComment && options.locations && new line_loc_t; - var ch = input.charCodeAt(tokPos+=2); - while (tokPos < inputLen && ch !== 10 && ch !== 13 && ch !== 8232 && ch !== 8329) { - ++tokPos; - ch = input.charCodeAt(tokPos); - } - if (options.onComment) - options.onComment(false, input.slice(start + 2, tokPos), start, tokPos, - startLoc, options.locations && new line_loc_t); - } - - // Called at the start of the parse and after every token. Skips - // whitespace and comments, and. - - function skipSpace() { - while (tokPos < inputLen) { - var ch = input.charCodeAt(tokPos); - if (ch === 32) { // ' ' - ++tokPos; - } else if(ch === 13) { - ++tokPos; - var next = input.charCodeAt(tokPos); - if(next === 10) { - ++tokPos; - } - if(options.locations) { - ++tokCurLine; - tokLineStart = tokPos; - } - } else if (ch === 10) { - ++tokPos; - ++tokCurLine; - tokLineStart = tokPos; - } else if(ch < 14 && ch > 8) { - ++tokPos; - } else if (ch === 47) { // '/' - var next = input.charCodeAt(tokPos+1); - if (next === 42) { // '*' - skipBlockComment(); - } else if (next === 47) { // '/' - skipLineComment(); - } else break; - } else if ((ch < 14 && ch > 8) || ch === 32 || ch === 160) { // ' ', '\xa0' - ++tokPos; - } else if (ch >= 5760 && nonASCIIwhitespace.test(String.fromCharCode(ch))) { - ++tokPos; - } else { - break; - } - } - } - - // ### Token reading - - // This is the function that is called to fetch the next token. It - // is somewhat obscure, because it works in character codes rather - // than characters, and because operator parsing has been inlined - // into it. - // - // All in the name of speed. - // - // The `forceRegexp` parameter is used in the one case where the - // `tokRegexpAllowed` trick does not work. See `parseStatement`. - - function readToken_dot() { - var next = input.charCodeAt(tokPos+1); - if (next >= 48 && next <= 57) return readNumber(true); - ++tokPos; - return finishToken(_dot); - } - - function readToken_slash() { // '/' - var next = input.charCodeAt(tokPos+1); - if (tokRegexpAllowed) {++tokPos; return readRegexp();} - if (next === 61) return finishOp(_assign, 2); - return finishOp(_slash, 1); - } - - function readToken_mult_modulo() { // '%*' - var next = input.charCodeAt(tokPos+1); - if (next === 61) return finishOp(_assign, 2); - return finishOp(_bin10, 1); - } - - function readToken_pipe_amp(code) { // '|&' - var next = input.charCodeAt(tokPos+1); - if (next === code) return finishOp(code === 124 ? _bin1 : _bin2, 2); - if (next === 61) return finishOp(_assign, 2); - return finishOp(code === 124 ? _bin3 : _bin5, 1); - } - - function readToken_caret() { // '^' - var next = input.charCodeAt(tokPos+1); - if (next === 61) return finishOp(_assign, 2); - return finishOp(_bin4, 1); - } - - function readToken_plus_min(code) { // '+-' - var next = input.charCodeAt(tokPos+1); - if (next === code) return finishOp(_incdec, 2); - if (next === 61) return finishOp(_assign, 2); - return finishOp(_plusmin, 1); - } - - function readToken_lt_gt(code) { // '<>' - var next = input.charCodeAt(tokPos+1); - var size = 1; - if (next === code) { - size = code === 62 && input.charCodeAt(tokPos+2) === 62 ? 3 : 2; - if (input.charCodeAt(tokPos + size) === 61) return finishOp(_assign, size + 1); - return finishOp(_bin8, size); - } - if (next === 61) - size = input.charCodeAt(tokPos+2) === 61 ? 3 : 2; - return finishOp(_bin7, size); - } - - function readToken_eq_excl(code) { // '=!' - var next = input.charCodeAt(tokPos+1); - if (next === 61) return finishOp(_bin6, input.charCodeAt(tokPos+2) === 61 ? 3 : 2); - return finishOp(code === 61 ? _eq : _prefix, 1); - } - - function getTokenFromCode(code) { - switch(code) { - // The interpretation of a dot depends on whether it is followed - // by a digit. - case 46: // '.' - return readToken_dot(); - - // Punctuation tokens. - case 40: ++tokPos; return finishToken(_parenL); - case 41: ++tokPos; return finishToken(_parenR); - case 59: ++tokPos; return finishToken(_semi); - case 44: ++tokPos; return finishToken(_comma); - case 91: ++tokPos; return finishToken(_bracketL); - case 93: ++tokPos; return finishToken(_bracketR); - case 123: ++tokPos; return finishToken(_braceL); - case 125: ++tokPos; return finishToken(_braceR); - case 58: ++tokPos; return finishToken(_colon); - case 63: ++tokPos; return finishToken(_question); - - // '0x' is a hexadecimal number. - case 48: // '0' - var next = input.charCodeAt(tokPos+1); - if (next === 120 || next === 88) return readHexNumber(); - // Anything else beginning with a digit is an integer, octal - // number, or float. - case 49: case 50: case 51: case 52: case 53: case 54: case 55: case 56: case 57: // 1-9 - return readNumber(false); - - // Quotes produce strings. - case 34: case 39: // '"', "'" - return readString(code); - - // Operators are parsed inline in tiny state machines. '=' (61) is - // often referred to. `finishOp` simply skips the amount of - // characters it is given as second argument, and returns a token - // of the type given by its first argument. - - case 47: // '/' - return readToken_slash(code); - - case 37: case 42: // '%*' - return readToken_mult_modulo(); - - case 124: case 38: // '|&' - return readToken_pipe_amp(code); - - case 94: // '^' - return readToken_caret(); - - case 43: case 45: // '+-' - return readToken_plus_min(code); - - case 60: case 62: // '<>' - return readToken_lt_gt(code); - - case 61: case 33: // '=!' - return readToken_eq_excl(code); - - case 126: // '~' - return finishOp(_prefix, 1); - } - - return false; - } - - function readToken(forceRegexp) { - if (!forceRegexp) tokStart = tokPos; - else tokPos = tokStart + 1; - if (options.locations) tokStartLoc = new line_loc_t; - if (forceRegexp) return readRegexp(); - if (tokPos >= inputLen) return finishToken(_eof); - - var code = input.charCodeAt(tokPos); - // Identifier or keyword. '\uXXXX' sequences are allowed in - // identifiers, so '\' also dispatches to that. - if (isIdentifierStart(code) || code === 92 /* '\' */) return readWord(); - - var tok = getTokenFromCode(code); - - if (tok === false) { - // If we are here, we either found a non-ASCII identifier - // character, or something that's entirely disallowed. - var ch = String.fromCharCode(code); - if (ch === "\\" || nonASCIIidentifierStart.test(ch)) return readWord(); - raise(tokPos, "Unexpected character '" + ch + "'"); - } - return tok; - } - - function finishOp(type, size) { - var str = input.slice(tokPos, tokPos + size); - tokPos += size; - finishToken(type, str); - } - - // Parse a regular expression. Some context-awareness is necessary, - // since a '/' inside a '[]' set does not end the expression. - - function readRegexp() { - var content = "", escaped, inClass, start = tokPos; - for (;;) { - if (tokPos >= inputLen) raise(start, "Unterminated regular expression"); - var ch = input.charAt(tokPos); - if (newline.test(ch)) raise(start, "Unterminated regular expression"); - if (!escaped) { - if (ch === "[") inClass = true; - else if (ch === "]" && inClass) inClass = false; - else if (ch === "/" && !inClass) break; - escaped = ch === "\\"; - } else escaped = false; - ++tokPos; - } - var content = input.slice(start, tokPos); - ++tokPos; - // Need to use `readWord1` because '\uXXXX' sequences are allowed - // here (don't ask). - var mods = readWord1(); - if (mods && !/^[gmsiy]*$/.test(mods)) raise(start, "Invalid regexp flag"); - return finishToken(_regexp, new RegExp(content, mods)); - } - - // Read an integer in the given radix. Return null if zero digits - // were read, the integer value otherwise. When `len` is given, this - // will return `null` unless the integer has exactly `len` digits. - - function readInt(radix, len) { - var start = tokPos, total = 0; - for (var i = 0, e = len == null ? Infinity : len; i < e; ++i) { - var code = input.charCodeAt(tokPos), val; - if (code >= 97) val = code - 97 + 10; // a - else if (code >= 65) val = code - 65 + 10; // A - else if (code >= 48 && code <= 57) val = code - 48; // 0-9 - else val = Infinity; - if (val >= radix) break; - ++tokPos; - total = total * radix + val; - } - if (tokPos === start || len != null && tokPos - start !== len) return null; - - return total; - } - - function readHexNumber() { - tokPos += 2; // 0x - var val = readInt(16); - if (val == null) raise(tokStart + 2, "Expected hexadecimal number"); - if (isIdentifierStart(input.charCodeAt(tokPos))) raise(tokPos, "Identifier directly after number"); - return finishToken(_num, val); - } - - // Read an integer, octal integer, or floating-point number. - - function readNumber(startsWithDot) { - var start = tokPos, isFloat = false, octal = input.charCodeAt(tokPos) === 48; - if (!startsWithDot && readInt(10) === null) raise(start, "Invalid number"); - if (input.charCodeAt(tokPos) === 46) { - ++tokPos; - readInt(10); - isFloat = true; - } - var next = input.charCodeAt(tokPos); - if (next === 69 || next === 101) { // 'eE' - next = input.charCodeAt(++tokPos); - if (next === 43 || next === 45) ++tokPos; // '+-' - if (readInt(10) === null) raise(start, "Invalid number") - isFloat = true; - } - if (isIdentifierStart(input.charCodeAt(tokPos))) raise(tokPos, "Identifier directly after number"); - - var str = input.slice(start, tokPos), val; - if (isFloat) val = parseFloat(str); - else if (!octal || str.length === 1) val = parseInt(str, 10); - else if (/[89]/.test(str) || strict) raise(start, "Invalid number"); - else val = parseInt(str, 8); - return finishToken(_num, val); - } - - // Read a string value, interpreting backslash-escapes. - - function readString(quote) { - tokPos++; - var out = ""; - for (;;) { - if (tokPos >= inputLen) raise(tokStart, "Unterminated string constant"); - var ch = input.charCodeAt(tokPos); - if (ch === quote) { - ++tokPos; - return finishToken(_string, out); - } - if (ch === 92) { // '\' - ch = input.charCodeAt(++tokPos); - var octal = /^[0-7]+/.exec(input.slice(tokPos, tokPos + 3)); - if (octal) octal = octal[0]; - while (octal && parseInt(octal, 8) > 255) octal = octal.slice(0, octal.length - 1); - if (octal === "0") octal = null; - ++tokPos; - if (octal) { - if (strict) raise(tokPos - 2, "Octal literal in strict mode"); - out += String.fromCharCode(parseInt(octal, 8)); - tokPos += octal.length - 1; - } else { - switch (ch) { - case 110: out += "\n"; break; // 'n' -> '\n' - case 114: out += "\r"; break; // 'r' -> '\r' - case 120: out += String.fromCharCode(readHexChar(2)); break; // 'x' - case 117: out += String.fromCharCode(readHexChar(4)); break; // 'u' - case 85: out += String.fromCharCode(readHexChar(8)); break; // 'U' - case 116: out += "\t"; break; // 't' -> '\t' - case 98: out += "\b"; break; // 'b' -> '\b' - case 118: out += "\u000b"; break; // 'v' -> '\u000b' - case 102: out += "\f"; break; // 'f' -> '\f' - case 48: out += "\0"; break; // 0 -> '\0' - case 13: if (input.charCodeAt(tokPos) === 10) ++tokPos; // '\r\n' - case 10: // ' \n' - if (options.locations) { tokLineStart = tokPos; ++tokCurLine; } - break; - default: out += String.fromCharCode(ch); break; - } - } - } else { - if (ch === 13 || ch === 10 || ch === 8232 || ch === 8329) raise(tokStart, "Unterminated string constant"); - out += String.fromCharCode(ch); // '\' - ++tokPos; - } - } - } - - // Used to read character escape sequences ('\x', '\u', '\U'). - - function readHexChar(len) { - var n = readInt(16, len); - if (n === null) raise(tokStart, "Bad character escape sequence"); - return n; - } - - // Used to signal to callers of `readWord1` whether the word - // contained any escape sequences. This is needed because words with - // escape sequences must not be interpreted as keywords. - - var containsEsc; - - // Read an identifier, and return it as a string. Sets `containsEsc` - // to whether the word contained a '\u' escape. - // - // Only builds up the word character-by-character when it actually - // containeds an escape, as a micro-optimization. - - function readWord1() { - containsEsc = false; - var word, first = true, start = tokPos; - for (;;) { - var ch = input.charCodeAt(tokPos); - if (isIdentifierChar(ch)) { - if (containsEsc) word += input.charAt(tokPos); - ++tokPos; - } else if (ch === 92) { // "\" - if (!containsEsc) word = input.slice(start, tokPos); - containsEsc = true; - if (input.charCodeAt(++tokPos) != 117) // "u" - raise(tokPos, "Expecting Unicode escape sequence \\uXXXX"); - ++tokPos; - var esc = readHexChar(4); - var escStr = String.fromCharCode(esc); - if (!escStr) raise(tokPos - 1, "Invalid Unicode escape"); - if (!(first ? isIdentifierStart(esc) : isIdentifierChar(esc))) - raise(tokPos - 4, "Invalid Unicode escape"); - word += escStr; - } else { - break; - } - first = false; - } - return containsEsc ? word : input.slice(start, tokPos); - } - - // Read an identifier or keyword token. Will check for reserved - // words when necessary. - - function readWord() { - var word = readWord1(); - var type = _name; - if (!containsEsc) { - if (isKeyword(word)) type = keywordTypes[word]; - else if (options.forbidReserved && - (options.ecmaVersion === 3 ? isReservedWord3 : isReservedWord5)(word) || - strict && isStrictReservedWord(word)) - raise(tokStart, "The keyword '" + word + "' is reserved"); - } - return finishToken(type, word); - } - - // ## Parser - - // A recursive descent parser operates by defining functions for all - // syntactic elements, and recursively calling those, each function - // advancing the input stream and returning an AST node. Precedence - // of constructs (for example, the fact that `!x[1]` means `!(x[1])` - // instead of `(!x)[1]` is handled by the fact that the parser - // function that parses unary prefix operators is called first, and - // in turn calls the function that parses `[]` subscripts — that - // way, it'll receive the node for `x[1]` already parsed, and wraps - // *that* in the unary operator node. - // - // Acorn uses an [operator precedence parser][opp] to handle binary - // operator precedence, because it is much more compact than using - // the technique outlined above, which uses different, nesting - // functions to specify precedence, for all of the ten binary - // precedence levels that JavaScript defines. - // - // [opp]: http://en.wikipedia.org/wiki/Operator-precedence_parser - - // ### Parser utilities - - // Continue to the next token. - - function next() { - lastStart = tokStart; - lastEnd = tokEnd; - lastEndLoc = tokEndLoc; - readToken(); - } - - // Enter strict mode. Re-reads the next token to please pedantic - // tests ("use strict"; 010; -- should fail). - - function setStrict(strct) { - strict = strct; - tokPos = lastEnd; - while (tokPos < tokLineStart) { - tokLineStart = input.lastIndexOf("\n", tokLineStart - 2) + 1; - --tokCurLine; - } - skipSpace(); - readToken(); - } - - // Start an AST node, attaching a start offset. - - function node_t() { - this.type = null; - this.start = tokStart; - this.end = null; - } - - function node_loc_t() { - this.start = tokStartLoc; - this.end = null; - if (sourceFile !== null) this.source = sourceFile; - } - - function startNode() { - var node = new node_t(); - if (options.locations) - node.loc = new node_loc_t(); - if (options.ranges) - node.range = [tokStart, 0]; - return node; - } - - // Start a node whose start offset information should be based on - // the start of another node. For example, a binary operator node is - // only started after its left-hand side has already been parsed. - - function startNodeFrom(other) { - var node = new node_t(); - node.start = other.start; - if (options.locations) { - node.loc = new node_loc_t(); - node.loc.start = other.loc.start; - } - if (options.ranges) - node.range = [other.range[0], 0]; - - return node; - } - - // Finish an AST node, adding `type` and `end` properties. - - function finishNode(node, type) { - node.type = type; - node.end = lastEnd; - if (options.locations) - node.loc.end = lastEndLoc; - if (options.ranges) - node.range[1] = lastEnd; - return node; - } - - // Test whether a statement node is the string literal `"use strict"`. - - function isUseStrict(stmt) { - return options.ecmaVersion >= 5 && stmt.type === "ExpressionStatement" && - stmt.expression.type === "Literal" && stmt.expression.value === "use strict"; - } - - // Predicate that tests whether the next token is of the given - // type, and if yes, consumes it as a side effect. - - function eat(type) { - if (tokType === type) { - next(); - return true; - } - } - - // Test whether a semicolon can be inserted at the current position. - - function canInsertSemicolon() { - return !options.strictSemicolons && - (tokType === _eof || tokType === _braceR || newline.test(input.slice(lastEnd, tokStart))); - } - - // Consume a semicolon, or, failing that, see if we are allowed to - // pretend that there is a semicolon at this position. - - function semicolon() { - if (!eat(_semi) && !canInsertSemicolon()) unexpected(); - } - - // Expect a token of a given type. If found, consume it, otherwise, - // raise an unexpected token error. - - function expect(type) { - if (tokType === type) next(); - else unexpected(); - } - - // Raise an unexpected token error. - - function unexpected() { - raise(tokStart, "Unexpected token"); - } - - // Verify that a node is an lval — something that can be assigned - // to. - - function checkLVal(expr) { - if (expr.type !== "Identifier" && expr.type !== "MemberExpression") - raise(expr.start, "Assigning to rvalue"); - if (strict && expr.type === "Identifier" && isStrictBadIdWord(expr.name)) - raise(expr.start, "Assigning to " + expr.name + " in strict mode"); - } - - // ### Statement parsing - - // Parse a program. Initializes the parser, reads any number of - // statements, and wraps them in a Program node. Optionally takes a - // `program` argument. If present, the statements will be appended - // to its body instead of creating a new node. - - function parseTopLevel(program) { - lastStart = lastEnd = tokPos; - if (options.locations) lastEndLoc = new line_loc_t; - inFunction = strict = null; - labels = []; - readToken(); - - var node = program || startNode(), first = true; - if (!program) node.body = []; - while (tokType !== _eof) { - var stmt = parseStatement(); - node.body.push(stmt); - if (first && isUseStrict(stmt)) setStrict(true); - first = false; - } - return finishNode(node, "Program"); - } - - var loopLabel = {kind: "loop"}, switchLabel = {kind: "switch"}; - - // Parse a single statement. - // - // If expecting a statement and finding a slash operator, parse a - // regular expression literal. This is to handle cases like - // `if (foo) /blah/.exec(foo);`, where looking at the previous token - // does not help. - - function parseStatement() { - if (tokType === _slash) - readToken(true); - - var starttype = tokType, node = startNode(); - - // Most types of statements are recognized by the keyword they - // start with. Many are trivial to parse, some require a bit of - // complexity. - - switch (starttype) { - case _break: case _continue: - next(); - var isBreak = starttype === _break; - if (eat(_semi) || canInsertSemicolon()) node.label = null; - else if (tokType !== _name) unexpected(); - else { - node.label = parseIdent(); - semicolon(); - } - - // Verify that there is an actual destination to break or - // continue to. - for (var i = 0; i < labels.length; ++i) { - var lab = labels[i]; - if (node.label == null || lab.name === node.label.name) { - if (lab.kind != null && (isBreak || lab.kind === "loop")) break; - if (node.label && isBreak) break; - } - } - if (i === labels.length) raise(node.start, "Unsyntactic " + starttype.keyword); - return finishNode(node, isBreak ? "BreakStatement" : "ContinueStatement"); - - case _debugger: - next(); - semicolon(); - return finishNode(node, "DebuggerStatement"); - - case _do: - next(); - labels.push(loopLabel); - node.body = parseStatement(); - labels.pop(); - expect(_while); - node.test = parseParenExpression(); - semicolon(); - return finishNode(node, "DoWhileStatement"); - - // Disambiguating between a `for` and a `for`/`in` loop is - // non-trivial. Basically, we have to parse the init `var` - // statement or expression, disallowing the `in` operator (see - // the second parameter to `parseExpression`), and then check - // whether the next token is `in`. When there is no init part - // (semicolon immediately after the opening parenthesis), it is - // a regular `for` loop. - - case _for: - next(); - labels.push(loopLabel); - expect(_parenL); - if (tokType === _semi) return parseFor(node, null); - if (tokType === _var) { - var init = startNode(); - next(); - parseVar(init, true); - if (init.declarations.length === 1 && eat(_in)) - return parseForIn(node, init); - return parseFor(node, init); - } - var init = parseExpression(false, true); - if (eat(_in)) {checkLVal(init); return parseForIn(node, init);} - return parseFor(node, init); - - case _function: - next(); - return parseFunction(node, true); - - case _if: - next(); - node.test = parseParenExpression(); - node.consequent = parseStatement(); - node.alternate = eat(_else) ? parseStatement() : null; - return finishNode(node, "IfStatement"); - - case _return: - if (!inFunction) raise(tokStart, "'return' outside of function"); - next(); - - // In `return` (and `break`/`continue`), the keywords with - // optional arguments, we eagerly look for a semicolon or the - // possibility to insert one. - - if (eat(_semi) || canInsertSemicolon()) node.argument = null; - else { node.argument = parseExpression(); semicolon(); } - return finishNode(node, "ReturnStatement"); - - case _switch: - next(); - node.discriminant = parseParenExpression(); - node.cases = []; - expect(_braceL); - labels.push(switchLabel); - - // Statements under must be grouped (by label) in SwitchCase - // nodes. `cur` is used to keep the node that we are currently - // adding statements to. - - for (var cur, sawDefault; tokType != _braceR;) { - if (tokType === _case || tokType === _default) { - var isCase = tokType === _case; - if (cur) finishNode(cur, "SwitchCase"); - node.cases.push(cur = startNode()); - cur.consequent = []; - next(); - if (isCase) cur.test = parseExpression(); - else { - if (sawDefault) raise(lastStart, "Multiple default clauses"); sawDefault = true; - cur.test = null; - } - expect(_colon); - } else { - if (!cur) unexpected(); - cur.consequent.push(parseStatement()); - } - } - if (cur) finishNode(cur, "SwitchCase"); - next(); // Closing brace - labels.pop(); - return finishNode(node, "SwitchStatement"); - - case _throw: - next(); - if (newline.test(input.slice(lastEnd, tokStart))) - raise(lastEnd, "Illegal newline after throw"); - node.argument = parseExpression(); - semicolon(); - return finishNode(node, "ThrowStatement"); - - case _try: - next(); - node.block = parseBlock(); - node.handler = null; - if (tokType === _catch) { - var clause = startNode(); - next(); - expect(_parenL); - clause.param = parseIdent(); - if (strict && isStrictBadIdWord(clause.param.name)) - raise(clause.param.start, "Binding " + clause.param.name + " in strict mode"); - expect(_parenR); - clause.guard = null; - clause.body = parseBlock(); - node.handler = finishNode(clause, "CatchClause"); - } - node.finalizer = eat(_finally) ? parseBlock() : null; - if (!node.handler && !node.finalizer) - raise(node.start, "Missing catch or finally clause"); - return finishNode(node, "TryStatement"); - - case _var: - next(); - node = parseVar(node); - semicolon(); - return node; - - case _while: - next(); - node.test = parseParenExpression(); - labels.push(loopLabel); - node.body = parseStatement(); - labels.pop(); - return finishNode(node, "WhileStatement"); - - case _with: - if (strict) raise(tokStart, "'with' in strict mode"); - next(); - node.object = parseParenExpression(); - node.body = parseStatement(); - return finishNode(node, "WithStatement"); - - case _braceL: - return parseBlock(); - - case _semi: - next(); - return finishNode(node, "EmptyStatement"); - - // If the statement does not start with a statement keyword or a - // brace, it's an ExpressionStatement or LabeledStatement. We - // simply start parsing an expression, and afterwards, if the - // next token is a colon and the expression was a simple - // Identifier node, we switch to interpreting it as a label. - - default: - var maybeName = tokVal, expr = parseExpression(); - if (starttype === _name && expr.type === "Identifier" && eat(_colon)) { - for (var i = 0; i < labels.length; ++i) - if (labels[i].name === maybeName) raise(expr.start, "Label '" + maybeName + "' is already declared"); - var kind = tokType.isLoop ? "loop" : tokType === _switch ? "switch" : null; - labels.push({name: maybeName, kind: kind}); - node.body = parseStatement(); - labels.pop(); - node.label = expr; - return finishNode(node, "LabeledStatement"); - } else { - node.expression = expr; - semicolon(); - return finishNode(node, "ExpressionStatement"); - } - } - } - - // Used for constructs like `switch` and `if` that insist on - // parentheses around their expression. - - function parseParenExpression() { - expect(_parenL); - var val = parseExpression(); - expect(_parenR); - return val; - } - - // Parse a semicolon-enclosed block of statements, handling `"use - // strict"` declarations when `allowStrict` is true (used for - // function bodies). - - function parseBlock(allowStrict) { - var node = startNode(), first = true, strict = false, oldStrict; - node.body = []; - expect(_braceL); - while (!eat(_braceR)) { - var stmt = parseStatement(); - node.body.push(stmt); - if (first && isUseStrict(stmt)) { - oldStrict = strict; - setStrict(strict = true); - } - first = false - } - if (strict && !oldStrict) setStrict(false); - return finishNode(node, "BlockStatement"); - } - - // Parse a regular `for` loop. The disambiguation code in - // `parseStatement` will already have parsed the init statement or - // expression. - - function parseFor(node, init) { - node.init = init; - expect(_semi); - node.test = tokType === _semi ? null : parseExpression(); - expect(_semi); - node.update = tokType === _parenR ? null : parseExpression(); - expect(_parenR); - node.body = parseStatement(); - labels.pop(); - return finishNode(node, "ForStatement"); - } - - // Parse a `for`/`in` loop. - - function parseForIn(node, init) { - node.left = init; - node.right = parseExpression(); - expect(_parenR); - node.body = parseStatement(); - labels.pop(); - return finishNode(node, "ForInStatement"); - } - - // Parse a list of variable declarations. - - function parseVar(node, noIn) { - node.declarations = []; - node.kind = "var"; - for (;;) { - var decl = startNode(); - decl.id = parseIdent(); - if (strict && isStrictBadIdWord(decl.id.name)) - raise(decl.id.start, "Binding " + decl.id.name + " in strict mode"); - decl.init = eat(_eq) ? parseExpression(true, noIn) : null; - node.declarations.push(finishNode(decl, "VariableDeclarator")); - if (!eat(_comma)) break; - } - return finishNode(node, "VariableDeclaration"); - } - - // ### Expression parsing - - // These nest, from the most general expression type at the top to - // 'atomic', nondivisible expression types at the bottom. Most of - // the functions will simply let the function(s) below them parse, - // and, *if* the syntactic construct they handle is present, wrap - // the AST node that the inner parser gave them in another node. - - // Parse a full expression. The arguments are used to forbid comma - // sequences (in argument lists, array literals, or object literals) - // or the `in` operator (in for loops initalization expressions). - - function parseExpression(noComma, noIn) { - var expr = parseMaybeAssign(noIn); - if (!noComma && tokType === _comma) { - var node = startNodeFrom(expr); - node.expressions = [expr]; - while (eat(_comma)) node.expressions.push(parseMaybeAssign(noIn)); - return finishNode(node, "SequenceExpression"); - } - return expr; - } - - // Parse an assignment expression. This includes applications of - // operators like `+=`. - - function parseMaybeAssign(noIn) { - var left = parseMaybeConditional(noIn); - if (tokType.isAssign) { - var node = startNodeFrom(left); - node.operator = tokVal; - node.left = left; - next(); - node.right = parseMaybeAssign(noIn); - checkLVal(left); - return finishNode(node, "AssignmentExpression"); - } - return left; - } - - // Parse a ternary conditional (`?:`) operator. - - function parseMaybeConditional(noIn) { - var expr = parseExprOps(noIn); - if (eat(_question)) { - var node = startNodeFrom(expr); - node.test = expr; - node.consequent = parseExpression(true); - expect(_colon); - node.alternate = parseExpression(true, noIn); - return finishNode(node, "ConditionalExpression"); - } - return expr; - } - - // Start the precedence parser. - - function parseExprOps(noIn) { - return parseExprOp(parseMaybeUnary(noIn), -1, noIn); - } - - // Parse binary operators with the operator precedence parsing - // algorithm. `left` is the left-hand side of the operator. - // `minPrec` provides context that allows the function to stop and - // defer further parser to one of its callers when it encounters an - // operator that has a lower precedence than the set it is parsing. - - function parseExprOp(left, minPrec, noIn) { - var prec = tokType.binop; - if (prec != null && (!noIn || tokType !== _in)) { - if (prec > minPrec) { - var node = startNodeFrom(left); - node.left = left; - node.operator = tokVal; - next(); - node.right = parseExprOp(parseMaybeUnary(noIn), prec, noIn); - var node = finishNode(node, /&&|\|\|/.test(node.operator) ? "LogicalExpression" : "BinaryExpression"); - return parseExprOp(node, minPrec, noIn); - } - } - return left; - } - - // Parse unary operators, both prefix and postfix. - - function parseMaybeUnary(noIn) { - if (tokType.prefix) { - var node = startNode(), update = tokType.isUpdate; - node.operator = tokVal; - node.prefix = true; - next(); - node.argument = parseMaybeUnary(noIn); - if (update) checkLVal(node.argument); - else if (strict && node.operator === "delete" && - node.argument.type === "Identifier") - raise(node.start, "Deleting local variable in strict mode"); - return finishNode(node, update ? "UpdateExpression" : "UnaryExpression"); - } - var expr = parseExprSubscripts(); - while (tokType.postfix && !canInsertSemicolon()) { - var node = startNodeFrom(expr); - node.operator = tokVal; - node.prefix = false; - node.argument = expr; - checkLVal(expr); - next(); - expr = finishNode(node, "UpdateExpression"); - } - return expr; - } - - // Parse call, dot, and `[]`-subscript expressions. - - function parseExprSubscripts() { - return parseSubscripts(parseExprAtom()); - } - - function parseSubscripts(base, noCalls) { - if (eat(_dot)) { - var node = startNodeFrom(base); - node.object = base; - node.property = parseIdent(true); - node.computed = false; - return parseSubscripts(finishNode(node, "MemberExpression"), noCalls); - } else if (eat(_bracketL)) { - var node = startNodeFrom(base); - node.object = base; - node.property = parseExpression(); - node.computed = true; - expect(_bracketR); - return parseSubscripts(finishNode(node, "MemberExpression"), noCalls); - } else if (!noCalls && eat(_parenL)) { - var node = startNodeFrom(base); - node.callee = base; - node.arguments = parseExprList(_parenR, false); - return parseSubscripts(finishNode(node, "CallExpression"), noCalls); - } else return base; - } - - // Parse an atomic expression — either a single token that is an - // expression, an expression started by a keyword like `function` or - // `new`, or an expression wrapped in punctuation like `()`, `[]`, - // or `{}`. - - function parseExprAtom() { - switch (tokType) { - case _this: - var node = startNode(); - next(); - return finishNode(node, "ThisExpression"); - case _name: - return parseIdent(); - case _num: case _string: case _regexp: - var node = startNode(); - node.value = tokVal; - node.raw = input.slice(tokStart, tokEnd); - next(); - return finishNode(node, "Literal"); - - case _null: case _true: case _false: - var node = startNode(); - node.value = tokType.atomValue; - node.raw = tokType.keyword - next(); - return finishNode(node, "Literal"); - - case _parenL: - var tokStartLoc1 = tokStartLoc, tokStart1 = tokStart; - next(); - var val = parseExpression(); - val.start = tokStart1; - val.end = tokEnd; - if (options.locations) { - val.loc.start = tokStartLoc1; - val.loc.end = tokEndLoc; - } - if (options.ranges) - val.range = [tokStart1, tokEnd]; - expect(_parenR); - return val; - - case _bracketL: - var node = startNode(); - next(); - node.elements = parseExprList(_bracketR, true, true); - return finishNode(node, "ArrayExpression"); - - case _braceL: - return parseObj(); - - case _function: - var node = startNode(); - next(); - return parseFunction(node, false); - - case _new: - return parseNew(); - - default: - unexpected(); - } - } - - // New's precedence is slightly tricky. It must allow its argument - // to be a `[]` or dot subscript expression, but not a call — at - // least, not without wrapping it in parentheses. Thus, it uses the - - function parseNew() { - var node = startNode(); - next(); - node.callee = parseSubscripts(parseExprAtom(), true); - if (eat(_parenL)) node.arguments = parseExprList(_parenR, false); - else node.arguments = []; - return finishNode(node, "NewExpression"); - } - - // Parse an object literal. - - function parseObj() { - var node = startNode(), first = true, sawGetSet = false; - node.properties = []; - next(); - while (!eat(_braceR)) { - if (!first) { - expect(_comma); - if (options.allowTrailingCommas && eat(_braceR)) break; - } else first = false; - - var prop = {key: parsePropertyName()}, isGetSet = false, kind; - if (eat(_colon)) { - prop.value = parseExpression(true); - kind = prop.kind = "init"; - } else if (options.ecmaVersion >= 5 && prop.key.type === "Identifier" && - (prop.key.name === "get" || prop.key.name === "set")) { - isGetSet = sawGetSet = true; - kind = prop.kind = prop.key.name; - prop.key = parsePropertyName(); - if (tokType !== _parenL) unexpected(); - prop.value = parseFunction(startNode(), false); - } else unexpected(); - - // getters and setters are not allowed to clash — either with - // each other or with an init property — and in strict mode, - // init properties are also not allowed to be repeated. - - if (prop.key.type === "Identifier" && (strict || sawGetSet)) { - for (var i = 0; i < node.properties.length; ++i) { - var other = node.properties[i]; - if (other.key.name === prop.key.name) { - var conflict = kind == other.kind || isGetSet && other.kind === "init" || - kind === "init" && (other.kind === "get" || other.kind === "set"); - if (conflict && !strict && kind === "init" && other.kind === "init") conflict = false; - if (conflict) raise(prop.key.start, "Redefinition of property"); - } - } - } - node.properties.push(prop); - } - return finishNode(node, "ObjectExpression"); - } - - function parsePropertyName() { - if (tokType === _num || tokType === _string) return parseExprAtom(); - return parseIdent(true); - } - - // Parse a function declaration or literal (depending on the - // `isStatement` parameter). - - function parseFunction(node, isStatement) { - if (tokType === _name) node.id = parseIdent(); - else if (isStatement) unexpected(); - else node.id = null; - node.params = []; - var first = true; - expect(_parenL); - while (!eat(_parenR)) { - if (!first) expect(_comma); else first = false; - node.params.push(parseIdent()); - } - - // Start a new scope with regard to labels and the `inFunction` - // flag (restore them to their old value afterwards). - var oldInFunc = inFunction, oldLabels = labels; - inFunction = true; labels = []; - node.body = parseBlock(true); - inFunction = oldInFunc; labels = oldLabels; - - // If this is a strict mode function, verify that argument names - // are not repeated, and it does not try to bind the words `eval` - // or `arguments`. - if (strict || node.body.body.length && isUseStrict(node.body.body[0])) { - for (var i = node.id ? -1 : 0; i < node.params.length; ++i) { - var id = i < 0 ? node.id : node.params[i]; - if (isStrictReservedWord(id.name) || isStrictBadIdWord(id.name)) - raise(id.start, "Defining '" + id.name + "' in strict mode"); - if (i >= 0) for (var j = 0; j < i; ++j) if (id.name === node.params[j].name) - raise(id.start, "Argument name clash in strict mode"); - } - } - - return finishNode(node, isStatement ? "FunctionDeclaration" : "FunctionExpression"); - } - - // Parses a comma-separated list of expressions, and returns them as - // an array. `close` is the token type that ends the list, and - // `allowEmpty` can be turned on to allow subsequent commas with - // nothing in between them to be parsed as `null` (which is needed - // for array literals). - - function parseExprList(close, allowTrailingComma, allowEmpty) { - var elts = [], first = true; - while (!eat(close)) { - if (!first) { - expect(_comma); - if (allowTrailingComma && options.allowTrailingCommas && eat(close)) break; - } else first = false; - - if (allowEmpty && tokType === _comma) elts.push(null); - else elts.push(parseExpression(true)); - } - return elts; - } - - // Parse the next token as an identifier. If `liberal` is true (used - // when parsing properties), it will also convert keywords into - // identifiers. - - function parseIdent(liberal) { - var node = startNode(); - node.name = tokType === _name ? tokVal : (liberal && !options.forbidReserved && tokType.keyword) || unexpected(); - next(); - return finishNode(node, "Identifier"); - } - -}); - -(function (global) { - if (typeof window === "undefined") { - window = this; - } - //$data = window["$data"] || (window["$data"] = {}); - $data = window["$data"] || (window["$data"] = (function _data_handler() { - //console.log("@@@@", this); - if (this instanceof _data_handler) { - //console.log( - var type = _data_handler["implementation"].apply(this, arguments); - return new type(arguments[1]); - } else { - - return _data_handler["implementation"].apply(this, arguments) - } - })); -})(this); - -if (typeof console === 'undefined') { - console = { - warn: function () { }, - error: function () { }, - log: function () { }, - dir: function () { }, - time: function () { }, - timeEnd: function () { } - }; -} - -if (!console.warn) console.warn = function () { }; -if (!console.error) console.error = function () { }; - -(function ($data) { - /// - /// Collection of JayData services - /// - $data.__namespace = true; - $data.version = "JayData 1.3.6"; - $data.versionNumber = "1.3.6"; - $data.root = {}; - $data.Acorn = $data.Acorn || (typeof acorn == 'object' ? acorn : undefined); - $data.Esprima = $data.Esprima || (typeof esprima == 'object' ? esprima : undefined); - -})($data); - -// Do not remove this block, it is used by jsdoc -/** - @name $data.Base - @class base class -*/ -Exception = function(message, name, data) { - Error.call(this); - if (Error.captureStackTrace) - Error.captureStackTrace(this, this.constructor); - - this.name = name || "Exception"; - this.message = message; - this.data = data; - - //this.toString = function() { return JSON.stringify(this); }; - -} - -Exception.prototype.__proto__ = Error.prototype; - -Exception.prototype._getStackTrace = function () { - var callstack = []; - var isCallstackPopulated = false; - // unreachable code - //return; - /*try { - i.dont.exist += 0; - } - catch (e) { - if (e.stack) { // Firefox, Chrome - var lines = e.stack.split('\n'); - for (var i = 0, len = lines.length; i < len; i++) { - //if (lines[i].match(/^\s*[A-Za-z0-9\-_\$]+\(/)) { - if (lines[i].indexOf(" at ") >= 0) - callstack.push(lines[i]); - } - //Remove call to printStackTrace() - callstack.shift(); - //TODO: Remove call to new Exception( chain - //callstack.shift(); - isCallstackPopulated = true; - } - else if (window.opera && e.message) { //Opera - var lines = e.message.split('\n'); - for (var i = 0, len = lines.length; i < len; i++) { - if (lines[i].match(/^\s*[A-Za-z0-9\-_\$]+\(/)) { - var entry = lines[i]; - //Append next line also since it has the file info - if (lines[i + 1]) { - entry += ' at ' + lines[i + 1]; - i++; - } - callstack.push(entry); - } - } - //Remove call to printStackTrace() - callstack.shift(); - //TODO: Remove call to new Exception( chain - //callstack.shift(); - isCallstackPopulated = true; - } - } - - //if (!isCallstackPopulated) { //IE and Safari - // var currentFunction = arguments.callee.caller; - // while (currentFunction) { - // var fn = currentFunction.toString(); - // var fname = fn.substring(fn.indexOf("function") + 8, fn.indexOf('(')) || 'anonymous'; - // callstack.push(fname); - // if (currentFunction == currentFunction.caller) { - // Guard.raise("Infinite loop"); - // } - // currentFunction = currentFunction.caller; - // } - //} - return callstack.join("\n\r"); */ -}; -Guard = {}; -Guard.requireValue = function (name, value) { - if (typeof value === 'undefined' || value === null) { - Guard.raise(name + " requires a value other than undefined or null"); - } -}; - -Guard.requireType = function (name, value, typeOrTypes) { - var types = typeOrTypes instanceof Array ? typeOrTypes : [typeOrTypes]; - return types.some(function (item) { - switch (typeof item) { - case "string": - return typeof value === item; - case "function": - return value instanceof item; - default: - Guard.raise("Unknown type format : " + typeof item + " for: "+ name); - } - }); -}; - -Guard.raise = function(exception){ - if (typeof intellisense === 'undefined') { - if (exception instanceof Exception){ - console.error(exception.name + ':', exception.message + '\n', exception); - }else{ - console.error(exception); - } - throw exception; - } -}; - -Object.isNullOrUndefined = function (value) { - return value === undefined || value === null; -}; -(function ObjectMethodsForPreHTML5Browsers() { - - if (!Object.getOwnPropertyNames){ - Object.getOwnPropertyNames = function(o){ - var names = []; - - for (var i in o){ - if (o.hasOwnProperty(i)) names.push(i); - } - - return names; - }; - } - - if (!Object.create) { - Object.create = function (o) { - if (arguments.length > 1) { - Guard.raise(new Error('Object.create implementation only accepts the first parameter.')); - } - function F() { } - F.prototype = o; - return new F(); - }; - } - - if (!Object.keys) { - var hasOwnProperty = Object.prototype.hasOwnProperty, - hasDontEnumBug = !({ toString: null }).propertyIsEnumerable('toString'), - dontEnums = ['toString', - 'toLocaleString', - 'valueOf', - 'hasOwnProperty', - 'isPrototypeOf', - 'propertyIsEnumerable', - 'constructor'], - dontEnumsLength = dontEnums.length; - - Object.keys = function (obj) { - - ///Refactor to Assert.IsObjectOrFunction - if (typeof obj !== 'object' && typeof obj !== 'function' || obj === null) Guard.raise(new TypeError('Object.keys called on non-object')); - - var result = []; - - for (var prop in obj) { - if (hasOwnProperty.call(obj, prop)) { - result.push(prop); - } - } - - if (hasDontEnumBug) { - for (var i = 0; i < dontEnumsLength; i++) { - if (hasOwnProperty.call(obj, dontEnums[i])) { - result.push(dontEnums[i]); - } - } - } - - return result; - }; - } - - if (!Object.defineProperty) { - Object.defineProperty = function (obj, propName, propDef) { - obj[propName] = propDef.value || {}; - }; - } - - if (!Object.defineProperties) { - Object.defineProperties = function (obj, defines) { - for (var i in defines) { - if(defines.hasOwnProperty(i)) - obj[i] = defines[i].value || {}; - } - }; - } - - if (!Array.prototype.forEach) { - Array.prototype.forEach = function (handler, thisArg) { - for (var i = 0, l = this.length; i < l; i++) { - if (thisArg) { handler.call(thisArg, this[i], i, this); } - else { handler(this[i], i, this); }; - }; - }; - }; - - if (!Array.prototype.filter) { - Array.prototype.filter = function (handler, thisArg) { - var result = []; - for (var i = 0, l = this.length; i < l; i++) { - var r = thisArg ? - handler.call(thisArg, this[i], i, this) : - handler(this[i], i, this); - if (r === true) { - result.push(this[i]); - } - } - return result; - }; - } - - if (!Array.prototype.map) { - Array.prototype.map = function (handler, thisArg) { - var result = []; - for (var i = 0, l = this.length; i < l; i++) { - var r = thisArg ? - handler.call(thisArg, this[i], i, this) : - handler(this[i], i, this); - result.push(r); - } - return result; - }; - } - - if (!Array.prototype.some) { - Array.prototype.some = function (handler, thisArg) { - for (var i = 0, l = this.length; i < l; i++) { - var r = thisArg ? - handler.call(thisArg, this[i], i, this) : - handler(this[i], i, this); - if (r) { return true; } - - } - return false; - }; - } - - if (!Array.prototype.indexOf) { - Array.prototype.indexOf = function (item, from) { - for (var i = 0, l = this.length; i < l; i++) { - if (this[i] === item) { - return i; - }; - }; - return -1; - }; - } - - if (!String.prototype.trimLeft) { - String.prototype.trimLeft = function () { - return this.replace(/^\s+/, ""); - } - } - - if (!String.prototype.trimRight) { - String.prototype.trimRight = function () { - return this.replace(/\s+$/, ""); - } - } - - if (!Function.prototype.bind) { - Function.prototype.bind = function (oThis) { - if (typeof this !== "function") { - // closest thing possible to the ECMAScript 5 internal IsCallable function - throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable"); - } - - var aArgs = Array.prototype.slice.call(arguments, 1), - fToBind = this, - fNOP = function () { }, - fBound = function () { - return fToBind.apply(this instanceof fNOP && oThis - ? this - : oThis, - aArgs.concat(Array.prototype.slice.call(arguments))); - }; - - fNOP.prototype = this.prototype; - fBound.prototype = new fNOP(); - - return fBound; - }; - } - - if (typeof Uint8Array == 'undefined'){ - Uint8Array = function(v){ - if (v instanceof Uint8Array) return v; - var self = this; - var buffer = Array.isArray(v) ? v : new Array(v); - this.length = buffer.length; - this.byteLength = this.length; - this.byteOffset = 0; - this.buffer = { byteLength: self.length }; - var getter = function(index){ - return buffer[index]; - }; - var setter = function(index, value){ - buffer[index] = (value | 0) & 0xff; - }; - var makeAccessor = function(i){ - buffer[i] = buffer[i] || 0; - Object.defineProperty(self, i, { - enumerable: true, - configurable: false, - get: function(){ - if (isNaN(+i) || ((i | 0) < 0 || (i | 0) >= self.length)){ - try{ - if (typeof document != 'undefined') document.createTextNode("").splitText(1); - return new RangeError("INDEX_SIZE_ERR"); - }catch(e){ - return e; - } - } - return getter(i); - }, - set: function(v){ - if (isNaN(+i) || ((i | 0) < 0 || (i | 0) >= self.length)){ - try{ - if (typeof document != 'undefined') document.createTextNode("").splitText(1); - return new RangeError("INDEX_SIZE_ERR"); - }catch(e){ - return e; - } - } - setter(i | 0, v); - } - }); - }; - for (var i = 0; i < self.length; i++){ - makeAccessor(i); - } - }; - } - -})(); -(function init($data, global) { - - function il(msg) { - if (typeof intellisense !== 'undefined') { - if (!intellisense.i) { - intellisense.i = 0; - } - intellisense.i = intellisense.i + 1; - intellisense.logMessage(msg + ":" + intellisense.i); - } - } - - function MemberDefinition(memberDefinitionData, definedClass) { - - ///* - ///* - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - ///[false] if false value is stored in initData, otherwise on the object - ///[true] if set to false propertyChange events are not raise and property tracking is disabled - - this.kind = MemberTypes.property; - //this.definedBy = definedClass; - Object.defineProperty(this, 'definedBy', { value: definedClass, enumerable: false, configurable: false, writable: false }); - if (memberDefinitionData) { - if (typeof memberDefinitionData === 'function' || typeof memberDefinitionData.asFunction === 'function') { - this.method = memberDefinitionData; - this.kind = MemberTypes.method; - } else { - this.enumerable = true; - this.configurable = true; - if (typeof memberDefinitionData === "number") { - this.value = memberDefinitionData; - this.type = $data.Number; - this.dataType = $data.Number; - } else if (typeof memberDefinitionData === "string") { - this.value = memberDefinitionData; - this.dataType = $data.String; - this.type = $data.String; - } else { - for (var item in memberDefinitionData) { - if (memberDefinitionData.hasOwnProperty(item)) { - this[item] = memberDefinitionData[item]; - } - } - } - } - if (this.type !== undefined) { - this.dataType = this.dataType || this.type; - } else { - this.type = this.dataType; - } - - this.originalType = this.type; - if (this.elementType !== undefined) { - this.originalElementType = this.elementType; - } - } - } - MemberDefinition.prototype.createPropertyDescriptor = function (classFunction, value) { - /// - var pd = this; - var result = { - enumerable: this.enumerable == undefined ? true : this.enumerable, - configurable: this.configurable == undefined ? true : this.configurable - }; - if (this.set && this.get) { - result.set = this.set; - result.get = this.get; - } else if ("value" in this || value) { - result.value = value || this.value; - //TODO - //result.writable = this.writable; - result.writable = true; - } - else { - result.set = function (value) { this.storeProperty(pd, value); }; - result.get = function () { return this.retrieveProperty(pd); }; - } - return result; - }; - MemberDefinition.prototype.createStorePropertyDescriptor = function (value) { - var pd = this; - return { enumerable: false, writable: true, configurable: pd.configurable, value: value }; - }; - MemberDefinition.prototype.createGetMethod = function () { - var pd = this; - return { - enumerable: false, writable: false, configurable: false, - value: function (callback, tran) { return this.getProperty(pd, callback, tran); } - }; - }; - MemberDefinition.prototype.createSetMethod = function () { - var pd = this; - return { - enumerable: false, writable: false, configurable: false, - value: function (value, callback, tran) { return this.setProperty(pd, value, callback, tran); } - }; - }; - MemberDefinition.translateDefinition = function (memDef, name, classFunction) { - var holder = classFunction; - var memberDefinition; - - if (memDef.type && Container.isTypeRegistered(memDef.type)) { - holder = Container.resolveType(memDef.type); - if (typeof holder.translateDefinition === 'function') { - memberDefinition = holder.translateDefinition.apply(holder, arguments); - memberDefinition.name = memberDefinition.name || name; - } else { - holder = classFunction; - } - } - - - if (!(memberDefinition instanceof MemberDefinition)) { - memberDefinition = new MemberDefinition(memberDefinition || memDef, holder); - memberDefinition.name = name; - } - classFunction.resolverThunks = classFunction.resolverThunks || []; - classFunction.childResolverThunks = classFunction.childResolverThunks || []; - - - var t = memberDefinition.type; - var et = memberDefinition.elementType; - - function addChildThunk(referencedType) { - if (referencedType && referencedType.isAssignableTo && $data.Entity && referencedType.isAssignableTo($data.Entity)) { - classFunction.childResolverThunks.push(function () { - if (referencedType.resolveForwardDeclarations) { - referencedType.resolveForwardDeclarations(); - } - }); - } - } - - addChildThunk(t); - addChildThunk(et); - - if ("string" === typeof t) { - if ("@" === t[0]) { - memberDefinition.type = t.substr(1); - memberDefinition.dataType = t.substr(1); - } else { - //forward declared types get this callback when type is registered - classFunction.resolverThunks.push(function () { - var rt = classFunction.container.resolveType(t); - addChildThunk(rt); - memberDefinition.type = rt; - memberDefinition.dataType = rt; - }); - } - } - - if (et) { - if ("string" === typeof et) { - if ("@" === et[0]) { - memberDefinition.elementType = et.substr(1); - } else { - //forward declared types get this callback when type is registered - classFunction.resolverThunks.push(function () { - var rt = classFunction.container.resolveType(et); - addChildThunk(rt); - memberDefinition.elementType = rt; - }); - - } - } - } - - - //if (!classFunction) - - classFunction.resolveForwardDeclarations = function () { - classFunction.resolveForwardDeclarations = function () { }; - $data.Trace.log("resolving: " + classFunction.fullName); - this.resolverThunks.forEach(function (thunk) { - thunk(); - }); - //this.resolverThunks = []; - this.childResolverThunks.forEach(function (thunk) { - thunk(); - }); - //this.childResolverThunks = []; - } - - return memberDefinition; - }; - - MemberDefinition.prototype.toJSON = function () { - var property = {}; - for (var name in this) { - if (name !== 'defineBy' && name !== 'storageModel') { - if ((name === 'type' || name === 'dataType') && (this[name] && typeof this[name] === 'function')) { - try { - property[name] = Container.resolveName(this[name]); - } catch (e) { - property[name] = this[name]; - } - } else { - property[name] = this[name]; - } - } - } - return property; - } - - //TODO global/window - $data.MemberDefinition = window["MemberDefinition"] = MemberDefinition; - - var memberDefinitionPrefix = '$'; - function MemberDefinitionCollection() { }; - MemberDefinitionCollection.prototype = { - clearCache: function () { - this.arrayCache = undefined; - this.pubMapPropsCache = undefined; - this.keyPropsCache = undefined; - this.propByTypeCache = undefined; - this.pubMapMethodsCache = undefined; - this.pubMapPropNamesCache = undefined; - }, - asArray: function () { - if (!this.arrayCache) { - this.arrayCache = []; - for (var i in this) { - if (i.indexOf(memberDefinitionPrefix) === 0) - this.arrayCache.push(this[i]); - } - } - return this.arrayCache; - }, - getPublicMappedProperties: function () { - if (!this.pubMapPropsCache) { - this.pubMapPropsCache = []; - for (var i in this) { - if (i.indexOf(memberDefinitionPrefix) === 0 && this[i].kind == 'property' && !this[i].notMapped && this[i].enumerable) - this.pubMapPropsCache.push(this[i]); - } - } - return this.pubMapPropsCache;// || (this.pubMapPropsCache = this.asArray().filter(function (m) { return m.kind == 'property' && !m.notMapped && m.enumerable; })); - }, - getPublicMappedPropertyNames: function () { - if (!this.pubMapPropNamesCache) { - this.pubMapPropNamesCache = []; - for (var i in this) { - if (i.indexOf(memberDefinitionPrefix) === 0 && this[i].kind == 'property' && !this[i].notMapped && this[i].enumerable) - this.pubMapPropNamesCache.push(this[i].name); - } - } - return this.pubMapPropNamesCache; - }, - getKeyProperties: function () { - if (!this.keyPropsCache) { - this.keyPropsCache = []; - for (var i in this) { - if (i.indexOf(memberDefinitionPrefix) === 0 && this[i].kind == 'property' && this[i].key) - this.keyPropsCache.push(this[i]); - } - } - return this.keyPropsCache; - //return this.keyPropsCache || (this.keyPropsCache = this.asArray().filter(function (m) { return m.kind == 'property' && m.key; })); - }, - getPublicMappedMethods: function () { - if (!this.pubMapMethodsCache) { - this.pubMapMethodsCache = []; - for (var i in this) { - if (i.indexOf(memberDefinitionPrefix) === 0 && this[i].kind == 'method' && this[i].method/* && this.hasOwnProperty(i)*/) - this.pubMapMethodsCache.push(this[i]); - } - } - return this.pubMapMethodsCache; - }, - getPropertyByType: function (type) { - if (!this.propByTypeCache) { - this.propByTypeCache = []; - for (var i in this) { - if (i.indexOf(memberDefinitionPrefix) === 0 && this[i].dataType == type) - this.propByTypeCache.push(this[i]); - } - } - return this.propByTypeCache; - //return this.propByTypeCache || (this.propByTypeCache = this.asArray().filter(function (m) { return m.dataType == type; })); - }, - getMember: function (name) { return this[memberDefinitionPrefix + name]; }, - setMember: function (value) { this[memberDefinitionPrefix + value.name] = value; } - }; - MemberDefinitionCollection.prototype.constructor = MemberDefinitionCollection; - $data.MemberDefinitionCollection = window["MemberDefinitionCollection"] = MemberDefinitionCollection; - - function ClassEngineBase() { - this.classNames = {}; - } - - function MemberTypes() { - /// - /// - /// - /// - } - MemberTypes.__enum = true; - - MemberTypes.method = "method"; - MemberTypes.property = "property"; - MemberTypes.navProperty = "navProperty"; - MemberTypes.complexProperty = "complexProperty"; - MemberTypes.field = "field"; - - $data.MemberTypes = MemberTypes; - - //function classToJSON() { - // var ret = {}; - // for (var i in this) { - // if (this.hasOwnProperty(i)) { - // ret[i] = this[i]; - // } - // } - // return ret; - //} - //$data.Base.toJSON = classToJSON; - - ClassEngineBase.prototype = { - - //getClass: function (classReference) { - //}, - - //getProperties: function (classFunction) { - // return classFunction.propertyDefinitions; - //}, - - define: function (className, baseClass, container, instanceDefinition, classDefinition) { - /// - /// Creates a Jaydata type - /// Name of the class - /// Basetype of the class - /// - /// Class definition (properties, methods, etc) - /// Class static definition - /// - /// - /// var t = new $data.Class.define('Types.A', $data.Base, null, { - /// constructor: function(){ }, - /// func1: function(){ }, - /// member1: { type: 'string' } - /// }, { - /// staticFunc1: function() {} - /// }) - /// - /// - /// - - return this.defineEx(className, [{ type: baseClass }], container, instanceDefinition, classDefinition); - }, - defineEx: function (className, baseClasses, container, instanceDefinition, classDefinition) { - /// - /// Creates a Jaydata type - /// Name of the class - /// Basetypes of the class. First is a real base, others are mixins - /// - /// Class definition (properties, methods, etc) - /// Class static definition - /// - /// - /// var t = new $data.Class.define('Types.A', [$data.Base, $data.Mixin1, $data.Mixin2], null, { - /// constructor: function(){ }, - /// func1: function(){ }, - /// member1: { type: 'string' } - /// }, { - /// staticFunc1: function() {} - /// }) - /// - /// - /// - /// - /// Creates a Jaydata type - /// Name of the class - /// Basetypes of the class. First is a real base, others are mixins or propagations - /// - /// Class definition (properties, methods, etc) - /// Class static definition - /// - /// - /// var t = new $data.Class.define('Types.A', [ - /// { type: $data.Base, params: [1, 'secondParameterValue', new ConstructorParameter(0)] }, - /// { type: $data.Mixin1, }, - /// { type: $data.Mixin2, }, - /// { type: $data.Propagation1, params: [new ConstructorParameter(1)], propagateTo:'Propagation1' }, - /// { type: $data.Propagation2, params: ['firstParameterValue'], propagateTo:'Propagation2' } - /// ], null, { - /// constructor: function(){ }, - /// func1: function(){ }, - /// member1: { type: 'string' } - /// }, { - /// staticFunc1: function() {} - /// }) - /// - /// - /// - - container = container || $data.Container; - - if (baseClasses.length == 0) { - baseClasses.push({ type: $data.Base }); - } else if (baseClasses.length > 0 && !baseClasses[0].type) { - baseClasses[0].type = $data.Base; - } - for (var i = 0, l = baseClasses.length; i < l; i++) { - if (typeof baseClasses[i] === 'function') - baseClasses[i] = { type: baseClasses[i] }; - } - - var providedCtor = instanceDefinition ? instanceDefinition.constructor : undefined; - - var classNameParts = className.split('.'); - var shortClassName = classNameParts.splice(classNameParts.length - 1, 1)[0]; - - var root = container === $data.Container ? window : container; - for (var i = 0; i < classNameParts.length; i++) { - var part = classNameParts[i]; - if (!root[part]) { - var ns = {}; - ns.__namespace = true; - root[part] = ns; - } - root = root[part]; - } - - - var classFunction = null; - classFunction = this.classFunctionBuilder(shortClassName, baseClasses, classDefinition, instanceDefinition); - classFunction.fullName = className; - classFunction.namespace = classNameParts.join('.'); //classname splitted - classFunction.name = shortClassName; - classFunction.container = container; - classFunction.container.registerType(className, classFunction); - - this.buildType(classFunction, baseClasses, instanceDefinition, classDefinition); - - - - if (typeof intellisense !== 'undefined') { - if (instanceDefinition && instanceDefinition.constructor) { - intellisense.annotate(classFunction, instanceDefinition.constructor); - } - } - - root[shortClassName] = this.classNames[className] = classFunction; - //classFunction.toJSON = classToJSON; - var baseCount = classFunction.baseTypes.length; - for (var i = 0; i < baseCount; i++) { - var b = classFunction.baseTypes[i]; - if ("inheritedTypeProcessor" in b) { - b.inheritedTypeProcessor(classFunction); - } - } - //classFunction.prototype.constructor = instanceDefinition.constructor; - //classFunction.constructor = instanceDefinition.constructor; - //classFunction.toJSON = function () { return classFunction.memberDefinitions.filter( function(md) { return md; }; - return classFunction; - }, - classFunctionBuilder: function (name, base, classDefinition, instanceDefinition) { - var body = this.bodyBuilder(base, classDefinition, instanceDefinition); - return new Function('base', 'classDefinition', 'instanceDefinition', 'name', 'return function ' + name + ' (){ ' + - body + ' \n}; ')(base, classDefinition, instanceDefinition, name); - }, - bodyBuilder: function (bases, classDefinition, instanceDefinition) { - var mixin = ''; - var body = ''; - var propagation = ''; - - for (var i = 0, l = bases.length; i < l; i++) { - var base = bases[i]; - var index = i; - if (index == 0) { //ctor func - if (base && base.type && base.type !== $data.Base && base.type.fullName) { - body += ' var baseArguments = $data.typeSystem.createCtorParams(arguments, base[' + index + '].params, this); \n'; - body += ' ' + base.type.fullName + '.apply(this, baseArguments); \n'; - } - } else { - if (base && base.type && base.propagateTo) { - //propagation - propagation += ' ' + (!propagation ? 'var ' : '' + '') + 'propagationArguments = $data.typeSystem.createCtorParams(arguments, base[' + index + '].params, this); \n'; - propagation += ' this["' + base.propagateTo + '"] = Object.create(' + base.type.fullName + '.prototype); \n' + - ' ' + base.type.fullName + '.apply(this["' + base.propagateTo + '"], propagationArguments); \n'; - } - else if (base && base.type && base.type.memberDefinitions && base.type.memberDefinitions.$constructor && !base.propagateTo) { - //mixin - mixin += ' ' + base.type.fullName + '.memberDefinitions.$constructor.method.apply(this); \n'; - } - } - } - if (instanceDefinition && instanceDefinition.constructor != Object) - body += " instanceDefinition.constructor.apply(this, arguments); \n"; - - return '\n //mixins \n' + mixin + '\n //construction \n' + body + '\n //propagations \n' + propagation; - }, - - buildType: function (classFunction, baseClasses, instanceDefinition, classDefinition) { - var baseClass = baseClasses[0].type; - classFunction.inheritsFrom = baseClass; - - if (baseClass) { - classFunction.prototype = Object.create(baseClass.prototype); - classFunction.memberDefinitions = Object.create(baseClass.memberDefinitions || new MemberDefinitionCollection()); - classFunction.memberDefinitions.clearCache(); - - var staticDefs = baseClass.staticDefinitions; - if (staticDefs) { - staticDefs = staticDefs.asArray(); - if (staticDefs) { - for (var i = 0; i < staticDefs.length; i++) { - this.buildMember(classFunction, staticDefs[i], undefined, 'staticDefinitions'); - } - } - } - classFunction.baseTypes = baseClass.baseTypes ? [].concat(baseClass.baseTypes) : []; - for (var i = 0; i < baseClasses.length; i++) { - classFunction.baseTypes.push(baseClasses[i].type); - } - //classFunction.baseTypes = (baseClass.baseTypes || []).concat(baseClasses.map(function (base) { return base.type; })); - if (!classFunction.isAssignableTo) { - Object.defineProperty(classFunction, "isAssignableTo", { - value: function (type) { - return this === type || this.baseTypes.indexOf(type) >= 0; - }, - writable: false, - enumerable: false, - configurable: false - }); - } - } - - if (classDefinition) { - this.buildStaticMembers(classFunction, classDefinition); - - if (classDefinition.constructor) - classFunction.classConstructor = classDefinition.constructor; - } - - if (instanceDefinition) { - this.buildInstanceMembers(classFunction, instanceDefinition); - } - - var mixins = [].concat(baseClasses); - mixins.shift(); - if (Object.keys(mixins).length > 0) - this.buildInstanceMixins(classFunction, mixins); - - classFunction.__class = true; - - classFunction.prototype.constructor = classFunction; - - Object.defineProperty(classFunction.prototype, "getType", { - value: function () { - return classFunction; - }, - writable: false, - enumerable: false, - configurable: false - }); - }, - - addMethod: function (holder, name, method, propagation) { - if (!propagation || (typeof intellisense !== 'undefined')) { - holder[name] = method; - } else { - holder[name] = function () { - return method.apply(this[propagation], arguments); - }; - } - }, - - addProperty: function (holder, name, propertyDescriptor, propagation) { - - //holder[name] = {}; - - if (propagation) { - propertyDescriptor.configurable = true; - if (propertyDescriptor.get) { - var origGet = propertyDescriptor.get; - propertyDescriptor.get = function () { - if (!this[propagation]) - Guard.raise(new Exception("not inicialized")); - return origGet.apply(this[propagation], arguments); - }; - } - if (propertyDescriptor.set) { - var origSet = propertyDescriptor.set; - propertyDescriptor.set = function () { - if (!this[propagation]) - Guard.raise(new Exception("not inicialized")); - origSet.apply(this[propagation], arguments); - }; - } - } - - Object.defineProperty(holder, name, propertyDescriptor); - }, - - addField: function (holder, name, field) { - Guard.raise("not implemented"); - }, - - buildMethod: function (classFunction, memberDefinition, propagation) { - ///The object that will receive member - ///the newly added member - var holder = memberDefinition.classMember ? classFunction : classFunction.prototype; - this.addMethod(holder, memberDefinition.name, memberDefinition.method, propagation); - }, - - buildProperty: function (classFunction, memberDefinition, propagation) { - ///The object that will receive member - ///the newly added member - var holder = memberDefinition.classMember ? classFunction : classFunction.prototype; - var pd = memberDefinition.createPropertyDescriptor(classFunction); - this.addProperty(holder, memberDefinition.name, pd, propagation); - - //if lazyload TODO - if (!memberDefinition.classMember && classFunction.__setPropertyfunctions == true && memberDefinition.withoutGetSetMethod !== true && - !('get_' + memberDefinition.name in holder || 'set_' + memberDefinition.name in holder)) { - var pdGetMethod = memberDefinition.createGetMethod(); - this.addProperty(holder, 'get_' + memberDefinition.name, pdGetMethod, propagation); - - var pdSetMethod = memberDefinition.createSetMethod(); - this.addProperty(holder, 'set_' + memberDefinition.name, pdSetMethod, propagation); - } - }, - - - buildMember: function (classFunction, memberDefinition, propagation, memberCollectionName) { - /// - memberCollectionName = memberCollectionName || 'memberDefinitions'; - classFunction[memberCollectionName] = classFunction[memberCollectionName] || new MemberDefinitionCollection(); - classFunction[memberCollectionName].setMember(memberDefinition); - - switch (memberDefinition.kind) { - case MemberTypes.method: - this.buildMethod(classFunction, memberDefinition, propagation); - break; - case MemberTypes.navProperty: - case MemberTypes.complexProperty: - case MemberTypes.property: - this.buildProperty(classFunction, memberDefinition, propagation); - break; - default: Guard.raise("Unknown member type: " + memberDefinition.kind + "," + memberDefinition.name); - } - }, - - buildStaticMembers: function (classFunction, memberListDefinition) { - ///The class constructor that will be extended - /// - var t = this; - for (var item in memberListDefinition) { - if (memberListDefinition.hasOwnProperty(item)) { - var memberDefinition = MemberDefinition.translateDefinition(memberListDefinition[item], item, classFunction); - memberDefinition.classMember = true; - t.buildMember(classFunction, memberDefinition, undefined, 'staticDefinitions'); - } - } - }, - - buildInstanceMembers: function (classFunction, memberListDefinition) { - ///The class constructor whose prototype will be extended - /// - ///pinning t outside of the closure seems actually faster then passing in the this and referencing - var t = this; - for (var item in memberListDefinition) { - if (memberListDefinition.hasOwnProperty(item)) { - var memberDefinition = MemberDefinition.translateDefinition(memberListDefinition[item], item, classFunction); - t.buildMember(classFunction, memberDefinition, undefined, 'memberDefinitions'); - } - } - }, - - copyMembers: function (sourceType, targetType) { - /// - /// - function il(msg) { - if (typeof intellisense === 'undefined') { - return; - } - intellisense.logMessage(msg); - } - - Object.keys(sourceType.prototype).forEach(function (item, i, src) { - if (item !== 'constructor' && item !== 'toString') { - il("copying item:" + item); - targetType.prototype[item] = sourceType[item]; - } - }); - }, - - buildInstanceMixins: function (classFunction, mixinList) { - ///The class constructor whose prototype will be extended - /// - - classFunction.mixins = classFunction.mixins || []; - classFunction.propagations = classFunction.propagations || []; - - for (var i = 0; i < mixinList.length; i++) { - var item = mixinList[i]; - //if (classFunction.memberDefinitions.getMember(item.type.name)) { - if (item.propagateTo) { - this.buildInstancePropagation(classFunction, item); - classFunction.propagations.push(item); - classFunction.propagations[item.type.name] = true; - } else { - this.buildInstanceMixin(classFunction, item); - classFunction.mixins.push(item); - classFunction.mixins[item.type.name] = true; - } - }; - }, - buildInstanceMixin: function (classFunction, typeObj) { - ///The class constructor whose prototype will be extended - /// - - var memberDefs = typeObj.type.memberDefinitions.asArray(); - for (var i = 0, l = memberDefs.length; i < l; i++) { - var itemName = memberDefs[i].name; - if (itemName !== 'constructor' && !classFunction.memberDefinitions.getMember(itemName)) { - this.buildMember(classFunction, memberDefs[i]); - } - } - - if (typeObj.type.staticDefinitions) { - var staticDefs = typeObj.type.staticDefinitions.asArray(); - for (var i = 0, l = staticDefs.length; i < l; i++) { - var itemName = staticDefs[i].name; - if (itemName !== 'constructor' && !classFunction.memberDefinitions.getMember(itemName)) { - this.buildMember(classFunction, staticDefs[i], undefined, 'staticDefinitions'); - } - } - } - }, - buildInstancePropagation: function (classFunction, typeObj) { - ///The class constructor whose prototype will be extended - /// - - var memberDefs = typeObj.type.memberDefinitions.asArray(); - for (var i = 0, l = memberDefs.length; i < l; i++) { - var itemName = memberDefs[i].name; - if (itemName !== 'constructor' && !classFunction.memberDefinitions.getMember(itemName)) { - this.buildMember(classFunction, memberDefs[i], typeObj.propagateTo); - } - } - } - - }; - - $data.Class = Class = new ClassEngineBase(); - - //(function (global) { - global = window; - function ContainerCtor(parentContainer) { - var parent = parentContainer; - if (parent) { - parent.addChildContainer(this); - } - - var classNames = {}; - var consolidatedClassNames = []; - var classTypes = []; - - this.classNames = classNames; - this.consolidatedClassNames = consolidatedClassNames; - this.classTypes = classTypes; - - var mappedTo = []; - this.mappedTo = mappedTo; - - var self = this; - - this["holder"] = null; - - var IoC = function (type, parameters) { - var t = self.resolveType(type); - var inst = Object.create(t.prototype); - t.apply(inst, parameters); - return inst; - }; - - var pendingResolutions = {}; - this.pendingResolutions = pendingResolutions; - - function addPendingResolution(name, onResolved) { - pendingResolutions[name] = pendingResolutions[name] || []; - pendingResolutions[name].push(onResolved); - } - - this.addChildContainer = function (container) { - //children.push(container); - } - - this.createInstance = function (type, parameters) { return IoC(type, parameters); }; - - this.mapType = function (aliasTypeOrName, realTypeOrName) { - Guard.requireValue("aliasType", aliasTypeOrName); - Guard.requireValue("realType", realTypeOrName); - var aliasT = this.getType(aliasTypeOrName); - var realT = this.getType(realTypeOrName); - var aliasPos = classTypes.indexOf(aliasT); - var realPos = classTypes.indexOf(realT); - mappedTo[aliasPos] = realPos; - }, - - //this.resolve = function (type, parameters) { - // var classFunction = this.resolveType(type, parameters); - // return new classFunction(parameters); - //}; - - - - this.isPrimitiveType = function (type) { - var t = this.resolveType(type); - - switch (true) { - case t === Number: - case t === String: - case t === Date: - case t === Boolean: - case t === Array: - case t === Object: - - case t === $data.Number: - case t === $data.Integer: - case t === $data.Date: - case t === $data.String: - case t === $data.Boolean: - case t === $data.Array: - case t === $data.Object: - case t === $data.Guid: - - case t === $data.Byte: - case t === $data.SByte: - case t === $data.Decimal: - case t === $data.Float: - case t === $data.Int16: - case t === $data.Int32: - case t === $data.Int64: - case t === $data.DateTimeOffset: - case t === $data.Time: - - case t === $data.SimpleBase: - case t === $data.Geospatial: - case t === $data.GeographyBase: - case t === $data.GeographyPoint: - case t === $data.GeographyLineString: - case t === $data.GeographyPolygon: - case t === $data.GeographyMultiPoint: - case t === $data.GeographyMultiLineString: - case t === $data.GeographyMultiPolygon: - case t === $data.GeographyCollection: - case t === $data.GeometryBase: - case t === $data.GeometryPoint: - case t === $data.GeometryLineString: - case t === $data.GeometryPolygon: - case t === $data.GeometryMultiPoint: - case t === $data.GeometryMultiLineString: - case t === $data.GeometryMultiPolygon: - case t === $data.GeometryCollection: - - return true; - default: - return false; - } - - //return t === Number || t === String || t === Date || t === String || t === Boolean || t === Array || t === Object || - // t === $data.Number || t === $data.Integer || t === $data.Date || t === $data.String || t === $data.Boolean || t === $data.Array || t === $data.Object || - // t === $data.GeographyPoint || t === $data.Guid; - }; - - - this.resolveName = function (type) { - var t = this.resolveType(type); - var tPos = classTypes.indexOf(t); - return consolidatedClassNames[tPos]; - }; - - this.resolveType = function (typeOrName, onResolved) { - //if ("string" === typeof typeOrName) { - // console.log("@@@@String type!!!", typeOrName) - //} - var t = typeOrName; - t = this.getType(t, onResolved ? true : false, onResolved); - var posT = classTypes.indexOf(t); - return typeof mappedTo[posT] === 'undefined' ? t : classTypes[mappedTo[posT]]; - }; - - - - this.getType = function (typeOrName, doNotThrow, onResolved) { - Guard.requireValue("typeOrName", typeOrName); - if (typeof typeOrName === 'function') { - return typeOrName; - }; - - if (!(typeOrName in classNames)) { - if (parent) { - var tp = parent.getType(typeOrName, true); - if (tp) return tp; - } - if (onResolved) { - addPendingResolution(typeOrName, onResolved); - return; - } - else if (doNotThrow) { - return undefined; - } else { - Guard.raise(new Exception("Unable to resolve type:" + typeOrName)); - } - }; - var result = classTypes[classNames[typeOrName]]; - if (onResolved) { - onResolved(result); - } - return result; - }; - - this.getName = function (typeOrName) { - var t = this.getType(typeOrName); - var tPos = classTypes.indexOf(t); - if (tPos == -1) - Guard.raise("unknown type to request name for: " + typeOrName); - return consolidatedClassNames[tPos]; - }; - - this.getTypes = function () { - var keys = Object.keys(classNames); - var ret = []; - for (var i = 0; i < keys.length; i++) { - var className = keys[i]; - ret.push({ name: className, type: classTypes[classNames[className]], toString: function () { return this.name; } }); - } - return ret; - }; - - //this.getTypeName( in type); - //this.resolveType() - //this.inferTypeFromValue = function (value) { - - this.getTypeName = function (value) { - //TODO refactor - switch (typeof value) { - case 'object': - if (value == null) return '$data.Object'; - if (value instanceof Array) return '$data.Array'; - if (value.getType) return value.getType().fullName; - if (value instanceof Date) return '$data.Date'; - if (value instanceof $data.Guid) return '$data.Guid'; - if (value instanceof $data.DateTimeOffset) return '$data.DateTimeOffset'; - if (value instanceof $data.GeographyPoint) return '$data.GeographyPoint'; - if (value instanceof $data.GeographyLineString) return '$data.GeographyLineString'; - if (value instanceof $data.GeographyPolygon) return '$data.GeographyPolygon'; - if (value instanceof $data.GeographyMultiPoint) return '$data.GeographyMultiPoint'; - if (value instanceof $data.GeographyMultiLineString) return '$data.GeographyMultiLineString'; - if (value instanceof $data.GeographyMultiPolygon) return '$data.GeographyMultiPolygon'; - if (value instanceof $data.GeographyCollection) return '$data.GeographyCollection'; - if (value instanceof $data.GeographyBase) return '$data.GeographyBase'; - if (value instanceof $data.GeometryPoint) return '$data.GeometryPoint'; - if (value instanceof $data.GeometryLineString) return '$data.GeometryLineString'; - if (value instanceof $data.GeometryPolygon) return '$data.GeometryPolygon'; - if (value instanceof $data.GeometryMultiPoint) return '$data.GeometryMultiPoint'; - if (value instanceof $data.GeometryMultiLineString) return '$data.GeometryMultiLineString'; - if (value instanceof $data.GeometryMultiPolygon) return '$data.GeometryMultiPolygon'; - if (value instanceof $data.GeometryCollection) return '$data.GeometryCollection'; - if (value instanceof $data.GeometryBase) return '$data.GeometryBase'; - if (value instanceof $data.Geospatial) return '$data.Geospatial'; - if (value instanceof $data.SimpleBase) return '$data.SimpleBase'; - if (typeof value.toHexString === 'function') return '$data.ObjectID'; - //if(value instanceof "number") return - default: - return typeof value; - } - }; - - this.isTypeRegistered = function (typeOrName) { - if (typeof typeOrName === 'function') { - return classTypes.indexOf(typeOrName) > -1; - } else { - return typeOrName in classNames; - } - }; - - this.unregisterType = function (type) { - Guard.raise("Unimplemented"); - }; - - - - this.getDefault = function (typeOrName) { - var t = this.resolveType(typeOrName); - switch (t) { - case $data.Number: return 0.0; - case $data.Float: return 0.0; - case $data.Decimal: return '0.0'; - case $data.Integer: return 0; - case $data.Int16: return 0; - case $data.Int32: return 0; - case $data.Int64: return '0'; - case $data.Byte: return 0; - case $data.SByte: return 0; - case $data.String: return null; - case $data.Boolean: return false; - default: return null; - } - }; - - //name array ['', '', ''] - this.getIndex = function (typeOrName) { - var t = this.resolveType(typeOrName); - return classTypes.indexOf(t); - } - - this.resolveByIndex = function (index) { - return classTypes[index]; - } - - this.registerType = function (nameOrNamesArray, type, factoryFunc) { - /// - ///Registers a type and optionally a lifetimeManager with a name - ///that can be used to later resolve the type or create new instances - ///The names of the type - ///The type to register - /// - /// - /// - ///Registers a new type that - ///The name of the type - ///The type to register - /// - - - ///TODO remove - /*if (typeof typeNameOrAlias === 'string') { - if (classNames.indexOf(typeNameOrAlias) > -1) { - Guard.raise("Type already registered. Remove first"); - } - }*/ - - if (!nameOrNamesArray) { - return; - } - - //todo add ('number', 'number') - if (typeof type === "string") { - type = self.resolveType(type); - } - - var namesArray = []; - if (typeof nameOrNamesArray === 'string') { - var tmp = []; - tmp.push(nameOrNamesArray); - namesArray = tmp; - } else { - namesArray = nameOrNamesArray; - } - - for (var i = 0; i < namesArray.length; i++) { - var parts = namesArray[i].split('.'); - var item = {}; - item.shortName = parts[parts.length - 1]; - item.fullName = namesArray[i]; - namesArray[i] = item; - } - - //if (type. - - - var creatorFnc = function () { return IoC(type, arguments); }; - - if (typeof intellisense !== 'undefined') { - intellisense.annotate(creatorFnc, type); - } - - for (var i = 0, l = namesArray.length; i < l; i++) { - var item = namesArray[i]; - if (!(("create" + item.shortName) in self)) { - if (typeof factoryFunc === 'function') { - self["create" + item.shortName] = factoryFunc; - } else { - self["create" + item.shortName] = creatorFnc; - } - } - - var typePos = classTypes.indexOf(type); - if (typePos == -1) { - //new type - typePos = classTypes.push(type) - 1; - var fn = item.fullName; - consolidatedClassNames[typePos] = item.fullName; - }; - - classNames[item.fullName] = typePos; - - var pending = pendingResolutions[item.fullName] || []; - if (pending.length > 0) { - pending.forEach(function (t) { - t(type); - }); - pendingResolutions[item.fullName] = []; - } - } - if (parent) { - parent.registerType.apply(parent, arguments); - } - if (!type.name) { - type.name = namesArray[0].shortName; - } - }; - - - var _converters = { - from: {}, - to: {} - }; - this.converters = _converters; - - this.convertTo = function (value, tType, eType /*if Array*/, options) { - Guard.requireValue("typeOrName", tType); - - if(Object.isNullOrUndefined(value)) - return value; - - var sourceTypeName = Container.getTypeName(value); - var sourceType = Container.resolveType(sourceTypeName); - var sourceTypeName = Container.resolveName(sourceType); - var targetType = Container.resolveType(tType); - var targetTypeName = Container.resolveName(targetType); - - var result; - try { - if (typeof targetType['from' + sourceTypeName] === 'function') { - // target from - result = targetType['from' + sourceTypeName].apply(targetType, arguments); - - } else if (typeof sourceType['to' + targetTypeName] === 'function') { - // source to - result = sourceType['to' + targetTypeName].apply(sourceType, arguments); - - } else if (_converters.to[targetTypeName] && _converters.to[targetTypeName][sourceTypeName]) { - // target from source - result = _converters.to[targetTypeName][sourceTypeName].apply(_converters, arguments); - - } else if (_converters.from[sourceTypeName] && _converters.from[sourceTypeName][targetTypeName]) { - // source to target - result = _converters.from[sourceTypeName][targetTypeName].apply(_converters, arguments); - - } else if (targetTypeName === sourceTypeName || value instanceof targetType) { - result = value; - - } else if (_converters.to[targetTypeName] && _converters.to[targetTypeName]['default']) { - // target from anything - result = _converters.to[targetTypeName]['default'].apply(_converters, arguments); - - } else { - throw "converter not found"; - } - } catch (e) { - Guard.raise(new Exception("Value '" + sourceTypeName + "' not convertable to '" + targetTypeName + "'", 'TypeError', value)); - } - - if (targetType === $data.Array && eType && Array.isArray(result)) { - for (var i = 0; i < result.length; i++) { - result[i] = this.convertTo.call(this, result[i], eType, undefined, options); - } - } - - return result; - }; - this.registerConverter = function (target, sourceOrToConverters, toConverterOrFromConverters, fromConverter) { - //registerConverter($data.Guid, { $data.String: fn, int: fn }, { string: fn, int:fn }) - //registerConverter($data.Guid, $data.String, fn, fn); - - var targetName = Container.resolveName(target); - if (Container.isTypeRegistered(sourceOrToConverters)) { - //isSource - _converters.to[targetName] = _converters.to[targetName] || {}; - _converters.from[targetName] = _converters.from[targetName] || {}; - - var sourceName = Container.resolveName(sourceOrToConverters); - - if (toConverterOrFromConverters) - _converters.to[targetName][sourceName] = toConverterOrFromConverters; - if (fromConverter) - _converters.from[targetName][sourceName] = fromConverter; - - } else { - // converterGroup - - //fromConverters - if (_converters.to[targetName]) { - _converters.to[targetName] = $data.typeSystem.extend(_converters.to[targetName], sourceOrToConverters); - } else { - _converters.to[targetName] = sourceOrToConverters; - } - - //toConverters - if (_converters.from[targetName]) { - _converters.from[targetName] = $data.typeSystem.extend(_converters.from[targetName], toConverterOrFromConverters); - } else { - _converters.from[targetName] = toConverterOrFromConverters; - } - } - }; - } - $data.ContainerClass = ContainerCtor; - - var c; - - global["Container"] = $data.Container = c = global["C$"] = new ContainerCtor(); - - $data.createContainer = function () { - return new ContainerCtor($data.Container); - } - - //})(window); - - global["$C"] = function () { Class.define.apply(Class, arguments); }; - - - var storeProperty = function (memberDefinition, value) { - var backingFieldName = "_" + memberDefinition.name; - if (!this[backingFieldName]) { - Object.defineProperty(this, backingFieldName, memberDefinition.createStorePropertyDescriptor(value)); - } - else { - this[backingFieldName] = value; - } - }; - var retrieveProperty = function (memberDefinition) { - var backingFieldName = "_" + memberDefinition.name; - return this[backingFieldName]; - }; - - - $data.Class.define('$data.Base', function Base() { }, null, { - storeProperty: storeProperty, - retrieveProperty: retrieveProperty, - setProperty: function (memberDefinition, value, callback) { - this[memberDefinition.name] = value; - callback(); - }, - getProperty: function (memberDefinition, callback) { - callback.apply(this, [this[memberDefinition.name]]); - } - }, { - create: function () { return Container.createInstance(this, arguments); }, - extend: function (name, container, instanceDefinition, classDefinition) { - if (container && !(container instanceof ContainerCtor)) { - classDefinition = instanceDefinition; - instanceDefinition = container; - container = undefined; - } - return $data.Class.define(name, this, container, instanceDefinition, classDefinition); - }, - getMemberDefinition: function (name) { - return this.memberDefinitions.getMember(name); - }, - addProperty: function (name, getterOrType, setterOrGetter, setter) { - var _getter = getterOrType; - var _setter = setterOrGetter; - var _type; - if (typeof _getter === 'string') { - _type = getterOrType; - _getter = setterOrGetter; - _setter = setter; - } - - var propDef = { - notMapped: true, - storeOnObject: true, - get: typeof _getter === 'function' ? _getter : function () { }, - set: typeof _setter === 'function' ? _setter : function () { }, - type: _type - }; - - var memberDefinition = MemberDefinition.translateDefinition(propDef, name, this); - $data.Class.buildMember(this, memberDefinition); - - this.memberDefinitions.clearCache(); - - return this; - }, - addMember: function (name, definition, isClassMember) { - var memberDefinition = MemberDefinition.translateDefinition(definition, name, this); - - if (isClassMember) { - memberDefinition.classMember = true; - $data.Class.buildMember(this, memberDefinition, undefined, 'staticDefinitions'); - this.staticDefinitions.clearCache(); - } else { - $data.Class.buildMember(this, memberDefinition); - this.memberDefinitions.clearCache(); - } - return this; - }, - describeField: function (name, definition) { - var memDef = this.memberDefinitions.getMember(name); - if (!memDef) { - this.addMember(name, definition); - } else { - Guard.raise(new Exception("Field '" + name + "' already defined!", "Invalid operation")); - } - return this; - }, - storeProperty: storeProperty, - retrieveProperty: retrieveProperty, - 'from$data.Object': function (value) { - return value; - } - }); - - - //override after typeSystem initialized - - - $data.Class.ConstructorParameter = ConstructorParameter = $data.Class.define('ConstructorParameter', null, null, { - constructor: function (paramIndex) { - /// - this.paramIndex = paramIndex; - }, - paramIndex: {} - }); - /*$data.Class.MixinParameter = MixinParameter = $data.Class.define('MixinParameter', null, null, { - constructor: function (typeName) { - /// - this.typeName = typeName; - }, - typeName: {} - });*/ - - //var e = new Entity(); - - - /*$data.Interface = Class.define("Interface", null, null, { - constructor: function() { Guard.raise("Can not create an interface"); } - }, - { - define: function (name, definition) { - var result = Class.define(name, $data.Interface, null, null, definition); - delete result.__class; - result.__interface = true; - return result; - } - }); - - - - $data.Observable = Observable = Class.define("Observable", null, null, { - propertyChanged: { dataType: $data.Event } - }, { - createFromInstance: function(instance) { - var propNames = instance.getClass().memberDefinitions.f - } - });*/ - - - -})($data, window); - -$data.defaultErrorCallback = function () { - //console.log('DEFAULT ERROR CALLBACK:'); - /*if (console.dir) - console.dir(arguments); - else - console.log(arguments);*/ - if (arguments.length > 0 && arguments[arguments.length - 1] && typeof arguments[arguments.length - 1].reject === 'function') { - (console.error || console.log).call(console, arguments[0]); - arguments[arguments.length - 1].reject.apply(arguments[arguments.length - 1], arguments); - } else { - if (arguments[0] instanceof Error) { - Guard.raise(arguments[0]); - } else { - Guard.raise(new Exception("DEFAULT ERROR CALLBACK!", "DefaultError", arguments)); - } - } -}; -$data.defaultSuccessCallback = function () { /*console.log('DEFAULT SUCCES CALLBACK');*/ }; -$data.defaultNotifyCallback = function () { /*console.log('DEFAULT NOTIFY CALLBACK');*/ }; - -$data.typeSystem = { - __namespace: true, - /*inherit: function (ctor, baseType) { - var proto = new baseType(); - ctor.prototype = $.extend(proto, ctor.prototype); - //console.dir(proto); - ctor.prototype.base = new baseType(); - //console.dir(ctor.prototype.base); - ctor.prototype.constructor = ctor; - return ctor; - },*/ - //mix: function (type, mixin) { - // type.prototype = $.extend(type.prototype || {}, mixin.prototype || {}); - // type.mixins = type.mixins || []; - // type.mixins.push(mixin); - // return type; - //}, - extend: function (target) { - /// - /// Extends an object with properties of additional parameters. - /// - /// - /// Object that will be extended. - /// Object to extend target with. - /// Object to extend target with. - /// - /// - if (typeof target !== 'object' && typeof target !== 'function') - Guard.raise('Target must be object or function'); - - for (var i = 1; i < arguments.length; i++) { - var obj = arguments[i]; - if (obj === null || typeof obj === 'undefined') - continue; - for (key in obj) { - target[key] = obj[key]; - } - } - return target; - }, - createCallbackSetting: function (callBack, defaultSetting) { - var setting = { - success: $data.defaultSuccessCallback, - error: $data.defaultErrorCallback, - notify: $data.defaultNotifyCallback - }; - - if (defaultSetting != undefined && defaultSetting != null) { - setting = defaultSetting; - } - - var result; - if (callBack == null || callBack == undefined) { - result = setting; - - } else if (typeof callBack == 'function') { - result = this.extend(setting, { success: callBack }); - - } else { - result = this.extend(setting, callBack); - } - - function wrapCode(fn) { var t = this; function r() { fn.apply(t, arguments); fn = function () { } } return r; } - - if (typeof result.error === 'function') - result.error = wrapCode(result.error); - - return result; - }, - createCtorParams: function (source, indexes, thisObj) { - ///Paramerter array - /// - /// - if (indexes) { - var paramArray = []; - for (var i = 0, l = indexes.length; i < l; i++) { - var item = i; - if (indexes[item] instanceof ConstructorParameter) - paramArray.push(source[indexes[item].paramIndex]); - else if (typeof indexes[item] === "function") - paramArray.push(indexes[item].apply(thisObj)); - else - paramArray.push(indexes[item]); - } - return paramArray; - } - return source; - }, - writePropertyValues: function (obj) { - if (obj && obj.getType && obj.getType().memberDefinitions) { - this.writeProperties(obj, obj.getType().memberDefinitions.asArray().filter( - function (md) { return (md.kind == "property" || md.kind == "navProperty" || md.kind == "complexProperty") && !md.prototypeProperty; } - )); - } - }, - writeProperties: function (obj, members) { - var defines = {}; - for (var i = 0, l = members.length; i < l; i++) { - var memDef = members[i]; - defines[memDef.name] = memDef.createPropertyDescriptor(null, memDef.value); - } - - Object.defineProperties(obj, defines); - - }, - writeProperty: function (obj, member, value) { - var memDef = typeof member === 'string' ? obj.getType().memberDefinitions.getMember(member) : member; - if (memDef) { - var propDef = memDef.createPropertyDescriptor(null, value); - //////OPTIMIZATION - Object.defineProperty(obj, memDef.name, propDef); - } - } -}; - -$data.debug = function () { - (console.debug || console.log).apply(console, arguments); -}; - -$data.debugWith = function () { - var cArgs = arguments; - return function (r) { - (console.debug || console.log).apply(console, cArgs); - if ((typeof Error !== 'undefined' && r instanceof Error) || - (typeof Exception !== 'undefined' && r instanceof Exception)) { - (console.error || console.log).apply(console, arguments); - } else { - (console.debug || console.log).apply(console, arguments); - } - } -}; - -$data.fdebug = { - success: $data.debugWith('success'), - error: $data.debugWith('error') -}; -$data.Number = typeof Number !== 'undefined' ? Number : function JayNumber() { }; -$data.Date = typeof Date !== 'undefined' ? Date : function JayDate() { }; -$data.String = typeof String !== 'undefined' ? String : function JayString() { }; -$data.Boolean = typeof Boolean !== 'undefined' ? Boolean : function JayBoolean() { }; -$data.Array = typeof Array !== 'undefined' ? Array : function JayArray() { }; -$data.Object = typeof Object !== 'undefined' ? Object : function JayObject() { }; -$data.Function = Function; - -$data.Byte = function JayByte() { }; -$data.SByte = function JaySByte() { }; -$data.Decimal = function JayDecimal() { }; -$data.Float = $data.Single = function JayFloat() { }; -$data.Integer = function JayInteger() { }; -$data.Int16 = function JayInt16(v) { }; -$data.Int32 = function JayInt32() { }; -$data.Int64 = function JayInt64(v) { }; -$data.ObjectID = typeof $data.mongoDBDriver !== 'undefined' && typeof $data.mongoDBDriver.ObjectID !== 'undefined' ? $data.mongoDBDriver.ObjectID : function JayObjectID() { }; -$data.Time = function JayTime() { }; -$data.DateTimeOffset = function JayDateTimeOffset(val) { - this.value = val; -}; -$data.DateTimeOffset.prototype.toJSON = function () { - return this.value instanceof Date ? this.value.toISOString() : this.value; -}; - -$data.Container.registerType(["$data.Number", "number", "JayNumber", "double"], $data.Number); -$data.Container.registerType(["$data.Integer", "int", "integer", "JayInteger"], $data.Integer); -$data.Container.registerType(["$data.Int32", "int32", "JayInt32"], $data.Int32); -$data.Container.registerType(["$data.Byte", "byte", "JayByte"], $data.Byte); -$data.Container.registerType(["$data.SByte", "sbyte", "JaySByte"], $data.SByte); -$data.Container.registerType(["$data.Decimal", "decimal", "JayDecimal"], $data.Decimal); -$data.Container.registerType(["$data.Float", "$data.Single", "float", "single", "JayFloat"], $data.Float); -$data.Container.registerType(["$data.Int16", "int16", "word", "JayInt16"], $data.Int16); -$data.Container.registerType(["$data.Int64", "int64", "long", "JayInt64"], $data.Int64); -$data.Container.registerType(["$data.String", "string", "text", "character", "JayString"], $data.String); -$data.Container.registerType(["$data.Array", "array", "Array", "[]", "JayArray"], $data.Array, function () { - return $data.Array.apply(undefined, arguments); -}); -$data.Container.registerType(["$data.Date", "datetime", "date", "JayDate"], $data.Date); -$data.Container.registerType(["$data.Time", "time", "JayTime"], $data.Time); -$data.Container.registerType(["$data.DateTimeOffset", "offset", "datetimeoffset", "JayDateTimeOffset"], $data.DateTimeOffset); -$data.Container.registerType(["$data.Boolean", "bool", "boolean", "JayBoolean"], $data.Boolean); -$data.Container.registerType(["$data.Object", "Object", "object", "{}", "JayObject"], $data.Object); -$data.Container.registerType(["$data.Function", "Function", "function"], $data.Function); -$data.Container.registerType(['$data.ObjectID', 'ObjectID', 'objectId', 'objectid', 'ID', 'Id', 'id', 'JayObjectID'], $data.ObjectID); -$data.Class.define('$data.TraceBase', null, null, { - log: function () { }, - warn: function () { }, - error: function () { } -}); - -$data.Trace = new $data.TraceBase(); -$data.Class.define('$data.Logger', $data.TraceBase, null, { - log: function () { - Array.prototype.unshift.call(arguments, this.getDateFormat()); - console.log.apply(console, arguments); - }, - warn: function () { - Array.prototype.unshift.call(arguments, this.getDateFormat()); - console.warn.apply(console, arguments); - }, - error: function () { - Array.prototype.unshift.call(arguments, this.getDateFormat()); - console.error.apply(console, arguments); - }, - - getDateFormat: function () { - var date = new Date(); - return date.getHours() + ':' + date.getMinutes() + ':' + date.getSeconds() + '.' + date.getMilliseconds(); - } -}); -$data.Number = typeof Number !== 'undefined' ? Number : function JayNumber() { }; -$data.Date = typeof Date !== 'undefined' ? Date : function JayDate() { }; -$data.String = typeof String !== 'undefined' ? String : function JayString() { }; -$data.Boolean = typeof Boolean !== 'undefined' ? Boolean : function JayBoolean() { }; -$data.Array = typeof Array !== 'undefined' ? Array : function JayArray() { }; -$data.Object = typeof Object !== 'undefined' ? Object : function JayObject() { }; -$data.Function = Function; - -$data.Byte = function JayByte() { }; -$data.SByte = function JaySByte() { }; -$data.Decimal = function JayDecimal() { }; -$data.Float = $data.Single = function JayFloat() { }; -$data.Integer = function JayInteger() { }; -$data.Int16 = function JayInt16(v) { }; -$data.Int32 = function JayInt32() { }; -$data.Int64 = function JayInt64(v) { }; -$data.ObjectID = typeof $data.mongoDBDriver !== 'undefined' && typeof $data.mongoDBDriver.ObjectID !== 'undefined' ? $data.mongoDBDriver.ObjectID : function JayObjectID() { }; -$data.Time = function JayTime() { }; -$data.DateTimeOffset = function JayDateTimeOffset(val) { - this.value = val; -}; -$data.DateTimeOffset.prototype.toJSON = function () { - return this.value instanceof Date ? this.value.toISOString() : this.value; -}; - -$data.Container.registerType(["$data.Number", "number", "JayNumber", "double"], $data.Number); -$data.Container.registerType(["$data.Integer", "int", "integer", "JayInteger"], $data.Integer); -$data.Container.registerType(["$data.Int32", "int32", "JayInt32"], $data.Int32); -$data.Container.registerType(["$data.Byte", "byte", "JayByte"], $data.Byte); -$data.Container.registerType(["$data.SByte", "sbyte", "JaySByte"], $data.SByte); -$data.Container.registerType(["$data.Decimal", "decimal", "JayDecimal"], $data.Decimal); -$data.Container.registerType(["$data.Float", "$data.Single", "float", "single", "JayFloat"], $data.Float); -$data.Container.registerType(["$data.Int16", "int16", "word", "JayInt16"], $data.Int16); -$data.Container.registerType(["$data.Int64", "int64", "long", "JayInt64"], $data.Int64); -$data.Container.registerType(["$data.String", "string", "text", "character", "JayString"], $data.String); -$data.Container.registerType(["$data.Array", "array", "Array", "[]", "JayArray"], $data.Array, function () { - return $data.Array.apply(undefined, arguments); -}); -$data.Container.registerType(["$data.Date", "datetime", "date", "JayDate"], $data.Date); -$data.Container.registerType(["$data.Time", "time", "JayTime"], $data.Time); -$data.Container.registerType(["$data.DateTimeOffset", "offset", "datetimeoffset", "JayDateTimeOffset"], $data.DateTimeOffset); -$data.Container.registerType(["$data.Boolean", "bool", "boolean", "JayBoolean"], $data.Boolean); -$data.Container.registerType(["$data.Object", "Object", "object", "{}", "JayObject"], $data.Object); -$data.Container.registerType(["$data.Function", "Function", "function"], $data.Function); -$data.Container.registerType(['$data.ObjectID', 'ObjectID', 'objectId', 'objectid', 'ID', 'Id', 'id', 'JayObjectID'], $data.ObjectID); -/* $data.SimpleBase */ -$data.SimpleBase = function SimpleBase(data) { - if (typeof data === 'object' && data) { - if (Array.isArray(this.constructor.validMembers)) { - for (var i = 0; i < this.constructor.validMembers.length; i++) { - var name = this.constructor.validMembers[i]; - - if (data[name] !== undefined) { - this[name] = data[name]; - } - } - - } else { - delete data.type; - $data.typeSystem.extend(this, data); - } - } -} -$data.SimpleBase.registerType = function (name, type, base) { - base = base || $data.SimpleBase; - - type.type = name; - type.prototype = Object.create(base.prototype); - type.prototype.constructor = type; -} -$data.Container.registerType(['$data.SimpleBase', 'SimpleBase'], $data.SimpleBase);$data.Geospatial = function Geospatial() { - this.type = this.constructor.type; - if (Array.isArray(this.constructor.validMembers)) { - for (var i = 0; i < this.constructor.validMembers.length; i++) { - var name = this.constructor.validMembers[i]; - this[name] = undefined; - } - } - - $data.SimpleBase.apply(this, arguments); - this.type = this.constructor.type || 'Unknown'; -}; -$data.SimpleBase.registerType('Geospatial', $data.Geospatial); -$data.Container.registerType(['$data.Geospatial', 'Geospatial'], $data.Geospatial); - -$data.point = function (arg) { - if (arg && arg.crs) { - if (arg.crs.properties && arg.crs.properties.name === $data.GeometryBase.defaultCrs.properties.name) { - return new $data.GeometryPoint(arg); - } else { - return new $data.GeographyPoint(arg); - } - } else if(arg) { - if ('x' in arg && 'y' in arg) { - return new $data.GeometryPoint(arg.x, arg.y); - } else if ('longitude' in arg && 'latitude' in arg) { - return new $data.GeographyPoint(arg.longitude, arg.latitude); - } else if ('lng' in arg && 'lat' in arg) { - return new $data.GeographyPoint(arg.lng, arg.lat); - } - } -}; -/* $data.GeographyBase */ -$data.GeographyBase = function GeographyBase() { - $data.Geospatial.apply(this, arguments); - - this.crs = $data.GeographyBase.defaultCrs; - $data.GeographyBase.validateGeoJSON(this); -}; - -$data.GeographyBase.defaultCrs = { - properties: { - name: 'EPSG:4326' - }, - type: 'name' -}; - -$data.GeographyBase.parseFromString = function (strData) { - var lparenIdx = strData.indexOf('('); - if(lparenIdx >= 0){ - var name = strData.substring(0, lparenIdx).toLowerCase(); - var type = $data.GeographyBase.registered[name]; - - if (type && type.parseFromString && type != $data.GeographyBase) { - return type.parseFromString(strData); - } else { - Guard.raise(new Exception('parseFromString', 'Not Implemented', strData)); - } - } -}; -$data.GeographyBase.stringifyToUrl = function (geoData) { - if (geoData instanceof $data.GeographyBase && geoData.constructor && geoData.constructor.stringifyToUrl) { - return geoData.constructor.stringifyToUrl(geoData); - } else if (geoData instanceof $data.GeographyBase && geoData.constructor && Array.isArray(geoData.constructor.validMembers) && geoData.constructor.validMembers[0] === 'coordinates') { - var data = "geography'" + geoData.type.toUpperCase() + '('; - function buildArray(d, context) { - if (Array.isArray(d[0])) { - - for (var i = 0; i < d.length; i++) { - if (i > 0) data += ','; - if (Array.isArray(d[i][0])) - data += '('; - - buildArray(d[i]); - - if (Array.isArray(d[i][0])) - data += ')'; - } - - } else { - data += d.join(' '); - } - } - buildArray(geoData.coordinates, data); - - data += ")'"; - return data; - } else { - Guard.raise(new Exception('stringifyToUrl on instance type', 'Not Implemented', geoData)); - } -}; -$data.GeographyBase.registerType = function (name, type, base) { - $data.SimpleBase.registerType(name, type, base || $data.GeographyBase); - - $data.GeographyBase.registered = $data.GeographyBase.registered || {}; - $data.GeographyBase.registered[name.toLowerCase()] = type; -}; -$data.GeographyBase.validateGeoJSON = function (geoData) { - var type = geoData.type; - if (type) { - var geoType = $data.GeographyBase.registered[type.toLowerCase()]; - if (typeof geoType.validateGeoJSON === 'function') { - var isValid = geoType.validateGeoJSON(geoData); - if (isValid) { - return isValid; - } else { - Guard.raise(new Exception("Invalid '" + type + "' format!", 'Format Exception', geoData)); - } - } - } - console.log('GeoJSON validation missing', geoData); - return; -}; -$data.SimpleBase.registerType('GeographyBase', $data.GeographyBase, $data.Geospatial); -$data.Container.registerType(['$data.GeographyBase'], $data.GeographyBase); - -/* $data.GeographyPoint */ -$data.GeographyPoint = function GeographyPoint(lon, lat) { - if (lon && typeof lon === 'object' && Array.isArray(lon)) { - $data.GeographyBase.call(this, { coordinates: lon }); - } else if (lon && typeof lon === 'object' && ('longitude' in lon || 'latitude' in lon)) { - $data.GeographyBase.call(this, { coordinates: [lon.longitude, lon.latitude] }); - } else if (lon && typeof lon === 'object' && ('lng' in lon || 'lat' in lon)) { - $data.GeographyBase.call(this, { coordinates: [lon.lng, lon.lat] }); - } else if (lon && typeof lon === 'object') { - $data.GeographyBase.call(this, lon); - } else { - $data.GeographyBase.call(this, { coordinates: [lon || 0, lat || 0] }); - } -}; -$data.GeographyPoint.validateGeoJSON = function (geoData) { - return geoData && - Array.isArray(geoData.coordinates) && - geoData.coordinates.length == 2 && - typeof geoData.coordinates[0] === 'number' && - typeof geoData.coordinates[1] === 'number'; -}; -$data.GeographyPoint.parseFromString = function (strData) { - var data = strData.substring(strData.indexOf('(') + 1, strData.lastIndexOf(')')); - var values = data.split(' '); - - return new $data.GeographyPoint(parseFloat(values[0]), parseFloat(values[1])); -}; -$data.GeographyPoint.validMembers = ['coordinates']; -$data.GeographyBase.registerType('Point', $data.GeographyPoint); -Object.defineProperty($data.GeographyPoint.prototype, 'longitude', { get: function () { return this.coordinates[0]; }, set: function (v) { this.coordinates[0] = v; } }); -Object.defineProperty($data.GeographyPoint.prototype, 'latitude', { get: function () { return this.coordinates[1]; }, set: function (v) { this.coordinates[1] = v; } }); -$data.Container.registerType(['$data.GeographyPoint', 'GeographyPoint', '$data.Geography', 'Geography', 'geography', 'geo'], $data.GeographyPoint); -$data.Geography = $data.GeographyPoint; - -/* $data.GeographyLineString */ -$data.GeographyLineString = function GeographyLineString(data) { - if (Array.isArray(data)) { - $data.GeographyBase.call(this, { coordinates: data }); - } else { - $data.GeographyBase.call(this, data); - } -}; -$data.GeographyLineString.validateGeoJSON = function (geoData) { - var isValid = geoData && - Array.isArray(geoData.coordinates); - - for (var i = 0; isValid && i < geoData.coordinates.length; i++) { - var point = geoData.coordinates[i]; - isValid = isValid && - Array.isArray(point) && - point.length == 2 && - typeof point[0] === 'number' && - typeof point[1] === 'number'; - } - - return isValid; -}; -$data.GeographyLineString.validMembers = ['coordinates']; -$data.GeographyBase.registerType('LineString', $data.GeographyLineString); -$data.Container.registerType(['$data.GeographyLineString', 'GeographyLineString'], $data.GeographyLineString); - -/* $data.GeographyPolygon */ -$data.GeographyPolygon = function GeographyPolygon(data) { - if (typeof data === 'object' && (('topLeft' in data && 'bottomRight' in data) || ('topRight' in data && 'bottomLeft' in data))) { - var tl, tr, bl, br; - - if ('topLeft' in data && 'bottomRight' in data) { - tl = data.topLeft instanceof $data.GeographyPoint ? data.topLeft : new $data.GeographyPoint(data.topLeft); - br = data.bottomRight instanceof $data.GeographyPoint ? data.bottomRight : new $data.GeographyPoint(data.bottomRight); - tr = new $data.GeographyPoint([br.coordinates[0], tl.coordinates[1]]); - bl = new $data.GeographyPoint([tl.coordinates[0], br.coordinates[1]]); - } else { - tr = data.topRight instanceof $data.GeographyPoint ? data.topRight : new $data.GeographyPoint(data.topRight); - bl = data.bottomLeft instanceof $data.GeographyPoint ? data.bottomLeft : new $data.GeographyPoint(data.bottomLeft); - tl = new $data.GeographyPoint([bl.coordinates[0], tr.coordinates[1]]); - br = new $data.GeographyPoint([tr.coordinates[0], bl.coordinates[1]]); - } - - var coordinates = []; - coordinates.push([].concat(tl.coordinates)); - coordinates.push([].concat(tr.coordinates)); - coordinates.push([].concat(br.coordinates)); - coordinates.push([].concat(bl.coordinates)); - coordinates.push([].concat(tl.coordinates)); - - $data.GeographyBase.call(this, { coordinates: [coordinates] }); - - }else if (Array.isArray(data)) { - $data.GeographyBase.call(this, { coordinates: data }); - } else { - $data.GeographyBase.call(this, data); - } -}; -$data.GeographyPolygon.validateGeoJSON = function (geoData) { - var isValid = geoData && - Array.isArray(geoData.coordinates); - - for (var i = 0; isValid && i < geoData.coordinates.length; i++) { - var polygon = geoData.coordinates[i]; - var isValid = isValid && Array.isArray(polygon); - - for (var j = 0; isValid && j < polygon.length; j++) { - var point = polygon[j]; - - isValid = isValid && - Array.isArray(point) && - point.length == 2 && - typeof point[0] === 'number' && - typeof point[1] === 'number'; - } - } - - return isValid; -}; -$data.GeographyPolygon.parseFromString = function (strData) { - var data = strData.substring(strData.indexOf('(') + 1, strData.lastIndexOf(')')); - var rings = data.substring(data.indexOf('(') + 1, data.lastIndexOf(')')).split('),('); - - var data = []; - for (var i = 0; i < rings.length; i++) { - var polyPoints = []; - var pairs = rings[i].split(','); - for (var j = 0; j < pairs.length; j++) { - var values = pairs[j].split(' '); - - polyPoints.push([parseFloat(values[0]), parseFloat(values[1])]); - } - data.push(polyPoints); - } - - return new $data.GeographyPolygon(data); -}; -$data.GeographyPolygon.validMembers = ['coordinates']; -$data.GeographyBase.registerType('Polygon', $data.GeographyPolygon); -$data.Container.registerType(['$data.GeographyPolygon', 'GeographyPolygon'], $data.GeographyPolygon); - -/* $data.GeographyMultiPoint */ -$data.GeographyMultiPoint = function GeographyMultiPoint(data) { - if (Array.isArray(data)) { - $data.GeographyBase.call(this, { coordinates: data }); - } else { - $data.GeographyBase.call(this, data); - } -}; -$data.GeographyMultiPoint.validateGeoJSON = function (geoData) { - var isValid = geoData && - Array.isArray(geoData.coordinates); - - for (var i = 0; isValid && i < geoData.coordinates.length; i++) { - var point = geoData.coordinates[i]; - isValid = isValid && - Array.isArray(point) && - point.length == 2 && - typeof point[0] === 'number' && - typeof point[1] === 'number'; - } - - return isValid; -}; -$data.GeographyMultiPoint.validMembers = ['coordinates']; -$data.GeographyBase.registerType('MultiPoint', $data.GeographyMultiPoint); -$data.Container.registerType(['$data.GeographyMultiPoint', 'GeographyMultiPoint'], $data.GeographyMultiPoint); - -/* $data.GeographyMultiLineString */ -$data.GeographyMultiLineString = function GeographyMultiLineString(data) { - if (Array.isArray(data)) { - $data.GeographyBase.call(this, { coordinates: data }); - } else { - $data.GeographyBase.call(this, data); - } -}; -$data.GeographyMultiLineString.validateGeoJSON = function (geoData) { - var isValid = geoData && - Array.isArray(geoData.coordinates); - - for (var i = 0; isValid && i < geoData.coordinates.length; i++) { - var polygon = geoData.coordinates[i]; - var isValid = isValid && Array.isArray(polygon); - - for (var j = 0; isValid && j < polygon.length; j++) { - var point = polygon[j]; - - isValid = isValid && - Array.isArray(point) && - point.length == 2 && - typeof point[0] === 'number' && - typeof point[1] === 'number'; - } - } - - return isValid; -}; -$data.GeographyMultiLineString.validMembers = ['coordinates']; -$data.GeographyBase.registerType('MultiLineString', $data.GeographyMultiLineString); -$data.Container.registerType(['$data.GeographyMultiLineString', 'GeographyMultiLineString'], $data.GeographyMultiLineString); - -/* $data.GeographyMultiPolygon */ -$data.GeographyMultiPolygon = function GeographyMultiPolygon(data) { - if (Array.isArray(data)) { - $data.GeographyBase.call(this, { coordinates: data }); - } else { - $data.GeographyBase.call(this, data); - } -}; -$data.GeographyMultiPolygon.validateGeoJSON = function (geoData) { - var isValid = geoData && - Array.isArray(geoData.coordinates); - - for (var k = 0; isValid && k < geoData.coordinates.length; k++) { - var polygons = geoData.coordinates[k]; - var isValid = isValid && Array.isArray(polygons); - - for (var i = 0; isValid && i < polygons.length; i++) { - var polygon = polygons[i]; - var isValid = isValid && Array.isArray(polygon); - - for (var j = 0; isValid && j < polygon.length; j++) { - var point = polygon[j]; - - isValid = isValid && - Array.isArray(point) && - point.length == 2 && - typeof point[0] === 'number' && - typeof point[1] === 'number'; - } - } - } - - return isValid; -}; -$data.GeographyMultiPolygon.validMembers = ['coordinates']; -$data.GeographyBase.registerType('MultiPolygon', $data.GeographyMultiPolygon); -$data.Container.registerType(['$data.GeographyMultiPolygon', 'GeographyMultiPolygon'], $data.GeographyMultiPolygon); - -/* $data.GeographyCollection */ -$data.GeographyCollection = function GeographyCollection(data) { - if (Array.isArray(data)) { - $data.GeographyBase.call(this, { geometries: data }); - } else { - $data.GeographyBase.call(this, data); - } -}; -$data.GeographyCollection.validateGeoJSON = function (geoData) { - var isValid = geoData && - Array.isArray(geoData.geometries); - - for (var i = 0; isValid && i < geoData.geometries.length; i++) { - var geometry = geoData.geometries[i]; - try { - isValid = isValid && $data.GeographyBase.validateGeoJSON(geometry); - } catch (e) { - isValid = false; - } - } - - return isValid; -}; -$data.GeographyCollection.validMembers = ['geometries']; -$data.GeographyBase.registerType('GeometryCollection', $data.GeographyCollection); -$data.Container.registerType(['$data.GeographyCollection', 'GeographyCollection'], $data.GeographyCollection); - - -/* converters */ -$data.Container.registerConverter($data.GeographyPoint, $data.Object, function (value) { - return value ? new $data.GeographyPoint(value) : value; -}); -$data.Container.registerConverter($data.GeographyLineString, $data.Object, function (value) { - return value ? new $data.GeographyLineString(value) : value; -}); -$data.Container.registerConverter($data.GeographyPolygon, $data.Object, function (value) { - return value ? new $data.GeographyPolygon(value) : value; -}); -$data.Container.registerConverter($data.GeographyMultiPoint, $data.Object, function (value) { - return value ? new $data.GeographyMultiPoint(value) : value; -}); -$data.Container.registerConverter($data.GeographyMultiLineString, $data.Object, function (value) { - return value ? new $data.GeographyMultiLineString(value) : value; -}); -$data.Container.registerConverter($data.GeographyMultiPolygon, $data.Object, function (value) { - return value ? new $data.GeographyMultiPolygon(value) : value; -}); -$data.Container.registerConverter($data.GeographyCollection, $data.Object, function (value) { - return value ? new $data.GeographyCollection(value) : value; -}); -/* $data.Geometry */ -$data.GeometryBase = function GeometryBase() { - $data.Geospatial.apply(this, arguments); - - this.crs = $data.GeometryBase.defaultCrs; - $data.GeometryBase.validateGeoJSON(this); -}; - -$data.GeometryBase.defaultCrs = { - properties: { - name: 'EPSG:0' - }, - type: 'name' -}; - -$data.GeometryBase.parseFromString = function (strData) { - var lparenIdx = strData.indexOf('('); - if (lparenIdx >= 0) { - var name = strData.substring(0, lparenIdx).toLowerCase(); - var type = $data.GeometryBase.registered[name]; - - if (type && type.parseFromString && type != $data.GeometryBase) { - return type.parseFromString(strData); - } else { - Guard.raise(new Exception('parseFromString', 'Not Implemented', strData)); - } - } -}; -$data.GeometryBase.stringifyToUrl = function (geoData) { - if (geoData instanceof $data.GeometryBase && geoData.constructor && geoData.constructor.stringifyToUrl) { - return geoData.constructor.stringifyToUrl(geoData); - } else if (geoData instanceof $data.GeometryBase && geoData.constructor && Array.isArray(geoData.constructor.validMembers) && geoData.constructor.validMembers[0] === 'coordinates') { - var data = "geometry'" + geoData.type.toUpperCase() + '('; - function buildArray(d, context) { - if (Array.isArray(d[0])) { - - for (var i = 0; i < d.length; i++) { - if (i > 0) data += ','; - if (Array.isArray(d[i][0])) - data += '('; - - buildArray(d[i]); - - if (Array.isArray(d[i][0])) - data += ')'; - } - - } else { - data += d.join(' '); - } - } - buildArray(geoData.coordinates, data); - - data += ")'"; - return data; - } else { - Guard.raise(new Exception('stringifyToUrl on instance type', 'Not Implemented', geoData)); - } -}; -$data.GeometryBase.registerType = function (name, type, base) { - $data.SimpleBase.registerType(name, type, base || $data.GeometryBase); - - $data.GeometryBase.registered = $data.GeometryBase.registered || {}; - $data.GeometryBase.registered[name.toLowerCase()] = type; -}; -$data.GeometryBase.validateGeoJSON = function (geoData) { - var type = geoData.type; - if (type) { - var geoType = $data.GeometryBase.registered[type.toLowerCase()]; - if (typeof geoType.validateGeoJSON === 'function') { - var isValid = geoType.validateGeoJSON(geoData); - if (isValid) { - return isValid; - } else { - Guard.raise(new Exception("Invalid '" + type + "' format!", 'Format Exception', geoData)); - } - } - } - console.log('GeoJSON validation missing', geoData); - return; -}; -$data.SimpleBase.registerType('GeometryBase', $data.GeometryBase, $data.Geospatial); -$data.Container.registerType(['$data.GeometryBase'], $data.GeometryBase); - -/* $data.GeometryPoint */ -$data.GeometryPoint = function GeometryPoint(x, y) { - var param = x; - if (param && typeof param === 'object' && Array.isArray(param)) { - $data.GeometryBase.call(this, { coordinates: param }); - } else if (param && typeof param === 'object' && ('x' in param || 'y' in param)) { - $data.GeometryBase.call(this, { coordinates: [param.x, param.y] }); - } else if (param && typeof param === 'object') { - $data.GeometryBase.call(this, param); - } else { - $data.GeometryBase.call(this, { coordinates: [x || 0, y || 0] }); - } -}; -$data.GeometryPoint.validateGeoJSON = function (geoData) { - return geoData && - Array.isArray(geoData.coordinates) && - geoData.coordinates.length == 2 && - typeof geoData.coordinates[0] === 'number' && - typeof geoData.coordinates[1] === 'number'; -}; -$data.GeometryPoint.parseFromString = function (strData) { - var data = strData.substring(strData.indexOf('(') + 1, strData.lastIndexOf(')')); - var values = data.split(' '); - - return new $data.GeometryPoint(parseFloat(values[0]), parseFloat(values[1])); -}; -$data.GeometryPoint.validMembers = ['coordinates']; -$data.GeometryBase.registerType('Point', $data.GeometryPoint); -Object.defineProperty($data.GeometryPoint.prototype, 'x', { get: function () { return this.coordinates[0]; }, set: function (v) { this.coordinates[0] = v; } }); -Object.defineProperty($data.GeometryPoint.prototype, 'y', { get: function () { return this.coordinates[1]; }, set: function (v) { this.coordinates[1] = v; } }); -$data.Container.registerType(['$data.GeometryPoint', 'GeometryPoint'], $data.GeometryPoint); - -/* $data.GeometryLineString */ -$data.GeometryLineString = function GeometryLineString(data) { - if (Array.isArray(data)) { - $data.GeometryBase.call(this, { coordinates: data }); - } else { - $data.GeometryBase.call(this, data); - } -}; -$data.GeometryLineString.validateGeoJSON = function (geoData) { - var isValid = geoData && - Array.isArray(geoData.coordinates); - - for (var i = 0; isValid && i < geoData.coordinates.length; i++) { - var point = geoData.coordinates[i]; - isValid = isValid && - Array.isArray(point) && - point.length == 2 && - typeof point[0] === 'number' && - typeof point[1] === 'number'; - } - - return isValid; -}; -$data.GeometryLineString.validMembers = ['coordinates']; -$data.GeometryBase.registerType('LineString', $data.GeometryLineString); -$data.Container.registerType(['$data.GeometryLineString', 'GeometryLineString'], $data.GeometryLineString); - -/* $data.GeometryPolygon */ -$data.GeometryPolygon = function GeometryPolygon(data) { - if (typeof data === 'object' && (('topLeft' in data && 'bottomRight' in data) || ('topRight' in data && 'bottomLeft' in data))) { - var tl, tr, bl, br; - - if ('topLeft' in data && 'bottomRight' in data) { - tl = data.topLeft instanceof $data.GeometryPoint ? data.topLeft : new $data.GeometryPoint(data.topLeft); - br = data.bottomRight instanceof $data.GeometryPoint ? data.bottomRight : new $data.GeometryPoint(data.bottomRight); - tr = new $data.GeometryPoint([br.coordinates[0], tl.coordinates[1]]); - bl = new $data.GeometryPoint([tl.coordinates[0], br.coordinates[1]]); - } else { - tr = data.topRight instanceof $data.GeometryPoint ? data.topRight : new $data.GeometryPoint(data.topRight); - bl = data.bottomLeft instanceof $data.GeometryPoint ? data.bottomLeft : new $data.GeometryPoint(data.bottomLeft); - tl = new $data.GeometryPoint([bl.coordinates[0], tr.coordinates[1]]); - br = new $data.GeometryPoint([tr.coordinates[0], bl.coordinates[1]]); - } - - var coordinates = []; - coordinates.push([].concat(tl.coordinates)); - coordinates.push([].concat(tr.coordinates)); - coordinates.push([].concat(br.coordinates)); - coordinates.push([].concat(bl.coordinates)); - coordinates.push([].concat(tl.coordinates)); - - $data.GeometryBase.call(this, { coordinates: [coordinates] }); - - }else if (Array.isArray(data)) { - $data.GeometryBase.call(this, { coordinates: data }); - } else { - $data.GeometryBase.call(this, data); - } -}; -$data.GeometryPolygon.validateGeoJSON = function (geoData) { - var isValid = geoData && - Array.isArray(geoData.coordinates); - - for (var i = 0; isValid && i < geoData.coordinates.length; i++) { - var polygon = geoData.coordinates[i]; - var isValid = isValid && Array.isArray(polygon); - - for (var j = 0; isValid && j < polygon.length; j++) { - var point = polygon[j]; - - isValid = isValid && - Array.isArray(point) && - point.length == 2 && - typeof point[0] === 'number' && - typeof point[1] === 'number'; - } - } - - return isValid; -}; -$data.GeometryPolygon.parseFromString = function (strData) { - var data = strData.substring(strData.indexOf('(') + 1, strData.lastIndexOf(')')); - var rings = data.substring(data.indexOf('(') + 1, data.lastIndexOf(')')).split('),('); - - var data = []; - for (var i = 0; i < rings.length; i++) { - var polyPoints = []; - var pairs = rings[i].split(','); - for (var j = 0; j < pairs.length; j++) { - var values = pairs[j].split(' '); - - polyPoints.push([parseFloat(values[0]), parseFloat(values[1])]); - } - data.push(polyPoints); - } - - return new $data.GeometryPolygon(data); -}; -$data.GeometryPolygon.validMembers = ['coordinates']; -$data.GeometryBase.registerType('Polygon', $data.GeometryPolygon); -$data.Container.registerType(['$data.GeometryPolygon', 'GeometryPolygon'], $data.GeometryPolygon); - -/* $data.GeometryMultiPoint */ -$data.GeometryMultiPoint = function GeometryMultiPoint(data) { - if (Array.isArray(data)) { - $data.GeometryBase.call(this, { coordinates: data }); - } else { - $data.GeometryBase.call(this, data); - } -}; -$data.GeometryMultiPoint.validateGeoJSON = function (geoData) { - var isValid = geoData && - Array.isArray(geoData.coordinates); - - for (var i = 0; isValid && i < geoData.coordinates.length; i++) { - var point = geoData.coordinates[i]; - isValid = isValid && - Array.isArray(point) && - point.length == 2 && - typeof point[0] === 'number' && - typeof point[1] === 'number'; - } - - return isValid; -}; -$data.GeometryMultiPoint.validMembers = ['coordinates']; -$data.GeometryBase.registerType('MultiPoint', $data.GeometryMultiPoint); -$data.Container.registerType(['$data.GeometryMultiPoint', 'GeometryMultiPoint'], $data.GeometryMultiPoint); - -/* $data.GeometryMultiLineString */ -$data.GeometryMultiLineString = function GeometryMultiLineString(data) { - if (Array.isArray(data)) { - $data.GeometryBase.call(this, { coordinates: data }); - } else { - $data.GeometryBase.call(this, data); - } -}; -$data.GeometryMultiLineString.validateGeoJSON = function (geoData) { - var isValid = geoData && - Array.isArray(geoData.coordinates); - - for (var i = 0; isValid && i < geoData.coordinates.length; i++) { - var polygon = geoData.coordinates[i]; - var isValid = isValid && Array.isArray(polygon); - - for (var j = 0; isValid && j < polygon.length; j++) { - var point = polygon[j]; - - isValid = isValid && - Array.isArray(point) && - point.length == 2 && - typeof point[0] === 'number' && - typeof point[1] === 'number'; - } - } - - return isValid; -}; -$data.GeometryMultiLineString.validMembers = ['coordinates']; -$data.GeometryBase.registerType('MultiLineString', $data.GeometryMultiLineString); -$data.Container.registerType(['$data.GeometryMultiLineString', 'GeometryMultiLineString'], $data.GeometryMultiLineString); - -/* $data.GeometryMultiPolygon */ -$data.GeometryMultiPolygon = function GeometryMultiPolygon(data) { - if (Array.isArray(data)) { - $data.GeometryBase.call(this, { coordinates: data }); - } else { - $data.GeometryBase.call(this, data); - } -}; -$data.GeometryMultiPolygon.validateGeoJSON = function (geoData) { - var isValid = geoData && - Array.isArray(geoData.coordinates); - - for (var k = 0; isValid && k < geoData.coordinates.length; k++) { - var polygons = geoData.coordinates[k]; - var isValid = isValid && Array.isArray(polygons); - - for (var i = 0; isValid && i < polygons.length; i++) { - var polygon = polygons[i]; - var isValid = isValid && Array.isArray(polygon); - - for (var j = 0; isValid && j < polygon.length; j++) { - var point = polygon[j]; - - isValid = isValid && - Array.isArray(point) && - point.length == 2 && - typeof point[0] === 'number' && - typeof point[1] === 'number'; - } - } - } - - return isValid; -}; -$data.GeometryMultiPolygon.validMembers = ['coordinates']; -$data.GeometryBase.registerType('MultiPolygon', $data.GeometryMultiPolygon); -$data.Container.registerType(['$data.GeometryMultiPolygon', 'GeometryMultiPolygon'], $data.GeometryMultiPolygon); - -/* $data.GeometryCollection */ -$data.GeometryCollection = function GeometryCollection(data) { - if (Array.isArray(data)) { - $data.GeometryBase.call(this, { geometries: data }); - } else { - $data.GeometryBase.call(this, data); - } -}; -$data.GeometryCollection.validateGeoJSON = function (geoData) { - var isValid = geoData && - Array.isArray(geoData.geometries); - - for (var i = 0; isValid && i < geoData.geometries.length; i++) { - var geometry = geoData.geometries[i]; - try { - isValid = isValid && $data.GeometryBase.validateGeoJSON(geometry); - } catch (e) { - isValid = false; - } - } - - return isValid; -}; -$data.GeometryCollection.validMembers = ['geometries']; -$data.GeometryBase.registerType('GeometryCollection', $data.GeometryCollection); -$data.Container.registerType(['$data.GeometryCollection', 'GeometryCollection'], $data.GeometryCollection); - -/* converters */ -$data.Container.registerConverter($data.GeometryPoint, $data.Object, function (value) { - return value ? new $data.GeometryPoint(value) : value; -}); -$data.Container.registerConverter($data.GeometryLineString, $data.Object, function (value) { - return value ? new $data.GeometryLineString(value) : value; -}); -$data.Container.registerConverter($data.GeometryPolygon, $data.Object, function (value) { - return value ? new $data.GeometryPolygon(value) : value; -}); -$data.Container.registerConverter($data.GeometryMultiPoint, $data.Object, function (value) { - return value ? new $data.GeometryMultiPoint(value) : value; -}); -$data.Container.registerConverter($data.GeometryMultiLineString, $data.Object, function (value) { - return value ? new $data.GeometryMultiLineString(value) : value; -}); -$data.Container.registerConverter($data.GeometryMultiPolygon, $data.Object, function (value) { - return value ? new $data.GeometryMultiPolygon(value) : value; -}); -$data.Container.registerConverter($data.GeometryCollection, $data.Object, function (value) { - return value ? new $data.GeometryCollection(value) : value; -});$data.Guid = function Guid(value) { - /// - - if (value === undefined || (typeof value === 'string' && /^[a-zA-z0-9]{8}-[a-zA-z0-9]{4}-[a-zA-z0-9]{4}-[a-zA-z0-9]{4}-[a-zA-z0-9]{12}$/.test(value))) { - this.value = value || '00000000-0000-0000-0000-000000000000'; - } else { - throw Guard.raise(new Exception('TypeError: ', 'value not convertable to $data.Guid', value)); - } -}; -$data.Container.registerType(['$data.Guid', 'Guid', 'guid'], $data.Guid); -$data.Container.registerConverter('$data.Guid', { - '$data.String': function (value) { - return value ? $data.parseGuid(value).toString() : value; - }, - '$data.Guid': function (value) { - return value ? value.toString() : value; - } -}, { - '$data.String': function (value) { - return value ? value.toString() : value; - } -}); - - -$data.Guid.prototype.toJSON = function () { - return this.value; -}; - -$data.Guid.prototype.valueOf = function () { - return this.value; -}; - -$data.Guid.prototype.toString = function () { - return this.value; -}; - -$data.Guid.NewGuid = function () { - return $data.createGuid(); -}; - -$data.parseGuid = function (guid) { - return new $data.Guid(guid); -}; - -(function () { - /*! - Math.uuid.js (v1.4) - http://www.broofa.com - mailto:robert@broofa.com - - Copyright (c) 2010 Robert Kieffer - Dual licensed under the MIT and GPL licenses. - */ - - var CHARS = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split(''); - - $data.createGuid = function (guidString) { - if (guidString) { - return new $data.Guid(guidString); - }; - - var len; - var chars = CHARS, uuid = [], i; - var radix = chars.length; - - if (len) { - // Compact form - for (i = 0; i < len; i++) uuid[i] = chars[0 | Math.random() * radix]; - } else { - // rfc4122, version 4 form - var r; - - // rfc4122 requires these characters - uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-'; - uuid[14] = '4'; - - // Fill in random data. At i==19 set the high bits of clock sequence as - // per rfc4122, sec. 4.1.5 - for (i = 0; i < 36; i++) { - if (!uuid[i]) { - r = 0 | Math.random() * 16; - uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r]; - } - } - } - - return $data.parseGuid(uuid.join('')); - }; -})();$data.Blob = function Blob(){}; - -$data.Blob.createFromHexString = function(value){ - if (value != value.match(new RegExp('[0-9a-fA-F]+'))[0]){ - Guard.raise(new Exception('TypeError: ', 'value not convertable to $data.Blob', value)); - }else{ - //if (value.length & 1) value = '0' + value; - var arr = new (typeof Buffer != 'undefined' ? Buffer : Uint8Array)(value.length >> 1); - for (var i = 0, j = 1, k = 0; i < value.length; i += 2, j += 2, k++) { - arr[k] = parseInt('0x' + value[i] + value[j], 16); - } - - return arr; - } -}; - -$data.Blob.toString = function(value){ - if (!value || !value.length) return null; - var s = ''; - for (var i = 0; i < value.length; i++){ - s += String.fromCharCode(value[i]); - } - - return s; -}; - -$data.Blob.toBase64 = function(value){ - if (!value || !value.length) return null; - return btoa($data.Blob.toString(value)); -}; - -$data.Blob.toArray = function(src){ - if (!src || !src.length) return null; - var arr = new Array(src.length); - for (var i = 0; i < src.length; i++){ - arr[i] = src[i]; - } - - return arr; -}; - -/*$data.Blob.toJSON = function(value){ - return JSON.stringify($data.Blob.toArray(value)); -};*/ - -$data.Blob.toHexString = function(value){ - if (!value || !value.length) return null; - var s = ''; - for (var i = 0; i < value.length; i++){ - s += ('00' + value[i].toString(16)).slice(-2); - } - - return s.toUpperCase(); -}; - -$data.Blob.toDataURL = function(value){ - if (!value || !value.length) return null; - return 'data:application/octet-stream;base64,' + btoa($data.Blob.toString(value)); -}; - -$data.Container.registerType(["$data.Blob", "blob", "JayBlob"], $data.Blob); -$data.Container.registerConverter('$data.Blob',{ - '$data.String': function (value){ - if (value && value.length){ - var blob = new (typeof Buffer !== 'undefined' ? Buffer : Uint8Array)(value.length); - for (var i = 0; i < value.length; i++){ - blob[i] = value.charCodeAt(i); - } - - return blob; - }else return null; - }, - '$data.Array': function(value){ - return new (typeof Buffer !== 'undefined' ? Buffer : Uint8Array)(value); - }, - '$data.Number': function(value){ - return new (typeof Buffer !== 'undefined' ? Buffer : Uint8Array)($data.packIEEE754(value, 11, 52).reverse()); - }, - '$data.Boolean': function(value){ - return new (typeof Buffer !== 'undefined' ? Buffer : Uint8Array)([value | 0]); - }, - 'default': function(value){ - if (typeof Blob !== 'undefined' && value instanceof Blob){ - var req = new XMLHttpRequest(); - req.open('GET', URL.createObjectURL(value), false); - req.responseType = 'arraybuffer'; - req.send(null); - return $data.Container.convertTo(req.response, $data.Blob); - } else if (typeof ArrayBuffer !== 'undefined' && value instanceof ArrayBuffer) { - return new (typeof Buffer !== 'undefined' ? Buffer : Uint8Array)(new Uint8Array(value)); - }else if (value instanceof Uint8Array){ - if (typeof Buffer !== 'undefined') return new Buffer(value); - else return value; - }else if (typeof Buffer !== 'undefined' ? value instanceof Buffer : false){ - return value; - }else if (value.buffer){ - return new (typeof Buffer !== 'undefined' ? Buffer : Uint8Array)(value); - }else if (typeof value == 'object' && value instanceof Object){ - var arr = []; - for (var i in value){ - arr[i] = value[i]; - } - if (!arr.length) throw 0; - return new (typeof Buffer !== 'undefined' ? Buffer : Uint8Array)(arr); - } - throw 0; - } -}, { - '$data.String': function(value){ - return $data.Blob.toString(value); - }, - '$data.Array': function(value){ - return $data.Blob.toArray(value); - } -}); -(function ($data) { - - function Edm_Boolean() { }; - $data.Container.registerType('Edm.Boolean', Edm_Boolean); - $data.Container.mapType(Edm_Boolean, $data.Boolean); - - function Edm_Binary() { }; - $data.Container.registerType('Edm.Binary', Edm_Binary); - $data.Container.mapType(Edm_Binary, $data.Blob); - - function Edm_DateTime() { }; - $data.Container.registerType('Edm.DateTime', Edm_DateTime); - $data.Container.mapType(Edm_DateTime, $data.Date); - - function Edm_DateTimeOffset() { }; - $data.Container.registerType('Edm.DateTimeOffset', Edm_DateTimeOffset); - $data.Container.mapType(Edm_DateTimeOffset, $data.DateTimeOffset); - - function Edm_Time() { }; - $data.Container.registerType('Edm.Time', Edm_Time); - $data.Container.mapType(Edm_Time, $data.Time); - - function Edm_Decimal() { }; - $data.Container.registerType('Edm.Decimal', Edm_Decimal); - $data.Container.mapType(Edm_Decimal, $data.Decimal); - - function Edm_Float() { }; - $data.Container.registerType('Edm.Float', Edm_Float); - $data.Container.mapType(Edm_Float, $data.Float); - - function Edm_Single() { }; - $data.Container.registerType('Edm.Single', Edm_Single); - $data.Container.mapType(Edm_Single, $data.Float); - - function Edm_Double() { }; - $data.Container.registerType('Edm.Double', Edm_Double); - $data.Container.mapType(Edm_Double, $data.Number); - - function Edm_Guid() { }; - $data.Container.registerType('Edm.Guid', Edm_Guid); - $data.Container.mapType(Edm_Guid, $data.Guid); - - function Edm_Int16() { }; - $data.Container.registerType('Edm.Int16', Edm_Int16); - $data.Container.mapType(Edm_Int16, $data.Int16); - - function Edm_Int32() { }; - $data.Container.registerType('Edm.Int32', Edm_Int32); - $data.Container.mapType(Edm_Int32, $data.Integer); - - function Edm_Int64() { }; - $data.Container.registerType('Edm.Int64', Edm_Int64); - $data.Container.mapType(Edm_Int64, $data.Int64); - - function Edm_Byte() { }; - $data.Container.registerType('Edm.Byte', Edm_Byte); - $data.Container.mapType(Edm_Byte, $data.Byte); - - function Edm_SByte() { }; - $data.Container.registerType('Edm.SByte', Edm_SByte); - $data.Container.mapType(Edm_SByte, $data.SByte); - - function Edm_String() { }; - $data.Container.registerType('Edm.String', Edm_String); - $data.Container.mapType(Edm_String, $data.String); - - function Edm_GeographyPoint() { }; - $data.Container.registerType('Edm.GeographyPoint', Edm_GeographyPoint); - $data.Container.mapType(Edm_GeographyPoint, $data.GeographyPoint); - - function Edm_GeographyLineString() { }; - $data.Container.registerType('Edm.GeographyLineString', Edm_GeographyLineString); - $data.Container.mapType(Edm_GeographyLineString, $data.GeographyLineString); - - function Edm_GeographyPolygon() { }; - $data.Container.registerType('Edm.GeographyPolygon', Edm_GeographyPolygon); - $data.Container.mapType(Edm_GeographyPolygon, $data.GeographyPolygon); - - function Edm_GeographyMultiPoint() { }; - $data.Container.registerType('Edm.GeographyMultiPoint', Edm_GeographyMultiPoint); - $data.Container.mapType(Edm_GeographyMultiPoint, $data.GeographyMultiPoint); - - function Edm_GeographyMultiLineString() { }; - $data.Container.registerType('Edm.GeographyMultiLineString', Edm_GeographyMultiLineString); - $data.Container.mapType(Edm_GeographyMultiLineString, $data.GeographyMultiLineString); - - function Edm_GeographyMultiPolygon() { }; - $data.Container.registerType('Edm.GeographyMultiPolygon', Edm_GeographyMultiPolygon); - $data.Container.mapType(Edm_GeographyMultiPolygon, $data.GeographyMultiPolygon); - - function Edm_GeographyCollection() { }; - $data.Container.registerType('Edm.GeographyCollection', Edm_GeographyCollection); - $data.Container.mapType(Edm_GeographyCollection, $data.GeographyCollection); - - function Edm_GeometryPoint() { }; - $data.Container.registerType('Edm.GeometryPoint', Edm_GeometryPoint); - $data.Container.mapType(Edm_GeometryPoint, $data.GeometryPoint); - - function Edm_GeometryLineString() { }; - $data.Container.registerType('Edm.GeometryLineString', Edm_GeometryLineString); - $data.Container.mapType(Edm_GeometryLineString, $data.GeometryLineString); - - function Edm_GeometryPolygon() { }; - $data.Container.registerType('Edm.GeometryPolygon', Edm_GeometryPolygon); - $data.Container.mapType(Edm_GeometryPolygon, $data.GeometryPolygon); - - function Edm_GeometryMultiPoint() { }; - $data.Container.registerType('Edm.GeometryMultiPoint', Edm_GeometryMultiPoint); - $data.Container.mapType(Edm_GeometryMultiPoint, $data.GeometryMultiPoint); - - function Edm_GeometryMultiLineString() { }; - $data.Container.registerType('Edm.GeometryMultiLineString', Edm_GeometryMultiLineString); - $data.Container.mapType(Edm_GeometryMultiLineString, $data.GeometryMultiLineString); - - function Edm_GeometryMultiPolygon() { }; - $data.Container.registerType('Edm.GeometryMultiPolygon', Edm_GeometryMultiPolygon); - $data.Container.mapType(Edm_GeometryMultiPolygon, $data.GeometryMultiPolygon); - - function Edm_GeometryCollection() { }; - $data.Container.registerType('Edm.GeometryCollection', Edm_GeometryCollection); - $data.Container.mapType(Edm_GeometryCollection, $data.GeometryCollection); - - $data.oDataEdmMapping = { - '$data.Byte': 'Edm.Byte', - '$data.SByte': 'Edm.SByte', - '$data.Decimal': 'Edm.Decimal', - '$data.Float': 'Edm.Float', - '$data.Int16': 'Edm.Int16', - '$data.Int64': 'Edm.Int64', - '$data.DateTimeOffset': 'Edm.DateTimeOffset', - '$data.Time': 'Edm.Time', - '$data.Boolean': 'Edm.Boolean', - '$data.Blob': 'Edm.Binary', - '$data.Date': 'Edm.DateTime', - '$data.Number': 'Edm.Double', - '$data.Integer': 'Edm.Int32', - '$data.Int32': 'Edm.Int32', - '$data.String': 'Edm.String', - '$data.ObjectID': 'Edm.String', - '$data.GeographyPoint': 'Edm.GeographyPoint', - '$data.GeographyLineString': 'Edm.GeographyLineString', - '$data.GeographyPolygon': 'Edm.GeographyPolygon', - '$data.GeographyMultiPoint': 'Edm.GeographyMultiPoint', - '$data.GeographyMultiLineString': 'Edm.GeographyMultiLineString', - '$data.GeographyMultiPolygon': 'Edm.GeographyMultiPolygon', - '$data.GeographyCollection': 'Edm.GeographyCollection', - '$data.GeometryPoint': 'Edm.GeometryPoint', - '$data.GeometryLineString': 'Edm.GeometryLineString', - '$data.GeometryPolygon': 'Edm.GeometryPolygon', - '$data.GeometryMultiPoint': 'Edm.GeometryMultiPoint', - '$data.GeometryMultiLineString': 'Edm.GeometryMultiLineString', - '$data.GeometryMultiPolygon': 'Edm.GeometryMultiPolygon', - '$data.GeometryCollection': 'Edm.GeometryCollection', - '$data.Guid': 'Edm.Guid' - }; - -})($data); -$data.Container.registerConverter('$data.Boolean', { - '$data.String': function(value){ - if (value.toLowerCase() == 'true') return true; - if (value.toLowerCase() == 'false') return false; - - return !!value; - }, - 'default': function(value){ - return !!value; - } -}); - -$data.Container.registerConverter('$data.Integer', { - 'default': function (value) { - if (value === Number.POSITIVE_INFINITY || - value === Number.NEGATIVE_INFINITY || - value === Number.MAX_VALUE || - value === Number.MIN_VALUE) { - return value; - } - - var r = parseInt(+value, 10); - if (isNaN(r)) throw 0; - return r; - } -}); - -$data.Container.registerConverter('$data.Int32', { - 'default': function (value) { - return value | 0; - } -}); - -$data.Container.registerConverter('$data.Number', { - 'default': function(value){ - var r = +value; - if (isNaN(r)) throw 0; - return r; - } -}); - -$data.Container.registerConverter('$data.Byte', { - 'default': function(value){ - return (value | 0) & 0xff; - } -}); - -$data.Container.registerConverter('$data.Date', { - 'default': function(value){ - var d = new Date(value); - if (isNaN(d)) throw 0; - return d; - } -}); - -$data.Container.registerConverter('$data.DateTimeOffset', { - '$data.Date': function(value){ - return value; - }, - 'default': function(value){ - var d = new Date(value); - if (isNaN(d)) throw 0; - return d; - } -}); -(function () { - function parseFromString(value) { - var regex = /^([0-9]|0[0-9]|1[0-9]|2[0-3]):([0-5][0-9]|[0-9])(:([0-5][0-9]|[0-9])(\.(\d+))?)?$/; - - var matches = regex.exec(value) - if (!matches) throw 0; - var time = ''; - time += ('00' + matches[1]).slice(-2); - time += ':' + ('00' + matches[2]).slice(-2); - if (matches[4]) { - time += ':' + ('00' + matches[4]).slice(-2); - } else { - time += ':00'; - } - if (matches[6]) - time += '.' + (matches[6] + '000').slice(0, 3); - - return time; - } - - $data.Container.registerConverter('$data.Time', { - '$data.String': parseFromString, - '$data.Number': function tt(value) { - var metrics = [1000, 60, 60]; - var result = [0, 0, 0, value | 0]; - - for (var i = 0; i < metrics.length; i++) { - result[metrics.length - (i + 1)] = (result[metrics.length - i] / metrics[i]) | 0; - result[metrics.length - i] -= result[metrics.length - (i + 1)] * metrics[i]; - } - - var time = ''; - for (var i = 0; i < result.length; i++) { - if (i < result.length - 1) { - time += ('00' + result[i]).slice(-2); - if (i < result.length - 2) time += ':'; - } else { - time += '.' + ('000' + result[i]).slice(-3); - } - } - - return parseFromString(time); - }, - '$data.Date': function (value) { - var val = value.getHours() + ':' + value.getMinutes() + ':' + value.getSeconds(); - var ms = value.getMilliseconds() - if (ms) { - val += '.' + ms; - } - - return parseFromString(val); - } - }); -})(); - -$data.Container.registerConverter('$data.Decimal', { - '$data.Boolean': function(value){ - return value ? '1' : '0'; - }, - '$data.Number': function(value){ - return value.toString(); - }, - '$data.String': function(value){ - if (!/^\-?([0-9]+(\.[0-9]+)?|Infinity)$/.test(value)) throw 0; - return value; - }, - '$data.Date': function(value){ - var r = value.valueOf(); - if (isNaN(r)) throw 0; - return r.toString(); - } -}); - -$data.packIEEE754 = function(v, ebits, fbits){ - var bias = (1 << (ebits - 1)) - 1, s, e, f, ln, i, bits, str, bytes; - - // Compute sign, exponent, fraction - if (v !== v){ - // NaN - // http://dev.w3.org/2006/webapi/WebIDL/#es-type-mapping - e = (1 << bias) - 1; f = Math.pow(2, fbits - 1); s = 0; - }else if (v === Infinity || v === -Infinity){ - e = (1 << bias) - 1; f = 0; s = (v < 0) ? 1 : 0; - }else if (v === 0){ - e = 0; f = 0; s = (1 / v === -Infinity) ? 1 : 0; - }else{ - s = v < 0; - v = Math.abs(v); - - if (v >= Math.pow(2, 1 - bias)){ - // Normalized - ln = Math.min(Math.floor(Math.log(v) / Math.LN2), bias); - e = ln + bias; - f = Math.round(v * Math.pow(2, fbits - ln) - Math.pow(2, fbits)); - }else{ - // Denormalized - e = 0; - f = Math.round(v / Math.pow(2, 1 - bias - fbits)); - } - } - - // Pack sign, exponent, fraction - bits = []; - for (i = fbits; i; i -= 1) { bits.push(f % 2 ? 1 : 0); f = Math.floor(f / 2); } - for (i = ebits; i; i -= 1) { bits.push(e % 2 ? 1 : 0); e = Math.floor(e / 2); } - bits.push(s ? 1 : 0); - bits.reverse(); - str = bits.join(''); - - // Bits to bytes - bytes = []; - while (str.length){ - bytes.push(parseInt(str.substring(0, 8), 2)); - str = str.substring(8); - } - - return bytes; -}; - -$data.unpackIEEE754 = function(bytes, ebits, fbits){ - // Bytes to bits - var bits = [], i, j, b, str, bias, s, e, f; - - for (i = bytes.length; i; i -= 1){ - b = bytes[i - 1]; - for (j = 8; j; j -= 1){ - bits.push(b % 2 ? 1 : 0); b = b >> 1; - } - } - bits.reverse(); - str = bits.join(''); - - // Unpack sign, exponent, fraction - bias = (1 << (ebits - 1)) - 1; - s = parseInt(str.substring(0, 1), 2) ? -1 : 1; - e = parseInt(str.substring(1, 1 + ebits), 2); - f = parseInt(str.substring(1 + ebits), 2); - - // Produce number - if (e === (1 << ebits) - 1){ - return f !== 0 ? NaN : s * Infinity; - }else if (e > 0){ - // Normalized - return s * Math.pow(2, e - bias) * (1 + f / Math.pow(2, fbits)); - }else if (f !== 0){ - // Denormalized - return s * Math.pow(2, -(bias - 1)) * (f / Math.pow(2, fbits)); - }else{ - return s < 0 ? -0 : 0; - } -}; - -$data.IEEE754 = function(v, e, f){ - return $data.unpackIEEE754($data.packIEEE754(v, e, f), e, f); -}; - -$data.Container.registerConverter('$data.Float', { - 'default': function(value){ - var r = +value; - if (isNaN(r)) throw 0; - return $data.IEEE754(r, 8, 23); - } -}); - -$data.Container.registerConverter('$data.Int16', { - 'default': function(value){ - var r = (value | 0) & 0xffff; - if (r >= 0x8000) return r - 0x10000; - return r; - } -}); - -$data.Container.registerConverter('$data.Int64', { - '$data.Boolean': function(value){ - return value ? '1' : '0'; - }, - '$data.Number': function(value){ - var r = value.toString(); - if (r.indexOf('.') > 0) return r.split('.')[0]; - if (r.indexOf('.') == 0) throw 0; - return r; - }, - '$data.String': function(value){ - if (!/^\-?([0-9]+(\.[0-9]+)?|Infinity)$/.test(value)) throw 0; - if (value.indexOf('.') > 0) return value.split('.')[0]; - if (value.indexOf('.') == 0) throw 0; - return value; - }, - '$data.Date': function(value){ - var r = value.valueOf(); - if (isNaN(r)) throw 0; - return r.toString(); - } -}); - -$data.Container.registerConverter('$data.SByte', { - 'default': function(value){ - var r = (value | 0) & 0xff; - if (r >= 0x80) return r - 0x100; - return r; - } -}); - -$data.Container.registerConverter('$data.String', { - '$data.Date': function(value){ - return value.toISOString(); - }, - '$data.ObjectID': function(value){ - return btoa(value.toString()); - }, - 'default': function(value){ - if (typeof value === 'object') return JSON.stringify(value); - return value.toString(); - } -}); - -$data.Container.registerConverter('$data.Object', { - '$data.String': function(value){ - return JSON.parse(value); - }, - '$data.Function': function(){ - throw 0; - } -}); - -$data.Container.registerConverter('$data.Array', { - '$data.String': function(value){ - var r = JSON.parse(value); - if (!Array.isArray(r)) throw 0; - return r; - } -}); - -$data.Container.registerConverter('$data.ObjectID', { - '$data.ObjectID': function(value){ - try{ - return btoa(value.toString()); - }catch(e){ - return value; - } - }, - '$data.String': function(id){ - return id; - } -}); - -$data.Container.proxyConverter = function(v){ return v; }; -$data.Container.defaultConverter = function(type){ return function(v){ return $data.Container.convertTo(v, type); }; }; -$data.StringFunctions = { - startsWith: function () { - var self, str; - if (arguments.length == 2) { - self = arguments[0]; - str = arguments[1]; - } else if (arguments.length == 1 && typeof this === 'string') { - self = this; - str = arguments[0]; - } else if (this instanceof String) { - self = this.valueOf(); - str = arguments[0]; - } else - return false; - - if (typeof self !== 'string') return false; - return self.indexOf(str) === 0; - }, - endsWith: function () { - var self, str; - if (arguments.length == 2) { - self = arguments[0]; - str = arguments[1]; - } else if (arguments.length == 1 && typeof this === 'string') { - self = this; - str = arguments[0]; - } else if (this instanceof String) { - self = this.valueOf(); - str = arguments[0]; - } else - return false; - - if (typeof self !== 'string') return false; - return self.slice(-str.length) === str; - }, - contains: function () { - var self, str; - if (arguments.length == 2) { - self = arguments[0]; - str = arguments[1]; - } else if (arguments.length == 1 && typeof this === 'string') { - self = this; - str = arguments[0]; - } else if (this instanceof String) { - self = this.valueOf(); - str = arguments[0]; - } else - return false; - - if (typeof self !== 'string') return false; - return self.indexOf(str) >= 0; - } -}; -//TODO: Finish refactoring ExpressionNode.js - -$data.Class.define("$data.Expressions.ExpressionType", null, null, {}, { - Constant: "constant", // { type:LITERAL, executable:true, valueType:, value: } - Variable: "variable", // { type:VARIABLE, executable:true, name: } - MemberAccess: "memberAccess", // { type:MEMBERACCESS, executable:true, expression:, member: } - Call: "call", - - /* binary operators */ - Equal: "equal", - NotEqual: "notEqual", - EqualTyped: "equalTyped", - NotEqualTyped: "notEqualTyped", - GreaterThen: "greaterThan", - LessThen: "lessThan", - GreaterThenOrEqual: "greaterThanOrEqual", - LessThenOrEqual: "lessThenOrEqual", - Or: "or", - OrBitwise: "orBitwise", - And: "and", - AndBitwise: "andBitwise", - - - In: "in", - - Add: "add", - Divide: "divide", - Multiply: "multiply", - Subtract: "subtract", - Modulo: "modulo", - ArrayIndex: "arrayIndex", - - /* unary operators */ - New: "new", - Positive: "positive", - Negative: "negative", - Increment: "increment", - Decrement: "decrement", - Not: "not", - - - This: "this", - LambdaParameterReference: "lambdaParameterReference", - LambdaParameter: "lambdaParameter", - Parameter: "parameter", - - ArrayLiteral: "arrayLiteral", - ObjectLiteral: "objectLiteral", - ObjectField: "objectField", - Function: "Function", - Unknown: "UNKNOWN", - - EntitySet: "EntitySet", - ServiceOperation: "ServiceOperation", - EntityField: "EntityField", - EntityContext: "EntityContext", - Entity: "Entity", - Filter: "Filter", - First: "First", - Count: "Count", - InlineCount: "InlineCount", - Single: "Single", - Find: "Find", - Some: "Some", - Every: "Every", - ToArray: "ToArray", - BatchDelete: "BatchDelete", - ForEach: "ForEach", - Projection: "Projection", - EntityMember: "EntityMember", - EntityFieldOperation: "EntityFieldOperation", - FrameOperation: "FrameOperation", - EntityFunctionOperation: "EntityFunctionOperation", - ContextFunctionOperation: "ContextFunctionOperation", - EntityBinary: "EntityBinary", - Code: "Code", - ParametricQuery: "ParametricQuery", - MemberInfo: "MemberInfo", - QueryParameter: "QueryParameter", - ComplexEntityField: "ComplexEntityField", - - Take: "Take", - Skip: "Skip", - OrderBy: "OrderBy", - OrderByDescending: "OrderByDescending", - Include: "Include", - - IndexedPhysicalAnd:"IndexedDBPhysicalAndFilterExpression", - IndexedLogicalAnd:"IndexedDBLogicalAndFilterExpression", - IndexedLogicalOr: "IndexedDBLogicalOrFilterExpression", - IndexedLogicalIn: "IndexedDBLogicalInFilterExpression" -}); - -$data.BinaryOperator = function () { - /// - /// - /// -} - -$data.binaryOperators = [ - { operator: "==", expressionType: $data.Expressions.ExpressionType.Equal, type: "boolean", implementation: function (a, b) { return a == b; } }, - { operator: "===", expressionType: $data.Expressions.ExpressionType.EqualTyped, type: "boolean", implementation: function (a, b) { return a === b; } }, - { operator: "!=", expressionType: $data.Expressions.ExpressionType.NotEqual, type: "boolean", implementation: function (a, b) { return a != b; } }, - { operator: "!==", expressionType: $data.Expressions.ExpressionType.NotEqualTyped, type: "boolean", implementation: function (a, b) { return a !== b; } }, - { operator: ">", expressionType: $data.Expressions.ExpressionType.GreaterThen, type: "boolean", implementation: function (a, b) { return a > b; } }, - { operator: ">=", expressionType: $data.Expressions.ExpressionType.GreaterThenOrEqual, type: "boolean", implementation: function (a, b) { return a >= b; } }, - { operator: "<=", expressionType: $data.Expressions.ExpressionType.LessThenOrEqual, type: "boolean", implementation: function (a, b) { return a <= b; } }, - { operator: "<", expressionType: $data.Expressions.ExpressionType.LessThen, type: "boolean", implementation: function (a, b) { return a < b; } }, - { operator: "&&", expressionType: $data.Expressions.ExpressionType.And, type: "boolean", implementation: function (a, b) { return a && b; } }, - { operator: "||", expressionType: $data.Expressions.ExpressionType.Or, type: "boolean", implementation: function (a, b) { return a || b; } }, - { operator: "&", expressionType: $data.Expressions.ExpressionType.AndBitwise, type: "number", implementation: function (a, b) { return a & b; } }, - { operator: "|", expressionType: $data.Expressions.ExpressionType.OrBitwise, type: "number", implementation: function (a, b) { return a | b; } }, - { operator: "+", expressionType: $data.Expressions.ExpressionType.Add, type: "number", implementation: function (a, b) { return a + b; } }, - { operator: "-", expressionType: $data.Expressions.ExpressionType.Subtract, type: "number", implementation: function (a, b) { return a - b; } }, - { operator: "/", expressionType: $data.Expressions.ExpressionType.Divide, type: "number", implementation: function (a, b) { return a / b; } }, - { operator: "%", expressionType: $data.Expressions.ExpressionType.Modulo, type: "number", implementation: function (a, b) { return a % b; } }, - { operator: "*", expressionType: $data.Expressions.ExpressionType.Multiply, type: "number", implementation: function (a, b) { return a * b; } }, - { operator: "[", expressionType: $data.Expressions.ExpressionType.ArrayIndex, type: "number", implementation: function (a, b) { return a[b]; } }, - { operator: "in", expressionType: $data.Expressions.ExpressionType.In, type: 'boolean', implementation: function (a, b) { return a in b; } } -]; - - -$data.binaryOperators.resolve = function (operator) { - var result = $data.binaryOperators.filter(function (item) { return item.operator == operator; }); - if (result.length > 0) - return operator; - //Guard.raise("Unknown operator: " + operator); -}; - -$data.binaryOperators.contains = function (operator) { - return $data.binaryOperators.some(function (item) { return item.operator == operator; }); -}; - -$data.binaryOperators.getOperator = function (operator) { - /// - var result = $data.binaryOperators.filter(function (item) { return item.operator == operator; }); - if (result.length < 1) - Guard.raise("Unknown operator: " + operator); - return result[0]; -}; - - -$data.unaryOperators = [ - { operator: "+", arity: "prefix", expressionType: $data.Expressions.ExpressionType.Positive, type: "number", implementation: function (operand) { return +operand; } }, - { operator: "-", arity: "prefix", expressionType: $data.Expressions.ExpressionType.Negative, type: "number", implementation: function (operand) { return -operand; } }, - { operator: "++", arity: "prefix", expressionType: $data.Expressions.ExpressionType.Increment, type: "number", implementation: function (operand) { return ++operand; } }, - { operator: "--", arity: "prefix", expressionType: $data.Expressions.ExpressionType.Decrement, type: "number", implementation: function (operand) { return --operand; } }, - { operator: "++", arity: "suffix", expressionType: $data.Expressions.ExpressionType.Increment, type: "number", implementation: function (operand) { return operand++; } }, - { operator: "!", arity: "prefix", expressionType: $data.Expressions.ExpressionType.Not, type: "boolean", implementation: function (operand) { return !operand; } }, - { operator: "--", arity: "suffix", expressionType: $data.Expressions.ExpressionType.Decrement, type: "number", implementation: function (operand) { return operand--; } } - - //{ operator: "new", expressionType : $data.Expressions.ExpressionType.New, type: "object", implementation: function(operand) { return new operand; } -]; - -$data.unaryOperators.resolve = function (operator) { - var result = $data.unaryOperators.filter(function (item) { return item.operator == operator; }); - if (result.length > 0) - return operator; - //Guard.raise("Unknown operator: " + operator); -}; - -$data.unaryOperators.contains = function (operator) { - return $data.unaryOperators.some(function (item) { return item.operator == operator; }); -}; - -$data.unaryOperators.getOperator = function (operator, arity) { - /// - var result = $data.unaryOperators.filter(function (item) { return item.operator == operator && (!arity || item.arity == arity); }); - if (result.length < 1) - Guard.raise("Unknown operator: " + operator); - return result[0]; -}; - - -$data.timeIt = function (fn, iterations) { - iterations = iterations || 1; - - console.time("!"); - for (var i = 0; i < iterations; i++) { - fn(); - } - console.timeEnd("!"); -} - -$data.Expressions.OperatorTypes = { - UNARY: "UNARY", // { type:UNARY, executable:true, operator:, operand: } - INCDEC: "INCDEC", // { type:INCDEC, executable:true, operator:, operand:, suffix: } - DECISION: "DECISION", // { type:DECISION, executable:true, expression:, left:, right: } - METHODCALL: "METHODCALL", // { type:METHODCALL, executable:true, object:, method:, args: } - NEW: "NEW", // { type:NEW, executable:true, values: [] }; - JSONASSIGN: "JSONASSIGN", // { type:JSONASSIGN, executable:true, left:, right: } - ARRAYACCESS: "ARRAYACCESS", // { type:ARRAYACCESS, executable:true, array:, index: } - UNKNOWN: "UNKNOWN" -}; - -$data.executable = true; - -function jsonify(obj) { return JSON.stringify(obj, null, "\t"); } - -$C('$data.Expressions.ExpressionNode', null, null, { - constructor: function () { - ///Provides a base class for all Expressions. - ///Represents the expression type of the node - ///For the list of expression node types refer to $data.Expressions.ExpressionType - /// - ///The result type of the expression - ///True if the expression can be evaluated to yield a result - ///this.nodeType = $data.Expressions.ExpressionType.Unknown; - ///this.type = type; - ///this.nodeType = $data.Expressions.ExpressionType.Unknown; - ///this.executable = (executable === undefined || executable === null) ? true : executable; - ///TODO - this.expressionType = this.constructor; - }, - - getJSON: function () { return jsonify(this); }, - - //TOBLOG maybe - /*expressionType: { - value: undefined, - ////OPTIMIZE - set: function (value) { - var _expressionType; - Object.defineProperty(this, "expressionType", { - set: function (value) { - if (typeof value === 'string') { - value = Container.resolveType(value); - } - _expressionType = value; - }, - get: function (value) { - //IE ommits listing JSON.stringify in call chain - - if (arguments.callee.caller == jsonify || arguments.callee.caller == JSON.stringify) { - return Container.resolveName(_expressionType); - } - return _expressionType; - }, - enumerable: true - }); - - this.expressionType = value; - }, - get: function () { return undefined; }, - enumerable: true - },*/ - expressionType: { - set: function (value) { - if (typeof value === 'string') { - value = Container.resolveType(value); - } - this._expressionType = value; - }, - get: function (value) { - //IE ommits listing JSON.stringify in call chain - - if (arguments.callee.caller == jsonify || arguments.callee.caller == JSON.stringify) { - return Container.resolveName(this._expressionType); - } - return this._expressionType; - }, - enumerable: true - }, - ///toString: function () { }, - nodeType: { value: $data.Expressions.ExpressionType.Unknown, writable: false }, - - type: {}, - - isTerminated: { value: false }, - - toString: function () { - return this.value; - } -}, null); - - -$C('$data.Expressions.UnaryExpression', $data.Expressions.ExpressionNode, null, { - constructor: function (operand, operator, nodeType, resolution) { - /// - /// Represents an operation with only one operand and an operator - /// - /// - /// - /// - /// - this.operand = operand; - this.operator = operator; - this.nodeType = nodeType; - this.resolution = resolution; - }, - - operator: { value: undefined, writable: true }, - operand: { value: undefined, writable: true }, - nodeType: { value: undefined, writable: true } -}); -$C('$data.Expressions.ArrayLiteralExpression', $data.Expressions.ExpressionNode, null, { - constructor: function (items) { - /// - /// - /// - this.items = items || []; - }, - nodeType: { value: $data.Expressions.ExpressionType.ArrayLiteral, writable: true }, - - items: { value: undefined, dataType: Array, elementType: $data.Expressions.ExpressionNode }, - - toString: function (debug) { - //var result; - //result = debug ? this.type + " " : ""; - //result = result + this.name; - ///Represents a call to an object or global method - ///The expression for object that has the method - ///The member descriptor - this.expression = expression; - this.member = member; - this.args = args; - }, - - nodeType: { - value: $data.Expressions.ExpressionType.Call - }, - - expression: { - value: undefined, - dataType: $data.Expressions.ExpressionNode, - writable: true - }, - - member: { - value: undefined, - dataType: $data.MemberDefinition, - writable: true - }, - - type: { - value: undefined, - writable: true - }, - - implementation: { - get: function () { - return function(thisObj, method, args) { - if (typeof method !== 'function') { - method = thisObj[method]; - } - Guard.requireType("method", method, Function); - return method.apply(thisObj, args); - }; - }, - set: function (value) { Guard.raise("Property can not be set"); } - }, - - toString: function (debug) { - return this.object.toString() + "." + this.member.toString() + "(" + ")"; - } - -}); -$C('$data.Expressions.CodeParser', null, null, { - - constructor: function (scopeContext) { - /// - /// - /// - /// - /// - this.scopeContext = scopeContext; - this.lambdaParams = []; - }, - - log: function(logInfo) { - if (this.scopeContext) - this.scopeContext.log(logInfo); - }, - - parseExpression: function (code, resolver) { - /// - ///Parses the provided code and returns a parser result with parser information - ///The JavaScript code to parse ex: "function (a,b,c) { return a + b /c }" - ///The ParameterResolver class that resolves vaiable and parameteres references - /// - /// - if (typeof code === 'object') { code = ''; } - var result = { - success: true, - errorMessage: '', - errorDetails: '' - }; - /// - - //console.log(code.toString()); - if ($data.Acorn){ - //console.log('using acorn.js'); - return { success: true, expression: this.ParserBuild($data.Acorn.parse('(' + code.toString() + ')').body[0]), errors: [] }; - }else if ($data.Esprima){ - //console.log('using esprima.js'); - return { success: true, expression: this.ParserBuild($data.Esprima.parse('(' + code.toString() + ')').body[0]), errors: [] }; - }else{ - //console.log('using JayLint'); - var AST = $data.ASTParser.parseCode(code); - this.log({ event: "AST", data: AST }); - if (!AST.success) { - return { - success: false, - error: "ASTParser error", - errorMessage: (AST.errors) ? JSON.stringify(AST.errors) : "could not get code" - }; - } - var b = this.Build2(AST.tree.first[0]); - result = { success: true, expression: b, errors: AST.errors }; - return result; - } - }, - - createExpression: function (code, resolver) { - /// - ///Parses the provided code and returns a JavaScript code expression tree - ///The JavaScript code to parse ex: "a + b /c" - ///The ParameterResolver class that resolves vaiable and parameteres references - /// - /// - /// - ///Parses the provided code and returns a JavaScript code expression tree - ///The JavaScript function to parse ex: "function (a,b,c) { return a + b /c }" - ///The ParameterResolver class that resolves vaiable and parameteres references - /// - /// - - var result = this.parseExpression(code, resolver); - if (!result.success) { - Guard.raise("ExpressionParserError: " + result.errorMessage); - } - return result.expression; - }, - - ParserBuild: function(node){ - //console.log(node); - return this['Parser' + node.type](node); - }, - - ParserExpressionStatement: function(node){ - return this.ParserBuild(node.expression); - }, - - ParserBlockStatement: function(node){ - return this.ParserBuild(node.body[0]); - }, - - ParserReturnStatement: function(node){ - return this.ParserBuild(node.argument); - }, - - ParserMemberExpression: function(node){ - return new $data.Expressions.PropertyExpression( - this.ParserBuild(node.object), - new $data.Expressions.ConstantExpression(node.property.name || node.property.value, typeof (node.property.name || node.property.value)) - ); - }, - - ParserIdentifier: function(node){ - return this.ParserParameter(node, - this.lambdaParams.indexOf(node.name) > -1 - ? $data.Expressions.ExpressionType.LambdaParameterReference - : $data.Expressions.ExpressionType.Parameter - ); - }, - - ParserObjectExpression: function(node){ - var props = new Array(node.properties.length); - for (var i = 0; i < node.properties.length; i++){ - props[i] = this.ParserProperty(node.properties[i]); - } - - return new $data.Expressions.ObjectLiteralExpression(props); - }, - - ParserArrayExpression: function(node){ - var items = new Array(node.elements.length); - for (var i = 0; i < node.elements.length; i++){ - items[i] = this.ParserBuild(node.elements[i]); - } - - return new $data.Expressions.ArrayLiteralExpression(items); - }, - - ParserProperty: function(node){ - return new $data.Expressions.ObjectFieldExpression(node.key.name, this.ParserBuild(node.value)); - }, - - ParserFunctionExpression: function(node){ - var params = new Array(node.params.length); - for (var i = 0; i < node.params.length; i++){ - this.lambdaParams.push(node.params[i].name); - params[i] = this.ParserParameter(node.params[i], $data.Expressions.ExpressionType.LambdaParameter); - params[i].owningFunction = result; - } - var result = new $data.Expressions.FunctionExpression(node.id ? node.id.name : node.id, params, this.ParserBuild(node.body)); - - return result; - }, - - ParserParameter: function(node, nodeType){ - var result = new $data.Expressions.ParameterExpression(node.name, null, nodeType); - if (nodeType == $data.Expressions.ExpressionType.LambdaParameterReference){ - result.paramIndex = this.lambdaParams.indexOf(node.name); - } - - return result; - }, - - ParserLogicalExpression: function(node){ - return this.ParserBinaryExpression(node); - }, - - ParserOperators: { - value: { - "==": { expressionType: $data.Expressions.ExpressionType.Equal, type: "boolean", implementation: function (a, b) { return a == b; } }, - "===": { expressionType: $data.Expressions.ExpressionType.EqualTyped, type: "boolean", implementation: function (a, b) { return a === b; } }, - "!=": { expressionType: $data.Expressions.ExpressionType.NotEqual, type: "boolean", implementation: function (a, b) { return a != b; } }, - "!==": { expressionType: $data.Expressions.ExpressionType.NotEqualTyped, type: "boolean", implementation: function (a, b) { return a !== b; } }, - ">": { expressionType: $data.Expressions.ExpressionType.GreaterThen, type: "boolean", implementation: function (a, b) { return a > b; } }, - ">=": { expressionType: $data.Expressions.ExpressionType.GreaterThenOrEqual, type: "boolean", implementation: function (a, b) { return a >= b; } }, - "<=": { expressionType: $data.Expressions.ExpressionType.LessThenOrEqual, type: "boolean", implementation: function (a, b) { return a <= b; } }, - "<": { expressionType: $data.Expressions.ExpressionType.LessThen, type: "boolean", implementation: function (a, b) { return a < b; } }, - "&&": { expressionType: $data.Expressions.ExpressionType.And, type: "boolean", implementation: function (a, b) { return a && b; } }, - "||": { expressionType: $data.Expressions.ExpressionType.Or, type: "boolean", implementation: function (a, b) { return a || b; } }, - "&": { expressionType: $data.Expressions.ExpressionType.AndBitwise, type: "number", implementation: function (a, b) { return a & b; } }, - "|": { expressionType: $data.Expressions.ExpressionType.OrBitwise, type: "number", implementation: function (a, b) { return a | b; } }, - "+": { expressionType: $data.Expressions.ExpressionType.Add, type: "number", implementation: function (a, b) { return a + b; } }, - "-": { expressionType: $data.Expressions.ExpressionType.Subtract, type: "number", implementation: function (a, b) { return a - b; } }, - "/": { expressionType: $data.Expressions.ExpressionType.Divide, type: "number", implementation: function (a, b) { return a / b; } }, - "%": { expressionType: $data.Expressions.ExpressionType.Modulo, type: "number", implementation: function (a, b) { return a % b; } }, - "*": { expressionType: $data.Expressions.ExpressionType.Multiply, type: "number", implementation: function (a, b) { return a * b; } }, - "[": { expressionType: $data.Expressions.ExpressionType.ArrayIndex, type: "number", implementation: function (a, b) { return a[b]; } }, - "in": { expressionType: $data.Expressions.ExpressionType.In, type: 'boolean', implementation: function (a, b) { return a in b; } } - } - }, - - ParserUnaryOperators: { - value: { - "+": { arity: "prefix", expressionType: $data.Expressions.ExpressionType.Positive, type: "number", implementation: function (operand) { return +operand; } }, - "-": { arity: "prefix", expressionType: $data.Expressions.ExpressionType.Negative, type: "number", implementation: function (operand) { return -operand; } }, - "++true": { arity: "prefix", expressionType: $data.Expressions.ExpressionType.Increment, type: "number", implementation: function (operand) { return ++operand; } }, - "--true": { arity: "prefix", expressionType: $data.Expressions.ExpressionType.Decrement, type: "number", implementation: function (operand) { return --operand; } }, - "++false": { arity: "suffix", expressionType: $data.Expressions.ExpressionType.Increment, type: "number", implementation: function (operand) { return operand++; } }, - "!": { arity: "prefix", expressionType: $data.Expressions.ExpressionType.Not, type: "boolean", implementation: function (operand) { return !operand; } }, - "--false": { arity: "suffix", expressionType: $data.Expressions.ExpressionType.Decrement, type: "number", implementation: function (operand) { return operand--; } } - } - }, - - ParserUnaryExpression: function(node){ - return new $data.Expressions.UnaryExpression(this.ParserBuild(node.argument), this.ParserUnaryOperators[node.operator], this.ParserUnaryOperators[node.operator].expressionType); - }, - - ParserUpdateExpression: function(node){ - return new $data.Expressions.UnaryExpression(this.ParserBuild(node.argument), this.ParserUnaryOperators[node.operator + node.prefix], this.ParserUnaryOperators[node.operator + node.prefix].nodeType); - }, - - ParserBinaryExpression: function(node){ - return new $data.Expressions.SimpleBinaryExpression( - this.ParserBuild(node.left), - this.ParserBuild(node.right), - this.ParserOperators[node.operator].expressionType, - node.operator, - this.ParserOperators[node.operator].type - ); - }, - - ParserThisExpression: function(node){ - return new $data.Expressions.ThisExpression(); - }, - - ParserLiteral: function(node){ - return new $data.Expressions.ConstantExpression(node.value, typeof node.value); - }, - - ParserCallExpression: function(node){ - var method = this.ParserBuild(node.callee); - var args = new Array(node.arguments.length); - for (var i = 0; i < node.arguments.length; i++){ - args[i] = this.ParserBuild(node.arguments[i]); - } - - var member; - var expression; - switch (true){ - case method instanceof $data.Expressions.PropertyExpression: - expression = method.expression; - member = method.member; - break; - case method instanceof $data.Expressions.ParameterExpression: - expression = new $data.Expressions.ConstantExpression(null, typeof null); - member = method; - break; - } - - return new $data.Expressions.CallExpression(expression, member, args); - }/*, - - Build2: function (node) { - /// - /// - var n; - switch (node.arity) { - case "number": - case "string": - n = this.BuildConstant(node); - break; - case "prefix": - switch (node.value) { - case "{": - n = this.BuildObjectLiteral(node); - break; - case "[": - n = this.BuildArrayLiteral(node); - break; - case $data.unaryOperators.resolve(node.value): - n = this.BuildUnary(node); - break; - //TODO: default case - } - break; - case "suffix": - switch (node.value) { - case $data.unaryOperators.resolve(node.value): - n = this.BuildUnary(node); - break; - default: - Guard.raise("Unknown suffix: " + node.value); - } - break; - case "infix": - switch (node.value) { - case "[": - n = this.BuildArray(node); - break; - case $data.binaryOperators.resolve(node.value): - n = this.BuildSimpleBinary(node); - break; - case "function": - Guard.raise("Unexpected function arity"); - case "(": - n = this.BuildCall(node); - break; - case ".": - n = this.BuildProperty(node); - break; - default: - debugger; - //TODO: remove debugger, throw exception or break - } - break; - case "statement": - switch (node.value) { - case "function": - n = this.BuildFunction(node); - //TODO: consider adding break - } - break; - default: - switch (node.value) { - case "function": - n = this.BuildFunction(node); - break; - case "true": - case "false": - case "null": - n = this.BuildConstant(node); - break; - case "this": - n = this.BuildThis(node); - break; - default: - n = this.BuildParameter(node); - break; - } - } - return n; - }, - - BuildThis: function (node) { - var result = Container.createThisExpression(); - return result; - }, - - BuildConstant: function (node) { - /// - var value = node.value; - var type = node.type; - if (node.reserved === true) { - switch (node.value) { - case "true": value = true; type = typeof true; break; - case "false": value = false; type = typeof false; break; - case "null": value = null; type = typeof null; break; - //TODO: missing default case - } - } - var result = new $data.Expressions.ConstantExpression(value, type); - return result; - }, - - BuildFunctionParameter: function (node) { - - }, - - BuildArray: function (node) { - switch (node.second.type) { - case "string": - return this.BuildProperty(node); - case "number": - default: - return this.BuildSimpleBinary(node); - } - }, - - BuildParameter: function (node) { - /// - /// - var paramName = node.value; - //TODO - //var paramType = this.resolver.resolveParameterType(node); - var nodeType = node.funct ? $data.Expressions.ExpressionType.LambdaParameter : - this.lambdaParams.indexOf(node.value) > -1 ? - $data.Expressions.ExpressionType.LambdaParameterReference : $data.Expressions.ExpressionType.Parameter; - var result = new $data.Expressions.ParameterExpression(node.value, null, nodeType); - - if (nodeType == $data.Expressions.ExpressionType.LambdaParameterReference) { - result.paramIndex = this.lambdaParams.indexOf(node.value); - } - - return result; - }, - - BuildArrayLiteral: function(node) { - var self = this; - var items = node.first.map(function (item) { return self.Build2(item); }); - var result = new $data.Expressions.ArrayLiteralExpression(items); - return result; - }, - - BuildObjectLiteral: function (node) { - var self = this; - var fields = node.first.map(function (item) { - var eItem = self.Build2(item.first); - var result = new $data.Expressions.ObjectFieldExpression(item.value, eItem); - return result; - }); - var result = new $data.Expressions.ObjectLiteralExpression(fields); - return result; - }, - - BuildFunction: function (node) { - /// - /// - var self = this; - var paramStack = []; - var params = node.first && node.first.map(function (paramNode) { - //paramStack.push(paramNode.value); - this.lambdaParams.push(paramNode.value); - return self.BuildParameter(paramNode); - }, this); - params = params || []; - - //skipping return for convenience - //Possible we should raise an error as predicates and selectors can - //not be code blocks just expressions - - var hasReturn = node.block.length == 0 ? false : - node.block[0].value === "return" ? true : false; - var body = (node.block.length > 0) ? this.Build2(hasReturn ? node.block[0].first : node.block[0]) : null; - - paramStack.forEach(function () { this.lambdaParams.pop(); }, this); - - var result = new $data.Expressions.FunctionExpression(node.value, params, body); - params.forEach(function (param) { - param.owningFunction = result; - }); - - //TODO place on prototyope - result.name = node.name; - return result; - }, - - BuildCall: function (node) { - var self = this; - var method = self.Build2(node.first); - var args = node.second.map(function (exp) { return self.Build2(exp); }); - var member; - var expression; - switch(true){ - case method instanceof $data.Expressions.PropertyExpression: - expression = method.expression; - member = method.member; - break; - case method instanceof $data.Expressions.ParameterExpression: - expression = Container.createConstantExpression(null, typeof null); - member = method; - break; - //TODO: missing default case - } - - var result = Container.createCallExpression(expression, member, args); - return result; - }, - - BuildProperty: function (node) { - ///Builds a PropertyExpression from the AST node - /// - /// - var expression = this.Build2(node.first); - //TODO - //var type = expression.type; - //var member = type.getMemberDefinition() - //TODO how to not if????? - var member; - if (node.second.identifier) { - member = new $data.Expressions.ConstantExpression(node.second.value, "string"); - } else { - member = this.Build2(node.second); - } - var result = new $data.Expressions.PropertyExpression(expression, member); - return result; - }, - - - BuildUnary: function(node) { - var operator = $data.unaryOperators.getOperator(node.value, node.arity); - var nodeType = operator.expressionType; - var operand = this.Build2(node.first); - var result = new $data.Expressions.UnaryExpression(operand, operator, nodeType); - return result; - }, - - BuildSimpleBinary: function (node) { - /// - - var operator = $data.binaryOperators.getOperator(node.value); - var nodeType = operator.expressionType; - - var left = this.Build2(node.first || node.left); - var right = this.Build2(node.second || node.right); - var result = new $data.Expressions.SimpleBinaryExpression(left, right, nodeType, node.value, operator.type); - return result; - } - - //Build: function (node, expNode) { - // var n; - // switch (node.arity) { - // case "ternary": - // if (node.value == "?") - // n = this.BuildDecision(node, expNode); - // else - // Guard.raise("Value of ternary node isn't implemented: " + node.value); - // break; - // case null: - // default: - // Guard.raise("Arity isn't implemented: " + node.arity); - // } - // return n; - //},*/ - -}); - -$C('$data.Expressions.ConstantExpression', $data.Expressions.ExpressionNode, null, { - constructor: function (value, type, name) { - this.value = value; - //TODO - //this.type = Container.getTypeName(value); - - this.type = type; - this.name = name; - if (!Object.isNullOrUndefined(this.value)) { - this.type = Container.resolveType(this.type) - if (Container.resolveType(Container.getTypeName(this.value)) !== this.type) - this.value = Container.convertTo(value, this.type); - } - }, - nodeType: { value: $data.Expressions.ExpressionType.Constant, enumerable: true }, - type: { value: Object, writable: true }, - value: { value: undefined, writable: true }, - toString: function (debug) { - //return "[constant: " + this.value.toString() + "]"; - return this.value.toString(); - } -}); - - -$C('$data.Expressions.FunctionExpression', $data.Expressions.ExpressionNode, null, { - constructor: function (name, parameters, body) { - /// - ///Represents a function declaration. - ///Function name - ///The list of function parameters - /// - /// - ///The list of function parameters - ///The function body - - this.parameters = parameters || []; - this.name = name; - this.body = body; - }, - - toString: function (debug) { - var paramStrings = this.parameters.map(function (p) { - return p.toString(); - }); - paramStrings = paramStrings.join(","); - var bodyString = (this.body ? this.body.toString(debug) : ''); - return "function " + this.name + "(" + paramStrings + ") { " + bodyString + "}"; - }, - nodeType: { value: $data.Expressions.ExpressionType.Function, writable: true }, - parameters: { value: undefined, dataType: Array, elementType: $data.Expressions.ParameterExpression }, - body: { value: undefined, dataType: $data.Expressions.ExpressionNode }, - type: {} -}, null); -$C('$data.Expressions.ObjectFieldExpression', $data.Expressions.ExpressionNode, null, { - constructor: function (fieldName, expression) { - /// - /// - this.fieldName = fieldName; - this.expression = expression; - }, - nodeType: { value: $data.Expressions.ExpressionType.ObjectField, writable: true }, - - toString: function (debug) { - //var result; - //result = debug ? this.type + " " : ""; - //result = result + this.name; - var result = "unimplemented"; - return result; - } -}, null); - -$C('$data.Expressions.ObjectLiteralExpression', $data.Expressions.ExpressionNode, null, { - constructor: function (members) { - ///Represent an object initializer literal expression Ex: { prop: value} - /// - this.members = members; - }, - nodeType: { value: $data.Expressions.ExpressionType.ObjectLiteral, writable: true }, - - toString: function (debug) { - //var result; - //result = debug ? this.type + " " : ""; - //result = result + this.name; - var result = "unimplemented"; - return result; - }, - - implementation: { - get: function () { - return function(namesAndValues) { - var result = { }; - namesAndValues.forEach(function(item) { - result[item.name] = item.value; - }); - return result; - }; - }, - set: function () { - } - } - -}, null); -$C('$data.Expressions.PagingExpression', $data.Expressions.ExpressionNode, null, { - constructor: function (source, expression, nType) { - /// - /// - this.source = source; - this.amount = expression; - this.nodeType = nType; - }, - nodeType: { value: $data.Expressions.ExpressionType.Unknown, writable: true }, - - toString: function (debug) { - //var result; - //result = debug ? this.type + " " : ""; - //result = result + this.name; - var result = "unimplemented"; - return result; - } -}, null); -$C('$data.Expressions.ParameterExpression', $data.Expressions.ExpressionNode, null, { - constructor: function (name, type, nodeType) { - /// - /// - //this.writePropertyValue("name", name); - //this.writePropertyValue("type", type); - this.nodeType = nodeType || $data.Expressions.ExpressionType.Parameter; - this.name = name; - this.type = type || "unknown"; - var _owningFunction; - }, - - owningFunction: { value: undefined, enumerable: false }, - nodeType: { value: $data.Expressions.ExpressionType.Parameter, writable: true }, - name: { value: undefined, dataType: String, writable: true }, - type: { value: undefined, dataType: "object", writable: true}, - toString: function (debug) { - var result; - result = debug ? this.type + " " : ""; - result = result + this.name; - return result; - } -}, null); -$C('$data.Expressions.PropertyExpression', $data.Expressions.ExpressionNode, null, { - constructor: function (expression, member) { - ///Represents accessing a property or field of an object - ///The expression for the property owner object - ///The member descriptor - ///The expression for the property owner object - ///The member descriptor - - this.expression = expression; - this.member = member; - - this.type = member.dataType; - }, - - nodeType: { - value: $data.Expressions.ExpressionType.MemberAccess - }, - - expression: { - value: undefined, - dataType: $data.Expressions.ExpressionNode, - writable: true - }, - - implementation: { - get: function () { - return function (holder, memberName) { - if (holder[memberName] === undefined) - Guard.raise(new Exception("Parameter '" + memberName + "' not found in context", 'Property not found!')); - return holder[memberName]; - }; - }, - set: function () { - } - }, - - member: { - value: undefined, - dataType: $data.MemberDefinition, - writable: true - }, - - type: { - value: undefined, - writable: true - }, - - toString: function (debug) { - return this.expression.toString() + "." + this.member.toString(); - } - -}); - -$C('$data.Expressions.SimpleBinaryExpression', $data.Expressions.ExpressionNode, null, { - constructor: function (left, right, nodeType, operator, type, resolution) { - ///Represents a bin operation with left and right operands and an operator/// - ///The left element of the binary operation - ///The right element of the binary operation - /// - this.left = left; - this.right = right; - this.nodeType = nodeType; - this.operator = operator; - this.type = type; - this.resolution = resolution; - }, - - implementation: { - get: function () { - return $data.binaryOperators.getOperator(this.operator).implementation; - }, - set: function () { } - - }, - //nodeType: { value: $data.Expressions.ExpressionType }, - type: { value: "number", writable: true } -}); -$C('$data.Expressions.ThisExpression', $data.Expressions.ExpressionNode, null, { - nodeType: { value: $data.Expressions.ExpressionType.This } -}); -$C('$data.Expressions.ExpressionVisitor', null, null, - { - constructor: function () { - this._deep = 0; - }, - - Visit: function (eNode, context) { - /// - /// - /// - // - - //this._deep = this._deep + 1; - if (!eNode) { - return eNode; - } - - var result = null; - - switch (eNode.expressionType) { - case $data.Expressions.ParameterExpression: - result = this.VisitParameter(eNode, context); - break; - case $data.Expressions.ConstantExpression: - result = this.VisitConstant(eNode, context); - break; - case $data.Expressions.FunctionExpression: - result = this.VisitFunction(eNode, context); - break; - case $data.Expressions.CallExpression: - result = this.VisitCall(eNode, context); - break; - case $data.Expressions.SimpleBinaryExpression: - result = this.VisitBinary(eNode, context); - break; - case $data.Expressions.PropertyExpression: - result = this.VisitProperty(eNode, context); - break; - //result = th - case $data.Expressions.ThisExpression: - result = this.VisitThis(eNode, context); - break; - case $data.Expressions.ObjectLiteralExpression: - result = this.VisitObjectLiteral(eNode, context); - break; - case $data.Expressions.ObjectFieldExpression: - result = this.VisitObjectField(eNode, context); - break; - case $data.Expressions.ArrayLiteralExpression: - result = this.VisitArrayLiteral(eNode, context); - break; - case $data.Expressions.UnaryExpression: - result = this.VisitUnary(eNode, context); - break; - case $data.Expressions.EntityContextExpression: - result = this.VisitEntityContext(eNode, context); - break; - default: - debugger; - break; - //case VARIABLE: - - // result = this.VisitVariable(eNode, context); - // break; - //case MEMBERACCESS: - // result = this.VisitMember(eNode, context); - // break; - //case BINARY: - // result = this.VisitBinary(eNode, context); - // break; - //case UNARY: - // result = this.VisitUnary(eNode, context); - // break; - //case INCDEC: - // result = this.VisitIncDec(eNode, context); - // break; - //case EQUALITY: result = this.VisitEquality(eNode, context); break; - //case DECISION: result = this.VisitDecision(eNode, context); break; - //case METHODCALL: result = this.VisitMethodCall(eNode, context); break; - //case NEW: result = this.VisitNew(eNode, context); break; - //case JSONASSIGN: result = this.VisitJsonAssign(eNode, context); break; - //case ARRAYACCESS: result = this.VisitArrayAccess(eNode, context); break; - //default: - // Guard.raise("Type isn't implemented: " + eNode.type); - } - - this._deep = this._deep - 1; - return result; - }, - - VisitArrayLiteral: function(eNode, context) { - /// - var self = this; - var items = eNode.items.map(function (item) { - return self.Visit(item, context); - }); - var result = Container.createArrayLiteralExpression(items); - return result; - }, - - VisitObjectLiteral: function(eNode, context) { - /// - var self = this; - var members = eNode.members.map(function (member) { - return self.Visit(member, context); - }); - var result = Container.createObjectLiteralExpression(members); - return result; - }, - - VisitObjectField: function(eNode, context) { - /// - var expression = this.Visit(eNode.expression, context); - var result = Container.createObjectFieldExpression(eNode.fieldName, expression); - return result; - }, - - VisitThis: function (eNode, context) { - return eNode; - }, - VisitCall: function (eNode, context) { - /// - var self = this; - var args = eNode.args.map(function (arg) { return this.Visit(arg, context); }, this); - var expression = this.Visit(eNode.expression, context); - var member = this.Visit(eNode.member, context); - return new $data.Expressions.CallExpression(expression, member, args); - }, - - VisitParameter: function(eNode, context) { - /// - /// - //var result = new $data.Expressions.ParameterExpression(eNode.name, eNode.type, eNode.nodeType); - return eNode; - }, - - VisitConstant: function (eNode, context) { - /// - /// - //var result = new $data.Expressions.ParameterExpression(eNode.name, eNode.type, eNode.nodeType); - return eNode; - }, - - VisitFunction: function(eNode, context) { - /// - var self = this; - - var params = eNode.parameters.map(function (p, i) { - return self.Visit(p, context); - }); - - var body = self.Visit(eNode.body, context); - var result = new $data.Expressions.FunctionExpression(eNode.name, params, body); - return result; - }, - - VisitBinary: function (eNode, context) { - /// - /// - /// - // - - var left = this.Visit(eNode.left, context); - var right = this.Visit(eNode.right, context); - return new $data.Expressions.SimpleBinaryExpression(left, right, eNode.nodeType, eNode.operator, eNode.type); - }, - - VisitProperty: function (eNode, context) { - /// - var expression = this.Visit(eNode.expression, context); - var member = this.Visit(eNode.member, context); - return new $data.Expressions.PropertyExpression(expression, member); - //var member = - }, - - VisitUnary: function (eNode, context) { - /// - /// - /// - /// - var operand = this.Visit(eNode.operand, context); - if (operand === eNode.operand) - return eNode; - return new $data.Expressions.UnaryExpression(operand, eNode.operator, eNode.nodeType); - }, - - VisitEntityContext: function (eNode, context) { - /// - /// - //var result = new $data.Expressions.ParameterExpression(eNode.name, eNode.type, eNode.nodeType); - return eNode; - }, - - VisitDecision: function (eNode, context) { - /// - /// - /// - // - - var expression = this.Visit(eNode.expression, context); - var left = this.Visit(eNode.left, context); - var right = this.Visit(eNode.right, context); - if (expression === eNode.expression && left === eNode.left && right === eNode.right) - return eNode; - return $data.Expressions.ExpressionNodeTypes.DecisionExpressionNode.create(eNode.executable, expression, left, right); - }, - - VisitNew: function (eNode, context) { - /// - /// - /// - // - - var values = this.VisitArray(eNode.values, context); - if (values === eNode.values) - return eNode; - return $data.Expressions.ExpressionNodeTypes.NewExpressionNode.create(true, values); - }, - VisitArrayAccess: function (eNode, context) { - /// - /// - /// - // - - var array = this.Visit(eNode.array, context); - var index = this.Visit(eNode.index, context); - if (array === eNode.array && index === eNode.index) - return eNode; - return $data.Expressions.ExpressionNodeTypes.ArrayAccessExpressionNode.create(true, array, index); - }, - VisitArray: function (eNodes, context) { - var args = []; - var ok = true; - for (var i = 0; i < eNodes.length; i++) { - args[i] = this.Visit(eNodes[i], context); - ok = ok && args[i] === eNodes[i]; - } - return ok ? eNodes : args; - }, - GetMemberChain: function (memberAccess, context) { - // { type:MEMBERACCESS, executable:true, expression:, member: } - if (memberAccess.expression.type == MEMBERACCESS) { - var a = this.GetMemberChain(memberAccess.expression, context); - a.push(memberAccess.member); - return a; - } - return [memberAccess.expression, memberAccess.member]; - } - }, {});$C("$data.Expressions.ParameterProcessor", $data.Expressions.ExpressionVisitor, null, { - constructor: function () { - ///Provides a base class for several ParameterProcessors like GlobalParameterProcessor or LambdaParameterProcessor - }, - - Visit: function (node, context) { - if ((node instanceof $data.Expressions.ParameterExpression || - node instanceof $data.Expressions.ThisExpression) - && this.canResolve(node)) { - var result = this.resolve(node, context); - if (result !== node) - result["resolvedBy"] = this.constructor.name; - return result; - } else { - return node; - } - }, - - canResolve: function (paramExpression) { - /// - Guard.raise("Pure method"); - }, - resolve: function (paramExpression) { - /// - Guard.raise("Pure method"); - } -}); -$C("$data.Expressions.GlobalContextProcessor", $data.Expressions.ParameterProcessor, null, { - constructor: function (global) { - /// - this.global = global; - }, - - canResolve: function (paramExpression) { - /// - return paramExpression.nodeType == $data.Expressions.ExpressionType.Parameter && this.global && typeof this.global === 'object' && - paramExpression.name in this.global; - }, - - resolve: function (paramExpression) { - /// - /// - var resultValue = this.global[paramExpression.name]; - var expression = Container.createConstantExpression(resultValue, typeof resultValue, paramExpression.name); - return expression; - } - -}); - - - -$C("$data.Expressions.ConstantValueResolver", $data.Expressions.ParameterProcessor, null, { - constructor: function (paramsObject, global, scopeContext) { - /// - this.globalResolver = Container.createGlobalContextProcessor(global); - this.paramResolver = Container.createGlobalContextProcessor(paramsObject); - this.paramsObject = paramsObject; - this.scopeContext = scopeContext; - }, - - canResolve: function (paramExpression) { - /// - return (paramExpression.name === '$context') || (paramExpression.nodeType == $data.Expressions.ExpressionType.This && this.paramsObject) - ? true : (this.paramResolver.canResolve(paramExpression) || this.globalResolver.canResolve(paramExpression)); - }, - - resolve: function (paramExpression) { - /// - /// - if (paramExpression.name === '$context') { - return Container.createEntityContextExpression(this.scopeContext); - } - if (paramExpression.nodeType == $data.Expressions.ExpressionType.This) { - return Container.createConstantExpression(this.paramsObject, typeof this.paramsObject, 'this'); - } - return this.paramResolver.canResolve(paramExpression) ? this.paramResolver.resolve(paramExpression) : this.globalResolver.resolve(paramExpression); - } - -});$C("$data.Expressions.LocalContextProcessor", $data.Expressions.GlobalContextProcessor, null, { - constructor: function (evalMethod) { - /// - this.canResolve = function (paramExpression) { - /// - return paramExpression.nodeType == $data.Expressions.ExpressionType.Parameter && - (evalMethod("typeof " + paramExpression.name) !== 'undefined'); - }; - this.resolve = function(paramExpression) { - /// - /// - var resultValue = evalMethod(paramExpression.name); - var expression = Container.createConstantExpression(resultValue, typeof resultValue); - return expression; - }; - - } - }); -$C("$data.Expressions.LambdaParameterProcessor", $data.Expressions.ParameterProcessor, null, { - constructor: function (lambdaParameterTypeInfos) { - /// - /// - var paramIndices = {}; - var $idx = "name"; - - this.canResolve = function (paramExpression, context) { - if (paramExpression.nodeType == $data.Expressions.ExpressionType.LambdaParameter) { - var fnParams = paramExpression.owningFunction.parameters; - - if (fnParams.length == 1 && paramExpression.name == fnParams[0].name) { - paramIndices[paramExpression.name] = lambdaParameterTypeInfos[0]; - return true; - } - - for (var j = 0; j < fnParams.length; j++) { - if (fnParams[j].name == paramExpression.name) { - paramIndices[paramExpression.name] = lambdaParameterTypeInfos[j]; - return true; - } - } - return false; - } - return false; - }; - - this.resolve = function(paramExpression, context) { - var lambdaParamType = paramIndices[paramExpression.name]; - var result = Container.createParameterExpression(paramExpression.name, - lambdaParamType, - $data.Expressions.ExpressionType.LambdaParameter); - result.owningFunction = paramExpression.owningFunction; - return result; - }; - } - -}); -$C('$data.Expressions.ParameterResolverVisitor', $data.Expressions.ExpressionVisitor, null, { - - constructor: function (expression, resolver) { - /// - /// ParameterResolverVisitor traverses the JavaScript Code Expression tree and converts - /// outer but otherwise execution local variable references into ConstantExpressions-t. - /// for example: context.Persons.filter(function(p) { return p.Name == document.location.href }) - /// is transformed into a constant that has the current href as its value - /// - /// - /// - this.lambdaParamCache = {}; - }, - - Visit: function (expression, resolver) { - /// - /// - //TODO base call is just ugly - return $data.Expressions.ExpressionVisitor.prototype.Visit.call(this, expression, resolver); - - }, - - - VisitArrayLiteral: function(eNode, context) { - var self = this; - var items = eNode.items.map(function (item) { return self.Visit(item, context); }); - var allLocal = items.every(function (item) { - return item instanceof $data.Expressions.ConstantExpression; - }); - - if (allLocal) { - items = items.map(function (item) { return item.value }); - return Container.createConstantExpression(items, "array"); - } else { - return Container.createArrayLiteralExpression(items); - } - }, - - VisitObjectLiteral: function(eNode, context) { - var self = this; - var members = eNode.members.map(function (item) { return self.Visit(item, context); }); - var allLocal = members.every(function (member) { - return member.expression instanceof $data.Expressions.ConstantExpression; - }); - - if (allLocal) { - var params = members.map(function (member) { return { name: member.fieldName, value: member.expression.value }; }); - var value = eNode.implementation(params); - return Container.createConstantExpression(value, typeof value); - } else { - return Container.createObjectLiteralExpression(members); - } - }, - - VisitThis: function(eNode, resolver) { - return resolver.Visit(eNode, resolver); - }, - - VisitParameter: function(eNode, resolver) { - /// - /// - /// - - var node; - ///TODO let the resolver handle lambdaReferences if it wants to deal with it - switch(eNode.nodeType){ - case $data.Expressions.ExpressionType.Parameter: - case $data.Expressions.ExpressionType.LambdaParameter: - node = resolver.Visit(eNode, resolver); - if (node.nodeType == $data.Expressions.ExpressionType.LambdaParameter) { - this.lambdaParamCache[node.name] = node; - } - return node; - case $data.Expressions.ExpressionType.LambdaParameterReference: - var lambdaParam = this.lambdaParamCache[eNode.name]; - if (lambdaParam) { - node = Container.createParameterExpression(eNode.name, - lambdaParam.type, - $data.Expressions.ExpressionType.LambdaParameterReference); - node.paramIndex = eNode.paramIndex; - //node.typeName = lambdaParam.type.name || lambdaParam.type; - return node; - } - break; - default: - return eNode; - - } - - - return eNode; - }, - - VisitConstant: function (eNode, context) { - /// - /// - return eNode; - }, - - VisitFunction: function(eNode, context) { - /// - - var self = this; - var params = eNode.parameters.map(function (p, i) { - var result = self.Visit(p, context); - return result; - }); - var body = self.Visit(eNode.body, context); - var result = new $data.Expressions.FunctionExpression(eNode.name, params, body); - - return result; - }, - - VisitBinary: function (eNode, context) { - /// - /// - /// - /// - - var left = this.Visit(eNode.left, context); - var right = this.Visit(eNode.right, context); - var expr = $data.Expressions; - - if (left instanceof expr.ConstantExpression && right instanceof expr.ConstantExpression) - { - var result = eNode.implementation(left.value, right.value); - return Container.createConstantExpression(result, typeof result); - } - return new Container.createSimpleBinaryExpression(left, right, eNode.nodeType, eNode.operator, eNode.type); - }, - - VisitUnary: function (eNode, context) { - /// - /// - /// - /// - - var operand = this.Visit(eNode.operand, context); - //var imp = $data.unaryOperators.getOperator( - var expr = $data.Expressions; - if (operand instanceof expr.ConstantExpression) - { - var result = eNode.operator.implementation(operand.value); - return Container.createConstantExpression(result, typeof result); - } - return new Container.createUnaryExpression(operand, eNode.operator, eNode.nodeType); - }, - - VisitProperty: function (eNode, context) { - /// - var expression = this.Visit(eNode.expression, context); - var member = this.Visit(eNode.member, context); - var result; - if (expression instanceof $data.Expressions.ConstantExpression && - member instanceof $data.Expressions.ConstantExpression) { - ///TODO implement checking for the member, throw on error - result = eNode.implementation(expression.value, member.value); - - //Method call processed before - //if (typeof result === 'function') { - // return new $data.Expressions.ConstantExpression( - // function () { return result.apply(expression.value, arguments); }); - //} - return Container.createConstantExpression(result, typeof result, expression.name + '$' + member.value); - } - if (expression === eNode.expression && member === eNode.member) - return eNode; - - result = Container.createPropertyExpression(expression, member); - return result; - }, - - VisitCall: function (eNode, context) { - /// - function isExecutable(args, body, obj) { - return body instanceof $data.Expressions.ConstantExpression && - //global methods will not have a this. - (!obj || obj instanceof $data.Expressions.ConstantExpression) && - args.every(function(item) { - return item instanceof $data.Expressions.ConstantExpression; - }); - } - var call = $data.Expressions.ExpressionVisitor.prototype.VisitCall.apply(this, arguments); - var obj = call.expression; - var body = call.member; - var args = call.args; - - function convertToValue(arg) { - if (arg instanceof $data.Expressions.ConstantExpression) - return arg.value; - return arg; - }; - - if (isExecutable(args, body, obj)) { - var fn = body.value; - if (typeof fn === 'string' && obj.value) { - fn = obj.value[fn]; - } - if (typeof fn !== 'function') { - //TODO dig that name out from somewhere - Guard.raise("Constant expression is not a method..."); - } - var value = eNode.implementation(obj.value, fn, args.map(convertToValue)); - return new $data.Expressions.ConstantExpression(value, typeof value); - } - return call; - } -}, {}); -$C("$data.Expressions.AggregatedVisitor", $data.Expressions.ExpressionVisitor, null, { - constructor: function (visitors) { - /// - - this.Visit = function (node, context) { - for (var i = 0; i < visitors.length; i++) { - var n = visitors[i].Visit(node, context); - if (n !== node) - return n; - } - return node; - }; - } - -}); -//"use strict"; // suspicious code - -$C('$data.Expressions.LogicalSchemaBinderVisitor', - $data.Expressions.ExpressionVisitor, null, - { - constructor: function (expression, binder) { - - }, - - VisitProperty: function (expression, context) { - /// - var exp = this.Visit(expression.expression, context); - var mem = this.Visit(expression.member, context); - - var type = exp.type; - var memberType = context.memberResolver.resolve(type, mem.value); - mem.type = memberType; - return Container.createPropertyExpression(exp, mem); - } - - }, {});$data.Class.define('$data.Expressions.ExpTreeVisitor', - null, null, - { - constructor: function () { - this._deep = 0; - }, - Visit: function (eNode, context) { - /// - /// - /// - // - this._deep = this._deep + 1; - var result = null; - switch (eNode.type) { - case LITERAL: result = this.VisitLiteral(eNode, context); break; - case VARIABLE: result = this.VisitVariable(eNode, context); break; - case MEMBERACCESS: result = this.VisitMember(eNode, context); break; - case BINARY: result = this.VisitBinary(eNode, context); break; - case UNARY: result = this.VisitUnary(eNode, context); break; - case INCDEC: result = this.VisitIncDec(eNode, context); break; - case EQUALITY: result = this.VisitEquality(eNode, context); break; - case DECISION: result = this.VisitDecision(eNode, context); break; - case METHODCALL: result = this.VisitMethodCall(eNode, context); break; - case NEW: result = this.VisitNew(eNode, context); break; - case JSONASSIGN: result = this.VisitJsonAssign(eNode, context); break; - case ARRAYACCESS: result = this.VisitArrayAccess(eNode, context); break; - default: - Guard.raise("Type isn't implemented: " + eNode.type); - } - this._deep = this._deep - 1; - return result; - }, - VisitLiteral: function (eNode, context) { - /// - /// - /// - // - - return eNode; - }, - VisitVariable: function (eNode, context) { - /// - /// - /// - // - - return eNode; - }, - VisitMember: function (eNode, context) { - /// - /// - /// - // - - var expression = this.Visit(eNode.expression, context); - var member = this.Visit(eNode.member, context); - if (expression === eNode.expression && member === eNode.member) - return eNode; - return $data.Expressions.ExpressionNodeTypes.MemberAccessExpressionNode.create(eNode.executable, expression, member); - }, - VisitBinary: function (eNode, context) { - /// - /// - /// - // - - var left = this.Visit(eNode.left, context); - var right = this.Visit(eNode.right, context); - if (left === eNode.left && right === eNode.right) - return eNode; - return $data.Expressions.ExpressionNodeTypes.BinaryExpressionNode.create(eNode.executable, eNode.operator, left, right); - }, - VisitUnary: function (eNode, context) { - /// - /// - /// - // - - var operand = this.Visit(eNode.operand, context); - if (operand === eNode.operand) - return eNode; - return $data.Expressions.ExpressionNodeTypes.UnaryExpressionNode.create(eNode.executable, eNode.operator, operand); - }, - VisitIncDec: function (eNode, context) { - /// - /// - /// - // - - var operand = this.Visit(eNode.operand, context); - if (operand === eNode.operand) - return eNode; - return $data.Expressions.ExpressionNodeTypes.IncDecExpressionNode.create(eNode.executable, eNode.operator, operand, eNode.suffix); - }, - VisitEquality: function (eNode, context) { - /// - /// - /// - // - - var left = this.Visit(eNode.left, context); - var right = this.Visit(eNode.right, context); - if (left === eNode.left && right === eNode.right) - return eNode; - return $data.Expressions.ExpressionNodeTypes.EqualityExpressionNode.create(eNode.executable, eNode.operator, left, right); - }, - VisitDecision: function (eNode, context) { - /// - /// - /// - // - - var expression = this.Visit(eNode.expression, context); - var left = this.Visit(eNode.left, context); - var right = this.Visit(eNode.right, context); - if (expression === eNode.expression && left === eNode.left && right === eNode.right) - return eNode; - return $data.Expressions.ExpressionNodeTypes.DecisionExpressionNode.create(eNode.executable, expression, left, right); - }, - VisitMethodCall: function (eNode, context) { - /// - /// - /// - // - - var object = eNode.object ? this.Visit(eNode.object, context) : null; - var args = this.VisitArray(eNode.args, context); - if (object === eNode.object && args === eNode.args) - return eNode; - return $data.Expressions.ExpressionNodeTypes.MethodcallExpressionNode.create(eNode.executable, object, eNode.method, args); - }, - VisitNew: function (eNode, context) { - /// - /// - /// - // - - var values = this.VisitArray(eNode.values, context); - if (values === eNode.values) - return eNode; - return $data.Expressions.ExpressionNodeTypes.NewExpressionNode.create(true, values); - }, - VisitJsonAssign: function (eNode, context) { - /// - /// - /// - // - - var left = this.Visit(eNode.left, context); - var right = this.Visit(eNode.right, context); - if (left === eNode.left && right === eNode.right) - return eNode; - left.JSONASSIGN = true; - right.JSONASSIGN = true; - return $data.Expressions.ExpressionNodeTypes.JsonAssignExpressionNode.create(true, left, right); - }, - VisitArrayAccess: function (eNode, context) { - /// - /// - /// - // - - var array = this.Visit(eNode.array, context); - var index = this.Visit(eNode.index, context); - if (array === eNode.array && index === eNode.index) - return eNode; - return $data.Expressions.ExpressionNodeTypes.ArrayAccessExpressionNode.create(true, array, index); - }, - VisitArray: function (eNodes, context) { - var args = []; - var ok = true; - for (var i = 0; i < eNodes.length; i++) { - args[i] = this.Visit(eNodes[i], context); - ok = ok && args[i] === eNodes[i]; - } - return ok ? eNodes : args; - }, - GetMemberChain: function (memberAccess, context) { - // { type:MEMBERACCESS, executable:true, expression:, member: } - if (memberAccess.expression.type == MEMBERACCESS) { - var a = this.GetMemberChain(memberAccess.expression, context); - a.push(memberAccess.member); - return a; - } - return [memberAccess.expression, memberAccess.member]; - } - }, {});$data.Class.define('$data.Expressions.SetExecutableVisitor', $data.Expressions.ExpTreeVisitor, null, -{ - Visit: function (eNode, context) { - switch (eNode.type) { - case LITERAL: return this.VisitLiteral(eNode, context); - case VARIABLE: return this.VisitVariable(eNode, context); - case MEMBERACCESS: return this.VisitMember(eNode, context); - case BINARY: return this.VisitBinary(eNode, context); - case UNARY: return this.VisitUnary(eNode, context); - case INCDEC: return this.VisitIncDec(eNode, context); - case EQUALITY: return this.VisitEquality(eNode, context); - case DECISION: return this.VisitDecision(eNode, context); - case METHODCALL: return this.VisitMethodCall(eNode, context); - case NEW: return this.VisitNew(eNode, context); - case JSONASSIGN: return this.VisitJsonAssign(eNode, context); - case ARRAYACCESS: return this.VisitArrayAccess(eNode, context); - default: - Guard.raise("Type isn't implemented: " + eNode.type); - } - }, - - VisitBinary: function (eNode, context) { - var left = this.Visit(eNode.left, context); - var right = this.Visit(eNode.right, context); - if (left === eNode.left && right === eNode.right && (left.executable && right.executable == eNode.executable)) - return eNode; - return $data.Expressions.ExpressionNodeTypes.BinaryExpressionNode.create(left.executable && right.executable, eNode.operator, left, right); - }, - VisitUnary: function (eNode, context) { - var operand = this.Visit(eNode.operand, context); - if (operand === eNode.operand) - return eNode; - return $data.Expressions.ExpressionNodeTypes.UnaryExpressionNode.create(operand.executable, eNode.operator, operand); - }, - VisitIncDec: function (eNode, context) { - var operand = this.Visit(eNode.operand, context); - if (operand === eNode.operand) - return eNode; - return $data.Expressions.ExpressionNodeTypes.IncDecExpressionNode.create(operand.executable, eNode.operator, operand, eNode.suffix); - }, - VisitEquality: function (eNode, context) { - var left = this.Visit(eNode.left, context); - var right = this.Visit(eNode.right, context); - if (left === eNode.left && right === eNode.right && (left.executable && right.executable == eNode.executable)) - return eNode; - return $data.Expressions.ExpressionNodeTypes.EqualityExpressionNode.create(left.executable && right.executable, eNode.operator, left, right); - }, - VisitDecision: function (eNode, context) { - var expression = this.Visit(eNode.expression, context); - var left = this.Visit(eNode.left, context); - var right = this.Visit(eNode.right, context); - if (expression === eNode.expression && left === eNode.left && right === eNode.right && (left.executable && right.executable && expression.executable == eNode.executable)) - return eNode; - return $data.Expressions.ExpressionNodeTypes.DecisionExpressionNode.create(left.executable && right.executable && expression.executable, expression, left, right); - }, - VisitMethodCall: function (eNode, context) { - var object = eNode.object ? this.Visit(eNode.object, context) : null; - var args = this.VisitArray(eNode.args, context); - if (object === eNode.object && args === eNode.args && ((object == null ? true : object.executable) == eNode.executable)) - return eNode; - return $data.Expressions.ExpressionNodeTypes.MethodcallExpressionNode.create(object == null ? true : object.executable, object, eNode.method, args); - }, - VisitNew: function (eNode, context) { - // { type:NEW, executable:true, values: [] }; - var values = this.VisitArray(eNode.values, context); - if (values === eNode.values) - return eNode; - return $data.Expressions.ExpressionNodeTypes.NewExpressionNode.create(true, values); - }, - VisitJsonAssign: function (eNode, context) { - // { type:JSONASSIGN, executable:true, left: variable, right: right } - var left = this.Visit(eNode.left, context); - var right = this.Visit(eNode.right, context); - if (left === eNode.left && right === eNode.right) - return eNode; - left.JSONASSIGN = true; - right.JSONASSIGN = true; - return $data.Expressions.ExpressionNodeTypes.JsonAssignExpressionNode.create(true, left, right); - }, - VisitArrayAccess: function (eNode, context) { - // { type:ARRAYACCESS, executable:true, array:, index: } - var array = this.Visit(eNode.array, context); - var index = this.Visit(eNode.index, context); - if (array === eNode.array && index === eNode.index) - return eNode; - return $data.Expressions.ExpressionNodeTypes.ArrayAccessExpressionNode.create(true, array, index); - }, - VisitArray: function (eNodes, context) { - var args = []; - var ok = true; - for (var i = 0; i < eNodes.length; i++) { - args[i] = this.Visit(eNodes[i], context); - ok = ok && args[i] === eNodes[i]; - } - return ok ? eNodes : args; - }, - - VisitLiteral: function (eNode, context) { - return { type: eNode.type, executable: true, value: eNode.value, valueType: eNode.valueType }; - }, - VisitVariable: function (eNode, context) { - if (typeof context.paramContext[eNode.name] == undefined) // isn't param //TODO: check ParamContext - Guard.raise("Variable is not defined in the paramContext: " + eNode.name); - //this._setExecutable(eNode, true); - return $data.Expressions.ExpressionNodeTypes.VariableExpressionNode.create(true, "Math", "GLOBALOBJECT"); - }, - VisitMember: function (eNode, context) { - var chain = this.GetMemberChain(eNode); - var firstMember = chain[0].name; - var isLambdaParam = context.lambdaParams.indexOf(firstMember) >= 0; - var isLocalParam = firstMember == context.paramsName; //TODO: check ParamContext // old: typeof context.paramContext[firstMember] != "undefined"; - if (!isLocalParam && !isLambdaParam) - Guard.raise("Variable is not defined in the paramContext or the lambda parameters: " + firstMember); - - return $data.Expressions.ExpressionNodeTypes.MemberAccessExpressionNode.create(isLocalParam, eNode.expression, eNode.member); - } -}, null);$data.Class.define('$data.Expressions.ExecutorVisitor', $data.Expressions.ExpTreeVisitor, null, -{ - //-- - VisitVariable: function (eNode, context) { - if (!eNode.executable) - return eNode; - var value = (eNode.name == context.paramsName) ? context.paramContext : window[eNode.name]; - if (typeof value == 'undefined') - Guard.raise( - new Exception("Unknown variable in '" + context.operation + "' operation. The variable isn't referenced in the parameter context and it's not a global variable: '" + eNode.name + "'.", - "InvalidOperation", { operationName: context.operation, missingParameterName: eNode.name }) - ); - return $data.Expressions.ExpressionNodeTypes.LiteralExpressionNode.create(true, typeof value, value); - }, - VisitMember: function (eNode, context) { - if (!eNode.executable) - return eNode; - var chain = this.GetMemberChain(eNode); - var value; - for (var i = 0; i < chain.length; i++) { - if (i == 0) - value = context.paramContext; - else - value = value[chain[i].name]; - } - return $data.Expressions.ExpressionNodeTypes.LiteralExpressionNode.create(true, typeof value, value); - - - }, - VisitUnary: function (eNode, context) { - var operand = this.Visit(eNode.operand, context); - if (operand !== eNode.operand) - eNode = $data.Expressions.ExpressionNodeTypes.UnaryExpressionNode.create(eNode.executable, eNode.operator, operand); - if (!eNode.executable) - return eNode; - // executing and returning with result as a literal - var value; - var src; - var operandValue = ((operand.valueType == "string") ? ("'" + operand.value + "'") : operand.value); - src = "value = " + eNode.operator + " " + operandValue; - eval(src); - - return $data.Expressions.ExpressionNodeTypes.LiteralExpressionNode.create(true, typeof value, value); - }, - VisitIncDec: function (eNode, context) { - var operand = this.Visit(eNode.operand, context); - if (operand !== eNode.operand) - eNode = $data.Expressions.ExpressionNodeTypes.IncDecExpressionNode.create(eNode.executable, eNode.operator, operand, eNode.suffix); - if (!eNode.executable) - return eNode; - // executing and returning with result as a literal - var value; - if (eNode.suffix) - value = eNode.operator == "++" ? operand.value++ : operand.value--; - else - value = eNode.operator == "++" ? ++operand.value : --operand.value; - return $data.Expressions.ExpressionNodeTypes.LiteralExpressionNode.create(true, typeof value, value); - }, - VisitBinary: function (eNode, context) { - var left = this.Visit(eNode.left, context); - var right = this.Visit(eNode.right, context); - if (left !== eNode.left || right !== eNode.right) - eNode = $data.Expressions.ExpressionNodeTypes.BinaryExpressionNode.create(eNode.executable, eNode.operator, left, right); - if (!eNode.executable) - return eNode; - // executing and returning with result as a literal - var value; - var src; - var leftValue = ((left.valueType == "string") ? ("'" + left.value + "'") : left.value); - var rightValue = ((right.valueType == "string") ? ("'" + right.value + "'") : right.value); - src = "value = " + leftValue + " " + eNode.operator + " " + rightValue; - eval(src); - - return $data.Expressions.ExpressionNodeTypes.LiteralExpressionNode.create(true, typeof value, value); - }, - VisitEquality: function (eNode, context) { - var left = this.Visit(eNode.left, context); - var right = this.Visit(eNode.right, context); - if (left !== eNode.left || right !== eNode.right) - eNode = $data.Expressions.ExpressionNodeTypes.EqualityExpressionNode.create(eNode.executable, eNode.operator, left, right); - if (!eNode.executable) - return eNode; - // executing and returning with result as a literal - var value; - var src; - var leftValue = ((left.valueType == "string") ? ("'" + left.value + "'") : left.value); - var rightValue = ((right.valueType == "string") ? ("'" + right.value + "'") : right.value); - src = "value = " + leftValue + " " + eNode.operator + " " + rightValue; - eval(src); - return $data.Expressions.ExpressionNodeTypes.LiteralExpressionNode.create(true, typeof value, value); - }, - VisitDecision: function (eNode, context) { - var expression = this.Visit(eNode.expression, context); - var left = this.Visit(eNode.left, context); - var right = this.Visit(eNode.right, context); - if (expression !== eNode.expression || left !== eNode.left || right !== eNode.right) - eNode = $data.Expressions.ExpressionNodeTypes.DecisionExpressionNode.create(eNode.executable, expression, left, right); - if (!eNode.executable) - return eNode; - // executing and returning with result as a literal - var value = expression.value ? left.value : right.value; - return $data.Expressions.ExpressionNodeTypes.LiteralExpressionNode.create(true, typeof value, value); - }, - VisitMethodCall: function (eNode, context) { - var object = eNode.object ? this.Visit(eNode.object, context) : null; - var args = this.VisitArray(eNode.args, context); - if (object !== eNode.object || args != eNode.args) - eNode = $data.Expressions.ExpressionNodeTypes.MethodcallExpressionNode.create(eNode.executable, object, eNode.method, args); - if (!eNode.executable) - return eNode; - // executing and returning with result as a literal - var a = []; - for (var i = 0; i < args.length; i++) { - var arg = args[i]; - var t = typeof arg.value; - a.push((t == "string") ? ("'" + arg.value + "'") : arg.value); - } - var value; - var src = object ? - "value = object.value[eNode.method](" + a.join(",") + ");" - : - "value = " + eNode.method + "(" + a.join(",") + ");"; - eval(src); - - return $data.Expressions.ExpressionNodeTypes.LiteralExpressionNode.create(true, typeof value, value); - }, - VisitArrayAccess: function (eNode, context) { - // { type:ARRAYACCESS, executable:true, array:, index: } - var arrayNode = this.Visit(eNode.array, context); - var indexNode = this.Visit(eNode.index, context); - var value = arrayNode.value[indexNode.value]; - return $data.Expressions.ExpressionNodeTypes.LiteralExpressionNode.create(true, typeof value, value); - } -}, null); $data.Class.define('$data.Expressions.ExpressionBuilder', null, null, -{ - constructor: function (context) { - this.context = context; - }, - _isLambdaParam: function (name) { - var p = this.context.lambdaParams; - for (var i = 0; i < p.length; i++) { - if (p[i] == name) - return true; - } - return false; - }, - _isParam: function (name) { - return this.context.paramContext[name] != undefined; - }, - _isParamRoot: function (name) { - return this.context.paramsName == name; - }, - Build: function (node, expNode) { - var n; - switch (node.arity) { - case "infix": - if ("(" == node.value) - n = this.BuildMethodCall(node, expNode); - else if ("." == node.value) - n = this.BuildMember(node, expNode); - else if (["===", "==", "!==", "!=", ">", "<", ">=", "<="].indexOf(node.value) >= 0) - n = this.BuildEquality(node, expNode); - else if (["&&", "||"].indexOf(node.value) >= 0) - n = this.BuildBinary(node, expNode); - else if (["+", "-", "*", "/", "%"].indexOf(node.value) >= 0) - n = this.BuildBinary(node, expNode); - else if ("[" == node.value) - n = this.BuildArrayAccess(node, expNode); - else - Guard.raise("Value of infix node isn't implemented: " + node.value); - break; - case "prefix": - if (["+", "-", "!"].indexOf(node.value) >= 0) - n = this.BuildUnary(node, expNode); - else if (["++", "--"].indexOf(node.value) >= 0) - n = this.BuildIncDec(node, expNode); - else if ("{" == node.value/* && "object" == node.type*/) //TODO: check the second condition necessity - n = this.BuildNewExpression(node, expNode); - else - Guard.raise("Value of prefix node isn't implemented: " + node.value); - break; - case "suffix": - if (["++", "--"].indexOf(node.value) >= 0) - n = this.BuildIncDec(node, expNode); - else - Guard.raise("Value of suffix node isn't implemented: " + node.value); - break; - case "string": - case "number": - n = this.BuildLiteral(node, expNode); //TODO: more arity to literal? - break; - case "ternary": - if (node.value == "?") - n = this.BuildDecision(node, expNode); - else - Guard.raise("Value of ternary node isn't implemented: " + node.value); - break; - case null: - case undefined: - if (node.type == "boolean" && (node.value == "true" || node.value == "false")) - n = this.BuildBoolLiteral(node, expNode); - else - n = this.BuildVariable(node, expNode); - break; - default: - Guard.raise("Arity isn't implemented: " + node.arity); - } - return n; - }, - BuildNewExpression: function (node, expNode) { - var newExpression = $data.Expressions.ExpressionNodeTypes.NewExpressionNode.create(true, []); - var n = node.first; - for (var i = 0; i < n.length; i++) - newExpression.values.push(this.Build(n[i], newExpression)); - return newExpression; - }, - BuildLiteral: function (node, expNode) { - return $data.Expressions.ExpressionNodeTypes.LiteralExpressionNode.create(true, node.arity, node.value); - }, - BuildBoolLiteral: function (node, expNode) { - return $data.Expressions.ExpressionNodeTypes.LiteralExpressionNode.create(true, node.type, node.value == "true" ? true : false); - }, - BuildVariable: function (node, expNode) { - if (!node.first) { - if (expNode.type == MEMBERACCESS) { - var subType; - if (this._isLambdaParam(node.value)) - subType = "LAMBDAPARAM"; - else if (this._isParamRoot(node.value)) - subType = "PARAMETERROOT"; - else if (this._isParam(node.value)) - subType = "PARAMETER"; - else - subType = "PROPERTY"; - } - else { - if (this._isLambdaParam(node.value)) - subType = "LAMBDAPARAM"; - else if (this._isParamRoot(node.value)) - subType = "PARAMETERROOT"; - else if (this._isParam(node.value)) - subType = "PARAMETER"; - else if (window[node.value] != undefined) - subType = "GLOBALOBJECT"; - else - Guard.raise( - new Exception("Unknown variable in '" + this.context.operation + "' operation. The variable isn't referenced in the parameter context and it's not a global variable: '" + node.value + "'.", - "InvalidOperation", { operationName: this.context.operation, missingParameterName: node.value }) - ); - } - return $data.Expressions.ExpressionNodeTypes.VariableExpressionNode.create(true, node.value, subType); - } - - var left = $data.Expressions.ExpressionNodeTypes.LiteralExpressionNode.create(true, "name", node.value); - - var jsonAssign = $data.Expressions.ExpressionNodeTypes.JsonAssignExpressionNode.create(true); - var right = this.Build(node.first, jsonAssign); - //left.parent = jsonAssign; - jsonAssign.left = left; - jsonAssign.right = right; - - left.JSONASSIGN = true; - right.JSONASSIGN = true; - - return jsonAssign; - }, - BuildMember: function (node, expNode) { - if (node.value != "." || node.arity != "infix") { - if (node.type == "string") { //TODO: more types? - return $data.Expressions.ExpressionNodeTypes.LiteralExpressionNode.create(true, node.arity, node.value); - } - return $data.Expressions.ExpressionNodeTypes.MemberAccessExpressionNode.create(true, null, node.value); - } - var result = $data.Expressions.ExpressionNodeTypes.MemberAccessExpressionNode.create(true); - var expression = this.Build(node.first, result); - var member = this.Build(node.second, result); - result.expression = expression; - result.member = member; - return result; - }, - BuildUnary: function (node, expNode) { - var result = $data.Expressions.ExpressionNodeTypes.UnaryExpressionNode.create(true, node.value); - result.operand = this.Build(node.first, result); - return result; - }, - BuildIncDec: function (node, expNode) { - var result = $data.Expressions.ExpressionNodeTypes.IncDecExpressionNode.create(true, node.value, null, node.arity == "suffix"); - result.operand = this.Build(node.first, result); - return result; - }, - BuildBinary: function (node, expNode) { - if (!node.first) Guard.raise("Cannot build binary: node.first is null"); - if (!node.second) Guard.raise("Cannot build binary: node.second is null"); - var result = $data.Expressions.ExpressionNodeTypes.BinaryExpressionNode.create(true, node.value); - result.left = this.Build(node.first, result); - result.right = this.Build(node.second, result); - return result; - }, - BuildEquality: function (node, expNode) { - var result = $data.Expressions.ExpressionNodeTypes.EqualityExpressionNode.create(true, node.value); - result.left = this.Build(node.first, result); - result.right = this.Build(node.second, result); - return result; - }, - BuildDecision: function (node, expNode) { - var result = $data.Expressions.ExpressionNodeTypes.DecisionExpressionNode.create(true); - result.expression = this.Build(node.first, result); - result.left = this.Build(node.second, result); - result.right = this.Build(node.third, result); - return result; - }, - BuildMethodCall: function (node, expNode) { - var result = $data.Expressions.ExpressionNodeTypes.MethodcallExpressionNode.create(true); - if (node.first.type == "function") { - //-- object's function - result.object = this.Build(node.first.first, result); - result.method = node.first.second.value; - } - else { - //-- global function - if (node.first.type != null) - Guard.raise("Cannot build MethodCall because type is " + type); - result.object = null; - result.method = node.first.value; - } - var argNodes = node.second; - var args = []; - for (var i = 0; i < argNodes.length; i++) { - var arg = argNodes[i]; - args[i] = this.Build(arg, result); - } - result.args = args; - return result; - }, - BuildArrayAccess: function (node, expNode) { - // { type:ARRAYACCESS, executable:true, array:, index: } - var result = $data.Expressions.ExpressionNodeTypes.ArrayAccessExpressionNode.create(true); - result.array = this.Build(node.first, result); - result.index = this.Build(node.second, result); - return result; - } -}, null);$C('$data.Expressions.AssociationInfoExpression', $data.Expressions.ExpressionNode, null, { - constructor: function (associationInfo) { - this.associationInfo = associationInfo; - }, - nodeType: { value: $data.Expressions.ExpressionType.AssociationInfo, enumerable: true } -});$C('$data.Expressions.CodeExpression', $data.Expressions.ExpressionNode, null, { - constructor: function (source, parameters) { - if (Container.resolveType(Container.getTypeName(source)) == $data.String && source.replace(/^[\s\xA0]+/, "").match("^function") != "function") { - source = "function (it) { return " + source + "; }"; - } - - this.source = source; - this.parameters = parameters; - }, - nodeType: { value: $data.Expressions.ExpressionType.Code, enumerable: true } -});$C('$data.Expressions.CodeToEntityConverter', $data.Expressions.ExpressionVisitor, null, { - constructor: function (scopeContext) { - ///This visitor converts a JS language tree into a semantical Entity Expression Tree This visitor should be invoked on a CodeExpression - ///context.thisArg contains parameters, context.lambdaParams should have an array value - this.scopeContext = scopeContext; - this.parameters = []; - - }, - - - VisitBinary: function (expression, context) { - var left = this.Visit(expression.left, context); - var right = this.Visit(expression.right, context); - - if ((!(left instanceof $data.Expressions.ConstantExpression) && right instanceof $data.Expressions.ConstantExpression) || - (!(right instanceof $data.Expressions.ConstantExpression) && left instanceof $data.Expressions.ConstantExpression)) { - - var refExpression, constExpr; - if (right instanceof $data.Expressions.ConstantExpression) { - refExpression = left; - constExpr = right; - } else { - refExpression = right; - constExpr = left; - } - - var memInfo; - if ((memInfo = refExpression.selector) instanceof $data.Expressions.MemberInfoExpression || - (memInfo = refExpression.operation) instanceof $data.Expressions.MemberInfoExpression) { - - - if (memInfo.memberDefinition && (memInfo.memberDefinition.type || memInfo.memberDefinition.dataType)) { - var fieldType = Container.resolveType(memInfo.memberDefinition.type || memInfo.memberDefinition.dataType); - var constExprType = Container.resolveType(constExpr.type); - - if (fieldType !== constExprType) { - - var value = constExpr.value; - if (expression.operator === $data.Expressions.ExpressionType.In) { - if (Array.isArray(value)) { - var resultExp = []; - for (var i = 0; i < value.length; i++) { - resultExp.push(new $data.Expressions.ConstantExpression(value[i], fieldType)); - } - value = resultExp; - fieldType = $data.Array; - } else { - fieldType = constExprType; - } - } - - if (right === constExpr) { - right = new $data.Expressions.ConstantExpression(value, fieldType, right.name); - } else { - left = new $data.Expressions.ConstantExpression(value, fieldType, left.name); - } - } - } - } - } - - var operatorResolution = this.scopeContext.resolveBinaryOperator(expression.nodeType, expression, context.frameType); - var result = Container.createSimpleBinaryExpression(left, right, expression.nodeType, expression.operator, expression.type, operatorResolution); - return result; - }, - - VisitUnary: function (expression, context) { - var operand = this.Visit(expression.operand, context); - var operatorResolution = this.scopeContext.resolveUnaryOperator(expression.nodeType, expression, context.frameType); - var result = Container.createUnaryExpression(operand, expression.operator, expression.nodeType, operatorResolution); - return result; - }, - - VisitParameter: function (expression, context) { - Guard.requireValue("context", context); - var et = $data.Expressions.ExpressionType; - switch (expression.nodeType) { - case et.LambdaParameterReference: - var result = Container.createEntityExpression(context.lambdaParameters[expression.paramIndex], { lambda: expression.name }); - return result; - case et.LambdaParameter: - //TODO: throw descriptive exception or return a value - break; - default: - Guard.raise("Global parameter " + expression.name + " not found. For query parameters use 'this.field' notation"); - break; - } - }, - - VisitThis: function (expression, context) { - ///converts the ThisExpression into a QueryParameterExpression tha't value will be evaluated and stored in this.parameters collection - var index = this.parameters.push({ name: "", value: undefined }) - 1; - var result = Container.createQueryParameterExpression("", index, context.queryParameters, undefined); - return result; - }, - - VisitFunction: function (expression, context) { - var result = $data.Expressions.ExpressionVisitor.prototype.VisitFunction.apply(this, arguments); - return result.body; - }, - - VisitCall: function (expression, context) { - //var exp = this.Visit(expression.expression); - var self = this; - var exp = this.Visit(expression.expression, context); - var member = this.Visit(expression.member, context); - var args = expression.args.map(function (arg) { - return self.Visit(arg, context); - }); - var result; - - ///filter=>function(p) { return p.Title == this.xyz.BogusFunction('asd','basd');} - switch (true) { - case exp instanceof $data.Expressions.QueryParameterExpression: - var argValues = args.map(function (a) { return a.value; }); - result = expression.implementation(exp.value, member.value, argValues); - //var args = expressions - return Container.createQueryParameterExpression(exp.name + "$" + member.value, exp.index, result, typeof result); - case exp instanceof $data.Expressions.EntityFieldExpression: - - case exp instanceof $data.Expressions.EntityFieldOperationExpression: - var operation = this.scopeContext.resolveFieldOperation(member.value, exp, context.frameType); - if (!operation) { - Guard.raise("Unknown entity field operation: " + member.getJSON()); - } - member = Container.createMemberInfoExpression(operation); - result = Container.createEntityFieldOperationExpression(exp, member, this._resolveFunctionArguments(args, operation.parameters)); - return result; - - case exp instanceof $data.Expressions.EntitySetExpression: - var operation = this.scopeContext.resolveSetOperations(member.value, exp, context.frameType); - if (!operation) { - Guard.raise("Unknown entity field operation: " + member.getJSON()); - } - member = Container.createMemberInfoExpression(operation); - result = Container.createFrameOperationExpression(exp, member, this._resolveFunctionArguments(args, operation.parameters)); - return result; - - case exp instanceof $data.Expressions.EntityExpression: - var operation = this.scopeContext.resolveTypeOperations(member.value, exp, context.frameType); - if (!operation) { - Guard.raise("Unknown entity function operation: " + member.getJSON()); - } - - member = Container.createMemberInfoExpression(operation); - result = Container.createEntityFunctionOperationExpression(exp, member, this._resolveFunctionArguments(args, operation.method.params)); - return result; - break; - case exp instanceof $data.Expressions.EntityContextExpression: - var operation = this.scopeContext.resolveContextOperations(member.value, exp, context.frameType); - if (!operation) { - Guard.raise("Unknown entity function operation: " + member.getJSON()); - } - - member = Container.createMemberInfoExpression(operation); - result = Container.createContextFunctionOperationExpression(exp, member, this._resolveFunctionArguments(args, operation.method.params)); - return result; - break; - default: - Guard.raise("VisitCall: Only fields can have operations: " + expression.getType().name); - //TODO we must not alter the visited tree - } - - }, - _resolveFunctionArguments: function (args, params) { - if (params) // remove current field poz - params = params.filter(function (p, i) { return p.name !== '@expression'; }); - - //objectArgs - if (args.length === 1 && args[0] instanceof $data.Expressions.ConstantExpression && typeof args[0].value === 'object' && args[0].value && params && params[0] && - args[0].value.constructor === $data.Object && params.some(function (param) { return param.name in args[0].value })) { - - return params.map(function (p) { - var type = p.type || p.dataType || args[0].type; - return new $data.Expressions.ConstantExpression(args[0].value[p.name], Container.resolveType(type), p.name); - }); - - } else { - return args.map(function (expr, i) { - if (expr instanceof $data.Expressions.ConstantExpression && params && params[i]) { - var type = params[i].type || params[i].dataType || expr.type; - return new $data.Expressions.ConstantExpression(expr.value, Container.resolveType(type), params[i].name); - } else { - return expr; - } - }); - } - }, - - VisitProperty: function (expression, context) { - /// - var exp = this.Visit(expression.expression, context); - var member = this.Visit(expression.member, context); - - //Guard.requireType("member", member, $data.Expressions.ConstantExpression); - Guard.requireType("member", member, $data.Expressions.ConstantExpression); - - function isPrimitiveType(memberDefinitionArg) { - - var t = memberDefinitionArg.dataType; - if (typeof t === 'function') { return false; } - - // suspicious code - /*switch (t) { - //TODO: implement this - }*/ - } - - switch (exp.expressionType) { - case $data.Expressions.EntityExpression: - var memberDefinition = exp.getMemberDefinition(member.value); - if (!memberDefinition) { - Guard.raise(new Exception("Unknown member: " + member.value, "MemberNotFound")); - } - //var storageMemberDefinition = - var storageField = memberDefinition.storageModel - .PhysicalType.memberDefinitions.getMember(memberDefinition.name); - var res; - var memberDefinitionExp; - switch (storageField.kind) { - case "property": - memberDefinitionExp = Container.createMemberInfoExpression(memberDefinition); - res = Container.createEntityFieldExpression(exp, memberDefinitionExp); - return res; - case "navProperty": - var assocInfo = memberDefinition.storageModel.Associations[memberDefinition.name]; - var setExpression = Container.createEntitySetExpression(exp, Container.createAssociationInfoExpression(assocInfo)); - if (assocInfo.ToMultiplicity !== "*") { - var ee = Container.createEntityExpression(setExpression, {}); - return ee; - }/* else { - context.lambdaParameters.push(setExpression); - }*/ - return setExpression; - case "complexProperty": - memberDefinitionExp = Container.createMemberInfoExpression(memberDefinition); - res = Container.createComplexTypeExpression(exp, memberDefinitionExp); - return res; - //TODO: missing default case - } - - //s/switch => property or navigationproperty - case $data.Expressions.ComplexTypeExpression: - var memDef = exp.getMemberDefinition(member.value); - if (!(memDef)) { - Guard.raise("Unknown member " + member.value + " on " + exp.entityType.name); - } - var memDefExp = Container.createMemberInfoExpression(memDef); - var result; - //TODO!!!! - if (Container.isPrimitiveType(Container.resolveType(memDef.dataType))) { - result = Container.createEntityFieldExpression(exp, memDefExp); - return result; - } - result = Container.createComplexTypeExpression(exp, memDefExp); - return result; - case $data.Expressions.QueryParameterExpression: - var value = expression.implementation(exp.value, member.value); - this.parameters[exp.index].name += "$" + member.value; - this.parameters[exp.index].value = value; - return Container.createQueryParameterExpression(exp.name + "$" + member.value, exp.index, value, Container.getTypeName(value)); - case $data.Expressions.EntityFieldExpression: - case $data.Expressions.EntityFieldOperationExpression: - var operation = this.scopeContext.resolveFieldOperation(member.value, exp, context.frameType); - if (!operation) { - Guard.raise("Unknown entity field operation: " + member.getJSON()); - } - member = Container.createMemberInfoExpression(operation); - result = Container.createEntityFieldOperationExpression(exp, member, []); - - return result; - default: - Guard.raise("Unknown expression type to handle: " + exp.expressionType.name); - } - } -});$C('$data.Expressions.ComplexTypeExpression', $data.Expressions.ExpressionNode, null, { - constructor: function (source, selector) { - /// - /// - /// - /// - /// - /// - /// - /// - Guard.requireType("source", source, [$data.Expressions.EntityExpression, $data.Expressions.ComplexTypeExpression]); - Guard.requireType("selector", selector, [$data.Expressions.EntityExpression, $data.Expressions.MemberInfoExpression]); - this.source = source; - this.selector = selector; - var dt = source.entityType.getMemberDefinition(selector.memberName).dataType; - var t = Container.resolveType(dt); - this.entityType = t; - }, - - getMemberDefinition: function (name) { - return this.entityType.getMemberDefinition(name); - }, - - nodeType: { value: $data.Expressions.ExpressionType.Com } -}); - -$C('$data.Expressions.EntityContextExpression', $data.Expressions.ExpressionNode, null, { - constructor: function (instance) { - /// - //Object.defineProperty(this, "instance", { value: instance, enumerable: false }); - this.instance = instance; - //this.storage_type = {}; - //this.typeName = this.type.name; - }, - instance: { enumerable: false }, - nodeType : { value: $data.Expressions.ExpressionType.EntityContext, enumerable: true } - -}); -$C('$data.Expressions.EntityExpression', $data.Expressions.ExpressionNode, null, { - constructor: function (source, selector) { - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - Guard.requireValue("source", source); - Guard.requireValue("selector", selector); - if (!(source instanceof $data.Expressions.EntitySetExpression) && !(source instanceof $data.Expressions.ServiceOperationExpression)) { - Guard.raise("Only EntitySetExpressions can be the source for an EntityExpression"); - } - - this.source = source; - this.selector = selector; - - this.entityType = this.source.elementType; - this.storageModel = this.source.storageModel; - - Guard.requireValue("entityType", this.entityType); - Guard.requireValue("storageModel", this.storageModel); - - }, - - getMemberDefinition: function (name) { - var memdef = this.entityType.getMemberDefinition(name); - if (!(memdef)) { - Guard.raise(new Exception("Unknown member " + name + " on type "+ this.entityType.name, "MemberNotFound")); - }; - memdef.storageModel = this.storageModel; - return memdef; - }, - - nodeType: { value: $data.Expressions.ExpressionType.Entity } -});$C('$data.Expressions.EntityExpressionVisitor', null, null, { - - constructor: function () { - this.lambdaTypes = []; - }, - - canVisit: function (expression) { - return expression instanceof $data.Expressions.ExpressionNode; - }, - - Visit: function (expression, context) { - if (!this.canVisit(expression)) - return expression; - - var visitorName = "Visit" + expression.getType().name; - if (visitorName in this) { - var fn = this[visitorName]; - var result = fn.call(this, expression, context); - if (typeof result === 'undefined') { - return expression; - } - return result; - } - //console.log("unhandled expression type:" + expression.getType().name); - return expression; - }, - VisitToArrayExpression: function (expression, context) { - var source = this.Visit(expression.source, context); - if (source !== expression.source) { - return Container.createToArrayExpression(source); - } - return expression; - }, - VisitForEachExpression: function (expression, context) { - var source = this.Visit(expression.source, context); - if (source !== expression.source) { - return Container.createForEachExpression(source); - } - return expression; - }, - VisitMemberInfoExpression: function (expression, context) { - return expression; - }, - - VisitSingleExpression: function (expression, context) { - var source = this.Visit(expression.source, context); - if (source !== expression.source) - return Container.createSingleExpression(source); - return expression; - }, - - VisitFirstExpression: function (expression, context) { - var source = this.Visit(expression.source, context); - if (source !== expression.source) - return Container.createFirstExpression(source); - return expression; - }, - - VisitSomeExpression: function (expression, context) { - var source = this.Visit(expression.source, context); - if (source !== expression.source) - return Container.createSomeExpression(source); - return expression; - }, - - VisitFindExpression: function (expression, context) { - var source = this.Visit(expression.source, context); - if (source !== expression.source) - return Container.createFindExpression(source); - return expression; - }, - - VisitEveryExpression: function (expression, context) { - var source = this.Visit(expression.source, context); - if (source !== expression.source) - return Container.createEveryExpression(source); - return expression; - }, - - VisitCountExpression: function (expression, context) { - var source = this.Visit(expression.source, context); - if (source !== expression.source) - return Container.createCountExpression(source); - return expression; - }, - - VisitBatchDeleteExpression: function (expression, context) { - var source = this.Visit(expression.source, context); - if (source !== expression.source) { - return Container.createBatchDeleteExpression(source); - } - return expression; - }, - - VisitObjectLiteralExpression: function (expression, context) { - var newValues = expression.members.map(function (ofe) { - return this.Visit(ofe, context); - }, this); - var equal = true; - for (var i = 0; i < expression.members.length; i++) { - equal = equal && (expression.members[i] === newValues[i]); - } - if (!equal) { - return Container.createObjectLiteralExpression(newValues); - } - return expression; - }, - VisitObjectFieldExpression: function (expression, context) { - var newExpression = this.Visit(expression.expression, context); - if (expression.expression !== newExpression) { - return Container.createObjectFieldExpression(expression.fieldName, newExpression); - } - return expression; - }, - VisitIncludeExpression: function (expression, context) { - var newExpression = this.Visit(expression.source, context); - if (newExpression !== expression.source) { - return Container.createIncludeExpression(newExpression, expression.selector); - } - return expression; - }, - - VisitUnaryExpression: function(expression, context) { - - /// - /// - var operand = this.Visit(expression.operand, context); - if (expression.operand !== operand) { - return Container.createUnaryExpression(operand, expression.operator, expression.nodeType, expression.resolution); - }; - return expression; - }, - - VisitSimpleBinaryExpression: function (expression, context) { - /// - /// - /// - // - var left = this.Visit(expression.left, context); - var right = this.Visit(expression.right, context); - if (left !== expression.left || right !== expression.right) { - return new $data.Expressions.SimpleBinaryExpression(left, right, expression.nodeType, - expression.operator, expression.type, expression.resolution); - } - return expression; - }, - - VisitEntityContextExpression: function (expression, context) { - return expression; - }, - - VisitCodeExpression: function (expression, context) { - /// - /// - /// - return expression; - }, - - VisitComplexTypeExpression: function (expression, context) { - var source = this.Visit(expression.source, context); - var selector = this.Visit(expression.selector, context); - if (source !== expression.source || selector !== expression.selector) { - var result = Container.createComplexTypeExpression(source, selector); - return result; - } - return expression; - }, - - VisitEntityExpression: function (expression, context) { - var source = this.Visit(expression.source, context); - var selector = this.Visit(expression.selector, context); - if (source !== expression.source || selector !== expression.selector) { - var result = Container.createEntityExpression(source, selector); - return result; - } - return expression; - }, - - VisitEntityFieldExpression: function (expression, context) { - var source = this.Visit(expression.source, context); - var selector = this.Visit(expression.selector, context); - if (source !== expression.source || selector !== expression.selector) { - var result = Container.createEntityFieldExpression(source, selector); - return result; - } - return expression; - }, - - VisitEntityFieldOperationExpression: function (expression, context) { - var source = this.Visit(expression.source, context); - var operation = this.Visit(expression.operation, context); - var parameters = expression.parameters.map(function (p) { - return this.Visit(p); - }, this); - var result = Container.createEntityFieldOperationExpression(source, operation, parameters); - return result; - }, - - VisitParametricQueryExpression: function (expression, context) { - var exp = this.Visit(expression.expression, context); - var args = expression.parameters.map(function (p) { - return this.Visit(p); - }, this); - var result = Container.createParametricQueryExpression(exp, args); - return result; - }, - - VisitEntitySetExpression: function (expression, context) { - var source = this.Visit(expression.source, context); - var selector = this.Visit(expression.selector, context); - if (source !== expression.source || selector !== expression.selector) { - return Container.createEntitySetExpression(source, selector, expression.params, expression.instance); - } - return expression; - }, - - VisitInlineCountExpression: function (expression, context) { - var source = this.Visit(expression.source, context); - var selector = this.Visit(expression.selector, context); - if (source !== expression.source || selector !== expression.selector) { - return Container.createInlineCountExpression(source, selector, expression.params, expression.instance); - } - return expression; - }, - - VisitFilterExpression: function (expression, context) { - var source = this.Visit(expression.source, context); - var selector = this.Visit(expression.selector, context); - if (source !== expression.source || selector !== expression.selector) { - return Container.createFilterExpression(source, selector, expression.params, expression.instance); - } - return expression; - }, - - VisitProjectionExpression: function (expression, context) { - var source = this.Visit(expression.source, context); - var selector = this.Visit(expression.selector, context); - if (source !== expression.source || selector !== expression.selector) { - var expr = Container.createProjectionExpression(source, selector, expression.params, expression.instance); - expr.projectionAs = expression.projectionAs; - return expr; - } - return expression; - }, - - VisitOrderExpression: function (expression, context) { - var source = this.Visit(expression.source, context); - var selector = this.Visit(expression.selector, context); - if (source !== expression.source || selector !== expression.selector) { - return Container.createOrderExpression(source, selector, expression.nodeType); - } - return expression; - }, - VisitPagingExpression: function (expression, context) { - var source = this.Visit(expression.source, context); - var amount = this.Visit(expression.amount, context); - if (source !== expression.source || amount !== expression.amount) { - return Container.createPagingExpression(source, amount, expression.nodeType); - } - return expression; - } -}); -$C('$data.Expressions.ExpressionMonitor', $data.Expressions.EntityExpressionVisitor, null, { - constructor: function (monitorDefinition) { - this.Visit = function (expression, context) { - - var result = expression; - var methodName; - if (this.canVisit(expression)) { - - //if (monitorDefinition.FilterExpressionNode) { - - //}; - - if (monitorDefinition.VisitExpressionNode) { - monitorDefinition.VisitExpressionNode.apply(monitorDefinition, arguments); - }; - - methodName = "Visit" + expression.getType().name; - if (methodName in monitorDefinition) { - result = monitorDefinition[methodName].apply(monitorDefinition, arguments); - } - } - - - //apply is about 3-4 times faster then call on webkit - - var args = arguments; - if (result !== expression) args = [result, context]; - result = $data.Expressions.EntityExpressionVisitor.prototype.Visit.apply(this, args); - - args = [result, context]; - - if (this.canVisit(result)) { - var expressionTypeName = result.getType().name; - if (monitorDefinition.MonitorExpressionNode) { - monitorDefinition.MonitorExpressionNode.apply(monitorDefinition, args); - } - methodName = "Monitor" + expressionTypeName; - if (methodName in monitorDefinition) { - monitorDefinition[methodName].apply(monitorDefinition, args); - } - - if (monitorDefinition.MutateExpressionNode) { - monitorDefinition.MutateExpressionNode.apply(monitorDefinition, args); - } - methodName = "Mutate" + expressionTypeName; - if (methodName in monitorDefinition) { - result = monitorDefinition[methodName].apply(monitorDefinition, args); - } - - } - return result; - }; - } -});$C('$data.Expressions.EntityFieldExpression', $data.Expressions.ExpressionNode, null, { - constructor: function (source, selector) { - /// - /// - this.selector = selector; - this.source = source; - - - if (this.selector instanceof $data.Expressions.MemberInfoExpression || this.selector.name) { - this.memberName = this.selector.name; - } - }, - - nodeType: { value: $data.Expressions.ExpressionType.EntityField } -}); - -$C('$data.Expressions.EntityFieldOperationExpression', $data.Expressions.ExpressionNode, null, { - constructor: function (source, operation, parameters) { - this.source = source; - this.operation = operation; - this.parameters = parameters; - }, - nodeType: { value: $data.Expressions.ExpressionType.EntityFieldOperation } - -});$C('$data.Expressions.EntitySetExpression', $data.Expressions.ExpressionNode, null, { - constructor: function (source, selector, params, instance) { - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - Guard.requireType("source", source, - [$data.Expressions.EntityContextExpression, $data.Expressions.EntitySetExpression]); - Guard.requireType("selector", source, - [$data.Expressions.MemberInfoExpression, $data.Expressions.CodeExpression, $data.Expressions.ParametricQueryExpression]); - - this.source = source; - this.selector = selector; - this.params = params; - //Object.defineProperty(this, "instance", { value: instance, enumerable: false, writable: true }); - this.instance = instance; - - function findContext() { - //TODO: use source from function parameter and return a value at the end of the function - var r = source; - while (r) { - if (r instanceof $data.Expressions.EntityContextExpression) { - return r; - } - r = r.source; - } - } - - ///TODO!!! - this.storage_type = {}; - var c = findContext(); - switch (true) { - case this.source instanceof $data.Expressions.EntityContextExpression: - Guard.requireType("selector", selector, $data.Expressions.MemberInfoExpression); - this.elementType = selector.memberDefinition.elementType; - this.storageModel = c.instance._storageModel.getStorageModel(this.elementType); - break; - case this.source instanceof $data.Expressions.EntityExpression: - Guard.requireType("selector", selector, $data.Expressions.AssociationInfoExpression); - this.elementType = selector.associationInfo.ToType; - this.storageModel = c.instance._storageModel.getStorageModel(this.elementType); - break; - case this.source instanceof $data.Expressions.EntitySetExpression: - this.elementType = this.source.elementType; - this.storageModel = this.source.storageModel; - break; - case this.source instanceof $data.Expressions.ServiceOperationExpression: - this.elementType = this.source.elementType;//????????? - this.storageModel = this.source.storageModel; - break; - default: - Guard.raise("take and skip must be the last expressions in the chain!"); - //Guard.raise("Unknown source type for EntitySetExpression: " + this.getType().name); - break; - } - - // suspicious code - /*if (this.source instanceof $data.Expressions.EntitySetExpression) { - //TODO: missing operation - }*/ - //EntityTypeInfo - - }, - instance: { enumerable: false }, - nodeType: { value: $data.Expressions.ExpressionType.EntitySet, enumerable: true } -}); -$C('$data.Expressions.FrameOperationExpression', $data.Expressions.ExpressionNode, null, { - constructor: function (source, operation, parameters) { - this.source = source; - this.operation = operation; - this.parameters = parameters; - }, - nodeType: { value: $data.Expressions.ExpressionType.FrameOperation } - -}); - -$C('$data.Expressions.EntityFunctionOperationExpression', $data.Expressions.FrameOperationExpression, null, { - nodeType: { value: $data.Expressions.ExpressionType.EntityFunctionOperation } -}); - -$C('$data.Expressions.ContextFunctionOperationExpression', $data.Expressions.FrameOperationExpression, null, { - nodeType: { value: $data.Expressions.ExpressionType.ContextFunctionOperation } -}); -$C('$data.Expressions.FilterExpression', $data.Expressions.EntitySetExpression, null, { - constructor: function (source, selector) { - /// - /// - /// - /// - /// - /// - /// - /// - this.resultType = $data.Array; - }, - nodeType: { value: $data.Expressions.ExpressionType.Filter, enumerable: true } -}); - -$C('$data.Expressions.InlineCountExpression', $data.Expressions.EntitySetExpression, null, { - constructor: function (source, selector) { - }, - nodeType: { value: $data.Expressions.ExpressionType.InlineCount, enumerable: true } -}); - -$C('$data.Expressions.FrameOperator', $data.Expressions.ExpressionNode, null, { - constructor: function () { - this.isTerminated = true; - } -}); - -$C('$data.Expressions.CountExpression', $data.Expressions.FrameOperator, null, { - constructor: function (source) { - /// - /// - /// - this.source = source; - this.resultType = $data.Integer; - }, - nodeType: { value: $data.Expressions.ExpressionType.Count, enumerable: true } -}); - -$C('$data.Expressions.SingleExpression', $data.Expressions.FrameOperator, null, { - constructor: function (source) { - /// - /// - /// - this.source = source; - this.resultType = $data.Object; - }, - nodeType: { value: $data.Expressions.ExpressionType.Single, enumerable: true } -}); - -$C('$data.Expressions.FindExpression', $data.Expressions.FrameOperator, null, { - constructor: function (source, params) { - /// - /// - /// - this.source = source; - this.params = params; - this.resultType = $data.Object; - }, - nodeType: { value: $data.Expressions.ExpressionType.Find, enumerable: true } -}); - -$C('$data.Expressions.FirstExpression', $data.Expressions.FrameOperator, null, { - constructor: function (source) { - /// - /// - /// - this.source = source; - this.resultType = $data.Object; - }, - nodeType: { value: $data.Expressions.ExpressionType.First, enumerable: true } -}); - -$C('$data.Expressions.ForEachExpression', $data.Expressions.FrameOperator, null, { - constructor: function (source) { - /// - /// - /// - this.source = source; - this.resultType = $data.Array; - }, - nodeType: { value: $data.Expressions.ExpressionType.ForEach, enumerable: true } -}); -$C('$data.Expressions.ToArrayExpression', $data.Expressions.FrameOperator, null, { - constructor: function (source) { - /// - /// - /// - this.source = source; - this.resultType = $data.Array; - }, - nodeType: { value: $data.Expressions.ExpressionType.ToArray, enumerable: true } -}); - -$C('$data.Expressions.SomeExpression', $data.Expressions.FrameOperator, null, { - constructor: function (source) { - /// - /// - /// - this.source = source; - this.resultType = $data.Object; - }, - nodeType: { value: $data.Expressions.ExpressionType.Some, enumerable: true } -}); - -$C('$data.Expressions.EveryExpression', $data.Expressions.FrameOperator, null, { - constructor: function (source) { - /// - /// - /// - this.source = source; - this.resultType = $data.Object; - }, - nodeType: { value: $data.Expressions.ExpressionType.Every, enumerable: true } -}); - -$C('$data.Expressions.BatchDeleteExpression', $data.Expressions.FrameOperator, null, { - constructor: function (source) { - /// - /// - /// - this.source = source; - this.resultType = $data.Integer; - }, - nodeType: { value: $data.Expressions.ExpressionType.BatchDelete, enumerable: true } -});$C('$data.Expressions.IncludeExpression', $data.Expressions.EntitySetExpression, null, { - constructor: function (source, selector) { - }, - nodeType: { value: $data.Expressions.ExpressionType.Include, writable: true }, - - toString: function (debug) { - //var result; - //result = debug ? this.type + " " : ""; - //result = result + this.name; - var result = "unimplemented"; - return result; - } -}, null); -$C('$data.Expressions.MemberInfoExpression', $data.Expressions.ExpressionNode, null, { - constructor: function (memberDefinition) { - this.memberDefinition = memberDefinition; - this.memberName = memberDefinition.name; - }, - nodeType: { value: $data.Expressions.ExpressionType.MemberInfo, enumerable: true } - -});$C('$data.Expressions.OrderExpression', $data.Expressions.EntitySetExpression, null, { - constructor: function (source, expression, nType) { - /// - /// - //this.source = source; - //this.selector = expression; - this.nodeType = nType; - }, - nodeType: { value: $data.Expressions.ExpressionType.OrderBy, writable: true }, - - toString: function (debug) { - //var result; - //result = debug ? this.type + " " : ""; - //result = result + this.name; - var result = "unimplemented"; - return result; - } -}, null); -$C('$data.Expressions.ParametricQueryExpression', $data.Expressions.ExpressionNode, null, { - constructor: function (expression, parameters) { - this.expression = expression; - this.parameters = parameters || []; - }, - nodeType: { value: $data.Expressions.ExpressionType.ParametricQuery, enumerable: true } -});$C('$data.Expressions.ProjectionExpression', $data.Expressions.EntitySetExpression, null, { - constructor: function (source, selector, params, instance) { - - }, - nodeType: { value: $data.Expressions.ExpressionType.Projection, enumerable: true } - -}); - - -$C('$data.Expressions.QueryExpressionCreator', $data.Expressions.EntityExpressionVisitor, null, { - constructor: function (scopeContext) { - /// - Guard.requireValue("scopeContext", scopeContext); - this.scopeContext = scopeContext; - }, - VisitEntitySetExpression: function (expression, context) { - if (expression.source instanceof $data.Expressions.EntityContextExpression) { - this.lambdaTypes.push(expression); - } - return expression; - }, - - VisitServiceOperationExpression: function (expression, context) { - if (expression.source instanceof $data.Expressions.EntityContextExpression) { - this.lambdaTypes.push(expression); - } - return expression; - }, - - VisitCodeExpression: function (expression, context) { - ///Converts the CodeExpression into an EntityExpression - /// - var source = expression.source.toString(); - var jsCodeTree = Container.createCodeParser(this.scopeContext).createExpression(source); - this.scopeContext.log({ event: "JSCodeExpression", data: jsCodeTree }); - - //TODO rename classes to reflex variable names - //TODO engage localValueResolver here - //var globalVariableResolver = Container.createGlobalContextProcessor(window); - var constantResolver = Container.createConstantValueResolver(expression.parameters, window, this.scopeContext); - var parameterProcessor = Container.createParameterResolverVisitor(); - - jsCodeTree = parameterProcessor.Visit(jsCodeTree, constantResolver); - - this.scopeContext.log({ event: "JSCodeExpressionResolved", data: jsCodeTree }); - var code2entity = Container.createCodeToEntityConverter(this.scopeContext); - - ///user provided query parameter object (specified as thisArg earlier) is passed in - var entityExpression = code2entity.Visit(jsCodeTree, { queryParameters: expression.parameters, lambdaParameters: this.lambdaTypes, frameType: context.frameType }); - - ///parameters are referenced, ordered and named, also collected in a flat list of name value pairs - var result = Container.createParametricQueryExpression(entityExpression, code2entity.parameters); - this.scopeContext.log({ event: "EntityExpression", data: entityExpression }); - - return result; - }, - - - VisitFilterExpression: function (expression, context) { - var source = this.Visit(expression.source, context); - context = context || {}; - context.frameType = expression.getType(); - var selector = this.Visit(expression.selector, context); - if (source !== expression.source || selector !== expression.selector) { - return Container.createFilterExpression(source, selector, expression.params, expression.instance); - } - return expression; - }, - - VisitInlineCountExpression: function (expression, context) { - var source = this.Visit(expression.source, context); - context = context || {}; - context.frameType = expression.getType(); - var selector = this.Visit(expression.selector, context); - if (source !== expression.source || selector !== expression.selector) { - return Container.createInlineCountExpression(source, selector, expression.params, expression.instance); - } - return expression; - }, - - VisitProjectionExpression: function (expression, context) { - var source = this.Visit(expression.source, context); - context = context || {}; - context.frameType = expression.getType(); - var selector = this.Visit(expression.selector, context); - if (source !== expression.source || selector !== expression.selector) { - var expr = Container.createProjectionExpression(source, selector, expression.params, expression.instance); - expr.projectionAs = expression.projectionAs; - return expr; - } - return expression; - }, - - VisitOrderExpression: function (expression, context) { - var source = this.Visit(expression.source, context); - context = context || {}; - context.frameType = expression.getType(); - var selector = this.Visit(expression.selector, context); - if (source !== expression.source || selector !== expression.selector) { - return Container.createOrderExpression(source, selector, expression.nodeType); - } - return expression; - } -});$C('$data.Expressions.QueryParameterExpression', $data.Expressions.ExpressionNode, null, { - constructor: function (name, index, value, type) { - this.name = name; - this.index = index; - this.value = value; - //TODO - this.type = Container.getTypeName(value); - }, - - nodeType: { value: $data.Expressions.ExpressionType.QueryParameter, writable: false } -});$C('$data.Expressions.RepresentationExpression', $data.Expressions.ExpressionNode, null, { - constructor: function (kind) { - }, - - getMemberDefinition: function (name) { - return this.entityType.getMemberDefinition(name); - }, - - nodeType: { value: $data.Expressions.ExpressionType.Entity } -}); - -$C('$data.Expressions.ServiceOperationExpression', $data.Expressions.ExpressionNode, null, { - constructor: function (source, selector, params, cfg, boundItem) { - /// - /// - /// - /// - /// - /// - Guard.requireType("source", source, [$data.Expressions.EntityContextExpression]); - Guard.requireType("selector", source, [$data.Expressions.MemberInfoExpression]); - - this.source = source; - this.selector = selector - this.params = params - this.cfg = cfg; - this.boundItem = boundItem; - - function findContext() { - //TODO: use source from function parameter and return a value at the end of the function - var r = source; - while (r) { - if (r instanceof $data.Expressions.EntityContextExpression) { - return r; - } - r = r.source; - } - } - - var c = findContext(); - switch (true) { - case this.source instanceof $data.Expressions.EntityContextExpression: - this.elementType = cfg.elementType ? Container.resolveType(cfg.elementType) : (this.elementType ? Container.resolveType(cfg.returnType) : null); - this.storageModel = cfg.elementType ? c.instance._storageModel.getStorageModel(Container.resolveType(cfg.elementType)) : null; - break; - default: - Guard.raise("Unknown source type for EntitySetExpression: " + this.source.getType().name); - } - - }, - nodeType: { value: $data.Expressions.ExpressionType.ServiceOperation, enumerable: true } -});$C('$data.Expressions.ContinuationExpressionBuilder', $data.Expressions.EntityExpressionVisitor, null, { - constructor: function (mode) { - this.mode = mode; - }, - compile: function (query) { - - var findContext = { mode: "find", skipExists: false }; - this.Visit(query.expression, findContext); - - var result = { - skip: findContext.skipSize, - take: findContext.pageSize, - message: '' - } - - - if ('pageSize' in findContext) { - var expression; - var context = { mode: this.mode, pageSize: findContext.pageSize }; - - if (!findContext.skipExists && (findContext.pageSize)) { - context.append = true; - expression = this.Visit(query.expression, context); - - } else if (findContext.skipExists) { - expression = this.Visit(query.expression, context) - } - - if (!context.abort) { - result.expression = expression - } - else { - result.skip = (result.skip || 0) - result.take; - result.message = 'Invalid skip value!'; - } - }else{ - result.message = 'take expression not defined in the chain!'; - } - - return result; - }, - VisitPagingExpression: function (expression, context) { - - switch (context.mode) { - case 'find': - if (expression.nodeType === $data.Expressions.ExpressionType.Take) { - context.pageSize = expression.amount.value; - } else { - context.skipSize = expression.amount.value; - context.skipExists = true; - } - break; - case 'prev': - if (expression.nodeType === $data.Expressions.ExpressionType.Skip) { - var amount = expression.amount.value - context.pageSize; - context.abort = amount < 0 && expression.amount.value >= context.pageSize; - - var constExp = Container.createConstantExpression(Math.max(amount, 0), "number"); - return Container.createPagingExpression(expression.source, constExp, expression.nodeType); - } else if (context.append) { - //no skip expression, skip: 0, no prev - context.abort = true; - } - break; - case 'next': - if (expression.nodeType === $data.Expressions.ExpressionType.Skip) { - var amount = context.pageSize + expression.amount.value; - var constExp = Container.createConstantExpression(amount, "number"); - return Container.createPagingExpression(expression.source, constExp, expression.nodeType); - } else if (context.append) { - //no skip expression, skip: 0 - var constExp = Container.createConstantExpression(context.pageSize, "number"); - return Container.createPagingExpression(expression, constExp, $data.Expressions.ExpressionType.Skip); - } - break; - default: - } - - this.Visit(expression.source, context); - } -}); -$data.Class.define('$data.Validation.ValidationError', null, null, { - constructor: function (message, propertyDefinition, type) { - /// - /// - - this.Message = message; - this.PropertyDefinition = propertyDefinition; - this.Type = type; - }, - Type: { dataType: 'string' }, - Message: { dataType: "string" }, - PropertyDefinition: { dataType: $data.MemberDefinition } -}, null); - -$data.Class.define('$data.Validation.EntityValidationBase', null, null, { - - ValidateEntity: function (entity) { - /// - return []; - }, - - ValidateEntityField: function (entity, memberDefinition) { - /// - /// - return []; - }, - - getValidationValue: function (memberDefinition, validationName) { - Guard.raise("Pure class"); - }, - getValidationMessage: function (memberDefinition, validationName, defaultMessage) { - Guard.raise("Pure class"); - } - -}, null); - -$data.Validation = $data.Validation || {}; -$data.Validation.Entity = new $data.Validation.EntityValidationBase(); -$data.Class.define('$data.Validation.Defaults', null, null, null, { - validators: { - value: { - required: function (value, definedValue) { return !Object.isNullOrUndefined(value); }, - customValidator: function (value, definedValue) { return Object.isNullOrUndefined(value) || typeof definedValue == "function" ? definedValue(value) : true; }, - - minValue: function (value, definedValue) { return Object.isNullOrUndefined(value) || value >= definedValue; }, - maxValue: function (value, definedValue) { return Object.isNullOrUndefined(value) || value <= definedValue; }, - - minLength: function (value, definedValue) { return Object.isNullOrUndefined(value) || value.length >= definedValue; }, - maxLength: function (value, definedValue) { return Object.isNullOrUndefined(value) || value.length <= definedValue; }, - length: function (value, definedValue) { return Object.isNullOrUndefined(value) || value.length == definedValue; }, - regex: function (value, definedValue) { - return Object.isNullOrUndefined(value) || - value.match(typeof definedValue === 'string' - ? new RegExp((definedValue.indexOf('/') === 0 && definedValue.lastIndexOf('/') === (definedValue.length - 1)) ? definedValue.slice(1, -1) : definedValue) - : definedValue) - } - } - }, - - _getGroupValidations: function (validations) { - var validators = {}; - if (Array.isArray(validations)) { - for (var i = 0; i < validations.length; i++) { - var validator = validations[i]; - if (typeof this.validators[validator] === 'function') { - validators[validator] = this.validators[validator]; - } - } - } - - return validators; - } -}); - -$data.Class.define('$data.Validation.EntityValidation', $data.Validation.EntityValidationBase, null, { - - ValidateEntity: function (entity) { - /// - - var errors = []; - entity.getType().memberDefinitions.getPublicMappedProperties().forEach(function (memDef) { - errors = errors.concat(this.ValidateEntityField(entity, memDef, undefined, true)); - }, this); - return errors; - }, - ValidateEntityField: function (entity, memberDefinition, newValue, valueNotSet) { - /// - /// - var errors = []; - var resolvedType = Container.resolveType(memberDefinition.dataType); - var typeName = Container.resolveName(resolvedType); - var value = !valueNotSet ? newValue : entity[memberDefinition.name]; - - if (!memberDefinition.inverseProperty && resolvedType && typeof resolvedType.isAssignableTo === 'function' && resolvedType.isAssignableTo($data.Entity)) { - typeName = $data.Entity.fullName; - } - - this.fieldValidate(entity, memberDefinition, value, errors, typeName); - return errors; - }, - - getValidationValue: function (memberDefinition, validationName) { - var value; - if (memberDefinition[validationName] && memberDefinition[validationName].value) - value = memberDefinition[validationName].value; - else - value = memberDefinition[validationName]; - - if (this.convertableValidation[validationName]) { - var typeToConvert; - if (this.convertableValidation[validationName] === true) { - typeToConvert = memberDefinition.type; - } else { - typeToConvert = this.convertableValidation[validationName]; - } - - if (typeToConvert) - value = Container.convertTo(value, typeToConvert, memberDefinition.elementType); - } - - return value; - }, - getValidationMessage: function (memberDefinition, validationName, defaultMessage) { - var eMessage = defaultMessage; - if (typeof memberDefinition[validationName] == "object" && memberDefinition[validationName].message) - eMessage = memberDefinition[validationName].message; - else if (memberDefinition.errorMessage) - eMessage = memberDefinition.errorMessage; - - return eMessage; - }, - createValidationError: function (memberDefinition, validationName, defaultMessage) { - return new $data.Validation.ValidationError(this.getValidationMessage(memberDefinition, validationName, defaultMessage), memberDefinition, validationName); - }, - - convertableValidation: { - value: { - required: '$data.Boolean', - minValue: true, - maxValue: true, - minLength: '$data.Integer', - maxLength: '$data.Integer', - length: '$data.Integer' - } - - }, - supportedValidations: { - value: { - //'$data.Entity': $data.Validation.Defaults._getGroupValidations(['required', 'customValidator']), - '$data.ObjectID': $data.Validation.Defaults._getGroupValidations(['required', 'customValidator']), - '$data.Byte': $data.Validation.Defaults._getGroupValidations(['required', 'customValidator', 'minValue', 'maxValue']), - '$data.SByte': $data.Validation.Defaults._getGroupValidations(['required', 'customValidator', 'minValue', 'maxValue']), - '$data.Decimal': $data.Validation.Defaults._getGroupValidations(['required', 'customValidator', 'minValue', 'maxValue']), - '$data.Float': $data.Validation.Defaults._getGroupValidations(['required', 'customValidator', 'minValue', 'maxValue']), - '$data.Number': $data.Validation.Defaults._getGroupValidations(['required', 'customValidator', 'minValue', 'maxValue']), - '$data.Int16': $data.Validation.Defaults._getGroupValidations(['required', 'customValidator', 'minValue', 'maxValue']), - '$data.Integer': $data.Validation.Defaults._getGroupValidations(['required', 'customValidator', 'minValue', 'maxValue']), - '$data.Int32': $data.Validation.Defaults._getGroupValidations(['required', 'customValidator', 'minValue', 'maxValue']), - '$data.Int64': $data.Validation.Defaults._getGroupValidations(['required', 'customValidator', 'minValue', 'maxValue']), - '$data.String': $data.Validation.Defaults._getGroupValidations(['required', 'customValidator', 'minLength', 'maxLength', 'length', 'regex']), - '$data.Date': $data.Validation.Defaults._getGroupValidations(['required', 'customValidator', 'minValue', 'maxValue']), - '$data.DateTimeOffset': $data.Validation.Defaults._getGroupValidations(['required', 'customValidator', 'minValue', 'maxValue']), - '$data.Time': $data.Validation.Defaults._getGroupValidations(['required', 'customValidator', 'minValue', 'maxValue']), - '$data.Boolean': $data.Validation.Defaults._getGroupValidations(['required', 'customValidator']), - '$data.Array': $data.Validation.Defaults._getGroupValidations(['required', 'customValidator', 'length']), - '$data.Object': $data.Validation.Defaults._getGroupValidations(['required', 'customValidator']), - '$data.Guid': $data.Validation.Defaults._getGroupValidations(['required', 'customValidator']), - '$data.Blob': $data.Validation.Defaults._getGroupValidations(['required', 'customValidator', 'minLength', 'maxLength', 'length']), - '$data.GeographyPoint': $data.Validation.Defaults._getGroupValidations(['required', 'customValidator']), - '$data.GeographyLineString': $data.Validation.Defaults._getGroupValidations(['required', 'customValidator']), - '$data.GeographyPolygon': $data.Validation.Defaults._getGroupValidations(['required', 'customValidator']), - '$data.GeographyMultiPoint': $data.Validation.Defaults._getGroupValidations(['required', 'customValidator']), - '$data.GeographyMultiLineString': $data.Validation.Defaults._getGroupValidations(['required', 'customValidator']), - '$data.GeographyMultiPolygon': $data.Validation.Defaults._getGroupValidations(['required', 'customValidator']), - '$data.GeographyCollection': $data.Validation.Defaults._getGroupValidations(['required', 'customValidator']), - '$data.GeometryPoint': $data.Validation.Defaults._getGroupValidations(['required', 'customValidator']), - '$data.GeometryLineString': $data.Validation.Defaults._getGroupValidations(['required', 'customValidator']), - '$data.GeometryPolygon': $data.Validation.Defaults._getGroupValidations(['required', 'customValidator']), - '$data.GeometryMultiPoint': $data.Validation.Defaults._getGroupValidations(['required', 'customValidator']), - '$data.GeometryMultiLineString': $data.Validation.Defaults._getGroupValidations(['required', 'customValidator']), - '$data.GeometryMultiPolygon': $data.Validation.Defaults._getGroupValidations(['required', 'customValidator']), - '$data.GeometryCollection': $data.Validation.Defaults._getGroupValidations(['required', 'customValidator']) - } - }, - - fieldValidate: function (entity, memberDefinition, value, errors, validationTypeName) { - /// - /// - /// - /// - if (entity.entityState == $data.EntityState.Modified && entity.changedProperties && entity.changedProperties.indexOf(memberDefinition) < 0) - return; - - var validatonGroup = this.supportedValidations[validationTypeName]; - if (validatonGroup) { - var validations = Object.keys(validatonGroup); - validations.forEach(function (validation) { - if (memberDefinition[validation] && validatonGroup[validation] && !validatonGroup[validation].call(entity, value, this.getValidationValue(memberDefinition, validation))) - errors.push(this.createValidationError(memberDefinition, validation, 'Validation error!')); - }, this); - - if (validationTypeName === $data.Entity.fullName && value instanceof $data.Entity && !value.isValid()) { - errors.push(this.createValidationError(memberDefinition, 'ComplexProperty', 'Validation error!')); - } - } - } - -}, null); - -$data.Validation.Entity = new $data.Validation.EntityValidation(); - -$data.Class.define('$data.Notifications.ChangeDistributorBase', null, null, { - distributeData: function (collectorData) { - Guard.raise("Pure class"); - } -}, null); -$data.Class.define('$data.Notifications.ChangeCollectorBase', null, null, { - buildData: function (entityContextData) { - Guard.raise("Pure class"); - }, - processChangedData: function (entityData) { - if (this.Distrbutor && this.Distrbutor.distributeData) - this.Distrbutor.distributeData(this.buildData(entityData)); - }, - Distrbutor: { enumerable: false, dataType: $data.Notifications.ChangeDistributorBase, storeOnObject: true } -}, null); -$data.Class.define('$data.Notifications.ChangeDistributor', $data.Notifications.ChangeDistributorBase, null, { - constructor: function (broadcastUrl) { - this.broadcastUrl = broadcastUrl; - }, - distributeData: function (data) { - $data.ajax({ - url: this.broadcastUrl, - type: "POST", - data: 'data=' + JSON.stringify(data), - succes: this.success, - error: this.error - }); - }, - broadcastUrl: { dataType: "string" }, - success: function () { }, - error: function () { } -}, null); -$data.Class.define('$data.Notifications.ChangeCollector', $data.Notifications.ChangeCollectorBase, null, { - buildData: function (entities) { - var result = []; - entities.forEach(function (element) { - var resObj = { entityState: element.data.entityState, typeName: element.data.getType().name }; - var enumerableMemDefCollection = []; - - switch (element.data.entityState) { - case $data.EntityState.Added: - enumerableMemDefCollection = element.data.getType().memberDefinitions.getPublicMappedProperties(); - break; - case $data.EntityState.Modified: - enumerableMemDefCollection = element.data.changedProperties; - break; - case $data.EntityState.Deleted: - enumerableMemDefCollection = element.data.getType().memberDefinitions.getKeyProperties(); - break; - default: - break; - } - - enumerableMemDefCollection.forEach(function (memDef) { - resObj[memDef.name] = element.data[memDef.name]; - }); - - result.push(resObj); - }); - - return result; - } -}, null);$data.Class.define('$data.Transaction', null, null, { - constructor: function () { - this._objectId = (new Date()).getTime(); - $data.Trace.log("create: ", this._objectId); - - this.oncomplete = new $data.Event("oncomplete", this); - this.onerror = new $data.Event("onerror", this); - }, - abort: function () { - Guard.raise(new Exception('Not Implemented', 'Not Implemented', arguments)); - }, - - _objectId: { type: $data.Integer }, - transaction: { type: $data.Object }, - - oncomplete: { type: $data.Event }, - onerror: { type: $data.Event } -}, null);$data.Class.define('$data.Access', null, null, {}, { - isAuthorized: function(access, user, sets, callback){ - var pHandler = new $data.PromiseHandler(); - var clbWrapper = pHandler.createCallback(callback); - var pHandlerResult = pHandler.getPromise(); - - //clbWrapper.error('Authorization failed', 'Access authorization'); - clbWrapper.success(true); - - return pHandlerResult; - - /*var error; - - if (!access) error = 'Access undefined'; - if (typeof access !== 'number') error = 'Invalid access type'; - if (!user) user = {}; //error = 'User undefined'; - if (!user.roles) user.roles = {}; //error = 'User has no roles'; - if (!roles) roles = {}; //error = 'Roles undefined'; - if (!(roles instanceof Array || typeof roles === 'object')) error = 'Invald roles type'; - - var pHandler = new $data.PromiseHandler(); - var clbWrapper = pHandler.createCallback(callback); - var pHandlerResult = pHandler.getPromise(); - - if (error){ - clbWrapper.error(error, 'Access authorization'); - return pHandlerResult; - } - - if (user.roles instanceof Array){ - var r = {}; - for (var i = 0; i < user.roles.length; i++){ - if (typeof user.roles[i] === 'string') r[user.roles[i]] = true; - } - user.roles = r; - } - - if (roles instanceof Array){ - var r = {}; - for (var i = 0; i < roles.length; i++){ - if (typeof roles[i] === 'string') r[roles[i]] = true; - } - roles = r; - } - - var args = arguments; - var readyFn = function(result){ - if (result) clbWrapper.success(result); - else clbWrapper.error('Authorization failed', args); - }; - - var rolesKeys = Object.getOwnPropertyNames(roles); - var i = 0; - - var callbackFn = function(result){ - if (result) readyFn(result); - - if (typeof roles[rolesKeys[i]] === 'boolean' && roles[rolesKeys[i]]){ - if (user.roles[rolesKeys[i]]) readyFn(true); - else{ - i++; - if (i < rolesKeys.length) callbackFn(); - else readyFn(false); - } - }else if (typeof roles[rolesKeys[i]] === 'function'){ - var r = roles[rolesKeys[i]].call(user); - - if (typeof r === 'function') r.call(user, (i < rolesKeys.length ? callbackFn : readyFn)); - else{ - if (r) readyFn(true); - else{ - i++; - if (i < rolesKeys.length) callbackFn(); - else readyFn(false); - } - } - }else if (typeof roles[rolesKeys[i]] === 'number'){ - if (((typeof user.roles[rolesKeys[i]] === 'number' && (user.roles[rolesKeys[i]] & access)) || - (typeof user.roles[rolesKeys[i]] !== 'number' && user.roles[rolesKeys[i]])) && - (roles[rolesKeys[i]] & access)) user.roles[rolesKeys[i]] && readyFn(true); - else{ - i++; - if (i < rolesKeys.length) callbackFn(); - else readyFn(false); - } - } - }; - - callbackFn(); - - return pHandlerResult;*/ - }, - getAccessBitmaskFromPermission: function(p){ - var access = $data.Access.None; - - if (p.Create) access |= $data.Access.Create; - if (p.Read) access |= $data.Access.Read; - if (p.Update) access |= $data.Access.Update; - if (p.Delete) access |= $data.Access.Delete; - if (p.DeleteBatch) access |= $data.Access.DeleteBatch; - if (p.Execute) access |= $data.Access.Execute; - - return access; - }, - None: { value: 0 }, - Create: { value: 1 }, - Read: { value: 2 }, - Update: { value: 4 }, - Delete: { value: 8 }, - DeleteBatch: { value: 16 }, - Execute: { value: 32 } -}); -$data.Class.define('$data.Promise', null, null, { - always: function () { Guard.raise(new Exception('$data.Promise.always', 'Not implemented!')); }, - done: function () { Guard.raise(new Exception('$data.Promise.done', 'Not implemented!')); }, - fail: function () { Guard.raise(new Exception('$data.Promise.fail', 'Not implemented!')); }, - isRejected: function () { Guard.raise(new Exception('$data.Promise.isRejected', 'Not implemented!')); }, - isResolved: function () { Guard.raise(new Exception('$data.Promise.isResolved', 'Not implemented!')); }, - //notify: function () { Guard.raise(new Exception('$data.Promise.notify', 'Not implemented!')); }, - //notifyWith: function () { Guard.raise(new Exception('$data.Promise.notifyWith', 'Not implemented!')); }, - pipe: function () { Guard.raise(new Exception('$data.Promise.pipe', 'Not implemented!')); }, - progress: function () { Guard.raise(new Exception('$data.Promise.progress', 'Not implemented!')); }, - promise: function () { Guard.raise(new Exception('$data.Promise.promise', 'Not implemented!')); }, - //reject: function () { Guard.raise(new Exception('$data.Promise.reject', 'Not implemented!')); }, - //rejectWith: function () { Guard.raise(new Exception('$data.Promise.rejectWith', 'Not implemented!')); }, - //resolve: function () { Guard.raise(new Exception('$data.Promise.resolve', 'Not implemented!')); }, - //resolveWith: function () { Guard.raise(new Exception('$data.Promise.resolveWith', 'Not implemented!')); }, - state: function () { Guard.raise(new Exception('$data.Promise.state', 'Not implemented!')); }, - then: function () { Guard.raise(new Exception('$data.Promise.then', 'Not implemented!')); } -}, null); - -$data.Class.define('$data.PromiseHandlerBase', null, null, { - constructor: function () { }, - createCallback: function (callBack) { - callBack = $data.typeSystem.createCallbackSetting(callBack); - - return cbWrapper = { - success: callBack.success, - error: callBack.error, - notify: callBack.notify - }; - }, - getPromise: function () { - return new $data.Promise(); - } -}, null); - -$data.PromiseHandler = $data.PromiseHandlerBase; -var EventSubscriber = $data.Class.define("EventSubscriber", null, null, { - constructor: function (handler, state, thisArg) { - /// - /// event handler - /// - /// - /// - /// - /// - /// - /// custom state object - /// [i]this[/i] context for handler - /// - /// event handler - /// custom state object - /// [i]this[/i] context for handler - this.handler = handler; - this.state = state; - this.thisArg = thisArg; - }, - handler: {}, - state: {}, - thisArg: {} -}); - -$data.Event = Event = $data.Class.define("$data.Event", null, null, { - constructor: function (name, sender) { - ///The name of the event - ///The originator/sender of the event. [this] in handlers will be set to this - var subscriberList = null; - var parentObject = sender; - - function detachHandler(list, handler) { - /// - /// - list.forEach(function (item, index) { - if (item.handler == handler) { - list.splice(index, 1); - } - }); - } - - this.attach = function (handler, state, thisArg) { - /// - /// - /// - /// - /// - /// - /// - /// - /// - if (!subscriberList) { - subscriberList = []; - } - subscriberList.push(new EventSubscriber(handler, state, thisArg || sender)); - }; - this.detach = function (handler) { - detachHandler(subscriberList, handler); - }; - this.fire = function (eventData, snder) { - var snd = snder || sender || this; - //eventData.eventName = name; - /// - if (subscriberList) { - subscriberList.forEach(function (subscriber) { - /// - try { - subscriber.handler.call(subscriber.thisArg, snd, eventData, subscriber.state); - } catch(ex) { - console.log("unhandled exception in event handler. exception suppressed"); - console.dir(ex); - } - }); - } - }; - this.fireCancelAble = function (eventData, snder) { - var snd = snder || sender || this; - //eventData.eventName = name; - /// - var isValid = true; - if (subscriberList) { - subscriberList.forEach(function (subscriber) { - /// - try { - isValid = isValid && (subscriber.handler.call(subscriber.thisArg, snd, eventData, subscriber.state) === false ? false : true); - } catch (ex) { - console.log("unhandled exception in event handler. exception suppressed"); - console.dir(ex); - } - }); - } - return isValid; - }; - } -}); - - -var eventData = $data.Class.define("EventData", null, null, { - eventName: {} -}); - -var PropertyChangeEventData = $data.Class.define("PropertyChangeEventData", EventData, null, { - constructor: function (propertyName, oldValue, newValue) { - this.propertyName = propertyName; - this.oldValue = oldValue; - this.newValue = newValue; - }, - propertyName: {}, - oldValue: {}, - newValue: {} -}); - -var PropertyValidationEventData = $data.Class.define("PropertyValidationEventData", EventData, null, { - constructor: function (propertyName, oldValue, newValue, errors) { - this.propertyName = propertyName; - this.oldValue = oldValue; - this.newValue = newValue; - this.errors = errors; - this.cancel = false; - }, - propertyName: {}, - oldValue: {}, - newValue: {}, - errors: {}, - cancel: {} -}); - - - -$data.Entity = Entity = $data.Class.define("$data.Entity", null, null, { - constructor: function (initData, newInstanceOptions) { - /// - /// This class provide a light weight, object-relational interface between - /// your javascript code and database. - /// - /// - /// - /// initialization data - /// - /// var category = new $news.Types.Category({ Title: 'Tech' }); - /// $news.context.Categories.add(category); - /// - /// - /// - /// initialization data - /// - /// - /// - /// - /// Determines the current $data.Entity is validated. - /// array of $data.Validation.ValidationError - /// array of MemberDefinition - /// - /// array of MemberDefinition - - this.initData = {}; - var thisType = this.getType(); - if (thisType.__copyPropertiesToInstance) { - $data.typeSystem.writePropertyValues(this); - } - - var ctx = null; - this.context = ctx; - if ("setDefaultValues" in thisType) { - if (!newInstanceOptions || newInstanceOptions.setDefaultValues !== false) { - if (!initData || Object.keys(initData).length < 1) { - initData = thisType.setDefaultValues(initData); - } - } - } - - if (typeof initData === "object") { - var typeMemDefs = thisType.memberDefinitions; - var memDefNames = typeMemDefs.getPublicMappedPropertyNames(); - - for (var i in initData) { - if (memDefNames.indexOf(i) > -1) { - var memberDef = typeMemDefs.getMember(i); - var type = Container.resolveType(memberDef.type); - var value = initData[i]; - - if (memberDef.concurrencyMode === $data.ConcurrencyMode.Fixed) { - this.initData[i] = value; - } else { - if (newInstanceOptions && newInstanceOptions.converters) { - var converter = newInstanceOptions.converters[Container.resolveName(type)]; - if (converter) - value = converter(value); - } - - this.initData[i] = Container.convertTo(value, type, memberDef.elementType, newInstanceOptions); - } - } - } - - } - - if (newInstanceOptions && newInstanceOptions.entityBuilder) { - newInstanceOptions.entityBuilder(this, thisType.memberDefinitions.asArray(), thisType); - } - - this.changedProperties = undefined; - this.entityState = undefined; - - }, - toString: function () { - /// Returns a string that represents the current $data.Entity - /// - - return this.getType().fullName + "(" + (this.Id || this.Name || '') + ")" - }, - toJSON: function () { - /// Creates pure JSON object from $data.Entity. - /// JSON representation - - var result = {}; - var self = this; - this.getType().memberDefinitions.getPublicMappedProperties().forEach(function (memDef) { - if (self[memDef.name] instanceof Date && memDef.type && Container.resolveType(memDef.type) === $data.DateTimeOffset) { - result[memDef.name] = new $data.DateTimeOffset(self[memDef.name]); - } else { - result[memDef.name] = self[memDef.name]; - } - }); - return result; - }, - equals: function (entity) { - /// Determines whether the specified $data.Entity is equal to the current $data.Entity. - /// [b]true[/b] if the specified $data.Entity is equal to the current $data.Entity; otherwise, [b]false[/b]. - - if (entity.getType() !== this.getType()) { - return false; - } - var entityPk = this.getType().memberDefinitions.getKeyProperties(); - for (var i = 0; i < entityPk.length; i++) { - if (this[entityPk[i].name] != entity[entityPk[i].name]) { - return false; - } - } - return true; - }, - - propertyChanging: { - dataType: $data.Event, storeOnObject: true, monitorChanges: false, notMapped: true, enumerable: false, prototypeProperty: true, - get: function () { - if (!this._propertyChanging) - this._propertyChanging = new Event('propertyChanging', this); - - return this._propertyChanging; - }, - set: function (value) { this._propertyChanging = value; } - }, - - propertyChanged: { - dataType: $data.Event, storeOnObject: true, monitorChanges: false, notMapped: true, enumerable: false, prototypeProperty: true, - get: function () { - if (!this._propertyChanged) - this._propertyChanged = new Event('propertyChanged', this); - - return this._propertyChanged; - }, - set: function (value) { this._propertyChanged = value; } - }, - - propertyValidationError: { - dataType: $data.Event, storeOnObject: true, monitorChanges: false, notMapped: true, enumerable: false, prototypeProperty: true, - get: function () { - if (!this._propertyValidationError) - this._propertyValidationError = new Event('propertyValidationError', this); - - return this._propertyValidationError; - }, - set: function (value) { this._propertyValidationError = value; } - }, - - // protected - storeProperty: function (memberDefinition, value) { - /// - /// - - if (memberDefinition.concurrencyMode !== $data.ConcurrencyMode.Fixed) { - value = Container.convertTo(value, memberDefinition.type, memberDefinition.elementType); - } - - var eventData = null; - if (memberDefinition.monitorChanges != false && (this._propertyChanging || this._propertyChanged || "instancePropertyChanged" in this.constructor)) { - var origValue = this[memberDefinition.name]; - eventData = new PropertyChangeEventData(memberDefinition.name, origValue, value); - if (this._propertyChanging) - this.propertyChanging.fire(eventData); - } - - if (memberDefinition.monitorChanges != false && (this._propertyValidationError || "instancePropertyValidationError" in this.constructor)) { - var errors = $data.Validation.Entity.ValidateEntityField(this, memberDefinition, value); - if (errors.length > 0) { - var origValue = this[memberDefinition.name]; - var errorEventData = new PropertyValidationEventData(memberDefinition.name, origValue, value, errors); - - if (this._propertyValidationError) - this.propertyValidationError.fire(errorEventData); - if ("instancePropertyValidationError" in this.constructor) - this.constructor["instancePropertyValidationError"].fire(errorEventData, this); - - if (errorEventData.cancel == true) - return; - } - } - - if (memberDefinition.storeOnObject == true) { - //TODO refactor to Base.getBackingFieldName - var backingFieldName = "_" + memberDefinition.name; - this[backingFieldName] = value; - } else { - this.initData[memberDefinition.name] = value; - } - this.isValidated = false; - - if (memberDefinition.monitorChanges != false && this.entityState == $data.EntityState.Unchanged) - this.entityState = $data.EntityState.Modified; - - this._setPropertyChanged(memberDefinition); - - if (memberDefinition.monitorChanges != false) { - //if (!this.changedProperties) { - // this.changedProperties = []; - //} - - //if (!this.changedProperties.some(function (memDef) { return memDef.name == memberDefinition.name })) - // this.changedProperties.push(memberDefinition); - - if (this._propertyChanged) - this.propertyChanged.fire(eventData); - - //TODO mixin framework - if ("instancePropertyChanged" in this.constructor) { - this.constructor["instancePropertyChanged"].fire(eventData, this); - } - } - }, - _setPropertyChanged: function (memberDefinition) { - if (memberDefinition.monitorChanges != false) { - if (!this.changedProperties) { - this.changedProperties = []; - } - - if (!this.changedProperties.some(function (memDef) { return memDef.name == memberDefinition.name })) - this.changedProperties.push(memberDefinition); - } - }, - - // protected - retrieveProperty: function (memberDefinition) { - /// - - if (memberDefinition.storeOnObject == true) { - //TODO refactor to Base.getBackingFieldName - var backingFieldName = "_" + memberDefinition.name; - return this[backingFieldName]; - } else { - return this.initData[memberDefinition.name]; - } - }, - - // protected - getProperty: function (memberDefinition, callback, tran) { - /// Retrieve value of member - /// - /// - /// - /// - /// - /// - /// value associated for [i]memberDefinition[/i] - - callback = $data.typeSystem.createCallbackSetting(callback); - if (this[memberDefinition.name] != undefined) { - if (tran instanceof $data.Transaction) - callback.success(this[memberDefinition.name], tran); - else - callback.success(this[memberDefinition.name]); - return; - } - - var context = this.context; - if (!this.context) { - try { - var that = this; - var storeToken = this.storeToken || this.getType().storeToken; - if (storeToken && typeof storeToken.factory === 'function') { - var ctx = storeToken.factory(); - return ctx.onReady().then(function (context) { - return context.loadItemProperty(that, memberDefinition, callback); - }); - } - } catch (e) { } - - Guard.raise(new Exception('Entity not in context', 'Invalid operation')); - } else { - return context.loadItemProperty(this, memberDefinition, callback, tran); - } - }, - // protected - setProperty: function (memberDefinition, value, callback, tran) { - /// - /// - /// done - this[memberDefinition.name] = value; - - //callback = $data.typeSystem.createCallbackSetting(callback); - var pHandler = new $data.PromiseHandler(); - callback = pHandler.createCallback(callback); - callback.success(this[memberDefinition.name]); - return pHandler.getPromise(); - }, - - isValid: function () { - /// Determines the current $data.Entity is validated and valid. - /// - - if (!this.isValidated) { - this.ValidationErrors = $data.Validation.Entity.ValidateEntity(this); - this.isValidated = true; - } - return this.ValidationErrors.length == 0; - }, - isValidated: { dataType: "bool", storeOnObject: true, monitorChanges: false, notMapped: true, enumerable: false, value: false }, - ValidationErrors: { - dataType: Array, - elementType: $data.Validation.ValidationError, - storeOnObject: true, - monitorChanges: true, - notMapped: true, - enumerable: false - }, - - resetChanges: function () { - /// reset changes - - delete this._changedProperties; - }, - - changedProperties: { - dataType: Array, - elementType: window["MemberDefinition"], - storeOnObject: true, - monitorChanges: false, - notMapped: true, - enumerable: false - }, - - entityState: { dataType: "integer", storeOnObject: true, monitorChanges: false, notMapped: true, enumerable: false }, - /* - toJSON: function () { - if (this.context) { - var itemType = this.getType(); - var storageModel = this.context._storageModel[itemType.name]; - var o = new Object(); - for (var property in this) { - if (typeof this[property] !== "function") { - var excludedFields = storageModel.Associations.every(function (association) { - return association.FromPropertyName == property && (association.FromMultiplicity == "0..1" || association.FromMultiplicity == "1"); - }, this); - if (!excludedFields) { - o[property] = this[property]; - } - } - } - return o; - } - return this; - } */ - //, - - //onReady: function (callback) { - // this.__onReadyList = this.__onReadyList || []; - // this.__onReadyList.push(callback); - //}, - - remove: function () { - if ($data.ItemStore && 'EntityInstanceRemove' in $data.ItemStore) - return $data.ItemStore.EntityInstanceRemove.apply(this, arguments); - else - throw 'not implemented'; //todo - }, - save: function () { - if ($data.ItemStore && 'EntityInstanceSave' in $data.ItemStore) - return $data.ItemStore.EntityInstanceSave.apply(this, arguments); - else - throw 'not implemented'; //todo - }, - refresh: function () { - if ($data.ItemStore && 'EntityInstanceSave' in $data.ItemStore) - return $data.ItemStore.EntityInstanceRefresh.apply(this, arguments); - else - throw 'not implemented'; //todo - }, - storeToken: { type: Object, monitorChanges: false, notMapped: true, storeOnObject: true }, - - getFieldUrl: function (field) { - if (this.context) { - return this.context.getFieldUrl(this, field); - } else if (this.getType().storeToken && typeof this.getType().storeToken.factory === 'function') { - var context = this.getType().storeToken.factory(); - return context.getFieldUrl(this, field); - } else if (this.getType().storeToken){ - try { - var ctx = $data.ItemStore._getContextPromise('default', this.getType()); - if (ctx instanceof $data.EntityContext) { - return ctx.getFieldUrl(this, field); - } - } catch (e) { - } - } - return '#'; - } -}, -{ - //create get_[property] and set_[property] functions for properties - __setPropertyfunctions: { value: true, notMapped: true, enumerable: false, storeOnObject: true }, - //copy public properties to current instance - __copyPropertiesToInstance: { value: false, notMapped: true, enumerable: false, storeOnObject: true }, - - inheritedTypeProcessor: function (type) { - if ($data.ItemStore && 'EntityInheritedTypeProcessor' in $data.ItemStore) - $data.ItemStore.EntityInheritedTypeProcessor.apply(this, arguments); - - //default value setter method factory - type.defaultValues = {}; - - type.memberDefinitions.asArray().forEach(function (pd) { - if (pd.hasOwnProperty("defaultValue")) { - type.defaultValues[pd.name] = pd.defaultValue; - } - }); - - if (Object.keys(type.defaultValues).length > 0) { - type.setDefaultValues = function (initData, instance) { - initData = initData || {}; - var dv = type.defaultValues; - for (var n in dv) { - if (!(n in initData)) { - var value = dv[n]; - if ("function" === typeof value) { - initData[n] = dv[n](n, instance); - } else { - initData[n] = dv[n]; - } - } - } - return initData; - } - } - }, - - - //Type Events - addEventListener: function(eventName, fn) { - var delegateName = "on" + eventName; - if (!(delegateName in this)) { - this[delegateName] = new $data.Event(eventName, this); - } - this[delegateName].attach(fn); - }, - removeEventListener: function(eventName, fn) { - var delegateName = "on" + eventName; - if (!(delegateName in this)) { - return; - } - this[delegateName].detach(fn); - }, - raiseEvent: function(eventName, data) { - var delegateName = "on" + eventName; - if (!(delegateName in this)) { - return; - } - this[delegateName].fire(data); - }, - - getFieldNames: function () { - return this.memberDefinitions.getPublicMappedPropertyNames(); - }, - - 'from$data.Object': function (value, type, t, options) { - if (!Object.isNullOrUndefined(value)) { - var newInstanceOptions; - if (options && options.converters) { - newInstanceOptions = { - converters: options.converters - } - } - - return new this(value, newInstanceOptions); - } else { - return value; - } - } - -}); - - -$data.define = function (name, container, definition) { - if (container && !(container instanceof $data.ContainerClass)) { - definition = container; - container = undefined; - } - if (!definition) { - throw new Error("json object type is not supported yet"); - } - var _def = {}; - var hasKey = false; - var keyFields = []; - Object.keys(definition).forEach(function (fieldName) { - var propDef = definition[fieldName]; - if (typeof propDef === 'object' && ("type" in propDef || "get" in propDef || "set" in propDef)) { - - _def[fieldName] = propDef; - if (propDef.key) { - keyFields.push(propDef); - } - - if (("get" in propDef || "set" in propDef) && (!('notMapped' in propDef) || propDef.notMapped === true)) { - propDef.notMapped = true; - propDef.storeOnObject = true; - } - if ("get" in propDef && !("set" in propDef)) { - propDef.set = function () { }; - } else if ("set" in propDef && !("get" in propDef)) { - propDef.get = function () { }; - } - - } else { - _def[fieldName] = { type: propDef }; - } - }); - - if (keyFields.length < 1) { - var keyProp; - switch (true) { - case "id" in _def: - keyProp = "id"; - break; - case "Id" in _def: - keyProp = "Id" - break; - case "ID" in _def: - keyProp = "ID" - break; - } - if (keyProp) { - _def[keyProp].key = true; - var propTypeName = $data.Container.resolveName(_def[keyProp].type); - _def[keyProp].computed = true; - //if ("$data.Number" === propTypeName || "$data.Integer" === propTypeName) { - //} - } else { - _def.Id = { type: "int", key: true, computed: true } - } - } - - - var entityType = $data.Entity.extend(name, container, _def); - return entityType; -} -$data.implementation = function (name) { - return Container.resolveType(name); -}; - - - - -(function () { - - $data.defaults = $data.defaults || {}; - $data.defaults.defaultDatabaseName = 'JayDataDefault'; - -})(); - - -$data.Class.define('$data.StorageModel', null, null, { - constructor: function () { - ///User defined type - this.ComplexTypes = []; - this.Associations = []; - }, - LogicalType: {}, - LogicalTypeName: {}, - PhysicalType: {}, - PhysicalTypeName: {}, - EventHandlers: {}, - TableName: {}, - TableOptions: { value: undefined }, - ComplexTypes: {}, - Associations: {}, - ContextType: {}, - Roles: {} -}, null); -$data.Class.define('$data.Association', null, null, { - constructor: function (initParam) { - if (initParam) { - this.From = initParam.From; - this.FromType = initParam.FromType; - this.FromMultiplicity = initParam.FromMultiplicity; - this.FromPropertyName = initParam.FromPropertyName; - this.To = initParam.To; - this.ToType = initParam.ToType; - this.ToMultiplicity = initParam.ToMultiplicity; - this.ToPropertyName = initParam.ToPropertyName; - } - }, - From: {}, - FromType: {}, - FromMultiplicity: {}, - FromPropertyName: {}, - To: {}, - ToType: {}, - ToMultiplicity: {}, - ToPropertyName: {}, - ReferentialConstraint: {} -}, null); -$data.Class.define('$data.ComplexType', $data.Association, null, {}, null); - -$data.Class.define('$data.EntityContext', null, null, -{ - constructor: function (storageProviderCfg) { - /// Provides facilities for querying and working with entity data as objects. - ///Storage provider specific configuration object. - - if ($data.ItemStore && 'ContextRegister' in $data.ItemStore) - $data.ItemStore.ContextRegister.apply(this, arguments); - - if (storageProviderCfg.queryCache) - this.queryCache = storageProviderCfg.queryCache; - - if ("string" === typeof storageProviderCfg) { - if (0 === storageProviderCfg.indexOf("http")) { - storageProviderCfg = { - name: "oData", - oDataServiceHost: storageProviderCfg - } - } else { - storageProviderCfg = { - name: "local", - databaseName: storageProviderCfg - } - } - } - - if ("provider" in storageProviderCfg) { - storageProviderCfg.name = storageProviderCfg.provider; - } - - //Initialize properties - this.lazyLoad = false; - this.trackChanges = false; - this._entitySetReferences = {}; - this._storageModel = []; - - var ctx = this; - ctx._isOK = false; - - var origSuccessInitProvider = this._successInitProvider; - this._successInitProvider = function (errorOrContext) { - if (errorOrContext instanceof $data.EntityContext) { - origSuccessInitProvider(ctx); - } else { - origSuccessInitProvider(ctx, errorOrContext); - } - } - - this._storageModel.getStorageModel = function (typeName) { - var name = Container.resolveName(typeName); - return ctx._storageModel[name]; - }; - if (typeof storageProviderCfg.name === 'string') { - var tmp = storageProviderCfg.name; - storageProviderCfg.name = [tmp]; - } - var i = 0, providerType; - var providerList = [].concat(storageProviderCfg.name); - var callBack = $data.typeSystem.createCallbackSetting({ success: this._successInitProvider, error: this._successInitProvider }); - - this._initStorageModelSync(); - ctx._initializeEntitySets(ctx.getType()); - - $data.StorageProviderLoader.load(providerList, { - success: function (providerType) { - ctx.storageProvider = new providerType(storageProviderCfg, ctx); - ctx.storageProvider.setContext(ctx); - ctx.stateManager = new $data.EntityStateManager(ctx); - - var contextType = ctx.getType(); - if (providerType.name in contextType._storageModelCache) { - ctx._storageModel = contextType._storageModelCache[providerType.name]; - } else { - ctx._initializeStorageModel(); - contextType._storageModelCache[providerType.name] = ctx._storageModel; - } - - //ctx._initializeEntitySets(contextType); - if (storageProviderCfg && storageProviderCfg.user) Object.defineProperty(ctx, 'user', { value: storageProviderCfg.user, enumerable: true }); - if (storageProviderCfg && storageProviderCfg.checkPermission) Object.defineProperty(ctx, 'checkPermission', { value: storageProviderCfg.checkPermission, enumerable: true }); - - //ctx._isOK = false; - ctx._initializeStore(callBack); - }, - error: function () { - callBack.error('Provider fallback failed!'); - } - }); - - - - this.addEventListener = function (eventName, fn) { - var delegateName = "on" + eventName; - if (!(delegateName in this)) { - this[delegateName] = new $data.Event(eventName, this); - } - this[delegateName].attach(fn); - }; - - this.removeEventListener = function (eventName, fn) { - var delegateName = "on" + eventName; - if (!(delegateName in this)) { - return; - } - this[delegateName].detach(fn); - }; - - this.raiseEvent = function (eventName, data) { - var delegateName = "on" + eventName; - if (!(delegateName in this)) { - return; - } - this[delegateName].fire(data); - }; - - - this.ready = this.onReady({ - success: $data.defaultSuccessCallback, - error: function () { - if ($data.PromiseHandler !== $data.PromiseHandlerBase) { - $data.defaultErrorCallback.apply(this, arguments); - } else { - $data.Trace.error(arguments); - } - } - }); - }, - beginTransaction: function () { - var tables = null; - var callBack = null; - var isWrite = false; - - function readParam(value) { - if (Object.isNullOrUndefined(value)) return; - - if (typeof value === 'boolean') { - isWrite = value; - } else if (Array.isArray(value)) { - tables = value; - } else { - callBack = value; - } - } - - readParam(arguments[0]); - readParam(arguments[1]); - readParam(arguments[2]); - - var pHandler = new $data.PromiseHandler(); - callBack = pHandler.createCallback(callBack); - - //callBack = $data.typeSystem.createCallbackSetting(callBack); - this.storageProvider._beginTran(tables, isWrite, callBack); - - return pHandler.getPromise(); - }, - _isReturnTransaction: function (transaction) { - return transaction instanceof $data.Base || transaction === 'returnTransaction'; - }, - _applyTransaction: function (scope, cb, args, transaction, isReturnTransaction) { - if (isReturnTransaction === true) { - if (transaction instanceof $data.Transaction) { - Array.prototype.push.call(args, transaction); - cb.apply(scope, args); - } else { - this.beginTransaction(function (tran) { - Array.prototype.push.call(args, tran); - cb.apply(scope, args); - }); - } - } - else { - cb.apply(scope, args); - } - }, - - getDataType: function (dataType) { - // Obsolate - if (typeof dataType == "string") { - var memDef_dataType = this[dataType]; - if (memDef_dataType === undefined || memDef_dataType === null) { memDef_dataType = eval(dataType); } - return memDef_dataType; - } - return dataType; - }, - _initializeEntitySets: function (ctor) { - - for (var i = 0, l = this._storageModel.length; i < l; i++){ - var storageModel = this._storageModel[i]; - this[storageModel.ItemName] = new $data.EntitySet(storageModel.LogicalType, this, storageModel.ItemName, storageModel.EventHandlers, storageModel.Roles); - var sm = this[storageModel.ItemName]; - sm.name = storageModel.ItemName; - sm.tableName = storageModel.TableName; - sm.tableOptions = storageModel.TableOptions; - sm.eventHandlers = storageModel.EventHandlers; - this._entitySetReferences[storageModel.LogicalType.name] = sm; - - this._initializeActions(sm, ctor, ctor.getMemberDefinition(storageModel.ItemName)); - - } - - }, - _initializeStore: function (callBack) { - if (this.storageProvider) { - this.storageProvider.initializeStore(callBack); - } - }, - - _initStorageModelSync: function() { - var _memDefArray = this.getType().memberDefinitions.asArray(); - - - for (var i = 0; i < _memDefArray.length; i++) { - var item = _memDefArray[i]; - if ('dataType' in item) { - var itemResolvedDataType = Container.resolveType(item.dataType); - if (itemResolvedDataType && itemResolvedDataType.isAssignableTo && itemResolvedDataType.isAssignableTo($data.EntitySet)) { - var elementType = Container.resolveType(item.elementType); - var storageModel = new $data.StorageModel(); - storageModel.TableName = item.tableName || item.name; - storageModel.TableOptions = item.tableOptions; - storageModel.ItemName = item.name; - storageModel.LogicalType = elementType; - storageModel.LogicalTypeName = elementType.name; - storageModel.PhysicalTypeName = $data.EntityContext._convertLogicalTypeNameToPhysical(storageModel.LogicalTypeName); - storageModel.ContextType = this.getType(); - storageModel.Roles = item.roles; - if (item.indices) { - storageModel.indices = item.indices; - } - if (item.beforeCreate) { - if (!storageModel.EventHandlers) storageModel.EventHandlers = {}; - storageModel.EventHandlers.beforeCreate = item.beforeCreate; - } - if (item.beforeRead) { - if (!storageModel.EventHandlers) storageModel.EventHandlers = {}; - storageModel.EventHandlers.beforeRead = item.beforeRead; - } - if (item.beforeUpdate) { - if (!storageModel.EventHandlers) storageModel.EventHandlers = {}; - storageModel.EventHandlers.beforeUpdate = item.beforeUpdate; - } - if (item.beforeDelete) { - if (!storageModel.EventHandlers) storageModel.EventHandlers = {}; - storageModel.EventHandlers.beforeDelete = item.beforeDelete; - } - if (item.afterCreate) { - if (!storageModel.EventHandlers) storageModel.EventHandlers = {}; - storageModel.EventHandlers.afterCreate = item.afterCreate; - } - if (item.afterRead) { - if (!storageModel.EventHandlers) storageModel.EventHandlers = {}; - storageModel.EventHandlers.afterRead = item.afterRead; - } - if (item.afterUpdate) { - if (!storageModel.EventHandlers) storageModel.EventHandlers = {}; - storageModel.EventHandlers.afterUpdate = item.afterUpdate; - } - if (item.afterDelete) { - if (!storageModel.EventHandlers) storageModel.EventHandlers = {}; - storageModel.EventHandlers.afterDelete = item.afterDelete; - } - this._storageModel.push(storageModel); - var name = Container.resolveName(elementType); - this._storageModel[name] = storageModel; - } - } - } - - }, - _initializeStorageModel: function () { - - - var _memDefArray = this.getType().memberDefinitions.asArray(); - - - if (typeof intellisense !== 'undefined') - return; - - - for (var i = 0; i < this._storageModel.length; i++) { - var storageModel = this._storageModel[i]; - - ///Storage model item - var dbEntityInstanceDefinition = {}; - - storageModel.Associations = storageModel.Associations || []; - storageModel.ComplexTypes = storageModel.ComplexTypes || []; - for (var j = 0; j < storageModel.LogicalType.memberDefinitions.getPublicMappedProperties().length; j++) { - var memDef = storageModel.LogicalType.memberDefinitions.getPublicMappedProperties()[j]; - ///Member definition instance - - var memDefResolvedDataType = Container.resolveType(memDef.dataType); - - if ((this.storageProvider.supportedDataTypes.indexOf(memDefResolvedDataType) > -1) && Object.isNullOrUndefined(memDef.inverseProperty)) { - //copy member definition - var t = JSON.parse(JSON.stringify(memDef)); - //change datatype to resolved type - t.dataType = memDefResolvedDataType; - dbEntityInstanceDefinition[memDef.name] = t; - continue; - } - - this._buildDbType_navigationPropertyComplite(memDef, memDefResolvedDataType, storageModel); - - //var memDef_dataType = this.getDataType(memDef.dataType); - if ((memDefResolvedDataType === $data.Array || (memDefResolvedDataType.isAssignableTo && memDefResolvedDataType.isAssignableTo($data.EntitySet))) && - (memDef.inverseProperty && memDef.inverseProperty !== '$$unbound')) { - this._buildDbType_Collection_OneManyDefinition(dbEntityInstanceDefinition, storageModel, memDefResolvedDataType, memDef); - } else { - if (memDef.inverseProperty) { - if (memDef.inverseProperty === '$$unbound') { - //member definition is navigation but not back reference - if (memDefResolvedDataType === $data.Array) { - this._buildDbType_Collection_OneManyDefinition(dbEntityInstanceDefinition, storageModel, memDefResolvedDataType, memDef); - } else { - this._buildDbType_ElementType_OneManyDefinition(dbEntityInstanceDefinition, storageModel, memDefResolvedDataType, memDef); - } - } else { - //member definition is navigation property one..one or one..many case - var fields = memDefResolvedDataType.memberDefinitions.getMember(memDef.inverseProperty); - if (fields) { - if (fields.elementType) { - //member definition is one..many connection - var referealResolvedType = Container.resolveType(fields.elementType); - if (referealResolvedType === storageModel.LogicalType) { - this._buildDbType_ElementType_OneManyDefinition(dbEntityInstanceDefinition, storageModel, memDefResolvedDataType, memDef); - } else { - if (typeof intellisense === 'undefined') { - Guard.raise(new Exception('Inverse property not valid, refereed item element type not match: ' + storageModel.LogicalTypeName, ', property: ' + memDef.name)); - } - } - } else { - //member definition is one..one connection - this._buildDbType_ElementType_OneOneDefinition(dbEntityInstanceDefinition, storageModel, memDefResolvedDataType, memDef); - } - } else { - if (typeof intellisense === 'undefined') { - Guard.raise(new Exception('Inverse property not valid')); - } - } - } - } else { - //member definition is a complex type - this._buildDbType_addComplexTypePropertyDefinition(dbEntityInstanceDefinition, storageModel, memDefResolvedDataType, memDef); - } - } - } - this._buildDbType_modifyInstanceDefinition(dbEntityInstanceDefinition, storageModel, this); - var dbEntityClassDefinition = {}; - dbEntityClassDefinition.convertTo = this._buildDbType_generateConvertToFunction(storageModel, this); - this._buildDbType_modifyClassDefinition(dbEntityClassDefinition, storageModel, this); - - //create physical type - //TODO - storageModel.PhysicalType = $data.Class.define(storageModel.PhysicalTypeName, $data.Entity, storageModel.LogicalType.container, dbEntityInstanceDefinition, dbEntityClassDefinition); - } - }, - _initializeActions: function (es, ctor, esDef) { - if (esDef && esDef.actions) { - var actionKeys = Object.keys(esDef.actions); - for (var i = 0; i < actionKeys.length; i++) { - var actionName = actionKeys[i]; - var action = esDef.actions[actionName]; - if (typeof action === 'function') { - es[actionName] = action; - } else { - var actionDef = $data.MemberDefinition.translateDefinition(action, actionName, ctor); - if (actionDef instanceof $data.MemberDefinition && actionDef.kind === $data.MemberTypes.method) { - es[actionName] = actionDef.method; - } - } - } - } - }, - _buildDbType_navigationPropertyComplite: function (memDef, memDefResolvedDataType, storageModel) { - if (!memDef.inverseProperty) { - var refMemDefs = null; - if (memDefResolvedDataType === $data.Array || (memDefResolvedDataType.isAssignableTo && memDefResolvedDataType.isAssignableTo($data.EntitySet))) { - var refStorageModel = this._storageModel.getStorageModel(Container.resolveType(memDef.elementType)); - if (refStorageModel) { - refMemDefs = []; - var pubDefs = refStorageModel.LogicalType.memberDefinitions.getPublicMappedProperties(); - for (var i = 0; i < pubDefs.length; i++) { - var m = pubDefs[i]; - if ((m.inverseProperty == memDef.name) && (Container.resolveType(m.dataType) === Container.resolveType(storageModel.LogicalType))) - refMemDefs.push(m); - } - } - } else { - var refStorageModel = this._storageModel.getStorageModel(memDefResolvedDataType); - if (refStorageModel) { - refMemDefs = []; - var pubDefs = refStorageModel.LogicalType.memberDefinitions.getPublicMappedProperties(); - for (var i = 0; i < pubDefs.length; i++) { - var m = pubDefs[i]; - if (m.elementType && ((m.inverseProperty == memDef.name) && (Container.resolveType(m.elementType) === storageModel.LogicalType))) - refMemDefs.push(m); - else if ((m.inverseProperty == memDef.name) && (Container.resolveType(m.dataType) === storageModel.LogicalType)) - refMemDefs.push(m); - } - } - } - if (refMemDefs) { - if (refMemDefs.length > 1) { - if (typeof intellisense !== 'undefined') { - Guard.raise(new Exception('More than one inverse property refer to this member definition: ' + memDef.name + ', type: ' + Container.resolveName(storageModel.LogicalType))); - } - } - var refMemDef = refMemDefs.pop(); - if (refMemDef) { - memDef.inverseProperty = refMemDef.name; - } - } - } else { - var refStorageModel = null; - if (memDefResolvedDataType === $data.Array || (memDefResolvedDataType.isAssignableTo && memDefResolvedDataType.isAssignableTo($data.EntitySet))) { - refStorageModel = this._storageModel.getStorageModel(Container.resolveType(memDef.elementType)); - - } else { - refStorageModel = this._storageModel.getStorageModel(memDefResolvedDataType); - } - - var p = refStorageModel.LogicalType.memberDefinitions.getMember(memDef.inverseProperty); - if (p) { - if (p.inverseProperty) { - if (p.inverseProperty != memDef.name) { - if (typeof intellisense === 'undefined') { - Guard.raise(new Exception('Inverse property mismatch')); - } - } - } else { - p.inverseProperty = memDef.name; - } - } - - } - }, - _buildDbType_generateConvertToFunction: function (storageModel) { return function (instance) { return instance; }; }, - _buildDbType_modifyInstanceDefinition: function (instanceDefinition, storageModel) { return; }, - _buildDbType_modifyClassDefinition: function (classDefinition, storageModel) { return; }, - _buildDbType_addComplexTypePropertyDefinition: function (dbEntityInstanceDefinition, storageModel, memDef_dataType, memDef) { - this._addNavigationPropertyDefinition(dbEntityInstanceDefinition, memDef, memDef.name, $data.MemberTypes.complexProperty); - var complexType = this._createComplexElement(storageModel.LogicalType, "", memDef.name, memDef_dataType, "", ""); - storageModel.ComplexTypes[memDef.name] = complexType; - storageModel.ComplexTypes.push(complexType); - }, - _buildDbType_Collection_OneManyDefinition: function (dbEntityInstanceDefinition, storageModel, memDef_dataType, memDef) { - var refereedType = Container.resolveType(memDef.elementType); - if (refereedType === undefined || refereedType === null) { - if (typeof intellisense === 'undefined') { - Guard.raise(new Exception("Element type definition error", "Field definition", memDef)); - } - } - var refereedStorageModel = this._storageModel.getStorageModel(refereedType); - //var refereedStorageModel = this._storageModel.filter(function (s) { return s.LogicalType === refereedType; })[0]; - if (!refereedStorageModel) { - if (typeof intellisense === 'undefined') { - Guard.raise(new Exception("No EntitySet definition for the following element type", "Field definition", memDef)); - } - } - - this._addNavigationPropertyDefinition(dbEntityInstanceDefinition, memDef, memDef.name); - var associationType = memDef.inverseProperty === '$$unbound' ? '$$unbound' : '0..1'; - var association = this._addAssociationElement(storageModel.LogicalType, associationType, memDef.name, refereedStorageModel.LogicalType, "*", memDef.inverseProperty); - storageModel.Associations[memDef.name] = association; - storageModel.Associations.push(association); - }, - _buildDbType_ElementType_OneManyDefinition: function (dbEntityInstanceDefinition, storageModel, memDef_dataType, memDef) { - var refereedType = Container.resolveType(memDef.dataType); - if (refereedType === undefined || refereedType === null) { - if (typeof intellisense === 'undefined') { - Guard.raise(new Exception("Element type definition error", "Field definition", memDef)); - } - } - var refereedStorageModel = this._storageModel.getStorageModel(refereedType); - //var refereedStorageModel = this._storageModel.filter(function (s) { return s.LogicalType === refereedType; })[0]; - if (!refereedStorageModel) { - if (typeof intellisense === 'undefined') { - Guard.raise(new Exception("No EntitySet definition for the following element type", "Field definition", memDef)); - } - } - - this._addNavigationPropertyDefinition(dbEntityInstanceDefinition, memDef, memDef.name); - var associationType = memDef.inverseProperty === '$$unbound' ? '$$unbound' : '*'; - var association = this._addAssociationElement(storageModel.LogicalType, associationType, memDef.name, refereedStorageModel.LogicalType, "0..1", memDef.inverseProperty); - storageModel.Associations[memDef.name] = association; - storageModel.Associations.push(association); - }, - _buildDbType_ElementType_OneOneDefinition: function (dbEntityInstanceDefinition, storageModel, memDef_dataType, memDef) { - var refereedType = Container.resolveType(memDef.dataType); - if (refereedType === undefined || refereedType === null) { - if (typeof intellisense === 'undefined') { - Guard.raise(new Exception("Element type definition error", "Field definition", memDef)); - } - } - var refereedStorageModel = this._storageModel.getStorageModel(refereedType);; - //var refereedStorageModel = this._storageModel.filter(function (s) { return s.LogicalType === refereedType; })[0]; - if (!refereedStorageModel) { - if (typeof intellisense === 'undefined') { - Guard.raise(new Exception("No EntitySet definition following element type", "Field definition", memDef)); - } - } - - var refereedMemberDefinition = refereedStorageModel.LogicalType.memberDefinitions.getMember(memDef.inverseProperty); - if (!refereedMemberDefinition.required && !memDef.required) { if (typeof intellisense === 'undefined') { if (typeof intellisense === 'undefined') { Guard.raise(new Exception('In one to one connection, one side must required!', 'One to One connection', memDef)); } } } - - this._addNavigationPropertyDefinition(dbEntityInstanceDefinition, memDef, memDef.name); - - var association = this._addAssociationElement(storageModel.LogicalType, - memDef.required ? "0..1" : "1", - memDef.name, - refereedStorageModel.LogicalType, - memDef.required ? "1" : "0..1", - memDef.inverseProperty); - storageModel.Associations[memDef.name] = association; - storageModel.Associations.push(association); - }, - _addNavigationPropertyDefinition: function (definition, member, associationName, kind) { - var t = JSON.parse(JSON.stringify(member)); - t.dataType = $data.EntitySet; - t.notMapped = true; - t.kind = kind ? kind : $data.MemberTypes.navProperty; - t.association = associationName; - definition[member.name] = t; - }, - _addAssociationElement: function (fromType, fromMultiplicity, fromPropName, toType, toMultiplicity, toPropName) { - return new $data.Association({ - From: fromType.name, - FromType: fromType, - FromMultiplicity: fromMultiplicity, - FromPropertyName: fromPropName, - To: toType.name, - ToType: toType, - ToMultiplicity: toMultiplicity, - ReferentialConstraint: [], - ToPropertyName: toPropName - }); - }, - _createComplexElement: function (fromType, fromMultiplicity, fromPropName, toType, toMultiplicity, toPropName) { - return new $data.ComplexType({ - From: fromType.name, - FromType: fromType, - FromMultiplicity: fromMultiplicity, - FromPropertyName: fromPropName, - To: toType.name, - ToType: toType, - ToMultiplicity: toMultiplicity, - ReferentialConstraint: [], - ToPropertyName: toPropName - }); - }, - - _successInitProvider: function (context, error) { - if (context instanceof $data.EntityContext && context._isOK !== undefined) { - if (!error) { - context._isOK = true; - if (context.onReadyFunction) { - for (var i = 0; i < context.onReadyFunction.length; i++) { - context.onReadyFunction[i].success(context); - } - context.onReadyFunction = undefined; - } - } else { - context._isOK = error; - if (context.onReadyFunction) { - for (var i = 0; i < context.onReadyFunction.length; i++) { - context.onReadyFunction[i].error(error); - } - context.onReadyFunction = undefined; - } - } - } - }, - onReady: function (fn) { - /// - /// - /// Sets the callback function to be called when the initialization of the EntityContext has successfully finished. - /// - /// - /// Success callback - /// Current entityContext object - /// - /// - /// - /// - /// - /// Sets the callback functions to be called when the initialization of the EntityContext has finished. - /// - /// - /// Success and error callbacks definition. - /// Example: [code]{ success: function(db) { .. }, error: function() { .. } }[/code] - /// - /// - /// - var pHandler = new $data.PromiseHandler(); - var callBack = pHandler.createCallback(fn); - if (this._isOK === true) { - callBack.success(this); - } else if (this._isOK !== false) { - callBack.error(this._isOK); - } else { - this.onReadyFunction = this.onReadyFunction || []; - this.onReadyFunction.push(callBack); - } - - return pHandler.getPromise(); - }, - ready: { type: $data.Promise }, - getEntitySetFromElementType: function (elementType) { - /// - /// Gets the matching EntitySet for an element type. - /// - /// - /// - /// - /// Gets the matching EntitySet for an element type. - /// - /// - /// - var result = this._entitySetReferences[elementType]; - if (!result) { - try { - result = this._entitySetReferences[eval(elementType).name]; - } catch (ex) { } - } - return result; - }, - executeQuery: function (queryable, callBack, transaction) { - var query = new $data.Query(queryable.expression, queryable.defaultType, this); - query.transaction = transaction instanceof $data.Transaction ? transaction : undefined; - var returnTransaction = this._isReturnTransaction(transaction); - - callBack = $data.typeSystem.createCallbackSetting(callBack); - var that = this; - var clbWrapper = {}; - clbWrapper.success = function (query) { - if ($data.QueryCache && $data.QueryCache.isCacheable(that, query)) { - $data.QueryCache.addToCache(that, query); - } - - query.buildResultSet(that); - - if ($data.ItemStore && 'QueryResultModifier' in $data.ItemStore) - $data.ItemStore.QueryResultModifier.call(that, query); - - var successResult; - - if (query.expression.nodeType === $data.Expressions.ExpressionType.Single || - query.expression.nodeType === $data.Expressions.ExpressionType.Find || - query.expression.nodeType === $data.Expressions.ExpressionType.Count || - query.expression.nodeType === $data.Expressions.ExpressionType.BatchDelete || - query.expression.nodeType === $data.Expressions.ExpressionType.Some || - query.expression.nodeType === $data.Expressions.ExpressionType.Every) { - if (query.result.length !== 1) { - callBack.error(new Exception('result count failed')); - return; - } - - successResult = query.result[0]; - } else if (query.expression.nodeType === $data.Expressions.ExpressionType.First) { - if (query.result.length === 0) { - callBack.error(new Exception('result count failed')); - return; - } - - successResult = query.result[0]; - } else { - if (typeof query.__count === 'number' && query.result) - query.result.totalCount = query.__count; - - that.storageProvider._buildContinuationFunction(that, query); - - successResult = query.result; - } - - var readyFn = function () { - that._applyTransaction(callBack, callBack.success, [successResult], query.transaction, returnTransaction); - - /*if (returnTransaction === true) { - if (query.transaction) - callBack.success(successResult, query.transaction); - else { - that.beginTransaction(function (tran) { - callBack.success(successResult, tran); - }); - } - } - else - callBack.success(successResult);*/ - }; - - var i = 0; - var sets = query.getEntitySets(); - - var callbackFn = function () { - var es = sets[i]; - if (es.afterRead) { - i++; - var r = es.afterRead.call(this, successResult, sets, query); - if (typeof r === 'function') { - r.call(this, i < sets.length ? callbackFn : readyFn, successResult, sets, query); - } else { - if (i < sets.length) { - callbackFn(); - } else readyFn(); - } - } else readyFn(); - } - - if (sets.length) callbackFn(); - else readyFn(); - }; - - clbWrapper.error = function () { - if(returnTransaction) - callBack.error.apply(this, arguments); - else - callBack.error.apply(this, Array.prototype.filter.call(arguments, function (p) { return !(p instanceof $data.Transaction); })); - }; - var sets = query.getEntitySets(); - - var authorizedFn = function () { - var ex = true; - var wait = false; - var ctx = that; - - var readyFn = function (cancel) { - if (cancel === false) ex = false; - - if (ex) { - if (query.transaction) { - if ($data.QueryCache && $data.QueryCache.isInCache(that, query)) { - $data.QueryCache.executeQuery(that, query, clbWrapper); - } else { - ctx.storageProvider.executeQuery(query, clbWrapper); - } - } else { - ctx.beginTransaction(function (tran) { - query.transaction = tran; - if ($data.QueryCache && $data.QueryCache.isInCache(that, query)) { - $data.QueryCache.executeQuery(that, query, clbWrapper); - } else { - ctx.storageProvider.executeQuery(query, clbWrapper); - } - }); - } - } else { - query.rawDataList = []; - query.result = []; - clbWrapper.success(query); - } - }; - - var i = 0; - var callbackFn = function (cancel) { - if (cancel === false) ex = false; - - var es = sets[i]; - if (es.beforeRead) { - i++; - var r = es.beforeRead.call(this, sets, query); - if (typeof r === 'function') { - r.call(this, (i < sets.length && ex) ? callbackFn : readyFn, sets, query); - } else { - if (r === false) ex = false; - - if (i < sets.length && ex) { - callbackFn(); - } else readyFn(); - } - } else readyFn(); - }; - - if (sets.length) callbackFn(); - else readyFn(); - }; - - if (this.user && this.checkPermission) { - this.checkPermission(query.expression.nodeType === $data.Expressions.ExpressionType.BatchDelete ? $data.Access.DeleteBatch : $data.Access.Read, this.user, sets, { - success: authorizedFn, - error: clbWrapper.error - }); - } else authorizedFn(); - }, - saveChanges: function (callback, transaction) { - /// - /// - /// Saves the changes made to the context. - /// - /// - /// Success callback - /// Current entityContext object - /// - /// - /// - /// - /// - /// Saves the changes made to the context. - /// - /// - /// Success and error callbacks definition. - /// Example: [code]{ success: function(db) { .. }, error: function() { .. } }[/code] - /// - /// - /// - - if ($data.QueryCache) { - $data.QueryCache.reset(this); - } - - var changedEntities = []; - var trackedEntities = this.stateManager.trackedEntities; - var pHandler = new $data.PromiseHandler(); - var clbWrapper = pHandler.createCallback(callback); - var pHandlerResult = pHandler.getPromise(); - var returnTransaction = this._isReturnTransaction(transaction); - - var skipItems = []; - while (trackedEntities.length > 0) { - var additionalEntities = []; - //trackedEntities.forEach(function (entityCachedItem) { - for (var i = 0; i < trackedEntities.length; i++) { - var entityCachedItem = trackedEntities[i]; - - var sModel = this._storageModel.getStorageModel(entityCachedItem.data.getType()); - if (entityCachedItem.data.entityState == $data.EntityState.Unchanged) { - entityCachedItem.skipSave = true; - skipItems.push(entityCachedItem.data); - } else { - if (entityCachedItem.data.entityState == $data.EntityState.Modified) { - if (entityCachedItem.data.changedProperties) { - var changeStoredProperty = entityCachedItem.data.changedProperties.some(function (p) { - var pMemDef = sModel.PhysicalType.memberDefinitions.getMember(p.name); - if (pMemDef.kind == $data.MemberTypes.navProperty) { - var a = sModel.Associations[pMemDef.association]; - var multiplicity = a.FromMultiplicity + a.ToMultiplicity; - return ((multiplicity == '*0..1') || (multiplicity == '0..11')) - } - return true; - }); - if (!changeStoredProperty) { - entityCachedItem.skipSave = true; - skipItems.push(entityCachedItem.data); - } - } - } - } - - //type before events with items - this.processEntityTypeBeforeEventHandler(skipItems, entityCachedItem); - - var navigationProperties = []; - var smPhyMemDefs = sModel.PhysicalType.memberDefinitions.asArray(); - for (var ism = 0; ism < smPhyMemDefs.length; ism++) { - var p = smPhyMemDefs[ism]; - if (p.kind == $data.MemberTypes.navProperty) - navigationProperties.push(p); - } - //var navigationProperties = sModel.PhysicalType.memberDefinitions.asArray().filter(function (p) { return p.kind == $data.MemberTypes.navProperty; }); - //navigationProperties.forEach(function (navProp) { - for (var j = 0; j < navigationProperties.length; j++) { - var navProp = navigationProperties[j]; - - var association = sModel.Associations[navProp.name]; //eg.:"Profile" - var name = navProp.name; //eg.: "Profile" - var navPropertyName = association.ToPropertyName; //eg.: User - - var connectedDataList = [].concat(entityCachedItem.data[name]); - //connectedDataList.forEach(function (data) { - for (var k = 0; k < connectedDataList.length; k++) { - var data = connectedDataList[k]; - - if (data) { - var value = data[navPropertyName]; - var associationType = association.FromMultiplicity + association.ToMultiplicity; - if (association.FromMultiplicity === '$$unbound') { - if (data instanceof $data.Array) { - entityCachedItem.dependentOn = entityCachedItem.dependentOn || []; - //data.forEach(function (dataItem) { - for (var l = 0; l < data.length; l++) { - var dataItem = data[l]; - - if ((entityCachedItem.dependentOn.indexOf(data) < 0) && (data.skipSave !== true)) { - entityCachedItem.dependentOn.push(data); - } - } - //}, this); - } else { - entityCachedItem.dependentOn = entityCachedItem.dependentOn || []; - if ((entityCachedItem.dependentOn.indexOf(data) < 0) && (data.skipSave !== true)) { - entityCachedItem.dependentOn.push(data); - } - } - } else { - switch (associationType) { - case "*0..1": //Array - if (value) { - if (value instanceof Array) { - if (value.indexOf(entityCachedItem.data) == -1) { - value.push(entityCachedItem.data); - data.initData[navPropertyName] = value; - data._setPropertyChanged(association.ToType.getMemberDefinition(navPropertyName)); - } - } else { - if (typeof intellisense === 'undefined') { - Guard.raise("Item must be array or subtype of array"); - } - } - } else { - data.initData[navPropertyName] = [entityCachedItem.data]; - data._setPropertyChanged(association.ToType.getMemberDefinition(navPropertyName)); - } - break; - default: //Item - if (value) { - if (value !== entityCachedItem.data) { - if (typeof intellisense === 'undefined') { - Guard.raise("Integrity check error! Item assigned to another entity!"); - } - } - } else { - data.initData[navPropertyName] = entityCachedItem.data; //set back reference for live object - data._setPropertyChanged(association.ToType.getMemberDefinition(navPropertyName)); - } - break; - } - switch (associationType) { - case "*0..1": - case "0..11": - entityCachedItem.dependentOn = entityCachedItem.dependentOn || []; - if ((entityCachedItem.dependentOn.indexOf(data) < 0) && (data.skipSave !== true)) { - entityCachedItem.dependentOn.push(data); - } - break; - } - } - if (!data.entityState) { - if (data.storeToken === this.storeToken) { - data.entityState = $data.EntityState.Modified; - } else { - data.entityState = $data.EntityState.Added; - } - } - if (additionalEntities.indexOf(data) == -1) { - additionalEntities.push(data); - } - } - } - //}, this); - } - //}, this); - } - //}, this); - - //trackedEntities.forEach(function (entity) { - for (var i = 0; i < trackedEntities.length; i++) { - var entity = trackedEntities[i]; - - if (entity.skipSave !== true) { changedEntities.push(entity); } - } - //}); - - trackedEntities = []; - //additionalEntities.forEach(function (item) { - for (var i = 0; i < additionalEntities.length; i++) { - var item = additionalEntities[i]; - - if (!skipItems.some(function (entity) { return entity == item; })) { - if (!changedEntities.some(function (entity) { return entity.data == item; })) { - trackedEntities.push({ data: item, entitySet: this.getEntitySetFromElementType(item.getType().name) }); - } - } - } - //}, this); - } - - - //changedEntities.forEach(function (d) { - for (var j = 0; j < changedEntities.length; j++) { - var d = changedEntities[j]; - - if (d.dependentOn) { - var temp = []; - for (var i = 0; i < d.dependentOn.length; i++) { - if (skipItems.indexOf(d.dependentOn[i]) < 0) { - temp.push(d.dependentOn[i]); - } - } - d.dependentOn = temp; - } - } - //}); - skipItems = null; - var ctx = this; - if (changedEntities.length == 0) { - this.stateManager.trackedEntities.length = 0; - ctx._applyTransaction(clbWrapper, clbWrapper.success, [0], transaction, returnTransaction); - - /*if (returnTransaction) { - clbWrapper.success(0, transaction); - } else { - clbWrapper.success(0); - }*/ - return pHandlerResult; - } - - //validate entities - var errors = []; - //changedEntities.forEach(function (entity) { - for (var i = 0; i < changedEntities.length; i++) { - var entity = changedEntities[i]; - - if (entity.data.entityState === $data.EntityState.Added) { - //entity.data.getType().memberDefinitions.getPublicMappedProperties().forEach(function (memDef) { - for (var j = 0; j < entity.data.getType().memberDefinitions.getPublicMappedProperties().length; j++) { - var memDef = entity.data.getType().memberDefinitions.getPublicMappedProperties()[j]; - - var memDefType = Container.resolveType(memDef.type); - if (memDef.required && !memDef.computed && !entity.data[memDef.name] && !memDef.isDependentProperty) { - switch (memDefType) { - case $data.String: - case $data.Number: - case $data.Float: - case $data.Decimal: - case $data.Integer: - case $data.Int16: - case $data.Int32: - case $data.Int64: - case $data.Byte: - case $data.SByte: - case $data.Date: - case $data.Boolean: - entity.data[memDef.name] = Container.getDefault(memDef.dataType); - break; - default: - break; - } - } - } - //}, this); - } - if ((entity.data.entityState === $data.EntityState.Added || entity.data.entityState === $data.EntityState.Modified) - && !entity.data.isValid()) { - errors.push({ item: entity.data, errors: entity.data.ValidationErrors }); - } - } - //}); - if (errors.length > 0) { - clbWrapper.error(errors); - return pHandlerResult; - } - - var access = $data.Access.None; - - var eventData = {}; - var sets = []; - for (var i = 0; i < changedEntities.length; i++) { - var it = changedEntities[i]; - var n = it.entitySet.elementType.name; - if (sets.indexOf(it.entitySet) < 0) sets.push(it.entitySet); - var es = this._entitySetReferences[n]; - if (es.beforeCreate || es.beforeUpdate || es.beforeDelete || (this.user && this.checkPermission)) { - if (!eventData[n]) eventData[n] = {}; - - switch (it.data.entityState) { - case $data.EntityState.Added: - access |= $data.Access.Create; - if (es.beforeCreate) { - if (!eventData[n].createAll) eventData[n].createAll = []; - eventData[n].createAll.push(it); - } - break; - case $data.EntityState.Modified: - access |= $data.Access.Update; - if (es.beforeUpdate) { - if (!eventData[n].modifyAll) eventData[n].modifyAll = []; - eventData[n].modifyAll.push(it); - } - break; - case $data.EntityState.Deleted: - access |= $data.Access.Delete; - if (es.beforeDelete) { - if (!eventData[n].deleteAll) eventData[n].deleteAll = []; - eventData[n].deleteAll.push(it); - } - break; - } - } - } - - var readyFn = function (cancel) { - if (cancel === false) { - cancelEvent = 'async'; - changedEntities.length = 0; - } - - if (changedEntities.length) { - //console.log('changedEntities: ', changedEntities.map(function(it){ return it.data.initData; })); - - var innerCallback = { - success: function (tran) { - ctx._postProcessSavedItems(clbWrapper, changedEntities, tran, returnTransaction); - }, - error: function () { - //TODO remove trans from args; - if (returnTransaction) - clbWrapper.error.apply(this, arguments); - else - clbWrapper.error.apply(this, Array.prototype.filter.call(arguments, function (p) { return !(p instanceof $data.Transaction); })); - } - }; - - if (transaction instanceof $data.Transaction){ - ctx.storageProvider.saveChanges(innerCallback, changedEntities, transaction); - } else { - ctx.beginTransaction(true, function (tran) { - ctx.storageProvider.saveChanges(innerCallback, changedEntities, tran); - }); - } - } else if (cancelEvent) { - clbWrapper.error(new Exception('Cancelled event in ' + cancelEvent, 'CancelEvent')); - } else { - ctx._applyTransaction(clbWrapper, clbWrapper.success, [0], transaction, returnTransaction); - - /*if(returnTransaction) - clbWrapper.success(0, transaction); - else - clbWrapper.success(0);*/ - }; - - /*else if (cancelEvent) clbWrapper.error(new $data.Exception('saveChanges cancelled from event [' + cancelEvent + ']')); - else Guard.raise('No changed entities');*/ - }; - - var cancelEvent; - var ies = Object.getOwnPropertyNames(eventData); - var i = 0; - var cmd = ['beforeUpdate', 'beforeDelete', 'beforeCreate']; - var cmdAll = { - beforeCreate: 'createAll', - beforeDelete: 'deleteAll', - beforeUpdate: 'modifyAll' - }; - - var callbackFn = function (cancel) { - if (cancel === false) { - cancelEvent = 'async'; - changedEntities.length = 0; - - readyFn(cancel); - return; - } - - var es = ctx._entitySetReferences[ies[i]]; - var c = cmd.pop(); - var ed = eventData[ies[i]]; - var all = ed[cmdAll[c]]; - - if (all) { - var m = []; - for (var im = 0; im < all.length; im++) { - m.push(all[im].data); - } - //var m = all.map(function(it){ return it.data; }); - if (!cmd.length) { - cmd = ['beforeUpdate', 'beforeDelete', 'beforeCreate']; - i++; - } - - var r = es[c].call(ctx, m); - if (typeof r === 'function') { - r.call(ctx, (i < ies.length && !cancelEvent) ? callbackFn : readyFn, m); - } else if (r === false) { - cancelEvent = (es.name + '.' + c); - //all.forEach(function (it) { - for (var index = 0; index < all.length; index++) { - var it = all[index]; - - var ix = changedEntities.indexOf(it); - changedEntities.splice(ix, 1); - } - //}); - - readyFn(); - } else { - if (i < ies.length && !cancelEvent) callbackFn(); - else readyFn(); - } - } else { - if (!cmd.length) { - cmd = ['beforeUpdate', 'beforeDelete', 'beforeCreate']; - i++; - } - - if (i < ies.length && !cancelEvent) callbackFn(); - else readyFn(); - } - }; - - if (this.user && this.checkPermission) { - this.checkPermission(access, this.user, sets, { - success: function () { - if (i < ies.length) callbackFn(); - else readyFn(); - }, - error: clbWrapper.error - }); - } else { - if (i < ies.length) callbackFn(); - else readyFn(); - } - - return pHandlerResult; - }, - - processEntityTypeBeforeEventHandler: function (skipItems, entityCachedItem) { - if (!entityCachedItem.skipSave) { - var entity = entityCachedItem.data; - var entityType = entity.getType(); - var state = entity.entityState; - - switch (true) { - case state === $data.EntityState.Added && entityType.onbeforeCreate instanceof $data.Event: - if (entityType.onbeforeCreate.fireCancelAble(entity) === false) { - entityCachedItem.skipSave = true; - skipItems.push(entity); - } - break; - case state === $data.EntityState.Modified && entityType.onbeforeUpdate instanceof $data.Event: - if (entityType.onbeforeUpdate.fireCancelAble(entity) === false) { - entityCachedItem.skipSave = true; - skipItems.push(entity); - } - break; - case state === $data.EntityState.Deleted && entityType.onbeforeDelete instanceof $data.Event: - if (entityType.onbeforeDelete.fireCancelAble(entity) === false) { - entityCachedItem.skipSave = true; - skipItems.push(entity); - } - break; - default: - break; - } - } - }, - processEntityTypeAfterEventHandler: function (entityCachedItem) { - var entity = entityCachedItem.data; - var entityType = entity.getType(); - var state = entity.entityState; - - switch (true) { - case state === $data.EntityState.Added && entityType.onafterCreate instanceof $data.Event: - entityType.onafterCreate.fire(entity); - break; - case state === $data.EntityState.Modified && entityType.onafterUpdate instanceof $data.Event: - entityType.onafterUpdate.fire(entity); - break; - case state === $data.EntityState.Deleted && entityType.onafterDelete instanceof $data.Event: - entityType.onafterDelete.fire(entity); - break; - default: - break; - } - }, - - bulkInsert: function (entitySet, fields, datas, callback) { - var pHandler = new $data.PromiseHandler(); - callback = pHandler.createCallback(callback); - if (typeof entitySet === 'string') { - var currentEntitySet; - - for (var entitySetName in this._entitySetReferences) { - var actualEntitySet = this._entitySetReferences[entitySetName]; - if (actualEntitySet.tableName === entitySet) { - currentEntitySet = actualEntitySet; - break; - } - } - - if (!currentEntitySet) - currentEntitySet = this[entitySet]; - - entitySet = currentEntitySet; - } - if (entitySet) { - this.storageProvider.bulkInsert(entitySet, fields, datas, callback); - } else { - callback.error(new Exception('EntitySet not found')); - } - return pHandler.getPromise(); - }, - - prepareRequest: function () { }, - _postProcessSavedItems: function (callBack, changedEntities, transaction, returnTransaction) { - if (this.ChangeCollector && this.ChangeCollector instanceof $data.Notifications.ChangeCollectorBase) - this.ChangeCollector.processChangedData(changedEntities); - - var eventData = {}; - var ctx = this; - //changedEntities.forEach(function (entity) { - for (var i = 0; i < changedEntities.length; i++) { - var entity = changedEntities[i]; - - if (!entity.data.storeToken) - entity.data.storeToken = ctx.storeToken; - - //type after events with items - this.processEntityTypeAfterEventHandler(entity); - - var oes = entity.data.entityState; - - entity.data.entityState = $data.EntityState.Unchanged; - entity.data.changedProperties = []; - entity.physicalData = undefined; - - var n = entity.entitySet.elementType.name; - var es = ctx._entitySetReferences[n]; - - - var eventName = undefined; - switch (oes) { - case $data.EntityState.Added: - eventName = 'added'; - break; - case $data.EntityState.Deleted: - eventName = 'deleted'; - break; - case $data.EntityState.Modified: - eventName = 'updated'; - break; - } - if (eventName) { - this.raiseEvent(eventName, entity); - } - - if (es.afterCreate || es.afterUpdate || es.afterDelete) { - if (!eventData[n]) eventData[n] = {}; - - switch (oes) { - case $data.EntityState.Added: - if (es.afterCreate) { - if (!eventData[n].createAll) eventData[n].createAll = []; - eventData[n].createAll.push(entity); - } - break; - case $data.EntityState.Modified: - if (es.afterUpdate) { - if (!eventData[n].modifyAll) eventData[n].modifyAll = []; - eventData[n].modifyAll.push(entity); - } - break; - case $data.EntityState.Deleted: - if (es.afterDelete) { - if (!eventData[n].deleteAll) eventData[n].deleteAll = []; - eventData[n].deleteAll.push(entity); - } - break; - } - } - } - //}); - - var ies = Object.getOwnPropertyNames(eventData); - var i = 0; - var ctx = this; - var cmd = ['afterUpdate', 'afterDelete', 'afterCreate']; - var cmdAll = { - afterCreate: 'createAll', - afterDelete: 'deleteAll', - afterUpdate: 'modifyAll' - }; - - var readyFn = function () { - if (!ctx.trackChanges) { - ctx.stateManager.reset(); - } - - ctx._applyTransaction(callBack, callBack.success, [changedEntities.length], transaction, returnTransaction); - - /*if (returnTransaction) - callBack.success(changedEntities.length, transaction); - else - callBack.success(changedEntities.length);*/ - }; - - var callbackFn = function () { - var es = ctx._entitySetReferences[ies[i]]; - var c = cmd.pop(); - var ed = eventData[ies[i]]; - var all = ed[cmdAll[c]]; - if (all) { - var m = []; - for (var im = 0; im < all.length; im++) { - m.push(all[im].data); - } - //var m = all.map(function(it){ return it.data; }); - if (!cmd.length) { - cmd = ['afterUpdate', 'afterDelete', 'afterCreate']; - i++; - } - - var r = es[c].call(ctx, m); - if (typeof r === 'function') { - r.call(ctx, i < ies.length ? callbackFn : readyFn, m); - } else { - if (i < ies.length) callbackFn(); - else readyFn(); - } - } else { - if (!cmd.length) { - cmd = ['afterUpdate', 'afterDelete', 'afterCreate']; - i++; - } - - if (i < ies.length) callbackFn(); - else readyFn(); - } - }; - - if (i < ies.length) callbackFn(); - else readyFn(); - }, - forEachEntitySet: function (fn, ctx) { - /// - /// Iterates over the entity sets' of current EntityContext. - /// - /// - /// - /// - /// 'this' argument for the 'fn' function. - for (var entitySetName in this._entitySetReferences) { - var actualEntitySet = this._entitySetReferences[entitySetName]; - fn.call(ctx, actualEntitySet); - } - }, - - loadItemProperty: function (entity, property, callback, transaction) { - /// - /// Loads a property of the entity through the storage provider. - /// Entity object - /// Property name - /// - /// C allback function - /// - /// - /// - /// - /// - /// Loads a property of the entity through the storage provider. - /// Entity object - /// Property name - /// - /// Success and error callbacks definition. - /// Example: [code]{ success: function(db) { .. }, error: function() { .. } }[/code] - /// - /// - /// - /// - /// Loads a property of the entity through the storage provider. - /// Entity object - /// Property definition - /// - /// Callback function - /// - /// - /// - /// - /// - /// Loads a property of the entity through the storage provider. - /// Entity object - /// Property definition - /// - /// Success and error callbacks definition. - /// Example: [code]{ success: function(db) { .. }, error: function() { .. } }[/code] - /// - /// - /// - Guard.requireType('entity', entity, $data.Entity); - - var memberDefinition = typeof property === 'string' ? entity.getType().memberDefinitions.getMember(property) : property; - var returnTransaction = this._isReturnTransaction(transaction); - - if (entity[memberDefinition.name] != undefined) { - - var pHandler = new $data.PromiseHandler(); - callBack = pHandler.createCallback(callback); - this._applyTransaction(callback, callback.success, [entity[memberDefinition.name]], transaction, returnTransaction); - /*if (returnTransaction) - callback.success(entity[memberDefinition.name], transaction); - else - callback.success(entity[memberDefinition.name]);*/ - - return pHandler.getPromise(); - } - - var isSingleSide = true; - var storageModel = this._storageModel.getStorageModel(entity.getType().fullName); - var elementType = Container.resolveType(memberDefinition.dataType); - if (elementType === $data.Array || (elementType.isAssignableTo && elementType.isAssignableTo($data.EntitySet))) { - elementType = Container.resolveType(memberDefinition.elementType); - - isSingleSide = false; - - } else { - var associations; - for (var i = 0; i < storageModel.Associations.length; i++) { - var assoc = storageModel.Associations[i]; - if (assoc.FromPropertyName == memberDefinition.name) { - associations = assoc; - break; - } - } - //var associations = storageModel.Associations.filter(function (assoc) { return assoc.FromPropertyName == memberDefinition.name; })[0]; - if (associations && associations.FromMultiplicity === "0..1" && associations.ToMultiplicity === "1") - isSingleSide = false; - } - - var keyProp = storageModel.LogicalType.memberDefinitions.getKeyProperties(); - if (isSingleSide === true) { - //singleSide - - var filterFunc = "function (e) { return"; - var filterParams = {}; - //storageModel.LogicalType.memberDefinitions.getKeyProperties().forEach(function (memDefKey, index) { - for (var index = 0; index < keyProp.length; index++) { - var memDefKey = keyProp[index]; - - if (index > 0) - filterFunc += ' &&'; - filterFunc += " e." + memDefKey.name + " == this.key" + index; - filterParams['key' + index] = entity[memDefKey.name]; - } - //}); - filterFunc += "; }" - - var entitySet = this.getEntitySetFromElementType(entity.getType()); - return entitySet - .map('function (e) { return e.' + memberDefinition.name + ' }') - .single(filterFunc, filterParams, callback, transaction); - } else { - //multipleSide - - var filterFunc = "function (e) { return" - var filterParams = {}; - //storageModel.LogicalType.memberDefinitions.getKeyProperties().forEach(function (memDefKey, index) { - for (var index = 0; index < keyProp.length; index++) { - var memDefKey = keyProp[index]; - - if (index > 0) - filterFunc += ' &&'; - filterFunc += " e." + memberDefinition.inverseProperty + "." + memDefKey.name + " == this.key" + index; - filterParams['key' + index] = entity[memDefKey.name]; - } - //}); - filterFunc += "; }" - - var entitySet = this.getEntitySetFromElementType(elementType); - return entitySet - .filter(filterFunc, filterParams) - .toArray(callback, transaction); - } - - }, - - getTraceString: function (queryable) { - /// - /// Returns a trace string. Used for debugging purposes! - /// - /// - /// Trace string - var query = new $data.Query(queryable.expression, queryable.defaultType, this); - return this.storageProvider.getTraceString(query); - }, - log: function (logInfo) { - //noop as do nothing - }, - - resolveBinaryOperator: function (operator, expression, frameType) { - return this.storageProvider.resolveBinaryOperator(operator, expression, frameType); - }, - resolveUnaryOperator: function (operator, expression, frameType) { - return this.storageProvider.resolveUnaryOperator(operator, expression, frameType); - }, - resolveFieldOperation: function (operation, expression, frameType) { - return this.storageProvider.resolveFieldOperation(operation, expression, frameType); - }, - resolveSetOperations: function (operation, expression, frameType) { - return this.storageProvider.resolveSetOperations(operation, expression, frameType); - }, - resolveTypeOperations: function (operation, expression, frameType) { - return this.storageProvider.resolveTypeOperations(operation, expression, frameType); - }, - resolveContextOperations: function (operation, expression, frameType) { - return this.storageProvider.resolveContextOperations(operation, expression, frameType); - }, - - _generateServiceOperationQueryable: function (functionName, returnEntitySet, arg, parameters) { - if (typeof console !== 'undefined' && console.log) - console.log('Obsolate: _generateServiceOperationQueryable, $data.EntityContext'); - - var params = []; - for (var i = 0; i < parameters.length; i++) { - var obj = {}; - obj[parameters[i]] = Container.resolveType(Container.getTypeName(arg[i])); - params.push(obj); - } - - var tempOperation = $data.EntityContext.generateServiceOperation({ serviceName: functionName, returnType: $data.Queryable, elementType: this[returnEntitySet].elementType, params: params }); - return tempOperation.apply(this, arg); - }, - attach: function (entity, mode) { - /// - /// Attaches an entity to its matching entity set. - /// - /// - /// Returns the attached entity. - - if (entity instanceof $data.EntityWrapper) { - entity = entity.getEntity(); - } - var entitySet = this.getEntitySetFromElementType(entity.getType()); - return entitySet.attach(entity, mode); - }, - attachOrGet: function (entity, mode) { - /// - /// Attaches an entity to its matching entity set, or returns if it's already attached. - /// - /// - /// Returns the entity. - - if (entity instanceof $data.EntityWrapper) { - entity = entity.getEntity(); - } - var entitySet = this.getEntitySetFromElementType(entity.getType()); - return entitySet.attachOrGet(entity, mode); - }, - - addMany: function (entities) { - /// - /// Adds several entities to their matching entity set. - /// - /// - /// Returns the added entities. - var self = this; - entities.forEach(function (entity) { - self.add(entity); - }); - return entities; - }, - - add: function (entity) { - /// - /// Adds a new entity to its matching entity set. - /// - /// - /// Returns the added entity. - - if (entity instanceof $data.EntityWrapper) { - entity = entity.getEntity(); - } - var entitySet = this.getEntitySetFromElementType(entity.getType()); - return entitySet.add(entity); - }, - remove: function (entity) { - /// - /// Removes an entity from its matching entity set. - /// - /// - /// Returns the removed entity. - - if (entity instanceof $data.EntityWrapper) { - entity = entity.getEntity(); - } - var entitySet = this.getEntitySetFromElementType(entity.getType()); - return entitySet.remove(entity); - }, - storeToken: { type: Object }, - - getFieldUrl: function (entity, member, collection) { - try { - var entitySet = typeof collection === 'string' ? this[collection] : collection; - var fieldName = typeof member === 'string' ? member : member.name; - if (entity instanceof $data.Entity) { - entitySet = this.getEntitySetFromElementType(entity.getType()); - } else if (!Object.isNullOrUndefined(entity) && entity.constructor !== $data.Object) { //just a single key - var keyDef = entitySet.elementType.memberDefinitions.getKeyProperties()[0]; - var key = {}; - key[keyDef.name] = entity; - entity = key; - } - - //key object - if (!(entity instanceof $data.Entity)) { - entity = new entitySet.elementType(entity); - } - - return this.storageProvider.getFieldUrl(entity, fieldName, entitySet); - } catch (e) {} - return '#'; - } -}, { - inheritedTypeProcessor: function(type) { - if (type.resolveForwardDeclarations) { - type.resolveForwardDeclarations(); - } - }, - generateServiceOperation: function (cfg) { - - var fn; - if (cfg.serviceMethod) { - var returnType = cfg.returnType ? Container.resolveType(cfg.returnType) : {}; - if (returnType.isAssignableTo && returnType.isAssignableTo($data.Queryable)) { - fn = cfg.serviceMethod; - } else { - fn = function () { - var lastParam = arguments[arguments.length - 1]; - - var pHandler = new $data.PromiseHandler(); - var cbWrapper; - - var args = arguments; - if (typeof lastParam === 'function') { - cbWrapper = pHandler.createCallback(lastParam); - arguments[arguments.length - 1] = cbWrapper; - } else { - cbWrapper = pHandler.createCallback(); - arguments.push(cbWrapper); - } - - try { - var result = cfg.serviceMethod.apply(this, arguments); - if (result !== undefined) - cbWrapper.success(result); - } catch (e) { - cbWrapper.error(e); - } - - return pHandler.getPromise(); - } - } - - } else { - fn = function () { - var context = this; - - var boundItem; - if (this instanceof $data.Entity) { - if (!cfg.method) { - cfg.method = 'POST'; - } - - if (this.context) { - context = this.context; - } else { - Guard.raise('entity not attached into context'); - return; - } - - boundItem = { - data: this, - entitySet: context.getEntitySetFromElementType(this.getType()) - }; - } - - var virtualEntitySet = cfg.elementType ? context.getEntitySetFromElementType(Container.resolveType(cfg.elementType)) : null; - - var paramConstExpression = null; - if (cfg.params) { - paramConstExpression = []; - for (var i = 0; i < cfg.params.length; i++) { - //TODO: check params type - for (var name in cfg.params[i]) { - paramConstExpression.push(Container.createConstantExpression(arguments[i], Container.resolveType(cfg.params[i][name]), name)); - } - } - } - - var ec = Container.createEntityContextExpression(context); - var memberdef = (boundItem ? boundItem.data : context).getType().getMemberDefinition(cfg.serviceName); - var es = Container.createServiceOperationExpression(ec, - Container.createMemberInfoExpression(memberdef), - paramConstExpression, - cfg, - boundItem); - - //Get callback function - var clb = arguments[arguments.length - 1]; - if (typeof clb !== 'function') { - clb = undefined; - } - - if (virtualEntitySet) { - var q = Container.createQueryable(virtualEntitySet, es); - if (clb) { - es.isTerminated = true; - return q._runQuery(clb); - } - return q; - } - else { - var returnType = cfg.returnType ? Container.resolveType(cfg.returnType) : null; - - var q = Container.createQueryable(context, es); - q.defaultType = returnType || $data.Object; - - if (returnType === $data.Queryable) { - q.defaultType = Container.resolveType(cfg.elementType); - if (clb) { - es.isTerminated = true; - return q._runQuery(clb); - } - return q; - } - es.isTerminated = true; - return q._runQuery(clb); - } - }; - }; - - var params = []; - if (cfg.params) { - for (var i = 0; i < cfg.params.length; i++) { - var param = cfg.params[i]; - for (var name in param) { - params.push({ - name: name, - type: param[name] - }); - } - } - } - $data.typeSystem.extend(fn, cfg, { params: params }); - - return fn; - }, - _convertLogicalTypeNameToPhysical: function (name) { - return name + '_$db$'; - }, - _storageModelCache: { - get: function () { - if (!this.__storageModelCache) - this.__storageModelCache = {}; - return this.__storageModelCache; - }, - set: function () { - //todo exception - } - } -}); -$data.Class.define('$data.QueryProvider', null, null, -{ - //TODO: instance member????? - constructor: function () { this.requiresExpressions= false }, - executeQuery: function (queryable, resultHandler) { - }, - getTraceString: function (queryable) { - } -}, null);$data.Class.define('$data.ModelBinder', null, null, { - - constructor: function (context) { - this.context = context; - this.providerName = null; - if (this.context.storageProvider && typeof this.context.storageProvider.getType === 'function') { - this.references = !(this.context.storageProvider.providerConfiguration.modelBinderOptimization || false); - for (var i in $data.RegisteredStorageProviders) { - if ($data.RegisteredStorageProviders[i] === this.context.storageProvider.getType()) { - this.providerName = i; - } - } - } - }, - - _deepExtend: function (o, r) { - if (o === null || o === undefined) { - return r; - } - for (var i in r) { - if (o.hasOwnProperty(i)) { - if (typeof r[i] === 'object') { - if (Array.isArray(r[i])) { - for (var j = 0; j < r[i].length; j++) { - if (o[i].indexOf(r[i][j]) < 0) { - o[i].push(r[i][j]); - } - } - } else this._deepExtend(o[i], r[i]); - } - } else { - o[i] = r[i]; - } - } - return this._finalize(o); - }, - - _finalize: function(o){ - if (o instanceof $data.Entity) { - o.changedProperties = undefined; - o.storeToken = this.context.storeToken; - } - return o; - }, - - _buildSelector: function (meta, context) { - if (meta.$selector) { - if (!(Array.isArray(meta.$selector))) { - meta.$selector = [meta.$selector]; - } - - for (var i = 0; i < meta.$selector.length; i++) { - var selector = meta.$selector[i].replace('json:', ''); - context.src += 'if('; - var path = selector.split('.'); - for (var j = 0; j < path.length; j++) { - context.src += 'di.' + path.slice(0, j + 1).join('.') + (j < path.length - 1 ? ' && ' : ' !== undefined && typeof di.' + selector + ' === "object"'); - } - context.src += '){di = di.' + selector + ';}' + (i < meta.$selector.length - 1 ? 'else ' : ''); - } - - context.src += 'if (di === null){'; - if (context.iter) context.src += context.iter + ' = null;'; - context.src += 'return null;'; - context.src += '}'; - } - }, - - _buildKey: function (name, type, keys, context, data) { - if (keys) { - var type = Container.resolveType(type); - var typeIndex = Container.getIndex(type); - type = type.fullName || type.name; - context.src += 'var ' + name + 'Fn = function(di){'; - if (!(Array.isArray(keys)) || keys.length == 1) { - if (typeof keys !== 'string') keys = keys[0]; - context.src += 'if (typeof di.' + keys + ' === "undefined") return undefined;'; - context.src += 'if (di.' + keys + ' === null) return null;'; - context.src += 'var key = ("' + type + '_' + typeIndex + '_' + keys + '#" + di.' + keys + ');'; - } else { - context.src += 'var key = "";'; - for (var i = 0; i < keys.length; i++) { - var id = typeof keys[i] !== 'object' ? keys[i] : keys[i].$source; - context.src += 'if (typeof di.' + id + ' === "undefined") return undefined;'; - context.src += 'if (di.' + id + ' === null) return null;'; - context.src += 'key += ("' + type + '_' + typeIndex + '_' + id + '#" + di.' + id + ');'; - } - } - - context.src += 'return key;};'; - } - - context.src += 'var ' + name + ' = ' + (keys ? name + 'Fn(' + (data || 'di') + ')' : 'undefined') + ';'; - }, - - build: function (meta, context) { - if (meta.$selector) { - if (!(Array.isArray(meta.$selector))) meta.$selector = [meta.$selector]; - for (var i = 0; i < meta.$selector.length; i++) { - meta.$selector[i] = meta.$selector[i].replace('json:', ''); - } - } - - if (meta.$value) { - if (typeof meta.$value === 'function') { - context.src += 'var di = di || data;'; - context.src += 'var fn = function(){ return meta' + (context.meta.length ? '.' + context.meta.join('.') : '') + '.$value.call(self, meta' + (context.meta.length ? '.' + context.meta.join('.') : '') + ', di); };'; - if (meta.$type) { - var type = Container.resolveName(Container.resolveType(meta.$type)); - var typeIndex = Container.getIndex(Container.resolveType(meta.$type)); - var converter = this.context.storageProvider.fieldConverter.fromDb[type]; - if (converter) { - context.item = 'self.context.storageProvider.fieldConverter.fromDb["' + type + '"](fn())'; - } else { - context.item = 'new (Container.resolveByIndex(' + typeIndex + '))(fn())'; - } - } else context.item = 'fn()'; - } else if (meta.$type) { - var type = Container.resolveName(Container.resolveType(meta.$type)); - var typeIndex = Container.getIndex(Container.resolveType(meta.$type)); - var converter = this.context.storageProvider.fieldConverter.fromDb[type]; - if (converter) { - context.item = 'self.context.storageProvider.fieldConverter.fromDb["' + type + '"](' + meta.$value + ')'; - } else { - context.item = 'new (Container.resolveByIndex(' + typeIndex + '))(' + meta.$value + ')'; - } - } else context.item = meta.$value; - } else if (meta.$source) { - var type = Container.resolveName(Container.resolveType(meta.$type)); - var typeIndex = Container.getIndex(Container.resolveType(meta.$type)); - var converter = this.context.storageProvider.fieldConverter.fromDb[type]; - var item = '_' + type.replace(/\./gi, '_') + '_'; - if (!context.forEach) context.src += 'var di = data;'; - context.item = item; - this._buildSelector(meta, context); - if (converter) { - context.src += 'var ' + item + ' = self.context.storageProvider.fieldConverter.fromDb["' + type + '"](di.' + meta.$source + ');'; - } else { - context.src += 'var ' + item + ' = new (Container.resolveByIndex(' + typeIndex + '))(di.' + meta.$source + ');'; - } - } else if (meta.$item) { - context.meta.push('$item'); - var iter = (context.item && context.current ? context.item + '.' + context.current : (context.item ? context.item : 'result')); - context.iter = iter; - if (iter.indexOf('.') < 0) context.src += 'var ' + iter + ';'; - context.src += 'var fn = function(di){'; - if (meta.$selector) { - context.src += 'if (typeof di !== "undefined" && !(Array.isArray(di))){'; - this._buildSelector(meta, context); - context.src += '}'; - } - if (this.references && meta.$keys) this._buildKey('forKey', meta.$type, meta.$keys, context); - //else if (this.references && meta.$item && meta.$item.$keys) this._buildKey('forKey', meta.$type, meta.$item.$keys, context); - //else context.src += 'var forKey = typeof itemKey !== "undefined" ? itemKey : undefined;'; - /*context.src += 'if (typeof forKey !== "undefined" && forKey){'; - context.src += 'if (cache[forKey]){'; - context.src += iter + ' = cache[forKey];'; - context.src += '}else{'; - context.src += iter + ' = [];'; - context.src += 'cache[forKey] = ' + iter + ';'; - context.src += '}'; - context.src += '}else{'; - context.src += iter + ' = [];'; - context.src += '}';*/ - context.src += iter + ' = typeof ' + iter + ' == "undefined" ? [] : ' + iter + ';'; - //context.src += iter + ' = [];'; - if (this.references && meta.$item.$keys) { - var keycacheName = 'keycache_' + iter.replace(/\./gi, '_'); - context.src += 'var ' + keycacheName + ';'; - context.src += 'var kci = keycacheIter.indexOf(' + iter + ');'; - context.src += 'if (kci < 0){'; - context.src += keycacheName + ' = [];'; - context.src += 'keycache.push(' + keycacheName + ');'; - context.src += 'keycacheIter.push(' + iter + ');'; - context.src += '}else{'; - context.src += keycacheName + ' = keycache[kci];'; - context.src += '}'; - //context.src += 'var ' + keycacheName + ' = ' + (meta.$item.$keys ? '[]' : 'null') + ';'; - } - context.iter = undefined; - context.forEach = true; - var itemForKey = 'itemForKey_' + iter.replace(/\./gi, '_'); - context.src += 'var forEachFn = function(di, i){'; - context.src += 'var diBackup = di;'; - if (this.providerName == "sqLite" && this.references && meta.$item.$keys) this._buildKey(itemForKey, meta.$type, meta.$item.$keys, context); - var item = context.item || 'iter'; - context.item = item; - if (!meta.$item.$source) { - this._buildSelector(meta.$item, context); - } - this.build(meta.$item, context); - if (this.references && meta.$keys) { - context.src += 'if (forKey){'; - context.src += 'if (cache[forKey]){'; - context.src += iter + ' = cache[forKey];'; - context.src += 'if (' + iter + '.indexOf(' + (context.item || item) + ') < 0){'; - context.src += iter + '.push(' + (context.item || item) + ');'; - context.src += '}}else{'; - context.src += 'cache[forKey] = ' + iter + ';'; - context.src += iter + '.push(' + (context.item || item) + ');'; - context.src += '}}else{'; - if (this.references && meta.$item.$keys) this._buildKey('cacheKey', meta.$type, meta.$item.$keys, context, 'diBackup'); - context.src += 'if (typeof cacheKey != "undefined" && cacheKey !== null){'; - context.src += 'if (keycache_' + iter.replace(/\./gi, '_') + ' && cacheKey){'; - context.src += 'if (keycache_' + iter.replace(/\./gi, '_') + '.indexOf(cacheKey) < 0){'; - context.src += iter + '.push(' + (context.item || item) + ');'; - context.src += 'keycache_' + iter.replace(/\./gi, '_') + '.push(cacheKey);'; - context.src += '}'; - context.src += '}else{'; - context.src += iter + '.push(' + (context.item || item) + ');'; - context.src += '}'; - context.src += '}'; - context.src += '}'; - } else { - if (this.references && meta.$item.$keys) { - context.src += 'if (typeof ' + itemForKey + ' !== "undefined" && ' + itemForKey + ' !== null){'; - context.src += 'if (typeof keycache_' + iter.replace(/\./gi, '_') + ' !== "undefined" && ' + itemForKey + '){'; - context.src += 'if (keycache_' + iter.replace(/\./gi, '_') + '.indexOf(' + itemForKey + ') < 0){'; - context.src += iter + '.push(' + (context.item || item) + ');'; - context.src += 'keycache_' + iter.replace(/\./gi, '_') + '.push(' + itemForKey + ');' - context.src += '}}else{'; - context.src += iter + '.push(' + (context.item || item) + ');'; - context.src += '}}else{'; - context.src += iter + '.push(' + (context.item || item) + ');'; - context.src += '}'; - /*context.src += 'if (typeof itemKey !== "undefined" && itemKey !== null){'; - context.src += 'if (typeof keycache_' + iter.replace(/\./gi, '_') + ' !== "undefined" && itemKey){'; - context.src += 'if (keycache_' + iter.replace(/\./gi, '_') + '.indexOf(itemKey) < 0){'; - context.src += iter + '.push(' + (context.item || item) + ');'; - context.src += 'keycache_' + iter.replace(/\./gi, '_') + '.push(itemKey);' - context.src += '}}else{'; - context.src += iter + '.push(' + (context.item || item) + ');'; - context.src += '}}else{'; - context.src += iter + '.push(' + (context.item || item) + ');'; - context.src += '}';*/ - } else { - context.src += iter + '.push(' + (context.item || item) + ');'; - } - } - context.src += '};'; - context.src += 'if (Array.isArray(di)) di.forEach(forEachFn);'; - context.src += 'else forEachFn(di, 0);'; - context.forEach = false; - context.item = null; - context.src += '};fn(typeof di === "undefined" ? data : di);' - context.meta.pop(); - } else if (meta.$type) { - if (!context.forEach) { - context.src += 'if (typeof di === "undefined"){'; - context.src += 'var di = data;'; - this._buildSelector(meta, context); - context.src += '}'; - } - var resolvedType = Container.resolveType(meta.$type); - var type = Container.resolveName(resolvedType); - var typeIndex = Container.getIndex(resolvedType); - var isEntityType = resolvedType.isAssignableTo && resolvedType.isAssignableTo($data.Entity); - var item = '_' + type.replace(/\./gi, '_') + '_'; - if (context.item == item) item += 'new_'; - context.item = item; - - - var isPrimitive = false; - if (!meta.$source && !meta.$value && resolvedType !== $data.Array && resolvedType !== $data.Object && !resolvedType.isAssignableTo) - isPrimitive = true; - if (resolvedType === $data.Object || resolvedType === $data.Array) { - var keys = Object.keys(meta); - if (keys.length == 1 || (keys.length == 2 && meta.$selector)) isPrimitive = true; - } - - if (isPrimitive) { - var converter = this.context.storageProvider.fieldConverter.fromDb[type]; - if (converter) { - context.src += 'var ' + item + ' = di != undefined ? self.context.storageProvider.fieldConverter.fromDb["' + type + '"](di) : di;'; - } else { - context.src += 'var ' + item + ' = di;'; - } - } else { - if (this.references && meta.$keys) { - this._buildKey('itemKey', meta.$type, meta.$keys, context); - context.src += 'if (itemKey === null) return null;'; - context.src += 'var ' + item + ';'; - context.src += 'if (itemKey && cache[itemKey]){'; - context.src += item + ' = cache[itemKey];'; - context.src += '}else{'; - if (isEntityType) { - context.src += item + ' = new (Container.resolveByIndex(' + typeIndex + '))(undefined, { setDefaultValues: false });'; - } else { - context.src += item + ' = new (Container.resolveByIndex(' + typeIndex + '))();'; - } - context.src += 'if (itemKey){'; - context.src += 'cache[itemKey] = ' + item + ';'; - context.src += '}'; - context.src += '}'; - } else { - if (isEntityType) { - context.src += 'var ' + item + ' = new (Container.resolveByIndex(' + typeIndex + '))(undefined, { setDefaultValues: false });'; - } else { - context.src += 'var ' + item + ' = new (Container.resolveByIndex(' + typeIndex + '))();'; - } - } - } - for (var i in meta) { - if (i.indexOf('$') < 0) { - context.current = i; - if (!meta[i].$item) { - if (meta[i].$value) { - context.meta.push(i); - var item = context.item; - this.build(meta[i], context); - context.src += item + '.' + i + ' = ' + context.item + ';'; - context.item = item; - context.meta.pop(); - } else if (meta[i].$source) { - context.src += 'var fn = function(di){'; - this._buildSelector(meta[i], context); - if (meta[i].$type) { - var type = Container.resolveName(Container.resolveType(meta[i].$type)); - var typeIndex = Container.getIndex(Container.resolveType(meta[i].$type)); - var converter = this.context.storageProvider.fieldConverter.fromDb[type]; - if (converter) { - context.src += 'return self.context.storageProvider.fieldConverter.fromDb["' + type + '"](di.' + meta[i].$source + ');'; - } else { - context.src += 'return new (Container.resolveByIndex(' + typeIndex + '))(di.' + meta[i].$source + ');'; - } - } else { - context.src += item + '.' + i + ' = di.' + meta[i].$source + ';'; - } - context.src += '};'; - if (meta[i].$type) context.src += item + '.' + i + ' = fn(di);'; - else context.src += 'fn(di);'; - } else if (meta[i].$type) { - context.meta.push(i); - context.src += 'var fn = function(di){'; - this._buildSelector(meta[i], context); - this.build(meta[i], context); - context.src += 'return ' + context.item + ';};'; - if (meta[i].$type === $data.Object) context.src += item + '.' + i + ' = self._deepExtend(' + item + '.' + i + ', fn(di));'; - else context.src += item + '.' + i + ' = fn(di);'; - context.item = item; - context.meta.pop(); - } else if (meta.$type) { - var memDef = Container.resolveType(meta.$type).memberDefinitions.getMember(i); - var type = Container.resolveName(memDef.type); - var entityType = Container.resolveType(meta.$type); - var entityTypeIndex = Container.getIndex(meta.$type); - var converter = this.context.storageProvider.fieldConverter.fromDb[type]; - if (this.providerName && memDef && memDef.converter && memDef.converter[this.providerName] && typeof memDef.converter[this.providerName].fromDb == 'function') { - context.src += item + '.' + i + ' = Container.resolveByIndex("' + entityTypeIndex + '").memberDefinitions.getMember("' + i + '").converter.' + this.providerName + '.fromDb(di.' + meta[i] + ', Container.resolveByIndex("' + entityTypeIndex + '").memberDefinitions.getMember("' + i + '"), self.context, Container.resolveByIndex("' + entityTypeIndex + '"));'; - } else if (converter) { - context.src += item + '.' + i + ' = self.context.storageProvider.fieldConverter.fromDb["' + type + '"](di.' + meta[i] + ');'; - } else { - //var type = Container.resolveName(Container.resolveType(type.memberDefinitions.getMember(i).type)); - var typeIndex = Container.getIndex(Container.resolveType(type.memberDefinitions.getMember(i).type)); - context.src += item + '.' + i + ' = new (Container.resolveByIndex(' + typeIndex + '))(di.' + meta[i] + ');'; - } - } - } else { - context.meta.push(i); - this.build(meta[i], context); - context.item = item; - context.meta.pop(); - } - } - } - context.src += item + ' = self._finalize(' + item + ');'; - } - }, - - call: function (data, meta) { - if (!Object.getOwnPropertyNames(meta).length) { - return data; - } - var context = { - src: '', - meta: [] - }; - context.src += 'var self = this;'; - context.src += 'var result;'; - context.src += 'var cache = {};'; - context.src += 'var keycache = [];'; - context.src += 'var keycacheIter = [];'; - this.build(meta, context); - if (context.item) context.src += 'if (typeof result === "undefined") result = ' + context.item + ';'; - context.src += 'return result;'; - - /*var beautify = require('beautifyjs'); - console.log(beautify.js_beautify(context.src));*/ - - var fn = new Function('meta', 'data', context.src).bind(this); - var ret = fn(meta, data); - return ret; - } -}); -$C('$data.queryBuilder', null, null, { - constructor: function () { - this._fragments = {}; - this.selectedFragment = null; - this._binderConfig = {}; - this.modelBinderConfig = this._binderConfig; - this._binderConfigPropertyStack = []; - }, - selectTextPart: function (name) { - if (!this._fragments[name]) { - this._fragments[name] = { text: '', params: [] }; - } - this.selectedFragment = this._fragments[name]; - }, - getTextPart: function (name) { - return this._fragments[name]; - }, - addText: function (textParticle) { - this.selectedFragment.text += textParticle; - }, - addParameter: function (param) { - this.selectedFragment.params.push(param); - }, - selectModelBinderProperty: function (name) { - this._binderConfigPropertyStack.push(this.modelBinderConfig); - if (!(name in this.modelBinderConfig)) { - this.modelBinderConfig[name] = {}; - } - this.modelBinderConfig = this.modelBinderConfig[name]; - }, - popModelBinderProperty: function () { - if (this._binderConfigPropertyStack.length === 0) { - this.modelBinderConfig = this._binderConfig(); - } else { - this.modelBinderConfig = this._binderConfigPropertyStack.pop(); - } - }, - resetModelBinderProperty: function (name) { - this._binderConfigPropertyStack = []; - this.modelBinderConfig = this._binderConfig; - }, - addKeyField: function (name) { - if(!this.modelBinderConfig['$keys']){ - this.modelBinderConfig['$keys'] = new Array(); - } - this.modelBinderConfig['$keys'].push(name); - } -}); -$C('$data.Query', null, null, -{ - constructor: function (expression, defaultType, context) { - /// - /// - /// - - this.expression = expression; - this.context = context; - - //TODO: expressions get as JSON string?! - - this.expressions = expression; - this.defaultType = defaultType; - this.result = []; - this.rawDataList = []; - this.modelBinderConfig = {}; - this.context = context; - }, - - rawDataList: { dataType: "Array" }, - result: { dataType: "Array" }, - resultType: {}, - buildResultSet: function (ctx) { - var converter = new $data.ModelBinder(this.context); - this.result = converter.call(this.rawDataList, this.modelBinderConfig); - return; - }, - getEntitySets: function(){ - var ret = []; - var ctx = this.context; - - var fn = function(expression){ - if (expression instanceof $data.Expressions.EntitySetExpression){ - if (ret.indexOf(ctx._entitySetReferences[expression.elementType.name]) < 0) - ret.push(ctx._entitySetReferences[expression.elementType.name]); - } - if (expression.source) fn(expression.source); - }; - - fn(this.expression); - - return ret; - } -}, null); -$data.Class.define('$data.Queryable', null, null, -{ - constructor: function (source, rootExpression) { - /// - /// Provides a base class for classes supporting JavaScript Language Query. - /// Provides a base class for classes supporting JavaScript Language Query. - /// - /// - /// - /// - /// Provides a base class for classes supporting JavaScript Language Query. - /// Provides a base class for classes supporting JavaScript Language Query. - /// - /// - /// - - var context = source instanceof $data.EntityContext ? source : source.entityContext; - this.defaultType = source instanceof $data.EntityContext ? null : source.defaultType; - this.entityContext = context; - this.expression = rootExpression; - }, - - filter: function (predicate, thisArg) { - ///Filters a set of entities using a boolean expression. - ///A boolean query expression - ///The query parameters - /// - /// - ///Filters a set of entities using a boolean expression formulated as string. - /// - ///The expression body of the predicate function in string. - ///To reference the lambda parameter use the 'it' context variable. - ///Example: filter("it.Title == 'Hello'") - /// - /// - /// - /// - /// - ///Filters a set of entities using a bool expression formulated as a JavaScript function. - /// - /// - /// - ///Contains the predicate parameters - /// - /// - /// - ///Filtering a set of entities with a predicate function - ///var males = Persons.filter( function( person ) { return person.Gender == 'Male' } ); - /// - /// - ///Filtering a set of entities with a predicate function and parameters - ///var draftables = Persons.filter( function( person ) { - /// return person.Gender == this.gender && person.Age > this.age - /// }, { gender: 'Male', age: 21 }); - /// - /// - ///Filtering a set of entities with a predicate as a string and parameters - ///var draftables = Persons.filter("it.Gender == this.gender && it.Age > this.age", - /// { gender: 'Male', age: 21 }); - /// - /// - if (arguments.length === 3) { - predicate = "it." + arguments[0] + - (arguments[1][0] === "." ? (arguments[1] + "(param)") : (" " + arguments[1] + " param")); - thisArg = { param : arguments[2] } - } - this._checkOperation('filter'); - var expression = Container.createCodeExpression(predicate, thisArg); - var expressionSource = this.expression; - if (this.expression instanceof $data.Expressions.FilterExpression) { - expressionSource = this.expression.source; - - var operatorResolution = this.entityContext.storageProvider.resolveBinaryOperator("and"); - expression = Container.createSimpleBinaryExpression(this.expression.selector, expression, "and", "filter", "boolean", operatorResolution); - } - var exp = Container.createFilterExpression(expressionSource, expression); - var q = Container.createQueryable(this, exp); - return q; - }, - where: function (predicate, params) { - ///Where is a convenience alias for C# developers. Use filter instead. - /// - return this.filter(predicate, params); - }, - - map: function (projection, thisArg, mappedTo) { - /// Map specifies the shape or type of each returned element. You can specify whether your results will consist of complete Person objects, just one member, a subset of members, or some completely different result type based on a computation or new object creation. When map produces something other than a copy of the source element, the operation is called a projection. The use of projections to transform data is a powerful capability of JavaScript Language Query expressions. - /// A projection expression - /// The query parameters - /// - /// - /// Map specifies the shape or type of each returned element. You can specify whether your results will consist of complete Person objects, just one member, a subset of members, or some completely different result type based on a computation or new object creation. When map produces something other than a copy of the source element, the operation is called a projection. The use of projections to transform data is a powerful capability of JavaScript Language Query expressions. - /// - /// The expression body of the projection function in string. - /// To reference the lambda parameter use the 'it' context variable. - /// Example: map("{ i: it.Id, t: it.Title }") - /// - /// - /// - /// - /// - /// Map specifies the shape or type of each returned element. You can specify whether your results will consist of complete Person objects, just one member, a subset of members, or some completely different result type based on a computation or new object creation. When map produces something other than a copy of the source element, the operation is called a projection. The use of projections to transform data is a powerful capability of JavaScript Language Query expressions. - /// - /// Projection function to specify the shape or type of each returned element. - /// - /// - /// Contains the projection parameters. - /// - /// - /// - /// Projection to get an array of the full name property of a set of Person entities - /// var personFullNames = Persons.map( function( person ) { return person.FullName; } ); - /// - /// - /// Projection to get an array of the required fields of Person entities in an anonymous type. - /// var custom = Persons.map( function( person ) { - /// return { FullName: person.FullName, Info: { Address: person.Location.Address, Phone: person.Phone } }; - /// }); - /// - /// - - this._checkOperation('map'); - var codeExpression = Container.createCodeExpression(projection, thisArg); - var exp = Container.createProjectionExpression(this.expression, codeExpression); - - if (mappedTo === 'default') - exp.projectionAs = this.defaultType; - else if (mappedTo) - exp.projectionAs = Container.resolveType(mappedTo); - else - exp.projectionAs = $data.Object; - - var q = Container.createQueryable(this, exp); - return q; - }, - select: function (projection, thisArg, mappedTo) { - ///Select is a convenience alias for C# developers. Use map instead. - /// - return this.map(projection, thisArg, mappedTo); - }, - - length: function (onResult, transaction) { - /// Returns the number of entities (or projected object) in a query as the callback parameter. - /// A callback function - /// - /// - /// Returns the number of entities (or projected object) in a query as the callback parameter. - /// - /// The callback function to handle the result. - /// - /// - /// - /// - /// Returns the number of entities (or projected object) in a query as the callback parameter. - /// - /// Object of callback functions to handle success and error. - /// Example: { success: function(cnt) { ... }, error: function() { alert("Something went wrong..."); } } - /// - /// - /// - /// Get the count of Person entities. - /// Persons.length( function( cnt ) { alert("There are " + cnt + " person(s) in the database."); } ); - /// - /// - - this._checkOperation('length'); - var pHandler = new $data.PromiseHandler(); - var cbWrapper = pHandler.createCallback(onResult); - - var countExpression = Container.createCountExpression(this.expression); - var preparator = Container.createQueryExpressionCreator(this.entityContext); - try { - var expression = preparator.Visit(countExpression); - this.entityContext.log({ event: "EntityExpression", data: expression }); - - this.entityContext.executeQuery(Container.createQueryable(this, expression), cbWrapper, transaction); - } catch (e) { - cbWrapper.error(e); - } - - return pHandler.getPromise(); - }, - count: function (onResult, transaction) { - ///Count is a convenience alias for C# developers. Use length instead. - /// - return this.length(onResult, transaction); - }, - - forEach: function (iterator, transaction) { - /// Calls the iterator function for all entity (or projected object) in the query. - /// Iterator function - /// - /// - /// Calls the iterator function for all entity (or projected object) in the query. - /// - /// Iterator function to handle the result elements. - /// - /// - /// - /// Log the full name of each Person. - /// Persons.forEach( function( person ) { console.log(person.FullName; } ); - /// - /// - - this._checkOperation('forEach'); - var pHandler = new $data.PromiseHandler(); - function iteratorFunc(items) { items.forEach(iterator); } - var cbWrapper = pHandler.createCallback(iteratorFunc); - - var forEachExpression = Container.createForEachExpression(this.expression); - var preparator = Container.createQueryExpressionCreator(this.entityContext); - try { - var expression = preparator.Visit(forEachExpression); - this.entityContext.log({ event: "EntityExpression", data: expression }); - - this.entityContext.executeQuery(Container.createQueryable(this, expression), cbWrapper, transaction); - } catch (e) { - cbWrapper.error(e); - } - - return pHandler.getPromise(); - }, - - toArray: function (onResult_items, transaction) { - /// Returns the query result as the callback parameter. - /// A callback function - /// - /// - /// Returns the query result as the callback parameter. - /// - /// The callback function to handle the result. - /// - /// - /// - /// - /// Returns the query result as the callback parameter. - /// - /// Object of callback functions to handle success and error. - /// Example: { success: function(result) { ... }, error: function() { alert("Something went wrong..."); } } - /// - /// - /// - /// Get all Person entities. - /// Persons.toArray( function( result ) { console.dir(result); } ); - /// - /// - - if (onResult_items instanceof $data.Array) - { - return this.toArray(function (results) { - onResult_items.length = 0; - results.forEach(function (item, idx) { - onResult_items.push(item); - }); - }); - } - - this._checkOperation('toArray'); - var pHandler = new $data.PromiseHandler(); - var cbWrapper = pHandler.createCallback(onResult_items); - - var toArrayExpression = Container.createToArrayExpression(this.expression); - var preparator = Container.createQueryExpressionCreator(this.entityContext); - try { - var expression = preparator.Visit(toArrayExpression); - this.entityContext.log({ event: "EntityExpression", data: expression }); - - this.entityContext.executeQuery(Container.createQueryable(this, expression), cbWrapper, transaction); - } catch (e) { - cbWrapper.error(e); - } - - return pHandler.getPromise(); - }, - toLiveArray: function (onResult, transaction) { - var self = this; - var result = []; - - var doAction = function (action) { - return function (onResult) { - var pHandler = new $data.PromiseHandler(); - var callback = pHandler.createCallback(onResult); - - var successFunc = function (res) { - result.length = 0; - - var data = res; - $data.typeSystem.extend(result, data); - - result.prev = doAction(function (cb) { - data.prev(cb); - }); - result.next = doAction(function (cb) { - data.next(cb); - }); - - callback.success.apply(this, [result].concat(Array.prototype.slice.call(arguments, 1))); - } - - action({ - success: successFunc, - error: callback.error - }, transaction); - - var promise = pHandler.getPromise(); - $data.typeSystem.extend(result, promise); - - return result; - } - } - - result.refresh = doAction(function (cb) { - self.toArray(cb); - }); - - return result.refresh.apply(result, arguments); - }, - - single: function (filterPredicate, thisArg, onResult, transaction) { - /// Filters a set of entities using a boolean expression and returns a single element or throws an error if more than one element is filtered. - /// A callback function - /// - /// - /// Filters a set of entities using a boolean expression and returns a single element or throws an error if more than one element is filtered. - /// - /// Same as in filter. - /// - /// - /// The callback function to handle the result, same as in toArray. - /// - /// - /// - /// - /// Filters a set of entities using a boolean expression and returns a single element or throws an error if more than one element is filtered. - /// - /// Same as in filter. - /// - /// - /// The callback function to handle the result, same as in toArray. - /// - /// - /// - /// Get "George" from the Person entity set. - /// Persons.single( function( person ) { return person.FirstName == this.name; }, { name: "George" }, { - /// success: function ( result ){ ... }, - /// error: function () { ... } - /// }); - /// - /// - - this._checkOperation('single'); - var q = this; - if (filterPredicate) { - q = this.filter(filterPredicate, thisArg); - } - q = q.take(2); - - var pHandler = new $data.PromiseHandler(); - var cbWrapper = pHandler.createCallback(onResult); - - var singleExpression = Container.createSingleExpression(q.expression); - var preparator = Container.createQueryExpressionCreator(q.entityContext); - try { - var expression = preparator.Visit(singleExpression); - this.entityContext.log({ event: "EntityExpression", data: expression }); - - q.entityContext.executeQuery(Container.createQueryable(q, expression), cbWrapper, transaction); - } catch (e) { - cbWrapper.error(e); - } - - return pHandler.getPromise(); - }, - - some: function (filterPredicate, thisArg, onResult, transaction) { - /// Filters a set of entities using a boolean expression and returns true if the query has any result element. - /// Filter function - /// The query parameters for filter function - /// A callback function - /// - /// - /// Filters a set of entities using a boolean expression and returns true if the query has any result element. - /// - /// Same as in filter. - /// - /// - /// The callback function to handle the result, same as in toArray. - /// - /// - /// - /// - /// Filters a set of entities using a boolean expression and returns true if the query has any result element. - /// - /// Same as in filter. - /// - /// - /// The callback function to handle the result, same as in toArray. - /// - /// - /// - /// Is there any person who's first name is "George"? - /// Persons.some( function( person ) { return person.FirstName == this.name; }, { name: "George" }, { - /// success: function ( result ){ ... }, - /// error: function () { ... } - /// }); - /// - /// - - this._checkOperation('some'); - var q = this; - if (filterPredicate) { - q = this.filter(filterPredicate, thisArg); - } - q = q.take(1); - - var pHandler = new $data.PromiseHandler(); - var cbWrapper = pHandler.createCallback(onResult); - - var someExpression = Container.createSomeExpression(q.expression); - var preparator = Container.createQueryExpressionCreator(q.entityContext); - try { - var expression = preparator.Visit(someExpression); - this.entityContext.log({ event: "EntityExpression", data: expression }); - - q.entityContext.executeQuery(Container.createQueryable(q, expression), cbWrapper, transaction); - } catch (e) { - cbWrapper.error(e); - } - - return pHandler.getPromise(); - }, - - every: function (filterPredicate, thisArg, onResult, transaction) { - /// Filters a set of entities using a boolean expression and returns true if all elements of the EntitySet is in the result set. - /// Filter function - /// The query parameters for filter function - /// A callback function - /// - /// - /// Filters a set of entities using a boolean expression and returns a - /// - /// Same as in filter. - /// - /// - /// The callback function to handle the result, same as in toArray. - /// - /// - /// - /// - /// Filters a set of entities using a boolean expression and returns a single element or throws an error if more than one element is filtered. - /// - /// Same as in filter. - /// - /// - /// The callback function to handle the result, same as in toArray. - /// - /// - /// - /// Result is true when all person are married. - /// Persons.every( function( person ) { return person.Married == true; }, null, { - /// success: function ( result ){ ... }, - /// error: function () { ... } - /// }); - /// - /// - - this._checkOperation('every'); - var q = this; - if (filterPredicate) { - q = this.filter(filterPredicate, thisArg); - } - q = q.take(1); - - var pHandler = new $data.PromiseHandler(); - var cbWrapper = pHandler.createCallback(onResult); - - var everyExpression = Container.createEveryExpression(q.expression); - var preparator = Container.createQueryExpressionCreator(q.entityContext); - try { - var expression = preparator.Visit(everyExpression); - this.entityContext.log({ event: "EntityExpression", data: expression }); - - q.entityContext.executeQuery(Container.createQueryable(q, expression), cbWrapper, transaction); - } catch (e) { - cbWrapper.error(e); - } - - return pHandler.getPromise(); - }, - - - take: function (amount) { - /// Returns only a specified number of elements from the start of the result set. - /// The number of elements to return. - /// - /// - /// Returns only a specified number of elements from the start of the result set. - /// - /// The number of elements to skip. - /// - /// - /// - /// Log the full name of each Person. - /// Persons.take(10).forEach( function( person ) { console.log(person.FullName; } ); - /// - /// - - this._checkOperation('take'); - var constExp = Container.createConstantExpression(amount, "number"); - var takeExp = Container.createPagingExpression(this.expression, constExp, $data.Expressions.ExpressionType.Take); - return Container.createQueryable(this, takeExp); - }, - skip: function (amount) { - /// Skip a specified number of elements from the start of the result set. - /// The number of elements to skip. - /// - /// - /// Skip a specified number of elements from the start of the result set. - /// - /// The number of elements to skip. - /// - /// - /// - /// Log the full name of each Person. - /// Persons.skip(1).take(5).forEach( function( person ) { console.log(person.FullName; } ); - /// - /// - - this._checkOperation('skip'); - var constExp = Container.createConstantExpression(amount, "number"); - var takeExp = Container.createPagingExpression(this.expression, constExp, $data.Expressions.ExpressionType.Skip); - return Container.createQueryable(this, takeExp); - }, - - order: function(selector) { - if (selector === '' || selector === undefined || selector === null) { - return this; - } - if(selector[0] === "-") { - var orderString = "it." + selector.replace("-",""); - return this.orderByDescending(orderString); - } else { - return this.orderBy("it." + selector); - } - - }, - - orderBy: function (selector, thisArg) { - ///Order a set of entities using an expression. - ///An order expression - ///The query parameters - /// - /// - ///Order a set of entities using an expression. - /// - ///The expression body of the order function in string. - ///To reference the lambda parameter use the 'it' context variable. - ///Example: orderBy("it.Id") - /// - /// - /// - /// - /// - ///Order a set of entities using an expression. - /// - /// - /// - ///Contains the predicate parameters - /// - /// - /// - ///Ordering a set of entities with a predicate function - ///var males = Persons.orderBy( function( person ) { return person.Id; } ); - /// - /// - - this._checkOperation('orderBy'); - var codeExpression = Container.createCodeExpression(selector, thisArg); - var exp = Container.createOrderExpression(this.expression, codeExpression, $data.Expressions.ExpressionType.OrderBy); - var q = Container.createQueryable(this, exp); - return q; - }, - orderByDescending: function (selector, thisArg) { - ///Order a set of entities descending using an expression. - ///An order expression - ///The query parameters - /// - /// - ///Order a set of entities descending using an expression. - /// - ///The expression body of the order function in string. - ///To reference the lambda parameter use the 'it' context variable. - ///Example: orderBy("it.Id") - /// - /// - /// - /// - /// - ///Order a set of entities descending using an expression. - /// - /// - /// - ///Contains the predicate parameters - /// - /// - /// - ///Ordering a set of entities with a predicate function - ///var males = Persons.orderByDescending( function( person ) { return person.Id; } ); - /// - /// - - this._checkOperation('orderByDescending'); - var codeExpression = Container.createCodeExpression(selector, thisArg); - var exp = Container.createOrderExpression(this.expression, codeExpression, $data.Expressions.ExpressionType.OrderByDescending); - var q = Container.createQueryable(this, exp); - return q; - }, - - first: function (filterPredicate, thisArg, onResult, transaction) { - /// Filters a set of entities using a boolean expression and returns the first element. - /// A callback function - /// - /// - /// Filters a set of entities using a boolean expression and returns the first element. - /// - /// Same as in filter. - /// - /// - /// The callback function to handle the result, same as in toArray. - /// - /// - /// - /// - /// Filters a set of entities using a boolean expression and returns the first element. - /// - /// Same as in filter. - /// - /// - /// The callback function to handle the result, same as in toArray. - /// - /// - /// - /// Get "George" from the Person entity set. - /// Persons.first( function( person ) { return person.FirstName == this.name; }, { name: "George" }, function ( result ){ ... }); - /// - /// - - this._checkOperation('first'); - var q = this; - if (filterPredicate) { - q = this.filter(filterPredicate, thisArg); - } - q = q.take(1); - - var pHandler = new $data.PromiseHandler(); - var cbWrapper = pHandler.createCallback(onResult); - - var firstExpression = Container.createFirstExpression(q.expression); - var preparator = Container.createQueryExpressionCreator(q.entityContext); - try { - var expression = preparator.Visit(firstExpression); - q.entityContext.log({ event: "EntityExpression", data: expression }); - - q.entityContext.executeQuery(Container.createQueryable(q, expression), cbWrapper, transaction); - } catch (e) { - cbWrapper.error(e); - } - - return pHandler.getPromise(); - }, - - find: function (keyValue, onResult, transaction) { - - var pHandler = new $data.PromiseHandler(); - var cbWrapper = pHandler.createCallback(onResult); - - var keys = this.defaultType.memberDefinitions.getKeyProperties(); - - try { - - if (keys.length === 1 && typeof keyValue !== 'object') { - var keyV = {}; - keyV[keys[0].name] = keyValue; - keyValue = keyV; - } - - if (typeof keyValue !== 'object') { - throw new Exception('Key parameter is invalid'); - } else { - - - var parameters = []; - for (var i = 0; i < keys.length; i++) { - var keyProp = keys[i]; - if (!(keyProp.name in keyValue)) { - throw new Exception('Key value missing'); - } - parameters.push(Container.createConstantExpression(keyValue[keyProp.name], keyProp.type, keyProp.name)); - } - - var operation = this.entityContext.storageProvider.supportedSetOperations['find']; - if (operation) { - - var findExpression = Container.createFindExpression(this.expression, parameters); - var preparator = Container.createQueryExpressionCreator(this.entityContext); - try { - var expression = preparator.Visit(findExpression); - this.entityContext.log({ event: "EntityExpression", data: expression }); - - this.entityContext.executeQuery(Container.createQueryable(this, expression), cbWrapper, transaction); - } catch (e) { - cbWrapper.error(e); - } - - } else { - var predicate = ''; - var params = {} - for (var i = 0; i < parameters.length; i++) { - var param = parameters[i]; - params[param.name] = param.value; - if (i > 0) predicate += ' && '; - predicate += "it." + param.name + " == this." + param.name; - } - - this.single(predicate, params, cbWrapper, transaction); - } - } - } catch (e) { - cbWrapper.error(e); - } - - return pHandler.getPromise(); - }, - - include: function (selector) { - /// Includes the given entity set in the query if it's an inverse property. - /// Entity set name - /// - /// - /// Includes the given entity set in the query if it's an inverse property. - /// - /// The name of the entity set you want to include in the query. - /// - /// - /// - /// Include the Category on every Article. - /// Articles.include("Category"); - /// - /// - - this._checkOperation('include'); - var constExp = Container.createConstantExpression(selector, "string"); - var takeExp = Container.createIncludeExpression(this.expression, constExp); - return Container.createQueryable(this, takeExp); - }, - - withInlineCount: function (selector) { - this._checkOperation('withInlineCount'); - var constExp = Container.createConstantExpression(selector || 'allpages', "string"); - var inlineCountExp = Container.createInlineCountExpression(this.expression, constExp); - return Container.createQueryable(this, inlineCountExp); - }, - - removeAll: function (onResult, transaction) { - /// Delete the query result and returns the number of deleted entities in a query as the callback parameter. - /// A callback function - /// - /// - /// Delete the query result and returns the number of deleted entities in a query as the callback parameter. - /// - /// The callback function to handle the result. - /// - /// - /// - /// - /// Delete the query result and returns the number of deleted entities in a query as the callback parameter. - /// - /// Object of callback functions to handle success and error. - /// Example: { success: function(result) { ... }, error: function() { alert("Something went wrong..."); } } - /// - /// - /// - /// Delete all People who are younger than 18 years old. - /// Persons.filter( function( p ){ return p.Age < 18; } ).removeAll( function( result ) { console.dir(result); } ); - /// - /// - - this._checkOperation('batchDelete'); - var pHandler = new $data.PromiseHandler(); - var cbWrapper = pHandler.createCallback(onResult); - - var batchDeleteExpression = Container.createBatchDeleteExpression(this.expression); - var preparator = Container.createQueryExpressionCreator(this.entityContext); - try { - var expression = preparator.Visit(batchDeleteExpression); - this.entityContext.log({ event: "EntityExpression", data: expression }); - - this.entityContext.executeQuery(Container.createQueryable(this, expression), cbWrapper, transaction); - } catch (e) { - cbWrapper.error(e); - } - - return pHandler.getPromise(); - }, - - - _runQuery: function (onResult_items, transaction) { - var pHandler = new $data.PromiseHandler(); - var cbWrapper = pHandler.createCallback(onResult_items); - - var preparator = Container.createQueryExpressionCreator(this.entityContext); - try { - var expression = preparator.Visit(this.expression); - this.entityContext.log({ event: "EntityExpression", data: expression }); - - this.entityContext.executeQuery(Container.createQueryable(this, expression), cbWrapper, transaction); - } catch (e) { - cbWrapper.error(e); - } - - return pHandler.getPromise(); - }, - - toTraceString: function (name) { - /// Returns the trace string of the query. - /// Name of the execution method (toArray, length, etc.). - /// - /// - /// Returns the trace string of the query. - /// - /// Name of the execution method (toArray, length, etc.). Optional. Default value is "toArray". - /// - /// - /// - /// Get the trace string for Articles.toArray() - /// Articles.toTraceString(); - /// - /// - - var expression = this.expression; - - if (name) { - expression = Container['create' + name + 'Expression'](expression); - } else { - expression = Container.createToArrayExpression(expression); - } - - var preparator = Container.createQueryExpressionCreator(this.entityContext); - expression = preparator.Visit(expression); - - //this.expression = expression; - var q = Container.createQueryable(this, expression) - return q.entityContext.getTraceString(q); - }, - - _checkOperation: function (name) { - var operation = this.entityContext.resolveSetOperations(name); - if (operation.invokable != undefined && !operation.invokable) - Guard.raise(new Exception("Operation '" + name + "' is not invokable with the provider")); - }, - defaultType: {} - -}, null); -///EntitySet is responsible for -/// -creating and holding entityType through schema -/// - provide Add method -/// - provide Delete method -/// - provide Update method -/// - provide queryProvider for queryable - -$data.EntitySchemaConfig = function EntitySchemaConfig() { - this.Name = ""; -}; -$data.entitySetState = { created: 0, defined: 1, active: 2 }; - -$data.Class.defineEx('$data.EntitySet', - [ - { type: $data.Queryable, params: [new ConstructorParameter(1)] } - ], null, -{ - constructor: function (elementType, context, collectionName, eventHandlers, roles) { - /// - /// Represents a typed entity set that is used to perform create, read, update, and delete operations - /// Type of entity set elements, elementType must be subclass of $data.Entity - /// Context of the EntitySet - /// Name of the EntitySet - /// - this.createNew = this[elementType.name] = this.elementType = this.defaultType = elementType; - var self = this; - context['createAdd' + elementType.name] = function (initData) { - var entity = new elementType(initData); - return self.add(entity); - } - this.stateManager = new $data.EntityStateManager(this); - - this.collectionName = collectionName; - this.roles = roles; - - for (var i in eventHandlers){ - this[i] = eventHandlers[i]; - } - }, - - addNew: function(item, cb) { - var callback = $data.typeSystem.createCallbackSetting(cb); - var _item = new this.createNew(item); - this.entityContext.saveChanges(cb); - return _item; - }, - - executeQuery: function (expression, on_ready) { - //var compiledQuery = this.entityContext - var callBack = $data.typeSystem.createCallbackSetting(on_ready); - this.entityContext.executeQuery(expression, callBack); - }, - getTraceString: function (expression) { - return this.entityContext.getTraceString(expression); - }, - setContext: function (entityContext) { - this.entitySetState = $data.entitySetState.active; - this.entityContext = entityContext; - this.entityContext[this.schema.name] = this[this.schema.name]; - }, - _trackEntity: function (entity) { - var trackedEntities = this.entityContext.stateManager.trackedEntities; - for (var i = 0; i < trackedEntities.length; i++) { - if (trackedEntities[i].data === entity) - return; - } - trackedEntities.push({ entitySet: this, data: entity }); - }, - add: function (entity) { - /// - /// Creates a typed entity and adds to the context. - /// The init parameters whish is based on Entity - /// - /// - /// Persons.add({ Name: 'John', Email: 'john@example.com', Age: 30, Gender: 'Male' }); - /// - /// - /// - /// - /// Adds the given entity to the context. - /// The entity to add - /// - /// - /// Persons.add(new $news.Types.Person({ Name: 'John', Email: 'john@example.com', Age: 30, Gender: 'Male' })); - /// - /// - /// - /// - /// var person = new $news.Types.Person({ Name: 'John', Email: 'john@example.com', Age: 30, Gender: 'Male' }); - /// Persons.add(person); - /// - /// - /// - - var data = entity; - if (entity instanceof $data.EntityWrapper) { - data = entity.getEntity(); - } else if (!(entity instanceof this.createNew)) { - data = new this.createNew(entity); - } - data.entityState = $data.EntityState.Added; - data.changedProperties = undefined; - data.context = this.entityContext; - this._trackEntity(data); - return data; - }, - - addMany: function(entities) { - var result = []; - var self = this; - entities.forEach(function (entity) { - result.push(self.add(entity)); - }); - return result; - }, - remove: function (entity) { - /// - /// Creates a typed entity and marks it as Deleted. - /// The init parameters whish is based on Entity - /// - /// Person will be marked as Deleted where an id is 5. Id is a key of entity. - /// Persons.remove({ Id: 5 }); - /// - /// - /// - /// - /// Marks the given entity as Deleted. - /// The entity to remove - /// - /// - /// Persons.remove(person); - /// - /// - /// - /// Person will be marked as Deleted where an Id is 5. Id is a key of entity. - /// Persons.add(new $news.Types.Person({ Id: 5 })); - /// - /// - /// - - var data = entity; - if (entity instanceof $data.EntityWrapper) { - data = entity.getEntity(); - } else if (!(entity instanceof this.createNew)) { - data = new this.createNew(entity); - } - data.entityState = $data.EntityState.Deleted; - data.changedProperties = undefined; - this._trackEntity(data); - }, - attach: function (entity, mode) { - /// - /// Creates a typed entity and adds to the Context with Unchanged state. - /// The init parameters whish is based on Entity - /// - /// - /// Persons.attach({ Id: 5, Email: 'newEmail@example.com' }); - /// - /// - /// - /// - /// Adds to the context and sets state Unchanged. - /// The entity to attach - /// - /// - /// Persons.attach(person); - /// - /// - /// - /// Set an entity's related entities without loading - /// - /// var categoryPromo = new $news.Types.Category({ Id: 5 }); - /// Category.attach(categoryPromo); - /// var article = new $news.Types.Article({ Title: 'New Article title', Body: 'Article body', Category: [ categoryPromo ] }); - /// Article.attach(article); - /// - /// - /// - - var data = entity; - if (entity instanceof $data.EntityWrapper) { - data = entity.getEntity(); - } else if (!(entity instanceof this.createNew)) { - data = new this.createNew(entity); - } - - for (var i = 0; i < this.entityContext.stateManager.trackedEntities.length; i++) { - var current = this.entityContext.stateManager.trackedEntities[i]; - if (current.data === data) - break; - if (current.data.equals(data)) { - Guard.raise(new Exception("Context already contains this entity!!!")); - } - } - - if (mode === true) { - if (data.changedProperties && data.changedProperties.length > 0) { - data.entityState = $data.EntityState.Modified; - } else { - data.entityState = $data.EntityState.Unchanged; - } - } else { - if (typeof mode === "string") mode = $data.EntityAttachMode[mode]; - var attachMode = mode || $data.EntityAttachMode[$data.EntityAttachMode.defaultMode]; - if (typeof attachMode === "function") { - attachMode.call($data.EntityAttachMode, data); - } else { - data.entityState = $data.EntityState.Unchanged; - data.changedProperties = undefined; - } - } - /*if (!keepChanges) { - data.entityState = $data.EntityState.Unchanged; - data.changedProperties = undefined; - }*/ - data.context = this.entityContext; - this._trackEntity(data); - }, - detach: function (entity) { - /// - /// Creates a typed entity and detach from the Context with Detached state. - /// The init parameters whish is based on Entity - /// - /// Person will be Detached where an id is 5. Id is a key of entity. - /// Persons.detach({ Id: 5 }); - /// - /// - /// - /// - /// Detach from the context and sets state Detached. - /// The entity to detach - /// - /// - /// Persons.detach(person); - /// - /// - /// - /// Person will be Detached where an Id is 5. Id is a key of entity. - /// Persons.add(new $news.Types.Person({ Id: 5 })); - /// - /// - /// - - var data = entity; - if (entity instanceof $data.EntityWrapper) { - data = entity.getEntity(); - } else if (!(entity instanceof this.createNew)) { - data = new this.createNew(entity); - } - - var existsItem; - var trackedEnt = this.entityContext.stateManager.trackedEntities; - for (var i = 0; i < trackedEnt.length; i++) { - if (trackedEnt[i].data.equals(data)) - existsItem = trackedEnt[i]; - } - - //var existsItem = this.entityContext.stateManager.trackedEntities.filter(function (i) { return i.data.equals(data); }).pop(); - if (existsItem) { - var idx = this.entityContext.stateManager.trackedEntities.indexOf(existsItem); - entity.entityState = $data.EntityState.Detached; - this.entityContext.stateManager.trackedEntities.splice(idx, 1); - return; - } - }, - attachOrGet: function (entity, mode) { - /// - /// Creates a typed entity and adds to the Context with Unchanged state. - /// The init parameters whish is based on Entity - /// - /// - /// Id is a key of entity. - /// var person = Persons.attachOrGet({ Id: 5 }); - /// - /// - /// - /// - /// If not in context then adds to it and sets state Unchanged. - /// The entity to detach - /// - /// - /// - /// var attachedPerson = Persons.attachOrGet(person); - /// - /// - /// - /// Id is a key of entity. - /// var p = new $news.Types.Person({ Id: 5 }); - /// var attachedPerson = Persons.attachOrGet(p); - /// - /// - /// - - var data = entity; - if (entity instanceof $data.EntityWrapper) { - data = entity.getEntity(); - } else if (!(entity instanceof this.createNew)) { - data = new this.createNew(entity); - } - - var existsItem; - var trackedEnt = this.entityContext.stateManager.trackedEntities; - for (var i = 0; i < trackedEnt.length; i++) { - if (trackedEnt[i].data.equals(data)) - existsItem = trackedEnt[i]; - } - //var existsItem = this.entityContext.stateManager.trackedEntities.filter(function (i) { return i.data.equals(data); }).pop(); - if (existsItem) { - return existsItem.data; - } - - if (typeof mode === "string") mode = $data.EntityAttachMode[mode]; - var attachMode = mode || $data.EntityAttachMode[$data.EntityAttachMode.defaultMode]; - if (typeof attachMode === "function") { - attachMode.call($data.EntityAttachMode, data); - } else { - data.entityState = $data.EntityState.Unchanged; - data.changedProperties = undefined; - } - //data.entityState = $data.EntityState.Unchanged; - //data.changedProperties = undefined; - data.context = this.entityContext; - this._trackEntity(data); - return data; - }, - //find: function (keys) { - // //todo global scope - // if (!this.entityKeys) { - // this.entityKeys = this.createNew.memberDefinition.filter(function (prop) { return prop.key; }, this); - // } - // this.entityContext.stateManager.trackedEntities.forEach(function (item) { - // if (item.entitySet == this) { - // var isOk = true; - // this.entityKeys.forEach(function (item, index) { isOK = isOk && (item.data[item.name] == keys[index]); }, this); - // if (isOk) { - // return item.data; - // } - // } - // }, this); - // //TODO: db call - // return null; - //}, - loadItemProperty: function (entity, memberDefinition, callback) { - /// - /// Loads a property of the entity through the storage provider. - /// Entity object - /// Property name - /// - /// Callback function - /// - /// - /// - /// - /// - /// Loads a property of the entity through the storage provider. - /// Entity object - /// Property name - /// - /// Success and error callbacks definition. - /// Example: [code]{ success: function(db) { .. }, error: function() { .. } }[/code] - /// - /// - /// - /// - /// Loads a property of the entity through the storage provider. - /// Entity object - /// Property definition - /// - /// Callback function - /// - /// - /// - /// - /// - /// Loads a property of the entity through the storage provider. - /// Entity object - /// Property definition - /// - /// Success and error callbacks definition. - /// Example: [code]{ success: function(db) { .. }, error: function() { .. } }[/code] - /// - /// - /// - - return this.entityContext.loadItemProperty(entity, memberDefinition, callback); - }, - saveChanges: function () { - return this.entityContext.saveChanges.apply(this.entityContext, arguments); - }, - addProperty: function (name, getter, setter) { - return this.elementType.addProperty.apply(this.elementType, arguments); - }, - expression: { - get: function () { - if (!this._expression) { - var ec = Container.createEntityContextExpression(this.entityContext); - //var name = entitySet.collectionName; - //var entitySet = this.entityContext[entitySetName]; - var memberdef = this.entityContext.getType().getMemberDefinition(this.collectionName); - var es = Container.createEntitySetExpression(ec, - Container.createMemberInfoExpression(memberdef), null, - this); - this._expression = es; - } - - return this._expression; - }, - set: function (value) { - this._expression = value; - } - }, - getFieldUrl: function (keys, field) { - return this.entityContext.getFieldUrl(keys, field, this); - }, - bulkInsert: function (fields, datas, callback) { - return this.entityContext.bulkInsert(this, fields, datas, callback); - } -}, null); -$data.EntityState = { - Detached:0, - Unchanged: 10, - Added: 20, - Modified: 30, - Deleted: 40 -};$data.Class.define("$data.EntityAttachMode", null, null, {}, { - defaultMode: 'Default', - AllChanged: function (data) { - var memDefs = data.getType().memberDefinitions.getPublicMappedProperties(); - for (var i = 0; i < memDefs.length; i++) { - data._setPropertyChanged(memDefs[i]); - } - data.entityState = $data.EntityState.Modified; - }, - KeepChanges: function (data) { - if (data.changedProperties && data.changedProperties.length > 0) { - data.entityState = $data.EntityState.Modified; - } else { - data.entityState = $data.EntityState.Unchanged; - } - }, - Default: function (data) { - data.entityState = $data.EntityState.Unchanged; - data.changedProperties = undefined; - } -});$data.Class.define('$data.EntityStateManager', null, null, -{ - constructor: function (entityContext) { - this.entityContext = null; - this.trackedEntities = []; - this.init(entityContext); - }, - init: function (entityContext) { - this.entityContext = entityContext; - }, - reset: function () { - this.trackedEntities = []; - } -}, null);$data.Class.define('$data.ItemStoreClass', null, null, { - constructor: function () { - var self = this; - self.itemStoreConfig = { - aliases: {}, - contextTypes: {} - } - - self.resetStoreToDefault('local', true); - $data.addStore = function () { - return self.addItemStoreAlias.apply(self, arguments); - }; - $data.implementation = self.implementation; - - $data.Entity.addMember('storeToken', { - get: function () { - if (this.storeConfigs && this.storeConfigs['default']) - return this.storeConfigs.stores[this.storeConfigs['default']]; - }, - set: function (value) { - self._setTypeStoreConfig(this, 'default', value); - } - }, true); - }, - itemStoreConfig: {}, - - addItemStoreAlias: function (name, contextFactoryOrToken, isDefault) { - var self = this; - var promise = new $data.PromiseHandler(); - var callback = promise.createCallback(); - - if ('string' === typeof name) { - //storeToken - if ('object' === typeof contextFactoryOrToken && 'factory' in contextFactoryOrToken) { - var type = Container.resolveType(contextFactoryOrToken.typeName); - - self.itemStoreConfig.aliases[name] = contextFactoryOrToken.factory; - self.itemStoreConfig.contextTypes[name] = type; - if (isDefault) { - self.itemStoreConfig['default'] = name; - } - - callback.success(); - return promise.getPromise(); - } - //contextFactory - else if ('function' === typeof contextFactoryOrToken) { - var preContext = contextFactoryOrToken(); - var contextPromise; - if (preContext && preContext instanceof $data.EntityContext) { - callback.success(preContext); - contextPromise = promise.getPromise(); - } else { - contextPromise = preContext; - } - - return contextPromise.then(function (ctx) { - if (typeof ctx === 'function') { - //factory resolve factory - return self.addItemStoreAlias(name, ctx, isDefault); - } - - if (ctx instanceof $data.EntityContext) { - return ctx.onReady() - .then(function (ctx) { - self.itemStoreConfig.aliases[name] = contextFactoryOrToken; - self.itemStoreConfig.contextTypes[name] = ctx.getType(); - if (isDefault) { - self.itemStoreConfig['default'] = name; - } - - return ctx; - }); - } else { - promise = new $data.PromiseHandler(); - callback = promise.createCallback(); - callback.error(new Exception('factory dont have context instance', 'Invalid arguments')); - return promise.getPromise(); - } - }); - } - } - - callback.error(new Exception('Name or factory missing', 'Invalid arguments')); - return promise.getPromise(); - }, - resetStoreToDefault: function (name, isDefault) { - this.itemStoreConfig.aliases[name] = this._getDefaultItemStoreFactory; - delete this.itemStoreConfig.contextTypes[name]; - if (isDefault) { - this.itemStoreConfig['default'] = name; - } - }, - _setStoreAlias: function (entity, storeToken) { - if ('object' === typeof storeToken && !entity.storeToken) - entity.storeToken = storeToken - return entity; - }, - _getStoreAlias: function (entity, storeAlias) { - var type; - if (entity instanceof $data.Entity) { - var alias = storeAlias || entity.storeToken; - if (alias) { - return alias; - } else { - type = entity.getType(); - } - } else { - type = entity; - } - - return storeAlias || (type.storeConfigs ? type.storeConfigs['default'] : undefined) || type.storeToken; - }, - _getStoreContext: function (aliasOrToken, type, nullIfInvalid) { - var contextPromise = this._getContextPromise(aliasOrToken, type); - - if (!contextPromise || contextPromise instanceof $data.EntityContext) { - var promise = new $data.PromiseHandler(); - var callback = promise.createCallback(); - callback.success(contextPromise); - contextPromise = promise.getPromise(); - } - - return contextPromise.then(function (context) { - if (context instanceof $data.EntityContext) { - return context.onReady(); - } else if (nullIfInvalid) { - return null; - } else { - var promise = new $data.PromiseHandler(); - var callback = promise.createCallback(); - callback.error(new Exception('factory return type error', 'Error')); - return promise.getPromise(); - } - }); - }, - _getContextPromise: function (aliasOrToken, type) { - /*Token*/ - if (aliasOrToken && 'object' === typeof aliasOrToken && 'function' === typeof aliasOrToken.factory) { - return aliasOrToken.factory(type); - } else if (aliasOrToken && 'object' === typeof aliasOrToken && 'object' === typeof aliasOrToken.args && 'string' === typeof aliasOrToken.typeName) { - var type = Container.resolveType(aliasOrToken.typeName); - return new type(JSON.parse(JSON.stringify(aliasOrToken.args))); - } - /*resolve alias from type (Token)*/ - else if (aliasOrToken && 'string' === typeof aliasOrToken && type.storeConfigs && type.storeConfigs.stores[aliasOrToken] && typeof type.storeConfigs.stores[aliasOrToken].factory === 'function') { - return type.storeConfigs.stores[aliasOrToken].factory(); - } - /*resolve alias from type (constructor options)*/ - else if (aliasOrToken && 'string' === typeof aliasOrToken && type.storeConfigs && type.storeConfigs.stores[aliasOrToken]) { - return this._getDefaultItemStoreFactory(type, type.storeConfigs.stores[aliasOrToken]); - } - /*resolve alias from ItemStore (factories)*/ - else if (aliasOrToken && 'string' === typeof aliasOrToken && this.itemStoreConfig.aliases[aliasOrToken]) { - return this.itemStoreConfig.aliases[aliasOrToken](type); - } - /*token is factory*/ - else if (aliasOrToken && 'function' === typeof aliasOrToken) { - return aliasOrToken(); - } - /*default no hint*/ - else { - return this.itemStoreConfig.aliases[this.itemStoreConfig['default']](type); - } - - }, - _getStoreEntitySet: function (storeAlias, instanceOrType) { - var aliasOrToken = this._getStoreAlias(instanceOrType, storeAlias); - var type = ("function" === typeof instanceOrType) ? instanceOrType : instanceOrType.getType();; - - return this._getStoreContext(aliasOrToken, type) - .then(function (ctx) { - var entitySet = ctx.getEntitySetFromElementType(type); - if (!entitySet) { - var d = new $data.PromiseHandler(); - var callback = d.createCallback(); - callback.error("EntitySet not exist for " + type.fullName); - return d.getPromise(); - } - return entitySet; - }); - }, - _getDefaultItemStoreFactory: function (instanceOrType, initStoreConfig) { - if (instanceOrType) { - var type = ("function" === typeof instanceOrType) ? instanceOrType : instanceOrType.getType(); - var typeName = $data.Container.resolveName(type) + "_items"; - var typeName = typeName.replace(/\./g, "_"); - - var storeConfig = $data.typeSystem.extend({ - collectionName: initStoreConfig && initStoreConfig.collectionName ? initStoreConfig.collectionName : 'Items', - tableName: typeName, - initParam: { provider: 'local', databaseName: typeName } - }, initStoreConfig); - - var contextDef = {}; - contextDef[storeConfig.collectionName] = { type: $data.EntitySet, elementType: type } - if (storeConfig.tableName) - contextDef[storeConfig.collectionName]['tableName'] = storeConfig.tableName; - - var inMemoryType = $data.EntityContext.extend(typeName, contextDef); - var ctx = new inMemoryType(storeConfig.initParam); - if (initStoreConfig && typeof initStoreConfig === 'object') - initStoreConfig.factory = ctx._storeToken.factory; - return ctx; - } - return undefined; - }, - implementation: function (name, contextOrAlias) { - var self = $data.ItemStore; - var result; - - if (typeof contextOrAlias === 'string') { - contextOrAlias = self.itemStoreConfig.contextTypes[contextOrAlias] - } else if (contextOrAlias instanceof $data.EntityContext) { - contextOrAlias = contextOrAlias.getType(); - } else if (!(typeof contextOrAlias === 'function' && contextOrAlias.isAssignableTo)) { - contextOrAlias = self.itemStoreConfig.contextTypes[self.itemStoreConfig['default']]; - } - - if (contextOrAlias) { - result = self._resolveFromContext(contextOrAlias, name); - } - - if (!result) { - result = Container.resolveType(name); - } - - return result; - }, - _resolveFromContext: function (contextType, name) { - var memDefs = contextType.memberDefinitions.getPublicMappedProperties(); - for (var i = 0; i < memDefs.length; i++) { - var memDef = memDefs[i]; - if (memDef.type) { - var memDefType = Container.resolveType(memDef.type); - if (memDefType.isAssignableTo && memDefType.isAssignableTo($data.EntitySet)) { - var elementType = Container.resolveType(memDef.elementType); - if (elementType.name === name) { - return elementType; - } - } - } - } - return null; - }, - - - //Entity Instance - EntityInstanceSave: function (storeAlias, hint) { - var self = $data.ItemStore; - var entity = this; - return self._getStoreEntitySet(storeAlias, entity) - .then(function (entitySet) { - return self._getSaveMode(entity, entitySet, hint, storeAlias) - .then(function (mode) { - mode = mode || 'add'; - switch (mode) { - case 'add': - entitySet.add(entity); - break; - case 'attach': - entitySet.attach(entity, true); - entity.entityState = $data.EntityState.Modified; - break; - default: - var d = new $data.PromiseHandler(); - var callback = d.createCallback(); - callback.error('save mode not supported: ' + mode); - return d.getPromise(); - } - - return entitySet.entityContext.saveChanges() - .then(function () { self._setStoreAlias(entity, entitySet.entityContext.storeToken); return entity; }); - }); - }); - }, - EntityInstanceRemove: function (storeAlias) { - var self = $data.ItemStore; - var entity = this; - return self._getStoreEntitySet(storeAlias, entity) - .then(function (entitySet) { - entitySet.remove(entity); - - return entitySet.entityContext.saveChanges() - .then(function () { return entity; }); - }); - }, - EntityInstanceRefresh: function (storeAlias, keepStore) { - var self = $data.ItemStore; - var entity = this; - var entityType = entity.getType(); - - var key = self._getKeyObjectFromEntity(entity, entityType); - - return entityType.read(key, storeAlias) - .then(function (loadedEntity) { - entityType.memberDefinitions.getPublicMappedProperties().forEach(function (memDef) { - entity[memDef.name] = loadedEntity[memDef.name]; - }); - entity.storeToken = (keepStore ? entity.storeToken : undefined) || loadedEntity.storeToken; - entity.changedProperties = undefined; - return entity; - }); - }, - - //Entity Type - EntityInheritedTypeProcessor: function (type) { - var self = $data.ItemStore; - type.readAll = self.EntityTypeReadAll(type); - type.read = self.EntityTypeRead(type); - type.removeAll = self.EntityTypeRemoveAll(type); - type.remove = self.EntityTypeRemove(type); - type.get = self.EntityTypeGet(type); //Not complete - type.save = self.EntityTypeSave(type); - type.addMany = self.EntityTypeAddMany(type); - type.itemCount = self.EntityTypeItemCount(type); - type.query = self.EntityTypeQuery(type); - type.takeFirst = self.EntityTypeTakeFirst(type); - - type.setStore = self.EntityTypeSetStore(type); - }, - EntityTypeReadAll: function (type) { - return function (storeAlias) { - var self = $data.ItemStore; - return self._getStoreEntitySet(storeAlias, type) - .then(function (entitySet) { - return entitySet.forEach(function (item) { self._setStoreAlias(item, entitySet.entityContext.storeToken); }); - }); - } - }, - EntityTypeRemoveAll: function (type) { - return function (storeAlias) { - var self = $data.ItemStore; - return self._getStoreEntitySet(storeAlias, type) - .then(function (entitySet) { - return entitySet.toArray().then(function (items) { - items.forEach(function (item) { - entitySet.remove(item); - }); - - return entitySet.entityContext.saveChanges() - .then(function () { return items; }); - }); - }); - } - }, - EntityTypeRead: function (type) { - return function (key, storeAlias) { - var self = $data.ItemStore; - return self._getStoreEntitySet(storeAlias, type) - .then(function (entitySet) { - try { - var singleParam = self._findByIdQueryable(entitySet, key); - return entitySet.single(singleParam.predicate, singleParam.thisArgs) - .then(function (item) { return self._setStoreAlias(item, entitySet.entityContext.storeToken); }); - } catch (e) { - var d = new $data.PromiseHandler(); - var callback = d.createCallback(); - callback.error(e); - return d.getPromise(); - } - }); - }; - }, - EntityTypeGet: function (type) { - return function (key, storeAlias) { - var self = $data.ItemStore; - var item = new type(self._getKeyObjectFromEntity(key)); - item.refresh(storeAlias); - return item; - }; - }, - EntityTypeSave: function (type) { - return function (initData, storeAlias, hint) { - - var self = $data.ItemStore; - var instance = new type(initData); - return instance.save(storeAlias, hint); - } - }, - EntityTypeAddMany: function (type) { - return function (initDatas, storeAlias) { - var self = $data.ItemStore; - return self._getStoreEntitySet(storeAlias, type) - .then(function (entitySet) { - var items = entitySet.addMany(initDatas); - return entitySet.entityContext.saveChanges() - .then(function () { - return items; - }); - }); - } - }, - EntityTypeRemove: function (type) { - return function (key, storeAlias) { - var self = $data.ItemStore; - var entityPk = type.memberDefinitions.getKeyProperties(); - var entity; - if (entityPk.length === 1) { - var obj = {}; - obj[entityPk[0].name] = key; - entity = new type(obj); - } else { - entity = new type(key); - } - return entity.remove(storeAlias); - } - }, - EntityTypeItemCount: function (type) { - return function (storeAlias) { - var self = $data.ItemStore; - return self._getStoreEntitySet(storeAlias, type) - .then(function (entitySet) { - return entitySet.length(); - }); - } - }, - EntityTypeQuery: function (type) { - return function (predicate, thisArg, storeAlias) { - var self = $data.ItemStore; - return self._getStoreEntitySet(storeAlias, type) - .then(function (entitySet) { - return entitySet.filter(predicate, thisArg).forEach(function (item) { self._setStoreAlias(item, entitySet.entityContext.storeToken); }); - }); - } - }, - EntityTypeTakeFirst: function (type) { - return function (predicate, thisArg, storeAlias) { - var self = $data.ItemStore; - return self._getStoreEntitySet(storeAlias, type) - .then(function (entitySet) { - return entitySet.first(predicate, thisArg) - .then(function (item) { return self._setStoreAlias(item, entitySet.entityContext.storeToken); }); - }); - } - }, - - EntityTypeSetStore: function (type) { - return function (name, config) { - if (typeof name === 'object' && typeof config === 'undefined') { - config = name; - name = 'default'; - } - - var self = $data.ItemStore; - - var defStoreConfig = {}; - if (config) { - if (config.tableName) { - defStoreConfig.tableName = config.tableName; - delete config.tableName; - } - - if (config.collectionName) { - defStoreConfig.collectionName = config.collectionName; - delete config.collectionName; - } - - if (typeof config.dataSource === 'string') { - var ds = config.dataSource; - if (ds.lastIndexOf('/') === ds.length - 1) { - ds = ds.substring(0, ds.lastIndexOf('/')); - } - var parsedApiUrl = ds.substring(0, ds.lastIndexOf('/')); - if (!defStoreConfig.tableName) - defStoreConfig.tableName = ds.substring(ds.lastIndexOf('/') + 1); - - var provider = config.provider || config.name; - switch (provider) { - case 'oData': - config.oDataServiceHost = config.oDataServiceHost || parsedApiUrl; - break; - case 'webApi': - config.apiUrl = config.apiUrl || parsedApiUrl; - break; - default: - break; - } - } - - - } else { - config = { name: 'local' }; - } - - defStoreConfig.initParam = config; - self._setTypeStoreConfig(type, name, defStoreConfig); - - return type; - } - }, - _setTypeStoreConfig: function(type, name, config){ - if (!type.storeConfigs) { - type.storeConfigs = { - stores: {} - }; - } - type.storeConfigs.stores[name] = config; - if (name === 'default') { - type.storeConfigs['default'] = name; - } - }, - - _findByIdQueryable: function (set, keys) { - var keysProps = set.defaultType.memberDefinitions.getKeyProperties(); - if (keysProps.length > 1 && keys && 'object' === typeof keys) { - var predicate = "", thisArgs = {}; - for (var i = 0; i < keysProps.length; i++) { - if (i > 0) predicate += " && "; - - var key = keysProps[i]; - predicate += "it." + key.name + " == this." + key.name; - thisArgs[key.name] = keys[key.name]; - } - - return { - predicate: predicate, - thisArgs: thisArgs - }; - } else if (keysProps.length === 1) { - return { - predicate: "it." + keysProps[0].name + " == this.value", - thisArgs: { value: keys } - }; - } else { - throw 'invalid keys'; - } - }, - _getKeyObjectFromEntity: function (obj, entityType) { - var key; - var keyDefs = entityType.memberDefinitions.getKeyProperties(); - if (keyDefs.length === 1) - key = obj && typeof obj === 'object' ? obj[keyDefs[0].name] : obj; - else { - key = {}; - - for (var i = 0; i < keyDefs.length; i++) { - key[keyDefs[0].name] = obj ? obj[keyDefs[0].name] : obj; - } - } - - return key; - }, - _getSaveMode: function (entity, entitySet, hint, storeAlias) { - var self = this; - var promise = new $data.PromiseHandler(); - var callback = promise.createCallback(); - var entityType = entity.getType(); - - switch (true) { - case hint === 'update': - callback.success('attach'); break; - case hint === 'new': - callback.success('add'); break; - case false === entityType.memberDefinitions.getKeyProperties().every(function (keyDef) { return entity[keyDef.name]; }): - callback.success('add'); break; - case !!entity.storeToken: - callback.success('attach'); break; - break; - default: - //use the current entity store informations - storeAlias = this._getStoreAlias(entity, storeAlias); - entityType.read(self._getKeyObjectFromEntity(entity, entityType), storeAlias) - .then(function () { callback.success('attach'); }) - .fail(function () { callback.success('add'); }); - break; - } - - return promise.getPromise(); - }, - - //EntityContext - ContextRegister: function (storageProviderCfg) { - //context instance - var self = this; - var args = JSON.parse(JSON.stringify(storageProviderCfg)); - this.storeToken = { - typeName: this.getType().fullName, - args: args, - factory: function () { - return new (self.getType())(args); - } - } - - //set elementType storetoken - var members = this.getType().memberDefinitions.getPublicMappedProperties(); - for (var i = 0; i < members.length; i++) { - var item = members[i]; - if (item.type) { - var itemResolvedDataType = Container.resolveType(item.type); - if (itemResolvedDataType && itemResolvedDataType.isAssignableTo && itemResolvedDataType.isAssignableTo($data.EntitySet)) { - var elementType = Container.resolveType(item.elementType); - if (!elementType.storeToken) { - elementType.storeToken = this.storeToken; - } - } - } - } - - }, - QueryResultModifier: function (query) { - var self = $data.ItemStore; - var context = query.context; - var type = query.modelBinderConfig.$type; - if ('string' === typeof type) { - type = Container.resolveType(type); - } - - if (type === $data.Array && query.modelBinderConfig.$item && query.modelBinderConfig.$item.$type) { - type = query.modelBinderConfig.$item.$type; - } - - //TODO: runs when model binding missed (inmemory) - if ((typeof type === 'undefined' && query.result && query.result[0] instanceof $data.Entity)) { - var navProps = !type ? [] : type.memberDefinitions.getPublicMappedProperties().filter(function (memDef) { - return !!memDef.inverseProperty; - }); - - for (var i = 0; i < query.result.length; i++) { - self._setStoreAlias(query.result[i], context.storeToken); - - for (var j = 0; j < navProps.length; j++) { - var navProp = navProps[j]; - if (query.result[i][navProp.name] instanceof $data.Entity) { - self._setStoreAlias(query.result[i][navProp.name], context.storeToken); - } else if (Array.isArray(query.result[i][navProp.name])) { - for (var k = 0; k < query.result[i][navProp.name].length; k++) { - if (query.result[i][navProp.name][k] instanceof $data.Entity) { - self._setStoreAlias(query.result[i][navProp.name][k], context.storeToken); - } - } - } - } - } - } - } -}); -$data.ItemStore = new $data.ItemStoreClass(); - -$data.Entity.addMember('field', function (propName) { - var def = this.memberDefinitions.getMember(propName); - if (def) { - if (def.definedBy === this) { - return new $data.MemberWrapper(def); - } else { - Guard.raise(new Exception("Member '" + propName + "' defined on '" + def.definedBy.fullName + "'!", 'Invalid Operation')); - } - } else { - Guard.raise(new Exception("Member '" + propName + "' not exists!", 'Invalid Operation')); - } - - return this; -}, true); - - -$data.Class.define('$data.MemberWrapper', null, null, { - constructor: function (memberDefinition) { - this.memberDefinition = memberDefinition; - }, - setKey: function (value) { - this.memberDefinition.key = value || value === undefined ? true : false; - return this; - }, - setComputed: function (value) { - this.memberDefinition.computed = value || value === undefined ? true : false; - return this; - }, - setRequired: function (value) { - this.memberDefinition.required = value || value === undefined ? true : false; - return this; - }, - setNullable: function (value) { - this.memberDefinition.nullable = value || value === undefined ? true : false; - return this; - }, - changeDefinition: function (attr, value) { - this.memberDefinition[attr] = value; - return this; - } -}); - -$data.Class.define('$data.StorageProviderLoaderBase', null, null, { - isSupported: function (providerName) { - $data.Trace.log('Detecting ' + providerName + ' provider support'); - var supported = true; - switch (providerName) { - case 'indexedDb': - supported = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB || (window.msIndexedDB && !(/^file:/.test(window.location.href))); - break; - case 'storm': - supported = 'XMLHttpRequest' in window; - break; - case 'webSql': - case 'sqLite': - supported = 'openDatabase' in window; - break; - case 'LocalStore': - supported = 'localStorage' in window && window.localStorage ? true : false; - break; - case 'sqLite': - supported = 'openDatabase' in window; - break; - case 'mongoDB': - supported = $data.mongoDBDriver; - break; - default: - break; - } - $data.Trace.log(providerName + ' provider is ' + (supported ? '' : 'not') + ' supported'); - return supported; - }, - scriptLoadTimeout: { type: 'int', value: 1000 }, - scriptLoadInterval: { type: 'int', value: 50 }, - npmModules: { - value: { - 'indexedDb': 'jaydata-indexeddb', - 'InMemory': 'jaydata-inmemory', - 'LocalStore': 'jaydata-inmemory', - 'mongoDB': 'jaydata-mongodb', - 'oData': 'jaydata-odata', - 'webApi': 'jaydata-webapi', - 'sqLite': 'jaydata-sqlite', - 'webSql': 'jaydata-sqlite', - 'storm': 'jaydata-storm' - } - }, - ProviderNames: { - value: { - 'indexedDb': 'IndexedDb', - 'InMemory': 'InMemory', - 'LocalStore': 'InMemory', - 'oData': 'oData', - 'webApi': 'WebApi', - 'sqLite': 'SqLite', - 'webSql': 'SqLite', - 'storm': 'Storm' - } - }, - load: function (providerList, callback) { - $data.RegisteredStorageProviders = $data.RegisteredStorageProviders || {}; - - $data.Trace.log('Loading provider(s): ' + providerList); - callback = $data.typeSystem.createCallbackSetting(callback); - - var self = this; - var cacheKey = providerList.join(','); - self._fallbackCache = self._fallbackCache || {}; - - if (self._fallbackCache[cacheKey]) { - callback.success(self._fallbackCache[cacheKey]); - } else { - this.find(providerList, { - success: function (provider, selectedProvider) { - self._fallbackCache[cacheKey] = provider; - callback.success.call(this, provider); - }, - error: callback.error - }); - } - }, - find: function (providerList, callback) { - var currentProvider = providerList.shift(); - var currentProvider = this.getVirtual(currentProvider); - if(Array.isArray(currentProvider)){ - providerList = currentProvider; - currentProvider = providerList.shift(); - } - - while (currentProvider && !this.isSupported(currentProvider)) { - currentProvider = providerList.shift(); - } - - $data.Trace.log('First supported provider is ' + currentProvider); - - if (!currentProvider){ - $data.Trace.log('Provider fallback failed'); - callback.error(); - } - - if ($data.RegisteredStorageProviders) { - $data.Trace.log('Is the ' + currentProvider + ' provider already registered?'); - var provider = $data.RegisteredStorageProviders[currentProvider]; - if (provider) { - $data.Trace.log(currentProvider + ' provider registered'); - callback.success(provider) - return; - }else{ - $data.Trace.log(currentProvider + ' provider not registered'); - } - } - - if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') { - // NodeJS - $data.Trace.log('node.js detected trying to load NPM module'); - this.loadNpmModule(currentProvider, providerList, callback); - } else { - $data.Trace.log('Browser detected trying to load provider'); - this.loadProvider(currentProvider, providerList, callback); - } - }, - loadProvider: function (currentProvider, providerList, callback) { - var self = this; - var mappedName = $data.StorageProviderLoader.ProviderNames[currentProvider] || currentProvider; - $data.Trace.log(currentProvider + ' provider is mapped to name ' + mappedName + 'Provider'); - if (mappedName) { - var url = this.getUrl(mappedName); - $data.Trace.log(currentProvider + ' provider from URL: ' + url); - - var loader = this.loadScript; - if (document && document.createElement) { - $data.Trace.log('document and document.createElement detected, using script element loader method'); - loader = this.loadScriptElement; - } - - loader.call(this, url, currentProvider, function (successful) { - var provider = $data.RegisteredStorageProviders[currentProvider]; - if (successful && provider) { - $data.Trace.log(currentProvider + ' provider successfully registered'); - callback.success(provider); - } else if (providerList.length > 0) { - $data.Trace.log(currentProvider + ' provider failed to load, trying to fallback to ' + providerList + ' provider(s)'); - self.find(providerList, callback); - } else { - $data.Trace.log(currentProvider + ' provider failed to load'); - callback.error(); - } - }); - } - }, - getUrl: function (providerName) { - var jaydataScriptMin = document.querySelector('script[src$="jaydata.min.js"]'); - var jaydataScript = document.querySelector('script[src$="jaydata.js"]'); - if (jaydataScriptMin) return jaydataScriptMin.src.substring(0, jaydataScriptMin.src.lastIndexOf('/') + 1) + 'jaydataproviders/' + providerName + 'Provider.min.js'; - else if (jaydataScript) return jaydataScript.src.substring(0, jaydataScript.src.lastIndexOf('/') + 1) + 'jaydataproviders/' + providerName + 'Provider.js'; - else return 'jaydataproviders/' + providerName + 'Provider.js'; - }, - loadScript: function (url, currentProvider, callback) { - if (!url){ - callback(false); - return; - } - - function getHttpRequest() { - if (window.XMLHttpRequest) - return new XMLHttpRequest(); - else if (window.ActiveXObject !== undefined) - return new ActiveXObject("MsXml2.XmlHttp"); - else{ - $data.Trace.log('XMLHttpRequest or MsXml2.XmlHttp ActiveXObject not found'); - callback(false); - return; - } - } - - var oXmlHttp = getHttpRequest(); - oXmlHttp.onreadystatechange = function () { - $data.Trace.log('HTTP request is in state: ' + oXmlHttp.readyState); - if (oXmlHttp.readyState == 4) { - if (oXmlHttp.status == 200 || oXmlHttp.status == 304) { - $data.Trace.log('HTTP request succeeded'); - $data.Trace.log('HTTP request response text: ' + oXmlHttp.responseText); - eval.call(window, oXmlHttp.responseText); - if (typeof callback === 'function') - callback(true); - else $data.Trace.log('Callback function is undefined'); - } else { - $data.Trace.log('HTTP request status: ', oXmlHttp.status); - if (typeof callback === 'function') - callback(false); - else $data.Trace.log('Callback function is undefined'); - } - } - }; - oXmlHttp.open('GET', url, true); - oXmlHttp.send(null); - }, - loadScriptElement: function (url, currentProvider, callback) { - var head = document.getElementsByTagName('head')[0] || document.documentElement; - - var script = document.createElement('script'); - script.type = 'text/javascript'; - script.src = url; - $data.Trace.log('Appending child ' + script + ' to ' + head); - head.appendChild(script); - - var loadInterval = this.scriptLoadInterval || 50; - var iteration = Math.ceil(this.scriptLoadTimeout / loadInterval); - $data.Trace.log('Script element watcher iterating ' + iteration + ' times'); - function watcher() { - $data.Trace.log('Script element watcher iteration ' + iteration); - var provider = $data.RegisteredStorageProviders[currentProvider]; - if (provider) { - $data.Trace.log(currentProvider + ' provider registered'); - callback(true); - } else { - iteration--; - if (iteration > 0) { - $data.Trace.log('Script element watcher next iteration'); - setTimeout(watcher, loadInterval); - } else { - $data.Trace.log('Script element loader failed'); - callback(false); - } - } - } - setTimeout(watcher, loadInterval); - }, - - loadNpmModule: function (currentProvider, providerList, callback) { - var provider = null; - try { - require(this.npmModules[currentProvider]); - provider = $data.RegisteredStorageProviders[currentProvider]; - $data.Trace.log('NPM module loader successfully registered ' + currentProvider + ' provider'); - } catch (e) { - $data.Trace.log('NPM module loader failed for ' + currentProvider + ' provider'); - } - - if (provider) { - callback.success(provider); - } else if (providerList.length > 0) { - this.find(providerList, callback); - } else { - callback.error(); - } - }, - - virtualProviders: { - type: $data.Array, - value: { - local: { - fallbacks: ['webSql', 'indexedDb', 'LocalStore'] - } - } - }, - getVirtual: function(name){ - if(this.virtualProviders[name]) - return [].concat(this.virtualProviders[name].fallbacks); - - return name; - } -}); - -$data.StorageProviderLoader = new $data.StorageProviderLoaderBase(); -$data.storageProviders = { - DbCreationType: { - Merge: 10, - DropTableIfChanged: 20, - DropTableIfChange: 20, - DropAllExistingTables: 30, - ErrorIfChange: 40, - DropDbIfChange: 50 - } -} - -$data.ConcurrencyMode = { Fixed: 'fixed', None: 'none' }; -$data.Class.define('$data.StorageProviderBase', null, null, -{ - constructor: function (schemaConfiguration, context) { - this.providerConfiguration = schemaConfiguration || {}; - - this.name = this.getType().name; - if ($data.RegisteredStorageProviders) { - var keys = Object.keys($data.RegisteredStorageProviders); - for (var i = 0; i < keys.length; i++) { - if (this instanceof $data.RegisteredStorageProviders[keys[i]]) { - this.name = keys[i]; - break; - } - } - } - }, - providers: {}, - supportedDataTypes: { value: [], writable: false }, - initializeStore: function (callBack) { - Guard.raise("Pure class"); - }, - - executeQuery: function (queryable, callBack) { - Guard.raise("Pure class"); - }, - loadRawData: function (tableName, callBack) { - callBack = $data.typeSystem.createCallbackSetting(callBack); - callBack.error(new Exception('loadRawData is not supported', 'Invalid Operation')); - }, - - buildIndependentBlocks: function (changedItems) { - /// - /// Build and processes a dependency graph from the changed items, - /// and generates blocks that can be inserted to the database sequentially. - /// - /// Array of changed items to build independent blocks from. - var edgesTo = []; - var edgesFrom = []; - - function hasOwnProperty(obj) { - /// - /// Returns true if object has own property (used for 'hashset'-like objects) - /// - /// Target object - /// True if the object has own property - for (var p in obj) { - if (obj.hasOwnProperty(p)) - return true; - } - return false; - } - - // Building edgesTo and edgesFrom arrays (containing only indeces of items in changedItems array. - for (var i = 0; i < changedItems.length; i++) { - var current = changedItems[i]; - if (!current.dependentOn || current.dependentOn.length == 0) { - // This item is independent - continue; - } - - var to = null; - // Iterating over items 'current' depends on - for (var j = 0; j < current.dependentOn.length; j++) { - var currentDependency = current.dependentOn[j]; - if (currentDependency.entityState == $data.EntityState.Unchanged) { - continue; - } - to = to || {}; - // Getting the index of current dependency - var ixDependendOn = -1; - for (var k = 0; k < changedItems.length; k++) { - if (changedItems[k].data == currentDependency) { - ixDependendOn = k; - break; - } - } - // Sanity check - if (ixDependendOn == -1) { - Guard.raise(new Exception('Dependent object not found', 'ObjectNotFound', current.dependentOn[j])); - } - // Setting edge in 'to' array - to[ixDependendOn] = true; - // Setting edge in 'from' array - from = edgesFrom[ixDependendOn] || {}; - from[i] = true; - edgesFrom[ixDependendOn] = from; - } - // Persisting found edges in edgesTo array - if (to !== null) - edgesTo[i] = to; - } - - // Array of sequentialyl independent blocks (containing objects, not just their id's) - var independentBlocks = []; - // Objects getting their dependency resolved in the current cycle. - var currentBlock = []; - // Filling currentBlock with initially independent objects. - for (var x = 0; x < changedItems.length; x++) { - if (!edgesTo.hasOwnProperty(x)) { - currentBlock.push(x); - } - } - while (currentBlock.length > 0) { - // Shifting currentBlock to cbix, - // and clearing currentBlock for next independent block - var cbix = [].concat(currentBlock); - currentBlock = []; - // Iterating over previous independent block, to generate the new one - for (var b = 0; b < cbix.length; b++) { - var dependentNodes = edgesFrom[cbix[b]]; - if (typeof dependentNodes !== 'undefined') { - for (var d in dependentNodes) { - // Removing edge from 'edgesTo' - delete edgesTo[d][cbix[b]]; - // Check if has any more dependency - if (!hasOwnProperty(edgesTo[d])) { - // It doesn't, so let's clean up a bit - delete edgesTo[d]; - // and push the item to 'currentBlock' - currentBlock.push(d); - } - } - } - // Clearing processed item from 'edgesFrom' - delete edgesFrom[cbix[b]]; - } - // Push cbix t to independentBlocks - var cb = []; - for (var c = 0; c < cbix.length; c++) { - var item = changedItems[cbix[c]]; - if (item.data.entityState != $data.EntityState.Unchanged) - cb.push(item); - } - if (cb.length > 0) - independentBlocks.push(cb); - } - return independentBlocks; - }, - getTraceString: function (queryable) { - Guard.raise("Pure class"); - }, - setContext: function (ctx) { - this.context = ctx; - }, - - _buildContinuationFunction: function (context, query) { - if (Array.isArray(query.result)) { - query.result.next = this._buildPagingMethod(context, query, 'next'); - query.result.prev = this._buildPagingMethod(context, query, 'prev'); - } - }, - _buildPagingMethod: function (context, query, mode) { - return function (onResult_items) { - var pHandler = new $data.PromiseHandler(); - var cbWrapper = pHandler.createCallback(onResult_items); - - var continuation = new $data.Expressions.ContinuationExpressionBuilder(mode); - var continuationResult = continuation.compile(query); - if (continuationResult.expression) { - var queryable = Container.createQueryable(context, continuationResult.expression); - queryable.defaultType = query.defaultType; - context.executeQuery(queryable, cbWrapper); - } else { - cbWrapper.error(new Exception(continuationResult.message, 'Invalid Operation', continuationResult)); - } - - return pHandler.getPromise(); - } - }, - - buildDbType_modifyInstanceDefinition: function (instanceDefinition, storageModel) { - var buildDbType_copyPropertyDefinition = function (propertyDefinition, refProp) { - var cPropertyDef; - if (refProp) { - cPropertyDef = JSON.parse(JSON.stringify(instanceDefinition[refProp])); - cPropertyDef.kind = propertyDefinition.kind; - cPropertyDef.name = propertyDefinition.name; - cPropertyDef.notMapped = false; - } else { - cPropertyDef = JSON.parse(JSON.stringify(propertyDefinition)); - } - - cPropertyDef.dataType = Container.resolveType(propertyDefinition.dataType); - cPropertyDef.type = cPropertyDef.dataType; - cPropertyDef.key = false; - cPropertyDef.computed = false; - return cPropertyDef; - }; - var buildDbType_createConstrain = function (foreignType, dataType, propertyName, prefix, keyPropertyName) { - var constrain = new Object(); - constrain[foreignType.name] = propertyName; - constrain[dataType.name] = keyPropertyName ? keyPropertyName : prefix + '__' + propertyName; - return constrain; - }; - - if (storageModel.Associations) { - storageModel.Associations.forEach(function (association) { - var addToEntityDef = false; - var foreignType = association.FromType; - var dataType = association.ToType; - var foreignPropName = association.ToPropertyName; - - var memDef = association.FromType.getMemberDefinition(association.FromPropertyName); - var keyProperties = []; - if (memDef && typeof memDef.keys === "string" && memDef.keys) { - keyProperties = [memDef.keys]; - } else if (memDef && Array.isArray(memDef.keys)) { - keyProperties = [].concat(memDef.keys); - } - - association.ReferentialConstraint = association.ReferentialConstraint || []; - - if ((association.FromMultiplicity == "*" && association.ToMultiplicity == "0..1") || (association.FromMultiplicity == "0..1" && association.ToMultiplicity == "1")) { - foreignType = association.ToType; - dataType = association.FromType; - foreignPropName = association.FromPropertyName; - addToEntityDef = true; - } - - foreignType.memberDefinitions.getPublicMappedProperties().filter(function (d) { return d.key }).forEach(function (d, i) { - var constraint = buildDbType_createConstrain(foreignType, dataType, d.name, foreignPropName, keyProperties[i]); - if (addToEntityDef) { - //instanceDefinition[foreignPropName + '__' + d.name] = buildDbType_copyPropertyDefinition(d, foreignPropName); - instanceDefinition[constraint[dataType.name]] = buildDbType_copyPropertyDefinition(d, foreignPropName); - - var dependentMemDef = dataType.getMemberDefinition(keyProperties[i]); - if (dependentMemDef) { - dependentMemDef.isDependentProperty = true; - dependentMemDef.navigationPropertyName = association.FromPropertyName; - } - } - association.ReferentialConstraint.push(constraint); - }, this); - }, this); - } - //Copy complex type properties - if (storageModel.ComplexTypes) { - storageModel.ComplexTypes.forEach(function (complexType) { - complexType.ReferentialConstraint = complexType.ReferentialConstraint || []; - - complexType.ToType.memberDefinitions.getPublicMappedProperties().forEach(function (d) { - instanceDefinition[complexType.FromPropertyName + '__' + d.name] = buildDbType_copyPropertyDefinition(d); - complexType.ReferentialConstraint.push(buildDbType_createConstrain(complexType.ToType, complexType.FromType, d.name, complexType.FromPropertyName)); - }, this); - }, this); - } - }, - buildDbType_generateConvertToFunction: function (storageModel) { - return function (logicalEntity) { - var dbInstance = new storageModel.PhysicalType(); - dbInstance.entityState = logicalEntity.entityState; - - //logicalEntity.changedProperties.forEach(function(memberDef){ - //}, this); - storageModel.PhysicalType.memberDefinitions.getPublicMappedProperties().forEach(function (property) { - if (logicalEntity[property.name] !== undefined) { - dbInstance[property.name] = logicalEntity[property.name]; - } - }, this); - - if (storageModel.Associations) { - storageModel.Associations.forEach(function (association) { - if ((association.FromMultiplicity == "*" && association.ToMultiplicity == "0..1") || (association.FromMultiplicity == "0..1" && association.ToMultiplicity == "1")) { - var complexInstance = logicalEntity[association.FromPropertyName]; - if (complexInstance !== undefined) { - association.ReferentialConstraint.forEach(function (constrain) { - if (complexInstance !== null) { - dbInstance[constrain[association.From]] = complexInstance[constrain[association.To]]; - } else { - dbInstance[constrain[association.From]] = null; - } - }, this); - } - } - }, this); - } - if (storageModel.ComplexTypes) { - storageModel.ComplexTypes.forEach(function (cmpType) { - var complexInstance = logicalEntity[cmpType.FromPropertyName]; - if (complexInstance !== undefined) { - cmpType.ReferentialConstraint.forEach(function (constrain) { - if (complexInstance !== null) { - dbInstance[constrain[cmpType.From]] = complexInstance[constrain[cmpType.To]]; - } else { - dbInstance[constrain[cmpType.From]] = null; - } - }, this); - } - }, this); - } - return dbInstance; - }; - }, - - bulkInsert: function (a, b, c, callback) { - callback.error(new Exception('Not Implemented')); - }, - - supportedFieldOperations: { - value: { - length: { dataType: "number", allowedIn: "filter, map" }, - substr: { dataType: "string", allowedIn: "filter", parameters: [{ name: "startFrom", dataType: "number" }, { name: "length", dataType: "number" }] }, - toLowerCase: { dataType: "string" } - }, - enumerable: true, - writable: true - }, - - resolveFieldOperation: function (operationName, expression, frameType) { - /// - var result = this.supportedFieldOperations[operationName]; - if (Array.isArray(result)) { - var i = 0; - for (; i < result.length; i++) { - if (result[i].allowedType === 'default' || Container.resolveType(result[i].allowedType) === Container.resolveType(expression.selector.memberDefinition.type) && - (frameType && result[i].allowedIn && - ( - (Array.isArray(result[i].allowedIn) && result[i].allowedIn.some(function(type){ return frameType === Container.resolveType(type); })) || - (!Array.isArray(result[i].allowedIn) && (frameType === Container.resolveType(result[i].allowedIn))) - ) - ) - ) { - result = result[i]; - break; - } - } - if (i === result.length) { - result = undefined; - } - } - - if (!result) { - Guard.raise(new Exception("Field operation '" + operationName + "' is not supported by the provider")); - }; - if (frameType && result.allowedIn) { - if ((result.allowedIn instanceof Array && !result.allowedIn.some(function (type) { return frameType === Container.resolveType(type); })) || - (!(result.allowedIn instanceof Array) && frameType !== Container.resolveType(result.allowedIn))) { - Guard.raise(new Exception(operationName + " not supported in: " + frameType.name)); - } - } - result.name = operationName; - return result; - }, - - supportedBinaryOperators: { - value: { - equal: { mapTo: 'eq', dataType: "boolean" } - }, - enumerable: true, - writable: true - }, - - resolveBinaryOperator: function (operator, expression, frameType) { - var result = this.supportedBinaryOperators[operator]; - if (!result) { - Guard.raise(new Exception("Binary operator '" + operator + "' is not supported by the provider")); - }; - if (frameType && result.allowedIn) { - if ((result.allowedIn instanceof Array && !result.allowedIn.some(function (type) { return frameType === Container.resolveType(type); })) || - (!(result.allowedIn instanceof Array) && frameType !== Container.resolveType(result.allowedIn))) { - Guard.raise(new Exception(operator + " not supported in: " + frameType.name)); - } - } - result.name = operator; - return result; - }, - - supportedUnaryOperators: { - value: { - not: { mapTo: 'not' } - }, - enumerable: true, - writable: true - }, - resolveUnaryOperator: function (operator, expression, frameType) { - var result = this.supportedUnaryOperators[operator]; - if (!result) { - Guard.raise(new Exception("Unary operator '" + operator + "' is not supported by the provider")); - }; - if (frameType && result.allowedIn) { - if ((result.allowedIn instanceof Array && !result.allowedIn.some(function (type) { return frameType === Container.resolveType(type); })) || - (!(result.allowedIn instanceof Array) && frameType !== Container.resolveType(result.allowedIn))) { - Guard.raise(new Exception(operator + " not supported in: " + frameType.name)); - } - } - result.name = operator; - return result; - }, - - supportedSetOperations: { - value: { - toArray: { invokable: true, allowedIn: [] } - }, - enumerable: true, - writable: true - }, - resolveSetOperations: function (operation, expression, frameType) { - var result = this.supportedSetOperations[operation]; - if (!result) { - Guard.raise(new Exception("Operation '" + operation + "' is not supported by the provider")); - }; - var allowedIn = result.allowedIn || []; - if (frameType && allowedIn) { - if ((allowedIn instanceof Array && !allowedIn.some(function (type) { return frameType === Container.resolveType(type); })) || - (!(allowedIn instanceof Array) && frameType !== Container.resolveType(allowedIn))) { - Guard.raise(new Exception(operation + " not supported in: " + frameType.name)); - } - } - return result; - }, - - resolveTypeOperations: function (operation, expression, frameType) { - Guard.raise(new Exception("Entity '" + expression.entityType.name + "' Operation '" + operation + "' is not supported by the provider")); - }, - - resolveContextOperations: function (operation, expression, frameType) { - Guard.raise(new Exception("Context '" + expression.instance.getType().name + "' Operation '" + operation + "' is not supported by the provider")); - }, - - makePhysicalTypeDefinition: function (entityDefinition, association) { - }, - - _beginTran: function (tables, isWrite, callBack) { - callBack.success(new $data.Transaction()); - }, - - getFieldUrl: function () { - return '#'; - }, - - supportedAutoincrementKeys: { - value: { } - } -}, -{ - onRegisterProvider: { value: new $data.Event() }, - registerProvider: function (name, provider) { - this.onRegisterProvider.fire({ name: name, provider: provider }, this); - $data.RegisteredStorageProviders = $data.RegisteredStorageProviders || []; - $data.RegisteredStorageProviders[name] = provider; - }, - getProvider: function (name) { - var provider = $data.RegisteredStorageProviders[name]; - if (!provider) - console.warn("Provider not found: '" + name + "'"); - return provider; - /*var provider = $data.RegisteredStorageProviders[name]; - if (!provider) - Guard.raise(new Exception("Provider not found: '" + name + "'", "Not Found")); - return provider;*/ - }, - isSupported: { - get: function () { return true; }, - set: function () { } - } -}); -$data.Class.define('$data.ServiceOperation', null, null, {}, { - translateDefinition: function (propertyDef, name, definedBy) { - propertyDef.serviceName = name; - var memDef = new $data.MemberDefinition(this.generateServiceOperation(propertyDef), this); - memDef.name = name; - return memDef; - }, - generateServiceOperation: function (cfg) { - - var fn; - if (cfg.serviceMethod) { - var returnType = cfg.returnType ? Container.resolveType(cfg.returnType) : {}; - if (returnType.isAssignableTo && returnType.isAssignableTo($data.Queryable)) { - fn = cfg.serviceMethod; - } else { - fn = function () { - var lastParam = arguments[arguments.length - 1]; - - var pHandler = new $data.PromiseHandler(); - var cbWrapper; - - var args = arguments; - if (typeof lastParam === 'function') { - cbWrapper = pHandler.createCallback(lastParam); - arguments[arguments.length - 1] = cbWrapper; - } else { - cbWrapper = pHandler.createCallback(); - arguments.push(cbWrapper); - } - - try { - var result = cfg.serviceMethod.apply(this, arguments); - if (result !== undefined) - cbWrapper.success(result); - } catch (e) { - cbWrapper.error(e); - } - - return pHandler.getPromise(); - } - } - - } else { - fn = function () { - var context = this; - var memberdef; - - var boundItem; - if (this instanceof $data.Entity || this instanceof $data.EntitySet) { - var entitySet; - if (this instanceof $data.Entity) { - if (this.context) { - context = this.context; - entitySet = context.getEntitySetFromElementType(this.getType()); - } else if (this.storeToken && typeof this.storeToken.factory === 'function') { - context = this.storeToken.factory(); - entitySet = context.getEntitySetFromElementType(this.getType()); - } else { - Guard.raise(new Exception("entity can't resolve context", 'Not Found!', this)); - return; - } - } else if (this instanceof $data.EntitySet) { - context = this.entityContext; - entitySet = this; - - var esDef = context.getType().getMemberDefinition(entitySet.name); - memberdef = $data.MemberDefinition.translateDefinition(esDef.actions[cfg.serviceName], cfg.serviceName, entitySet.getType()); - } - - - boundItem = { - data: this, - entitySet: entitySet - }; - } - - var virtualEntitySet = cfg.elementType ? context.getEntitySetFromElementType(Container.resolveType(cfg.elementType)) : null; - - var paramConstExpression = null; - if (cfg.params) { - paramConstExpression = []; - //object as parameter - if (arguments[0] && typeof arguments[0] === 'object' && arguments[0].constructor === $data.Object && cfg.params && cfg.params[0] && cfg.params[0].name in arguments[0]) { - var argObj = arguments[0]; - for (var i = 0; i < cfg.params.length; i++) { - var paramConfig = cfg.params[i]; - if (paramConfig.name && paramConfig.type && paramConfig.name in argObj) { - paramConstExpression.push(Container.createConstantExpression(argObj[paramConfig.name], Container.resolveType(paramConfig.type), paramConfig.name)); - } - } - } - //arg params - else { - for (var i = 0; i < cfg.params.length; i++) { - if (typeof arguments[i] == 'function') break; - - //TODO: check params type - var paramConfig = cfg.params[i]; - if (paramConfig.name && paramConfig.type && arguments[i] !== undefined) { - paramConstExpression.push(Container.createConstantExpression(arguments[i], Container.resolveType(paramConfig.type), paramConfig.name)); - } - } - } - } - - var ec = Container.createEntityContextExpression(context); - if (!memberdef) { - if (boundItem && boundItem.data) { - memberdef = boundItem.data.getType().getMemberDefinition(cfg.serviceName); - } else { - memberdef = context.getType().getMemberDefinition(cfg.serviceName); - } - } - var es = Container.createServiceOperationExpression(ec, - Container.createMemberInfoExpression(memberdef), - paramConstExpression, - cfg, - boundItem); - - //Get callback function - var clb = arguments[arguments.length - 1]; - if (!(typeof clb === 'function' || (typeof clb === 'object' /*&& clb.constructor === $data.Object*/ && (typeof clb.success === 'function' || typeof clb.error === 'function')))) { - clb = undefined; - } - - if (virtualEntitySet) { - var q = Container.createQueryable(virtualEntitySet, es); - if (clb) { - es.isTerminated = true; - return q._runQuery(clb); - } - return q; - } - else { - var returnType = cfg.returnType ? Container.resolveType(cfg.returnType) : null; - - var q = Container.createQueryable(context, es); - q.defaultType = returnType || $data.Object; - - if (returnType === $data.Queryable) { - q.defaultType = Container.resolveType(cfg.elementType); - if (clb) { - es.isTerminated = true; - return q._runQuery(clb); - } - return q; - } - es.isTerminated = true; - return q._runQuery(clb); - } - }; - }; - - var params = cfg.params || []; - $data.typeSystem.extend(fn, cfg, { params: params }); - - return fn; - } -}); - -$data.Class.define('$data.ServiceAction', $data.ServiceOperation, null, {}, { - generateServiceOperation: function (cfg) { - if (!cfg.method) { - cfg.method = 'POST'; //default Action method is POST - } - - return $data.ServiceOperation.generateServiceOperation.apply(this, arguments); - } -});$data.Base.extend('$data.EntityWrapper', { - getEntity: function () { - Guard.raise("pure object"); - } -}); -if (typeof jQuery !== 'undefined' && jQuery.ajax) { - $data.ajax = $data.ajax || jQuery.ajax; -} - -if (typeof WinJS !== 'undefined' && WinJS.xhr) { - $data.ajax = $data.ajax || function (options) { - $data.typeSystem.extend(options, { - dataType: 'json', - headers: {} - }); - var dataTypes = { - 'json': { - accept: 'application/json, text/javascript', - convert: JSON.parse - }, - 'text': { - accept: 'text/plain', - convert: function (e) { return e; } - }, - 'html': { - accept: 'text/html', - convert: function (e) { return e; } - }, - 'xml': { - accept: 'application/xml, text/xml', - convert: function (e) { - // TODO? - return e; - } - } - } - var dataTypeContext = dataTypes[options.dataType.toLowerCase()]; - - options.headers.Accept = dataTypeContext.accept; - - var successClb = options.success || $data.defaultSuccessCallback; - var errorClb = options.error || $data.defaultErrorCallback; - var progressClb = options.progress; - - var success = function (r) { - var result = dataTypeContext.convert(r.responseText); - successClb(result); - } - var error = function (r) { - var error = dataTypeContext.convert(r.responseText); - errorClb(error); - } - var progress = progressClb; - - WinJS.xhr(options) - .done(success, error, progress); - } -} - -if (typeof Ext !== 'undefined' && typeof Ext.Ajax) { - $data.ajax = $data.ajax || function (options) { - Ext.Ajax.request(options); - }; -} - -$data.ajax = $data.ajax || function () { - var cfg = arguments[arguments.length - 1]; - var clb = $data.typeSystem.createCallbackSetting(cfg); - clb.error("Not implemented"); -}; - - -$C('$data.modelBinder.FindProjectionVisitor', $data.Expressions.EntityExpressionVisitor, null, { - VisitProjectionExpression: function (expression) { - this.projectionExpression = expression; - } -}); - -$C('$data.modelBinder.ModelBinderConfigCompiler', $data.Expressions.EntityExpressionVisitor, null, { - constructor: function (query, includes, oDataProvider) { - this._query = query; - this._includes = includes; - this._isoDataProvider = oDataProvider || false; - }, - VisitSingleExpression: function (expression) { - this._defaultModelBinder(expression); - }, - VisitSomeExpression: function (expression) { - this._defaultModelBinder(expression); - }, - VisitFindExpression: function (expression) { - this._defaultModelBinder(expression); - }, - VisitEveryExpression: function (expression) { - this._defaultModelBinder(expression); - }, - VisitToArrayExpression: function (expression) { - this._defaultModelBinder(expression); - }, - VisitFirstExpression: function (expression) { - this._defaultModelBinder(expression); - }, - VisitForEachExpression: function (expression) { - this._defaultModelBinder(expression); - }, - VisitServiceOperationExpression: function (expression) { - if (expression.cfg.returnType) { - var returnType = Container.resolveType(expression.cfg.returnType); - if ((typeof returnType.isAssignableTo === 'function' && returnType.isAssignableTo($data.Queryable)) || returnType === $data.Array) { - this._defaultModelBinder(expression); - } else { - var builder = Container.createqueryBuilder(); - builder.modelBinderConfig['$type'] = returnType; - if (typeof returnType.isAssignableTo === 'function' && returnType.isAssignableTo($data.Entity)) { - builder.modelBinderConfig['$selector'] = ['json:' + expression.cfg.serviceName]; - } else { - builder.modelBinderConfig['$type'] = returnType; - builder.modelBinderConfig['$value'] = function (a, v) { - return (expression.cfg.serviceName in v) ? v[expression.cfg.serviceName] : v.value; - } - } - this.VisitExpression(expression, builder); - builder.resetModelBinderProperty(); - this._query.modelBinderConfig = builder.modelBinderConfig; - } - } - }, - VisitCountExpression: function (expression) { - var builder = Container.createqueryBuilder(); - - builder.modelBinderConfig['$type'] = $data.Array; - builder.selectModelBinderProperty('$item'); - builder.modelBinderConfig['$type'] = $data.Integer; - builder.modelBinderConfig['$source'] = 'cnt'; - builder.resetModelBinderProperty(); - this._query.modelBinderConfig = builder.modelBinderConfig; - }, - VisitBatchDeleteExpression: function (expression) { - var builder = Container.createqueryBuilder(); - - builder.modelBinderConfig['$type'] = $data.Array; - builder.selectModelBinderProperty('$item'); - builder.modelBinderConfig['$type'] = $data.Integer; - builder.modelBinderConfig['$source'] = 'cnt'; - builder.resetModelBinderProperty(); - this._query.modelBinderConfig = builder.modelBinderConfig; - }, - VisitConstantExpression: function (expression, builder) { - builder.modelBinderConfig['$type'] = expression.type; - builder.modelBinderConfig['$value'] = expression.value; - }, - - VisitExpression: function (expression, builder) { - var projVisitor = Container.createFindProjectionVisitor(); - projVisitor.Visit(expression); - - if (projVisitor.projectionExpression) { - this.Visit(projVisitor.projectionExpression, builder); - } else { - this.DefaultSelection(builder, this._query.defaultType, this._includes); - } - }, - _defaultModelBinder: function (expression) { - var builder = Container.createqueryBuilder(); - builder.modelBinderConfig['$type'] = $data.Array; - if (this._isoDataProvider) { - builder.modelBinderConfig['$selector'] = ['json:d.results', 'json:d', 'json:results']; - } - builder.modelBinderConfig['$item'] = {}; - builder.selectModelBinderProperty('$item'); - - this.VisitExpression(expression, builder); - - builder.resetModelBinderProperty(); - this._query.modelBinderConfig = builder.modelBinderConfig; - }, - _addPropertyToModelBinderConfig: function (elementType, builder) { - var storageModel = this._query.context._storageModel.getStorageModel(elementType); - if (elementType.memberDefinitions) { - elementType.memberDefinitions.getPublicMappedProperties().forEach(function (prop) { - if ((!storageModel) || (storageModel && !storageModel.Associations[prop.name] && !storageModel.ComplexTypes[prop.name])) { - - var type = Container.resolveType(prop.dataType); - if (!storageModel && this._query.context.storageProvider.supportedDataTypes.indexOf(type) < 0) { - //complex type - builder.selectModelBinderProperty(prop.name); - builder.modelBinderConfig['$type'] = type; - if (this._isoDataProvider) { - builder.modelBinderConfig['$selector'] = ['json:' + prop.name + '.results', 'json:' + prop.name]; - } else { - builder.modelBinderConfig['$selector'] = 'json:' + prop.name; - } - this._addPropertyToModelBinderConfig(type, builder); - builder.popModelBinderProperty(); - } else { - if (prop.key) { - builder.addKeyField(prop.name); - } - if (prop.concurrencyMode === $data.ConcurrencyMode.Fixed) { - builder.modelBinderConfig[prop.name] = { $selector: 'json:__metadata', $source: 'etag' } - } else if (type === $data.Array && prop.elementType) { - builder.selectModelBinderProperty(prop.name); - builder.modelBinderConfig['$type'] = type; - if (this._isoDataProvider) { - builder.modelBinderConfig['$selector'] = ['json:' + prop.name + '.results', 'json:' + prop.name]; - } else { - builder.modelBinderConfig['$selector'] = 'json:' + prop.name; - } - builder.selectModelBinderProperty('$item'); - var arrayElementType = Container.resolveType(prop.elementType); - builder.modelBinderConfig['$type'] = arrayElementType; - this._addPropertyToModelBinderConfig(arrayElementType, builder); - builder.popModelBinderProperty(); - builder.popModelBinderProperty(); - } else { - builder.modelBinderConfig[prop.name] = prop.name; - } - } - } - }, this); - } else { - /*builder._binderConfig = { - $selector: ['json:results'], - $type: $data.Array, - $item:{ - $type: elementType, - $value: function (meta, data) { return data; } - } - }*/ - builder._binderConfig.$item = builder._binderConfig.$item || {}; - builder.modelBinderConfig = builder._binderConfig.$item; - - - - } - if (storageModel) { - this._addComplexTypeProperties(storageModel.ComplexTypes, builder); - } - }, - _addComplexTypeProperties: function (complexTypes, builder) { - complexTypes.forEach(function (ct) { - if (ct.ToType !== $data.Array){ - builder.selectModelBinderProperty(ct.FromPropertyName); - builder.modelBinderConfig['$type'] = ct.ToType; - if (this._isoDataProvider) { - builder.modelBinderConfig['$selector'] = ['json:' + ct.FromPropertyName + '.results', 'json:' + ct.FromPropertyName]; - } else { - builder.modelBinderConfig['$selector'] = 'json:' + ct.FromPropertyName; - } - this._addPropertyToModelBinderConfig(ct.ToType, builder); - - builder.popModelBinderProperty(); - }else{ - var dt = ct.ToType; - var et = Container.resolveType(ct.FromType.memberDefinitions.getMember(ct.FromPropertyName).elementType); - if (dt === $data.Array && et && et.isAssignableTo && et.isAssignableTo($data.Entity)){ - config = { - $type: $data.Array, - $selector: 'json:' + ct.FromPropertyName, - $item: { - $type: et - } - }; - var md = et.memberDefinitions.getPublicMappedProperties(); - for (var i = 0; i < md.length; i++){ - config.$item[md[i].name] = { $type: md[i].type, $source: md[i].name }; - } - builder.modelBinderConfig[ct.FromPropertyName] = config; - }else{ - builder.modelBinderConfig[ct.FromPropertyName] = { - $type: ct.ToType, - $source: ct.FromPropertyName - }; - } - } - }, this); - }, - DefaultSelection: function (builder, type, allIncludes) { - //no projection, get all item from entitySet - builder.modelBinderConfig['$type'] = type; - - var storageModel = this._query.context._storageModel.getStorageModel(type); - this._addPropertyToModelBinderConfig(type, builder); - if (allIncludes) { - allIncludes.forEach(function (include) { - var includes = include.name.split('.'); - var association = null; - var tmpStorageModel = storageModel; - var itemCount = 0; - for (var i = 0; i < includes.length; i++) { - if (builder.modelBinderConfig.$item) { - builder.selectModelBinderProperty('$item'); - itemCount++; - } - builder.selectModelBinderProperty(includes[i]); - association = tmpStorageModel.Associations[includes[i]]; - tmpStorageModel = this._query.context._storageModel.getStorageModel(association.ToType); - } - if (this._isoDataProvider) { - builder.modelBinderConfig['$selector'] = ['json:' + includes[includes.length - 1] + '.results', 'json:' + includes[includes.length - 1]]; - } else { - builder.modelBinderConfig['$selector'] = 'json:' + includes[includes.length - 1]; - } - if (association.ToMultiplicity === '*') { - builder.modelBinderConfig['$type'] = $data.Array; - builder.selectModelBinderProperty('$item'); - builder.modelBinderConfig['$type'] = include.type; - this._addPropertyToModelBinderConfig(include.type, builder); - builder.popModelBinderProperty(); - } else { - builder.modelBinderConfig['$type'] = include.type; - this._addPropertyToModelBinderConfig(include.type, builder); - } - - for (var i = 0; i < includes.length + itemCount; i++) { - builder.popModelBinderProperty(); - } - }, this); - } - }, - VisitProjectionExpression: function (expression, builder) { - this.hasProjection = true; - this.Visit(expression.selector, builder); - - if (expression.selector && expression.selector.expression instanceof $data.Expressions.ObjectLiteralExpression) { - builder.modelBinderConfig['$type'] = expression.projectionAs || builder.modelBinderConfig['$type'] || $data.Object; - } - }, - VisitParametricQueryExpression: function (expression, builder) { - if (expression.expression instanceof $data.Expressions.EntityExpression || expression.expression instanceof $data.Expressions.EntitySetExpression) { - this.VisitEntityAsProjection(expression, builder); - } else { - this.Visit(expression.expression, builder); - } - - }, - VisitEntityAsProjection: function (expression, builder) { - this.mapping = ''; - this.Visit(expression.expression, builder); - var includes; - if (this.mapping && this._includes instanceof Array) { - includes = this._includes.filter(function (inc) { - return inc.name.indexOf(this.mapping + '.') === 0 - }, this); - includes = includes.map(function (inc) { - return { name: inc.name.replace(this.mapping + '.', ''), type: inc.type }; - }, this); - - if (includes.length > 0){ - this.DefaultSelection(builder, expression.expression.entityType, includes); - //console.warn('WARN: include for mapped properties is not supported!'); - } - } - - if (expression.expression instanceof $data.Expressions.EntityExpression) { - this.DefaultSelection(builder, expression.expression.entityType/*, includes*/) - } else if (expression.expression instanceof $data.Expressions.EntitySetExpression) { - builder.modelBinderConfig.$type = $data.Array; - builder.modelBinderConfig.$item = {}; - builder.selectModelBinderProperty('$item'); - this.DefaultSelection(builder, expression.expression.elementType /*, includes*/) - builder.popModelBinderProperty(); - } - - }, - - VisitEntityFieldExpression: function (expression, builder) { - this.Visit(expression.source, builder); - this.Visit(expression.selector, builder); - }, - VisitMemberInfoExpression: function (expression, builder) { - builder.modelBinderConfig['$type'] = expression.memberDefinition.type; - if (expression.memberDefinition.storageModel && expression.memberName in expression.memberDefinition.storageModel.ComplexTypes) { - this._addPropertyToModelBinderConfig(Container.resolveType(expression.memberDefinition.type), builder); - } else { - if (!(builder.modelBinderConfig.$type && Container.resolveType(builder.modelBinderConfig.$type).isAssignableTo && Container.resolveType(builder.modelBinderConfig.$type).isAssignableTo($data.Entity))) - builder.modelBinderConfig['$source'] = expression.memberName; - } - }, - VisitEntitySetExpression: function (expression, builder) { - if (expression.source instanceof $data.Expressions.EntityExpression) { - this.Visit(expression.source, builder); - this.Visit(expression.selector, builder); - } - - }, - VisitComplexTypeExpression: function (expression, builder) { - this.Visit(expression.source, builder); - this.Visit(expression.selector, builder); - - - if (('$selector' in builder.modelBinderConfig) && (builder.modelBinderConfig.$selector.length > 0)) { - if (builder.modelBinderConfig.$selector instanceof $data.Array) { - var temp = builder.modelBinderConfig.$selector[1]; - builder.modelBinderConfig.$selector[0] = temp + '.' + expression.selector.memberName + '.results'; - builder.modelBinderConfig.$selector[1] = temp + '.' + expression.selector.memberName; - } else { - builder.modelBinderConfig.$selector += '.' + expression.selector.memberName; - } - - } else { - if (this._isoDataProvider) { - builder.modelBinderConfig['$selector'] = ['json:' + expression.selector.memberName + '.results', 'json:' + expression.selector.memberName]; - } else { - builder.modelBinderConfig['$selector'] = 'json:' + expression.selector.memberName; - } - } - }, - VisitEntityExpression: function (expression, builder) { - this.Visit(expression.source, builder); - }, - VisitAssociationInfoExpression: function (expression, builder) { - if (('$selector' in builder.modelBinderConfig) && (builder.modelBinderConfig.$selector.length > 0)) { - if (builder.modelBinderConfig.$selector instanceof $data.Array) { - var temp = builder.modelBinderConfig.$selector[1]; - builder.modelBinderConfig.$selector[0] = temp + '.' + expression.associationInfo.FromPropertyName + '.results'; - builder.modelBinderConfig.$selector[1] = temp + '.' + expression.associationInfo.FromPropertyName; - } else { - builder.modelBinderConfig.$selector += '.' + expression.associationInfo.FromPropertyName; - } - - } else { - if (this._isoDataProvider) { - builder.modelBinderConfig['$selector'] = ['json:' + expression.associationInfo.FromPropertyName + '.results', 'json:' + expression.associationInfo.FromPropertyName]; - } else { - builder.modelBinderConfig['$selector'] = 'json:' + expression.associationInfo.FromPropertyName; - } - } - - if (this.mapping && this.mapping.length > 0) { this.mapping += '.'; } - this.mapping += expression.associationInfo.FromPropertyName; - }, - VisitObjectLiteralExpression: function (expression, builder) { - builder.modelBinderConfig['$type'] = $data.Object; - expression.members.forEach(function (of) { - this.Visit(of, builder); - }, this); - }, - VisitObjectFieldExpression: function (expression, builder) { - builder.selectModelBinderProperty(expression.fieldName); - if (expression.expression instanceof $data.Expressions.EntityExpression || expression.expression instanceof $data.Expressions.EntitySetExpression) { - this.VisitEntityAsProjection(expression, builder); - } else { - this.Visit(expression.expression, builder); - } - builder.popModelBinderProperty(); - } -}); -$data.Class.define("$data.Authentication.AuthenticationBase", null, null, { - constructor: function (cfg) { - this.configuration = cfg || {}; - this.Authenticated = false; - }, - /// { error:, abort:, pending:, success: } - Login: function (callbacks) { - Guard.raise("Pure class"); - }, - Logout: function () { - Guard.raise("Pure class"); - }, - CreateRequest: function (cfg) { - Guard.raise("Pure class"); - } - -}, null);$data.Class.define("$data.Authentication.Anonymous", $data.Authentication.AuthenticationBase, null, { - constructor: function (cfg) { - this.configuration = cfg || {}; - this.Authenticated = false; - }, - /// { error:, abort:, pending:, success: } - Login: function (callbacks) { - }, - Logout: function () { - }, - CreateRequest: function (cfg) { - $data.ajax(cfg); - } - -}, null);$data.Class.define("$data.Authentication.FacebookAuth", $data.Authentication.AuthenticationBase, null, { - constructor: function (cfg) { - this.configuration = $data.typeSystem.extend({ - Url_code: '', - type_code: '', - scope: '', - Url_token: '', - type_token: '', - access_token: '', - app_id: '' - }, cfg); - }, - Login: function (callbacks) { - if (this.Authenticated) { - return; - } - - var provider = this; - provider.configuration.stateCallbacks = callbacks || {}; - - $data.ajax({ - url: this.configuration.Url_code, - data: 'type=' + provider.configuration.type_code + '&client_id=' + provider.configuration.app_id + '&scope=' + provider.configuration.scope, - type: 'POST', - dataType: 'json', - success: function (data) { - if (typeof provider.configuration.stateCallbacks.pending == "function") - provider.configuration.stateCallbacks.pending(data); - provider._processRequestToken(data); - provider.Authenticated = true; - }, - error: function () { - if (typeof provider.configuration.stateCallbacks.error == "function") - provider.configuration.stateCallbacks.error(arguments); - } - }); - }, - Logout: function () { - this.Authenticated = false; - }, - CreateRequest: function (cfg) { - if (!cfg) - return; - var _this = this; - - if (cfg.url.indexOf('access_token=') === -1) { - if (cfg.url && this.Authenticated) { - var andChar = '?'; - if (cfg.url.indexOf(andChar) > 0) - andChar = '&'; - - if (this.configuration.access_token) - cfg.url = cfg.url + andChar + 'access_token=' + this.configuration.access_token; - } - } - - $data.ajax(cfg); - }, - _processRequestToken: function (verification_data) { - var provider = this; - - $data.ajax({ - url: provider.configuration.Url_token, - data: 'type=' + provider.configuration.type_token + '&client_id=' + provider.configuration.app_id + '&code=' + verification_data.code, - type: 'POST', - dataType: 'json', - success: function(result) { - provider.configuration.access_token = result.access_token; - if (typeof provider.configuration.stateCallbacks.success == "function") - provider.configuration.stateCallbacks.success(result); - }, - error: function(obj) { - var data = eval('(' + obj.responseText + ')'); - if (data.error) { - if (data.error.message == "authorization_pending") { - setTimeout(function() { - provider._processRequestToken(verification_data); - }, 2000); - } else if ("authorization_declined") { - if (typeof provider.configuration.stateCallbacks.abort == "function") - provider.configuration.stateCallbacks.abort(arguments); - } - } - } - }); - } -}, null);$data.Class.define("$data.Authentication.BasicAuth.BasicAuth", $data.Authentication.AuthenticationBase, null, { - constructor: function (cfg) { - this.configuration = $data.typeSystem.extend({ - Username: '', - Password: '' - }, cfg); - }, - Login: function (callbacks) { - if (callbacks && typeof callbacks.pending == "function") - callbacks.pending(); - }, - Logout: function () { - }, - CreateRequest: function (cfg) { - if (!cfg) - return; - var _this = this; - - var origBeforeSend = cfg.beforeSend; - cfg.beforeSend = function (xhr) { - xhr.setRequestHeader("Authorization", "Basic " + _this.__encodeBase64(_this.configuration.Username + ":" + _this.configuration.Password)); - - if(typeof origBeforeSend == "function") - origBeforeSend(xhr); - }; - - $data.ajax(cfg); - }, - __encodeBase64: function (val) { - var b64array = "ABCDEFGHIJKLMNOP" + - "QRSTUVWXYZabcdef" + - "ghijklmnopqrstuv" + - "wxyz0123456789+/" + - "="; - - input = val; - var base64 = ""; - var hex = ""; - var chr1, chr2, chr3 = ""; - var enc1, enc2, enc3, enc4 = ""; - var i = 0; - - do { - chr1 = input.charCodeAt(i++); - chr2 = input.charCodeAt(i++); - chr3 = input.charCodeAt(i++); - - enc1 = chr1 >> 2; - enc2 = ((chr1 & 3) << 4) | (chr2 >> 4); - enc3 = ((chr2 & 15) << 2) | (chr3 >> 6); - enc4 = chr3 & 63; - - if (isNaN(chr2)) { - enc3 = enc4 = 64; - } else if (isNaN(chr3)) { - enc4 = 64; - } - - base64 = base64 + - b64array.charAt(enc1) + - b64array.charAt(enc2) + - b64array.charAt(enc3) + - b64array.charAt(enc4); - chr1 = chr2 = chr3 = ""; - enc1 = enc2 = enc3 = enc4 = ""; - } while (i < input.length); - - return base64; - } -}, null); -$data.Class.define('$data.MetadataLoaderClass', null, null, { - load: function (metadataUri, callBack, config) { - - var cnf = { - EntityBaseClass: '$data.Entity', - ContextBaseClass: '$data.EntityContext', - AutoCreateContext: false, - DefaultNamespace: ('ns' + Math.random()).replace('.', '') + metadataUri.replace(/[^\w]/g, "_"), - ContextInstanceName: 'context', - EntitySetBaseClass: '$data.EntitySet', - CollectionBaseClass: 'Array', - url: metadataUri, - user: undefined, - password: undefined, - withCredentials: undefined, - httpHeaders: undefined, - - typeFilter: '', - navigation: true, - generateKeys: true, - dependentRelationsOnly: false - }; - - $data.typeSystem.extend( cnf, config || {}); - - if (cnf.DefaultNamespace && cnf.DefaultNamespace.lastIndexOf('.') !== (cnf.DefaultNamespace.length - 1)) - cnf.DefaultNamespace += '.'; - - this.factoryCache = this.factoryCache || {}; - callBack = $data.typeSystem.createCallbackSetting(callBack); - - if (metadataUri in this.factoryCache) { - - /*console.log("served from cache"); - console.dir(this.factoryCache[metadataUri]);*/ - callBack.success.apply({}, this.factoryCache[metadataUri]); - return; - } - - - - - var metadataUri; - if (cnf.url) { - cnf.SerivceUri = cnf.url.replace('/$metadata', ''); - if (cnf.url.indexOf('/$metadata') === -1) { - cnf.metadataUri = cnf.url.replace(/\/+$/, '') + '/$metadata'; - } else { - cnf.metadataUri = cnf.url; - } - } else { - callBack.error('metadata url is missing'); - } - - var self = this; - self._loadXMLDoc(cnf, function (xml, response) { - if (response.statusCode < 200 || response.statusCode > 299) { - callBack.error(response); - return; - } - - var versionInfo = self._findVersion(xml); - if (self.xsltRepoUrl) { - console.log('XSLT: ' + self.xsltRepoUrl + self._supportedODataVersionXSLT) - self._loadXMLDoc({ - metadataUri: self.xsltRepoUrl + self._supportedODataVersionXSLT, - user: cnf.user, - password: cnf.password, - httpHeaders: cnf.httpHeaders - }, function (xsl, response) { - if (response.statusCode < 200 || response.statusCode > 299) { - callBack.error(response); - return; - } - var text = response.responseText; - text = text.replace('xmlns:edm="@@VERSIONNS@@"', 'xmlns:edm="' + versionInfo.ns + '"'); - text = text.replace('@@VERSION@@', versionInfo.version); - - if (window.ActiveXObject === undefined) { - var parser = new DOMParser(); - xsl = parser.parseFromString(text, "text/xml"); - } else { - xsl = new ActiveXObject("Microsoft.XMLDOM"); - xsl.async = false; - xsl.loadXML(text); - } - - self._transform(callBack, versionInfo, xml, xsl, cnf); - }); - } else { - self._transform(callBack, versionInfo, xml, undefined, cnf); - } - - }); - }, - debugMode: { type: 'bool', value: false }, - xsltRepoUrl: { type: 'string', value: '' }, - - createFactoryFunc: function (ctxType, cnf, versionInfo) { - var self = this; - return function (config) { - if (ctxType) { - var cfg = $data.typeSystem.extend({ - name: 'oData', - oDataServiceHost: cnf.SerivceUri, - //maxDataServiceVersion: '', - user: cnf.user, - password: cnf.password, - withCredentials: cnf.withCredentials, - maxDataServiceVersion: versionInfo.maxVersion || '3.0' - }, config) - - - return new ctxType(cfg); - } else { - return null; - } - } - }, - - _transform: function (callBack, versionInfo, xml, xsl, cnf) { - var self = this; - var codeText = self._processResults(cnf.url, versionInfo, xml, xsl, cnf); - - try { - eval(codeText); - } catch (e) { - callBack.error(new Exception('SyntaxError', 'Unexpected model', [e, codeText])); - return; - } - - var ctxType; - if (!$data.generatedContexts || !(ctxType = $data.generatedContexts.pop())) { - callBack.error(new Exception('No context found in service', 'Not found', [cnf, codeText])); - return; - } - - var factoryFn = self.createFactoryFunc(ctxType, cnf, versionInfo); - this.factoryCache[cnf.url] = [factoryFn, ctxType]; - - factoryFn.type = ctxType; - - if (self.debugMode) { - factoryFn.codeText = codeText; - callBack.success(factoryFn, ctxType, codeText); - } - else { - callBack.success(factoryFn, ctxType); - } - }, - _loadXMLDoc: function (cnf, callback) { - var that = this; - if ($data.postMessageODataHandler) { - - if (cnf.user && cnf.password && (!cnf.httpHeaders || (cnf.httpHeaders && !cnf.httpHeaders['Authorization']))) { - httpHeader = httpHeader || {}; - httpHeader["Authorization"] = "Basic " + this.__encodeBase64(cnf.user + ":" + cnf.password); - } - - $data.postMessageODataHandler.requestProxy({ - url: cnf.metadataUri, - httpHeaders: cnf.httpHeaders, - success: function (response) { - var doc; - if (typeof module !== 'undefined' && typeof require !== 'undefined') { - doc = response.responseText; - } else if (window.ActiveXObject) { - doc = new ActiveXObject('Microsoft.XMLDOM'); - doc.async = 'false'; - doc.loadXML(response.responseText); - } else { - var parser = new DOMParser(); - doc = parser.parseFromString(response.responseText, 'text/xml'); - } - - callback(doc, response); - }, - error: function (e) { - that._loadXHTTP_XMLDoc(cnf, callback); - } - - }); - - } else { - this._loadXHTTP_XMLDoc(cnf, callback); - } - }, - _loadXHTTP_XMLDoc: function (cnf, callback) { - var xhttp = new XMLHttpRequest(); - xhttp.open("GET", cnf.metadataUri, true); - if (cnf.httpHeaders) { - Object.keys(cnf.httpHeaders).forEach(function (header) { - xhttp.setRequestHeader(header, cnf.httpHeaders[header]); - }); - } - xhttp.onreadystatechange = function () { - if (xhttp.readyState === 4) { - var response = { requestUri: cnf.metadataUri, statusCode: xhttp.status, statusText: xhttp.statusText, responseText: xhttp.responseText }; - callback(xhttp.responseXML || xhttp.responseText, response); - } - }; - - if (cnf.user && cnf.password && (!cnf.httpHeaders || (cnf.httpHeaders && !cnf.httpHeaders['Authorization']))) - xhttp.setRequestHeader("Authorization", "Basic " + this.__encodeBase64(cnf.user + ":" + cnf.password)); - - xhttp.send(""); - }, - _processResults: function (metadataUri, versionInfo, metadata, xsl, cnf) { - var transformXslt = this.getCurrentXSLTVersion(versionInfo, metadata); - cnf.typeFilter = this._prepareTypeFilter(metadata, versionInfo, cnf); - - if (window.ActiveXObject !== undefined) { - var xslt = new ActiveXObject("Msxml2.XSLTemplate.6.0"); - var xsldoc = new ActiveXObject("Msxml2.FreeThreadedDOMDocument.6.0"); - var xslproc; - xsldoc.async = false; - if (xsl) - xsldoc.load(xsl); - else - xsldoc.loadXML(transformXslt); - if (xsldoc.parseError.errorCode != 0) { - var myErr = xsldoc.parseError; - } else { - xslt.stylesheet = xsldoc; - var xmldoc = new ActiveXObject("Msxml2.DOMDocument.6.0"); - xmldoc.async = false; - xmldoc.load(metadata); - if (xmldoc.parseError.errorCode != 0) { - var myErr = xmldoc.parseError; - } else { - xslproc = xslt.createProcessor(); - xslproc.input = xmldoc; - - xslproc.addParameter('SerivceUri', cnf.SerivceUri); - xslproc.addParameter('EntityBaseClass', cnf.EntityBaseClass); - xslproc.addParameter('ContextBaseClass', cnf.ContextBaseClass); - xslproc.addParameter('AutoCreateContext', cnf.AutoCreateContext); - xslproc.addParameter('ContextInstanceName', cnf.ContextInstanceName); - xslproc.addParameter('EntitySetBaseClass', cnf.EntitySetBaseClass); - xslproc.addParameter('CollectionBaseClass', cnf.CollectionBaseClass); - xslproc.addParameter('DefaultNamespace', cnf.DefaultNamespace); - xslproc.addParameter('MaxDataserviceVersion', versionInfo.maxVersion || '3.0'); - xslproc.addParameter('AllowedTypesList', cnf.typeFilter); - xslproc.addParameter('GenerateNavigationProperties', cnf.navigation); - - xslproc.transform(); - return xslproc.output; - } - } - return ''; - } else if (typeof document !== 'undefined' && document.implementation && document.implementation.createDocument) { - var xsltStylesheet; - if (xsl) { - xsltStylesheet = xsl; - } else { - var parser = new DOMParser(); - xsltStylesheet = parser.parseFromString(transformXslt, "text/xml"); - } - - var xsltProcessor = new XSLTProcessor(); - xsltProcessor.importStylesheet(xsltStylesheet); - xsltProcessor.setParameter(null, 'SerivceUri', cnf.SerivceUri); - xsltProcessor.setParameter(null, 'EntityBaseClass', cnf.EntityBaseClass); - xsltProcessor.setParameter(null, 'ContextBaseClass', cnf.ContextBaseClass); - xsltProcessor.setParameter(null, 'AutoCreateContext', cnf.AutoCreateContext); - xsltProcessor.setParameter(null, 'ContextInstanceName', cnf.ContextInstanceName); - xsltProcessor.setParameter(null, 'EntitySetBaseClass', cnf.EntitySetBaseClass); - xsltProcessor.setParameter(null, 'CollectionBaseClass', cnf.CollectionBaseClass); - xsltProcessor.setParameter(null, 'DefaultNamespace', cnf.DefaultNamespace); - xsltProcessor.setParameter(null, 'MaxDataserviceVersion', versionInfo.maxVersion || '3.0'); - xsltProcessor.setParameter(null, 'AllowedTypesList', cnf.typeFilter); - xsltProcessor.setParameter(null, 'GenerateNavigationProperties', cnf.navigation); - resultDocument = xsltProcessor.transformToFragment(metadata, document); - - return resultDocument.textContent; - } else if (typeof module !== 'undefined' && typeof require !== 'undefined') { - var xslt = require('node_xslt'); - - return xslt.transform(xslt.readXsltString(transformXslt), xslt.readXmlString(metadata), [ - 'SerivceUri', "'" + cnf.SerivceUri + "'", - 'EntityBaseClass', "'" + cnf.EntityBaseClass + "'", - 'ContextBaseClass', "'" + cnf.ContextBaseClass + "'", - 'AutoCreateContext', "'" + cnf.AutoCreateContext + "'", - 'ContextInstanceName', "'" + cnf.ContextInstanceName + "'", - 'EntitySetBaseClass', "'" + cnf.EntitySetBaseClass + "'", - 'CollectionBaseClass', "'" + cnf.CollectionBaseClass + "'", - 'DefaultNamespace', "'" + cnf.DefaultNamespace + "'", - 'MaxDataserviceVersion', "'" + (versionInfo.maxVersion || '3.0') + "'", - 'AllowedTypesList', "'" + cnf.typeFilter + "'", - 'GenerateNavigationProperties', "'" + cnf.navigation + "'" - ]); - } - }, - _prepareTypeFilter: function (doc, versionInfo, cnf) { - var result = ''; - if (!(typeof doc === 'object' && "querySelector" in doc && "querySelectorAll" in doc)) - return result; - - var config = []; - if (typeof cnf.typeFilter === 'object' && cnf.typeFilter) { - var types = Object.keys(cnf.typeFilter); - for (var i = 0; i < types.length; i++) { - var cfg = cnf.typeFilter[types[i]]; - var typeData = {}; - if (typeof cfg === 'object' && cfg) { - if (Array.isArray(cfg)) { - typeData.Name = types[i]; - typeData.Fields = cfg; - } else { - typeData.Name = cfg.name || types[i]; - typeData.Fields = cfg.members || []; - } - } else if (cfg) { - typeData.Name = types[i]; - typeData.Fields = []; - } else { - continue; - } - - var typeShortName = typeData.Name; - var containerName = ""; - if (typeData.Name.lastIndexOf('.') > 0) - { - containerName = typeData.Name.substring(0, typeData.Name.lastIndexOf('.')); - typeShortName = typeData.Name.substring(typeData.Name.lastIndexOf('.') + 1); - } - - var conainers = doc.querySelectorAll("EntityContainer[Name = '" + containerName + "']"); - for (var j = 0; j < conainers.length; j++) { - var entitySetDef = conainers[j].querySelector("EntitySet[Name = '" + typeShortName + "']"); - if (entitySetDef != null) - { - typeData.Name = entitySetDef.attributes["EntityType"].value; - break; - } - - } - - config.push(typeData); - } - - var discoveredData; - if (cnf.dependentRelationsOnly) { - discoveredData = this._discoverProperyDependencies(config, doc, cnf.navigation, cnf.generateKeys); - } else { - discoveredData = this._discoverTypeDependencies(config, doc, cnf.navigation, cnf.generateKeys); - } - - var complex = doc.querySelectorAll("ComplexType"); - for (var i = 0; i < complex.length; i++) - { - var cns = complex[i].parentNode.attributes["Namespace"].value; - var data = !cns ? complex[i].attributes["Name"].value : (cns + "." + complex[i].attributes["Name"].value); - discoveredData.push({ Name: data, Fields: [] }); - } - - for (var i = 0; i < discoveredData.length; i++) - { - var row = discoveredData[i]; - if (row.Fields.length > 0) { - result += row.Name + ":" + row.Fields.join(",") + ";"; - } - else { - result += row.Name + ";"; - } - } - - } - - return result; - }, - _discoverTypeDependencies: function (types, doc, withNavPropertis, withKeys) { - var allowedTypes = []; - var allowedTypeNames = []; - var collect = []; - - for (var i = 0; i < types.length; i++) - { - var idx = collect.indexOf(types[i].Name); - if(idx >= 0){ - collect.splice(idx, 1); - } - this._discoverType(types[i], doc, allowedTypes, allowedTypeNames, withNavPropertis, withKeys, true, collect); - } - - for (var i = 0; i < collect.length; i++) - { - this._discoverType({ Name: collect[i], Fields: [] }, doc, allowedTypes, allowedTypeNames, withNavPropertis, withKeys, false, []); - } - - return allowedTypes; - }, - _discoverType: function(typeData, doc, allowedTypes, allowedTypeNames, withNavPropertis, withKeys, collectTypes, collectedTypes) { - var typeName = typeData.Name; - - if (allowedTypeNames.indexOf(typeName) >= 0) - { - return; - } - console.log("Discover: " + typeName); - - var typeShortName = typeName; - var typeNamespace = ''; - if (typeName.lastIndexOf('.') > 0) - { - typeNamespace = typeName.substring(0, typeName.lastIndexOf('.')); - typeShortName = typeName.substring(typeName.lastIndexOf('.') + 1); - } - - var schemaNode = doc.querySelector("Schema[Namespace = '" + typeNamespace + "']"); - if (schemaNode != null) - { - var typeNode = schemaNode.querySelector("EntityType[Name = '" + typeShortName + "'], ComplexType[Name = '" + typeShortName + "']"); - if (typeNode != null) - { - allowedTypes.push(typeData); - allowedTypeNames.push(typeName); - - if (withKeys && typeData.Fields.length > 0) { - var keys = typeNode.querySelectorAll("Key PropertyRef"); - if (keys != null) - { - for (var j = 0; j < keys.length; j++) - { - var keyField = keys[j].attributes["Name"].value; - if (typeData.Fields.indexOf(keyField) < 0) - typeData.Fields.splice(j, 0, keyField); - } - } - } - - if (withNavPropertis) - { - var navPropNodes = typeNode.querySelectorAll("NavigationProperty"); - for (var j = 0; j < navPropNodes.length; j++) - { - var navProp = navPropNodes[j]; - if (typeData.Fields.length == 0 || typeData.Fields.indexOf(navProp.attributes["Name"].value) >=0) - { - - var FromRole = navProp.attributes["FromRole"].value; - var ToRole = navProp.attributes["ToRole"].value; - - var association = schemaNode.querySelector("Association End[Role = '" + FromRole + "']:not([Type = '" + typeName + "'])"); - if (association == null) - { - association = schemaNode.querySelector("Association End[Role = '" + ToRole + "']:not([Type = '" + typeName + "'])"); - } - - if (association != null) - { - var nav_type = association.attributes["Type"].value; - - if (collectTypes) - { - if (collectedTypes.indexOf(nav_type) < 0 && allowedTypeNames.indexOf(nav_type) < 0) - collectedTypes.push(nav_type); - } - else - { - this._discoverType({ Name: nav_type, Fields: [] }, doc, allowedTypes, allowedTypeNames, withNavPropertis, withKeys, false, collectedTypes); - } - } - } - } - } - } - } - }, - - _discoverProperyDependencies: function (types, doc, withNavPropertis, withKeys) { - var allowedTypes = []; - var allowedTypeNames = types.map(function(t) { return t.Name; }); - - for (var i = 0; i < types.length; i++) - { - this._discoverProperties(types[i], doc, allowedTypes, allowedTypeNames, withNavPropertis, withKeys); - } - - return allowedTypes; - }, - _discoverProperties: function(typeData, doc, allowedTypes, allowedTypeNames, withNavPropertis, withKeys) { - var typeName = typeData.Name; - console.log("Discover: " + typeName); - - var hasProperty = typeData.Fields.length != 0; - var typeShortName = typeName; - var typeNamespace = ''; - if (typeName.lastIndexOf('.') > 0) - { - typeNamespace = typeName.substring(0, typeName.lastIndexOf('.')); - typeShortName = typeName.substring(typeName.lastIndexOf('.') + 1); - } - - var schemaNode = doc.querySelector("Schema[Namespace = '" + typeNamespace + "']"); - if (schemaNode != null) - { - var typeNode = schemaNode.querySelector("EntityType[Name = '" + typeShortName + "'], ComplexType[Name = '" + typeShortName + "']"); - if (typeNode != null) - { - allowedTypes.push(typeData); - - if (!hasProperty) - { - var properties = typeNode.querySelectorAll("Property"); - if (properties != null) - { - for (var j = 0; j < properties.length; j++) - { - var field = properties[j].attributes["Name"].value; - typeData.Fields.push(field); - } - } - - if (withNavPropertis) - { - var navPropNodes = typeNode.querySelectorAll("NavigationProperty"); - for (var j = 0; j < navPropNodes.length; j++) - { - var navProp = navPropNodes[j]; - var nav_name = navProp.attributes["Name"].value; - var types = [ navProp.attributes["FromRole"].value, navProp.attributes["ToRole"].value ]; - - var nav_type = ''; - for (var t = 0; t < types.length; t++) - { - var association = schemaNode.querySelector("Association End[Role = '" + types[t] + "']"); - if (association != null) - { - nav_type = association.attributes["Type"].value; - if (nav_type != typeName || t == 1) - break; - } - } - - if (allowedTypeNames.indexOf(nav_type) >= 0) - { - typeData.Fields.push(nav_name); - } - } - } - } - else if (withKeys) - { - var keys = typeNode.querySelectorAll("Key PropertyRef"); - if (keys != null) - { - for (var j = 0; j < keys.length; j++) - { - var keyField = keys[j].attributes["Name"].value; - if (typeData.Fields.indexOf(keyField) < 0) - typeData.Fields.splice(j, 0, keyField); - } - } - } - } - } - }, - - _findVersion: function (metadata) { - var maxDSVersion = ''; - - if (typeof metadata === 'object' && "getElementsByTagName" in metadata){ - var version = 'http://schemas.microsoft.com/ado/2008/09/edm'; - var item = metadata.getElementsByTagName('Schema'); - if (item) - item = item[0]; - if (item) - item = item.attributes; - if (item) - item = item.getNamedItem('xmlns'); - if (item) - version = item.value; - - var maxDSVersion = metadata.getElementsByTagName('edmx:DataServices')[0] || metadata.getElementsByTagName('DataServices')[0]; - if (maxDSVersion) - maxDSVersion = maxDSVersion.attributes.getNamedItem('m:MaxDataServiceVersion'); - if (maxDSVersion && version) - maxDSVersion = maxDSVersion.value; - - - var versionNum = this._supportedODataVersions[version]; - return { - ns: version, - version: versionNum || 'unknown', - maxVersion: maxDSVersion || this._maxDataServiceVersions[version || 'unknown'] - }; - }else if (typeof module !== 'undefined' && typeof require !== 'undefined'){ - var schemaXml = metadata; - - var schemaNamespace = 'http://schemas.microsoft.com/ado/2008/09/edm'; - var version = 'nodejs'; - for (var i in this._supportedODataVersions){ - if (schemaXml.search(new RegExp('= 0){ - schemaNamespace = i; - version = this._supportedODataVersions[i]; - break; - } - } - - return { - ns: schemaNamespace, - version: version, - maxVersion: this._maxDataServiceVersions[version || 'unknown'] - } - } - }, - _supportedODataVersions: { - value: { - "http://schemas.microsoft.com/ado/2006/04/edm": "V1", - "http://schemas.microsoft.com/ado/2008/09/edm": "V2", - "http://schemas.microsoft.com/ado/2009/11/edm": "V3", - "http://schemas.microsoft.com/ado/2007/05/edm": "V11", - "http://schemas.microsoft.com/ado/2009/08/edm": "V22" - } - }, - _maxDataServiceVersions: { - value: { - "http://schemas.microsoft.com/ado/2006/04/edm": "2.0", - "http://schemas.microsoft.com/ado/2008/09/edm": "2.0", - "http://schemas.microsoft.com/ado/2009/11/edm": "3.0", - "http://schemas.microsoft.com/ado/2007/05/edm": "2.0", - "http://schemas.microsoft.com/ado/2009/08/edm": "2.0" - } - }, - _supportedODataVersionXSLT: { - value: "JayDataContextGenerator.xslt" - }, - getCurrentXSLTVersion: function (versionInfo, metadata) { - return this._metadataConverterXSLT.replace('@@VERSIONNS@@', versionInfo.ns).replace('@@VERSION@@', versionInfo.version); - }, - __encodeBase64: function (val) { - var b64array = "ABCDEFGHIJKLMNOP" + - "QRSTUVWXYZabcdef" + - "ghijklmnopqrstuv" + - "wxyz0123456789+/" + - "="; - - var input = val; - var base64 = ""; - var hex = ""; - var chr1, chr2, chr3 = ""; - var enc1, enc2, enc3, enc4 = ""; - var i = 0; - - do { - chr1 = input.charCodeAt(i++); - chr2 = input.charCodeAt(i++); - chr3 = input.charCodeAt(i++); - - enc1 = chr1 >> 2; - enc2 = ((chr1 & 3) << 4) | (chr2 >> 4); - enc3 = ((chr2 & 15) << 2) | (chr3 >> 6); - enc4 = chr3 & 63; - - if (isNaN(chr2)) { - enc3 = enc4 = 64; - } else if (isNaN(chr3)) { - enc4 = 64; - } - - base64 = base64 + - b64array.charAt(enc1) + - b64array.charAt(enc2) + - b64array.charAt(enc3) + - b64array.charAt(enc4); - chr1 = chr2 = chr3 = ""; - enc1 = enc2 = enc3 = enc4 = ""; - } while (i < input.length); - - return base64; - }, - _metadataConverterXSLT: { - type: 'string', - value: - "\r\n" + - "\r\n" + - " \r\n" + - " \r\n" + - "\r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - "\r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - "\r\n" + - " Microsoft.Crm.Sdk.Data.Services.Product;Microsoft.Crm.Sdk.Data.Services.LeadAddress:Telephone1,City,UTCOffset;\r\n" + - "\r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " 0\">\r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - "\r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " 0\">\r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - "\r\n" + - " 0\">\r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " 0\">\r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - "\r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - "\r\n" + - "\r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " 0)\"/>\r\n" + - " 0)\"/>\r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - "\r\n" + - "/*//////////////////////////////////////////////////////////////////////////////////////\r\n" + - "////// Autogenerated by JaySvcUtil.exe http://JayData.org for more info /////////\r\n" + - "////// oData @@VERSION@@ /////////\r\n" + - "//////////////////////////////////////////////////////////////////////////////////////*/\r\n" + - "(function(global, $data, undefined) {\r\n" + - "\r\n" + - " \r\n" + - "\r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " 0)\"/>\r\n" + - " \r\n" + - " \r\n" + - " 0)\"/>\r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " 0)\"/>\r\n" + - " \r\n" + - " \r\n" + - " 0)\"/>\r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - "\r\n" + - " Info: generating type \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - "\r\n" + - "\r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " 0)\"/>\r\n" + - " \r\n" + - " \r\n" + - " 0)\"/>\r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " .extend('.', {\r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " , \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " , \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " ,\r\n" + - " \r\n" + - " ,\r\n" + - " \r\n" + - " \r\n" + - " });\r\n" + - "\r\n" + - "\r\n" + - "\r\n" + - "\r\n" + - "\r\n" + - " .extend('', {\r\n" + - " \r\n" + - "\r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - "\r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " ,\r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " ,\r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " });\r\n" + - "\r\n" + - " $data.generatedContexts = $data.generatedContexts || [];\r\n" + - " $data.generatedContexts.push();\r\n" + - " \r\n" + - " /*Context Instance*/\r\n" + - " = new ({ name:'oData', oDataServiceHost: '', maxDataServiceVersion: '' });\r\n" + - "\r\n" + - "\r\n" + - "\r\n" + - " \r\n" + - "})(window, $data);\r\n" + - " \r\n" + - " \r\n" + - "\r\n" + - " \r\n" + - "\r\n" + - " \r\n" + - " '\r\n" + - " \r\n" + - " ': { type: \r\n" + - " \r\n" + - " \r\n" + - " $data.ServiceAction\r\n" + - " \r\n" + - " \r\n" + - " $data.ServiceOperation\r\n" + - " \r\n" + - " \r\n" + - "\r\n" + - " \r\n" + - "\r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " , params: [\r\n" + - " 1) or (($IsBindable = 'false' or $IsBindable = '') and position() > 0)]\">\r\n" + - " { name: '\r\n" + - " \r\n" + - " ', type: '\r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " ' }\r\n" + - " , \r\n" + - " \r\n" + - " ]\r\n" + - "\r\n" + - " }\r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " , returnType: \r\n" + - " \r\n" + - " null\r\n" + - " $data.Queryable\r\n" + - " \r\n" + - " '\r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " '\r\n" + - " \r\n" + - " \r\n" + - "\r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " , elementType: '\r\n" + - " \r\n" + - " '\r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " , method: '\r\n" + - " \r\n" + - " '\r\n" + - " \r\n" + - " \r\n" + - " , \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " : \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " , '\r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " ': '\r\n" + - " \r\n" + - " '\r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - "\r\n" + - " '': { type: , elementType: }\r\n" + - "\r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " , actions: { \r\n" + - " \r\n" + - " \r\n" + - " ,\r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " }\r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " true\r\n" + - " \r\n" + - " '': { '$':\r\n" + - " , \r\n" + - " \r\n" + - " '$':\r\n" + - " , \r\n" + - " \r\n" + - " }\r\n" + - "\r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - "\r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " 'Array'\r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " ''\r\n" + - " \r\n" + - " \r\n" + - " ''\r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " ''\r\n" + - " \r\n" + - " \r\n" + - " ''\r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - "\r\n" + - " \r\n" + - " $data.ConcurrencyMode.\r\n" + - " \r\n" + - "\r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " true\r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - "\r\n" + - " \r\n" + - " true \r\n" + - " \r\n" + - "\r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " Number.POSITIVE_INFINITY\r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - "\r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " true''\r\n" + - " \r\n" + - "\r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " ''\r\n" + - " ''\r\n" + - " \r\n" + - " '$$unbound'\r\n" + - " \r\n" + - " ''\r\n" + - " \r\n" + - " \r\n" + - " ''\r\n" + - " \r\n" + - " \r\n" + - " ''\r\n" + - " \r\n" + - " \r\n" + - " '$$unbound'\r\n" + - " Warning: inverseProperty other side missing: \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " ''\r\n" + - " true\r\n" + - " \r\n" + - " \r\n" + - " ''\r\n" + - " \r\n" + - " \r\n" + - " '$$unbound'\r\n" + - " \r\n" + - " Warning: inverseProperty other side missing: \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - "\r\n" + - "\r\n" + - " \r\n" + - "\r\n" + - " \r\n" + - " \r\n" + - " Warning: .: is an unknown/unprocessed attribued\r\n" + - " \r\n" + - " \r\n" + - "\r\n" - } - -}); - -$data.MetadataLoader = new $data.MetadataLoaderClass(); -$data.service = function (serviceUri, config, cb) { - var _url, _config, _callback; - function getParam(paramValue) { - switch (typeof paramValue) { - case 'object': - if (typeof paramValue.success === 'function' || typeof paramValue.error === 'function') { - _callback = paramValue; - } else { - _config = paramValue; - } - break; - case 'function': - _callback = paramValue; - break; - default: - break; - } - } - getParam(config); - getParam(cb); - - if (typeof serviceUri === 'object') { - _config = $data.typeSystem.extend(serviceUri, _config); - serviceUri = serviceUri.url; - delete _config.url; - } - - var pHandler = new $data.PromiseHandler(); - _callback = pHandler.createCallback(_callback); - - $data.MetadataLoader.load(serviceUri, { - success: function (factory) { - var type = factory.type; - //register to local store - if (_config) { - var storeAlias = _config.serviceName || _config.storeAlias; - if (storeAlias && 'addStore' in $data) { - $data.addStore(storeAlias, factory, _config.isDefault === undefined || _config.isDefault) - } - } - - _callback.success(factory, type); - }, - error: _callback.error - }, _config); - - return pHandler.getPromise(); -}; -(function ($data) { - if (typeof jQuery !== 'undefined') { - $data.Class.define('$data.Deferred', $data.PromiseHandlerBase, null, { - constructor: function () { - this.deferred = new $.Deferred(); - }, - deferred: {}, - createCallback: function (callBack) { - callBack = $data.typeSystem.createCallbackSetting(callBack); - var self = this; - - return cbWrapper = { - success: function () { - callBack.success.apply(self.deferred, arguments); - self.deferred.resolve.apply(self.deferred, arguments); - }, - error: function () { - Array.prototype.push.call(arguments, self.deferred); - callBack.error.apply(self.deferred, arguments); - }, - notify: function () { - callBack.notify.apply(self.deferred, arguments); - self.deferred.notify.apply(self.deferred, arguments); - } - }; - }, - getPromise: function () { - return this.deferred.promise(); - } - }, null); - - $data.PromiseHandler = $data.Deferred; - } -})($data); -(function ($data) { - - $data.initService = function (apiKey, options) { - var d = new $data.PromiseHandler(); - var cfg; - - if (typeof apiKey === 'object') { - //appId, serviceName, ownerid, isSSL, port, license, url - cfg = apiKey; - var protocol = cfg.isSSL || cfg.isSSL === undefined ? 'https' : 'http'; - var port = cfg.port ? (':' + cfg.port) : ''; - - if (typeof cfg.license === 'string' && cfg.license.toLowerCase() === 'business') { - if (cfg.appId && cfg.serviceName) { - apiKey = protocol + '://' + cfg.appId + '.jaystack.net' + port + '/' + cfg.serviceName; - } else { - apiKey = cfg.url; - } - } else { - if (cfg.ownerId && cfg.appId && cfg.serviceName) { - apiKey = protocol + '://open.jaystack.net/' + cfg.ownerId + '/' + cfg.appId + '/api/' + cfg.serviceName; - } else { - apiKey = cfg.url; - } - } - - delete cfg.url; - cfg = $data.typeSystem.extend(cfg, options); - } else { - cfg = options; - } - - $data.service(apiKey, cfg).then(function (factory) { - var ctx = factory(); - return ctx.onReady() - .then(function (context) { - context.serviceFactory = factory; - d.deferred.resolve(context, factory, factory.type); - }).fail(function () { - d.deferred.reject.apply(d.deferred, arguments); - }); - }).fail(function(){ - d.deferred.reject.apply(d.deferred, arguments); - }); - - return d.getPromise(); - }; - -})($data); diff --git a/release/jaydata.min.js b/release/jaydata.min.js deleted file mode 100644 index 360ec873..00000000 --- a/release/jaydata.min.js +++ /dev/null @@ -1,632 +0,0 @@ -// JayData 1.3.6 -// Dual licensed under MIT and GPL v2 -// Copyright JayStack Technologies (http://jaydata.org/licensing) -// -// JayData is a standards-based, cross-platform Javascript library and a set of -// practices to access and manipulate data from various online and offline sources. -// -// Credits: -// Hajnalka Battancs, Dániel József, János Roden, László Horváth, Péter Nochta -// Péter Zentai, Róbert Bónay, Szabolcs Czinege, Viktor Borza, Viktor Lázár, -// Zoltán Gyebrovszki, Gábor Dolla -// -// More info: http://jaydata.org -(function(a){if("object"==typeof exports&&"object"==typeof module)return a(exports);if("function"==typeof define&&define.amd)return define(["exports"],a);a(self.acorn||(self.acorn={}))})(function(a){function b(a){u=a||{};for(var b in Ua)u.hasOwnProperty(b)||(u[b]=Ua[b]);xa=u.sourceFile||null}function c(a,b){var c=sb(m,a),b=b+(" ("+c.line+":"+c.column+")"),d=new SyntaxError(b);d.pos=a;d.loc=c;d.raisedAt=k;throw d;}function d(a){function b(a){if(1==a.length)return c+="return str === "+JSON.stringify(a[0])+ -";";c+="switch(str){";for(var d=0;da?36===a: -91>a?!0:97>a?95===a:123>a?!0:170<=a&&Va.test(String.fromCharCode(a))}function f(a){return 48>a?36===a:58>a?!0:65>a?!1:91>a?!0:97>a?95===a:123>a?!0:170<=a&&tb.test(String.fromCharCode(a))}function g(){this.line=Q;this.column=k-K}function h(){Q=1;k=K=0;na=!0;j()}function i(a,b){ba=k;u.locations&&(oa=new g);s=a;j();R=b;na=a.beforeExpr}function j(){for(;k -a&&8a&&8=a?a=E(!0):(++k,a=i(ya)),a;case 40:return++k,i(N);case 41:return++k,i(L);case 59:return++k,i(O);case 44:return++k,i(S);case 91:return++k,i(pa);case 93:return++k,i(qa);case 123:return++k,i(da);case 125:return++k,i(Z);case 58:return++k,i(ea);case 63:return++k,i(za);case 48:if(a= -m.charCodeAt(k+1),120===a||88===a)return k+=2,a=z(16),null==a&&c(G+2,"Expected hexadecimal number"),e(m.charCodeAt(k))&&c(k,"Identifier directly after number"),i(fa,a);case 49:case 50:case 51:case 52:case 53:case 54:case 55:case 56:case 57:return E(!1);case 34:case 39:var b;a:{k++;for(var d="";;){k>=Y&&c(G,"Unterminated string constant");var f=m.charCodeAt(k);if(f===a){++k;b=i(ia,d);break a}if(92===f){var f=m.charCodeAt(++k),g=/^[0-7]+/.exec(m.slice(k,k+3));for(g&&(g=g[0]);g&&255=Y)return i(sa);var b=m.charCodeAt(k);if(e(b)||92===b)return v();a=l(b); -if(!1===a){b=String.fromCharCode(b);if("\\"===b||Va.test(b))return v();c(k,"Unexpected character '"+b+"'")}return a}function q(a,b){var c=m.slice(k,k+b);k+=b;i(a,c)}function n(){for(var a="",b,d,e=k;;){k>=Y&&c(e,"Unterminated regular expression");a=m.charAt(k);Ca.test(a)&&c(e,"Unterminated regular expression");if(b)b=!1;else{if("["===a)d=!0;else if("]"===a&&d)d=!1;else if("/"===a&&!d)break;b="\\"===a}++k}a=m.slice(e,k);++k;(b=o())&&!/^[gmsiy]*$/.test(b)&&c(e,"Invalid regexp flag");return i(Da,RegExp(a, -b))}function z(a,b){for(var c=k,d=0,e=0,f=null==b?Infinity:b;e=g?g-48:Infinity;if(g>=a)break;++k;d=d*a+g}return k===c||null!=b&&k-c!==b?null:d}function E(a){var b=k,d=!1,f=48===m.charCodeAt(k);!a&&null===z(10)&&c(b,"Invalid number");46===m.charCodeAt(k)&&(++k,z(10),d=!0);a=m.charCodeAt(k);if(69===a||101===a)a=m.charCodeAt(++k),(43===a||45===a)&&++k,null===z(10)&&c(b,"Invalid number"),d=!0;e(m.charCodeAt(k))&&c(k,"Identifier directly after number"); -var a=m.slice(b,k),g;d?g=parseFloat(a):!f||1===a.length?g=parseInt(a,10):/[89]/.test(a)||J?c(b,"Invalid number"):g=parseInt(a,8);return i(fa,g)}function A(a){a=z(16,a);null===a&&c(G,"Bad character escape sequence");return a}function o(){ha=!1;for(var a,b=!0,d=k;;){var g=m.charCodeAt(k);if(f(g))ha&&(a+=m.charAt(k)),++k;else if(92===g){ha||(a=m.slice(d,k));ha=!0;117!=m.charCodeAt(++k)&&c(k,"Expecting Unicode escape sequence \\uXXXX");++k;var g=A(4),j=String.fromCharCode(g);j||c(k-1,"Invalid Unicode escape"); -(b?e(g):f(g))||c(k-4,"Invalid Unicode escape");a+=j}else break;b=!1}return ha?a:m.slice(d,k)}function v(){var a=o(),b=aa;ha||(Gb(a)?b=Ea[a]:(u.forbidReserved&&(3===u.ecmaVersion?Hb:Ib)(a)||J&&Xa(a))&&c(G,"The keyword '"+a+"' is reserved"));return i(b,a)}function r(){Fa=G;X=ba;Ga=oa;w()}function T(a){J=a;for(k=X;kb){var e=y(a);e.left=a;e.operator=R;r();e.right=Ra(Sa(c),d,c);e=p(e,/&&|\|\|/.test(e.operator)?"LogicalExpression":"BinaryExpression");return Ra(e,b,c)}return a}function Sa(a){if(s.prefix){var b=t(),d=s.isUpdate;b.operator=R;b.prefix=!0;r();b.argument= -Sa(a);d?ta(b.argument):J&&"delete"===b.operator&&"Identifier"===b.argument.type&&c(b.start,"Deleting local variable in strict mode");return p(b,d?"UpdateExpression":"UnaryExpression")}for(a=ma(wa());s.postfix&&!U();)b=y(a),b.operator=R,b.prefix=!1,b.argument=a,ta(a),r(),a=p(b,"UpdateExpression");return a}function ma(a,b){if(x(ya)){var c=y(a);c.object=a;c.property=W(!0);c.computed=!1;return ma(p(c,"MemberExpression"),b)}return x(pa)?(c=y(a),c.object=a,c.property=I(),c.computed=!0,C(qa),ma(p(c,"MemberExpression"), -b)):!b&&x(N)?(c=y(a),c.callee=a,c.arguments=Ta(L,!1),ma(p(c,"CallExpression"),b)):a}function wa(){switch(s){case mb:var a=t();r();return p(a,"ThisExpression");case aa:return W();case fa:case ia:case Da:return a=t(),a.value=R,a.raw=m.slice(G,ba),r(),p(a,"Literal");case nb:case ob:case pb:return a=t(),a.value=s.atomValue,a.raw=s.keyword,r(),p(a,"Literal");case N:var a=ra,b=G;r();var d=I();d.start=b;d.end=ba;u.locations&&(d.loc.start=a,d.loc.end=oa);u.ranges&&(d.range=[b,ba]);C(L);return d;case pa:return a= -t(),r(),a.elements=Ta(qa,!0,!0),p(a,"ArrayExpression");case da:a=t();b=!0;d=!1;a.properties=[];for(r();!x(Z);){if(b)b=!1;else if(C(S),u.allowTrailingCommas&&x(Z))break;var e={key:s===fa||s===ia?wa():W(!0)},f=!1,g;x(ea)?(e.value=I(!0),g=e.kind="init"):5<=u.ecmaVersion&&"Identifier"===e.key.type&&("get"===e.key.name||"set"===e.key.name)?(f=d=!0,g=e.kind=e.key.name,e.key=s===fa||s===ia?wa():W(!0),s!==N&&V(),e.value=Na(t(),!1)):V();if("Identifier"===e.key.type&&(J||d))for(var j=0;jd?a.id:a.params[d],(Xa(e.name)||ua(e.name))&&c(e.start,"Defining '"+e.name+"' in strict mode"),0<=d)for(var f=0;f1&&Guard.raise(Error("Object.create implementation only accepts the first parameter."));b.prototype=a;return new b};if(!Object.keys){var a=Object.prototype.hasOwnProperty,b=!{toString:null}.propertyIsEnumerable("toString"),c=["toString","toLocaleString","valueOf","hasOwnProperty", -"isPrototypeOf","propertyIsEnumerable","constructor"],d=c.length;Object.keys=function(e){(typeof e!=="object"&&typeof e!=="function"||e===null)&&Guard.raise(new TypeError("Object.keys called on non-object"));var f=[],g;for(g in e)a.call(e,g)&&f.push(g);if(b)for(g=0;g=b.length)try{typeof document!="undefined"&&document.createTextNode("").splitText(1);return new RangeError("INDEX_SIZE_ERR")}catch(d){return d}return c[a]},set:function(d){if(isNaN(+a)||(a|0)<0||(a|0)>=b.length)try{typeof document!="undefined"&&document.createTextNode("").splitText(1);return new RangeError("INDEX_SIZE_ERR")}catch(e){return e}c[a| -0]=(d|0)&255}})},d=0;d-1:a in c};this.unregisterType=function(){Guard.raise("Unimplemented")};this.getDefault=function(b){switch(this.resolveType(b)){case a.Number:return 0;case a.Float:return 0;case a.Decimal:return"0.0";case a.Integer:return 0;case a.Int16:return 0;case a.Int32:return 0;case a.Int64:return"0"; -case a.Byte:return 0;case a.SByte:return 0;case a.String:return null;case a.Boolean:return false;default:return null}};this.getIndex=function(a){a=this.resolveType(a);return e.indexOf(a)};this.resolveByIndex=function(a){return e[a]};this.registerType=function(a,f,n){if(a){typeof f==="string"&&(f=g.resolveType(f));var o=[];if(typeof a==="string"){o=[];o.push(a)}else o=a;for(var B=0;B0){D.forEach(function(a){a(f)});i[y.fullName]=[]}}b&&b.registerType.apply(b,arguments);if(!f.name)f.name=o[0].shortName}};var o={from:{},to:{}};this.converters=o;this.convertTo=function(b,c,d,e){Guard.requireValue("typeOrName", -c);if(Object.isNullOrUndefined(b))return b;var f=Container.getTypeName(b),g=Container.resolveType(f),f=Container.resolveName(g),j=Container.resolveType(c),h=Container.resolveName(j),i;try{if(typeof j["from"+f]==="function")i=j["from"+f].apply(j,arguments);else if(typeof g["to"+h]==="function")i=g["to"+h].apply(g,arguments);else if(o.to[h]&&o.to[h][f])i=o.to[h][f].apply(o,arguments);else if(o.from[f]&&o.from[f][h])i=o.from[f][h].apply(o,arguments);else if(h===f||b instanceof j)i=b;else if(o.to[h]&& -o.to[h]["default"])i=o.to[h]["default"].apply(o,arguments);else throw"converter not found";}catch(l){Guard.raise(new Exception("Value '"+f+"' not convertable to '"+h+"'","TypeError",b))}if(j===a.Array&&d&&Array.isArray(i))for(f=0;f0&&!c[0].type)c[0].type=a.Base;for(var g=0,h=c.length;g=0},writable:false,enumerable:false,configurable:false})}if(e){this.buildStaticMembers(a,e);if(e.constructor)a.classConstructor=e.constructor}c&&this.buildInstanceMembers(a,c);b=[].concat(b);b.shift();Object.keys(b).length>0&&this.buildInstanceMixins(a,b);a.__class=true;a.prototype.constructor= -a;Object.defineProperty(a.prototype,"getType",{value:function(){return a},writable:false,enumerable:false,configurable:false})},addMethod:function(a,b,c,d){a[b]=!d||typeof intellisense!=="undefined"?c:function(){return c.apply(this[d],arguments)}},addProperty:function(a,b,c,d){if(d){c.configurable=true;if(c.get){var e=c.get;c.get=function(){this[d]||Guard.raise(new Exception("not inicialized"));return e.apply(this[d],arguments)}}if(c.set){var f=c.set;c.set=function(){this[d]||Guard.raise(new Exception("not inicialized")); -f.apply(this[d],arguments)}}}Object.defineProperty(a,b,c)},addField:function(){Guard.raise("not implemented")},buildMethod:function(a,b,c){this.addMethod(b.classMember?a:a.prototype,b.name,b.method,c)},buildProperty:function(a,b,c){var d=b.classMember?a:a.prototype,e=b.createPropertyDescriptor(a);this.addProperty(d,b.name,e,c);if(!b.classMember&&a.__setPropertyfunctions==true&&b.withoutGetSetMethod!==true&&!("get_"+b.name in d||"set_"+b.name in d)){a=b.createGetMethod();this.addProperty(d,"get_"+ -b.name,a,c);a=b.createSetMethod();this.addProperty(d,"set_"+b.name,a,c)}},buildMember:function(a,b,c,e){e=e||"memberDefinitions";a[e]=a[e]||new d;a[e].setMember(b);switch(b.kind){case f.method:this.buildMethod(a,b,c);break;case f.navProperty:case f.complexProperty:case f.property:this.buildProperty(a,b,c);break;default:Guard.raise("Unknown member type: "+b.kind+","+b.name)}},buildStaticMembers:function(a,b){for(var d in b)if(b.hasOwnProperty(d)){var e=c.translateDefinition(b[d],d,a);e.classMember= -true;this.buildMember(a,e,void 0,"staticDefinitions")}},buildInstanceMembers:function(a,b){for(var d in b)if(b.hasOwnProperty(d)){var e=c.translateDefinition(b[d],d,a);this.buildMember(a,e,void 0,"memberDefinitions")}},copyMembers:function(a,b){Object.keys(a.prototype).forEach(function(c){if(c!=="constructor"&&c!=="toString"){typeof intellisense!=="undefined"&&intellisense.logMessage("copying item:"+c);b.prototype[c]=a[c]}})},buildInstanceMixins:function(a,b){a.mixins=a.mixins||[];a.propagations= -a.propagations||[];for(var c=0;c0&&arguments[arguments.length-1]&&typeof arguments[arguments.length-1].reject==="function"){(console.error||console.log).call(console,arguments[0]);arguments[arguments.length-1].reject.apply(arguments[arguments.length-1],arguments)}else arguments[0]instanceof Error?Guard.raise(arguments[0]):Guard.raise(new Exception("DEFAULT ERROR CALLBACK!","DefaultError",arguments))};$data.defaultSuccessCallback=function(){};$data.defaultNotifyCallback=function(){}; -$data.typeSystem={__namespace:!0,extend:function(a){typeof a!=="object"&&typeof a!=="function"&&Guard.raise("Target must be object or function");for(var b=1;b=0){b=a.substring(0,b).toLowerCase();if((b=$data.GeographyBase.registered[b])&&b.parseFromString&&b!=$data.GeographyBase)return b.parseFromString(a);Guard.raise(new Exception("parseFromString","Not Implemented",a))}}; -$data.GeographyBase.stringifyToUrl=function(a){if(a instanceof $data.GeographyBase&&a.constructor&&a.constructor.stringifyToUrl)return a.constructor.stringifyToUrl(a);if(a instanceof $data.GeographyBase&&a.constructor&&Array.isArray(a.constructor.validMembers)&&a.constructor.validMembers[0]==="coordinates"){var b="geography'"+a.type.toUpperCase()+"(",c=function(a){if(Array.isArray(a[0]))for(var e=0;e0&&(b=b+",");Array.isArray(a[e][0])&&(b=b+"(");c(a[e]);Array.isArray(a[e][0])&&(b= -b+")")}else b=b+a.join(" ")};c(a.coordinates,b);return b=b+")'"}Guard.raise(new Exception("stringifyToUrl on instance type","Not Implemented",a))};$data.GeographyBase.registerType=function(a,b,c){$data.SimpleBase.registerType(a,b,c||$data.GeographyBase);$data.GeographyBase.registered=$data.GeographyBase.registered||{};$data.GeographyBase.registered[a.toLowerCase()]=b}; -$data.GeographyBase.validateGeoJSON=function(a){var b=a.type;if(b){var c=$data.GeographyBase.registered[b.toLowerCase()];if(typeof c.validateGeoJSON==="function"){if(c=c.validateGeoJSON(a))return c;Guard.raise(new Exception("Invalid '"+b+"' format!","Format Exception",a))}}console.log("GeoJSON validation missing",a)};$data.SimpleBase.registerType("GeographyBase",$data.GeographyBase,$data.Geospatial);$data.Container.registerType(["$data.GeographyBase"],$data.GeographyBase); -$data.GeographyPoint=function(a,b){a&&typeof a==="object"&&Array.isArray(a)?$data.GeographyBase.call(this,{coordinates:a}):a&&typeof a==="object"&&("longitude"in a||"latitude"in a)?$data.GeographyBase.call(this,{coordinates:[a.longitude,a.latitude]}):a&&typeof a==="object"&&("lng"in a||"lat"in a)?$data.GeographyBase.call(this,{coordinates:[a.lng,a.lat]}):a&&typeof a==="object"?$data.GeographyBase.call(this,a):$data.GeographyBase.call(this,{coordinates:[a||0,b||0]})}; -$data.GeographyPoint.validateGeoJSON=function(a){return a&&Array.isArray(a.coordinates)&&a.coordinates.length==2&&typeof a.coordinates[0]==="number"&&typeof a.coordinates[1]==="number"};$data.GeographyPoint.parseFromString=function(a){a=a.substring(a.indexOf("(")+1,a.lastIndexOf(")")).split(" ");return new $data.GeographyPoint(parseFloat(a[0]),parseFloat(a[1]))};$data.GeographyPoint.validMembers=["coordinates"];$data.GeographyBase.registerType("Point",$data.GeographyPoint); -Object.defineProperty($data.GeographyPoint.prototype,"longitude",{get:function(){return this.coordinates[0]},set:function(a){this.coordinates[0]=a}});Object.defineProperty($data.GeographyPoint.prototype,"latitude",{get:function(){return this.coordinates[1]},set:function(a){this.coordinates[1]=a}});$data.Container.registerType("$data.GeographyPoint,GeographyPoint,$data.Geography,Geography,geography,geo".split(","),$data.GeographyPoint);$data.Geography=$data.GeographyPoint; -$data.GeographyLineString=function(a){Array.isArray(a)?$data.GeographyBase.call(this,{coordinates:a}):$data.GeographyBase.call(this,a)};$data.GeographyLineString.validateGeoJSON=function(a){for(var b=a&&Array.isArray(a.coordinates),c=0;b&&c=0){b=a.substring(0,b).toLowerCase();if((b=$data.GeometryBase.registered[b])&&b.parseFromString&&b!=$data.GeometryBase)return b.parseFromString(a);Guard.raise(new Exception("parseFromString","Not Implemented",a))}}; -$data.GeometryBase.stringifyToUrl=function(a){if(a instanceof $data.GeometryBase&&a.constructor&&a.constructor.stringifyToUrl)return a.constructor.stringifyToUrl(a);if(a instanceof $data.GeometryBase&&a.constructor&&Array.isArray(a.constructor.validMembers)&&a.constructor.validMembers[0]==="coordinates"){var b="geometry'"+a.type.toUpperCase()+"(",c=function(a){if(Array.isArray(a[0]))for(var e=0;e0&&(b=b+",");Array.isArray(a[e][0])&&(b=b+"(");c(a[e]);Array.isArray(a[e][0])&&(b=b+")")}else b= -b+a.join(" ")};c(a.coordinates,b);return b=b+")'"}Guard.raise(new Exception("stringifyToUrl on instance type","Not Implemented",a))};$data.GeometryBase.registerType=function(a,b,c){$data.SimpleBase.registerType(a,b,c||$data.GeometryBase);$data.GeometryBase.registered=$data.GeometryBase.registered||{};$data.GeometryBase.registered[a.toLowerCase()]=b}; -$data.GeometryBase.validateGeoJSON=function(a){var b=a.type;if(b){var c=$data.GeometryBase.registered[b.toLowerCase()];if(typeof c.validateGeoJSON==="function"){if(c=c.validateGeoJSON(a))return c;Guard.raise(new Exception("Invalid '"+b+"' format!","Format Exception",a))}}console.log("GeoJSON validation missing",a)};$data.SimpleBase.registerType("GeometryBase",$data.GeometryBase,$data.Geospatial);$data.Container.registerType(["$data.GeometryBase"],$data.GeometryBase); -$data.GeometryPoint=function(a,b){a&&typeof a==="object"&&Array.isArray(a)?$data.GeometryBase.call(this,{coordinates:a}):a&&typeof a==="object"&&("x"in a||"y"in a)?$data.GeometryBase.call(this,{coordinates:[a.x,a.y]}):a&&typeof a==="object"?$data.GeometryBase.call(this,a):$data.GeometryBase.call(this,{coordinates:[a||0,b||0]})}; -$data.GeometryPoint.validateGeoJSON=function(a){return a&&Array.isArray(a.coordinates)&&a.coordinates.length==2&&typeof a.coordinates[0]==="number"&&typeof a.coordinates[1]==="number"};$data.GeometryPoint.parseFromString=function(a){a=a.substring(a.indexOf("(")+1,a.lastIndexOf(")")).split(" ");return new $data.GeometryPoint(parseFloat(a[0]),parseFloat(a[1]))};$data.GeometryPoint.validMembers=["coordinates"];$data.GeometryBase.registerType("Point",$data.GeometryPoint); -Object.defineProperty($data.GeometryPoint.prototype,"x",{get:function(){return this.coordinates[0]},set:function(a){this.coordinates[0]=a}});Object.defineProperty($data.GeometryPoint.prototype,"y",{get:function(){return this.coordinates[1]},set:function(a){this.coordinates[1]=a}});$data.Container.registerType(["$data.GeometryPoint","GeometryPoint"],$data.GeometryPoint); -$data.GeometryLineString=function(a){Array.isArray(a)?$data.GeometryBase.call(this,{coordinates:a}):$data.GeometryBase.call(this,a)};$data.GeometryLineString.validateGeoJSON=function(a){for(var b=a&&Array.isArray(a.coordinates),c=0;b&&c>1),c=0,d=1,e=0;c=Math.pow(2,1-d)){g=Math.min(Math.floor(Math.log(a)/Math.LN2),d);f=g+d;d=Math.round(a*Math.pow(2,c-g)-Math.pow(2,c))}else{f=0;d=Math.round(a/Math.pow(2,1-d-c))}}for(a=[];c;c=c-1){a.push(d%2?1:0);d=Math.floor(d/2)}for(c=b;c;c=c-1){a.push(f%2?1:0);f=Math.floor(f/2)}a.push(e? -1:0);a.reverse();b=a.join("");for(e=[];b.length;){e.push(parseInt(b.substring(0,8),2));b=b.substring(8)}return e}; -$data.unpackIEEE754=function(a,b,c){var d=[],e,f,g;for(e=a.length;e;e=e-1){g=a[e-1];for(f=8;f;f=f-1){d.push(g%2?1:0);g=g>>1}}d.reverse();f=d.join("");a=(1<0?d*Math.pow(2,e-a)*(1+f/Math.pow(2,c)):f!==0?d*Math.pow(2,-(a-1))*(f/Math.pow(2,c)):d<0?-0.0:0};$data.IEEE754=function(a,b,c){return $data.unpackIEEE754($data.packIEEE754(a,b,c),b,c)}; -$data.Container.registerConverter("$data.Float",{"default":function(a){a=+a;if(isNaN(a))throw 0;return $data.IEEE754(a,8,23)}});$data.Container.registerConverter("$data.Int16",{"default":function(a){a=(a|0)&65535;return a>=32768?a-65536:a}}); -$data.Container.registerConverter("$data.Int64",{"$data.Boolean":function(a){return a?"1":"0"},"$data.Number":function(a){a=a.toString();if(a.indexOf(".")>0)return a.split(".")[0];if(a.indexOf(".")==0)throw 0;return a},"$data.String":function(a){if(!/^\-?([0-9]+(\.[0-9]+)?|Infinity)$/.test(a))throw 0;if(a.indexOf(".")>0)return a.split(".")[0];if(a.indexOf(".")==0)throw 0;return a},"$data.Date":function(a){a=a.valueOf();if(isNaN(a))throw 0;return a.toString()}}); -$data.Container.registerConverter("$data.SByte",{"default":function(a){a=(a|0)&255;return a>=128?a-256:a}});$data.Container.registerConverter("$data.String",{"$data.Date":function(a){return a.toISOString()},"$data.ObjectID":function(a){return btoa(a.toString())},"default":function(a){return typeof a==="object"?JSON.stringify(a):a.toString()}});$data.Container.registerConverter("$data.Object",{"$data.String":function(a){return JSON.parse(a)},"$data.Function":function(){throw 0;}}); -$data.Container.registerConverter("$data.Array",{"$data.String":function(a){a=JSON.parse(a);if(!Array.isArray(a))throw 0;return a}});$data.Container.registerConverter("$data.ObjectID",{"$data.ObjectID":function(a){try{return btoa(a.toString())}catch(b){return a}},"$data.String":function(a){return a}});$data.Container.proxyConverter=function(a){return a};$data.Container.defaultConverter=function(a){return function(b){return $data.Container.convertTo(b,a)}}; -$data.StringFunctions={startsWith:function(){var a,b;if(arguments.length==2){a=arguments[0];b=arguments[1]}else if(arguments.length==1&&typeof this==="string"){a=this;b=arguments[0]}else if(this instanceof String){a=this.valueOf();b=arguments[0]}else return false;return typeof a!=="string"?false:a.indexOf(b)===0},endsWith:function(){var a,b;if(arguments.length==2){a=arguments[0];b=arguments[1]}else if(arguments.length==1&&typeof this==="string"){a=this;b=arguments[0]}else if(this instanceof String){a= -this.valueOf();b=arguments[0]}else return false;return typeof a!=="string"?false:a.slice(-b.length)===b},contains:function(){var a,b;if(arguments.length==2){a=arguments[0];b=arguments[1]}else if(arguments.length==1&&typeof this==="string"){a=this;b=arguments[0]}else if(this instanceof String){a=this.valueOf();b=arguments[0]}else return false;return typeof a!=="string"?false:a.indexOf(b)>=0}}; -$data.Class.define("$data.Expressions.ExpressionType",null,null,{},{Constant:"constant",Variable:"variable",MemberAccess:"memberAccess",Call:"call",Equal:"equal",NotEqual:"notEqual",EqualTyped:"equalTyped",NotEqualTyped:"notEqualTyped",GreaterThen:"greaterThan",LessThen:"lessThan",GreaterThenOrEqual:"greaterThanOrEqual",LessThenOrEqual:"lessThenOrEqual",Or:"or",OrBitwise:"orBitwise",And:"and",AndBitwise:"andBitwise",In:"in",Add:"add",Divide:"divide",Multiply:"multiply",Subtract:"subtract",Modulo:"modulo", -ArrayIndex:"arrayIndex",New:"new",Positive:"positive",Negative:"negative",Increment:"increment",Decrement:"decrement",Not:"not",This:"this",LambdaParameterReference:"lambdaParameterReference",LambdaParameter:"lambdaParameter",Parameter:"parameter",ArrayLiteral:"arrayLiteral",ObjectLiteral:"objectLiteral",ObjectField:"objectField",Function:"Function",Unknown:"UNKNOWN",EntitySet:"EntitySet",ServiceOperation:"ServiceOperation",EntityField:"EntityField",EntityContext:"EntityContext",Entity:"Entity",Filter:"Filter", -First:"First",Count:"Count",InlineCount:"InlineCount",Single:"Single",Find:"Find",Some:"Some",Every:"Every",ToArray:"ToArray",BatchDelete:"BatchDelete",ForEach:"ForEach",Projection:"Projection",EntityMember:"EntityMember",EntityFieldOperation:"EntityFieldOperation",FrameOperation:"FrameOperation",EntityFunctionOperation:"EntityFunctionOperation",ContextFunctionOperation:"ContextFunctionOperation",EntityBinary:"EntityBinary",Code:"Code",ParametricQuery:"ParametricQuery",MemberInfo:"MemberInfo",QueryParameter:"QueryParameter", -ComplexEntityField:"ComplexEntityField",Take:"Take",Skip:"Skip",OrderBy:"OrderBy",OrderByDescending:"OrderByDescending",Include:"Include",IndexedPhysicalAnd:"IndexedDBPhysicalAndFilterExpression",IndexedLogicalAnd:"IndexedDBLogicalAndFilterExpression",IndexedLogicalOr:"IndexedDBLogicalOrFilterExpression",IndexedLogicalIn:"IndexedDBLogicalInFilterExpression"});$data.BinaryOperator=function(){}; -$data.binaryOperators=[{operator:"==",expressionType:$data.Expressions.ExpressionType.Equal,type:"boolean",implementation:function(a,b){return a==b}},{operator:"===",expressionType:$data.Expressions.ExpressionType.EqualTyped,type:"boolean",implementation:function(a,b){return a===b}},{operator:"!=",expressionType:$data.Expressions.ExpressionType.NotEqual,type:"boolean",implementation:function(a,b){return a!=b}},{operator:"!==",expressionType:$data.Expressions.ExpressionType.NotEqualTyped,type:"boolean", -implementation:function(a,b){return a!==b}},{operator:">",expressionType:$data.Expressions.ExpressionType.GreaterThen,type:"boolean",implementation:function(a,b){return a>b}},{operator:">=",expressionType:$data.Expressions.ExpressionType.GreaterThenOrEqual,type:"boolean",implementation:function(a,b){return a>=b}},{operator:"<=",expressionType:$data.Expressions.ExpressionType.LessThenOrEqual,type:"boolean",implementation:function(a,b){return a<=b}},{operator:"<",expressionType:$data.Expressions.ExpressionType.LessThen, -type:"boolean",implementation:function(a,b){return a0)return a};$data.binaryOperators.contains=function(a){return $data.binaryOperators.some(function(b){return b.operator==a})};$data.binaryOperators.getOperator=function(a){var b=$data.binaryOperators.filter(function(b){return b.operator==a});b.length<1&&Guard.raise("Unknown operator: "+a);return b[0]}; -$data.unaryOperators=[{operator:"+",arity:"prefix",expressionType:$data.Expressions.ExpressionType.Positive,type:"number",implementation:function(a){return+a}},{operator:"-",arity:"prefix",expressionType:$data.Expressions.ExpressionType.Negative,type:"number",implementation:function(a){return-a}},{operator:"++",arity:"prefix",expressionType:$data.Expressions.ExpressionType.Increment,type:"number",implementation:function(a){return++a}},{operator:"--",arity:"prefix",expressionType:$data.Expressions.ExpressionType.Decrement, -type:"number",implementation:function(a){return--a}},{operator:"++",arity:"suffix",expressionType:$data.Expressions.ExpressionType.Increment,type:"number",implementation:function(a){return a++}},{operator:"!",arity:"prefix",expressionType:$data.Expressions.ExpressionType.Not,type:"boolean",implementation:function(a){return!a}},{operator:"--",arity:"suffix",expressionType:$data.Expressions.ExpressionType.Decrement,type:"number",implementation:function(a){return a--}}]; -$data.unaryOperators.resolve=function(a){if($data.unaryOperators.filter(function(b){return b.operator==a}).length>0)return a};$data.unaryOperators.contains=function(a){return $data.unaryOperators.some(function(b){return b.operator==a})};$data.unaryOperators.getOperator=function(a,b){var c=$data.unaryOperators.filter(function(c){return c.operator==a&&(!b||c.arity==b)});c.length<1&&Guard.raise("Unknown operator: "+a);return c[0]}; -$data.timeIt=function(a,b){b=b||1;console.time("!");for(var c=0;c-1?$data.Expressions.ExpressionType.LambdaParameterReference:$data.Expressions.ExpressionType.Parameter)},ParserObjectExpression:function(a){for(var b=Array(a.properties.length),c=0;c":{expressionType:$data.Expressions.ExpressionType.GreaterThen,type:"boolean",implementation:function(a,b){return a>b}},">=":{expressionType:$data.Expressions.ExpressionType.GreaterThenOrEqual,type:"boolean",implementation:function(a, -b){return a>=b}},"<=":{expressionType:$data.Expressions.ExpressionType.LessThenOrEqual,type:"boolean",implementation:function(a,b){return a<=b}},"<":{expressionType:$data.Expressions.ExpressionType.LessThen,type:"boolean",implementation:function(a,b){return a=0,e=c==b.paramsName;!e&&!d&&Guard.raise("Variable is not defined in the paramContext or the lambda parameters: "+c);return $data.Expressions.ExpressionNodeTypes.MemberAccessExpressionNode.create(e,a.expression,a.member)}},null); -$data.Class.define("$data.Expressions.ExecutorVisitor",$data.Expressions.ExpTreeVisitor,null,{VisitVariable:function(a,b){if(!a.executable)return a;var c=a.name==b.paramsName?b.paramContext:window[a.name];typeof c=="undefined"&&Guard.raise(new Exception("Unknown variable in '"+b.operation+"' operation. The variable isn't referenced in the parameter context and it's not a global variable: '"+a.name+"'.","InvalidOperation",{operationName:b.operation,missingParameterName:a.name}));return $data.Expressions.ExpressionNodeTypes.LiteralExpressionNode.create(true, -typeof c,c)},VisitMember:function(a,b){if(!a.executable)return a;for(var c=this.GetMemberChain(a),d,e=0;e","<",">=","<="].indexOf(a.value)>=0?c=this.BuildEquality(a,b):["&&","||"].indexOf(a.value)>=0?c=this.BuildBinary(a,b):["+","-","*","/","%"].indexOf(a.value)>=0?c=this.BuildBinary(a,b):"["==a.value?c=this.BuildArrayAccess(a,b):Guard.raise("Value of infix node isn't implemented: "+a.value);break;case "prefix":["+","-","!"].indexOf(a.value)>=0?c=this.BuildUnary(a,b):["++","--"].indexOf(a.value)>=0?c=this.BuildIncDec(a,b):"{"==a.value?c=this.BuildNewExpression(a,b):Guard.raise("Value of prefix node isn't implemented: "+ -a.value);break;case "suffix":["++","--"].indexOf(a.value)>=0?c=this.BuildIncDec(a,b):Guard.raise("Value of suffix node isn't implemented: "+a.value);break;case "string":case "number":c=this.BuildLiteral(a,b);break;case "ternary":a.value=="?"?c=this.BuildDecision(a,b):Guard.raise("Value of ternary node isn't implemented: "+a.value);break;case null:case void 0:c=a.type=="boolean"&&(a.value=="true"||a.value=="false")?this.BuildBoolLiteral(a,b):this.BuildVariable(a,b);break;default:Guard.raise("Arity isn't implemented: "+ -a.arity)}return c},BuildNewExpression:function(a){for(var b=$data.Expressions.ExpressionNodeTypes.NewExpressionNode.create(true,[]),a=a.first,c=0;c=b.pageSize;c=Container.createConstantExpression(Math.max(c,0),"number"); -return Container.createPagingExpression(a.source,c,a.nodeType)}if(b.append)b.abort=true;break;case "next":if(a.nodeType===$data.Expressions.ExpressionType.Skip){c=b.pageSize+a.amount.value;c=Container.createConstantExpression(c,"number");return Container.createPagingExpression(a.source,c,a.nodeType)}if(b.append){c=Container.createConstantExpression(b.pageSize,"number");return Container.createPagingExpression(a,c,$data.Expressions.ExpressionType.Skip)}}this.Visit(a.source,b)}}); -$data.Class.define("$data.Validation.ValidationError",null,null,{constructor:function(a,b,c){this.Message=a;this.PropertyDefinition=b;this.Type=c},Type:{dataType:"string"},Message:{dataType:"string"},PropertyDefinition:{dataType:$data.MemberDefinition}},null); -$data.Class.define("$data.Validation.EntityValidationBase",null,null,{ValidateEntity:function(){return[]},ValidateEntityField:function(){return[]},getValidationValue:function(){Guard.raise("Pure class")},getValidationMessage:function(){Guard.raise("Pure class")}},null);$data.Validation=$data.Validation||{};$data.Validation.Entity=new $data.Validation.EntityValidationBase; -$data.Class.define("$data.Validation.Defaults",null,null,null,{validators:{value:{required:function(a){return!Object.isNullOrUndefined(a)},customValidator:function(a,b){return Object.isNullOrUndefined(a)||typeof b=="function"?b(a):true},minValue:function(a,b){return Object.isNullOrUndefined(a)||a>=b},maxValue:function(a,b){return Object.isNullOrUndefined(a)||a<=b},minLength:function(a,b){return Object.isNullOrUndefined(a)||a.length>=b},maxLength:function(a,b){return Object.isNullOrUndefined(a)||a.length<= -b},length:function(a,b){return Object.isNullOrUndefined(a)||a.length==b},regex:function(a,b){return Object.isNullOrUndefined(a)||a.match(typeof b==="string"?RegExp(b.indexOf("/")===0&&b.lastIndexOf("/")===b.length-1?b.slice(1,-1):b):b)}}},_getGroupValidations:function(a){var b={};if(Array.isArray(a))for(var c=0;c-1){var g=d.getMember(f),h=Container.resolveType(g.type), -i=a[f];if(g.concurrencyMode===$data.ConcurrencyMode.Fixed)this.initData[f]=i;else{if(b&&b.converters){var j=b.converters[Container.resolveName(h)];j&&(i=j(i))}this.initData[f]=Container.convertTo(i,h,g.elementType,b)}}}b&&b.entityBuilder&&b.entityBuilder(this,c.memberDefinitions.asArray(),c);this.entityState=this.changedProperties=void 0},toString:function(){return this.getType().fullName+"("+(this.Id||this.Name||"")+")"},toJSON:function(){var a={},b=this;this.getType().memberDefinitions.getPublicMappedProperties().forEach(function(c){a[c.name]= -b[c.name]instanceof Date&&c.type&&Container.resolveType(c.type)===$data.DateTimeOffset?new $data.DateTimeOffset(b[c.name]):b[c.name]});return a},equals:function(a){if(a.getType()!==this.getType())return false;for(var b=this.getType().memberDefinitions.getKeyProperties(),c=0;c0){d=this[a.name];d=new PropertyValidationEventData(a.name,d,b,e);this._propertyValidationError&&this.propertyValidationError.fire(d);"instancePropertyValidationError"in this.constructor&&this.constructor.instancePropertyValidationError.fire(d, -this);if(d.cancel==true)return}}a.storeOnObject==true?this["_"+a.name]=b:this.initData[a.name]=b;this.isValidated=false;if(a.monitorChanges!=false&&this.entityState==$data.EntityState.Unchanged)this.entityState=$data.EntityState.Modified;this._setPropertyChanged(a);if(a.monitorChanges!=false){this._propertyChanged&&this.propertyChanged.fire(c);"instancePropertyChanged"in this.constructor&&this.constructor.instancePropertyChanged.fire(c,this)}},_setPropertyChanged:function(a){if(a.monitorChanges!= -false){if(!this.changedProperties)this.changedProperties=[];this.changedProperties.some(function(b){return b.name==a.name})||this.changedProperties.push(a)}},retrieveProperty:function(a){return a.storeOnObject==true?this["_"+a.name]:this.initData[a.name]},getProperty:function(a,b,c){b=$data.typeSystem.createCallbackSetting(b);if(this[a.name]!=void 0)c instanceof $data.Transaction?b.success(this[a.name],c):b.success(this[a.name]);else{var d=this.context;if(this.context)return d.loadItemProperty(this, -a,b,c);try{var e=this,f=this.storeToken||this.getType().storeToken;if(f&&typeof f.factory==="function")return f.factory().onReady().then(function(c){return c.loadItemProperty(e,a,b)})}catch(g){}Guard.raise(new Exception("Entity not in context","Invalid operation"))}},setProperty:function(a,b,c){this[a.name]=b;b=new $data.PromiseHandler;c=b.createCallback(c);c.success(this[a.name]);return b.getPromise()},isValid:function(){if(!this.isValidated){this.ValidationErrors=$data.Validation.Entity.ValidateEntity(this); -this.isValidated=true}return this.ValidationErrors.length==0},isValidated:{dataType:"bool",storeOnObject:!0,monitorChanges:!1,notMapped:!0,enumerable:!1,value:!1},ValidationErrors:{dataType:Array,elementType:$data.Validation.ValidationError,storeOnObject:!0,monitorChanges:!0,notMapped:!0,enumerable:!1},resetChanges:function(){delete this._changedProperties},changedProperties:{dataType:Array,elementType:window.MemberDefinition,storeOnObject:!0,monitorChanges:!1,notMapped:!0,enumerable:!1},entityState:{dataType:"integer", -storeOnObject:!0,monitorChanges:!1,notMapped:!0,enumerable:!1},remove:function(){if($data.ItemStore&&"EntityInstanceRemove"in $data.ItemStore)return $data.ItemStore.EntityInstanceRemove.apply(this,arguments);throw"not implemented";},save:function(){if($data.ItemStore&&"EntityInstanceSave"in $data.ItemStore)return $data.ItemStore.EntityInstanceSave.apply(this,arguments);throw"not implemented";},refresh:function(){if($data.ItemStore&&"EntityInstanceSave"in $data.ItemStore)return $data.ItemStore.EntityInstanceRefresh.apply(this, -arguments);throw"not implemented";},storeToken:{type:Object,monitorChanges:!1,notMapped:!0,storeOnObject:!0},getFieldUrl:function(a){if(this.context)return this.context.getFieldUrl(this,a);if(this.getType().storeToken&&typeof this.getType().storeToken.factory==="function")return this.getType().storeToken.factory().getFieldUrl(this,a);if(this.getType().storeToken)try{var b=$data.ItemStore._getContextPromise("default",this.getType());if(b instanceof $data.EntityContext)return b.getFieldUrl(this,a)}catch(c){}return"#"}}, -{__setPropertyfunctions:{value:!0,notMapped:!0,enumerable:!1,storeOnObject:!0},__copyPropertiesToInstance:{value:!1,notMapped:!0,enumerable:!1,storeOnObject:!0},inheritedTypeProcessor:function(a){$data.ItemStore&&"EntityInheritedTypeProcessor"in $data.ItemStore&&$data.ItemStore.EntityInheritedTypeProcessor.apply(this,arguments);a.defaultValues={};a.memberDefinitions.asArray().forEach(function(b){if(b.hasOwnProperty("defaultValue"))a.defaultValues[b.name]=b.defaultValue});if(Object.keys(a.defaultValues).length> -0)a.setDefaultValues=function(b,c){var b=b||{},d=a.defaultValues,e;for(e in d)e in b||(b[e]="function"===typeof d[e]?d[e](e,c):d[e]);return b}},addEventListener:function(a,b){var c="on"+a;c in this||(this[c]=new $data.Event(a,this));this[c].attach(b)},removeEventListener:function(a,b){var c="on"+a;c in this&&this[c].detach(b)},raiseEvent:function(a,b){var c="on"+a;c in this&&this[c].fire(b)},getFieldNames:function(){return this.memberDefinitions.getPublicMappedPropertyNames()},"from$data.Object":function(a, -b,c,d){if(Object.isNullOrUndefined(a))return a;var e;d&&d.converters&&(e={converters:d.converters});return new this(a,e)}}); -$data.define=function(a,b,c){if(b&&!(b instanceof $data.ContainerClass)){c=b;b=void 0}if(!c)throw Error("json object type is not supported yet");var d={},e=[];Object.keys(c).forEach(function(a){var b=c[a];if(typeof b==="object"&&("type"in b||"get"in b||"set"in b)){d[a]=b;b.key&&e.push(b);if(("get"in b||"set"in b)&&(!("notMapped"in b)||b.notMapped===true)){b.notMapped=true;b.storeOnObject=true}if("get"in b&&!("set"in b))b.set=function(){};else if("set"in b&&!("get"in b))b.get=function(){}}else d[a]= -{type:b}});if(e.length<1){var f;switch(true){case "id"in d:f="id";break;case "Id"in d:f="Id";break;case "ID"in d:f="ID"}if(f){d[f].key=true;$data.Container.resolveName(d[f].type);d[f].computed=true}else d.Id={type:"int",key:true,computed:true}}return $data.Entity.extend(a,b,d)};$data.implementation=function(a){return Container.resolveType(a)};(function(){$data.defaults=$data.defaults||{};$data.defaults.defaultDatabaseName="JayDataDefault"})(); -$data.Class.define("$data.StorageModel",null,null,{constructor:function(){this.ComplexTypes=[];this.Associations=[]},LogicalType:{},LogicalTypeName:{},PhysicalType:{},PhysicalTypeName:{},EventHandlers:{},TableName:{},TableOptions:{value:void 0},ComplexTypes:{},Associations:{},ContextType:{},Roles:{}},null); -$data.Class.define("$data.Association",null,null,{constructor:function(a){if(a){this.From=a.From;this.FromType=a.FromType;this.FromMultiplicity=a.FromMultiplicity;this.FromPropertyName=a.FromPropertyName;this.To=a.To;this.ToType=a.ToType;this.ToMultiplicity=a.ToMultiplicity;this.ToPropertyName=a.ToPropertyName}},From:{},FromType:{},FromMultiplicity:{},FromPropertyName:{},To:{},ToType:{},ToMultiplicity:{},ToPropertyName:{},ReferentialConstraint:{}},null); -$data.Class.define("$data.ComplexType",$data.Association,null,{},null); -$data.Class.define("$data.EntityContext",null,null,{constructor:function(a){$data.ItemStore&&"ContextRegister"in $data.ItemStore&&$data.ItemStore.ContextRegister.apply(this,arguments);if(a.queryCache)this.queryCache=a.queryCache;"string"===typeof a&&(a=0===a.indexOf("http")?{name:"oData",oDataServiceHost:a}:{name:"local",databaseName:a});if("provider"in a)a.name=a.provider;this.trackChanges=this.lazyLoad=false;this._entitySetReferences={};this._storageModel=[];var b=this;b._isOK=false;var c=this._successInitProvider; -this._successInitProvider=function(a){a instanceof $data.EntityContext?c(b):c(b,a)};this._storageModel.getStorageModel=function(a){a=Container.resolveName(a);return b._storageModel[a]};if(typeof a.name==="string")a.name=[a.name];var d=[].concat(a.name),e=$data.typeSystem.createCallbackSetting({success:this._successInitProvider,error:this._successInitProvider});this._initStorageModelSync();b._initializeEntitySets(b.getType());$data.StorageProviderLoader.load(d,{success:function(c){b.storageProvider= -new c(a,b);b.storageProvider.setContext(b);b.stateManager=new $data.EntityStateManager(b);var d=b.getType();if(c.name in d._storageModelCache)b._storageModel=d._storageModelCache[c.name];else{b._initializeStorageModel();d._storageModelCache[c.name]=b._storageModel}a&&a.user&&Object.defineProperty(b,"user",{value:a.user,enumerable:true});a&&a.checkPermission&&Object.defineProperty(b,"checkPermission",{value:a.checkPermission,enumerable:true});b._initializeStore(e)},error:function(){e.error("Provider fallback failed!")}}); -this.addEventListener=function(a,b){var c="on"+a;c in this||(this[c]=new $data.Event(a,this));this[c].attach(b)};this.removeEventListener=function(a,b){var c="on"+a;c in this&&this[c].detach(b)};this.raiseEvent=function(a,b){var c="on"+a;c in this&&this[c].fire(b)};this.ready=this.onReady({success:$data.defaultSuccessCallback,error:function(){$data.PromiseHandler!==$data.PromiseHandlerBase?$data.defaultErrorCallback.apply(this,arguments):$data.Trace.error(arguments)}})},beginTransaction:function(a, -b,c){function d(a){Object.isNullOrUndefined(a)||(typeof a==="boolean"?g=a:Array.isArray(a)?e=a:f=a)}var e=null,f=null,g=false;d(a);d(b);d(c);a=new $data.PromiseHandler;f=a.createCallback(f);this.storageProvider._beginTran(e,g,f);return a.getPromise()},_isReturnTransaction:function(a){return a instanceof $data.Base||a==="returnTransaction"},_applyTransaction:function(a,b,c,d,e){if(e===true)if(d instanceof $data.Transaction){Array.prototype.push.call(c,d);b.apply(a,c)}else this.beginTransaction(function(d){Array.prototype.push.call(c, -d);b.apply(a,c)});else b.apply(a,c)},getDataType:function(a){if(typeof a=="string"){var b=this[a];if(b===void 0||b===null)b=eval(a);return b}return a},_initializeEntitySets:function(a){for(var b=0,c=this._storageModel.length;b-1&&Object.isNullOrUndefined(e.inverseProperty)){var g=JSON.parse(JSON.stringify(e));g.dataType=f;c[e.name]=g}else{this._buildDbType_navigationPropertyComplite(e,f,b);if((f===$data.Array||f.isAssignableTo&&f.isAssignableTo($data.EntitySet))&&e.inverseProperty&&e.inverseProperty!=="$$unbound")this._buildDbType_Collection_OneManyDefinition(c,b,f,e);else if(e.inverseProperty)if(e.inverseProperty==="$$unbound")f===$data.Array?this._buildDbType_Collection_OneManyDefinition(c, -b,f,e):this._buildDbType_ElementType_OneManyDefinition(c,b,f,e);else(g=f.memberDefinitions.getMember(e.inverseProperty))?g.elementType?Container.resolveType(g.elementType)===b.LogicalType?this._buildDbType_ElementType_OneManyDefinition(c,b,f,e):typeof intellisense==="undefined"&&Guard.raise(new Exception("Inverse property not valid, refereed item element type not match: "+b.LogicalTypeName,", property: "+e.name)):this._buildDbType_ElementType_OneOneDefinition(c,b,f,e):typeof intellisense==="undefined"&& -Guard.raise(new Exception("Inverse property not valid"));else this._buildDbType_addComplexTypePropertyDefinition(c,b,f,e)}}this._buildDbType_modifyInstanceDefinition(c,b,this);d={};d.convertTo=this._buildDbType_generateConvertToFunction(b,this);this._buildDbType_modifyClassDefinition(d,b,this);b.PhysicalType=$data.Class.define(b.PhysicalTypeName,$data.Entity,b.LogicalType.container,c,d)}},_initializeActions:function(a,b,c){if(c&&c.actions)for(var d=Object.keys(c.actions),e=0;e1&&typeof intellisense!=="undefined"&&Guard.raise(new Exception("More than one inverse property refer to this member definition: "+a.name+ -", type: "+Container.resolveName(c.LogicalType)));if(c=d.pop())a.inverseProperty=c.name}}},_buildDbType_generateConvertToFunction:function(){return function(a){return a}},_buildDbType_modifyInstanceDefinition:function(){},_buildDbType_modifyClassDefinition:function(){},_buildDbType_addComplexTypePropertyDefinition:function(a,b,c,d){this._addNavigationPropertyDefinition(a,d,d.name,$data.MemberTypes.complexProperty);a=this._createComplexElement(b.LogicalType,"",d.name,c,"","");b.ComplexTypes[d.name]= -a;b.ComplexTypes.push(a)},_buildDbType_Collection_OneManyDefinition:function(a,b,c,d){c=Container.resolveType(d.elementType);(c===void 0||c===null)&&typeof intellisense==="undefined"&&Guard.raise(new Exception("Element type definition error","Field definition",d));(c=this._storageModel.getStorageModel(c))||typeof intellisense==="undefined"&&Guard.raise(new Exception("No EntitySet definition for the following element type","Field definition",d));this._addNavigationPropertyDefinition(a,d,d.name);a= -this._addAssociationElement(b.LogicalType,d.inverseProperty==="$$unbound"?"$$unbound":"0..1",d.name,c.LogicalType,"*",d.inverseProperty);b.Associations[d.name]=a;b.Associations.push(a)},_buildDbType_ElementType_OneManyDefinition:function(a,b,c,d){c=Container.resolveType(d.dataType);(c===void 0||c===null)&&typeof intellisense==="undefined"&&Guard.raise(new Exception("Element type definition error","Field definition",d));(c=this._storageModel.getStorageModel(c))||typeof intellisense==="undefined"&& -Guard.raise(new Exception("No EntitySet definition for the following element type","Field definition",d));this._addNavigationPropertyDefinition(a,d,d.name);a=this._addAssociationElement(b.LogicalType,d.inverseProperty==="$$unbound"?"$$unbound":"*",d.name,c.LogicalType,"0..1",d.inverseProperty);b.Associations[d.name]=a;b.Associations.push(a)},_buildDbType_ElementType_OneOneDefinition:function(a,b,c,d){c=Container.resolveType(d.dataType);(c===void 0||c===null)&&typeof intellisense==="undefined"&&Guard.raise(new Exception("Element type definition error", -"Field definition",d));(c=this._storageModel.getStorageModel(c))||typeof intellisense==="undefined"&&Guard.raise(new Exception("No EntitySet definition following element type","Field definition",d));!c.LogicalType.memberDefinitions.getMember(d.inverseProperty).required&&!d.required&&typeof intellisense==="undefined"&&typeof intellisense==="undefined"&&Guard.raise(new Exception("In one to one connection, one side must required!","One to One connection",d));this._addNavigationPropertyDefinition(a,d, -d.name);a=this._addAssociationElement(b.LogicalType,d.required?"0..1":"1",d.name,c.LogicalType,d.required?"1":"0..1",d.inverseProperty);b.Associations[d.name]=a;b.Associations.push(a)},_addNavigationPropertyDefinition:function(a,b,c,d){var e=JSON.parse(JSON.stringify(b));e.dataType=$data.EntitySet;e.notMapped=true;e.kind=d?d:$data.MemberTypes.navProperty;e.association=c;a[b.name]=e},_addAssociationElement:function(a,b,c,d,e,f){return new $data.Association({From:a.name,FromType:a,FromMultiplicity:b, -FromPropertyName:c,To:d.name,ToType:d,ToMultiplicity:e,ReferentialConstraint:[],ToPropertyName:f})},_createComplexElement:function(a,b,c,d,e,f){return new $data.ComplexType({From:a.name,FromType:a,FromMultiplicity:b,FromPropertyName:c,To:d.name,ToType:d,ToMultiplicity:e,ReferentialConstraint:[],ToPropertyName:f})},_successInitProvider:function(a,b){if(a instanceof $data.EntityContext&&a._isOK!==void 0)if(b){a._isOK=b;if(a.onReadyFunction){for(c=0;c0;){for(var i=[],j=0;j0){f.error(h);return e}for(var h=$data.Access.None,t={},n=[],j=0;j0&&(g=g+" &&");g=g+(" e."+j.name+" == this.key"+i);h["key"+i]=a[j.name]}g=g+"; }";a=this.getEntitySetFromElementType(a.getType());return a.map("function (e) { return e."+b.name+" }").single(g,h,c,d)}g="function (e) { return";h={};for(i=0;i0&&(g=g+" &&");g=g+(" e."+b.inverseProperty+"."+j.name+" == this.key"+i);h["key"+i]=a[j.name]}g=g+"; }";a=this.getEntitySetFromElementType(e); -return a.filter(g,h).toArray(c,d)},getTraceString:function(a){return this.storageProvider.getTraceString(new $data.Query(a.expression,a.defaultType,this))},log:function(){},resolveBinaryOperator:function(a,b,c){return this.storageProvider.resolveBinaryOperator(a,b,c)},resolveUnaryOperator:function(a,b,c){return this.storageProvider.resolveUnaryOperator(a,b,c)},resolveFieldOperation:function(a,b,c){return this.storageProvider.resolveFieldOperation(a,b,c)},resolveSetOperations:function(a,b,c){return this.storageProvider.resolveSetOperations(a, -b,c)},resolveTypeOperations:function(a,b,c){return this.storageProvider.resolveTypeOperations(a,b,c)},resolveContextOperations:function(a,b,c){return this.storageProvider.resolveContextOperations(a,b,c)},_generateServiceOperationQueryable:function(a,b,c,d){typeof console!=="undefined"&&console.log&&console.log("Obsolate: _generateServiceOperationQueryable, $data.EntityContext");for(var e=[],f=0;f0?$data.EntityState.Modified: -$data.EntityState.Unchanged;else{typeof b==="string"&&(b=$data.EntityAttachMode[b]);d=b||$data.EntityAttachMode[$data.EntityAttachMode.defaultMode];if(typeof d==="function")d.call($data.EntityAttachMode,c);else{c.entityState=$data.EntityState.Unchanged;c.changedProperties=void 0}}c.context=this.entityContext;this._trackEntity(c)},detach:function(a){var b=a;a instanceof $data.EntityWrapper?b=a.getEntity():a instanceof this.createNew||(b=new this.createNew(a));for(var c,d=this.entityContext.stateManager.trackedEntities, -e=0;e0?$data.EntityState.Modified:$data.EntityState.Unchanged},Default:function(a){a.entityState=$data.EntityState.Unchanged;a.changedProperties=void 0}}); -$data.Class.define("$data.EntityStateManager",null,null,{constructor:function(a){this.entityContext=null;this.trackedEntities=[];this.init(a)},init:function(a){this.entityContext=a},reset:function(){this.trackedEntities=[]}},null); -$data.Class.define("$data.ItemStoreClass",null,null,{constructor:function(){var a=this;a.itemStoreConfig={aliases:{},contextTypes:{}};a.resetStoreToDefault("local",true);$data.addStore=function(){return a.addItemStoreAlias.apply(a,arguments)};$data.implementation=a.implementation;$data.Entity.addMember("storeToken",{get:function(){if(this.storeConfigs&&this.storeConfigs["default"])return this.storeConfigs.stores[this.storeConfigs["default"]]},set:function(b){a._setTypeStoreConfig(this,"default",b)}}, -true)},itemStoreConfig:{},addItemStoreAlias:function(a,b,c){var d=this,e=new $data.PromiseHandler,f=e.createCallback();if("string"===typeof a){if("object"===typeof b&&"factory"in b){var g=Container.resolveType(b.typeName);d.itemStoreConfig.aliases[a]=b.factory;d.itemStoreConfig.contextTypes[a]=g;c&&(d.itemStoreConfig["default"]=a);f.success();return e.getPromise()}if("function"===typeof b){if((g=b())&&g instanceof $data.EntityContext){f.success(g);g=e.getPromise()}return g.then(function(g){if(typeof g=== -"function")return d.addItemStoreAlias(a,g,c);if(g instanceof $data.EntityContext)return g.onReady().then(function(e){d.itemStoreConfig.aliases[a]=b;d.itemStoreConfig.contextTypes[a]=e.getType();c&&(d.itemStoreConfig["default"]=a);return e});e=new $data.PromiseHandler;f=e.createCallback();f.error(new Exception("factory dont have context instance","Invalid arguments"));return e.getPromise()})}}f.error(new Exception("Name or factory missing","Invalid arguments"));return e.getPromise()},resetStoreToDefault:function(a, -b){this.itemStoreConfig.aliases[a]=this._getDefaultItemStoreFactory;delete this.itemStoreConfig.contextTypes[a];b&&(this.itemStoreConfig["default"]=a)},_setStoreAlias:function(a,b){if("object"===typeof b&&!a.storeToken)a.storeToken=b;return a},_getStoreAlias:function(a,b){var c;if(a instanceof $data.Entity){if(c=b||a.storeToken)return c;c=a.getType()}else c=a;return b||(c.storeConfigs?c.storeConfigs["default"]:void 0)||c.storeToken},_getStoreContext:function(a,b,c){a=this._getContextPromise(a,b); -if(!a||a instanceof $data.EntityContext){b=new $data.PromiseHandler;b.createCallback().success(a);a=b.getPromise()}return a.then(function(a){if(a instanceof $data.EntityContext)return a.onReady();if(c)return null;a=new $data.PromiseHandler;a.createCallback().error(new Exception("factory return type error","Error"));return a.getPromise()})},_getContextPromise:function(a,b){if(a&&"object"===typeof a&&"function"===typeof a.factory)return a.factory(b);if(a&&"object"===typeof a&&"object"===typeof a.args&& -"string"===typeof a.typeName){b=Container.resolveType(a.typeName);return new b(JSON.parse(JSON.stringify(a.args)))}return a&&"string"===typeof a&&b.storeConfigs&&b.storeConfigs.stores[a]&&typeof b.storeConfigs.stores[a].factory==="function"?b.storeConfigs.stores[a].factory():a&&"string"===typeof a&&b.storeConfigs&&b.storeConfigs.stores[a]?this._getDefaultItemStoreFactory(b,b.storeConfigs.stores[a]):a&&"string"===typeof a&&this.itemStoreConfig.aliases[a]?this.itemStoreConfig.aliases[a](b):a&&"function"=== -typeof a?a():this.itemStoreConfig.aliases[this.itemStoreConfig["default"]](b)},_getStoreEntitySet:function(a,b){var c=this._getStoreAlias(b,a),d="function"===typeof b?b:b.getType();return this._getStoreContext(c,d).then(function(a){a=a.getEntitySetFromElementType(d);if(!a){a=new $data.PromiseHandler;a.createCallback().error("EntitySet not exist for "+d.fullName);return a.getPromise()}return a})},_getDefaultItemStoreFactory:function(a,b){if(a){var c="function"===typeof a?a:a.getType(),d=$data.Container.resolveName(c)+ -"_items",d=d.replace(/\./g,"_"),e=$data.typeSystem.extend({collectionName:b&&b.collectionName?b.collectionName:"Items",tableName:d,initParam:{provider:"local",databaseName:d}},b),f={};f[e.collectionName]={type:$data.EntitySet,elementType:c};if(e.tableName)f[e.collectionName].tableName=e.tableName;c=new ($data.EntityContext.extend(d,f))(e.initParam);if(b&&typeof b==="object")b.factory=c._storeToken.factory;return c}},implementation:function(a,b){var c=$data.ItemStore,d;typeof b==="string"?b=c.itemStoreConfig.contextTypes[b]: -b instanceof $data.EntityContext?b=b.getType():typeof b==="function"&&b.isAssignableTo||(b=c.itemStoreConfig.contextTypes[c.itemStoreConfig["default"]]);b&&(d=c._resolveFromContext(b,a));d||(d=Container.resolveType(a));return d},_resolveFromContext:function(a,b){for(var c=a.memberDefinitions.getPublicMappedProperties(),d=0;d1&&b&&"object"===typeof b){for(var d="",e={},f=0;f0&&(d=d+" && ");var g=c[f],d=d+("it."+g.name+" == this."+g.name);e[g.name]=b[g.name]}return{predicate:d,thisArgs:e}}if(c.length=== -1)return{predicate:"it."+c[0].name+" == this.value",thisArgs:{value:b}};throw"invalid keys";},_getKeyObjectFromEntity:function(a,b){var c,d=b.memberDefinitions.getKeyProperties();if(d.length===1)c=a&&typeof a==="object"?a[d[0].name]:a;else{c={};for(var e=0;e0){$data.Trace.log(a+" provider failed to load, trying to fallback to "+b+" provider(s)");d.find(b,c)}else{$data.Trace.log(a+" provider failed to load");c.error()}})}},getUrl:function(a){var b=document.querySelector('script[src$="jaydata.min.js"]'),c=document.querySelector('script[src$="jaydata.js"]');return b? -b.src.substring(0,b.src.lastIndexOf("/")+1)+"jaydataproviders/"+a+"Provider.min.js":c?c.src.substring(0,c.src.lastIndexOf("/")+1)+"jaydataproviders/"+a+"Provider.js":"jaydataproviders/"+a+"Provider.js"},loadScript:function(a,b,c){if(a){var d;if(window.XMLHttpRequest)d=new XMLHttpRequest;else if(window.ActiveXObject!==void 0)d=new ActiveXObject("MsXml2.XmlHttp");else{$data.Trace.log("XMLHttpRequest or MsXml2.XmlHttp ActiveXObject not found");c(false);d=void 0}d.onreadystatechange=function(){$data.Trace.log("HTTP request is in state: "+ -d.readyState);if(d.readyState==4)if(d.status==200||d.status==304){$data.Trace.log("HTTP request succeeded");$data.Trace.log("HTTP request response text: "+d.responseText);eval.call(window,d.responseText);typeof c==="function"?c(true):$data.Trace.log("Callback function is undefined")}else{$data.Trace.log("HTTP request status: ",d.status);typeof c==="function"?c(false):$data.Trace.log("Callback function is undefined")}};d.open("GET",a,true);d.send(null)}else c(false)},loadScriptElement:function(a,b, -c){function d(){$data.Trace.log("Script element watcher iteration "+h);if($data.RegisteredStorageProviders[b]){$data.Trace.log(b+" provider registered");c(true)}else{h--;if(h>0){$data.Trace.log("Script element watcher next iteration");setTimeout(d,g)}else{$data.Trace.log("Script element loader failed");c(false)}}}var e=document.getElementsByTagName("head")[0]||document.documentElement,f=document.createElement("script");f.type="text/javascript";f.src=a;$data.Trace.log("Appending child "+f+" to "+e); -e.appendChild(f);var g=this.scriptLoadInterval||50,h=Math.ceil(this.scriptLoadTimeout/g);$data.Trace.log("Script element watcher iterating "+h+" times");setTimeout(d,g)},loadNpmModule:function(a,b,c){var d=null;try{require(this.npmModules[a]);d=$data.RegisteredStorageProviders[a];$data.Trace.log("NPM module loader successfully registered "+a+" provider")}catch(e){$data.Trace.log("NPM module loader failed for "+a+" provider")}d?c.success(d):b.length>0?this.find(b,c):c.error()},virtualProviders:{type:$data.Array, -value:{local:{fallbacks:["webSql","indexedDb","LocalStore"]}}},getVirtual:function(a){return this.virtualProviders[a]?[].concat(this.virtualProviders[a].fallbacks):a}});$data.StorageProviderLoader=new $data.StorageProviderLoaderBase;$data.storageProviders={DbCreationType:{Merge:10,DropTableIfChanged:20,DropTableIfChange:20,DropAllExistingTables:30,ErrorIfChange:40,DropDbIfChange:50}};$data.ConcurrencyMode={Fixed:"fixed",None:"none"}; -$data.Class.define("$data.StorageProviderBase",null,null,{constructor:function(a){this.providerConfiguration=a||{};this.name=this.getType().name;if($data.RegisteredStorageProviders)for(var a=Object.keys($data.RegisteredStorageProviders),b=0;b0;){f=[].concat(e);e=[];for(g=0;g -0&&d.push(g)}return d},getTraceString:function(){Guard.raise("Pure class")},setContext:function(a){this.context=a},_buildContinuationFunction:function(a,b){if(Array.isArray(b.result)){b.result.next=this._buildPagingMethod(a,b,"next");b.result.prev=this._buildPagingMethod(a,b,"prev")}},_buildPagingMethod:function(a,b,c){return function(d){var e=new $data.PromiseHandler,d=e.createCallback(d),f=(new $data.Expressions.ContinuationExpressionBuilder(c)).compile(b);if(f.expression){f=Container.createQueryable(a, -f.expression);f.defaultType=b.defaultType;a.executeQuery(f,d)}else d.error(new Exception(f.message,"Invalid Operation",f));return e.getPromise()}},buildDbType_modifyInstanceDefinition:function(a,b){var c=function(b,c){var d;if(c){d=JSON.parse(JSON.stringify(a[c]));d.kind=b.kind;d.name=b.name;d.notMapped=false}else d=JSON.parse(JSON.stringify(b));d.dataType=Container.resolveType(b.dataType);d.type=d.dataType;d.key=false;d.computed=false;return d},d=function(a,b,c,d,i){var j={};j[a.name]=c;j[b.name]= -i?i:d+"__"+c;return j};b.Associations&&b.Associations.forEach(function(b){var f=false,g=b.FromType,h=b.ToType,i=b.ToPropertyName,j=b.FromType.getMemberDefinition(b.FromPropertyName),l=[];j&&typeof j.keys==="string"&&j.keys?l=[j.keys]:j&&Array.isArray(j.keys)&&(l=[].concat(j.keys));b.ReferentialConstraint=b.ReferentialConstraint||[];if(b.FromMultiplicity=="*"&&b.ToMultiplicity=="0..1"||b.FromMultiplicity=="0..1"&&b.ToMultiplicity=="1"){g=b.ToType;h=b.FromType;i=b.FromPropertyName;f=true}g.memberDefinitions.getPublicMappedProperties().filter(function(a){return a.key}).forEach(function(j, -q){var n=d(g,h,j.name,i,l[q]);if(f){a[n[h.name]]=c(j,i);var z=h.getMemberDefinition(l[q]);if(z){z.isDependentProperty=true;z.navigationPropertyName=b.FromPropertyName}}b.ReferentialConstraint.push(n)},this)},this);b.ComplexTypes&&b.ComplexTypes.forEach(function(b){b.ReferentialConstraint=b.ReferentialConstraint||[];b.ToType.memberDefinitions.getPublicMappedProperties().forEach(function(f){a[b.FromPropertyName+"__"+f.name]=c(f);b.ReferentialConstraint.push(d(b.ToType,b.FromType,f.name,b.FromPropertyName))}, -this)},this)},buildDbType_generateConvertToFunction:function(a){return function(b){var c=new a.PhysicalType;c.entityState=b.entityState;a.PhysicalType.memberDefinitions.getPublicMappedProperties().forEach(function(a){b[a.name]!==void 0&&(c[a.name]=b[a.name])},this);a.Associations&&a.Associations.forEach(function(a){if(a.FromMultiplicity=="*"&&a.ToMultiplicity=="0..1"||a.FromMultiplicity=="0..1"&&a.ToMultiplicity=="1"){var e=b[a.FromPropertyName];e!==void 0&&a.ReferentialConstraint.forEach(function(b){c[b[a.From]]= -e!==null?e[b[a.To]]:null},this)}},this);a.ComplexTypes&&a.ComplexTypes.forEach(function(a){var e=b[a.FromPropertyName];e!==void 0&&a.ReferentialConstraint.forEach(function(b){c[b[a.From]]=e!==null?e[b[a.To]]:null},this)},this);return c}},bulkInsert:function(a,b,c,d){d.error(new Exception("Not Implemented"))},supportedFieldOperations:{value:{length:{dataType:"number",allowedIn:"filter, map"},substr:{dataType:"string",allowedIn:"filter",parameters:[{name:"startFrom",dataType:"number"},{name:"length", -dataType:"number"}]},toLowerCase:{dataType:"string"}},enumerable:!0,writable:!0},resolveFieldOperation:function(a,b,c){var d=this.supportedFieldOperations[a];if(Array.isArray(d)){for(var e=0;e0&&this.DefaultSelection(b,a.expression.entityType,c)}if(a.expression instanceof $data.Expressions.EntityExpression)this.DefaultSelection(b, -a.expression.entityType);else if(a.expression instanceof $data.Expressions.EntitySetExpression){b.modelBinderConfig.$type=$data.Array;b.modelBinderConfig.$item={};b.selectModelBinderProperty("$item");this.DefaultSelection(b,a.expression.elementType);b.popModelBinderProperty()}},VisitEntityFieldExpression:function(a,b){this.Visit(a.source,b);this.Visit(a.selector,b)},VisitMemberInfoExpression:function(a,b){b.modelBinderConfig.$type=a.memberDefinition.type;if(a.memberDefinition.storageModel&&a.memberName in -a.memberDefinition.storageModel.ComplexTypes)this._addPropertyToModelBinderConfig(Container.resolveType(a.memberDefinition.type),b);else if(!b.modelBinderConfig.$type||!Container.resolveType(b.modelBinderConfig.$type).isAssignableTo||!Container.resolveType(b.modelBinderConfig.$type).isAssignableTo($data.Entity))b.modelBinderConfig.$source=a.memberName},VisitEntitySetExpression:function(a,b){if(a.source instanceof $data.Expressions.EntityExpression){this.Visit(a.source,b);this.Visit(a.selector,b)}}, -VisitComplexTypeExpression:function(a,b){this.Visit(a.source,b);this.Visit(a.selector,b);if("$selector"in b.modelBinderConfig&&b.modelBinderConfig.$selector.length>0)if(b.modelBinderConfig.$selector instanceof $data.Array){var c=b.modelBinderConfig.$selector[1];b.modelBinderConfig.$selector[0]=c+"."+a.selector.memberName+".results";b.modelBinderConfig.$selector[1]=c+"."+a.selector.memberName}else b.modelBinderConfig.$selector=b.modelBinderConfig.$selector+("."+a.selector.memberName);else b.modelBinderConfig.$selector= -this._isoDataProvider?["json:"+a.selector.memberName+".results","json:"+a.selector.memberName]:"json:"+a.selector.memberName},VisitEntityExpression:function(a,b){this.Visit(a.source,b)},VisitAssociationInfoExpression:function(a,b){if("$selector"in b.modelBinderConfig&&b.modelBinderConfig.$selector.length>0)if(b.modelBinderConfig.$selector instanceof $data.Array){var c=b.modelBinderConfig.$selector[1];b.modelBinderConfig.$selector[0]=c+"."+a.associationInfo.FromPropertyName+".results";b.modelBinderConfig.$selector[1]= -c+"."+a.associationInfo.FromPropertyName}else b.modelBinderConfig.$selector=b.modelBinderConfig.$selector+("."+a.associationInfo.FromPropertyName);else b.modelBinderConfig.$selector=this._isoDataProvider?["json:"+a.associationInfo.FromPropertyName+".results","json:"+a.associationInfo.FromPropertyName]:"json:"+a.associationInfo.FromPropertyName;if(this.mapping&&this.mapping.length>0)this.mapping=this.mapping+".";this.mapping=this.mapping+a.associationInfo.FromPropertyName},VisitObjectLiteralExpression:function(a, -b){b.modelBinderConfig.$type=$data.Object;a.members.forEach(function(a){this.Visit(a,b)},this)},VisitObjectFieldExpression:function(a,b){b.selectModelBinderProperty(a.fieldName);a.expression instanceof $data.Expressions.EntityExpression||a.expression instanceof $data.Expressions.EntitySetExpression?this.VisitEntityAsProjection(a,b):this.Visit(a.expression,b);b.popModelBinderProperty()}}); -$data.Class.define("$data.Authentication.AuthenticationBase",null,null,{constructor:function(a){this.configuration=a||{};this.Authenticated=false},Login:function(){Guard.raise("Pure class")},Logout:function(){Guard.raise("Pure class")},CreateRequest:function(){Guard.raise("Pure class")}},null); -$data.Class.define("$data.Authentication.Anonymous",$data.Authentication.AuthenticationBase,null,{constructor:function(a){this.configuration=a||{};this.Authenticated=false},Login:function(){},Logout:function(){},CreateRequest:function(a){$data.ajax(a)}},null); -$data.Class.define("$data.Authentication.FacebookAuth",$data.Authentication.AuthenticationBase,null,{constructor:function(a){this.configuration=$data.typeSystem.extend({Url_code:"",type_code:"",scope:"",Url_token:"",type_token:"",access_token:"",app_id:""},a)},Login:function(a){if(!this.Authenticated){var b=this;b.configuration.stateCallbacks=a||{};$data.ajax({url:this.configuration.Url_code,data:"type="+b.configuration.type_code+"&client_id="+b.configuration.app_id+"&scope="+b.configuration.scope, -type:"POST",dataType:"json",success:function(a){typeof b.configuration.stateCallbacks.pending=="function"&&b.configuration.stateCallbacks.pending(a);b._processRequestToken(a);b.Authenticated=true},error:function(){typeof b.configuration.stateCallbacks.error=="function"&&b.configuration.stateCallbacks.error(arguments)}})}},Logout:function(){this.Authenticated=false},CreateRequest:function(a){if(a){if(a.url.indexOf("access_token=")===-1&&a.url&&this.Authenticated){var b="?";a.url.indexOf(b)>0&&(b="&"); -if(this.configuration.access_token)a.url=a.url+b+"access_token="+this.configuration.access_token}$data.ajax(a)}},_processRequestToken:function(a){var b=this;$data.ajax({url:b.configuration.Url_token,data:"type="+b.configuration.type_token+"&client_id="+b.configuration.app_id+"&code="+a.code,type:"POST",dataType:"json",success:function(a){b.configuration.access_token=a.access_token;typeof b.configuration.stateCallbacks.success=="function"&&b.configuration.stateCallbacks.success(a)},error:function(c){var d= -eval("("+c.responseText+")");d.error&&(d.error.message=="authorization_pending"?setTimeout(function(){b._processRequestToken(a)},2E3):typeof b.configuration.stateCallbacks.abort=="function"&&b.configuration.stateCallbacks.abort(arguments))}})}},null); -$data.Class.define("$data.Authentication.BasicAuth.BasicAuth",$data.Authentication.AuthenticationBase,null,{constructor:function(a){this.configuration=$data.typeSystem.extend({Username:"",Password:""},a)},Login:function(a){a&&typeof a.pending=="function"&&a.pending()},Logout:function(){},CreateRequest:function(a){if(a){var b=this,c=a.beforeSend;a.beforeSend=function(a){a.setRequestHeader("Authorization","Basic "+b.__encodeBase64(b.configuration.Username+":"+b.configuration.Password));typeof c=="function"&& -c(a)};$data.ajax(a)}},__encodeBase64:function(a){input=a;var a="",b,c,d="",e,f,g="",h=0;do{b=input.charCodeAt(h++);c=input.charCodeAt(h++);d=input.charCodeAt(h++);e=b>>2;b=(b&3)<<4|c>>4;f=(c&15)<<2|d>>6;g=d&63;isNaN(c)?f=g=64:isNaN(d)&&(g=64);a=a+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".charAt(e)+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".charAt(b)+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".charAt(f)+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".charAt(g)}while(h< -input.length);return a}},null); -$data.Class.define("$data.MetadataLoaderClass",null,null,{load:function(a,b,c){var d={EntityBaseClass:"$data.Entity",ContextBaseClass:"$data.EntityContext",AutoCreateContext:false,DefaultNamespace:("ns"+Math.random()).replace(".","")+a.replace(/[^\w]/g,"_"),ContextInstanceName:"context",EntitySetBaseClass:"$data.EntitySet",CollectionBaseClass:"Array",url:a,user:void 0,password:void 0,withCredentials:void 0,httpHeaders:void 0,typeFilter:"",navigation:true,generateKeys:true,dependentRelationsOnly:false}; -$data.typeSystem.extend(d,c||{});if(d.DefaultNamespace&&d.DefaultNamespace.lastIndexOf(".")!==d.DefaultNamespace.length-1)d.DefaultNamespace=d.DefaultNamespace+".";this.factoryCache=this.factoryCache||{};b=$data.typeSystem.createCallbackSetting(b);if(a in this.factoryCache)b.success.apply({},this.factoryCache[a]);else{if(d.url){d.SerivceUri=d.url.replace("/$metadata","");d.metadataUri=d.url.indexOf("/$metadata")===-1?d.url.replace(/\/+$/,"")+"/$metadata":d.url}else b.error("metadata url is missing"); -var e=this;e._loadXMLDoc(d,function(a,c){if(c.statusCode<200||c.statusCode>299)b.error(c);else{var h=e._findVersion(a);if(e.xsltRepoUrl){console.log("XSLT: "+e.xsltRepoUrl+e._supportedODataVersionXSLT);e._loadXMLDoc({metadataUri:e.xsltRepoUrl+e._supportedODataVersionXSLT,user:d.user,password:d.password,httpHeaders:d.httpHeaders},function(c,g){if(g.statusCode<200||g.statusCode>299)b.error(g);else{var l=g.responseText,l=l.replace('xmlns:edm="@@VERSIONNS@@"','xmlns:edm="'+h.ns+'"'),l=l.replace("@@VERSION@@", -h.version);if(window.ActiveXObject===void 0)c=(new DOMParser).parseFromString(l,"text/xml");else{c=new ActiveXObject("Microsoft.XMLDOM");c.async=false;c.loadXML(l)}e._transform(b,h,a,c,d)}})}else e._transform(b,h,a,void 0,d)}})}},debugMode:{type:"bool",value:!1},xsltRepoUrl:{type:"string",value:""},createFactoryFunc:function(a,b,c){return function(d){if(a){d=$data.typeSystem.extend({name:"oData",oDataServiceHost:b.SerivceUri,user:b.user,password:b.password,withCredentials:b.withCredentials,maxDataServiceVersion:c.maxVersion|| -"3.0"},d);return new a(d)}return null}},_transform:function(a,b,c,d,e){c=this._processResults(e.url,b,c,d,e);try{eval(c)}catch(f){a.error(new Exception("SyntaxError","Unexpected model",[f,c]));return}var g;if(!$data.generatedContexts||!(g=$data.generatedContexts.pop()))a.error(new Exception("No context found in service","Not found",[e,c]));else{b=this.createFactoryFunc(g,e,b);this.factoryCache[e.url]=[b,g];b.type=g;if(this.debugMode){b.codeText=c;a.success(b,g,c)}else a.success(b,g)}},_loadXMLDoc:function(a, -b){var c=this;if($data.postMessageODataHandler){if(a.user&&a.password&&(!a.httpHeaders||a.httpHeaders&&!a.httpHeaders.Authorization)){httpHeader=httpHeader||{};httpHeader.Authorization="Basic "+this.__encodeBase64(a.user+":"+a.password)}$data.postMessageODataHandler.requestProxy({url:a.metadataUri,httpHeaders:a.httpHeaders,success:function(a){var c;if(typeof module!=="undefined"&&typeof require!=="undefined")c=a.responseText;else if(window.ActiveXObject){c=new ActiveXObject("Microsoft.XMLDOM");c.async= -"false";c.loadXML(a.responseText)}else c=(new DOMParser).parseFromString(a.responseText,"text/xml");b(c,a)},error:function(){c._loadXHTTP_XMLDoc(a,b)}})}else this._loadXHTTP_XMLDoc(a,b)},_loadXHTTP_XMLDoc:function(a,b){var c=new XMLHttpRequest;c.open("GET",a.metadataUri,true);a.httpHeaders&&Object.keys(a.httpHeaders).forEach(function(b){c.setRequestHeader(b,a.httpHeaders[b])});c.onreadystatechange=function(){c.readyState===4&&b(c.responseXML||c.responseText,{requestUri:a.metadataUri,statusCode:c.status, -statusText:c.statusText,responseText:c.responseText})};a.user&&a.password&&(!a.httpHeaders||a.httpHeaders&&!a.httpHeaders.Authorization)&&c.setRequestHeader("Authorization","Basic "+this.__encodeBase64(a.user+":"+a.password));c.send("")},_processResults:function(a,b,c,d,e){var f=this.getCurrentXSLTVersion(b,c);e.typeFilter=this._prepareTypeFilter(c,b,e);if(window.ActiveXObject!==void 0){var a=new ActiveXObject("Msxml2.XSLTemplate.6.0"),g=new ActiveXObject("Msxml2.FreeThreadedDOMDocument.6.0");g.async= -false;d?g.load(d):g.loadXML(f);if(g.parseError.errorCode==0){a.stylesheet=g;d=new ActiveXObject("Msxml2.DOMDocument.6.0");d.async=false;d.load(c);if(d.parseError.errorCode==0){c=a.createProcessor();c.input=d;c.addParameter("SerivceUri",e.SerivceUri);c.addParameter("EntityBaseClass",e.EntityBaseClass);c.addParameter("ContextBaseClass",e.ContextBaseClass);c.addParameter("AutoCreateContext",e.AutoCreateContext);c.addParameter("ContextInstanceName",e.ContextInstanceName);c.addParameter("EntitySetBaseClass", -e.EntitySetBaseClass);c.addParameter("CollectionBaseClass",e.CollectionBaseClass);c.addParameter("DefaultNamespace",e.DefaultNamespace);c.addParameter("MaxDataserviceVersion",b.maxVersion||"3.0");c.addParameter("AllowedTypesList",e.typeFilter);c.addParameter("GenerateNavigationProperties",e.navigation);c.transform();return c.output}}return""}if(typeof document!=="undefined"&&document.implementation&&document.implementation.createDocument){d=d?d:(new DOMParser).parseFromString(f,"text/xml");a=new XSLTProcessor; -a.importStylesheet(d);a.setParameter(null,"SerivceUri",e.SerivceUri);a.setParameter(null,"EntityBaseClass",e.EntityBaseClass);a.setParameter(null,"ContextBaseClass",e.ContextBaseClass);a.setParameter(null,"AutoCreateContext",e.AutoCreateContext);a.setParameter(null,"ContextInstanceName",e.ContextInstanceName);a.setParameter(null,"EntitySetBaseClass",e.EntitySetBaseClass);a.setParameter(null,"CollectionBaseClass",e.CollectionBaseClass);a.setParameter(null,"DefaultNamespace",e.DefaultNamespace);a.setParameter(null, -"MaxDataserviceVersion",b.maxVersion||"3.0");a.setParameter(null,"AllowedTypesList",e.typeFilter);a.setParameter(null,"GenerateNavigationProperties",e.navigation);resultDocument=a.transformToFragment(c,document);return resultDocument.textContent}if(typeof module!=="undefined"&&typeof require!=="undefined"){a=require("node_xslt");return a.transform(a.readXsltString(f),a.readXmlString(c),["SerivceUri","'"+e.SerivceUri+"'","EntityBaseClass","'"+e.EntityBaseClass+"'","ContextBaseClass","'"+e.ContextBaseClass+ -"'","AutoCreateContext","'"+e.AutoCreateContext+"'","ContextInstanceName","'"+e.ContextInstanceName+"'","EntitySetBaseClass","'"+e.EntitySetBaseClass+"'","CollectionBaseClass","'"+e.CollectionBaseClass+"'","DefaultNamespace","'"+e.DefaultNamespace+"'","MaxDataserviceVersion","'"+(b.maxVersion||"3.0")+"'","AllowedTypesList","'"+e.typeFilter+"'","GenerateNavigationProperties","'"+e.navigation+"'"])}},_prepareTypeFilter:function(a,b,c){b="";if(!(typeof a==="object"&&"querySelector"in a&&"querySelectorAll"in -a))return b;var d=[];if(typeof c.typeFilter==="object"&&c.typeFilter){for(var e=Object.keys(c.typeFilter),f=0;f0){i=h.Name.substring(0,h.Name.lastIndexOf("."));g=h.Name.substring(h.Name.lastIndexOf(".")+1)}for(var i=a.querySelectorAll("EntityContainer[Name = '"+ -i+"']"),j=0;j0?b+(a.Name+":"+a.Fields.join(",")+";"):b+(a.Name+";")}}return b},_discoverTypeDependencies:function(a,b,c,d){for(var e=[],f=[],g=[],h=0;h=0&&g.splice(i,1);this._discoverType(a[h],b,e,f,c,d,true,g)}for(h=0;h=0)){console.log("Discover: "+i);var j=i,l="";if(i.lastIndexOf(".")> -0){l=i.substring(0,i.lastIndexOf("."));j=i.substring(i.lastIndexOf(".")+1)}l=b.querySelector("Schema[Namespace = '"+l+"']");if(l!=null){var w=l.querySelector("EntityType[Name = '"+j+"'], ComplexType[Name = '"+j+"']");if(w!=null){c.push(a);d.push(i);if(f&&a.Fields.length>0){var q=w.querySelectorAll("Key PropertyRef");if(q!=null)for(j=0;j=0){q=n.attributes.ToRole.value;n=l.querySelector("Association End[Role = '"+n.attributes.FromRole.value+"']:not([Type = '"+i+"'])");n==null&&(n=l.querySelector("Association End[Role = '"+q+"']:not([Type = '"+i+"'])"));if(n!=null){q=n.attributes.Type.value;g?h.indexOf(q)<0&&d.indexOf(q)<0&&h.push(q):this._discoverType({Name:q,Fields:[]},b,c,d,e,f,false,h)}}}}}}}},_discoverProperyDependencies:function(a,b,c,d){for(var e=[],f=a.map(function(a){return a.Name}), -g=0;g0){j=g.substring(0,g.lastIndexOf("."));i=g.substring(g.lastIndexOf(".")+1)}b=b.querySelector("Schema[Namespace = '"+j+"']");if(b!=null){i=b.querySelector("EntityType[Name = '"+i+"'], ComplexType[Name = '"+i+"']");if(i!=null){c.push(a);if(h){if(f){d=i.querySelectorAll("Key PropertyRef");if(d!= -null)for(c=0;c=0&&a.Fields.push(f)}}}}}},_findVersion:function(a){var b="";if(typeof a==="object"&&"getElementsByTagName"in a){var c="http://schemas.microsoft.com/ado/2008/09/edm",d=a.getElementsByTagName("Schema");d&&(d=d[0]);if(d)d=d.attributes;d&&(d=d.getNamedItem("xmlns"));if(d)c=d.value;(b=a.getElementsByTagName("edmx:DataServices")[0]||a.getElementsByTagName("DataServices")[0])&&(b=b.attributes.getNamedItem("m:MaxDataServiceVersion"));if(b&&c)b=b.value;return{ns:c,version:this._supportedODataVersions[c]|| -"unknown",maxVersion:b||this._maxDataServiceVersions[c||"unknown"]}}if(typeof module!=="undefined"&&typeof require!=="undefined"){b="http://schemas.microsoft.com/ado/2008/09/edm";c="nodejs";for(d in this._supportedODataVersions)if(a.search(RegExp('=0){b=d;c=this._supportedODataVersions[d];break}return{ns:b,version:c,maxVersion:this._maxDataServiceVersions[c||"unknown"]}}},_supportedODataVersions:{value:{"http://schemas.microsoft.com/ado/2006/04/edm":"V1","http://schemas.microsoft.com/ado/2008/09/edm":"V2", -"http://schemas.microsoft.com/ado/2009/11/edm":"V3","http://schemas.microsoft.com/ado/2007/05/edm":"V11","http://schemas.microsoft.com/ado/2009/08/edm":"V22"}},_maxDataServiceVersions:{value:{"http://schemas.microsoft.com/ado/2006/04/edm":"2.0","http://schemas.microsoft.com/ado/2008/09/edm":"2.0","http://schemas.microsoft.com/ado/2009/11/edm":"3.0","http://schemas.microsoft.com/ado/2007/05/edm":"2.0","http://schemas.microsoft.com/ado/2009/08/edm":"2.0"}},_supportedODataVersionXSLT:{value:"JayDataContextGenerator.xslt"}, -getCurrentXSLTVersion:function(a){return this._metadataConverterXSLT.replace("@@VERSIONNS@@",a.ns).replace("@@VERSION@@",a.version)},__encodeBase64:function(a){var b="",c,d,e="",f,g,h="",i=0;do{c=a.charCodeAt(i++);d=a.charCodeAt(i++);e=a.charCodeAt(i++);f=c>>2;c=(c&3)<<4|d>>4;g=(d&15)<<2|e>>6;h=e&63;isNaN(d)?g=h=64:isNaN(e)&&(h=64);b=b+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".charAt(f)+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".charAt(c)+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".charAt(g)+ -"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".charAt(h)}while(i\r\n\r\n \r\n \r\n\r\n \r\n \r\n \r\n\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n\r\n Microsoft.Crm.Sdk.Data.Services.Product;Microsoft.Crm.Sdk.Data.Services.LeadAddress:Telephone1,City,UTCOffset;\r\n\r\n \r\n \r\n \r\n \r\n <\!--\r\n create field: @@\r\n --\>\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n\r\n \r\n \r\n <\!--\r\n create type: \r\n --\>\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n <\!--\r\n createTypeList: \r\n --\>\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n <\!--\r\n rem: @@ \r\n --\>\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n\r\n \r\n \r\n \r\n \r\n \r\n \r\n\r\n<\!-- TODO EXSLT node-set --\>\r\n <\!----\>\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n\r\n/*//////////////////////////////////////////////////////////////////////////////////////\r\n////// Autogenerated by JaySvcUtil.exe http://JayData.org for more info /////////\r\n////// oData @@VERSION@@ /////////\r\n//////////////////////////////////////////////////////////////////////////////////////*/\r\n(function(global, $data, undefined) {\r\n\r\n \r\n\r\n \r\n <\!-- TODO EXSLT node-set--\>\r\n <\!----\>\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n <\!----\>\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n\r\n Info: generating type \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n\r\n\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n .extend(\'.\', {\r\n \r\n \r\n \r\n \r\n \r\n , \r\n \r\n \r\n \r\n \r\n \r\n \r\n , \r\n \r\n \r\n \r\n \r\n ,\r\n \r\n ,\r\n \r\n \r\n });\r\n\r\n\r\n\r\n\r\n\r\n .extend(\'\', {\r\n <\!--or (@IsBindable = \'true\' and (@IsAlwaysBindable = \'false\' or @m:IsAlwaysBindable = \'false\' or @metadata:IsAlwaysBindable = \'false\'))--\>\r\n\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n\r\n \r\n \r\n \r\n \r\n \r\n ,\r\n \r\n \r\n \r\n \r\n \r\n \r\n ,\r\n \r\n \r\n \r\n \r\n });\r\n\r\n $data.generatedContexts = $data.generatedContexts || [];\r\n $data.generatedContexts.push();\r\n \r\n /*Context Instance*/\r\n = new ({ name:\'oData\', oDataServiceHost: \'\', maxDataServiceVersion: \'\' });\r\n\r\n\r\n\r\n \r\n})(window, $data);\r\n \r\n \r\n\r\n \r\n\r\n \r\n \'\r\n \r\n \': { type: \r\n \r\n \r\n $data.ServiceAction\r\n \r\n \r\n $data.ServiceOperation\r\n \r\n \r\n\r\n \r\n\r\n \r\n \r\n \r\n , params: [\r\n \r\n { name: \'\r\n \r\n \', type: \'\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \' }\r\n , \r\n \r\n ]\r\n\r\n }\r\n \r\n \r\n \r\n , returnType: \r\n \r\n null\r\n $data.Queryable\r\n \r\n \'\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \'\r\n \r\n \r\n\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n , elementType: \'\r\n \r\n \'\r\n \r\n \r\n \r\n \r\n , method: \'\r\n \r\n \'\r\n \r\n \r\n , \r\n \r\n \r\n \r\n : \r\n \r\n \r\n \r\n , \'\r\n \r\n \r\n \r\n \': \'\r\n \r\n \'\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n\r\n \'\': { type: , elementType: }\r\n\r\n \r\n \r\n \r\n \r\n , actions: { \r\n \r\n \r\n ,\r\n \r\n \r\n \r\n }\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n true\r\n \r\n \'\': { \'$\':\r\n , \r\n \r\n \'$\':\r\n , \r\n \r\n }\r\n\r\n \r\n \r\n \r\n\r\n \r\n \r\n \r\n \'Array\'\r\n \r\n \r\n \r\n \r\n \'\'\r\n \r\n \r\n \'\'\r\n \r\n \r\n \r\n \r\n \'\'\r\n \r\n \r\n \'\'\r\n \r\n \r\n \r\n\r\n \r\n $data.ConcurrencyMode.\r\n \r\n\r\n \r\n \r\n \r\n \r\n \r\n \r\n true\r\n \r\n \r\n \r\n\r\n \r\n true \r\n \r\n\r\n \r\n \r\n \r\n Number.POSITIVE_INFINITY\r\n \r\n \r\n \r\n \r\n \r\n \r\n\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n true\'\'\r\n \r\n\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \'\'\r\n \'\'\r\n \r\n \'$$unbound\'\r\n \r\n \'\'\r\n \r\n \r\n \'\'\r\n \r\n \r\n \'\'\r\n \r\n \r\n \'$$unbound\'\r\n Warning: inverseProperty other side missing: \r\n \r\n \r\n \r\n \r\n \r\n \'\'\r\n true\r\n \r\n \r\n \'\'\r\n \r\n \r\n \'$$unbound\'\r\n \r\n Warning: inverseProperty other side missing: \r\n \r\n \r\n \r\n \r\n \r\n \r\n\r\n\r\n \r\n\r\n \r\n <\!--!!!!--\>\r\n Warning: .: is an unknown/unprocessed attribued\r\n \r\n <\!--\r\n !!\r\n --\>\r\n\r\n'}}); -$data.MetadataLoader=new $data.MetadataLoaderClass; -$data.service=function(a,b,c){function d(a){switch(typeof a){case "object":typeof a.success==="function"||typeof a.error==="function"?f=a:e=a;break;case "function":f=a}}var e,f;d(b);d(c);if(typeof a==="object"){e=$data.typeSystem.extend(a,e);a=a.url;delete e.url}b=new $data.PromiseHandler;f=b.createCallback(f);$data.MetadataLoader.load(a,{success:function(a){var b=a.type;if(e){var c=e.serviceName||e.storeAlias;c&&"addStore"in $data&&$data.addStore(c,a,e.isDefault===void 0||e.isDefault)}f.success(a, -b)},error:f.error},e);return b.getPromise()}; -(function(a){if(typeof jQuery!=="undefined"){a.Class.define("$data.Deferred",a.PromiseHandlerBase,null,{constructor:function(){this.deferred=new $.Deferred},deferred:{},createCallback:function(b){var b=a.typeSystem.createCallbackSetting(b),c=this;return cbWrapper={success:function(){b.success.apply(c.deferred,arguments);c.deferred.resolve.apply(c.deferred,arguments)},error:function(){Array.prototype.push.call(arguments,c.deferred);b.error.apply(c.deferred,arguments)},notify:function(){b.notify.apply(c.deferred, -arguments);c.deferred.notify.apply(c.deferred,arguments)}}},getPromise:function(){return this.deferred.promise()}},null);a.PromiseHandler=a.Deferred}})($data); -(function(a){a.initService=function(b,c){var d=new a.PromiseHandler,e;if(typeof b==="object"){e=b;var f=e.isSSL||e.isSSL===void 0?"https":"http",g=e.port?":"+e.port:"",b=typeof e.license==="string"&&e.license.toLowerCase()==="business"?e.appId&&e.serviceName?f+"://"+e.appId+".jaystack.net"+g+"/"+e.serviceName:e.url:e.ownerId&&e.appId&&e.serviceName?f+"://open.jaystack.net/"+e.ownerId+"/"+e.appId+"/api/"+e.serviceName:e.url;delete e.url;e=a.typeSystem.extend(e,c)}else e=c;a.service(b,e).then(function(a){return a().onReady().then(function(b){b.serviceFactory= -a;d.deferred.resolve(b,a,a.type)}).fail(function(){d.deferred.reject.apply(d.deferred,arguments)})}).fail(function(){d.deferred.reject.apply(d.deferred,arguments)});return d.getPromise()}})($data); diff --git a/release/jaydatamodules/angular.js b/release/jaydatamodules/angular.js deleted file mode 100644 index d245542a..00000000 --- a/release/jaydatamodules/angular.js +++ /dev/null @@ -1,249 +0,0 @@ -// JayData 1.3.6 -// Dual licensed under MIT and GPL v2 -// Copyright JayStack Technologies (http://jaydata.org/licensing) -// -// JayData is a standards-based, cross-platform Javascript library and a set of -// practices to access and manipulate data from various online and offline sources. -// -// Credits: -// Hajnalka Battancs, Dániel József, János Roden, László Horváth, Péter Nochta -// Péter Zentai, Róbert Bónay, Szabolcs Czinege, Viktor Borza, Viktor Lázár, -// Zoltán Gyebrovszki, Gábor Dolla -// -// More info: http://jaydata.org -(function() { - -Object.defineProperty($data.Entity.prototype, "_isNew", { - get: function () { - return !this.storeToken; - } -}); -Object.defineProperty($data.Entity.prototype, "_isDirty", { - get: function () { - return !this._isNew && this.changedProperties && this.changedProperties.length > 0; - } -}); - -var originalSave = $data.Entity.prototype.save; -var originalRemove = $data.Entity.prototype.remove; -var originalSaveChanges = $data.EntityContext.prototype.saveChanges; - -var _getCacheKey = function (query) { - var key = query.expression.getJSON(); - var hash = 0, i, charC; - if (key.length == 0) return hash; - for (i = 0; i < key.length; i++) { - charC = key.charCodeAt(i); - hash = ((hash << 5) - hash) + charC; - hash = hash & hash; - } - return hash; -} - -angular.module('jaydata', ['ng', ['$provide', function ($provide) { - - $provide.factory('$data', ['$rootScope', '$q', function ($rootScope, $q) { - var cache = {}; - - $data.Entity.prototype.hasOwnProperty = function (propName) { - var member; - if (this.getType && this.getType().memberDefinitions) { - if (member = this.getType().memberDefinitions['$' + propName]) { - return ("property" === member.kind) && member.enumerable; - } else { - return false; - } - } - return Object.prototype.hasOwnProperty.apply(this, arguments); - } - - $data.Queryable.prototype.toLiveArray = function (cb) { - var _this = this; - - var trace = this.toTraceString(); - var cacheKey = _getCacheKey(this); // trace.queryText || trace.sqlText + JSON.stringify(trace.params); - - if (cache[cacheKey]) { - return cache[cacheKey]; - } - - var result = []; - cache[cacheKey] = result; - - result.state = "inprogress"; - result.successHandlers = []; - result.errorHandlers = []; - - - if (cb && typeof cb === 'function') { - chainOrFire(cb, "success"); - } - - function chainOrFire(cb, type) { - if (!cb) return; - var targetCbArr = type === "success" ? result.successHandlers : result.errorHandlers; - if (result.state === "completed") { - cb(result); - } else { - targetCbArr.push(cb); - } - return result; - } - - result.then = result.success = function (cb) { - return chainOrFire(cb, "success"); - }; - - result.error = function (cb) { - return result; - }; - - result.refresh = function (cb) { - //result = []; - result.length = 0; - result.state = "inprogress"; - chainOrFire(cb, "success"); - _this.toArray({ success: result.resolve, error: result.reject }); - return result; - } - - result.resolve = function (items) { - result.state = "completed"; - items.forEach(function (item) { - result.push(item); - }); - result.successHandlers.forEach(function (handler) { - handler(result); - }); - if (!$rootScope.$$phase) $rootScope.$apply(); - } - - result.reject = function (err) { - result.state = "failed"; - result.errorHandlers.forEach(function (handler) { - handler(err); - }); - if (!$rootScope.$$phase) $rootScope.$apply(); - } - - this.toArray({ success: result.resolve, error: result.reject }); - - return result; - }; - - $data.Entity.prototype.save = function () { - var _this = this; - var d = $q.defer(); - originalSave.call(_this).then(function () { - cache = {}; - d.resolve(_this); - if (!$rootScope.$$phase) $rootScope.$apply(); - }).fail(function (err) { - d.reject(err); - if (!$rootScope.$$phase) $rootScope.$apply(); - }); - return d.promise; - }; - - $data.ItemStoreClass.prototype.EntityInstanceSave = function (storeAlias, hint) { - var self = $data.ItemStore; - var entity = this; - return self._getStoreEntitySet(storeAlias, entity) - .then(function (entitySet) { - return self._getSaveMode(entity, entitySet, hint, storeAlias) - .then(function (mode) { - mode = mode || 'add'; - switch (mode) { - case 'add': - entitySet.add(entity); - break; - case 'attach': - entitySet.attach(entity, true); - entity.entityState = $data.EntityState.Modified; - break; - default: - var d = new $data.PromiseHandler(); - var callback = d.createCallback(); - callback.error('save mode not supported: ' + mode); - return d.getPromise(); - } - - return originalSaveChanges.call(entitySet.entityContext) - .then(function () { self._setStoreAlias(entity, entitySet.entityContext.storeToken); return entity; }); - }); - }); - }; - $data.ItemStoreClass.prototype.EntityInstanceRemove = function (storeAlias) { - var self = $data.ItemStore; - var entity = this; - return self._getStoreEntitySet(storeAlias, entity) - .then(function (entitySet) { - entitySet.remove(entity); - - return originalSaveChanges.call(entitySet.entityContext) - .then(function () { return entity; }); - }); - }; - $data.ItemStoreClass.prototype.EntityTypeRemoveAll = function (type) { - return function (storeAlias) { - var self = $data.ItemStore; - return self._getStoreEntitySet(storeAlias, type) - .then(function (entitySet) { - return entitySet.toArray().then(function (items) { - items.forEach(function (item) { - entitySet.remove(item); - }); - - return originalSaveChanges.call(entitySet.entityContext) - .then(function () { return items; }); - }); - }); - } - }; - $data.ItemStoreClass.prototype.EntityTypeAddMany = function (type) { - return function (initDatas, storeAlias) { - var self = $data.ItemStore; - return self._getStoreEntitySet(storeAlias, type) - .then(function (entitySet) { - var items = entitySet.addMany(initDatas); - return originalSaveChanges.call(entitySet.entityContext) - .then(function () { - return items; - }); - }); - } - }; - - $data.Entity.prototype.remove = function () { - var d = $q.defer(); - var _this = this; - originalRemove.call(_this).then(function () { - cache = {}; - d.resolve(_this); - if (!$rootScope.$$phase) $rootScope.$apply(); - }).fail(function (err) { - d.reject(err); - if (!$rootScope.$$phase) $rootScope.$apply(); - }); - - return d.promise; - } - - $data.EntityContext.prototype.saveChanges = function () { - var _this = this; - var d = $q.defer(); - originalSaveChanges.call(_this).then(function (n) { - cache = {}; - d.resolve(n); - if (!$rootScope.$$phase) $rootScope.$apply(); - }).fail(function (err) { - d.reject(err); - if (!$rootScope.$$phase) $rootScope.$apply(); - }); - return d.promise; - } - return $data; - }]); -}]]); - -})(); diff --git a/release/jaydatamodules/angular.min.js b/release/jaydatamodules/angular.min.js deleted file mode 100644 index 387c0b5a..00000000 --- a/release/jaydatamodules/angular.min.js +++ /dev/null @@ -1,21 +0,0 @@ -// JayData 1.3.6 -// Dual licensed under MIT and GPL v2 -// Copyright JayStack Technologies (http://jaydata.org/licensing) -// -// JayData is a standards-based, cross-platform Javascript library and a set of -// practices to access and manipulate data from various online and offline sources. -// -// Credits: -// Hajnalka Battancs, Dániel József, János Roden, László Horváth, Péter Nochta -// Péter Zentai, Róbert Bónay, Szabolcs Czinege, Viktor Borza, Viktor Lázár, -// Zoltán Gyebrovszki, Gábor Dolla -// -// More info: http://jaydata.org -(function(){Object.defineProperty($data.Entity.prototype,"_isNew",{get:function(){return!this.storeToken}});Object.defineProperty($data.Entity.prototype,"_isDirty",{get:function(){return!this._isNew&&this.changedProperties&&0Returns an object of name-value pairs that represents values in a form. - * It is able to nest values whose element's name has square brackets.

- * Example html: - * @codestart html - * <form> - * <input name="foo[bar]" value='2'/> - * <input name="foo[ced]" value='4'/> - * <form/> - * @codeend - * Example code: - * @codestart - * $('form').formBinder() //-> { foo:{bar:2, ced: 4} } - * @codeend - * - * @demo jquery/dom/form_params/form_params.html - * - * @param {Boolean} [convert] True if strings that look like numbers and booleans should be converted. Defaults to true. - * @return {Object} An object of name-value pairs. - */ - formBinder: function (obj, convert) { - if (this[0].nodeName.toLowerCase() == 'form' && this[0].elements) { - - return jQuery(jQuery.makeArray(this[0].elements)).getParams(obj, convert); - } - return jQuery("input[name], textarea[name], select[name]", this[0]).getParams(obj, convert); - }, - getParams: function (obj, convert) { - var data = obj || {}, - current; - - convert = convert === undefined ? true : convert; - - this.each(function () { - var el = this, - type = el.type && el.type.toLowerCase(); - //if we are submit, ignore - if ((type == 'submit') || !el.name) { - return; - } - - var key = el.name, - value = $.data(el, "value") || $.fn.val.call([el]), - isRadioCheck = radioCheck.test(el.type), - parts = key.match(keyBreaker), - write = !isRadioCheck || !!el.checked, - //make an array of values - lastPart; - - if (convert) { - if (isNumber(value)) { - value = parseFloat(value); - } else if (value === 'true' || value === 'false') { - value = Boolean(value); - } - - } - - // go through and create nested objects - current = data; - for (var i = 0; i < parts.length - 1; i++) { - if (!current[parts[i]]) { - current[parts[i]] = {}; - } - current = current[parts[i]]; - } - lastPart = parts[parts.length - 1]; - - //now we are on the last part, set the value - if (lastPart in current && type === "checkbox") { - if (!$.isArray(current[lastPart])) { - current[lastPart] = current[lastPart] === undefined ? [] : [current[lastPart]]; - } - if (write) { - current[lastPart].push(value); - } - } else if (write || !current[lastPart]) { - current[lastPart] = write ? value : undefined; - } - - }); - return data; - } - }); -})(jQuery); \ No newline at end of file diff --git a/release/jaydatamodules/formBinder.min.js b/release/jaydatamodules/formBinder.min.js deleted file mode 100644 index 147004ec..00000000 --- a/release/jaydatamodules/formBinder.min.js +++ /dev/null @@ -1,15 +0,0 @@ -// JayData 1.3.6 -// Dual licensed under MIT and GPL v2 -// Copyright JayStack Technologies (http://jaydata.org/licensing) -// -// JayData is a standards-based, cross-platform Javascript library and a set of -// practices to access and manipulate data from various online and offline sources. -// -// Credits: -// Hajnalka Battancs, Dániel József, János Roden, László Horváth, Péter Nochta -// Péter Zentai, Róbert Bónay, Szabolcs Czinege, Viktor Borza, Viktor Lázár, -// Zoltán Gyebrovszki, Gábor Dolla -// -// More info: http://jaydata.org -(function(d){var j=/radio|checkbox/i,k=/[^\[\]]+/g,l=/^[\-+]?[0-9]*\.?[0-9]+([eE][\-+]?[0-9]+)?$/;d.fn.extend({formBinder:function(d,e){return"form"==this[0].nodeName.toLowerCase()&&this[0].elements?jQuery(jQuery.makeArray(this[0].elements)).getParams(d,e):jQuery("input[name], textarea[name], select[name]",this[0]).getParams(d,e)},getParams:function(m,e){var h=m||{},b,e=void 0===e?!0:e;this.each(function(){var i=this.type&&this.type.toLowerCase();if("submit"!=i&&this.name){var a=this.name,c=d.data(this, -"value")||d.fn.val.call([this]),f=j.test(this.type),a=a.match(k),f=!f||!!this.checked;if(e)if("number"==typeof c||("string"!=typeof c?0:c.match(l)))c=parseFloat(c);else if("true"===c||"false"===c)c=Boolean(c);b=h;for(var g=0;ge.references.indexOf(b)&&e.references.push(b)}return{cacheKey:c,clientId:b}}var l=d.Entity.inheritedTypeProcessor,j={templateResolvers:[function(a,b){if(b)return(b=b.trim())?"<"===b[0]||"{"===b[0]?b:void 0:void 0},function(){},function(a,b){if(!b)var c=d.Container.resolveName(a).split("."),b= -c[c.length-1];return $("#"+b).html()}],templateCompiler:function(a){return h.compile(a)},templateCache:{},getTemplate:function(a,b){var c,e,d=a.fullName+"::"+b;e=c=this.templateCache[d];for(i=0;!c&&iarguments.length&&(a=void 0);return new h.SafeString(d.render(this,a))});d.displayCache={};var n=0,m=0;h.registerHelper("entityScope",function(){var a=d.Container.resolveName(this.getType()).split("."), -a=a[a.length-1],b=this[this.getType().memberDefinitions.getKeyProperties()[0].name],a="data-"+a.toLowerCase()+"-"+b,b=k(this),a=a+(" data-cache-client="+b.clientId);return a+=" data-cache-item="+b.cacheKey});h.registerHelper("entityCommand",function(a){d.entityCache=d.entityCache||{};var b=d.Container.resolveName(this.getType()).split("."),b=b[b.length-1],c=this[this.getType().memberDefinitions.getKeyProperties()[0].name],e=b+":"+c;d.entityCache[e]||(d.entityCache[e]=this);a="data-command="+a+" data-type="+ -b+" data-id="+c;b=k(this);a+=" data-cache-client="+b.clientId;return a+=" data-cache-item="+b.cacheKey});d.setCommandHandler=function(a,b){$(b||document).delegate("[data-command]","click",function(){$(this).data("type");$(this).data("id");var b=a[$(this).data("command")+$(this).data("type")],e=$(this).data("cache-item"),e=[d.displayCache[e].value,$(this).data("id")];b.apply(a,e)})};$(document).delegate(".single-select","click",function(a){a=$(a.srcElement).parentsUntil(this);$(this).children().removeClass("active"); -a.addClass("active")})})($data,Handlebars); diff --git a/release/jaydatamodules/inMemory.js b/release/jaydatamodules/inMemory.js deleted file mode 100644 index 641a8879..00000000 --- a/release/jaydatamodules/inMemory.js +++ /dev/null @@ -1,45 +0,0 @@ -// JayData 1.3.6 -// Dual licensed under MIT and GPL v2 -// Copyright JayStack Technologies (http://jaydata.org/licensing) -// -// JayData is a standards-based, cross-platform Javascript library and a set of -// practices to access and manipulate data from various online and offline sources. -// -// Credits: -// Hajnalka Battancs, Dániel József, János Roden, László Horváth, Péter Nochta -// Péter Zentai, Róbert Bónay, Szabolcs Czinege, Viktor Borza, Viktor Lázár, -// Zoltán Gyebrovszki, Gábor Dolla -// -// More info: http://jaydata.org -(function ($data) { - - $data.Array.prototype.toQueryable = function () { - if (this.length > 0) { - var firtsItem = this[0]; - var type = Container.resolveType(Container.getTypeName(firtsItem)); - - if (!type.isAssignableTo || !type.isAssignableTo($data.Entity)) - Guard.raise(new Exception("Type '" + Container.resolveName(type) + "' is not subclass of $data.Entity", "Not supported", type)); - - for (var i = 0; i < this.length; i++) { - Guard.requireType('array item check', this[i], type); - } - - } - - var typeName = 'inMemoryArray_' + type.name; - if (!Container.isTypeRegistered(typeName)) { - $data.EntityContext.extend(typeName, { - Source: { - type: $data.EntitySet, - elementType: type - } - }); - } - - var context = Container['create' + typeName]({ name: 'InMemory', source: { Source: this} }); - - return context.Source; - } - -})($data); \ No newline at end of file diff --git a/release/jaydatamodules/inMemory.min.js b/release/jaydatamodules/inMemory.min.js deleted file mode 100644 index 4607c585..00000000 --- a/release/jaydatamodules/inMemory.min.js +++ /dev/null @@ -1,15 +0,0 @@ -// JayData 1.3.6 -// Dual licensed under MIT and GPL v2 -// Copyright JayStack Technologies (http://jaydata.org/licensing) -// -// JayData is a standards-based, cross-platform Javascript library and a set of -// practices to access and manipulate data from various online and offline sources. -// -// Credits: -// Hajnalka Battancs, Dániel József, János Roden, László Horváth, Péter Nochta -// Péter Zentai, Róbert Bónay, Szabolcs Czinege, Viktor Borza, Viktor Lázár, -// Zoltán Gyebrovszki, Gábor Dolla -// -// More info: http://jaydata.org -(function(c){c.Array.prototype.toQueryable=function(){if(0= 200 && statusCode <= 299) { - success(event.data); - } else { - error(event.data); - } - }; - window.addEventListener('message', listener, false); - $data.Trace.log('before post', targetIframe); - targetIframe.postMessage(request, targetOrigin); - } else { - return odata.originalHttpClient.request(request, success, error); - } - } - }, - requestProxy: function (request, success, error) { - success = request.success || success; - error = request.error || error; - - delete request.success; - delete request.error; - - var targetIframe = request.targetIframe || $data.postMessageODataHandler.postMessageHttpClient.targetIframe; - var targetOrigin = request.targetOrigin || $data.postMessageODataHandler.postMessageHttpClient.targetOrigin || '*'; - - - if (targetIframe) { - request.requestProxy = true; - var listener = function (event) { - $data.Trace.log('in listener'); - window.removeEventListener('message', listener); - var statusCode = event.data.statusCode; - if (statusCode >= 200 && statusCode <= 299) { - success(event.data); - } else { - error(event.data); - } - }; - window.addEventListener('message', listener, false); - $data.Trace.log('before post', targetIframe); - targetIframe.postMessage(request, targetOrigin); - } else { - error({ message: "No iframe detected", request: request, response: undefined }); - } - } - }; - odata.defaultHttpClient = $data.postMessageODataHandler.postMessageHttpClient; - -})($data, window); - -(function ($data) { - $data.MsCrm = { - disableBatch: true - }; - $data.MsCrm.Auth = { - trace: true, - clientAuthorizationPath: "/WebResources/new_authorize.html", - messageHandlerPath: "/WebResources/new_postmessage.html", - login: function do_login(crmUrl, cb, local) { - var iframe; - - var onMessagehandlerLoaded = function (e) { - if ($data.MsCrm.Auth.trace) $data.Trace.log("Message received", crmUrl); - if (e.data.MessageHandlerLoaded) { - if ($data.MsCrm.Auth.trace) $data.Trace.log("Message handler loaded", crmUrl); - window.removeEventListener("message", onMessagehandlerLoaded); - window.OData.defaultHttpClient.targetIframe = iframe.contentWindow; - cb(iframe.contentWindow, crmUrl); - } - } - - var onAuthenticated = function (e) { - iframe = document.createElement("iframe"); - if (e.data.Authenticated) { - $data.Trace.log("Logged in to CRM: " + crmUrl); - window.removeEventListener("message", onAuthenticated); - window.addEventListener("message", onMessagehandlerLoaded); - var url = local ? "postmessage.html" : crmUrl + $data.MsCrm.Auth.messageHandlerPath; - iframe.src = url; - iframe.style.display = "none"; - document.body.appendChild(iframe); - } - } - window.addEventListener("message", onAuthenticated); - var url = local ? "authorize.html" : crmUrl + $data.MsCrm.Auth.clientAuthorizationPath; - url = url; - var w = window.open(url, "_blank", "resizable=false,location=0,menubar=0,toolbar=0,width=400,height=600"); - } - - } - $data.MsCrm.init = function (crmAddress, contextType, cb) { - var config = {}; - if (typeof crmAddress === 'object' && crmAddress) { - config = crmAddress; - crmAddress = config.url; - delete config.url; - } - var serviceUrl = crmAddress + '/XRMServices/2011/OrganizationData.svc'; - - if (window.location.href.indexOf(crmAddress) > -1) { - initContext(); - } else { - $data.MsCrm.Auth.login(crmAddress, function () { - initContext(); - }); - } - - function initContext() { - if (!(contextType.isAssignableTo && contextType.isAssignableTo($data.EntityContext))) { - cb = contextType; - config.disableBatch = $data.MsCrm.disableBatch; - $data.service(serviceUrl, config, function (factory) { - var ctx = factory(); - ctx.onReady().then(function () { - cb(ctx, factory); - }); - }); - } else { - function factory() { - return new contextType({ name: 'oData', oDataServiceHost: serviceUrl, disableBatch: $data.MsCrm.disableBatch }); - } - var ctx = factory(); - ctx.onReady().then(function () { - cb(ctx, factory); - }); - } - } - } - -})($data); \ No newline at end of file diff --git a/release/jaydatamodules/jaydata.mscrm.min.js b/release/jaydatamodules/jaydata.mscrm.min.js deleted file mode 100644 index e88c4dce..00000000 --- a/release/jaydatamodules/jaydata.mscrm.min.js +++ /dev/null @@ -1,19 +0,0 @@ -// JayData 1.3.6 -// Dual licensed under MIT and GPL v2 -// Copyright JayStack Technologies (http://jaydata.org/licensing) -// -// JayData is a standards-based, cross-platform Javascript library and a set of -// practices to access and manipulate data from various online and offline sources. -// -// Credits: -// Hajnalka Battancs, Dániel József, János Roden, László Horváth, Péter Nochta -// Péter Zentai, Róbert Bónay, Szabolcs Czinege, Viktor Borza, Viktor Lázár, -// Zoltán Gyebrovszki, Gábor Dolla -// -// More info: http://jaydata.org -(function(b,c,h){var f=c.OData;f.originalHttpClient=f.defaultHttpClient;b.postMessageODataHandler={postMessageHttpClient:{targetIframe:h,request:function(a,e,g){var d=a.targetIframe||b.postMessageODataHandler.postMessageHttpClient.targetIframe,h=a.targetOrigin||b.postMessageODataHandler.postMessageHttpClient.targetOrigin||"*";if(d){var i=function(a){b.Trace.log("in listener");c.removeEventListener("message",i);var d=a.data.statusCode;200<=d&&299>=d?e(a.data):g(a.data)};c.addEventListener("message", -i,!1);b.Trace.log("before post",d);d.postMessage(a,h)}else return f.originalHttpClient.request(a,e,g)}},requestProxy:function(a,e,g){e=a.success||e;g=a.error||g;delete a.success;delete a.error;var d=a.targetIframe||b.postMessageODataHandler.postMessageHttpClient.targetIframe,f=a.targetOrigin||b.postMessageODataHandler.postMessageHttpClient.targetOrigin||"*";if(d){a.requestProxy=!0;var i=function(a){b.Trace.log("in listener");c.removeEventListener("message",i);var d=a.data.statusCode;200<=d&&299>= -d?e(a.data):g(a.data)};c.addEventListener("message",i,!1);b.Trace.log("before post",d);d.postMessage(a,f)}else g({message:"No iframe detected",request:a,response:h})}};f.defaultHttpClient=b.postMessageODataHandler.postMessageHttpClient})($data,window); -(function(b){b.MsCrm={disableBatch:!0};b.MsCrm.Auth={trace:!0,clientAuthorizationPath:"/WebResources/new_authorize.html",messageHandlerPath:"/WebResources/new_postmessage.html",login:function(c,h,f){var a,e=function(d){b.MsCrm.Auth.trace&&b.Trace.log("Message received",c);d.data.MessageHandlerLoaded&&(b.MsCrm.Auth.trace&&b.Trace.log("Message handler loaded",c),window.removeEventListener("message",e),window.OData.defaultHttpClient.targetIframe=a.contentWindow,h(a.contentWindow,c))},g=function(d){a= -document.createElement("iframe");d.data.Authenticated&&(b.Trace.log("Logged in to CRM: "+c),window.removeEventListener("message",g),window.addEventListener("message",e),a.src=f?"postmessage.html":c+b.MsCrm.Auth.messageHandlerPath,a.style.display="none",document.body.appendChild(a))};window.addEventListener("message",g);window.open(f?"authorize.html":c+b.MsCrm.Auth.clientAuthorizationPath,"_blank","resizable=false,location=0,menubar=0,toolbar=0,width=400,height=600")}};b.MsCrm.init=function(c,h,f){function a(){if(!h.isAssignableTo|| -!h.isAssignableTo(b.EntityContext))f=h,e.disableBatch=b.MsCrm.disableBatch,b.service(g,e,function(a){var b=a();b.onReady().then(function(){f(b,a)})});else{var a=function(){return new h({name:"oData",oDataServiceHost:g,disableBatch:b.MsCrm.disableBatch})},c=a();c.onReady().then(function(){f(c,a)})}}var e={};"object"===typeof c&&c&&(e=c,c=e.url,delete e.url);var g=c+"/XRMServices/2011/OrganizationData.svc";-1Contains options.owningContextType if initialized in a scope of a context - var memberDefinitions = type.memberDefinitions, - fields = {}; - //debugger; - function getNullable(canonicType, pd) { - if (canonicType === "$data.Boolean") { - //grid validation errs on requied/nonnull bools - return true; - } - return pd.required !== true; - }; - - function getRequired(canonicType, pd) { - if ("$data.Boolean" === canonicType) { - return false; - } - return pd.required || "nullable" in pd ? !(pd.nullable) : false; - } - - memberDefinitions - .getPublicMappedProperties() - .forEach(function (pd) { - var canonicType = $data.Container.resolveName(pd.type); - //if (pd.dataType !== "Array" && !(pd.inverseProperty)) { - fields[pd.name] = { - //TODO - type: getKendoTypeName(canonicType, pd), - nullable: getNullable(canonicType, pd), - defaultValue: pd.defaultValue, - //nullable: false, - //nullable: "nullable" in pd ? pd.nullable : true, - editable: !pd.computed, - //defaultValue: true, - //defaultValue: 'abc', - //defaultValue: pd.type === "Edm.Boolean" ? false : undefined, - validation: { - required: getRequired(canonicType, pd) - } - } - //}; - }); - - function setInitialValue(obj, memDef) { - return; - //if (!obj[memDef.name]) { - // function getDefault() { - // switch ($data.Container.resolveType(memDef.type)) { - // case $data.Number: return 0.0; - // case $data.Integer: return 0; - // case $data.Date: return new Date(); - // case $data.Boolean: return false; - // } - // } - // obj[memDef.name] = getDefault(); - //} - } - - //console.dir(memberDefinitions.getPublicMappedMethods()); - var modelDefinition = { - fields: fields, - init: function (data) { - //console.dir(arguments); - var ctxType = options && options.owningContextType || undefined; - - var contextSetTypes = []; - if (options && options.owningContextType) { - contextSetTypes = options.owningContextType - .memberDefinitions - .getPublicMappedProperties() - .filter(function (pd) { - return $data.Container.resolveType(pd.type) === $data.EntitySet - }) - .map(function (pd) { - return $data.Container.resolveType(pd.elementType) - }); - } - - var newInstanceOptions = { - entityBuilder: function (instance, members) { - members.forEach(function (memberInfo) { - if (!(memberInfo.key === true) && (memberInfo.required === true || memberInfo.nullable === false)) { - var memberType = $data.Container.resolveType(memberInfo.type); - if (memberType.isAssignableTo && memberType.isAssignableTo($data.Entity) && contextSetTypes.indexOf(memberType) === -1) { - //it's a complex property - var _data; - if (data) { - _data = data[memberInfo.name]; - } - instance[memberInfo.name] = new memberType(_data, newInstanceOptions); - } - else { - setInitialValue(instance, memberInfo); - } - } - }); - } - } - - var jayInstance = data instanceof type ? data : new type(data, newInstanceOptions); - - var seed = jayInstance.initData; - - var feed = {}; - - //TODO create precompiled strategy - for (var j in seed) { - var md = type.getMemberDefinition(j); - var seedValue = seed[j]; - if (seedValue instanceof $data.Entity) { - var kendoInstance = seedValue.asKendoObservable(); - feed[j] = kendoInstance; - } - else if (md && $data.Container.resolveType(md.type) === Array) { - var jayType = $data.Container.resolveType(md.elementType); - var kendoType = jayType; - if (jayType.asKendoModel) { - kendoType = jayType.asKendoModel(); - } - var feedValue = new kendo.data.ObservableArray(seed[j], kendoType); - feed[j] = feedValue; - feed[j].bind('change', function (e) { - jayInstance.changeFromKendo = true; - this.parent().dirty = true; - jayInstance[md.name] = this.toJSON(); - delete jayInstance.changeFromKendo; - }); - } - else if (md && $data.Container.resolveType(md.type) === $data.Blob){ - feed[j] = $data.Blob.toBase64(seedValue); - //feed[j] = new kendo.data.Observable($data.Blob.toBase64(seedValue)); - /*feed[j].bind('change', function(e){ - //jayInstance.changeFromKendo = true; - jayInstance[md.name] = $data.Container.convertTo(atob(this), $data.Blob); - //delete jayInstance.changeFromKendo; - });*/ - } - else { - feed[j] = seedValue; - } - } - - var arrayMemberDef = type.memberDefinitions.getPublicMappedProperties().filter(function (item) { - return (($data.Container.resolveType(item.dataType) === Array) && (!$data.Container.resolveType(item.elementType).asKendoModel)) - }); - for (var j = 0; j < arrayMemberDef.length; j++) { - var memberDef = arrayMemberDef[j]; - if (seed[memberDef.name] === null || seed[memberDef.name] === undefined) { - feed[memberDef.name] = new kendo.data.ObservableArray([], $data.Container.resolveType(memberDef.elementType)) - feed[memberDef.name].bind('change', function (e) { - jayInstance.changeFromKendo = true; - this.parent().dirty = true; - jayInstance[memberDef.name] = this.toJSON(); - delete jayInstance.changeFromKendo; - }); - } - } - - var self = this; - this.innerInstance = function () { - return jayInstance - } - - //kendo.data.Model.fn.init.call(this, feed); - $data.kendo.BaseModelType.fn.init.call(this, feed); - - jayInstance.propertyChanged.attach(function (obj, propinfo) { - var jay = this; - var newValue = propinfo.newValue; - var md = jayInstance.getType().getMemberDefinition(propinfo.propertyName); - if (!jay.changeFromKendo) { - newValue = newValue ? (newValue.asKendoObservable ? newValue.asKendoObservable() : newValue) : newValue; - jayInstance.changeFromJay = true; - if ($data.Container.resolveType(md.type) === $data.Blob && newValue){ - newValue = $data.Blob.toBase64(newValue); - } - self.set(propinfo.propertyName, newValue); - if (md.computed && self[propinfo.propertyName] !== newValue){ - self[propinfo.propertyName] = newValue; - } - delete jayInstance.changeFromJay; - }else{ - if ($data.Container.resolveType(md.type) === $data.Blob){ - var blob = $data.Blob.toString(newValue); - newValue = $data.Container.convertTo(atob(blob), $data.Blob); - jayInstance.changeFromJay = true; - jayInstance.initData[md.name] = newValue; - //self.set(propinfo.propertyName, blob); - delete jayInstance.changeFromJay; - } - } - }); - - this.bind("set", function (e) { - var propName = e.field; - var propNameParts = propName.split("."); - jayInstance.changeFromKendo = true; - if (propNameParts.length == 1) { - var propValue = e.value; - if (!jayInstance.changeFromJay) { - propValue = propValue.innerInstance ? propValue.innerInstance() : propValue; - jayInstance[propName] = propValue; - if (options && options.autoSave) { - jayInstance.save(); - } - } - } - else { - var rootProp = jayInstance[propNameParts[0]]; - if (rootProp instanceof $data.Entity) { - jayInstance[propNameParts[0]] = rootProp; - } - } - delete jayInstance.changeFromKendo; - }); - if (options && options.newInstanceCallback) { - options.newInstanceCallback(jayInstance); - } - }, - save: function () { - //console.log("item.save", this, arguments); - return this.innerInstance().save(); - }, - remove: function () { - return this.innerInstance().remove(); - } - - }; - - var keyProperties = memberDefinitions.getKeyProperties(); - switch (keyProperties.length) { - case 0: - break; - case 1: - modelDefinition.id = keyProperties[0].name; - break; - default: - console.warn("entity with multiple keys not supported"); - break; - } - $data.Trace.log("md", modelDefinition); - - var returnValue = kendo.data.Model.define($data.kendo.BaseModelType, modelDefinition); - - return returnValue; - } - - function asKendoModel(options) { - var cacheObject = options || type; - return cacheObject.kendoModelType || (cacheObject.kendoModelType = createKendoModel(options)); - } - - function asKendoObservable(instance, options) { - var kendoModel = type.asKendoModel(options); - return new kendoModel(instance); - } - - type.asKendoModel = asKendoModel; - //type.asKendoModelType = asKendoModel; - - type.prototype.asKendoObservable = function (options) { - var self = this; - - var kendoObservable = asKendoObservable(this, options); - - return kendoObservable; - } - - function r(value) { - return value || ''; - } - function registerStoreAlias(type, options) { - if (!options.provider) - return; - var key = r(options.databaseName) + r(options.tableName) + r(options.url) + r(options.apiUrl) + r(options.oDataServiceHost); - var storeDef = { - provider: options.provider, - databaseName: options.databaseName, - tableName: options.tableName, - dataSource: options.url, - apiUrl: options.apiUrl, - oDataServiceHost: options.oDataServiceHost - }; - Object.keys(storeDef).forEach(function (k) { - delete options[k]; - }); - - type.setStore(key, storeDef); - return key; - } - - type.asKendoDataSource = function (options, modelOptions, storeAlias) { - options = options || {}; - var mOptions = modelOptions || {}; - var salias = registerStoreAlias(type, options) || storeAlias; - var token = $data.ItemStore._getStoreAlias(type, salias); - var ctx = $data.ItemStore._getContextPromise(token, type); - var set = ctx.getEntitySetFromElementType(type); - return set.asKendoDataSource(options, mOptions); - }; - - if (oldProcessor) { - oldProcessor(type); - } - } - $data.Queryable.addMember("asKendoColumns", function (columns) { - var result = []; - columns = columns || {}; - var showComplex = columns['$showComplexFields'] === true; - delete columns['$showComplexFields']; - - this.defaultType - .memberDefinitions - .getPublicMappedProperties() - .forEach(function (pd) { - //if (pd.dataType !== "Array" && !(pd.inverseProperty)) { - if (showComplex || kendoTypeMap[$data.Container.resolveName(pd.type)]) { - var col = columns[pd.name] || {}; - var colD = { field: pd.name }; - $.extend(colD, col) - result.push(colD); - } - //} - }); - - function append(field) { - field = Array.isArray(field) ? field : [field]; - var result = this.concat(field); - return prepareResult(result); - } - - function prepend(field) { - field = Array.isArray(field) ? field : [field]; - var result = field.concat(this); - return prepareResult(result); - } - - function setColumn(colName, def) { - var it = this.filter(function (item) { - return item.field == colName - })[0]; - $.extend(it, def); - return this; - } - - function prepareResult(r) { - r.prepend = prepend; - r.append = append; - r.setColumn = setColumn; - return r; - } - return prepareResult(result); - //return ['id', 'Year', 'Manufacturer', { command: ["edit", "create", "destroy", "update"] }]; - }), - - //, { command: ["edit", "create", "destroy", "update"]} - $data.EntityContext.addProperty("EntitySetNames", function () { - var self = this; - //var sets = Object.keys(self._entitySetReferences); - //return sets; - return Object.keys(self._entitySetReferences).map(function (set) { - return self._entitySetReferences[set].tableName; - }); - }); - - $data.Queryable.addMember("asKendoModel", function (options) { - options.owningContextType = options.owningContextType || this.entityContext.getType(); - return this.defaultType.asKendoModel(options); - }); - - $data.Queryable.addMember("asKendoRemoteTransportClass", function (modelItemClass) { - var self = this; - var ctx = self.entityContext; - function reset() { - ctx.stateManager.reset(); - }; - var TransportClass = kendo.data.RemoteTransport.extend({ - init: function () { - this.items = []; - }, - read: function (options) { - var query = self; - - query.entityContext.onReady().then(function () { - var _this = this; - var q = query; - var sp = query.entityContext.storageProvider; - var withInlineCount = query.entityContext.storageProvider.supportedSetOperations.withInlineCount; - var withLength = (!withInlineCount) && query.entityContext.storageProvider.supportedSetOperations.length; - - if (withInlineCount) { - q = q.withInlineCount(); - } - - if (options.data.filter) { - var filter = ""; - var thisArg = {}; - options.data.filter.filters.forEach(function (f, index) { - if (index > 0) { - filter += options.data.filter.logic == "or" ? " || " : " && "; - } - - switch (f.operator) { - case 'eq': - filter += "it." + f.field; - filter += " == this." + f.field; - break; - case 'neq': - filter += "it." + f.field; - filter += " != this." + f.field; - break; - case 'startswith': - filter += "it." + f.field; - filter += ".startsWith(this." + f.field + ")"; - break; - case 'contains': - filter += "it." + f.field; - filter += ".contains(this." + f.field + ")"; - break; - case 'doesnotcontain': - filter += "!"; - filter += "it." + f.field; - filter += ".contains(this." + f.field + ")"; - break; - case 'endswith': - filter += "it." + f.field; - filter += ".endsWith(this." + f.field + ")"; - break; - case 'gte': - filter += "it." + f.field; - filter += " >= this." + f.field; - break; - case 'gt': - filter += "it." + f.field; - filter += " > this." + f.field; - break; - case 'lte': - filter += "it." + f.field; - filter += " <= this." + f.field; - break; - case 'lt': - filter += "it." + f.field; - filter += " < this." + f.field; - break; - default: - $data.Trace.log('unknown operator', f.operator); - break; - } - thisArg[f.field] = f.value; - }) - q = q.filter(filter, thisArg); - } - var allItemsQ = q; - - if (options.data.sort) { - options.data.sort.forEach(function (s) { - q = q.order((s.dir == 'desc' ? "-" : "") + s.field); - }) - } - - if (options.data.skip) { - q = q.skip(options.data.skip); - } - if (options.data.take) { - q = q.take(options.data.take); - } - - //Data.defaultHttpClient.enableJsonpCallback = true; - var promises = []; - - promises.push(q.toArray()); - //var ta = q.toArray(); - if (withLength) { - promises.push(allItemsQ.length()); - } - else if (!withInlineCount) { - promises.push(allItemsQ.toArray()); - } - - $data.Trace.log(promises); - jQuery.when.apply(this, promises).then(function (items, total) { - console.dir(arguments); - //var result = items.map(function (item) { return item instanceof $data.Entity ? new model(item.initData) : item; }); - var result = items.map(function (item) { - var d = (item instanceof $data.Entity) ? item.initData : item; - var kendoItem = item.asKendoObservable(); - return kendoItem; - }); - var r = { - data: result, - total: withInlineCount ? items.totalCount : (withLength ? total : total.length) - } - $data.Trace.log(r); - options.success(r); - }).fail(function () { - console.log("error in create"); - options.error({}, arguments); - }); - }); - }, - create: function (options, model) { - var query = self; - query.entityContext.onReady().then(function () { - if (model.length > 1) { - var modelItems = []; - model.forEach(function (modelItem) { - modelItems.push(modelItem.innerInstance()); - }); - ctx.addMany(modelItems); - ctx.saveChanges().then(function () { - var data = []; - modelItems.forEach(function (modelItem) { - data.push(modelItem.initData); - }); - options.success(/*{ data: data }*/); - }).fail(function () { - console.log("error in create"); - options.error({}, arguments); - ctx.stateManager.reset(); - }); - } - else { - console.dir(ctx.storeToken); - model[0] - .innerInstance() - .save(ctx.storeToken) - .then(function () { - options.success(/*{ data: model[0].innerInstance().initData }*/); - }) - .fail(function () { - console.log("error in create"); - options.error({}, arguments); - }); - } - }); - }, - update: function (options, model) { - var query = self; - query.entityContext.onReady().then(function () { - if (model.length > 1) { - var items = model.map(function (item) { - return item.innerInstance() - }); - items.forEach(function (item) { - ctx.attach(item, true); - }); - ctx.saveChanges().then(function () { - options.success(); - }).fail(function () { - ctx.stateManager.reset(); - //alert("error in batch update"); - options.error({}, arguments); - }); - } - else { - model[0].innerInstance().save().then(function (item) { - options.success(); - }).fail(function () { - //alert("error in update") - options.error({}, arguments); - }); - } - }); - }, - - destroy: function (options, model) { - var query = self; - query.entityContext.onReady().then(function () { - if (model.length > 1) { - model.forEach(function (item) { - ctx.remove(item.innerInstance()); - }); - ctx.saveChanges().then(function () { - options.success({ data: options.data }); - }).fail(function () { - ctx.stateManager.reset(); - //alert("error in save:" + arguments[0]); - options.error({}, "error", options.data); - }); - } - else { - model[0].innerInstance().remove().then(function () { - options.success({ data: options.data }); - }).fail(function () { - ctx.stateManager.reset(); - //alert("error in save:" + arguments[0]); - options.error({}, "error", options.data); - }); - } - }); - }, - setup: function () { - $data.Trace.log("setup"); - $data.Trace.log(arguments); - } - }); - return TransportClass; - }); - - var jayDataSource = kendo.data.DataSource.extend({ - init: function () { - kendo.data.DataSource.fn.init.apply(this, arguments); - }, - createItem: function (initData) { - var type = this.options.schema.model; - return new type(initData); - }, - _promise: function (data, models, type) { - var that = this, - extend = $.extend, - transport = that.transport; - - return $.Deferred(function (deferred) { - transport[type].call(transport, extend({ - success: function (response) { - deferred.resolve({ - response: response, - models: models, - type: type - }); - }, - error: function (response, status, error) { - deferred.reject(response); - that.error(response, status, error); - } - }, data), models - ); - }).promise(); - } - }); - - $data.kendo = $data.kendo || {}; - - $data.kendo.defaultPageSize = 25; - - $data.Queryable.addMember("asKendoDataSource", function (ds, modelOptions) { - var self = this; - - modelOptions = modelOptions || {}; - var model = self.asKendoModel(modelOptions); - - ds = ds || {}; - //unless user explicitly opts out server side logic - //we just force it. - ds.serverPaging = ds.serverPaging || true; - ds.serverFiltering = ds.serverFiltering || true; - ds.serverSorting = ds.serverSorting || true; - ds.pageSize = ds.pageSize === undefined ? $data.kendo.defaultPageSize : ds.pageSize; - - var TransportClass = self.asKendoRemoteTransportClass(model); - ds.transport = new TransportClass(); - - ds.schema = { - model: model, - data: "data", - total: "total" - }; - return new jayDataSource(ds); - }); - - kendo.data.binders.submit = kendo.data.Binder.extend({ - init: function (element, bindings, options) { - kendo.data.Binder.fn.init.call(this, element, bindings, options); - $(element).bind("submit", function () { - var obj = bindings.submit.source; - var fn = obj[bindings.submit.path]; - if (typeof fn === 'function') { - fn.apply(obj, arguments); - return false; - } - }); - }, - refresh: function () { - } - }); -})($data); \ No newline at end of file diff --git a/release/jaydatamodules/kendo.min.js b/release/jaydatamodules/kendo.min.js deleted file mode 100644 index f8470174..00000000 --- a/release/jaydatamodules/kendo.min.js +++ /dev/null @@ -1,32 +0,0 @@ -// JayData 1.3.6 -// Dual licensed under MIT and GPL v2 -// Copyright JayStack Technologies (http://jaydata.org/licensing) -// -// JayData is a standards-based, cross-platform Javascript library and a set of -// practices to access and manipulate data from various online and offline sources. -// -// Credits: -// Hajnalka Battancs, Dániel József, János Roden, László Horváth, Péter Nochta -// Péter Zentai, Róbert Bónay, Szabolcs Czinege, Viktor Borza, Viktor Lázár, -// Zoltán Gyebrovszki, Gábor Dolla -// -// More info: http://jaydata.org -(function(d){var l=d.Entity.inheritedTypeProcessor;d.kendo={};d.kendo.BaseModelType=kendo.data.Model.define({init:function(a){kendo.data.Model.fn.init.call(this,a)}});var q={"$data.Blob":"string","$data.String":"string","$data.Boolean":"boolean","$data.Integer":"number","$data.Number":"number","$data.Date":"date","$data.DateTimeOffset":"date","$data.Time":"string","$data.Byte":"number","$data.SByte":"number","$data.Int16":"number","$data.Int32":"number","$data.Int64":"number","$data.Decimal":"string", -"$data.Float":"number"};d.Entity.inheritedTypeProcessor=function(a){function f(b){var e=a.memberDefinitions,h={};e.getPublicMappedProperties().forEach(function(a){var c=d.Container.resolveName(a.type);h[a.name]={type:q[c]||"object",nullable:"$data.Boolean"===c?!0:!0!==a.required,defaultValue:a.defaultValue,editable:!a.computed,validation:{required:"$data.Boolean"===c?!1:a.required||"nullable"in a?!a.nullable:!1}}});var c={fields:h,init:function(c){var e=[];b&&b.owningContextType&&(e=b.owningContextType.memberDefinitions.getPublicMappedProperties().filter(function(a){return d.Container.resolveType(a.type)=== -d.EntitySet}).map(function(a){return d.Container.resolveType(a.elementType)}));var h={entityBuilder:function(a,g){g.forEach(function(g){if(!0!==g.key&&(!0===g.required||!1===g.nullable)){var b=d.Container.resolveType(g.type);if(b.isAssignableTo&&b.isAssignableTo(d.Entity)&&-1===e.indexOf(b)){var f;c&&(f=c[g.name]);a[g.name]=new b(f,h)}}})}},g=c instanceof a?c:new a(c,h),f=g.initData,j={},k;for(k in f){var m=a.getMemberDefinition(k),i=f[k];if(i instanceof d.Entity)i=i.asKendoObservable(),j[k]=i;else if(m&& -d.Container.resolveType(m.type)===Array){var l=i=d.Container.resolveType(m.elementType);i.asKendoModel&&(l=i.asKendoModel());i=new kendo.data.ObservableArray(f[k],l);j[k]=i;j[k].bind("change",function(){g.changeFromKendo=!0;this.parent().dirty=!0;g[m.name]=this.toJSON();delete g.changeFromKendo})}else j[k]=m&&d.Container.resolveType(m.type)===d.Blob?d.Blob.toBase64(i):i}i=a.memberDefinitions.getPublicMappedProperties().filter(function(a){return d.Container.resolveType(a.dataType)===Array&&!d.Container.resolveType(a.elementType).asKendoModel}); -for(k=0;k', - iElems[0] - ){}; - return version > 4 ? version : undefined; - }()); - - ko.utils.ensureSelectElementIsRenderedCorrectly = function(selectElement) { - // Workaround for IE9 rendering bug - it doesn't reliably display all the text in dynamically-added select boxes unless you force it to re-render by updating the width. - // (See https://github.com/SteveSanderson/knockout/issues/312, http://stackoverflow.com/questions/5908494/select-only-shows-first-char-of-selected-option) - if (ieVersion >= 9) { - var originalWidth = selectElement.style.width; - selectElement.style.width = 0; - selectElement.style.width = originalWidth; - } - }; - - ko.utils.setOptionNodeSelectionState = function (optionNode, isSelected) { - // IE6 sometimes throws "unknown error" if you try to write to .selected directly, whereas Firefox struggles with setAttribute. Pick one based on browser. - if (navigator.userAgent.indexOf("MSIE 6") >= 0) - optionNode.setAttribute("selected", isSelected); - else - optionNode.selected = isSelected; - }; - - ko.utils.setTextContent = function(element, textContent) { - var value = ko.utils.unwrapObservable(textContent); - if ((value === null) || (value === undefined)) - value = ""; - - 'innerText' in element ? element.innerText = value - : element.textContent = value; - - if (ieVersion >= 9) { - // Believe it or not, this actually fixes an IE9 rendering bug - // (See https://github.com/SteveSanderson/knockout/issues/209) - element.style.display = element.style.display; - } - }; - - function ensureDropdownSelectionIsConsistentWithModelValue(element, modelValue, preferModelValue) { - if (preferModelValue) { - if (modelValue !== ko.selectExtensions.readValue(element)) - ko.selectExtensions.writeValue(element, modelValue); - } - - // No matter which direction we're syncing in, we want the end result to be equality between dropdown value and model value. - // If they aren't equal, either we prefer the dropdown value, or the model value couldn't be represented, so either way, - // change the model value to match the dropdown. - if (modelValue !== ko.selectExtensions.readValue(element)) - ko.utils.triggerEvent(element, "change"); - }; - - ko.bindingHandlers['options'] = { - 'update': function (element, valueAccessor, allBindingsAccessor) { - if (element.tagName.toLowerCase() !== "select") - throw new Error("options binding applies only to SELECT elements"); - - var selectWasPreviouslyEmpty = element.length == 0; - var previousSelectedValues = ko.utils.arrayMap(ko.utils.arrayFilter(element.childNodes, function (node) { - return node.tagName && (node.tagName.toLowerCase() === "option") && node.selected; - }), function (node) { - return ko.selectExtensions.readValue(node) || node.innerText || node.textContent; - }); - var previousScrollTop = element.scrollTop; - - var value = ko.utils.unwrapObservable(valueAccessor()); - var selectedValue = element.value; - - // Remove all existing