diff --git a/README.md b/README.md index 72aaac4..fd11e48 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # o.js -_o.js beta v0.3.8_ +_o.js beta v0.4.0_ o.js is a client side Odata Javascript library to simplify the request of data. The main goal is to build a **standalone, lightweight and easy** to understand Odata lib. @@ -223,7 +223,7 @@ o().config({ username: null, // the basic auth username password: null, // the basic auth password isAsync: true // set this to false to enable sync requests. Only usable without basic auth - isCors: true, // set this to false to disable CORS + isWithCredentials: false, // set this to true to use basic auth isHashRoute: true, // set this var to false to disable automatic #-hash setting on routes appending: '' // set this value to append something to a any request. eg.: [{name:'apikey', value:'xyz'}] }); @@ -274,3 +274,7 @@ Currently the following queries are supported: `.remove(object)` - DELETE data on an endpoint (You must define only one resource: e.g: Products(1) ) `.query()` - get the query as string + +`.raw` - gives the raw data in a callback + +`.xhr` - allows access to the xhr object to get headers \ No newline at end of file diff --git a/example/app.js b/example/app.js index 6401be7..9f8f787 100644 --- a/example/app.js +++ b/example/app.js @@ -30,7 +30,7 @@ function ViewModel() { //o.js init o().config({ - endpoint: 'http://services.odata.org/V4/%28S%28wptr35qf3bz4kb5oatn432ul%29%29/TripPinServiceRW/', + endpoint: 'https://services.odata.org/V4/%28S%28wptr35qf3bz4kb5oatn432ul%29%29/TripPinServiceRW/', version: 4, strictMode: true, start: function () { @@ -39,6 +39,7 @@ function ViewModel() { ready: function () { self.isLoading(false); }, + headers: [{ name: 'If-Match', value: '*' }] }); diff --git a/example/index.html b/example/index.html index e1ab0d8..2d2b344 100644 --- a/example/index.html +++ b/example/index.html @@ -125,7 +125,7 @@ <h2 data-bind="text:LastName"></h4> <!-- Include all compiled plugins (below), or include individual files as needed --> <script src="js/bootstrap.min.js"></script> <script src="js/q.js"></script> - <script src="../o.min.js"></script> + <script src="../o.js"></script> <script src="js/knockout-3.2.0.js"></script> <script src="js/ko.bindinghandler.currency.js"></script> <script src="app.js"></script> diff --git a/o.js b/o.js index d3472ee..a59ba8f 100644 --- a/o.js +++ b/o.js @@ -1,18 +1,18 @@ // +++ -// o.js v0.3.8 +// o.js v0.4.0 // // o.js is a simple oData wrapper for JavaScript. // Currently supporting the following operations: // .get() / .post() / .put() / .delete() / .first() / .take() / .skip() / .filter() / .orderBy() / .orderByDesc() / .count() /.search() / .select() / .any() / .ref() / .deleteRef() // // By Jan Hommes -// Date: 11/08/2017 +// Date: 08/06/2018 // Contributors: Matteo Antony Mistretta (https://github.com/IceOnFire), // // -------------------- // The MIT License (MIT) // -// Copyright (c) 2017 Jan Hommes +// Copyright (c) 2018 Jan Hommes // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -32,7 +32,8 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // +++ -; (function (root, factory) { +; +(function (root, factory) { if (typeof define === 'function' && define.amd) { define(['q'], factory); } else if (typeof exports === 'object') { @@ -47,21 +48,21 @@ //base config object base.oConfig = base.oConfig || { endpoint: null, - format: 'json', // The media format. Default is JSON. - autoFormat: true, // Will always append a $format=json to each query if set to true. - version: 4, // currently only tested for Version 4. Most will work in version 3 as well. - strictMode: true, // strict mode throws exception, non strict mode only logs them - start: null, // a function which is executed on loading - ready: null, // a function which is executed on ready - error: null, // a function which is executed on error - headers: [], // an array of additional headers [{name:'headername',value:'headervalue'}] - username: null, // the basic auth username - password: null, // the basic auth password - isAsync: true, // set this to false to enable sync requests. Only usable without basic auth - isCors: true, // set this to false to disable CORS - openAjaxRequests: 0,// a counter for all open ajax request to determine that are all ready TODO: Move this out of the config - isHashRoute: true, // set this var to false to disable automatic #-hash setting on routes - appending: '' // set this value to append something to a any request. eg.: [{name:'apikey', value:'xyz'}] + format: 'json', // The media format. Default is JSON. + autoFormat: true, // Will always append a $format=json to each query if set to true. + version: 4, // currently only tested for Version 4. Most will work in version 3 as well. + strictMode: true, // strict mode throws exception, non strict mode only logs them + start: null, // a function which is executed on loading + ready: null, // a function which is executed on ready + error: null, // a function which is executed on error + headers: [], // an array of additional headers [{name:'headername',value:'headervalue'}] + username: null, // the basic auth username + password: null, // the basic auth password + isAsync: true, // set this to false to enable sync requests. Only usable without basic auth + isWithCredentials: false, // set this to true if used with basic auth + openAjaxRequests: 0, // a counter for all open ajax request to determine that are all ready TODO: Move this out of the config + isHashRoute: true, // set this var to false to disable automatic #-hash setting on routes + appending: '' // set this value to append something to a any request. eg.: [{name:'apikey', value:'xyz'}] }; // +++ @@ -77,7 +78,7 @@ // username: The basic auth username // password: The basic auth password // isAsync: If set to false, the request are done sync. Default is true. - // IsCors: set this to false to disable CORS + // isWithCredentials: set this to true if used with basic auth // +++ base.config = function (config) { base.oConfig = merge(base.oConfig, config); @@ -110,8 +111,7 @@ if (typeof res === 'undefined') { return (base); - } - else { + } else { return (new oData(res, base.oConfig)); } } @@ -123,15 +123,15 @@ // --------------------+++ VARIABLES +++--------------------------- //base internal variables - var resource = null; // the main resource string - var resourceList = []; // an array list of all resource used - var routeList = []; // an array list of all routes used - var isEndpoint = true; // true if an endpoint is configured - var currentPromise = null; // if promise (Q.js) is used, we hold it here - var overideLoading = null; // if set, this resource call don't use the global loading function - var isXDomainRequest = false; // this is set to true in IE 9 and IE 8 to support CORS operations. No basic auth support :( - var beforeRoutingFunc = function () { }; // A function which is called before routing. - var internalParam = {}; // like base.param this object holds all parameter for a route but with the leading : for easier using in regexes + var resource = null; // the main resource string + var resourceList = []; // an array list of all resource used + var routeList = []; // an array list of all routes used + var isEndpoint = true; // true if an endpoint is configured + var currentPromise = null; // if promise (Q.js) is used, we hold it here + var overideLoading = null; // if set, this resource call don't use the global loading function + var isXDomainRequest = false; // this is set to true in IE 9 and IE 8 to support CORS operations. No basic auth support :( + var beforeRoutingFunc = function () {}; // A function which is called before routing. + var internalParam = {}; // like base.param this object holds all parameter for a route but with the leading : for easier using in regexes var opertionMapping = { '==': 'eq', '===': 'eq', @@ -153,11 +153,12 @@ //base external variables - base.data = []; //holds the data after an callback - base.inlinecount = null; //if inlinecount is set, here the counting is gold - base.param = {}; //this object holds all parameter for a route - base.oConfig = config; //the internal config, passed over from the o function - base.raw = null; //holds the data after an callback (raw data, containing also metadata) + base.data = []; //holds the data after an callback + base.inlinecount = null; //if inlinecount is set, here the counting is gold + base.param = {}; //this object holds all parameter for a route + base.oConfig = config; //the internal config, passed over from the o function + base.raw = null; //holds the data after an callback (raw data, containing also metadata) + base.xhr = null; //holds the xhr client after an callback (for getting response headers for example) // ---------------------+++ PUBLICS +++---------------------------- @@ -197,8 +198,7 @@ } }, 100) }); - } - else { + } else { throwEx('Routes without a callback are not supported. Please define a function like .route("YourRoute", function() { }).'); } } @@ -277,8 +277,7 @@ var filterVal = checkEmpty(jsToOdata(filterStr)); if (isQuery('$filter')) { appendQuery('$filter', filterVal, filterVal); - } - else { + } else { addQuery('$filter', filterVal, filterVal); } return (base); @@ -291,8 +290,7 @@ var filterVal = res + '/any(x:x/' + jsToOdata(checkEmpty(filter)) + ')' if (isQuery('$filter')) { appendQuery('$filter', filterVal, filterVal); - } - else { + } else { addQuery('$filter', filterVal, filterVal); } return (base); @@ -331,9 +329,11 @@ // +++ base.count = function () { if (base.oConfig.version >= 4) { - resource.path.push({ resource: '$count', get: null }); - } - else { + resource.path.push({ + resource: '$count', + get: null + }); + } else { removeQuery('$format'); addQuery('$count', 'count'); } @@ -349,8 +349,7 @@ if (!isQueryThrowEx('$count')) { addQuery('$count', countOption); } - } - else { + } else { countOption = countOption || 'allpages'; if (!isQueryThrowEx('$inlinecount')) { addQuery('$inlinecount', countOption); @@ -382,7 +381,7 @@ base.loading = function (func1, func2) { func2 = func2 || func1; if (!func1) - overideLoading = [function () { }, function () { }]; + overideLoading = [function () {}, function () {}]; else { overideLoading = [func1, func2]; } @@ -406,17 +405,27 @@ if (base.oConfig.version < 4) { resource.method = 'POST'; resource.path.push('$links'); - resource.path.push({ resource: navPath, get: null }); - } - else { + resource.path.push({ + resource: navPath, + get: null + }); + } else { resource.method = 'POST'; - resource.path.push({ resource: navPath, get: null }); - resource.path.push({ resource: '$ref', get: null }); + resource.path.push({ + resource: navPath, + get: null + }); + resource.path.push({ + resource: '$ref', + get: null + }); } var newResource = parseUri(res || navPath); newResource.path[newResource.path.length - 1].get = id; var baseRes = buildQuery(newResource); - resource.data = { '@odata.id': baseRes }; + resource.data = { + '@odata.id': baseRes + }; return (base); } @@ -436,12 +445,20 @@ if (base.oConfig.version < 4) { resource.method = 'POST'; resource.path.push('$links'); - resource.path.push({ resource: navPath, get: null }); - } - else { + resource.path.push({ + resource: navPath, + get: null + }); + } else { resource.method = 'POST'; - resource.path.push({ resource: navPath, get: null }); - resource.path.push({ resource: '$ref', get: null }); + resource.path.push({ + resource: navPath, + get: null + }); + resource.path.push({ + resource: '$ref', + get: null + }); } if (id) { var newResource = parseUri(res || navPath); @@ -603,8 +620,7 @@ if (!isQueryThrowEx('$search')) { addQuery('$search', searchStr, searchStr); } - } - else { + } else { if (!isQueryThrowEx('$filter')) { addQuery('$filter', searchStr, searchStr, '$search'); } @@ -653,12 +669,10 @@ if (typeof Q !== 'undefined') { var p = Q; return (p); - } - else if (typeof window === 'undefined') { + } else if (typeof window === 'undefined') { var p = require('q'); return (p); - } - else { + } else { return (null); } } @@ -668,7 +682,8 @@ // +++ function buildFilterByData(column, filterList, operation, combine) { if (isArray(filterList)) { - var filterStr = "", arr = []; + var filterStr = "", + arr = []; for (i = 0; i < filterList.length; ++i) { arr[i] = '(' + column + ' ' + operation + ' ' + filterList[i][column] + ')'; } @@ -693,13 +708,11 @@ for (var m = 0; m < searchWordSplit.length; m++) { if (base.oConfig.version == 4) { wordArr.push(searchFunc + '(' + searchColumns[i] + ',\'' + searchWordSplit[m] + '\')'); - } - else { + } else { wordArr.push(searchFunc + '(\'' + searchWordSplit[m] + '\',' + searchColumns[i] + ')'); } } - } - else { + } else { wordArr.push(searchColumns[i] + ' ' + searchFunc + ' \'' + searchWord + '\''); } columnArr.push('(' + wordArr.join(' and ') + ')'); @@ -783,8 +796,7 @@ param[prop.substring(1)] = matches[i]; i++; } - } - else { + } else { for (var i = 1; i < matches.length; i++) { internalParam[':' + (i - 1)] = matches[i]; param[(i - 1)] = matches[i]; @@ -825,7 +837,10 @@ } routeRegex = new RegExp('^' + routeArr.join('/') + '$'); } - return ({ regex: routeRegex, param: param }); + return ({ + regex: routeRegex, + param: param + }); } // +++ @@ -834,8 +849,7 @@ function deepCopy(obj) { if (JSON) { return (JSON.parse(JSON.stringify(obj))); - } - else { + } else { throwEx('No JSON Support.'); } } @@ -901,10 +915,14 @@ //create a CORS ajax Request if (resourceList.length === 0 && !isSave) { - startAjaxReq(createCORSRequest('GET', buildQuery()), null, callback, errorCallback, false, - [ - { name: 'Accept', value: 'application/json,text/plain' }, - { name: 'Content-Type', value: 'application/json' } + startAjaxReq(createCORSRequest('GET', buildQuery()), null, callback, errorCallback, false, [{ + name: 'Accept', + value: 'application/json,text/plain' + }, + { + name: 'Content-Type', + value: 'application/json' + } ], param, resource.progress); } @@ -917,10 +935,14 @@ var ajaxReq = createCORSRequest(resource.method, buildQuery()); //check if we only have one request or we need to force batch because of isXDomainRequest if ((countMethod(['POST', 'PATCH', 'DELETE', 'PUT']) <= 1 && isSave) && !isXDomainRequest) { - startAjaxReq(ajaxReq, stringify(resource.data), callback, errorCallback, false, - [ - { name: 'Accept', value: 'application/json' }, - { name: 'Content-Type', value: 'application/json' } + startAjaxReq(ajaxReq, stringify(resource.data), callback, errorCallback, false, [{ + name: 'Accept', + value: 'application/json' + }, + { + name: 'Content-Type', + value: 'application/json' + } ], param, resourceList[resourceList.length - 1].progress); // because the post/put/delete is done, we remove the resource to assume that it will not be posted again @@ -942,7 +964,10 @@ // start the request startAjaxReq(createCORSRequest('POST', endpoint), buildBatchBody(guid, isSave), callback, errorCallback, true, // add the necessary headers - [{ name: 'Content-Type', value: 'multipart/mixed; boundary=batch_' + guid }], + [{ + name: 'Content-Type', + value: 'multipart/mixed; boundary=batch_' + guid + }], param, resourceList[resourceList.length - 1].progress); if (isSave) { // because the post/put/delete is done, we remove the resource to assume that it will not be posted again @@ -974,8 +999,7 @@ //Check if we have a endpoint and save it to the var if ((res.toUpperCase().indexOf('HTTP://') > -1 || res.toUpperCase().indexOf('HTTPS://') > -1)) { isEndpoint = false; - } - else { + } else { //check if endpoint is defined if (!base.oConfig.endpoint) { throwEx('You can not use resource query without defining your oData endpoint. Use o().config({endpoint:yourEndpoint}) to define your oData endpoint.'); @@ -998,8 +1022,7 @@ if (isQuery('$expand')) { resource.queryList[resource.query.$expand].value += ',' + expandStr; resource.queryList[resource.query.$expand].original = resource.queryList[resource.query.$expand].value; - } - else { + } else { addQuery('$expand', expandStr, expandStr); } } @@ -1028,7 +1051,10 @@ var querySplit = query.split('&'); for (var i = 0; i < querySplit.length; i++) { var pair = querySplit[i].split('='); - reqObj.queryList.push({ name: pair[0], value: pair[1] }); + reqObj.queryList.push({ + name: pair[0], + value: pair[1] + }); reqObj.query[pair[0]] = reqObj.queryList.length - 1; } } @@ -1038,14 +1064,18 @@ for (var i = 0; i < uriSplit.length; i++) { if (startsWith(uriSplit[i], '$') && uriSplit[i] !== '$link') { reqObj.appending = uriSplit[i]; - } - else { + } else { var index = uriSplit[i].split('('); if (index.length === 1 || startsWith(uriSplit[i], '(')) { - reqObj.path.push({ 'resource': uriSplit[i], 'get': null }); - } - else { - reqObj.path.push({ 'resource': index[0], 'get': index[1].substring(0, index[1].length - 1) }); + reqObj.path.push({ + 'resource': uriSplit[i], + 'get': null + }); + } else { + reqObj.path.push({ + 'resource': index[0], + 'get': index[1].substring(0, index[1].length - 1) + }); } } } @@ -1058,7 +1088,11 @@ // +++ function addQuery(queryName, queryValue, queryOriginal, queryPseudonym) { queryOriginal = queryOriginal || null; - resource.queryList.push({ name: queryName, value: queryValue, original: queryOriginal }); + resource.queryList.push({ + name: queryName, + value: queryValue, + original: queryOriginal + }); resource.query[queryPseudonym || queryName] = resource.queryList.length - 1; } @@ -1129,7 +1163,7 @@ } // +++ - // Checks if a value is positiv and a integer + // Checks if a value is positive and an integer // +++ function checkEmpty(str, throwName) { if (typeof str !== 'undefined' && str !== null && str.length > 0) @@ -1344,8 +1378,7 @@ ajaxRequest.status = 400; ajaxRequest.onreadystatechange(); }; - } - else if (typeof progress === 'function') { + } else if (typeof progress === 'function') { ajaxRequest.onprogress = progress; } @@ -1354,6 +1387,9 @@ if (ajaxRequest.readyState === 4) { if (ajaxRequest.status >= 200 && ajaxRequest.status < 300) { + //attaching the client + tempBase.xhr = ajaxRequest; + //dealing with the response if (ajaxRequest.status !== 204) { if (!isBatch) { @@ -1385,8 +1421,7 @@ if (typeof callback === 'function') { callback.call(tempBase, tempBase.data, param); } - } - else { + } else { try { var errResponse = ajaxRequest.responseText; @@ -1396,20 +1431,17 @@ if (errResponse !== '' && errResponse['odata.error']) { var errorMsg = errResponse['odata.error'].message.value + ' | HTTP Status: ' + ajaxRequest.status + ' | oData Code: ' + errResponse['odata.error'].code; throwEx(errorMsg); - } - else { + } else { throwEx('Request to ' + buildQuery() + ' failed with HTTP status ' + (ajaxRequest.status || 404) + '.'); } } catch (ex) { endLoading(tempBase, true, ajaxRequest.status || 404, ajaxRequest.responseText); if (typeof errorCallback === 'function') { errorCallback(ajaxRequest.status || 404, ex) - } - else if (currentPromise) { + } else if (currentPromise) { ex.status = (ajaxRequest.status || 404); currentPromise.reject(ex); - } - else { + } else { throw ex; } } @@ -1423,7 +1455,7 @@ if (base.oConfig.username && base.oConfig.password) { //ajaxRequest.withCredentials=true; if (isXDomainRequest) { - throwEx('CORS and Basic Auth is not supported for IE <= 9. Try to set isCors:false in the OData config if you do not need CORS support.'); + throwEx('CORS and Basic Auth is not supported for IE <= 9. Try to set isWithCredentials:false in the OData config if you do not need CORS support.'); } ajaxRequest.setRequestHeader('Authorization', 'Basic ' + encodeBase64(base.oConfig.username + ':' + base.oConfig.password)); } @@ -1477,28 +1509,24 @@ var count = tryParseInt(response, -1); if (count !== -1) { tempBase.data = count; - } - else { + } else { if (JSON && response !== '') { var data = JSON.parse(response); tempBase.raw = data; if (data.hasOwnProperty('value')) { if (isQuery(['$first']) && data.value.length && data.value.length <= 1) { tempBase.data = data.value[0]; - } - else { + } else { tempBase.data = data.value; } if (data.hasOwnProperty('odata.count') || data.hasOwnProperty('@odata.count')) { tempBase.inlinecount = data['odata.count'] || data['@odata.count']; } - } - else { + } else { tempBase.data = data; } resource.data = tempBase.data; - } - else { + } else { tempBase.data = response; } } @@ -1515,17 +1543,15 @@ if (typeof window === 'undefined') { var Xhr2 = require('xhr2'); xhr = new Xhr2(); - } - else { + } else { xhr = new XMLHttpRequest(); } if (base.oConfig.isCors && 'withCredentials' in xhr) { - xhr.withCredentials=true; + xhr.withCredentials = true; // XHR for Chrome/Firefox/Opera/Safari. xhr.open(method, url, base.oConfig.isAsync); - } - else if (base.oConfig.isCors && typeof XDomainRequest !== 'undefined') { + } else if (base.oConfig.isCors && typeof XDomainRequest !== 'undefined') { // XDomainRequest for IE. xhr = new XDomainRequest(); // does not support PUT PATCH operations -> Switch to batch @@ -1534,8 +1560,7 @@ xhr.open(method, url); else xhr.open('POST', url); - } - else { + } else { // CORS not supported or forced xhr.open(method, url, base.oConfig.isAsync); } @@ -1612,12 +1637,10 @@ if (c < 128) { utftext += String.fromCharCode(c); - } - else if ((c > 127) && (c < 2048)) { + } else if ((c > 127) && (c < 2048)) { utftext += String.fromCharCode((c >> 6) | 192); utftext += String.fromCharCode((c & 63) | 128); - } - else { + } else { utftext += String.fromCharCode((c >> 12) | 224); utftext += String.fromCharCode(((c >> 6) & 63) | 128); utftext += String.fromCharCode((c & 63) | 128); @@ -1636,4 +1659,4 @@ } return o; -})); +})); \ No newline at end of file diff --git a/o.min.js b/o.min.js index e3abd36..b279ee4 100644 --- a/o.min.js +++ b/o.min.js @@ -1 +1 @@ -!function(e,n){"function"==typeof define&&define.amd?define(["q"],n):"object"==typeof exports?module.exports=n(require("q")):e.o=n(e.Q)}(this,function(e){function n(e){function n(){for(var e,n={},t=0,r=arguments.length;r>t;t++)for(e in arguments[t])arguments[t].hasOwnProperty(e)&&(n[e]=arguments[t][e]);return n}var r=this;return r.oConfig=r.oConfig||{endpoint:null,format:"json",autoFormat:!0,version:4,strictMode:!0,start:null,ready:null,error:null,headers:[],username:null,password:null,isAsync:!0,isCors:!0,openAjaxRequests:0,isHashRoute:!0,appending:""},r.config=function(e){r.oConfig=n(r.oConfig,e)},r.isEndpoint=function(){return null!==r.oConfig.endpoint},"undefined"==typeof e?r:new t(e,r.oConfig)}function t(n,t){function r(){if("undefined"!=typeof e){var n=e;return n}if("undefined"==typeof window){var n=require("q");return n}return null}function o(e,n,t,r){if(R(n)){var o="",a=[];for(i=0;i<n.length;++i)a[i]="("+e+" "+t+" "+n[i][e]+")";return o=a.join(" "+r+" ")}return""}function a(e,n,t){t=t||(4==B.oConfig.version?"contains":"substringof");for(var r=n.split(" "),o="contains"===t||"substringof"===t,a=[],i=0;i<e.length;i++){var u=[];if(o)for(var s=0;s<r.length;s++)4==B.oConfig.version?u.push(t+"("+e[i]+",'"+r[s]+"')"):u.push(t+"('"+r[s]+"',"+e[i]+")");else u.push(e[i]+" "+t+" '"+n+"'");a.push("("+u.join(" and ")+")")}return a.join("or")}function u(e){var n=e||U;n&&0!==n.path.length||k('No resource defined. Define a resource first with o("YourResourcePath").');var t="";I&&(t=B.oConfig.endpoint+(b(B.oConfig.endpoint,"/")?"":"/"));for(var r=0;r<n.path.length;r++)t+=n.path[r].resource,n.path[r].get&&(t+="("+(W[n.path[r].get]||n.path[r].get)+")"),t+="/";return n.appending||(t=t.slice(0,-1)),t+n.appending+s()}function s(){var e="";for(queryName in U.query)U.query.hasOwnProperty(queryName)&&null!=U.query[queryName]&&(e+="&"+U.queryList[U.query[queryName]].name+"="+J(U.queryList[U.query[queryName]].value,W));return e.length>0?"?"+e.substring(1):""}function f(e){for(var n=0;n<M.length;n++)if(M[n].route.regex.test(e)){W={};var t={},r=M[n].route.regex.exec(e);if("undefined"!=typeof M[n].route.param){var o=1;for(prop in M[n].route.param)W[prop]=r[o],t[prop.substring(1)]=r[o],o++}else for(var o=1;o<r.length;o++)W[":"+(o-1)]=r[o],t[o-1]=r[o];Q(t)||g(M[n].callback,t),B.param=t}}function p(e){var n=e;if(!(e instanceof RegExp)){B.oConfig.isHashRoute&&!N(e,"#")&&(e="#"+e);for(var t=e.split("/"),r={},o=0;o<t.length;o++)N(t[o],":")&&(r[t[o]]=!0,t[o]="([\\w| |-]+|\\[W| |-]+)");n=new RegExp("^"+t.join("/")+"$")}return{regex:n,param:r}}function l(e){return JSON?JSON.parse(JSON.stringify(e)):void k("No JSON Support.")}function d(e){var n=new RegExp("'.*?'",""),t=n.exec(e);e=e.replace(n,"{0}");for(key in K)e=e.split(key).join(" "+K[key]+" ");if(null!=t)for(var r=0;r<t.length;r++)e=e.replace("{0}",t[r]);return e}function h(e){U&&G.push(U),U="string"==typeof e?m(e):e,!w("$format")&&B.oConfig.autoFormat&&C("$format",B.oConfig.format);for(var n=0;n<B.oConfig.appending.length;n++)C(B.oConfig.appending[n].name,B.oConfig.appending[n].value)}function c(e,n,t,r){if(null===U&&k('You must define a resource to perform a get(), post(), put() or delete() function. Define a resource with o("YourODataResource").'),0!==G.length||t){G.push(U);var o=_(U.method,u());if(P(["POST","PATCH","DELETE","PUT"])<=1&&t&&!z)j(o,A(U.data),e,n,!1,[{name:"Accept",value:"application/json"},{name:"Content-Type",value:"application/json"}],r,G[G.length-1].progress),E(["POST","PATCH","DELETE","PUT"]);else{for(var a=$(),i=B.oConfig.endpoint+(b(B.oConfig.endpoint,"/")?"":"/")+"$batch",s=0;s<B.oConfig.appending.length;s++)i+=(0===s?"?":"&")+B.oConfig.appending[s].name+"="+B.oConfig.appending[s].value;j(_("POST",i),L(a,t),e,n,!0,[{name:"Content-Type",value:"multipart/mixed; boundary=batch_"+a}],r,G[G.length-1].progress),t&&E(["POST","PUT","DELETE"])}}else j(_("GET",u()),null,e,n,!1,[{name:"Accept",value:"application/json,text/plain"},{name:"Content-Type",value:"application/json"}],r,U.progress)}function g(e,n){""!==U.path[0].resource?c(e,null,!1,n):e.call(B,n)}function y(e){return"undefined"==typeof e?B:(e.toUpperCase().indexOf("HTTP://")>-1||e.toUpperCase().indexOf("HTTPS://")>-1?I=!1:B.oConfig.endpoint||k("You can not use resource query without defining your oData endpoint. Use o().config({endpoint:yourEndpoint}) to define your oData endpoint."),routeName=e,h(e),B)}function v(e){w("$expand")?(U.queryList[U.query.$expand].value+=","+e,U.queryList[U.query.$expand].original=U.queryList[U.query.$expand].value):C("$expand",e,e)}function m(e){var n=e.split("?"),t=e,r="",o={path:[],appending:"",query:{},queryList:[],method:"GET",data:null,progress:null};if(2===n.length){t=n[0],r=n[1];for(var a=r.split("&"),i=0;i<a.length;i++){var u=a[i].split("=");o.queryList.push({name:u[0],value:u[1]}),o.query[u[0]]=o.queryList.length-1}}for(var s=t.split("/"),i=0;i<s.length;i++)if(N(s[i],"$")&&"$link"!==s[i])o.appending=s[i];else{var f=s[i].split("(");1===f.length||N(s[i],"(")?o.path.push({resource:s[i],get:null}):o.path.push({resource:f[0],get:f[1].substring(0,f[1].length-1)})}return o}function C(e,n,t,r){t=t||null,U.queryList.push({name:e,value:n,original:t}),U.query[r||e]=U.queryList.length-1}function x(e,n,t,r,o){t=t||null,r=r||" or ",e=o||e,U.queryList[U.query[e]].value="("+U.queryList[U.query[e]].value+")"+r+"("+n+")",t&&(U.queryList[U.query[e]].original=U.queryList[U.query[e]].value)}function q(e){U.query[e]=null}function T(e){if(w(e)){var n=e;return R(n)&&(n=n.join(",")),k("There is already a depending query. You can not use them together/twice: "+n),!0}return!1}function w(e){for(var n=R(e)?e:[e],t=!1,r=0;r<n.length;r++)U.query.hasOwnProperty(n[r])&&(t=!0);return t}function $(){var e=(new Date).getTime(),n="xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,function(n){var t=(e+16*Math.random())%16|0;return e=Math.floor(e/16),("x"==n?t:7&t|8).toString(16)});return n}function S(e,n){return"undefined"!=typeof e&&null!==e&&e.length>0?e:void k(n+": Parameter must be set.")}function O(e,n){if("number"==typeof e)return e;var t=n;return e&&e.length>0&&(isNaN(e)||(t=parseInt(e))),t}function P(e){for(var n=0,t=0;t<G.length;t++)e.indexOf(G[t].method)>-1&&n++;return n}function E(e){for(var n=[],t=0;t<G.length;t++)e.indexOf(G[t].method)>-1&&n.push(t);for(var t=n.length-1;t>=0;t--)G.splice(n[t],1);G[0]&&(U=G[0])}function A(e){return JSON?JSON.stringify(e):(k("No JSON support."),e)}function R(e){return"undefined"==typeof Array.isArray?"[object Array]"===e.toString():Array.isArray(e)}function b(e,n){return e?-1!==e.indexOf(n,e.length-n.length):!1}function N(e,n){return 0===e.indexOf(n)}function k(e){function n(e){this.message=e,this.name="o.js exception"}if(n.prototype=new Error,B.oConfig.strictMode===!0)throw new n(e);console.log("o.js exception: "+e)}function L(e,n){var t="",r=$(),o=!1;n&&(t+="--batch_"+e+"\n",t+="Content-Type: multipart/mixed; boundary=changeset_"+r+"\n\n");var a=null;null!==B.oConfig.endpoint&&(a=B.oConfig.endpoint.indexOf("://")>-1?B.oConfig.endpoint.split("/")[2]:B.oConfig.endpoint.split("/")[0],a=a.split(":")[0]);for(var i=0;i<G.length;i++){var s=G[i];if(U=s,"GET"!==s.method||n){if(("POST"===s.method||"PUT"===s.method||"PATCH"===s.method||"DELETE"===s.method)&&n){t+="--changeset_"+r+"\n",t+="Content-Type: application/http\n",t+="Content-Transfer-Encoding: binary\n",t+="Content-ID:"+i+"1\n\n",t+=s.method+" "+u(s)+" HTTP/1.1\n",t+="Host: "+a+"\n";for(var f=0;f<B.oConfig.headers.length;f++){var p=B.oConfig.headers[f];t+=p.name+": "+p.value+"\n"}t+="Content-Type: application/json\n",t+="\n"+A(U.data)+"\n\n\n",o=!0}}else{t+="--batch_"+e+"\n",t+="Content-Type: application/http\n",t+="Content-Transfer-Encoding: binary\n\n",t+=s.method+" "+u(s)+" HTTP/1.1\n",t+="Host: "+a+"\n";for(var f=0;f<B.oConfig.headers.length;f++){var p=B.oConfig.headers[f];t+=p.name+": "+p.value+"\n"}t+="\n"}}return o&&(t+="--changeset_"+r+"--\n\n"),t+="--batch_"+e+"--"}function j(e,n,t,r,o,a,i,s){B.oConfig.start&&null==F&&(B.oConfig.openAjaxRequests++,B.oConfig.start()),F&&F[0]&&F[0](!0);var f=B;if(z?(e.onload=function(n){e.readyState=4,e.status=200,e.onreadystatechange()},e.onerror=function(n){e.readyState=0,e.status=400,e.onreadystatechange()}):"function"==typeof s&&(e.onprogress=s),e.onreadystatechange=function(){if(4===e.readyState){if(e.status>=200&&e.status<300){if(204!==e.status)if(o){var n,a=[],s=/({[\s\S]*?--batchresponse_)/g;do n=s.exec(e.responseText),n&&(D(n[0].substring(0,n[0].length-16),f),a.push(f.data));while(n);f.data=a}else D(e.responseText,f);X&&X.resolve(f),"function"==typeof t&&t.call(f,f.data,i)}else try{var p=e.responseText;if(JSON&&""!=e.responseText&&(p=JSON.parse(e.responseText)),""!==p&&p["odata.error"]){var l=p["odata.error"].message.value+" | HTTP Status: "+e.status+" | oData Code: "+p["odata.error"].code;k(l)}else k("Request to "+u()+" failed with HTTP status "+(e.status||404)+".")}catch(d){if(H(f,!0,e.status||404,e.responseText),"function"==typeof r)r(e.status||404,d);else{if(!X)throw d;d.status=e.status||404,X.reject(d)}}H(f,!1)}},B.oConfig.username&&B.oConfig.password&&(z&&k("CORS and Basic Auth is not supported for IE <= 9. Try to set isCors:false in the OData config if you do not need CORS support."),e.setRequestHeader("Authorization","Basic "+Y(B.oConfig.username+":"+B.oConfig.password))),!z){if(a)for(var p=0;p<a.length;p++)e.setRequestHeader(a[p].name,a[p].value);if(B.oConfig.headers.length>0)for(var p=0;p<B.oConfig.headers.length;p++)e.setRequestHeader(B.oConfig.headers[p].name,B.oConfig.headers[p].value)}e.send(n)}function H(e,n,t,r){e.oConfig.ready&&null==F&&(e.oConfig.openAjaxRequests--,e.oConfig.openAjaxRequests<=0&&e.oConfig.ready()),F&&F[1]&&F[1](!1),e.oConfig.error&&n&&e.oConfig.error(t,r)}function D(e,n){var t=O(e,-1);if(-1!==t)n.data=t;else if(JSON&&""!==e){var r=JSON.parse(e);n.raw=r,r.hasOwnProperty("value")?(w(["$first"])&&r.value.length&&r.value.length<=1?n.data=r.value[0]:n.data=r.value,(r.hasOwnProperty("odata.count")||r.hasOwnProperty("@odata.count"))&&(n.inlinecount=r["odata.count"]||r["@odata.count"])):n.data=r,U.data=n.data}else n.data=e}function _(e,n){var t=null;if("undefined"==typeof window){var r=require("xhr2");t=new r}else t=new XMLHttpRequest;return B.oConfig.isCors&&"withCredentials"in t?(t.withCredentials=!0,t.open(e,n,B.oConfig.isAsync)):B.oConfig.isCors&&"undefined"!=typeof XDomainRequest?(t=new XDomainRequest,z=!0,"GET"==e?t.open(e,n):t.open("POST",n)):t.open(e,n,B.oConfig.isAsync),t}function J(){var e=arguments[0],n=arguments[1];for(var t in n){var r=new RegExp(t,"g");"string"==typeof e&&(e=e.replace(r,n[t]))}return e}function Y(e){var n={_keyStr:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",encode:function(e){var t,r,o,a,i,u,s,f="",p=0;for(e=n._utf8_encode(e);p<e.length;)t=e.charCodeAt(p++),r=e.charCodeAt(p++),o=e.charCodeAt(p++),a=t>>2,i=(3&t)<<4|r>>4,u=(15&r)<<2|o>>6,s=63&o,isNaN(r)?u=s=64:isNaN(o)&&(s=64),f=f+this._keyStr.charAt(a)+this._keyStr.charAt(i)+this._keyStr.charAt(u)+this._keyStr.charAt(s);return f},_utf8_encode:function(e){e=e.replace(/\r\n/g,"\n");for(var n="",t=0;t<e.length;t++){var r=e.charCodeAt(t);128>r?n+=String.fromCharCode(r):r>127&&2048>r?(n+=String.fromCharCode(r>>6|192),n+=String.fromCharCode(63&r|128)):(n+=String.fromCharCode(r>>12|224),n+=String.fromCharCode(r>>6&63|128),n+=String.fromCharCode(63&r|128))}return n}};return n.encode(e)}var B=this,U=null,G=[],M=[],I=!0,X=null,F=null,z=!1,Q=function(){},W={},K={"==":"eq","===":"eq","!=":"ne","!==":"ne",">":"gt",">=":"ge","<":"lt","<=":"le","&&":"and","||":"or","!":"not","*":"mul","%":"mod"};return B.data=[],B.inlinecount=null,B.param={},B.oConfig=t,B.raw=null,B.routes=B.route=function(e,n){R(e)||(e=[e]),"undefined"==typeof window&&k("Routes are only supported in a browser env.");for(var t=window.location.hash,r=0;r<e.length;r++)"undefined"!=typeof n?M.push({name:e[r],route:p(e[r]),callback:n,param:{},interval:setInterval(function(){window.location.hash!=t&&(t=window.location.hash,f(window.location.hash))},100)}):k('Routes without a callback are not supported. Please define a function like .route("YourRoute", function() { }).');return B.triggerRoute(window.location.hash),B},B.beforeRouting=function(e){return Q=e,B},B.isEndpoint=function(){return I},B.triggerRoute=function(e){return f(e),B},B.find=function(e){return U.path[U.path.length-1].get=e,B},B.top=B.take=function(e){return T(["$top"])||C("$top",e,e),B},B.skip=function(e){return T("$skip")||C("$skip",e,e),B},B.first=function(){return T(["$top","$first"])||C("$top",1,null,"$first"),B},B.filter=B.where=function(e){var n=S(d(e));return w("$filter")?x("$filter",n,n):C("$filter",n,n),B},B.any=function(e,n){var t=e+"/any(x:x/"+d(S(n))+")";return w("$filter")?x("$filter",t,t):C("$filter",t,t),B},B.orderBy=function(e,n){return"undefined"==typeof n&&(n="asc"),T("$orderby")||C("$orderby",S(e)+" "+n),B},B.orderByDesc=function(e){return B.orderBy(e,"desc")},B.select=function(e){return C("$select",S(e)),B},B.count=function(){return B.oConfig.version>=4?U.path.push({resource:"$count",get:null}):(q("$format"),C("$count","count")),B},B.inlineCount=function(e){return B.oConfig.version>=4?(e=e||"true",T("$count")||C("$count",e)):(e=e||"allpages",T("$inlinecount")||C("$inlinecount",e)),B},B.batch=function(e){return h(e),B},B.expand=function(e){return v(e),B},B.loading=function(e,n){return n=n||e,F=e?[e,n]:[function(){},function(){}],B},B.ref=B.link=function(e,n,t){t||(t=n,n=null),q("$format"),(null==U||U.get)&&k("You need to define a resource with the find() method to append an navigation property"),B.oConfig.version<4?(U.method="POST",U.path.push("$links"),U.path.push({resource:e,get:null})):(U.method="POST",U.path.push({resource:e,get:null}),U.path.push({resource:"$ref",get:null}));var r=m(n||e);r.path[r.path.length-1].get=t;var o=u(r);return U.data={"@odata.id":o},B},B.removeRef=B.deleteRef=function(e,n,t){if(t||(t=n,n=null),q("$format"),(null==U||U.get)&&k("You need to define a resource with the find() method to append an navigation property"),B.oConfig.version<4?(U.method="POST",U.path.push("$links"),U.path.push({resource:e,get:null})):(U.method="POST",U.path.push({resource:e,get:null}),U.path.push({resource:"$ref",get:null})),t){var r=m(n||e);r.path[r.path.length-1].get=t;var o=u(r);C("$id",o)}return U.method="DELETE",B},B.get=function(e,n){var t=r();return t&&"undefined"==typeof e&&(X=t.defer()),c(e,n,!1),t&&"undefined"==typeof e?X.promise:B},B.save=function(e,n){if("GET"===U.method&&null!==U.data){var t=l(U);t.method="PATCH",t.data=U.data,h(t)}var o=r();return o&&"undefined"==typeof e?(X=o.defer(),c(e,n,!0),X.promise):(c(e,n,!0),B)},B.post=function(e,n){return q("$format"),n&&h(n),U.method="POST",U.data=e,B},B.patch=function(e,n){return n&&h(n),U.path[U.path.length-1]&&U.path[U.path.length-1].get||k("Bulk updates are not supported. You need to query a unique resource with find() to patch/put it."),U.method="PATCH",U.data=e,B},B.put=function(e,n){return n&&h(n),U.path[U.path.length-1]&&U.path[U.path.length-1].get||k("Bulk updates are not supported. You need to query a unique resource with find() to patch/put it."),U.method="PUT",U.data=e,B},B.remove=B["delete"]=function(e){return e&&h(e),U.path[U.path.length-1]&&U.path[U.path.length-1].get||k("Bulk deletes are not supported. You need to query a unique resource with find() to delete it."),U.method="DELETE",B},B.query=function(e){return u(e)},B.search=function(e,n,t,r){var o=a(e,n,t);return 4==B.oConfig.version&&r?T("$search")||C("$search",o,o):T("$filter")||C("$filter",o,o,"$search"),B},B.filterByList=B.exclude=function(e,n){if(!T("$filter")){var t=o(e,n,K["!="],K["&&"]);C("$filter",S(t),t)}return B},B.include=function(e,n){if(!T("$filter")){var t=o(e,n,K["=="],K["||"]);C("$filter",S(t),t)}return B},B.progress=function(e){return null!=U&&(U.progress=e),B},y(n)}return n}); \ No newline at end of file +!function(e,n){"function"==typeof define&&define.amd?define(["q"],n):"object"==typeof exports?module.exports=n(require("q")):e.o=n(e.Q)}(this,function(e){function n(e){function n(){for(var e,n={},t=0,r=arguments.length;r>t;t++)for(e in arguments[t])arguments[t].hasOwnProperty(e)&&(n[e]=arguments[t][e]);return n}var r=this;return r.oConfig=r.oConfig||{endpoint:null,format:"json",autoFormat:!0,version:4,strictMode:!0,start:null,ready:null,error:null,headers:[],username:null,password:null,isAsync:!0,isWithCredentials:!1,openAjaxRequests:0,isHashRoute:!0,appending:""},r.config=function(e){r.oConfig=n(r.oConfig,e)},r.isEndpoint=function(){return null!==r.oConfig.endpoint},"undefined"==typeof e?r:new t(e,r.oConfig)}function t(n,t){function r(){if("undefined"!=typeof e){var n=e;return n}if("undefined"==typeof window){var n=require("q");return n}return null}function o(e,n,t,r){if(R(n)){var o="",a=[];for(i=0;i<n.length;++i)a[i]="("+e+" "+t+" "+n[i][e]+")";return o=a.join(" "+r+" ")}return""}function a(e,n,t){t=t||(4==B.oConfig.version?"contains":"substringof");for(var r=n.split(" "),o="contains"===t||"substringof"===t,a=[],i=0;i<e.length;i++){var u=[];if(o)for(var s=0;s<r.length;s++)4==B.oConfig.version?u.push(t+"("+e[i]+",'"+r[s]+"')"):u.push(t+"('"+r[s]+"',"+e[i]+")");else u.push(e[i]+" "+t+" '"+n+"'");a.push("("+u.join(" and ")+")")}return a.join("or")}function u(e){var n=e||U;n&&0!==n.path.length||k('No resource defined. Define a resource first with o("YourResourcePath").');var t="";I&&(t=B.oConfig.endpoint+(b(B.oConfig.endpoint,"/")?"":"/"));for(var r=0;r<n.path.length;r++)t+=n.path[r].resource,n.path[r].get&&(t+="("+(Q[n.path[r].get]||n.path[r].get)+")"),t+="/";return n.appending||(t=t.slice(0,-1)),t+n.appending+s()}function s(){var e="";for(queryName in U.query)U.query.hasOwnProperty(queryName)&&null!=U.query[queryName]&&(e+="&"+U.queryList[U.query[queryName]].name+"="+J(U.queryList[U.query[queryName]].value,Q));return e.length>0?"?"+e.substring(1):""}function f(e){for(var n=0;n<M.length;n++)if(M[n].route.regex.test(e)){Q={};var t={},r=M[n].route.regex.exec(e);if("undefined"!=typeof M[n].route.param){var o=1;for(prop in M[n].route.param)Q[prop]=r[o],t[prop.substring(1)]=r[o],o++}else for(var o=1;o<r.length;o++)Q[":"+(o-1)]=r[o],t[o-1]=r[o];z(t)||g(M[n].callback,t),B.param=t}}function l(e){var n=e;if(!(e instanceof RegExp)){B.oConfig.isHashRoute&&!N(e,"#")&&(e="#"+e);for(var t=e.split("/"),r={},o=0;o<t.length;o++)N(t[o],":")&&(r[t[o]]=!0,t[o]="([\\w| |-]+|\\[W| |-]+)");n=new RegExp("^"+t.join("/")+"$")}return{regex:n,param:r}}function p(e){return JSON?JSON.parse(JSON.stringify(e)):void k("No JSON Support.")}function d(e){var n=new RegExp("'.*?'",""),t=n.exec(e);e=e.replace(n,"{0}");for(key in K)e=e.split(key).join(" "+K[key]+" ");if(null!=t)for(var r=0;r<t.length;r++)e=e.replace("{0}",t[r]);return e}function h(e){U&&G.push(U),U="string"==typeof e?m(e):e,!w("$format")&&B.oConfig.autoFormat&&C("$format",B.oConfig.format);for(var n=0;n<B.oConfig.appending.length;n++)C(B.oConfig.appending[n].name,B.oConfig.appending[n].value)}function c(e,n,t,r){if(null===U&&k('You must define a resource to perform a get(), post(), put() or delete() function. Define a resource with o("YourODataResource").'),0!==G.length||t){G.push(U);var o=_(U.method,u());if(P(["POST","PATCH","DELETE","PUT"])<=1&&t&&!F)j(o,A(U.data),e,n,!1,[{name:"Accept",value:"application/json"},{name:"Content-Type",value:"application/json"}],r,G[G.length-1].progress),E(["POST","PATCH","DELETE","PUT"]);else{for(var a=$(),i=B.oConfig.endpoint+(b(B.oConfig.endpoint,"/")?"":"/")+"$batch",s=0;s<B.oConfig.appending.length;s++)i+=(0===s?"?":"&")+B.oConfig.appending[s].name+"="+B.oConfig.appending[s].value;j(_("POST",i),L(a,t),e,n,!0,[{name:"Content-Type",value:"multipart/mixed; boundary=batch_"+a}],r,G[G.length-1].progress),t&&E(["POST","PUT","DELETE"])}}else j(_("GET",u()),null,e,n,!1,[{name:"Accept",value:"application/json,text/plain"},{name:"Content-Type",value:"application/json"}],r,U.progress)}function g(e,n){""!==U.path[0].resource?c(e,null,!1,n):e.call(B,n)}function y(e){return"undefined"==typeof e?B:(e.toUpperCase().indexOf("HTTP://")>-1||e.toUpperCase().indexOf("HTTPS://")>-1?I=!1:B.oConfig.endpoint||k("You can not use resource query without defining your oData endpoint. Use o().config({endpoint:yourEndpoint}) to define your oData endpoint."),routeName=e,h(e),B)}function v(e){w("$expand")?(U.queryList[U.query.$expand].value+=","+e,U.queryList[U.query.$expand].original=U.queryList[U.query.$expand].value):C("$expand",e,e)}function m(e){var n=e.split("?"),t=e,r="",o={path:[],appending:"",query:{},queryList:[],method:"GET",data:null,progress:null};if(2===n.length){t=n[0],r=n[1];for(var a=r.split("&"),i=0;i<a.length;i++){var u=a[i].split("=");o.queryList.push({name:u[0],value:u[1]}),o.query[u[0]]=o.queryList.length-1}}for(var s=t.split("/"),i=0;i<s.length;i++)if(N(s[i],"$")&&"$link"!==s[i])o.appending=s[i];else{var f=s[i].split("(");1===f.length||N(s[i],"(")?o.path.push({resource:s[i],get:null}):o.path.push({resource:f[0],get:f[1].substring(0,f[1].length-1)})}return o}function C(e,n,t,r){t=t||null,U.queryList.push({name:e,value:n,original:t}),U.query[r||e]=U.queryList.length-1}function x(e,n,t,r,o){t=t||null,r=r||" or ",e=o||e,U.queryList[U.query[e]].value="("+U.queryList[U.query[e]].value+")"+r+"("+n+")",t&&(U.queryList[U.query[e]].original=U.queryList[U.query[e]].value)}function q(e){U.query[e]=null}function T(e){if(w(e)){var n=e;return R(n)&&(n=n.join(",")),k("There is already a depending query. You can not use them together/twice: "+n),!0}return!1}function w(e){for(var n=R(e)?e:[e],t=!1,r=0;r<n.length;r++)U.query.hasOwnProperty(n[r])&&(t=!0);return t}function $(){var e=(new Date).getTime(),n="xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,function(n){var t=(e+16*Math.random())%16|0;return e=Math.floor(e/16),("x"==n?t:7&t|8).toString(16)});return n}function S(e,n){return"undefined"!=typeof e&&null!==e&&e.length>0?e:void k(n+": Parameter must be set.")}function O(e,n){if("number"==typeof e)return e;var t=n;return e&&e.length>0&&(isNaN(e)||(t=parseInt(e))),t}function P(e){for(var n=0,t=0;t<G.length;t++)e.indexOf(G[t].method)>-1&&n++;return n}function E(e){for(var n=[],t=0;t<G.length;t++)e.indexOf(G[t].method)>-1&&n.push(t);for(var t=n.length-1;t>=0;t--)G.splice(n[t],1);G[0]&&(U=G[0])}function A(e){return JSON?JSON.stringify(e):(k("No JSON support."),e)}function R(e){return"undefined"==typeof Array.isArray?"[object Array]"===e.toString():Array.isArray(e)}function b(e,n){return e?-1!==e.indexOf(n,e.length-n.length):!1}function N(e,n){return 0===e.indexOf(n)}function k(e){function n(e){this.message=e,this.name="o.js exception"}if(n.prototype=new Error,B.oConfig.strictMode===!0)throw new n(e);console.log("o.js exception: "+e)}function L(e,n){var t="",r=$(),o=!1;n&&(t+="--batch_"+e+"\n",t+="Content-Type: multipart/mixed; boundary=changeset_"+r+"\n\n");var a=null;null!==B.oConfig.endpoint&&(a=B.oConfig.endpoint.indexOf("://")>-1?B.oConfig.endpoint.split("/")[2]:B.oConfig.endpoint.split("/")[0],a=a.split(":")[0]);for(var i=0;i<G.length;i++){var s=G[i];if(U=s,"GET"!==s.method||n){if(("POST"===s.method||"PUT"===s.method||"PATCH"===s.method||"DELETE"===s.method)&&n){t+="--changeset_"+r+"\n",t+="Content-Type: application/http\n",t+="Content-Transfer-Encoding: binary\n",t+="Content-ID:"+i+"1\n\n",t+=s.method+" "+u(s)+" HTTP/1.1\n",t+="Host: "+a+"\n";for(var f=0;f<B.oConfig.headers.length;f++){var l=B.oConfig.headers[f];t+=l.name+": "+l.value+"\n"}t+="Content-Type: application/json\n",t+="\n"+A(U.data)+"\n\n\n",o=!0}}else{t+="--batch_"+e+"\n",t+="Content-Type: application/http\n",t+="Content-Transfer-Encoding: binary\n\n",t+=s.method+" "+u(s)+" HTTP/1.1\n",t+="Host: "+a+"\n";for(var f=0;f<B.oConfig.headers.length;f++){var l=B.oConfig.headers[f];t+=l.name+": "+l.value+"\n"}t+="\n"}}return o&&(t+="--changeset_"+r+"--\n\n"),t+="--batch_"+e+"--"}function j(e,n,t,r,o,a,i,s){B.oConfig.start&&null==X&&(B.oConfig.openAjaxRequests++,B.oConfig.start()),X&&X[0]&&X[0](!0);var f=B;if(F?(e.onload=function(n){e.readyState=4,e.status=200,e.onreadystatechange()},e.onerror=function(n){e.readyState=0,e.status=400,e.onreadystatechange()}):"function"==typeof s&&(e.onprogress=s),e.onreadystatechange=function(){if(4===e.readyState){if(e.status>=200&&e.status<300){if(f.xhr=e,204!==e.status)if(o){var n,a=[],s=/({[\s\S]*?--batchresponse_)/g;do n=s.exec(e.responseText),n&&(D(n[0].substring(0,n[0].length-16),f),a.push(f.data));while(n);f.data=a}else D(e.responseText,f);W&&W.resolve(f),"function"==typeof t&&t.call(f,f.data,i)}else try{var l=e.responseText;if(JSON&&""!=e.responseText&&(l=JSON.parse(e.responseText)),""!==l&&l["odata.error"]){var p=l["odata.error"].message.value+" | HTTP Status: "+e.status+" | oData Code: "+l["odata.error"].code;k(p)}else k("Request to "+u()+" failed with HTTP status "+(e.status||404)+".")}catch(d){if(H(f,!0,e.status||404,e.responseText),"function"==typeof r)r(e.status||404,d);else{if(!W)throw d;d.status=e.status||404,W.reject(d)}}H(f,!1)}},B.oConfig.username&&B.oConfig.password&&(F&&k("CORS and Basic Auth is not supported for IE <= 9. Try to set isWithCredentials:false in the OData config if you do not need CORS support."),e.setRequestHeader("Authorization","Basic "+Y(B.oConfig.username+":"+B.oConfig.password))),!F){if(a)for(var l=0;l<a.length;l++)e.setRequestHeader(a[l].name,a[l].value);if(B.oConfig.headers.length>0)for(var l=0;l<B.oConfig.headers.length;l++)e.setRequestHeader(B.oConfig.headers[l].name,B.oConfig.headers[l].value)}e.send(n)}function H(e,n,t,r){e.oConfig.ready&&null==X&&(e.oConfig.openAjaxRequests--,e.oConfig.openAjaxRequests<=0&&e.oConfig.ready()),X&&X[1]&&X[1](!1),e.oConfig.error&&n&&e.oConfig.error(t,r)}function D(e,n){var t=O(e,-1);if(-1!==t)n.data=t;else if(JSON&&""!==e){var r=JSON.parse(e);n.raw=r,r.hasOwnProperty("value")?(w(["$first"])&&r.value.length&&r.value.length<=1?n.data=r.value[0]:n.data=r.value,(r.hasOwnProperty("odata.count")||r.hasOwnProperty("@odata.count"))&&(n.inlinecount=r["odata.count"]||r["@odata.count"])):n.data=r,U.data=n.data}else n.data=e}function _(e,n){var t=null;if("undefined"==typeof window){var r=require("xhr2");t=new r}else t=new XMLHttpRequest;return B.oConfig.isCors&&"withCredentials"in t?(t.withCredentials=!0,t.open(e,n,B.oConfig.isAsync)):B.oConfig.isCors&&"undefined"!=typeof XDomainRequest?(t=new XDomainRequest,F=!0,"GET"==e?t.open(e,n):t.open("POST",n)):t.open(e,n,B.oConfig.isAsync),t}function J(){var e=arguments[0],n=arguments[1];for(var t in n){var r=new RegExp(t,"g");"string"==typeof e&&(e=e.replace(r,n[t]))}return e}function Y(e){var n={_keyStr:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",encode:function(e){var t,r,o,a,i,u,s,f="",l=0;for(e=n._utf8_encode(e);l<e.length;)t=e.charCodeAt(l++),r=e.charCodeAt(l++),o=e.charCodeAt(l++),a=t>>2,i=(3&t)<<4|r>>4,u=(15&r)<<2|o>>6,s=63&o,isNaN(r)?u=s=64:isNaN(o)&&(s=64),f=f+this._keyStr.charAt(a)+this._keyStr.charAt(i)+this._keyStr.charAt(u)+this._keyStr.charAt(s);return f},_utf8_encode:function(e){e=e.replace(/\r\n/g,"\n");for(var n="",t=0;t<e.length;t++){var r=e.charCodeAt(t);128>r?n+=String.fromCharCode(r):r>127&&2048>r?(n+=String.fromCharCode(r>>6|192),n+=String.fromCharCode(63&r|128)):(n+=String.fromCharCode(r>>12|224),n+=String.fromCharCode(r>>6&63|128),n+=String.fromCharCode(63&r|128))}return n}};return n.encode(e)}var B=this,U=null,G=[],M=[],I=!0,W=null,X=null,F=!1,z=function(){},Q={},K={"==":"eq","===":"eq","!=":"ne","!==":"ne",">":"gt",">=":"ge","<":"lt","<=":"le","&&":"and","||":"or","!":"not","*":"mul","%":"mod"};return B.data=[],B.inlinecount=null,B.param={},B.oConfig=t,B.raw=null,B.xhr=null,B.routes=B.route=function(e,n){R(e)||(e=[e]),"undefined"==typeof window&&k("Routes are only supported in a browser env.");for(var t=window.location.hash,r=0;r<e.length;r++)"undefined"!=typeof n?M.push({name:e[r],route:l(e[r]),callback:n,param:{},interval:setInterval(function(){window.location.hash!=t&&(t=window.location.hash,f(window.location.hash))},100)}):k('Routes without a callback are not supported. Please define a function like .route("YourRoute", function() { }).');return B.triggerRoute(window.location.hash),B},B.beforeRouting=function(e){return z=e,B},B.isEndpoint=function(){return I},B.triggerRoute=function(e){return f(e),B},B.find=function(e){return U.path[U.path.length-1].get=e,B},B.top=B.take=function(e){return T(["$top"])||C("$top",e,e),B},B.skip=function(e){return T("$skip")||C("$skip",e,e),B},B.first=function(){return T(["$top","$first"])||C("$top",1,null,"$first"),B},B.filter=B.where=function(e){var n=S(d(e));return w("$filter")?x("$filter",n,n):C("$filter",n,n),B},B.any=function(e,n){var t=e+"/any(x:x/"+d(S(n))+")";return w("$filter")?x("$filter",t,t):C("$filter",t,t),B},B.orderBy=function(e,n){return"undefined"==typeof n&&(n="asc"),T("$orderby")||C("$orderby",S(e)+" "+n),B},B.orderByDesc=function(e){return B.orderBy(e,"desc")},B.select=function(e){return C("$select",S(e)),B},B.count=function(){return B.oConfig.version>=4?U.path.push({resource:"$count",get:null}):(q("$format"),C("$count","count")),B},B.inlineCount=function(e){return B.oConfig.version>=4?(e=e||"true",T("$count")||C("$count",e)):(e=e||"allpages",T("$inlinecount")||C("$inlinecount",e)),B},B.batch=function(e){return h(e),B},B.expand=function(e){return v(e),B},B.loading=function(e,n){return n=n||e,X=e?[e,n]:[function(){},function(){}],B},B.ref=B.link=function(e,n,t){t||(t=n,n=null),q("$format"),(null==U||U.get)&&k("You need to define a resource with the find() method to append an navigation property"),B.oConfig.version<4?(U.method="POST",U.path.push("$links"),U.path.push({resource:e,get:null})):(U.method="POST",U.path.push({resource:e,get:null}),U.path.push({resource:"$ref",get:null}));var r=m(n||e);r.path[r.path.length-1].get=t;var o=u(r);return U.data={"@odata.id":o},B},B.removeRef=B.deleteRef=function(e,n,t){if(t||(t=n,n=null),q("$format"),(null==U||U.get)&&k("You need to define a resource with the find() method to append an navigation property"),B.oConfig.version<4?(U.method="POST",U.path.push("$links"),U.path.push({resource:e,get:null})):(U.method="POST",U.path.push({resource:e,get:null}),U.path.push({resource:"$ref",get:null})),t){var r=m(n||e);r.path[r.path.length-1].get=t;var o=u(r);C("$id",o)}return U.method="DELETE",B},B.get=function(e,n){var t=r();return t&&"undefined"==typeof e&&(W=t.defer()),c(e,n,!1),t&&"undefined"==typeof e?W.promise:B},B.save=function(e,n){if("GET"===U.method&&null!==U.data){var t=p(U);t.method="PATCH",t.data=U.data,h(t)}var o=r();return o&&"undefined"==typeof e?(W=o.defer(),c(e,n,!0),W.promise):(c(e,n,!0),B)},B.post=function(e,n){return q("$format"),n&&h(n),U.method="POST",U.data=e,B},B.patch=function(e,n){return n&&h(n),U.path[U.path.length-1]&&U.path[U.path.length-1].get||k("Bulk updates are not supported. You need to query a unique resource with find() to patch/put it."),U.method="PATCH",U.data=e,B},B.put=function(e,n){return n&&h(n),U.path[U.path.length-1]&&U.path[U.path.length-1].get||k("Bulk updates are not supported. You need to query a unique resource with find() to patch/put it."),U.method="PUT",U.data=e,B},B.remove=B["delete"]=function(e){return e&&h(e),U.path[U.path.length-1]&&U.path[U.path.length-1].get||k("Bulk deletes are not supported. You need to query a unique resource with find() to delete it."),U.method="DELETE",B},B.query=function(e){return u(e)},B.search=function(e,n,t,r){var o=a(e,n,t);return 4==B.oConfig.version&&r?T("$search")||C("$search",o,o):T("$filter")||C("$filter",o,o,"$search"),B},B.filterByList=B.exclude=function(e,n){if(!T("$filter")){var t=o(e,n,K["!="],K["&&"]);C("$filter",S(t),t)}return B},B.include=function(e,n){if(!T("$filter")){var t=o(e,n,K["=="],K["||"]);C("$filter",S(t),t)}return B},B.progress=function(e){return null!=U&&(U.progress=e),B},y(n)}return n}); \ No newline at end of file diff --git a/package.json b/package.json index e7f4ced..af7094e 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "o.js", "description": "o.js is a client side Odata Javascript library to simplify the request of data. The main goal is to build a standalone, lightweight and easy to understand Odata lib.", - "version": "0.3.8", + "version": "0.4.0", "main": "o.js", "dependencies": { "q": "^1.5.0", diff --git a/spec/o.spec.js b/spec/o.spec.js index 556538a..47b13bb 100644 --- a/spec/o.spec.js +++ b/spec/o.spec.js @@ -75,7 +75,7 @@ describe('o.js tests:', function () { beforeAll(function (done) { o().config({ - endpoint: 'http://services.odata.org/V4/(S(ms4wufavzmwsg3fjo3eqdgak))/TripPinServiceRW/', + endpoint: 'https://services.odata.org/V4/(S(ms4wufavzmwsg3fjo3eqdgak))/TripPinServiceRW/', version: 4, strictMode: true, headers: [{ name: 'If-Match', value: '*' }] @@ -217,7 +217,7 @@ describe('o.js tests:', function () { it('PATCH People(testEntity) - no endpoint - no query', function (done) { var name = 'Test_' + Math.random(); - o('http://services.odata.org/V4/(S(ms4wufavzmwsg3fjo3eqdgak))/TripPinServiceRW/People(\'' + testEntity.UserName + '\')').patch({ FirstName: name }).save(function (data) { + o('https://services.odata.org/V4/(S(ms4wufavzmwsg3fjo3eqdgak))/TripPinServiceRW/People(\'' + testEntity.UserName + '\')').patch({ FirstName: name }).save(function (data) { expect(data.length).toBe(0); done(); }, function (e) { @@ -247,8 +247,8 @@ describe('o.js tests:', function () { }); it('POST People(testEntity)/Friends/$ref - no endpoint - no query', function (done) { - o('http://services.odata.org/V4/(S(ms4wufavzmwsg3fjo3eqdgak))/TripPinServiceRW/People(\'' + testEntity.UserName + '\')') - .ref('Friends', 'http://services.odata.org/V4/(S(ms4wufavzmwsg3fjo3eqdgak))/TripPinServiceRW/People', '\'' + testEntity.UserName + '\'').save(function (data) { + o('https://services.odata.org/V4/(S(ms4wufavzmwsg3fjo3eqdgak))/TripPinServiceRW/People(\'' + testEntity.UserName + '\')') + .ref('Friends', 'https://services.odata.org/V4/(S(ms4wufavzmwsg3fjo3eqdgak))/TripPinServiceRW/People', '\'' + testEntity.UserName + '\'').save(function (data) { expect(data.length).toBe(0); done(); }, function (e) { @@ -258,8 +258,8 @@ describe('o.js tests:', function () { }); xit('DELETE People(testEntity)/Friends/$ref - no endpoint - no query', function (done) { - o('http://services.odata.org/V4/(S(ms4wufavzmwsg3fjo3eqdgak))/TripPinServiceRW/People(\'' + testEntity.UserName + '\')') - .removeRef('Friends', 'http://services.odata.org/V4/(S(ms4wufavzmwsg3fjo3eqdgak))/TripPinServiceRW/People', '\'' + testEntity.UserName + '\'').save(function (data) { + o('https://services.odata.org/V4/(S(ms4wufavzmwsg3fjo3eqdgak))/TripPinServiceRW/People(\'' + testEntity.UserName + '\')') + .removeRef('Friends', 'https://services.odata.org/V4/(S(ms4wufavzmwsg3fjo3eqdgak))/TripPinServiceRW/People', '\'' + testEntity.UserName + '\'').save(function (data) { expect(data.length).toBe(0); done(); }, function (e) {