You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@olingo.apache.org by ch...@apache.org on 2014/08/28 05:48:24 UTC
[17/51] [partial] rename folder /datajs into /odatajs. no file
modification.
http://git-wip-us.apache.org/repos/asf/olingo-odata4-js/blob/d5ec5557/odatajs/src/lib/odata/utils.js
----------------------------------------------------------------------
diff --git a/odatajs/src/lib/odata/utils.js b/odatajs/src/lib/odata/utils.js
new file mode 100644
index 0000000..efaea05
--- /dev/null
+++ b/odatajs/src/lib/odata/utils.js
@@ -0,0 +1,1261 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+ /** @module odata/utils */
+
+var utils = require('./../datajs.js').utils;
+
+// Imports
+var assigned = utils.assigned;
+var contains = utils.contains;
+var find = utils.find;
+var isArray = utils.isArray;
+var isDate = utils.isDate;
+var isObject = utils.isObject;
+var parseInt10 = utils.parseInt10;
+
+
+/** Gets the type name of a data item value that belongs to a feed, an entry, a complex type property, or a collection property
+ * @param {string} value - Value of the data item from which the type name is going to be retrieved.
+ * @param {object} [metadata] - Object containing metadata about the data tiem.
+ * @returns {string} Data item type name; null if the type name cannot be found within the value or the metadata
+ * 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.
+ */
+var dataItemTypeName = function (value, 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 GEOGRAPHY = "Geography";
+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 GEOGRAPHY_POINT = GEOGRAPHY + "Point";
+var GEOGRAPHY_LINESTRING = GEOGRAPHY + "LineString";
+var GEOGRAPHY_POLYGON = GEOGRAPHY + "Polygon";
+var GEOGRAPHY_COLLECTION = GEOGRAPHY + "Collection";
+var GEOGRAPHY_MULTIPOLYGON = GEOGRAPHY + "MultiPolygon";
+var GEOGRAPHY_MULTILINESTRING = GEOGRAPHY + "MultiLineString";
+var GEOGRAPHY_MULTIPOINT = GEOGRAPHY + "MultiPoint";
+
+var GEOMETRY = "Geometry";
+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 GEOMETRY_POINT = GEOMETRY + "Point";
+var GEOMETRY_LINESTRING = GEOMETRY + "LineString";
+var GEOMETRY_POLYGON = GEOMETRY + "Polygon";
+var GEOMETRY_COLLECTION = GEOMETRY + "Collection";
+var GEOMETRY_MULTIPOLYGON = GEOMETRY + "MultiPolygon";
+var GEOMETRY_MULTILINESTRING = GEOMETRY + "MultiLineString";
+var GEOMETRY_MULTIPOINT = 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 geometryTypes = [
+ GEOMETRY,
+ GEOMETRY_POINT,
+ GEOMETRY_LINESTRING,
+ GEOMETRY_POLYGON,
+ GEOMETRY_COLLECTION,
+ GEOMETRY_MULTIPOLYGON,
+ GEOMETRY_MULTILINESTRING,
+ 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 geographyTypes = [
+ GEOGRAPHY,
+ GEOGRAPHY_POINT,
+ GEOGRAPHY_LINESTRING,
+ GEOGRAPHY_POLYGON,
+ GEOGRAPHY_COLLECTION,
+ GEOGRAPHY_MULTIPOLYGON,
+ GEOGRAPHY_MULTILINESTRING,
+ GEOGRAPHY_MULTIPOINT
+];
+
+/** Invokes a function once per schema in metadata.
+ * @param metadata - Metadata store; one of edmx, schema, or an array of any of them.
+ * @param {Function} callback - Callback function to invoke once per schema.
+ * @returns The first truthy value to be returned from the callback; null or the last falsy value otherwise.
+ */
+function forEachSchema(metadata, callback) {
+
+
+ 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);
+ }
+}
+
+/** Formats a millisecond and a nanosecond value into a single string.
+ * @param {Numaber} ms - Number of milliseconds to format.</param>
+ * @param {Numaber} ns - Number of nanoseconds to format.</param>
+ * @returns {String} Formatted text.
+ * If the value is already as string it's returned as-is.</remarks>
+ */
+function formatMilliseconds(ms, ns) {
+
+ // 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;
+}
+
+function formatDateTimeOffsetJSON(value) {
+ return "\/Date(" + value.getTime() + ")\/";
+}
+
+/** Formats a DateTime or DateTimeOffset value a string.
+ * @param {Date} value - Value to format
+ * @returns {String} Formatted text.
+ * If the value is already as string it's returned as-is
+´*/
+function formatDateTimeOffset(value) {
+
+ if (typeof value === "string") {
+ return value;
+ }
+
+ var hasOffset = isDateTimeOffset(value);
+ var offset = getCanonicalTimezone(value.__offset);
+ if (hasOffset && offset !== "Z") {
+ // We're about to change the value, so make a copy.
+ value = new Date(value.valueOf());
+
+ var timezone = parseTimezone(offset);
+ var hours = value.getUTCHours() + (timezone.d * timezone.h);
+ var minutes = value.getUTCMinutes() + (timezone.d * timezone.m);
+
+ value.setUTCHours(hours, minutes);
+ } else if (!hasOffset) {
+ // Don't suffix a 'Z' for Edm.DateTime values.
+ offset = "";
+ }
+
+ var year = value.getUTCFullYear();
+ var month = value.getUTCMonth() + 1;
+ var sign = "";
+ if (year <= 0) {
+ year = -(year - 1);
+ sign = "-";
+ }
+
+ var ms = formatMilliseconds(value.getUTCMilliseconds(), value.__ns);
+
+ return sign +
+ formatNumberWidth(year, 4) + "-" +
+ formatNumberWidth(month, 2) + "-" +
+ formatNumberWidth(value.getUTCDate(), 2) + "T" +
+ formatNumberWidth(value.getUTCHours(), 2) + ":" +
+ formatNumberWidth(value.getUTCMinutes(), 2) + ":" +
+ formatNumberWidth(value.getUTCSeconds(), 2) +
+ ms + offset;
+}
+
+/** Converts a duration to a string in xsd:duration format.
+ * @param {Object} value - Object with ms and __edmType properties.
+ * @returns {String} String representation of the time object in xsd:duration format.
+ */
+function formatDuration(value) {
+
+ 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";
+}
+
+/** Formats the specified value to the given width.
+ * @param {Number} value - Number to format (non-negative).
+ * @param {Number} width - Minimum width for number.
+ * @param {Boolean} append - Flag indicating if the value is padded at the beginning (false) or at the end (true).
+ * @returns {String} Text representation.
+ */
+function formatNumberWidth(value, width, append) {
+ var result = value.toString(10);
+ while (result.length < width) {
+ if (append) {
+ result += "0";
+ } else {
+ result = "0" + result;
+ }
+ }
+
+ return result;
+}
+
+/** Gets the canonical timezone representation.
+ * @param {String} timezone - Timezone representation.
+ * @returns {String} An 'Z' string if the timezone is absent or 0; the timezone otherwise.
+ */
+function getCanonicalTimezone(timezone) {
+
+ return (!timezone || timezone === "Z" || timezone === "+00:00" || timezone === "-00:00") ? "Z" : timezone;
+}
+
+/** Gets the type of a collection type name.
+ * @param {String} typeName - Type name of the collection.
+ * @returns {String} Type of the collection; null if the type name is not a collection type.
+ */
+function getCollectionType(typeName) {
+
+ if (typeof typeName === "string") {
+ var end = typeName.indexOf(")", 10);
+ if (typeName.indexOf("Collection(") === 0 && end > 0) {
+ return typeName.substring(11, end);
+ }
+ }
+ return null;
+}
+
+/** Sends a request containing OData payload to a server.
+* @param request - Object that represents the request to be sent..
+* @param success - Callback for a successful read operation.
+* @param error - Callback for handling errors.
+* @param handler - Handler for data serialization.
+* @param httpClient - HTTP client layer.
+* @param context - Context used for processing the request
+*/
+function invokeRequest(request, success, error, handler, httpClient, context) {
+
+ 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;
+ }
+ // errors in success handler for sync requests result in error handler calls. So here we fix this.
+ try {
+ success(response.data, response);
+ } catch (err) {
+ err.bIsSuccessHandlerError = true;
+ throw err;
+ }
+ }, error);
+}
+
+/** Tests whether a value is a batch object in the library's internal representation.
+ * @param value - Value to test.
+ * @returns {Boolean} True is the value is a batch object; false otherwise.
+ */
+function isBatch(value) {
+
+ return isComplex(value) && isArray(value.__batchRequests);
+}
+
+// Regular expression used for testing and parsing for a collection type.
+var collectionTypeRE = /Collection\((.*)\)/;
+
+/** Tests whether a value is a collection value in the library's internal representation.
+ * @param value - Value to test.
+ * @param {Sting} typeName - Type name of the value. This is used to disambiguate from a collection property value.
+ * @returns {Boolean} True is the value is a feed value; false otherwise.
+ */
+function isCollection(value, typeName) {
+
+ var colData = value && value.results || value;
+ return !!colData &&
+ (isCollectionType(typeName)) ||
+ (!typeName && isArray(colData) && !isComplex(colData[0]));
+}
+
+/** Checks whether the specified type name is a collection type.
+ * @param {String} typeName - Name of type to check.
+ * @returns {Boolean} True if the type is the name of a collection type; false otherwise.
+ */
+function isCollectionType(typeName) {
+ return collectionTypeRE.test(typeName);
+}
+
+/** Tests whether a value is a complex type value in the library's internal representation.
+ * @param value - Value to test.
+ * @returns {Boolean} True is the value is a complex type value; false otherwise.
+ */
+function isComplex(value) {
+
+ return !!value &&
+ isObject(value) &&
+ !isArray(value) &&
+ !isDate(value);
+}
+
+/** Checks whether a Date object is DateTimeOffset value
+ * @param {Date} value - Value to check
+ * @returns {Boolean} true if the value is a DateTimeOffset, false otherwise.
+ */
+function isDateTimeOffset(value) {
+ return (value.__edmType === "Edm.DateTimeOffset" || (!value.__edmType && value.__offset));
+}
+
+/** Tests whether a value is a deferred navigation property in the library's internal representation.
+ * @param value - Value to test.
+ * @returns {Boolean} True is the value is a deferred navigation property; false otherwise.
+ */
+function isDeferred(value) {
+
+ if (!value && !isComplex(value)) {
+ return false;
+ }
+ var metadata = value.__metadata || {};
+ var deferred = value.__deferred || {};
+ return !metadata.type && !!deferred.uri;
+}
+
+/** Tests whether a value is an entry object in the library's internal representation.
+ * @param value - Value to test.
+ * @returns {Boolean} True is the value is an entry object; false otherwise.
+ */
+function isEntry(value) {
+
+ return isComplex(value) && value.__metadata && "uri" in value.__metadata;
+}
+
+/** Tests whether a value is a feed value in the library's internal representation.
+ * @param value - Value to test.
+ * @param {Sting} typeName - Type name of the value. This is used to disambiguate from a collection property value.
+ * @returns {Boolean} True is the value is a feed value; false otherwise.
+ */
+function isFeed(value, typeName) {
+
+ var feedData = value && value.results || value;
+ return isArray(feedData) && (
+ (!isCollectionType(typeName)) &&
+ (isComplex(feedData[0]))
+ );
+}
+
+/** Checks whether the specified type name is a geography EDM type.
+ * @param {String} typeName - Name of type to check.
+ * @returns {Boolean} True if the type is a geography EDM type; false otherwise.
+ */
+function isGeographyEdmType(typeName) {
+
+ //check with edm
+ var ret = contains(geographyEdmTypes, typeName) ||
+ (typeName.indexOf('.') === -1 && contains(geographyTypes, typeName));
+ return ret;
+
+}
+
+/** Checks whether the specified type name is a geometry EDM type.
+ * @param {String} typeName - Name of type to check.
+ * @returns {Boolean} True if the type is a geometry EDM type; false otherwise.
+ */
+function isGeometryEdmType(typeName) {
+
+ var ret = contains(geometryEdmTypes, typeName) ||
+ (typeName.indexOf('.') === -1 && contains(geometryTypes, typeName));
+ return ret;
+}
+
+/** Tests whether a value is a named stream value in the library's internal representation.
+ * @param value - Value to test.
+ * @returns {Boolean} True is the value is a named stream; false otherwise.
+ */
+function isNamedStream(value) {
+
+ if (!value && !isComplex(value)) {
+ return false;
+ }
+ var metadata = value.__metadata;
+ var mediaResource = value.__mediaresource;
+ return !metadata && !!mediaResource && !!mediaResource.media_src;
+}
+
+/** Tests whether a value is a primitive type value in the library's internal representation.
+ * @param value - Value to test.
+ * @returns {Boolean} True is the value is a primitive type value.
+ * Date objects are considered primitive types by the library.
+ */
+function isPrimitive(value) {
+
+ return isDate(value) ||
+ typeof value === "string" ||
+ typeof value === "number" ||
+ typeof value === "boolean";
+}
+
+/** Checks whether the specified type name is a primitive EDM type.
+ * @param {String} typeName - Name of type to check.
+ * @returns {Boolean} True if the type is a primitive EDM type; false otherwise.
+ */
+function isPrimitiveEdmType(typeName) {
+
+ return contains(primitiveEdmTypes, typeName);
+}
+
+/** Gets the kind of a navigation property value.
+ * @param value - Value of the navigation property.
+ * @param {Object} [propertyModel] - Object that describes the navigation property in an OData conceptual schema.
+ * @returns {String} String value describing the kind of the navigation property; null if the kind cannot be determined.
+ */
+function navigationPropertyKind(value, propertyModel) {
+
+ 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;
+}
+
+/** Looks up a property by name.
+ * @param {Array} properties - Array of property objects as per EDM metadata (may be null)
+ * @param {String} name - Name to look for.
+ * @returns {Object} The property object; null if not found.
+ */
+function lookupProperty(properties, name) {
+
+ return find(properties, function (property) {
+ return property.name === name;
+ });
+}
+
+/** Looks up a type object by name.
+ * @param {String} name - Name, possibly null or empty.
+ * @param metadata - Metadata store; one of edmx, schema, or an array of any of them.
+ * @param {String} kind - Kind of object to look for as per EDM metadata.
+ * @returns An type description if the name is found; null otherwise
+ */
+function lookupInMetadata(name, metadata, kind) {
+
+ return (name) ? forEachSchema(metadata, function (schema) {
+ return lookupInSchema(name, schema, kind);
+ }) : null;
+}
+
+/** Looks up a entity set by name.
+ * @param {Array} properties - Array of entity set objects as per EDM metadata( may be null)
+ * @param {String} name - Name to look for.
+ * @returns {Object} The entity set object; null if not found.
+ */
+function lookupEntitySet(entitySets, name) {
+
+ return find(entitySets, function (entitySet) {
+ return entitySet.name === name;
+ });
+}
+
+/** Looks up a entity set by name.
+ * @param {Array} properties - Array of entity set objects as per EDM metadata (may be null)
+ * @param {String} name - Name to look for.
+ * @returns {Object} The entity set object; null if not found.
+ */
+function lookupSingleton(singletons, name) {
+
+ return find(singletons, function (singleton) {
+ return singleton.name === name;
+ });
+}
+
+/** Looks up a complex type object by name.
+ * @param {String} name - Name, possibly null or empty.
+ * @param metadata - Metadata store; one of edmx, schema, or an array of any of them.
+ * @returns A complex type description if the name is found; null otherwise.</returns>
+ */
+function lookupComplexType(name, metadata) {
+
+ return lookupInMetadata(name, metadata, "complexType");
+}
+
+/** Looks up an entity type object by name.
+ * @param {String} name - Name, possibly null or empty.
+ * @param metadata - Metadata store; one of edmx, schema, or an array of any of them.
+ * @returns An entity type description if the name is found; null otherwise.</returns>
+ */
+function lookupEntityType(name, metadata) {
+
+ return lookupInMetadata(name, metadata, "entityType");
+}
+
+
+/** Looks up an
+ * @param {String} name - Name, possibly null or empty.
+ * @param metadata - Metadata store; one of edmx, schema, or an array of any of them.
+ * @returns An entity container description if the name is found; null otherwise.</returns>
+ */
+function lookupDefaultEntityContainer(metadata) {
+
+ return forEachSchema(metadata, function (schema) {
+ if (isObject(schema.entityContainer)) {
+ return schema.entityContainer;
+ }
+ });
+}
+
+/** Looks up an entity container object by name.
+ * @param {String} name - Name, possibly null or empty.
+ * @param metadata - Metadata store; one of edmx, schema, or an array of any of them.
+ * @returns An entity container description if the name is found; null otherwise.</returns>
+ */
+function lookupEntityContainer(name, metadata) {
+
+ return lookupInMetadata(name, metadata, "entityContainer");
+}
+
+/** Looks up a function import by name.
+ * @param {Array} properties - Array of function import objects as per EDM metadata (May be null)
+ * @param {String} name - Name to look for.
+ * @returns {Object} The entity set object; null if not found.
+ */
+function lookupFunctionImport(functionImports, name) {
+ return find(functionImports, function (functionImport) {
+ return functionImport.name === name;
+ });
+}
+
+/** Looks up the target entity type for a navigation property.
+ * @param {Object} navigationProperty -
+ * @param {Object} metadata -
+ * @returns {String} The entity type name for the specified property, null if not found.
+ */
+function lookupNavigationPropertyType(navigationProperty, metadata) {
+
+ var result = null;
+ if (navigationProperty) {
+ var rel = navigationProperty.relationship;
+ var association = forEachSchema(metadata, function (schema) {
+ // The name should be the namespace qualified name in 'ns'.'type' format.
+ var nameOnly = removeNamespace(schema.namespace, rel);
+ var associations = schema.association;
+ if (nameOnly && associations) {
+ var i, len;
+ for (i = 0, len = associations.length; i < len; i++) {
+ if (associations[i].name === nameOnly) {
+ return associations[i];
+ }
+ }
+ }
+ return null;
+ });
+
+ if (association) {
+ var end = association.end[0];
+ if (end.role !== navigationProperty.toRole) {
+ end = association.end[1];
+ // For metadata to be valid, end.role === navigationProperty.toRole now.
+ }
+ result = end.type;
+ }
+ }
+ return result;
+}
+
+/** Looks up the target entityset name for a navigation property.
+ * @param {Object} navigationProperty -
+ * @param {Object} metadata -
+ * @returns {String} The entityset name for the specified property, null if not found.
+ */
+function lookupNavigationPropertyEntitySet(navigationProperty, sourceEntitySetName, metadata) {
+
+ if (navigationProperty) {
+ var rel = navigationProperty.relationship;
+ var associationSet = forEachSchema(metadata, function (schema) {
+ var containers = schema.entityContainer;
+ for (var i = 0; i < containers.length; i++) {
+ var associationSets = containers[i].associationSet;
+ if (associationSets) {
+ for (var j = 0; j < associationSets.length; j++) {
+ if (associationSets[j].association == rel) {
+ return associationSets[j];
+ }
+ }
+ }
+ }
+ return null;
+ });
+ if (associationSet && associationSet.end[0] && associationSet.end[1]) {
+ return (associationSet.end[0].entitySet == sourceEntitySetName) ? associationSet.end[1].entitySet : associationSet.end[0].entitySet;
+ }
+ }
+ return null;
+}
+
+/** Gets the entitySet info, container name and functionImports for an entitySet
+ * @param {Object} navigationProperty -
+ * @param {Object} metadata -
+ * @returns {Object} The info about the entitySet.
+ */
+function getEntitySetInfo(entitySetName, metadata) {
+
+ var info = forEachSchema(metadata, function (schema) {
+ var container = schema.entityContainer;
+ var entitySets = container.entitySet;
+ if (entitySets) {
+ for (var j = 0; j < entitySets.length; j++) {
+ if (entitySets[j].name == entitySetName) {
+ return { entitySet: entitySets[j], containerName: container.name, functionImport: container.functionImport };
+ }
+ }
+ }
+ return null;
+ });
+
+ return info;
+}
+
+/** Given an expected namespace prefix, removes it from a full name.
+ * @param {String} ns - Expected namespace.
+ * @param {String} fullName - Full name in 'ns'.'name' form.
+ * @returns {String} The local name, null if it isn't found in the expected namespace.
+ */
+function removeNamespace(ns, fullName) {
+
+ if (fullName.indexOf(ns) === 0 && fullName.charAt(ns.length) === ".") {
+ return fullName.substr(ns.length + 1);
+ }
+
+ return null;
+}
+
+/** Looks up a schema object by name.
+ * @param {String} name - Name (assigned).
+ * @param schema - Schema object as per EDM metadata.
+ * @param {String} kind - Kind of object to look for as per EDM metadata.
+ * @returns An entity type description if the name is found; null otherwise.</returns>
+ */
+function lookupInSchema(name, schema, kind) {
+
+ 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;
+}
+
+/** Compares to version strings and returns the higher one.
+ * @param {String} left - Version string in the form "major.minor.rev"
+ * @param {String} right - Version string in the form "major.minor.rev"
+ * @returns {String} The higher version string.
+ */
+function maxVersion(left, right) {
+
+ 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 = {
+ // Headers shared by request and response
+ "content-type": "Content-Type",
+ "content-encoding": "Content-Encoding",
+ "content-length": "Content-Length",
+ "odata-version": "OData-Version",
+
+ // Headers used by request
+ "accept": "Accept",
+ "accept-charset": "Accept-Charset",
+ "if-match": "If-Match",
+ "if-none-match": "If-None-Match",
+ "odata-isolation": "OData-Isolation",
+ "odata-maxversion": "OData-MaxVersion",
+ "prefer": "Prefer",
+ "content-id": "Content-ID",
+ "content-transfer-encoding": "Content-Transfer-Encoding",
+
+ // Headers used by response
+ "etag": "ETag",
+ "location": "Location",
+ "odata-entityid": "OData-EntityId",
+ "preference-applied": "Preference-Applied",
+ "retry-after": "Retry-After"
+};
+
+/** Normalizes headers so they can be found with consistent casing.
+ * @param {Object} headers - Dictionary of name/value pairs.
+ */
+function normalizeHeaders(headers) {
+
+ 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;
+ }
+ }
+}
+
+/** Parses a string into a boolean value.
+ * @param propertyValue - Value to parse.
+ * @returns {Boolean} true if the property value is 'true'; false otherwise.
+ */
+function parseBool(propertyValue) {
+
+ 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+))?(.*)$/;
+
+/** Parses a string into a DateTime value.
+ * @param {String} value - Value to parse.
+ * @param {Boolean} withOffset - Whether offset is expected.
+ * @returns {Date} The parsed value.
+ */
+function parseDateTimeMaybeOffset(value, withOffset, nullOnError) {
+
+ // We cannot parse this in cases of failure to match or if offset information is specified.
+ var parts = parseDateTimeRE.exec(value);
+ var offset = (parts) ? getCanonicalTimezone(parts[8]) : null;
+
+ if (!parts || (!withOffset && offset !== "Z")) {
+ if (nullOnError) {
+ return null;
+ }
+ throw { message: "Invalid date/time value" };
+ }
+
+ // Pre-parse years, account for year '0' being invalid in dateTime.
+ var year = parseInt10(parts[1]);
+ if (year <= 0) {
+ year++;
+ }
+
+ // Pre-parse optional milliseconds, fill in default. Fail if value is too precise.
+ var ms = parts[7];
+ var ns = 0;
+ if (!ms) {
+ ms = 0;
+ } else {
+ if (ms.length > 7) {
+ if (nullOnError) {
+ return null;
+ }
+ throw { message: "Cannot parse date/time value to given precision." };
+ }
+
+ ns = formatNumberWidth(ms.substring(3), 4, true);
+ ms = formatNumberWidth(ms.substring(0, 3), 3, true);
+
+ ms = parseInt10(ms);
+ ns = parseInt10(ns);
+ }
+
+ // Pre-parse other time components and offset them if necessary.
+ var hours = parseInt10(parts[4]);
+ var minutes = parseInt10(parts[5]);
+ var seconds = parseInt10(parts[6]) || 0;
+ if (offset !== "Z") {
+ // The offset is reversed to get back the UTC date, which is
+ // what the API will eventually have.
+ var timezone = parseTimezone(offset);
+ var direction = -(timezone.d);
+ hours += timezone.h * direction;
+ minutes += timezone.m * direction;
+ }
+
+ // Set the date and time separately with setFullYear, so years 0-99 aren't biased like in Date.UTC.
+ var result = new Date();
+ result.setUTCFullYear(
+ year, // Year.
+ parseInt10(parts[2]) - 1, // Month (zero-based for Date.UTC and setFullYear).
+ parseInt10(parts[3]) // Date.
+ );
+ result.setUTCHours(hours, minutes, seconds, ms);
+
+ if (isNaN(result.valueOf())) {
+ if (nullOnError) {
+ return null;
+ }
+ throw { message: "Invalid date/time value" };
+ }
+
+ if (withOffset) {
+ result.__edmType = "Edm.DateTimeOffset";
+ result.__offset = offset;
+ }
+
+ if (ns) {
+ result.__ns = ns;
+ }
+
+ return result;
+}
+
+/** Parses a string into a Date object.
+ * @param {String} propertyValue - Value to parse.
+ * @returns {Date} The parsed with year, month, day set, time values are set to 0
+ */
+function parseDate(propertyValue, nullOnError) {
+ var parts = propertyValue.split('-');
+
+ if (parts.length != 3 && nullOnError) {
+ return null;
+ }
+ return new Date(
+ parseInt10(parts[0]), // Year.
+ parseInt10(parts[1]) - 1, // Month (zero-based for Date.UTC and setFullYear).
+ parseInt10(parts[2],
+ 0,0,0,0) // Date.
+ );
+
+}
+
+var parseTimeOfDayRE = /^(\d+):(\d+)(:(\d+)(.(\d+))?)?$/;
+
+function parseTimeOfDay(propertyValue, nullOnError) {
+ var parts = parseTimeOfDayRE.exec(propertyValue);
+
+
+ return {
+ 'h' :parseInt10(parts[1]),
+ 'm' :parseInt10(parts[2]),
+ 's' :parseInt10(parts[4]),
+ 'ms' :parseInt10(parts[6]),
+ };
+}
+
+/** Parses a string into a DateTimeOffset value.
+ * @param {String} propertyValue - Value to parse.
+ * @returns {Date} 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.
+ */
+function parseDateTimeOffset(propertyValue, nullOnError) {
+
+
+ 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)?)?/;
+
+function isEdmDurationValue(value) {
+ parseTimeRE.test(value);
+}
+
+/** Parses a string in xsd:duration format.
+ * @param {String} duration - Duration value.
+
+ * This method will throw an exception if the input string has a year or a month component.
+
+ * @returns {Object} Object representing the time
+ */
+function parseDuration(duration) {
+
+ 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;
+}
+
+/** Parses a timezone description in (+|-)nn:nn format.
+ * @param {String} timezone - Timezone offset.
+ * @returns {Object} An object with a (d)irection property of 1 for + and -1 for -, offset (h)ours and offset (m)inutes.
+ */
+function parseTimezone(timezone) {
+
+ 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 };
+}
+
+/** Prepares a request object so that it can be sent through the network.
+* @param request - Object that represents the request to be sent.
+* @param handler - Handler for data serialization
+* @param context - Context used for preparing the request
+*/
+function prepareRequest(request, handler, context) {
+
+ // 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["OData-MaxVersion"])) {
+ request.headers["OData-MaxVersion"] = handler.maxDataServiceVersion || "4.0";
+ }
+
+ if (request.async === undefined) {
+ request.async = true;
+ }
+
+}
+
+/** Traverses a tree of objects invoking callback for every value.
+ * @param {Object} item - Object or array to traverse.
+ * @param {Function} callback - Callback function with key and value, similar to JSON.parse reviver.
+ * @returns {Object} The object with traversed properties.
+ Unlike the JSON reviver, this won't delete null members.</remarks>
+*/
+function traverseInternal(item, owner, callback) {
+
+ 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;
+}
+
+/** Traverses a tree of objects invoking callback for every value.
+ * @param {Object} item - Object or array to traverse.
+ * @param {Function} callback - Callback function with key and value, similar to JSON.parse reviver.
+ * @returns {Object} The traversed object.
+ * Unlike the JSON reviver, this won't delete null members.</remarks>
+*/
+function traverse(item, callback) {
+
+ return callback("", traverseInternal(item, "", callback));
+}
+
+exports.dataItemTypeName = dataItemTypeName;
+exports.EDM_BINARY = EDM_BINARY;
+exports.EDM_BOOLEAN = EDM_BOOLEAN;
+exports.EDM_BYTE = EDM_BYTE;
+exports.EDM_DATETIME = EDM_DATETIME;
+exports.EDM_DATETIMEOFFSET = EDM_DATETIMEOFFSET;
+exports.EDM_DECIMAL = EDM_DECIMAL;
+exports.EDM_DOUBLE = EDM_DOUBLE;
+exports.EDM_GEOGRAPHY = EDM_GEOGRAPHY;
+exports.EDM_GEOGRAPHY_POINT = EDM_GEOGRAPHY_POINT;
+exports.EDM_GEOGRAPHY_LINESTRING = EDM_GEOGRAPHY_LINESTRING;
+exports.EDM_GEOGRAPHY_POLYGON = EDM_GEOGRAPHY_POLYGON;
+exports.EDM_GEOGRAPHY_COLLECTION = EDM_GEOGRAPHY_COLLECTION;
+exports.EDM_GEOGRAPHY_MULTIPOLYGON = EDM_GEOGRAPHY_MULTIPOLYGON;
+exports.EDM_GEOGRAPHY_MULTILINESTRING = EDM_GEOGRAPHY_MULTILINESTRING;
+exports.EDM_GEOGRAPHY_MULTIPOINT = EDM_GEOGRAPHY_MULTIPOINT;
+exports.EDM_GEOMETRY = EDM_GEOMETRY;
+exports.EDM_GEOMETRY_POINT = EDM_GEOMETRY_POINT;
+exports.EDM_GEOMETRY_LINESTRING = EDM_GEOMETRY_LINESTRING;
+exports.EDM_GEOMETRY_POLYGON = EDM_GEOMETRY_POLYGON;
+exports.EDM_GEOMETRY_COLLECTION = EDM_GEOMETRY_COLLECTION;
+exports.EDM_GEOMETRY_MULTIPOLYGON = EDM_GEOMETRY_MULTIPOLYGON;
+exports.EDM_GEOMETRY_MULTILINESTRING = EDM_GEOMETRY_MULTILINESTRING;
+exports.EDM_GEOMETRY_MULTIPOINT = EDM_GEOMETRY_MULTIPOINT;
+exports.EDM_GUID = EDM_GUID;
+exports.EDM_INT16 = EDM_INT16;
+exports.EDM_INT32 = EDM_INT32;
+exports.EDM_INT64 = EDM_INT64;
+exports.EDM_SBYTE = EDM_SBYTE;
+exports.EDM_SINGLE = EDM_SINGLE;
+exports.EDM_STRING = EDM_STRING;
+exports.EDM_TIME = EDM_TIME;
+exports.GEOJSON_POINT = GEOJSON_POINT;
+exports.GEOJSON_LINESTRING = GEOJSON_LINESTRING;
+exports.GEOJSON_POLYGON = GEOJSON_POLYGON;
+exports.GEOJSON_MULTIPOINT = GEOJSON_MULTIPOINT;
+exports.GEOJSON_MULTILINESTRING = GEOJSON_MULTILINESTRING;
+exports.GEOJSON_MULTIPOLYGON = GEOJSON_MULTIPOLYGON;
+exports.GEOJSON_GEOMETRYCOLLECTION = GEOJSON_GEOMETRYCOLLECTION;
+exports.forEachSchema = forEachSchema;
+exports.formatDateTimeOffset = formatDateTimeOffset;
+exports.formatDateTimeOffsetJSON = formatDateTimeOffsetJSON;
+exports.formatDuration = formatDuration;
+exports.formatNumberWidth = formatNumberWidth;
+exports.getCanonicalTimezone = getCanonicalTimezone;
+exports.getCollectionType = getCollectionType;
+exports.invokeRequest = invokeRequest;
+exports.isBatch = isBatch;
+exports.isCollection = isCollection;
+exports.isCollectionType = isCollectionType;
+exports.isComplex = isComplex;
+exports.isDateTimeOffset = isDateTimeOffset;
+exports.isDeferred = isDeferred;
+exports.isEntry = isEntry;
+exports.isFeed = isFeed;
+exports.isGeographyEdmType = isGeographyEdmType;
+exports.isGeometryEdmType = isGeometryEdmType;
+exports.isNamedStream = isNamedStream;
+exports.isPrimitive = isPrimitive;
+exports.isPrimitiveEdmType = isPrimitiveEdmType;
+exports.lookupComplexType = lookupComplexType;
+exports.lookupDefaultEntityContainer = lookupDefaultEntityContainer;
+exports.lookupEntityContainer = lookupEntityContainer;
+exports.lookupEntitySet = lookupEntitySet;
+exports.lookupSingleton = lookupSingleton;
+exports.lookupEntityType = lookupEntityType;
+exports.lookupFunctionImport = lookupFunctionImport;
+exports.lookupNavigationPropertyType = lookupNavigationPropertyType;
+exports.lookupNavigationPropertyEntitySet = lookupNavigationPropertyEntitySet;
+exports.lookupInSchema = lookupInSchema;
+exports.lookupProperty = lookupProperty;
+exports.lookupInMetadata = lookupInMetadata;
+exports.getEntitySetInfo = getEntitySetInfo;
+exports.maxVersion = maxVersion;
+exports.navigationPropertyKind = navigationPropertyKind;
+exports.normalizeHeaders = normalizeHeaders;
+exports.parseBool = parseBool;
+
+
+exports.parseDate = parseDate;
+exports.parseDateTimeOffset = parseDateTimeOffset;
+exports.parseDuration = parseDuration;
+exports.parseTimeOfDay = parseTimeOfDay;
+
+exports.parseInt10 = parseInt10;
+exports.prepareRequest = prepareRequest;
+exports.removeNamespace = removeNamespace;
+exports.traverse = traverse;
+
http://git-wip-us.apache.org/repos/asf/olingo-odata4-js/blob/d5ec5557/odatajs/src/lib/store.js
----------------------------------------------------------------------
diff --git a/odatajs/src/lib/store.js b/odatajs/src/lib/store.js
new file mode 100644
index 0000000..5989253
--- /dev/null
+++ b/odatajs/src/lib/store.js
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+ /** @module store */
+
+
+
+
+
+exports.defaultStoreMechanism = "best";
+
+/** Creates a new store object.
+ * @param {String} name - Store name.
+ * @param {String} [mechanism] -
+ * @returns {Object} Store object.
+*/
+exports.createStore = function (name, mechanism) {
+
+
+ if (!mechanism) {
+ mechanism = exports.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 };
+};
+
+exports.mechanisms = mechanisms;
+
+
+exports.DomStore = DomStore = require('./store/dom.js');
+exports.IndexedDBStore = IndexedDBStore = require('./store/indexeddb.js');
+exports.MemoryStore = MemoryStore = require('./store/memory.js');
+
+var mechanisms = {
+ indexeddb: IndexedDBStore,
+ dom: DomStore,
+ memory: MemoryStore
+};
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/olingo-odata4-js/blob/d5ec5557/odatajs/src/lib/store/dom.js
----------------------------------------------------------------------
diff --git a/odatajs/src/lib/store/dom.js b/odatajs/src/lib/store/dom.js
new file mode 100644
index 0000000..740b1ba
--- /dev/null
+++ b/odatajs/src/lib/store/dom.js
@@ -0,0 +1,330 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/** @module store/dom */
+
+
+
+var utils = require('./../datajs.js').utils;
+
+// Imports.
+var throwErrorCallback = utils.throwErrorCallback;
+var delay = utils.delay;
+
+var localStorage = null;
+
+/** This method is used to override the Date.toJSON method and is called only by
+ * JSON.stringify. It should never be called directly.
+ * @summary Converts a Date object into an object representation friendly to JSON serialization.
+ * @returns {Object} Object that represents the Date.
+ */
+function domStoreDateToJSON() {
+ 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;
+}
+
+/** This method is used during JSON parsing and invoked only by the reviver function.
+ * It should never be called directly.
+ * @summary JSON reviver function for converting an object representing a Date in a JSON stream to a Date object
+ * @param Object - Object to convert.
+ * @returns {Date} Date object.
+ */
+function domStoreJSONToDate(_, value) {
+ 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;
+}
+
+/** Qualifies the key with the name of the store.
+ * @param {Object} store - Store object whose name will be used for qualifying the key.
+ * @param {String} key - Key string.
+ * @returns {String} Fully qualified key string.
+ */
+function qualifyDomStoreKey(store, key) {
+ return store.name + "#!#" + key;
+}
+
+/** Gets the key part of a fully qualified key string.
+ * @param {Object} store - Store object whose name will be used for qualifying the key.
+ * @param {String} key - Fully qualified key string.
+ * @returns {String} Key part string
+ */
+function unqualifyDomStoreKey(store, key) {
+ return key.replace(store.name + "#!#", "");
+}
+
+/** Constructor for store objects that use DOM storage as the underlying mechanism.
+ * @class DomStore
+ * @constructor
+ * @param {String} name - Store name.
+ */
+function DomStore(name) {
+ this.name = name;
+}
+
+/** Creates a store object that uses DOM Storage as its underlying mechanism.
+ * @method module:store/dom~DomStore.create
+ * @param {String} name - Store name.
+ * @returns {Object} Store object.
+ */
+DomStore.create = function (name) {
+
+ if (DomStore.isSupported()) {
+ localStorage = localStorage || window.localStorage;
+ return new DomStore(name);
+ }
+
+ throw { message: "Web Storage not supported by the browser" };
+};
+
+/** Checks whether the underlying mechanism for this kind of store objects is supported by the browser.
+ * @method DomStore.isSupported
+ * @returns {Boolean} - True if the mechanism is supported by the browser; otherwise false.
+*/
+DomStore.isSupported = function () {
+ return !!window.localStorage;
+};
+
+/** Adds a new value identified by a key to the store.
+ * @method module:store/dom~DomStore#add
+ * @param {String} key - Key string.
+ * @param value - Value that is going to be added to the store.
+ * @param {Funcktion} success - Callback for a successful add operation.</param>
+ * @param {Funcktion} [error] - Callback for handling errors. If not specified then store.defaultError is invoked.</param>
+ * This method errors out if the store already contains the specified key.
+ */
+DomStore.prototype.add = function (key, value, success, error) {
+ 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);
+};
+
+/** 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.
+ * @summary Adds or updates a value identified by a key to the store.
+ * @method module:store/dom~DomStore#addOrUpdate
+ * @param {String} key - Key string.
+ * @param value - Value that is going to be added or updated to the store.
+ * @param {Function} success - Callback for a successful add or update operation.</param>
+ * @param {Function} [error] - Callback for handling errors. If not specified then store.defaultError is invoked.</param>
+ */
+DomStore.prototype.addOrUpdate = function (key, value, success, error) {
+ 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;
+ }
+ }
+};
+
+/** In case of an error, this method will not restore any keys that might have been deleted at that point.
+ * @summary Removes all the data associated with this store object.
+ * @method module:store/dom~DomStore#clear
+ * @param {Function} success - Callback for a successful clear operation.</param>
+ * @param {Function} [error] - Callback for handling errors. If not specified then store.defaultError is invoked.</param>
+ */
+DomStore.prototype.clear = function (success, error) {
+
+ 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);
+ }
+};
+
+/** This function does nothing in DomStore as it does not have a connection model
+ * @method module:store/dom~DomStore#close
+ */
+DomStore.prototype.close = function () {
+};
+
+/** Checks whether a key exists in the store.
+ * @method module:store/dom~DomStore#contains
+ * @param {String} key - Key string.
+ * @param {Function} success - Callback indicating whether the store contains the key or not.</param>
+ * @param {Function} [error] - Callback for handling errors. If not specified then store.defaultError is invoked.</param>
+*/
+DomStore.prototype.contains = function (key, success, error) {
+ 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;
+
+/** Gets all the keys that exist in the store.
+ * @method module:store/dom~DomStore#getAllKeys
+ * @param {Function} success - Callback for a successful get operation.</param>
+ * @param {Function} [error] - Callback for handling errors. If not specified then store.defaultError is invoked.</param>
+ */
+DomStore.prototype.getAllKeys = function (success, error) {
+
+ 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";
+
+/** Reads the value associated to a key in the store.
+ * @method module:store/dom~DomStore#read
+ * @param {String} key - Key string.
+ * @param {Function} success - Callback for a successful reads operation.
+ * @param {Function} [error] - Callback for handling errors. If not specified then store.defaultError is invoked.
+ */
+DomStore.prototype.read = function (key, success, error) {
+
+ 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);
+ }
+ }
+};
+
+/** Removes a key and its value from the store.
+ * @method module:store/dom~DomStore#remove
+ * @param {String} key - Key string.
+ * @param {Funtion} success - Callback for a successful remove operation.</param>
+ * @param {Funtion} [error] - Callback for handling errors. If not specified then store.defaultError is invoked.</param>
+ */
+DomStore.prototype.remove = function (key, success, error) {
+ 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);
+ }
+ }
+};
+
+/** Updates the value associated to a key in the store.
+ * @method module:store/dom~DomStore#update
+ * @param {String} key - Key string.
+ * @param value - New value.
+ * @param {Function} success - Callback for a successful update operation.
+ * @param {Function} [error] - 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.
+ */
+DomStore.prototype.update = function (key, value, success, error) {
+ 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);
+};
+
+module.exports = DomStore;
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/olingo-odata4-js/blob/d5ec5557/odatajs/src/lib/store/indexeddb.js
----------------------------------------------------------------------
diff --git a/odatajs/src/lib/store/indexeddb.js b/odatajs/src/lib/store/indexeddb.js
new file mode 100644
index 0000000..397354a
--- /dev/null
+++ b/odatajs/src/lib/store/indexeddb.js
@@ -0,0 +1,445 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/** @module store/indexeddb */
+var utils = require('./../datajs.js').utils;
+
+// Imports.
+var throwErrorCallback = utils.throwErrorCallback;
+var delay = utils.delay;
+
+
+var indexedDB = utils.inBrowser() ? window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB || window.indexedDB : undefined;
+var IDBKeyRange = utils.inBrowser() ? window.IDBKeyRange || window.webkitIDBKeyRange : undefined;
+var IDBTransaction = utils.inBrowser() ? window.IDBTransaction || window.webkitIDBTransaction || {} : {} ;
+
+var IDBT_READ_ONLY = IDBTransaction.READ_ONLY || "readonly";
+var IDBT_READ_WRITE = IDBTransaction.READ_WRITE || "readwrite";
+
+/** Returns either a specific error handler or the default error handler
+ * @param {Function} error - The specific error handler
+ * @param {Function} defaultError - The default error handler
+ * @returns {Function} The error callback
+ */
+function getError(error, defaultError) {
+
+ 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 });
+ };
+}
+
+/** Opens the store object's indexed db database.
+ * @param {IndexedDBStore} store - The store object
+ * @param {Function} success - The success callback
+ * @param {Function} error - The error callback
+ */
+function openStoreDb(store, success, error) {
+
+ 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);
+ };
+}
+
+/** Opens a new transaction to the store
+ * @param {IndexedDBStore} store - The store object
+ * @param {Short} mode - The read/write mode of the transaction (constants from IDBTransaction)
+ * @param {Function} success - The success callback
+ * @param {Function} error - The error callback
+ */
+function openTransaction(store, mode, success, error) {
+
+ 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);
+}
+
+/** Creates a new IndexedDBStore.
+ * @class IndexedDBStore
+ * @constructor
+ * @param {String} name - The name of the store.
+ * @returns {Object} The new IndexedDBStore.
+ */
+function IndexedDBStore(name) {
+ this.name = name;
+}
+
+/** Creates a new IndexedDBStore.
+ * @method module:store/indexeddb~IndexedDBStore.create
+ * @param {String} name - The name of the store.
+ * @returns {Object} The new IndexedDBStore.
+ */
+IndexedDBStore.create = function (name) {
+ if (IndexedDBStore.isSupported()) {
+ return new IndexedDBStore(name);
+ }
+
+ throw { message: "IndexedDB is not supported on this browser" };
+};
+
+/** Returns whether IndexedDB is supported.
+ * @method module:store/indexeddb~IndexedDBStore.isSupported
+ * @returns {Boolean} True if IndexedDB is supported, false otherwise.
+ */
+IndexedDBStore.isSupported = function () {
+ return !!indexedDB;
+};
+
+/** Adds a key/value pair to the store
+ * @method module:store/indexeddb~IndexedDBStore#add
+ * @param {String} key - The key
+ * @param {Object} value - The value
+ * @param {Function} success - The success callback
+ * @param {Function} error - The error callback
+*/
+IndexedDBStore.prototype.add = function (key, value, success, error) {
+ 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);
+};
+
+/** Adds or updates a key/value pair in the store
+ * @method module:store/indexeddb~IndexedDBStore#addOrUpdate
+ * @param {String} key - The key
+ * @param {Object} value - The value
+ * @param {Function} success - The success callback
+ * @param {Function} error - The error callback
+ */
+IndexedDBStore.prototype.addOrUpdate = function (key, value, success, error) {
+ 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);
+};
+
+/** Clears the store
+ * @method module:store/indexeddb~IndexedDBStore#clear
+ * @param {Function} success - The success callback
+ * @param {Function} error - The error callback
+ */
+IndexedDBStore.prototype.clear = function (success, error) {
+ 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);
+};
+/** Closes the connection to the database
+ * @method module:store/indexeddb~IndexedDBStore#close
+*/
+IndexedDBStore.prototype.close = function () {
+
+ if (this.db) {
+ this.db.close();
+ this.db = null;
+ }
+};
+
+/** Returns whether the store contains a key
+ * @method module:store/indexeddb~IndexedDBStore#contains
+ * @param {String} key - The key
+ * @param {Function} success - The success callback
+ * @param {Function} error - The error callback
+ */
+IndexedDBStore.prototype.contains = function (key, success, error) {
+ 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;
+
+/** Gets all the keys from the store
+ * @method module:store/indexeddb~IndexedDBStore#getAllKeys
+ * @param {Function} success - The success callback
+ * @param {Function} error - The error callback
+ */
+IndexedDBStore.prototype.getAllKeys = function (success, error) {
+ 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";
+
+/** Reads the value for the specified key
+ * @method module:store/indexeddb~IndexedDBStore#read
+ * @param {String} key - The key
+ * @param {Function} success - The success callback
+ * @param {Function} error - The error callback
+ * If the key does not exist, the success handler will be called with value = undefined
+ */
+IndexedDBStore.prototype.read = function (key, success, error) {
+ 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);
+};
+
+/** Removes the specified key from the store
+ * @method module:store/indexeddb~IndexedDBStore#remove
+ * @param {String} key - The key
+ * @param {Function} success - The success callback
+ * @param {Function} error - The error callback
+ */
+IndexedDBStore.prototype.remove = function (key, success, error) {
+
+ 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);
+};
+
+/** Updates a key/value pair in the store
+ * @method module:store/indexeddb~IndexedDBStore#update
+ * @param {String} key - The key
+ * @param {Object} value - The value
+ * @param {Function} success - The success callback
+ * @param {Function} error - The error callback
+ */
+IndexedDBStore.prototype.update = function (key, value, success, error) {
+ 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);
+};
+
+
+module.exports = IndexedDBStore;
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/olingo-odata4-js/blob/d5ec5557/odatajs/src/lib/store/memory.js
----------------------------------------------------------------------
diff --git a/odatajs/src/lib/store/memory.js b/odatajs/src/lib/store/memory.js
new file mode 100644
index 0000000..7203c46
--- /dev/null
+++ b/odatajs/src/lib/store/memory.js
@@ -0,0 +1,246 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/** @module store/memory */
+
+
+var utils = require('./../datajs.js').utils;
+
+// Imports.
+var throwErrorCallback = utils.throwErrorCallback;
+var delay = utils.delay;
+
+/** Constructor for store objects that use a sorted array as the underlying mechanism.
+ * @class MemoryStore
+ * @constructor
+ * @param {String} name - Store name.
+ */
+function MemoryStore(name) {
+
+ var holes = [];
+ var items = [];
+ var keys = {};
+
+ this.name = name;
+
+ var getErrorCallback = function (error) {
+ return error || this.defaultError;
+ };
+
+ /** Validates that the specified key is not undefined, not null, and not an array
+ * @param key - Key value.
+ * @param {Function} error - Error callback.
+ * @returns {Boolean} True if the key is valid. False if the key is invalid and the error callback has been queued for execution.
+ */
+ function validateKeyInput(key, error) {
+
+ 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 method errors out if the store already contains the specified key.
+ * @summery Adds a new value identified by a key to the store.
+ * @method module:store/memory~MemoryStore#add
+ * @param {String} key - Key string.
+ * @param value - Value that is going to be added to the store.
+ * @param {Function} success - Callback for a successful add operation.</param>
+ * @param {Function} error - Callback for handling errors. If not specified then store.defaultError is invoked.</param>
+ */
+ this.add = function (key, value, success, error) {
+ 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 method will overwrite the key's current value if it already exists in the store; otherwise it simply adds the new key and value.
+ * @summary Adds or updates a value identified by a key to the store.
+ * @method module:store/memory~MemoryStore#addOrUpdate
+ * @param {String} key - Key string.
+ * @param value - Value that is going to be added or updated to the store.
+ * @param {Function} success - Callback for a successful add or update operation.</param>
+ * @param {Function} [error] - Callback for handling errors. If not specified then store.defaultError is invoked.</param>
+ */
+ this.addOrUpdate = function (key, value, success, error) {
+
+ 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);
+ }
+ };
+
+ /** Removes all the data associated with this store object.
+ * @method module:store/memory~MemoryStore#clear
+ * @param {Function} success>Callback for a successful clear operation.
+ */
+ this.clear = function (success) {
+ items = [];
+ keys = {};
+ holes = [];
+ delay(success);
+ };
+
+ /** Checks whether a key exists in the store.
+ * @method module:store/memory~MemoryStore#contains
+ * @param {String} key - Key string.
+ * @param {Funktion} success - Callback indicating whether the store contains the key or not.</param>
+ */
+ this.contains = function (key, success) {
+ var contained = keys.hasOwnProperty(key);
+ delay(success, contained);
+ };
+
+ /** Gets all the keys that exist in the store.
+ * @method module:store/memory~MemoryStore#getAllKeys
+ * @param {Function} success - Callback for a successful get operation.</param>
+ */
+ this.getAllKeys = function (success) {
+
+ var results = [];
+ for (var name in keys) {
+ results.push(name);
+ }
+ delay(success, results);
+ };
+
+ /** Reads the value associated to a key in the store.
+ * @method module:store/memory~MemoryStore#read
+ * @param {String} key - Key string.
+ * @param {Function} Function - Callback for a successful reads operation.</param>
+ * @param {Function{}Function - Callback for handling errors. If not specified then store.defaultError is invoked.</param>
+ */
+ this.read = function (key, success, error) {
+ error = getErrorCallback(error);
+
+ if (validateKeyInput(key, error)) {
+ var index = keys[key];
+ delay(success, key, items[index]);
+ }
+ };
+
+ /** Removes a key and its value from the store.
+ * @method module:store/memory~MemoryStore#remove
+ * @param {String} key - Key string.
+ * @param {Function} success - Callback for a successful remove operation.</param>
+ * @param {Function} [error] - Callback for handling errors. If not specified then store.defaultError is invoked.</param>
+ */
+ this.remove = function (key, success, error) {
+ 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);
+ }
+ };
+
+ /** Updates the value associated to a key in the store.
+ * @method module:store/memory~MemoryStore#update
+ * @param {String} key - Key string.
+ * @param value - New value.
+ * @param {Function} success - Callback for a successful update operation.</param>
+ * @param {Function} [error] - Callback for handling errors. If not specified then store.defaultError is invoked.</param>
+ * This method errors out if the specified key is not found in the store.
+ */
+ this.update = function (key, value, success, error) {
+ 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 });
+ }
+ }
+ };
+}
+
+/** Creates a store object that uses memory storage as its underlying mechanism.
+ * @method MemoryStore.create
+ * @param {String} name - Store name.
+ * @returns {Object} Store object.
+ */
+MemoryStore.create = function (name) {
+ return new MemoryStore(name);
+};
+
+/** Checks whether the underlying mechanism for this kind of store objects is supported by the browser.
+ * @method MemoryStore.isSupported
+ * @returns {Boolean} True if the mechanism is supported by the browser; otherwise false.
+ */
+MemoryStore.isSupported = function () {
+ return true;
+};
+
+/** This function does nothing in MemoryStore as it does not have a connection model.
+*/
+MemoryStore.prototype.close = function () {
+};
+
+MemoryStore.prototype.defaultError = throwErrorCallback;
+
+/** Identifies the underlying mechanism used by the store.
+*/
+MemoryStore.prototype.mechanism = "memory";
+
+
+/** MemoryStore (see {@link MemoryStore}) */
+module.exports = MemoryStore;
\ No newline at end of file