blob: 066bc50926c7332335f4de0f63d79ae0b00a6ee1 [file] [log] [blame]
// 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 = {
// 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",
// Headers used by response
"etag": "ETag",
"location": "Location",
"odata-entityid": "OData-EntityId",
"preference-applied": "Preference-Applied",
"retry-after": "Retry-After"
};
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["OData-MaxVersion"])) {
request.headers["OData-MaxVersion"] = handler.maxDataServiceVersion || "4.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);