UNPKG

@nextcloud/cdav-library

Version:

CalDAV and CardDAV client library for Nextcloud

1 lines 207 kB
{"version":3,"file":"index.mjs","sources":["../src/parser.js","../src/utility/namespaceUtility.js","../src/utility/xmlUtility.js","../src/errors/attachError.js","../src/errors/networkRequestAbortedError.js","../src/errors/networkRequestError.js","../src/errors/networkRequestHttpError.js","../src/errors/networkRequestServerError.js","../src/errors/networkRequestClientError.js","../src/request.js","../src/utility/stringUtility.js","../src/models/davEventListener.js","../src/debug.js","../src/propset/davCollectionPropSet.js","../src/models/davObject.js","../src/models/davCollection.js","../src/models/davCollectionPublishable.js","../src/models/davCollectionShareable.js","../src/models/vobject.js","../src/propset/calendarPropSet.js","../src/models/calendar.js","../src/models/subscription.js","../src/propset/scheduleInboxPropSet.js","../src/models/scheduleInbox.js","../src/models/scheduleOutbox.js","../src/models/calendarTrashBin.js","../src/models/deletedCalendar.js","../src/models/calendarHome.js","../src/propset/addressBookPropSet.js","../src/models/vcard.js","../src/models/addressBook.js","../src/models/addressBookHome.js","../src/propset/principalPropSet.js","../src/models/principal.js","../src/index.js"],"sourcesContent":["/**\n * CDAV Library\n *\n * This library is part of the Nextcloud project\n *\n * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors\n * SPDX-License-Identifier: AGPL-3.0-or-later\n */\n\n/**\n *\n */\nexport default class Parser {\n\n\t/**\n\t *\n\t */\n\tconstructor() {\n\t\t/**\n\t\t * Key Value Map of propertyName => parser\n\t\t * @type {object}\n\t\t * @private\n\t\t */\n\t\tthis._parser = {}\n\n\t\t// initialize default parsers shipped with this lib\n\t\tthis._registerDefaultParsers()\n\t}\n\n\t/**\n\t * checks if a parser exists for a given property name\n\t *\n\t * @param {string} propertyName\n\t * @return {boolean}\n\t */\n\tcanParse(propertyName) {\n\t\treturn Object.prototype.hasOwnProperty.call(this._parser, propertyName)\n\t}\n\n\t/**\n\t * parses a single prop Node\n\t *\n\t * @param {Document} document\n\t * @param {Node} node\n\t * @param {XPathNSResolver} resolver\n\t * @return {*}\n\t */\n\tparse(document, node, resolver) {\n\t\tconst propertyName = `{${node.namespaceURI}}${node.localName}`\n\t\tif (!this.canParse(propertyName)) {\n\t\t\tthrow new Error(`Unable to parse unknown property \"${propertyName}\"`)\n\t\t}\n\n\t\treturn this._parser[propertyName](document, node, resolver)\n\t}\n\n\t/**\n\t * registers a parser for propertyName\n\t *\n\t * @param {string} propertyName\n\t * @param {Function} parser\n\t */\n\tregisterParser(propertyName, parser) {\n\t\tthis._parser[propertyName] = parser\n\t}\n\n\t/**\n\t * unregisters a parser for propertyName\n\t *\n\t * @param {string} propertyName\n\t */\n\tunregisterParser(propertyName) {\n\t\tdelete this._parser[propertyName]\n\t}\n\n\t/**\n\t * registers the predefined parsers\n\t *\n\t * @private\n\t */\n\t_registerDefaultParsers() {\n\t\t// RFC 4918 - HTTP Extensions for Web Distributed Authoring and Versioning (WebDAV)\n\t\tthis.registerParser('{DAV:}displayname', Parser.text)\n\t\tthis.registerParser('{DAV:}creationdate', Parser.text)\n\t\tthis.registerParser('{DAV:}getcontentlength', Parser.decInt)\n\t\tthis.registerParser('{DAV:}getcontenttype', Parser.text)\n\t\tthis.registerParser('{DAV:}getcontentlanguage', Parser.text)\n\t\tthis.registerParser('{DAV:}getlastmodified', Parser.rfc1123Date)\n\t\tthis.registerParser('{DAV:}getetag', Parser.text)\n\t\tthis.registerParser('{DAV:}resourcetype', Parser.resourceType)\n\n\t\t// RFC 3744 - Web Distributed Authoring and Versioning (WebDAV) Access Control Protocol\n\t\tthis.registerParser('{DAV:}inherited-acl-set', Parser.hrefs)\n\t\tthis.registerParser('{DAV:}group', Parser.href)\n\t\tthis.registerParser('{DAV:}owner', Parser.href)\n\t\tthis.registerParser('{DAV:}current-user-privilege-set', Parser.privileges)\n\t\tthis.registerParser('{DAV:}principal-collection-set', Parser.hrefs)\n\t\tthis.registerParser('{DAV:}principal-URL', Parser.href)\n\t\tthis.registerParser('{DAV:}alternate-URI-set', Parser.hrefs)\n\t\tthis.registerParser('{DAV:}group-member-set', Parser.hrefs)\n\t\tthis.registerParser('{DAV:}group-membership', Parser.hrefs)\n\n\t\t// RFC 5397 - WebDAV Current Principal Extension\n\t\tthis.registerParser('{DAV:}current-user-principal', Parser.currentUserPrincipal)\n\n\t\t// RFC 6578 - Collection Synchronization for Web Distributed Authoring and Versioning (WebDAV)\n\t\tthis.registerParser('{DAV:}sync-token', Parser.text)\n\n\t\t// RFC 6352 - CardDAV: vCard Extensions to Web Distributed Authoring and Versioning (WebDAV)\n\t\tthis.registerParser('{urn:ietf:params:xml:ns:carddav}address-data', Parser.text)\n\t\tthis.registerParser('{urn:ietf:params:xml:ns:carddav}addressbook-description', Parser.text)\n\t\tthis.registerParser('{urn:ietf:params:xml:ns:carddav}supported-address-data', Parser.addressDataTypes)\n\t\tthis.registerParser('{urn:ietf:params:xml:ns:carddav}max-resource-size', Parser.decInt)\n\t\tthis.registerParser('{urn:ietf:params:xml:ns:carddav}addressbook-home-set', Parser.hrefs)\n\t\tthis.registerParser('{urn:ietf:params:xml:ns:carddav}principal-address', Parser.href)\n\t\tthis.registerParser('{urn:ietf:params:xml:ns:carddav}supported-collation-set', Parser.supportedCardDAVCollations)\n\n\t\t// RFC 4791 - Calendaring Extensions to WebDAV (CalDAV)\n\t\tthis.registerParser('{urn:ietf:params:xml:ns:caldav}calendar-data', Parser.text)\n\t\tthis.registerParser('{urn:ietf:params:xml:ns:caldav}calendar-home-set', Parser.hrefs)\n\t\tthis.registerParser('{urn:ietf:params:xml:ns:caldav}calendar-description', Parser.text)\n\t\tthis.registerParser('{urn:ietf:params:xml:ns:caldav}calendar-timezone', Parser.text)\n\t\tthis.registerParser('{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set', Parser.calendarComps)\n\t\tthis.registerParser('{urn:ietf:params:xml:ns:caldav}supported-calendar-data', Parser.calendarDatas)\n\t\tthis.registerParser('{urn:ietf:params:xml:ns:caldav}max-resource-size', Parser.decInt)\n\t\tthis.registerParser('{urn:ietf:params:xml:ns:caldav}min-date-time', Parser.iCalendarTimestamp)\n\t\tthis.registerParser('{urn:ietf:params:xml:ns:caldav}max-date-time', Parser.iCalendarTimestamp)\n\t\tthis.registerParser('{urn:ietf:params:xml:ns:caldav}max-instances', Parser.decInt)\n\t\tthis.registerParser('{urn:ietf:params:xml:ns:caldav}max-attendees-per-instance', Parser.decInt)\n\t\tthis.registerParser('{urn:ietf:params:xml:ns:caldav}supported-collation-set', Parser.supportedCalDAVCollations)\n\n\t\t// RFC 6638 - Scheduling Extensions to CalDAV\n\t\tthis.registerParser('{urn:ietf:params:xml:ns:caldav}schedule-outbox-URL', Parser.href)\n\t\tthis.registerParser('{urn:ietf:params:xml:ns:caldav}schedule-inbox-URL', Parser.href)\n\t\tthis.registerParser('{urn:ietf:params:xml:ns:caldav}calendar-user-address-set', Parser.hrefs)\n\t\tthis.registerParser('{urn:ietf:params:xml:ns:caldav}calendar-user-type', Parser.text)\n\t\tthis.registerParser('{urn:ietf:params:xml:ns:caldav}schedule-calendar-transp', Parser.scheduleCalendarTransp)\n\t\tthis.registerParser('{urn:ietf:params:xml:ns:caldav}schedule-default-calendar-URL', Parser.href)\n\t\tthis.registerParser('{urn:ietf:params:xml:ns:caldav}schedule-tag', Parser.text)\n\n\t\t// RFC 7809 - Calendaring Extensions to WebDAV (CalDAV): Time Zones by Reference\n\t\tthis.registerParser('{urn:ietf:params:xml:ns:caldav}timezone-service-set', Parser.hrefs)\n\t\tthis.registerParser('{urn:ietf:params:xml:ns:caldav}calendar-timezone-id', Parser.text)\n\n\t\t// RFC 7953 - Calendar Availability\n\t\tthis.registerParser('{urn:ietf:params:xml:ns:caldav}calendar-availability', Parser.text)\n\n\t\t// Apple\n\t\tthis.registerParser('{http://apple.com/ns/ical/}calendar-order', Parser.decInt)\n\t\tthis.registerParser('{http://apple.com/ns/ical/}calendar-color', Parser.color)\n\t\tthis.registerParser('{http://calendarserver.org/ns/}source', Parser.href)\n\n\t\t// https://tools.ietf.org/html/draft-daboo-valarm-extensions-04#section-11.1\n\t\tthis.registerParser('{urn:ietf:params:xml:ns:caldav}default-alarm-vevent-datetime', Parser.text)\n\t\tthis.registerParser('{urn:ietf:params:xml:ns:caldav}default-alarm-vevent-date', Parser.text)\n\t\tthis.registerParser('{urn:ietf:params:xml:ns:caldav}default-alarm-vtodo-datetime', Parser.text)\n\t\tthis.registerParser('{urn:ietf:params:xml:ns:caldav}default-alarm-vtodo-date', Parser.text)\n\n\t\t// https://github.com/apple/ccs-calendarserver/blob/master/doc/Extensions/caldav-ctag.txt\n\t\tthis.registerParser('{http://calendarserver.org/ns/}getctag', Parser.text)\n\n\t\t// https://github.com/apple/ccs-calendarserver/blob/master/doc/Extensions/caldav-proxy.txt\n\t\tthis.registerParser('{http://calendarserver.org/ns/}calendar-proxy-read-for', Parser.hrefs)\n\t\tthis.registerParser('{http://calendarserver.org/ns/}calendar-proxy-write-for', Parser.hrefs)\n\n\t\t// https://github.com/apple/ccs-calendarserver/blob/master/doc/Extensions/caldav-sharing.txt\n\t\tthis.registerParser('{http://calendarserver.org/ns/}allowed-sharing-modes', Parser.allowedSharingModes)\n\t\tthis.registerParser('{http://calendarserver.org/ns/}shared-url', Parser.href)\n\t\tthis.registerParser('{http://sabredav.org/ns}owner-principal', Parser.href)\n\t\tthis.registerParser('{http://sabredav.org/ns}read-only', Parser.bool)\n\t\tthis.registerParser('{http://calendarserver.org/ns/}pre-publish-url', Parser.href)\n\t\tthis.registerParser('{http://calendarserver.org/ns/}publish-url', Parser.href)\n\n\t\t// Nextcloud custom sharing\n\t\tthis.registerParser('{http://owncloud.org/ns}invite', Parser.ocInvite)\n\n\t\t// Nextcloud specific\n\t\tthis.registerParser('{http://owncloud.org/ns}calendar-enabled', Parser.bool)\n\t\tthis.registerParser('{http://owncloud.org/ns}enabled', Parser.bool)\n\t\tthis.registerParser('{http://owncloud.org/ns}read-only', Parser.bool)\n\t\tthis.registerParser('{http://nextcloud.com/ns}owner-displayname', Parser.text)\n\t\tthis.registerParser('{http://nextcloud.com/ns}deleted-at', Parser.iso8601DateTime)\n\t\tthis.registerParser('{http://nextcloud.com/ns}calendar-uri', Parser.text)\n\t\tthis.registerParser('{http://nextcloud.com/ns}has-photo', Parser.bool)\n\t\tthis.registerParser('{http://nextcloud.com/ns}trash-bin-retention-duration', Parser.decInt)\n\t\tthis.registerParser('{http://nextcloud.com/ns}language', Parser.text)\n\t\tthis.registerParser('{http://nextcloud.com/ns}room-type', Parser.text)\n\t\tthis.registerParser('{http://nextcloud.com/ns}room-seating-capacity', Parser.decInt)\n\t\tthis.registerParser('{http://nextcloud.com/ns}room-building-address', Parser.text)\n\t\tthis.registerParser('{http://nextcloud.com/ns}room-building-story', Parser.text)\n\t\tthis.registerParser('{http://nextcloud.com/ns}room-building-room-number', Parser.text)\n\t\tthis.registerParser('{http://nextcloud.com/ns}room-features', Parser.text)\n\n\t\t// Sabre/Dav specific\n\t\tthis.registerParser('{http://sabredav.org/ns}email-address', Parser.text)\n\t}\n\n\t/**\n\t * returns text value of Node\n\t *\n\t * @param {Document} document\n\t * @param {Node} node\n\t * @param {XPathNSResolver} resolver\n\t * @return {string}\n\t */\n\tstatic text(document, node, resolver) {\n\t\treturn document.evaluate('string(.)', node, resolver, XPathResult.ANY_TYPE, null).stringValue\n\t}\n\n\t/**\n\t * returns boolean value of Node\n\t *\n\t * @param {Document} document\n\t * @param {Node} node\n\t * @param {XPathNSResolver} resolver\n\t * @return {boolean}\n\t */\n\tstatic bool(document, node, resolver) {\n\t\treturn Parser.text(document, node, resolver) === '1'\n\t}\n\n\t/**\n\t * returns decimal integer value of Node\n\t *\n\t * @param {Document} document\n\t * @param {Node} node\n\t * @param {XPathNSResolver} resolver\n\t * @return {number}\n\t */\n\tstatic decInt(document, node, resolver) {\n\t\treturn parseInt(Parser.text(document, node, resolver), 10)\n\t}\n\n\t/**\n\t * returns Date value of Node\n\t *\n\t * @param {Document} document\n\t * @param {Node} node\n\t * @param {XPathNSResolver} resolver\n\t * @return {Date}\n\t */\n\tstatic rfc1123Date(document, node, resolver) {\n\t\tconst text = Parser.text(document, node, resolver)\n\n\t\t// TODO this might not work in every browser\n\t\treturn new Date(text)\n\t}\n\n\t/**\n\t * returns Date from an ISO8601 string\n\t *\n\t * @param {Document} document\n\t * @param {Node} node\n\t * @param {XPathNSResolver} resolver\n\t * @return {Date}\n\t */\n\tstatic iso8601DateTime(document, node, resolver) {\n\t\tconst text = Parser.text(document, node, resolver)\n\n\t\treturn new Date(text)\n\t}\n\n\t/**\n\t * returns Date value of Node\n\t *\n\t * @param {Document} document\n\t * @param {Node} node\n\t * @param {XPathNSResolver} resolver\n\t * @return {Date}\n\t */\n\tstatic iCalendarTimestamp(document, node, resolver) {\n\t\tconst text = Parser.text(document, node, resolver)\n\n\t\tconst year = parseInt(text.slice(0, 4), 10)\n\t\tconst month = parseInt(text.slice(4, 6), 10) - 1\n\t\tconst date = parseInt(text.slice(6, 8), 10)\n\n\t\tconst hour = parseInt(text.slice(9, 11), 10)\n\t\tconst minute = parseInt(text.slice(11, 13), 10)\n\t\tconst second = parseInt(text.slice(13, 15), 10)\n\n\t\tconst dateObj = new Date()\n\t\tdateObj.setUTCFullYear(year, month, date)\n\t\tdateObj.setUTCHours(hour, minute, second, 0)\n\t\treturn dateObj\n\t}\n\n\t/**\n\t * parses a {DAV:}resourcetype Node\n\t *\n\t * @param {Document} document\n\t * @param {Node} node\n\t * @param {XPathNSResolver} resolver\n\t * @return {string[]}\n\t */\n\tstatic resourceType(document, node, resolver) {\n\t\tconst result = []\n\t\tconst children = document.evaluate('*', node, resolver, XPathResult.ANY_TYPE, null)\n\t\tlet childNode\n\n\t\twhile ((childNode = children.iterateNext()) !== null) {\n\t\t\tconst ns = document.evaluate('namespace-uri(.)', childNode, resolver, XPathResult.ANY_TYPE, null).stringValue\n\t\t\tconst local = document.evaluate('local-name(.)', childNode, resolver, XPathResult.ANY_TYPE, null).stringValue\n\n\t\t\tresult.push(`{${ns}}${local}`)\n\t\t}\n\n\t\treturn result\n\t}\n\n\t/**\n\t * parses a node with one href nodes as child\n\t *\n\t * @param {Document} document\n\t * @param {Node} node\n\t * @param {XPathNSResolver} resolver\n\t * @return {string}\n\t */\n\tstatic href(document, node, resolver) {\n\t\treturn document.evaluate('string(d:href)', node, resolver, XPathResult.ANY_TYPE, null).stringValue\n\t}\n\n\t/**\n\t * parses a node with multiple href nodes as children\n\t *\n\t * @param {Document} document\n\t * @param {Node} node\n\t * @param {XPathNSResolver} resolver\n\t * @return {string[]}\n\t */\n\tstatic hrefs(document, node, resolver) {\n\t\tconst result = []\n\t\tconst hrefs = document.evaluate('d:href', node, resolver, XPathResult.ANY_TYPE, null)\n\t\tlet hrefNode\n\n\t\twhile ((hrefNode = hrefs.iterateNext()) !== null) {\n\t\t\tresult.push(document.evaluate('string(.)', hrefNode, resolver, XPathResult.ANY_TYPE, null).stringValue)\n\t\t}\n\n\t\treturn result\n\t}\n\n\t/**\n\t * Parses a set of {DAV:}privilege Nodes\n\t *\n\t * @param {Document} document\n\t * @param {Node} node\n\t * @param {XPathNSResolver} resolver\n\t * @return {string[]}\n\t */\n\tstatic privileges(document, node, resolver) {\n\t\tconst result = []\n\t\tconst privileges = document.evaluate('d:privilege/*', node, resolver, XPathResult.ANY_TYPE, null)\n\t\tlet privilegeNode\n\n\t\twhile ((privilegeNode = privileges.iterateNext()) !== null) {\n\t\t\tconst ns = document.evaluate('namespace-uri(.)', privilegeNode, resolver, XPathResult.ANY_TYPE, null).stringValue\n\t\t\tconst local = document.evaluate('local-name(.)', privilegeNode, resolver, XPathResult.ANY_TYPE, null).stringValue\n\n\t\t\tresult.push(`{${ns}}${local}`)\n\t\t}\n\n\t\treturn result\n\t}\n\n\t/**\n\t * parses the {DAV:}current-user-principal Node\n\t *\n\t * @param {Document} document\n\t * @param {Node} node\n\t * @param {XPathNSResolver} resolver\n\t * @return {object}\n\t * @property {string} type\n\t * @property {string} href\n\t */\n\tstatic currentUserPrincipal(document, node, resolver) {\n\t\tconst unauthenticatedCount\n\t\t\t= document.evaluate('count(d:unauthenticated)', node, resolver, XPathResult.ANY_TYPE, null).numberValue\n\n\t\tif (unauthenticatedCount !== 0) {\n\t\t\treturn {\n\t\t\t\ttype: 'unauthenticated',\n\t\t\t\thref: null,\n\t\t\t}\n\t\t} else {\n\t\t\treturn {\n\t\t\t\ttype: 'href',\n\t\t\t\thref: Parser.href(...arguments),\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Parses a {urn:ietf:params:xml:ns:carddav}supported-address-data Node\n\t *\n\t * @param {Document} document\n\t * @param {Node} node\n\t * @param {XPathNSResolver} resolver\n\t * @return {*}\n\t */\n\tstatic addressDataTypes(document, node, resolver) {\n\t\tconst result = []\n\t\tconst addressDatas = document.evaluate('cr:address-data-type', node, resolver, XPathResult.ANY_TYPE, null)\n\t\tlet addressDataNode\n\n\t\twhile ((addressDataNode = addressDatas.iterateNext()) !== null) {\n\t\t\tresult.push({\n\t\t\t\t'content-type': document.evaluate('string(@content-type)', addressDataNode, resolver, XPathResult.ANY_TYPE, null).stringValue,\n\t\t\t\tversion: document.evaluate('string(@version)', addressDataNode, resolver, XPathResult.ANY_TYPE, null).stringValue,\n\t\t\t})\n\t\t}\n\n\t\treturn result\n\t}\n\n\t/**\n\t * Parses a {urn:ietf:params:xml:ns:carddav}supported-collation-set Node\n\t *\n\t * @param {Document} document\n\t * @param {Node} node\n\t * @param {XPathNSResolver} resolver\n\t * @return {*}\n\t */\n\tstatic supportedCardDAVCollations(document, node, resolver) {\n\t\tconst result = []\n\t\tconst collations = document.evaluate('cr:supported-collation', node, resolver, XPathResult.ANY_TYPE, null)\n\t\tlet collationNode\n\n\t\twhile ((collationNode = collations.iterateNext()) !== null) {\n\t\t\tresult.push(document.evaluate('string(.)', collationNode, resolver, XPathResult.ANY_TYPE, null).stringValue)\n\t\t}\n\n\t\treturn result\n\t}\n\n\t/**\n\t * Parses a {urn:ietf:params:xml:ns:caldav}supported-collation-set Node\n\t *\n\t * @param {Document} document\n\t * @param {Node} node\n\t * @param {XPathNSResolver} resolver\n\t * @return {*}\n\t */\n\tstatic supportedCalDAVCollations(document, node, resolver) {\n\t\tconst result = []\n\t\tconst collations = document.evaluate('cl:supported-collation', node, resolver, XPathResult.ANY_TYPE, null)\n\t\tlet collationNode\n\n\t\twhile ((collationNode = collations.iterateNext()) !== null) {\n\t\t\tresult.push(document.evaluate('string(.)', collationNode, resolver, XPathResult.ANY_TYPE, null).stringValue)\n\t\t}\n\n\t\treturn result\n\t}\n\n\t/**\n\t * Parses a {urn:ietf:params:xml:ns:caldav}supported-calendar-component-set Node\n\t *\n\t * @param {Document} document\n\t * @param {Node} node\n\t * @param {XPathNSResolver} resolver\n\t * @return {string[]}\n\t */\n\tstatic calendarComps(document, node, resolver) {\n\t\tconst result = []\n\t\tconst comps = document.evaluate('cl:comp', node, resolver, XPathResult.ANY_TYPE, null)\n\t\tlet compNode\n\n\t\twhile ((compNode = comps.iterateNext()) !== null) {\n\t\t\tresult.push(document.evaluate('string(@name)', compNode, resolver, XPathResult.ANY_TYPE, null).stringValue)\n\t\t}\n\n\t\treturn result\n\t}\n\n\t/**\n\t * Parses a {urn:ietf:params:xml:ns:caldav}supported-calendar-data Node\n\t *\n\t * @param {Document} document\n\t * @param {Node} node\n\t * @param {XPathNSResolver} resolver\n\t * @return {*}\n\t */\n\tstatic calendarDatas(document, node, resolver) {\n\t\tconst result = []\n\t\tconst calendarDatas = document.evaluate('cl:calendar-data', node, resolver, XPathResult.ANY_TYPE, null)\n\t\tlet calendarDataNode\n\n\t\twhile ((calendarDataNode = calendarDatas.iterateNext()) !== null) {\n\t\t\tresult.push({\n\t\t\t\t'content-type': document.evaluate('string(@content-type)', calendarDataNode, resolver, XPathResult.ANY_TYPE, null).stringValue,\n\t\t\t\tversion: document.evaluate('string(@version)', calendarDataNode, resolver, XPathResult.ANY_TYPE, null).stringValue,\n\t\t\t})\n\t\t}\n\n\t\treturn result\n\t}\n\n\t/**\n\t * Parses a {urn:ietf:params:xml:ns:caldav}schedule-calendar-transp Node\n\t *\n\t * @param {Document} document\n\t * @param {Node} node\n\t * @param {XPathNSResolver} resolver\n\t * @return {string}\n\t */\n\tstatic scheduleCalendarTransp(document, node, resolver) {\n\t\tconst children = document.evaluate('cl:opaque | cl:transparent', node, resolver, XPathResult.ANY_TYPE, null)\n\t\tconst childNode = children.iterateNext()\n\t\tif (childNode) {\n\t\t\treturn document.evaluate('local-name(.)', childNode, resolver, XPathResult.ANY_TYPE, null).stringValue\n\t\t}\n\t}\n\n\t/**\n\t * Parses a {http://apple.com/ns/ical/}calendar-color Node\n\t * strips the alpha value of RGB values\n\t *\n\t * @param {Document} document\n\t * @param {Node} node\n\t * @param {XPathNSResolver} resolver\n\t * @return {string}\n\t */\n\tstatic color(document, node, resolver) {\n\t\tconst text = Parser.text(document, node, resolver)\n\t\t// some stupid clients store an alpha value in the rgb hash (like #rrggbbaa) *cough cough* Apple Calendar *cough cough*\n\t\t// but some browsers can't parse that *cough cough* Safari 9 *cough cough*\n\t\t// Safari 10 seems to support this though\n\t\tif (text.length === 9) {\n\t\t\treturn text.slice(0, 7)\n\t\t}\n\n\t\treturn text\n\t}\n\n\t/**\n\t * Parses a {http://calendarserver.org/ns/}allowed-sharing-modes Node\n\t *\n\t * @param {Document} document\n\t * @param {Node} node\n\t * @param {XPathNSResolver} resolver\n\t * @return {string[]}\n\t */\n\tstatic allowedSharingModes(document, node, resolver) {\n\t\tconst result = []\n\t\tconst children = document.evaluate('cs:can-be-shared | cs:can-be-published', node, resolver, XPathResult.ANY_TYPE, null)\n\t\tlet childNode\n\n\t\twhile ((childNode = children.iterateNext()) !== null) {\n\t\t\tconst ns = document.evaluate('namespace-uri(.)', childNode, resolver, XPathResult.ANY_TYPE, null).stringValue\n\t\t\tconst local = document.evaluate('local-name(.)', childNode, resolver, XPathResult.ANY_TYPE, null).stringValue\n\n\t\t\tresult.push(`{${ns}}${local}`)\n\t\t}\n\n\t\treturn result\n\t}\n\n\t/**\n\t * Parses a {http://owncloud.org/ns}invite Node\n\t *\n\t * @param {Document} document\n\t * @param {Node} node\n\t * @param {XPathNSResolver} resolver\n\t * @return {*}\n\t */\n\tstatic ocInvite(document, node, resolver) {\n\t\tconst result = []\n\t\tconst users = document.evaluate('oc:user', node, resolver, XPathResult.ANY_TYPE, null)\n\t\tlet userNode\n\n\t\twhile ((userNode = users.iterateNext()) !== null) {\n\t\t\tresult.push({\n\t\t\t\thref: Parser.href(document, userNode, resolver),\n\t\t\t\t'common-name': document.evaluate('string(oc:common-name)', userNode, resolver, XPathResult.ANY_TYPE, null).stringValue,\n\t\t\t\t'invite-accepted': document.evaluate('count(oc:invite-accepted)', userNode, resolver, XPathResult.ANY_TYPE, null).numberValue === 1,\n\t\t\t\taccess: Parser.ocAccess(document, userNode, resolver),\n\t\t\t})\n\t\t}\n\n\t\treturn result\n\t}\n\n\t/**\n\t * Parses a set of {http://owncloud.org/ns}access Nodes\n\t *\n\t * @param {Document} document\n\t * @param {Node} node\n\t * @param {XPathNSResolver} resolver\n\t * @return {string[]}\n\t */\n\tstatic ocAccess(document, node, resolver) {\n\t\tconst result = []\n\t\tconst privileges = document.evaluate('oc:access/*', node, resolver, XPathResult.ANY_TYPE, null)\n\t\tlet privilegeNode\n\n\t\twhile ((privilegeNode = privileges.iterateNext()) !== null) {\n\t\t\tconst ns = document.evaluate('namespace-uri(.)', privilegeNode, resolver, XPathResult.ANY_TYPE, null).stringValue\n\t\t\tconst local = document.evaluate('local-name(.)', privilegeNode, resolver, XPathResult.ANY_TYPE, null).stringValue\n\n\t\t\tresult.push(`{${ns}}${local}`)\n\t\t}\n\n\t\treturn result\n\t}\n\n}\n","/**\n * CDAV Library\n *\n * This library is part of the Nextcloud project\n *\n * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors\n * SPDX-License-Identifier: AGPL-3.0-or-later\n */\n\nexport const DAV = 'DAV:'\nexport const IETF_CALDAV = 'urn:ietf:params:xml:ns:caldav'\nexport const IETF_CARDDAV = 'urn:ietf:params:xml:ns:carddav'\nexport const OWNCLOUD = 'http://owncloud.org/ns'\nexport const NEXTCLOUD = 'http://nextcloud.com/ns'\nexport const APPLE = 'http://apple.com/ns/ical/'\nexport const CALENDARSERVER = 'http://calendarserver.org/ns/'\nexport const SABREDAV = 'http://sabredav.org/ns'\n\nexport const NS_MAP = {\n\td: DAV,\n\tcl: IETF_CALDAV,\n\tcr: IETF_CARDDAV,\n\toc: OWNCLOUD,\n\tnc: NEXTCLOUD,\n\taapl: APPLE,\n\tcs: CALENDARSERVER,\n\tsd: SABREDAV,\n}\n\n/**\n * maps namespace like DAV: to it's short equivalent\n *\n * @param {string} short\n * @return {string}\n */\nexport function resolve(short) {\n\treturn NS_MAP[short] || null\n}\n","/**\n * CDAV Library\n *\n * This library is part of the Nextcloud project\n *\n * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors\n * SPDX-License-Identifier: AGPL-3.0-or-later\n */\n\nconst serializer = new XMLSerializer()\nlet prefixMap = {}\n\n/**\n * builds the root skeleton\n *\n * @params {...Array} array of namespace / name pairs\n * @return {*[]}\n */\nexport function getRootSkeleton() {\n\tif (arguments.length === 0) {\n\t\treturn [{}, null]\n\t}\n\n\tconst skeleton = {\n\t\tname: arguments[0],\n\t\tchildren: [],\n\t}\n\n\tlet childrenWrapper = skeleton.children\n\n\tconst args = Array.prototype.slice.call(arguments, 1)\n\targs.forEach(function(argument) {\n\t\tconst level = {\n\t\t\tname: argument,\n\t\t\tchildren: [],\n\t\t}\n\t\tchildrenWrapper.push(level)\n\t\tchildrenWrapper = level.children\n\t})\n\n\treturn [skeleton, childrenWrapper]\n}\n\n/**\n * serializes an simple xml representation into a string\n *\n * @param {object} json\n * @return {string}\n */\nexport function serialize(json) {\n\tjson = json || {}\n\tif (typeof json !== 'object' || !Object.prototype.hasOwnProperty.call(json, 'name')) {\n\t\treturn ''\n\t}\n\n\tconst root = document.implementation.createDocument('', '', null)\n\txmlify(root, root, json)\n\n\treturn serializer.serializeToString(root)\n}\n\nfunction xmlify(xmlDoc, parent, json) {\n\tconst [ns, localName] = json.name\n\tconst element = xmlDoc.createElementNS(ns, getPrefixedNameForNamespace(ns, localName))\n\n\tjson.attributes = json.attributes || []\n\tjson.attributes.forEach((attribute) => {\n\t\tif (attribute.length === 2) {\n\t\t\tconst [name, value] = attribute\n\t\t\telement.setAttribute(name, value)\n\t\t} else {\n\t\t\tconst [namespace, localName, value] = attribute\n\t\t\telement.setAttributeNS(namespace, localName, value)\n\t\t}\n\t})\n\n\tif (json.value) {\n\t\telement.textContent = json.value\n\t} else if (json.children) {\n\t\tjson.children.forEach((child) => {\n\t\t\txmlify(xmlDoc, element, child)\n\t\t})\n\t}\n\n\tparent.appendChild(element)\n}\n\nexport function resetPrefixMap() {\n\tprefixMap = {}\n}\n\nfunction getPrefixedNameForNamespace(ns, localName) {\n\tif (!Object.prototype.hasOwnProperty.call(prefixMap, ns)) {\n\t\tprefixMap[ns] = 'x' + Object.keys(prefixMap).length\n\t}\n\n\treturn prefixMap[ns] + ':' + localName\n}\n","/**\n * CDAV Library\n *\n * This library is part of the Nextcloud project\n *\n * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors\n * SPDX-License-Identifier: AGPL-3.0-or-later\n */\n\n/**\n * Generic error class that allows attaching more than just a message\n *\n * @abstract\n */\nexport default class AttachError extends Error {\n\n\t/**\n\t *\n\t * @param {object} attach\n\t */\n\tconstructor(attach) {\n\t\tsuper()\n\n\t\tObject.assign(this, attach)\n\t}\n\n}\n","/**\n * CDAV Library\n *\n * This library is part of the Nextcloud project\n *\n * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors\n * SPDX-License-Identifier: AGPL-3.0-or-later\n */\n\nimport AttachError from './attachError.js'\n\nexport default class NetworkRequestAbortedError extends AttachError {}\n","/**\n * CDAV Library\n *\n * This library is part of the Nextcloud project\n *\n * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors\n * SPDX-License-Identifier: AGPL-3.0-or-later\n */\n\nimport AttachError from './attachError.js'\n\nexport default class NetworkRequestError extends AttachError {}\n","/**\n * CDAV Library\n *\n * This library is part of the Nextcloud project\n *\n * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors\n * SPDX-License-Identifier: AGPL-3.0-or-later\n */\n\nimport AttachError from './attachError.js'\n\nexport default class NetworkRequestHttpError extends AttachError {}\n","/**\n * CDAV Library\n *\n * This library is part of the Nextcloud project\n *\n * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors\n * SPDX-License-Identifier: AGPL-3.0-or-later\n */\n\nimport NetworkRequestHttpError from './networkRequestHttpError.js'\n\nexport default class NetworkRequestServerError extends NetworkRequestHttpError {}\n","/**\n * CDAV Library\n *\n * This library is part of the Nextcloud project\n *\n * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors\n * SPDX-License-Identifier: AGPL-3.0-or-later\n */\n\nimport NetworkRequestHttpError from './networkRequestHttpError.js'\n\nexport default class NetworkRequestClientError extends NetworkRequestHttpError {}\n","/**\n * CDAV Library\n *\n * This library is part of the Nextcloud project\n *\n * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors\n * SPDX-License-Identifier: AGPL-3.0-or-later\n */\n\nimport * as NS from './utility/namespaceUtility.js'\nimport * as XMLUtility from './utility/xmlUtility.js'\n\nimport NetworkRequestAbortedError from './errors/networkRequestAbortedError.js'\nimport NetworkRequestError from './errors/networkRequestError.js'\nimport NetworkRequestServerError from './errors/networkRequestServerError.js'\nimport NetworkRequestClientError from './errors/networkRequestClientError.js'\nimport NetworkRequestHttpError from './errors/networkRequestHttpError.js'\n\n/**\n * Request class is used to send any kind of request to the DAV server\n * It also parses incoming XML responses\n */\nexport default class Request {\n\n\t/**\n\t * Creates a new Request object\n\t *\n\t * @param {string} baseUrl - root url of DAV server, use OC.remote('dav')\n\t * @param {Parser} parser - instance of Parser class\n\t * @param {Function} xhrProvider - Function that returns new XMLHttpRequest objects\n\t */\n\tconstructor(baseUrl, parser, xhrProvider = () => new XMLHttpRequest()) {\n\t\tthis.baseUrl = baseUrl\n\t\tthis.parser = parser\n\t\tthis.xhrProvider = xhrProvider\n\t}\n\n\t/**\n\t * sends a GET request\n\t *\n\t * @param {string} url - URL to do the request on\n\t * @param {object} headers - additional HTTP headers to send\n\t * @param {string} body - request body\n\t * @param {Function} beforeRequestHandler - custom function to be called before the request is made\n\t * @param {Function} afterRequestHandler - custom function to be called after the request was made\n\t * @return {Promise<{Object}>}\n\t * @property {string | object} body\n\t * @property {number} status\n\t * @property {XMLHttpRequest} xhr\n\t */\n\tasync get(url, headers = {}, body = null, beforeRequestHandler = () => null, afterRequestHandler = () => null) {\n\t\treturn this.request('GET', url, headers, body, beforeRequestHandler, afterRequestHandler)\n\t}\n\n\t/**\n\t * sends a PATCH request\n\t *\n\t * @param {string} url - URL to do the request on\n\t * @param {object} headers - additional HTTP headers to send\n\t * @param {string} body - request body\n\t * @param {Function} beforeRequestHandler - custom function to be called before the request is made\n\t * @param {Function} afterRequestHandler - custom function to be called after the request was made\n\t * @return {Promise<{Object}>}\n\t * @property {string | object} body\n\t * @property {number} status\n\t * @property {XMLHttpRequest} xhr\n\t */\n\tasync patch(url, headers, body, beforeRequestHandler = () => null, afterRequestHandler = () => null) {\n\t\treturn this.request('PATCH', url, headers, body, beforeRequestHandler, afterRequestHandler)\n\t}\n\n\t/**\n\t * sends a POST request\n\t *\n\t * @param {string} url - URL to do the request on\n\t * @param {object} headers - additional HTTP headers to send\n\t * @param {string} body - request body\n\t * @param {Function} beforeRequestHandler - custom function to be called before the request is made\n\t * @param {Function} afterRequestHandler - custom function to be called after the request was made\n\t * @return {Promise<{Object}>}\n\t * @property {string | object} body\n\t * @property {number} status\n\t * @property {XMLHttpRequest} xhr\n\t */\n\tasync post(url, headers, body, beforeRequestHandler = () => null, afterRequestHandler = () => null) {\n\t\treturn this.request('POST', url, headers, body, beforeRequestHandler, afterRequestHandler)\n\t}\n\n\t/**\n\t * sends a PUT request\n\t *\n\t * @param {string} url - URL to do the request on\n\t * @param {object} headers - additional HTTP headers to send\n\t * @param {string} body - request body\n\t * @param {Function} beforeRequestHandler - custom function to be called before the request is made\n\t * @param {Function} afterRequestHandler - custom function to be called after the request was made\n\t * @return {Promise<{Object}>}\n\t * @property {string | object} body\n\t * @property {number} status\n\t * @property {XMLHttpRequest} xhr\n\t */\n\tasync put(url, headers, body, beforeRequestHandler = () => null, afterRequestHandler = () => null) {\n\t\treturn this.request('PUT', url, headers, body, beforeRequestHandler, afterRequestHandler)\n\t}\n\n\t/**\n\t * sends a DELETE request\n\t *\n\t * @param {string} url - URL to do the request on\n\t * @param {object} headers - additional HTTP headers to send\n\t * @param {string} body - request body\n\t * @param {Function} beforeRequestHandler - custom function to be called before the request is made\n\t * @param {Function} afterRequestHandler - custom function to be called after the request was made\n\t * @return {Promise<{Object}>}\n\t * @property {string | object} body\n\t * @property {number} status\n\t * @property {XMLHttpRequest} xhr\n\t */\n\tasync delete(url, headers = {}, body = null, beforeRequestHandler = () => null, afterRequestHandler = () => null) {\n\t\treturn this.request('DELETE', url, headers, body, beforeRequestHandler, afterRequestHandler)\n\t}\n\n\t/**\n\t * sends a COPY request\n\t * https://tools.ietf.org/html/rfc4918#section-9.8\n\t *\n\t * @param {string} url - URL to do the request on\n\t * @param {string} destination - place to copy the object/collection to\n\t * @param {number | string} depth - 0 = copy collection without content, Infinity = copy collection with content\n\t * @param {boolean} overwrite - whether or not to overwrite destination if existing\n\t * @param {object} headers - additional HTTP headers to send\n\t * @param {string} body - request body\n\t * @param {Function} beforeRequestHandler - custom function to be called before the request is made\n\t * @param {Function} afterRequestHandler - custom function to be called after the request was made\n\t * @return {Promise<{Object}>}\n\t * @property {string | object} body\n\t * @property {number} status\n\t * @property {XMLHttpRequest} xhr\n\t */\n\tasync copy(url, destination, depth = 0, overwrite = false, headers = {}, body = null, beforeRequestHandler = () => null, afterRequestHandler = () => null) {\n\t\theaders.Destination = destination\n\t\theaders.Depth = depth\n\t\theaders.Overwrite = overwrite ? 'T' : 'F'\n\n\t\treturn this.request('COPY', url, headers, body, beforeRequestHandler, afterRequestHandler)\n\t}\n\n\t/**\n\t * sends a MOVE request\n\t * https://tools.ietf.org/html/rfc4918#section-9.9\n\t *\n\t * @param {string} url - URL to do the request on\n\t * @param {string} destination - place to move the object/collection to\n\t * @param {boolean} overwrite - whether or not to overwrite destination if existing\n\t * @param {object} headers - additional HTTP headers to send\n\t * @param {string} body - request body\n\t * @param {Function} beforeRequestHandler - custom function to be called before the request is made\n\t * @param {Function} afterRequestHandler - custom function to be called after the request was made\n\t * @return {Promise<{Object}>}\n\t * @property {string | object} body\n\t * @property {number} status\n\t * @property {XMLHttpRequest} xhr\n\t */\n\tasync move(url, destination, overwrite = false, headers = {}, body = null, beforeRequestHandler = () => null, afterRequestHandler = () => null) {\n\t\theaders.Destination = destination\n\t\theaders.Depth = 'Infinity'\n\t\theaders.Overwrite = overwrite ? 'T' : 'F'\n\n\t\treturn this.request('MOVE', url, headers, body, beforeRequestHandler, afterRequestHandler)\n\t}\n\n\t/**\n\t * sends a LOCK request\n\t * https://tools.ietf.org/html/rfc4918#section-9.10\n\t *\n\t * @param {string} url - URL to do the request on\n\t * @param {object} headers - additional HTTP headers to send\n\t * @param {string} body - request body\n\t * @param {Function} beforeRequestHandler - custom function to be called before the request is made\n\t * @param {Function} afterRequestHandler - custom function to be called after the request was made\n\t * @return {Promise<{Object}>}\n\t * @property {string | object} body\n\t * @property {number} status\n\t * @property {XMLHttpRequest} xhr\n\t */\n\tasync lock(url, headers = {}, body = null, beforeRequestHandler = () => null, afterRequestHandler = () => null) {\n\n\t\t// TODO - add parameters for Depth and Timeout\n\n\t\treturn this.request('LOCK', url, headers, body, beforeRequestHandler, afterRequestHandler)\n\t}\n\n\t/**\n\t * sends an UNLOCK request\n\t * https://tools.ietf.org/html/rfc4918#section-9.11\n\t *\n\t * @param {string} url - URL to do the request on\n\t * @param {object} headers - additional HTTP headers to send\n\t * @param {string} body - request body\n\t * @param {Function} beforeRequestHandler - custom function to be called before the request is made\n\t * @param {Function} afterRequestHandler - custom function to be called after the request was made\n\t * @return {Promise<{Object}>}\n\t * @property {string | object} body\n\t * @property {number} status\n\t * @property {XMLHttpRequest} xhr\n\t */\n\tasync unlock(url, headers = {}, body = null, beforeRequestHandler = () => null, afterRequestHandler = () => null) {\n\n\t\t// TODO - add parameter for Lock-Token\n\n\t\treturn this.request('UNLOCK', url, headers, body, beforeRequestHandler, afterRequestHandler)\n\t}\n\n\t/**\n\t * sends a PROPFIND request\n\t * https://tools.ietf.org/html/rfc4918#section-9.1\n\t *\n\t * @param {string} url - URL to do the request on\n\t * @param {string[][]} properties - list of properties to search for, formatted as [namespace, localName]\n\t * @param {number | string} depth - Depth header to send\n\t * @param {object} headers - additional HTTP headers to send\n\t * @param {Function} beforeRequestHandler - custom function to be called before the request is made\n\t * @param {Function} afterRequestHandler - custom function to be called after the request was made\n\t * @return {Promise<{Object}>}\n\t * @property {string | object} body\n\t * @property {number} status\n\t * @property {XMLHttpRequest} xhr\n\t */\n\tasync propFind(url, properties, depth = 0, headers = {}, beforeRequestHandler = () => null, afterRequestHandler = () => null) {\n\t\t// adjust headers\n\t\theaders.Depth = depth\n\n\t\t// create request body\n\t\tconst [skeleton, dPropChildren] = XMLUtility.getRootSkeleton([NS.DAV, 'propfind'], [NS.DAV, 'prop'])\n\t\tdPropChildren.push(...properties.map(p => ({ name: p })))\n\t\tconst body = XMLUtility.serialize(skeleton)\n\n\t\treturn this.request('PROPFIND', url, headers, body, beforeRequestHandler, afterRequestHandler)\n\t}\n\n\t/**\n\t * sends a PROPPATCH request\n\t * https://tools.ietf.org/html/rfc4918#section-9.2\n\t *\n\t * @param {string} url - URL to do the request on\n\t * @param {object} headers - additional HTTP headers to send\n\t * @param {string} body - request body\n\t * @param {Function} beforeRequestHandler - custom function to be called before the request is made\n\t * @param {Function} afterRequestHandler - custom function to be called after the request was made\n\t * @return {Promise<{Object}>}\n\t * @property {string | object} body\n\t * @property {number} status\n\t * @property {XMLHttpRequest} xhr\n\t */\n\tasync propPatch(url, headers, body, beforeRequestHandler = () => null, afterRequestHandler = () => null) {\n\t\treturn this.request('PROPPATCH', url, headers, body, beforeRequestHandler, afterRequestHandler)\n\t}\n\n\t/**\n\t * sends a MKCOL request\n\t * https://tools.ietf.org/html/rfc4918#section-9.3\n\t * https://tools.ietf.org/html/rfc5689\n\t *\n\t * @param {string} url - URL to do the request on\n\t * @param {object} headers - additional HTTP headers to send\n\t * @param {string} body - request body\n\t * @param {Function} beforeRequestHandler - custom function to be called before the request is made\n\t * @param {Function} afterRequestHandler - custom function to be called after the request was made\n\t * @return {Promise<{Object}>}\n\t * @property {string | object} body\n\t * @property {number} status\n\t * @property {XMLHttpRequest} xhr\n\t */\n\tasync mkCol(url, headers, body, beforeRequestHandler = () => null, afterRequestHandler = () => null) {\n\t\treturn this.request('MKCOL', url, headers, body, beforeRequestHandler, afterRequestHandler)\n\t}\n\n\t/**\n\t * sends a REPORT request\n\t * https://tools.ietf.org/html/rfc3253#section-3.6\n\t *\n\t * @param {string} url - URL to do the request on\n\t * @param {object} headers - additional HTTP headers to send\n\t * @param {string} body - request body\n\t * @param {Function} beforeRequestHandler - custom function to be called before the request is made\n\t * @param {Function} afterRequestHandler - custom function to be called after the request was made\n\t * @return {Promise<{Object}>}\n\t * @property {string | object} body\n\t * @property {number} status\n\t * @property {XMLHttpRequest} xhr\n\t */\n\tasync report(url, headers, body, beforeRequestHandler = () => null, afterRequestHandler = () => null) {\n\t\treturn this.request('REPORT', url, headers, body, beforeRequestHandler, afterRequestHandler)\n\t}\n\n\t/**\n\t * sends generic request\n\t *\n\t * @param {string} method - HTTP Method name\n\t * @param {string} url - URL to do the request on\n\t * @param {object} headers - additional HTTP headers to send\n\t * @param {string} body - request body\n\t * @param {Function} beforeRequestHandler - custom function to be called before the request is made\n\t * @param {Function} afterRequestHandler - custom function to be called after the request was made\n\t * @return {Promise<{Object}>}\n\t * @property {string | object} body\n\t * @property {number} status\n\t * @property {XMLHttpRequest} xhr\n\t */\n\tasync request(method, url, headers, body, beforeRequestHandler = () => null, afterRequestHandler = () => null) {\n\t\tconst xhr = this.xhrProvider()\n\t\tconst assignHeaders = Object.assign({}, getDefaultHeaders(), headers)\n\n\t\txhr.open(method, this.absoluteUrl(url), true)\n\n\t\tfor (const header in assignHeaders) {\n\t\t\txhr.setRequestHeader(header, assignHeaders[header])\n\t\t}\n\n\t\tbeforeRequestHandler(xhr)\n\n\t\tif (body === null || body === undefined) {\n\t\t\txhr.send()\n\t\t} else {\n\t\t\txhr.send(body)\n\t\t}\n\n\t\treturn new Promise((resolve, reject) => {\n\t\t\txhr.onreadystatechange = () => {\n\t\t\t\tif (xhr.readyState !== 4) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tafterRequestHandler(xhr)\n\n\t\t\t\tlet responseBody = xhr.response\n\t\t\t\tif (!wasRequestSuccessful(xhr.status)) {\n\t\t\t\t\tif (xhr.status >= 400 && xhr.status < 500) {\n\t\t\t\t\t\treject(new NetworkRequestClientError({\n\t\t\t\t\t\t\tbody: responseBody,\n\t\t\t\t\t\t\tstatus: xhr.status,\n\t\t\t\t\t\t\txhr,\n\t\t\t\t\t\t}))\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t\tif (xhr.status >= 500 && xhr.status < 600) {\n\t\t\t\t\t\treject(new NetworkRequestServerError({\n\t\t\t\t\t\t\tbody: responseBody,\n\t\t\t\t\t\t\tstatus: xhr.status,\n\t\t\t\t\t\t\txhr,\n\t\t\t\t\t\t}))\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\n\t\t\t\t\treject(new NetworkRequestHttpError({\n\t\t\t\t\t\tbody: responseBody,\n\t\t\t\t\t\tstatus: xhr.status,\n\t\t\t\t\t\txhr,\n\t\t\t\t\t}))\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tif (xhr.status === 207) {\n\t\t\t\t\tresponseBody = this._parseMultiStatusResponse(responseBody)\n\t\t\t\t\tif (parseInt(assignHeaders.Depth, 10) === 0 && method === 'PROPFIND') {\n\t\t\t\t\t\tresponseBody = responseBody[Object.keys(responseBody)[0]]\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tresolve({\n\t\t\t\t\tbody: responseBody,\n\t\t\t\t\tstatus: xhr.status,\n\t\t\t\t\txhr,\n\t\t\t\t})\n\t\t\t}\n\n\t\t\txhr.onerror = () => reject(new NetworkRequestError({\n\t\t\t\tbody: null,\n\t\t\t\tstatus: -1,\n\t\t\t\txhr,\n\t\t\t}))\n\n\t\t\txhr.onabort = () => reject(new NetworkRequestAbortedError({\n\t\t\t\tbody: null,\n\t\t\t\tstatus: -1,\n\t\t\t\txhr,\n\t\t\t}))\n\t\t})\n\t}\n\n\t/**\n\t * returns name of file / folder of a url\n\t *\n\t * @param url\n\t * @params {string} url\n\t * @return {string}\n\t */\n\tfilename(url) {\n\t\tlet pathname = this.pathname(url)\n\t\tif (pathname.slice(-1) === '/') {\n\t\t\tpathname = pathname.slice(0, -1)\n\t\t}\n\n\t\tconst slashPos = pathname.lastIndexOf('/')\n\t\treturn pathname.slice(slashPos)\n\t}\n\n\t/**\n\t * returns pathname for a URL\n\t *\n\t * @param url\n\t * @params {string} url\n\t * @return {string}\n\t */\n\tpathname(url) {\n\t\tconst urlObject = new URL(url, this.baseUrl)\n\t\treturn urlObject.pathname\n\t}\n\n\t/**\n\t * returns absolute url\n\t *\n\t * @param {string} url\n\t * @return {string}\n\t */\n\tabsoluteUrl(url) {\n\t\tconst urlObject = new URL(url, this.baseUrl)\n\t\treturn urlObject.href\n\t}\n\n\t/**\n\t * parses a multi status response (207), sorts them by path\n\t * and drops all unsuccessful responses\n\t *\n\t * @param {string} body\n\t * @return {object}\n\t * @private\n\t */\n\t_parseMultiStatusResponse(body) {\n\t\tconst result = {}\n\t\tconst domParser = new DOMParser()\n\t\tconst document = domParser.parseFromString(body, 'application/xml')\n\n\t\tconst responses = document.evaluate('/d:multistatus/d:response', document, NS.resolve, XPathResult.ANY_TYPE, null)\n\t\tlet responseNode\n\n\t\twhile ((responseNode = responses.iterateNext()) !== null) {\n\t\t\tconst href = document.evaluate('string(d:href)', responseNode, NS.resolve, XPathResult.ANY_TYPE, null).stringValue\n\t\t\tconst parsedProperties = {}\n\t\t\tconst propStats = document.evaluate('d:propstat', responseNode, NS.resolve, XPathResult.ANY_TYPE, null)\n\t\t\tlet propStatNode\n\n\t\t\twhile ((propStatNode = propStats.iterateNext()) !== null) {\n\t\t\t\tconst status = document.evaluate('string(d:status)', propStatNode, NS.resolve, XPathResult.ANY_TYPE, null).stringValue\n\t\t\t\tif (!wasRequestSuccessful(getStatusCodeFromString(status))) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\tconst props = document.evaluate('d:prop/*', propStatNode, NS.resolve, XPathResult.ANY_TYPE, null)\n\t\t\t\tlet propNode\n\n\t\t\t\twhile ((propNode = props.iterateNext()) !== null) {\n\t\t\t\t\tif (this.parser.canParse(`{${propNode.namespaceURI}}${propNode.localName}`)) {\n\t\t\t\t\t\tparsedProperties[`{${propNode.namespaceURI}}${propNode.localName}`]\n\t\t\t\t\t\t\t= this.parser.parse(document, propNode, NS.resolve)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tresult[href] = parsedProperties\n\t\t}\n\n\t\treturn result\n\t}\n\n}\n\n/**\n * Check if response code is in the 2xx section\n *\n * @param {number} status\n * @return {boolean}\n * @private\n */\nfunction wasRequestSuccessful(status) {\n\treturn status >= 200 && status < 300\n}\n\n/**\n * Extract numeric status code from string like \"HTTP/1.1 200 OK\"\n *\n * @param {string} status\n * @return {number}\n * @private\n */\nfunction getStatusCodeFromString(status) {\n\treturn parseInt(status.split(' ')[1], 10)\n}\n\n/**\n * get object with default headers to include in every request\n *\n * @return {object}\n * @property {string} depth\n * @property {string} Content-Type\n * @private\n */\nfunction getDefaultHeaders() {\n\t// TODO: https://tools.ietf.org/html/rfc4918#section-9.1\n\t// \"Servers SHOULD treat request without a Depth header\n\t// as if a \"Depth: infinity\" header was included.\"\n\t// Should infinity be the default?\n\n\treturn {\n\t\tDepth: '0',\n\t\t'Content-Type': 'application/xml; charset=utf-8',\n\t}\n}\n","/**\n * CDAV Library\n *\n * This library is part of the Nextcloud project\n *\n * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors\n * SPDX-License-Identifier: AGPL-3.0-or-later\n */\n\n// uuidv4 taken from https://stackoverflow.com/a/2117523\nfunction uuidv4() {\n\treturn 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {\n\t\tconst r = Math.random() * 16 | 0; const v = c === 'x' ? r : (r & 0x3 | 0x8)\n\t\treturn v.toString(16).toUpperCase()\n\t})\n}\n\n/**\n * generates a unique id with the option to pass a prefix and a filetype\n *\n * @param {string} prefix\n * @param {string} suffix\n * @return {string}\n */\nexport function uid(prefix, suffix) {\n\tprefix = prefix || ''\n\tsuffix = suffix || ''\n\n\tif (prefix !== '') {\n\t\tprefix += '-'\n\t}\n\tif (suffix !== '') {\n\t\tsuffix = '.' + suffix\n\t}\n\n\treturn prefix + uuidv4() + suffix\n}\n\n/**\n * generates a uri and checks with isAvailable, whether or not the uri is still available\n *\n * @param {string} start\n * @param {Function} isAvailable\n * @return {string}\n */\nexport function uri(start, isAvailable) {\n\tstart = start || ''\n\n\tlet uri = start.toString().toLowerCase()\n\t\t.