http://git-wip-us.apache.org/repos/asf/olingo-odata3-js/blob/da25dc30/JSLib/src/odata-utils.js
----------------------------------------------------------------------
diff --git a/JSLib/src/odata-utils.js b/JSLib/src/odata-utils.js
new file mode 100644
index 0000000..ad2cf00
--- /dev/null
+++ b/JSLib/src/odata-utils.js
@@ -0,0 +1,1117 @@
+// Copyright (c) Microsoft Open Technologies, Inc.  All rights reserved.
+// Permission is hereby granted, free of charge, to any person obtaining a 
copy of this software and associated documentation 
+// files (the "Software"), to deal  in the Software without restriction, 
including without limitation the rights  to use, copy,
+// modify, merge, publish, distribute, sublicense, and/or sell copies of the 
Software, and to permit persons to whom the 
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in 
all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR  
IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// WARRANTIES OF MERCHANTABILITY,  FITNESS FOR A PARTICULAR PURPOSE AND 
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
DEALINGS IN THE SOFTWARE.
+
+// odata-utils.js
+
+(function (window, undefined) {
+
+    var datajs = window.datajs || {};
+    var odata = window.OData || {};
+
+    // Imports
+    var assigned = datajs.assigned;
+    var contains = datajs.contains;
+    var find = datajs.find;
+    var isArray = datajs.isArray;
+    var isDate = datajs.isDate;
+    var isObject = datajs.isObject;
+    var parseInt10 = datajs.parseInt10;
+
+    // CONTENT START
+
+    var dataItemTypeName = function (value, metadata) {
+        /// <summary>Gets the type name of a data item value that belongs to a 
feed, an entry, a complex type property, or a collection property.</summary>
+        /// <param name="value">Value of the data item from which the type 
name is going to be retrieved.</param>
+        /// <param name="metadata" type="object" optional="true">Object 
containing metadata about the data tiem.</param>
+        /// <remarks>
+        ///    This function will first try to get the type name from the data 
item's value itself if it is an object with a __metadata property; otherwise
+        ///    it will try to recover it from the metadata.  If both attempts 
fail, it will return null.
+        /// </remarks>
+        /// <returns type="String">Data item type name; null if the type name 
cannot be found within the value or the metadata</returns>
+
+        var valueTypeName = ((value && value.__metadata) || {}).type;
+        return valueTypeName || (metadata ? metadata.type : null);
+    };
+
+    var EDM = "Edm.";
+    var EDM_BINARY = EDM + "Binary";
+    var EDM_BOOLEAN = EDM + "Boolean";
+    var EDM_BYTE = EDM + "Byte";
+    var EDM_DATETIME = EDM + "DateTime";
+    var EDM_DATETIMEOFFSET = EDM + "DateTimeOffset";
+    var EDM_DECIMAL = EDM + "Decimal";
+    var EDM_DOUBLE = EDM + "Double";
+    var EDM_GUID = EDM + "Guid";
+    var EDM_INT16 = EDM + "Int16";
+    var EDM_INT32 = EDM + "Int32";
+    var EDM_INT64 = EDM + "Int64";
+    var EDM_SBYTE = EDM + "SByte";
+    var EDM_SINGLE = EDM + "Single";
+    var EDM_STRING = EDM + "String";
+    var EDM_TIME = EDM + "Time";
+
+    var EDM_GEOGRAPHY = EDM + "Geography";
+    var EDM_GEOGRAPHY_POINT = EDM_GEOGRAPHY + "Point";
+    var EDM_GEOGRAPHY_LINESTRING = EDM_GEOGRAPHY + "LineString";
+    var EDM_GEOGRAPHY_POLYGON = EDM_GEOGRAPHY + "Polygon";
+    var EDM_GEOGRAPHY_COLLECTION = EDM_GEOGRAPHY + "Collection";
+    var EDM_GEOGRAPHY_MULTIPOLYGON = EDM_GEOGRAPHY + "MultiPolygon";
+    var EDM_GEOGRAPHY_MULTILINESTRING = EDM_GEOGRAPHY + "MultiLineString";
+    var EDM_GEOGRAPHY_MULTIPOINT = EDM_GEOGRAPHY + "MultiPoint";
+
+    var EDM_GEOMETRY = EDM + "Geometry";
+    var EDM_GEOMETRY_POINT = EDM_GEOMETRY + "Point";
+    var EDM_GEOMETRY_LINESTRING = EDM_GEOMETRY + "LineString";
+    var EDM_GEOMETRY_POLYGON = EDM_GEOMETRY + "Polygon";
+    var EDM_GEOMETRY_COLLECTION = EDM_GEOMETRY + "Collection";
+    var EDM_GEOMETRY_MULTIPOLYGON = EDM_GEOMETRY + "MultiPolygon";
+    var EDM_GEOMETRY_MULTILINESTRING = EDM_GEOMETRY + "MultiLineString";
+    var EDM_GEOMETRY_MULTIPOINT = EDM_GEOMETRY + "MultiPoint";
+
+    var GEOJSON_POINT = "Point";
+    var GEOJSON_LINESTRING = "LineString";
+    var GEOJSON_POLYGON = "Polygon";
+    var GEOJSON_MULTIPOINT = "MultiPoint";
+    var GEOJSON_MULTILINESTRING = "MultiLineString";
+    var GEOJSON_MULTIPOLYGON = "MultiPolygon";
+    var GEOJSON_GEOMETRYCOLLECTION = "GeometryCollection";
+
+    var primitiveEdmTypes = [
+        EDM_STRING,
+        EDM_INT32,
+        EDM_INT64,
+        EDM_BOOLEAN,
+        EDM_DOUBLE,
+        EDM_SINGLE,
+        EDM_DATETIME,
+        EDM_DATETIMEOFFSET,
+        EDM_TIME,
+        EDM_DECIMAL,
+        EDM_GUID,
+        EDM_BYTE,
+        EDM_INT16,
+        EDM_SBYTE,
+        EDM_BINARY
+    ];
+
+    var geometryEdmTypes = [
+        EDM_GEOMETRY,
+        EDM_GEOMETRY_POINT,
+        EDM_GEOMETRY_LINESTRING,
+        EDM_GEOMETRY_POLYGON,
+        EDM_GEOMETRY_COLLECTION,
+        EDM_GEOMETRY_MULTIPOLYGON,
+        EDM_GEOMETRY_MULTILINESTRING,
+        EDM_GEOMETRY_MULTIPOINT
+    ];
+
+    var geographyEdmTypes = [
+        EDM_GEOGRAPHY,
+        EDM_GEOGRAPHY_POINT,
+        EDM_GEOGRAPHY_LINESTRING,
+        EDM_GEOGRAPHY_POLYGON,
+        EDM_GEOGRAPHY_COLLECTION,
+        EDM_GEOGRAPHY_MULTIPOLYGON,
+        EDM_GEOGRAPHY_MULTILINESTRING,
+        EDM_GEOGRAPHY_MULTIPOINT
+    ];
+
+    var forEachSchema = function (metadata, callback) {
+        /// <summary>Invokes a function once per schema in metadata.</summary>
+        /// <param name="metadata">Metadata store; one of edmx, schema, or an 
array of any of them.</param>
+        /// <param name="callback" type="Function">Callback function to invoke 
once per schema.</param>
+        /// <returns>
+        /// The first truthy value to be returned from the callback; null or 
the last falsy value otherwise.
+        /// </returns>
+
+        if (!metadata) {
+            return null;
+        }
+
+        if (isArray(metadata)) {
+            var i, len, result;
+            for (i = 0, len = metadata.length; i < len; i++) {
+                result = forEachSchema(metadata[i], callback);
+                if (result) {
+                    return result;
+                }
+            }
+
+            return null;
+        } else {
+            if (metadata.dataServices) {
+                return forEachSchema(metadata.dataServices.schema, callback);
+            }
+
+            return callback(metadata);
+        }
+    };
+
+    var formatMilliseconds = function (ms, ns) {
+        /// <summary>Formats a millisecond and a nanosecond value into a 
single string.</summary>
+        /// <param name="ms" type="Number" mayBeNull="false">Number of 
milliseconds to format.</param>
+        /// <param name="ns" type="Number" mayBeNull="false">Number of 
nanoseconds to format.</param>
+        /// <returns type="String">Formatted text.</returns>
+        /// <remarks>If the value is already as string it's returned 
as-is.</remarks>
+
+        // Avoid generating milliseconds if not necessary.
+        if (ms === 0) {
+            ms = "";
+        } else {
+            ms = "." + formatNumberWidth(ms.toString(), 3);
+        }
+        if (ns > 0) {
+            if (ms === "") {
+                ms = ".000";
+            }
+            ms += formatNumberWidth(ns.toString(), 4);
+        }
+        return ms;
+    };
+
+    var formatDateTimeOffset = function (value) {
+        /// <summary>Formats a DateTime or DateTimeOffset value a 
string.</summary>
+        /// <param name="value" type="Date" mayBeNull="false">Value to 
format.</param>
+        /// <returns type="String">Formatted text.</returns>
+        /// <remarks>If the value is already as string it's returned 
as-is.</remarks>
+
+        if (typeof value === "string") {
+            return value;
+        }
+
+        var hasOffset = isDateTimeOffset(value);
+        var offset = getCanonicalTimezone(value.__offset);
+        if (hasOffset && offset !== "Z") {
+            // We're about to change the value, so make a copy.
+            value = new Date(value.valueOf());
+
+            var timezone = parseTimezone(offset);
+            var hours = value.getUTCHours() + (timezone.d * timezone.h);
+            var minutes = value.getUTCMinutes() + (timezone.d * timezone.m);
+
+            value.setUTCHours(hours, minutes);
+        } else if (!hasOffset) {
+            // Don't suffix a 'Z' for Edm.DateTime values.
+            offset = "";
+        }
+
+        var year = value.getUTCFullYear();
+        var month = value.getUTCMonth() + 1;
+        var sign = "";
+        if (year <= 0) {
+            year = -(year - 1);
+            sign = "-";
+        }
+
+        var ms = formatMilliseconds(value.getUTCMilliseconds(), value.__ns);
+
+        return sign +
+            formatNumberWidth(year, 4) + "-" +
+            formatNumberWidth(month, 2) + "-" +
+            formatNumberWidth(value.getUTCDate(), 2) + "T" +
+            formatNumberWidth(value.getUTCHours(), 2) + ":" +
+            formatNumberWidth(value.getUTCMinutes(), 2) + ":" +
+            formatNumberWidth(value.getUTCSeconds(), 2) +
+            ms + offset;
+    };
+
+    var formatDuration = function (value) {
+        /// <summary>Converts a duration to a string in xsd:duration 
format.</summary>
+        /// <param name="value" type="Object">Object with ms and __edmType 
properties.</param>
+        /// <returns type="String">String representation of the time object in 
xsd:duration format.</returns>
+
+        var ms = value.ms;
+
+        var sign = "";
+        if (ms < 0) {
+            sign = "-";
+            ms = -ms;
+        }
+
+        var days = Math.floor(ms / 86400000);
+        ms -= 86400000 * days;
+        var hours = Math.floor(ms / 3600000);
+        ms -= 3600000 * hours;
+        var minutes = Math.floor(ms / 60000);
+        ms -= 60000 * minutes;
+        var seconds = Math.floor(ms / 1000);
+        ms -= seconds * 1000;
+
+        return sign + "P" +
+               formatNumberWidth(days, 2) + "DT" +
+               formatNumberWidth(hours, 2) + "H" +
+               formatNumberWidth(minutes, 2) + "M" +
+               formatNumberWidth(seconds, 2) +
+               formatMilliseconds(ms, value.ns) + "S";
+    };
+
+    var formatNumberWidth = function (value, width, append) {
+        /// <summary>Formats the specified value to the given width.</summary>
+        /// <param name="value" type="Number">Number to format 
(non-negative).</param>
+        /// <param name="width" type="Number">Minimum width for number.</param>
+        /// <param name="append" type="Boolean">Flag indicating if the value 
is padded at the beginning (false) or at the end (true).</param>
+        /// <returns type="String">Text representation.</returns>
+        var result = value.toString(10);
+        while (result.length < width) {
+            if (append) {
+                result += "0";
+            } else {
+                result = "0" + result;
+            }
+        }
+
+        return result;
+    };
+
+    var getCanonicalTimezone = function (timezone) {
+        /// <summary>Gets the canonical timezone representation.</summary>
+        /// <param name="timezone" type="String">Timezone 
representation.</param>
+        /// <returns type="String">An 'Z' string if the timezone is absent or 
0; the timezone otherwise.</returns>
+
+        return (!timezone || timezone === "Z" || timezone === "+00:00" || 
timezone === "-00:00") ? "Z" : timezone;
+    };
+
+    var getCollectionType = function (typeName) {
+        /// <summary>Gets the type of a collection type name.</summary>
+        /// <param name="typeName" type="String">Type name of the 
collection.</param>
+        /// <returns type="String">Type of the collection; null if the type 
name is not a collection type.</returns>
+
+        if (typeof typeName === "string") {
+            var end = typeName.indexOf(")", 10);
+            if (typeName.indexOf("Collection(") === 0 && end > 0) {
+                return typeName.substring(11, end);
+            }
+        }
+        return null;
+    };
+
+    var invokeRequest = function (request, success, error, handler, 
httpClient, context) {
+        /// <summary>Sends a request containing OData payload to a 
server.</summary>
+        /// <param name="request">Object that represents the request to be 
sent..</param>
+        /// <param name="success">Callback for a successful read 
operation.</param>
+        /// <param name="error">Callback for handling errors.</param>
+        /// <param name="handler">Handler for data serialization.</param>
+        /// <param name="httpClient">HTTP client layer.</param>
+        /// <param name="context">Context used for processing the 
request</param>
+
+        return httpClient.request(request, function (response) {
+            try {
+                if (response.headers) {
+                    normalizeHeaders(response.headers);
+                }
+
+                if (response.data === undefined && response.statusCode !== 
204) {
+                    handler.read(response, context);
+                }
+            } catch (err) {
+                if (err.request === undefined) {
+                    err.request = request;
+                }
+                if (err.response === undefined) {
+                    err.response = response;
+                }
+                error(err);
+                return;
+            }
+
+            success(response.data, response);
+        }, error);
+    };
+
+    var isBatch = function (value) {
+        /// <summary>Tests whether a value is a batch object in the library's 
internal representation.</summary>
+        /// <param name="value">Value to test.</param>
+        /// <returns type="Boolean">True is the value is a batch object; false 
otherwise.</returns>
+
+        return isComplex(value) && isArray(value.__batchRequests);
+    };
+
+    // Regular expression used for testing and parsing for a collection type.
+    var collectionTypeRE = /Collection\((.*)\)/;
+
+    var isCollection = function (value, typeName) {
+        /// <summary>Tests whether a value is a collection value in the 
library's internal representation.</summary>
+        /// <param name="value">Value to test.</param>
+        /// <param name="typeName" type="Sting">Type name of the value. This 
is used to disambiguate from a collection property value.</param>
+        /// <returns type="Boolean">True is the value is a feed value; false 
otherwise.</returns>
+
+        var colData = value && value.results || value;
+        return !!colData &&
+            (isCollectionType(typeName)) ||
+            (!typeName && isArray(colData) && !isComplex(colData[0]));
+    };
+
+    var isCollectionType = function (typeName) {
+        /// <summary>Checks whether the specified type name is a collection 
type.</summary>
+        /// <param name="typeName" type="String">Name of type to check.</param>
+        /// <returns type="Boolean">True if the type is the name of a 
collection type; false otherwise.</returns>
+        return collectionTypeRE.test(typeName);
+    };
+
+    var isComplex = function (value) {
+        /// <summary>Tests whether a value is a complex type value in the 
library's internal representation.</summary>
+        /// <param name="value">Value to test.</param>
+        /// <returns type="Boolean">True is the value is a complex type value; 
false otherwise.</returns>
+
+        return !!value &&
+            isObject(value) &&
+            !isArray(value) &&
+            !isDate(value);
+    };
+
+    var isDateTimeOffset = function (value) {
+        /// <summary>Checks whether a Date object is DateTimeOffset 
value</summary>
+        /// <param name="value" type="Date" mayBeNull="false">Value to 
check.</param>
+        /// <returns type="Boolean">true if the value is a DateTimeOffset, 
false otherwise.</returns>
+        return (value.__edmType === "Edm.DateTimeOffset" || (!value.__edmType 
&& value.__offset));
+    };
+
+    var isDeferred = function (value) {
+        /// <summary>Tests whether a value is a deferred navigation property 
in the library's internal representation.</summary>
+        /// <param name="value">Value to test.</param>
+        /// <returns type="Boolean">True is the value is a deferred navigation 
property; false otherwise.</returns>
+
+        if (!value && !isComplex(value)) {
+            return false;
+        }
+        var metadata = value.__metadata || {};
+        var deferred = value.__deferred || {};
+        return !metadata.type && !!deferred.uri;
+    };
+
+    var isEntry = function (value) {
+        /// <summary>Tests whether a value is an entry object in the library's 
internal representation.</summary>
+        /// <param name="value">Value to test.</param>
+        /// <returns type="Boolean">True is the value is an entry object; 
false otherwise.</returns>
+
+        return isComplex(value) && value.__metadata && "uri" in 
value.__metadata;
+    };
+
+    var isFeed = function (value, typeName) {
+        /// <summary>Tests whether a value is a feed value in the library's 
internal representation.</summary>
+        /// <param name="value">Value to test.</param>
+        /// <param name="typeName" type="Sting">Type name of the value. This 
is used to disambiguate from a collection property value.</param>
+        /// <returns type="Boolean">True is the value is a feed value; false 
otherwise.</returns>
+
+        var feedData = value && value.results || value;
+        return isArray(feedData) && (
+            (!isCollectionType(typeName)) &&
+            (isComplex(feedData[0]))
+        );
+    };
+
+    var isGeographyEdmType = function (typeName) {
+        /// <summary>Checks whether the specified type name is a geography EDM 
type.</summary>
+        /// <param name="typeName" type="String">Name of type to check.</param>
+        /// <returns type="Boolean">True if the type is a geography EDM type; 
false otherwise.</returns>
+
+        return contains(geographyEdmTypes, typeName);
+    };
+
+    var isGeometryEdmType = function (typeName) {
+        /// <summary>Checks whether the specified type name is a geometry EDM 
type.</summary>
+        /// <param name="typeName" type="String">Name of type to check.</param>
+        /// <returns type="Boolean">True if the type is a geometry EDM type; 
false otherwise.</returns>
+
+        return contains(geometryEdmTypes, typeName);
+    };
+
+    var isNamedStream = function (value) {
+        /// <summary>Tests whether a value is a named stream value in the 
library's internal representation.</summary>
+        /// <param name="value">Value to test.</param>
+        /// <returns type="Boolean">True is the value is a named stream; false 
otherwise.</returns>
+
+        if (!value && !isComplex(value)) {
+            return false;
+        }
+        var metadata = value.__metadata;
+        var mediaResource = value.__mediaresource;
+        return !metadata && !!mediaResource && !!mediaResource.media_src;
+    };
+
+    var isPrimitive = function (value) {
+        /// <summary>Tests whether a value is a primitive type value in the 
library's internal representation.</summary>
+        /// <param name="value">Value to test.</param>
+        /// <remarks>
+        ///    Date objects are considered primitive types by the library.
+        /// </remarks>
+        /// <returns type="Boolean">True is the value is a primitive type 
value.</returns>
+
+        return isDate(value) ||
+            typeof value === "string" ||
+            typeof value === "number" ||
+            typeof value === "boolean";
+    };
+
+    var isPrimitiveEdmType = function (typeName) {
+        /// <summary>Checks whether the specified type name is a primitive EDM 
type.</summary>
+        /// <param name="typeName" type="String">Name of type to check.</param>
+        /// <returns type="Boolean">True if the type is a primitive EDM type; 
false otherwise.</returns>
+
+        return contains(primitiveEdmTypes, typeName);
+    };
+
+    var navigationPropertyKind = function (value, propertyModel) {
+        /// <summary>Gets the kind of a navigation property value.</summary>
+        /// <param name="value">Value of the navigation property.</param>
+        /// <param name="propertyModel" type="Object" optional="true">
+        ///     Object that describes the navigation property in an OData 
conceptual schema.
+        /// </param>
+        /// <remarks>
+        ///     The returned string is as follows
+        /// </remarks>
+        /// <returns type="String">String value describing the kind of the 
navigation property; null if the kind cannot be determined.</returns>
+
+        if (isDeferred(value)) {
+            return "deferred";
+        }
+        if (isEntry(value)) {
+            return "entry";
+        }
+        if (isFeed(value)) {
+            return "feed";
+        }
+        if (propertyModel && propertyModel.relationship) {
+            if (value === null || value === undefined || !isFeed(value)) {
+                return "entry";
+            }
+            return "feed";
+        }
+        return null;
+    };
+
+    var lookupProperty = function (properties, name) {
+        /// <summary>Looks up a property by name.</summary>
+        /// <param name="properties" type="Array" mayBeNull="true">Array of 
property objects as per EDM metadata.</param>
+        /// <param name="name" type="String">Name to look for.</param>
+        /// <returns type="Object">The property object; null if not 
found.</returns>
+
+        return find(properties, function (property) {
+            return property.name === name;
+        });
+    };
+
+    var lookupInMetadata = function (name, metadata, kind) {
+        /// <summary>Looks up a type object by name.</summary>
+        /// <param name="name" type="String">Name, possibly null or 
empty.</param>
+        /// <param name="metadata">Metadata store; one of edmx, schema, or an 
array of any of them.</param>
+        /// <param name="kind" type="String">Kind of object to look for as per 
EDM metadata.</param>
+        /// <returns>An type description if the name is found; null 
otherwise.</returns>
+
+        return (name) ? forEachSchema(metadata, function (schema) {
+            return lookupInSchema(name, schema, kind);
+        }) : null;
+    };
+
+    var lookupEntitySet = function (entitySets, name) {
+        /// <summary>Looks up a entity set by name.</summary>
+        /// <param name="properties" type="Array" mayBeNull="true">Array of 
entity set objects as per EDM metadata.</param>
+        /// <param name="name" type="String">Name to look for.</param>
+        /// <returns type="Object">The entity set object; null if not 
found.</returns>
+
+        return find(entitySets, function (entitySet) {
+            return entitySet.name === name;
+        });
+    };
+
+    var lookupComplexType = function (name, metadata) {
+        /// <summary>Looks up a complex type object by name.</summary>
+        /// <param name="name" type="String">Name, possibly null or 
empty.</param>
+        /// <param name="metadata">Metadata store; one of edmx, schema, or an 
array of any of them.</param>
+        /// <returns>A complex type description if the name is found; null 
otherwise.</returns>
+
+        return lookupInMetadata(name, metadata, "complexType");
+    };
+
+    var lookupEntityType = function (name, metadata) {
+        /// <summary>Looks up an entity type object by name.</summary>
+        /// <param name="name" type="String">Name, possibly null or 
empty.</param>
+        /// <param name="metadata">Metadata store; one of edmx, schema, or an 
array of any of them.</param>
+        /// <returns>An entity type description if the name is found; null 
otherwise.</returns>
+
+        return lookupInMetadata(name, metadata, "entityType");
+    };
+
+    var lookupDefaultEntityContainer = function (metadata) {
+        /// <summary>Looks up an</summary>
+        /// <param name="name" type="String">Name, possibly null or 
empty.</param>
+        /// <param name="metadata">Metadata store; one of edmx, schema, or an 
array of any of them.</param>
+        /// <returns>An entity container description if the name is found; 
null otherwise.</returns>
+
+        return forEachSchema(metadata, function (schema) {
+            return find(schema.entityContainer, function (container) {
+                return parseBool(container.isDefaultEntityContainer);
+            });
+        });
+    };
+
+    var lookupEntityContainer = function (name, metadata) {
+        /// <summary>Looks up an entity container object by name.</summary>
+        /// <param name="name" type="String">Name, possibly null or 
empty.</param>
+        /// <param name="metadata">Metadata store; one of edmx, schema, or an 
array of any of them.</param>
+        /// <returns>An entity container description if the name is found; 
null otherwise.</returns>
+
+        return lookupInMetadata(name, metadata, "entityContainer");
+    };
+
+    var lookupFunctionImport = function (functionImports, name) {
+        /// <summary>Looks up a function import by name.</summary>
+        /// <param name="properties" type="Array" mayBeNull="true">Array of 
function import objects as per EDM metadata.</param>
+        /// <param name="name" type="String">Name to look for.</param>
+        /// <returns type="Object">The entity set object; null if not 
found.</returns>
+
+        return find(functionImports, function (functionImport) {
+            return functionImport.name === name;
+        });
+    };
+
+    var lookupNavigationPropertyType = function (navigationProperty, metadata) 
{
+        /// <summary>Looks up the target entity type for a navigation 
property.</summary>
+        /// <param name="navigationProperty" type="Object"></param>
+        /// <param name="metadata" type="Object"></param>
+        /// <returns type="String">The entity type name for the specified 
property, null if not found.</returns>
+
+        var result = null;
+        if (navigationProperty) {
+            var rel = navigationProperty.relationship;
+            var association = forEachSchema(metadata, function (schema) {
+                // The name should be the namespace qualified name in 
'ns'.'type' format.
+                var nameOnly = removeNamespace(schema["namespace"], rel);
+                var associations = schema.association;
+                if (nameOnly && associations) {
+                    var i, len;
+                    for (i = 0, len = associations.length; i < len; i++) {
+                        if (associations[i].name === nameOnly) {
+                            return associations[i];
+                        }
+                    }
+                }
+                return null;
+            });
+
+            if (association) {
+                var end = association.end[0];
+                if (end.role !== navigationProperty.toRole) {
+                    end = association.end[1];
+                    // For metadata to be valid, end.role === 
navigationProperty.toRole now.
+                }
+                result = end.type;
+            }
+        }
+        return result;
+    };
+
+    var lookupNavigationPropertyEntitySet = function (navigationProperty, 
sourceEntitySetName, metadata) {
+        /// <summary>Looks up the target entityset name for a navigation 
property.</summary>
+        /// <param name="navigationProperty" type="Object"></param>
+        /// <param name="metadata" type="Object"></param>
+        /// <returns type="String">The entityset name for the specified 
property, null if not found.</returns>
+
+        if (navigationProperty) {
+            var rel = navigationProperty.relationship;
+            var associationSet = forEachSchema(metadata, function (schema) {
+                var containers = schema.entityContainer;
+                for (var i = 0; i < containers.length; i++) {
+                    var associationSets = containers[i].associationSet;
+                    if (associationSets) {
+                        for (var j = 0; j < associationSets.length; j++) {
+                            if (associationSets[j].association == rel) {
+                                return associationSets[j];
+                            }
+                        }
+                    }
+                }
+                return null;
+            });
+            if (associationSet && associationSet.end[0] && 
associationSet.end[1]) {
+                return (associationSet.end[0].entitySet == 
sourceEntitySetName) ? associationSet.end[1].entitySet : 
associationSet.end[0].entitySet;
+            }
+        }
+        return null;
+    };
+
+    var getEntitySetInfo = function (entitySetName, metadata) {
+        /// <summary>Gets the entitySet info, container name and 
functionImports for an entitySet</summary>
+        /// <param name="navigationProperty" type="Object"></param>
+        /// <param name="metadata" type="Object"></param>
+        /// <returns type="Object">The info about the entitySet.</returns>
+
+        var info = forEachSchema(metadata, function (schema) {
+            var containers = schema.entityContainer;
+            for (var i = 0; i < containers.length; i++) {
+                var entitySets = containers[i].entitySet;
+                if (entitySets) {
+                    for (var j = 0; j < entitySets.length; j++) {
+                        if (entitySets[j].name == entitySetName) {
+                            return { entitySet: entitySets[j], containerName: 
containers[i].name, functionImport: containers[i].functionImport };
+                        }
+                    }
+                }
+            }
+            return null;
+        });
+
+        return info;
+    };
+
+    var removeNamespace = function (ns, fullName) {
+        /// <summary>Given an expected namespace prefix, removes it from a 
full name.</summary>
+        /// <param name="ns" type="String">Expected namespace.</param>
+        /// <param name="fullName" type="String">Full name in 'ns'.'name' 
form.</param>
+        /// <returns type="String">The local name, null if it isn't found in 
the expected namespace.</returns>
+
+        if (fullName.indexOf(ns) === 0 && fullName.charAt(ns.length) === ".") {
+            return fullName.substr(ns.length + 1);
+        }
+
+        return null;
+    };
+
+    var lookupInSchema = function (name, schema, kind) {
+        /// <summary>Looks up a schema object by name.</summary>
+        /// <param name="name" type="String">Name (assigned).</param>
+        /// <param name="schema">Schema object as per EDM metadata.</param>
+        /// <param name="kind" type="String">Kind of object to look for as per 
EDM metadata.</param>
+        /// <returns>An entity type description if the name is found; null 
otherwise.</returns>
+
+        if (name && schema) {
+            // The name should be the namespace qualified name in 'ns'.'type' 
format.
+            var nameOnly = removeNamespace(schema["namespace"], name);
+            if (nameOnly) {
+                return find(schema[kind], function (item) {
+                    return item.name === nameOnly;
+                });
+            }
+        }
+        return null;
+    };
+
+    var maxVersion = function (left, right) {
+        /// <summary>Compares to version strings and returns the higher 
one.</summary>
+        /// <param name="left" type="String">Version string in the form 
"major.minor.rev"</param>
+        /// <param name="right" type="String">Version string in the form 
"major.minor.rev"</param>
+        /// <returns type="String">The higher version string.</returns>
+
+        if (left === right) {
+            return left;
+        }
+
+        var leftParts = left.split(".");
+        var rightParts = right.split(".");
+
+        var len = (leftParts.length >= rightParts.length) ?
+            leftParts.length :
+            rightParts.length;
+
+        for (var i = 0; i < len; i++) {
+            var leftVersion = leftParts[i] && parseInt10(leftParts[i]);
+            var rightVersion = rightParts[i] && parseInt10(rightParts[i]);
+            if (leftVersion > rightVersion) {
+                return left;
+            }
+            if (leftVersion < rightVersion) {
+                return right;
+            }
+        }
+    };
+
+    var normalHeaders = {
+        "accept": "Accept",
+        "content-type": "Content-Type",
+        "dataserviceversion": "DataServiceVersion",
+        "maxdataserviceversion": "MaxDataServiceVersion"
+    };
+
+    var normalizeHeaders = function (headers) {
+        /// <summary>Normalizes headers so they can be found with consistent 
casing.</summary>
+        /// <param name="headers" type="Object">Dictionary of name/value 
pairs.</param>
+
+        for (var name in headers) {
+            var lowerName = name.toLowerCase();
+            var normalName = normalHeaders[lowerName];
+            if (normalName && name !== normalName) {
+                var val = headers[name];
+                delete headers[name];
+                headers[normalName] = val;
+            }
+        }
+    };
+
+    var parseBool = function (propertyValue) {
+        /// <summary>Parses a string into a boolean value.</summary>
+        /// <param name="propertyValue">Value to parse.</param>
+        /// <returns type="Boolean">true if the property value is 'true'; 
false otherwise.</returns>
+
+        if (typeof propertyValue === "boolean") {
+            return propertyValue;
+        }
+
+        return typeof propertyValue === "string" && 
propertyValue.toLowerCase() === "true";
+    };
+
+
+    // The captured indices for this expression are:
+    // 0     - complete input
+    // 1,2,3 - year with optional minus sign, month, day
+    // 4,5,6 - hours, minutes, seconds
+    // 7     - optional milliseconds
+    // 8     - everything else (presumably offset information)
+    var parseDateTimeRE = 
/^(-?\d{4,})-(\d{2})-(\d{2})T(\d{2}):(\d{2})(?::(\d{2}))?(?:\.(\d+))?(.*)$/;
+
+    var parseDateTimeMaybeOffset = function (value, withOffset, nullOnError) {
+        /// <summary>Parses a string into a DateTime value.</summary>
+        /// <param name="value" type="String">Value to parse.</param>
+        /// <param name="withOffset" type="Boolean">Whether offset is 
expected.</param>
+        /// <returns type="Date">The parsed value.</returns>
+
+        // We cannot parse this in cases of failure to match or if offset 
information is specified.
+        var parts = parseDateTimeRE.exec(value);
+        var offset = (parts) ? getCanonicalTimezone(parts[8]) : null;
+
+        if (!parts || (!withOffset && offset !== "Z")) {
+            if (nullOnError) {
+                return null;
+            }
+            throw { message: "Invalid date/time value" };
+        }
+
+        // Pre-parse years, account for year '0' being invalid in dateTime.
+        var year = parseInt10(parts[1]);
+        if (year <= 0) {
+            year++;
+        }
+
+        // Pre-parse optional milliseconds, fill in default. Fail if value is 
too precise.
+        var ms = parts[7];
+        var ns = 0;
+        if (!ms) {
+            ms = 0;
+        } else {
+            if (ms.length > 7) {
+                if (nullOnError) {
+                    return null;
+                }
+                throw { message: "Cannot parse date/time value to given 
precision." };
+            }
+
+            ns = formatNumberWidth(ms.substring(3), 4, true);
+            ms = formatNumberWidth(ms.substring(0, 3), 3, true);
+
+            ms = parseInt10(ms);
+            ns = parseInt10(ns);
+        }
+
+        // Pre-parse other time components and offset them if necessary.
+        var hours = parseInt10(parts[4]);
+        var minutes = parseInt10(parts[5]);
+        var seconds = parseInt10(parts[6]) || 0;
+        if (offset !== "Z") {
+            // The offset is reversed to get back the UTC date, which is
+            // what the API will eventually have.
+            var timezone = parseTimezone(offset);
+            var direction = -(timezone.d);
+            hours += timezone.h * direction;
+            minutes += timezone.m * direction;
+        }
+
+        // Set the date and time separately with setFullYear, so years 0-99 
aren't biased like in Date.UTC.
+        var result = new Date();
+        result.setUTCFullYear(
+            year,                       // Year.
+            parseInt10(parts[2]) - 1,   // Month (zero-based for Date.UTC and 
setFullYear).
+            parseInt10(parts[3])        // Date.
+            );
+        result.setUTCHours(hours, minutes, seconds, ms);
+
+        if (isNaN(result.valueOf())) {
+            if (nullOnError) {
+                return null;
+            }
+            throw { message: "Invalid date/time value" };
+        }
+
+        if (withOffset) {
+            result.__edmType = "Edm.DateTimeOffset";
+            result.__offset = offset;
+        }
+
+        if (ns) {
+            result.__ns = ns;
+        }
+
+        return result;
+    };
+
+    var parseDateTime = function (propertyValue, nullOnError) {
+        /// <summary>Parses a string into a DateTime value.</summary>
+        /// <param name="propertyValue" type="String">Value to parse.</param>
+        /// <returns type="Date">The parsed value.</returns>
+
+        return parseDateTimeMaybeOffset(propertyValue, false, nullOnError);
+    };
+
+    var parseDateTimeOffset = function (propertyValue, nullOnError) {
+        /// <summary>Parses a string into a DateTimeOffset value.</summary>
+        /// <param name="propertyValue" type="String">Value to parse.</param>
+        /// <returns type="Date">The parsed value.</returns>
+        /// <remarks>
+        /// The resulting object is annotated with an __edmType property and
+        /// an __offset property reflecting the original intended offset of
+        /// the value. The time is adjusted for UTC time, as the current
+        /// timezone-aware Date APIs will only work with the local timezone.
+        /// </remarks>
+
+        return parseDateTimeMaybeOffset(propertyValue, true, nullOnError);
+    };
+
+    // The captured indices for this expression are:
+    // 0       - complete input
+    // 1       - direction
+    // 2,3,4   - years, months, days
+    // 5,6,7,8 - hours, minutes, seconds, miliseconds
+
+    var parseTimeRE = 
/^([+-])?P(?:(\d+)Y)?(?:(\d+)M)?(?:(\d+)D)?(?:T(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)(?:\.(\d+))?S)?)?/;
+
+    var isEdmDurationValue = function(value) {
+        parseTimeRE.test(value);
+    };
+
+    var parseDuration = function (duration) {
+        /// <summary>Parses a string in xsd:duration format.</summary>
+        /// <param name="duration" type="String">Duration value.</param>
+        /// <remarks>
+        /// This method will throw an exception if the input string has a year 
or a month component.
+        /// </remarks>
+        /// <returns type="Object">Object representing the time</returns>
+
+        var parts = parseTimeRE.exec(duration);
+
+        if (parts === null) {
+            throw { message: "Invalid duration value." };
+        }
+
+        var years = parts[2] || "0";
+        var months = parts[3] || "0";
+        var days = parseInt10(parts[4] || 0);
+        var hours = parseInt10(parts[5] || 0);
+        var minutes = parseInt10(parts[6] || 0);
+        var seconds = parseFloat(parts[7] || 0);
+
+        if (years !== "0" || months !== "0") {
+            throw { message: "Unsupported duration value." };
+        }
+
+        var ms = parts[8];
+        var ns = 0;
+        if (!ms) {
+            ms = 0;
+        } else {
+            if (ms.length > 7) {
+                throw { message: "Cannot parse duration value to given 
precision." };
+            }
+
+            ns = formatNumberWidth(ms.substring(3), 4, true);
+            ms = formatNumberWidth(ms.substring(0, 3), 3, true);
+
+            ms = parseInt10(ms);
+            ns = parseInt10(ns);
+        }
+
+        ms += seconds * 1000 + minutes * 60000 + hours * 3600000 + days * 
86400000;
+
+        if (parts[1] === "-") {
+            ms = -ms;
+        }
+
+        var result = { ms: ms, __edmType: "Edm.Time" };
+
+        if (ns) {
+            result.ns = ns;
+        }
+        return result;
+    };
+
+    var parseTimezone = function (timezone) {
+        /// <summary>Parses a timezone description in (+|-)nn:nn 
format.</summary>
+        /// <param name="timezone" type="String">Timezone offset.</param>
+        /// <returns type="Object">
+        /// An object with a (d)irection property of 1 for + and -1 for -,
+        /// offset (h)ours and offset (m)inutes.
+        /// </returns>
+
+        var direction = timezone.substring(0, 1);
+        direction = (direction === "+") ? 1 : -1;
+
+        var offsetHours = parseInt10(timezone.substring(1));
+        var offsetMinutes = 
parseInt10(timezone.substring(timezone.indexOf(":") + 1));
+        return { d: direction, h: offsetHours, m: offsetMinutes };
+    };
+
+    var prepareRequest = function (request, handler, context) {
+        /// <summary>Prepares a request object so that it can be sent through 
the network.</summary>
+        /// <param name="request">Object that represents the request to be 
sent.</param>
+        /// <param name="handler">Handler for data serialization</param>
+        /// <param name="context">Context used for preparing the 
request</param>
+
+        // Default to GET if no method has been specified.
+        if (!request.method) {
+            request.method = "GET";
+        }
+
+        if (!request.headers) {
+            request.headers = {};
+        } else {
+            normalizeHeaders(request.headers);
+        }
+
+        if (request.headers.Accept === undefined) {
+            request.headers.Accept = handler.accept;
+        }
+
+        if (assigned(request.data) && request.body === undefined) {
+            handler.write(request, context);
+        }
+
+        if (!assigned(request.headers.MaxDataServiceVersion)) {
+            request.headers.MaxDataServiceVersion = 
handler.maxDataServiceVersion || "1.0";
+        }
+    };
+
+    var traverseInternal = function (item, owner, callback) {
+        /// <summary>Traverses a tree of objects invoking callback for every 
value.</summary>
+        /// <param name="item" type="Object">Object or array to 
traverse.</param>
+        /// <param name="callback" type="Function">
+        /// Callback function with key and value, similar to JSON.parse 
reviver.
+        /// </param>
+        /// <returns type="Object">The object with traversed 
properties.</returns>
+        /// <remarks>Unlike the JSON reviver, this won't delete null 
members.</remarks>
+
+        if (item && typeof item === "object") {
+            for (var name in item) {
+                var value = item[name];
+                var result = traverseInternal(value, name, callback);
+                result = callback(name, result, owner);
+                if (result !== value) {
+                    if (value === undefined) {
+                        delete item[name];
+                    } else {
+                        item[name] = result;
+                    }
+                }
+            }
+        }
+
+        return item;
+    };
+
+    var traverse = function (item, callback) {
+        /// <summary>Traverses a tree of objects invoking callback for every 
value.</summary>
+        /// <param name="item" type="Object">Object or array to 
traverse.</param>
+        /// <param name="callback" type="Function">
+        /// Callback function with key and value, similar to JSON.parse 
reviver.
+        /// </param>
+        /// <returns type="Object">The traversed object.</returns>
+        /// <remarks>Unlike the JSON reviver, this won't delete null 
members.</remarks>
+
+        return callback("", traverseInternal(item, "", callback));
+    };
+
+    // DATAJS INTERNAL START
+    odata.dataItemTypeName = dataItemTypeName;
+    odata.EDM_BINARY = EDM_BINARY;
+    odata.EDM_BOOLEAN = EDM_BOOLEAN;
+    odata.EDM_BYTE = EDM_BYTE;
+    odata.EDM_DATETIME = EDM_DATETIME;
+    odata.EDM_DATETIMEOFFSET = EDM_DATETIMEOFFSET;
+    odata.EDM_DECIMAL = EDM_DECIMAL;
+    odata.EDM_DOUBLE = EDM_DOUBLE;
+    odata.EDM_GEOGRAPHY = EDM_GEOGRAPHY;
+    odata.EDM_GEOGRAPHY_POINT = EDM_GEOGRAPHY_POINT;
+    odata.EDM_GEOGRAPHY_LINESTRING = EDM_GEOGRAPHY_LINESTRING;
+    odata.EDM_GEOGRAPHY_POLYGON = EDM_GEOGRAPHY_POLYGON;
+    odata.EDM_GEOGRAPHY_COLLECTION = EDM_GEOGRAPHY_COLLECTION;
+    odata.EDM_GEOGRAPHY_MULTIPOLYGON = EDM_GEOGRAPHY_MULTIPOLYGON;
+    odata.EDM_GEOGRAPHY_MULTILINESTRING = EDM_GEOGRAPHY_MULTILINESTRING;
+    odata.EDM_GEOGRAPHY_MULTIPOINT = EDM_GEOGRAPHY_MULTIPOINT;
+    odata.EDM_GEOMETRY = EDM_GEOMETRY;
+    odata.EDM_GEOMETRY_POINT = EDM_GEOMETRY_POINT;
+    odata.EDM_GEOMETRY_LINESTRING = EDM_GEOMETRY_LINESTRING;
+    odata.EDM_GEOMETRY_POLYGON = EDM_GEOMETRY_POLYGON;
+    odata.EDM_GEOMETRY_COLLECTION = EDM_GEOMETRY_COLLECTION;
+    odata.EDM_GEOMETRY_MULTIPOLYGON = EDM_GEOMETRY_MULTIPOLYGON;
+    odata.EDM_GEOMETRY_MULTILINESTRING = EDM_GEOMETRY_MULTILINESTRING;
+    odata.EDM_GEOMETRY_MULTIPOINT = EDM_GEOMETRY_MULTIPOINT;
+    odata.EDM_GUID = EDM_GUID;
+    odata.EDM_INT16 = EDM_INT16;
+    odata.EDM_INT32 = EDM_INT32;
+    odata.EDM_INT64 = EDM_INT64;
+    odata.EDM_SBYTE = EDM_SBYTE;
+    odata.EDM_SINGLE = EDM_SINGLE;
+    odata.EDM_STRING = EDM_STRING;
+    odata.EDM_TIME = EDM_TIME;
+    odata.GEOJSON_POINT = GEOJSON_POINT;
+    odata.GEOJSON_LINESTRING = GEOJSON_LINESTRING;
+    odata.GEOJSON_POLYGON = GEOJSON_POLYGON;
+    odata.GEOJSON_MULTIPOINT = GEOJSON_MULTIPOINT;
+    odata.GEOJSON_MULTILINESTRING = GEOJSON_MULTILINESTRING;
+    odata.GEOJSON_MULTIPOLYGON = GEOJSON_MULTIPOLYGON;
+    odata.GEOJSON_GEOMETRYCOLLECTION = GEOJSON_GEOMETRYCOLLECTION;
+    odata.forEachSchema = forEachSchema;
+    odata.formatDateTimeOffset = formatDateTimeOffset;
+    odata.formatDuration = formatDuration;
+    odata.formatNumberWidth = formatNumberWidth;
+    odata.getCanonicalTimezone = getCanonicalTimezone;
+    odata.getCollectionType = getCollectionType;
+    odata.invokeRequest = invokeRequest;
+    odata.isBatch = isBatch;
+    odata.isCollection = isCollection;
+    odata.isCollectionType = isCollectionType;
+    odata.isComplex = isComplex;
+    odata.isDateTimeOffset = isDateTimeOffset;
+    odata.isDeferred = isDeferred;
+    odata.isEntry = isEntry;
+    odata.isFeed = isFeed;
+    odata.isGeographyEdmType = isGeographyEdmType;
+    odata.isGeometryEdmType = isGeometryEdmType;
+    odata.isNamedStream = isNamedStream;
+    odata.isPrimitive = isPrimitive;
+    odata.isPrimitiveEdmType = isPrimitiveEdmType;
+    odata.lookupComplexType = lookupComplexType;
+    odata.lookupDefaultEntityContainer = lookupDefaultEntityContainer;
+    odata.lookupEntityContainer = lookupEntityContainer;
+    odata.lookupEntitySet = lookupEntitySet;
+    odata.lookupEntityType = lookupEntityType;
+    odata.lookupFunctionImport = lookupFunctionImport;
+    odata.lookupNavigationPropertyType = lookupNavigationPropertyType;
+    odata.lookupNavigationPropertyEntitySet = 
lookupNavigationPropertyEntitySet;
+    odata.lookupInSchema = lookupInSchema;
+    odata.lookupProperty = lookupProperty;
+    odata.lookupInMetadata = lookupInMetadata;
+    odata.getEntitySetInfo = getEntitySetInfo;
+    odata.maxVersion = maxVersion;
+    odata.navigationPropertyKind = navigationPropertyKind;
+    odata.normalizeHeaders = normalizeHeaders;
+    odata.parseBool = parseBool;
+    odata.parseDateTime = parseDateTime;
+    odata.parseDateTimeOffset = parseDateTimeOffset;
+    odata.parseDuration = parseDuration;
+    odata.parseTimezone = parseTimezone;
+    odata.parseInt10 = parseInt10;
+    odata.prepareRequest = prepareRequest;
+    odata.removeNamespace = removeNamespace;
+    odata.traverse = traverse;
+
+    // DATAJS INTERNAL END
+
+    // CONTENT END
+})(this);
\ No newline at end of file

Reply via email to