diff --git a/README.md b/README.md index 4625ae1..25f40bf 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # o.js -_o.js beta v0.3.1_ +_o.js beta v0.3.3_ 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. @@ -185,7 +185,7 @@ However, if you have set an endpoint you can still do a full endpoint request fo ```javascript //basic config o().config({ - endpoint: null, + endpoint: null, // The default endpoint to use. 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. @@ -196,11 +196,7 @@ o().config({ 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'}] + isAsync: true // set this to false to enable sync requests. Only usable without basic auth }); ``` diff --git a/o.js b/o.js index fbed10c..1ef0180 100644 --- a/o.js +++ b/o.js @@ -1,12 +1,12 @@ // +++ -// o.js v0.3.1 +// o.js v0.3.3 // // 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: 06.06.2017 +// Date: 06/07/2017 // Contributors: Matteo Antony Mistretta (https://github.com/IceOnFire) // // -------------------- @@ -339,7 +339,7 @@ // adds a inline count // +++ base.inlineCount = function (countOption) { - if (oConfig.version == 4) { + if (base.oConfig.version == 4) { countOption = countOption || 'true'; if (!isQueryThrowEx('$count')) { addQuery('$count', countOption); @@ -393,7 +393,7 @@ if (resource == null || resource.get) { throwEx('You need to define a resource with the find() method to append an navigation property'); } - if (oConfig.version < 4) { + if (base.oConfig.version < 4) { resource.method = 'POST'; resource.path.push('$link'); resource.path.push({ resource: navPath, get: null }); @@ -418,7 +418,7 @@ if (resource == null || resource.get) { throwEx('You need to define a resource with the find() method to append an navigation property'); } - if (oConfig.version < 4) { + if (base.oConfig.version < 4) { resource.method = 'POST'; resource.path.push('$link'); resource.path.push({ resource: navPath, get: null }); @@ -443,7 +443,6 @@ // +++ // This function actually queries the oData service with a GET request - // TODO: maybe add some pseudonyms... // +++ base.get = function (callback, errorCallback) { // init the q -> if node require a node promise -> if ES6, try ES6 promise @@ -513,10 +512,9 @@ } // +++ - // does a update with the given Data to the current dataset - // always uses the PATCH http method + // does a update with the given Data to the current dataset with PATCH. // +++ - base.patch = base.put = function (data, res) { + base.patch = function (data, res) { //add the resource if (res) { @@ -533,6 +531,26 @@ return (base); } + // +++ + // does a update with the given Data to the current dataset with PUT. + // +++ + base.put = function (data, res) { + + //add the resource + if (res) { + addNewResource(res); + } + + if (!resource.path[resource.path.length - 1] || !resource.path[resource.path.length - 1].get) + throwEx('Bulk updates are not supported. You need to query a unique resource with find() to patch/put it.'); + + //set the method and data + resource.method = 'PUT'; + resource.data = data; + + return (base); + } + // +++ // does a delete with the given Data to the current dataset // +++ @@ -565,7 +583,7 @@ var searchStr = buildSearchFilter(searchColumns, searchWord, searchFunc); - if (oConfig.version == 4 && isSupported) { + if (base.oConfig.version == 4 && isSupported) { if (!isQueryThrowEx('$search')) { addQuery('$search', searchStr, searchStr); } @@ -648,7 +666,7 @@ // builds a search filter // ++++ function buildSearchFilter(searchColumns, searchWord, searchFunc) { - searchFunc = searchFunc || (oConfig.version == 4 ? 'contains' : 'substringof'); + searchFunc = searchFunc || (base.oConfig.version == 4 ? 'contains' : 'substringof'); var searchWordSplit = searchWord.split(' '); var isNotExactSearch = (searchFunc === 'contains' || searchFunc === 'substringof'); @@ -657,7 +675,7 @@ var wordArr = []; if (isNotExactSearch) { for (var m = 0; m < searchWordSplit.length; m++) { - if (oConfig.version == 4) { + if (base.oConfig.version == 4) { wordArr.push(searchFunc + '(' + searchColumns[i] + ',\'' + searchWordSplit[m] + '\')'); } else { @@ -776,7 +794,7 @@ var routeRegex = routeStr; if (!(routeStr instanceof RegExp)) { //set the hash if needed - if (oConfig.isHashRoute && !startsWith(routeStr, '#')) { + if (base.oConfig.isHashRoute && !startsWith(routeStr, '#')) { routeStr = '#' + routeStr; } //build up a regex @@ -844,13 +862,13 @@ resource = res; //add the default format - if (!isQuery('$format') && oConfig.autoFormat) { + if (!isQuery('$format') && base.oConfig.autoFormat) { addQuery('$format', base.oConfig.format); } //appendings - for (var i = 0; i < oConfig.appending.length; i++) { - addQuery(oConfig.appending[i].name, oConfig.appending[i].value); + for (var i = 0; i < base.oConfig.appending.length; i++) { + addQuery(base.oConfig.appending[i].name, base.oConfig.appending[i].value); } } @@ -876,36 +894,36 @@ //build a ajax request 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']) <= 1 && isSave) && !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' } ], 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 - removeResource(['POST', 'PATCH', 'DELETE']); + // because the post/put/delete is done, we remove the resource to assume that it will not be posted again + removeResource(['POST', 'PATCH', 'DELETE', 'PUT']); } - //do a $batch request + // do a $batch request else { - //generate a uui for this batch + // generate a uui for this batch var guid = generateUUID(); - //build the endpoint + // build the endpoint var endpoint = base.oConfig.endpoint + (endsWith(base.oConfig.endpoint, '/') ? '' : '/') + '$batch'; - //appendings - for (var i = 0; i < oConfig.appending.length; i++) { - endpoint += (i === 0 ? '?' : '&') + oConfig.appending[i].name + '=' + oConfig.appending[i].value; + // appendings + for (var i = 0; i < base.oConfig.appending.length; i++) { + endpoint += (i === 0 ? '?' : '&') + base.oConfig.appending[i].name + '=' + base.oConfig.appending[i].value; } - //start the request + // start the request startAjaxReq(createCORSRequest('POST', endpoint), buildBatchBody(guid, isSave), callback, errorCallback, true, - //add the necessary headers + // add the necessary headers [{ 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 + // because the post/put/delete is done, we remove the resource to assume that it will not be posted again removeResource(['POST', 'PUT', 'DELETE']); } } @@ -1482,7 +1500,7 @@ function strFormat() { var str = arguments[0]; var para = arguments[1]; - for (p in para) { + for (var p in para) { var regex = new RegExp(p, 'g'); if (typeof str === 'string') str = str.replace(regex, para[p]); diff --git a/o.min.js b/o.min.js index 7f20a2d..a9eb277 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(A(n)){var o="",a=[];for(i=0;i0?"?"+e.substring(1):""}function f(e){for(var n=0;n-1||e.toUpperCase().indexOf("HTTPS://")>-1?X=!1:G.oConfig.endpoint||L("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),G)}function m(e){$("$expand")?(M.queryList[M.query.$expand].value+=","+e,M.queryList[M.query.$expand].original=M.queryList[M.query.$expand].value):x("$expand",e,e)}function C(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 i=r.split("&"),a=0;a0?e:void L(n+": Parameter must be set.")}function E(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-1&&n++;return n}function b(e){for(var n=[],t=0;t-1&&n.push(t);for(var t=n.length-1;t>=0;t--)U.splice(n[t],1);U[0]&&(M=U[0])}function R(e){return JSON?JSON.stringify(e):(L("No JSON support."),e)}function A(e){return"undefined"==typeof Array.isArray?"[object Array]"===e.toString():Array.isArray(e)}function N(e,n){return e?-1!==e.indexOf(n,e.length-n.length):!1}function k(e,n){return 0===e.indexOf(n)}function L(e){function n(e){this.message=e,this.name="o.js exception"}if(n.prototype=new Error,G.oConfig.strictMode===!0)throw new n(e);console.log("o.js exception: "+e)}function j(e,n){var t="",r=S(),o=!1;n&&(t+="--batch_"+e+"\n",t+="Content-Type: multipart/mixed; boundary=changeset_"+r+"\n\n");for(var i=0;i=200&&e.status<300){if(204!==e.status)if(o){var n,i=[],s=/({[\s\S]*?--batchresponse_)/g;do n=s.exec(e.responseText),n&&(_(n[0].substring(0,n[0].length-16),f),i.push(f.data));while(n);f.data=i}else _(e.responseText,f);F&&F.resolve(f),"function"==typeof t&&t.call(f,f.data,a)}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;L(l)}else L("Request to "+u()+" failed with HTTP status "+(e.status||404)+".")}catch(d){if(D(f,!0,e.status||404,e.responseText),"function"==typeof r)r(e.status||404,d);else{if(!F)throw d;d.status=e.status||404,F.reject(d)}}D(f,!1)}},G.oConfig.username&&G.oConfig.password&&(Q&&L("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 "+B(G.oConfig.username+":"+G.oConfig.password))),!Q){if(i)for(var p=0;p0)for(var p=0;p>2,a=(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(i)+this._keyStr.charAt(a)+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;tr?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 G=this,M=null,U=[],I=[],X=!0,F=null,z=null,Q=!1,W=function(){},K={},V={"==":"eq","===":"eq","!=":"ne","!==":"ne",">":"gt",">=":"ge","<":"lt","<=":"le","&&":"and","||":"or","!":"not","*":"mul","%":"mod"};return G.data=[],G.inlinecount=null,G.param={},G.oConfig=t,G.routes=G.route=function(e,n){A(e)||(e=[e]),"undefined"==typeof window&&L("Routes are only supported in a browser env.");for(var t=window.location.hash,r=0;rt;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;i0?"?"+e.substring(1):""}function f(e){for(var n=0;n-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;i0?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-1&&n++;return n}function E(e){for(var n=[],t=0;t-1&&n.push(t);for(var t=n.length-1;t>=0;t--)G.splice(n[t],1);G[0]&&(U=G[0])}function b(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 A(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");for(var a=0;a=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;p0)for(var p=0;p>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;tr?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.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