From 281c2ca7261af3094fd27f6c05bbac271fca3dd4 Mon Sep 17 00:00:00 2001 From: aymkdn Date: Thu, 9 Jun 2022 09:35:56 +0200 Subject: [PATCH] v6.2.0 - Added option `modify` to `$SP().getManager()` - Added `$SP().isSPO()` - Changed `$SP().ajax()` to better managed 401 error for the REST API calls - Changed `$SP().cleanResult()` when dealing with a date (`$SP().cleanResult("2022-01-19 00:00:00")` will now return "2022-01-19" instead of "2022-01-19 00:00:00") - Changed `$SP().toDate()` to ignore the timezone (e.g. `$SP().toDate("2022-01-19")` used to return different result based on the user's timezone, but now it returns the correct date at 00:00:00 in the current timezone) - /!\ Changed `$SP().getVersions()`: only compatible with REST API, and it returns a different result/outcome than before - /!\ Changed `$SP().hasREST()`: it will always return TRUE (it's possible to override the value) because REST API is around for a while now and I assume everyone is using at least SP2013 (see issue https://github.com/Aymkdn/SharepointPlus/issues/180) - Fixed `$SP().isMember()` for the `url` option (see issue https://github.com/Aymkdn/SharepointPlus/issues/175) - Removed support for IE10 in the bundle for browsers --- .npmignore | 1 + README.md | 6 +- browser/sharepointplus.js | 2 +- changelog.md | 12 + dist/index.js | 3 + dist/lists/addAttachment.js | 2 +- dist/lists/cleanResult.js | 3 +- dist/lists/get.js | 5 +- dist/lists/getVersions.js | 67 +- dist/lists/hasPermission.js | 2 +- dist/lists/removeAttachment.js | 2 +- dist/lists/restoreVersion.js | 2 +- dist/main.js | 3 +- dist/modals/closeModalDialog.js | 2 +- dist/people/getManager.js | 24 +- dist/people/isMember.js | 9 +- dist/utils/ajax.js | 13 +- dist/utils/hasREST.js | 37 +- dist/utils/isSPO.js | 44 + dist/utils/toDate.js | 4 +- docs/core.html | 8 +- docs/files.html | 6 +- docs/index.html | 34 +- docs/lists.html | 42 +- docs/modals.html | 6 +- docs/node.html | 10 +- docs/people.html | 18 +- docs/utils.html | 50 +- es5/index.js | 2 + es5/lists/addAttachment.js | 2 +- es5/lists/cleanResult.js | 3 +- es5/lists/get.js | 5 +- es5/lists/getVersions.js | 64 +- es5/lists/hasPermission.js | 2 +- es5/lists/removeAttachment.js | 2 +- es5/lists/restoreVersion.js | 2 +- es5/main.js | 3 +- es5/modals/closeModalDialog.js | 2 +- es5/people/getManager.js | 23 +- es5/people/isMember.js | 9 +- es5/utils/ajax.js | 13 +- es5/utils/hasREST.js | 37 +- es5/utils/isSPO.js | 30 + es5/utils/toDate.js | 4 +- jsdoc_template/tmpl/mainpage.tmpl | 26 +- package.json | 11 +- src/index.js | 2 + src/lists/addAttachment.js | 2 +- src/lists/cleanResult.js | 3 +- src/lists/get.js | 6 +- src/lists/getVersions.js | 34 +- src/lists/hasPermission.js | 2 +- src/lists/removeAttachment.js | 2 +- src/lists/restoreVersion.js | 2 +- src/main.js | 3 +- src/modals/closeModalDialog.js | 2 +- src/people/getManager.js | 15 +- src/people/isMember.js | 6 +- src/utils/ajax.js | 23 +- src/utils/hasREST.js | 30 +- src/utils/isSPO.js | 22 + src/utils/toDate.js | 4 +- tests/index.js | 176 +- tests/sharepointplus-5.2.js | 5732 ----------------------------- 64 files changed, 539 insertions(+), 6184 deletions(-) create mode 100644 dist/utils/isSPO.js create mode 100644 es5/utils/isSPO.js create mode 100644 src/utils/isSPO.js delete mode 100644 tests/sharepointplus-5.2.js diff --git a/.npmignore b/.npmignore index dfe5471..191e7aa 100644 --- a/.npmignore +++ b/.npmignore @@ -1,3 +1,4 @@ generate_docs.bat inch.json node_modules +package-lock.json diff --git a/README.md b/README.md index 2416658..4b15daa 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ SharepointPlus ($SP) is a JavaScript library which offers some extended features ## Documentation -Browse the [online documentation here](http://aymkdn.github.com/SharepointPlus/). +Browse the [online documentation here](https://aymkdn.github.io/SharepointPlus/). ## Quick Start @@ -23,7 +23,7 @@ Then: import $SP from 'sharepointplus' ``` -Please, make sure to read [the documentation](http://aymkdn.github.com/SharepointPlus/) to optimize your bundle size. +Please, make sure to read [the documentation](https://aymkdn.github.io/SharepointPlus/) to optimize your bundle size. ### Browser Only @@ -65,4 +65,4 @@ $SP().list('ListName').get({ ## More information -Please visit the [online documentation](http://aymkdn.github.com/SharepointPlus/) to know more. +Please visit the [online documentation](https://aymkdn.github.io/SharepointPlus/) to know more. diff --git a/browser/sharepointplus.js b/browser/sharepointplus.js index 33c1199..35ec53a 100644 --- a/browser/sharepointplus.js +++ b/browser/sharepointplus.js @@ -1 +1 @@ -window.$SP=function(e){var t={};function r(n){if(t[n])return t[n].exports;var i=t[n]={i:n,l:!1,exports:{}};return e[n].call(i.exports,i,i.exports,r),i.l=!0,i.exports}return r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var i in e)r.d(n,i,function(t){return e[t]}.bind(null,i));return n},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="",r(r.s=226)}([function(e,t,r){"use strict";(function(e){r.d(t,"a",(function(){return a}));r(146),r(40),r(2),r(3),r(6),r(148),r(32),r(4);var n=r(49),i=r(161);function o(e,t,r,n,i,o,a){try{var s=e[o](a),u=s.value}catch(e){return void r(e)}s.done?t(u):Promise.resolve(u).then(n,i)}function a(e){return s.apply(this,arguments)}function s(){var t;return t=regeneratorRuntime.mark((function t(r){var o,s,u,c,l,f,p,h;return regeneratorRuntime.wrap((function(t){for(;;)switch(t.prev=t.next){case 0:if(r.headers=r.headers||{},t.prev=1,o=!1,r.url.toLowerCase().indexOf("/_api/")>-1&&-1===r.url.toLowerCase().indexOf("_api/web/url")&&(void 0===r.headers.Accept&&(r.headers.Accept="application/json;odata="+e._SP_JSON_ACCEPT),void 0===r.headers["Content-Type"]&&(r.headers["Content-Type"]="application/json;odata="+e._SP_JSON_ACCEPT),void 0===r.headers["X-RequestDigest"]&&-1===r.url.indexOf("contextinfo")&&(o=!0)),r.url.toLowerCase().indexOf("_vti_bin/client.svc/processquery")>-1&&void 0===r.headers["X-RequestDigest"]&&(o=!0),!o){t.next=11;break}return t.next=8,n.a.call(this,{url:r.url.toLowerCase().split("_api")[0]});case 8:return s=t.sent,r.headers["X-RequestDigest"]=s,t.abrupt("return",a.call(this,r));case 11:if(void 0===r.headers["Content-Type"]&&(r.headers["Content-Type"]="text/xml; charset=utf-8"),!e._SP_ISBROWSER){t.next=36;break}return"POST"!==r.method||r.body||(r.body=""),t.next=16,new Promise((function(e){Object(i.a)(r,(function(t,r,n){e({code:t,responseText:r,request:n})}))}));case 16:if(u=t.sent,c=u.code,l=u.responseText,f=u.request,!(c>=200&&c<300&&"Error"!==l&&"Abort"!==l&&"Timeout"!==l)){t.next=24;break}return p=f.responseType&&"document"!==f.responseType?l:f.responseXML||f.responseText,(f.getResponseHeader("Content-Type")||"").indexOf("/json")>-1&&"string"==typeof p&&(p=JSON.parse(p)),t.abrupt("return",Promise.resolve(p));case 24:if(403!=c||!l.includes("security validation for this page is invalid")){t.next=33;break}return delete r.headers["X-RequestDigest"],t.next=28,n.a.call(this,{cache:!1});case 28:return h=t.sent,r.headers["X-RequestDigest"]=h,t.abrupt("return",a.call(this,r));case 33:return t.abrupt("return",Promise.reject({statusCode:c,responseText:l,request:f}));case 34:t.next=36;break;case 36:t.next=41;break;case 38:return t.prev=38,t.t0=t.catch(1),t.abrupt("return",Promise.reject({error:t.t0,statusCode:t.t0.statusCode,response:t.t0.response,responseText:t.t0.response?t.t0.response.body:""}));case 41:case"end":return t.stop()}}),t,this,[[1,38]])})),(s=function(){var e=this,r=arguments;return new Promise((function(n,i){var a=t.apply(e,r);function s(e){o(a,n,i,s,u,"next",e)}function u(e){o(a,n,i,s,u,"throw",e)}s(void 0)}))}).apply(this,arguments)}}).call(this,r(10))},function(e,t,r){"use strict";r.d(t,"a",(function(){return n}));r(6),r(14);function n(e,t,r){return'<'+e+' xmlns="'+(r=r||"http://schemas.microsoft.com/sharepoint/soap/").replace(/webpartpages\/$/,"webpartpages")+'">'+t+""}},function(e,t,r){var n=r(110),i=r(34),o=r(174);n||i(Object.prototype,"toString",o,{unsafe:!0})},function(e,t,r){"use strict";var n,i,o,a,s=r(11),u=r(52),c=r(7),l=r(53),f=r(175),p=r(34),h=r(135),d=r(62),v=r(136),m=r(22),g=r(63),y=r(111),b=r(43),w=r(102),S=r(176),x=r(139),A=r(48),P=r(140).set,E=r(178),I=r(179),_=r(180),k=r(143),T=r(181),D=r(46),O=r(127),R=r(12),N=r(112),C=R("species"),L="Promise",j=D.get,V=D.set,M=D.getterFor(L),U=f,F=c.TypeError,W=c.document,B=c.process,G=l("fetch"),q=k.f,H=q,Y="process"==b(B),$=!!(W&&W.createEvent&&c.dispatchEvent),z=O(L,(function(){if(!(w(U)!==String(U))){if(66===N)return!0;if(!Y&&"function"!=typeof PromiseRejectionEvent)return!0}if(u&&!U.prototype.finally)return!0;if(N>=51&&/native code/.test(U))return!1;var e=U.resolve(1),t=function(e){e((function(){}),(function(){}))};return(e.constructor={})[C]=t,!(e.then((function(){}))instanceof t)})),Q=z||!x((function(e){U.all(e).catch((function(){}))})),X=function(e){var t;return!(!m(e)||"function"!=typeof(t=e.then))&&t},Z=function(e,t,r){if(!t.notified){t.notified=!0;var n=t.reactions;E((function(){for(var i=t.value,o=1==t.state,a=0;n.length>a;){var s,u,c,l=n[a++],f=o?l.ok:l.fail,p=l.resolve,h=l.reject,d=l.domain;try{f?(o||(2===t.rejection&&te(e,t),t.rejection=1),!0===f?s=i:(d&&d.enter(),s=f(i),d&&(d.exit(),c=!0)),s===l.promise?h(F("Promise-chain cycle")):(u=X(s))?u.call(s,p,h):p(s)):h(i)}catch(e){d&&!c&&d.exit(),h(e)}}t.reactions=[],t.notified=!1,r&&!t.rejection&&J(e,t)}))}},K=function(e,t,r){var n,i;$?((n=W.createEvent("Event")).promise=t,n.reason=r,n.initEvent(e,!1,!0),c.dispatchEvent(n)):n={promise:t,reason:r},(i=c["on"+e])?i(n):"unhandledrejection"===e&&_("Unhandled promise rejection",r)},J=function(e,t){P.call(c,(function(){var r,n=t.value;if(ee(t)&&(r=T((function(){Y?B.emit("unhandledRejection",n,e):K("unhandledrejection",e,n)})),t.rejection=Y||ee(t)?2:1,r.error))throw r.value}))},ee=function(e){return 1!==e.rejection&&!e.parent},te=function(e,t){P.call(c,(function(){Y?B.emit("rejectionHandled",e):K("rejectionhandled",e,t.value)}))},re=function(e,t,r,n){return function(i){e(t,r,i,n)}},ne=function(e,t,r,n){t.done||(t.done=!0,n&&(t=n),t.value=r,t.state=2,Z(e,t,!0))},ie=function(e,t,r,n){if(!t.done){t.done=!0,n&&(t=n);try{if(e===r)throw F("Promise can't be resolved itself");var i=X(r);i?E((function(){var n={done:!1};try{i.call(r,re(ie,e,n,t),re(ne,e,n,t))}catch(r){ne(e,n,r,t)}})):(t.value=r,t.state=1,Z(e,t,!1))}catch(r){ne(e,{done:!1},r,t)}}};z&&(U=function(e){y(this,U,L),g(e),n.call(this);var t=j(this);try{e(re(ie,this,t),re(ne,this,t))}catch(e){ne(this,t,e)}},(n=function(e){V(this,{type:L,done:!1,notified:!1,parent:!1,reactions:[],rejection:!1,state:0,value:void 0})}).prototype=h(U.prototype,{then:function(e,t){var r=M(this),n=q(A(this,U));return n.ok="function"!=typeof e||e,n.fail="function"==typeof t&&t,n.domain=Y?B.domain:void 0,r.parent=!0,r.reactions.push(n),0!=r.state&&Z(this,r,!1),n.promise},catch:function(e){return this.then(void 0,e)}}),i=function(){var e=new n,t=j(e);this.promise=e,this.resolve=re(ie,e,t),this.reject=re(ne,e,t)},k.f=q=function(e){return e===U||e===o?new i(e):H(e)},u||"function"!=typeof f||(a=f.prototype.then,p(f.prototype,"then",(function(e,t){var r=this;return new U((function(e,t){a.call(r,e,t)})).then(e,t)}),{unsafe:!0}),"function"==typeof G&&s({global:!0,enumerable:!0,forced:!0},{fetch:function(e){return I(U,G.apply(c,arguments))}}))),s({global:!0,wrap:!0,forced:z},{Promise:U}),d(U,L,!1,!0),v(L),o=l(L),s({target:L,stat:!0,forced:z},{reject:function(e){var t=q(this);return t.reject.call(void 0,e),t.promise}}),s({target:L,stat:!0,forced:u||z},{resolve:function(e){return I(u&&this===o?U:this,e)}}),s({target:L,stat:!0,forced:Q},{all:function(e){var t=this,r=q(t),n=r.resolve,i=r.reject,o=T((function(){var r=g(t.resolve),o=[],a=0,s=1;S(e,(function(e){var u=a++,c=!1;o.push(void 0),s++,r.call(t,e).then((function(e){c||(c=!0,o[u]=e,--s||n(o))}),i)})),--s||n(o)}));return o.error&&i(o.value),r.promise},race:function(e){var t=this,r=q(t),n=r.reject,i=T((function(){var i=g(t.resolve);S(e,(function(e){i.call(t,e).then(r.resolve,n)}))}));return i.error&&n(i.value),r.promise}})},function(e,t,r){var n=function(e){"use strict";var t,r=Object.prototype,n=r.hasOwnProperty,i="function"==typeof Symbol?Symbol:{},o=i.iterator||"@@iterator",a=i.asyncIterator||"@@asyncIterator",s=i.toStringTag||"@@toStringTag";function u(e,t,r,n){var i=t&&t.prototype instanceof v?t:v,o=Object.create(i.prototype),a=new _(n||[]);return o._invoke=function(e,t,r){var n=l;return function(i,o){if(n===p)throw new Error("Generator is already running");if(n===h){if("throw"===i)throw o;return T()}for(r.method=i,r.arg=o;;){var a=r.delegate;if(a){var s=P(a,r);if(s){if(s===d)continue;return s}}if("next"===r.method)r.sent=r._sent=r.arg;else if("throw"===r.method){if(n===l)throw n=h,r.arg;r.dispatchException(r.arg)}else"return"===r.method&&r.abrupt("return",r.arg);n=p;var u=c(e,t,r);if("normal"===u.type){if(n=r.done?h:f,u.arg===d)continue;return{value:u.arg,done:r.done}}"throw"===u.type&&(n=h,r.method="throw",r.arg=u.arg)}}}(e,r,a),o}function c(e,t,r){try{return{type:"normal",arg:e.call(t,r)}}catch(e){return{type:"throw",arg:e}}}e.wrap=u;var l="suspendedStart",f="suspendedYield",p="executing",h="completed",d={};function v(){}function m(){}function g(){}var y={};y[o]=function(){return this};var b=Object.getPrototypeOf,w=b&&b(b(k([])));w&&w!==r&&n.call(w,o)&&(y=w);var S=g.prototype=v.prototype=Object.create(y);function x(e){["next","throw","return"].forEach((function(t){e[t]=function(e){return this._invoke(t,e)}}))}function A(e){var t;this._invoke=function(r,i){function o(){return new Promise((function(t,o){!function t(r,i,o,a){var s=c(e[r],e,i);if("throw"!==s.type){var u=s.arg,l=u.value;return l&&"object"==typeof l&&n.call(l,"__await")?Promise.resolve(l.__await).then((function(e){t("next",e,o,a)}),(function(e){t("throw",e,o,a)})):Promise.resolve(l).then((function(e){u.value=e,o(u)}),(function(e){return t("throw",e,o,a)}))}a(s.arg)}(r,i,t,o)}))}return t=t?t.then(o,o):o()}}function P(e,r){var n=e.iterator[r.method];if(n===t){if(r.delegate=null,"throw"===r.method){if(e.iterator.return&&(r.method="return",r.arg=t,P(e,r),"throw"===r.method))return d;r.method="throw",r.arg=new TypeError("The iterator does not provide a 'throw' method")}return d}var i=c(n,e.iterator,r.arg);if("throw"===i.type)return r.method="throw",r.arg=i.arg,r.delegate=null,d;var o=i.arg;return o?o.done?(r[e.resultName]=o.value,r.next=e.nextLoc,"return"!==r.method&&(r.method="next",r.arg=t),r.delegate=null,d):o:(r.method="throw",r.arg=new TypeError("iterator result is not an object"),r.delegate=null,d)}function E(e){var t={tryLoc:e[0]};1 in e&&(t.catchLoc=e[1]),2 in e&&(t.finallyLoc=e[2],t.afterLoc=e[3]),this.tryEntries.push(t)}function I(e){var t=e.completion||{};t.type="normal",delete t.arg,e.completion=t}function _(e){this.tryEntries=[{tryLoc:"root"}],e.forEach(E,this),this.reset(!0)}function k(e){if(e){var r=e[o];if(r)return r.call(e);if("function"==typeof e.next)return e;if(!isNaN(e.length)){var i=-1,a=function r(){for(;++i=0;--o){var a=this.tryEntries[o],s=a.completion;if("root"===a.tryLoc)return i("end");if(a.tryLoc<=this.prev){var u=n.call(a,"catchLoc"),c=n.call(a,"finallyLoc");if(u&&c){if(this.prev=0;--r){var i=this.tryEntries[r];if(i.tryLoc<=this.prev&&n.call(i,"finallyLoc")&&this.prev=0;--t){var r=this.tryEntries[t];if(r.finallyLoc===e)return this.complete(r.completion,r.afterLoc),I(r),d}},catch:function(e){for(var t=this.tryEntries.length-1;t>=0;--t){var r=this.tryEntries[t];if(r.tryLoc===e){var n=r.completion;if("throw"===n.type){var i=n.arg;I(r)}return i}}throw new Error("illegal catch attempt")},delegateYield:function(e,r,n){return this.delegate={iterator:k(e),resultName:r,nextLoc:n},"next"===this.method&&(this.arg=t),d}},e}(e.exports);try{regeneratorRuntime=n}catch(e){Function("r","regeneratorRuntime = r")(n)}},function(e,t,r){"use strict";r.d(t,"a",(function(){return a}));r(2),r(3),r(6),r(14),r(4);var n=r(0),i=r(1);function o(e,t,r,n,i,o,a){try{var s=e[o](a),u=s.value}catch(e){return void r(e)}s.done?t(u):Promise.resolve(u).then(n,i)}function a(){return s.apply(this,arguments)}function s(){var e;return e=regeneratorRuntime.mark((function e(){var t,r;return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(void 0!==this.url){e.next=26;break}if(void 0===window.L_Menu_BaseUrl){e.next=5;break}return e.abrupt("return",Promise.resolve(u(window.L_Menu_BaseUrl)));case 5:if(void 0===window._spPageContextInfo||void 0===window._spPageContextInfo.webServerRelativeUrl){e.next=9;break}return e.abrupt("return",Promise.resolve(u(window._spPageContextInfo.webServerRelativeUrl)));case 9:return e.prev=9,e.next=12,n.a.call(this,{url:"/_vti_bin/Webs.asmx",body:Object(i.a)("WebUrlFromPageUrl",""+window.location.href.replace(/&/g,"&")+"")});case 12:if(t=e.sent,!(r=t.getElementsByTagName("WebUrlFromPageUrlResult")).length){e.next=18;break}return e.abrupt("return",Promise.resolve(u(r[0].firstChild.nodeValue.toLowerCase())));case 18:return e.abrupt("return",Promise.reject("[SharepointPlus 'getURL'] Unable to retrieve the URL"));case 19:e.next=24;break;case 21:return e.prev=21,e.t0=e.catch(9),e.abrupt("return",Promise.reject(e.t0));case 24:e.next=27;break;case 26:return e.abrupt("return",Promise.resolve(u(this.url)));case 27:case"end":return e.stop()}}),e,this,[[9,21]])})),(s=function(){var t=this,r=arguments;return new Promise((function(n,i){var a=e.apply(t,r);function s(e){o(a,n,i,s,u,"next",e)}function u(e){o(a,n,i,s,u,"throw",e)}s(void 0)}))}).apply(this,arguments)}function u(e){return""===e||"/"===e?window.location.protocol+"//"+window.location.host+"/":e}},function(e,t,r){"use strict";var n=r(11),i=r(81);n({target:"RegExp",proto:!0,forced:/./.exec!==i},{exec:i})},function(e,t,r){(function(t){var r=function(e){return e&&e.Math==Math&&e};e.exports=r("object"==typeof globalThis&&globalThis)||r("object"==typeof window&&window)||r("object"==typeof self&&self)||r("object"==typeof t&&t)||Function("return this")()}).call(this,r(10))},function(e,t){e.exports=function(e){try{return!!e()}catch(e){return!0}}},function(e,t,r){"use strict";var n,i=r(25),o=r(7),a=r(22),s=r(26),u=r(79),c=r(28),l=r(34),f=r(24).f,p=r(108),h=r(78),d=r(12),v=r(72),m=o.DataView,g=m&&m.prototype,y=o.Int8Array,b=y&&y.prototype,w=o.Uint8ClampedArray,S=w&&w.prototype,x=y&&p(y),A=b&&p(b),P=Object.prototype,E=P.isPrototypeOf,I=d("toStringTag"),_=v("TYPED_ARRAY_TAG"),k=!(!o.ArrayBuffer||!m),T=k&&!!h&&"Opera"!==u(o.opera),D=!1,O={Int8Array:1,Uint8Array:1,Uint8ClampedArray:1,Int16Array:2,Uint16Array:2,Int32Array:4,Uint32Array:4,Float32Array:4,Float64Array:8},R=function(e){return a(e)&&s(O,u(e))};for(n in O)o[n]||(T=!1);if((!T||"function"!=typeof x||x===Function.prototype)&&(x=function(){throw TypeError("Incorrect invocation")},T))for(n in O)o[n]&&h(o[n],x);if((!T||!A||A===P)&&(A=x.prototype,T))for(n in O)o[n]&&h(o[n].prototype,A);if(T&&p(S)!==A&&h(S,A),i&&!s(A,I))for(n in D=!0,f(A,I,{get:function(){return a(this)?this[_]:void 0}}),O)o[n]&&c(o[n],_,n);k&&h&&p(g)!==P&&h(g,P),e.exports={NATIVE_ARRAY_BUFFER:k,NATIVE_ARRAY_BUFFER_VIEWS:T,TYPED_ARRAY_TAG:D&&_,aTypedArray:function(e){if(R(e))return e;throw TypeError("Target is not a typed array")},aTypedArrayConstructor:function(e){if(h){if(E.call(x,e))return e}else for(var t in O)if(s(O,n)){var r=o[t];if(r&&(e===r||E.call(r,e)))return e}throw TypeError("Target is not a typed array constructor")},exportTypedArrayMethod:function(e,t,r){if(i){if(r)for(var n in O){var a=o[n];a&&s(a.prototype,e)&&delete a.prototype[e]}A[e]&&!r||l(A,e,r?t:T&&b[e]||t)}},exportTypedArrayStaticMethod:function(e,t,r){var n,a;if(i){if(h){if(r)for(n in O)(a=o[n])&&s(a,e)&&delete a[e];if(x[e]&&!r)return;try{return l(x,e,r?t:T&&y[e]||t)}catch(e){}}for(n in O)!(a=o[n])||a[e]&&!r||l(a,e,t)}},isView:function(e){var t=u(e);return"DataView"===t||s(O,t)},isTypedArray:R,TypedArray:x,TypedArrayPrototype:A}},function(e,t){var r;r=function(){return this}();try{r=r||new Function("return this")()}catch(e){"object"==typeof window&&(r=window)}e.exports=r},function(e,t,r){var n=r(7),i=r(50).f,o=r(28),a=r(34),s=r(101),u=r(124),c=r(127);e.exports=function(e,t){var r,l,f,p,h,d=e.target,v=e.global,m=e.stat;if(r=v?n:m?n[d]||s(d,{}):(n[d]||{}).prototype)for(l in t){if(p=t[l],f=e.noTargetGet?(h=i(r,l))&&h.value:r[l],!c(v?l:d+(m?".":"#")+l,e.forced)&&void 0!==f){if(typeof p==typeof f)continue;u(p,f)}(e.sham||f&&f.sham)&&o(p,"sham",!0),a(r,l,p,e)}}},function(e,t,r){var n=r(7),i=r(103),o=r(26),a=r(72),s=r(106),u=r(128),c=i("wks"),l=n.Symbol,f=u?l:l&&l.withoutSetter||a;e.exports=function(e){return o(c,e)||(s&&o(l,e)?c[e]=l[e]:c[e]=f("Symbol."+e)),c[e]}},function(e,t,r){"use strict";var n=r(39),i=r(132),o=r(64),a=r(46),s=r(133),u=a.set,c=a.getterFor("Array Iterator");e.exports=s(Array,"Array",(function(e,t){u(this,{type:"Array Iterator",target:n(e),index:0,kind:t})}),(function(){var e=c(this),t=e.target,r=e.kind,n=e.index++;return!t||n>=t.length?(e.target=void 0,{value:void 0,done:!0}):"keys"==r?{value:n,done:!1}:"values"==r?{value:t[n],done:!1}:{value:[n,t[n]],done:!1}}),"values"),o.Arguments=o.Array,i("keys"),i("values"),i("entries")},function(e,t,r){"use strict";var n=r(114),i=r(23),o=r(29),a=r(15),s=r(35),u=r(33),c=r(115),l=r(116),f=Math.max,p=Math.min,h=Math.floor,d=/\$([$&'`]|\d\d?|<[^>]*>)/g,v=/\$([$&'`]|\d\d?)/g;n("replace",2,(function(e,t,r,n){return[function(r,n){var i=u(this),o=null==r?void 0:r[e];return void 0!==o?o.call(r,i,n):t.call(String(i),r,n)},function(e,o){if(n.REPLACE_KEEPS_$0||"string"==typeof o&&-1===o.indexOf("$0")){var u=r(t,e,this,o);if(u.done)return u.value}var h=i(e),d=String(this),v="function"==typeof o;v||(o=String(o));var g=h.global;if(g){var y=h.unicode;h.lastIndex=0}for(var b=[];;){var w=l(h,d);if(null===w)break;if(b.push(w),!g)break;""===String(w[0])&&(h.lastIndex=c(d,a(h.lastIndex),y))}for(var S,x="",A=0,P=0;P=A&&(x+=d.slice(A,I)+O,A=I+E.length)}return x+d.slice(A)}];function m(e,r,n,i,a,s){var u=n+e.length,c=i.length,l=v;return void 0!==a&&(a=o(a),l=d),t.call(s,l,(function(t,o){var s;switch(o.charAt(0)){case"$":return"$";case"&":return e;case"`":return r.slice(0,n);case"'":return r.slice(u);case"<":s=a[o.slice(1,-1)];break;default:var l=+o;if(0===l)return t;if(l>c){var f=h(l/10);return 0===f?t:f<=c?void 0===i[f-1]?o.charAt(1):i[f-1]+o.charAt(1):t}s=i[l-1]}return void 0===s?"":s}))}}))},function(e,t,r){var n=r(35),i=Math.min;e.exports=function(e){return e>0?i(n(e),9007199254740991):0}},function(e,t,r){var n=r(7),i=r(145),o=r(13),a=r(28),s=r(12),u=s("iterator"),c=s("toStringTag"),l=o.values;for(var f in i){var p=n[f],h=p&&p.prototype;if(h){if(h[u]!==l)try{a(h,u,l)}catch(e){h[u]=l}if(h[c]||a(h,c,f),i[f])for(var d in o)if(h[d]!==o[d])try{a(h,d,o[d])}catch(e){h[d]=o[d]}}}},function(e,t,r){"use strict";var n=r(11),i=r(7),o=r(53),a=r(52),s=r(25),u=r(106),c=r(128),l=r(8),f=r(26),p=r(74),h=r(22),d=r(23),v=r(29),m=r(39),g=r(59),y=r(51),b=r(75),w=r(76),S=r(60),x=r(170),A=r(105),P=r(50),E=r(24),I=r(99),_=r(28),k=r(34),T=r(103),D=r(71),O=r(73),R=r(72),N=r(12),C=r(130),L=r(131),j=r(62),V=r(46),M=r(31).forEach,U=D("hidden"),F=N("toPrimitive"),W=V.set,B=V.getterFor("Symbol"),G=Object.prototype,q=i.Symbol,H=o("JSON","stringify"),Y=P.f,$=E.f,z=x.f,Q=I.f,X=T("symbols"),Z=T("op-symbols"),K=T("string-to-symbol-registry"),J=T("symbol-to-string-registry"),ee=T("wks"),te=i.QObject,re=!te||!te.prototype||!te.prototype.findChild,ne=s&&l((function(){return 7!=b($({},"a",{get:function(){return $(this,"a",{value:7}).a}})).a}))?function(e,t,r){var n=Y(G,t);n&&delete G[t],$(e,t,r),n&&e!==G&&$(G,t,n)}:$,ie=function(e,t){var r=X[e]=b(q.prototype);return W(r,{type:"Symbol",tag:e,description:t}),s||(r.description=t),r},oe=c?function(e){return"symbol"==typeof e}:function(e){return Object(e)instanceof q},ae=function(e,t,r){e===G&&ae(Z,t,r),d(e);var n=g(t,!0);return d(r),f(X,n)?(r.enumerable?(f(e,U)&&e[U][n]&&(e[U][n]=!1),r=b(r,{enumerable:y(0,!1)})):(f(e,U)||$(e,U,y(1,{})),e[U][n]=!0),ne(e,n,r)):$(e,n,r)},se=function(e,t){d(e);var r=m(t),n=w(r).concat(fe(r));return M(n,(function(t){s&&!ue.call(r,t)||ae(e,t,r[t])})),e},ue=function(e){var t=g(e,!0),r=Q.call(this,t);return!(this===G&&f(X,t)&&!f(Z,t))&&(!(r||!f(this,t)||!f(X,t)||f(this,U)&&this[U][t])||r)},ce=function(e,t){var r=m(e),n=g(t,!0);if(r!==G||!f(X,n)||f(Z,n)){var i=Y(r,n);return!i||!f(X,n)||f(r,U)&&r[U][n]||(i.enumerable=!0),i}},le=function(e){var t=z(m(e)),r=[];return M(t,(function(e){f(X,e)||f(O,e)||r.push(e)})),r},fe=function(e){var t=e===G,r=z(t?Z:m(e)),n=[];return M(r,(function(e){!f(X,e)||t&&!f(G,e)||n.push(X[e])})),n};(u||(k((q=function(){if(this instanceof q)throw TypeError("Symbol is not a constructor");var e=arguments.length&&void 0!==arguments[0]?String(arguments[0]):void 0,t=R(e),r=function(e){this===G&&r.call(Z,e),f(this,U)&&f(this[U],t)&&(this[U][t]=!1),ne(this,t,y(1,e))};return s&&re&&ne(G,t,{configurable:!0,set:r}),ie(t,e)}).prototype,"toString",(function(){return B(this).tag})),k(q,"withoutSetter",(function(e){return ie(R(e),e)})),I.f=ue,E.f=ae,P.f=ce,S.f=x.f=le,A.f=fe,C.f=function(e){return ie(N(e),e)},s&&($(q.prototype,"description",{configurable:!0,get:function(){return B(this).description}}),a||k(G,"propertyIsEnumerable",ue,{unsafe:!0}))),n({global:!0,wrap:!0,forced:!u,sham:!u},{Symbol:q}),M(w(ee),(function(e){L(e)})),n({target:"Symbol",stat:!0,forced:!u},{for:function(e){var t=String(e);if(f(K,t))return K[t];var r=q(t);return K[t]=r,J[r]=t,r},keyFor:function(e){if(!oe(e))throw TypeError(e+" is not a symbol");if(f(J,e))return J[e]},useSetter:function(){re=!0},useSimple:function(){re=!1}}),n({target:"Object",stat:!0,forced:!u,sham:!s},{create:function(e,t){return void 0===t?b(e):se(b(e),t)},defineProperty:ae,defineProperties:se,getOwnPropertyDescriptor:ce}),n({target:"Object",stat:!0,forced:!u},{getOwnPropertyNames:le,getOwnPropertySymbols:fe}),n({target:"Object",stat:!0,forced:l((function(){A.f(1)}))},{getOwnPropertySymbols:function(e){return A.f(v(e))}}),H)&&n({target:"JSON",stat:!0,forced:!u||l((function(){var e=q();return"[null]"!=H([e])||"{}"!=H({a:e})||"{}"!=H(Object(e))}))},{stringify:function(e,t,r){for(var n,i=[e],o=1;arguments.length>o;)i.push(arguments[o++]);if(n=t,(h(t)||void 0!==e)&&!oe(e))return p(t)||(t=function(e,t){if("function"==typeof n&&(t=n.call(this,e,t)),!oe(t))return t}),i[1]=t,H.apply(null,i)}});q.prototype[F]||_(q.prototype,F,q.prototype.valueOf),j(q,"Symbol"),O[U]=!0},function(e,t,r){"use strict";var n=r(11),i=r(25),o=r(7),a=r(26),s=r(22),u=r(24).f,c=r(124),l=o.Symbol;if(i&&"function"==typeof l&&(!("description"in l.prototype)||void 0!==l().description)){var f={},p=function(){var e=arguments.length<1||void 0===arguments[0]?void 0:String(arguments[0]),t=this instanceof p?new l(e):void 0===e?l():l(e);return""===e&&(f[t]=!0),t};c(p,l);var h=p.prototype=l.prototype;h.constructor=p;var d=h.toString,v="Symbol(test)"==String(l("test")),m=/^Symbol\((.*)\)[^)]+$/;u(h,"description",{configurable:!0,get:function(){var e=s(this)?this.valueOf():this,t=d.call(e);if(a(f,e))return"";var r=v?t.slice(7,-1):t.replace(m,"$1");return""===r?void 0:r}}),n({global:!0,forced:!0},{Symbol:p})}},function(e,t,r){r(131)("iterator")},function(e,t,r){"use strict";var n=r(144).charAt,i=r(46),o=r(133),a=i.set,s=i.getterFor("String Iterator");o(String,"String",(function(e){a(this,{type:"String Iterator",string:String(e),index:0})}),(function(){var e,t=s(this),r=t.string,i=t.index;return i>=r.length?{value:void 0,done:!0}:(e=n(r,i),t.index+=e.length,{value:e,done:!1})}))},function(e,t,r){"use strict";var n=r(11),i=r(22),o=r(74),a=r(47),s=r(15),u=r(39),c=r(113),l=r(65),f=r(12)("species"),p=[].slice,h=Math.max;n({target:"Array",proto:!0,forced:!l("slice")},{slice:function(e,t){var r,n,l,d=u(this),v=s(d.length),m=a(e,v),g=a(void 0===t?v:t,v);if(o(d)&&("function"!=typeof(r=d.constructor)||r!==Array&&!o(r.prototype)?i(r)&&null===(r=r[f])&&(r=void 0):r=void 0,r===Array||void 0===r))return p.call(d,m,g);for(n=new(void 0===r?Array:r)(h(g-m,0)),l=0;m"+this.listID+""),headers:{SOAPAction:"http://schemas.microsoft.com/sharepoint/soap/GetList"}}).then((function(e){var t,r,n,i,o,a,s,u,c,l,f,p,h,d=[],v=e.getElementsByTagName("Field"),m=0,g=e.getElementsByTagName("List");for(r=(g=g.length>0?g[0]:null).attributes,d._List={},s=0;s0&&(t.Property[p[0].firstChild.nodeValue]=h.length>0?h[0].firstChild.nodeValue:null);break;default:t.Choices=[]}t[n]=i}if((o=v[s].getElementsByTagName("Default").length)>0){a=v[s].getElementsByTagName("Default"),d[m].DefaultValue=[];for(var y=0;yA;A++)if((p||A in w)&&(y=S(g=w[A],A,b),e))if(t)E[A]=y;else if(y)switch(e){case 3:return!0;case 5:return g;case 6:return A;case 2:u.call(E,g)}else if(l)return!1;return f?-1:c||l?l:E}};e.exports={forEach:c(0),map:c(1),filter:c(2),some:c(3),every:c(4),find:c(5),findIndex:c(6)}},function(e,t,r){"use strict";var n=r(114),i=r(150),o=r(23),a=r(33),s=r(48),u=r(115),c=r(15),l=r(116),f=r(81),p=r(8),h=[].push,d=Math.min,v=!p((function(){return!RegExp(4294967295,"y")}));n("split",2,(function(e,t,r){var n;return n="c"=="abbc".split(/(b)*/)[1]||4!="test".split(/(?:)/,-1).length||2!="ab".split(/(?:ab)*/).length||4!=".".split(/(.?)(.?)/).length||".".split(/()()/).length>1||"".split(/.?/).length?function(e,r){var n=String(a(this)),o=void 0===r?4294967295:r>>>0;if(0===o)return[];if(void 0===e)return[n];if(!i(e))return t.call(n,e,o);for(var s,u,c,l=[],p=(e.ignoreCase?"i":"")+(e.multiline?"m":"")+(e.unicode?"u":"")+(e.sticky?"y":""),d=0,v=new RegExp(e.source,p+"g");(s=f.call(v,n))&&!((u=v.lastIndex)>d&&(l.push(n.slice(d,s.index)),s.length>1&&s.index=o));)v.lastIndex===s.index&&v.lastIndex++;return d===n.length?!c&&v.test("")||l.push(""):l.push(n.slice(d)),l.length>o?l.slice(0,o):l}:"0".split(void 0,0).length?function(e,r){return void 0===e&&0===r?[]:t.call(this,e,r)}:t,[function(t,r){var i=a(this),o=null==t?void 0:t[e];return void 0!==o?o.call(t,i,r):n.call(String(i),t,r)},function(e,i){var a=r(n,e,this,i,n!==t);if(a.done)return a.value;var f=o(e),p=String(this),h=s(f,RegExp),m=f.unicode,g=(f.ignoreCase?"i":"")+(f.multiline?"m":"")+(f.unicode?"u":"")+(v?"y":"g"),y=new h(v?f:"^(?:"+f.source+")",g),b=void 0===i?4294967295:i>>>0;if(0===b)return[];if(0===p.length)return null===l(y,p)?[p]:[];for(var w=0,S=0,x=[];S0?n:r)(e)}},function(e,t,r){"use strict";var n=r(11),i=r(58),o=r(39),a=r(80),s=[].join,u=i!=Object,c=a("join",",");n({target:"Array",proto:!0,forced:u||c},{join:function(e){return s.call(o(this),void 0===e?",":e)}})},function(e,t,r){var n=r(34),i=Date.prototype,o=i.toString,a=i.getTime;new Date(NaN)+""!="Invalid Date"&&n(i,"toString",(function(){var e=a.call(this);return e==e?o.call(this):"Invalid Date"}))},function(e,t,r){"use strict";r.d(t,"a",(function(){return n}));r(6),r(14);function n(e){return e.replace(/&(?!amp;|lt;|gt;)/g,"&").replace(//g,">")}},function(e,t,r){var n=r(58),i=r(33);e.exports=function(e){return n(i(e))}},function(e,t,r){"use strict";var n=r(11),i=r(61).indexOf,o=r(80),a=[].indexOf,s=!!a&&1/[1].indexOf(1,-0)<0,u=o("indexOf");n({target:"Array",proto:!0,forced:s||u},{indexOf:function(e){return s?a.apply(this,arguments)||0:i(this,e,arguments.length>1?arguments[1]:void 0)}})},function(e,t,r){"use strict";var n=r(11),i=r(152);n({target:"Array",proto:!0,forced:[].forEach!=i},{forEach:i})},function(e,t,r){"use strict";(function(e){r.d(t,"a",(function(){return b}));r(17),r(18),r(19),r(117),r(41),r(40),r(13),r(36),r(66),r(21),r(37),r(221),r(109),r(2),r(3),r(6),r(20),r(121),r(14),r(32),r(223),r(44),r(16),r(4);var n=r(27),i=r(86),o=r(68),a=r(30),s=r(38),u=r(88),c=r(1),l=r(0),f=r(69),p=r(89),h=r(90);function d(e){return(d="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function v(e,t,r,n,i,o,a){try{var s=e[o](a),u=s.value}catch(e){return void r(e)}s.done?t(u):Promise.resolve(u).then(n,i)}function m(e,t){for(var r=0;r(.*<\/DateRangesOverlap>)(.*)<\/And>$/))&&3===v.length&&(m.WhereCAML=""+v[2]+v[1]+""),S=S.length>0?S.map((function(e){return""+e+m.WhereCAML+""})):m.WhereCAML,d.where=S,d.whereCAML=!0),d.fields+=(""===d.fields?"":",")+m.Fields.join(","),d.orderby+=(""===d.orderby?"":",")+m.OrderBy,d.calendarViaView=d.calendar,d.calendar=!1,d.view="";case 40:if(!Array.isArray(d.where)){t.next=46;break}return x=d.where.length,A=0,t.abrupt("return",{v:Promise.all(d.where.map((function(e){var t={};for(var r in d)t[r]=d[r];return t.where=e,b.call(w,t).then((function(e){return"function"==typeof t.progress&&t.progress(++A,x),e}))}))).then((function(e){var t=[];return e.forEach((function(e){return t=t.concat(e)})),t}))});case 46:d.originalWhere=d.where,d.nextWhere=[];case 48:if(d.progress=d.progress||function(){},P="",I="",D="",N="",L="",d.fields.length>0)for("string"==typeof d.fields&&(d.fields=d.fields.replace(/^\s+/,"").replace(/\s+$/,"").replace(/( )?,( )?/g,",").split(",")),E=0;E';if(""!==d.orderby)for(_=d.orderby.split(","),E=0;E<_.length;E++)k="ASC",(T=_[E].trim().split(" ")).length>0&&(2==T.length&&(k=T[1].toUpperCase()),I+='');if(!0!==d.calendar&&!0!==d.calendarViaView||""!==I||(I=''),""!==d.groupby)for(O=d.groupby.split(","),E=0;E';if(Array.isArray(d.merge)&&(d.mergeData=d.mergeData||[]),!0===d.calendar||!0===d.calendarViaView)for(R=["Title","EventDate","EndDate","Duration","fAllDayEvent","fRecurrence","RecurrenceData","ID","MasterSeriesItemID","UID","RecurrenceID"],E=0;E';if(!d.folderOptions||d.folderOptions.rootFolder){t.next=61;break}return t.next=59,a.a.call(w);case 59:V=t.sent,d.folderOptions.rootFolder=V._List.RootFolder;case 61:if(void 0!==d.queryOptions){t.next=81;break}if(d._queryOptions=""+d.dateInUTC+'True'+(""===P?"":"False")+""+d.expandUserField+"",!d.folderOptions){t.next=78;break}t.t0=d.folderOptions.show,t.next="FilesAndFolders_Recursive"===t.t0?67:"FilesOnly_InFolder"===t.t0?69:"FilesAndFolders_InFolder"===t.t0?71:(t.t0,73);break;case 67:return C="RecursiveAll",t.abrupt("break",74);case 69:return C="FilesOnly",t.abrupt("break",74);case 71:return C="",t.abrupt("break",74);case 73:C="Recursive";case 74:d._queryOptions+='',d.folderOptions.path&&(d._queryOptions+=""+d.folderOptions.rootFolder+"/"+d.folderOptions.path+""),t.next=79;break;case 78:d._queryOptions+='';case 79:t.next=82;break;case 81:d._queryOptions=d.queryOptions;case 82:return d.calendarOptions&&(d._queryOptions+=""+d.calendarOptions.referenceDate+"v3"+d.calendarOptions.splitRecurrence+""),""!==d.where&&(L=d.whereCAML?d.where:Object(o.a)(d.where)),!0===d.calendar&&(j="<"+d.calendarOptions.range+" />",L=""!==L?""+L+j+"":j),L=d.whereFct(L),N=""+w.listID+""+(d.viewID||"")+""+(""!=L?""+L+"":"")+(""!=D?""+D+"":"")+(""!=I?""+I+"":"")+""+P+""+d.rowlimit+""+d._queryOptions+"",N=Object(c.a)("GetListItems",N),t.next=90,l.a.call(w,{url:w.url+"/_vti_bin/Lists.asmx",body:N});case 90:if(M=t.sent,ae=[],0===(U=M.getElementsByTagName("z:row")).length&&(U=M.getElementsByTagName("row")),ce=d.json&&!d.join&&!d.innerjoin&&!d.outerjoin&&!d.joinIndex,d.showListInAttribute)for(le=0,fe=U.length;le0)for(E=0,W=ae.length;E0)){t.next=110;break}if(0===d.results.length&&(d.results=ae),d.progress(d.results.length),!z){t.next=107;break}return d.listItemCollectionPositionNext=Object(s.a)(z),t.abrupt("return",{v:b.call(w,d)});case 107:ae=d.results;case 108:t.next=118;break;case 110:if(!(d.nextWhere.length>0)){t.next=116;break}return 0===d.results.length&&(d.results=ae),d.where=d.nextWhere.slice(0),t.abrupt("return",{v:b.call(w,d)});case 116:d.where=d.originalWhere,ae=d.results.length>0?d.results:ae;case 118:if(!d.joinData){t.next=139;break}for(G=d.joinData.noindex,q=[],H="",$={length:0},G.length||alert("$SP.get() -- Error 'get': you must define the ON clause when JOIN is used."),E=0,W=ae.length;E0)){t.next=159;break}for(re=[],ne=[],ie=[],d.join.onLookup&&(Se=d.join.onLookup,d.join.on="'"+(d.join.alias||d.join.list)+"'."+d.join.onLookup+" = '"+d.alias+"'.ID"),G=y(d.join.on),re.noindex=G,E=0,W=ae.length;E0){for(oe=Object(h.a)(ie,60),F=0;F0)){t.next=173;break}return(se=d.merge.shift()).merge=d.merge.slice(0),se.json=d.join,w.listID=se.list,w.url=se.url||w.url,se.mergeData=d.mergeData.concat(ae.map((function(e){return e.Source=ue,e}))),t.next=170,b.call(w,se);case 170:ae=t.sent,t.next=174;break;case 173:ae=d.mergeData.concat(ae.map((function(e){return e.Source=ue,e})));case 174:if(ae.NextPage=z,!d.json||ce){t.next=180;break}if(Ee=[],!(ae.length>0&&"function"==typeof ae[0].getAttribute)){t.next=180;break}for(Ie=0,_e=ae.length;Ie<_e;Ie++)Ee.push(ae[Ie].getAttributes());return t.abrupt("return",{v:Promise.resolve(Ee)});case 180:return t.abrupt("return",{v:Promise.resolve(ae)});case 181:case"end":return t.stop()}}),t)}))(),"t0",2);case 2:if("object"!==d(m=t.t0)){t.next=5;break}return t.abrupt("return",m.v);case 5:t.next=10;break;case 7:return t.prev=7,t.t1=t.catch(0),t.abrupt("return",Promise.reject(t.t1));case 10:case"end":return t.stop()}}),t,null,[[0,7]])})),(w=function(){var e=this,r=arguments;return new Promise((function(n,i){var o=t.apply(e,r);function a(e){v(o,n,i,a,s,"next",e)}function s(e){v(o,n,i,a,s,"throw",e)}a(void 0)}))}).apply(this,arguments)}}).call(this,r(10))},function(e,t){var r={}.toString;e.exports=function(e){return r.call(e).slice(8,-1)}},function(e,t,r){var n=r(7),i=r(145),o=r(152),a=r(28);for(var s in i){var u=n[s],c=u&&u.prototype;if(c&&c.forEach!==o)try{a(c,"forEach",o)}catch(e){c.forEach=o}}},function(e,t,r){"use strict";(function(e){r.d(t,"a",(function(){return p}));r(17),r(18),r(19),r(13),r(36),r(21),r(54),r(2),r(3),r(20),r(16),r(4);var n=r(0),i=r(1),o=r(27),a=r(55),s=r(56),u=r(38),c=r(83);function l(e){return(l="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function f(e,t,r,n,i,o,a){try{var s=e[o](a),u=s.value}catch(e){return void r(e)}s.done?t(u):Promise.resolve(u).then(n,i)}function p(e,t){return h.apply(this,arguments)}function h(){var t;return t=regeneratorRuntime.mark((function t(r,f){var h,d,v,m,g,y,b,w,S,x,A,P,E,I,_,k,T=this;return regeneratorRuntime.wrap((function(t){for(;;)switch(t.prev=t.next){case 0:if(t.prev=0,this.listID){t.next=3;break}throw"[SharepointPlus 'add'] the list ID/Name is required.";case 3:if(this.url){t.next=5;break}throw"[SharepointPlus 'add'] not able to find the URL!";case 5:if(h={},Object(o.a)(!0,h,f),h.escapeChar=null==h.escapeChar||h.escapeChar,h.progress=h.progress||function(){},h.packetsize=h.packetsize||30,h.rootFolder=h.rootFolder||"",Array.isArray(r)||(r=[r]),d=r.length,h.progressVar=h.progressVar||{current:0,max:d,passed:[],failed:[],eventID:"spAdd"+(""+Math.random()).slice(2)},!(d>h.packetsize)){t.next=22;break}v=r.slice(0),m=v.splice(0,h.packetsize),e._SP_ADD_PROGRESSVAR[h.progressVar.eventID]=function(e){return p.call(T,v,e)},d=(r=m).length,t.next=25;break;case 22:if(0!==d){t.next=25;break}return h.progress(1,1),t.abrupt("return",Promise.resolve({passed:[],failed:[]}));case 25:h.progressVar.current+=d,S='",w=0;case 28:if(!(w',S+="New",t.t0=regeneratorRuntime.keys(r[w]);case 32:if((t.t1=t.t0()).done){t.next=63;break}if(b=t.t1.value,!Object.prototype.hasOwnProperty.call(r[w],b)){t.next=61;break}g=b,y=r[w][b],Array.isArray(y)&&(y=0===y.length?"":";#"+y.join(";#")+";#"),t.t2=g,t.next="RecurrenceData"===t.t2?41:58;break;case 41:if("object"===l(y)&&(y=Object(a.a)(y)),void 0===r[w].fRecurrence&&(S+="1"),void 0===r[w].EventType&&(S+="1"),void 0===r[w].UID&&(S+="{"+Object(s.a)()+"}"),void 0===r[w].fAllDayEvent&&(S+="0"),void 0!==r[w].TimeZone){t.next=56;break}if(!e._SP_CACHE_TIMEZONEINFO[this.url]){t.next=51;break}S+=""+e._SP_CACHE_TIMEZONEINFO[this.url].ID+"",t.next=56;break;case 51:return t.next=53,c.a.call(this,{url:this.url});case 53:return x=t.sent,e._SP_CACHE_TIMEZONEINFO[this.url]=x,t.abrupt("return",p.call(this,r,f));case 56:return S+="",t.abrupt("break",61);case 58:"boolean"==typeof y&&(y=y?"1":"0"),h.escapeChar&&"string"==typeof y&&(y=Object(u.a)(y)),S+=""+y+"";case 61:t.next=32;break;case 63:S+="";case 64:w++,t.next=28;break;case 67:return S+="",t.next=70,n.a.call(this,{url:this.url+"/_vti_bin/lists.asmx",body:Object(i.a)("UpdateListItems",""+this.listID+""+S+""),headers:{SOAPAction:"http://schemas.microsoft.com/sharepoint/soap/UpdateListItems"}});case 70:for(A=t.sent,P=A.getElementsByTagName("Result"),E=P.length,I=h.progressVar.passed,_=h.progressVar.failed,w=0;w0)){t.next=79;break}return e._SP_ADD_PROGRESSVAR[h.progressVar.eventID]=void 0,t.abrupt("return",Promise.resolve({passed:I,failed:_}));case 79:return t.abrupt("return",e._SP_ADD_PROGRESSVAR[h.progressVar.eventID](h));case 80:t.next=84;break;case 82:return e._SP_ADD_PROGRESSVAR[h.progressVar.eventID]&&(e._SP_ADD_PROGRESSVAR[h.progressVar.eventID]=void 0),t.abrupt("return",Promise.resolve({passed:I,failed:_}));case 84:t.next=89;break;case 86:return t.prev=86,t.t3=t.catch(0),t.abrupt("return",Promise.reject(t.t3));case 89:case"end":return t.stop()}}),t,this,[[0,86]])})),(h=function(){var e=this,r=arguments;return new Promise((function(n,i){var o=t.apply(e,r);function a(e){f(o,n,i,a,s,"next",e)}function s(e){f(o,n,i,a,s,"throw",e)}a(void 0)}))}).apply(this,arguments)}}).call(this,r(10))},function(e,t,r){var n,i,o,a=r(167),s=r(7),u=r(22),c=r(28),l=r(26),f=r(71),p=r(73),h=s.WeakMap;if(a){var d=new h,v=d.get,m=d.has,g=d.set;n=function(e,t){return g.call(d,e,t),t},i=function(e){return v.call(d,e)||{}},o=function(e){return m.call(d,e)}}else{var y=f("state");p[y]=!0,n=function(e,t){return c(e,y,t),t},i=function(e){return l(e,y)?e[y]:{}},o=function(e){return l(e,y)}}e.exports={set:n,get:i,has:o,enforce:function(e){return o(e)?i(e):n(e,{})},getterFor:function(e){return function(t){var r;if(!u(t)||(r=i(t)).type!==e)throw TypeError("Incompatible receiver, "+e+" required");return r}}}},function(e,t,r){var n=r(35),i=Math.max,o=Math.min;e.exports=function(e,t){var r=n(e);return r<0?i(r+t,0):o(r,t)}},function(e,t,r){var n=r(23),i=r(63),o=r(12)("species");e.exports=function(e,t){var r,a=n(e).constructor;return void 0===a||null==(r=n(a)[o])?t:i(r)}},function(e,t,r){"use strict";(function(e){r.d(t,"a",(function(){return a}));r(37),r(2),r(3),r(6),r(14),r(32),r(82),r(4);var n=r(0),i=r(5);function o(e,t,r,n,i,o,a){try{var s=e[o](a),u=s.value}catch(e){return void r(e)}s.done?t(u):Promise.resolve(u).then(n,i)}function a(e){return s.apply(this,arguments)}function s(){var t;return t=regeneratorRuntime.mark((function t(r){var o,a,s,u;return regeneratorRuntime.wrap((function(t){for(;;)switch(t.prev=t.next){case 0:if(t.prev=0,(r=r||{}).cache=!1!==r.cache,(s=r.url||this.url)&&s.startsWith("http")){t.next=8;break}return t.next=7,i.a.call(this);case 7:s=t.sent;case 8:if(s=s.replace(/\/$/,""),r.cache&&(a=e._SP_CACHE_REQUESTDIGEST[s]),!a){t.next=13;break}if(!((new Date).getTime()-new Date(a.split(",")[1]).getTime()<1800)){t.next=13;break}return t.abrupt("return",Promise.resolve(a));case 13:if(!(e._SP_ISBROWSER&&document&&r.cache)){t.next=19;break}if(!(o=document.querySelector("#__REQUESTDIGEST"))){t.next=19;break}return a=o.value,e._SP_CACHE_REQUESTDIGEST[s]=a,t.abrupt("return",Promise.resolve(a));case 19:return t.next=21,n.a.call(this,{url:s+"/_api/contextinfo",method:"POST"});case 21:return u=t.sent,a=u.d.GetContextWebInformation.FormDigestValue,e._SP_CACHE_REQUESTDIGEST[s]=a,e._SP_ISBROWSER&&document&&(o=document.querySelector("#__REQUESTDIGEST"))&&(o.value=a),t.abrupt("return",Promise.resolve(a));case 28:return t.prev=28,t.t0=t.catch(0),t.abrupt("return",Promise.reject(t.t0));case 31:case"end":return t.stop()}}),t,this,[[0,28]])})),(s=function(){var e=this,r=arguments;return new Promise((function(n,i){var a=t.apply(e,r);function s(e){o(a,n,i,s,u,"next",e)}function u(e){o(a,n,i,s,u,"throw",e)}s(void 0)}))}).apply(this,arguments)}}).call(this,r(10))},function(e,t,r){var n=r(25),i=r(99),o=r(51),a=r(39),s=r(59),u=r(26),c=r(122),l=Object.getOwnPropertyDescriptor;t.f=n?l:function(e,t){if(e=a(e),t=s(t,!0),c)try{return l(e,t)}catch(e){}if(u(e,t))return o(!i.f.call(e,t),e[t])}},function(e,t){e.exports=function(e,t){return{enumerable:!(1&e),configurable:!(2&e),writable:!(4&e),value:t}}},function(e,t){e.exports=!1},function(e,t,r){var n=r(125),i=r(7),o=function(e){return"function"==typeof e?e:void 0};e.exports=function(e,t){return arguments.length<2?o(n[e])||o(i[e]):n[e]&&n[e][t]||i[e]&&i[e][t]}},function(e,t,r){"use strict";var n=r(11),i=r(47),o=r(35),a=r(15),s=r(29),u=r(107),c=r(113),l=r(65),f=Math.max,p=Math.min;n({target:"Array",proto:!0,forced:!l("splice")},{splice:function(e,t){var r,n,l,h,d,v,m=s(this),g=a(m.length),y=i(e,g),b=arguments.length;if(0===b?r=n=0:1===b?(r=0,n=g-y):(r=b-2,n=p(f(o(t),0),g-y)),g+r-n>9007199254740991)throw TypeError("Maximum allowed length exceeded");for(l=u(m,n),h=0;hg-n+r;h--)delete m[h-1]}else if(r>n)for(h=g-n;h>y;h--)v=h+r-1,(d=h+n-1)in m?m[v]=m[d]:delete m[v];for(h=0;h]+>([^<]+)<\/[^>]+>$/m,"$1")};return["firstDayOfWeek","daily","weekly","monthly","monthlyByDay","yearly","yearlyByDay","windowEnd","repeatInstances"].forEach((function(e){var n=i.getElementsByTagName(e);if(1===n.length){var o=n[0];switch(/y$/.test(e)&&(r.type=e),e){case"firstDayOfWeek":for(var s=a(o),u=0;u"+(e.firstDayOfWeek.toLowerCase().slice(0,2)||"mo")+"<"+e.type+" ",e.type){case"daily":r+=(e.frequency?'dayFrequency="'+e.frequency+'"':'weekday="TRUE"')+" />";break;case"weekly":t.forEach((function(t){e.on[t]&&(r+=t.slice(0,2)+'="TRUE" ')})),r+='weekFrequency="'+e.frequency+'" />';break;case"monthly":r+='monthFrequency="'+e.frequency+'" day="'+e.on.day+'" />';break;case"yearlyByDay":case"monthlyByDay":["day","weekday","weekend"].concat(t).forEach((function(t,n){e.on[t]&&(r+=n<3?t+("weekend"===t?"_day":""):t.slice(0,2),-1===(r+='="TRUE" ').indexOf("ayOfMonth")&&(r+="week"+("monthlyByDay"===e.type?"d":"D")+'ayOfMonth="'+e.on[t]+'" '),t=e.on[t])})),e.on.month&&(r+=' month="'+e.on.month+'"'),r+=("monthlyByDay"===e.type?"month":"year")+'Frequency="'+e.frequency+'" />';break;case"yearly":r+='yearFrequency="'+e.frequency+'" month="'+e.on.month+'" day="'+e.on.day+'" />'}return r+="",e.endDate?r+=""+new Date(e.endDate).toISOString().replace(/.000Z/,"Z")+"":e.endAfter?r+=""+e.endAfter+"":r+="FALSE",r+=""}return null}},function(e,t,r){"use strict";r.d(t,"a",(function(){return n}));r(37),r(2),r(118);function n(){for(var e="",t=0;t<32;t++){var r=Math.floor(16*Math.random());switch(t){case 8:e+="-";break;case 12:r=4,e+="-";break;case 16:r=3&r|8,e+="-";break;case 20:e+="-"}e+=r.toString(16)}return e}},function(e,t,r){"use strict";(function(e){r.d(t,"a",(function(){return i}));r(17),r(18),r(19),r(13),r(21),r(54),r(67),r(2),r(20),r(16);function n(e){return(n="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function i(t,r){var o,a=this;switch(arguments.length){case 0:throw"Error 'removeNotify': you must provide 'name' or 'options'.";case 2:if("object"!==n(r))throw"Error 'removeNotify': you must provide an object for 'options'."}if(1===arguments.length&&"object"===n(t)&&(r=t,t=void 0),(r=r||{all:!1}).timeout=!0===r.timeout,!1===e._SP_NOTIFY_READY&&e._SP_NOTIFY_QUEUE.length>0)return setTimeout((function(){return i.call(a,t,r)}),150),this;if(!0===r.all){for(var s=[];e._SP_NOTIFY.length>0;)o=e._SP_NOTIFY.shift(),!1===r.includeSticky&&!0===o.options.sticky?s.push(o):(SP.UI.Notify.removeNotification(o.id),setTimeout((function(){return o.options.after.call(a,o.name,!1)}),150));e._SP_NOTIFY=s.slice(0)}else if(void 0!==t)for(var u=0,c=e._SP_NOTIFY.length;ul;)if((s=u[l++])!=s)return!0}else for(;c>l;l++)if((e||l in u)&&u[l]===r)return e||l||0;return!e&&-1}};e.exports={includes:a(!0),indexOf:a(!1)}},function(e,t,r){var n=r(24).f,i=r(26),o=r(12)("toStringTag");e.exports=function(e,t,r){e&&!i(e=r?e:e.prototype,o)&&n(e,o,{configurable:!0,value:t})}},function(e,t){e.exports=function(e){if("function"!=typeof e)throw TypeError(String(e)+" is not a function");return e}},function(e,t){e.exports={}},function(e,t,r){var n=r(8),i=r(12),o=r(112),a=i("species");e.exports=function(e){return o>=51||!n((function(){var t=[];return(t.constructor={})[a]=function(){return{foo:1}},1!==t[e](Boolean).foo}))}},function(e,t,r){"use strict";var n=r(11),i=r(31).map,o=r(8),a=r(65)("map"),s=a&&!o((function(){[].map.call({length:-1,0:1},(function(e){throw e}))}));n({target:"Array",proto:!0,forced:!a||!s},{map:function(e){return i(this,e,arguments.length>1?arguments[1]:void 0)}})},function(e,t,r){var n=r(25),i=r(24).f,o=Function.prototype,a=o.toString,s=/^\s*function ([^ (]*)/;!n||"name"in o||i(o,"name",{configurable:!0,get:function(){try{return a.call(this).match(s)[1]}catch(e){return""}}})},function(e,t,r){"use strict";r.d(t,"a",(function(){return o}));r(17),r(18),r(19),r(41),r(13),r(36),r(21),r(2),r(6),r(20),r(14),r(44),r(16);var n=r(38);function i(e){return(i="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function o(e,t){var r=e.replace(/(\s+)?(=|~=|<=|>=|~<>|<>|<|>| LIKE | IN )(\s+)?/g,"$2").replace(/""|''/g,"Null").replace(/==/g,"=");/\w+ IN \[([^[]+,)?Null,?/.test(r)&&(r=r.replace(/(\w+) IN \[([^\[]+,)?Null(,[^\]]+)?\]/g,"($1 = Null OR $&)").replace(/\[([^\[]+,)?Null(,[^\]]+)?\]/g,"[$1$2]").replace(/(\[),|(,),|,(\])/g,"$1$2$3"));var a=[];t=!1!==t;for(var s=r.length,u="",c="",l=!1,f="",p={open:0},h=!1,d=0;d0&&d=0?(""!=u&&(a[0]="<"+u+">"+a[0]),a[0]+=o(r.substring(m+1,d)),""!=u&&(a[0]+=""),u=""):a[0]=o(r.substring(m+1,d));break;case"[":for(m=d,g=!1;d'+w.join('')+""+c,f="",c="",b>0&&(""!=u&&(a[0]="<"+u+">"+a[0]),a[0]+=a[b],""!=u&&(a[0]+=""),delete a[b],u="");break;case">":case"<":d++,"="==r.charAt(d)?(a.push("<"+(">"==v?"G":"L")+"eq>"),c=""==v?"G":"L")+"eq>"):"<"==v&&">"==r.charAt(d)?(a.push(""),c=""):(d--,a.push("<"+(">"==v?"G":"L")+"t>"),c=""==v?"G":"L")+"t>");break;case"~":("="==r.charAt(d+1)||"<"===r.charAt(d+1)&&">"===r.charAt(d+2))&&(h=!0);break;case"=":a.push(""),c="";break;case" ":" AND "==r.substring(d,d+5).toUpperCase()?(u="And",d+=4):" OR "==r.substring(d,d+4).toUpperCase()?(u="Or",d+=3):" LIKE "==r.slice(d,d+6).toUpperCase()?(d+=5,a.push(""),c=""):" IN "==r.slice(d,d+4).toUpperCase()?(d+=3,a.push(""),c=""):f+=v;break;case'"':case"'":for(var x=v,A="",P="";(v=r.charAt(++d))!=x&&d",f="";var E="Text";/\d{4}-\d\d?-\d\d?((T| )\d{2}:\d{2}:\d{2})?/.test(A)&&(E="DateTime",/\d{4}-\d\d?-\d\d?((T| )\d{2}:\d{2}:\d{2})/.test(A)&&(P=' IncludeTimeValue="TRUE"')),t&&(A=Object(n.a)(A)),"[Me]"===A?(A='',E="Integer"):"[Today"==A.slice(0,6)&&(E="DateTime",A=''),a[b]+='"+A+"",a[b]+=c,c="",b>0&&(""!=u&&(a[0]="<"+u+">"+a[0]),a[0]+=a[b],""!=u&&(a[0]+=""),delete a[b],u="");break;case"0":case"1":case"2":case"3":case"4":case"5":case"6":case"7":case"8":case"9":if(""!=c){for(var I=v;!isNaN(v=r.charAt(++d))&&d",f="",a[b]+=''+I.replace(/ $/,"")+"",a[b]+=c,c="",b>0&&(""!=u&&(a[0]="<"+u+">"+a[0]),a[0]+=a[b],""!=u&&(a[0]+=""),delete a[b],u=""),d-=2;break}default:""==c?f+=v:"n"==v.toLowerCase()&&"null"==r.substring(d,d+4).toLowerCase()?(b=a.length-1,""==c?(a[b]="",c=""):""==c&&(a[b]="",c=""),d+=3,a[b]+='',f="",a[b]+=c,c="",b>0&&(""!=u&&(a[0]="<"+u+">"+a[0]),a[0]+=a[b],""!=u&&(a[0]+=""),delete a[b],u="")):("t"===v.toLowerCase()&&"true"===r.substring(d,d+4).toLowerCase()||"f"===v.toLowerCase()&&"false"===r.substring(d,d+5).toLowerCase())&&(b=a.length-1,d+=3,"f"===v.toLowerCase()&&d++,a[b]+=''+("t"===v.toLowerCase()?1:0)+"",f="",a[b]+=c,c="",b>0&&(""!=u&&(a[0]="<"+u+">"+a[0]),a[0]+=a[b],""!=u&&(a[0]+=""),delete a[b],u=""))}}return a.join("")}},function(e,t,r){"use strict";r.d(t,"a",(function(){return n}));r(21),r(6),r(14),r(32);function n(e){if(!e)return{id:"",value:""};var t=e.split(";#");return t.length<=2?{id:t[0],value:void 0===t[1]?t[0]:t[1]}:{id:e.replace(/([0-9]+;#)([^;]+)/g,"$1").replace(/;#;#/g,",").slice(0,-2).split(","),value:e.replace(/([0-9]+;#)([^;]+)/g,"$2").split(";#")}}},function(e,t,r){"use strict";function n(e){var t={width:0,height:0},r={width:0,height:0},n=e||window,i=n.document,o=i.documentElement,a=i.querySelector("body");return t.width=n.innerWidth||o.clientWidth||a.clientWidth,t.height=n.innerHeight||o.clientHeight||a.clientHeight,r.width=Math.max(a.scrollWidth,o.scrollWidth,a.offsetWidth,o.offsetWidth,a.clientWidth,o.clientWidth),r.height=Math.max(a.scrollHeight,o.scrollHeight,a.offsetHeight,o.offsetHeight,a.clientHeight,o.clientHeight),document.all&&document.querySelector&&!document.addEventListener&&t.width+4==r.width&&t.height+4==r.height&&(t.width=r.width,t.height=r.height),{vw:t,doc:r}}r.d(t,"a",(function(){return n}))},function(e,t,r){var n=r(103),i=r(72),o=n("keys");e.exports=function(e){return o[e]||(o[e]=i(e))}},function(e,t){var r=0,n=Math.random();e.exports=function(e){return"Symbol("+String(void 0===e?"":e)+")_"+(++r+n).toString(36)}},function(e,t){e.exports={}},function(e,t,r){var n=r(43);e.exports=Array.isArray||function(e){return"Array"==n(e)}},function(e,t,r){var n,i=r(23),o=r(169),a=r(104),s=r(73),u=r(129),c=r(100),l=r(71),f=l("IE_PROTO"),p=function(){},h=function(e){return" diff --git a/docs/files.html b/docs/files.html index 388aa59..8c7d467 100644 --- a/docs/files.html +++ b/docs/files.html @@ -32,7 +32,7 @@ SharepointPlus @@ -49,7 +49,7 @@
  • Wiki
  • Changelog
  • - + @@ -298,7 +298,7 @@

    Example:

    Documentation built by @aymkdn (http://kodono.info) with JSDoc.
    This work is licensed under LGPL-3.
    Designed with Bootstrap from Twitter.
    -
    Automatically generated with JSDoc on Fri, 05 Mar 2021 13:23:39 GMT
    +
    Automatically generated with JSDoc on Thu, 09 Jun 2022 07:35:43 GMT
    diff --git a/docs/index.html b/docs/index.html index 21b6380..9acb55e 100644 --- a/docs/index.html +++ b/docs/index.html @@ -32,7 +32,7 @@ SharepointPlus @@ -49,7 +49,7 @@
  • Wiki
  • Changelog
  • - + @@ -59,7 +59,7 @@
    -

    SharepointPlus 6.1.5

    +

    SharepointPlus 6.2.0

    Important changes introduced with SharepointPlus v6.0. Make sure to read the announcement and to read the changelog.
    @@ -71,12 +71,13 @@

    Description

    SharepointPlus ($SP) is a JavaScript API for Sharepoint. This library offers some extended features for SharePoint entirely on client side (requires no server install). $SP will simplify your interactions with Sharepoint.

    -

    Sharepoint Support

    +

    SharePoint Support

      -
    • Sharepoint 2007 : SharepointPlus v3.0.5 is the last release tested with Sharepoint 2007 – after this version I cannot assure the retro-compatibility
    • -
    • Sharepoint 2010 : Compatible since SharepointPlus v3.14, but, please read carefully this noticeSP2010 is not tested anymore
    • -
    • Sharepoint 2013/2016/2019/Online : Compatible since SharepointPlus v3.13 – ✓ fully tested, 100% compatible!
    • +
    • SharePoint 2007 : Compatible until SharepointPlus v3.0.5SP2007 is not tested anymore
    • +
    • SharePoint 2010 : Compatible until SharepointPlus v5.2SP2010 is not tested anymore
    • +
    • SharePoint 2013 : Compatible until SharepointPlus v6.1.5SP2013 is not tested anymore
    • +
    • SharePoint Online : ✓ current supported version
    @@ -179,7 +180,7 @@

    `require` only

    In that case you won't benefit from the tree-shaking optimization.

    -

    Browser only

    +

    Browser only

    You can also just drop one file in your HTML document which will contain all the 70+ functions and all the polyfills to support the different browsers:

    @@ -187,12 +188,12 @@ 

    Browser only

    Note: because this file contains all the functions as well as all the polyfills, it might be quite big (~55KB gzipped).
    -

    Browser Support

    +

    Browser Support

    - IE10+, and all modern browsers (Firefox, Chrome, Edge, ...) (see coverage) are supported with the bundle for browsers. + IE11+, and all modern browsers (Firefox, Chrome, Edge, ...) (see coverage) are supported with the bundle for browsers.

    -
    +

    How does SharepointPlus work?

    -

    SharepointPlus uses the different Sharepoint Web Services to deal with the Sharepoint server.

    -

    Below is a comparative between Web Services (used by SharepointPlus) and REST API (used by some other libraries):

    +

    SharepointPlus mainly uses the different Web Services to deal with the SharePoint server.

    +

    But the library also uses several REST API, e.g. when the Web Service is not available anymore, or if the REST API provides more functionalities.

    +

    Examples

    @@ -381,7 +383,7 @@

    Older Versions

    Documentation built by @aymkdn (http://kodono.info) with JSDoc.
    This work is licensed under LGPL-3.
    Designed with Bootstrap from Twitter.
    -
    Automatically generated with JSDoc on Fri, 05 Mar 2021 13:23:39 GMT
    +
    Automatically generated with JSDoc on Thu, 09 Jun 2022 07:35:43 GMT
    diff --git a/docs/lists.html b/docs/lists.html index b2e20e9..ebe6493 100644 --- a/docs/lists.html +++ b/docs/lists.html @@ -32,7 +32,7 @@ SharepointPlus
    @@ -49,7 +49,7 @@
  • Wiki
  • Changelog
  • - + @@ -191,7 +191,7 @@

    Example:

    }); // to read a file and send it - // with something like: + // with something like: <input type="file" onchange="addAttachment(event)"> function addAttachment(event) { let files = event.target.files; let fileReader = new FileReader(); @@ -259,7 +259,8 @@

    Example:

    $SP().cleanResult("string;#Paul"); // -> "Paul" $SP().cleanResult("string;#"); // -> "" $SP().cleanResult(";#Paul;#Jacques;#Aymeric;#"); // -> "Paul;Jacques;Aymeric" - $SP().cleanResult(";#Paul;#Jacques;#Aymeric;#", ", "); // -> "Paul, Jacques, Aymeric" + $SP().cleanResult(";#Paul;#Jacques;#Aymeric;#", ", "); // -> "Paul, Jacques, Aymeric" + $SP().cleanResult("2022-01-19 00:00:00"); // -> "2022-01-19"
    @@ -790,12 +791,12 @@

    Example:

    -

    getVersions

    (source code) +

    getVersions

    (source code)
    getVersions(ID)
    -
    When versionning is activated on a list, you can use this function to get the different version label/number for a list item
    +
    When versionning is activated on a list, you can use this function to get the different versions of a list item

    Parameters:

    @@ -809,7 +810,7 @@

    Parameters:

    Returns:

    -
    Promise
    resolve(arrayOflistOfVersions)
    +
    Promise
    resolve(arrayOfVersions)
    @@ -817,17 +818,9 @@

    Returns:

    Example:

    -
    $SP().list("My List").getVersions({
    -    ID:1
    -  }).then(function(versions) {
    +          
    $SP().list("My List").getVersions(1234).then(function(versions) {
         versions.forEach(function(version) {
           console.log(version);
    -      // returns:
    -      //  - CheckInComment
    -      //  - Created
    -      //  - VersionID
    -      //  - IsCurrentVersion (boolean)
    -      //  - VersionLabel (e.g. "1.0", "2.0", …)
         })
       });
    @@ -896,12 +889,19 @@

    h

    Parameters:

    -
    String|Array
    perm
    Can be one of the values listed on https://docs.microsoft.com/en-us/previous-versions/office/developer/sharepoint-2010/ee556747(v%3Doffice.14) - return {Promise} A promise with a boolean (TRUE/FALSE) if the requested perm was a string, or an object ({perm1:BOOLEAN, perm2:BOOLEAN}) if it was an array
    +
    String|Array
    perm
    +
    +

    Returns:

    +
    + +
    Promise
    A promise with a boolean (TRUE/FALSE) if the requested perm was a string, or an object ({perm1:BOOLEAN, perm2:BOOLEAN}) if it was an array
    +
    +
    +

    Example:

    @@ -1391,7 +1391,7 @@

    Example:

    $SP().list("My List").removeAttachment({
         ID:1,
    -    filename:"https://mysite.share.point.com/Toolbox/Lists/Tasks/Attachments/2305/image1.png"
    +    fileURL:"https://mysite.share.point.com/Toolbox/Lists/Tasks/Attachments/2305/image1.png"
       })

    @@ -1426,7 +1426,7 @@

    Parameters:

    Returns:

    -
    Promise
    resolve(htmlPage), reject(errorMessage)
    +
    Promise
    resolve(true), reject(errorMessage)
    @@ -1801,7 +1801,7 @@

    Example:

    Documentation built by @aymkdn (http://kodono.info) with JSDoc.
    This work is licensed under LGPL-3.
    Designed with Bootstrap from Twitter.
    -
    Automatically generated with JSDoc on Fri, 05 Mar 2021 13:23:39 GMT
    +
    Automatically generated with JSDoc on Thu, 09 Jun 2022 07:35:43 GMT
    diff --git a/docs/modals.html b/docs/modals.html index ee91497..1ec098a 100644 --- a/docs/modals.html +++ b/docs/modals.html @@ -32,7 +32,7 @@ SharepointPlus @@ -49,7 +49,7 @@
  • Wiki
  • Changelog
  • - + @@ -380,7 +380,7 @@

    Parameters:

    Documentation built by @aymkdn (http://kodono.info) with JSDoc.
    This work is licensed under LGPL-3.
    Designed with Bootstrap from Twitter.
    -
    Automatically generated with JSDoc on Fri, 05 Mar 2021 13:23:39 GMT
    +
    Automatically generated with JSDoc on Thu, 09 Jun 2022 07:35:43 GMT
    diff --git a/docs/node.html b/docs/node.html index d9803fc..c43d00e 100644 --- a/docs/node.html +++ b/docs/node.html @@ -32,7 +32,7 @@ SharepointPlus @@ -49,7 +49,7 @@
  • Wiki
  • Changelog
  • - + @@ -60,7 +60,7 @@

    Methods

    -

    auth

    (source code) +

    auth

    (source code)
    auth(credentialOptions)
    @@ -117,7 +117,7 @@

    Example:

    -

    proxy

    (source code) +

    proxy

    (source code)
    proxy(proxyURL)
    @@ -169,7 +169,7 @@

    Example:

    Documentation built by @aymkdn (http://kodono.info) with JSDoc.
    This work is licensed under LGPL-3.
    Designed with Bootstrap from Twitter.
    -
    Automatically generated with JSDoc on Fri, 05 Mar 2021 13:23:39 GMT
    +
    Automatically generated with JSDoc on Thu, 09 Jun 2022 07:35:43 GMT
    diff --git a/docs/people.html b/docs/people.html index 40e3bdf..30db793 100644 --- a/docs/people.html +++ b/docs/people.html @@ -32,7 +32,7 @@ SharepointPlus
    @@ -49,7 +49,7 @@
  • Wiki
  • Changelog
  • - +
    @@ -160,7 +160,7 @@

    getMana

    Parameters:

    -
    String
    username Optional
    With or without the domain, and you can also use an email address, and if you leave it empty it's the current user by default
    Object
    setup Optional
    Options (see below)
    String
    setup.url Optional, Default: 'current website'
    The website url
    +
    String
    username Optional
    Username with the domain, and if you leave it empty it's the current user by default
    Object
    setup Optional
    Options (see below)
    String
    setup.url Optional, Default: 'current website'
    The website url
    Function
    setup.modify Optional
    Permits to modify the manager's username returned by the service
    @@ -177,7 +177,7 @@

    Returns:

    Example:

    -
    $SP().getManager("john_doe",{url:"http://my.si.te/subdir/"})
    +          
    $SP().getManager("domain\\john_doe",{url:"http://my.si.te/subdir/"})
       .then(function(manager) {
         console.log(manager); // 42;#Smith,, Jane,#i:0#.w|domain\Jane_Smith,#Jane_Smith@Domain.com,#Jane_Smith@Domain.com,#Smith,, Jane
         manager = $SP().getPeopleLookup(manager);
    @@ -185,7 +185,13 @@ 

    Example:

    }) .catch(function(err) { console.log("Err => ",err) - });
    + }); + + $SP().getManager("domain\\john_doe",{ + modify:function(managerUserName) { + return (managerUserName.startsWith('i:0') ? managerUserName : "i:0#.w|" + managerUserName); + } + })

    @@ -470,7 +476,7 @@

    Example:

    This work is licensed under LGPL-3.
    Designed with Bootstrap from Twitter.
    -
    Automatically generated with JSDoc on Fri, 05 Mar 2021 13:23:39 GMT
    +
    Automatically generated with JSDoc on Thu, 09 Jun 2022 07:35:43 GMT
    diff --git a/docs/utils.html b/docs/utils.html index 98e3f09..e9703dd 100644 --- a/docs/utils.html +++ b/docs/utils.html @@ -32,7 +32,7 @@ SharepointPlus
    @@ -49,7 +49,7 @@
  • Wiki
  • Changelog
  • - + @@ -485,17 +485,17 @@

    Returns:

    -

    hasREST

    (source code) +

    hasREST

    (source code)
    hasREST(settings)
    -
    Verify if the website supports REST API (Sharepoint 2013 and later)
    +
    In the earlier version of SharePointPlus, this function was used to check if the REST API was available – these days I assume everyone is now using at least SharePoint 2013, so this function always returns TRUE – if you don't have REST API you can still define _SP_CACHE_HASREST["url to check"]=false

    Parameters:

    -
    Object
    settings
    String
    settings.url Optional, Default: current
    To check another URL (or if you need on a Node server)
    +
    Object
    settings
    String
    settings.url Optional, Default: current
    To check another URL
    @@ -521,6 +521,44 @@

    Returns:

    + +
    +

    isSPO

    (source code) + +
    +
    isSPO(settings)
    +
    +
    Return TRUE if the SharePoint is SharePoint Online
    + +
    +

    Parameters:

    + +
    Object
    settings
    String
    settings.url Optional, Default: current
    To check another URL
    +
    + + + +
    +

    Returns:

    +
    + +
    Boolean
    Return TRUE if it's SPO or FALSE if not, or NULL is not able to determine it
    +
    +
    + + + +
    +
    + + + + + + + + +

    newGuid

    (source code) @@ -886,7 +924,7 @@

    Example:

    Documentation built by @aymkdn (http://kodono.info) with JSDoc.
    This work is licensed under LGPL-3.
    Designed with Bootstrap from Twitter.
    -
    Automatically generated with JSDoc on Fri, 05 Mar 2021 13:23:39 GMT
    +
    Automatically generated with JSDoc on Thu, 09 Jun 2022 07:35:43 GMT
    diff --git a/es5/index.js b/es5/index.js index 1a7db64..6918343 100644 --- a/es5/index.js +++ b/es5/index.js @@ -57,6 +57,7 @@ import getServerTime from './utils/getServerTime.js'; import getTimeZoneInfo from './utils/getTimeZoneInfo.js'; import getURL from './utils/getURL.js'; import hasREST from './utils/hasREST.js'; +import isSPO from './utils/isSPO.js'; import newGuid from './utils/newGuid.js'; import regionalDateFormat from './utils/regionalDateFormat.js'; import regionalSettings from './utils/regionalSettings.js'; @@ -134,6 +135,7 @@ export default spInit({ getTimeZoneInfo: getTimeZoneInfo, getURL: getURL, hasREST: hasREST, + isSPO: isSPO, newGuid: newGuid, regionalDateFormat: regionalDateFormat, regionalSettings: regionalSettings, diff --git a/es5/lists/addAttachment.js b/es5/lists/addAttachment.js index 0efc126..48b79eb 100644 --- a/es5/lists/addAttachment.js +++ b/es5/lists/addAttachment.js @@ -28,7 +28,7 @@ import restoreVersion from './restoreVersion.js'; }); // to read a file and send it - // with something like: + // with something like: <input type="file" onchange="addAttachment(event)"> function addAttachment(event) { let files = event.target.files; let fileReader = new FileReader(); diff --git a/es5/lists/cleanResult.js b/es5/lists/cleanResult.js index e5f3aa0..9b6fbcb 100644 --- a/es5/lists/cleanResult.js +++ b/es5/lists/cleanResult.js @@ -14,9 +14,10 @@ $SP().cleanResult("string;#"); // -> "" $SP().cleanResult(";#Paul;#Jacques;#Aymeric;#"); // -> "Paul;Jacques;Aymeric" $SP().cleanResult(";#Paul;#Jacques;#Aymeric;#", ", "); // -> "Paul, Jacques, Aymeric" + $SP().cleanResult("2022-01-19 00:00:00"); // -> "2022-01-19" */ export default function cleanResult(str, separator) { if (str === null || typeof str === "undefined") return ""; separator = separator || ";"; - return typeof str === "string" ? str.replace(/^(string;|float;|datetime;)#?/, "").replace(/;#-?[0-9]+;#/g, separator).replace(/^-?[0-9]+;#/, "").replace(/^;#|;#$/g, "").replace(/;#/g, separator) : str; + return typeof str === "string" ? str.replace(/^(string;|float;|datetime;)#?/, "").replace(/^(\d{4}-\d{2}-\d{2}) 00:00:00$/, "$1").replace(/;#-?[0-9]+;#/g, separator).replace(/^-?[0-9]+;#/, "").replace(/^;#|;#$/g, "").replace(/;#/g, separator) : str; } \ No newline at end of file diff --git a/es5/lists/get.js b/es5/lists/get.js index 512b026..01b09a6 100644 --- a/es5/lists/get.js +++ b/es5/lists/get.js @@ -56,7 +56,7 @@ function () { @param {String} on The ON clause @return {Array} array of {ListName1:FieldName1, ListName2:FieldName2} @example - $SP()._parseOn("'List1'.field1 = 'List2'.field2 AND 'List1'.Other_x0020_Field = 'List2'.Some_x0020_Field") + _parseOn("'List1'.field1 = 'List2'.field2 AND 'List1'.Other_x0020_Field = 'List2'.Some_x0020_Field") */ @@ -1112,8 +1112,7 @@ function _get() { if (joinLookupField) { if (joinWhereLookup.length > 0) { - // SP2013 limits to 60 items per IN - wh = arrayChunk(joinWhereLookup, 60); + wh = arrayChunk(joinWhereLookup, global._SP_MAXWHERE_ONLOOKUP); for (j = 0; j < wh.length; j++) { wh[j] = joinLookupField + ' IN ["' + wh[j].join('","') + '"]'; diff --git a/es5/lists/getVersions.js b/es5/lists/getVersions.js index 02dfa23..f64a027 100644 --- a/es5/lists/getVersions.js +++ b/es5/lists/getVersions.js @@ -1,29 +1,18 @@ import _regeneratorRuntime from "@babel/runtime-corejs3/regenerator"; -import _mapInstanceProperty from "@babel/runtime-corejs3/core-js-stable/instance/map"; -import _Array$isArray from "@babel/runtime-corejs3/core-js-stable/array/is-array"; import _asyncToGenerator from "@babel/runtime-corejs3/helpers/esm/asyncToGenerator"; -import info from './info.js'; import ajax from '../utils/ajax.js'; /** @name $SP().list.getVersions @function - @description When versionning is activated on a list, you can use this function to get the different version label/number for a list item + @description When versionning is activated on a list, you can use this function to get the different versions of a list item @param {Number} ID The item ID - @return {Promise} resolve(arrayOflistOfVersions) + @return {Promise} resolve(arrayOfVersions) @example - $SP().list("My List").getVersions({ - ID:1 - }).then(function(versions) { + $SP().list("My List").getVersions(1234).then(function(versions) { versions.forEach(function(version) { console.log(version); - // returns: - // - CheckInComment - // - Created - // - VersionID - // - IsCurrentVersion (boolean) - // - VersionLabel (e.g. "1.0", "2.0", …) }) }); */ @@ -36,13 +25,12 @@ function _getVersions() { _getVersions = _asyncToGenerator( /*#__PURE__*/ _regeneratorRuntime.mark(function _callee(itemID) { - var infos, rootFolder; - return _regeneratorRuntime.wrap(function _callee$(_context2) { + return _regeneratorRuntime.wrap(function _callee$(_context) { while (1) { - switch (_context2.prev = _context2.next) { + switch (_context.prev = _context.next) { case 0: if (this.listID) { - _context2.next = 2; + _context.next = 2; break; } @@ -50,7 +38,7 @@ function _getVersions() { case 2: if (this.url) { - _context2.next = 4; + _context.next = 4; break; } @@ -58,48 +46,22 @@ function _getVersions() { case 4: if (itemID) { - _context2.next = 6; + _context.next = 6; break; } throw "[SharepointPlus 'getVersions'] the item ID is required."; case 6: - _context2.next = 8; - return info.call(this); - - case 8: - infos = _context2.sent; - rootFolder = infos._List.RootFolder; // if no versionning - - if (!(infos._List.EnableVersioning !== "True")) { - _context2.next = 12; - break; - } - - return _context2.abrupt("return", []); - - case 12: - return _context2.abrupt("return", ajax.call(this, { - url: this.url + "/_api/web/GetFileByServerRelativeUrl('" + encodeURIComponent(rootFolder + "/" + itemID + "_.000") + "')/Versions" + return _context.abrupt("return", ajax.call(this, { + url: this.url + "/_api/lists/getbytitle('" + this.listID + "')/Items(" + itemID + ")/Versions" }).then(function (res) { - var _context; - - if (!res || !res.d || !_Array$isArray(res.d.results)) return []; - return _mapInstanceProperty(_context = res.d.results).call(_context, function (item) { - return { - CheckInComment: item.CheckInComment, - Created: item.Created, - VersionID: item.ID, - IsCurrentVersion: item.IsCurrentVersion, - VersionLabel: item.VersionLabel - }; - }); + return (res.d ? res.d.results : res.value) || []; })); - case 13: + case 7: case "end": - return _context2.stop(); + return _context.stop(); } } }, _callee, this); diff --git a/es5/lists/hasPermission.js b/es5/lists/hasPermission.js index 6192453..73b6d84 100644 --- a/es5/lists/hasPermission.js +++ b/es5/lists/hasPermission.js @@ -8,7 +8,7 @@ import ajax from '../utils/ajax.js'; @function @description This function permits to check if the current user has a specific permission for a list/library @param {String|Array} perm Can be one of the values listed on https://docs.microsoft.com/en-us/previous-versions/office/developer/sharepoint-2010/ee556747(v%3Doffice.14) - return {Promise} A promise with a boolean (TRUE/FALSE) if the requested perm was a string, or an object ({perm1:BOOLEAN, perm2:BOOLEAN}) if it was an array + @return {Promise} A promise with a boolean (TRUE/FALSE) if the requested perm was a string, or an object ({perm1:BOOLEAN, perm2:BOOLEAN}) if it was an array @example // check permissions for list 'Travels' diff --git a/es5/lists/removeAttachment.js b/es5/lists/removeAttachment.js index 7a4be06..ea36f07 100644 --- a/es5/lists/removeAttachment.js +++ b/es5/lists/removeAttachment.js @@ -17,7 +17,7 @@ import restoreVersion from './restoreVersion.js'; @example $SP().list("My List").removeAttachment({ ID:1, - filename:"https://mysite.share.point.com/Toolbox/Lists/Tasks/Attachments/2305/image1.png" + fileURL:"https://mysite.share.point.com/Toolbox/Lists/Tasks/Attachments/2305/image1.png" }) */ diff --git a/es5/lists/restoreVersion.js b/es5/lists/restoreVersion.js index 2f46d41..d6a18ee 100644 --- a/es5/lists/restoreVersion.js +++ b/es5/lists/restoreVersion.js @@ -13,7 +13,7 @@ import getRequestDigest from '../utils/getRequestDigest.js'; @param {Object} [setup] Options (see below) @param {Number} setup.ID The item ID @param {Number} setup.VersionID The version ID from $SP().list().getVersions() - @return {Promise} resolve(htmlPage), reject(errorMessage) + @return {Promise} resolve(true), reject(errorMessage) @example $SP().list("My List").restoreVersion({ diff --git a/es5/main.js b/es5/main.js index 5f12ac9..20b1280 100644 --- a/es5/main.js +++ b/es5/main.js @@ -27,6 +27,7 @@ global._SP_PLUGINS = {}; global._SP_MODALDIALOG_LOADED = false; global._SP_MAXWHERE_ONLOOKUP = 30; global._SP_ISBROWSER = new Function("try {return this===window;}catch(e){ return false;}")(); +global._SP_ISSPO = {}; global._SP_JSON_ACCEPT = "verbose"; // other options are "minimalmetadata" and "nometadata" var SharepointPlus = @@ -60,7 +61,7 @@ function () { _createClass(SharepointPlus, [{ key: "getVersion", value: function getVersion() { - return "6.1.5"; + return "6.2.0"; } /** @name $SP().auth diff --git a/es5/modals/closeModalDialog.js b/es5/modals/closeModalDialog.js index 73eae3a..a96b82e 100644 --- a/es5/modals/closeModalDialog.js +++ b/es5/modals/closeModalDialog.js @@ -29,7 +29,7 @@ export default function closeModalDialog(dialogResult, returnValue) { var fct = function fct() { var md; - if (_typeof(dialogResult) === "object" && typeof dialogResult.type !== "undefined" && dialogResult.type === "modalDialog") { + if (_typeof(dialogResult) === "object" && dialogResult.type === "modalDialog") { md = { id: dialogResult.id, dialogResult: returnValue, diff --git a/es5/people/getManager.js b/es5/people/getManager.js index 6e49a51..0ba9521 100644 --- a/es5/people/getManager.js +++ b/es5/people/getManager.js @@ -1,6 +1,5 @@ import _regeneratorRuntime from "@babel/runtime-corejs3/regenerator"; import _Promise from "@babel/runtime-corejs3/core-js-stable/promise"; -import _startsWithInstanceProperty from "@babel/runtime-corejs3/core-js-stable/instance/starts-with"; import _typeof from "@babel/runtime-corejs3/helpers/esm/typeof"; import _asyncToGenerator from "@babel/runtime-corejs3/helpers/esm/asyncToGenerator"; import getURL from '../utils/getURL.js'; @@ -12,13 +11,14 @@ import people from './people.js'; @category people @description Return the manager for the provided user, as a People string - @param {String} [username] With or without the domain, and you can also use an email address, and if you leave it empty it's the current user by default + @param {String} [username] Username with the domain, and if you leave it empty it's the current user by default @param {Object} [setup] Options (see below) @param {String} [setup.url='current website'] The website url + @param {Function} [setup.modify] Permits to modify the manager's username returned by the service @return {Function} resolve(manager), reject(error) @example - $SP().getManager("john_doe",{url:"http://my.si.te/subdir/"}) + $SP().getManager("domain\\john_doe",{url:"http://my.si.te/subdir/"}) .then(function(manager) { console.log(manager); // 42;#Smith,, Jane,#i:0#.w|domain\Jane_Smith,#Jane_Smith@Domain.com,#Jane_Smith@Domain.com,#Smith,, Jane manager = $SP().getPeopleLookup(manager); @@ -27,6 +27,12 @@ import people from './people.js'; .catch(function(err) { console.log("Err => ",err) }); + + $SP().getManager("domain\\john_doe",{ + modify:function(managerUserName) { + return (managerUserName.startsWith('i:0') ? managerUserName : "i:0#.w|" + managerUserName); + } + }) */ export default function getManager(_x, _x2) { @@ -69,13 +75,16 @@ function _getManager() { setup.url = _context.sent; case 8: - _context.next = 10; + setup.modify = setup.modify || function (val) { + return val; + }; + + _context.next = 11; return people.call(this, username, setup); - case 10: + case 11: pres = _context.sent; - managerUserName = pres.Manager; - if (!_startsWithInstanceProperty(managerUserName).call(managerUserName, 'i:0')) managerUserName = "i:0#.w|" + managerUserName; + managerUserName = setup.modify(pres.Manager); _context.next = 15; return getUserInfo.call(this, managerUserName, setup); diff --git a/es5/people/isMember.js b/es5/people/isMember.js index 7eaf071..e480c3e 100644 --- a/es5/people/isMember.js +++ b/es5/people/isMember.js @@ -77,7 +77,8 @@ function _isMember() { _context.next = 15; return usergroups.call(this, setup.user, { - cache: setup.cache + cache: setup.cache, + url: setup.url }); case 15: @@ -104,7 +105,8 @@ function _isMember() { case 22: _context.next = 24; return groupMembers.call(this, setup.group, { - cache: setup.cache + cache: setup.cache, + url: setup.url }); case 24: @@ -117,7 +119,8 @@ function _isMember() { _context.next = 28; return distributionLists.call(this, setup.user, { - cache: setup.cache + cache: setup.cache, + url: setup.url }); case 28: diff --git a/es5/utils/ajax.js b/es5/utils/ajax.js index 4941d11..7ac2aec 100644 --- a/es5/utils/ajax.js +++ b/es5/utils/ajax.js @@ -143,7 +143,7 @@ function _ajax() { return _context10.abrupt("return", _Promise.resolve(body)); case 24: - if (!(code == 403 && _includesInstanceProperty(responseText).call(responseText, "security validation for this page is invalid"))) { + if (!(code == 401 && settings.headers["X-RequestDigest"] && new Date(settings.headers["X-RequestDigest"].split(",")[1]) < new Date() || (code == 403 || code == 500) && (_includesInstanceProperty(responseText).call(responseText, "security validation for this page is invalid") || _includesInstanceProperty(responseText).call(responseText, "The security validation for this page has timed out")))) { _context10.next = 33; break; } @@ -215,14 +215,18 @@ function _ajax() { if (settings.method.toUpperCase() === "POST" && typeof settings.body !== "undefined") settings.headers['Content-Length'] = Buffer.byteLength(settings.body); // add User Agent settings.headers['User-Agent'] = 'SharepointPlus'; //'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:52.0) Gecko/20100101 Firefox/52.0'; + // do some changes in the options: https://github.com/s-KaiNet/sp-request/blob/master/UpgradeTo3x.md opts = { json: false, + responseType: 'text', method: settings.method, strictSSL: false, + rejectUnauthorized: false, headers: settings.headers, jar: true, - resolveWithFullResponse: true + resolveWithFullResponse: true, + resolveBodyOnly: false }; if (settings.body) opts.body = settings.body; if (this.proxyweb) opts.proxy = this.proxyweb; // looks like the Content-Length creates some issues @@ -230,7 +234,10 @@ function _ajax() { if (opts.headers) delete opts.headers["Content-Length"]; // check if we have some other parameters for (stg in settings) { - if (Object.prototype.hasOwnProperty.call(settings, stg) && !opts[stg]) opts[stg] = settings[stg]; + if (Object.prototype.hasOwnProperty.call(settings, stg) && !opts[stg]) { + opts[stg] = settings[stg]; + if (stg === 'encoding' && settings[stg] === null) opts['responseType'] = 'buffer'; + } } _context10.next = 63; diff --git a/es5/utils/hasREST.js b/es5/utils/hasREST.js index 538387e..20979ef 100644 --- a/es5/utils/hasREST.js +++ b/es5/utils/hasREST.js @@ -1,17 +1,15 @@ -import _parseInt from "@babel/runtime-corejs3/core-js-stable/parse-int"; import _Promise from "@babel/runtime-corejs3/core-js-stable/promise"; import _sliceInstanceProperty from "@babel/runtime-corejs3/core-js-stable/instance/slice"; -import ajax from './ajax.js'; + /** @name $SP().hasREST @function @category utils - @description Verify if the website supports REST API (Sharepoint 2013 and later) + @description In the earlier version of SharePointPlus, this function was used to check if the REST API was available – these days I assume everyone is now using at least SharePoint 2013, so this function always returns TRUE – if you don't have REST API you can still define _SP_CACHE_HASREST["url to check"]=false @param {Object} settings - @param {String} [settings.url=current] To check another URL (or if you need on a Node server) + @param {String} [settings.url=current] To check another URL @return {Promise} A resolved Promise that gives TRUE or FALSE */ - export default function hasREST(settings) { var _context; @@ -24,32 +22,5 @@ export default function hasREST(settings) { return _Promise.resolve(global._SP_CACHE_HASREST[url]); } - var hasREST, - needAjax = settings.url || !global._SP_ISBROWSER || typeof SP === "undefined" ? true : false; - - if (!needAjax) { - if (typeof SP !== "undefined" && SP.ClientSchemaVersions) { - // eslint-disable-line - // cache - hasREST = _parseInt(SP.ClientSchemaVersions.currentVersion) > 14; // eslint-disable-line - - global._SP_CACHE_HASREST[url] = hasREST; - return _Promise.resolve(hasREST); - } else needAjax = true; - } - - if (needAjax) { - return ajax.call(this, { - url: url + "/_api/web/Url" - }).then(function () { - global._SP_CACHE_HASREST[url] = true; - return _Promise.resolve(true); - }).catch(function () { - global._SP_CACHE_HASREST[url] = false; - return _Promise.resolve(false); - }); - } else { - global._SP_CACHE_HASREST[url] = false; - return _Promise.resolve(false); - } + return _Promise.resolve(true); } \ No newline at end of file diff --git a/es5/utils/isSPO.js b/es5/utils/isSPO.js new file mode 100644 index 0000000..bd9491d --- /dev/null +++ b/es5/utils/isSPO.js @@ -0,0 +1,30 @@ +import _typeof from "@babel/runtime-corejs3/helpers/esm/typeof"; +import _endsWithInstanceProperty from "@babel/runtime-corejs3/core-js-stable/instance/ends-with"; +import _sliceInstanceProperty from "@babel/runtime-corejs3/core-js-stable/instance/slice"; + +/** + * @name isSPO + * @category utils + * @function + * @description Return TRUE if the SharePoint is SharePoint Online + * @param {Object} settings + * @param {String} [settings.url=current] To check another URL + * @return {Boolean} Return TRUE if it's SPO or FALSE if not, or NULL is not able to determine it + * + * @note You can force an URL to be seen as SPO by defining the global variable _SP_ISSPO['url to check']=true + */ +export default function isSPO(settings) { + var _context; + + settings = settings || {}; + + var url = _sliceInstanceProperty(_context = (settings.url || this.url || window.location.href).split("/")).call(_context, 0, 3).join("/"); // check global variable + + + if (typeof global._SP_ISSPO[url] === "boolean") return global._SP_ISSPO[url]; // check .sharepoint.com in URL + + if (_endsWithInstanceProperty(url).call(url, '.sharepoint.com')) return true; // check _spPageContextInfo + + if (_typeof(global._spPageContextInfo) === "object") return global._spPageContextInfo.isSPO || false; + return null; +} \ No newline at end of file diff --git a/es5/utils/toDate.js b/es5/utils/toDate.js index b57be0c..3d5ff87 100644 --- a/es5/utils/toDate.js +++ b/es5/utils/toDate.js @@ -16,9 +16,9 @@ export default function toDate(strDate, forceUTC) { if (typeof strDate !== "string" && !isNaN(new Date(strDate))) return strDate; // check if it's a date, more robust than "d instanceof Date" - if (_sliceInstanceProperty(strDate).call(strDate, 0, 10) === "datetime;#") strDate = _sliceInstanceProperty(strDate).call(strDate, 10); // if it's a short date like 2020-01-19 then we use new Date() + if (_sliceInstanceProperty(strDate).call(strDate, 0, 10) === "datetime;#") strDate = _sliceInstanceProperty(strDate).call(strDate, 10); // if it's a short date like 2020-01-19 then we use new Date() with 'T00:00:00' to avoid timezone issue - if (strDate.length === 10 && /\d{4}-\d{2}-\d{2}/.test(strDate)) return new Date(strDate); + if (strDate.length === 10 && /\d{4}-\d{2}-\d{2}/.test(strDate)) return new Date(strDate + 'T00:00:00'); if (strDate.length != 19 && strDate.length != 20) throw "[SharepointPlus toDate] '" + strDate + "' is invalid."; var year = strDate.substring(0, 4); var month = strDate.substring(5, 7); diff --git a/jsdoc_template/tmpl/mainpage.tmpl b/jsdoc_template/tmpl/mainpage.tmpl index d04e0b1..ace9edd 100644 --- a/jsdoc_template/tmpl/mainpage.tmpl +++ b/jsdoc_template/tmpl/mainpage.tmpl @@ -17,12 +17,13 @@ var self = this;

    SharepointPlus ($SP) is a JavaScript API for Sharepoint. This library offers some extended features for SharePoint entirely on client side (requires no server install). $SP will simplify your interactions with Sharepoint.

    -

    Sharepoint Support

    +

    SharePoint Support

      -
    • Sharepoint 2007 : SharepointPlus v3.0.5 is the last release tested with Sharepoint 2007 – after this version I cannot assure the retro-compatibility
    • -
    • Sharepoint 2010 : Compatible since SharepointPlus v3.14, but, please read carefully this noticeSP2010 is not tested anymore
    • -
    • Sharepoint 2013/2016/2019/Online : Compatible since SharepointPlus v3.13 – ✓ fully tested, 100% compatible!
    • +
    • SharePoint 2007 : Compatible until SharepointPlus v3.0.5SP2007 is not tested anymore
    • +
    • SharePoint 2010 : Compatible until SharepointPlus v5.2SP2010 is not tested anymore
    • +
    • SharePoint 2013 : Compatible until SharepointPlus v6.1.5SP2013 is not tested anymore
    • +
    • SharePoint Online : ✓ current supported version
    @@ -125,7 +126,7 @@ const $SP = require("sharepointplus/dist");

    In that case you won't benefit from the tree-shaking optimization.

    -

    Browser only

    +

    Browser only

    You can also just drop one file in your HTML document which will contain all the 70+ functions and all the polyfills to support the different browsers:

    @@ -133,12 +134,12 @@ const $SP = require("sharepointplus/dist");
     
    Note: because this file contains all the functions as well as all the polyfills, it might be quite big (~55KB gzipped).
    -

    Browser Support

    +

    Browser Support

    - IE10+, and all modern browsers (Firefox, Chrome, Edge, ...) (see coverage) are supported with the bundle for browsers. + IE11+, and all modern browsers (Firefox, Chrome, Edge, ...) (see coverage) are supported with the bundle for browsers.

    -
    +

    How does SharepointPlus work?

    -

    SharepointPlus uses the different Sharepoint Web Services to deal with the Sharepoint server.

    -

    Below is a comparative between Web Services (used by SharepointPlus) and REST API (used by some other libraries):

    +

    SharepointPlus mainly uses the different Web Services to deal with the SharePoint server.

    +

    But the library also uses several REST API, e.g. when the Web Service is not available anymore, or if the REST API provides more functionalities.

    +

    Examples

    diff --git a/package.json b/package.json index 496f83c..5e3752e 100644 --- a/package.json +++ b/package.json @@ -1,13 +1,13 @@ { "name": "sharepointplus", - "version": "6.1.5", + "version": "6.2.0", "description": "A library that offers an easy interface to deal with Sharepoint", "module": "./es5/index.js", "main": "./es5/index.js", "scripts": { - "start": "NODE_ENV=cjs nodemon --exec npx babel-node test.js", - "build:es5": "(if exist es5 rmdir es5 /s /q) && NODE_ENV=es babel src --out-dir es5", - "build:dist": "(if exist dist rmdir dist /s /q) && NODE_ENV=dist babel src --out-dir dist", + "start": "cross-env NODE_ENV=cjs nodemon --exec npx babel-node test.js", + "build:es5": "(if exist es5 rmdir es5 /s /q) && cross-env NODE_ENV=es babel src --out-dir es5", + "build:dist": "(if exist dist rmdir dist /s /q) && cross-env NODE_ENV=dist babel src --out-dir dist", "build:browser": "npx webpack", "build": "npm run lint && npm run build:es5 && npm run build:dist && npm run build:browser && npm run tests && npm run doc", "lint": "eslint ./src/", @@ -24,6 +24,7 @@ }, "dependencies": { "@babel/runtime-corejs3": "^7.7.7", + "cross-env": "^7.0.3", "sp-request": "^2.1.3" }, "devDependencies": { @@ -36,6 +37,8 @@ "babel-eslint": "^10.0.3", "babel-loader": "^8.0.6", "babel-plugin-add-module-exports": "^1.0.2", + "browserslist": "^4.20.4", + "caniuse-lite": "^1.0.30001352", "cheerio": "^1.0.0-rc.5", "command-line-args": "^5.1.1", "core-js": "^3.6.1", diff --git a/src/index.js b/src/index.js index 65d682e..96e9f40 100644 --- a/src/index.js +++ b/src/index.js @@ -57,6 +57,7 @@ import getServerTime from './utils/getServerTime.js' import getTimeZoneInfo from './utils/getTimeZoneInfo.js' import getURL from './utils/getURL.js' import hasREST from './utils/hasREST.js' +import isSPO from './utils/isSPO.js' import newGuid from './utils/newGuid.js' import regionalDateFormat from './utils/regionalDateFormat.js' import regionalSettings from './utils/regionalSettings.js' @@ -134,6 +135,7 @@ export default spInit({ getTimeZoneInfo:getTimeZoneInfo, getURL:getURL, hasREST:hasREST, + isSPO:isSPO, newGuid:newGuid, regionalDateFormat:regionalDateFormat, regionalSettings:regionalSettings, diff --git a/src/lists/addAttachment.js b/src/lists/addAttachment.js index d28df94..ecc2913 100644 --- a/src/lists/addAttachment.js +++ b/src/lists/addAttachment.js @@ -25,7 +25,7 @@ import restoreVersion from './restoreVersion.js' }); // to read a file and send it - // with something like: + // with something like: <input type="file" onchange="addAttachment(event)"> function addAttachment(event) { let files = event.target.files; let fileReader = new FileReader(); diff --git a/src/lists/cleanResult.js b/src/lists/cleanResult.js index dc8452a..25107fd 100644 --- a/src/lists/cleanResult.js +++ b/src/lists/cleanResult.js @@ -14,9 +14,10 @@ $SP().cleanResult("string;#"); // -> "" $SP().cleanResult(";#Paul;#Jacques;#Aymeric;#"); // -> "Paul;Jacques;Aymeric" $SP().cleanResult(";#Paul;#Jacques;#Aymeric;#", ", "); // -> "Paul, Jacques, Aymeric" + $SP().cleanResult("2022-01-19 00:00:00"); // -> "2022-01-19" */ export default function cleanResult(str,separator) { if (str===null || typeof str==="undefined") return ""; separator = separator || ";"; - return (typeof str==="string"?str.replace(/^(string;|float;|datetime;)#?/,"").replace(/;#-?[0-9]+;#/g,separator).replace(/^-?[0-9]+;#/,"").replace(/^;#|;#$/g,"").replace(/;#/g,separator):str); + return (typeof str==="string"?str.replace(/^(string;|float;|datetime;)#?/,"").replace(/^(\d{4}-\d{2}-\d{2}) 00:00:00$/, "$1").replace(/;#-?[0-9]+;#/g,separator).replace(/^-?[0-9]+;#/,"").replace(/^;#|;#$/g,"").replace(/;#/g,separator):str); } diff --git a/src/lists/get.js b/src/lists/get.js index 1b64542..82fdbdd 100644 --- a/src/lists/get.js +++ b/src/lists/get.js @@ -33,7 +33,7 @@ class extendMyObject { @param {String} on The ON clause @return {Array} array of {ListName1:FieldName1, ListName2:FieldName2} @example - $SP()._parseOn("'List1'.field1 = 'List2'.field2 AND 'List1'.Other_x0020_Field = 'List2'.Some_x0020_Field") + _parseOn("'List1'.field1 = 'List2'.field2 AND 'List1'.Other_x0020_Field = 'List2'.Some_x0020_Field") */ function _parseOn(q) { var factory = [], i=0, mtch, queryString = q.replace(/(\s+)?(=)(\s+)?/g,"$2").replace(/==/g,"=").split(" AND "); @@ -856,6 +856,7 @@ export default async function get (options) { joinLookupField=setup.join.onLookup; setup.join.on="'"+(setup.join.alias||setup.join.list)+"'."+setup.join.onLookup+" = '"+setup.alias+"'.ID"; } + on=_parseOn(setup.join.on); joinData["noindex"]=on; // keep a copy of it for the next treatment in the tied list for (i=0,stop=aReturn.length; i0) { - // SP2013 limits to 60 items per IN - wh=arrayChunk(joinWhereLookup, 60); + wh=arrayChunk(joinWhereLookup, global._SP_MAXWHERE_ONLOOKUP); for (j=0; j { - if (!res || !res.d || !Array.isArray(res.d.results)) return []; - return res.d.results.map(item => { - return { - CheckInComment:item.CheckInComment, - Created:item.Created, - VersionID:item.ID, - IsCurrentVersion:item.IsCurrentVersion, - VersionLabel:item.VersionLabel - } - }) + return ((res.d ? res.d.results : res.value)||[]) }) } diff --git a/src/lists/hasPermission.js b/src/lists/hasPermission.js index 55db42e..a7d290c 100644 --- a/src/lists/hasPermission.js +++ b/src/lists/hasPermission.js @@ -5,7 +5,7 @@ import ajax from '../utils/ajax.js' @function @description This function permits to check if the current user has a specific permission for a list/library @param {String|Array} perm Can be one of the values listed on https://docs.microsoft.com/en-us/previous-versions/office/developer/sharepoint-2010/ee556747(v%3Doffice.14) - return {Promise} A promise with a boolean (TRUE/FALSE) if the requested perm was a string, or an object ({perm1:BOOLEAN, perm2:BOOLEAN}) if it was an array + @return {Promise} A promise with a boolean (TRUE/FALSE) if the requested perm was a string, or an object ({perm1:BOOLEAN, perm2:BOOLEAN}) if it was an array @example // check permissions for list 'Travels' diff --git a/src/lists/removeAttachment.js b/src/lists/removeAttachment.js index dbb03c3..ff609bc 100644 --- a/src/lists/removeAttachment.js +++ b/src/lists/removeAttachment.js @@ -16,7 +16,7 @@ import restoreVersion from './restoreVersion.js' @example $SP().list("My List").removeAttachment({ ID:1, - filename:"https://mysite.share.point.com/Toolbox/Lists/Tasks/Attachments/2305/image1.png" + fileURL:"https://mysite.share.point.com/Toolbox/Lists/Tasks/Attachments/2305/image1.png" }) */ export default function removeAttachment(setup) { diff --git a/src/lists/restoreVersion.js b/src/lists/restoreVersion.js index dfa9276..bb6043c 100644 --- a/src/lists/restoreVersion.js +++ b/src/lists/restoreVersion.js @@ -10,7 +10,7 @@ import getRequestDigest from '../utils/getRequestDigest.js' @param {Object} [setup] Options (see below) @param {Number} setup.ID The item ID @param {Number} setup.VersionID The version ID from $SP().list().getVersions() - @return {Promise} resolve(htmlPage), reject(errorMessage) + @return {Promise} resolve(true), reject(errorMessage) @example $SP().list("My List").restoreVersion({ diff --git a/src/main.js b/src/main.js index 6d6215d..08d5e60 100644 --- a/src/main.js +++ b/src/main.js @@ -22,6 +22,7 @@ global._SP_PLUGINS={}; global._SP_MODALDIALOG_LOADED=false; global._SP_MAXWHERE_ONLOOKUP=30; global._SP_ISBROWSER=(new Function("try {return this===window;}catch(e){ return false;}"))(); +global._SP_ISSPO={}; global._SP_JSON_ACCEPT="verbose"; // other options are "minimalmetadata" and "nometadata" export default class SharepointPlus { @@ -45,7 +46,7 @@ export default class SharepointPlus { @return {String} The current SharepointPlus version */ - getVersion () { return "6.1.5" } + getVersion () { return "6.2.0" } /** @name $SP().auth diff --git a/src/modals/closeModalDialog.js b/src/modals/closeModalDialog.js index 5f43450..7b83110 100644 --- a/src/modals/closeModalDialog.js +++ b/src/modals/closeModalDialog.js @@ -26,7 +26,7 @@ export default function closeModalDialog(dialogResult, returnValue) { var fct = function() { var md; - if (typeof dialogResult === "object" && typeof dialogResult.type !== "undefined" && dialogResult.type === "modalDialog") { + if (typeof dialogResult === "object" && dialogResult.type === "modalDialog") { md = {id:dialogResult.id, dialogResult:returnValue, returnValue:undefined, type:"closeModalDialog"}; dialogResult.modal.close(md); // if it's a wait screen, then we need to remove the +

    +
    + + + `); + /*await _sp.ajax({ + url:optionsCLI.url+"/_api/web/getfilebyserverrelativeurl('/"+optionsCLI.url.split('/').slice(3).join("/")+"/SitePages/Tests.aspx')/getlimitedwebpartmanager(1)/ImportWebPart", + method:"POST", + body:JSON.stringify({ webPartXml:` Content Editor @@ -332,7 +377,7 @@ var prompt = require('prompt'); Cannot import this Web Part. /_layouts/15/images/mscontl.gif - Microsoft.SharePoint, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c + Microsoft.SharePoint, Version=16.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c Microsoft.SharePoint.WebPartPages.ContentEditorWebPart ]]> - `.replace(//g,">"), - storage:"Shared" - } - }) + ` + }) + })*/ console.log("Test Environment Setup: Completed!"); } catch(err) { diff --git a/tests/sharepointplus-5.2.js b/tests/sharepointplus-5.2.js deleted file mode 100644 index 1577e36..0000000 --- a/tests/sharepointplus-5.2.js +++ /dev/null @@ -1,5732 +0,0 @@ -/*! - * SharepointPlus v5.2 - * Copyright 2018, Aymeric (@aymkdn) - * Contact: http://kodono.info - * Documentation: http://aymkdn.github.com/SharepointPlus/ - * License: LGPL-3 (http://aymkdn.github.com/SharepointPlus/license.md) - */ - -/** - * @name SPArrayChunk - * @category utils - * @function - * @description Permits to cut an array into smaller blocks - * @param {Array} b The array to split - * @param {Number} e The size of each block - * @return {Array} An array that contains several arrays with the required size - */ -var SPArrayChunk=function(b,e){var d=[];for(var c=0,a=b.length;cf;f+=1)if(null!==(r=arguments[f]))for(t in r)e!==r[t]&&"undefined"==typeof e[t]&&(u&&r[t]&&(y(r[t])||(n=Array.isArray(r[t])))?(n?(n=!1,o=e[t]&&Array.isArray(e[t])?e[t]:[]):o=e[t]&&y(e[t])?e[t]:{},e[t]=SPExtend(u,o,r[t])):void 0!==r[t]&&(e[t]=r[t]));return e} - -// Encode an ArrayBuffer as a base64 string -// source: https://gist.github.com/jonleighton/958841 -var SPArrayBufferToBase64=function(arrayBuffer) { - var base64 = '' - var encodings = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' - var bytes = new Uint8Array(arrayBuffer) - var byteLength = bytes.byteLength - var byteRemainder = byteLength % 3 - var mainLength = byteLength - byteRemainder - var a, b, c, d, chunk - - // Main loop deals with bytes in chunks of 3 - for (var i = 0; i < mainLength; i = i + 3) { - // Combine the three bytes into a single integer - chunk = (bytes[i] << 16) | (bytes[i + 1] << 8) | bytes[i + 2] - - // Use bitmasks to extract 6-bit segments from the triplet - a = (chunk & 16515072) >> 18 // 16515072 = (2^6 - 1) << 18 - b = (chunk & 258048) >> 12 // 258048 = (2^6 - 1) << 12 - c = (chunk & 4032) >> 6 // 4032 = (2^6 - 1) << 6 - d = chunk & 63 // 63 = 2^6 - 1 - - // Convert the raw binary segments to the appropriate ASCII encoding - base64 += encodings[a] + encodings[b] + encodings[c] + encodings[d] - } - - // Deal with the remaining bytes and padding - if (byteRemainder == 1) { - chunk = bytes[mainLength] - - a = (chunk & 252) >> 2 // 252 = (2^6 - 1) << 2 - - // Set the 4 least significant bits to zero - b = (chunk & 3) << 4 // 3 = 2^2 - 1 - - base64 += encodings[a] + encodings[b] + '==' - } else if (byteRemainder == 2) { - chunk = (bytes[mainLength] << 8) | bytes[mainLength + 1] - - a = (chunk & 64512) >> 10 // 64512 = (2^6 - 1) << 10 - b = (chunk & 1008) >> 4 // 1008 = (2^6 - 1) << 4 - - // Set the 2 least significant bits to zero - c = (chunk & 15) << 2 // 15 = 2^4 - 1 - - base64 += encodings[a] + encodings[b] + encodings[c] + '=' - } - - return base64 -} - -// Global -var _SP_CACHE_CONTENTTYPES=[]; -var _SP_CACHE_CONTENTTYPE=[]; -var _SP_CACHE_SAVEDVIEW=[]; -var _SP_CACHE_SAVEDVIEWS=[]; -var _SP_CACHE_SAVEDLISTS=[]; -var _SP_CACHE_USERGROUPS=[] -var _SP_CACHE_GROUPMEMBERS=[]; -var _SP_CACHE_DISTRIBUTIONLISTS=[]; -var _SP_CACHE_REGIONALSETTINGS=void 0; -var _SP_CACHE_DATEFORMAT=void 0; -var _SP_CACHE_HASREST={}; -var _SP_CACHE_REQUESTDIGEST={}; -var _SP_CACHE_TIMEZONEINFO={}; -var _SP_ADD_PROGRESSVAR={}; -var _SP_UPDATE_PROGRESSVAR={}; -var _SP_MODERATE_PROGRESSVAR={}; -var _SP_REMOVE_PROGRESSVAR={}; -var _SP_BASEURL=void 0; -var _SP_NOTIFY_READY=false; -var _SP_NOTIFY_QUEUE=[]; -var _SP_NOTIFY=[]; -var _SP_PLUGINS={}; -var _SP_MODALDIALOG_LOADED=false; -var _SP_MAXWHERE_ONLOOKUP=30; -var _SP_ISBROWSER=(new Function("try {return this===window;}catch(e){ return false;}"))(); -var _SP_JSON_ACCEPT="verbose"; // other options are "minimalmetadata" and "nometadata" - -(function(window, document, undefined) { - // define a faster way to apply a function to an array - var fastMap = function(source,fn) { - var iterations = source.length; - var dest = new Array(iterations); - var _n = iterations / 8; - var _caseTest = iterations % 8; - for (var i = iterations-1; i > -1; i--) { - var n = _n; - var caseTest = _caseTest; - do { - switch (caseTest) { - case 0: dest[i]=fn(source[i]); i--; // eslint-disable-line - case 7: dest[i]=fn(source[i]); i--; // eslint-disable-line - case 6: dest[i]=fn(source[i]); i--; // eslint-disable-line - case 5: dest[i]=fn(source[i]); i--; // eslint-disable-line - case 4: dest[i]=fn(source[i]); i--; // eslint-disable-line - case 3: dest[i]=fn(source[i]); i--; // eslint-disable-line - case 2: dest[i]=fn(source[i]); i--; // eslint-disable-line - case 1: dest[i]=fn(source[i]); i--; // eslint-disable-line - } - caseTest = 0; - } while (--n > 0); - } - return dest; - }; - - /** - @name $SP() - @class This is the object uses for all SharepointPlus related actions - */ - function SharepointPlus() { - if (!(this instanceof SharepointPlus)) return new SharepointPlus(); - } - - SharepointPlus.prototype = { - data:[], - length:0, - listQueue:[], - needQueue:false, - module_sprequest:null, - credentialOptions:null, - proxyweb:null, - /** - @name $SP().getVersion - @function - @category core - @description Returns the SP version - - @return {String} The current SharepointPlus version - */ - getVersion:function() { return "5.2" }, - /** - * @ignore - * @name $SP()._promise - * @function - * @description (internal use only) If will reduce the code after compression - * @param {Function} fct - * @return {Promise} - */ - _promise:function(fct) { return new Promise(fct) }, - /** - @name $SP().hasREST - @function - @category utils - @description Verify if the website supports REST API (Sharepoint 2013 and later) - @param {Object} settings - @param {String} [settings.url=current] To check another URL (or if you need on a Node server) - @return {Promise} A resolved Promise that gives TRUE or FALSE - */ - hasREST:function(settings) { - var _this=this; - return _this._promise(function(prom_resolve) { - settings = settings||{}; - var url=(settings.url || _this.url || window.location.href).split("/").slice(0,3).join("/"); - // check cache - if (typeof _SP_CACHE_HASREST[url] === "boolean") { - prom_resolve(_SP_CACHE_HASREST[url]); - return - } - var hasREST, needAjax=(settings.url || !_SP_ISBROWSER || typeof SP === "undefined" ? true : false); - if (!needAjax) { - if (typeof SP !== "undefined" && SP.ClientSchemaVersions) { // eslint-disable-line - // cache - hasREST=(parseInt(SP.ClientSchemaVersions.currentVersion)>14); // eslint-disable-line - _SP_CACHE_HASREST[url]=hasREST; - prom_resolve(hasREST); - return; - } - else needAjax=true; - } - if (needAjax) { - _this.ajax({url:url + "/_api/web/Url"}).then(function() { _SP_CACHE_HASREST[url]=true; prom_resolve(true) }, function() { _SP_CACHE_HASREST[url]=false; prom_resolve(false) }) - } else { - _SP_CACHE_HASREST[url]=false; - prom_resolve(false); - } - }) - }, - /** - * @name $SP().getRequestDigest - * @function - * @category utils - * @description Retrieve a Request Digest (and it will change the value of document.querySelector("#__REQUESTDIGEST") when a new Request Digest is created) - * @param {Object} settings - * @param {String} [settings.url=current] To check another URL (or if you use it on a Node server) - * @param {Boolean} [settings.cache=true] TRUE to use the cache and/or the one into the page for the digest, FALSE to get a new one - * @return {Promise} resolve(Request Digest), reject(reject from $SP().ajax()) - * - * @example - * $SP().getRequestDigest({cache:false}).then(function(digest) { console.log("The new digest is "+digest)}) - */ - getRequestDigest:function(settings) { - var _this=this; - return _this._promise(function(prom_resolve, prom_reject){ - settings=settings||{}; - settings.cache=(settings.cache===false?false:true); - var e, digest, url=(settings.url||_this.url); - if (!url) url=window.location.href.split("/").slice(0,3).join("/"); - url=url.toLowerCase(); - if (url.indexOf("_api") !== -1) url=url.split("_api")[0]; - else if (url.indexOf("_vti_bin/client.svc/processquery") !== -1) url=url.split("_vti_bin/client.svc/processquery")[0]; - // check cache - if (settings.cache) digest=_SP_CACHE_REQUESTDIGEST[url]; - if (digest) { - // check date to be less than 24h - if (new Date().getTime() - new Date(digest.split(",")[1]).getTime() < 86400000) { - prom_resolve(digest); - return; - } - } - if (_SP_ISBROWSER && document && settings.cache) { - e=document.querySelector("#__REQUESTDIGEST"); - if (e) { - digest=e.value; - // cache - _SP_CACHE_REQUESTDIGEST[url]=digest; - prom_resolve(digest); - return - } - } - // do a request - _this.ajax({ - url:url + "/_api/contextinfo", - method:"POST" - }).then(function(data) { - digest=data.d.GetContextWebInformation.FormDigestValue - // cache - _SP_CACHE_REQUESTDIGEST[url]=digest; - if (document) { - e=document.querySelector("#__REQUESTDIGEST"); - if (e) e.value=digest; - } - prom_resolve(digest); - }, function(rej) { - prom_reject(rej) - }) - }) - }, - /** - * @name $SP().ajax - * @function - * @category utils - * @description Permits to do an Ajax request based on https://github.com/yanatan16/nanoajax for Browsers, and https://github.com/s-KaiNet/sp-request for NodeKJ - * @param {Object} settings (See options below) - * @param {String} settings.url The url to call - * @param {String} [settings.method="GET"|"POST"] The HTTP Method ("GET" or "POST" if "body" is provided) - * @param {Object} [settings.headers] the headers - * @param {String} [settings.body] The data to send to the server - * @param {Function} [settings.onprogress=function(event){}] Show the download/upload progress (within browser only) - * @param {Function} [settings.getXHR=function(xhr){}] Pass the XMLHttpRequest object as a parameter (within browser only) - * @return {Promise} resolve(responseText||responseXML), reject({response, statusCode, responseText}) - * - * @example - * // for a regular request - * $SP().ajax({url:'https://my.web.site'}).then(function(data) { console.log(data) }) - * - * // if the URL contains /_api/ and if "Accept", "Content-Type" or "X-RequestDigest", then they are auto-defined - * - * // (in browser) manipulate xhr for specific needs, like reading a remote file (based on https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Sending_and_Receiving_Binary_Data) - * $SP().ajax({url:'https://url.com/file.png', getXHR:function(xhr){ xhr.responseType = 'arraybuffer' }}).then(function(data) { - * // ArrayBuffer result - * var blob = new Blob([data], {type: "image/png"}); - * fileReader.readAsArrayBuffer(blob); - * }) - * - * // (in browser) show progress on download, and cancel the download after 5 seconds - * var _xhr; - * $SP().ajax({ - * url:'https://server/files/video.mp4', - * getXHR:function(xhr) { - * _xhr = xhr; - * xhr.responseType = 'arraybuffer' - * }, - * onprogress:function(event) { - * console.log(event.loaded+" / "+event.total) - * } - * }); - * setTimeout(function() { _xhr.abort() }, 5000); // abort after 5 seconds - * - * // (in Node) to get the Buffer from a remote file we could use `encoding:null` from https://github.com/request/request - * sp.ajax({url:'https://my.web.site/lib/file.pdf', encoding:null}).then(data => { - * // 'data' is a Buffer - * }) - * - * // for a CORS/cross-domain request you may need to use 'false' for 'Content-Type' - * $SP().ajax({url:'https://my.cross-domain.web/site', headers:{"Content-Type":false}}).then(function(data) { console.log(data) }) - */ - ajax:function(settings) { - var _this=this; - settings.headers=settings.headers||{}; - // https://github.com/yanatan16/nanoajax — not the original version (include upload.onprogress and getXHR) - // eslint-disable-next-line - !function(e,t){function n(e){return e&&t.XDomainRequest&&!/MSIE 1/.test(navigator.userAgent)?new XDomainRequest:t.XMLHttpRequest?new XMLHttpRequest:void 0}function o(e,t,n){e[t]=e[t]||n}var r=["responseType","withCredentials","timeout"];e.ajax=function(e,a){function s(e,t){return function(){p||(a(void 0===c.status?e:c.status,0===c.status?"Error":c.response||c.responseText||t,c),p=!0)}}var u=e.headers||{},i=e.body,d=e.method||(i?"POST":"GET"),p=!1,c=n(e.cors);c.open(d,e.url,!0);var l=c.onload=s(200);c.onreadystatechange=function(){4===c.readyState&&l()},c.onerror=s(null,"Error"),c.ontimeout=s(null,"Timeout"),c.onabort=s(null,"Abort"),i&&(t.FormData&&i instanceof t.FormData||o(u,"Content-Type","application/x-www-form-urlencoded"));for(var f,v=0,g=r.length;g>v;v++)f=r[v],void 0!==e[f]&&(c[f]=e[f]);for(var f in u)u[f]!==!1&&c.setRequestHeader(f,u[f]);return e.onprogress&&(d.toUpperCase()==='GET'?c.addEventListener("progress",e.onprogress,!1):c.upload.addEventListener("progress",e.onprogress,!1)),e.getXHR&&e.getXHR(c),c.send(i),c},t.nanoajax=e}({},window); - - return _this._promise(function(prom_resolve, prom_reject) { - var addRequestDigest=false; - // add "Accept": "application/json;odata=verbose" for headers if there is "_api/" in URL, except for "_api/web/Url" - if (settings.url.toLowerCase().indexOf("/_api/") > -1 && settings.url.toLowerCase().indexOf("_api/web/url") === -1) { - if (typeof settings.headers["Accept"]==="undefined") settings.headers.Accept = "application/json;odata="+_SP_JSON_ACCEPT; - if (typeof settings.headers["Content-Type"]==="undefined") settings.headers["Content-Type"] = "application/json;odata="+_SP_JSON_ACCEPT; - if (typeof settings.headers["X-RequestDigest"]==="undefined" && settings.url.indexOf("contextinfo") === -1) { - addRequestDigest=true; - } - } - // if "_vti_bin/client.svc/ProcessQuery" we want to add the RequestDigest - if (settings.url.toLowerCase().indexOf("_vti_bin/client.svc/processquery") > -1 && typeof settings.headers["X-RequestDigest"]==="undefined") { - addRequestDigest=true - } - if (addRequestDigest) { - // we need to retrieve the Request Digest - _this.getRequestDigest({url:settings.url.toLowerCase().split("_api")[0]}) - .then(function(requestDigest) { - settings.headers["X-RequestDigest"]=requestDigest; - return _this.ajax(settings) - }) - .then(function(res) { prom_resolve(res) }) - .catch(function(rej) { prom_reject(rej) }); - return; - } - // use XML as the default content type - if (typeof settings.headers["Content-Type"]==="undefined") settings.headers["Content-Type"]="text/xml; charset=utf-8"; - - // check if it's NodeJS - if (_SP_ISBROWSER) { - // IE will return an "400 Bad Request" if it's a POST with no body - if (settings.method==="POST" && !settings.body) settings.body=""; - // eslint-disable-next-line - nanoajax.ajax(settings, function(code, responseText, request) { - if (code >= 200 && code < 300 && responseText !== "Error" && responseText !== "Abort" && responseText !== "Timeout") { - var body=(!request.responseType || request.responseType==='document'?request.responseXML || request.responseText : responseText); - if (request.getResponseHeader("Content-Type").indexOf("/json") > -1 && typeof body==="string") body=JSON.parse(body); // parse JSON - prom_resolve(body) - } else { - // check if it's an issue with validation code - if (code == 403 && responseText.indexOf("The security validation for this page is invalid and might be corrupted. Please use your web browser's Back button to try your operation again.") > -1) { - // then we retry - delete settings.headers["X-RequestDigest"]; - _this.getRequestDigest({cache:false}) - .then(function(requestDigest) { - settings.headers["X-RequestDigest"]=requestDigest; - return _this.ajax(settings) - }) - .then(function(res) { prom_resolve(res) }) - .catch(function(rej) { prom_reject(rej) }); - - } else { - prom_reject({statusCode:code, responseText:responseText, request:request}); - } - } - }) - } else { - // we use the module 'sp-request' from https://github.com/s-KaiNet/sp-request - if (_this.module_sprequest === null) { - if (_this.credentialOptions === null) { - throw "[SharepointPlus 'ajax'] please use `$SP().auth()` to provide your credentials first"; - } - _this.module_sprequest = require('sp-request').create(_this.credentialOptions); - } - - if (settings.headers['Content-Type'] && settings.headers['Content-Type'].indexOf('xml') > -1) settings.headers['Accept'] = 'application/xml, text/xml, */*; q=0.01'; - if (!settings.method) settings.method=(typeof settings.body !== "undefined"?"POST":"GET"); - if (settings.method.toUpperCase() === "POST" && typeof settings.body !== "undefined") settings.headers['Content-Length'] = Buffer.byteLength(settings.body); - // add User Agent - settings.headers['User-Agent'] = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:52.0) Gecko/20100101 Firefox/52.0'; - var opts = { - json:false, - method:settings.method, - strictSSL: false, - headers: settings.headers, - jar:true - }; - if (settings.body) opts.body=settings.body; - if (_this.proxyweb) opts.proxy=_this.proxyweb; - // looks like the Content-Length creates some issues - if (opts.headers) delete opts.headers["Content-Length"]; - // check if we have some other parameters - for (var stg in settings) { - if (settings.hasOwnProperty(stg) && !opts[stg]) opts[stg] = settings[stg]; - } - - _this.module_sprequest(settings.url, opts) - .then(function(response) { - if (response.statusCode === 200 && response.statusMessage !== "Error" && response.statusMessage !== "Abort" && response.statusMessage !== "Timeout") { - // check if it's XML, then parse it - if (response.headers['content-type'].indexOf('xml') > -1 && response.body.slice(0,5) === ' -1 && typeof response.body === "string") response.body=JSON.parse(response.body) - prom_resolve(response.body); - } - } else { - prom_reject({response:response, statusCode:response.statusCode, responseText:response.body}); - } - }) - .catch(function(err) { - prom_reject({error:err, statusCode:err.statusCode, response:err.response, responseText:(err.response?err.response.body:'')}); - }); - } - }) - }, - /** - @name $SP().auth - @function - @category node - @description Permits to use credentials when doing requests (for Node module only) - - @param {Object} credentialOptions Options from https://github.com/s-KaiNet/node-sp-auth - @return {Object} the current SharepointPlus object - - @example - var user1 = {username:'aymeric', password:'sharepointplus', domain:'kodono'}; - $SP().auth(user1) - .list("My List","http://my.sharpoi.nt/other.directory/") - .get({...}); - // or : - var sp = $SP().auth(user1); - sp.list("My List", "https://web.si.te").get({...}); - sp.list("Other List"; "http://my.sharpoi.nt/other.directory/").update(...); - */ - auth:function(credentialOptions) { - this.credentialOptions = credentialOptions; - return this; - }, - /** - @name $SP().proxy - @function - @category node - @description Permits to define a proxy server (for Node module only) - - @param {String} proxyURL Looks like "http://domain%5Cusername:password@proxy.something:80" - @return {Object} the current SharepointPlus object - - @example - var user1 = {username:'aymeric', password:'sharepointplus', domain:'kodono'}; - var proxy = "http://" + user1.domain + "%5C" + user1.username + ":" + user1.password + "@proxy:80"; - $SP().proxy(proxy).auth(user1) - .list("My List","http://my.sharpoi.nt/other.directory/") - .get({...}); - // or : - var sp = $SP().proxy(proxy).auth(user1); - sp.list("My List", "https://web.si.te").get({...}); - sp.list("Other List"; "http://my.sharpoi.nt/other.directory/").update(...); - */ - proxy:function(proxy) { - this.proxyweb = proxy; - return this; - }, - /** - @name $SP().list - @namespace - @description Permits to define the list ID/name - - @param {String} listID Ths list ID or the list name - @param {String} [url] If the list name is provided, then you need to make sure URL is provided too (then no need to define the URL again for the chained functions like 'get' or 'update') - @return {Object} the current SharepointPlus object - - @example - $SP().list("My List"); - $SP().list("My List","http://my.sharpoi.nt/other.directory/"); - */ - list:function(list,url) { - var _this=this.reset(); - if (url) { - // make sure we don't have a '/' at the end - _this.url=(url.slice(-1)==='/'?url.slice(0,-1):url); - } - else _this._getURL(); - _this.listID = list.replace(/&/g,"&"); - return _this; - }, - /** - @ignore - @name $SP()._getURL - @function - @param {Boolean} [setURL=true] If we don't want to set this.url but just return the URL found - - @description (internal use only) Store the current URL website into this.url - @return {Promise} resolve(url), reject(error) - */ - _getURL:function(setURL) { - var _this=this; - setURL=(setURL===false?false:true); - return _this._promise(function(prom_resolve, prom_reject) { - if (typeof _this.url === "undefined") { - // search for the local base URL - if (typeof _SP_BASEURL !== "undefined") { - if (setURL) _this.url=_SP_BASEURL; - if (_this.url==="" || _this.url==="/") _this.url=window.location.protocol+"//"+window.location.host+"/"; - prom_resolve(_SP_BASEURL) - } else { - // try to build it - if (typeof window.L_Menu_BaseUrl!=="undefined") { - if (setURL) _this.url=_SP_BASEURL=window.L_Menu_BaseUrl; // eslint-disable-line - if (_this.url==="" || _this.url==="/") _this.url=window.location.protocol+"//"+window.location.host+"/"; - prom_resolve(window.L_Menu_BaseUrl) // eslint-disable-line - } else { - // eslint-disable-next-line - if (typeof window._spPageContextInfo !== "undefined" && typeof window._spPageContextInfo.webServerRelativeUrl !== "undefined") { - if (setURL) _this.url=_SP_BASEURL=window._spPageContextInfo.webServerRelativeUrl; // eslint-disable-line - if (_this.url==="" || _this.url==="/") _this.url=window.location.protocol+"//"+window.location.host+"/"; - prom_resolve(window._spPageContextInfo.webServerRelativeUrl) // eslint-disable-line - } else { - // we'll use the Webs.asmx service to find the base URL - _this.needQueue=true; - _this.ajax({ - url: "/_vti_bin/Webs.asmx", - body: _this._buildBodyForSOAP("WebUrlFromPageUrl", ""+window.location.href.replace(/&/g,"&")+""), - }).then(function(data) { - var result=data.getElementsByTagName('WebUrlFromPageUrlResult'); - if (result.length) { - var u=result[0].firstChild.nodeValue.toLowerCase(); - if (setURL) _this.url = _SP_BASEURL = u; - prom_resolve(u); - } else { - prom_reject("[SharepointPlus '_getURL'] Unable to retrieve the URL") - } - _this.needQueue=false; - }, function(error) { prom_reject(error) }) - return; - } - } - } - } - prom_reject("[SharepointPlus '_getURL'] Unable to retrieve the URL"); - }) - }, - /** - @name $SP().getURL - @function - @category utils - @description Return the current base URL website - @return {Promise} resolve(The current base URL website), reject(error) - */ - getURL:function() { return this._getURL(false) }, - /** - @ignore - @name $SP()._buildBodyForSOAP - @function - @param {String} methodName - @param {String} bodyContent - @param {String} [xmlns="http://schemas.microsoft.com/sharepoint/soap/"] - @description (internal use only) Permits to create the body for a SOAP request - */ - _buildBodyForSOAP:function(methodName, bodyContent, xmlns) { - xmlns = xmlns || "http://schemas.microsoft.com/sharepoint/soap/"; - return '<'+methodName+' xmlns="'+xmlns+'">' + bodyContent + ''; - }, - /** - * @name $SP().webService - * @function - * @category core - * @description Permits to directly deal with a WebService (similar to SPServices http://sympmarc.github.io/SPServices/core/web-services.html) - * @param {Object} options - * @param {String} operation The method name to use (e.g. UpdateList, GetList, ....) - * @param {String} service The name of the service (Lists, Versions, PublishedLinksService, ...) it's the ".asmx" name without the extension - * @param {Object} [properties={}] The properties to call - * @param {String} [webURL=current website] The URL of the website - * @param {String|Boolean} [soapURL='http://schemas.microsoft.com/sharepoint/soap/'] If the SOAP url is not the default one, then you can customize it... it will be send in the request's headers as "SOAPAction" - * @param {Boolean} [soapAction=true] Some web services don't want the "SOAPAction" header - * @return {Promise} resolve(responseBody), reject(see $SP().ajax()) - * - * @example - * $SP().webService({ // http://sympmarc.github.io/SPServices/core/web-services/Lists/UpdateList.html - * service:"Lists", - * operation:"Updatelist", - * webURL:"http://what.ever/" - * properties:{ - * listName:"Test", - * listProperties:"...", - * newFields:"...", - * updateFields:"...", - * deleteFields:"...", - * listVersion:"..." - * } - * }).then(function(response) { - * // do something with the response - * }, function(error) { - * console.log("Error => ",error) - * }); - * - * // to remove a person from a group - * $SP().webService({ - * service:"UserGroup", - * operation:"RemoveUserFromGroup", - * soapURL:"http://schemas.microsoft.com/sharepoint/soap/directory/", - * properties:{ - * groupName:"Group", - * userLoginName:"domain\\user" - * } - * }).then(function(response) { - * console.log("OK => ",response) - * }, function(error) { console.log("Error => ",error) }); - */ - webService:function(options) { - var _this=this; - return _this._promise(function(prom_resolve, prom_reject) { - var bodyContent="", prop, params; - if (!options.service) throw "Error 'webService': the option 'service' is required"; - if (!options.operation) throw "Error 'webService': the option 'operation' is required"; - options.webURL = options.webURL || _this.url; - // if we didn't define the url in the parameters, then we need to find it - if (!options.webURL) { - _this._getURL().then(function(url) { - options.webURL=url; - _this.webService(options).then(function(res) { - prom_resolve(res); - }, function(rej) { - prom_reject(rej) - }) - }, function(rej) { - prom_reject(rej) - }) - return; - } - options.properties = options.properties || {}; - for (prop in options.properties) { - if (options.properties.hasOwnProperty(prop)) { - bodyContent += '<'+prop+'>'+options.properties[prop]+'' - } - } - options.soapAction=(options.soapAction===false?false:true); - options.soapURL=options.soapURL||'http://schemas.microsoft.com/sharepoint/soap/'; - if (!options.soapAction) options.soapURL=options.soapURL.replace(/\/$/,""); - bodyContent = _this._buildBodyForSOAP(options.operation, bodyContent, options.soapURL); - params={ - url: options.webURL+"/_vti_bin/"+options.service+".asmx", - body: bodyContent - }; - if (options.soapAction) params.headers={'SOAPAction':options.soapURL+options.operation }; - _this.ajax(params).then(function(data) { prom_resolve(data) }, function(rej) { prom_reject(rej) }); - }) - }, - /** - @ignore - @name $SP()._addInQueue - @function - @description (internal use only) Add a function in the queue - */ - _addInQueue:function() { - this.listQueue.push(arguments); - if (this.listQueue.length===1) this._testQueue(); - return this - }, - /** - @ignore - @name $SP()._testQueue - @function - @description (internal use only) Permits to treat the queue - */ - _testQueue:function() { - var _this=this; - if (_this.needQueue) { - setTimeout(function() { _this._testQueue.call(_this) }, 25); - } else { - if (_this.listQueue.length > 0) { - var todo = _this.listQueue.shift(); - _this[todo[0]].apply(_this, todo[1]); - } - _this.needQueue=(_this.listQueue.length>0); - if (_this.needQueue) { - setTimeout(function() { _this._testQueue.call(_this) }, 25); - } - } - }, - /** - @name $SP().parse - @function - @category lists - @description Use a WHERE sentence to transform it into a CAML Syntax sentence - - @param {String} where The WHERE sentence to change - @param {String} [escapeChar=true] Determines if we want to escape the special chars that will cause an error (for example '&' will be automatically converted to '&amp;') - @example - $SP().parse('ContentType = "My Content Type" OR Description <> null AND Fiscal_x0020_Week >= 43 AND Result_x0020_Date < "2012-02-03"'); - // -> return <And><And><Or><Eq><FieldRef Name="ContentType" /><Value Type="Text">My Content Type</Value></Eq><IsNotNull><FieldRef Name="Description" /></IsNotNull></Or><Geq><FieldRef Name="Fiscal_x0020_Week" /><Value Type="Number">43</Value></Geq></And><Lt><FieldRef Name="Result_x0020_Date" /><Value Type="DateTime">2012-02-03</Value></Lt></And> - - // available operators : - // "<" : less than - // "<=" : less than or equal to - // ">" : greater than - // ">=" : greater than or equal to - // "<>" : different - // "~=" : this must be only used when comparing to a number that represents the User ID (e.g. 'User ~= 328') - that permits to query a list with too many items but with the User column that is indexed - // " AND " - // " OR " - // " LIKE " : for example 'Title LIKE "foo"' will return "foobar" "foo" "barfoobar" "barfoo" and so on - // " IN " : for example 'Location IN ["Los Angeles","San Francisco","New York"]', equivalent to 'Location = "Los Angeles" OR Location = "San Francisco" OR Location = "New York"' — SP2013 limits each IN to 60 items. If you want to check Lookup IDs instead of text you can use '~' followed by the ID, for example 'Location IN ["~23", "~26", "~30"]' - - // special words: - // '[Me]' : for the current user - // '[Today]' : to use the today date - // '[Today+X]' : to use today + X days - // Null : for the Null value - // TRUE : for the Yes/No columns - // FALSE : for the Yes/No columns - - // in the below example, on the "&" will be escaped - var bar="Bob & Marley"; - var foo="O'Conney"; - $SP().parse('Bar = "'+bar+'" AND Foo = "'+foo+'"'); // -> <And><Eq><FieldRef Name="Bar" /><Value Type="Text">Bob & Marley</Value></Eq><Eq><FieldRef Name="Foo" /><Value Type="Text">O'Conney</Value></Eq></And> - $SP().parse("Bar = '"+bar+"' AND Foo = '"+foo+"'"); // don't put the simple and double quotes this way because it'll cause an issue with O'Conney - */ - parse:function(q, escapeChar) { - // schema: https://docs.microsoft.com/en-us/sharepoint/dev/schema/query-schema - var queryString = q.replace(/(\s+)?(=|~=|<=|>=|<>|<|>| LIKE | IN )(\s+)?/g,"$2").replace(/""|''/g,"Null").replace(/==/g,"="); // remove unnecessary white space & replace ' - // the Null doesn't work with IN, so we need to move it outside - if (/\w+ IN \[([^[]+,)?Null,?/.test(queryString)) { - // eslint-disable-next-line - queryString = queryString.replace(/(\w+) IN \[([^\[]+,)?Null(,[^\]]+)?\]/g,"($1 = Null OR $&)") // eslint-disable-next-line - .replace(/\[([^\[]+,)?Null(,[^\]]+)?\]/g,"[$1$2]") - .replace(/(\[),|(,),|,(\])/g,"$1$2$3"); - } - - var factory = []; - escapeChar = (escapeChar===false ? false : true) - var limitMax = queryString.length; - var closeOperator="", closeTag = "", ignoreNextChar=false; - var lastField = ""; - var parenthesis = {open:0}; - var lookupId = false; - for (var i=0; i < queryString.length; i++) { - var letter = queryString.charAt(i); - switch (letter) { - case "(": // find the deepest ( - var start = i; - var openedApos=false; - while (queryString.charAt(i) == "(" && i < limitMax) { i++; parenthesis.open++; } - // find the corresponding ) - while (parenthesis.open>0 && i < limitMax) { - i++; - // if there is a ' opened then ignore the ) until the next ' - var charAtI = queryString.charAt(i); - if (charAtI=="\\") ignoreNextChar=true; // when we have a backslash \then ignore the next char - else if (!ignoreNextChar && (charAtI=="'" || charAtI=='"')) openedApos=!openedApos; - else if (!ignoreNextChar && charAtI==")" && !openedApos) parenthesis.open--; - else ignoreNextChar=false; - } - - var lastIndex = factory.length-1; - - // concat with the first index - if (lastIndex>=0) { - if (closeOperator != "") factory[0] = "<"+closeOperator+">"+factory[0]; - factory[0] += this.parse(queryString.substring(start+1, i)); - if (closeOperator != "") factory[0] += ""; - closeOperator = ""; - } else factory[0] = this.parse(queryString.substring(start+1, i)); - break; - case "[": // for operator IN - var start = i; // eslint-disable-line - var openedApos=false; // eslint-disable-line - // find the corresponding ] - while (i < limitMax) { - i++; - // if there is a ' opened then ignore the ) until the next ' - var charAtI = queryString.charAt(i); // eslint-disable-line - if (charAtI=="\\") ignoreNextChar=true; // when we have a backslash \then ignore the next char - else if (!ignoreNextChar && (charAtI=="'" || charAtI=='"')) openedApos=!openedApos; - else if (!ignoreNextChar && !openedApos && charAtI=="]") break; - else ignoreNextChar=false; - } - - var lastIndex = factory.length-1; // eslint-disable-line - var arrIn = JSON.parse('[' + queryString.substring(start+1, i) + ']'); - // we want to detect the type for the values - var typeIn = "Text"; - switch(typeof arrIn[0]) { - case "number": typeIn = "Number"; break; - default: { - // check if it starts with ~ and then it's a number -- lookupid - if (arrIn[0].charAt(0) === "~" && typeof (arrIn[0].slice(1)*1) === "number") { - typeIn = "Integer"; - // change all array values - arrIn.forEach(function(e,i) { arrIn[i]=e.slice(1) }) - } - } - } - factory[lastIndex] += '' + arrIn.join('') + '' + closeTag; - lastField = ""; - closeTag = ""; - // concat with the first index - if (lastIndex>0) { - if (closeOperator != "") factory[0] = "<"+closeOperator+">"+factory[0]; - factory[0] += factory[lastIndex]; - if (closeOperator != "") factory[0] += ""; - delete(factory[lastIndex]); - closeOperator = ""; - } - break; - case ">": // look at the operand - case "<": - i++; - if (queryString.charAt(i) == "=") { // >= or <= - factory.push("<"+(letter==">"?"G":"L")+"eq>"); - closeTag = ""?"G":"L")+"eq>"; - } else if (letter == "<" && queryString.charAt(i) == ">") { // <> - factory.push(""); - closeTag = ""; - } else { - i--; - factory.push("<"+(letter==">"?"G":"L")+"t>"); - closeTag = ""?"G":"L")+"t>"; - } - break; - case "~": // special operator '~=' for People - if (queryString.charAt(i+1) == "=") lookupId=true - break; - case "=": - factory.push(""); - closeTag = ""; - break; - case " ": // check if it's AND or OR - if (queryString.substring(i,i+5).toUpperCase() == " AND ") { - // add the open tag in the array - closeOperator = "And"; - i+=4; - } - else if (queryString.substring(i,i+4).toUpperCase() == " OR ") { - // add the open tag in the array - closeOperator = "Or"; - i+=3; - } - else if (queryString.slice(i,i+6).toUpperCase() == " LIKE ") { - i+=5; - factory.push(""); - closeTag = ""; - } - else if (queryString.slice(i,i+4).toUpperCase() == " IN ") { - i+=3; - factory.push(""); - closeTag = ""; - } - else lastField += letter; - break; - case '"': // look now for the next " - case "'": - var apos = letter; - var word = "", other=""; - while ((letter = queryString.charAt(++i)) != apos && i < limitMax) { - if (letter == "\\") letter = queryString.charAt(++i); - word+=letter; - } - lastIndex = factory.length-1; - factory[lastIndex] += ''; - lastField = ""; - var type = "Text"; //(isNaN(word) ? "Text" : "Number"); // check the type - // check automatically if it's a DateTime - if (/\d{4}-\d\d?-\d\d?((T| )\d{2}:\d{2}:\d{2})?/.test(word)) { - type="DateTime"; - // check if we want to evaluate the TIME also - if (/\d{4}-\d\d?-\d\d?((T| )\d{2}:\d{2}:\d{2})/.test(word)) other=' IncludeTimeValue="TRUE"'; - } - if (escapeChar) word = this._cleanString(word); - // special words ([Today] and [Me]) - if (word === "[Me]") { - word = ''; - type = "Integer"; - } else if (word.slice(0,6) == "[Today") { - type="DateTime"; - // find the offset if defined - word = ''; - } - - factory[lastIndex] += ''+word+''; - factory[lastIndex] += closeTag; - closeTag = ""; - // concat with the first index - if (lastIndex>0) { - if (closeOperator != "") factory[0] = "<"+closeOperator+">"+factory[0]; - factory[0] += factory[lastIndex]; - if (closeOperator != "") factory[0] += ""; - delete(factory[lastIndex]); - closeOperator = ""; - } - break; - case "0": case "1": case "2": case "3": case "4": case "5": case "6": case "7": case "8": case "9": - if (closeTag != "") { // it's the value - var value = letter; - while (!isNaN(letter = queryString.charAt(++i)) && i < limitMax) value+=""+letter; - lastIndex = factory.length-1; - factory[lastIndex] += ''; - lastField = ""; - factory[lastIndex] += ''+value.replace(/ $/,"")+''; - factory[lastIndex] += closeTag; - closeTag = ""; - // concat with the first index - if (lastIndex>0) { - if (closeOperator != "") factory[0] = "<"+closeOperator+">"+factory[0]; - factory[0] += factory[lastIndex]; - if (closeOperator != "") factory[0] += ""; - delete(factory[lastIndex]); - closeOperator = ""; - } - i-=2; - break; - } - default: // eslint-disable-line - if (closeTag == "") lastField += letter; - else if (letter.toLowerCase() == "n" && queryString.substring(i,i+4).toLowerCase() == "null") { // if we have NULL as the value - lastIndex = factory.length-1; - if (closeTag == "") { // <> - factory[lastIndex] = ""; - closeTag = ""; - } else if (closeTag == "") { // = - factory[lastIndex] = ""; - closeTag = ""; - } - i+=3; - factory[lastIndex] += ''; - lastField = ""; - factory[lastIndex] += closeTag; - closeTag = ""; - // concat with the first index - if (lastIndex>0) { - if (closeOperator != "") factory[0] = "<"+closeOperator+">"+factory[0]; - factory[0] += factory[lastIndex]; - if (closeOperator != "") factory[0] += ""; - delete(factory[lastIndex]); - closeOperator = ""; - } - } - else if ((letter.toLowerCase() === "t" && queryString.substring(i,i+4).toLowerCase() === "true") || (letter.toLowerCase() === "f" && queryString.substring(i,i+5).toLowerCase() === "false")) { // when we have TRUE/FALSE as the value - lastIndex = factory.length-1; - i+=3; - if (letter.toLowerCase() === "f") i++; - factory[lastIndex] += ''+(letter.toLowerCase() === "t"?1:0)+''; - lastField = ""; - factory[lastIndex] += closeTag; - closeTag = ""; - // concat with the first index - if (lastIndex>0) { - if (closeOperator != "") factory[0] = "<"+closeOperator+">"+factory[0]; - factory[0] += factory[lastIndex]; - if (closeOperator != "") factory[0] += ""; - delete(factory[lastIndex]); - closeOperator = ""; - } - } - } - } - return factory.join(""); - }, - /** - @ignore - @name $SP()._parseOn - @function - @description (internal use only) Look at the ON clause to convert it - - @param {String} on The ON clause - @return {Array} array of clauses - @example - $SP()._parseOn("'List1'.field1 = 'List2'.field2 AND 'List1'.Other_x0020_Field = 'List2'.Some_x0020_Field") - */ - _parseOn:function(q) { - var factory = [], i=0, mtch, queryString = q.replace(/(\s+)?(=)(\s+)?/g,"$2").replace(/==/g,"=").split(" AND "); - for (; i<rule><firstDayOfWeek>mo</firstDayOfWeek><repeat><monthlyByDay weekday="TRUE" weekdayOfMonth="last" monthFrequency="1" /></repeat><windowEnd>2019-01-19T16:00:00Z</windowEnd></rule></recurrence>'); - // it will return the below object - { - "type":"monthlyByDay", - "firstDayOfWeek":"monday", - "on":{ - "weekday":"last" - }, - "frequency":1, - "endDate":"2019-01-19T16:00:00.000Z" - } - - // from recurrence object to RecurrenceData XML string - $SP().parseRecurrence({"type":"weekly","frequency":1,"on":{"monday":true,"tuesday":true,"wednesday":true},"endDate":new Date("2007-05-31T22:00:00.000Z")}); // -> <recurrence><rule><firstDayOfWeek>mo</firstDayOfWeek><repeat><weekly mo="TRUE" tu="TRUE" we="TRUE" weekFrequency="1" /></repeat><windowEnd>2007-05-31T22:00:00Z</windowEnd></rule></recurrence> - - // recurrence object examples: - // Every weekday - { - "type":"daily", - "firstDayOfWeek":"monday", - "on":{ - "weekday":true - } - } - - // Every X days - { - "type":"daily", - "firstDayOfWeek":"monday", - "frequency":X - } - - // Every week on Monday and Wednesday - { - "type":"weekly", - "firstDayOfWeek":"monday", - "on":{ - "monday":true, - "wednesday":true - }, - "frequency":1 - } - - // Every day 10 of every 2 months - { - "type":"monthly", - "firstDayOfWeek":"monday", - "on":{ - "day":10 - }, - "frequency":2 - } - - // Every second tuesday of every 6 months - { - "type":"monthlyByDay", - "firstDayOfWeek":"monday", - "on":{ - "tuesday":"second" - }, - "frequency":6 - } - - // Every December 25 - { - "type":"yearly", - "firstDayOfWeek":"monday", - "on":{ - "month":12, - "day":25 - }, - "frequency":1 - } - - // The third weekday of September - { - "type":"yearlyByDay", - "firstDayOfWeek":"monday", - "on":{ - "month":9, - "weekday":"third" - }, - "frequency":1 - } - */ - parseRecurrence:function(data) { - // check if it's XML - var days = [ "monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday" ]; - var ret=null; - // e.g. mo2019-01-19T16:00:00Z - if (typeof data === "string" && data.charAt(0)==='<') { - ret = {}; - var parser = new DOMParser(); - var xmlDoc = parser.parseFromString(data, "text/xml"); - var XMLS = new XMLSerializer(); // for IE - var getText = function(elem) { - // IE doesn't provide innerHTML, so we serialize and use regexp - return (elem.innerHTML ? elem.innerHTML : XMLS.serializeToString(elem).replace(/^<[^>]+>([^<]+)<\/[^>]+>$/m,"$1")) - } - - // references: - // - http://thehightechheels.blogspot.com/2012/12/sharepoint-evenet-recurrencedata-xml.html - // - https://fatalfrenchy.wordpress.com/2010/07/16/sharepoint-recurrence-data-schema/ - var tags = ['firstDayOfWeek', 'daily', 'weekly', 'monthly', 'monthlyByDay', 'yearly', 'yearlyByDay', 'windowEnd', 'repeatInstances']; - tags.forEach(function(tag) { - var xmlField = xmlDoc.getElementsByTagName(tag); - if (xmlField.length===1) { - var elem = xmlField[0]; - if (/y$/.test(tag)) ret.type = tag; - switch(tag) { - case "firstDayOfWeek": { - var firstDayOfWeek = getText(elem) - for (var i=0; i<'+data.type+' '; - switch (data.type) { - case "daily": { - ret += (data.frequency ? 'dayFrequency="'+data.frequency+'"' : 'weekday="TRUE"') + ' />'; - break; - } - case "weekly": { - days.forEach(function(day) { - if (data.on[day]) ret += day.slice(0,2) + '="TRUE" '; - }); - ret += 'weekFrequency="' + data.frequency + '" />'; - break; - } - case "monthly": { - ret += 'monthFrequency="' + data.frequency + '" day="' + data.on.day + '" />'; - break; - } - case "yearlyByDay": - case "monthlyByDay": { - ['day', 'weekday', 'weekend'].concat(days).forEach(function(day, idx) { - if (data.on[day]) { - if (idx < 3) { - ret += day + (day==='weekend'?'_day':''); - } else { - ret += day.slice(0,2) - } - // monthlyByDay has "weekdayOfMonth" with "day" in lowercase, when yearlyByDay has "weekDayOfMonth" - ret += '="TRUE" '; - // avoid repeating "weekDayOfMonth" - if (ret.indexOf('ayOfMonth') === -1) { - ret += 'week' + (data.type==="monthlyByDay" ? 'd' : 'D') + 'ayOfMonth="' + data.on[day] + '" '; - } - day = data.on[day]; - } - }); - - if (data.on.month) ret += ' month="' + data.on.month + '"'; - ret += (data.type==="monthlyByDay" ? 'month' : 'year') + 'Frequency="' + data.frequency + '" />'; - break; - } - case "yearly": { - ret += 'yearFrequency="' + data.frequency + '" month="' + data.on.month + '" day="' + data.on.day + '" />'; - break; - } - } - ret += ''; - if (data.endDate) { - ret += '' + new Date(data.endDate).toISOString().replace(/.000Z/,"Z") + ''; - } else if (data.endAfter) { - ret += '' + data.endAfter + ''; - } else { - ret += 'FALSE'; - } - - ret += ''; - - return ret; - } - - return null; - }, - /** - @ignore - @name $SP()._cleanString - @function - @description clean a string to remove the bad characters when using AJAX over Sharepoint web services (like <, > and &) - Note: That should only be used as an internal function - @param {String} string The string to clean - */ - _cleanString:function(str) { - return str.replace(/&(?!amp;|lt;|gt;)/g, "&").replace(//g, ">"); - }, - /** - @name $SP().cleanResult - @function - @category lists - @description clean a string returned by a GET (remove ";#" and "string;#" and null becomes "") - - @param {String} str The string to clean - @param {String} [separator=";"] When it's a list we may want to have a different output (see examples) - @return {String} the cleaned string - - @example - $SP().cleanResult("15;#Paul"); // -> "Paul" - $SP().cleanResult("string;#Paul"); // -> "Paul" - $SP().cleanResult("string;#"); // -> "" - $SP().cleanResult(";#Paul;#Jacques;#Aymeric;#"); // -> "Paul;Jacques;Aymeric" - $SP().cleanResult(";#Paul;#Jacques;#Aymeric;#", ", "); // -> "Paul, Jacques, Aymeric" - */ - cleanResult:function(str,separator) { - if (str===null || typeof str==="undefined") return ""; - separator = separator || ";"; - return (typeof str==="string"?str.replace(/^(string;|float;|datetime;)#?/,"").replace(/;#-?[0-9]+;#/g,separator).replace(/^-?[0-9]+;#/,"").replace(/^;#|;#$/g,"").replace(/;#/g,separator):str); - }, - /** - @name $SP().list.get - @function - @description Get the content of the list based on different criteria (by default the default view is used) - - @param {Object} [options] Options (see below) - @param {String} [options.fields=""] The fields you want to grab (be sure to add "Attachments" as a field if you want to know the direct link to an attachment) - @param {String} [options.view=""] If you specify a viewID or a viewName that exists for that list, then the fields/where/order settings for this view will be used in addition to the FIELDS/WHERE/ORDERBY you have defined (the user settings will be used first) - @param {String|Array} [options.where=""] The query string (like SQL syntax) (you'll need to use double \\ before the inside ' -- see example below); you can use an array that will make the sequential requests but it will return all the data into one array (useful for the Sharepoint 2010 throttling limit) - @param {Boolean} [options.whereCAML=false] If you want to pass a WHERE clause that is with CAML Syntax only instead of SQL-like syntax -- see $SP().parse() for more info - @param {Boolean} [options.whereEscapeChar=true] Determines if we want to escape the special chars that will cause an error (for example '&' will be automatically converted to '&&amp;') -- this is applied to the WHERE clause only - @param {Function} [options.whereFct=function(w){return w}] Permits to apply your own function on the WHERE clause after conversion to CAML (can be useful also when you use the "view" parameter) - @param {Function} [options.progress] When using an array for the WHERE or the PAGING option then you can call the progress function (see the example) - @param {String} [options.orderby=""] The field used to sort the list result (you can also add "ASC" -default- or "DESC") - @param {String} [options.groupby=""] The field used to group by the list result - @param {Integer} [options.rowlimit=0] You can define the number of rows you want to receive back (0 is infinite) - @param {Boolean} [options.paging=false] If you have defined the 'rowlimit' then you can use 'paging' to cut by packets your full request -- this is useful when there is a list view threshold (attention: we cannot use "WHERE" or "ORDERBY" with this option) - @param {Integer} [options.page=infinite] When you use the `paging` option, several requests will be done until we get all the data, but using the `page` option you can define the number of requests/pages you want to get - @param {String} [options.listItemCollectionPositionNext=""] When doing paging, this is the index used by Sharepoint to get the next/previous page - @param {Boolean} [options.useIndexForOrderBy=false] Based on https://spservices.codeplex.com/discussions/280642#post1323410 it permits to override the 5,000 items limit in an unique call (for Sharepoint 2010 only) -- see the example below to know how to use it - @param {Boolean} [options.expandUserField=false] When you get a user field, you can have more information (like name,email,sip,...) by switching this to TRUE - @param {Boolean} [options.dateInUTC=false] TRUE to return dates in Coordinated Universal Time (UTC) format. FALSE to return dates in ISO format. - @param {Object} [options.folderOptions] Permits to read the content of a Document Library (see below) - @param {String} [options.folderOptions.path=""] Relative path of the folders we want to explore (by default it's the root of the document library) - @param {String} [options.folderOptions.show="FilesAndFolders_InFolder"] Four values: "FilesOnly_Recursive" that lists all the files recursively from the provided path (and its children); "FilesAndFolders_Recursive" that lists all the files and folders recursively from the provided path (and its children); "FilesOnly_InFolder" that lists all the files from the provided path; "FilesAndFolders_InFolder" that lists all the files and folders from the provided path - @param {Boolean} [options.queryOptions=undefined] If you want to provide your own QueryOptions and overwrite the ones built for you -- it should be some XML code (see https://msdn.microsoft.com/en-us/library/lists.lists.getlistitems%28v=office.12%29.aspx?f=255&MSPPError=-2147217396) - @param {Object} [options.join] Permits to create a JOIN closure between the current list and another one: it will be the same syntax than a regular GET (see the example below) - @param {String} [options.join.list] Permits to establish the link between two lists (see the example below) - @param {String} [options.join.url='current website'] The website url (if different than the current website) - @param {String} [options.join.on] Permits to establish the link between two lists (only between the direct parent list and its child, not with the grand parent) (see the example below) - @param {String} [options.join.onLookup] Permits to establish the link between two lists based on a lookup field... it's more optimized than the simple `join.on` (see the example below) - @param {Boolean} [options.join.outer=false] If you want to do an outer join (you can also directly use "outerjoin" instead of "join") - @param {Array} [merge] Permits to merge several lists together and return only one dataset; obviously it makes more sense to use this option when you have tables in different locations, but with the same columns. It must be an array of the same settings as $SP().list().get(). Each row of the dataset will have an extra `Source` parameter to know from where the data is coming from (see an example below) - @param {Boolean} [options.calendar=false] If you want to get the events from a Calendar List - @param {Object} [options.calendarOptions] Options that will be used when "calendar:true" (see the example to know how to use it) - @param {Boolean} [options.calendarOptions.splitRecurrence=true] By default we split the events with a recurrence (so 1 item = 1 day of the recurrence) - @param {String|Date} [options.calendarOptions.referenceDate=today] This is the date used to retrieve the events -- that can be a JS Date object or a SP Date (String) [attention: if 'splitRecurrence' is FALSE, then Sharepoint will ignore this 'referenceDate'...] - @param {String} [options.calendarOptions.range="Month"] By default we have all the events in the reference month (based on the referenceDate), but we can restrict it to a week with "Week" (from Monday to Sunday) (see https://blog.kodono.info/wordpress/2018/07/09/sharepoint-daterangesoverlap-value/) - @return {Promise} resolve(data returned by the server), reject(error from $SP().ajax()) - - @example - $SP().list("List Name").get().then(function(data) { - for (var i=0; i<data.length; i++) console.log(data[i].getAttribute("Title")); - }); - - // with some fields and an orderby command - $SP().list("ListName","http://www.mysharepoint.com/mydir/").get({ - fields:"Title,Organization", - orderby:"Title DESC,Test_x0020_Date ASC" - }).then(function(data) { - for (var i=0; i<data.length; i++) console.log(data[i].getAttribute("Title")); - }); - - // handle the errors - $SP().list("List Name").get().then(function(data) { - for (var i=0; i<data.length; i++) console.log(data[i].getAttribute("Title")); - }).catch(function(err) { - console.log("Error => ",err) - }); - - // the WHERE clause must be SQL-like - // the field names must be the internal names used by Sharepoint - // ATTENTION - note that here we open the WHERE string with simple quotes (') and that should be your default behavior each time you use WHERE - var name = "O'Sullivan, James"; - $SP().list("My List").get({ - fields:"Title", - where:'Fiscal_x0020_Week > 30 AND Fiscal_x0020_Week < 50 AND Name = "'+name+'"' - }).then(function(row) { - for (var i=row.length;i--;) console.log(row[i].getAttribute("Title")); - }); - - // Same example but this time we write the name directly inside the query... - // So make sure to use a single backslash (\) if you have a simple quote ' inside your WHERE with a double quotes (") to open/close the string - $SP().list("My List").get({ - fields:"Title", - where:'Fiscal_x0020_Week > 30 AND Fiscal_x0020_Week < 50 AND Name = "O\'Sullivan, James"' - }).then(function(row) { - for (var i=row.length;i--;) console.log(row[i].getAttribute("Title")); - }); - // Or to use a double backslash (\\) if you have a simple quote ' inside your WHERE with a simple quote (') to open/close the string - $SP().list("My List").get({ - fields:"Title", - where:"Fiscal_x0020_Week > 30 AND Fiscal_x0020_Week < 50 AND Name = 'O\\'Sullivan, James'" - }).then(function(row) { - for (var i=row.length;i--;) console.log(row[i].getAttribute("Title")); - }); - - // also in the WHERE clause you can use '[Me]' to filter by the current user, - $SP().list("My List").get({ - fields:"Title", - where:"Author = '[Me]'" - }).then(function(row) { - console.log(row[0].getAttribute("Title")); - }); - - // also in the WHERE clause you can use '[Today]' or '[Today-X]' with 'X' a number, - // Here it will return the records done yesterday - $SP().list("My List").get({ - fields:"Title", - where:"Created = '[Today-1]'" - }).then(function(row) { - console.log(row[0].getAttribute("Title")); - }); - - // Since 3.0.8, if you do a WHERE on a Date with the Time included, then it will compare with the tim - // see http://blogs.syrinx.com/blogs/sharepoint/archive/2008/08/05/caml-queries-with-dates.aspx - // here it will only show the items created at 2PM exactly -- if you want to check only the today, then use "Created = '2014-03-12'" - $SP().list("My List").get({ - fields:"Title", - where:"Created = '2014-03-12 14:00:00'" - }).then(function(row) { - console.log(row[0].getAttribute("Title")); - }); - - // We have a list called "My List" with a view already set that is called "Marketing View" with some FIELDS and a WHERE clause - // so the function will grab the view information and will get the data from the list with "Author = '[Me]'" and adding the view's WHERE clause too - $SP().list("My List","http://my.sharepoint.com/my/site/").get({ - view:"Marketing View", - where:"Author = '[Me]'" - }).then(function(data) { - for (var i=data.length; i--;) console.log(data[i].getAttribute("Title") + " by " + data[i].getAttribute("Author")); - }); - - // use the paging option for the large list to avoid the message "the attempted operation is prohibited because it exceeds the list view threshold enforced by the administrator" - // ATTENTION: if you use the WHERE option then it could return the "view threshold" error message because the packet from the WHERE is too big and SharePoint is too stupid... - $SP().list("My List").get({ - fields:"ID,Title", - rowlimit:5000, - paging:true, - progress:function progress(nbItemsLoaded) { - // for each new page this function will be called - console.log("It's still loading... already "+nbItemsLoaded+" items have been loaded!"); - } - }).then(function(data) { - console.log(data.length); // -> 23587 - }) - // add the `page` option to stop after a number of requests/pages - // for example you only want the last record from a list that has more than 5,000 items - $SP().list("My List").get({fields:"ID",orderby:"ID DESC",rowlimit:1,paging:true,page:1}).then(function(data) { - console.log("last ID : "+data[0].getAttribute("ID")); - }) - // use `listItemCollectionPositionNext` to start from this index - $SP().list("My List").get({ - fields:"ID", - orderby:"ID DESC", - rowlimit:10, - paging:true, - page:1 - }).then(function(data) { - // "data" is our first page of data - // get the next block - return $SP().list("My List").get({fields:"ID",orderby:"ID DESC",rowlimit:10,paging:true,page:1,listItemCollectionPositionNext:data.NextPage}) - }).then(function(data) { - // here "data" is the 2nd block of data - // If you want to access to the previous page, you can use: - // listItemCollectionPositionNext:"Paged=TRUE&PagedPrev=True&p_ID="+ID - // with ID the first/smallest ID of the current set. - // reference: https://social.technet.microsoft.com/wiki/contents/articles/18606.sharepoint-2013-paging-with-sharepoint-client-object-model.aspx - }) - - // We can also find the files from a Document Shared Library - $SP().list("Shared Documents","http://my.share.point.com/my_site/").get({ - fields:"FileLeafRef,File_x0020_Size", - }).then(function(data) { - for (var i=0; i<<data.length; i++) console.log("FileName:"+data[i].getAttribute("FileLeafRef"),"FileSize:"+data[i].getAttribute("File_x0020_Size")); - }); - - // We can join two or more lists together based on a condition - // ATTENTION: in that case the DATA passed to the callback will return a value for "LIST NAME.FIELD_x0020_NAME" and not directly "FIELD_x0020_NAME" - // ATTENTION: you need to make sure to call the 'fields' that will be used in the 'on' clause - $SP().list("Finance and Expense","http://my.sharepoint.com/my_sub/dir/").get({ - fields:"Expense_x0020_Type", - where:"Finance_x0020_Category = 'Supplies'", - join:{ - list:"Purchasing List", - fields:"Region,Year,Expense_x0020_Type,Finance_x0020_Category,Cost", - where:"Region = 'EMEA' AND Year = 2012", - orderby:"Expense_x0020_Type,Finance_x0020_Category", - on:"'Purchasing List'.Expense_x0020_Type = 'Finance and Expense'.Expense_x0020_Type", - join:{ - list:"Financial Static Data", - fields:"Region,Year,Expense_x0020_Type,Finance_x0020_Category,Forecast", - where:"Region = 'EMEA' AND Year = 2012", - on:"'Purchasing List'.Region = 'Financial Static Data'.Region AND 'Purchasing List'.Expense_x0020_Type = 'Financial Static Data'.Expense_x0020_Type" - } - } - }).then(function(data) { - for (var i=0; i<data.length; i++) { - console.log(data[i].getAttribute("Purchasing List.Region")+" | "+data[i].getAttribute("Purchasing List.Year")+" | "+data[i].getAttribute("Purchasing List.Expense_x0020_Type")+" | "+data[i].getAttribute("Purchasing List.Cost")); - } - }); - - // By default "join" is an "inner join", but you can also do an "outerjoin" - // ATTENTION: in that case the DATA passed to the callback will return a value for "LIST NAME.FIELD_x0020_NAME" and not directly "FIELD_x0020_NAME" - // ATTENTION: you need to make sure to call the 'fields' that will be used in the 'on' clause - $SP().list("Finance and Expense","http://my.sharepoint.com/my_sub/dir/").get({ - fields:"Expense_x0020_Type", - where:"Finance_x0020_Category = 'Supplies'", - join:{ - list:"Purchasing List", - fields:"Region,Year,Expense_x0020_Type,Finance_x0020_Category,Cost", - where:"Region = 'EMEA' AND Year = 2012", - orderby:"Expense_x0020_Type,Finance_x0020_Category", - on:"'Purchasing List'.Expense_x0020_Type = 'Finance and Expense'.Expense_x0020_Type", - outerjoin:{ - list:"Financial Static Data", - fields:"Region,Year,Expense_x0020_Type,Finance_x0020_Category,Forecast", - where:"Region = 'EMEA' AND Year = 2012", - on:"'Purchasing List'.Region = 'Financial Static Data'.Region AND 'Purchasing List'.Expense_x0020_Type = 'Financial Static Data'.Expense_x0020_Type" - } - } - }).then(function(data) { - for (var i=0; i<data.length; i++) - console.log(data[i].getAttribute("Purchasing List.Region")+" | "+data[i].getAttribute("Purchasing List.Year")+" | "+data[i].getAttribute("Purchasing List.Expense_x0020_Type")+" | "+data[i].getAttribute("Purchasing List.Cost")); - }) - - // Another example of "outerjoin", but this time with fields tied to a Lookup ID - // Here 1 Project can have several Deliverables based on field "Project ID", and 1 Deliverable can have several team members based on "Deliverable ID" - $SP().list("Projects").get({ - fields:"ID,Project_x0020_Name", - where:"Status = 'In Progress'", - outerjoin:{ - list:"Deliverables", - fields:"ID,Name", - onLookup:"Project_x0020_ID", - outerjoin:{ - list:"Team Members", - fields:"ID,Deliverable_x0020_ID,Name", - onLookup:"Deliverable_x0020_ID" - } - } - }).then(function(data) { - var html = '<table class="table default"><thead><tr><th>Project ID</th><th>Project Name</th><th>Deliverable ID</th><th>Deliverable Name</th><th>Team ID</th><th>Member Name</th></tr></thead><tbody>' - for (var i=0;i<data.length; i++) { - html += '<tr><td>'+data[i].getAttribute("Projects.ID")+'</td><td>'+data[i].getAttribute("Projects.Project_x0020_Name")+'</td><td>'+data[i].getAttribute("Deliverables.ID")+'</td><td>'+data[i].getAttribute("Deliverables.Name")+'</td><td>'+data[i].getAttribute("Team Members.ID")+'</td><td>'+data[i].getAttribute("Team Members.Name")+'</td></tr>' - } - html += '</tbody></table>'; - $('#part1').html(html); - }) - - // With Sharepoint 2010 we are limited due to the throttling limit (see here for some very interesting information http://www.glynblogs.com/2011/03/sharepoint-2010-list-view-throttling-and-custom-caml-queries.html) - // So for example if I do WHERE:'Fiscal_x0020_Year = 2012' it will return an error because I have 6,500 items - // So I'll do several requests for each Fiscal_x0020_Week into this Fiscal Year - var query=[],q=[]; - for (var i=1; i<54; i++) { - q.push("Fiscal_x0020_Week = "+i); - if (i%8==0 || i == 53) { - query.push("("+q.join(" OR ")+") AND Fiscal_x0020_Year = 2012"); - q=[] - } - } - // it returns : - // [ - // "(Fiscal_x0020_Week = 1 OR Fiscal_x0020_Week = 2 OR Fiscal_x0020_Week = 3 OR Fiscal_x0020_Week = 4 OR Fiscal_x0020_Week = 5 OR Fiscal_x0020_Week = 6 OR Fiscal_x0020_Week = 7 OR Fiscal_x0020_Week = 8) AND Fiscal_x0020_Year = 2012", - // ... - // "(Fiscal_x0020_Week = 49 OR Fiscal_x0020_Week = 50 OR Fiscal_x0020_Week = 51 OR Fiscal_x0020_Week = 52 OR Fiscal_x0020_Week = 53) AND Fiscal_x0020_Year = 2012" - // ] - $SP().list("Sessions").get({ - fields:"Title,Score", - where:query, - progress:function progress(current,max) { - // when we use an array for the WHERE clause we are able to provide `current` and `max` - console.log("Progress: "+current+" / "+max); - } - }).then(function(data) { - console.log(data.length); // -> 6,523 - }); - // also regarding the throttling limit, you can query a list on a user column in using the User ID - // For example if John Doe is recorded as "328;#Doe, John" then you'll have to use the special operator "~=" - $SP().list("Sessions").get({ - fields:"Title", - where:"User ~= 328" - }).then(function(data) { - console.log(data.length); - }); - - // if you want to list only the files visible into a folder for a Document Library - $SP().list("My Shared Documents").get({ - fields:"BaseName,FileRef,FSObjType", // "BaseName" is the name of the file/folder; "FileRef" is the full path of the file/folder; "FSObjType" is 0 for a file and 1 for a folder (you need to apply $SP().cleanResult()), "File_x0020_Size" the filesize in bytes - folderOptions:{ - path:"My Folder/Sub Folder/", - show:"FilesOnly_InFolder" - } - }); - - // if you want to list all the files and folders for a Document Library - $SP().list("My Shared Documents").get({ - fields:"BaseName,FileRef,FSObjType", // "BaseName" is the name of the file/folder; "FileRef" is the full path of the file/folder; "FSObjType" is 0 for a file and 1 for a folder (you need to apply $SP().cleanResult()) - folderOptions:{ - show:"FilesAndFolders_Recursive" - } - }); - - // How to retrieve the events from a Calendar List - // NOTE -- when "calendar:true" we automatically get some fields: "Title", "EventDate" -- the Start Date --, "EndDate", "RecurrenceData", "Duration", "fAllDayEvent", "fRecurrence", "ID", "EventType", "UID" and "MasterSeriesItemID" - $SP().list("My Calendar").get({ - fields:"Description", - calendar:true, - calendarOptions:{ - referenceDate:new Date(2012,4,4), - range: "Week" - }, - where:"Category = 'Yellow'" - }).then(function(data) { - var events=[]; - for (var i=0; i<data.length; i++) { - // several information are available -- see below - events.push({ - Title: data[i].getAttribute("Title"), - StartDateTime: data[i].getAttribute("EventDate"), // you can use $SP().toDate() to have a JS Date - EndDateTime: data[i].getAttribute("EndDate"), // you can use $SP().toDate() to have a JS Date - Recurrence: (data[i].getAttribute("fRecurrence") == 1 ? true : false), - AllDayEvent: (data[i].getAttribute("fAllDayEvent") == 1 ? true : false), - RecurrenceEnd: (data[i].getAttribute("RecurrenceData")||"").replace(/.+([^<]+)<\/windowEnd>.+/,"$1"), // see the NOTE below - ID: data[i].getAttribute("ID"), // the ID for the recurrence events is special but can be also passed to "Display.aspx?ID=" - Duration: 1*data[i].getAttribute("Duration") // Duration is in SECONDS - }) - // NOTE: with data[i].getAttribute("RecurrenceData") you'll find more info about the recurrence (like the end date for the serie, and much more), - // but because there are a lot of scenario, I won't handle the different cases. - // e.g. for a daily recurrence you can find the end date of the serie with: data[i].getAttribute("RecurrenceData").replace(/.+([^<]+)<\/windowEnd>.+/,"$1") - // --> it will return a SP Date - } - }) - - // if we want to merge two tables - $SP().list("Requests").get({ - fields:"ID", - where:"User_x0020_Name ~= 16358", - merge:[{ - list:"Requests Archive", - fields:"ID", - where:"User_x0020_Name ~= 16358" - }] - }) - .then(function(data) { - data.forEach(function(d) { console.log(d.Source.list, d.Source.url, d.getAttribute("ID")) }) - }) - - // for Discussion Board, please refer to https://github.com/Aymkdn/SharepointPlus/wiki/Sharepoint-Discussion-Board - - // [It doesn't work with Sharepoint 2013 anymore, only for SP2010] - // You can use `useIndexForOrderBy:true` to override the list view threshold -- see https://spservices.codeplex.com/discussions/280642 - // To make it to work, you need : - // 1) to have "ID > 0 AND Another_Index_Column_Filtered" in the WHERE Clause (so at least two filters), and then we can add some other WHERE (even the not indexed columns) - // 2) To use `orderby`, with an indexed column - // 3) To use `useIndexForOrderBy:true` - // see the below example with Trainer an indexed column, and Equipment a column not indexed - // NOTE: you need to test your WHERE to see if it works or not, because it's vary a lot depending of the kind of WHERE clause you'll use - $SP().list("Calendar",'http://intranet.dell.com/services/Educate/Toolbox/scheduling_tool/').get({ - fields:"Trainer", - where:'ID > 0 AND Trainer <> "" AND Equipment LIKE "Box"', - orderby:'Trainer', - useIndexForOrderBy:true - }).then(function(d) { - console.log(d.length) - }) - */ - get:function(options) { - var _this = this; - return _this._promise(function(prom_resolve, prom_reject) { - // check if we need to queue it - if (_this.needQueue) { return _this._addInQueue('get',arguments) } - if (!_this.listID) return prom_reject("[SharepointPlus 'get']: the list ID/Name is required"); - - // default values - var setup={}; - SPExtend(true, setup, options); - if (!_this.url) return prom_reject("[SharepointPlus 'get']: not able to find the URL!"); // we cannot determine the url - - setup.fields = setup.fields || ""; - setup.where = setup.where || ""; - setup.whereFct = setup.whereFct || function(w) { return w }; - setup.orderby = setup.orderby || ""; - setup.useIndexForOrderBy = (setup.useIndexForOrderBy===true ? true : false); - setup.groupby = setup.groupby || ""; - setup.rowlimit = setup.rowlimit || 0; - setup.whereEscapeChar= (setup.whereEscapeChar===false ? false : true); - setup.paging = (setup.paging===true ? true : false); - setup.page = (setup.paging===false || isNaN(setup.page) ? 5000 : setup.page); - if (setup.paging && setup.rowlimit === 0) setup.rowlimit = 5000; // if rowlimit is not defined, we set it to 5000 by default - setup.expandUserField = (setup.expandUserField===true || setup.expandUserField==="True"?"True":"False"); - setup.dateInUTC = (setup.dateInUTC===true?"True":"False"); - setup.folderOptions = setup.folderOptions || null; - setup.view = setup.view || ""; - setup.calendar = (setup.calendar===true ? true : false); - if (setup.calendar===true) { - setup.calendarOptions = setup.calendarOptions || {}; - setup.calendarOptions.referenceDate = setup.calendarOptions.referenceDate || new Date(); - if (typeof setup.calendarOptions.referenceDate !== "string") setup.calendarOptions.referenceDate=_this.toSPDate(setup.calendarOptions.referenceDate) - setup.calendarOptions.splitRecurrence = (setup.calendarOptions.splitRecurrence===false ? "FALSE" : "TRUE"); - setup.calendarOptions.range = setup.calendarOptions.range || "Month"; - } - // if (setup.whereCAML!==true) setup.whereCAML = (setup.view!=""); - setup.results = setup.results || []; // internal use when there is a paging - setup.listItemCollectionPositionNext = setup.listItemCollectionPositionNext || ""; // for paging - // protect & into ListItemCollectionPositionNext - if (setup.listItemCollectionPositionNext) setup.listItemCollectionPositionNext = setup.listItemCollectionPositionNext.replace(/&/g,"&").replace(/&amp;/g,"&"); - - // if setup.where is an array, then it means we want to do several requests - // so we keep the first WHERE - if (Array.isArray(setup.where)) { - setup.where = setup.where.slice(0); // clone the original array - if (!setup.originalWhere) setup.originalWhere = setup.where.slice(0); - setup.nextWhere = setup.where.slice(1); - setup.where = setup.where.shift(); - } else { - setup.originalWhere = setup.where; - setup.nextWhere = []; - } - // we use the progress only when WHERE is an array - setup.progress = setup.progress || (function() {}); - - // if view is defined, then we need to find the view ID - if (setup.view !== "") { - // retrieve the View ID based on its name - // and find the view details - _this.view(setup.view).then(function(view) { - setup.view=view.ID; - var where = (setup.whereCAML ? setup.where : _this.parse(setup.where)); - // if we have a 'DateRangesOverlap' then we want to move this part at the end -- since v3.0.9 - var mtchDateRanges = view.WhereCAML.match(/^(.*<\/DateRangesOverlap>)(.*)<\/And>$/); - if (mtchDateRanges && mtchDateRanges.length === 3) view.WhereCAML = ''+mtchDateRanges[2]+mtchDateRanges[1]+'' - where += view.WhereCAML; - if (setup.where !== "" && view.WhereCAML !== "") where = "" + where + ""; - setup.where=where; - setup.fields += (setup.fields===""?"":",") + view.Fields.join(","); - setup.orderby += (setup.orderby===""?"":",") + view.OrderBy; - setup.whereCAML=true; - setup.useOWS=true; - // disable the calendar option - setup.calendarViaView=setup.calendar; - setup.calendar=false; - delete setup.view; - _this.get(setup).then(function(res) { prom_resolve(res) }, function(rej) { prom_reject(rej) }); - }) - .catch(function(err) { - prom_reject(err); - }) - return; - } - // what about the fields ? - var fields="", i, orderby="", fieldsDir, direction, splt, groupby="", gFields, tmpFields, body="", viewAttr, where="", whereDateRanges; - - if (setup.fields.length>0) { - if (typeof setup.fields === "string") setup.fields = setup.fields.replace(/^\s+/,"").replace(/\s+$/,"").replace(/( )?,( )?/g,",").split(","); - for (i=0; i'; - } - - // what about sorting ? - if (setup.orderby !== "") { - fieldsDir = setup.orderby.split(","); - for (i=0; i 0) { - if (splt.length==2) direction = splt[1].toUpperCase(); - orderby += ''; - } - } - } - // if calendar:true and no orderby, then we order by the EventDate - if ((setup.calendar===true||setup.calendarViaView===true) && orderby==="") orderby = '' - - // what about groupby ? - if (setup.groupby !== "") { - gFields = setup.groupby.split(","); - for (i=0; i'; - } - - // if we merge several similar lists - if (Array.isArray(setup.merge)) { - setup.mergeData = setup.mergeData || []; - } - - // when it's a calendar we want to retrieve some fields by default - if (setup.calendar===true || setup.calendarViaView===true) { - tmpFields = ["Title", "EventDate", "EndDate", "Duration", "fAllDayEvent", "fRecurrence", "RecurrenceData", "ID", "MasterSeriesItemID", "UID", "RecurrenceID"]; - for (i=0; i'; - } - - // forge the parameters - // if no queryOptions provided then we set the default ones - if (setup.queryOptions === undefined) { - setup._queryOptions = ""+setup.dateInUTC+"" - + "" - + "True" - + (fields==="" ? "" : "False") - + ""+setup.expandUserField+""; - // check if we want something related to the folders - if (setup.folderOptions) { - switch (setup.folderOptions.show) { - case "FilesAndFolders_Recursive": viewAttr="RecursiveAll"; break - case "FilesOnly_InFolder": viewAttr="FilesOnly"; break - case "FilesAndFolders_InFolder": viewAttr=""; break - case "FilesOnly_Recursive": - default: viewAttr="Recursive" - } - setup._queryOptions += "" - if (setup.folderOptions.path) setup._queryOptions += ""+_this.url + '/' + _this.listID + '/' + setup.folderOptions.path+"" - } else - setup._queryOptions += "" - } else setup._queryOptions = setup.queryOptions - if (setup.calendarOptions) { - setup._queryOptions += "" + setup.calendarOptions.referenceDate + "" - + "v3" - + ""+setup.calendarOptions.splitRecurrence+""; - } - - // what about the Where ? - if (setup.where !== "") { - if (setup.whereCAML) where=setup.where; - else where=_this.parse(setup.where); - } - if (setup.calendar===true) { - whereDateRanges = "" - + "" - + "" - + "" - + "<" + setup.calendarOptions.range + " />" /* there is a property called IncludeTimeValue="TRUE" */ - + "" - if (where !== "") where = "" + where + whereDateRanges + ""; - else where = whereDateRanges; - } - where = setup.whereFct(where); - body = ""+_this.listID+"" - + ""+(setup.viewID||"")+"" - + "" - + "" - + ( where!="" ? ""+ where +"" : "" ) - + ( groupby!="" ? ""+groupby+"" : "" ) - + ( orderby!="" ? ""+orderby+"" : "" ) - + "" - + "" - + "" - + "" - + fields - + "" - + "" - + ""+setup.rowlimit+"" - + "" - + "" - + setup._queryOptions - + "" - + ""; - body = _this._buildBodyForSOAP("GetListItems", body); - - // do the request - _this.ajax({ - url: _this.url + "/_vti_bin/Lists.asmx", - body: body - }).then(function(data) { - var rows, i, j, stop, collection, on, aResult, prevIndex, index, listIndexFound, nextPage, - joinDataLen, tmp, attributes, attributesReturn, attr, attributesJoinData, joinIndexLen, idx, sp, - joinData, joinIndex, joinWhereLookup, wh, aReturn, mergeSetup, mergeSource; - // we want to use myElem to change the getAttribute function - rows=data.getElementsByTagName('z:row'); - if (rows.length===0) rows=data.getElementsByTagName('row'); // for Chrome 'bug' - aReturn = fastMap(rows, function(row) { return myElem(row); }); - - // if setup.results length is bigger than 0 then it means we need to add the current data - if (setup.results.length>0) - for (i=0,stop=aReturn.length; i 0) { - // check if we need to go to another request - if (setup.results.length===0) setup.results=aReturn; - // notify that we keep loading - setup.progress(setup.results.length); - if (nextPage) { - // we need more calls - setup.listItemCollectionPositionNext=_this._cleanString(nextPage); - _this.get(setup).then(function(res) { prom_resolve(res) }, function(rej) { prom_reject(rej) }); - return; - } else { - aReturn = setup.results - // it means we're done, no more call - } - } else if (setup.nextWhere.length>0) { // if we need to so some more request - if (setup.results.length===0) setup.results=aReturn - setup.where = setup.nextWhere.slice(0); - _this.get(setup).then(function(res) { prom_resolve(res) }, function(rej) { prom_reject(rej) }); - return; - } else { - // rechange setup.where with the original one just in case it was an array to make sure we didn't override the original array - setup.where = setup.originalWhere; - aReturn = (setup.results.length>0?setup.results:aReturn); - } - - // we have data from a previous list, so let's merge all together the both of them - if (setup.joinData) { - on = setup.joinData["noindex"]; - aResult = []; - prevIndex=""; - listIndexFound={length:0}; - if (!on.length) alert("$SP.get() -- Error 'get': you must define the ON clause when JOIN is used."); - // we have a linked list so do some stuff here to tie the two lists together - for (i=0,stop=aReturn.length; i0) { - // SP2013 limits to 60 items per IN - wh=SPArrayChunk(joinWhereLookup, 60); - for (j=0; j0) { - mergeSetup = setup.merge.shift(); - mergeSetup.merge = setup.merge.slice(0); - sp=_this.list(mergeSetup.list,mergeSetup.url||_this.url); - // we need to identify the Source of each set - mergeSetup.mergeData=setup.mergeData.concat(aReturn.map(function(ret) { - ret.Source = mergeSource; - return ret; - })); - sp.get(mergeSetup).then(function(res) { prom_resolve(res) }, function(rej) { prom_reject(rej) }); - return; - } else { - aReturn = setup.mergeData.concat(aReturn.map(function(ret) { - ret.Source = mergeSource; - return ret; - })); - } - } - - aReturn["NextPage"]=nextPage; - prom_resolve(aReturn) - }, - function(rej) { - prom_reject(rej) - }); - }); - }, - /** - @name $SP().list.createFile - @function - @category files - @description Create/Upload a file into a Document library - - @param {Object} setup Options (see below) - @param {ArrayBuffer} setup.content The file content - @param {String} setup.filename The relative path (within the document library) to the file to create - @param {Object} [setup.fields] If you want to set some fields for the created document - @param {Function} [setup.progress=function(percentage){}] The upload progress in percentage - @param {Function} [setup.getXHR=function(xhr){}] To manipulate the XMLHttpRequest object used during the upload - @return {Promise} resolve(object that represents the file), reject(error) - - @example - $SP().list("Documents", "http://my.other.site/website/").createFile({ - content:"*ArrayBuffer*", - filename:"Demo/HelloWorld.txt" - }).then(function(file) { - console.log(file.Url+" has been created") - // and to get more info, like the ID, you can do: - $SP().ajax({url:file.AllFieldsUrl}).then(function(body) { - console.log(body.d) - }) - }, function(error) { - console.log("Error: ",error) - }) - - // create a text document with some fields - $SP().list("Shared Documents").createFile({ - content:"ArrayBuffer*", - filename:"SubFolder/myfile.txt", - fields:{ - "Title":"My Document", - "File_x0020_Description":"This is my file!" - } - }).then(function(file) { - alert("File "+file.Name+" created at " + file.Url); - }); - - // use onprogress and abort - $SP().list("Documents").createFile({ - content:"*ArrayBuffer*", - filename:"BigFile.iso", - progress:function(perc) { - console.log("percentage of progress => ",perc) - }, - getXHR:function(xhr) { - // automtically abort after 3 seconds - setTimeout(function() { - xhr.abort() - }, 3000) - } - }).then(function(file) { - console.log(file.Url+" has been created") - }, function(error) { - console.log("Error: ",error) - }) - - // example with a input[type="file"] - // <input type="file" id="file_to_upload"> <button type="button" onclick="_uploadFile()">Upload</button> - function _uploadFile() { - var files; - // retrive file from INPUT - files = document.querySelector('#file_to_upload').files; - if (!files || files.length === 0) { - alert("ERROR: Select a file"); - return; - } - files = Array.prototype.slice.call(files); - // read the files - Promise.all(files.map(function(file) { - return new Promise(function(prom_res, prom_rej) { - // use fileReader - var fileReader = new FileReader(); - fileReader.onloadend = function(e) { - file.content = e.target.result; - prom_res(file); - } - fileReader.onerror = function(e) { - prom_rej(e.target.error); - } - fileReader.readAsArrayBuffer(file); - }) - })).then(function(files) { - // upload files - return Promise.all(files.map(function(file) { - return $SP().list("SharepointPlusLibrary").createFile({ - content:file.content, - filename:file.name, - progress:function(perc) { - console.log("Progress => ",perc+"%") - } - }) - })) - }) - } - - // if you want to add some headers, for example for authentication method - $SP().list("SharepointPlusLibrary").createFile({ - content:file.content, - filename:file.name, - getXHR:function(xhr) { - xhr.setRequestHeader('Authorization','Bearer XYZ') - } - }) - - // NOTE: in some cases the files are automatically checked out, so you have to use $SP().checkin() - */ - createFile:function(setup) { - var _this=this; - return _this._promise(function(prom_resolve, prom_reject) { - // check if we need to queue it - if (_this.needQueue) { return _this._addInQueue('createFile',arguments) } - // default values - setup = setup || {}; - if (setup.content === undefined) throw "[SharepointPlus 'createFile']: the file content is required."; - if (setup.filename === undefined) throw "[SharepointPlus 'createFile']: the filename is required."; - if (!_this.listID) throw "[SharepointPlus 'createFile']: the library name is required."; - if (!_this.url) throw "[SharepointPlus 'createFile']: not able to find the URL!"; // we cannot determine the url - setup.extendedFields = setup.extendedFields || ""; - setup.progress=setup.progress||function(){}; - setup.getXHR=setup.getXHR||function(){}; - // we now decide what to do based on if we have REST - // if no, then relay on Copy Web Service - _this.hasREST().then(function(hasREST) { - if (!hasREST) { - // use Copy Web Service - // if we have setup.fields, then we need to figure out the Type using $SP().list().info() - if (setup.fields && !setup.extendedFields) { - _this.info().then(function(fields) { - // we use extendedFields to define the Type - for (var i=fields.length; i--;) { - if (setup.fields[fields[i]["StaticName"]]) { - setup.extendedFields += '' - } - } - if (!setup.extendedFields) delete setup.fields; - if (setup.useCallback) _this.createFile(setup); - else _this.createFile(setup).then(function(res) { prom_resolve(res) }, function(rej) { prom_reject(rej) }) - }); - return; - } - var destination = "/" + _this.listID + "/" + setup.filename - destination = (_this.url + destination).replace(/([^:]\/)\//g,"$1"); - if (destination.slice(0,4) !== "http") destination=window.location.protocol + "//" + window.location.host + destination; - setup.content=SPArrayBufferToBase64(setup.content); // ArrayBuffer to Base64 String - var soapEnv = "http://null" - +""+destination+"" - +''+setup.extendedFields+'' - +""+setup.content+"" - soapEnv = _this._buildBodyForSOAP("CopyIntoItems", soapEnv); - _this.ajax({ - url: _this.url + "/_vti_bin/copy.asmx", - body: soapEnv, - onprogress:function(evt) { - if (evt.lengthComputable) { - setup.progress(parseInt(evt.loaded / evt.total * 100)); - } - }, - getXHR:setup.getXHR, - headers:{'SOAPAction':'http://schemas.microsoft.com/sharepoint/soap/CopyIntoItems'} - }).then(function(data) { - var a = data.getElementsByTagName('CopyResult'); - a = (a.length>0 ? a[0] : null); - if (a && a.getAttribute("ErrorCode") !== "Success") { - prom_reject("[SharepointPlus 'createFile'] Error creating ("+destination+"): "+a.getAttribute("ErrorCode")+" - "+a.getAttribute("ErrorMessage")); - } else { - prom_resolve({Url:destination, Name:setup.filename}); - } - }, function(rej) { - prom_reject(rej); - }); - } else { - // use REST API - // we need to find the RootFolder for the list - var file = {}; - _this.info() - .then(function(infos) { - var rootFolder = infos._List.RootFolder; - var folder = setup.filename.split("/"); - var filename = setup.filename; - if (folder.length > 1) { - filename=folder.slice(-1)[0]; - folder="/"+folder.slice(0,-1).join("/"); - } - else folder=""; - folder = rootFolder+folder; - - // to avoid invalid characters - // see http://www.simplyaprogrammer.com/2008/05/importing-files-into-sharepoint.html - // eslint-disable-next-line - var _filename = filename.replace(/[\*\?\|:"'<>#{}%~&]/g,"").replace(/^[\. ]+|[\. ]+$/g,"").replace(/ {2,}/g," ").replace(/\.{2,}/g,"."); - if (_filename.length>=128) { - _filename = _filename.slice(0,115)+'__'+_filename.slice(-8); - } - - var urlCall = _this.url+"/_api/web/GetFolderByServerRelativeUrl('"+encodeURIComponent(folder)+"')/files/add(url='"+encodeURIComponent(_filename)+"',overwrite=true)"; - // the URL must not be longer than 20 characters - // The browsers could crash if we try to use send() with a large ArrayBuffer (https://stackoverflow.com/questions/46297625/large-arraybuffer-crashes-with-xmlhttprequest-send) - // so I convert ArrayBuffer into a Blob - // note: we cannot use startUpload/continueUpload/finishUpload because it's only available for Sharepoint Online - if (typeof Blob !== "undefined") setup.content = new Blob([setup.content]); - return _this.ajax({ - url: urlCall, - body: setup.content, - onprogress:function(evt) { - if (evt.lengthComputable) { - setup.progress(parseInt(evt.loaded / evt.total * 100)); - } - }, - getXHR:setup.getXHR - }) - }) - .then(function(body) { - // retrieve the full path - SPExtend(true, file, body.d); - file.Url=file.__metadata.uri.split("/").slice(0,3).join("/")+body.d.ServerRelativeUrl; - file.AllFieldsUrl=body.d.ListItemAllFields.__deferred.uri; - // if we want to update some fields - if (setup.fields) { - // using "ListItemAllFields.__deferred.uri" we can find the URL to get details about the uploaded file - return _this.ajax({url:file.AllFieldsUrl}) - } else { - prom_resolve(file) - } - }) - .then(function(body) { - SPExtend(true, file, body.d); - var params={ID:file.ID}; - SPExtend(params, setup.fields); - return _this.update(params); - }) - .then(function(items) { - if (items.failed.length>0) { - prom_reject("File '"+file.Url+"' added, but fields not updated: "+items.failed[0].errorMessage) - } else { - items=items.passed[0]; - for (var attr in items) { - file[attr]=items[attr]; - } - prom_resolve(file) - } - }) - .catch(function(err) { prom_reject(err) }); - } - }) - }) - }, - /** - @name $SP().list.createFolder - @function - @category files - @description Create a folter in a Document library - - @param {String} path The relative path to the new folder - @return {Promise} resolve({BaseName,ID,FSObjType}), reject(error) - - @example - // create a folder called "first" at the root of the Shared Documents library - // the result should be "http://mysite/Shared Documents/first/" - // if the folder already exists, it returns a resolved Promise but with an errorMessage included - $SP().list("Shared Documents").createFolder("first").then(function(folder) { alert("Folder created!"); }) - - // create a folder called "second" under "first" - // the result should be "http://mysite/Shared Documents/first/second/" - // if "first" doesn't exist then it's automatically created - $SP().list("Documents").createFolder("first/second").then(function(folder) { alert("Folder created!"); } - - // Note: To delete a folder you can use $SP().list().remove() with ID and FileRef parameters - */ - createFolder:function(folderPath) { - // default values - var _this=this; - return _this._promise(function(prom_resolve, prom_reject) { - // check if we need to queue it - if (_this.needQueue) { return _this._addInQueue('createFolder',arguments) } - if (folderPath === undefined) throw "[SharepointPlus 'createFolder']: the folder path is required."; - // split the path based on '/' - // eslint-disable-next-line - var path=folderPath.replace(/[\*\?\|:"'<>#{}%~&]/g,"").replace(/^[\. ]+|[\. ]+$/g,"").replace(/ {2,}/g," ").replace(/\.{2,}/g,"."), toAdd=[], tmpPath="", i; - // trim "/" at the beginning and end - if (path.charAt(0)==="/") path=path.slice(1); - if (path.slice(-1)==="/") path=path.slice(0,-1); - path=path.split('/'); - for (i=0; i0?'/':'') + path[i]; - toAdd.push({FSObjType:1, BaseName:tmpPath}) - } - _this.add(toAdd) - .then(function(rows) { - // remove first and last "/" for folderPath - if (folderPath.charAt(0)==="/") folderPath=folderPath.slice(1); - if (folderPath.slice(-1)==="/") folderPath=folderPath.slice(0,-1); - // check if our final folder has been correctly created - var i; - for (i=rows.passed.length; i--;) { - if (rows.passed[i].BaseName === folderPath) { - prom_resolve(rows.passed[i]); - return; - } - } - for (i=rows.failed.length; i--;) { - if (rows.failed[i].BaseName === folderPath) { - if (rows.failed[i].errorMessage.indexOf('0x8107090d') > -1) { // duplicate folder - rows.failed[i].errorMessage="Folder '"+rows.failed[i].BaseName+"' already exists."; - prom_resolve(rows.failed[i]); - } else { - prom_reject(rows.failed[i].errorMessage); - } - return; - } - } - prom_reject("Unknown error"); - }) - .catch(function(error) { prom_reject(error) }); - }) - }, - /** - @name $SP().checkin - @function - @category files - @description Checkin a file - - @param {Object} [setup] Options (see below) - @param {String} setup.destination The full path to the file to check in - @param {String} [setup.comments=""] The comments related to the check in - @param {String} [setup.url='current website'] The website url - @return {Promise} resolve() then checked in is done, reject(error) otherwise - - @example - // with Promise - $SP().checkin({ - destination:"http://mysite/Shared Documents/myfile.txt", - comments:"Automatic check in with SharepointPlus" - }).then(function() { - alert("Done"); - }).catch(function(error) { - alert("Check in failed") - }) - */ - checkin:function(setup) { - // default values - var _this=this; - return _this._promise(function(prom_resolve, prom_reject) { - setup = setup || {}; - if (!setup.destination) throw "[SharepointPlus 'checkin'] the file destination path is required."; - if (_this.url && !setup.url) setup.url=_this.url; - if (!setup.url) { - _this.getURL() - .then(function(url) { setup.url=url; return _this.checkin(setup) }) - .then(function(res) { prom_resolve(res) }) - .catch(function(rej) { prom_reject(rej) }) - return; - } - setup.comments = (setup.comments || "").replace(/&/g,"&"); - _this.ajax({ - url: setup.url + "/_vti_bin/Lists.asmx", - body:_this._buildBodyForSOAP("CheckInFile", ''+setup.destination+''+setup.comments+'1'), - headers:{'SOAPAction':'http://schemas.microsoft.com/sharepoint/soap/CheckInFile'} - }).then(function(data) { - var res = data.getElementsByTagName('CheckInFileResult'); - res = (res.length>0 ? res[0] : null); - if (res && res.firstChild.nodeValue != "true") { - prom_reject(res); - } else { - prom_resolve(); - } - }, function(err) { prom_reject(err) }); - }) - }, - /** - @name $SP().list.addAttachment - @function - @description Add an attachment to a Sharepoint List Item - - @param {Object} setup Options (see below) - @param {Number} setup.ID The item ID to attach the file - @param {String} setup.filename The name of the file - @param {String} setup.attachment An array buffer of the file content - @return {Promise} resolve(fileURL), reject() - - @example - $SP().list("My List").addAttachment({ - ID:1, - filename:"helloworld.txt", - attachment:"*ArrayBuffer*" - }).then(function(fileURL) { - alert(fileURL) - }); - */ - addAttachment:function(setup) { - var _this=this; - return _this._promise(function(prom_resolve, prom_reject) { - // check if we need to queue it - if (_this.needQueue) { return _this._addInQueue('addAttachment',arguments) } - if (arguments.length===0) throw "[SharepointPlus 'addAttachment'] the arguments are mandatory."; - if (!_this.listID) throw "[SharepointPlus 'addAttachment'] the list ID/Name is required."; - if (!_this.url) throw "[SharepointPlus 'addAttachment'] not able to find the URL!"; // we cannot determine the url - if (!setup.ID) throw "[SharepointPlus 'addAttachment'] the item ID is required."; - if (!setup.filename) throw "[SharepointPlus 'addAttachment'] the filename is required."; - if (!setup.attachment) throw "[SharepointPlus 'addAttachment'] the ArrayBuffer of the attachment's content is required."; - // avoid invalid characters - // eslint-disable-next-line - var filename = setup.filename.replace(/[\*\?\|\\/:"'<>#{}%~&]/g,"").replace(/^[\. ]+|[\. ]+$/g,"").replace(/ {2,}/g," ").replace(/\.{2,}/g,"."); - if (filename.length>=128) { - filename = filename.slice(0,115)+'__'+filename.slice(-8); - } - _this.ajax({ - url: _this.url + "/_vti_bin/Lists.asmx", - body: _this._buildBodyForSOAP("AddAttachment", ""+_this.listID+""+setup.ID+""+filename+""+SPArrayBufferToBase64(setup.attachment)+""), - headers:{'SOAPAction': 'http://schemas.microsoft.com/sharepoint/soap/AddAttachment' } - }) - .then(function(data) { - var res = data.getElementsByTagName('AddAttachmentResult'); - res = (res.length>0 ? res[0] : null); - var fileURL = ""; - if (res) fileURL = _this.url + "/" + res.firstChild.nodeValue; - if (!fileURL) prom_reject(res); - else prom_resolve(fileURL); - }) - .catch(function(error) { prom_reject(error) }); - }) - }, - /** - @name $SP().list.getAttachment - @function - @description Get the attachment(s) for an item - - @param {String|Number} itemID The item ID - @return {Promise} resolve([results]) - - @example - $SP().list("My List","http://my.site.com/mydir/").getAttachment(1).then(function(attachments) { - for (var i=0; i<attachments.length; i++) console.log(attachments[i]); -> "https://my.site.com/site/Lists/Something/Attachments/46/helloworld.txt" - }); - - // you can also use $SP().list().get() using the "Attachments" field - */ - getAttachment:function(itemID) { - var _this=this; - return _this._promise(function(prom_resolve, prom_reject) { - // check if we need to queue it - if (_this.needQueue) { return _this._addInQueue('getAttachment',arguments) } - if (!_this.listID) throw "[SharepointPlus 'getAttachment']: the list ID/Name is required"; - if (!_this.url) throw "[SharepointPlus 'getAttachment']: not able to find the URL!"; // we cannot determine the url - // do the request - _this.ajax({ - url: _this.url + "/_vti_bin/lists.asmx", - body: _this._buildBodyForSOAP("GetAttachmentCollection", ""+_this.listID+""+itemID+""), - headers:{'SOAPAction':'http://schemas.microsoft.com/sharepoint/soap/GetAttachmentCollection'} - }).then(function(data) { - var aReturn = [], i=0, a = data.getElementsByTagName('Attachment'); - for (; i < a.length; i++) aReturn.push(a[i].firstChild.nodeValue); - prom_resolve(aReturn) - }, function(err) { prom_reject(err) }); - }) - }, - /** - @name $SP().list.getContentTypes - @function - @description Get the Content Types for the list (returns Name, ID and Description) - - @param {Object} [options] - @param {Boolean} [options.cache=true] Do we want to use the cache on recall for this function? - @return {Promise} resolve(contentTypes), reject(error) - - @example - $SP().list("List Name").getContentTypes().then(function(contentTypes) { - for (var i=0; i<contentTypes.length; i++) console.log(contentTypes[i].Name, contentTypes[i].ID, contentTypes[i].Description); - }); - */ - getContentTypes:function(options) { - var _this=this; - return _this._promise(function(prom_resolve, prom_reject) { - // check if we need to queue it - if (_this.needQueue) { return _this._addInQueue('getContentTypes',arguments) } - if (!_this.listID) throw "[SharepointPlus 'getContentTypes'] the list ID/name is required."; - // default values - if (!_this.url) throw "[SharepointPlus 'getContentTypes'] not able to find the URL!"; // we cannot determine the url - - // check the Cache - options=options||{cache:true}; - if (options.cache) { - for (var i=0; i<_SP_CACHE_CONTENTTYPES.length; i++) { - if (_SP_CACHE_CONTENTTYPES[i].list === _this.listID && _SP_CACHE_CONTENTTYPES[i].url === _this.url) { - prom_resolve(_SP_CACHE_CONTENTTYPES[i].contentTypes); - return; - } - } - } - - // do the request - _this.ajax({ - url: _this.url + "/_vti_bin/lists.asmx", - body: _this._buildBodyForSOAP("GetListContentTypes", ''+_this.listID+''), - headers:{'SOAPAction':'http://schemas.microsoft.com/sharepoint/soap/GetListContentTypes'} - }).then(function(data) { - var arr = data.getElementsByTagName('ContentType'), ID, i=0, aReturn = []; - for (; i < arr.length; i++) { - ID = arr[i].getAttribute("ID"); - if (ID) { - aReturn.push({ - "ID":ID, - "Name":arr[i].getAttribute("Name"), - "Description":arr[i].getAttribute("Description") - }); - } - } - // we cache the result - _SP_CACHE_CONTENTTYPES.push({"list":_this.listID, "url":_this.url, "contentTypes":aReturn}); - prom_resolve(aReturn); - }, function(err) { prom_reject(err) }); - }) - }, - /** - @name $SP().list.getContentTypeInfo - @function - @description Get the Content Type Info for a Content Type into the list - - @param {String} contentType The Name or the ID (from $SP().list.getContentTypes) of the Content Type - @param {Object} [options] - @param {Boolean} [options.cache=true] Do we use the cache? - @return {Promise} resolve(fields), reject(error) - - @example - $SP().list("List Name").getContentTypeInfo("Item").then(function(fields) { - for (var i=0; i<fields.length; i++) console.log(fields[i]["DisplayName"]+ ": "+fields[i]["Description"]); - }); - - $SP().list("List Name").getContentTypeInfo("0x01009C5212B2D8FF564EBE4873A01C57D0F9001").then(function(fields) { - for (var i=0; i<fields.length; i++) console.log(fields[i]["DisplayName"]+ ": "+fields[i]["Description"]); - }); - */ - getContentTypeInfo:function(contentType, options) { - var _this=this; - return _this._promise(function(prom_resolve, prom_reject) { - // check if we need to queue it - if (_this.needQueue) { return _this._addInQueue('getContentTypeInfo',arguments) } - if (!_this.listID) throw "[SharepointPlus 'getContentTypeInfo'] the list ID/Name is required."; - if (arguments.length >= 1 && typeof contentType !== "string") throw "[SharepointPlus 'getContentTypeInfo'] the Content Type Name/ID is required."; - // default values - if (!_this.url) throw "[SharepointPlus 'getContentTypeInfo'] not able to find the URL!"; // we cannot determine the url - options=options||{cache:true} - - // look at the cache - if (options.cache) { - for (var i=0; i<_SP_CACHE_CONTENTTYPE.length; i++) { - if (_SP_CACHE_CONTENTTYPE[i].list === _this.listID && _SP_CACHE_CONTENTTYPE[i].url === _this.url && _SP_CACHE_CONTENTTYPE[i].contentType === contentType) { - prom_resolve(_SP_CACHE_CONTENTTYPE[i].info); - } - } - } - - // do we have a Content Type Name or ID ? - if (contentType.slice(0,2) !== "0x") { - // it's a Name so get the related ID using $SP.list.getContentTypes - _this.getContentTypes(options) - .then(function(types) { - var found=false; - for (var i=types.length; i--;) { - if (types[i]["Name"]===contentType) { - _this.getContentTypeInfo(types[i]["ID"], options).then(function(res) { prom_resolve(res) }, function(rej) { prom_reject(rej) }); - found=true; - break; - } - } - if (!found) throw "[SharepointPlus 'getContentTypeInfo'] not able to find the Content Type called '"+contentType+"' at "+_this.url; - }) - .catch(function(err) { - prom_reject(err) - }) - return; - } - - // do the request - _this.ajax({ - url: _this.url + "/_vti_bin/lists.asmx", - body: _this._buildBodyForSOAP("GetListContentType", ''+_this.listID+''+contentType+''), - headers:{'SOAPAction':'http://schemas.microsoft.com/sharepoint/soap/GetListContentType'} - }).then(function(data) { - var aReturn = [], i, j, a, r, k, q, arr = data.getElementsByTagName('Field'), index = 0, aIndex, attributes, attrName, lenDefault, attrValue, nodeDefault; - for (i=0; i < arr.length; i++) { - if (arr[i].getAttribute("ID")) { - aReturn[index] = []; - aIndex=aReturn[index]; - attributes=arr[i].attributes; - for (j=attributes.length; j--;) { - attrName=attributes[j].nodeName; - attrValue=attributes[j].nodeValue; - if (attrName==="Type") { - switch (attrValue) { - case "Choice": - case "MultiChoice": { - aIndex["FillInChoice"] = arr[i].getAttribute("FillInChoice"); - a=arr[i].getElementsByTagName("CHOICE"); - r=[]; - for(k=0; k0) { - nodeDefault=arr[i].getElementsByTagName("Default"); - aReturn[index]["DefaultValue"]=[]; - for (q=0; q ",infos[i]); - // for list's details: - console.log(infos._List) - }); - */ - info:function() { - var _this=this; - return _this._promise(function(prom_resolve, prom_reject) { - // check if we need to queue it - if (_this.needQueue) { return _this._addInQueue('info',arguments) } - if (!_this.listID) throw "[SharepointPlus 'info'] the list ID/Name is required."; - if (!_this.url) throw "[SharepointPlus 'info'] not able to find the URL!"; // we cannot determine the url - - // do the request - _this.ajax({ - url: _this.url + "/_vti_bin/lists.asmx", - body: _this._buildBodyForSOAP("GetList", ''+_this.listID+''), - headers:{'SOAPAction':'http://schemas.microsoft.com/sharepoint/soap/GetList'} - }).then(function(data) { - var aReturn = [], arr = data.getElementsByTagName('Field'), index = 0, aIndex, attributes, attrName, attrValue, lenDefault, nodeDefault,i,j,a,r,k,nName,nValue; - // retrieve list info first - var listDetails = data.getElementsByTagName('List'); - listDetails = (listDetails.length>0 ? listDetails[0] : null); - attributes=listDetails.attributes; - aReturn["_List"]={}; - for (i=0; i0) aIndex["Property"][nName[0].firstChild.nodeValue]=(nValue.length>0?nValue[0].firstChild.nodeValue:null); - } - break; - } - default: - aIndex["Choices"] = []; - } - } - aIndex[attrName]= attrValue; - } - - // find the default values - lenDefault=arr[i].getElementsByTagName("Default").length; - if (lenDefault>0) { - nodeDefault=arr[i].getElementsByTagName("Default"); - aReturn[index]["DefaultValue"]=[]; - for (var q=0; q'+_this.listID+''+viewID+''), - headers:{'SOAPAction':'http://schemas.microsoft.com/sharepoint/soap/GetView'} - }).then(function(data) { - var node=data.getElementsByTagName('View'), i, where; - node = (node.length>0 ? node[0] : null); - var oReturn = {DefaultView:(node.getAttribute("DefaultView")=="TRUE"), Name:node.getAttribute("DisplayName"), ID:viewID, Type:node.getAttribute("Type"), Url:node.getAttribute("Url"), OrderBy:[], Fields:[], RowLimit:"", WhereCAML:"", Node:node}; - var arr = data.getElementsByTagName('ViewFields')[0].getElementsByTagName('FieldRef'); - // find fields - for ( i=0; i < arr.length; i++) oReturn.Fields.push(arr[i].getAttribute("Name")); - // find orderby - arr = data.getElementsByTagName('OrderBy'); - arr = (arr.length>0 ? arr[0] : null); - if (arr) { - arr = arr.getElementsByTagName('FieldRef'); - for (i=0; i0 ? where[0] : null); - if (where) { - where=where.xml || (new XMLSerializer()).serializeToString(where); - where=where.match(/]+>(.*)<\/Where>/); - if(where.length==2) oReturn.WhereCAML=where[1]; - } - - // cache the data - found=false; - _SP_CACHE_SAVEDVIEW.forEach(function(c) { - if (c.url===_this.url && c.list===list && (c.viewID===viewID || c.viewName===viewID)) { - c.data=oReturn; - found=true; - } - }) - if (!found) _SP_CACHE_SAVEDVIEW.push({url:_this.url,list:_this.listID,data:oReturn,viewID:viewID,viewName:oReturn.Name}); - prom_resolve(oReturn); - }, function(error) { prom_reject(error) }) - }) - }, - /** - @name $SP().list.views - @function - @description Get the views' info for a List - - @param {Hash} [options] - @param {Boolean} [options.cache=true] Get the info from the cache - @return {Promise} resolve({DefaultView, Name, ID, Type, Url}), reject(error) - - @example - $SP().list("My List").views().then(function(view) { - for (var i=0; i<view.length; i++) { - console.log("View #"+i+": "+view[i].Name); - } - }); - - */ - views:function(options) { - var _this=this; - return _this._promise(function(prom_resolve, prom_reject) { - // check if we need to queue it - if (_this.needQueue) { return _this._addInQueue('views',arguments) } - if (!_this.listID) throw "[SharepointPlus 'views'] the list ID/Name is required."; - options = options||{}; - options.cache = (options.cache === false ? false : true); - - // default values - if (!_this.url) throw "[SharepointPlus 'views'] not able to find the URL!"; // we cannot determine the url - - // check the cache - var found=false; - if (options.cache) { - _SP_CACHE_SAVEDVIEWS.forEach(function(c) { - if (c.url===_this.url && c.listID === _this.listID) { - found=true; - prom_resolve(c.data); - } - }) - if (found) return; - } - - // do the request - _this.ajax({ - url: _this.url + "/_vti_bin/Views.asmx", - body: _this._buildBodyForSOAP("GetViewCollection", ''+_this.listID+''), - headers:{'SOAPAction':'http://schemas.microsoft.com/sharepoint/soap/GetViewCollection'} - }).then(function(data) { - var aReturn = [], arr = data.getElementsByTagName('View'), i=0, found=false; - for (; i < arr.length; i++) { - aReturn[i] = { - ID: arr[i].getAttribute("Name"), - Name: arr[i].getAttribute("DisplayName"), - Url: arr[i].getAttribute("Url"), - DefaultView:(arr[i].getAttribute("DefaultView")=="TRUE"), - Type:arr[i].getAttribute("Type"), - Node: arr[i] - } - } - - // scache - _SP_CACHE_SAVEDVIEWS.forEach(function(c) { - if (c.url===_this.url && c.listID === _this.listID) { - c.data=aReturn; - found=true; - } - }) - if (!found) _SP_CACHE_SAVEDVIEWS.push({url:_this.url,listID:_this.listID,data:aReturn}); - prom_resolve(aReturn); - }, function(error) { prom_reject(error) }); - }) - }, - /** - @name $SP().lists - @function - @description Get all the lists from the site - - @param {Object} [setup] Options (see below) - @param {String} [setup.url='current website'] The website url - @param {Boolean} [setup.cache=true] To get the result from the cache when available - @return {Promise} resolve({ID, Name, Description, Url, .....}), reject(error) - - @example - $SP().lists().then(function(lists) { - for (var i=0; i<lists.length; i++) console.log("List #"+i+": "+lists[i].Name); - }); - */ - lists:function(setup) { - var _this=this; - return _this._promise(function(prom_resolve, prom_reject) { - // default values - setup = setup || {}; - // if we didn't define the url in the parameters, then we need to find it - if (!setup.url) { - _this.getURL() - .then(function(url) { setup.url=url; return _this.lists(setup) }) - .then(function(res) { prom_resolve(res) }) - .catch(function(rej) { prom_reject(rej) }); - return; - } - setup.cache=(setup.cache===false?false:true); - - // check cache - var found=false; - if (setup.cache) { - _SP_CACHE_SAVEDLISTS.forEach(function(c) { - if (c.url===setup.url) { - found=true; - prom_resolve(c.data) - } - }) - if (found) return; - } - - // do the request - _this.ajax({ - url:setup.url + "/_vti_bin/lists.asmx", - body:_this._buildBodyForSOAP("GetListCollection", ""), - headers:{'SOAPAction':'http://schemas.microsoft.com/sharepoint/soap/GetListCollection'} - }) - .then(function(data) { - var aReturn = [], arr = data.getElementsByTagName('List'), i, j, attributes; - for (i=0; i < arr.length; i++) { - aReturn[i]={}; - attributes=arr[i].attributes; - for (j=attributes.length; j--;) aReturn[i][attributes[j].nodeName]=attributes[j].nodeValue; - aReturn[i].Url=arr[i].getAttribute("DefaultViewUrl") - aReturn[i].Name=arr[i].getAttribute("Title") - } - - // cache - found=false; - if (setup.cache) { - _SP_CACHE_SAVEDLISTS.forEach(function(c) { - if (c.url===setup.url) found=true; - }) - } - if (!found) _SP_CACHE_SAVEDLISTS.push({url:setup.url,data:aReturn}); - prom_resolve(aReturn); - }) - .catch(function(error) { prom_reject(error) }) - }) - }, - /** - @name $SP().list.add - @function - @description Add items into a Sharepoint List - note: A Date must be provided as "YYYY-MM-DD" (only date comparison) or "YYYY-MM-DD hh:mm:ss" (date AND time comparison), or you can use $SP().toSPDate(new Date()) - note: A person must be provided as "-1;#email" (e.g. "-1;#foo@bar.com") OR NT login with double \ (eg "-1;#europe\\foo_bar") OR the user ID as a number - note SP2013: If "-1;#" doesn't work on Sharepoint 2013, then try with "i:0#.w|" (e.g. "i:0#.w|europe\\foo_bar") ("i:0#.w|" may vary based on your authentification system -- see https://social.technet.microsoft.com/wiki/contents/articles/13921.sharepoint-20102013-claims-encoding.aspx) - note: A lookup value must be provided as "X;#value", with X the ID of the value from the lookup list. - --> it should also be possible to not pass the value but only the ID, e.g.: "X;#" or the ID as a number without anything else - note: A URL field must be provided as "http://www.website.com, Name" - note: A multiple selection must be provided as ";#choice 1;#choice 2;#", or just pass an array as the value and it will do the trick - note: A multiple selection of Lookup must be provided as ";#X;#Choice 1;#Y;#Choice 2;#" (with X the ID for "Choice 1", and "Y" for "Choice 2") - --> it should also be possible to not pass the values but only the ID, e.g.: ";#X;#;#Y;#;#" - note: A Yes/No checkbox must be provided as "1" (for TRUE) or "0" (for "False") - note: A Term / Taxonomy / Managed Metadata field must be provided as "0;#|UniqueIdentifier" for the special hidden related column (see https://github.com/Aymkdn/SharepointPlus/wiki/ to know more) - note: You cannot change the Approval Status when adding, you need to use the $SP().moderate function - - @param {Object|Array} items List of items (e.g. [{Field_x0020_Name: "Value", OtherField: "new value"}, {Field_x0020_Name: "Value2", OtherField: "new value2"}]) - @param {Object} [options] Options (see below) - @param {Number} [options.packetsize=15] If you have too many items to add, then we use `packetsize` to cut them into several requests (because Sharepoint cannot handle too many items at once) - @param {Function} [options.progress] (current,max) If you provide more than 'packetsize' items then they will be treated by packets and you can use "progress" to know more about the steps - @param {Boolean} [options.escapeChar=true] Determines if we want to escape the special chars that will cause an error (for example '&' will be automatically converted to '&amp;') - @param {String} [options.rootFolder=''] When dealing with Discussion Board you need to provide the rootFolder of the Message when you post a reply - @return {Promise} resolve({passed, failed}), reject(error) - - @example - $SP().list("My List").add({Title:"Ok"}); - - $SP().list("List Name").add([{Title:"Ok"}, {Title:"Good"}]).then(function(items) { alert("Done!"); }); - - $SP().list("My List","http://my.sharepoi.nt/dir/").add({Title:"Ok"}).then(function(items) { - if (items.failed.length > 0) { - for (var i=0; i < items.failed.length; i++) console.log("Error '"+items.failed[i].errorMessage+"' with:"+items.failed[i].Title); // the 'errorMessage' attribute is added to the object - } - if (items.passed.length > 0) { - for (var i=0; i < items.passed.length; i++) console.log("Success for:"+items.passed[i].Title+" (ID:"+items.passed[i].ID+")"); - } - }); - - // different ways to add John and Tom into the table - $SP().list("List Name").add({Title:"John is the Tom's Manager",Manager:"-1;#john@compagny.com",Report:"-1;#tom@compagny.com"}); // if you don't know the ID - $SP().list("My List").add({Title:"John is the Tom's Manager",Manager:"157",Report:"874"}); // if you know the Lookup ID - - // Calendar events: - // - you must define "EventDate" and "EndDate" - // - for a full day event you have to define "fAllDayEvent" to "1" - // - for recurrent events you have to define "RecurrenceData" and you can either provide an already formatted XML string or the recurrence object that will then be converted with $SP().parseRecurrence() - // EXAMPLE: event occurs every week on monday and friday, until December 31, 2019, between 10am and 11am, starting from December 20, 2018 - // EventDate is the StartTime and must be the same as EndDate (except for the Time) - $SP().list("My Calendar").add({ - Title:"Team Meeting", - EventDate:"2018-12-20 10:00:00", // the Date part is when the recurrent event starts, and the Time part will be used for each event - EndDate:"2019-12-31 11:00:00", // it must be the last Date/Time for the meeting, then the Time is used as an end time for each event - RecurrenceData: { - "type":"weekly", - "frequency":1, - "on":{ - "monday":true, - "friday":true - }, - "endDate":new Date(2019,11,31,11,0,0) // December 31, 2019, at 11am - } - }) - - // for Discussion Board, please refer to https://github.com/Aymkdn/SharepointPlus/wiki/Sharepoint-Discussion-Board - */ - add:function(items, options) { - var _this=this; - return _this._promise(function(prom_resolve, prom_reject) { - // check if we need to queue it - if (_this.needQueue) { return _this._addInQueue('add',arguments) } - if (!_this.listID) throw "[SharepointPlus 'add'] the list ID/Name is required."; - if (!_this.url) throw "[SharepointPlus 'add'] not able to find the URL!"; // we cannot determine the url - - // default values - var setup={}; - SPExtend(true, setup, options); - setup.escapeChar = (setup.escapeChar == undefined) ? true : setup.escapeChar; - setup.progress= setup.progress || function(){}; - setup.packetsize=setup.packetsize||15; - setup.rootFolder=setup.rootFolder||""; - - if (!Array.isArray(items)) items = [ items ]; - - var itemsLength=items.length, nextPacket, cutted, itemKey, itemValue, it, i; - // define current and max for the progress - setup.progressVar = setup.progressVar || {current:0,max:itemsLength,passed:[],failed:[],eventID:"spAdd"+(""+Math.random()).slice(2)}; - // we cannot add more than 15 items in the same time, so split by 15 elements - // and also to avoid surcharging the server - if (itemsLength > setup.packetsize) { - nextPacket=items.slice(0); - cutted=nextPacket.splice(0,setup.packetsize); - _SP_ADD_PROGRESSVAR[setup.progressVar.eventID] = function(setup) { - return _this.add(nextPacket,setup); - }; - items = cutted; - itemsLength = items.length; - } else if (itemsLength === 0) { - setup.progress(1,1); - prom_resolve({passed:[], failed:[]}) - return; - } - - // increment the progress - setup.progressVar.current += itemsLength; - - // build a part of the request - var updates = ''; - for (i=0; i < items.length; i++) { - updates += ''; - updates += 'New'; - for (it in items[i]) { - if (items[i].hasOwnProperty(it)) { - itemKey = it; - itemValue = items[i][it]; - if (Array.isArray(itemValue)) { - if (itemValue.length === 0) itemValue=''; - else itemValue = ";#" + itemValue.join(";#") + ";#"; // an array should be seperate by ";#" - } - - switch (itemKey) { - case "RecurrenceData": { - // if we have RecurrenceData, and if it's an object, then we convert it - if (typeof itemValue === 'object') itemValue = _this.parseRecurrence(itemValue); - // add additional fields - // see https://fatalfrenchy.wordpress.com/2010/07/16/sharepoint-recurrence-data-schema/ - // and https://stackoverflow.com/a/44487221/1134119 - if (typeof items[i]['fRecurrence'] === 'undefined') updates += "1"; - if (typeof items[i]['EventType'] === 'undefined') updates += "1"; - if (typeof items[i]['UID'] === 'undefined') updates += "{"+_this.newGuid()+"}"; - if (typeof items[i]['fAllDayEvent'] === 'undefined') updates += "0"; - if (typeof items[i]['TimeZone'] === 'undefined') { - // to avoid any issues, the TimeZone is required - if (_SP_CACHE_TIMEZONEINFO[_this.url]) updates += ""+_SP_CACHE_TIMEZONEINFO[_this.url].ID+""; - else { - return _this.getTimeZoneInfo({url:_this.url}) - .then(function(info) { - _SP_CACHE_TIMEZONEINFO[_this.url]=info; - return _this.add(items, options) - }) - .then(function(res) { - prom_resolve(res) - }) - .catch(function(err) { - prom_reject(err) - }) - } - } - //if (typeof items[i]['XMLTZone'] === 'undefined') updates += "0]]>"; - //if (typeof items[i]['WorkspaceLink'] === 'undefined') updates += "0"; - // we also define the duration - /*if (!items[i]['Duration']) { - var startTime = items[i]['EventDate'].match(/[ T]([0-9]+):([0-9]+):[0-9]+/); - var endTime = items[i]['EndDate'].match(/[ T]([0-9]+):([0-9]+):[0-9]+/); - if (startTime && endTime && startTime.length===3 && endTime.length===3) { - var duration = (new Date(1981,0,19,endTime[1],endTime[2]) - new Date(1981,0,19,startTime[1],startTime[2])) / 1000; - if (duration>0) updates += ""+duration+""; - } - }*/ - updates += ""; - break; - } - default:{ - if (typeof itemValue === 'boolean') itemValue = (itemValue ? '1' : '0'); - if (setup.escapeChar && typeof itemValue === "string") itemValue = _this._cleanString(itemValue); // replace & (and not &) by "&" to avoid some issues - updates += ""+itemValue+""; - } - } - } - } - updates += ''; - } - updates += ''; - - // send the request - _this.ajax({ - url:_this.url + "/_vti_bin/lists.asmx", - body:_this._buildBodyForSOAP("UpdateListItems", ""+_this.listID+"" + updates + ""), - headers:{'SOAPAction':'http://schemas.microsoft.com/sharepoint/soap/UpdateListItems'} - }).then(function(data) { - var result = data.getElementsByTagName('Result'), len=result.length, passed = setup.progressVar.passed, failed = setup.progressVar.failed, i, rows; - for (i=0; i < len; i++) { - if (result[i].getElementsByTagName('ErrorCode')[0].firstChild.nodeValue === "0x00000000") { // success - rows=result[i].getElementsByTagName('z:row'); - if (rows.length==0) rows=result[i].getElementsByTagName('row'); // for Chrome 'bug' - if (items[i]) { - items[i].ID = rows[0].getAttribute("ows_ID"); - items[i].raw = rows[0]; - passed.push(items[i]); - } - } else if (items[i]) { - items[i].errorMessage = result[i].getElementsByTagName('ErrorText')[0].firstChild.nodeValue; - failed.push(items[i]); - } - } - - setup.progress(setup.progressVar.current,setup.progressVar.max); - // check if we have some other packets that are waiting to be treated - if (setup.progressVar.current < setup.progressVar.max) { - if (_SP_ADD_PROGRESSVAR[setup.progressVar.eventID]) { - _SP_ADD_PROGRESSVAR[setup.progressVar.eventID](setup).then(function(res) { prom_resolve(res) }, function(rej) { prom_reject(rej) }) - } - } else { - if (_SP_ADD_PROGRESSVAR[setup.progressVar.eventID]) delete _SP_ADD_PROGRESSVAR[setup.progressVar.eventID]; - prom_resolve({passed:passed, failed:failed}); - } - }, function(rej) { prom_reject(rej) }); - }) - }, - /** - @name $SP().list.update - @function - @description Update items from a Sharepoint List - - @param {Array} items List of items (e.g. [{ID: 1, Field_x0020_Name: "Value", OtherField: "new value"}, {ID:22, Field_x0020_Name: "Value2", OtherField: "new value2"}]) - @param {Object} [options] Options (see below) - @param {String} [options.where=""] You can define a WHERE clause - @param {Number} [options.packetsize=15] If you have too many items to update, then we use `packetsize` to cut them into several requests (because Sharepoint cannot handle too many items at once) - @param {Function} [options.progress] Two parameters: 'current' and 'max' -- if you provide more than 'packetsize' ID then they will be treated by packets and you can use "progress" to know more about the steps - @param {Boolean} [options.escapeChar=true] Determines if we want to escape the special chars that will cause an error (for example '&' will be automatically converted to '&amp;') - @param {String|Date} [options.event] If you want to create an exception occurrence for a recurrent event you must define the "event" option using the date of the occurence to change (see the below example) - @return {Promise} resolve({passed, failed}), reject(error) - - @example - $SP().list("My List").update({ID:1, Title:"Ok"}); - // if you use the WHERE then you must not provide the item ID: - $SP().list("List Name").update({Title:"Ok"},{where:"Status = 'Complete'"}); - - $SP().list("My List","http://sharepoint.org/mydir/").update([{ID:5, Title:"Ok"}, {ID: 15, Title:"Good"}]); - - $SP().list("List Name").update({ID:43, Title:"Ok"}).then(function(items) { - for (var i=0; i < items.failed.length; i++) console.log("Error '"+items.failed[i].errorMessage+"' with:"+items.failed[i].Title); - var len=items.passed.length; - console.log(len+(len>1?" items have been successfully added":" item has been successfully added")) - }); - - // For recurrent calendar events, you can edit one of the occurrence using the `event` option; it will procure a new separate event - // e.g. you have an event #1589 that occurs every weekday, from 9am to 10am, but you want to update the one on December 17, 2018 to be from 2pm to 3pm - $SP().list("Calendar").update({ - Title:'Special Team Meeting', // if you want to change the event's title - EventDate:$SP().toSPDate(new Date(2018,11,17,14,0,0), true), // the new start date for the meeting (2pm) - EndDate:$SP().toSPDate(new Date(2018,11,17,15,0,0), true) // the new end date for the meeting (3pm) - }, { - where:'ID = 1589', // the criteria that permits to identify your master recurrent event -- IT IS REQUIRED - event:new Date(2018,11,17) // date of the event that needs to be changed... if the event ID is "5274.0.2019-01-07T15:00:00Z", then you can use "2019-01-07T15:00:00Z" - }) - - // Note: if you update a complete serie for a recurrent event, then all the exception occurrences will be automatically deleted and replace with the full serie of events - */ - update:function(items, options) { - var _this=this; - return _this._promise(function(prom_resolve, prom_reject) { - // check if we need to queue it - if (_this.needQueue) { return _this._addInQueue('update',arguments) } - if (!_this.listID) return prom_reject("[SharepointPlus 'update'] the list ID/name is required."); - if (!_this.url) return prom_reject("[SharepointPlus 'update'] not able to find the URL!"); // we cannot determine the url - - // default values - var setup={}; - SPExtend(true, setup, options); - setup.where = setup.where || ""; - setup.escapeChar = (setup.escapeChar == undefined) ? true : setup.escapeChar; - setup.progress= setup.progress || function(){}; - setup.packetsize=setup.packetsize||15; - - if (!Array.isArray(items)) items = [ items ]; - var itemsLength=items.length, nextPacket, cutted, itemKey, itemValue, it, i; - - // if there is a WHERE clause - if (itemsLength === 1 && setup.where) { - // call GET first - delete items[0].ID; - var getFields = []; - var getParams = function() { - return new Promise(function(pr, pj) { - var params = {fields:["ID"],where:setup.where}; - if (!setup.event) pr(params); - // if we want to update an event - else { - params.calendar=true; - params.calendarOptions={referenceDate:setup.event} - // we also need all the columns for the event - _this.get({fields:'ContentType', where:setup.where}) - .then(function(data) { - if (data.length===0) return Promise.reject("[SharepointPlus 'update'] Unable to find an event with `"+setup.where+"`"); - return _this.getContentTypeInfo(data[0].getAttribute('ContentType') || 'Event'); - }) - .then(function(fields) { - fields.forEach(function(field) { - var fieldID = field.Name||field.StaticName; - if (fieldID === "TimeZone" || (field.Group !== '_Hidden' && field.ReadOnly !== 'TRUE' && field.Hidden !== 'TRUE' && typeof items[0][fieldID] === 'undefined')) { - params.fields.push(fieldID); - } - }); - - pr(params); - }) - .catch(function(err) { - pj(err) - }) - } - - }) - } - - getParams() - .then(function(params) { - getFields = params.fields.slice(0); - return _this.get(params) - }) - .then(function(data) { - // we need a function to clone the items - var clone = function(obj){ - var newObj = {}; - for (var k in obj) newObj[k]=obj[k]; - return newObj; - }; - var aItems=[], i=data.length, it; - if (!setup.event) { - while (i--) { - it=clone(items[0]); - it.ID=data[i].getAttribute("ID"); - aItems.push(it); - } - delete setup.where; - // now call again the UPDATE - return _this.update(aItems,setup) - } else { - // the treatment is different for `event` - var eventDate = (typeof setup.event !== 'string' ? _this.toSPDate(setup.event) : setup.event.slice(0,10)); - var event = data.filter(function(d) { - return d.getAttribute("ID").indexOf("."+eventDate+"T") !== -1; - }); - if (event.length===0) return Promise.reject("[SharepointPlus 'update'] No event found on "+eventDate); - event = event[0]; - - // see https://fatalfrenchy.wordpress.com/2010/07/16/sharepoint-recurrence-data-schema/ - it=clone(items[0]); - getFields.forEach(function(field) { - if (field!=='ID' && typeof it[field] === 'undefined') { - var val = event.getAttribute(field); - if (val !== undefined && val !== null) it[field] = val; - } - }); - it.MasterSeriesItemID = event.getAttribute("ID").split('.')[0]; - it.UID = event.getAttribute("UID"); - it.EventType = 4; - it.fRecurrence = 1; - it.RecurrenceData = "No Recurrence"; - it.RecurrenceID = event.getAttribute("RecurrenceID"); // the occurrence event date - it.TimeZone = event.getAttribute("TimeZone"); - return _this.add(it, setup); - } - }) - .then(function(res) { prom_resolve(res) }) - .catch(function(rej) { prom_reject(rej) }) - return - } - - // define current and max for the progress - setup.progressVar = setup.progressVar || {current:0,max:itemsLength,passed:[],failed:[],eventID:"spUpdate"+(""+Math.random()).slice(2)}; - // we cannot add more than 15 items in the same time, so split by 15 elements - // and also to avoid surcharging the server - if (itemsLength > setup.packetsize) { - nextPacket=items.slice(0); - cutted=nextPacket.splice(0,setup.packetsize); - _SP_UPDATE_PROGRESSVAR[setup.progressVar.eventID] = function(setup) { - return _this.update(nextPacket,setup); - }; - items = cutted; - itemsLength = items.length; - } else if (itemsLength == 0) { - prom_resolve({passed:[], failed:[]}); - return; - } - - // increment the progress - setup.progressVar.current += itemsLength; - - // build a part of the request - var updates = ''; - for (i=0; i < itemsLength; i++) { - updates += ''; - if (!items[i].ID) return prom_reject("[SharepointPlus 'update'] you have to provide the item ID called 'ID'"); - for (it in items[i]) { - if (items[i].hasOwnProperty(it)) { - itemKey = it; - itemValue = items[i][it]; - if (Array.isArray(itemValue)) { - if (itemValue.length===0) itemValue=''; - else itemValue = ";#" + itemValue.join(";#") + ";#"; // an array should be seperate by ";#" - } - - // if we have RecurrenceData, and if it's an object, then we convert it - if (itemKey === 'RecurrenceData') { - if (typeof itemValue === 'object') itemValue = _this.parseRecurrence(itemValue); - // add additional fields - if (!items[i]['RecurrenceID']) { - if (typeof items[i]['fRecurrence'] === 'undefined') updates += "1"; - if (typeof items[i]['EventType'] === 'undefined') updates += "1"; - if (typeof items[i]['UID'] === 'undefined') updates += "{"+_this.newGuid()+"}"; - if (typeof items[i]['fAllDayEvent'] === 'undefined') updates += "0"; - } - updates += ""; - } else { - if (typeof itemValue === 'boolean') itemValue = (itemValue ? '1' : '0'); - if (setup.escapeChar && typeof itemValue === "string") itemValue = _this._cleanString(itemValue); // replace & (and not &) by "&" to avoid some issues - updates += ""+itemValue+""; - } - } - } - updates += ''; - } - updates += ''; - - // send the request - _this.ajax({ - url:_this.url + "/_vti_bin/lists.asmx", - body:_this._buildBodyForSOAP("UpdateListItems", ""+_this.listID+"" + updates + ""), - headers:{'SOAPAction':'http://schemas.microsoft.com/sharepoint/soap/UpdateListItems'} - }) - .then(function(data) { - var result = data.getElementsByTagName('Result'), len=result.length, passed = setup.progressVar.passed, failed = setup.progressVar.failed, i; - for (i=0; i < len; i++) { - if (result[i].getElementsByTagName('ErrorCode')[0].firstChild.nodeValue === "0x00000000" && items[i]) {// success - var raw=result[i].getElementsByTagName('z:row'); - if (raw.length===0) raw=result[i].getElementsByTagName('row'); // for Chrome 'bug' - items[i].raw=raw[0]; - passed.push(items[i]); - } - else if (items[i]) { - items[i].errorMessage = result[i].getElementsByTagName('ErrorText')[0].firstChild.nodeValue; - failed.push(items[i]); - } - } - - setup.progress(setup.progressVar.current,setup.progressVar.max); - // check if we have some other packets that are waiting to be treated - if (setup.progressVar.current < setup.progressVar.max) { - if (_SP_UPDATE_PROGRESSVAR[setup.progressVar.eventID]) { - _SP_UPDATE_PROGRESSVAR[setup.progressVar.eventID](setup).then(function(res) { prom_resolve(res) }, function(rej) { prom_reject(rej) }) - } - } - else { - if (_SP_UPDATE_PROGRESSVAR[setup.progressVar.eventID]) delete _SP_UPDATE_PROGRESSVAR[setup.progressVar.eventID]; - prom_resolve({passed:passed, failed:failed}); - } - }, function(rej) { prom_reject(rej) }); - }) - }, - /** - @name $SP().list.history - @function - @description When versioning is an active option for your list, then you can use this function to find the previous values for a field - - @param {Object} params See below - @param {String|Number} params.ID The item ID - @param {String} params.Name The field name - @return {Promise} resolve(data), reject(error) - - @example - $SP().list("My List").history({ID:1981, Name:"Critical_x0020_Comments"}).then(function(data) { - for (var i=0,len=data.length; i<len; i++) { - console.log("Date: "+data[i].getAttribute("Modified")); // you can use $SP().toDate() to convert it to a JavaScript Date object - console.log("Editor: "+data[i].getAttribute("Editor")); // it's the long format type, so the result looks like that "328;#Doe,, John,#DOMAIN\john_doe,#John_Doe@example.com,#,#Doe,, John" - console.log("Content: "+data[i].getAttribute("Critical_x0020_Comments")); // use the field name here - } - }); - */ - history:function(params) { - var _this=this; - return _this._promise(function(prom_resolve, prom_reject) { - // check if we need to queue it - if (_this.needQueue) { return _this._addInQueue('history',arguments) } - if (!_this.listID) throw "[SharepointPlus 'history'] the list ID/Name is required."; - params=params||{}; - if (!params.ID || !params.Name) throw "[SharepointPlus 'history'] you must provide the item ID and field Name."; - - // send the request - _this.ajax({ - url:_this.url + "/_vti_bin/lists.asmx", - body:_this._buildBodyForSOAP("GetVersionCollection", ""+_this.listID+""+params.ID+""+params.Name+""), - headers:{'SOAPAction':'http://schemas.microsoft.com/sharepoint/soap/GetVersionCollection'} - }) - .then(function(data) { - prom_resolve(data.getElementsByTagName('Version')) - }, function(error) { prom_reject(error) }) - }) - }, - /** - @name $SP().list.moderate - @function - @description Moderate items from a Sharepoint List - - @param {Array} approval List of items and ApprovalStatus (e.g. [{ID:1, ApprovalStatus:"Approved"}, {ID:22, ApprovalStatus:"Pending"}]) - @param {Object} [setup] Options (see below) - @param {Number} [setup.packetsize=15] If you have too many items to moderate, then we use `packetsize` to cut them into several requests (because Sharepoint cannot handle too many items at once) - @param {Function} [setup.progress] Two parameters: 'current' and 'max' -- if you provide more than `packetsize` ID then they will be treated by packets and you can use "progress" to know more about the steps - @return {Promise} resolve({passed, failed}), reject(error) - - @example - $SP().list("My List").moderate({ID:1, ApprovalStatus:"Rejected"}); // you must always provide the ID - - $SP().list("Other List").moderate([{ID:5, ApprovalStatus:"Pending"}, {ID: 15, ApprovalStatus:"Approved"}]).then(function(items) { - for (var i=0; i < items.failed.length; i++) console.log("Error with:"+items.failed[i].ID); - for (var i=0; i < items.passed.length; i++) console.log("Success with:"+items.passed[i].getAttribute("Title")); - }); - */ - moderate:function(items, setup) { - var _this=this; - return _this._promise(function(prom_resolve, prom_reject) { - // check if we need to queue it - if (_this.needQueue) { return _this._addInQueue('moderate',arguments) } - if (!_this.listID) throw "[SharepointPlus 'moderate'] the list ID/Name is required."; - - // default values - setup = setup || {}; - if (!_this.url) throw "[SharepointPlus 'moderate'] not able to find the URL!"; // we cannot determine the url - setup.progress= setup.progress || function(){}; - - if (!Array.isArray(items)) items = [ items ]; - var itemsLength=items.length, nextPacket, cutted, itemKey, itemValue, it, i; - - // define current and max for the progress - setup.progressVar = setup.progressVar || {current:0,max:itemsLength,passed:[],failed:[],eventID:"spModerate"+(""+Math.random()).slice(2)}; - // we cannot add more than 15 items in the same time, so split by 15 elements - // and also to avoid surcharging the server - if (itemsLength > 15) { - nextPacket=items.slice(0); - cutted=nextPacket.splice(0,15); - _SP_MODERATE_PROGRESSVAR[setup.progressVar.eventID] = function(setup) { - return _this.moderate(nextPacket,setup); - }; - _this.moderate(cutted,setup); - return; - } else if (itemsLength === 0) { - setup.after({passed:[], failed:[]}); - return; - } - - // increment the progress - setup.progressVar.current += itemsLength; - - // build a part of the request - var updates = ''; - for (i=0; i < itemsLength; i++) { - updates += ''; - if (!items[i].ID) throw "[SharepointPlus 'moderate'] you have to provide the item ID called 'ID'"; - else if (typeof items[i].ApprovalStatus === "undefined") throw "[SharepointPlus 'moderate'] you have to provide the approval status 'ApprovalStatus' (Approved, Rejected, Pending, Draft or Scheduled)"; - for (it in items[i]) { - if (items[i].hasOwnProperty(it)) { - itemKey = it; - itemValue = items[i][it]; - if (itemKey == "ApprovalStatus") { - itemKey = "_ModerationStatus"; - switch (itemValue.toLowerCase()) { - case "approve": - case "approved": itemValue=0; break; - case "reject": - case "deny": - case "denied": - case "rejected": itemValue=1; break; - case "pending": itemValue=2; break; - case "draft": itemValue=3; break; - case "scheduled": itemValue=4; break; - default: itemValue=2; break; - } - } - } - updates += ""+itemValue+""; - } - updates += ''; - } - updates += ''; - - // send the request - _this.ajax({ - url:_this.url + "/_vti_bin/lists.asmx", - body:_this._buildBodyForSOAP("UpdateListItems", ""+_this.listID+"" + updates + ""), - headers:{'SOAPAction':'http://schemas.microsoft.com/sharepoint/soap/UpdateListItems'} - }).then(function(data) { - var result = data.getElementsByTagName('Result'), len=result.length, passed = setup.progressVar.passed, failed = setup.progressVar.failed, rows, i; - for (i=0; i < len; i++) { - rows=result[i].getElementsByTagName('z:row'); - if (rows.length==0) rows=data.getElementsByTagName('row'); // for Chrome - var item = myElem(rows[0]); - if (result[i].getElementsByTagName('ErrorCode')[0].firstChild.nodeValue == "0x00000000") // success - passed.push(item); - else { - items[i].errorMessage = result[i].getElementsByTagName('ErrorText')[0].firstChild.nodeValue; - failed.push(items[i]); - } - } - - setup.progress(setup.progressVar.current,setup.progressVar.max); - // check if we have some other packets that are waiting to be treated - if (setup.progressVar.current < setup.progressVar.max) { - if (_SP_MODERATE_PROGRESSVAR[setup.progressVar.eventID]) { - _SP_MODERATE_PROGRESSVAR[setup.progressVar.eventID](setup).then(function(res) { prom_resolve(res) }, function(rej) {prom_reject(rej) }); - } - } else { - if (_SP_MODERATE_PROGRESSVAR[setup.progressVar.eventID]) delete _SP_MODERATE_PROGRESSVAR[setup.progressVar.eventID]; - prom_resolve({passed:passed, failed:failed}) - } - }, function(rej) { prom_reject(rej) }); - }) - }, - /** - @name $SP().list.remove - @function - @description Delete items from a Sharepoint List - - @param {Objet|Array} [itemsID] List of items ID (e.g. [{ID:1}, {ID:22}]) | ATTENTION if you want to delete a file you have to add the "FileRef" e.g. {ID:2,FileRef:"path/to/the/file.ext"} - @param {Object} [options] Options (see below) - @param {String} [options.where] If you don't specify the itemsID (first param) then you have to use a `where` clause - it will search for the list of items ID based on the `where` and it will then delete all of them - @param {Number} [options.packetsize=15] If you have too many items to delete, then we use `packetsize` to cut them into several requests (because Sharepoint cannot handle too many items at once) - @param {Function} [options.progress] Two parameters: 'current' and 'max' -- If you provide more than 'packetsize' ID then they will be treated by packets and you can use "progress" to know more about the steps - @param {String|Date} [options.event] If you want to delete an occurrence of a recurrent event from a calendar (see the below example) - @return {Promise} resolve({passed, failed}), reject(error) - - @example - $SP().list("My List").remove({ID:1}); - // we can use the WHERE clause instead providing the ID - $SP().list("My List").remove({where:"Title = 'OK'",progress:function(current,max) { - console.log(current+"/"+max); - }}); - - // delete several items - $SP().list("List Name", "http://my.sharepoint.com/sub/dir/").remove([{ID:5}, {ID:7}]); - - $SP().list("List").remove({ID:43, Title:"My title"}).then(function(items) { - for (var i=0; i < items.failed.length; i++) console.log("Error with:"+items.failed[i].ID+" ("+items.failed[i].errorMessage+")"); // only .ID and .errorMessage are available - }); - - // example for deleting a file - $SP().list("My Shared Documents").remove({ID:4,FileRef:"site/subsite/My Shared Documents/something.xls"}); - // or use {where} - $SP().list("My Shared Documents").remove({where:"ID = 4"}); - - // if you want to delete one occurrence of a recurrent event you must use option "event" - // e.g. you have an event #1589 that occurs every weekday, from 9am to 10am, but you want to delete the one on December 17, 2018 - $SP().list("Calendar").remove({ - where:'ID = 1589', // the criteria that permits to identify your master recurrent event -- IT IS REQUIRED - event:new Date(2018,11,17) // date of the event that needs to be deleted, it can be the "RecurrenceID" - }) - */ - remove:function(items, options) { - var _this=this; - return _this._promise(function(prom_resolve, prom_reject) { - // check if we need to queue it - if (_this.needQueue) { return _this._addInQueue('remove',arguments) } - if (!_this.url) throw "[SharepointPlus 'remove'] not able to find the URL!"; // we cannot determine the url - // default values - if (!options && items.where) { options=items; items=[]; } // the case when we use the "where" - var setup={}; - SPExtend(true, setup, options); - setup.progress= setup.progress || (function() {}); - setup.packetsize = setup.packetsize || 15; - - if (!Array.isArray(items)) items = [ items ]; - var itemsLength=items.length, nextPacket, cutted, i; - - // if there is a WHERE clause - if (setup.where) { - // call GET first - if (itemsLength===1) delete items[0].ID; - var getParams = {fields:["ID","FileRef"],where:setup.where}; - // if we want to delete an event - if (setup.event) { - getParams.fields.push("Title"); - getParams.calendar=true; - getParams.calendarOptions={referenceDate:setup.event} - } - _this.get(getParams) - .then(function(data) { - // we need a function to clone the items - var clone = function(obj){ - var newObj = {}; - for (var k in obj) newObj[k]=obj[k]; - return newObj; - }; - var aItems=[],fileRef,i=data.length,it={}; - if (!setup.event) { - while (i--) { - it=clone(items[0]); - it.ID=data[i].getAttribute("ID"); - fileRef=data[i].getAttribute("FileRef"); - if (fileRef) it.FileRef=_this.cleanResult(fileRef); - aItems.push(it); - } - delete setup.where; - // now call again the REMOVE - return _this.remove(aItems,setup); - } else { - // the treatment is different for `event` - var eventDate = (typeof setup.event !== 'string' ? _this.toSPDate(setup.event) : setup.event.slice(0,10)); - var event = data.filter(function(d) { - return d.getAttribute("ID").indexOf("."+eventDate+"T") !== -1 || (d.getAttribute("RecurrenceID")||"").startsWith(eventDate); - }); - if (event.length===0) return Promise.reject("[SharepointPlus 'remove'] No event found on "+eventDate); - event = event[0]; - - // see https://fatalfrenchy.wordpress.com/2010/07/16/sharepoint-recurrence-data-schema/ - it.MasterSeriesItemID = event.getAttribute("ID").split('.')[0]; - it.UID = event.getAttribute("UID"); - it.EventType = 3; - it.fRecurrence = 1; - it.fAllDayEvent = event.getAttribute("fAllDayEvent")||"0"; - it.RecurrenceData = "No Recurrence"; - it.RecurrenceID = event.getAttribute("RecurrenceID"); // the occurrence event date - it.Title = "Deleted: " + event.getAttribute("Title")||""; - it.EventDate = event.getAttribute("EventDate"); - it.EndDate = event.getAttribute("EndDate"); - return _this.add(it, setup); - } - }) - .then(function(res) { prom_resolve(res) }) - .catch(function(rej) { prom_reject(rej) }) - return; - } else if (itemsLength === 0) { - // nothing to delete - prom_resolve({passed:[], failed:[]}); - return; - } - - // define current and max for the progress - setup.progressVar = setup.progressVar || {current:0,max:itemsLength,passed:[],failed:[],eventID:"spRemove"+(""+Math.random()).slice(2)}; - // we cannot add more than setup.packetsize items in the same time, so split by setup.packetsize elements - // and also to avoid surcharging the server - if (itemsLength > setup.packetsize) { - nextPacket=items.slice(0); - cutted=nextPacket.splice(0,setup.packetsize); - _SP_REMOVE_PROGRESSVAR[setup.progressVar.eventID] = function(setup) { - return _this.remove(nextPacket,setup); - }; - items = cutted; - itemsLength = items.length; - } - // increment the progress - setup.progressVar.current += itemsLength; - - // build a part of the request - var updates = ''; - for (i=0; i < items.length; i++) { - updates += ''; - if (items[i].ID == undefined) throw "Error 'delete': you have to provide the item ID called 'ID'"; - updates += ""+items[i].ID+""; - if (items[i].FileRef != undefined) updates += ""+items[i].FileRef+""; - updates += ''; - } - updates += ''; - - // send the request - _this.ajax({ - url:_this.url + "/_vti_bin/lists.asmx", - body:_this._buildBodyForSOAP("UpdateListItems", ""+_this.listID+"" + updates + ""), - headers:{'SOAPAction':'http://schemas.microsoft.com/sharepoint/soap/UpdateListItems'} - }).then(function(data) { - var result = data.getElementsByTagName('Result'), len=result.length, passed = setup.progressVar.passed, failed = setup.progressVar.failed, i; - for (i=0; i < len; i++) { - if (result[i].getElementsByTagName('ErrorCode')[0].firstChild.nodeValue === "0x00000000") // success - passed.push(items[i]); - else { - items[i].errorMessage = result[i].getElementsByTagName('ErrorText')[0].firstChild.nodeValue; - failed.push(items[i]); - } - } - - setup.progress(setup.progressVar.current,setup.progressVar.max); - // check if we have some other packets that are waiting to be treated - if (setup.progressVar.current < setup.progressVar.max) { - if (_SP_REMOVE_PROGRESSVAR[setup.progressVar.eventID]) { - if (setup.useCallback) _SP_REMOVE_PROGRESSVAR[setup.progressVar.eventID](setup); - else _SP_REMOVE_PROGRESSVAR[setup.progressVar.eventID](setup).then(function(res) { prom_resolve(res) }, function(rej) { prom_reject(rej) }) - } - } else { - if (_SP_REMOVE_PROGRESSVAR[setup.progressVar.eventID]) delete _SP_REMOVE_PROGRESSVAR[setup.progressVar.eventID]; - prom_resolve({passed:passed, failed:failed}); - } - }); - }) - }, - /** - @name $SP().usergroups - @function - @category people - @description Find the Sharepoint groups where the specified user is member of - - @param {String} username The username with the domain ("domain\\login" for Sharepoint 2010, or e.g. "i:0#.w|domain\\login" for Sharepoint 2013) - @param {Object} [setup] Options (see below) - @param {String} [setup.url='current website'] The website url - @param {Boolean} [setup.cache=true] Keep a cache of the result - @return {Promise} result(groups), reject(error) - - @example - $SP().usergroups("mydomain\\john_doe",{url:"http://my.si.te/subdir/"}).then(function(groups) { - for (var i=0; i < groups.length; i++) console.log(groups[i]); // -> "Roadmap Admin", "Global Viewers", ... - }); - */ - usergroups:function(username, setup) { - var _this=this; - return _this._promise(function(prom_resolve, prom_reject) { - // default values - if (!username) throw "[SharepointPlus 'usergroups']: the username is required."; - setup = setup || {}; - setup.cache = (setup.cache === false ? false : true); - if (!setup.url) { - _this.getURL() - .then(function(url) { return _this.usergroups(username, {url:url}) }) - .then(function(res) { prom_resolve(res) }) - .catch(function(rej) { prom_reject(rej) }) - return; - } - - username=username.toLowerCase(); - setup.url=setup.url.toLowerCase(); - // check the cache - // [ {user:"username", url:"url", data:"the groups"}, ... ] - var found=false; - if (setup.cache) { - _SP_CACHE_USERGROUPS.forEach(function(c) { - if (c.user === username && c.url === setup.url) { - prom_resolve(c.data); - found=true; - } - }) - } - if (found) return; - - // send the request - _this.ajax({ - url:setup.url + "/_vti_bin/usergroup.asmx", - body:_this._buildBodyForSOAP("GetGroupCollectionFromUser", ""+username+"", "http://schemas.microsoft.com/sharepoint/soap/directory/"), - headers:{'SOAPAction':'http://schemas.microsoft.com/sharepoint/soap/directory/GetGroupCollectionFromUser'} - }).then(function(data) { - var aResult=[]; - // get the details - data=data.getElementsByTagName('Group'); - for (var i=0,len=data.length; i "In Progress" - */ - workflowStatusToText:function(code) { - code = code * 1; - switch(code) { - case 0: return "Not Started"; - case 1: return "Failed On Start"; - case 2: return "In Progress"; - case 3: return "Error Occurred"; - case 4: return "Stopped By User"; - case 5: return "Completed"; - case 6: return "Failed On Start Retrying"; - case 7: return "Error Occurred Retrying"; - case 8: return "View Query Overflow"; - case 15: return "Canceled"; - case 16: return "Approved"; - case 17: return "Rejected"; - default: return "Unknown"; - } - }, - /** - * @name $SP().list.getWorkflowID - * @function - * @description Find the WorkflowID for a workflow, and some other related info - * - * @param {Object} setup - * @param {Number} setup.ID The item ID that is tied to the workflow - * @param {String} setup.workflowName The name of the workflow - * @return {Promise} resolve({workflowID, fileRef, description, instances}), reject(error) - * - * @example - * $SP().list("List Name").getWorkflowID({ID:15, workflowName:"Workflow for List Name (manual)"}).then(function(params) { - * alert("Workflow ID:"+params.workflowID+" and the FileRef is: "+params.fileRef); - * }); - */ - getWorkflowID:function(setup) { - var _this=this; - return _this._promise(function(prom_resolve, prom_reject) { - // check if we need to queue it - if (_this.needQueue) { return _this._addInQueue('getWorkflowID',arguments) } - if (!_this.listID) throw "[SharepointPlus 'getWorkflowID'] the list ID/Name is required."; - if (!_this.url) throw "[SharepointPlus 'getWorkflowID'] not able to find the URL!"; // we cannot determine the url - setup = setup || {}; - if (!setup.ID || !setup.workflowName) throw "[SharepointPlus 'getWorkflowID'] all parameters are mandatory"; - - // find the fileRef - _this.get({fields:"FieldRef",where:"ID = "+setup.ID}) - .then(function(d) { - if (d.length===0) throw "[SharepointPlus 'getWorkflowID'] I'm not able to find the item ID "+setup.ID; - - var fileRef = _this.cleanResult(d[0].getAttribute("FileRef")); - if(!_this.url.startsWith("http")) { - // we need to find the full path - fileRef=window.location.href.split("/").slice(0,3).join("/") + "/" + fileRef; - } - if (!fileRef.startsWith("http")) { - fileRef = _this.url.split("/").slice(0,3).join("/") +"/" + fileRef; - } - _this.ajax({ - url: _this.url+"/_vti_bin/Workflow.asmx", - body: _this._buildBodyForSOAP("GetWorkflowDataForItem", ''+fileRef+'', "http://schemas.microsoft.com/sharepoint/soap/workflow/"), - headers:{'SOAPAction':'http://schemas.microsoft.com/sharepoint/soap/workflow/GetWorkflowDataForItem'} - }).then(function(data) { - // we want to use myElem to change the getAttribute function - var res={},i,row, rows=data.getElementsByTagName('WorkflowTemplate'); - if (rows.length===0) { - // depending of the permissions, we couldn't have the WorkflowTemplate data - // in that case we have to get the workflow ID with another way - var context = SP.ClientContext.get_current(); // eslint-disable-line - var lists = context.get_web().get_lists(); - var list = lists.getByTitle(_this.listID); - var item = list.getItemById(setup.ID); - context.load(list); - context.load(item); - var workflows = list.get_workflowAssociations(); - context.load(workflows); - context.executeQueryAsync(function() { - var enumerator = workflows.getEnumerator(); - while(enumerator.moveNext()) { - var workflow = enumerator.get_current(); - if (workflow.get_name() === setup.workflowName) { - res = { - "fileRef":fileRef, - "description":workflow.get_description(), - "workflowID":"{"+workflow.get_id().toString()+"}", - "instances":[] - } - break; - } - } - prom_resolve(res); - }, - function() { - throw "[SharepointPlus 'getWorkflowID'] Problem while dealing with SP.ClientContext.get_current()"; - }); - } else { - for (i=rows.length; i--;) { - if (rows[i].getAttribute("Name") == setup.workflowName) { - res = { - "fileRef":fileRef, - "description":rows[i].getAttribute("Description"), - "workflowID":"{"+rows[i].getElementsByTagName('WorkflowTemplateIdSet')[0].getAttribute("TemplateId")+"}", - "instances":[] - }; - } - } - if (!res.fileRef) { - throw "[SharepointPlus 'getWorkflowID'] it seems the requested workflow ('"+setup.workflowName+"') doesn't exist!"; - } - rows=data.getElementsByTagName("Workflow"); - for (i=0; i"+p[i].value+""; - workflowParameters += ""; - } - - _this.ajax({ - url: _this.url + "/_vti_bin/Workflow.asmx", - body:_this._buildBodyForSOAP("StartWorkflow", ""+setup.fileRef+""+setup.workflowID+""+workflowParameters+"", "http://schemas.microsoft.com/sharepoint/soap/workflow/"), - headers:{'SOAPAction':'http://schemas.microsoft.com/sharepoint/soap/workflow/StartWorkflow'} - }).then(function() { - prom_resolve() - }, function(error) { prom_reject(error) }); - } - }) - }, - /** - @name $SP().list.startWorkflow2013 - @function - @description Manually start a work (that has been set to be manually started) (for "Sharepoint 2013 workflow" as the platform type) - - @param {Object} setup - @param {Number} [setup.ID] The item ID that tied to the workflow - @param {String} setup.workflowName The name of the workflow - @param {Array|Object} [setup.parameters] An array of object with {name:"Name of the parameter", value:"Value of the parameter"} - @return {Promise} resolve() when started, reject(error) - - @example - // if you want to call a Site Workflow, just leave the list name empty and don't provide an item ID, e.g.: - $SP().list("").startWorkflow2013({workflowName:"My Site Workflow"}); - - // to start a workflow for a list item - $SP().list("List Name").startWorkflow2013({ID:15, workflowName:"Workflow for List Name (manual)", parameters:{name:"Message",value:"Welcome here!"}).then(function() { - console.log("workflow started") - }, function(error) { - console.log("Error: ",error); - }); - **/ - startWorkflow2013:function(setup) { - var _this=this; - return _this._promise(function(prom_resolve, prom_reject) { - // check if we need to queue it - if (_this.needQueue) { return _this._addInQueue('startWorkflow2013',arguments) } - if (!_this.url) throw "[SharepointPlus 'startWorkflow2013'] not able to find the URL!"; - - setup = setup || {}; - setup.platformType = setup.platformType || 2013; // internal use when calling Site Workflow from startWorkflow() - if (!setup.workflowName) throw "[SharepointPlus 'startWorkflow2013'] Please provide the workflow name." - if (_this.listID && !setup.ID) throw "Error 'startWorkflow2013': Please provide the item ID." - - // we need "sp.workflowservices.js" - if (typeof SP === "undefined" || typeof SP.SOD === "undefined") { // eslint-disable-line - throw "[SharepointPlus 'startWorkflow2013']: SP.SOD.executeFunc is required (from the Microsoft file called init.js)"; - } - - SP.SOD.executeFunc("sp.js", "SP.ClientContext" , function(){ // eslint-disable-line - SP.SOD.registerSod('sp.workflowservices.js', SP.Utilities.Utility.getLayoutsPageUrl('sp.workflowservices.js')); // eslint-disable-line - SP.SOD.executeFunc('sp.workflowservices.js', "SP.WorkflowServices.WorkflowServicesManager", function() { // eslint-disable-line - var context = new SP.ClientContext(_this.url); // eslint-disable-line - var web = context.get_web(); - - var servicesManager = SP.WorkflowServices.WorkflowServicesManager.newObject(context, web); // eslint-disable-line - context.load(servicesManager); - // list the existing workflows - var subscriptions = servicesManager.getWorkflowSubscriptionService().enumerateSubscriptions(); - context.load(subscriptions); - - context.executeQueryAsync(function() { - var subsEnum = subscriptions.getEnumerator(), sub; - var initiationParams = {}, i, passed=false; - var workflowName = setup.workflowName.toLowerCase(); - // set the parameters - if (setup.parameters) { - if (setup.parameters.length === undefined) setup.parameters = [ setup.parameters ]; - for (i=0; i'); - var fr = document.getElementById('iframe_'+idx); - fr.onload=function() { - fr.contentWindow.__doPostBack('ctl00$PlaceHolderMain$HtmlAnchorEnd',''); - setTimeout(function() { - document.body.removeChild(fr); - prom_res(); - }, 1000); - } - fr.src=lastIntance.StatusPageUrl; - }); - }) - }, - /** - @name $SP().distributionLists - @function - @category people - @description Find the distribution lists where the specified user is member of - - @param {String} username The username with or without the domain ("domain\\login" for Sharepoint 2010, or e.g. "i:0#.w|domain\\login" for Sharepoint 2013) - @param {Object} [setup] Options (see below) - @param {String} [setup.url='current website'] The website url - @param {Boolean} [setup.cache=true] Cache the response from the server - @return {Promise} resolve(mailings), reject(error) - - @example - $SP().distributionLists("mydomain\\john_doe",{url:"http://my.si.te/subdir/"}).then(function(mailing) { - for (var i=0; i < mailing.length; i++) console.log(mailing[i]); // -> {SourceReference: "cn=listname,ou=distribution lists,ou=rainbow,dc=com", DisplayName:"listname", MailNickname:"List Name", Url:"mailto:listname@rainbow.com"} - }); - */ - distributionLists:function(username, setup) { - var _this=this; - return _this._promise(function(prom_resolve, prom_reject) { - if (!username) throw "[SharepointPlus 'distributionLists'] the username is required."; - // default values - setup = setup || {}; - if (!setup.url) { - _this.getURL() - .then(function(url) { setup.url=url; return _this.distributionLists(username, setup) }) - .then(function(res) { prom_resolve(res) }) - .catch(function(rej) { prom_reject(rej) }); - return; - } - - username = username.toLowerCase(); - setup.url=setup.url.toLowerCase(); - setup.cache = (setup.cache === false ? false : true) - // check the cache - // [ {user:"username", url:"url", data:"the distribution lists"}, ... ] - var found=false; - if (setup.cache) { - _SP_CACHE_DISTRIBUTIONLISTS.forEach(function(c) { - if (c.user === username && c.url === setup.url) { - prom_resolve(c.data); - found=true; - } - }) - } - if (found) return; - - // send the request - _this.ajax({ - url:setup.url + "/_vti_bin/UserProfileService.asmx", - body:_this._buildBodyForSOAP("GetCommonMemberships", ""+username+"", "http://microsoft.com/webservices/SharePointPortalServer/UserProfileService"), - headers:{'SOAPAction':'http://microsoft.com/webservices/SharePointPortalServer/UserProfileService/GetUserMemberships'} - }).then(function(data) { - var aResult=[]; - // get the details - data=data.getElementsByTagName('MembershipData'); - for (var i=0,len=data.length; i {ID:"1234", Name:"Doe, John", LoginName:"mydomain\john_doe", Email:"john_doe@rainbow.com"} - }); - */ - groupMembers:function(groupname, setup) { - var _this=this; - return _this._promise(function(prom_resolve, prom_reject) { - if (!groupname) throw "[SharepointPlus 'groupMembers'] the groupname is required."; - // default values - setup = setup || {}; - setup.cache = (setup.cache === false ? false : true); - if (!setup.url) { - if (!_this.url) { - _this.getURL() - .then(function(url) { setup.url=url; return _this.groupMembers(groupname, setup) }) - .then(function(res) { prom_resolve(res) }) - .catch(function(rej) { prom_reject(rej) }); - return - } - } - - groupname=groupname.toLowerCase(); - setup.url=setup.url.toLowerCase(); - // check the cache - // [ {user:"username", url:"url", data:"the distribution lists"}, ... ] - var found=false; - if (setup.cache) { - _SP_CACHE_GROUPMEMBERS.forEach(function(c) { - if (c.group === groupname && c.url === setup.url) { - prom_resolve(c.data); - found=true; - } - }) - } - if (found) return; - - // send the request - _this.ajax({ - url:setup.url + "/_vti_bin/usergroup.asmx", - body:_this._buildBodyForSOAP("GetUserCollectionFromGroup", ""+_this._cleanString(groupname)+"", "http://schemas.microsoft.com/sharepoint/soap/directory/"), - headers:{'SOAPAction':'http://schemas.microsoft.com/sharepoint/soap/directory/GetUserCollectionFromGroup'} - }).then(function(data) { - var aResult=[]; - // get the details - data=data.getElementsByTagName('User'); - for (var i=0,len=data.length; i -1) { - prom_resolve(true); - found=true; - } - } - if (!found) prom_resolve(false) - } - }) - .catch(function(error) { prom_reject(error) }) - }) - }, - /** - @name $SP().people - @function - @category people - @description Find the user details like manager, email, ... - - @param {String} [username] With or without the domain, and you can also use an email address, and if you leave it empty it's the current user by default (if you use the domain, don't forget to use a double \ like "mydomain\\john_doe") - @param {Object} [setup] Options (see below) - @param {String} [setup.url='current website'] The website url - @return {Function} resolve(people), reject(error) - - @example - $SP().people("john_doe",{url:"http://my.si.te/subdir/"}).then(function(people) { - for (var i=0; i < people.length; i++) console.log(people[i]+" = "+people[people[i]]); - }, function(err) { - console.log("Err => ",err) - }); - */ - people:function(username, setup) { - var _this=this; - return _this._promise(function(prom_resolve, prom_reject) { - if (arguments.length===1 && typeof username === "object") { setup=username; username="" } - // default values - username = username || ""; - setup = setup || {}; - if (!setup.url) { - _this.getURL() - .then(function(url) { setup.url=url; return _this.people(username,setup) }) - .then(function(res) { prom_resolve(res) }) - .catch(function(rej) { prom_reject(rej) }) - return; - } - - _this.ajax({ - url:setup.url + "/_vti_bin/UserProfileService.asmx", - body:_this._buildBodyForSOAP("GetUserProfileByName", ""+username+"", "http://microsoft.com/webservices/SharePointPortalServer/UserProfileService"), - headers:{'SOAPAction':'http://microsoft.com/webservices/SharePointPortalServer/UserProfileService/GetUserProfileByName'} - }).then(function(data) { - var aResult=[], name, value; - // get the details - data=data.getElementsByTagName('PropertyData'); - for (var i=0,len=data.length; i0 ? value[0] : null); - if (value&&value.firstChild) value=value.firstChild.nodeValue; - else value="No Value"; - aResult.push(name); - aResult[name]=value; - } - prom_resolve(aResult); - }, function(error) { prom_reject(error) }); - }) - }, - /** - @name $SP().getUserInfo - @function - @category people - @description Find the User ID, work email, and preferred name for the specified username (this is useful because of the User ID that can then be used for filtering a list) - - @param {String} username That must be "domain\\login" for Sharepoint 2010, or something like "i:0#.w|domain\\login" for Sharepoint 2013 - @param {Object} [setup] Options (see below) - @param {String} [setup.url='current website'] The website url - @return {Promise} resolve({ID,Sid,Name,LoginName,Email,Notes,IsSiteAdmin,IsDomainGroup,Flags}), reject(error) - - @example - $SP().getUserInfo("domain\\john_doe",{url:"http://my.si.te/subdir/"}).then(function(info) { - alert("User ID = "+info.ID) - }, function(error) { - console.log(error) - }); - */ - getUserInfo:function(username, setup) { - var _this=this; - return _this._promise(function(prom_resolve, prom_reject) { - if (typeof username !== "string") throw "[SharepointPlus 'getUserInfo'] the username is required."; - // default values - setup = setup || {}; - if (!setup.url) { - _this.getURL() - .then(function(url) { setup.url=url; return _this.getUserInfo(username,setup) }) - .then(function(res) { prom_resolve(res) }) - .catch(function(rej) { prom_reject(rej) }) - return; - } - - _this.ajax({ - url:setup.url + "/_vti_bin/usergroup.asmx", - body:_this._buildBodyForSOAP("GetUserInfo", ''+username+'', "http://schemas.microsoft.com/sharepoint/soap/directory/") - }).then(function(data) { - // get the details - data=data.getElementsByTagName('User'); - if (data.length===0) { - prom_reject("[SharepointPlus 'getUserInfo'] nothing returned?!") - } else { - prom_resolve({ID:data[0].getAttribute("ID"),Sid:data[0].getAttribute("Sid"),Name:data[0].getAttribute("Name"),LoginName:data[0].getAttribute("LoginName"),Email:data[0].getAttribute("Email"),Notes:data[0].getAttribute("Notes"),IsSiteAdmin:data[0].getAttribute("IsSiteAdmin"),IsDomainGroup:data[0].getAttribute("IsDomainGroup"),Flags:data[0].getAttribute("Flags")}) - } - }, function(error) { prom_reject(error) }); - }) - }, - /** - @name $SP().whoami - @function - @category people - @description Find the current user's details like manager, email, colleagues, ... - - @param {Object} [setup] Options (see below) - @param {String} [setup.url='current website'] The website url - @return {Promise}} resolve(people), reject(error) - - @example - $SP().whoami({url:"http://my.si.te/subdir/"}).then(function(people) { - for (var i=0; i < people.length; i++) console.log(people[i]+" = "+people[people[i]]); - }); - */ - whoami:function(setup) { - return this.people("",setup); - }, - /** - @name $SP().regionalSettings - @function - @category utils - @description Find the region settings (of the current user) defined with _layouts/regionalsetng.aspx?Type=User (lcid, cultureInfo, timeZone, calendar, alternateCalendar, workWeek, timeFormat..) - - @return {Promise} resolve({lcid, cultureInfo, timeZone, calendar, alternateCalendar, workWeek:{days, firstDayOfWeek, firstWeekOfYear, startTime, endTime}}), reject(error) - - @example - $SP().regionalSettings().then(function(region) { - // show the selected timezone, and the working days - console.log("timeZone: "+region.timeZone); - console.log("working days: "+region.workWeek.days.join(", ")) - }, function(error) { - console.log(error) - }) - */ - regionalSettings:function(url) { - var _this = this; - return _this._promise(function(prom_resolve, prom_reject) { - // check cache - if (_SP_CACHE_REGIONALSETTINGS) { - prom_resolve(_SP_CACHE_REGIONALSETTINGS) - return; - } - // find the base URL - if (!url) { - _this.getURL() - .then(function(url) { return _this.regionalSettings(url) }) - .then(function(res) { prom_resolve(res) }) - .catch(function(rej) { prom_reject(rej) }) - return; - } - _this.ajax({url:url + "/_layouts/regionalsetng.aspx?Type=User"}) - .then(function(data) { - var result = {lcid:"", cultureInfo:"", timeZone:"", calendar:"", alternateCalendar:""}; - var div = document.createElement('div'); - div.innerHTML = data; - var tmp, i; - var getValue = function(id) { - var e = div.querySelector("select[id$='"+id+"']"); - return e.options[e.selectedIndex].innerHTML; - }; - - result.lcid = div.querySelector("select[id$='LCID']").value; - result.cultureInfo = getValue("LCID"); - result.timeZone = getValue("TimeZone"); - result.calendar = getValue("DdlwebCalType"); - result.alternateCalendar = getValue("DdlwebAltCalType"); - - tmp=document.querySelectorAll("input[id*='ChkListWeeklyMultiDays']"); - result.workWeek = {days:[], firstDayOfWeek:"", firstWeekOfYear:"", startTime:"", endTime:""}; - for (i=0; i https://gist.github.com/Aymkdn/b17903cf7786578300f04f50460ebe96 - */ - regionalDateFormat:function(url) { - var _this = this; - return _this._promise(function(prom_resolve, prom_reject) { - // check cache - if (_SP_CACHE_DATEFORMAT) { - prom_resolve(_SP_CACHE_DATEFORMAT); - return; - } - // find the base URL - if (!url) { - _this.getURL() - .then(function(url) { return _this.regionalDateFormat(url) }) - .then(function(res) { prom_resolve(res) }) - .catch(function(rej) { prom_reject(rej) }) - return; - } - - // check if we have LCID - var lcid = ""; - if (typeof _spRegionalSettings !== "undefined") lcid=_spRegionalSettings.localeId; // eslint-disable-line - else if (_SP_CACHE_REGIONALSETTINGS) lcid=_SP_CACHE_REGIONALSETTINGS.lcid; - if (!lcid) { - _this.regionalSettings(url) - .then(function() { return _this.regionalDateFormat(url) }) - .then(function(res) { prom_resolve(res) }) - .catch(function(rej) { prom_reject(rej) }) - return; - } - - _this.ajax({url:url + "/_layouts/iframe.aspx?cal=1&date=1/1/2000&lcid="+lcid}) - .then(function(data) { - var div = document.createElement('div'); - div.innerHTML = data; - - // div will contain the full datepicker page, for the January 2000 - // search for 3/January/2000 - var x = div.querySelector('a[id="20000103"]').getAttribute("href").replace(/javascript:ClickDay\('(.*)'\)/,"$1"); - // source : http://stackoverflow.com/questions/7885096/how-do-i-decode-a-string-with-escaped-unicode - var r = /\\u([\d\w]{4})/gi; - x = x.replace(r, function (match, grp) { return String.fromCharCode(parseInt(grp, 16)); } ); - x = unescape(x); // eg: 3.1.2000 - x = x.replace(/20/, "YY"); // 3.1.YY00 - x = x.replace(/00/, "YY"); // 3.1.YYYY - x = x.replace(/03/, "DD"); // 3.1.YYYY - x = x.replace(/3/, "D"); // D.1.YYYY - x = x.replace(/01/, "MM"); // D.1.YYYY - x = x.replace(/1/, "M"); // D.M.YYYY - _SP_CACHE_DATEFORMAT = x; - prom_resolve(x) - }, function(error) { prom_reject(error) }); - }) - }, - /** - @name $SP().addressbook - @function - @category people - @description Find an user based on a part of his name - - @param {String} [word] A part of the name from the guy you're looking for - @param {Object} [setup] Options (see below) - @param {String} [setup.limit=10] Number of results returned - @param {String} [setup.type='User'] Possible values are: 'All', 'DistributionList', 'SecurityGroup', 'SharePointGroup', 'User', and 'None' (see http://msdn.microsoft.com/en-us/library/people.spprincipaltype.aspx) - @param {String} [setup.url='current website'] The website url - @return {Promise} resolve([{AccountName,UserInfoID,DisplayName,Email,Departement,Title,PrincipalType}]), reject(error) - - @example - $SP().addressbook("john", {limit:25}).then(function(people) { - for (var i=0; i < people.length; i++) { - for (var j=0; j < people[i].length; j++) console.log(people[i][j]+" = "+people[i][people[i][j]]); - } - }); - */ - addressbook:function(username, setup) { - var _this=this; - switch(arguments.length) { - case 0: username=""; setup={}; break; - case 1:{ - if (typeof username==="string") setup={} - else { setup=username; username="" } - break; - } - } - return _this._promise(function(prom_resolve, prom_reject) { - if (!setup.url) { - _this.getURL() - .then(function(url) { setup.url=url; return _this.addressbook(username, setup) }) - .then(function(res) { prom_resolve(res) }) - .catch(function(rej) { prom_reject(rej) }) - return; - } - setup.limit = setup.limit || 10; - setup.type = setup.type || "User"; - - _this.ajax({ - url:setup.url + "/_vti_bin/People.asmx", - body:_this._buildBodyForSOAP("SearchPrincipals", ""+username+""+setup.limit+""+setup.type+""), - headers:{'SOAPAction':'http://schemas.microsoft.com/sharepoint/soap/SearchPrincipals'} - }).then(function(data) { - var aResult=[], children, name, value; - // get the details - data=data.getElementsByTagName('PrincipalInfo'); - for (var i=0,lenR=data.length; i -1 || forceUTC ? new Date(Date.UTC(year,month-1,day,hour,min,sec)) : new Date(year,month-1,day,hour,min,sec)); - }, - /** - @name $SP().toSPDate - @function - @category utils - @description Change a Date object into a Sharepoint date string - @param {Date} dateObject The Date object you want to convert - @param {Date} [includeTime=false] By default the time is not returned (if the time appears then the WHERE clause will do a time comparison) - @return {String} the equivalent string for the Date object passed - - @example - $SP().toSPDate(new Date(2012,9,31), true); // --> "2012-10-31T00:00:00Z" - $SP().toSPDate(new Date(2012,9,31)); // --> "2012-10-31" - */ - toSPDate:function(oDate, includeTime) { - if (!oDate || typeof oDate !== "object" || typeof oDate.getFullYear !== "function") return ""; // "oDate instanceof Date" returns false for an unknown reason - var pad = function(p_str){ - if(p_str.toString().length==1){p_str = '0' + p_str;} - return p_str; - }; - var month = pad(oDate.getMonth()+1); - var day = pad(oDate.getDate()); - var year = oDate.getFullYear(); - var hours = pad(oDate.getHours()); - var minutes = pad(oDate.getMinutes()); - var seconds = pad(oDate.getSeconds()); - return year+"-"+month+"-"+day+(includeTime?"T"+hours+":"+minutes+":"+seconds+"Z" : ""); - }, - /** - @name $SP().getLookup - @function - @category utils - @description Split the ID and Value - @param {String} str The string to split - @return {Object} .id returns the ID (or an array of IDs), and .value returns the value (or an array of values) - @example - $SP().getLookup("328;#Foo"); // --> {id:"328", value:"Foo"} - $SP().getLookup("328;#Foo;#191;#Other Value"); // --> {id:["328", "191"], value:["Foo", "Other Value"]} - $SP().getLookup("328"); // --> {id:"328", value:"328"} - */ - getLookup:function(str) { - if (!str) return {id:"", value:""} - var a=str.split(";#"); - if (a.length<=2) return {id:a[0], value:(typeof a[1]==="undefined"?a[0]:a[1])}; - else { - // we have several lookups - return {id:str.replace(/([0-9]+;#)([^;]+)/g,"$1").replace(/;#;#/g,",").slice(0,-2).split(","), value:str.replace(/([0-9]+;#)([^;]+)/g,"$2").split(";#")} - } - }, - /** - @name $SP().getPeopleLookup - @function - @category utils - @description When returning a people field from a list using 'expandUserField' to true, then this utility function will split into more friendly pieces - @param {String} str The string to split - @return {Object|Array} An object (or array of objects) with 'id', 'name', 'username', 'email' - @example - $SP().getPeopleLookup("42;#Doe,, John,#i:0#.w|domain\John_Doe,#John_Doe@Domain.com,#John_Doe@Domain.com,#Doe,, John"); // --> {id:"42", name:"Doe, John", username:'i:0#.w|domain\John_Doe', email:'John_Doe@Domain.com'} - $SP().getPeopleLookup("42;#Doe,, John,#i:0#.w|domain\John_Doe,#John_Doe@Domain.com,#John_Doe@Domain.com,#Doe,, John;#1981;#Doe,, Jane,#i:0#.w|domain\Jane_Doe,#Jane_Doe@Domain.com,#Jane_Doe@Domain.com,#Doe,, Jane"); // --> [ {id:"42", name:"Doe, John", username:'i:0#.w|domain\John_Doe', email:'John_Doe@Domain.com'}, {id:"1981", name:"Doe, Jane", username:'i:0#.w|domain\Jane_Doe', email:'Jane_Doe@Domain.com'} ] - */ - getPeopleLookup:function(str) { - if (!str) return {id:'', name:'', username:'', email:''}; - // check if we have several people - var splt = str.split(";#"); - var res = []; - for (var i=0; i "Big_x0020_Title" - */ - toXSLString:function(str) { - if (typeof str !== "string") throw "[SharepointPlus 'toXLSString'] '"+str+"' is not a string...."; - // if the first car is a number, then FullEscape it - var FullEscape = function(strg) { - var hexVals = new Array("0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"); - var rstr = "", i, c, num, temp, hexString, tmpStr, k; - for (i=0; i < strg.length; i++) { - c = strg.charAt(i); - num = c.charCodeAt(0); - temp = 0; - hexString = ""; - while (num >= 16) { - temp = num % 16; - num = Math.floor(num / 16); - hexString += hexVals[temp]; - } - hexString += hexVals[num]; - tmpStr = ""; - for (k=hexString.length-1; k >= 0; k--) tmpStr += hexString.charAt(k); - rstr += "%" + tmpStr; - } - return rstr; - }; - var aSpaces = str.split(" "), ret = "", i, c; - // check if there is a number and work length is smaller than 5 letters - if (/^[0-9]/.test(aSpaces[0]) && aSpaces[0].length < 5) { - // change the first letter - ret = FullEscape(str.charAt(0)); - str = str.substring(1); - } - for (i=0; i < str.length; i++) { - c = str.charAt(i); - if (/[0-9A-Za-z_]/.test(c) === false) ret += FullEscape(c).toLowerCase(); - else ret += c; - } - return ret.replace(/%([a-zA-Z0-9][a-zA-Z0-9])/g,"_x00$1_").substring(0,32); - }, - /** - @name $SP().formfields - @namespace - @description Retrieve the fields info in the NewForm and in the EditForm - @return {Array} An array of hash with several keys: name, values, elements, type, and tr - - @param {String|Array} [fields=""] A list of fields to get (e.g. "field1,other field,field2" or ["field1","other field","field2"]) and by default we take all fields ... ATTENTION if you have a field with "," then use only the Array as a parameter - @param {Object} [setup] Options (see below) - @param {Boolean} [setup.mandatory=undefined] Set it to 'true' to look for the mandatory fields (the "false" value has no effect) - @param {Boolean} [setup.cache=true] By default the form is scanned only once, but you can use {cache:false} to force the form to be rescanned - - @example - $SP().formfields(); // return all the fields - - $SP().formfields({mandatory:true}).each(function() { // return all the mandatory fields - var field = this; - if (field.val().length==0) console.log(field.name()+" is empty!"); - }); - $SP().formfields("Title,Contact Name,Email").each(function() { // return these three fields - var field = this; - console.log(field.name()+" has these values: "+field.val()); - }); - // if you have a field with a comma use an Array - $SP().formfields(["Title","Long field, isn't it?","Contact Name","Email"]).each(function() { - var field = this; - console.log(field.name()+" has the description: "+field.description()); - }); - // returns the fields "Title" and "New & York", and also the mandatory fields - $SP().formfields(["Title", "New & York"],{mandatory:true}); - */ - formfields:function(fields, settings) { - return this.plugin('formfields', {fields:fields, settings:settings}) - //throw "[SharepointPlus 'formfields'] You need to load the 'formfields' plugin."; - }, - /** - @name $SP().notify - @function - @category modals - @description Permits to notify the user using the SP.UI.Notify.addNotification system - - @param {String} message Message to show - @param {Object} [options] - @param {Integer} [options.timeout=5] The number of seconds that the notification is shown - @param {Boolean} [options.override=false] This option to TRUE permits to remove the previous/current notification that is showing (even if the timeout is not over and even if it's a sticky) and replace it with the new one - @param {Boolean} [options.overrideAll=false] Same as previously except that it will remove *all* the previous notifications that are currently showing - @param {Boolean} [options.overrideSticky=true] When "overrideAll:true" then even the sticky notifications are removed, but you can block this behavior with "overrideSticky:false" - @param {Boolean} [options.sticky=false] Keep the notification on the screen until it's manually removed (or automatically removed with "overrideAll:true" and "overrideSticky:true") - @param {String} [options.name=random()] You can give a name to the notification (to use it with $SP().removeNotify('name')) - @param {Function} [options.after=function(name,afterDelay){}] You can call this function when the notification is removed -- the argument "name" is the name of the notification (see previous option), the argument "afterDelay" is TRUE when the notification has been removed by the system after it's normal timeout - - @example - $SP().notify('Processing the data...', {sticky:true}); // the notification will stay on the screen until we remove it - $SP().notify('All done!', {overrideAll:true}); // the "Processing the data..." is removed from the screen and a 5 seconds message says "All done!" - - $SP().notify('Please wait 10 seconds...', { - name:"My 10 seconds notification", - timeout:10, - after:function(name,afterDelay) { - if (afterDelay) alert("OK, you waited during 10 seconds!") - else alert("Something just removed this notification called '"+name+"'' before the timeout :-(") - } - }) - */ - notify:function(message,options) { - var _this=this; - if (message === undefined) throw "[SharepointPlus notify'] you must provide the message to show." - if (typeof message !== "string") throw "[SharepointPlus notify'] you must provide a string for the message to show." - - options = options || {}; - options.timeout = (!isNaN(options.timeout) ? options.timeout : 5); - options.override = (options.override === true ? true : false); - options.overrideAll = (options.overrideAll === true ? true : false); - options.overrideSticky = (options.overrideSticky === false ? false : true); - options.sticky = (options.sticky === true ? true : false); - options.name = options.name || new Date().getTime(); - options.after = options.after || function(){}; - - // [internal use] "fake" is used just to treat the queue due to the notifications ready - options.fake = (options.fake === true ? true : false); - // [internal use] "ignoreQueue" is when we want to treat directly the message without flushing the queue - options.ignoreQueue = (options.ignoreQueue === true ? true : false); - - if (_SP_NOTIFY_READY === false) { - _SP_NOTIFY_QUEUE.push({message:message, options:options}); - // we need core.js and sp.js - ExecuteOrDelayUntilScriptLoaded(function() { // eslint-disable-line - ExecuteOrDelayUntilScriptLoaded(function() { // eslint-disable-line - _SP_NOTIFY_READY=true; - _this.notify("fake",{fake:true}); - }, "core.js") - }, "sp.js") - return _this - } else { - // check if we don't have some notifications in queue first - if (options.ignoreQueue!==true) { - while (_SP_NOTIFY_QUEUE.length > 0) { - var a = _SP_NOTIFY_QUEUE.shift(); - a.options.ignoreQueue=true; - _this.notify(a.message, a.options); - } - } - if (options.fake===true) return; - - // for the override options - if (_SP_NOTIFY.length > 0) { - if (options.overrideAll) - _this.removeNotify({all:true, includeSticky:options.overrideSticky}) - else if (options.override) - _this.removeNotify(_SP_NOTIFY[_SP_NOTIFY.length-1].name) - } - - _SP_NOTIFY.push({name:options.name, id:SP.UI.Notify.addNotification(message, true), options:options}) // eslint-disable-line - } - - // setup a timeout - if (!options.sticky) { - setTimeout(function() { - _this.removeNotify(options.name, {timeout:true}) - }, options.timeout*1000) - } - - return _this; - }, - /** - @name $SP().removeNotify - @function - @category modals - @description Permits to remove a notification that is shown on the screen - - @param {String} [name] Name of the notification - @param {Object} [options] If you pass the options, then the 'name' is ignored - @param {Boolean} [options.all=false] To TRUE to remove ALL notifications - @param {Boolean} [options.includeSticky=true] To FALSE if you don't want to remove the sticky notifications (only works with the "all:true" option) - - @example - $SP().notify('Processing the data...', {sticky:true,name:"Processing data"}); // the notification will stay on the screen until we remove it - $SP().removeNotify("Processing data"); // the notification is removed - - $SP().notify('Doing some stuff...'); - $SP().notify('Doing some other stuff...'); - $SP().removeNotify({all:true}); // all the notifications are removed - - $SP().notify('Doing some stuff...'); - $SP().notify('Doing some other stuff...'); - $SP().notify('This is a sticky message', {sticky:true}); - $SP().removeNotify({all:true, includeSticky:false}); // all the notifications are removed except the sticky one - */ - removeNotify:function(name,options) { - var _this=this; - switch (arguments.length) { - case 0: throw "Error 'removeNotify': you must provide 'name' or 'options'." - case 2: { - if (typeof options !== "object") throw "Error 'removeNotify': you must provide an object for 'options'." - } - } - - if (arguments.length === 1 && typeof name === "object") { - options = name; - name = undefined; - } - options = options || {all:false}; - // [internal use] timeout is a boolean to say if it's a timeout remove or if we forced it - options.timeout = (options.timeout === true ? true : false); - - // make sure we are ready - if (_SP_NOTIFY_READY === false && _SP_NOTIFY_QUEUE.length > 0) { - setTimeout(function() { _this.removeNotify(name, options) }, 150) - return _this; - } - - var notif; - // if we want to delete all the notifications - if (options.all === true) { - var a=[] - while (_SP_NOTIFY.length > 0) { - notif = _SP_NOTIFY.shift(); - if (options.includeSticky === false && notif.options.sticky === true) a.push(notif) - else { - SP.UI.Notify.removeNotification(notif.id); // eslint-disable-line - setTimeout(function() { notif.options.after.call(_this, notif.name, false) }, 150) - } - } - _SP_NOTIFY = a.slice(0); // if we want to keep the sticky notifs - } else if (name !== undefined) { - // search for the notification - for (var i=0,len=_SP_NOTIFY.length; iHello World</h1><p><button type="button" onclick="$SP().closeModalDialog(\'here\')">Close</button></p>', - callback:function(dialogResult, returnValue) { - alert("Result="+dialogResult); // -> "here" - } - }) - - // using as a Promise - $SP().showModalDialog({ - title:"Dialog", - html:'<h1>Hello World</h1><p><button type="button" onclick="$SP().closeModalDialog(\'here\')">Close</button></p>' - }) - .then(function(res) { - alert("Result="+res.dialogResult); // -> "here" - }) - - // show a waiting message - $SP().waitModalDialog("Working..."); - // --- do some stuff --- - // close the waiting message and open a new modal dialog - $SP().showModalDialog({ - closePrevious:true, - title:"Success", - html:'<h1>Done!</h1>' - }) - // and use $SP().closeModalDialog() to close it - */ - showModalDialog:function(options) { - var _this=this; - return this._promise(function(prom_res) { - // in some weird cases the script is not loaded correctly, so we need to ensure it - if (!_SP_MODALDIALOG_LOADED) { - _SP_MODALDIALOG_LOADED=(typeof SP === "object" && typeof SP.UI === "object" && typeof SP.UI.ModalDialog === "function" && typeof SP.UI.ModalDialog.showModalDialog === "function"); // eslint-disable-line - if (!_SP_MODALDIALOG_LOADED) { - LoadSodByKey("sp.ui.dialog.js", function() { // eslint-disable-line - _SP_MODALDIALOG_LOADED=true; - _this.showModalDialog(options) - .then(function(res) { prom_res(res) }) - }); - return _this; - } - } - var size, ohtml; - // source: http://stackoverflow.com/a/24603642/1134119 - function iFrameReady(a,b){function e(){d||(d=!0,clearTimeout(c),b.call(this))}function f(){"complete"===this.readyState&&e.call(this)}function g(a,b,c){return a.addEventListener?a.addEventListener(b,c):a.attachEvent("on"+b,function(){return c.call(a,window.event)})}function h(){var b=a.contentDocument||a.contentWindow.document;0!==b.URL.indexOf("about:")?"complete"===b.readyState?e.call(b):(g(b,"DOMContentLoaded",e),g(b,"readystatechange",f)):c=setTimeout(h,1)}var c,d=!1;g(a,"load",function(){var b=a.contentDocument;b||(b=a.contentWindow,b&&(b=b.document)),b&&e.call(b)}),h()} // eslint-disable-line - - options.id = (options.id || "").replace(/\W+/g,""); - options.id = options.id || new Date().getTime(); - var modal_id = "sp_frame_"+options.id; - if (options.html && typeof options.html === "string") { - ohtml = document.createElement('div'); - ohtml.style.padding="10px"; - ohtml.style.display="inline-block"; - ohtml.className = "sp-showModalDialog"; - ohtml.id = 'content_'+modal_id; - ohtml.innerHTML = options.html; - options.html = ohtml; - } - // if width and height are set to "calculated" then we'll use the viewport size to define them - if (options.width === "calculated" || options.height === "calculated") { - size = _this.getPageSize(); - if (options.width === "calculated") { - options.width = size.vw.width; - if (options.width > 768) { - // we want to adjust to use 2/3 - options.width = 2*options.width/3 - } - } - if (options.height === "calculated") { - options.height = size.vw.height; - if (options.height > 576) { - // we want to adjust to use 90% - options.height = 90*options.height/100 - } - } - } - if (options.width === "full" || options.height === "full") { - size = _this.getPageSize(); - if (options.width === "full") options.width = size.vw.width; - if (options.height === "full") options.height = size.vw.height; - } - options.wait = (options.wait === true ? true : false); - options.closePrevious = (options.closePrevious === true ? true : false); - if (options.previousClose === true) options.closePrevious=true; - if (options.closePrevious) _this.closeModalDialog(); - - // if showClose=false and callback is used, then showClose=false and hideClose=true - // the reason is callback won't be triggered if showclose is false - if (options.showClose === false && (options.dialogReturnValueCallback || options.callback)) { - options.showClose = true; - options.hideClose = true; - } - - // define our own callback function to properly delete the Modal when it's closed - var callback = options.dialogReturnValueCallback || options.callback || function() {}; - options.dialogReturnValueCallback = function(dialogResult, returnValue) { - // if we use .close() then we have only one argument - var id, dialog; - if (typeof dialogResult === "object" && typeof dialogResult.type !== "undefined" && dialogResult.type === "closeModalDialog") { - var args = dialogResult; - dialogResult = args.dialogResult; - returnValue = args.returnValue; - id = args.id; - } - - // make sure we remove the correct modal, so if "id" is provided, we look for it - if (id) { - for (var i=0; i for overlay - window.top.document.body.removeChild(window.top.document.getElementById("style_"+dialog.id)); - callback.call(this, dialogResult, returnValue); - prom_res({dialogResult:dialogResult, returnValue:returnValue}); - }; - - var fct = function() { - var modal = (options.wait ? SP.UI.ModalDialog.showWaitScreenWithNoClose(options.title, options.message, options.height, options.width) : SP.UI.ModalDialog.showModalDialog(options)); // eslint-disable-line - - // search for the lastest iframe + ms-dlgContent in the top frame body - var wt = window.top; - var id = modal_id; - var frames = wt.document.querySelectorAll('body > iframe'); - var frame = frames[frames.length-1]; - var biggestZ = 0; - // we define an attribute to find them later - frame.setAttribute("id", id); - // record it into a special object - if (typeof wt._SP_MODALDIALOG === "undefined") wt._SP_MODALDIALOG=[]; - - wt._SP_MODALDIALOG.push({id:id, modal:modal, zIndex:frame.style.zIndex, options:options, type:"modalDialog"}); - // check the z-index for .ms-dlgOverlay - wt._SP_MODALDIALOG.forEach(function(val) { - if (val.zIndex > biggestZ) biggestZ = val.zIndex; - }); - biggestZ--; - wt.document.body.insertAdjacentHTML('beforeend', ''); - // if showClose=true and callback is used, then showClose=false and hideClose=true - // the reason is callback won't be triggered if showclose is false - if (options.hideClose === true) { - var cross = frame.nextSibling.querySelector('.ms-dlgCloseBtn'); - cross.parentNode.removeChild(cross); - } - if (typeof options.onload==="function") options.onload(); - if (options.url && options.onurlload && typeof options.onurlload === "function") { - // find the iframe - var frameURL = wt.document.getElementById(id); - if (frameURL) frameURL = frameURL.nextSibling; - if (frameURL) frameURL = frameURL.querySelector('iframe'); - if (frameURL) { - iFrameReady(frameURL, options.onurlload) - } - } - }; - SP.SOD.executeOrDelayUntilScriptLoaded(fct, 'sp.ui.dialog.js'); // eslint-disable-line - }) - }, - /** - @name $SP().closeModalDialog - @function - @category modals - @description Close the last modal dialog - - @param {Object} [dialogResult] One of the enumeration values specifying the result of the modal dialog (SP.UI.DialogResult|), or the modal object returned by $SP().getModalDialog() - @param {Object} [returnValue] The return value of the modal dialog - - @example - // if the user use the cross to close the modal, then `dialogResult` equals to 0 in the callback - // but you can trigger the close of the modal and pass anything you want - $SP().showModalDialog({ - id:"demo", - title:"Hello World", - html:'<p>This is an example. Click one of the buttons.</p><p class="ms-alignCenter"><button onclick="$SP().closeModalDialog(\'Continue has been clicked\')">Continue</button></p>', - callback:function(res) { - alert(res) - } - }) - - // or - var modal = $SP().getModalDialog('demo'); - if (modal) $SP().closeModalDialog(modal); - */ - closeModalDialog:function(dialogResult, returnValue) { - var fct = function() { - var md; - if (typeof dialogResult === "object" && typeof dialogResult.type !== "undefined" && dialogResult.type === "modalDialog") { - md = {id:dialogResult.id, dialogResult:returnValue, returnValue:undefined, type:"closeModalDialog"}; - dialogResult.modal.close(md); - // if it's a wait screen, then we need to remove the